summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--NEWS7
-rw-r--r--lib/extensions.py2
-rw-r--r--psycopg/adapter_pdecimal.c35
-rw-r--r--psycopg/adapter_pfloat.c25
-rw-r--r--psycopg/adapter_pint.c266
-rw-r--r--psycopg/adapter_pint.h53
-rw-r--r--psycopg/psycopgmodule.c10
-rw-r--r--psycopg/python.h2
-rw-r--r--setup.py4
-rwxr-xr-xtests/types_basic.py10
10 files changed, 401 insertions, 13 deletions
diff --git a/NEWS b/NEWS
index 0037c3b..08c01f2 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,10 @@
+What's new in psycopg 2.4.2
+---------------------------
+
+ - Fixed escape for negative numbers prefixed by minus operator
+ (ticket #57).
+
+
What's new in psycopg 2.4.1
---------------------------
diff --git a/lib/extensions.py b/lib/extensions.py
index 82e17fa..838d869 100644
--- a/lib/extensions.py
+++ b/lib/extensions.py
@@ -39,7 +39,7 @@ from psycopg2._psycopg import DECIMALARRAY, FLOATARRAY, INTEGERARRAY, INTERVALAR
from psycopg2._psycopg import LONGINTEGERARRAY, ROWIDARRAY, STRINGARRAY, TIMEARRAY
from psycopg2._psycopg import UNICODEARRAY
-from psycopg2._psycopg import Binary, Boolean, Float, QuotedString, AsIs
+from psycopg2._psycopg import Binary, Boolean, Int, Float, QuotedString, AsIs
try:
from psycopg2._psycopg import MXDATE, MXDATETIME, MXINTERVAL, MXTIME
from psycopg2._psycopg import MXDATEARRAY, MXDATETIMEARRAY, MXINTERVALARRAY, MXTIMEARRAY
diff --git a/psycopg/adapter_pdecimal.c b/psycopg/adapter_pdecimal.c
index 9b57346..e14e769 100644
--- a/psycopg/adapter_pdecimal.c
+++ b/psycopg/adapter_pdecimal.c
@@ -41,8 +41,10 @@ pdecimal_getquoted(pdecimalObject *self, PyObject *args)
PyObject *check, *res = NULL;
check = PyObject_CallMethod(self->wrapped, "is_finite", NULL);
if (check == Py_True) {
- res = PyObject_Str(self->wrapped);
- goto end;
+ if (!(res = PyObject_Str(self->wrapped))) {
+ goto end;
+ }
+ goto output;
}
else if (check) {
res = Bytes_FromString("'NaN'::numeric");
@@ -70,16 +72,39 @@ pdecimal_getquoted(pdecimalObject *self, PyObject *args)
goto end;
}
- res = PyObject_Str(self->wrapped);
+ /* wrapped is finite */
+ if (!(res = PyObject_Str(self->wrapped))) {
+ goto end;
+ }
+
+ /* res may be unicode and may suffer for issue #57 */
+output:
+
#if PY_MAJOR_VERSION > 2
/* unicode to bytes in Py3 */
- if (res) {
+ {
PyObject *tmp = PyUnicode_AsUTF8String(res);
Py_DECREF(res);
- res = tmp;
+ if (!(res = tmp)) {
+ goto end;
+ }
}
#endif
+ if ('-' == Bytes_AS_STRING(res)[0]) {
+ /* Prepend a space in front of negative numbers (ticket #57) */
+ PyObject *tmp;
+ if (!(tmp = Bytes_FromString(" "))) {
+ Py_DECREF(res);
+ res = NULL;
+ goto end;
+ }
+ Bytes_ConcatAndDel(&tmp, res);
+ if (!(res = tmp)) {
+ goto end;
+ }
+ }
+
end:
Py_XDECREF(check);
return res;
diff --git a/psycopg/adapter_pfloat.c b/psycopg/adapter_pfloat.c
index 715ed8f..1b8074f 100644
--- a/psycopg/adapter_pfloat.c
+++ b/psycopg/adapter_pfloat.c
@@ -49,18 +49,37 @@ pfloat_getquoted(pfloatObject *self, PyObject *args)
rv = Bytes_FromString("'-Infinity'::float");
}
else {
- rv = PyObject_Repr(self->wrapped);
+ if (!(rv = PyObject_Repr(self->wrapped))) {
+ goto exit;
+ }
#if PY_MAJOR_VERSION > 2
/* unicode to bytes in Py3 */
- if (rv) {
+ {
PyObject *tmp = PyUnicode_AsUTF8String(rv);
Py_DECREF(rv);
- rv = tmp;
+ if (!(rv = tmp)) {
+ goto exit;
+ }
}
#endif
+
+ if ('-' == Bytes_AS_STRING(rv)[0]) {
+ /* Prepend a space in front of negative numbers (ticket #57) */
+ PyObject *tmp;
+ if (!(tmp = Bytes_FromString(" "))) {
+ Py_DECREF(rv);
+ rv = NULL;
+ goto exit;
+ }
+ Bytes_ConcatAndDel(&tmp, rv);
+ if (!(rv = tmp)) {
+ goto exit;
+ }
+ }
}
+exit:
return rv;
}
diff --git a/psycopg/adapter_pint.c b/psycopg/adapter_pint.c
new file mode 100644
index 0000000..ad89a06
--- /dev/null
+++ b/psycopg/adapter_pint.c
@@ -0,0 +1,266 @@
+/* adapter_int.c - psycopg pint type wrapper implementation
+ *
+ * Copyright (C) 2011 Daniele Varrazzo <daniele.varrazzo@gmail.com>
+ *
+ * This file is part of psycopg.
+ *
+ * psycopg2 is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link this program with the OpenSSL library (or with
+ * modified versions of OpenSSL that use the same license as OpenSSL),
+ * and distribute linked combinations including the two.
+ *
+ * You must obey the GNU Lesser General Public License in all respects for
+ * all of the code used other than OpenSSL.
+ *
+ * psycopg2 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 Lesser General Public
+ * License for more details.
+ */
+
+#define PSYCOPG_MODULE
+#include "psycopg/psycopg.h"
+
+#include "psycopg/adapter_pint.h"
+#include "psycopg/microprotocols_proto.h"
+
+
+/** the Int object **/
+
+static PyObject *
+pint_getquoted(pintObject *self, PyObject *args)
+{
+ PyObject *res;
+ if (!(res = PyObject_Str(self->wrapped))) {
+ goto exit;
+ }
+
+#if PY_MAJOR_VERSION > 2
+ /* unicode to bytes in Py3 */
+ {
+ PyObject *tmp = PyUnicode_AsUTF8String(res);
+ Py_DECREF(res);
+ if (!(res = tmp)) {
+ goto exit;
+ }
+ }
+#endif
+
+ if ('-' == Bytes_AS_STRING(res)[0]) {
+ /* Prepend a space in front of negative numbers (ticket #57) */
+ PyObject *tmp;
+ if (!(tmp = Bytes_FromString(" "))) {
+ Py_DECREF(res);
+ res = NULL;
+ goto exit;
+ }
+ Bytes_ConcatAndDel(&tmp, res);
+ if (!(res = tmp)) {
+ goto exit;
+ }
+ }
+
+exit:
+ return res;
+}
+
+static PyObject *
+pint_str(pintObject *self)
+{
+ return psycopg_ensure_text(pint_getquoted(self, NULL));
+}
+
+static PyObject *
+pint_conform(pintObject *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 int object */
+
+/* object member list */
+
+static struct PyMemberDef pintObject_members[] = {
+ {"adapted", T_OBJECT, offsetof(pintObject, wrapped), READONLY},
+ {NULL}
+};
+
+/* object method table */
+
+static PyMethodDef pintObject_methods[] = {
+ {"getquoted", (PyCFunction)pint_getquoted, METH_NOARGS,
+ "getquoted() -> wrapped object value as SQL-quoted string"},
+ {"__conform__", (PyCFunction)pint_conform, METH_VARARGS, NULL},
+ {NULL} /* Sentinel */
+};
+
+/* initialization and finalization methods */
+
+static int
+pint_setup(pintObject *self, PyObject *obj)
+{
+ Dprintf("pint_setup: init pint object at %p, refcnt = "
+ FORMAT_CODE_PY_SSIZE_T,
+ self, Py_REFCNT(self)
+ );
+
+ Py_INCREF(obj);
+ self->wrapped = obj;
+
+ Dprintf("pint_setup: good pint object at %p, refcnt = "
+ FORMAT_CODE_PY_SSIZE_T,
+ self, Py_REFCNT(self)
+ );
+ return 0;
+}
+
+static int
+pint_traverse(PyObject *obj, visitproc visit, void *arg)
+{
+ pintObject *self = (pintObject *)obj;
+
+ Py_VISIT(self->wrapped);
+ return 0;
+}
+
+static void
+pint_dealloc(PyObject* obj)
+{
+ pintObject *self = (pintObject *)obj;
+
+ Py_CLEAR(self->wrapped);
+
+ Dprintf("pint_dealloc: deleted pint object at %p, refcnt = "
+ FORMAT_CODE_PY_SSIZE_T,
+ obj, Py_REFCNT(obj)
+ );
+
+ Py_TYPE(obj)->tp_free(obj);
+}
+
+static int
+pint_init(PyObject *obj, PyObject *args, PyObject *kwds)
+{
+ PyObject *o;
+
+ if (!PyArg_ParseTuple(args, "O", &o))
+ return -1;
+
+ return pint_setup((pintObject *)obj, o);
+}
+
+static PyObject *
+pint_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+ return type->tp_alloc(type, 0);
+}
+
+static void
+pint_del(PyObject* self)
+{
+ PyObject_GC_Del(self);
+}
+
+static PyObject *
+pint_repr(pintObject *self)
+{
+ return PyString_FromFormat("<psycopg2._psycopg.Int object at %p>",
+ self);
+}
+
+
+/* object type */
+
+#define pintType_doc \
+"Int(str) -> new Int adapter object"
+
+PyTypeObject pintType = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "psycopg2._psycopg.Int",
+ sizeof(pintObject),
+ 0,
+ pint_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+
+ 0, /*tp_compare*/
+
+ (reprfunc)pint_repr, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+
+ 0, /*tp_call*/
+ (reprfunc)pint_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*/
+ pintType_doc, /*tp_doc*/
+
+ pint_traverse, /*tp_traverse*/
+ 0, /*tp_clear*/
+
+ 0, /*tp_richcompare*/
+ 0, /*tp_weaklistoffset*/
+
+ 0, /*tp_iter*/
+ 0, /*tp_iternext*/
+
+ /* Attribute descriptor and subclassing stuff */
+
+ pintObject_methods, /*tp_methods*/
+ pintObject_members, /*tp_members*/
+ 0, /*tp_getset*/
+ 0, /*tp_base*/
+ 0, /*tp_dict*/
+
+ 0, /*tp_descr_get*/
+ 0, /*tp_descr_set*/
+ 0, /*tp_dictoffset*/
+
+ pint_init, /*tp_init*/
+ 0, /*tp_alloc will be set to PyType_GenericAlloc in module init*/
+ pint_new, /*tp_new*/
+ (freefunc)pint_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_Int(PyObject *module, PyObject *args)
+{
+ PyObject *obj;
+
+ if (!PyArg_ParseTuple(args, "O", &obj))
+ return NULL;
+
+ return PyObject_CallFunctionObjArgs((PyObject *)&pintType, obj, NULL);
+}
diff --git a/psycopg/adapter_pint.h b/psycopg/adapter_pint.h
new file mode 100644
index 0000000..fd553e8
--- /dev/null
+++ b/psycopg/adapter_pint.h
@@ -0,0 +1,53 @@
+/* adapter_pint.h - definition for the psycopg int type wrapper
+ *
+ * Copyright (C) 2011 Daniele Varrazzo <daniele.varrazzo@gmail.com>
+ *
+ * This file is part of psycopg.
+ *
+ * psycopg2 is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link this program with the OpenSSL library (or with
+ * modified versions of OpenSSL that use the same license as OpenSSL),
+ * and distribute linked combinations including the two.
+ *
+ * You must obey the GNU Lesser General Public License in all respects for
+ * all of the code used other than OpenSSL.
+ *
+ * psycopg2 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 Lesser General Public
+ * License for more details.
+ */
+
+#ifndef PSYCOPG_PINT_H
+#define PSYCOPG_PINT_H 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern HIDDEN PyTypeObject pintType;
+
+typedef struct {
+ PyObject_HEAD
+
+ /* this is the real object we wrap */
+ PyObject *wrapped;
+
+} pintObject;
+
+/* functions exported to psycopgmodule.c */
+
+HIDDEN PyObject *psyco_Int(PyObject *module, PyObject *args);
+#define psyco_Int_doc \
+ "Int(obj) -> new int value"
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !defined(PSYCOPG_PINT_H) */
diff --git a/psycopg/psycopgmodule.c b/psycopg/psycopgmodule.c
index 0106941..99d7c6c 100644
--- a/psycopg/psycopgmodule.c
+++ b/psycopg/psycopgmodule.c
@@ -39,6 +39,7 @@
#include "psycopg/adapter_qstring.h"
#include "psycopg/adapter_binary.h"
#include "psycopg/adapter_pboolean.h"
+#include "psycopg/adapter_pint.h"
#include "psycopg/adapter_pfloat.h"
#include "psycopg/adapter_pdecimal.h"
#include "psycopg/adapter_asis.h"
@@ -316,9 +317,9 @@ psyco_adapters_init(PyObject *mod)
microprotocols_add(&PyFloat_Type, NULL, (PyObject*)&pfloatType);
#if PY_MAJOR_VERSION < 3
- microprotocols_add(&PyInt_Type, NULL, (PyObject*)&asisType);
+ microprotocols_add(&PyInt_Type, NULL, (PyObject*)&pintType);
#endif
- microprotocols_add(&PyLong_Type, NULL, (PyObject*)&asisType);
+ microprotocols_add(&PyLong_Type, NULL, (PyObject*)&pintType);
microprotocols_add(&PyBool_Type, NULL, (PyObject*)&pbooleanType);
/* strings */
@@ -758,6 +759,8 @@ static PyMethodDef psycopgMethods[] = {
METH_VARARGS, psyco_QuotedString_doc},
{"Boolean", (PyCFunction)psyco_Boolean,
METH_VARARGS, psyco_Float_doc},
+ {"Int", (PyCFunction)psyco_Int,
+ METH_VARARGS, psyco_Int_doc},
{"Float", (PyCFunction)psyco_Float,
METH_VARARGS, psyco_Decimal_doc},
{"Decimal", (PyCFunction)psyco_Decimal,
@@ -848,6 +851,7 @@ INIT_MODULE(_psycopg)(void)
Py_TYPE(&binaryType) = &PyType_Type;
Py_TYPE(&isqlquoteType) = &PyType_Type;
Py_TYPE(&pbooleanType) = &PyType_Type;
+ Py_TYPE(&pintType) = &PyType_Type;
Py_TYPE(&pfloatType) = &PyType_Type;
Py_TYPE(&pdecimalType) = &PyType_Type;
Py_TYPE(&asisType) = &PyType_Type;
@@ -863,6 +867,7 @@ INIT_MODULE(_psycopg)(void)
if (PyType_Ready(&binaryType) == -1) goto exit;
if (PyType_Ready(&isqlquoteType) == -1) goto exit;
if (PyType_Ready(&pbooleanType) == -1) goto exit;
+ if (PyType_Ready(&pintType) == -1) goto exit;
if (PyType_Ready(&pfloatType) == -1) goto exit;
if (PyType_Ready(&pdecimalType) == -1) goto exit;
if (PyType_Ready(&asisType) == -1) goto exit;
@@ -978,6 +983,7 @@ INIT_MODULE(_psycopg)(void)
binaryType.tp_alloc = PyType_GenericAlloc;
isqlquoteType.tp_alloc = PyType_GenericAlloc;
pbooleanType.tp_alloc = PyType_GenericAlloc;
+ pintType.tp_alloc = PyType_GenericAlloc;
pfloatType.tp_alloc = PyType_GenericAlloc;
pdecimalType.tp_alloc = PyType_GenericAlloc;
connectionType.tp_alloc = PyType_GenericAlloc;
diff --git a/psycopg/python.h b/psycopg/python.h
index fed0303..ed69f4c 100644
--- a/psycopg/python.h
+++ b/psycopg/python.h
@@ -129,6 +129,7 @@ typedef unsigned long Py_uhash_t;
#define Bytes_FromString PyString_FromString
#define Bytes_FromStringAndSize PyString_FromStringAndSize
#define Bytes_FromFormat PyString_FromFormat
+#define Bytes_ConcatAndDel PyString_ConcatAndDel
#define _Bytes_Resize _PyString_Resize
#else
@@ -144,6 +145,7 @@ typedef unsigned long Py_uhash_t;
#define Bytes_FromString PyBytes_FromString
#define Bytes_FromStringAndSize PyBytes_FromStringAndSize
#define Bytes_FromFormat PyBytes_FromFormat
+#define Bytes_ConcatAndDel PyBytes_ConcatAndDel
#define _Bytes_Resize _PyBytes_Resize
#endif
diff --git a/setup.py b/setup.py
index 9ae8117..bb75411 100644
--- a/setup.py
+++ b/setup.py
@@ -414,7 +414,7 @@ sources = [
'adapter_asis.c', 'adapter_binary.c', 'adapter_datetime.c',
'adapter_list.c', 'adapter_pboolean.c', 'adapter_pdecimal.c',
- 'adapter_pfloat.c', 'adapter_qstring.c',
+ 'adapter_pint.c', 'adapter_pfloat.c', 'adapter_qstring.c',
'microprotocols.c', 'microprotocols_proto.c',
'typecast.c',
]
@@ -427,7 +427,7 @@ depends = [
'adapter_asis.h', 'adapter_binary.h', 'adapter_datetime.h',
'adapter_list.h', 'adapter_pboolean.h', 'adapter_pdecimal.h',
- 'adapter_pfloat.h', 'adapter_qstring.h',
+ 'adapter_pint.h', 'adapter_pfloat.h', 'adapter_qstring.h',
'microprotocols.h', 'microprotocols_proto.h',
'typecast.h', 'typecast_binary.h',
diff --git a/tests/types_basic.py b/tests/types_basic.py
index 1ca668d..709907e 100755
--- a/tests/types_basic.py
+++ b/tests/types_basic.py
@@ -275,6 +275,16 @@ class TypesBasicTests(unittest.TestCase):
o2 = self.execute("SELECT %s::bytea AS foo", (o1,))
self.assertEqual(b('x'), o2[0])
+ def testNegNumber(self):
+ d1 = self.execute("select -%s;", (decimal.Decimal('-1.0'),))
+ self.assertEqual(1, d1)
+ f1 = self.execute("select -%s;", (-1.0,))
+ self.assertEqual(1, f1)
+ i1 = self.execute("select -%s;", (-1,))
+ self.assertEqual(1, i1)
+ l1 = self.execute("select -%s;", (-1L,))
+ self.assertEqual(1, l1)
+
class AdaptSubclassTest(unittest.TestCase):
def test_adapt_subtype(self):