From d6e232e2b9bbf1d3967dec7ea792985bf22f9236 Mon Sep 17 00:00:00 2001 From: Federico Di Gregorio Date: Fri, 13 Apr 2007 14:07:11 +0000 Subject: [PATCH] Various fixes, now all examples work. --- ChangeLog | 10 +++ NEWS | 19 +++--- ZPsycopgDA/DA.py | 2 +- examples/copy_from.py | 4 +- psycopg/cursor.h | 1 + psycopg/cursor_type.c | 150 +++++++++++++++++++++++------------------- psycopg/psycopg.h | 2 +- setup.cfg | 2 +- setup.py | 2 +- 9 files changed, 110 insertions(+), 82 deletions(-) diff --git a/ChangeLog b/ChangeLog index 0f847efb..75011f5e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,15 @@ +2007-04-14 Federico Di Gregorio + + * psycopg/psycopg.h: fixed probable typo in definition of + CONV_CODE_PY_SSIZE_T: d -> i for Python < 2.5. + + * Fixed some of the examples. + 2007-04-13 Federico Di Gregorio + * Applied slighly modified patch from daniel (#176). Made + buffer size a compile-time parameter. + * Applied patch from David Rushby: typecast_binary.c cleanup. * Applied patch from David Rushby for int->size_t transition. diff --git a/NEWS b/NEWS index 2ae73154..80caa2de 100644 --- a/NEWS +++ b/NEWS @@ -1,22 +1,21 @@ What's new in psycopg 2.0.6 --------------------------- -* Full support for PostgreSQL 8.2, including NULLs in arrays. - -* Full support for Python 2.5 and 64 bit architectures. - -* Support for almost all PostgreSQL encodings. - -* Better management of times and dates both from Python and in Zope. +* Better support for PostgreSQL, Python and win32: + - full support for PostgreSQL 8.2, including NULLs in arrays + - support for almost all existing PostgreSQL encodings + - full list of PostgreSQL error codes available by importing the + psycopg2.errorcodes module + - full support for Python 2.5 and 64 bit architectures + - better build support on win32 platform * Support for per-connection type-casters (used by ZPsycopgDA too, this fixes a long standing bug that made different connections use a random set of date/time type-casters instead of the configured one.) -* We now have a full list of PostgreSQL error codes available by - importing the psycopg2.errorcodes module. +* Better management of times and dates both from Python and in Zope. -* Better build support on win32 platform. +* copy_to and copy_from now take an extra "columns" parameter. * Fixed some small buglets and build glitches: - removed double mutex destroy diff --git a/ZPsycopgDA/DA.py b/ZPsycopgDA/DA.py index 48ac6125..81cd2646 100644 --- a/ZPsycopgDA/DA.py +++ b/ZPsycopgDA/DA.py @@ -18,7 +18,7 @@ # See the LICENSE file for details. -ALLOWED_PSYCOPG_VERSIONS = ('2.0.5', '2.0.6b2', '2.0.6') +ALLOWED_PSYCOPG_VERSIONS = ('2.0.6',) import sys import time diff --git a/examples/copy_from.py b/examples/copy_from.py index edd32948..dfeff5c9 100644 --- a/examples/copy_from.py +++ b/examples/copy_from.py @@ -45,8 +45,8 @@ conn.commit() io = open('copy_from.txt', 'wr') data = ['Tom\tJenkins\t37\n', - 'Madonna\t\N\t45\n', - 'Federico\tDi Gregorio\t\N\n'] + 'Madonna\t\\N\t45\n', + 'Federico\tDi Gregorio\t\\N\n'] io.writelines(data) io.close() diff --git a/psycopg/cursor.h b/psycopg/cursor.h index 8fb04898..d8af1e81 100644 --- a/psycopg/cursor.h +++ b/psycopg/cursor.h @@ -63,6 +63,7 @@ typedef struct { PyObject *copyfile; /* file-like used during COPY TO/FROM ops */ Py_ssize_t copysize; /* size of the copy buffer during COPY TO/FROM ops */ #define DEFAULT_COPYSIZE 16384 +#define DEFAULT_COPYBUFF 1024 PyObject *tuple_factory; /* factory for result tuples */ PyObject *tzinfo_factory; /* factory for tzinfo objects */ diff --git a/psycopg/cursor_type.c b/psycopg/cursor_type.c index 6dbe846f..e4b0319c 100644 --- a/psycopg/cursor_type.c +++ b/psycopg/cursor_type.c @@ -88,15 +88,6 @@ _mogrify(PyObject *var, PyObject *fmt, connectionObject *conn, PyObject **new) c = PyString_AsString(fmt); while(*c) { - /* check if some crazy guy mixed formats */ - if (kind == 2) { - Py_XDECREF(n); - psyco_set_error(ProgrammingError, (PyObject*)conn, - "argument formats can't be mixed", NULL, NULL); - return -1; - } - kind = 1; - /* handle plain percent symbol in format string */ if (c[0] == '%' && c[1] == '%') { c+=2; force = 1; @@ -109,6 +100,15 @@ _mogrify(PyObject *var, PyObject *fmt, connectionObject *conn, PyObject **new) 4/ ...and add it to the new dictionary to be used as argument */ else if (c[0] == '%' && c[1] == '(') { + + /* check if some crazy guy mixed formats */ + if (kind == 2) { + Py_XDECREF(n); + psyco_set_error(ProgrammingError, (PyObject*)conn, + "argument formats can't be mixed", NULL, NULL); + return -1; + } + kind = 1; /* let's have d point the end of the argument */ for (d = c + 2; *d && *d != ')'; d++); @@ -1076,6 +1076,55 @@ psyco_curs_scroll(cursorObject *self, PyObject *args, PyObject *kwargs) #ifdef PSYCOPG_EXTENSIONS +#define COPY_BUFFER_SIZE 1024 + +static int _psyco_curs_copy_columns(PyObject *columns, char *columnlist) +{ + PyObject *col, *coliter; + Py_ssize_t collen; + char* colname; + int offset = 1; + + columnlist[0] = '\0'; + if (columns == NULL || columns == Py_None) return 0; + + coliter = PyObject_GetIter(columns); + if (coliter == NULL) return 0; + + columnlist[0] = '('; + + while ((col = PyIter_Next(coliter)) != NULL) { + if (!PyString_Check(col)) { + Py_DECREF(col); + Py_DECREF(coliter); + PyErr_SetString(PyExc_ValueError, + "elements in column list must be strings"); + return -1; + } + PyString_AsStringAndSize(col, &colname, &collen); + if (offset + collen > DEFAULT_COPYBUFF - 2) { + Py_DECREF(col); + Py_DECREF(coliter); + PyErr_SetString(PyExc_ValueError, "column list too long"); + return -1; + } + strncpy(&columnlist[offset], colname, collen); + offset += collen; + columnlist[offset++] = ','; + Py_DECREF(col); + } + Py_DECREF(coliter); + + if (offset == 2) { + return 0; + } + else { + columnlist[offset - 1] = ')'; + columnlist[offset] = '\0'; + return 1; + } +} + /* extension: copy_from - implements COPY FROM */ #define psyco_curs_copy_from_doc \ @@ -1100,12 +1149,12 @@ _psyco_curs_has_read_check(PyObject* o, void* var) static PyObject * psyco_curs_copy_from(cursorObject *self, PyObject *args, PyObject *kwargs) { - char query[1024]; + char query[DEFAULT_COPYBUFF]; char *table_name; char *sep = "\t", *null = NULL; - Py_ssize_t bufsize = DEFAULT_COPYSIZE; + Py_ssize_t bufsize = DEFAULT_COPYBUFF; PyObject *file, *columns = NULL, *res = NULL; - char columnlist[1024] = ""; + char columnlist[DEFAULT_COPYBUFF]; static char *kwlist[] = {"file", "table", "sep", "null", "size", "columns", NULL}; @@ -1119,58 +1168,19 @@ psyco_curs_copy_from(cursorObject *self, PyObject *args, PyObject *kwargs) return NULL; } - if (columns != NULL && columns != Py_None) { - PyObject* collistiter = PyObject_GetIter(columns); - PyObject* col; - Py_ssize_t collistlen = 2; - Py_ssize_t colitemlen; - char* colname; - if (collistiter == NULL) { - return NULL; - } - strcpy(columnlist, " ("); - while ((col = PyIter_Next(collistiter)) != NULL) { - if (!PyString_Check(col)) { - Py_DECREF(col); - Py_DECREF(collistiter); - PyErr_SetString(PyExc_ValueError, - "Elements in column list must be strings"); - return NULL; - } - PyString_AsStringAndSize(col, &colname, &colitemlen); - if (collistlen + colitemlen > 1022) { - Py_DECREF(col); - Py_DECREF(collistiter); - PyErr_SetString(PyExc_ValueError, "Column list too long"); - return NULL; - } - strncpy(&columnlist[collistlen], colname, colitemlen); - collistlen += colitemlen; - columnlist[collistlen++] = ','; - Py_DECREF(col); - } - Py_DECREF(collistiter); - - if (collistlen == 2) { /* empty list; we printed no comma */ - collistlen++; - } - - columnlist[collistlen - 1] = ')'; - columnlist[collistlen] = '\0'; - } - - if (PyErr_Occurred()) { + if (_psyco_curs_copy_columns(columns, columnlist) == -1) return NULL; - } EXC_IF_CURS_CLOSED(self); if (null) { - PyOS_snprintf(query, 1023, "COPY %s%s FROM stdin USING DELIMITERS '%s'" + PyOS_snprintf(query, DEFAULT_COPYBUFF-1, + "COPY %s%s FROM stdin USING DELIMITERS '%s'" " WITH NULL AS '%s'", table_name, columnlist, sep, null); } else { - PyOS_snprintf(query, 1023, "COPY %s%s FROM stdin USING DELIMITERS '%s'", + PyOS_snprintf(query, DEFAULT_COPYBUFF-1, + "COPY %s%s FROM stdin USING DELIMITERS '%s'", table_name, columnlist, sep); } Dprintf("psyco_curs_copy_from: query = %s", query); @@ -1188,8 +1198,10 @@ psyco_curs_copy_from(cursorObject *self, PyObject *args, PyObject *kwargs) return res; } +/* extension: copy_to - implements COPY TO */ + #define psyco_curs_copy_to_doc \ -"copy_to(file, table, sep='\\t', null='\\N') -- Copy table to file." +"copy_to(file, table, sep='\\t', null='\\N', columns=None) -- Copy table to file." static int _psyco_curs_has_write_check(PyObject* o, void* var) @@ -1209,28 +1221,34 @@ _psyco_curs_has_write_check(PyObject* o, void* var) static PyObject * psyco_curs_copy_to(cursorObject *self, PyObject *args, PyObject *kwargs) { - char query[256]; + char query[DEFAULT_COPYBUFF]; + char columnlist[DEFAULT_COPYBUFF]; char *table_name; char *sep = "\t", *null = NULL; - PyObject *file, *res = NULL; + PyObject *file, *columns = NULL, *res = NULL; - static char *kwlist[] = {"file", "table", "sep", "null", NULL}; + static char *kwlist[] = {"file", "table", "sep", "null", "columns", NULL}; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&s|ss", kwlist, + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&s|ssO", kwlist, _psyco_curs_has_write_check, &file, - &table_name, &sep, &null)) { + &table_name, &sep, &null, &columns)) { return NULL; } + + if (_psyco_curs_copy_columns(columns, columnlist) == -1) + return NULL; EXC_IF_CURS_CLOSED(self); if (null) { - PyOS_snprintf(query, 255, "COPY %s TO stdout USING DELIMITERS '%s'" - " WITH NULL AS '%s'", table_name, sep, null); + PyOS_snprintf(query, DEFAULT_COPYBUFF-1, + "COPY %s%s TO stdout USING DELIMITERS '%s'" + " WITH NULL AS '%s'", table_name, columnlist, sep, null); } else { - PyOS_snprintf(query, 255, "COPY %s TO stdout USING DELIMITERS '%s'", - table_name, sep); + PyOS_snprintf(query, DEFAULT_COPYBUFF-1, + "COPY %s%s TO stdout USING DELIMITERS '%s'", + table_name, columnlist, sep); } self->copysize = 0; diff --git a/psycopg/psycopg.h b/psycopg/psycopg.h index 15ce8bbc..aeb174c9 100644 --- a/psycopg/psycopg.h +++ b/psycopg/psycopg.h @@ -48,7 +48,7 @@ extern "C" { #if PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION >= 5 #define CONV_CODE_PY_SSIZE_T "n" #else - #define CONV_CODE_PY_SSIZE_T "d" + #define CONV_CODE_PY_SSIZE_T "i" typedef int Py_ssize_t; #define PY_SSIZE_T_MIN INT_MIN diff --git a/setup.cfg b/setup.cfg index b4ea08bf..3d753738 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,5 @@ [build_ext] -define=PSYCOPG_EXTENSIONS,PSYCOPG_DISPLAY_SIZE,PSYCOPG_NEW_BOOLEAN,HAVE_PQFREEMEM,HAVE_PQPROTOCOL3 +define=PSYCOPG_EXTENSIONS,PSYCOPG_DISPLAY_SIZE,PSYCOPG_NEW_BOOLEAN,HAVE_PQFREEMEM,HAVE_PQPROTOCOL3,PSYCOPG_DEBUG # PSYCOPG_EXTENSIONS enables extensions to PEP-249 (you really want this) # PSYCOPG_DISPLAY_SIZE enable display size calculation (a little slower) # HAVE_PQFREEMEM should be defined on PostgreSQL >= 7.4 diff --git a/setup.py b/setup.py index c11c25f8..9dfc79b4 100644 --- a/setup.py +++ b/setup.py @@ -54,7 +54,7 @@ from distutils.command.build_ext import build_ext from distutils.sysconfig import get_python_inc from distutils.ccompiler import get_default_compiler -PSYCOPG_VERSION = '2.0.6b2' +PSYCOPG_VERSION = '2.0.6' version_flags = [] PLATFORM_IS_WINDOWS = sys.platform.lower().startswith('win')