From 9316c6af537b17d3ecfc70b81aa9183a7023a99b Mon Sep 17 00:00:00 2001 From: Federico Di Gregorio Date: Sat, 12 Mar 2005 06:39:47 +0000 Subject: [PATCH] Optimizations to type casting (in preparation to array support.) --- ChangeLog | 21 +++++++++++-- examples/cursor.py | 3 +- examples/lastrowid.py | 4 +-- examples/mogrify.py | 6 ++-- examples/threads.py | 2 +- psycopg/cursor_type.c | 33 +++++++++----------- psycopg/microprotocols_proto.c | 15 --------- psycopg/typecast.c | 41 +++++++++++++------------ psycopg/typecast.h | 6 +++- psycopg/typecast_basic.c | 56 ++++++++++++++++------------------ psycopg/typecast_datetime.c | 32 ++++++------------- psycopg/typecast_mxdatetime.c | 22 +++++-------- scripts/maketypes.sh | 8 ++--- setup.cfg | 2 +- setup.py | 2 +- tests/types_basic.py | 2 ++ 16 files changed, 118 insertions(+), 137 deletions(-) diff --git a/ChangeLog b/ChangeLog index 887e4208..67b060b3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,13 +1,30 @@ +2005-03-12 Federico Di Gregorio + + * psycopg/cursor_type.c (_psyco_curs_buildrow_fill): modified to + call typecast_cast(). + + * psycopg/typecast.c (typecast_call/typecast_cast): modified + typecast_call to use the new typecast_cast that avoids one string + conversion on every cast. + 2005-03-04 Federico Di Gregorio - * Release 1.99.13.1. + * Release 1.99.12.1. * psycopg/adapter_asis.c (asis_str): changed call to PyObject_Repr to PyObject_Str to avoid problems with long integers. +2005-03-03 Federico Di Gregorio + + * psycopg/typecast.h: added array casting functions. + + * scripts/maketypes.sh: does not generate pgversion.h anymore. + + * Updated all examples for the release. + 2005-03-02 Federico Di Gregorio - * Release 1.99.13. + * Release 1.99.12. * psycopg/adapter_*.c: added __conform__ to all adapters. diff --git a/examples/cursor.py b/examples/cursor.py index 22052789..607ce189 100644 --- a/examples/cursor.py +++ b/examples/cursor.py @@ -26,7 +26,6 @@ if len(sys.argv) > 1: DSN = sys.argv[1] print "Opening connection using dsn:", DSN - conn = psycopg.connect(DSN) print "Encoding for this connection is", conn.encoding @@ -51,7 +50,7 @@ class Cursor(psycopg.extensions.cursor): raise NoDataError("no more data") return d -curs = conn.cursor(factory=Cursor) +curs = conn.cursor(cursor_factory=Cursor) curs.execute("SELECT 1 AS foo") print "Result of fetchone():", curs.fetchone() diff --git a/examples/lastrowid.py b/examples/lastrowid.py index aa578509..4b01c85f 100644 --- a/examples/lastrowid.py +++ b/examples/lastrowid.py @@ -49,10 +49,10 @@ curs.execute("""INSERT INTO test_oid moid = curs.lastrowid print "Oid for %(name)s %(surname)s" % data[1], "is", moid -curs.execute("SELECT * FROM test_oid WHERE oid = %d", (foid,)) +curs.execute("SELECT * FROM test_oid WHERE oid = %s", (foid,)) print "Oid", foid, "selected %s %s" % curs.fetchone() -curs.execute("SELECT * FROM test_oid WHERE oid = %d", (moid,)) +curs.execute("SELECT * FROM test_oid WHERE oid = %s", (moid,)) print "Oid", moid, "selected %s %s" % curs.fetchone() curs.execute("DROP TABLE test_oid") diff --git a/examples/mogrify.py b/examples/mogrify.py index ad6d11c4..d66e13d1 100644 --- a/examples/mogrify.py +++ b/examples/mogrify.py @@ -1,4 +1,4 @@ -# mogrify.py - test all possible type mogrifications +# mogrify.py - test all possible simple type mogrifications # -*- encoding: latin1 -*- # # Copyright (C) 2004 Federico Di Gregorio @@ -33,14 +33,14 @@ curs = conn.cursor() curs.execute("SELECT %(foo)s AS foo", {'foo':'bar'}) curs.execute("SELECT %(foo)s AS foo", {'foo':None}) curs.execute("SELECT %(foo)s AS foo", {'foo':True}) -curs.execute("SELECT %(foo)f AS foo", {'foo':42}) +curs.execute("SELECT %(foo)s AS foo", {'foo':42}) curs.execute("SELECT %(foo)s AS foo", {'foo':u'yattą!'}) curs.execute("SELECT %(foo)s AS foo", {'foo':u'bar'}) print curs.mogrify("SELECT %(foo)s AS foo", {'foo':'bar'}) print curs.mogrify("SELECT %(foo)s AS foo", {'foo':None}) print curs.mogrify("SELECT %(foo)s AS foo", {'foo':True}) -print curs.mogrify("SELECT %(foo)f AS foo", {'foo':42}) +print curs.mogrify("SELECT %(foo)s AS foo", {'foo':42}) print curs.mogrify("SELECT %(foo)s AS foo", {'foo':u'yattą!'}) print curs.mogrify("SELECT %(foo)s AS foo", {'foo':u'bar'}) diff --git a/examples/threads.py b/examples/threads.py index ca67104c..d4b4fb2b 100644 --- a/examples/threads.py +++ b/examples/threads.py @@ -81,7 +81,7 @@ def insert_func(conn_or_pool, rows): conn = conn_or_pool.getconn() c = conn.cursor() try: - c.execute("INSERT INTO test_threads VALUES (%s, %d, %f)", + c.execute("INSERT INTO test_threads VALUES (%s, %s, %s)", (str(i), i, float(i))) except psycopg.ProgrammingError, err: print name, ": an error occurred; skipping this insert" diff --git a/psycopg/cursor_type.c b/psycopg/cursor_type.c index 3dab43a4..135e57f2 100644 --- a/psycopg/cursor_type.c +++ b/psycopg/cursor_type.c @@ -30,6 +30,7 @@ #include "psycopg/cursor.h" #include "psycopg/connection.h" #include "psycopg/pqpath.h" +#include "psycopg/typecast.h" #include "psycopg/microprotocols.h" #include "psycopg/microprotocols_proto.h" #include "pgversion.h" @@ -545,31 +546,26 @@ static PyObject * _psyco_curs_buildrow_fill(cursorObject *self, PyObject *res, int row, int n, int istuple) { - int i; - PyObject *str, *val; + int i, len; + unsigned char *str; + PyObject *val; for (i=0; i < n; i++) { if (PQgetisnull(self->pgres, row, i)) { - Py_INCREF(Py_None); - str = Py_None; + str = NULL; + len = 0; } else { - char *s = PQgetvalue(self->pgres, row, i); - int l = PQgetlength(self->pgres, row, i); - str = PyString_FromStringAndSize(s, l); - - Dprintf("_psyco_curs_buildrow: row %ld, element %d, len %i", - self->row, i, l); + str = PQgetvalue(self->pgres, row, i); + len = PQgetlength(self->pgres, row, i); } - Dprintf("_psyco_curs_buildrow: str->refcnt = %d", str->ob_refcnt); - val = PyObject_CallFunction(PyTuple_GET_ITEM(self->casts, i), "OO", - str, self); - /* here we DECREF str because the typecaster should already have - INCREFfed it, if necessary and we don't need it anymore */ - Py_DECREF(str); - Dprintf("_psyco_curs_buildrow: str->refcnt = %d", str->ob_refcnt); - + Dprintf("_psyco_curs_buildrow: row %ld, element %d, len %i", + self->row, i, len); + + val = typecast_cast(PyTuple_GET_ITEM(self->casts, i), str, len, + (PyObject*)self); + if (val) { Dprintf("_psyco_curs_buildrow: val->refcnt = %d", val->ob_refcnt); if (istuple) { @@ -590,7 +586,6 @@ _psyco_curs_buildrow_fill(cursorObject *self, PyObject *res, } } return res; - } static PyObject * diff --git a/psycopg/microprotocols_proto.c b/psycopg/microprotocols_proto.c index 861c8efa..60e4c6e4 100644 --- a/psycopg/microprotocols_proto.c +++ b/psycopg/microprotocols_proto.c @@ -34,21 +34,6 @@ /** void protocol implementation **/ -/* prepare - prepare object for quotation */ - -#define psyco_isqlquote_prepare_doc \ -"prepare(conn) -> prepare object with connection 'conn'" - -static PyObject * -psyco_isqlquote_prepare(isqlquoteObject *self, PyObject *args) -{ - PyObject* conn = NULL; - - if (!PyArg_ParseTuple(args, "O", &conn)) return NULL; - - Py_INCREF(Py_None); - return Py_None; -} /* getquoted - return quoted representation for object */ diff --git a/psycopg/typecast.c b/psycopg/typecast.c index ee910114..82367b3b 100644 --- a/psycopg/typecast.c +++ b/psycopg/typecast.c @@ -305,31 +305,15 @@ typecast_destroy(typecastObject *self) static PyObject * typecast_call(PyObject *obj, PyObject *args, PyObject *kwargs) { - PyObject *string, *cursor, *res; - - typecastObject *self = (typecastObject *)obj; + PyObject *string, *cursor; if (!PyArg_ParseTuple(args, "OO", &string, &cursor)) { return NULL; } - if (self->ccast) { - Dprintf("typecast_call: calling C cast function"); - res = self->ccast(string, cursor); - } - else if (self->pcast) { - Dprintf("typecast_call: calling python callable"); - Py_INCREF(string); - res = PyObject_CallFunction(self->pcast, "OO", string, cursor); - } - else { - Py_INCREF(Py_None); - res = Py_None; - } - - Dprintf("typecast_call: string argument has refcnt = %d", - string->ob_refcnt); - return res; + return typecast_cast(obj, + PyString_AsString(string), PyString_Size(string), + cursor); } @@ -436,4 +420,21 @@ typecast_from_c(typecastObject_initlist *type) return (PyObject *)obj; } +PyObject * +typecast_cast(PyObject *obj, unsigned char *str, int len, PyObject *curs) +{ + typecastObject *self = (typecastObject *)obj; + if (self->ccast) { + Dprintf("typecast_call: calling C cast function"); + return 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); + } + else { + PyErr_SetString(Error, "internal error: no casting function found"); + return NULL; + } +} diff --git a/psycopg/typecast.h b/psycopg/typecast.h index de31337d..3cf50181 100644 --- a/psycopg/typecast.h +++ b/psycopg/typecast.h @@ -29,7 +29,7 @@ extern "C" { #endif /* type of type-casting functions (both C and Python) */ -typedef PyObject *(*typecast_function)(PyObject *, PyObject *); +typedef PyObject *(*typecast_function)(unsigned char *, int len, PyObject *); /** typecast type **/ @@ -73,5 +73,9 @@ extern PyObject *typecast_from_c(typecastObject_initlist *type); /* the python callable typecast creator function */ extern PyObject *typecast_from_python( PyObject *self, PyObject *args, PyObject *keywds); + +/* the function used to dispatch typecasting calls */ +extern PyObject *typecast_cast( + PyObject *self, unsigned char *str, int len, PyObject *curs); #endif /* !defined(PSYCOPG_TYPECAST_H) */ diff --git a/psycopg/typecast_basic.c b/psycopg/typecast_basic.c index e698a496..b6e62601 100644 --- a/psycopg/typecast_basic.c +++ b/psycopg/typecast_basic.c @@ -20,59 +20,57 @@ */ #include +#include /** INTEGER - cast normal integers (4 bytes) to python int **/ static PyObject * -typecast_INTEGER_cast(PyObject *s, PyObject *curs) +typecast_INTEGER_cast(unsigned char *s, int len, PyObject *curs) { - if (s == Py_None) {Py_INCREF(s); return s;} - return PyNumber_Int(s); + if (s == NULL) {Py_INCREF(Py_None); return Py_None;} + return PyInt_FromString(s, NULL, 0); } /** LONGINTEGER - cast long integers (8 bytes) to python long **/ static PyObject * -typecast_LONGINTEGER_cast(PyObject *s, PyObject *curs) +typecast_LONGINTEGER_cast(unsigned char *s, int len, PyObject *curs) { - if (s == Py_None) {Py_INCREF(s); return s;} - return PyNumber_Long(s); + if (s == NULL) {Py_INCREF(Py_None); return Py_None;} + return PyLong_FromString(s, NULL, 0); } /** FLOAT - cast floating point numbers to python float **/ static PyObject * -typecast_FLOAT_cast(PyObject *s, PyObject *curs) +typecast_FLOAT_cast(unsigned char *s, int len, PyObject *curs) { - if (s == Py_None) {Py_INCREF(s); return s;} - return PyNumber_Float(s); + if (s == NULL) {Py_INCREF(Py_None); return Py_None;} + return PyFloat_FromDouble(atof(s)); } /** STRING - cast strings of any type to python string **/ static PyObject * -typecast_STRING_cast(PyObject *s, PyObject *curs) +typecast_STRING_cast(unsigned char *s, int len, PyObject *curs) { - Py_INCREF(s); - return s; + if (s == NULL) {Py_INCREF(Py_None); return Py_None;} + return PyString_FromStringAndSize(s, len); } /** UNICODE - cast strings of any type to a python unicode object **/ static PyObject * -typecast_UNICODE_cast(PyObject *s, PyObject *curs) +typecast_UNICODE_cast(unsigned char *s, int len, PyObject *curs) { PyObject *enc; - - if (s == Py_None) {Py_INCREF(s); return s;} + + if (s == NULL) {Py_INCREF(Py_None); return Py_None;} enc = PyDict_GetItemString(psycoEncodings, ((cursorObject*)curs)->conn->encoding); if (enc) { - return PyUnicode_Decode(PyString_AsString(s), - PyString_Size(s), - PyString_AsString(enc), - NULL); + return PyUnicode_Decode(s, strlen(s), PyString_AsString(enc), NULL); } else { PyErr_Format(InterfaceError, @@ -134,15 +132,15 @@ typecast_BINARY_cast_unescape(unsigned char *str, size_t *to_length) #endif static PyObject * -typecast_BINARY_cast(PyObject *s, PyObject *curs) +typecast_BINARY_cast(unsigned char *s, int l, PyObject *curs) { PyObject *res; unsigned char *str; size_t len; - if (s == Py_None) {Py_INCREF(s); return s;} + if (s == NULL) {Py_INCREF(Py_None); return Py_None;} - str = PQunescapeBytea(PyString_AS_STRING(s), &len); + str = PQunescapeBytea(s, &len); Dprintf("typecast_BINARY_cast: unescaped %d bytes", len); /* TODO: using a PyBuffer would make this a zero-copy operation but we'll @@ -159,13 +157,13 @@ typecast_BINARY_cast(PyObject *s, PyObject *curs) /** BOOLEAN - cast boolean value into right python object **/ static PyObject * -typecast_BOOLEAN_cast(PyObject *s, PyObject *curs) +typecast_BOOLEAN_cast(unsigned char *s, int len, PyObject *curs) { PyObject *res; - - if (s == Py_None) {Py_INCREF(s); return s;} - if (PyString_AS_STRING(s)[0] == 't') + if (s == NULL) {Py_INCREF(Py_None); return Py_None;} + + if (s[0] == 't') res = Py_True; else res = Py_False; @@ -178,10 +176,10 @@ typecast_BOOLEAN_cast(PyObject *s, PyObject *curs) #ifdef HAVE_DECIMAL static PyObject * -typecast_DECIMAL_cast(PyObject *s, PyObject *curs) +typecast_DECIMAL_cast(unsigned char *s, int len, PyObject *curs) { - if (s == Py_None) {Py_INCREF(s); return s;} - return PyObject_CallFunction(decimalType, "O", s); + if (s == NULL) {Py_INCREF(Py_None); return Py_None;} + return PyObject_CallFunction(decimalType, "s", s); } #else #define typecast_DECIMAL_cast typecast_FLOAT_cast diff --git a/psycopg/typecast_datetime.c b/psycopg/typecast_datetime.c index 0f29bfd1..b02da586 100644 --- a/psycopg/typecast_datetime.c +++ b/psycopg/typecast_datetime.c @@ -34,15 +34,12 @@ extern PyObject *pyDeltaTypeP; /** DATE - cast a date into a date python object **/ static PyObject * -typecast_PYDATE_cast(PyObject *s, PyObject *curs) +typecast_PYDATE_cast(unsigned char *str, int len, PyObject *curs) { PyObject* obj = NULL; int n, y=0, m=0, d=0; - char *str; - if (s == Py_None) {Py_INCREF(s); return s;} - - str = PyString_AsString(s); + if (str == NULL) {Py_INCREF(Py_None); return Py_None;} /* check for infinity */ if (!strcmp(str, "infinity") || !strcmp(str, "-infinity")) { @@ -70,18 +67,16 @@ typecast_PYDATE_cast(PyObject *s, PyObject *curs) /** DATETIME - cast a timestamp into a datetime python object **/ static PyObject * -typecast_PYDATETIME_cast(PyObject *s, PyObject *curs) +typecast_PYDATETIME_cast(unsigned char *str, int len, PyObject *curs) { PyObject* obj = NULL; int n, y=0, m=0, d=0; int hh=0, mm=0; int tzh=0, tzm=0; double ss=0.0; - char tzs=0, *str; + char tzs=0; - if (s == Py_None) {Py_INCREF(s); return s;} - - str = PyString_AsString(s); + if (str == NULL) {Py_INCREF(Py_None); return Py_None;} /* check for infinity */ if (!strcmp(str, "infinity") || !strcmp(str, "-infinity")) { @@ -136,16 +131,13 @@ typecast_PYDATETIME_cast(PyObject *s, PyObject *curs) /** TIME - parse time into a time object **/ static PyObject * -typecast_PYTIME_cast(PyObject *s, PyObject *curs) +typecast_PYTIME_cast(unsigned char *str, int len, PyObject *curs) { PyObject* obj = NULL; int n, hh=0, mm=0; - double ss=0.0; - char *str; + double ss=0.0; - if (s == Py_None) {Py_INCREF(s); return s;} - - str = PyString_AsString(s); + if (str == NULL) {Py_INCREF(Py_None); return Py_None;} n = sscanf(str, "%d:%d:%lf", &hh, &mm, &ss); @@ -168,19 +160,15 @@ typecast_PYTIME_cast(PyObject *s, PyObject *curs) /** INTERVAL - parse an interval into a timedelta object **/ static PyObject * -typecast_PYINTERVAL_cast(PyObject *s, PyObject *curs) +typecast_PYINTERVAL_cast(unsigned char *str, int len, PyObject *curs) { long years = 0, months = 0, days = 0, denominator = 1; double hours = 0.0, minutes = 0.0, seconds = 0.0, hundredths = 0.0; double v = 0.0, sign = 1.0; int part = 0, sec; - double micro; - char *str; - if (s == Py_None) {Py_INCREF(s); return s;} - - str = PyString_AsString(s); + if (str == NULL) {Py_INCREF(Py_None); return Py_None;} Dprintf("typecast_PYINTERVAL_cast: s = %s", str); diff --git a/psycopg/typecast_mxdatetime.c b/psycopg/typecast_mxdatetime.c index 5f853a1c..6dd73a94 100644 --- a/psycopg/typecast_mxdatetime.c +++ b/psycopg/typecast_mxdatetime.c @@ -28,16 +28,13 @@ extern mxDateTimeModule_APIObject *mxDateTimeP; /** DATE - cast a date into mx.DateTime python object **/ static PyObject * -typecast_MXDATE_cast(PyObject *s, PyObject *curs) +typecast_MXDATE_cast(unsigned char *str, int len, PyObject *curs) { int n, y=0, m=0, d=0; int hh=0, mm=0; double ss=0.0; - char *str; - if (s == Py_None) {Py_INCREF(s); return s;} - - str = PyString_AsString(s); + if (str == NULL) {Py_INCREF(Py_None); return Py_None;} /* check for infinity */ if (!strcmp(str, "infinity") || !strcmp(str, "-infinity")) { @@ -63,15 +60,12 @@ typecast_MXDATE_cast(PyObject *s, PyObject *curs) /** TIME - parse time into an mx.DateTime object **/ static PyObject * -typecast_MXTIME_cast(PyObject *s, PyObject *curs) +typecast_MXTIME_cast(unsigned char *str, int len, PyObject *curs) { int n, hh=0, mm=0; - double ss=0.0; - char *str; + double ss=0.0; - if (s == Py_None) {Py_INCREF(s); return s;} - - str = PyString_AsString(s); + if (str == NULL) {Py_INCREF(Py_None); return Py_None;} Dprintf("typecast_MXTIME_cast: s = %s", str); @@ -90,17 +84,15 @@ typecast_MXTIME_cast(PyObject *s, PyObject *curs) /** INTERVAL - parse an interval into an mx.DateTimeDelta **/ static PyObject * -typecast_MXINTERVAL_cast(PyObject *s, PyObject *curs) +typecast_MXINTERVAL_cast(unsigned char *str, int len, PyObject *curs) { long years = 0, months = 0, days = 0, denominator = 1; double hours = 0.0, minutes = 0.0, seconds = 0.0, hundredths = 0.0; double v = 0.0, sign = 1.0; int part = 0; - char *str; - if (s == Py_None) {Py_INCREF(s); return s;} + if (str == NULL) {Py_INCREF(Py_None); return Py_None;} - str = PyString_AsString(s); Dprintf("typecast_MXINTERVAL_cast: s = %s", str); while (*str) { diff --git a/scripts/maketypes.sh b/scripts/maketypes.sh index e5078aee..3ce5a625 100644 --- a/scripts/maketypes.sh +++ b/scripts/maketypes.sh @@ -37,8 +37,8 @@ echo -n generating typecast_builtins.c ... awk '/#define .+OID/ {print $2 " " $3}' "$PGTYPE" | \ python $SCRIPTSDIR/buildtypes.py >$SRCDIR/typecast_builtins.c echo " done" -echo -n generating pgversion.h ... -echo "#define PG_VERSION_MAJOR $PGMAJOR" >$SRCDIR/pgversion.h -echo "#define PG_VERSION_MINOR $PGMINOR" >>$SRCDIR/pgversion.h -echo " done" +#echo -n generating pgversion.h ... +#echo "#define PG_VERSION_MAJOR $PGMAJOR" >$SRCDIR/pgversion.h +#echo "#define PG_VERSION_MINOR $PGMINOR" >>$SRCDIR/pgversion.h +#echo " done" diff --git a/setup.cfg b/setup.cfg index 46bcbc0a..7409c618 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 diff --git a/setup.py b/setup.py index 3a8bc20e..046d056e 100644 --- a/setup.py +++ b/setup.py @@ -47,7 +47,7 @@ from distutils.core import setup, Extension from distutils.sysconfig import get_python_inc import distutils.ccompiler -PSYCOPG_VERSION = '1.99.12.1' +PSYCOPG_VERSION = '1.99.13/devel' version_flags = [] have_pydatetime = False diff --git a/tests/types_basic.py b/tests/types_basic.py index d12c0a0a..fe8478b8 100644 --- a/tests/types_basic.py +++ b/tests/types_basic.py @@ -40,6 +40,8 @@ class TypesBasicTests(TestCase): def testNumber(self): s = self.execute("SELECT %s AS foo", (1971,)) self.failUnless(s == 1971, "wrong integer quoting: " + str(s)) + s = self.execute("SELECT %s AS foo", (1971L,)) + self.failUnless(s == 1971L, "wrong integer quoting: " + str(s)) s = self.execute("SELECT %s AS foo", (19.10,)) self.failUnless(s == 19.10, "wrong float quoting: " + str(s))