summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog12
-rw-r--r--psycopg/cursor.h1
-rw-r--r--psycopg/cursor_type.c1
-rw-r--r--psycopg/typecast.c73
-rw-r--r--psycopg/typecast.h1
-rw-r--r--psycopg/typecast_array.c92
-rw-r--r--psycopg/typecast_basic.c32
-rw-r--r--setup.cfg2
8 files changed, 184 insertions, 30 deletions
diff --git a/ChangeLog b/ChangeLog
index efeb534..2856d32 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,15 @@
+2005-03-23 Federico Di Gregorio <fog@debian.org>
+
+ * psycopg/typecast_basic.c: all the basic casters now respect the
+ passed string length.
+
+ * psycopg/typecast.c (typecast_cast): set curs->caster to self
+ during the type-casting.
+
+ * psycopg/cursor_type.c: added "typecaster" attribute to the
+ cursor (this is safe, cursors can't be shared among threads and
+ the attribute is RO.)
+
2005-03-22 Federico Di Gregorio <fog@debian.org>
* psycopg/typecast_array.c: added some more structure to implement
diff --git a/psycopg/cursor.h b/psycopg/cursor.h
index 37b3ebb..496c68b 100644
--- a/psycopg/cursor.h
+++ b/psycopg/cursor.h
@@ -56,6 +56,7 @@ typedef struct {
Oid lastoid; /* last oid from an insert or InvalidOid */
PyObject *casts; /* an array (tuple) of typecast functions */
+ PyObject *caster; /* the current typecaster object */
PyObject *copyfile; /* file-like used during COPY TO/FROM ops */
long int copysize; /* size of the copy buffer during COPY TO/FROM ops */
diff --git a/psycopg/cursor_type.c b/psycopg/cursor_type.c
index 08895bc..4d066a4 100644
--- a/psycopg/cursor_type.c
+++ b/psycopg/cursor_type.c
@@ -1217,6 +1217,7 @@ static struct PyMemberDef cursorObject_members[] = {
{"query", T_STRING, OFFSETOF(query), RO},
{"row_factory", T_OBJECT, OFFSETOF(tuple_factory), 0},
{"tzinfo_factory", T_OBJECT, OFFSETOF(tzinfo_factory), 0},
+ {"typecaster", T_OBJECT, OFFSETOF(caster), RO},
#endif
{NULL}
};
diff --git a/psycopg/typecast.c b/psycopg/typecast.c
index 78e873c..da13cb8 100644
--- a/psycopg/typecast.c
+++ b/psycopg/typecast.c
@@ -154,6 +154,25 @@ typecast_init(PyObject *dict)
return 0;
}
+/* typecast_get_by_name - get a type object by name (slow!) */
+
+static PyObject*
+typecast_get_by_name(unsigned char *name)
+{
+ PyObject *value, *res = NULL;
+ int ppos = 0;
+
+ while (PyDict_Next(psyco_types, &ppos, NULL, &value)) {
+ if (strcmp(PyString_AsString(((typecastObject*)value)->name),
+ name) == 0) {
+ res = value;
+ break;
+ }
+ }
+
+ /* borrowed reference */
+ return res;
+}
/* typecast_add - add a type object to the dictionary */
int
@@ -179,6 +198,8 @@ typecast_add(PyObject *obj, int binary)
}
}
+ Dprintf("typecast_add: base caster: %p", type->bcast);
+
return 0;
}
@@ -196,7 +217,7 @@ static struct memberlist typecastObject_memberlist[] = {
/* numeric methods */
static PyObject *
-typecast_new(PyObject *name, PyObject *values, PyObject *cast);
+typecast_new(PyObject *name, PyObject *values, PyObject *cast, PyObject *base);
static int
typecast_coerce(PyObject **pv, PyObject **pw)
@@ -207,7 +228,7 @@ typecast_coerce(PyObject **pv, PyObject **pw)
args = PyTuple_New(1);
Py_INCREF(*pw);
PyTuple_SET_ITEM(args, 0, *pw);
- coer = typecast_new(NULL, args, NULL);
+ coer = typecast_new(NULL, args, NULL, NULL);
*pw = coer;
Py_DECREF(args);
Py_INCREF(*pv);
@@ -347,7 +368,7 @@ PyTypeObject typecastType = {
};
static PyObject *
-typecast_new(PyObject *name, PyObject *values, PyObject *cast)
+typecast_new(PyObject *name, PyObject *values, PyObject *cast, PyObject *base)
{
typecastObject *obj;
@@ -368,7 +389,11 @@ typecast_new(PyObject *name, PyObject *values, PyObject *cast)
obj->pcast = NULL;
obj->ccast = NULL;
-
+ obj->bcast = base;
+
+ if (obj->bcast) Py_INCREF(obj->bcast);
+
+ /* FIXME: raise an exception when None is passed as Python caster */
if (cast && cast != Py_None) {
Py_INCREF(cast);
obj->pcast = cast;
@@ -382,27 +407,36 @@ typecast_new(PyObject *name, PyObject *values, PyObject *cast)
PyObject *
typecast_from_python(PyObject *self, PyObject *args, PyObject *keywds)
{
- PyObject *v, *name, *cast = NULL;
+ PyObject *v, *name, *cast = NULL, *base = NULL;
- static char *kwlist[] = {"values", "name", "castobj", NULL};
+ static char *kwlist[] = {"values", "name", "castobj", "baseobj", NULL};
- if (!PyArg_ParseTupleAndKeywords(args, keywds, "O!|O!O", kwlist,
+ if (!PyArg_ParseTupleAndKeywords(args, keywds, "O!|O!OO", kwlist,
&PyTuple_Type, &v,
&PyString_Type, &name,
- &cast)) {
+ &cast, &base)) {
return NULL;
}
- return typecast_new(name, v, cast);
+ return typecast_new(name, v, cast, base);
}
PyObject *
typecast_from_c(typecastObject_initlist *type)
{
- PyObject *tuple;
+ PyObject *tuple, *base = NULL;
typecastObject *obj;
int i, len = 0;
+ /* before doing anything else we look for the base */
+ if (type->base) {
+ base = typecast_get_by_name(type->base);
+ if (!base) {
+ PyErr_Format(Error, "typecast base not found: %s", type->base);
+ return NULL;
+ }
+ }
+
while (type->values[len] != 0) len++;
tuple = PyTuple_New(len);
@@ -411,9 +445,10 @@ typecast_from_c(typecastObject_initlist *type)
for (i = 0; i < len ; i++) {
PyTuple_SET_ITEM(tuple, i, PyInt_FromLong(type->values[i]));
}
-
+
+
obj = (typecastObject *)
- typecast_new(PyString_FromString(type->name), tuple, NULL);
+ typecast_new(PyString_FromString(type->name), tuple, NULL, base);
if (obj) {
obj->ccast = type->cast;
@@ -425,18 +460,26 @@ typecast_from_c(typecastObject_initlist *type)
PyObject *
typecast_cast(PyObject *obj, unsigned char *str, int len, PyObject *curs)
{
+ PyObject *old, *res = NULL;
typecastObject *self = (typecastObject *)obj;
+ /* we don't incref, the caster *can't* die at this point */
+ old = ((cursorObject*)curs)->caster;
+ ((cursorObject*)curs)->caster = obj;
+
if (self->ccast) {
Dprintf("typecast_call: calling C cast function");
- return self->ccast(str, len, curs);
+ res = self->ccast(str, len, curs);
}
else if (self->pcast) {
Dprintf("typecast_call: calling python callable");
- return PyObject_CallFunction(self->pcast, "s#O", str, len, curs);
+ res = PyObject_CallFunction(self->pcast, "s#O", str, len, curs);
}
else {
PyErr_SetString(Error, "internal error: no casting function found");
- return NULL;
}
+
+ ((cursorObject*)curs)->caster = old;
+
+ return res;
}
diff --git a/psycopg/typecast.h b/psycopg/typecast.h
index 3e5a4c4..09e1dae 100644
--- a/psycopg/typecast.h
+++ b/psycopg/typecast.h
@@ -43,6 +43,7 @@ typedef struct {
typecast_function ccast; /* the C casting function */
PyObject *pcast; /* the python casting function */
+ PyObject *bcast; /* base cast, used by array typecasters */
} typecastObject;
/* the initialization values are stored here */
diff --git a/psycopg/typecast_array.c b/psycopg/typecast_array.c
index d4a982a..a949673 100644
--- a/psycopg/typecast_array.c
+++ b/psycopg/typecast_array.c
@@ -20,23 +20,97 @@
*/
-/* the pointer to the datetime module API is initialized by the module init
- code, we just need to grab it */
-extern PyObject* pyDateTimeModuleP;
-extern PyObject *pyDateTypeP;
-extern PyObject *pyTimeTypeP;
-extern PyObject *pyDateTimeTypeP;
-extern PyObject *pyDeltaTypeP;
+/** typecast_array_scan - scan a string looking for array items **/
+
+#define ASCAN_EOF 0
+#define ASCAN_BEGIN 1
+#define ASCAN_END 2
+#define ASCAN_TOKEN 3
+
+static int
+typecast_array_tokenize(unsigned char *str, int strlength,
+ int *pos, unsigned char** token, int *length)
+{
+ int i = *pos;
+
+ Dprintf("TOKENIZE for %d, pos = %d", strlength, *pos);
+
+ while (i < strlength) {
+ switch (str[i]) {
+ case '{':
+ *pos = i+1;
+ return ASCAN_BEGIN;
+
+ case '}':
+ *pos = i+1;
+ return ASCAN_END;
+
+ case ',':
+ *token = &str[*pos];
+ *length = i - *pos;
+ *pos = i+1;
+ Dprintf("TOKENIZE pos = %d, length = %d", *pos, *length);
+ return ASCAN_TOKEN;
+
+ default:
+ i++;
+ }
+ }
+
+ *token = &str[*pos];
+ *length = i - *pos;
+
+ return ASCAN_EOF;
+}
+
+static int
+typecast_array_scan(unsigned char *str, int strlength,
+ PyObject *curs, PyObject *base, PyObject *array)
+{
+ int state, length, pos = 0;
+ unsigned char *token;
+
+ while (1) {
+ state = typecast_array_tokenize(str, strlength, &pos, &token, &length);
+
+ if (state == ASCAN_TOKEN || state == ASCAN_EOF) {
+ PyObject *obj = typecast_cast(base, token, length, curs);
+ if (obj == NULL) return 0;
+ PyList_Append(array, obj);
+ Py_DECREF(obj);
+ }
+ else {
+ Dprintf("** RECURSION (not supported right now)!!");
+ }
+
+ if (state == ASCAN_EOF) break;
+ }
+
+ return 1;
+}
/** LONGINTEGERARRAY and INTEGERARRAY - cast integers arrays **/
static PyObject *
typecast_INTEGERARRAY_cast(unsigned char *str, int len, PyObject *curs)
{
- PyObject* obj = NULL;
-
+ PyObject *obj = NULL;
+ PyObject *base = ((typecastObject*)((cursorObject*)curs)->caster)->bcast;
+
if (str == NULL) {Py_INCREF(Py_None); return Py_None;}
+ if (str[0] != '{') {
+ PyErr_SetString(Error, "array does not start with '{'");
+ return NULL;
+ }
+
+ obj = PyList_New(0);
+ /* scan the array skipping the first level of {} */
+ if (typecast_array_scan(&str[1], len-2, curs, base, obj) == 0) {
+ Py_DECREF(obj);
+ obj = NULL;
+ }
+
return obj;
}
diff --git a/psycopg/typecast_basic.c b/psycopg/typecast_basic.c
index b6e6260..f9a2fb3 100644
--- a/psycopg/typecast_basic.c
+++ b/psycopg/typecast_basic.c
@@ -27,8 +27,11 @@
static PyObject *
typecast_INTEGER_cast(unsigned char *s, int len, PyObject *curs)
{
+ unsigned char buffer[12];
+
if (s == NULL) {Py_INCREF(Py_None); return Py_None;}
- return PyInt_FromString(s, NULL, 0);
+ strncpy(buffer, s, len); buffer[len] = '\0';
+ return PyInt_FromString(buffer, NULL, 0);
}
/** LONGINTEGER - cast long integers (8 bytes) to python long **/
@@ -36,7 +39,10 @@ typecast_INTEGER_cast(unsigned char *s, int len, PyObject *curs)
static PyObject *
typecast_LONGINTEGER_cast(unsigned char *s, int len, PyObject *curs)
{
+ unsigned char buffer[24];
+
if (s == NULL) {Py_INCREF(Py_None); return Py_None;}
+ strncpy(buffer, s, len); buffer[len] = '\0';
return PyLong_FromString(s, NULL, 0);
}
@@ -45,7 +51,10 @@ typecast_LONGINTEGER_cast(unsigned char *s, int len, PyObject *curs)
static PyObject *
typecast_FLOAT_cast(unsigned char *s, int len, PyObject *curs)
{
+ unsigned char buffer[64];
+
if (s == NULL) {Py_INCREF(Py_None); return Py_None;}
+ strncpy(buffer, s, len); buffer[len] = '\0';
return PyFloat_FromDouble(atof(s));
}
@@ -70,7 +79,7 @@ typecast_UNICODE_cast(unsigned char *s, int len, PyObject *curs)
enc = PyDict_GetItemString(psycoEncodings,
((cursorObject*)curs)->conn->encoding);
if (enc) {
- return PyUnicode_Decode(s, strlen(s), PyString_AsString(enc), NULL);
+ return PyUnicode_Decode(s, len, PyString_AsString(enc), NULL);
}
else {
PyErr_Format(InterfaceError,
@@ -135,13 +144,17 @@ static PyObject *
typecast_BINARY_cast(unsigned char *s, int l, PyObject *curs)
{
PyObject *res;
- unsigned char *str;
+ unsigned char *str, saved;
size_t len;
if (s == NULL) {Py_INCREF(Py_None); return Py_None;}
-
+
+ /* PQunescapeBytea absolutely wants a 0-terminated string and we don't
+ want to copy the whole buffer, right? Wrong... :/ */
+ saved = s[l]; s[l] = '\0';
str = PQunescapeBytea(s, &len);
Dprintf("typecast_BINARY_cast: unescaped %d bytes", len);
+ s[l] = saved;
/* TODO: using a PyBuffer would make this a zero-copy operation but we'll
need to define our own buffer-derived object to keep a reference to the
@@ -178,8 +191,17 @@ typecast_BOOLEAN_cast(unsigned char *s, int len, PyObject *curs)
static PyObject *
typecast_DECIMAL_cast(unsigned char *s, int len, PyObject *curs)
{
+ PyObject *res = NULL;
+ unsigned char *buffer;
+
if (s == NULL) {Py_INCREF(Py_None); return Py_None;}
- return PyObject_CallFunction(decimalType, "s", s);
+
+ buffer = PyMem_Malloc(len+1);
+ strncpy(buffer, s, len); buffer[len] = '\0';
+ res = PyObject_CallFunction(decimalType, "s", buffer);
+ PyMem_Free(buffer);
+
+ return res;
}
#else
#define typecast_DECIMAL_cast typecast_FLOAT_cast
diff --git a/setup.cfg b/setup.cfg
index 46bcbc0..7409c61 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,5 +1,5 @@
[build_ext]
-define=PSYCOPG_EXTENSIONS,PSYCOPG_DISPLAY_SIZE,HAVE_PQFREEMEM,HAVE_PQPROTOCOL3
+define=PSYCOPG_EXTENSIONS,PSYCOPG_DISPLAY_SIZE,HAVE_PQFREEMEM,HAVE_PQPROTOCOL3,PSYCOPG_DEBUG
# PSYCOPG_DEBUG can be added to enable verbose debug information
# PSYCOPG_OWN_QUOTING can be added above but it is deprecated