Added adapter to handle float('inf') and float('nan')

This commit is contained in:
Federico Di Gregorio 2009-01-23 00:09:20 +01:00
parent e22451736a
commit eb25f9f154
9 changed files with 330 additions and 5 deletions

View File

@ -1,4 +1,12 @@
2009-01-20 Federico Di Gregorio <fog@initd.org
2009-01-23 Federico Di Gregorio <fog@initd.org>
* Fixed problem mailed by Markus Demleitner about Python to
PostgreSQL conversions of "nan" and "inf" floats resulting in
backend errors. Added the new psycopg2.extensions.Float adapter
that correctly handle both values and unit tests. (The opposite
conversion was already working.)
2009-01-20 Federico Di Gregorio <fog@initd.org>
* Fixed problem reported by Lawrence Oluyede where
register_type() didn't work on connection and cursors

View File

@ -31,7 +31,7 @@ from _psycopg import DECIMALARRAY, FLOATARRAY, INTEGERARRAY, INTERVALARRAY
from _psycopg import LONGINTEGERARRAY, ROWIDARRAY, STRINGARRAY, TIMEARRAY
from _psycopg import UNICODEARRAY
from _psycopg import Boolean, QuotedString, AsIs
from _psycopg import Boolean, Float, QuotedString, AsIs
try:
from _psycopg import DateFromMx, TimeFromMx, TimestampFromMx
from _psycopg import IntervalFromMx

246
psycopg/adapter_pfloat.c Normal file
View File

@ -0,0 +1,246 @@
/* adapter_float.c - psycopg pfloat type wrapper implementation
*
* Copyright (C) 2003-2009 Federico Di Gregorio <fog@debian.org>
*
* This file is part of psycopg.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <structmember.h>
#include <floatobject.h>
#include <math.h>
#define PSYCOPG_MODULE
#include "psycopg/config.h"
#include "psycopg/python.h"
#include "psycopg/psycopg.h"
#include "psycopg/adapter_pfloat.h"
#include "psycopg/microprotocols_proto.h"
/** the Float object **/
static PyObject *
pfloat_str(pfloatObject *self)
{
double n = PyFloat_AsDouble(self->wrapped);
if (isnan(n))
return PyString_FromString("'NaN'::float");
else if (isinf(n))
return PyString_FromString("'Infinity'::float");
else
return PyObject_Str(self->wrapped);
}
static PyObject *
pfloat_getquoted(pfloatObject *self, PyObject *args)
{
if (!PyArg_ParseTuple(args, "")) return NULL;
return pfloat_str(self);
}
static PyObject *
pfloat_conform(pfloatObject *self, PyObject *args)
{
PyObject *res, *proto;
if (!PyArg_ParseTuple(args, "O", &proto)) return NULL;
if (proto == (PyObject*)&isqlquoteType)
res = (PyObject*)self;
else
res = Py_None;
Py_INCREF(res);
return res;
}
/** the Float object */
/* object member list */
static struct PyMemberDef pfloatObject_members[] = {
{"adapted", T_OBJECT, offsetof(pfloatObject, wrapped), RO},
{NULL}
};
/* object method table */
static PyMethodDef pfloatObject_methods[] = {
{"getquoted", (PyCFunction)pfloat_getquoted, METH_VARARGS,
"getquoted() -> wrapped object value as SQL-quoted string"},
{"__conform__", (PyCFunction)pfloat_conform, METH_VARARGS, NULL},
{NULL} /* Sentinel */
};
/* initialization and finalization methods */
static int
pfloat_setup(pfloatObject *self, PyObject *obj)
{
Dprintf("pfloat_setup: init pfloat object at %p, refcnt = "
FORMAT_CODE_PY_SSIZE_T,
self, ((PyObject *)self)->ob_refcnt
);
Py_INCREF(obj);
self->wrapped = obj;
Dprintf("pfloat_setup: good pfloat object at %p, refcnt = "
FORMAT_CODE_PY_SSIZE_T,
self, ((PyObject *)self)->ob_refcnt
);
return 0;
}
static int
pfloat_traverse(PyObject *obj, visitproc visit, void *arg)
{
pfloatObject *self = (pfloatObject *)obj;
Py_VISIT(self->wrapped);
return 0;
}
static void
pfloat_dealloc(PyObject* obj)
{
pfloatObject *self = (pfloatObject *)obj;
Py_CLEAR(self->wrapped);
Dprintf("pfloat_dealloc: deleted pfloat object at %p, refcnt = "
FORMAT_CODE_PY_SSIZE_T,
obj, obj->ob_refcnt
);
obj->ob_type->tp_free(obj);
}
static int
pfloat_init(PyObject *obj, PyObject *args, PyObject *kwds)
{
PyObject *o;
if (!PyArg_ParseTuple(args, "O", &o))
return -1;
return pfloat_setup((pfloatObject *)obj, o);
}
static PyObject *
pfloat_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
return type->tp_alloc(type, 0);
}
static void
pfloat_del(PyObject* self)
{
PyObject_GC_Del(self);
}
static PyObject *
pfloat_repr(pfloatObject *self)
{
return PyString_FromFormat("<psycopg2._psycopg.Float object at %p>",
self);
}
/* object type */
#define pfloatType_doc \
"Float(str) -> new Float adapter object"
PyTypeObject pfloatType = {
PyObject_HEAD_INIT(NULL)
0,
"psycopg2._psycopg.Float",
sizeof(pfloatObject),
0,
pfloat_dealloc, /*tp_dealloc*/
0, /*tp_print*/
0, /*tp_getattr*/
0, /*tp_setattr*/
0, /*tp_compare*/
(reprfunc)pfloat_repr, /*tp_repr*/
0, /*tp_as_number*/
0, /*tp_as_sequence*/
0, /*tp_as_mapping*/
0, /*tp_hash */
0, /*tp_call*/
(reprfunc)pfloat_str, /*tp_str*/
0, /*tp_getattro*/
0, /*tp_setattro*/
0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_HAVE_GC, /*tp_flags*/
pfloatType_doc, /*tp_doc*/
pfloat_traverse, /*tp_traverse*/
0, /*tp_clear*/
0, /*tp_richcompare*/
0, /*tp_weaklistoffset*/
0, /*tp_iter*/
0, /*tp_iternext*/
/* Attribute descriptor and subclassing stuff */
pfloatObject_methods, /*tp_methods*/
pfloatObject_members, /*tp_members*/
0, /*tp_getset*/
0, /*tp_base*/
0, /*tp_dict*/
0, /*tp_descr_get*/
0, /*tp_descr_set*/
0, /*tp_dictoffset*/
pfloat_init, /*tp_init*/
0, /*tp_alloc will be set to PyType_GenericAlloc in module init*/
pfloat_new, /*tp_new*/
(freefunc)pfloat_del, /*tp_free Low-level free-memory routine */
0, /*tp_is_gc For PyObject_IS_GC */
0, /*tp_bases*/
0, /*tp_mro method resolution order */
0, /*tp_cache*/
0, /*tp_subclasses*/
0 /*tp_weaklist*/
};
/** module-level functions **/
PyObject *
psyco_Float(PyObject *module, PyObject *args)
{
PyObject *obj;
if (!PyArg_ParseTuple(args, "O", &obj))
return NULL;
return PyObject_CallFunction((PyObject *)&pfloatType, "O", obj);
}

54
psycopg/adapter_pfloat.h Normal file
View File

@ -0,0 +1,54 @@
/* adapter_pfloat.h - definition for the psycopg float type wrapper
*
* Copyright (C) 2003-2004 Federico Di Gregorio <fog@debian.org>
*
* This file is part of psycopg.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef PSYCOPG_PFLOAT_H
#define PSYCOPG_PFLOAT_H 1
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include "psycopg/config.h"
#ifdef __cplusplus
extern "C" {
#endif
extern HIDDEN PyTypeObject pfloatType;
typedef struct {
PyObject_HEAD
/* this is the real object we wrap */
PyObject *wrapped;
} pfloatObject;
/* functions exported to psycopgmodule.c */
HIDDEN PyObject *psyco_Float(PyObject *module, PyObject *args);
#define psyco_Float_doc \
"Float(obj) -> new float value"
#ifdef __cplusplus
}
#endif
#endif /* !defined(PSYCOPG_PFLOAT_H) */

View File

@ -36,6 +36,7 @@
#include "psycopg/adapter_qstring.h"
#include "psycopg/adapter_binary.h"
#include "psycopg/adapter_pboolean.h"
#include "psycopg/adapter_pfloat.h"
#include "psycopg/adapter_asis.h"
#include "psycopg/adapter_list.h"
#include "psycopg/typecast_binary.h"
@ -271,7 +272,7 @@ psyco_adapters_init(PyObject *mod)
{
PyObject *call;
microprotocols_add(&PyFloat_Type, NULL, (PyObject*)&asisType);
microprotocols_add(&PyFloat_Type, NULL, (PyObject*)&pfloatType);
microprotocols_add(&PyInt_Type, NULL, (PyObject*)&asisType);
microprotocols_add(&PyLong_Type, NULL, (PyObject*)&asisType);
microprotocols_add(&PyBool_Type, NULL, (PyObject*)&pbooleanType);
@ -629,6 +630,8 @@ static PyMethodDef psycopgMethods[] = {
{"QuotedString", (PyCFunction)psyco_QuotedString,
METH_VARARGS, psyco_QuotedString_doc},
{"Boolean", (PyCFunction)psyco_Boolean,
METH_VARARGS, psyco_Float_doc},
{"Float", (PyCFunction)psyco_Float,
METH_VARARGS, psyco_Boolean_doc},
{"Binary", (PyCFunction)psyco_Binary,
METH_VARARGS, psyco_Binary_doc},
@ -693,6 +696,7 @@ init_psycopg(void)
binaryType.ob_type = &PyType_Type;
isqlquoteType.ob_type = &PyType_Type;
pbooleanType.ob_type = &PyType_Type;
pfloatType.ob_type = &PyType_Type;
asisType.ob_type = &PyType_Type;
listType.ob_type = &PyType_Type;
chunkType.ob_type = &PyType_Type;
@ -704,6 +708,7 @@ init_psycopg(void)
if (PyType_Ready(&binaryType) == -1) return;
if (PyType_Ready(&isqlquoteType) == -1) return;
if (PyType_Ready(&pbooleanType) == -1) return;
if (PyType_Ready(&pfloatType) == -1) return;
if (PyType_Ready(&asisType) == -1) return;
if (PyType_Ready(&listType) == -1) return;
if (PyType_Ready(&chunkType) == -1) return;
@ -804,6 +809,7 @@ init_psycopg(void)
binaryType.tp_alloc = PyType_GenericAlloc;
isqlquoteType.tp_alloc = PyType_GenericAlloc;
pbooleanType.tp_alloc = PyType_GenericAlloc;
pfloatType.tp_alloc = PyType_GenericAlloc;
connectionType.tp_alloc = PyType_GenericAlloc;
asisType.tp_alloc = PyType_GenericAlloc;
qstringType.tp_alloc = PyType_GenericAlloc;

View File

@ -142,6 +142,8 @@
<File name="psycopg/lobject_type.c" subtype="Code" buildaction="Compile" />
<File name="psycopg/typecast_basic.c.old" subtype="Code" buildaction="Nothing" />
<File name="sandbox/test_isready_connection_closed.py" subtype="Code" buildaction="Nothing" />
<File name="psycopg/adapter_pfloat.c" subtype="Code" buildaction="Compile" />
<File name="psycopg/adapter_pfloat.h" subtype="Code" buildaction="Nothing" />
</Contents>
<compiler ctype="GccCompiler" />
<MonoDevelop.ChangeLogAddIn.ChangeLogInfo policy="UpdateNearestChangeLog" />

View File

@ -11,6 +11,6 @@
<Execute type="None" entry="psycopg2" />
</StartMode>
<Entries>
<Entry filename="./psycopg2.mdp" />
<Entry filename="psycopg2.mdp" />
</Entries>
</Combine>

View File

@ -316,7 +316,8 @@ sources = [
'connection_type.c', 'connection_int.c', 'cursor_type.c', 'cursor_int.c',
'lobject_type.c', 'lobject_int.c',
'adapter_qstring.c', 'adapter_pboolean.c', 'adapter_binary.c',
'adapter_asis.c', 'adapter_list.c', 'adapter_datetime.c', 'utils.c']
'adapter_asis.c', 'adapter_list.c', 'adapter_datetime.c', 'adapter_pfloat.c',
'utils.c']
parser = ConfigParser.ConfigParser()
parser.read('setup.cfg')

View File

@ -60,6 +60,14 @@ class TypesBasicTests(unittest.TestCase):
self.failUnless(abs(s - 19.10) < 0.001,
"wrong float quoting: " + str(s))
def testFloat(self):
s = self.execute("SELECT %s AS foo", (float("nan"),))
self.failUnless(str(s) == "nan", "wrong float quoting: " + str(s))
self.failUnless(type(s) == float, "wrong float conversion: " + repr(s))
s = self.execute("SELECT %s AS foo", (float("inf"),))
self.failUnless(str(s) == "inf", "wrong float quoting: " + str(s))
self.failUnless(type(s) == float, "wrong float conversion: " + repr(s))
def testBinary(self):
s = ''.join([chr(x) for x in range(256)])
b = psycopg2.Binary(s)