diff --git a/ChangeLog b/ChangeLog index 844062e6..57b803b6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -3,6 +3,12 @@ * connection.h: added codec attribute to avoid repeated codec name lookups during unicode query/params manipulations. + * setup.py: bumped to version 2.3.2.dev0 + + * psycopg/connection_int.c: applied patch from Marti Raudsepp to close + ticket #24. Fixed segfault in connection when DateStyle not available + (e.g. pgbouncer appars not passing it to the client) + 2010-12-15 Daniele Varrazzo * psycopg/utils.c: Added psycopg_strdup function. diff --git a/Makefile b/Makefile index d33787b3..0e39cb7f 100644 --- a/Makefile +++ b/Makefile @@ -62,6 +62,9 @@ docs-html: doc/html/genindex.html docs-txt: doc/psycopg2.txt +# for PyPI documentation +docs-zip: doc/docs.zip + sdist: $(SDIST) # The environment is currently required to build the documentation. @@ -119,6 +122,8 @@ doc/html/genindex.html: $(PLATLIB) $(PURELIB) $(SOURCE_DOC) doc/psycopg2.txt: $(PLATLIB) $(PURELIB) $(SOURCE_DOC) PYTHONPATH=$(ENV_LIB):$(BUILD_DIR) $(MAKE) SPHINXBUILD=$(ENV_BIN)/sphinx-build -C doc text +doc/docs.zip: doc/html/genindex.html + (cd doc/html && zip -r ../docs.zip *) clean: rm -rf build MANIFEST diff --git a/NEWS-2.3 b/NEWS-2.3 index 704996e4..05462025 100644 --- a/NEWS-2.3 +++ b/NEWS-2.3 @@ -1,12 +1,26 @@ -What's new in psycopg 2.3.2 +What's new in psycopg 2.3.3 --------------------------- +* Changes: + - Improved PostgreSQL-Python encodings mapping. Added a few missing encodings: EUC_CN, EUC_JIS_2004, ISO885910, ISO885916, LATIN10, SHIFT_JIS_2004. - Dropped repeated dictionary lookups with unicode query/parameters. - Empty lists correctly roundtrip Python -> PostgreSQL -> Python. +* Bug fixes + + - Fixed adaptation of None in composite types (ticket #26). Bug report by + Karsten Hilbert. + + +What's new in psycopg 2.3.2 +--------------------------- + + - Fixed segfault with middleware not passing DateStyle to the client + (ticket #24). Bug report and patch by Marti Raudsepp. + What's new in psycopg 2.3.1 --------------------------- diff --git a/ZPsycopgDA/DA.py b/ZPsycopgDA/DA.py index e9f4312f..d88766ef 100644 --- a/ZPsycopgDA/DA.py +++ b/ZPsycopgDA/DA.py @@ -16,7 +16,7 @@ # their work without bothering about the module dependencies. -ALLOWED_PSYCOPG_VERSIONS = ('2.3.0','2.3.1') +ALLOWED_PSYCOPG_VERSIONS = ('2.3.0','2.3.1','2.3.2') import sys import time diff --git a/lib/__init__.py b/lib/__init__.py index 2968284c..065b2a70 100644 --- a/lib/__init__.py +++ b/lib/__init__.py @@ -82,6 +82,7 @@ from _psycopg import __version__ import psycopg2.extensions as _ext _ext.register_adapter(tuple, _ext.SQL_IN) +_ext.register_adapter(type(None), _ext.NoneAdapter) __all__ = filter(lambda k: not k.startswith('_'), locals().keys()) diff --git a/lib/extensions.py b/lib/extensions.py index 43092fb3..e9b6813c 100644 --- a/lib/extensions.py +++ b/lib/extensions.py @@ -138,6 +138,19 @@ class SQL_IN(object): __str__ = getquoted +class NoneAdapter(object): + """Adapt None to NULL. + + This adapter is not used normally as a fast path in mogrify uses NULL, + but it makes easier to adapt composite types. + """ + def __init__(self, obj): + pass + + def getquoted(self): + return "NULL" + + # Add the "cleaned" version of the encodings to the key. # When the encoding is set its name is cleaned up from - and _ and turned # uppercase, so an encoding not respecting these rules wouldn't be found in the @@ -146,4 +159,5 @@ for k, v in encodings.items(): k = k.replace('_', '').replace('-', '').upper() encodings[k] = v + __all__ = filter(lambda k: not k.startswith('_'), locals().keys()) diff --git a/psycopg/connection_int.c b/psycopg/connection_int.c index 83c77ad0..6dc7ebb9 100644 --- a/psycopg/connection_int.c +++ b/psycopg/connection_int.c @@ -373,6 +373,10 @@ conn_is_datestyle_ok(PGconn *pgconn) ds = PQparameterStatus(pgconn, "DateStyle"); Dprintf("conn_connect: DateStyle %s", ds); + /* pgbouncer does not pass on DateStyle */ + if (ds == NULL) + return 0; + /* Return true if ds starts with "ISO" * e.g. "ISO, DMY" is fine, "German" not. */ return (ds[0] == 'I' && ds[1] == 'S' && ds[2] == 'O'); diff --git a/psycopg/microprotocols.c b/psycopg/microprotocols.c index 2173815d..f48c0fbf 100644 --- a/psycopg/microprotocols.c +++ b/psycopg/microprotocols.c @@ -134,11 +134,6 @@ microprotocols_adapt(PyObject *obj, PyObject *proto, PyObject *alt) /* we don't check for exact type conformance as specified in PEP 246 because the ISQLQuote type is abstract and there is no way to get a quotable object to be its instance */ - - /* None is always adapted to NULL */ - - if (obj == Py_None) - return Bytes_FromString("NULL"); Dprintf("microprotocols_adapt: trying to adapt %s", Py_TYPE(obj)->tp_name); diff --git a/setup.py b/setup.py index 701ad5bf..8296fec7 100644 --- a/setup.py +++ b/setup.py @@ -66,7 +66,7 @@ except ImportError: # Take a look at http://www.python.org/dev/peps/pep-0386/ # for a consistent versioning pattern. -PSYCOPG_VERSION = '2.3.2.dev0' +PSYCOPG_VERSION = '2.3.3.dev0' version_flags = ['dt', 'dec'] @@ -471,8 +471,8 @@ setup(name="psycopg2", maintainer_email="fog@initd.org", author="Federico Di Gregorio", author_email="fog@initd.org", - url="http://initd.org/tracker/psycopg", - download_url = "http://initd.org/pub/software/psycopg2", + url="http://initd.org/psycopg/", + download_url = "http://initd.org/psycopg/download/", license="GPL with exceptions or ZPL", platforms = ["any"], description=__doc__.split("\n")[0], diff --git a/tests/test_cancel.py b/tests/test_cancel.py index 8e651ffa..6f9295de 100644 --- a/tests/test_cancel.py +++ b/tests/test_cancel.py @@ -35,7 +35,7 @@ class CancelTests(unittest.TestCase): cur = conn.cursor() try: self.assertRaises(psycopg2.extensions.QueryCanceledError, - cur.execute, "select pg_sleep(10000)") + cur.execute, "select pg_sleep(60)") # make sure the connection still works conn.rollback() cur.execute("select 1") diff --git a/tests/types_extras.py b/tests/types_extras.py index cb17fdf4..8b2d5fd1 100644 --- a/tests/types_extras.py +++ b/tests/types_extras.py @@ -354,6 +354,42 @@ class HstoreTestCase(unittest.TestCase): ok(dict(zip(ab, ab))) +class AdaptTypeTestCase(unittest.TestCase): + def setUp(self): + self.conn = psycopg2.connect(tests.dsn) + + def tearDown(self): + self.conn.close() + + def test_none_in_record(self): + curs = self.conn.cursor() + s = curs.mogrify("SELECT %s;", [(42, None)]) + self.assertEqual("SELECT (42, NULL);", s) + curs.execute("SELECT %s;", [(42, None)]) + d = curs.fetchone()[0] + self.assertEqual("(42,)", d) + + def test_none_fast_path(self): + # the None adapter is not actually invoked in regular adaptation + ext = psycopg2.extensions + + class WonkyAdapter(object): + def __init__(self, obj): pass + def getquoted(self): return "NOPE!" + + curs = self.conn.cursor() + + orig_adapter = ext.adapters[type(None), ext.ISQLQuote] + try: + ext.register_adapter(type(None), WonkyAdapter) + self.assertEqual(ext.adapt(None).getquoted(), "NOPE!") + + s = curs.mogrify("SELECT %s;", (None,)) + self.assertEqual("SELECT NULL;", s) + + finally: + ext.register_adapter(type(None), orig_adapter) + def test_suite(): return unittest.TestLoader().loadTestsFromName(__name__)