Array support works, at least for INTEGERS.

This commit is contained in:
Federico Di Gregorio 2005-03-22 16:33:57 +00:00
parent 07a38c31cd
commit def14d5925
8 changed files with 184 additions and 30 deletions

View File

@ -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

View File

@ -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 */

View File

@ -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}
};

View File

@ -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;
}

View File

@ -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 */

View File

@ -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;
}

View File

@ -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

View File

@ -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