mirror of
https://github.com/psycopg/psycopg2.git
synced 2025-01-31 09:24:07 +03:00
Array support works, at least for INTEGERS.
This commit is contained in:
parent
07a38c31cd
commit
def14d5925
12
ChangeLog
12
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
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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}
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user