mirror of
				https://github.com/psycopg/psycopg2.git
				synced 2025-10-31 15:57:31 +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); | ||||
|  | @ -412,8 +446,9 @@ typecast_from_c(typecastObject_initlist *type) | |||
|         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,22 +20,96 @@ | |||
|  */ | ||||
| 
 | ||||
| 
 | ||||
| /* 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