Merge from 2_0_x branch up to r814.

This commit is contained in:
Federico Di Gregorio 2006-09-01 13:46:57 +00:00
parent 39249e4741
commit 64933f2004
22 changed files with 364 additions and 90 deletions

111
ChangeLog
View File

@ -1,5 +1,116 @@
2006-09-01 Federico Di Gregorio <fog@initd.org>
* Release 2.0.5.
* Fixed patch from #119, see tracker for details.
* Preparing release 2.0.5.
* psycopg/psycopgmodule.c: fixed filling of connection errors
to include OperationalError.
* setup.py: removed pydatetime option from initialize_options
to make sure that the value in setup.cfg is used.
* psycopg/psycopgmodule.c: applied patch from jdahlin (#120)
to have .connect() accept either a string or int as the port
parameter.
* psycopg/adapter_binary.c: applied patch from jdahlin (#119)
to fix the segfault on empty binary buffers.
* psycopg/connection_type.c: added .status attribute to expose
the internal status.
* psycopg/pqpath.c: applied patch from intgr (#117) to fix
segfault on null queries.
* psycopg/cursor_type.c: applied patch from intgr (#116) to
fix bad keyword naming and segfault in .executemany().
* ZPsycopgDA/DA.py: applied ImageFile patch from Charlie
Clark.
* lib/pool.py: applied logging patch from Charlie Clark.
It will probably get a makeup and be moved to the top-level
module later.
2006-08-02 Federico Di Gregorio <fog@initd.org>
* Release 2.0.4.
* Fixed bug in float conversion (check for NULL string was
erroneously removed in 2.0.3!)
2006-07-31 Federico Di Gregorio <fog@initd.org>
* Release 2.0.3.
* psycopg/cursor_type.c: applied patch from jbellis (#113) to
allow column selection in .copy_from().
* psycopg/psycopgmodule.c: fixed memory leak in custom exceptions
(applied patch from #114).
2006-07-26 Federico Di Gregorio <fog@initd.org>
* psycopg/adapter_datetime.c (pydatetime_str): fixed error
in conversion of microseconds for intervals and better algo
(thanks to Mario Frasca.)
2006-06-18 Federico Di Gregorio <fog@initd.org>
* psycopg/adapter_binary.c: same as below.
* psycopg/adapter_qstring.c: does not segfault anymore if
.getquoted() is called without preparing the qstring with
the connection.
2006-06-15 Federico Di Gregorio <fog@initd.org>
* psycopg/typecast_basic.c: fixed problem with bogus
conversion when importing gtk (that was crazy, I didn't
understand why it happened but the new code just fixes it.)
* ZPsycopgDA/db.py: better type analisys, using an hash
instead of a series of if (variation on patch from Charlie
Clark.)
2006-06-11 Federico Di Gregorio <fog@initd.org>
* Release 2.0.2.
* psycopg/typecast_array.c (typecast_array_cleanup): fixed a
problem with typecast_array_cleanup always returning the new
string length shorter by 1 (Closes: #93).
* psycopg/adapter_binary.c: as below.
* psycopg/adapter_qstring.c: wrapped #warning in #ifdef __GCC__
because other compilers don't have it and it will just break
compilation (patch from jason, our great win32 builder).
* psycopg/adapter_list.c: applied patch to adapt an empty list
into an empty array and not to NULL (from iGGy, closes: #108).
* psycopg/cursor_type.c: applied patch from wkv to avoid
under-allocating query space when the parameters are not of the
right type (Closes: #110).
* psycopg/connection_int.c: applied patch from wkv to avoid off
by one allocation of connection encoding string (Closes: #109).
2006-06-09 Federico Di Gregorio <fog@initd.org>
* Release 2.0.1.
* Fixed some buglets in ZPsycopgDA (was unable to load due
to shorter version number in psycopg module.)
2006-06-08 Federico Di Gregorio <fog@initd.org> 2006-06-08 Federico Di Gregorio <fog@initd.org>
* Release 2.0.
* ZPsycopgDA/DA.py: removed Browse table for 2.0 release; we'll * ZPsycopgDA/DA.py: removed Browse table for 2.0 release; we'll
add it back later. add it back later.

26
NEWS
View File

@ -1,3 +1,29 @@
What's new in psycopg 2.0.4
---------------------------
* Fixed float conversion bug introduced in 2.0.3.
What's new in psycopg 2.0.3
---------------------------
* Fixed various buglets and a memory leak (see ChangeLog for details)
What's new in psycopg 2.0.2
---------------------------
* Fixed a bug in array typecasting that sometimes made psycopg forget about
the last element in the array.
* Fixed some minor buglets in string memory allocations.
* Builds again with compilers different from gcc (#warning about PostgreSQL
version is issued only if __GCC__ is defined.)
What's new in psycopg 2.0.1
---------------------------
* ZPsycopgDA now actually loads.
What's new in psycopg 2.0 What's new in psycopg 2.0
------------------------- -------------------------

View File

@ -18,7 +18,7 @@
# See the LICENSE file for details. # See the LICENSE file for details.
ALLOWED_PSYCOPG_VERSIONS = ('2.0',) ALLOWED_PSYCOPG_VERSIONS = ('2.0.1', '2.0.2', '2.0.3', '2.0.4', '2.0.5')
import sys import sys
import time import time
@ -30,11 +30,21 @@ import Shared.DC.ZRDB.Connection
from db import DB from db import DB
from Globals import HTMLFile from Globals import HTMLFile
from ImageFile import ImageFile
from ExtensionClass import Base from ExtensionClass import Base
from App.Dialogs import MessageDialog from App.Dialogs import MessageDialog
from DateTime import DateTime from DateTime import DateTime
# Build Zope version in a float for later cheks
import App
zope_version = App.version_txt.getZopeVersion()
zope_version = float("%s.%s" %(zope_version[:2]))
# ImageFile is deprecated in Zope >= 2.9
if zope_version < 2.9:
from ImageFile import ImageFile
else:
from App.ImageFile import ImageFile
# import psycopg and functions/singletons needed for date/time conversions # import psycopg and functions/singletons needed for date/time conversions
import psycopg2 import psycopg2

View File

@ -26,7 +26,7 @@ import site
import pool import pool
import psycopg2 import psycopg2
from psycopg2.extensions import INTEGER, LONGINTEGER, FLOAT, BOOLEAN, DATE from psycopg2.extensions import INTEGER, LONGINTEGER, FLOAT, BOOLEAN, DATE, TIME
from psycopg2 import NUMBER, STRING, ROWID, DATETIME from psycopg2 import NUMBER, STRING, ROWID, DATETIME
@ -42,6 +42,7 @@ class DB(TM, dbi_db.DB):
self.encoding = enc self.encoding = enc
self.failures = 0 self.failures = 0
self.calls = 0 self.calls = 0
self.make_mappings()
def getconn(self, create=True): def getconn(self, create=True):
conn = pool.getconn(self.dsn) conn = pool.getconn(self.dsn)
@ -89,32 +90,23 @@ class DB(TM, dbi_db.DB):
def sortKey(self): def sortKey(self):
return 1 return 1
def make_mappings(self):
"""Generate the mappings used later by self.convert_description()."""
self.type_mappings = {}
for t, s in [(INTEGER,'i'), (LONGINTEGER, 'i'), (NUMBER, 'n'),
(BOOLEAN,'n'), (ROWID, 'i'),
(DATETIME, 'd'), (DATE, 'd'), (TIME, 'd')]:
for v in t.values:
self.type_mappings[v] = (t, s)
def convert_description(self, desc, use_psycopg_types=False): def convert_description(self, desc, use_psycopg_types=False):
"""Convert DBAPI-2.0 description field to Zope format.""" """Convert DBAPI-2.0 description field to Zope format."""
items = [] items = []
for name, typ, width, ds, p, scale, null_ok in desc: for name, typ, width, ds, p, scale, null_ok in desc:
if typ == NUMBER: m = self.type_mappings.get(typ, (STRING, 's'))
if typ == INTEGER or typ == LONGINTEGER:
typs = 'i'
else:
typs = 'n'
typp = NUMBER
elif typ == BOOLEAN:
typs = 'n'
typp = BOOLEAN
elif typ == ROWID:
typs = 'i'
typp = ROWID
# FIXME: shouldn't DATETIME include other types?
elif typ == DATETIME or typ == DATE or typ == TIME:
typs = 'd'
typp = DATETIME
else:
typs = 's'
typp = STRING
items.append({ items.append({
'name': name, 'name': name,
'type': use_psycopg_types and typp or typs, 'type': use_psycopg_types and m[0] or m[1],
'width': width, 'width': width,
'precision': p, 'precision': p,
'scale': scale, 'scale': scale,

View File

@ -51,6 +51,16 @@ ISOLATION_LEVEL_SERIALIZABLE = 2
ISOLATION_LEVEL_REPEATABLE_READ = ISOLATION_LEVEL_SERIALIZABLE ISOLATION_LEVEL_REPEATABLE_READ = ISOLATION_LEVEL_SERIALIZABLE
ISOLATION_LEVEL_READ_UNCOMMITTED = ISOLATION_LEVEL_READ_COMMITTED ISOLATION_LEVEL_READ_UNCOMMITTED = ISOLATION_LEVEL_READ_COMMITTED
"""Transaction status values."""
STATUS_SETUP = 0
STATUS_READY = 1
STATUS_BEGIN = 2
STATUS_SYNC = 3
STATUS_ASYNC = 4
# This is a usefull mnemonic to check if the connection is in a transaction
STATUS_IN_TRANSACTION = STATUS_BEGIN
def register_adapter(typ, callable): def register_adapter(typ, callable):
"""Register 'callable' as an ISQLQuote adapter for type 'typ'.""" """Register 'callable' as an ISQLQuote adapter for type 'typ'."""

View File

@ -19,11 +19,27 @@ This module implements thread-safe (and not) connection pools.
import psycopg2 import psycopg2
try: try:
import logging
# do basic initialization if the module is not already initialized
logging.basicConfig(level=logging.INFO,
format='%(asctime)s %(levelname)s %(message)s')
# create logger object for psycopg2 module and sub-modules
_logger = logging.getLogger("psycopg2")
def dbg(*args):
_logger.debug("psycopg2", ' '.join([str(x) for x in args]))
try:
import App # does this make sure that we're running in Zope?
_logger.info("installed. Logging using Python logging module")
except:
_logger.debug("installed. Logging using Python logging module")
except ImportError:
from zLOG import LOG, DEBUG, INFO from zLOG import LOG, DEBUG, INFO
def dbg(*args): def dbg(*args):
LOG('ZPsycopgDA', DEBUG, "", LOG('ZPsycopgDA', DEBUG, "",
' '.join([str(x) for x in args])+'\n') ' '.join([str(x) for x in args])+'\n')
LOG('ZPsycopgDA', INFO, "Installed", "Logging using Zope's zLOG\n") LOG('ZPsycopgDA', INFO, "Installed", "Logging using Zope's zLOG\n")
except: except:
import sys import sys
def dbg(*args): def dbg(*args):

View File

@ -44,11 +44,11 @@ binary_escape(unsigned char *from, unsigned int from_length,
#if PG_MAJOR_VERSION > 8 || \ #if PG_MAJOR_VERSION > 8 || \
(PG_MAJOR_VERSION == 8 && PG_MINOR_VERSION > 1) || \ (PG_MAJOR_VERSION == 8 && PG_MINOR_VERSION > 1) || \
(PG_MAJOR_VERSION == 8 && PG_MINOR_VERSION == 1 && PG_PATCH_VERSION >= 4) (PG_MAJOR_VERSION == 8 && PG_MINOR_VERSION == 1 && PG_PATCH_VERSION >= 4)
if (conn)
return PQescapeByteaConn(conn, from, from_length, to_length); return PQescapeByteaConn(conn, from, from_length, to_length);
#else else
#warning "YOUR POSTGRESQL VERSION IS TOO OLD AND IT CAN BE INSECURE"
return PQescapeBytea(from, from_length, to_length);
#endif #endif
return PQescapeBytea(from, from_length, to_length);
} }
#else #else
static unsigned char * static unsigned char *
@ -141,14 +141,18 @@ binary_quote(binaryObject *self)
if (PyString_Check(self->wrapped) || PyBuffer_Check(self->wrapped)) { if (PyString_Check(self->wrapped) || PyBuffer_Check(self->wrapped)) {
/* escape and build quoted buffer */ /* escape and build quoted buffer */
PyObject_AsCharBuffer(self->wrapped, &buffer, &buffer_len); PyObject_AsCharBuffer(self->wrapped, &buffer, &buffer_len);
to = (char *)binary_escape((unsigned char*)buffer, buffer_len, &len, to = (char *)binary_escape((unsigned char*)buffer, buffer_len, &len,
((connectionObject*)self->conn)->pgconn); self->conn ? ((connectionObject*)self->conn)->pgconn : NULL);
if (to == NULL) { if (to == NULL) {
PyErr_NoMemory(); PyErr_NoMemory();
return NULL; return NULL;
} }
if (len > 0)
self->buffer = PyString_FromFormat("'%s'", to); self->buffer = PyString_FromFormat("'%s'", to);
else
self->buffer = PyString_FromString("''");
PQfreemem(to); PQfreemem(to);
} }

View File

@ -64,15 +64,14 @@ pydatetime_str(pydatetimeObject *self)
PyDateTime_Delta *obj = (PyDateTime_Delta*)self->wrapped; PyDateTime_Delta *obj = (PyDateTime_Delta*)self->wrapped;
char buffer[8]; char buffer[8];
int i, j, x; int i;
int a = obj->microseconds; int a = obj->microseconds;
for (i=1000000, j=0; i > 0 ; i /= 10) { for (i=0; i < 6 ; i++) {
x = a/i; buffer[5-i] = '0' + (a % 10);
a -= x*i; a /= 10;
buffer[j++] = '0'+x;
} }
buffer[j] = '\0'; buffer[6] = '\0';
return PyString_FromFormat("'%d days %d.%s seconds'", return PyString_FromFormat("'%d days %d.%s seconds'",
obj->days, obj->seconds, buffer); obj->days, obj->seconds, buffer);

View File

@ -46,7 +46,7 @@ list_quote(listObject *self)
/* empty arrays are converted to NULLs (still searching for a way to /* empty arrays are converted to NULLs (still searching for a way to
insert an empty array in postgresql */ insert an empty array in postgresql */
if (len == 0) return PyString_FromString("NULL"); if (len == 0) return PyString_FromString("'{}'");
tmp = PyTuple_New(len); tmp = PyTuple_New(len);

View File

@ -45,11 +45,11 @@ qstring_escape(char *to, char *from, size_t len, PGconn *conn)
(PG_MAJOR_VERSION == 8 && PG_MINOR_VERSION > 1) || \ (PG_MAJOR_VERSION == 8 && PG_MINOR_VERSION > 1) || \
(PG_MAJOR_VERSION == 8 && PG_MINOR_VERSION == 1 && PG_PATCH_VERSION >= 4) (PG_MAJOR_VERSION == 8 && PG_MINOR_VERSION == 1 && PG_PATCH_VERSION >= 4)
int err; int err;
if (conn)
return PQescapeStringConn(conn, to, from, len, &err); return PQescapeStringConn(conn, to, from, len, &err);
#else else
#warning "YOUR POSTGRESQL VERSION IS TOO OLD AND IT CAN BE INSECURE"
return PQescapeString(to, from, len);
#endif #endif
return PQescapeString(to, from, len);
} }
#else #else
static size_t static size_t
@ -147,7 +147,7 @@ qstring_quote(qstringObject *self)
Py_BEGIN_ALLOW_THREADS; Py_BEGIN_ALLOW_THREADS;
len = qstring_escape(buffer+1, s, len, len = qstring_escape(buffer+1, s, len,
((connectionObject*)self->conn)->pgconn); self->conn ? ((connectionObject*)self->conn)->pgconn : NULL);
buffer[0] = '\'' ; buffer[len+1] = '\''; buffer[0] = '\'' ; buffer[len+1] = '\'';
Py_END_ALLOW_THREADS; Py_END_ALLOW_THREADS;

View File

@ -112,7 +112,7 @@ conn_connect(connectionObject *self)
return -1; return -1;
} }
tmp = PQgetvalue(pgres, 0, 0); tmp = PQgetvalue(pgres, 0, 0);
self->encoding = PyMem_Malloc(strlen(tmp)); self->encoding = PyMem_Malloc(strlen(tmp)+1);
if (self->encoding == NULL) { if (self->encoding == NULL) {
/* exception already set by PyMem_Malloc() */ /* exception already set by PyMem_Malloc() */
PQfinish(pgconn); PQfinish(pgconn);

View File

@ -260,6 +260,9 @@ static struct PyMemberDef connectionObject_members[] = {
{"notifies", T_OBJECT, offsetof(connectionObject, notifies), RO}, {"notifies", T_OBJECT, offsetof(connectionObject, notifies), RO},
{"dsn", T_STRING, offsetof(connectionObject, dsn), RO, {"dsn", T_STRING, offsetof(connectionObject, dsn), RO,
"The current connection string."}, "The current connection string."},
{"status", T_LONG,
offsetof(connectionObject, status), RO,
"The current transaction status."},
#endif #endif
{NULL} {NULL}
}; };

View File

@ -434,7 +434,7 @@ psyco_curs_execute(cursorObject *self, PyObject *args, PyObject *kwargs)
} }
#define psyco_curs_executemany_doc \ #define psyco_curs_executemany_doc \
"executemany(query, vars_list=(), async=0) -- Execute many queries with bound vars." "executemany(query, vars_list) -- Execute many queries with bound vars."
static PyObject * static PyObject *
psyco_curs_executemany(cursorObject *self, PyObject *args, PyObject *kwargs) psyco_curs_executemany(cursorObject *self, PyObject *args, PyObject *kwargs)
@ -442,9 +442,9 @@ psyco_curs_executemany(cursorObject *self, PyObject *args, PyObject *kwargs)
PyObject *operation = NULL, *vars = NULL; PyObject *operation = NULL, *vars = NULL;
PyObject *v, *iter = NULL; PyObject *v, *iter = NULL;
static char *kwlist[] = {"query", "vars", NULL}; static char *kwlist[] = {"query", "vars_list", NULL};
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|O", kwlist, if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO", kwlist,
&operation, &vars)) { &operation, &vars)) {
return NULL; return NULL;
} }
@ -891,6 +891,7 @@ psyco_curs_callproc(cursorObject *self, PyObject *args, PyObject *kwargs)
if(parameters && parameters != Py_None) { if(parameters && parameters != Py_None) {
nparameters = PyObject_Length(parameters); nparameters = PyObject_Length(parameters);
if (nparameters < 0) nparameters = 0;
} }
/* allocate some memory, build the SQL and create a PyString from it */ /* allocate some memory, build the SQL and create a PyString from it */
@ -1047,7 +1048,7 @@ psyco_curs_scroll(cursorObject *self, PyObject *args, PyObject *kwargs)
/* extension: copy_from - implements COPY FROM */ /* extension: copy_from - implements COPY FROM */
#define psyco_curs_copy_from_doc \ #define psyco_curs_copy_from_doc \
"copy_from(file, table, sep='\\t', null='\\N') -- Copy table from file." "copy_from(file, table, sep='\\t', null='\\N', columns=None) -- Copy table from file."
static int static int
_psyco_curs_has_read_check(PyObject* o, void* var) _psyco_curs_has_read_check(PyObject* o, void* var)
@ -1068,29 +1069,76 @@ _psyco_curs_has_read_check(PyObject* o, void* var)
static PyObject * static PyObject *
psyco_curs_copy_from(cursorObject *self, PyObject *args, PyObject *kwargs) psyco_curs_copy_from(cursorObject *self, PyObject *args, PyObject *kwargs)
{ {
char query[256]; char query[1024];
char *table_name; char *table_name;
char *sep = "\t", *null = NULL; char *sep = "\t", *null = NULL;
long int bufsize = DEFAULT_COPYSIZE; long int bufsize = DEFAULT_COPYSIZE;
PyObject *file, *columns, *res = NULL; PyObject *file, *columns = NULL, *res = NULL;
char columnlist[1024] = "";
static char *kwlist[] = {"file", "table", "sep", "null", "size", NULL}; static char *kwlist[] = {"file", "table", "sep", "null", "size",
"columns", NULL};
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&s|ssi", kwlist, if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&s|ssiO", kwlist,
_psyco_curs_has_read_check, &file, _psyco_curs_has_read_check, &file,
&table_name, &sep, &null, &bufsize)) { &table_name, &sep, &null, &bufsize,
&columns)) {
return NULL;
}
if (columns != NULL && columns != Py_None) {
PyObject* collistiter = PyObject_GetIter(columns);
if (collistiter == NULL) {
return NULL;
}
PyObject* col;
int collistlen = 2;
int colitemlen;
char* colname;
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()) {
return NULL; return NULL;
} }
EXC_IF_CURS_CLOSED(self); EXC_IF_CURS_CLOSED(self);
if (null) { if (null) {
PyOS_snprintf(query, 255, "COPY %s FROM stdin USING DELIMITERS '%s'" PyOS_snprintf(query, 1023, "COPY %s%s FROM stdin USING DELIMITERS '%s'"
" WITH NULL AS '%s'", table_name, sep, null); " WITH NULL AS '%s'", table_name, columnlist, sep, null);
} }
else { else {
PyOS_snprintf(query, 255, "COPY %s FROM stdin USING DELIMITERS '%s'", PyOS_snprintf(query, 1023, "COPY %s%s FROM stdin USING DELIMITERS '%s'",
table_name, sep); table_name, columnlist, sep);
} }
Dprintf("psyco_curs_copy_from: query = %s", query); Dprintf("psyco_curs_copy_from: query = %s", query);

View File

@ -85,7 +85,7 @@ pq_raise(connectionObject *conn, cursorObject *curs, PyObject *exc, char *msg)
#ifdef HAVE_PQPROTOCOL3 #ifdef HAVE_PQPROTOCOL3
char *pgstate = char *pgstate =
PQresultErrorField(curs->pgres, PG_DIAG_SQLSTATE); PQresultErrorField(curs->pgres, PG_DIAG_SQLSTATE);
if (!strncmp(pgstate, "23", 2)) if (pgstate != NULL && !strncmp(pgstate, "23", 2))
exc = IntegrityError; exc = IntegrityError;
else else
exc = ProgrammingError; exc = ProgrammingError;

View File

@ -118,12 +118,15 @@ _psyco_connect_fill_exc(connectionObject *conn)
Py_INCREF(DataError); Py_INCREF(DataError);
conn->exc_NotSupportedError = NotSupportedError; conn->exc_NotSupportedError = NotSupportedError;
Py_INCREF(NotSupportedError); Py_INCREF(NotSupportedError);
conn->exc_OperationalError = OperationalError;
Py_INCREF(OperationalError);
} }
static PyObject * static PyObject *
psyco_connect(PyObject *self, PyObject *args, PyObject *keywds) psyco_connect(PyObject *self, PyObject *args, PyObject *keywds)
{ {
PyObject *conn, *factory = NULL; PyObject *conn, *factory = NULL;
PyObject *pyport = NULL;
int idsn=-1, iport=-1; int idsn=-1, iport=-1;
char *dsn=NULL, *database=NULL, *user=NULL, *password=NULL; char *dsn=NULL, *database=NULL, *user=NULL, *password=NULL;
@ -134,12 +137,25 @@ psyco_connect(PyObject *self, PyObject *args, PyObject *keywds)
"user", "password", "sslmode", "user", "password", "sslmode",
"connection_factory", NULL}; "connection_factory", NULL};
if (!PyArg_ParseTupleAndKeywords(args, keywds, "|sssisssO", kwlist, if (!PyArg_ParseTupleAndKeywords(args, keywds, "|sssOsssO", kwlist,
&dsn, &database, &host, &iport, &dsn, &database, &host, &pyport,
&user, &password, &sslmode, &factory)) { &user, &password, &sslmode, &factory)) {
return NULL; return NULL;
} }
if (pyport && PyString_Check(pyport)) {
PyObject *pyint = PyInt_FromString(PyString_AsString(pyport), NULL, 10);
if (!pyint) return NULL;
iport = PyInt_AsLong(pyint);
}
else if (pyport && PyInt_Check(pyport)) {
iport = PyInt_AsLong(pyport);
}
else if (pyport != NULL) {
PyErr_SetString(PyExc_TypeError, "port must be a string or int");
return NULL;
}
if (iport > 0) if (iport > 0)
PyOS_snprintf(port, 16, "%d", iport); PyOS_snprintf(port, 16, "%d", iport);
@ -429,6 +445,7 @@ psyco_set_error(PyObject *exc, PyObject *curs, char *msg,
PyObject_SetAttrString(err, "cursor", Py_None); PyObject_SetAttrString(err, "cursor", Py_None);
PyErr_SetObject(exc, err); PyErr_SetObject(exc, err);
Py_DECREF(err);
} }
} }

View File

@ -39,7 +39,7 @@ typecast_array_cleanup(char **str, int *len)
if ((*str)[i] != '=') return -1; if ((*str)[i] != '=') return -1;
*str = &((*str)[i+1]); *str = &((*str)[i+1]);
*len = *len - i - 2; *len = *len - i - 1;
return 0; return 0;
} }
@ -218,6 +218,8 @@ typecast_GENERIC_ARRAY_cast(char *str, int len, PyObject *curs)
PyObject *obj = NULL; PyObject *obj = NULL;
PyObject *base = ((typecastObject*)((cursorObject*)curs)->caster)->bcast; PyObject *base = ((typecastObject*)((cursorObject*)curs)->caster)->bcast;
Dprintf("typecast_GENERIC_ARRAY_cast: str = '%s', len = %d", str, len);
if (str == NULL) {Py_INCREF(Py_None); return Py_None;} if (str == NULL) {Py_INCREF(Py_None); return Py_None;}
if (str[0] == '[') if (str[0] == '[')
typecast_array_cleanup(&str, &len); typecast_array_cleanup(&str, &len);
@ -226,7 +228,7 @@ typecast_GENERIC_ARRAY_cast(char *str, int len, PyObject *curs)
return NULL; return NULL;
} }
Dprintf("typecast_GENERIC_ARRAY_cast: scanning %s", str); Dprintf("typecast_GENERIC_ARRAY_cast: str = '%s', len = %d", str, len);
obj = PyList_New(0); obj = PyList_New(0);

View File

@ -54,15 +54,14 @@ typecast_LONGINTEGER_cast(char *s, int len, PyObject *curs)
static PyObject * static PyObject *
typecast_FLOAT_cast(char *s, int len, PyObject *curs) typecast_FLOAT_cast(char *s, int len, PyObject *curs)
{ {
/* FIXME: is 64 large enough for any float? */ PyObject *str = NULL, *flo = NULL;
char buffer[64]; char *pend;
if (s == NULL) {Py_INCREF(Py_None); return Py_None;} if (s == NULL) {Py_INCREF(Py_None); return Py_None;}
if (s[len] != '\0') { str = PyString_FromStringAndSize(s, len);
strncpy(buffer, s, len); buffer[len] = '\0'; flo = PyFloat_FromString(str, &pend);
s = buffer; Py_DECREF(str);
} return flo;
return PyFloat_FromDouble(atof(s));
} }
/** STRING - cast strings of any type to python string **/ /** STRING - cast strings of any type to python string **/

View File

@ -1,21 +1,28 @@
import psycopg import psycopg2
conn = psycopg.connect("dbname=test") conn = psycopg2.connect("dbname=test")
curs = conn.cursor() curs = conn.cursor()
curs.execute("SELECT ARRAY[1,2,3] AS foo") #curs.execute("SELECT ARRAY[1,2,3] AS foo")
print curs.fetchone()[0] #print curs.fetchone()[0]
curs.execute("SELECT ARRAY['1','2','3'] AS foo") #curs.execute("SELECT ARRAY['1','2','3'] AS foo")
print curs.fetchone()[0] #print curs.fetchone()[0]
curs.execute("""SELECT ARRAY[',','"','\\\\'] AS foo""") #curs.execute("""SELECT ARRAY[',','"','\\\\'] AS foo""")
d = curs.fetchone()[0] #d = curs.fetchone()[0]
print d, '->', d[0], d[1], d[2] #print d, '->', d[0], d[1], d[2]
curs.execute("SELECT ARRAY[ARRAY[1,2],ARRAY[3,4]] AS foo") #curs.execute("SELECT ARRAY[ARRAY[1,2],ARRAY[3,4]] AS foo")
print curs.fetchone()[0] #print curs.fetchone()[0]
#curs.execute("SELECT ARRAY[ARRAY[now(), now()], ARRAY[now(), now()]] AS foo")
#print curs.description
#print curs.fetchone()[0]
curs.execute("SELECT 1 AS foo, ARRAY[1,2] AS bar")
print curs.fetchone()
curs.execute("SELECT * FROM test()")
print curs.fetchone()
curs.execute("SELECT ARRAY[ARRAY[now(), now()], ARRAY[now(), now()]] AS foo")
print curs.description
print curs.fetchone()[0]

19
sandbox/gtk.py Normal file
View File

@ -0,0 +1,19 @@
import psycopg2
o = psycopg2.connect("dbname=test")
c = o.cursor()
def sql():
c.execute("SELECT 1.23 AS foo")
print 1, c.fetchone()
#print c.description
c.execute("SELECT 1.23::float AS foo")
print 2, c.fetchone()
#print c.description
print "BEFORE"
sql()
import gtk
print "AFTER"
sql()

9
sandbox/textfloat.py Normal file
View File

@ -0,0 +1,9 @@
import gtk
import psycopg2
o = psycopg2.connect("dbname=test")
c = o.cursor()
c.execute("SELECT 1.23::float AS foo")
x = c.fetchone()[0]
print x, type(x)

View File

@ -52,7 +52,7 @@ from distutils.command.build_ext import build_ext
from distutils.sysconfig import get_python_inc from distutils.sysconfig import get_python_inc
from distutils.ccompiler import get_default_compiler from distutils.ccompiler import get_default_compiler
PSYCOPG_VERSION = '2.0' PSYCOPG_VERSION = '2.0.5'
version_flags = [] version_flags = []
# to work around older distutil limitations # to work around older distutil limitations
@ -95,7 +95,6 @@ class psycopg_build_ext(build_ext):
def initialize_options(self): def initialize_options(self):
build_ext.initialize_options(self) build_ext.initialize_options(self)
self.use_pydatetime = 1
self.use_pg_dll = 1 self.use_pg_dll = 1
self.pgdir = None self.pgdir = None
self.pg_config = self.DEFAULT_PG_CONFIG self.pg_config = self.DEFAULT_PG_CONFIG

View File

@ -62,6 +62,9 @@ class TypesBasicTests(TestCase):
b = psycopg2.Binary(s) b = psycopg2.Binary(s)
r = str(self.execute("SELECT %s::bytea AS foo", (b,))) r = str(self.execute("SELECT %s::bytea AS foo", (b,)))
self.failUnless(r == s, "wrong binary quoting") self.failUnless(r == s, "wrong binary quoting")
# test to make sure an empty Binary is converted to an empty string
b = psycopg2.Binary('')
self.assertEqual(str(b), "''")
def testArray(self): def testArray(self):
s = self.execute("SELECT %s AS foo", ([[1,2],[3,4]],)) s = self.execute("SELECT %s AS foo", ([[1,2],[3,4]],))