Array tokenization seems working.

This commit is contained in:
Federico Di Gregorio 2005-03-23 10:32:30 +00:00
parent cddb1a15d4
commit 19cb161d27
8 changed files with 125 additions and 32 deletions

View File

@ -17,8 +17,17 @@
* scripts/buildtypes.py: new version to include array data. * scripts/buildtypes.py: new version to include array data.
2005-03-15 Federico Di Gregorio <fog@debian.org>
* lib/extensions.py: Added AsIs import.
2005-03-12 Federico Di Gregorio <fog@debian.org> 2005-03-12 Federico Di Gregorio <fog@debian.org>
* psycopg/cursor.h: removed "qattr", not used anymore and added
"cast", holding the typecaster currently in use.
* Release 1.99.13.
* psycopg/cursor_type.c (psyco_curs_executemany): implemented as a * psycopg/cursor_type.c (psyco_curs_executemany): implemented as a
wrapper to extract python arguments and then call wrapper to extract python arguments and then call
_psyco_curs_execute(). _psyco_curs_execute().

View File

@ -1,8 +1,8 @@
recursive-include psycopg *.c *.h recursive-include psycopg *.c *.h
recursive-include lib *.py recursive-include lib *.py
recursive-include tests *.py
recursive-include ZPsycopgDA *.py *.gif *.dtml recursive-include ZPsycopgDA *.py *.gif *.dtml
recursive-include examples *.py somehackers.jpg whereareyou.jpg recursive-include examples *.py somehackers.jpg whereareyou.jpg
#recursive-include tests *.py
recursive-include doc TODO HACKING SUCCESS ChangeLog-1.x recursive-include doc TODO HACKING SUCCESS ChangeLog-1.x
include scripts/maketypes.sh scripts/buildtypes.py include scripts/maketypes.sh scripts/buildtypes.py
include AUTHORS README INSTALL ChangeLog setup.py setup.cfg include AUTHORS README INSTALL ChangeLog setup.py setup.cfg

8
NEWS
View File

@ -1,3 +1,11 @@
What's new in psycopg 1.99.13
-----------------------------
* Added missing .executemany() method.
* Optimized type cast from PostgreSQL to Python (psycopg should be even
faster than before.)
What's new in psycopg 1.99.12 What's new in psycopg 1.99.12
----------------------------- -----------------------------

View File

@ -25,7 +25,7 @@ This module holds all the extensions to the DBAPI-2.0 provided by psycopg:
from _psycopg import UNICODE, INTEGER, LONGINTEGER, BOOLEAN, FLOAT from _psycopg import UNICODE, INTEGER, LONGINTEGER, BOOLEAN, FLOAT
from _psycopg import TIME, DATE, INTERVAL from _psycopg import TIME, DATE, INTERVAL
from _psycopg import Boolean, QuotedString from _psycopg import Boolean, QuotedString, AsIs
try: try:
from _psycopg import DateFromMx, TimeFromMx, TimestampFromMx from _psycopg import DateFromMx, TimeFromMx, TimestampFromMx
from _psycopg import IntervalFromMx from _psycopg import IntervalFromMx

View File

@ -468,11 +468,9 @@ typecast_cast(PyObject *obj, unsigned char *str, int len, PyObject *curs)
((cursorObject*)curs)->caster = obj; ((cursorObject*)curs)->caster = obj;
if (self->ccast) { if (self->ccast) {
Dprintf("typecast_call: calling C cast function");
res = self->ccast(str, len, curs); res = self->ccast(str, len, curs);
} }
else if (self->pcast) { else if (self->pcast) {
Dprintf("typecast_call: calling python callable");
res = PyObject_CallFunction(self->pcast, "s#O", str, len, curs); res = PyObject_CallFunction(self->pcast, "s#O", str, len, curs);
} }
else { else {

View File

@ -19,9 +19,12 @@
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/ */
#define MAX_DIMENSIONS 16
/** typecast_array_scan - scan a string looking for array items **/ /** typecast_array_scan - scan a string looking for array items **/
#define ASCAN_ERROR -1
#define ASCAN_EOF 0 #define ASCAN_EOF 0
#define ASCAN_BEGIN 1 #define ASCAN_BEGIN 1
#define ASCAN_END 2 #define ASCAN_END 2
@ -32,16 +35,20 @@ static int
typecast_array_tokenize(unsigned char *str, int strlength, typecast_array_tokenize(unsigned char *str, int strlength,
int *pos, unsigned char** token, int *length) int *pos, unsigned char** token, int *length)
{ {
int i; int i, l, res = ASCAN_TOKEN;
int quoted = 0; int qs = 0; /* 2 = in quotes, 1 = quotes closed */
/* first we check for quotes, used when the content of the item contains /* first we check for quotes, used when the content of the item contains
special or quoted characters */ special or quoted characters */
if (str[*pos] == '"') { if (str[*pos] == '"') {
quoted = 1; qs = 2;
*pos += 1; *pos += 1;
} }
Dprintf("typecast_array_tokenize: '%s'; %d/%d",
&str[*pos], *pos, strlength);
for (i = *pos ; i < strlength ; i++) { for (i = *pos ; i < strlength ; i++) {
switch (str[i]) { switch (str[i]) {
case '{': case '{':
@ -49,49 +56,116 @@ typecast_array_tokenize(unsigned char *str, int strlength,
return ASCAN_BEGIN; return ASCAN_BEGIN;
case '}': case '}':
*pos = i+1; /* we tokenize the last item in the array and then return it to
return ASCAN_END; the user togheter with the closing bracket marker */
res = ASCAN_END;
goto tokenize;
case '"':
/* this will close the quoting only if the previous character was
NOT a backslash */
if (qs == 2 && str[i-1] != '\\') qs = 1;
continue;
case '\\':
/* something has been quoted, sigh, we'll need a copy buffer */
res = ASCAN_QUOTED;
continue;
case ',': case ',':
/* if we're inside quotes we use the comma as a normal char */
if (qs == 2)
continue;
else
goto tokenize;
}
}
res = ASCAN_EOF;
tokenize:
l = i - *pos - qs;
/* if res is ASCAN_QUOTED we need to copy the string to a newly allocated
buffer and return it */
if (res == ASCAN_QUOTED) {
unsigned char *buffer = PyMem_Malloc(l+1);
if (buffer == NULL) return ASCAN_ERROR;
*token = buffer;
for (i = *pos; i < l+*pos; i++) {
if (str[i] != '\\')
*(buffer++) = str[i];
}
*buffer = '\0';
*length = (int)buffer - (int)*token;
*pos = i+2;
}
else {
*token = &str[*pos]; *token = &str[*pos];
*length = i - *pos; *length = l;
if (quoted == 1)
*length -= 1;
*pos = i+1; *pos = i+1;
return ASCAN_TOKEN; if (res == ASCAN_END && str[*pos] == ',')
*pos += 1; /* skip both the bracket and the comma */
default:
/* nothing to do right now */
break;
}
} }
*token = &str[*pos]; return res;
*length = i - *pos;
if (quoted == 1)
*length -= 1;
return ASCAN_EOF;
} }
static int static int
typecast_array_scan(unsigned char *str, int strlength, typecast_array_scan(unsigned char *str, int strlength,
PyObject *curs, PyObject *base, PyObject *array) PyObject *curs, PyObject *base, PyObject *array)
{ {
int state, length, pos = 0; int state, length, bracket = 0, pos = 0;
unsigned char *token; unsigned char *token;
PyObject *stack[MAX_DIMENSIONS];
int stack_index = 0;
while (1) { while (1) {
state = typecast_array_tokenize(str, strlength, &pos, &token, &length); state = typecast_array_tokenize(str, strlength, &pos, &token, &length);
if (state == ASCAN_TOKEN
|| state == ASCAN_QUOTED
|| (state == ASCAN_EOF && bracket == 0)
|| (state == ASCAN_END && bracket == 0)) {
if (state == ASCAN_TOKEN || state == ASCAN_EOF) {
PyObject *obj = typecast_cast(base, token, length, curs); PyObject *obj = typecast_cast(base, token, length, curs);
/* before anything else we free the memory */
if (state == ASCAN_QUOTED) PyMem_Free(token);
if (obj == NULL) return 0; if (obj == NULL) return 0;
PyList_Append(array, obj); PyList_Append(array, obj);
Py_DECREF(obj); Py_DECREF(obj);
} }
else { else if (state == ASCAN_BEGIN) {
Dprintf("** RECURSION (not supported right now)!!"); PyObject *sub = PyList_New(0);
if (sub == NULL) return 0;
PyList_Append(array, sub);
Py_DECREF(sub);
if (stack_index == MAX_DIMENSIONS)
return 0;
stack[stack_index++] = array;
array = sub;
}
else if (state == ASCAN_ERROR) {
return 0;
}
/* reset the closing bracket marker just before cheking for ASCAN_END:
this is to make sure we don't mistake two closing brackets for an
empty item */
bracket = 0;
if (state == ASCAN_END) {
if (--stack_index < 0)
return 0;
array = stack[stack_index];
bracket = 1;
} }
if (state == ASCAN_EOF) break; if (state == ASCAN_EOF) break;
@ -116,6 +190,8 @@ typecast_GENERIC_ARRAY_cast(unsigned char *str, int len, PyObject *curs)
return NULL; return NULL;
} }
Dprintf("typecast_GENERIC_ARRAY_cast: scanning %s", str);
obj = PyList_New(0); obj = PyList_New(0);
/* scan the array skipping the first level of {} */ /* scan the array skipping the first level of {} */

View File

@ -9,7 +9,9 @@ print curs.fetchone()
curs.execute("SELECT ARRAY['1','2','3'] AS foo") curs.execute("SELECT ARRAY['1','2','3'] AS foo")
print curs.fetchone() print curs.fetchone()
curs.execute("""SELECT ARRAY['','"',''] AS foo""") curs.execute("""SELECT ARRAY[',','"','\\\\'] AS foo""")
d = curs.fetchone() d = curs.fetchone()
print d, '->', d[0][0], d[0][1], d[0][2] print d, '->', d[0][0], d[0][1], d[0][2]
curs.execute("SELECT ARRAY[ARRAY[1,2],ARRAY[3,4]] AS foo")
print curs.fetchone()

View File

@ -47,7 +47,7 @@ from distutils.core import setup, Extension
from distutils.sysconfig import get_python_inc from distutils.sysconfig import get_python_inc
import distutils.ccompiler import distutils.ccompiler
PSYCOPG_VERSION = '1.99.13/devel' PSYCOPG_VERSION = '1.99.13'
version_flags = [] version_flags = []
have_pydatetime = False have_pydatetime = False