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>
* Release 2.0.
* ZPsycopgDA/DA.py: removed Browse table for 2.0 release; we'll
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
-------------------------

View File

@ -18,7 +18,7 @@
# 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 time
@ -30,11 +30,21 @@ import Shared.DC.ZRDB.Connection
from db import DB
from Globals import HTMLFile
from ImageFile import ImageFile
from ExtensionClass import Base
from App.Dialogs import MessageDialog
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 psycopg2

View File

@ -26,7 +26,7 @@ import site
import pool
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
@ -42,6 +42,7 @@ class DB(TM, dbi_db.DB):
self.encoding = enc
self.failures = 0
self.calls = 0
self.make_mappings()
def getconn(self, create=True):
conn = pool.getconn(self.dsn)
@ -89,32 +90,23 @@ class DB(TM, dbi_db.DB):
def sortKey(self):
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):
"""Convert DBAPI-2.0 description field to Zope format."""
items = []
for name, typ, width, ds, p, scale, null_ok in desc:
if typ == NUMBER:
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
m = self.type_mappings.get(typ, (STRING, 's'))
items.append({
'name': name,
'type': use_psycopg_types and typp or typs,
'type': use_psycopg_types and m[0] or m[1],
'width': width,
'precision': p,
'scale': scale,

View File

@ -51,6 +51,16 @@ ISOLATION_LEVEL_SERIALIZABLE = 2
ISOLATION_LEVEL_REPEATABLE_READ = ISOLATION_LEVEL_SERIALIZABLE
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):
"""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
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
def dbg(*args):
LOG('ZPsycopgDA', DEBUG, "",
' '.join([str(x) for x in args])+'\n')
LOG('ZPsycopgDA', INFO, "Installed", "Logging using Zope's zLOG\n")
except:
import sys
def dbg(*args):

View File

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

View File

@ -64,15 +64,14 @@ pydatetime_str(pydatetimeObject *self)
PyDateTime_Delta *obj = (PyDateTime_Delta*)self->wrapped;
char buffer[8];
int i, j, x;
int i;
int a = obj->microseconds;
for (i=1000000, j=0; i > 0 ; i /= 10) {
x = a/i;
a -= x*i;
buffer[j++] = '0'+x;
for (i=0; i < 6 ; i++) {
buffer[5-i] = '0' + (a % 10);
a /= 10;
}
buffer[j] = '\0';
buffer[6] = '\0';
return PyString_FromFormat("'%d days %d.%s seconds'",
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
insert an empty array in postgresql */
if (len == 0) return PyString_FromString("NULL");
if (len == 0) return PyString_FromString("'{}'");
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_PATCH_VERSION >= 4)
int err;
if (conn)
return PQescapeStringConn(conn, to, from, len, &err);
#else
#warning "YOUR POSTGRESQL VERSION IS TOO OLD AND IT CAN BE INSECURE"
return PQescapeString(to, from, len);
else
#endif
return PQescapeString(to, from, len);
}
#else
static size_t
@ -147,7 +147,7 @@ qstring_quote(qstringObject *self)
Py_BEGIN_ALLOW_THREADS;
len = qstring_escape(buffer+1, s, len,
((connectionObject*)self->conn)->pgconn);
self->conn ? ((connectionObject*)self->conn)->pgconn : NULL);
buffer[0] = '\'' ; buffer[len+1] = '\'';
Py_END_ALLOW_THREADS;

View File

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

View File

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

View File

@ -434,7 +434,7 @@ psyco_curs_execute(cursorObject *self, PyObject *args, PyObject *kwargs)
}
#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 *
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 *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)) {
return NULL;
}
@ -891,6 +891,7 @@ psyco_curs_callproc(cursorObject *self, PyObject *args, PyObject *kwargs)
if(parameters && parameters != Py_None) {
nparameters = PyObject_Length(parameters);
if (nparameters < 0) nparameters = 0;
}
/* 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 */
#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
_psyco_curs_has_read_check(PyObject* o, void* var)
@ -1068,29 +1069,76 @@ _psyco_curs_has_read_check(PyObject* o, void* var)
static PyObject *
psyco_curs_copy_from(cursorObject *self, PyObject *args, PyObject *kwargs)
{
char query[256];
char query[1024];
char *table_name;
char *sep = "\t", *null = NULL;
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,
&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;
}
EXC_IF_CURS_CLOSED(self);
if (null) {
PyOS_snprintf(query, 255, "COPY %s FROM stdin USING DELIMITERS '%s'"
" WITH NULL AS '%s'", table_name, sep, null);
PyOS_snprintf(query, 1023, "COPY %s%s FROM stdin USING DELIMITERS '%s'"
" WITH NULL AS '%s'", table_name, columnlist, sep, null);
}
else {
PyOS_snprintf(query, 255, "COPY %s FROM stdin USING DELIMITERS '%s'",
table_name, sep);
PyOS_snprintf(query, 1023, "COPY %s%s FROM stdin USING DELIMITERS '%s'",
table_name, columnlist, sep);
}
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
char *pgstate =
PQresultErrorField(curs->pgres, PG_DIAG_SQLSTATE);
if (!strncmp(pgstate, "23", 2))
if (pgstate != NULL && !strncmp(pgstate, "23", 2))
exc = IntegrityError;
else
exc = ProgrammingError;

View File

@ -118,12 +118,15 @@ _psyco_connect_fill_exc(connectionObject *conn)
Py_INCREF(DataError);
conn->exc_NotSupportedError = NotSupportedError;
Py_INCREF(NotSupportedError);
conn->exc_OperationalError = OperationalError;
Py_INCREF(OperationalError);
}
static PyObject *
psyco_connect(PyObject *self, PyObject *args, PyObject *keywds)
{
PyObject *conn, *factory = NULL;
PyObject *pyport = NULL;
int idsn=-1, iport=-1;
char *dsn=NULL, *database=NULL, *user=NULL, *password=NULL;
@ -134,12 +137,25 @@ psyco_connect(PyObject *self, PyObject *args, PyObject *keywds)
"user", "password", "sslmode",
"connection_factory", NULL};
if (!PyArg_ParseTupleAndKeywords(args, keywds, "|sssisssO", kwlist,
&dsn, &database, &host, &iport,
if (!PyArg_ParseTupleAndKeywords(args, keywds, "|sssOsssO", kwlist,
&dsn, &database, &host, &pyport,
&user, &password, &sslmode, &factory)) {
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)
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);
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;
*str = &((*str)[i+1]);
*len = *len - i - 2;
*len = *len - i - 1;
return 0;
}
@ -218,6 +218,8 @@ typecast_GENERIC_ARRAY_cast(char *str, int len, PyObject *curs)
PyObject *obj = NULL;
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[0] == '[')
typecast_array_cleanup(&str, &len);
@ -226,7 +228,7 @@ typecast_GENERIC_ARRAY_cast(char *str, int len, PyObject *curs)
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);

View File

@ -54,15 +54,14 @@ typecast_LONGINTEGER_cast(char *s, int len, PyObject *curs)
static PyObject *
typecast_FLOAT_cast(char *s, int len, PyObject *curs)
{
/* FIXME: is 64 large enough for any float? */
char buffer[64];
PyObject *str = NULL, *flo = NULL;
char *pend;
if (s == NULL) {Py_INCREF(Py_None); return Py_None;}
if (s[len] != '\0') {
strncpy(buffer, s, len); buffer[len] = '\0';
s = buffer;
}
return PyFloat_FromDouble(atof(s));
str = PyString_FromStringAndSize(s, len);
flo = PyFloat_FromString(str, &pend);
Py_DECREF(str);
return flo;
}
/** 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.execute("SELECT ARRAY[1,2,3] AS foo")
print curs.fetchone()[0]
#curs.execute("SELECT ARRAY[1,2,3] AS foo")
#print curs.fetchone()[0]
curs.execute("SELECT ARRAY['1','2','3'] AS foo")
print curs.fetchone()[0]
#curs.execute("SELECT ARRAY['1','2','3'] AS foo")
#print curs.fetchone()[0]
curs.execute("""SELECT ARRAY[',','"','\\\\'] AS foo""")
d = curs.fetchone()[0]
print d, '->', d[0], d[1], d[2]
#curs.execute("""SELECT ARRAY[',','"','\\\\'] AS foo""")
#d = curs.fetchone()[0]
#print d, '->', d[0], d[1], d[2]
curs.execute("SELECT ARRAY[ARRAY[1,2],ARRAY[3,4]] AS foo")
print curs.fetchone()[0]
#curs.execute("SELECT ARRAY[ARRAY[1,2],ARRAY[3,4]] AS foo")
#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.ccompiler import get_default_compiler
PSYCOPG_VERSION = '2.0'
PSYCOPG_VERSION = '2.0.5'
version_flags = []
# to work around older distutil limitations
@ -95,7 +95,6 @@ class psycopg_build_ext(build_ext):
def initialize_options(self):
build_ext.initialize_options(self)
self.use_pydatetime = 1
self.use_pg_dll = 1
self.pgdir = None
self.pg_config = self.DEFAULT_PG_CONFIG

View File

@ -62,6 +62,9 @@ class TypesBasicTests(TestCase):
b = psycopg2.Binary(s)
r = str(self.execute("SELECT %s::bytea AS foo", (b,)))
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):
s = self.execute("SELECT %s AS foo", ([[1,2],[3,4]],))