diff -Nru psycopg2-2.0.13/AUTHORS psycopg2-2.4.5/AUTHORS --- psycopg2-2.0.13/AUTHORS 2009-04-26 12:09:21.000000000 +0000 +++ psycopg2-2.4.5/AUTHORS 2011-02-06 15:58:34.000000000 +0000 @@ -1,8 +1,15 @@ Main authors: Federico Di Gregorio + Daniele Varrazzo For the win32 port: - Jason Erickson (most of his changes are still in 2.0) + Jason Erickson Additional Help: + Peter Fein contributed a logging connection/cursor class that even if it + was not used directly heavily influenced the implementation currently in + psycopg2.extras. + + Jan Urbański (re)started the work on asynchronous queries and contributed + both on that and on other parts of psycopg2. diff -Nru psycopg2-2.0.13/ChangeLog psycopg2-2.4.5/ChangeLog --- psycopg2-2.0.13/ChangeLog 2009-10-04 21:42:29.000000000 +0000 +++ psycopg2-2.4.5/ChangeLog 2011-02-06 15:58:34.000000000 +0000 @@ -1,3 +1,345 @@ +2010-12-18 Daniele Varrazzo + + * 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. + +2010-12-14 Daniele Varrazzo + + * psycopg/connection_type.c: No need to put connection fields to zero. + + * lib/extensions.py: Improved mapping from PG to Py encodings. + + * psycopg/psycopgmodule.c: Added a few missing encodings: EUC_CN, + EUC_JIS_2004, ISO885910, ISO885916, LATIN10, SHIFT_JIS_2004. + +2010-12-04 Daniele Varrazzo + + * setup.py: bumped to version 2.3.1.dev0 + + * datetime modules reorganized to work around CentOS 5.5 x86_64 buld + problem. Closes ticket #23 + + * psycopg/typecast.h: dropped private functions interfaces. + +2010-12-01 Daniele Varrazzo + + * lib/extras.py: DictRow items can be updated. Patch by Alex Aster. + +2010-11-28 Daniele Varrazzo + + * Cancel patch from Jan integrated. + + * psycopg/connection_type.c: can't cancel a prepared connection + +2010-11-22 Daniele Varrazzo + + * psycopg/connection_int.c: dropped notices hack to get COPY errors from + V2 protocol. + +2010-11-18 Daniele Varrazzo + + * psycopg/connection.h: Added enum with possilbe isolation level states. + Also, general isolation levels cleanup and tests added. + + * psycopg/utils.c: compiler warning dropped. + + * typecast.h: functions exported to drop warnings. + + * datetime module initialized at is supposed to be. + + * mx.DateTime module initialized at is supposed to be. + +2010-11-17 Daniele Varrazzo + + * psycopg/connection_type.c: don't clobber exception if + isolation_level_switch fails. + +2010-11-16 Daniele Varrazzo + + * psycopg/connection_int.c: abort connection to protocol 2 server. + + * psycopg/pqpath.c + * psycopg/connection_int.c: dropped support for protocol 2 at compile + time and protocol 2-specific code. + + * psycopg/connection_int.c: don't run a query at every connection to detect + client encoding: use PQparameterStatus() instead. + + * psycopg/connection_int.c: don't run a query at every connection to set + the datestyle to ISO if PQparameterStatus() reports it already is. + +2010-11-11 Daniele Varrazzo + + * lib/extras.py: build the namedtuple only once per execution, not once + per fetch. + + * lib/extras.py: don't change the exception raised when fetching without a + result from NamedTupleCursor. + + * psycopg/connection_int.c: fixed notices order (ticket #9). + +2010-11-10 Daniele Varrazzo + + * psycopg/green.c: functions unused outside the module marked static. + +2010-11-09 Daniele Varrazzo + + * Replaced PyObject_CallFunction() with *ObjArgs() where more efficient. + + * Dropped PyArg_ParseTuple() calls in functions taking no arguments. + + * psycopg/microprotocols.c: small optimizations. + + * Avoid pointless string manipulation in NamedTupleCursor (ticket #10) + + * psycopg/microprotocols.c: Check the presence of a mro. + +2010-11-08 Daniele Varrazzo + + * psycopg/microprotocols.c: use faster function to build tuples. + + * psycopg/microprotocols.c: fixed refcount bug. + + * psycopg/microprotocols.c: use the adapter of an object superclass if + available. + + * psycopg/adapter_pdecimal.c: fixed crash in pdecimal_str with Python + 2.5.x releases in which is_finite() is not available. + +2010-11-06 Daniele Varrazzo + + * lib/extras.py: added NamedTupleCursor. + +2010-11-05 Daniele Varrazzo + + * setup.py: bumped to version 2.3.dev0 + + * merged dev branches for 2 phase commit, notify payload, hstore adapter + +2010-10-22 Daniele Varrazzo + + * MANIFEST.in: Dropped reference to removed TODO file: it breaks 'pip'. + +2010-10-08 Daniele Varrazzo + + * psycopg/typecast_binary.c: use PQfreemem to free memory allocated by + the libpq. Bug reported by Anton Kovalev. + + * dropped PSYCOPG_OWN_QUOTING. + + * psycopg/connection_int.c: Fixed access to freed memory in + conn_get_isolation_level(). Bug reported by Anton Kovalev. + +2010-10-06 Daniele Varrazzo + + * Merged James Henstridge work on two-phase commit support. + + 2008-07-24 James Henstridge + + * tests/test_psycopg2_dbapi20.py (Psycopg2TPCTests): hook up two + phase commit tests. + + * psycopg/xid_type.c (xid_len, xid_getitem): implement sequence + behaviour, as required for transaction IDs. + (XidType): There is no point in allowing subclasses of Xid. + + 2008-07-23 James Henstridge + + * psycopg/connection_type.c (psyco_conn_xid): add a + Connection.xid() method that instantiates Xid objects. + + * psycopg/psycopgmodule.c (init_psycopg): initialise the Xid + object type. + + * psycopg/xid.h: + * psycopg/xid_type.c: Implement a basic transaction ID object for + use in two phase commit. + + 2008-05-12 James Henstridge + + * beginnings of a TPC test harness + +2010-10-05 Daniele Varrazzo + + * psycopg/cursor_type.c: Common code in execute() and mogrify() merged. + + * psycopg/cursor_type.c: cursor.mogrify() accepts unicode queries. + +2010-09-23 Daniele Varrazzo + + * lib/errorcodes.py: Added PostgreSQL 9.0 error codes. + +2010-08-05 Daniele Varrazzo + + * psycopg/connection_int.c: don't execute a ROLLBACK on close()/GC. + The command wasn't sent since 2.2.0 due to a bug, but after a ML + discussion this behaviour proved more correct so the bug has become a + feature. + +2010-07-18 Federico Di Gregorio + + * Release 2.2.2. + +2010-07-09 Daniele Varrazzo + + * psycopg/cursor_type.c: executemany() propagates exceptions raised by the + iterable to the caller. + + * lib/pool.py: dropped logging.basicConfig() call. It messes up with + projects using logging but where no handler is installed on the root + logger. Bug reported by Joe Abbate. + + * psycopg/cursor_type.c: exceptions raised in the columns iterator of the + copy methods propagated to the caller. + +2010-05-20 Daniele Varrazzo + + * psycopg/typecast_datetime.c: Round seconds in historical timezones to + the nearest minute. + + * lib/extras.py: register_tstz_w_secs() is now no-op. + +2010-05-17 Federico Di Gregorio + + * Release 2.2.1. + + * Builds again on Windows. + +2010-05-16 Federico Di Gregorio + + * Release 2.2.0. + +2010-05-15 Federico Di Gregorio + + * typecast.c: Fixed problem related to receiving None from Python + when a string was expected. + +2010-05-07 Daniele Varrazzo + + * psycopg/adapter_datetime.c: Fixed TimestampFromTicks for second + values > 59.5. + + Bug reported and fixed by Jozsef Szalay on 2010-05-06 at 14:11:59.999920. + + * psycopg/adapter_datetime.c: Fixed same bug for TimeFromTicks. + +2010-05-04 Daniele Varrazzo + + * Added typecasters for arrays of specific MX/Py time-related types. + + * psycopg/adapter_[mx]datetime.c: Explicit cast of the SQL representation + of time-related objects. + +2010-05-03 Daniele Varrazzo + + * psycopg/adapter_binary.c: Adapt buffer objects using an explicit cast on + the string literal (bug reported by Peter Eisentraut) + +2010-04-20 Daniele Varrazzo + + * lib/pqpath.c: Fixed reference leak in notify reception. + + * Notifies are collected if available after every query execution. + +2010-04-13 Daniele Varrazzo + + * lib/extensions.py: DECIMAL typecaster imported from _psycopg. + + * lib/extensions.py: PY* and MX* time typecaster imported from _psycopg. + +2010-04-11 Daniele Varrazzo + + * psycopg/connection_type.c: Correctly parse keywords in connect(). + +2010-04-07 Daniele Varrazzo + + * psycopg/pqpath.c: Ensure running COPY in blocking mode. + + * psycopg/pqpath.c: Free the GIL in blocking operations in V2 COPY FROM. + + * psycopg/pqpath.c: Evaluate Python objects only once outside the COPY I/O + loops. + +2010-04-05 Federico Di Gregorio + + * Fixed problem with asynchronous NOTIFYs. + + * Integrated async pacthes from Jan's git tree. + +2010-03-13 Federico Di Gregorio + + * Release 2.0.14. + +2010-03-11 Federico Di Gregorio + + * setup.py: one-liner from Devrim GÜNDÜZ to build with PostgreSQL + alpha. + +2010-02-28 Federico Di Gregorio + + * psycopg/adapt_decimal.c: Python 2.4 decimal type does not support + .isfinite() and two different calls to ._isinfinity() and ._isnan() are + required. This fixes both a test failure and a segfault. + +2010-02-15 Federico Di Gregorio + + * Added new Decimal adapter that correctly converts NaN and infinity + to PostgreSQL NaN numeric values. Also added tests. + +2010-02-15 Daniele Varrazzo + + * lib/errorcodes.py: Updated to PostgreSQL 8.4; added lookup() function. + +2010-02-12 Daniele Varrazzo + + * Stop the loop variable used to create __all__ leaking in the module. + + * Fixed Inet constructor. + + * Fixed docstring for 'QueryCanceledError' exception. + +2010-02-10 Federico Di Gregorio + + * lib/extensions.py: Binary was not imported from _psycopg; now it is + + * lib/__init__: SQL_IN adapter is now automatically registered. + + * ZPsycopgDA/db.py: removed logging debug calls; psycopg does + not depend on the logger module. + +2010-02-12 Federico Di Gregorio + + * License migration: psycopg2 is now LGPL3 + OpenSSL exception. + + * TODO file was never updated so lets remove it. + +2010-02-10 Federico Di Gregorio + + * lib/extras.py: fixed register_tstz_w_secs() error as reported by + Karsten Hilbert. + +2009-11-25 Federico Di Gregorio + + * psycopg/microprotocols.c: "can't adapt" message now includes full + type information (adapted patch from Eric Chamberlain). + + * tests/types_basic.py: fixed test broken by float precision fix. + +2009-11-09 Federico Di Gregorio + + * psycopg/adapter_pfloat.c: applied patch from Remy Blankto fix float + loss of precision. + 2009-10-04 Federico Di Gregorio * Release 2.0.13. diff -Nru psycopg2-2.0.13/debian/changelog psycopg2-2.4.5/debian/changelog --- psycopg2-2.0.13/debian/changelog 2012-05-31 11:48:44.000000000 +0000 +++ psycopg2-2.4.5/debian/changelog 2012-05-31 11:48:45.000000000 +0000 @@ -1,20 +1,119 @@ -psycopg2 (2.0.13-1~0.CAT.9.10ubuntu2) karmic; urgency=low +psycopg2 (2.4.5-1~lucid1~ppa1) lucid; urgency=low - * rebuilt for karmic + * No-change backport to lucid - -- Andreas Hasenack Wed, 13 Jan 2010 10:29:42 -0200 + -- Sidnei da Silva Thu, 31 May 2012 08:40:40 -0300 -psycopg2 (2.0.13-1~0.CAT.9.04ubuntu1) jaunty; urgency=low +psycopg2 (2.4.5-1) unstable; urgency=low - * rebuilt for jaunty + * Team upload + * New upstream release + - Update debian/copyright + + -- Scott Kitterman Mon, 02 Apr 2012 10:23:45 -0400 + +psycopg2 (2.4.4-4) unstable; urgency=low + + * Team upload + * Rename python-pyscopg2-docs to the more usual python-pyscopg2-doc + - Not creating a transitional package due to the package only existing + for a short period and never in a stable release + * Fix typos in the last debian/changelog entry + * Fix misspellings in package descriptions in debian/control + * Bump standards version to 3.9.3 without further change + + -- Scott Kitterman Sun, 18 Mar 2012 10:31:22 -0400 + +psycopg2 (2.4.4-3) unstable; urgency=low + + * Team upload + * Fix debian/rules so python3-psycopg2-dbg so files are properly marked as + debug versions (Closes: #658219) + * Use local Python objects.inv instead of downloading from python.org + - Add python-doc and python333-doc to Build-Depends-Indep + - Add Quilt use to debian/rules and README.source + - Add debian/patches/local_inventory + + -- Scott Kitterman Thu, 02 Feb 2012 09:04:44 -0500 + +psycopg2 (2.4.4-2) unstable; urgency=low + + * Team upload + * Move python-egenix-mxdatetime from Depends to Recommends since support for + it is now detected at runtime (Closes: #523414) + * Add separate python-pyscopg2-docs package suggested by both python-psycopg2 + and python3-psycopg2 (only ship actual documentation in the binary and + not the source to build the documentation) + - Adjust debian/rules for an arch-indep package and python-sphinx + - Rebuild html and text docs from source instead of shipping provided + files + - Add python-sphinx (>= 1.0.7+dfsg-1~) to build-depends and use + sphinxdoc:Depends + - Add debian/python-psycopg2-docs.install + * Build packages for Python3 (Closes: #645906) + - Add python3-psycopg2/-dbg to debian/control + - Add python3-all-dev and python3-all-dbg to build-depends + - Adjust debian/rules + * Drop build-depends on autoconf (Closes: #589131) + * Drop redundant build-depends on python (python-all-dev covers it) + + -- Scott Kitterman Mon, 30 Jan 2012 09:12:12 -0500 + +psycopg2 (2.4.4-1) unstable; urgency=low + + * Team upload + * New upstream release (Closes: #607714) + - Update debian/copyright + * Add build-arch and build-indep targets to debian/rules + * Adjust debian/rules so docmentation is not compressed (Closes: #589142), + objects.inv is not compressed (Closes: #608745), and the upstream NEWS + file is included in the package (Closes: #651835) + * Exclude redundant COPYING files in the doc directory from being installed + * Remove redundant Section: field from the python-psycopg2 entry in debian/ + control + * Use dh_prep instead of deprecated dh_clean -k + * Remove obsolete debian/pycompat file - -- Andreas Hasenack Tue, 05 Jan 2010 14:01:18 -0200 + -- Scott Kitterman Wed, 18 Jan 2012 12:59:48 -0500 -psycopg2 (2.0.13-1~0.CAT.8.04) hardy-cat; urgency=low +psycopg2 (2.4.2-1) unstable; urgency=low - * backport to hardy + [ Jakub Wilk ] + * Add Debian Python Modules Team to Uploaders. - -- LaMont Jones Thu, 19 Nov 2009 09:59:21 -0600 + [ Fabio Tranchitella ] + * New upstream release. + * debian/watch: updated, use pypi. + * debian/control, debian/rules: switched to dh_python2. + * debian/control: bumped Standard-Version to 3.9.2, no changes required. + + -- Fabio Tranchitella Sun, 19 Jun 2011 18:25:53 +0000 + +psycopg2 (2.2.1-1) unstable; urgency=low + + * New upstream release. (Closes: #582823) + * debian/control: + - bumped Standards-Version to 3.9.0, no changes required. + - removed the zope-psycopg2da binary package. (Closes: #583293) + + -- Fabio Tranchitella Fri, 02 Jul 2010 15:04:19 +0200 + +psycopg2 (2.0.14-1) unstable; urgency=low + + * New upstream release. + * debian/control: bumped Standards-Version to 3.8.5, no changes required. + + -- Fabio Tranchitella Sat, 10 Apr 2010 10:32:45 +0200 + +psycopg2 (2.0.13-2) unstable; urgency=low + + [ Piotr Ożarowski ] + * Add XB-Python-Version to python-psycopg2-dbg and zope-psycopgda2 + + [ Fabio Tranchitella ] + * Do not build anymore python-psycopg2da. (Closes: #558204) + + -- Fabio Tranchitella Wed, 06 Jan 2010 15:36:39 +0100 psycopg2 (2.0.13-1) unstable; urgency=low diff -Nru psycopg2-2.0.13/debian/control psycopg2-2.4.5/debian/control --- psycopg2-2.0.13/debian/control 2012-05-31 11:48:44.000000000 +0000 +++ psycopg2-2.4.5/debian/control 2012-05-31 11:48:45.000000000 +0000 @@ -1,21 +1,23 @@ Source: psycopg2 Section: python Priority: optional -Build-Depends: debhelper (>= 5.0.37.2), python-all-dev, python-all-dbg, python-central (>= 0.5.0), python (>= 2.3.5-7), python-egenix-mx-base-dev, autoconf, libpq-dev -Build-Depends-Indep: zope-debhelper (>= 0.3.4) +Build-Depends: debhelper (>= 5.0.37.2), python-all-dev (>= 2.6.6-3~), + python-all-dbg (>= 2.6.6-3~), python3-all-dev, python3-all-dbg, + python-egenix-mx-base-dev, libpq-dev, python-sphinx (>= 1.0.7+dfsg-1~), quilt (>= 0.46-7) +Build-Depends-Indep: python-doc, python3-doc Maintainer: Fabio Tranchitella -Standards-Version: 3.8.3 -XS-Python-Version: all +Uploaders: Debian Python Modules Team +Standards-Version: 3.9.3 Vcs-Svn: svn://svn.debian.org/python-modules/packages/psycopg2/trunk/ Vcs-Browser: http://svn.debian.org/viewsvn/python-modules/packages/psycopg2/trunk/ Homepage: http://initd.org/projects/psycopg Package: python-psycopg2 Architecture: any -Section: python -Depends: ${python:Depends}, ${shlibs:Depends}, python-egenix-mxdatetime +Depends: ${python:Depends}, ${shlibs:Depends}, ${misc:Depends} +Recommends: python-egenix-mxdatetime +Suggests: python-psycopg2-doc Provides: ${python:Provides} -XB-Python-Version: ${python:Versions} Description: Python module for PostgreSQL psycopg is a PostgreSQL database adapter for the Python programming language (just like pygresql and popy.) This is version 2, a complete rewrite of the @@ -26,7 +28,7 @@ psycopg is different from the other database adapter because it was designed for heavily multi-threaded applications that create and destroy lots of cursors and make a conspicuous number of concurrent INSERTs or UPDATEs. - psycopg 2 also provides full asycronous operations for the really brave + psycopg 2 also provides full asynchronous operations for the really brave programmer. . The main advantages of psycopg2 are that it supports the full Python DBAPI-2.0 @@ -37,7 +39,7 @@ Priority: extra Architecture: any Section: debug -Depends: python-psycopg2 (= ${binary:Version}), python-dbg, ${shlibs:Depends} +Depends: python-psycopg2 (= ${binary:Version}), python-dbg, ${shlibs:Depends}, ${misc:Depends} Description: Python module for PostgreSQL (debug extension) psycopg is a PostgreSQL database adapter for the Python programming language (just like pygresql and popy.) This is version 2, a complete rewrite of the @@ -47,19 +49,64 @@ . This package contains the extensions built for the Python debug interpreter. -Package: zope-psycopgda2 -Architecture: all -Section: zope -Depends: ${zope:Depends}, python-psycopg2 (>= ${source:Version}) -Description: Zope database adapter based on python-psycopg2 - The package contains the PostgreSQL database adapter for Zope 2.7, 2.8 and - 2.9 based on the psycopg2 Python module. +Package: python3-psycopg2 +Architecture: any +Depends: ${python3:Depends}, ${shlibs:Depends}, ${misc:Depends} +Suggests: python-psycopg2-doc +Provides: ${python3:Provides} +Description: Python 3 module for PostgreSQL + psycopg is a PostgreSQL database adapter for the Python3 programming language + (just like pygresql and popy.) This is version 2, a complete rewrite of the + original code to provide new-style classes for connection and cursor objects + and other sweet candies. Like the original, psycopg 2 was written with the + aim of being very small and fast, and stable as a rock. + . + psycopg is different from the other database adapter because it was designed + for heavily multi-threaded applications that create and destroy lots of + cursors and make a conspicuous number of concurrent INSERTs or UPDATEs. + psycopg 2 also provides full asynchronous operations for the really brave + programmer. + . + The main advantages of psycopg2 are that it supports the full Python DBAPI-2.0 + and being thread safe at level 2. It also includes some extensions to the + standard DBAPI-2.0 to allow for better thread performance. + +Package: python3-psycopg2-dbg +Priority: extra +Architecture: any +Section: debug +Depends: python3-psycopg2 (= ${binary:Version}), python3-dbg, ${shlibs:Depends}, ${misc:Depends} +Description: Python 3 module for PostgreSQL (debug extension) + psycopg is a PostgreSQL database adapter for the Python3 programming language + (just like pygresql and popy.) This is version 2, a complete rewrite of the + original code to provide new-style classes for connection and cursor objects + and other sweet candies. Like the original, psycopg 2 was written with the + aim of being very small and fast, and stable as a rock. + . + This package contains the extensions built for the Python debug interpreter. -Package: python-psycopg2da +Package: python-psycopg2-doc Architecture: all -Section: python -Depends: ${zope:Depends}, python-psycopg2 (>= ${source:Version}) -XB-Python-Version: ${zope:PythonVersion} -Description: Zope database adapter based on python-psycopg2 -- zope3 version - The package contains the PostgreSQL database adapter for Zope 3 based on - the psycopg2 Python module. +Section: doc +Depends: ${sphinxdoc:Depends}, ${misc:Depends} +Suggests: python-psycopg2 | python3-psycopg2 +Replaces: python-psycopg2-docs +Breaks: python-psycopg2-docs +Description: Python module for PostgreSQL (documentation package) + psycopg is a PostgreSQL database adapter for the Python programming language + (just like pygresql and popy.) This is version 2, a complete rewrite of the + original code to provide new-style classes for connection and cursor objects + and other sweet candies. Like the original, psycopg 2 was written with the + aim of being very small and fast, and stable as a rock. + . + psycopg is different from the other database adapter because it was designed + for heavily multi-threaded applications that create and destroy lots of + cursors and make a conspicuous number of concurrent INSERTs or UPDATEs. + psycopg 2 also provides full asynchronous operations for the really brave + programmer. + . + The main advantages of psycopg2 are that it supports the full Python DBAPI-2.0 + and being thread safe at level 2. It also includes some extensions to the + standard DBAPI-2.0 to allow for better thread performance. + . + This package contains the psycopg2 documentation diff -Nru psycopg2-2.0.13/debian/copyright psycopg2-2.4.5/debian/copyright --- psycopg2-2.0.13/debian/copyright 2009-04-26 12:09:21.000000000 +0000 +++ psycopg2-2.4.5/debian/copyright 2012-05-31 11:48:45.000000000 +0000 @@ -11,8 +11,8 @@ Copyright: - Copyright (C) 2001-2006 Federico Di Gregorio - Copyright (C) 2001 Michele Comitini + Copyright (C) 2001-2011 Federico Di Gregorio + Copyright (C) 2010-2011 Daniele Varrazzo For psycopg2da: Copyright (C) 2006 Fabio Tranchitella @@ -21,83 +21,148 @@ Copyright (C) 2001-2005 Federico Di Gregorio Copyright (C) 2002 Tom Jenkins - For the file tests/dbapi20.py: - Copyright (C) 2003 Ian Bicking + For the file psycopg/xid_type.c: + Copyright (C) 2008 Canonical Ltd. - For the file scripts/ext2html.py: - Copyright (C) 2003 Daniele Varrazzo + For the files psycopg/xid.h, tests/test_connection.py, and + tests/test_dates.py: + Copyright (C) 2008-2011 James Henstridge + For the files tests/test_cancel.py and tests/test_async.py + Copyright (C) 2010-2011 Jan Urbański + + For the file lib/errorcodes.py: + Copyright (C) 2006-2010 Johan Dahlin License for psycopg2, ZPsycopgDA and psycopg2da: - psycopg is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - On Debian GNU/Linux systems, the complete text of the GNU General - Public License can be found in '/usr/share/common-licenses/GPL'. - - As a special exception, specific permission is granted for the GPLed - code in this distribition to be linked to OpenSSL and PostgreSQL libpq - without invoking GPL clause 2(b). - - Note that the GPL was chosen to avoid proprietary adapters based on - psycopg code. Using psycopg in a proprietary product (even bundling - psycopg with the proprietary product) is fine as long as: +psycopg2 is free software: you can redistribute it and/or modify it +under the terms of the GNU Lesser General Public License as published +by the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +psycopg2 is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +License for more details. + +In addition, as a special exception, the copyright holders give +permission to link this program with the OpenSSL library (or with +modified versions of OpenSSL that use the same license as OpenSSL), +and distribute linked combinations including the two. + +You must obey the GNU Lesser General Public License in all respects for +all of the code used other than OpenSSL. If you modify file(s) with this +exception, you may extend this exception to your version of the file(s), +but you are not obligated to do so. If you do not wish to do so, delete +this exception statement from your version. If you delete this exception +statement from all source files in the program, then also delete it here. + +You should have received a copy of the GNU Lesser General Public License +along with psycopg2 (see the doc/ directory.) +If not, see . + +On Debian systems see /usr/share/common-licenses/LGPL-3 for details + +Alternative licenses +==================== + +If you prefer you can use the Zope Database Adapter ZPsycopgDA (i.e., +every file inside the ZPsycopgDA directory) user the ZPL license as +published on the Zope web site, http://www.zope.org/Resources/ZPL. + +Also, the following BSD-like license applies (at your option) to the +files following the pattern psycopg/adapter*.{h,c} and +psycopg/microprotocol*.{h,c}: + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this + software in a product, an acknowledgment in the product documentation + would be appreciated but is not required. - 1. psycopg is called from Python only using only the provided API - (i.e., no linking with C code and no C modules based on it); and + 2. Altered source versions must be plainly marked as such, and must not + be misrepresented as being the original software. - 2. all the other points of the GPL are respected (you offer a copy - of psycopg's source code, and so on.) + 3. This notice may not be removed or altered from any source distribution. -License for the files tests/dbapi20.py and scripts/ext2html.py: +Alternative licenses for psycopg2da: - These modules have been placed in the public domain. + If you prefer you can use the Zope3 Database Adapter psycopg2da (i.e., + every file inside the psycopg2da directory) user the ZPL license as + published on the Zope web site, http://www.zope.org/Resources/ZPL. + See above for full license text. -Alternative licenses for ZPsycopgDA: - If you prefer you can use the Zope Database Adapter ZPsycopgDA (i.e., - every file inside the ZPsycopgDA directory) user the ZPL license as - published on the Zope web site, http://www.zope.org/Resources/ZPL. +License for the files tests/dbapi20.py and scripts/ext2html.py: + These modules have been placed in the public domain. -Alternative licenses for psycopg2da: - If you prefer you can use the Zope3 Database Adapter psycopg2da (i.e., - every file inside the psycopg2da directory) user the ZPL license as - published on the Zope web site, http://www.zope.org/Resources/ZPL. +doc/pep-0249.txt has been placed in the public domain -Alternative licenses for psycopg/adapter*.{j,c} and -psycopg/microprotocol*.{h.c}: +License and copyright for doc/html/_static/jquery.js: - Also, the following BSD-like license applies (at your option) to the - files following the pattern psycopg/adapter*.{h,c} and - psycopg/microprotocol*.{h,c}: - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this - software in a product, an acknowledgment in the product documentation - would be appreciated but is not required. - - 2. Altered source versions must be plainly marked as such, and must not - be misrepresented as being the original software. - - 3. This notice may not be removed or altered from any source distribution. - - psycopg is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. + * Copyright 2010,2011 John Resig + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * Includes Sizzle.js + * http://sizzlejs.com/ + * Copyright 2010,2011 The Dojo Foundation + * Released under the MIT, BSD, and GPL Licenses. + +Copyright (c) The Regents of the University of California. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. Neither the name of the University nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. + + +License and copyright for doc/html/_static/doctools.js, sidebar.js, +searchtools.js, basic.css, websupport.js, and default.css + * :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + +See above for full BSD license text + +License and copyright for doc/html/_static/underscore.js +// Underscore.js 1.3.1 +// (c) 2009-2012 Jeremy Ashkenas, DocumentCloud Inc. +// Underscore is freely distributable under the MIT license. +// Portions of Underscore are inspired or borrowed from Prototype, +// Oliver Steele's Functional, and John Resig's Micro-Templating. +// For all details and documentation: +// http://documentcloud.github.com/underscore +See above for full BSD license text Proprietary licenses: diff -Nru psycopg2-2.0.13/debian/patches/local_inventory psycopg2-2.4.5/debian/patches/local_inventory --- psycopg2-2.0.13/debian/patches/local_inventory 1970-01-01 00:00:00.000000000 +0000 +++ psycopg2-2.4.5/debian/patches/local_inventory 2012-05-31 11:48:45.000000000 +0000 @@ -0,0 +1,18 @@ +Use local copies of object.inv for building documentation. +Doesn't need forwarding. +Patch by Scott Kitterman Feb 2, 2012 +Index: psycopg2-2.4.4/doc/src/conf.py +=================================================================== +--- psycopg2-2.4.4.orig/doc/src/conf.py 2012-02-02 15:07:12.370516809 -0500 ++++ psycopg2-2.4.4/doc/src/conf.py 2012-02-02 15:08:07.238515187 -0500 +@@ -61,8 +61,8 @@ + release = version + + intersphinx_mapping = { +- 'py': ('http://docs.python.org/', None), +- 'py3': ('http://docs.python.org/3.2', None), ++ 'py': ('/usr/share/doc/python2.7-doc/html', None), ++ 'py3': ('/usr/share/doc/python3.2-doc/html', None), + } + + # The language for content autogenerated by Sphinx. Refer to documentation diff -Nru psycopg2-2.0.13/debian/patches/series psycopg2-2.4.5/debian/patches/series --- psycopg2-2.0.13/debian/patches/series 1970-01-01 00:00:00.000000000 +0000 +++ psycopg2-2.4.5/debian/patches/series 2012-05-31 11:48:45.000000000 +0000 @@ -0,0 +1 @@ +local_inventory diff -Nru psycopg2-2.0.13/debian/python-psycopg2-doc.install psycopg2-2.4.5/debian/python-psycopg2-doc.install --- psycopg2-2.0.13/debian/python-psycopg2-doc.install 1970-01-01 00:00:00.000000000 +0000 +++ psycopg2-2.4.5/debian/python-psycopg2-doc.install 2012-05-31 11:48:45.000000000 +0000 @@ -0,0 +1,4 @@ +doc/html usr/share/doc/python-psycopg2-docs/ +doc/psycopg2.txt usr/share/doc/python-psycopg2-docs/ +doc/HACKING usr/share/doc/python-psycopg2-docs/ +doc/pep-0249.txt usr/share/doc/python-psycopg2-docs/ diff -Nru psycopg2-2.0.13/debian/README.source psycopg2-2.4.5/debian/README.source --- psycopg2-2.0.13/debian/README.source 1970-01-01 00:00:00.000000000 +0000 +++ psycopg2-2.4.5/debian/README.source 2012-05-31 11:48:45.000000000 +0000 @@ -0,0 +1,58 @@ +This package uses quilt to manage all modifications to the upstream +source. Changes are stored in the source package as diffs in +debian/patches and applied during the build. + +To configure quilt to use debian/patches instead of patches, you want +either to export QUILT_PATCHES=debian/patches in your environment +or use this snippet in your ~/.quiltrc: + + for where in ./ ../ ../../ ../../../ ../../../../ ../../../../../; do + if [ -e ${where}debian/rules -a -d ${where}debian/patches ]; then + export QUILT_PATCHES=debian/patches + break + fi + done + +To get the fully patched source after unpacking the source package, cd to +the root level of the source package and run: + + quilt push -a + +The last patch listed in debian/patches/series will become the current +patch. + +To add a new set of changes, first run quilt push -a, and then run: + + quilt new + +where is a descriptive name for the patch, used as the filename in +debian/patches. Then, for every file that will be modified by this patch, +run: + + quilt add + +before editing those files. You must tell quilt with quilt add what files +will be part of the patch before making changes or quilt will not work +properly. After editing the files, run: + + quilt refresh + +to save the results as a patch. + +Alternately, if you already have an external patch and you just want to +add it to the build system, run quilt push -a and then: + + quilt import -P /path/to/patch + quilt push -a + +(add -p 0 to quilt import if needed). as above is the filename to +use in debian/patches. The last quilt push -a will apply the patch to +make sure it works properly. + +To remove an existing patch from the list of patches that will be applied, +run: + + quilt delete + +You may need to run quilt pop -a to unapply patches first before running +this command. diff -Nru psycopg2-2.0.13/debian/rules psycopg2-2.4.5/debian/rules --- psycopg2-2.0.13/debian/rules 2012-05-31 11:48:44.000000000 +0000 +++ psycopg2-2.4.5/debian/rules 2012-05-31 11:48:45.000000000 +0000 @@ -3,6 +3,7 @@ # GNU copyright 1997 to 1999 by Joey Hess. PYVERS=$(shell pyversions -r debian/control) +PY3VERS=$(shell py3versions -r debian/control) -include /usr/share/python/python.mk ifeq (,$(py_sitename)) py_sitename = site-packages @@ -11,10 +12,13 @@ py_libdir_sh = $(py_libdir) py_setup_install_args = endif +DEFAULT_PYTHON = $(shell pyversions -d) +DEFAULT_BUILDIR = $(shell $(DEFAULT_PYTHON) -c 'from distutils.command.build import build; from distutils.core import Distribution; b = build(Distribution()); b.finalize_options(); print(b.build_platlib)') configure: configure-stamp configure-stamp: dh_testdir + dh_quilt_patch rm -f configure touch configure-stamp @@ -24,9 +28,15 @@ for python in $(PYVERS); do \ $$python setup.py build ; \ done + for python3 in $(PY3VERS); do \ + $$python3 setup.py build ; \ + done for python in $(PYVERS); do \ $$python-dbg setup.py build ; \ done + for python3 in $(PY3VERS); do \ + $$python3-dbg setup.py build ; \ + done touch build-stamp clean: configure @@ -36,41 +46,65 @@ -for python in $(PYVERS); do \ $$python setup.py clean ; \ done + -for python3 in $(PY3VERS); do \ + $$python3 setup.py clean ; \ + done + rm -rf $(CURDIR)/doc/src/_build + dh_quilt_unpatch dh_clean -install-arch: build +build-arch: build +build-indep: + mv $(CURDIR)/doc/psycopg2.txt $(CURDIR)/doc/psycopg2.txt.old + mv $(CURDIR)/doc/html $(CURDIR)/doc/html.old + export PYTHONPATH=$(CURDIR)/$(DEFAULT_BUILDIR)/; \ + cd $(CURDIR)/doc; \ + make -f Makefile + +install-arch: build-arch dh_testdir dh_testroot - dh_clean -k + dh_prep dh_installdirs # psycopg2 for python in $(PYVERS); do \ $$python setup.py install \ --root=$(CURDIR)/debian/python-psycopg2 --no-compile $(py_setup_install_args); \ done + for python3 in $(PY3VERS); do \ + $$python3 setup.py install \ + --root=$(CURDIR)/debian/python3-psycopg2 --no-compile --install-layout=deb; \ + done for python in $(PYVERS); do \ $$python-dbg setup.py install \ --root=$(CURDIR)/debian/python-psycopg2-dbg --no-compile $(py_setup_install_args); \ done + for python3 in $(PY3VERS); do \ + $$python3-dbg setup.py install \ + --root=$(CURDIR)/debian/python3-psycopg2-dbg --no-compile --install-layout=deb; \ + done find debian/python-*-dbg ! -type d ! -name '*.so' | xargs rm -f find debian/python-*-dbg -depth -empty -exec rmdir {} \; -install-indep: build - # Zope package - dh_installzope -p zope-psycopgda2 ZPsycopgDA - # Zope3 package - dh_installzope -p python-psycopg2da psycopg2da +install-indep: build-indep + dh_testdir + dh_testroot + dh_prep + dh_install # Build architecture-independent files here. binary-indep: build install-indep dh_testdir dh_testroot - dh_installdocs -i AUTHORS - dh_installchangelogs -i + dh_installdocs -i README AUTHORS NEWS + dh_installchangelogs -i ChangeLog + dh_sphinxdoc -i + rm -rf $(CURDIR)/doc/html + mv $(CURDIR)/doc/psycopg2.txt.old $(CURDIR)/doc/psycopg2.txt + mv $(CURDIR)/doc/html.old $(CURDIR)/doc/html dh_link -i dh_compress -i dh_fixperms -i - dh_pycentral -p python-psycopg2da dh_installdeb -i dh_gencontrol -i dh_md5sums -i @@ -80,17 +114,20 @@ binary-arch: build install-arch dh_testdir dh_testroot - dh_installdocs -a README AUTHORS doc tests + dh_installdocs -a README AUTHORS NEWS dh_installchangelogs -a ChangeLog dh_link -a dh_strip -ppython-psycopg2 --dbg-package=python-psycopg2-dbg rm -rf debian/python-psycopg2-dbg/usr/share/doc/python-psycopg2-dbg ln -s python-psycopg2 debian/python-psycopg2-dbg/usr/share/doc/python-psycopg2-dbg - dh_compress -a + dh_strip -ppython3-psycopg2 --dbg-package=python3-psycopg2-dbg + rm -rf debian/python3-psycopg2-dbg/usr/share/doc/python3-psycopg2-dbg + ln -s python3-psycopg2 debian/python3-psycopg2-dbg/usr/share/doc/python3-psycopg2-dbg + dh_compress -a -X.js -X_sources -Xobjects.inv dh_fixperms -a dh_makeshlibs -a - dh_pycentral -a - dh_python -a + dh_python2 -a + dh_python3 -a dh_installdeb -a dh_shlibdeps -a dh_gencontrol -a diff -Nru psycopg2-2.0.13/debian/source/format psycopg2-2.4.5/debian/source/format --- psycopg2-2.0.13/debian/source/format 1970-01-01 00:00:00.000000000 +0000 +++ psycopg2-2.4.5/debian/source/format 2012-05-31 11:48:45.000000000 +0000 @@ -0,0 +1 @@ +1.0 diff -Nru psycopg2-2.0.13/debian/watch psycopg2-2.4.5/debian/watch --- psycopg2-2.0.13/debian/watch 2009-04-26 12:09:21.000000000 +0000 +++ psycopg2-2.4.5/debian/watch 2012-05-31 11:48:45.000000000 +0000 @@ -1,2 +1,2 @@ version=3 -http://www.initd.org/pub/software/psycopg/psycopg2-([0-9][0-9\.\-]*).tar.gz debian uupdate +http://pypi.python.org/packages/source/p/psycopg2/psycopg2-([0-9][0-9\.\-]*).tar.gz debian uupdate diff -Nru psycopg2-2.0.13/doc/api-screen.css psycopg2-2.4.5/doc/api-screen.css --- psycopg2-2.0.13/doc/api-screen.css 2009-04-26 12:09:21.000000000 +0000 +++ psycopg2-2.4.5/doc/api-screen.css 1970-01-01 00:00:00.000000000 +0000 @@ -1,138 +0,0 @@ -/* Based on the Epydoc "default.css" -** with some missing reST-related classes -** and Python syntax support (from SilverCity) -*/ - -/* Body color */ -body { background: #ffffff; color: #000000; } - -/* Tables */ -table.summary, table.details, table.index - { background: #e8f0f8; color: #000000; } -tr.summary, tr.details, tr.index - { background: #70b0f0; color: #000000; - text-align: left; font-size: 120%; } -tr.group { background: #c0e0f8; color: #000000; - text-align: left; font-size: 120%; - font-style: italic; } - -/* Documentation page titles */ -h2.module { margin-top: 0.2em; } -h2.class { margin-top: 0.2em; } - -/* Headings */ -h1.heading { font-size: +140%; font-style: italic; - font-weight: bold; } -h2.heading { font-size: +125%; font-style: italic; - font-weight: bold; } -h3.heading { font-size: +110%; font-style: italic; - font-weight: normal; } - -/* Base tree */ -pre.base-tree { font-size: 80%; margin: 0; } - -/* TOC */ -p.toc { margin: 0; } - -/* Details Sections */ -table.func-details { background: #e8f0f8; color: #000000; - border: 2px groove #c0d0d0; - padding: 0 1em 0 1em; margin: 0.4em 0 0 0; } -h3.func-detail { background: transparent; color: #000000; - margin: 0 0 1em 0; } - -table.var-details { background: #e8f0f8; color: #000000; - border: 2px groove #c0d0d0; - padding: 0 1em 0 1em; margin: 0.4em 0 0 0; } -h3.var-details { background: transparent; color: #000000; - margin: 0 0 1em 0; } - -/* Function signatures */ -.sig { background: transparent; color: #000000; - font-weight: bold; } -.sig-name { background: transparent; color: #006080; } -.sig-arg, .sig-kwarg, .sig-vararg - { background: transparent; color: #008060; } -.sig-default { background: transparent; color: #602000; } -.summary-sig { background: transparent; color: #000000; } -.summary-sig-name { background: transparent; color: #204080; } -.summary-sig-arg, .summary-sig-kwarg, .summary-sig-vararg - { background: transparent; color: #008060; } - -/* Doctest blocks */ -.py-src { background: transparent; color: #000000; } -.py-prompt { background: transparent; color: #005050; - font-weight: bold;} -.py-string { background: transparent; color: #006030; } -.py-comment { background: transparent; color: #003060; } -.py-keyword { background: transparent; color: #600000; } -.py-output { background: transparent; color: #404040; } -div.code-block, -pre.literal-block, -pre.doctestblock { background: #f4faff; color: #000000; - padding: .5em; margin: 1em; - border: 1px solid #708890; } -table pre.doctestblock - { background: #dce4ec; color: #000000; - padding: .5em; margin: 1em; - border: 1px solid #708890; } -div.code-block { font-family: monospace; } - -/* Variable values */ -pre.variable { background: #dce4ec; color: #000000; - padding: .5em; margin: 0; - border: 1px solid #708890; } -.variable-linewrap { background: transparent; color: #604000; } -.variable-ellipsis { background: transparent; color: #604000; } -.variable-quote { background: transparent; color: #604000; } -.re { background: transparent; color: #000000; } -.re-char { background: transparent; color: #006030; } -.re-op { background: transparent; color: #600000; } -.re-group { background: transparent; color: #003060; } -.re-ref { background: transparent; color: #404040; } - -/* Navigation bar */ -table.navbar { background: #a0c0ff; color: #0000ff; - border: 2px groove #c0d0d0; } -th.navbar { background: #a0c0ff; color: #0000ff; } -th.navselect { background: #70b0ff; color: #000000; } -.nomargin { margin: 0; } - -/* Links */ -a:link { background: transparent; color: #0000ff; } -a:visited { background: transparent; color: #204080; } -a.navbar:link { background: transparent; color: #0000ff; - text-decoration: none; } -a.navbar:visited { background: transparent; color: #204080; - text-decoration: none; } - -/* Admonitions */ -div.warning, -div.note { background-color: #c0e0f8; - border: thin solid black; - padding: 1em; - margin-left: 1em; - margin-right: 1em; } -div.warning .first, -div.note .first { font-family: sans-serif; - font-size: 110%; - margin-right: 0.5em; } - -/* Lists */ -ul { margin-top: 0; } - -/* Python syntax */ -.p_character { color: olive; } -.p_classname { color: blue; font-weight: bold; } -.p_commentblock {color: gray; font-style: italic; } -.p_commentline { color: green; font-style: italic; } -.p_default {} -.p_defname { color: #009999; font-weight: bold; } -.p_identifier { color: black; } -.p_number { color: #009999; } -.p_operator { color: black; } -.p_string { color: #7F007F; } -.p_stringeol { color: #7F007F; } -.p_triple { color: #7F0000; } -.p_tripledouble { color: #7F0000; } -.p_word { color: navy; font-weight: bold; } diff -Nru psycopg2-2.0.13/doc/async.txt psycopg2-2.4.5/doc/async.txt --- psycopg2-2.0.13/doc/async.txt 2009-04-26 12:09:21.000000000 +0000 +++ psycopg2-2.4.5/doc/async.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,67 +0,0 @@ -psycopg asynchronous API -************************ - -** Important: async quaeries are not enabled for 2.0 ** - -Program code can initiate an asynchronous query by passing an 'async=1' flag -to the .execute() method. A very simple example, from the connection to the -query: - - conn = psycopg.connect(database='test') - curs = conn.cursor() - curs.execute("SEECT * from test WHERE fielda > %s", (1971,), async=1) - -From then on any query on other cursors derived from the same connection is -doomed to fail (and raise an exception) until the original cursor (the one -executing the query) complete the asynchronous operation. This can happen in -a number of different ways: - - 1) one of the .fetchXXX() methods is called, effectively blocking untill - data has been sent from the backend to the client, terminating the - query. - - 2) .cancel() is called. This method tries to abort the current query and - will block until the query is aborted or fully executed. The return - value is True if the query was successfully aborted or False if it - was executed. Query result are discarded in both cases. - - 3) .execute() is called again on the same cursor (.execute() on a - different cursor will simply raise an exception.) This waits for the - complete execution of the current query, discard any data and execute - the new one. - -Note that calling .execute() two times in a row will not abort the former -query and will temporarily go to synchronous mode until the first of the two -queries is executed. - -Cursors now have some extra methods that make them usefull during -asynchronous queries: - - .fileno() - Returns the file descriptor associated with the current connection and - make possible to use a cursor in a context where a file object would be - expected (like in a select() call.) - - .isbusy() - Returns True if the backend is still processing the query or false if - data is ready to be fetched (by one of the .fetchXXX() methods.) - -A code snippet that shows how to use the cursor object in a select() call: - - import psycopg - import select - - conn = psycopg.connect(database='test') - curs = conn.cursor() - curs.execute("SEECT * from test WHERE fielda > %s", (1971,), async=1) - - # wait for input with a maximum timeout of 5 seconds - query_ended = False - while not query_ended: - rread, rwrite, rspec = select([cursor, another_file], [], [], 5) - if not cursor.isbusy(): - query_ended = True - # manage input from other sources like other_file, etc. - print "Query Results:" - for row in cursor: - print row diff -Nru psycopg2-2.0.13/doc/COPYING psycopg2-2.4.5/doc/COPYING --- psycopg2-2.0.13/doc/COPYING 1970-01-01 00:00:00.000000000 +0000 +++ psycopg2-2.4.5/doc/COPYING 2011-02-06 15:58:34.000000000 +0000 @@ -0,0 +1,676 @@ + + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. + diff -Nru psycopg2-2.0.13/doc/COPYING.LESSER psycopg2-2.4.5/doc/COPYING.LESSER --- psycopg2-2.0.13/doc/COPYING.LESSER 1970-01-01 00:00:00.000000000 +0000 +++ psycopg2-2.4.5/doc/COPYING.LESSER 2011-02-06 15:58:34.000000000 +0000 @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff -Nru psycopg2-2.0.13/doc/extensions.rst psycopg2-2.4.5/doc/extensions.rst --- psycopg2-2.0.13/doc/extensions.rst 2009-04-26 12:09:21.000000000 +0000 +++ psycopg2-2.4.5/doc/extensions.rst 1970-01-01 00:00:00.000000000 +0000 @@ -1,260 +0,0 @@ -======================================= - psycopg 2 extensions to the DBAPI 2.0 -======================================= - -This document is a short summary of the extensions built in psycopg 2.0.x over -the standard `Python Database API Specification 2.0`__, usually called simply -DBAPI-2.0 or even PEP-249. Before reading on this document please make sure -you already know how to program in Python using a DBAPI-2.0 compliant driver: -basic concepts like opening a connection, executing queries and commiting or -rolling back a transaction will not be explained but just used. - -.. __: http://www.python.org/peps/pep-0249.html - -Many objects and extension functions are defined in the `psycopg2.extensions` -module. - - -Connection and cursor factories -=============================== - -psycopg 2 exposes two new-style classes that can be sub-classed and expanded to -adapt them to the needs of the programmer: `cursor` and `connection`. The -`connection` class is usually sub-classed only to provide an easy way to create -customized cursors but other uses are possible. `cursor` is much more -interesting, because it is the class where query building, execution and result -type-casting into Python variables happens. - -An example of cursor subclass performing logging is:: - - import psycopg2 - import psycopg2.extensions - import logging - - class LoggingCursor(psycopg2.extensions.cursor): - def execute(self, sql, args=None): - logger = logging.getLogger('sql_debug') - logger.info(self.mogrify(sql, args)) - - try: - psycopg2.extensions.cursor.execute(self, sql, args) - except Exception, exc: - logger.error("%s: %s" % (exc.__class__.__name__, exc)) - raise - - conn = psycopg2.connect(DSN) - curs = conn.cursor(cursor_factory=LoggingCursor) - curs.execute("INSERT INTO mytable VALUES (%s, %s, %s);", - (10, 20, 30)) - - -Row factories -------------- - -tzinfo factories ----------------- - - -Setting transaction isolation levels -==================================== - -psycopg2 connection objects hold informations about the PostgreSQL `transaction -isolation level`_. The current transaction level can be read from the -`.isolation_level` attribute. The default isolation level is ``READ -COMMITTED``. A different isolation level con be set through the -`.set_isolation_level()` method. The level can be set to one of the following -constants, defined in `psycopg2.extensions`: - -`ISOLATION_LEVEL_AUTOCOMMIT` - No transaction is started when command are issued and no - `.commit()`/`.rollback()` is required. Some PostgreSQL command such as - ``CREATE DATABASE`` can't run into a transaction: to run such command use - `.set_isolation_level(ISOLATION_LEVEL_AUTOCOMMIT)`. - -`ISOLATION_LEVEL_READ_COMMITTED` - This is the default value. A new transaction is started at the first - `.execute()` command on a cursor and at each new `.execute()` after a - `.commit()` or a `.rollback()`. The transaction runs in the PostgreSQL - ``READ COMMITTED`` isolation level. - -`ISOLATION_LEVEL_SERIALIZABLE` - Transactions are run at a ``SERIALIZABLE`` isolation level. - - -.. _transaction isolation level: - http://www.postgresql.org/docs/8.1/static/transaction-iso.html - - -Adaptation of Python values to SQL types -======================================== - -psycopg2 casts Python variables to SQL literals by type. Standard Python types -are already adapted to the proper SQL literal. - -Example: the Python function:: - - curs.execute("""INSERT INTO atable (anint, adate, astring) - VALUES (%s, %s, %s)""", - (10, datetime.date(2005, 11, 18), "O'Reilly")) - -is converted into the SQL command:: - - INSERT INTO atable (anint, adate, astring) - VALUES (10, '2005-11-18', 'O''Reilly'); - -Named arguments are supported too with ``%(name)s`` placeholders. Notice that: - - - The Python string operator ``%`` is not used: the `.execute()` function - accepts the values tuple or dictionary as second parameter. - - - The variables placeholder must always be a ``%s``, even if a different - placeholder (such as a ``%d`` for an integer) may look more appropriate. - - - For positional variables binding, the second argument must always be a - tuple, even if it contains a single variable. - - - Only variable values should be bound via this method: it shouldn't be used - to set table or field names. For these elements, ordinary string formatting - should be used before running `.execute()`. - - -Adapting new types ------------------- - -Any Python class or type can be adapted to an SQL string. Adaptation mechanism -is similar to the Object Adaptation proposed in the `PEP-246`_ and is exposed -by the `adapt()` function. - -psycopg2 `.execute()` method adapts its ``vars`` arguments to the `ISQLQuote` -protocol. Objects that conform to this protocol expose a ``getquoted()`` method -returning the SQL representation of the object as a string. - -The easiest way to adapt an object to an SQL string is to register an adapter -function via the `register_adapter()` function. The adapter function must take -the value to be adapted as argument and return a conform object. A convenient -object is the `AsIs` wrapper, whose ``getquoted()`` result is simply the -``str()``\ ingification of the wrapped object. - -Example: mapping of a ``Point`` class into the ``point`` PostgreSQL geometric -type:: - - from psycopg2.extensions import adapt, register_adapter, AsIs - - class Point(object): - def __init__(self, x=0.0, y=0.0): - self.x = x - self.y = y - - def adapt_point(point): - return AsIs("'(%s,%s)'" % (adapt(point.x), adapt(point.y))) - - register_adapter(Point, adapt_point) - - curs.execute("INSERT INTO atable (apoint) VALUES (%s)", - (Point(1.23, 4.56),)) - -The above function call results in the SQL command:: - - INSERT INTO atable (apoint) VALUES ((1.23, 4.56)); - - -.. _PEP-246: http://www.python.org/peps/pep-0246.html - - -Type casting of SQL types into Python values -============================================ - -PostgreSQL objects read from the database can be adapted to Python objects -through an user-defined adapting function. An adapter function takes two -argments: the object string representation as returned by PostgreSQL and the -cursor currently being read, and should return a new Python object. For -example, the following function parses a PostgreSQL ``point`` into the -previously defined ``Point`` class:: - - def cast_point(value, curs): - if value is not None: - # Convert from (f1, f2) syntax using a regular expression. - m = re.match("\((.*),(.*)\)", value) - if m: - return Point(float(m.group(1)), float(m.group(2))) - -To create a mapping from the PostgreSQL type (either standard or user-defined), -its ``oid`` must be known. It can be retrieved either by the second column of -the cursor description:: - - curs.execute("SELECT NULL::point") - point_oid = curs.description[0][1] # usually returns 600 - -or by querying the system catalogs for the type name and namespace (the -namespace for system objects is ``pg_catalog``):: - - curs.execute(""" - SELECT pg_type.oid - FROM pg_type JOIN pg_namespace - ON typnamespace = pg_namespace.oid - WHERE typname = %(typename)s - AND nspname = %(namespace)s""", - {'typename': 'point', 'namespace': 'pg_catalog'}) - - point_oid = curs.fetchone()[0] - -After you know the object ``oid``, you must can and register the new type:: - - POINT = psycopg2.extensions.new_type((point_oid,), "POINT", cast_point) - psycopg2.extensions.register_type(POINT) - -The `new_type()` function binds the object oids (more than one can be -specified) to the adapter function. `register_type()` completes the spell. -Conversion is automatically performed when a column whose type is a registered -``oid`` is read:: - - curs.execute("SELECT '(10.2,20.3)'::point") - point = curs.fetchone()[0] - print type(point), point.x, point.y - # Prints: " 10.2 20.3" - - -Working with times and dates -============================ - - -Receiving NOTIFYs -================= - - -Using COPY TO and COPY FROM -=========================== - -psycopg2 `cursor` object provides an interface to the efficient `PostgreSQL -COPY command`__ to move data from files to tables and back. - -The `.copy_to(file, table)` method writes the content of the table -named ``table`` *to* the file-like object ``file``. ``file`` must have a -``write()`` method. - -The `.copy_from(file, table)` reads data *from* the file-like object -``file`` appending them to the table named ``table``. ``file`` must have both -``read()`` and ``readline()`` method. - -Both methods accept two optional arguments: ``sep`` (defaulting to a tab) is -the columns separator and ``null`` (defaulting to ``\N``) represents ``NULL`` -values in the file. - -.. __: http://www.postgresql.org/docs/8.1/static/sql-copy.html - - -PostgreSQL status message and executed query -============================================ - -`cursor` objects have two special fields related to the last executed query: - - - `.query` is the textual representation (str or unicode, depending on what - was passed to `.execute()` as first argument) of the query *after* argument - binding and mogrification has been applied. To put it another way, `.query` - is the *exact* query that was sent to the PostgreSQL backend. - - - `.statusmessage` is the status message that the backend sent upon query - execution. It usually contains the basic type of the query (SELECT, - INSERT, UPDATE, ...) and some additional information like the number of - rows updated and so on. Refer to the PostgreSQL manual for more - information. diff -Nru psycopg2-2.0.13/doc/html/advanced.html psycopg2-2.4.5/doc/html/advanced.html --- psycopg2-2.0.13/doc/html/advanced.html 1970-01-01 00:00:00.000000000 +0000 +++ psycopg2-2.4.5/doc/html/advanced.html 2012-03-28 21:17:18.000000000 +0000 @@ -0,0 +1,479 @@ + + + + + + + + + + More advanced topics — Psycopg 2.4.5 documentation + + + + + + + + + + + + + + + +
+
+
+
+ +
+

More advanced topics

+
+

Connection and cursor factories

+

Psycopg exposes two new-style classes that can be sub-classed and expanded to +adapt them to the needs of the programmer: psycopg2.extensions.cursor +and psycopg2.extensions.connection. The connection class is +usually sub-classed only to provide an easy way to create customized cursors +but other uses are possible. cursor is much more interesting, because +it is the class where query building, execution and result type-casting into +Python variables happens.

+

An example of cursor subclass performing logging is:

+
import psycopg2
+import psycopg2.extensions
+import logging
+
+class LoggingCursor(psycopg2.extensions.cursor):
+    def execute(self, sql, args=None):
+        logger = logging.getLogger('sql_debug')
+        logger.info(self.mogrify(sql, args))
+
+        try:
+            psycopg2.extensions.cursor.execute(self, sql, args)
+        except Exception, exc:
+            logger.error("%s: %s" % (exc.__class__.__name__, exc))
+            raise
+
+conn = psycopg2.connect(DSN)
+cur = conn.cursor(cursor_factory=LoggingCursor)
+cur.execute("INSERT INTO mytable VALUES (%s, %s, %s);",
+             (10, 20, 30))
+
+
+
+
+

Adapting new Python types to SQL syntax

+

Any Python class or type can be adapted to an SQL string. Adaptation mechanism +is similar to the Object Adaptation proposed in the PEP 246 and is exposed +by the psycopg2.extensions.adapt() function.

+

The execute() method adapts its arguments to the +ISQLQuote protocol. Objects that conform to this +protocol expose a getquoted() method returning the SQL representation +of the object as a string (the method must return bytes in Python 3). +Optionally the conform object may expose a +prepare() method.

+

There are two basic ways to have a Python object adapted to SQL:

+
    +
  • the object itself is conform, or knows how to make itself conform. Such +object must expose a __conform__() method that will be called with the +protocol object as argument. The object can check that the protocol is +ISQLQuote, in which case it can return self (if the object also +implements getquoted()) or a suitable wrapper object. This option is +viable if you are the author of the object and if the object is specifically +designed for the database (i.e. having Psycopg as a dependency and polluting +its interface with the required methods doesn’t bother you). For a simple +example you can take a look at the source code for the +psycopg2.extras.Inet object.
  • +
  • If implementing the ISQLQuote interface directly in the object is not an +option (maybe because the object to adapt comes from a third party library), +you can use an adaptation function, taking the object to be adapted as +argument and returning a conforming object. The adapter must be +registered via the register_adapter() function. A +simple example wrapper is psycopg2.extras.UUID_adapter used by the +register_uuid() function.
  • +
+

A convenient object to write adapters is the AsIs +wrapper, whose getquoted() result is simply the str()ing conversion of +the wrapped object.

+

Example: mapping of a Point class into the point PostgreSQL +geometric type:

+
>>> from psycopg2.extensions import adapt, register_adapter, AsIs
+
+>>> class Point(object):
+...    def __init__(self, x, y):
+...        self.x = x
+...        self.y = y
+
+>>> def adapt_point(point):
+...     return AsIs("'(%s, %s)'" % (adapt(point.x), adapt(point.y)))
+
+>>> register_adapter(Point, adapt_point)
+
+>>> cur.execute("INSERT INTO atable (apoint) VALUES (%s)",
+...             (Point(1.23, 4.56),))
+
+
+

The above function call results in the SQL command:

+
INSERT INTO atable (apoint) VALUES ('(1.23, 4.56)');
+
+
+
+

Type casting of SQL types into Python objects

+

PostgreSQL objects read from the database can be adapted to Python objects +through an user-defined adapting function. An adapter function takes two +arguments: the object string representation as returned by PostgreSQL and the +cursor currently being read, and should return a new Python object. For +example, the following function parses the PostgreSQL point +representation into the previously defined Point class:

+
>>> def cast_point(value, cur):
+...    if value is None:
+...        return None
+...
+...    # Convert from (f1, f2) syntax using a regular expression.
+...    m = re.match(r"\(([^)]+),([^)]+)\)", value)
+...    if m:
+...        return Point(float(m.group(1)), float(m.group(2)))
+...    else:
+...        raise InterfaceError("bad point representation: %r" % value)
+
+
+

In order to create a mapping from a PostgreSQL type (either standard or +user-defined), its OID must be known. It can be retrieved either by the second +column of the cursor.description:

+
>>> cur.execute("SELECT NULL::point")
+>>> point_oid = cur.description[0][1]
+>>> point_oid
+600
+
+
+

or by querying the system catalog for the type name and namespace (the +namespace for system objects is pg_catalog):

+
>>> cur.execute("""
+...    SELECT pg_type.oid
+...      FROM pg_type JOIN pg_namespace
+...             ON typnamespace = pg_namespace.oid
+...     WHERE typname = %(typename)s
+...       AND nspname = %(namespace)s""",
+...    {'typename': 'point', 'namespace': 'pg_catalog'})
+>>> point_oid = cur.fetchone()[0]
+>>> point_oid
+600
+
+
+

After you know the object OID, you can create and register the new type:

+
>>> POINT = psycopg2.extensions.new_type((point_oid,), "POINT", cast_point)
+>>> psycopg2.extensions.register_type(POINT)
+
+
+

The new_type() function binds the object OIDs +(more than one can be specified) to the adapter function. +register_type() completes the spell. Conversion +is automatically performed when a column whose type is a registered OID is +read:

+
>>> cur.execute("SELECT '(10.2,20.3)'::point")
+>>> point = cur.fetchone()[0]
+>>> print type(point), point.x, point.y
+<class 'Point'> 10.2 20.3
+
+
+

A typecaster created by new_type() can be also used with +new_array_type() to create a typecaster converting a +PostgreSQL array into a Python list.

+
+
+

Asynchronous notifications

+

Psycopg allows asynchronous interaction with other database sessions using the +facilities offered by PostgreSQL commands LISTEN and NOTIFY. Please +refer to the PostgreSQL documentation for examples about how to use this form of +communication.

+

Notifications are instances of the Notify object made +available upon reception in the connection.notifies list. Notifications can +be sent from Python code simply executing a NOTIFY command in an +execute() call.

+

Because of the way sessions interact with notifications (see NOTIFY +documentation), you should keep the connection in autocommit +mode if you wish to receive or send notifications in a timely manner.

+

Notifications are received after every query execution. If the user is +interested in receiving notifications but not in performing any query, the +poll() method can be used to check for new messages without +wasting resources.

+

A simple application could poll the connection from time to time to check if +something new has arrived. A better strategy is to use some I/O completion +function such as select() to sleep until awaken from the kernel when there is +some data to read on the connection, thereby using no CPU unless there is +something to read:

+
import select
+import psycopg2
+import psycopg2.extensions
+
+conn = psycopg2.connect(DSN)
+conn.set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT)
+
+curs = conn.cursor()
+curs.execute("LISTEN test;")
+
+print "Waiting for notifications on channel 'test'"
+while 1:
+    if select.select([conn],[],[],5) == ([],[],[]):
+        print "Timeout"
+    else:
+        conn.poll()
+        while conn.notifies:
+            notify = conn.notifies.pop()
+            print "Got NOTIFY:", notify.pid, notify.channel, notify.payload
+
+
+

Running the script and executing a command such as NOTIFY test, 'hello' +in a separate psql shell, the output may look similar to:

+
Waiting for notifications on channel 'test'
+Timeout
+Timeout
+Got NOTIFY: 6535 test hello
+Timeout
+...
+
+

Note that the payload is only available from PostgreSQL 9.0: notifications +received from a previous version server will have the +payload attribute set to the empty string.

+

+Changed in version 2.3: Added Notify object and handling notification +payload.

+
+
+

Asynchronous support

+

+New in version 2.2.0.

+

Psycopg can issue asynchronous queries to a PostgreSQL database. An asynchronous +communication style is established passing the parameter async=1 to the +connect() function: the returned connection will work in +asynchronous mode.

+

In asynchronous mode, a Psycopg connection will rely on the caller to poll the +socket file descriptor, checking if it is ready to accept data or if a query +result has been transferred and is ready to be read on the client. The caller +can use the method fileno() to get the connection file +descriptor and poll() to make communication proceed according to +the current connection state.

+

The following is an example loop using methods fileno() and poll() +together with the Python select() function in order to carry on +asynchronous operations with Psycopg:

+
def wait(conn):
+    while 1:
+        state = conn.poll()
+        if state == psycopg2.extensions.POLL_OK:
+            break
+        elif state == psycopg2.extensions.POLL_WRITE:
+            select.select([], [conn.fileno()], [])
+        elif state == psycopg2.extensions.POLL_READ:
+            select.select([conn.fileno()], [], [])
+        else:
+            raise psycopg2.OperationalError("poll() returned %s" % state)
+
+
+

The above loop of course would block an entire application: in a real +asynchronous framework, select() would be called on many file descriptors +waiting for any of them to be ready. Nonetheless the function can be used to +connect to a PostgreSQL server only using nonblocking commands and the +connection obtained can be used to perform further nonblocking queries. After +poll() has returned POLL_OK, and thus wait() has +returned, the connection can be safely used:

+
>>> aconn = psycopg2.connect(database='test', async=1)
+>>> wait(aconn)
+>>> acurs = aconn.cursor()
+
+
+

Note that there are a few other requirements to be met in order to have a +completely non-blocking connection attempt: see the libpq documentation for +PQconnectStart().

+

The same loop should be also used to perform nonblocking queries: after +sending a query via execute() or callproc(), call +poll() on the connection available from cursor.connection until it +returns POLL_OK, at which point the query has been completely sent to the +server and, if it produced data, the results have been transferred to the +client and available using the regular cursor methods:

+
>>> acurs.execute("SELECT pg_sleep(5); SELECT 42;")
+>>> wait(acurs.connection)
+>>> acurs.fetchone()[0]
+42
+
+
+

When an asynchronous query is being executed, connection.isexecuting() returns +True. Two cursors can’t execute concurrent queries on the same asynchronous +connection.

+

There are several limitations in using asynchronous connections: the +connection is always in autocommit mode and it is not +possible to change it. So a +transaction is not implicitly started at the first query and is not possible +to use methods commit() and rollback(): you can +manually control transactions using execute() to send database +commands such as BEGIN, COMMIT and ROLLBACK. Similarly +set_session() can’t be used but it is still possible to invoke the +SET command with the proper default_transaction_... parameter.

+

With asynchronous connections it is also not possible to use +set_client_encoding(), executemany(), large +objects, named cursors.

+

COPY commands are not supported either in asynchronous mode, but +this will be probably implemented in a future release.

+
+
+

Support to coroutine libraries

+

+New in version 2.2.0.

+

Psycopg can be used together with coroutine-based libraries, and participate +to cooperative multithreading.

+

Coroutine-based libraries (such as Eventlet or gevent) can usually patch the +Python standard library in order to enable a coroutine switch in the presence of +blocking I/O: the process is usually referred as making the system green, in +reference to the green threads.

+

Because Psycopg is a C extension module, it is not possible for coroutine +libraries to patch it: Psycopg instead enables cooperative multithreading by +allowing the registration of a wait callback using the +psycopg2.extensions.set_wait_callback() function. When a wait callback is +registered, Psycopg will use libpq non-blocking calls instead of the regular +blocking ones, and will delegate to the callback the responsibility to wait +for the socket to become readable or writable.

+

Working this way, the caller does not have the complete freedom to schedule the +socket check whenever they want as with an asynchronous connection, but has the advantage of maintaining a complete DB API 2.0 +semantics: from the point of view of the end user, all Psycopg functions and +objects will work transparently in the coroutine environment (blocking the +calling green thread and giving other green threads the possibility to be +scheduled), allowing non modified code and third party libraries (such as +SQLAlchemy) to be used in coroutine-based programs.

+
+

Warning

+

Psycopg connections are not green thread safe and can’t be used +concurrently by different green threads. Trying to execute more than one +command at time using one cursor per thread will result in an error (or a +deadlock on versions before 2.4.2).

+

Therefore, programmers are advised to either avoid sharing connections +between coroutines or to use a library-friendly lock to synchronize shared +connections, e.g. for pooling.

+
+

Coroutine libraries authors should provide a callback implementation (and +possibly a method to register it) to make Psycopg as green as they want. An +example callback (using select() to block) is provided as +psycopg2.extras.wait_select(): it boils down to something similar to:

+
def wait_select(conn):
+    while 1:
+        state = conn.poll()
+        if state == extensions.POLL_OK:
+            break
+        elif state == extensions.POLL_READ:
+            select.select([conn.fileno()], [], [])
+        elif state == extensions.POLL_WRITE:
+            select.select([], [conn.fileno()], [])
+        else:
+            raise OperationalError("bad state from poll: %s" % state)
+
+
+

Providing callback functions for the single coroutine libraries is out of +psycopg2 scope, as the callback can be tied to the libraries’ implementation +details. You can check the psycogreen project for further informations and +resources about the topic.

+
+

Warning

+

COPY commands are currently not supported when a wait callback +is registered, but they will be probably implemented in a future release.

+

Large objects are not supported either: they are +not compatible with asynchronous connections.

+
+
+
+ + +
+
+
+
+
+

Table Of Contents

+ + +

Previous topic

+

The cursor class

+

Next topic

+

psycopg2.extensions – Extensions to the DB API

+

This Page

+ + + +
+
+
+
+ + + + \ No newline at end of file diff -Nru psycopg2-2.0.13/doc/html/.buildinfo psycopg2-2.4.5/doc/html/.buildinfo --- psycopg2-2.0.13/doc/html/.buildinfo 1970-01-01 00:00:00.000000000 +0000 +++ psycopg2-2.4.5/doc/html/.buildinfo 2012-03-28 21:17:18.000000000 +0000 @@ -0,0 +1,4 @@ +# Sphinx build info version 1 +# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done. +config: 13ef4311c6e057df4834d7421f1db915 +tags: fbb0d17656682115ca4d033fb2f83ba1 diff -Nru psycopg2-2.0.13/doc/html/connection.html psycopg2-2.4.5/doc/html/connection.html --- psycopg2-2.0.13/doc/html/connection.html 1970-01-01 00:00:00.000000000 +0000 +++ psycopg2-2.4.5/doc/html/connection.html 2012-03-28 21:17:18.000000000 +0000 @@ -0,0 +1,719 @@ + + + + + + + + + + The connection class — Psycopg 2.4.5 documentation + + + + + + + + + + + + + + + +
+
+
+
+ +
+

The connection class

+
+
+class connection
+

Handles the connection to a PostgreSQL database instance. It encapsulates +a database session.

+

Connections are created using the factory function +connect().

+

Connections are thread safe and can be shared among many threads. See +Thread and process safety for details.

+
+
+cursor([name] [, cursor_factory] [, withhold])
+

Return a new cursor object using the connection.

+

If name is specified, the returned cursor will be a server +side cursor (also known as named cursor). +Otherwise it will be a regular client side cursor. By default a +WITHOUT HOLD cursor is created; to create a WITH HOLD +cursor, pass a True value as the withhold parameter. See +Server side cursors.

+

The name can be a string not valid as a PostgreSQL identifier: for +example it may start with a digit and contain non-alphanumeric +characters and quotes.

+

+Changed in version 2.4: previously only valid PostgreSQL identifiers were accepted as +cursor name.

+
+

Warning

+

It is unsafe to expose the name to an untrusted source, for +instance you shouldn’t allow name to be read from a HTML form. +Consider it as part of the query, not as a query parameter.

+
+

The cursor_factory argument can be used to create non-standard +cursors. The class returned should be a subclass of +psycopg2.extensions.cursor. See Connection and cursor factories for +details.

+
+

DB API extension

+

The name and cursor_factory parameters are Psycopg +extensions to the DB API 2.0.

+
+
+ +
+
+commit()
+

Commit any pending transaction to the database. Psycopg can be set to +perform automatic commits at each operation, see +set_isolation_level().

+
+ +
+
+rollback()
+

Roll back to the start of any pending transaction. Closing a +connection without committing the changes first will cause an implicit +rollback to be performed.

+
+ +
+
+close()
+

Close the connection now (rather than whenever del is executed). +The connection will be unusable from this point forward; an +InterfaceError will be raised if any operation is +attempted with the connection. The same applies to all cursor objects +trying to use the connection. Note that closing a connection without +committing the changes first will cause any pending change to be +discarded as if a ROLLBACK was performed (unless a different +isolation level has been selected: see +set_isolation_level()).

+

+Changed in version 2.2: previously an explicit ROLLBACK was issued by Psycopg on +close(). The command could have been sent to the backend at an +inappropriate time, so Psycopg currently relies on the backend to +implicitly discard uncommitted changes. Some middleware are known +to behave incorrectly though when the connection is closed during +a transaction (when status is +STATUS_IN_TRANSACTION), e.g. PgBouncer +reports an unclean server and discards the connection. To +avoid this problem you can ensure to terminate the transaction +with a commit()/rollback() before +closing.

+
+ +

Exceptions as connection class attributes

+

The connection also exposes as attributes the same exceptions +available in the psycopg2 module. See Exceptions.

+

Two-phase commit support methods

+

+New in version 2.3.

+
+

See also

+

Two-Phase Commit protocol support for an introductory explanation of these methods.

+
+

Note that PostgreSQL supports two-phase commit since release 8.1: these +methods raise NotSupportedError if used with an older version +server.

+
+
+xid(format_id, gtrid, bqual)
+

Returns a Xid instance to be passed to the +tpc_*() methods of this connection. The argument types and +constraints are explained in Two-Phase Commit protocol support.

+

The values passed to the method will be available on the returned +object as the members format_id, +gtrid, bqual. +The object also allows accessing to these members and unpacking as a +3-items tuple.

+
+ +
+
+tpc_begin(xid)
+

Begins a TPC transaction with the given transaction ID xid.

+

This method should be called outside of a transaction (i.e. nothing +may have executed since the last commit() or +rollback() and connection.status is +STATUS_READY).

+

Furthermore, it is an error to call commit() or rollback() +within the TPC transaction: in this case a ProgrammingError +is raised.

+

The xid may be either an object returned by the xid() +method or a plain string: the latter allows to create a transaction +using the provided string as PostgreSQL transaction id. See also +tpc_recover().

+
+ +
+
+tpc_prepare()
+

Performs the first phase of a transaction started with +tpc_begin(). A ProgrammingError is raised if +this method is used outside of a TPC transaction.

+

After calling tpc_prepare(), no statements can be executed until +tpc_commit() or tpc_rollback() will be +called. The reset() method can be used to restore the +status of the connection to STATUS_READY: the +transaction will remain prepared in the database and will be +possible to finish it with tpc_commit(xid) and +tpc_rollback(xid).

+
+

See also

+

the PREPARE TRANSACTION PostgreSQL command.

+
+
+ +
+
+tpc_commit([xid])
+

When called with no arguments, tpc_commit() commits a TPC +transaction previously prepared with tpc_prepare().

+

If tpc_commit() is called prior to tpc_prepare(), a single phase +commit is performed. A transaction manager may choose to do this if +only a single resource is participating in the global transaction.

+

When called with a transaction ID xid, the database commits +the given transaction. If an invalid transaction ID is +provided, a ProgrammingError will be raised. This form +should be called outside of a transaction, and is intended for use in +recovery.

+

On return, the TPC transaction is ended.

+
+

See also

+

the COMMIT PREPARED PostgreSQL command.

+
+
+ +
+
+tpc_rollback([xid])
+

When called with no arguments, tpc_rollback() rolls back a TPC +transaction. It may be called before or after +tpc_prepare().

+

When called with a transaction ID xid, it rolls back the given +transaction. If an invalid transaction ID is provided, a +ProgrammingError is raised. This form should be called +outside of a transaction, and is intended for use in recovery.

+

On return, the TPC transaction is ended.

+
+

See also

+

the ROLLBACK PREPARED PostgreSQL command.

+
+
+ +
+
+tpc_recover()
+

Returns a list of Xid representing pending +transactions, suitable for use with tpc_commit() or +tpc_rollback().

+

If a transaction was not initiated by Psycopg, the returned Xids will +have attributes format_id and +bqual set to None and the +gtrid set to the PostgreSQL transaction ID: such Xids are still +usable for recovery. Psycopg uses the same algorithm of the +PostgreSQL JDBC driver to encode a XA triple in a string, so +transactions initiated by a program using such driver should be +unpacked correctly.

+

Xids returned by tpc_recover() also have extra attributes +prepared, owner, +database populated with the values read +from the server.

+
+

See also

+

the pg_prepared_xacts system view.

+
+
+ +
+

DB API extension

+

The above methods are the only ones defined by the DB API 2.0 protocol. +The Psycopg connection objects exports the following additional +methods and attributes.

+
+
+
+closed
+

Read-only attribute reporting whether the database connection is open +(0) or closed (1).

+
+ +
+
+cancel()
+

Cancel the current database operation.

+

The method interrupts the processing of the current operation. If no +query is being executed, it does nothing. You can call this function +from a different thread than the one currently executing a database +operation, for instance if you want to cancel a long running query if a +button is pushed in the UI. Interrupting query execution will cause the +cancelled method to raise a +QueryCanceledError. Note that the termination +of the query is not guaranteed to succeed: see the documentation for +PQcancel().

+

+New in version 2.3.

+
+ +
+
+reset()
+

Reset the connection to the default.

+

The method rolls back an eventual pending transaction and executes the +PostgreSQL RESET and SET SESSION AUTHORIZATION to revert the +session to the default values. A two-phase commit transaction prepared +using tpc_prepare() will remain in the database +available for recover.

+

+New in version 2.0.12.

+
+ +
+
+dsn
+

Read-only string containing the connection string used by the +connection.

+
+ +
+
+set_session([isolation_level,] [readonly,] [deferrable,] [autocommit])
+

Set one or more parameters for the next transactions or statements in +the current session. See SET TRANSACTION for further details.

+ +++ + + + +
Parameters:
    +
  • isolation_level – set the isolation level for the next +transactions/statements. The value can be one of the +constants defined in the +extensions module or one of the literal values +READ UNCOMMITTED, READ COMMITTED, REPEATABLE READ, +SERIALIZABLE.
  • +
  • readonly – if True, set the connection to read only; +read/write if False.
  • +
  • deferrable – if True, set the connection to deferrable; +non deferrable if False. Only available from PostgreSQL 9.1.
  • +
  • autocommit – switch the connection to autocommit mode: not a +PostgreSQL session setting but an alias for setting the +autocommit attribute.
  • +
+
+

The parameters isolation_level, readonly and deferrable also +accept the string DEFAULT as a value: the effect is to reset the +parameter to the server default.

+

The function must be invoked with no transaction in progress. At every +function invocation, only the specified parameters are changed.

+

The default for the values are defined by the server configuration: +see values for default_transaction_isolation, +default_transaction_read_only, default_transaction_deferrable.

+
+

Note

+

There is currently no builtin method to read the current value for +the parameters: use SHOW default_transaction_... to read +the values from the backend.

+
+

+New in version 2.4.2.

+
+ +
+
+autocommit
+

Read/write attribute: if True, no transaction is handled by the +driver and every statement sent to the backend has immediate effect; +if False a new transaction is started at the first command +execution: the methods commit() or rollback() must be manually +invoked to terminate the transaction.

+

The autocommit mode is useful to execute commands requiring to be run +outside a transaction, such as CREATE DATABASE or +VACUUM.

+

The default is False (manual commit) as per DBAPI specification.

+
+

Warning

+

By default, any query execution, including a simple SELECT +will start a transaction: for long-running programs, if no further +action is taken, the session will remain “idle in transaction”, a +condition non desiderable for several reasons (locks are held by +the session, tables bloat...). For long lived scripts, either +ensure to terminate a transaction as soon as possible or use an +autocommit connection.

+
+

+New in version 2.4.2.

+
+ +
+
+isolation_level
+
+ +
+
+set_isolation_level(level)
+
+

Note

+

From version 2.4.2, set_session() and autocommit, offer +finer control on the transaction characteristics.

+
+

Read or set the transaction isolation level for the current session. +The level defines the different phenomena that can happen in the +database between concurrent transactions.

+

The value set or read is an integer: symbolic constants are defined in +the module psycopg2.extensions: see +Isolation level constants for the available values.

+

The default level is READ COMMITTED: at this level a +transaction is automatically started the first time a database command +is executed. If you want an autocommit mode, switch to +ISOLATION_LEVEL_AUTOCOMMIT before +executing any command:

+
>>> conn.set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT)
+
+
+

See also Transactions control.

+
+ +
+
+encoding
+
+ +
+
+set_client_encoding(enc)
+

Read or set the client encoding for the current session. The default +is the encoding defined by the database. It should be one of the +characters set supported by PostgreSQL

+
+ +
+
+notices
+

A list containing all the database messages sent to the client during +the session.

+
>>> cur.execute("CREATE TABLE foo (id serial PRIMARY KEY);")
+>>> pprint(conn.notices)
+['NOTICE:  CREATE TABLE / PRIMARY KEY will create implicit index "foo_pkey" for table "foo"\n',
+ 'NOTICE:  CREATE TABLE will create implicit sequence "foo_id_seq" for serial column "foo.id"\n']
+
+
+

To avoid a leak in case excessive notices are generated, only the last +50 messages are kept.

+

You can configure what messages to receive using PostgreSQL logging +configuration parameters such as log_statement, +client_min_messages, log_min_duration_statement etc.

+
+ +
+
+notifies
+

List of Notify objects containing asynchronous +notifications received by the session.

+

For other details see Asynchronous notifications.

+

+Changed in version 2.3: Notifications are instances of the Notify object. Previously the +list was composed by 2 items tuples (pid,channel) and +the payload was not accessible. To keep backward compatibility, +Notify objects can still be accessed as 2 items tuples.

+
+ +
+
+get_backend_pid()
+

Returns the process ID (PID) of the backend server process handling +this connection.

+

Note that the PID belongs to a process executing on the database +server host, not the local host!

+
+

See also

+

libpq docs for PQbackendPID() for details.

+
+

+New in version 2.0.8.

+
+ +
+
+get_parameter_status(parameter)
+

Look up a current parameter setting of the server.

+

Potential values for parameter are: server_version, +server_encoding, client_encoding, is_superuser, +session_authorization, DateStyle, TimeZone, +integer_datetimes, and standard_conforming_strings.

+

If server did not report requested parameter, return None.

+
+

See also

+

libpq docs for PQparameterStatus() for details.

+
+

+New in version 2.0.12.

+
+ +
+
+get_transaction_status()
+

Return the current session transaction status as an integer. Symbolic +constants for the values are defined in the module +psycopg2.extensions: see Transaction status constants +for the available values.

+
+

See also

+

libpq docs for PQtransactionStatus() for details.

+
+
+ +
+
+protocol_version
+

A read-only integer representing frontend/backend protocol being used. +Currently Psycopg supports only protocol 3, which allows connection +to PostgreSQL server from version 7.4. Psycopg versions previous than +2.3 support both protocols 2 and 3.

+
+

See also

+

libpq docs for PQprotocolVersion() for details.

+
+

+New in version 2.0.12.

+
+ +
+
+server_version
+

A read-only integer representing the backend version.

+

The number is formed by converting the major, minor, and revision +numbers into two-decimal-digit numbers and appending them together. +For example, version 8.1.5 will be returned as 80105.

+
+

See also

+

libpq docs for PQserverVersion() for details.

+
+

+New in version 2.0.12.

+
+ +
+
+status
+

A read-only integer representing the status of the connection. +Symbolic constants for the values are defined in the module +psycopg2.extensions: see Connection status constants +for the available values.

+
+ +
+
+lobject([oid[, mode[, new_oid[, new_file[, lobject_factory]]]]])
+

Return a new database large object as a lobject +instance.

+

See Access to PostgreSQL large objects for an overview.

+ +++ + + + +
Parameters:
    +
  • oid – The OID of the object to read or write. 0 to create +a new large object and and have its OID assigned automatically.
  • +
  • mode – Access mode to the object, see below.
  • +
  • new_oid – Create a new object using the specified OID. The +function raises OperationalError if the OID is already +in use. Default is 0, meaning assign a new one automatically.
  • +
  • new_file – The name of a file to be imported in the the database +(using the lo_import() function)
  • +
  • lobject_factory – Subclass of +lobject to be instantiated.
  • +
+
+

Available values for mode are:

+ ++++ + + + + + + + + + + + + + + + + + + + + + + + + + +
modemeaning
rOpen for read only
wOpen for write only
rwOpen for read/write
nDon’t open the file
bDon’t decode read data (return data as str in Python 2 or bytes in Python 3)
tDecode read data according to connection.encoding (return data as unicode in Python 2 or str in Python 3)
+

b and t can be specified together with a read/write mode. If +neither b nor t is specified, the default is b in Python 2 +and t in Python 3.

+

+New in version 2.0.8.

+

+Changed in version 2.4: added b and t mode and unicode +support.

+
+ +

Methods related to asynchronous support.

+

+New in version 2.2.0.

+ +
+
+async
+

Read only attribute: 1 if the connection is asynchronous, 0 otherwise.

+
+ +
+
+poll()
+

Used during an asynchronous connection attempt, or when a cursor is +executing a query on an asynchronous connection, make communication +proceed if it wouldn’t block.

+

Return one of the constants defined in Poll constants. If it +returns POLL_OK then the connection has been +estabilished or the query results are available on the client. +Otherwise wait until the file descriptor returned by fileno() is +ready to read or to write, as explained in Asynchronous support. +poll() should be also used by the function installed by +set_wait_callback() as explained in +Support to coroutine libraries.

+

poll() is also used to receive asynchronous notifications from the +database: see Asynchronous notifications from further details.

+
+ +
+
+fileno()
+

Return the file descriptor underlying the connection: useful to read +its status during asynchronous communication.

+
+ +
+
+isexecuting()
+

Return True if the connection is executing an asynchronous operation.

+
+ +
+ +
+ + +
+
+
+
+
+

Previous topic

+

The psycopg2 module content

+

Next topic

+

The cursor class

+

This Page

+ + + +
+
+
+
+ + + + \ No newline at end of file diff -Nru psycopg2-2.0.13/doc/html/cursor.html psycopg2-2.4.5/doc/html/cursor.html --- psycopg2-2.0.13/doc/html/cursor.html 1970-01-01 00:00:00.000000000 +0000 +++ psycopg2-2.4.5/doc/html/cursor.html 2012-03-28 21:17:18.000000000 +0000 @@ -0,0 +1,657 @@ + + + + + + + + + + The cursor class — Psycopg 2.4.5 documentation + + + + + + + + + + + + + + + +
+
+
+
+ +
+

The cursor class

+
+
+class cursor
+

Allows Python code to execute PostgreSQL command in a database session. +Cursors are created by the connection.cursor() method: they are +bound to the connection for the entire lifetime and all the commands are +executed in the context of the database session wrapped by the connection.

+

Cursors created from the same connection are not isolated, i.e., any +changes done to the database by a cursor are immediately visible by the +other cursors. Cursors created from different connections can or can not +be isolated, depending on the connections’ isolation level. See also rollback() and +commit() methods.

+

Cursors are not thread safe: a multithread application can create +many cursors from the same connection and should use each cursor from +a single thread. See Thread and process safety for details.

+
+
+description
+

This read-only attribute is a sequence of 7-item sequences.

+

Each of these sequences is a named tuple (a regular tuple if +collections.namedtuple() is not available) containing information +describing one result column:

+
    +
  1. name: the name of the column returned.
  2. +
  3. type_code: the PostgreSQL OID of the column. You can use the +pg_type system table to get more informations about the type. +This is the value used by Psycopg to decide what Python type use +to represent the value. See also +Type casting of SQL types into Python objects.
  4. +
  5. display_size: the actual length of the column in bytes. +Obtaining this value is computationally intensive, so it is +always None unless the PSYCOPG_DISPLAY_SIZE parameter +is set at compile time. See also PQgetlength.
  6. +
  7. internal_size: the size in bytes of the column associated to +this column on the server. Set to a negative value for +variable-size types See also PQfsize.
  8. +
  9. precision: total number of significant digits in columns of +type NUMERIC. None for other types.
  10. +
  11. scale: count of decimal digits in the fractional part in +columns of type NUMERIC. None for other types.
  12. +
  13. null_ok: always None as not easy to retrieve from the libpq.
  14. +
+

This attribute will be None for operations that do not return rows +or if the cursor has not had an operation invoked via the +execute*() methods yet.

+

+Changed in version 2.4: if possible, columns descriptions are named tuple instead of +regular tuples.

+
+ +
+
+close()
+

Close the cursor now (rather than whenever del is executed). +The cursor will be unusable from this point forward; an +InterfaceError will be raised if any operation is +attempted with the cursor.

+
+ +
+
+closed
+

Read-only boolean attribute: specifies if the cursor is closed +(True) or not (False).

+
+

DB API extension

+

The closed attribute is a Psycopg extension to the +DB API 2.0.

+
+

+New in version 2.0.7.

+
+ +
+
+connection
+

Read-only attribute returning a reference to the connection +object on which the cursor was created.

+
+ +
+
+name
+

Read-only attribute containing the name of the cursor if it was +creates as named cursor by connection.cursor(), or None if +it is a client side cursor. See Server side cursors.

+
+

DB API extension

+

The name attribute is a Psycopg extension to the DB API 2.0.

+
+
+ +
+
+withhold
+

Read/write attribute: specifies if a named cursor lifetime should +extend outside of the current transaction, i.e., it is possible to +fetch from the cursor even after a commection.commit() (but not after +a connection.rollback()). See Server side cursors

+

+New in version 2.4.3.

+
+

DB API extension

+

The withhold attribute is a Psycopg extension to the DB API 2.0.

+
+
+ +

Commands execution methods

+
+
+execute(operation[, parameters])
+

Prepare and execute a database operation (query or command).

+

Parameters may be provided as sequence or mapping and will be bound to +variables in the operation. Variables are specified either with +positional (%s) or named (%(name)s) placeholders. See +Passing parameters to SQL queries.

+

The method returns None. If a query was executed, the returned +values can be retrieved using fetch*() methods.

+
+ +
+
+executemany(operation, seq_of_parameters)
+

Prepare a database operation (query or command) and then execute it +against all parameter tuples or mappings found in the sequence +seq_of_parameters.

+

The function is mostly useful for commands that update the database: +any result set returned by the query is discarded.

+

Parameters are bounded to the query using the same rules described in +the execute() method.

+
+ +
+
+callproc(procname[, parameters])
+

Call a stored database procedure with the given name. The sequence of +parameters must contain one entry for each argument that the procedure +expects. The result of the call is returned as modified copy of the +input sequence. Input parameters are left untouched, output and +input/output parameters replaced with possibly new values.

+

The procedure may also provide a result set as output. This must then +be made available through the standard fetch*() methods.

+
+ +
+
+mogrify(operation[, parameters])
+

Return a query string after arguments binding. The string returned is +exactly the one that would be sent to the database running the +execute() method or similar.

+
>>> cur.mogrify("INSERT INTO test (num, data) VALUES (%s, %s)", (42, 'bar'))
+"INSERT INTO test (num, data) VALUES (42, E'bar')"
+
+
+
+

DB API extension

+

The mogrify() method is a Psycopg extension to the DB API 2.0.

+
+
+ +
+
+setinputsizes(sizes)
+

This method is exposed in compliance with the DB API 2.0. It currently +does nothing but it is safe to call it.

+
+ +

Results retrieval methods

+

The following methods are used to read data from the database after an +execute() call.

+
+

Note

+

cursor objects are iterable, so, instead of calling +explicitly fetchone() in a loop, the object itself can +be used:

+
>>> cur.execute("SELECT * FROM test;")
+>>> for record in cur:
+...     print record
+...
+(1, 100, "abc'def")
+(2, None, 'dada')
+(3, 42, 'bar')
+
+
+

+Changed in version 2.4: iterating over a named cursor +fetches itersize records at time from the backend. +Previously only one record was fetched per roundtrip, resulting +in a large overhead.

+
+
+
+fetchone()
+

Fetch the next row of a query result set, returning a single tuple, +or None when no more data is available:

+
>>> cur.execute("SELECT * FROM test WHERE id = %s", (3,))
+>>> cur.fetchone()
+(3, 42, 'bar')
+
+
+

A ProgrammingError is raised if the previous call +to execute*() did not produce any result set or no call was issued +yet.

+
+ +
+
+fetchmany([size=cursor.arraysize])
+

Fetch the next set of rows of a query result, returning a list of +tuples. An empty list is returned when no more rows are available.

+

The number of rows to fetch per call is specified by the parameter. +If it is not given, the cursor’s arraysize determines +the number of rows to be fetched. The method should try to fetch as +many rows as indicated by the size parameter. If this is not possible +due to the specified number of rows not being available, fewer rows +may be returned:

+
>>> cur.execute("SELECT * FROM test;")
+>>> cur.fetchmany(2)
+[(1, 100, "abc'def"), (2, None, 'dada')]
+>>> cur.fetchmany(2)
+[(3, 42, 'bar')]
+>>> cur.fetchmany(2)
+[]
+
+
+

A ProgrammingError is raised if the previous call to +execute*() did not produce any result set or no call was issued yet.

+

Note there are performance considerations involved with the size +parameter. For optimal performance, it is usually best to use the +arraysize attribute. If the size parameter is used, +then it is best for it to retain the same value from one +fetchmany() call to the next.

+
+ +
+
+fetchall()
+

Fetch all (remaining) rows of a query result, returning them as a list +of tuples. An empty list is returned if there is no more record to +fetch.

+
>>> cur.execute("SELECT * FROM test;")
+>>> cur.fetchall()
+[(1, 100, "abc'def"), (2, None, 'dada'), (3, 42, 'bar')]
+
+
+

A ProgrammingError is raised if the previous call to +execute*() did not produce any result set or no call was issued yet.

+
+ +
+
+scroll(value[, mode='relative'])
+

Scroll the cursor in the result set to a new position according +to mode.

+

If mode is relative (default), value is taken as offset to +the current position in the result set, if set to absolute, +value states an absolute target position.

+

If the scroll operation would leave the result set, a +ProgrammingError is raised and the cursor position is +not changed.

+

The method can be used both for client-side cursors and +server-side cursors.

+
+

Note

+

According to the DB API 2.0, the exception raised for a cursor out +of bound should have been IndexError. The best option is +probably to catch both exceptions in your code:

+
try:
+    cur.scroll(1000 * 1000)
+except (ProgrammingError, IndexError), exc:
+    deal_with_it(exc)
+
+
+
+
+ +
+
+arraysize
+

This read/write attribute specifies the number of rows to fetch at a +time with fetchmany(). It defaults to 1 meaning to fetch +a single row at a time.

+
+ +
+
+itersize
+

Read/write attribute specifying the number of rows to fetch from the +backend at each network roundtrip during iteration on a named cursor. The +default is 2000.

+

+New in version 2.4.

+
+

DB API extension

+

The itersize attribute is a Psycopg extension to the DB API 2.0.

+
+
+ +
+
+rowcount
+

This read-only attribute specifies the number of rows that the last +execute*() produced (for DQL statements +like SELECT) or affected (for +DML statements like UPDATE +or INSERT).

+

The attribute is -1 in case no execute*() has been performed on +the cursor or the row count of the last operation if it can’t be +determined by the interface.

+
+

Note

+

The DB API 2.0 interface reserves to redefine the latter case to +have the object return None instead of -1 in future versions +of the specification.

+
+
+ +
+
+rownumber
+

This read-only attribute provides the current 0-based index of the +cursor in the result set or None if the index cannot be +determined.

+

The index can be seen as index of the cursor in a sequence (the result +set). The next fetch operation will fetch the row indexed by +rownumber in that sequence.

+
+ +
+
+lastrowid
+

This read-only attribute provides the OID of the last row inserted +by the cursor. If the table wasn’t created with OID support or the +last operation is not a single record insert, the attribute is set to +None.

+
+

Note

+

PostgreSQL currently advices to not create OIDs on the tables and +the default for CREATE TABLE is to not support them. The +INSERT ... RETURNING syntax available from PostgreSQL 8.3 allows +more flexibility.

+
+
+ +
+
+query
+

Read-only attribute containing the body of the last query sent to the +backend (including bound arguments). None if no query has been +executed yet:

+
>>> cur.execute("INSERT INTO test (num, data) VALUES (%s, %s)", (42, 'bar'))
+>>> cur.query
+"INSERT INTO test (num, data) VALUES (42, E'bar')"
+
+
+
+

DB API extension

+

The query attribute is a Psycopg extension to the DB API 2.0.

+
+
+ +
+
+statusmessage
+

Read-only attribute containing the message returned by the last +command:

+
>>> cur.execute("INSERT INTO test (num, data) VALUES (%s, %s)", (42, 'bar'))
+>>> cur.statusmessage
+'INSERT 0 1'
+
+
+
+

DB API extension

+

The statusmessage attribute is a Psycopg extension to the +DB API 2.0.

+
+
+ +
+
+cast(oid, s)
+

Convert a value from the PostgreSQL string representation to a Python +object.

+

Use the most specific of the typecasters registered by +register_type().

+

+New in version 2.4.

+
+

DB API extension

+

The cast() method is a Psycopg extension to the DB API 2.0.

+
+
+ +
+
+tzinfo_factory
+

The time zone factory used to handle data types such as +TIMESTAMP WITH TIME ZONE. It should be a tzinfo +object. A few implementations are available in the psycopg2.tz +module.

+
+ +
+
+nextset()
+

This method is not supported (PostgreSQL does not have multiple data +sets) and will raise a NotSupportedError exception.

+
+ +
+
+setoutputsize(size[, column])
+

This method is exposed in compliance with the DB API 2.0. It currently +does nothing but it is safe to call it.

+
+ +

COPY-related methods

+
+

DB API extension

+

The COPY command is a PostgreSQL extension to the SQL standard. +As such, its support is a Psycopg extension to the DB API 2.0.

+
+
+
+copy_from(file, table, sep='\t', null='\\N', size=8192, columns=None)
+

Read data from the file-like object file appending them to +the table named table. See Using COPY TO and COPY FROM for an overview.

+ +++ + + + +
Parameters:
    +
  • file – file-like object to read data from. It must have both +read() and readline() methods.
  • +
  • table – name of the table to copy data into.
  • +
  • sep – columns separator expected in the file. Defaults to a tab.
  • +
  • null – textual representation of NULL in the file. +The default is the two characters string \N.
  • +
  • size – size of the buffer used to read from the file.
  • +
  • columns – iterable with name of the columns to import. +The length and types should match the content of the file to read. +If not specified, it is assumed that the entire table matches the +file structure.
  • +
+
+

Example:

+
>>> f = StringIO("42\tfoo\n74\tbar\n")
+>>> cur.copy_from(f, 'test', columns=('num', 'data'))
+>>> cur.execute("select * from test where id > 5;")
+>>> cur.fetchall()
+[(6, 42, 'foo'), (7, 74, 'bar')]
+
+
+

+Changed in version 2.0.6: added the columns parameter.

+

+Changed in version 2.4: data read from files implementing the io.TextIOBase interface +are encoded in the connection encoding when sent to +the backend.

+
+ +
+
+copy_to(file, table, sep='\t', null='\\N', columns=None)
+

Write the content of the table named table to the file-like +object file. See Using COPY TO and COPY FROM for an overview.

+ +++ + + + +
Parameters:
    +
  • file – file-like object to write data into. It must have a +write() method.
  • +
  • table – name of the table to copy data from.
  • +
  • sep – columns separator expected in the file. Defaults to a tab.
  • +
  • null – textual representation of NULL in the file. +The default is the two characters string \N.
  • +
  • columns – iterable with name of the columns to export. +If not specified, export all the columns.
  • +
+
+

Example:

+
>>> cur.copy_to(sys.stdout, 'test', sep="|")
+1|100|abc'def
+2|\N|dada
+...
+
+
+

+Changed in version 2.0.6: added the columns parameter.

+

+Changed in version 2.4: data sent to files implementing the io.TextIOBase interface +are decoded in the connection encoding when read +from the backend.

+
+ +
+
+copy_expert(sql, file, size=8192)
+

Submit a user-composed COPY statement. The method is useful to +handle all the parameters that PostgreSQL makes available (see +COPY command documentation).

+ +++ + + + +
Parameters:
    +
  • sql – the COPY statement to execute.
  • +
  • file – a file-like object; must be a readable file for +COPY FROM or an writable file for COPY TO.
  • +
  • size – size of the read buffer to be used in COPY FROM.
  • +
+
+

Example:

+
>>> cur.copy_expert("COPY test TO STDOUT WITH CSV HEADER", sys.stdout)
+id,num,data
+1,100,abc'def
+2,,dada
+...
+
+
+

+New in version 2.0.6.

+

+Changed in version 2.4: files implementing the io.TextIOBase interface are dealt with +using Unicode data instead of bytes.

+
+ +
+ +
+ + +
+
+
+
+
+

Previous topic

+

The connection class

+

Next topic

+

More advanced topics

+

This Page

+ + + +
+
+
+
+ + + + \ No newline at end of file diff -Nru psycopg2-2.0.13/doc/html/errorcodes.html psycopg2-2.4.5/doc/html/errorcodes.html --- psycopg2-2.0.13/doc/html/errorcodes.html 1970-01-01 00:00:00.000000000 +0000 +++ psycopg2-2.4.5/doc/html/errorcodes.html 2012-03-28 21:17:18.000000000 +0000 @@ -0,0 +1,174 @@ + + + + + + + + + + psycopg2.errorcodes – Error codes defined by PostgreSQL — Psycopg 2.4.5 documentation + + + + + + + + + + + + + + + +
+
+
+
+ +
+

psycopg2.errorcodes – Error codes defined by PostgreSQL

+

+New in version 2.0.6.

+

This module contains symbolic names for all PostgreSQL error codes and error +classes codes. Subclasses of Error make the PostgreSQL error +code available in the pgcode attribute.

+

From PostgreSQL documentation:

+
+

All messages emitted by the PostgreSQL server are assigned five-character +error codes that follow the SQL standard’s conventions for SQLSTATE +codes. Applications that need to know which error condition has occurred +should usually test the error code, rather than looking at the textual +error message. The error codes are less likely to change across +PostgreSQL releases, and also are not subject to change due to +localization of error messages. Note that some, but not all, of the error +codes produced by PostgreSQL are defined by the SQL standard; some +additional error codes for conditions not defined by the standard have +been invented or borrowed from other databases.

+

According to the standard, the first two characters of an error code +denote a class of errors, while the last three characters indicate a +specific condition within that class. Thus, an application that does not +recognize the specific error code can still be able to infer what to do +from the error class.

+
+ +

An example of the available constants defined in the module:

+
>>> errorcodes.CLASS_SYNTAX_ERROR_OR_ACCESS_RULE_VIOLATION
+'42'
+>>> errorcodes.UNDEFINED_TABLE
+'42P01'
+
+
+

Constants representing all the error values documented by PostgreSQL versions +between 8.1 and 9.1 are included in the module.

+
+
+psycopg2.errorcodes.lookup(code)
+

Lookup an error code or class code and return its symbolic name.

+

Raise KeyError if the code is not found.

+
>>> try:
+...     cur.execute("SELECT ouch FROM aargh;")
+... except Exception, e:
+...     pass
+...
+>>> errorcodes.lookup(e.pgcode[:2])
+'CLASS_SYNTAX_ERROR_OR_ACCESS_RULE_VIOLATION'
+>>> errorcodes.lookup(e.pgcode)
+'UNDEFINED_TABLE'
+
+
+

+New in version 2.0.14.

+
+ +
+ + +
+
+
+
+
+

Previous topic

+

psycopg2.extras – Miscellaneous goodies for Psycopg 2

+

Next topic

+

Frequently Asked Questions

+

This Page

+ + + +
+
+
+
+ + + + \ No newline at end of file diff -Nru psycopg2-2.0.13/doc/html/extensions.html psycopg2-2.4.5/doc/html/extensions.html --- psycopg2-2.0.13/doc/html/extensions.html 1970-01-01 00:00:00.000000000 +0000 +++ psycopg2-2.4.5/doc/html/extensions.html 2012-03-28 21:17:18.000000000 +0000 @@ -0,0 +1,1005 @@ + + + + + + + + + + psycopg2.extensions – Extensions to the DB API — Psycopg 2.4.5 documentation + + + + + + + + + + + + + + + +
+
+
+
+ +
+

psycopg2.extensions – Extensions to the DB API

+

The module contains a few objects and function extending the minimum set of +functionalities defined by the DB API 2.0.

+
+
+class psycopg2.extensions.connection
+

Is the class usually returned by the connect() function. +It is exposed by the extensions module in order to allow +subclassing to extend its behaviour: the subclass should be passed to the +connect() function using the connection_factory parameter. +See also Connection and cursor factories.

+

Subclasses should have constructor signature (dsn, async=0).

+

For a complete description of the class, see connection.

+
+ +
+
+class psycopg2.extensions.cursor
+

It is the class usually returnded by the connection.cursor() +method. It is exposed by the extensions module in order to allow +subclassing to extend its behaviour: the subclass should be passed to the +cursor() method using the cursor_factory parameter. See +also Connection and cursor factories.

+

For a complete description of the class, see cursor.

+
+ +
+
+class psycopg2.extensions.lobject(conn[, oid[, mode[, new_oid[, new_file]]]])
+

Wrapper for a PostgreSQL large object. See Access to PostgreSQL large objects for an +overview.

+

The class can be subclassed: see the connection.lobject() to know +how to specify a lobject subclass.

+

+New in version 2.0.8.

+
+
+oid
+

Database OID of the object.

+
+ +
+
+mode
+

The mode the database was open. See connection.lobject() for a +description of the available modes.

+
+ +
+
+read(bytes=-1)
+

Read a chunk of data from the current file position. If -1 (default) +read all the remaining data.

+

The result is an Unicode string (decoded according to +connection.encoding) if the file was open in t mode, a bytes +string for b mode.

+

+Changed in version 2.4: added Unicode support.

+
+ +
+
+write(str)
+

Write a string to the large object. Return the number of bytes +written. Unicode strings are encoded in the connection.encoding +before writing.

+

+Changed in version 2.4: added Unicode support.

+
+ +
+
+export(file_name)
+

Export the large object content to the file system.

+

The method uses the efficient lo_export() libpq function.

+
+ +
+
+seek(offset, whence=0)
+

Set the lobject current position.

+
+ +
+
+tell()
+

Return the lobject current position.

+
+ +
+
+truncate(len=0)
+

+New in version 2.2.0.

+

Truncate the lobject to the given size.

+

The method will only be available if Psycopg has been built against libpq +from PostgreSQL 8.3 or later and can only be used with PostgreSQL servers +running these versions. It uses the lo_truncate() libpq function.

+
+

Warning

+

If Psycopg is built with lo_truncate() support (i.e. if the +pg_config used during setup is version >= 8.3), but at +runtime an older libpq is found, Psycopg will fail to import. See +the lo_truncate FAQ about the problem.

+
+
+ +
+
+close()
+

Close the object.

+
+ +
+
+closed
+

Boolean attribute specifying if the object is closed.

+
+ +
+ +

Close the object and remove it from the database.

+
+ +
+ +
+
+class psycopg2.extensions.Notify(pid, channel, payload='')
+

A notification received from the backend.

+

Notify instances are made available upon reception on the +notifies member of the listening connection. The object +can be also accessed as a 2 items tuple returning the members +(pid,channel) for backward compatibility.

+

See Asynchronous notifications for details.

+

+New in version 2.3.

+
+
+pid
+

The ID of the backend process that sent the notification.

+

Note: if the sending session was handled by Psycopg, you can use +get_backend_pid() to know its PID.

+
+ +
+
+channel
+

The name of the channel to which the notification was sent.

+
+ +
+
+payload
+

The payload message of the notification.

+

Attaching a payload to a notification is only available since +PostgreSQL 9.0: for notifications received from previous versions +of the server this member is always the empty string.

+
+ +
+ +
+
+class psycopg2.extensions.Xid(format_id, gtrid, bqual)
+

A transaction identifier used for two-phase commit.

+

Usually returned by the connection methods xid() and +tpc_recover(). +Xid instances can be unpacked as a 3-item tuples containing the items +(format_id,gtrid,bqual). +The str() of the object returns the transaction ID used +in the commands sent to the server.

+

See Two-Phase Commit protocol support for an introduction.

+

+New in version 2.3.

+
+
+from_string(s)
+

Create a Xid object from a string representation. Static method.

+

If s is a PostgreSQL transaction ID produced by a XA transaction, +the returned object will have format_id, gtrid, bqual set to +the values of the preparing XA id. +Otherwise only the gtrid is populated with the unparsed string. +The operation is the inverse of the one performed by str(xid).

+
+ +
+
+format_id
+

Format ID in a XA transaction.

+

A non-negative 32 bit integer. +None if the transaction doesn’t follow the XA standard.

+
+ +
+
+gtrid
+

Global transaction ID in a XA transaction.

+

If the transaction doesn’t follow the XA standard, it is the plain +transaction ID used in the server commands.

+
+ +
+
+bqual
+

Branch qualifier of the transaction.

+

In a XA transaction every resource participating to a transaction +receives a distinct branch qualifier. +None if the transaction doesn’t follow the XA standard.

+
+ +
+
+prepared
+

Timestamp (with timezone) in which a recovered transaction was prepared.

+
+ +
+
+owner
+

Name of the user who prepared a recovered transaction.

+
+ +
+
+database
+

Database the recovered transaction belongs to.

+
+ +
+ +
+
+psycopg2.extensions.set_wait_callback(f)
+

Register a callback function to block waiting for data.

+

The callback should have signature fun(conn) and +is called to wait for data available whenever a blocking function from the +libpq is called. Use set_wait_callback(None) to revert to the +original behaviour (i.e. using blocking libpq functions).

+

The function is an hook to allow coroutine-based libraries (such as +Eventlet or gevent) to switch when Psycopg is blocked, allowing +other coroutines to run concurrently.

+

See wait_select() for an example of a wait callback +implementation.

+

+New in version 2.2.0.

+
+ +
+
+psycopg2.extensions.get_wait_callback()
+

Return the currently registered wait callback.

+

Return None if no callback is currently registered.

+

+New in version 2.2.0.

+
+ +
+

SQL adaptation protocol objects

+

Psycopg provides a flexible system to adapt Python objects to the SQL syntax +(inspired to the PEP 246), allowing serialization in PostgreSQL. See +Adapting new Python types to SQL syntax for a detailed description. The following objects +deal with Python objects adaptation:

+
+
+psycopg2.extensions.adapt(obj)
+

Return the SQL representation of obj as a string. Raise a +ProgrammingError if how to adapt the object is unknown. +In order to allow new objects to be adapted, register a new adapter for it +using the register_adapter() function.

+

The function is the entry point of the adaptation mechanism: it can be +used to write adapters for complex objects by recursively calling +adapt() on its components.

+
+ +
+
+psycopg2.extensions.register_adapter(class, adapter)
+

Register a new adapter for the objects of class class.

+

adapter should be a function taking a single argument (the object +to adapt) and returning an object conforming the ISQLQuote +protocol (e.g. exposing a getquoted() method). The AsIs is +often useful for this task.

+

Once an object is registered, it can be safely used in SQL queries and by +the adapt() function.

+
+ +
+
+class psycopg2.extensions.ISQLQuote(wrapped_object)
+

Represents the SQL adaptation protocol. Objects conforming this protocol +should implement a getquoted() and optionally a prepare() method.

+

Adapters may subclass ISQLQuote, but is not necessary: it is +enough to expose a getquoted() method to be conforming.

+
+
+_wrapped
+

The wrapped object passes to the constructor

+
+ +
+
+getquoted()
+

Subclasses or other conforming objects should return a valid SQL +string representing the wrapped object. In Python 3 the SQL must be +returned in a bytes object. The ISQLQuote implementation does +nothing.

+
+ +
+
+prepare(conn)
+

Prepare the adapter for a connection. The method is optional: if +implemented, it will be invoked before getquoted() with the +connection to adapt for as argument.

+

A conform object can implement this method if the SQL +representation depends on any server parameter, such as the server +version or the standard_conforming_string setting. Container +objects may store the connection and use it to recursively prepare +contained objects: see the implementation for +psycopg2.extensions.SQL_IN for a simple example.

+
+ +
+ +
+
+class psycopg2.extensions.AsIs(object)
+

Adapter conform to the ISQLQuote protocol useful for objects +whose string representation is already valid as SQL representation.

+
+
+getquoted()
+

Return the str() conversion of the wrapped object.

+
>>> AsIs(42).getquoted()
+'42'
+
+
+
+ +
+ +
+
+class psycopg2.extensions.QuotedString(str)
+

Adapter conform to the ISQLQuote protocol for string-like +objects.

+
+
+getquoted()
+

Return the string enclosed in single quotes. Any single quote +appearing in the the string is escaped by doubling it according to SQL +string constants syntax. Backslashes are escaped too.

+
>>> QuotedString(r"O'Reilly").getquoted()
+"'O''Reilly'"
+
+
+
+ +
+ +
+
+class psycopg2.extensions.Binary(str)
+

Adapter conform to the ISQLQuote protocol for binary objects.

+
+
+getquoted()
+

Return the string enclosed in single quotes. It performs the same +escaping of the QuotedString adapter, plus it knows how to +escape non-printable chars.

+
>>> Binary("\x00\x08\x0F").getquoted()
+"'\\\\000\\\\010\\\\017'"
+
+
+
+ +

+Changed in version 2.0.14: previously the adapter was not exposed by the extensions +module. In older versions it can be imported from the implementation +module psycopg2._psycopg.

+
+ +
+
+class psycopg2.extensions.Boolean
+
+class psycopg2.extensions.Float
+
+class psycopg2.extensions.SQL_IN
+

Specialized adapters for builtin objects.

+
+ +
+
+class psycopg2.extensions.DateFromPy
+
+class psycopg2.extensions.TimeFromPy
+
+class psycopg2.extensions.TimestampFromPy
+
+class psycopg2.extensions.IntervalFromPy
+

Specialized adapters for Python datetime objects.

+
+ +
+
+class psycopg2.extensions.DateFromMx
+
+class psycopg2.extensions.TimeFromMx
+
+class psycopg2.extensions.TimestampFromMx
+
+class psycopg2.extensions.IntervalFromMx
+

Specialized adapters for mx.DateTime objects.

+
+ +
+
+psycopg2.extensions.adapters
+

Dictionary of the currently registered object adapters. Use +register_adapter() to add an adapter for a new type.

+
+ +
+
+

Database types casting functions

+

These functions are used to manipulate type casters to convert from PostgreSQL +types to Python objects. See Type casting of SQL types into Python objects for +details.

+
+
+psycopg2.extensions.new_type(oids, name, adapter)
+

Create a new type caster to convert from a PostgreSQL type to a Python +object. The object created must be registered using +register_type() to be used.

+ +++ + + + +
Parameters:
    +
  • oids – tuple of OIDs of the PostgreSQL type to convert.
  • +
  • name – the name of the new type adapter.
  • +
  • adapter – the adaptation function.
  • +
+
+

The object OID can be read from the cursor.description attribute +or by querying from the PostgreSQL catalog.

+

adapter should have signature fun(value, cur) where +value is the string representation returned by PostgreSQL and +cur is the cursor from which data are read. In case of +NULL, value will be None. The adapter should return the +converted object.

+

See Type casting of SQL types into Python objects for an usage example.

+
+ +
+
+psycopg2.extensions.new_array_type(oids, name, base_caster)
+

Create a new type caster to convert from a PostgreSQL array type to a list +of Python object. The object created must be registered using +register_type() to be used.

+ +++ + + + +
Parameters:
    +
  • oids – tuple of OIDs of the PostgreSQL type to convert. It should +probably be the oid of the array type (e.g. the typarray field in +the pg_type table.
  • +
  • name – the name of the new type adapter.
  • +
  • base_caster – a Psycopg typecaster, e.g. created using the +new_type() function. The caster should be able to parse a single +item of the desired type.
  • +
+
+

+New in version 2.4.3.

+
+

Note

+

The function can be used to create a generic array typecaster, +returning a list of strings: just use the STRING as base +typecaster. For instance, if you want to receive from the database an +array of macaddr, each address represented by string, you can +use:

+
psycopg2.extensions.register_type(
+    psycopg2.extensions.new_array_type(
+        (1040,), 'MACADDR[]', psycopg2.STRING))
+
+
+
+
+ +
+
+psycopg2.extensions.register_type(obj[, scope])
+

Register a type caster created using new_type().

+

If scope is specified, it should be a connection or a +cursor: the type caster will be effective only limited to the +specified object. Otherwise it will be globally registered.

+
+ +
+
+psycopg2.extensions.string_types
+

The global register of type casters.

+
+ +
+
+psycopg2.extensions.encodings
+

Mapping from PostgreSQL encoding names to Python codec names. +Used by Psycopg when adapting or casting unicode strings. See +Unicode handling.

+
+ +
+
+

Additional exceptions

+

The module exports a few exceptions in addition to the standard ones defined by the DB API 2.0.

+
+
+exception psycopg2.extensions.QueryCanceledError
+

(subclasses OperationalError)

+

Error related to SQL query cancellation. It can be trapped specifically to +detect a timeout.

+

+New in version 2.0.7.

+
+ +
+
+exception psycopg2.extensions.TransactionRollbackError
+

(subclasses OperationalError)

+

Error causing transaction rollback (deadlocks, serialisation failures, +etc). It can be trapped specifically to detect a deadlock.

+

+New in version 2.0.7.

+
+ +
+
+

Isolation level constants

+

Psycopg2 connection objects hold informations about the PostgreSQL +transaction isolation level. The current transaction level can be read +from the isolation_level attribute. The default isolation +level is READ COMMITTED. A different isolation level con be set +through the set_isolation_level() method. The level can be +set to one of the following constants:

+
+
+psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT
+

No transaction is started when command are issued and no +commit() or rollback() is required. +Some PostgreSQL command such as CREATE DATABASE or VACUUM +can’t run into a transaction: to run such command use:

+
>>> conn.set_isolation_level(ISOLATION_LEVEL_AUTOCOMMIT)
+
+
+

See also Transactions control.

+
+ +
+
+psycopg2.extensions.ISOLATION_LEVEL_READ_UNCOMMITTED
+

The READ UNCOMMITTED isolation level is defined in the SQL standard +but not available in the MVCC model of PostgreSQL: it is replaced by the +stricter READ COMMITTED.

+
+ +
+
+psycopg2.extensions.ISOLATION_LEVEL_READ_COMMITTED
+

This is usually the the default PostgreSQL value, but a different default +may be set in the database configuration.

+

A new transaction is started at the first execute() command on a +cursor and at each new execute() after a commit() or a +rollback(). The transaction runs in the PostgreSQL +READ COMMITTED isolation level: a SELECT query sees only +data committed before the query began; it never sees either uncommitted +data or changes committed during query execution by concurrent +transactions.

+
+

See also

+

Read Committed Isolation Level in PostgreSQL +documentation.

+
+
+ +
+
+psycopg2.extensions.ISOLATION_LEVEL_REPEATABLE_READ
+

As in ISOLATION_LEVEL_READ_COMMITTED, a new transaction is started at +the first execute() command. Transactions run at a +REPEATABLE READ isolation level: all the queries in a transaction +see a snapshot as of the start of the transaction, not as of the start of +the current query within the transaction. However applications using this +level must be prepared to retry transactions due to serialization +failures.

+

While this level provides a guarantee that each transaction sees a +completely stable view of the database, this view will not necessarily +always be consistent with some serial (one at a time) execution of +concurrent transactions of the same level.

+

+Changed in version 2.4.2: The value was an alias for ISOLATION_LEVEL_SERIALIZABLE before. The +two levels are distinct since PostgreSQL 9.1

+
+

See also

+

Repeatable Read Isolation Level in PostgreSQL +documentation.

+
+
+ +
+
+psycopg2.extensions.ISOLATION_LEVEL_SERIALIZABLE
+

As in ISOLATION_LEVEL_READ_COMMITTED, a new transaction is started at +the first execute() command. Transactions run at a +SERIALIZABLE isolation level. This is the strictest transactions +isolation level, equivalent to having the transactions executed serially +rather than concurrently. However applications using this level must be +prepared to retry reansactions due to serialization failures.

+

Starting from PostgreSQL 9.1, this mode monitors for conditions which +could make execution of a concurrent set of serializable transactions +behave in a manner inconsistent with all possible serial (one at a time) +executions of those transaction. In previous version the behaviour was the +same of the REPEATABLE READ isolation level.

+
+

See also

+

Serializable Isolation Level in PostgreSQL documentation.

+
+
+ +
+
+

Transaction status constants

+

These values represent the possible status of a transaction: the current value +can be read using the connection.get_transaction_status() method.

+
+
+psycopg2.extensions.TRANSACTION_STATUS_IDLE
+

The session is idle and there is no current transaction.

+
+ +
+
+psycopg2.extensions.TRANSACTION_STATUS_ACTIVE
+

A command is currently in progress.

+
+ +
+
+psycopg2.extensions.TRANSACTION_STATUS_INTRANS
+

The session is idle in a valid transaction block.

+
+ +
+
+psycopg2.extensions.TRANSACTION_STATUS_INERROR
+

The session is idle in a failed transaction block.

+
+ +
+
+psycopg2.extensions.TRANSACTION_STATUS_UNKNOWN
+

Reported if the connection with the server is bad.

+
+ +
+
+

Connection status constants

+

These values represent the possible status of a connection: the current value +can be read from the status attribute.

+

It is possible to find the connection in other status than the one shown below. +Those are the only states in which a working connection is expected to be found +during the execution of regular Python client code: other states are for +internal usage and Python code should not rely on them.

+
+
+psycopg2.extensions.STATUS_READY
+

Connection established. No transaction in progress.

+
+ +
+
+psycopg2.extensions.STATUS_BEGIN
+

Connection established. A transaction is currently in progress.

+
+ +
+
+psycopg2.extensions.STATUS_IN_TRANSACTION
+

An alias for STATUS_BEGIN

+
+ +
+
+psycopg2.extensions.STATUS_PREPARED
+

The connection has been prepared for the second phase in a two-phase +commit transaction. The connection can’t be used to send commands +to the database until the transaction is finished with +tpc_commit() or tpc_rollback().

+

+New in version 2.3.

+
+ +
+
+

Poll constants

+

+New in version 2.2.0.

+

These values can be returned by connection.poll() during asynchronous +connection and communication. They match the values in the libpq enum +PostgresPollingStatusType. See Asynchronous support and +Support to coroutine libraries.

+
+
+psycopg2.extensions.POLL_OK
+

The data being read is available, or the file descriptor is ready for +writing: reading or writing will not block.

+
+ +
+
+psycopg2.extensions.POLL_READ
+

Some data is being read from the backend, but it is not available yet on +the client and reading would block. Upon receiving this value, the client +should wait for the connection file descriptor to be ready for reading. +For example:

+
select.select([conn.fileno()], [], [])
+
+
+
+ +
+
+psycopg2.extensions.POLL_WRITE
+

Some data is being sent to the backend but the connection file descriptor +can’t currently accept new data. Upon receiving this value, the client +should wait for the connection file descriptor to be ready for writing. +For example:

+
select.select([], [conn.fileno()], [])
+
+
+
+ +
+
+psycopg2.extensions.POLL_ERROR
+

There was a problem during connection polling. This value should actually +never be returned: in case of poll error usually an exception containing +the relevant details is raised.

+
+ +
+
+

Additional database types

+

The extensions module includes typecasters for many standard +PostgreSQL types. These objects allow the conversion of returned data into +Python objects. All the typecasters are automatically registered, except +UNICODE and UNICODEARRAY: you can register them using +register_type() in order to receive Unicode objects instead of strings +from the database. See Unicode handling for details.

+
+
+psycopg2.extensions.BOOLEAN
+
+psycopg2.extensions.DATE
+
+psycopg2.extensions.DECIMAL
+
+psycopg2.extensions.FLOAT
+
+psycopg2.extensions.INTEGER
+
+psycopg2.extensions.INTERVAL
+
+psycopg2.extensions.LONGINTEGER
+
+psycopg2.extensions.TIME
+
+psycopg2.extensions.UNICODE
+

Typecasters for basic types. Note that a few other ones (BINARY, +DATETIME, NUMBER, ROWID, +STRING) are exposed by the psycopg2 module for DB API 2.0 +compliance.

+
+ +
+
+psycopg2.extensions.BINARYARRAY
+
+psycopg2.extensions.BOOLEANARRAY
+
+psycopg2.extensions.DATEARRAY
+
+psycopg2.extensions.DATETIMEARRAY
+
+psycopg2.extensions.DECIMALARRAY
+
+psycopg2.extensions.FLOATARRAY
+
+psycopg2.extensions.INTEGERARRAY
+
+psycopg2.extensions.INTERVALARRAY
+
+psycopg2.extensions.LONGINTEGERARRAY
+
+psycopg2.extensions.ROWIDARRAY
+
+psycopg2.extensions.STRINGARRAY
+
+psycopg2.extensions.TIMEARRAY
+
+psycopg2.extensions.UNICODEARRAY
+

Typecasters to convert arrays of sql types into Python lists.

+
+ +
+
+psycopg2.extensions.PYDATE
+
+psycopg2.extensions.PYDATETIME
+
+psycopg2.extensions.PYINTERVAL
+
+psycopg2.extensions.PYTIME
+
+psycopg2.extensions.PYDATEARRAY
+
+psycopg2.extensions.PYDATETIMEARRAY
+
+psycopg2.extensions.PYINTERVALARRAY
+
+psycopg2.extensions.PYTIMEARRAY
+

Typecasters to convert time-related data types to Python datetime +objects.

+
+ +
+
+psycopg2.extensions.MXDATE
+
+psycopg2.extensions.MXDATETIME
+
+psycopg2.extensions.MXINTERVAL
+
+psycopg2.extensions.MXTIME
+
+psycopg2.extensions.MXDATEARRAY
+
+psycopg2.extensions.MXDATETIMEARRAY
+
+psycopg2.extensions.MXINTERVALARRAY
+
+psycopg2.extensions.MXTIMEARRAY
+

Typecasters to convert time-related data types to mx.DateTime objects. +Only available if Psycopg was compiled with mx support.

+
+ +

+Changed in version 2.2.0: previously the DECIMAL typecaster and the specific time-related +typecasters (PY* and MX*) were not exposed by the extensions +module. In older versions they can be imported from the implementation +module psycopg2._psycopg.

+
+
+ + +
+
+
+ +
+
+ + + + \ No newline at end of file diff -Nru psycopg2-2.0.13/doc/html/extras.html psycopg2-2.4.5/doc/html/extras.html --- psycopg2-2.0.13/doc/html/extras.html 1970-01-01 00:00:00.000000000 +0000 +++ psycopg2-2.4.5/doc/html/extras.html 2012-03-28 21:17:18.000000000 +0000 @@ -0,0 +1,610 @@ + + + + + + + + + + psycopg2.extras – Miscellaneous goodies for Psycopg 2 — Psycopg 2.4.5 documentation + + + + + + + + + + + + + + + +
+
+
+
+ +
+

psycopg2.extras – Miscellaneous goodies for Psycopg 2

+

This module is a generic place used to hold little helper functions and +classes until a better place in the distribution is found.

+
+

Connection and cursor subclasses

+

A few objects that change the way the results are returned by the cursor or +modify the object behavior in some other way. Typically connection +subclasses are passed as connection_factory argument to +connect() so that the connection will generate the matching +cursor subclass. Alternatively a cursor subclass can be used one-off by +passing it as the cursor_factory argument to the cursor() +method of a regular connection.

+
+

Dictionary-like cursor

+

The dict cursors allow to access to the retrieved records using an iterface +similar to the Python dictionaries instead of the tuples.

+
>>> dict_cur = conn.cursor(cursor_factory=psycopg2.extras.DictCursor)
+>>> dict_cur.execute("INSERT INTO test (num, data) VALUES(%s, %s)",
+...                  (100, "abc'def"))
+>>> dict_cur.execute("SELECT * FROM test")
+>>> rec = dict_cur.fetchone()
+>>> rec['id']
+1
+>>> rec['num']
+100
+>>> rec['data']
+"abc'def"
+
+
+

The records still support indexing as the original tuple:

+
>>> rec[2]
+"abc'def"
+
+
+
+
+class psycopg2.extras.DictCursor(*args, **kwargs)
+

A cursor that keeps a list of column name -> index mappings.

+
+ +
+
+class psycopg2.extras.DictConnection
+

A connection that uses DictCursor automatically.

+
+ +
+
+class psycopg2.extras.DictRow(cursor)
+

A row object that allow by-colmun-name access to data.

+
+ +
+
+

Real dictionary cursor

+
+
+class psycopg2.extras.RealDictCursor(*args, **kwargs)
+

A cursor that uses a real dict as the base type for rows.

+

Note that this cursor is extremely specialized and does not allow +the normal access (using integer indices) to fetched data. If you need +to access database rows both as a dictionary and a list, then use +the generic DictCursor instead of RealDictCursor.

+
+ +
+
+class psycopg2.extras.RealDictConnection
+

A connection that uses RealDictCursor automatically.

+
+ +
+
+class psycopg2.extras.RealDictRow(cursor)
+

A dict subclass representing a data record.

+
+ +
+
+

namedtuple cursor

+

+New in version 2.3.

+

These objects require collections.namedtuple() to be found, so it is +available out-of-the-box only from Python 2.6. Anyway, the namedtuple +implementation is compatible with previous Python versions, so all you +have to do is to download it and make it available where we +expect it to be...

+
from somewhere import namedtuple
+import collections
+collections.namedtuple = namedtuple
+from psycopg.extras import NamedTupleConnection
+# ...
+
+
+
+
+class psycopg2.extras.NamedTupleCursor
+

A cursor that generates results as namedtuple.

+

fetch*() methods will return named tuples instead of regular tuples, so +their elements can be accessed both as regular numeric items as well as +attributes.

+
>>> nt_cur = conn.cursor(cursor_factory=psycopg2.extras.NamedTupleCursor)
+>>> rec = nt_cur.fetchone()
+>>> rec
+Record(id=1, num=100, data="abc'def")
+>>> rec[1]
+100
+>>> rec.data
+"abc'def"
+
+
+
+ +
+
+class psycopg2.extras.NamedTupleConnection
+

A connection that uses NamedTupleCursor automatically.

+
+ +
+
+

Logging cursor

+
+
+class psycopg2.extras.LoggingConnection
+

A connection that logs all queries to a file or logger object.

+
+
+initialize(logobj)
+

Initialize the connection to log to logobj.

+

The logobj parameter can be an open file object or a Logger +instance from the standard logging module.

+
+ +
+
+filter(msg, curs)
+

Filter the query before logging it.

+

This is the method to overwrite to filter unwanted queries out of the +log or to add some extra data to the output. The default implementation +just does nothing.

+
+ +
+ +
+
+class psycopg2.extras.LoggingCursor
+

A cursor that logs queries using its connection logging facilities.

+
+ +
+
+class psycopg2.extras.MinTimeLoggingConnection
+

A connection that logs queries based on execution time.

+

This is just an example of how to sub-class LoggingConnection to +provide some extra filtering for the logged queries. Both the +inizialize() and filter() methods are overwritten to make sure +that only queries executing for more than mintime ms are logged.

+

Note that this connection uses the specialized cursor +MinTimeLoggingCursor.

+
+ +
+
+class psycopg2.extras.MinTimeLoggingCursor
+

The cursor sub-class companion to MinTimeLoggingConnection.

+
+ +
+
+
+

Additional data types

+
+

Hstore data type

+

+New in version 2.3.

+

The hstore data type is a key-value store embedded in PostgreSQL. It has +been available for several server versions but with the release 9.0 it has +been greatly improved in capacity and usefulness with the addiction of many +functions. It supports GiST or GIN indexes allowing search by keys or +key/value pairs as well as regular BTree indexes for equality, uniqueness etc.

+

Psycopg can convert Python dict objects to and from hstore structures. +Only dictionaries with string/unicode keys and values are supported. None +is also allowed as value but not as a key. Psycopg uses a more efficient hstore +representation when dealing with PostgreSQL 9.0 but previous server versions +are supported as well. By default the adapter/typecaster are disabled: they +can be enabled using the register_hstore() function.

+
+
+psycopg2.extras.register_hstore(conn_or_curs, globally=False, unicode=False, oid=None, array_oid=None)
+

Register adapter and typecaster for dict-hstore conversions.

+ +++ + + + +
Parameters:
    +
  • conn_or_curs – a connection or cursor: the typecaster will be +registered only on this object unless globally is set to True
  • +
  • globally – register the adapter globally, not only on conn_or_curs
  • +
  • unicode – if True, keys and values returned from the database +will be unicode instead of str. The option is not available on +Python 3
  • +
  • oid – the OID of the hstore type if known. If not, it will be +queried on conn_or_curs.
  • +
  • array_oid – the OID of the hstore array type if known. If not, it +will be queried on conn_or_curs.
  • +
+
+

The connection or cursor passed to the function will be used to query the +database and look for the OID of the hstore type (which may be different +across databases). If querying is not desirable (e.g. with +asynchronous connections) you may specify it in the +oid parameter, which can be found using a query such as SELECT +'hstore'::regtype::oid. Analogously you can obtain a value for array_oid +using a query such as SELECT 'hstore[]'::regtype::oid.

+

Note that, when passing a dictionary from Python to the database, both +strings and unicode keys and values are supported. Dictionaries returned +from the database have keys/values according to the unicode parameter.

+

The hstore contrib module must be already installed in the database +(executing the hstore.sql script in your contrib directory). +Raise ProgrammingError if the type is not found.

+

+Changed in version 2.4: added the oid parameter. If not specified, the typecaster is +installed also if hstore is not installed in the public +schema.

+

+Changed in version 2.4.3: added support for hstore array.

+
+ +
+
+

Composite types casting

+

+New in version 2.4.

+

Using register_composite() it is possible to cast a PostgreSQL composite +type (either created with the CREATE TYPE command or implicitly defined +after a table row type) into a Python named tuple, or into a regular tuple if +collections.namedtuple() is not found.

+
>>> cur.execute("CREATE TYPE card AS (value int, suit text);")
+>>> psycopg2.extras.register_composite('card', cur)
+<psycopg2.extras.CompositeCaster object at 0x...>
+
+>>> cur.execute("select (8, 'hearts')::card")
+>>> cur.fetchone()[0]
+card(value=8, suit='hearts')
+
+
+

Nested composite types are handled as expected, but the type of the composite +components must be registered as well.

+
>>> cur.execute("CREATE TYPE card_back AS (face card, back text);")
+>>> psycopg2.extras.register_composite('card_back', cur)
+<psycopg2.extras.CompositeCaster object at 0x...>
+
+>>> cur.execute("select ((8, 'hearts'), 'blue')::card_back")
+>>> cur.fetchone()[0]
+card_back(face=card(value=8, suit='hearts'), back='blue')
+
+
+

Adaptation from Python tuples to composite types is automatic instead and +requires no adapter registration.

+
+
+psycopg2.extras.register_composite(name, conn_or_curs, globally=False)
+

Register a typecaster to convert a composite type into a tuple.

+ +++ + + + + + +
Parameters:
    +
  • name – the name of a PostgreSQL composite type, e.g. created using +the CREATE TYPE command
  • +
  • conn_or_curs – a connection or cursor used to find the type oid and +components; the typecaster is registered in a scope limited to this +object, unless globally is set to True
  • +
  • globally – if False (default) register the typecaster only on +conn_or_curs, otherwise register it globally
  • +
+
Returns:

the registered CompositeCaster instance responsible for the +conversion

+
+

+Changed in version 2.4.3: added support for array of composite types

+
+ +
+
+class psycopg2.extras.CompositeCaster(name, oid, attrs, array_oid=None)
+

Helps conversion of a PostgreSQL composite type into a Python object.

+

The class is usually created by the register_composite() function. +You may want to create and register manually instances of the class if +querying the database at registration time is not desirable (such as when +using an asynchronous connections).

+
+
+name
+

The name of the PostgreSQL type.

+
+ +
+
+oid
+

The oid of the PostgreSQL type.

+
+ +
+
+array_oid
+

The oid of the PostgreSQL array type, if available.

+
+ +
+
+type
+

The type of the Python objects returned. If collections.namedtuple() +is available, it is a named tuple with attributes equal to the type +components. Otherwise it is just the tuple object.

+
+ +
+
+attnames
+

List of component names of the type to be casted.

+
+ +
+
+atttypes
+

List of component type oids of the type to be casted.

+
+ +
+ +
+
+

UUID data type

+

+New in version 2.0.9.

+

+Changed in version 2.0.13: added UUID array support.

+
>>> psycopg2.extras.register_uuid()
+<psycopg2._psycopg.type object at 0x...>
+
+>>> # Python UUID can be used in SQL queries
+>>> import uuid
+>>> my_uuid = uuid.UUID('{12345678-1234-5678-1234-567812345678}')
+>>> psycopg2.extensions.adapt(my_uuid).getquoted()
+"'12345678-1234-5678-1234-567812345678'::uuid"
+
+>>> # PostgreSQL UUID are transformed into Python UUID objects.
+>>> cur.execute("SELECT 'a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11'::uuid")
+>>> cur.fetchone()[0]
+UUID('a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11')
+
+
+
+
+psycopg2.extras.register_uuid(oids=None, conn_or_curs=None)
+

Create the UUID type and an uuid.UUID adapter.

+ +++ + + + +
Parameters:
    +
  • oids – oid for the PostgreSQL uuid type, or 2-items sequence +with oids of the type and the array. If not specified, use PostgreSQL +standard oids.
  • +
  • conn_or_curs – where to register the typecaster. If not specified, +register it globally.
  • +
+
+
+ +
+
+class psycopg2.extras.UUID_adapter(uuid)
+

Adapt Python’s uuid.UUID type to PostgreSQL’s uuid.

+
+ +
+
+

inet data type

+

+New in version 2.0.9.

+

+Changed in version 2.4.5: added inet array support.

+
>>> psycopg2.extras.register_inet()
+<psycopg2._psycopg.type object at 0x...>
+
+>>> cur.mogrify("SELECT %s", (Inet('127.0.0.1/32'),))
+"SELECT E'127.0.0.1/32'::inet"
+
+>>> cur.execute("SELECT '192.168.0.1/24'::inet")
+>>> cur.fetchone()[0].addr
+'192.168.0.1/24'
+
+
+
+
+psycopg2.extras.register_inet(oid=None, conn_or_curs=None)
+

Create the INET type and an Inet adapter.

+ +++ + + + +
Parameters:
    +
  • oid – oid for the PostgreSQL inet type, or 2-items sequence +with oids of the type and the array. If not specified, use PostgreSQL +standard oids.
  • +
  • conn_or_curs – where to register the typecaster. If not specified, +register it globally.
  • +
+
+
+ +
+
+class psycopg2.extras.Inet(addr)
+

Wrap a string to allow for correct SQL-quoting of inet values.

+

Note that this adapter does NOT check the passed value to make +sure it really is an inet-compatible address but DOES call adapt() +on it to make sure it is impossible to execute an SQL-injection +by passing an evil value to the initializer.

+
+ +
+
+
+

Fractional time zones

+
+
+psycopg2.extras.register_tstz_w_secs(oids=None, conn_or_curs=None)
+

The function used to register an alternate type caster for +TIMESTAMP WITH TIME ZONE to deal with historical time zones with +seconds in the UTC offset.

+

These are now correctly handled by the default type caster, so currently +the function doesn’t do anything.

+

+New in version 2.0.9.

+

+Changed in version 2.2.2: function is no-op: see Time zones handling.

+
+ +
+
+

Coroutine support

+
+
+psycopg2.extras.wait_select(conn)
+

Wait until a connection or cursor has data available.

+

The function is an example of a wait callback to be registered with +set_wait_callback(). This function uses select() +to wait for data available.

+
+ +
+
+ + +
+
+
+ +
+
+ + + + \ No newline at end of file diff -Nru psycopg2-2.0.13/doc/html/faq.html psycopg2-2.4.5/doc/html/faq.html --- psycopg2-2.0.13/doc/html/faq.html 1970-01-01 00:00:00.000000000 +0000 +++ psycopg2-2.4.5/doc/html/faq.html 2012-03-28 21:17:18.000000000 +0000 @@ -0,0 +1,300 @@ + + + + + + + + + + Frequently Asked Questions — Psycopg 2.4.5 documentation + + + + + + + + + + + + + + +
+
+
+
+ +
+

Frequently Asked Questions

+

Here are a few gotchas you may encounter using psycopg2. Feel free to +suggest new entries!

+
+

Problems with transactions handling

+
+
Why does psycopg2 leave database sessions “idle in transaction”?
+

Psycopg normally starts a new transaction the first time a query is +executed, e.g. calling cursor.execute(), even if the command is a +SELECT. The transaction is not closed until an explicit +commit() or rollback().

+

If you are writing a long-living program, you should probably make sure to +call one of the transaction closing methods before leaving the connection +unused for a long time (which may also be a few seconds, depending on the +concurrency level in your database). Alternatively you can use a +connection in autocommit mode to avoid a new transaction to +be started at the first command.

+
+
+
+
I receive the error current transaction is aborted, commands ignored until end of transaction block and can’t do anything else!
+
There was a problem in the previous command to the database, which +resulted in an error. The database will not recover automatically from +this condition: you must run a rollback() before sending +new commands to the session (if this seems too harsh, remember that +PostgreSQL supports nested transactions using the SAVEPOINT command).
+
+
+
Why do I get the error current transaction is aborted, commands ignored until end of transaction block when I use multiprocessing (or any other forking system) and not when use threading?
+
Psycopg’s connections can’t be shared across processes (but are thread +safe). If you are forking the Python process make sure to create a new +connection in each forked child. See Thread and process safety for further +informations.
+
+
+
+

Problems with type conversions

+
+
Why does cursor.execute() raise the exception can’t adapt?
+
Psycopg converts Python objects in a SQL string representation by looking +at the object class. The exception is raised when you are trying to pass +as query parameter an object for which there is no adapter registered for +its class. See Adapting new Python types to SQL syntax for informations.
+
+
+
I can’t pass an integer or a float parameter to my query: it says a number is required, but it is a number!
+

In your query string, you always have to use %s placeholders, +event when passing a number. All Python objects are converted by Psycopg +in their SQL representation, so they get passed to the query as strings. +See Passing parameters to SQL queries.

+
>>> cur.execute("INSERT INTO numbers VALUES (%d)", (42,)) # WRONG
+>>> cur.execute("INSERT INTO numbers VALUES (%s)", (42,)) # correct
+
+
+
+
+
+
I try to execute a query but it fails with the error not all arguments converted during string formatting (or object does not support indexing). Why?
+

Psycopg always require positional arguments to be passed as a sequence, even +when the query takes a single parameter. And remember that to make a +single item tuple in Python you need a comma! See Passing parameters to SQL queries.

+
>>> cur.execute("INSERT INTO foo VALUES (%s)", "bar")    # WRONG
+>>> cur.execute("INSERT INTO foo VALUES (%s)", ("bar"))  # WRONG
+>>> cur.execute("INSERT INTO foo VALUES (%s)", ("bar",)) # correct
+>>> cur.execute("INSERT INTO foo VALUES (%s)", ["bar"])  # correct
+
+
+
+
+
+
My database is Unicode, but I receive all the strings as UTF-8 str. Can I receive unicode objects instead?
+

The following magic formula will do the trick:

+
psycopg2.extensions.register_type(psycopg2.extensions.UNICODE)
+psycopg2.extensions.register_type(psycopg2.extensions.UNICODEARRAY)
+
+
+

See Unicode handling for the gory details.

+
+
+
+
Psycopg converts decimal/numeric database types into Python Decimal objects. Can I have float instead?
+

You can register a customized adapter for PostgreSQL decimal type:

+
DEC2FLOAT = psycopg2.extensions.new_type(
+    psycopg2.extensions.DECIMAL.values,
+    'DEC2FLOAT',
+    lambda value, curs: float(value) if value is not None else None)
+psycopg2.extensions.register_type(DEC2FLOAT)
+
+
+

See Type casting of SQL types into Python objects to read the relevant +documentation. If you find psycopg2.extensions.DECIMAL not avalable, use +psycopg2._psycopg.DECIMAL instead.

+
+
+
+
Transferring binary data from PostgreSQL 9.0 doesn’t work.
+

PostgreSQL 9.0 uses by default the “hex” format to transfer +bytea data: the format can’t be parsed by the libpq 8.4 and +earlier. The problem is solved in Psycopg 2.4.1, that uses its own parser +for the bytea format. For previous Psycopg releases, three options +to solve the problem are:

+
    +
  • set the bytea_output parameter to escape in the server;
  • +
  • execute the database command SET bytea_output TO escape; in the +session before reading binary data;
  • +
  • upgrade the libpq library on the client to at least 9.0.
  • +
+
+
+
+
Arrays of TYPE are not casted to list.
+
Arrays are only casted to list when their oid is known, and an array +typecaster is registered for them. If there is no typecaster, the array is +returned unparsed from PostgreSQL (e.g. {a,b,c}). It is easy to create +a generic arrays typecaster, returning a list of array: an example is +provided in the new_array_type() documentation.
+
+
+
+

Best practices

+
+
When should I save and re-use a cursor as opposed to creating a new one as needed?
+
Cursors are lightweight objects and creating lots of them should not pose +any kind of problem. But note that cursors used to fetch result sets will +cache the data and use memory in proportion to the result set size. Our +suggestion is to almost always create a new cursor and dispose old ones as +soon as the data is not required anymore (call close() on +them.) The only exception are tight loops where one usually use the same +cursor for a whole bunch of INSERTs or UPDATEs.
+
+
+
When should I save and re-use a connection as opposed to creating a new one as needed?
+
Creating a connection can be slow (think of SSL over TCP) so the best +practice is to create a single connection and keep it open as long as +required. It is also good practice to rollback or commit frequently (even +after a single SELECT statement) to make sure the backend is never +left “idle in transaction”. See also psycopg2.pool for lightweight +connection pooling.
+
+
+
What are the advantages or disadvantages of using named cursors?
+
The only disadvantages is that they use up resources on the server and +that there is a little overhead because a at least two queries (one to +create the cursor and one to fetch the initial result set) are issued to +the backend. The advantage is that data is fetched one chunk at a time: +using small fetchmany() values it is possible to use very +little memory on the client and to skip or discard parts of the result set.
+
+
+
+

Problems compiling and deploying psycopg2

+
+
I can’t compile psycopg2: the compiler says error: Python.h: No such file or directory. What am I missing?
+
You need to install a Python development package: it is usually called +python-dev.
+
+
+
I can’t compile psycopg2: the compiler says error: libpq-fe.h: No such file or directory. What am I missing?
+
You need to install the development version of the libpq: the package is +usually called libpq-dev.
+
+
+
psycopg2 raises ImportError with message _psycopg.so: undefined symbol: lo_truncate when imported.
+

This means that Psycopg has been compiled with lo_truncate() support, +which means that the libpq used at compile time was version >= 8.3, but at +runtime an older libpq library is found. You can use:

+
$ ldd /path/to/packages/psycopg2/_psycopg.so | grep libpq
+
+

to find what is the version used at runtime.

+

You can avoid the problem by using the same version of the +pg_config at install time and the libpq at runtime.

+
+
+
+
Psycopg raises ImportError: cannot import name tz on import in mod_wsgi / ASP, but it works fine otherwise.
+
If psycopg2 is installed in an egg (e.g. because installed by +easy_install), the user running the program may be unable to +write in the eggs cache. Set the env variable +PYTHON_EGG_CACHE to a writable directory. With modwsgi you can +use the WSGIPythonEggs directive.
+
+
+
+ + +
+
+
+
+
+

Table Of Contents

+ + +

Previous topic

+

psycopg2.errorcodes – Error codes defined by PostgreSQL

+

This Page

+ + + +
+
+
+
+ + + + \ No newline at end of file diff -Nru psycopg2-2.0.13/doc/html/genindex.html psycopg2-2.4.5/doc/html/genindex.html --- psycopg2-2.0.13/doc/html/genindex.html 1970-01-01 00:00:00.000000000 +0000 +++ psycopg2-2.4.5/doc/html/genindex.html 2012-03-28 21:17:18.000000000 +0000 @@ -0,0 +1,2594 @@ + + + + + + + + + + + + Index — Psycopg 2.4.5 documentation + + + + + + + + + + + + + +
+
+
+
+ + +

Index

+ +
+ _ + | A + | B + | C + | D + | E + | F + | G + | H + | I + | L + | M + | N + | O + | P + | Q + | R + | S + | T + | U + | V + | W + | X + +
+

_

+ + +
+ +
_wrapped (psycopg2.extensions.ISQLQuote attribute) +
+ +
+ +

A

+ + + +
+ +
AbstractConnectionPool (class in psycopg2.pool) +
+ + +
adapt() (in module psycopg2.extensions) +
+ + +
Adaptation +
+ +
+ +
Boolean +
+ + +
Creating new adapters +
+ + +
Date/Time objects +
+ + +
Lists +
+ + +
None +
+ + +
Objects +
+ + +
Strings +
+ + +
Tuple +
+ + +
dict +
+ + +
namedtuple +
+ + +
numbers +
+ + +
tuple +
+ +
+ +
adapters (in module psycopg2.extensions) +
+ + +
apilevel (in module psycopg2) +
+ + +
+ Array +
+ +
+ +
Adaptation +
+ +
+ +
array_oid (psycopg2.extras.CompositeCaster attribute) +
+ + +
arraysize (cursor attribute) +
+ +
+ +
AsIs (class in psycopg2.extensions) +
+ + +
async (connection attribute) +
+ + +
+ Asynchronous +
+ +
+ +
Connection +
+ + +
Notifications +
+ +
+ +
attnames (psycopg2.extras.CompositeCaster attribute) +
+ + +
atttypes (psycopg2.extras.CompositeCaster attribute) +
+ + +
Autocommit +
+ +
+ +
Transaction +
+ +
+ +
autocommit (connection attribute) +
+ +
+ +

B

+ + + +
+ +
+ Backend +
+ +
+ +
PID +
+ +
+ +
Begin +
+ + +
Binary (class in psycopg2.extensions) +
+ + +
BINARY (in module psycopg2) +
+ + +
Binary string +
+ + +
Binary() (in module psycopg2) +
+ + +
BINARYARRAY (in module psycopg2.extensions) +
+ + +
+ Boolean +
+ +
+ +
Adaptation +
+ +
+
+ +
Boolean (class in psycopg2.extensions) +
+ + +
BOOLEAN (in module psycopg2.extensions) +
+ + +
BOOLEANARRAY (in module psycopg2.extensions) +
+ + +
bqual (psycopg2.extensions.Xid attribute) +
+ + +
+ Buffer +
+ +
+ +
Adaptation +
+ +
+ +
+ bytea +
+ +
+ +
Adaptation +
+ +
+ +
+ bytearray +
+ +
+ +
Adaptation +
+ +
+ +
+ bytes +
+ +
+ +
Adaptation +
+ +
+
+ +

C

+ + + +
+ +
callproc() (cursor method) +
+ + +
cancel() (connection method) +
+ + +
cast() (cursor method) +
+ + +
channel (psycopg2.extensions.Notify attribute) +
+ + +
+ Client +
+ +
+ +
Encoding +
+ + +
Logging +
+ +
+ +
close() (connection method) +
+ +
+ +
(cursor method) +
+ + +
(psycopg2.extensions.lobject method) +
+ +
+ +
closeall() (psycopg2.pool.AbstractConnectionPool method) +
+ + +
closed (connection attribute) +
+ +
+ +
(cursor attribute) +
+ + +
(psycopg2.extensions.lobject attribute) +
+ +
+ +
Commit +
+ +
+ +
Prepared +
+ + +
Transaction +
+ +
+ +
commit() (connection method) +
+ + +
+ Composite types +
+ +
+ +
Data types +
+ +
+ +
CompositeCaster (class in psycopg2.extras) +
+ + +
connect() (in module psycopg2) +
+ + +
+ Connection +
+ +
+ +
Asynchronous +
+ + +
Parameters +
+ + +
Pooling +
+ + +
Status +
+ + +
Subclassing +
+ +
+
+ +
connection (built-in class) +
+ +
+ +
(class in psycopg2.extensions) +
+ + +
(cursor attribute) +
+ +
+ +
+ Connection status +
+ +
+ +
Constants +
+ +
+ +
Connection string +
+ + +
+ Constants +
+ +
+ +
Connection status +
+ + +
Isolation level +
+ + +
Poll status +
+ + +
Transaction status +
+ +
+ +
+ COPY +
+ +
+ +
SQL command +
+ +
+ +
copy_expert() (cursor method) +
+ + +
copy_from() (cursor method) +
+ + +
copy_to() (cursor method) +
+ + +
Coroutine +
+ + +
+ Coroutine; +
+ +
+ +
Example +
+ +
+ +
+ Cursor +
+ +
+ +
Dictionary +
+ + +
Logging +
+ + +
Named +
+ + +
Server side +
+ + +
Subclassing +
+ + +
namedtuple +
+ +
+ +
cursor (built-in class) +
+ +
+ +
(class in psycopg2.extensions) +
+ + +
(psycopg2.Error attribute) +
+ +
+ +
cursor() (connection method) +
+ +
+ +

D

+ + + +
+ +
+ Data types +
+ +
+ +
Adaptation +
+ + +
Additional +
+ + +
Composite types +
+ + +
Creating new adapters +
+ + +
INET +
+ + +
UUID +
+ + +
hstore +
+ +
+ +
database (psycopg2.extensions.Xid attribute) +
+ + +
DatabaseError +
+ + +
DataError +
+ + +
DATE (in module psycopg2.extensions) +
+ + +
+ Date objects +
+ +
+ +
Adaptation +
+ +
+ +
Date() (in module psycopg2) +
+ + +
DATEARRAY (in module psycopg2.extensions) +
+ + +
DateFromMx (class in psycopg2.extensions) +
+ + +
DateFromPy (class in psycopg2.extensions) +
+ + +
DateFromTicks() (in module psycopg2) +
+ + +
DATETIME (in module psycopg2) +
+ + +
DATETIMEARRAY (in module psycopg2.extensions) +
+ +
+ +
+ Decimal +
+ +
+ +
Adaptation +
+ +
+ +
DECIMAL (in module psycopg2.extensions) +
+ + +
DECIMALARRAY (in module psycopg2.extensions) +
+ + +
+ DECLARE +
+ +
+ +
SQL command +
+ +
+ +
description (cursor attribute) +
+ + +
+ dict +
+ +
+ +
Adaptation, [1] +
+ +
+ +
DictConnection (class in psycopg2.extras) +
+ + +
DictCursor (class in psycopg2.extras) +
+ + +
+ Dictionary +
+ +
+ +
Cursor +
+ +
+ +
DictRow (class in psycopg2.extras) +
+ + +
dsn (connection attribute) +
+ + +
DSN (Database Source Name) +
+ +
+ +

E

+ + + +
+ +
+ Encoding +
+ +
+ +
Client +
+ + +
Mapping +
+ +
+ +
encoding (connection attribute) +
+ + +
encodings (in module psycopg2.extensions) +
+ + +
+ environment variable +
+ +
+ +
PSYCOPG_DISPLAY_SIZE +
+ + +
PYTHON_EGG_CACHE +
+ + +
standard_conforming_string +
+ +
+ +
Error +
+ +
+ +
Codes +
+ +
+ +
Eventlet +
+ +
+ +
+ Example +
+ +
+ +
Coroutine; +
+ + +
Cursor subclass +
+ + +
Types adaptation +
+ + +
Usage +
+ +
+ +
+ Exceptions +
+ +
+ +
Additional +
+ + +
DB API +
+ + +
In the connection class +
+ +
+ +
execute() (cursor method) +
+ + +
executemany() (cursor method) +
+ + +
export() (psycopg2.extensions.lobject method) +
+ +
+ +

F

+ + + +
+ +
+ FETCH +
+ +
+ +
SQL command +
+ +
+ +
fetchall() (cursor method) +
+ + +
fetchmany() (cursor method) +
+ + +
fetchone() (cursor method) +
+ + +
fileno() (connection method) +
+ + +
filter() (psycopg2.extras.LoggingConnection method) +
+ + +
FixedOffsetTimezone (class in psycopg2.tz) +
+ +
+ +
+ Float +
+ +
+ +
Adaptation +
+ +
+ +
Float (class in psycopg2.extensions) +
+ + +
FLOAT (in module psycopg2.extensions) +
+ + +
FLOATARRAY (in module psycopg2.extensions) +
+ + +
format_id (psycopg2.extensions.Xid attribute) +
+ + +
from_string() (psycopg2.extensions.Xid method) +
+ +
+ +

G

+ + + +
+ +
get_backend_pid() (connection method) +
+ + +
get_parameter_status() (connection method) +
+ + +
get_transaction_status() (connection method) +
+ + +
get_wait_callback() (in module psycopg2.extensions) +
+ + +
getconn() (psycopg2.pool.AbstractConnectionPool method) +
+ +
+ +
getquoted() (psycopg2.extensions.AsIs method) +
+ +
+ +
(psycopg2.extensions.Binary method) +
+ + +
(psycopg2.extensions.ISQLQuote method) +
+ + +
(psycopg2.extensions.QuotedString method) +
+ +
+ +
gevent +
+ + +
Greenlet +
+ + +
gtrid (psycopg2.extensions.Xid attribute) +
+ +
+ +

H

+ + + +
+ +
+ Host +
+ +
+ +
Connection +
+ +
+
+ +
+ hstore +
+ +
+ +
Adaptation +
+ + +
Data types +
+ +
+
+ +

I

+ + + +
+ +
IN operator +
+ + +
+ INET +
+ +
+ +
Data types +
+ +
+ +
Inet (class in psycopg2.extras) +
+ + +
initialize() (psycopg2.extras.LoggingConnection method) +
+ + +
+ Integer +
+ +
+ +
Adaptation +
+ +
+ +
INTEGER (in module psycopg2.extensions) +
+ + +
INTEGERARRAY (in module psycopg2.extensions) +
+ + +
IntegrityError +
+ + +
InterfaceError +
+ + +
InternalError +
+ + +
INTERVAL (in module psycopg2.extensions) +
+ + +
+ Interval objects +
+ +
+ +
Adaptation +
+ +
+ +
INTERVALARRAY (in module psycopg2.extensions) +
+ +
+ +
IntervalFromMx (class in psycopg2.extensions) +
+ + +
IntervalFromPy (class in psycopg2.extensions) +
+ + +
isexecuting() (connection method) +
+ + +
+ Isolation level +
+ +
+ +
Constants +
+ + +
Transaction +
+ +
+ +
isolation_level (connection attribute) +
+ + +
ISOLATION_LEVEL_AUTOCOMMIT (in module psycopg2.extensions) +
+ + +
ISOLATION_LEVEL_READ_COMMITTED (in module psycopg2.extensions) +
+ + +
ISOLATION_LEVEL_READ_UNCOMMITTED (in module psycopg2.extensions) +
+ + +
ISOLATION_LEVEL_REPEATABLE_READ (in module psycopg2.extensions) +
+ + +
ISOLATION_LEVEL_SERIALIZABLE (in module psycopg2.extensions) +
+ + +
ISQLQuote (class in psycopg2.extensions) +
+ + +
itersize (cursor attribute) +
+ +
+ +

L

+ + + +
+ +
Large objects +
+ + +
lastrowid (cursor attribute) +
+ + +
+ LISTEN +
+ +
+ +
SQL command +
+ +
+ +
+ Lists +
+ +
+ +
Adaptation +
+ +
+ +
lobject (class in psycopg2.extensions) +
+ + +
lobject() (connection method) +
+ + +
LocalTimezone (class in psycopg2.tz) +
+ +
+ +
+ Logging +
+ +
+ +
Client +
+ + +
Cursor +
+ +
+ +
LoggingConnection (class in psycopg2.extras) +
+ + +
LoggingCursor (class in psycopg2.extras) +
+ + +
LONGINTEGER (in module psycopg2.extensions) +
+ + +
LONGINTEGERARRAY (in module psycopg2.extensions) +
+ + +
lookup() (in module psycopg2.errorcodes) +
+ +
+ +

M

+ + + +
+ +
+ memoryview +
+ +
+ +
Adaptation +
+ +
+ +
MinTimeLoggingConnection (class in psycopg2.extras) +
+ + +
MinTimeLoggingCursor (class in psycopg2.extras) +
+ + +
mode (psycopg2.extensions.lobject attribute) +
+ + +
mogrify() (cursor method) +
+ + +
+ MOVE +
+ +
+ +
SQL command +
+ +
+ +
Multiprocess +
+ + +
Multithread +
+ +
+ +
Connection pooling +
+ +
+ +
+ mx.DateTime +
+ +
+ +
Adaptation +
+ +
+
+ +
MXDATE (in module psycopg2.extensions) +
+ + +
MXDATEARRAY (in module psycopg2.extensions) +
+ + +
MXDATETIME (in module psycopg2.extensions) +
+ + +
MXDATETIMEARRAY (in module psycopg2.extensions) +
+ + +
MXINTERVAL (in module psycopg2.extensions) +
+ + +
MXINTERVALARRAY (in module psycopg2.extensions) +
+ + +
MXTIME (in module psycopg2.extensions) +
+ + +
MXTIMEARRAY (in module psycopg2.extensions) +
+ +
+ +

N

+ + + +
+ +
name (cursor attribute) +
+ +
+ +
(psycopg2.extras.CompositeCaster attribute) +
+ +
+ +
+ Named +
+ +
+ +
Cursor +
+ +
+ +
+ namedtuple +
+ +
+ +
Adaptation +
+ + +
Cursor +
+ +
+ +
NamedTupleConnection (class in psycopg2.extras) +
+ + +
NamedTupleCursor (class in psycopg2.extras) +
+ + +
new_array_type() (in module psycopg2.extensions) +
+ + +
new_type() (in module psycopg2.extensions) +
+ + +
nextset() (cursor method) +
+ + +
+ None +
+ +
+ +
Adaptation +
+ +
+
+ +
notices (connection attribute) +
+ + +
+ Notifications +
+ +
+ +
Asynchronous +
+ +
+ +
notifies (connection attribute) +
+ + +
+ NOTIFY +
+ +
+ +
SQL command +
+ +
+ +
Notify (class in psycopg2.extensions) +
+ + +
NotSupportedError +
+ + +
+ NULL +
+ +
+ +
Adaptation +
+ +
+ +
NUMBER (in module psycopg2) +
+ +
+ +

O

+ + + +
+ +
+ Objects +
+ +
+ +
Adaptation +
+ + +
Creating new adapters +
+ +
+ +
oid +
+ +
+ +
(psycopg2.extensions.lobject attribute) +
+ + +
(psycopg2.extras.CompositeCaster attribute) +
+ +
+
+ +
OperationalError +
+ + +
owner (psycopg2.extensions.Xid attribute) +
+ +
+ +

P

+ + + +
+ +
+ Parameters +
+ +
+ +
Connection +
+ + +
Query +
+ + +
Server +
+ +
+ +
paramstyle (in module psycopg2) +
+ + +
+ Password +
+ +
+ +
Connection +
+ +
+ +
payload (psycopg2.extensions.Notify attribute) +
+ + +
PersistentConnectionPool (class in psycopg2.pool) +
+ + +
+ PgBouncer +
+ +
+ +
unclean server +
+ +
+ +
pgcode (psycopg2.Error attribute) +
+ + +
pgerror (psycopg2.Error attribute) +
+ + +
+ PID +
+ +
+ +
Backend +
+ +
+ +
pid (psycopg2.extensions.Notify attribute) +
+ + +
+ Poll status +
+ +
+ +
Constants +
+ +
+ +
poll() (connection method) +
+ + +
POLL_ERROR (in module psycopg2.extensions) +
+ + +
POLL_OK (in module psycopg2.extensions) +
+ + +
POLL_READ (in module psycopg2.extensions) +
+ + +
POLL_WRITE (in module psycopg2.extensions) +
+ + +
+ Pooling +
+ +
+ +
Connection +
+ +
+ +
+ Port +
+ +
+ +
Connection +
+ +
+ +
+ Prepare +
+ +
+ +
Transaction +
+ +
+ +
prepare() (psycopg2.extensions.ISQLQuote method) +
+ + +
+ Prepared +
+ +
+ +
Commit +
+ + +
Rollback +
+ +
+ +
prepared (psycopg2.extensions.Xid attribute) +
+ +
+ +
ProgrammingError +
+ + +
+ Protocol +
+ +
+ +
Version +
+ +
+ +
protocol_version (connection attribute) +
+ + +
psycopg2 (module) +
+ + +
psycopg2.errorcodes (module) +
+ + +
psycopg2.extensions (module) +
+ + +
psycopg2.extras (module) +
+ + +
psycopg2.pool (module) +
+ + +
psycopg2.tz (module) +
+ + +
PSYCOPG_DISPLAY_SIZE +
+ + +
putconn() (psycopg2.pool.AbstractConnectionPool method) +
+ + +
PYDATE (in module psycopg2.extensions) +
+ + +
PYDATEARRAY (in module psycopg2.extensions) +
+ + +
PYDATETIME (in module psycopg2.extensions) +
+ + +
PYDATETIMEARRAY (in module psycopg2.extensions) +
+ + +
PYINTERVAL (in module psycopg2.extensions) +
+ + +
PYINTERVALARRAY (in module psycopg2.extensions) +
+ + +
+ Python Enhancement Proposals +
+ +
+ +
PEP 246, [1] +
+ +
+ +
PYTHON_EGG_CACHE +
+ + +
PYTIME (in module psycopg2.extensions) +
+ + +
PYTIMEARRAY (in module psycopg2.extensions) +
+ +
+ +

Q

+ + + +
+ +
+ Query +
+ +
+ +
Parameters +
+ +
+ +
query (cursor attribute) +
+ +
+ +
QueryCanceledError +
+ + +
QuotedString (class in psycopg2.extensions) +
+ +
+ +

R

+ + + +
+ +
Read only +
+ + +
read() (psycopg2.extensions.lobject method) +
+ + +
RealDictConnection (class in psycopg2.extras) +
+ + +
RealDictCursor (class in psycopg2.extras) +
+ + +
RealDictRow (class in psycopg2.extras) +
+ + +
+ Recover +
+ +
+ +
Transaction +
+ +
+ +
register_adapter() (in module psycopg2.extensions) +
+ + +
register_composite() (in module psycopg2.extras) +
+ + +
register_hstore() (in module psycopg2.extras) +
+ + +
register_inet() (in module psycopg2.extras) +
+ +
+ +
register_tstz_w_secs() (in module psycopg2.extras) +
+ + +
register_type() (in module psycopg2.extensions) +
+ + +
register_uuid() (in module psycopg2.extras) +
+ + +
reset() (connection method) +
+ + +
Rollback +
+ +
+ +
Prepared +
+ + +
Transaction +
+ +
+ +
rollback() (connection method) +
+ + +
rowcount (cursor attribute) +
+ + +
ROWID (in module psycopg2) +
+ + +
ROWIDARRAY (in module psycopg2.extensions) +
+ + +
rownumber (cursor attribute) +
+ +
+ +

S

+ + + +
+ +
scroll() (cursor method) +
+ + +
Security +
+ + +
seek() (psycopg2.extensions.lobject method) +
+ + +
+ Server +
+ +
+ +
Parameters +
+ + +
Version +
+ +
+ +
+ Server side +
+ +
+ +
Cursor +
+ +
+ +
server_version (connection attribute) +
+ + +
set_client_encoding() (connection method) +
+ + +
set_isolation_level() (connection method) +
+ + +
set_session() (connection method) +
+ + +
set_wait_callback() (in module psycopg2.extensions) +
+ + +
setinputsizes() (cursor method) +
+ + +
setoutputsize() (cursor method) +
+ + +
SimpleConnectionPool (class in psycopg2.pool) +
+ + +
+ SQL command +
+ +
+ +
COPY +
+ + +
DECLARE +
+ + +
FETCH +
+ + +
LISTEN +
+ + +
MOVE +
+ + +
NOTIFY +
+ +
+ +
SQL injection +
+ +
+ +
SQL_IN (class in psycopg2.extensions) +
+ + +
standard_conforming_string +
+ + +
+ Status +
+ +
+ +
Connection +
+ + +
Transaction +
+ +
+ +
status (connection attribute) +
+ + +
STATUS_BEGIN (in module psycopg2.extensions) +
+ + +
STATUS_IN_TRANSACTION (in module psycopg2.extensions) +
+ + +
STATUS_PREPARED (in module psycopg2.extensions) +
+ + +
STATUS_READY (in module psycopg2.extensions) +
+ + +
statusmessage (cursor attribute) +
+ + +
STRING (in module psycopg2) +
+ + +
string_types (in module psycopg2.extensions) +
+ + +
STRINGARRAY (in module psycopg2.extensions) +
+ + +
+ Strings +
+ +
+ +
Adaptation +
+ +
+ +
+ Subclassing +
+ +
+ +
Connection +
+ + +
Cursor +
+ +
+
+ +

T

+ + + +
+ +
tell() (psycopg2.extensions.lobject method) +
+ + +
Thread safety +
+ + +
ThreadedConnectionPool (class in psycopg2.pool) +
+ + +
threadsafety (in module psycopg2) +
+ + +
TIME (in module psycopg2.extensions) +
+ + +
+ Time objects +
+ +
+ +
Adaptation +
+ +
+ +
Time Zones +
+ + +
+ Time zones +
+ +
+ +
Fractional +
+ +
+ +
Time() (in module psycopg2) +
+ + +
TIMEARRAY (in module psycopg2.extensions) +
+ + +
TimeFromMx (class in psycopg2.extensions) +
+ + +
TimeFromPy (class in psycopg2.extensions) +
+ + +
TimeFromTicks() (in module psycopg2) +
+ + +
Timestamp() (in module psycopg2) +
+ + +
TimestampFromMx (class in psycopg2.extensions) +
+ + +
TimestampFromPy (class in psycopg2.extensions) +
+ + +
TimestampFromTicks() (in module psycopg2) +
+ + +
tpc_begin() (connection method) +
+ + +
tpc_commit() (connection method) +
+ +
+ +
tpc_prepare() (connection method) +
+ + +
tpc_recover() (connection method) +
+ + +
tpc_rollback() (connection method) +
+ + +
Transaction +
+ +
+ +
Autocommit +
+ + +
Commit +
+ + +
Isolation level +
+ + +
Prepare +
+ + +
Recover +
+ + +
Rollback +
+ + +
Status +
+ + +
Two-phase commit +
+ +
+ +
+ Transaction status +
+ +
+ +
Constants +
+ +
+ +
TRANSACTION_STATUS_ACTIVE (in module psycopg2.extensions) +
+ + +
TRANSACTION_STATUS_IDLE (in module psycopg2.extensions) +
+ + +
TRANSACTION_STATUS_INERROR (in module psycopg2.extensions) +
+ + +
TRANSACTION_STATUS_INTRANS (in module psycopg2.extensions) +
+ + +
TRANSACTION_STATUS_UNKNOWN (in module psycopg2.extensions) +
+ + +
TransactionRollbackError +
+ + +
truncate() (psycopg2.extensions.lobject method) +
+ + +
+ tuple +
+ +
+ +
Adaptation +
+ +
+ +
+ Tuple +
+ +
+ +
Adaptation +
+ +
+ +
+ Two-phase commit +
+ +
+ +
Transaction +
+ + +
methods +
+ +
+ +
type (psycopg2.extras.CompositeCaster attribute) +
+ + +
Type casting +
+ + +
tzinfo_factory (cursor attribute) +
+ +
+ +

U

+ + + +
+ +
Unicode +
+ +
+ +
Adaptation +
+ +
+ +
UNICODE (in module psycopg2.extensions) +
+ + +
UNICODEARRAY (in module psycopg2.extensions) +
+ + +
unlink() (psycopg2.extensions.lobject method) +
+ +
+ +
+ Usage +
+ +
+ +
Example +
+ +
+ +
+ Username +
+ +
+ +
Connection +
+ +
+ +
+ UUID +
+ +
+ +
Data types +
+ +
+ +
UUID_adapter (class in psycopg2.extras) +
+ +
+ +

V

+ + +
+ +
+ Version +
+ +
+ +
Protocol +
+ + +
Server +
+ +
+
+ +

W

+ + + +
+ +
Wait callback +
+ + +
wait_select() (in module psycopg2.extras) +
+ + +
Warning +
+ +
+ +
withhold (cursor attribute) +
+ + +
write() (psycopg2.extensions.lobject method) +
+ +
+ +

X

+ + + +
+ +
Xid (class in psycopg2.extensions) +
+ +
+ +
xid() (connection method) +
+ +
+ + + +
+
+
+
+
+ + + + + +
+
+
+
+ + + + \ No newline at end of file diff -Nru psycopg2-2.0.13/doc/html/index.html psycopg2-2.4.5/doc/html/index.html --- psycopg2-2.0.13/doc/html/index.html 1970-01-01 00:00:00.000000000 +0000 +++ psycopg2-2.4.5/doc/html/index.html 2012-03-28 21:17:18.000000000 +0000 @@ -0,0 +1,190 @@ + + + + + + + + + + Psycopg – PostgreSQL database adapter for Python — Psycopg 2.4.5 documentation + + + + + + + + + + + + + + +
+
+
+
+ +
+

Psycopg – PostgreSQL database adapter for Python

+

Psycopg is a PostgreSQL database adapter for the Python programming +language. Its main features are that it supports the full Python DB API 2.0 +and it is thread safe (threads can share the connections). It was designed for +heavily multi-threaded applications that create and destroy lots of cursors and +make a large number of concurrent INSERTs or UPDATEs. +The Psycopg distribution includes ZPsycopgDA, a Zope Database Adapter.

+

Psycopg 2 is mostly implemented in C as a libpq wrapper, resulting in being +both efficient and secure. It features client-side and server-side cursors, asynchronous communication and notifications, COPY TO/COPY FROM +support, and a flexible objects adaptation system. Many basic Python types are supported +out-of-the-box and mapped to matching PostgreSQL data types, such as strings +(both bytes and Unicode), numbers (ints, longs, floats, decimals), booleans and +datetime objects (both built-in and mx.DateTime), several types of +binary objects. Also available are mappings between lists +and PostgreSQL arrays of any supported type, between dictionaries and +PostgreSQL hstores, and between tuples/namedtuples and +PostgreSQL composite types.

+

Psycopg 2 is both Unicode and Python 3 friendly.

+

Contents

+ +

Indices and tables

+ +
+ + +
+
+
+
+
+

Next topic

+

Basic module usage

+

This Page

+ + + +
+
+
+
+ + + + \ No newline at end of file diff -Nru psycopg2-2.0.13/doc/html/module.html psycopg2-2.4.5/doc/html/module.html --- psycopg2-2.0.13/doc/html/module.html 1970-01-01 00:00:00.000000000 +0000 +++ psycopg2-2.4.5/doc/html/module.html 2012-03-28 21:17:18.000000000 +0000 @@ -0,0 +1,456 @@ + + + + + + + + + + The psycopg2 module content — Psycopg 2.4.5 documentation + + + + + + + + + + + + + + + +
+
+
+
+ +
+

The psycopg2 module content

+

The module interface respects the standard defined in the DB API 2.0.

+
+
+psycopg2.connect(dsn or params [, connection_factory] [, async=0])
+

Create a new database session and return a new connection object.

+

The connection parameters can be specified either as a string:

+
conn = psycopg2.connect("dbname=test user=postgres password=secret")
+
+
+

or using a set of keyword arguments:

+
conn = psycopg2.connect(database="test", user="postgres", password="secret")
+
+
+

The basic connection parameters are:

+
    +
  • dbname – the database name (only in dsn string)
  • +
  • database – the database name (only as keyword argument)
  • +
  • user – user name used to authenticate
  • +
  • password – password used to authenticate
  • +
  • host – database host address (defaults to UNIX socket if not provided)
  • +
  • port – connection port number (defaults to 5432 if not provided)
  • +
+

Any other connection parameter supported by the client library/server can +be passed either in the connection string or as keyword. See the +PostgreSQL documentation for a complete list of supported parameters. +Also note that the same parameters can be passed to the client library +using environment variables.

+

Using the connection_factory parameter a different class or +connections factory can be specified. It should be a callable object +taking a dsn argument. See Connection and cursor factories for +details.

+

Using async=1 an asynchronous connection will be created: see +Asynchronous support to know about advantages and limitations.

+

+Changed in version 2.4.3: any keyword argument is passed to the connection. Previously only the +basic parameters (plus sslmode) were supported as keywords.

+
+

DB API extension

+

The parameters connection_factory and async are Psycopg extensions +to the DB API 2.0.

+
+
+ +
+
+psycopg2.apilevel
+

String constant stating the supported DB API level. For psycopg2 is +2.0.

+
+ +
+
+psycopg2.threadsafety
+

Integer constant stating the level of thread safety the interface +supports. For psycopg2 is 2, i.e. threads can share the module +and the connection. See Thread and process safety for details.

+
+ +
+
+psycopg2.paramstyle
+

String constant stating the type of parameter marker formatting expected +by the interface. For psycopg2 is pyformat. See also +Passing parameters to SQL queries.

+
+ +
+

Exceptions

+

In compliance with the DB API 2.0, the module makes informations about errors +available through the following exceptions:

+
+
+exception psycopg2.Warning
+

Exception raised for important warnings like data truncations while +inserting, etc. It is a subclass of the Python StandardError.

+
+ +
+
+exception psycopg2.Error
+

Exception that is the base class of all other error exceptions. You can +use this to catch all errors with one single except statement. Warnings +are not considered errors and thus not use this class as base. It +is a subclass of the Python StandardError.

+
+
+pgerror
+

String representing the error message returned by the backend, +None if not available.

+
+ +
+
+pgcode
+

String representing the error code returned by the backend, None +if not available. The errorcodes module contains +symbolic constants representing PostgreSQL error codes.

+
+ +
>>> try:
+...     cur.execute("SELECT * FROM barf")
+... except Exception, e:
+...     pass
+
+>>> e.pgcode
+'42P01'
+>>> print e.pgerror
+ERROR:  relation "barf" does not exist
+LINE 1: SELECT * FROM barf
+                      ^
+
+
+
+
+cursor
+

The cursor the exception was raised from; None if not applicable.

+
+ +
+

DB API extension

+

The pgerror, pgcode, and cursor attributes +are Psycopg extensions.

+
+
+ +
+
+exception psycopg2.InterfaceError
+

Exception raised for errors that are related to the database interface +rather than the database itself. It is a subclass of Error.

+
+ +
+
+exception psycopg2.DatabaseError
+

Exception raised for errors that are related to the database. It is a +subclass of Error.

+
+ +
+
+exception psycopg2.DataError
+

Exception raised for errors that are due to problems with the processed +data like division by zero, numeric value out of range, etc. It is a +subclass of DatabaseError.

+
+ +
+
+exception psycopg2.OperationalError
+

Exception raised for errors that are related to the database’s operation +and not necessarily under the control of the programmer, e.g. an +unexpected disconnect occurs, the data source name is not found, a +transaction could not be processed, a memory allocation error occurred +during processing, etc. It is a subclass of DatabaseError.

+
+ +
+
+exception psycopg2.IntegrityError
+

Exception raised when the relational integrity of the database is +affected, e.g. a foreign key check fails. It is a subclass of +DatabaseError.

+
+ +
+
+exception psycopg2.InternalError
+

Exception raised when the database encounters an internal error, e.g. the +cursor is not valid anymore, the transaction is out of sync, etc. It is a +subclass of DatabaseError.

+
+ +
+
+exception psycopg2.ProgrammingError
+

Exception raised for programming errors, e.g. table not found or already +exists, syntax error in the SQL statement, wrong number of parameters +specified, etc. It is a subclass of DatabaseError.

+
+ +
+
+exception psycopg2.NotSupportedError
+

Exception raised in case a method or database API was used which is not +supported by the database, e.g. requesting a rollback() on a +connection that does not support transaction or has transactions turned +off. It is a subclass of DatabaseError.

+
+ +
+

DB API extension

+

Psycopg may raise a few other, more specialized, exceptions: currently +QueryCanceledError and +TransactionRollbackError are defined. These +exceptions are not exposed by the main psycopg2 module but are +made available by the extensions module. All the +additional exceptions are subclasses of standard DB API 2.0 exceptions, so +trapping them specifically is not required.

+
+

This is the exception inheritance layout:

+
+StandardError
+|__ Warning
+|__ Error
+    |__ InterfaceError
+    |__ DatabaseError
+        |__ DataError
+        |__ OperationalError
+        |   |__ psycopg2.extensions.QueryCanceledError
+        |   |__ psycopg2.extensions.TransactionRollbackError
+        |__ IntegrityError
+        |__ InternalError
+        |__ ProgrammingError
+        |__ NotSupportedError
+
+
+
+

Type Objects and Constructors

+
+

Note

+

This section is mostly copied verbatim from the DB API 2.0 +specification. While these objects are exposed in compliance to the +DB API, Psycopg offers very accurate tools to convert data between Python +and PostgreSQL formats. See Adapting new Python types to SQL syntax and +Type casting of SQL types into Python objects

+
+

Many databases need to have the input in a particular format for +binding to an operation’s input parameters. For example, if an +input is destined for a DATE column, then it must be bound to the +database in a particular string format. Similar problems exist +for “Row ID” columns or large binary items (e.g. blobs or RAW +columns). This presents problems for Python since the parameters +to the .execute*() method are untyped. When the database module +sees a Python string object, it doesn’t know if it should be bound +as a simple CHAR column, as a raw BINARY item, or as a DATE.

+

To overcome this problem, a module must provide the constructors +defined below to create objects that can hold special values. +When passed to the cursor methods, the module can then detect the +proper type of the input parameter and bind it accordingly.

+

A Cursor Object’s description attribute returns information about +each of the result columns of a query. The type_code must compare +equal to one of Type Objects defined below. Type Objects may be +equal to more than one type code (e.g. DATETIME could be equal to +the type codes for date, time and timestamp columns; see the +Implementation Hints below for details).

+

The module exports the following constructors and singletons:

+
+
+psycopg2.Date(year, month, day)
+

This function constructs an object holding a date value.

+
+ +
+
+psycopg2.Time(hour, minute, second)
+

This function constructs an object holding a time value.

+
+ +
+
+psycopg2.Timestamp(year, month, day, hour, minute, second)
+

This function constructs an object holding a time stamp value.

+
+ +
+
+psycopg2.DateFromTicks(ticks)
+

This function constructs an object holding a date value from the given +ticks value (number of seconds since the epoch; see the documentation of +the standard Python time module for details).

+
+ +
+
+psycopg2.TimeFromTicks(ticks)
+

This function constructs an object holding a time value from the given +ticks value (number of seconds since the epoch; see the documentation of +the standard Python time module for details).

+
+ +
+
+psycopg2.TimestampFromTicks(ticks)
+

This function constructs an object holding a time stamp value from the +given ticks value (number of seconds since the epoch; see the +documentation of the standard Python time module for details).

+
+ +
+
+psycopg2.Binary(string)
+

This function constructs an object capable of holding a binary (long) +string value.

+
+ +
+
+psycopg2.STRING
+

This type object is used to describe columns in a database that are +string-based (e.g. CHAR).

+
+ +
+
+psycopg2.BINARY
+

This type object is used to describe (long) binary columns in a database +(e.g. LONG, RAW, BLOBs).

+
+ +
+
+psycopg2.NUMBER
+

This type object is used to describe numeric columns in a database.

+
+ +
+
+psycopg2.DATETIME
+

This type object is used to describe date/time columns in a database.

+
+ +
+
+psycopg2.ROWID
+

This type object is used to describe the “Row ID” column in a database.

+
+ +
+
+ + +
+
+
+
+
+

Table Of Contents

+ + +

Previous topic

+

Basic module usage

+

Next topic

+

The connection class

+

This Page

+ + + +
+
+
+
+ + + + \ No newline at end of file Binary files /tmp/2HyIbqyseQ/psycopg2-2.0.13/doc/html/objects.inv and /tmp/gnadcqLbN0/psycopg2-2.4.5/doc/html/objects.inv differ diff -Nru psycopg2-2.0.13/doc/html/pool.html psycopg2-2.4.5/doc/html/pool.html --- psycopg2-2.0.13/doc/html/pool.html 1970-01-01 00:00:00.000000000 +0000 +++ psycopg2-2.4.5/doc/html/pool.html 2012-03-28 21:17:18.000000000 +0000 @@ -0,0 +1,190 @@ + + + + + + + + + + psycopg2.pool – Connections pooling — Psycopg 2.4.5 documentation + + + + + + + + + + + + + + + +
+
+
+
+ +
+

psycopg2.pool – Connections pooling

+

Creating new PostgreSQL connections can be an expensive operation. This +module offers a few pure Python classes implementing simple connection pooling +directly in the client application.

+
+
+class psycopg2.pool.AbstractConnectionPool(minconn, maxconn, *args, **kwargs)
+

Base class implementing generic key-based pooling code.

+

New minconn connections are created automatically. The pool will support +a maximum of about maxconn connections. *args and **kwargs are +passed to the connect() function.

+

The following methods are expected to be implemented by subclasses:

+
+
+getconn(key=None)
+

Get a free connection and assign it to key if not None.

+
+ +
+
+putconn(conn, key=None, close=False)
+

Put away a connection.

+

If close is True, discard the connection from the pool.

+
+ +
+
+closeall()
+

Close all the connections handled by the pool.

+

Note that all the connections are closed, including ones +eventually in use by the application.

+
+ +
+ +

The following classes are AbstractConnectionPool subclasses ready to +be used.

+
+
+class psycopg2.pool.SimpleConnectionPool(minconn, maxconn, *args, **kwargs)
+

A connection pool that can’t be shared across different threads.

+
+

Note

+

This pool class is useful only for single-threaded applications.

+
+
+ +
+
+class psycopg2.pool.ThreadedConnectionPool(minconn, maxconn, *args, **kwargs)
+

A connection pool that works with the threading module.

+
+

Note

+

This pool class can be safely used in multi-threaded applications.

+
+
+ +
+
+class psycopg2.pool.PersistentConnectionPool(minconn, maxconn, *args, **kwargs)
+

A pool that assigns persistent connections to different threads.

+

Note that this connection pool generates by itself the required keys +using the current thread id. This means that until a thread puts away +a connection it will always get the same connection object by successive +getconn() calls. This also means that a thread can’t use more than one +single connection from the pool.

+
+

Note

+

This pool class is mostly designed to interact with Zope and probably +not useful in generic applications.

+
+
+ +
+ + +
+
+
+
+
+

Previous topic

+

psycopg2.tztzinfo implementations for Psycopg 2

+

Next topic

+

psycopg2.extras – Miscellaneous goodies for Psycopg 2

+

This Page

+ + + +
+
+
+
+ + + + \ No newline at end of file diff -Nru psycopg2-2.0.13/doc/html/py-modindex.html psycopg2-2.4.5/doc/html/py-modindex.html --- psycopg2-2.0.13/doc/html/py-modindex.html 1970-01-01 00:00:00.000000000 +0000 +++ psycopg2-2.4.5/doc/html/py-modindex.html 2012-03-28 21:17:18.000000000 +0000 @@ -0,0 +1,136 @@ + + + + + + + + + + Python Module Index — Psycopg 2.4.5 documentation + + + + + + + + + + + + + + + + +
+
+
+
+ + +

Python Module Index

+ +
+ p +
+ + + + + + + + + + + + + + + + + + + + + + +
 
+ p
+ psycopg2 +
    + psycopg2.errorcodes +
    + psycopg2.extensions +
    + psycopg2.extras +
    + psycopg2.pool +
    + psycopg2.tz +
+ + +
+
+
+
+
+ + +
+
+
+
+ + + + \ No newline at end of file diff -Nru psycopg2-2.0.13/doc/html/search.html psycopg2-2.4.5/doc/html/search.html --- psycopg2-2.0.13/doc/html/search.html 1970-01-01 00:00:00.000000000 +0000 +++ psycopg2-2.4.5/doc/html/search.html 2012-03-28 21:17:18.000000000 +0000 @@ -0,0 +1,105 @@ + + + + + + + + + + Search — Psycopg 2.4.5 documentation + + + + + + + + + + + + + + + + + +
+
+
+
+ +

Search

+
+ +

+ Please activate JavaScript to enable the search + functionality. +

+
+

+ From here you can search these documents. Enter your search + words into the box below and click "search". Note that the search + function will automatically search for all of the words. Pages + containing fewer words won't appear in the result list. +

+
+ + + +
+ +
+ +
+ +
+
+
+
+
+
+
+
+
+ + + + \ No newline at end of file diff -Nru psycopg2-2.0.13/doc/html/searchindex.js psycopg2-2.4.5/doc/html/searchindex.js --- psycopg2-2.0.13/doc/html/searchindex.js 1970-01-01 00:00:00.000000000 +0000 +++ psycopg2-2.4.5/doc/html/searchindex.js 2012-03-28 21:17:18.000000000 +0000 @@ -0,0 +1 @@ +Search.setIndex({objects:{"":{cursor:[5,1,1,""],psycopg2:[4,0,1,""],connection:[3,1,1,""]},"psycopg2.extensions":{MXINTERVALARRAY:[8,3,1,""],TimeFromPy:[8,1,1,""],PYDATETIMEARRAY:[8,3,1,""],TRANSACTION_STATUS_UNKNOWN:[8,3,1,""],TRANSACTION_STATUS_INERROR:[8,3,1,""],new_type:[8,5,1,""],POLL_OK:[8,3,1,""],Xid:[8,1,1,""],MXINTERVAL:[8,3,1,""],INTERVALARRAY:[8,3,1,""],QueryCanceledError:[8,4,1,""],UNICODEARRAY:[8,3,1,""],ISOLATION_LEVEL_READ_UNCOMMITTED:[8,3,1,""],PYTIMEARRAY:[8,3,1,""],DECIMALARRAY:[8,3,1,""],new_array_type:[8,5,1,""],STATUS_PREPARED:[8,3,1,""],PYDATETIME:[8,3,1,""],ISOLATION_LEVEL_AUTOCOMMIT:[8,3,1,""],string_types:[8,3,1,""],FLOAT:[8,3,1,""],MXTIMEARRAY:[8,3,1,""],adapters:[8,3,1,""],STRINGARRAY:[8,3,1,""],IntervalFromMx:[8,1,1,""],encodings:[8,3,1,""],TIMEARRAY:[8,3,1,""],cursor:[8,1,1,""],MXDATEARRAY:[8,3,1,""],INTERVAL:[8,3,1,""],POLL_READ:[8,3,1,""],adapt:[8,5,1,""],UNICODE:[8,3,1,""],INTEGER:[8,3,1,""],INTEGERARRAY:[8,3,1,""],TRANSACTION_STATUS_INTRANS:[8,3,1,""],ISOLATION_LEVEL_READ_COMMITTED:[8,3,1,""],LONGINTEGER:[8,3,1,""],STATUS_BEGIN:[8,3,1,""],STATUS_READY:[8,3,1,""],POLL_WRITE:[8,3,1,""],connection:[8,1,1,""],TIME:[8,3,1,""],MXDATETIMEARRAY:[8,3,1,""],register_adapter:[8,5,1,""],Binary:[8,1,1,""],get_wait_callback:[8,5,1,""],POLL_ERROR:[8,3,1,""],MXTIME:[8,3,1,""],BOOLEANARRAY:[8,3,1,""],PYDATE:[8,3,1,""],FLOATARRAY:[8,3,1,""],PYDATEARRAY:[8,3,1,""],DateFromPy:[8,1,1,""],DECIMAL:[8,3,1,""],PYINTERVALARRAY:[8,3,1,""],DATETIMEARRAY:[8,3,1,""],ISQLQuote:[8,1,1,""],TimeFromMx:[8,1,1,""],TRANSACTION_STATUS_ACTIVE:[8,3,1,""],PYINTERVAL:[8,3,1,""],TransactionRollbackError:[8,4,1,""],DATEARRAY:[8,3,1,""],ROWIDARRAY:[8,3,1,""],TimestampFromMx:[8,1,1,""],BOOLEAN:[8,3,1,""],Notify:[8,1,1,""],AsIs:[8,1,1,""],MXDATE:[8,3,1,""],PYTIME:[8,3,1,""],Float:[8,1,1,""],ISOLATION_LEVEL_REPEATABLE_READ:[8,3,1,""],IntervalFromPy:[8,1,1,""],DATE:[8,3,1,""],TimestampFromPy:[8,1,1,""],STATUS_IN_TRANSACTION:[8,3,1,""],BINARYARRAY:[8,3,1,""],TRANSACTION_STATUS_IDLE:[8,3,1,""],MXDATETIME:[8,3,1,""],lobject:[8,1,1,""],SQL_IN:[8,1,1,""],LONGINTEGERARRAY:[8,3,1,""],QuotedString:[8,1,1,""],set_wait_callback:[8,5,1,""],ISOLATION_LEVEL_SERIALIZABLE:[8,3,1,""],DateFromMx:[8,1,1,""],register_type:[8,5,1,""],Boolean:[8,1,1,""]},"psycopg2.extensions.Xid":{prepared:[8,6,1,""],database:[8,6,1,""],gtrid:[8,6,1,""],owner:[8,6,1,""],format_id:[8,6,1,""],from_string:[8,2,1,""],bqual:[8,6,1,""]},"psycopg2.extras.CompositeCaster":{attnames:[7,6,1,""],name:[7,6,1,""],oid:[7,6,1,""],atttypes:[7,6,1,""],array_oid:[7,6,1,""],type:[7,6,1,""]},"psycopg2.extras":{register_tstz_w_secs:[7,5,1,""],register_inet:[7,5,1,""],register_uuid:[7,5,1,""],RealDictRow:[7,1,1,""],LoggingConnection:[7,1,1,""],CompositeCaster:[7,1,1,""],register_composite:[7,5,1,""],NamedTupleCursor:[7,1,1,""],DictRow:[7,1,1,""],LoggingCursor:[7,1,1,""],wait_select:[7,5,1,""],MinTimeLoggingConnection:[7,1,1,""],DictConnection:[7,1,1,""],RealDictCursor:[7,1,1,""],UUID_adapter:[7,1,1,""],RealDictConnection:[7,1,1,""],MinTimeLoggingCursor:[7,1,1,""],register_hstore:[7,5,1,""],NamedTupleConnection:[7,1,1,""],DictCursor:[7,1,1,""],Inet:[7,1,1,""]},"psycopg2.tz":{LocalTimezone:[1,1,1,""],FixedOffsetTimezone:[1,1,1,""]},psycopg2:{Binary:[4,5,1,""],InternalError:[4,4,1,""],Error:[4,4,1,""],NUMBER:[4,3,1,""],errorcodes:[6,0,1,""],connect:[4,5,1,""],threadsafety:[4,3,1,""],tz:[1,0,1,""],TimeFromTicks:[4,5,1,""],Timestamp:[4,5,1,""],Warning:[4,4,1,""],Date:[4,5,1,""],ProgrammingError:[4,4,1,""],TimestampFromTicks:[4,5,1,""],DataError:[4,4,1,""],STRING:[4,3,1,""],NotSupportedError:[4,4,1,""],IntegrityError:[4,4,1,""],DATETIME:[4,3,1,""],Time:[4,5,1,""],pool:[11,0,1,""],DateFromTicks:[4,5,1,""],BINARY:[4,3,1,""],InterfaceError:[4,4,1,""],paramstyle:[4,3,1,""],extras:[7,0,1,""],extensions:[8,0,1,""],OperationalError:[4,4,1,""],ROWID:[4,3,1,""],apilevel:[4,3,1,""],DatabaseError:[4,4,1,""]},"psycopg2.pool":{SimpleConnectionPool:[11,1,1,""],PersistentConnectionPool:[11,1,1,""],AbstractConnectionPool:[11,1,1,""],ThreadedConnectionPool:[11,1,1,""]},"psycopg2.extensions.ISQLQuote":{"_wrapped":[8,6,1,""],getquoted:[8,2,1,""],prepare:[8,2,1,""]},"psycopg2.pool.AbstractConnectionPool":{getconn:[11,2,1,""],closeall:[11,2,1,""],putconn:[11,2,1,""]},cursor:{fetchall:[5,2,1,""],rownumber:[5,6,1,""],query:[5,6,1,""],close:[5,2,1,""],withhold:[5,6,1,""],fetchone:[5,2,1,""],copy_expert:[5,2,1,""],tzinfo_factory:[5,6,1,""],setoutputsize:[5,2,1,""],closed:[5,6,1,""],copy_to:[5,2,1,""],callproc:[5,2,1,""],description:[5,6,1,""],setinputsizes:[5,2,1,""],arraysize:[5,6,1,""],fetchmany:[5,2,1,""],itersize:[5,6,1,""],statusmessage:[5,6,1,""],mogrify:[5,2,1,""],execute:[5,2,1,""],executemany:[5,2,1,""],name:[5,6,1,""],nextset:[5,2,1,""],copy_from:[5,2,1,""],cast:[5,2,1,""],connection:[5,6,1,""],lastrowid:[5,6,1,""],rowcount:[5,6,1,""],scroll:[5,2,1,""]},connection:{encoding:[3,6,1,""],isolation_level:[3,6,1,""],set_client_encoding:[3,2,1,""],tpc_commit:[3,2,1,""],cancel:[3,2,1,""],close:[3,2,1,""],poll:[3,2,1,""],autocommit:[3,6,1,""],set_isolation_level:[3,2,1,""],xid:[3,2,1,""],isexecuting:[3,2,1,""],dsn:[3,6,1,""],closed:[3,6,1,""],notices:[3,6,1,""],protocol_version:[3,6,1,""],status:[3,6,1,""],rollback:[3,2,1,""],tpc_rollback:[3,2,1,""],lobject:[3,2,1,""],set_session:[3,2,1,""],get_transaction_status:[3,2,1,""],tpc_prepare:[3,2,1,""],commit:[3,2,1,""],get_parameter_status:[3,2,1,""],server_version:[3,6,1,""],reset:[3,2,1,""],fileno:[3,2,1,""],get_backend_pid:[3,2,1,""],tpc_recover:[3,2,1,""],cursor:[3,2,1,""],tpc_begin:[3,2,1,""],async:[3,6,1,""],notifies:[3,6,1,""]},"psycopg2.Error":{cursor:[4,6,1,""],pgcode:[4,6,1,""],pgerror:[4,6,1,""]},"psycopg2.extensions.Binary":{getquoted:[8,2,1,""]},"psycopg2.errorcodes":{lookup:[6,5,1,""]},"psycopg2.extensions.AsIs":{getquoted:[8,2,1,""]},"psycopg2.extensions.lobject":{truncate:[8,2,1,""],read:[8,2,1,""],oid:[8,6,1,""],write:[8,2,1,""],"export":[8,2,1,""],mode:[8,6,1,""],closed:[8,6,1,""],close:[8,2,1,""],unlink:[8,2,1,""],seek:[8,2,1,""],tell:[8,2,1,""]},"psycopg2.extensions.QuotedString":{getquoted:[8,2,1,""]},"psycopg2.extras.LoggingConnection":{filter:[7,2,1,""],initialize:[7,2,1,""]},"psycopg2.extensions.Notify":{pid:[8,6,1,""],payload:[8,6,1,""],channel:[8,6,1,""]}},terms:{untrust:[10,3],sleep:9,forget:10,whose:[8,9],accur:4,display_s:5,pprint:3,under:4,mxtimearrai:8,digit:[5,3],everi:[10,3,8,9],rel:5,a0eebc99:7,null_ok:5,affect:[5,4],factori:[0,4,5,3,8,10,9],"10l":10,direct:2,xf9:10,second:[2,4,7,8,10,9],xf2:10,blue:7,eventlet:[8,9],libpq:[0,2,5,3,8,10,9],asia:10,"new":[0,2,4,5,3,8,9,10,11],typnam:9,abov:[10,3,9],internalerror:4,never:[10,2,8],here:[10,2],met:9,path:2,anymor:[2,4],precis:5,datetim:[10,0,1,8,4],parlanc:10,unix:4,total:5,middlewar:3,describ:[5,4],would:[5,10,8,9],call:[2,5,3,7,8,9,10,11],type:[0,2,4,5,3,7,8,10,9],until:[2,3,7,8,9,10,11],fastcgi:10,realdictrow:7,unescap:10,relat:[5,10,3,8,4],notic:3,warn:[10,3,8,9,4],tpc_recov:[10,3,8],hold:[1,4,3,7,8,10],unpack:[3,8],must:[2,4,5,3,7,8,10,9],join:9,bqual:[3,8],some_t:10,setup:8,work:[2,8,9,11],isqlquot:[8,9],give:9,"08t01":10,indic:[5,0,6,7],want:[10,3,7,8,9],declar:10,register_inet:7,enc:3,end:[10,3,2,9],quot:[10,3,7,8],ordinari:10,how:[10,7,8,9],recoveri:[10,3],env:2,widespread:10,updat:[5,0,2],recogn:[10,6],timedelta:10,after:[2,5,3,7,8,10,9],befor:[2,3,7,8,10,9],wrong:[10,2,4],attempt:[5,3,9],third:9,imposs:7,perform:[5,10,3,8,9],maintain:9,green:[10,9],first:[2,3,6,8,10,9],order:[10,8,9],oper:[4,5,3,8,9,10,11],macaddr:8,frontend:3,over:[5,2],fall:10,becaus:[2,9],caster:[7,8],flexibl:[5,0,8],aargh:6,uuid:7,wsgipythonegg:2,fix:1,"__class__":9,better:[7,9],persist:[10,11],them:[2,4,5,3,8,10,9],thei:[2,5,7,8,10,9],proce:[3,9],safe:[0,2,5,3,8,9,10,11],"break":9,interrupt:3,mytabl:9,foo_pkei:3,timeout:[8,9],each:[5,3,2,8,4],side:[5,0,3,10],mean:[5,10,3,2,11],a_str:10,binaryarrai:8,typecast:[2,5,7,8,10,9],dsn:[3,8,9,4],adapt:[0,2,4,7,8,10,9],got:9,copy_to:[5,10],pydat:8,free:[2,11],standard:[3,4,5,6,7,8,10,9],small:[2,1],filter:7,refcursor:10,unabl:2,subtl:10,onto:10,rang:4,isolation_level:[3,8],wast:9,system:[0,2,5,3,8,10,9],hook:8,alreadi:[10,3,7,8,4],messag:[2,3,4,5,6,8,9],wasn:5,primari:[10,3],xid:[10,3,8],tpc_:3,too:[10,2,8],similarli:9,class_syntax_error_or_access_rule_viol:6,listen:[8,9],namespac:9,tool:[10,4],target:[5,10],keyword:4,provid:[2,4,5,3,7,8,10,9],nextset:5,project:9,minut:[10,1,4],psycogreen:9,loggingconnect:7,close:[2,5,3,8,10,11],raw:4,manner:[8,9],seen:5,seem:2,seek:8,plpgsql:10,typarrai:8,latter:[5,3],rome:10,register_adapt:[8,9],even:[5,10,2],though:[10,3],object:[0,2,4,5,3,7,8,9,10,11],register_typ:[5,10,2,8,9],regular:[5,3,7,8,10,9],addict:7,mxdatetim:8,phase:[10,0,3,8],don:[10,3],doc:3,doe:[2,3,4,5,6,7,8,9],logobj:7,neg:[5,10,8],wait_select:[7,8,9],syntax:[0,2,4,5,8,10,9],protocol:[10,0,3,8,9],involv:5,absolut:5,submit:5,layout:4,transactionrollbackerror:[8,4],explain:3,configur:[10,3,8],new_array_typ:[2,8,9],tzinfo:[5,0,1,10],transaction_status_unknown:8,deal_with_it:5,report:[3,8],bar:[5,10,2],hello:9,method:[1,2,4,5,3,7,8,9,10,11],isexecut:[3,9],bad:[8,9],steal:10,num:[5,10,7],result:[0,2,4,5,3,7,8,10,9],respons:[10,7,9],fail:[10,2,8,4],best:[5,0,2],subject:6,databas:[0,2,3,4,5,6,7,8,10,9],timestampfromtick:4,"__conform__":9,integer_datetim:3,mvcc:[0,1,2,3,4,5,6,7,8,9,10,11],awai:11,approach:10,pyintervalarrai:8,attribut:[1,3,4,5,6,7,8,10,9],accord:[3,5,6,7,8,10,9],extend:[5,10,8],pqparameterstatu:3,extens:[0,2,4,5,3,7,8,10,9],easi:[5,2,9],howev:[10,8],against:[5,10,8],quotedstr:8,col:10,con:8,excess:3,assum:[5,10],three:[10,6,2],been:[1,2,3,5,6,7,8,9],much:9,interest:9,basic:[10,0,8,9,4],register_composit:[10,7],isolation_level_autocommit:[3,8,9],ani:[0,2,4,5,3,8,10,9],multithread:[5,9],craft:10,child:2,"catch":[5,4],emploi:10,east:1,get_backend_pid:[3,8],properti:10,printabl:8,kwarg:[7,11],conn:[4,3,7,8,9,10,11],iterfac:7,incorrectli:3,receiv:[10,3,2,8,9],suggest:2,make:[0,2,3,4,5,6,7,8,10,9],transpar:9,complex:8,complet:[10,8,9,4],lobject_factori:3,hang:10,evil:7,hand:10,rais:[2,3,4,5,6,7,8,10,9],modwsgi:2,redefin:5,kept:3,thu:[10,6,9,4],dada:5,inherit:4,client:[0,2,4,5,3,8,9,10,11],thi:[1,2,3,4,5,6,7,8,9,10,11],programm:[9,4],left:[5,10,2],identifi:[10,3,8],just:[7,8],pqconnectstart:9,memoryview:10,yet:[5,8],languag:[10,0],previous:[1,4,5,3,8,10,9],awaken:9,expos:[4,5,3,8,10,9],had:[5,10],seq_of_paramet:5,els:[2,9],save:[2,1],applic:[0,4,5,6,8,9,11],mayb:9,specif:[3,4,5,6,8,10,9],manual:[3,7,9],base_cast:8,underli:3,old:2,namedtupleconnect:7,interv:[10,8],intern:[8,4],autocommit:[10,3,2,9],type_cod:[5,4],cooper:9,subclass:[0,3,4,6,7,8,9,11],overcom:4,condit:[10,6,2,8,3],foo:[5,10,3,2],operationalerror:[3,8,9,4],plu:[8,4],"6bb9bd380a11":7,unsaf:3,postgresql:[0,2,3,4,5,6,7,8,9,10,11],unpars:[10,2,8],internal_s:5,commit:[0,2,5,3,8,10,9],produc:[5,10,6,8,9],longinteg:8,"float":[10,0,2,8,9],encod:[5,10,3,8],bound:[5,10,4],down:9,transaction_status_act:8,contrib:7,accordingli:4,jdbc:[10,3],deal:[10,7,8],wai:[10,7,9],support:[0,2,4,5,3,7,8,9,10,11],transform:7,"class":[0,1,2,3,4,5,6,7,8,9,10,11],avail:[0,3,4,5,6,7,8,10,9],reli:[10,3,8,9],fraction:[5,0,7],gin:7,fork:[10,2],form:[10,3,1,9],offer:[10,3,9,11,4],datestyl:3,paramstyl:4,sqlalchemi:9,"true":[5,3,7,11,10,9],reset:3,attr:7,arrays:5,maximum:11,tell:8,emit:[10,6],featur:[10,0],classic:10,log_min_duration_stat:3,exist:[10,4],localtimezon:1,check:[7,9,4],readonli:3,when:[2,4,5,3,7,8,10,9],test:[4,5,6,7,10,9],roll:3,notif:[0,3,8,9],intend:3,determin:5,stringio:5,iters:[5,10],intens:5,consid:[3,4],sql:[0,2,4,5,6,7,8,10,9],format_id:[3,8],uniformli:10,longer:10,furthermor:3,withhold:[5,10,3],ignor:2,time:[0,2,4,5,3,7,8,10,9],push:3,lastrowid:5,backward:[3,8],skip:2,global:[10,3,7,8],ouch:6,invent:6,signific:5,computation:5,row:[5,7,4],decid:5,depend:[5,2,8,9],zone:[5,0,7,10],decim:[0,2,5,3,8,10],readabl:[5,9],x08:8,curnam:10,mintimeloggingconnect:7,set_client_encod:[10,3,9],x00:8,sourc:[10,3,9,4],setoutputs:5,string:[0,2,4,5,3,7,8,10,9],exact:1,level:[0,2,4,5,3,8,10],did:[5,3],restor:3,x0f:8,iter:[5,10],item:[2,4,5,3,7,8],round:10,sign:1,btree:7,port:4,pytim:8,appear:8,current:[2,4,5,3,7,8,9,10,11],sinc:[10,3,8,4],fetchal:[5,10],pose:2,gener:[2,3,7,8,10,11],unauthor:10,gtrid:[3,8],explicitli:5,address:[7,8,4],wait:[3,7,8,9],box:[0,7],pqfsize:5,dictcursor:7,behav:[10,3,8],extrem:[10,7],semant:9,extra:[10,0,3,7,9],proportion:10,modul:[0,1,3,4,5,6,7,8,9,10,11],visibl:5,marker:4,instal:[10,3,2,7],cursor_factori:[3,7,8,9],memori:[10,2,1,4],compositecast:7,live:[10,3,2],msg:7,scope:[10,7,8,9],integrityerror:4,status_in_transact:[3,8],examin:10,obj:8,prepar:[5,10,3,8,9],timestamptz:10,descriptor:[3,8,9],can:[0,1,2,3,4,5,6,7,8,9,10,11],heart:7,a_dat:10,encapsul:[10,3],stream:10,backslash:8,topic:[0,9],abort:[10,2],occur:[6,4],alwai:[2,5,8,11,10,9],multipl:5,"42p01":[6,4],write:[2,5,3,8,10,9],bytea_output:[10,2],pure:11,server_encod:3,map:[0,5,7,8,10,9],usabl:[10,3],new_typ:[2,8,9],mai:[2,4,5,3,7,8,10,9],data:[0,2,4,5,3,7,8,10,9],stress:10,practic:[10,0,2],datefromtick:4,explicit:[3,2],inform:[5,2,8,9,4],"switch":[3,8,9],pg_namespac:9,callabl:4,still:[6,7,3,9],psycopg_display_s:5,disconnect:4,monitor:8,lo_export:[10,8],platform:1,main:[10,0,4],non:[10,3,8,9],standarderror:4,savepoint:2,initi:[3,2,7],therebi:9,now:[5,10,3,7],nor:[10,3],introduct:8,name:[1,2,3,4,5,6,7,8,10,9],revert:[3,8],separ:[5,10,9],compil:[5,0,2,8],set_sess:[10,3,9],replac:[5,8],continu:10,bb6d:7,wrap:[5,10,7,8,9],year:4,happen:[3,9],dispos:2,shown:8,tpc_rollback:[10,3,8],log_stat:3,formula:2,correct:[10,2,7],earlier:2,client_encod:3,dictconnect:7,"byte":[0,5,3,8,10,9],"_psycopg":[2,8,7],pg_type:[5,8,9],card:7,care:10,synchron:9,recov:[3,2,8],turn:4,place:7,think:2,frequent:[0,2],lambda:2,origin:[7,8],directli:[1,9,11],carri:9,onc:[10,8],arrai:[0,2,7,8,10,9],"long":[10,0,3,2,4],oppos:2,open:[10,3,2,8,7],size:[5,2,8],given:[5,3,8,4],returnd:8,convent:6,necessarili:[8,4],conveni:[10,9],memo:10,copi:[5,0,9,4,10],specifi:[4,5,3,7,8,10,9],enclos:8,mostli:[5,0,11,4,10],than:[3,4,5,6,7,8,9,10,11],png:10,serv:10,wide:10,were:[10,3,8,4],posit:[5,10,2,8],pre:[1,3,4,5,6,7,8,11],sai:2,argument:[1,2,4,5,3,7,8,10,9],timestampfrompi:8,maxconn:11,dbapi:3,advic:5,destroi:[10,0],note:[2,3,4,5,6,7,8,9,10,11],take:[2,8,9,4],advis:9,environ:[9,4],noth:[5,3,7,8],channel:[3,8,9],begin:[10,3,9],sure:[10,2,7],normal:[2,7],buffer:[5,10],pytimearrai:8,reffunc:10,pair:7,later:8,runtim:[2,8],serializ:[3,8],show:[10,3],serialis:8,concurr:[0,3,2,8,9],composit:[10,0,7],register_tstz_w_sec:[10,7],onli:[2,4,5,3,7,8,9,10,11],slow:2,transact:[0,2,4,5,3,8,10,9],activ:10,state:[5,8,9,4],dict:7,overwritten:7,callproc:[5,10,9],mypic:10,get:[5,2,9,11],mintim:7,repr:10,soon:[10,3,2],ssl:2,cannot:[5,2],requir:[2,4,3,7,8,9,10,11],fileno:[3,8,9],tfoo:5,borrow:6,where:[2,5,7,8,10,9],isolation_level_read_uncommit:8,kernel:9,atabl:9,getquot:[7,8,9],mxtime:8,reserv:5,detect:[8,4],enough:8,unicodearrai:[10,2,8],between:[0,3,4,6,10,9],"import":[2,4,5,3,7,8,10,9],across:[6,2,11,7],realdictconnect:7,come:[10,9],inconsist:8,improv:[7,1],among:3,overview:[5,3,8],inspir:[10,8],pop:9,cancel:[3,8],poll:[0,3,8,9],mxintervalarrai:8,"100kb":10,invers:8,closeal:11,sslmode:4,timearrai:8,gist:7,those:8,"case":[4,5,3,8,10,9],interoper:10,trick:2,cast:[0,2,4,5,7,8,10,9],invok:[5,3,8,9],"na\u00efv":10,apilevel:4,datetimearrai:8,advantag:[2,9,4],stdout:5,destin:4,"__init__":[1,9],point_oid:9,develop:2,harsh:2,author:[10,3,9],same:[1,2,4,5,3,8,9,10,11],binari:[10,0,2,8,4],epoch:4,html:3,document:[1,2,3,4,5,6,8,10,9],apoint:9,lo_trunc:[2,8],finish:[3,8],utf8:10,nest:[2,7],oid:[2,5,3,7,8,9],driver:[10,3],companion:7,capabl:4,mani:[0,4,5,3,7,8,10,9],appropri:10,copy_from:[5,10],without:[10,3,9],timefrommx:8,deferr:3,model:[10,8],execut:[2,3,4,5,6,7,8,10,9],set_isolation_level:[10,3,8,9],europ:10,miscellan:[0,7],hint:4,except:[0,2,3,4,5,6,8,10,9],param:4,blob:[10,4],real:[7,9],read:[2,5,3,8,10,9],vacuum:[10,3,8],integ:[2,4,3,7,8,10],server:[0,2,3,4,5,6,7,8,10,9],getconn:11,either:[4,5,3,7,8,10,9],output:[5,7,9],manag:3,what:[5,6,2,3],desider:[10,3],rowid:[8,4],egenix:10,statusmessag:5,sqlstate:6,inject:[10,7],datefrompi:8,refer:[5,10,9],inizi:7,nspname:9,"__name__":9,timefromtick:4,greatli:7,realdictcursor:7,effici:[10,0,7,8],your:[5,10,2,7],complianc:[5,8,4],hex:[10,2],floatarrai:8,overwrit:7,start:[10,3,2,8,9],interfac:[5,10,9,4],lot:[0,2],verbatim:4,tupl:[0,2,5,3,7,8,10],pqbackendpid:3,gori:2,tripl:3,pqgetlength:5,possibl:[2,5,3,7,8,10,9],"default":[1,2,4,5,3,7,8,10],embed:7,deadlock:[8,9],expect:[4,5,7,8,10,11],pgbouncer:3,decreas:10,file:[2,5,3,7,8,10,9],proport:2,fill:10,denot:6,longintegerarrai:8,tight:2,valid:[10,3,8,4],writabl:[5,2,9],geometr:9,you:[2,4,5,3,7,8,10,9],nt_cur:7,sequenc:[5,10,3,2,7],symbol:[6,2,3,4],pool:[0,2,9,11],network:5,directori:[2,7],descript:[5,8,9,4],threadedconnectionpool:11,potenti:3,escap:[10,2,8],cpu:9,represent:[2,5,7,8,10,9],all:[2,3,4,5,6,7,8,9,10,11],consider:5,month:4,bytearrai:10,follow:[2,3,4,5,6,8,9,10,11],content:[5,0,8,4,10],program:[0,2,4,3,10,9],liter:[1,3,4,5,6,7,8,10,11],fals:[5,10,3,7,11],faq:8,mechan:[10,8,9],failur:8,veri:[2,4],pg_catalog:9,list:[0,2,4,5,3,7,8,10,9],tpc_begin:[10,3],default_transaction_:[3,9],sync:4,zero:4,postgr:[10,4],design:[0,9,11],pass:[0,1,2,3,4,5,6,7,8,9,10,11],further:[10,3,2,9],whenc:8,deleg:9,abc:[5,10,7],sub:[7,9],section:4,abl:[10,6,8],version:[2,3,5,6,7,8,10,9],"public":7,querycancelederror:[3,8,4],full:0,behaviour:8,shouldn:[10,3],poll_ok:[3,8,9],modifi:[5,7,9],invoc:3,valu:[0,2,3,4,5,6,7,8,10,9],search:[0,7],putconn:11,another_d:10,prior:3,amount:10,action:[10,3],introductori:3,n74:5,via:[5,10,9],put:11,inappropri:3,establish:[8,9],card_back:7,select:[2,3,4,5,6,7,8,10,9],distinct:8,regist:[2,5,7,8,10,9],two:[0,1,2,3,5,6,8,10,9],poll_read:[8,9],tzinfo_factori:[5,1],taken:[5,10,3],minor:3,more:[0,4,5,3,7,9,10,11],desir:[7,8],particular:[10,4],known:[10,3,2,9,7],cach:[2,1],psql:9,none:[1,2,4,5,3,7,8,9,10,11],hour:[10,4],attnam:7,dev:2,mogrifi:[5,10,7,9],remain:[5,10,3,8],del:[5,10,3],pyinterv:8,def:[5,10,7,9],registr:[7,9],share:[0,2,4,3,9,10,11],accept:[10,3,8,9],minimum:8,huge:10,cours:9,secur:0,sql_in:8,divis:4,datefrommx:8,dec2float:2,csv:5,simpl:[4,3,8,11,10,9],register_uuid:[7,9],resourc:[10,3,2,8,9],catalog:[8,9],unlink:8,associ:5,hstore:[10,0,7],roundtrip:[5,10],caus:[3,8],callback:[7,8,9],egg:2,help:7,major:3,regtyp:7,held:[10,3],through:[5,8,9,4],paramet:[0,2,4,5,3,7,8,10,9],style:[10,9],overhead:[5,10,2],psycopg2:[0,1,2,3,4,5,6,7,8,9,10,11],dbname:[10,4],feel:2,pend:3,pgerror:4,finer:3,wouldn:3,good:2,"return":[1,2,3,4,5,6,7,8,10,9],timestamp:[5,10,7,8,4],pollut:9,framework:9,from_str:8,eventu:[10,3,11],dictrow:7,authent:4,easili:10,found:[1,2,4,5,6,7,8],unicod:[0,2,5,3,7,8,10],truncat:[8,4],idea:1,procedur:5,realli:7,connect:[0,2,4,5,3,7,8,9,10,11],event:2,safeti:[0,2,4,5,3,10],publish:10,payload:[3,8,9],transaction_status_idl:8,print:[5,10,9,4],file_nam:8,qualifi:[10,8],asp:2,uncommit:[3,8],advanc:[0,9],xe0:10,reason:[10,3],base:[4,5,7,8,11,9],ask:[0,2],asi:[8,9],thread:[0,2,4,5,3,9,10,11],undefined_t:6,lifetim:[5,10],assign:[10,6,3,11],aconn:9,notifi:[3,8,9],get_wait_callback:8,estabilish:3,exchang:10,number:[0,2,4,5,3,8,10],placehold:[5,10,2],done:5,construct:4,stabl:8,miss:2,differ:[1,4,5,3,7,8,9,10,11],script:[10,3,7,9],interact:[10,9,11],least:[10,2],stori:10,namedtupl:[5,0,7,10],statement:[5,3,2,4],colmun:7,store:[5,10,7,8],schema:7,option:[5,2,8,9,7],loggingcursor:[7,9],part:[5,3,2],pars:[10,2,8,9],kind:[10,2],grep:2,whenev:[5,3,8,9],remov:8,str:[2,3,7,8,10,9],session_author:3,packag:2,"null":[5,10,8,9],array_oid:7,built:[0,8],equival:8,self:9,also:[0,1,2,3,4,5,6,7,8,9,10,11],build:9,default_transaction_deferr:3,distribut:[10,0,7],interpol:10,most:[5,10],atttyp:7,addr:7,tpc:3,exc:[5,9],persistentconnectionpool:11,destruct:10,alphanumer:3,session:[2,4,5,3,8,10,9],fine:2,find:[10,2,8,7],access:[10,0,3,7,8],indexerror:5,xec:10,unus:[5,3,2],namedtuplecursor:7,express:9,stringarrai:8,acur:9,common:10,bytea:[10,2],set:[1,2,4,5,3,7,8,10,9],typnamespac:9,sep:5,see:[2,3,4,5,6,7,8,10,9],arg:[7,9,11],simpleconnectionpool:11,disadvantag:2,analog:7,someth:[10,9],particip:[3,8,9],altern:[10,2,7],signatur:8,isolation_level_serializ:8,mxdatetimearrai:8,isol:[5,0,3,8,10],solv:2,popul:[3,8],both:[5,0,3,7,10],last:[5,6,3],foreign:4,context:[5,10],whole:[10,2],standard_conforming_str:[3,8],simpli:9,point:[5,10,3,8,9],instanti:3,schedul:9,threadsafeti:4,transaction_status_inerror:8,header:5,littl:[2,7],backend:[2,4,5,3,8,10],java:10,stamp:4,intervalarrai:8,due:[5,6,8,4],empti:[5,10,8,9],implicit:[10,3],secret:4,xac:10,strategi:9,xa8:10,strictest:8,look:[2,3,6,7,10,9],xa0:10,xa4:10,histor:[10,7],"while":[10,6,8,9,4],executemani:[5,10,9],behavior:7,error:[0,2,3,4,6,8,10,9],fun:8,loop:[5,2,9],readi:[3,8,9,11],itself:[5,9,11,4],limit:[10,7,8,9,4],coroutin:[0,3,7,8,10,9],poll_error:8,belong:[3,8],decod:[5,10,3,8],zope:[0,11],mod_wsgi:2,x82:10,optim:5,wherea:10,user:[2,4,5,8,10,9],robust:10,typic:7,task:8,older:[10,3,2,8],entri:[5,10,2,8],string_typ:8,pickl:1,conn_or_cur:7,expens:11,calcutta:10,"_wrap":8,programmingerror:[4,5,3,7,8,10],propos:[10,9],explan:3,abstractconnectionpool:11,ldd:2,tick:4,cur:[2,3,4,5,6,7,8,10,9],commect:5,recurs:8,new_fil:[3,8],input:[5,10,4],subsequ:10,dataerror:4,varchar:10,format:[10,2,8,4],bit:[10,8],characterist:3,docutil:[1,3,4,5,6,7,8,11],status_prepar:8,collect:[5,7],"boolean":[5,0,8,10],encount:[2,4],often:[10,8],creation:1,some:[3,6,7,8,10,9],back:[10,3,7],server_vers:3,scale:5,pep:[8,9],per:[5,10,3,9],concaten:10,retri:8,larg:[0,4,5,3,8,10,9],previou:[2,5,3,7,8,10,9],run:[2,5,3,8,10,9],python_egg_cach:2,viabl:9,status_readi:[3,8],constraint:3,my_uuid:7,rowcount:5,idl:[10,3,2,8],default_transaction_isol:3,goodi:[0,7],block:[3,2,8,9],within:[6,8,3],tpc_commit:[10,3,8],protocol_vers:3,chang:[1,3,5,6,7,8,10,9],span:[1,3,4,5,6,7,8,11],question:[0,2],textual:[5,6],custom:[2,9],pg_sleep:9,includ:[0,3,5,6,8,11],"\u00e0\u00e8\u00ec\u00f2\u00f9":10,suit:7,forward:[5,3],translat:10,line:[10,4],info:9,utc:[10,7,1],utf:2,consist:8,caller:9,readlin:[5,10],similar:[5,7,9,4],constant:[0,3,4,6,8,10],parser:2,gunpoint:10,doesn:[2,4,7,8,10,9],repres:[3,4,5,6,7,8,10],"char":[8,4],xb9:10,invalid:3,xb2:10,codec:[10,8],pg_config:[2,8],cur2:10,cur1:10,isolation_level_read_commit:8,register_hstor:[10,7],algorithm:3,rather:[5,6,8,3,4],reansact:8,errorcod:[0,6,4],scroll:[5,10],code:[0,4,5,6,8,9,10,11],queri:[0,2,4,5,3,7,8,10,9],friendli:[0,9],send:[10,2,8,9],sent:[5,10,3,8,9],rollback:[2,4,5,3,8,10,9],rownumb:5,untouch:5,implicitli:[3,7,9],relev:[2,8],recip:10,magic:2,button:3,fewer:5,"try":[2,3,4,5,6,10,9],dealt:5,databaseerror:4,pleas:[10,9],uniqu:7,download:7,append:[5,10,3],compat:[3,7,8,9],index:[5,0,3,2,7],compar:[1,4],resembl:10,get_parameter_statu:3,mxinterv:8,timefrompi:8,pydatetimearrai:8,decimalarrai:8,cast_point:9,usag:[10,0,8],len:8,bodi:5,let:10,becom:9,miser:10,convert:[2,4,5,3,7,8,10,9],convers:[0,2,7,8,10,9],fetch:[5,10,2,7],an_int:10,implement:[0,1,4,5,7,8,9,10,11],nearest:10,appli:[10,3],nonblock:9,api:[0,1,2,3,4,5,6,7,8,9,10,11],"9c0b":7,intervalfrommx:8,from:[0,1,2,3,4,5,6,7,8,9,10,11],commun:[10,0,3,8,9],doubl:[10,8],upgrad:2,next:[5,3],few:[2,4,5,7,8,9,10,11],inet:[7,9],retriev:[5,10,7,9],alia:[10,3,8],mxdate:8,control:[0,4,3,8,10,9],malform:10,process:[0,2,4,5,3,8,10,9],lock:[10,3,9],tab:5,serial:[10,3,8],default_transaction_read_onli:3,instead:[2,5,7,8,10,9],textiobas:5,xc3:10,alloc:[10,4],bind:[5,10,9,4],element:[10,7],issu:[2,5,3,8,10,9],allow:[1,5,3,7,8,10,9],elif:9,move:10,comma:[10,2],pg_prepared_xact:3,copy_expert:[5,10],bunch:2,reilli:[10,8],fixedoffsettimezon:[10,1],therefor:9,recept:[8,9],python:[0,1,2,4,5,3,7,8,9,10,11],get_transaction_statu:[3,8],spell:9,dai:[10,4],multiprocess:[10,2],somewher:7,dict_cur:7,zpsycopgda:0,trap:[8,4],foo_id_seq:3,chunk:[2,8],postgrespollingstatustyp:8,consum:10,"static":8,integerarrai:8,our:2,datearrai:8,patch:9,special:[10,7,8,4],out:[0,4,5,7,10,9],variabl:[5,10,2,9,4],pgsql:10,typenam:9,suitabl:[10,3,9],sever:[10,0,3,7,9],rec:7,client_min_messag:3,manipul:[10,8],setinputs:5,dictionari:[10,0,7,8],releas:[2,3,6,7,10,9],log:[3,7,9],unwant:7,could:[10,3,8,9,4],keep:[10,3,2,9,7],length:5,outsid:[5,10,3],retain:5,timezon:[10,8,3,1],date:[10,8,4],owner:[3,8],facil:[7,9],lo_import:[10,3],unknown:[10,8],capac:7,wrapper:[10,0,8,9],attach:8,attack:10,termin:[10,3],boil:9,shell:9,status_begin:8,gotcha:2,fetchmani:[5,10,2],exactli:[5,1],bloat:[10,3],bother:9,structur:[5,10,7],charact:[5,10,6,3],stricter:8,terribl:10,barf:4,have:[2,3,4,5,6,7,8,10,9],tabl:[0,3,4,5,6,7,8,10],need:[2,4,6,7,10,9],tbar:5,mix:10,builtin:[10,3,8],which:[2,3,4,5,6,7,8,10,9],singl:[2,4,5,3,8,9,10,11],unless:[5,3,7,9],lobject:[10,3,8],who:8,set_wait_callback:[3,7,8,9],deploi:[10,0,2],why:2,request:[3,4],latin9:10,face:7,snapshot:8,psycopg:[0,1,2,4,5,3,7,8,10,9],rowidarrai:8,text:7,pqserververs:3,anywai:7,intervalfrompi:8,notsupportederror:[5,3,4],should:[2,3,4,5,6,8,10,9],aval:2,mintimeloggingcursor:7,local:[6,1,3],pyformat:4,enabl:[10,7,9],integr:4,contain:[3,4,5,6,8,10],shh:1,view:[3,8,9],conform:[8,9],statu:[0,3,8],correctli:[10,3,7],numer:[5,10,2,4,7],written:[10,8],progress:[3,8],neither:3,kei:[10,3,7,11,4],entir:[5,10,9],group:9,addit:[0,3,4,6,7,8],minconn:11,equal:[7,4],etc:[3,7,8,4],instanc:[1,3,7,8,10,9],isolation_level_repeatable_read:8,arriv:9,respect:4,unclean:3,poll_writ:[8,9],creat:[0,1,2,4,5,3,7,8,9,10,11],is_superus:3,compos:[5,3],transaction_status_intran:8,compon:[10,7,8],immedi:[5,10,3],presenc:9,untyp:4,togeth:[3,9],present:4,multi:[0,11],plain:[3,8],cursor:[0,1,2,4,5,3,7,8,10,9],defin:[0,3,4,6,7,8,10,9],observ:10,helper:7,almost:2,site:10,lightweight:2,revis:[10,3],parti:9,began:8,member:[3,8],handl:[0,2,5,3,7,8,9,10,11],fetchon:[5,10,7,9],infer:6,booleanarrai:8,upon:[8,9],effect:[3,8],pgcode:[6,4],expand:9,off:[7,4],sql_debug:9,well:[10,7],exampl:[2,3,4,5,6,7,8,10,9],command:[2,5,3,7,8,10,9],choos:3,undefin:2,pqprotocolvers:3,usual:[2,5,6,7,8,10,9],less:6,obtain:[5,10,7,9],tcp:2,detail:[2,4,5,3,8,10,9],heavili:0,"4ef8":7,web:10,field:[10,8],add:[7,8],tpc_prepar:[10,3],dql:5,logger:[7,9],match:[5,0,7,8,9],futur:[5,9],branch:[10,8],five:6,know:[6,8,9,4],xe2:10,desk:10,password:4,xe8:10,insert:[0,2,4,5,7,10,9],like:[4,5,6,7,8,10],success:11,anyth:[2,7],necessari:[10,8],async:[3,8,9,4],page:[10,0],wrapped_object:8,phenomena:3,gevent:[8,9],"export":[5,10,3,8,4],mxdatearrai:8,proper:[10,9,4],guarante:[3,8],librari:[0,2,4,3,8,10,9],interfaceerror:[5,3,9,4],leak:3,avoid:[3,2,9],leav:[5,2],mode:[2,5,3,8,10,9],importerror:2,"enum":8,pydatetim:8,host:[3,4],offset:[5,8,7,1,10],about:[4,5,8,11,10,9],actual:[5,8],socket:[9,4],column:[4,5,3,7,10,9],freedom:9,new_oid:[3,8],constructor:[0,1,8,4],adapt_point:9,discard:[5,3,2,11],disabl:7,own:2,easy_instal:2,automat:[2,3,7,8,9,10,11],dataset:10,guard:10,merg:10,pictur:10,transfer:[10,2,9],u20ac:10,procnam:5,"function":[0,1,4,5,3,7,8,9,10,11],unexpect:4,keyerror:6,gain:10,count:5,pqtransactionstatu:3,succe:3,made:[5,10,8,9,4],whether:3,wish:9,dml:5,asynchron:[0,4,3,7,8,9],record:[5,10,7],below:[3,8,4],ensur:3,otherwis:[10,3,2,8,7],problem:[0,2,4,3,8,10],"int":[10,0,7],dure:[1,2,4,5,3,8,10],pid:[3,8,9],ing:9,probabl:[5,2,8,9,11],nonetheless:9,connection_factori:[7,8,4],other:[2,3,4,5,6,7,8,10,9],lookup:6,uuid_adapt:[7,9],rememb:[10,2],repeat:[3,8],singleton:4,pydatearrai:8,pqcancel:3,timestampfrommx:8,reliabl:10,rule:5,getlogg:9},objtypes:{"0":"py:module","1":"py:class","2":"py:method","3":"py:data","4":"py:exception","5":"py:function","6":"py:attribute"},titles:["Psycopg – PostgreSQL database adapter for Python","psycopg2.tztzinfo implementations for Psycopg 2","Frequently Asked Questions","The connection class","The psycopg2 module content","The cursor class","psycopg2.errorcodes – Error codes defined by PostgreSQL","psycopg2.extras – Miscellaneous goodies for Psycopg 2","psycopg2.extensions – Extensions to the DB API","More advanced topics","Basic module usage","psycopg2.pool – Connections pooling"],objnames:{"0":["py","module","Python module"],"1":["py","class","Python class"],"2":["py","method","Python method"],"3":["py","data","Python data"],"4":["py","exception","Python exception"],"5":["py","function","Python function"],"6":["py","attribute","Python attribute"]},filenames:["index","tz","faq","connection","module","cursor","errorcodes","extras","extensions","advanced","usage","pool"]}) \ No newline at end of file diff -Nru psycopg2-2.0.13/doc/html/_sources/advanced.txt psycopg2-2.4.5/doc/html/_sources/advanced.txt --- psycopg2-2.0.13/doc/html/_sources/advanced.txt 1970-01-01 00:00:00.000000000 +0000 +++ psycopg2-2.4.5/doc/html/_sources/advanced.txt 2012-03-28 21:17:18.000000000 +0000 @@ -0,0 +1,494 @@ +More advanced topics +==================== + +.. sectionauthor:: Daniele Varrazzo + +.. testsetup:: * + + import re + import select + + cur.execute("CREATE TABLE atable (apoint point)") + conn.commit() + + def wait(conn): + while 1: + state = conn.poll() + if state == psycopg2.extensions.POLL_OK: + break + elif state == psycopg2.extensions.POLL_WRITE: + select.select([], [conn.fileno()], []) + elif state == psycopg2.extensions.POLL_READ: + select.select([conn.fileno()], [], []) + else: + raise psycopg2.OperationalError("poll() returned %s" % state) + + aconn = psycopg2.connect(database='test', async=1) + wait(aconn) + acurs = aconn.cursor() + +.. index:: + double: Subclassing; Cursor + double: Subclassing; Connection + +.. _subclassing-connection: +.. _subclassing-cursor: + +Connection and cursor factories +------------------------------- + +Psycopg exposes two new-style classes that can be sub-classed and expanded to +adapt them to the needs of the programmer: `psycopg2.extensions.cursor` +and `psycopg2.extensions.connection`. The `connection` class is +usually sub-classed only to provide an easy way to create customized cursors +but other uses are possible. `cursor` is much more interesting, because +it is the class where query building, execution and result type-casting into +Python variables happens. + +.. index:: + single: Example; Cursor subclass + +An example of cursor subclass performing logging is:: + + import psycopg2 + import psycopg2.extensions + import logging + + class LoggingCursor(psycopg2.extensions.cursor): + def execute(self, sql, args=None): + logger = logging.getLogger('sql_debug') + logger.info(self.mogrify(sql, args)) + + try: + psycopg2.extensions.cursor.execute(self, sql, args) + except Exception, exc: + logger.error("%s: %s" % (exc.__class__.__name__, exc)) + raise + + conn = psycopg2.connect(DSN) + cur = conn.cursor(cursor_factory=LoggingCursor) + cur.execute("INSERT INTO mytable VALUES (%s, %s, %s);", + (10, 20, 30)) + + + +.. index:: + single: Objects; Creating new adapters + single: Adaptation; Creating new adapters + single: Data types; Creating new adapters + +.. _adapting-new-types: + +Adapting new Python types to SQL syntax +--------------------------------------- + +Any Python class or type can be adapted to an SQL string. Adaptation mechanism +is similar to the Object Adaptation proposed in the :pep:`246` and is exposed +by the `psycopg2.extensions.adapt()` function. + +The `~cursor.execute()` method adapts its arguments to the +`~psycopg2.extensions.ISQLQuote` protocol. Objects that conform to this +protocol expose a `!getquoted()` method returning the SQL representation +of the object as a string (the method must return `!bytes` in Python 3). +Optionally the conform object may expose a +`~psycopg2.extensions.ISQLQuote.prepare()` method. + +There are two basic ways to have a Python object adapted to SQL: + +- the object itself is conform, or knows how to make itself conform. Such + object must expose a `__conform__()` method that will be called with the + protocol object as argument. The object can check that the protocol is + `!ISQLQuote`, in which case it can return `!self` (if the object also + implements `!getquoted()`) or a suitable wrapper object. This option is + viable if you are the author of the object and if the object is specifically + designed for the database (i.e. having Psycopg as a dependency and polluting + its interface with the required methods doesn't bother you). For a simple + example you can take a look at the source code for the + `psycopg2.extras.Inet` object. + +- If implementing the `!ISQLQuote` interface directly in the object is not an + option (maybe because the object to adapt comes from a third party library), + you can use an *adaptation function*, taking the object to be adapted as + argument and returning a conforming object. The adapter must be + registered via the `~psycopg2.extensions.register_adapter()` function. A + simple example wrapper is `!psycopg2.extras.UUID_adapter` used by the + `~psycopg2.extras.register_uuid()` function. + +A convenient object to write adapters is the `~psycopg2.extensions.AsIs` +wrapper, whose `!getquoted()` result is simply the `!str()`\ ing conversion of +the wrapped object. + +.. index:: + single: Example; Types adaptation + +Example: mapping of a `!Point` class into the |point|_ PostgreSQL +geometric type: + +.. doctest:: + + >>> from psycopg2.extensions import adapt, register_adapter, AsIs + + >>> class Point(object): + ... def __init__(self, x, y): + ... self.x = x + ... self.y = y + + >>> def adapt_point(point): + ... return AsIs("'(%s, %s)'" % (adapt(point.x), adapt(point.y))) + + >>> register_adapter(Point, adapt_point) + + >>> cur.execute("INSERT INTO atable (apoint) VALUES (%s)", + ... (Point(1.23, 4.56),)) + + +.. |point| replace:: :sql:`point` +.. _point: http://www.postgresql.org/docs/current/static/datatype-geometric.html#DATATYPE-GEOMETRIC + +The above function call results in the SQL command:: + + INSERT INTO atable (apoint) VALUES ('(1.23, 4.56)'); + + + +.. index:: Type casting + +.. _type-casting-from-sql-to-python: + +Type casting of SQL types into Python objects +--------------------------------------------- + +PostgreSQL objects read from the database can be adapted to Python objects +through an user-defined adapting function. An adapter function takes two +arguments: the object string representation as returned by PostgreSQL and the +cursor currently being read, and should return a new Python object. For +example, the following function parses the PostgreSQL :sql:`point` +representation into the previously defined `!Point` class: + + >>> def cast_point(value, cur): + ... if value is None: + ... return None + ... + ... # Convert from (f1, f2) syntax using a regular expression. + ... m = re.match(r"\(([^)]+),([^)]+)\)", value) + ... if m: + ... return Point(float(m.group(1)), float(m.group(2))) + ... else: + ... raise InterfaceError("bad point representation: %r" % value) + + +In order to create a mapping from a PostgreSQL type (either standard or +user-defined), its OID must be known. It can be retrieved either by the second +column of the `cursor.description`: + + >>> cur.execute("SELECT NULL::point") + >>> point_oid = cur.description[0][1] + >>> point_oid + 600 + +or by querying the system catalog for the type name and namespace (the +namespace for system objects is :sql:`pg_catalog`): + + >>> cur.execute(""" + ... SELECT pg_type.oid + ... FROM pg_type JOIN pg_namespace + ... ON typnamespace = pg_namespace.oid + ... WHERE typname = %(typename)s + ... AND nspname = %(namespace)s""", + ... {'typename': 'point', 'namespace': 'pg_catalog'}) + >>> point_oid = cur.fetchone()[0] + >>> point_oid + 600 + +After you know the object OID, you can create and register the new type: + + >>> POINT = psycopg2.extensions.new_type((point_oid,), "POINT", cast_point) + >>> psycopg2.extensions.register_type(POINT) + +The `~psycopg2.extensions.new_type()` function binds the object OIDs +(more than one can be specified) to the adapter function. +`~psycopg2.extensions.register_type()` completes the spell. Conversion +is automatically performed when a column whose type is a registered OID is +read: + + >>> cur.execute("SELECT '(10.2,20.3)'::point") + >>> point = cur.fetchone()[0] + >>> print type(point), point.x, point.y + 10.2 20.3 + +A typecaster created by `!new_type()` can be also used with +`~psycopg2.extensions.new_array_type()` to create a typecaster converting a +PostgreSQL array into a Python list. + + +.. index:: + pair: Asynchronous; Notifications + pair: LISTEN; SQL command + pair: NOTIFY; SQL command + +.. _async-notify: + +Asynchronous notifications +-------------------------- + +Psycopg allows asynchronous interaction with other database sessions using the +facilities offered by PostgreSQL commands |LISTEN|_ and |NOTIFY|_. Please +refer to the PostgreSQL documentation for examples about how to use this form of +communication. + +Notifications are instances of the `~psycopg2.extensions.Notify` object made +available upon reception in the `connection.notifies` list. Notifications can +be sent from Python code simply executing a :sql:`NOTIFY` command in an +`~cursor.execute()` call. + +Because of the way sessions interact with notifications (see |NOTIFY|_ +documentation), you should keep the connection in `~connection.autocommit` +mode if you wish to receive or send notifications in a timely manner. + +.. |LISTEN| replace:: :sql:`LISTEN` +.. _LISTEN: http://www.postgresql.org/docs/current/static/sql-listen.html +.. |NOTIFY| replace:: :sql:`NOTIFY` +.. _NOTIFY: http://www.postgresql.org/docs/current/static/sql-notify.html + +Notifications are received after every query execution. If the user is +interested in receiving notifications but not in performing any query, the +`~connection.poll()` method can be used to check for new messages without +wasting resources. + +A simple application could poll the connection from time to time to check if +something new has arrived. A better strategy is to use some I/O completion +function such as :py:func:`~select.select` to sleep until awaken from the kernel when there is +some data to read on the connection, thereby using no CPU unless there is +something to read:: + + import select + import psycopg2 + import psycopg2.extensions + + conn = psycopg2.connect(DSN) + conn.set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT) + + curs = conn.cursor() + curs.execute("LISTEN test;") + + print "Waiting for notifications on channel 'test'" + while 1: + if select.select([conn],[],[],5) == ([],[],[]): + print "Timeout" + else: + conn.poll() + while conn.notifies: + notify = conn.notifies.pop() + print "Got NOTIFY:", notify.pid, notify.channel, notify.payload + +Running the script and executing a command such as :sql:`NOTIFY test, 'hello'` +in a separate :program:`psql` shell, the output may look similar to:: + + Waiting for notifications on channel 'test' + Timeout + Timeout + Got NOTIFY: 6535 test hello + Timeout + ... + +Note that the payload is only available from PostgreSQL 9.0: notifications +received from a previous version server will have the +`~psycopg2.extensions.Notify.payload` attribute set to the empty string. + +.. versionchanged:: 2.3 + Added `~psycopg2.extensions.Notify` object and handling notification + payload. + + + +.. index:: + double: Asynchronous; Connection + +.. _async-support: + +Asynchronous support +-------------------- + +.. versionadded:: 2.2.0 + +Psycopg can issue asynchronous queries to a PostgreSQL database. An asynchronous +communication style is established passing the parameter *async*\=1 to the +`~psycopg2.connect()` function: the returned connection will work in +*asynchronous mode*. + +In asynchronous mode, a Psycopg connection will rely on the caller to poll the +socket file descriptor, checking if it is ready to accept data or if a query +result has been transferred and is ready to be read on the client. The caller +can use the method `~connection.fileno()` to get the connection file +descriptor and `~connection.poll()` to make communication proceed according to +the current connection state. + +The following is an example loop using methods `!fileno()` and `!poll()` +together with the Python :py:func:`~select.select` function in order to carry on +asynchronous operations with Psycopg:: + + def wait(conn): + while 1: + state = conn.poll() + if state == psycopg2.extensions.POLL_OK: + break + elif state == psycopg2.extensions.POLL_WRITE: + select.select([], [conn.fileno()], []) + elif state == psycopg2.extensions.POLL_READ: + select.select([conn.fileno()], [], []) + else: + raise psycopg2.OperationalError("poll() returned %s" % state) + +The above loop of course would block an entire application: in a real +asynchronous framework, `!select()` would be called on many file descriptors +waiting for any of them to be ready. Nonetheless the function can be used to +connect to a PostgreSQL server only using nonblocking commands and the +connection obtained can be used to perform further nonblocking queries. After +`!poll()` has returned `~psycopg2.extensions.POLL_OK`, and thus `!wait()` has +returned, the connection can be safely used: + + >>> aconn = psycopg2.connect(database='test', async=1) + >>> wait(aconn) + >>> acurs = aconn.cursor() + +Note that there are a few other requirements to be met in order to have a +completely non-blocking connection attempt: see the libpq documentation for +|PQconnectStart|_. + +.. |PQconnectStart| replace:: `!PQconnectStart()` +.. _PQconnectStart: http://www.postgresql.org/docs/current/static/libpq-connect.html#LIBPQ-PQCONNECTSTARTPARAMS + +The same loop should be also used to perform nonblocking queries: after +sending a query via `~cursor.execute()` or `~cursor.callproc()`, call +`!poll()` on the connection available from `cursor.connection` until it +returns `!POLL_OK`, at which point the query has been completely sent to the +server and, if it produced data, the results have been transferred to the +client and available using the regular cursor methods: + + >>> acurs.execute("SELECT pg_sleep(5); SELECT 42;") + >>> wait(acurs.connection) + >>> acurs.fetchone()[0] + 42 + +When an asynchronous query is being executed, `connection.isexecuting()` returns +`!True`. Two cursors can't execute concurrent queries on the same asynchronous +connection. + +There are several limitations in using asynchronous connections: the +connection is always in `~connection.autocommit` mode and it is not +possible to change it. So a +transaction is not implicitly started at the first query and is not possible +to use methods `~connection.commit()` and `~connection.rollback()`: you can +manually control transactions using `~cursor.execute()` to send database +commands such as :sql:`BEGIN`, :sql:`COMMIT` and :sql:`ROLLBACK`. Similarly +`~connection.set_session()` can't be used but it is still possible to invoke the +:sql:`SET` command with the proper :sql:`default_transaction_...` parameter. + +With asynchronous connections it is also not possible to use +`~connection.set_client_encoding()`, `~cursor.executemany()`, :ref:`large +objects `, :ref:`named cursors `. + +:ref:`COPY commands ` are not supported either in asynchronous mode, but +this will be probably implemented in a future release. + + + + +.. index:: + single: Greenlet + single: Coroutine + single: Eventlet + single: gevent + single: Wait callback + +.. _green-support: + +Support to coroutine libraries +------------------------------ + +.. versionadded:: 2.2.0 + +Psycopg can be used together with coroutine_\-based libraries, and participate +to cooperative multithreading. + +Coroutine-based libraries (such as Eventlet_ or gevent_) can usually patch the +Python standard library in order to enable a coroutine switch in the presence of +blocking I/O: the process is usually referred as making the system *green*, in +reference to the `green threads`_. + +Because Psycopg is a C extension module, it is not possible for coroutine +libraries to patch it: Psycopg instead enables cooperative multithreading by +allowing the registration of a *wait callback* using the +`psycopg2.extensions.set_wait_callback()` function. When a wait callback is +registered, Psycopg will use `libpq non-blocking calls`__ instead of the regular +blocking ones, and will delegate to the callback the responsibility to wait +for the socket to become readable or writable. + +Working this way, the caller does not have the complete freedom to schedule the +socket check whenever they want as with an :ref:`asynchronous connection +`, but has the advantage of maintaining a complete |DBAPI| +semantics: from the point of view of the end user, all Psycopg functions and +objects will work transparently in the coroutine environment (blocking the +calling green thread and giving other green threads the possibility to be +scheduled), allowing non modified code and third party libraries (such as +SQLAlchemy_) to be used in coroutine-based programs. + +.. warning:: + Psycopg connections are not *green thread safe* and can't be used + concurrently by different green threads. Trying to execute more than one + command at time using one cursor per thread will result in an error (or a + deadlock on versions before 2.4.2). + + Therefore, programmers are advised to either avoid sharing connections + between coroutines or to use a library-friendly lock to synchronize shared + connections, e.g. for pooling. + +Coroutine libraries authors should provide a callback implementation (and +possibly a method to register it) to make Psycopg as green as they want. An +example callback (using `!select()` to block) is provided as +`psycopg2.extras.wait_select()`: it boils down to something similar to:: + + def wait_select(conn): + while 1: + state = conn.poll() + if state == extensions.POLL_OK: + break + elif state == extensions.POLL_READ: + select.select([conn.fileno()], [], []) + elif state == extensions.POLL_WRITE: + select.select([], [conn.fileno()], []) + else: + raise OperationalError("bad state from poll: %s" % state) + +Providing callback functions for the single coroutine libraries is out of +psycopg2 scope, as the callback can be tied to the libraries' implementation +details. You can check the `psycogreen`_ project for further informations and +resources about the topic. + +.. _coroutine: http://en.wikipedia.org/wiki/Coroutine +.. _greenlet: http://pypi.python.org/pypi/greenlet +.. _green threads: http://en.wikipedia.org/wiki/Green_threads +.. _Eventlet: http://eventlet.net/ +.. _gevent: http://www.gevent.org/ +.. _SQLAlchemy: http://www.sqlalchemy.org/ +.. _psycogreen: http://bitbucket.org/dvarrazzo/psycogreen/ +.. __: http://www.postgresql.org/docs/current/static/libpq-async.html + +.. warning:: + + :ref:`COPY commands ` are currently not supported when a wait callback + is registered, but they will be probably implemented in a future release. + + :ref:`Large objects ` are not supported either: they are + not compatible with asynchronous connections. + + +.. testcode:: + :hide: + + aconn.close() + conn.rollback() + cur.execute("DROP TABLE atable") + conn.commit() + cur.close() + conn.close() diff -Nru psycopg2-2.0.13/doc/html/_sources/connection.txt psycopg2-2.4.5/doc/html/_sources/connection.txt --- psycopg2-2.0.13/doc/html/_sources/connection.txt 1970-01-01 00:00:00.000000000 +0000 +++ psycopg2-2.4.5/doc/html/_sources/connection.txt 2012-03-28 21:17:18.000000000 +0000 @@ -0,0 +1,679 @@ +The ``connection`` class +======================== + +.. sectionauthor:: Daniele Varrazzo + +.. testsetup:: + + from pprint import pprint + import psycopg2.extensions + + drop_test_table('foo') + +.. class:: connection + + Handles the connection to a PostgreSQL database instance. It encapsulates + a database session. + + Connections are created using the factory function + `~psycopg2.connect()`. + + Connections are thread safe and can be shared among many threads. See + :ref:`thread-safety` for details. + + .. method:: cursor([name] [, cursor_factory] [, withhold]) + + Return a new `cursor` object using the connection. + + If *name* is specified, the returned cursor will be a :ref:`server + side cursor ` (also known as *named cursor*). + Otherwise it will be a regular *client side* cursor. By default a + :sql:`WITHOUT HOLD` cursor is created; to create a :sql:`WITH HOLD` + cursor, pass a `!True` value as the *withhold* parameter. See + :ref:`server-side-cursors`. + + The name can be a string not valid as a PostgreSQL identifier: for + example it may start with a digit and contain non-alphanumeric + characters and quotes. + + .. versionchanged:: 2.4 + previously only valid PostgreSQL identifiers were accepted as + cursor name. + + .. warning:: + It is unsafe to expose the *name* to an untrusted source, for + instance you shouldn't allow *name* to be read from a HTML form. + Consider it as part of the query, not as a query parameter. + + The *cursor_factory* argument can be used to create non-standard + cursors. The class returned should be a subclass of + `psycopg2.extensions.cursor`. See :ref:`subclassing-cursor` for + details. + + .. extension:: + + The `name` and `cursor_factory` parameters are Psycopg + extensions to the |DBAPI|. + + + .. index:: + pair: Transaction; Commit + + .. method:: commit() + + Commit any pending transaction to the database. Psycopg can be set to + perform automatic commits at each operation, see + `~connection.set_isolation_level()`. + + + .. index:: + pair: Transaction; Rollback + + .. method:: rollback() + + Roll back to the start of any pending transaction. Closing a + connection without committing the changes first will cause an implicit + rollback to be performed. + + + .. method:: close() + + Close the connection now (rather than whenever `del` is executed). + The connection will be unusable from this point forward; an + `~psycopg2.InterfaceError` will be raised if any operation is + attempted with the connection. The same applies to all cursor objects + trying to use the connection. Note that closing a connection without + committing the changes first will cause any pending change to be + discarded as if a :sql:`ROLLBACK` was performed (unless a different + isolation level has been selected: see + `~connection.set_isolation_level()`). + + .. index:: + single: PgBouncer; unclean server + + .. versionchanged:: 2.2 + previously an explicit :sql:`ROLLBACK` was issued by Psycopg on + `!close()`. The command could have been sent to the backend at an + inappropriate time, so Psycopg currently relies on the backend to + implicitly discard uncommitted changes. Some middleware are known + to behave incorrectly though when the connection is closed during + a transaction (when `~connection.status` is + `~psycopg2.extensions.STATUS_IN_TRANSACTION`), e.g. PgBouncer_ + reports an ``unclean server`` and discards the connection. To + avoid this problem you can ensure to terminate the transaction + with a `~connection.commit()`/`~connection.rollback()` before + closing. + + .. _PgBouncer: http://pgbouncer.projects.postgresql.org/ + + + .. index:: + single: Exceptions; In the connection class + + .. rubric:: Exceptions as connection class attributes + + The `!connection` also exposes as attributes the same exceptions + available in the `psycopg2` module. See :ref:`dbapi-exceptions`. + + + + .. index:: + single: Two-phase commit; methods + + .. rubric:: Two-phase commit support methods + + .. versionadded:: 2.3 + + .. seealso:: :ref:`tpc` for an introductory explanation of these methods. + + Note that PostgreSQL supports two-phase commit since release 8.1: these + methods raise `~psycopg2.NotSupportedError` if used with an older version + server. + + + .. _tpc_methods: + + .. method:: xid(format_id, gtrid, bqual) + + Returns a `~psycopg2.extensions.Xid` instance to be passed to the + `!tpc_*()` methods of this connection. The argument types and + constraints are explained in :ref:`tpc`. + + The values passed to the method will be available on the returned + object as the members `~psycopg2.extensions.Xid.format_id`, + `~psycopg2.extensions.Xid.gtrid`, `~psycopg2.extensions.Xid.bqual`. + The object also allows accessing to these members and unpacking as a + 3-items tuple. + + + .. method:: tpc_begin(xid) + + Begins a TPC transaction with the given transaction ID *xid*. + + This method should be called outside of a transaction (i.e. nothing + may have executed since the last `~connection.commit()` or + `~connection.rollback()` and `connection.status` is + `~psycopg2.extensions.STATUS_READY`). + + Furthermore, it is an error to call `!commit()` or `!rollback()` + within the TPC transaction: in this case a `~psycopg2.ProgrammingError` + is raised. + + The *xid* may be either an object returned by the `~connection.xid()` + method or a plain string: the latter allows to create a transaction + using the provided string as PostgreSQL transaction id. See also + `~connection.tpc_recover()`. + + + .. index:: + pair: Transaction; Prepare + + .. method:: tpc_prepare() + + Performs the first phase of a transaction started with + `~connection.tpc_begin()`. A `~psycopg2.ProgrammingError` is raised if + this method is used outside of a TPC transaction. + + After calling `!tpc_prepare()`, no statements can be executed until + `~connection.tpc_commit()` or `~connection.tpc_rollback()` will be + called. The `~connection.reset()` method can be used to restore the + status of the connection to `~psycopg2.extensions.STATUS_READY`: the + transaction will remain prepared in the database and will be + possible to finish it with `!tpc_commit(xid)` and + `!tpc_rollback(xid)`. + + .. seealso:: the |PREPARE TRANSACTION|_ PostgreSQL command. + + .. |PREPARE TRANSACTION| replace:: :sql:`PREPARE TRANSACTION` + .. _PREPARE TRANSACTION: http://www.postgresql.org/docs/current/static/sql-prepare-transaction.html + + + .. index:: + pair: Commit; Prepared + + .. method:: tpc_commit([xid]) + + When called with no arguments, `!tpc_commit()` commits a TPC + transaction previously prepared with `~connection.tpc_prepare()`. + + If `!tpc_commit()` is called prior to `!tpc_prepare()`, a single phase + commit is performed. A transaction manager may choose to do this if + only a single resource is participating in the global transaction. + + When called with a transaction ID *xid*, the database commits + the given transaction. If an invalid transaction ID is + provided, a `~psycopg2.ProgrammingError` will be raised. This form + should be called outside of a transaction, and is intended for use in + recovery. + + On return, the TPC transaction is ended. + + .. seealso:: the |COMMIT PREPARED|_ PostgreSQL command. + + .. |COMMIT PREPARED| replace:: :sql:`COMMIT PREPARED` + .. _COMMIT PREPARED: http://www.postgresql.org/docs/current/static/sql-commit-prepared.html + + + .. index:: + pair: Rollback; Prepared + + .. method:: tpc_rollback([xid]) + + When called with no arguments, `!tpc_rollback()` rolls back a TPC + transaction. It may be called before or after + `~connection.tpc_prepare()`. + + When called with a transaction ID *xid*, it rolls back the given + transaction. If an invalid transaction ID is provided, a + `~psycopg2.ProgrammingError` is raised. This form should be called + outside of a transaction, and is intended for use in recovery. + + On return, the TPC transaction is ended. + + .. seealso:: the |ROLLBACK PREPARED|_ PostgreSQL command. + + .. |ROLLBACK PREPARED| replace:: :sql:`ROLLBACK PREPARED` + .. _ROLLBACK PREPARED: http://www.postgresql.org/docs/current/static/sql-rollback-prepared.html + + + .. index:: + pair: Transaction; Recover + + .. method:: tpc_recover() + + Returns a list of `~psycopg2.extensions.Xid` representing pending + transactions, suitable for use with `tpc_commit()` or + `tpc_rollback()`. + + If a transaction was not initiated by Psycopg, the returned Xids will + have attributes `~psycopg2.extensions.Xid.format_id` and + `~psycopg2.extensions.Xid.bqual` set to `!None` and the + `~psycopg2.extensions.Xid.gtrid` set to the PostgreSQL transaction ID: such Xids are still + usable for recovery. Psycopg uses the same algorithm of the + `PostgreSQL JDBC driver`__ to encode a XA triple in a string, so + transactions initiated by a program using such driver should be + unpacked correctly. + + .. __: http://jdbc.postgresql.org/ + + Xids returned by `!tpc_recover()` also have extra attributes + `~psycopg2.extensions.Xid.prepared`, `~psycopg2.extensions.Xid.owner`, + `~psycopg2.extensions.Xid.database` populated with the values read + from the server. + + .. seealso:: the |pg_prepared_xacts|_ system view. + + .. |pg_prepared_xacts| replace:: `pg_prepared_xacts` + .. _pg_prepared_xacts: http://www.postgresql.org/docs/current/static/view-pg-prepared-xacts.html + + + + .. extension:: + + The above methods are the only ones defined by the |DBAPI| protocol. + The Psycopg connection objects exports the following additional + methods and attributes. + + + .. attribute:: closed + + Read-only attribute reporting whether the database connection is open + (0) or closed (1). + + + .. method:: cancel + + Cancel the current database operation. + + The method interrupts the processing of the current operation. If no + query is being executed, it does nothing. You can call this function + from a different thread than the one currently executing a database + operation, for instance if you want to cancel a long running query if a + button is pushed in the UI. Interrupting query execution will cause the + cancelled method to raise a + `~psycopg2.extensions.QueryCanceledError`. Note that the termination + of the query is not guaranteed to succeed: see the documentation for + |PQcancel|_. + + .. |PQcancel| replace:: `!PQcancel()` + .. _PQcancel: http://www.postgresql.org/docs/current/static/libpq-cancel.html#LIBPQ-PQCANCEL + + .. versionadded:: 2.3 + + + .. method:: reset + + Reset the connection to the default. + + The method rolls back an eventual pending transaction and executes the + PostgreSQL |RESET|_ and |SET SESSION AUTHORIZATION|__ to revert the + session to the default values. A two-phase commit transaction prepared + using `~connection.tpc_prepare()` will remain in the database + available for recover. + + .. |RESET| replace:: :sql:`RESET` + .. _RESET: http://www.postgresql.org/docs/current/static/sql-reset.html + + .. |SET SESSION AUTHORIZATION| replace:: :sql:`SET SESSION AUTHORIZATION` + .. __: http://www.postgresql.org/docs/current/static/sql-set-session-authorization.html + + .. versionadded:: 2.0.12 + + + .. attribute:: dsn + + Read-only string containing the connection string used by the + connection. + + + .. index:: + pair: Transaction; Autocommit + pair: Transaction; Isolation level + + .. method:: set_session([isolation_level,] [readonly,] [deferrable,] [autocommit]) + + Set one or more parameters for the next transactions or statements in + the current session. See |SET TRANSACTION|_ for further details. + + .. |SET TRANSACTION| replace:: :sql:`SET TRANSACTION` + .. _SET TRANSACTION: http://www.postgresql.org/docs/current/static/sql-set-transaction.html + + :param isolation_level: set the `isolation level`_ for the next + transactions/statements. The value can be one of the + :ref:`constants ` defined in the + `~psycopg2.extensions` module or one of the literal values + ``READ UNCOMMITTED``, ``READ COMMITTED``, ``REPEATABLE READ``, + ``SERIALIZABLE``. + :param readonly: if `!True`, set the connection to read only; + read/write if `!False`. + :param deferrable: if `!True`, set the connection to deferrable; + non deferrable if `!False`. Only available from PostgreSQL 9.1. + :param autocommit: switch the connection to autocommit mode: not a + PostgreSQL session setting but an alias for setting the + `autocommit` attribute. + + The parameters *isolation_level*, *readonly* and *deferrable* also + accept the string ``DEFAULT`` as a value: the effect is to reset the + parameter to the server default. + + .. _isolation level: + http://www.postgresql.org/docs/current/static/transaction-iso.html + + The function must be invoked with no transaction in progress. At every + function invocation, only the specified parameters are changed. + + The default for the values are defined by the server configuration: + see values for |default_transaction_isolation|__, + |default_transaction_read_only|__, |default_transaction_deferrable|__. + + .. |default_transaction_isolation| replace:: :sql:`default_transaction_isolation` + .. __: http://www.postgresql.org/docs/current/static/runtime-config-client.html#GUC-DEFAULT-TRANSACTION-ISOLATION + .. |default_transaction_read_only| replace:: :sql:`default_transaction_read_only` + .. __: http://www.postgresql.org/docs/current/static/runtime-config-client.html#GUC-DEFAULT-TRANSACTION-READ-ONLY + .. |default_transaction_deferrable| replace:: :sql:`default_transaction_deferrable` + .. __: http://www.postgresql.org/docs/current/static/runtime-config-client.html#GUC-DEFAULT-TRANSACTION-DEFERRABLE + + .. note:: + + There is currently no builtin method to read the current value for + the parameters: use :sql:`SHOW default_transaction_...` to read + the values from the backend. + + .. versionadded:: 2.4.2 + + + .. attribute:: autocommit + + Read/write attribute: if `!True`, no transaction is handled by the + driver and every statement sent to the backend has immediate effect; + if `!False` a new transaction is started at the first command + execution: the methods `commit()` or `rollback()` must be manually + invoked to terminate the transaction. + + The autocommit mode is useful to execute commands requiring to be run + outside a transaction, such as :sql:`CREATE DATABASE` or + :sql:`VACUUM`. + + The default is `!False` (manual commit) as per DBAPI specification. + + .. warning:: + + By default, any query execution, including a simple :sql:`SELECT` + will start a transaction: for long-running programs, if no further + action is taken, the session will remain "idle in transaction", a + condition non desiderable for several reasons (locks are held by + the session, tables bloat...). For long lived scripts, either + ensure to terminate a transaction as soon as possible or use an + autocommit connection. + + .. versionadded:: 2.4.2 + + + .. attribute:: isolation_level + .. method:: set_isolation_level(level) + + .. note:: + + From version 2.4.2, `set_session()` and `autocommit`, offer + finer control on the transaction characteristics. + + Read or set the `transaction isolation level`_ for the current session. + The level defines the different phenomena that can happen in the + database between concurrent transactions. + + The value set or read is an integer: symbolic constants are defined in + the module `psycopg2.extensions`: see + :ref:`isolation-level-constants` for the available values. + + The default level is :sql:`READ COMMITTED`: at this level a + transaction is automatically started the first time a database command + is executed. If you want an *autocommit* mode, switch to + `~psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT` before + executing any command:: + + >>> conn.set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT) + + See also :ref:`transactions-control`. + + .. index:: + pair: Client; Encoding + + .. attribute:: encoding + .. method:: set_client_encoding(enc) + + Read or set the client encoding for the current session. The default + is the encoding defined by the database. It should be one of the + `characters set supported by PostgreSQL`__ + + .. __: http://www.postgresql.org/docs/current/static/multibyte.html + + + .. index:: + pair: Client; Logging + + .. attribute:: notices + + A list containing all the database messages sent to the client during + the session. + + .. doctest:: + :options: NORMALIZE_WHITESPACE + + >>> cur.execute("CREATE TABLE foo (id serial PRIMARY KEY);") + >>> pprint(conn.notices) + ['NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "foo_pkey" for table "foo"\n', + 'NOTICE: CREATE TABLE will create implicit sequence "foo_id_seq" for serial column "foo.id"\n'] + + To avoid a leak in case excessive notices are generated, only the last + 50 messages are kept. + + You can configure what messages to receive using `PostgreSQL logging + configuration parameters`__ such as ``log_statement``, + ``client_min_messages``, ``log_min_duration_statement`` etc. + + .. __: http://www.postgresql.org/docs/current/static/runtime-config-logging.html + + + .. attribute:: notifies + + List of `~psycopg2.extensions.Notify` objects containing asynchronous + notifications received by the session. + + For other details see :ref:`async-notify`. + + .. versionchanged:: 2.3 + Notifications are instances of the `!Notify` object. Previously the + list was composed by 2 items tuples :samp:`({pid},{channel})` and + the payload was not accessible. To keep backward compatibility, + `!Notify` objects can still be accessed as 2 items tuples. + + .. index:: + pair: Backend; PID + + .. method:: get_backend_pid() + + Returns the process ID (PID) of the backend server process handling + this connection. + + Note that the PID belongs to a process executing on the database + server host, not the local host! + + .. seealso:: libpq docs for `PQbackendPID()`__ for details. + + .. __: http://www.postgresql.org/docs/current/static/libpq-status.html#LIBPQ-PQBACKENDPID + + .. versionadded:: 2.0.8 + + + .. index:: + pair: Server; Parameters + + .. method:: get_parameter_status(parameter) + + Look up a current parameter setting of the server. + + Potential values for ``parameter`` are: ``server_version``, + ``server_encoding``, ``client_encoding``, ``is_superuser``, + ``session_authorization``, ``DateStyle``, ``TimeZone``, + ``integer_datetimes``, and ``standard_conforming_strings``. + + If server did not report requested parameter, return `!None`. + + .. seealso:: libpq docs for `PQparameterStatus()`__ for details. + + .. __: http://www.postgresql.org/docs/current/static/libpq-status.html#LIBPQ-PQPARAMETERSTATUS + + .. versionadded:: 2.0.12 + + + .. index:: + pair: Transaction; Status + + .. method:: get_transaction_status() + + Return the current session transaction status as an integer. Symbolic + constants for the values are defined in the module + `psycopg2.extensions`: see :ref:`transaction-status-constants` + for the available values. + + .. seealso:: libpq docs for `PQtransactionStatus()`__ for details. + + .. __: http://www.postgresql.org/docs/current/static/libpq-status.html#LIBPQ-PQTRANSACTIONSTATUS + + + .. index:: + pair: Protocol; Version + + .. attribute:: protocol_version + + A read-only integer representing frontend/backend protocol being used. + Currently Psycopg supports only protocol 3, which allows connection + to PostgreSQL server from version 7.4. Psycopg versions previous than + 2.3 support both protocols 2 and 3. + + .. seealso:: libpq docs for `PQprotocolVersion()`__ for details. + + .. __: http://www.postgresql.org/docs/current/static/libpq-status.html#LIBPQ-PQPROTOCOLVERSION + + .. versionadded:: 2.0.12 + + + .. index:: + pair: Server; Version + + .. attribute:: server_version + + A read-only integer representing the backend version. + + The number is formed by converting the major, minor, and revision + numbers into two-decimal-digit numbers and appending them together. + For example, version 8.1.5 will be returned as ``80105``. + + .. seealso:: libpq docs for `PQserverVersion()`__ for details. + + .. __: http://www.postgresql.org/docs/current/static/libpq-status.html#LIBPQ-PQSERVERVERSION + + .. versionadded:: 2.0.12 + + + .. index:: + pair: Connection; Status + + .. attribute:: status + + A read-only integer representing the status of the connection. + Symbolic constants for the values are defined in the module + `psycopg2.extensions`: see :ref:`connection-status-constants` + for the available values. + + + .. method:: lobject([oid [, mode [, new_oid [, new_file [, lobject_factory]]]]]) + + Return a new database large object as a `~psycopg2.extensions.lobject` + instance. + + See :ref:`large-objects` for an overview. + + :param oid: The OID of the object to read or write. 0 to create + a new large object and and have its OID assigned automatically. + :param mode: Access mode to the object, see below. + :param new_oid: Create a new object using the specified OID. The + function raises `~psycopg2.OperationalError` if the OID is already + in use. Default is 0, meaning assign a new one automatically. + :param new_file: The name of a file to be imported in the the database + (using the |lo_import|_ function) + :param lobject_factory: Subclass of + `~psycopg2.extensions.lobject` to be instantiated. + + .. |lo_import| replace:: `!lo_import()` + .. _lo_import: http://www.postgresql.org/docs/current/static/lo-interfaces.html#LO-IMPORT + + Available values for *mode* are: + + ======= ========= + *mode* meaning + ======= ========= + ``r`` Open for read only + ``w`` Open for write only + ``rw`` Open for read/write + ``n`` Don't open the file + ``b`` Don't decode read data (return data as `!str` in Python 2 or `!bytes` in Python 3) + ``t`` Decode read data according to `connection.encoding` (return data as `!unicode` in Python 2 or `!str` in Python 3) + ======= ========= + + ``b`` and ``t`` can be specified together with a read/write mode. If + neither ``b`` nor ``t`` is specified, the default is ``b`` in Python 2 + and ``t`` in Python 3. + + .. versionadded:: 2.0.8 + + .. versionchanged:: 2.4 added ``b`` and ``t`` mode and unicode + support. + + + .. rubric:: Methods related to asynchronous support. + + .. versionadded:: 2.2.0 + + .. seealso:: :ref:`async-support` and :ref:`green-support`. + + + .. attribute:: async + + Read only attribute: 1 if the connection is asynchronous, 0 otherwise. + + + .. method:: poll() + + Used during an asynchronous connection attempt, or when a cursor is + executing a query on an asynchronous connection, make communication + proceed if it wouldn't block. + + Return one of the constants defined in :ref:`poll-constants`. If it + returns `~psycopg2.extensions.POLL_OK` then the connection has been + estabilished or the query results are available on the client. + Otherwise wait until the file descriptor returned by `fileno()` is + ready to read or to write, as explained in :ref:`async-support`. + `poll()` should be also used by the function installed by + `~psycopg2.extensions.set_wait_callback()` as explained in + :ref:`green-support`. + + `poll()` is also used to receive asynchronous notifications from the + database: see :ref:`async-notify` from further details. + + + .. method:: fileno() + + Return the file descriptor underlying the connection: useful to read + its status during asynchronous communication. + + + .. method:: isexecuting() + + Return `!True` if the connection is executing an asynchronous operation. + + +.. testcode:: + :hide: + + conn.rollback() diff -Nru psycopg2-2.0.13/doc/html/_sources/cursor.txt psycopg2-2.4.5/doc/html/_sources/cursor.txt --- psycopg2-2.0.13/doc/html/_sources/cursor.txt 1970-01-01 00:00:00.000000000 +0000 +++ psycopg2-2.4.5/doc/html/_sources/cursor.txt 2012-03-28 21:17:18.000000000 +0000 @@ -0,0 +1,555 @@ +The ``cursor`` class +==================== + +.. sectionauthor:: Daniele Varrazzo + +.. testsetup:: * + + from StringIO import StringIO + import sys + + create_test_table() + + # initial data + cur.executemany("INSERT INTO test (num, data) VALUES (%s, %s)", + [(100, "abc'def"), (None, 'dada'), (42, 'bar')]) + conn.commit() + + +.. class:: cursor + + Allows Python code to execute PostgreSQL command in a database session. + Cursors are created by the `connection.cursor()` method: they are + bound to the connection for the entire lifetime and all the commands are + executed in the context of the database session wrapped by the connection. + + Cursors created from the same connection are not isolated, i.e., any + changes done to the database by a cursor are immediately visible by the + other cursors. Cursors created from different connections can or can not + be isolated, depending on the connections' :ref:`isolation level + `. See also `~connection.rollback()` and + `~connection.commit()` methods. + + Cursors are *not* thread safe: a multithread application can create + many cursors from the same connection and should use each cursor from + a single thread. See :ref:`thread-safety` for details. + + + .. attribute:: description + + This read-only attribute is a sequence of 7-item sequences. + + Each of these sequences is a named tuple (a regular tuple if + :func:`collections.namedtuple` is not available) containing information + describing one result column: + + 0. `!name`: the name of the column returned. + 1. `!type_code`: the PostgreSQL OID of the column. You can use the + |pg_type|_ system table to get more informations about the type. + This is the value used by Psycopg to decide what Python type use + to represent the value. See also + :ref:`type-casting-from-sql-to-python`. + 2. `!display_size`: the actual length of the column in bytes. + Obtaining this value is computationally intensive, so it is + always `!None` unless the :envvar:`PSYCOPG_DISPLAY_SIZE` parameter + is set at compile time. See also PQgetlength_. + 3. `!internal_size`: the size in bytes of the column associated to + this column on the server. Set to a negative value for + variable-size types See also PQfsize_. + 4. `!precision`: total number of significant digits in columns of + type |NUMERIC|_. `!None` for other types. + 5. `!scale`: count of decimal digits in the fractional part in + columns of type |NUMERIC|. `!None` for other types. + 6. `!null_ok`: always `!None` as not easy to retrieve from the libpq. + + This attribute will be `!None` for operations that do not return rows + or if the cursor has not had an operation invoked via the + |execute*|_ methods yet. + + .. |pg_type| replace:: :sql:`pg_type` + .. _pg_type: http://www.postgresql.org/docs/current/static/catalog-pg-type.html + .. _PQgetlength: http://www.postgresql.org/docs/current/static/libpq-exec.html#LIBPQ-PQGETLENGTH + .. _PQfsize: http://www.postgresql.org/docs/current/static/libpq-exec.html#LIBPQ-PQFSIZE + .. _NUMERIC: http://www.postgresql.org/docs/current/static/datatype-numeric.html#DATATYPE-NUMERIC-DECIMAL + .. |NUMERIC| replace:: :sql:`NUMERIC` + + .. versionchanged:: 2.4 + if possible, columns descriptions are named tuple instead of + regular tuples. + + .. method:: close() + + Close the cursor now (rather than whenever `del` is executed). + The cursor will be unusable from this point forward; an + `~psycopg2.InterfaceError` will be raised if any operation is + attempted with the cursor. + + .. attribute:: closed + + Read-only boolean attribute: specifies if the cursor is closed + (`!True`) or not (`!False`). + + .. extension:: + + The `closed` attribute is a Psycopg extension to the + |DBAPI|. + + .. versionadded:: 2.0.7 + + + .. attribute:: connection + + Read-only attribute returning a reference to the `connection` + object on which the cursor was created. + + + .. attribute:: name + + Read-only attribute containing the name of the cursor if it was + creates as named cursor by `connection.cursor()`, or `!None` if + it is a client side cursor. See :ref:`server-side-cursors`. + + .. extension:: + + The `name` attribute is a Psycopg extension to the |DBAPI|. + + + .. attribute:: withhold + + Read/write attribute: specifies if a named cursor lifetime should + extend outside of the current transaction, i.e., it is possible to + fetch from the cursor even after a `commection.commit()` (but not after + a `connection.rollback()`). See :ref:`server-side-cursors` + + .. versionadded:: 2.4.3 + + .. extension:: + + The `withhold` attribute is a Psycopg extension to the |DBAPI|. + + + .. |execute*| replace:: `execute*()` + + .. _execute*: + + .. rubric:: Commands execution methods + + + .. method:: execute(operation [, parameters]) + + Prepare and execute a database operation (query or command). + + Parameters may be provided as sequence or mapping and will be bound to + variables in the operation. Variables are specified either with + positional (``%s``) or named (:samp:`%({name})s`) placeholders. See + :ref:`query-parameters`. + + The method returns `!None`. If a query was executed, the returned + values can be retrieved using |fetch*|_ methods. + + + .. method:: executemany(operation, seq_of_parameters) + + Prepare a database operation (query or command) and then execute it + against all parameter tuples or mappings found in the sequence + `seq_of_parameters`. + + The function is mostly useful for commands that update the database: + any result set returned by the query is discarded. + + Parameters are bounded to the query using the same rules described in + the `~cursor.execute()` method. + + + .. method:: callproc(procname [, parameters]) + + Call a stored database procedure with the given name. The sequence of + parameters must contain one entry for each argument that the procedure + expects. The result of the call is returned as modified copy of the + input sequence. Input parameters are left untouched, output and + input/output parameters replaced with possibly new values. + + The procedure may also provide a result set as output. This must then + be made available through the standard |fetch*|_ methods. + + + .. method:: mogrify(operation [, parameters]) + + Return a query string after arguments binding. The string returned is + exactly the one that would be sent to the database running the + `~cursor.execute()` method or similar. + + >>> cur.mogrify("INSERT INTO test (num, data) VALUES (%s, %s)", (42, 'bar')) + "INSERT INTO test (num, data) VALUES (42, E'bar')" + + .. extension:: + + The `mogrify()` method is a Psycopg extension to the |DBAPI|. + + .. method:: setinputsizes(sizes) + + This method is exposed in compliance with the |DBAPI|. It currently + does nothing but it is safe to call it. + + + + .. |fetch*| replace:: `!fetch*()` + + .. _fetch*: + + .. rubric:: Results retrieval methods + + + The following methods are used to read data from the database after an + `~cursor.execute()` call. + + .. _cursor-iterable: + + .. note:: + + `cursor` objects are iterable, so, instead of calling + explicitly `~cursor.fetchone()` in a loop, the object itself can + be used: + + >>> cur.execute("SELECT * FROM test;") + >>> for record in cur: + ... print record + ... + (1, 100, "abc'def") + (2, None, 'dada') + (3, 42, 'bar') + + .. versionchanged:: 2.4 + iterating over a :ref:`named cursor ` + fetches `~cursor.itersize` records at time from the backend. + Previously only one record was fetched per roundtrip, resulting + in a large overhead. + + .. method:: fetchone() + + Fetch the next row of a query result set, returning a single tuple, + or `!None` when no more data is available: + + >>> cur.execute("SELECT * FROM test WHERE id = %s", (3,)) + >>> cur.fetchone() + (3, 42, 'bar') + + A `~psycopg2.ProgrammingError` is raised if the previous call + to |execute*|_ did not produce any result set or no call was issued + yet. + + + .. method:: fetchmany([size=cursor.arraysize]) + + Fetch the next set of rows of a query result, returning a list of + tuples. An empty list is returned when no more rows are available. + + The number of rows to fetch per call is specified by the parameter. + If it is not given, the cursor's `~cursor.arraysize` determines + the number of rows to be fetched. The method should try to fetch as + many rows as indicated by the size parameter. If this is not possible + due to the specified number of rows not being available, fewer rows + may be returned: + + >>> cur.execute("SELECT * FROM test;") + >>> cur.fetchmany(2) + [(1, 100, "abc'def"), (2, None, 'dada')] + >>> cur.fetchmany(2) + [(3, 42, 'bar')] + >>> cur.fetchmany(2) + [] + + A `~psycopg2.ProgrammingError` is raised if the previous call to + |execute*|_ did not produce any result set or no call was issued yet. + + Note there are performance considerations involved with the size + parameter. For optimal performance, it is usually best to use the + `~cursor.arraysize` attribute. If the size parameter is used, + then it is best for it to retain the same value from one + `fetchmany()` call to the next. + + + .. method:: fetchall() + + Fetch all (remaining) rows of a query result, returning them as a list + of tuples. An empty list is returned if there is no more record to + fetch. + + >>> cur.execute("SELECT * FROM test;") + >>> cur.fetchall() + [(1, 100, "abc'def"), (2, None, 'dada'), (3, 42, 'bar')] + + A `~psycopg2.ProgrammingError` is raised if the previous call to + |execute*|_ did not produce any result set or no call was issued yet. + + + .. method:: scroll(value [, mode='relative']) + + Scroll the cursor in the result set to a new position according + to mode. + + If `mode` is ``relative`` (default), value is taken as offset to + the current position in the result set, if set to ``absolute``, + value states an absolute target position. + + If the scroll operation would leave the result set, a + `~psycopg2.ProgrammingError` is raised and the cursor position is + not changed. + + The method can be used both for client-side cursors and + :ref:`server-side cursors `. + + .. note:: + + According to the |DBAPI|_, the exception raised for a cursor out + of bound should have been `!IndexError`. The best option is + probably to catch both exceptions in your code:: + + try: + cur.scroll(1000 * 1000) + except (ProgrammingError, IndexError), exc: + deal_with_it(exc) + + + .. attribute:: arraysize + + This read/write attribute specifies the number of rows to fetch at a + time with `~cursor.fetchmany()`. It defaults to 1 meaning to fetch + a single row at a time. + + + .. attribute:: itersize + + Read/write attribute specifying the number of rows to fetch from the + backend at each network roundtrip during :ref:`iteration + ` on a :ref:`named cursor `. The + default is 2000. + + .. versionadded:: 2.4 + + .. extension:: + + The `itersize` attribute is a Psycopg extension to the |DBAPI|. + + + .. attribute:: rowcount + + This read-only attribute specifies the number of rows that the last + |execute*|_ produced (for :abbr:`DQL (Data Query Language)` statements + like :sql:`SELECT`) or affected (for + :abbr:`DML (Data Manipulation Language)` statements like :sql:`UPDATE` + or :sql:`INSERT`). + + The attribute is -1 in case no |execute*| has been performed on + the cursor or the row count of the last operation if it can't be + determined by the interface. + + .. note:: + The |DBAPI|_ interface reserves to redefine the latter case to + have the object return `!None` instead of -1 in future versions + of the specification. + + + .. attribute:: rownumber + + This read-only attribute provides the current 0-based index of the + cursor in the result set or `!None` if the index cannot be + determined. + + The index can be seen as index of the cursor in a sequence (the result + set). The next fetch operation will fetch the row indexed by + `rownumber` in that sequence. + + + .. index:: oid + + .. attribute:: lastrowid + + This read-only attribute provides the OID of the last row inserted + by the cursor. If the table wasn't created with OID support or the + last operation is not a single record insert, the attribute is set to + `!None`. + + .. note:: + + PostgreSQL currently advices to not create OIDs on the tables and + the default for |CREATE-TABLE|__ is to not support them. The + |INSERT-RETURNING|__ syntax available from PostgreSQL 8.3 allows + more flexibility. + + .. |CREATE-TABLE| replace:: :sql:`CREATE TABLE` + .. __: http://www.postgresql.org/docs/current/static/sql-createtable.html + + .. |INSERT-RETURNING| replace:: :sql:`INSERT ... RETURNING` + .. __: http://www.postgresql.org/docs/current/static/sql-insert.html + + + .. attribute:: query + + Read-only attribute containing the body of the last query sent to the + backend (including bound arguments). `!None` if no query has been + executed yet: + + >>> cur.execute("INSERT INTO test (num, data) VALUES (%s, %s)", (42, 'bar')) + >>> cur.query + "INSERT INTO test (num, data) VALUES (42, E'bar')" + + .. extension:: + + The `query` attribute is a Psycopg extension to the |DBAPI|. + + + .. attribute:: statusmessage + + Read-only attribute containing the message returned by the last + command: + + >>> cur.execute("INSERT INTO test (num, data) VALUES (%s, %s)", (42, 'bar')) + >>> cur.statusmessage + 'INSERT 0 1' + + .. extension:: + + The `statusmessage` attribute is a Psycopg extension to the + |DBAPI|. + + + .. method:: cast(oid, s) + + Convert a value from the PostgreSQL string representation to a Python + object. + + Use the most specific of the typecasters registered by + `~psycopg2.extensions.register_type()`. + + .. versionadded:: 2.4 + + .. extension:: + + The `cast()` method is a Psycopg extension to the |DBAPI|. + + + .. attribute:: tzinfo_factory + + The time zone factory used to handle data types such as + :sql:`TIMESTAMP WITH TIME ZONE`. It should be a `~datetime.tzinfo` + object. A few implementations are available in the `psycopg2.tz` + module. + + + .. method:: nextset() + + This method is not supported (PostgreSQL does not have multiple data + sets) and will raise a `~psycopg2.NotSupportedError` exception. + + + .. method:: setoutputsize(size [, column]) + + This method is exposed in compliance with the |DBAPI|. It currently + does nothing but it is safe to call it. + + + + .. rubric:: COPY-related methods + + .. extension:: + + The :sql:`COPY` command is a PostgreSQL extension to the SQL standard. + As such, its support is a Psycopg extension to the |DBAPI|. + + .. method:: copy_from(file, table, sep='\\t', null='\\\\N', size=8192, columns=None) + + Read data *from* the file-like object *file* appending them to + the table named *table*. See :ref:`copy` for an overview. + + :param file: file-like object to read data from. It must have both + `!read()` and `!readline()` methods. + :param table: name of the table to copy data into. + :param sep: columns separator expected in the file. Defaults to a tab. + :param null: textual representation of :sql:`NULL` in the file. + The default is the two characters string ``\N``. + :param size: size of the buffer used to read from the file. + :param columns: iterable with name of the columns to import. + The length and types should match the content of the file to read. + If not specified, it is assumed that the entire table matches the + file structure. + + Example:: + + >>> f = StringIO("42\tfoo\n74\tbar\n") + >>> cur.copy_from(f, 'test', columns=('num', 'data')) + >>> cur.execute("select * from test where id > 5;") + >>> cur.fetchall() + [(6, 42, 'foo'), (7, 74, 'bar')] + + .. versionchanged:: 2.0.6 + added the *columns* parameter. + + .. versionchanged:: 2.4 + data read from files implementing the `io.TextIOBase` interface + are encoded in the connection `~connection.encoding` when sent to + the backend. + + .. method:: copy_to(file, table, sep='\\t', null='\\\\N', columns=None) + + Write the content of the table named *table* *to* the file-like + object *file*. See :ref:`copy` for an overview. + + :param file: file-like object to write data into. It must have a + `!write()` method. + :param table: name of the table to copy data from. + :param sep: columns separator expected in the file. Defaults to a tab. + :param null: textual representation of :sql:`NULL` in the file. + The default is the two characters string ``\N``. + :param columns: iterable with name of the columns to export. + If not specified, export all the columns. + + Example:: + + >>> cur.copy_to(sys.stdout, 'test', sep="|") + 1|100|abc'def + 2|\N|dada + ... + + .. versionchanged:: 2.0.6 + added the *columns* parameter. + + .. versionchanged:: 2.4 + data sent to files implementing the `io.TextIOBase` interface + are decoded in the connection `~connection.encoding` when read + from the backend. + + + .. method:: copy_expert(sql, file, size=8192) + + Submit a user-composed :sql:`COPY` statement. The method is useful to + handle all the parameters that PostgreSQL makes available (see + |COPY|__ command documentation). + + :param sql: the :sql:`COPY` statement to execute. + :param file: a file-like object; must be a readable file for + :sql:`COPY FROM` or an writable file for :sql:`COPY TO`. + :param size: size of the read buffer to be used in :sql:`COPY FROM`. + + Example: + + >>> cur.copy_expert("COPY test TO STDOUT WITH CSV HEADER", sys.stdout) + id,num,data + 1,100,abc'def + 2,,dada + ... + + .. |COPY| replace:: :sql:`COPY` + .. __: http://www.postgresql.org/docs/current/static/sql-copy.html + + .. versionadded:: 2.0.6 + + .. versionchanged:: 2.4 + files implementing the `io.TextIOBase` interface are dealt with + using Unicode data instead of bytes. + + +.. testcode:: + :hide: + + conn.rollback() diff -Nru psycopg2-2.0.13/doc/html/_sources/errorcodes.txt psycopg2-2.4.5/doc/html/_sources/errorcodes.txt --- psycopg2-2.0.13/doc/html/_sources/errorcodes.txt 1970-01-01 00:00:00.000000000 +0000 +++ psycopg2-2.4.5/doc/html/_sources/errorcodes.txt 2012-03-28 21:17:18.000000000 +0000 @@ -0,0 +1,76 @@ +`psycopg2.errorcodes` -- Error codes defined by PostgreSQL +=============================================================== + +.. sectionauthor:: Daniele Varrazzo + +.. index:: + single: Error; Codes + +.. module:: psycopg2.errorcodes + +.. testsetup:: * + + from psycopg2 import errorcodes + +.. versionadded:: 2.0.6 + +This module contains symbolic names for all PostgreSQL error codes and error +classes codes. Subclasses of `~psycopg2.Error` make the PostgreSQL error +code available in the `~psycopg2.Error.pgcode` attribute. + +From PostgreSQL documentation: + + All messages emitted by the PostgreSQL server are assigned five-character + error codes that follow the SQL standard's conventions for :sql:`SQLSTATE` + codes. Applications that need to know which error condition has occurred + should usually test the error code, rather than looking at the textual + error message. The error codes are less likely to change across + PostgreSQL releases, and also are not subject to change due to + localization of error messages. Note that some, but not all, of the error + codes produced by PostgreSQL are defined by the SQL standard; some + additional error codes for conditions not defined by the standard have + been invented or borrowed from other databases. + + According to the standard, the first two characters of an error code + denote a class of errors, while the last three characters indicate a + specific condition within that class. Thus, an application that does not + recognize the specific error code can still be able to infer what to do + from the error class. + +.. seealso:: `PostgreSQL Error Codes table`__ + + .. __: http://www.postgresql.org/docs/current/static/errcodes-appendix.html#ERRCODES-TABLE + + +An example of the available constants defined in the module: + + >>> errorcodes.CLASS_SYNTAX_ERROR_OR_ACCESS_RULE_VIOLATION + '42' + >>> errorcodes.UNDEFINED_TABLE + '42P01' + +Constants representing all the error values documented by PostgreSQL versions +between 8.1 and 9.1 are included in the module. + + +.. autofunction:: lookup(code) + + .. doctest:: + + >>> try: + ... cur.execute("SELECT ouch FROM aargh;") + ... except Exception, e: + ... pass + ... + >>> errorcodes.lookup(e.pgcode[:2]) + 'CLASS_SYNTAX_ERROR_OR_ACCESS_RULE_VIOLATION' + >>> errorcodes.lookup(e.pgcode) + 'UNDEFINED_TABLE' + + .. versionadded:: 2.0.14 + + +.. testcode:: + :hide: + + conn.rollback() diff -Nru psycopg2-2.0.13/doc/html/_sources/extensions.txt psycopg2-2.4.5/doc/html/_sources/extensions.txt --- psycopg2-2.0.13/doc/html/_sources/extensions.txt 1970-01-01 00:00:00.000000000 +0000 +++ psycopg2-2.4.5/doc/html/_sources/extensions.txt 2012-03-28 21:17:18.000000000 +0000 @@ -0,0 +1,691 @@ +`psycopg2.extensions` -- Extensions to the DB API +====================================================== + +.. sectionauthor:: Daniele Varrazzo + +.. module:: psycopg2.extensions + +.. testsetup:: * + + from psycopg2.extensions import AsIs, Binary, QuotedString, ISOLATION_LEVEL_AUTOCOMMIT + +The module contains a few objects and function extending the minimum set of +functionalities defined by the |DBAPI|_. + + +.. class:: connection + + Is the class usually returned by the `~psycopg2.connect()` function. + It is exposed by the `extensions` module in order to allow + subclassing to extend its behaviour: the subclass should be passed to the + `!connect()` function using the `connection_factory` parameter. + See also :ref:`subclassing-connection`. + + Subclasses should have constructor signature :samp:`({dsn}, {async}=0)`. + + For a complete description of the class, see `connection`. + +.. class:: cursor + + It is the class usually returnded by the `connection.cursor()` + method. It is exposed by the `extensions` module in order to allow + subclassing to extend its behaviour: the subclass should be passed to the + `!cursor()` method using the `cursor_factory` parameter. See + also :ref:`subclassing-cursor`. + + For a complete description of the class, see `cursor`. + +.. class:: lobject(conn [, oid [, mode [, new_oid [, new_file ]]]]) + + Wrapper for a PostgreSQL large object. See :ref:`large-objects` for an + overview. + + The class can be subclassed: see the `connection.lobject()` to know + how to specify a `!lobject` subclass. + + .. versionadded:: 2.0.8 + + .. attribute:: oid + + Database OID of the object. + + .. attribute:: mode + + The mode the database was open. See `connection.lobject()` for a + description of the available modes. + + .. method:: read(bytes=-1) + + Read a chunk of data from the current file position. If -1 (default) + read all the remaining data. + + The result is an Unicode string (decoded according to + `connection.encoding`) if the file was open in ``t`` mode, a bytes + string for ``b`` mode. + + .. versionchanged:: 2.4 + added Unicode support. + + .. method:: write(str) + + Write a string to the large object. Return the number of bytes + written. Unicode strings are encoded in the `connection.encoding` + before writing. + + .. versionchanged:: 2.4 + added Unicode support. + + .. method:: export(file_name) + + Export the large object content to the file system. + + The method uses the efficient |lo_export|_ libpq function. + + .. |lo_export| replace:: `!lo_export()` + .. _lo_export: http://www.postgresql.org/docs/current/static/lo-interfaces.html#LO-EXPORT + + .. method:: seek(offset, whence=0) + + Set the lobject current position. + + .. method:: tell() + + Return the lobject current position. + + .. method:: truncate(len=0) + + .. versionadded:: 2.2.0 + + Truncate the lobject to the given size. + + The method will only be available if Psycopg has been built against libpq + from PostgreSQL 8.3 or later and can only be used with PostgreSQL servers + running these versions. It uses the |lo_truncate|_ libpq function. + + .. |lo_truncate| replace:: `!lo_truncate()` + .. _lo_truncate: http://www.postgresql.org/docs/current/static/lo-interfaces.html#LO-TRUNCATE + + .. warning:: + + If Psycopg is built with |lo_truncate| support (i.e. if the + :program:`pg_config` used during setup is version >= 8.3), but at + runtime an older libpq is found, Psycopg will fail to import. See + :ref:`the lo_truncate FAQ ` about the problem. + + .. method:: close() + + Close the object. + + .. attribute:: closed + + Boolean attribute specifying if the object is closed. + + .. method:: unlink() + + Close the object and remove it from the database. + + +.. autoclass:: Notify(pid, channel, payload='') + :members: pid, channel, payload + + .. versionadded:: 2.3 + + +.. autoclass:: Xid(format_id, gtrid, bqual) + :members: format_id, gtrid, bqual, prepared, owner, database + + .. versionadded:: 2.3 + + .. automethod:: from_string(s) + + +.. autofunction:: set_wait_callback(f) + + .. versionadded:: 2.2.0 + +.. autofunction:: get_wait_callback() + + .. versionadded:: 2.2.0 + + +.. _sql-adaptation-objects: + +SQL adaptation protocol objects +------------------------------- + +Psycopg provides a flexible system to adapt Python objects to the SQL syntax +(inspired to the :pep:`246`), allowing serialization in PostgreSQL. See +:ref:`adapting-new-types` for a detailed description. The following objects +deal with Python objects adaptation: + +.. function:: adapt(obj) + + Return the SQL representation of *obj* as a string. Raise a + `~psycopg2.ProgrammingError` if how to adapt the object is unknown. + In order to allow new objects to be adapted, register a new adapter for it + using the `register_adapter()` function. + + The function is the entry point of the adaptation mechanism: it can be + used to write adapters for complex objects by recursively calling + `!adapt()` on its components. + +.. function:: register_adapter(class, adapter) + + Register a new adapter for the objects of class *class*. + + *adapter* should be a function taking a single argument (the object + to adapt) and returning an object conforming the `ISQLQuote` + protocol (e.g. exposing a `!getquoted()` method). The `AsIs` is + often useful for this task. + + Once an object is registered, it can be safely used in SQL queries and by + the `adapt()` function. + +.. class:: ISQLQuote(wrapped_object) + + Represents the SQL adaptation protocol. Objects conforming this protocol + should implement a `getquoted()` and optionally a `prepare()` method. + + Adapters may subclass `!ISQLQuote`, but is not necessary: it is + enough to expose a `!getquoted()` method to be conforming. + + .. attribute:: _wrapped + + The wrapped object passes to the constructor + + .. method:: getquoted() + + Subclasses or other conforming objects should return a valid SQL + string representing the wrapped object. In Python 3 the SQL must be + returned in a `!bytes` object. The `!ISQLQuote` implementation does + nothing. + + .. method:: prepare(conn) + + Prepare the adapter for a connection. The method is optional: if + implemented, it will be invoked before `!getquoted()` with the + connection to adapt for as argument. + + A conform object can implement this method if the SQL + representation depends on any server parameter, such as the server + version or the :envvar:`standard_conforming_string` setting. Container + objects may store the connection and use it to recursively prepare + contained objects: see the implementation for + `psycopg2.extensions.SQL_IN` for a simple example. + + +.. class:: AsIs(object) + + Adapter conform to the `ISQLQuote` protocol useful for objects + whose string representation is already valid as SQL representation. + + .. method:: getquoted() + + Return the `str()` conversion of the wrapped object. + + >>> AsIs(42).getquoted() + '42' + +.. class:: QuotedString(str) + + Adapter conform to the `ISQLQuote` protocol for string-like + objects. + + .. method:: getquoted() + + Return the string enclosed in single quotes. Any single quote + appearing in the the string is escaped by doubling it according to SQL + string constants syntax. Backslashes are escaped too. + + >>> QuotedString(r"O'Reilly").getquoted() + "'O''Reilly'" + +.. class:: Binary(str) + + Adapter conform to the `ISQLQuote` protocol for binary objects. + + .. method:: getquoted() + + Return the string enclosed in single quotes. It performs the same + escaping of the `QuotedString` adapter, plus it knows how to + escape non-printable chars. + + >>> Binary("\x00\x08\x0F").getquoted() + "'\\\\000\\\\010\\\\017'" + + .. versionchanged:: 2.0.14 + previously the adapter was not exposed by the `extensions` + module. In older versions it can be imported from the implementation + module `!psycopg2._psycopg`. + + + +.. class:: Boolean + Float + SQL_IN + + Specialized adapters for builtin objects. + +.. class:: DateFromPy + TimeFromPy + TimestampFromPy + IntervalFromPy + + Specialized adapters for Python datetime objects. + +.. class:: DateFromMx + TimeFromMx + TimestampFromMx + IntervalFromMx + + Specialized adapters for `mx.DateTime`_ objects. + +.. data:: adapters + + Dictionary of the currently registered object adapters. Use + `register_adapter()` to add an adapter for a new type. + + + +Database types casting functions +-------------------------------- + +These functions are used to manipulate type casters to convert from PostgreSQL +types to Python objects. See :ref:`type-casting-from-sql-to-python` for +details. + +.. function:: new_type(oids, name, adapter) + + Create a new type caster to convert from a PostgreSQL type to a Python + object. The object created must be registered using + `register_type()` to be used. + + :param oids: tuple of OIDs of the PostgreSQL type to convert. + :param name: the name of the new type adapter. + :param adapter: the adaptation function. + + The object OID can be read from the `cursor.description` attribute + or by querying from the PostgreSQL catalog. + + *adapter* should have signature :samp:`fun({value}, {cur})` where + *value* is the string representation returned by PostgreSQL and + *cur* is the cursor from which data are read. In case of + :sql:`NULL`, *value* will be `!None`. The adapter should return the + converted object. + + See :ref:`type-casting-from-sql-to-python` for an usage example. + + +.. function:: new_array_type(oids, name, base_caster) + + Create a new type caster to convert from a PostgreSQL array type to a list + of Python object. The object created must be registered using + `register_type()` to be used. + + :param oids: tuple of OIDs of the PostgreSQL type to convert. It should + probably be the oid of the array type (e.g. the ``typarray`` field in + the ``pg_type`` table. + :param name: the name of the new type adapter. + :param base_caster: a Psycopg typecaster, e.g. created using the + `new_type()` function. The caster should be able to parse a single + item of the desired type. + + .. versionadded:: 2.4.3 + + .. _cast-array-unknown: + + .. note:: + + The function can be used to create a generic array typecaster, + returning a list of strings: just use the `~psycopg2.STRING` as base + typecaster. For instance, if you want to receive from the database an + array of :sql:`macaddr`, each address represented by string, you can + use:: + + psycopg2.extensions.register_type( + psycopg2.extensions.new_array_type( + (1040,), 'MACADDR[]', psycopg2.STRING)) + + +.. function:: register_type(obj [, scope]) + + Register a type caster created using `new_type()`. + + If *scope* is specified, it should be a `connection` or a + `cursor`: the type caster will be effective only limited to the + specified object. Otherwise it will be globally registered. + + +.. data:: string_types + + The global register of type casters. + + +.. index:: + single: Encoding; Mapping + +.. data:: encodings + + Mapping from `PostgreSQL encoding`__ names to `Python codec`__ names. + Used by Psycopg when adapting or casting unicode strings. See + :ref:`unicode-handling`. + + .. __: http://www.postgresql.org/docs/current/static/multibyte.html + .. __: http://docs.python.org/library/codecs.html#standard-encodings + + + +.. index:: + single: Exceptions; Additional + +Additional exceptions +--------------------- + +The module exports a few exceptions in addition to the :ref:`standard ones +` defined by the |DBAPI|_. + +.. exception:: QueryCanceledError + + (subclasses `~psycopg2.OperationalError`) + + Error related to SQL query cancellation. It can be trapped specifically to + detect a timeout. + + .. versionadded:: 2.0.7 + + +.. exception:: TransactionRollbackError + + (subclasses `~psycopg2.OperationalError`) + + Error causing transaction rollback (deadlocks, serialisation failures, + etc). It can be trapped specifically to detect a deadlock. + + .. versionadded:: 2.0.7 + + + +.. index:: + pair: Isolation level; Constants + +.. _isolation-level-constants: + +Isolation level constants +------------------------- + +Psycopg2 `connection` objects hold informations about the PostgreSQL +`transaction isolation level`_. The current transaction level can be read +from the `~connection.isolation_level` attribute. The default isolation +level is :sql:`READ COMMITTED`. A different isolation level con be set +through the `~connection.set_isolation_level()` method. The level can be +set to one of the following constants: + +.. data:: ISOLATION_LEVEL_AUTOCOMMIT + + No transaction is started when command are issued and no + `~connection.commit()` or `~connection.rollback()` is required. + Some PostgreSQL command such as :sql:`CREATE DATABASE` or :sql:`VACUUM` + can't run into a transaction: to run such command use:: + + >>> conn.set_isolation_level(ISOLATION_LEVEL_AUTOCOMMIT) + + See also :ref:`transactions-control`. + +.. data:: ISOLATION_LEVEL_READ_UNCOMMITTED + + The :sql:`READ UNCOMMITTED` isolation level is defined in the SQL standard + but not available in the |MVCC| model of PostgreSQL: it is replaced by the + stricter :sql:`READ COMMITTED`. + +.. data:: ISOLATION_LEVEL_READ_COMMITTED + + This is usually the the default PostgreSQL value, but a different default + may be set in the database configuration. + + A new transaction is started at the first `~cursor.execute()` command on a + cursor and at each new `!execute()` after a `~connection.commit()` or a + `~connection.rollback()`. The transaction runs in the PostgreSQL + :sql:`READ COMMITTED` isolation level: a :sql:`SELECT` query sees only + data committed before the query began; it never sees either uncommitted + data or changes committed during query execution by concurrent + transactions. + + .. seealso:: `Read Committed Isolation Level`__ in PostgreSQL + documentation. + + .. __: http://www.postgresql.org/docs/current/static/transaction-iso.html#XACT-READ-COMMITTED + +.. data:: ISOLATION_LEVEL_REPEATABLE_READ + + As in `!ISOLATION_LEVEL_READ_COMMITTED`, a new transaction is started at + the first `~cursor.execute()` command. Transactions run at a + :sql:`REPEATABLE READ` isolation level: all the queries in a transaction + see a snapshot as of the start of the transaction, not as of the start of + the current query within the transaction. However applications using this + level must be prepared to retry transactions due to serialization + failures. + + While this level provides a guarantee that each transaction sees a + completely stable view of the database, this view will not necessarily + always be consistent with some serial (one at a time) execution of + concurrent transactions of the same level. + + .. versionchanged:: 2.4.2 + The value was an alias for `!ISOLATION_LEVEL_SERIALIZABLE` before. The + two levels are distinct since PostgreSQL 9.1 + + .. seealso:: `Repeatable Read Isolation Level`__ in PostgreSQL + documentation. + + .. __: http://www.postgresql.org/docs/current/static/transaction-iso.html#XACT-REPEATABLE-READ + +.. data:: ISOLATION_LEVEL_SERIALIZABLE + + As in `!ISOLATION_LEVEL_READ_COMMITTED`, a new transaction is started at + the first `~cursor.execute()` command. Transactions run at a + :sql:`SERIALIZABLE` isolation level. This is the strictest transactions + isolation level, equivalent to having the transactions executed serially + rather than concurrently. However applications using this level must be + prepared to retry reansactions due to serialization failures. + + Starting from PostgreSQL 9.1, this mode monitors for conditions which + could make execution of a concurrent set of serializable transactions + behave in a manner inconsistent with all possible serial (one at a time) + executions of those transaction. In previous version the behaviour was the + same of the :sql:`REPEATABLE READ` isolation level. + + .. seealso:: `Serializable Isolation Level`__ in PostgreSQL documentation. + + .. __: http://www.postgresql.org/docs/current/static/transaction-iso.html#XACT-SERIALIZABLE + + + +.. index:: + pair: Transaction status; Constants + +.. _transaction-status-constants: + +Transaction status constants +---------------------------- + +These values represent the possible status of a transaction: the current value +can be read using the `connection.get_transaction_status()` method. + +.. data:: TRANSACTION_STATUS_IDLE + + The session is idle and there is no current transaction. + +.. data:: TRANSACTION_STATUS_ACTIVE + + A command is currently in progress. + +.. data:: TRANSACTION_STATUS_INTRANS + + The session is idle in a valid transaction block. + +.. data:: TRANSACTION_STATUS_INERROR + + The session is idle in a failed transaction block. + +.. data:: TRANSACTION_STATUS_UNKNOWN + + Reported if the connection with the server is bad. + + + +.. index:: + pair: Connection status; Constants + +.. _connection-status-constants: + +Connection status constants +--------------------------- + +These values represent the possible status of a connection: the current value +can be read from the `~connection.status` attribute. + +It is possible to find the connection in other status than the one shown below. +Those are the only states in which a working connection is expected to be found +during the execution of regular Python client code: other states are for +internal usage and Python code should not rely on them. + +.. data:: STATUS_READY + + Connection established. No transaction in progress. + +.. data:: STATUS_BEGIN + + Connection established. A transaction is currently in progress. + +.. data:: STATUS_IN_TRANSACTION + + An alias for `STATUS_BEGIN` + +.. data:: STATUS_PREPARED + + The connection has been prepared for the second phase in a :ref:`two-phase + commit ` transaction. The connection can't be used to send commands + to the database until the transaction is finished with + `~connection.tpc_commit()` or `~connection.tpc_rollback()`. + + .. versionadded:: 2.3 + + + +.. index:: + pair: Poll status; Constants + +.. _poll-constants: + +Poll constants +-------------- + +.. versionadded:: 2.2.0 + +These values can be returned by `connection.poll()` during asynchronous +connection and communication. They match the values in the libpq enum +`!PostgresPollingStatusType`. See :ref:`async-support` and +:ref:`green-support`. + +.. data:: POLL_OK + + The data being read is available, or the file descriptor is ready for + writing: reading or writing will not block. + +.. data:: POLL_READ + + Some data is being read from the backend, but it is not available yet on + the client and reading would block. Upon receiving this value, the client + should wait for the connection file descriptor to be ready *for reading*. + For example:: + + select.select([conn.fileno()], [], []) + +.. data:: POLL_WRITE + + Some data is being sent to the backend but the connection file descriptor + can't currently accept new data. Upon receiving this value, the client + should wait for the connection file descriptor to be ready *for writing*. + For example:: + + select.select([], [conn.fileno()], []) + +.. data:: POLL_ERROR + + There was a problem during connection polling. This value should actually + never be returned: in case of poll error usually an exception containing + the relevant details is raised. + + + +Additional database types +------------------------- + +The `!extensions` module includes typecasters for many standard +PostgreSQL types. These objects allow the conversion of returned data into +Python objects. All the typecasters are automatically registered, except +`UNICODE` and `UNICODEARRAY`: you can register them using +`register_type()` in order to receive Unicode objects instead of strings +from the database. See :ref:`unicode-handling` for details. + +.. data:: BOOLEAN + DATE + DECIMAL + FLOAT + INTEGER + INTERVAL + LONGINTEGER + TIME + UNICODE + + Typecasters for basic types. Note that a few other ones (`~psycopg2.BINARY`, + `~psycopg2.DATETIME`, `~psycopg2.NUMBER`, `~psycopg2.ROWID`, + `~psycopg2.STRING`) are exposed by the `psycopg2` module for |DBAPI|_ + compliance. + +.. data:: BINARYARRAY + BOOLEANARRAY + DATEARRAY + DATETIMEARRAY + DECIMALARRAY + FLOATARRAY + INTEGERARRAY + INTERVALARRAY + LONGINTEGERARRAY + ROWIDARRAY + STRINGARRAY + TIMEARRAY + UNICODEARRAY + + Typecasters to convert arrays of sql types into Python lists. + +.. data:: PYDATE + PYDATETIME + PYINTERVAL + PYTIME + PYDATEARRAY + PYDATETIMEARRAY + PYINTERVALARRAY + PYTIMEARRAY + + Typecasters to convert time-related data types to Python `!datetime` + objects. + +.. data:: MXDATE + MXDATETIME + MXINTERVAL + MXTIME + MXDATEARRAY + MXDATETIMEARRAY + MXINTERVALARRAY + MXTIMEARRAY + + Typecasters to convert time-related data types to `mx.DateTime`_ objects. + Only available if Psycopg was compiled with `!mx` support. + +.. versionchanged:: 2.2.0 + previously the `DECIMAL` typecaster and the specific time-related + typecasters (`!PY*` and `!MX*`) were not exposed by the `extensions` + module. In older versions they can be imported from the implementation + module `!psycopg2._psycopg`. + diff -Nru psycopg2-2.0.13/doc/html/_sources/extras.txt psycopg2-2.4.5/doc/html/_sources/extras.txt --- psycopg2-2.0.13/doc/html/_sources/extras.txt 1970-01-01 00:00:00.000000000 +0000 +++ psycopg2-2.4.5/doc/html/_sources/extras.txt 2012-03-28 21:17:18.000000000 +0000 @@ -0,0 +1,296 @@ +`psycopg2.extras` -- Miscellaneous goodies for Psycopg 2 +============================================================= + +.. sectionauthor:: Daniele Varrazzo + +.. module:: psycopg2.extras + +.. testsetup:: + + import psycopg2.extras + from psycopg2.extras import Inet + + create_test_table() + +This module is a generic place used to hold little helper functions and +classes until a better place in the distribution is found. + + +.. index:: + pair: Cursor; Dictionary + +.. _dict-cursor: + + +Connection and cursor subclasses +-------------------------------- + +A few objects that change the way the results are returned by the cursor or +modify the object behavior in some other way. Typically `!connection` +subclasses are passed as *connection_factory* argument to +`~psycopg2.connect()` so that the connection will generate the matching +`!cursor` subclass. Alternatively a `!cursor` subclass can be used one-off by +passing it as the *cursor_factory* argument to the `~connection.cursor()` +method of a regular `!connection`. + +Dictionary-like cursor +^^^^^^^^^^^^^^^^^^^^^^ + +The dict cursors allow to access to the retrieved records using an iterface +similar to the Python dictionaries instead of the tuples. + + >>> dict_cur = conn.cursor(cursor_factory=psycopg2.extras.DictCursor) + >>> dict_cur.execute("INSERT INTO test (num, data) VALUES(%s, %s)", + ... (100, "abc'def")) + >>> dict_cur.execute("SELECT * FROM test") + >>> rec = dict_cur.fetchone() + >>> rec['id'] + 1 + >>> rec['num'] + 100 + >>> rec['data'] + "abc'def" + +The records still support indexing as the original tuple: + + >>> rec[2] + "abc'def" + + +.. autoclass:: DictCursor + +.. autoclass:: DictConnection + +.. autoclass:: DictRow + + +Real dictionary cursor +^^^^^^^^^^^^^^^^^^^^^^ + +.. autoclass:: RealDictCursor + +.. autoclass:: RealDictConnection + +.. autoclass:: RealDictRow + + + +.. index:: + pair: Cursor; namedtuple + +`namedtuple` cursor +^^^^^^^^^^^^^^^^^^^^ + +.. versionadded:: 2.3 + +These objects require :py:func:`collections.namedtuple` to be found, so it is +available out-of-the-box only from Python 2.6. Anyway, the namedtuple +implementation is compatible with previous Python versions, so all you +have to do is to `download it`__ and make it available where we +expect it to be... :: + + from somewhere import namedtuple + import collections + collections.namedtuple = namedtuple + from psycopg.extras import NamedTupleConnection + # ... + +.. __: http://code.activestate.com/recipes/500261-named-tuples/ + +.. autoclass:: NamedTupleCursor + +.. autoclass:: NamedTupleConnection + + +.. index:: + pair: Cursor; Logging + +Logging cursor +^^^^^^^^^^^^^^ + +.. autoclass:: LoggingConnection + :members: initialize,filter + +.. autoclass:: LoggingCursor + + +.. autoclass:: MinTimeLoggingConnection + :members: initialize,filter + +.. autoclass:: MinTimeLoggingCursor + + + +.. index:: + single: Data types; Additional + +Additional data types +--------------------- + + +.. _adapt-hstore: + +.. index:: + pair: hstore; Data types + pair: dict; Adaptation + +Hstore data type +^^^^^^^^^^^^^^^^ + +.. versionadded:: 2.3 + +The |hstore|_ data type is a key-value store embedded in PostgreSQL. It has +been available for several server versions but with the release 9.0 it has +been greatly improved in capacity and usefulness with the addiction of many +functions. It supports GiST or GIN indexes allowing search by keys or +key/value pairs as well as regular BTree indexes for equality, uniqueness etc. + +Psycopg can convert Python `!dict` objects to and from |hstore| structures. +Only dictionaries with string/unicode keys and values are supported. `!None` +is also allowed as value but not as a key. Psycopg uses a more efficient |hstore| +representation when dealing with PostgreSQL 9.0 but previous server versions +are supported as well. By default the adapter/typecaster are disabled: they +can be enabled using the `register_hstore()` function. + +.. autofunction:: register_hstore + +.. |hstore| replace:: :sql:`hstore` +.. _hstore: http://www.postgresql.org/docs/current/static/hstore.html + + + +.. _adapt-composite: + +.. index:: + pair: Composite types; Data types + pair: tuple; Adaptation + pair: namedtuple; Adaptation + +Composite types casting +^^^^^^^^^^^^^^^^^^^^^^^ + +.. versionadded:: 2.4 + +Using `register_composite()` it is possible to cast a PostgreSQL composite +type (either created with the |CREATE TYPE|_ command or implicitly defined +after a table row type) into a Python named tuple, or into a regular tuple if +:py:func:`collections.namedtuple` is not found. + +.. |CREATE TYPE| replace:: :sql:`CREATE TYPE` +.. _CREATE TYPE: http://www.postgresql.org/docs/current/static/sql-createtype.html + +.. doctest:: + + >>> cur.execute("CREATE TYPE card AS (value int, suit text);") + >>> psycopg2.extras.register_composite('card', cur) + + + >>> cur.execute("select (8, 'hearts')::card") + >>> cur.fetchone()[0] + card(value=8, suit='hearts') + +Nested composite types are handled as expected, but the type of the composite +components must be registered as well. + +.. doctest:: + + >>> cur.execute("CREATE TYPE card_back AS (face card, back text);") + >>> psycopg2.extras.register_composite('card_back', cur) + + + >>> cur.execute("select ((8, 'hearts'), 'blue')::card_back") + >>> cur.fetchone()[0] + card_back(face=card(value=8, suit='hearts'), back='blue') + +Adaptation from Python tuples to composite types is automatic instead and +requires no adapter registration. + +.. autofunction:: register_composite + +.. autoclass:: CompositeCaster + + + +.. index:: + pair: UUID; Data types + +UUID data type +^^^^^^^^^^^^^^ + +.. versionadded:: 2.0.9 +.. versionchanged:: 2.0.13 added UUID array support. + +.. doctest:: + + >>> psycopg2.extras.register_uuid() + + + >>> # Python UUID can be used in SQL queries + >>> import uuid + >>> my_uuid = uuid.UUID('{12345678-1234-5678-1234-567812345678}') + >>> psycopg2.extensions.adapt(my_uuid).getquoted() + "'12345678-1234-5678-1234-567812345678'::uuid" + + >>> # PostgreSQL UUID are transformed into Python UUID objects. + >>> cur.execute("SELECT 'a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11'::uuid") + >>> cur.fetchone()[0] + UUID('a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11') + + +.. autofunction:: register_uuid + +.. autoclass:: UUID_adapter + + + +.. index:: + pair: INET; Data types + +:sql:`inet` data type +^^^^^^^^^^^^^^^^^^^^^^ + +.. versionadded:: 2.0.9 +.. versionchanged:: 2.4.5 added inet array support. + +.. doctest:: + + >>> psycopg2.extras.register_inet() + + + >>> cur.mogrify("SELECT %s", (Inet('127.0.0.1/32'),)) + "SELECT E'127.0.0.1/32'::inet" + + >>> cur.execute("SELECT '192.168.0.1/24'::inet") + >>> cur.fetchone()[0].addr + '192.168.0.1/24' + + +.. autofunction:: register_inet + +.. autoclass:: Inet + + + +.. index:: + single: Time zones; Fractional + +Fractional time zones +--------------------- + +.. autofunction:: register_tstz_w_secs + + .. versionadded:: 2.0.9 + + .. versionchanged:: 2.2.2 + function is no-op: see :ref:`tz-handling`. + +.. index:: + pair: Example; Coroutine; + + + +Coroutine support +----------------- + +.. autofunction:: wait_select(conn) + diff -Nru psycopg2-2.0.13/doc/html/_sources/faq.txt psycopg2-2.4.5/doc/html/_sources/faq.txt --- psycopg2-2.0.13/doc/html/_sources/faq.txt 1970-01-01 00:00:00.000000000 +0000 +++ psycopg2-2.4.5/doc/html/_sources/faq.txt 2012-03-28 21:17:18.000000000 +0000 @@ -0,0 +1,245 @@ +Frequently Asked Questions +========================== + +.. sectionauthor:: Daniele Varrazzo + +Here are a few gotchas you may encounter using `psycopg2`. Feel free to +suggest new entries! + + +Problems with transactions handling +----------------------------------- + +.. _faq-idle-in-transaction: +.. cssclass:: faq + +Why does `!psycopg2` leave database sessions "idle in transaction"? + Psycopg normally starts a new transaction the first time a query is + executed, e.g. calling `cursor.execute()`, even if the command is a + :sql:`SELECT`. The transaction is not closed until an explicit + `~connection.commit()` or `~connection.rollback()`. + + If you are writing a long-living program, you should probably make sure to + call one of the transaction closing methods before leaving the connection + unused for a long time (which may also be a few seconds, depending on the + concurrency level in your database). Alternatively you can use a + connection in `~connection.autocommit` mode to avoid a new transaction to + be started at the first command. + + +.. _faq-transaction-aborted: +.. cssclass:: faq + +I receive the error *current transaction is aborted, commands ignored until end of transaction block* and can't do anything else! + There was a problem *in the previous* command to the database, which + resulted in an error. The database will not recover automatically from + this condition: you must run a `~connection.rollback()` before sending + new commands to the session (if this seems too harsh, remember that + PostgreSQL supports nested transactions using the |SAVEPOINT|_ command). + + .. |SAVEPOINT| replace:: :sql:`SAVEPOINT` + .. _SAVEPOINT: http://www.postgresql.org/docs/current/static/sql-savepoint.html + + +.. _faq-transaction-aborted-multiprocess: +.. cssclass:: faq + +Why do I get the error *current transaction is aborted, commands ignored until end of transaction block* when I use `!multiprocessing` (or any other forking system) and not when use `!threading`? + Psycopg's connections can't be shared across processes (but are thread + safe). If you are forking the Python process make sure to create a new + connection in each forked child. See :ref:`thread-safety` for further + informations. + + +Problems with type conversions +------------------------------ + +.. _faq-cant-adapt: +.. cssclass:: faq + +Why does `!cursor.execute()` raise the exception *can't adapt*? + Psycopg converts Python objects in a SQL string representation by looking + at the object class. The exception is raised when you are trying to pass + as query parameter an object for which there is no adapter registered for + its class. See :ref:`adapting-new-types` for informations. + + +.. _faq-number-required: +.. cssclass:: faq + +I can't pass an integer or a float parameter to my query: it says *a number is required*, but *it is* a number! + In your query string, you always have to use ``%s`` placeholders, + event when passing a number. All Python objects are converted by Psycopg + in their SQL representation, so they get passed to the query as strings. + See :ref:`query-parameters`. :: + + >>> cur.execute("INSERT INTO numbers VALUES (%d)", (42,)) # WRONG + >>> cur.execute("INSERT INTO numbers VALUES (%s)", (42,)) # correct + + +.. _faq-not-all-arguments-converted: +.. cssclass:: faq + +I try to execute a query but it fails with the error *not all arguments converted during string formatting* (or *object does not support indexing*). Why? + Psycopg always require positional arguments to be passed as a sequence, even + when the query takes a single parameter. And remember that to make a + single item tuple in Python you need a comma! See :ref:`query-parameters`. + :: + + >>> cur.execute("INSERT INTO foo VALUES (%s)", "bar") # WRONG + >>> cur.execute("INSERT INTO foo VALUES (%s)", ("bar")) # WRONG + >>> cur.execute("INSERT INTO foo VALUES (%s)", ("bar",)) # correct + >>> cur.execute("INSERT INTO foo VALUES (%s)", ["bar"]) # correct + + +.. _faq-unicode: +.. cssclass:: faq + +My database is Unicode, but I receive all the strings as UTF-8 `!str`. Can I receive `!unicode` objects instead? + The following magic formula will do the trick:: + + psycopg2.extensions.register_type(psycopg2.extensions.UNICODE) + psycopg2.extensions.register_type(psycopg2.extensions.UNICODEARRAY) + + See :ref:`unicode-handling` for the gory details. + + +.. _faq-float: +.. cssclass:: faq + +Psycopg converts :sql:`decimal`\/\ :sql:`numeric` database types into Python `!Decimal` objects. Can I have `!float` instead? + You can register a customized adapter for PostgreSQL decimal type:: + + DEC2FLOAT = psycopg2.extensions.new_type( + psycopg2.extensions.DECIMAL.values, + 'DEC2FLOAT', + lambda value, curs: float(value) if value is not None else None) + psycopg2.extensions.register_type(DEC2FLOAT) + + See :ref:`type-casting-from-sql-to-python` to read the relevant + documentation. If you find `!psycopg2.extensions.DECIMAL` not avalable, use + `!psycopg2._psycopg.DECIMAL` instead. + + +.. _faq-bytea-9.0: +.. cssclass:: faq + +Transferring binary data from PostgreSQL 9.0 doesn't work. + PostgreSQL 9.0 uses by default `the "hex" format`__ to transfer + :sql:`bytea` data: the format can't be parsed by the libpq 8.4 and + earlier. The problem is solved in Psycopg 2.4.1, that uses its own parser + for the :sql:`bytea` format. For previous Psycopg releases, three options + to solve the problem are: + + - set the bytea_output__ parameter to ``escape`` in the server; + - execute the database command ``SET bytea_output TO escape;`` in the + session before reading binary data; + - upgrade the libpq library on the client to at least 9.0. + + .. __: http://www.postgresql.org/docs/current/static/datatype-binary.html + .. __: http://www.postgresql.org/docs/current/static/runtime-config-client.html#GUC-BYTEA-OUTPUT + + +.. _faq-array: +.. cssclass:: faq + +Arrays of *TYPE* are not casted to list. + Arrays are only casted to list when their oid is known, and an array + typecaster is registered for them. If there is no typecaster, the array is + returned unparsed from PostgreSQL (e.g. ``{a,b,c}``). It is easy to create + a generic arrays typecaster, returning a list of array: an example is + provided in the `~psycopg2.extensions.new_array_type()` documentation. + + +Best practices +-------------- + +.. _faq-reuse-cursors: +.. cssclass:: faq + +When should I save and re-use a cursor as opposed to creating a new one as needed? + Cursors are lightweight objects and creating lots of them should not pose + any kind of problem. But note that cursors used to fetch result sets will + cache the data and use memory in proportion to the result set size. Our + suggestion is to almost always create a new cursor and dispose old ones as + soon as the data is not required anymore (call `~cursor.close()` on + them.) The only exception are tight loops where one usually use the same + cursor for a whole bunch of :sql:`INSERT`\s or :sql:`UPDATE`\s. + + +.. _faq-reuse-connections: +.. cssclass:: faq + +When should I save and re-use a connection as opposed to creating a new one as needed? + Creating a connection can be slow (think of SSL over TCP) so the best + practice is to create a single connection and keep it open as long as + required. It is also good practice to rollback or commit frequently (even + after a single :sql:`SELECT` statement) to make sure the backend is never + left "idle in transaction". See also `psycopg2.pool` for lightweight + connection pooling. + + +.. _faq-named-cursors: +.. cssclass:: faq + +What are the advantages or disadvantages of using named cursors? + The only disadvantages is that they use up resources on the server and + that there is a little overhead because a at least two queries (one to + create the cursor and one to fetch the initial result set) are issued to + the backend. The advantage is that data is fetched one chunk at a time: + using small `~cursor.fetchmany()` values it is possible to use very + little memory on the client and to skip or discard parts of the result set. + + +Problems compiling and deploying psycopg2 +----------------------------------------- + +.. _faq-python-h: +.. cssclass:: faq + +I can't compile `!psycopg2`: the compiler says *error: Python.h: No such file or directory*. What am I missing? + You need to install a Python development package: it is usually called + ``python-dev``. + + +.. _faq-libpq-fe-h: +.. cssclass:: faq + +I can't compile `!psycopg2`: the compiler says *error: libpq-fe.h: No such file or directory*. What am I missing? + You need to install the development version of the libpq: the package is + usually called ``libpq-dev``. + + +.. _faq-lo_truncate: +.. cssclass:: faq + +`!psycopg2` raises `!ImportError` with message *_psycopg.so: undefined symbol: lo_truncate* when imported. + This means that Psycopg has been compiled with |lo_truncate|_ support, + which means that the libpq used at compile time was version >= 8.3, but at + runtime an older libpq library is found. You can use:: + + $ ldd /path/to/packages/psycopg2/_psycopg.so | grep libpq + + to find what is the version used at runtime. + + You can avoid the problem by using the same version of the + :program:`pg_config` at install time and the libpq at runtime. + + .. |lo_truncate| replace:: `!lo_truncate()` + .. _lo_truncate: http://www.postgresql.org/docs/current/static/lo-interfaces.html#LO-TRUNCATE + + +.. _faq-import-mod_wsgi: +.. cssclass:: faq + +Psycopg raises *ImportError: cannot import name tz* on import in mod_wsgi / ASP, but it works fine otherwise. + If `!psycopg2` is installed in an egg_ (e.g. because installed by + :program:`easy_install`), the user running the program may be unable to + write in the `eggs cache`__. Set the env variable + :envvar:`PYTHON_EGG_CACHE` to a writable directory. With modwsgi you can + use the WSGIPythonEggs__ directive. + + .. _egg: http://peak.telecommunity.com/DevCenter/PythonEggs + .. __: http://stackoverflow.com/questions/2192323/what-is-the-python-egg-cache-python-egg-cache + .. __: http://code.google.com/p/modwsgi/wiki/ConfigurationDirectives#WSGIPythonEggs + diff -Nru psycopg2-2.0.13/doc/html/_sources/index.txt psycopg2-2.4.5/doc/html/_sources/index.txt --- psycopg2-2.0.13/doc/html/_sources/index.txt 1970-01-01 00:00:00.000000000 +0000 +++ psycopg2-2.4.5/doc/html/_sources/index.txt 2012-03-28 21:17:18.000000000 +0000 @@ -0,0 +1,73 @@ +================================================= +Psycopg -- PostgreSQL database adapter for Python +================================================= + +.. sectionauthor:: Daniele Varrazzo + +Psycopg_ is a PostgreSQL_ database adapter for the Python_ programming +language. Its main features are that it supports the full Python |DBAPI|_ +and it is thread safe (threads can share the connections). It was designed for +heavily multi-threaded applications that create and destroy lots of cursors and +make a large number of concurrent :sql:`INSERT`\ s or :sql:`UPDATE`\ s. +The Psycopg distribution includes ZPsycopgDA, a Zope_ Database Adapter. + +Psycopg 2 is mostly implemented in C as a libpq_ wrapper, resulting in being +both efficient and secure. It features client-side and :ref:`server-side +` cursors, :ref:`asynchronous communication +` and :ref:`notifications `, |COPY-TO-FROM|__ +support, and a flexible :ref:`objects adaptation system +`. Many basic Python types are supported +out-of-the-box and mapped to matching PostgreSQL data types, such as strings +(both bytes and Unicode), numbers (ints, longs, floats, decimals), booleans and +datetime objects (both built-in and `mx.DateTime`_), several types of +:ref:`binary objects `. Also available are mappings between lists +and PostgreSQL arrays of any supported type, between :ref:`dictionaries and +PostgreSQL hstores `, and between :ref:`tuples/namedtuples and +PostgreSQL composite types `. + +Psycopg 2 is both Unicode and Python 3 friendly. + + +.. _Psycopg: http://initd.org/psycopg/ +.. _PostgreSQL: http://www.postgresql.org/ +.. _Python: http://www.python.org/ +.. _Zope: http://www.zope.org/ +.. _libpq: http://www.postgresql.org/docs/current/static/libpq.html +.. |COPY-TO-FROM| replace:: :sql:`COPY TO/COPY FROM` +.. __: http://www.postgresql.org/docs/current/static/sql-copy.html + + +.. rubric:: Contents + +.. toctree:: + :maxdepth: 2 + + usage + module + connection + cursor + advanced + extensions + tz + pool + extras + errorcodes + faq + + +.. ifconfig:: builder != 'text' + + .. rubric:: Indices and tables + + * :ref:`genindex` + * :ref:`search` + + +.. ifconfig:: todo_include_todos + + .. note:: + + **To Do items in the documentation** + + .. todolist:: + diff -Nru psycopg2-2.0.13/doc/html/_sources/module.txt psycopg2-2.4.5/doc/html/_sources/module.txt --- psycopg2-2.0.13/doc/html/_sources/module.txt 1970-01-01 00:00:00.000000000 +0000 +++ psycopg2-2.4.5/doc/html/_sources/module.txt 2012-03-28 21:17:18.000000000 +0000 @@ -0,0 +1,324 @@ +The `psycopg2` module content +================================== + +.. sectionauthor:: Daniele Varrazzo + +.. module:: psycopg2 + +The module interface respects the standard defined in the |DBAPI|_. + +.. index:: + single: Connection string + double: Connection; Parameters + single: Username; Connection + single: Password; Connection + single: Host; Connection + single: Port; Connection + single: DSN (Database Source Name) + +.. function:: connect(dsn or params [, connection_factory] [, async=0]) + + Create a new database session and return a new `connection` object. + + The connection parameters can be specified either as a string:: + + conn = psycopg2.connect("dbname=test user=postgres password=secret") + + or using a set of keyword arguments:: + + conn = psycopg2.connect(database="test", user="postgres", password="secret") + + The basic connection parameters are: + + - `!dbname` -- the database name (only in dsn string) + - `!database` -- the database name (only as keyword argument) + - `!user` -- user name used to authenticate + - `!password` -- password used to authenticate + - `!host` -- database host address (defaults to UNIX socket if not provided) + - `!port` -- connection port number (defaults to 5432 if not provided) + + Any other connection parameter supported by the client library/server can + be passed either in the connection string or as keyword. See the + PostgreSQL documentation for a complete `list of supported parameters`__. + Also note that the same parameters can be passed to the client library + using `environment variables`__. + + .. __: http://www.postgresql.org/docs/current/static/libpq-connect.html#LIBPQ-PQCONNECTDBPARAMS + .. __: http://www.postgresql.org/docs/current/static/libpq-envars.html + + Using the *connection_factory* parameter a different class or + connections factory can be specified. It should be a callable object + taking a *dsn* argument. See :ref:`subclassing-connection` for + details. + + Using *async*\=1 an asynchronous connection will be created: see + :ref:`async-support` to know about advantages and limitations. + + .. versionchanged:: 2.4.3 + any keyword argument is passed to the connection. Previously only the + basic parameters (plus `!sslmode`) were supported as keywords. + + .. extension:: + + The parameters *connection_factory* and *async* are Psycopg extensions + to the |DBAPI|. + + +.. data:: apilevel + + String constant stating the supported DB API level. For `psycopg2` is + ``2.0``. + +.. data:: threadsafety + + Integer constant stating the level of thread safety the interface + supports. For `psycopg2` is ``2``, i.e. threads can share the module + and the connection. See :ref:`thread-safety` for details. + +.. data:: paramstyle + + String constant stating the type of parameter marker formatting expected + by the interface. For `psycopg2` is ``pyformat``. See also + :ref:`query-parameters`. + + + +.. index:: + single: Exceptions; DB API + +.. _dbapi-exceptions: + +Exceptions +---------- + +In compliance with the |DBAPI|_, the module makes informations about errors +available through the following exceptions: + +.. exception:: Warning + + Exception raised for important warnings like data truncations while + inserting, etc. It is a subclass of the Python `~exceptions.StandardError`. + +.. exception:: Error + + Exception that is the base class of all other error exceptions. You can + use this to catch all errors with one single `!except` statement. Warnings + are not considered errors and thus not use this class as base. It + is a subclass of the Python `!StandardError`. + + .. attribute:: pgerror + + String representing the error message returned by the backend, + `!None` if not available. + + .. attribute:: pgcode + + String representing the error code returned by the backend, `!None` + if not available. The `~psycopg2.errorcodes` module contains + symbolic constants representing PostgreSQL error codes. + + .. doctest:: + :options: +NORMALIZE_WHITESPACE + + >>> try: + ... cur.execute("SELECT * FROM barf") + ... except Exception, e: + ... pass + + >>> e.pgcode + '42P01' + >>> print e.pgerror + ERROR: relation "barf" does not exist + LINE 1: SELECT * FROM barf + ^ + .. attribute:: cursor + + The cursor the exception was raised from; `None` if not applicable. + + .. extension:: + + The `~Error.pgerror`, `~Error.pgcode`, and `~Error.cursor` attributes + are Psycopg extensions. + + +.. exception:: InterfaceError + + Exception raised for errors that are related to the database interface + rather than the database itself. It is a subclass of `Error`. + +.. exception:: DatabaseError + + Exception raised for errors that are related to the database. It is a + subclass of `Error`. + +.. exception:: DataError + + Exception raised for errors that are due to problems with the processed + data like division by zero, numeric value out of range, etc. It is a + subclass of `DatabaseError`. + +.. exception:: OperationalError + + Exception raised for errors that are related to the database's operation + and not necessarily under the control of the programmer, e.g. an + unexpected disconnect occurs, the data source name is not found, a + transaction could not be processed, a memory allocation error occurred + during processing, etc. It is a subclass of `DatabaseError`. + +.. exception:: IntegrityError + + Exception raised when the relational integrity of the database is + affected, e.g. a foreign key check fails. It is a subclass of + `DatabaseError`. + +.. exception:: InternalError + + Exception raised when the database encounters an internal error, e.g. the + cursor is not valid anymore, the transaction is out of sync, etc. It is a + subclass of `DatabaseError`. + +.. exception:: ProgrammingError + + Exception raised for programming errors, e.g. table not found or already + exists, syntax error in the SQL statement, wrong number of parameters + specified, etc. It is a subclass of `DatabaseError`. + +.. exception:: NotSupportedError + + Exception raised in case a method or database API was used which is not + supported by the database, e.g. requesting a `!rollback()` on a + connection that does not support transaction or has transactions turned + off. It is a subclass of `DatabaseError`. + + +.. extension:: + + Psycopg may raise a few other, more specialized, exceptions: currently + `~psycopg2.extensions.QueryCanceledError` and + `~psycopg2.extensions.TransactionRollbackError` are defined. These + exceptions are not exposed by the main `!psycopg2` module but are + made available by the `~psycopg2.extensions` module. All the + additional exceptions are subclasses of standard |DBAPI| exceptions, so + trapping them specifically is not required. + + +This is the exception inheritance layout: + +.. parsed-literal:: + + `!StandardError` + \|__ `Warning` + \|__ `Error` + \|__ `InterfaceError` + \|__ `DatabaseError` + \|__ `DataError` + \|__ `OperationalError` + \| \|__ `psycopg2.extensions.QueryCanceledError` + \| \|__ `psycopg2.extensions.TransactionRollbackError` + \|__ `IntegrityError` + \|__ `InternalError` + \|__ `ProgrammingError` + \|__ `NotSupportedError` + + + +.. _type-objects-and-constructors: + +Type Objects and Constructors +----------------------------- + +.. note:: + + This section is mostly copied verbatim from the |DBAPI|_ + specification. While these objects are exposed in compliance to the + DB API, Psycopg offers very accurate tools to convert data between Python + and PostgreSQL formats. See :ref:`adapting-new-types` and + :ref:`type-casting-from-sql-to-python` + +Many databases need to have the input in a particular format for +binding to an operation's input parameters. For example, if an +input is destined for a DATE column, then it must be bound to the +database in a particular string format. Similar problems exist +for "Row ID" columns or large binary items (e.g. blobs or RAW +columns). This presents problems for Python since the parameters +to the .execute*() method are untyped. When the database module +sees a Python string object, it doesn't know if it should be bound +as a simple CHAR column, as a raw BINARY item, or as a DATE. + +To overcome this problem, a module must provide the constructors +defined below to create objects that can hold special values. +When passed to the cursor methods, the module can then detect the +proper type of the input parameter and bind it accordingly. + +A Cursor Object's description attribute returns information about +each of the result columns of a query. The type_code must compare +equal to one of Type Objects defined below. Type Objects may be +equal to more than one type code (e.g. DATETIME could be equal to +the type codes for date, time and timestamp columns; see the +Implementation Hints below for details). + +The module exports the following constructors and singletons: + +.. function:: Date(year,month,day) + + This function constructs an object holding a date value. + +.. function:: Time(hour,minute,second) + + This function constructs an object holding a time value. + +.. function:: Timestamp(year,month,day,hour,minute,second) + + This function constructs an object holding a time stamp value. + +.. function:: DateFromTicks(ticks) + + This function constructs an object holding a date value from the given + ticks value (number of seconds since the epoch; see the documentation of + the standard Python time module for details). + +.. function:: TimeFromTicks(ticks) + + This function constructs an object holding a time value from the given + ticks value (number of seconds since the epoch; see the documentation of + the standard Python time module for details). + +.. function:: TimestampFromTicks(ticks) + + This function constructs an object holding a time stamp value from the + given ticks value (number of seconds since the epoch; see the + documentation of the standard Python time module for details). + +.. function:: Binary(string) + + This function constructs an object capable of holding a binary (long) + string value. + + +.. data:: STRING + + This type object is used to describe columns in a database that are + string-based (e.g. CHAR). + +.. data:: BINARY + + This type object is used to describe (long) binary columns in a database + (e.g. LONG, RAW, BLOBs). + +.. data:: NUMBER + + This type object is used to describe numeric columns in a database. + +.. data:: DATETIME + + This type object is used to describe date/time columns in a database. + +.. data:: ROWID + + This type object is used to describe the "Row ID" column in a database. + + +.. testcode:: + :hide: + + conn.rollback() diff -Nru psycopg2-2.0.13/doc/html/_sources/pool.txt psycopg2-2.4.5/doc/html/_sources/pool.txt --- psycopg2-2.0.13/doc/html/_sources/pool.txt 1970-01-01 00:00:00.000000000 +0000 +++ psycopg2-2.4.5/doc/html/_sources/pool.txt 2012-03-28 21:17:18.000000000 +0000 @@ -0,0 +1,64 @@ +`psycopg2.pool` -- Connections pooling +====================================== + +.. sectionauthor:: Daniele Varrazzo + +.. index:: + pair: Connection; Pooling + +.. module:: psycopg2.pool + +Creating new PostgreSQL connections can be an expensive operation. This +module offers a few pure Python classes implementing simple connection pooling +directly in the client application. + +.. class:: AbstractConnectionPool(minconn, maxconn, \*args, \*\*kwargs) + + Base class implementing generic key-based pooling code. + + New *minconn* connections are created automatically. The pool will support + a maximum of about *maxconn* connections. *\*args* and *\*\*kwargs* are + passed to the `~psycopg2.connect()` function. + + The following methods are expected to be implemented by subclasses: + + .. method:: getconn(key=None) + + Get a free connection and assign it to *key* if not `!None`. + + .. method:: putconn(conn, key=None, close=False) + + Put away a connection. + + If *close* is `!True`, discard the connection from the pool. + + .. method:: closeall + + Close all the connections handled by the pool. + + Note that all the connections are closed, including ones + eventually in use by the application. + + +The following classes are `AbstractConnectionPool` subclasses ready to +be used. + +.. autoclass:: SimpleConnectionPool + + .. note:: This pool class is useful only for single-threaded applications. + + +.. index:: Multithread; Connection pooling + +.. autoclass:: ThreadedConnectionPool + + .. note:: This pool class can be safely used in multi-threaded applications. + + +.. autoclass:: PersistentConnectionPool + + .. note:: + + This pool class is mostly designed to interact with Zope and probably + not useful in generic applications. + diff -Nru psycopg2-2.0.13/doc/html/_sources/tz.txt psycopg2-2.4.5/doc/html/_sources/tz.txt --- psycopg2-2.0.13/doc/html/_sources/tz.txt 1970-01-01 00:00:00.000000000 +0000 +++ psycopg2-2.4.5/doc/html/_sources/tz.txt 2012-03-28 21:17:18.000000000 +0000 @@ -0,0 +1,16 @@ +`psycopg2.tz` -- ``tzinfo`` implementations for Psycopg 2 +=============================================================== + +.. sectionauthor:: Daniele Varrazzo + +.. module:: psycopg2.tz + +This module holds two different tzinfo implementations that can be used as the +`tzinfo` argument to `~datetime.datetime` constructors, directly passed to +Psycopg functions or used to set the `cursor.tzinfo_factory` attribute in +cursors. + +.. autoclass:: psycopg2.tz.FixedOffsetTimezone + +.. autoclass:: psycopg2.tz.LocalTimezone + diff -Nru psycopg2-2.0.13/doc/html/_sources/usage.txt psycopg2-2.4.5/doc/html/_sources/usage.txt --- psycopg2-2.0.13/doc/html/_sources/usage.txt 1970-01-01 00:00:00.000000000 +0000 +++ psycopg2-2.4.5/doc/html/_sources/usage.txt 2012-03-28 21:17:18.000000000 +0000 @@ -0,0 +1,780 @@ +Basic module usage +================== + +.. sectionauthor:: Daniele Varrazzo + +.. index:: + pair: Example; Usage + +The basic Psycopg usage is common to all the database adapters implementing +the |DBAPI|_ protocol. Here is an interactive session showing some of the +basic commands:: + + >>> import psycopg2 + + # Connect to an existing database + >>> conn = psycopg2.connect("dbname=test user=postgres") + + # Open a cursor to perform database operations + >>> cur = conn.cursor() + + # Execute a command: this creates a new table + >>> cur.execute("CREATE TABLE test (id serial PRIMARY KEY, num integer, data varchar);") + + # Pass data to fill a query placeholders and let Psycopg perform + # the correct conversion (no more SQL injections!) + >>> cur.execute("INSERT INTO test (num, data) VALUES (%s, %s)", + ... (100, "abc'def")) + + # Query the database and obtain data as Python objects + >>> cur.execute("SELECT * FROM test;") + >>> cur.fetchone() + (1, 100, "abc'def") + + # Make the changes to the database persistent + >>> conn.commit() + + # Close communication with the database + >>> cur.close() + >>> conn.close() + + +The main entry points of Psycopg are: + +- The function `~psycopg2.connect()` creates a new database session and + returns a new `connection` instance. + +- The class `connection` encapsulates a database session. It allows to: + + - create new `cursor`\s using the `~connection.cursor()` method to + execute database commands and queries, + + - terminate the session using the methods `~connection.commit()` or + `~connection.rollback()`. + +- The class `cursor` allows interaction with the database: + + - send commands to the database using methods such as `~cursor.execute()` + and `~cursor.executemany()`, + + - retrieve data from the database :ref:`by iteration ` or + using methods such as `~cursor.fetchone()`, `~cursor.fetchmany()`, + `~cursor.fetchall()`. + + + +.. index:: + pair: Query; Parameters + +.. _query-parameters: + +Passing parameters to SQL queries +--------------------------------- + +Psycopg casts Python variables to SQL literals by type. Many standard Python types +are already `adapted to the correct SQL representation`__. + +.. __: python-types-adaptation_ + +Example: the Python function call:: + + >>> cur.execute( + ... """INSERT INTO some_table (an_int, a_date, a_string) + ... VALUES (%s, %s, %s);""", + ... (10, datetime.date(2005, 11, 18), "O'Reilly")) + +is converted into the SQL command:: + + INSERT INTO some_table (an_int, a_date, a_string) + VALUES (10, '2005-11-18', 'O''Reilly'); + +Named arguments are supported too using :samp:`%({name})s` placeholders. +Using named arguments the values can be passed to the query in any order and +many placeholders can use the same values:: + + >>> cur.execute( + ... """INSERT INTO some_table (an_int, a_date, another_date, a_string) + ... VALUES (%(int)s, %(date)s, %(date)s, %(str)s);""", + ... {'int': 10, 'str': "O'Reilly", 'date': datetime.date(2005, 11, 18)}) + +While the mechanism resembles regular Python strings manipulation, there are a +few subtle differences you should care about when passing parameters to a +query: + +- The Python string operator ``%`` is not used: the `~cursor.execute()` + method accepts a tuple or dictionary of values as second parameter. + |sql-warn|__. + + .. |sql-warn| replace:: **Never** use ``%`` or ``+`` to merge values + into queries + + .. __: sql-injection_ + +- The variables placeholder must *always be a* ``%s``, even if a different + placeholder (such as a ``%d`` for integers or ``%f`` for floats) may look + more appropriate:: + + >>> cur.execute("INSERT INTO numbers VALUES (%d)", (42,)) # WRONG + >>> cur.execute("INSERT INTO numbers VALUES (%s)", (42,)) # correct + +- For positional variables binding, *the second argument must always be a + sequence*, even if it contains a single variable. And remember that Python + requires a comma to create a single element tuple:: + + >>> cur.execute("INSERT INTO foo VALUES (%s)", "bar") # WRONG + >>> cur.execute("INSERT INTO foo VALUES (%s)", ("bar")) # WRONG + >>> cur.execute("INSERT INTO foo VALUES (%s)", ("bar",)) # correct + >>> cur.execute("INSERT INTO foo VALUES (%s)", ["bar"]) # correct + +- Only variable values should be bound via this method: it shouldn't be used + to set table or field names. For these elements, ordinary string formatting + should be used before running `~cursor.execute()`. + + + +.. index:: Security, SQL injection + +.. _sql-injection: + +The problem with the query parameters +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The SQL representation for many data types is often not the same of the Python +string representation. The classic example is with single quotes in +strings: SQL uses them as string constants bounds and requires them to be +escaped, whereas in Python single quotes can be left unescaped in strings +bounded by double quotes. For this reason a naïve approach to the composition +of query strings, e.g. using string concatenation, is a recipe for terrible +problems:: + + >>> SQL = "INSERT INTO authors (name) VALUES ('%s');" # NEVER DO THIS + >>> data = ("O'Reilly", ) + >>> cur.execute(SQL % data) # THIS WILL FAIL MISERABLY + ProgrammingError: syntax error at or near "Reilly" + LINE 1: INSERT INTO authors (name) VALUES ('O'Reilly') + ^ + +If the variable containing the data to be sent to the database comes from an +untrusted source (e.g. a form published on a web site) an attacker could +easily craft a malformed string, either gaining access to unauthorized data or +performing destructive operations on the database. This form of attack is +called `SQL injection`_ and is known to be one of the most widespread forms of +attack to servers. Before continuing, please print `this page`__ as a memo and +hang it onto your desk. + +.. _SQL injection: http://en.wikipedia.org/wiki/SQL_injection +.. __: http://xkcd.com/327/ + +Psycopg can `automatically convert Python objects to and from SQL +literals`__: using this feature your code will be more robust and +reliable. We must stress this point: + +.. __: python-types-adaptation_ + +.. warning:: + + Never, **never**, **NEVER** use Python string concatenation (``+``) or + string parameters interpolation (``%``) to pass variables to a SQL query + string. Not even at gunpoint. + +The correct way to pass variables in a SQL command is using the second +argument of the `~cursor.execute()` method:: + + >>> SQL = "INSERT INTO authors (name) VALUES (%s);" # Note: no quotes + >>> data = ("O'Reilly", ) + >>> cur.execute(SQL, data) # Note: no % operator + + + +.. index:: + single: Adaptation + pair: Objects; Adaptation + single: Data types; Adaptation + +.. _python-types-adaptation: + +Adaptation of Python values to SQL types +---------------------------------------- + +Many standards Python types are adapted into SQL and returned as Python +objects when a query is executed. + +If you need to convert other Python types to and from PostgreSQL data types, +see :ref:`adapting-new-types` and :ref:`type-casting-from-sql-to-python`. You +can also find a few other specialized adapters in the `psycopg2.extras` +module. + +In the following examples the method `~cursor.mogrify()` is used to show +the SQL string that would be sent to the database. + +.. _adapt-consts: + +.. index:: + pair: None; Adaptation + single: NULL; Adaptation + pair: Boolean; Adaptation + +- Python `None` and boolean values `True` and `False` are converted into the + proper SQL literals:: + + >>> cur.mogrify("SELECT %s, %s, %s;", (None, True, False)) + >>> 'SELECT NULL, true, false;' + +.. _adapt-numbers: + +.. index:: + single: Adaptation; numbers + single: Integer; Adaptation + single: Float; Adaptation + single: Decimal; Adaptation + +- Numeric objects: `int`, `long`, `float`, `~decimal.Decimal` are converted in + the PostgreSQL numerical representation:: + + >>> cur.mogrify("SELECT %s, %s, %s, %s;", (10, 10L, 10.0, Decimal("10.00"))) + >>> 'SELECT 10, 10, 10.0, 10.00;' + +.. _adapt-string: + +.. index:: + pair: Strings; Adaptation + single: Unicode; Adaptation + +- String types: `str`, `unicode` are converted in SQL string syntax. + `!unicode` objects (`!str` in Python 3) are encoded in the connection + `~connection.encoding` to be sent to the backend: trying to send a character + not supported by the encoding will result in an error. Received data can be + converted either as `!str` or `!unicode`: see :ref:`unicode-handling`. + +.. _adapt-binary: + +.. index:: + single: Buffer; Adaptation + single: bytea; Adaptation + single: bytes; Adaptation + single: bytearray; Adaptation + single: memoryview; Adaptation + single: Binary string + +- Binary types: Python types representing binary objects are converted into + PostgreSQL binary string syntax, suitable for :sql:`bytea` fields. Such + types are `buffer` (only available in Python 2), `memoryview` (available + from Python 2.7), `bytearray` (available from Python 2.6) and `bytes` + (only from Python 3: the name is available from Python 2.6 but it's only an + alias for the type `!str`). Any object implementing the `Revised Buffer + Protocol`__ should be usable as binary type where the protocol is supported + (i.e. from Python 2.6). Received data is returned as `!buffer` (in Python 2) + or `!memoryview` (in Python 3). + + .. __: http://www.python.org/dev/peps/pep-3118/ + + .. versionchanged:: 2.4 + only strings were supported before. + + .. versionchanged:: 2.4.1 + can parse the 'hex' format from 9.0 servers without relying on the + version of the client library. + + .. note:: + + In Python 2, if you have binary data in a `!str` object, you can pass them + to a :sql:`bytea` field using the `psycopg2.Binary` wrapper:: + + mypic = open('picture.png', 'rb').read() + curs.execute("insert into blobs (file) values (%s)", + (psycopg2.Binary(mypic),)) + + .. warning:: + + Since version 9.0 PostgreSQL uses by default `a new "hex" format`__ to + emit :sql:`bytea` fields. Starting from Psycopg 2.4.1 the format is + correctly supported. If you use a previous version you will need some + extra care when receiving bytea from PostgreSQL: you must have at least + libpq 9.0 installed on the client or alternatively you can set the + `bytea_output`__ configuration parameter to ``escape``, either in the + server configuration file or in the client session (using a query such as + ``SET bytea_output TO escape;``) before receiving binary data. + + .. __: http://www.postgresql.org/docs/current/static/datatype-binary.html + .. __: http://www.postgresql.org/docs/current/static/runtime-config-client.html#GUC-BYTEA-OUTPUT + +.. _adapt-date: + +.. index:: + single: Adaptation; Date/Time objects + single: Date objects; Adaptation + single: Time objects; Adaptation + single: Interval objects; Adaptation + single: mx.DateTime; Adaptation + +- Date and time objects: builtin `~datetime.datetime`, `~datetime.date`, + `~datetime.time`, `~datetime.timedelta` are converted into PostgreSQL's + :sql:`timestamp`, :sql:`date`, :sql:`time`, :sql:`interval` data types. + Time zones are supported too. The Egenix `mx.DateTime`_ objects are adapted + the same way:: + + >>> dt = datetime.datetime.now() + >>> dt + datetime.datetime(2010, 2, 8, 1, 40, 27, 425337) + + >>> cur.mogrify("SELECT %s, %s, %s;", (dt, dt.date(), dt.time())) + "SELECT '2010-02-08T01:40:27.425337', '2010-02-08', '01:40:27.425337';" + + >>> cur.mogrify("SELECT %s;", (dt - datetime.datetime(2010,1,1),)) + "SELECT '38 days 6027.425337 seconds';" + +.. _adapt-list: + +.. index:: + single: Array; Adaptation + double: Lists; Adaptation + +- Python lists are converted into PostgreSQL :sql:`ARRAY`\ s:: + + >>> cur.mogrify("SELECT %s;", ([10, 20, 30], )) + 'SELECT ARRAY[10, 20, 30];' + + .. note:: + + Reading back from PostgreSQL, arrays are converted to list of Python + objects as expected, but only if the types are known one. Arrays of + unknown types are returned as represented by the database (e.g. + ``{a,b,c}``). You can easily create a typecaster for :ref:`array of + unknown types `. + +.. _adapt-tuple: + +.. index:: + double: Tuple; Adaptation + single: IN operator + +- Python tuples are converted in a syntax suitable for the SQL :sql:`IN` + operator and to represent a composite type:: + + >>> cur.mogrify("SELECT %s IN %s;", (10, (10, 20, 30))) + 'SELECT 10 IN (10, 20, 30);' + + .. note:: + + SQL doesn't allow an empty list in the IN operator, so your code should + guard against empty tuples. + + If you want PostgreSQL composite types to be converted into a Python + tuple/namedtuple you can use the `~psycopg2.extras.register_composite()` + function. + + .. versionadded:: 2.0.6 + the tuple :sql:`IN` adaptation. + + .. versionchanged:: 2.0.14 + the tuple :sql:`IN` adapter is always active. In previous releases it + was necessary to import the `~psycopg2.extensions` module to have it + registered. + + .. versionchanged:: 2.3 + `~collections.namedtuple` instances are adapted like regular tuples and + can thus be used to represent composite types. + +.. _adapt-dict: + +.. index:: + single: dict; Adaptation + single: hstore; Adaptation + +- Python dictionaries are converted into the |hstore|_ data type. By default + the adapter is not enabled: see `~psycopg2.extras.register_hstore()` for + further details. + + .. |hstore| replace:: :sql:`hstore` + .. _hstore: http://www.postgresql.org/docs/current/static/hstore.html + + .. versionadded:: 2.3 + the :sql:`hstore` adaptation. + + +.. index:: + single: Unicode + +.. _unicode-handling: + +Unicode handling +^^^^^^^^^^^^^^^^ + +Psycopg can exchange Unicode data with a PostgreSQL database. Python +`!unicode` objects are automatically *encoded* in the client encoding +defined on the database connection (the `PostgreSQL encoding`__, available in +`connection.encoding`, is translated into a `Python codec`__ using the +`~psycopg2.extensions.encodings` mapping):: + + >>> print u, type(u) + àèìòù€ + + >>> cur.execute("INSERT INTO test (num, data) VALUES (%s,%s);", (74, u)) + +.. __: http://www.postgresql.org/docs/current/static/multibyte.html +.. __: http://docs.python.org/library/codecs.html#standard-encodings + +When reading data from the database, in Python 2 the strings returned are +usually 8 bit `!str` objects encoded in the database client encoding:: + + >>> print conn.encoding + UTF8 + + >>> cur.execute("SELECT data FROM test WHERE num = 74") + >>> x = cur.fetchone()[0] + >>> print x, type(x), repr(x) + àèìòù€ '\xc3\xa0\xc3\xa8\xc3\xac\xc3\xb2\xc3\xb9\xe2\x82\xac' + + >>> conn.set_client_encoding('LATIN9') + + >>> cur.execute("SELECT data FROM test WHERE num = 74") + >>> x = cur.fetchone()[0] + >>> print type(x), repr(x) + '\xe0\xe8\xec\xf2\xf9\xa4' + +In Python 3 instead the strings are automatically *decoded* in the connection +`~connection.encoding`, as the `!str` object can represent Unicode characters. +In Python 2 you must register a :ref:`typecaster +` in order to receive `!unicode` objects:: + + >>> psycopg2.extensions.register_type(psycopg2.extensions.UNICODE, cur) + + >>> cur.execute("SELECT data FROM test WHERE num = 74") + >>> x = cur.fetchone()[0] + >>> print x, type(x), repr(x) + àèìòù€ u'\xe0\xe8\xec\xf2\xf9\u20ac' + +In the above example, the `~psycopg2.extensions.UNICODE` typecaster is +registered only on the cursor. It is also possible to register typecasters on +the connection or globally: see the function +`~psycopg2.extensions.register_type()` and +:ref:`type-casting-from-sql-to-python` for details. + +.. note:: + + In Python 2, if you want to uniformly receive all your database input in + Unicode, you can register the related typecasters globally as soon as + Psycopg is imported:: + + import psycopg2 + import psycopg2.extensions + psycopg2.extensions.register_type(psycopg2.extensions.UNICODE) + psycopg2.extensions.register_type(psycopg2.extensions.UNICODEARRAY) + + and then forget about this story. + + +.. index:: + single: Time Zones + +.. _tz-handling: + +Time zones handling +^^^^^^^^^^^^^^^^^^^ + +The PostgreSQL type :sql:`timestamp with time zone` is converted into Python +`~datetime.datetime` objects with a `~datetime.datetime.tzinfo` attribute set +to a `~psycopg2.tz.FixedOffsetTimezone` instance. + + >>> cur.execute("SET TIME ZONE 'Europe/Rome';") # UTC + 1 hour + >>> cur.execute("SELECT '2010-01-01 10:30:45'::timestamptz;") + >>> cur.fetchone()[0].tzinfo + psycopg2.tz.FixedOffsetTimezone(offset=60, name=None) + +Note that only time zones with an integer number of minutes are supported: +this is a limitation of the Python `datetime` module. A few historical time +zones had seconds in the UTC offset: these time zones will have the offset +rounded to the nearest minute, with an error of up to 30 seconds. + + >>> cur.execute("SET TIME ZONE 'Asia/Calcutta';") # offset was +5:53:20 + >>> cur.execute("SELECT '1930-01-01 10:30:45'::timestamptz;") + >>> cur.fetchone()[0].tzinfo + psycopg2.tz.FixedOffsetTimezone(offset=353, name=None) + +.. versionchanged:: 2.2.2 + timezones with seconds are supported (with rounding). Previously such + timezones raised an error. In order to deal with them in previous + versions use `psycopg2.extras.register_tstz_w_secs()`. + + +.. index:: Transaction, Begin, Commit, Rollback, Autocommit, Read only + +.. _transactions-control: + +Transactions control +-------------------- + +In Psycopg transactions are handled by the `connection` class. By +default, the first time a command is sent to the database (using one of the +`cursor`\ s created by the connection), a new transaction is created. +The following database commands will be executed in the context of the same +transaction -- not only the commands issued by the first cursor, but the ones +issued by all the cursors created by the same connection. Should any command +fail, the transaction will be aborted and no further command will be executed +until a call to the `~connection.rollback()` method. + +The connection is responsible to terminate its transaction, calling either the +`~connection.commit()` or `~connection.rollback()` method. Committed +changes are immediately made persistent into the database. Closing the +connection using the `~connection.close()` method or destroying the +connection object (using `!del` or letting it fall out of scope) +will result in an implicit `!rollback()` call. + +It is possible to set the connection in *autocommit* mode: this way all the +commands executed will be immediately committed and no rollback is possible. A +few commands (e.g. :sql:`CREATE DATABASE`, :sql:`VACUUM`...) require to be run +outside any transaction: in order to be able to run these commands from +Psycopg, the session must be in autocommit mode: you can use the +`~connection.autocommit` property (`~connection.set_isolation_level()` in +older versions). + +.. warning:: + + By default even a simple :sql:`SELECT` will start a transaction: in + long-running programs, if no further action is taken, the session will + remain "idle in transaction", a condition non desiderable for several + reasons (locks are held by the session, tables bloat...). For long lived + scripts, either make sure to terminate a transaction as soon as possible or + use an autocommit connection. + +A few other transaction properties can be set session-wide by the +`!connection`: for instance it is possible to have read-only transactions or +change the isolation level. See the `~connection.set_session()` method for all +the details. + + +.. index:: + pair: Server side; Cursor + pair: Named; Cursor + pair: DECLARE; SQL command + pair: FETCH; SQL command + pair: MOVE; SQL command + +.. _server-side-cursors: + +Server side cursors +------------------- + +When a database query is executed, the Psycopg `cursor` usually fetches +all the records returned by the backend, transferring them to the client +process. If the query returned an huge amount of data, a proportionally large +amount of memory will be allocated by the client. + +If the dataset is too large to be practically handled on the client side, it is +possible to create a *server side* cursor. Using this kind of cursor it is +possible to transfer to the client only a controlled amount of data, so that a +large dataset can be examined without keeping it entirely in memory. + +Server side cursor are created in PostgreSQL using the |DECLARE|_ command and +subsequently handled using :sql:`MOVE`, :sql:`FETCH` and :sql:`CLOSE` commands. + +Psycopg wraps the database server side cursor in *named cursors*. A named +cursor is created using the `~connection.cursor()` method specifying the +*name* parameter. Such cursor will behave mostly like a regular cursor, +allowing the user to move in the dataset using the `~cursor.scroll()` +method and to read the data using `~cursor.fetchone()` and +`~cursor.fetchmany()` methods. + +Named cursors are also :ref:`iterable ` like regular cursors. +Note however that before Psycopg 2.4 iteration was performed fetching one +record at time from the backend, resulting in a large overhead. The attribute +`~cursor.itersize` now controls how many records are fetched at time +during the iteration: the default value of 2000 allows to fetch about 100KB +per roundtrip assuming records of 10-20 columns of mixed number and strings; +you may decrease this value if you are dealing with huge records. + +Named cursors are usually created :sql:`WITHOUT HOLD`, meaning they live only +as long as the current transaction. Trying to fetch from a named cursor after +a `~connection.commit()` or to create a named cursor when the `connection` +transaction isolation level is set to `AUTOCOMMIT` will result in an exception. +It is possible to create a :sql:`WITH HOLD` cursor by specifying a `!True` +value for the `withhold` parameter to `~connection.cursor()` or by setting the +`~cursor.withhold` attribute to `!True` before calling `~cursor.execute()` on +the cursor. It is extremely important to always `~cursor.close()` such cursors, +otherwise they will continue to hold server-side resources until the connection +will be eventually closed. Also note that while :sql:`WITH HOLD` cursors +lifetime extends well after `~connection.commit()`, calling +`~connection.rollback()` will automatically close the cursor. + +.. note:: + + It is also possible to use a named cursor to consume a cursor created + in some other way than using the |DECLARE| executed by + `~cursor.execute()`. For example, you may have a PL/pgSQL function + returning a cursor:: + + CREATE FUNCTION reffunc(refcursor) RETURNS refcursor AS $$ + BEGIN + OPEN $1 FOR SELECT col FROM test; + RETURN $1; + END; + $$ LANGUAGE plpgsql; + + You can read the cursor content by calling the function with a regular, + non-named, Psycopg cursor: + + .. code-block:: python + + cur1 = conn.cursor() + cur1.callproc('reffunc', ['curname']) + + and then use a named cursor in the same transaction to "steal the cursor": + + .. code-block:: python + + cur2 = conn.cursor('curname') + for record in cur2: # or cur2.fetchone, fetchmany... + # do something with record + pass + + +.. |DECLARE| replace:: :sql:`DECLARE` +.. _DECLARE: http://www.postgresql.org/docs/current/static/sql-declare.html + + + +.. index:: Thread safety, Multithread, Multiprocess + +.. _thread-safety: + +Thread and process safety +------------------------- + +The Psycopg module and the `connection` objects are *thread-safe*: many +threads can access the same database either using separate sessions and +creating a `!connection` per thread or using the same +connection and creating separate `cursor`\ s. In |DBAPI|_ parlance, Psycopg is +*level 2 thread safe*. + +The difference between the above two approaches is that, using different +connections, the commands will be executed in different sessions and will be +served by different server processes. On the other hand, using many cursors on +the same connection, all the commands will be executed in the same session +(and in the same transaction if the connection is not in :ref:`autocommit +` mode), but they will be serialized. + +The above observations are only valid for regular threads: they don't apply to +forked processes nor to green threads. `libpq` connections `shouldn't be used by a +forked processes`__, so when using a module such as `multiprocessing` or a +forking web deploy method such as FastCGI make sure to create the connections +*after* the fork. + +.. __: http://www.postgresql.org/docs/current/static/libpq-connect.html#LIBPQ-CONNECT + +Connections shouldn't be shared either by different green threads: see +:ref:`green-support` for further details. + + + +.. index:: + pair: COPY; SQL command + +.. _copy: + +Using COPY TO and COPY FROM +--------------------------- + +Psycopg `cursor` objects provide an interface to the efficient +PostgreSQL |COPY|__ command to move data from files to tables and back. +The methods exposed are: + +`~cursor.copy_from()` + Reads data *from* a file-like object appending them to a database table + (:sql:`COPY table FROM file` syntax). The source file must have both + `!read()` and `!readline()` method. + +`~cursor.copy_to()` + Writes the content of a table *to* a file-like object (:sql:`COPY table TO + file` syntax). The target file must have a `write()` method. + +`~cursor.copy_expert()` + Allows to handle more specific cases and to use all the :sql:`COPY` + features available in PostgreSQL. + +Please refer to the documentation of the single methods for details and +examples. + +.. |COPY| replace:: :sql:`COPY` +.. __: http://www.postgresql.org/docs/current/static/sql-copy.html + + + +.. index:: + single: Large objects + +.. _large-objects: + +Access to PostgreSQL large objects +---------------------------------- + +PostgreSQL offers support for `large objects`__, which provide stream-style +access to user data that is stored in a special large-object structure. They +are useful with data values too large to be manipulated conveniently as a +whole. + +.. __: http://www.postgresql.org/docs/current/static/largeobjects.html + +Psycopg allows access to the large object using the +`~psycopg2.extensions.lobject` class. Objects are generated using the +`connection.lobject()` factory method. Data can be retrieved either as bytes +or as Unicode strings. + +Psycopg large object support efficient import/export with file system files +using the |lo_import|_ and |lo_export|_ libpq functions. + +.. |lo_import| replace:: `!lo_import()` +.. _lo_import: http://www.postgresql.org/docs/current/static/lo-interfaces.html#LO-IMPORT +.. |lo_export| replace:: `!lo_export()` +.. _lo_export: http://www.postgresql.org/docs/current/static/lo-interfaces.html#LO-EXPORT + + + +.. index:: + pair: Two-phase commit; Transaction + +.. _tpc: + +Two-Phase Commit protocol support +--------------------------------- + +.. versionadded:: 2.3 + +Psycopg exposes the two-phase commit features available since PostgreSQL 8.1 +implementing the *two-phase commit extensions* proposed by the |DBAPI|. + +The |DBAPI| model of two-phase commit is inspired by the `XA specification`__, +according to which transaction IDs are formed from three components: + +- a format ID (non-negative 32 bit integer) +- a global transaction ID (string not longer than 64 bytes) +- a branch qualifier (string not longer than 64 bytes) + +For a particular global transaction, the first two components will be the same +for all the resources. Every resource will be assigned a different branch +qualifier. + +According to the |DBAPI| specification, a transaction ID is created using the +`connection.xid()` method. Once you have a transaction id, a distributed +transaction can be started with `connection.tpc_begin()`, prepared using +`~connection.tpc_prepare()` and completed using `~connection.tpc_commit()` or +`~connection.tpc_rollback()`. Transaction IDs can also be retrieved from the +database using `~connection.tpc_recover()` and completed using the above +`!tpc_commit()` and `!tpc_rollback()`. + +PostgreSQL doesn't follow the XA standard though, and the ID for a PostgreSQL +prepared transaction can be any string up to 200 characters long. +Psycopg's `~psycopg2.extensions.Xid` objects can represent both XA-style +transactions IDs (such as the ones created by the `!xid()` method) and +PostgreSQL transaction IDs identified by an unparsed string. + +The format in which the Xids are converted into strings passed to the +database is the same employed by the `PostgreSQL JDBC driver`__: this should +allow interoperation between tools written in Python and in Java. For example +a recovery tool written in Python would be able to recognize the components of +transactions produced by a Java program. + +For further details see the documentation for the above methods. + +.. __: http://www.opengroup.org/bookstore/catalog/c193.htm +.. __: http://jdbc.postgresql.org/ + Binary files /tmp/2HyIbqyseQ/psycopg2-2.0.13/doc/html/_static/ajax-loader.gif and /tmp/gnadcqLbN0/psycopg2-2.4.5/doc/html/_static/ajax-loader.gif differ diff -Nru psycopg2-2.0.13/doc/html/_static/basic.css psycopg2-2.4.5/doc/html/_static/basic.css --- psycopg2-2.0.13/doc/html/_static/basic.css 1970-01-01 00:00:00.000000000 +0000 +++ psycopg2-2.4.5/doc/html/_static/basic.css 2012-03-28 21:17:18.000000000 +0000 @@ -0,0 +1,540 @@ +/* + * basic.css + * ~~~~~~~~~ + * + * Sphinx stylesheet -- basic theme. + * + * :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ + +/* -- main layout ----------------------------------------------------------- */ + +div.clearer { + clear: both; +} + +/* -- relbar ---------------------------------------------------------------- */ + +div.related { + width: 100%; + font-size: 90%; +} + +div.related h3 { + display: none; +} + +div.related ul { + margin: 0; + padding: 0 0 0 10px; + list-style: none; +} + +div.related li { + display: inline; +} + +div.related li.right { + float: right; + margin-right: 5px; +} + +/* -- sidebar --------------------------------------------------------------- */ + +div.sphinxsidebarwrapper { + padding: 10px 5px 0 10px; +} + +div.sphinxsidebar { + float: left; + width: 230px; + margin-left: -100%; + font-size: 90%; +} + +div.sphinxsidebar ul { + list-style: none; +} + +div.sphinxsidebar ul ul, +div.sphinxsidebar ul.want-points { + margin-left: 20px; + list-style: square; +} + +div.sphinxsidebar ul ul { + margin-top: 0; + margin-bottom: 0; +} + +div.sphinxsidebar form { + margin-top: 10px; +} + +div.sphinxsidebar input { + border: 1px solid #98dbcc; + font-family: sans-serif; + font-size: 1em; +} + +div.sphinxsidebar #searchbox input[type="text"] { + width: 170px; +} + +div.sphinxsidebar #searchbox input[type="submit"] { + width: 30px; +} + +img { + border: 0; +} + +/* -- search page ----------------------------------------------------------- */ + +ul.search { + margin: 10px 0 0 20px; + padding: 0; +} + +ul.search li { + padding: 5px 0 5px 20px; + background-image: url(file.png); + background-repeat: no-repeat; + background-position: 0 7px; +} + +ul.search li a { + font-weight: bold; +} + +ul.search li div.context { + color: #888; + margin: 2px 0 0 30px; + text-align: left; +} + +ul.keywordmatches li.goodmatch a { + font-weight: bold; +} + +/* -- index page ------------------------------------------------------------ */ + +table.contentstable { + width: 90%; +} + +table.contentstable p.biglink { + line-height: 150%; +} + +a.biglink { + font-size: 1.3em; +} + +span.linkdescr { + font-style: italic; + padding-top: 5px; + font-size: 90%; +} + +/* -- general index --------------------------------------------------------- */ + +table.indextable { + width: 100%; +} + +table.indextable td { + text-align: left; + vertical-align: top; +} + +table.indextable dl, table.indextable dd { + margin-top: 0; + margin-bottom: 0; +} + +table.indextable tr.pcap { + height: 10px; +} + +table.indextable tr.cap { + margin-top: 10px; + background-color: #f2f2f2; +} + +img.toggler { + margin-right: 3px; + margin-top: 3px; + cursor: pointer; +} + +div.modindex-jumpbox { + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; + margin: 1em 0 1em 0; + padding: 0.4em; +} + +div.genindex-jumpbox { + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; + margin: 1em 0 1em 0; + padding: 0.4em; +} + +/* -- general body styles --------------------------------------------------- */ + +a.headerlink { + visibility: hidden; +} + +h1:hover > a.headerlink, +h2:hover > a.headerlink, +h3:hover > a.headerlink, +h4:hover > a.headerlink, +h5:hover > a.headerlink, +h6:hover > a.headerlink, +dt:hover > a.headerlink { + visibility: visible; +} + +div.body p.caption { + text-align: inherit; +} + +div.body td { + text-align: left; +} + +.field-list ul { + padding-left: 1em; +} + +.first { + margin-top: 0 !important; +} + +p.rubric { + margin-top: 30px; + font-weight: bold; +} + +img.align-left, .figure.align-left, object.align-left { + clear: left; + float: left; + margin-right: 1em; +} + +img.align-right, .figure.align-right, object.align-right { + clear: right; + float: right; + margin-left: 1em; +} + +img.align-center, .figure.align-center, object.align-center { + display: block; + margin-left: auto; + margin-right: auto; +} + +.align-left { + text-align: left; +} + +.align-center { + text-align: center; +} + +.align-right { + text-align: right; +} + +/* -- sidebars -------------------------------------------------------------- */ + +div.sidebar { + margin: 0 0 0.5em 1em; + border: 1px solid #ddb; + padding: 7px 7px 0 7px; + background-color: #ffe; + width: 40%; + float: right; +} + +p.sidebar-title { + font-weight: bold; +} + +/* -- topics ---------------------------------------------------------------- */ + +div.topic { + border: 1px solid #ccc; + padding: 7px 7px 0 7px; + margin: 10px 0 10px 0; +} + +p.topic-title { + font-size: 1.1em; + font-weight: bold; + margin-top: 10px; +} + +/* -- admonitions ----------------------------------------------------------- */ + +div.admonition { + margin-top: 10px; + margin-bottom: 10px; + padding: 7px; +} + +div.admonition dt { + font-weight: bold; +} + +div.admonition dl { + margin-bottom: 0; +} + +p.admonition-title { + margin: 0px 10px 5px 0px; + font-weight: bold; +} + +div.body p.centered { + text-align: center; + margin-top: 25px; +} + +/* -- tables ---------------------------------------------------------------- */ + +table.docutils { + border: 0; + border-collapse: collapse; +} + +table.docutils td, table.docutils th { + padding: 1px 8px 1px 5px; + border-top: 0; + border-left: 0; + border-right: 0; + border-bottom: 1px solid #aaa; +} + +table.field-list td, table.field-list th { + border: 0 !important; +} + +table.footnote td, table.footnote th { + border: 0 !important; +} + +th { + text-align: left; + padding-right: 5px; +} + +table.citation { + border-left: solid 1px gray; + margin-left: 1px; +} + +table.citation td { + border-bottom: none; +} + +/* -- other body styles ----------------------------------------------------- */ + +ol.arabic { + list-style: decimal; +} + +ol.loweralpha { + list-style: lower-alpha; +} + +ol.upperalpha { + list-style: upper-alpha; +} + +ol.lowerroman { + list-style: lower-roman; +} + +ol.upperroman { + list-style: upper-roman; +} + +dl { + margin-bottom: 15px; +} + +dd p { + margin-top: 0px; +} + +dd ul, dd table { + margin-bottom: 10px; +} + +dd { + margin-top: 3px; + margin-bottom: 10px; + margin-left: 30px; +} + +dt:target, .highlighted { + background-color: #fbe54e; +} + +dl.glossary dt { + font-weight: bold; + font-size: 1.1em; +} + +.field-list ul { + margin: 0; + padding-left: 1em; +} + +.field-list p { + margin: 0; +} + +.refcount { + color: #060; +} + +.optional { + font-size: 1.3em; +} + +.versionmodified { + font-style: italic; +} + +.system-message { + background-color: #fda; + padding: 5px; + border: 3px solid red; +} + +.footnote:target { + background-color: #ffa; +} + +.line-block { + display: block; + margin-top: 1em; + margin-bottom: 1em; +} + +.line-block .line-block { + margin-top: 0; + margin-bottom: 0; + margin-left: 1.5em; +} + +.guilabel, .menuselection { + font-family: sans-serif; +} + +.accelerator { + text-decoration: underline; +} + +.classifier { + font-style: oblique; +} + +abbr, acronym { + border-bottom: dotted 1px; + cursor: help; +} + +/* -- code displays --------------------------------------------------------- */ + +pre { + overflow: auto; + overflow-y: hidden; /* fixes display issues on Chrome browsers */ +} + +td.linenos pre { + padding: 5px 0px; + border: 0; + background-color: transparent; + color: #aaa; +} + +table.highlighttable { + margin-left: 0.5em; +} + +table.highlighttable td { + padding: 0 0.5em 0 0.5em; +} + +tt.descname { + background-color: transparent; + font-weight: bold; + font-size: 1.2em; +} + +tt.descclassname { + background-color: transparent; +} + +tt.xref, a tt { + background-color: transparent; + font-weight: bold; +} + +h1 tt, h2 tt, h3 tt, h4 tt, h5 tt, h6 tt { + background-color: transparent; +} + +.viewcode-link { + float: right; +} + +.viewcode-back { + float: right; + font-family: sans-serif; +} + +div.viewcode-block:target { + margin: -1px -10px; + padding: 0 10px; +} + +/* -- math display ---------------------------------------------------------- */ + +img.math { + vertical-align: middle; +} + +div.body div.math p { + text-align: center; +} + +span.eqno { + float: right; +} + +/* -- printout stylesheet --------------------------------------------------- */ + +@media print { + div.document, + div.documentwrapper, + div.bodywrapper { + margin: 0 !important; + width: 100%; + } + + div.sphinxsidebar, + div.related, + div.footer, + #top-link { + display: none; + } +} \ No newline at end of file Binary files /tmp/2HyIbqyseQ/psycopg2-2.0.13/doc/html/_static/comment-bright.png and /tmp/gnadcqLbN0/psycopg2-2.4.5/doc/html/_static/comment-bright.png differ Binary files /tmp/2HyIbqyseQ/psycopg2-2.0.13/doc/html/_static/comment-close.png and /tmp/gnadcqLbN0/psycopg2-2.4.5/doc/html/_static/comment-close.png differ Binary files /tmp/2HyIbqyseQ/psycopg2-2.0.13/doc/html/_static/comment.png and /tmp/gnadcqLbN0/psycopg2-2.4.5/doc/html/_static/comment.png differ diff -Nru psycopg2-2.0.13/doc/html/_static/default.css psycopg2-2.4.5/doc/html/_static/default.css --- psycopg2-2.0.13/doc/html/_static/default.css 1970-01-01 00:00:00.000000000 +0000 +++ psycopg2-2.4.5/doc/html/_static/default.css 2012-03-28 21:17:18.000000000 +0000 @@ -0,0 +1,256 @@ +/* + * default.css_t + * ~~~~~~~~~~~~~ + * + * Sphinx stylesheet -- default theme. + * + * :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ + +@import url("basic.css"); + +/* -- page layout ----------------------------------------------------------- */ + +body { + font-family: sans-serif; + font-size: 100%; + background-color: #11303d; + color: #000; + margin: 0; + padding: 0; +} + +div.document { + background-color: #1c4e63; +} + +div.documentwrapper { + float: left; + width: 100%; +} + +div.bodywrapper { + margin: 0 0 0 230px; +} + +div.body { + background-color: #ffffff; + color: #000000; + padding: 0 20px 30px 20px; +} + +div.footer { + color: #ffffff; + width: 100%; + padding: 9px 0 9px 0; + text-align: center; + font-size: 75%; +} + +div.footer a { + color: #ffffff; + text-decoration: underline; +} + +div.related { + background-color: #133f52; + line-height: 30px; + color: #ffffff; +} + +div.related a { + color: #ffffff; +} + +div.sphinxsidebar { +} + +div.sphinxsidebar h3 { + font-family: 'Trebuchet MS', sans-serif; + color: #ffffff; + font-size: 1.4em; + font-weight: normal; + margin: 0; + padding: 0; +} + +div.sphinxsidebar h3 a { + color: #ffffff; +} + +div.sphinxsidebar h4 { + font-family: 'Trebuchet MS', sans-serif; + color: #ffffff; + font-size: 1.3em; + font-weight: normal; + margin: 5px 0 0 0; + padding: 0; +} + +div.sphinxsidebar p { + color: #ffffff; +} + +div.sphinxsidebar p.topless { + margin: 5px 10px 10px 10px; +} + +div.sphinxsidebar ul { + margin: 10px; + padding: 0; + color: #ffffff; +} + +div.sphinxsidebar a { + color: #98dbcc; +} + +div.sphinxsidebar input { + border: 1px solid #98dbcc; + font-family: sans-serif; + font-size: 1em; +} + + + +/* -- hyperlink styles ------------------------------------------------------ */ + +a { + color: #355f7c; + text-decoration: none; +} + +a:visited { + color: #355f7c; + text-decoration: none; +} + +a:hover { + text-decoration: underline; +} + + + +/* -- body styles ----------------------------------------------------------- */ + +div.body h1, +div.body h2, +div.body h3, +div.body h4, +div.body h5, +div.body h6 { + font-family: 'Trebuchet MS', sans-serif; + background-color: #f2f2f2; + font-weight: normal; + color: #20435c; + border-bottom: 1px solid #ccc; + margin: 20px -20px 10px -20px; + padding: 3px 0 3px 10px; +} + +div.body h1 { margin-top: 0; font-size: 200%; } +div.body h2 { font-size: 160%; } +div.body h3 { font-size: 140%; } +div.body h4 { font-size: 120%; } +div.body h5 { font-size: 110%; } +div.body h6 { font-size: 100%; } + +a.headerlink { + color: #c60f0f; + font-size: 0.8em; + padding: 0 4px 0 4px; + text-decoration: none; +} + +a.headerlink:hover { + background-color: #c60f0f; + color: white; +} + +div.body p, div.body dd, div.body li { + text-align: justify; + line-height: 130%; +} + +div.admonition p.admonition-title + p { + display: inline; +} + +div.admonition p { + margin-bottom: 5px; +} + +div.admonition pre { + margin-bottom: 5px; +} + +div.admonition ul, div.admonition ol { + margin-bottom: 5px; +} + +div.note { + background-color: #eee; + border: 1px solid #ccc; +} + +div.seealso { + background-color: #ffc; + border: 1px solid #ff6; +} + +div.topic { + background-color: #eee; +} + +div.warning { + background-color: #ffe4e4; + border: 1px solid #f66; +} + +p.admonition-title { + display: inline; +} + +p.admonition-title:after { + content: ":"; +} + +pre { + padding: 5px; + background-color: #eeffcc; + color: #333333; + line-height: 120%; + border: 1px solid #ac9; + border-left: none; + border-right: none; +} + +tt { + background-color: #ecf0f3; + padding: 0 1px 0 1px; + font-size: 0.95em; +} + +th { + background-color: #ede; +} + +.warning tt { + background: #efc2c2; +} + +.note tt { + background: #d6d6d6; +} + +.viewcode-back { + font-family: sans-serif; +} + +div.viewcode-block:target { + background-color: #f4debf; + border-top: 1px solid #ac9; + border-bottom: 1px solid #ac9; +} \ No newline at end of file diff -Nru psycopg2-2.0.13/doc/html/_static/doctools.js psycopg2-2.4.5/doc/html/_static/doctools.js --- psycopg2-2.0.13/doc/html/_static/doctools.js 1970-01-01 00:00:00.000000000 +0000 +++ psycopg2-2.4.5/doc/html/_static/doctools.js 2012-03-28 21:17:18.000000000 +0000 @@ -0,0 +1,247 @@ +/* + * doctools.js + * ~~~~~~~~~~~ + * + * Sphinx JavaScript utilities for all documentation. + * + * :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ + +/** + * select a different prefix for underscore + */ +$u = _.noConflict(); + +/** + * make the code below compatible with browsers without + * an installed firebug like debugger +if (!window.console || !console.firebug) { + var names = ["log", "debug", "info", "warn", "error", "assert", "dir", + "dirxml", "group", "groupEnd", "time", "timeEnd", "count", "trace", + "profile", "profileEnd"]; + window.console = {}; + for (var i = 0; i < names.length; ++i) + window.console[names[i]] = function() {}; +} + */ + +/** + * small helper function to urldecode strings + */ +jQuery.urldecode = function(x) { + return decodeURIComponent(x).replace(/\+/g, ' '); +} + +/** + * small helper function to urlencode strings + */ +jQuery.urlencode = encodeURIComponent; + +/** + * This function returns the parsed url parameters of the + * current request. Multiple values per key are supported, + * it will always return arrays of strings for the value parts. + */ +jQuery.getQueryParameters = function(s) { + if (typeof s == 'undefined') + s = document.location.search; + var parts = s.substr(s.indexOf('?') + 1).split('&'); + var result = {}; + for (var i = 0; i < parts.length; i++) { + var tmp = parts[i].split('=', 2); + var key = jQuery.urldecode(tmp[0]); + var value = jQuery.urldecode(tmp[1]); + if (key in result) + result[key].push(value); + else + result[key] = [value]; + } + return result; +}; + +/** + * small function to check if an array contains + * a given item. + */ +jQuery.contains = function(arr, item) { + for (var i = 0; i < arr.length; i++) { + if (arr[i] == item) + return true; + } + return false; +}; + +/** + * highlight a given string on a jquery object by wrapping it in + * span elements with the given class name. + */ +jQuery.fn.highlightText = function(text, className) { + function highlight(node) { + if (node.nodeType == 3) { + var val = node.nodeValue; + var pos = val.toLowerCase().indexOf(text); + if (pos >= 0 && !jQuery(node.parentNode).hasClass(className)) { + var span = document.createElement("span"); + span.className = className; + span.appendChild(document.createTextNode(val.substr(pos, text.length))); + node.parentNode.insertBefore(span, node.parentNode.insertBefore( + document.createTextNode(val.substr(pos + text.length)), + node.nextSibling)); + node.nodeValue = val.substr(0, pos); + } + } + else if (!jQuery(node).is("button, select, textarea")) { + jQuery.each(node.childNodes, function() { + highlight(this); + }); + } + } + return this.each(function() { + highlight(this); + }); +}; + +/** + * Small JavaScript module for the documentation. + */ +var Documentation = { + + init : function() { + this.fixFirefoxAnchorBug(); + this.highlightSearchWords(); + this.initIndexTable(); + }, + + /** + * i18n support + */ + TRANSLATIONS : {}, + PLURAL_EXPR : function(n) { return n == 1 ? 0 : 1; }, + LOCALE : 'unknown', + + // gettext and ngettext don't access this so that the functions + // can safely bound to a different name (_ = Documentation.gettext) + gettext : function(string) { + var translated = Documentation.TRANSLATIONS[string]; + if (typeof translated == 'undefined') + return string; + return (typeof translated == 'string') ? translated : translated[0]; + }, + + ngettext : function(singular, plural, n) { + var translated = Documentation.TRANSLATIONS[singular]; + if (typeof translated == 'undefined') + return (n == 1) ? singular : plural; + return translated[Documentation.PLURALEXPR(n)]; + }, + + addTranslations : function(catalog) { + for (var key in catalog.messages) + this.TRANSLATIONS[key] = catalog.messages[key]; + this.PLURAL_EXPR = new Function('n', 'return +(' + catalog.plural_expr + ')'); + this.LOCALE = catalog.locale; + }, + + /** + * add context elements like header anchor links + */ + addContextElements : function() { + $('div[id] > :header:first').each(function() { + $('\u00B6'). + attr('href', '#' + this.id). + attr('title', _('Permalink to this headline')). + appendTo(this); + }); + $('dt[id]').each(function() { + $('\u00B6'). + attr('href', '#' + this.id). + attr('title', _('Permalink to this definition')). + appendTo(this); + }); + }, + + /** + * workaround a firefox stupidity + */ + fixFirefoxAnchorBug : function() { + if (document.location.hash && $.browser.mozilla) + window.setTimeout(function() { + document.location.href += ''; + }, 10); + }, + + /** + * highlight the search words provided in the url in the text + */ + highlightSearchWords : function() { + var params = $.getQueryParameters(); + var terms = (params.highlight) ? params.highlight[0].split(/\s+/) : []; + if (terms.length) { + var body = $('div.body'); + window.setTimeout(function() { + $.each(terms, function() { + body.highlightText(this.toLowerCase(), 'highlighted'); + }); + }, 10); + $('') + .appendTo($('#searchbox')); + } + }, + + /** + * init the domain index toggle buttons + */ + initIndexTable : function() { + var togglers = $('img.toggler').click(function() { + var src = $(this).attr('src'); + var idnum = $(this).attr('id').substr(7); + $('tr.cg-' + idnum).toggle(); + if (src.substr(-9) == 'minus.png') + $(this).attr('src', src.substr(0, src.length-9) + 'plus.png'); + else + $(this).attr('src', src.substr(0, src.length-8) + 'minus.png'); + }).css('display', ''); + if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) { + togglers.click(); + } + }, + + /** + * helper function to hide the search marks again + */ + hideSearchWords : function() { + $('#searchbox .highlight-link').fadeOut(300); + $('span.highlighted').removeClass('highlighted'); + }, + + /** + * make the url absolute + */ + makeURL : function(relativeURL) { + return DOCUMENTATION_OPTIONS.URL_ROOT + '/' + relativeURL; + }, + + /** + * get the current relative url + */ + getCurrentURL : function() { + var path = document.location.pathname; + var parts = path.split(/\//); + $.each(DOCUMENTATION_OPTIONS.URL_ROOT.split(/\//), function() { + if (this == '..') + parts.pop(); + }); + var url = parts.join('/'); + return path.substring(url.lastIndexOf('/') + 1, path.length - 1); + } +}; + +// quick alias for translations +_ = Documentation.gettext; + +$(document).ready(function() { + Documentation.init(); +}); Binary files /tmp/2HyIbqyseQ/psycopg2-2.0.13/doc/html/_static/down.png and /tmp/gnadcqLbN0/psycopg2-2.4.5/doc/html/_static/down.png differ Binary files /tmp/2HyIbqyseQ/psycopg2-2.0.13/doc/html/_static/down-pressed.png and /tmp/gnadcqLbN0/psycopg2-2.4.5/doc/html/_static/down-pressed.png differ Binary files /tmp/2HyIbqyseQ/psycopg2-2.0.13/doc/html/_static/file.png and /tmp/gnadcqLbN0/psycopg2-2.4.5/doc/html/_static/file.png differ diff -Nru psycopg2-2.0.13/doc/html/_static/jquery.js psycopg2-2.4.5/doc/html/_static/jquery.js --- psycopg2-2.0.13/doc/html/_static/jquery.js 1970-01-01 00:00:00.000000000 +0000 +++ psycopg2-2.4.5/doc/html/_static/jquery.js 2012-03-28 21:17:18.000000000 +0000 @@ -0,0 +1,9404 @@ +/*! + * jQuery JavaScript Library v1.7.2 + * http://jquery.com/ + * + * Copyright 2011, John Resig + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * Includes Sizzle.js + * http://sizzlejs.com/ + * Copyright 2011, The Dojo Foundation + * Released under the MIT, BSD, and GPL Licenses. + * + * Date: Wed Mar 21 12:46:34 2012 -0700 + */ +(function( window, undefined ) { + +// Use the correct document accordingly with window argument (sandbox) +var document = window.document, + navigator = window.navigator, + location = window.location; +var jQuery = (function() { + +// Define a local copy of jQuery +var jQuery = function( selector, context ) { + // The jQuery object is actually just the init constructor 'enhanced' + return new jQuery.fn.init( selector, context, rootjQuery ); + }, + + // Map over jQuery in case of overwrite + _jQuery = window.jQuery, + + // Map over the $ in case of overwrite + _$ = window.$, + + // A central reference to the root jQuery(document) + rootjQuery, + + // A simple way to check for HTML strings or ID strings + // Prioritize #id over to avoid XSS via location.hash (#9521) + quickExpr = /^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/, + + // Check if a string has a non-whitespace character in it + rnotwhite = /\S/, + + // Used for trimming whitespace + trimLeft = /^\s+/, + trimRight = /\s+$/, + + // Match a standalone tag + rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>)?$/, + + // JSON RegExp + rvalidchars = /^[\],:{}\s]*$/, + rvalidescape = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, + rvalidtokens = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, + rvalidbraces = /(?:^|:|,)(?:\s*\[)+/g, + + // Useragent RegExp + rwebkit = /(webkit)[ \/]([\w.]+)/, + ropera = /(opera)(?:.*version)?[ \/]([\w.]+)/, + rmsie = /(msie) ([\w.]+)/, + rmozilla = /(mozilla)(?:.*? rv:([\w.]+))?/, + + // Matches dashed string for camelizing + rdashAlpha = /-([a-z]|[0-9])/ig, + rmsPrefix = /^-ms-/, + + // Used by jQuery.camelCase as callback to replace() + fcamelCase = function( all, letter ) { + return ( letter + "" ).toUpperCase(); + }, + + // Keep a UserAgent string for use with jQuery.browser + userAgent = navigator.userAgent, + + // For matching the engine and version of the browser + browserMatch, + + // The deferred used on DOM ready + readyList, + + // The ready event handler + DOMContentLoaded, + + // Save a reference to some core methods + toString = Object.prototype.toString, + hasOwn = Object.prototype.hasOwnProperty, + push = Array.prototype.push, + slice = Array.prototype.slice, + trim = String.prototype.trim, + indexOf = Array.prototype.indexOf, + + // [[Class]] -> type pairs + class2type = {}; + +jQuery.fn = jQuery.prototype = { + constructor: jQuery, + init: function( selector, context, rootjQuery ) { + var match, elem, ret, doc; + + // Handle $(""), $(null), or $(undefined) + if ( !selector ) { + return this; + } + + // Handle $(DOMElement) + if ( selector.nodeType ) { + this.context = this[0] = selector; + this.length = 1; + return this; + } + + // The body element only exists once, optimize finding it + if ( selector === "body" && !context && document.body ) { + this.context = document; + this[0] = document.body; + this.selector = selector; + this.length = 1; + return this; + } + + // Handle HTML strings + if ( typeof selector === "string" ) { + // Are we dealing with HTML string or an ID? + if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) { + // Assume that strings that start and end with <> are HTML and skip the regex check + match = [ null, selector, null ]; + + } else { + match = quickExpr.exec( selector ); + } + + // Verify a match, and that no context was specified for #id + if ( match && (match[1] || !context) ) { + + // HANDLE: $(html) -> $(array) + if ( match[1] ) { + context = context instanceof jQuery ? context[0] : context; + doc = ( context ? context.ownerDocument || context : document ); + + // If a single string is passed in and it's a single tag + // just do a createElement and skip the rest + ret = rsingleTag.exec( selector ); + + if ( ret ) { + if ( jQuery.isPlainObject( context ) ) { + selector = [ document.createElement( ret[1] ) ]; + jQuery.fn.attr.call( selector, context, true ); + + } else { + selector = [ doc.createElement( ret[1] ) ]; + } + + } else { + ret = jQuery.buildFragment( [ match[1] ], [ doc ] ); + selector = ( ret.cacheable ? jQuery.clone(ret.fragment) : ret.fragment ).childNodes; + } + + return jQuery.merge( this, selector ); + + // HANDLE: $("#id") + } else { + elem = document.getElementById( match[2] ); + + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + if ( elem && elem.parentNode ) { + // Handle the case where IE and Opera return items + // by name instead of ID + if ( elem.id !== match[2] ) { + return rootjQuery.find( selector ); + } + + // Otherwise, we inject the element directly into the jQuery object + this.length = 1; + this[0] = elem; + } + + this.context = document; + this.selector = selector; + return this; + } + + // HANDLE: $(expr, $(...)) + } else if ( !context || context.jquery ) { + return ( context || rootjQuery ).find( selector ); + + // HANDLE: $(expr, context) + // (which is just equivalent to: $(context).find(expr) + } else { + return this.constructor( context ).find( selector ); + } + + // HANDLE: $(function) + // Shortcut for document ready + } else if ( jQuery.isFunction( selector ) ) { + return rootjQuery.ready( selector ); + } + + if ( selector.selector !== undefined ) { + this.selector = selector.selector; + this.context = selector.context; + } + + return jQuery.makeArray( selector, this ); + }, + + // Start with an empty selector + selector: "", + + // The current version of jQuery being used + jquery: "1.7.2", + + // The default length of a jQuery object is 0 + length: 0, + + // The number of elements contained in the matched element set + size: function() { + return this.length; + }, + + toArray: function() { + return slice.call( this, 0 ); + }, + + // Get the Nth element in the matched element set OR + // Get the whole matched element set as a clean array + get: function( num ) { + return num == null ? + + // Return a 'clean' array + this.toArray() : + + // Return just the object + ( num < 0 ? this[ this.length + num ] : this[ num ] ); + }, + + // Take an array of elements and push it onto the stack + // (returning the new matched element set) + pushStack: function( elems, name, selector ) { + // Build a new jQuery matched element set + var ret = this.constructor(); + + if ( jQuery.isArray( elems ) ) { + push.apply( ret, elems ); + + } else { + jQuery.merge( ret, elems ); + } + + // Add the old object onto the stack (as a reference) + ret.prevObject = this; + + ret.context = this.context; + + if ( name === "find" ) { + ret.selector = this.selector + ( this.selector ? " " : "" ) + selector; + } else if ( name ) { + ret.selector = this.selector + "." + name + "(" + selector + ")"; + } + + // Return the newly-formed element set + return ret; + }, + + // Execute a callback for every element in the matched set. + // (You can seed the arguments with an array of args, but this is + // only used internally.) + each: function( callback, args ) { + return jQuery.each( this, callback, args ); + }, + + ready: function( fn ) { + // Attach the listeners + jQuery.bindReady(); + + // Add the callback + readyList.add( fn ); + + return this; + }, + + eq: function( i ) { + i = +i; + return i === -1 ? + this.slice( i ) : + this.slice( i, i + 1 ); + }, + + first: function() { + return this.eq( 0 ); + }, + + last: function() { + return this.eq( -1 ); + }, + + slice: function() { + return this.pushStack( slice.apply( this, arguments ), + "slice", slice.call(arguments).join(",") ); + }, + + map: function( callback ) { + return this.pushStack( jQuery.map(this, function( elem, i ) { + return callback.call( elem, i, elem ); + })); + }, + + end: function() { + return this.prevObject || this.constructor(null); + }, + + // For internal use only. + // Behaves like an Array's method, not like a jQuery method. + push: push, + sort: [].sort, + splice: [].splice +}; + +// Give the init function the jQuery prototype for later instantiation +jQuery.fn.init.prototype = jQuery.fn; + +jQuery.extend = jQuery.fn.extend = function() { + var options, name, src, copy, copyIsArray, clone, + target = arguments[0] || {}, + i = 1, + length = arguments.length, + deep = false; + + // Handle a deep copy situation + if ( typeof target === "boolean" ) { + deep = target; + target = arguments[1] || {}; + // skip the boolean and the target + i = 2; + } + + // Handle case when target is a string or something (possible in deep copy) + if ( typeof target !== "object" && !jQuery.isFunction(target) ) { + target = {}; + } + + // extend jQuery itself if only one argument is passed + if ( length === i ) { + target = this; + --i; + } + + for ( ; i < length; i++ ) { + // Only deal with non-null/undefined values + if ( (options = arguments[ i ]) != null ) { + // Extend the base object + for ( name in options ) { + src = target[ name ]; + copy = options[ name ]; + + // Prevent never-ending loop + if ( target === copy ) { + continue; + } + + // Recurse if we're merging plain objects or arrays + if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) { + if ( copyIsArray ) { + copyIsArray = false; + clone = src && jQuery.isArray(src) ? src : []; + + } else { + clone = src && jQuery.isPlainObject(src) ? src : {}; + } + + // Never move original objects, clone them + target[ name ] = jQuery.extend( deep, clone, copy ); + + // Don't bring in undefined values + } else if ( copy !== undefined ) { + target[ name ] = copy; + } + } + } + } + + // Return the modified object + return target; +}; + +jQuery.extend({ + noConflict: function( deep ) { + if ( window.$ === jQuery ) { + window.$ = _$; + } + + if ( deep && window.jQuery === jQuery ) { + window.jQuery = _jQuery; + } + + return jQuery; + }, + + // Is the DOM ready to be used? Set to true once it occurs. + isReady: false, + + // A counter to track how many items to wait for before + // the ready event fires. See #6781 + readyWait: 1, + + // Hold (or release) the ready event + holdReady: function( hold ) { + if ( hold ) { + jQuery.readyWait++; + } else { + jQuery.ready( true ); + } + }, + + // Handle when the DOM is ready + ready: function( wait ) { + // Either a released hold or an DOMready/load event and not yet ready + if ( (wait === true && !--jQuery.readyWait) || (wait !== true && !jQuery.isReady) ) { + // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443). + if ( !document.body ) { + return setTimeout( jQuery.ready, 1 ); + } + + // Remember that the DOM is ready + jQuery.isReady = true; + + // If a normal DOM Ready event fired, decrement, and wait if need be + if ( wait !== true && --jQuery.readyWait > 0 ) { + return; + } + + // If there are functions bound, to execute + readyList.fireWith( document, [ jQuery ] ); + + // Trigger any bound ready events + if ( jQuery.fn.trigger ) { + jQuery( document ).trigger( "ready" ).off( "ready" ); + } + } + }, + + bindReady: function() { + if ( readyList ) { + return; + } + + readyList = jQuery.Callbacks( "once memory" ); + + // Catch cases where $(document).ready() is called after the + // browser event has already occurred. + if ( document.readyState === "complete" ) { + // Handle it asynchronously to allow scripts the opportunity to delay ready + return setTimeout( jQuery.ready, 1 ); + } + + // Mozilla, Opera and webkit nightlies currently support this event + if ( document.addEventListener ) { + // Use the handy event callback + document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false ); + + // A fallback to window.onload, that will always work + window.addEventListener( "load", jQuery.ready, false ); + + // If IE event model is used + } else if ( document.attachEvent ) { + // ensure firing before onload, + // maybe late but safe also for iframes + document.attachEvent( "onreadystatechange", DOMContentLoaded ); + + // A fallback to window.onload, that will always work + window.attachEvent( "onload", jQuery.ready ); + + // If IE and not a frame + // continually check to see if the document is ready + var toplevel = false; + + try { + toplevel = window.frameElement == null; + } catch(e) {} + + if ( document.documentElement.doScroll && toplevel ) { + doScrollCheck(); + } + } + }, + + // See test/unit/core.js for details concerning isFunction. + // Since version 1.3, DOM methods and functions like alert + // aren't supported. They return false on IE (#2968). + isFunction: function( obj ) { + return jQuery.type(obj) === "function"; + }, + + isArray: Array.isArray || function( obj ) { + return jQuery.type(obj) === "array"; + }, + + isWindow: function( obj ) { + return obj != null && obj == obj.window; + }, + + isNumeric: function( obj ) { + return !isNaN( parseFloat(obj) ) && isFinite( obj ); + }, + + type: function( obj ) { + return obj == null ? + String( obj ) : + class2type[ toString.call(obj) ] || "object"; + }, + + isPlainObject: function( obj ) { + // Must be an Object. + // Because of IE, we also have to check the presence of the constructor property. + // Make sure that DOM nodes and window objects don't pass through, as well + if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) { + return false; + } + + try { + // Not own constructor property must be Object + if ( obj.constructor && + !hasOwn.call(obj, "constructor") && + !hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) { + return false; + } + } catch ( e ) { + // IE8,9 Will throw exceptions on certain host objects #9897 + return false; + } + + // Own properties are enumerated firstly, so to speed up, + // if last one is own, then all properties are own. + + var key; + for ( key in obj ) {} + + return key === undefined || hasOwn.call( obj, key ); + }, + + isEmptyObject: function( obj ) { + for ( var name in obj ) { + return false; + } + return true; + }, + + error: function( msg ) { + throw new Error( msg ); + }, + + parseJSON: function( data ) { + if ( typeof data !== "string" || !data ) { + return null; + } + + // Make sure leading/trailing whitespace is removed (IE can't handle it) + data = jQuery.trim( data ); + + // Attempt to parse using the native JSON parser first + if ( window.JSON && window.JSON.parse ) { + return window.JSON.parse( data ); + } + + // Make sure the incoming data is actual JSON + // Logic borrowed from http://json.org/json2.js + if ( rvalidchars.test( data.replace( rvalidescape, "@" ) + .replace( rvalidtokens, "]" ) + .replace( rvalidbraces, "")) ) { + + return ( new Function( "return " + data ) )(); + + } + jQuery.error( "Invalid JSON: " + data ); + }, + + // Cross-browser xml parsing + parseXML: function( data ) { + if ( typeof data !== "string" || !data ) { + return null; + } + var xml, tmp; + try { + if ( window.DOMParser ) { // Standard + tmp = new DOMParser(); + xml = tmp.parseFromString( data , "text/xml" ); + } else { // IE + xml = new ActiveXObject( "Microsoft.XMLDOM" ); + xml.async = "false"; + xml.loadXML( data ); + } + } catch( e ) { + xml = undefined; + } + if ( !xml || !xml.documentElement || xml.getElementsByTagName( "parsererror" ).length ) { + jQuery.error( "Invalid XML: " + data ); + } + return xml; + }, + + noop: function() {}, + + // Evaluates a script in a global context + // Workarounds based on findings by Jim Driscoll + // http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context + globalEval: function( data ) { + if ( data && rnotwhite.test( data ) ) { + // We use execScript on Internet Explorer + // We use an anonymous function so that context is window + // rather than jQuery in Firefox + ( window.execScript || function( data ) { + window[ "eval" ].call( window, data ); + } )( data ); + } + }, + + // Convert dashed to camelCase; used by the css and data modules + // Microsoft forgot to hump their vendor prefix (#9572) + camelCase: function( string ) { + return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); + }, + + nodeName: function( elem, name ) { + return elem.nodeName && elem.nodeName.toUpperCase() === name.toUpperCase(); + }, + + // args is for internal usage only + each: function( object, callback, args ) { + var name, i = 0, + length = object.length, + isObj = length === undefined || jQuery.isFunction( object ); + + if ( args ) { + if ( isObj ) { + for ( name in object ) { + if ( callback.apply( object[ name ], args ) === false ) { + break; + } + } + } else { + for ( ; i < length; ) { + if ( callback.apply( object[ i++ ], args ) === false ) { + break; + } + } + } + + // A special, fast, case for the most common use of each + } else { + if ( isObj ) { + for ( name in object ) { + if ( callback.call( object[ name ], name, object[ name ] ) === false ) { + break; + } + } + } else { + for ( ; i < length; ) { + if ( callback.call( object[ i ], i, object[ i++ ] ) === false ) { + break; + } + } + } + } + + return object; + }, + + // Use native String.trim function wherever possible + trim: trim ? + function( text ) { + return text == null ? + "" : + trim.call( text ); + } : + + // Otherwise use our own trimming functionality + function( text ) { + return text == null ? + "" : + text.toString().replace( trimLeft, "" ).replace( trimRight, "" ); + }, + + // results is for internal usage only + makeArray: function( array, results ) { + var ret = results || []; + + if ( array != null ) { + // The window, strings (and functions) also have 'length' + // Tweaked logic slightly to handle Blackberry 4.7 RegExp issues #6930 + var type = jQuery.type( array ); + + if ( array.length == null || type === "string" || type === "function" || type === "regexp" || jQuery.isWindow( array ) ) { + push.call( ret, array ); + } else { + jQuery.merge( ret, array ); + } + } + + return ret; + }, + + inArray: function( elem, array, i ) { + var len; + + if ( array ) { + if ( indexOf ) { + return indexOf.call( array, elem, i ); + } + + len = array.length; + i = i ? i < 0 ? Math.max( 0, len + i ) : i : 0; + + for ( ; i < len; i++ ) { + // Skip accessing in sparse arrays + if ( i in array && array[ i ] === elem ) { + return i; + } + } + } + + return -1; + }, + + merge: function( first, second ) { + var i = first.length, + j = 0; + + if ( typeof second.length === "number" ) { + for ( var l = second.length; j < l; j++ ) { + first[ i++ ] = second[ j ]; + } + + } else { + while ( second[j] !== undefined ) { + first[ i++ ] = second[ j++ ]; + } + } + + first.length = i; + + return first; + }, + + grep: function( elems, callback, inv ) { + var ret = [], retVal; + inv = !!inv; + + // Go through the array, only saving the items + // that pass the validator function + for ( var i = 0, length = elems.length; i < length; i++ ) { + retVal = !!callback( elems[ i ], i ); + if ( inv !== retVal ) { + ret.push( elems[ i ] ); + } + } + + return ret; + }, + + // arg is for internal usage only + map: function( elems, callback, arg ) { + var value, key, ret = [], + i = 0, + length = elems.length, + // jquery objects are treated as arrays + isArray = elems instanceof jQuery || length !== undefined && typeof length === "number" && ( ( length > 0 && elems[ 0 ] && elems[ length -1 ] ) || length === 0 || jQuery.isArray( elems ) ) ; + + // Go through the array, translating each of the items to their + if ( isArray ) { + for ( ; i < length; i++ ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret[ ret.length ] = value; + } + } + + // Go through every key on the object, + } else { + for ( key in elems ) { + value = callback( elems[ key ], key, arg ); + + if ( value != null ) { + ret[ ret.length ] = value; + } + } + } + + // Flatten any nested arrays + return ret.concat.apply( [], ret ); + }, + + // A global GUID counter for objects + guid: 1, + + // Bind a function to a context, optionally partially applying any + // arguments. + proxy: function( fn, context ) { + if ( typeof context === "string" ) { + var tmp = fn[ context ]; + context = fn; + fn = tmp; + } + + // Quick check to determine if target is callable, in the spec + // this throws a TypeError, but we will just return undefined. + if ( !jQuery.isFunction( fn ) ) { + return undefined; + } + + // Simulated bind + var args = slice.call( arguments, 2 ), + proxy = function() { + return fn.apply( context, args.concat( slice.call( arguments ) ) ); + }; + + // Set the guid of unique handler to the same of original handler, so it can be removed + proxy.guid = fn.guid = fn.guid || proxy.guid || jQuery.guid++; + + return proxy; + }, + + // Mutifunctional method to get and set values to a collection + // The value/s can optionally be executed if it's a function + access: function( elems, fn, key, value, chainable, emptyGet, pass ) { + var exec, + bulk = key == null, + i = 0, + length = elems.length; + + // Sets many values + if ( key && typeof key === "object" ) { + for ( i in key ) { + jQuery.access( elems, fn, i, key[i], 1, emptyGet, value ); + } + chainable = 1; + + // Sets one value + } else if ( value !== undefined ) { + // Optionally, function values get executed if exec is true + exec = pass === undefined && jQuery.isFunction( value ); + + if ( bulk ) { + // Bulk operations only iterate when executing function values + if ( exec ) { + exec = fn; + fn = function( elem, key, value ) { + return exec.call( jQuery( elem ), value ); + }; + + // Otherwise they run against the entire set + } else { + fn.call( elems, value ); + fn = null; + } + } + + if ( fn ) { + for (; i < length; i++ ) { + fn( elems[i], key, exec ? value.call( elems[i], i, fn( elems[i], key ) ) : value, pass ); + } + } + + chainable = 1; + } + + return chainable ? + elems : + + // Gets + bulk ? + fn.call( elems ) : + length ? fn( elems[0], key ) : emptyGet; + }, + + now: function() { + return ( new Date() ).getTime(); + }, + + // Use of jQuery.browser is frowned upon. + // More details: http://docs.jquery.com/Utilities/jQuery.browser + uaMatch: function( ua ) { + ua = ua.toLowerCase(); + + var match = rwebkit.exec( ua ) || + ropera.exec( ua ) || + rmsie.exec( ua ) || + ua.indexOf("compatible") < 0 && rmozilla.exec( ua ) || + []; + + return { browser: match[1] || "", version: match[2] || "0" }; + }, + + sub: function() { + function jQuerySub( selector, context ) { + return new jQuerySub.fn.init( selector, context ); + } + jQuery.extend( true, jQuerySub, this ); + jQuerySub.superclass = this; + jQuerySub.fn = jQuerySub.prototype = this(); + jQuerySub.fn.constructor = jQuerySub; + jQuerySub.sub = this.sub; + jQuerySub.fn.init = function init( selector, context ) { + if ( context && context instanceof jQuery && !(context instanceof jQuerySub) ) { + context = jQuerySub( context ); + } + + return jQuery.fn.init.call( this, selector, context, rootjQuerySub ); + }; + jQuerySub.fn.init.prototype = jQuerySub.fn; + var rootjQuerySub = jQuerySub(document); + return jQuerySub; + }, + + browser: {} +}); + +// Populate the class2type map +jQuery.each("Boolean Number String Function Array Date RegExp Object".split(" "), function(i, name) { + class2type[ "[object " + name + "]" ] = name.toLowerCase(); +}); + +browserMatch = jQuery.uaMatch( userAgent ); +if ( browserMatch.browser ) { + jQuery.browser[ browserMatch.browser ] = true; + jQuery.browser.version = browserMatch.version; +} + +// Deprecated, use jQuery.browser.webkit instead +if ( jQuery.browser.webkit ) { + jQuery.browser.safari = true; +} + +// IE doesn't match non-breaking spaces with \s +if ( rnotwhite.test( "\xA0" ) ) { + trimLeft = /^[\s\xA0]+/; + trimRight = /[\s\xA0]+$/; +} + +// All jQuery objects should point back to these +rootjQuery = jQuery(document); + +// Cleanup functions for the document ready method +if ( document.addEventListener ) { + DOMContentLoaded = function() { + document.removeEventListener( "DOMContentLoaded", DOMContentLoaded, false ); + jQuery.ready(); + }; + +} else if ( document.attachEvent ) { + DOMContentLoaded = function() { + // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443). + if ( document.readyState === "complete" ) { + document.detachEvent( "onreadystatechange", DOMContentLoaded ); + jQuery.ready(); + } + }; +} + +// The DOM ready check for Internet Explorer +function doScrollCheck() { + if ( jQuery.isReady ) { + return; + } + + try { + // If IE is used, use the trick by Diego Perini + // http://javascript.nwbox.com/IEContentLoaded/ + document.documentElement.doScroll("left"); + } catch(e) { + setTimeout( doScrollCheck, 1 ); + return; + } + + // and execute any waiting functions + jQuery.ready(); +} + +return jQuery; + +})(); + + +// String to Object flags format cache +var flagsCache = {}; + +// Convert String-formatted flags into Object-formatted ones and store in cache +function createFlags( flags ) { + var object = flagsCache[ flags ] = {}, + i, length; + flags = flags.split( /\s+/ ); + for ( i = 0, length = flags.length; i < length; i++ ) { + object[ flags[i] ] = true; + } + return object; +} + +/* + * Create a callback list using the following parameters: + * + * flags: an optional list of space-separated flags that will change how + * the callback list behaves + * + * By default a callback list will act like an event callback list and can be + * "fired" multiple times. + * + * Possible flags: + * + * once: will ensure the callback list can only be fired once (like a Deferred) + * + * memory: will keep track of previous values and will call any callback added + * after the list has been fired right away with the latest "memorized" + * values (like a Deferred) + * + * unique: will ensure a callback can only be added once (no duplicate in the list) + * + * stopOnFalse: interrupt callings when a callback returns false + * + */ +jQuery.Callbacks = function( flags ) { + + // Convert flags from String-formatted to Object-formatted + // (we check in cache first) + flags = flags ? ( flagsCache[ flags ] || createFlags( flags ) ) : {}; + + var // Actual callback list + list = [], + // Stack of fire calls for repeatable lists + stack = [], + // Last fire value (for non-forgettable lists) + memory, + // Flag to know if list was already fired + fired, + // Flag to know if list is currently firing + firing, + // First callback to fire (used internally by add and fireWith) + firingStart, + // End of the loop when firing + firingLength, + // Index of currently firing callback (modified by remove if needed) + firingIndex, + // Add one or several callbacks to the list + add = function( args ) { + var i, + length, + elem, + type, + actual; + for ( i = 0, length = args.length; i < length; i++ ) { + elem = args[ i ]; + type = jQuery.type( elem ); + if ( type === "array" ) { + // Inspect recursively + add( elem ); + } else if ( type === "function" ) { + // Add if not in unique mode and callback is not in + if ( !flags.unique || !self.has( elem ) ) { + list.push( elem ); + } + } + } + }, + // Fire callbacks + fire = function( context, args ) { + args = args || []; + memory = !flags.memory || [ context, args ]; + fired = true; + firing = true; + firingIndex = firingStart || 0; + firingStart = 0; + firingLength = list.length; + for ( ; list && firingIndex < firingLength; firingIndex++ ) { + if ( list[ firingIndex ].apply( context, args ) === false && flags.stopOnFalse ) { + memory = true; // Mark as halted + break; + } + } + firing = false; + if ( list ) { + if ( !flags.once ) { + if ( stack && stack.length ) { + memory = stack.shift(); + self.fireWith( memory[ 0 ], memory[ 1 ] ); + } + } else if ( memory === true ) { + self.disable(); + } else { + list = []; + } + } + }, + // Actual Callbacks object + self = { + // Add a callback or a collection of callbacks to the list + add: function() { + if ( list ) { + var length = list.length; + add( arguments ); + // Do we need to add the callbacks to the + // current firing batch? + if ( firing ) { + firingLength = list.length; + // With memory, if we're not firing then + // we should call right away, unless previous + // firing was halted (stopOnFalse) + } else if ( memory && memory !== true ) { + firingStart = length; + fire( memory[ 0 ], memory[ 1 ] ); + } + } + return this; + }, + // Remove a callback from the list + remove: function() { + if ( list ) { + var args = arguments, + argIndex = 0, + argLength = args.length; + for ( ; argIndex < argLength ; argIndex++ ) { + for ( var i = 0; i < list.length; i++ ) { + if ( args[ argIndex ] === list[ i ] ) { + // Handle firingIndex and firingLength + if ( firing ) { + if ( i <= firingLength ) { + firingLength--; + if ( i <= firingIndex ) { + firingIndex--; + } + } + } + // Remove the element + list.splice( i--, 1 ); + // If we have some unicity property then + // we only need to do this once + if ( flags.unique ) { + break; + } + } + } + } + } + return this; + }, + // Control if a given callback is in the list + has: function( fn ) { + if ( list ) { + var i = 0, + length = list.length; + for ( ; i < length; i++ ) { + if ( fn === list[ i ] ) { + return true; + } + } + } + return false; + }, + // Remove all callbacks from the list + empty: function() { + list = []; + return this; + }, + // Have the list do nothing anymore + disable: function() { + list = stack = memory = undefined; + return this; + }, + // Is it disabled? + disabled: function() { + return !list; + }, + // Lock the list in its current state + lock: function() { + stack = undefined; + if ( !memory || memory === true ) { + self.disable(); + } + return this; + }, + // Is it locked? + locked: function() { + return !stack; + }, + // Call all callbacks with the given context and arguments + fireWith: function( context, args ) { + if ( stack ) { + if ( firing ) { + if ( !flags.once ) { + stack.push( [ context, args ] ); + } + } else if ( !( flags.once && memory ) ) { + fire( context, args ); + } + } + return this; + }, + // Call all the callbacks with the given arguments + fire: function() { + self.fireWith( this, arguments ); + return this; + }, + // To know if the callbacks have already been called at least once + fired: function() { + return !!fired; + } + }; + + return self; +}; + + + + +var // Static reference to slice + sliceDeferred = [].slice; + +jQuery.extend({ + + Deferred: function( func ) { + var doneList = jQuery.Callbacks( "once memory" ), + failList = jQuery.Callbacks( "once memory" ), + progressList = jQuery.Callbacks( "memory" ), + state = "pending", + lists = { + resolve: doneList, + reject: failList, + notify: progressList + }, + promise = { + done: doneList.add, + fail: failList.add, + progress: progressList.add, + + state: function() { + return state; + }, + + // Deprecated + isResolved: doneList.fired, + isRejected: failList.fired, + + then: function( doneCallbacks, failCallbacks, progressCallbacks ) { + deferred.done( doneCallbacks ).fail( failCallbacks ).progress( progressCallbacks ); + return this; + }, + always: function() { + deferred.done.apply( deferred, arguments ).fail.apply( deferred, arguments ); + return this; + }, + pipe: function( fnDone, fnFail, fnProgress ) { + return jQuery.Deferred(function( newDefer ) { + jQuery.each( { + done: [ fnDone, "resolve" ], + fail: [ fnFail, "reject" ], + progress: [ fnProgress, "notify" ] + }, function( handler, data ) { + var fn = data[ 0 ], + action = data[ 1 ], + returned; + if ( jQuery.isFunction( fn ) ) { + deferred[ handler ](function() { + returned = fn.apply( this, arguments ); + if ( returned && jQuery.isFunction( returned.promise ) ) { + returned.promise().then( newDefer.resolve, newDefer.reject, newDefer.notify ); + } else { + newDefer[ action + "With" ]( this === deferred ? newDefer : this, [ returned ] ); + } + }); + } else { + deferred[ handler ]( newDefer[ action ] ); + } + }); + }).promise(); + }, + // Get a promise for this deferred + // If obj is provided, the promise aspect is added to the object + promise: function( obj ) { + if ( obj == null ) { + obj = promise; + } else { + for ( var key in promise ) { + obj[ key ] = promise[ key ]; + } + } + return obj; + } + }, + deferred = promise.promise({}), + key; + + for ( key in lists ) { + deferred[ key ] = lists[ key ].fire; + deferred[ key + "With" ] = lists[ key ].fireWith; + } + + // Handle state + deferred.done( function() { + state = "resolved"; + }, failList.disable, progressList.lock ).fail( function() { + state = "rejected"; + }, doneList.disable, progressList.lock ); + + // Call given func if any + if ( func ) { + func.call( deferred, deferred ); + } + + // All done! + return deferred; + }, + + // Deferred helper + when: function( firstParam ) { + var args = sliceDeferred.call( arguments, 0 ), + i = 0, + length = args.length, + pValues = new Array( length ), + count = length, + pCount = length, + deferred = length <= 1 && firstParam && jQuery.isFunction( firstParam.promise ) ? + firstParam : + jQuery.Deferred(), + promise = deferred.promise(); + function resolveFunc( i ) { + return function( value ) { + args[ i ] = arguments.length > 1 ? sliceDeferred.call( arguments, 0 ) : value; + if ( !( --count ) ) { + deferred.resolveWith( deferred, args ); + } + }; + } + function progressFunc( i ) { + return function( value ) { + pValues[ i ] = arguments.length > 1 ? sliceDeferred.call( arguments, 0 ) : value; + deferred.notifyWith( promise, pValues ); + }; + } + if ( length > 1 ) { + for ( ; i < length; i++ ) { + if ( args[ i ] && args[ i ].promise && jQuery.isFunction( args[ i ].promise ) ) { + args[ i ].promise().then( resolveFunc(i), deferred.reject, progressFunc(i) ); + } else { + --count; + } + } + if ( !count ) { + deferred.resolveWith( deferred, args ); + } + } else if ( deferred !== firstParam ) { + deferred.resolveWith( deferred, length ? [ firstParam ] : [] ); + } + return promise; + } +}); + + + + +jQuery.support = (function() { + + var support, + all, + a, + select, + opt, + input, + fragment, + tds, + events, + eventName, + i, + isSupported, + div = document.createElement( "div" ), + documentElement = document.documentElement; + + // Preliminary tests + div.setAttribute("className", "t"); + div.innerHTML = "
a"; + + all = div.getElementsByTagName( "*" ); + a = div.getElementsByTagName( "a" )[ 0 ]; + + // Can't get basic test support + if ( !all || !all.length || !a ) { + return {}; + } + + // First batch of supports tests + select = document.createElement( "select" ); + opt = select.appendChild( document.createElement("option") ); + input = div.getElementsByTagName( "input" )[ 0 ]; + + support = { + // IE strips leading whitespace when .innerHTML is used + leadingWhitespace: ( div.firstChild.nodeType === 3 ), + + // Make sure that tbody elements aren't automatically inserted + // IE will insert them into empty tables + tbody: !div.getElementsByTagName("tbody").length, + + // Make sure that link elements get serialized correctly by innerHTML + // This requires a wrapper element in IE + htmlSerialize: !!div.getElementsByTagName("link").length, + + // Get the style information from getAttribute + // (IE uses .cssText instead) + style: /top/.test( a.getAttribute("style") ), + + // Make sure that URLs aren't manipulated + // (IE normalizes it by default) + hrefNormalized: ( a.getAttribute("href") === "/a" ), + + // Make sure that element opacity exists + // (IE uses filter instead) + // Use a regex to work around a WebKit issue. See #5145 + opacity: /^0.55/.test( a.style.opacity ), + + // Verify style float existence + // (IE uses styleFloat instead of cssFloat) + cssFloat: !!a.style.cssFloat, + + // Make sure that if no value is specified for a checkbox + // that it defaults to "on". + // (WebKit defaults to "" instead) + checkOn: ( input.value === "on" ), + + // Make sure that a selected-by-default option has a working selected property. + // (WebKit defaults to false instead of true, IE too, if it's in an optgroup) + optSelected: opt.selected, + + // Test setAttribute on camelCase class. If it works, we need attrFixes when doing get/setAttribute (ie6/7) + getSetAttribute: div.className !== "t", + + // Tests for enctype support on a form(#6743) + enctype: !!document.createElement("form").enctype, + + // Makes sure cloning an html5 element does not cause problems + // Where outerHTML is undefined, this still works + html5Clone: document.createElement("nav").cloneNode( true ).outerHTML !== "<:nav>", + + // Will be defined later + submitBubbles: true, + changeBubbles: true, + focusinBubbles: false, + deleteExpando: true, + noCloneEvent: true, + inlineBlockNeedsLayout: false, + shrinkWrapBlocks: false, + reliableMarginRight: true, + pixelMargin: true + }; + + // jQuery.boxModel DEPRECATED in 1.3, use jQuery.support.boxModel instead + jQuery.boxModel = support.boxModel = (document.compatMode === "CSS1Compat"); + + // Make sure checked status is properly cloned + input.checked = true; + support.noCloneChecked = input.cloneNode( true ).checked; + + // Make sure that the options inside disabled selects aren't marked as disabled + // (WebKit marks them as disabled) + select.disabled = true; + support.optDisabled = !opt.disabled; + + // Test to see if it's possible to delete an expando from an element + // Fails in Internet Explorer + try { + delete div.test; + } catch( e ) { + support.deleteExpando = false; + } + + if ( !div.addEventListener && div.attachEvent && div.fireEvent ) { + div.attachEvent( "onclick", function() { + // Cloning a node shouldn't copy over any + // bound event handlers (IE does this) + support.noCloneEvent = false; + }); + div.cloneNode( true ).fireEvent( "onclick" ); + } + + // Check if a radio maintains its value + // after being appended to the DOM + input = document.createElement("input"); + input.value = "t"; + input.setAttribute("type", "radio"); + support.radioValue = input.value === "t"; + + input.setAttribute("checked", "checked"); + + // #11217 - WebKit loses check when the name is after the checked attribute + input.setAttribute( "name", "t" ); + + div.appendChild( input ); + fragment = document.createDocumentFragment(); + fragment.appendChild( div.lastChild ); + + // WebKit doesn't clone checked state correctly in fragments + support.checkClone = fragment.cloneNode( true ).cloneNode( true ).lastChild.checked; + + // Check if a disconnected checkbox will retain its checked + // value of true after appended to the DOM (IE6/7) + support.appendChecked = input.checked; + + fragment.removeChild( input ); + fragment.appendChild( div ); + + // Technique from Juriy Zaytsev + // http://perfectionkills.com/detecting-event-support-without-browser-sniffing/ + // We only care about the case where non-standard event systems + // are used, namely in IE. Short-circuiting here helps us to + // avoid an eval call (in setAttribute) which can cause CSP + // to go haywire. See: https://developer.mozilla.org/en/Security/CSP + if ( div.attachEvent ) { + for ( i in { + submit: 1, + change: 1, + focusin: 1 + }) { + eventName = "on" + i; + isSupported = ( eventName in div ); + if ( !isSupported ) { + div.setAttribute( eventName, "return;" ); + isSupported = ( typeof div[ eventName ] === "function" ); + } + support[ i + "Bubbles" ] = isSupported; + } + } + + fragment.removeChild( div ); + + // Null elements to avoid leaks in IE + fragment = select = opt = div = input = null; + + // Run tests that need a body at doc ready + jQuery(function() { + var container, outer, inner, table, td, offsetSupport, + marginDiv, conMarginTop, style, html, positionTopLeftWidthHeight, + paddingMarginBorderVisibility, paddingMarginBorder, + body = document.getElementsByTagName("body")[0]; + + if ( !body ) { + // Return for frameset docs that don't have a body + return; + } + + conMarginTop = 1; + paddingMarginBorder = "padding:0;margin:0;border:"; + positionTopLeftWidthHeight = "position:absolute;top:0;left:0;width:1px;height:1px;"; + paddingMarginBorderVisibility = paddingMarginBorder + "0;visibility:hidden;"; + style = "style='" + positionTopLeftWidthHeight + paddingMarginBorder + "5px solid #000;"; + html = "
" + + "" + + "
"; + + container = document.createElement("div"); + container.style.cssText = paddingMarginBorderVisibility + "width:0;height:0;position:static;top:0;margin-top:" + conMarginTop + "px"; + body.insertBefore( container, body.firstChild ); + + // Construct the test element + div = document.createElement("div"); + container.appendChild( div ); + + // Check if table cells still have offsetWidth/Height when they are set + // to display:none and there are still other visible table cells in a + // table row; if so, offsetWidth/Height are not reliable for use when + // determining if an element has been hidden directly using + // display:none (it is still safe to use offsets if a parent element is + // hidden; don safety goggles and see bug #4512 for more information). + // (only IE 8 fails this test) + div.innerHTML = "
t
"; + tds = div.getElementsByTagName( "td" ); + isSupported = ( tds[ 0 ].offsetHeight === 0 ); + + tds[ 0 ].style.display = ""; + tds[ 1 ].style.display = "none"; + + // Check if empty table cells still have offsetWidth/Height + // (IE <= 8 fail this test) + support.reliableHiddenOffsets = isSupported && ( tds[ 0 ].offsetHeight === 0 ); + + // Check if div with explicit width and no margin-right incorrectly + // gets computed margin-right based on width of container. For more + // info see bug #3333 + // Fails in WebKit before Feb 2011 nightlies + // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right + if ( window.getComputedStyle ) { + div.innerHTML = ""; + marginDiv = document.createElement( "div" ); + marginDiv.style.width = "0"; + marginDiv.style.marginRight = "0"; + div.style.width = "2px"; + div.appendChild( marginDiv ); + support.reliableMarginRight = + ( parseInt( ( window.getComputedStyle( marginDiv, null ) || { marginRight: 0 } ).marginRight, 10 ) || 0 ) === 0; + } + + if ( typeof div.style.zoom !== "undefined" ) { + // Check if natively block-level elements act like inline-block + // elements when setting their display to 'inline' and giving + // them layout + // (IE < 8 does this) + div.innerHTML = ""; + div.style.width = div.style.padding = "1px"; + div.style.border = 0; + div.style.overflow = "hidden"; + div.style.display = "inline"; + div.style.zoom = 1; + support.inlineBlockNeedsLayout = ( div.offsetWidth === 3 ); + + // Check if elements with layout shrink-wrap their children + // (IE 6 does this) + div.style.display = "block"; + div.style.overflow = "visible"; + div.innerHTML = "
"; + support.shrinkWrapBlocks = ( div.offsetWidth !== 3 ); + } + + div.style.cssText = positionTopLeftWidthHeight + paddingMarginBorderVisibility; + div.innerHTML = html; + + outer = div.firstChild; + inner = outer.firstChild; + td = outer.nextSibling.firstChild.firstChild; + + offsetSupport = { + doesNotAddBorder: ( inner.offsetTop !== 5 ), + doesAddBorderForTableAndCells: ( td.offsetTop === 5 ) + }; + + inner.style.position = "fixed"; + inner.style.top = "20px"; + + // safari subtracts parent border width here which is 5px + offsetSupport.fixedPosition = ( inner.offsetTop === 20 || inner.offsetTop === 15 ); + inner.style.position = inner.style.top = ""; + + outer.style.overflow = "hidden"; + outer.style.position = "relative"; + + offsetSupport.subtractsBorderForOverflowNotVisible = ( inner.offsetTop === -5 ); + offsetSupport.doesNotIncludeMarginInBodyOffset = ( body.offsetTop !== conMarginTop ); + + if ( window.getComputedStyle ) { + div.style.marginTop = "1%"; + support.pixelMargin = ( window.getComputedStyle( div, null ) || { marginTop: 0 } ).marginTop !== "1%"; + } + + if ( typeof container.style.zoom !== "undefined" ) { + container.style.zoom = 1; + } + + body.removeChild( container ); + marginDiv = div = container = null; + + jQuery.extend( support, offsetSupport ); + }); + + return support; +})(); + + + + +var rbrace = /^(?:\{.*\}|\[.*\])$/, + rmultiDash = /([A-Z])/g; + +jQuery.extend({ + cache: {}, + + // Please use with caution + uuid: 0, + + // Unique for each copy of jQuery on the page + // Non-digits removed to match rinlinejQuery + expando: "jQuery" + ( jQuery.fn.jquery + Math.random() ).replace( /\D/g, "" ), + + // The following elements throw uncatchable exceptions if you + // attempt to add expando properties to them. + noData: { + "embed": true, + // Ban all objects except for Flash (which handle expandos) + "object": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000", + "applet": true + }, + + hasData: function( elem ) { + elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ]; + return !!elem && !isEmptyDataObject( elem ); + }, + + data: function( elem, name, data, pvt /* Internal Use Only */ ) { + if ( !jQuery.acceptData( elem ) ) { + return; + } + + var privateCache, thisCache, ret, + internalKey = jQuery.expando, + getByName = typeof name === "string", + + // We have to handle DOM nodes and JS objects differently because IE6-7 + // can't GC object references properly across the DOM-JS boundary + isNode = elem.nodeType, + + // Only DOM nodes need the global jQuery cache; JS object data is + // attached directly to the object so GC can occur automatically + cache = isNode ? jQuery.cache : elem, + + // Only defining an ID for JS objects if its cache already exists allows + // the code to shortcut on the same path as a DOM node with no cache + id = isNode ? elem[ internalKey ] : elem[ internalKey ] && internalKey, + isEvents = name === "events"; + + // Avoid doing any more work than we need to when trying to get data on an + // object that has no data at all + if ( (!id || !cache[id] || (!isEvents && !pvt && !cache[id].data)) && getByName && data === undefined ) { + return; + } + + if ( !id ) { + // Only DOM nodes need a new unique ID for each element since their data + // ends up in the global cache + if ( isNode ) { + elem[ internalKey ] = id = ++jQuery.uuid; + } else { + id = internalKey; + } + } + + if ( !cache[ id ] ) { + cache[ id ] = {}; + + // Avoids exposing jQuery metadata on plain JS objects when the object + // is serialized using JSON.stringify + if ( !isNode ) { + cache[ id ].toJSON = jQuery.noop; + } + } + + // An object can be passed to jQuery.data instead of a key/value pair; this gets + // shallow copied over onto the existing cache + if ( typeof name === "object" || typeof name === "function" ) { + if ( pvt ) { + cache[ id ] = jQuery.extend( cache[ id ], name ); + } else { + cache[ id ].data = jQuery.extend( cache[ id ].data, name ); + } + } + + privateCache = thisCache = cache[ id ]; + + // jQuery data() is stored in a separate object inside the object's internal data + // cache in order to avoid key collisions between internal data and user-defined + // data. + if ( !pvt ) { + if ( !thisCache.data ) { + thisCache.data = {}; + } + + thisCache = thisCache.data; + } + + if ( data !== undefined ) { + thisCache[ jQuery.camelCase( name ) ] = data; + } + + // Users should not attempt to inspect the internal events object using jQuery.data, + // it is undocumented and subject to change. But does anyone listen? No. + if ( isEvents && !thisCache[ name ] ) { + return privateCache.events; + } + + // Check for both converted-to-camel and non-converted data property names + // If a data property was specified + if ( getByName ) { + + // First Try to find as-is property data + ret = thisCache[ name ]; + + // Test for null|undefined property data + if ( ret == null ) { + + // Try to find the camelCased property + ret = thisCache[ jQuery.camelCase( name ) ]; + } + } else { + ret = thisCache; + } + + return ret; + }, + + removeData: function( elem, name, pvt /* Internal Use Only */ ) { + if ( !jQuery.acceptData( elem ) ) { + return; + } + + var thisCache, i, l, + + // Reference to internal data cache key + internalKey = jQuery.expando, + + isNode = elem.nodeType, + + // See jQuery.data for more information + cache = isNode ? jQuery.cache : elem, + + // See jQuery.data for more information + id = isNode ? elem[ internalKey ] : internalKey; + + // If there is already no cache entry for this object, there is no + // purpose in continuing + if ( !cache[ id ] ) { + return; + } + + if ( name ) { + + thisCache = pvt ? cache[ id ] : cache[ id ].data; + + if ( thisCache ) { + + // Support array or space separated string names for data keys + if ( !jQuery.isArray( name ) ) { + + // try the string as a key before any manipulation + if ( name in thisCache ) { + name = [ name ]; + } else { + + // split the camel cased version by spaces unless a key with the spaces exists + name = jQuery.camelCase( name ); + if ( name in thisCache ) { + name = [ name ]; + } else { + name = name.split( " " ); + } + } + } + + for ( i = 0, l = name.length; i < l; i++ ) { + delete thisCache[ name[i] ]; + } + + // If there is no data left in the cache, we want to continue + // and let the cache object itself get destroyed + if ( !( pvt ? isEmptyDataObject : jQuery.isEmptyObject )( thisCache ) ) { + return; + } + } + } + + // See jQuery.data for more information + if ( !pvt ) { + delete cache[ id ].data; + + // Don't destroy the parent cache unless the internal data object + // had been the only thing left in it + if ( !isEmptyDataObject(cache[ id ]) ) { + return; + } + } + + // Browsers that fail expando deletion also refuse to delete expandos on + // the window, but it will allow it on all other JS objects; other browsers + // don't care + // Ensure that `cache` is not a window object #10080 + if ( jQuery.support.deleteExpando || !cache.setInterval ) { + delete cache[ id ]; + } else { + cache[ id ] = null; + } + + // We destroyed the cache and need to eliminate the expando on the node to avoid + // false lookups in the cache for entries that no longer exist + if ( isNode ) { + // IE does not allow us to delete expando properties from nodes, + // nor does it have a removeAttribute function on Document nodes; + // we must handle all of these cases + if ( jQuery.support.deleteExpando ) { + delete elem[ internalKey ]; + } else if ( elem.removeAttribute ) { + elem.removeAttribute( internalKey ); + } else { + elem[ internalKey ] = null; + } + } + }, + + // For internal use only. + _data: function( elem, name, data ) { + return jQuery.data( elem, name, data, true ); + }, + + // A method for determining if a DOM node can handle the data expando + acceptData: function( elem ) { + if ( elem.nodeName ) { + var match = jQuery.noData[ elem.nodeName.toLowerCase() ]; + + if ( match ) { + return !(match === true || elem.getAttribute("classid") !== match); + } + } + + return true; + } +}); + +jQuery.fn.extend({ + data: function( key, value ) { + var parts, part, attr, name, l, + elem = this[0], + i = 0, + data = null; + + // Gets all values + if ( key === undefined ) { + if ( this.length ) { + data = jQuery.data( elem ); + + if ( elem.nodeType === 1 && !jQuery._data( elem, "parsedAttrs" ) ) { + attr = elem.attributes; + for ( l = attr.length; i < l; i++ ) { + name = attr[i].name; + + if ( name.indexOf( "data-" ) === 0 ) { + name = jQuery.camelCase( name.substring(5) ); + + dataAttr( elem, name, data[ name ] ); + } + } + jQuery._data( elem, "parsedAttrs", true ); + } + } + + return data; + } + + // Sets multiple values + if ( typeof key === "object" ) { + return this.each(function() { + jQuery.data( this, key ); + }); + } + + parts = key.split( ".", 2 ); + parts[1] = parts[1] ? "." + parts[1] : ""; + part = parts[1] + "!"; + + return jQuery.access( this, function( value ) { + + if ( value === undefined ) { + data = this.triggerHandler( "getData" + part, [ parts[0] ] ); + + // Try to fetch any internally stored data first + if ( data === undefined && elem ) { + data = jQuery.data( elem, key ); + data = dataAttr( elem, key, data ); + } + + return data === undefined && parts[1] ? + this.data( parts[0] ) : + data; + } + + parts[1] = value; + this.each(function() { + var self = jQuery( this ); + + self.triggerHandler( "setData" + part, parts ); + jQuery.data( this, key, value ); + self.triggerHandler( "changeData" + part, parts ); + }); + }, null, value, arguments.length > 1, null, false ); + }, + + removeData: function( key ) { + return this.each(function() { + jQuery.removeData( this, key ); + }); + } +}); + +function dataAttr( elem, key, data ) { + // If nothing was found internally, try to fetch any + // data from the HTML5 data-* attribute + if ( data === undefined && elem.nodeType === 1 ) { + + var name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase(); + + data = elem.getAttribute( name ); + + if ( typeof data === "string" ) { + try { + data = data === "true" ? true : + data === "false" ? false : + data === "null" ? null : + jQuery.isNumeric( data ) ? +data : + rbrace.test( data ) ? jQuery.parseJSON( data ) : + data; + } catch( e ) {} + + // Make sure we set the data so it isn't changed later + jQuery.data( elem, key, data ); + + } else { + data = undefined; + } + } + + return data; +} + +// checks a cache object for emptiness +function isEmptyDataObject( obj ) { + for ( var name in obj ) { + + // if the public data object is empty, the private is still empty + if ( name === "data" && jQuery.isEmptyObject( obj[name] ) ) { + continue; + } + if ( name !== "toJSON" ) { + return false; + } + } + + return true; +} + + + + +function handleQueueMarkDefer( elem, type, src ) { + var deferDataKey = type + "defer", + queueDataKey = type + "queue", + markDataKey = type + "mark", + defer = jQuery._data( elem, deferDataKey ); + if ( defer && + ( src === "queue" || !jQuery._data(elem, queueDataKey) ) && + ( src === "mark" || !jQuery._data(elem, markDataKey) ) ) { + // Give room for hard-coded callbacks to fire first + // and eventually mark/queue something else on the element + setTimeout( function() { + if ( !jQuery._data( elem, queueDataKey ) && + !jQuery._data( elem, markDataKey ) ) { + jQuery.removeData( elem, deferDataKey, true ); + defer.fire(); + } + }, 0 ); + } +} + +jQuery.extend({ + + _mark: function( elem, type ) { + if ( elem ) { + type = ( type || "fx" ) + "mark"; + jQuery._data( elem, type, (jQuery._data( elem, type ) || 0) + 1 ); + } + }, + + _unmark: function( force, elem, type ) { + if ( force !== true ) { + type = elem; + elem = force; + force = false; + } + if ( elem ) { + type = type || "fx"; + var key = type + "mark", + count = force ? 0 : ( (jQuery._data( elem, key ) || 1) - 1 ); + if ( count ) { + jQuery._data( elem, key, count ); + } else { + jQuery.removeData( elem, key, true ); + handleQueueMarkDefer( elem, type, "mark" ); + } + } + }, + + queue: function( elem, type, data ) { + var q; + if ( elem ) { + type = ( type || "fx" ) + "queue"; + q = jQuery._data( elem, type ); + + // Speed up dequeue by getting out quickly if this is just a lookup + if ( data ) { + if ( !q || jQuery.isArray(data) ) { + q = jQuery._data( elem, type, jQuery.makeArray(data) ); + } else { + q.push( data ); + } + } + return q || []; + } + }, + + dequeue: function( elem, type ) { + type = type || "fx"; + + var queue = jQuery.queue( elem, type ), + fn = queue.shift(), + hooks = {}; + + // If the fx queue is dequeued, always remove the progress sentinel + if ( fn === "inprogress" ) { + fn = queue.shift(); + } + + if ( fn ) { + // Add a progress sentinel to prevent the fx queue from being + // automatically dequeued + if ( type === "fx" ) { + queue.unshift( "inprogress" ); + } + + jQuery._data( elem, type + ".run", hooks ); + fn.call( elem, function() { + jQuery.dequeue( elem, type ); + }, hooks ); + } + + if ( !queue.length ) { + jQuery.removeData( elem, type + "queue " + type + ".run", true ); + handleQueueMarkDefer( elem, type, "queue" ); + } + } +}); + +jQuery.fn.extend({ + queue: function( type, data ) { + var setter = 2; + + if ( typeof type !== "string" ) { + data = type; + type = "fx"; + setter--; + } + + if ( arguments.length < setter ) { + return jQuery.queue( this[0], type ); + } + + return data === undefined ? + this : + this.each(function() { + var queue = jQuery.queue( this, type, data ); + + if ( type === "fx" && queue[0] !== "inprogress" ) { + jQuery.dequeue( this, type ); + } + }); + }, + dequeue: function( type ) { + return this.each(function() { + jQuery.dequeue( this, type ); + }); + }, + // Based off of the plugin by Clint Helfers, with permission. + // http://blindsignals.com/index.php/2009/07/jquery-delay/ + delay: function( time, type ) { + time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time; + type = type || "fx"; + + return this.queue( type, function( next, hooks ) { + var timeout = setTimeout( next, time ); + hooks.stop = function() { + clearTimeout( timeout ); + }; + }); + }, + clearQueue: function( type ) { + return this.queue( type || "fx", [] ); + }, + // Get a promise resolved when queues of a certain type + // are emptied (fx is the type by default) + promise: function( type, object ) { + if ( typeof type !== "string" ) { + object = type; + type = undefined; + } + type = type || "fx"; + var defer = jQuery.Deferred(), + elements = this, + i = elements.length, + count = 1, + deferDataKey = type + "defer", + queueDataKey = type + "queue", + markDataKey = type + "mark", + tmp; + function resolve() { + if ( !( --count ) ) { + defer.resolveWith( elements, [ elements ] ); + } + } + while( i-- ) { + if (( tmp = jQuery.data( elements[ i ], deferDataKey, undefined, true ) || + ( jQuery.data( elements[ i ], queueDataKey, undefined, true ) || + jQuery.data( elements[ i ], markDataKey, undefined, true ) ) && + jQuery.data( elements[ i ], deferDataKey, jQuery.Callbacks( "once memory" ), true ) )) { + count++; + tmp.add( resolve ); + } + } + resolve(); + return defer.promise( object ); + } +}); + + + + +var rclass = /[\n\t\r]/g, + rspace = /\s+/, + rreturn = /\r/g, + rtype = /^(?:button|input)$/i, + rfocusable = /^(?:button|input|object|select|textarea)$/i, + rclickable = /^a(?:rea)?$/i, + rboolean = /^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i, + getSetAttribute = jQuery.support.getSetAttribute, + nodeHook, boolHook, fixSpecified; + +jQuery.fn.extend({ + attr: function( name, value ) { + return jQuery.access( this, jQuery.attr, name, value, arguments.length > 1 ); + }, + + removeAttr: function( name ) { + return this.each(function() { + jQuery.removeAttr( this, name ); + }); + }, + + prop: function( name, value ) { + return jQuery.access( this, jQuery.prop, name, value, arguments.length > 1 ); + }, + + removeProp: function( name ) { + name = jQuery.propFix[ name ] || name; + return this.each(function() { + // try/catch handles cases where IE balks (such as removing a property on window) + try { + this[ name ] = undefined; + delete this[ name ]; + } catch( e ) {} + }); + }, + + addClass: function( value ) { + var classNames, i, l, elem, + setClass, c, cl; + + if ( jQuery.isFunction( value ) ) { + return this.each(function( j ) { + jQuery( this ).addClass( value.call(this, j, this.className) ); + }); + } + + if ( value && typeof value === "string" ) { + classNames = value.split( rspace ); + + for ( i = 0, l = this.length; i < l; i++ ) { + elem = this[ i ]; + + if ( elem.nodeType === 1 ) { + if ( !elem.className && classNames.length === 1 ) { + elem.className = value; + + } else { + setClass = " " + elem.className + " "; + + for ( c = 0, cl = classNames.length; c < cl; c++ ) { + if ( !~setClass.indexOf( " " + classNames[ c ] + " " ) ) { + setClass += classNames[ c ] + " "; + } + } + elem.className = jQuery.trim( setClass ); + } + } + } + } + + return this; + }, + + removeClass: function( value ) { + var classNames, i, l, elem, className, c, cl; + + if ( jQuery.isFunction( value ) ) { + return this.each(function( j ) { + jQuery( this ).removeClass( value.call(this, j, this.className) ); + }); + } + + if ( (value && typeof value === "string") || value === undefined ) { + classNames = ( value || "" ).split( rspace ); + + for ( i = 0, l = this.length; i < l; i++ ) { + elem = this[ i ]; + + if ( elem.nodeType === 1 && elem.className ) { + if ( value ) { + className = (" " + elem.className + " ").replace( rclass, " " ); + for ( c = 0, cl = classNames.length; c < cl; c++ ) { + className = className.replace(" " + classNames[ c ] + " ", " "); + } + elem.className = jQuery.trim( className ); + + } else { + elem.className = ""; + } + } + } + } + + return this; + }, + + toggleClass: function( value, stateVal ) { + var type = typeof value, + isBool = typeof stateVal === "boolean"; + + if ( jQuery.isFunction( value ) ) { + return this.each(function( i ) { + jQuery( this ).toggleClass( value.call(this, i, this.className, stateVal), stateVal ); + }); + } + + return this.each(function() { + if ( type === "string" ) { + // toggle individual class names + var className, + i = 0, + self = jQuery( this ), + state = stateVal, + classNames = value.split( rspace ); + + while ( (className = classNames[ i++ ]) ) { + // check each className given, space seperated list + state = isBool ? state : !self.hasClass( className ); + self[ state ? "addClass" : "removeClass" ]( className ); + } + + } else if ( type === "undefined" || type === "boolean" ) { + if ( this.className ) { + // store className if set + jQuery._data( this, "__className__", this.className ); + } + + // toggle whole className + this.className = this.className || value === false ? "" : jQuery._data( this, "__className__" ) || ""; + } + }); + }, + + hasClass: function( selector ) { + var className = " " + selector + " ", + i = 0, + l = this.length; + for ( ; i < l; i++ ) { + if ( this[i].nodeType === 1 && (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) > -1 ) { + return true; + } + } + + return false; + }, + + val: function( value ) { + var hooks, ret, isFunction, + elem = this[0]; + + if ( !arguments.length ) { + if ( elem ) { + hooks = jQuery.valHooks[ elem.type ] || jQuery.valHooks[ elem.nodeName.toLowerCase() ]; + + if ( hooks && "get" in hooks && (ret = hooks.get( elem, "value" )) !== undefined ) { + return ret; + } + + ret = elem.value; + + return typeof ret === "string" ? + // handle most common string cases + ret.replace(rreturn, "") : + // handle cases where value is null/undef or number + ret == null ? "" : ret; + } + + return; + } + + isFunction = jQuery.isFunction( value ); + + return this.each(function( i ) { + var self = jQuery(this), val; + + if ( this.nodeType !== 1 ) { + return; + } + + if ( isFunction ) { + val = value.call( this, i, self.val() ); + } else { + val = value; + } + + // Treat null/undefined as ""; convert numbers to string + if ( val == null ) { + val = ""; + } else if ( typeof val === "number" ) { + val += ""; + } else if ( jQuery.isArray( val ) ) { + val = jQuery.map(val, function ( value ) { + return value == null ? "" : value + ""; + }); + } + + hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ]; + + // If set returns undefined, fall back to normal setting + if ( !hooks || !("set" in hooks) || hooks.set( this, val, "value" ) === undefined ) { + this.value = val; + } + }); + } +}); + +jQuery.extend({ + valHooks: { + option: { + get: function( elem ) { + // attributes.value is undefined in Blackberry 4.7 but + // uses .value. See #6932 + var val = elem.attributes.value; + return !val || val.specified ? elem.value : elem.text; + } + }, + select: { + get: function( elem ) { + var value, i, max, option, + index = elem.selectedIndex, + values = [], + options = elem.options, + one = elem.type === "select-one"; + + // Nothing was selected + if ( index < 0 ) { + return null; + } + + // Loop through all the selected options + i = one ? index : 0; + max = one ? index + 1 : options.length; + for ( ; i < max; i++ ) { + option = options[ i ]; + + // Don't return options that are disabled or in a disabled optgroup + if ( option.selected && (jQuery.support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null) && + (!option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" )) ) { + + // Get the specific value for the option + value = jQuery( option ).val(); + + // We don't need an array for one selects + if ( one ) { + return value; + } + + // Multi-Selects return an array + values.push( value ); + } + } + + // Fixes Bug #2551 -- select.val() broken in IE after form.reset() + if ( one && !values.length && options.length ) { + return jQuery( options[ index ] ).val(); + } + + return values; + }, + + set: function( elem, value ) { + var values = jQuery.makeArray( value ); + + jQuery(elem).find("option").each(function() { + this.selected = jQuery.inArray( jQuery(this).val(), values ) >= 0; + }); + + if ( !values.length ) { + elem.selectedIndex = -1; + } + return values; + } + } + }, + + attrFn: { + val: true, + css: true, + html: true, + text: true, + data: true, + width: true, + height: true, + offset: true + }, + + attr: function( elem, name, value, pass ) { + var ret, hooks, notxml, + nType = elem.nodeType; + + // don't get/set attributes on text, comment and attribute nodes + if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { + return; + } + + if ( pass && name in jQuery.attrFn ) { + return jQuery( elem )[ name ]( value ); + } + + // Fallback to prop when attributes are not supported + if ( typeof elem.getAttribute === "undefined" ) { + return jQuery.prop( elem, name, value ); + } + + notxml = nType !== 1 || !jQuery.isXMLDoc( elem ); + + // All attributes are lowercase + // Grab necessary hook if one is defined + if ( notxml ) { + name = name.toLowerCase(); + hooks = jQuery.attrHooks[ name ] || ( rboolean.test( name ) ? boolHook : nodeHook ); + } + + if ( value !== undefined ) { + + if ( value === null ) { + jQuery.removeAttr( elem, name ); + return; + + } else if ( hooks && "set" in hooks && notxml && (ret = hooks.set( elem, value, name )) !== undefined ) { + return ret; + + } else { + elem.setAttribute( name, "" + value ); + return value; + } + + } else if ( hooks && "get" in hooks && notxml && (ret = hooks.get( elem, name )) !== null ) { + return ret; + + } else { + + ret = elem.getAttribute( name ); + + // Non-existent attributes return null, we normalize to undefined + return ret === null ? + undefined : + ret; + } + }, + + removeAttr: function( elem, value ) { + var propName, attrNames, name, l, isBool, + i = 0; + + if ( value && elem.nodeType === 1 ) { + attrNames = value.toLowerCase().split( rspace ); + l = attrNames.length; + + for ( ; i < l; i++ ) { + name = attrNames[ i ]; + + if ( name ) { + propName = jQuery.propFix[ name ] || name; + isBool = rboolean.test( name ); + + // See #9699 for explanation of this approach (setting first, then removal) + // Do not do this for boolean attributes (see #10870) + if ( !isBool ) { + jQuery.attr( elem, name, "" ); + } + elem.removeAttribute( getSetAttribute ? name : propName ); + + // Set corresponding property to false for boolean attributes + if ( isBool && propName in elem ) { + elem[ propName ] = false; + } + } + } + } + }, + + attrHooks: { + type: { + set: function( elem, value ) { + // We can't allow the type property to be changed (since it causes problems in IE) + if ( rtype.test( elem.nodeName ) && elem.parentNode ) { + jQuery.error( "type property can't be changed" ); + } else if ( !jQuery.support.radioValue && value === "radio" && jQuery.nodeName(elem, "input") ) { + // Setting the type on a radio button after the value resets the value in IE6-9 + // Reset value to it's default in case type is set after value + // This is for element creation + var val = elem.value; + elem.setAttribute( "type", value ); + if ( val ) { + elem.value = val; + } + return value; + } + } + }, + // Use the value property for back compat + // Use the nodeHook for button elements in IE6/7 (#1954) + value: { + get: function( elem, name ) { + if ( nodeHook && jQuery.nodeName( elem, "button" ) ) { + return nodeHook.get( elem, name ); + } + return name in elem ? + elem.value : + null; + }, + set: function( elem, value, name ) { + if ( nodeHook && jQuery.nodeName( elem, "button" ) ) { + return nodeHook.set( elem, value, name ); + } + // Does not return so that setAttribute is also used + elem.value = value; + } + } + }, + + propFix: { + tabindex: "tabIndex", + readonly: "readOnly", + "for": "htmlFor", + "class": "className", + maxlength: "maxLength", + cellspacing: "cellSpacing", + cellpadding: "cellPadding", + rowspan: "rowSpan", + colspan: "colSpan", + usemap: "useMap", + frameborder: "frameBorder", + contenteditable: "contentEditable" + }, + + prop: function( elem, name, value ) { + var ret, hooks, notxml, + nType = elem.nodeType; + + // don't get/set properties on text, comment and attribute nodes + if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { + return; + } + + notxml = nType !== 1 || !jQuery.isXMLDoc( elem ); + + if ( notxml ) { + // Fix name and attach hooks + name = jQuery.propFix[ name ] || name; + hooks = jQuery.propHooks[ name ]; + } + + if ( value !== undefined ) { + if ( hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) { + return ret; + + } else { + return ( elem[ name ] = value ); + } + + } else { + if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) { + return ret; + + } else { + return elem[ name ]; + } + } + }, + + propHooks: { + tabIndex: { + get: function( elem ) { + // elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set + // http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/ + var attributeNode = elem.getAttributeNode("tabindex"); + + return attributeNode && attributeNode.specified ? + parseInt( attributeNode.value, 10 ) : + rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ? + 0 : + undefined; + } + } + } +}); + +// Add the tabIndex propHook to attrHooks for back-compat (different case is intentional) +jQuery.attrHooks.tabindex = jQuery.propHooks.tabIndex; + +// Hook for boolean attributes +boolHook = { + get: function( elem, name ) { + // Align boolean attributes with corresponding properties + // Fall back to attribute presence where some booleans are not supported + var attrNode, + property = jQuery.prop( elem, name ); + return property === true || typeof property !== "boolean" && ( attrNode = elem.getAttributeNode(name) ) && attrNode.nodeValue !== false ? + name.toLowerCase() : + undefined; + }, + set: function( elem, value, name ) { + var propName; + if ( value === false ) { + // Remove boolean attributes when set to false + jQuery.removeAttr( elem, name ); + } else { + // value is true since we know at this point it's type boolean and not false + // Set boolean attributes to the same name and set the DOM property + propName = jQuery.propFix[ name ] || name; + if ( propName in elem ) { + // Only set the IDL specifically if it already exists on the element + elem[ propName ] = true; + } + + elem.setAttribute( name, name.toLowerCase() ); + } + return name; + } +}; + +// IE6/7 do not support getting/setting some attributes with get/setAttribute +if ( !getSetAttribute ) { + + fixSpecified = { + name: true, + id: true, + coords: true + }; + + // Use this for any attribute in IE6/7 + // This fixes almost every IE6/7 issue + nodeHook = jQuery.valHooks.button = { + get: function( elem, name ) { + var ret; + ret = elem.getAttributeNode( name ); + return ret && ( fixSpecified[ name ] ? ret.nodeValue !== "" : ret.specified ) ? + ret.nodeValue : + undefined; + }, + set: function( elem, value, name ) { + // Set the existing or create a new attribute node + var ret = elem.getAttributeNode( name ); + if ( !ret ) { + ret = document.createAttribute( name ); + elem.setAttributeNode( ret ); + } + return ( ret.nodeValue = value + "" ); + } + }; + + // Apply the nodeHook to tabindex + jQuery.attrHooks.tabindex.set = nodeHook.set; + + // Set width and height to auto instead of 0 on empty string( Bug #8150 ) + // This is for removals + jQuery.each([ "width", "height" ], function( i, name ) { + jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], { + set: function( elem, value ) { + if ( value === "" ) { + elem.setAttribute( name, "auto" ); + return value; + } + } + }); + }); + + // Set contenteditable to false on removals(#10429) + // Setting to empty string throws an error as an invalid value + jQuery.attrHooks.contenteditable = { + get: nodeHook.get, + set: function( elem, value, name ) { + if ( value === "" ) { + value = "false"; + } + nodeHook.set( elem, value, name ); + } + }; +} + + +// Some attributes require a special call on IE +if ( !jQuery.support.hrefNormalized ) { + jQuery.each([ "href", "src", "width", "height" ], function( i, name ) { + jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], { + get: function( elem ) { + var ret = elem.getAttribute( name, 2 ); + return ret === null ? undefined : ret; + } + }); + }); +} + +if ( !jQuery.support.style ) { + jQuery.attrHooks.style = { + get: function( elem ) { + // Return undefined in the case of empty string + // Normalize to lowercase since IE uppercases css property names + return elem.style.cssText.toLowerCase() || undefined; + }, + set: function( elem, value ) { + return ( elem.style.cssText = "" + value ); + } + }; +} + +// Safari mis-reports the default selected property of an option +// Accessing the parent's selectedIndex property fixes it +if ( !jQuery.support.optSelected ) { + jQuery.propHooks.selected = jQuery.extend( jQuery.propHooks.selected, { + get: function( elem ) { + var parent = elem.parentNode; + + if ( parent ) { + parent.selectedIndex; + + // Make sure that it also works with optgroups, see #5701 + if ( parent.parentNode ) { + parent.parentNode.selectedIndex; + } + } + return null; + } + }); +} + +// IE6/7 call enctype encoding +if ( !jQuery.support.enctype ) { + jQuery.propFix.enctype = "encoding"; +} + +// Radios and checkboxes getter/setter +if ( !jQuery.support.checkOn ) { + jQuery.each([ "radio", "checkbox" ], function() { + jQuery.valHooks[ this ] = { + get: function( elem ) { + // Handle the case where in Webkit "" is returned instead of "on" if a value isn't specified + return elem.getAttribute("value") === null ? "on" : elem.value; + } + }; + }); +} +jQuery.each([ "radio", "checkbox" ], function() { + jQuery.valHooks[ this ] = jQuery.extend( jQuery.valHooks[ this ], { + set: function( elem, value ) { + if ( jQuery.isArray( value ) ) { + return ( elem.checked = jQuery.inArray( jQuery(elem).val(), value ) >= 0 ); + } + } + }); +}); + + + + +var rformElems = /^(?:textarea|input|select)$/i, + rtypenamespace = /^([^\.]*)?(?:\.(.+))?$/, + rhoverHack = /(?:^|\s)hover(\.\S+)?\b/, + rkeyEvent = /^key/, + rmouseEvent = /^(?:mouse|contextmenu)|click/, + rfocusMorph = /^(?:focusinfocus|focusoutblur)$/, + rquickIs = /^(\w*)(?:#([\w\-]+))?(?:\.([\w\-]+))?$/, + quickParse = function( selector ) { + var quick = rquickIs.exec( selector ); + if ( quick ) { + // 0 1 2 3 + // [ _, tag, id, class ] + quick[1] = ( quick[1] || "" ).toLowerCase(); + quick[3] = quick[3] && new RegExp( "(?:^|\\s)" + quick[3] + "(?:\\s|$)" ); + } + return quick; + }, + quickIs = function( elem, m ) { + var attrs = elem.attributes || {}; + return ( + (!m[1] || elem.nodeName.toLowerCase() === m[1]) && + (!m[2] || (attrs.id || {}).value === m[2]) && + (!m[3] || m[3].test( (attrs[ "class" ] || {}).value )) + ); + }, + hoverHack = function( events ) { + return jQuery.event.special.hover ? events : events.replace( rhoverHack, "mouseenter$1 mouseleave$1" ); + }; + +/* + * Helper functions for managing events -- not part of the public interface. + * Props to Dean Edwards' addEvent library for many of the ideas. + */ +jQuery.event = { + + add: function( elem, types, handler, data, selector ) { + + var elemData, eventHandle, events, + t, tns, type, namespaces, handleObj, + handleObjIn, quick, handlers, special; + + // Don't attach events to noData or text/comment nodes (allow plain objects tho) + if ( elem.nodeType === 3 || elem.nodeType === 8 || !types || !handler || !(elemData = jQuery._data( elem )) ) { + return; + } + + // Caller can pass in an object of custom data in lieu of the handler + if ( handler.handler ) { + handleObjIn = handler; + handler = handleObjIn.handler; + selector = handleObjIn.selector; + } + + // Make sure that the handler has a unique ID, used to find/remove it later + if ( !handler.guid ) { + handler.guid = jQuery.guid++; + } + + // Init the element's event structure and main handler, if this is the first + events = elemData.events; + if ( !events ) { + elemData.events = events = {}; + } + eventHandle = elemData.handle; + if ( !eventHandle ) { + elemData.handle = eventHandle = function( e ) { + // Discard the second event of a jQuery.event.trigger() and + // when an event is called after a page has unloaded + return typeof jQuery !== "undefined" && (!e || jQuery.event.triggered !== e.type) ? + jQuery.event.dispatch.apply( eventHandle.elem, arguments ) : + undefined; + }; + // Add elem as a property of the handle fn to prevent a memory leak with IE non-native events + eventHandle.elem = elem; + } + + // Handle multiple events separated by a space + // jQuery(...).bind("mouseover mouseout", fn); + types = jQuery.trim( hoverHack(types) ).split( " " ); + for ( t = 0; t < types.length; t++ ) { + + tns = rtypenamespace.exec( types[t] ) || []; + type = tns[1]; + namespaces = ( tns[2] || "" ).split( "." ).sort(); + + // If event changes its type, use the special event handlers for the changed type + special = jQuery.event.special[ type ] || {}; + + // If selector defined, determine special event api type, otherwise given type + type = ( selector ? special.delegateType : special.bindType ) || type; + + // Update special based on newly reset type + special = jQuery.event.special[ type ] || {}; + + // handleObj is passed to all event handlers + handleObj = jQuery.extend({ + type: type, + origType: tns[1], + data: data, + handler: handler, + guid: handler.guid, + selector: selector, + quick: selector && quickParse( selector ), + namespace: namespaces.join(".") + }, handleObjIn ); + + // Init the event handler queue if we're the first + handlers = events[ type ]; + if ( !handlers ) { + handlers = events[ type ] = []; + handlers.delegateCount = 0; + + // Only use addEventListener/attachEvent if the special events handler returns false + if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) { + // Bind the global event handler to the element + if ( elem.addEventListener ) { + elem.addEventListener( type, eventHandle, false ); + + } else if ( elem.attachEvent ) { + elem.attachEvent( "on" + type, eventHandle ); + } + } + } + + if ( special.add ) { + special.add.call( elem, handleObj ); + + if ( !handleObj.handler.guid ) { + handleObj.handler.guid = handler.guid; + } + } + + // Add to the element's handler list, delegates in front + if ( selector ) { + handlers.splice( handlers.delegateCount++, 0, handleObj ); + } else { + handlers.push( handleObj ); + } + + // Keep track of which events have ever been used, for event optimization + jQuery.event.global[ type ] = true; + } + + // Nullify elem to prevent memory leaks in IE + elem = null; + }, + + global: {}, + + // Detach an event or set of events from an element + remove: function( elem, types, handler, selector, mappedTypes ) { + + var elemData = jQuery.hasData( elem ) && jQuery._data( elem ), + t, tns, type, origType, namespaces, origCount, + j, events, special, handle, eventType, handleObj; + + if ( !elemData || !(events = elemData.events) ) { + return; + } + + // Once for each type.namespace in types; type may be omitted + types = jQuery.trim( hoverHack( types || "" ) ).split(" "); + for ( t = 0; t < types.length; t++ ) { + tns = rtypenamespace.exec( types[t] ) || []; + type = origType = tns[1]; + namespaces = tns[2]; + + // Unbind all events (on this namespace, if provided) for the element + if ( !type ) { + for ( type in events ) { + jQuery.event.remove( elem, type + types[ t ], handler, selector, true ); + } + continue; + } + + special = jQuery.event.special[ type ] || {}; + type = ( selector? special.delegateType : special.bindType ) || type; + eventType = events[ type ] || []; + origCount = eventType.length; + namespaces = namespaces ? new RegExp("(^|\\.)" + namespaces.split(".").sort().join("\\.(?:.*\\.)?") + "(\\.|$)") : null; + + // Remove matching events + for ( j = 0; j < eventType.length; j++ ) { + handleObj = eventType[ j ]; + + if ( ( mappedTypes || origType === handleObj.origType ) && + ( !handler || handler.guid === handleObj.guid ) && + ( !namespaces || namespaces.test( handleObj.namespace ) ) && + ( !selector || selector === handleObj.selector || selector === "**" && handleObj.selector ) ) { + eventType.splice( j--, 1 ); + + if ( handleObj.selector ) { + eventType.delegateCount--; + } + if ( special.remove ) { + special.remove.call( elem, handleObj ); + } + } + } + + // Remove generic event handler if we removed something and no more handlers exist + // (avoids potential for endless recursion during removal of special event handlers) + if ( eventType.length === 0 && origCount !== eventType.length ) { + if ( !special.teardown || special.teardown.call( elem, namespaces ) === false ) { + jQuery.removeEvent( elem, type, elemData.handle ); + } + + delete events[ type ]; + } + } + + // Remove the expando if it's no longer used + if ( jQuery.isEmptyObject( events ) ) { + handle = elemData.handle; + if ( handle ) { + handle.elem = null; + } + + // removeData also checks for emptiness and clears the expando if empty + // so use it instead of delete + jQuery.removeData( elem, [ "events", "handle" ], true ); + } + }, + + // Events that are safe to short-circuit if no handlers are attached. + // Native DOM events should not be added, they may have inline handlers. + customEvent: { + "getData": true, + "setData": true, + "changeData": true + }, + + trigger: function( event, data, elem, onlyHandlers ) { + // Don't do events on text and comment nodes + if ( elem && (elem.nodeType === 3 || elem.nodeType === 8) ) { + return; + } + + // Event object or event type + var type = event.type || event, + namespaces = [], + cache, exclusive, i, cur, old, ontype, special, handle, eventPath, bubbleType; + + // focus/blur morphs to focusin/out; ensure we're not firing them right now + if ( rfocusMorph.test( type + jQuery.event.triggered ) ) { + return; + } + + if ( type.indexOf( "!" ) >= 0 ) { + // Exclusive events trigger only for the exact event (no namespaces) + type = type.slice(0, -1); + exclusive = true; + } + + if ( type.indexOf( "." ) >= 0 ) { + // Namespaced trigger; create a regexp to match event type in handle() + namespaces = type.split("."); + type = namespaces.shift(); + namespaces.sort(); + } + + if ( (!elem || jQuery.event.customEvent[ type ]) && !jQuery.event.global[ type ] ) { + // No jQuery handlers for this event type, and it can't have inline handlers + return; + } + + // Caller can pass in an Event, Object, or just an event type string + event = typeof event === "object" ? + // jQuery.Event object + event[ jQuery.expando ] ? event : + // Object literal + new jQuery.Event( type, event ) : + // Just the event type (string) + new jQuery.Event( type ); + + event.type = type; + event.isTrigger = true; + event.exclusive = exclusive; + event.namespace = namespaces.join( "." ); + event.namespace_re = event.namespace? new RegExp("(^|\\.)" + namespaces.join("\\.(?:.*\\.)?") + "(\\.|$)") : null; + ontype = type.indexOf( ":" ) < 0 ? "on" + type : ""; + + // Handle a global trigger + if ( !elem ) { + + // TODO: Stop taunting the data cache; remove global events and always attach to document + cache = jQuery.cache; + for ( i in cache ) { + if ( cache[ i ].events && cache[ i ].events[ type ] ) { + jQuery.event.trigger( event, data, cache[ i ].handle.elem, true ); + } + } + return; + } + + // Clean up the event in case it is being reused + event.result = undefined; + if ( !event.target ) { + event.target = elem; + } + + // Clone any incoming data and prepend the event, creating the handler arg list + data = data != null ? jQuery.makeArray( data ) : []; + data.unshift( event ); + + // Allow special events to draw outside the lines + special = jQuery.event.special[ type ] || {}; + if ( special.trigger && special.trigger.apply( elem, data ) === false ) { + return; + } + + // Determine event propagation path in advance, per W3C events spec (#9951) + // Bubble up to document, then to window; watch for a global ownerDocument var (#9724) + eventPath = [[ elem, special.bindType || type ]]; + if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) { + + bubbleType = special.delegateType || type; + cur = rfocusMorph.test( bubbleType + type ) ? elem : elem.parentNode; + old = null; + for ( ; cur; cur = cur.parentNode ) { + eventPath.push([ cur, bubbleType ]); + old = cur; + } + + // Only add window if we got to document (e.g., not plain obj or detached DOM) + if ( old && old === elem.ownerDocument ) { + eventPath.push([ old.defaultView || old.parentWindow || window, bubbleType ]); + } + } + + // Fire handlers on the event path + for ( i = 0; i < eventPath.length && !event.isPropagationStopped(); i++ ) { + + cur = eventPath[i][0]; + event.type = eventPath[i][1]; + + handle = ( jQuery._data( cur, "events" ) || {} )[ event.type ] && jQuery._data( cur, "handle" ); + if ( handle ) { + handle.apply( cur, data ); + } + // Note that this is a bare JS function and not a jQuery handler + handle = ontype && cur[ ontype ]; + if ( handle && jQuery.acceptData( cur ) && handle.apply( cur, data ) === false ) { + event.preventDefault(); + } + } + event.type = type; + + // If nobody prevented the default action, do it now + if ( !onlyHandlers && !event.isDefaultPrevented() ) { + + if ( (!special._default || special._default.apply( elem.ownerDocument, data ) === false) && + !(type === "click" && jQuery.nodeName( elem, "a" )) && jQuery.acceptData( elem ) ) { + + // Call a native DOM method on the target with the same name name as the event. + // Can't use an .isFunction() check here because IE6/7 fails that test. + // Don't do default actions on window, that's where global variables be (#6170) + // IE<9 dies on focus/blur to hidden element (#1486) + if ( ontype && elem[ type ] && ((type !== "focus" && type !== "blur") || event.target.offsetWidth !== 0) && !jQuery.isWindow( elem ) ) { + + // Don't re-trigger an onFOO event when we call its FOO() method + old = elem[ ontype ]; + + if ( old ) { + elem[ ontype ] = null; + } + + // Prevent re-triggering of the same event, since we already bubbled it above + jQuery.event.triggered = type; + elem[ type ](); + jQuery.event.triggered = undefined; + + if ( old ) { + elem[ ontype ] = old; + } + } + } + } + + return event.result; + }, + + dispatch: function( event ) { + + // Make a writable jQuery.Event from the native event object + event = jQuery.event.fix( event || window.event ); + + var handlers = ( (jQuery._data( this, "events" ) || {} )[ event.type ] || []), + delegateCount = handlers.delegateCount, + args = [].slice.call( arguments, 0 ), + run_all = !event.exclusive && !event.namespace, + special = jQuery.event.special[ event.type ] || {}, + handlerQueue = [], + i, j, cur, jqcur, ret, selMatch, matched, matches, handleObj, sel, related; + + // Use the fix-ed jQuery.Event rather than the (read-only) native event + args[0] = event; + event.delegateTarget = this; + + // Call the preDispatch hook for the mapped type, and let it bail if desired + if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) { + return; + } + + // Determine handlers that should run if there are delegated events + // Avoid non-left-click bubbling in Firefox (#3861) + if ( delegateCount && !(event.button && event.type === "click") ) { + + // Pregenerate a single jQuery object for reuse with .is() + jqcur = jQuery(this); + jqcur.context = this.ownerDocument || this; + + for ( cur = event.target; cur != this; cur = cur.parentNode || this ) { + + // Don't process events on disabled elements (#6911, #8165) + if ( cur.disabled !== true ) { + selMatch = {}; + matches = []; + jqcur[0] = cur; + for ( i = 0; i < delegateCount; i++ ) { + handleObj = handlers[ i ]; + sel = handleObj.selector; + + if ( selMatch[ sel ] === undefined ) { + selMatch[ sel ] = ( + handleObj.quick ? quickIs( cur, handleObj.quick ) : jqcur.is( sel ) + ); + } + if ( selMatch[ sel ] ) { + matches.push( handleObj ); + } + } + if ( matches.length ) { + handlerQueue.push({ elem: cur, matches: matches }); + } + } + } + } + + // Add the remaining (directly-bound) handlers + if ( handlers.length > delegateCount ) { + handlerQueue.push({ elem: this, matches: handlers.slice( delegateCount ) }); + } + + // Run delegates first; they may want to stop propagation beneath us + for ( i = 0; i < handlerQueue.length && !event.isPropagationStopped(); i++ ) { + matched = handlerQueue[ i ]; + event.currentTarget = matched.elem; + + for ( j = 0; j < matched.matches.length && !event.isImmediatePropagationStopped(); j++ ) { + handleObj = matched.matches[ j ]; + + // Triggered event must either 1) be non-exclusive and have no namespace, or + // 2) have namespace(s) a subset or equal to those in the bound event (both can have no namespace). + if ( run_all || (!event.namespace && !handleObj.namespace) || event.namespace_re && event.namespace_re.test( handleObj.namespace ) ) { + + event.data = handleObj.data; + event.handleObj = handleObj; + + ret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle || handleObj.handler ) + .apply( matched.elem, args ); + + if ( ret !== undefined ) { + event.result = ret; + if ( ret === false ) { + event.preventDefault(); + event.stopPropagation(); + } + } + } + } + } + + // Call the postDispatch hook for the mapped type + if ( special.postDispatch ) { + special.postDispatch.call( this, event ); + } + + return event.result; + }, + + // Includes some event props shared by KeyEvent and MouseEvent + // *** attrChange attrName relatedNode srcElement are not normalized, non-W3C, deprecated, will be removed in 1.8 *** + props: "attrChange attrName relatedNode srcElement altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "), + + fixHooks: {}, + + keyHooks: { + props: "char charCode key keyCode".split(" "), + filter: function( event, original ) { + + // Add which for key events + if ( event.which == null ) { + event.which = original.charCode != null ? original.charCode : original.keyCode; + } + + return event; + } + }, + + mouseHooks: { + props: "button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "), + filter: function( event, original ) { + var eventDoc, doc, body, + button = original.button, + fromElement = original.fromElement; + + // Calculate pageX/Y if missing and clientX/Y available + if ( event.pageX == null && original.clientX != null ) { + eventDoc = event.target.ownerDocument || document; + doc = eventDoc.documentElement; + body = eventDoc.body; + + event.pageX = original.clientX + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - ( doc && doc.clientLeft || body && body.clientLeft || 0 ); + event.pageY = original.clientY + ( doc && doc.scrollTop || body && body.scrollTop || 0 ) - ( doc && doc.clientTop || body && body.clientTop || 0 ); + } + + // Add relatedTarget, if necessary + if ( !event.relatedTarget && fromElement ) { + event.relatedTarget = fromElement === event.target ? original.toElement : fromElement; + } + + // Add which for click: 1 === left; 2 === middle; 3 === right + // Note: button is not normalized, so don't use it + if ( !event.which && button !== undefined ) { + event.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) ); + } + + return event; + } + }, + + fix: function( event ) { + if ( event[ jQuery.expando ] ) { + return event; + } + + // Create a writable copy of the event object and normalize some properties + var i, prop, + originalEvent = event, + fixHook = jQuery.event.fixHooks[ event.type ] || {}, + copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props; + + event = jQuery.Event( originalEvent ); + + for ( i = copy.length; i; ) { + prop = copy[ --i ]; + event[ prop ] = originalEvent[ prop ]; + } + + // Fix target property, if necessary (#1925, IE 6/7/8 & Safari2) + if ( !event.target ) { + event.target = originalEvent.srcElement || document; + } + + // Target should not be a text node (#504, Safari) + if ( event.target.nodeType === 3 ) { + event.target = event.target.parentNode; + } + + // For mouse/key events; add metaKey if it's not there (#3368, IE6/7/8) + if ( event.metaKey === undefined ) { + event.metaKey = event.ctrlKey; + } + + return fixHook.filter? fixHook.filter( event, originalEvent ) : event; + }, + + special: { + ready: { + // Make sure the ready event is setup + setup: jQuery.bindReady + }, + + load: { + // Prevent triggered image.load events from bubbling to window.load + noBubble: true + }, + + focus: { + delegateType: "focusin" + }, + blur: { + delegateType: "focusout" + }, + + beforeunload: { + setup: function( data, namespaces, eventHandle ) { + // We only want to do this special case on windows + if ( jQuery.isWindow( this ) ) { + this.onbeforeunload = eventHandle; + } + }, + + teardown: function( namespaces, eventHandle ) { + if ( this.onbeforeunload === eventHandle ) { + this.onbeforeunload = null; + } + } + } + }, + + simulate: function( type, elem, event, bubble ) { + // Piggyback on a donor event to simulate a different one. + // Fake originalEvent to avoid donor's stopPropagation, but if the + // simulated event prevents default then we do the same on the donor. + var e = jQuery.extend( + new jQuery.Event(), + event, + { type: type, + isSimulated: true, + originalEvent: {} + } + ); + if ( bubble ) { + jQuery.event.trigger( e, null, elem ); + } else { + jQuery.event.dispatch.call( elem, e ); + } + if ( e.isDefaultPrevented() ) { + event.preventDefault(); + } + } +}; + +// Some plugins are using, but it's undocumented/deprecated and will be removed. +// The 1.7 special event interface should provide all the hooks needed now. +jQuery.event.handle = jQuery.event.dispatch; + +jQuery.removeEvent = document.removeEventListener ? + function( elem, type, handle ) { + if ( elem.removeEventListener ) { + elem.removeEventListener( type, handle, false ); + } + } : + function( elem, type, handle ) { + if ( elem.detachEvent ) { + elem.detachEvent( "on" + type, handle ); + } + }; + +jQuery.Event = function( src, props ) { + // Allow instantiation without the 'new' keyword + if ( !(this instanceof jQuery.Event) ) { + return new jQuery.Event( src, props ); + } + + // Event object + if ( src && src.type ) { + this.originalEvent = src; + this.type = src.type; + + // Events bubbling up the document may have been marked as prevented + // by a handler lower down the tree; reflect the correct value. + this.isDefaultPrevented = ( src.defaultPrevented || src.returnValue === false || + src.getPreventDefault && src.getPreventDefault() ) ? returnTrue : returnFalse; + + // Event type + } else { + this.type = src; + } + + // Put explicitly provided properties onto the event object + if ( props ) { + jQuery.extend( this, props ); + } + + // Create a timestamp if incoming event doesn't have one + this.timeStamp = src && src.timeStamp || jQuery.now(); + + // Mark it as fixed + this[ jQuery.expando ] = true; +}; + +function returnFalse() { + return false; +} +function returnTrue() { + return true; +} + +// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding +// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html +jQuery.Event.prototype = { + preventDefault: function() { + this.isDefaultPrevented = returnTrue; + + var e = this.originalEvent; + if ( !e ) { + return; + } + + // if preventDefault exists run it on the original event + if ( e.preventDefault ) { + e.preventDefault(); + + // otherwise set the returnValue property of the original event to false (IE) + } else { + e.returnValue = false; + } + }, + stopPropagation: function() { + this.isPropagationStopped = returnTrue; + + var e = this.originalEvent; + if ( !e ) { + return; + } + // if stopPropagation exists run it on the original event + if ( e.stopPropagation ) { + e.stopPropagation(); + } + // otherwise set the cancelBubble property of the original event to true (IE) + e.cancelBubble = true; + }, + stopImmediatePropagation: function() { + this.isImmediatePropagationStopped = returnTrue; + this.stopPropagation(); + }, + isDefaultPrevented: returnFalse, + isPropagationStopped: returnFalse, + isImmediatePropagationStopped: returnFalse +}; + +// Create mouseenter/leave events using mouseover/out and event-time checks +jQuery.each({ + mouseenter: "mouseover", + mouseleave: "mouseout" +}, function( orig, fix ) { + jQuery.event.special[ orig ] = { + delegateType: fix, + bindType: fix, + + handle: function( event ) { + var target = this, + related = event.relatedTarget, + handleObj = event.handleObj, + selector = handleObj.selector, + ret; + + // For mousenter/leave call the handler if related is outside the target. + // NB: No relatedTarget if the mouse left/entered the browser window + if ( !related || (related !== target && !jQuery.contains( target, related )) ) { + event.type = handleObj.origType; + ret = handleObj.handler.apply( this, arguments ); + event.type = fix; + } + return ret; + } + }; +}); + +// IE submit delegation +if ( !jQuery.support.submitBubbles ) { + + jQuery.event.special.submit = { + setup: function() { + // Only need this for delegated form submit events + if ( jQuery.nodeName( this, "form" ) ) { + return false; + } + + // Lazy-add a submit handler when a descendant form may potentially be submitted + jQuery.event.add( this, "click._submit keypress._submit", function( e ) { + // Node name check avoids a VML-related crash in IE (#9807) + var elem = e.target, + form = jQuery.nodeName( elem, "input" ) || jQuery.nodeName( elem, "button" ) ? elem.form : undefined; + if ( form && !form._submit_attached ) { + jQuery.event.add( form, "submit._submit", function( event ) { + event._submit_bubble = true; + }); + form._submit_attached = true; + } + }); + // return undefined since we don't need an event listener + }, + + postDispatch: function( event ) { + // If form was submitted by the user, bubble the event up the tree + if ( event._submit_bubble ) { + delete event._submit_bubble; + if ( this.parentNode && !event.isTrigger ) { + jQuery.event.simulate( "submit", this.parentNode, event, true ); + } + } + }, + + teardown: function() { + // Only need this for delegated form submit events + if ( jQuery.nodeName( this, "form" ) ) { + return false; + } + + // Remove delegated handlers; cleanData eventually reaps submit handlers attached above + jQuery.event.remove( this, "._submit" ); + } + }; +} + +// IE change delegation and checkbox/radio fix +if ( !jQuery.support.changeBubbles ) { + + jQuery.event.special.change = { + + setup: function() { + + if ( rformElems.test( this.nodeName ) ) { + // IE doesn't fire change on a check/radio until blur; trigger it on click + // after a propertychange. Eat the blur-change in special.change.handle. + // This still fires onchange a second time for check/radio after blur. + if ( this.type === "checkbox" || this.type === "radio" ) { + jQuery.event.add( this, "propertychange._change", function( event ) { + if ( event.originalEvent.propertyName === "checked" ) { + this._just_changed = true; + } + }); + jQuery.event.add( this, "click._change", function( event ) { + if ( this._just_changed && !event.isTrigger ) { + this._just_changed = false; + jQuery.event.simulate( "change", this, event, true ); + } + }); + } + return false; + } + // Delegated event; lazy-add a change handler on descendant inputs + jQuery.event.add( this, "beforeactivate._change", function( e ) { + var elem = e.target; + + if ( rformElems.test( elem.nodeName ) && !elem._change_attached ) { + jQuery.event.add( elem, "change._change", function( event ) { + if ( this.parentNode && !event.isSimulated && !event.isTrigger ) { + jQuery.event.simulate( "change", this.parentNode, event, true ); + } + }); + elem._change_attached = true; + } + }); + }, + + handle: function( event ) { + var elem = event.target; + + // Swallow native change events from checkbox/radio, we already triggered them above + if ( this !== elem || event.isSimulated || event.isTrigger || (elem.type !== "radio" && elem.type !== "checkbox") ) { + return event.handleObj.handler.apply( this, arguments ); + } + }, + + teardown: function() { + jQuery.event.remove( this, "._change" ); + + return rformElems.test( this.nodeName ); + } + }; +} + +// Create "bubbling" focus and blur events +if ( !jQuery.support.focusinBubbles ) { + jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) { + + // Attach a single capturing handler while someone wants focusin/focusout + var attaches = 0, + handler = function( event ) { + jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ), true ); + }; + + jQuery.event.special[ fix ] = { + setup: function() { + if ( attaches++ === 0 ) { + document.addEventListener( orig, handler, true ); + } + }, + teardown: function() { + if ( --attaches === 0 ) { + document.removeEventListener( orig, handler, true ); + } + } + }; + }); +} + +jQuery.fn.extend({ + + on: function( types, selector, data, fn, /*INTERNAL*/ one ) { + var origFn, type; + + // Types can be a map of types/handlers + if ( typeof types === "object" ) { + // ( types-Object, selector, data ) + if ( typeof selector !== "string" ) { // && selector != null + // ( types-Object, data ) + data = data || selector; + selector = undefined; + } + for ( type in types ) { + this.on( type, selector, data, types[ type ], one ); + } + return this; + } + + if ( data == null && fn == null ) { + // ( types, fn ) + fn = selector; + data = selector = undefined; + } else if ( fn == null ) { + if ( typeof selector === "string" ) { + // ( types, selector, fn ) + fn = data; + data = undefined; + } else { + // ( types, data, fn ) + fn = data; + data = selector; + selector = undefined; + } + } + if ( fn === false ) { + fn = returnFalse; + } else if ( !fn ) { + return this; + } + + if ( one === 1 ) { + origFn = fn; + fn = function( event ) { + // Can use an empty set, since event contains the info + jQuery().off( event ); + return origFn.apply( this, arguments ); + }; + // Use same guid so caller can remove using origFn + fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ ); + } + return this.each( function() { + jQuery.event.add( this, types, fn, data, selector ); + }); + }, + one: function( types, selector, data, fn ) { + return this.on( types, selector, data, fn, 1 ); + }, + off: function( types, selector, fn ) { + if ( types && types.preventDefault && types.handleObj ) { + // ( event ) dispatched jQuery.Event + var handleObj = types.handleObj; + jQuery( types.delegateTarget ).off( + handleObj.namespace ? handleObj.origType + "." + handleObj.namespace : handleObj.origType, + handleObj.selector, + handleObj.handler + ); + return this; + } + if ( typeof types === "object" ) { + // ( types-object [, selector] ) + for ( var type in types ) { + this.off( type, selector, types[ type ] ); + } + return this; + } + if ( selector === false || typeof selector === "function" ) { + // ( types [, fn] ) + fn = selector; + selector = undefined; + } + if ( fn === false ) { + fn = returnFalse; + } + return this.each(function() { + jQuery.event.remove( this, types, fn, selector ); + }); + }, + + bind: function( types, data, fn ) { + return this.on( types, null, data, fn ); + }, + unbind: function( types, fn ) { + return this.off( types, null, fn ); + }, + + live: function( types, data, fn ) { + jQuery( this.context ).on( types, this.selector, data, fn ); + return this; + }, + die: function( types, fn ) { + jQuery( this.context ).off( types, this.selector || "**", fn ); + return this; + }, + + delegate: function( selector, types, data, fn ) { + return this.on( types, selector, data, fn ); + }, + undelegate: function( selector, types, fn ) { + // ( namespace ) or ( selector, types [, fn] ) + return arguments.length == 1? this.off( selector, "**" ) : this.off( types, selector, fn ); + }, + + trigger: function( type, data ) { + return this.each(function() { + jQuery.event.trigger( type, data, this ); + }); + }, + triggerHandler: function( type, data ) { + if ( this[0] ) { + return jQuery.event.trigger( type, data, this[0], true ); + } + }, + + toggle: function( fn ) { + // Save reference to arguments for access in closure + var args = arguments, + guid = fn.guid || jQuery.guid++, + i = 0, + toggler = function( event ) { + // Figure out which function to execute + var lastToggle = ( jQuery._data( this, "lastToggle" + fn.guid ) || 0 ) % i; + jQuery._data( this, "lastToggle" + fn.guid, lastToggle + 1 ); + + // Make sure that clicks stop + event.preventDefault(); + + // and execute the function + return args[ lastToggle ].apply( this, arguments ) || false; + }; + + // link all the functions, so any of them can unbind this click handler + toggler.guid = guid; + while ( i < args.length ) { + args[ i++ ].guid = guid; + } + + return this.click( toggler ); + }, + + hover: function( fnOver, fnOut ) { + return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver ); + } +}); + +jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " + + "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " + + "change select submit keydown keypress keyup error contextmenu").split(" "), function( i, name ) { + + // Handle event binding + jQuery.fn[ name ] = function( data, fn ) { + if ( fn == null ) { + fn = data; + data = null; + } + + return arguments.length > 0 ? + this.on( name, null, data, fn ) : + this.trigger( name ); + }; + + if ( jQuery.attrFn ) { + jQuery.attrFn[ name ] = true; + } + + if ( rkeyEvent.test( name ) ) { + jQuery.event.fixHooks[ name ] = jQuery.event.keyHooks; + } + + if ( rmouseEvent.test( name ) ) { + jQuery.event.fixHooks[ name ] = jQuery.event.mouseHooks; + } +}); + + + +/*! + * Sizzle CSS Selector Engine + * Copyright 2011, The Dojo Foundation + * Released under the MIT, BSD, and GPL Licenses. + * More information: http://sizzlejs.com/ + */ +(function(){ + +var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g, + expando = "sizcache" + (Math.random() + '').replace('.', ''), + done = 0, + toString = Object.prototype.toString, + hasDuplicate = false, + baseHasDuplicate = true, + rBackslash = /\\/g, + rReturn = /\r\n/g, + rNonWord = /\W/; + +// Here we check if the JavaScript engine is using some sort of +// optimization where it does not always call our comparision +// function. If that is the case, discard the hasDuplicate value. +// Thus far that includes Google Chrome. +[0, 0].sort(function() { + baseHasDuplicate = false; + return 0; +}); + +var Sizzle = function( selector, context, results, seed ) { + results = results || []; + context = context || document; + + var origContext = context; + + if ( context.nodeType !== 1 && context.nodeType !== 9 ) { + return []; + } + + if ( !selector || typeof selector !== "string" ) { + return results; + } + + var m, set, checkSet, extra, ret, cur, pop, i, + prune = true, + contextXML = Sizzle.isXML( context ), + parts = [], + soFar = selector; + + // Reset the position of the chunker regexp (start from head) + do { + chunker.exec( "" ); + m = chunker.exec( soFar ); + + if ( m ) { + soFar = m[3]; + + parts.push( m[1] ); + + if ( m[2] ) { + extra = m[3]; + break; + } + } + } while ( m ); + + if ( parts.length > 1 && origPOS.exec( selector ) ) { + + if ( parts.length === 2 && Expr.relative[ parts[0] ] ) { + set = posProcess( parts[0] + parts[1], context, seed ); + + } else { + set = Expr.relative[ parts[0] ] ? + [ context ] : + Sizzle( parts.shift(), context ); + + while ( parts.length ) { + selector = parts.shift(); + + if ( Expr.relative[ selector ] ) { + selector += parts.shift(); + } + + set = posProcess( selector, set, seed ); + } + } + + } else { + // Take a shortcut and set the context if the root selector is an ID + // (but not if it'll be faster if the inner selector is an ID) + if ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML && + Expr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) { + + ret = Sizzle.find( parts.shift(), context, contextXML ); + context = ret.expr ? + Sizzle.filter( ret.expr, ret.set )[0] : + ret.set[0]; + } + + if ( context ) { + ret = seed ? + { expr: parts.pop(), set: makeArray(seed) } : + Sizzle.find( parts.pop(), parts.length === 1 && (parts[0] === "~" || parts[0] === "+") && context.parentNode ? context.parentNode : context, contextXML ); + + set = ret.expr ? + Sizzle.filter( ret.expr, ret.set ) : + ret.set; + + if ( parts.length > 0 ) { + checkSet = makeArray( set ); + + } else { + prune = false; + } + + while ( parts.length ) { + cur = parts.pop(); + pop = cur; + + if ( !Expr.relative[ cur ] ) { + cur = ""; + } else { + pop = parts.pop(); + } + + if ( pop == null ) { + pop = context; + } + + Expr.relative[ cur ]( checkSet, pop, contextXML ); + } + + } else { + checkSet = parts = []; + } + } + + if ( !checkSet ) { + checkSet = set; + } + + if ( !checkSet ) { + Sizzle.error( cur || selector ); + } + + if ( toString.call(checkSet) === "[object Array]" ) { + if ( !prune ) { + results.push.apply( results, checkSet ); + + } else if ( context && context.nodeType === 1 ) { + for ( i = 0; checkSet[i] != null; i++ ) { + if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && Sizzle.contains(context, checkSet[i])) ) { + results.push( set[i] ); + } + } + + } else { + for ( i = 0; checkSet[i] != null; i++ ) { + if ( checkSet[i] && checkSet[i].nodeType === 1 ) { + results.push( set[i] ); + } + } + } + + } else { + makeArray( checkSet, results ); + } + + if ( extra ) { + Sizzle( extra, origContext, results, seed ); + Sizzle.uniqueSort( results ); + } + + return results; +}; + +Sizzle.uniqueSort = function( results ) { + if ( sortOrder ) { + hasDuplicate = baseHasDuplicate; + results.sort( sortOrder ); + + if ( hasDuplicate ) { + for ( var i = 1; i < results.length; i++ ) { + if ( results[i] === results[ i - 1 ] ) { + results.splice( i--, 1 ); + } + } + } + } + + return results; +}; + +Sizzle.matches = function( expr, set ) { + return Sizzle( expr, null, null, set ); +}; + +Sizzle.matchesSelector = function( node, expr ) { + return Sizzle( expr, null, null, [node] ).length > 0; +}; + +Sizzle.find = function( expr, context, isXML ) { + var set, i, len, match, type, left; + + if ( !expr ) { + return []; + } + + for ( i = 0, len = Expr.order.length; i < len; i++ ) { + type = Expr.order[i]; + + if ( (match = Expr.leftMatch[ type ].exec( expr )) ) { + left = match[1]; + match.splice( 1, 1 ); + + if ( left.substr( left.length - 1 ) !== "\\" ) { + match[1] = (match[1] || "").replace( rBackslash, "" ); + set = Expr.find[ type ]( match, context, isXML ); + + if ( set != null ) { + expr = expr.replace( Expr.match[ type ], "" ); + break; + } + } + } + } + + if ( !set ) { + set = typeof context.getElementsByTagName !== "undefined" ? + context.getElementsByTagName( "*" ) : + []; + } + + return { set: set, expr: expr }; +}; + +Sizzle.filter = function( expr, set, inplace, not ) { + var match, anyFound, + type, found, item, filter, left, + i, pass, + old = expr, + result = [], + curLoop = set, + isXMLFilter = set && set[0] && Sizzle.isXML( set[0] ); + + while ( expr && set.length ) { + for ( type in Expr.filter ) { + if ( (match = Expr.leftMatch[ type ].exec( expr )) != null && match[2] ) { + filter = Expr.filter[ type ]; + left = match[1]; + + anyFound = false; + + match.splice(1,1); + + if ( left.substr( left.length - 1 ) === "\\" ) { + continue; + } + + if ( curLoop === result ) { + result = []; + } + + if ( Expr.preFilter[ type ] ) { + match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter ); + + if ( !match ) { + anyFound = found = true; + + } else if ( match === true ) { + continue; + } + } + + if ( match ) { + for ( i = 0; (item = curLoop[i]) != null; i++ ) { + if ( item ) { + found = filter( item, match, i, curLoop ); + pass = not ^ found; + + if ( inplace && found != null ) { + if ( pass ) { + anyFound = true; + + } else { + curLoop[i] = false; + } + + } else if ( pass ) { + result.push( item ); + anyFound = true; + } + } + } + } + + if ( found !== undefined ) { + if ( !inplace ) { + curLoop = result; + } + + expr = expr.replace( Expr.match[ type ], "" ); + + if ( !anyFound ) { + return []; + } + + break; + } + } + } + + // Improper expression + if ( expr === old ) { + if ( anyFound == null ) { + Sizzle.error( expr ); + + } else { + break; + } + } + + old = expr; + } + + return curLoop; +}; + +Sizzle.error = function( msg ) { + throw new Error( "Syntax error, unrecognized expression: " + msg ); +}; + +/** + * Utility function for retreiving the text value of an array of DOM nodes + * @param {Array|Element} elem + */ +var getText = Sizzle.getText = function( elem ) { + var i, node, + nodeType = elem.nodeType, + ret = ""; + + if ( nodeType ) { + if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) { + // Use textContent || innerText for elements + if ( typeof elem.textContent === 'string' ) { + return elem.textContent; + } else if ( typeof elem.innerText === 'string' ) { + // Replace IE's carriage returns + return elem.innerText.replace( rReturn, '' ); + } else { + // Traverse it's children + for ( elem = elem.firstChild; elem; elem = elem.nextSibling) { + ret += getText( elem ); + } + } + } else if ( nodeType === 3 || nodeType === 4 ) { + return elem.nodeValue; + } + } else { + + // If no nodeType, this is expected to be an array + for ( i = 0; (node = elem[i]); i++ ) { + // Do not traverse comment nodes + if ( node.nodeType !== 8 ) { + ret += getText( node ); + } + } + } + return ret; +}; + +var Expr = Sizzle.selectors = { + order: [ "ID", "NAME", "TAG" ], + + match: { + ID: /#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/, + CLASS: /\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/, + NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/, + ATTR: /\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(?:(['"])(.*?)\3|(#?(?:[\w\u00c0-\uFFFF\-]|\\.)*)|)|)\s*\]/, + TAG: /^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/, + CHILD: /:(only|nth|last|first)-child(?:\(\s*(even|odd|(?:[+\-]?\d+|(?:[+\-]?\d*)?n\s*(?:[+\-]\s*\d+)?))\s*\))?/, + POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/, + PSEUDO: /:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/ + }, + + leftMatch: {}, + + attrMap: { + "class": "className", + "for": "htmlFor" + }, + + attrHandle: { + href: function( elem ) { + return elem.getAttribute( "href" ); + }, + type: function( elem ) { + return elem.getAttribute( "type" ); + } + }, + + relative: { + "+": function(checkSet, part){ + var isPartStr = typeof part === "string", + isTag = isPartStr && !rNonWord.test( part ), + isPartStrNotTag = isPartStr && !isTag; + + if ( isTag ) { + part = part.toLowerCase(); + } + + for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) { + if ( (elem = checkSet[i]) ) { + while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {} + + checkSet[i] = isPartStrNotTag || elem && elem.nodeName.toLowerCase() === part ? + elem || false : + elem === part; + } + } + + if ( isPartStrNotTag ) { + Sizzle.filter( part, checkSet, true ); + } + }, + + ">": function( checkSet, part ) { + var elem, + isPartStr = typeof part === "string", + i = 0, + l = checkSet.length; + + if ( isPartStr && !rNonWord.test( part ) ) { + part = part.toLowerCase(); + + for ( ; i < l; i++ ) { + elem = checkSet[i]; + + if ( elem ) { + var parent = elem.parentNode; + checkSet[i] = parent.nodeName.toLowerCase() === part ? parent : false; + } + } + + } else { + for ( ; i < l; i++ ) { + elem = checkSet[i]; + + if ( elem ) { + checkSet[i] = isPartStr ? + elem.parentNode : + elem.parentNode === part; + } + } + + if ( isPartStr ) { + Sizzle.filter( part, checkSet, true ); + } + } + }, + + "": function(checkSet, part, isXML){ + var nodeCheck, + doneName = done++, + checkFn = dirCheck; + + if ( typeof part === "string" && !rNonWord.test( part ) ) { + part = part.toLowerCase(); + nodeCheck = part; + checkFn = dirNodeCheck; + } + + checkFn( "parentNode", part, doneName, checkSet, nodeCheck, isXML ); + }, + + "~": function( checkSet, part, isXML ) { + var nodeCheck, + doneName = done++, + checkFn = dirCheck; + + if ( typeof part === "string" && !rNonWord.test( part ) ) { + part = part.toLowerCase(); + nodeCheck = part; + checkFn = dirNodeCheck; + } + + checkFn( "previousSibling", part, doneName, checkSet, nodeCheck, isXML ); + } + }, + + find: { + ID: function( match, context, isXML ) { + if ( typeof context.getElementById !== "undefined" && !isXML ) { + var m = context.getElementById(match[1]); + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + return m && m.parentNode ? [m] : []; + } + }, + + NAME: function( match, context ) { + if ( typeof context.getElementsByName !== "undefined" ) { + var ret = [], + results = context.getElementsByName( match[1] ); + + for ( var i = 0, l = results.length; i < l; i++ ) { + if ( results[i].getAttribute("name") === match[1] ) { + ret.push( results[i] ); + } + } + + return ret.length === 0 ? null : ret; + } + }, + + TAG: function( match, context ) { + if ( typeof context.getElementsByTagName !== "undefined" ) { + return context.getElementsByTagName( match[1] ); + } + } + }, + preFilter: { + CLASS: function( match, curLoop, inplace, result, not, isXML ) { + match = " " + match[1].replace( rBackslash, "" ) + " "; + + if ( isXML ) { + return match; + } + + for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) { + if ( elem ) { + if ( not ^ (elem.className && (" " + elem.className + " ").replace(/[\t\n\r]/g, " ").indexOf(match) >= 0) ) { + if ( !inplace ) { + result.push( elem ); + } + + } else if ( inplace ) { + curLoop[i] = false; + } + } + } + + return false; + }, + + ID: function( match ) { + return match[1].replace( rBackslash, "" ); + }, + + TAG: function( match, curLoop ) { + return match[1].replace( rBackslash, "" ).toLowerCase(); + }, + + CHILD: function( match ) { + if ( match[1] === "nth" ) { + if ( !match[2] ) { + Sizzle.error( match[0] ); + } + + match[2] = match[2].replace(/^\+|\s*/g, ''); + + // parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6' + var test = /(-?)(\d*)(?:n([+\-]?\d*))?/.exec( + match[2] === "even" && "2n" || match[2] === "odd" && "2n+1" || + !/\D/.test( match[2] ) && "0n+" + match[2] || match[2]); + + // calculate the numbers (first)n+(last) including if they are negative + match[2] = (test[1] + (test[2] || 1)) - 0; + match[3] = test[3] - 0; + } + else if ( match[2] ) { + Sizzle.error( match[0] ); + } + + // TODO: Move to normal caching system + match[0] = done++; + + return match; + }, + + ATTR: function( match, curLoop, inplace, result, not, isXML ) { + var name = match[1] = match[1].replace( rBackslash, "" ); + + if ( !isXML && Expr.attrMap[name] ) { + match[1] = Expr.attrMap[name]; + } + + // Handle if an un-quoted value was used + match[4] = ( match[4] || match[5] || "" ).replace( rBackslash, "" ); + + if ( match[2] === "~=" ) { + match[4] = " " + match[4] + " "; + } + + return match; + }, + + PSEUDO: function( match, curLoop, inplace, result, not ) { + if ( match[1] === "not" ) { + // If we're dealing with a complex expression, or a simple one + if ( ( chunker.exec(match[3]) || "" ).length > 1 || /^\w/.test(match[3]) ) { + match[3] = Sizzle(match[3], null, null, curLoop); + + } else { + var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not); + + if ( !inplace ) { + result.push.apply( result, ret ); + } + + return false; + } + + } else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) { + return true; + } + + return match; + }, + + POS: function( match ) { + match.unshift( true ); + + return match; + } + }, + + filters: { + enabled: function( elem ) { + return elem.disabled === false && elem.type !== "hidden"; + }, + + disabled: function( elem ) { + return elem.disabled === true; + }, + + checked: function( elem ) { + return elem.checked === true; + }, + + selected: function( elem ) { + // Accessing this property makes selected-by-default + // options in Safari work properly + if ( elem.parentNode ) { + elem.parentNode.selectedIndex; + } + + return elem.selected === true; + }, + + parent: function( elem ) { + return !!elem.firstChild; + }, + + empty: function( elem ) { + return !elem.firstChild; + }, + + has: function( elem, i, match ) { + return !!Sizzle( match[3], elem ).length; + }, + + header: function( elem ) { + return (/h\d/i).test( elem.nodeName ); + }, + + text: function( elem ) { + var attr = elem.getAttribute( "type" ), type = elem.type; + // IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc) + // use getAttribute instead to test this case + return elem.nodeName.toLowerCase() === "input" && "text" === type && ( attr === type || attr === null ); + }, + + radio: function( elem ) { + return elem.nodeName.toLowerCase() === "input" && "radio" === elem.type; + }, + + checkbox: function( elem ) { + return elem.nodeName.toLowerCase() === "input" && "checkbox" === elem.type; + }, + + file: function( elem ) { + return elem.nodeName.toLowerCase() === "input" && "file" === elem.type; + }, + + password: function( elem ) { + return elem.nodeName.toLowerCase() === "input" && "password" === elem.type; + }, + + submit: function( elem ) { + var name = elem.nodeName.toLowerCase(); + return (name === "input" || name === "button") && "submit" === elem.type; + }, + + image: function( elem ) { + return elem.nodeName.toLowerCase() === "input" && "image" === elem.type; + }, + + reset: function( elem ) { + var name = elem.nodeName.toLowerCase(); + return (name === "input" || name === "button") && "reset" === elem.type; + }, + + button: function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && "button" === elem.type || name === "button"; + }, + + input: function( elem ) { + return (/input|select|textarea|button/i).test( elem.nodeName ); + }, + + focus: function( elem ) { + return elem === elem.ownerDocument.activeElement; + } + }, + setFilters: { + first: function( elem, i ) { + return i === 0; + }, + + last: function( elem, i, match, array ) { + return i === array.length - 1; + }, + + even: function( elem, i ) { + return i % 2 === 0; + }, + + odd: function( elem, i ) { + return i % 2 === 1; + }, + + lt: function( elem, i, match ) { + return i < match[3] - 0; + }, + + gt: function( elem, i, match ) { + return i > match[3] - 0; + }, + + nth: function( elem, i, match ) { + return match[3] - 0 === i; + }, + + eq: function( elem, i, match ) { + return match[3] - 0 === i; + } + }, + filter: { + PSEUDO: function( elem, match, i, array ) { + var name = match[1], + filter = Expr.filters[ name ]; + + if ( filter ) { + return filter( elem, i, match, array ); + + } else if ( name === "contains" ) { + return (elem.textContent || elem.innerText || getText([ elem ]) || "").indexOf(match[3]) >= 0; + + } else if ( name === "not" ) { + var not = match[3]; + + for ( var j = 0, l = not.length; j < l; j++ ) { + if ( not[j] === elem ) { + return false; + } + } + + return true; + + } else { + Sizzle.error( name ); + } + }, + + CHILD: function( elem, match ) { + var first, last, + doneName, parent, cache, + count, diff, + type = match[1], + node = elem; + + switch ( type ) { + case "only": + case "first": + while ( (node = node.previousSibling) ) { + if ( node.nodeType === 1 ) { + return false; + } + } + + if ( type === "first" ) { + return true; + } + + node = elem; + + /* falls through */ + case "last": + while ( (node = node.nextSibling) ) { + if ( node.nodeType === 1 ) { + return false; + } + } + + return true; + + case "nth": + first = match[2]; + last = match[3]; + + if ( first === 1 && last === 0 ) { + return true; + } + + doneName = match[0]; + parent = elem.parentNode; + + if ( parent && (parent[ expando ] !== doneName || !elem.nodeIndex) ) { + count = 0; + + for ( node = parent.firstChild; node; node = node.nextSibling ) { + if ( node.nodeType === 1 ) { + node.nodeIndex = ++count; + } + } + + parent[ expando ] = doneName; + } + + diff = elem.nodeIndex - last; + + if ( first === 0 ) { + return diff === 0; + + } else { + return ( diff % first === 0 && diff / first >= 0 ); + } + } + }, + + ID: function( elem, match ) { + return elem.nodeType === 1 && elem.getAttribute("id") === match; + }, + + TAG: function( elem, match ) { + return (match === "*" && elem.nodeType === 1) || !!elem.nodeName && elem.nodeName.toLowerCase() === match; + }, + + CLASS: function( elem, match ) { + return (" " + (elem.className || elem.getAttribute("class")) + " ") + .indexOf( match ) > -1; + }, + + ATTR: function( elem, match ) { + var name = match[1], + result = Sizzle.attr ? + Sizzle.attr( elem, name ) : + Expr.attrHandle[ name ] ? + Expr.attrHandle[ name ]( elem ) : + elem[ name ] != null ? + elem[ name ] : + elem.getAttribute( name ), + value = result + "", + type = match[2], + check = match[4]; + + return result == null ? + type === "!=" : + !type && Sizzle.attr ? + result != null : + type === "=" ? + value === check : + type === "*=" ? + value.indexOf(check) >= 0 : + type === "~=" ? + (" " + value + " ").indexOf(check) >= 0 : + !check ? + value && result !== false : + type === "!=" ? + value !== check : + type === "^=" ? + value.indexOf(check) === 0 : + type === "$=" ? + value.substr(value.length - check.length) === check : + type === "|=" ? + value === check || value.substr(0, check.length + 1) === check + "-" : + false; + }, + + POS: function( elem, match, i, array ) { + var name = match[2], + filter = Expr.setFilters[ name ]; + + if ( filter ) { + return filter( elem, i, match, array ); + } + } + } +}; + +var origPOS = Expr.match.POS, + fescape = function(all, num){ + return "\\" + (num - 0 + 1); + }; + +for ( var type in Expr.match ) { + Expr.match[ type ] = new RegExp( Expr.match[ type ].source + (/(?![^\[]*\])(?![^\(]*\))/.source) ); + Expr.leftMatch[ type ] = new RegExp( /(^(?:.|\r|\n)*?)/.source + Expr.match[ type ].source.replace(/\\(\d+)/g, fescape) ); +} +// Expose origPOS +// "global" as in regardless of relation to brackets/parens +Expr.match.globalPOS = origPOS; + +var makeArray = function( array, results ) { + array = Array.prototype.slice.call( array, 0 ); + + if ( results ) { + results.push.apply( results, array ); + return results; + } + + return array; +}; + +// Perform a simple check to determine if the browser is capable of +// converting a NodeList to an array using builtin methods. +// Also verifies that the returned array holds DOM nodes +// (which is not the case in the Blackberry browser) +try { + Array.prototype.slice.call( document.documentElement.childNodes, 0 )[0].nodeType; + +// Provide a fallback method if it does not work +} catch( e ) { + makeArray = function( array, results ) { + var i = 0, + ret = results || []; + + if ( toString.call(array) === "[object Array]" ) { + Array.prototype.push.apply( ret, array ); + + } else { + if ( typeof array.length === "number" ) { + for ( var l = array.length; i < l; i++ ) { + ret.push( array[i] ); + } + + } else { + for ( ; array[i]; i++ ) { + ret.push( array[i] ); + } + } + } + + return ret; + }; +} + +var sortOrder, siblingCheck; + +if ( document.documentElement.compareDocumentPosition ) { + sortOrder = function( a, b ) { + if ( a === b ) { + hasDuplicate = true; + return 0; + } + + if ( !a.compareDocumentPosition || !b.compareDocumentPosition ) { + return a.compareDocumentPosition ? -1 : 1; + } + + return a.compareDocumentPosition(b) & 4 ? -1 : 1; + }; + +} else { + sortOrder = function( a, b ) { + // The nodes are identical, we can exit early + if ( a === b ) { + hasDuplicate = true; + return 0; + + // Fallback to using sourceIndex (in IE) if it's available on both nodes + } else if ( a.sourceIndex && b.sourceIndex ) { + return a.sourceIndex - b.sourceIndex; + } + + var al, bl, + ap = [], + bp = [], + aup = a.parentNode, + bup = b.parentNode, + cur = aup; + + // If the nodes are siblings (or identical) we can do a quick check + if ( aup === bup ) { + return siblingCheck( a, b ); + + // If no parents were found then the nodes are disconnected + } else if ( !aup ) { + return -1; + + } else if ( !bup ) { + return 1; + } + + // Otherwise they're somewhere else in the tree so we need + // to build up a full list of the parentNodes for comparison + while ( cur ) { + ap.unshift( cur ); + cur = cur.parentNode; + } + + cur = bup; + + while ( cur ) { + bp.unshift( cur ); + cur = cur.parentNode; + } + + al = ap.length; + bl = bp.length; + + // Start walking down the tree looking for a discrepancy + for ( var i = 0; i < al && i < bl; i++ ) { + if ( ap[i] !== bp[i] ) { + return siblingCheck( ap[i], bp[i] ); + } + } + + // We ended someplace up the tree so do a sibling check + return i === al ? + siblingCheck( a, bp[i], -1 ) : + siblingCheck( ap[i], b, 1 ); + }; + + siblingCheck = function( a, b, ret ) { + if ( a === b ) { + return ret; + } + + var cur = a.nextSibling; + + while ( cur ) { + if ( cur === b ) { + return -1; + } + + cur = cur.nextSibling; + } + + return 1; + }; +} + +// Check to see if the browser returns elements by name when +// querying by getElementById (and provide a workaround) +(function(){ + // We're going to inject a fake input element with a specified name + var form = document.createElement("div"), + id = "script" + (new Date()).getTime(), + root = document.documentElement; + + form.innerHTML = ""; + + // Inject it into the root element, check its status, and remove it quickly + root.insertBefore( form, root.firstChild ); + + // The workaround has to do additional checks after a getElementById + // Which slows things down for other browsers (hence the branching) + if ( document.getElementById( id ) ) { + Expr.find.ID = function( match, context, isXML ) { + if ( typeof context.getElementById !== "undefined" && !isXML ) { + var m = context.getElementById(match[1]); + + return m ? + m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ? + [m] : + undefined : + []; + } + }; + + Expr.filter.ID = function( elem, match ) { + var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id"); + + return elem.nodeType === 1 && node && node.nodeValue === match; + }; + } + + root.removeChild( form ); + + // release memory in IE + root = form = null; +})(); + +(function(){ + // Check to see if the browser returns only elements + // when doing getElementsByTagName("*") + + // Create a fake element + var div = document.createElement("div"); + div.appendChild( document.createComment("") ); + + // Make sure no comments are found + if ( div.getElementsByTagName("*").length > 0 ) { + Expr.find.TAG = function( match, context ) { + var results = context.getElementsByTagName( match[1] ); + + // Filter out possible comments + if ( match[1] === "*" ) { + var tmp = []; + + for ( var i = 0; results[i]; i++ ) { + if ( results[i].nodeType === 1 ) { + tmp.push( results[i] ); + } + } + + results = tmp; + } + + return results; + }; + } + + // Check to see if an attribute returns normalized href attributes + div.innerHTML = ""; + + if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" && + div.firstChild.getAttribute("href") !== "#" ) { + + Expr.attrHandle.href = function( elem ) { + return elem.getAttribute( "href", 2 ); + }; + } + + // release memory in IE + div = null; +})(); + +if ( document.querySelectorAll ) { + (function(){ + var oldSizzle = Sizzle, + div = document.createElement("div"), + id = "__sizzle__"; + + div.innerHTML = "

"; + + // Safari can't handle uppercase or unicode characters when + // in quirks mode. + if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) { + return; + } + + Sizzle = function( query, context, extra, seed ) { + context = context || document; + + // Only use querySelectorAll on non-XML documents + // (ID selectors don't work in non-HTML documents) + if ( !seed && !Sizzle.isXML(context) ) { + // See if we find a selector to speed up + var match = /^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec( query ); + + if ( match && (context.nodeType === 1 || context.nodeType === 9) ) { + // Speed-up: Sizzle("TAG") + if ( match[1] ) { + return makeArray( context.getElementsByTagName( query ), extra ); + + // Speed-up: Sizzle(".CLASS") + } else if ( match[2] && Expr.find.CLASS && context.getElementsByClassName ) { + return makeArray( context.getElementsByClassName( match[2] ), extra ); + } + } + + if ( context.nodeType === 9 ) { + // Speed-up: Sizzle("body") + // The body element only exists once, optimize finding it + if ( query === "body" && context.body ) { + return makeArray( [ context.body ], extra ); + + // Speed-up: Sizzle("#ID") + } else if ( match && match[3] ) { + var elem = context.getElementById( match[3] ); + + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + if ( elem && elem.parentNode ) { + // Handle the case where IE and Opera return items + // by name instead of ID + if ( elem.id === match[3] ) { + return makeArray( [ elem ], extra ); + } + + } else { + return makeArray( [], extra ); + } + } + + try { + return makeArray( context.querySelectorAll(query), extra ); + } catch(qsaError) {} + + // qSA works strangely on Element-rooted queries + // We can work around this by specifying an extra ID on the root + // and working up from there (Thanks to Andrew Dupont for the technique) + // IE 8 doesn't work on object elements + } else if ( context.nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) { + var oldContext = context, + old = context.getAttribute( "id" ), + nid = old || id, + hasParent = context.parentNode, + relativeHierarchySelector = /^\s*[+~]/.test( query ); + + if ( !old ) { + context.setAttribute( "id", nid ); + } else { + nid = nid.replace( /'/g, "\\$&" ); + } + if ( relativeHierarchySelector && hasParent ) { + context = context.parentNode; + } + + try { + if ( !relativeHierarchySelector || hasParent ) { + return makeArray( context.querySelectorAll( "[id='" + nid + "'] " + query ), extra ); + } + + } catch(pseudoError) { + } finally { + if ( !old ) { + oldContext.removeAttribute( "id" ); + } + } + } + } + + return oldSizzle(query, context, extra, seed); + }; + + for ( var prop in oldSizzle ) { + Sizzle[ prop ] = oldSizzle[ prop ]; + } + + // release memory in IE + div = null; + })(); +} + +(function(){ + var html = document.documentElement, + matches = html.matchesSelector || html.mozMatchesSelector || html.webkitMatchesSelector || html.msMatchesSelector; + + if ( matches ) { + // Check to see if it's possible to do matchesSelector + // on a disconnected node (IE 9 fails this) + var disconnectedMatch = !matches.call( document.createElement( "div" ), "div" ), + pseudoWorks = false; + + try { + // This should fail with an exception + // Gecko does not error, returns false instead + matches.call( document.documentElement, "[test!='']:sizzle" ); + + } catch( pseudoError ) { + pseudoWorks = true; + } + + Sizzle.matchesSelector = function( node, expr ) { + // Make sure that attribute selectors are quoted + expr = expr.replace(/\=\s*([^'"\]]*)\s*\]/g, "='$1']"); + + if ( !Sizzle.isXML( node ) ) { + try { + if ( pseudoWorks || !Expr.match.PSEUDO.test( expr ) && !/!=/.test( expr ) ) { + var ret = matches.call( node, expr ); + + // IE 9's matchesSelector returns false on disconnected nodes + if ( ret || !disconnectedMatch || + // As well, disconnected nodes are said to be in a document + // fragment in IE 9, so check for that + node.document && node.document.nodeType !== 11 ) { + return ret; + } + } + } catch(e) {} + } + + return Sizzle(expr, null, null, [node]).length > 0; + }; + } +})(); + +(function(){ + var div = document.createElement("div"); + + div.innerHTML = "
"; + + // Opera can't find a second classname (in 9.6) + // Also, make sure that getElementsByClassName actually exists + if ( !div.getElementsByClassName || div.getElementsByClassName("e").length === 0 ) { + return; + } + + // Safari caches class attributes, doesn't catch changes (in 3.2) + div.lastChild.className = "e"; + + if ( div.getElementsByClassName("e").length === 1 ) { + return; + } + + Expr.order.splice(1, 0, "CLASS"); + Expr.find.CLASS = function( match, context, isXML ) { + if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) { + return context.getElementsByClassName(match[1]); + } + }; + + // release memory in IE + div = null; +})(); + +function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) { + for ( var i = 0, l = checkSet.length; i < l; i++ ) { + var elem = checkSet[i]; + + if ( elem ) { + var match = false; + + elem = elem[dir]; + + while ( elem ) { + if ( elem[ expando ] === doneName ) { + match = checkSet[elem.sizset]; + break; + } + + if ( elem.nodeType === 1 && !isXML ){ + elem[ expando ] = doneName; + elem.sizset = i; + } + + if ( elem.nodeName.toLowerCase() === cur ) { + match = elem; + break; + } + + elem = elem[dir]; + } + + checkSet[i] = match; + } + } +} + +function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) { + for ( var i = 0, l = checkSet.length; i < l; i++ ) { + var elem = checkSet[i]; + + if ( elem ) { + var match = false; + + elem = elem[dir]; + + while ( elem ) { + if ( elem[ expando ] === doneName ) { + match = checkSet[elem.sizset]; + break; + } + + if ( elem.nodeType === 1 ) { + if ( !isXML ) { + elem[ expando ] = doneName; + elem.sizset = i; + } + + if ( typeof cur !== "string" ) { + if ( elem === cur ) { + match = true; + break; + } + + } else if ( Sizzle.filter( cur, [elem] ).length > 0 ) { + match = elem; + break; + } + } + + elem = elem[dir]; + } + + checkSet[i] = match; + } + } +} + +if ( document.documentElement.contains ) { + Sizzle.contains = function( a, b ) { + return a !== b && (a.contains ? a.contains(b) : true); + }; + +} else if ( document.documentElement.compareDocumentPosition ) { + Sizzle.contains = function( a, b ) { + return !!(a.compareDocumentPosition(b) & 16); + }; + +} else { + Sizzle.contains = function() { + return false; + }; +} + +Sizzle.isXML = function( elem ) { + // documentElement is verified for cases where it doesn't yet exist + // (such as loading iframes in IE - #4833) + var documentElement = (elem ? elem.ownerDocument || elem : 0).documentElement; + + return documentElement ? documentElement.nodeName !== "HTML" : false; +}; + +var posProcess = function( selector, context, seed ) { + var match, + tmpSet = [], + later = "", + root = context.nodeType ? [context] : context; + + // Position selectors must be done after the filter + // And so must :not(positional) so we move all PSEUDOs to the end + while ( (match = Expr.match.PSEUDO.exec( selector )) ) { + later += match[0]; + selector = selector.replace( Expr.match.PSEUDO, "" ); + } + + selector = Expr.relative[selector] ? selector + "*" : selector; + + for ( var i = 0, l = root.length; i < l; i++ ) { + Sizzle( selector, root[i], tmpSet, seed ); + } + + return Sizzle.filter( later, tmpSet ); +}; + +// EXPOSE +// Override sizzle attribute retrieval +Sizzle.attr = jQuery.attr; +Sizzle.selectors.attrMap = {}; +jQuery.find = Sizzle; +jQuery.expr = Sizzle.selectors; +jQuery.expr[":"] = jQuery.expr.filters; +jQuery.unique = Sizzle.uniqueSort; +jQuery.text = Sizzle.getText; +jQuery.isXMLDoc = Sizzle.isXML; +jQuery.contains = Sizzle.contains; + + +})(); + + +var runtil = /Until$/, + rparentsprev = /^(?:parents|prevUntil|prevAll)/, + // Note: This RegExp should be improved, or likely pulled from Sizzle + rmultiselector = /,/, + isSimple = /^.[^:#\[\.,]*$/, + slice = Array.prototype.slice, + POS = jQuery.expr.match.globalPOS, + // methods guaranteed to produce a unique set when starting from a unique set + guaranteedUnique = { + children: true, + contents: true, + next: true, + prev: true + }; + +jQuery.fn.extend({ + find: function( selector ) { + var self = this, + i, l; + + if ( typeof selector !== "string" ) { + return jQuery( selector ).filter(function() { + for ( i = 0, l = self.length; i < l; i++ ) { + if ( jQuery.contains( self[ i ], this ) ) { + return true; + } + } + }); + } + + var ret = this.pushStack( "", "find", selector ), + length, n, r; + + for ( i = 0, l = this.length; i < l; i++ ) { + length = ret.length; + jQuery.find( selector, this[i], ret ); + + if ( i > 0 ) { + // Make sure that the results are unique + for ( n = length; n < ret.length; n++ ) { + for ( r = 0; r < length; r++ ) { + if ( ret[r] === ret[n] ) { + ret.splice(n--, 1); + break; + } + } + } + } + } + + return ret; + }, + + has: function( target ) { + var targets = jQuery( target ); + return this.filter(function() { + for ( var i = 0, l = targets.length; i < l; i++ ) { + if ( jQuery.contains( this, targets[i] ) ) { + return true; + } + } + }); + }, + + not: function( selector ) { + return this.pushStack( winnow(this, selector, false), "not", selector); + }, + + filter: function( selector ) { + return this.pushStack( winnow(this, selector, true), "filter", selector ); + }, + + is: function( selector ) { + return !!selector && ( + typeof selector === "string" ? + // If this is a positional selector, check membership in the returned set + // so $("p:first").is("p:last") won't return true for a doc with two "p". + POS.test( selector ) ? + jQuery( selector, this.context ).index( this[0] ) >= 0 : + jQuery.filter( selector, this ).length > 0 : + this.filter( selector ).length > 0 ); + }, + + closest: function( selectors, context ) { + var ret = [], i, l, cur = this[0]; + + // Array (deprecated as of jQuery 1.7) + if ( jQuery.isArray( selectors ) ) { + var level = 1; + + while ( cur && cur.ownerDocument && cur !== context ) { + for ( i = 0; i < selectors.length; i++ ) { + + if ( jQuery( cur ).is( selectors[ i ] ) ) { + ret.push({ selector: selectors[ i ], elem: cur, level: level }); + } + } + + cur = cur.parentNode; + level++; + } + + return ret; + } + + // String + var pos = POS.test( selectors ) || typeof selectors !== "string" ? + jQuery( selectors, context || this.context ) : + 0; + + for ( i = 0, l = this.length; i < l; i++ ) { + cur = this[i]; + + while ( cur ) { + if ( pos ? pos.index(cur) > -1 : jQuery.find.matchesSelector(cur, selectors) ) { + ret.push( cur ); + break; + + } else { + cur = cur.parentNode; + if ( !cur || !cur.ownerDocument || cur === context || cur.nodeType === 11 ) { + break; + } + } + } + } + + ret = ret.length > 1 ? jQuery.unique( ret ) : ret; + + return this.pushStack( ret, "closest", selectors ); + }, + + // Determine the position of an element within + // the matched set of elements + index: function( elem ) { + + // No argument, return index in parent + if ( !elem ) { + return ( this[0] && this[0].parentNode ) ? this.prevAll().length : -1; + } + + // index in selector + if ( typeof elem === "string" ) { + return jQuery.inArray( this[0], jQuery( elem ) ); + } + + // Locate the position of the desired element + return jQuery.inArray( + // If it receives a jQuery object, the first element is used + elem.jquery ? elem[0] : elem, this ); + }, + + add: function( selector, context ) { + var set = typeof selector === "string" ? + jQuery( selector, context ) : + jQuery.makeArray( selector && selector.nodeType ? [ selector ] : selector ), + all = jQuery.merge( this.get(), set ); + + return this.pushStack( isDisconnected( set[0] ) || isDisconnected( all[0] ) ? + all : + jQuery.unique( all ) ); + }, + + andSelf: function() { + return this.add( this.prevObject ); + } +}); + +// A painfully simple check to see if an element is disconnected +// from a document (should be improved, where feasible). +function isDisconnected( node ) { + return !node || !node.parentNode || node.parentNode.nodeType === 11; +} + +jQuery.each({ + parent: function( elem ) { + var parent = elem.parentNode; + return parent && parent.nodeType !== 11 ? parent : null; + }, + parents: function( elem ) { + return jQuery.dir( elem, "parentNode" ); + }, + parentsUntil: function( elem, i, until ) { + return jQuery.dir( elem, "parentNode", until ); + }, + next: function( elem ) { + return jQuery.nth( elem, 2, "nextSibling" ); + }, + prev: function( elem ) { + return jQuery.nth( elem, 2, "previousSibling" ); + }, + nextAll: function( elem ) { + return jQuery.dir( elem, "nextSibling" ); + }, + prevAll: function( elem ) { + return jQuery.dir( elem, "previousSibling" ); + }, + nextUntil: function( elem, i, until ) { + return jQuery.dir( elem, "nextSibling", until ); + }, + prevUntil: function( elem, i, until ) { + return jQuery.dir( elem, "previousSibling", until ); + }, + siblings: function( elem ) { + return jQuery.sibling( ( elem.parentNode || {} ).firstChild, elem ); + }, + children: function( elem ) { + return jQuery.sibling( elem.firstChild ); + }, + contents: function( elem ) { + return jQuery.nodeName( elem, "iframe" ) ? + elem.contentDocument || elem.contentWindow.document : + jQuery.makeArray( elem.childNodes ); + } +}, function( name, fn ) { + jQuery.fn[ name ] = function( until, selector ) { + var ret = jQuery.map( this, fn, until ); + + if ( !runtil.test( name ) ) { + selector = until; + } + + if ( selector && typeof selector === "string" ) { + ret = jQuery.filter( selector, ret ); + } + + ret = this.length > 1 && !guaranteedUnique[ name ] ? jQuery.unique( ret ) : ret; + + if ( (this.length > 1 || rmultiselector.test( selector )) && rparentsprev.test( name ) ) { + ret = ret.reverse(); + } + + return this.pushStack( ret, name, slice.call( arguments ).join(",") ); + }; +}); + +jQuery.extend({ + filter: function( expr, elems, not ) { + if ( not ) { + expr = ":not(" + expr + ")"; + } + + return elems.length === 1 ? + jQuery.find.matchesSelector(elems[0], expr) ? [ elems[0] ] : [] : + jQuery.find.matches(expr, elems); + }, + + dir: function( elem, dir, until ) { + var matched = [], + cur = elem[ dir ]; + + while ( cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !jQuery( cur ).is( until )) ) { + if ( cur.nodeType === 1 ) { + matched.push( cur ); + } + cur = cur[dir]; + } + return matched; + }, + + nth: function( cur, result, dir, elem ) { + result = result || 1; + var num = 0; + + for ( ; cur; cur = cur[dir] ) { + if ( cur.nodeType === 1 && ++num === result ) { + break; + } + } + + return cur; + }, + + sibling: function( n, elem ) { + var r = []; + + for ( ; n; n = n.nextSibling ) { + if ( n.nodeType === 1 && n !== elem ) { + r.push( n ); + } + } + + return r; + } +}); + +// Implement the identical functionality for filter and not +function winnow( elements, qualifier, keep ) { + + // Can't pass null or undefined to indexOf in Firefox 4 + // Set to 0 to skip string check + qualifier = qualifier || 0; + + if ( jQuery.isFunction( qualifier ) ) { + return jQuery.grep(elements, function( elem, i ) { + var retVal = !!qualifier.call( elem, i, elem ); + return retVal === keep; + }); + + } else if ( qualifier.nodeType ) { + return jQuery.grep(elements, function( elem, i ) { + return ( elem === qualifier ) === keep; + }); + + } else if ( typeof qualifier === "string" ) { + var filtered = jQuery.grep(elements, function( elem ) { + return elem.nodeType === 1; + }); + + if ( isSimple.test( qualifier ) ) { + return jQuery.filter(qualifier, filtered, !keep); + } else { + qualifier = jQuery.filter( qualifier, filtered ); + } + } + + return jQuery.grep(elements, function( elem, i ) { + return ( jQuery.inArray( elem, qualifier ) >= 0 ) === keep; + }); +} + + + + +function createSafeFragment( document ) { + var list = nodeNames.split( "|" ), + safeFrag = document.createDocumentFragment(); + + if ( safeFrag.createElement ) { + while ( list.length ) { + safeFrag.createElement( + list.pop() + ); + } + } + return safeFrag; +} + +var nodeNames = "abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|" + + "header|hgroup|mark|meter|nav|output|progress|section|summary|time|video", + rinlinejQuery = / jQuery\d+="(?:\d+|null)"/g, + rleadingWhitespace = /^\s+/, + rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig, + rtagName = /<([\w:]+)/, + rtbody = /]", "i"), + // checked="checked" or checked + rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i, + rscriptType = /\/(java|ecma)script/i, + rcleanScript = /^\s*", "" ], + legend: [ 1, "
", "
" ], + thead: [ 1, "", "
" ], + tr: [ 2, "", "
" ], + td: [ 3, "", "
" ], + col: [ 2, "", "
" ], + area: [ 1, "", "" ], + _default: [ 0, "", "" ] + }, + safeFragment = createSafeFragment( document ); + +wrapMap.optgroup = wrapMap.option; +wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; +wrapMap.th = wrapMap.td; + +// IE can't serialize and + + + + + + + + + + +
+
+
+
+ +
+

psycopg2.tztzinfo implementations for Psycopg 2

+

This module holds two different tzinfo implementations that can be used as the +tzinfo argument to datetime constructors, directly passed to +Psycopg functions or used to set the cursor.tzinfo_factory attribute in +cursors.

+
+
+class psycopg2.tz.FixedOffsetTimezone(offset=None, name=None)
+

Fixed offset in minutes east from UTC.

+

This is exactly the implementation found in Python 2.3.x documentation, +with a small change to the __init__() method to allow for pickling +and a default name in the form sHH:MM (s is the sign.).

+

The implementation also caches instances. During creation, if a +FixedOffsetTimezone instance has previously been created with the same +offset and name that instance will be returned. This saves memory and +improves comparability.

+
+ +
+
+class psycopg2.tz.LocalTimezone
+

Platform idea of local timezone.

+

This is the exact implementation from the Python 2.3 documentation.

+
+ +
+ + +
+
+
+
+
+

Previous topic

+

psycopg2.extensions – Extensions to the DB API

+

Next topic

+

psycopg2.pool – Connections pooling

+

This Page

+ + + +
+
+
+
+ + + + \ No newline at end of file diff -Nru psycopg2-2.0.13/doc/html/usage.html psycopg2-2.4.5/doc/html/usage.html --- psycopg2-2.0.13/doc/html/usage.html 1970-01-01 00:00:00.000000000 +0000 +++ psycopg2-2.4.5/doc/html/usage.html 2012-03-28 21:17:18.000000000 +0000 @@ -0,0 +1,710 @@ + + + + + + + + + + Basic module usage — Psycopg 2.4.5 documentation + + + + + + + + + + + + + + + +
+
+
+
+ +
+

Basic module usage

+

The basic Psycopg usage is common to all the database adapters implementing +the DB API 2.0 protocol. Here is an interactive session showing some of the +basic commands:

+
>>> import psycopg2
+
+# Connect to an existing database
+>>> conn = psycopg2.connect("dbname=test user=postgres")
+
+# Open a cursor to perform database operations
+>>> cur = conn.cursor()
+
+# Execute a command: this creates a new table
+>>> cur.execute("CREATE TABLE test (id serial PRIMARY KEY, num integer, data varchar);")
+
+# Pass data to fill a query placeholders and let Psycopg perform
+# the correct conversion (no more SQL injections!)
+>>> cur.execute("INSERT INTO test (num, data) VALUES (%s, %s)",
+...      (100, "abc'def"))
+
+# Query the database and obtain data as Python objects
+>>> cur.execute("SELECT * FROM test;")
+>>> cur.fetchone()
+(1, 100, "abc'def")
+
+# Make the changes to the database persistent
+>>> conn.commit()
+
+# Close communication with the database
+>>> cur.close()
+>>> conn.close()
+
+
+

The main entry points of Psycopg are:

+ +
+

Passing parameters to SQL queries

+

Psycopg casts Python variables to SQL literals by type. Many standard Python types +are already adapted to the correct SQL representation.

+

Example: the Python function call:

+
>>> cur.execute(
+...     """INSERT INTO some_table (an_int, a_date, a_string)
+...         VALUES (%s, %s, %s);""",
+...     (10, datetime.date(2005, 11, 18), "O'Reilly"))
+
+
+

is converted into the SQL command:

+
INSERT INTO some_table (an_int, a_date, a_string)
+ VALUES (10, '2005-11-18', 'O''Reilly');
+
+

Named arguments are supported too using %(name)s placeholders. +Using named arguments the values can be passed to the query in any order and +many placeholders can use the same values:

+
>>> cur.execute(
+...     """INSERT INTO some_table (an_int, a_date, another_date, a_string)
+...         VALUES (%(int)s, %(date)s, %(date)s, %(str)s);""",
+...     {'int': 10, 'str': "O'Reilly", 'date': datetime.date(2005, 11, 18)})
+
+
+

While the mechanism resembles regular Python strings manipulation, there are a +few subtle differences you should care about when passing parameters to a +query:

+
    +
  • The Python string operator % is not used: the execute() +method accepts a tuple or dictionary of values as second parameter. +Never use % or + to merge values +into queries.

    +
  • +
  • The variables placeholder must always be a %s, even if a different +placeholder (such as a %d for integers or %f for floats) may look +more appropriate:

    +
    >>> cur.execute("INSERT INTO numbers VALUES (%d)", (42,)) # WRONG
    +>>> cur.execute("INSERT INTO numbers VALUES (%s)", (42,)) # correct
    +
    +
    +
  • +
  • For positional variables binding, the second argument must always be a +sequence, even if it contains a single variable. And remember that Python +requires a comma to create a single element tuple:

    +
    >>> cur.execute("INSERT INTO foo VALUES (%s)", "bar")    # WRONG
    +>>> cur.execute("INSERT INTO foo VALUES (%s)", ("bar"))  # WRONG
    +>>> cur.execute("INSERT INTO foo VALUES (%s)", ("bar",)) # correct
    +>>> cur.execute("INSERT INTO foo VALUES (%s)", ["bar"])  # correct
    +
    +
    +
  • +
  • Only variable values should be bound via this method: it shouldn’t be used +to set table or field names. For these elements, ordinary string formatting +should be used before running execute().

    +
  • +
+
+

The problem with the query parameters

+

The SQL representation for many data types is often not the same of the Python +string representation. The classic example is with single quotes in +strings: SQL uses them as string constants bounds and requires them to be +escaped, whereas in Python single quotes can be left unescaped in strings +bounded by double quotes. For this reason a naïve approach to the composition +of query strings, e.g. using string concatenation, is a recipe for terrible +problems:

+
>>> SQL = "INSERT INTO authors (name) VALUES ('%s');" # NEVER DO THIS
+>>> data = ("O'Reilly", )
+>>> cur.execute(SQL % data) # THIS WILL FAIL MISERABLY
+ProgrammingError: syntax error at or near "Reilly"
+LINE 1: INSERT INTO authors (name) VALUES ('O'Reilly')
+                                              ^
+
+
+

If the variable containing the data to be sent to the database comes from an +untrusted source (e.g. a form published on a web site) an attacker could +easily craft a malformed string, either gaining access to unauthorized data or +performing destructive operations on the database. This form of attack is +called SQL injection and is known to be one of the most widespread forms of +attack to servers. Before continuing, please print this page as a memo and +hang it onto your desk.

+

Psycopg can automatically convert Python objects to and from SQL +literals: using this feature your code will be more robust and +reliable. We must stress this point:

+
+

Warning

+

Never, never, NEVER use Python string concatenation (+) or +string parameters interpolation (%) to pass variables to a SQL query +string. Not even at gunpoint.

+
+

The correct way to pass variables in a SQL command is using the second +argument of the execute() method:

+
>>> SQL = "INSERT INTO authors (name) VALUES (%s);" # Note: no quotes
+>>> data = ("O'Reilly", )
+>>> cur.execute(SQL, data) # Note: no % operator
+
+
+
+
+
+

Adaptation of Python values to SQL types

+

Many standards Python types are adapted into SQL and returned as Python +objects when a query is executed.

+

If you need to convert other Python types to and from PostgreSQL data types, +see Adapting new Python types to SQL syntax and Type casting of SQL types into Python objects. You +can also find a few other specialized adapters in the psycopg2.extras +module.

+

In the following examples the method mogrify() is used to show +the SQL string that would be sent to the database.

+
    +
  • Python None and boolean values True and False are converted into the +proper SQL literals:

    +
    >>> cur.mogrify("SELECT %s, %s, %s;", (None, True, False))
    +>>> 'SELECT NULL, true, false;'
    +
    +
    +
  • +
+
    +
  • Numeric objects: int, long, float, Decimal are converted in +the PostgreSQL numerical representation:

    +
    >>> cur.mogrify("SELECT %s, %s, %s, %s;", (10, 10L, 10.0, Decimal("10.00")))
    +>>> 'SELECT 10, 10, 10.0, 10.00;'
    +
    +
    +
  • +
+
    +
  • String types: str, unicode are converted in SQL string syntax. +unicode objects (str in Python 3) are encoded in the connection +encoding to be sent to the backend: trying to send a character +not supported by the encoding will result in an error. Received data can be +converted either as str or unicode: see Unicode handling.
  • +
+
    +
  • Binary types: Python types representing binary objects are converted into +PostgreSQL binary string syntax, suitable for bytea fields. Such +types are buffer (only available in Python 2), memoryview (available +from Python 2.7), bytearray (available from Python 2.6) and bytes +(only from Python 3: the name is available from Python 2.6 but it’s only an +alias for the type str). Any object implementing the Revised Buffer +Protocol should be usable as binary type where the protocol is supported +(i.e. from Python 2.6). Received data is returned as buffer (in Python 2) +or memoryview (in Python 3).

    +

    +Changed in version 2.4: only strings were supported before.

    +

    +Changed in version 2.4.1: can parse the ‘hex’ format from 9.0 servers without relying on the +version of the client library.

    +
    +

    Note

    +

    In Python 2, if you have binary data in a str object, you can pass them +to a bytea field using the psycopg2.Binary wrapper:

    +
    mypic = open('picture.png', 'rb').read()
    +curs.execute("insert into blobs (file) values (%s)",
    +    (psycopg2.Binary(mypic),))
    +
    +
    +
    +
    +

    Warning

    +

    Since version 9.0 PostgreSQL uses by default a new “hex” format to +emit bytea fields. Starting from Psycopg 2.4.1 the format is +correctly supported. If you use a previous version you will need some +extra care when receiving bytea from PostgreSQL: you must have at least +libpq 9.0 installed on the client or alternatively you can set the +bytea_output configuration parameter to escape, either in the +server configuration file or in the client session (using a query such as +SET bytea_output TO escape;) before receiving binary data.

    +
    +
  • +
+
    +
  • Date and time objects: builtin datetime, date, +time, timedelta are converted into PostgreSQL’s +timestamp, date, time, interval data types. +Time zones are supported too. The Egenix mx.DateTime objects are adapted +the same way:

    +
    >>> dt = datetime.datetime.now()
    +>>> dt
    +datetime.datetime(2010, 2, 8, 1, 40, 27, 425337)
    +
    +>>> cur.mogrify("SELECT %s, %s, %s;", (dt, dt.date(), dt.time()))
    +"SELECT '2010-02-08T01:40:27.425337', '2010-02-08', '01:40:27.425337';"
    +
    +>>> cur.mogrify("SELECT %s;", (dt - datetime.datetime(2010,1,1),))
    +"SELECT '38 days 6027.425337 seconds';"
    +
    +
    +
  • +
+
    +
  • Python lists are converted into PostgreSQL ARRAYs:

    +
    >>> cur.mogrify("SELECT %s;", ([10, 20, 30], ))
    +'SELECT ARRAY[10, 20, 30];'
    +
    +
    +
    +

    Note

    +

    Reading back from PostgreSQL, arrays are converted to list of Python +objects as expected, but only if the types are known one. Arrays of +unknown types are returned as represented by the database (e.g. +{a,b,c}). You can easily create a typecaster for array of +unknown types.

    +
    +
  • +
+
    +
  • Python tuples are converted in a syntax suitable for the SQL IN +operator and to represent a composite type:

    +
    >>> cur.mogrify("SELECT %s IN %s;", (10, (10, 20, 30)))
    +'SELECT 10 IN (10, 20, 30);'
    +
    +
    +
    +

    Note

    +

    SQL doesn’t allow an empty list in the IN operator, so your code should +guard against empty tuples.

    +
    +

    If you want PostgreSQL composite types to be converted into a Python +tuple/namedtuple you can use the register_composite() +function.

    +

    +New in version 2.0.6: the tuple IN adaptation.

    +

    +Changed in version 2.0.14: the tuple IN adapter is always active. In previous releases it +was necessary to import the extensions module to have it +registered.

    +

    +Changed in version 2.3: namedtuple instances are adapted like regular tuples and +can thus be used to represent composite types.

    +
  • +
+
    +
  • Python dictionaries are converted into the hstore data type. By default +the adapter is not enabled: see register_hstore() for +further details.

    +

    +New in version 2.3: the hstore adaptation.

    +
  • +
+
+

Unicode handling

+

Psycopg can exchange Unicode data with a PostgreSQL database. Python +unicode objects are automatically encoded in the client encoding +defined on the database connection (the PostgreSQL encoding, available in +connection.encoding, is translated into a Python codec using the +encodings mapping):

+
>>> print u, type(u)
+àèìòù€ <type 'unicode'>
+
+>>> cur.execute("INSERT INTO test (num, data) VALUES (%s,%s);", (74, u))
+
+
+

When reading data from the database, in Python 2 the strings returned are +usually 8 bit str objects encoded in the database client encoding:

+
>>> print conn.encoding
+UTF8
+
+>>> cur.execute("SELECT data FROM test WHERE num = 74")
+>>> x = cur.fetchone()[0]
+>>> print x, type(x), repr(x)
+àèìòù€ <type 'str'> '\xc3\xa0\xc3\xa8\xc3\xac\xc3\xb2\xc3\xb9\xe2\x82\xac'
+
+>>> conn.set_client_encoding('LATIN9')
+
+>>> cur.execute("SELECT data FROM test WHERE num = 74")
+>>> x = cur.fetchone()[0]
+>>> print type(x), repr(x)
+<type 'str'> '\xe0\xe8\xec\xf2\xf9\xa4'
+
+
+

In Python 3 instead the strings are automatically decoded in the connection +encoding, as the str object can represent Unicode characters. +In Python 2 you must register a typecaster in order to receive unicode objects:

+
>>> psycopg2.extensions.register_type(psycopg2.extensions.UNICODE, cur)
+
+>>> cur.execute("SELECT data FROM test WHERE num = 74")
+>>> x = cur.fetchone()[0]
+>>> print x, type(x), repr(x)
+àèìòù€ <type 'unicode'> u'\xe0\xe8\xec\xf2\xf9\u20ac'
+
+
+

In the above example, the UNICODE typecaster is +registered only on the cursor. It is also possible to register typecasters on +the connection or globally: see the function +register_type() and +Type casting of SQL types into Python objects for details.

+
+

Note

+

In Python 2, if you want to uniformly receive all your database input in +Unicode, you can register the related typecasters globally as soon as +Psycopg is imported:

+
import psycopg2
+import psycopg2.extensions
+psycopg2.extensions.register_type(psycopg2.extensions.UNICODE)
+psycopg2.extensions.register_type(psycopg2.extensions.UNICODEARRAY)
+
+
+

and then forget about this story.

+
+
+
+

Time zones handling

+

The PostgreSQL type timestamp with time zone is converted into Python +datetime objects with a tzinfo attribute set +to a FixedOffsetTimezone instance.

+
>>> cur.execute("SET TIME ZONE 'Europe/Rome';")  # UTC + 1 hour
+>>> cur.execute("SELECT '2010-01-01 10:30:45'::timestamptz;")
+>>> cur.fetchone()[0].tzinfo
+psycopg2.tz.FixedOffsetTimezone(offset=60, name=None)
+
+
+

Note that only time zones with an integer number of minutes are supported: +this is a limitation of the Python datetime module. A few historical time +zones had seconds in the UTC offset: these time zones will have the offset +rounded to the nearest minute, with an error of up to 30 seconds.

+
>>> cur.execute("SET TIME ZONE 'Asia/Calcutta';")  # offset was +5:53:20
+>>> cur.execute("SELECT '1930-01-01 10:30:45'::timestamptz;")
+>>> cur.fetchone()[0].tzinfo
+psycopg2.tz.FixedOffsetTimezone(offset=353, name=None)
+
+
+

+Changed in version 2.2.2: timezones with seconds are supported (with rounding). Previously such +timezones raised an error. In order to deal with them in previous +versions use psycopg2.extras.register_tstz_w_secs().

+
+
+
+

Transactions control

+

In Psycopg transactions are handled by the connection class. By +default, the first time a command is sent to the database (using one of the +cursors created by the connection), a new transaction is created. +The following database commands will be executed in the context of the same +transaction – not only the commands issued by the first cursor, but the ones +issued by all the cursors created by the same connection. Should any command +fail, the transaction will be aborted and no further command will be executed +until a call to the rollback() method.

+

The connection is responsible to terminate its transaction, calling either the +commit() or rollback() method. Committed +changes are immediately made persistent into the database. Closing the +connection using the close() method or destroying the +connection object (using del or letting it fall out of scope) +will result in an implicit rollback() call.

+

It is possible to set the connection in autocommit mode: this way all the +commands executed will be immediately committed and no rollback is possible. A +few commands (e.g. CREATE DATABASE, VACUUM...) require to be run +outside any transaction: in order to be able to run these commands from +Psycopg, the session must be in autocommit mode: you can use the +autocommit property (set_isolation_level() in +older versions).

+
+

Warning

+

By default even a simple SELECT will start a transaction: in +long-running programs, if no further action is taken, the session will +remain “idle in transaction”, a condition non desiderable for several +reasons (locks are held by the session, tables bloat...). For long lived +scripts, either make sure to terminate a transaction as soon as possible or +use an autocommit connection.

+
+

A few other transaction properties can be set session-wide by the +connection: for instance it is possible to have read-only transactions or +change the isolation level. See the set_session() method for all +the details.

+
+
+

Server side cursors

+

When a database query is executed, the Psycopg cursor usually fetches +all the records returned by the backend, transferring them to the client +process. If the query returned an huge amount of data, a proportionally large +amount of memory will be allocated by the client.

+

If the dataset is too large to be practically handled on the client side, it is +possible to create a server side cursor. Using this kind of cursor it is +possible to transfer to the client only a controlled amount of data, so that a +large dataset can be examined without keeping it entirely in memory.

+

Server side cursor are created in PostgreSQL using the DECLARE command and +subsequently handled using MOVE, FETCH and CLOSE commands.

+

Psycopg wraps the database server side cursor in named cursors. A named +cursor is created using the cursor() method specifying the +name parameter. Such cursor will behave mostly like a regular cursor, +allowing the user to move in the dataset using the scroll() +method and to read the data using fetchone() and +fetchmany() methods.

+

Named cursors are also iterable like regular cursors. +Note however that before Psycopg 2.4 iteration was performed fetching one +record at time from the backend, resulting in a large overhead. The attribute +itersize now controls how many records are fetched at time +during the iteration: the default value of 2000 allows to fetch about 100KB +per roundtrip assuming records of 10-20 columns of mixed number and strings; +you may decrease this value if you are dealing with huge records.

+

Named cursors are usually created WITHOUT HOLD, meaning they live only +as long as the current transaction. Trying to fetch from a named cursor after +a commit() or to create a named cursor when the connection +transaction isolation level is set to AUTOCOMMIT will result in an exception. +It is possible to create a WITH HOLD cursor by specifying a True +value for the withhold parameter to cursor() or by setting the +withhold attribute to True before calling execute() on +the cursor. It is extremely important to always close() such cursors, +otherwise they will continue to hold server-side resources until the connection +will be eventually closed. Also note that while WITH HOLD cursors +lifetime extends well after commit(), calling +rollback() will automatically close the cursor.

+
+

Note

+

It is also possible to use a named cursor to consume a cursor created +in some other way than using the DECLARE executed by +execute(). For example, you may have a PL/pgSQL function +returning a cursor:

+
CREATE FUNCTION reffunc(refcursor) RETURNS refcursor AS $$
+BEGIN
+    OPEN $1 FOR SELECT col FROM test;
+    RETURN $1;
+END;
+$$ LANGUAGE plpgsql;
+
+

You can read the cursor content by calling the function with a regular, +non-named, Psycopg cursor:

+
cur1 = conn.cursor()
+cur1.callproc('reffunc', ['curname'])
+
+
+

and then use a named cursor in the same transaction to “steal the cursor”:

+
cur2 = conn.cursor('curname')
+for record in cur2:     # or cur2.fetchone, fetchmany...
+    # do something with record
+    pass
+
+
+
+
+
+

Thread and process safety

+

The Psycopg module and the connection objects are thread-safe: many +threads can access the same database either using separate sessions and +creating a connection per thread or using the same +connection and creating separate cursors. In DB API 2.0 parlance, Psycopg is +level 2 thread safe.

+

The difference between the above two approaches is that, using different +connections, the commands will be executed in different sessions and will be +served by different server processes. On the other hand, using many cursors on +the same connection, all the commands will be executed in the same session +(and in the same transaction if the connection is not in autocommit mode), but they will be serialized.

+

The above observations are only valid for regular threads: they don’t apply to +forked processes nor to green threads. libpq connections shouldn’t be used by a +forked processes, so when using a module such as multiprocessing or a +forking web deploy method such as FastCGI make sure to create the connections +after the fork.

+

Connections shouldn’t be shared either by different green threads: see +Support to coroutine libraries for further details.

+
+
+

Using COPY TO and COPY FROM

+

Psycopg cursor objects provide an interface to the efficient +PostgreSQL COPY command to move data from files to tables and back. +The methods exposed are:

+
+
copy_from()
+
Reads data from a file-like object appending them to a database table +(COPY table FROM file syntax). The source file must have both +read() and readline() method.
+
copy_to()
+
Writes the content of a table to a file-like object (COPY table TO +file syntax). The target file must have a write() method.
+
copy_expert()
+
Allows to handle more specific cases and to use all the COPY +features available in PostgreSQL.
+
+

Please refer to the documentation of the single methods for details and +examples.

+
+
+

Access to PostgreSQL large objects

+

PostgreSQL offers support for large objects, which provide stream-style +access to user data that is stored in a special large-object structure. They +are useful with data values too large to be manipulated conveniently as a +whole.

+

Psycopg allows access to the large object using the +lobject class. Objects are generated using the +connection.lobject() factory method. Data can be retrieved either as bytes +or as Unicode strings.

+

Psycopg large object support efficient import/export with file system files +using the lo_import() and lo_export() libpq functions.

+
+
+

Two-Phase Commit protocol support

+

+New in version 2.3.

+

Psycopg exposes the two-phase commit features available since PostgreSQL 8.1 +implementing the two-phase commit extensions proposed by the DB API 2.0.

+

The DB API 2.0 model of two-phase commit is inspired by the XA specification, +according to which transaction IDs are formed from three components:

+
    +
  • a format ID (non-negative 32 bit integer)
  • +
  • a global transaction ID (string not longer than 64 bytes)
  • +
  • a branch qualifier (string not longer than 64 bytes)
  • +
+

For a particular global transaction, the first two components will be the same +for all the resources. Every resource will be assigned a different branch +qualifier.

+

According to the DB API 2.0 specification, a transaction ID is created using the +connection.xid() method. Once you have a transaction id, a distributed +transaction can be started with connection.tpc_begin(), prepared using +tpc_prepare() and completed using tpc_commit() or +tpc_rollback(). Transaction IDs can also be retrieved from the +database using tpc_recover() and completed using the above +tpc_commit() and tpc_rollback().

+

PostgreSQL doesn’t follow the XA standard though, and the ID for a PostgreSQL +prepared transaction can be any string up to 200 characters long. +Psycopg’s Xid objects can represent both XA-style +transactions IDs (such as the ones created by the xid() method) and +PostgreSQL transaction IDs identified by an unparsed string.

+

The format in which the Xids are converted into strings passed to the +database is the same employed by the PostgreSQL JDBC driver: this should +allow interoperation between tools written in Python and in Java. For example +a recovery tool written in Python would be able to recognize the components of +transactions produced by a Java program.

+

For further details see the documentation for the above methods.

+
+
+ + +
+
+
+ +
+
+ + + + \ No newline at end of file diff -Nru psycopg2-2.0.13/doc/Makefile psycopg2-2.4.5/doc/Makefile --- psycopg2-2.0.13/doc/Makefile 1970-01-01 00:00:00.000000000 +0000 +++ psycopg2-2.4.5/doc/Makefile 2011-02-06 15:58:34.000000000 +0000 @@ -0,0 +1,23 @@ +.PHONY: help clean html text doctest + +docs: html text + +check: doctest + +help: + cd src && $(MAKE) $@ + +html: + cd src && $(MAKE) $@ + cp -r src/_build/html . + +text: + cd src && $(MAKE) $@ + cd src && tools/stitch_text.py index.rst _build/text > ../psycopg2.txt + +doctest: + cd src && $(MAKE) $@ + +clean: + cd src && $(MAKE) $@ + rm -rf html psycopg2.txt diff -Nru psycopg2-2.0.13/doc/pep-0249.txt psycopg2-2.4.5/doc/pep-0249.txt --- psycopg2-2.0.13/doc/pep-0249.txt 1970-01-01 00:00:00.000000000 +0000 +++ psycopg2-2.4.5/doc/pep-0249.txt 2010-07-25 09:49:01.000000000 +0000 @@ -0,0 +1,1005 @@ +PEP: 249 +Title: Python Database API Specification v2.0 +Version: $Revision: 1555 $ +Author: db-sig@python.org (Python Database SIG) +Editor: mal@lemburg.com (Marc-Andre Lemburg) +Status: Final +Type: Informational +Replaces: 248 +Release-Date: 07 Apr 1999 + +Introduction + + This API has been defined to encourage similarity between the + Python modules that are used to access databases. By doing this, + we hope to achieve a consistency leading to more easily understood + modules, code that is generally more portable across databases, + and a broader reach of database connectivity from Python. + + The interface specification consists of several sections: + + * Module Interface + * Connection Objects + * Cursor Objects + * DBI Helper Objects + * Type Objects and Constructors + * Implementation Hints + * Major Changes from 1.0 to 2.0 + + Comments and questions about this specification may be directed + to the SIG for Database Interfacing with Python + (db-sig@python.org). + + For more information on database interfacing with Python and + available packages see the Database Topic + Guide at http://www.python.org/topics/database/. + + This document describes the Python Database API Specification 2.0 + and a set of common optional extensions. The previous version 1.0 + version is still available as reference, in PEP 248. Package + writers are encouraged to use this version of the specification as + basis for new interfaces. + +Module Interface + + Access to the database is made available through connection + objects. The module must provide the following constructor for + these: + + connect(parameters...) + + Constructor for creating a connection to the database. + Returns a Connection Object. It takes a number of + parameters which are database dependent. [1] + + These module globals must be defined: + + apilevel + + String constant stating the supported DB API level. + Currently only the strings '1.0' and '2.0' are allowed. + + If not given, a DB-API 1.0 level interface should be + assumed. + + threadsafety + + Integer constant stating the level of thread safety the + interface supports. Possible values are: + + 0 Threads may not share the module. + 1 Threads may share the module, but not connections. + 2 Threads may share the module and connections. + 3 Threads may share the module, connections and + cursors. + + Sharing in the above context means that two threads may + use a resource without wrapping it using a mutex semaphore + to implement resource locking. Note that you cannot always + make external resources thread safe by managing access + using a mutex: the resource may rely on global variables + or other external sources that are beyond your control. + + paramstyle + + String constant stating the type of parameter marker + formatting expected by the interface. Possible values are + [2]: + + 'qmark' Question mark style, + e.g. '...WHERE name=?' + 'numeric' Numeric, positional style, + e.g. '...WHERE name=:1' + 'named' Named style, + e.g. '...WHERE name=:name' + 'format' ANSI C printf format codes, + e.g. '...WHERE name=%s' + 'pyformat' Python extended format codes, + e.g. '...WHERE name=%(name)s' + + The module should make all error information available through + these exceptions or subclasses thereof: + + Warning + + Exception raised for important warnings like data + truncations while inserting, etc. It must be a subclass of + the Python StandardError (defined in the module + exceptions). + + Error + + Exception that is the base class of all other error + exceptions. You can use this to catch all errors with one + single 'except' statement. Warnings are not considered + errors and thus should not use this class as base. It must + be a subclass of the Python StandardError (defined in the + module exceptions). + + InterfaceError + + Exception raised for errors that are related to the + database interface rather than the database itself. It + must be a subclass of Error. + + DatabaseError + + Exception raised for errors that are related to the + database. It must be a subclass of Error. + + DataError + + Exception raised for errors that are due to problems with + the processed data like division by zero, numeric value + out of range, etc. It must be a subclass of DatabaseError. + + OperationalError + + Exception raised for errors that are related to the + database's operation and not necessarily under the control + of the programmer, e.g. an unexpected disconnect occurs, + the data source name is not found, a transaction could not + be processed, a memory allocation error occurred during + processing, etc. It must be a subclass of DatabaseError. + + IntegrityError + + Exception raised when the relational integrity of the + database is affected, e.g. a foreign key check fails. It + must be a subclass of DatabaseError. + + InternalError + + Exception raised when the database encounters an internal + error, e.g. the cursor is not valid anymore, the + transaction is out of sync, etc. It must be a subclass of + DatabaseError. + + ProgrammingError + + Exception raised for programming errors, e.g. table not + found or already exists, syntax error in the SQL + statement, wrong number of parameters specified, etc. It + must be a subclass of DatabaseError. + + NotSupportedError + + Exception raised in case a method or database API was used + which is not supported by the database, e.g. requesting a + .rollback() on a connection that does not support + transaction or has transactions turned off. It must be a + subclass of DatabaseError. + + This is the exception inheritance layout: + + StandardError + |__Warning + |__Error + |__InterfaceError + |__DatabaseError + |__DataError + |__OperationalError + |__IntegrityError + |__InternalError + |__ProgrammingError + |__NotSupportedError + + Note: The values of these exceptions are not defined. They should + give the user a fairly good idea of what went wrong, though. + + +Connection Objects + + Connection Objects should respond to the following methods: + + .close() + + Close the connection now (rather than whenever __del__ is + called). The connection will be unusable from this point + forward; an Error (or subclass) exception will be raised + if any operation is attempted with the connection. The + same applies to all cursor objects trying to use the + connection. Note that closing a connection without + committing the changes first will cause an implicit + rollback to be performed. + + + .commit() + + Commit any pending transaction to the database. Note that + if the database supports an auto-commit feature, this must + be initially off. An interface method may be provided to + turn it back on. + + Database modules that do not support transactions should + implement this method with void functionality. + + .rollback() + + This method is optional since not all databases provide + transaction support. [3] + + In case a database does provide transactions this method + causes the the database to roll back to the start of any + pending transaction. Closing a connection without + committing the changes first will cause an implicit + rollback to be performed. + + .cursor() + + Return a new Cursor Object using the connection. If the + database does not provide a direct cursor concept, the + module will have to emulate cursors using other means to + the extent needed by this specification. [4] + + +Cursor Objects + + These objects represent a database cursor, which is used to + manage the context of a fetch operation. Cursors created from + the same connection are not isolated, i.e., any changes + done to the database by a cursor are immediately visible by the + other cursors. Cursors created from different connections can + or can not be isolated, depending on how the transaction support + is implemented (see also the connection's rollback() and commit() + methods.) + + Cursor Objects should respond to the following methods and + attributes: + + .description + + This read-only attribute is a sequence of 7-item + sequences. Each of these sequences contains information + describing one result column: (name, type_code, + display_size, internal_size, precision, scale, + null_ok). The first two items (name and type_code) are + mandatory, the other five are optional and must be set to + None if meaningfull values are not provided. + + This attribute will be None for operations that + do not return rows or if the cursor has not had an + operation invoked via the executeXXX() method yet. + + The type_code can be interpreted by comparing it to the + Type Objects specified in the section below. + + .rowcount + + This read-only attribute specifies the number of rows that + the last executeXXX() produced (for DQL statements like + 'select') or affected (for DML statements like 'update' or + 'insert'). + + The attribute is -1 in case no executeXXX() has been + performed on the cursor or the rowcount of the last + operation is not determinable by the interface. [7] + + Note: Future versions of the DB API specification could + redefine the latter case to have the object return None + instead of -1. + + .callproc(procname[,parameters]) + + (This method is optional since not all databases provide + stored procedures. [3]) + + Call a stored database procedure with the given name. The + sequence of parameters must contain one entry for each + argument that the procedure expects. The result of the + call is returned as modified copy of the input + sequence. Input parameters are left untouched, output and + input/output parameters replaced with possibly new values. + + The procedure may also provide a result set as + output. This must then be made available through the + standard fetchXXX() methods. + + .close() + + Close the cursor now (rather than whenever __del__ is + called). The cursor will be unusable from this point + forward; an Error (or subclass) exception will be raised + if any operation is attempted with the cursor. + + .execute(operation[,parameters]) + + Prepare and execute a database operation (query or + command). Parameters may be provided as sequence or + mapping and will be bound to variables in the operation. + Variables are specified in a database-specific notation + (see the module's paramstyle attribute for details). [5] + + A reference to the operation will be retained by the + cursor. If the same operation object is passed in again, + then the cursor can optimize its behavior. This is most + effective for algorithms where the same operation is used, + but different parameters are bound to it (many times). + + For maximum efficiency when reusing an operation, it is + best to use the setinputsizes() method to specify the + parameter types and sizes ahead of time. It is legal for + a parameter to not match the predefined information; the + implementation should compensate, possibly with a loss of + efficiency. + + The parameters may also be specified as list of tuples to + e.g. insert multiple rows in a single operation, but this + kind of usage is depreciated: executemany() should be used + instead. + + Return values are not defined. + + .executemany(operation,seq_of_parameters) + + Prepare a database operation (query or command) and then + execute it against all parameter sequences or mappings + found in the sequence seq_of_parameters. + + Modules are free to implement this method using multiple + calls to the execute() method or by using array operations + to have the database process the sequence as a whole in + one call. + + Use of this method for an operation which produces one or + more result sets constitutes undefined behavior, and the + implementation is permitted (but not required) to raise + an exception when it detects that a result set has been + created by an invocation of the operation. + + The same comments as for execute() also apply accordingly + to this method. + + Return values are not defined. + + .fetchone() + + Fetch the next row of a query result set, returning a + single sequence, or None when no more data is + available. [6] + + An Error (or subclass) exception is raised if the previous + call to executeXXX() did not produce any result set or no + call was issued yet. + + fetchmany([size=cursor.arraysize]) + + Fetch the next set of rows of a query result, returning a + sequence of sequences (e.g. a list of tuples). An empty + sequence is returned when no more rows are available. + + The number of rows to fetch per call is specified by the + parameter. If it is not given, the cursor's arraysize + determines the number of rows to be fetched. The method + should try to fetch as many rows as indicated by the size + parameter. If this is not possible due to the specified + number of rows not being available, fewer rows may be + returned. + + An Error (or subclass) exception is raised if the previous + call to executeXXX() did not produce any result set or no + call was issued yet. + + Note there are performance considerations involved with + the size parameter. For optimal performance, it is + usually best to use the arraysize attribute. If the size + parameter is used, then it is best for it to retain the + same value from one fetchmany() call to the next. + + .fetchall() + + Fetch all (remaining) rows of a query result, returning + them as a sequence of sequences (e.g. a list of tuples). + Note that the cursor's arraysize attribute can affect the + performance of this operation. + + An Error (or subclass) exception is raised if the previous + call to executeXXX() did not produce any result set or no + call was issued yet. + + .nextset() + + (This method is optional since not all databases support + multiple result sets. [3]) + + This method will make the cursor skip to the next + available set, discarding any remaining rows from the + current set. + + If there are no more sets, the method returns + None. Otherwise, it returns a true value and subsequent + calls to the fetch methods will return rows from the next + result set. + + An Error (or subclass) exception is raised if the previous + call to executeXXX() did not produce any result set or no + call was issued yet. + + .arraysize + + This read/write attribute specifies the number of rows to + fetch at a time with fetchmany(). It defaults to 1 meaning + to fetch a single row at a time. + + Implementations must observe this value with respect to + the fetchmany() method, but are free to interact with the + database a single row at a time. It may also be used in + the implementation of executemany(). + + .setinputsizes(sizes) + + This can be used before a call to executeXXX() to + predefine memory areas for the operation's parameters. + + sizes is specified as a sequence -- one item for each + input parameter. The item should be a Type Object that + corresponds to the input that will be used, or it should + be an integer specifying the maximum length of a string + parameter. If the item is None, then no predefined memory + area will be reserved for that column (this is useful to + avoid predefined areas for large inputs). + + This method would be used before the executeXXX() method + is invoked. + + Implementations are free to have this method do nothing + and users are free to not use it. + + .setoutputsize(size[,column]) + + Set a column buffer size for fetches of large columns + (e.g. LONGs, BLOBs, etc.). The column is specified as an + index into the result sequence. Not specifying the column + will set the default size for all large columns in the + cursor. + + This method would be used before the executeXXX() method + is invoked. + + Implementations are free to have this method do nothing + and users are free to not use it. + + +Type Objects and Constructors + + Many databases need to have the input in a particular format for + binding to an operation's input parameters. For example, if an + input is destined for a DATE column, then it must be bound to the + database in a particular string format. Similar problems exist + for "Row ID" columns or large binary items (e.g. blobs or RAW + columns). This presents problems for Python since the parameters + to the executeXXX() method are untyped. When the database module + sees a Python string object, it doesn't know if it should be bound + as a simple CHAR column, as a raw BINARY item, or as a DATE. + + To overcome this problem, a module must provide the constructors + defined below to create objects that can hold special values. + When passed to the cursor methods, the module can then detect the + proper type of the input parameter and bind it accordingly. + + A Cursor Object's description attribute returns information about + each of the result columns of a query. The type_code must compare + equal to one of Type Objects defined below. Type Objects may be + equal to more than one type code (e.g. DATETIME could be equal to + the type codes for date, time and timestamp columns; see the + Implementation Hints below for details). + + The module exports the following constructors and singletons: + + Date(year,month,day) + + This function constructs an object holding a date value. + + Time(hour,minute,second) + + This function constructs an object holding a time value. + + Timestamp(year,month,day,hour,minute,second) + + This function constructs an object holding a time stamp + value. + + DateFromTicks(ticks) + + This function constructs an object holding a date value + from the given ticks value (number of seconds since the + epoch; see the documentation of the standard Python time + module for details). + + TimeFromTicks(ticks) + + This function constructs an object holding a time value + from the given ticks value (number of seconds since the + epoch; see the documentation of the standard Python time + module for details). + + TimestampFromTicks(ticks) + + This function constructs an object holding a time stamp + value from the given ticks value (number of seconds since + the epoch; see the documentation of the standard Python + time module for details). + + Binary(string) + + This function constructs an object capable of holding a + binary (long) string value. + + + STRING + + This type object is used to describe columns in a database + that are string-based (e.g. CHAR). + + BINARY + + This type object is used to describe (long) binary columns + in a database (e.g. LONG, RAW, BLOBs). + + NUMBER + + This type object is used to describe numeric columns in a + database. + + DATETIME + + This type object is used to describe date/time columns in + a database. + + ROWID + + This type object is used to describe the "Row ID" column + in a database. + + SQL NULL values are represented by the Python None singleton on + input and output. + + Note: Usage of Unix ticks for database interfacing can cause + troubles because of the limited date range they cover. + + +Implementation Hints for Module Authors + + * The preferred object types for the date/time objects are those + defined in the mxDateTime package. It provides all necessary + constructors and methods both at Python and C level. + + * The preferred object type for Binary objects are the + buffer types available in standard Python starting with + version 1.5.2. Please see the Python documentation for + details. For information about the the C interface have a + look at Include/bufferobject.h and + Objects/bufferobject.c in the Python source + distribution. + + * Starting with Python 2.3, module authors can also use the object + types defined in the standard datetime module for date/time + processing. However, it should be noted that this does not + expose a C API like mxDateTime does which means that integration + with C based database modules is more difficult. + + * Here is a sample implementation of the Unix ticks based + constructors for date/time delegating work to the generic + constructors: + + import time + + def DateFromTicks(ticks): + return apply(Date,time.localtime(ticks)[:3]) + + def TimeFromTicks(ticks): + return apply(Time,time.localtime(ticks)[3:6]) + + def TimestampFromTicks(ticks): + return apply(Timestamp,time.localtime(ticks)[:6]) + + * This Python class allows implementing the above type + objects even though the description type code field yields + multiple values for on type object: + + class DBAPITypeObject: + def __init__(self,*values): + self.values = values + def __cmp__(self,other): + if other in self.values: + return 0 + if other < self.values: + return 1 + else: + return -1 + + The resulting type object compares equal to all values + passed to the constructor. + + * Here is a snippet of Python code that implements the exception + hierarchy defined above: + + import exceptions + + class Error(exceptions.StandardError): + pass + + class Warning(exceptions.StandardError): + pass + + class InterfaceError(Error): + pass + + class DatabaseError(Error): + pass + + class InternalError(DatabaseError): + pass + + class OperationalError(DatabaseError): + pass + + class ProgrammingError(DatabaseError): + pass + + class IntegrityError(DatabaseError): + pass + + class DataError(DatabaseError): + pass + + class NotSupportedError(DatabaseError): + pass + + In C you can use the PyErr_NewException(fullname, + base, NULL) API to create the exception objects. + + +Optional DB API Extensions + + During the lifetime of DB API 2.0, module authors have often + extended their implementations beyond what is required by this DB + API specification. To enhance compatibility and to provide a clean + upgrade path to possible future versions of the specification, + this section defines a set of common extensions to the core DB API + 2.0 specification. + + As with all DB API optional features, the database module authors + are free to not implement these additional attributes and methods + (using them will then result in an AttributeError) or to raise a + NotSupportedError in case the availability can only be checked at + run-time. + + It has been proposed to make usage of these extensions optionally + visible to the programmer by issuing Python warnings through the + Python warning framework. To make this feature useful, the warning + messages must be standardized in order to be able to mask them. These + standard messages are referred to below as "Warning Message". + + Cursor Attribute .rownumber + + This read-only attribute should provide the current 0-based + index of the cursor in the result set or None if the index cannot + be determined. + + The index can be seen as index of the cursor in a sequence (the + result set). The next fetch operation will fetch the row + indexed by .rownumber in that sequence. + + Warning Message: "DB-API extension cursor.rownumber used" + + Connection Attributes .Error, .ProgrammingError, etc. + + All exception classes defined by the DB API standard should be + exposed on the Connection objects are attributes (in addition + to being available at module scope). + + These attributes simplify error handling in multi-connection + environments. + + Warning Message: "DB-API extension connection. used" + + Cursor Attributes .connection + + This read-only attribute return a reference to the Connection + object on which the cursor was created. + + The attribute simplifies writing polymorph code in + multi-connection environments. + + Warning Message: "DB-API extension cursor.connection used" + + Cursor Method .scroll(value[,mode='relative']) + + Scroll the cursor in the result set to a new position according + to mode. + + If mode is 'relative' (default), value is taken as offset to + the current position in the result set, if set to 'absolute', + value states an absolute target position. + + An IndexError should be raised in case a scroll operation would + leave the result set. In this case, the cursor position is left + undefined (ideal would be to not move the cursor at all). + + Note: This method should use native scrollable cursors, if + available , or revert to an emulation for forward-only + scrollable cursors. The method may raise NotSupportedErrors to + signal that a specific operation is not supported by the + database (e.g. backward scrolling). + + Warning Message: "DB-API extension cursor.scroll() used" + + Cursor Attribute .messages + + This is a Python list object to which the interface appends + tuples (exception class, exception value) for all messages + which the interfaces receives from the underlying database for + this cursor. + + The list is cleared by all standard cursor methods calls (prior + to executing the call) except for the .fetchXXX() calls + automatically to avoid excessive memory usage and can also be + cleared by executing "del cursor.messages[:]". + + All error and warning messages generated by the database are + placed into this list, so checking the list allows the user to + verify correct operation of the method calls. + + The aim of this attribute is to eliminate the need for a + Warning exception which often causes problems (some warnings + really only have informational character). + + Warning Message: "DB-API extension cursor.messages used" + + Connection Attribute .messages + + Same as cursor.messages except that the messages in the list + are connection oriented. + + The list is cleared automatically by all standard connection + methods calls (prior to executing the call) to avoid excessive + memory usage and can also be cleared by executing "del + connection.messages[:]". + + Warning Message: "DB-API extension connection.messages used" + + Cursor Method .next() + + Return the next row from the currently executing SQL statement + using the same semantics as .fetchone(). A StopIteration + exception is raised when the result set is exhausted for Python + versions 2.2 and later. Previous versions don't have the + StopIteration exception and so the method should raise an + IndexError instead. + + Warning Message: "DB-API extension cursor.next() used" + + Cursor Method .__iter__() + + Return self to make cursors compatible to the iteration protocol. + + Warning Message: "DB-API extension cursor.__iter__() used" + + Cursor Attribute .lastrowid + + This read-only attribute provides the rowid of the last + modified row (most databases return a rowid only when a single + INSERT operation is performed). If the operation does not set + a rowid or if the database does not support rowids, this + attribute should be set to None. + + The semantics of .lastrowid are undefined in case the last + executed statement modified more than one row, e.g. when + using INSERT with .executemany(). + + Warning Message: "DB-API extension cursor.lastrowid used" + + +Optional Error Handling Extension + + The core DB API specification only introduces a set of exceptions + which can be raised to report errors to the user. In some cases, + exceptions may be too disruptive for the flow of a program or even + render execution impossible. + + For these cases and in order to simplify error handling when + dealing with databases, database module authors may choose to + implement user defineable error handlers. This section describes a + standard way of defining these error handlers. + + Cursor/Connection Attribute .errorhandler + + Read/write attribute which references an error handler to call + in case an error condition is met. + + The handler must be a Python callable taking the following + arguments: errorhandler(connection, cursor, errorclass, + errorvalue) where connection is a reference to the connection + on which the cursor operates, cursor a reference to the cursor + (or None in case the error does not apply to a cursor), + errorclass is an error class which to instantiate using + errorvalue as construction argument. + + The standard error handler should add the error information to + the appropriate .messages attribute (connection.messages or + cursor.messages) and raise the exception defined by the given + errorclass and errorvalue parameters. + + If no errorhandler is set (the attribute is None), the standard + error handling scheme as outlined above, should be applied. + + Warning Message: "DB-API extension .errorhandler used" + + Cursors should inherit the .errorhandler setting from their + connection objects at cursor creation time. + + +Frequently Asked Questions + + The database SIG often sees reoccurring questions about the DB API + specification. This section covers some of the issues people + sometimes have with the specification. + + Question: + + How can I construct a dictionary out of the tuples returned by + .fetchxxx(): + + Answer: + + There are several existing tools available which provide + helpers for this task. Most of them use the approach of using + the column names defined in the cursor attribute .description + as basis for the keys in the row dictionary. + + Note that the reason for not extending the DB API specification + to also support dictionary return values for the .fetchxxx() + methods is that this approach has several drawbacks: + + * Some databases don't support case-sensitive column names or + auto-convert them to all lowercase or all uppercase + characters. + + * Columns in the result set which are generated by the query + (e.g. using SQL functions) don't map to table column names + and databases usually generate names for these columns in a + very database specific way. + + As a result, accessing the columns through dictionary keys + varies between databases and makes writing portable code + impossible. + + +Major Changes from Version 1.0 to Version 2.0 + + The Python Database API 2.0 introduces a few major changes + compared to the 1.0 version. Because some of these changes will + cause existing DB API 1.0 based scripts to break, the major + version number was adjusted to reflect this change. + + These are the most important changes from 1.0 to 2.0: + + * The need for a separate dbi module was dropped and the + functionality merged into the module interface itself. + + * New constructors and Type Objects were added for date/time + values, the RAW Type Object was renamed to BINARY. The + resulting set should cover all basic data types commonly + found in modern SQL databases. + + * New constants (apilevel, threadlevel, paramstyle) and + methods (executemany, nextset) were added to provide better + database bindings. + + * The semantics of .callproc() needed to call stored + procedures are now clearly defined. + + * The definition of the .execute() return value changed. + Previously, the return value was based on the SQL statement + type (which was hard to implement right) -- it is undefined + now; use the more flexible .rowcount attribute + instead. Modules are free to return the old style return + values, but these are no longer mandated by the + specification and should be considered database interface + dependent. + + * Class based exceptions were incorporated into the + specification. Module implementors are free to extend the + exception layout defined in this specification by + subclassing the defined exception classes. + + Post-publishing additions to the DB API 2.0 specification: + + * Additional optional DB API extensions to the set of + core functionality were specified. + + +Open Issues + + Although the version 2.0 specification clarifies a lot of + questions that were left open in the 1.0 version, there are still + some remaining issues which should be addressed in future + versions: + + * Define a useful return value for .nextset() for the case where + a new result set is available. + + * Create a fixed point numeric type for use as loss-less + monetary and decimal interchange format. + + +Footnotes + + [1] As a guideline the connection constructor parameters should be + implemented as keyword parameters for more intuitive use and + follow this order of parameters: + + dsn Data source name as string + user User name as string (optional) + password Password as string (optional) + host Hostname (optional) + database Database name (optional) + + E.g. a connect could look like this: + + connect(dsn='myhost:MYDB',user='guido',password='234$') + + [2] Module implementors should prefer 'numeric', 'named' or + 'pyformat' over the other formats because these offer more + clarity and flexibility. + + [3] If the database does not support the functionality required + by the method, the interface should throw an exception in + case the method is used. + + The preferred approach is to not implement the method and + thus have Python generate an AttributeError in + case the method is requested. This allows the programmer to + check for database capabilities using the standard + hasattr() function. + + For some dynamically configured interfaces it may not be + appropriate to require dynamically making the method + available. These interfaces should then raise a + NotSupportedError to indicate the non-ability + to perform the roll back when the method is invoked. + + [4] a database interface may choose to support named cursors by + allowing a string argument to the method. This feature is + not part of the specification, since it complicates + semantics of the .fetchXXX() methods. + + [5] The module will use the __getitem__ method of the parameters + object to map either positions (integers) or names (strings) + to parameter values. This allows for both sequences and + mappings to be used as input. + + The term "bound" refers to the process of binding an input + value to a database execution buffer. In practical terms, + this means that the input value is directly used as a value + in the operation. The client should not be required to + "escape" the value so that it can be used -- the value + should be equal to the actual database value. + + [6] Note that the interface may implement row fetching using + arrays and other optimizations. It is not + guaranteed that a call to this method will only move the + associated cursor forward by one row. + + [7] The rowcount attribute may be coded in a way that updates + its value dynamically. This can be useful for databases that + return usable rowcount values only after the first call to + a .fetchXXX() method. + +Acknowledgements + + Many thanks go to Andrew Kuchling who converted the Python + Database API Specification 2.0 from the original HTML format into + the PEP format. + +Copyright + + This document has been placed in the Public Domain. + + + +Local Variables: +mode: indented-text +indent-tabs-mode: nil +End: diff -Nru psycopg2-2.0.13/doc/psycopg2.txt psycopg2-2.4.5/doc/psycopg2.txt --- psycopg2-2.0.13/doc/psycopg2.txt 1970-01-01 00:00:00.000000000 +0000 +++ psycopg2-2.4.5/doc/psycopg2.txt 2012-03-28 21:17:20.000000000 +0000 @@ -0,0 +1,3876 @@ + +Psycopg -- PostgreSQL database adapter for Python +************************************************* + +Psycopg is a PostgreSQL database adapter for the Python programming +language. Its main features are that it supports the full Python DB +API 2.0 and it is thread safe (threads can share the connections). It +was designed for heavily multi-threaded applications that create and +destroy lots of cursors and make a large number of concurrent +"INSERT"s or "UPDATE"s. The Psycopg distribution includes ZPsycopgDA, +a Zope Database Adapter. + +Psycopg 2 is mostly implemented in C as a libpq wrapper, resulting in +being both efficient and secure. It features client-side and *server- +side* cursors, *asynchronous communication* and *notifications*, "COPY +TO/COPY FROM" support, and a flexible *objects adaptation system*. +Many basic Python types are supported out-of-the-box and mapped to +matching PostgreSQL data types, such as strings (both bytes and +Unicode), numbers (ints, longs, floats, decimals), booleans and +datetime objects (both built-in and mx.DateTime), several types of +*binary objects*. Also available are mappings between lists and +PostgreSQL arrays of any supported type, between *dictionaries and +PostgreSQL hstores*, and between *tuples/namedtuples and PostgreSQL +composite types*. + +Psycopg 2 is both Unicode and Python 3 friendly. + +-[ Contents ]- + +* Basic module usage + * Passing parameters to SQL queries + * Adaptation of Python values to SQL types + * Transactions control + * Server side cursors + * Thread and process safety + * Using COPY TO and COPY FROM + * Access to PostgreSQL large objects + * Two-Phase Commit protocol support +* The "psycopg2" module content + * Exceptions + * Type Objects and Constructors +* The "connection" class +* The "cursor" class +* More advanced topics + * Connection and cursor factories + * Adapting new Python types to SQL syntax + * Type casting of SQL types into Python objects + * Asynchronous notifications + * Asynchronous support + * Support to coroutine libraries +* "psycopg2.extensions" -- Extensions to the DB API + * SQL adaptation protocol objects + * Database types casting functions + * Additional exceptions + * Isolation level constants + * Transaction status constants + * Connection status constants + * Poll constants + * Additional database types +* "psycopg2.tz" -- "tzinfo" implementations for Psycopg 2 +* "psycopg2.pool" -- Connections pooling +* "psycopg2.extras" -- Miscellaneous goodies for Psycopg 2 + * Connection and cursor subclasses + * Additional data types + * Fractional time zones + * Coroutine support +* "psycopg2.errorcodes" -- Error codes defined by PostgreSQL +* Frequently Asked Questions + * Problems with transactions handling + * Problems with type conversions + * Best practices + * Problems compiling and deploying psycopg2 + + +Basic module usage +****************** + +The basic Psycopg usage is common to all the database adapters +implementing the DB API 2.0 protocol. Here is an interactive session +showing some of the basic commands: + + >>> import psycopg2 + + # Connect to an existing database + >>> conn = psycopg2.connect("dbname=test user=postgres") + + # Open a cursor to perform database operations + >>> cur = conn.cursor() + + # Execute a command: this creates a new table + >>> cur.execute("CREATE TABLE test (id serial PRIMARY KEY, num integer, data varchar);") + + # Pass data to fill a query placeholders and let Psycopg perform + # the correct conversion (no more SQL injections!) + >>> cur.execute("INSERT INTO test (num, data) VALUES (%s, %s)", + ... (100, "abc'def")) + + # Query the database and obtain data as Python objects + >>> cur.execute("SELECT * FROM test;") + >>> cur.fetchone() + (1, 100, "abc'def") + + # Make the changes to the database persistent + >>> conn.commit() + + # Close communication with the database + >>> cur.close() + >>> conn.close() + +The main entry points of Psycopg are: + +* The function "connect()" creates a new database session and returns + a new "connection" instance. + +* The class "connection" encapsulates a database session. It allows + to: + + * create new "cursor"s using the "cursor()" method to execute + database commands and queries, + + * terminate the session using the methods "commit()" or + "rollback()". + +* The class "cursor" allows interaction with the database: + + * send commands to the database using methods such as "execute()" + and "executemany()", + + * retrieve data from the database *by iteration* or using methods + such as "fetchone()", "fetchmany()", "fetchall()". + + +Passing parameters to SQL queries +================================= + +Psycopg casts Python variables to SQL literals by type. Many standard +Python types are already adapted to the correct SQL representation. + +Example: the Python function call: + + >>> cur.execute( + ... """INSERT INTO some_table (an_int, a_date, a_string) + ... VALUES (%s, %s, %s);""", + ... (10, datetime.date(2005, 11, 18), "O'Reilly")) + +is converted into the SQL command: + + INSERT INTO some_table (an_int, a_date, a_string) + VALUES (10, '2005-11-18', 'O''Reilly'); + +Named arguments are supported too using "%(*name*)s" placeholders. +Using named arguments the values can be passed to the query in any +order and many placeholders can use the same values: + + >>> cur.execute( + ... """INSERT INTO some_table (an_int, a_date, another_date, a_string) + ... VALUES (%(int)s, %(date)s, %(date)s, %(str)s);""", + ... {'int': 10, 'str': "O'Reilly", 'date': datetime.date(2005, 11, 18)}) + +While the mechanism resembles regular Python strings manipulation, +there are a few subtle differences you should care about when passing +parameters to a query: + +* The Python string operator "%" is not used: the "execute()" method + accepts a tuple or dictionary of values as second parameter. + **Never** use "%" or "+" to merge values into queries. + +* The variables placeholder must *always be a* "%s", even if a + different placeholder (such as a "%d" for integers or "%f" for + floats) may look more appropriate: + + >>> cur.execute("INSERT INTO numbers VALUES (%d)", (42,)) # WRONG + >>> cur.execute("INSERT INTO numbers VALUES (%s)", (42,)) # correct + +* For positional variables binding, *the second argument must always + be a sequence*, even if it contains a single variable. And remember + that Python requires a comma to create a single element tuple: + + >>> cur.execute("INSERT INTO foo VALUES (%s)", "bar") # WRONG + >>> cur.execute("INSERT INTO foo VALUES (%s)", ("bar")) # WRONG + >>> cur.execute("INSERT INTO foo VALUES (%s)", ("bar",)) # correct + >>> cur.execute("INSERT INTO foo VALUES (%s)", ["bar"]) # correct + +* Only variable values should be bound via this method: it shouldn't + be used to set table or field names. For these elements, ordinary + string formatting should be used before running "execute()". + + +The problem with the query parameters +------------------------------------- + +The SQL representation for many data types is often not the same of +the Python string representation. The classic example is with single +quotes in strings: SQL uses them as string constants bounds and +requires them to be escaped, whereas in Python single quotes can be +left unescaped in strings bounded by double quotes. For this reason a +naïve approach to the composition of query strings, e.g. using string +concatenation, is a recipe for terrible problems: + + >>> SQL = "INSERT INTO authors (name) VALUES ('%s');" # NEVER DO THIS + >>> data = ("O'Reilly", ) + >>> cur.execute(SQL % data) # THIS WILL FAIL MISERABLY + ProgrammingError: syntax error at or near "Reilly" + LINE 1: INSERT INTO authors (name) VALUES ('O'Reilly') + ^ + +If the variable containing the data to be sent to the database comes +from an untrusted source (e.g. a form published on a web site) an +attacker could easily craft a malformed string, either gaining access +to unauthorized data or performing destructive operations on the +database. This form of attack is called SQL injection and is known to +be one of the most widespread forms of attack to servers. Before +continuing, please print this page as a memo and hang it onto your +desk. + +Psycopg can automatically convert Python objects to and from SQL +literals: using this feature your code will be more robust and +reliable. We must stress this point: + +Warning: Never, **never**, **NEVER** use Python string concatenation ("+") or + string parameters interpolation ("%") to pass variables to a SQL + query string. Not even at gunpoint. + +The correct way to pass variables in a SQL command is using the second +argument of the "execute()" method: + + >>> SQL = "INSERT INTO authors (name) VALUES (%s);" # Note: no quotes + >>> data = ("O'Reilly", ) + >>> cur.execute(SQL, data) # Note: no % operator + + +Adaptation of Python values to SQL types +======================================== + +Many standards Python types are adapted into SQL and returned as +Python objects when a query is executed. + +If you need to convert other Python types to and from PostgreSQL data +types, see *Adapting new Python types to SQL syntax* and *Type casting +of SQL types into Python objects*. You can also find a few other +specialized adapters in the "psycopg2.extras" module. + +In the following examples the method "mogrify()" is used to show the +SQL string that would be sent to the database. + +* Python "None" and boolean values "True" and "False" are converted + into the proper SQL literals: + + >>> cur.mogrify("SELECT %s, %s, %s;", (None, True, False)) + >>> 'SELECT NULL, true, false;' + +* Numeric objects: "int", "long", "float", "Decimal" are converted in + the PostgreSQL numerical representation: + + >>> cur.mogrify("SELECT %s, %s, %s, %s;", (10, 10L, 10.0, Decimal("10.00"))) + >>> 'SELECT 10, 10, 10.0, 10.00;' + +* String types: "str", "unicode" are converted in SQL string syntax. + "unicode" objects ("str" in Python 3) are encoded in the connection + "encoding" to be sent to the backend: trying to send a character not + supported by the encoding will result in an error. Received data can + be converted either as "str" or "unicode": see *Unicode handling*. + +* Binary types: Python types representing binary objects are converted + into PostgreSQL binary string syntax, suitable for "bytea" fields. + Such types are "buffer" (only available in Python 2), "memoryview" + (available from Python 2.7), "bytearray" (available from Python 2.6) + and "bytes" (only from Python 3: the name is available from Python + 2.6 but it's only an alias for the type "str"). Any object + implementing the Revised Buffer Protocol should be usable as binary + type where the protocol is supported (i.e. from Python 2.6). + Received data is returned as "buffer" (in Python 2) or "memoryview" + (in Python 3). + + Changed in version 2.4: only strings were supported before. + + Changed in version 2.4.1: can parse the 'hex' format from 9.0 + servers without relying on the version of the client library. + + Note: In Python 2, if you have binary data in a "str" object, you can + pass them to a "bytea" field using the "psycopg2.Binary" wrapper: + + mypic = open('picture.png', 'rb').read() + curs.execute("insert into blobs (file) values (%s)", + (psycopg2.Binary(mypic),)) + + Warning: Since version 9.0 PostgreSQL uses by default a new "hex" format to + emit "bytea" fields. Starting from Psycopg 2.4.1 the format is + correctly supported. If you use a previous version you will need + some extra care when receiving bytea from PostgreSQL: you must + have at least libpq 9.0 installed on the client or alternatively + you can set the bytea_output configuration parameter to "escape", + either in the server configuration file or in the client session + (using a query such as "SET bytea_output TO escape;") before + receiving binary data. + +* Date and time objects: builtin "datetime", "date", "time", + "timedelta" are converted into PostgreSQL's "timestamp", "date", + "time", "interval" data types. Time zones are supported too. The + Egenix mx.DateTime objects are adapted the same way: + + >>> dt = datetime.datetime.now() + >>> dt + datetime.datetime(2010, 2, 8, 1, 40, 27, 425337) + + >>> cur.mogrify("SELECT %s, %s, %s;", (dt, dt.date(), dt.time())) + "SELECT '2010-02-08T01:40:27.425337', '2010-02-08', '01:40:27.425337';" + + >>> cur.mogrify("SELECT %s;", (dt - datetime.datetime(2010,1,1),)) + "SELECT '38 days 6027.425337 seconds';" + +* Python lists are converted into PostgreSQL "ARRAY"s: + + >>> cur.mogrify("SELECT %s;", ([10, 20, 30], )) + 'SELECT ARRAY[10, 20, 30];' + + Note: Reading back from PostgreSQL, arrays are converted to list of + Python objects as expected, but only if the types are known one. + Arrays of unknown types are returned as represented by the + database (e.g. "{a,b,c}"). You can easily create a typecaster for + *array of unknown types*. + +* Python tuples are converted in a syntax suitable for the SQL "IN" + operator and to represent a composite type: + + >>> cur.mogrify("SELECT %s IN %s;", (10, (10, 20, 30))) + 'SELECT 10 IN (10, 20, 30);' + + Note: SQL doesn't allow an empty list in the IN operator, so your code + should guard against empty tuples. + + If you want PostgreSQL composite types to be converted into a Python + tuple/namedtuple you can use the "register_composite()" function. + + New in version 2.0.6: the tuple "IN" adaptation. + + Changed in version 2.0.14: the tuple "IN" adapter is always active. + In previous releases it was necessary to import the "extensions" + module to have it registered. + + Changed in version 2.3: "namedtuple" instances are adapted like + regular tuples and can thus be used to represent composite types. + +* Python dictionaries are converted into the "hstore" data type. By + default the adapter is not enabled: see "register_hstore()" for + further details. + + New in version 2.3: the "hstore" adaptation. + + +Unicode handling +---------------- + +Psycopg can exchange Unicode data with a PostgreSQL database. Python +"unicode" objects are automatically *encoded* in the client encoding +defined on the database connection (the PostgreSQL encoding, available +in "connection.encoding", is translated into a Python codec using the +"encodings" mapping): + + >>> print u, type(u) + àèìòù€ + + >>> cur.execute("INSERT INTO test (num, data) VALUES (%s,%s);", (74, u)) + +When reading data from the database, in Python 2 the strings returned +are usually 8 bit "str" objects encoded in the database client +encoding: + + >>> print conn.encoding + UTF8 + + >>> cur.execute("SELECT data FROM test WHERE num = 74") + >>> x = cur.fetchone()[0] + >>> print x, type(x), repr(x) + àèìòù€ '\xc3\xa0\xc3\xa8\xc3\xac\xc3\xb2\xc3\xb9\xe2\x82\xac' + + >>> conn.set_client_encoding('LATIN9') + + >>> cur.execute("SELECT data FROM test WHERE num = 74") + >>> x = cur.fetchone()[0] + >>> print type(x), repr(x) + '\xe0\xe8\xec\xf2\xf9\xa4' + +In Python 3 instead the strings are automatically *decoded* in the +connection "encoding", as the "str" object can represent Unicode +characters. In Python 2 you must register a *typecaster* in order to +receive "unicode" objects: + + >>> psycopg2.extensions.register_type(psycopg2.extensions.UNICODE, cur) + + >>> cur.execute("SELECT data FROM test WHERE num = 74") + >>> x = cur.fetchone()[0] + >>> print x, type(x), repr(x) + àèìòù€ u'\xe0\xe8\xec\xf2\xf9\u20ac' + +In the above example, the "UNICODE" typecaster is registered only on +the cursor. It is also possible to register typecasters on the +connection or globally: see the function "register_type()" and *Type +casting of SQL types into Python objects* for details. + +Note: In Python 2, if you want to uniformly receive all your database + input in Unicode, you can register the related typecasters globally + as soon as Psycopg is imported: + + import psycopg2 + import psycopg2.extensions + psycopg2.extensions.register_type(psycopg2.extensions.UNICODE) + psycopg2.extensions.register_type(psycopg2.extensions.UNICODEARRAY) + + and then forget about this story. + + +Time zones handling +------------------- + +The PostgreSQL type "timestamp with time zone" is converted into +Python "datetime" objects with a "tzinfo" attribute set to a +"FixedOffsetTimezone" instance. + +>>> cur.execute("SET TIME ZONE 'Europe/Rome';") # UTC + 1 hour +>>> cur.execute("SELECT '2010-01-01 10:30:45'::timestamptz;") +>>> cur.fetchone()[0].tzinfo +psycopg2.tz.FixedOffsetTimezone(offset=60, name=None) + +Note that only time zones with an integer number of minutes are +supported: this is a limitation of the Python "datetime" module. A +few historical time zones had seconds in the UTC offset: these time +zones will have the offset rounded to the nearest minute, with an +error of up to 30 seconds. + +>>> cur.execute("SET TIME ZONE 'Asia/Calcutta';") # offset was +5:53:20 +>>> cur.execute("SELECT '1930-01-01 10:30:45'::timestamptz;") +>>> cur.fetchone()[0].tzinfo +psycopg2.tz.FixedOffsetTimezone(offset=353, name=None) + +Changed in version 2.2.2: timezones with seconds are supported (with +rounding). Previously such timezones raised an error. In order to +deal with them in previous versions use +"psycopg2.extras.register_tstz_w_secs()". + + +Transactions control +==================== + +In Psycopg transactions are handled by the "connection" class. By +default, the first time a command is sent to the database (using one +of the "cursor"s created by the connection), a new transaction is +created. The following database commands will be executed in the +context of the same transaction -- not only the commands issued by the +first cursor, but the ones issued by all the cursors created by the +same connection. Should any command fail, the transaction will be +aborted and no further command will be executed until a call to the +"rollback()" method. + +The connection is responsible to terminate its transaction, calling +either the "commit()" or "rollback()" method. Committed changes are +immediately made persistent into the database. Closing the connection +using the "close()" method or destroying the connection object (using +"del" or letting it fall out of scope) will result in an implicit +"rollback()" call. + +It is possible to set the connection in *autocommit* mode: this way +all the commands executed will be immediately committed and no +rollback is possible. A few commands (e.g. "CREATE DATABASE", +"VACUUM"...) require to be run outside any transaction: in order to be +able to run these commands from Psycopg, the session must be in +autocommit mode: you can use the "autocommit" property +("set_isolation_level()" in older versions). + +Warning: By default even a simple "SELECT" will start a transaction: in long- + running programs, if no further action is taken, the session will + remain "idle in transaction", a condition non desiderable for + several reasons (locks are held by the session, tables bloat...). + For long lived scripts, either make sure to terminate a transaction + as soon as possible or use an autocommit connection. + +A few other transaction properties can be set session-wide by the +"connection": for instance it is possible to have read-only +transactions or change the isolation level. See the "set_session()" +method for all the details. + + +Server side cursors +=================== + +When a database query is executed, the Psycopg "cursor" usually +fetches all the records returned by the backend, transferring them to +the client process. If the query returned an huge amount of data, a +proportionally large amount of memory will be allocated by the client. + +If the dataset is too large to be practically handled on the client +side, it is possible to create a *server side* cursor. Using this kind +of cursor it is possible to transfer to the client only a controlled +amount of data, so that a large dataset can be examined without +keeping it entirely in memory. + +Server side cursor are created in PostgreSQL using the "DECLARE" +command and subsequently handled using "MOVE", "FETCH" and "CLOSE" +commands. + +Psycopg wraps the database server side cursor in *named cursors*. A +named cursor is created using the "cursor()" method specifying the +*name* parameter. Such cursor will behave mostly like a regular +cursor, allowing the user to move in the dataset using the "scroll()" +method and to read the data using "fetchone()" and "fetchmany()" +methods. + +Named cursors are also *iterable* like regular cursors. Note however +that before Psycopg 2.4 iteration was performed fetching one record at +time from the backend, resulting in a large overhead. The attribute +"itersize" now controls how many records are fetched at time during +the iteration: the default value of 2000 allows to fetch about 100KB +per roundtrip assuming records of 10-20 columns of mixed number and +strings; you may decrease this value if you are dealing with huge +records. + +Named cursors are usually created "WITHOUT HOLD", meaning they live +only as long as the current transaction. Trying to fetch from a named +cursor after a "commit()" or to create a named cursor when the +"connection" transaction isolation level is set to "AUTOCOMMIT" will +result in an exception. It is possible to create a "WITH HOLD" cursor +by specifying a "True" value for the "withhold" parameter to +"cursor()" or by setting the "withhold" attribute to "True" before +calling "execute()" on the cursor. It is extremely important to always +"close()" such cursors, otherwise they will continue to hold server- +side resources until the connection will be eventually closed. Also +note that while "WITH HOLD" cursors lifetime extends well after +"commit()", calling "rollback()" will automatically close the cursor. + +Note: It is also possible to use a named cursor to consume a cursor + created in some other way than using the "DECLARE" executed by + "execute()". For example, you may have a PL/pgSQL function returning + a cursor: + + CREATE FUNCTION reffunc(refcursor) RETURNS refcursor AS $$ + BEGIN + OPEN $1 FOR SELECT col FROM test; + RETURN $1; + END; + $$ LANGUAGE plpgsql; + + You can read the cursor content by calling the function with a + regular, non-named, Psycopg cursor: + + cur1 = conn.cursor() + cur1.callproc('reffunc', ['curname']) + + and then use a named cursor in the same transaction to "steal the + cursor": + + cur2 = conn.cursor('curname') + for record in cur2: # or cur2.fetchone, fetchmany... + # do something with record + pass + + +Thread and process safety +========================= + +The Psycopg module and the "connection" objects are *thread-safe*: +many threads can access the same database either using separate +sessions and creating a "connection" per thread or using the same +connection and creating separate "cursor"s. In DB API 2.0 parlance, +Psycopg is *level 2 thread safe*. + +The difference between the above two approaches is that, using +different connections, the commands will be executed in different +sessions and will be served by different server processes. On the +other hand, using many cursors on the same connection, all the +commands will be executed in the same session (and in the same +transaction if the connection is not in *autocommit* mode), but they +will be serialized. + +The above observations are only valid for regular threads: they don't +apply to forked processes nor to green threads. "libpq" connections +shouldn't be used by a forked processes, so when using a module such +as "multiprocessing" or a forking web deploy method such as FastCGI +make sure to create the connections *after* the fork. + +Connections shouldn't be shared either by different green threads: see +*Support to coroutine libraries* for further details. + + +Using COPY TO and COPY FROM +=========================== + +Psycopg "cursor" objects provide an interface to the efficient +PostgreSQL "COPY" command to move data from files to tables and back. +The methods exposed are: + +"copy_from()" + Reads data *from* a file-like object appending them to a database + table ("COPY table FROM file" syntax). The source file must have + both "read()" and "readline()" method. + +"copy_to()" + Writes the content of a table *to* a file-like object ("COPY table + TO file" syntax). The target file must have a "write()" method. + +"copy_expert()" + Allows to handle more specific cases and to use all the "COPY" + features available in PostgreSQL. + +Please refer to the documentation of the single methods for details +and examples. + + +Access to PostgreSQL large objects +================================== + +PostgreSQL offers support for large objects, which provide stream- +style access to user data that is stored in a special large-object +structure. They are useful with data values too large to be +manipulated conveniently as a whole. + +Psycopg allows access to the large object using the "lobject" class. +Objects are generated using the "connection.lobject()" factory method. +Data can be retrieved either as bytes or as Unicode strings. + +Psycopg large object support efficient import/export with file system +files using the "lo_import()" and "lo_export()" libpq functions. + + +Two-Phase Commit protocol support +================================= + +New in version 2.3. + +Psycopg exposes the two-phase commit features available since +PostgreSQL 8.1 implementing the *two-phase commit extensions* proposed +by the DB API 2.0. + +The DB API 2.0 model of two-phase commit is inspired by the XA +specification, according to which transaction IDs are formed from +three components: + +* a format ID (non-negative 32 bit integer) + +* a global transaction ID (string not longer than 64 bytes) + +* a branch qualifier (string not longer than 64 bytes) + +For a particular global transaction, the first two components will be +the same for all the resources. Every resource will be assigned a +different branch qualifier. + +According to the DB API 2.0 specification, a transaction ID is created +using the "connection.xid()" method. Once you have a transaction id, a +distributed transaction can be started with "connection.tpc_begin()", +prepared using "tpc_prepare()" and completed using "tpc_commit()" or +"tpc_rollback()". Transaction IDs can also be retrieved from the +database using "tpc_recover()" and completed using the above +"tpc_commit()" and "tpc_rollback()". + +PostgreSQL doesn't follow the XA standard though, and the ID for a +PostgreSQL prepared transaction can be any string up to 200 characters +long. Psycopg's "Xid" objects can represent both XA-style transactions +IDs (such as the ones created by the "xid()" method) and PostgreSQL +transaction IDs identified by an unparsed string. + +The format in which the Xids are converted into strings passed to the +database is the same employed by the PostgreSQL JDBC driver: this +should allow interoperation between tools written in Python and in +Java. For example a recovery tool written in Python would be able to +recognize the components of transactions produced by a Java program. + +For further details see the documentation for the above methods. + + + +The "psycopg2" module content +***************************** + +The module interface respects the standard defined in the DB API 2.0. + +psycopg2.connect(dsn or params [, connection_factory] [, async=0]) + + Create a new database session and return a new "connection" object. + + The connection parameters can be specified either as a string: + + conn = psycopg2.connect("dbname=test user=postgres password=secret") + + or using a set of keyword arguments: + + conn = psycopg2.connect(database="test", user="postgres", password="secret") + + The basic connection parameters are: + + * "dbname" -- the database name (only in dsn string) + + * "database" -- the database name (only as keyword argument) + + * "user" -- user name used to authenticate + + * "password" -- password used to authenticate + + * "host" -- database host address (defaults to UNIX socket if not + provided) + + * "port" -- connection port number (defaults to 5432 if not + provided) + + Any other connection parameter supported by the client + library/server can be passed either in the connection string or as + keyword. See the PostgreSQL documentation for a complete list of + supported parameters. Also note that the same parameters can be + passed to the client library using environment variables. + + Using the *connection_factory* parameter a different class or + connections factory can be specified. It should be a callable + object taking a *dsn* argument. See *Connection and cursor + factories* for details. + + Using *async*=1 an asynchronous connection will be created: see + *Asynchronous support* to know about advantages and limitations. + + Changed in version 2.4.3: any keyword argument is passed to the + connection. Previously only the basic parameters (plus "sslmode") + were supported as keywords. + + DB API extension: The parameters *connection_factory* and *async* + are Psycopg extensions to the DB API 2.0. + +psycopg2.apilevel + + String constant stating the supported DB API level. For "psycopg2" + is "2.0". + +psycopg2.threadsafety + + Integer constant stating the level of thread safety the interface + supports. For "psycopg2" is "2", i.e. threads can share the module + and the connection. See *Thread and process safety* for details. + +psycopg2.paramstyle + + String constant stating the type of parameter marker formatting + expected by the interface. For "psycopg2" is "pyformat". See also + *Passing parameters to SQL queries*. + + +Exceptions +========== + +In compliance with the DB API 2.0, the module makes informations about +errors available through the following exceptions: + +exception exception psycopg2.Warning + + Exception raised for important warnings like data truncations while + inserting, etc. It is a subclass of the Python "StandardError". + +exception exception psycopg2.Error + + Exception that is the base class of all other error exceptions. You + can use this to catch all errors with one single "except" + statement. Warnings are not considered errors and thus not use this + class as base. It is a subclass of the Python "StandardError". + + pgerror + + String representing the error message returned by the backend, + "None" if not available. + + pgcode + + String representing the error code returned by the backend, + "None" if not available. The "errorcodes" module contains + symbolic constants representing PostgreSQL error codes. + + >>> try: + ... cur.execute("SELECT * FROM barf") + ... except Exception, e: + ... pass + + >>> e.pgcode + '42P01' + >>> print e.pgerror + ERROR: relation "barf" does not exist + LINE 1: SELECT * FROM barf + ^ + + cursor + + The cursor the exception was raised from; "None" if not + applicable. + + DB API extension: The "pgerror", "pgcode", and "cursor" attributes + are Psycopg extensions. + +exception exception psycopg2.InterfaceError + + Exception raised for errors that are related to the database + interface rather than the database itself. It is a subclass of + "Error". + +exception exception psycopg2.DatabaseError + + Exception raised for errors that are related to the database. It + is a subclass of "Error". + +exception exception psycopg2.DataError + + Exception raised for errors that are due to problems with the + processed data like division by zero, numeric value out of range, + etc. It is a subclass of "DatabaseError". + +exception exception psycopg2.OperationalError + + Exception raised for errors that are related to the database's + operation and not necessarily under the control of the programmer, + e.g. an unexpected disconnect occurs, the data source name is not + found, a transaction could not be processed, a memory allocation + error occurred during processing, etc. It is a subclass of + "DatabaseError". + +exception exception psycopg2.IntegrityError + + Exception raised when the relational integrity of the database is + affected, e.g. a foreign key check fails. It is a subclass of + "DatabaseError". + +exception exception psycopg2.InternalError + + Exception raised when the database encounters an internal error, + e.g. the cursor is not valid anymore, the transaction is out of + sync, etc. It is a subclass of "DatabaseError". + +exception exception psycopg2.ProgrammingError + + Exception raised for programming errors, e.g. table not found or + already exists, syntax error in the SQL statement, wrong number of + parameters specified, etc. It is a subclass of "DatabaseError". + +exception exception psycopg2.NotSupportedError + + Exception raised in case a method or database API was used which is + not supported by the database, e.g. requesting a "rollback()" on a + connection that does not support transaction or has transactions + turned off. It is a subclass of "DatabaseError". + +DB API extension: Psycopg may raise a few other, more specialized, +exceptions: currently "QueryCanceledError" and +"TransactionRollbackError" are defined. These exceptions are not +exposed by the main "psycopg2" module but are made available by the +"extensions" module. All the additional exceptions are subclasses of +standard DB API 2.0 exceptions, so trapping them specifically is not +required. + +This is the exception inheritance layout: + + "StandardError" + |__ "Warning" + |__ "Error" + |__ "InterfaceError" + |__ "DatabaseError" + |__ "DataError" + |__ "OperationalError" + | |__ "psycopg2.extensions.QueryCanceledError" + | |__ "psycopg2.extensions.TransactionRollbackError" + |__ "IntegrityError" + |__ "InternalError" + |__ "ProgrammingError" + |__ "NotSupportedError" + + +Type Objects and Constructors +============================= + +Note: This section is mostly copied verbatim from the DB API 2.0 + specification. While these objects are exposed in compliance to the + DB API, Psycopg offers very accurate tools to convert data between + Python and PostgreSQL formats. See *Adapting new Python types to + SQL syntax* and *Type casting of SQL types into Python objects* + +Many databases need to have the input in a particular format for +binding to an operation's input parameters. For example, if an input +is destined for a DATE column, then it must be bound to the database +in a particular string format. Similar problems exist for "Row ID" +columns or large binary items (e.g. blobs or RAW columns). This +presents problems for Python since the parameters to the .execute*() +method are untyped. When the database module sees a Python string +object, it doesn't know if it should be bound as a simple CHAR column, +as a raw BINARY item, or as a DATE. + +To overcome this problem, a module must provide the constructors +defined below to create objects that can hold special values. When +passed to the cursor methods, the module can then detect the proper +type of the input parameter and bind it accordingly. + +A Cursor Object's description attribute returns information about each +of the result columns of a query. The type_code must compare equal to +one of Type Objects defined below. Type Objects may be equal to more +than one type code (e.g. DATETIME could be equal to the type codes for +date, time and timestamp columns; see the Implementation Hints below +for details). + +The module exports the following constructors and singletons: + +psycopg2.Date(year, month, day) + + This function constructs an object holding a date value. + +psycopg2.Time(hour, minute, second) + + This function constructs an object holding a time value. + +psycopg2.Timestamp(year, month, day, hour, minute, second) + + This function constructs an object holding a time stamp value. + +psycopg2.DateFromTicks(ticks) + + This function constructs an object holding a date value from the + given ticks value (number of seconds since the epoch; see the + documentation of the standard Python time module for details). + +psycopg2.TimeFromTicks(ticks) + + This function constructs an object holding a time value from the + given ticks value (number of seconds since the epoch; see the + documentation of the standard Python time module for details). + +psycopg2.TimestampFromTicks(ticks) + + This function constructs an object holding a time stamp value from + the given ticks value (number of seconds since the epoch; see the + documentation of the standard Python time module for details). + +psycopg2.Binary(string) + + This function constructs an object capable of holding a binary + (long) string value. + +psycopg2.STRING + + This type object is used to describe columns in a database that are + string-based (e.g. CHAR). + +psycopg2.BINARY + + This type object is used to describe (long) binary columns in a + database (e.g. LONG, RAW, BLOBs). + +psycopg2.NUMBER + + This type object is used to describe numeric columns in a database. + +psycopg2.DATETIME + + This type object is used to describe date/time columns in a + database. + +psycopg2.ROWID + + This type object is used to describe the "Row ID" column in a + database. + + + +The "connection" class +********************** + +class class connection + + Handles the connection to a PostgreSQL database instance. It + encapsulates a database session. + + Connections are created using the factory function "connect()". + + Connections are thread safe and can be shared among many threads. + See *Thread and process safety* for details. + + cursor([name] [, cursor_factory] [, withhold]) + + Return a new "cursor" object using the connection. + + If *name* is specified, the returned cursor will be a *server + side cursor* (also known as *named cursor*). Otherwise it will + be a regular *client side* cursor. By default a "WITHOUT HOLD" + cursor is created; to create a "WITH HOLD" cursor, pass a "True" + value as the *withhold* parameter. See *Server side cursors*. + + The name can be a string not valid as a PostgreSQL identifier: + for example it may start with a digit and contain non- + alphanumeric characters and quotes. + + Changed in version 2.4: previously only valid PostgreSQL + identifiers were accepted as cursor name. + + Warning: It is unsafe to expose the *name* to an untrusted source, for + instance you shouldn't allow *name* to be read from a HTML + form. Consider it as part of the query, not as a query + parameter. + + The *cursor_factory* argument can be used to create non-standard + cursors. The class returned should be a subclass of + "psycopg2.extensions.cursor". See *Connection and cursor + factories* for details. + + DB API extension: The "name" and "cursor_factory" parameters are + Psycopg extensions to the DB API 2.0. + + commit() + + Commit any pending transaction to the database. Psycopg can be + set to perform automatic commits at each operation, see + "set_isolation_level()". + + rollback() + + Roll back to the start of any pending transaction. Closing a + connection without committing the changes first will cause an + implicit rollback to be performed. + + close() + + Close the connection now (rather than whenever "del" is + executed). The connection will be unusable from this point + forward; an "InterfaceError" will be raised if any operation is + attempted with the connection. The same applies to all cursor + objects trying to use the connection. Note that closing a + connection without committing the changes first will cause any + pending change to be discarded as if a "ROLLBACK" was performed + (unless a different isolation level has been selected: see + "set_isolation_level()"). + + Changed in version 2.2: previously an explicit "ROLLBACK" was + issued by Psycopg on "close()". The command could have been sent + to the backend at an inappropriate time, so Psycopg currently + relies on the backend to implicitly discard uncommitted changes. + Some middleware are known to behave incorrectly though when the + connection is closed during a transaction (when "status" is + "STATUS_IN_TRANSACTION"), e.g. PgBouncer reports an "unclean + server" and discards the connection. To avoid this problem you + can ensure to terminate the transaction with a + "commit()"/"rollback()" before closing. + + -[ Exceptions as connection class attributes ]- + + The "connection" also exposes as attributes the same exceptions + available in the "psycopg2" module. See *Exceptions*. + + -[ Two-phase commit support methods ]- + + New in version 2.3. + + See also: + + *Two-Phase Commit protocol support* for an introductory + explanation of these methods. + + Note that PostgreSQL supports two-phase commit since release 8.1: + these methods raise "NotSupportedError" if used with an older + version server. + + xid(format_id, gtrid, bqual) + + Returns a "Xid" instance to be passed to the "tpc_*()" methods + of this connection. The argument types and constraints are + explained in *Two-Phase Commit protocol support*. + + The values passed to the method will be available on the + returned object as the members "format_id", "gtrid", "bqual". + The object also allows accessing to these members and unpacking + as a 3-items tuple. + + tpc_begin(xid) + + Begins a TPC transaction with the given transaction ID *xid*. + + This method should be called outside of a transaction (i.e. + nothing may have executed since the last "commit()" or + "rollback()" and "connection.status" is "STATUS_READY"). + + Furthermore, it is an error to call "commit()" or "rollback()" + within the TPC transaction: in this case a "ProgrammingError" is + raised. + + The *xid* may be either an object returned by the "xid()" method + or a plain string: the latter allows to create a transaction + using the provided string as PostgreSQL transaction id. See also + "tpc_recover()". + + tpc_prepare() + + Performs the first phase of a transaction started with + "tpc_begin()". A "ProgrammingError" is raised if this method is + used outside of a TPC transaction. + + After calling "tpc_prepare()", no statements can be executed + until "tpc_commit()" or "tpc_rollback()" will be called. The + "reset()" method can be used to restore the status of the + connection to "STATUS_READY": the transaction will remain + prepared in the database and will be possible to finish it with + "tpc_commit(xid)" and "tpc_rollback(xid)". + + See also: + + the "PREPARE TRANSACTION" PostgreSQL command. + + tpc_commit([xid]) + + When called with no arguments, "tpc_commit()" commits a TPC + transaction previously prepared with "tpc_prepare()". + + If "tpc_commit()" is called prior to "tpc_prepare()", a single + phase commit is performed. A transaction manager may choose to + do this if only a single resource is participating in the global + transaction. + + When called with a transaction ID *xid*, the database commits + the given transaction. If an invalid transaction ID is + provided, a "ProgrammingError" will be raised. This form should + be called outside of a transaction, and is intended for use in + recovery. + + On return, the TPC transaction is ended. + + See also: + + the "COMMIT PREPARED" PostgreSQL command. + + tpc_rollback([xid]) + + When called with no arguments, "tpc_rollback()" rolls back a TPC + transaction. It may be called before or after "tpc_prepare()". + + When called with a transaction ID *xid*, it rolls back the given + transaction. If an invalid transaction ID is provided, a + "ProgrammingError" is raised. This form should be called + outside of a transaction, and is intended for use in recovery. + + On return, the TPC transaction is ended. + + See also: + + the "ROLLBACK PREPARED" PostgreSQL command. + + tpc_recover() + + Returns a list of "Xid" representing pending transactions, + suitable for use with "tpc_commit()" or "tpc_rollback()". + + If a transaction was not initiated by Psycopg, the returned Xids + will have attributes "format_id" and "bqual" set to "None" and + the "gtrid" set to the PostgreSQL transaction ID: such Xids are + still usable for recovery. Psycopg uses the same algorithm of + the PostgreSQL JDBC driver to encode a XA triple in a string, so + transactions initiated by a program using such driver should be + unpacked correctly. + + Xids returned by "tpc_recover()" also have extra attributes + "prepared", "owner", "database" populated with the values read + from the server. + + See also: + + the "pg_prepared_xacts" system view. + + DB API extension: The above methods are the only ones defined by + the DB API 2.0 protocol. The Psycopg connection objects exports the + following additional methods and attributes. + + closed + + Read-only attribute reporting whether the database connection is + open (0) or closed (1). + + cancel() + + Cancel the current database operation. + + The method interrupts the processing of the current operation. + If no query is being executed, it does nothing. You can call + this function from a different thread than the one currently + executing a database operation, for instance if you want to + cancel a long running query if a button is pushed in the UI. + Interrupting query execution will cause the cancelled method to + raise a "QueryCanceledError". Note that the termination of the + query is not guaranteed to succeed: see the documentation for + "PQcancel()". + + New in version 2.3. + + reset() + + Reset the connection to the default. + + The method rolls back an eventual pending transaction and + executes the PostgreSQL "RESET" and "SET SESSION AUTHORIZATION" + to revert the session to the default values. A two-phase commit + transaction prepared using "tpc_prepare()" will remain in the + database available for recover. + + New in version 2.0.12. + + dsn + + Read-only string containing the connection string used by the + connection. + + set_session([isolation_level,] [readonly,] [deferrable,] [autocommit]) + + Set one or more parameters for the next transactions or + statements in the current session. See "SET TRANSACTION" for + further details. + + Parameters: + * **isolation_level** -- set the isolation level for the next + transactions/statements. The value can be one of the + *constants* defined in the "extensions" module or one of + the literal values "READ UNCOMMITTED", "READ COMMITTED", + "REPEATABLE READ", "SERIALIZABLE". + + * **readonly** -- if "True", set the connection to read only; + read/write if "False". + + * **deferrable** -- if "True", set the connection to + deferrable; non deferrable if "False". Only available from + PostgreSQL 9.1. + + * **autocommit** -- switch the connection to autocommit mode: + not a PostgreSQL session setting but an alias for setting + the "autocommit" attribute. + + The parameters *isolation_level*, *readonly* and *deferrable* + also accept the string "DEFAULT" as a value: the effect is to + reset the parameter to the server default. + + The function must be invoked with no transaction in progress. At + every function invocation, only the specified parameters are + changed. + + The default for the values are defined by the server + configuration: see values for "default_transaction_isolation", + "default_transaction_read_only", + "default_transaction_deferrable". + + Note: There is currently no builtin method to read the current value + for the parameters: use "SHOW default_transaction_..." to read + the values from the backend. + + New in version 2.4.2. + + autocommit + + Read/write attribute: if "True", no transaction is handled by + the driver and every statement sent to the backend has immediate + effect; if "False" a new transaction is started at the first + command execution: the methods "commit()" or "rollback()" must + be manually invoked to terminate the transaction. + + The autocommit mode is useful to execute commands requiring to + be run outside a transaction, such as "CREATE DATABASE" or + "VACUUM". + + The default is "False" (manual commit) as per DBAPI + specification. + + Warning: By default, any query execution, including a simple "SELECT" + will start a transaction: for long-running programs, if no + further action is taken, the session will remain "idle in + transaction", a condition non desiderable for several reasons + (locks are held by the session, tables bloat...). For long + lived scripts, either ensure to terminate a transaction as + soon as possible or use an autocommit connection. + + New in version 2.4.2. + + isolation_level + + set_isolation_level(level) + + Note: From version 2.4.2, "set_session()" and "autocommit", offer + finer control on the transaction characteristics. + + Read or set the transaction isolation level for the current + session. The level defines the different phenomena that can + happen in the database between concurrent transactions. + + The value set or read is an integer: symbolic constants are + defined in the module "psycopg2.extensions": see *Isolation + level constants* for the available values. + + The default level is "READ COMMITTED": at this level a + transaction is automatically started the first time a database + command is executed. If you want an *autocommit* mode, switch + to "ISOLATION_LEVEL_AUTOCOMMIT" before executing any command: + + >>> conn.set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT) + + See also *Transactions control*. + + encoding + + set_client_encoding(enc) + + Read or set the client encoding for the current session. The + default is the encoding defined by the database. It should be + one of the characters set supported by PostgreSQL + + notices + + A list containing all the database messages sent to the client + during the session. + + >>> cur.execute("CREATE TABLE foo (id serial PRIMARY KEY);") + >>> pprint(conn.notices) + ['NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "foo_pkey" for table "foo"\n', + 'NOTICE: CREATE TABLE will create implicit sequence "foo_id_seq" for serial column "foo.id"\n'] + + To avoid a leak in case excessive notices are generated, only + the last 50 messages are kept. + + You can configure what messages to receive using PostgreSQL + logging configuration parameters such as "log_statement", + "client_min_messages", "log_min_duration_statement" etc. + + notifies + + List of "Notify" objects containing asynchronous notifications + received by the session. + + For other details see *Asynchronous notifications*. + + Changed in version 2.3: Notifications are instances of the + "Notify" object. Previously the list was composed by 2 items + tuples "(*pid*,*channel*)" and the payload was not accessible. + To keep backward compatibility, "Notify" objects can still be + accessed as 2 items tuples. + + get_backend_pid() + + Returns the process ID (PID) of the backend server process + handling this connection. + + Note that the PID belongs to a process executing on the database + server host, not the local host! + + See also: + + libpq docs for PQbackendPID() for details. + + New in version 2.0.8. + + get_parameter_status(parameter) + + Look up a current parameter setting of the server. + + Potential values for "parameter" are: "server_version", + "server_encoding", "client_encoding", "is_superuser", + "session_authorization", "DateStyle", "TimeZone", + "integer_datetimes", and "standard_conforming_strings". + + If server did not report requested parameter, return "None". + + See also: + + libpq docs for PQparameterStatus() for details. + + New in version 2.0.12. + + get_transaction_status() + + Return the current session transaction status as an integer. + Symbolic constants for the values are defined in the module + "psycopg2.extensions": see *Transaction status constants* for + the available values. + + See also: + + libpq docs for PQtransactionStatus() for details. + + protocol_version + + A read-only integer representing frontend/backend protocol being + used. Currently Psycopg supports only protocol 3, which allows + connection to PostgreSQL server from version 7.4. Psycopg + versions previous than 2.3 support both protocols 2 and 3. + + See also: + + libpq docs for PQprotocolVersion() for details. + + New in version 2.0.12. + + server_version + + A read-only integer representing the backend version. + + The number is formed by converting the major, minor, and + revision numbers into two-decimal-digit numbers and appending + them together. For example, version 8.1.5 will be returned as + "80105". + + See also: + + libpq docs for PQserverVersion() for details. + + New in version 2.0.12. + + status + + A read-only integer representing the status of the connection. + Symbolic constants for the values are defined in the module + "psycopg2.extensions": see *Connection status constants* for the + available values. + + lobject([oid[, mode[, new_oid[, new_file[, lobject_factory]]]]]) + + Return a new database large object as a "lobject" instance. + + See *Access to PostgreSQL large objects* for an overview. + + Parameters: + * **oid** -- The OID of the object to read or write. 0 to + create a new large object and and have its OID assigned + automatically. + + * **mode** -- Access mode to the object, see below. + + * **new_oid** -- Create a new object using the specified OID. + The function raises "OperationalError" if the OID is + already in use. Default is 0, meaning assign a new one + automatically. + + * **new_file** -- The name of a file to be imported in the + the database (using the "lo_import()" function) + + * **lobject_factory** -- Subclass of "lobject" to be + instantiated. + + Available values for *mode* are: + + +---------+-------------------------------------------------------------------------------------------------------------------+ + | *mode* | meaning | + +=========+===================================================================================================================+ + | "r" | Open for read only | + +---------+-------------------------------------------------------------------------------------------------------------------+ + | "w" | Open for write only | + +---------+-------------------------------------------------------------------------------------------------------------------+ + | "rw" | Open for read/write | + +---------+-------------------------------------------------------------------------------------------------------------------+ + | "n" | Don't open the file | + +---------+-------------------------------------------------------------------------------------------------------------------+ + | "b" | Don't decode read data (return data as "str" in Python 2 or "bytes" in Python 3) | + +---------+-------------------------------------------------------------------------------------------------------------------+ + | "t" | Decode read data according to "connection.encoding" (return data as "unicode" in Python 2 or "str" in Python 3) | + +---------+-------------------------------------------------------------------------------------------------------------------+ + + "b" and "t" can be specified together with a read/write mode. If + neither "b" nor "t" is specified, the default is "b" in Python 2 + and "t" in Python 3. + + New in version 2.0.8. + + Changed in version 2.4: added "b" and "t" mode and unicode + support. + + -[ Methods related to asynchronous support. ]- + + New in version 2.2.0. + + See also: + + *Asynchronous support* and *Support to coroutine libraries*. + + async + + Read only attribute: 1 if the connection is asynchronous, 0 + otherwise. + + poll() + + Used during an asynchronous connection attempt, or when a cursor + is executing a query on an asynchronous connection, make + communication proceed if it wouldn't block. + + Return one of the constants defined in *Poll constants*. If it + returns "POLL_OK" then the connection has been estabilished or + the query results are available on the client. Otherwise wait + until the file descriptor returned by "fileno()" is ready to + read or to write, as explained in *Asynchronous support*. + "poll()" should be also used by the function installed by + "set_wait_callback()" as explained in *Support to coroutine + libraries*. + + "poll()" is also used to receive asynchronous notifications from + the database: see *Asynchronous notifications* from further + details. + + fileno() + + Return the file descriptor underlying the connection: useful to + read its status during asynchronous communication. + + isexecuting() + + Return "True" if the connection is executing an asynchronous + operation. + + + +The "cursor" class +****************** + +class class cursor + + Allows Python code to execute PostgreSQL command in a database + session. Cursors are created by the "connection.cursor()" method: + they are bound to the connection for the entire lifetime and all + the commands are executed in the context of the database session + wrapped by the connection. + + Cursors created from the same connection are not isolated, i.e., + any changes done to the database by a cursor are immediately + visible by the other cursors. Cursors created from different + connections can or can not be isolated, depending on the + connections' *isolation level*. See also "rollback()" and + "commit()" methods. + + Cursors are *not* thread safe: a multithread application can create + many cursors from the same connection and should use each cursor + from a single thread. See *Thread and process safety* for details. + + description + + This read-only attribute is a sequence of 7-item sequences. + + Each of these sequences is a named tuple (a regular tuple if + "collections.namedtuple()" is not available) containing + information describing one result column: + + 1. "name": the name of the column returned. + + 2. "type_code": the PostgreSQL OID of the column. You can use + the "pg_type" system table to get more informations about the + type. This is the value used by Psycopg to decide what Python + type use to represent the value. See also *Type casting of + SQL types into Python objects*. + + 3. "display_size": the actual length of the column in bytes. + Obtaining this value is computationally intensive, so it is + always "None" unless the "PSYCOPG_DISPLAY_SIZE" parameter is + set at compile time. See also PQgetlength. + + 4. "internal_size": the size in bytes of the column associated + to this column on the server. Set to a negative value for + variable-size types See also PQfsize. + + 5. "precision": total number of significant digits in columns of + type "NUMERIC". "None" for other types. + + 6. "scale": count of decimal digits in the fractional part in + columns of type "NUMERIC". "None" for other types. + + 7. "null_ok": always "None" as not easy to retrieve from the + libpq. + + This attribute will be "None" for operations that do not return + rows or if the cursor has not had an operation invoked via the + "execute*()" methods yet. + + Changed in version 2.4: if possible, columns descriptions are + named tuple instead of regular tuples. + + close() + + Close the cursor now (rather than whenever "del" is executed). + The cursor will be unusable from this point forward; an + "InterfaceError" will be raised if any operation is attempted + with the cursor. + + closed + + Read-only boolean attribute: specifies if the cursor is closed + ("True") or not ("False"). + + DB API extension: The "closed" attribute is a Psycopg extension + to the DB API 2.0. + + New in version 2.0.7. + + connection + + Read-only attribute returning a reference to the "connection" + object on which the cursor was created. + + name + + Read-only attribute containing the name of the cursor if it was + creates as named cursor by "connection.cursor()", or "None" if + it is a client side cursor. See *Server side cursors*. + + DB API extension: The "name" attribute is a Psycopg extension to + the DB API 2.0. + + withhold + + Read/write attribute: specifies if a named cursor lifetime + should extend outside of the current transaction, i.e., it is + possible to fetch from the cursor even after a + "commection.commit()" (but not after a "connection.rollback()"). + See *Server side cursors* + + New in version 2.4.3. + + DB API extension: The "withhold" attribute is a Psycopg + extension to the DB API 2.0. + + -[ Commands execution methods ]- + + execute(operation[, parameters]) + + Prepare and execute a database operation (query or command). + + Parameters may be provided as sequence or mapping and will be + bound to variables in the operation. Variables are specified + either with positional ("%s") or named ("%(*name*)s") + placeholders. See *Passing parameters to SQL queries*. + + The method returns "None". If a query was executed, the returned + values can be retrieved using "fetch*()" methods. + + executemany(operation, seq_of_parameters) + + Prepare a database operation (query or command) and then execute + it against all parameter tuples or mappings found in the + sequence "seq_of_parameters". + + The function is mostly useful for commands that update the + database: any result set returned by the query is discarded. + + Parameters are bounded to the query using the same rules + described in the "execute()" method. + + callproc(procname[, parameters]) + + Call a stored database procedure with the given name. The + sequence of parameters must contain one entry for each argument + that the procedure expects. The result of the call is returned + as modified copy of the input sequence. Input parameters are + left untouched, output and input/output parameters replaced with + possibly new values. + + The procedure may also provide a result set as output. This must + then be made available through the standard "fetch*()" methods. + + mogrify(operation[, parameters]) + + Return a query string after arguments binding. The string + returned is exactly the one that would be sent to the database + running the "execute()" method or similar. + + >>> cur.mogrify("INSERT INTO test (num, data) VALUES (%s, %s)", (42, 'bar')) + "INSERT INTO test (num, data) VALUES (42, E'bar')" + + DB API extension: The "mogrify()" method is a Psycopg extension + to the DB API 2.0. + + setinputsizes(sizes) + + This method is exposed in compliance with the DB API 2.0. It + currently does nothing but it is safe to call it. + + -[ Results retrieval methods ]- + + The following methods are used to read data from the database after + an "execute()" call. + + Note: "cursor" objects are iterable, so, instead of calling explicitly + "fetchone()" in a loop, the object itself can be used: + + >>> cur.execute("SELECT * FROM test;") + >>> for record in cur: + ... print record + ... + (1, 100, "abc'def") + (2, None, 'dada') + (3, 42, 'bar') + + Changed in version 2.4: iterating over a *named cursor* fetches + "itersize" records at time from the backend. Previously only one + record was fetched per roundtrip, resulting in a large overhead. + + fetchone() + + Fetch the next row of a query result set, returning a single + tuple, or "None" when no more data is available: + + >>> cur.execute("SELECT * FROM test WHERE id = %s", (3,)) + >>> cur.fetchone() + (3, 42, 'bar') + + A "ProgrammingError" is raised if the previous call to + "execute*()" did not produce any result set or no call was + issued yet. + + fetchmany([size=cursor.arraysize]) + + Fetch the next set of rows of a query result, returning a list + of tuples. An empty list is returned when no more rows are + available. + + The number of rows to fetch per call is specified by the + parameter. If it is not given, the cursor's "arraysize" + determines the number of rows to be fetched. The method should + try to fetch as many rows as indicated by the size parameter. If + this is not possible due to the specified number of rows not + being available, fewer rows may be returned: + + >>> cur.execute("SELECT * FROM test;") + >>> cur.fetchmany(2) + [(1, 100, "abc'def"), (2, None, 'dada')] + >>> cur.fetchmany(2) + [(3, 42, 'bar')] + >>> cur.fetchmany(2) + [] + + A "ProgrammingError" is raised if the previous call to + "execute*()" did not produce any result set or no call was + issued yet. + + Note there are performance considerations involved with the size + parameter. For optimal performance, it is usually best to use + the "arraysize" attribute. If the size parameter is used, then + it is best for it to retain the same value from one + "fetchmany()" call to the next. + + fetchall() + + Fetch all (remaining) rows of a query result, returning them as + a list of tuples. An empty list is returned if there is no more + record to fetch. + + >>> cur.execute("SELECT * FROM test;") + >>> cur.fetchall() + [(1, 100, "abc'def"), (2, None, 'dada'), (3, 42, 'bar')] + + A "ProgrammingError" is raised if the previous call to + "execute*()" did not produce any result set or no call was + issued yet. + + scroll(value[, mode='relative']) + + Scroll the cursor in the result set to a new position according + to mode. + + If "mode" is "relative" (default), value is taken as offset to + the current position in the result set, if set to "absolute", + value states an absolute target position. + + If the scroll operation would leave the result set, a + "ProgrammingError" is raised and the cursor position is not + changed. + + The method can be used both for client-side cursors and *server- + side cursors*. + + Note: According to the DB API 2.0, the exception raised for a cursor + out of bound should have been "IndexError". The best option + is probably to catch both exceptions in your code: + + try: + cur.scroll(1000 * 1000) + except (ProgrammingError, IndexError), exc: + deal_with_it(exc) + + arraysize + + This read/write attribute specifies the number of rows to fetch + at a time with "fetchmany()". It defaults to 1 meaning to fetch + a single row at a time. + + itersize + + Read/write attribute specifying the number of rows to fetch from + the backend at each network roundtrip during *iteration* on a + *named cursor*. The default is 2000. + + New in version 2.4. + + DB API extension: The "itersize" attribute is a Psycopg + extension to the DB API 2.0. + + rowcount + + This read-only attribute specifies the number of rows that the + last "execute*()" produced (for DQL (Data Query Language) + statements like "SELECT") or affected (for DML (Data + Manipulation Language) statements like "UPDATE" or "INSERT"). + + The attribute is -1 in case no "execute*()" has been performed + on the cursor or the row count of the last operation if it can't + be determined by the interface. + + Note: The DB API 2.0 interface reserves to redefine the latter case + to have the object return "None" instead of -1 in future + versions of the specification. + + rownumber + + This read-only attribute provides the current 0-based index of + the cursor in the result set or "None" if the index cannot be + determined. + + The index can be seen as index of the cursor in a sequence (the + result set). The next fetch operation will fetch the row indexed + by "rownumber" in that sequence. + + lastrowid + + This read-only attribute provides the OID of the last row + inserted by the cursor. If the table wasn't created with OID + support or the last operation is not a single record insert, the + attribute is set to "None". + + Note: PostgreSQL currently advices to not create OIDs on the tables + and the default for "CREATE TABLE" is to not support them. The + "INSERT ... RETURNING" syntax available from PostgreSQL 8.3 + allows more flexibility. + + query + + Read-only attribute containing the body of the last query sent + to the backend (including bound arguments). "None" if no query + has been executed yet: + + >>> cur.execute("INSERT INTO test (num, data) VALUES (%s, %s)", (42, 'bar')) + >>> cur.query + "INSERT INTO test (num, data) VALUES (42, E'bar')" + + DB API extension: The "query" attribute is a Psycopg extension + to the DB API 2.0. + + statusmessage + + Read-only attribute containing the message returned by the last + command: + + >>> cur.execute("INSERT INTO test (num, data) VALUES (%s, %s)", (42, 'bar')) + >>> cur.statusmessage + 'INSERT 0 1' + + DB API extension: The "statusmessage" attribute is a Psycopg + extension to the DB API 2.0. + + cast(oid, s) + + Convert a value from the PostgreSQL string representation to a + Python object. + + Use the most specific of the typecasters registered by + "register_type()". + + New in version 2.4. + + DB API extension: The "cast()" method is a Psycopg extension to + the DB API 2.0. + + tzinfo_factory + + The time zone factory used to handle data types such as + "TIMESTAMP WITH TIME ZONE". It should be a "tzinfo" object. A + few implementations are available in the "psycopg2.tz" module. + + nextset() + + This method is not supported (PostgreSQL does not have multiple + data sets) and will raise a "NotSupportedError" exception. + + setoutputsize(size[, column]) + + This method is exposed in compliance with the DB API 2.0. It + currently does nothing but it is safe to call it. + + -[ COPY-related methods ]- + + DB API extension: The "COPY" command is a PostgreSQL extension to + the SQL standard. As such, its support is a Psycopg extension to + the DB API 2.0. + + copy_from(file, table, sep='\t', null='\\N', size=8192, columns=None) + + Read data *from* the file-like object *file* appending them to + the table named *table*. See *Using COPY TO and COPY FROM* for + an overview. + + Parameters: + * **file** -- file-like object to read data from. It must + have both "read()" and "readline()" methods. + + * **table** -- name of the table to copy data into. + + * **sep** -- columns separator expected in the file. Defaults + to a tab. + + * **null** -- textual representation of "NULL" in the file. + The default is the two characters string "\N". + + * **size** -- size of the buffer used to read from the file. + + * **columns** -- iterable with name of the columns to import. + The length and types should match the content of the file + to read. If not specified, it is assumed that the entire + table matches the file structure. + + Example: + + >>> f = StringIO("42\tfoo\n74\tbar\n") + >>> cur.copy_from(f, 'test', columns=('num', 'data')) + >>> cur.execute("select * from test where id > 5;") + >>> cur.fetchall() + [(6, 42, 'foo'), (7, 74, 'bar')] + + Changed in version 2.0.6: added the *columns* parameter. + + Changed in version 2.4: data read from files implementing the + "io.TextIOBase" interface are encoded in the connection + "encoding" when sent to the backend. + + copy_to(file, table, sep='\t', null='\\N', columns=None) + + Write the content of the table named *table* *to* the file-like + object *file*. See *Using COPY TO and COPY FROM* for an + overview. + + Parameters: + * **file** -- file-like object to write data into. It must + have a "write()" method. + + * **table** -- name of the table to copy data from. + + * **sep** -- columns separator expected in the file. Defaults + to a tab. + + * **null** -- textual representation of "NULL" in the file. + The default is the two characters string "\N". + + * **columns** -- iterable with name of the columns to export. + If not specified, export all the columns. + + Example: + + >>> cur.copy_to(sys.stdout, 'test', sep="|") + 1|100|abc'def + 2|\N|dada + ... + + Changed in version 2.0.6: added the *columns* parameter. + + Changed in version 2.4: data sent to files implementing the + "io.TextIOBase" interface are decoded in the connection + "encoding" when read from the backend. + + copy_expert(sql, file, size=8192) + + Submit a user-composed "COPY" statement. The method is useful to + handle all the parameters that PostgreSQL makes available (see + "COPY" command documentation). + + Parameters: + * **sql** -- the "COPY" statement to execute. + + * **file** -- a file-like object; must be a readable file for + "COPY FROM" or an writable file for "COPY TO". + + * **size** -- size of the read buffer to be used in "COPY + FROM". + + Example: + + >>> cur.copy_expert("COPY test TO STDOUT WITH CSV HEADER", sys.stdout) + id,num,data + 1,100,abc'def + 2,,dada + ... + + New in version 2.0.6. + + Changed in version 2.4: files implementing the "io.TextIOBase" + interface are dealt with using Unicode data instead of bytes. + + + +More advanced topics +******************** + + +Connection and cursor factories +=============================== + +Psycopg exposes two new-style classes that can be sub-classed and +expanded to adapt them to the needs of the programmer: +"psycopg2.extensions.cursor" and "psycopg2.extensions.connection". +The "connection" class is usually sub-classed only to provide an easy +way to create customized cursors but other uses are possible. "cursor" +is much more interesting, because it is the class where query +building, execution and result type-casting into Python variables +happens. + +An example of cursor subclass performing logging is: + + import psycopg2 + import psycopg2.extensions + import logging + + class LoggingCursor(psycopg2.extensions.cursor): + def execute(self, sql, args=None): + logger = logging.getLogger('sql_debug') + logger.info(self.mogrify(sql, args)) + + try: + psycopg2.extensions.cursor.execute(self, sql, args) + except Exception, exc: + logger.error("%s: %s" % (exc.__class__.__name__, exc)) + raise + + conn = psycopg2.connect(DSN) + cur = conn.cursor(cursor_factory=LoggingCursor) + cur.execute("INSERT INTO mytable VALUES (%s, %s, %s);", + (10, 20, 30)) + + +Adapting new Python types to SQL syntax +======================================= + +Any Python class or type can be adapted to an SQL string. Adaptation +mechanism is similar to the Object Adaptation proposed in the **PEP +246** and is exposed by the "psycopg2.extensions.adapt()" function. + +The "execute()" method adapts its arguments to the "ISQLQuote" +protocol. Objects that conform to this protocol expose a +"getquoted()" method returning the SQL representation of the object as +a string (the method must return "bytes" in Python 3). Optionally the +conform object may expose a "prepare()" method. + +There are two basic ways to have a Python object adapted to SQL: + +* the object itself is conform, or knows how to make itself conform. + Such object must expose a "__conform__()" method that will be called + with the protocol object as argument. The object can check that the + protocol is "ISQLQuote", in which case it can return "self" (if the + object also implements "getquoted()") or a suitable wrapper object. + This option is viable if you are the author of the object and if the + object is specifically designed for the database (i.e. having + Psycopg as a dependency and polluting its interface with the + required methods doesn't bother you). For a simple example you can + take a look at the source code for the "psycopg2.extras.Inet" + object. + +* If implementing the "ISQLQuote" interface directly in the object is + not an option (maybe because the object to adapt comes from a third + party library), you can use an *adaptation function*, taking the + object to be adapted as argument and returning a conforming object. + The adapter must be registered via the "register_adapter()" + function. A simple example wrapper is + "psycopg2.extras.UUID_adapter" used by the "register_uuid()" + function. + +A convenient object to write adapters is the "AsIs" wrapper, whose +"getquoted()" result is simply the "str()"ing conversion of the +wrapped object. + +Example: mapping of a "Point" class into the "point" PostgreSQL +geometric type: + + >>> from psycopg2.extensions import adapt, register_adapter, AsIs + + >>> class Point(object): + ... def __init__(self, x, y): + ... self.x = x + ... self.y = y + + >>> def adapt_point(point): + ... return AsIs("'(%s, %s)'" % (adapt(point.x), adapt(point.y))) + + >>> register_adapter(Point, adapt_point) + + >>> cur.execute("INSERT INTO atable (apoint) VALUES (%s)", + ... (Point(1.23, 4.56),)) + +The above function call results in the SQL command: + + INSERT INTO atable (apoint) VALUES ('(1.23, 4.56)'); + + +Type casting of SQL types into Python objects +============================================= + +PostgreSQL objects read from the database can be adapted to Python +objects through an user-defined adapting function. An adapter +function takes two arguments: the object string representation as +returned by PostgreSQL and the cursor currently being read, and should +return a new Python object. For example, the following function +parses the PostgreSQL "point" representation into the previously +defined "Point" class: + +>>> def cast_point(value, cur): +... if value is None: +... return None +... +... # Convert from (f1, f2) syntax using a regular expression. +... m = re.match(r"\(([^)]+),([^)]+)\)", value) +... if m: +... return Point(float(m.group(1)), float(m.group(2))) +... else: +... raise InterfaceError("bad point representation: %r" % value) + +In order to create a mapping from a PostgreSQL type (either standard +or user-defined), its OID must be known. It can be retrieved either by +the second column of the "cursor.description": + +>>> cur.execute("SELECT NULL::point") +>>> point_oid = cur.description[0][1] +>>> point_oid +600 + +or by querying the system catalog for the type name and namespace (the +namespace for system objects is "pg_catalog"): + +>>> cur.execute(""" +... SELECT pg_type.oid +... FROM pg_type JOIN pg_namespace +... ON typnamespace = pg_namespace.oid +... WHERE typname = %(typename)s +... AND nspname = %(namespace)s""", +... {'typename': 'point', 'namespace': 'pg_catalog'}) +>>> point_oid = cur.fetchone()[0] +>>> point_oid +600 + +After you know the object OID, you can create and register the new +type: + +>>> POINT = psycopg2.extensions.new_type((point_oid,), "POINT", cast_point) +>>> psycopg2.extensions.register_type(POINT) + +The "new_type()" function binds the object OIDs (more than one can be +specified) to the adapter function. "register_type()" completes the +spell. Conversion is automatically performed when a column whose type +is a registered OID is read: + +>>> cur.execute("SELECT '(10.2,20.3)'::point") +>>> point = cur.fetchone()[0] +>>> print type(point), point.x, point.y + 10.2 20.3 + +A typecaster created by "new_type()" can be also used with +"new_array_type()" to create a typecaster converting a PostgreSQL +array into a Python list. + + +Asynchronous notifications +========================== + +Psycopg allows asynchronous interaction with other database sessions +using the facilities offered by PostgreSQL commands "LISTEN" and +"NOTIFY". Please refer to the PostgreSQL documentation for examples +about how to use this form of communication. + +Notifications are instances of the "Notify" object made available upon +reception in the "connection.notifies" list. Notifications can be sent +from Python code simply executing a "NOTIFY" command in an "execute()" +call. + +Because of the way sessions interact with notifications (see "NOTIFY" +documentation), you should keep the connection in "autocommit" mode if +you wish to receive or send notifications in a timely manner. + +Notifications are received after every query execution. If the user is +interested in receiving notifications but not in performing any query, +the "poll()" method can be used to check for new messages without +wasting resources. + +A simple application could poll the connection from time to time to +check if something new has arrived. A better strategy is to use some +I/O completion function such as "select()" to sleep until awaken from +the kernel when there is some data to read on the connection, thereby +using no CPU unless there is something to read: + + import select + import psycopg2 + import psycopg2.extensions + + conn = psycopg2.connect(DSN) + conn.set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT) + + curs = conn.cursor() + curs.execute("LISTEN test;") + + print "Waiting for notifications on channel 'test'" + while 1: + if select.select([conn],[],[],5) == ([],[],[]): + print "Timeout" + else: + conn.poll() + while conn.notifies: + notify = conn.notifies.pop() + print "Got NOTIFY:", notify.pid, notify.channel, notify.payload + +Running the script and executing a command such as "NOTIFY test, +'hello'" in a separate **psql** shell, the output may look similar to: + + Waiting for notifications on channel 'test' + Timeout + Timeout + Got NOTIFY: 6535 test hello + Timeout + ... + +Note that the payload is only available from PostgreSQL 9.0: +notifications received from a previous version server will have the +"payload" attribute set to the empty string. + +Changed in version 2.3: Added "Notify" object and handling +notification payload. + + +Asynchronous support +==================== + +New in version 2.2.0. + +Psycopg can issue asynchronous queries to a PostgreSQL database. An +asynchronous communication style is established passing the parameter +*async*=1 to the "connect()" function: the returned connection will +work in *asynchronous mode*. + +In asynchronous mode, a Psycopg connection will rely on the caller to +poll the socket file descriptor, checking if it is ready to accept +data or if a query result has been transferred and is ready to be read +on the client. The caller can use the method "fileno()" to get the +connection file descriptor and "poll()" to make communication proceed +according to the current connection state. + +The following is an example loop using methods "fileno()" and "poll()" +together with the Python "select()" function in order to carry on +asynchronous operations with Psycopg: + + def wait(conn): + while 1: + state = conn.poll() + if state == psycopg2.extensions.POLL_OK: + break + elif state == psycopg2.extensions.POLL_WRITE: + select.select([], [conn.fileno()], []) + elif state == psycopg2.extensions.POLL_READ: + select.select([conn.fileno()], [], []) + else: + raise psycopg2.OperationalError("poll() returned %s" % state) + +The above loop of course would block an entire application: in a real +asynchronous framework, "select()" would be called on many file +descriptors waiting for any of them to be ready. Nonetheless the +function can be used to connect to a PostgreSQL server only using +nonblocking commands and the connection obtained can be used to +perform further nonblocking queries. After "poll()" has returned +"POLL_OK", and thus "wait()" has returned, the connection can be +safely used: + +>>> aconn = psycopg2.connect(database='test', async=1) +>>> wait(aconn) +>>> acurs = aconn.cursor() + +Note that there are a few other requirements to be met in order to +have a completely non-blocking connection attempt: see the libpq +documentation for "PQconnectStart()". + +The same loop should be also used to perform nonblocking queries: +after sending a query via "execute()" or "callproc()", call "poll()" +on the connection available from "cursor.connection" until it returns +"POLL_OK", at which point the query has been completely sent to the +server and, if it produced data, the results have been transferred to +the client and available using the regular cursor methods: + +>>> acurs.execute("SELECT pg_sleep(5); SELECT 42;") +>>> wait(acurs.connection) +>>> acurs.fetchone()[0] +42 + +When an asynchronous query is being executed, +"connection.isexecuting()" returns "True". Two cursors can't execute +concurrent queries on the same asynchronous connection. + +There are several limitations in using asynchronous connections: the +connection is always in "autocommit" mode and it is not possible to +change it. So a transaction is not implicitly started at the first +query and is not possible to use methods "commit()" and "rollback()": +you can manually control transactions using "execute()" to send +database commands such as "BEGIN", "COMMIT" and "ROLLBACK". Similarly +"set_session()" can't be used but it is still possible to invoke the +"SET" command with the proper "default_transaction_..." parameter. + +With asynchronous connections it is also not possible to use +"set_client_encoding()", "executemany()", *large objects*, *named +cursors*. + +*COPY commands* are not supported either in asynchronous mode, but +this will be probably implemented in a future release. + + +Support to coroutine libraries +============================== + +New in version 2.2.0. + +Psycopg can be used together with coroutine-based libraries, and +participate to cooperative multithreading. + +Coroutine-based libraries (such as Eventlet or gevent) can usually +patch the Python standard library in order to enable a coroutine +switch in the presence of blocking I/O: the process is usually +referred as making the system *green*, in reference to the green +threads. + +Because Psycopg is a C extension module, it is not possible for +coroutine libraries to patch it: Psycopg instead enables cooperative +multithreading by allowing the registration of a *wait callback* using +the "psycopg2.extensions.set_wait_callback()" function. When a wait +callback is registered, Psycopg will use libpq non-blocking calls +instead of the regular blocking ones, and will delegate to the +callback the responsibility to wait for the socket to become readable +or writable. + +Working this way, the caller does not have the complete freedom to +schedule the socket check whenever they want as with an *asynchronous +connection*, but has the advantage of maintaining a complete DB API +2.0 semantics: from the point of view of the end user, all Psycopg +functions and objects will work transparently in the coroutine +environment (blocking the calling green thread and giving other green +threads the possibility to be scheduled), allowing non modified code +and third party libraries (such as SQLAlchemy) to be used in +coroutine-based programs. + +Warning: Psycopg connections are not *green thread safe* and can't be used + concurrently by different green threads. Trying to execute more than + one command at time using one cursor per thread will result in an + error (or a deadlock on versions before 2.4.2).Therefore, + programmers are advised to either avoid sharing connections between + coroutines or to use a library-friendly lock to synchronize shared + connections, e.g. for pooling. + +Coroutine libraries authors should provide a callback implementation +(and possibly a method to register it) to make Psycopg as green as +they want. An example callback (using "select()" to block) is provided +as "psycopg2.extras.wait_select()": it boils down to something similar +to: + + def wait_select(conn): + while 1: + state = conn.poll() + if state == extensions.POLL_OK: + break + elif state == extensions.POLL_READ: + select.select([conn.fileno()], [], []) + elif state == extensions.POLL_WRITE: + select.select([], [conn.fileno()], []) + else: + raise OperationalError("bad state from poll: %s" % state) + +Providing callback functions for the single coroutine libraries is out +of psycopg2 scope, as the callback can be tied to the libraries' +implementation details. You can check the psycogreen project for +further informations and resources about the topic. + +Warning: *COPY commands* are currently not supported when a wait callback is + registered, but they will be probably implemented in a future + release.*Large objects* are not supported either: they are not + compatible with asynchronous connections. + + + +"psycopg2.extensions" -- Extensions to the DB API +************************************************* + +The module contains a few objects and function extending the minimum +set of functionalities defined by the DB API 2.0. + +class class psycopg2.extensions.connection + + Is the class usually returned by the "connect()" function. It is + exposed by the "extensions" module in order to allow subclassing to + extend its behaviour: the subclass should be passed to the + "connect()" function using the "connection_factory" parameter. See + also *Connection and cursor factories*. + + Subclasses should have constructor signature "(*dsn*, *async*=0)". + + For a complete description of the class, see "connection". + +class class psycopg2.extensions.cursor + + It is the class usually returnded by the "connection.cursor()" + method. It is exposed by the "extensions" module in order to allow + subclassing to extend its behaviour: the subclass should be passed + to the "cursor()" method using the "cursor_factory" parameter. See + also *Connection and cursor factories*. + + For a complete description of the class, see "cursor". + +class class psycopg2.extensions.lobject(conn[, oid[, mode[, new_oid[, new_file]]]]) + + Wrapper for a PostgreSQL large object. See *Access to PostgreSQL + large objects* for an overview. + + The class can be subclassed: see the "connection.lobject()" to know + how to specify a "lobject" subclass. + + New in version 2.0.8. + + oid + + Database OID of the object. + + mode + + The mode the database was open. See "connection.lobject()" for a + description of the available modes. + + read(bytes=-1) + + Read a chunk of data from the current file position. If -1 + (default) read all the remaining data. + + The result is an Unicode string (decoded according to + "connection.encoding") if the file was open in "t" mode, a bytes + string for "b" mode. + + Changed in version 2.4: added Unicode support. + + write(str) + + Write a string to the large object. Return the number of bytes + written. Unicode strings are encoded in the + "connection.encoding" before writing. + + Changed in version 2.4: added Unicode support. + + export(file_name) + + Export the large object content to the file system. + + The method uses the efficient "lo_export()" libpq function. + + seek(offset, whence=0) + + Set the lobject current position. + + tell() + + Return the lobject current position. + + truncate(len=0) + + New in version 2.2.0. + + Truncate the lobject to the given size. + + The method will only be available if Psycopg has been built + against libpq from PostgreSQL 8.3 or later and can only be used + with PostgreSQL servers running these versions. It uses the + "lo_truncate()" libpq function. + + Warning: If Psycopg is built with "lo_truncate()" support (i.e. if the + **pg_config** used during setup is version >= 8.3), but at + runtime an older libpq is found, Psycopg will fail to import. + See *the lo_truncate FAQ* about the problem. + + close() + + Close the object. + + closed + + Boolean attribute specifying if the object is closed. + + unlink() + + Close the object and remove it from the database. + +class class psycopg2.extensions.Notify(pid, channel, payload='') + + A notification received from the backend. + + "Notify" instances are made available upon reception on the + "notifies" member of the listening connection. The object can be + also accessed as a 2 items tuple returning the members + "(*pid*,*channel*)" for backward compatibility. + + See *Asynchronous notifications* for details. + + New in version 2.3. + + pid + + The ID of the backend process that sent the notification. + + Note: if the sending session was handled by Psycopg, you can use + "get_backend_pid()" to know its PID. + + channel + + The name of the channel to which the notification was sent. + + payload + + The payload message of the notification. + + Attaching a payload to a notification is only available since + PostgreSQL 9.0: for notifications received from previous + versions of the server this member is always the empty string. + +class class psycopg2.extensions.Xid(format_id, gtrid, bqual) + + A transaction identifier used for two-phase commit. + + Usually returned by the connection methods "xid()" and + "tpc_recover()". "Xid" instances can be unpacked as a 3-item tuples + containing the items "(*format_id*,*gtrid*,*bqual*)". The "str()" + of the object returns the *transaction ID* used in the commands + sent to the server. + + See *Two-Phase Commit protocol support* for an introduction. + + New in version 2.3. + + from_string(s) + + Create a "Xid" object from a string representation. Static + method. + + If *s* is a PostgreSQL transaction ID produced by a XA + transaction, the returned object will have "format_id", "gtrid", + "bqual" set to the values of the preparing XA id. Otherwise only + the "gtrid" is populated with the unparsed string. The operation + is the inverse of the one performed by "str(xid)". + + format_id + + Format ID in a XA transaction. + + A non-negative 32 bit integer. "None" if the transaction doesn't + follow the XA standard. + + gtrid + + Global transaction ID in a XA transaction. + + If the transaction doesn't follow the XA standard, it is the + plain *transaction ID* used in the server commands. + + bqual + + Branch qualifier of the transaction. + + In a XA transaction every resource participating to a + transaction receives a distinct branch qualifier. "None" if the + transaction doesn't follow the XA standard. + + prepared + + Timestamp (with timezone) in which a recovered transaction was + prepared. + + owner + + Name of the user who prepared a recovered transaction. + + database + + Database the recovered transaction belongs to. + +psycopg2.extensions.set_wait_callback(f) + + Register a callback function to block waiting for data. + + The callback should have signature "fun(*conn*)" and is called to + wait for data available whenever a blocking function from the libpq + is called. Use "set_wait_callback(None)" to revert to the original + behaviour (i.e. using blocking libpq functions). + + The function is an hook to allow coroutine-based libraries (such as + Eventlet or gevent) to switch when Psycopg is blocked, allowing + other coroutines to run concurrently. + + See "wait_select()" for an example of a wait callback + implementation. + + New in version 2.2.0. + +psycopg2.extensions.get_wait_callback() + + Return the currently registered wait callback. + + Return "None" if no callback is currently registered. + + New in version 2.2.0. + + +SQL adaptation protocol objects +=============================== + +Psycopg provides a flexible system to adapt Python objects to the SQL +syntax (inspired to the **PEP 246**), allowing serialization in +PostgreSQL. See *Adapting new Python types to SQL syntax* for a +detailed description. The following objects deal with Python objects +adaptation: + +psycopg2.extensions.adapt(obj) + + Return the SQL representation of *obj* as a string. Raise a + "ProgrammingError" if how to adapt the object is unknown. In order + to allow new objects to be adapted, register a new adapter for it + using the "register_adapter()" function. + + The function is the entry point of the adaptation mechanism: it can + be used to write adapters for complex objects by recursively + calling "adapt()" on its components. + +psycopg2.extensions.register_adapter(class, adapter) + + Register a new adapter for the objects of class *class*. + + *adapter* should be a function taking a single argument (the object + to adapt) and returning an object conforming the "ISQLQuote" + protocol (e.g. exposing a "getquoted()" method). The "AsIs" is + often useful for this task. + + Once an object is registered, it can be safely used in SQL queries + and by the "adapt()" function. + +class class psycopg2.extensions.ISQLQuote(wrapped_object) + + Represents the SQL adaptation protocol. Objects conforming this + protocol should implement a "getquoted()" and optionally a + "prepare()" method. + + Adapters may subclass "ISQLQuote", but is not necessary: it is + enough to expose a "getquoted()" method to be conforming. + + _wrapped + + The wrapped object passes to the constructor + + getquoted() + + Subclasses or other conforming objects should return a valid SQL + string representing the wrapped object. In Python 3 the SQL must + be returned in a "bytes" object. The "ISQLQuote" implementation + does nothing. + + prepare(conn) + + Prepare the adapter for a connection. The method is optional: + if implemented, it will be invoked before "getquoted()" with the + connection to adapt for as argument. + + A conform object can implement this method if the SQL + representation depends on any server parameter, such as the + server version or the "standard_conforming_string" setting. + Container objects may store the connection and use it to + recursively prepare contained objects: see the implementation + for "psycopg2.extensions.SQL_IN" for a simple example. + +class class psycopg2.extensions.AsIs(object) + + Adapter conform to the "ISQLQuote" protocol useful for objects + whose string representation is already valid as SQL representation. + + getquoted() + + Return the "str()" conversion of the wrapped object. + + >>> AsIs(42).getquoted() + '42' + +class class psycopg2.extensions.QuotedString(str) + + Adapter conform to the "ISQLQuote" protocol for string-like + objects. + + getquoted() + + Return the string enclosed in single quotes. Any single quote + appearing in the the string is escaped by doubling it according + to SQL string constants syntax. Backslashes are escaped too. + + >>> QuotedString(r"O'Reilly").getquoted() + "'O''Reilly'" + +class class psycopg2.extensions.Binary(str) + + Adapter conform to the "ISQLQuote" protocol for binary objects. + + getquoted() + + Return the string enclosed in single quotes. It performs the + same escaping of the "QuotedString" adapter, plus it knows how + to escape non-printable chars. + + >>> Binary("\x00\x08\x0F").getquoted() + "'\\\\000\\\\010\\\\017'" + + Changed in version 2.0.14: previously the adapter was not exposed + by the "extensions" module. In older versions it can be imported + from the implementation module "psycopg2._psycopg". + +class class psycopg2.extensions.Boolean +class class psycopg2.extensions.Float +class class psycopg2.extensions.SQL_IN + + Specialized adapters for builtin objects. + +class class psycopg2.extensions.DateFromPy +class class psycopg2.extensions.TimeFromPy +class class psycopg2.extensions.TimestampFromPy +class class psycopg2.extensions.IntervalFromPy + + Specialized adapters for Python datetime objects. + +class class psycopg2.extensions.DateFromMx +class class psycopg2.extensions.TimeFromMx +class class psycopg2.extensions.TimestampFromMx +class class psycopg2.extensions.IntervalFromMx + + Specialized adapters for mx.DateTime objects. + +psycopg2.extensions.adapters + + Dictionary of the currently registered object adapters. Use + "register_adapter()" to add an adapter for a new type. + + +Database types casting functions +================================ + +These functions are used to manipulate type casters to convert from +PostgreSQL types to Python objects. See *Type casting of SQL types +into Python objects* for details. + +psycopg2.extensions.new_type(oids, name, adapter) + + Create a new type caster to convert from a PostgreSQL type to a + Python object. The object created must be registered using + "register_type()" to be used. + + Parameters: + * **oids** -- tuple of OIDs of the PostgreSQL type to convert. + + * **name** -- the name of the new type adapter. + + * **adapter** -- the adaptation function. + + The object OID can be read from the "cursor.description" attribute + or by querying from the PostgreSQL catalog. + + *adapter* should have signature "fun(*value*, *cur*)" where *value* + is the string representation returned by PostgreSQL and *cur* is + the cursor from which data are read. In case of "NULL", *value* + will be "None". The adapter should return the converted object. + + See *Type casting of SQL types into Python objects* for an usage + example. + +psycopg2.extensions.new_array_type(oids, name, base_caster) + + Create a new type caster to convert from a PostgreSQL array type to + a list of Python object. The object created must be registered + using "register_type()" to be used. + + Parameters: + * **oids** -- tuple of OIDs of the PostgreSQL type to convert. + It should probably be the oid of the array type (e.g. the + "typarray" field in the "pg_type" table. + + * **name** -- the name of the new type adapter. + + * **base_caster** -- a Psycopg typecaster, e.g. created using + the "new_type()" function. The caster should be able to parse + a single item of the desired type. + + New in version 2.4.3. + + Note: The function can be used to create a generic array typecaster, + returning a list of strings: just use the "STRING" as base + typecaster. For instance, if you want to receive from the + database an array of "macaddr", each address represented by + string, you can use: + + psycopg2.extensions.register_type( + psycopg2.extensions.new_array_type( + (1040,), 'MACADDR[]', psycopg2.STRING)) + +psycopg2.extensions.register_type(obj[, scope]) + + Register a type caster created using "new_type()". + + If *scope* is specified, it should be a "connection" or a "cursor": + the type caster will be effective only limited to the specified + object. Otherwise it will be globally registered. + +psycopg2.extensions.string_types + + The global register of type casters. + +psycopg2.extensions.encodings + + Mapping from PostgreSQL encoding names to Python codec names. Used + by Psycopg when adapting or casting unicode strings. See *Unicode + handling*. + + +Additional exceptions +===================== + +The module exports a few exceptions in addition to the *standard ones* +defined by the DB API 2.0. + +exception exception psycopg2.extensions.QueryCanceledError + + (subclasses "OperationalError") + + Error related to SQL query cancellation. It can be trapped + specifically to detect a timeout. + + New in version 2.0.7. + +exception exception psycopg2.extensions.TransactionRollbackError + + (subclasses "OperationalError") + + Error causing transaction rollback (deadlocks, serialisation + failures, etc). It can be trapped specifically to detect a + deadlock. + + New in version 2.0.7. + + +Isolation level constants +========================= + +Psycopg2 "connection" objects hold informations about the PostgreSQL +transaction isolation level. The current transaction level can be +read from the "isolation_level" attribute. The default isolation +level is "READ COMMITTED". A different isolation level con be set +through the "set_isolation_level()" method. The level can be set to +one of the following constants: + +psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT + + No transaction is started when command are issued and no "commit()" + or "rollback()" is required. Some PostgreSQL command such as + "CREATE DATABASE" or "VACUUM" can't run into a transaction: to run + such command use: + + >>> conn.set_isolation_level(ISOLATION_LEVEL_AUTOCOMMIT) + + See also *Transactions control*. + +psycopg2.extensions.ISOLATION_LEVEL_READ_UNCOMMITTED + + The "READ UNCOMMITTED" isolation level is defined in the SQL + standard but not available in the MVCC (Multiversion concurrency + control) model of PostgreSQL: it is replaced by the stricter "READ + COMMITTED". + +psycopg2.extensions.ISOLATION_LEVEL_READ_COMMITTED + + This is usually the the default PostgreSQL value, but a different + default may be set in the database configuration. + + A new transaction is started at the first "execute()" command on a + cursor and at each new "execute()" after a "commit()" or a + "rollback()". The transaction runs in the PostgreSQL "READ + COMMITTED" isolation level: a "SELECT" query sees only data + committed before the query began; it never sees either uncommitted + data or changes committed during query execution by concurrent + transactions. + + See also: + + Read Committed Isolation Level in PostgreSQL documentation. + +psycopg2.extensions.ISOLATION_LEVEL_REPEATABLE_READ + + As in "ISOLATION_LEVEL_READ_COMMITTED", a new transaction is + started at the first "execute()" command. Transactions run at a + "REPEATABLE READ" isolation level: all the queries in a transaction + see a snapshot as of the start of the transaction, not as of the + start of the current query within the transaction. However + applications using this level must be prepared to retry + transactions due to serialization failures. + + While this level provides a guarantee that each transaction sees a + completely stable view of the database, this view will not + necessarily always be consistent with some serial (one at a time) + execution of concurrent transactions of the same level. + + Changed in version 2.4.2: The value was an alias for + "ISOLATION_LEVEL_SERIALIZABLE" before. The two levels are distinct + since PostgreSQL 9.1 + + See also: + + Repeatable Read Isolation Level in PostgreSQL documentation. + +psycopg2.extensions.ISOLATION_LEVEL_SERIALIZABLE + + As in "ISOLATION_LEVEL_READ_COMMITTED", a new transaction is + started at the first "execute()" command. Transactions run at a + "SERIALIZABLE" isolation level. This is the strictest transactions + isolation level, equivalent to having the transactions executed + serially rather than concurrently. However applications using this + level must be prepared to retry reansactions due to serialization + failures. + + Starting from PostgreSQL 9.1, this mode monitors for conditions + which could make execution of a concurrent set of serializable + transactions behave in a manner inconsistent with all possible + serial (one at a time) executions of those transaction. In previous + version the behaviour was the same of the "REPEATABLE READ" + isolation level. + + See also: + + Serializable Isolation Level in PostgreSQL documentation. + + +Transaction status constants +============================ + +These values represent the possible status of a transaction: the +current value can be read using the +"connection.get_transaction_status()" method. + +psycopg2.extensions.TRANSACTION_STATUS_IDLE + + The session is idle and there is no current transaction. + +psycopg2.extensions.TRANSACTION_STATUS_ACTIVE + + A command is currently in progress. + +psycopg2.extensions.TRANSACTION_STATUS_INTRANS + + The session is idle in a valid transaction block. + +psycopg2.extensions.TRANSACTION_STATUS_INERROR + + The session is idle in a failed transaction block. + +psycopg2.extensions.TRANSACTION_STATUS_UNKNOWN + + Reported if the connection with the server is bad. + + +Connection status constants +=========================== + +These values represent the possible status of a connection: the +current value can be read from the "status" attribute. + +It is possible to find the connection in other status than the one +shown below. Those are the only states in which a working connection +is expected to be found during the execution of regular Python client +code: other states are for internal usage and Python code should not +rely on them. + +psycopg2.extensions.STATUS_READY + + Connection established. No transaction in progress. + +psycopg2.extensions.STATUS_BEGIN + + Connection established. A transaction is currently in progress. + +psycopg2.extensions.STATUS_IN_TRANSACTION + + An alias for "STATUS_BEGIN" + +psycopg2.extensions.STATUS_PREPARED + + The connection has been prepared for the second phase in a *two- + phase commit* transaction. The connection can't be used to send + commands to the database until the transaction is finished with + "tpc_commit()" or "tpc_rollback()". + + New in version 2.3. + + +Poll constants +============== + +New in version 2.2.0. + +These values can be returned by "connection.poll()" during +asynchronous connection and communication. They match the values in +the libpq enum "PostgresPollingStatusType". See *Asynchronous +support* and *Support to coroutine libraries*. + +psycopg2.extensions.POLL_OK + + The data being read is available, or the file descriptor is ready + for writing: reading or writing will not block. + +psycopg2.extensions.POLL_READ + + Some data is being read from the backend, but it is not available + yet on the client and reading would block. Upon receiving this + value, the client should wait for the connection file descriptor to + be ready *for reading*. For example: + + select.select([conn.fileno()], [], []) + +psycopg2.extensions.POLL_WRITE + + Some data is being sent to the backend but the connection file + descriptor can't currently accept new data. Upon receiving this + value, the client should wait for the connection file descriptor to + be ready *for writing*. For example: + + select.select([], [conn.fileno()], []) + +psycopg2.extensions.POLL_ERROR + + There was a problem during connection polling. This value should + actually never be returned: in case of poll error usually an + exception containing the relevant details is raised. + + +Additional database types +========================= + +The "extensions" module includes typecasters for many standard +PostgreSQL types. These objects allow the conversion of returned data +into Python objects. All the typecasters are automatically +registered, except "UNICODE" and "UNICODEARRAY": you can register them +using "register_type()" in order to receive Unicode objects instead of +strings from the database. See *Unicode handling* for details. + +psycopg2.extensions.BOOLEAN +psycopg2.extensions.DATE +psycopg2.extensions.DECIMAL +psycopg2.extensions.FLOAT +psycopg2.extensions.INTEGER +psycopg2.extensions.INTERVAL +psycopg2.extensions.LONGINTEGER +psycopg2.extensions.TIME +psycopg2.extensions.UNICODE + + Typecasters for basic types. Note that a few other ones ("BINARY", + "DATETIME", "NUMBER", "ROWID", "STRING") are exposed by the + "psycopg2" module for DB API 2.0 compliance. + +psycopg2.extensions.BINARYARRAY +psycopg2.extensions.BOOLEANARRAY +psycopg2.extensions.DATEARRAY +psycopg2.extensions.DATETIMEARRAY +psycopg2.extensions.DECIMALARRAY +psycopg2.extensions.FLOATARRAY +psycopg2.extensions.INTEGERARRAY +psycopg2.extensions.INTERVALARRAY +psycopg2.extensions.LONGINTEGERARRAY +psycopg2.extensions.ROWIDARRAY +psycopg2.extensions.STRINGARRAY +psycopg2.extensions.TIMEARRAY +psycopg2.extensions.UNICODEARRAY + + Typecasters to convert arrays of sql types into Python lists. + +psycopg2.extensions.PYDATE +psycopg2.extensions.PYDATETIME +psycopg2.extensions.PYINTERVAL +psycopg2.extensions.PYTIME +psycopg2.extensions.PYDATEARRAY +psycopg2.extensions.PYDATETIMEARRAY +psycopg2.extensions.PYINTERVALARRAY +psycopg2.extensions.PYTIMEARRAY + + Typecasters to convert time-related data types to Python "datetime" + objects. + +psycopg2.extensions.MXDATE +psycopg2.extensions.MXDATETIME +psycopg2.extensions.MXINTERVAL +psycopg2.extensions.MXTIME +psycopg2.extensions.MXDATEARRAY +psycopg2.extensions.MXDATETIMEARRAY +psycopg2.extensions.MXINTERVALARRAY +psycopg2.extensions.MXTIMEARRAY + + Typecasters to convert time-related data types to mx.DateTime + objects. Only available if Psycopg was compiled with "mx" support. + +Changed in version 2.2.0: previously the "DECIMAL" typecaster and the +specific time-related typecasters ("PY*" and "MX*") were not exposed +by the "extensions" module. In older versions they can be imported +from the implementation module "psycopg2._psycopg". + + + +'psycopg2.tz' -- 'tzinfo' implementations for Psycopg 2 +************************************************************ + +This module holds two different tzinfo implementations that can be +used as the 'tzinfo' argument to 'datetime' constructors, directly +passed to Psycopg functions or used to set the +'cursor.tzinfo_factory' attribute in cursors. + +class class psycopg2.tz.FixedOffsetTimezone(offset=None, name=None) + + Fixed offset in minutes east from UTC. + + This is exactly the implementation found in Python 2.3.x + documentation, with a small change to the '__init__()' method to + allow for pickling and a default name in the form 'sHH:MM' ('s' + is the sign.). + +class class psycopg2.tz.LocalTimezone + + Platform idea of local timezone. + + This is the exact implementation from the Python 2.3 documentation. + + + +'psycopg2.pool' -- Connections pooling +**************************************** + +Creating new PostgreSQL connections can be an expensive operation. +This module offers a few pure Python classes implementing simple +connection pooling directly in the client application. + +class class psycopg2.pool.AbstractConnectionPool(minconn, maxconn, *args, **kwargs) + + Base class implementing generic key-based pooling code. + + New *minconn* connections are created automatically. The pool will + support a maximum of about *maxconn* connections. **args* and + ***kwargs* are passed to the 'connect()' function. + + The following methods are expected to be implemented by subclasses: + + getconn(key=None) + + Get a free connection and assign it to *key* if not 'None'. + + putconn(conn, key=None, close=False) + + Put away a connection. + + If *close* is 'True', discard the connection from the pool. + + closeall() + + Close all the connections handled by the pool. + + Note that all the connections are closed, including ones + eventually in use by the application. + +The following classes are 'AbstractConnectionPool' subclasses ready +to be used. + +class class psycopg2.pool.SimpleConnectionPool(minconn, maxconn, *args, **kwargs) + + A connection pool that can't be shared across different threads. + + Note: This pool class is useful only for single-threaded applications. + +class class psycopg2.pool.ThreadedConnectionPool(minconn, maxconn, *args, **kwargs) + + A connection pool that works with the threading module. + + Note: This pool class can be safely used in multi-threaded + applications. + +class class psycopg2.pool.PersistentConnectionPool(minconn, maxconn, *args, **kwargs) + + A pool that assigns persistent connections to different threads. + + Note that this connection pool generates by itself the required + keys using the current thread id. This means that until a thread + puts away a connection it will always get the same connection + object by successive 'getconn()' calls. This also means that a + thread can't use more than one single connection from the pool. + + Note: This pool class is mostly designed to interact with Zope and + probably not useful in generic applications. + + + +"psycopg2.extras" -- Miscellaneous goodies for Psycopg 2 +******************************************************** + +This module is a generic place used to hold little helper functions +and classes until a better place in the distribution is found. + + +Connection and cursor subclasses +================================ + +A few objects that change the way the results are returned by the +cursor or modify the object behavior in some other way. Typically +"connection" subclasses are passed as *connection_factory* argument to +"connect()" so that the connection will generate the matching "cursor" +subclass. Alternatively a "cursor" subclass can be used one-off by +passing it as the *cursor_factory* argument to the "cursor()" method +of a regular "connection". + + +Dictionary-like cursor +---------------------- + +The dict cursors allow to access to the retrieved records using an +iterface similar to the Python dictionaries instead of the tuples. + +>>> dict_cur = conn.cursor(cursor_factory=psycopg2.extras.DictCursor) +>>> dict_cur.execute("INSERT INTO test (num, data) VALUES(%s, %s)", +... (100, "abc'def")) +>>> dict_cur.execute("SELECT * FROM test") +>>> rec = dict_cur.fetchone() +>>> rec['id'] +1 +>>> rec['num'] +100 +>>> rec['data'] +"abc'def" + +The records still support indexing as the original tuple: + +>>> rec[2] +"abc'def" + +class class psycopg2.extras.DictCursor(*args, **kwargs) + + A cursor that keeps a list of column name -> index mappings. + +class class psycopg2.extras.DictConnection + + A connection that uses "DictCursor" automatically. + +class class psycopg2.extras.DictRow(cursor) + + A row object that allow by-colmun-name access to data. + + +Real dictionary cursor +---------------------- + +class class psycopg2.extras.RealDictCursor(*args, **kwargs) + + A cursor that uses a real dict as the base type for rows. + + Note that this cursor is extremely specialized and does not allow + the normal access (using integer indices) to fetched data. If you + need to access database rows both as a dictionary and a list, then + use the generic "DictCursor" instead of "RealDictCursor". + +class class psycopg2.extras.RealDictConnection + + A connection that uses "RealDictCursor" automatically. + +class class psycopg2.extras.RealDictRow(cursor) + + A "dict" subclass representing a data record. + + +"namedtuple" cursor +------------------- + +New in version 2.3. + +These objects require "collections.namedtuple()" to be found, so it is +available out-of-the-box only from Python 2.6. Anyway, the namedtuple +implementation is compatible with previous Python versions, so all you +have to do is to download it and make it available where we expect it +to be... + + from somewhere import namedtuple + import collections + collections.namedtuple = namedtuple + from psycopg.extras import NamedTupleConnection + # ... + +class class psycopg2.extras.NamedTupleCursor + + A cursor that generates results as "namedtuple". + + "fetch*()" methods will return named tuples instead of regular + tuples, so their elements can be accessed both as regular numeric + items as well as attributes. + + >>> nt_cur = conn.cursor(cursor_factory=psycopg2.extras.NamedTupleCursor) + >>> rec = nt_cur.fetchone() + >>> rec + Record(id=1, num=100, data="abc'def") + >>> rec[1] + 100 + >>> rec.data + "abc'def" + +class class psycopg2.extras.NamedTupleConnection + + A connection that uses "NamedTupleCursor" automatically. + + +Logging cursor +-------------- + +class class psycopg2.extras.LoggingConnection + + A connection that logs all queries to a file or logger object. + + initialize(logobj) + + Initialize the connection to log to "logobj". + + The "logobj" parameter can be an open file object or a Logger + instance from the standard logging module. + + filter(msg, curs) + + Filter the query before logging it. + + This is the method to overwrite to filter unwanted queries out + of the log or to add some extra data to the output. The default + implementation just does nothing. + +class class psycopg2.extras.LoggingCursor + + A cursor that logs queries using its connection logging facilities. + +class class psycopg2.extras.MinTimeLoggingConnection + + A connection that logs queries based on execution time. + + This is just an example of how to sub-class "LoggingConnection" to + provide some extra filtering for the logged queries. Both the + "inizialize()" and "filter()" methods are overwritten to make sure + that only queries executing for more than "mintime" ms are logged. + + Note that this connection uses the specialized cursor + "MinTimeLoggingCursor". + +class class psycopg2.extras.MinTimeLoggingCursor + + The cursor sub-class companion to "MinTimeLoggingConnection". + + +Additional data types +===================== + + +Hstore data type +---------------- + +New in version 2.3. + +The "hstore" data type is a key-value store embedded in PostgreSQL. +It has been available for several server versions but with the release +9.0 it has been greatly improved in capacity and usefulness with the +addiction of many functions. It supports GiST or GIN indexes allowing +search by keys or key/value pairs as well as regular BTree indexes for +equality, uniqueness etc. + +Psycopg can convert Python "dict" objects to and from "hstore" +structures. Only dictionaries with string/unicode keys and values are +supported. "None" is also allowed as value but not as a key. Psycopg +uses a more efficient "hstore" representation when dealing with +PostgreSQL 9.0 but previous server versions are supported as well. By +default the adapter/typecaster are disabled: they can be enabled using +the "register_hstore()" function. + +psycopg2.extras.register_hstore(conn_or_curs, globally=False, unicode=False, oid=None, array_oid=None) + + Register adapter and typecaster for "dict"-"hstore" conversions. + + Parameters: + * **conn_or_curs** -- a connection or cursor: the typecaster + will be registered only on this object unless *globally* is + set to "True" + + * **globally** -- register the adapter globally, not only on + *conn_or_curs* + + * **unicode** -- if "True", keys and values returned from the + database will be "unicode" instead of "str". The option is not + available on Python 3 + + * **oid** -- the OID of the "hstore" type if known. If not, it + will be queried on *conn_or_curs*. + + * **array_oid** -- the OID of the "hstore" array type if known. + If not, it will be queried on *conn_or_curs*. + + The connection or cursor passed to the function will be used to + query the database and look for the OID of the "hstore" type (which + may be different across databases). If querying is not desirable + (e.g. with *asynchronous connections*) you may specify it in the + *oid* parameter, which can be found using a query such as "SELECT + 'hstore'::regtype::oid". Analogously you can obtain a value for + *array_oid* using a query such as "SELECT + 'hstore[]'::regtype::oid". + + Note that, when passing a dictionary from Python to the database, + both strings and unicode keys and values are supported. + Dictionaries returned from the database have keys/values according + to the *unicode* parameter. + + The "hstore" contrib module must be already installed in the + database (executing the "hstore.sql" script in your "contrib" + directory). Raise "ProgrammingError" if the type is not found. + + Changed in version 2.4: added the *oid* parameter. If not + specified, the typecaster is installed also if "hstore" is not + installed in the "public" schema. + + Changed in version 2.4.3: added support for "hstore" array. + + +Composite types casting +----------------------- + +New in version 2.4. + +Using "register_composite()" it is possible to cast a PostgreSQL +composite type (either created with the "CREATE TYPE" command or +implicitly defined after a table row type) into a Python named tuple, +or into a regular tuple if "collections.namedtuple()" is not found. + + >>> cur.execute("CREATE TYPE card AS (value int, suit text);") + >>> psycopg2.extras.register_composite('card', cur) + + + >>> cur.execute("select (8, 'hearts')::card") + >>> cur.fetchone()[0] + card(value=8, suit='hearts') + +Nested composite types are handled as expected, but the type of the +composite components must be registered as well. + + >>> cur.execute("CREATE TYPE card_back AS (face card, back text);") + >>> psycopg2.extras.register_composite('card_back', cur) + + + >>> cur.execute("select ((8, 'hearts'), 'blue')::card_back") + >>> cur.fetchone()[0] + card_back(face=card(value=8, suit='hearts'), back='blue') + +Adaptation from Python tuples to composite types is automatic instead +and requires no adapter registration. + +psycopg2.extras.register_composite(name, conn_or_curs, globally=False) + + Register a typecaster to convert a composite type into a tuple. + + Parameters: + * **name** -- the name of a PostgreSQL composite type, e.g. + created using the "CREATE TYPE" command + + * **conn_or_curs** -- a connection or cursor used to find the + type oid and components; the typecaster is registered in a + scope limited to this object, unless *globally* is set to + "True" + + * **globally** -- if "False" (default) register the typecaster + only on *conn_or_curs*, otherwise register it globally + + Returns: + the registered "CompositeCaster" instance responsible for the + conversion + + Changed in version 2.4.3: added support for array of composite + types + +class class psycopg2.extras.CompositeCaster(name, oid, attrs, array_oid=None) + + Helps conversion of a PostgreSQL composite type into a Python + object. + + The class is usually created by the "register_composite()" + function. You may want to create and register manually instances of + the class if querying the database at registration time is not + desirable (such as when using an *asynchronous connections*). + + name + + The name of the PostgreSQL type. + + oid + + The oid of the PostgreSQL type. + + array_oid + + The oid of the PostgreSQL array type, if available. + + type + + The type of the Python objects returned. If + "collections.namedtuple()" is available, it is a named tuple + with attributes equal to the type components. Otherwise it is + just the "tuple" object. + + attnames + + List of component names of the type to be casted. + + atttypes + + List of component type oids of the type to be casted. + + +UUID data type +-------------- + +New in version 2.0.9. + +Changed in version 2.0.13: added UUID array support. + + >>> psycopg2.extras.register_uuid() + + + >>> # Python UUID can be used in SQL queries + >>> import uuid + >>> my_uuid = uuid.UUID('{12345678-1234-5678-1234-567812345678}') + >>> psycopg2.extensions.adapt(my_uuid).getquoted() + "'12345678-1234-5678-1234-567812345678'::uuid" + + >>> # PostgreSQL UUID are transformed into Python UUID objects. + >>> cur.execute("SELECT 'a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11'::uuid") + >>> cur.fetchone()[0] + UUID('a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11') + +psycopg2.extras.register_uuid(oids=None, conn_or_curs=None) + + Create the UUID type and an uuid.UUID adapter. + + Parameters: + * **oids** -- oid for the PostgreSQL "uuid" type, or 2-items + sequence with oids of the type and the array. If not + specified, use PostgreSQL standard oids. + + * **conn_or_curs** -- where to register the typecaster. If not + specified, register it globally. + +class class psycopg2.extras.UUID_adapter(uuid) + + Adapt Python's uuid.UUID type to PostgreSQL's uuid. + + +"inet" data type +---------------- + +New in version 2.0.9. + +Changed in version 2.4.5: added inet array support. + + >>> psycopg2.extras.register_inet() + + + >>> cur.mogrify("SELECT %s", (Inet('127.0.0.1/32'),)) + "SELECT E'127.0.0.1/32'::inet" + + >>> cur.execute("SELECT '192.168.0.1/24'::inet") + >>> cur.fetchone()[0].addr + '192.168.0.1/24' + +psycopg2.extras.register_inet(oid=None, conn_or_curs=None) + + Create the INET type and an Inet adapter. + + Parameters: + * **oid** -- oid for the PostgreSQL "inet" type, or 2-items + sequence with oids of the type and the array. If not + specified, use PostgreSQL standard oids. + + * **conn_or_curs** -- where to register the typecaster. If not + specified, register it globally. + +class class psycopg2.extras.Inet(addr) + + Wrap a string to allow for correct SQL-quoting of inet values. + + Note that this adapter does NOT check the passed value to make sure + it really is an inet-compatible address but DOES call adapt() on it + to make sure it is impossible to execute an SQL-injection by + passing an evil value to the initializer. + + +Fractional time zones +===================== + +psycopg2.extras.register_tstz_w_secs(oids=None, conn_or_curs=None) + + The function used to register an alternate type caster for + "TIMESTAMP WITH TIME ZONE" to deal with historical time zones with + seconds in the UTC offset. + + These are now correctly handled by the default type caster, so + currently the function doesn't do anything. + + New in version 2.0.9. + + Changed in version 2.2.2: function is no-op: see *Time zones + handling*. + + +Coroutine support +================= + +psycopg2.extras.wait_select(conn) + + Wait until a connection or cursor has data available. + + The function is an example of a wait callback to be registered with + "set_wait_callback()". This function uses "select()" to wait for + data available. + + + +"psycopg2.errorcodes" -- Error codes defined by PostgreSQL +********************************************************** + +New in version 2.0.6. + +This module contains symbolic names for all PostgreSQL error codes and +error classes codes. Subclasses of "Error" make the PostgreSQL error +code available in the "pgcode" attribute. + +From PostgreSQL documentation: + + All messages emitted by the PostgreSQL server are assigned five- + character error codes that follow the SQL standard's conventions + for "SQLSTATE" codes. Applications that need to know which error + condition has occurred should usually test the error code, rather + than looking at the textual error message. The error codes are + less likely to change across PostgreSQL releases, and also are not + subject to change due to localization of error messages. Note that + some, but not all, of the error codes produced by PostgreSQL are + defined by the SQL standard; some additional error codes for + conditions not defined by the standard have been invented or + borrowed from other databases. + + According to the standard, the first two characters of an error + code denote a class of errors, while the last three characters + indicate a specific condition within that class. Thus, an + application that does not recognize the specific error code can + still be able to infer what to do from the error class. + +See also: + + PostgreSQL Error Codes table + +An example of the available constants defined in the module: + +>>> errorcodes.CLASS_SYNTAX_ERROR_OR_ACCESS_RULE_VIOLATION +'42' +>>> errorcodes.UNDEFINED_TABLE +'42P01' + +Constants representing all the error values documented by PostgreSQL +versions between 8.1 and 9.1 are included in the module. + +psycopg2.errorcodes.lookup(code) + + Lookup an error code or class code and return its symbolic name. + + Raise "KeyError" if the code is not found. + + >>> try: + ... cur.execute("SELECT ouch FROM aargh;") + ... except Exception, e: + ... pass + ... + >>> errorcodes.lookup(e.pgcode[:2]) + 'CLASS_SYNTAX_ERROR_OR_ACCESS_RULE_VIOLATION' + >>> errorcodes.lookup(e.pgcode) + 'UNDEFINED_TABLE' + + New in version 2.0.14. + + + +Frequently Asked Questions +************************** + +Here are a few gotchas you may encounter using "psycopg2". Feel free +to suggest new entries! + + +Problems with transactions handling +=================================== + +Why does "psycopg2" leave database sessions "idle in transaction"? + Psycopg normally starts a new transaction the first time a query is + executed, e.g. calling "cursor.execute()", even if the command is a + "SELECT". The transaction is not closed until an explicit + "commit()" or "rollback()". + + If you are writing a long-living program, you should probably make + sure to call one of the transaction closing methods before leaving + the connection unused for a long time (which may also be a few + seconds, depending on the concurrency level in your database). + Alternatively you can use a connection in "autocommit" mode to + avoid a new transaction to be started at the first command. + +I receive the error *current transaction is aborted, commands ignored +until end of transaction block* and can't do anything else! + There was a problem *in the previous* command to the database, + which resulted in an error. The database will not recover + automatically from this condition: you must run a "rollback()" + before sending new commands to the session (if this seems too + harsh, remember that PostgreSQL supports nested transactions using + the "SAVEPOINT" command). + +Why do I get the error *current transaction is aborted, commands +ignored until end of transaction block* when I use "multiprocessing" +(or any other forking system) and not when use "threading"? + Psycopg's connections can't be shared across processes (but are + thread safe). If you are forking the Python process make sure to + create a new connection in each forked child. See *Thread and + process safety* for further informations. + + +Problems with type conversions +============================== + +Why does "cursor.execute()" raise the exception *can't adapt*? + Psycopg converts Python objects in a SQL string representation by + looking at the object class. The exception is raised when you are + trying to pass as query parameter an object for which there is no + adapter registered for its class. See *Adapting new Python types + to SQL syntax* for informations. + +I can't pass an integer or a float parameter to my query: it says *a +number is required*, but *it is* a number! + In your query string, you always have to use "%s" placeholders, + event when passing a number. All Python objects are converted by + Psycopg in their SQL representation, so they get passed to the + query as strings. See *Passing parameters to SQL queries*. + + >>> cur.execute("INSERT INTO numbers VALUES (%d)", (42,)) # WRONG + >>> cur.execute("INSERT INTO numbers VALUES (%s)", (42,)) # correct + +I try to execute a query but it fails with the error *not all +arguments converted during string formatting* (or *object does not +support indexing*). Why? + Psycopg always require positional arguments to be passed as a + sequence, even when the query takes a single parameter. And + remember that to make a single item tuple in Python you need a + comma! See *Passing parameters to SQL queries*. + + >>> cur.execute("INSERT INTO foo VALUES (%s)", "bar") # WRONG + >>> cur.execute("INSERT INTO foo VALUES (%s)", ("bar")) # WRONG + >>> cur.execute("INSERT INTO foo VALUES (%s)", ("bar",)) # correct + >>> cur.execute("INSERT INTO foo VALUES (%s)", ["bar"]) # correct + +My database is Unicode, but I receive all the strings as UTF-8 "str". +Can I receive "unicode" objects instead? + The following magic formula will do the trick: + + psycopg2.extensions.register_type(psycopg2.extensions.UNICODE) + psycopg2.extensions.register_type(psycopg2.extensions.UNICODEARRAY) + + See *Unicode handling* for the gory details. + +Psycopg converts "decimal"/"numeric" database types into Python +"Decimal" objects. Can I have "float" instead? + You can register a customized adapter for PostgreSQL decimal type: + + DEC2FLOAT = psycopg2.extensions.new_type( + psycopg2.extensions.DECIMAL.values, + 'DEC2FLOAT', + lambda value, curs: float(value) if value is not None else None) + psycopg2.extensions.register_type(DEC2FLOAT) + + See *Type casting of SQL types into Python objects* to read the + relevant documentation. If you find "psycopg2.extensions.DECIMAL" + not avalable, use "psycopg2._psycopg.DECIMAL" instead. + +Transferring binary data from PostgreSQL 9.0 doesn't work. + PostgreSQL 9.0 uses by default the "hex" format to transfer "bytea" + data: the format can't be parsed by the libpq 8.4 and earlier. The + problem is solved in Psycopg 2.4.1, that uses its own parser for + the "bytea" format. For previous Psycopg releases, three options to + solve the problem are: + + * set the bytea_output parameter to "escape" in the server; + + * execute the database command "SET bytea_output TO escape;" in the + session before reading binary data; + + * upgrade the libpq library on the client to at least 9.0. + +Arrays of *TYPE* are not casted to list. + Arrays are only casted to list when their oid is known, and an + array typecaster is registered for them. If there is no typecaster, + the array is returned unparsed from PostgreSQL (e.g. "{a,b,c}"). It + is easy to create a generic arrays typecaster, returning a list of + array: an example is provided in the "new_array_type()" + documentation. + + +Best practices +============== + +When should I save and re-use a cursor as opposed to creating a new +one as needed? + Cursors are lightweight objects and creating lots of them should + not pose any kind of problem. But note that cursors used to fetch + result sets will cache the data and use memory in proportion to the + result set size. Our suggestion is to almost always create a new + cursor and dispose old ones as soon as the data is not required + anymore (call "close()" on them.) The only exception are tight + loops where one usually use the same cursor for a whole bunch of + "INSERT"s or "UPDATE"s. + +When should I save and re-use a connection as opposed to creating a +new one as needed? + Creating a connection can be slow (think of SSL over TCP) so the + best practice is to create a single connection and keep it open as + long as required. It is also good practice to rollback or commit + frequently (even after a single "SELECT" statement) to make sure + the backend is never left "idle in transaction". See also + "psycopg2.pool" for lightweight connection pooling. + +What are the advantages or disadvantages of using named cursors? + The only disadvantages is that they use up resources on the server + and that there is a little overhead because a at least two queries + (one to create the cursor and one to fetch the initial result set) + are issued to the backend. The advantage is that data is fetched + one chunk at a time: using small "fetchmany()" values it is + possible to use very little memory on the client and to skip or + discard parts of the result set. + + +Problems compiling and deploying psycopg2 +========================================= + +I can't compile "psycopg2": the compiler says *error: Python.h: No +such file or directory*. What am I missing? + You need to install a Python development package: it is usually + called "python-dev". + +I can't compile "psycopg2": the compiler says *error: libpq-fe.h: No +such file or directory*. What am I missing? + You need to install the development version of the libpq: the + package is usually called "libpq-dev". + +"psycopg2" raises "ImportError" with message *_psycopg.so: undefined +symbol: lo_truncate* when imported. + This means that Psycopg has been compiled with "lo_truncate()" + support, which means that the libpq used at compile time was + version >= 8.3, but at runtime an older libpq library is found. You + can use: + + $ ldd /path/to/packages/psycopg2/_psycopg.so | grep libpq + + to find what is the version used at runtime. + + You can avoid the problem by using the same version of the + **pg_config** at install time and the libpq at runtime. + +Psycopg raises *ImportError: cannot import name tz* on import in +mod_wsgi / ASP, but it works fine otherwise. + If "psycopg2" is installed in an egg (e.g. because installed by + **easy_install**), the user running the program may be unable to + write in the eggs cache. Set the env variable "PYTHON_EGG_CACHE" to + a writable directory. With modwsgi you can use the WSGIPythonEggs + directive. + + diff -Nru psycopg2-2.0.13/doc/README psycopg2-2.4.5/doc/README --- psycopg2-2.0.13/doc/README 1970-01-01 00:00:00.000000000 +0000 +++ psycopg2-2.4.5/doc/README 2011-02-06 15:58:34.000000000 +0000 @@ -0,0 +1,42 @@ +How to build psycopg documentation +---------------------------------- + +- Install Sphinx, maybe in a virtualenv. Tested with Sphinx 0.6.4:: + + ~$ virtualenv pd + New python executable in pd/bin/python + Installing setuptools............done. + ~$ cd pd + ~/pd$ source bin/activate + (pd)~/pd$ + +- Install Sphinx in the env:: + + (pd)~/pd$ easy_install sphinx + Searching for sphinx + Reading http://pypi.python.org/simple/sphinx/ + Reading http://sphinx.pocoo.org/ + Best match: Sphinx 0.6.4 + ... + Finished processing dependencies for sphinx + +- Build psycopg2 and ensure the package can be imported (it will be used for + reading the version number, autodocs etc.):: + + (pd)~/pd/psycopg2$ python setup.py build + (pd)~/pd/psycopg2$ python setup.py install + running install + ... + creating ~/pd/lib/python2.6/site-packages/psycopg2 + ... + +- Move to the ``doc`` dir and run ``make`` from there:: + + (pd)~/pd/psycopg2$ cd doc/ + (pd)~/pd/psycopg2/doc$ make + Running Sphinx v0.6.4 + ... + +You should have the rendered documentation in ``./html`` and the text file +``psycopg2.txt`` now. + diff -Nru psycopg2-2.0.13/doc/src/advanced.rst psycopg2-2.4.5/doc/src/advanced.rst --- psycopg2-2.0.13/doc/src/advanced.rst 1970-01-01 00:00:00.000000000 +0000 +++ psycopg2-2.4.5/doc/src/advanced.rst 2012-03-28 21:09:15.000000000 +0000 @@ -0,0 +1,494 @@ +More advanced topics +==================== + +.. sectionauthor:: Daniele Varrazzo + +.. testsetup:: * + + import re + import select + + cur.execute("CREATE TABLE atable (apoint point)") + conn.commit() + + def wait(conn): + while 1: + state = conn.poll() + if state == psycopg2.extensions.POLL_OK: + break + elif state == psycopg2.extensions.POLL_WRITE: + select.select([], [conn.fileno()], []) + elif state == psycopg2.extensions.POLL_READ: + select.select([conn.fileno()], [], []) + else: + raise psycopg2.OperationalError("poll() returned %s" % state) + + aconn = psycopg2.connect(database='test', async=1) + wait(aconn) + acurs = aconn.cursor() + +.. index:: + double: Subclassing; Cursor + double: Subclassing; Connection + +.. _subclassing-connection: +.. _subclassing-cursor: + +Connection and cursor factories +------------------------------- + +Psycopg exposes two new-style classes that can be sub-classed and expanded to +adapt them to the needs of the programmer: `psycopg2.extensions.cursor` +and `psycopg2.extensions.connection`. The `connection` class is +usually sub-classed only to provide an easy way to create customized cursors +but other uses are possible. `cursor` is much more interesting, because +it is the class where query building, execution and result type-casting into +Python variables happens. + +.. index:: + single: Example; Cursor subclass + +An example of cursor subclass performing logging is:: + + import psycopg2 + import psycopg2.extensions + import logging + + class LoggingCursor(psycopg2.extensions.cursor): + def execute(self, sql, args=None): + logger = logging.getLogger('sql_debug') + logger.info(self.mogrify(sql, args)) + + try: + psycopg2.extensions.cursor.execute(self, sql, args) + except Exception, exc: + logger.error("%s: %s" % (exc.__class__.__name__, exc)) + raise + + conn = psycopg2.connect(DSN) + cur = conn.cursor(cursor_factory=LoggingCursor) + cur.execute("INSERT INTO mytable VALUES (%s, %s, %s);", + (10, 20, 30)) + + + +.. index:: + single: Objects; Creating new adapters + single: Adaptation; Creating new adapters + single: Data types; Creating new adapters + +.. _adapting-new-types: + +Adapting new Python types to SQL syntax +--------------------------------------- + +Any Python class or type can be adapted to an SQL string. Adaptation mechanism +is similar to the Object Adaptation proposed in the :pep:`246` and is exposed +by the `psycopg2.extensions.adapt()` function. + +The `~cursor.execute()` method adapts its arguments to the +`~psycopg2.extensions.ISQLQuote` protocol. Objects that conform to this +protocol expose a `!getquoted()` method returning the SQL representation +of the object as a string (the method must return `!bytes` in Python 3). +Optionally the conform object may expose a +`~psycopg2.extensions.ISQLQuote.prepare()` method. + +There are two basic ways to have a Python object adapted to SQL: + +- the object itself is conform, or knows how to make itself conform. Such + object must expose a `__conform__()` method that will be called with the + protocol object as argument. The object can check that the protocol is + `!ISQLQuote`, in which case it can return `!self` (if the object also + implements `!getquoted()`) or a suitable wrapper object. This option is + viable if you are the author of the object and if the object is specifically + designed for the database (i.e. having Psycopg as a dependency and polluting + its interface with the required methods doesn't bother you). For a simple + example you can take a look at the source code for the + `psycopg2.extras.Inet` object. + +- If implementing the `!ISQLQuote` interface directly in the object is not an + option (maybe because the object to adapt comes from a third party library), + you can use an *adaptation function*, taking the object to be adapted as + argument and returning a conforming object. The adapter must be + registered via the `~psycopg2.extensions.register_adapter()` function. A + simple example wrapper is `!psycopg2.extras.UUID_adapter` used by the + `~psycopg2.extras.register_uuid()` function. + +A convenient object to write adapters is the `~psycopg2.extensions.AsIs` +wrapper, whose `!getquoted()` result is simply the `!str()`\ ing conversion of +the wrapped object. + +.. index:: + single: Example; Types adaptation + +Example: mapping of a `!Point` class into the |point|_ PostgreSQL +geometric type: + +.. doctest:: + + >>> from psycopg2.extensions import adapt, register_adapter, AsIs + + >>> class Point(object): + ... def __init__(self, x, y): + ... self.x = x + ... self.y = y + + >>> def adapt_point(point): + ... return AsIs("'(%s, %s)'" % (adapt(point.x), adapt(point.y))) + + >>> register_adapter(Point, adapt_point) + + >>> cur.execute("INSERT INTO atable (apoint) VALUES (%s)", + ... (Point(1.23, 4.56),)) + + +.. |point| replace:: :sql:`point` +.. _point: http://www.postgresql.org/docs/current/static/datatype-geometric.html#DATATYPE-GEOMETRIC + +The above function call results in the SQL command:: + + INSERT INTO atable (apoint) VALUES ('(1.23, 4.56)'); + + + +.. index:: Type casting + +.. _type-casting-from-sql-to-python: + +Type casting of SQL types into Python objects +--------------------------------------------- + +PostgreSQL objects read from the database can be adapted to Python objects +through an user-defined adapting function. An adapter function takes two +arguments: the object string representation as returned by PostgreSQL and the +cursor currently being read, and should return a new Python object. For +example, the following function parses the PostgreSQL :sql:`point` +representation into the previously defined `!Point` class: + + >>> def cast_point(value, cur): + ... if value is None: + ... return None + ... + ... # Convert from (f1, f2) syntax using a regular expression. + ... m = re.match(r"\(([^)]+),([^)]+)\)", value) + ... if m: + ... return Point(float(m.group(1)), float(m.group(2))) + ... else: + ... raise InterfaceError("bad point representation: %r" % value) + + +In order to create a mapping from a PostgreSQL type (either standard or +user-defined), its OID must be known. It can be retrieved either by the second +column of the `cursor.description`: + + >>> cur.execute("SELECT NULL::point") + >>> point_oid = cur.description[0][1] + >>> point_oid + 600 + +or by querying the system catalog for the type name and namespace (the +namespace for system objects is :sql:`pg_catalog`): + + >>> cur.execute(""" + ... SELECT pg_type.oid + ... FROM pg_type JOIN pg_namespace + ... ON typnamespace = pg_namespace.oid + ... WHERE typname = %(typename)s + ... AND nspname = %(namespace)s""", + ... {'typename': 'point', 'namespace': 'pg_catalog'}) + >>> point_oid = cur.fetchone()[0] + >>> point_oid + 600 + +After you know the object OID, you can create and register the new type: + + >>> POINT = psycopg2.extensions.new_type((point_oid,), "POINT", cast_point) + >>> psycopg2.extensions.register_type(POINT) + +The `~psycopg2.extensions.new_type()` function binds the object OIDs +(more than one can be specified) to the adapter function. +`~psycopg2.extensions.register_type()` completes the spell. Conversion +is automatically performed when a column whose type is a registered OID is +read: + + >>> cur.execute("SELECT '(10.2,20.3)'::point") + >>> point = cur.fetchone()[0] + >>> print type(point), point.x, point.y + 10.2 20.3 + +A typecaster created by `!new_type()` can be also used with +`~psycopg2.extensions.new_array_type()` to create a typecaster converting a +PostgreSQL array into a Python list. + + +.. index:: + pair: Asynchronous; Notifications + pair: LISTEN; SQL command + pair: NOTIFY; SQL command + +.. _async-notify: + +Asynchronous notifications +-------------------------- + +Psycopg allows asynchronous interaction with other database sessions using the +facilities offered by PostgreSQL commands |LISTEN|_ and |NOTIFY|_. Please +refer to the PostgreSQL documentation for examples about how to use this form of +communication. + +Notifications are instances of the `~psycopg2.extensions.Notify` object made +available upon reception in the `connection.notifies` list. Notifications can +be sent from Python code simply executing a :sql:`NOTIFY` command in an +`~cursor.execute()` call. + +Because of the way sessions interact with notifications (see |NOTIFY|_ +documentation), you should keep the connection in `~connection.autocommit` +mode if you wish to receive or send notifications in a timely manner. + +.. |LISTEN| replace:: :sql:`LISTEN` +.. _LISTEN: http://www.postgresql.org/docs/current/static/sql-listen.html +.. |NOTIFY| replace:: :sql:`NOTIFY` +.. _NOTIFY: http://www.postgresql.org/docs/current/static/sql-notify.html + +Notifications are received after every query execution. If the user is +interested in receiving notifications but not in performing any query, the +`~connection.poll()` method can be used to check for new messages without +wasting resources. + +A simple application could poll the connection from time to time to check if +something new has arrived. A better strategy is to use some I/O completion +function such as :py:func:`~select.select` to sleep until awaken from the kernel when there is +some data to read on the connection, thereby using no CPU unless there is +something to read:: + + import select + import psycopg2 + import psycopg2.extensions + + conn = psycopg2.connect(DSN) + conn.set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT) + + curs = conn.cursor() + curs.execute("LISTEN test;") + + print "Waiting for notifications on channel 'test'" + while 1: + if select.select([conn],[],[],5) == ([],[],[]): + print "Timeout" + else: + conn.poll() + while conn.notifies: + notify = conn.notifies.pop() + print "Got NOTIFY:", notify.pid, notify.channel, notify.payload + +Running the script and executing a command such as :sql:`NOTIFY test, 'hello'` +in a separate :program:`psql` shell, the output may look similar to:: + + Waiting for notifications on channel 'test' + Timeout + Timeout + Got NOTIFY: 6535 test hello + Timeout + ... + +Note that the payload is only available from PostgreSQL 9.0: notifications +received from a previous version server will have the +`~psycopg2.extensions.Notify.payload` attribute set to the empty string. + +.. versionchanged:: 2.3 + Added `~psycopg2.extensions.Notify` object and handling notification + payload. + + + +.. index:: + double: Asynchronous; Connection + +.. _async-support: + +Asynchronous support +-------------------- + +.. versionadded:: 2.2.0 + +Psycopg can issue asynchronous queries to a PostgreSQL database. An asynchronous +communication style is established passing the parameter *async*\=1 to the +`~psycopg2.connect()` function: the returned connection will work in +*asynchronous mode*. + +In asynchronous mode, a Psycopg connection will rely on the caller to poll the +socket file descriptor, checking if it is ready to accept data or if a query +result has been transferred and is ready to be read on the client. The caller +can use the method `~connection.fileno()` to get the connection file +descriptor and `~connection.poll()` to make communication proceed according to +the current connection state. + +The following is an example loop using methods `!fileno()` and `!poll()` +together with the Python :py:func:`~select.select` function in order to carry on +asynchronous operations with Psycopg:: + + def wait(conn): + while 1: + state = conn.poll() + if state == psycopg2.extensions.POLL_OK: + break + elif state == psycopg2.extensions.POLL_WRITE: + select.select([], [conn.fileno()], []) + elif state == psycopg2.extensions.POLL_READ: + select.select([conn.fileno()], [], []) + else: + raise psycopg2.OperationalError("poll() returned %s" % state) + +The above loop of course would block an entire application: in a real +asynchronous framework, `!select()` would be called on many file descriptors +waiting for any of them to be ready. Nonetheless the function can be used to +connect to a PostgreSQL server only using nonblocking commands and the +connection obtained can be used to perform further nonblocking queries. After +`!poll()` has returned `~psycopg2.extensions.POLL_OK`, and thus `!wait()` has +returned, the connection can be safely used: + + >>> aconn = psycopg2.connect(database='test', async=1) + >>> wait(aconn) + >>> acurs = aconn.cursor() + +Note that there are a few other requirements to be met in order to have a +completely non-blocking connection attempt: see the libpq documentation for +|PQconnectStart|_. + +.. |PQconnectStart| replace:: `!PQconnectStart()` +.. _PQconnectStart: http://www.postgresql.org/docs/current/static/libpq-connect.html#LIBPQ-PQCONNECTSTARTPARAMS + +The same loop should be also used to perform nonblocking queries: after +sending a query via `~cursor.execute()` or `~cursor.callproc()`, call +`!poll()` on the connection available from `cursor.connection` until it +returns `!POLL_OK`, at which point the query has been completely sent to the +server and, if it produced data, the results have been transferred to the +client and available using the regular cursor methods: + + >>> acurs.execute("SELECT pg_sleep(5); SELECT 42;") + >>> wait(acurs.connection) + >>> acurs.fetchone()[0] + 42 + +When an asynchronous query is being executed, `connection.isexecuting()` returns +`!True`. Two cursors can't execute concurrent queries on the same asynchronous +connection. + +There are several limitations in using asynchronous connections: the +connection is always in `~connection.autocommit` mode and it is not +possible to change it. So a +transaction is not implicitly started at the first query and is not possible +to use methods `~connection.commit()` and `~connection.rollback()`: you can +manually control transactions using `~cursor.execute()` to send database +commands such as :sql:`BEGIN`, :sql:`COMMIT` and :sql:`ROLLBACK`. Similarly +`~connection.set_session()` can't be used but it is still possible to invoke the +:sql:`SET` command with the proper :sql:`default_transaction_...` parameter. + +With asynchronous connections it is also not possible to use +`~connection.set_client_encoding()`, `~cursor.executemany()`, :ref:`large +objects `, :ref:`named cursors `. + +:ref:`COPY commands ` are not supported either in asynchronous mode, but +this will be probably implemented in a future release. + + + + +.. index:: + single: Greenlet + single: Coroutine + single: Eventlet + single: gevent + single: Wait callback + +.. _green-support: + +Support to coroutine libraries +------------------------------ + +.. versionadded:: 2.2.0 + +Psycopg can be used together with coroutine_\-based libraries, and participate +to cooperative multithreading. + +Coroutine-based libraries (such as Eventlet_ or gevent_) can usually patch the +Python standard library in order to enable a coroutine switch in the presence of +blocking I/O: the process is usually referred as making the system *green*, in +reference to the `green threads`_. + +Because Psycopg is a C extension module, it is not possible for coroutine +libraries to patch it: Psycopg instead enables cooperative multithreading by +allowing the registration of a *wait callback* using the +`psycopg2.extensions.set_wait_callback()` function. When a wait callback is +registered, Psycopg will use `libpq non-blocking calls`__ instead of the regular +blocking ones, and will delegate to the callback the responsibility to wait +for the socket to become readable or writable. + +Working this way, the caller does not have the complete freedom to schedule the +socket check whenever they want as with an :ref:`asynchronous connection +`, but has the advantage of maintaining a complete |DBAPI| +semantics: from the point of view of the end user, all Psycopg functions and +objects will work transparently in the coroutine environment (blocking the +calling green thread and giving other green threads the possibility to be +scheduled), allowing non modified code and third party libraries (such as +SQLAlchemy_) to be used in coroutine-based programs. + +.. warning:: + Psycopg connections are not *green thread safe* and can't be used + concurrently by different green threads. Trying to execute more than one + command at time using one cursor per thread will result in an error (or a + deadlock on versions before 2.4.2). + + Therefore, programmers are advised to either avoid sharing connections + between coroutines or to use a library-friendly lock to synchronize shared + connections, e.g. for pooling. + +Coroutine libraries authors should provide a callback implementation (and +possibly a method to register it) to make Psycopg as green as they want. An +example callback (using `!select()` to block) is provided as +`psycopg2.extras.wait_select()`: it boils down to something similar to:: + + def wait_select(conn): + while 1: + state = conn.poll() + if state == extensions.POLL_OK: + break + elif state == extensions.POLL_READ: + select.select([conn.fileno()], [], []) + elif state == extensions.POLL_WRITE: + select.select([], [conn.fileno()], []) + else: + raise OperationalError("bad state from poll: %s" % state) + +Providing callback functions for the single coroutine libraries is out of +psycopg2 scope, as the callback can be tied to the libraries' implementation +details. You can check the `psycogreen`_ project for further informations and +resources about the topic. + +.. _coroutine: http://en.wikipedia.org/wiki/Coroutine +.. _greenlet: http://pypi.python.org/pypi/greenlet +.. _green threads: http://en.wikipedia.org/wiki/Green_threads +.. _Eventlet: http://eventlet.net/ +.. _gevent: http://www.gevent.org/ +.. _SQLAlchemy: http://www.sqlalchemy.org/ +.. _psycogreen: http://bitbucket.org/dvarrazzo/psycogreen/ +.. __: http://www.postgresql.org/docs/current/static/libpq-async.html + +.. warning:: + + :ref:`COPY commands ` are currently not supported when a wait callback + is registered, but they will be probably implemented in a future release. + + :ref:`Large objects ` are not supported either: they are + not compatible with asynchronous connections. + + +.. testcode:: + :hide: + + aconn.close() + conn.rollback() + cur.execute("DROP TABLE atable") + conn.commit() + cur.close() + conn.close() diff -Nru psycopg2-2.0.13/doc/src/conf.py psycopg2-2.4.5/doc/src/conf.py --- psycopg2-2.0.13/doc/src/conf.py 1970-01-01 00:00:00.000000000 +0000 +++ psycopg2-2.4.5/doc/src/conf.py 2012-03-28 21:09:15.000000000 +0000 @@ -0,0 +1,261 @@ +# -*- coding: utf-8 -*- +# +# Psycopg documentation build configuration file, created by +# sphinx-quickstart on Sun Feb 7 13:48:41 2010. +# +# This file is execfile()d with the current directory set to its containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +import sys, os + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +sys.path.append(os.path.abspath('tools/lib')) + +# -- General configuration ----------------------------------------------------- + +# Add any Sphinx extension module names here, as strings. They can be extensions +# coming with Sphinx (named 'sphinx.ext.*') or your custom ones. +extensions = ['sphinx.ext.autodoc', 'sphinx.ext.todo', 'sphinx.ext.ifconfig', + 'sphinx.ext.doctest', 'sphinx.ext.intersphinx' ] + +# Specific extensions for Psycopg documentation. +extensions += [ 'dbapi_extension', 'sql_role' ] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix of source filenames. +source_suffix = '.rst' + +# The encoding of source files. +#source_encoding = 'utf-8' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = u'Psycopg' +copyright = u'2001-2011, Federico Di Gregorio. Documentation by Daniele Varrazzo' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = '2.0' + +# The full version, including alpha/beta/rc tags. +try: + import psycopg2 + release = psycopg2.__version__.split()[0] + version = '.'.join(release.split('.')[:2]) +except ImportError: + print "WARNING: couldn't import psycopg to read version." + release = version + +intersphinx_mapping = { + 'py': ('http://docs.python.org/', None), + 'py3': ('http://docs.python.org/3.2', None), + } + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +#language = None + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +#today = '' +# Else, today_fmt is used as the format for a strftime call. +#today_fmt = '%B %d, %Y' + +# List of documents that shouldn't be included in the build. +#unused_docs = [] + +# List of directories, relative to source directory, that shouldn't be searched +# for source files. +exclude_trees = ['_build', 'html'] + +# The reST default role (used for this markup: `text`) to use for all documents. +default_role = 'obj' + +# If true, '()' will be appended to :func: etc. cross-reference text. +#add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +#add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +#show_authors = False + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# A list of ignored prefixes for module index sorting. +#modindex_common_prefix = [] + +# Include TODO items in the documentation +todo_include_todos = False + +rst_epilog = """ +.. |DBAPI| replace:: DB API 2.0 + +.. _DBAPI: http://www.python.org/dev/peps/pep-0249/ + +.. _transaction isolation level: + http://www.postgresql.org/docs/current/static/transaction-iso.html + +.. _mx.DateTime: http://www.egenix.com/products/python/mxBase/mxDateTime/ + +.. |MVCC| replace:: :abbr:`MVCC (Multiversion concurrency control)` +""" + +# -- Options for HTML output --------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. Major themes that come with +# Sphinx are currently 'default' and 'sphinxdoc'. +html_theme = 'default' + +# The stylesheet to use with HTML output: this will include the original one +# adding a few classes. +html_style = 'psycopg.css' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +#html_theme_options = {} + +# Add any paths that contain custom themes here, relative to this directory. +#html_theme_path = [] + +# The name for this set of Sphinx documents. If None, it defaults to +# " v documentation". +#html_title = None + +# A shorter title for the navigation bar. Default is the same as html_title. +#html_short_title = None + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +#html_logo = None + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +#html_favicon = None + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + +# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, +# using the given strftime format. +#html_last_updated_fmt = '%b %d, %Y' + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +#html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +#html_sidebars = {} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +#html_additional_pages = {} + +# If false, no module index is generated. +#html_use_modindex = True + +# If false, no index is generated. +#html_use_index = True + +# If true, the index is split into individual pages for each letter. +#html_split_index = False + +# If true, links to the reST sources are added to the pages. +#html_show_sourcelink = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +#html_use_opensearch = '' + +# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml"). +#html_file_suffix = '' + +# Output file base name for HTML help builder. +htmlhelp_basename = 'psycopgdoc' + + +# -- Options for LaTeX output -------------------------------------------------- + +# The paper size ('letter' or 'a4'). +#latex_paper_size = 'letter' + +# The font size ('10pt', '11pt' or '12pt'). +#latex_font_size = '10pt' + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, author, documentclass [howto/manual]). +latex_documents = [ + ('index', 'psycopg.tex', u'Psycopg Documentation', + u'Federico Di Gregorio', 'manual'), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +#latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +#latex_use_parts = False + +# Additional stuff for the LaTeX preamble. +#latex_preamble = '' + +# Documents to append as an appendix to all manuals. +#latex_appendices = [] + +# If false, no module index is generated. +#latex_use_modindex = True + + +doctest_global_setup = """ + +import os +import psycopg2 + +def test_connect(): + try: + dsn = os.environ['PSYCOPG2_DSN'] + except KeyError: + assert False, "You need to set the environment variable PSYCOPG2_DSN" \ + " in order to test the documentation!" + return psycopg2.connect(dsn) + +conn = test_connect() +cur = conn.cursor() + +def drop_test_table(name): + cur.execute("SAVEPOINT drop_test_table;") + try: + cur.execute("DROP TABLE %s;" % name) + except: + cur.execute("ROLLBACK TO SAVEPOINT drop_test_table;") + conn.commit() + +def create_test_table(): + drop_test_table('test') + cur.execute("CREATE TABLE test (id SERIAL PRIMARY KEY, num INT, data TEXT)") + conn.commit() + +""" diff -Nru psycopg2-2.0.13/doc/src/connection.rst psycopg2-2.4.5/doc/src/connection.rst --- psycopg2-2.0.13/doc/src/connection.rst 1970-01-01 00:00:00.000000000 +0000 +++ psycopg2-2.4.5/doc/src/connection.rst 2012-03-28 21:09:15.000000000 +0000 @@ -0,0 +1,679 @@ +The ``connection`` class +======================== + +.. sectionauthor:: Daniele Varrazzo + +.. testsetup:: + + from pprint import pprint + import psycopg2.extensions + + drop_test_table('foo') + +.. class:: connection + + Handles the connection to a PostgreSQL database instance. It encapsulates + a database session. + + Connections are created using the factory function + `~psycopg2.connect()`. + + Connections are thread safe and can be shared among many threads. See + :ref:`thread-safety` for details. + + .. method:: cursor([name] [, cursor_factory] [, withhold]) + + Return a new `cursor` object using the connection. + + If *name* is specified, the returned cursor will be a :ref:`server + side cursor ` (also known as *named cursor*). + Otherwise it will be a regular *client side* cursor. By default a + :sql:`WITHOUT HOLD` cursor is created; to create a :sql:`WITH HOLD` + cursor, pass a `!True` value as the *withhold* parameter. See + :ref:`server-side-cursors`. + + The name can be a string not valid as a PostgreSQL identifier: for + example it may start with a digit and contain non-alphanumeric + characters and quotes. + + .. versionchanged:: 2.4 + previously only valid PostgreSQL identifiers were accepted as + cursor name. + + .. warning:: + It is unsafe to expose the *name* to an untrusted source, for + instance you shouldn't allow *name* to be read from a HTML form. + Consider it as part of the query, not as a query parameter. + + The *cursor_factory* argument can be used to create non-standard + cursors. The class returned should be a subclass of + `psycopg2.extensions.cursor`. See :ref:`subclassing-cursor` for + details. + + .. extension:: + + The `name` and `cursor_factory` parameters are Psycopg + extensions to the |DBAPI|. + + + .. index:: + pair: Transaction; Commit + + .. method:: commit() + + Commit any pending transaction to the database. Psycopg can be set to + perform automatic commits at each operation, see + `~connection.set_isolation_level()`. + + + .. index:: + pair: Transaction; Rollback + + .. method:: rollback() + + Roll back to the start of any pending transaction. Closing a + connection without committing the changes first will cause an implicit + rollback to be performed. + + + .. method:: close() + + Close the connection now (rather than whenever `del` is executed). + The connection will be unusable from this point forward; an + `~psycopg2.InterfaceError` will be raised if any operation is + attempted with the connection. The same applies to all cursor objects + trying to use the connection. Note that closing a connection without + committing the changes first will cause any pending change to be + discarded as if a :sql:`ROLLBACK` was performed (unless a different + isolation level has been selected: see + `~connection.set_isolation_level()`). + + .. index:: + single: PgBouncer; unclean server + + .. versionchanged:: 2.2 + previously an explicit :sql:`ROLLBACK` was issued by Psycopg on + `!close()`. The command could have been sent to the backend at an + inappropriate time, so Psycopg currently relies on the backend to + implicitly discard uncommitted changes. Some middleware are known + to behave incorrectly though when the connection is closed during + a transaction (when `~connection.status` is + `~psycopg2.extensions.STATUS_IN_TRANSACTION`), e.g. PgBouncer_ + reports an ``unclean server`` and discards the connection. To + avoid this problem you can ensure to terminate the transaction + with a `~connection.commit()`/`~connection.rollback()` before + closing. + + .. _PgBouncer: http://pgbouncer.projects.postgresql.org/ + + + .. index:: + single: Exceptions; In the connection class + + .. rubric:: Exceptions as connection class attributes + + The `!connection` also exposes as attributes the same exceptions + available in the `psycopg2` module. See :ref:`dbapi-exceptions`. + + + + .. index:: + single: Two-phase commit; methods + + .. rubric:: Two-phase commit support methods + + .. versionadded:: 2.3 + + .. seealso:: :ref:`tpc` for an introductory explanation of these methods. + + Note that PostgreSQL supports two-phase commit since release 8.1: these + methods raise `~psycopg2.NotSupportedError` if used with an older version + server. + + + .. _tpc_methods: + + .. method:: xid(format_id, gtrid, bqual) + + Returns a `~psycopg2.extensions.Xid` instance to be passed to the + `!tpc_*()` methods of this connection. The argument types and + constraints are explained in :ref:`tpc`. + + The values passed to the method will be available on the returned + object as the members `~psycopg2.extensions.Xid.format_id`, + `~psycopg2.extensions.Xid.gtrid`, `~psycopg2.extensions.Xid.bqual`. + The object also allows accessing to these members and unpacking as a + 3-items tuple. + + + .. method:: tpc_begin(xid) + + Begins a TPC transaction with the given transaction ID *xid*. + + This method should be called outside of a transaction (i.e. nothing + may have executed since the last `~connection.commit()` or + `~connection.rollback()` and `connection.status` is + `~psycopg2.extensions.STATUS_READY`). + + Furthermore, it is an error to call `!commit()` or `!rollback()` + within the TPC transaction: in this case a `~psycopg2.ProgrammingError` + is raised. + + The *xid* may be either an object returned by the `~connection.xid()` + method or a plain string: the latter allows to create a transaction + using the provided string as PostgreSQL transaction id. See also + `~connection.tpc_recover()`. + + + .. index:: + pair: Transaction; Prepare + + .. method:: tpc_prepare() + + Performs the first phase of a transaction started with + `~connection.tpc_begin()`. A `~psycopg2.ProgrammingError` is raised if + this method is used outside of a TPC transaction. + + After calling `!tpc_prepare()`, no statements can be executed until + `~connection.tpc_commit()` or `~connection.tpc_rollback()` will be + called. The `~connection.reset()` method can be used to restore the + status of the connection to `~psycopg2.extensions.STATUS_READY`: the + transaction will remain prepared in the database and will be + possible to finish it with `!tpc_commit(xid)` and + `!tpc_rollback(xid)`. + + .. seealso:: the |PREPARE TRANSACTION|_ PostgreSQL command. + + .. |PREPARE TRANSACTION| replace:: :sql:`PREPARE TRANSACTION` + .. _PREPARE TRANSACTION: http://www.postgresql.org/docs/current/static/sql-prepare-transaction.html + + + .. index:: + pair: Commit; Prepared + + .. method:: tpc_commit([xid]) + + When called with no arguments, `!tpc_commit()` commits a TPC + transaction previously prepared with `~connection.tpc_prepare()`. + + If `!tpc_commit()` is called prior to `!tpc_prepare()`, a single phase + commit is performed. A transaction manager may choose to do this if + only a single resource is participating in the global transaction. + + When called with a transaction ID *xid*, the database commits + the given transaction. If an invalid transaction ID is + provided, a `~psycopg2.ProgrammingError` will be raised. This form + should be called outside of a transaction, and is intended for use in + recovery. + + On return, the TPC transaction is ended. + + .. seealso:: the |COMMIT PREPARED|_ PostgreSQL command. + + .. |COMMIT PREPARED| replace:: :sql:`COMMIT PREPARED` + .. _COMMIT PREPARED: http://www.postgresql.org/docs/current/static/sql-commit-prepared.html + + + .. index:: + pair: Rollback; Prepared + + .. method:: tpc_rollback([xid]) + + When called with no arguments, `!tpc_rollback()` rolls back a TPC + transaction. It may be called before or after + `~connection.tpc_prepare()`. + + When called with a transaction ID *xid*, it rolls back the given + transaction. If an invalid transaction ID is provided, a + `~psycopg2.ProgrammingError` is raised. This form should be called + outside of a transaction, and is intended for use in recovery. + + On return, the TPC transaction is ended. + + .. seealso:: the |ROLLBACK PREPARED|_ PostgreSQL command. + + .. |ROLLBACK PREPARED| replace:: :sql:`ROLLBACK PREPARED` + .. _ROLLBACK PREPARED: http://www.postgresql.org/docs/current/static/sql-rollback-prepared.html + + + .. index:: + pair: Transaction; Recover + + .. method:: tpc_recover() + + Returns a list of `~psycopg2.extensions.Xid` representing pending + transactions, suitable for use with `tpc_commit()` or + `tpc_rollback()`. + + If a transaction was not initiated by Psycopg, the returned Xids will + have attributes `~psycopg2.extensions.Xid.format_id` and + `~psycopg2.extensions.Xid.bqual` set to `!None` and the + `~psycopg2.extensions.Xid.gtrid` set to the PostgreSQL transaction ID: such Xids are still + usable for recovery. Psycopg uses the same algorithm of the + `PostgreSQL JDBC driver`__ to encode a XA triple in a string, so + transactions initiated by a program using such driver should be + unpacked correctly. + + .. __: http://jdbc.postgresql.org/ + + Xids returned by `!tpc_recover()` also have extra attributes + `~psycopg2.extensions.Xid.prepared`, `~psycopg2.extensions.Xid.owner`, + `~psycopg2.extensions.Xid.database` populated with the values read + from the server. + + .. seealso:: the |pg_prepared_xacts|_ system view. + + .. |pg_prepared_xacts| replace:: `pg_prepared_xacts` + .. _pg_prepared_xacts: http://www.postgresql.org/docs/current/static/view-pg-prepared-xacts.html + + + + .. extension:: + + The above methods are the only ones defined by the |DBAPI| protocol. + The Psycopg connection objects exports the following additional + methods and attributes. + + + .. attribute:: closed + + Read-only attribute reporting whether the database connection is open + (0) or closed (1). + + + .. method:: cancel + + Cancel the current database operation. + + The method interrupts the processing of the current operation. If no + query is being executed, it does nothing. You can call this function + from a different thread than the one currently executing a database + operation, for instance if you want to cancel a long running query if a + button is pushed in the UI. Interrupting query execution will cause the + cancelled method to raise a + `~psycopg2.extensions.QueryCanceledError`. Note that the termination + of the query is not guaranteed to succeed: see the documentation for + |PQcancel|_. + + .. |PQcancel| replace:: `!PQcancel()` + .. _PQcancel: http://www.postgresql.org/docs/current/static/libpq-cancel.html#LIBPQ-PQCANCEL + + .. versionadded:: 2.3 + + + .. method:: reset + + Reset the connection to the default. + + The method rolls back an eventual pending transaction and executes the + PostgreSQL |RESET|_ and |SET SESSION AUTHORIZATION|__ to revert the + session to the default values. A two-phase commit transaction prepared + using `~connection.tpc_prepare()` will remain in the database + available for recover. + + .. |RESET| replace:: :sql:`RESET` + .. _RESET: http://www.postgresql.org/docs/current/static/sql-reset.html + + .. |SET SESSION AUTHORIZATION| replace:: :sql:`SET SESSION AUTHORIZATION` + .. __: http://www.postgresql.org/docs/current/static/sql-set-session-authorization.html + + .. versionadded:: 2.0.12 + + + .. attribute:: dsn + + Read-only string containing the connection string used by the + connection. + + + .. index:: + pair: Transaction; Autocommit + pair: Transaction; Isolation level + + .. method:: set_session([isolation_level,] [readonly,] [deferrable,] [autocommit]) + + Set one or more parameters for the next transactions or statements in + the current session. See |SET TRANSACTION|_ for further details. + + .. |SET TRANSACTION| replace:: :sql:`SET TRANSACTION` + .. _SET TRANSACTION: http://www.postgresql.org/docs/current/static/sql-set-transaction.html + + :param isolation_level: set the `isolation level`_ for the next + transactions/statements. The value can be one of the + :ref:`constants ` defined in the + `~psycopg2.extensions` module or one of the literal values + ``READ UNCOMMITTED``, ``READ COMMITTED``, ``REPEATABLE READ``, + ``SERIALIZABLE``. + :param readonly: if `!True`, set the connection to read only; + read/write if `!False`. + :param deferrable: if `!True`, set the connection to deferrable; + non deferrable if `!False`. Only available from PostgreSQL 9.1. + :param autocommit: switch the connection to autocommit mode: not a + PostgreSQL session setting but an alias for setting the + `autocommit` attribute. + + The parameters *isolation_level*, *readonly* and *deferrable* also + accept the string ``DEFAULT`` as a value: the effect is to reset the + parameter to the server default. + + .. _isolation level: + http://www.postgresql.org/docs/current/static/transaction-iso.html + + The function must be invoked with no transaction in progress. At every + function invocation, only the specified parameters are changed. + + The default for the values are defined by the server configuration: + see values for |default_transaction_isolation|__, + |default_transaction_read_only|__, |default_transaction_deferrable|__. + + .. |default_transaction_isolation| replace:: :sql:`default_transaction_isolation` + .. __: http://www.postgresql.org/docs/current/static/runtime-config-client.html#GUC-DEFAULT-TRANSACTION-ISOLATION + .. |default_transaction_read_only| replace:: :sql:`default_transaction_read_only` + .. __: http://www.postgresql.org/docs/current/static/runtime-config-client.html#GUC-DEFAULT-TRANSACTION-READ-ONLY + .. |default_transaction_deferrable| replace:: :sql:`default_transaction_deferrable` + .. __: http://www.postgresql.org/docs/current/static/runtime-config-client.html#GUC-DEFAULT-TRANSACTION-DEFERRABLE + + .. note:: + + There is currently no builtin method to read the current value for + the parameters: use :sql:`SHOW default_transaction_...` to read + the values from the backend. + + .. versionadded:: 2.4.2 + + + .. attribute:: autocommit + + Read/write attribute: if `!True`, no transaction is handled by the + driver and every statement sent to the backend has immediate effect; + if `!False` a new transaction is started at the first command + execution: the methods `commit()` or `rollback()` must be manually + invoked to terminate the transaction. + + The autocommit mode is useful to execute commands requiring to be run + outside a transaction, such as :sql:`CREATE DATABASE` or + :sql:`VACUUM`. + + The default is `!False` (manual commit) as per DBAPI specification. + + .. warning:: + + By default, any query execution, including a simple :sql:`SELECT` + will start a transaction: for long-running programs, if no further + action is taken, the session will remain "idle in transaction", a + condition non desiderable for several reasons (locks are held by + the session, tables bloat...). For long lived scripts, either + ensure to terminate a transaction as soon as possible or use an + autocommit connection. + + .. versionadded:: 2.4.2 + + + .. attribute:: isolation_level + .. method:: set_isolation_level(level) + + .. note:: + + From version 2.4.2, `set_session()` and `autocommit`, offer + finer control on the transaction characteristics. + + Read or set the `transaction isolation level`_ for the current session. + The level defines the different phenomena that can happen in the + database between concurrent transactions. + + The value set or read is an integer: symbolic constants are defined in + the module `psycopg2.extensions`: see + :ref:`isolation-level-constants` for the available values. + + The default level is :sql:`READ COMMITTED`: at this level a + transaction is automatically started the first time a database command + is executed. If you want an *autocommit* mode, switch to + `~psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT` before + executing any command:: + + >>> conn.set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT) + + See also :ref:`transactions-control`. + + .. index:: + pair: Client; Encoding + + .. attribute:: encoding + .. method:: set_client_encoding(enc) + + Read or set the client encoding for the current session. The default + is the encoding defined by the database. It should be one of the + `characters set supported by PostgreSQL`__ + + .. __: http://www.postgresql.org/docs/current/static/multibyte.html + + + .. index:: + pair: Client; Logging + + .. attribute:: notices + + A list containing all the database messages sent to the client during + the session. + + .. doctest:: + :options: NORMALIZE_WHITESPACE + + >>> cur.execute("CREATE TABLE foo (id serial PRIMARY KEY);") + >>> pprint(conn.notices) + ['NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "foo_pkey" for table "foo"\n', + 'NOTICE: CREATE TABLE will create implicit sequence "foo_id_seq" for serial column "foo.id"\n'] + + To avoid a leak in case excessive notices are generated, only the last + 50 messages are kept. + + You can configure what messages to receive using `PostgreSQL logging + configuration parameters`__ such as ``log_statement``, + ``client_min_messages``, ``log_min_duration_statement`` etc. + + .. __: http://www.postgresql.org/docs/current/static/runtime-config-logging.html + + + .. attribute:: notifies + + List of `~psycopg2.extensions.Notify` objects containing asynchronous + notifications received by the session. + + For other details see :ref:`async-notify`. + + .. versionchanged:: 2.3 + Notifications are instances of the `!Notify` object. Previously the + list was composed by 2 items tuples :samp:`({pid},{channel})` and + the payload was not accessible. To keep backward compatibility, + `!Notify` objects can still be accessed as 2 items tuples. + + .. index:: + pair: Backend; PID + + .. method:: get_backend_pid() + + Returns the process ID (PID) of the backend server process handling + this connection. + + Note that the PID belongs to a process executing on the database + server host, not the local host! + + .. seealso:: libpq docs for `PQbackendPID()`__ for details. + + .. __: http://www.postgresql.org/docs/current/static/libpq-status.html#LIBPQ-PQBACKENDPID + + .. versionadded:: 2.0.8 + + + .. index:: + pair: Server; Parameters + + .. method:: get_parameter_status(parameter) + + Look up a current parameter setting of the server. + + Potential values for ``parameter`` are: ``server_version``, + ``server_encoding``, ``client_encoding``, ``is_superuser``, + ``session_authorization``, ``DateStyle``, ``TimeZone``, + ``integer_datetimes``, and ``standard_conforming_strings``. + + If server did not report requested parameter, return `!None`. + + .. seealso:: libpq docs for `PQparameterStatus()`__ for details. + + .. __: http://www.postgresql.org/docs/current/static/libpq-status.html#LIBPQ-PQPARAMETERSTATUS + + .. versionadded:: 2.0.12 + + + .. index:: + pair: Transaction; Status + + .. method:: get_transaction_status() + + Return the current session transaction status as an integer. Symbolic + constants for the values are defined in the module + `psycopg2.extensions`: see :ref:`transaction-status-constants` + for the available values. + + .. seealso:: libpq docs for `PQtransactionStatus()`__ for details. + + .. __: http://www.postgresql.org/docs/current/static/libpq-status.html#LIBPQ-PQTRANSACTIONSTATUS + + + .. index:: + pair: Protocol; Version + + .. attribute:: protocol_version + + A read-only integer representing frontend/backend protocol being used. + Currently Psycopg supports only protocol 3, which allows connection + to PostgreSQL server from version 7.4. Psycopg versions previous than + 2.3 support both protocols 2 and 3. + + .. seealso:: libpq docs for `PQprotocolVersion()`__ for details. + + .. __: http://www.postgresql.org/docs/current/static/libpq-status.html#LIBPQ-PQPROTOCOLVERSION + + .. versionadded:: 2.0.12 + + + .. index:: + pair: Server; Version + + .. attribute:: server_version + + A read-only integer representing the backend version. + + The number is formed by converting the major, minor, and revision + numbers into two-decimal-digit numbers and appending them together. + For example, version 8.1.5 will be returned as ``80105``. + + .. seealso:: libpq docs for `PQserverVersion()`__ for details. + + .. __: http://www.postgresql.org/docs/current/static/libpq-status.html#LIBPQ-PQSERVERVERSION + + .. versionadded:: 2.0.12 + + + .. index:: + pair: Connection; Status + + .. attribute:: status + + A read-only integer representing the status of the connection. + Symbolic constants for the values are defined in the module + `psycopg2.extensions`: see :ref:`connection-status-constants` + for the available values. + + + .. method:: lobject([oid [, mode [, new_oid [, new_file [, lobject_factory]]]]]) + + Return a new database large object as a `~psycopg2.extensions.lobject` + instance. + + See :ref:`large-objects` for an overview. + + :param oid: The OID of the object to read or write. 0 to create + a new large object and and have its OID assigned automatically. + :param mode: Access mode to the object, see below. + :param new_oid: Create a new object using the specified OID. The + function raises `~psycopg2.OperationalError` if the OID is already + in use. Default is 0, meaning assign a new one automatically. + :param new_file: The name of a file to be imported in the the database + (using the |lo_import|_ function) + :param lobject_factory: Subclass of + `~psycopg2.extensions.lobject` to be instantiated. + + .. |lo_import| replace:: `!lo_import()` + .. _lo_import: http://www.postgresql.org/docs/current/static/lo-interfaces.html#LO-IMPORT + + Available values for *mode* are: + + ======= ========= + *mode* meaning + ======= ========= + ``r`` Open for read only + ``w`` Open for write only + ``rw`` Open for read/write + ``n`` Don't open the file + ``b`` Don't decode read data (return data as `!str` in Python 2 or `!bytes` in Python 3) + ``t`` Decode read data according to `connection.encoding` (return data as `!unicode` in Python 2 or `!str` in Python 3) + ======= ========= + + ``b`` and ``t`` can be specified together with a read/write mode. If + neither ``b`` nor ``t`` is specified, the default is ``b`` in Python 2 + and ``t`` in Python 3. + + .. versionadded:: 2.0.8 + + .. versionchanged:: 2.4 added ``b`` and ``t`` mode and unicode + support. + + + .. rubric:: Methods related to asynchronous support. + + .. versionadded:: 2.2.0 + + .. seealso:: :ref:`async-support` and :ref:`green-support`. + + + .. attribute:: async + + Read only attribute: 1 if the connection is asynchronous, 0 otherwise. + + + .. method:: poll() + + Used during an asynchronous connection attempt, or when a cursor is + executing a query on an asynchronous connection, make communication + proceed if it wouldn't block. + + Return one of the constants defined in :ref:`poll-constants`. If it + returns `~psycopg2.extensions.POLL_OK` then the connection has been + estabilished or the query results are available on the client. + Otherwise wait until the file descriptor returned by `fileno()` is + ready to read or to write, as explained in :ref:`async-support`. + `poll()` should be also used by the function installed by + `~psycopg2.extensions.set_wait_callback()` as explained in + :ref:`green-support`. + + `poll()` is also used to receive asynchronous notifications from the + database: see :ref:`async-notify` from further details. + + + .. method:: fileno() + + Return the file descriptor underlying the connection: useful to read + its status during asynchronous communication. + + + .. method:: isexecuting() + + Return `!True` if the connection is executing an asynchronous operation. + + +.. testcode:: + :hide: + + conn.rollback() diff -Nru psycopg2-2.0.13/doc/src/cursor.rst psycopg2-2.4.5/doc/src/cursor.rst --- psycopg2-2.0.13/doc/src/cursor.rst 1970-01-01 00:00:00.000000000 +0000 +++ psycopg2-2.4.5/doc/src/cursor.rst 2012-03-28 21:09:15.000000000 +0000 @@ -0,0 +1,555 @@ +The ``cursor`` class +==================== + +.. sectionauthor:: Daniele Varrazzo + +.. testsetup:: * + + from StringIO import StringIO + import sys + + create_test_table() + + # initial data + cur.executemany("INSERT INTO test (num, data) VALUES (%s, %s)", + [(100, "abc'def"), (None, 'dada'), (42, 'bar')]) + conn.commit() + + +.. class:: cursor + + Allows Python code to execute PostgreSQL command in a database session. + Cursors are created by the `connection.cursor()` method: they are + bound to the connection for the entire lifetime and all the commands are + executed in the context of the database session wrapped by the connection. + + Cursors created from the same connection are not isolated, i.e., any + changes done to the database by a cursor are immediately visible by the + other cursors. Cursors created from different connections can or can not + be isolated, depending on the connections' :ref:`isolation level + `. See also `~connection.rollback()` and + `~connection.commit()` methods. + + Cursors are *not* thread safe: a multithread application can create + many cursors from the same connection and should use each cursor from + a single thread. See :ref:`thread-safety` for details. + + + .. attribute:: description + + This read-only attribute is a sequence of 7-item sequences. + + Each of these sequences is a named tuple (a regular tuple if + :func:`collections.namedtuple` is not available) containing information + describing one result column: + + 0. `!name`: the name of the column returned. + 1. `!type_code`: the PostgreSQL OID of the column. You can use the + |pg_type|_ system table to get more informations about the type. + This is the value used by Psycopg to decide what Python type use + to represent the value. See also + :ref:`type-casting-from-sql-to-python`. + 2. `!display_size`: the actual length of the column in bytes. + Obtaining this value is computationally intensive, so it is + always `!None` unless the :envvar:`PSYCOPG_DISPLAY_SIZE` parameter + is set at compile time. See also PQgetlength_. + 3. `!internal_size`: the size in bytes of the column associated to + this column on the server. Set to a negative value for + variable-size types See also PQfsize_. + 4. `!precision`: total number of significant digits in columns of + type |NUMERIC|_. `!None` for other types. + 5. `!scale`: count of decimal digits in the fractional part in + columns of type |NUMERIC|. `!None` for other types. + 6. `!null_ok`: always `!None` as not easy to retrieve from the libpq. + + This attribute will be `!None` for operations that do not return rows + or if the cursor has not had an operation invoked via the + |execute*|_ methods yet. + + .. |pg_type| replace:: :sql:`pg_type` + .. _pg_type: http://www.postgresql.org/docs/current/static/catalog-pg-type.html + .. _PQgetlength: http://www.postgresql.org/docs/current/static/libpq-exec.html#LIBPQ-PQGETLENGTH + .. _PQfsize: http://www.postgresql.org/docs/current/static/libpq-exec.html#LIBPQ-PQFSIZE + .. _NUMERIC: http://www.postgresql.org/docs/current/static/datatype-numeric.html#DATATYPE-NUMERIC-DECIMAL + .. |NUMERIC| replace:: :sql:`NUMERIC` + + .. versionchanged:: 2.4 + if possible, columns descriptions are named tuple instead of + regular tuples. + + .. method:: close() + + Close the cursor now (rather than whenever `del` is executed). + The cursor will be unusable from this point forward; an + `~psycopg2.InterfaceError` will be raised if any operation is + attempted with the cursor. + + .. attribute:: closed + + Read-only boolean attribute: specifies if the cursor is closed + (`!True`) or not (`!False`). + + .. extension:: + + The `closed` attribute is a Psycopg extension to the + |DBAPI|. + + .. versionadded:: 2.0.7 + + + .. attribute:: connection + + Read-only attribute returning a reference to the `connection` + object on which the cursor was created. + + + .. attribute:: name + + Read-only attribute containing the name of the cursor if it was + creates as named cursor by `connection.cursor()`, or `!None` if + it is a client side cursor. See :ref:`server-side-cursors`. + + .. extension:: + + The `name` attribute is a Psycopg extension to the |DBAPI|. + + + .. attribute:: withhold + + Read/write attribute: specifies if a named cursor lifetime should + extend outside of the current transaction, i.e., it is possible to + fetch from the cursor even after a `commection.commit()` (but not after + a `connection.rollback()`). See :ref:`server-side-cursors` + + .. versionadded:: 2.4.3 + + .. extension:: + + The `withhold` attribute is a Psycopg extension to the |DBAPI|. + + + .. |execute*| replace:: `execute*()` + + .. _execute*: + + .. rubric:: Commands execution methods + + + .. method:: execute(operation [, parameters]) + + Prepare and execute a database operation (query or command). + + Parameters may be provided as sequence or mapping and will be bound to + variables in the operation. Variables are specified either with + positional (``%s``) or named (:samp:`%({name})s`) placeholders. See + :ref:`query-parameters`. + + The method returns `!None`. If a query was executed, the returned + values can be retrieved using |fetch*|_ methods. + + + .. method:: executemany(operation, seq_of_parameters) + + Prepare a database operation (query or command) and then execute it + against all parameter tuples or mappings found in the sequence + `seq_of_parameters`. + + The function is mostly useful for commands that update the database: + any result set returned by the query is discarded. + + Parameters are bounded to the query using the same rules described in + the `~cursor.execute()` method. + + + .. method:: callproc(procname [, parameters]) + + Call a stored database procedure with the given name. The sequence of + parameters must contain one entry for each argument that the procedure + expects. The result of the call is returned as modified copy of the + input sequence. Input parameters are left untouched, output and + input/output parameters replaced with possibly new values. + + The procedure may also provide a result set as output. This must then + be made available through the standard |fetch*|_ methods. + + + .. method:: mogrify(operation [, parameters]) + + Return a query string after arguments binding. The string returned is + exactly the one that would be sent to the database running the + `~cursor.execute()` method or similar. + + >>> cur.mogrify("INSERT INTO test (num, data) VALUES (%s, %s)", (42, 'bar')) + "INSERT INTO test (num, data) VALUES (42, E'bar')" + + .. extension:: + + The `mogrify()` method is a Psycopg extension to the |DBAPI|. + + .. method:: setinputsizes(sizes) + + This method is exposed in compliance with the |DBAPI|. It currently + does nothing but it is safe to call it. + + + + .. |fetch*| replace:: `!fetch*()` + + .. _fetch*: + + .. rubric:: Results retrieval methods + + + The following methods are used to read data from the database after an + `~cursor.execute()` call. + + .. _cursor-iterable: + + .. note:: + + `cursor` objects are iterable, so, instead of calling + explicitly `~cursor.fetchone()` in a loop, the object itself can + be used: + + >>> cur.execute("SELECT * FROM test;") + >>> for record in cur: + ... print record + ... + (1, 100, "abc'def") + (2, None, 'dada') + (3, 42, 'bar') + + .. versionchanged:: 2.4 + iterating over a :ref:`named cursor ` + fetches `~cursor.itersize` records at time from the backend. + Previously only one record was fetched per roundtrip, resulting + in a large overhead. + + .. method:: fetchone() + + Fetch the next row of a query result set, returning a single tuple, + or `!None` when no more data is available: + + >>> cur.execute("SELECT * FROM test WHERE id = %s", (3,)) + >>> cur.fetchone() + (3, 42, 'bar') + + A `~psycopg2.ProgrammingError` is raised if the previous call + to |execute*|_ did not produce any result set or no call was issued + yet. + + + .. method:: fetchmany([size=cursor.arraysize]) + + Fetch the next set of rows of a query result, returning a list of + tuples. An empty list is returned when no more rows are available. + + The number of rows to fetch per call is specified by the parameter. + If it is not given, the cursor's `~cursor.arraysize` determines + the number of rows to be fetched. The method should try to fetch as + many rows as indicated by the size parameter. If this is not possible + due to the specified number of rows not being available, fewer rows + may be returned: + + >>> cur.execute("SELECT * FROM test;") + >>> cur.fetchmany(2) + [(1, 100, "abc'def"), (2, None, 'dada')] + >>> cur.fetchmany(2) + [(3, 42, 'bar')] + >>> cur.fetchmany(2) + [] + + A `~psycopg2.ProgrammingError` is raised if the previous call to + |execute*|_ did not produce any result set or no call was issued yet. + + Note there are performance considerations involved with the size + parameter. For optimal performance, it is usually best to use the + `~cursor.arraysize` attribute. If the size parameter is used, + then it is best for it to retain the same value from one + `fetchmany()` call to the next. + + + .. method:: fetchall() + + Fetch all (remaining) rows of a query result, returning them as a list + of tuples. An empty list is returned if there is no more record to + fetch. + + >>> cur.execute("SELECT * FROM test;") + >>> cur.fetchall() + [(1, 100, "abc'def"), (2, None, 'dada'), (3, 42, 'bar')] + + A `~psycopg2.ProgrammingError` is raised if the previous call to + |execute*|_ did not produce any result set or no call was issued yet. + + + .. method:: scroll(value [, mode='relative']) + + Scroll the cursor in the result set to a new position according + to mode. + + If `mode` is ``relative`` (default), value is taken as offset to + the current position in the result set, if set to ``absolute``, + value states an absolute target position. + + If the scroll operation would leave the result set, a + `~psycopg2.ProgrammingError` is raised and the cursor position is + not changed. + + The method can be used both for client-side cursors and + :ref:`server-side cursors `. + + .. note:: + + According to the |DBAPI|_, the exception raised for a cursor out + of bound should have been `!IndexError`. The best option is + probably to catch both exceptions in your code:: + + try: + cur.scroll(1000 * 1000) + except (ProgrammingError, IndexError), exc: + deal_with_it(exc) + + + .. attribute:: arraysize + + This read/write attribute specifies the number of rows to fetch at a + time with `~cursor.fetchmany()`. It defaults to 1 meaning to fetch + a single row at a time. + + + .. attribute:: itersize + + Read/write attribute specifying the number of rows to fetch from the + backend at each network roundtrip during :ref:`iteration + ` on a :ref:`named cursor `. The + default is 2000. + + .. versionadded:: 2.4 + + .. extension:: + + The `itersize` attribute is a Psycopg extension to the |DBAPI|. + + + .. attribute:: rowcount + + This read-only attribute specifies the number of rows that the last + |execute*|_ produced (for :abbr:`DQL (Data Query Language)` statements + like :sql:`SELECT`) or affected (for + :abbr:`DML (Data Manipulation Language)` statements like :sql:`UPDATE` + or :sql:`INSERT`). + + The attribute is -1 in case no |execute*| has been performed on + the cursor or the row count of the last operation if it can't be + determined by the interface. + + .. note:: + The |DBAPI|_ interface reserves to redefine the latter case to + have the object return `!None` instead of -1 in future versions + of the specification. + + + .. attribute:: rownumber + + This read-only attribute provides the current 0-based index of the + cursor in the result set or `!None` if the index cannot be + determined. + + The index can be seen as index of the cursor in a sequence (the result + set). The next fetch operation will fetch the row indexed by + `rownumber` in that sequence. + + + .. index:: oid + + .. attribute:: lastrowid + + This read-only attribute provides the OID of the last row inserted + by the cursor. If the table wasn't created with OID support or the + last operation is not a single record insert, the attribute is set to + `!None`. + + .. note:: + + PostgreSQL currently advices to not create OIDs on the tables and + the default for |CREATE-TABLE|__ is to not support them. The + |INSERT-RETURNING|__ syntax available from PostgreSQL 8.3 allows + more flexibility. + + .. |CREATE-TABLE| replace:: :sql:`CREATE TABLE` + .. __: http://www.postgresql.org/docs/current/static/sql-createtable.html + + .. |INSERT-RETURNING| replace:: :sql:`INSERT ... RETURNING` + .. __: http://www.postgresql.org/docs/current/static/sql-insert.html + + + .. attribute:: query + + Read-only attribute containing the body of the last query sent to the + backend (including bound arguments). `!None` if no query has been + executed yet: + + >>> cur.execute("INSERT INTO test (num, data) VALUES (%s, %s)", (42, 'bar')) + >>> cur.query + "INSERT INTO test (num, data) VALUES (42, E'bar')" + + .. extension:: + + The `query` attribute is a Psycopg extension to the |DBAPI|. + + + .. attribute:: statusmessage + + Read-only attribute containing the message returned by the last + command: + + >>> cur.execute("INSERT INTO test (num, data) VALUES (%s, %s)", (42, 'bar')) + >>> cur.statusmessage + 'INSERT 0 1' + + .. extension:: + + The `statusmessage` attribute is a Psycopg extension to the + |DBAPI|. + + + .. method:: cast(oid, s) + + Convert a value from the PostgreSQL string representation to a Python + object. + + Use the most specific of the typecasters registered by + `~psycopg2.extensions.register_type()`. + + .. versionadded:: 2.4 + + .. extension:: + + The `cast()` method is a Psycopg extension to the |DBAPI|. + + + .. attribute:: tzinfo_factory + + The time zone factory used to handle data types such as + :sql:`TIMESTAMP WITH TIME ZONE`. It should be a `~datetime.tzinfo` + object. A few implementations are available in the `psycopg2.tz` + module. + + + .. method:: nextset() + + This method is not supported (PostgreSQL does not have multiple data + sets) and will raise a `~psycopg2.NotSupportedError` exception. + + + .. method:: setoutputsize(size [, column]) + + This method is exposed in compliance with the |DBAPI|. It currently + does nothing but it is safe to call it. + + + + .. rubric:: COPY-related methods + + .. extension:: + + The :sql:`COPY` command is a PostgreSQL extension to the SQL standard. + As such, its support is a Psycopg extension to the |DBAPI|. + + .. method:: copy_from(file, table, sep='\\t', null='\\\\N', size=8192, columns=None) + + Read data *from* the file-like object *file* appending them to + the table named *table*. See :ref:`copy` for an overview. + + :param file: file-like object to read data from. It must have both + `!read()` and `!readline()` methods. + :param table: name of the table to copy data into. + :param sep: columns separator expected in the file. Defaults to a tab. + :param null: textual representation of :sql:`NULL` in the file. + The default is the two characters string ``\N``. + :param size: size of the buffer used to read from the file. + :param columns: iterable with name of the columns to import. + The length and types should match the content of the file to read. + If not specified, it is assumed that the entire table matches the + file structure. + + Example:: + + >>> f = StringIO("42\tfoo\n74\tbar\n") + >>> cur.copy_from(f, 'test', columns=('num', 'data')) + >>> cur.execute("select * from test where id > 5;") + >>> cur.fetchall() + [(6, 42, 'foo'), (7, 74, 'bar')] + + .. versionchanged:: 2.0.6 + added the *columns* parameter. + + .. versionchanged:: 2.4 + data read from files implementing the `io.TextIOBase` interface + are encoded in the connection `~connection.encoding` when sent to + the backend. + + .. method:: copy_to(file, table, sep='\\t', null='\\\\N', columns=None) + + Write the content of the table named *table* *to* the file-like + object *file*. See :ref:`copy` for an overview. + + :param file: file-like object to write data into. It must have a + `!write()` method. + :param table: name of the table to copy data from. + :param sep: columns separator expected in the file. Defaults to a tab. + :param null: textual representation of :sql:`NULL` in the file. + The default is the two characters string ``\N``. + :param columns: iterable with name of the columns to export. + If not specified, export all the columns. + + Example:: + + >>> cur.copy_to(sys.stdout, 'test', sep="|") + 1|100|abc'def + 2|\N|dada + ... + + .. versionchanged:: 2.0.6 + added the *columns* parameter. + + .. versionchanged:: 2.4 + data sent to files implementing the `io.TextIOBase` interface + are decoded in the connection `~connection.encoding` when read + from the backend. + + + .. method:: copy_expert(sql, file, size=8192) + + Submit a user-composed :sql:`COPY` statement. The method is useful to + handle all the parameters that PostgreSQL makes available (see + |COPY|__ command documentation). + + :param sql: the :sql:`COPY` statement to execute. + :param file: a file-like object; must be a readable file for + :sql:`COPY FROM` or an writable file for :sql:`COPY TO`. + :param size: size of the read buffer to be used in :sql:`COPY FROM`. + + Example: + + >>> cur.copy_expert("COPY test TO STDOUT WITH CSV HEADER", sys.stdout) + id,num,data + 1,100,abc'def + 2,,dada + ... + + .. |COPY| replace:: :sql:`COPY` + .. __: http://www.postgresql.org/docs/current/static/sql-copy.html + + .. versionadded:: 2.0.6 + + .. versionchanged:: 2.4 + files implementing the `io.TextIOBase` interface are dealt with + using Unicode data instead of bytes. + + +.. testcode:: + :hide: + + conn.rollback() diff -Nru psycopg2-2.0.13/doc/src/errorcodes.rst psycopg2-2.4.5/doc/src/errorcodes.rst --- psycopg2-2.0.13/doc/src/errorcodes.rst 1970-01-01 00:00:00.000000000 +0000 +++ psycopg2-2.4.5/doc/src/errorcodes.rst 2012-03-28 21:09:15.000000000 +0000 @@ -0,0 +1,76 @@ +`psycopg2.errorcodes` -- Error codes defined by PostgreSQL +=============================================================== + +.. sectionauthor:: Daniele Varrazzo + +.. index:: + single: Error; Codes + +.. module:: psycopg2.errorcodes + +.. testsetup:: * + + from psycopg2 import errorcodes + +.. versionadded:: 2.0.6 + +This module contains symbolic names for all PostgreSQL error codes and error +classes codes. Subclasses of `~psycopg2.Error` make the PostgreSQL error +code available in the `~psycopg2.Error.pgcode` attribute. + +From PostgreSQL documentation: + + All messages emitted by the PostgreSQL server are assigned five-character + error codes that follow the SQL standard's conventions for :sql:`SQLSTATE` + codes. Applications that need to know which error condition has occurred + should usually test the error code, rather than looking at the textual + error message. The error codes are less likely to change across + PostgreSQL releases, and also are not subject to change due to + localization of error messages. Note that some, but not all, of the error + codes produced by PostgreSQL are defined by the SQL standard; some + additional error codes for conditions not defined by the standard have + been invented or borrowed from other databases. + + According to the standard, the first two characters of an error code + denote a class of errors, while the last three characters indicate a + specific condition within that class. Thus, an application that does not + recognize the specific error code can still be able to infer what to do + from the error class. + +.. seealso:: `PostgreSQL Error Codes table`__ + + .. __: http://www.postgresql.org/docs/current/static/errcodes-appendix.html#ERRCODES-TABLE + + +An example of the available constants defined in the module: + + >>> errorcodes.CLASS_SYNTAX_ERROR_OR_ACCESS_RULE_VIOLATION + '42' + >>> errorcodes.UNDEFINED_TABLE + '42P01' + +Constants representing all the error values documented by PostgreSQL versions +between 8.1 and 9.1 are included in the module. + + +.. autofunction:: lookup(code) + + .. doctest:: + + >>> try: + ... cur.execute("SELECT ouch FROM aargh;") + ... except Exception, e: + ... pass + ... + >>> errorcodes.lookup(e.pgcode[:2]) + 'CLASS_SYNTAX_ERROR_OR_ACCESS_RULE_VIOLATION' + >>> errorcodes.lookup(e.pgcode) + 'UNDEFINED_TABLE' + + .. versionadded:: 2.0.14 + + +.. testcode:: + :hide: + + conn.rollback() diff -Nru psycopg2-2.0.13/doc/src/extensions.rst psycopg2-2.4.5/doc/src/extensions.rst --- psycopg2-2.0.13/doc/src/extensions.rst 1970-01-01 00:00:00.000000000 +0000 +++ psycopg2-2.4.5/doc/src/extensions.rst 2012-03-28 21:09:15.000000000 +0000 @@ -0,0 +1,691 @@ +`psycopg2.extensions` -- Extensions to the DB API +====================================================== + +.. sectionauthor:: Daniele Varrazzo + +.. module:: psycopg2.extensions + +.. testsetup:: * + + from psycopg2.extensions import AsIs, Binary, QuotedString, ISOLATION_LEVEL_AUTOCOMMIT + +The module contains a few objects and function extending the minimum set of +functionalities defined by the |DBAPI|_. + + +.. class:: connection + + Is the class usually returned by the `~psycopg2.connect()` function. + It is exposed by the `extensions` module in order to allow + subclassing to extend its behaviour: the subclass should be passed to the + `!connect()` function using the `connection_factory` parameter. + See also :ref:`subclassing-connection`. + + Subclasses should have constructor signature :samp:`({dsn}, {async}=0)`. + + For a complete description of the class, see `connection`. + +.. class:: cursor + + It is the class usually returnded by the `connection.cursor()` + method. It is exposed by the `extensions` module in order to allow + subclassing to extend its behaviour: the subclass should be passed to the + `!cursor()` method using the `cursor_factory` parameter. See + also :ref:`subclassing-cursor`. + + For a complete description of the class, see `cursor`. + +.. class:: lobject(conn [, oid [, mode [, new_oid [, new_file ]]]]) + + Wrapper for a PostgreSQL large object. See :ref:`large-objects` for an + overview. + + The class can be subclassed: see the `connection.lobject()` to know + how to specify a `!lobject` subclass. + + .. versionadded:: 2.0.8 + + .. attribute:: oid + + Database OID of the object. + + .. attribute:: mode + + The mode the database was open. See `connection.lobject()` for a + description of the available modes. + + .. method:: read(bytes=-1) + + Read a chunk of data from the current file position. If -1 (default) + read all the remaining data. + + The result is an Unicode string (decoded according to + `connection.encoding`) if the file was open in ``t`` mode, a bytes + string for ``b`` mode. + + .. versionchanged:: 2.4 + added Unicode support. + + .. method:: write(str) + + Write a string to the large object. Return the number of bytes + written. Unicode strings are encoded in the `connection.encoding` + before writing. + + .. versionchanged:: 2.4 + added Unicode support. + + .. method:: export(file_name) + + Export the large object content to the file system. + + The method uses the efficient |lo_export|_ libpq function. + + .. |lo_export| replace:: `!lo_export()` + .. _lo_export: http://www.postgresql.org/docs/current/static/lo-interfaces.html#LO-EXPORT + + .. method:: seek(offset, whence=0) + + Set the lobject current position. + + .. method:: tell() + + Return the lobject current position. + + .. method:: truncate(len=0) + + .. versionadded:: 2.2.0 + + Truncate the lobject to the given size. + + The method will only be available if Psycopg has been built against libpq + from PostgreSQL 8.3 or later and can only be used with PostgreSQL servers + running these versions. It uses the |lo_truncate|_ libpq function. + + .. |lo_truncate| replace:: `!lo_truncate()` + .. _lo_truncate: http://www.postgresql.org/docs/current/static/lo-interfaces.html#LO-TRUNCATE + + .. warning:: + + If Psycopg is built with |lo_truncate| support (i.e. if the + :program:`pg_config` used during setup is version >= 8.3), but at + runtime an older libpq is found, Psycopg will fail to import. See + :ref:`the lo_truncate FAQ ` about the problem. + + .. method:: close() + + Close the object. + + .. attribute:: closed + + Boolean attribute specifying if the object is closed. + + .. method:: unlink() + + Close the object and remove it from the database. + + +.. autoclass:: Notify(pid, channel, payload='') + :members: pid, channel, payload + + .. versionadded:: 2.3 + + +.. autoclass:: Xid(format_id, gtrid, bqual) + :members: format_id, gtrid, bqual, prepared, owner, database + + .. versionadded:: 2.3 + + .. automethod:: from_string(s) + + +.. autofunction:: set_wait_callback(f) + + .. versionadded:: 2.2.0 + +.. autofunction:: get_wait_callback() + + .. versionadded:: 2.2.0 + + +.. _sql-adaptation-objects: + +SQL adaptation protocol objects +------------------------------- + +Psycopg provides a flexible system to adapt Python objects to the SQL syntax +(inspired to the :pep:`246`), allowing serialization in PostgreSQL. See +:ref:`adapting-new-types` for a detailed description. The following objects +deal with Python objects adaptation: + +.. function:: adapt(obj) + + Return the SQL representation of *obj* as a string. Raise a + `~psycopg2.ProgrammingError` if how to adapt the object is unknown. + In order to allow new objects to be adapted, register a new adapter for it + using the `register_adapter()` function. + + The function is the entry point of the adaptation mechanism: it can be + used to write adapters for complex objects by recursively calling + `!adapt()` on its components. + +.. function:: register_adapter(class, adapter) + + Register a new adapter for the objects of class *class*. + + *adapter* should be a function taking a single argument (the object + to adapt) and returning an object conforming the `ISQLQuote` + protocol (e.g. exposing a `!getquoted()` method). The `AsIs` is + often useful for this task. + + Once an object is registered, it can be safely used in SQL queries and by + the `adapt()` function. + +.. class:: ISQLQuote(wrapped_object) + + Represents the SQL adaptation protocol. Objects conforming this protocol + should implement a `getquoted()` and optionally a `prepare()` method. + + Adapters may subclass `!ISQLQuote`, but is not necessary: it is + enough to expose a `!getquoted()` method to be conforming. + + .. attribute:: _wrapped + + The wrapped object passes to the constructor + + .. method:: getquoted() + + Subclasses or other conforming objects should return a valid SQL + string representing the wrapped object. In Python 3 the SQL must be + returned in a `!bytes` object. The `!ISQLQuote` implementation does + nothing. + + .. method:: prepare(conn) + + Prepare the adapter for a connection. The method is optional: if + implemented, it will be invoked before `!getquoted()` with the + connection to adapt for as argument. + + A conform object can implement this method if the SQL + representation depends on any server parameter, such as the server + version or the :envvar:`standard_conforming_string` setting. Container + objects may store the connection and use it to recursively prepare + contained objects: see the implementation for + `psycopg2.extensions.SQL_IN` for a simple example. + + +.. class:: AsIs(object) + + Adapter conform to the `ISQLQuote` protocol useful for objects + whose string representation is already valid as SQL representation. + + .. method:: getquoted() + + Return the `str()` conversion of the wrapped object. + + >>> AsIs(42).getquoted() + '42' + +.. class:: QuotedString(str) + + Adapter conform to the `ISQLQuote` protocol for string-like + objects. + + .. method:: getquoted() + + Return the string enclosed in single quotes. Any single quote + appearing in the the string is escaped by doubling it according to SQL + string constants syntax. Backslashes are escaped too. + + >>> QuotedString(r"O'Reilly").getquoted() + "'O''Reilly'" + +.. class:: Binary(str) + + Adapter conform to the `ISQLQuote` protocol for binary objects. + + .. method:: getquoted() + + Return the string enclosed in single quotes. It performs the same + escaping of the `QuotedString` adapter, plus it knows how to + escape non-printable chars. + + >>> Binary("\x00\x08\x0F").getquoted() + "'\\\\000\\\\010\\\\017'" + + .. versionchanged:: 2.0.14 + previously the adapter was not exposed by the `extensions` + module. In older versions it can be imported from the implementation + module `!psycopg2._psycopg`. + + + +.. class:: Boolean + Float + SQL_IN + + Specialized adapters for builtin objects. + +.. class:: DateFromPy + TimeFromPy + TimestampFromPy + IntervalFromPy + + Specialized adapters for Python datetime objects. + +.. class:: DateFromMx + TimeFromMx + TimestampFromMx + IntervalFromMx + + Specialized adapters for `mx.DateTime`_ objects. + +.. data:: adapters + + Dictionary of the currently registered object adapters. Use + `register_adapter()` to add an adapter for a new type. + + + +Database types casting functions +-------------------------------- + +These functions are used to manipulate type casters to convert from PostgreSQL +types to Python objects. See :ref:`type-casting-from-sql-to-python` for +details. + +.. function:: new_type(oids, name, adapter) + + Create a new type caster to convert from a PostgreSQL type to a Python + object. The object created must be registered using + `register_type()` to be used. + + :param oids: tuple of OIDs of the PostgreSQL type to convert. + :param name: the name of the new type adapter. + :param adapter: the adaptation function. + + The object OID can be read from the `cursor.description` attribute + or by querying from the PostgreSQL catalog. + + *adapter* should have signature :samp:`fun({value}, {cur})` where + *value* is the string representation returned by PostgreSQL and + *cur* is the cursor from which data are read. In case of + :sql:`NULL`, *value* will be `!None`. The adapter should return the + converted object. + + See :ref:`type-casting-from-sql-to-python` for an usage example. + + +.. function:: new_array_type(oids, name, base_caster) + + Create a new type caster to convert from a PostgreSQL array type to a list + of Python object. The object created must be registered using + `register_type()` to be used. + + :param oids: tuple of OIDs of the PostgreSQL type to convert. It should + probably be the oid of the array type (e.g. the ``typarray`` field in + the ``pg_type`` table. + :param name: the name of the new type adapter. + :param base_caster: a Psycopg typecaster, e.g. created using the + `new_type()` function. The caster should be able to parse a single + item of the desired type. + + .. versionadded:: 2.4.3 + + .. _cast-array-unknown: + + .. note:: + + The function can be used to create a generic array typecaster, + returning a list of strings: just use the `~psycopg2.STRING` as base + typecaster. For instance, if you want to receive from the database an + array of :sql:`macaddr`, each address represented by string, you can + use:: + + psycopg2.extensions.register_type( + psycopg2.extensions.new_array_type( + (1040,), 'MACADDR[]', psycopg2.STRING)) + + +.. function:: register_type(obj [, scope]) + + Register a type caster created using `new_type()`. + + If *scope* is specified, it should be a `connection` or a + `cursor`: the type caster will be effective only limited to the + specified object. Otherwise it will be globally registered. + + +.. data:: string_types + + The global register of type casters. + + +.. index:: + single: Encoding; Mapping + +.. data:: encodings + + Mapping from `PostgreSQL encoding`__ names to `Python codec`__ names. + Used by Psycopg when adapting or casting unicode strings. See + :ref:`unicode-handling`. + + .. __: http://www.postgresql.org/docs/current/static/multibyte.html + .. __: http://docs.python.org/library/codecs.html#standard-encodings + + + +.. index:: + single: Exceptions; Additional + +Additional exceptions +--------------------- + +The module exports a few exceptions in addition to the :ref:`standard ones +` defined by the |DBAPI|_. + +.. exception:: QueryCanceledError + + (subclasses `~psycopg2.OperationalError`) + + Error related to SQL query cancellation. It can be trapped specifically to + detect a timeout. + + .. versionadded:: 2.0.7 + + +.. exception:: TransactionRollbackError + + (subclasses `~psycopg2.OperationalError`) + + Error causing transaction rollback (deadlocks, serialisation failures, + etc). It can be trapped specifically to detect a deadlock. + + .. versionadded:: 2.0.7 + + + +.. index:: + pair: Isolation level; Constants + +.. _isolation-level-constants: + +Isolation level constants +------------------------- + +Psycopg2 `connection` objects hold informations about the PostgreSQL +`transaction isolation level`_. The current transaction level can be read +from the `~connection.isolation_level` attribute. The default isolation +level is :sql:`READ COMMITTED`. A different isolation level con be set +through the `~connection.set_isolation_level()` method. The level can be +set to one of the following constants: + +.. data:: ISOLATION_LEVEL_AUTOCOMMIT + + No transaction is started when command are issued and no + `~connection.commit()` or `~connection.rollback()` is required. + Some PostgreSQL command such as :sql:`CREATE DATABASE` or :sql:`VACUUM` + can't run into a transaction: to run such command use:: + + >>> conn.set_isolation_level(ISOLATION_LEVEL_AUTOCOMMIT) + + See also :ref:`transactions-control`. + +.. data:: ISOLATION_LEVEL_READ_UNCOMMITTED + + The :sql:`READ UNCOMMITTED` isolation level is defined in the SQL standard + but not available in the |MVCC| model of PostgreSQL: it is replaced by the + stricter :sql:`READ COMMITTED`. + +.. data:: ISOLATION_LEVEL_READ_COMMITTED + + This is usually the the default PostgreSQL value, but a different default + may be set in the database configuration. + + A new transaction is started at the first `~cursor.execute()` command on a + cursor and at each new `!execute()` after a `~connection.commit()` or a + `~connection.rollback()`. The transaction runs in the PostgreSQL + :sql:`READ COMMITTED` isolation level: a :sql:`SELECT` query sees only + data committed before the query began; it never sees either uncommitted + data or changes committed during query execution by concurrent + transactions. + + .. seealso:: `Read Committed Isolation Level`__ in PostgreSQL + documentation. + + .. __: http://www.postgresql.org/docs/current/static/transaction-iso.html#XACT-READ-COMMITTED + +.. data:: ISOLATION_LEVEL_REPEATABLE_READ + + As in `!ISOLATION_LEVEL_READ_COMMITTED`, a new transaction is started at + the first `~cursor.execute()` command. Transactions run at a + :sql:`REPEATABLE READ` isolation level: all the queries in a transaction + see a snapshot as of the start of the transaction, not as of the start of + the current query within the transaction. However applications using this + level must be prepared to retry transactions due to serialization + failures. + + While this level provides a guarantee that each transaction sees a + completely stable view of the database, this view will not necessarily + always be consistent with some serial (one at a time) execution of + concurrent transactions of the same level. + + .. versionchanged:: 2.4.2 + The value was an alias for `!ISOLATION_LEVEL_SERIALIZABLE` before. The + two levels are distinct since PostgreSQL 9.1 + + .. seealso:: `Repeatable Read Isolation Level`__ in PostgreSQL + documentation. + + .. __: http://www.postgresql.org/docs/current/static/transaction-iso.html#XACT-REPEATABLE-READ + +.. data:: ISOLATION_LEVEL_SERIALIZABLE + + As in `!ISOLATION_LEVEL_READ_COMMITTED`, a new transaction is started at + the first `~cursor.execute()` command. Transactions run at a + :sql:`SERIALIZABLE` isolation level. This is the strictest transactions + isolation level, equivalent to having the transactions executed serially + rather than concurrently. However applications using this level must be + prepared to retry reansactions due to serialization failures. + + Starting from PostgreSQL 9.1, this mode monitors for conditions which + could make execution of a concurrent set of serializable transactions + behave in a manner inconsistent with all possible serial (one at a time) + executions of those transaction. In previous version the behaviour was the + same of the :sql:`REPEATABLE READ` isolation level. + + .. seealso:: `Serializable Isolation Level`__ in PostgreSQL documentation. + + .. __: http://www.postgresql.org/docs/current/static/transaction-iso.html#XACT-SERIALIZABLE + + + +.. index:: + pair: Transaction status; Constants + +.. _transaction-status-constants: + +Transaction status constants +---------------------------- + +These values represent the possible status of a transaction: the current value +can be read using the `connection.get_transaction_status()` method. + +.. data:: TRANSACTION_STATUS_IDLE + + The session is idle and there is no current transaction. + +.. data:: TRANSACTION_STATUS_ACTIVE + + A command is currently in progress. + +.. data:: TRANSACTION_STATUS_INTRANS + + The session is idle in a valid transaction block. + +.. data:: TRANSACTION_STATUS_INERROR + + The session is idle in a failed transaction block. + +.. data:: TRANSACTION_STATUS_UNKNOWN + + Reported if the connection with the server is bad. + + + +.. index:: + pair: Connection status; Constants + +.. _connection-status-constants: + +Connection status constants +--------------------------- + +These values represent the possible status of a connection: the current value +can be read from the `~connection.status` attribute. + +It is possible to find the connection in other status than the one shown below. +Those are the only states in which a working connection is expected to be found +during the execution of regular Python client code: other states are for +internal usage and Python code should not rely on them. + +.. data:: STATUS_READY + + Connection established. No transaction in progress. + +.. data:: STATUS_BEGIN + + Connection established. A transaction is currently in progress. + +.. data:: STATUS_IN_TRANSACTION + + An alias for `STATUS_BEGIN` + +.. data:: STATUS_PREPARED + + The connection has been prepared for the second phase in a :ref:`two-phase + commit ` transaction. The connection can't be used to send commands + to the database until the transaction is finished with + `~connection.tpc_commit()` or `~connection.tpc_rollback()`. + + .. versionadded:: 2.3 + + + +.. index:: + pair: Poll status; Constants + +.. _poll-constants: + +Poll constants +-------------- + +.. versionadded:: 2.2.0 + +These values can be returned by `connection.poll()` during asynchronous +connection and communication. They match the values in the libpq enum +`!PostgresPollingStatusType`. See :ref:`async-support` and +:ref:`green-support`. + +.. data:: POLL_OK + + The data being read is available, or the file descriptor is ready for + writing: reading or writing will not block. + +.. data:: POLL_READ + + Some data is being read from the backend, but it is not available yet on + the client and reading would block. Upon receiving this value, the client + should wait for the connection file descriptor to be ready *for reading*. + For example:: + + select.select([conn.fileno()], [], []) + +.. data:: POLL_WRITE + + Some data is being sent to the backend but the connection file descriptor + can't currently accept new data. Upon receiving this value, the client + should wait for the connection file descriptor to be ready *for writing*. + For example:: + + select.select([], [conn.fileno()], []) + +.. data:: POLL_ERROR + + There was a problem during connection polling. This value should actually + never be returned: in case of poll error usually an exception containing + the relevant details is raised. + + + +Additional database types +------------------------- + +The `!extensions` module includes typecasters for many standard +PostgreSQL types. These objects allow the conversion of returned data into +Python objects. All the typecasters are automatically registered, except +`UNICODE` and `UNICODEARRAY`: you can register them using +`register_type()` in order to receive Unicode objects instead of strings +from the database. See :ref:`unicode-handling` for details. + +.. data:: BOOLEAN + DATE + DECIMAL + FLOAT + INTEGER + INTERVAL + LONGINTEGER + TIME + UNICODE + + Typecasters for basic types. Note that a few other ones (`~psycopg2.BINARY`, + `~psycopg2.DATETIME`, `~psycopg2.NUMBER`, `~psycopg2.ROWID`, + `~psycopg2.STRING`) are exposed by the `psycopg2` module for |DBAPI|_ + compliance. + +.. data:: BINARYARRAY + BOOLEANARRAY + DATEARRAY + DATETIMEARRAY + DECIMALARRAY + FLOATARRAY + INTEGERARRAY + INTERVALARRAY + LONGINTEGERARRAY + ROWIDARRAY + STRINGARRAY + TIMEARRAY + UNICODEARRAY + + Typecasters to convert arrays of sql types into Python lists. + +.. data:: PYDATE + PYDATETIME + PYINTERVAL + PYTIME + PYDATEARRAY + PYDATETIMEARRAY + PYINTERVALARRAY + PYTIMEARRAY + + Typecasters to convert time-related data types to Python `!datetime` + objects. + +.. data:: MXDATE + MXDATETIME + MXINTERVAL + MXTIME + MXDATEARRAY + MXDATETIMEARRAY + MXINTERVALARRAY + MXTIMEARRAY + + Typecasters to convert time-related data types to `mx.DateTime`_ objects. + Only available if Psycopg was compiled with `!mx` support. + +.. versionchanged:: 2.2.0 + previously the `DECIMAL` typecaster and the specific time-related + typecasters (`!PY*` and `!MX*`) were not exposed by the `extensions` + module. In older versions they can be imported from the implementation + module `!psycopg2._psycopg`. + diff -Nru psycopg2-2.0.13/doc/src/extras.rst psycopg2-2.4.5/doc/src/extras.rst --- psycopg2-2.0.13/doc/src/extras.rst 1970-01-01 00:00:00.000000000 +0000 +++ psycopg2-2.4.5/doc/src/extras.rst 2012-03-28 21:09:15.000000000 +0000 @@ -0,0 +1,296 @@ +`psycopg2.extras` -- Miscellaneous goodies for Psycopg 2 +============================================================= + +.. sectionauthor:: Daniele Varrazzo + +.. module:: psycopg2.extras + +.. testsetup:: + + import psycopg2.extras + from psycopg2.extras import Inet + + create_test_table() + +This module is a generic place used to hold little helper functions and +classes until a better place in the distribution is found. + + +.. index:: + pair: Cursor; Dictionary + +.. _dict-cursor: + + +Connection and cursor subclasses +-------------------------------- + +A few objects that change the way the results are returned by the cursor or +modify the object behavior in some other way. Typically `!connection` +subclasses are passed as *connection_factory* argument to +`~psycopg2.connect()` so that the connection will generate the matching +`!cursor` subclass. Alternatively a `!cursor` subclass can be used one-off by +passing it as the *cursor_factory* argument to the `~connection.cursor()` +method of a regular `!connection`. + +Dictionary-like cursor +^^^^^^^^^^^^^^^^^^^^^^ + +The dict cursors allow to access to the retrieved records using an iterface +similar to the Python dictionaries instead of the tuples. + + >>> dict_cur = conn.cursor(cursor_factory=psycopg2.extras.DictCursor) + >>> dict_cur.execute("INSERT INTO test (num, data) VALUES(%s, %s)", + ... (100, "abc'def")) + >>> dict_cur.execute("SELECT * FROM test") + >>> rec = dict_cur.fetchone() + >>> rec['id'] + 1 + >>> rec['num'] + 100 + >>> rec['data'] + "abc'def" + +The records still support indexing as the original tuple: + + >>> rec[2] + "abc'def" + + +.. autoclass:: DictCursor + +.. autoclass:: DictConnection + +.. autoclass:: DictRow + + +Real dictionary cursor +^^^^^^^^^^^^^^^^^^^^^^ + +.. autoclass:: RealDictCursor + +.. autoclass:: RealDictConnection + +.. autoclass:: RealDictRow + + + +.. index:: + pair: Cursor; namedtuple + +`namedtuple` cursor +^^^^^^^^^^^^^^^^^^^^ + +.. versionadded:: 2.3 + +These objects require :py:func:`collections.namedtuple` to be found, so it is +available out-of-the-box only from Python 2.6. Anyway, the namedtuple +implementation is compatible with previous Python versions, so all you +have to do is to `download it`__ and make it available where we +expect it to be... :: + + from somewhere import namedtuple + import collections + collections.namedtuple = namedtuple + from psycopg.extras import NamedTupleConnection + # ... + +.. __: http://code.activestate.com/recipes/500261-named-tuples/ + +.. autoclass:: NamedTupleCursor + +.. autoclass:: NamedTupleConnection + + +.. index:: + pair: Cursor; Logging + +Logging cursor +^^^^^^^^^^^^^^ + +.. autoclass:: LoggingConnection + :members: initialize,filter + +.. autoclass:: LoggingCursor + + +.. autoclass:: MinTimeLoggingConnection + :members: initialize,filter + +.. autoclass:: MinTimeLoggingCursor + + + +.. index:: + single: Data types; Additional + +Additional data types +--------------------- + + +.. _adapt-hstore: + +.. index:: + pair: hstore; Data types + pair: dict; Adaptation + +Hstore data type +^^^^^^^^^^^^^^^^ + +.. versionadded:: 2.3 + +The |hstore|_ data type is a key-value store embedded in PostgreSQL. It has +been available for several server versions but with the release 9.0 it has +been greatly improved in capacity and usefulness with the addiction of many +functions. It supports GiST or GIN indexes allowing search by keys or +key/value pairs as well as regular BTree indexes for equality, uniqueness etc. + +Psycopg can convert Python `!dict` objects to and from |hstore| structures. +Only dictionaries with string/unicode keys and values are supported. `!None` +is also allowed as value but not as a key. Psycopg uses a more efficient |hstore| +representation when dealing with PostgreSQL 9.0 but previous server versions +are supported as well. By default the adapter/typecaster are disabled: they +can be enabled using the `register_hstore()` function. + +.. autofunction:: register_hstore + +.. |hstore| replace:: :sql:`hstore` +.. _hstore: http://www.postgresql.org/docs/current/static/hstore.html + + + +.. _adapt-composite: + +.. index:: + pair: Composite types; Data types + pair: tuple; Adaptation + pair: namedtuple; Adaptation + +Composite types casting +^^^^^^^^^^^^^^^^^^^^^^^ + +.. versionadded:: 2.4 + +Using `register_composite()` it is possible to cast a PostgreSQL composite +type (either created with the |CREATE TYPE|_ command or implicitly defined +after a table row type) into a Python named tuple, or into a regular tuple if +:py:func:`collections.namedtuple` is not found. + +.. |CREATE TYPE| replace:: :sql:`CREATE TYPE` +.. _CREATE TYPE: http://www.postgresql.org/docs/current/static/sql-createtype.html + +.. doctest:: + + >>> cur.execute("CREATE TYPE card AS (value int, suit text);") + >>> psycopg2.extras.register_composite('card', cur) + + + >>> cur.execute("select (8, 'hearts')::card") + >>> cur.fetchone()[0] + card(value=8, suit='hearts') + +Nested composite types are handled as expected, but the type of the composite +components must be registered as well. + +.. doctest:: + + >>> cur.execute("CREATE TYPE card_back AS (face card, back text);") + >>> psycopg2.extras.register_composite('card_back', cur) + + + >>> cur.execute("select ((8, 'hearts'), 'blue')::card_back") + >>> cur.fetchone()[0] + card_back(face=card(value=8, suit='hearts'), back='blue') + +Adaptation from Python tuples to composite types is automatic instead and +requires no adapter registration. + +.. autofunction:: register_composite + +.. autoclass:: CompositeCaster + + + +.. index:: + pair: UUID; Data types + +UUID data type +^^^^^^^^^^^^^^ + +.. versionadded:: 2.0.9 +.. versionchanged:: 2.0.13 added UUID array support. + +.. doctest:: + + >>> psycopg2.extras.register_uuid() + + + >>> # Python UUID can be used in SQL queries + >>> import uuid + >>> my_uuid = uuid.UUID('{12345678-1234-5678-1234-567812345678}') + >>> psycopg2.extensions.adapt(my_uuid).getquoted() + "'12345678-1234-5678-1234-567812345678'::uuid" + + >>> # PostgreSQL UUID are transformed into Python UUID objects. + >>> cur.execute("SELECT 'a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11'::uuid") + >>> cur.fetchone()[0] + UUID('a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11') + + +.. autofunction:: register_uuid + +.. autoclass:: UUID_adapter + + + +.. index:: + pair: INET; Data types + +:sql:`inet` data type +^^^^^^^^^^^^^^^^^^^^^^ + +.. versionadded:: 2.0.9 +.. versionchanged:: 2.4.5 added inet array support. + +.. doctest:: + + >>> psycopg2.extras.register_inet() + + + >>> cur.mogrify("SELECT %s", (Inet('127.0.0.1/32'),)) + "SELECT E'127.0.0.1/32'::inet" + + >>> cur.execute("SELECT '192.168.0.1/24'::inet") + >>> cur.fetchone()[0].addr + '192.168.0.1/24' + + +.. autofunction:: register_inet + +.. autoclass:: Inet + + + +.. index:: + single: Time zones; Fractional + +Fractional time zones +--------------------- + +.. autofunction:: register_tstz_w_secs + + .. versionadded:: 2.0.9 + + .. versionchanged:: 2.2.2 + function is no-op: see :ref:`tz-handling`. + +.. index:: + pair: Example; Coroutine; + + + +Coroutine support +----------------- + +.. autofunction:: wait_select(conn) + diff -Nru psycopg2-2.0.13/doc/src/faq.rst psycopg2-2.4.5/doc/src/faq.rst --- psycopg2-2.0.13/doc/src/faq.rst 1970-01-01 00:00:00.000000000 +0000 +++ psycopg2-2.4.5/doc/src/faq.rst 2012-03-28 21:09:15.000000000 +0000 @@ -0,0 +1,245 @@ +Frequently Asked Questions +========================== + +.. sectionauthor:: Daniele Varrazzo + +Here are a few gotchas you may encounter using `psycopg2`. Feel free to +suggest new entries! + + +Problems with transactions handling +----------------------------------- + +.. _faq-idle-in-transaction: +.. cssclass:: faq + +Why does `!psycopg2` leave database sessions "idle in transaction"? + Psycopg normally starts a new transaction the first time a query is + executed, e.g. calling `cursor.execute()`, even if the command is a + :sql:`SELECT`. The transaction is not closed until an explicit + `~connection.commit()` or `~connection.rollback()`. + + If you are writing a long-living program, you should probably make sure to + call one of the transaction closing methods before leaving the connection + unused for a long time (which may also be a few seconds, depending on the + concurrency level in your database). Alternatively you can use a + connection in `~connection.autocommit` mode to avoid a new transaction to + be started at the first command. + + +.. _faq-transaction-aborted: +.. cssclass:: faq + +I receive the error *current transaction is aborted, commands ignored until end of transaction block* and can't do anything else! + There was a problem *in the previous* command to the database, which + resulted in an error. The database will not recover automatically from + this condition: you must run a `~connection.rollback()` before sending + new commands to the session (if this seems too harsh, remember that + PostgreSQL supports nested transactions using the |SAVEPOINT|_ command). + + .. |SAVEPOINT| replace:: :sql:`SAVEPOINT` + .. _SAVEPOINT: http://www.postgresql.org/docs/current/static/sql-savepoint.html + + +.. _faq-transaction-aborted-multiprocess: +.. cssclass:: faq + +Why do I get the error *current transaction is aborted, commands ignored until end of transaction block* when I use `!multiprocessing` (or any other forking system) and not when use `!threading`? + Psycopg's connections can't be shared across processes (but are thread + safe). If you are forking the Python process make sure to create a new + connection in each forked child. See :ref:`thread-safety` for further + informations. + + +Problems with type conversions +------------------------------ + +.. _faq-cant-adapt: +.. cssclass:: faq + +Why does `!cursor.execute()` raise the exception *can't adapt*? + Psycopg converts Python objects in a SQL string representation by looking + at the object class. The exception is raised when you are trying to pass + as query parameter an object for which there is no adapter registered for + its class. See :ref:`adapting-new-types` for informations. + + +.. _faq-number-required: +.. cssclass:: faq + +I can't pass an integer or a float parameter to my query: it says *a number is required*, but *it is* a number! + In your query string, you always have to use ``%s`` placeholders, + event when passing a number. All Python objects are converted by Psycopg + in their SQL representation, so they get passed to the query as strings. + See :ref:`query-parameters`. :: + + >>> cur.execute("INSERT INTO numbers VALUES (%d)", (42,)) # WRONG + >>> cur.execute("INSERT INTO numbers VALUES (%s)", (42,)) # correct + + +.. _faq-not-all-arguments-converted: +.. cssclass:: faq + +I try to execute a query but it fails with the error *not all arguments converted during string formatting* (or *object does not support indexing*). Why? + Psycopg always require positional arguments to be passed as a sequence, even + when the query takes a single parameter. And remember that to make a + single item tuple in Python you need a comma! See :ref:`query-parameters`. + :: + + >>> cur.execute("INSERT INTO foo VALUES (%s)", "bar") # WRONG + >>> cur.execute("INSERT INTO foo VALUES (%s)", ("bar")) # WRONG + >>> cur.execute("INSERT INTO foo VALUES (%s)", ("bar",)) # correct + >>> cur.execute("INSERT INTO foo VALUES (%s)", ["bar"]) # correct + + +.. _faq-unicode: +.. cssclass:: faq + +My database is Unicode, but I receive all the strings as UTF-8 `!str`. Can I receive `!unicode` objects instead? + The following magic formula will do the trick:: + + psycopg2.extensions.register_type(psycopg2.extensions.UNICODE) + psycopg2.extensions.register_type(psycopg2.extensions.UNICODEARRAY) + + See :ref:`unicode-handling` for the gory details. + + +.. _faq-float: +.. cssclass:: faq + +Psycopg converts :sql:`decimal`\/\ :sql:`numeric` database types into Python `!Decimal` objects. Can I have `!float` instead? + You can register a customized adapter for PostgreSQL decimal type:: + + DEC2FLOAT = psycopg2.extensions.new_type( + psycopg2.extensions.DECIMAL.values, + 'DEC2FLOAT', + lambda value, curs: float(value) if value is not None else None) + psycopg2.extensions.register_type(DEC2FLOAT) + + See :ref:`type-casting-from-sql-to-python` to read the relevant + documentation. If you find `!psycopg2.extensions.DECIMAL` not avalable, use + `!psycopg2._psycopg.DECIMAL` instead. + + +.. _faq-bytea-9.0: +.. cssclass:: faq + +Transferring binary data from PostgreSQL 9.0 doesn't work. + PostgreSQL 9.0 uses by default `the "hex" format`__ to transfer + :sql:`bytea` data: the format can't be parsed by the libpq 8.4 and + earlier. The problem is solved in Psycopg 2.4.1, that uses its own parser + for the :sql:`bytea` format. For previous Psycopg releases, three options + to solve the problem are: + + - set the bytea_output__ parameter to ``escape`` in the server; + - execute the database command ``SET bytea_output TO escape;`` in the + session before reading binary data; + - upgrade the libpq library on the client to at least 9.0. + + .. __: http://www.postgresql.org/docs/current/static/datatype-binary.html + .. __: http://www.postgresql.org/docs/current/static/runtime-config-client.html#GUC-BYTEA-OUTPUT + + +.. _faq-array: +.. cssclass:: faq + +Arrays of *TYPE* are not casted to list. + Arrays are only casted to list when their oid is known, and an array + typecaster is registered for them. If there is no typecaster, the array is + returned unparsed from PostgreSQL (e.g. ``{a,b,c}``). It is easy to create + a generic arrays typecaster, returning a list of array: an example is + provided in the `~psycopg2.extensions.new_array_type()` documentation. + + +Best practices +-------------- + +.. _faq-reuse-cursors: +.. cssclass:: faq + +When should I save and re-use a cursor as opposed to creating a new one as needed? + Cursors are lightweight objects and creating lots of them should not pose + any kind of problem. But note that cursors used to fetch result sets will + cache the data and use memory in proportion to the result set size. Our + suggestion is to almost always create a new cursor and dispose old ones as + soon as the data is not required anymore (call `~cursor.close()` on + them.) The only exception are tight loops where one usually use the same + cursor for a whole bunch of :sql:`INSERT`\s or :sql:`UPDATE`\s. + + +.. _faq-reuse-connections: +.. cssclass:: faq + +When should I save and re-use a connection as opposed to creating a new one as needed? + Creating a connection can be slow (think of SSL over TCP) so the best + practice is to create a single connection and keep it open as long as + required. It is also good practice to rollback or commit frequently (even + after a single :sql:`SELECT` statement) to make sure the backend is never + left "idle in transaction". See also `psycopg2.pool` for lightweight + connection pooling. + + +.. _faq-named-cursors: +.. cssclass:: faq + +What are the advantages or disadvantages of using named cursors? + The only disadvantages is that they use up resources on the server and + that there is a little overhead because a at least two queries (one to + create the cursor and one to fetch the initial result set) are issued to + the backend. The advantage is that data is fetched one chunk at a time: + using small `~cursor.fetchmany()` values it is possible to use very + little memory on the client and to skip or discard parts of the result set. + + +Problems compiling and deploying psycopg2 +----------------------------------------- + +.. _faq-python-h: +.. cssclass:: faq + +I can't compile `!psycopg2`: the compiler says *error: Python.h: No such file or directory*. What am I missing? + You need to install a Python development package: it is usually called + ``python-dev``. + + +.. _faq-libpq-fe-h: +.. cssclass:: faq + +I can't compile `!psycopg2`: the compiler says *error: libpq-fe.h: No such file or directory*. What am I missing? + You need to install the development version of the libpq: the package is + usually called ``libpq-dev``. + + +.. _faq-lo_truncate: +.. cssclass:: faq + +`!psycopg2` raises `!ImportError` with message *_psycopg.so: undefined symbol: lo_truncate* when imported. + This means that Psycopg has been compiled with |lo_truncate|_ support, + which means that the libpq used at compile time was version >= 8.3, but at + runtime an older libpq library is found. You can use:: + + $ ldd /path/to/packages/psycopg2/_psycopg.so | grep libpq + + to find what is the version used at runtime. + + You can avoid the problem by using the same version of the + :program:`pg_config` at install time and the libpq at runtime. + + .. |lo_truncate| replace:: `!lo_truncate()` + .. _lo_truncate: http://www.postgresql.org/docs/current/static/lo-interfaces.html#LO-TRUNCATE + + +.. _faq-import-mod_wsgi: +.. cssclass:: faq + +Psycopg raises *ImportError: cannot import name tz* on import in mod_wsgi / ASP, but it works fine otherwise. + If `!psycopg2` is installed in an egg_ (e.g. because installed by + :program:`easy_install`), the user running the program may be unable to + write in the `eggs cache`__. Set the env variable + :envvar:`PYTHON_EGG_CACHE` to a writable directory. With modwsgi you can + use the WSGIPythonEggs__ directive. + + .. _egg: http://peak.telecommunity.com/DevCenter/PythonEggs + .. __: http://stackoverflow.com/questions/2192323/what-is-the-python-egg-cache-python-egg-cache + .. __: http://code.google.com/p/modwsgi/wiki/ConfigurationDirectives#WSGIPythonEggs + diff -Nru psycopg2-2.0.13/doc/src/index.rst psycopg2-2.4.5/doc/src/index.rst --- psycopg2-2.0.13/doc/src/index.rst 1970-01-01 00:00:00.000000000 +0000 +++ psycopg2-2.4.5/doc/src/index.rst 2012-03-28 21:09:15.000000000 +0000 @@ -0,0 +1,73 @@ +================================================= +Psycopg -- PostgreSQL database adapter for Python +================================================= + +.. sectionauthor:: Daniele Varrazzo + +Psycopg_ is a PostgreSQL_ database adapter for the Python_ programming +language. Its main features are that it supports the full Python |DBAPI|_ +and it is thread safe (threads can share the connections). It was designed for +heavily multi-threaded applications that create and destroy lots of cursors and +make a large number of concurrent :sql:`INSERT`\ s or :sql:`UPDATE`\ s. +The Psycopg distribution includes ZPsycopgDA, a Zope_ Database Adapter. + +Psycopg 2 is mostly implemented in C as a libpq_ wrapper, resulting in being +both efficient and secure. It features client-side and :ref:`server-side +` cursors, :ref:`asynchronous communication +` and :ref:`notifications `, |COPY-TO-FROM|__ +support, and a flexible :ref:`objects adaptation system +`. Many basic Python types are supported +out-of-the-box and mapped to matching PostgreSQL data types, such as strings +(both bytes and Unicode), numbers (ints, longs, floats, decimals), booleans and +datetime objects (both built-in and `mx.DateTime`_), several types of +:ref:`binary objects `. Also available are mappings between lists +and PostgreSQL arrays of any supported type, between :ref:`dictionaries and +PostgreSQL hstores `, and between :ref:`tuples/namedtuples and +PostgreSQL composite types `. + +Psycopg 2 is both Unicode and Python 3 friendly. + + +.. _Psycopg: http://initd.org/psycopg/ +.. _PostgreSQL: http://www.postgresql.org/ +.. _Python: http://www.python.org/ +.. _Zope: http://www.zope.org/ +.. _libpq: http://www.postgresql.org/docs/current/static/libpq.html +.. |COPY-TO-FROM| replace:: :sql:`COPY TO/COPY FROM` +.. __: http://www.postgresql.org/docs/current/static/sql-copy.html + + +.. rubric:: Contents + +.. toctree:: + :maxdepth: 2 + + usage + module + connection + cursor + advanced + extensions + tz + pool + extras + errorcodes + faq + + +.. ifconfig:: builder != 'text' + + .. rubric:: Indices and tables + + * :ref:`genindex` + * :ref:`search` + + +.. ifconfig:: todo_include_todos + + .. note:: + + **To Do items in the documentation** + + .. todolist:: + diff -Nru psycopg2-2.0.13/doc/src/Makefile psycopg2-2.4.5/doc/src/Makefile --- psycopg2-2.0.13/doc/src/Makefile 1970-01-01 00:00:00.000000000 +0000 +++ psycopg2-2.4.5/doc/src/Makefile 2011-02-06 15:58:34.000000000 +0000 @@ -0,0 +1,99 @@ +# Makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +PAPER = +BUILDDIR = _build + +# DSN for the doctest database +PSYCOPG2_DSN="user=postgres dbname=test" + +# Internal variables. +PAPEROPT_a4 = -D latex_paper_size=a4 +PAPEROPT_letter = -D latex_paper_size=letter +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . + +.PHONY: help clean html dirhtml pickle json htmlhelp qthelp latex changes linkcheck doctest + +help: + @echo "Please use \`make ' where is one of" + @echo " html to make standalone HTML files" + @echo " dirhtml to make HTML files named index.html in directories" + @echo " pickle to make pickle files" + @echo " json to make JSON files" + @echo " htmlhelp to make HTML files and a HTML help project" + @echo " qthelp to make HTML files and a qthelp project" + @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" + @echo " changes to make an overview of all changed/added/deprecated items" + @echo " linkcheck to check all external links for integrity" + @echo " doctest to run all doctests embedded in the documentation (if enabled)" + +clean: + -rm -rf $(BUILDDIR)/* + -rm -rf ./html/* + +html: + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." + +dirhtml: + $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." + +text: + $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text + @echo + @echo "Build finished. The text pages are in $(BUILDDIR)/text." + +pickle: + $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle + @echo + @echo "Build finished; now you can process the pickle files." + +json: + $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json + @echo + @echo "Build finished; now you can process the JSON files." + +htmlhelp: + $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp + @echo + @echo "Build finished; now you can run HTML Help Workshop with the" \ + ".hhp project file in $(BUILDDIR)/htmlhelp." + +qthelp: + $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp + @echo + @echo "Build finished; now you can run "qcollectiongenerator" with the" \ + ".qhcp project file in $(BUILDDIR)/qthelp, like this:" + @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/psycopg.qhcp" + @echo "To view the help file:" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/psycopg.qhc" + +latex: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo + @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." + @echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \ + "run these through (pdf)latex." + +changes: + $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes + @echo + @echo "The overview file is in $(BUILDDIR)/changes." + +linkcheck: + $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck + @echo + @echo "Link check complete; look for any errors in the above output " \ + "or in $(BUILDDIR)/linkcheck/output.txt." + +doctest: + PSYCOPG2_DSN=$(PSYCOPG2_DSN) \ + $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest + @echo "Testing of doctests in the sources finished, look at the " \ + "results in $(BUILDDIR)/doctest/output.txt." diff -Nru psycopg2-2.0.13/doc/src/module.rst psycopg2-2.4.5/doc/src/module.rst --- psycopg2-2.0.13/doc/src/module.rst 1970-01-01 00:00:00.000000000 +0000 +++ psycopg2-2.4.5/doc/src/module.rst 2012-03-28 21:09:15.000000000 +0000 @@ -0,0 +1,324 @@ +The `psycopg2` module content +================================== + +.. sectionauthor:: Daniele Varrazzo + +.. module:: psycopg2 + +The module interface respects the standard defined in the |DBAPI|_. + +.. index:: + single: Connection string + double: Connection; Parameters + single: Username; Connection + single: Password; Connection + single: Host; Connection + single: Port; Connection + single: DSN (Database Source Name) + +.. function:: connect(dsn or params [, connection_factory] [, async=0]) + + Create a new database session and return a new `connection` object. + + The connection parameters can be specified either as a string:: + + conn = psycopg2.connect("dbname=test user=postgres password=secret") + + or using a set of keyword arguments:: + + conn = psycopg2.connect(database="test", user="postgres", password="secret") + + The basic connection parameters are: + + - `!dbname` -- the database name (only in dsn string) + - `!database` -- the database name (only as keyword argument) + - `!user` -- user name used to authenticate + - `!password` -- password used to authenticate + - `!host` -- database host address (defaults to UNIX socket if not provided) + - `!port` -- connection port number (defaults to 5432 if not provided) + + Any other connection parameter supported by the client library/server can + be passed either in the connection string or as keyword. See the + PostgreSQL documentation for a complete `list of supported parameters`__. + Also note that the same parameters can be passed to the client library + using `environment variables`__. + + .. __: http://www.postgresql.org/docs/current/static/libpq-connect.html#LIBPQ-PQCONNECTDBPARAMS + .. __: http://www.postgresql.org/docs/current/static/libpq-envars.html + + Using the *connection_factory* parameter a different class or + connections factory can be specified. It should be a callable object + taking a *dsn* argument. See :ref:`subclassing-connection` for + details. + + Using *async*\=1 an asynchronous connection will be created: see + :ref:`async-support` to know about advantages and limitations. + + .. versionchanged:: 2.4.3 + any keyword argument is passed to the connection. Previously only the + basic parameters (plus `!sslmode`) were supported as keywords. + + .. extension:: + + The parameters *connection_factory* and *async* are Psycopg extensions + to the |DBAPI|. + + +.. data:: apilevel + + String constant stating the supported DB API level. For `psycopg2` is + ``2.0``. + +.. data:: threadsafety + + Integer constant stating the level of thread safety the interface + supports. For `psycopg2` is ``2``, i.e. threads can share the module + and the connection. See :ref:`thread-safety` for details. + +.. data:: paramstyle + + String constant stating the type of parameter marker formatting expected + by the interface. For `psycopg2` is ``pyformat``. See also + :ref:`query-parameters`. + + + +.. index:: + single: Exceptions; DB API + +.. _dbapi-exceptions: + +Exceptions +---------- + +In compliance with the |DBAPI|_, the module makes informations about errors +available through the following exceptions: + +.. exception:: Warning + + Exception raised for important warnings like data truncations while + inserting, etc. It is a subclass of the Python `~exceptions.StandardError`. + +.. exception:: Error + + Exception that is the base class of all other error exceptions. You can + use this to catch all errors with one single `!except` statement. Warnings + are not considered errors and thus not use this class as base. It + is a subclass of the Python `!StandardError`. + + .. attribute:: pgerror + + String representing the error message returned by the backend, + `!None` if not available. + + .. attribute:: pgcode + + String representing the error code returned by the backend, `!None` + if not available. The `~psycopg2.errorcodes` module contains + symbolic constants representing PostgreSQL error codes. + + .. doctest:: + :options: +NORMALIZE_WHITESPACE + + >>> try: + ... cur.execute("SELECT * FROM barf") + ... except Exception, e: + ... pass + + >>> e.pgcode + '42P01' + >>> print e.pgerror + ERROR: relation "barf" does not exist + LINE 1: SELECT * FROM barf + ^ + .. attribute:: cursor + + The cursor the exception was raised from; `None` if not applicable. + + .. extension:: + + The `~Error.pgerror`, `~Error.pgcode`, and `~Error.cursor` attributes + are Psycopg extensions. + + +.. exception:: InterfaceError + + Exception raised for errors that are related to the database interface + rather than the database itself. It is a subclass of `Error`. + +.. exception:: DatabaseError + + Exception raised for errors that are related to the database. It is a + subclass of `Error`. + +.. exception:: DataError + + Exception raised for errors that are due to problems with the processed + data like division by zero, numeric value out of range, etc. It is a + subclass of `DatabaseError`. + +.. exception:: OperationalError + + Exception raised for errors that are related to the database's operation + and not necessarily under the control of the programmer, e.g. an + unexpected disconnect occurs, the data source name is not found, a + transaction could not be processed, a memory allocation error occurred + during processing, etc. It is a subclass of `DatabaseError`. + +.. exception:: IntegrityError + + Exception raised when the relational integrity of the database is + affected, e.g. a foreign key check fails. It is a subclass of + `DatabaseError`. + +.. exception:: InternalError + + Exception raised when the database encounters an internal error, e.g. the + cursor is not valid anymore, the transaction is out of sync, etc. It is a + subclass of `DatabaseError`. + +.. exception:: ProgrammingError + + Exception raised for programming errors, e.g. table not found or already + exists, syntax error in the SQL statement, wrong number of parameters + specified, etc. It is a subclass of `DatabaseError`. + +.. exception:: NotSupportedError + + Exception raised in case a method or database API was used which is not + supported by the database, e.g. requesting a `!rollback()` on a + connection that does not support transaction or has transactions turned + off. It is a subclass of `DatabaseError`. + + +.. extension:: + + Psycopg may raise a few other, more specialized, exceptions: currently + `~psycopg2.extensions.QueryCanceledError` and + `~psycopg2.extensions.TransactionRollbackError` are defined. These + exceptions are not exposed by the main `!psycopg2` module but are + made available by the `~psycopg2.extensions` module. All the + additional exceptions are subclasses of standard |DBAPI| exceptions, so + trapping them specifically is not required. + + +This is the exception inheritance layout: + +.. parsed-literal:: + + `!StandardError` + \|__ `Warning` + \|__ `Error` + \|__ `InterfaceError` + \|__ `DatabaseError` + \|__ `DataError` + \|__ `OperationalError` + \| \|__ `psycopg2.extensions.QueryCanceledError` + \| \|__ `psycopg2.extensions.TransactionRollbackError` + \|__ `IntegrityError` + \|__ `InternalError` + \|__ `ProgrammingError` + \|__ `NotSupportedError` + + + +.. _type-objects-and-constructors: + +Type Objects and Constructors +----------------------------- + +.. note:: + + This section is mostly copied verbatim from the |DBAPI|_ + specification. While these objects are exposed in compliance to the + DB API, Psycopg offers very accurate tools to convert data between Python + and PostgreSQL formats. See :ref:`adapting-new-types` and + :ref:`type-casting-from-sql-to-python` + +Many databases need to have the input in a particular format for +binding to an operation's input parameters. For example, if an +input is destined for a DATE column, then it must be bound to the +database in a particular string format. Similar problems exist +for "Row ID" columns or large binary items (e.g. blobs or RAW +columns). This presents problems for Python since the parameters +to the .execute*() method are untyped. When the database module +sees a Python string object, it doesn't know if it should be bound +as a simple CHAR column, as a raw BINARY item, or as a DATE. + +To overcome this problem, a module must provide the constructors +defined below to create objects that can hold special values. +When passed to the cursor methods, the module can then detect the +proper type of the input parameter and bind it accordingly. + +A Cursor Object's description attribute returns information about +each of the result columns of a query. The type_code must compare +equal to one of Type Objects defined below. Type Objects may be +equal to more than one type code (e.g. DATETIME could be equal to +the type codes for date, time and timestamp columns; see the +Implementation Hints below for details). + +The module exports the following constructors and singletons: + +.. function:: Date(year,month,day) + + This function constructs an object holding a date value. + +.. function:: Time(hour,minute,second) + + This function constructs an object holding a time value. + +.. function:: Timestamp(year,month,day,hour,minute,second) + + This function constructs an object holding a time stamp value. + +.. function:: DateFromTicks(ticks) + + This function constructs an object holding a date value from the given + ticks value (number of seconds since the epoch; see the documentation of + the standard Python time module for details). + +.. function:: TimeFromTicks(ticks) + + This function constructs an object holding a time value from the given + ticks value (number of seconds since the epoch; see the documentation of + the standard Python time module for details). + +.. function:: TimestampFromTicks(ticks) + + This function constructs an object holding a time stamp value from the + given ticks value (number of seconds since the epoch; see the + documentation of the standard Python time module for details). + +.. function:: Binary(string) + + This function constructs an object capable of holding a binary (long) + string value. + + +.. data:: STRING + + This type object is used to describe columns in a database that are + string-based (e.g. CHAR). + +.. data:: BINARY + + This type object is used to describe (long) binary columns in a database + (e.g. LONG, RAW, BLOBs). + +.. data:: NUMBER + + This type object is used to describe numeric columns in a database. + +.. data:: DATETIME + + This type object is used to describe date/time columns in a database. + +.. data:: ROWID + + This type object is used to describe the "Row ID" column in a database. + + +.. testcode:: + :hide: + + conn.rollback() diff -Nru psycopg2-2.0.13/doc/src/pool.rst psycopg2-2.4.5/doc/src/pool.rst --- psycopg2-2.0.13/doc/src/pool.rst 1970-01-01 00:00:00.000000000 +0000 +++ psycopg2-2.4.5/doc/src/pool.rst 2011-12-11 21:18:11.000000000 +0000 @@ -0,0 +1,64 @@ +`psycopg2.pool` -- Connections pooling +====================================== + +.. sectionauthor:: Daniele Varrazzo + +.. index:: + pair: Connection; Pooling + +.. module:: psycopg2.pool + +Creating new PostgreSQL connections can be an expensive operation. This +module offers a few pure Python classes implementing simple connection pooling +directly in the client application. + +.. class:: AbstractConnectionPool(minconn, maxconn, \*args, \*\*kwargs) + + Base class implementing generic key-based pooling code. + + New *minconn* connections are created automatically. The pool will support + a maximum of about *maxconn* connections. *\*args* and *\*\*kwargs* are + passed to the `~psycopg2.connect()` function. + + The following methods are expected to be implemented by subclasses: + + .. method:: getconn(key=None) + + Get a free connection and assign it to *key* if not `!None`. + + .. method:: putconn(conn, key=None, close=False) + + Put away a connection. + + If *close* is `!True`, discard the connection from the pool. + + .. method:: closeall + + Close all the connections handled by the pool. + + Note that all the connections are closed, including ones + eventually in use by the application. + + +The following classes are `AbstractConnectionPool` subclasses ready to +be used. + +.. autoclass:: SimpleConnectionPool + + .. note:: This pool class is useful only for single-threaded applications. + + +.. index:: Multithread; Connection pooling + +.. autoclass:: ThreadedConnectionPool + + .. note:: This pool class can be safely used in multi-threaded applications. + + +.. autoclass:: PersistentConnectionPool + + .. note:: + + This pool class is mostly designed to interact with Zope and probably + not useful in generic applications. + diff -Nru psycopg2-2.0.13/doc/src/_static/psycopg.css psycopg2-2.4.5/doc/src/_static/psycopg.css --- psycopg2-2.0.13/doc/src/_static/psycopg.css 1970-01-01 00:00:00.000000000 +0000 +++ psycopg2-2.4.5/doc/src/_static/psycopg.css 2011-02-06 15:58:34.000000000 +0000 @@ -0,0 +1,28 @@ +@import url("default.css"); + +blockquote { + font-style: italic; +} + +div.admonition-todo { + background-color: #ffa; + border: 1px solid #ee2; +} + +div.dbapi-extension { + background-color: #eef; + border: 1px solid #aaf; +} + +tt.sql { + font-size: 1em; + background-color: transparent; +} + +a > tt.sql:hover { + text-decoration: underline; +} + +dl.faq dt { + font-weight: bold; +} diff -Nru psycopg2-2.0.13/doc/src/tools/lib/dbapi_extension.py psycopg2-2.4.5/doc/src/tools/lib/dbapi_extension.py --- psycopg2-2.0.13/doc/src/tools/lib/dbapi_extension.py 1970-01-01 00:00:00.000000000 +0000 +++ psycopg2-2.4.5/doc/src/tools/lib/dbapi_extension.py 2011-02-06 15:58:34.000000000 +0000 @@ -0,0 +1,53 @@ +# -*- coding: utf-8 -*- +""" + extension + ~~~~~~~~~ + + A directive to create a box warning that a certain bit of Psycopg is an + extension to the DBAPI 2.0. + + :copyright: Copyright 2010 by Daniele Varrazzo. +""" + +from docutils import nodes + +from sphinx.locale import _ +from sphinx.util.compat import Directive, make_admonition + +class extension_node(nodes.Admonition, nodes.Element): pass + + +class Extension(Directive): + """ + An extension entry, displayed as an admonition. + """ + + has_content = True + required_arguments = 0 + optional_arguments = 0 + final_argument_whitespace = False + option_spec = {} + + def run(self): + nodes = make_admonition(extension_node, + self.name, [_('DB API extension')], self.options, + self.content, self.lineno, self.content_offset, + self.block_text, self.state, self.state_machine) + nodes[0]['classes'].append('dbapi-extension') + return nodes + + +def visit_extension_node(self, node): + self.visit_admonition(node) + +def depart_extension_node(self, node): + self.depart_admonition(node) + +def setup(app): + app.add_node(extension_node, + html=(visit_extension_node, depart_extension_node), + latex=(visit_extension_node, depart_extension_node), + text=(visit_extension_node, depart_extension_node)) + + app.add_directive('extension', Extension) + diff -Nru psycopg2-2.0.13/doc/src/tools/lib/sql_role.py psycopg2-2.4.5/doc/src/tools/lib/sql_role.py --- psycopg2-2.0.13/doc/src/tools/lib/sql_role.py 1970-01-01 00:00:00.000000000 +0000 +++ psycopg2-2.4.5/doc/src/tools/lib/sql_role.py 2011-02-06 15:58:34.000000000 +0000 @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +""" + sql role + ~~~~~~~~ + + An interpreted text role to style SQL syntax in Psycopg documentation. + + :copyright: Copyright 2010 by Daniele Varrazzo. +""" + +from docutils import nodes, utils +from docutils.parsers.rst import roles + +def sql_role(name, rawtext, text, lineno, inliner, options={}, content=[]): + text = utils.unescape(text) + options['classes'] = ['sql'] + return [nodes.literal(rawtext, text, **options)], [] + +def setup(app): + roles.register_local_role('sql', sql_role) + diff -Nru psycopg2-2.0.13/doc/src/tools/stitch_text.py psycopg2-2.4.5/doc/src/tools/stitch_text.py --- psycopg2-2.0.13/doc/src/tools/stitch_text.py 1970-01-01 00:00:00.000000000 +0000 +++ psycopg2-2.4.5/doc/src/tools/stitch_text.py 2011-02-06 15:58:34.000000000 +0000 @@ -0,0 +1,56 @@ +#! /usr/bin/env python +"""A script to stitch together the generated text files in the correct order. +""" + +import os +import sys + +def main(): + if len(sys.argv) != 3: + print >>sys.stderr, "usage: %s index.rst text-dir" + return 2 + + _, index, txt_dir = sys.argv + + for fb in iter_file_base(index): + emit(fb, txt_dir) + + return 0 + +def iter_file_base(fn): + have_line = iter(open(fn)).next + + while not have_line().startswith('.. toctree'): + pass + while have_line().strip().startswith(':'): + pass + + yield os.path.splitext(os.path.basename(fn))[0] + + n = 0 + while 1: + line = have_line() + if line.isspace(): + continue + if line.startswith(".."): + break + n += 1 + yield line.strip() + + if n < 5: + # maybe format changed? + raise Exception("Not enough files found. Format change in index.rst?") + +def emit(basename, txt_dir): + for line in open(os.path.join(txt_dir, basename + ".txt")): + line = line.replace("``", "'") + sys.stdout.write(line) + + # some space between sections + print + print + + +if __name__ == '__main__': + sys.exit(main()) + diff -Nru psycopg2-2.0.13/doc/src/tz.rst psycopg2-2.4.5/doc/src/tz.rst --- psycopg2-2.0.13/doc/src/tz.rst 1970-01-01 00:00:00.000000000 +0000 +++ psycopg2-2.4.5/doc/src/tz.rst 2011-02-27 12:03:48.000000000 +0000 @@ -0,0 +1,16 @@ +`psycopg2.tz` -- ``tzinfo`` implementations for Psycopg 2 +=============================================================== + +.. sectionauthor:: Daniele Varrazzo + +.. module:: psycopg2.tz + +This module holds two different tzinfo implementations that can be used as the +`tzinfo` argument to `~datetime.datetime` constructors, directly passed to +Psycopg functions or used to set the `cursor.tzinfo_factory` attribute in +cursors. + +.. autoclass:: psycopg2.tz.FixedOffsetTimezone + +.. autoclass:: psycopg2.tz.LocalTimezone + diff -Nru psycopg2-2.0.13/doc/src/usage.rst psycopg2-2.4.5/doc/src/usage.rst --- psycopg2-2.0.13/doc/src/usage.rst 1970-01-01 00:00:00.000000000 +0000 +++ psycopg2-2.4.5/doc/src/usage.rst 2012-03-28 21:09:15.000000000 +0000 @@ -0,0 +1,780 @@ +Basic module usage +================== + +.. sectionauthor:: Daniele Varrazzo + +.. index:: + pair: Example; Usage + +The basic Psycopg usage is common to all the database adapters implementing +the |DBAPI|_ protocol. Here is an interactive session showing some of the +basic commands:: + + >>> import psycopg2 + + # Connect to an existing database + >>> conn = psycopg2.connect("dbname=test user=postgres") + + # Open a cursor to perform database operations + >>> cur = conn.cursor() + + # Execute a command: this creates a new table + >>> cur.execute("CREATE TABLE test (id serial PRIMARY KEY, num integer, data varchar);") + + # Pass data to fill a query placeholders and let Psycopg perform + # the correct conversion (no more SQL injections!) + >>> cur.execute("INSERT INTO test (num, data) VALUES (%s, %s)", + ... (100, "abc'def")) + + # Query the database and obtain data as Python objects + >>> cur.execute("SELECT * FROM test;") + >>> cur.fetchone() + (1, 100, "abc'def") + + # Make the changes to the database persistent + >>> conn.commit() + + # Close communication with the database + >>> cur.close() + >>> conn.close() + + +The main entry points of Psycopg are: + +- The function `~psycopg2.connect()` creates a new database session and + returns a new `connection` instance. + +- The class `connection` encapsulates a database session. It allows to: + + - create new `cursor`\s using the `~connection.cursor()` method to + execute database commands and queries, + + - terminate the session using the methods `~connection.commit()` or + `~connection.rollback()`. + +- The class `cursor` allows interaction with the database: + + - send commands to the database using methods such as `~cursor.execute()` + and `~cursor.executemany()`, + + - retrieve data from the database :ref:`by iteration ` or + using methods such as `~cursor.fetchone()`, `~cursor.fetchmany()`, + `~cursor.fetchall()`. + + + +.. index:: + pair: Query; Parameters + +.. _query-parameters: + +Passing parameters to SQL queries +--------------------------------- + +Psycopg casts Python variables to SQL literals by type. Many standard Python types +are already `adapted to the correct SQL representation`__. + +.. __: python-types-adaptation_ + +Example: the Python function call:: + + >>> cur.execute( + ... """INSERT INTO some_table (an_int, a_date, a_string) + ... VALUES (%s, %s, %s);""", + ... (10, datetime.date(2005, 11, 18), "O'Reilly")) + +is converted into the SQL command:: + + INSERT INTO some_table (an_int, a_date, a_string) + VALUES (10, '2005-11-18', 'O''Reilly'); + +Named arguments are supported too using :samp:`%({name})s` placeholders. +Using named arguments the values can be passed to the query in any order and +many placeholders can use the same values:: + + >>> cur.execute( + ... """INSERT INTO some_table (an_int, a_date, another_date, a_string) + ... VALUES (%(int)s, %(date)s, %(date)s, %(str)s);""", + ... {'int': 10, 'str': "O'Reilly", 'date': datetime.date(2005, 11, 18)}) + +While the mechanism resembles regular Python strings manipulation, there are a +few subtle differences you should care about when passing parameters to a +query: + +- The Python string operator ``%`` is not used: the `~cursor.execute()` + method accepts a tuple or dictionary of values as second parameter. + |sql-warn|__. + + .. |sql-warn| replace:: **Never** use ``%`` or ``+`` to merge values + into queries + + .. __: sql-injection_ + +- The variables placeholder must *always be a* ``%s``, even if a different + placeholder (such as a ``%d`` for integers or ``%f`` for floats) may look + more appropriate:: + + >>> cur.execute("INSERT INTO numbers VALUES (%d)", (42,)) # WRONG + >>> cur.execute("INSERT INTO numbers VALUES (%s)", (42,)) # correct + +- For positional variables binding, *the second argument must always be a + sequence*, even if it contains a single variable. And remember that Python + requires a comma to create a single element tuple:: + + >>> cur.execute("INSERT INTO foo VALUES (%s)", "bar") # WRONG + >>> cur.execute("INSERT INTO foo VALUES (%s)", ("bar")) # WRONG + >>> cur.execute("INSERT INTO foo VALUES (%s)", ("bar",)) # correct + >>> cur.execute("INSERT INTO foo VALUES (%s)", ["bar"]) # correct + +- Only variable values should be bound via this method: it shouldn't be used + to set table or field names. For these elements, ordinary string formatting + should be used before running `~cursor.execute()`. + + + +.. index:: Security, SQL injection + +.. _sql-injection: + +The problem with the query parameters +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The SQL representation for many data types is often not the same of the Python +string representation. The classic example is with single quotes in +strings: SQL uses them as string constants bounds and requires them to be +escaped, whereas in Python single quotes can be left unescaped in strings +bounded by double quotes. For this reason a naïve approach to the composition +of query strings, e.g. using string concatenation, is a recipe for terrible +problems:: + + >>> SQL = "INSERT INTO authors (name) VALUES ('%s');" # NEVER DO THIS + >>> data = ("O'Reilly", ) + >>> cur.execute(SQL % data) # THIS WILL FAIL MISERABLY + ProgrammingError: syntax error at or near "Reilly" + LINE 1: INSERT INTO authors (name) VALUES ('O'Reilly') + ^ + +If the variable containing the data to be sent to the database comes from an +untrusted source (e.g. a form published on a web site) an attacker could +easily craft a malformed string, either gaining access to unauthorized data or +performing destructive operations on the database. This form of attack is +called `SQL injection`_ and is known to be one of the most widespread forms of +attack to servers. Before continuing, please print `this page`__ as a memo and +hang it onto your desk. + +.. _SQL injection: http://en.wikipedia.org/wiki/SQL_injection +.. __: http://xkcd.com/327/ + +Psycopg can `automatically convert Python objects to and from SQL +literals`__: using this feature your code will be more robust and +reliable. We must stress this point: + +.. __: python-types-adaptation_ + +.. warning:: + + Never, **never**, **NEVER** use Python string concatenation (``+``) or + string parameters interpolation (``%``) to pass variables to a SQL query + string. Not even at gunpoint. + +The correct way to pass variables in a SQL command is using the second +argument of the `~cursor.execute()` method:: + + >>> SQL = "INSERT INTO authors (name) VALUES (%s);" # Note: no quotes + >>> data = ("O'Reilly", ) + >>> cur.execute(SQL, data) # Note: no % operator + + + +.. index:: + single: Adaptation + pair: Objects; Adaptation + single: Data types; Adaptation + +.. _python-types-adaptation: + +Adaptation of Python values to SQL types +---------------------------------------- + +Many standards Python types are adapted into SQL and returned as Python +objects when a query is executed. + +If you need to convert other Python types to and from PostgreSQL data types, +see :ref:`adapting-new-types` and :ref:`type-casting-from-sql-to-python`. You +can also find a few other specialized adapters in the `psycopg2.extras` +module. + +In the following examples the method `~cursor.mogrify()` is used to show +the SQL string that would be sent to the database. + +.. _adapt-consts: + +.. index:: + pair: None; Adaptation + single: NULL; Adaptation + pair: Boolean; Adaptation + +- Python `None` and boolean values `True` and `False` are converted into the + proper SQL literals:: + + >>> cur.mogrify("SELECT %s, %s, %s;", (None, True, False)) + >>> 'SELECT NULL, true, false;' + +.. _adapt-numbers: + +.. index:: + single: Adaptation; numbers + single: Integer; Adaptation + single: Float; Adaptation + single: Decimal; Adaptation + +- Numeric objects: `int`, `long`, `float`, `~decimal.Decimal` are converted in + the PostgreSQL numerical representation:: + + >>> cur.mogrify("SELECT %s, %s, %s, %s;", (10, 10L, 10.0, Decimal("10.00"))) + >>> 'SELECT 10, 10, 10.0, 10.00;' + +.. _adapt-string: + +.. index:: + pair: Strings; Adaptation + single: Unicode; Adaptation + +- String types: `str`, `unicode` are converted in SQL string syntax. + `!unicode` objects (`!str` in Python 3) are encoded in the connection + `~connection.encoding` to be sent to the backend: trying to send a character + not supported by the encoding will result in an error. Received data can be + converted either as `!str` or `!unicode`: see :ref:`unicode-handling`. + +.. _adapt-binary: + +.. index:: + single: Buffer; Adaptation + single: bytea; Adaptation + single: bytes; Adaptation + single: bytearray; Adaptation + single: memoryview; Adaptation + single: Binary string + +- Binary types: Python types representing binary objects are converted into + PostgreSQL binary string syntax, suitable for :sql:`bytea` fields. Such + types are `buffer` (only available in Python 2), `memoryview` (available + from Python 2.7), `bytearray` (available from Python 2.6) and `bytes` + (only from Python 3: the name is available from Python 2.6 but it's only an + alias for the type `!str`). Any object implementing the `Revised Buffer + Protocol`__ should be usable as binary type where the protocol is supported + (i.e. from Python 2.6). Received data is returned as `!buffer` (in Python 2) + or `!memoryview` (in Python 3). + + .. __: http://www.python.org/dev/peps/pep-3118/ + + .. versionchanged:: 2.4 + only strings were supported before. + + .. versionchanged:: 2.4.1 + can parse the 'hex' format from 9.0 servers without relying on the + version of the client library. + + .. note:: + + In Python 2, if you have binary data in a `!str` object, you can pass them + to a :sql:`bytea` field using the `psycopg2.Binary` wrapper:: + + mypic = open('picture.png', 'rb').read() + curs.execute("insert into blobs (file) values (%s)", + (psycopg2.Binary(mypic),)) + + .. warning:: + + Since version 9.0 PostgreSQL uses by default `a new "hex" format`__ to + emit :sql:`bytea` fields. Starting from Psycopg 2.4.1 the format is + correctly supported. If you use a previous version you will need some + extra care when receiving bytea from PostgreSQL: you must have at least + libpq 9.0 installed on the client or alternatively you can set the + `bytea_output`__ configuration parameter to ``escape``, either in the + server configuration file or in the client session (using a query such as + ``SET bytea_output TO escape;``) before receiving binary data. + + .. __: http://www.postgresql.org/docs/current/static/datatype-binary.html + .. __: http://www.postgresql.org/docs/current/static/runtime-config-client.html#GUC-BYTEA-OUTPUT + +.. _adapt-date: + +.. index:: + single: Adaptation; Date/Time objects + single: Date objects; Adaptation + single: Time objects; Adaptation + single: Interval objects; Adaptation + single: mx.DateTime; Adaptation + +- Date and time objects: builtin `~datetime.datetime`, `~datetime.date`, + `~datetime.time`, `~datetime.timedelta` are converted into PostgreSQL's + :sql:`timestamp`, :sql:`date`, :sql:`time`, :sql:`interval` data types. + Time zones are supported too. The Egenix `mx.DateTime`_ objects are adapted + the same way:: + + >>> dt = datetime.datetime.now() + >>> dt + datetime.datetime(2010, 2, 8, 1, 40, 27, 425337) + + >>> cur.mogrify("SELECT %s, %s, %s;", (dt, dt.date(), dt.time())) + "SELECT '2010-02-08T01:40:27.425337', '2010-02-08', '01:40:27.425337';" + + >>> cur.mogrify("SELECT %s;", (dt - datetime.datetime(2010,1,1),)) + "SELECT '38 days 6027.425337 seconds';" + +.. _adapt-list: + +.. index:: + single: Array; Adaptation + double: Lists; Adaptation + +- Python lists are converted into PostgreSQL :sql:`ARRAY`\ s:: + + >>> cur.mogrify("SELECT %s;", ([10, 20, 30], )) + 'SELECT ARRAY[10, 20, 30];' + + .. note:: + + Reading back from PostgreSQL, arrays are converted to list of Python + objects as expected, but only if the types are known one. Arrays of + unknown types are returned as represented by the database (e.g. + ``{a,b,c}``). You can easily create a typecaster for :ref:`array of + unknown types `. + +.. _adapt-tuple: + +.. index:: + double: Tuple; Adaptation + single: IN operator + +- Python tuples are converted in a syntax suitable for the SQL :sql:`IN` + operator and to represent a composite type:: + + >>> cur.mogrify("SELECT %s IN %s;", (10, (10, 20, 30))) + 'SELECT 10 IN (10, 20, 30);' + + .. note:: + + SQL doesn't allow an empty list in the IN operator, so your code should + guard against empty tuples. + + If you want PostgreSQL composite types to be converted into a Python + tuple/namedtuple you can use the `~psycopg2.extras.register_composite()` + function. + + .. versionadded:: 2.0.6 + the tuple :sql:`IN` adaptation. + + .. versionchanged:: 2.0.14 + the tuple :sql:`IN` adapter is always active. In previous releases it + was necessary to import the `~psycopg2.extensions` module to have it + registered. + + .. versionchanged:: 2.3 + `~collections.namedtuple` instances are adapted like regular tuples and + can thus be used to represent composite types. + +.. _adapt-dict: + +.. index:: + single: dict; Adaptation + single: hstore; Adaptation + +- Python dictionaries are converted into the |hstore|_ data type. By default + the adapter is not enabled: see `~psycopg2.extras.register_hstore()` for + further details. + + .. |hstore| replace:: :sql:`hstore` + .. _hstore: http://www.postgresql.org/docs/current/static/hstore.html + + .. versionadded:: 2.3 + the :sql:`hstore` adaptation. + + +.. index:: + single: Unicode + +.. _unicode-handling: + +Unicode handling +^^^^^^^^^^^^^^^^ + +Psycopg can exchange Unicode data with a PostgreSQL database. Python +`!unicode` objects are automatically *encoded* in the client encoding +defined on the database connection (the `PostgreSQL encoding`__, available in +`connection.encoding`, is translated into a `Python codec`__ using the +`~psycopg2.extensions.encodings` mapping):: + + >>> print u, type(u) + àèìòù€ + + >>> cur.execute("INSERT INTO test (num, data) VALUES (%s,%s);", (74, u)) + +.. __: http://www.postgresql.org/docs/current/static/multibyte.html +.. __: http://docs.python.org/library/codecs.html#standard-encodings + +When reading data from the database, in Python 2 the strings returned are +usually 8 bit `!str` objects encoded in the database client encoding:: + + >>> print conn.encoding + UTF8 + + >>> cur.execute("SELECT data FROM test WHERE num = 74") + >>> x = cur.fetchone()[0] + >>> print x, type(x), repr(x) + àèìòù€ '\xc3\xa0\xc3\xa8\xc3\xac\xc3\xb2\xc3\xb9\xe2\x82\xac' + + >>> conn.set_client_encoding('LATIN9') + + >>> cur.execute("SELECT data FROM test WHERE num = 74") + >>> x = cur.fetchone()[0] + >>> print type(x), repr(x) + '\xe0\xe8\xec\xf2\xf9\xa4' + +In Python 3 instead the strings are automatically *decoded* in the connection +`~connection.encoding`, as the `!str` object can represent Unicode characters. +In Python 2 you must register a :ref:`typecaster +` in order to receive `!unicode` objects:: + + >>> psycopg2.extensions.register_type(psycopg2.extensions.UNICODE, cur) + + >>> cur.execute("SELECT data FROM test WHERE num = 74") + >>> x = cur.fetchone()[0] + >>> print x, type(x), repr(x) + àèìòù€ u'\xe0\xe8\xec\xf2\xf9\u20ac' + +In the above example, the `~psycopg2.extensions.UNICODE` typecaster is +registered only on the cursor. It is also possible to register typecasters on +the connection or globally: see the function +`~psycopg2.extensions.register_type()` and +:ref:`type-casting-from-sql-to-python` for details. + +.. note:: + + In Python 2, if you want to uniformly receive all your database input in + Unicode, you can register the related typecasters globally as soon as + Psycopg is imported:: + + import psycopg2 + import psycopg2.extensions + psycopg2.extensions.register_type(psycopg2.extensions.UNICODE) + psycopg2.extensions.register_type(psycopg2.extensions.UNICODEARRAY) + + and then forget about this story. + + +.. index:: + single: Time Zones + +.. _tz-handling: + +Time zones handling +^^^^^^^^^^^^^^^^^^^ + +The PostgreSQL type :sql:`timestamp with time zone` is converted into Python +`~datetime.datetime` objects with a `~datetime.datetime.tzinfo` attribute set +to a `~psycopg2.tz.FixedOffsetTimezone` instance. + + >>> cur.execute("SET TIME ZONE 'Europe/Rome';") # UTC + 1 hour + >>> cur.execute("SELECT '2010-01-01 10:30:45'::timestamptz;") + >>> cur.fetchone()[0].tzinfo + psycopg2.tz.FixedOffsetTimezone(offset=60, name=None) + +Note that only time zones with an integer number of minutes are supported: +this is a limitation of the Python `datetime` module. A few historical time +zones had seconds in the UTC offset: these time zones will have the offset +rounded to the nearest minute, with an error of up to 30 seconds. + + >>> cur.execute("SET TIME ZONE 'Asia/Calcutta';") # offset was +5:53:20 + >>> cur.execute("SELECT '1930-01-01 10:30:45'::timestamptz;") + >>> cur.fetchone()[0].tzinfo + psycopg2.tz.FixedOffsetTimezone(offset=353, name=None) + +.. versionchanged:: 2.2.2 + timezones with seconds are supported (with rounding). Previously such + timezones raised an error. In order to deal with them in previous + versions use `psycopg2.extras.register_tstz_w_secs()`. + + +.. index:: Transaction, Begin, Commit, Rollback, Autocommit, Read only + +.. _transactions-control: + +Transactions control +-------------------- + +In Psycopg transactions are handled by the `connection` class. By +default, the first time a command is sent to the database (using one of the +`cursor`\ s created by the connection), a new transaction is created. +The following database commands will be executed in the context of the same +transaction -- not only the commands issued by the first cursor, but the ones +issued by all the cursors created by the same connection. Should any command +fail, the transaction will be aborted and no further command will be executed +until a call to the `~connection.rollback()` method. + +The connection is responsible to terminate its transaction, calling either the +`~connection.commit()` or `~connection.rollback()` method. Committed +changes are immediately made persistent into the database. Closing the +connection using the `~connection.close()` method or destroying the +connection object (using `!del` or letting it fall out of scope) +will result in an implicit `!rollback()` call. + +It is possible to set the connection in *autocommit* mode: this way all the +commands executed will be immediately committed and no rollback is possible. A +few commands (e.g. :sql:`CREATE DATABASE`, :sql:`VACUUM`...) require to be run +outside any transaction: in order to be able to run these commands from +Psycopg, the session must be in autocommit mode: you can use the +`~connection.autocommit` property (`~connection.set_isolation_level()` in +older versions). + +.. warning:: + + By default even a simple :sql:`SELECT` will start a transaction: in + long-running programs, if no further action is taken, the session will + remain "idle in transaction", a condition non desiderable for several + reasons (locks are held by the session, tables bloat...). For long lived + scripts, either make sure to terminate a transaction as soon as possible or + use an autocommit connection. + +A few other transaction properties can be set session-wide by the +`!connection`: for instance it is possible to have read-only transactions or +change the isolation level. See the `~connection.set_session()` method for all +the details. + + +.. index:: + pair: Server side; Cursor + pair: Named; Cursor + pair: DECLARE; SQL command + pair: FETCH; SQL command + pair: MOVE; SQL command + +.. _server-side-cursors: + +Server side cursors +------------------- + +When a database query is executed, the Psycopg `cursor` usually fetches +all the records returned by the backend, transferring them to the client +process. If the query returned an huge amount of data, a proportionally large +amount of memory will be allocated by the client. + +If the dataset is too large to be practically handled on the client side, it is +possible to create a *server side* cursor. Using this kind of cursor it is +possible to transfer to the client only a controlled amount of data, so that a +large dataset can be examined without keeping it entirely in memory. + +Server side cursor are created in PostgreSQL using the |DECLARE|_ command and +subsequently handled using :sql:`MOVE`, :sql:`FETCH` and :sql:`CLOSE` commands. + +Psycopg wraps the database server side cursor in *named cursors*. A named +cursor is created using the `~connection.cursor()` method specifying the +*name* parameter. Such cursor will behave mostly like a regular cursor, +allowing the user to move in the dataset using the `~cursor.scroll()` +method and to read the data using `~cursor.fetchone()` and +`~cursor.fetchmany()` methods. + +Named cursors are also :ref:`iterable ` like regular cursors. +Note however that before Psycopg 2.4 iteration was performed fetching one +record at time from the backend, resulting in a large overhead. The attribute +`~cursor.itersize` now controls how many records are fetched at time +during the iteration: the default value of 2000 allows to fetch about 100KB +per roundtrip assuming records of 10-20 columns of mixed number and strings; +you may decrease this value if you are dealing with huge records. + +Named cursors are usually created :sql:`WITHOUT HOLD`, meaning they live only +as long as the current transaction. Trying to fetch from a named cursor after +a `~connection.commit()` or to create a named cursor when the `connection` +transaction isolation level is set to `AUTOCOMMIT` will result in an exception. +It is possible to create a :sql:`WITH HOLD` cursor by specifying a `!True` +value for the `withhold` parameter to `~connection.cursor()` or by setting the +`~cursor.withhold` attribute to `!True` before calling `~cursor.execute()` on +the cursor. It is extremely important to always `~cursor.close()` such cursors, +otherwise they will continue to hold server-side resources until the connection +will be eventually closed. Also note that while :sql:`WITH HOLD` cursors +lifetime extends well after `~connection.commit()`, calling +`~connection.rollback()` will automatically close the cursor. + +.. note:: + + It is also possible to use a named cursor to consume a cursor created + in some other way than using the |DECLARE| executed by + `~cursor.execute()`. For example, you may have a PL/pgSQL function + returning a cursor:: + + CREATE FUNCTION reffunc(refcursor) RETURNS refcursor AS $$ + BEGIN + OPEN $1 FOR SELECT col FROM test; + RETURN $1; + END; + $$ LANGUAGE plpgsql; + + You can read the cursor content by calling the function with a regular, + non-named, Psycopg cursor: + + .. code-block:: python + + cur1 = conn.cursor() + cur1.callproc('reffunc', ['curname']) + + and then use a named cursor in the same transaction to "steal the cursor": + + .. code-block:: python + + cur2 = conn.cursor('curname') + for record in cur2: # or cur2.fetchone, fetchmany... + # do something with record + pass + + +.. |DECLARE| replace:: :sql:`DECLARE` +.. _DECLARE: http://www.postgresql.org/docs/current/static/sql-declare.html + + + +.. index:: Thread safety, Multithread, Multiprocess + +.. _thread-safety: + +Thread and process safety +------------------------- + +The Psycopg module and the `connection` objects are *thread-safe*: many +threads can access the same database either using separate sessions and +creating a `!connection` per thread or using the same +connection and creating separate `cursor`\ s. In |DBAPI|_ parlance, Psycopg is +*level 2 thread safe*. + +The difference between the above two approaches is that, using different +connections, the commands will be executed in different sessions and will be +served by different server processes. On the other hand, using many cursors on +the same connection, all the commands will be executed in the same session +(and in the same transaction if the connection is not in :ref:`autocommit +` mode), but they will be serialized. + +The above observations are only valid for regular threads: they don't apply to +forked processes nor to green threads. `libpq` connections `shouldn't be used by a +forked processes`__, so when using a module such as `multiprocessing` or a +forking web deploy method such as FastCGI make sure to create the connections +*after* the fork. + +.. __: http://www.postgresql.org/docs/current/static/libpq-connect.html#LIBPQ-CONNECT + +Connections shouldn't be shared either by different green threads: see +:ref:`green-support` for further details. + + + +.. index:: + pair: COPY; SQL command + +.. _copy: + +Using COPY TO and COPY FROM +--------------------------- + +Psycopg `cursor` objects provide an interface to the efficient +PostgreSQL |COPY|__ command to move data from files to tables and back. +The methods exposed are: + +`~cursor.copy_from()` + Reads data *from* a file-like object appending them to a database table + (:sql:`COPY table FROM file` syntax). The source file must have both + `!read()` and `!readline()` method. + +`~cursor.copy_to()` + Writes the content of a table *to* a file-like object (:sql:`COPY table TO + file` syntax). The target file must have a `write()` method. + +`~cursor.copy_expert()` + Allows to handle more specific cases and to use all the :sql:`COPY` + features available in PostgreSQL. + +Please refer to the documentation of the single methods for details and +examples. + +.. |COPY| replace:: :sql:`COPY` +.. __: http://www.postgresql.org/docs/current/static/sql-copy.html + + + +.. index:: + single: Large objects + +.. _large-objects: + +Access to PostgreSQL large objects +---------------------------------- + +PostgreSQL offers support for `large objects`__, which provide stream-style +access to user data that is stored in a special large-object structure. They +are useful with data values too large to be manipulated conveniently as a +whole. + +.. __: http://www.postgresql.org/docs/current/static/largeobjects.html + +Psycopg allows access to the large object using the +`~psycopg2.extensions.lobject` class. Objects are generated using the +`connection.lobject()` factory method. Data can be retrieved either as bytes +or as Unicode strings. + +Psycopg large object support efficient import/export with file system files +using the |lo_import|_ and |lo_export|_ libpq functions. + +.. |lo_import| replace:: `!lo_import()` +.. _lo_import: http://www.postgresql.org/docs/current/static/lo-interfaces.html#LO-IMPORT +.. |lo_export| replace:: `!lo_export()` +.. _lo_export: http://www.postgresql.org/docs/current/static/lo-interfaces.html#LO-EXPORT + + + +.. index:: + pair: Two-phase commit; Transaction + +.. _tpc: + +Two-Phase Commit protocol support +--------------------------------- + +.. versionadded:: 2.3 + +Psycopg exposes the two-phase commit features available since PostgreSQL 8.1 +implementing the *two-phase commit extensions* proposed by the |DBAPI|. + +The |DBAPI| model of two-phase commit is inspired by the `XA specification`__, +according to which transaction IDs are formed from three components: + +- a format ID (non-negative 32 bit integer) +- a global transaction ID (string not longer than 64 bytes) +- a branch qualifier (string not longer than 64 bytes) + +For a particular global transaction, the first two components will be the same +for all the resources. Every resource will be assigned a different branch +qualifier. + +According to the |DBAPI| specification, a transaction ID is created using the +`connection.xid()` method. Once you have a transaction id, a distributed +transaction can be started with `connection.tpc_begin()`, prepared using +`~connection.tpc_prepare()` and completed using `~connection.tpc_commit()` or +`~connection.tpc_rollback()`. Transaction IDs can also be retrieved from the +database using `~connection.tpc_recover()` and completed using the above +`!tpc_commit()` and `!tpc_rollback()`. + +PostgreSQL doesn't follow the XA standard though, and the ID for a PostgreSQL +prepared transaction can be any string up to 200 characters long. +Psycopg's `~psycopg2.extensions.Xid` objects can represent both XA-style +transactions IDs (such as the ones created by the `!xid()` method) and +PostgreSQL transaction IDs identified by an unparsed string. + +The format in which the Xids are converted into strings passed to the +database is the same employed by the `PostgreSQL JDBC driver`__: this should +allow interoperation between tools written in Python and in Java. For example +a recovery tool written in Python would be able to recognize the components of +transactions produced by a Java program. + +For further details see the documentation for the above methods. + +.. __: http://www.opengroup.org/bookstore/catalog/c193.htm +.. __: http://jdbc.postgresql.org/ + diff -Nru psycopg2-2.0.13/doc/TODO psycopg2-2.4.5/doc/TODO --- psycopg2-2.0.13/doc/TODO 2009-04-26 12:09:21.000000000 +0000 +++ psycopg2-2.4.5/doc/TODO 1970-01-01 00:00:00.000000000 +0000 @@ -1,33 +0,0 @@ -TODO list for psycopg 2 or later -******************************** - -Move items to the DONE section only after writing a test for the -implementation. Also add a note on how the item was resolved. -(Obviously I was joking about the test..) - -* Find a better way to compile the type-casting code instead of including it - in typecast.c directy. (Including is not that bad, but the need to touch - psycopg/typecast.c every time is bad bad bad.) - -* executemany() should _not_ take the async flag, remove it and force multiple - queries to be synchronous. - -* Fix all the docstrings. - -* Support the protocols API fully. - -* Unify the common code in typecast_datetime.c and typecast_mxdatetime.c. - -* Port typecasters to new-style classes. - -* Write a complete postgresql<->python encodings table. - -* Implement binary typecasters (should be easy, but it will take time.) - -DONE -==== - -* Convert type-casters to new-style types in Python 2.2+. - -* callproc() never worked, fix it or remove it and raise right exception. - [Removed callproc code, now an exception is raised.] diff -Nru psycopg2-2.0.13/examples/binary.py psycopg2-2.4.5/examples/binary.py --- psycopg2-2.0.13/examples/binary.py 2009-04-26 12:09:21.000000000 +0000 +++ psycopg2-2.4.5/examples/binary.py 2011-12-19 10:30:20.000000000 +0000 @@ -1,22 +1,22 @@ # binary.py - working with binary data # -# Copyright (C) 2001-2004 Federico Di Gregorio +# Copyright (C) 2001-2010 Federico Di Gregorio # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by the -# Free Software Foundation; either version 2, or (at your option) any later -# version. +# psycopg2 is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. # -# This program is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY -# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# for more details. +# psycopg2 is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +# License for more details. ## put in DSN your DSN string DSN = 'dbname=test' -## don't modify anything below tis line (except for experimenting) +## don't modify anything below this line (except for experimenting) import sys import psycopg2 @@ -79,7 +79,7 @@ print "done" print " python type of image data is", type(row[0]) -# this rollback is requires because we can't drop a table with a binary cusor +# this rollback is required because we can't drop a table with a binary cusor # declared and still open conn.rollback() diff -Nru psycopg2-2.0.13/examples/copy_from.py psycopg2-2.4.5/examples/copy_from.py --- psycopg2-2.0.13/examples/copy_from.py 2009-04-26 12:09:21.000000000 +0000 +++ psycopg2-2.4.5/examples/copy_from.py 2011-12-19 10:30:20.000000000 +0000 @@ -12,13 +12,12 @@ # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License # for more details. -# ## put in DSN your DSN string DSN = 'dbname=test' -## don't modify anything below tis line (except for experimenting) +## don't modify anything below this line (except for experimenting) import sys import os @@ -166,7 +165,7 @@ curs.copy_from(data, 'test_copy') except StandardError, err: conn.rollback() - print " Catched error (as expected):\n", err + print " Caught error (as expected):\n", err conn.rollback() diff -Nru psycopg2-2.0.13/examples/copy_to.py psycopg2-2.4.5/examples/copy_to.py --- psycopg2-2.0.13/examples/copy_to.py 2009-04-26 12:09:21.000000000 +0000 +++ psycopg2-2.4.5/examples/copy_to.py 2011-12-19 10:30:20.000000000 +0000 @@ -12,13 +12,12 @@ # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License # for more details. -# ## put in DSN your DSN string DSN = 'dbname=test' -## don't modify anything below tis line (except for experimenting) +## don't modify anything below this line (except for experimenting) import sys import os diff -Nru psycopg2-2.0.13/examples/cursor.py psycopg2-2.4.5/examples/cursor.py --- psycopg2-2.0.13/examples/cursor.py 2009-04-26 12:09:21.000000000 +0000 +++ psycopg2-2.4.5/examples/cursor.py 2011-12-19 10:30:20.000000000 +0000 @@ -1,16 +1,16 @@ # cursor.py - how to subclass the cursor type # -# Copyright (C) 2004 Federico Di Gregorio +# Copyright (C) 2004-2010 Federico Di Gregorio # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by the -# Free Software Foundation; either version 2, or (at your option) any later -# version. +# psycopg2 is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. # -# This program is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY -# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# for more details. +# psycopg2 is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +# License for more details. ## put in DSN your DSN string @@ -58,6 +58,6 @@ try: curs.fetchone() except NoDataError, err: - print "Exception caugth:", err + print "Exception caught:", err conn.rollback() diff -Nru psycopg2-2.0.13/examples/dialtone.py psycopg2-2.4.5/examples/dialtone.py --- psycopg2-2.0.13/examples/dialtone.py 2009-04-26 12:09:21.000000000 +0000 +++ psycopg2-2.4.5/examples/dialtone.py 2011-12-19 10:30:20.000000000 +0000 @@ -104,10 +104,10 @@ - Discussion Psycopg 2 has a great new feature: adaptation. The big thing about -adaptation is that it enable the programmer to glue most of the +adaptation is that it enables the programmer to glue most of the code out there without many difficulties. -This recipe tries to focus the attention on a way to generate SQL queries to +This recipe tries to focus attention on a way to generate SQL queries to insert completely new objects inside a database. As you can see objects do not know anything about the code that is handling them. We specify all the fields that we need for each object through the persistent_fields dict. @@ -116,7 +116,7 @@ register_adapter(Album, ObjectMapper) register_adapter(Order, ObjectMapper) -In these line we notify the system that when we call adapt with an Album instance +In these lines we notify the system that when we call adapt with an Album instance as an argument we want it to istantiate ObjectMapper passing the Album instance as argument (self.orig in the ObjectMapper class). diff -Nru psycopg2-2.0.13/examples/dict.py psycopg2-2.4.5/examples/dict.py --- psycopg2-2.0.13/examples/dict.py 2009-04-26 12:09:21.000000000 +0000 +++ psycopg2-2.4.5/examples/dict.py 2011-02-06 15:58:34.000000000 +0000 @@ -1,16 +1,16 @@ # dict.py - using DictCUrsor/DictRow # -# Copyright (C) 2005 Federico Di Gregorio +# Copyright (C) 2005-2010 Federico Di Gregorio # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by the -# Free Software Foundation; either version 2, or (at your option) any later -# version. +# psycopg2 is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. # -# This program is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY -# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# for more details. +# psycopg2 is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +# License for more details. ## put in DSN your DSN string diff -Nru psycopg2-2.0.13/examples/dt.py psycopg2-2.4.5/examples/dt.py --- psycopg2-2.0.13/examples/dt.py 2009-04-26 12:09:21.000000000 +0000 +++ psycopg2-2.4.5/examples/dt.py 2011-12-19 10:30:20.000000000 +0000 @@ -1,22 +1,22 @@ # datetime.py - example of using date and time types # -# Copyright (C) 2001-2004 Federico Di Gregorio +# Copyright (C) 2001-2010 Federico Di Gregorio # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by the -# Free Software Foundation; either version 2, or (at your option) any later -# version. +# psycopg2 is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. # -# This program is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY -# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# for more details. +# psycopg2 is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +# License for more details. ## put in DSN your DSN string DSN = 'dbname=test' -## don't modify anything below tis line (except for experimenting) +## don't modify anything below this line (except for experimenting) import sys import psycopg2 @@ -73,7 +73,7 @@ curs.execute("SELECT d, t, dt, z FROM test_dt WHERE k = 1") for n, x in zip(mx1[1:], curs.fetchone()): try: - # this will work only is psycopg has been compiled with datetime + # this will work only if psycopg has been compiled with datetime # as the default typecaster for date/time values s = repr(n) + "\n -> " + str(adapt(n)) + \ "\n -> " + repr(x) + "\n -> " + x.isoformat() @@ -87,7 +87,7 @@ curs.execute("SELECT d, t, dt, z FROM test_dt WHERE k = 2") for n, x in zip(dt1[1:], curs.fetchone()): try: - # this will work only is psycopg has been compiled with datetime + # this will work only if psycopg has been compiled with datetime # as the default typecaster for date/time values s = repr(n) + "\n -> " + repr(x) + "\n -> " + x.isoformat() except: diff -Nru psycopg2-2.0.13/examples/encoding.py psycopg2-2.4.5/examples/encoding.py --- psycopg2-2.0.13/examples/encoding.py 2009-04-26 12:09:21.000000000 +0000 +++ psycopg2-2.4.5/examples/encoding.py 2011-02-06 15:58:34.000000000 +0000 @@ -1,17 +1,17 @@ -# enkoding.py - show to change client enkoding (and test it works) +# encoding.py - show to change client enkoding (and test it works) # -*- encoding: utf8 -*- # -# Copyright (C) 2004 Federico Di Gregorio +# Copyright (C) 2004-2010 Federico Di Gregorio # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by the -# Free Software Foundation; either version 2, or (at your option) any later -# version. +# psycopg2 is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. # -# This program is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY -# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# for more details. +# psycopg2 is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +# License for more details. ## put in DSN your DSN string diff -Nru psycopg2-2.0.13/examples/fetch.py psycopg2-2.4.5/examples/fetch.py --- psycopg2-2.0.13/examples/fetch.py 2009-04-26 12:09:21.000000000 +0000 +++ psycopg2-2.4.5/examples/fetch.py 2011-12-19 10:30:20.000000000 +0000 @@ -1,23 +1,22 @@ # fetch.py -- example about declaring cursors # -# Copyright (C) 2001-2005 Federico Di Gregorio -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by the -# Free Software Foundation; either version 2, or (at your option) any later -# version. -# -# This program is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY -# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# for more details. +# Copyright (C) 2001-2010 Federico Di Gregorio # +# psycopg2 is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# psycopg2 is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +# License for more details. ## put in DSN your DSN string DSN = 'dbname=test' -## don't modify anything below tis line (except for experimenting) +## don't modify anything below this line (except for experimenting) import sys import psycopg2 @@ -53,7 +52,7 @@ # does some nice tricks with the transaction and postgres cursors # (remember to always commit or rollback before a DECLARE) # -# we don't need to DECLARE ourselves, psycopg now support named +# we don't need to DECLARE ourselves, psycopg now supports named # cursors (but we leave the code here, comments, as an example of # what psycopg is doing under the hood) # diff -Nru psycopg2-2.0.13/examples/lastrowid.py psycopg2-2.4.5/examples/lastrowid.py --- psycopg2-2.0.13/examples/lastrowid.py 2009-04-26 12:09:21.000000000 +0000 +++ psycopg2-2.4.5/examples/lastrowid.py 2011-12-19 10:30:20.000000000 +0000 @@ -1,22 +1,22 @@ # lastrowid.py - example of using .lastrowid attribute # -# Copyright (C) 2001-2004 Federico Di Gregorio +# Copyright (C) 2001-2010 Federico Di Gregorio # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by the -# Free Software Foundation; either version 2, or (at your option) any later -# version. +# psycopg2 is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. # -# This program is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY -# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# for more details. +# psycopg2 is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +# License for more details. ## put in DSN your DSN string DSN = 'dbname=test' -## don't modify anything below tis line (except for experimenting) +## don't modify anything below this line (except for experimenting) import sys, psycopg2 diff -Nru psycopg2-2.0.13/examples/mogrify.py psycopg2-2.4.5/examples/mogrify.py --- psycopg2-2.0.13/examples/mogrify.py 2009-04-26 12:09:21.000000000 +0000 +++ psycopg2-2.4.5/examples/mogrify.py 2011-02-06 15:58:34.000000000 +0000 @@ -1,17 +1,17 @@ # mogrify.py - test all possible simple type mogrifications # -*- encoding: latin1 -*- # -# Copyright (C) 2004 Federico Di Gregorio +# Copyright (C) 2004-2010 Federico Di Gregorio # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by the -# Free Software Foundation; either version 2, or (at your option) any later -# version. +# psycopg2 is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. # -# This program is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY -# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# for more details. +# psycopg2 is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +# License for more details.. ## put in DSN your DSN string diff -Nru psycopg2-2.0.13/examples/myfirstrecipe.py psycopg2-2.4.5/examples/myfirstrecipe.py --- psycopg2-2.0.13/examples/myfirstrecipe.py 2009-04-26 12:09:21.000000000 +0000 +++ psycopg2-2.4.5/examples/myfirstrecipe.py 2011-02-06 15:58:34.000000000 +0000 @@ -1,6 +1,6 @@ """ Using a tuple as a bound variable in "SELECT ... IN (...)" clauses -in PostgreSQL using psycopg 2 +in PostgreSQL using psycopg2 Some time ago someone asked on the psycopg mailing list how to have a bound variable expand to the right SQL for an SELECT IN clause: @@ -18,29 +18,29 @@ writing a wrapper class returning the pre-quoted text in an __str__ method. -But psycopg 2 offers a simple and elegant solution by partially -implementing the Object Adaptation from PEP 246. psycopg 2 (still in -beta and currently labeled as 1.99.9) moves the type-casting logic into -external adapters and a somehow broken adapt() function. +But psycopg2 offers a simple and elegant solution by partially +implementing the Object Adaptation from PEP 246. psycopg2 moves +the type-casting logic into external adapters and a somehow +broken adapt() function. -While the original adapt() takes 3 arguments, psycopg's one only takes +While the original adapt() takes 3 arguments, psycopg2's one only takes 1: the bound variable to be adapted. The result is an object supporting -a not-yet well defined protocol that we can call IPsycopgSQLQuote: +a not-yet well defined protocol that we can call ISQLQuote: - class IPsycopgSQLQuote: + class ISQLQuote: def getquoted(self): "Returns a quoted string representing the bound variable." - def getbinary(self): - "Returns a binary quoted string representing the bound variable." - - def getbuffer(self): + def getbinary(self): + "Returns a binary quoted string representing the bound variable." + + def getbuffer(self): "Returns the wrapped object itself." __str__ = getquoted -Then one of the functions (usually .getquoted()) is called by psycopg at +Then one of the functions (usually .getquoted()) is called by psycopg2 at the right time to obtain the right, sql-quoted representation for the corresponding bound variable. @@ -52,20 +52,24 @@ adapter that adapts tuple objects into the right SQL string, by calling recursively adapt() on each element. -Note: psycopg 2 adapter code is still very young and will probably move -to a more 'standard' (3 arguments) implementation for the adapt() -function; as long as that does not slow down too much query execution. - -Psycopg 2 development can be tracked on the psycopg mailing list: +psycopg2 development can be tracked on the psycopg mailing list: http://lists.initd.org/mailman/listinfo/psycopg -and on the psycopg 2 wiki: - - http://wiki.initd.org/Projects/Psycopg2 - """ +# Copyright (C) 2001-2010 Federico Di Gregorio +# +# psycopg2 is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# psycopg2 is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +# License for more details. + import psycopg2 import psycopg2.extensions from psycopg2.extensions import adapt as psycoadapt diff -Nru psycopg2-2.0.13/examples/notify.py psycopg2-2.4.5/examples/notify.py --- psycopg2-2.0.13/examples/notify.py 2009-04-26 12:09:21.000000000 +0000 +++ psycopg2-2.4.5/examples/notify.py 2011-12-19 10:30:20.000000000 +0000 @@ -1,26 +1,27 @@ # notify.py - example of getting notifies # -# Copyright (C) 2001-2005 Federico Di Gregorio +# Copyright (C) 2001-2010 Federico Di Gregorio # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by the -# Free Software Foundation; either version 2, or (at your option) any later -# version. +# psycopg2 is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. # -# This program is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY -# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# for more details. +# psycopg2 is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +# License for more details. ## put in DSN your DSN string DSN = 'dbname=test' -## don't modify anything below tis line (except for experimenting) +## don't modify anything below this line (except for experimenting) import sys -import psycopg2 import select +import psycopg2 +from psycopg2.extensions import ISOLATION_LEVEL_AUTOCOMMIT if len(sys.argv) > 1: DSN = sys.argv[1] @@ -29,15 +30,16 @@ conn = psycopg2.connect(DSN) print "Encoding for this connection is", conn.encoding -conn.set_isolation_level(0) +conn.set_isolation_level(ISOLATION_LEVEL_AUTOCOMMIT) curs = conn.cursor() curs.execute("listen test") print "Waiting for 'NOTIFY test'" while 1: - if select.select([curs],[],[],5)==([],[],[]): + if select.select([conn],[],[],5)==([],[],[]): print "Timeout" else: - if curs.isready(): - print "Got NOTIFY: %s" % str(curs.connection.notifies.pop()) + conn.poll() + while conn.notifies: + print "Got NOTIFY:", conn.notifies.pop() diff -Nru psycopg2-2.0.13/examples/simple.py psycopg2-2.4.5/examples/simple.py --- psycopg2-2.0.13/examples/simple.py 2009-04-26 12:09:21.000000000 +0000 +++ psycopg2-2.4.5/examples/simple.py 2011-02-06 15:58:34.000000000 +0000 @@ -1,17 +1,18 @@ # simple.py - very simple example of plain DBAPI-2.0 usage +# # currently used as test-me-stress-me script for psycopg 2.0 # -# Copyright (C) 2001-2003 Federico Di Gregorio +# Copyright (C) 2001-2010 Federico Di Gregorio # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by the -# Free Software Foundation; either version 2, or (at your option) any later -# version. +# psycopg2 is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. # -# This program is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY -# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# for more details. +# psycopg2 is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +# License for more details. ## put in DSN your DSN string diff -Nru psycopg2-2.0.13/examples/threads.py psycopg2-2.4.5/examples/threads.py --- psycopg2-2.0.13/examples/threads.py 2009-04-26 12:09:21.000000000 +0000 +++ psycopg2-2.4.5/examples/threads.py 2011-12-19 10:30:20.000000000 +0000 @@ -1,17 +1,17 @@ # threads.py -- example of multiple threads using psycopg # -*- encoding: latin1 -*- # -# Copyright (C) 2001-2004 Federico Di Gregorio +# Copyright (C) 2001-2010 Federico Di Gregorio # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by the -# Free Software Foundation; either version 2, or (at your option) any later -# version. +# psycopg2 is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. # -# This program is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY -# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# for more details. +# psycopg2 is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +# License for more details. ## put in DSN your DSN string @@ -29,15 +29,16 @@ SELECT_DIV = 250 # the available modes are: -# 0 - one connection for all insert and one for all select threads +# 0 - one connection for all inserts and one for all select threads # 1 - connections generated using the connection pool MODE = 1 -## don't modify anything below tis line (except for experimenting) +## don't modify anything below this line (except for experimenting) import sys, psycopg2, threading from psycopg2.pool import ThreadedConnectionPool +from psycopg2.extensions import ISOLATION_LEVEL_AUTOCOMMIT if len(sys.argv) > 1: DSN = sys.argv[1] @@ -89,21 +90,21 @@ conn.commit() ## a nice select function that prints the current number of rows in the -## database (and transefer them, putting some pressure on the network) +## database (and transfer them, putting some pressure on the network) def select_func(conn_or_pool, z): name = threading.currentThread().getName() if MODE == 0: conn = conn_or_pool - conn.set_isolation_level(0) + conn.set_isolation_level(ISOLATION_LEVEL_AUTOCOMMIT) for i in range(SELECT_SIZE): if divmod(i, SELECT_STEP)[1] == 0: try: if MODE == 1: conn = conn_or_pool.getconn() - conn.set_isolation_level(0) + conn.set_isolation_level(ISOLATION_LEVEL_AUTOCOMMIT) c = conn.cursor() c.execute("SELECT * FROM test_threads WHERE value2 < %s", (int(i/z),)) diff -Nru psycopg2-2.0.13/examples/typecast.py psycopg2-2.4.5/examples/typecast.py --- psycopg2-2.0.13/examples/typecast.py 2009-04-26 12:09:21.000000000 +0000 +++ psycopg2-2.4.5/examples/typecast.py 2011-02-06 15:58:34.000000000 +0000 @@ -1,16 +1,16 @@ # typecast.py - example of per-cursor and per-connection typecasters. # -# Copyright (C) 2001-2007 Federico Di Gregorio +# Copyright (C) 2001-2010 Federico Di Gregorio # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by the -# Free Software Foundation; either version 2, or (at your option) any later -# version. +# psycopg2 is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. # -# This program is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY -# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# for more details. +# psycopg2 is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +# License for more details. ## put in DSN your DSN string diff -Nru psycopg2-2.0.13/examples/tz.py psycopg2-2.4.5/examples/tz.py --- psycopg2-2.0.13/examples/tz.py 2009-04-26 12:09:21.000000000 +0000 +++ psycopg2-2.4.5/examples/tz.py 2011-02-06 15:58:34.000000000 +0000 @@ -1,17 +1,17 @@ # tz.py - example of datetime objects with time zones # -*- encoding: utf8 -*- # -# Copyright (C) 2004 Federico Di Gregorio +# Copyright (C) 2004-2010 Federico Di Gregorio # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by the -# Free Software Foundation; either version 2, or (at your option) any later -# version. +# psycopg2 is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. # -# This program is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY -# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# for more details. +# psycopg2 is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +# License for more details. ## put in DSN your DSN string diff -Nru psycopg2-2.0.13/examples/usercast.py psycopg2-2.4.5/examples/usercast.py --- psycopg2-2.0.13/examples/usercast.py 2009-08-09 12:51:14.000000000 +0000 +++ psycopg2-2.4.5/examples/usercast.py 2011-12-19 10:30:20.000000000 +0000 @@ -1,23 +1,23 @@ # usercast.py -- example of user defined typecasters # -*- encoding: latin-1 -*- # -# Copyright (C) 2001-2005 Federico Di Gregorio +# Copyright (C) 2001-2010 Federico Di Gregorio # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by the -# Free Software Foundation; either version 2, or (at your option) any later -# version. +# psycopg2 is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. # -# This program is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY -# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# for more details. +# psycopg2 is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +# License for more details. ## put in DSN your DSN string DSN = 'dbname=test' -## don't modify anything below tis line (except for experimenting) +## don't modify anything below this line (except for experimenting) import sys import psycopg2 diff -Nru psycopg2-2.0.13/INSTALL psycopg2-2.4.5/INSTALL --- psycopg2-2.0.13/INSTALL 2009-04-26 12:09:21.000000000 +0000 +++ psycopg2-2.4.5/INSTALL 2012-03-28 21:09:15.000000000 +0000 @@ -1,21 +1,22 @@ Compiling and installing psycopg ******************************** -** Important note: if you plan to use psyopg2 in a multithreaed application +** Important note: if you plan to use psycopg2 in a multithreaded application, make sure that your libpq has been compiled with the --with-thread-safety option. psycopg2 will work correctly even with a non-thread-safe libpq but libpq will leak memory. -While psycopg 1.x used autoconf for its build process psycopg 2 switched to -the more pythonic setup.py. Before building psycopg look at setup.cfg file -and change any settings to follow your system (or taste); then: +psycopg2 uses distutils for its build process, so most of the process is +executed by the setup.py script. Before building psycopg look at +setup.cfg file and change any settings to follow your system (or taste); +then: python setup.py build to build in the local directory; and: python setup.py install - + to install system-wide. @@ -34,6 +35,45 @@ include_dirs variable (and note that a working pg_config is better.) +Running the test suite +====================== + +The included Makefile allows to run all the tests included in the +distribution. Just use: + + make + make check + +The tests are run against a database called psycopg2_test on unix socket +and standard port. You can configure a different database to run the test +by setting the environment variables: + +- PSYCOPG2_TESTDB +- PSYCOPG2_TESTDB_HOST +- PSYCOPG2_TESTDB_PORT +- PSYCOPG2_TESTDB_USER + +The database should be created before running the tests. + +The standard Python unittest is used to run the tests. But if unittest2 is +found it will be used instead, with the result of having more informations +about skipped tests. + + +Building the documentation +========================== + +In order to build the documentation included in the distribution, use + + make env + make docs + +The first command will install all the dependencies (Sphinx, Docutils) in +an 'env' directory in the project tree. The second command will build both +the html format (in the 'doc/html' directory) and in plain text +(doc/psycopg2.txt) + + Using setuptools and EasyInstall ================================ @@ -56,7 +96,7 @@ You need a PostgreSQL with include and libary files installed. At least v8.0 is required. -First you need to create a libpython2X.a as described in +First you need to create a libpython2X.a as described in http://starship.python.net/crew/kernr/mingw32/Notes.html. Then run: python setup.py build_ext --compiler=mingw32 install diff -Nru psycopg2-2.0.13/lib/errorcodes.py psycopg2-2.4.5/lib/errorcodes.py --- psycopg2-2.0.13/lib/errorcodes.py 2009-04-26 12:09:21.000000000 +0000 +++ psycopg2-2.4.5/lib/errorcodes.py 2012-03-28 21:09:15.000000000 +0000 @@ -4,23 +4,49 @@ """ # psycopg2/errorcodes.py - PostgreSQL error codes # -# Copyright (C) 2006 Johan Dahlin +# Copyright (C) 2006-2010 Johan Dahlin # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by the -# Free Software Foundation; either version 2, or (at your option) any later -# version. +# psycopg2 is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. # -# This program is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY -# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# for more details. +# In addition, as a special exception, the copyright holders give +# permission to link this program with the OpenSSL library (or with +# modified versions of OpenSSL that use the same license as OpenSSL), +# and distribute linked combinations including the two. +# +# You must obey the GNU Lesser General Public License in all respects for +# all of the code used other than OpenSSL. +# +# psycopg2 is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +# License for more details. # # Based on: # -# http://www.postgresql.org/docs/8.1/static/errcodes-appendix.html +# http://www.postgresql.org/docs/current/static/errcodes-appendix.html # +def lookup(code, _cache={}): + """Lookup an error code or class code and return its symbolic name. + + Raise `KeyError` if the code is not found. + """ + if _cache: + return _cache[code] + + # Generate the lookup map at first usage. + for k, v in globals().iteritems(): + if isinstance(v, str) and len(v) in (2, 5): + _cache[v] = k + + return lookup(code) + + +# autogenerated data: do not edit below this point. + # Error classes CLASS_SUCCESSFUL_COMPLETION = '00' CLASS_WARNING = '01' @@ -33,6 +59,7 @@ CLASS_LOCATOR_EXCEPTION = '0F' CLASS_INVALID_GRANTOR = '0L' CLASS_INVALID_ROLE_SPECIFICATION = '0P' +CLASS_CASE_NOT_FOUND = '20' CLASS_CARDINALITY_VIOLATION = '21' CLASS_DATA_EXCEPTION = '22' CLASS_INTEGRITY_CONSTRAINT_VIOLATION = '23' @@ -59,20 +86,21 @@ CLASS_OPERATOR_INTERVENTION = '57' CLASS_SYSTEM_ERROR = '58' CLASS_CONFIGURATION_FILE_ERROR = 'F0' +CLASS_FOREIGN_DATA_WRAPPER_ERROR = 'HV' CLASS_PL_PGSQL_ERROR = 'P0' CLASS_INTERNAL_ERROR = 'XX' # Class 00 - Successful Completion -SUCCESSFUL_COMPLETION = '00' +SUCCESSFUL_COMPLETION = '00000' # Class 01 - Warning WARNING = '01000' -DYNAMIC_RESULT_SETS_RETURNED = '0100C' -IMPLICIT_ZERO_BIT_PADDING = '01008' NULL_VALUE_ELIMINATED_IN_SET_FUNCTION = '01003' -PRIVILEGE_NOT_GRANTED = '01007' -PRIVILEGE_NOT_REVOKED = '01006' STRING_DATA_RIGHT_TRUNCATION = '01004' +PRIVILEGE_NOT_REVOKED = '01006' +PRIVILEGE_NOT_GRANTED = '01007' +IMPLICIT_ZERO_BIT_PADDING = '01008' +DYNAMIC_RESULT_SETS_RETURNED = '0100C' DEPRECATED_FEATURE = '01P01' # Class 02 - No Data (this is also a warning class per the SQL standard) @@ -84,10 +112,10 @@ # Class 08 - Connection Exception CONNECTION_EXCEPTION = '08000' -CONNECTION_DOES_NOT_EXIST = '08003' -CONNECTION_FAILURE = '08006' SQLCLIENT_UNABLE_TO_ESTABLISH_SQLCONNECTION = '08001' +CONNECTION_DOES_NOT_EXIST = '08003' SQLSERVER_REJECTED_ESTABLISHMENT_OF_SQLCONNECTION = '08004' +CONNECTION_FAILURE = '08006' TRANSACTION_RESOLUTION_UNKNOWN = '08007' PROTOCOL_VIOLATION = '08P01' @@ -111,49 +139,61 @@ # Class 0P - Invalid Role Specification INVALID_ROLE_SPECIFICATION = '0P000' +# Class 20 - Case Not Found +CASE_NOT_FOUND = '20000' + # Class 21 - Cardinality Violation CARDINALITY_VIOLATION = '21000' # Class 22 - Data Exception DATA_EXCEPTION = '22000' -ARRAY_SUBSCRIPT_ERROR = '2202E' -CHARACTER_NOT_IN_REPERTOIRE = '22021' -DATETIME_FIELD_OVERFLOW = '22008' -DIVISION_BY_ZERO = '22012' +STRING_DATA_RIGHT_TRUNCATION = '22001' +NULL_VALUE_NO_INDICATOR_PARAMETER = '22002' +NUMERIC_VALUE_OUT_OF_RANGE = '22003' +NULL_VALUE_NOT_ALLOWED = '22004' ERROR_IN_ASSIGNMENT = '22005' +INVALID_DATETIME_FORMAT = '22007' +DATETIME_FIELD_OVERFLOW = '22008' +INVALID_TIME_ZONE_DISPLACEMENT_VALUE = '22009' ESCAPE_CHARACTER_CONFLICT = '2200B' -INDICATOR_OVERFLOW = '22022' +INVALID_USE_OF_ESCAPE_CHARACTER = '2200C' +INVALID_ESCAPE_OCTET = '2200D' +ZERO_LENGTH_CHARACTER_STRING = '2200F' +MOST_SPECIFIC_TYPE_MISMATCH = '2200G' +NOT_AN_XML_DOCUMENT = '2200L' +INVALID_XML_DOCUMENT = '2200M' +INVALID_XML_CONTENT = '2200N' +INVALID_XML_COMMENT = '2200S' +INVALID_XML_PROCESSING_INSTRUCTION = '2200T' +INVALID_INDICATOR_PARAMETER_VALUE = '22010' +SUBSTRING_ERROR = '22011' +DIVISION_BY_ZERO = '22012' +INVALID_ARGUMENT_FOR_NTILE_FUNCTION = '22014' INTERVAL_FIELD_OVERFLOW = '22015' +INVALID_ARGUMENT_FOR_NTH_VALUE_FUNCTION = '22016' +INVALID_CHARACTER_VALUE_FOR_CAST = '22018' +INVALID_ESCAPE_CHARACTER = '22019' +INVALID_REGULAR_EXPRESSION = '2201B' INVALID_ARGUMENT_FOR_LOGARITHM = '2201E' INVALID_ARGUMENT_FOR_POWER_FUNCTION = '2201F' INVALID_ARGUMENT_FOR_WIDTH_BUCKET_FUNCTION = '2201G' -INVALID_CHARACTER_VALUE_FOR_CAST = '22018' -INVALID_DATETIME_FORMAT = '22007' -INVALID_ESCAPE_CHARACTER = '22019' -INVALID_ESCAPE_OCTET = '2200D' -INVALID_ESCAPE_SEQUENCE = '22025' -NONSTANDARD_USE_OF_ESCAPE_CHARACTER = '22P06' -INVALID_INDICATOR_PARAMETER_VALUE = '22010' +INVALID_ROW_COUNT_IN_LIMIT_CLAUSE = '2201W' +INVALID_ROW_COUNT_IN_RESULT_OFFSET_CLAUSE = '2201X' INVALID_LIMIT_VALUE = '22020' +CHARACTER_NOT_IN_REPERTOIRE = '22021' +INDICATOR_OVERFLOW = '22022' INVALID_PARAMETER_VALUE = '22023' -INVALID_REGULAR_EXPRESSION = '2201B' -INVALID_TIME_ZONE_DISPLACEMENT_VALUE = '22009' -INVALID_USE_OF_ESCAPE_CHARACTER = '2200C' -MOST_SPECIFIC_TYPE_MISMATCH = '2200G' -NULL_VALUE_NOT_ALLOWED = '22004' -NULL_VALUE_NO_INDICATOR_PARAMETER = '22002' -NUMERIC_VALUE_OUT_OF_RANGE = '22003' +UNTERMINATED_C_STRING = '22024' +INVALID_ESCAPE_SEQUENCE = '22025' STRING_DATA_LENGTH_MISMATCH = '22026' -STRING_DATA_RIGHT_TRUNCATION = '22001' -SUBSTRING_ERROR = '22011' TRIM_ERROR = '22027' -UNTERMINATED_C_STRING = '22024' -ZERO_LENGTH_CHARACTER_STRING = '2200F' +ARRAY_SUBSCRIPT_ERROR = '2202E' FLOATING_POINT_EXCEPTION = '22P01' INVALID_TEXT_REPRESENTATION = '22P02' INVALID_BINARY_REPRESENTATION = '22P03' BAD_COPY_FILE_FORMAT = '22P04' UNTRANSLATABLE_CHARACTER = '22P05' +NONSTANDARD_USE_OF_ESCAPE_CHARACTER = '22P06' # Class 23 - Integrity Constraint Violation INTEGRITY_CONSTRAINT_VIOLATION = '23000' @@ -162,6 +202,7 @@ FOREIGN_KEY_VIOLATION = '23503' UNIQUE_VIOLATION = '23505' CHECK_VIOLATION = '23514' +EXCLUSION_VIOLATION = '23P01' # Class 24 - Invalid Cursor State INVALID_CURSOR_STATE = '24000' @@ -170,12 +211,12 @@ INVALID_TRANSACTION_STATE = '25000' ACTIVE_SQL_TRANSACTION = '25001' BRANCH_TRANSACTION_ALREADY_ACTIVE = '25002' -HELD_CURSOR_REQUIRES_SAME_ISOLATION_LEVEL = '25008' INAPPROPRIATE_ACCESS_MODE_FOR_BRANCH_TRANSACTION = '25003' INAPPROPRIATE_ISOLATION_LEVEL_FOR_BRANCH_TRANSACTION = '25004' NO_ACTIVE_SQL_TRANSACTION_FOR_BRANCH_TRANSACTION = '25005' READ_ONLY_SQL_TRANSACTION = '25006' SCHEMA_AND_DATA_STATEMENT_MIXING_NOT_SUPPORTED = '25007' +HELD_CURSOR_REQUIRES_SAME_ISOLATION_LEVEL = '25008' NO_ACTIVE_SQL_TRANSACTION = '25P01' IN_FAILED_SQL_TRANSACTION = '25P02' @@ -187,6 +228,7 @@ # Class 28 - Invalid Authorization Specification INVALID_AUTHORIZATION_SPECIFICATION = '28000' +INVALID_PASSWORD = '28P01' # Class 2B - Dependent Privilege Descriptors Still Exist DEPENDENT_PRIVILEGE_DESCRIPTORS_STILL_EXIST = '2B000' @@ -197,10 +239,10 @@ # Class 2F - SQL Routine Exception SQL_ROUTINE_EXCEPTION = '2F000' -FUNCTION_EXECUTED_NO_RETURN_STATEMENT = '2F005' MODIFYING_SQL_DATA_NOT_PERMITTED = '2F002' PROHIBITED_SQL_STATEMENT_ATTEMPTED = '2F003' READING_SQL_DATA_NOT_PERMITTED = '2F004' +FUNCTION_EXECUTED_NO_RETURN_STATEMENT = '2F005' # Class 34 - Invalid Cursor Name INVALID_CURSOR_NAME = '34000' @@ -231,44 +273,43 @@ # Class 40 - Transaction Rollback TRANSACTION_ROLLBACK = '40000' -TRANSACTION_INTEGRITY_CONSTRAINT_VIOLATION = '40002' SERIALIZATION_FAILURE = '40001' +TRANSACTION_INTEGRITY_CONSTRAINT_VIOLATION = '40002' STATEMENT_COMPLETION_UNKNOWN = '40003' DEADLOCK_DETECTED = '40P01' # Class 42 - Syntax Error or Access Rule Violation SYNTAX_ERROR_OR_ACCESS_RULE_VIOLATION = '42000' -SYNTAX_ERROR = '42601' INSUFFICIENT_PRIVILEGE = '42501' -CANNOT_COERCE = '42846' -GROUPING_ERROR = '42803' -INVALID_FOREIGN_KEY = '42830' +SYNTAX_ERROR = '42601' INVALID_NAME = '42602' +INVALID_COLUMN_DEFINITION = '42611' NAME_TOO_LONG = '42622' -RESERVED_NAME = '42939' +DUPLICATE_COLUMN = '42701' +AMBIGUOUS_COLUMN = '42702' +UNDEFINED_COLUMN = '42703' +UNDEFINED_OBJECT = '42704' +DUPLICATE_OBJECT = '42710' +DUPLICATE_ALIAS = '42712' +DUPLICATE_FUNCTION = '42723' +AMBIGUOUS_FUNCTION = '42725' +GROUPING_ERROR = '42803' DATATYPE_MISMATCH = '42804' -INDETERMINATE_DATATYPE = '42P18' WRONG_OBJECT_TYPE = '42809' -UNDEFINED_COLUMN = '42703' +INVALID_FOREIGN_KEY = '42830' +CANNOT_COERCE = '42846' UNDEFINED_FUNCTION = '42883' +RESERVED_NAME = '42939' UNDEFINED_TABLE = '42P01' UNDEFINED_PARAMETER = '42P02' -UNDEFINED_OBJECT = '42704' -DUPLICATE_COLUMN = '42701' DUPLICATE_CURSOR = '42P03' DUPLICATE_DATABASE = '42P04' -DUPLICATE_FUNCTION = '42723' DUPLICATE_PREPARED_STATEMENT = '42P05' DUPLICATE_SCHEMA = '42P06' DUPLICATE_TABLE = '42P07' -DUPLICATE_ALIAS = '42712' -DUPLICATE_OBJECT = '42710' -AMBIGUOUS_COLUMN = '42702' -AMBIGUOUS_FUNCTION = '42725' AMBIGUOUS_PARAMETER = '42P08' AMBIGUOUS_ALIAS = '42P09' INVALID_COLUMN_REFERENCE = '42P10' -INVALID_COLUMN_DEFINITION = '42611' INVALID_CURSOR_DEFINITION = '42P11' INVALID_DATABASE_DEFINITION = '42P12' INVALID_FUNCTION_DEFINITION = '42P13' @@ -276,6 +317,11 @@ INVALID_SCHEMA_DEFINITION = '42P15' INVALID_TABLE_DEFINITION = '42P16' INVALID_OBJECT_DEFINITION = '42P17' +INDETERMINATE_DATATYPE = '42P18' +INVALID_RECURSION = '42P19' +WINDOWING_ERROR = '42P20' +COLLATION_MISMATCH = '42P21' +INDETERMINATE_COLLATION = '42P22' # Class 44 - WITH CHECK OPTION Violation WITH_CHECK_OPTION_VIOLATION = '44000' @@ -304,6 +350,7 @@ ADMIN_SHUTDOWN = '57P01' CRASH_SHUTDOWN = '57P02' CANNOT_CONNECT_NOW = '57P03' +DATABASE_DROPPED = '57P04' # Class 58 - System Error (errors external to PostgreSQL itself) IO_ERROR = '58030' @@ -314,9 +361,40 @@ CONFIG_FILE_ERROR = 'F0000' LOCK_FILE_EXISTS = 'F0001' +# Class HV - Foreign Data Wrapper Error (SQL/MED) +FDW_ERROR = 'HV000' +FDW_OUT_OF_MEMORY = 'HV001' +FDW_DYNAMIC_PARAMETER_VALUE_NEEDED = 'HV002' +FDW_INVALID_DATA_TYPE = 'HV004' +FDW_COLUMN_NAME_NOT_FOUND = 'HV005' +FDW_INVALID_DATA_TYPE_DESCRIPTORS = 'HV006' +FDW_INVALID_COLUMN_NAME = 'HV007' +FDW_INVALID_COLUMN_NUMBER = 'HV008' +FDW_INVALID_USE_OF_NULL_POINTER = 'HV009' +FDW_INVALID_STRING_FORMAT = 'HV00A' +FDW_INVALID_HANDLE = 'HV00B' +FDW_INVALID_OPTION_INDEX = 'HV00C' +FDW_INVALID_OPTION_NAME = 'HV00D' +FDW_OPTION_NAME_NOT_FOUND = 'HV00J' +FDW_REPLY_HANDLE = 'HV00K' +FDW_UNABLE_TO_CREATE_EXECUTION = 'HV00L' +FDW_UNABLE_TO_CREATE_REPLY = 'HV00M' +FDW_UNABLE_TO_ESTABLISH_CONNECTION = 'HV00N' +FDW_NO_SCHEMAS = 'HV00P' +FDW_SCHEMA_NOT_FOUND = 'HV00Q' +FDW_TABLE_NOT_FOUND = 'HV00R' +FDW_FUNCTION_SEQUENCE_ERROR = 'HV010' +FDW_TOO_MANY_HANDLES = 'HV014' +FDW_INCONSISTENT_DESCRIPTOR_INFORMATION = 'HV021' +FDW_INVALID_ATTRIBUTE_VALUE = 'HV024' +FDW_INVALID_STRING_LENGTH_OR_BUFFER_LENGTH = 'HV090' +FDW_INVALID_DESCRIPTOR_FIELD_IDENTIFIER = 'HV091' + # Class P0 - PL/pgSQL Error PLPGSQL_ERROR = 'P0000' RAISE_EXCEPTION = 'P0001' +NO_DATA_FOUND = 'P0002' +TOO_MANY_ROWS = 'P0003' # Class XX - Internal Error INTERNAL_ERROR = 'XX000' diff -Nru psycopg2-2.0.13/lib/extensions.py psycopg2-2.4.5/lib/extensions.py --- psycopg2-2.0.13/lib/extensions.py 2009-10-04 21:32:35.000000000 +0000 +++ psycopg2-2.4.5/lib/extensions.py 2011-12-19 10:30:20.000000000 +0000 @@ -12,62 +12,85 @@ """ # psycopg/extensions.py - DBAPI-2.0 extensions specific to psycopg # -# Copyright (C) 2003-2004 Federico Di Gregorio +# Copyright (C) 2003-2010 Federico Di Gregorio # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by the -# Free Software Foundation; either version 2, or (at your option) any later -# version. +# psycopg2 is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. # -# This program is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY -# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# for more details. - -from _psycopg import UNICODE, INTEGER, LONGINTEGER, BOOLEAN, FLOAT -from _psycopg import TIME, DATE, INTERVAL -from _psycopg import BINARYARRAY, BOOLEANARRAY, DATEARRAY, DATETIMEARRAY -from _psycopg import DECIMALARRAY, FLOATARRAY, INTEGERARRAY, INTERVALARRAY -from _psycopg import LONGINTEGERARRAY, ROWIDARRAY, STRINGARRAY, TIMEARRAY -from _psycopg import UNICODEARRAY +# In addition, as a special exception, the copyright holders give +# permission to link this program with the OpenSSL library (or with +# modified versions of OpenSSL that use the same license as OpenSSL), +# and distribute linked combinations including the two. +# +# You must obey the GNU Lesser General Public License in all respects for +# all of the code used other than OpenSSL. +# +# psycopg2 is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +# License for more details. + +from psycopg2._psycopg import UNICODE, INTEGER, LONGINTEGER, BOOLEAN, FLOAT +from psycopg2._psycopg import TIME, DATE, INTERVAL, DECIMAL +from psycopg2._psycopg import BINARYARRAY, BOOLEANARRAY, DATEARRAY, DATETIMEARRAY +from psycopg2._psycopg import DECIMALARRAY, FLOATARRAY, INTEGERARRAY, INTERVALARRAY +from psycopg2._psycopg import LONGINTEGERARRAY, ROWIDARRAY, STRINGARRAY, TIMEARRAY +from psycopg2._psycopg import UNICODEARRAY -from _psycopg import Boolean, Float, QuotedString, AsIs +from psycopg2._psycopg import Binary, Boolean, Int, Float, QuotedString, AsIs try: - from _psycopg import DateFromMx, TimeFromMx, TimestampFromMx - from _psycopg import IntervalFromMx -except: + from psycopg2._psycopg import MXDATE, MXDATETIME, MXINTERVAL, MXTIME + from psycopg2._psycopg import MXDATEARRAY, MXDATETIMEARRAY, MXINTERVALARRAY, MXTIMEARRAY + from psycopg2._psycopg import DateFromMx, TimeFromMx, TimestampFromMx + from psycopg2._psycopg import IntervalFromMx +except ImportError: pass + try: - from _psycopg import DateFromPy, TimeFromPy, TimestampFromPy - from _psycopg import IntervalFromPy -except: + from psycopg2._psycopg import PYDATE, PYDATETIME, PYINTERVAL, PYTIME + from psycopg2._psycopg import PYDATEARRAY, PYDATETIMEARRAY, PYINTERVALARRAY, PYTIMEARRAY + from psycopg2._psycopg import DateFromPy, TimeFromPy, TimestampFromPy + from psycopg2._psycopg import IntervalFromPy +except ImportError: pass -from _psycopg import adapt, adapters, encodings, connection, cursor, lobject -from _psycopg import string_types, binary_types, new_type, register_type -from _psycopg import ISQLQuote +from psycopg2._psycopg import adapt, adapters, encodings, connection, cursor, lobject, Xid +from psycopg2._psycopg import string_types, binary_types, new_type, new_array_type, register_type +from psycopg2._psycopg import ISQLQuote, Notify -from _psycopg import QueryCanceledError, TransactionRollbackError +from psycopg2._psycopg import QueryCanceledError, TransactionRollbackError + +try: + from psycopg2._psycopg import set_wait_callback, get_wait_callback +except ImportError: + pass """Isolation level values.""" -ISOLATION_LEVEL_AUTOCOMMIT = 0 -ISOLATION_LEVEL_READ_COMMITTED = 1 -ISOLATION_LEVEL_SERIALIZABLE = 2 - -# PostgreSQL maps the the other standard values to already defined levels -ISOLATION_LEVEL_REPEATABLE_READ = ISOLATION_LEVEL_SERIALIZABLE -ISOLATION_LEVEL_READ_UNCOMMITTED = ISOLATION_LEVEL_READ_COMMITTED +ISOLATION_LEVEL_AUTOCOMMIT = 0 +ISOLATION_LEVEL_READ_UNCOMMITTED = 4 +ISOLATION_LEVEL_READ_COMMITTED = 1 +ISOLATION_LEVEL_REPEATABLE_READ = 2 +ISOLATION_LEVEL_SERIALIZABLE = 3 """psycopg connection status values.""" STATUS_SETUP = 0 STATUS_READY = 1 STATUS_BEGIN = 2 -STATUS_SYNC = 3 -STATUS_ASYNC = 4 +STATUS_SYNC = 3 # currently unused +STATUS_ASYNC = 4 # currently unused +STATUS_PREPARED = 5 # This is a usefull mnemonic to check if the connection is in a transaction STATUS_IN_TRANSACTION = STATUS_BEGIN +"""psycopg asynchronous connection polling values""" +POLL_OK = 0 +POLL_READ = 1 +POLL_WRITE = 2 +POLL_ERROR = 3 + """Backend transaction status values.""" TRANSACTION_STATUS_IDLE = 0 TRANSACTION_STATUS_ACTIVE = 1 @@ -75,6 +98,16 @@ TRANSACTION_STATUS_INERROR = 3 TRANSACTION_STATUS_UNKNOWN = 4 +import sys as _sys + +# Return bytes from a string +if _sys.version_info[0] < 3: + def b(s): + return s +else: + def b(s): + return s.encode('utf8') + def register_adapter(typ, callable): """Register 'callable' as an ISQLQuote adapter for type 'typ'.""" adapters[(typ, ISQLQuote)] = callable @@ -97,12 +130,34 @@ for obj in pobjs: if hasattr(obj, 'prepare'): obj.prepare(self._conn) - qobjs = [str(o.getquoted()) for o in pobjs] - return '(' + ', '.join(qobjs) + ')' + qobjs = [o.getquoted() for o in pobjs] + return b('(') + b(', ').join(qobjs) + b(')') + + def __str__(self): + return str(self.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, _null=b("NULL")): + return _null - __str__ = getquoted -register_adapter(tuple, SQL_IN) +# 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 +# encodings keys and would raise an exception with the unicode typecaster +for k, v in encodings.items(): + k = k.replace('_', '').replace('-', '').upper() + encodings[k] = v +del k, v -__all__ = [ k for k in locals().keys() if not k.startswith('_') ] +__all__ = filter(lambda k: not k.startswith('_'), locals().keys()) diff -Nru psycopg2-2.0.13/lib/extras.py psycopg2-2.4.5/lib/extras.py --- psycopg2-2.0.13/lib/extras.py 2009-10-04 21:32:35.000000000 +0000 +++ psycopg2-2.4.5/lib/extras.py 2012-03-28 21:09:15.000000000 +0000 @@ -5,20 +5,30 @@ """ # psycopg/extras.py - miscellaneous extra goodies for psycopg # -# Copyright (C) 2003-2004 Federico Di Gregorio +# Copyright (C) 2003-2010 Federico Di Gregorio # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by the -# Free Software Foundation; either version 2, or (at your option) any later -# version. +# psycopg2 is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. # -# This program is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY -# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# for more details. +# In addition, as a special exception, the copyright holders give +# permission to link this program with the OpenSSL library (or with +# modified versions of OpenSSL that use the same license as OpenSSL), +# and distribute linked combinations including the two. +# +# You must obey the GNU Lesser General Public License in all respects for +# all of the code used other than OpenSSL. +# +# psycopg2 is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +# License for more details. import os +import sys import time +import warnings import re as regex try: @@ -26,18 +36,19 @@ except: logging = None -from psycopg2 import DATETIME, DataError +import psycopg2 from psycopg2 import extensions as _ext from psycopg2.extensions import cursor as _cursor from psycopg2.extensions import connection as _connection from psycopg2.extensions import adapt as _A +from psycopg2.extensions import b class DictCursorBase(_cursor): """Base class for all dict-like cursors.""" def __init__(self, *args, **kwargs): - if kwargs.has_key('row_factory'): + if 'row_factory' in kwargs: row_factory = kwargs['row_factory'] del kwargs['row_factory'] else: @@ -75,21 +86,23 @@ res = _cursor.fetchall(self) return res - def next(self): + def __iter__(self): if self._prefetch: - res = _cursor.fetchone(self) - if res is None: - raise StopIteration() + res = _cursor.__iter__(self) + first = res.next() if self._query_executed: self._build_index() if not self._prefetch: - res = _cursor.fetchone(self) - if res is None: - raise StopIteration() - return res + res = _cursor.__iter__(self) + first = res.next() + + yield first + while 1: + yield res.next() + class DictConnection(_connection): - """A connection that uses DictCursor automatically.""" + """A connection that uses `DictCursor` automatically.""" def cursor(self, name=None): if name is None: return _connection.cursor(self, cursor_factory=DictCursor) @@ -104,10 +117,10 @@ DictCursorBase.__init__(self, *args, **kwargs) self._prefetch = 1 - def execute(self, query, vars=None, async=0): + def execute(self, query, vars=None): self.index = {} self._query_executed = 1 - return _cursor.execute(self, query, vars, async) + return _cursor.execute(self, query, vars) def callproc(self, procname, vars=None): self.index = {} @@ -121,7 +134,7 @@ self._query_executed = 0 class DictRow(list): - """A row object that allow by-colun-name access to data.""" + """A row object that allow by-colmun-name access to data.""" __slots__ = ('_index',) @@ -130,15 +143,17 @@ self[:] = [None] * len(cursor.description) def __getitem__(self, x): - if type(x) != int: + if not isinstance(x, (int, slice)): x = self._index[x] return list.__getitem__(self, x) + def __setitem__(self, x, v): + if not isinstance(x, (int, slice)): + x = self._index[x] + list.__setitem__(self, x, v) + def items(self): - res = [] - for n, v in self._index.items(): - res.append((n, list.__getitem__(self, v))) - return res + return list(self.iteritems()) def keys(self): return self._index.keys() @@ -147,7 +162,7 @@ return tuple(self[:]) def has_key(self, x): - return self._index.has_key(x) + return x in self._index def get(self, x, default=None): try: @@ -156,7 +171,7 @@ return default def iteritems(self): - for n, v in self._index.items(): + for n, v in self._index.iteritems(): yield n, list.__getitem__(self, v) def iterkeys(self): @@ -166,13 +181,21 @@ return list.__iter__(self) def copy(self): - return dict(self.items()) + return dict(self.iteritems()) def __contains__(self, x): - return self._index.__contains__(x) + return x in self._index + + # grop the crusty Py2 methods + if sys.version_info[0] > 2: + items = iteritems; del iteritems + keys = iterkeys; del iterkeys + values = itervalues; del itervalues + del has_key + class RealDictConnection(_connection): - """A connection that uses RealDictCursor automatically.""" + """A connection that uses `RealDictCursor` automatically.""" def cursor(self, name=None): if name is None: return _connection.cursor(self, cursor_factory=RealDictCursor) @@ -185,7 +208,7 @@ Note that this cursor is extremely specialized and does not allow the normal access (using integer indices) to fetched data. If you need to access database rows both as a dictionary and a list, then use - the generic DictCursor instead of RealDictCursor. + the generic `DictCursor` instead of `!RealDictCursor`. """ def __init__(self, *args, **kwargs): @@ -193,10 +216,10 @@ DictCursorBase.__init__(self, *args, **kwargs) self._prefetch = 0 - def execute(self, query, vars=None, async=0): + def execute(self, query, vars=None): self.column_mapping = [] self._query_executed = 1 - return _cursor.execute(self, query, vars, async) + return _cursor.execute(self, query, vars) def callproc(self, procname, vars=None): self.column_mapping = [] @@ -210,11 +233,16 @@ self._query_executed = 0 class RealDictRow(dict): + """A `!dict` subclass representing a data record.""" __slots__ = ('_column_mapping') def __init__(self, cursor): dict.__init__(self) + # Required for named cursors + if cursor.description and not cursor.column_mapping: + cursor._build_index() + self._column_mapping = cursor.column_mapping def __setitem__(self, name, value): @@ -223,14 +251,98 @@ return dict.__setitem__(self, name, value) +class NamedTupleConnection(_connection): + """A connection that uses `NamedTupleCursor` automatically.""" + def cursor(self, *args, **kwargs): + kwargs['cursor_factory'] = NamedTupleCursor + return _connection.cursor(self, *args, **kwargs) + +class NamedTupleCursor(_cursor): + """A cursor that generates results as `~collections.namedtuple`. + + `!fetch*()` methods will return named tuples instead of regular tuples, so + their elements can be accessed both as regular numeric items as well as + attributes. + + >>> nt_cur = conn.cursor(cursor_factory=psycopg2.extras.NamedTupleCursor) + >>> rec = nt_cur.fetchone() + >>> rec + Record(id=1, num=100, data="abc'def") + >>> rec[1] + 100 + >>> rec.data + "abc'def" + """ + Record = None + + def execute(self, query, vars=None): + self.Record = None + return _cursor.execute(self, query, vars) + + def executemany(self, query, vars): + self.Record = None + return _cursor.executemany(self, query, vars) + + def callproc(self, procname, vars=None): + self.Record = None + return _cursor.callproc(self, procname, vars) + + def fetchone(self): + t = _cursor.fetchone(self) + if t is not None: + nt = self.Record + if nt is None: + nt = self.Record = self._make_nt() + return nt(*t) + + def fetchmany(self, size=None): + ts = _cursor.fetchmany(self, size) + nt = self.Record + if nt is None: + nt = self.Record = self._make_nt() + return [nt(*t) for t in ts] + + def fetchall(self): + ts = _cursor.fetchall(self) + nt = self.Record + if nt is None: + nt = self.Record = self._make_nt() + return [nt(*t) for t in ts] + + def __iter__(self): + it = _cursor.__iter__(self) + t = it.next() + + nt = self.Record + if nt is None: + nt = self.Record = self._make_nt() + + yield nt(*t) + + while 1: + yield nt(*it.next()) + + try: + from collections import namedtuple + except ImportError, _exc: + def _make_nt(self): + raise self._exc + else: + def _make_nt(self, namedtuple=namedtuple): + return namedtuple("Record", [d[0] for d in self.description or ()]) + + class LoggingConnection(_connection): - """A connection that logs all queries to a file or logger object.""" + """A connection that logs all queries to a file or logger__ object. + + .. __: http://docs.python.org/library/logging.html + """ def initialize(self, logobj): - """Initialize the connection to log to `logobj`. + """Initialize the connection to log to `!logobj`. - The `logobj` parameter can be an open file object or a Logger instance - from the standard logging module. + The `!logobj` parameter can be an open file object or a Logger + instance from the standard logging module. """ self._logobj = logobj if logging and isinstance(logobj, logging.Logger): @@ -270,9 +382,9 @@ class LoggingCursor(_cursor): """A cursor that logs queries using its connection logging facilities.""" - def execute(self, query, vars=None, async=0): + def execute(self, query, vars=None): try: - return _cursor.execute(self, query, vars, async) + return _cursor.execute(self, query, vars) finally: self.connection.log(self.query, self) @@ -286,12 +398,13 @@ class MinTimeLoggingConnection(LoggingConnection): """A connection that logs queries based on execution time. - This is just an example of how to sub-class LoggingConnection to provide - some extra filtering for the logged queries. Both the `.inizialize()` and - `.filter()` methods are overwritten to make sure that only queries - executing for more than `mintime` ms are logged. + This is just an example of how to sub-class `LoggingConnection` to + provide some extra filtering for the logged queries. Both the + `inizialize()` and `filter()` methods are overwritten to make sure + that only queries executing for more than ``mintime`` ms are logged. - Note that this connection uses the specialized cursor MinTimeLoggingCursor. + Note that this connection uses the specialized cursor + `MinTimeLoggingCursor`. """ def initialize(self, logobj, mintime=0): LoggingConnection.initialize(self, logobj) @@ -310,11 +423,11 @@ return _connection.cursor(self, name, cursor_factory=MinTimeLoggingCursor) class MinTimeLoggingCursor(LoggingCursor): - """The cursor sub-class companion to MinTimeLoggingConnection.""" + """The cursor sub-class companion to `MinTimeLoggingConnection`.""" - def execute(self, query, vars=None, async=0): + def execute(self, query, vars=None): self.timestamp = time.time() - return LoggingCursor.execute(self, query, vars, async) + return LoggingCursor.execute(self, query, vars) def callproc(self, procname, vars=None): self.timestamp = time.time() @@ -323,61 +436,54 @@ # a dbtype and adapter for Python UUID type -try: - import uuid +class UUID_adapter(object): + """Adapt Python's uuid.UUID__ type to PostgreSQL's uuid__. - class UUID_adapter(object): - """Adapt Python's uuid.UUID type to PostgreSQL's uuid.""" - - def __init__(self, uuid): - self._uuid = uuid - - def prepare(self, conn): - pass - - def getquoted(self): - return "'"+str(self._uuid)+"'::uuid" - - __str__ = getquoted + .. __: http://docs.python.org/library/uuid.html + .. __: http://www.postgresql.org/docs/current/static/datatype-uuid.html + """ - def register_uuid(oids=None, conn_or_curs=None): - """Create the UUID type and an uuid.UUID adapter.""" - if not oids: - oid1 = 2950 - oid2 = 2951 - elif type(oids) == list: - oid1, oid2 = oids - else: - oid1 = oids - oid2 = 2951 - - def parseUUIDARRAY(data, cursor): - if data is None: - return None - elif data == '{}': - return [] - else: - return [((len(x) > 0 and x != 'NULL') and uuid.UUID(x) or None) - for x in data[1:-1].split(',')] + def __init__(self, uuid): + self._uuid = uuid - _ext.UUID = _ext.new_type((oid1, ), "UUID", - lambda data, cursor: data and uuid.UUID(data) or None) - _ext.UUIDARRAY = _ext.new_type((oid2,), "UUID[]", parseUUIDARRAY) + def prepare(self, conn): + pass - _ext.register_type(_ext.UUID, conn_or_curs) - _ext.register_type(_ext.UUIDARRAY, conn_or_curs) - _ext.register_adapter(uuid.UUID, UUID_adapter) + def getquoted(self): + return "'"+str(self._uuid)+"'::uuid" - return _ext.UUID + __str__ = getquoted -except ImportError, e: - def register_uuid(oid=None): - """Create the UUID type and an uuid.UUID adapter. +def register_uuid(oids=None, conn_or_curs=None): + """Create the UUID type and an uuid.UUID adapter. - This is a fake function that will always raise an error because the - import of the uuid module failed. - """ - raise e + :param oids: oid for the PostgreSQL :sql:`uuid` type, or 2-items sequence + with oids of the type and the array. If not specified, use PostgreSQL + standard oids. + :param conn_or_curs: where to register the typecaster. If not specified, + register it globally. + """ + + import uuid + + if not oids: + oid1 = 2950 + oid2 = 2951 + elif isinstance(oids, (list, tuple)): + oid1, oid2 = oids + else: + oid1 = oids + oid2 = 2951 + + _ext.UUID = _ext.new_type((oid1, ), "UUID", + lambda data, cursor: data and uuid.UUID(data) or None) + _ext.UUIDARRAY = _ext.new_array_type((oid2,), "UUID[]", _ext.UUID) + + _ext.register_type(_ext.UUID, conn_or_curs) + _ext.register_type(_ext.UUIDARRAY, conn_or_curs) + _ext.register_adapter(uuid.UUID, UUID_adapter) + + return _ext.UUID # a type, dbtype and adapter for PostgreSQL inet type @@ -391,76 +497,481 @@ by passing an evil value to the initializer. """ def __init__(self, addr): - self.addr - + self.addr = addr + + def __repr__(self): + return "%s(%r)" % (self.__class__.__name__, self.addr) + def prepare(self, conn): self._conn = conn - + def getquoted(self): - obj = adapt(self.addr) + obj = _A(self.addr) if hasattr(obj, 'prepare'): obj.prepare(self._conn) - return obj.getquoted()+"::inet" + return obj.getquoted() + b("::inet") + + def __conform__(self, foo): + if foo is _ext.ISQLQuote: + return self def __str__(self): return str(self.addr) - + def register_inet(oid=None, conn_or_curs=None): - """Create the INET type and an Inet adapter.""" - if not oid: oid = 869 - _ext.INET = _ext.new_type((oid, ), "INET", + """Create the INET type and an Inet adapter. + + :param oid: oid for the PostgreSQL :sql:`inet` type, or 2-items sequence + with oids of the type and the array. If not specified, use PostgreSQL + standard oids. + :param conn_or_curs: where to register the typecaster. If not specified, + register it globally. + """ + if not oid: + oid1 = 869 + oid2 = 1041 + elif isinstance(oid, (list, tuple)): + oid1, oid2 = oid + else: + oid1 = oid + oid2 = 1041 + + _ext.INET = _ext.new_type((oid1, ), "INET", lambda data, cursor: data and Inet(data) or None) + _ext.INETARRAY = _ext.new_array_type((oid2, ), "INETARRAY", _ext.INET) + _ext.register_type(_ext.INET, conn_or_curs) + _ext.register_type(_ext.INETARRAY, conn_or_curs) + return _ext.INET -# safe management of times with a non-standard time zone +def register_tstz_w_secs(oids=None, conn_or_curs=None): + """The function used to register an alternate type caster for + :sql:`TIMESTAMP WITH TIME ZONE` to deal with historical time zones with + seconds in the UTC offset. -def _convert_tstz_w_secs(s, cursor): - try: - return DATETIME(s, cursor) - - except (DataError,), exc: - if exc.message != "unable to parse time": - raise - - if regex.match('(\+|-)\d\d:\d\d:\d\d', s[-9:]) is None: - raise - - # parsing doesn't succeed even if seconds are ":00" so truncate in - # any case - return DATETIME(s[:-3], cursor) + These are now correctly handled by the default type caster, so currently + the function doesn't do anything. + """ + warnings.warn("deprecated", DeprecationWarning) -def register_tstz_w_secs(oids=None, conn_or_curs=None): - """Register alternate type caster for TIMESTAMP WITH TIME ZONE. - The Python datetime module cannot handle time zones with - seconds in the UTC offset. There are, however, historical - "time zones" which contain such offsets, eg. "Asia/Calcutta". - In many cases those offsets represent true local time. - - If you encounter "unable to parse time" on a perfectly valid - timestamp you likely want to try this type caster. It truncates - the seconds from the time zone data and retries casting - the timestamp. Note that this will generate timestamps - which are INACCURATE by the number of seconds truncated - (unless the seconds were 00). - - - which OIDs to use this type caster for, - defaults to TIMESTAMP WITH TIME ZONE - - a cursor or connection if you want to attach - this type caster to that only, defaults to - None meaning all connections and cursors - """ - if oids is None: - oids = (1184,) # hardcoded from PostgreSQL headers +import select +from psycopg2.extensions import POLL_OK, POLL_READ, POLL_WRITE +from psycopg2 import OperationalError + +def wait_select(conn): + """Wait until a connection or cursor has data available. + + The function is an example of a wait callback to be registered with + `~psycopg2.extensions.set_wait_callback()`. This function uses `!select()` + to wait for data available. + """ + while 1: + state = conn.poll() + if state == POLL_OK: + break + elif state == POLL_READ: + select.select([conn.fileno()], [], []) + elif state == POLL_WRITE: + select.select([], [conn.fileno()], []) + else: + raise OperationalError("bad state from poll: %s" % state) + + +class HstoreAdapter(object): + """Adapt a Python dict to the hstore syntax.""" + def __init__(self, wrapped): + self.wrapped = wrapped + + def prepare(self, conn): + self.conn = conn + + # use an old-style getquoted implementation if required + if conn.server_version < 90000: + self.getquoted = self._getquoted_8 + + def _getquoted_8(self): + """Use the operators available in PG pre-9.0.""" + if not self.wrapped: + return b("''::hstore") + + adapt = _ext.adapt + rv = [] + for k, v in self.wrapped.iteritems(): + k = adapt(k) + k.prepare(self.conn) + k = k.getquoted() + + if v is not None: + v = adapt(v) + v.prepare(self.conn) + v = v.getquoted() + else: + v = b('NULL') + + # XXX this b'ing is painfully inefficient! + rv.append(b("(") + k + b(" => ") + v + b(")")) + + return b("(") + b('||').join(rv) + b(")") + + def _getquoted_9(self): + """Use the hstore(text[], text[]) function.""" + if not self.wrapped: + return b("''::hstore") + + k = _ext.adapt(self.wrapped.keys()) + k.prepare(self.conn) + v = _ext.adapt(self.wrapped.values()) + v.prepare(self.conn) + return b("hstore(") + k.getquoted() + b(", ") + v.getquoted() + b(")") + + getquoted = _getquoted_9 + + _re_hstore = regex.compile(r""" + # hstore key: + # a string of normal or escaped chars + "((?: [^"\\] | \\. )*)" + \s*=>\s* # hstore value + (?: + NULL # the value can be null - not catched + # or a quoted string like the key + | "((?: [^"\\] | \\. )*)" + ) + (?:\s*,\s*|$) # pairs separated by comma or end of string. + """, regex.VERBOSE) + + @classmethod + def parse(self, s, cur, _bsdec=regex.compile(r"\\(.)")): + """Parse an hstore representation in a Python string. + + The hstore is represented as something like:: + + "a"=>"1", "b"=>"2" + + with backslash-escaped strings. + """ + if s is None: + return None + + rv = {} + start = 0 + for m in self._re_hstore.finditer(s): + if m is None or m.start() != start: + raise psycopg2.InterfaceError( + "error parsing hstore pair at char %d" % start) + k = _bsdec.sub(r'\1', m.group(1)) + v = m.group(2) + if v is not None: + v = _bsdec.sub(r'\1', v) + + rv[k] = v + start = m.end() + + if start < len(s): + raise psycopg2.InterfaceError( + "error parsing hstore: unparsed data after char %d" % start) + + return rv + + @classmethod + def parse_unicode(self, s, cur): + """Parse an hstore returning unicode keys and values.""" + if s is None: + return None + + s = s.decode(_ext.encodings[cur.connection.encoding]) + return self.parse(s, cur) + + @classmethod + def get_oids(self, conn_or_curs): + """Return the lists of OID of the hstore and hstore[] types. + """ + if hasattr(conn_or_curs, 'execute'): + conn = conn_or_curs.connection + curs = conn_or_curs + else: + conn = conn_or_curs + curs = conn_or_curs.cursor() + + # Store the transaction status of the connection to revert it after use + conn_status = conn.status + + # column typarray not available before PG 8.3 + typarray = conn.server_version >= 80300 and "typarray" or "NULL" + + rv0, rv1 = [], [] + + # get the oid for the hstore + curs.execute("""\ +SELECT t.oid, %s +FROM pg_type t JOIN pg_namespace ns + ON typnamespace = ns.oid +WHERE typname = 'hstore'; +""" % typarray) + for oids in curs: + rv0.append(oids[0]) + rv1.append(oids[1]) + + # revert the status of the connection as before the command + if (conn_status != _ext.STATUS_IN_TRANSACTION + and not conn.autocommit): + conn.rollback() + + return tuple(rv0), tuple(rv1) + +def register_hstore(conn_or_curs, globally=False, unicode=False, + oid=None, array_oid=None): + """Register adapter and typecaster for `!dict`\-\ |hstore| conversions. + + :param conn_or_curs: a connection or cursor: the typecaster will be + registered only on this object unless *globally* is set to `!True` + :param globally: register the adapter globally, not only on *conn_or_curs* + :param unicode: if `!True`, keys and values returned from the database + will be `!unicode` instead of `!str`. The option is not available on + Python 3 + :param oid: the OID of the |hstore| type if known. If not, it will be + queried on *conn_or_curs*. + :param array_oid: the OID of the |hstore| array type if known. If not, it + will be queried on *conn_or_curs*. + + The connection or cursor passed to the function will be used to query the + database and look for the OID of the |hstore| type (which may be different + across databases). If querying is not desirable (e.g. with + :ref:`asynchronous connections `) you may specify it in the + *oid* parameter, which can be found using a query such as :sql:`SELECT + 'hstore'::regtype::oid`. Analogously you can obtain a value for *array_oid* + using a query such as :sql:`SELECT 'hstore[]'::regtype::oid`. + + + Note that, when passing a dictionary from Python to the database, both + strings and unicode keys and values are supported. Dictionaries returned + from the database have keys/values according to the *unicode* parameter. + + The |hstore| contrib module must be already installed in the database + (executing the ``hstore.sql`` script in your ``contrib`` directory). + Raise `~psycopg2.ProgrammingError` if the type is not found. + + .. versionchanged:: 2.4 + added the *oid* parameter. If not specified, the typecaster is + installed also if |hstore| is not installed in the :sql:`public` + schema. + + .. versionchanged:: 2.4.3 + added support for |hstore| array. + + """ + if oid is None: + oid = HstoreAdapter.get_oids(conn_or_curs) + if oid is None or not oid[0]: + raise psycopg2.ProgrammingError( + "hstore type not found in the database. " + "please install it from your 'contrib/hstore.sql' file") + else: + array_oid = oid[1] + oid = oid[0] + + if isinstance(oid, int): + oid = (oid,) + + if array_oid is not None: + if isinstance(array_oid, int): + array_oid = (array_oid,) + else: + array_oid = tuple([x for x in array_oid if x]) + + # create and register the typecaster + if sys.version_info[0] < 3 and unicode: + cast = HstoreAdapter.parse_unicode + else: + cast = HstoreAdapter.parse + + HSTORE = _ext.new_type(oid, "HSTORE", cast) + _ext.register_type(HSTORE, not globally and conn_or_curs or None) + _ext.register_adapter(dict, HstoreAdapter) + + if array_oid: + HSTOREARRAY = _ext.new_array_type(array_oid, "HSTOREARRAY", HSTORE) + _ext.register_type(HSTOREARRAY, not globally and conn_or_curs or None) + + +class CompositeCaster(object): + """Helps conversion of a PostgreSQL composite type into a Python object. + + The class is usually created by the `register_composite()` function. + You may want to create and register manually instances of the class if + querying the database at registration time is not desirable (such as when + using an :ref:`asynchronous connections `). + + .. attribute:: name + + The name of the PostgreSQL type. + + .. attribute:: oid + + The oid of the PostgreSQL type. + + .. attribute:: array_oid + + The oid of the PostgreSQL array type, if available. + + .. attribute:: type + + The type of the Python objects returned. If :py:func:`collections.namedtuple()` + is available, it is a named tuple with attributes equal to the type + components. Otherwise it is just the `!tuple` object. + + .. attribute:: attnames + + List of component names of the type to be casted. + + .. attribute:: atttypes + + List of component type oids of the type to be casted. + + """ + def __init__(self, name, oid, attrs, array_oid=None): + self.name = name + self.oid = oid + self.array_oid = array_oid + + self.attnames = [ a[0] for a in attrs ] + self.atttypes = [ a[1] for a in attrs ] + self._create_type(name, self.attnames) + self.typecaster = _ext.new_type((oid,), name, self.parse) + if array_oid: + self.array_typecaster = _ext.new_array_type( + (array_oid,), "%sARRAY" % name, self.typecaster) + else: + self.array_typecaster = None + + def parse(self, s, curs): + if s is None: + return None + + tokens = self.tokenize(s) + if len(tokens) != len(self.atttypes): + raise psycopg2.DataError( + "expecting %d components for the type %s, %d found instead" % + (len(self.atttypes), self.name, len(tokens))) + + attrs = [ curs.cast(oid, token) + for oid, token in zip(self.atttypes, tokens) ] + return self._ctor(*attrs) + + _re_tokenize = regex.compile(r""" + \(? ([,)]) # an empty token, representing NULL +| \(? " ((?: [^"] | "")*) " [,)] # or a quoted string +| \(? ([^",)]+) [,)] # or an unquoted string + """, regex.VERBOSE) + + _re_undouble = regex.compile(r'(["\\])\1') + + @classmethod + def tokenize(self, s): + rv = [] + for m in self._re_tokenize.finditer(s): + if m is None: + raise psycopg2.InterfaceError("can't parse type: %r" % s) + if m.group(1): + rv.append(None) + elif m.group(2): + rv.append(self._re_undouble.sub(r"\1", m.group(2))) + else: + rv.append(m.group(3)) + + return rv + + def _create_type(self, name, attnames): + try: + from collections import namedtuple + except ImportError: + self.type = tuple + self._ctor = lambda *args: tuple(args) + else: + self.type = namedtuple(name, attnames) + self._ctor = self.type + + @classmethod + def _from_db(self, name, conn_or_curs): + """Return a `CompositeCaster` instance for the type *name*. + + Raise `ProgrammingError` if the type is not found. + """ + if hasattr(conn_or_curs, 'execute'): + conn = conn_or_curs.connection + curs = conn_or_curs + else: + conn = conn_or_curs + curs = conn_or_curs.cursor() + + # Store the transaction status of the connection to revert it after use + conn_status = conn.status + + # Use the correct schema + if '.' in name: + schema, tname = name.split('.', 1) + else: + tname = name + schema = 'public' + + # column typarray not available before PG 8.3 + typarray = conn.server_version >= 80300 and "typarray" or "NULL" + + # get the type oid and attributes + curs.execute("""\ +SELECT t.oid, %s, attname, atttypid +FROM pg_type t +JOIN pg_namespace ns ON typnamespace = ns.oid +JOIN pg_attribute a ON attrelid = typrelid +WHERE typname = %%s AND nspname = %%s + AND attnum > 0 AND NOT attisdropped +ORDER BY attnum; +""" % typarray, (tname, schema)) + + recs = curs.fetchall() + + # revert the status of the connection as before the command + if (conn_status != _ext.STATUS_IN_TRANSACTION + and not conn.autocommit): + conn.rollback() + + if not recs: + raise psycopg2.ProgrammingError( + "PostgreSQL type '%s' not found" % name) + + type_oid = recs[0][0] + array_oid = recs[0][1] + type_attrs = [ (r[2], r[3]) for r in recs ] + + return CompositeCaster(tname, type_oid, type_attrs, + array_oid=array_oid) + +def register_composite(name, conn_or_curs, globally=False): + """Register a typecaster to convert a composite type into a tuple. + + :param name: the name of a PostgreSQL composite type, e.g. created using + the |CREATE TYPE|_ command + :param conn_or_curs: a connection or cursor used to find the type oid and + components; the typecaster is registered in a scope limited to this + object, unless *globally* is set to `!True` + :param globally: if `!False` (default) register the typecaster only on + *conn_or_curs*, otherwise register it globally + :return: the registered `CompositeCaster` instance responsible for the + conversion + + .. versionchanged:: 2.4.3 + added support for array of composite types + + """ + caster = CompositeCaster._from_db(name, conn_or_curs) + _ext.register_type(caster.typecaster, not globally and conn_or_curs or None) - _ext.TSTZ_W_SECS = _ext.new_type(oids, 'TSTZ_W_SECS', _convert_tstz_w_secs) - _ext.register_type(TSTZ_W_SECS, conn_or_curs) + if caster.array_typecaster is not None: + _ext.register_type(caster.array_typecaster, not globally and conn_or_curs or None) - return _ext.TSTZ_W_SECS + return caster -__all__ = [ k for k in locals().keys() if not k.startswith('_') ] +__all__ = filter(lambda k: not k.startswith('_'), locals().keys()) diff -Nru psycopg2-2.0.13/lib/__init__.py psycopg2-2.4.5/lib/__init__.py --- psycopg2-2.0.13/lib/__init__.py 2009-10-04 21:32:35.000000000 +0000 +++ psycopg2-2.4.5/lib/__init__.py 2011-12-11 21:18:11.000000000 +0000 @@ -18,55 +18,166 @@ """ # psycopg/__init__.py - initialization of the psycopg module # -# Copyright (C) 2003-2004 Federico Di Gregorio +# Copyright (C) 2003-2010 Federico Di Gregorio # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by the -# Free Software Foundation; either version 2, or (at your option) any later -# version. +# psycopg2 is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. # -# This program is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY -# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# for more details. +# In addition, as a special exception, the copyright holders give +# permission to link this program with the OpenSSL library (or with +# modified versions of OpenSSL that use the same license as OpenSSL), +# and distribute linked combinations including the two. +# +# You must obey the GNU Lesser General Public License in all respects for +# all of the code used other than OpenSSL. +# +# psycopg2 is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +# License for more details. # Import modules needed by _psycopg to allow tools like py2exe to do # their work without bothering about the module dependencies. -# -# TODO: we should probably use the Warnings framework to signal a missing -# module instead of raising an exception (in case we're running a thin -# embedded Python or something even more devious.) import sys, warnings -if sys.version_info[0] >= 2 and sys.version_info[1] >= 3: +if sys.version_info >= (2, 3): try: import datetime as _psycopg_needs_datetime except: warnings.warn( "can't import datetime module probably needed by _psycopg", RuntimeWarning) -if sys.version_info[0] >= 2 and sys.version_info[1] >= 4: +if sys.version_info >= (2, 4): try: import decimal as _psycopg_needs_decimal except: warnings.warn( "can't import decimal module probably needed by _psycopg", RuntimeWarning) -from psycopg2 import tz del sys, warnings +# Note: the first internal import should be _psycopg, otherwise the real cause +# of a failed loading of the C module may get hidden, see +# http://archives.postgresql.org/psycopg/2011-02/msg00044.php + # Import the DBAPI-2.0 stuff into top-level module. -from _psycopg import BINARY, NUMBER, STRING, DATETIME, ROWID +from psycopg2._psycopg import BINARY, NUMBER, STRING, DATETIME, ROWID + +from psycopg2._psycopg import Binary, Date, Time, Timestamp +from psycopg2._psycopg import DateFromTicks, TimeFromTicks, TimestampFromTicks + +from psycopg2._psycopg import Error, Warning, DataError, DatabaseError, ProgrammingError +from psycopg2._psycopg import IntegrityError, InterfaceError, InternalError +from psycopg2._psycopg import NotSupportedError, OperationalError + +from psycopg2._psycopg import _connect, apilevel, threadsafety, paramstyle +from psycopg2._psycopg import __version__ + +from psycopg2 import tz + + +# Register default adapters. + +import psycopg2.extensions as _ext +_ext.register_adapter(tuple, _ext.SQL_IN) +_ext.register_adapter(type(None), _ext.NoneAdapter) + +# Register the Decimal adapter here instead of in the C layer. +# This way a new class is registered for each sub-interpreter. +# See ticket #52 +try: + from decimal import Decimal +except ImportError: + pass +else: + from psycopg2._psycopg import Decimal as Adapter + _ext.register_adapter(Decimal, Adapter) + del Decimal, Adapter + +import re + +def _param_escape(s, + re_escape=re.compile(r"([\\'])"), + re_space=re.compile(r'\s')): + """ + Apply the escaping rule required by PQconnectdb + """ + if not s: return "''" + + s = re_escape.sub(r'\\\1', s) + if re_space.search(s): + s = "'" + s + "'" + + return s + +del re + + +def connect(dsn=None, + database=None, user=None, password=None, host=None, port=None, + connection_factory=None, async=False, **kwargs): + """ + Create a new database connection. + + The connection parameters can be specified either as a string: + + conn = psycopg2.connect("dbname=test user=postgres password=secret") + + or using a set of keyword arguments: + + conn = psycopg2.connect(database="test", user="postgres", password="secret") + + The basic connection parameters are: + + - *dbname*: the database name (only in dsn string) + - *database*: the database name (only as keyword argument) + - *user*: user name used to authenticate + - *password*: password used to authenticate + - *host*: database host address (defaults to UNIX socket if not provided) + - *port*: connection port number (defaults to 5432 if not provided) + + Using the *connection_factory* parameter a different class or connections + factory can be specified. It should be a callable object taking a dsn + argument. + + Using *async*=True an asynchronous connection will be created. + + Any other keyword parameter will be passed to the underlying client + library: the list of supported parameter depends on the library version. + + """ + if dsn is None: + # Note: reproducing the behaviour of the previous C implementation: + # keyword are silently swallowed if a DSN is specified. I would have + # raised an exception. File under "histerical raisins". + items = [] + if database is not None: + items.append(('dbname', database)) + if user is not None: + items.append(('user', user)) + if password is not None: + items.append(('password', password)) + if host is not None: + items.append(('host', host)) + # Reproducing the previous C implementation behaviour: swallow a + # negative port. The libpq would raise an exception for it. + if port is not None and int(port) > 0: + items.append(('port', port)) + + items.extend( + [(k, v) for (k, v) in kwargs.iteritems() if v is not None]) + dsn = " ".join(["%s=%s" % (k, _param_escape(str(v))) + for (k, v) in items]) + + if not dsn: + raise InterfaceError('missing dsn and no parameters') -from _psycopg import Binary, Date, Time, Timestamp -from _psycopg import DateFromTicks, TimeFromTicks, TimestampFromTicks + return _connect(dsn, + connection_factory=connection_factory, async=async) -from _psycopg import Error, Warning, DataError, DatabaseError, ProgrammingError -from _psycopg import IntegrityError, InterfaceError, InternalError -from _psycopg import NotSupportedError, OperationalError -from _psycopg import connect, apilevel, threadsafety, paramstyle -from _psycopg import __version__ +__all__ = filter(lambda k: not k.startswith('_'), locals().keys()) -__all__ = [ k for k in locals().keys() if not k.startswith('_') ] diff -Nru psycopg2-2.0.13/lib/pool.py psycopg2-2.4.5/lib/pool.py --- psycopg2-2.0.13/lib/pool.py 2009-04-26 12:09:21.000000000 +0000 +++ psycopg2-2.4.5/lib/pool.py 2011-12-11 21:18:11.000000000 +0000 @@ -4,25 +4,31 @@ """ # psycopg/pool.py - pooling code for psycopg # -# Copyright (C) 2003-2004 Federico Di Gregorio +# Copyright (C) 2003-2010 Federico Di Gregorio # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by the -# Free Software Foundation; either version 2, or (at your option) any later -# version. +# psycopg2 is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. # -# This program is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY -# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# for more details. +# In addition, as a special exception, the copyright holders give +# permission to link this program with the OpenSSL library (or with +# modified versions of OpenSSL that use the same license as OpenSSL), +# and distribute linked combinations including the two. +# +# You must obey the GNU Lesser General Public License in all respects for +# all of the code used other than OpenSSL. +# +# psycopg2 is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +# License for more details. import psycopg2 +import psycopg2.extensions as _ext 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): @@ -95,7 +101,7 @@ if self.closed: raise PoolError("connection pool is closed") if key is None: key = self._getkey() - if self._used.has_key(key): + if key in self._used: return self._used[key] if self._pool: @@ -110,13 +116,27 @@ def _putconn(self, conn, key=None, close=False): """Put away a connection.""" if self.closed: raise PoolError("connection pool is closed") - if key is None: key = self._rused[id(conn)] + if key is None: key = self._rused.get(id(conn)) if not key: raise PoolError("trying to put unkeyed connection") if len(self._pool) < self.minconn and not close: - self._pool.append(conn) + # Return the connection into a consistent state before putting + # it back into the pool + if not conn.closed: + status = conn.get_transaction_status() + if status == _ext.TRANSACTION_STATUS_UNKNOWN: + # server connection lost + conn.close() + elif status != _ext.TRANSACTION_STATUS_IDLE: + # connection in error or in transaction + conn.rollback() + self._pool.append(conn) + else: + # regular idle connection + self._pool.append(conn) + # If the connection is closed, we just discard it. else: conn.close() @@ -189,9 +209,9 @@ """A pool that assigns persistent connections to different threads. Note that this connection pool generates by itself the required keys - using the current thread id. This means that untill a thread put away + using the current thread id. This means that until a thread puts away a connection it will always get the same connection object by successive - .getconn() calls. This also means that a thread can't use more than one + `!getconn()` calls. This also means that a thread can't use more than one single connection from the pool. """ diff -Nru psycopg2-2.0.13/lib/psycopg1.py psycopg2-2.4.5/lib/psycopg1.py --- psycopg2-2.0.13/lib/psycopg1.py 2009-04-26 12:09:21.000000000 +0000 +++ psycopg2-2.4.5/lib/psycopg1.py 2011-12-19 10:30:20.000000000 +0000 @@ -8,31 +8,39 @@ """ # psycopg/psycopg1.py - psycopg 1.1.x compatibility module # -# Copyright (C) 2003-2004 Federico Di Gregorio +# Copyright (C) 2003-2010 Federico Di Gregorio # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by the -# Free Software Foundation; either version 2, or (at your option) any later -# version. +# psycopg2 is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. # -# This program is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY -# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# for more details. +# In addition, as a special exception, the copyright holders give +# permission to link this program with the OpenSSL library (or with +# modified versions of OpenSSL that use the same license as OpenSSL), +# and distribute linked combinations including the two. +# +# You must obey the GNU Lesser General Public License in all respects for +# all of the code used other than OpenSSL. +# +# psycopg2 is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +# License for more details. import _psycopg as _2psycopg from psycopg2.extensions import cursor as _2cursor from psycopg2.extensions import connection as _2connection from psycopg2 import * -del connect - +import psycopg2.extensions as _ext +_2connect = connect def connect(*args, **kwargs): """connect(dsn, ...) -> new psycopg 1.1.x compatible connection object""" kwargs['connection_factory'] = connection - conn = _2psycopg.connect(*args, **kwargs) - conn.set_isolation_level(2) + conn = _2connect(*args, **kwargs) + conn.set_isolation_level(_ext.ISOLATION_LEVEL_READ_COMMITTED) return conn class connection(_2connection): @@ -45,9 +53,9 @@ def autocommit(self, on_off=1): """autocommit(on_off=1) -> switch autocommit on (1) or off (0)""" if on_off > 0: - self.set_isolation_level(0) + self.set_isolation_level(_ext.ISOLATION_LEVEL_AUTOCOMMIT) else: - self.set_isolation_level(2) + self.set_isolation_level(_ext.ISOLATION_LEVEL_READ_COMMITTED) class cursor(_2cursor): diff -Nru psycopg2-2.0.13/lib/tz.py psycopg2-2.4.5/lib/tz.py --- psycopg2-2.0.13/lib/tz.py 2009-04-26 12:09:21.000000000 +0000 +++ psycopg2-2.4.5/lib/tz.py 2012-03-28 21:09:15.000000000 +0000 @@ -6,40 +6,73 @@ """ # psycopg/tz.py - tzinfo implementation # -# Copyright (C) 2003-2004 Federico Di Gregorio +# Copyright (C) 2003-2010 Federico Di Gregorio # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by the -# Free Software Foundation; either version 2, or (at your option) any later -# version. +# psycopg2 is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. # -# This program is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY -# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# for more details. +# In addition, as a special exception, the copyright holders give +# permission to link this program with the OpenSSL library (or with +# modified versions of OpenSSL that use the same license as OpenSSL), +# and distribute linked combinations including the two. +# +# You must obey the GNU Lesser General Public License in all respects for +# all of the code used other than OpenSSL. +# +# psycopg2 is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +# License for more details. import datetime import time - ZERO = datetime.timedelta(0) class FixedOffsetTimezone(datetime.tzinfo): """Fixed offset in minutes east from UTC. - This is exactly the implementation found in Python 2.3.x documentation, - with a small change to the __init__ method to allow for pickling and a - default name in the form 'sHH:MM' ('s' is the sign.) + This is exactly the implementation__ found in Python 2.3.x documentation, + with a small change to the `!__init__()` method to allow for pickling + and a default name in the form ``sHH:MM`` (``s`` is the sign.). + + The implementation also caches instances. During creation, if a + FixedOffsetTimezone instance has previously been created with the same + offset and name that instance will be returned. This saves memory and + improves comparability. + + .. __: http://docs.python.org/library/datetime.html#datetime-tzinfo """ _name = None _offset = ZERO - + + _cache = {} + def __init__(self, offset=None, name=None): if offset is not None: self._offset = datetime.timedelta(minutes = offset) if name is not None: self._name = name + def __new__(cls, offset=None, name=None): + """Return a suitable instance created earlier if it exists + """ + key = (offset, name) + try: + return cls._cache[key] + except KeyError: + tz = datetime.tzinfo.__new__(cls, offset, name) + tz.__init__(offset, name) + cls._cache[key] = tz + return tz + + def __repr__(self): + offset_mins = self._offset.seconds // 60 + self._offset.days * 24 * 60 + return "psycopg2.tz.FixedOffsetTimezone(offset=%r, name=%r)" \ + % (offset_mins, self._name) + def utcoffset(self, dt): return self._offset @@ -69,7 +102,7 @@ class LocalTimezone(datetime.tzinfo): """Platform idea of local timezone. - This is the exact implementation from the Pyhton 2.3 documentation. + This is the exact implementation from the Python 2.3 documentation. """ def utcoffset(self, dt): diff -Nru psycopg2-2.0.13/LICENSE psycopg2-2.4.5/LICENSE --- psycopg2-2.0.13/LICENSE 2009-04-26 12:09:21.000000000 +0000 +++ psycopg2-2.4.5/LICENSE 2011-02-06 15:58:34.000000000 +0000 @@ -1,24 +1,32 @@ -psycopg and the GPL -=================== +psycopg2 and the LGPL +===================== -psycopg is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. See file COPYING for details. - -As a special exception, specific permission is granted for the GPLed -code in this distribition to be linked to OpenSSL and PostgreSQL libpq -without invoking GPL clause 2(b). - -Note that the GPL was chosen to avoid proprietary adapters based on -psycopg code. Using psycopg in a proprietary product (even bundling -psycopg with the proprietary product) is fine as long as: +psycopg2 is free software: you can redistribute it and/or modify it +under the terms of the GNU Lesser General Public License as published +by the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +psycopg2 is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +License for more details. + +In addition, as a special exception, the copyright holders give +permission to link this program with the OpenSSL library (or with +modified versions of OpenSSL that use the same license as OpenSSL), +and distribute linked combinations including the two. + +You must obey the GNU Lesser General Public License in all respects for +all of the code used other than OpenSSL. If you modify file(s) with this +exception, you may extend this exception to your version of the file(s), +but you are not obligated to do so. If you do not wish to do so, delete +this exception statement from your version. If you delete this exception +statement from all source files in the program, then also delete it here. + +You should have received a copy of the GNU Lesser General Public License +along with psycopg2 (see the doc/ directory.) +If not, see . - 1. psycopg is called from Python only using only the provided API - (i.e., no linking with C code and no C modules based on it); and - - 2. all the other points of the GPL are respected (you offer a copy - of psycopg's source code, and so on.) Alternative licenses ==================== @@ -44,17 +52,3 @@ be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. - -psycopg is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -Proprietary licenses -==================== - -A non-exclusive license is available for companies that want to include -psycopg in their proprietary products without respecting the spirit of the -GPL. The price of the license is one day of development done by the author, -at the consulting fee he applies to his usual customers at the day of the -request. diff -Nru psycopg2-2.0.13/Makefile psycopg2-2.4.5/Makefile --- psycopg2-2.0.13/Makefile 1970-01-01 00:00:00.000000000 +0000 +++ psycopg2-2.4.5/Makefile 2011-02-06 15:58:34.000000000 +0000 @@ -0,0 +1,136 @@ +# Makefile for psycopg2. Do you want to... +# +# Build the library:: +# +# make +# +# Build the documentation:: +# +# make env +# make docs +# +# Create a source package:: +# +# make env # required to build the documentation +# make sdist +# +# Run the test:: +# +# make check # this requires setting up a test database with the correct user + +PYTHON := python$(PYTHON_VERSION) +PYTHON_VERSION ?= $(shell $(PYTHON) -c 'import sys; print ("%d.%d" % sys.version_info[:2])') +BUILD_DIR = $(shell pwd)/build/lib.$(PYTHON_VERSION) +ENV_DIR = $(shell pwd)/env/py-$(PYTHON_VERSION) +ENV_BIN = $(ENV_DIR)/bin +ENV_LIB = $(ENV_DIR)/lib + +SOURCE_C := $(wildcard psycopg/*.c psycopg/*.h) +SOURCE_PY := $(wildcard lib/*.py) +SOURCE_TESTS := $(wildcard tests/*.py) +SOURCE_DOC := $(wildcard doc/src/*.rst) +SOURCE := $(SOURCE_C) $(SOURCE_PY) $(SOURCE_TESTS) $(SOURCE_DOC) + +PACKAGE := $(BUILD_DIR)/psycopg2 +PLATLIB := $(PACKAGE)/_psycopg.so +PURELIB := $(patsubst lib/%,$(PACKAGE)/%,$(SOURCE_PY)) \ + $(patsubst tests/%,$(PACKAGE)/tests/%,$(SOURCE_TESTS)) + +BUILD_OPT := --build-lib=$(BUILD_DIR) +BUILD_EXT_OPT := --build-lib=$(BUILD_DIR) +SDIST_OPT := --formats=gztar + +ifdef PG_CONFIG + BUILD_EXT_OPT += --pg-config=$(PG_CONFIG) +endif + +VERSION := $(shell grep PSYCOPG_VERSION setup.py | head -1 | sed -e "s/.*'\(.*\)'/\1/") +SDIST := dist/psycopg2-$(VERSION).tar.gz + +EASY_INSTALL = PYTHONPATH=$(ENV_LIB) $(ENV_BIN)/easy_install-$(PYTHON_VERSION) -d $(ENV_LIB) -s $(ENV_BIN) +EZ_SETUP = $(ENV_BIN)/ez_setup.py + +.PHONY: env check clean + +default: package + +all: package sdist + +package: $(PLATLIB) $(PURELIB) + +docs: docs-html docs-txt + +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. +# It is not clean by 'make clean' + +env: easy_install + mkdir -p $(ENV_BIN) + mkdir -p $(ENV_LIB) + $(EASY_INSTALL) docutils + $(EASY_INSTALL) sphinx + +easy_install: ez_setup + PYTHONPATH=$(ENV_LIB) $(PYTHON) $(EZ_SETUP) -d $(ENV_LIB) -s $(ENV_BIN) setuptools + +ez_setup: + mkdir -p $(ENV_BIN) + mkdir -p $(ENV_LIB) + wget -O $(EZ_SETUP) http://peak.telecommunity.com/dist/ez_setup.py + +check: + PYTHONPATH=$(BUILD_DIR):$(PYTHONPATH) $(PYTHON) -c "from psycopg2 import tests; tests.unittest.main(defaultTest='tests.test_suite')" --verbose + +testdb: + @echo "* Creating $(TESTDB)" + @if psql -l | grep -q " $(TESTDB) "; then \ + dropdb $(TESTDB) >/dev/null; \ + fi + createdb $(TESTDB) + # Note to packagers: this requires the postgres user running the test + # to be a superuser. You may change this line to use the superuser only + # to install the contrib. Feel free to suggest a better way to set up the + # testing environment (as the current is enough for development). + psql -f `pg_config --sharedir`/contrib/hstore.sql $(TESTDB) + + +$(PLATLIB): $(SOURCE_C) + $(PYTHON) setup.py build_ext $(BUILD_EXT_OPT) + +$(PACKAGE)/%.py: lib/%.py + $(PYTHON) setup.py build_py $(BUILD_OPT) + touch $@ + +$(PACKAGE)/tests/%.py: tests/%.py + $(PYTHON) setup.py build_py $(BUILD_OPT) + touch $@ + +$(SDIST): docs MANIFEST $(SOURCE) + $(PYTHON) setup.py sdist $(SDIST_OPT) + +MANIFEST: MANIFEST.in $(SOURCE) + # Run twice as MANIFEST.in includes MANIFEST + $(PYTHON) setup.py sdist --manifest-only + $(PYTHON) setup.py sdist --manifest-only + +# docs depend on the build as it partly use introspection. +doc/html/genindex.html: $(PLATLIB) $(PURELIB) $(SOURCE_DOC) + PYTHONPATH=$(ENV_LIB):$(BUILD_DIR) $(MAKE) SPHINXBUILD=$(ENV_BIN)/sphinx-build -C doc html + +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 + $(MAKE) -C doc clean diff -Nru psycopg2-2.0.13/MANIFEST psycopg2-2.4.5/MANIFEST --- psycopg2-2.0.13/MANIFEST 2009-10-07 16:28:55.000000000 +0000 +++ psycopg2-2.4.5/MANIFEST 2012-03-28 21:17:27.000000000 +0000 @@ -1,9 +1,12 @@ +# file GENERATED by distutils, do NOT edit AUTHORS ChangeLog INSTALL LICENSE MANIFEST MANIFEST.in +Makefile +NEWS README setup.cfg setup.py @@ -15,6 +18,7 @@ ZPsycopgDA/dtml/browse.dtml ZPsycopgDA/dtml/edit.dtml ZPsycopgDA/dtml/table_info.dtml +ZPsycopgDA/icons/DBAdapterFolder_icon.gif ZPsycopgDA/icons/bin.gif ZPsycopgDA/icons/date.gif ZPsycopgDA/icons/datetime.gif @@ -37,13 +41,84 @@ debian/rules debian/watch debian/zope-psycopgda2.dzproduct +doc/COPYING +doc/COPYING.LESSER doc/ChangeLog-1.x doc/HACKING +doc/Makefile +doc/README doc/SUCCESS -doc/TODO -doc/api-screen.css -doc/async.txt -doc/extensions.rst +doc/pep-0249.txt +doc/psycopg2.txt +doc/html/.buildinfo +doc/html/advanced.html +doc/html/connection.html +doc/html/cursor.html +doc/html/errorcodes.html +doc/html/extensions.html +doc/html/extras.html +doc/html/faq.html +doc/html/genindex.html +doc/html/index.html +doc/html/module.html +doc/html/objects.inv +doc/html/pool.html +doc/html/py-modindex.html +doc/html/search.html +doc/html/searchindex.js +doc/html/tz.html +doc/html/usage.html +doc/html/_sources/advanced.txt +doc/html/_sources/connection.txt +doc/html/_sources/cursor.txt +doc/html/_sources/errorcodes.txt +doc/html/_sources/extensions.txt +doc/html/_sources/extras.txt +doc/html/_sources/faq.txt +doc/html/_sources/index.txt +doc/html/_sources/module.txt +doc/html/_sources/pool.txt +doc/html/_sources/tz.txt +doc/html/_sources/usage.txt +doc/html/_static/ajax-loader.gif +doc/html/_static/basic.css +doc/html/_static/comment-bright.png +doc/html/_static/comment-close.png +doc/html/_static/comment.png +doc/html/_static/default.css +doc/html/_static/doctools.js +doc/html/_static/down-pressed.png +doc/html/_static/down.png +doc/html/_static/file.png +doc/html/_static/jquery.js +doc/html/_static/minus.png +doc/html/_static/plus.png +doc/html/_static/psycopg.css +doc/html/_static/pygments.css +doc/html/_static/searchtools.js +doc/html/_static/sidebar.js +doc/html/_static/underscore.js +doc/html/_static/up-pressed.png +doc/html/_static/up.png +doc/html/_static/websupport.js +doc/src/Makefile +doc/src/advanced.rst +doc/src/conf.py +doc/src/connection.rst +doc/src/cursor.rst +doc/src/errorcodes.rst +doc/src/extensions.rst +doc/src/extras.rst +doc/src/faq.rst +doc/src/index.rst +doc/src/module.rst +doc/src/pool.rst +doc/src/tz.rst +doc/src/usage.rst +doc/src/_static/psycopg.css +doc/src/tools/stitch_text.py +doc/src/tools/lib/dbapi_extension.py +doc/src/tools/lib/sql_role.py examples/binary.py examples/copy_from.py examples/copy_to.py @@ -72,6 +147,8 @@ lib/pool.py lib/psycopg1.py lib/tz.py +psycopg/_psycopg.vc9.amd64.manifest +psycopg/_psycopg.vc9.x86.manifest psycopg/adapter_asis.c psycopg/adapter_asis.h psycopg/adapter_binary.c @@ -84,10 +161,15 @@ psycopg/adapter_mxdatetime.h psycopg/adapter_pboolean.c psycopg/adapter_pboolean.h +psycopg/adapter_pdecimal.c +psycopg/adapter_pdecimal.h psycopg/adapter_pfloat.c psycopg/adapter_pfloat.h +psycopg/adapter_pint.c +psycopg/adapter_pint.h psycopg/adapter_qstring.c psycopg/adapter_qstring.h +psycopg/bytes_format.c psycopg/config.h psycopg/connection.h psycopg/connection_int.c @@ -95,6 +177,8 @@ psycopg/cursor.h psycopg/cursor_int.c psycopg/cursor_type.c +psycopg/green.c +psycopg/green.h psycopg/lobject.h psycopg/lobject_int.c psycopg/lobject_type.c @@ -102,8 +186,9 @@ psycopg/microprotocols.h psycopg/microprotocols_proto.c psycopg/microprotocols_proto.h +psycopg/notify.h +psycopg/notify_type.c psycopg/pgtypes.h -psycopg/pgversion.h psycopg/pqpath.c psycopg/pqpath.h psycopg/psycopg.h @@ -119,6 +204,8 @@ psycopg/typecast_datetime.c psycopg/typecast_mxdatetime.c psycopg/utils.c +psycopg/xid.h +psycopg/xid_type.c psycopg2da/DEPENDENCIES.cfg psycopg2da/PACKAGE.cfg psycopg2da/PUBLICATION.cfg @@ -129,18 +216,31 @@ psycopg2da/psycopg2da-configure.zcml psycopg2da/tests.py scripts/buildtypes.py -scripts/ext2html.py -scripts/makedocs.py +scripts/fix_b.py +scripts/make_errorcodes.py scripts/maketypes.sh +scripts/refcounter.py +scripts/ticket58.py tests/__init__.py -tests/bugX000.py tests/dbapi20.py -tests/extras_dictcursor.py +tests/dbapi20_tpc.py +tests/test_async.py +tests/test_bugX000.py +tests/test_bug_gc.py +tests/test_cancel.py tests/test_connection.py +tests/test_copy.py +tests/test_cursor.py tests/test_dates.py +tests/test_extras_dictcursor.py +tests/test_green.py tests/test_lobject.py +tests/test_module.py +tests/test_notify.py tests/test_psycopg2_dbapi20.py tests/test_quote.py tests/test_transaction.py -tests/types_basic.py -tests/types_extras.py +tests/test_types_basic.py +tests/test_types_extras.py +tests/testconfig.py +tests/testutils.py diff -Nru psycopg2-2.0.13/MANIFEST.in psycopg2-2.4.5/MANIFEST.in --- psycopg2-2.0.13/MANIFEST.in 2009-04-26 12:09:21.000000000 +0000 +++ psycopg2-2.4.5/MANIFEST.in 2011-02-27 12:03:48.000000000 +0000 @@ -1,13 +1,16 @@ -recursive-include psycopg *.c *.h +recursive-include psycopg *.c *.h *.manifest recursive-include lib *.py recursive-include tests *.py recursive-include ZPsycopgDA *.py *.gif *.dtml recursive-include psycopg2da * recursive-include examples *.py somehackers.jpg whereareyou.jpg recursive-include debian * -recursive-include doc TODO HACKING SUCCESS ChangeLog-1.x async.txt -recursive-include doc *.rst *.css *.html +recursive-include doc README HACKING SUCCESS COPYING* ChangeLog-1.x pep-0249.txt +recursive-include doc *.txt *.html *.css *.js Makefile +recursive-include doc/src *.rst *.py *.css Makefile +recursive-include doc/html * +prune doc/src/_build recursive-include scripts *.py *.sh include scripts/maketypes.sh scripts/buildtypes.py -include AUTHORS README INSTALL LICENSE ChangeLog -include PKG-INFO MANIFEST.in MANIFEST setup.py setup.cfg +include AUTHORS README INSTALL LICENSE NEWS ChangeLog +include PKG-INFO MANIFEST.in MANIFEST setup.py setup.cfg Makefile diff -Nru psycopg2-2.0.13/NEWS psycopg2-2.4.5/NEWS --- psycopg2-2.0.13/NEWS 1970-01-01 00:00:00.000000000 +0000 +++ psycopg2-2.4.5/NEWS 2012-03-28 21:09:15.000000000 +0000 @@ -0,0 +1,856 @@ +What's new in psycopg 2.4.5 +--------------------------- + + - The close() methods on connections and cursors don't raise exceptions + if called on already closed objects. + - Fixed fetchmany() with no argument in cursor subclasses + (ticket #84). + - Use lo_creat() instead of lo_create() when possible for better + interaction with pgpool-II (ticket #88). + - Error and its subclasses are picklable, useful for multiprocessing + interaction (ticket #90). + - Better efficiency and formatting of timezone offset objects thanks + to Menno Smits (tickets #94, #95). + - Fixed 'rownumber' during iteration on cursor subclasses. + Regression introduced in 2.4.4 (ticket #100). + - Added support for 'inet' arrays. + - Fixed 'commit()' concurrency problem (ticket #103). + - Codebase cleaned up using the GCC Python plugin's static analysis + tool, which has revealed several unchecked return values, possible + NULL dereferences, reference counting problems. Many thanks to David + Malcolm for the useful tool and the assistance provided using it. + + +What's new in psycopg 2.4.4 +--------------------------- + + - 'register_composite()' also works with the types implicitly defined + after a table row, not only with the ones created by 'CREATE TYPE'. + - Values for the isolation level symbolic constants restored to what + they were before release 2.4.2 to avoid breaking apps using the + values instead of the constants. + - Named DictCursor/RealDictCursor honour itersize (ticket #80). + - Fixed rollback on error on Zope (ticket #73). + - Raise 'DatabaseError' instead of 'Error' with empty libpq errors, + consistently with other disconnection-related errors: regression + introduced in release 2.4.1 (ticket #82). + + +What's new in psycopg 2.4.3 +--------------------------- + + - connect() supports all the keyword arguments supported by the + database + - Added 'new_array_type()' function for easy creation of array + typecasters. + - Added support for arrays of hstores and composite types (ticket #66). + - Fixed segfault in case of transaction started with connection lost + (and possibly other events). + - Fixed adaptation of Decimal type in sub-interpreters, such as in + certain mod_wsgi configurations (ticket #52). + - Rollback connections in transaction or in error before putting them + back into a pool. Also discard broken connections (ticket #62). + - Lazy import of the slow uuid module, thanks to Marko Kreen. + - Fixed NamedTupleCursor.executemany() (ticket #65). + - Fixed --static-libpq setup option (ticket #64). + - Fixed interaction between RealDictCursor and named cursors + (ticket #67). + - Dropped limit on the columns length in COPY operations (ticket #68). + - Fixed reference leak with arguments referenced more than once + in queries (ticket #81). + - Fixed typecasting of arrays containing consecutive backslashes. + - 'errorcodes' map updated to PostgreSQL 9.1. + + +What's new in psycopg 2.4.2 +--------------------------- + + - Added 'set_session()' method and 'autocommit' property to the + connection. Added support for read-only sessions and, for PostgreSQL + 9.1, for the "repeatable read" isolation level and the "deferrable" + transaction property. + - Psycopg doesn't execute queries at connection time to find the + default isolation level. + - Fixed bug with multithread code potentially causing loss of sync + with the server communication or lock of the client (ticket #55). + - Don't fail import if mx.DateTime module can't be found, even if its + support was built (ticket #53). + - Fixed escape for negative numbers prefixed by minus operator + (ticket #57). + - Fixed refcount issue during copy. Reported and fixed by Dave + Malcolm (ticket #58, Red Hat Bug 711095). + - Trying to execute concurrent operations on the same connection + through concurrent green thread results in an error instead of a + deadlock. + - Fixed detection of pg_config on Window. Report and fix, plus some + long needed setup.py cleanup by Steve Lacy: thanks! + + +What's new in psycopg 2.4.1 +--------------------------- + + - Use own parser for bytea output, not requiring anymore the libpq 9.0 + to parse the hex format. + - Don't fail connection if the client encoding is a non-normalized + variant. Issue reported by Peter Eisentraut. + - Correctly detect an empty query sent to the backend (ticket #46). + - Fixed a SystemError clobbering libpq errors raised without SQLSTATE. + Bug vivisectioned by Eric Snow. + - Fixed interaction between NamedTuple and server-side cursors. + - Allow to specify --static-libpq on setup.py command line instead of + just in 'setup.cfg'. Patch provided by Matthew Ryan (ticket #48). + + +What's new in psycopg 2.4 +------------------------- + +* New features and changes: + + - Added support for Python 3.1 and 3.2. The conversion has also + brought several improvements: + + - Added 'b' and 't' mode to large objects: write can deal with both + bytes strings and unicode; read can return either bytes strings + or decoded unicode. + - COPY sends Unicode data to files implementing 'io.TextIOBase'. + - 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. + + - Improvements to the named cusors: + + - More efficient iteration on named cursors, fetching 'itersize' + records at time from the backend. + - The named cursors name can be an invalid identifier. + + - Improvements in data handling: + + - Added 'register_composite()' function to cast PostgreSQL + composite types into Python tuples/namedtuples. + - Adapt types 'bytearray' (from Python 2.6), 'memoryview' (from + Python 2.7) and other objects implementing the "Revised Buffer + Protocol" to 'bytea' data type. + - The 'hstore' adapter can work even when the data type is not + installed in the 'public' namespace. + - Raise a clean exception instead of returning bad data when + receiving bytea in 'hex' format and the client libpq can't parse + them. + - Empty lists correctly roundtrip Python -> PostgreSQL -> Python. + + - Other changes: + + - 'cursor.description' is provided as named tuples if available. + - The build script refuses to guess values if 'pg_config' is not + found. + - Connections and cursors are weakly referenceable. + +* Bug fixes: + + - Fixed adaptation of None in composite types (ticket #26). Bug + report by Karsten Hilbert. + - Fixed several reference leaks in less common code paths. + - Fixed segfault when a large object is closed and its connection no + more available. + - Added missing icon to ZPsycopgDA package, not available in Zope + 2.12.9 (ticket #30). Bug report and patch by Pumukel. + - Fixed conversion of negative infinity (ticket #40). Bug report and + patch by Marti Raudsepp. + + +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 +--------------------------- + + - Fixed build problem on CentOS 5.5 x86_64 (ticket #23). + + +What's new in psycopg 2.3.0 +--------------------------- + +psycopg 2.3 aims to expose some new features introduced in PostgreSQL 9.0. + +* Main new features: + + - `dict` to `hstore` adapter and `hstore` to `dict` typecaster, using both + 9.0 and pre-9.0 syntax. + - Two-phase commit protocol support as per DBAPI specification. + - Support for payload in notifications received from the backed. + - `namedtuple`-returning cursor. + - Query execution cancel. + +* Other features and changes: + + - Dropped support for protocol 2: Psycopg 2.3 can only connect to PostgreSQL + servers with version at least 7.4. + - Don't issue a query at every connection to detect the client encoding + and to set the datestyle to ISO if it is already compatible with what + expected. + - `mogrify()` now supports unicode queries. + - Subclasses of a type that can be adapted are adapted as the superclass. + - `errorcodes` knows a couple of new codes introduced in PostgreSQL 9.0. + - Dropped deprecated Psycopg "own quoting". + - Never issue a ROLLBACK on close/GC. This behaviour was introduced as a bug + in release 2.2, but trying to send a command while being destroyed has been + considered not safe. + +* Bug fixes: + + - Fixed use of `PQfreemem` instead of `free` in binary typecaster. + - Fixed access to freed memory in `conn_get_isolation_level()`. + - Fixed crash during Decimal adaptation with a few 2.5.x Python versions + (ticket #7). + - Fixed notices order (ticket #9). + + +What's new in psycopg 2.2.2 +--------------------------- + +* Bux fixes: + + - the call to logging.basicConfig() in pool.py has been dropped: it was + messing with some projects using logging (and a library should not + initialize the logging system anyway.) + - psycopg now correctly handles time zones with seconds in the UTC offset. + The old register_tstz_w_secs() function is deprecated and will raise a + warning if called. + - Exceptions raised by the column iterator are propagated. + - Exceptions raised by executemany() interators are propagated. + + +What's new in psycopg 2.2.1 +--------------------------- + +* Bux fixes: + + - psycopg now builds again on MS Windows. + + +What's new in psycopg 2.2.0 +--------------------------- + +This is the first release of the new 2.2 series, supporting not just one but +two different ways of executing asynchronous queries, thanks to Jan and Daniele +(with a little help from me and others, but they did 99% of the work so they +deserve their names here in the news.) + +psycopg now supports both classic select() loops and "green" coroutine +libraries. It is all in the documentation, so just point your browser to +doc/html/advanced.html. + +* Other new features: + + - truncate() method for lobjects. + - COPY functions are now a little bit faster. + - All builtin PostgreSQL to Python typecasters are now available from the + psycopg2.extensions module. + - Notifications from the backend are now available right after the execute() + call (before client code needed to call isbusy() to ensure NOTIFY + reception.) + - Better timezone support. + - Lots of documentation updates. + +* Bug fixes: + + - Fixed some gc/refcounting problems. + - Fixed reference leak in NOTIFY reception. + - Fixed problem with PostgreSQL not casting string literals to the correct + types in some situations: psycopg now add an explicit cast to dates, times + and bytea representations. + - Fixed TimestampFromTicks() and TimeFromTicks() for seconds >= 59.5. + - Fixed spurious exception raised when calling C typecasters from Python + ones. + + +What's new in psycopg 2.0.14 +---------------------------- + +* New features: + - Support for adapting tuples to PostgreSQL arrays is now enabled by + default and does not require importing psycopg2.extensions anymore. + - "can't adapt" error message now includes full type information. + - Thank to Daniele Varrazzo (piro) psycopg2's source package now includes + full documentation in HTML and plain text format. + +* Bug fixes: + - No loss of precision when using floats anymore. + - decimal.Decimal "nan" and "infinity" correctly converted to PostgreSQL + numeric NaN values (note that PostgreSQL numeric type does not support + infinity but just NaNs.) + - psycopg2.extensions now includes Binary. + +* It seems we're good citizens of the free software ecosystem and that big + big big companies and people ranting on the pgsql-hackers mailing list + we'll now not dislike us. *g* (See LICENSE file for the details.) + + +What's new in psycopg 2.0.13 +---------------------------- + +* New features: + - Support for UUID arrays. + - It is now possible to build psycopg linking to a static libpq + library. + +* Bug fixes: + - Fixed a deadlock related to using the same connection with + multiple cursors from different threads. + - Builds again with MSVC. + + +What's new in psycopg 2.0.12 +---------------------------- + +* New features: + - The connection object now has a reset() method that can be used to + reset the connection to its default state. + +* Bug fixes: + - copy_to() and copy_from() now accept a much larger number of columns. + - Fixed PostgreSQL version detection. + - Fixed ZPsycopgDA version check. + - Fixed regression in ZPsycopgDA that made it behave wrongly when + receiving serialization errors: now the query is re-issued as it + should be by propagating the correct exception to Zope. + - Writing "large" large objects should now work. + + +What's new in psycopg 2.0.11 +---------------------------- + +* New features: + - DictRow and RealDictRow now use less memory. If you inherit on them + remember to set __slots__ for your new attributes or be prepare to + go back to old memory usage. + +* Bug fixes: + - Fixed exeception in setup.py. + - More robust detection of PostgreSQL development versions. + - Fixed exception in RealDictCursor, introduced in 2.0.10. + + +What's new in psycopg 2.0.10 +---------------------------- + +* New features: + - A specialized type-caster that can parse time zones with seconds is + now available. Note that after enabling it (see extras.py) "wrong" + time zones will be parsed without raising an exception but the + result will be rounded. + - DictCursor can be used as a named cursor. + - DictRow now implements more dict methods. + - The connection object now expose PostgreSQL server version as the + .server_version attribute and the protocol version used as + .protocol_version. + - The connection object has a .get_parameter_status() methods that + can be used to obtain useful information from the server. + +* Bug fixes: + - None is now correctly always adapted to NULL. + - Two double memory free errors provoked by multithreading and + garbage collection are now fixed. + - Fixed usage of internal Python code in the notice processor; this + should fix segfaults when receiving a lot of notices in + multithreaded programs. + - Should build again on MSVC and Solaris. + - Should build with development versions of PostgreSQL (ones with + -devel version string.) + - Fixed some tests that failed even when psycopg was right. + + +What's new in psycopg 2.0.9 +--------------------------- + +* New features: + - "import psycopg2.extras" to get some support for handling times + and timestamps with seconds in the time zone offset. + - DictCursors can now be used as named cursors. + +* Bug fixes: + - register_type() now accept an explicit None as its second parameter. + - psycopg2 should build again on MSVC and Solaris. + + +What's new in psycopg 2.0.9 +--------------------------- + +* New features: + - COPY TO/COPY FROM queries now can be of any size and psycopg will + correctly quote separators. + - float values Inf and NaN are now correctly handled and can + round-trip to the database. + - executemany() now return the numer of total INSERTed or UPDATEd + rows. Note that, as it has always been, executemany() should not + be used to execute multiple SELECT statements and while it will + execute the statements without any problem, it will return the + wrong value. + - copy_from() and copy_to() can now use quoted separators. + - "import psycopg2.extras" to get UUID support. + +* Bug fixes: + - register_type() now works on connection and cursor subclasses. + - fixed a memory leak when using lobjects. + + +What's new in psycopg 2.0.8 +--------------------------- + +* New features: + - The connection object now has a get_backend_pid() method that + returns the current PostgreSQL connection backend process PID. + - The PostgreSQL large object API has been exposed through the + Cursor.lobject() method. + +* Bug fixes: + - Some fixes to ZPsycopgDA have been merged from the Debian package. + - A memory leak was fixed in Cursor.executemany(). + - A double free was fixed in pq_complete_error(), that caused crashes + under some error conditions. + +What's new in psycopg 2.0.7 +--------------------------- + +* Improved error handling: + - All instances of psycopg2.Error subclasses now have pgerror, + pgcode and cursor attributes. They will be set to None if no + value is available. + - Exception classes are now chosen based on the SQLSTATE value from + the result. (#184) + - The commit() and rollback() methods now set the pgerror and pgcode + attributes on exceptions. (#152) + - errors from commit() and rollback() are no longer considered + fatal. (#194) + - If a disconnect is detected during execute(), an exception will be + raised at that point rather than resulting in "ProgrammingError: + no results to fetch" later on. (#186) + +* Better PostgreSQL compatibility: + - If the server uses standard_conforming_strings, perform + appropriate quoting. + - BC dates are now handled if psycopg is compiled with mxDateTime + support. If using datetime, an appropriate ValueError is + raised. (#203) + +* Other bug fixes: + - If multiple sub-interpreters are in use, do not share the Decimal + type between them. (#192) + - Buffer objects obtained from psycopg are now accepted by psycopg + too, without segfaulting. (#209) + - A few small changes were made to improve DB-API compatibility. + All the dbapi20 tests now pass. + +* Miscellaneous: + - The PSYCOPG_DISPLAY_SIZE option is now off by default. This means + that display size will always be set to "None" in + cursor.description. Calculating the display size was expensive, + and infrequently used so this should improve performance. + - New QueryCanceledError and TransactionRollbackError exceptions + have been added to the psycopg2.extensions module. They can be + used to detect statement timeouts and deadlocks respectively. + - Cursor objects now have a "closed" attribute. (#164) + - If psycopg has been built with debug support, it is now necessary + to set the PSYCOPG_DEBUG environment variable to turn on debug + spew. + +What's new in psycopg 2.0.6 +--------------------------- + +* 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.) + +* Better management of times and dates both from Python and in Zope. + +* copy_to and copy_from now take an extra "columns" parameter. + +* Python tuples are now adapted to SQL sequences that can be used with + the "IN" operator by default if the psycopg2.extensions module is + imported (i.e., the SQL_IN adapter was moved from extras to extensions.) + +* Fixed some small buglets and build glitches: + - removed double mutex destroy + - removed all non-constant initializers + - fixed PyObject_HEAD declarations to avoid memory corruption + on 64 bit architectures + - fixed several Python API calls to work on 64 bit architectures + - applied compatibility macros from PEP 353 + - now using more than one argument format raise an error instead of + a segfault + +What's new in psycopg 2.0.5.1 +­---------------------------- + +* Now it really, really builds on MSVC and older gcc versions. + +What's new in psycopg 2.0.5 +­-------------------------- + +* Fixed various buglets such as: + - segfault when passing an empty string to Binary() + - segfault on null queries + - segfault and bad keyword naming in .executemany() + - OperationalError in connection objects was always None + +* Various changes to ZPsycopgDA to make it more zope2.9-ish. + +* connect() now accept both integers and strings as port parameter + +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 +------------------------- + +* Fixed handle leak on win32. + +* If available the new "safe" encoding functions of libpq are used. + +* django and tinyerp people, please switch to psycopg 2 _without_ + using a psycopg 1 compatibility layer (this release was anticipated + so that you all stop grumbling about psycopg 2 is still in beta.. :) + +What's new in psycopg 2.0 beta 7 +-------------------------------- + +* Ironed out last problems with times and date (should be quite solid now.) + +* Fixed problems with some arrays. + +* Slightly better ZPsycopgDA (no more double connection objects in the menu + and other minor fixes.) + +* ProgrammingError exceptions now have three extra attributes: .cursor + (it is possible to access the query that caused the exception using + error.cursor.query), .pgerror and .pgcode (PostgreSQL original error + text and code.) + +* The build system uses pg_config when available. + +* Documentation in the doc/ directory! (With many kudos to piro.) + +What's new in psycopg 2.0 beta 6 +-------------------------------- + +* Support for named cursors (see examples/fetch.py). + +* Safer parsing of time intervals. + +* Better parsing of times and dates, no more locale problems. + +* Should now play well with py2exe and similar tools. + +* The "decimal" module is now used if available under Python 2.3. + +What's new in psycopg 2.0 beta 5 +-------------------------------- + +* Fixed all known bugs. + +* The initial isolation level is now read from the server and + .set_isolation_level() now takes values defined in psycopg2.extensions. + +* .callproc() implemented as a SELECT of the given procedure. + +* Better docstrings for a few functions/methods. + +* Some time-related functions like psycopg2.TimeFromTicks() now take the + local timezone into account. Also a tzinfo object (as per datetime module + specifications) can be passed to the psycopg2.Time and psycopg2.Datetime + constructors. + +* All classes have been renamed to exist in the psycopg2._psycopg module, + to fix problems with automatic documentation generators like epydoc. + +* NOTIFY is correctly trapped (see examples/notify.py for example code.) + +What's new in psycopg 2.0 beta 4 +-------------------------------- + +* psycopg module is now named psycopg2. + +* No more segfaults when a UNICODE query can't be converted to the + backend encoding. + +* No more segfaults on empty queries. + +* psycopg2.connect() now takes an integer for the port keyword parameter. + +* "python setup.py bdist_rpm" now works. + +* Fixed lots of small bugs, see ChangeLog for details. + +What's new in psycopg 2.0 beta 3 +-------------------------------- + +* ZPsycopgDA now works (except table browsing.) + +* psycopg build again on Python 2.2. + +What's new in psycopg 2.0 beta 2 +-------------------------------- + +* Fixed ZPsycopgDA version check (ZPsycopgDA can now be imported in + Zope.) + +* psycopg.extras.DictRow works even after a new query on the generating + cursor. + +* Better setup.py for win32 (should build with MSCV or mingw.) + +* Generic fixed and memory leaks plugs. + +What's new in psycopg 2.0 beta 1 +-------------------------------- + +* Officially in beta (i.e., no new features will be added.) + +* Array support: list objects can be passed as bound variables and are + correctly returned for array columns. + +* Added the psycopg.psycopg1 compatibility module (if you want instant + psycopg 1 compatibility just "from psycopg import psycopg1 as psycopg".) + +* Complete support for BYTEA columns and buffer objects. + +* Added error codes to error messages. + +* The AsIs adapter is now exported by default (also Decimal objects are + adapted using the AsIs adapter (when str() is called on them they + already format themselves using the right precision and scale.) + +* The connect() function now takes "connection_factory" instead of + "factory" as keyword argument. + +* New setup.py code to build on win32 using mingw and better error + messages on missing datetime headers, + +* Internal changes that allow much better user-defined type casters. + +* A lot of bugfixes (binary, datetime, 64 bit arches, GIL, .executemany()) + +What's new in psycopg 1.99.13 +----------------------------- + +* Added missing .executemany() method. + +* Optimized type cast from PostgreSQL to Python (psycopg should be even + faster than before.) + +What's new in psycopg 1.99.12 +----------------------------- + +* .rowcount should be ok and in sync with psycopg 1. + +* Implemented the new COPY FROM/COPY TO code when connection to the + backend using libpq protocol 3 (this also removes all asprintf calls: + build on win32 works again.) A protocol 3-enabled psycopg *can* + connect to an old protocol 2 database and will detect it and use the + right code. + +* getquoted() called for real by the mogrification code. + +What's new in psycopg 1.99.11 +----------------------------- + +* 'cursor' argument in .cursor() connection method renamed to + 'cursor_factory'. + +* changed 'tuple_factory' cursor attribute name to 'row_factory'. + +* the .cursor attribute is gone and connections and cursors are propely + gc-managed. + +* fixes to the async core. + +What's new in psycopg 1.99.10 +----------------------------- + +* The adapt() function now fully supports the adaptation protocol + described in PEP 246. Note that the adapters registry now is indexed + by (type, protocol) and not by type alone. Change your adapters + accordingly. + +* More configuration options moved from setup.py to setup.cfg. + +* Fixed two memory leaks: one in cursor deallocation and one in row + fetching (.fetchXXX() methods.) + +What's new in psycopg 1.99.9 +---------------------------- + +* Added simple pooling code (psycopg.pool module); see the reworked + examples/threads.py for example code. + +* Added DECIMAL typecaster to convert postgresql DECIMAL and NUMERIC + types (i.e, all types with an OID of NUMERICOID.) Note that the + DECIMAL typecaster does not set scale and precision on the created + objects but uses Python defaults. + +* ZPsycopgDA back in and working using the new pooling code. + +* Isn't that enough? :) + +What's new in psycopg 1.99.8 +---------------------------- + +* added support for UNICODE queries. + +* added UNICODE typecaster; to activate it just do: + + psycopg.extensions.register_type(psycopg.extensions.UNICODE) + + Note that the UNICODE typecaster override the STRING one, so it is + not activated by default. + +* cursors now really support the iterator protocol. + +* solved the rounding errors in time conversions. + +* now cursors support .fileno() and .isready() methods, to be used in + select() calls. + +* .copy_from() and .copy_in() methods are back in (still using the old + protocol, will be updated to use new one in next releasae.) + +* fixed memory corruption bug reported on win32 platform. + +What's new in psycopg 1.99.7 +---------------------------- + +* added support for tuple factories in cursor objects (removed factory + argument in favor of a .tuple_factory attribute on the cursor object); + see the new module psycopg.extras for a cursor (DictCursor) that + return rows as objects that support indexing both by position and + column name. + +* added support for tzinfo objects in datetime.timestamp objects: the + PostgreSQL type "timestamp with time zone" is converted to + datetime.timestamp with a FixedOffsetTimezone initialized as necessary. + +What's new in psycopg 1.99.6 +---------------------------- + +* sslmode parameter from 1.1.x + +* various datetime conversion improvements. + +* now psycopg should compile without mx or without native datetime + (not both, obviously.) + +* included various win32/MSVC fixes (pthread.h changes, winsock2 + library, include path in setup.py, etc.) + +* ported interval fixes from 1.1.14/1.1.15. + +* the last query executed by a cursor is now available in the + .query attribute. + +* conversion of unicode strings to backend encoding now uses a table + (that still need to be filled.) + +* cursors now have a .mogrify() method that return the query string + instead of executing it. + +* connection objects now have a .dsn read-only attribute that holds the + connection string. + +* moved psycopg C module to _psycopg and made psycopg a python module: + this allows for a neat separation of DBAPI-2.0 functionality and psycopg + extensions; the psycopg namespace will be also used to provide + python-only extensions (like the pooling code, some ZPsycopgDA support + functions and the like.) + +What's new in psycopg 1.99.3 +---------------------------- + +* added support for python 2.3 datetime types (both ways) and made datetime + the default set of typecasters when available. + +* added example: dt.py. + +What's new in psycopg 1.99.3 +---------------------------- + +* initial working support for unicode bound variables: UTF-8 and latin-1 + backend encodings are natively supported (and the encoding.py example even + works!) + +* added .set_client_encoding() method on the connection object. + +* added examples: encoding.py, binary.py, lastrowid.py. + +What's new in psycopg 1.99.2 +---------------------------- + +* better typecasting: + - DateTimeDelta used for postgresql TIME (merge from 1.1) + - BYTEA now is converted to a real buffer object, not to a string + +* buffer objects are now adapted into Binary objects automatically. + +* ported scroll method from 1.1 (DBAPI-2.0 extension for cursors) + +* initial support for some DBAPI-2.0 extensions: + - .rownumber attribute for cursors + - .connection attribute for cursors + - .next() and .__iter__() methods to have cursors support the iterator + protocol + - all exception objects are exported to the connection object + +What's new in psycopg 1.99.1 +---------------------------- + +* implemented microprotocols to adapt arbitrary types to the interface used by + psycopg to bind variables in execute; + +* moved qstring, pboolean and mxdatetime to the new adapter layout (binary is + still missing; python 2.3 datetime needs to be written). + + +What's new in psycopg 1.99.0 +---------------------------- + +* reorganized the whole source tree; + +* async core is in place; + +* splitted QuotedString objects from mx stuff; + +* dropped autotools and moved to pythonic setup.py (needs work.) diff -Nru psycopg2-2.0.13/PKG-INFO psycopg2-2.4.5/PKG-INFO --- psycopg2-2.0.13/PKG-INFO 2009-10-07 16:28:55.000000000 +0000 +++ psycopg2-2.4.5/PKG-INFO 2012-03-28 21:17:27.000000000 +0000 @@ -1,30 +1,29 @@ -Metadata-Version: 1.0 +Metadata-Version: 1.1 Name: psycopg2 -Version: 2.0.13 +Version: 2.4.5 Summary: Python-PostgreSQL Database Adapter -Home-page: http://initd.org/tracker/psycopg +Home-page: http://initd.org/psycopg/ Author: Federico Di Gregorio Author-email: fog@initd.org License: GPL with exceptions or ZPL -Download-URL: http://initd.org/pub/software/psycopg2 -Description: psycopg is a PostgreSQL database adapter for the Python programming - language. This is version 2, a complete rewrite of the original code to - provide new-style classes for connection and cursor objects and other sweet - candies. Like the original, psycopg 2 was written with the aim of being - very small and fast, and stable as a rock. +Download-URL: http://initd.org/psycopg/tarballs/PSYCOPG-2-4/psycopg2-2.4.5.tar.gz +Description: psycopg2 is a PostgreSQL database adapter for the Python programming + language. psycopg2 was written with the aim of being very small and fast, + and stable as a rock. - psycopg is different from the other database adapter because it was + psycopg2 is different from the other database adapter because it was designed for heavily multi-threaded applications that create and destroy lots of cursors and make a conspicuous number of concurrent INSERTs or - UPDATEs. psycopg 2 also provide full asycronous operations for the really - brave programmer. + UPDATEs. psycopg2 also provide full asycronous operations and support + for coroutine libraries. Platform: any -Classifier: Development Status :: 4 - Beta +Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers -Classifier: License :: OSI Approved :: GNU General Public License (GPL) +Classifier: License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL) Classifier: License :: OSI Approved :: Zope Public License Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: C Classifier: Programming Language :: SQL Classifier: Topic :: Database diff -Nru psycopg2-2.0.13/psycopg/adapter_asis.c psycopg2-2.4.5/psycopg/adapter_asis.c --- psycopg2-2.0.13/psycopg/adapter_asis.c 2009-08-09 12:51:14.000000000 +0000 +++ psycopg2-2.4.5/psycopg/adapter_asis.c 2011-02-27 12:03:48.000000000 +0000 @@ -1,55 +1,66 @@ /* adapter_asis.c - adapt types as they are * - * Copyright (C) 2003-2004 Federico Di Gregorio + * Copyright (C) 2003-2010 Federico Di Gregorio * * This file is part of psycopg. * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2, - * or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * psycopg2 is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * In addition, as a special exception, the copyright holders give + * permission to link this program with the OpenSSL library (or with + * modified versions of OpenSSL that use the same license as OpenSSL), + * and distribute linked combinations including the two. + * + * You must obey the GNU Lesser General Public License in all respects for + * all of the code used other than OpenSSL. + * + * psycopg2 is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. */ -#define PY_SSIZE_T_CLEAN -#include -#include -#include -#include - #define PSYCOPG_MODULE -#include "psycopg/config.h" -#include "psycopg/python.h" #include "psycopg/psycopg.h" + #include "psycopg/adapter_asis.h" #include "psycopg/microprotocols_proto.h" +#include + + /** the AsIs object **/ static PyObject * -asis_str(asisObject *self) +asis_getquoted(asisObject *self, PyObject *args) { + PyObject *rv; if (self->wrapped == Py_None) { - return PyString_FromString("NULL"); + Py_INCREF(psyco_null); + rv = psyco_null; } else { - return PyObject_Str(self->wrapped); + rv = PyObject_Str(self->wrapped); +#if PY_MAJOR_VERSION > 2 + /* unicode to bytes in Py3 */ + if (rv) { + PyObject *tmp = PyUnicode_AsUTF8String(rv); + Py_DECREF(rv); + rv = tmp; + } +#endif } + + return rv; } static PyObject * -asis_getquoted(asisObject *self, PyObject *args) +asis_str(asisObject *self) { - if (!PyArg_ParseTuple(args, "")) return NULL; - return asis_str(self); + return psycopg_ensure_text(asis_getquoted(self, NULL)); } static PyObject * @@ -73,14 +84,14 @@ /* object member list */ static struct PyMemberDef asisObject_members[] = { - {"adapted", T_OBJECT, offsetof(asisObject, wrapped), RO}, + {"adapted", T_OBJECT, offsetof(asisObject, wrapped), READONLY}, {NULL} }; /* object method table */ static PyMethodDef asisObject_methods[] = { - {"getquoted", (PyCFunction)asis_getquoted, METH_VARARGS, + {"getquoted", (PyCFunction)asis_getquoted, METH_NOARGS, "getquoted() -> wrapped object value as SQL-quoted string"}, {"__conform__", (PyCFunction)asis_conform, METH_VARARGS, NULL}, {NULL} /* Sentinel */ @@ -93,7 +104,7 @@ { Dprintf("asis_setup: init asis object at %p, refcnt = " FORMAT_CODE_PY_SSIZE_T, - self, ((PyObject *)self)->ob_refcnt + self, Py_REFCNT(self) ); Py_INCREF(obj); @@ -101,7 +112,7 @@ Dprintf("asis_setup: good asis object at %p, refcnt = " FORMAT_CODE_PY_SSIZE_T, - self, ((PyObject *)self)->ob_refcnt + self, Py_REFCNT(self) ); return 0; } @@ -122,10 +133,10 @@ Dprintf("asis_dealloc: deleted asis object at %p, refcnt = " FORMAT_CODE_PY_SSIZE_T, - obj, obj->ob_refcnt + obj, Py_REFCNT(obj) ); - obj->ob_type->tp_free(obj); + Py_TYPE(obj)->tp_free(obj); } static int @@ -164,8 +175,7 @@ "AsIs(str) -> new AsIs adapter object" PyTypeObject asisType = { - PyObject_HEAD_INIT(NULL) - 0, + PyVarObject_HEAD_INIT(NULL, 0) "psycopg2._psycopg.AsIs", sizeof(asisObject), 0, @@ -237,5 +247,5 @@ if (!PyArg_ParseTuple(args, "O", &obj)) return NULL; - return PyObject_CallFunction((PyObject *)&asisType, "O", obj); + return PyObject_CallFunctionObjArgs((PyObject *)&asisType, obj, NULL); } diff -Nru psycopg2-2.0.13/psycopg/adapter_asis.h psycopg2-2.4.5/psycopg/adapter_asis.h --- psycopg2-2.0.13/psycopg/adapter_asis.h 2009-04-26 12:09:21.000000000 +0000 +++ psycopg2-2.4.5/psycopg/adapter_asis.h 2011-02-06 15:58:34.000000000 +0000 @@ -1,32 +1,31 @@ /* adapter_asis.h - definition for the psycopg AsIs type wrapper * - * Copyright (C) 2003-2005 Federico Di Gregorio + * Copyright (C) 2003-2010 Federico Di Gregorio * * This file is part of psycopg. * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2, - * or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * psycopg2 is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * In addition, as a special exception, the copyright holders give + * permission to link this program with the OpenSSL library (or with + * modified versions of OpenSSL that use the same license as OpenSSL), + * and distribute linked combinations including the two. + * + * You must obey the GNU Lesser General Public License in all respects for + * all of the code used other than OpenSSL. + * + * psycopg2 is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. */ #ifndef PSYCOPG_ASIS_H #define PSYCOPG_ASIS_H 1 -#define PY_SSIZE_T_CLEAN -#include - -#include "psycopg/config.h" - #ifdef __cplusplus extern "C" { #endif diff -Nru psycopg2-2.0.13/psycopg/adapter_binary.c psycopg2-2.4.5/psycopg/adapter_binary.c --- psycopg2-2.0.13/psycopg/adapter_binary.c 2009-08-09 12:51:14.000000000 +0000 +++ psycopg2-2.4.5/psycopg/adapter_binary.c 2012-03-28 21:09:15.000000000 +0000 @@ -1,43 +1,40 @@ /* adapter_binary.c - Binary objects * - * Copyright (C) 2003 Federico Di Gregorio + * Copyright (C) 2003-2010 Federico Di Gregorio * * This file is part of psycopg. * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2, - * or (at your option) any later version. + * psycopg2 is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * In addition, as a special exception, the copyright holders give + * permission to link this program with the OpenSSL library (or with + * modified versions of OpenSSL that use the same license as OpenSSL), + * and distribute linked combinations including the two. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You must obey the GNU Lesser General Public License in all respects for + * all of the code used other than OpenSSL. + * + * psycopg2 is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. */ -#define PY_SSIZE_T_CLEAN -#include -#include -#include - -#include -#include - #define PSYCOPG_MODULE -#include "psycopg/config.h" -#include "psycopg/python.h" #include "psycopg/psycopg.h" -#include "psycopg/connection.h" + #include "psycopg/adapter_binary.h" #include "psycopg/microprotocols_proto.h" +#include "psycopg/connection.h" + +#include + /** the quoting code */ -#ifndef PSYCOPG_OWN_QUOTING static unsigned char * binary_escape(unsigned char *from, size_t from_length, size_t *to_length, PGconn *conn) @@ -49,158 +46,118 @@ #endif return PQescapeBytea(from, from_length, to_length); } -#else -static unsigned char * -binary_escape(unsigned char *from, size_t from_length, - size_t *to_length, PGconn *conn) -{ - unsigned char *quoted, *chptr, *newptr; - size_t i, space, new_space; - - space = from_length + 2; - - Py_BEGIN_ALLOW_THREADS; - - quoted = (unsigned char*)calloc(space, sizeof(char)); - if (quoted == NULL) return NULL; - - chptr = quoted; - - for (i = 0; i < from_length; i++) { - if (chptr - quoted > space - 6) { - new_space = space * ((space) / (i + 1)) + 2 + 6; - if (new_space - space < 1024) space += 1024; - else space = new_space; - newptr = (unsigned char *)realloc(quoted, space); - if (newptr == NULL) { - free(quoted); - return NULL; - } - /* chptr has to be moved to the new location*/ - chptr = newptr + (chptr - quoted); - quoted = newptr; - Dprintf("binary_escape: reallocated %i bytes at %p", space,quoted); - } - if (from[i]) { - if (from[i] >= ' ' && from[i] <= '~') { - if (from[i] == '\'') { - *chptr = '\''; - chptr++; - *chptr = '\''; - chptr++; - } - else if (from[i] == '\\') { - memcpy(chptr, "\\\\\\\\", 4); - chptr += 4; - } - else { - /* leave it as it is if ascii printable */ - *chptr = from[i]; - chptr++; - } - } - else { - unsigned char c; - - /* escape to octal notation \nnn */ - *chptr++ = '\\'; - *chptr++ = '\\'; - c = from[i]; - *chptr = ((c >> 6) & 0x07) + 0x30; chptr++; - *chptr = ((c >> 3) & 0x07) + 0x30; chptr++; - *chptr = ( c & 0x07) + 0x30; chptr++; - } - } - else { - /* escape null as \\000 */ - memcpy(chptr, "\\\\000", 5); - chptr += 5; - } - } - *chptr = '\0'; - - Py_END_ALLOW_THREADS; - *to_length = chptr - quoted + 1; - return quoted; -} -#endif +#define HAS_BUFFER (PY_MAJOR_VERSION < 3) +#define HAS_MEMORYVIEW (PY_MAJOR_VERSION > 2 || PY_MINOR_VERSION >= 6) /* binary_quote - do the quote process on plain and unicode strings */ static PyObject * binary_quote(binaryObject *self) { - char *to; - const char *buffer; + char *to = NULL; + const char *buffer = NULL; Py_ssize_t buffer_len; size_t len = 0; + PyObject *rv = NULL; +#if HAS_MEMORYVIEW + Py_buffer view; + int got_view = 0; +#endif + + /* Allow Binary(None) to work */ + if (self->wrapped == Py_None) { + Py_INCREF(psyco_null); + rv = psyco_null; + goto exit; + } /* if we got a plain string or a buffer we escape it and save the buffer */ - if (PyString_Check(self->wrapped) || PyBuffer_Check(self->wrapped)) { - /* escape and build quoted buffer */ - if (PyObject_AsReadBuffer(self->wrapped, (const void **)&buffer, - &buffer_len) < 0) - return NULL; - to = (char *)binary_escape((unsigned char*)buffer, (size_t) buffer_len, - &len, self->conn ? ((connectionObject*)self->conn)->pgconn : NULL); - if (to == NULL) { - PyErr_NoMemory(); - return NULL; +#if HAS_MEMORYVIEW + if (PyObject_CheckBuffer(self->wrapped)) { + if (0 > PyObject_GetBuffer(self->wrapped, &view, PyBUF_CONTIG_RO)) { + goto exit; } + got_view = 1; + buffer = (const char *)(view.buf); + buffer_len = view.len; + } +#endif - if (len > 0) - self->buffer = PyString_FromFormat( - (self->conn && ((connectionObject*)self->conn)->equote) - ? "E'%s'" : "'%s'" , to); - else - self->buffer = PyString_FromString("''"); +#if HAS_BUFFER + if (!buffer && (Bytes_Check(self->wrapped) || PyBuffer_Check(self->wrapped))) { + if (PyObject_AsReadBuffer(self->wrapped, (const void **)&buffer, + &buffer_len) < 0) { + goto exit; + } + } +#endif - PQfreemem(to); + if (!buffer) { + goto exit; } - /* if the wrapped object is not a string or a buffer, this is an error */ - else { - PyErr_SetString(PyExc_TypeError, "can't escape non-string object"); - return NULL; + /* escape and build quoted buffer */ + + to = (char *)binary_escape((unsigned char*)buffer, (size_t)buffer_len, + &len, self->conn ? ((connectionObject*)self->conn)->pgconn : NULL); + if (to == NULL) { + PyErr_NoMemory(); + goto exit; } - return self->buffer; + if (len > 0) + rv = Bytes_FromFormat( + (self->conn && ((connectionObject*)self->conn)->equote) + ? "E'%s'::bytea" : "'%s'::bytea" , to); + else + rv = Bytes_FromString("''::bytea"); + +exit: + if (to) { PQfreemem(to); } +#if HAS_MEMORYVIEW + if (got_view) { PyBuffer_Release(&view); } +#endif + + /* if the wrapped object is not bytes or a buffer, this is an error */ + if (!rv && !PyErr_Occurred()) { + PyErr_Format(PyExc_TypeError, "can't escape %s to binary", + Py_TYPE(self->wrapped)->tp_name); + } + + return rv; } /* binary_str, binary_getquoted - return result of quoting */ static PyObject * -binary_str(binaryObject *self) +binary_getquoted(binaryObject *self, PyObject *args) { if (self->buffer == NULL) { - binary_quote(self); + self->buffer = binary_quote(self); } Py_XINCREF(self->buffer); return self->buffer; } static PyObject * -binary_getquoted(binaryObject *self, PyObject *args) +binary_str(binaryObject *self) { - if (!PyArg_ParseTuple(args, "")) return NULL; - return binary_str(self); + return psycopg_ensure_text(binary_getquoted(self, NULL)); } static PyObject * binary_prepare(binaryObject *self, PyObject *args) { - connectionObject *conn; + PyObject *conn; - if (!PyArg_ParseTuple(args, "O", &conn)) + if (!PyArg_ParseTuple(args, "O!", &connectionType, &conn)) return NULL; Py_XDECREF(self->conn); - if (conn) { - self->conn = (PyObject*)conn; - Py_INCREF(self->conn); - } + self->conn = conn; + Py_INCREF(self->conn); Py_INCREF(Py_None); return Py_None; @@ -227,15 +184,15 @@ /* object member list */ static struct PyMemberDef binaryObject_members[] = { - {"adapted", T_OBJECT, offsetof(binaryObject, wrapped), RO}, - {"buffer", T_OBJECT, offsetof(binaryObject, buffer), RO}, + {"adapted", T_OBJECT, offsetof(binaryObject, wrapped), READONLY}, + {"buffer", T_OBJECT, offsetof(binaryObject, buffer), READONLY}, {NULL} }; /* object method table */ static PyMethodDef binaryObject_methods[] = { - {"getquoted", (PyCFunction)binary_getquoted, METH_VARARGS, + {"getquoted", (PyCFunction)binary_getquoted, METH_NOARGS, "getquoted() -> wrapped object value as SQL-quoted binary string"}, {"prepare", (PyCFunction)binary_prepare, METH_VARARGS, "prepare(conn) -> prepare for binary encoding using conn"}, @@ -250,7 +207,7 @@ { Dprintf("binary_setup: init binary object at %p, refcnt = " FORMAT_CODE_PY_SSIZE_T, - self, ((PyObject *)self)->ob_refcnt + self, Py_REFCNT(self) ); self->buffer = NULL; @@ -260,7 +217,7 @@ Dprintf("binary_setup: good binary object at %p, refcnt = " FORMAT_CODE_PY_SSIZE_T, - self, ((PyObject *)self)->ob_refcnt); + self, Py_REFCNT(self)); return 0; } @@ -286,10 +243,10 @@ Dprintf("binary_dealloc: deleted binary object at %p, refcnt = " FORMAT_CODE_PY_SSIZE_T, - obj, obj->ob_refcnt + obj, Py_REFCNT(obj) ); - obj->ob_type->tp_free(obj); + Py_TYPE(obj)->tp_free(obj); } static int @@ -327,8 +284,7 @@ "Binary(buffer) -> new binary object" PyTypeObject binaryType = { - PyObject_HEAD_INIT(NULL) - 0, + PyVarObject_HEAD_INIT(NULL, 0) "psycopg2._psycopg.Binary", sizeof(binaryObject), 0, @@ -398,5 +354,5 @@ if (!PyArg_ParseTuple(args, "O", &str)) return NULL; - return PyObject_CallFunction((PyObject *)&binaryType, "O", str); + return PyObject_CallFunctionObjArgs((PyObject *)&binaryType, str, NULL); } diff -Nru psycopg2-2.0.13/psycopg/adapter_binary.h psycopg2-2.4.5/psycopg/adapter_binary.h --- psycopg2-2.0.13/psycopg/adapter_binary.h 2009-04-26 12:09:21.000000000 +0000 +++ psycopg2-2.4.5/psycopg/adapter_binary.h 2011-02-06 15:58:34.000000000 +0000 @@ -1,33 +1,31 @@ /* adapter_binary.h - definition for the Binary type * - * Copyright (C) 2003 Federico Di Gregorio + * Copyright (C) 2003-2010 Federico Di Gregorio * * This file is part of psycopg. * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2, - * or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * psycopg2 is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * In addition, as a special exception, the copyright holders give + * permission to link this program with the OpenSSL library (or with + * modified versions of OpenSSL that use the same license as OpenSSL), + * and distribute linked combinations including the two. + * + * You must obey the GNU Lesser General Public License in all respects for + * all of the code used other than OpenSSL. + * + * psycopg2 is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. */ #ifndef PSYCOPG_BINARY_H #define PSYCOPG_BINARY_H 1 -#define PY_SSIZE_T_CLEAN -#include -#include - -#include "psycopg/config.h" - #ifdef __cplusplus extern "C" { #endif diff -Nru psycopg2-2.0.13/psycopg/adapter_datetime.c psycopg2-2.4.5/psycopg/adapter_datetime.c --- psycopg2-2.0.13/psycopg/adapter_datetime.c 2009-08-09 12:51:14.000000000 +0000 +++ psycopg2-2.4.5/psycopg/adapter_datetime.c 2012-03-28 21:09:15.000000000 +0000 @@ -1,89 +1,132 @@ /* adapter_datetime.c - python date/time objects * - * Copyright (C) 2004 Federico Di Gregorio + * Copyright (C) 2003-2010 Federico Di Gregorio * * This file is part of psycopg. * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2, - * or (at your option) any later version. + * psycopg2 is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * In addition, as a special exception, the copyright holders give + * permission to link this program with the OpenSSL library (or with + * modified versions of OpenSSL that use the same license as OpenSSL), + * and distribute linked combinations including the two. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You must obey the GNU Lesser General Public License in all respects for + * all of the code used other than OpenSSL. + * + * psycopg2 is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. */ -#define PY_SSIZE_T_CLEAN -#include -#include -#include -#include - -#include -#include - #define PSYCOPG_MODULE -#include "psycopg/config.h" -#include "psycopg/python.h" #include "psycopg/psycopg.h" + #include "psycopg/adapter_datetime.h" #include "psycopg/microprotocols_proto.h" +#include + +#include +#include -/* the pointer to the datetime module API is initialized by the module init - code, we just need to grab it */ -extern HIDDEN PyObject* pyDateTimeModuleP; -extern HIDDEN PyObject *pyDateTypeP; -extern HIDDEN PyObject *pyTimeTypeP; -extern HIDDEN PyObject *pyDateTimeTypeP; -extern HIDDEN PyObject *pyDeltaTypeP; extern HIDDEN PyObject *pyPsycopgTzModule; extern HIDDEN PyObject *pyPsycopgTzLOCAL; +int +psyco_adapter_datetime_init(void) +{ + Dprintf("psyco_adapter_datetime_init: datetime init"); + + PyDateTime_IMPORT; + + if (!PyDateTimeAPI) { + PyErr_SetString(PyExc_ImportError, "datetime initialization failed"); + return -1; + } + return 0; +} + /* datetime_str, datetime_getquoted - return result of quoting */ static PyObject * -pydatetime_str(pydatetimeObject *self) +_pydatetime_string_date_time(pydatetimeObject *self) { - if (self->type <= PSYCO_DATETIME_TIMESTAMP) { - PyObject *res = NULL; - PyObject *iso = PyObject_CallMethod(self->wrapped, "isoformat", NULL); - if (iso) { - res = PyString_FromFormat("'%s'", PyString_AsString(iso)); - Py_DECREF(iso); - } - return res; + PyObject *rv = NULL; + PyObject *iso = NULL; + PyObject *tz; + + /* Select the right PG type to cast into. */ + char *fmt = NULL; + switch (self->type) { + case PSYCO_DATETIME_TIME: + fmt = "'%s'::time"; + break; + case PSYCO_DATETIME_DATE: + fmt = "'%s'::date"; + break; + case PSYCO_DATETIME_TIMESTAMP: + tz = PyObject_GetAttrString(self->wrapped, "tzinfo"); + if (!tz) { goto error; } + fmt = (tz == Py_None) ? "'%s'::timestamp" : "'%s'::timestamptz"; + Py_DECREF(tz); + break; } - else { - PyDateTime_Delta *obj = (PyDateTime_Delta*)self->wrapped; - char buffer[8]; - int i; - int a = obj->microseconds; - - for (i=0; i < 6 ; i++) { - buffer[5-i] = '0' + (a % 10); - a /= 10; - } - buffer[6] = '\0'; + if (!(iso = psycopg_ensure_bytes( + PyObject_CallMethod(self->wrapped, "isoformat", NULL)))) { + goto error; + } + + rv = Bytes_FromFormat(fmt, Bytes_AsString(iso)); + + Py_DECREF(iso); + return rv; + +error: + Py_XDECREF(iso); + return rv; +} + +static PyObject * +_pydatetime_string_delta(pydatetimeObject *self) +{ + PyDateTime_Delta *obj = (PyDateTime_Delta*)self->wrapped; + + char buffer[8]; + int i; + int a = obj->microseconds; - return PyString_FromFormat("'%d days %d.%s seconds'", - obj->days, obj->seconds, buffer); + for (i=0; i < 6 ; i++) { + buffer[5-i] = '0' + (a % 10); + a /= 10; } + buffer[6] = '\0'; + + return Bytes_FromFormat("'%d days %d.%s seconds'::interval", + obj->days, obj->seconds, buffer); } static PyObject * pydatetime_getquoted(pydatetimeObject *self, PyObject *args) { - if (!PyArg_ParseTuple(args, "")) return NULL; - return pydatetime_str(self); + if (self->type <= PSYCO_DATETIME_TIMESTAMP) { + return _pydatetime_string_date_time(self); + } + else { + return _pydatetime_string_delta(self); + } +} + +static PyObject * +pydatetime_str(pydatetimeObject *self) +{ + return psycopg_ensure_text(pydatetime_getquoted(self, NULL)); } static PyObject * @@ -107,15 +150,15 @@ /* object member list */ static struct PyMemberDef pydatetimeObject_members[] = { - {"adapted", T_OBJECT, offsetof(pydatetimeObject, wrapped), RO}, - {"type", T_INT, offsetof(pydatetimeObject, type), RO}, + {"adapted", T_OBJECT, offsetof(pydatetimeObject, wrapped), READONLY}, + {"type", T_INT, offsetof(pydatetimeObject, type), READONLY}, {NULL} }; /* object method table */ static PyMethodDef pydatetimeObject_methods[] = { - {"getquoted", (PyCFunction)pydatetime_getquoted, METH_VARARGS, + {"getquoted", (PyCFunction)pydatetime_getquoted, METH_NOARGS, "getquoted() -> wrapped object value as SQL date/time"}, {"__conform__", (PyCFunction)pydatetime_conform, METH_VARARGS, NULL}, {NULL} /* Sentinel */ @@ -128,7 +171,7 @@ { Dprintf("pydatetime_setup: init datetime object at %p, refcnt = " FORMAT_CODE_PY_SSIZE_T, - self, ((PyObject *)self)->ob_refcnt); + self, Py_REFCNT(self)); self->type = type; Py_INCREF(obj); @@ -136,7 +179,7 @@ Dprintf("pydatetime_setup: good pydatetime object at %p, refcnt = " FORMAT_CODE_PY_SSIZE_T, - self, ((PyObject *)self)->ob_refcnt); + self, Py_REFCNT(self)); return 0; } @@ -157,9 +200,9 @@ Py_CLEAR(self->wrapped); Dprintf("mpydatetime_dealloc: deleted pydatetime object at %p, " - "refcnt = " FORMAT_CODE_PY_SSIZE_T, obj, obj->ob_refcnt); + "refcnt = " FORMAT_CODE_PY_SSIZE_T, obj, Py_REFCNT(obj)); - obj->ob_type->tp_free(obj); + Py_TYPE(obj)->tp_free(obj); } static int @@ -199,8 +242,7 @@ "datetime(datetime, type) -> new datetime wrapper object" PyTypeObject pydatetimeType = { - PyObject_HEAD_INIT(NULL) - 0, + PyVarObject_HEAD_INIT(NULL, 0) "psycopg2._psycopg.datetime", sizeof(pydatetimeObject), 0, @@ -275,7 +317,8 @@ if (!PyArg_ParseTuple(args, "iii", &year, &month, &day)) return NULL; - obj = PyObject_CallFunction(pyDateTypeP, "iii", year, month, day); + obj = PyObject_CallFunction((PyObject*)PyDateTimeAPI->DateType, + "iii", year, month, day); if (obj) { res = PyObject_CallFunction((PyObject *)&pydatetimeType, @@ -304,10 +347,10 @@ second = floor(second); if (tzinfo == NULL) - obj = PyObject_CallFunction(pyTimeTypeP, "iiii", + obj = PyObject_CallFunction((PyObject*)PyDateTimeAPI->TimeType, "iiii", hours, minutes, (int)second, (int)round(micro)); else - obj = PyObject_CallFunction(pyTimeTypeP, "iiiiO", + obj = PyObject_CallFunction((PyObject*)PyDateTimeAPI->TimeType, "iiiiO", hours, minutes, (int)second, (int)round(micro), tzinfo); if (obj) { @@ -319,30 +362,25 @@ return res; } -PyObject * -psyco_Timestamp(PyObject *self, PyObject *args) +static PyObject * +_psyco_Timestamp(int year, int month, int day, + int hour, int minute, double second, PyObject *tzinfo) { + double micro; + PyObject *obj; PyObject *res = NULL; - PyObject *tzinfo = NULL; - int year, month, day; - int hour=0, minute=0; /* default to midnight */ - double micro, second=0.0; - - PyObject* obj = NULL; - - if (!PyArg_ParseTuple(args, "lii|iidO", &year, &month, &day, - &hour, &minute, &second, &tzinfo)) - return NULL; micro = (second - floor(second)) * 1000000.0; second = floor(second); if (tzinfo == NULL) - obj = PyObject_CallFunction(pyDateTimeTypeP, "iiiiiii", + obj = PyObject_CallFunction((PyObject*)PyDateTimeAPI->DateTimeType, + "iiiiiii", year, month, day, hour, minute, (int)second, (int)round(micro)); else - obj = PyObject_CallFunction(pyDateTimeTypeP, "iiiiiiiO", + obj = PyObject_CallFunction((PyObject*)PyDateTimeAPI->DateTimeType, + "iiiiiiiO", year, month, day, hour, minute, (int)second, (int)round(micro), tzinfo); @@ -356,6 +394,21 @@ } PyObject * +psyco_Timestamp(PyObject *self, PyObject *args) +{ + PyObject *tzinfo = NULL; + int year, month, day; + int hour=0, minute=0; /* default to midnight */ + double second=0.0; + + if (!PyArg_ParseTuple(args, "iii|iidO", &year, &month, &day, + &hour, &minute, &second, &tzinfo)) + return NULL; + + return _psyco_Timestamp(year, month, day, hour, minute, second, tzinfo); +} + +PyObject * psyco_DateFromTicks(PyObject *self, PyObject *args) { PyObject *res = NULL; @@ -366,7 +419,7 @@ if (!PyArg_ParseTuple(args, "d", &ticks)) return NULL; - t = (time_t)round(ticks); + t = (time_t)floor(ticks); if (localtime_r(&t, &tm)) { args = Py_BuildValue("iii", tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday); if (args) { @@ -374,6 +427,10 @@ Py_DECREF(args); } } + else { + PyErr_SetString(InterfaceError, "failed localtime call"); + } + return res; } @@ -388,7 +445,7 @@ if (!PyArg_ParseTuple(args,"d", &ticks)) return NULL; - t = (time_t)round(ticks); + t = (time_t)floor(ticks); ticks -= (double)t; if (localtime_r(&t, &tm)) { args = Py_BuildValue("iid", tm.tm_hour, tm.tm_min, @@ -398,6 +455,10 @@ Py_DECREF(args); } } + else { + PyErr_SetString(InterfaceError, "failed localtime call"); + } + return res; } @@ -412,23 +473,18 @@ if (!PyArg_ParseTuple(args, "d", &ticks)) return NULL; - t = (time_t)round(ticks); + t = (time_t)floor(ticks); ticks -= (double)t; if (localtime_r(&t, &tm)) { - PyObject *value = Py_BuildValue("iiiiidO", - tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday, - tm.tm_hour, tm.tm_min, - (double)tm.tm_sec + ticks, + res = _psyco_Timestamp( + tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, + tm.tm_hour, tm.tm_min, (double)tm.tm_sec + ticks, pyPsycopgTzLOCAL); - if (value) { - /* FIXME: not decref'ing the value here is a memory leak - but, on the other hand, if we decref we get a clean nice - segfault (on my 64 bit Python 2.4 box). So this leaks - will stay until after 2.0.7 when we'll try to plug it */ - res = psyco_Timestamp(self, value); - } } - + else { + PyErr_SetString(InterfaceError, "failed localtime call"); + } + return res; } @@ -439,7 +495,7 @@ { PyObject *obj; - if (!PyArg_ParseTuple(args, "O!", pyDateTypeP, &obj)) + if (!PyArg_ParseTuple(args, "O!", PyDateTimeAPI->DateType, &obj)) return NULL; return PyObject_CallFunction((PyObject *)&pydatetimeType, "Oi", obj, @@ -451,7 +507,7 @@ { PyObject *obj; - if (!PyArg_ParseTuple(args, "O!", pyTimeTypeP, &obj)) + if (!PyArg_ParseTuple(args, "O!", PyDateTimeAPI->TimeType, &obj)) return NULL; return PyObject_CallFunction((PyObject *)&pydatetimeType, "Oi", obj, @@ -463,7 +519,7 @@ { PyObject *obj; - if (!PyArg_ParseTuple(args, "O!", pyDateTimeTypeP, &obj)) + if (!PyArg_ParseTuple(args, "O!", PyDateTimeAPI->DateTimeType, &obj)) return NULL; return PyObject_CallFunction((PyObject *)&pydatetimeType, "Oi", obj, @@ -475,7 +531,7 @@ { PyObject *obj; - if (!PyArg_ParseTuple(args, "O!", pyDeltaTypeP, &obj)) + if (!PyArg_ParseTuple(args, "O!", PyDateTimeAPI->DeltaType, &obj)) return NULL; return PyObject_CallFunction((PyObject *)&pydatetimeType, "Oi", obj, diff -Nru psycopg2-2.0.13/psycopg/adapter_datetime.h psycopg2-2.4.5/psycopg/adapter_datetime.h --- psycopg2-2.0.13/psycopg/adapter_datetime.h 2009-04-26 12:09:21.000000000 +0000 +++ psycopg2-2.4.5/psycopg/adapter_datetime.h 2011-06-13 16:53:48.000000000 +0000 @@ -1,32 +1,31 @@ /* adapter_datetime.h - definition for the python date/time types * - * Copyright (C) 2004 Federico Di Gregorio + * Copyright (C) 2003-2010 Federico Di Gregorio * * This file is part of psycopg. * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2, - * or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * psycopg2 is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * In addition, as a special exception, the copyright holders give + * permission to link this program with the OpenSSL library (or with + * modified versions of OpenSSL that use the same license as OpenSSL), + * and distribute linked combinations including the two. + * + * You must obey the GNU Lesser General Public License in all respects for + * all of the code used other than OpenSSL. + * + * psycopg2 is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. */ #ifndef PSYCOPG_DATETIME_H #define PSYCOPG_DATETIME_H 1 -#define PY_SSIZE_T_CLEAN -#include - -#include "psycopg/config.h" - #ifdef __cplusplus extern "C" { #endif @@ -46,6 +45,8 @@ } pydatetimeObject; +HIDDEN int psyco_adapter_datetime_init(void); + /* functions exported to psycopgmodule.c */ #ifdef PSYCOPG_DEFAULT_PYDATETIME diff -Nru psycopg2-2.0.13/psycopg/adapter_list.c psycopg2-2.4.5/psycopg/adapter_list.c --- psycopg2-2.0.13/psycopg/adapter_list.c 2009-08-09 12:51:14.000000000 +0000 +++ psycopg2-2.4.5/psycopg/adapter_list.c 2012-03-28 21:09:15.000000000 +0000 @@ -1,33 +1,31 @@ /* adapter_list.c - python list objects * - * Copyright (C) 2004-2005 Federico Di Gregorio + * Copyright (C) 2004-2010 Federico Di Gregorio * * This file is part of psycopg. * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2, - * or (at your option) any later version. + * psycopg2 is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * In addition, as a special exception, the copyright holders give + * permission to link this program with the OpenSSL library (or with + * modified versions of OpenSSL that use the same license as OpenSSL), + * and distribute linked combinations including the two. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You must obey the GNU Lesser General Public License in all respects for + * all of the code used other than OpenSSL. + * + * psycopg2 is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. */ -#define PY_SSIZE_T_CLEAN -#include -#include -#include - #define PSYCOPG_MODULE -#include "psycopg/config.h" -#include "psycopg/python.h" #include "psycopg/psycopg.h" + #include "psycopg/adapter_list.h" #include "psycopg/microprotocols.h" #include "psycopg/microprotocols_proto.h" @@ -47,19 +45,22 @@ /* empty arrays are converted to NULLs (still searching for a way to insert an empty array in postgresql */ - if (len == 0) return PyString_FromString("'{}'"); + if (len == 0) return Bytes_FromString("'{}'"); tmp = PyTuple_New(len); for (i=0; iwrapped, i); - if (wrapped == Py_None) - quoted = PyString_FromString("NULL"); - else - quoted = microprotocol_getquoted(wrapped, - (connectionObject*)self->connection); - if (quoted == NULL) goto error; + PyObject *wrapped = PyList_GET_ITEM(self->wrapped, i); + if (wrapped == Py_None) { + Py_INCREF(psyco_null); + quoted = psyco_null; + } + else { + quoted = microprotocol_getquoted(wrapped, + (connectionObject*)self->connection); + if (quoted == NULL) goto error; + } /* here we don't loose a refcnt: SET_ITEM does not change the reference count and we are just transferring ownership of the tmp @@ -69,11 +70,11 @@ /* now that we have a tuple of adapted objects we just need to join them and put "ARRAY[] around the result */ - str = PyString_FromString(", "); + str = Bytes_FromString(", "); joined = PyObject_CallMethod(str, "join", "(O)", tmp); if (joined == NULL) goto error; - res = PyString_FromFormat("ARRAY[%s]", PyString_AsString(joined)); + res = Bytes_FromFormat("ARRAY[%s]", Bytes_AsString(joined)); error: Py_XDECREF(tmp); @@ -83,25 +84,23 @@ } static PyObject * -list_str(listObject *self, PyObject *args) +list_str(listObject *self) { - if (!PyArg_ParseTuple(args, "")) return NULL; - return list_quote(self); + return psycopg_ensure_text(list_quote(self)); } static PyObject * list_getquoted(listObject *self, PyObject *args) { - if (!PyArg_ParseTuple(args, "")) return NULL; return list_quote(self); } static PyObject * list_prepare(listObject *self, PyObject *args) { - connectionObject *conn; + PyObject *conn; - if (!PyArg_ParseTuple(args, "O", &conn)) + if (!PyArg_ParseTuple(args, "O!", &connectionType, &conn)) return NULL; /* note that we don't copy the encoding from the connection, but take a @@ -110,7 +109,7 @@ work even without a connection to the backend. */ Py_CLEAR(self->connection); Py_INCREF(conn); - self->connection = (PyObject*)conn; + self->connection = conn; Py_INCREF(Py_None); return Py_None; @@ -137,14 +136,14 @@ /* object member list */ static struct PyMemberDef listObject_members[] = { - {"adapted", T_OBJECT, offsetof(listObject, wrapped), RO}, + {"adapted", T_OBJECT, offsetof(listObject, wrapped), READONLY}, {NULL} }; /* object method table */ static PyMethodDef listObject_methods[] = { - {"getquoted", (PyCFunction)list_getquoted, METH_VARARGS, + {"getquoted", (PyCFunction)list_getquoted, METH_NOARGS, "getquoted() -> wrapped object value as SQL date/time"}, {"prepare", (PyCFunction)list_prepare, METH_VARARGS, "prepare(conn) -> set encoding to conn->encoding"}, @@ -159,7 +158,7 @@ { Dprintf("list_setup: init list object at %p, refcnt = " FORMAT_CODE_PY_SSIZE_T, - self, ((PyObject *)self)->ob_refcnt + self, Py_REFCNT(self) ); if (!PyList_Check(obj)) @@ -174,7 +173,7 @@ Dprintf("list_setup: good list object at %p, refcnt = " FORMAT_CODE_PY_SSIZE_T, - self, ((PyObject *)self)->ob_refcnt + self, Py_REFCNT(self) ); return 0; } @@ -199,9 +198,9 @@ if (self->encoding) free(self->encoding); Dprintf("list_dealloc: deleted list object at %p, " - "refcnt = " FORMAT_CODE_PY_SSIZE_T, obj, obj->ob_refcnt); + "refcnt = " FORMAT_CODE_PY_SSIZE_T, obj, Py_REFCNT(obj)); - obj->ob_type->tp_free(obj); + Py_TYPE(obj)->tp_free(obj); } static int @@ -240,8 +239,7 @@ "List(list) -> new list wrapper object" PyTypeObject listType = { - PyObject_HEAD_INIT(NULL) - 0, + PyVarObject_HEAD_INIT(NULL, 0) "psycopg2._psycopg.List", sizeof(listObject), 0, diff -Nru psycopg2-2.0.13/psycopg/adapter_list.h psycopg2-2.4.5/psycopg/adapter_list.h --- psycopg2-2.0.13/psycopg/adapter_list.h 2009-04-26 12:09:21.000000000 +0000 +++ psycopg2-2.4.5/psycopg/adapter_list.h 2011-02-06 15:58:34.000000000 +0000 @@ -1,32 +1,31 @@ /* adapter_list.h - definition for the python list types * - * Copyright (C) 2004-2005 Federico Di Gregorio + * Copyright (C) 2004-2010 Federico Di Gregorio * * This file is part of psycopg. * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2, - * or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * psycopg2 is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * In addition, as a special exception, the copyright holders give + * permission to link this program with the OpenSSL library (or with + * modified versions of OpenSSL that use the same license as OpenSSL), + * and distribute linked combinations including the two. + * + * You must obey the GNU Lesser General Public License in all respects for + * all of the code used other than OpenSSL. + * + * psycopg2 is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. */ #ifndef PSYCOPG_LIST_H #define PSYCOPG_LIST_H 1 -#define PY_SSIZE_T_CLEAN -#include - -#include "psycopg/config.h" - #ifdef __cplusplus extern "C" { #endif diff -Nru psycopg2-2.0.13/psycopg/adapter_mxdatetime.c psycopg2-2.4.5/psycopg/adapter_mxdatetime.c --- psycopg2-2.0.13/psycopg/adapter_mxdatetime.c 2009-08-09 12:51:14.000000000 +0000 +++ psycopg2-2.4.5/psycopg/adapter_mxdatetime.c 2011-06-13 16:53:48.000000000 +0000 @@ -1,41 +1,52 @@ /* adapter_mxdatetime.c - mx date/time objects * - * Copyright (C) 2003-2004 Federico Di Gregorio + * Copyright (C) 2003-2010 Federico Di Gregorio * * This file is part of psycopg. * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2, - * or (at your option) any later version. + * psycopg2 is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * In addition, as a special exception, the copyright holders give + * permission to link this program with the OpenSSL library (or with + * modified versions of OpenSSL that use the same license as OpenSSL), + * and distribute linked combinations including the two. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You must obey the GNU Lesser General Public License in all respects for + * all of the code used other than OpenSSL. + * + * psycopg2 is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. */ -#define PY_SSIZE_T_CLEAN -#include -#include -#include -#include -#include - #define PSYCOPG_MODULE -#include "psycopg/config.h" -#include "psycopg/python.h" #include "psycopg/psycopg.h" + #include "psycopg/adapter_mxdatetime.h" #include "psycopg/microprotocols_proto.h" -/* the pointer to the mxDateTime API is initialized by the module init code, - we just need to grab it */ -extern HIDDEN mxDateTimeModule_APIObject *mxDateTimeP; +#include +#include + + +/* Return 0 on success, -1 on failure, but don't set an exception */ + +int +psyco_adapter_mxdatetime_init(void) +{ + Dprintf("psyco_adapter_mxdatetime_init: mx.DateTime init"); + + if (mxDateTime_ImportModuleAndAPI()) { + Dprintf("psyco_adapter_mxdatetime_init: mx.DateTime initialization failed"); + PyErr_Clear(); + return -1; + } + return 0; +} /* mxdatetime_str, mxdatetime_getquoted - return result of quoting */ @@ -52,10 +63,10 @@ case PSYCO_MXDATETIME_DATE: dt = (mxDateTimeObject *)self->wrapped; if (dt->year >= 1) - PyOS_snprintf(buf, sizeof(buf) - 1, "'%04ld-%02d-%02d'", + PyOS_snprintf(buf, sizeof(buf) - 1, "'%04ld-%02d-%02d'::date", dt->year, (int)dt->month, (int)dt->day); else - PyOS_snprintf(buf, sizeof(buf) - 1, "'%04ld-%02d-%02d BC'", + PyOS_snprintf(buf, sizeof(buf) - 1, "'%04ld-%02d-%02d BC'::date", 1 - dt->year, (int)dt->month, (int)dt->day); break; @@ -63,12 +74,12 @@ dt = (mxDateTimeObject *)self->wrapped; if (dt->year >= 1) PyOS_snprintf(buf, sizeof(buf) - 1, - "'%04ld-%02d-%02dT%02d:%02d:%09.6f'", + "'%04ld-%02d-%02dT%02d:%02d:%09.6f'::timestamp", dt->year, (int)dt->month, (int)dt->day, (int)dt->hour, (int)dt->minute, dt->second); else PyOS_snprintf(buf, sizeof(buf) - 1, - "'%04ld-%02d-%02dT%02d:%02d:%09.6f BC'", + "'%04ld-%02d-%02dT%02d:%02d:%09.6f BC'::timestamp", 1 - dt->year, (int)dt->month, (int)dt->day, (int)dt->hour, (int)dt->minute, dt->second); break; @@ -81,16 +92,16 @@ time */ dtd = (mxDateTimeDeltaObject *)self->wrapped; if (0 <= dtd->seconds && dtd->seconds < 24*3600) { - PyOS_snprintf(buf, sizeof(buf) - 1, "'%02d:%02d:%09.6f'", + PyOS_snprintf(buf, sizeof(buf) - 1, "'%02d:%02d:%09.6f'::time", (int)dtd->hour, (int)dtd->minute, dtd->second); } else { double ss = dtd->hour*3600.0 + dtd->minute*60.0 + dtd->second; if (dtd->seconds >= 0) - PyOS_snprintf(buf, sizeof(buf) - 1, "'%ld days %.6f seconds'", + PyOS_snprintf(buf, sizeof(buf) - 1, "'%ld days %.6f seconds'::interval", dtd->day, ss); else - PyOS_snprintf(buf, sizeof(buf) - 1, "'-%ld days -%.6f seconds'", + PyOS_snprintf(buf, sizeof(buf) - 1, "'-%ld days -%.6f seconds'::interval", dtd->day, ss); } break; @@ -102,7 +113,6 @@ static PyObject * mxdatetime_getquoted(mxdatetimeObject *self, PyObject *args) { - if (!PyArg_ParseTuple(args, "")) return NULL; return mxdatetime_str(self); } @@ -127,15 +137,15 @@ /* object member list */ static struct PyMemberDef mxdatetimeObject_members[] = { - {"adapted", T_OBJECT, offsetof(mxdatetimeObject, wrapped), RO}, - {"type", T_INT, offsetof(mxdatetimeObject, type), RO}, + {"adapted", T_OBJECT, offsetof(mxdatetimeObject, wrapped), READONLY}, + {"type", T_INT, offsetof(mxdatetimeObject, type), READONLY}, {NULL} }; /* object method table */ static PyMethodDef mxdatetimeObject_methods[] = { - {"getquoted", (PyCFunction)mxdatetime_getquoted, METH_VARARGS, + {"getquoted", (PyCFunction)mxdatetime_getquoted, METH_NOARGS, "getquoted() -> wrapped object value as SQL date/time"}, {"__conform__", (PyCFunction)mxdatetime_conform, METH_VARARGS, NULL}, {NULL} /* Sentinel */ @@ -148,7 +158,7 @@ { Dprintf("mxdatetime_setup: init mxdatetime object at %p, refcnt = " FORMAT_CODE_PY_SSIZE_T, - self, ((PyObject *)self)->ob_refcnt + self, Py_REFCNT(self) ); self->type = type; @@ -157,7 +167,7 @@ Dprintf("mxdatetime_setup: good mxdatetime object at %p, refcnt = " FORMAT_CODE_PY_SSIZE_T, - self, ((PyObject *)self)->ob_refcnt + self, Py_REFCNT(self) ); return 0; } @@ -180,10 +190,10 @@ Dprintf("mxdatetime_dealloc: deleted mxdatetime object at %p, refcnt = " FORMAT_CODE_PY_SSIZE_T, - obj, obj->ob_refcnt + obj, Py_REFCNT(obj) ); - obj->ob_type->tp_free(obj); + Py_TYPE(obj)->tp_free(obj); } static int @@ -223,8 +233,7 @@ "MxDateTime(mx, type) -> new mx.DateTime wrapper object" PyTypeObject mxdatetimeType = { - PyObject_HEAD_INIT(NULL) - 0, + PyVarObject_HEAD_INIT(NULL, 0) "psycopg2._psycopg.MxDateTime", sizeof(mxdatetimeObject), 0, @@ -297,7 +306,7 @@ if (!PyArg_ParseTuple(args, "iii", &year, &month, &day)) return NULL; - mx = mxDateTimeP->DateTime_FromDateAndTime(year, month, day, 0, 0, 0.0); + mx = mxDateTime.DateTime_FromDateAndTime(year, month, day, 0, 0, 0.0); if (mx == NULL) return NULL; res = PyObject_CallFunction((PyObject *)&mxdatetimeType, "Oi", mx, @@ -316,7 +325,7 @@ if (!PyArg_ParseTuple(args, "iid", &hours, &minutes, &seconds)) return NULL; - mx = mxDateTimeP->DateTimeDelta_FromTime(hours, minutes, seconds); + mx = mxDateTime.DateTimeDelta_FromTime(hours, minutes, seconds); if (mx == NULL) return NULL; res = PyObject_CallFunction((PyObject *)&mxdatetimeType, "Oi", mx, @@ -337,7 +346,7 @@ &hour, &minute, &second)) return NULL; - mx = mxDateTimeP->DateTime_FromDateAndTime(year, month, day, + mx = mxDateTime.DateTime_FromDateAndTime(year, month, day, hour, minute, second); if (mx == NULL) return NULL; @@ -356,7 +365,7 @@ if (!PyArg_ParseTuple(args,"d", &ticks)) return NULL; - if (!(mx = mxDateTimeP->DateTime_FromTicks(ticks))) + if (!(mx = mxDateTime.DateTime_FromTicks(ticks))) return NULL; res = PyObject_CallFunction((PyObject *)&mxdatetimeType, "Oi", mx, @@ -374,10 +383,10 @@ if (!PyArg_ParseTuple(args,"d", &ticks)) return NULL; - if (!(dt = mxDateTimeP->DateTime_FromTicks(ticks))) + if (!(dt = mxDateTime.DateTime_FromTicks(ticks))) return NULL; - if (!(mx = mxDateTimeP->DateTimeDelta_FromDaysAndSeconds( + if (!(mx = mxDateTime.DateTimeDelta_FromDaysAndSeconds( 0, ((mxDateTimeObject*)dt)->abstime))) { Py_DECREF(dt); @@ -400,7 +409,7 @@ if (!PyArg_ParseTuple(args, "d", &ticks)) return NULL; - if (!(mx = mxDateTimeP->DateTime_FromTicks(ticks))) + if (!(mx = mxDateTime.DateTime_FromTicks(ticks))) return NULL; res = PyObject_CallFunction((PyObject *)&mxdatetimeType, "Oi", mx, @@ -416,7 +425,7 @@ { PyObject *mx; - if (!PyArg_ParseTuple(args, "O!", mxDateTimeP->DateTime_Type, &mx)) + if (!PyArg_ParseTuple(args, "O!", mxDateTime.DateTime_Type, &mx)) return NULL; return PyObject_CallFunction((PyObject *)&mxdatetimeType, "Oi", mx, @@ -428,7 +437,7 @@ { PyObject *mx; - if (!PyArg_ParseTuple(args, "O!", mxDateTimeP->DateTimeDelta_Type, &mx)) + if (!PyArg_ParseTuple(args, "O!", mxDateTime.DateTimeDelta_Type, &mx)) return NULL; return PyObject_CallFunction((PyObject *)&mxdatetimeType, "Oi", mx, @@ -440,7 +449,7 @@ { PyObject *mx; - if (!PyArg_ParseTuple(args, "O!", mxDateTimeP->DateTime_Type, &mx)) + if (!PyArg_ParseTuple(args, "O!", mxDateTime.DateTime_Type, &mx)) return NULL; return PyObject_CallFunction((PyObject *)&mxdatetimeType, "Oi", mx, @@ -452,7 +461,7 @@ { PyObject *mx; - if (!PyArg_ParseTuple(args, "O!", mxDateTimeP->DateTime_Type, &mx)) + if (!PyArg_ParseTuple(args, "O!", mxDateTime.DateTime_Type, &mx)) return NULL; return PyObject_CallFunction((PyObject *)&mxdatetimeType, "Oi", mx, diff -Nru psycopg2-2.0.13/psycopg/adapter_mxdatetime.h psycopg2-2.4.5/psycopg/adapter_mxdatetime.h --- psycopg2-2.0.13/psycopg/adapter_mxdatetime.h 2009-04-26 12:09:21.000000000 +0000 +++ psycopg2-2.4.5/psycopg/adapter_mxdatetime.h 2011-02-06 15:58:34.000000000 +0000 @@ -1,32 +1,31 @@ /* adapter_mxdatetime.h - definition for the mx date/time types * - * Copyright (C) 2003-2004 Federico Di Gregorio + * Copyright (C) 2003-2010 Federico Di Gregorio * * This file is part of psycopg. * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2, - * or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * psycopg2 is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * In addition, as a special exception, the copyright holders give + * permission to link this program with the OpenSSL library (or with + * modified versions of OpenSSL that use the same license as OpenSSL), + * and distribute linked combinations including the two. + * + * You must obey the GNU Lesser General Public License in all respects for + * all of the code used other than OpenSSL. + * + * psycopg2 is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. */ #ifndef PSYCOPG_MXDATETIME_H #define PSYCOPG_MXDATETIME_H 1 -#define PY_SSIZE_T_CLEAN -#include - -#include "psycopg/config.h" - #ifdef __cplusplus extern "C" { #endif @@ -74,6 +73,8 @@ #endif /* PSYCOPG_DEFAULT_MXDATETIME */ +HIDDEN int psyco_adapter_mxdatetime_init(void); + HIDDEN PyObject *psyco_DateFromMx(PyObject *module, PyObject *args); #define psyco_DateFromMx_doc \ "DateFromMx(mx) -> new date" diff -Nru psycopg2-2.0.13/psycopg/adapter_pboolean.c psycopg2-2.4.5/psycopg/adapter_pboolean.c --- psycopg2-2.0.13/psycopg/adapter_pboolean.c 2009-08-09 12:51:14.000000000 +0000 +++ psycopg2-2.4.5/psycopg/adapter_pboolean.c 2011-02-06 15:58:34.000000000 +0000 @@ -1,65 +1,63 @@ /* adapter_pboolean.c - psycopg boolean type wrapper implementation * - * Copyright (C) 2003-2004 Federico Di Gregorio + * Copyright (C) 2003-2010 Federico Di Gregorio * * This file is part of psycopg. * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2, - * or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * psycopg2 is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * In addition, as a special exception, the copyright holders give + * permission to link this program with the OpenSSL library (or with + * modified versions of OpenSSL that use the same license as OpenSSL), + * and distribute linked combinations including the two. + * + * You must obey the GNU Lesser General Public License in all respects for + * all of the code used other than OpenSSL. + * + * psycopg2 is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. */ -#define PY_SSIZE_T_CLEAN -#include -#include -#include -#include - #define PSYCOPG_MODULE -#include "psycopg/config.h" -#include "psycopg/python.h" #include "psycopg/psycopg.h" + #include "psycopg/adapter_pboolean.h" #include "psycopg/microprotocols_proto.h" +#include + /** the Boolean object **/ static PyObject * -pboolean_str(pbooleanObject *self) +pboolean_getquoted(pbooleanObject *self, PyObject *args) { #ifdef PSYCOPG_NEW_BOOLEAN if (PyObject_IsTrue(self->wrapped)) { - return PyString_FromString("true"); + return Bytes_FromString("true"); } else { - return PyString_FromString("false"); + return Bytes_FromString("false"); } #else if (PyObject_IsTrue(self->wrapped)) { - return PyString_FromString("'t'"); + return Bytes_FromString("'t'"); } else { - return PyString_FromString("'f'"); + return Bytes_FromString("'f'"); } #endif } static PyObject * -pboolean_getquoted(pbooleanObject *self, PyObject *args) +pboolean_str(pbooleanObject *self) { - if (!PyArg_ParseTuple(args, "")) return NULL; - return pboolean_str(self); + return psycopg_ensure_text(pboolean_getquoted(self, NULL)); } static PyObject * @@ -83,14 +81,14 @@ /* object member list */ static struct PyMemberDef pbooleanObject_members[] = { - {"adapted", T_OBJECT, offsetof(pbooleanObject, wrapped), RO}, + {"adapted", T_OBJECT, offsetof(pbooleanObject, wrapped), READONLY}, {NULL} }; /* object method table */ static PyMethodDef pbooleanObject_methods[] = { - {"getquoted", (PyCFunction)pboolean_getquoted, METH_VARARGS, + {"getquoted", (PyCFunction)pboolean_getquoted, METH_NOARGS, "getquoted() -> wrapped object value as SQL-quoted string"}, {"__conform__", (PyCFunction)pboolean_conform, METH_VARARGS, NULL}, {NULL} /* Sentinel */ @@ -103,7 +101,7 @@ { Dprintf("pboolean_setup: init pboolean object at %p, refcnt = " FORMAT_CODE_PY_SSIZE_T, - self, ((PyObject *)self)->ob_refcnt + self, Py_REFCNT(self) ); Py_INCREF(obj); @@ -111,7 +109,7 @@ Dprintf("pboolean_setup: good pboolean object at %p, refcnt = " FORMAT_CODE_PY_SSIZE_T, - self, ((PyObject *)self)->ob_refcnt + self, Py_REFCNT(self) ); return 0; } @@ -134,10 +132,10 @@ Dprintf("pboolean_dealloc: deleted pboolean object at %p, refcnt = " FORMAT_CODE_PY_SSIZE_T, - obj, obj->ob_refcnt + obj, Py_REFCNT(obj) ); - obj->ob_type->tp_free(obj); + Py_TYPE(obj)->tp_free(obj); } static int @@ -177,8 +175,7 @@ "Boolean(str) -> new Boolean adapter object" PyTypeObject pbooleanType = { - PyObject_HEAD_INIT(NULL) - 0, + PyVarObject_HEAD_INIT(NULL, 0) "psycopg2._psycopg.Boolean", sizeof(pbooleanObject), 0, @@ -250,5 +247,5 @@ if (!PyArg_ParseTuple(args, "O", &obj)) return NULL; - return PyObject_CallFunction((PyObject *)&pbooleanType, "O", obj); + return PyObject_CallFunctionObjArgs((PyObject *)&pbooleanType, obj, NULL); } diff -Nru psycopg2-2.0.13/psycopg/adapter_pboolean.h psycopg2-2.4.5/psycopg/adapter_pboolean.h --- psycopg2-2.0.13/psycopg/adapter_pboolean.h 2009-04-26 12:09:21.000000000 +0000 +++ psycopg2-2.4.5/psycopg/adapter_pboolean.h 2011-02-06 15:58:34.000000000 +0000 @@ -1,32 +1,31 @@ /* adapter_pboolean.h - definition for the psycopg boolean type wrapper * - * Copyright (C) 2003-2004 Federico Di Gregorio + * Copyright (C) 2003-2010 Federico Di Gregorio * * This file is part of psycopg. * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2, - * or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * psycopg2 is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * In addition, as a special exception, the copyright holders give + * permission to link this program with the OpenSSL library (or with + * modified versions of OpenSSL that use the same license as OpenSSL), + * and distribute linked combinations including the two. + * + * You must obey the GNU Lesser General Public License in all respects for + * all of the code used other than OpenSSL. + * + * psycopg2 is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. */ #ifndef PSYCOPG_PBOOLEAN_H #define PSYCOPG_PBOOLEAN_H 1 -#define PY_SSIZE_T_CLEAN -#include - -#include "psycopg/config.h" - #ifdef __cplusplus extern "C" { #endif diff -Nru psycopg2-2.0.13/psycopg/adapter_pdecimal.c psycopg2-2.4.5/psycopg/adapter_pdecimal.c --- psycopg2-2.0.13/psycopg/adapter_pdecimal.c 1970-01-01 00:00:00.000000000 +0000 +++ psycopg2-2.4.5/psycopg/adapter_pdecimal.c 2011-06-13 16:53:48.000000000 +0000 @@ -0,0 +1,307 @@ +/* adapter_pdecimal.c - psycopg Decimal type wrapper implementation + * + * Copyright (C) 2003-2010 Federico Di Gregorio + * + * This file is part of psycopg. + * + * psycopg2 is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * In addition, as a special exception, the copyright holders give + * permission to link this program with the OpenSSL library (or with + * modified versions of OpenSSL that use the same license as OpenSSL), + * and distribute linked combinations including the two. + * + * You must obey the GNU Lesser General Public License in all respects for + * all of the code used other than OpenSSL. + * + * psycopg2 is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + */ + +#define PSYCOPG_MODULE +#include "psycopg/psycopg.h" + +#include "psycopg/adapter_pdecimal.h" +#include "psycopg/microprotocols_proto.h" + +#include +#include + + +/** the Decimal object **/ + +static PyObject * +pdecimal_getquoted(pdecimalObject *self, PyObject *args) +{ + PyObject *check, *res = NULL; + check = PyObject_CallMethod(self->wrapped, "is_finite", NULL); + if (check == Py_True) { + if (!(res = PyObject_Str(self->wrapped))) { + goto end; + } + goto output; + } + else if (check) { + res = Bytes_FromString("'NaN'::numeric"); + goto end; + } + + /* is_finite() was introduced 2.5.1 < somewhere <= 2.5.4. + * We assume we are here because we didn't find the method. */ + PyErr_Clear(); + + if (!(check = PyObject_CallMethod(self->wrapped, "_isnan", NULL))) { + goto end; + } + if (PyObject_IsTrue(check)) { + res = Bytes_FromString("'NaN'::numeric"); + goto end; + } + + Py_DECREF(check); + if (!(check = PyObject_CallMethod(self->wrapped, "_isinfinity", NULL))) { + goto end; + } + if (PyObject_IsTrue(check)) { + res = Bytes_FromString("'NaN'::numeric"); + goto end; + } + + /* wrapped is finite */ + if (!(res = PyObject_Str(self->wrapped))) { + goto end; + } + + /* res may be unicode and may suffer for issue #57 */ +output: + +#if PY_MAJOR_VERSION > 2 + /* unicode to bytes in Py3 */ + { + PyObject *tmp = PyUnicode_AsUTF8String(res); + Py_DECREF(res); + if (!(res = tmp)) { + goto end; + } + } +#endif + + if ('-' == Bytes_AS_STRING(res)[0]) { + /* Prepend a space in front of negative numbers (ticket #57) */ + PyObject *tmp; + if (!(tmp = Bytes_FromString(" "))) { + Py_DECREF(res); + res = NULL; + goto end; + } + Bytes_ConcatAndDel(&tmp, res); + if (!(res = tmp)) { + goto end; + } + } + +end: + Py_XDECREF(check); + return res; +} + +static PyObject * +pdecimal_str(pdecimalObject *self) +{ + return psycopg_ensure_text(pdecimal_getquoted(self, NULL)); +} + +static PyObject * +pdecimal_conform(pdecimalObject *self, PyObject *args) +{ + PyObject *res, *proto; + + if (!PyArg_ParseTuple(args, "O", &proto)) return NULL; + + if (proto == (PyObject*)&isqlquoteType) + res = (PyObject*)self; + else + res = Py_None; + + Py_INCREF(res); + return res; +} + +/** the Decimal object */ + +/* object member list */ + +static struct PyMemberDef pdecimalObject_members[] = { + {"adapted", T_OBJECT, offsetof(pdecimalObject, wrapped), READONLY}, + {NULL} +}; + +/* object method table */ + +static PyMethodDef pdecimalObject_methods[] = { + {"getquoted", (PyCFunction)pdecimal_getquoted, METH_NOARGS, + "getquoted() -> wrapped object value as SQL-quoted string"}, + {"__conform__", (PyCFunction)pdecimal_conform, METH_VARARGS, NULL}, + {NULL} /* Sentinel */ +}; + +/* initialization and finalization methods */ + +static int +pdecimal_setup(pdecimalObject *self, PyObject *obj) +{ + Dprintf("pdecimal_setup: init pdecimal object at %p, refcnt = " + FORMAT_CODE_PY_SSIZE_T, + self, Py_REFCNT(self) + ); + + Py_INCREF(obj); + self->wrapped = obj; + + Dprintf("pdecimal_setup: good pdecimal object at %p, refcnt = " + FORMAT_CODE_PY_SSIZE_T, + self, Py_REFCNT(self) + ); + return 0; +} + +static int +pdecimal_traverse(PyObject *obj, visitproc visit, void *arg) +{ + pdecimalObject *self = (pdecimalObject *)obj; + + Py_VISIT(self->wrapped); + return 0; +} + +static void +pdecimal_dealloc(PyObject* obj) +{ + pdecimalObject *self = (pdecimalObject *)obj; + + Py_CLEAR(self->wrapped); + + Dprintf("pdecimal_dealloc: deleted pdecimal object at %p, refcnt = " + FORMAT_CODE_PY_SSIZE_T, + obj, Py_REFCNT(obj) + ); + + Py_TYPE(obj)->tp_free(obj); +} + +static int +pdecimal_init(PyObject *obj, PyObject *args, PyObject *kwds) +{ + PyObject *o; + + if (!PyArg_ParseTuple(args, "O", &o)) + return -1; + + return pdecimal_setup((pdecimalObject *)obj, o); +} + +static PyObject * +pdecimal_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + return type->tp_alloc(type, 0); +} + +static void +pdecimal_del(PyObject* self) +{ + PyObject_GC_Del(self); +} + +static PyObject * +pdecimal_repr(pdecimalObject *self) +{ + return PyString_FromFormat("", + self); +} + + +/* object type */ + +#define pdecimalType_doc \ +"Decimal(str) -> new Decimal adapter object" + +PyTypeObject pdecimalType = { + PyVarObject_HEAD_INIT(NULL, 0) + "psycopg2._psycopg.Decimal", + sizeof(pdecimalObject), + 0, + pdecimal_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + + 0, /*tp_compare*/ + + (reprfunc)pdecimal_repr, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + + 0, /*tp_call*/ + (reprfunc)pdecimal_str, /*tp_str*/ + + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + + Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_HAVE_GC, /*tp_flags*/ + pdecimalType_doc, /*tp_doc*/ + + pdecimal_traverse, /*tp_traverse*/ + 0, /*tp_clear*/ + + 0, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + + /* Attribute descriptor and subclassing stuff */ + + pdecimalObject_methods, /*tp_methods*/ + pdecimalObject_members, /*tp_members*/ + 0, /*tp_getset*/ + 0, /*tp_base*/ + 0, /*tp_dict*/ + + 0, /*tp_descr_get*/ + 0, /*tp_descr_set*/ + 0, /*tp_dictoffset*/ + + pdecimal_init, /*tp_init*/ + 0, /*tp_alloc will be set to PyType_GenericAlloc in module init*/ + pdecimal_new, /*tp_new*/ + (freefunc)pdecimal_del, /*tp_free Low-level free-memory routine */ + 0, /*tp_is_gc For PyObject_IS_GC */ + 0, /*tp_bases*/ + 0, /*tp_mro method resolution order */ + 0, /*tp_cache*/ + 0, /*tp_subclasses*/ + 0 /*tp_weaklist*/ +}; + + +/** module-level functions **/ + +PyObject * +psyco_Decimal(PyObject *module, PyObject *args) +{ + PyObject *obj; + + if (!PyArg_ParseTuple(args, "O", &obj)) + return NULL; + + return PyObject_CallFunctionObjArgs((PyObject *)&pdecimalType, obj, NULL); +} diff -Nru psycopg2-2.0.13/psycopg/adapter_pdecimal.h psycopg2-2.4.5/psycopg/adapter_pdecimal.h --- psycopg2-2.0.13/psycopg/adapter_pdecimal.h 1970-01-01 00:00:00.000000000 +0000 +++ psycopg2-2.4.5/psycopg/adapter_pdecimal.h 2011-02-06 15:58:34.000000000 +0000 @@ -0,0 +1,53 @@ +/* adapter_pdecimal.h - definition for the psycopg Decimal type wrapper + * + * Copyright (C) 2003-2010 Federico Di Gregorio + * + * This file is part of psycopg. + * + * psycopg2 is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * In addition, as a special exception, the copyright holders give + * permission to link this program with the OpenSSL library (or with + * modified versions of OpenSSL that use the same license as OpenSSL), + * and distribute linked combinations including the two. + * + * You must obey the GNU Lesser General Public License in all respects for + * all of the code used other than OpenSSL. + * + * psycopg2 is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + */ + +#ifndef PSYCOPG_PDECIMAL_H +#define PSYCOPG_PDECIMAL_H 1 + +#ifdef __cplusplus +extern "C" { +#endif + +extern HIDDEN PyTypeObject pdecimalType; + +typedef struct { + PyObject_HEAD + + /* this is the real object we wrap */ + PyObject *wrapped; + +} pdecimalObject; + +/* functions exported to psycopgmodule.c */ + +HIDDEN PyObject *psyco_Decimal(PyObject *module, PyObject *args); +#define psyco_Decimal_doc \ + "Decimal(obj) -> new decimal.Decimal value" + +#ifdef __cplusplus +} +#endif + +#endif /* !defined(PSYCOPG_PDECIMAL_H) */ diff -Nru psycopg2-2.0.13/psycopg/adapter_pfloat.c psycopg2-2.4.5/psycopg/adapter_pfloat.c --- psycopg2-2.0.13/psycopg/adapter_pfloat.c 2009-08-09 12:51:14.000000000 +0000 +++ psycopg2-2.4.5/psycopg/adapter_pfloat.c 2011-06-13 16:53:48.000000000 +0000 @@ -1,57 +1,92 @@ /* adapter_float.c - psycopg pfloat type wrapper implementation * - * Copyright (C) 2003-2009 Federico Di Gregorio + * Copyright (C) 2003-2010 Federico Di Gregorio * * This file is part of psycopg. * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2, - * or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * psycopg2 is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * In addition, as a special exception, the copyright holders give + * permission to link this program with the OpenSSL library (or with + * modified versions of OpenSSL that use the same license as OpenSSL), + * and distribute linked combinations including the two. + * + * You must obey the GNU Lesser General Public License in all respects for + * all of the code used other than OpenSSL. + * + * psycopg2 is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. */ -#define PY_SSIZE_T_CLEAN -#include -#include -#include -#include - #define PSYCOPG_MODULE -#include "psycopg/config.h" -#include "psycopg/python.h" #include "psycopg/psycopg.h" + #include "psycopg/adapter_pfloat.h" #include "psycopg/microprotocols_proto.h" +#include +#include + /** the Float object **/ static PyObject * -pfloat_str(pfloatObject *self) +pfloat_getquoted(pfloatObject *self, PyObject *args) { + PyObject *rv; double n = PyFloat_AsDouble(self->wrapped); if (isnan(n)) - return PyString_FromString("'NaN'::float"); - else if (isinf(n)) - return PyString_FromString("'Infinity'::float"); - else - return PyObject_Str(self->wrapped); + rv = Bytes_FromString("'NaN'::float"); + else if (isinf(n)) { + if (n > 0) + rv = Bytes_FromString("'Infinity'::float"); + else + rv = Bytes_FromString("'-Infinity'::float"); + } + else { + if (!(rv = PyObject_Repr(self->wrapped))) { + goto exit; + } + +#if PY_MAJOR_VERSION > 2 + /* unicode to bytes in Py3 */ + { + PyObject *tmp = PyUnicode_AsUTF8String(rv); + Py_DECREF(rv); + if (!(rv = tmp)) { + goto exit; + } + } +#endif + + if ('-' == Bytes_AS_STRING(rv)[0]) { + /* Prepend a space in front of negative numbers (ticket #57) */ + PyObject *tmp; + if (!(tmp = Bytes_FromString(" "))) { + Py_DECREF(rv); + rv = NULL; + goto exit; + } + Bytes_ConcatAndDel(&tmp, rv); + if (!(rv = tmp)) { + goto exit; + } + } + } + +exit: + return rv; } static PyObject * -pfloat_getquoted(pfloatObject *self, PyObject *args) +pfloat_str(pfloatObject *self) { - if (!PyArg_ParseTuple(args, "")) return NULL; - return pfloat_str(self); + return psycopg_ensure_text(pfloat_getquoted(self, NULL)); } static PyObject * @@ -75,14 +110,14 @@ /* object member list */ static struct PyMemberDef pfloatObject_members[] = { - {"adapted", T_OBJECT, offsetof(pfloatObject, wrapped), RO}, + {"adapted", T_OBJECT, offsetof(pfloatObject, wrapped), READONLY}, {NULL} }; /* object method table */ static PyMethodDef pfloatObject_methods[] = { - {"getquoted", (PyCFunction)pfloat_getquoted, METH_VARARGS, + {"getquoted", (PyCFunction)pfloat_getquoted, METH_NOARGS, "getquoted() -> wrapped object value as SQL-quoted string"}, {"__conform__", (PyCFunction)pfloat_conform, METH_VARARGS, NULL}, {NULL} /* Sentinel */ @@ -95,7 +130,7 @@ { Dprintf("pfloat_setup: init pfloat object at %p, refcnt = " FORMAT_CODE_PY_SSIZE_T, - self, ((PyObject *)self)->ob_refcnt + self, Py_REFCNT(self) ); Py_INCREF(obj); @@ -103,7 +138,7 @@ Dprintf("pfloat_setup: good pfloat object at %p, refcnt = " FORMAT_CODE_PY_SSIZE_T, - self, ((PyObject *)self)->ob_refcnt + self, Py_REFCNT(self) ); return 0; } @@ -126,10 +161,10 @@ Dprintf("pfloat_dealloc: deleted pfloat object at %p, refcnt = " FORMAT_CODE_PY_SSIZE_T, - obj, obj->ob_refcnt + obj, Py_REFCNT(obj) ); - obj->ob_type->tp_free(obj); + Py_TYPE(obj)->tp_free(obj); } static int @@ -169,8 +204,7 @@ "Float(str) -> new Float adapter object" PyTypeObject pfloatType = { - PyObject_HEAD_INIT(NULL) - 0, + PyVarObject_HEAD_INIT(NULL, 0) "psycopg2._psycopg.Float", sizeof(pfloatObject), 0, @@ -242,5 +276,5 @@ if (!PyArg_ParseTuple(args, "O", &obj)) return NULL; - return PyObject_CallFunction((PyObject *)&pfloatType, "O", obj); + return PyObject_CallFunctionObjArgs((PyObject *)&pfloatType, obj, NULL); } diff -Nru psycopg2-2.0.13/psycopg/adapter_pfloat.h psycopg2-2.4.5/psycopg/adapter_pfloat.h --- psycopg2-2.0.13/psycopg/adapter_pfloat.h 2009-04-26 12:09:21.000000000 +0000 +++ psycopg2-2.4.5/psycopg/adapter_pfloat.h 2011-02-06 15:58:34.000000000 +0000 @@ -1,32 +1,31 @@ /* adapter_pfloat.h - definition for the psycopg float type wrapper * - * Copyright (C) 2003-2004 Federico Di Gregorio + * Copyright (C) 2003-2010 Federico Di Gregorio * * This file is part of psycopg. * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2, - * or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * psycopg2 is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * In addition, as a special exception, the copyright holders give + * permission to link this program with the OpenSSL library (or with + * modified versions of OpenSSL that use the same license as OpenSSL), + * and distribute linked combinations including the two. + * + * You must obey the GNU Lesser General Public License in all respects for + * all of the code used other than OpenSSL. + * + * psycopg2 is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. */ #ifndef PSYCOPG_PFLOAT_H #define PSYCOPG_PFLOAT_H 1 -#define PY_SSIZE_T_CLEAN -#include - -#include "psycopg/config.h" - #ifdef __cplusplus extern "C" { #endif diff -Nru psycopg2-2.0.13/psycopg/adapter_pint.c psycopg2-2.4.5/psycopg/adapter_pint.c --- psycopg2-2.0.13/psycopg/adapter_pint.c 1970-01-01 00:00:00.000000000 +0000 +++ psycopg2-2.4.5/psycopg/adapter_pint.c 2011-06-13 16:53:48.000000000 +0000 @@ -0,0 +1,266 @@ +/* adapter_int.c - psycopg pint type wrapper implementation + * + * Copyright (C) 2011 Daniele Varrazzo + * + * This file is part of psycopg. + * + * psycopg2 is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * In addition, as a special exception, the copyright holders give + * permission to link this program with the OpenSSL library (or with + * modified versions of OpenSSL that use the same license as OpenSSL), + * and distribute linked combinations including the two. + * + * You must obey the GNU Lesser General Public License in all respects for + * all of the code used other than OpenSSL. + * + * psycopg2 is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + */ + +#define PSYCOPG_MODULE +#include "psycopg/psycopg.h" + +#include "psycopg/adapter_pint.h" +#include "psycopg/microprotocols_proto.h" + + +/** the Int object **/ + +static PyObject * +pint_getquoted(pintObject *self, PyObject *args) +{ + PyObject *res; + if (!(res = PyObject_Str(self->wrapped))) { + goto exit; + } + +#if PY_MAJOR_VERSION > 2 + /* unicode to bytes in Py3 */ + { + PyObject *tmp = PyUnicode_AsUTF8String(res); + Py_DECREF(res); + if (!(res = tmp)) { + goto exit; + } + } +#endif + + if ('-' == Bytes_AS_STRING(res)[0]) { + /* Prepend a space in front of negative numbers (ticket #57) */ + PyObject *tmp; + if (!(tmp = Bytes_FromString(" "))) { + Py_DECREF(res); + res = NULL; + goto exit; + } + Bytes_ConcatAndDel(&tmp, res); + if (!(res = tmp)) { + goto exit; + } + } + +exit: + return res; +} + +static PyObject * +pint_str(pintObject *self) +{ + return psycopg_ensure_text(pint_getquoted(self, NULL)); +} + +static PyObject * +pint_conform(pintObject *self, PyObject *args) +{ + PyObject *res, *proto; + + if (!PyArg_ParseTuple(args, "O", &proto)) return NULL; + + if (proto == (PyObject*)&isqlquoteType) + res = (PyObject*)self; + else + res = Py_None; + + Py_INCREF(res); + return res; +} + +/** the int object */ + +/* object member list */ + +static struct PyMemberDef pintObject_members[] = { + {"adapted", T_OBJECT, offsetof(pintObject, wrapped), READONLY}, + {NULL} +}; + +/* object method table */ + +static PyMethodDef pintObject_methods[] = { + {"getquoted", (PyCFunction)pint_getquoted, METH_NOARGS, + "getquoted() -> wrapped object value as SQL-quoted string"}, + {"__conform__", (PyCFunction)pint_conform, METH_VARARGS, NULL}, + {NULL} /* Sentinel */ +}; + +/* initialization and finalization methods */ + +static int +pint_setup(pintObject *self, PyObject *obj) +{ + Dprintf("pint_setup: init pint object at %p, refcnt = " + FORMAT_CODE_PY_SSIZE_T, + self, Py_REFCNT(self) + ); + + Py_INCREF(obj); + self->wrapped = obj; + + Dprintf("pint_setup: good pint object at %p, refcnt = " + FORMAT_CODE_PY_SSIZE_T, + self, Py_REFCNT(self) + ); + return 0; +} + +static int +pint_traverse(PyObject *obj, visitproc visit, void *arg) +{ + pintObject *self = (pintObject *)obj; + + Py_VISIT(self->wrapped); + return 0; +} + +static void +pint_dealloc(PyObject* obj) +{ + pintObject *self = (pintObject *)obj; + + Py_CLEAR(self->wrapped); + + Dprintf("pint_dealloc: deleted pint object at %p, refcnt = " + FORMAT_CODE_PY_SSIZE_T, + obj, Py_REFCNT(obj) + ); + + Py_TYPE(obj)->tp_free(obj); +} + +static int +pint_init(PyObject *obj, PyObject *args, PyObject *kwds) +{ + PyObject *o; + + if (!PyArg_ParseTuple(args, "O", &o)) + return -1; + + return pint_setup((pintObject *)obj, o); +} + +static PyObject * +pint_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + return type->tp_alloc(type, 0); +} + +static void +pint_del(PyObject* self) +{ + PyObject_GC_Del(self); +} + +static PyObject * +pint_repr(pintObject *self) +{ + return PyString_FromFormat("", + self); +} + + +/* object type */ + +#define pintType_doc \ +"Int(str) -> new Int adapter object" + +PyTypeObject pintType = { + PyVarObject_HEAD_INIT(NULL, 0) + "psycopg2._psycopg.Int", + sizeof(pintObject), + 0, + pint_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + + 0, /*tp_compare*/ + + (reprfunc)pint_repr, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + + 0, /*tp_call*/ + (reprfunc)pint_str, /*tp_str*/ + + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + + Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_HAVE_GC, /*tp_flags*/ + pintType_doc, /*tp_doc*/ + + pint_traverse, /*tp_traverse*/ + 0, /*tp_clear*/ + + 0, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + + /* Attribute descriptor and subclassing stuff */ + + pintObject_methods, /*tp_methods*/ + pintObject_members, /*tp_members*/ + 0, /*tp_getset*/ + 0, /*tp_base*/ + 0, /*tp_dict*/ + + 0, /*tp_descr_get*/ + 0, /*tp_descr_set*/ + 0, /*tp_dictoffset*/ + + pint_init, /*tp_init*/ + 0, /*tp_alloc will be set to PyType_GenericAlloc in module init*/ + pint_new, /*tp_new*/ + (freefunc)pint_del, /*tp_free Low-level free-memory routine */ + 0, /*tp_is_gc For PyObject_IS_GC */ + 0, /*tp_bases*/ + 0, /*tp_mro method resolution order */ + 0, /*tp_cache*/ + 0, /*tp_subclasses*/ + 0 /*tp_weaklist*/ +}; + + +/** module-level functions **/ + +PyObject * +psyco_Int(PyObject *module, PyObject *args) +{ + PyObject *obj; + + if (!PyArg_ParseTuple(args, "O", &obj)) + return NULL; + + return PyObject_CallFunctionObjArgs((PyObject *)&pintType, obj, NULL); +} diff -Nru psycopg2-2.0.13/psycopg/adapter_pint.h psycopg2-2.4.5/psycopg/adapter_pint.h --- psycopg2-2.0.13/psycopg/adapter_pint.h 1970-01-01 00:00:00.000000000 +0000 +++ psycopg2-2.4.5/psycopg/adapter_pint.h 2011-06-13 16:53:48.000000000 +0000 @@ -0,0 +1,53 @@ +/* adapter_pint.h - definition for the psycopg int type wrapper + * + * Copyright (C) 2011 Daniele Varrazzo + * + * This file is part of psycopg. + * + * psycopg2 is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * In addition, as a special exception, the copyright holders give + * permission to link this program with the OpenSSL library (or with + * modified versions of OpenSSL that use the same license as OpenSSL), + * and distribute linked combinations including the two. + * + * You must obey the GNU Lesser General Public License in all respects for + * all of the code used other than OpenSSL. + * + * psycopg2 is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + */ + +#ifndef PSYCOPG_PINT_H +#define PSYCOPG_PINT_H 1 + +#ifdef __cplusplus +extern "C" { +#endif + +extern HIDDEN PyTypeObject pintType; + +typedef struct { + PyObject_HEAD + + /* this is the real object we wrap */ + PyObject *wrapped; + +} pintObject; + +/* functions exported to psycopgmodule.c */ + +HIDDEN PyObject *psyco_Int(PyObject *module, PyObject *args); +#define psyco_Int_doc \ + "Int(obj) -> new int value" + +#ifdef __cplusplus +} +#endif + +#endif /* !defined(PSYCOPG_PINT_H) */ diff -Nru psycopg2-2.0.13/psycopg/adapter_qstring.c psycopg2-2.4.5/psycopg/adapter_qstring.c --- psycopg2-2.0.13/psycopg/adapter_qstring.c 2009-08-09 12:51:14.000000000 +0000 +++ psycopg2-2.4.5/psycopg/adapter_qstring.c 2012-03-28 21:09:15.000000000 +0000 @@ -1,44 +1,41 @@ /* adapter_qstring.c - QuotedString objects * - * Copyright (C) 2003-2004 Federico Di Gregorio + * Copyright (C) 2003-2010 Federico Di Gregorio * * This file is part of psycopg. * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2, - * or (at your option) any later version. + * psycopg2 is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * In addition, as a special exception, the copyright holders give + * permission to link this program with the OpenSSL library (or with + * modified versions of OpenSSL that use the same license as OpenSSL), + * and distribute linked combinations including the two. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You must obey the GNU Lesser General Public License in all respects for + * all of the code used other than OpenSSL. + * + * psycopg2 is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. */ -#define PY_SSIZE_T_CLEAN -#include -#include -#include - -#include -#include - #define PSYCOPG_MODULE -#include "psycopg/config.h" -#include "psycopg/python.h" #include "psycopg/psycopg.h" + #include "psycopg/connection.h" #include "psycopg/adapter_qstring.h" #include "psycopg/microprotocols_proto.h" +#include + /* qstring_quote - do the quote process on plain and unicode strings */ -static PyObject * +BORROWED static PyObject * qstring_quote(qstringObject *self) { PyObject *str; @@ -52,24 +49,12 @@ Dprintf("qstring_quote: encoding to %s", self->encoding); if (PyUnicode_Check(self->wrapped) && self->encoding) { - PyObject *enc = PyDict_GetItemString(psycoEncodings, self->encoding); - /* note that enc is a borrowed reference */ - - if (enc) { - const char *s = PyString_AsString(enc); - Dprintf("qstring_quote: encoding unicode object to %s", s); - str = PyUnicode_AsEncodedString(self->wrapped, s, NULL); - Dprintf("qstring_quote: got encoded object at %p", str); - if (str == NULL) return NULL; - } - else { - /* can't find the right encoder, raise exception */ - PyErr_Format(InterfaceError, - "can't encode unicode string to %s", self->encoding); - return NULL; - } + str = PyUnicode_AsEncodedString(self->wrapped, self->encoding, NULL); + Dprintf("qstring_quote: got encoded object at %p", str); + if (str == NULL) return NULL; } +#if PY_MAJOR_VERSION < 3 /* if the wrapped object is a simple string, we don't know how to (re)encode it, so we pass it as-is */ else if (PyString_Check(self->wrapped)) { @@ -77,6 +62,7 @@ /* INCREF to make it ref-wise identical to unicode one */ Py_INCREF(str); } +#endif /* if the wrapped object is not a string, this is an error */ else { @@ -86,7 +72,7 @@ } /* encode the string into buffer */ - PyString_AsStringAndSize(str, &s, &len); + Bytes_AsStringAndSize(str, &s, &len); /* Call qstring_escape with the GIL released, then reacquire the GIL before verifying that the results can fit into a Python string; raise @@ -109,8 +95,8 @@ Py_DECREF(str); return NULL; } - - self->buffer = PyString_FromStringAndSize(buffer, qlen); + + self->buffer = Bytes_FromStringAndSize(buffer, qlen); PyMem_Free(buffer); Py_DECREF(str); @@ -120,7 +106,7 @@ /* qstring_str, qstring_getquoted - return result of quoting */ static PyObject * -qstring_str(qstringObject *self) +qstring_getquoted(qstringObject *self, PyObject *args) { if (self->buffer == NULL) { qstring_quote(self); @@ -130,33 +116,30 @@ } static PyObject * -qstring_getquoted(qstringObject *self, PyObject *args) +qstring_str(qstringObject *self) { - if (!PyArg_ParseTuple(args, "")) return NULL; - return qstring_str(self); + return psycopg_ensure_text(qstring_getquoted(self, NULL)); } static PyObject * qstring_prepare(qstringObject *self, PyObject *args) { - connectionObject *conn; + PyObject *conn; - if (!PyArg_ParseTuple(args, "O", &conn)) + if (!PyArg_ParseTuple(args, "O!", &connectionType, &conn)) return NULL; /* we bother copying the encoding only if the wrapped string is unicode, we don't need the encoding if that's not the case */ if (PyUnicode_Check(self->wrapped)) { if (self->encoding) free(self->encoding); - self->encoding = strdup(conn->encoding); - Dprintf("qstring_prepare: set encoding to %s", conn->encoding); + self->encoding = strdup(((connectionObject *)conn)->codec); + Dprintf("qstring_prepare: set encoding to %s", self->encoding); } Py_CLEAR(self->conn); - if (conn) { - Py_INCREF(conn); - self->conn = (PyObject*)conn; - } + Py_INCREF(conn); + self->conn = conn; Py_INCREF(Py_None); return Py_None; @@ -183,16 +166,16 @@ /* object member list */ static struct PyMemberDef qstringObject_members[] = { - {"adapted", T_OBJECT, offsetof(qstringObject, wrapped), RO}, - {"buffer", T_OBJECT, offsetof(qstringObject, buffer), RO}, - {"encoding", T_STRING, offsetof(qstringObject, encoding), RO}, + {"adapted", T_OBJECT, offsetof(qstringObject, wrapped), READONLY}, + {"buffer", T_OBJECT, offsetof(qstringObject, buffer), READONLY}, + {"encoding", T_STRING, offsetof(qstringObject, encoding), READONLY}, {NULL} }; /* object method table */ static PyMethodDef qstringObject_methods[] = { - {"getquoted", (PyCFunction)qstring_getquoted, METH_VARARGS, + {"getquoted", (PyCFunction)qstring_getquoted, METH_NOARGS, "getquoted() -> wrapped object value as SQL-quoted string"}, {"prepare", (PyCFunction)qstring_prepare, METH_VARARGS, "prepare(conn) -> set encoding to conn->encoding and store conn"}, @@ -207,7 +190,7 @@ { Dprintf("qstring_setup: init qstring object at %p, refcnt = " FORMAT_CODE_PY_SSIZE_T, - self, ((PyObject *)self)->ob_refcnt + self, Py_REFCNT(self) ); self->buffer = NULL; @@ -221,7 +204,7 @@ Dprintf("qstring_setup: good qstring object at %p, refcnt = " FORMAT_CODE_PY_SSIZE_T, - self, ((PyObject *)self)->ob_refcnt + self, Py_REFCNT(self) ); return 0; } @@ -250,10 +233,10 @@ Dprintf("qstring_dealloc: deleted qstring object at %p, refcnt = " FORMAT_CODE_PY_SSIZE_T, - obj, obj->ob_refcnt + obj, Py_REFCNT(obj) ); - obj->ob_type->tp_free(obj); + Py_TYPE(obj)->tp_free(obj); } static int @@ -293,8 +276,7 @@ "QuotedString(str, enc) -> new quoted object with 'enc' encoding" PyTypeObject qstringType = { - PyObject_HEAD_INIT(NULL) - 0, + PyVarObject_HEAD_INIT(NULL, 0) "psycopg2._psycopg.QuotedString", sizeof(qstringObject), 0, diff -Nru psycopg2-2.0.13/psycopg/adapter_qstring.h psycopg2-2.4.5/psycopg/adapter_qstring.h --- psycopg2-2.0.13/psycopg/adapter_qstring.h 2009-04-26 12:09:21.000000000 +0000 +++ psycopg2-2.4.5/psycopg/adapter_qstring.h 2011-02-06 15:58:34.000000000 +0000 @@ -1,32 +1,31 @@ /* adapter_qstring.h - definition for the QuotedString type * - * Copyright (C) 2003 Federico Di Gregorio + * Copyright (C) 2003-2010 Federico Di Gregorio * * This file is part of psycopg. * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2, - * or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * psycopg2 is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * In addition, as a special exception, the copyright holders give + * permission to link this program with the OpenSSL library (or with + * modified versions of OpenSSL that use the same license as OpenSSL), + * and distribute linked combinations including the two. + * + * You must obey the GNU Lesser General Public License in all respects for + * all of the code used other than OpenSSL. + * + * psycopg2 is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. */ #ifndef PSYCOPG_QSTRING_H #define PSYCOPG_QSTRING_H 1 -#define PY_SSIZE_T_CLEAN -#include - -#include "psycopg/config.h" - #ifdef __cplusplus extern "C" { #endif @@ -38,6 +37,10 @@ PyObject *wrapped; PyObject *buffer; + /* NOTE: this used to be a PostgreSQL encoding: changed in 2.3.2 to be a + * Python codec name. I don't expect there has been any user for this + * object other than adapting str/unicode, so I don't expect client code + * broken for this reason. */ char *encoding; PyObject *conn; diff -Nru psycopg2-2.0.13/psycopg/bytes_format.c psycopg2-2.4.5/psycopg/bytes_format.c --- psycopg2-2.0.13/psycopg/bytes_format.c 1970-01-01 00:00:00.000000000 +0000 +++ psycopg2-2.4.5/psycopg/bytes_format.c 2012-03-28 21:09:15.000000000 +0000 @@ -0,0 +1,298 @@ +/* bytes_format.c - bytes-oriented version of PyString_Format + * + * Copyright (C) 2010 Daniele Varrazzo + * + * This file is part of psycopg. + * + * psycopg2 is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * In addition, as a special exception, the copyright holders give + * permission to link this program with the OpenSSL library (or with + * modified versions of OpenSSL that use the same license as OpenSSL), + * and distribute linked combinations including the two. + * + * You must obey the GNU Lesser General Public License in all respects for + * all of the code used other than OpenSSL. + * + * psycopg2 is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + */ + +/* This implementation is based on the PyString_Format function available in + * Python 2.7.1. The function is altered to be used with both Python 2 strings + * and Python 3 bytes and is stripped of the support of formats different than + * 's'. Original license follows. + * + * PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2 + * -------------------------------------------- + * + * 1. This LICENSE AGREEMENT is between the Python Software Foundation + * ("PSF"), and the Individual or Organization ("Licensee") accessing and + * otherwise using this software ("Python") in source or binary form and + * its associated documentation. + * + * 2. Subject to the terms and conditions of this License Agreement, PSF hereby + * grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce, + * analyze, test, perform and/or display publicly, prepare derivative works, + * distribute, and otherwise use Python alone or in any derivative version, + * provided, however, that PSF's License Agreement and PSF's notice of copyright, + * i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 + * Python Software Foundation; All Rights Reserved" are retained in Python alone or + * in any derivative version prepared by Licensee. + * + * 3. In the event Licensee prepares a derivative work that is based on + * or incorporates Python or any part thereof, and wants to make + * the derivative work available to others as provided herein, then + * Licensee hereby agrees to include in any such work a brief summary of + * the changes made to Python. + * + * 4. PSF is making Python available to Licensee on an "AS IS" + * basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR + * IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND + * DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS + * FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT + * INFRINGE ANY THIRD PARTY RIGHTS. + * + * 5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON + * FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS + * A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON, + * OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. + * + * 6. This License Agreement will automatically terminate upon a material + * breach of its terms and conditions. + * + * 7. Nothing in this License Agreement shall be deemed to create any + * relationship of agency, partnership, or joint venture between PSF and + * Licensee. This License Agreement does not grant permission to use PSF + * trademarks or trade name in a trademark sense to endorse or promote + * products or services of Licensee, or any third party. + * + * 8. By copying, installing or otherwise using Python, Licensee + * agrees to be bound by the terms and conditions of this License + * Agreement. + */ + +#define PSYCOPG_MODULE +#include "psycopg/psycopg.h" + +#ifndef Py_LOCAL_INLINE +#define Py_LOCAL_INLINE(type) static type +#endif + +/* Helpers for formatstring */ + +BORROWED Py_LOCAL_INLINE(PyObject *) +getnextarg(PyObject *args, Py_ssize_t arglen, Py_ssize_t *p_argidx) +{ + Py_ssize_t argidx = *p_argidx; + if (argidx < arglen) { + (*p_argidx)++; + if (arglen < 0) + return args; + else + return PyTuple_GetItem(args, argidx); + } + PyErr_SetString(PyExc_TypeError, + "not enough arguments for format string"); + return NULL; +} + +/* fmt%(v1,v2,...) is roughly equivalent to sprintf(fmt, v1, v2, ...) */ + +PyObject * +Bytes_Format(PyObject *format, PyObject *args) +{ + char *fmt, *res; + Py_ssize_t arglen, argidx; + Py_ssize_t reslen, rescnt, fmtcnt; + int args_owned = 0; + PyObject *result; + PyObject *dict = NULL; + if (format == NULL || !Bytes_Check(format) || args == NULL) { + PyErr_BadInternalCall(); + return NULL; + } + fmt = Bytes_AS_STRING(format); + fmtcnt = Bytes_GET_SIZE(format); + reslen = rescnt = fmtcnt + 100; + result = Bytes_FromStringAndSize((char *)NULL, reslen); + if (result == NULL) + return NULL; + res = Bytes_AsString(result); + if (PyTuple_Check(args)) { + arglen = PyTuple_GET_SIZE(args); + argidx = 0; + } + else { + arglen = -1; + argidx = -2; + } + if (Py_TYPE(args)->tp_as_mapping && !PyTuple_Check(args) && + !PyObject_TypeCheck(args, &Bytes_Type)) + dict = args; + while (--fmtcnt >= 0) { + if (*fmt != '%') { + if (--rescnt < 0) { + rescnt = fmtcnt + 100; + reslen += rescnt; + if (_Bytes_Resize(&result, reslen)) + return NULL; + res = Bytes_AS_STRING(result) + + reslen - rescnt; + --rescnt; + } + *res++ = *fmt++; + } + else { + /* Got a format specifier */ + Py_ssize_t width = -1; + int c = '\0'; + PyObject *v = NULL; + PyObject *temp = NULL; + char *pbuf; + Py_ssize_t len; + fmt++; + if (*fmt == '(') { + char *keystart; + Py_ssize_t keylen; + PyObject *key; + int pcount = 1; + + if (dict == NULL) { + PyErr_SetString(PyExc_TypeError, + "format requires a mapping"); + goto error; + } + ++fmt; + --fmtcnt; + keystart = fmt; + /* Skip over balanced parentheses */ + while (pcount > 0 && --fmtcnt >= 0) { + if (*fmt == ')') + --pcount; + else if (*fmt == '(') + ++pcount; + fmt++; + } + keylen = fmt - keystart - 1; + if (fmtcnt < 0 || pcount > 0) { + PyErr_SetString(PyExc_ValueError, + "incomplete format key"); + goto error; + } + key = Text_FromUTF8AndSize(keystart, keylen); + if (key == NULL) + goto error; + if (args_owned) { + Py_DECREF(args); + args_owned = 0; + } + args = PyObject_GetItem(dict, key); + Py_DECREF(key); + if (args == NULL) { + goto error; + } + args_owned = 1; + arglen = -1; + argidx = -2; + } + while (--fmtcnt >= 0) { + c = *fmt++; + break; + } + if (fmtcnt < 0) { + PyErr_SetString(PyExc_ValueError, + "incomplete format"); + goto error; + } + if (c != '%') { + v = getnextarg(args, arglen, &argidx); + if (v == NULL) + goto error; + } + switch (c) { + case '%': + pbuf = "%"; + len = 1; + break; + case 's': + /* only bytes! */ + if (!Bytes_CheckExact(v)) { + PyErr_Format(PyExc_ValueError, + "only bytes values expected, got %s", + Py_TYPE(v)->tp_name); + goto error; + } + temp = v; + Py_INCREF(v); + pbuf = Bytes_AS_STRING(temp); + len = Bytes_GET_SIZE(temp); + break; + default: + PyErr_Format(PyExc_ValueError, + "unsupported format character '%c' (0x%x) " + "at index " FORMAT_CODE_PY_SSIZE_T, + c, c, + (Py_ssize_t)(fmt - 1 - + Bytes_AsString(format))); + goto error; + } + if (width < len) + width = len; + if (rescnt < width) { + reslen -= rescnt; + rescnt = width + fmtcnt + 100; + reslen += rescnt; + if (reslen < 0) { + Py_DECREF(result); + Py_XDECREF(temp); + return PyErr_NoMemory(); + } + if (_Bytes_Resize(&result, reslen)) { + Py_XDECREF(temp); + return NULL; + } + res = Bytes_AS_STRING(result) + + reslen - rescnt; + } + Py_MEMCPY(res, pbuf, len); + res += len; + rescnt -= len; + while (--width >= len) { + --rescnt; + *res++ = ' '; + } + if (dict && (argidx < arglen) && c != '%') { + PyErr_SetString(PyExc_TypeError, + "not all arguments converted during string formatting"); + Py_XDECREF(temp); + goto error; + } + Py_XDECREF(temp); + } /* '%' */ + } /* until end */ + if (argidx < arglen && !dict) { + PyErr_SetString(PyExc_TypeError, + "not all arguments converted during string formatting"); + goto error; + } + if (args_owned) { + Py_DECREF(args); + } + if (_Bytes_Resize(&result, reslen - rescnt)) + return NULL; + return result; + + error: + Py_DECREF(result); + if (args_owned) { + Py_DECREF(args); + } + return NULL; +} + diff -Nru psycopg2-2.0.13/psycopg/config.h psycopg2-2.4.5/psycopg/config.h --- psycopg2-2.0.13/psycopg/config.h 2009-04-26 12:09:21.000000000 +0000 +++ psycopg2-2.4.5/psycopg/config.h 2012-03-28 21:09:15.000000000 +0000 @@ -1,29 +1,33 @@ /* config.h - general config and Dprintf macro * - * Copyright (C) 2003 Federico Di Gregorio + * Copyright (C) 2003-2010 Federico Di Gregorio * * This file is part of psycopg. * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2, - * or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * psycopg2 is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * In addition, as a special exception, the copyright holders give + * permission to link this program with the OpenSSL library (or with + * modified versions of OpenSSL that use the same license as OpenSSL), + * and distribute linked combinations including the two. + * + * You must obey the GNU Lesser General Public License in all respects for + * all of the code used other than OpenSSL. + * + * psycopg2 is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. */ #ifndef PSYCOPG_CONFIG_H #define PSYCOPG_CONFIG_H 1 /* GCC 4.0 and later have support for specifying symbol visibility */ -#if __GNUC__ >= 4 +#if __GNUC__ >= 4 && !defined(__MINGW32__) # define HIDDEN __attribute__((visibility("hidden"))) #else # define HIDDEN @@ -47,6 +51,10 @@ #else /* !__GNUC__ or __APPLE__ */ #ifdef PSYCOPG_DEBUG #include +#ifdef _WIN32 +#include +#define getpid _getpid +#endif static void Dprintf(const char *fmt, ...) { va_list ap; @@ -115,16 +123,9 @@ /* to work around the fact that Windows does not have a gmtime_r function, or a proper gmtime function */ #ifdef _WIN32 -static struct tm *gmtime_r(time_t *t, struct tm *tm) -{ - tm = gmtime(t); - return tm; -} -static struct tm *localtime_r(time_t *t, struct tm *tm) -{ - tm = localtime(t); - return tm; -} +#define gmtime_r(t, tm) (gmtime(t)?memcpy((tm), gmtime(t), sizeof(*(tm))):NULL) +#define localtime_r(t, tm) (localtime(t)?memcpy((tm), localtime(t), sizeof(*(tm))):NULL) + /* remove the inline keyword, since it doesn't work unless C++ file */ #define inline @@ -135,6 +136,8 @@ * in libxml2 code */ #define isinf(x) ((_fpclass(x) == _FPCLASS_PINF) ? 1 \ : ((_fpclass(x) == _FPCLASS_NINF) ? -1 : 0)) + +#define strcasecmp(x, y) lstrcmpi(x, y) #endif #endif @@ -157,4 +160,33 @@ #define isinf(x) (!finite((x)) && (x)==(x)) #endif +/* decorators for the gcc cpychecker plugin */ +#if defined(WITH_CPYCHECKER_RETURNS_BORROWED_REF_ATTRIBUTE) +#define BORROWED \ + __attribute__((cpychecker_returns_borrowed_ref)) +#else +#define BORROWED +#endif + +#if defined(WITH_CPYCHECKER_STEALS_REFERENCE_TO_ARG_ATTRIBUTE) +#define STEALS(n) \ + __attribute__((cpychecker_steals_reference_to_arg(n))) +#else +#define STEALS(n) +#endif + +#if defined(WITH_CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION_ATTRIBUTE) +#define RAISES_NEG \ + __attribute__((cpychecker_negative_result_sets_exception)) +#else +#define RAISES_NEG +#endif + +#if defined(WITH_CPYCHECKER_SETS_EXCEPTION_ATTRIBUTE) +#define RAISES \ + __attribute__((cpychecker_sets_exception)) +#else +#define RAISES +#endif + #endif /* !defined(PSYCOPG_CONFIG_H) */ diff -Nru psycopg2-2.0.13/psycopg/connection.h psycopg2-2.4.5/psycopg/connection.h --- psycopg2-2.0.13/psycopg/connection.h 2009-10-04 21:32:35.000000000 +0000 +++ psycopg2-2.4.5/psycopg/connection.h 2012-03-28 21:09:15.000000000 +0000 @@ -1,46 +1,72 @@ /* connection.h - definition for the psycopg connection type * - * Copyright (C) 2003 Federico Di Gregorio + * Copyright (C) 2003-2010 Federico Di Gregorio * * This file is part of psycopg. * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2, - * or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * psycopg2 is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * In addition, as a special exception, the copyright holders give + * permission to link this program with the OpenSSL library (or with + * modified versions of OpenSSL that use the same license as OpenSSL), + * and distribute linked combinations including the two. + * + * You must obey the GNU Lesser General Public License in all respects for + * all of the code used other than OpenSSL. + * + * psycopg2 is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. */ #ifndef PSYCOPG_CONNECTION_H #define PSYCOPG_CONNECTION_H 1 -#define PY_SSIZE_T_CLEAN -#include -#include - -#include "psycopg/config.h" +#include "psycopg/xid.h" #ifdef __cplusplus extern "C" { #endif +/* isolation levels */ +#define ISOLATION_LEVEL_AUTOCOMMIT 0 +#define ISOLATION_LEVEL_READ_UNCOMMITTED 4 +#define ISOLATION_LEVEL_READ_COMMITTED 1 +#define ISOLATION_LEVEL_REPEATABLE_READ 2 +#define ISOLATION_LEVEL_SERIALIZABLE 3 + /* connection status */ -#define CONN_STATUS_READY 1 -#define CONN_STATUS_BEGIN 2 -#define CONN_STATUS_SYNC 3 -#define CONN_STATUS_ASYNC 4 +#define CONN_STATUS_SETUP 0 +#define CONN_STATUS_READY 1 +#define CONN_STATUS_BEGIN 2 +#define CONN_STATUS_PREPARED 5 +/* async connection building statuses */ +#define CONN_STATUS_CONNECTING 20 +#define CONN_STATUS_DATESTYLE 21 + +/* async query execution status */ +#define ASYNC_DONE 0 +#define ASYNC_READ 1 +#define ASYNC_WRITE 2 + +/* polling result */ +#define PSYCO_POLL_OK 0 +#define PSYCO_POLL_READ 1 +#define PSYCO_POLL_WRITE 2 +#define PSYCO_POLL_ERROR 3 /* Hard limit on the notices stored by the Python connection */ #define CONN_NOTICES_LIMIT 50 +/* we need the initial date style to be ISO, for typecasters; if the user + later change it, she must know what she's doing... these are the queries we + need to issue */ +#define psyco_datestyle "SET DATESTYLE TO 'ISO'" + extern HIDDEN PyTypeObject connectionType; struct connectionObject_notice { @@ -56,18 +82,26 @@ char *dsn; /* data source name */ char *critical; /* critical error on this connection */ char *encoding; /* current backend encoding */ + char *codec; /* python codec name for encoding */ long int closed; /* 1 means connection has been closed; 2 that something horrible happened */ - long int isolation_level; /* isolation level for this connection */ long int mark; /* number of commits/rollbacks done so far */ int status; /* status of the connection */ + XidObject *tpc_xid; /* Transaction ID in two-phase commit */ + + long int async; /* 1 means the connection is async */ int protocol; /* protocol version */ int server_version; /* server version */ - PGconn *pgconn; /* the postgresql connection */ + PGconn *pgconn; /* the postgresql connection */ + PGcancel *cancel; /* the cancellation structure */ + /* Weakref to the object executing an asynchronous query. The object + * is a cursor for async connections, but it may be something else + * for a green connection. If NULL, the connection is idle. */ PyObject *async_cursor; + int async_status; /* asynchronous execution status */ /* notice processing */ PyObject *notice_list; @@ -82,24 +116,82 @@ PyObject *binary_types; /* a set of typecasters for binary types */ int equote; /* use E''-style quotes for escaped strings */ + PyObject *weakreflist; /* list of weak references */ + + int autocommit; + } connectionObject; +/* map isolation level values into a numeric const */ +typedef struct { + char *name; + int value; +} IsolationLevel; + /* C-callable functions in connection_int.c and connection_ext.c */ +HIDDEN PyObject *conn_text_from_chars(connectionObject *pgconn, const char *str); +HIDDEN int conn_get_standard_conforming_strings(PGconn *pgconn); +RAISES_NEG HIDDEN int conn_get_isolation_level(connectionObject *self); +HIDDEN int conn_get_protocol_version(PGconn *pgconn); +HIDDEN int conn_get_server_version(PGconn *pgconn); +HIDDEN PGcancel *conn_get_cancel(PGconn *pgconn); HIDDEN void conn_notice_process(connectionObject *self); HIDDEN void conn_notice_clean(connectionObject *self); -HIDDEN int conn_setup(connectionObject *self, PGconn *pgconn); -HIDDEN int conn_connect(connectionObject *self); +HIDDEN void conn_notifies_process(connectionObject *self); +RAISES_NEG HIDDEN int conn_setup(connectionObject *self, PGconn *pgconn); +HIDDEN int conn_connect(connectionObject *self, long int async); HIDDEN void conn_close(connectionObject *self); -HIDDEN int conn_commit(connectionObject *self); -HIDDEN int conn_rollback(connectionObject *self); -HIDDEN int conn_switch_isolation_level(connectionObject *self, int level); -HIDDEN int conn_set_client_encoding(connectionObject *self, const char *enc); +RAISES_NEG HIDDEN int conn_commit(connectionObject *self); +RAISES_NEG HIDDEN int conn_rollback(connectionObject *self); +RAISES_NEG HIDDEN int conn_set_session(connectionObject *self, const char *isolevel, + const char *readonly, const char *deferrable, + int autocommit); +HIDDEN int conn_set_autocommit(connectionObject *self, int value); +RAISES_NEG HIDDEN int conn_switch_isolation_level(connectionObject *self, int level); +RAISES_NEG HIDDEN int conn_set_client_encoding(connectionObject *self, const char *enc); +HIDDEN int conn_poll(connectionObject *self); +RAISES_NEG HIDDEN int conn_tpc_begin(connectionObject *self, XidObject *xid); +RAISES_NEG HIDDEN int conn_tpc_command(connectionObject *self, + const char *cmd, XidObject *xid); +HIDDEN PyObject *conn_tpc_recover(connectionObject *self); /* exception-raising macros */ #define EXC_IF_CONN_CLOSED(self) if ((self)->closed > 0) { \ PyErr_SetString(InterfaceError, "connection already closed"); \ return NULL; } +#define EXC_IF_CONN_ASYNC(self, cmd) if ((self)->async == 1) { \ + PyErr_SetString(ProgrammingError, #cmd " cannot be used " \ + "in asynchronous mode"); \ + return NULL; } + +#define EXC_IF_IN_TRANSACTION(self, cmd) \ + if (self->status != CONN_STATUS_READY) { \ + PyErr_Format(ProgrammingError, \ + "%s cannot be used inside a transaction", #cmd); \ + return NULL; \ + } + +#define EXC_IF_TPC_NOT_SUPPORTED(self) \ + if ((self)->server_version < 80100) { \ + PyErr_Format(NotSupportedError, \ + "server version %d: " \ + "two-phase transactions not supported", \ + (self)->server_version); \ + return NULL; \ + } + +#define EXC_IF_TPC_BEGIN(self, cmd) if ((self)->tpc_xid) { \ + PyErr_Format(ProgrammingError, "%s cannot be used " \ + "during a two-phase transaction", #cmd); \ + return NULL; } + +#define EXC_IF_TPC_PREPARED(self, cmd) \ + if ((self)->status == CONN_STATUS_PREPARED) { \ + PyErr_Format(ProgrammingError, "%s cannot be used " \ + "with a prepared two-phase transaction", #cmd); \ + return NULL; } + #ifdef __cplusplus } #endif diff -Nru psycopg2-2.0.13/psycopg/connection_int.c psycopg2-2.4.5/psycopg/connection_int.c --- psycopg2-2.0.13/psycopg/connection_int.c 2009-10-04 21:38:16.000000000 +0000 +++ psycopg2-2.4.5/psycopg/connection_int.c 2012-03-28 21:09:15.000000000 +0000 @@ -1,34 +1,70 @@ /* connection_int.c - code used by the connection object * - * Copyright (C) 2003 Federico Di Gregorio + * Copyright (C) 2003-2010 Federico Di Gregorio * * This file is part of psycopg. * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2, - * or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * psycopg2 is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * In addition, as a special exception, the copyright holders give + * permission to link this program with the OpenSSL library (or with + * modified versions of OpenSSL that use the same license as OpenSSL), + * and distribute linked combinations including the two. + * + * You must obey the GNU Lesser General Public License in all respects for + * all of the code used other than OpenSSL. + * + * psycopg2 is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. */ -#define PY_SSIZE_T_CLEAN -#include -#include - #define PSYCOPG_MODULE -#include "psycopg/config.h" #include "psycopg/psycopg.h" + #include "psycopg/connection.h" #include "psycopg/cursor.h" #include "psycopg/pqpath.h" +#include "psycopg/green.h" +#include "psycopg/notify.h" + +#include + +/* Mapping from isolation level name to value exposed by Python. + * + * Note: ordering matters: to get a valid pre-PG 8 level from one not valid, + * we increase a pointer in this list by one position. */ +const IsolationLevel conn_isolevels[] = { + {"", ISOLATION_LEVEL_AUTOCOMMIT}, + {"read uncommitted", ISOLATION_LEVEL_READ_UNCOMMITTED}, + {"read committed", ISOLATION_LEVEL_READ_COMMITTED}, + {"repeatable read", ISOLATION_LEVEL_REPEATABLE_READ}, + {"serializable", ISOLATION_LEVEL_SERIALIZABLE}, + {"default", -1}, /* never to be found on the server */ + { NULL } +}; + + +/* Return a new "string" from a char* from the database. + * + * On Py2 just get a string, on Py3 decode it in the connection codec. + * + * Use a fallback if the connection is NULL. + */ +PyObject * +conn_text_from_chars(connectionObject *self, const char *str) +{ +#if PY_MAJOR_VERSION < 3 + return PyString_FromString(str); +#else + const char *codec = self ? self->codec : "ascii"; + return PyUnicode_Decode(str, strlen(str), codec, "replace"); +#endif +} /* conn_notice_callback - process notices */ @@ -40,59 +76,70 @@ Dprintf("conn_notice_callback: %s", message); - /* unfortunately the old protocol return COPY FROM errors only as notices, - so we need to filter them looking for such errors (but we do it - only if the protocol if <3, else we don't need that) - - NOTE: if we get here and the connection is unlocked then there is a + /* NOTE: if we get here and the connection is unlocked then there is a problem but this should happen because the notice callback is only called from libpq and when we're inside libpq the connection is usually locked. */ - - if (self->protocol < 3 && strncmp(message, "ERROR", 5) == 0) - pq_set_critical(self, message); - else { - notice = (struct connectionObject_notice *) - malloc(sizeof(struct connectionObject_notice)); - notice->message = strdup(message); - notice->next = self->notice_pending; - self->notice_pending = notice; + notice = (struct connectionObject_notice *) + malloc(sizeof(struct connectionObject_notice)); + if (NULL == notice) { + /* Discard the notice in case of failed allocation. */ + return; } + notice->message = strdup(message); + if (NULL == notice->message) { + free(notice); + return; + } + notice->next = self->notice_pending; + self->notice_pending = notice; } +/* Expose the notices received as Python objects. + * + * The function should be called with the connection lock and the GIL. + */ void conn_notice_process(connectionObject *self) { struct connectionObject_notice *notice; - PyObject *msg; + Py_ssize_t nnotices; - Py_BEGIN_ALLOW_THREADS; - pthread_mutex_lock(&self->lock); + if (NULL == self->notice_pending) { + return; + } notice = self->notice_pending; + nnotices = PyList_GET_SIZE(self->notice_list); while (notice != NULL) { - Py_BLOCK_THREADS; - - msg = PyString_FromString(notice->message); - + PyObject *msg; + msg = conn_text_from_chars(self, notice->message); Dprintf("conn_notice_process: %s", notice->message); - PyList_Append(self->notice_list, msg); - Py_DECREF(msg); - - /* Remove the oldest item if the queue is getting too long. */ - if (PyList_GET_SIZE(self->notice_list) > CONN_NOTICES_LIMIT) - PySequence_DelItem(self->notice_list, 0); - - Py_UNBLOCK_THREADS; + /* Respect the order in which notices were produced, + because in notice_list they are reversed (see ticket #9) */ + if (msg) { + PyList_Insert(self->notice_list, nnotices, msg); + Py_DECREF(msg); + } + else { + /* We don't really have a way to report errors, so gulp it. + * The function should only fail for out of memory, so we are + * likely going to die anyway. */ + PyErr_Clear(); + } notice = notice->next; } - pthread_mutex_unlock(&self->lock); - Py_END_ALLOW_THREADS; + /* Remove the oldest item if the queue is getting too long. */ + nnotices = PyList_GET_SIZE(self->notice_list); + if (nnotices > CONN_NOTICES_LIMIT) { + PySequence_DelSlice(self->notice_list, + 0, nnotices - CONN_NOTICES_LIMIT); + } conn_notice_clean(self); } @@ -101,8 +148,6 @@ conn_notice_clean(connectionObject *self) { struct connectionObject_notice *tmp, *notice; - Py_BEGIN_ALLOW_THREADS; - pthread_mutex_lock(&self->lock); notice = self->notice_pending; @@ -112,42 +157,71 @@ free((void*)tmp->message); free(tmp); } - + self->notice_pending = NULL; - - pthread_mutex_unlock(&self->lock); - Py_END_ALLOW_THREADS; } -/* conn_setup - setup and read basic information about the connection */ -int -conn_setup(connectionObject *self, PGconn *pgconn) +/* conn_notifies_process - make received notification available + * + * The function should be called with the connection lock and holding the GIL. + */ + +void +conn_notifies_process(connectionObject *self) { - PGresult *pgres; - const char *data, *tmp; - const char *scs; /* standard-conforming strings */ - size_t i; - - /* we need the initial date style to be ISO, for typecasters; if the user - later change it, she must know what she's doing... */ - static const char datestyle[] = "SET DATESTYLE TO 'ISO'"; - static const char encoding[] = "SHOW client_encoding"; - static const char isolevel[] = "SHOW default_transaction_isolation"; - - static const char lvl1a[] = "read uncommitted"; - static const char lvl1b[] = "read committed"; - static const char lvl2a[] = "repeatable read"; - static const char lvl2b[] = "serializable"; + PGnotify *pgn = NULL; + PyObject *notify = NULL; + PyObject *pid = NULL, *channel = NULL, *payload = NULL; + + while ((pgn = PQnotifies(self->pgconn)) != NULL) { + + Dprintf("conn_notifies_process: got NOTIFY from pid %d, msg = %s", + (int) pgn->be_pid, pgn->relname); + + if (!(pid = PyInt_FromLong((long)pgn->be_pid))) { goto error; } + if (!(channel = conn_text_from_chars(self, pgn->relname))) { goto error; } + if (!(payload = conn_text_from_chars(self, pgn->extra))) { goto error; } + + if (!(notify = PyObject_CallFunctionObjArgs((PyObject *)&NotifyType, + pid, channel, payload, NULL))) { + goto error; + } - Py_BEGIN_ALLOW_THREADS; - pthread_mutex_lock(&self->lock); - Py_BLOCK_THREADS; + Py_DECREF(pid); pid = NULL; + Py_DECREF(channel); channel = NULL; + Py_DECREF(payload); payload = NULL; + + PyList_Append(self->notifies, (PyObject *)notify); - if (self->encoding) free(self->encoding); - self->equote = 0; - self->isolation_level = 0; + Py_DECREF(notify); notify = NULL; + PQfreemem(pgn); pgn = NULL; + } + return; /* no error */ + +error: + if (pgn) { PQfreemem(pgn); } + Py_XDECREF(notify); + Py_XDECREF(pid); + Py_XDECREF(channel); + Py_XDECREF(payload); + + /* TODO: callers currently don't expect an error from us */ + PyErr_Clear(); + +} + + +/* + * the conn_get_* family of functions makes it easier to obtain the connection + * parameters from query results or by interrogating the connection itself +*/ +int +conn_get_standard_conforming_strings(PGconn *pgconn) +{ + int equote; + const char *scs; /* * The presence of the 'standard_conforming_strings' parameter * means that the server _accepts_ the E'' quote. @@ -161,89 +235,285 @@ * not escaped strings (e.g. '\001' -> "\001"), relying on the * fact that the '\' will pass untouched the string parser. * In this case the E'' quotes are NOT to be used. - * - * The PSYCOPG_OWN_QUOTING implementation always returns escaped strings. */ scs = PQparameterStatus(pgconn, "standard_conforming_strings"); Dprintf("conn_connect: server standard_conforming_strings parameter: %s", scs ? scs : "unavailable"); -#ifndef PSYCOPG_OWN_QUOTING - self->equote = (scs && (0 == strcmp("off", scs))); -#else - self->equote = (scs != NULL); -#endif + equote = (scs && (0 == strcmp("off", scs))); Dprintf("conn_connect: server requires E'' quotes: %s", - self->equote ? "YES" : "NO"); + equote ? "YES" : "NO"); - Py_UNBLOCK_THREADS; - pgres = PQexec(pgconn, datestyle); - Py_BLOCK_THREADS; + return equote; +} - if (pgres == NULL || PQresultStatus(pgres) != PGRES_COMMAND_OK ) { - PyErr_SetString(OperationalError, "can't set datestyle to ISO"); - PQfinish(pgconn); - IFCLEARPGRES(pgres); - Py_UNBLOCK_THREADS; - pthread_mutex_unlock(&self->lock); - Py_BLOCK_THREADS; - return -1; + +/* Remove irrelevant chars from encoding name and turn it uppercase. + * + * Return a buffer allocated on Python heap into 'clean' and return 0 on + * success, otherwise return -1 and set an exception. + */ +RAISES_NEG static int +clear_encoding_name(const char *enc, char **clean) +{ + const char *i = enc; + char *j, *buf; + int rv = -1; + + /* convert to upper case and remove '-' and '_' from string */ + if (!(j = buf = PyMem_Malloc(strlen(enc) + 1))) { + PyErr_NoMemory(); + goto exit; } - CLEARPGRES(pgres); - Py_UNBLOCK_THREADS; - pgres = PQexec(pgconn, encoding); - Py_BLOCK_THREADS; + while (*i) { + if (!isalnum(*i)) { + ++i; + } + else { + *j++ = toupper(*i++); + } + } + *j = '\0'; - if (pgres == NULL || PQresultStatus(pgres) != PGRES_TUPLES_OK) { - PyErr_SetString(OperationalError, "can't fetch client_encoding"); - PQfinish(pgconn); - IFCLEARPGRES(pgres); - Py_UNBLOCK_THREADS; - pthread_mutex_unlock(&self->lock); - Py_BLOCK_THREADS; + Dprintf("clear_encoding_name: %s -> %s", enc, buf); + *clean = buf; + rv = 0; + +exit: + return rv; +} + +/* Convert a PostgreSQL encoding to a Python codec. + * + * Set 'codec' to a new copy of the codec name allocated on the Python heap. + * Return 0 in case of success, else -1 and set an exception. + * + * 'enc' should be already normalized (uppercase, no - or _). + */ +RAISES_NEG static int +conn_encoding_to_codec(const char *enc, char **codec) +{ + char *tmp; + Py_ssize_t size; + PyObject *pyenc = NULL; + int rv = -1; + + /* Find the Py codec name from the PG encoding */ + if (!(pyenc = PyDict_GetItemString(psycoEncodings, enc))) { + PyErr_Format(OperationalError, + "no Python codec for client encoding '%s'", enc); + goto exit; + } + + /* Convert the codec in a bytes string to extract the c string. */ + Py_INCREF(pyenc); + if (!(pyenc = psycopg_ensure_bytes(pyenc))) { + goto exit; + } + + if (-1 == Bytes_AsStringAndSize(pyenc, &tmp, &size)) { + goto exit; + } + + /* have our own copy of the python codec name */ + rv = psycopg_strdup(codec, tmp, size); + +exit: + Py_XDECREF(pyenc); + return rv; +} + +/* Read the client encoding from the connection. + * + * Store the encoding in the pgconn->encoding field and the name of the + * matching python codec in codec. The buffers are allocated on the Python + * heap. + * + * Return 0 on success, else nonzero. + */ +RAISES_NEG static int +conn_read_encoding(connectionObject *self, PGconn *pgconn) +{ + char *enc = NULL, *codec = NULL; + const char *tmp; + int rv = -1; + + tmp = PQparameterStatus(pgconn, "client_encoding"); + Dprintf("conn_connect: client encoding: %s", tmp ? tmp : "(none)"); + if (!tmp) { + PyErr_SetString(OperationalError, + "server didn't return client encoding"); + goto exit; + } + + if (0 > clear_encoding_name(tmp, &enc)) { + goto exit; + } + + /* Look for this encoding in Python codecs. */ + if (0 > conn_encoding_to_codec(enc, &codec)) { + goto exit; + } + + /* Good, success: store the encoding/codec in the connection. */ + PyMem_Free(self->encoding); + self->encoding = enc; + enc = NULL; + + PyMem_Free(self->codec); + self->codec = codec; + codec = NULL; + + rv = 0; + +exit: + PyMem_Free(enc); + PyMem_Free(codec); + return rv; +} + + +RAISES_NEG int +conn_get_isolation_level(connectionObject *self) +{ + PGresult *pgres = NULL; + char *error = NULL; + int rv = -1; + char *lname; + const IsolationLevel *level; + + /* this may get called by async connections too: here's your result */ + if (self->autocommit) { + return 0; + } + + Py_BEGIN_ALLOW_THREADS; + pthread_mutex_lock(&self->lock); + + if (!(lname = pq_get_guc_locked(self, "default_transaction_isolation", + &pgres, &error, &_save))) { + goto endlock; + } + + /* find the value for the requested isolation level */ + level = conn_isolevels; + while ((++level)->name) { + if (0 == strcasecmp(level->name, lname)) { + rv = level->value; + break; + } + } + if (-1 == rv) { + error = malloc(256); + PyOS_snprintf(error, 256, + "unexpected isolation level: '%s'", lname); + } + + free(lname); + +endlock: + pthread_mutex_unlock(&self->lock); + Py_END_ALLOW_THREADS; + + if (rv < 0) { + pq_complete_error(self, &pgres, &error); + } + + return rv; +} + + +int +conn_get_protocol_version(PGconn *pgconn) +{ + int ret; + ret = PQprotocolVersion(pgconn); + Dprintf("conn_connect: using protocol %d", ret); + return ret; +} + +int +conn_get_server_version(PGconn *pgconn) +{ + return (int)PQserverVersion(pgconn); +} + +PGcancel * +conn_get_cancel(PGconn *pgconn) +{ + return PQgetCancel(pgconn); +} + + +/* Return 1 if the server datestyle allows us to work without problems, + 0 if it needs to be set to something better, e.g. ISO. */ +static int +conn_is_datestyle_ok(PGconn *pgconn) +{ + const char *ds; + + 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'); +} + + +/* conn_setup - setup and read basic information about the connection */ + +RAISES_NEG int +conn_setup(connectionObject *self, PGconn *pgconn) +{ + PGresult *pgres = NULL; + char *error = NULL; + + self->equote = conn_get_standard_conforming_strings(pgconn); + self->server_version = conn_get_server_version(pgconn); + self->protocol = conn_get_protocol_version(self->pgconn); + if (3 != self->protocol) { + PyErr_SetString(InterfaceError, "only protocol 3 supported"); return -1; } - tmp = PQgetvalue(pgres, 0, 0); - self->encoding = malloc(strlen(tmp)+1); - if (self->encoding == NULL) { - PyErr_NoMemory(); - PQfinish(pgconn); - IFCLEARPGRES(pgres); - Py_UNBLOCK_THREADS; - pthread_mutex_unlock(&self->lock); - Py_BLOCK_THREADS; + + if (0 > conn_read_encoding(self, pgconn)) { return -1; } - for (i=0 ; i < strlen(tmp) ; i++) - self->encoding[i] = toupper(tmp[i]); - self->encoding[i] = '\0'; - CLEARPGRES(pgres); - Py_UNBLOCK_THREADS; - pgres = PQexec(pgconn, isolevel); + self->cancel = conn_get_cancel(self->pgconn); + if (self->cancel == NULL) { + PyErr_SetString(OperationalError, "can't get cancellation key"); + return -1; + } + + Py_BEGIN_ALLOW_THREADS; + pthread_mutex_lock(&self->lock); Py_BLOCK_THREADS; - if (pgres == NULL || PQresultStatus(pgres) != PGRES_TUPLES_OK) { - PyErr_SetString(OperationalError, - "can't fetch default_isolation_level"); - PQfinish(pgconn); - IFCLEARPGRES(pgres); + if (psyco_green() && (0 > pq_set_non_blocking(self, 1))) { + return -1; + } + + if (!conn_is_datestyle_ok(self->pgconn)) { + int res; Py_UNBLOCK_THREADS; - pthread_mutex_unlock(&self->lock); + res = pq_set_guc_locked(self, "datestyle", "ISO", + &pgres, &error, &_save); Py_BLOCK_THREADS; - return -1; + if (res < 0) { + pq_complete_error(self, &pgres, &error); + return -1; + } } - data = PQgetvalue(pgres, 0, 0); - if ((strncmp(lvl1a, data, strlen(lvl1a)) == 0) - || (strncmp(lvl1b, data, strlen(lvl1b)) == 0)) - self->isolation_level = 1; - else if ((strncmp(lvl2a, data, strlen(lvl2a)) == 0) - || (strncmp(lvl2b, data, strlen(lvl2b)) == 0)) - self->isolation_level = 2; - else - self->isolation_level = 2; - CLEARPGRES(pgres); + + /* for reset */ + self->autocommit = 0; Py_UNBLOCK_THREADS; pthread_mutex_unlock(&self->lock); @@ -254,16 +524,27 @@ /* conn_connect - execute a connection to the database */ -int -conn_connect(connectionObject *self) +static int +_conn_sync_connect(connectionObject *self) { PGconn *pgconn; + int green; - Py_BEGIN_ALLOW_THREADS; - pgconn = PQconnectdb(self->dsn); - Py_END_ALLOW_THREADS; - - Dprintf("conn_connect: new postgresql connection at %p", pgconn); + /* store this value to prevent inconsistencies due to a change + * in the middle of the function. */ + green = psyco_green(); + if (!green) { + Py_BEGIN_ALLOW_THREADS; + self->pgconn = pgconn = PQconnectdb(self->dsn); + Py_END_ALLOW_THREADS; + Dprintf("conn_connect: new postgresql connection at %p", pgconn); + } + else { + Py_BEGIN_ALLOW_THREADS; + self->pgconn = pgconn = PQconnectStart(self->dsn); + Py_END_ALLOW_THREADS; + Dprintf("conn_connect: new green postgresql connection at %p", pgconn); + } if (pgconn == NULL) { @@ -275,74 +556,405 @@ { Dprintf("conn_connect: PQconnectdb(%s) returned BAD", self->dsn); PyErr_SetString(OperationalError, PQerrorMessage(pgconn)); - PQfinish(pgconn); return -1; } PQsetNoticeProcessor(pgconn, conn_notice_callback, (void*)self); - if (conn_setup(self, pgconn) == -1) + /* if the connection is green, wait to finish connection */ + if (green) { + if (0 != psyco_wait(self)) { + return -1; + } + } + + /* From here the connection is considered ready: with the new status, + * poll() will use PQisBusy instead of PQconnectPoll. + */ + self->status = CONN_STATUS_READY; + + if (conn_setup(self, self->pgconn) == -1) { return -1; + } + + return 0; +} + +static int +_conn_async_connect(connectionObject *self) +{ + PGconn *pgconn; - if (PQsetnonblocking(pgconn, 1) != 0) { - Dprintf("conn_connect: PQsetnonblocking() FAILED"); - PyErr_SetString(OperationalError, "PQsetnonblocking() failed"); - PQfinish(pgconn); + self->pgconn = pgconn = PQconnectStart(self->dsn); + + Dprintf("conn_connect: new postgresql connection at %p", pgconn); + + if (pgconn == NULL) + { + Dprintf("conn_connect: PQconnectStart(%s) FAILED", self->dsn); + PyErr_SetString(OperationalError, "PQconnectStart() failed"); + return -1; + } + else if (PQstatus(pgconn) == CONNECTION_BAD) + { + Dprintf("conn_connect: PQconnectdb(%s) returned BAD", self->dsn); + PyErr_SetString(OperationalError, PQerrorMessage(pgconn)); return -1; } -#ifdef HAVE_PQPROTOCOL3 - self->protocol = PQprotocolVersion(pgconn); -#else - self->protocol = 2; -#endif - Dprintf("conn_connect: using protocol %d", self->protocol); + PQsetNoticeProcessor(pgconn, conn_notice_callback, (void*)self); - self->server_version = (int)PQserverVersion(pgconn); + /* The connection will be completed banging on poll(): + * First with _conn_poll_connecting() that will finish connection, + * then with _conn_poll_setup_async() that will do the same job + * of setup_async(). */ - self->pgconn = pgconn; return 0; } +int +conn_connect(connectionObject *self, long int async) +{ + if (async == 1) { + Dprintf("con_connect: connecting in ASYNC mode"); + return _conn_async_connect(self); + } + else { + Dprintf("con_connect: connecting in SYNC mode"); + return _conn_sync_connect(self); + } +} + + +/* poll during a connection attempt until the connection has established. */ + +static int +_conn_poll_connecting(connectionObject *self) +{ + int res = PSYCO_POLL_ERROR; + + Dprintf("conn_poll: poll connecting"); + switch (PQconnectPoll(self->pgconn)) { + case PGRES_POLLING_OK: + res = PSYCO_POLL_OK; + break; + case PGRES_POLLING_READING: + res = PSYCO_POLL_READ; + break; + case PGRES_POLLING_WRITING: + res = PSYCO_POLL_WRITE; + break; + case PGRES_POLLING_FAILED: + case PGRES_POLLING_ACTIVE: + PyErr_SetString(OperationalError, "asynchronous connection failed"); + res = PSYCO_POLL_ERROR; + break; + } + + return res; +} + + +/* Advance to the next state after an attempt of flushing output */ + +static int +_conn_poll_advance_write(connectionObject *self, int flush) +{ + int res; + + Dprintf("conn_poll: poll writing"); + switch (flush) { + case 0: /* success */ + /* we've finished pushing the query to the server. Let's start + reading the results. */ + Dprintf("conn_poll: async_status -> ASYNC_READ"); + self->async_status = ASYNC_READ; + res = PSYCO_POLL_READ; + break; + case 1: /* would block */ + res = PSYCO_POLL_WRITE; + break; + case -1: /* error */ + PyErr_SetString(OperationalError, PQerrorMessage(self->pgconn)); + res = PSYCO_POLL_ERROR; + break; + default: + Dprintf("conn_poll: unexpected result from flush: %d", flush); + res = PSYCO_POLL_ERROR; + break; + } + return res; +} + +/* Advance to the next state after a call to a pq_is_busy* function */ +static int +_conn_poll_advance_read(connectionObject *self, int busy) +{ + int res; + + Dprintf("conn_poll: poll reading"); + switch (busy) { + case 0: /* result is ready */ + res = PSYCO_POLL_OK; + Dprintf("conn_poll: async_status -> ASYNC_DONE"); + self->async_status = ASYNC_DONE; + break; + case 1: /* result not ready: fd would block */ + res = PSYCO_POLL_READ; + break; + case -1: /* ouch, error */ + res = PSYCO_POLL_ERROR; + break; + default: + Dprintf("conn_poll: unexpected result from pq_is_busy: %d", busy); + res = PSYCO_POLL_ERROR; + break; + } + return res; +} + +/* Poll the connection for the send query/retrieve result phase + + Advance the async_status (usually going WRITE -> READ -> DONE) but don't + mess with the connection status. */ + +static int +_conn_poll_query(connectionObject *self) +{ + int res = PSYCO_POLL_ERROR; + + switch (self->async_status) { + case ASYNC_WRITE: + Dprintf("conn_poll: async_status = ASYNC_WRITE"); + res = _conn_poll_advance_write(self, PQflush(self->pgconn)); + break; + + case ASYNC_READ: + Dprintf("conn_poll: async_status = ASYNC_READ"); + if (self->async) { + res = _conn_poll_advance_read(self, pq_is_busy(self)); + } + else { + /* we are a green connection being polled as result of a query. + this means that our caller has the lock and we are being called + from the callback. If we tried to acquire the lock now it would + be a deadlock. */ + res = _conn_poll_advance_read(self, pq_is_busy_locked(self)); + } + break; + + case ASYNC_DONE: + Dprintf("conn_poll: async_status = ASYNC_DONE"); + /* We haven't asked anything: just check for notifications. */ + res = _conn_poll_advance_read(self, pq_is_busy(self)); + break; + + default: + Dprintf("conn_poll: in unexpected async status: %d", + self->async_status); + res = PSYCO_POLL_ERROR; + break; + } + + return res; +} + +/* Advance to the next state during an async connection setup + * + * If the connection is green, this is performed by the regular + * sync code so the queries are sent by conn_setup() while in + * CONN_STATUS_READY state. + */ +static int +_conn_poll_setup_async(connectionObject *self) +{ + int res = PSYCO_POLL_ERROR; + PGresult *pgres; + + switch (self->status) { + case CONN_STATUS_CONNECTING: + /* Set the connection to nonblocking now. */ + if (pq_set_non_blocking(self, 1) != 0) { + break; + } + + self->equote = conn_get_standard_conforming_strings(self->pgconn); + self->protocol = conn_get_protocol_version(self->pgconn); + self->server_version = conn_get_server_version(self->pgconn); + if (3 != self->protocol) { + PyErr_SetString(InterfaceError, "only protocol 3 supported"); + break; + } + if (0 > conn_read_encoding(self, self->pgconn)) { + break; + } + self->cancel = conn_get_cancel(self->pgconn); + if (self->cancel == NULL) { + PyErr_SetString(OperationalError, "can't get cancellation key"); + break; + } + + /* asynchronous connections always use isolation level 0, the user is + * expected to manage the transactions himself, by sending + * (asynchronously) BEGIN and COMMIT statements. + */ + self->autocommit = 1; + + /* If the datestyle is ISO or anything else good, + * we can skip the CONN_STATUS_DATESTYLE step. */ + if (!conn_is_datestyle_ok(self->pgconn)) { + Dprintf("conn_poll: status -> CONN_STATUS_DATESTYLE"); + self->status = CONN_STATUS_DATESTYLE; + if (0 == pq_send_query(self, psyco_datestyle)) { + PyErr_SetString(OperationalError, PQerrorMessage(self->pgconn)); + break; + } + Dprintf("conn_poll: async_status -> ASYNC_WRITE"); + self->async_status = ASYNC_WRITE; + res = PSYCO_POLL_WRITE; + } + else { + Dprintf("conn_poll: status -> CONN_STATUS_READY"); + self->status = CONN_STATUS_READY; + res = PSYCO_POLL_OK; + } + break; + + case CONN_STATUS_DATESTYLE: + res = _conn_poll_query(self); + if (res == PSYCO_POLL_OK) { + res = PSYCO_POLL_ERROR; + pgres = pq_get_last_result(self); + if (pgres == NULL || PQresultStatus(pgres) != PGRES_COMMAND_OK ) { + PyErr_SetString(OperationalError, "can't set datestyle to ISO"); + break; + } + CLEARPGRES(pgres); + + Dprintf("conn_poll: status -> CONN_STATUS_READY"); + self->status = CONN_STATUS_READY; + res = PSYCO_POLL_OK; + } + break; + } + return res; +} + + +/* conn_poll - Main polling switch + * + * The function is called in all the states and connection types and invokes + * the right "next step". + */ + +int +conn_poll(connectionObject *self) +{ + int res = PSYCO_POLL_ERROR; + Dprintf("conn_poll: status = %d", self->status); + + switch (self->status) { + case CONN_STATUS_SETUP: + Dprintf("conn_poll: status -> CONN_STATUS_CONNECTING"); + self->status = CONN_STATUS_CONNECTING; + res = PSYCO_POLL_WRITE; + break; + + case CONN_STATUS_CONNECTING: + res = _conn_poll_connecting(self); + if (res == PSYCO_POLL_OK && self->async) { + res = _conn_poll_setup_async(self); + } + break; + + case CONN_STATUS_DATESTYLE: + res = _conn_poll_setup_async(self); + break; + + case CONN_STATUS_READY: + case CONN_STATUS_BEGIN: + case CONN_STATUS_PREPARED: + res = _conn_poll_query(self); + + if (res == PSYCO_POLL_OK && self->async && self->async_cursor) { + /* An async query has just finished: parse the tuple in the + * target cursor. */ + cursorObject *curs; + PyObject *py_curs = PyWeakref_GetObject(self->async_cursor); + if (Py_None == py_curs) { + pq_clear_async(self); + PyErr_SetString(InterfaceError, + "the asynchronous cursor has disappeared"); + res = PSYCO_POLL_ERROR; + break; + } + + curs = (cursorObject *)py_curs; + IFCLEARPGRES(curs->pgres); + curs->pgres = pq_get_last_result(self); + + /* fetch the tuples (if there are any) and build the result. We + * don't care if pq_fetch return 0 or 1, but if there was an error, + * we want to signal it to the caller. */ + if (pq_fetch(curs) == -1) { + res = PSYCO_POLL_ERROR; + } + + /* We have finished with our async_cursor */ + Py_CLEAR(self->async_cursor); + } + break; + + default: + Dprintf("conn_poll: in unexpected state"); + res = PSYCO_POLL_ERROR; + } + + return res; +} + /* conn_close - do anything needed to shut down the connection */ void conn_close(connectionObject *self) { + if (self->closed) { + return; + } + /* sets this connection as closed even for other threads; also note that we need to check the value of pgconn, because we get called even when the connection fails! */ Py_BEGIN_ALLOW_THREADS; pthread_mutex_lock(&self->lock); - if (self->closed == 0) - self->closed = 1; + /* We used to call pq_abort_locked here, but the idea of issuing a + * rollback on close/GC has been considered inappropriate. + * + * Dropping the connection on the server has the same effect as the + * transaction is automatically rolled back. Some middleware, such as + * PgBouncer, have problem with connections closed in the middle of the + * transaction though: to avoid these problems the transaction should be + * closed only in status CONN_STATUS_READY. + */ + + self->closed = 1; - /* execute a forced rollback on the connection (but don't check the - result, we're going to close the pq connection anyway */ - if (self->pgconn && self->closed == 1) { - PGresult *pgres = NULL; - char *error = NULL; - - if (pq_abort_locked(self, &pgres, &error) < 0) { - IFCLEARPGRES(pgres); - if (error) - free (error); - } - } if (self->pgconn) { PQfinish(self->pgconn); - Dprintf("conn_close: PQfinish called"); self->pgconn = NULL; - } - + Dprintf("conn_close: PQfinish called"); + PQfreeCancel(self->cancel); + self->cancel = NULL; + } + pthread_mutex_unlock(&self->lock); Py_END_ALLOW_THREADS; } /* conn_commit - commit on a connection */ -int +RAISES_NEG int conn_commit(connectionObject *self) { int res; @@ -353,7 +965,7 @@ /* conn_rollback - rollback a connection */ -int +RAISES_NEG int conn_rollback(connectionObject *self) { int res; @@ -362,82 +974,332 @@ return res; } -/* conn_switch_isolation_level - switch isolation level on the connection */ +RAISES_NEG int +conn_set_session(connectionObject *self, + const char *isolevel, const char *readonly, const char *deferrable, + int autocommit) +{ + PGresult *pgres = NULL; + char *error = NULL; + int res = -1; + + Py_BEGIN_ALLOW_THREADS; + pthread_mutex_lock(&self->lock); + + if (isolevel) { + Dprintf("conn_set_session: setting isolation to %s", isolevel); + if ((res = pq_set_guc_locked(self, + "default_transaction_isolation", isolevel, + &pgres, &error, &_save))) { + goto endlock; + } + } + + if (readonly) { + Dprintf("conn_set_session: setting read only to %s", readonly); + if ((res = pq_set_guc_locked(self, + "default_transaction_read_only", readonly, + &pgres, &error, &_save))) { + goto endlock; + } + } + + if (deferrable) { + Dprintf("conn_set_session: setting deferrable to %s", deferrable); + if ((res = pq_set_guc_locked(self, + "default_transaction_deferrable", deferrable, + &pgres, &error, &_save))) { + goto endlock; + } + } + + if (self->autocommit != autocommit) { + Dprintf("conn_set_session: setting autocommit to %d", autocommit); + self->autocommit = autocommit; + } + + res = 0; + +endlock: + pthread_mutex_unlock(&self->lock); + Py_END_ALLOW_THREADS; + + if (res < 0) { + pq_complete_error(self, &pgres, &error); + } + + return res; +} int +conn_set_autocommit(connectionObject *self, int value) +{ + Py_BEGIN_ALLOW_THREADS; + pthread_mutex_lock(&self->lock); + + self->autocommit = value; + + pthread_mutex_unlock(&self->lock); + Py_END_ALLOW_THREADS; + + return 0; +} + +/* conn_switch_isolation_level - switch isolation level on the connection */ + +RAISES_NEG int conn_switch_isolation_level(connectionObject *self, int level) { PGresult *pgres = NULL; char *error = NULL; - int res = 0; + int curr_level; + int ret = -1; - /* if the current isolation level is equal to the requested one don't switch */ - if (self->isolation_level == level) return 0; + /* use only supported levels on older PG versions */ + if (self->server_version < 80000) { + if (level == ISOLATION_LEVEL_READ_UNCOMMITTED) + level = ISOLATION_LEVEL_READ_COMMITTED; + else if (level == ISOLATION_LEVEL_REPEATABLE_READ) + level = ISOLATION_LEVEL_SERIALIZABLE; + } + + if (-1 == (curr_level = conn_get_isolation_level(self))) { + return -1; + } + + if (curr_level == level) { + /* no need to change level */ + return 0; + } + + /* Emulate the previous semantic of set_isolation_level() using the + * functions currently available. */ Py_BEGIN_ALLOW_THREADS; pthread_mutex_lock(&self->lock); - /* if the current isolation level is > 0 we need to abort the current - transaction before changing; that all folks! */ - if (self->isolation_level != level && self->isolation_level > 0) { - res = pq_abort_locked(self, &pgres, &error); + /* terminate the current transaction if any */ + if ((ret = pq_abort_locked(self, &pgres, &error, &_save))) { + goto endlock; + } + + if (level == 0) { + if ((ret = pq_set_guc_locked(self, + "default_transaction_isolation", "default", + &pgres, &error, &_save))) { + goto endlock; + } + self->autocommit = 1; + } + else { + /* find the name of the requested level */ + const IsolationLevel *isolevel = conn_isolevels; + while ((++isolevel)->name) { + if (level == isolevel->value) { + break; + } + } + if (!isolevel->name) { + ret = -1; + error = strdup("bad isolation level value"); + goto endlock; + } + + if ((ret = pq_set_guc_locked(self, + "default_transaction_isolation", isolevel->name, + &pgres, &error, &_save))) { + goto endlock; + } + self->autocommit = 0; } - self->isolation_level = level; Dprintf("conn_switch_isolation_level: switched to level %d", level); +endlock: pthread_mutex_unlock(&self->lock); Py_END_ALLOW_THREADS; - if (res < 0) + if (ret < 0) { pq_complete_error(self, &pgres, &error); + } - return res; + return ret; } + /* conn_set_client_encoding - switch client encoding on connection */ -int +RAISES_NEG int conn_set_client_encoding(connectionObject *self, const char *enc) { PGresult *pgres = NULL; char *error = NULL; - char query[48]; - int res = 0; + int res = -1; + char *codec = NULL; + char *clean_enc = NULL; /* If the current encoding is equal to the requested one we don't issue any query to the backend */ if (strcmp(self->encoding, enc) == 0) return 0; - /* TODO: check for async query here and raise error if necessary */ + /* We must know what python codec this encoding is. */ + if (0 > clear_encoding_name(enc, &clean_enc)) { goto exit; } + if (0 > conn_encoding_to_codec(clean_enc, &codec)) { goto exit; } Py_BEGIN_ALLOW_THREADS; pthread_mutex_lock(&self->lock); - /* set encoding, no encoding string is longer than 24 bytes */ - PyOS_snprintf(query, 47, "SET client_encoding = '%s'", enc); - /* abort the current transaction, to set the encoding ouside of transactions */ - res = pq_abort_locked(self, &pgres, &error); + if ((res = pq_abort_locked(self, &pgres, &error, &_save))) { + goto endlock; + } - if (res == 0) { - res = pq_execute_command_locked(self, query, &pgres, &error); - if (res == 0) { - /* no error, we can proceeed and store the new encoding */ - if (self->encoding) free(self->encoding); - self->encoding = strdup(enc); - Dprintf("conn_set_client_encoding: set encoding to %s", - self->encoding); - } + if ((res = pq_set_guc_locked(self, "client_encoding", clean_enc, + &pgres, &error, &_save))) { + goto endlock; + } + + /* no error, we can proceeed and store the new encoding */ + { + char *tmp = self->encoding; + self->encoding = clean_enc; + PyMem_Free(tmp); + clean_enc = NULL; + } + + /* Store the python codec too. */ + { + char *tmp = self->codec; + self->codec = codec; + PyMem_Free(tmp); + codec = NULL; } + Dprintf("conn_set_client_encoding: set encoding to %s (codec: %s)", + self->encoding, self->codec); +endlock: pthread_mutex_unlock(&self->lock); Py_END_ALLOW_THREADS; if (res < 0) pq_complete_error(self, &pgres, &error); +exit: + PyMem_Free(clean_enc); + PyMem_Free(codec); + return res; } + + +/* conn_tpc_begin -- begin a two-phase commit. + * + * The state of a connection in the middle of a TPC is exactly the same + * of a normal transaction, in CONN_STATUS_BEGIN, but with the tpc_xid + * member set to the xid used. This allows to reuse all the code paths used + * in regular transactions, as PostgreSQL won't even know we are in a TPC + * until PREPARE. */ + +RAISES_NEG int +conn_tpc_begin(connectionObject *self, XidObject *xid) +{ + PGresult *pgres = NULL; + char *error = NULL; + + Dprintf("conn_tpc_begin: starting transaction"); + + Py_BEGIN_ALLOW_THREADS; + pthread_mutex_lock(&self->lock); + + if (pq_begin_locked(self, &pgres, &error, &_save) < 0) { + pthread_mutex_unlock(&(self->lock)); + Py_BLOCK_THREADS; + pq_complete_error(self, &pgres, &error); + return -1; + } + + pthread_mutex_unlock(&self->lock); + Py_END_ALLOW_THREADS; + + /* The transaction started ok, let's store this xid. */ + Py_INCREF(xid); + self->tpc_xid = xid; + + return 0; +} + + +/* conn_tpc_command -- run one of the TPC-related PostgreSQL commands. + * + * The function doesn't change the connection state as it can be used + * for many commands and for recovered transactions. */ + +RAISES_NEG int +conn_tpc_command(connectionObject *self, const char *cmd, XidObject *xid) +{ + PGresult *pgres = NULL; + char *error = NULL; + PyObject *tid = NULL; + const char *ctid; + int rv = -1; + + Dprintf("conn_tpc_command: %s", cmd); + + /* convert the xid into PostgreSQL transaction id while keeping the GIL */ + if (!(tid = psycopg_ensure_bytes(xid_get_tid(xid)))) { goto exit; } + if (!(ctid = Bytes_AsString(tid))) { goto exit; } + + Py_BEGIN_ALLOW_THREADS; + pthread_mutex_lock(&self->lock); + + if (0 > (rv = pq_tpc_command_locked(self, cmd, ctid, + &pgres, &error, &_save))) { + pthread_mutex_unlock(&self->lock); + Py_BLOCK_THREADS; + pq_complete_error(self, &pgres, &error); + goto exit; + } + + pthread_mutex_unlock(&self->lock); + Py_END_ALLOW_THREADS; + +exit: + Py_XDECREF(tid); + return rv; +} + +/* conn_tpc_recover -- return a list of pending TPC Xid */ + +PyObject * +conn_tpc_recover(connectionObject *self) +{ + int status; + PyObject *xids = NULL; + PyObject *rv = NULL; + PyObject *tmp; + + /* store the status to restore it. */ + status = self->status; + + if (!(xids = xid_recover((PyObject *)self))) { goto exit; } + + if (status == CONN_STATUS_READY && self->status == CONN_STATUS_BEGIN) { + /* recover began a transaction: let's abort it. */ + if (!(tmp = PyObject_CallMethod((PyObject *)self, "rollback", NULL))) { + goto exit; + } + Py_DECREF(tmp); + } + + /* all fine */ + rv = xids; + xids = NULL; + +exit: + Py_XDECREF(xids); + + return rv; + +} diff -Nru psycopg2-2.0.13/psycopg/connection_type.c psycopg2-2.4.5/psycopg/connection_type.c --- psycopg2-2.0.13/psycopg/connection_type.c 2009-10-04 21:32:35.000000000 +0000 +++ psycopg2-2.4.5/psycopg/connection_type.c 2012-03-28 21:09:15.000000000 +0000 @@ -1,46 +1,48 @@ /* connection_type.c - python interface to connection objects * - * Copyright (C) 2003 Federico Di Gregorio + * Copyright (C) 2003-2010 Federico Di Gregorio * * This file is part of psycopg. * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2, - * or (at your option) any later version. + * psycopg2 is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * In addition, as a special exception, the copyright holders give + * permission to link this program with the OpenSSL library (or with + * modified versions of OpenSSL that use the same license as OpenSSL), + * and distribute linked combinations including the two. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You must obey the GNU Lesser General Public License in all respects for + * all of the code used other than OpenSSL. + * + * psycopg2 is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. */ -#define PY_SSIZE_T_CLEAN -#include -#include -#include - -#include -#include - #define PSYCOPG_MODULE -#include "psycopg/config.h" -#include "psycopg/python.h" #include "psycopg/psycopg.h" + #include "psycopg/connection.h" #include "psycopg/cursor.h" +#include "psycopg/pqpath.h" #include "psycopg/lobject.h" +#include "psycopg/green.h" +#include "psycopg/xid.h" + +#include +#include + /** DBAPI methods **/ /* cursor method - allocate a new cursor */ #define psyco_conn_cursor_doc \ -"cursor(cursor_factory=extensions.cursor) -- new cursor\n\n" \ +"cursor(name=None, cursor_factory=extensions.cursor, withhold=None) -- new cursor\n\n" \ "Return a new cursor.\n\nThe ``cursor_factory`` argument can be used to\n" \ "create non-standard cursors by passing a class different from the\n" \ "default. Note that the new class *should* be a sub-class of\n" \ @@ -51,17 +53,40 @@ psyco_conn_cursor(connectionObject *self, PyObject *args, PyObject *keywds) { const char *name = NULL; - PyObject *obj, *factory = NULL; + PyObject *obj, *factory = NULL, *withhold = NULL; - static char *kwlist[] = {"name", "cursor_factory", NULL}; + static char *kwlist[] = {"name", "cursor_factory", "withhold", NULL}; - if (!PyArg_ParseTupleAndKeywords(args, keywds, "|sO", kwlist, - &name, &factory)) { + if (!PyArg_ParseTupleAndKeywords(args, keywds, "|sOO", kwlist, + &name, &factory, &withhold)) { return NULL; } + if (withhold != NULL) { + if (PyObject_IsTrue(withhold) && name == NULL) { + PyErr_SetString(ProgrammingError, + "'withhold=True can be specified only for named cursors"); + return NULL; + } + } + EXC_IF_CONN_CLOSED(self); + if (self->status != CONN_STATUS_READY && + self->status != CONN_STATUS_BEGIN && + self->status != CONN_STATUS_PREPARED) { + PyErr_SetString(OperationalError, + "asynchronous connection attempt underway"); + return NULL; + } + + if (name != NULL && self->async == 1) { + PyErr_SetString(ProgrammingError, + "asynchronous connections " + "cannot produce named cursors"); + return NULL; + } + Dprintf("psyco_conn_cursor: new cursor for connection at %p", self); Dprintf("psyco_conn_cursor: parameters: name = %s", name); @@ -69,7 +94,7 @@ if (name) obj = PyObject_CallFunction(factory, "Os", self, name); else - obj = PyObject_CallFunction(factory, "O", self); + obj = PyObject_CallFunctionObjArgs(factory, self, NULL); if (obj == NULL) return NULL; if (PyObject_IsInstance(obj, (PyObject *)&cursorType) == 0) { @@ -78,10 +103,13 @@ Py_DECREF(obj); return NULL; } + + if (withhold != NULL && PyObject_IsTrue(withhold)) + ((cursorObject*)obj)->withhold = 1; Dprintf("psyco_conn_cursor: new cursor at %p: refcnt = " FORMAT_CODE_PY_SSIZE_T, - obj, obj->ob_refcnt + obj, Py_REFCNT(obj) ); return obj; } @@ -94,10 +122,6 @@ static PyObject * psyco_conn_close(connectionObject *self, PyObject *args) { - EXC_IF_CONN_CLOSED(self); - - if (!PyArg_ParseTuple(args, "")) return NULL; - Dprintf("psyco_conn_close: closing connection at %p", self); conn_close(self); Dprintf("psyco_conn_close: connection at %p closed", self); @@ -115,8 +139,8 @@ psyco_conn_commit(connectionObject *self, PyObject *args) { EXC_IF_CONN_CLOSED(self); - - if (!PyArg_ParseTuple(args, "")) return NULL; + EXC_IF_CONN_ASYNC(self, commit); + EXC_IF_TPC_BEGIN(self, commit); if (conn_commit(self) < 0) return NULL; @@ -135,8 +159,8 @@ psyco_conn_rollback(connectionObject *self, PyObject *args) { EXC_IF_CONN_CLOSED(self); - - if (!PyArg_ParseTuple(args, "")) return NULL; + EXC_IF_CONN_ASYNC(self, rollback); + EXC_IF_TPC_BEGIN(self, rollback); if (conn_rollback(self) < 0) return NULL; @@ -146,8 +170,424 @@ } +#define psyco_conn_xid_doc \ +"xid(format_id, gtrid, bqual) -- create a transaction identifier." + +static PyObject * +psyco_conn_xid(connectionObject *self, PyObject *args, PyObject *kwargs) +{ + EXC_IF_CONN_CLOSED(self); + EXC_IF_TPC_NOT_SUPPORTED(self); + + return PyObject_Call((PyObject *)&XidType, args, kwargs); +} + + +#define psyco_conn_tpc_begin_doc \ +"tpc_begin(xid) -- begin a TPC transaction with given transaction ID xid." + +static PyObject * +psyco_conn_tpc_begin(connectionObject *self, PyObject *args) +{ + PyObject *rv = NULL; + XidObject *xid = NULL; + PyObject *oxid; + + EXC_IF_CONN_CLOSED(self); + EXC_IF_CONN_ASYNC(self, tpc_begin); + EXC_IF_TPC_NOT_SUPPORTED(self); + EXC_IF_IN_TRANSACTION(self, tpc_begin); + + if (!PyArg_ParseTuple(args, "O", &oxid)) { + goto exit; + } + + if (NULL == (xid = xid_ensure(oxid))) { + goto exit; + } + + /* two phase commit and autocommit make no point */ + if (self->autocommit) { + PyErr_SetString(ProgrammingError, + "tpc_begin can't be called in autocommit mode"); + goto exit; + } + + if (conn_tpc_begin(self, xid) < 0) { + goto exit; + } + + Py_INCREF(Py_None); + rv = Py_None; + +exit: + Py_XDECREF(xid); + return rv; +} + + +#define psyco_conn_tpc_prepare_doc \ +"tpc_prepare() -- perform the first phase of a two-phase transaction." + +static PyObject * +psyco_conn_tpc_prepare(connectionObject *self, PyObject *args) +{ + EXC_IF_CONN_CLOSED(self); + EXC_IF_CONN_ASYNC(self, tpc_prepare); + EXC_IF_TPC_PREPARED(self, tpc_prepare); + + if (NULL == self->tpc_xid) { + PyErr_SetString(ProgrammingError, + "prepare must be called inside a two-phase transaction"); + return NULL; + } + + if (0 > conn_tpc_command(self, "PREPARE TRANSACTION", self->tpc_xid)) { + return NULL; + } + + /* transaction prepared: set the state so that no operation + * can be performed until commit. */ + self->status = CONN_STATUS_PREPARED; + + Py_INCREF(Py_None); + return Py_None; +} + + +/* the type of conn_commit/conn_rollback */ +typedef int (*_finish_f)(connectionObject *self); + +/* Implement tpc_commit/tpc_rollback. + * + * This is a common framework performing the chechs and state manipulation + * common to the two functions. + * + * Parameters are: + * - self, args: passed by Python + * - opc_f: the function to call in case of one-phase commit/rollback + * one of conn_commit/conn_rollback + * - tpc_cmd: the command to execute for a two-phase commit/rollback + * + * The function can be called in three cases: + * - If xid is specified, the status must be "ready"; + * issue the commit/rollback prepared. + * - if xid is not specified and status is "begin" with a xid, + * issue a normal commit/rollback. + * - if xid is not specified and status is "prepared", + * issue the commit/rollback prepared. + */ +static PyObject * +_psyco_conn_tpc_finish(connectionObject *self, PyObject *args, + _finish_f opc_f, char *tpc_cmd) +{ + PyObject *oxid = NULL; + XidObject *xid = NULL; + PyObject *rv = NULL; + + if (!PyArg_ParseTuple(args, "|O", &oxid)) { goto exit; } + + if (oxid) { + if (!(xid = xid_ensure(oxid))) { goto exit; } + } + + if (xid) { + /* committing/aborting a recovered transaction. */ + if (self->status != CONN_STATUS_READY) { + PyErr_SetString(ProgrammingError, + "tpc_commit/tpc_rollback with a xid " + "must be called outside a transaction"); + goto exit; + } + if (0 > conn_tpc_command(self, tpc_cmd, xid)) { + goto exit; + } + } else { + /* committing/aborting our own transaction. */ + if (!self->tpc_xid) { + PyErr_SetString(ProgrammingError, + "tpc_commit/tpc_rollback with no parameter " + "must be called in a two-phase transaction"); + goto exit; + } + + switch (self->status) { + case CONN_STATUS_BEGIN: + if (0 > opc_f(self)) { goto exit; } + break; + + case CONN_STATUS_PREPARED: + if (0 > conn_tpc_command(self, tpc_cmd, self->tpc_xid)) { + goto exit; + } + break; + + default: + PyErr_SetString(InterfaceError, + "unexpected state in tpc_commit/tpc_rollback"); + goto exit; + } + + Py_CLEAR(self->tpc_xid); + + /* connection goes ready */ + self->status = CONN_STATUS_READY; + } + + Py_INCREF(Py_None); + rv = Py_None; + +exit: + Py_XDECREF(xid); + return rv; +} + +#define psyco_conn_tpc_commit_doc \ +"tpc_commit([xid]) -- commit a transaction previously prepared." + +static PyObject * +psyco_conn_tpc_commit(connectionObject *self, PyObject *args) +{ + EXC_IF_CONN_CLOSED(self); + EXC_IF_CONN_ASYNC(self, tpc_commit); + EXC_IF_TPC_NOT_SUPPORTED(self); + + return _psyco_conn_tpc_finish(self, args, + conn_commit, "COMMIT PREPARED"); +} + +#define psyco_conn_tpc_rollback_doc \ +"tpc_rollback([xid]) -- abort a transaction previously prepared." + +static PyObject * +psyco_conn_tpc_rollback(connectionObject *self, PyObject *args) +{ + EXC_IF_CONN_CLOSED(self); + EXC_IF_CONN_ASYNC(self, tpc_rollback); + EXC_IF_TPC_NOT_SUPPORTED(self); + + return _psyco_conn_tpc_finish(self, args, + conn_rollback, "ROLLBACK PREPARED"); +} + +#define psyco_conn_tpc_recover_doc \ +"tpc_recover() -- returns a list of pending transaction IDs." + +static PyObject * +psyco_conn_tpc_recover(connectionObject *self, PyObject *args) +{ + EXC_IF_CONN_CLOSED(self); + EXC_IF_CONN_ASYNC(self, tpc_recover); + EXC_IF_TPC_PREPARED(self, tpc_recover); + EXC_IF_TPC_NOT_SUPPORTED(self); + + return conn_tpc_recover(self); +} + + #ifdef PSYCOPG_EXTENSIONS + +/* parse a python object into one of the possible isolation level values */ + +extern const IsolationLevel conn_isolevels[]; + +static const char * +_psyco_conn_parse_isolevel(connectionObject *self, PyObject *pyval) +{ + const IsolationLevel *isolevel = NULL; + + Py_INCREF(pyval); /* for ensure_bytes */ + + /* parse from one of the level constants */ + if (PyInt_Check(pyval)) { + long level = PyInt_AsLong(pyval); + if (level == -1 && PyErr_Occurred()) { goto exit; } + if (level < 1 || level > 4) { + PyErr_SetString(PyExc_ValueError, + "isolation_level must be between 1 and 4"); + goto exit; + } + + isolevel = conn_isolevels; + while ((++isolevel)->value != level) + ; /* continue */ + } + + /* parse from the string -- this includes "default" */ + else { + isolevel = conn_isolevels; + while ((++isolevel)->name) { + if (!(pyval = psycopg_ensure_bytes(pyval))) { + goto exit; + } + if (0 == strcasecmp(isolevel->name, Bytes_AS_STRING(pyval))) { + break; + } + } + if (!isolevel->name) { + char msg[256]; + snprintf(msg, sizeof(msg), + "bad value for isolation_level: '%s'", Bytes_AS_STRING(pyval)); + PyErr_SetString(PyExc_ValueError, msg); + } + } + + /* use only supported levels on older PG versions */ + if (isolevel && self->server_version < 80000) { + if (isolevel->value == ISOLATION_LEVEL_READ_UNCOMMITTED + || isolevel->value == ISOLATION_LEVEL_REPEATABLE_READ) { + ++isolevel; + } + } + +exit: + Py_XDECREF(pyval); + + return isolevel ? isolevel->name : NULL; +} + +/* convert True/False/"default" into a C string */ + +static const char * +_psyco_conn_parse_onoff(PyObject *pyval) +{ + int istrue = PyObject_IsTrue(pyval); + if (-1 == istrue) { return NULL; } + if (istrue) { + int cmp; + PyObject *pydef; + if (!(pydef = Text_FromUTF8("default"))) { return NULL; } + cmp = PyObject_RichCompareBool(pyval, pydef, Py_EQ); + Py_DECREF(pydef); + if (-1 == cmp) { return NULL; } + return cmp ? "default" : "on"; + } + else { + return "off"; + } +} + +/* set_session - set default transaction characteristics */ + +#define psyco_conn_set_session_doc \ +"set_session(...) -- Set one or more parameters for the next transactions.\n\n" \ +"Accepted arguments are 'isolation_level', 'readonly', 'deferrable', 'autocommit'." + +static PyObject * +psyco_conn_set_session(connectionObject *self, PyObject *args, PyObject *kwargs) +{ + PyObject *isolevel = Py_None; + PyObject *readonly = Py_None; + PyObject *deferrable = Py_None; + PyObject *autocommit = Py_None; + + const char *c_isolevel = NULL; + const char *c_readonly = NULL; + const char *c_deferrable = NULL; + int c_autocommit = self->autocommit; + + static char *kwlist[] = + {"isolation_level", "readonly", "deferrable", "autocommit", NULL}; + + EXC_IF_CONN_CLOSED(self); + EXC_IF_CONN_ASYNC(self, set_session); + EXC_IF_IN_TRANSACTION(self, set_session); + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|OOOO", kwlist, + &isolevel, &readonly, &deferrable, &autocommit)) { + return NULL; + } + + if (Py_None != isolevel) { + if (!(c_isolevel = _psyco_conn_parse_isolevel(self, isolevel))) { + return NULL; + } + } + + if (Py_None != readonly) { + if (!(c_readonly = _psyco_conn_parse_onoff(readonly))) { + return NULL; + } + } + if (Py_None != deferrable) { + if (self->server_version < 90100) { + PyErr_SetString(ProgrammingError, + "the 'deferrable' setting is only available" + " from PostgreSQL 9.1"); + return NULL; + } + if (!(c_deferrable = _psyco_conn_parse_onoff(deferrable))) { + return NULL; + } + } + if (Py_None != autocommit) { + c_autocommit = PyObject_IsTrue(autocommit); + if (-1 == c_autocommit) { return NULL; } + } + + if (0 > conn_set_session(self, + c_isolevel, c_readonly, c_deferrable, c_autocommit)) { + return NULL; + } + + Py_INCREF(Py_None); + return Py_None; +} + + +#define psyco_conn_autocommit_doc \ +"Set or return the autocommit status." + +static PyObject * +psyco_conn_autocommit_get(connectionObject *self) +{ + PyObject *ret; + ret = self->autocommit ? Py_True : Py_False; + Py_INCREF(ret); + return ret; +} + +BORROWED static PyObject * +_psyco_conn_autocommit_set_checks(connectionObject *self) +{ + /* wrapper to use the EXC_IF macros. + * return NULL in case of error, else whatever */ + EXC_IF_CONN_CLOSED(self); + EXC_IF_CONN_ASYNC(self, autocommit); + EXC_IF_IN_TRANSACTION(self, autocommit); + return Py_None; /* borrowed */ +} + +static int +psyco_conn_autocommit_set(connectionObject *self, PyObject *pyvalue) +{ + int value; + + if (!_psyco_conn_autocommit_set_checks(self)) { return -1; } + if (-1 == (value = PyObject_IsTrue(pyvalue))) { return -1; } + if (0 != conn_set_autocommit(self, value)) { return -1; } + + return 0; +} + + +/* isolation_level - return the current isolation level */ + +static PyObject * +psyco_conn_isolation_level_get(connectionObject *self) +{ + int rv; + + EXC_IF_CONN_CLOSED(self); + EXC_IF_TPC_PREPARED(self, set_isolation_level); + + rv = conn_get_isolation_level(self); + if (-1 == rv) { return NULL; } + return PyInt_FromLong((long)rv); +} + + /* set_isolation_level method - switch connection isolation level */ #define psyco_conn_set_isolation_level_doc \ @@ -159,22 +599,21 @@ int level = 1; EXC_IF_CONN_CLOSED(self); + EXC_IF_CONN_ASYNC(self, set_isolation_level); + EXC_IF_TPC_PREPARED(self, set_isolation_level); if (!PyArg_ParseTuple(args, "i", &level)) return NULL; - if (level < 0 || level > 2) { + if (level < 0 || level > 4) { PyErr_SetString(PyExc_ValueError, - "isolation level out of bounds (0,3)"); + "isolation level must be between 0 and 4"); return NULL; } if (conn_switch_isolation_level(self, level) < 0) { - PyErr_SetString(OperationalError, - PQerrorMessage(self->pgconn)); return NULL; } - Py_INCREF(Py_None); return Py_None; } @@ -187,33 +626,20 @@ static PyObject * psyco_conn_set_client_encoding(connectionObject *self, PyObject *args) { - const char *enc = NULL; - char *buffer; - size_t i, j; + const char *enc; + PyObject *rv = NULL; EXC_IF_CONN_CLOSED(self); + EXC_IF_CONN_ASYNC(self, set_client_encoding); + EXC_IF_TPC_PREPARED(self, set_client_encoding); if (!PyArg_ParseTuple(args, "s", &enc)) return NULL; - /* convert to upper case and remove '-' and '_' from string */ - buffer = PyMem_Malloc(strlen(enc)+1); - for (i=j=0 ; i < strlen(enc) ; i++) { - if (enc[i] == '_' || enc[i] == '-') - continue; - else - buffer[j++] = toupper(enc[i]); - } - buffer[j] = '\0'; - - if (conn_set_client_encoding(self, buffer) == 0) { - PyMem_Free(buffer); + if (conn_set_client_encoding(self, enc) >= 0) { Py_INCREF(Py_None); - return Py_None; - } - else { - PyMem_Free(buffer); - return NULL; + rv = Py_None; } + return rv; } /* get_transaction_status method - Get backend transaction status */ @@ -226,8 +652,6 @@ { EXC_IF_CONN_CLOSED(self); - if (!PyArg_ParseTuple(args, "")) return NULL; - return PyInt_FromLong((long)PQtransactionStatus(self->pgconn)); } @@ -257,14 +681,14 @@ Py_INCREF(Py_None); return Py_None; } - return PyString_FromString(val); + return conn_text_from_chars(self, val); } /* lobject method - allocate a new lobject */ #define psyco_conn_lobject_doc \ -"cursor(oid=0, mode=0, new_oid=0, new_file=None,\n" \ +"lobject(oid=0, mode=0, new_oid=0, new_file=None,\n" \ " lobject_factory=extensions.lobject) -- new lobject\n\n" \ "Return a new lobject.\n\nThe ``lobject_factory`` argument can be used\n" \ "to create non-standard lobjects by passing a class different from the\n" \ @@ -275,54 +699,38 @@ static PyObject * psyco_conn_lobject(connectionObject *self, PyObject *args, PyObject *keywds) { - Oid oid=InvalidOid, new_oid=InvalidOid; - char *smode = NULL, *new_file = NULL; - int mode=0; - PyObject *obj, *factory = NULL; + int oid = (int)InvalidOid, new_oid = (int)InvalidOid; + const char *new_file = NULL; + const char *smode = ""; + PyObject *factory = (PyObject *)&lobjectType; + PyObject *obj; static char *kwlist[] = {"oid", "mode", "new_oid", "new_file", "cursor_factory", NULL}; - + if (!PyArg_ParseTupleAndKeywords(args, keywds, "|izizO", kwlist, - &oid, &smode, &new_oid, &new_file, - &factory)) { + &oid, &smode, &new_oid, &new_file, + &factory)) { return NULL; } EXC_IF_CONN_CLOSED(self); + EXC_IF_CONN_ASYNC(self, lobject); + EXC_IF_GREEN(lobject); + EXC_IF_TPC_PREPARED(self, lobject); Dprintf("psyco_conn_lobject: new lobject for connection at %p", self); Dprintf("psyco_conn_lobject: parameters: oid = %d, mode = %s", oid, smode); Dprintf("psyco_conn_lobject: parameters: new_oid = %d, new_file = %s", new_oid, new_file); - - /* build a mode number out of the mode string: right now we only accept - 'r', 'w' and 'rw' (but note that 'w' implies 'rw' because PostgreSQL - backend does that. */ - if (smode) { - if (strncmp("rw", smode, 2) == 0) - mode = INV_READ+INV_WRITE; - else if (smode[0] == 'r') - mode = INV_READ; - else if (smode[0] == 'w') - mode = INV_WRITE; - else if (smode[0] == 'n') - mode = -1; - else { - PyErr_SetString(PyExc_TypeError, - "mode should be one of 'r', 'w' or 'rw'"); - return NULL; - } - } - if (factory == NULL) factory = (PyObject *)&lobjectType; if (new_file) - obj = PyObject_CallFunction(factory, "Oiiis", - self, oid, mode, new_oid, new_file); + obj = PyObject_CallFunction(factory, "Oisis", + self, oid, smode, new_oid, new_file); else - obj = PyObject_CallFunction(factory, "Oiii", - self, oid, mode, new_oid); + obj = PyObject_CallFunction(factory, "Oisi", + self, oid, smode, new_oid); if (obj == NULL) return NULL; if (PyObject_IsInstance(obj, (PyObject *)&lobjectType) == 0) { @@ -331,10 +739,10 @@ Py_DECREF(obj); return NULL; } - + Dprintf("psyco_conn_lobject: new lobject at %p: refcnt = " FORMAT_CODE_PY_SSIZE_T, - obj, obj->ob_refcnt); + obj, Py_REFCNT(obj)); return obj; } @@ -344,7 +752,7 @@ "get_backend_pid() -- Get backend process id." static PyObject * -psyco_conn_get_backend_pid(connectionObject *self) +psyco_conn_get_backend_pid(connectionObject *self, PyObject *args) { EXC_IF_CONN_CLOSED(self); @@ -357,11 +765,12 @@ "reset() -- Reset current connection to defaults." static PyObject * -psyco_conn_reset(connectionObject *self) +psyco_conn_reset(connectionObject *self, PyObject *args) { int res; EXC_IF_CONN_CLOSED(self); + EXC_IF_CONN_ASYNC(self, reset); if (pq_reset(self) < 0) return NULL; @@ -374,8 +783,6 @@ return Py_None; } -#endif - static PyObject * psyco_conn_get_exception(PyObject *self, void *closure) { @@ -385,6 +792,108 @@ return exception; } +static PyObject * +psyco_conn_poll(connectionObject *self, PyObject *args) +{ + int res; + + EXC_IF_CONN_CLOSED(self); + + res = conn_poll(self); + if (res != PSYCO_POLL_ERROR || !PyErr_Occurred()) { + return PyInt_FromLong(res); + } else { + /* There is an error and an exception is already in place */ + return NULL; + } +} + + +/* extension: fileno - return the file descriptor of the connection */ + +#define psyco_conn_fileno_doc \ +"fileno() -> int -- Return file descriptor associated to database connection." + +static PyObject * +psyco_conn_fileno(connectionObject *self, PyObject *args) +{ + long int socket; + + EXC_IF_CONN_CLOSED(self); + + socket = (long int)PQsocket(self->pgconn); + + return PyInt_FromLong(socket); +} + + +/* extension: isexecuting - check for asynchronous operations */ + +#define psyco_conn_isexecuting_doc \ +"isexecuting() -> bool -- Return True if the connection is " \ + "executing an asynchronous operation." + +static PyObject * +psyco_conn_isexecuting(connectionObject *self, PyObject *args) +{ + /* synchronous connections will always return False */ + if (self->async == 0) { + Py_INCREF(Py_False); + return Py_False; + } + + /* check if the connection is still being built */ + if (self->status != CONN_STATUS_READY) { + Py_INCREF(Py_True); + return Py_True; + } + + /* check if there is a query being executed */ + if (self->async_cursor != NULL) { + Py_INCREF(Py_True); + return Py_True; + } + + /* otherwise it's not executing */ + Py_INCREF(Py_False); + return Py_False; +} + + +/* extension: cancel - cancel the current operation */ + +#define psyco_conn_cancel_doc \ +"cancel() -- cancel the current operation" + +static PyObject * +psyco_conn_cancel(connectionObject *self, PyObject *args) +{ + char errbuf[256]; + + EXC_IF_CONN_CLOSED(self); + EXC_IF_TPC_PREPARED(self, cancel); + + /* do not allow cancellation while the connection is being built */ + Dprintf("psyco_conn_cancel: cancelling with key %p", self->cancel); + if (self->status != CONN_STATUS_READY && + self->status != CONN_STATUS_BEGIN) { + PyErr_SetString(OperationalError, + "asynchronous connection attempt underway"); + return NULL; + } + + if (PQcancel(self->cancel, errbuf, sizeof(errbuf)) == 0) { + Dprintf("psyco_conn_cancel: cancelling failed: %s", errbuf); + PyErr_SetString(OperationalError, errbuf); + return NULL; + } + Py_INCREF(Py_None); + return Py_None; +} + +#endif /* PSYCOPG_EXTENSIONS */ + + /** the connection object **/ @@ -394,18 +903,32 @@ {"cursor", (PyCFunction)psyco_conn_cursor, METH_VARARGS|METH_KEYWORDS, psyco_conn_cursor_doc}, {"close", (PyCFunction)psyco_conn_close, - METH_VARARGS, psyco_conn_close_doc}, + METH_NOARGS, psyco_conn_close_doc}, {"commit", (PyCFunction)psyco_conn_commit, - METH_VARARGS, psyco_conn_commit_doc}, + METH_NOARGS, psyco_conn_commit_doc}, {"rollback", (PyCFunction)psyco_conn_rollback, - METH_VARARGS, psyco_conn_rollback_doc}, + METH_NOARGS, psyco_conn_rollback_doc}, + {"xid", (PyCFunction)psyco_conn_xid, + METH_VARARGS|METH_KEYWORDS, psyco_conn_xid_doc}, + {"tpc_begin", (PyCFunction)psyco_conn_tpc_begin, + METH_VARARGS, psyco_conn_tpc_begin_doc}, + {"tpc_prepare", (PyCFunction)psyco_conn_tpc_prepare, + METH_NOARGS, psyco_conn_tpc_prepare_doc}, + {"tpc_commit", (PyCFunction)psyco_conn_tpc_commit, + METH_VARARGS, psyco_conn_tpc_commit_doc}, + {"tpc_rollback", (PyCFunction)psyco_conn_tpc_rollback, + METH_VARARGS, psyco_conn_tpc_rollback_doc}, + {"tpc_recover", (PyCFunction)psyco_conn_tpc_recover, + METH_NOARGS, psyco_conn_tpc_recover_doc}, #ifdef PSYCOPG_EXTENSIONS + {"set_session", (PyCFunction)psyco_conn_set_session, + METH_VARARGS|METH_KEYWORDS, psyco_conn_set_session_doc}, {"set_isolation_level", (PyCFunction)psyco_conn_set_isolation_level, METH_VARARGS, psyco_conn_set_isolation_level_doc}, {"set_client_encoding", (PyCFunction)psyco_conn_set_client_encoding, METH_VARARGS, psyco_conn_set_client_encoding_doc}, {"get_transaction_status", (PyCFunction)psyco_conn_get_transaction_status, - METH_VARARGS, psyco_conn_get_transaction_status_doc}, + METH_NOARGS, psyco_conn_get_transaction_status_doc}, {"get_parameter_status", (PyCFunction)psyco_conn_get_parameter_status, METH_VARARGS, psyco_conn_get_parameter_status_doc}, {"get_backend_pid", (PyCFunction)psyco_conn_get_backend_pid, @@ -414,6 +937,14 @@ METH_VARARGS|METH_KEYWORDS, psyco_conn_lobject_doc}, {"reset", (PyCFunction)psyco_conn_reset, METH_NOARGS, psyco_conn_reset_doc}, + {"poll", (PyCFunction)psyco_conn_poll, + METH_NOARGS, psyco_conn_lobject_doc}, + {"fileno", (PyCFunction)psyco_conn_fileno, + METH_NOARGS, psyco_conn_fileno_doc}, + {"isexecuting", (PyCFunction)psyco_conn_isexecuting, + METH_NOARGS, psyco_conn_isexecuting_doc}, + {"cancel", (PyCFunction)psyco_conn_cancel, + METH_NOARGS, psyco_conn_cancel_doc}, #endif {NULL} }; @@ -422,29 +953,28 @@ static struct PyMemberDef connectionObject_members[] = { #ifdef PSYCOPG_EXTENSIONS - {"closed", T_LONG, offsetof(connectionObject, closed), RO, + {"closed", T_LONG, offsetof(connectionObject, closed), READONLY, "True if the connection is closed."}, - {"isolation_level", T_LONG, - offsetof(connectionObject, isolation_level), RO, - "The current isolation level."}, - {"encoding", T_STRING, offsetof(connectionObject, encoding), RO, + {"encoding", T_STRING, offsetof(connectionObject, encoding), READONLY, "The current client encoding."}, - {"notices", T_OBJECT, offsetof(connectionObject, notice_list), RO}, - {"notifies", T_OBJECT, offsetof(connectionObject, notifies), RO}, - {"dsn", T_STRING, offsetof(connectionObject, dsn), RO, + {"notices", T_OBJECT, offsetof(connectionObject, notice_list), READONLY}, + {"notifies", T_OBJECT, offsetof(connectionObject, notifies), READONLY}, + {"dsn", T_STRING, offsetof(connectionObject, dsn), READONLY, "The current connection string."}, + {"async", T_LONG, offsetof(connectionObject, async), READONLY, + "True if the connection is asynchronous."}, {"status", T_INT, - offsetof(connectionObject, status), RO, + offsetof(connectionObject, status), READONLY, "The current transaction status."}, - {"string_types", T_OBJECT, offsetof(connectionObject, string_types), RO, + {"string_types", T_OBJECT, offsetof(connectionObject, string_types), READONLY, "A set of typecasters to convert textual values."}, - {"binary_types", T_OBJECT, offsetof(connectionObject, binary_types), RO, + {"binary_types", T_OBJECT, offsetof(connectionObject, binary_types), READONLY, "A set of typecasters to convert binary values."}, {"protocol_version", T_INT, - offsetof(connectionObject, protocol), RO, - "Protocol version (2 or 3) used for this connection."}, + offsetof(connectionObject, protocol), READONLY, + "Protocol version used for this connection. Currently always 3."}, {"server_version", T_INT, - offsetof(connectionObject, server_version), RO, + offsetof(connectionObject, server_version), READONLY, "Server version."}, #endif {NULL} @@ -465,6 +995,16 @@ EXCEPTION_GETTER(IntegrityError), EXCEPTION_GETTER(DataError), EXCEPTION_GETTER(NotSupportedError), +#ifdef PSYCOPG_EXTENSIONS + { "autocommit", + (getter)psyco_conn_autocommit_get, + (setter)psyco_conn_autocommit_set, + psyco_conn_autocommit_doc }, + { "isolation_level", + (getter)psyco_conn_isolation_level_get, + (setter)NULL, + "The current isolation level." }, +#endif {NULL} }; #undef EXCEPTION_GETTER @@ -472,40 +1012,39 @@ /* initialization and finalization methods */ static int -connection_setup(connectionObject *self, const char *dsn) +connection_setup(connectionObject *self, const char *dsn, long int async) { char *pos; - int res; + int res = -1; - Dprintf("connection_setup: init connection object at %p, refcnt = " - FORMAT_CODE_PY_SSIZE_T, - self, ((PyObject *)self)->ob_refcnt + Dprintf("connection_setup: init connection object at %p, " + "async %ld, refcnt = " FORMAT_CODE_PY_SSIZE_T, + self, async, Py_REFCNT(self) ); - self->dsn = strdup(dsn); - self->notice_list = PyList_New(0); - self->notifies = PyList_New(0); - self->closed = 0; - self->status = CONN_STATUS_READY; - self->critical = NULL; - self->async_cursor = NULL; - self->pgconn = NULL; - self->mark = 0; - self->string_types = PyDict_New(); - self->binary_types = PyDict_New(); - self->notice_pending = NULL; - self->encoding = NULL; + if (!(self->dsn = strdup(dsn))) { + PyErr_NoMemory(); + goto exit; + } + if (!(self->notice_list = PyList_New(0))) { goto exit; } + if (!(self->notifies = PyList_New(0))) { goto exit; } + self->async = async; + self->status = CONN_STATUS_SETUP; + self->async_status = ASYNC_DONE; + if (!(self->string_types = PyDict_New())) { goto exit; } + if (!(self->binary_types = PyDict_New())) { goto exit; } + /* other fields have been zeroed by tp_alloc */ pthread_mutex_init(&(self->lock), NULL); - if (conn_connect(self) != 0) { + if (conn_connect(self, async) != 0) { Dprintf("connection_init: FAILED"); - res = -1; + goto exit; } else { Dprintf("connection_setup: good connection object at %p, refcnt = " FORMAT_CODE_PY_SSIZE_T, - self, ((PyObject *)self)->ob_refcnt + self, Py_REFCNT(self) ); res = 0; } @@ -517,6 +1056,7 @@ *pos = 'x'; } +exit: return res; } @@ -524,17 +1064,23 @@ connection_dealloc(PyObject* obj) { connectionObject *self = (connectionObject *)obj; - + + if (self->weakreflist) { + PyObject_ClearWeakRefs(obj); + } + PyObject_GC_UnTrack(self); if (self->closed == 0) conn_close(self); - + conn_notice_clean(self); if (self->dsn) free(self->dsn); - if (self->encoding) free(self->encoding); + PyMem_Free(self->encoding); + PyMem_Free(self->codec); if (self->critical) free(self->critical); + Py_CLEAR(self->tpc_xid); Py_CLEAR(self->async_cursor); Py_CLEAR(self->notice_list); Py_CLEAR(self->notice_filter); @@ -546,21 +1092,23 @@ Dprintf("connection_dealloc: deleted connection object at %p, refcnt = " FORMAT_CODE_PY_SSIZE_T, - obj, obj->ob_refcnt + obj, Py_REFCNT(obj) ); - obj->ob_type->tp_free(obj); + Py_TYPE(obj)->tp_free(obj); } static int connection_init(PyObject *obj, PyObject *args, PyObject *kwds) { const char *dsn; + long int async = 0; + static char *kwlist[] = {"dsn", "async", NULL}; - if (!PyArg_ParseTuple(args, "s", &dsn)) + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|l", kwlist, &dsn, &async)) return -1; - return connection_setup((connectionObject *)obj, dsn); + return connection_setup((connectionObject *)obj, dsn, async); } static PyObject * @@ -586,6 +1134,7 @@ static int connection_traverse(connectionObject *self, visitproc visit, void *arg) { + Py_VISIT((PyObject *)(self->tpc_xid)); Py_VISIT(self->async_cursor); Py_VISIT(self->notice_list); Py_VISIT(self->notice_filter); @@ -606,8 +1155,7 @@ " ProgrammingError, IntegrityError, DataError, NotSupportedError" PyTypeObject connectionType = { - PyObject_HEAD_INIT(NULL) - 0, + PyVarObject_HEAD_INIT(NULL, 0) "psycopg2._psycopg.connection", sizeof(connectionObject), 0, @@ -628,14 +1176,16 @@ 0, /*tp_setattro*/ 0, /*tp_as_buffer*/ - Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_HAVE_GC, /*tp_flags*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC | + Py_TPFLAGS_HAVE_WEAKREFS, + /*tp_flags*/ connectionType_doc, /*tp_doc*/ (traverseproc)connection_traverse, /*tp_traverse*/ 0, /*tp_clear*/ 0, /*tp_richcompare*/ - 0, /*tp_weaklistoffset*/ + offsetof(connectionObject, weakreflist), /* tp_weaklistoffset */ 0, /*tp_iter*/ 0, /*tp_iternext*/ diff -Nru psycopg2-2.0.13/psycopg/cursor.h psycopg2-2.4.5/psycopg/cursor.h --- psycopg2-2.0.13/psycopg/cursor.h 2009-10-04 21:32:35.000000000 +0000 +++ psycopg2-2.4.5/psycopg/cursor.h 2012-03-28 21:09:15.000000000 +0000 @@ -1,32 +1,31 @@ /* cursor.h - definition for the psycopg cursor type * - * Copyright (C) 2003 Federico Di Gregorio + * Copyright (C) 2003-2010 Federico Di Gregorio * * This file is part of psycopg. * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2, - * or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * psycopg2 is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * In addition, as a special exception, the copyright holders give + * permission to link this program with the OpenSSL library (or with + * modified versions of OpenSSL that use the same license as OpenSSL), + * and distribute linked combinations including the two. + * + * You must obey the GNU Lesser General Public License in all respects for + * all of the code used other than OpenSSL. + * + * psycopg2 is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. */ #ifndef PSYCOPG_CURSOR_H #define PSYCOPG_CURSOR_H 1 -#define PY_SSIZE_T_CLEAN -#include -#include - -#include "psycopg/config.h" #include "psycopg/connection.h" #ifdef __cplusplus @@ -35,18 +34,20 @@ extern HIDDEN PyTypeObject cursorType; -typedef struct { +/* the typedef is forward-declared in psycopg.h */ +struct cursorObject { PyObject_HEAD connectionObject *conn; /* connection owning the cursor */ int closed:1; /* 1 if the cursor is closed */ int notuples:1; /* 1 if the command was not a SELECT query */ - int needsfetch:1; /* 1 if a call to pq_fetch is pending */ + int withhold:1; /* 1 if the cursor is named and uses WITH HOLD */ long int rowcount; /* number of rows affected by last execute */ long int columns; /* number of columns fetched from the db */ long int arraysize; /* how many rows should fetchmany() return */ + long int itersize; /* how many rows should iter(cur) fetch in named cursors */ long int row; /* the row counter for fetch*() operations */ long int mark; /* transaction marker, copied from conn */ @@ -58,13 +59,13 @@ PyObject *pgstatus; /* last message from the server after an execute */ Oid lastoid; /* last oid from an insert or InvalidOid */ - PyObject *casts; /* an array (tuple) of typecast functions */ - PyObject *caster; /* the current typecaster object */ + PyObject *casts; /* an array (tuple) of typecast functions */ + PyObject *caster; /* the current typecaster object */ - PyObject *copyfile; /* file-like used during COPY TO/FROM ops */ + 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 8132 +#define DEFAULT_COPYBUFF 8192 PyObject *tuple_factory; /* factory for result tuples */ PyObject *tzinfo_factory; /* factory for tzinfo objects */ @@ -78,9 +79,13 @@ PyObject *string_types; /* a set of typecasters for string types */ PyObject *binary_types; /* a set of typecasters for binary types */ -} cursorObject; + PyObject *weakreflist; /* list of weak references */ + +}; + /* C-callable functions in cursor_int.c and cursor_ext.c */ +BORROWED HIDDEN PyObject *curs_get_cast(cursorObject *self, PyObject *oid); HIDDEN void curs_reset(cursorObject *self); /* exception-raising macros */ @@ -95,10 +100,21 @@ return NULL; } #define EXC_IF_NO_MARK(self) \ -if ((self)->mark != (self)->conn->mark) { \ +if ((self)->mark != (self)->conn->mark && (self)->withhold == 0) { \ PyErr_SetString(ProgrammingError, "named cursor isn't valid anymore"); \ return NULL; } +#define EXC_IF_CURS_ASYNC(self, cmd) if ((self)->conn->async == 1) { \ + PyErr_SetString(ProgrammingError, #cmd " cannot be used " \ + "in asynchronous mode"); \ + return NULL; } + +#define EXC_IF_ASYNC_IN_PROGRESS(self, cmd) \ +if ((self)->conn->async_cursor != NULL) { \ + PyErr_SetString(ProgrammingError, #cmd " cannot be used " \ + "while an asynchronous query is underway"); \ + return NULL; } + #ifdef __cplusplus } #endif diff -Nru psycopg2-2.0.13/psycopg/cursor_int.c psycopg2-2.4.5/psycopg/cursor_int.c --- psycopg2-2.0.13/psycopg/cursor_int.c 2009-04-26 12:09:21.000000000 +0000 +++ psycopg2-2.4.5/psycopg/cursor_int.c 2012-03-28 21:09:15.000000000 +0000 @@ -1,33 +1,71 @@ /* cursor_int.c - code used by the cursor object * - * Copyright (C) 2003 Federico Di Gregorio + * Copyright (C) 2003-2010 Federico Di Gregorio * * This file is part of psycopg. * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2, - * or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * psycopg2 is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * In addition, as a special exception, the copyright holders give + * permission to link this program with the OpenSSL library (or with + * modified versions of OpenSSL that use the same license as OpenSSL), + * and distribute linked combinations including the two. + * + * You must obey the GNU Lesser General Public License in all respects for + * all of the code used other than OpenSSL. + * + * psycopg2 is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. */ -#define PY_SSIZE_T_CLEAN -#include -#include - #define PSYCOPG_MODULE -#include "psycopg/config.h" #include "psycopg/psycopg.h" + #include "psycopg/cursor.h" #include "psycopg/pqpath.h" +#include "psycopg/typecast.h" + +/* curs_get_cast - return the type caster for an oid. + * + * Return the most specific type caster, from cursor to connection to global. + * If no type caster is found, return the default one. + * + * Return a borrowed reference. + */ + +BORROWED PyObject * +curs_get_cast(cursorObject *self, PyObject *oid) +{ + PyObject *cast; + + /* cursor lookup */ + if (self->string_types != NULL && self->string_types != Py_None) { + cast = PyDict_GetItem(self->string_types, oid); + Dprintf("curs_get_cast: per-cursor dict: %p", cast); + if (cast) { return cast; } + } + + /* connection lookup */ + cast = PyDict_GetItem(self->conn->string_types, oid); + Dprintf("curs_get_cast: per-connection dict: %p", cast); + if (cast) { return cast; } + + /* global lookup */ + cast = PyDict_GetItem(psyco_types, oid); + Dprintf("curs_get_cast: global dict: %p", cast); + if (cast) { return cast; } + + /* fallback */ + return psyco_default_cast; +} + +#include + /* curs_reset - reset the cursor to a clean state */ diff -Nru psycopg2-2.0.13/psycopg/cursor_type.c psycopg2-2.4.5/psycopg/cursor_type.c --- psycopg2-2.0.13/psycopg/cursor_type.c 2009-10-04 21:35:29.000000000 +0000 +++ psycopg2-2.4.5/psycopg/cursor_type.c 2012-03-28 21:09:15.000000000 +0000 @@ -1,42 +1,44 @@ /* cursor_type.c - python interface to cursor objects * - * Copyright (C) 2003-2004 Federico Di Gregorio + * Copyright (C) 2003-2010 Federico Di Gregorio * * This file is part of psycopg. * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2, - * or (at your option) any later version. + * psycopg2 is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * In addition, as a special exception, the copyright holders give + * permission to link this program with the OpenSSL library (or with + * modified versions of OpenSSL that use the same license as OpenSSL), + * and distribute linked combinations including the two. * - * You should have received a copy of the GNU General Public Likcense - * along with this program; if not, write to the Free Software - * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You must obey the GNU Lesser General Public License in all respects for + * all of the code used other than OpenSSL. + * + * psycopg2 is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. */ -#define PY_SSIZE_T_CLEAN -#include -#include -#include - #define PSYCOPG_MODULE -#include "psycopg/config.h" -#include "psycopg/python.h" #include "psycopg/psycopg.h" + #include "psycopg/cursor.h" #include "psycopg/connection.h" +#include "psycopg/green.h" #include "psycopg/pqpath.h" #include "psycopg/typecast.h" #include "psycopg/microprotocols.h" #include "psycopg/microprotocols_proto.h" -#include "pgversion.h" + +#include + #include + extern PyObject *pyPsycopgTzFixedOffsetTimezone; @@ -50,21 +52,24 @@ static PyObject * psyco_curs_close(cursorObject *self, PyObject *args) { - if (!PyArg_ParseTuple(args, "")) return NULL; + EXC_IF_ASYNC_IN_PROGRESS(self, close); - EXC_IF_CURS_CLOSED(self); + if (self->closed) { + goto exit; + } if (self->name != NULL) { char buffer[128]; EXC_IF_NO_MARK(self); - PyOS_snprintf(buffer, 127, "CLOSE %s", self->name); + PyOS_snprintf(buffer, 127, "CLOSE \"%s\"", self->name); if (pq_execute(self, buffer, 0) == -1) return NULL; } self->closed = 1; Dprintf("psyco_curs_close: cursor at %p closed", self); +exit: Py_INCREF(Py_None); return Py_None; } @@ -74,11 +79,11 @@ /* mogrify a query string and build argument array or dict */ -static int -_mogrify(PyObject *var, PyObject *fmt, connectionObject *conn, PyObject **new) +RAISES_NEG static int +_mogrify(PyObject *var, PyObject *fmt, cursorObject *curs, PyObject **new) { - PyObject *key, *value, *n, *item; - char *d, *c; + PyObject *key, *value, *n; + const char *d, *c; Py_ssize_t index = 0; int force = 0, kind = 0; @@ -86,108 +91,112 @@ just before returning. we also init *new to NULL to exit with an error if we can't complete the mogrification */ n = *new = NULL; - c = PyString_AsString(fmt); + c = Bytes_AsString(fmt); while(*c) { - /* handle plain percent symbol in format string */ - if (c[0] == '%' && c[1] == '%') { - c+=2; force = 1; + if (*c++ != '%') { + /* a regular character */ + continue; } + switch (*c) { + + /* handle plain percent symbol in format string */ + case '%': + ++c; + force = 1; + break; + /* if we find '%(' then this is a dictionary, we: 1/ find the matching ')' and extract the key name 2/ locate the value in the dictionary (or return an error) 3/ mogrify the value into something usefull (quoting)... 4/ ...and add it to the new dictionary to be used as argument */ - else if (c[0] == '%' && c[1] == '(') { - + case '(': /* check if some crazy guy mixed formats */ if (kind == 2) { Py_XDECREF(n); - psyco_set_error(ProgrammingError, (PyObject*)conn, + psyco_set_error(ProgrammingError, curs, "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++); + for (d = c + 1; *d && *d != ')' && *d != '%'; d++); if (*d == ')') { - key = PyString_FromStringAndSize(c+2, (Py_ssize_t) (d-c-2)); - value = PyObject_GetItem(var, key); - /* key has refcnt 1, value the original value + 1 */ + if (!(key = Text_FromUTF8AndSize(c+1, (Py_ssize_t)(d-c-1)))) { + Py_XDECREF(n); + return -1; + } /* if value is NULL we did not find the key (or this is not a dictionary): let python raise a KeyError */ - if (value == NULL) { + if (!(value = PyObject_GetItem(var, key))) { Py_DECREF(key); /* destroy key */ Py_XDECREF(n); /* destroy n */ return -1; } + /* key has refcnt 1, value the original value + 1 */ Dprintf("_mogrify: value refcnt: " - FORMAT_CODE_PY_SSIZE_T " (+1)", value->ob_refcnt); + FORMAT_CODE_PY_SSIZE_T " (+1)", Py_REFCNT(value)); if (n == NULL) { - n = PyDict_New(); + if (!(n = PyDict_New())) { + Py_DECREF(key); + Py_DECREF(value); + return -1; + } } - if ((item = PyObject_GetItem(n, key)) == NULL) { + if (0 == PyDict_Contains(n, key)) { PyObject *t = NULL; - PyErr_Clear(); - /* None is always converted to NULL; this is an optimization over the adapting code and can go away in - the future if somebody finds a None adapter usefull. */ + the future if somebody finds a None adapter useful. */ if (value == Py_None) { - t = PyString_FromString("NULL"); + Py_INCREF(psyco_null); + t = psyco_null; PyDict_SetItem(n, key, t); /* t is a new object, refcnt = 1, key is at 2 */ - - /* if the value is None we need to substitute the - formatting char with 's' (FIXME: this should not be - necessary if we drop support for formats other than - %s!) */ - while (*d && !isalpha(*d)) d++; - if (*d) *d = 's'; } else { - t = microprotocol_getquoted(value, conn); - + t = microprotocol_getquoted(value, curs->conn); if (t != NULL) { PyDict_SetItem(n, key, t); /* both key and t refcnt +1, key is at 2 now */ } else { /* no adapter found, raise a BIG exception */ - Py_XDECREF(value); + Py_DECREF(key); + Py_DECREF(value); Py_DECREF(n); return -1; } } Py_XDECREF(t); /* t dies here */ - /* after the DECREF value has the original refcnt plus 1 - if it was added to the dictionary directly; good */ - Py_XDECREF(value); - } - else { - /* we have an item with one extra refcnt here, zap! */ - Py_DECREF(item); } + Py_DECREF(value); Py_DECREF(key); /* key has the original refcnt now */ Dprintf("_mogrify: after value refcnt: " - FORMAT_CODE_PY_SSIZE_T, - value->ob_refcnt - ); + FORMAT_CODE_PY_SSIZE_T, Py_REFCNT(value)); } - c = d; - } + else { + /* we found %( but not a ) */ + Py_XDECREF(n); + psyco_set_error(ProgrammingError, curs, + "incomplete placeholder: '%(' without ')'", NULL, NULL); + return -1; + } + c = d + 1; /* after the ) */ + break; - else if (c[0] == '%' && c[1] != '(') { + default: /* this is a format that expects a tuple; it is much easier, because we don't need to check the old/new dictionary for keys */ @@ -195,7 +204,7 @@ /* check if some crazy guy mixed formats */ if (kind == 1) { Py_XDECREF(n); - psyco_set_error(ProgrammingError, (PyObject*)conn, + psyco_set_error(ProgrammingError, curs, "argument formats can't be mixed", NULL, NULL); return -1; } @@ -212,20 +221,20 @@ } if (n == NULL) { - n = PyTuple_New(PyObject_Length(var)); + if (!(n = PyTuple_New(PyObject_Length(var)))) { + Py_DECREF(value); + return -1; + } } /* let's have d point just after the '%' */ - d = c+1; - if (value == Py_None) { - PyTuple_SET_ITEM(n, index, PyString_FromString("NULL")); - while (*d && !isalpha(*d)) d++; - if (*d) *d = 's'; + Py_INCREF(psyco_null); + PyTuple_SET_ITEM(n, index, psyco_null); Py_DECREF(value); } else { - PyObject *t = microprotocol_getquoted(value, conn); + PyObject *t = microprotocol_getquoted(value, curs->conn); if (t != NULL) { PyTuple_SET_ITEM(n, index, t); @@ -237,12 +246,8 @@ return -1; } } - c = d; index += 1; } - else { - c++; - } } if (force && n == NULL) @@ -261,31 +266,21 @@ after having set an exception. */ if (!sql || !PyObject_IsTrue(sql)) { - psyco_set_error(ProgrammingError, (PyObject*)self, + psyco_set_error(ProgrammingError, self, "can't execute an empty query", NULL, NULL); goto fail; } - if (PyString_Check(sql)) { + if (Bytes_Check(sql)) { /* Necessary for ref-count symmetry with the unicode case: */ Py_INCREF(sql); } else if (PyUnicode_Check(sql)) { - PyObject *enc = PyDict_GetItemString(psycoEncodings, - self->conn->encoding); - /* enc is a borrowed reference; we won't decref it */ - - if (enc) { - sql = PyUnicode_AsEncodedString(sql, PyString_AsString(enc), NULL); - /* if there was an error during the encoding from unicode to the - target encoding, we just let the exception propagate */ - if (sql == NULL) { goto fail; } - } else { - PyErr_Format(InterfaceError, - "can't encode unicode SQL statement to %s", - self->conn->encoding); - goto fail; - } + char *enc = self->conn->codec; + sql = PyUnicode_AsEncodedString(sql, enc, NULL); + /* if there was an error during the encoding from unicode to the + target encoding, we just let the exception propagate */ + if (sql == NULL) { goto fail; } } else { /* the is not unicode or string, raise an error */ @@ -299,35 +294,89 @@ return NULL; } +/* Merge together a query string and its arguments. + * + * The arguments have been already adapted to SQL. + * + * Return a new reference to a string with the merged query, + * NULL and set an exception if any happened. + */ +static PyObject * +_psyco_curs_merge_query_args(cursorObject *self, + PyObject *query, PyObject *args) +{ + PyObject *fquery; + + /* if PyString_Format() return NULL an error occured: if the error is + a TypeError we need to check the exception.args[0] string for the + values: + + "not enough arguments for format string" + "not all arguments converted" + + and return the appropriate ProgrammingError. we do that by grabbing + the curren exception (we will later restore it if the type or the + strings do not match.) */ + + if (!(fquery = Bytes_Format(query, args))) { + PyObject *err, *arg, *trace; + int pe = 0; + + PyErr_Fetch(&err, &arg, &trace); + + if (err && PyErr_GivenExceptionMatches(err, PyExc_TypeError)) { + Dprintf("psyco_curs_execute: TypeError exception catched"); + PyErr_NormalizeException(&err, &arg, &trace); + + if (PyObject_HasAttrString(arg, "args")) { + PyObject *args = PyObject_GetAttrString(arg, "args"); + PyObject *str = PySequence_GetItem(args, 0); + const char *s = Bytes_AS_STRING(str); + + Dprintf("psyco_curs_execute: -> %s", s); + + if (!strcmp(s, "not enough arguments for format string") + || !strcmp(s, "not all arguments converted")) { + Dprintf("psyco_curs_execute: -> got a match"); + psyco_set_error(ProgrammingError, self, + s, NULL, NULL); + pe = 1; + } + + Py_DECREF(args); + Py_DECREF(str); + } + } + + /* if we did not manage our own exception, restore old one */ + if (pe == 1) { + Py_XDECREF(err); Py_XDECREF(arg); Py_XDECREF(trace); + } + else { + PyErr_Restore(err, arg, trace); + } + } + + return fquery; +} + #define psyco_curs_execute_doc \ -"execute(query, vars=None, async=0) -- Execute query with bound vars." +"execute(query, vars=None) -- Execute query with bound vars." -static int +RAISES_NEG static int _psyco_curs_execute(cursorObject *self, PyObject *operation, PyObject *vars, long int async) { - int res = 0; + int res = -1; + int tmp; PyObject *fquery, *cvt = NULL; - Py_BEGIN_ALLOW_THREADS; - pthread_mutex_lock(&(self->conn->lock)); - if (self->conn->async_cursor != NULL - && self->conn->async_cursor != (PyObject*)self) { - pthread_mutex_unlock(&(self->conn->lock)); - Py_BLOCK_THREADS; - psyco_set_error(ProgrammingError, (PyObject*)self, - "asynchronous query already in execution", NULL, NULL); - return 0; - } - pthread_mutex_unlock(&(self->conn->lock)); - Py_END_ALLOW_THREADS; - operation = _psyco_curs_validate_sql_basic(self, operation); /* Any failure from here forward should 'goto fail' rather than 'return 0' directly. */ - if (operation == NULL) { goto fail; } + if (operation == NULL) { goto exit; } IFCLEARPGRES(self->pgres); @@ -344,65 +393,20 @@ if (vars && vars != Py_None) { - if(_mogrify(vars, operation, self->conn, &cvt) == -1) { goto fail; } + if (0 > _mogrify(vars, operation, self, &cvt)) { goto exit; } } if (vars && cvt) { - /* if PyString_Format() return NULL an error occured: if the error is - a TypeError we need to check the exception.args[0] string for the - values: - - "not enough arguments for format string" - "not all arguments converted" - - and return the appropriate ProgrammingError. we do that by grabbing - the curren exception (we will later restore it if the type or the - strings do not match.) */ - - if (!(fquery = PyString_Format(operation, cvt))) { - PyObject *err, *arg, *trace; - int pe = 0; - - PyErr_Fetch(&err, &arg, &trace); - - if (err && PyErr_GivenExceptionMatches(err, PyExc_TypeError)) { - Dprintf("psyco_curs_execute: TypeError exception catched"); - PyErr_NormalizeException(&err, &arg, &trace); - - if (PyObject_HasAttrString(arg, "args")) { - PyObject *args = PyObject_GetAttrString(arg, "args"); - PyObject *str = PySequence_GetItem(args, 0); - const char *s = PyString_AS_STRING(str); - - Dprintf("psyco_curs_execute: -> %s", s); - - if (!strcmp(s, "not enough arguments for format string") - || !strcmp(s, "not all arguments converted")) { - Dprintf("psyco_curs_execute: -> got a match"); - psyco_set_error(ProgrammingError, (PyObject*)self, - s, NULL, NULL); - pe = 1; - } - - Py_DECREF(args); - Py_DECREF(str); - } - } - - /* if we did not manage our own exception, restore old one */ - if (pe == 1) { - Py_XDECREF(err); Py_XDECREF(arg); Py_XDECREF(trace); - } - else { - PyErr_Restore(err, arg, trace); - } - goto fail; + if (!(fquery = _psyco_curs_merge_query_args(self, operation, cvt))) { + goto exit; } if (self->name != NULL) { - self->query = PyString_FromFormat( - "DECLARE %s CURSOR WITHOUT HOLD FOR %s", - self->name, PyString_AS_STRING(fquery)); + self->query = Bytes_FromFormat( + "DECLARE \"%s\" CURSOR %s HOLD FOR %s", + self->name, + self->withhold ? "WITH" : "WITHOUT", + Bytes_AS_STRING(fquery)); Py_DECREF(fquery); } else { @@ -411,9 +415,11 @@ } else { if (self->name != NULL) { - self->query = PyString_FromFormat( - "DECLARE %s CURSOR WITHOUT HOLD FOR %s", - self->name, PyString_AS_STRING(operation)); + self->query = Bytes_FromFormat( + "DECLARE \"%s\" CURSOR %s HOLD FOR %s", + self->name, + self->withhold ? "WITH" : "WITHOUT", + Bytes_AS_STRING(operation)); } else { /* Transfer reference ownership of the str in operation to @@ -426,68 +432,60 @@ /* At this point, the SQL statement must be str, not unicode */ - res = pq_execute(self, PyString_AS_STRING(self->query), async); - Dprintf("psyco_curs_execute: res = %d, pgres = %p", res, self->pgres); - if (res == -1) { goto fail; } - - res = 1; /* Success */ - goto cleanup; + tmp = pq_execute(self, Bytes_AS_STRING(self->query), async); + Dprintf("psyco_curs_execute: res = %d, pgres = %p", tmp, self->pgres); + if (tmp < 0) { goto exit; } + + res = 0; /* Success */ + +exit: + /* Py_XDECREF(operation) is safe because the original reference passed + by the caller was overwritten with either NULL or a new + reference */ + Py_XDECREF(operation); + Py_XDECREF(cvt); - fail: - res = 0; - /* Fall through to cleanup */ - cleanup: - /* Py_XDECREF(operation) is safe because the original reference passed - by the caller was overwritten with either NULL or a new - reference */ - Py_XDECREF(operation); - - Py_XDECREF(cvt); - - return res; + return res; } static PyObject * psyco_curs_execute(cursorObject *self, PyObject *args, PyObject *kwargs) { - long int async = 0; PyObject *vars = NULL, *operation = NULL; - static char *kwlist[] = {"query", "vars", "async", NULL}; + static char *kwlist[] = {"query", "vars", NULL}; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|Ol", kwlist, - &operation, &vars, &async)) { + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|O", kwlist, + &operation, &vars)) { return NULL; } if (self->name != NULL) { if (self->query != Py_None) { - psyco_set_error(ProgrammingError, (PyObject*)self, + psyco_set_error(ProgrammingError, self, "can't call .execute() on named cursors more than once", NULL, NULL); return NULL; } - if (self->conn->isolation_level == 0) { - psyco_set_error(ProgrammingError, (PyObject*)self, + if (self->conn->autocommit) { + psyco_set_error(ProgrammingError, self, "can't use a named cursor outside of transactions", NULL, NULL); return NULL; } - if (self->conn->mark != self->mark) { - psyco_set_error(ProgrammingError, (PyObject*)self, - "named cursor isn't valid anymore", NULL, NULL); - return NULL; - } + EXC_IF_NO_MARK(self); } EXC_IF_CURS_CLOSED(self); + EXC_IF_ASYNC_IN_PROGRESS(self, execute); + EXC_IF_TPC_PREPARED(self->conn, execute); - if (_psyco_curs_execute(self, operation, vars, async)) { - Py_INCREF(Py_None); - return Py_None; - } - else { + if (0 > _psyco_curs_execute(self, operation, vars, self->conn->async)) { return NULL; } + + /* success */ + Py_INCREF(Py_None); + return Py_None; } #define psyco_curs_executemany_doc \ @@ -499,21 +497,23 @@ PyObject *operation = NULL, *vars = NULL; PyObject *v, *iter = NULL; int rowcount = 0; - + static char *kwlist[] = {"query", "vars_list", NULL}; /* reset rowcount to -1 to avoid setting it when an exception is raised */ self->rowcount = -1; - + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO", kwlist, &operation, &vars)) { return NULL; } EXC_IF_CURS_CLOSED(self); + EXC_IF_CURS_ASYNC(self, executemany); + EXC_IF_TPC_PREPARED(self->conn, executemany); if (self->name != NULL) { - psyco_set_error(ProgrammingError, (PyObject*)self, + psyco_set_error(ProgrammingError, self, "can't call .executemany() on named cursors", NULL, NULL); return NULL; } @@ -524,14 +524,14 @@ } while ((v = PyIter_Next(vars)) != NULL) { - if (_psyco_curs_execute(self, operation, v, 0) == 0) { + if (0 > _psyco_curs_execute(self, operation, v, 0)) { Py_DECREF(v); Py_XDECREF(iter); return NULL; } else { if (self->rowcount == -1) - rowcount = -1; + rowcount = -1; else if (rowcount >= 0) rowcount += self->rowcount; Py_DECREF(v); @@ -540,8 +540,13 @@ Py_XDECREF(iter); self->rowcount = rowcount; - Py_INCREF(Py_None); - return Py_None; + if (!PyErr_Occurred()) { + Py_INCREF(Py_None); + return Py_None; + } + else { + return NULL; + } } @@ -550,125 +555,104 @@ "mogrify(query, vars=None) -> str -- Return query after vars binding." static PyObject * -psyco_curs_mogrify(cursorObject *self, PyObject *args, PyObject *kwargs) +_psyco_curs_mogrify(cursorObject *self, + PyObject *operation, PyObject *vars) { - PyObject *vars = NULL, *cvt = NULL, *operation = NULL; - PyObject *fquery; + PyObject *fquery = NULL, *cvt = NULL; - static char *kwlist[] = {"query", "vars", NULL}; - - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|O", kwlist, - &operation, &vars)) { - return NULL; - } - - if (PyUnicode_Check(operation)) { - PyErr_SetString(NotSupportedError, - "unicode queries not yet supported"); - return NULL; - } - - EXC_IF_CURS_CLOSED(self); - IFCLEARPGRES(self->pgres); + operation = _psyco_curs_validate_sql_basic(self, operation); + if (operation == NULL) { goto cleanup; } - /* note that we don't overwrite the last query executed on the cursor, we - just *return* the new query with bound variables + Dprintf("psyco_curs_mogrify: starting mogrify"); - TODO: refactor the common mogrification code (see psycopg_curs_execute - for comments, the code is amost identical) */ + /* here we are, and we have a sequence or a dictionary filled with + objects to be substituted (bound variables). we try to be smart and do + the right thing (i.e., what the user expects) */ - if (vars) + if (vars && vars != Py_None) { - if(_mogrify(vars, operation, self->conn, &cvt) == -1) return NULL; + if (0 > _mogrify(vars, operation, self, &cvt)) { + goto cleanup; + } } if (vars && cvt) { - if (!(fquery = PyString_Format(operation, cvt))) { - PyObject *err, *arg, *trace; - int pe = 0; - - PyErr_Fetch(&err, &arg, &trace); - - if (err && PyErr_GivenExceptionMatches(err, PyExc_TypeError)) { - Dprintf("psyco_curs_execute: TypeError exception catched"); - PyErr_NormalizeException(&err, &arg, &trace); - - if (PyObject_HasAttrString(arg, "args")) { - PyObject *args = PyObject_GetAttrString(arg, "args"); - PyObject *str = PySequence_GetItem(args, 0); - const char *s = PyString_AS_STRING(str); - - Dprintf("psyco_curs_execute: -> %s", s); - - if (!strcmp(s, "not enough arguments for format string") - || !strcmp(s, "not all arguments converted")) { - Dprintf("psyco_curs_execute: -> got a match"); - psyco_set_error(ProgrammingError, (PyObject*)self, - s, NULL, NULL); - pe = 1; - } - - Py_DECREF(args); - Py_DECREF(str); - } - } - - /* if we did not manage our own exception, restore old one */ - if (pe == 1) { - Py_XDECREF(err); Py_XDECREF(arg); Py_XDECREF(trace); - } - else { - PyErr_Restore(err, arg, trace); - } - return NULL; + if (!(fquery = _psyco_curs_merge_query_args(self, operation, cvt))) { + goto cleanup; } - Dprintf("psyco_curs_execute: cvt->refcnt = " FORMAT_CODE_PY_SSIZE_T + Dprintf("psyco_curs_mogrify: cvt->refcnt = " FORMAT_CODE_PY_SSIZE_T ", fquery->refcnt = " FORMAT_CODE_PY_SSIZE_T, - cvt->ob_refcnt, fquery->ob_refcnt - ); - Py_DECREF(cvt); + Py_REFCNT(cvt), Py_REFCNT(fquery)); } else { fquery = operation; - Py_INCREF(operation); + Py_INCREF(fquery); } +cleanup: + Py_XDECREF(operation); + Py_XDECREF(cvt); + return fquery; } + +static PyObject * +psyco_curs_mogrify(cursorObject *self, PyObject *args, PyObject *kwargs) +{ + PyObject *vars = NULL, *operation = NULL; + + static char *kwlist[] = {"query", "vars", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|O", kwlist, + &operation, &vars)) { + return NULL; + } + + EXC_IF_CURS_CLOSED(self); + + return _psyco_curs_mogrify(self, operation, vars); +} #endif +/* cast method - convert an oid/string into a Python object */ +#define psyco_curs_cast_doc \ +"cast(oid, s) -> value\n\n" \ +"Convert the string s to a Python object according to its oid.\n\n" \ +"Look for a typecaster first in the cursor, then in its connection," \ +"then in the global register. If no suitable typecaster is found," \ +"leave the value as a string." + +static PyObject * +psyco_curs_cast(cursorObject *self, PyObject *args) +{ + PyObject *oid; + PyObject *s; + PyObject *cast; + + if (!PyArg_ParseTuple(args, "OO", &oid, &s)) + return NULL; + + cast = curs_get_cast(self, oid); + return PyObject_CallFunctionObjArgs(cast, s, (PyObject *)self, NULL); +} + + /* fetchone method - fetch one row of results */ #define psyco_curs_fetchone_doc \ "fetchone() -> tuple or None\n\n" \ "Return the next row of a query result set in the form of a tuple (by\n" \ "default) or using the sequence factory previously set in the\n" \ -"`row_factory` attribute. Return `None` when no more data is available.\n" +"`row_factory` attribute. Return `!None` when no more data is available.\n" -static int +RAISES_NEG static int _psyco_curs_prefetch(cursorObject *self) { int i = 0; - /* check if the fetching cursor is the one that did the asynchronous query - and raise an exception if not */ - Py_BEGIN_ALLOW_THREADS; - pthread_mutex_lock(&(self->conn->lock)); - if (self->conn->async_cursor != NULL - && self->conn->async_cursor != (PyObject*)self) { - pthread_mutex_unlock(&(self->conn->lock)); - Py_BLOCK_THREADS; - psyco_set_error(ProgrammingError, (PyObject*)self, - "asynchronous fetch by wrong cursor", NULL, NULL); - return -2; - } - pthread_mutex_unlock(&(self->conn->lock)); - Py_END_ALLOW_THREADS; - - if (self->pgres == NULL || self->needsfetch) { - self->needsfetch = 0; + if (self->pgres == NULL) { Dprintf("_psyco_curs_prefetch: trying to fetch data"); do { i = pq_fetch(self); @@ -680,13 +664,14 @@ return i; } -static PyObject * +RAISES_NEG static int _psyco_curs_buildrow_fill(cursorObject *self, PyObject *res, int row, int n, int istuple) { int i, len, err; const char *str; PyObject *val; + int rv = -1; for (i=0; i < n; i++) { if (PQgetisnull(self->pgres, row, i)) { @@ -701,59 +686,59 @@ Dprintf("_psyco_curs_buildrow: row %ld, element %d, len %d", self->row, i, len); - val = typecast_cast(PyTuple_GET_ITEM(self->casts, i), str, len, - (PyObject*)self); + if (!(val = typecast_cast(PyTuple_GET_ITEM(self->casts, i), str, len, + (PyObject*)self))) { + goto exit; + } - if (val) { - Dprintf("_psyco_curs_buildrow: val->refcnt = " - FORMAT_CODE_PY_SSIZE_T, - val->ob_refcnt - ); - if (istuple) { - PyTuple_SET_ITEM(res, i, val); - } - else { - err = PySequence_SetItem(res, i, val); - Py_DECREF(val); - if (err == -1) { - Py_DECREF(res); - res = NULL; - break; - } - } + Dprintf("_psyco_curs_buildrow: val->refcnt = " + FORMAT_CODE_PY_SSIZE_T, + Py_REFCNT(val) + ); + if (istuple) { + PyTuple_SET_ITEM(res, i, val); } else { - /* an error occurred in the type system, we return NULL to raise - an exception. the typecast code should already have set the - exception type and text */ - Py_DECREF(res); - res = NULL; - break; + err = PySequence_SetItem(res, i, val); + Py_DECREF(val); + if (err == -1) { goto exit; } } } - return res; + + rv = 0; + +exit: + return rv; } static PyObject * _psyco_curs_buildrow(cursorObject *self, int row) { int n; + int istuple; + PyObject *t = NULL; + PyObject *rv = NULL; n = PQnfields(self->pgres); - return _psyco_curs_buildrow_fill(self, PyTuple_New(n), row, n, 1); -} + istuple = (self->tuple_factory == Py_None); -static PyObject * -_psyco_curs_buildrow_with_factory(cursorObject *self, int row) -{ - int n; - PyObject *res; + if (istuple) { + t = PyTuple_New(n); + } + else { + t = PyObject_CallFunctionObjArgs(self->tuple_factory, self, NULL); + } + if (!t) { goto exit; } - n = PQnfields(self->pgres); - if ((res = PyObject_CallFunction(self->tuple_factory, "O", self))== NULL) - return NULL; + if (0 <= _psyco_curs_buildrow_fill(self, t, row, n, istuple)) { + rv = t; + t = NULL; + } + +exit: + Py_XDECREF(t); + return rv; - return _psyco_curs_buildrow_fill(self, res, row, n, 0); } static PyObject * @@ -761,9 +746,7 @@ { PyObject *res; - if (args && !PyArg_ParseTuple(args, "")) return NULL; - - EXC_IF_CURS_CLOSED(self) + EXC_IF_CURS_CLOSED(self); if (_psyco_curs_prefetch(self) < 0) return NULL; EXC_IF_NO_TUPLES(self); @@ -771,7 +754,9 @@ char buffer[128]; EXC_IF_NO_MARK(self); - PyOS_snprintf(buffer, 127, "FETCH FORWARD 1 FROM %s", self->name); + EXC_IF_ASYNC_IN_PROGRESS(self, fetchone); + EXC_IF_TPC_PREPARED(self->conn, fetchone); + PyOS_snprintf(buffer, 127, "FETCH FORWARD 1 FROM \"%s\"", self->name); if (pq_execute(self, buffer, 0) == -1) return NULL; if (_psyco_curs_prefetch(self) < 0) return NULL; } @@ -785,17 +770,61 @@ return Py_None; } - if (self->tuple_factory == Py_None) - res = _psyco_curs_buildrow(self, self->row); - else - res = _psyco_curs_buildrow_with_factory(self, self->row); + res = _psyco_curs_buildrow(self, self->row); + self->row++; /* move the counter to next line */ + + /* if the query was async aggresively free pgres, to allow + successive requests to reallocate it */ + if (self->row >= self->rowcount + && self->conn->async_cursor + && PyWeakref_GetObject(self->conn->async_cursor) == (PyObject*)self) + IFCLEARPGRES(self->pgres); + return res; +} + +/* Efficient cursor.next() implementation for named cursors. + * + * Fetch several records at time. Return NULL when the cursor is exhausted. + */ +static PyObject * +psyco_curs_next_named(cursorObject *self) +{ + PyObject *res; + + Dprintf("psyco_curs_next_named"); + EXC_IF_CURS_CLOSED(self); + EXC_IF_ASYNC_IN_PROGRESS(self, next); + if (_psyco_curs_prefetch(self) < 0) return NULL; + EXC_IF_NO_TUPLES(self); + + EXC_IF_NO_MARK(self); + EXC_IF_TPC_PREPARED(self->conn, next); + + Dprintf("psyco_curs_next_named: row %ld", self->row); + Dprintf("psyco_curs_next_named: rowcount = %ld", self->rowcount); + if (self->row >= self->rowcount) { + char buffer[128]; + + PyOS_snprintf(buffer, 127, "FETCH FORWARD %ld FROM \"%s\"", + self->itersize, self->name); + if (pq_execute(self, buffer, 0) == -1) return NULL; + if (_psyco_curs_prefetch(self) < 0) return NULL; + } + + /* We exhausted the data: return NULL to stop iteration. */ + if (self->row >= self->rowcount) { + return NULL; + } + + res = _psyco_curs_buildrow(self, self->row); self->row++; /* move the counter to next line */ /* if the query was async aggresively free pgres, to allow successive requests to reallocate it */ if (self->row >= self->rowcount - && self->conn->async_cursor == (PyObject*)self) + && self->conn->async_cursor + && PyWeakref_GetObject(self->conn->async_cursor) == (PyObject*)self) IFCLEARPGRES(self->pgres); return res; @@ -808,21 +837,34 @@ "fetchmany(size=self.arraysize) -> list of tuple\n\n" \ "Return the next `size` rows of a query result set in the form of a list\n" \ "of tuples (by default) or using the sequence factory previously set in\n" \ -"the `row_factory` attribute. Return `None` when no more data is available.\n" +"the `row_factory` attribute.\n\n" \ +"Return an empty list when no more data is available.\n" static PyObject * psyco_curs_fetchmany(cursorObject *self, PyObject *args, PyObject *kwords) { int i; - PyObject *list, *res; + PyObject *list = NULL; + PyObject *row = NULL; + PyObject *rv = NULL; + PyObject *pysize = NULL; long int size = self->arraysize; static char *kwlist[] = {"size", NULL}; - if (!PyArg_ParseTupleAndKeywords(args, kwords, "|l", kwlist, &size)) { + /* allow passing None instead of omitting the *size* argument, + * or using the method from subclasses would be a problem */ + if (!PyArg_ParseTupleAndKeywords(args, kwords, "|O", kwlist, &pysize)) { return NULL; } + if (pysize && pysize != Py_None) { + size = PyInt_AsLong(pysize); + if (size == -1 && PyErr_Occurred()) { + return NULL; + } + } + EXC_IF_CURS_CLOSED(self); if (_psyco_curs_prefetch(self) < 0) return NULL; EXC_IF_NO_TUPLES(self); @@ -831,10 +873,12 @@ char buffer[128]; EXC_IF_NO_MARK(self); - PyOS_snprintf(buffer, 127, "FETCH FORWARD %d FROM %s", + EXC_IF_ASYNC_IN_PROGRESS(self, fetchmany); + EXC_IF_TPC_PREPARED(self->conn, fetchone); + PyOS_snprintf(buffer, 127, "FETCH FORWARD %d FROM \"%s\"", (int)size, self->name); - if (pq_execute(self, buffer, 0) == -1) return NULL; - if (_psyco_curs_prefetch(self) < 0) return NULL; + if (pq_execute(self, buffer, 0) == -1) { goto exit; } + if (_psyco_curs_prefetch(self) < 0) { goto exit; } } /* make sure size is not > than the available number of rows */ @@ -845,34 +889,38 @@ Dprintf("psyco_curs_fetchmany: size = %ld", size); if (size <= 0) { - return PyList_New(0); + rv = PyList_New(0); + goto exit; } - list = PyList_New(size); + if (!(list = PyList_New(size))) { goto exit; } for (i = 0; i < size; i++) { - if (self->tuple_factory == Py_None) - res = _psyco_curs_buildrow(self, self->row); - else - res = _psyco_curs_buildrow_with_factory(self, self->row); - + row = _psyco_curs_buildrow(self, self->row); self->row++; - if (res == NULL) { - Py_DECREF(list); - return NULL; - } + if (row == NULL) { goto exit; } - PyList_SET_ITEM(list, i, res); + PyList_SET_ITEM(list, i, row); } + row = NULL; /* if the query was async aggresively free pgres, to allow successive requests to reallocate it */ if (self->row >= self->rowcount - && self->conn->async_cursor == (PyObject*)self) + && self->conn->async_cursor + && PyWeakref_GetObject(self->conn->async_cursor) == (PyObject*)self) IFCLEARPGRES(self->pgres); - return list; + /* success */ + rv = list; + list = NULL; + +exit: + Py_XDECREF(list); + Py_XDECREF(row); + + return rv; } @@ -883,17 +931,15 @@ "Return all the remaining rows of a query result set.\n\n" \ "Rows are returned in the form of a list of tuples (by default) or using\n" \ "the sequence factory previously set in the `row_factory` attribute.\n" \ -"Return `None` when no more data is available.\n" +"Return `!None` when no more data is available.\n" static PyObject * psyco_curs_fetchall(cursorObject *self, PyObject *args) { int i, size; - PyObject *list, *res; - - if (!PyArg_ParseTuple(args, "")) { - return NULL; - } + PyObject *list = NULL; + PyObject *row = NULL; + PyObject *rv = NULL; EXC_IF_CURS_CLOSED(self); if (_psyco_curs_prefetch(self) < 0) return NULL; @@ -903,75 +949,81 @@ char buffer[128]; EXC_IF_NO_MARK(self); - PyOS_snprintf(buffer, 127, "FETCH FORWARD ALL FROM %s", self->name); - if (pq_execute(self, buffer, 0) == -1) return NULL; - if (_psyco_curs_prefetch(self) < 0) return NULL; + EXC_IF_ASYNC_IN_PROGRESS(self, fetchall); + EXC_IF_TPC_PREPARED(self->conn, fetchall); + PyOS_snprintf(buffer, 127, "FETCH FORWARD ALL FROM \"%s\"", self->name); + if (pq_execute(self, buffer, 0) == -1) { goto exit; } + if (_psyco_curs_prefetch(self) < 0) { goto exit; } } size = self->rowcount - self->row; if (size <= 0) { - return PyList_New(0); + rv = PyList_New(0); + goto exit; } - list = PyList_New(size); + if (!(list = PyList_New(size))) { goto exit; } for (i = 0; i < size; i++) { - if (self->tuple_factory == Py_None) - res = _psyco_curs_buildrow(self, self->row); - else - res = _psyco_curs_buildrow_with_factory(self, self->row); - + row = _psyco_curs_buildrow(self, self->row); self->row++; + if (row == NULL) { goto exit; } - if (res == NULL) { - Py_DECREF(list); - return NULL; - } - - PyList_SET_ITEM(list, i, res); + PyList_SET_ITEM(list, i, row); } + row = NULL; /* if the query was async aggresively free pgres, to allow successive requests to reallocate it */ if (self->row >= self->rowcount - && self->conn->async_cursor == (PyObject*)self) + && self->conn->async_cursor + && PyWeakref_GetObject(self->conn->async_cursor) == (PyObject*)self) IFCLEARPGRES(self->pgres); - return list; + /* success */ + rv = list; + list = NULL; + +exit: + Py_XDECREF(list); + Py_XDECREF(row); + + return rv; } /* callproc method - execute a stored procedure */ #define psyco_curs_callproc_doc \ -"callproc(procname, parameters=None, async=0) -- Execute stored procedure." +"callproc(procname, parameters=None) -- Execute stored procedure." static PyObject * -psyco_curs_callproc(cursorObject *self, PyObject *args, PyObject *kwargs) +psyco_curs_callproc(cursorObject *self, PyObject *args) { const char *procname = NULL; char *sql = NULL; - long int async = 0; Py_ssize_t procname_len, i, nparameters = 0, sl = 0; PyObject *parameters = Py_None; PyObject *operation = NULL; PyObject *res = NULL; - if (!PyArg_ParseTuple(args, "s#|Ol", - &procname, &procname_len, ¶meters, &async + if (!PyArg_ParseTuple(args, "s#|O", + &procname, &procname_len, ¶meters )) - { return NULL; } + { goto exit; } EXC_IF_CURS_CLOSED(self); + EXC_IF_ASYNC_IN_PROGRESS(self, callproc); + EXC_IF_TPC_PREPARED(self->conn, callproc); if (self->name != NULL) { - psyco_set_error(ProgrammingError, (PyObject*)self, + psyco_set_error(ProgrammingError, self, "can't call .callproc() on named cursors", NULL, NULL); - return NULL; + goto exit; } - if(parameters != Py_None) { + if (parameters != Py_None) { nparameters = PyObject_Length(parameters); if (nparameters < 0) nparameters = 0; } @@ -979,7 +1031,10 @@ /* allocate some memory, build the SQL and create a PyString from it */ sl = procname_len + 17 + nparameters*3 - (nparameters ? 1 : 0); sql = (char*)PyMem_Malloc(sl); - if (sql == NULL) return NULL; + if (sql == NULL) { + PyErr_NoMemory(); + goto exit; + } sprintf(sql, "SELECT * FROM %s(", procname); for(i=0; iconn->async)) { Py_INCREF(parameters); res = parameters; } - Py_DECREF(operation); +exit: + Py_XDECREF(operation); + PyMem_Free((void*)sql); return res; } @@ -1011,8 +1067,6 @@ static PyObject * psyco_curs_nextset(cursorObject *self, PyObject *args) { - if (!PyArg_ParseTuple(args, "")) return NULL; - EXC_IF_CURS_CLOSED(self); PyErr_SetString(NotSupportedError, "not supported by PostgreSQL"); @@ -1090,13 +1144,13 @@ } else if (strcmp( mode, "absolute") == 0) { newpos = value; } else { - psyco_set_error(ProgrammingError, (PyObject*)self, + psyco_set_error(ProgrammingError, self, "scroll mode must be 'relative' or 'absolute'", NULL, NULL); return NULL; } if (newpos < 0 || newpos >= self->rowcount ) { - psyco_set_error(ProgrammingError, (PyObject*)self, + psyco_set_error(ProgrammingError, self, "scroll destination out of bounds", NULL, NULL); return NULL; } @@ -1108,13 +1162,15 @@ char buffer[128]; EXC_IF_NO_MARK(self); + EXC_IF_ASYNC_IN_PROGRESS(self, scroll) + EXC_IF_TPC_PREPARED(self->conn, scroll); if (strcmp(mode, "absolute") == 0) { - PyOS_snprintf(buffer, 127, "MOVE ABSOLUTE %d FROM %s", + PyOS_snprintf(buffer, 127, "MOVE ABSOLUTE %d FROM \"%s\"", value, self->name); } else { - PyOS_snprintf(buffer, 127, "MOVE %d FROM %s", value, self->name); + PyOS_snprintf(buffer, 127, "MOVE %d FROM \"%s\"", value, self->name); } if (pq_execute(self, buffer, 0) == -1) return NULL; if (_psyco_curs_prefetch(self) < 0) return NULL; @@ -1127,35 +1183,54 @@ #ifdef PSYCOPG_EXTENSIONS -static int _psyco_curs_copy_columns(PyObject *columns, char *columnlist) +/* Return a newly allocated buffer containing the list of columns to be + * copied. On error return NULL and set an exception. + */ +static char *_psyco_curs_copy_columns(PyObject *columns) { PyObject *col, *coliter; Py_ssize_t collen; - char* colname; + char *colname; + char *columnlist = NULL; + Py_ssize_t bufsize = 512; Py_ssize_t offset = 1; - columnlist[0] = '\0'; - if (columns == NULL || columns == Py_None) return 0; + if (columns == NULL || columns == Py_None) { + if (NULL == (columnlist = PyMem_Malloc(2))) { + PyErr_NoMemory(); + goto error; + } + columnlist[0] = '\0'; + goto exit; + } - coliter = PyObject_GetIter(columns); - if (coliter == NULL) return 0; + if (NULL == (coliter = PyObject_GetIter(columns))) { + goto error; + } + if (NULL == (columnlist = PyMem_Malloc(bufsize))) { + Py_DECREF(coliter); + PyErr_NoMemory(); + goto error; + } 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); + if (!(col = psycopg_ensure_bytes(col))) { Py_DECREF(coliter); - PyErr_SetString(PyExc_ValueError, "column list too long"); - return -1; + goto error; + } + Bytes_AsStringAndSize(col, &colname, &collen); + while (offset + collen > bufsize - 2) { + char *tmp; + bufsize *= 2; + if (NULL == (tmp = PyMem_Realloc(columnlist, bufsize))) { + Py_DECREF(col); + Py_DECREF(coliter); + PyErr_NoMemory(); + goto error; + } + columnlist = tmp; } strncpy(&columnlist[offset], colname, collen); offset += collen; @@ -1164,29 +1239,45 @@ } Py_DECREF(coliter); + /* Error raised by the coliter generator */ + if (PyErr_Occurred()) { + goto error; + } + if (offset == 2) { - return 0; + goto exit; } else { columnlist[offset - 1] = ')'; columnlist[offset] = '\0'; - return 1; + goto exit; } + +error: + PyMem_Free(columnlist); + columnlist = NULL; + +exit: + return columnlist; } /* extension: copy_from - implements COPY FROM */ #define psyco_curs_copy_from_doc \ -"copy_from(file, table, sep='\\t', null='\\N', columns=None) -- Copy table from file." +"copy_from(file, table, sep='\\t', null='\\\\N', size=8192, columns=None) -- Copy table from file." -static int -_psyco_curs_has_read_check(PyObject* o, void* var) +STEALS(1) static int +_psyco_curs_has_read_check(PyObject *o, PyObject **var) { if (PyObject_HasAttrString(o, "readline") && PyObject_HasAttrString(o, "read")) { - /* It's OK to store a borrowed reference, because it is only held for - * the duration of psyco_curs_copy_from. */ - *((PyObject**)var) = o; + /* This routine stores a borrowed reference. Although it is only held + * for the duration of psyco_curs_copy_from, nested invocations of + * Py_BEGIN_ALLOW_THREADS could surrender control to another thread, + * which could invoke the garbage collector. We thus need an + * INCREF/DECREF pair if we store this pointer in a GC object, such as + * a cursorObject */ + *var = o; return 1; } else { @@ -1199,18 +1290,23 @@ static PyObject * psyco_curs_copy_from(cursorObject *self, PyObject *args, PyObject *kwargs) { - char query_buffer[DEFAULT_COPYBUFF]; + static char *kwlist[] = { + "file", "table", "sep", "null", "size", "columns", NULL}; + + const char *sep = "\t"; + const char *null = "\\N"; + const char *command = + "COPY %s%s FROM stdin WITH DELIMITER AS %s NULL AS %s"; + Py_ssize_t query_size; - char *query; + char *query = NULL; + char *columnlist = NULL; + char *quoted_delimiter = NULL; + char *quoted_null = NULL; + const char *table_name; - const char *sep = "\t", *null = NULL; Py_ssize_t bufsize = DEFAULT_COPYBUFF; PyObject *file, *columns = NULL, *res = NULL; - char columnlist[DEFAULT_COPYBUFF]; - char *quoted_delimiter; - - static char *kwlist[] = { - "file", "table", "sep", "null", "size", "columns", NULL}; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&s|ss" CONV_CODE_PY_SSIZE_T "O", kwlist, @@ -1220,65 +1316,54 @@ return NULL; } - if (_psyco_curs_copy_columns(columns, columnlist) == -1) - return NULL; - EXC_IF_CURS_CLOSED(self); + EXC_IF_CURS_ASYNC(self, copy_from); + EXC_IF_GREEN(copy_from); + EXC_IF_TPC_PREPARED(self->conn, copy_from); + + if (NULL == (columnlist = _psyco_curs_copy_columns(columns))) + goto exit; - quoted_delimiter = psycopg_escape_string((PyObject*)self->conn, sep, 0, NULL, NULL); - if (quoted_delimiter == NULL) { + if (!(quoted_delimiter = psycopg_escape_string( + (PyObject*)self->conn, sep, 0, NULL, NULL))) { PyErr_NoMemory(); - return NULL; + goto exit; } - - query = query_buffer; - if (null) { - char *quoted_null = psycopg_escape_string((PyObject*)self->conn, null, 0, NULL, NULL); - if (quoted_null == NULL) { - PyMem_Free(quoted_delimiter); - PyErr_NoMemory(); - return NULL; - } - query_size = PyOS_snprintf(query, DEFAULT_COPYBUFF, - "COPY %s%s FROM stdin WITH DELIMITER AS %s NULL AS %s", - table_name, columnlist, quoted_delimiter, quoted_null); - if (query_size >= DEFAULT_COPYBUFF) { - /* Got truncated, allocate dynamically */ - query = (char *)PyMem_Malloc((query_size + 1) * sizeof(char)); - PyOS_snprintf(query, query_size + 1, - "COPY %s%s FROM stdin WITH DELIMITER AS %s NULL AS %s", - table_name, columnlist, quoted_delimiter, quoted_null); - } - PyMem_Free(quoted_null); + + if (!(quoted_null = psycopg_escape_string( + (PyObject*)self->conn, null, 0, NULL, NULL))) { + PyErr_NoMemory(); + goto exit; } - else { - query_size = PyOS_snprintf(query, DEFAULT_COPYBUFF, - "COPY %s%s FROM stdin WITH DELIMITER AS %s", - table_name, columnlist, quoted_delimiter); - if (query_size >= DEFAULT_COPYBUFF) { - /* Got truncated, allocate dynamically */ - query = (char *)PyMem_Malloc((query_size + 1) * sizeof(char)); - PyOS_snprintf(query, query_size + 1, - "COPY %s%s FROM stdin WITH DELIMITER AS %s", - table_name, columnlist, quoted_delimiter); - } - } - PyMem_Free(quoted_delimiter); - + + query_size = strlen(command) + strlen(table_name) + strlen(columnlist) + + strlen(quoted_delimiter) + strlen(quoted_null) + 1; + if (!(query = PyMem_New(char, query_size))) { + PyErr_NoMemory(); + goto exit; + } + + PyOS_snprintf(query, query_size, command, + table_name, columnlist, quoted_delimiter, quoted_null); + Dprintf("psyco_curs_copy_from: query = %s", query); self->copysize = bufsize; + Py_INCREF(file); self->copyfile = file; - if (pq_execute(self, query, 0) == 1) { + if (pq_execute(self, query, 0) >= 0) { res = Py_None; Py_INCREF(Py_None); } - if (query && (query != query_buffer)) { - PyMem_Free(query); - } - self->copyfile = NULL; + Py_CLEAR(self->copyfile); + +exit: + PyMem_Free(columnlist); + PyMem_Free(quoted_delimiter); + PyMem_Free(quoted_null); + PyMem_Free(query); return res; } @@ -1286,15 +1371,13 @@ /* extension: copy_to - implements COPY TO */ #define psyco_curs_copy_to_doc \ -"copy_to(file, table, sep='\\t', null='\\N', columns=None) -- 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) +STEALS(1) static int +_psyco_curs_has_write_check(PyObject *o, PyObject **var) { if (PyObject_HasAttrString(o, "write")) { - /* It's OK to store a borrowed reference, because it is only held for - * the duration of psyco_curs_copy_to. */ - *((PyObject**)var) = o; + *var = o; return 1; } else { @@ -1307,16 +1390,21 @@ static PyObject * psyco_curs_copy_to(cursorObject *self, PyObject *args, PyObject *kwargs) { + static char *kwlist[] = {"file", "table", "sep", "null", "columns", NULL}; + + const char *sep = "\t"; + const char *null = "\\N"; + const char *command = + "COPY %s%s TO stdout WITH DELIMITER AS %s NULL AS %s"; + + Py_ssize_t query_size; char *query = NULL; - char query_buffer[DEFAULT_COPYBUFF]; - size_t query_size; - char columnlist[DEFAULT_COPYBUFF]; + char *columnlist = NULL; + char *quoted_delimiter = NULL; + char *quoted_null = NULL; + const char *table_name; - const char *sep = "\t", *null = NULL; PyObject *file, *columns = NULL, *res = NULL; - char *quoted_delimiter; - - static char *kwlist[] = {"file", "table", "sep", "null", "columns", NULL}; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&s|ssO", kwlist, _psyco_curs_has_write_check, &file, @@ -1324,63 +1412,54 @@ return NULL; } - if (_psyco_curs_copy_columns(columns, columnlist) == -1) - return NULL; - EXC_IF_CURS_CLOSED(self); - quoted_delimiter = psycopg_escape_string((PyObject*)self->conn, sep, 0, NULL, NULL); - if (quoted_delimiter == NULL) { + EXC_IF_CURS_ASYNC(self, copy_to); + EXC_IF_GREEN(copy_to); + EXC_IF_TPC_PREPARED(self->conn, copy_to); + + if (NULL == (columnlist = _psyco_curs_copy_columns(columns))) + goto exit; + + if (!(quoted_delimiter = psycopg_escape_string( + (PyObject*)self->conn, sep, 0, NULL, NULL))) { PyErr_NoMemory(); - return NULL; + goto exit; } - - query = query_buffer; - if (null) { - char *quoted_null = psycopg_escape_string((PyObject*)self->conn, null, 0, NULL, NULL); - if (NULL == quoted_null) { - PyMem_Free(quoted_delimiter); - PyErr_NoMemory(); - return NULL; - } - query_size = PyOS_snprintf(query, DEFAULT_COPYBUFF, - "COPY %s%s TO stdout WITH DELIMITER AS %s" - " NULL AS %s", table_name, columnlist, quoted_delimiter, quoted_null); - if (query_size >= DEFAULT_COPYBUFF) { - /* Got truncated, allocate dynamically */ - query = (char *)PyMem_Malloc((query_size + 1) * sizeof(char)); - PyOS_snprintf(query, query_size + 1, - "COPY %s%s TO stdout WITH DELIMITER AS %s" - " NULL AS %s", table_name, columnlist, quoted_delimiter, quoted_null); - } - PyMem_Free(quoted_null); + + if (!(quoted_null = psycopg_escape_string( + (PyObject*)self->conn, null, 0, NULL, NULL))) { + PyErr_NoMemory(); + goto exit; } - else { - query_size = PyOS_snprintf(query, DEFAULT_COPYBUFF, - "COPY %s%s TO stdout WITH DELIMITER AS %s", - table_name, columnlist, quoted_delimiter); - if (query_size >= DEFAULT_COPYBUFF) { - /* Got truncated, allocate dynamically */ - query = (char *)PyMem_Malloc((query_size + 1) * sizeof(char)); - PyOS_snprintf(query, query_size + 1, - "COPY %s%s TO stdout WITH DELIMITER AS %s", - table_name, columnlist, quoted_delimiter); - } + + query_size = strlen(command) + strlen(table_name) + strlen(columnlist) + + strlen(quoted_delimiter) + strlen(quoted_null) + 1; + if (!(query = PyMem_New(char, query_size))) { + PyErr_NoMemory(); + goto exit; } - PyMem_Free(quoted_delimiter); - + + PyOS_snprintf(query, query_size, command, + table_name, columnlist, quoted_delimiter, quoted_null); + Dprintf("psyco_curs_copy_to: query = %s", query); self->copysize = 0; + Py_INCREF(file); self->copyfile = file; - if (pq_execute(self, query, 0) == 1) { + if (pq_execute(self, query, 0) >= 0) { res = Py_None; Py_INCREF(Py_None); } - if (query && (query != query_buffer)) { - PyMem_Free(query); - } - self->copyfile = NULL; + + Py_CLEAR(self->copyfile); + +exit: + PyMem_Free(columnlist); + PyMem_Free(quoted_delimiter); + PyMem_Free(quoted_null); + PyMem_Free(query); return res; } @@ -1392,8 +1471,8 @@ */ #define psyco_curs_copy_expert_doc \ -"copy_expert(sql, file, size=None) -- Submit a user-composed COPY statement.\n" \ -"`file` must be an open, readable file for COPY FROM or an open, writeable\n" \ +"copy_expert(sql, file, size=8192) -- Submit a user-composed COPY statement.\n" \ +"`file` must be an open, readable file for COPY FROM or an open, writable\n" \ "file for COPY TO. The optional `size` argument, when specified for a COPY\n" \ "FROM statement, will be passed to file's read method to control the read\n" \ "buffer size." @@ -1411,131 +1490,98 @@ { return NULL; } EXC_IF_CURS_CLOSED(self); + EXC_IF_CURS_ASYNC(self, copy_expert); + EXC_IF_GREEN(copy_expert); + EXC_IF_TPC_PREPARED(self->conn, copy_expert); sql = _psyco_curs_validate_sql_basic(self, sql); - - /* Any failure from here forward should 'goto fail' rather than + + /* Any failure from here forward should 'goto exit' rather than 'return NULL' directly. */ - - if (sql == NULL) { goto fail; } + + if (sql == NULL) { goto exit; } /* This validation of file is rather weak, in that it doesn't enforce the assocation between "COPY FROM" -> "read" and "COPY TO" -> "write". However, the error handling in _pq_copy_[in|out] must be able to handle the case where the attempt to call file.read|write fails, so no harm done. */ - + if ( !PyObject_HasAttrString(file, "read") && !PyObject_HasAttrString(file, "write") ) { PyErr_SetString(PyExc_TypeError, "file must be a readable file-like" - " object for COPY FROM; a writeable file-like object for COPY TO." + " object for COPY FROM; a writable file-like object for COPY TO." ); - goto fail; + goto exit; } self->copysize = bufsize; + Py_INCREF(file); self->copyfile = file; /* At this point, the SQL statement must be str, not unicode */ - if (pq_execute(self, PyString_AS_STRING(sql), 0) != 1) { goto fail; } + if (pq_execute(self, Bytes_AS_STRING(sql), 0) >= 0) { + res = Py_None; + Py_INCREF(res); + } - res = Py_None; - Py_INCREF(res); - goto cleanup; - fail: - if (res != NULL) { - Py_DECREF(res); - res = NULL; - } - /* Fall through to cleanup */ - cleanup: - self->copyfile = NULL; + Py_CLEAR(self->copyfile); + +exit: Py_XDECREF(sql); return res; } -/* extension: fileno - return the file descripor of the connection */ +/* extension: closed - return true if cursor is closed */ -#define psyco_curs_fileno_doc \ -"fileno() -> int -- Return file descriptor associated to database connection." +#define psyco_curs_closed_doc \ +"True if cursor is closed, False if cursor is open" static PyObject * -psyco_curs_fileno(cursorObject *self, PyObject *args) +psyco_curs_get_closed(cursorObject *self, void *closure) { - long int socket; - - if (!PyArg_ParseTuple(args, "")) return NULL; - EXC_IF_CURS_CLOSED(self); - - /* note how we call PQflush() to make sure the user will use - select() in the safe way! */ - Py_BEGIN_ALLOW_THREADS; - pthread_mutex_lock(&(self->conn->lock)); - PQflush(self->conn->pgconn); - socket = (long int)PQsocket(self->conn->pgconn); - pthread_mutex_unlock(&(self->conn->lock)); - Py_END_ALLOW_THREADS; + PyObject *closed; - return PyInt_FromLong(socket); + closed = (self->closed || (self->conn && self->conn->closed)) ? + Py_True : Py_False; + Py_INCREF(closed); + return closed; } -/* extension: isready - return true if data from async execute is ready */ +/* extension: withhold - get or set "WITH HOLD" for named cursors */ -#define psyco_curs_isready_doc \ -"isready() -> bool -- Return True if data is ready after an async query." +#define psyco_curs_withhold_doc \ +"Set or return cursor use of WITH HOLD" static PyObject * -psyco_curs_isready(cursorObject *self, PyObject *args) +psyco_curs_withhold_get(cursorObject *self) { - int res; - - if (!PyArg_ParseTuple(args, "")) return NULL; - EXC_IF_CURS_CLOSED(self); - - /* pq_is_busy does its own locking, we don't need anything special but if - the cursor is ready we need to fetch the result and free the connection - for the next query. if -1 is returned we raise an exception. */ - - res = pq_is_busy(self->conn); - - if (res == 1) { - Py_INCREF(Py_False); - return Py_False; - } - else if (res == -1) { - return NULL; - } - else { - IFCLEARPGRES(self->pgres); - Py_BEGIN_ALLOW_THREADS; - pthread_mutex_lock(&(self->conn->lock)); - self->pgres = PQgetResult(self->conn->pgconn); - self->conn->async_cursor = NULL; - pthread_mutex_unlock(&(self->conn->lock)); - Py_END_ALLOW_THREADS; - self->needsfetch = 1; - Py_INCREF(Py_True); - return Py_True; - } + PyObject *ret; + ret = self->withhold ? Py_True : Py_False; + Py_INCREF(ret); + return ret; } -/* extension: closed - return true if cursor is closed*/ - -#define psyco_curs_closed_doc \ -"True if cursor is closed, False if cursor is open" - -static PyObject * -psyco_curs_get_closed(cursorObject *self, void *closure) +static int +psyco_curs_withhold_set(cursorObject *self, PyObject *pyvalue) { - PyObject *closed; + int value; - closed = (self->closed || (self->conn && self->conn->closed)) ? - Py_True : Py_False; - Py_INCREF(closed); - return closed; + if (self->name == NULL) { + PyErr_SetString(ProgrammingError, + "trying to set .withhold on unnamed cursor"); + return -1; + } + + if ((value = PyObject_IsTrue(pyvalue)) == -1) + return -1; + + self->withhold = value; + + return 0; } #endif @@ -1558,14 +1604,20 @@ { PyObject *res; - /* we don't parse arguments: psyco_curs_fetchone will do that for us */ - res = psyco_curs_fetchone((cursorObject*)self, NULL); + if (NULL == ((cursorObject*)self)->name) { + /* we don't parse arguments: psyco_curs_fetchone will do that for us */ + res = psyco_curs_fetchone((cursorObject*)self, NULL); - /* convert a None to NULL to signal the end of iteration */ - if (res && res == Py_None) { - Py_DECREF(res); - res = NULL; + /* convert a None to NULL to signal the end of iteration */ + if (res && res == Py_None) { + Py_DECREF(res); + res = NULL; + } } + else { + res = psyco_curs_next_named((cursorObject*)self); + } + return res; } @@ -1574,21 +1626,21 @@ static struct PyMethodDef cursorObject_methods[] = { /* DBAPI-2.0 core */ {"close", (PyCFunction)psyco_curs_close, - METH_VARARGS, psyco_curs_close_doc}, + METH_NOARGS, psyco_curs_close_doc}, {"execute", (PyCFunction)psyco_curs_execute, METH_VARARGS|METH_KEYWORDS, psyco_curs_execute_doc}, {"executemany", (PyCFunction)psyco_curs_executemany, METH_VARARGS|METH_KEYWORDS, psyco_curs_executemany_doc}, {"fetchone", (PyCFunction)psyco_curs_fetchone, - METH_VARARGS, psyco_curs_fetchone_doc}, + METH_NOARGS, psyco_curs_fetchone_doc}, {"fetchmany", (PyCFunction)psyco_curs_fetchmany, METH_VARARGS|METH_KEYWORDS, psyco_curs_fetchmany_doc}, {"fetchall", (PyCFunction)psyco_curs_fetchall, - METH_VARARGS, psyco_curs_fetchall_doc}, + METH_NOARGS, psyco_curs_fetchall_doc}, {"callproc", (PyCFunction)psyco_curs_callproc, METH_VARARGS, psyco_curs_callproc_doc}, {"nextset", (PyCFunction)psyco_curs_nextset, - METH_VARARGS, psyco_curs_nextset_doc}, + METH_NOARGS, psyco_curs_nextset_doc}, {"setinputsizes", (PyCFunction)psyco_curs_setinputsizes, METH_VARARGS, psyco_curs_setinputsizes_doc}, {"setoutputsize", (PyCFunction)psyco_curs_setoutputsize, @@ -1598,12 +1650,10 @@ METH_VARARGS|METH_KEYWORDS, psyco_curs_scroll_doc}, /* psycopg extensions */ #ifdef PSYCOPG_EXTENSIONS + {"cast", (PyCFunction)psyco_curs_cast, + METH_VARARGS, psyco_curs_cast_doc}, {"mogrify", (PyCFunction)psyco_curs_mogrify, METH_VARARGS|METH_KEYWORDS, psyco_curs_mogrify_doc}, - {"fileno", (PyCFunction)psyco_curs_fileno, - METH_VARARGS, psyco_curs_fileno_doc}, - {"isready", (PyCFunction)psyco_curs_isready, - METH_VARARGS, psyco_curs_isready_doc}, {"copy_from", (PyCFunction)psyco_curs_copy_from, METH_VARARGS|METH_KEYWORDS, psyco_curs_copy_from_doc}, {"copy_to", (PyCFunction)psyco_curs_copy_to, @@ -1620,29 +1670,31 @@ static struct PyMemberDef cursorObject_members[] = { /* DBAPI-2.0 basics */ - {"rowcount", T_LONG, OFFSETOF(rowcount), RO, + {"rowcount", T_LONG, OFFSETOF(rowcount), READONLY, "Number of rows read from the backend in the last command."}, {"arraysize", T_LONG, OFFSETOF(arraysize), 0, - "Number of records `fetchmany()` must fetch if not explicitely " \ + "Number of records `fetchmany()` must fetch if not explicitly " \ "specified."}, - {"description", T_OBJECT, OFFSETOF(description), RO, + {"itersize", T_LONG, OFFSETOF(itersize), 0, + "Number of records ``iter(cur)`` must fetch per network roundtrip."}, + {"description", T_OBJECT, OFFSETOF(description), READONLY, "Cursor description as defined in DBAPI-2.0."}, - {"lastrowid", T_LONG, OFFSETOF(lastoid), RO, + {"lastrowid", T_LONG, OFFSETOF(lastoid), READONLY, "The ``oid`` of the last row inserted by the cursor."}, /* DBAPI-2.0 extensions */ - {"rownumber", T_LONG, OFFSETOF(row), RO, + {"rownumber", T_LONG, OFFSETOF(row), READONLY, "The current row position."}, - {"connection", T_OBJECT, OFFSETOF(conn), RO, + {"connection", T_OBJECT, OFFSETOF(conn), READONLY, "The connection where the cursor comes from."}, #ifdef PSYCOPG_EXTENSIONS - {"name", T_STRING, OFFSETOF(name), RO}, - {"statusmessage", T_OBJECT, OFFSETOF(pgstatus), RO, + {"name", T_STRING, OFFSETOF(name), READONLY}, + {"statusmessage", T_OBJECT, OFFSETOF(pgstatus), READONLY, "The return message of the last command."}, - {"query", T_OBJECT, OFFSETOF(query), RO, + {"query", T_OBJECT, OFFSETOF(query), READONLY, "The last query text sent to the backend."}, {"row_factory", T_OBJECT, OFFSETOF(tuple_factory), 0}, {"tzinfo_factory", T_OBJECT, OFFSETOF(tzinfo_factory), 0}, - {"typecaster", T_OBJECT, OFFSETOF(caster), RO}, + {"typecaster", T_OBJECT, OFFSETOF(caster), READONLY}, {"string_types", T_OBJECT, OFFSETOF(string_types), 0}, {"binary_types", T_OBJECT, OFFSETOF(binary_types), 0}, #endif @@ -1654,6 +1706,10 @@ #ifdef PSYCOPG_EXTENSIONS { "closed", (getter)psyco_curs_get_closed, NULL, psyco_curs_closed_doc, NULL }, + { "withhold", + (getter)psyco_curs_withhold_get, + (setter)psyco_curs_withhold_set, + psyco_curs_withhold_doc, NULL }, #endif {NULL} }; @@ -1667,9 +1723,9 @@ Dprintf("cursor_setup: parameters: name = %s, conn = %p", name, conn); if (name) { - self->name = PyMem_Malloc(strlen(name)+1); - if (self->name == NULL) return 1; - strncpy(self->name, name, strlen(name)+1); + if (!(self->name = psycopg_escape_identifier_easy(name, 0))) { + return 1; + } } /* FIXME: why does this raise an excpetion on the _next_ line of code? @@ -1683,10 +1739,12 @@ self->conn = conn; self->closed = 0; + self->withhold = 0; self->mark = conn->mark; self->pgres = NULL; self->notuples = 1; self->arraysize = 1; + self->itersize = 2000; self->rowcount = -1; self->lastoid = InvalidOid; @@ -1695,6 +1753,7 @@ self->string_types = NULL; self->binary_types = NULL; + self->weakreflist = NULL; Py_INCREF(Py_None); self->description = Py_None; @@ -1711,7 +1770,7 @@ Dprintf("cursor_setup: good cursor object at %p, refcnt = " FORMAT_CODE_PY_SSIZE_T, - self, ((PyObject *)self)->ob_refcnt + self, Py_REFCNT(self) ); return 0; } @@ -1720,10 +1779,14 @@ cursor_dealloc(PyObject* obj) { cursorObject *self = (cursorObject *)obj; - + + if (self->weakreflist) { + PyObject_ClearWeakRefs(obj); + } + PyObject_GC_UnTrack(self); - if (self->name) PyMem_Free(self->name); + PyMem_Free(self->name); Py_CLEAR(self->conn); Py_CLEAR(self->casts); @@ -1739,9 +1802,9 @@ Dprintf("cursor_dealloc: deleted cursor object at %p, refcnt = " FORMAT_CODE_PY_SSIZE_T, - obj, obj->ob_refcnt); + obj, Py_REFCNT(obj)); - obj->ob_type->tp_free(obj); + Py_TYPE(obj)->tp_free(obj); } static int @@ -1799,8 +1862,7 @@ "A database cursor." PyTypeObject cursorType = { - PyObject_HEAD_INIT(NULL) - 0, + PyVarObject_HEAD_INIT(NULL, 0) "psycopg2._psycopg.cursor", sizeof(cursorObject), 0, @@ -1822,14 +1884,15 @@ 0, /*tp_as_buffer*/ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_ITER | - Py_TPFLAGS_HAVE_GC, /*tp_flags*/ + Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_HAVE_WEAKREFS , + /*tp_flags*/ cursorType_doc, /*tp_doc*/ (traverseproc)cursor_traverse, /*tp_traverse*/ 0, /*tp_clear*/ 0, /*tp_richcompare*/ - 0, /*tp_weaklistoffset*/ + offsetof(cursorObject, weakreflist), /*tp_weaklistoffset*/ cursor_iter, /*tp_iter*/ cursor_next, /*tp_iternext*/ diff -Nru psycopg2-2.0.13/psycopg/green.c psycopg2-2.4.5/psycopg/green.c --- psycopg2-2.0.13/psycopg/green.c 1970-01-01 00:00:00.000000000 +0000 +++ psycopg2-2.4.5/psycopg/green.c 2011-06-13 16:53:48.000000000 +0000 @@ -0,0 +1,213 @@ +/* green.c - cooperation with coroutine libraries. + * + * Copyright (C) 2010 Daniele Varrazzo + * + * This file is part of psycopg. + * + * psycopg2 is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * In addition, as a special exception, the copyright holders give + * permission to link this program with the OpenSSL library (or with + * modified versions of OpenSSL that use the same license as OpenSSL), + * and distribute linked combinations including the two. + * + * You must obey the GNU Lesser General Public License in all respects for + * all of the code used other than OpenSSL. + * + * psycopg2 is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + */ + +#define PSYCOPG_MODULE +#include "psycopg/psycopg.h" + +#include "psycopg/green.h" +#include "psycopg/connection.h" +#include "psycopg/pqpath.h" + + +HIDDEN PyObject *wait_callback = NULL; + +static PyObject *have_wait_callback(void); +static void psyco_clear_result_blocking(connectionObject *conn); + +/* Register a callback function to block waiting for data. + * + * The function is exported by the _psycopg module. + */ +PyObject * +psyco_set_wait_callback(PyObject *self, PyObject *obj) +{ + Py_XDECREF(wait_callback); + + if (obj != Py_None) { + wait_callback = obj; + Py_INCREF(obj); + } + else { + wait_callback = NULL; + } + + Py_INCREF(Py_None); + return Py_None; +} + + +/* Return the currently registered wait callback function. + * + * The function is exported by the _psycopg module. + */ +PyObject * +psyco_get_wait_callback(PyObject *self, PyObject *obj) +{ + PyObject *ret; + + ret = wait_callback; + if (!ret) { + ret = Py_None; + } + + Py_INCREF(ret); + return ret; +} + + +/* Return nonzero if a wait callback should be called. */ +int +psyco_green() +{ +#ifdef PSYCOPG_EXTENSIONS + return (NULL != wait_callback); +#else + return 0; +#endif +} + +/* Return the wait callback if available. + * + * If not available, set a Python exception and return. + * + * The function returns a new reference: decref after use. + */ +static PyObject * +have_wait_callback() +{ + PyObject *cb; + + cb = wait_callback; + if (!cb) { + PyErr_SetString(OperationalError, "wait callback not available"); + return NULL; + } + Py_INCREF(cb); + return cb; +} + +/* Block waiting for data available in an async connection. + * + * This function assumes `wait_callback` to be available: + * raise `InterfaceError` if it is not. Use `psyco_green()` to check if + * the function is to be called. + * + * Return 0 on success, else nonzero and set a Python exception. + */ +int +psyco_wait(connectionObject *conn) +{ + PyObject *rv; + PyObject *cb; + + Dprintf("psyco_wait"); + if (!(cb = have_wait_callback())) { + return -1; + } + + rv = PyObject_CallFunctionObjArgs(cb, conn, NULL); + Py_DECREF(cb); + + if (NULL != rv) { + Py_DECREF(rv); + return 0; + } else { + Dprintf("psyco_wait: error in wait callback"); + return -1; + } +} + +/* Replacement for PQexec using the user-provided wait function. + * + * The function should be called helding the connection lock, and + * the GIL because some Python code is expected to be called. + * + * If PGresult is NULL, there may have been either a libpq error + * or an exception raised by Python code: before raising an exception + * check if there is already one using `PyErr_Occurred()` */ +PGresult * +psyco_exec_green(connectionObject *conn, const char *command) +{ + PGresult *result = NULL; + + /* Check that there is a single concurrently executing query */ + if (conn->async_cursor) { + PyErr_SetString(ProgrammingError, + "a single async query can be executed on the same connection"); + goto end; + } + /* we don't care about which cursor is executing the query, and + * it may also be that no cursor is involved at all and this is + * an internal query. So just store anything in the async_cursor, + * respecting the code expecting it to be a weakref */ + if (!(conn->async_cursor = PyWeakref_NewRef((PyObject*)conn, NULL))) { + goto end; + } + + /* Send the query asynchronously */ + if (0 == pq_send_query(conn, command)) { + goto end; + } + + /* Enter the poll loop with a write. When writing is finished the poll + implementation will set the status to ASYNC_READ without exiting the + loop. If read is finished the status is finally set to ASYNC_DONE. + */ + conn->async_status = ASYNC_WRITE; + + if (0 != psyco_wait(conn)) { + psyco_clear_result_blocking(conn); + goto end; + } + + /* Now we can read the data without fear of blocking. */ + result = pq_get_last_result(conn); + +end: + conn->async_status = ASYNC_DONE; + Py_CLEAR(conn->async_cursor); + return result; +} + + +/* Discard the result of the currenly executed query, blocking. + * + * This function doesn't honour the wait callback: it can be used in case of + * emergency if the callback fails in order to put the connection back into a + * consistent state. + * + * If any command was issued before clearing the result, libpq would fail with + * the error "another command is already in progress". + */ +static void +psyco_clear_result_blocking(connectionObject *conn) +{ + PGresult *res; + + Dprintf("psyco_clear_result_blocking"); + while (NULL != (res = PQgetResult(conn->pgconn))) { + PQclear(res); + } +} diff -Nru psycopg2-2.0.13/psycopg/green.h psycopg2-2.4.5/psycopg/green.h --- psycopg2-2.0.13/psycopg/green.h 1970-01-01 00:00:00.000000000 +0000 +++ psycopg2-2.4.5/psycopg/green.h 2011-02-27 12:03:48.000000000 +0000 @@ -0,0 +1,75 @@ +/* green.c - cooperation with coroutine libraries. + * + * Copyright (C) 2010 Daniele Varrazzo + * + * This file is part of psycopg. + * + * psycopg2 is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * In addition, as a special exception, the copyright holders give + * permission to link this program with the OpenSSL library (or with + * modified versions of OpenSSL that use the same license as OpenSSL), + * and distribute linked combinations including the two. + * + * You must obey the GNU Lesser General Public License in all respects for + * all of the code used other than OpenSSL. + * + * psycopg2 is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + */ + +#ifndef PSYCOPG_GREEN_H +#define PSYCOPG_GREEN_H 1 + +#include +#include "psycopg/connection.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define psyco_set_wait_callback_doc \ +"Register a callback function to block waiting for data.\n" \ +"\n" \ +"The callback should have signature :samp:`fun({conn})` and\n" \ +"is called to wait for data available whenever a blocking function from the\n" \ +"libpq is called. Use `!set_wait_callback(None)` to revert to the\n" \ +"original behaviour (i.e. using blocking libpq functions).\n" \ +"\n" \ +"The function is an hook to allow coroutine-based libraries (such as\n" \ +"Eventlet_ or gevent_) to switch when Psycopg is blocked, allowing\n" \ +"other coroutines to run concurrently.\n" \ +"\n" \ +"See `~psycopg2.extras.wait_select()` for an example of a wait callback\n" \ +"implementation.\n" \ +"\n" \ +".. _Eventlet: http://eventlet.net/\n" \ +".. _gevent: http://www.gevent.org/\n" +HIDDEN PyObject *psyco_set_wait_callback(PyObject *self, PyObject *obj); + +#define psyco_get_wait_callback_doc \ +"Return the currently registered wait callback.\n" \ +"\n" \ +"Return `!None` if no callback is currently registered.\n" +HIDDEN PyObject *psyco_get_wait_callback(PyObject *self, PyObject *obj); + +HIDDEN int psyco_green(void); +HIDDEN int psyco_wait(connectionObject *conn); +HIDDEN PGresult *psyco_exec_green(connectionObject *conn, const char *command); + +#define EXC_IF_GREEN(cmd) \ +if (psyco_green()) { \ + PyErr_SetString(ProgrammingError, #cmd " cannot be used " \ + "with an asynchronous callback."); \ + return NULL; } + +#ifdef __cplusplus +} +#endif + +#endif /* !defined(PSYCOPG_GREEN_H) */ diff -Nru psycopg2-2.0.13/psycopg/lobject.h psycopg2-2.4.5/psycopg/lobject.h --- psycopg2-2.0.13/psycopg/lobject.h 2009-04-26 12:09:21.000000000 +0000 +++ psycopg2-2.4.5/psycopg/lobject.h 2012-03-28 21:09:15.000000000 +0000 @@ -1,32 +1,33 @@ /* lobject.h - definition for the psycopg lobject type * - * Copyright (C) 2006 Federico Di Gregorio + * Copyright (C) 2006-2010 Federico Di Gregorio * * This file is part of psycopg. * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2, - * or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * psycopg2 is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * In addition, as a special exception, the copyright holders give + * permission to link this program with the OpenSSL library (or with + * modified versions of OpenSSL that use the same license as OpenSSL), + * and distribute linked combinations including the two. + * + * You must obey the GNU Lesser General Public License in all respects for + * all of the code used other than OpenSSL. + * + * psycopg2 is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. */ #ifndef PSYCOPG_LOBJECT_H #define PSYCOPG_LOBJECT_H 1 -#include -#include #include -#include "psycopg/config.h" #include "psycopg/connection.h" #ifdef __cplusplus @@ -41,7 +42,8 @@ connectionObject *conn; /* connection owning the lobject */ long int mark; /* copied from conn->mark */ - const char *smode; /* string mode if lobject was opened */ + char *smode; /* string mode if lobject was opened */ + int mode; /* numeric version of smode */ int fd; /* the file descriptor for file-like ops */ Oid oid; /* the oid for this lobject */ @@ -49,18 +51,19 @@ /* functions exported from lobject_int.c */ -HIDDEN int lobject_open(lobjectObject *self, connectionObject *conn, - Oid oid, int mode, Oid new_oid, - const char *new_file); -HIDDEN int lobject_unlink(lobjectObject *self); -HIDDEN int lobject_export(lobjectObject *self, const char *filename); +RAISES_NEG HIDDEN int lobject_open(lobjectObject *self, connectionObject *conn, + Oid oid, const char *smode, Oid new_oid, + const char *new_file); +RAISES_NEG HIDDEN int lobject_unlink(lobjectObject *self); +RAISES_NEG HIDDEN int lobject_export(lobjectObject *self, const char *filename); -HIDDEN Py_ssize_t lobject_read(lobjectObject *self, char *buf, size_t len); -HIDDEN Py_ssize_t lobject_write(lobjectObject *self, const char *buf, +RAISES_NEG HIDDEN Py_ssize_t lobject_read(lobjectObject *self, char *buf, size_t len); +RAISES_NEG HIDDEN Py_ssize_t lobject_write(lobjectObject *self, const char *buf, size_t len); -HIDDEN int lobject_seek(lobjectObject *self, int pos, int whence); -HIDDEN int lobject_tell(lobjectObject *self); -HIDDEN int lobject_close(lobjectObject *self); +RAISES_NEG HIDDEN int lobject_seek(lobjectObject *self, int pos, int whence); +RAISES_NEG HIDDEN int lobject_tell(lobjectObject *self); +RAISES_NEG HIDDEN int lobject_truncate(lobjectObject *self, size_t len); +RAISES_NEG HIDDEN int lobject_close(lobjectObject *self); #define lobject_is_closed(self) \ ((self)->fd < 0 || !(self)->conn || (self)->conn->closed) @@ -73,18 +76,24 @@ return NULL; } #define EXC_IF_LOBJ_LEVEL0(self) \ -if (self->conn->isolation_level == 0) { \ - psyco_set_error(ProgrammingError, (PyObject*)self, \ +if (self->conn->autocommit) { \ + psyco_set_error(ProgrammingError, NULL, \ "can't use a lobject outside of transactions", NULL, NULL); \ return NULL; \ } #define EXC_IF_LOBJ_UNMARKED(self) \ if (self->conn->mark != self->mark) { \ - psyco_set_error(ProgrammingError, (PyObject*)self, \ + psyco_set_error(ProgrammingError, NULL, \ "lobject isn't valid anymore", NULL, NULL); \ return NULL; \ } +/* Values for the lobject mode */ +#define LOBJECT_READ 1 +#define LOBJECT_WRITE 2 +#define LOBJECT_BINARY 4 +#define LOBJECT_TEXT 8 + #ifdef __cplusplus } #endif diff -Nru psycopg2-2.0.13/psycopg/lobject_int.c psycopg2-2.4.5/psycopg/lobject_int.c --- psycopg2-2.0.13/psycopg/lobject_int.c 2009-10-04 21:32:35.000000000 +0000 +++ psycopg2-2.4.5/psycopg/lobject_int.c 2012-03-28 21:09:15.000000000 +0000 @@ -1,35 +1,37 @@ /* lobject_int.c - code used by the lobject object * - * Copyright (C) 2006 Federico Di Gregorio + * Copyright (C) 2006-2010 Federico Di Gregorio * * This file is part of psycopg. * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2, - * or (at your option) any later version. + * psycopg2 is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * In addition, as a special exception, the copyright holders give + * permission to link this program with the OpenSSL library (or with + * modified versions of OpenSSL that use the same license as OpenSSL), + * and distribute linked combinations including the two. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You must obey the GNU Lesser General Public License in all respects for + * all of the code used other than OpenSSL. + * + * psycopg2 is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. */ -#define PY_SSIZE_T_CLEAN -#include -#include - #define PSYCOPG_MODULE -#include "psycopg/config.h" #include "psycopg/psycopg.h" -#include "psycopg/connection.h" + #include "psycopg/lobject.h" +#include "psycopg/connection.h" #include "psycopg/pqpath.h" +#include + #ifdef PSYCOPG_EXTENSIONS static void @@ -41,31 +43,145 @@ *error = strdup(msg); } + +/* Check if the mode passed to the large object is valid. + * In case of success return a value >= 0 + * On error return a value < 0 and set an exception. + * + * Valid mode are [r|w|rw|n][t|b] + */ +RAISES_NEG static int +_lobject_parse_mode(const char *mode) +{ + int rv = 0; + size_t pos = 0; + + if (0 == strncmp("rw", mode, 2)) { + rv |= LOBJECT_READ | LOBJECT_WRITE; + pos += 2; + } + else { + switch (mode[0]) { + case 'r': + rv |= LOBJECT_READ; + pos += 1; + break; + case 'w': + rv |= LOBJECT_WRITE; + pos += 1; + break; + case 'n': + pos += 1; + break; + default: + rv |= LOBJECT_READ; + break; + } + } + + switch (mode[pos]) { + case 't': + rv |= LOBJECT_TEXT; + pos += 1; + break; + case 'b': + rv |= LOBJECT_BINARY; + pos += 1; + break; + default: +#if PY_MAJOR_VERSION < 3 + rv |= LOBJECT_BINARY; +#else + rv |= LOBJECT_TEXT; +#endif + break; + } + + if (pos != strlen(mode)) { + PyErr_Format(PyExc_ValueError, + "bad mode for lobject: '%s'", mode); + rv = -1; + } + + return rv; +} + + +/* Return a string representing the lobject mode. + * + * The return value is a new string allocated on the Python heap. + * + * The function must be called holding the GIL. + */ +static char * +_lobject_unparse_mode(int mode) +{ + char *buf; + char *c; + + /* the longest is 'rwt' */ + if (!(c = buf = PyMem_Malloc(4))) { + PyErr_NoMemory(); + return NULL; + } + + if (mode & LOBJECT_READ) { *c++ = 'r'; } + if (mode & LOBJECT_WRITE) { *c++ = 'w'; } + + if (buf == c) { + /* neither read nor write */ + *c++ = 'n'; + } + else { + if (mode & LOBJECT_TEXT) { + *c++ = 't'; + } + else { + *c++ = 'b'; + } + } + *c = '\0'; + + return buf; +} + /* lobject_open - create a new/open an existing lo */ -int +RAISES_NEG int lobject_open(lobjectObject *self, connectionObject *conn, - Oid oid, int mode, Oid new_oid, const char *new_file) + Oid oid, const char *smode, Oid new_oid, const char *new_file) { int retvalue = -1; PGresult *pgres = NULL; char *error = NULL; + int pgmode = 0; + int mode; + + if (0 > (mode = _lobject_parse_mode(smode))) { + return -1; + } Py_BEGIN_ALLOW_THREADS; pthread_mutex_lock(&(self->conn->lock)); - retvalue = pq_begin_locked(self->conn, &pgres, &error); + retvalue = pq_begin_locked(self->conn, &pgres, &error, &_save); if (retvalue < 0) goto end; /* if the oid is InvalidOid we create a new lob before opening it or we import a file from the FS, depending on the value of - new_name */ + new_file */ if (oid == InvalidOid) { if (new_file) self->oid = lo_import(self->conn->pgconn, new_file); - else - self->oid = lo_create(self->conn->pgconn, new_oid); + else { + /* Use lo_creat when possible to be more middleware-friendly. + See ticket #88. */ + if (new_oid != InvalidOid) + self->oid = lo_create(self->conn->pgconn, new_oid); + else + self->oid = lo_creat(self->conn->pgconn, INV_READ | INV_WRITE); + } Dprintf("lobject_open: large object created with oid = %d", self->oid); @@ -76,19 +192,19 @@ goto end; } - mode = INV_WRITE; + mode = (mode & ~LOBJECT_READ) | LOBJECT_WRITE; } else { self->oid = oid; - if (mode == 0) mode = INV_READ; } - /* if the oid is a real one we try to open with the given mode, - unless the mode is -1, meaning "don't open!" */ - if (mode != -1) { - self->fd = lo_open(self->conn->pgconn, self->oid, mode); - Dprintf("lobject_open: large object opened with fd = %d", - self->fd); + /* if the oid is a real one we try to open with the given mode */ + if (mode & LOBJECT_READ) { pgmode |= INV_READ; } + if (mode & LOBJECT_WRITE) { pgmode |= INV_WRITE; } + if (pgmode) { + self->fd = lo_open(self->conn->pgconn, self->oid, pgmode); + Dprintf("lobject_open: large object opened with mode = %i fd = %d", + pgmode, self->fd); if (self->fd == -1) { collect_error(self->conn, &error); @@ -96,17 +212,17 @@ goto end; } } + /* set the mode for future reference */ - switch (mode) { - case -1: - self->smode = "n"; break; - case INV_READ: - self->smode = "r"; break; - case INV_WRITE: - self->smode = "w"; break; - case INV_READ+INV_WRITE: - self->smode = "rw"; break; + self->mode = mode; + Py_BLOCK_THREADS; + self->smode = _lobject_unparse_mode(mode); + Py_UNBLOCK_THREADS; + if (NULL == self->smode) { + retvalue = 1; /* exception already set */ + goto end; } + retvalue = 0; end: @@ -115,17 +231,34 @@ if (retvalue < 0) pq_complete_error(self->conn, &pgres, &error); + /* if retvalue > 0, an exception is already set */ + return retvalue; } /* lobject_close - close an existing lo */ -static int +RAISES_NEG static int lobject_close_locked(lobjectObject *self, char **error) { int retvalue; - if (self->conn->isolation_level == 0 || + Dprintf("lobject_close_locked: conn->closed %ld", self->conn->closed); + switch (self->conn->closed) { + case 0: + /* Connection is open, go ahead */ + break; + case 1: + /* Connection is closed, return a success */ + return 0; + break; + default: + PyErr_SetString(OperationalError, "the connection is broken"); + return -1; + break; + } + + if (self->conn->autocommit || self->conn->mark != self->mark || self->fd == -1) return 0; @@ -138,7 +271,7 @@ return retvalue; } -int +RAISES_NEG int lobject_close(lobjectObject *self) { PGresult *pgres = NULL; @@ -160,7 +293,7 @@ /* lobject_unlink - remove an lo from database */ -int +RAISES_NEG int lobject_unlink(lobjectObject *self) { PGresult *pgres = NULL; @@ -170,7 +303,7 @@ Py_BEGIN_ALLOW_THREADS; pthread_mutex_lock(&(self->conn->lock)); - retvalue = pq_begin_locked(self->conn, &pgres, &error); + retvalue = pq_begin_locked(self->conn, &pgres, &error, &_save); if (retvalue < 0) goto end; @@ -194,24 +327,22 @@ /* lobject_write - write bytes to a lo */ -Py_ssize_t +RAISES_NEG Py_ssize_t lobject_write(lobjectObject *self, const char *buf, size_t len) { Py_ssize_t written; PGresult *pgres = NULL; char *error = NULL; - Dprintf("lobject_writing: fd = %d, len = %d", + Dprintf("lobject_writing: fd = %d, len = " FORMAT_CODE_SIZE_T, self->fd, len); Py_BEGIN_ALLOW_THREADS; pthread_mutex_lock(&(self->conn->lock)); - PQsetnonblocking(self->conn->pgconn, 0); written = lo_write(self->conn->pgconn, self->fd, buf, len); if (written < 0) collect_error(self->conn, &error); - PQsetnonblocking(self->conn->pgconn, 1); pthread_mutex_unlock(&(self->conn->lock)); Py_END_ALLOW_THREADS; @@ -223,7 +354,7 @@ /* lobject_read - read bytes from a lo */ -Py_ssize_t +RAISES_NEG Py_ssize_t lobject_read(lobjectObject *self, char *buf, size_t len) { Py_ssize_t n_read; @@ -247,7 +378,7 @@ /* lobject_seek - move the current position in the lo */ -int +RAISES_NEG int lobject_seek(lobjectObject *self, int pos, int whence) { PGresult *pgres = NULL; @@ -275,7 +406,7 @@ /* lobject_tell - tell the current position in the lo */ -int +RAISES_NEG int lobject_tell(lobjectObject *self) { PGresult *pgres = NULL; @@ -302,7 +433,7 @@ /* lobject_export - export to a local file */ -int +RAISES_NEG int lobject_export(lobjectObject *self, const char *filename) { PGresult *pgres = NULL; @@ -312,7 +443,7 @@ Py_BEGIN_ALLOW_THREADS; pthread_mutex_lock(&(self->conn->lock)); - retvalue = pq_begin_locked(self->conn, &pgres, &error); + retvalue = pq_begin_locked(self->conn, &pgres, &error, &_save); if (retvalue < 0) goto end; @@ -329,6 +460,36 @@ return retvalue; } +#if PG_VERSION_HEX >= 0x080300 + +RAISES_NEG int +lobject_truncate(lobjectObject *self, size_t len) +{ + int retvalue; + PGresult *pgres = NULL; + char *error = NULL; + + Dprintf("lobject_truncate: fd = %d, len = " FORMAT_CODE_SIZE_T, + self->fd, len); + + Py_BEGIN_ALLOW_THREADS; + pthread_mutex_lock(&(self->conn->lock)); + + retvalue = lo_truncate(self->conn->pgconn, self->fd, len); + Dprintf("lobject_truncate: result = %d", retvalue); + if (retvalue < 0) + collect_error(self->conn, &error); + + pthread_mutex_unlock(&(self->conn->lock)); + Py_END_ALLOW_THREADS; + + if (retvalue < 0) + pq_complete_error(self->conn, &pgres, &error); + return retvalue; + +} + +#endif /* PG_VERSION_HEX >= 0x080300 */ #endif diff -Nru psycopg2-2.0.13/psycopg/lobject_type.c psycopg2-2.4.5/psycopg/lobject_type.c --- psycopg2-2.0.13/psycopg/lobject_type.c 2009-04-26 12:09:21.000000000 +0000 +++ psycopg2-2.4.5/psycopg/lobject_type.c 2012-03-28 21:09:15.000000000 +0000 @@ -1,38 +1,38 @@ /* lobject_type.c - python interface to lobject objects * - * Copyright (C) 2003-2006 Federico Di Gregorio + * Copyright (C) 2006-2010 Federico Di Gregorio * * This file is part of psycopg. * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2, - * or (at your option) any later version. + * psycopg2 is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * In addition, as a special exception, the copyright holders give + * permission to link this program with the OpenSSL library (or with + * modified versions of OpenSSL that use the same license as OpenSSL), + * and distribute linked combinations including the two. * - * You should have received a copy of the GNU General Public Likcense - * along with this program; if not, write to the Free Software - * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You must obey the GNU Lesser General Public License in all respects for + * all of the code used other than OpenSSL. + * + * psycopg2 is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. */ -#include -#include -#include - #define PSYCOPG_MODULE -#include "psycopg/config.h" -#include "psycopg/python.h" #include "psycopg/psycopg.h" + #include "psycopg/lobject.h" #include "psycopg/connection.h" #include "psycopg/microprotocols.h" #include "psycopg/microprotocols_proto.h" #include "psycopg/pqpath.h" -#include "pgversion.h" + +#include #ifdef PSYCOPG_EXTENSIONS @@ -47,13 +47,11 @@ static PyObject * psyco_lobj_close(lobjectObject *self, PyObject *args) { - if (!PyArg_ParseTuple(args, "")) return NULL; - /* file-like objects can be closed multiple times and remember that closing the current transaction is equivalent to close all the opened large objects */ if (!lobject_is_closed(self) - && self->conn->isolation_level > 0 + && !self->conn->autocommit && self->conn->mark == self->mark) { Dprintf("psyco_lobj_close: closing lobject at %p", self); @@ -73,18 +71,48 @@ static PyObject * psyco_lobj_write(lobjectObject *self, PyObject *args) { - int len, res=0; - const char *buffer; + char *buffer; + Py_ssize_t len; + Py_ssize_t res; + PyObject *obj; + PyObject *data = NULL; + PyObject *rv = NULL; - if (!PyArg_ParseTuple(args, "s#", &buffer, &len)) return NULL; + if (!PyArg_ParseTuple(args, "O", &obj)) return NULL; EXC_IF_LOBJ_CLOSED(self); EXC_IF_LOBJ_LEVEL0(self); EXC_IF_LOBJ_UNMARKED(self); - if ((res = lobject_write(self, buffer, len)) < 0) return NULL; + if (Bytes_Check(obj)) { + Py_INCREF(obj); + data = obj; + } + else if (PyUnicode_Check(obj)) { + if (!(data = PyUnicode_AsEncodedString(obj, self->conn->codec, NULL))) { + goto exit; + } + } + else { + PyErr_Format(PyExc_TypeError, + "lobject.write requires a string; got %s instead", + Py_TYPE(obj)->tp_name); + goto exit; + } + + if (-1 == Bytes_AsStringAndSize(data, &buffer, &len)) { + goto exit; + } + + if (0 > (res = lobject_write(self, buffer, (size_t)len))) { + goto exit; + } + + rv = PyInt_FromLong((long)res); - return PyInt_FromLong((long)res); +exit: + Py_XDECREF(data); + return rv; } /* read method - read data from the lobject */ @@ -96,10 +124,11 @@ psyco_lobj_read(lobjectObject *self, PyObject *args) { PyObject *res; - int where, end, size = -1; + int where, end; + Py_ssize_t size = -1; char *buffer; - if (!PyArg_ParseTuple(args, "|i", &size)) return NULL; + if (!PyArg_ParseTuple(args, "|" CONV_CODE_PY_SSIZE_T, &size)) return NULL; EXC_IF_LOBJ_CLOSED(self); EXC_IF_LOBJ_LEVEL0(self); @@ -121,9 +150,13 @@ return NULL; } - res = PyString_FromStringAndSize(buffer, size); + if (self->mode & LOBJECT_BINARY) { + res = Bytes_FromStringAndSize(buffer, size); + } else { + res = PyUnicode_Decode(buffer, size, self->conn->codec, NULL); + } PyMem_Free(buffer); - + return res; } @@ -161,8 +194,6 @@ { int pos; - if (!PyArg_ParseTuple(args, "")) return NULL; - EXC_IF_LOBJ_CLOSED(self); EXC_IF_LOBJ_LEVEL0(self); EXC_IF_LOBJ_UNMARKED(self); @@ -181,8 +212,6 @@ static PyObject * psyco_lobj_unlink(lobjectObject *self, PyObject *args) { - if (!PyArg_ParseTuple(args, "")) return NULL; - if (lobject_unlink(self) < 0) return NULL; @@ -223,6 +252,32 @@ return closed; } +#if PG_VERSION_HEX >= 0x080300 + +#define psyco_lobj_truncate_doc \ +"truncate(len=0) -- Truncate large object to given size." + +static PyObject * +psyco_lobj_truncate(lobjectObject *self, PyObject *args) +{ + int len = 0; + + if (!PyArg_ParseTuple(args, "|i", &len)) + return NULL; + + EXC_IF_LOBJ_CLOSED(self); + EXC_IF_LOBJ_LEVEL0(self); + EXC_IF_LOBJ_UNMARKED(self); + + if (lobject_truncate(self, len) < 0) + return NULL; + + Py_INCREF(Py_None); + return Py_None; +} + +#endif /* PG_VERSION_HEX >= 0x080300 */ + /** the lobject object **/ @@ -236,23 +291,27 @@ {"seek", (PyCFunction)psyco_lobj_seek, METH_VARARGS, psyco_lobj_seek_doc}, {"tell", (PyCFunction)psyco_lobj_tell, - METH_VARARGS, psyco_lobj_tell_doc}, + METH_NOARGS, psyco_lobj_tell_doc}, {"close", (PyCFunction)psyco_lobj_close, - METH_VARARGS, psyco_lobj_close_doc}, + METH_NOARGS, psyco_lobj_close_doc}, {"unlink",(PyCFunction)psyco_lobj_unlink, - METH_VARARGS, psyco_lobj_unlink_doc}, + METH_NOARGS, psyco_lobj_unlink_doc}, {"export",(PyCFunction)psyco_lobj_export, METH_VARARGS, psyco_lobj_export_doc}, +#if PG_VERSION_HEX >= 0x080300 + {"truncate",(PyCFunction)psyco_lobj_truncate, + METH_VARARGS, psyco_lobj_truncate_doc}, +#endif /* PG_VERSION_HEX >= 0x080300 */ {NULL} }; /* object member list */ static struct PyMemberDef lobjectObject_members[] = { - {"oid", T_UINT, offsetof(lobjectObject, oid), RO, + {"oid", T_UINT, offsetof(lobjectObject, oid), READONLY, "The backend OID associated to this lobject."}, - {"mode", T_STRING, offsetof(lobjectObject, smode), RO, - "Open mode ('r', 'w', 'rw' or 'n')."}, + {"mode", T_STRING, offsetof(lobjectObject, smode), READONLY, + "Open mode."}, {NULL} }; @@ -268,12 +327,12 @@ static int lobject_setup(lobjectObject *self, connectionObject *conn, - Oid oid, int mode, Oid new_oid, const char *new_file) + Oid oid, const char *smode, Oid new_oid, const char *new_file) { Dprintf("lobject_setup: init lobject object at %p", self); - if (conn->isolation_level == 0) { - psyco_set_error(ProgrammingError, (PyObject*)self, + if (conn->autocommit) { + psyco_set_error(ProgrammingError, NULL, "can't use a lobject outside of transactions", NULL, NULL); return -1; } @@ -286,11 +345,11 @@ self->fd = -1; self->oid = InvalidOid; - if (lobject_open(self, conn, oid, mode, new_oid, new_file) == -1) + if (0 != lobject_open(self, conn, oid, smode, new_oid, new_file)) return -1; Dprintf("lobject_setup: good lobject object at %p, refcnt = " - FORMAT_CODE_PY_SSIZE_T, self, ((PyObject *)self)->ob_refcnt); + FORMAT_CODE_PY_SSIZE_T, self, Py_REFCNT(self)); Dprintf("lobject_setup: oid = %d, fd = %d", self->oid, self->fd); return 0; } @@ -303,27 +362,28 @@ if (lobject_close(self) < 0) PyErr_Print(); Py_XDECREF((PyObject*)self->conn); + PyMem_Free(self->smode); Dprintf("lobject_dealloc: deleted lobject object at %p, refcnt = " - FORMAT_CODE_PY_SSIZE_T, obj, obj->ob_refcnt); + FORMAT_CODE_PY_SSIZE_T, obj, Py_REFCNT(obj)); - obj->ob_type->tp_free(obj); + Py_TYPE(obj)->tp_free(obj); } static int lobject_init(PyObject *obj, PyObject *args, PyObject *kwds) { - Oid oid=InvalidOid, new_oid=InvalidOid; - int mode=0; + int oid = (int)InvalidOid, new_oid = (int)InvalidOid; + const char *smode = ""; const char *new_file = NULL; PyObject *conn; - if (!PyArg_ParseTuple(args, "O|iiis", - &conn, &oid, &mode, &new_oid, &new_file)) + if (!PyArg_ParseTuple(args, "O|iziz", + &conn, &oid, &smode, &new_oid, &new_file)) return -1; return lobject_setup((lobjectObject *)obj, - (connectionObject *)conn, oid, mode, new_oid, new_file); + (connectionObject *)conn, (Oid)oid, smode, (Oid)new_oid, new_file); } static PyObject * @@ -352,8 +412,7 @@ "A database large object." PyTypeObject lobjectType = { - PyObject_HEAD_INIT(NULL) - 0, + PyVarObject_HEAD_INIT(NULL, 0) "psycopg2._psycopg.lobject", sizeof(lobjectObject), 0, diff -Nru psycopg2-2.0.13/psycopg/microprotocols.c psycopg2-2.4.5/psycopg/microprotocols.c --- psycopg2-2.0.13/psycopg/microprotocols.c 2009-04-26 12:09:21.000000000 +0000 +++ psycopg2-2.4.5/psycopg/microprotocols.c 2012-03-28 21:09:15.000000000 +0000 @@ -1,36 +1,35 @@ /* microprotocols.c - minimalist and non-validating protocols implementation * - * Copyright (C) 2003-2004 Federico Di Gregorio + * Copyright (C) 2003-2010 Federico Di Gregorio * * This file is part of psycopg. * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2, - * or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * psycopg2 is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * In addition, as a special exception, the copyright holders give + * permission to link this program with the OpenSSL library (or with + * modified versions of OpenSSL that use the same license as OpenSSL), + * and distribute linked combinations including the two. + * + * You must obey the GNU Lesser General Public License in all respects for + * all of the code used other than OpenSSL. + * + * psycopg2 is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. */ -#define PY_SSIZE_T_CLEAN -#include -#include - #define PSYCOPG_MODULE -#include "psycopg/config.h" -#include "psycopg/python.h" #include "psycopg/psycopg.h" -#include "psycopg/cursor.h" -#include "psycopg/connection.h" + #include "psycopg/microprotocols.h" #include "psycopg/microprotocols_proto.h" +#include "psycopg/cursor.h" +#include "psycopg/connection.h" /** the adapters registry **/ @@ -53,102 +52,216 @@ } -/* microprotocols_add - add a reverse type-caster to the dictionary */ - +/* microprotocols_add - add a reverse type-caster to the dictionary + * + * Return 0 on success, else -1 and set an exception. + */ int microprotocols_add(PyTypeObject *type, PyObject *proto, PyObject *cast) { + PyObject *key = NULL; + int rv = -1; + if (proto == NULL) proto = (PyObject*)&isqlquoteType; Dprintf("microprotocols_add: cast %p for (%s, ?)", cast, type->tp_name); - PyDict_SetItem(psyco_adapters, - Py_BuildValue("(OO)", (PyObject*)type, proto), - cast); - return 0; + if (!(key = PyTuple_Pack(2, (PyObject*)type, proto))) { goto exit; } + if (0 != PyDict_SetItem(psyco_adapters, key, cast)) { goto exit; } + + rv = 0; + +exit: + Py_XDECREF(key); + return rv; +} + +/* Check if one of `obj` superclasses has an adapter for `proto`. + * + * If it does, return a *borrowed reference* to the adapter, else to None. + */ +BORROWED static PyObject * +_get_superclass_adapter(PyObject *obj, PyObject *proto) +{ + PyTypeObject *type; + PyObject *mro, *st; + PyObject *key, *adapter; + Py_ssize_t i, ii; + + type = Py_TYPE(obj); + if (!( +#if PY_MAJOR_VERSION < 3 + (Py_TPFLAGS_HAVE_CLASS & type->tp_flags) && +#endif + type->tp_mro)) { + /* has no mro */ + return Py_None; + } + + /* Walk the mro from the most specific subclass. */ + mro = type->tp_mro; + for (i = 1, ii = PyTuple_GET_SIZE(mro); i < ii; ++i) { + st = PyTuple_GET_ITEM(mro, i); + if (!(key = PyTuple_Pack(2, st, proto))) { return NULL; } + adapter = PyDict_GetItem(psyco_adapters, key); + Py_DECREF(key); + + if (adapter) { + Dprintf( + "microprotocols_adapt: using '%s' adapter to adapt '%s'", + ((PyTypeObject *)st)->tp_name, type->tp_name); + + /* register this adapter as good for the subclass too, + * so that the next time it will be found in the fast path */ + + /* Well, no, maybe this is not a good idea. + * It would become a leak in case of dynamic + * classes generated in a loop (think namedtuples). */ + + /* key = PyTuple_Pack(2, (PyObject*)type, proto); + * PyDict_SetItem(psyco_adapters, key, adapter); + * Py_DECREF(key); + */ + return adapter; + } + } + return Py_None; } + /* microprotocols_adapt - adapt an object to the built-in protocol */ PyObject * microprotocols_adapt(PyObject *obj, PyObject *proto, PyObject *alt) { - PyObject *adapter, *key; + PyObject *adapter, *adapted, *key, *meth; + char buffer[256]; /* 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 PyString_FromString("NULL"); - Dprintf("microprotocols_adapt: trying to adapt %s", obj->ob_type->tp_name); + Dprintf("microprotocols_adapt: trying to adapt %s", + Py_TYPE(obj)->tp_name); /* look for an adapter in the registry */ - key = Py_BuildValue("(OO)", (PyObject*)obj->ob_type, proto); + if (!(key = PyTuple_Pack(2, Py_TYPE(obj), proto))) { return NULL; } adapter = PyDict_GetItem(psyco_adapters, key); Py_DECREF(key); if (adapter) { - PyObject *adapted = PyObject_CallFunctionObjArgs(adapter, obj, NULL); + adapted = PyObject_CallFunctionObjArgs(adapter, obj, NULL); + return adapted; + } + + /* Check if a superclass can be adapted and use the same adapter. */ + if (!(adapter = _get_superclass_adapter(obj, proto))) { + return NULL; + } + if (Py_None != adapter) { + adapted = PyObject_CallFunctionObjArgs(adapter, obj, NULL); return adapted; } /* try to have the protocol adapt this object*/ - if (PyObject_HasAttrString(proto, "__adapt__")) { - PyObject *adapted = PyObject_CallMethod(proto, "__adapt__", "O", obj); + if ((meth = PyObject_GetAttrString(proto, "__adapt__"))) { + adapted = PyObject_CallFunctionObjArgs(meth, obj, NULL); + Py_DECREF(meth); if (adapted && adapted != Py_None) return adapted; Py_XDECREF(adapted); - if (PyErr_Occurred() && !PyErr_ExceptionMatches(PyExc_TypeError)) - return NULL; + if (PyErr_Occurred()) { + if (PyErr_ExceptionMatches(PyExc_TypeError)) { + PyErr_Clear(); + } else { + return NULL; + } + } + } + else { + /* proto.__adapt__ not found. */ + PyErr_Clear(); } /* and finally try to have the object adapt itself */ - if (PyObject_HasAttrString(obj, "__conform__")) { - PyObject *adapted = PyObject_CallMethod(obj, "__conform__","O", proto); + if ((meth = PyObject_GetAttrString(obj, "__conform__"))) { + adapted = PyObject_CallFunctionObjArgs(meth, proto, NULL); + Py_DECREF(meth); if (adapted && adapted != Py_None) return adapted; Py_XDECREF(adapted); - if (PyErr_Occurred() && !PyErr_ExceptionMatches(PyExc_TypeError)) - return NULL; + if (PyErr_Occurred()) { + if (PyErr_ExceptionMatches(PyExc_TypeError)) { + PyErr_Clear(); + } else { + return NULL; + } + } + } + else { + /* obj.__conform__ not found. */ + PyErr_Clear(); } /* else set the right exception and return NULL */ - psyco_set_error(ProgrammingError, NULL, "can't adapt", NULL, NULL); + PyOS_snprintf(buffer, 255, "can't adapt type '%s'", + Py_TYPE(obj)->tp_name); + psyco_set_error(ProgrammingError, NULL, buffer, NULL, NULL); return NULL; } -/* microprotocol_getquoted - utility function that adapt and call getquoted */ +/* microprotocol_getquoted - utility function that adapt and call getquoted. + * + * Return a bytes string, NULL on error. + */ PyObject * microprotocol_getquoted(PyObject *obj, connectionObject *conn) { PyObject *res = NULL; - PyObject *tmp = microprotocols_adapt( - obj, (PyObject*)&isqlquoteType, NULL); + PyObject *prepare = NULL; + PyObject *adapted; - if (tmp != NULL) { - Dprintf("microprotocol_getquoted: adapted to %s", - tmp->ob_type->tp_name); - - /* if requested prepare the object passing it the connection */ - if (PyObject_HasAttrString(tmp, "prepare") && conn) { - res = PyObject_CallMethod(tmp, "prepare", "O", (PyObject*)conn); - if (res == NULL) { - Py_DECREF(tmp); - return NULL; - } - else { + if (!(adapted = microprotocols_adapt(obj, (PyObject*)&isqlquoteType, NULL))) { + goto exit; + } + + Dprintf("microprotocol_getquoted: adapted to %s", + Py_TYPE(adapted)->tp_name); + + /* if requested prepare the object passing it the connection */ + if (conn) { + if ((prepare = PyObject_GetAttrString(adapted, "prepare"))) { + res = PyObject_CallFunctionObjArgs( + prepare, (PyObject *)conn, NULL); + if (res) { Py_DECREF(res); + res = NULL; + } else { + goto exit; } } + else { + /* adapted.prepare not found */ + PyErr_Clear(); + } + } - /* call the getquoted method on tmp (that should exist because we - adapted to the right protocol) */ - res = PyObject_CallMethod(tmp, "getquoted", NULL); - Py_DECREF(tmp); + /* call the getquoted method on adapted (that should exist because we + adapted to the right protocol) */ + res = PyObject_CallMethod(adapted, "getquoted", NULL); + + /* Convert to bytes. */ + if (res && PyUnicode_CheckExact(res)) { + PyObject *b; + const char *codec; + codec = (conn && conn->codec) ? conn->codec : "utf8"; + b = PyUnicode_AsEncodedString(res, codec, NULL); + Py_DECREF(res); + res = b; } +exit: + Py_XDECREF(adapted); + Py_XDECREF(prepare); + /* we return res with one extra reference, the caller shall free it */ return res; } diff -Nru psycopg2-2.0.13/psycopg/microprotocols.h psycopg2-2.4.5/psycopg/microprotocols.h --- psycopg2-2.0.13/psycopg/microprotocols.h 2009-04-26 12:09:21.000000000 +0000 +++ psycopg2-2.4.5/psycopg/microprotocols.h 2011-02-06 15:58:34.000000000 +0000 @@ -1,30 +1,31 @@ /* microprotocols.c - definitions for minimalist and non-validating protocols * - * Copyright (C) 2003-2004 Federico Di Gregorio + * Copyright (C) 2003-2010 Federico Di Gregorio * * This file is part of psycopg. * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2, - * or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * psycopg2 is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * In addition, as a special exception, the copyright holders give + * permission to link this program with the OpenSSL library (or with + * modified versions of OpenSSL that use the same license as OpenSSL), + * and distribute linked combinations including the two. + * + * You must obey the GNU Lesser General Public License in all respects for + * all of the code used other than OpenSSL. + * + * psycopg2 is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. */ #ifndef PSYCOPG_MICROPROTOCOLS_H #define PSYCOPG_MICROPROTOCOLS_H 1 -#define PY_SSIZE_T_CLEAN -#include -#include "psycopg/config.h" #include "psycopg/connection.h" #include "psycopg/cursor.h" diff -Nru psycopg2-2.0.13/psycopg/microprotocols_proto.c psycopg2-2.4.5/psycopg/microprotocols_proto.c --- psycopg2-2.0.13/psycopg/microprotocols_proto.c 2009-05-09 13:01:04.000000000 +0000 +++ psycopg2-2.4.5/psycopg/microprotocols_proto.c 2011-02-06 15:58:34.000000000 +0000 @@ -1,37 +1,35 @@ /* microprotocol_proto.c - psycopg protocols * - * Copyright (C) 2003 Federico Di Gregorio + * Copyright (C) 2003-2010 Federico Di Gregorio * * This file is part of psycopg. * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2, - * or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * psycopg2 is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * In addition, as a special exception, the copyright holders give + * permission to link this program with the OpenSSL library (or with + * modified versions of OpenSSL that use the same license as OpenSSL), + * and distribute linked combinations including the two. + * + * You must obey the GNU Lesser General Public License in all respects for + * all of the code used other than OpenSSL. + * + * psycopg2 is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. */ -#define PY_SSIZE_T_CLEAN -#include -#include -#include - -#include - #define PSYCOPG_MODULE -#include "psycopg/config.h" -#include "psycopg/python.h" #include "psycopg/psycopg.h" + #include "psycopg/microprotocols_proto.h" +#include + /** void protocol implementation **/ @@ -44,8 +42,6 @@ static PyObject * psyco_isqlquote_getquoted(isqlquoteObject *self, PyObject *args) { - if (!PyArg_ParseTuple(args, "")) return NULL; - Py_INCREF(Py_None); return Py_None; } @@ -58,8 +54,6 @@ static PyObject * psyco_isqlquote_getbinary(isqlquoteObject *self, PyObject *args) { - if (!PyArg_ParseTuple(args, "")) return NULL; - Py_INCREF(Py_None); return Py_None; } @@ -72,8 +66,6 @@ static PyObject * psyco_isqlquote_getbuffer(isqlquoteObject *self, PyObject *args) { - if (!PyArg_ParseTuple(args, "")) return NULL; - Py_INCREF(Py_None); return Py_None; } @@ -87,11 +79,11 @@ static struct PyMethodDef isqlquoteObject_methods[] = { {"getquoted", (PyCFunction)psyco_isqlquote_getquoted, - METH_VARARGS, psyco_isqlquote_getquoted_doc}, + METH_NOARGS, psyco_isqlquote_getquoted_doc}, {"getbinary", (PyCFunction)psyco_isqlquote_getbinary, - METH_VARARGS, psyco_isqlquote_getbinary_doc}, + METH_NOARGS, psyco_isqlquote_getbinary_doc}, {"getbuffer", (PyCFunction)psyco_isqlquote_getbuffer, - METH_VARARGS, psyco_isqlquote_getbuffer_doc}, + METH_NOARGS, psyco_isqlquote_getbuffer_doc}, /* {"prepare", (PyCFunction)psyco_isqlquote_prepare, METH_VARARGS, psyco_isqlquote_prepare_doc}, */ {NULL} @@ -101,7 +93,7 @@ static struct PyMemberDef isqlquoteObject_members[] = { /* DBAPI-2.0 extensions (exception objects) */ - {"_wrapped", T_OBJECT, offsetof(isqlquoteObject, wrapped), RO}, + {"_wrapped", T_OBJECT, offsetof(isqlquoteObject, wrapped), READONLY}, {NULL} }; @@ -123,7 +115,7 @@ Py_XDECREF(self->wrapped); - obj->ob_type->tp_free(obj); + Py_TYPE(obj)->tp_free(obj); } static int @@ -158,8 +150,7 @@ "returning the SQL representation of the object.\n\n" PyTypeObject isqlquoteType = { - PyObject_HEAD_INIT(NULL) - 0, + PyVarObject_HEAD_INIT(NULL, 0) "psycopg2._psycopg.ISQLQuote", sizeof(isqlquoteObject), 0, diff -Nru psycopg2-2.0.13/psycopg/microprotocols_proto.h psycopg2-2.4.5/psycopg/microprotocols_proto.h --- psycopg2-2.0.13/psycopg/microprotocols_proto.h 2009-04-26 12:09:21.000000000 +0000 +++ psycopg2-2.4.5/psycopg/microprotocols_proto.h 2011-02-06 15:58:34.000000000 +0000 @@ -1,33 +1,31 @@ /* microporotocols_proto.h - definiton for psycopg's protocols * - * Copyright (C) 2004 Federico Di Gregorio + * Copyright (C) 2003-2010 Federico Di Gregorio * * This file is part of psycopg. * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2, - * or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * psycopg2 is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * In addition, as a special exception, the copyright holders give + * permission to link this program with the OpenSSL library (or with + * modified versions of OpenSSL that use the same license as OpenSSL), + * and distribute linked combinations including the two. + * + * You must obey the GNU Lesser General Public License in all respects for + * all of the code used other than OpenSSL. + * + * psycopg2 is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. */ #ifndef PSYCOPG_ISQLQUOTE_H #define PSYCOPG_ISQLQUOTE_H 1 -#define PY_SSIZE_T_CLEAN -#include -#include - -#include "psycopg/config.h" - #ifdef __cplusplus extern "C" { #endif diff -Nru psycopg2-2.0.13/psycopg/notify.h psycopg2-2.4.5/psycopg/notify.h --- psycopg2-2.0.13/psycopg/notify.h 1970-01-01 00:00:00.000000000 +0000 +++ psycopg2-2.4.5/psycopg/notify.h 2011-02-06 15:58:34.000000000 +0000 @@ -0,0 +1,40 @@ +/* notify.h - definition for the psycopg Notify type + * + * Copyright (C) 2010 Daniele Varrazzo + * + * This file is part of psycopg. + * + * psycopg2 is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * In addition, as a special exception, the copyright holders give + * permission to link this program with the OpenSSL library (or with + * modified versions of OpenSSL that use the same license as OpenSSL), + * and distribute linked combinations including the two. + * + * You must obey the GNU Lesser General Public License in all respects for + * all of the code used other than OpenSSL. + * + * psycopg2 is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + */ + +#ifndef PSYCOPG_NOTIFY_H +#define PSYCOPG_NOTIFY_H 1 + +extern HIDDEN PyTypeObject NotifyType; + +typedef struct { + PyObject_HEAD + + PyObject *pid; + PyObject *channel; + PyObject *payload; + +} NotifyObject; + +#endif /* PSYCOPG_NOTIFY_H */ diff -Nru psycopg2-2.0.13/psycopg/notify_type.c psycopg2-2.4.5/psycopg/notify_type.c --- psycopg2-2.0.13/psycopg/notify_type.c 1970-01-01 00:00:00.000000000 +0000 +++ psycopg2-2.4.5/psycopg/notify_type.c 2011-02-27 12:03:48.000000000 +0000 @@ -0,0 +1,340 @@ +/* notify_type.c - python interface to Notify objects + * + * Copyright (C) 2010 Daniele Varrazzo + * + * This file is part of psycopg. + * + * psycopg2 is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * In addition, as a special exception, the copyright holders give + * permission to link this program with the OpenSSL library (or with + * modified versions of OpenSSL that use the same license as OpenSSL), + * and distribute linked combinations including the two. + * + * You must obey the GNU Lesser General Public License in all respects for + * all of the code used other than OpenSSL. + * + * psycopg2 is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + */ + +#define PSYCOPG_MODULE +#include "psycopg/psycopg.h" + +#include "psycopg/notify.h" + + +static const char notify_doc[] = + "A notification received from the backend.\n\n" + "`!Notify` instances are made available upon reception on the\n" + "`~connection.notifies` member of the listening connection. The object\n" + "can be also accessed as a 2 items tuple returning the members\n" + ":samp:`({pid},{channel})` for backward compatibility.\n\n" + "See :ref:`async-notify` for details."; + +static const char pid_doc[] = + "The ID of the backend process that sent the notification.\n\n" + "Note: if the sending session was handled by Psycopg, you can use\n" + "`~connection.get_backend_pid()` to know its PID."; + +static const char channel_doc[] = + "The name of the channel to which the notification was sent."; + +static const char payload_doc[] = + "The payload message of the notification.\n\n" + "Attaching a payload to a notification is only available since\n" + "PostgreSQL 9.0: for notifications received from previous versions\n" + "of the server this member is always the empty string."; + +static PyMemberDef notify_members[] = { + { "pid", T_OBJECT, offsetof(NotifyObject, pid), READONLY, (char *)pid_doc }, + { "channel", T_OBJECT, offsetof(NotifyObject, channel), READONLY, (char *)channel_doc }, + { "payload", T_OBJECT, offsetof(NotifyObject, payload), READONLY, (char *)payload_doc }, + { NULL } +}; + +static PyObject * +notify_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) +{ + NotifyObject *self = (NotifyObject *)type->tp_alloc(type, 0); + + return (PyObject *)self; +} + +static int +notify_init(NotifyObject *self, PyObject *args, PyObject *kwargs) +{ + static char *kwlist[] = {"pid", "channel", "payload", NULL}; + PyObject *pid = NULL, *channel = NULL, *payload = NULL; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO|O", kwlist, + &pid, &channel, &payload)) { + return -1; + } + + if (!payload) { + payload = Text_FromUTF8(""); + } + + Py_CLEAR(self->pid); + Py_INCREF(pid); + self->pid = pid; + + Py_CLEAR(self->channel); + Py_INCREF(channel); + self->channel = channel; + + Py_CLEAR(self->payload); + Py_INCREF(payload); + self->payload = payload; + + return 0; +} + +static int +notify_traverse(NotifyObject *self, visitproc visit, void *arg) +{ + Py_VISIT(self->pid); + Py_VISIT(self->channel); + Py_VISIT(self->payload); + return 0; +} + +static void +notify_dealloc(NotifyObject *self) +{ + Py_CLEAR(self->pid); + Py_CLEAR(self->channel); + Py_CLEAR(self->payload); + + Py_TYPE(self)->tp_free((PyObject *)self); +} + +static void +notify_del(PyObject *self) +{ + PyObject_GC_Del(self); +} + + +/* Convert a notify into a 2 or 3 items tuple. */ +static PyObject * +notify_astuple(NotifyObject *self, int with_payload) +{ + PyObject *tself; + if (!(tself = PyTuple_New(with_payload ? 3 : 2))) { return NULL; } + + Py_INCREF(self->pid); + PyTuple_SET_ITEM(tself, 0, self->pid); + + Py_INCREF(self->channel); + PyTuple_SET_ITEM(tself, 1, self->channel); + + if (with_payload) { + Py_INCREF(self->payload); + PyTuple_SET_ITEM(tself, 2, self->payload); + } + + return tself; +} + +/* note on Notify-tuple comparison. + * + * Such a comparison is required otherwise a check n == (pid, channel) + * would fail. We also want to compare two notifies, and the obvious meaning is + * "check that all the attributes are equal". Unfortunately this leads to an + * inconsistent situation: + * Notify(pid, channel, payload1) + * == (pid, channel) + * == Notify(pid, channel, payload2) + * even when payload1 != payload2. We can probably live with that, but hashing + * makes things worse: hashability is a desirable property for a Notify, and + * to maintain compatibility we should put a notify object in the same bucket + * of a 2-item tuples... but we can't put all the payloads with the same + * (pid, channel) in the same bucket: it would be an extremely poor hash. + * So we maintain compatibility in the sense that notify without payload + * behave as 2-item tuples in term of hashability, but if a payload is present + * the (pid, channel) pair is no more equivalent as dict key to the Notify. + */ +static PyObject * +notify_richcompare(NotifyObject *self, PyObject *other, int op) +{ + PyObject *rv = NULL; + PyObject *tself = NULL; + PyObject *tother = NULL; + + if (Py_TYPE(other) == &NotifyType) { + if (!(tself = notify_astuple(self, 1))) { goto exit; } + if (!(tother = notify_astuple((NotifyObject *)other, 1))) { goto exit; } + rv = PyObject_RichCompare(tself, tother, op); + } + else if (PyTuple_Check(other)) { + if (!(tself = notify_astuple(self, 0))) { goto exit; } + rv = PyObject_RichCompare(tself, other, op); + } + else { + Py_INCREF(Py_False); + rv = Py_False; + } + +exit: + Py_XDECREF(tself); + Py_XDECREF(tother); + return rv; +} + + +static Py_hash_t +notify_hash(NotifyObject *self) +{ + Py_hash_t rv = -1L; + PyObject *tself = NULL; + + /* if self == a tuple, then their hashes are the same. */ + int has_payload = PyObject_IsTrue(self->payload); + if (!(tself = notify_astuple(self, has_payload))) { goto exit; } + rv = PyObject_Hash(tself); + +exit: + Py_XDECREF(tself); + return rv; +} + + +static PyObject* +notify_repr(NotifyObject *self) +{ + PyObject *rv = NULL; + PyObject *format = NULL; + PyObject *args = NULL; + + if (!(format = Text_FromUTF8("Notify(%r, %r, %r)"))) { + goto exit; + } + + if (!(args = PyTuple_New(3))) { goto exit; } + Py_INCREF(self->pid); + PyTuple_SET_ITEM(args, 0, self->pid); + Py_INCREF(self->channel); + PyTuple_SET_ITEM(args, 1, self->channel); + Py_INCREF(self->payload); + PyTuple_SET_ITEM(args, 2, self->payload); + + rv = Text_Format(format, args); + +exit: + Py_XDECREF(args); + Py_XDECREF(format); + + return rv; +} + +/* Notify can be accessed as a 2 items tuple for backward compatibility */ + +static Py_ssize_t +notify_len(NotifyObject *self) +{ + return 2; +} + +static PyObject * +notify_getitem(NotifyObject *self, Py_ssize_t item) +{ + if (item < 0) + item += 2; + + switch (item) { + case 0: + Py_INCREF(self->pid); + return self->pid; + case 1: + Py_INCREF(self->channel); + return self->channel; + default: + PyErr_SetString(PyExc_IndexError, "index out of range"); + return NULL; + } +} + +static PySequenceMethods notify_sequence = { + (lenfunc)notify_len, /* sq_length */ + 0, /* sq_concat */ + 0, /* sq_repeat */ + (ssizeargfunc)notify_getitem, /* sq_item */ + 0, /* sq_slice */ + 0, /* sq_ass_item */ + 0, /* sq_ass_slice */ + 0, /* sq_contains */ + 0, /* sq_inplace_concat */ + 0, /* sq_inplace_repeat */ +}; + + +PyTypeObject NotifyType = { + PyVarObject_HEAD_INIT(NULL, 0) + "psycopg2.extensions.Notify", + sizeof(NotifyObject), + 0, + (destructor)notify_dealloc, /* tp_dealloc */ + 0, /*tp_print*/ + + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + + 0, /*tp_compare*/ + + (reprfunc)notify_repr, /*tp_repr*/ + 0, /*tp_as_number*/ + ¬ify_sequence, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + (hashfunc)notify_hash, /*tp_hash */ + + 0, /*tp_call*/ + 0, /*tp_str*/ + + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + + Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_GC, /*tp_flags*/ + notify_doc, /*tp_doc*/ + + (traverseproc)notify_traverse, /*tp_traverse*/ + 0, /*tp_clear*/ + + (richcmpfunc)notify_richcompare, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + + /* Attribute descriptor and subclassing stuff */ + + 0, /*tp_methods*/ + notify_members, /*tp_members*/ + 0, /*tp_getset*/ + 0, /*tp_base*/ + 0, /*tp_dict*/ + + 0, /*tp_descr_get*/ + 0, /*tp_descr_set*/ + 0, /*tp_dictoffset*/ + + (initproc)notify_init, /*tp_init*/ + 0, /*tp_alloc will be set to PyType_GenericAlloc in module init*/ + notify_new, /*tp_new*/ + (freefunc)notify_del, /*tp_free Low-level free-memory routine */ + 0, /*tp_is_gc For PyObject_IS_GC */ + 0, /*tp_bases*/ + 0, /*tp_mro method resolution order */ + 0, /*tp_cache*/ + 0, /*tp_subclasses*/ + 0 /*tp_weaklist*/ +}; + + diff -Nru psycopg2-2.0.13/psycopg/pgversion.h psycopg2-2.4.5/psycopg/pgversion.h --- psycopg2-2.0.13/psycopg/pgversion.h 2009-04-26 12:09:21.000000000 +0000 +++ psycopg2-2.4.5/psycopg/pgversion.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,2 +0,0 @@ -#define PG_VERSION_MAJOR 7 -#define PG_VERSION_MINOR 4 diff -Nru psycopg2-2.0.13/psycopg/pqpath.c psycopg2-2.4.5/psycopg/pqpath.c --- psycopg2-2.0.13/psycopg/pqpath.c 2009-10-04 21:32:35.000000000 +0000 +++ psycopg2-2.4.5/psycopg/pqpath.c 2012-03-28 21:09:15.000000000 +0000 @@ -1,22 +1,26 @@ /* pqpath.c - single path into libpq * - * Copyright (C) 2003 Federico Di Gregorio + * Copyright (C) 2003-2010 Federico Di Gregorio * * This file is part of psycopg. * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2, - * or (at your option) any later version. + * psycopg2 is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * In addition, as a special exception, the copyright holders give + * permission to link this program with the OpenSSL library (or with + * modified versions of OpenSSL that use the same license as OpenSSL), + * and distribute linked combinations including the two. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You must obey the GNU Lesser General Public License in all respects for + * all of the code used other than OpenSSL. + * + * psycopg2 is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. */ /* IMPORTANT NOTE: no function in this file do its own connection locking @@ -25,20 +29,20 @@ connection. */ -#define PY_SSIZE_T_CLEAN -#include -#include - #define PSYCOPG_MODULE -#include "psycopg/config.h" -#include "psycopg/python.h" #include "psycopg/psycopg.h" + #include "psycopg/pqpath.h" #include "psycopg/connection.h" #include "psycopg/cursor.h" +#include "psycopg/green.h" #include "psycopg/typecast.h" #include "psycopg/pgtypes.h" -#include "psycopg/pgversion.h" + +#include + + +extern HIDDEN PyObject *psyco_DescriptionType; /* Strip off the severity from a Postgres error message. */ @@ -60,7 +64,7 @@ code. A list of error codes can be found at: http://www.postgresql.org/docs/current/static/errcodes-appendix.html */ -static PyObject * +BORROWED static PyObject * exception_from_sqlstate(const char *sqlstate) { switch (sqlstate[0]) { @@ -72,6 +76,7 @@ break; case '2': switch (sqlstate[1]) { + case '0': /* Class 20 - Case Not Found */ case '1': /* Class 21 - Cardinality Violation */ return ProgrammingError; case '2': /* Class 22 - Data Exception */ @@ -131,6 +136,8 @@ return OperationalError; case 'F': /* Class F0 - Configuration File Error */ return InternalError; + case 'H': /* Class HV - Foreign Data Wrapper Error (SQL/MED) */ + return OperationalError; case 'P': /* Class P0 - PL/pgSQL Error */ return InternalError; case 'X': /* Class XX - Internal Error */ @@ -144,20 +151,20 @@ This function should be called while holding the GIL. */ -static void +RAISES static void pq_raise(connectionObject *conn, cursorObject *curs, PGresult *pgres) { - PyObject *pgc = (PyObject*)curs; PyObject *exc = NULL; const char *err = NULL; const char *err2 = NULL; const char *code = NULL; if (conn == NULL) { - PyErr_SetString(Error, "psycopg went psycotic and raised a null error"); + PyErr_SetString(DatabaseError, + "psycopg went psycotic and raised a null error"); return; } - + /* if the connection has somehow beed broken, we mark the connection object as closed but requiring cleanup */ if (conn->pgconn != NULL && PQstatus(conn->pgconn) == CONNECTION_BAD) @@ -168,20 +175,23 @@ if (pgres) { err = PQresultErrorMessage(pgres); -#ifdef HAVE_PQPROTOCOL3 - if (err != NULL && conn->protocol == 3) { + if (err != NULL) { + Dprintf("pq_raise: PQresultErrorMessage: err=%s", err); code = PQresultErrorField(pgres, PG_DIAG_SQLSTATE); } -#endif } - if (err == NULL) + if (err == NULL) { err = PQerrorMessage(conn->pgconn); + Dprintf("pq_raise: PQerrorMessage: err=%s", err); + } /* if the is no error message we probably called pq_raise without reason: we need to set an exception anyway because the caller will probably - raise and a meaningful message is better than an empty one */ - if (err == NULL) { - PyErr_SetString(Error, "psycopg went psycotic without error set"); + raise and a meaningful message is better than an empty one. + Note: it can happen without it being our error: see ticket #82 */ + if (err == NULL || err[0] == '\0') { + PyErr_SetString(DatabaseError, + "error with no message from the libpq"); return; } @@ -190,30 +200,17 @@ if (code != NULL) { exc = exception_from_sqlstate(code); } - - /* if exc is still NULL psycopg was not built with HAVE_PQPROTOCOL3 or the - connection is using protocol 2: in both cases we default to comparing - error messages */ - if (exc == NULL) { - if (!strncmp(err, "ERROR: Cannot insert a duplicate key", 37) - || !strncmp(err, "ERROR: ExecAppend: Fail to add null", 36) - || strstr(err, "referential integrity violation")) - exc = IntegrityError; - else if (strstr(err, "could not serialize") || - strstr(err, "deadlock detected")) -#ifdef PSYCOPG_EXTENSIONS - exc = TransactionRollbackError; -#else - exc = OperationalError; -#endif - else - exc = ProgrammingError; + else { + /* Fallback if there is no exception code (reported happening e.g. + * when the connection is closed). */ + exc = DatabaseError; } /* try to remove the initial "ERROR: " part from the postgresql error */ err2 = strip_severity(err); + Dprintf("pq_raise: err2=%s", err2); - psyco_set_error(exc, pgc, err2, err, code); + psyco_set_error(exc, curs, err2, err, code); } /* pq_set_critical, pq_resolve_critical - manage critical errors @@ -252,7 +249,9 @@ } } -static PyObject * +/* return -1 if the exception is set (i.e. if conn->critical is set), + * else 0 */ +RAISES_NEG static int pq_resolve_critical(connectionObject *conn, int close) { Dprintf("pq_resolve_critical: resolving %s", conn->critical); @@ -267,11 +266,13 @@ /* we don't want to destroy this connection but just close it */ if (close == 1) conn_close(conn); - + /* remember to clear the critical! */ - pq_clear_critical(conn); + pq_clear_critical(conn); + + return -1; } - return NULL; + return 0; } /* pq_clear_async - clear the effects of a previous async query @@ -279,21 +280,45 @@ note that this function does block because it needs to wait for the full result sets of the previous query to clear them. - this function does not call any Py_*_ALLOW_THREADS macros */ -static void +void pq_clear_async(connectionObject *conn) { PGresult *pgres; - do { - pgres = PQgetResult(conn->pgconn); + /* this will get all pending results (if the submitted query consisted of + many parts, i.e. "select 1; select 2", there will be many) and also + finalize asynchronous processing so the connection will be ready to + accept another query */ + + while ((pgres = PQgetResult(conn->pgconn)) != NULL) { Dprintf("pq_clear_async: clearing PGresult at %p", pgres); - IFCLEARPGRES(pgres); - } while (pgres != NULL); + CLEARPGRES(pgres); + } + Py_CLEAR(conn->async_cursor); +} + + +/* pq_set_non_blocking - set the nonblocking status on a connection. + + Accepted arg values are 1 (nonblocking) and 0 (blocking). + + Return 0 if everything ok, else < 0 and set an exception. + */ +RAISES_NEG int +pq_set_non_blocking(connectionObject *conn, int arg) +{ + int ret = PQsetnonblocking(conn->pgconn, arg); + if (0 != ret) { + Dprintf("PQsetnonblocking(%d) FAILED", arg); + PyErr_SetString(OperationalError, "PQsetnonblocking() failed"); + ret = -1; + } + return ret; } + /* pg_execute_command_locked - execute a no-result query on a locked connection. This function should only be called on a locked connection without @@ -301,24 +326,38 @@ On error, -1 is returned, and the pgres argument will hold the relevant result structure. + + The tstate parameter should be the pointer of the _save variable created by + Py_BEGIN_ALLOW_THREADS: this enables the function to acquire and release + again the GIL if needed, i.e. if a Python wait callback must be invoked. */ int pq_execute_command_locked(connectionObject *conn, const char *query, - PGresult **pgres, char **error) + PGresult **pgres, char **error, + PyThreadState **tstate) { int pgstatus, retvalue = -1; Dprintf("pq_execute_command_locked: pgconn = %p, query = %s", conn->pgconn, query); *error = NULL; - *pgres = PQexec(conn->pgconn, query); - if (*pgres == NULL) { - const char *msg; + if (!psyco_green()) { + *pgres = PQexec(conn->pgconn, query); + } else { + PyEval_RestoreThread(*tstate); + *pgres = psyco_exec_green(conn, query); + *tstate = PyEval_SaveThread(); + } + if (*pgres == NULL) { Dprintf("pq_execute_command_locked: PQexec returned NULL"); - msg = PQerrorMessage(conn->pgconn); - if (msg) - *error = strdup(msg); + PyEval_RestoreThread(*tstate); + if (!PyErr_Occurred()) { + const char *msg; + msg = PQerrorMessage(conn->pgconn); + if (msg && *msg) { *error = strdup(msg); } + } + *tstate = PyEval_SaveThread(); goto cleanup; } @@ -331,8 +370,8 @@ retvalue = 0; IFCLEARPGRES(*pgres); - - cleanup: + +cleanup: return retvalue; } @@ -344,7 +383,7 @@ This function should be called while holding the global interpreter lock. */ -void +RAISES void pq_complete_error(connectionObject *conn, PGresult **pgres, char **error) { Dprintf("pq_complete_error: pgconn = %p, pgres = %p, error = %s", @@ -373,25 +412,20 @@ relevant result structure. */ int -pq_begin_locked(connectionObject *conn, PGresult **pgres, char **error) +pq_begin_locked(connectionObject *conn, PGresult **pgres, char **error, + PyThreadState **tstate) { - const char *query[] = { - NULL, - "BEGIN; SET TRANSACTION ISOLATION LEVEL READ COMMITTED", - "BEGIN; SET TRANSACTION ISOLATION LEVEL SERIALIZABLE"}; int result; - Dprintf("pq_begin_locked: pgconn = %p, isolevel = %ld, status = %d", - conn->pgconn, conn->isolation_level, conn->status); + Dprintf("pq_begin_locked: pgconn = %p, autocommit = %d, status = %d", + conn->pgconn, conn->autocommit, conn->status); - if (conn->isolation_level == 0 || conn->status != CONN_STATUS_READY) { + if (conn->autocommit || conn->status != CONN_STATUS_READY) { Dprintf("pq_begin_locked: transaction in progress"); return 0; } - pq_clear_async(conn); - result = pq_execute_command_locked(conn, query[conn->isolation_level], - pgres, error); + result = pq_execute_command_locked(conn, "BEGIN", pgres, error, tstate); if (result == 0) conn->status = CONN_STATUS_BEGIN; @@ -411,52 +445,54 @@ PGresult *pgres = NULL; char *error = NULL; - Dprintf("pq_commit: pgconn = %p, isolevel = %ld, status = %d", - conn->pgconn, conn->isolation_level, conn->status); + Py_BEGIN_ALLOW_THREADS; + pthread_mutex_lock(&conn->lock); + + Dprintf("pq_commit: pgconn = %p, autocommit = %d, status = %d", + conn->pgconn, conn->autocommit, conn->status); - if (conn->isolation_level == 0 || conn->status != CONN_STATUS_BEGIN) { + if (conn->autocommit || conn->status != CONN_STATUS_BEGIN) { Dprintf("pq_commit: no transaction to commit"); - return 0; + retvalue = 0; + } + else { + conn->mark += 1; + retvalue = pq_execute_command_locked(conn, "COMMIT", &pgres, &error, &_save); } - Py_BEGIN_ALLOW_THREADS; - pthread_mutex_lock(&conn->lock); - conn->mark += 1; + Py_BLOCK_THREADS; + conn_notice_process(conn); + Py_UNBLOCK_THREADS; - pq_clear_async(conn); - retvalue = pq_execute_command_locked(conn, "COMMIT", &pgres, &error); + /* Even if an error occurred, the connection will be rolled back, + so we unconditionally set the connection status here. */ + conn->status = CONN_STATUS_READY; pthread_mutex_unlock(&conn->lock); Py_END_ALLOW_THREADS; - conn_notice_process(conn); - if (retvalue < 0) pq_complete_error(conn, &pgres, &error); - /* Even if an error occurred, the connection will be rolled back, - so we unconditionally set the connection status here. */ - conn->status = CONN_STATUS_READY; - return retvalue; } -int -pq_abort_locked(connectionObject *conn, PGresult **pgres, char **error) +RAISES_NEG int +pq_abort_locked(connectionObject *conn, PGresult **pgres, char **error, + PyThreadState **tstate) { int retvalue = -1; - Dprintf("pq_abort_locked: pgconn = %p, isolevel = %ld, status = %d", - conn->pgconn, conn->isolation_level, conn->status); + Dprintf("pq_abort_locked: pgconn = %p, autocommit = %d, status = %d", + conn->pgconn, conn->autocommit, conn->status); - if (conn->isolation_level == 0 || conn->status != CONN_STATUS_BEGIN) { + if (conn->autocommit || conn->status != CONN_STATUS_BEGIN) { Dprintf("pq_abort_locked: no transaction to abort"); return 0; } conn->mark += 1; - pq_clear_async(conn); - retvalue = pq_execute_command_locked(conn, "ROLLBACK", pgres, error); + retvalue = pq_execute_command_locked(conn, "ROLLBACK", pgres, error, tstate); if (retvalue == 0) conn->status = CONN_STATUS_READY; @@ -468,31 +504,28 @@ This function should be called while holding the global interpreter lock. */ -int +RAISES_NEG int pq_abort(connectionObject *conn) { int retvalue = -1; PGresult *pgres = NULL; char *error = NULL; - Dprintf("pq_abort: pgconn = %p, isolevel = %ld, status = %d", - conn->pgconn, conn->isolation_level, conn->status); - - if (conn->isolation_level == 0 || conn->status != CONN_STATUS_BEGIN) { - Dprintf("pq_abort: no transaction to abort"); - return 0; - } + Dprintf("pq_abort: pgconn = %p, autocommit = %d, status = %d", + conn->pgconn, conn->autocommit, conn->status); Py_BEGIN_ALLOW_THREADS; pthread_mutex_lock(&conn->lock); - retvalue = pq_abort_locked(conn, &pgres, &error); + retvalue = pq_abort_locked(conn, &pgres, &error, &_save); + + Py_BLOCK_THREADS; + conn_notice_process(conn); + Py_UNBLOCK_THREADS; pthread_mutex_unlock(&conn->lock); Py_END_ALLOW_THREADS; - conn_notice_process(conn); - if (retvalue < 0) pq_complete_error(conn, &pgres, &error); @@ -508,29 +541,30 @@ connection without holding the global interpreter lock. */ -int -pq_reset_locked(connectionObject *conn, PGresult **pgres, char **error) +RAISES_NEG int +pq_reset_locked(connectionObject *conn, PGresult **pgres, char **error, + PyThreadState **tstate) { int retvalue = -1; - Dprintf("pq_reset_locked: pgconn = %p, isolevel = %ld, status = %d", - conn->pgconn, conn->isolation_level, conn->status); + Dprintf("pq_reset_locked: pgconn = %p, autocommit = %d, status = %d", + conn->pgconn, conn->autocommit, conn->status); conn->mark += 1; - pq_clear_async(conn); - if (conn->isolation_level > 0 && conn->status == CONN_STATUS_BEGIN) { - retvalue = pq_execute_command_locked(conn, "ABORT", pgres, error); + if (!conn->autocommit && conn->status == CONN_STATUS_BEGIN) { + retvalue = pq_execute_command_locked(conn, "ABORT", pgres, error, tstate); if (retvalue != 0) return retvalue; } - retvalue = pq_execute_command_locked(conn, "RESET ALL", pgres, error); + retvalue = pq_execute_command_locked(conn, "RESET ALL", pgres, error, tstate); if (retvalue != 0) return retvalue; retvalue = pq_execute_command_locked(conn, - "SET SESSION AUTHORIZATION DEFAULT", pgres, error); + "SET SESSION AUTHORIZATION DEFAULT", pgres, error, tstate); if (retvalue != 0) return retvalue; + /* should set the tpc xid to null: postponed until we get the GIL again */ conn->status = CONN_STATUS_READY; return retvalue; @@ -543,25 +577,171 @@ PGresult *pgres = NULL; char *error = NULL; - Dprintf("pq_reset: pgconn = %p, isolevel = %ld, status = %d", - conn->pgconn, conn->isolation_level, conn->status); + Dprintf("pq_reset: pgconn = %p, autocommit = %d, status = %d", + conn->pgconn, conn->autocommit, conn->status); Py_BEGIN_ALLOW_THREADS; pthread_mutex_lock(&conn->lock); - retvalue = pq_reset_locked(conn, &pgres, &error); + retvalue = pq_reset_locked(conn, &pgres, &error, &_save); + + Py_BLOCK_THREADS; + conn_notice_process(conn); + Py_UNBLOCK_THREADS; pthread_mutex_unlock(&conn->lock); Py_END_ALLOW_THREADS; - conn_notice_process(conn); - - if (retvalue < 0) + if (retvalue < 0) { pq_complete_error(conn, &pgres, &error); - + } + else { + Py_CLEAR(conn->tpc_xid); + } return retvalue; } + +/* Get a session parameter. + * + * The function should be called on a locked connection without + * holding the GIL. + * + * The result is a new string allocated with malloc. + */ + +char * +pq_get_guc_locked( + connectionObject *conn, const char *param, + PGresult **pgres, char **error, PyThreadState **tstate) +{ + char query[256]; + int size; + char *rv = NULL; + + Dprintf("pq_get_guc_locked: reading %s", param); + + size = PyOS_snprintf(query, sizeof(query), "SHOW %s", param); + if (size >= sizeof(query)) { + *error = strdup("SHOW: query too large"); + goto cleanup; + } + + Dprintf("pq_get_guc_locked: pgconn = %p, query = %s", conn->pgconn, query); + + *error = NULL; + if (!psyco_green()) { + *pgres = PQexec(conn->pgconn, query); + } else { + PyEval_RestoreThread(*tstate); + *pgres = psyco_exec_green(conn, query); + *tstate = PyEval_SaveThread(); + } + + if (*pgres == NULL) { + Dprintf("pq_get_guc_locked: PQexec returned NULL"); + PyEval_RestoreThread(*tstate); + if (!PyErr_Occurred()) { + const char *msg; + msg = PQerrorMessage(conn->pgconn); + if (msg && *msg) { *error = strdup(msg); } + } + *tstate = PyEval_SaveThread(); + goto cleanup; + } + if (PQresultStatus(*pgres) != PGRES_TUPLES_OK) { + Dprintf("pq_get_guc_locked: result was not TUPLES_OK (%d)", + PQresultStatus(*pgres)); + goto cleanup; + } + + rv = strdup(PQgetvalue(*pgres, 0, 0)); + CLEARPGRES(*pgres); + +cleanup: + return rv; +} + +/* Set a session parameter. + * + * The function should be called on a locked connection without + * holding the GIL + */ + +int +pq_set_guc_locked( + connectionObject *conn, const char *param, const char *value, + PGresult **pgres, char **error, PyThreadState **tstate) +{ + char query[256]; + int size; + int rv = -1; + + Dprintf("pq_set_guc_locked: setting %s to %s", param, value); + + if (0 == strcmp(value, "default")) { + size = PyOS_snprintf(query, sizeof(query), + "SET %s TO DEFAULT", param); + } + else { + size = PyOS_snprintf(query, sizeof(query), + "SET %s TO '%s'", param, value); + } + if (size >= sizeof(query)) { + *error = strdup("SET: query too large"); + } + + rv = pq_execute_command_locked(conn, query, pgres, error, tstate); + + return rv; +} + +/* Call one of the PostgreSQL tpc-related commands. + * + * This function should only be called on a locked connection without + * holding the global interpreter lock. */ + +int +pq_tpc_command_locked(connectionObject *conn, const char *cmd, const char *tid, + PGresult **pgres, char **error, PyThreadState **tstate) +{ + int rv = -1; + char *etid = NULL, *buf = NULL; + Py_ssize_t buflen; + + Dprintf("_pq_tpc_command: pgconn = %p, command = %s", + conn->pgconn, cmd); + + conn->mark += 1; + + PyEval_RestoreThread(*tstate); + + /* convert the xid into the postgres transaction_id and quote it. */ + if (!(etid = psycopg_escape_string((PyObject *)conn, tid, 0, NULL, NULL))) + { goto exit; } + + /* prepare the command to the server */ + buflen = 2 + strlen(cmd) + strlen(etid); /* add space, zero */ + if (!(buf = PyMem_Malloc(buflen))) { + PyErr_NoMemory(); + goto exit; + } + if (0 > PyOS_snprintf(buf, buflen, "%s %s", cmd, etid)) { goto exit; } + + /* run the command and let it handle the error cases */ + *tstate = PyEval_SaveThread(); + rv = pq_execute_command_locked(conn, buf, pgres, error, tstate); + PyEval_RestoreThread(*tstate); + +exit: + PyMem_Free(buf); + PyMem_Free(etid); + + *tstate = PyEval_SaveThread(); + return rv; +} + + /* pq_is_busy - consume input and return connection status a status of 1 means that a call to pq_fetch will block, while a status of 0 @@ -575,8 +755,6 @@ pq_is_busy(connectionObject *conn) { int res; - PGnotify *pgn; - Dprintf("pq_is_busy: consuming input"); Py_BEGIN_ALLOW_THREADS; @@ -590,49 +768,82 @@ return -1; } + res = PQisBusy(conn->pgconn); + + Py_BLOCK_THREADS; + conn_notifies_process(conn); + conn_notice_process(conn); + Py_UNBLOCK_THREADS; - /* now check for notifies */ - while ((pgn = PQnotifies(conn->pgconn)) != NULL) { - PyObject *notify; + pthread_mutex_unlock(&(conn->lock)); + Py_END_ALLOW_THREADS; - Dprintf("curs_is_busy: got NOTIFY from pid %d, msg = %s", - (int) pgn->be_pid, pgn->relname); + return res; +} - Py_BLOCK_THREADS; - notify = PyTuple_New(2); - PyTuple_SET_ITEM(notify, 0, PyInt_FromLong((long)pgn->be_pid)); - PyTuple_SET_ITEM(notify, 1, PyString_FromString(pgn->relname)); - PyList_Append(conn->notifies, notify); - Py_UNBLOCK_THREADS; - free(pgn); +/* pq_is_busy_locked - equivalent to pq_is_busy but we already have the lock + * + * The function should be called with the lock and holding the GIL. + */ + +int +pq_is_busy_locked(connectionObject *conn) +{ + Dprintf("pq_is_busy_locked: consuming input"); + + if (PQconsumeInput(conn->pgconn) == 0) { + Dprintf("pq_is_busy_locked: PQconsumeInput() failed"); + PyErr_SetString(OperationalError, PQerrorMessage(conn->pgconn)); + return -1; } - res = PQisBusy(conn->pgconn); - + /* notices and notifies will be processed at the end of the loop we are in + * (async reading) by pq_fetch. */ + + return PQisBusy(conn->pgconn); +} + +/* pq_flush - flush output and return connection status + + a status of 1 means that a some data is still pending to be flushed, while a + status of 0 means that there is no data waiting to be sent. -1 means an + error and an exception will be set accordingly. + + this function locks the connection object + this function call Py_*_ALLOW_THREADS macros */ + +int +pq_flush(connectionObject *conn) +{ + int res; + + Dprintf("pq_flush: flushing output"); + + Py_BEGIN_ALLOW_THREADS; + pthread_mutex_lock(&(conn->lock)); + res = PQflush(conn->pgconn); pthread_mutex_unlock(&(conn->lock)); Py_END_ALLOW_THREADS; - - conn_notice_process(conn); return res; } -/* pq_execute - execute a query, possibly asyncronously +/* pq_execute - execute a query, possibly asynchronously this fucntion locks the connection object this function call Py_*_ALLOW_THREADS macros */ -int +RAISES_NEG int pq_execute(cursorObject *curs, const char *query, int async) { PGresult *pgres = NULL; char *error = NULL; + int async_status = ASYNC_WRITE; /* if the status of the connection is critical raise an exception and definitely close the connection */ if (curs->conn->critical) { - pq_resolve_critical(curs->conn, 1); - return -1; + return pq_resolve_critical(curs->conn, 1); } /* check status of connection, raise error if not OK */ @@ -646,7 +857,7 @@ Py_BEGIN_ALLOW_THREADS; pthread_mutex_lock(&(curs->conn->lock)); - if (pq_begin_locked(curs->conn, &pgres, &error) < 0) { + if (pq_begin_locked(curs->conn, &pgres, &error, &_save) < 0) { pthread_mutex_unlock(&(curs->conn->lock)); Py_BLOCK_THREADS; pq_complete_error(curs->conn, &pgres, &error); @@ -655,30 +866,44 @@ if (async == 0) { IFCLEARPGRES(curs->pgres); - Dprintf("pq_execute: executing SYNC query:"); + Dprintf("pq_execute: executing SYNC query: pgconn = %p", curs->conn->pgconn); Dprintf(" %-.200s", query); - curs->pgres = PQexec(curs->conn->pgconn, query); + if (!psyco_green()) { + curs->pgres = PQexec(curs->conn->pgconn, query); + } + else { + Py_BLOCK_THREADS; + curs->pgres = psyco_exec_green(curs->conn, query); + Py_UNBLOCK_THREADS; + } /* dont let pgres = NULL go to pq_fetch() */ if (curs->pgres == NULL) { pthread_mutex_unlock(&(curs->conn->lock)); Py_BLOCK_THREADS; - PyErr_SetString(OperationalError, - PQerrorMessage(curs->conn->pgconn)); + if (!PyErr_Occurred()) { + PyErr_SetString(OperationalError, + PQerrorMessage(curs->conn->pgconn)); + } return -1; } + + /* Process notifies here instead of when fetching the tuple as we are + * into the same critical section that received the data. Without this + * care, reading notifies may disrupt other thread communications. + * (as in ticket #55). */ + Py_BLOCK_THREADS; + conn_notifies_process(curs->conn); + conn_notice_process(curs->conn); + Py_UNBLOCK_THREADS; } else if (async == 1) { - /* first of all, let see if the previous query has already ended, if - not what should we do? just block and discard data or execute - another query? */ - pq_clear_async(curs->conn); + int ret; - Dprintf("pq_execute: executing ASYNC query:"); + Dprintf("pq_execute: executing ASYNC query: pgconn = %p", curs->conn->pgconn); Dprintf(" %-.200s", query); - /* then we can go on and send a new query without fear */ IFCLEARPGRES(curs->pgres); if (PQsendQuery(curs->conn->pgconn, query) == 0) { pthread_mutex_unlock(&(curs->conn->lock)); @@ -688,26 +913,94 @@ return -1; } Dprintf("pq_execute: async query sent to backend"); + + ret = PQflush(curs->conn->pgconn); + if (ret == 0) { + /* the query got fully sent to the server */ + Dprintf("pq_execute: query got flushed immediately"); + /* the async status will be ASYNC_READ */ + async_status = ASYNC_READ; + } + else if (ret == 1) { + /* not all of the query got sent to the server */ + async_status = ASYNC_WRITE; + } + else { + /* there was an error */ + return -1; + } } pthread_mutex_unlock(&(curs->conn->lock)); Py_END_ALLOW_THREADS; - conn_notice_process(curs->conn); - /* if the execute was sync, we call pq_fetch() immediately, to respect the old DBAPI-2.0 compatible behaviour */ if (async == 0) { Dprintf("pq_execute: entering syncronous DBAPI compatibility mode"); - if (pq_fetch(curs) == -1) return -1; + if (pq_fetch(curs) < 0) return -1; } else { - curs->conn->async_cursor = (PyObject*)curs; + PyObject *tmp; + curs->conn->async_status = async_status; + curs->conn->async_cursor = tmp = PyWeakref_NewRef((PyObject *)curs, NULL); + if (!tmp) { + /* weakref creation failed */ + return -1; + } } return 1-async; } +/* send an async query to the backend. + * + * Return 1 if command succeeded, else 0. + * + * The function should be called helding the connection lock and the GIL. + */ +int +pq_send_query(connectionObject *conn, const char *query) +{ + int rv; + + Dprintf("pq_send_query: sending ASYNC query:"); + Dprintf(" %-.200s", query); + + if (0 == (rv = PQsendQuery(conn->pgconn, query))) { + Dprintf("pq_send_query: error: %s", PQerrorMessage(conn->pgconn)); + } + + return rv; +} + +/* Return the last result available on the connection. + * + * The function will block will block only if a command is active and the + * necessary response data has not yet been read by PQconsumeInput. + * + * The result should be disposed using PQclear() + */ +PGresult * +pq_get_last_result(connectionObject *conn) +{ + PGresult *result = NULL, *res; + + /* Read until PQgetResult gives a NULL */ + while (NULL != (res = PQgetResult(conn->pgconn))) { + if (result) { + /* TODO too bad: we are discarding results from all the queries + * except the last. We could have populated `nextset()` with it + * but it would be an incompatible change (apps currently issue + * groups of queries expecting to receive the last result: they + * would start receiving the first instead). */ + PQclear(result); + } + result = res; + } + + return result; +} /* pq_fetch - fetch data after a query @@ -720,15 +1013,19 @@ 1 - result from backend (possibly data is ready) */ -static void +RAISES_NEG static int _pq_fetch_tuples(cursorObject *curs) { int i, *dsize = NULL; int pgnfields; int pgbintuples; + int rv = -1; + PyObject *description = NULL; + PyObject *casts = NULL; Py_BEGIN_ALLOW_THREADS; pthread_mutex_lock(&(curs->conn->lock)); + Py_END_ALLOW_THREADS; pgnfields = PQnfields(curs->pgres); pgbintuples = PQbinaryTuples(curs->pgres); @@ -736,20 +1033,20 @@ curs->notuples = 0; /* create the tuple for description and typecasting */ - Py_BLOCK_THREADS; - Py_XDECREF(curs->description); - Py_XDECREF(curs->casts); - curs->description = PyTuple_New(pgnfields); - curs->casts = PyTuple_New(pgnfields); + Py_CLEAR(curs->description); + Py_CLEAR(curs->casts); + if (!(description = PyTuple_New(pgnfields))) { goto exit; } + if (!(casts = PyTuple_New(pgnfields))) { goto exit; } curs->columns = pgnfields; - Py_UNBLOCK_THREADS; /* calculate the display size for each column (cpu intensive, can be switched off at configuration time) */ #ifdef PSYCOPG_DISPLAY_SIZE - Py_BLOCK_THREADS; - dsize = (int *)PyMem_Malloc(pgnfields * sizeof(int)); - Py_UNBLOCK_THREADS; + if (!(dsize = PyMem_New(int, pgnfields))) { + PyErr_NoMemory(); + goto exit; + } + Py_BEGIN_ALLOW_THREADS; if (dsize != NULL) { int j, len; for (i=0; i < pgnfields; i++) { @@ -762,6 +1059,7 @@ } } } + Py_END_ALLOW_THREADS; #endif /* calculate various parameters and typecasters */ @@ -770,14 +1068,11 @@ int fsize = PQfsize(curs->pgres, i); int fmod = PQfmod(curs->pgres, i); - PyObject *dtitem; - PyObject *type; + PyObject *dtitem = NULL; + PyObject *type = NULL; PyObject *cast = NULL; - Py_BLOCK_THREADS; - - dtitem = PyTuple_New(7); - PyTuple_SET_ITEM(curs->description, i, dtitem); + if (!(dtitem = PyTuple_New(7))) { goto exit; } /* fill the right cast function by accessing three different dictionaries: - the per-cursor dictionary, if available (can be NULL or None) @@ -785,21 +1080,11 @@ - the global dictionary (at module level) if we get no defined cast use the default one */ - type = PyInt_FromLong(ftype); - Dprintf("_pq_fetch_tuples: looking for cast %d:", ftype); - if (curs->string_types != NULL && curs->string_types != Py_None) { - cast = PyDict_GetItem(curs->string_types, type); - Dprintf("_pq_fetch_tuples: per-cursor dict: %p", cast); - } - if (cast == NULL) { - cast = PyDict_GetItem(curs->conn->string_types, type); - Dprintf("_pq_fetch_tuples: per-connection dict: %p", cast); - } - if (cast == NULL) { - cast = PyDict_GetItem(psyco_types, type); - Dprintf("_pq_fetch_tuples: global dict: %p", cast); + if (!(type = PyInt_FromLong(ftype))) { + goto err_for; } - if (cast == NULL) cast = psyco_default_cast; + Dprintf("_pq_fetch_tuples: looking for cast %d:", ftype); + cast = curs_get_cast(curs, type); /* else if we got binary tuples and if we got a field that is binary use the default cast @@ -813,19 +1098,28 @@ } Dprintf("_pq_fetch_tuples: using cast at %p (%s) for type %d", - cast, PyString_AS_STRING(((typecastObject*)cast)->name), + cast, Bytes_AS_STRING(((typecastObject*)cast)->name), PQftype(curs->pgres,i)); Py_INCREF(cast); - PyTuple_SET_ITEM(curs->casts, i, cast); + PyTuple_SET_ITEM(casts, i, cast); /* 1/ fill the other fields */ - PyTuple_SET_ITEM(dtitem, 0, - PyString_FromString(PQfname(curs->pgres, i))); + { + PyObject *tmp; + if (!(tmp = conn_text_from_chars( + curs->conn, PQfname(curs->pgres, i)))) { + goto err_for; + } + PyTuple_SET_ITEM(dtitem, 0, tmp); + } PyTuple_SET_ITEM(dtitem, 1, type); + type = NULL; /* 2/ display size is the maximum size of this field result tuples. */ if (dsize && dsize[i] >= 0) { - PyTuple_SET_ITEM(dtitem, 2, PyInt_FromLong(dsize[i])); + PyObject *tmp; + if (!(tmp = PyInt_FromLong(dsize[i]))) { goto err_for; } + PyTuple_SET_ITEM(dtitem, 2, tmp); } else { Py_INCREF(Py_None); @@ -836,21 +1130,35 @@ if (fmod > 0) fmod = fmod - sizeof(int); if (fsize == -1) { if (ftype == NUMERICOID) { - PyTuple_SET_ITEM(dtitem, 3, - PyInt_FromLong((fmod >> 16) & 0xFFFF)); + PyObject *tmp; + if (!(tmp = PyInt_FromLong((fmod >> 16)))) { goto err_for; } + PyTuple_SET_ITEM(dtitem, 3, tmp); } else { /* If variable length record, return maximum size */ - PyTuple_SET_ITEM(dtitem, 3, PyInt_FromLong(fmod)); + PyObject *tmp; + if (!(tmp = PyInt_FromLong(fmod))) { goto err_for; } + PyTuple_SET_ITEM(dtitem, 3, tmp); } } else { - PyTuple_SET_ITEM(dtitem, 3, PyInt_FromLong(fsize)); + PyObject *tmp; + if (!(tmp = PyInt_FromLong(fsize))) { goto err_for; } + PyTuple_SET_ITEM(dtitem, 3, tmp); } /* 4,5/ scale and precision */ if (ftype == NUMERICOID) { - PyTuple_SET_ITEM(dtitem, 4, PyInt_FromLong((fmod >> 16) & 0xFFFF)); - PyTuple_SET_ITEM(dtitem, 5, PyInt_FromLong(fmod & 0xFFFF)); + PyObject *tmp; + + if (!(tmp = PyInt_FromLong((fmod >> 16) & 0xFFFF))) { + goto err_for; + } + PyTuple_SET_ITEM(dtitem, 4, tmp); + + if (!(tmp = PyInt_FromLong(fmod & 0xFFFF))) { + PyTuple_SET_ITEM(dtitem, 5, tmp); + } + PyTuple_SET_ITEM(dtitem, 5, tmp); } else { Py_INCREF(Py_None); @@ -862,46 +1170,107 @@ /* 6/ FIXME: null_ok??? */ Py_INCREF(Py_None); PyTuple_SET_ITEM(dtitem, 6, Py_None); - - Py_UNBLOCK_THREADS; + + /* Convert into a namedtuple if available */ + if (Py_None != psyco_DescriptionType) { + PyObject *tmp = dtitem; + dtitem = PyObject_CallObject(psyco_DescriptionType, tmp); + Py_DECREF(tmp); + if (NULL == dtitem) { goto err_for; } + } + + PyTuple_SET_ITEM(description, i, dtitem); + dtitem = NULL; + + continue; + +err_for: + Py_XDECREF(type); + Py_XDECREF(dtitem); + goto exit; } - if (dsize) { - Py_BLOCK_THREADS; - PyMem_Free(dsize); - Py_UNBLOCK_THREADS; - } - + curs->description = description; description = NULL; + curs->casts = casts; casts = NULL; + rv = 0; + +exit: + PyMem_Free(dsize); + Py_XDECREF(description); + Py_XDECREF(casts); + + Py_BEGIN_ALLOW_THREADS; pthread_mutex_unlock(&(curs->conn->lock)); Py_END_ALLOW_THREADS; + + return rv; } -#ifdef HAVE_PQPROTOCOL3 static int _pq_copy_in_v3(cursorObject *curs) { /* COPY FROM implementation when protocol 3 is available: this function uses the new PQputCopyData() and can detect errors and set the correct exception */ - PyObject *o; + PyObject *o, *func = NULL, *size = NULL; Py_ssize_t length = 0; int res, error = 0; + if (!(func = PyObject_GetAttrString(curs->copyfile, "read"))) { + Dprintf("_pq_copy_in_v3: can't get o.read"); + error = 1; + goto exit; + } + if (!(size = PyInt_FromSsize_t(curs->copysize))) { + Dprintf("_pq_copy_in_v3: can't get int from copysize"); + error = 1; + goto exit; + } + while (1) { - o = PyObject_CallMethod(curs->copyfile, "read", - CONV_CODE_PY_SSIZE_T, curs->copysize); - if (!o || !PyString_Check(o) || (length = PyString_Size(o)) == -1) { + if (!(o = PyObject_CallFunctionObjArgs(func, size, NULL))) { + Dprintf("_pq_copy_in_v3: read() failed"); + error = 1; + break; + } + + /* a file may return unicode if implements io.TextIOBase */ + if (PyUnicode_Check(o)) { + PyObject *tmp; + Dprintf("_pq_copy_in_v3: encoding in %s", curs->conn->codec); + if (!(tmp = PyUnicode_AsEncodedString(o, curs->conn->codec, NULL))) { + Dprintf("_pq_copy_in_v3: encoding() failed"); + error = 1; + break; + } + Py_DECREF(o); + o = tmp; + } + + if (!Bytes_Check(o)) { + Dprintf("_pq_copy_in_v3: got %s instead of bytes", + Py_TYPE(o)->tp_name); + error = 1; + break; + } + + if (0 == (length = Bytes_GET_SIZE(o))) { + break; + } + if (length > INT_MAX) { + Dprintf("_pq_copy_in_v3: bad length: " FORMAT_CODE_PY_SSIZE_T, + length); error = 1; + break; } - if (length == 0 || length > INT_MAX || error == 1) break; Py_BEGIN_ALLOW_THREADS; - res = PQputCopyData(curs->conn->pgconn, PyString_AS_STRING(o), + res = PQputCopyData(curs->conn->pgconn, Bytes_AS_STRING(o), /* Py_ssize_t->int cast was validated above */ (int) length); - Dprintf("_pq_copy_in_v3: sent %d bytes of data; res = %d", - (int) length, res); - + Dprintf("_pq_copy_in_v3: sent " FORMAT_CODE_PY_SSIZE_T " bytes of data; res = %d", + length, res); + if (res == 0) { /* FIXME: in theory this should not happen but adding a check here would be a nice idea */ @@ -929,6 +1298,7 @@ else if (error == 2) res = PQputCopyEnd(curs->conn->pgconn, "error in PQputCopyData() call"); else + /* XXX would be nice to propagate the exeption */ res = PQputCopyEnd(curs->conn->pgconn, "error in .read() call"); IFCLEARPGRES(curs->pgres); @@ -952,66 +1322,56 @@ IFCLEARPGRES(curs->pgres); } } - - return error == 0 ? 1 : -1; -} -#endif -static int -_pq_copy_in(cursorObject *curs) -{ - /* COPY FROM implementation when protocol 3 is not available: this - function can't fail but the backend will send an ERROR notice that will - be catched by our notice collector */ - PyObject *o; - - while (1) { - o = PyObject_CallMethod(curs->copyfile, "readline", NULL); - if (o == NULL) return -1; - if (o == Py_None || PyString_GET_SIZE(o) == 0) break; - if (PQputline(curs->conn->pgconn, PyString_AS_STRING(o)) != 0) { - Py_DECREF(o); - return -1; - } - Py_DECREF(o); - } - Py_XDECREF(o); - PQputline(curs->conn->pgconn, "\\.\n"); - PQendcopy(curs->conn->pgconn); - - /* if for some reason we're using a protocol 3 libpq to connect to a - protocol 2 backend we still need to cycle on the result set */ - IFCLEARPGRES(curs->pgres); - while ((curs->pgres = PQgetResult(curs->conn->pgconn)) != NULL) { - if (PQresultStatus(curs->pgres) == PGRES_FATAL_ERROR) - pq_raise(curs->conn, curs, NULL); - IFCLEARPGRES(curs->pgres); - } - return 1; +exit: + Py_XDECREF(func); + Py_XDECREF(size); + return (error == 0 ? 1 : -1); } -#ifdef HAVE_PQPROTOCOL3 static int _pq_copy_out_v3(cursorObject *curs) { - PyObject *tmp = NULL; + PyObject *tmp = NULL, *func; + PyObject *obj = NULL; + int ret = -1; + int is_text; char *buffer; Py_ssize_t len; + if (!(func = PyObject_GetAttrString(curs->copyfile, "write"))) { + Dprintf("_pq_copy_out_v3: can't get o.write"); + goto exit; + } + + /* if the file is text we must pass it unicode. */ + if (-1 == (is_text = psycopg_is_text_file(curs->copyfile))) { + goto exit; + } + while (1) { Py_BEGIN_ALLOW_THREADS; len = PQgetCopyData(curs->conn->pgconn, &buffer, 0); Py_END_ALLOW_THREADS; if (len > 0 && buffer) { - tmp = PyObject_CallMethod(curs->copyfile, - "write", "s#", buffer, len); + if (is_text) { + obj = PyUnicode_Decode(buffer, len, curs->conn->codec, NULL); + } else { + obj = Bytes_FromStringAndSize(buffer, len); + } + PQfreemem(buffer); - if (tmp == NULL) - return -1; - else + if (!obj) { goto exit; } + tmp = PyObject_CallFunctionObjArgs(func, obj, NULL); + Py_DECREF(obj); + + if (tmp == NULL) { + goto exit; + } else { Py_DECREF(tmp); + } } /* we break on len == 0 but note that that should *not* happen, because we are not doing an async call (if it happens blame @@ -1021,7 +1381,7 @@ if (len == -2) { pq_raise(curs->conn, curs, NULL); - return -1; + goto exit; } /* and finally we grab the operation result from the backend */ @@ -1031,59 +1391,11 @@ pq_raise(curs->conn, curs, NULL); IFCLEARPGRES(curs->pgres); } - return 1; -} -#endif - -static int -_pq_copy_out(cursorObject *curs) -{ - PyObject *tmp = NULL; - - char buffer[4096]; - int status, ll=0; - Py_ssize_t len; - - while (1) { - Py_BEGIN_ALLOW_THREADS; - status = PQgetline(curs->conn->pgconn, buffer, 4096); - Py_END_ALLOW_THREADS; - if (status == 0) { - if (!ll && buffer[0] == '\\' && buffer[1] == '.') break; - - len = (Py_ssize_t) strlen(buffer); - buffer[len++] = '\n'; - ll = 0; - } - else if (status == 1) { - len = 4096-1; - ll = 1; - } - else { - return -1; - } - - tmp = PyObject_CallMethod(curs->copyfile, "write", "s#", buffer, len); - if (tmp == NULL) - return -1; - else - Py_DECREF(tmp); - } - - status = 1; - if (PQendcopy(curs->conn->pgconn) != 0) - status = -1; - - /* if for some reason we're using a protocol 3 libpq to connect to a - protocol 2 backend we still need to cycle on the result set */ - IFCLEARPGRES(curs->pgres); - while ((curs->pgres = PQgetResult(curs->conn->pgconn)) != NULL) { - if (PQresultStatus(curs->pgres) == PGRES_FATAL_ERROR) - pq_raise(curs->conn, curs, NULL); - IFCLEARPGRES(curs->pgres); - } + ret = 1; - return status; +exit: + Py_XDECREF(func); + return ret; } int @@ -1095,58 +1407,13 @@ /* even if we fail, we remove any information about the previous query */ curs_reset(curs); - /* we check the result from the previous execute; if the result is not - already there, we need to consume some input and go to sleep until we - get something edible to eat */ - if (!curs->pgres) { - - Dprintf("pq_fetch: no data: entering polling loop"); - - while (pq_is_busy(curs->conn) > 0) { - fd_set rfds; - struct timeval tv; - int sval, sock; - - Py_BEGIN_ALLOW_THREADS; - pthread_mutex_lock(&(curs->conn->lock)); - - sock = PQsocket(curs->conn->pgconn); - FD_ZERO(&rfds); - FD_SET(sock, &rfds); - - /* set a default timeout of 5 seconds - TODO: make use of the timeout, maybe allowing the user to - make a non-blocking (timeouted) call to fetchXXX */ - tv.tv_sec = 5; - tv.tv_usec = 0; - - Dprintf("pq_fetch: entering PDflush() loop"); - while (PQflush(curs->conn->pgconn) != 0); - sval = select(sock+1, &rfds, NULL, NULL, &tv); - - pthread_mutex_unlock(&(curs->conn->lock)); - Py_END_ALLOW_THREADS; - } - - Py_BEGIN_ALLOW_THREADS; - pthread_mutex_lock(&(curs->conn->lock)); - - Dprintf("pq_fetch: data is probably ready"); - IFCLEARPGRES(curs->pgres); - curs->pgres = PQgetResult(curs->conn->pgconn); - - pthread_mutex_unlock(&(curs->conn->lock)); - Py_END_ALLOW_THREADS; - } - /* check for PGRES_FATAL_ERROR result */ /* FIXME: I am not sure we need to check for critical error here. if (curs->pgres == NULL) { Dprintf("pq_fetch: got a NULL pgres, checking for critical"); pq_set_critical(curs->conn); if (curs->conn->critical) { - pq_resolve_critical(curs->conn); - return -1; + return pq_resolve_critical(curs->conn); } else { return 0; @@ -1161,7 +1428,7 @@ /* backend status message */ Py_XDECREF(curs->pgstatus); - curs->pgstatus = PyString_FromString(PQcmdStatus(curs->pgres)); + curs->pgstatus = conn_text_from_chars(curs->conn, PQcmdStatus(curs->pgres)); switch(pgstatus) { @@ -1179,12 +1446,7 @@ case PGRES_COPY_OUT: Dprintf("pq_fetch: data from a COPY TO (no tuples)"); -#ifdef HAVE_PQPROTOCOL3 - if (curs->conn->protocol == 3) - ex = _pq_copy_out_v3(curs); - else -#endif - ex = _pq_copy_out(curs); + ex = _pq_copy_out_v3(curs); curs->rowcount = -1; /* error caught by out glorious notice handler */ if (PyErr_Occurred()) ex = -1; @@ -1193,12 +1455,7 @@ case PGRES_COPY_IN: Dprintf("pq_fetch: data from a COPY FROM (no tuples)"); -#ifdef HAVE_PQPROTOCOL3 - if (curs->conn->protocol == 3) - ex = _pq_copy_in_v3(curs); - else -#endif - ex = _pq_copy_in(curs); + ex = _pq_copy_in_v3(curs); curs->rowcount = -1; /* error caught by out glorious notice handler */ if (PyErr_Occurred()) ex = -1; @@ -1208,33 +1465,31 @@ case PGRES_TUPLES_OK: Dprintf("pq_fetch: data from a SELECT (got tuples)"); curs->rowcount = PQntuples(curs->pgres); - _pq_fetch_tuples(curs); ex = 0; + if (0 == _pq_fetch_tuples(curs)) { ex = 0; } /* don't clear curs->pgres, because it contains the results! */ break; + case PGRES_EMPTY_QUERY: + PyErr_SetString(ProgrammingError, + "can't execute an empty query"); + IFCLEARPGRES(curs->pgres); + ex = -1; + break; + default: - Dprintf("pq_fetch: uh-oh, something FAILED"); + Dprintf("pq_fetch: uh-oh, something FAILED: pgconn = %p", curs->conn); pq_raise(curs->conn, curs, NULL); IFCLEARPGRES(curs->pgres); ex = -1; break; } - Dprintf("pq_fetch: fetching done; check for critical errors"); - - conn_notice_process(curs->conn); - /* error checking, close the connection if necessary (some critical errors are not really critical, like a COPY FROM error: if that's the case we raise the exception but we avoid to close the connection) */ + Dprintf("pq_fetch: fetching done; check for critical errors"); if (curs->conn->critical) { - if (ex == -1) { - pq_resolve_critical(curs->conn, 1); - } - else { - pq_resolve_critical(curs->conn, 0); - } - return -1; + return pq_resolve_critical(curs->conn, ex == -1 ? 1 : 0); } return ex; diff -Nru psycopg2-2.0.13/psycopg/pqpath.h psycopg2-2.4.5/psycopg/pqpath.h --- psycopg2-2.0.13/psycopg/pqpath.h 2009-10-04 21:32:35.000000000 +0000 +++ psycopg2-2.4.5/psycopg/pqpath.h 2012-03-28 21:09:15.000000000 +0000 @@ -1,28 +1,31 @@ /* pqpath.h - definitions for pqpath.c * - * Copyright (C) 2003 Federico Di Gregorio + * Copyright (C) 2003-2010 Federico Di Gregorio * * This file is part of psycopg. * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2, - * or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * psycopg2 is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * In addition, as a special exception, the copyright holders give + * permission to link this program with the OpenSSL library (or with + * modified versions of OpenSSL that use the same license as OpenSSL), + * and distribute linked combinations including the two. + * + * You must obey the GNU Lesser General Public License in all respects for + * all of the code used other than OpenSSL. + * + * psycopg2 is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. */ #ifndef PSYCOPG_PQPATH_H #define PSYCOPG_PQPATH_H 1 -#include "psycopg/config.h" #include "psycopg/cursor.h" #include "psycopg/connection.h" @@ -31,23 +34,42 @@ #define CLEARPGRES(pgres) PQclear(pgres); pgres = NULL /* exported functions */ -HIDDEN int pq_fetch(cursorObject *curs); -HIDDEN int pq_execute(cursorObject *curs, const char *query, int async); +HIDDEN PGresult *pq_get_last_result(connectionObject *conn); +RAISES_NEG HIDDEN int pq_fetch(cursorObject *curs); +RAISES_NEG HIDDEN int pq_execute(cursorObject *curs, const char *query, int async); +HIDDEN int pq_send_query(connectionObject *conn, const char *query); HIDDEN int pq_begin_locked(connectionObject *conn, PGresult **pgres, - char **error); + char **error, PyThreadState **tstate); HIDDEN int pq_commit(connectionObject *conn); -HIDDEN int pq_abort_locked(connectionObject *conn, PGresult **pgres, - char **error); -HIDDEN int pq_abort(connectionObject *conn); -HIDDEN int pq_reset(connectionObject *conn); +RAISES_NEG HIDDEN int pq_abort_locked(connectionObject *conn, PGresult **pgres, + char **error, PyThreadState **tstate); +RAISES_NEG HIDDEN int pq_abort(connectionObject *conn); +HIDDEN int pq_reset_locked(connectionObject *conn, PGresult **pgres, + char **error, PyThreadState **tstate); +RAISES_NEG HIDDEN int pq_reset(connectionObject *conn); +HIDDEN char *pq_get_guc_locked(connectionObject *conn, const char *param, + PGresult **pgres, + char **error, PyThreadState **tstate); +HIDDEN int pq_set_guc_locked(connectionObject *conn, const char *param, + const char *value, PGresult **pgres, + char **error, PyThreadState **tstate); +HIDDEN int pq_tpc_command_locked(connectionObject *conn, + const char *cmd, const char *tid, + PGresult **pgres, char **error, + PyThreadState **tstate); HIDDEN int pq_is_busy(connectionObject *conn); +HIDDEN int pq_is_busy_locked(connectionObject *conn); +HIDDEN int pq_flush(connectionObject *conn); +HIDDEN void pq_clear_async(connectionObject *conn); +RAISES_NEG HIDDEN int pq_set_non_blocking(connectionObject *conn, int arg); HIDDEN void pq_set_critical(connectionObject *conn, const char *msg); HIDDEN int pq_execute_command_locked(connectionObject *conn, const char *query, - PGresult **pgres, char **error); -HIDDEN void pq_complete_error(connectionObject *conn, PGresult **pgres, + PGresult **pgres, char **error, + PyThreadState **tstate); +RAISES HIDDEN void pq_complete_error(connectionObject *conn, PGresult **pgres, char **error); #endif /* !defined(PSYCOPG_PQPATH_H) */ diff -Nru psycopg2-2.0.13/psycopg/psycopg.h psycopg2-2.4.5/psycopg/psycopg.h --- psycopg2-2.0.13/psycopg/psycopg.h 2009-05-09 13:01:04.000000000 +0000 +++ psycopg2-2.4.5/psycopg/psycopg.h 2012-03-28 21:09:15.000000000 +0000 @@ -1,22 +1,26 @@ /* psycopg.h - definitions for the psycopg python module * - * Copyright (C) 2003 Federico Di Gregorio + * Copyright (C) 2003-2010 Federico Di Gregorio * * This file is part of psycopg. * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2, - * or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * psycopg2 is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * In addition, as a special exception, the copyright holders give + * permission to link this program with the OpenSSL library (or with + * modified versions of OpenSSL that use the same license as OpenSSL), + * and distribute linked combinations including the two. + * + * You must obey the GNU Lesser General Public License in all respects for + * all of the code used other than OpenSSL. + * + * psycopg2 is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. */ #ifndef PSYCOPG_H @@ -101,6 +105,9 @@ /* postgresql<->python encoding map */ extern HIDDEN PyObject *psycoEncodings; +/* SQL NULL */ +extern HIDDEN PyObject *psyco_null; + typedef struct { char *pgenc; char *pyenc; @@ -109,12 +116,22 @@ /* the Decimal type, used by the DECIMAL typecaster */ HIDDEN PyObject *psyco_GetDecimalType(void); +/* forward declaration */ +typedef struct cursorObject cursorObject; + /* some utility functions */ -HIDDEN void psyco_set_error(PyObject *exc, PyObject *curs, const char *msg, +RAISES HIDDEN void psyco_set_error(PyObject *exc, cursorObject *curs, const char *msg, const char *pgerror, const char *pgcode); HIDDEN char *psycopg_escape_string(PyObject *conn, const char *from, Py_ssize_t len, char *to, Py_ssize_t *tolen); +HIDDEN char *psycopg_escape_identifier_easy(const char *from, Py_ssize_t len); +HIDDEN int psycopg_strdup(char **to, const char *from, Py_ssize_t len); +HIDDEN int psycopg_is_text_file(PyObject *f); + +STEALS(1) HIDDEN PyObject * psycopg_ensure_bytes(PyObject *obj); + +STEALS(1) HIDDEN PyObject * psycopg_ensure_text(PyObject *obj); /* Exceptions docstrings */ #define Error_doc \ @@ -149,7 +166,7 @@ #ifdef PSYCOPG_EXTENSIONS #define QueryCanceledError_doc \ -"Error related to SQL query cancelation." +"Error related to SQL query cancellation." #define TransactionRollbackError_doc \ "Error causing transaction rollback (deadlocks, serialisation failures, etc)." diff -Nru psycopg2-2.0.13/psycopg/psycopgmodule.c psycopg2-2.4.5/psycopg/psycopgmodule.c --- psycopg2-2.0.13/psycopg/psycopgmodule.c 2009-05-09 13:01:04.000000000 +0000 +++ psycopg2-2.4.5/psycopg/psycopgmodule.c 2012-03-28 21:09:15.000000000 +0000 @@ -1,34 +1,37 @@ /* psycopgmodule.c - psycopg module (will import other C classes) * - * Copyright (C) 2003 Federico Di Gregorio + * Copyright (C) 2003-2010 Federico Di Gregorio * * This file is part of psycopg. * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2, - * or (at your option) any later version. + * psycopg2 is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * In addition, as a special exception, the copyright holders give + * permission to link this program with the OpenSSL library (or with + * modified versions of OpenSSL that use the same license as OpenSSL), + * and distribute linked combinations including the two. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You must obey the GNU Lesser General Public License in all respects for + * all of the code used other than OpenSSL. + * + * psycopg2 is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. */ -#define PY_SSIZE_T_CLEAN -#include - #define PSYCOPG_MODULE -#include "psycopg/config.h" -#include "psycopg/python.h" #include "psycopg/psycopg.h" + #include "psycopg/connection.h" #include "psycopg/cursor.h" +#include "psycopg/green.h" #include "psycopg/lobject.h" +#include "psycopg/notify.h" +#include "psycopg/xid.h" #include "psycopg/typecast.h" #include "psycopg/microprotocols.h" #include "psycopg/microprotocols_proto.h" @@ -36,7 +39,9 @@ #include "psycopg/adapter_qstring.h" #include "psycopg/adapter_binary.h" #include "psycopg/adapter_pboolean.h" +#include "psycopg/adapter_pint.h" #include "psycopg/adapter_pfloat.h" +#include "psycopg/adapter_pdecimal.h" #include "psycopg/adapter_asis.h" #include "psycopg/adapter_list.h" #include "psycopg/typecast_binary.h" @@ -44,17 +49,12 @@ #ifdef HAVE_MXDATETIME #include #include "psycopg/adapter_mxdatetime.h" -HIDDEN mxDateTimeModule_APIObject *mxDateTimeP = NULL; #endif /* some module-level variables, like the datetime module */ #include #include "psycopg/adapter_datetime.h" HIDDEN PyObject *pyDateTimeModuleP = NULL; -HIDDEN PyObject *pyDateTypeP = NULL; -HIDDEN PyObject *pyTimeTypeP = NULL; -HIDDEN PyObject *pyDateTimeTypeP = NULL; -HIDDEN PyObject *pyDeltaTypeP = NULL; /* pointers to the psycopg.tz classes */ HIDDEN PyObject *pyPsycopgTzModule = NULL; @@ -67,143 +67,50 @@ HIDDEN int psycopg_debug_enabled = 0; #endif +/* Python representation of SQL NULL */ +HIDDEN PyObject *psyco_null = NULL; + +/* The type of the cursor.description items */ +HIDDEN PyObject *psyco_DescriptionType = NULL; + /** connect module-level function **/ #define psyco_connect_doc \ -"connect(dsn, ...) -- Create a new database connection.\n\n" \ -"This function supports two different but equivalent sets of arguments.\n" \ -"A single data source name or ``dsn`` string can be used to specify the\n" \ -"connection parameters, as follows::\n\n" \ -" psycopg2.connect(\"dbname=xxx user=xxx ...\")\n\n" \ -"If ``dsn`` is not provided it is possible to pass the parameters as\n" \ -"keyword arguments; e.g.::\n\n" \ -" psycopg2.connect(database='xxx', user='xxx', ...)\n\n" \ -"The full list of available parameters is:\n\n" \ -"- ``dbname`` -- database name (only in 'dsn')\n" \ -"- ``database`` -- database name (only as keyword argument)\n" \ -"- ``host`` -- host address (defaults to UNIX socket if not provided)\n" \ -"- ``port`` -- port number (defaults to 5432 if not provided)\n" \ -"- ``user`` -- user name used to authenticate\n" \ -"- ``password`` -- password used to authenticate\n" \ -"- ``sslmode`` -- SSL mode (see PostgreSQL documentation)\n\n" \ -"If the ``connection_factory`` keyword argument is not provided this\n" \ -"function always return an instance of the `connection` class.\n" \ -"Else the given sub-class of `extensions.connection` will be used to\n" \ -"instantiate the connection object.\n\n" \ -":return: New database connection\n" \ -":rtype: `extensions.connection`" - -static size_t -_psyco_connect_fill_dsn(char *dsn, const char *kw, const char *v, size_t i) -{ - strcpy(&dsn[i], kw); i += strlen(kw); - strcpy(&dsn[i], v); i += strlen(v); - return i; -} +"_connect(dsn, [connection_factory], [async]) -- New database connection.\n\n" static PyObject * psyco_connect(PyObject *self, PyObject *args, PyObject *keywds) { - PyObject *conn = NULL, *factory = NULL; - PyObject *pyport = NULL; + PyObject *conn = NULL; + PyObject *factory = NULL; + const char *dsn = NULL; + int async = 0; - size_t idsn=-1; - int iport=-1; - const char *dsn_static = NULL; - char *dsn_dynamic=NULL; - const char *database=NULL, *user=NULL, *password=NULL; - const char *host=NULL, *sslmode=NULL; - char port[16]; - - static char *kwlist[] = {"dsn", "database", "host", "port", - "user", "password", "sslmode", - "connection_factory", NULL}; - - if (!PyArg_ParseTupleAndKeywords(args, keywds, "|sssOsssO", kwlist, - &dsn_static, &database, &host, &pyport, - &user, &password, &sslmode, &factory)) { + static char *kwlist[] = {"dsn", "connection_factory", "async", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, keywds, "s|Oi", kwlist, + &dsn, &factory, &async)) { return NULL; } - if (pyport && PyString_Check(pyport)) { - PyObject *pyint = PyInt_FromString(PyString_AsString(pyport), NULL, 10); - if (!pyint) goto fail; - /* Must use PyInt_AsLong rather than PyInt_AS_LONG, because - * PyInt_FromString can return a PyLongObject: */ - iport = PyInt_AsLong(pyint); - Py_DECREF(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"); - goto fail; - } - - if (iport > 0) - PyOS_snprintf(port, 16, "%d", iport); - - if (dsn_static == NULL) { - size_t l = 46; /* len(" dbname= user= password= host= port= sslmode=\0") */ - - if (database) l += strlen(database); - if (host) l += strlen(host); - if (iport > 0) l += strlen(port); - if (user) l += strlen(user); - if (password) l += strlen(password); - if (sslmode) l += strlen(sslmode); - - dsn_dynamic = malloc(l*sizeof(char)); - if (dsn_dynamic == NULL) { - PyErr_SetString(InterfaceError, "dynamic dsn allocation failed"); - goto fail; - } + Dprintf("psyco_connect: dsn = '%s', async = %d", dsn, async); - idsn = 0; - if (database) - idsn = _psyco_connect_fill_dsn(dsn_dynamic, " dbname=", database, idsn); - if (host) - idsn = _psyco_connect_fill_dsn(dsn_dynamic, " host=", host, idsn); - if (iport > 0) - idsn = _psyco_connect_fill_dsn(dsn_dynamic, " port=", port, idsn); - if (user) - idsn = _psyco_connect_fill_dsn(dsn_dynamic, " user=", user, idsn); - if (password) - idsn = _psyco_connect_fill_dsn(dsn_dynamic, " password=", password, idsn); - if (sslmode) - idsn = _psyco_connect_fill_dsn(dsn_dynamic, " sslmode=", sslmode, idsn); - - if (idsn > 0) { - dsn_dynamic[idsn] = '\0'; - memmove(dsn_dynamic, &dsn_dynamic[1], idsn); - } - else { - PyErr_SetString(InterfaceError, "missing dsn and no parameters"); - goto fail; - } + /* allocate connection, fill with errors and return it */ + if (factory == NULL || factory == Py_None) { + factory = (PyObject *)&connectionType; } - { - const char *dsn = (dsn_static != NULL ? dsn_static : dsn_dynamic); - Dprintf("psyco_connect: dsn = '%s'", dsn); - - /* allocate connection, fill with errors and return it */ - if (factory == NULL) factory = (PyObject *)&connectionType; - conn = PyObject_CallFunction(factory, "s", dsn); - } - - goto cleanup; - fail: - assert (PyErr_Occurred()); - if (conn != NULL) { - Py_DECREF(conn); - conn = NULL; - } - /* Fall through to cleanup: */ - cleanup: - if (dsn_dynamic != NULL) { - free(dsn_dynamic); - } + /* Here we are breaking the connection.__init__ interface defined + * by psycopg2. So, if not requiring an async conn, avoid passing + * the async parameter. */ + /* TODO: would it be possible to avoid an additional parameter + * to the conn constructor? A subclass? (but it would require mixins + * to further subclass) Another dsn parameter (but is not really + * a connection parameter that can be configured) */ + if (!async) { + conn = PyObject_CallFunction(factory, "s", dsn); + } else { + conn = PyObject_CallFunction(factory, "si", dsn, async); + } return conn; } @@ -212,11 +119,11 @@ #define psyco_register_type_doc \ "register_type(obj, conn_or_curs) -> None -- register obj with psycopg type system\n\n" \ ":Parameters:\n" \ -" * `obj`: A type adapter created by `new_type()`" \ +" * `obj`: A type adapter created by `new_type()`\n" \ " * `conn_or_curs`: A connection, cursor or None" #define typecast_from_python_doc \ -"new_type(oids, name, adapter) -> new type object\n\n" \ +"new_type(oids, name, castobj) -> new type object\n\n" \ "Create a new binding object. The object can be used with the\n" \ "`register_type()` function to bind PostgreSQL objects to python objects.\n\n" \ ":Parameters:\n" \ @@ -224,16 +131,17 @@ " * `name`: Name for the new type\n" \ " * `adapter`: Callable to perform type conversion.\n" \ " It must have the signature ``fun(value, cur)`` where ``value`` is\n" \ -" the string representation returned by PostgreSQL (`None` if ``NULL``)\n" \ +" the string representation returned by PostgreSQL (`!None` if ``NULL``)\n" \ " and ``cur`` is the cursor from which data are read." -static void -_psyco_register_type_set(PyObject **dict, PyObject *type) -{ - if (*dict == NULL) - *dict = PyDict_New(); - typecast_add(type, *dict, 0); -} +#define typecast_array_from_python_doc \ +"new_array_type(oids, name, baseobj) -> new type object\n\n" \ +"Create a new binding object to parse an array.\n\n" \ +"The object can be used with `register_type()`.\n\n" \ +":Parameters:\n" \ +" * `oids`: Tuple of ``oid`` of the PostgreSQL types to convert.\n" \ +" * `name`: Name for the new type\n" \ +" * `baseobj`: Adapter to perform type conversion of a single array item." static PyObject * psyco_register_type(PyObject *self, PyObject *args) @@ -246,10 +154,16 @@ if (obj != NULL && obj != Py_None) { if (PyObject_TypeCheck(obj, &cursorType)) { - _psyco_register_type_set(&(((cursorObject*)obj)->string_types), type); + PyObject **dict = &(((cursorObject*)obj)->string_types); + if (*dict == NULL) { + if (!(*dict = PyDict_New())) { return NULL; } + } + if (0 > typecast_add(type, *dict, 0)) { return NULL; } } else if (PyObject_TypeCheck(obj, &connectionType)) { - typecast_add(type, ((connectionObject*)obj)->string_types, 0); + if (0 > typecast_add(type, ((connectionObject*)obj)->string_types, 0)) { + return NULL; + } } else { PyErr_SetString(PyExc_TypeError, @@ -258,7 +172,7 @@ } } else { - typecast_add(type, NULL, 0); + if (0 > typecast_add(type, NULL, 0)) { return NULL; } } Py_INCREF(Py_None); @@ -266,43 +180,108 @@ } -/* default adapters */ - -static void +/* Initialize the default adapters map + * + * Return 0 on success, else -1 and set an exception. + */ +static int psyco_adapters_init(PyObject *mod) { - PyObject *call; + PyObject *call = NULL; + int rv = -1; + + if (0 != microprotocols_add(&PyFloat_Type, NULL, (PyObject*)&pfloatType)) { + goto exit; + } +#if PY_MAJOR_VERSION < 3 + if (0 != microprotocols_add(&PyInt_Type, NULL, (PyObject*)&pintType)) { + goto exit; + } +#endif + if (0 != microprotocols_add(&PyLong_Type, NULL, (PyObject*)&pintType)) { + goto exit; + } + if (0 != microprotocols_add(&PyBool_Type, NULL, (PyObject*)&pbooleanType)) { + goto exit; + } + + /* strings */ +#if PY_MAJOR_VERSION < 3 + if (0 != microprotocols_add(&PyString_Type, NULL, (PyObject*)&qstringType)) { + goto exit; + } +#endif + if (0 != microprotocols_add(&PyUnicode_Type, NULL, (PyObject*)&qstringType)) { + goto exit; + } + + /* binary */ +#if PY_MAJOR_VERSION < 3 + if (0 != microprotocols_add(&PyBuffer_Type, NULL, (PyObject*)&binaryType)) { + goto exit; + } +#else + if (0 != microprotocols_add(&PyBytes_Type, NULL, (PyObject*)&binaryType)) { + goto exit; + } +#endif + +#if PY_MAJOR_VERSION >= 3 || PY_MINOR_VERSION >= 6 + if (0 != microprotocols_add(&PyByteArray_Type, NULL, (PyObject*)&binaryType)) { + goto exit; + } +#endif +#if PY_MAJOR_VERSION >= 3 || PY_MINOR_VERSION >= 7 + if (0 != microprotocols_add(&PyMemoryView_Type, NULL, (PyObject*)&binaryType)) { + goto exit; + } +#endif - microprotocols_add(&PyFloat_Type, NULL, (PyObject*)&pfloatType); - microprotocols_add(&PyInt_Type, NULL, (PyObject*)&asisType); - microprotocols_add(&PyLong_Type, NULL, (PyObject*)&asisType); - microprotocols_add(&PyBool_Type, NULL, (PyObject*)&pbooleanType); - - microprotocols_add(&PyString_Type, NULL, (PyObject*)&qstringType); - microprotocols_add(&PyUnicode_Type, NULL, (PyObject*)&qstringType); - microprotocols_add(&PyBuffer_Type, NULL, (PyObject*)&binaryType); - microprotocols_add(&PyList_Type, NULL, (PyObject*)&listType); - microprotocols_add((PyTypeObject*)psyco_GetDecimalType(), - NULL, (PyObject*)&asisType); + if (0 != microprotocols_add(&PyList_Type, NULL, (PyObject*)&listType)) { + goto exit; + } /* the module has already been initialized, so we can obtain the callable objects directly from its dictionary :) */ - call = PyMapping_GetItemString(mod, "DateFromPy"); - microprotocols_add((PyTypeObject*)pyDateTypeP, NULL, call); - call = PyMapping_GetItemString(mod, "TimeFromPy"); - microprotocols_add((PyTypeObject*)pyTimeTypeP, NULL, call); - call = PyMapping_GetItemString(mod, "TimestampFromPy"); - microprotocols_add((PyTypeObject*)pyDateTimeTypeP, NULL, call); - call = PyMapping_GetItemString(mod, "IntervalFromPy"); - microprotocols_add((PyTypeObject*)pyDeltaTypeP, NULL, call); + if (!(call = PyMapping_GetItemString(mod, "DateFromPy"))) { goto exit; } + if (0 != microprotocols_add(PyDateTimeAPI->DateType, NULL, call)) { goto exit; } + Py_CLEAR(call); + + if (!(call = PyMapping_GetItemString(mod, "TimeFromPy"))) { goto exit; } + if (0 != microprotocols_add(PyDateTimeAPI->TimeType, NULL, call)) { goto exit; } + Py_CLEAR(call); + + if (!(call = PyMapping_GetItemString(mod, "TimestampFromPy"))) { goto exit; } + if (0 != microprotocols_add(PyDateTimeAPI->DateTimeType, NULL, call)) { goto exit; } + Py_CLEAR(call); + + if (!(call = PyMapping_GetItemString(mod, "IntervalFromPy"))) { goto exit; } + if (0 != microprotocols_add(PyDateTimeAPI->DeltaType, NULL, call)) { goto exit; } + Py_CLEAR(call); #ifdef HAVE_MXDATETIME /* as above, we use the callable objects from the psycopg module */ - call = PyMapping_GetItemString(mod, "TimestampFromMx"); - microprotocols_add(mxDateTimeP->DateTime_Type, NULL, call); - call = PyMapping_GetItemString(mod, "TimeFromMx"); - microprotocols_add(mxDateTimeP->DateTimeDelta_Type, NULL, call); + if (NULL != (call = PyMapping_GetItemString(mod, "TimestampFromMx"))) { + if (0 != microprotocols_add(mxDateTime.DateTime_Type, NULL, call)) { goto exit; } + Py_CLEAR(call); + + /* if we found the above, we have this too. */ + if (!(call = PyMapping_GetItemString(mod, "TimeFromMx"))) { goto exit; } + if (0 != microprotocols_add(mxDateTime.DateTimeDelta_Type, NULL, call)) { goto exit; } + Py_CLEAR(call); + } + else { + PyErr_Clear(); + } #endif + + /* Success! */ + rv = 0; + +exit: + Py_XDECREF(call); + + return rv; } /* psyco_encodings_fill @@ -310,7 +289,32 @@ Fill the module's postgresql<->python encoding table */ static encodingPair encodings[] = { - {"SQL_ASCII", "ascii"}, + {"ABC", "cp1258"}, + {"ALT", "cp866"}, + {"BIG5", "big5"}, + {"EUC_CN", "euccn"}, + {"EUC_JIS_2004", "euc_jis_2004"}, + {"EUC_JP", "euc_jp"}, + {"EUC_KR", "euc_kr"}, + {"GB18030", "gb18030"}, + {"GBK", "gbk"}, + {"ISO_8859_1", "iso8859_1"}, + {"ISO_8859_2", "iso8859_2"}, + {"ISO_8859_3", "iso8859_3"}, + {"ISO_8859_5", "iso8859_5"}, + {"ISO_8859_6", "iso8859_6"}, + {"ISO_8859_7", "iso8859_7"}, + {"ISO_8859_8", "iso8859_8"}, + {"ISO_8859_9", "iso8859_9"}, + {"ISO_8859_10", "iso8859_10"}, + {"ISO_8859_13", "iso8859_13"}, + {"ISO_8859_14", "iso8859_14"}, + {"ISO_8859_15", "iso8859_15"}, + {"ISO_8859_16", "iso8859_16"}, + {"JOHAB", "johab"}, + {"KOI8", "koi8_r"}, + {"KOI8R", "koi8_r"}, + {"KOI8U", "koi8_u"}, {"LATIN1", "iso8859_1"}, {"LATIN2", "iso8859_2"}, {"LATIN3", "iso8859_3"}, @@ -320,46 +324,30 @@ {"LATIN7", "iso8859_13"}, {"LATIN8", "iso8859_14"}, {"LATIN9", "iso8859_15"}, - {"ISO88591", "iso8859_1"}, - {"ISO88592", "iso8859_2"}, - {"ISO88593", "iso8859_3"}, - {"ISO88595", "iso8859_5"}, - {"ISO88596", "iso8859_6"}, - {"ISO88597", "iso8859_7"}, - {"ISO885913", "iso8859_13"}, - {"ISO88598", "iso8859_8"}, - {"ISO88599", "iso8859_9"}, - {"ISO885914", "iso8859_14"}, - {"ISO885915", "iso8859_15"}, - {"UNICODE", "utf_8"}, /* Not valid in 8.2, backward compatibility */ - {"UTF8", "utf_8"}, - {"WIN950", "cp950"}, - {"Windows950", "cp950"}, - {"BIG5", "big5"}, - {"EUC_JP", "euc_jp"}, - {"EUC_KR", "euc_kr"}, - {"GB18030", "gb18030"}, - {"GBK", "gbk"}, - {"WIN936", "gbk"}, - {"Windows936", "gbk"}, - {"JOHAB", "johab"}, - {"KOI8", "koi8_r"}, /* in PG: KOI8 == KOI8R == KOI8-R == KOI8-U - but in Python there is koi8_r AND koi8_u */ - {"KOI8R", "koi8_r"}, - {"SJIS", "cp932"}, + {"LATIN10", "iso8859_16"}, {"Mskanji", "cp932"}, {"ShiftJIS", "cp932"}, - {"WIN932", "cp932"}, - {"Windows932", "cp932"}, + {"SHIFT_JIS_2004", "shift_jis_2004"}, + {"SJIS", "cp932"}, + {"SQL_ASCII", "ascii"}, /* XXX this is wrong: SQL_ASCII means "no + * encoding" we should fix the unicode + * typecaster to return a str or bytes in Py3 + */ + {"TCVN", "cp1258"}, + {"TCVN5712", "cp1258"}, {"UHC", "cp949"}, - {"WIN949", "cp949"}, - {"Windows949", "cp949"}, + {"UNICODE", "utf_8"}, /* Not valid in 8.2, backward compatibility */ + {"UTF8", "utf_8"}, + {"VSCII", "cp1258"}, + {"WIN", "cp1251"}, {"WIN866", "cp866"}, - {"ALT", "cp866"}, {"WIN874", "cp874"}, + {"WIN932", "cp932"}, + {"WIN936", "gbk"}, + {"WIN949", "cp949"}, + {"WIN950", "cp950"}, {"WIN1250", "cp1250"}, {"WIN1251", "cp1251"}, - {"WIN", "cp1251"}, {"WIN1252", "cp1252"}, {"WIN1253", "cp1253"}, {"WIN1254", "cp1254"}, @@ -367,29 +355,38 @@ {"WIN1256", "cp1256"}, {"WIN1257", "cp1257"}, {"WIN1258", "cp1258"}, - {"ABC", "cp1258"}, - {"TCVN", "cp1258"}, - {"TCVN5712", "cp1258"}, - {"VSCII", "cp1258"}, + {"Windows932", "cp932"}, + {"Windows936", "gbk"}, + {"Windows949", "cp949"}, + {"Windows950", "cp950"}, /* those are missing from Python: */ -/* {"EUC_CN", "?"}, */ /* {"EUC_TW", "?"}, */ -/* {"LATIN10", "?"}, */ -/* {"ISO885916", "?"}, */ /* {"MULE_INTERNAL", "?"}, */ {NULL, NULL} }; -static void psyco_encodings_fill(PyObject *dict) +/* Initialize the encodings table. + * + * Return 0 on success, else -1 and set an exception. + */ +static int psyco_encodings_fill(PyObject *dict) { + PyObject *value = NULL; encodingPair *enc; + int rv = -1; for (enc = encodings; enc->pgenc != NULL; enc++) { - PyObject *value = PyString_FromString(enc->pyenc); - PyDict_SetItemString(dict, enc->pgenc, value); - Py_DECREF(value); + if (!(value = Text_FromUTF8(enc->pyenc))) { goto exit; } + if (0 != PyDict_SetItemString(dict, enc->pgenc, value)) { goto exit; } + Py_CLEAR(value); } + rv = 0; + +exit: + Py_XDECREF(value); + + return rv; } /* psyco_errors_init, psyco_errors_fill (callable from C) @@ -427,7 +424,7 @@ NotSupportedError_doc }, #ifdef PSYCOPG_EXTENSIONS { "psycopg2.extensions.QueryCanceledError", &QueryCanceledError, - &OperationalError, OperationalError_doc }, + &OperationalError, QueryCanceledError_doc }, { "psycopg2.extensions.TransactionRollbackError", &TransactionRollbackError, &OperationalError, TransactionRollbackError_doc }, @@ -435,7 +432,59 @@ {NULL} /* Sentinel */ }; -static void + +/* Error.__reduce_ex__ + * + * The method is required to make exceptions picklable: set the cursor + * attribute to None. Only working from Py 2.5: previous versions + * would require implementing __getstate__, and as of 2012 it's a little + * bit too late to care. */ +static PyObject * +psyco_error_reduce_ex(PyObject *self, PyObject *args) +{ + PyObject *proto = NULL; + PyObject *super = NULL; + PyObject *tuple = NULL; + PyObject *dict = NULL; + PyObject *rv = NULL; + + /* tuple = Exception.__reduce_ex__(self, proto) */ + if (!PyArg_ParseTuple(args, "O", &proto)) { + goto error; + } + if (!(super = PyObject_GetAttrString(PyExc_Exception, "__reduce_ex__"))) { + goto error; + } + if (!(tuple = PyObject_CallFunctionObjArgs(super, self, proto, NULL))) { + goto error; + } + + /* tuple[2]['cursor'] = None + * + * If these checks fail, we can still return a valid object. Pickle + * will likely fail downstream, but there's nothing else we can do here */ + if (!PyTuple_Check(tuple)) { goto exit; } + if (3 > PyTuple_GET_SIZE(tuple)) { goto exit; } + dict = PyTuple_GET_ITEM(tuple, 2); /* borrowed */ + if (!PyDict_Check(dict)) { goto exit; } + + /* Modify the tuple inplace and return it */ + if (0 != PyDict_SetItemString(dict, "cursor", Py_None)) { + goto error; + } + +exit: + rv = tuple; + tuple = NULL; + +error: + Py_XDECREF(tuple); + Py_XDECREF(super); + + return rv; +} + +static int psyco_errors_init(void) { /* the names of the exceptions here reflect the oranization of the @@ -443,24 +492,40 @@ live in _psycopg */ int i; - PyObject *dict; + PyObject *dict = NULL; PyObject *base; - PyObject *str; + PyObject *str = NULL; + PyObject *descr = NULL; + int rv = -1; + + static PyMethodDef psyco_error_reduce_ex_def = + {"__reduce_ex__", psyco_error_reduce_ex, METH_VARARGS, "pickle helper"}; for (i=0; exctable[i].name; i++) { - dict = PyDict_New(); + if (!(dict = PyDict_New())) { goto exit; } if (exctable[i].docstr) { - str = PyString_FromString(exctable[i].docstr); - PyDict_SetItemString(dict, "__doc__", str); + if (!(str = Text_FromUTF8(exctable[i].docstr))) { goto exit; } + if (0 != PyDict_SetItemString(dict, "__doc__", str)) { goto exit; } + Py_CLEAR(str); } - if (exctable[i].base == 0) + if (exctable[i].base == 0) { + #if PY_MAJOR_VERSION < 3 base = PyExc_StandardError; + #else + /* StandardError is gone in 3.0 */ + base = NULL; + #endif + } else base = *exctable[i].base; - *exctable[i].exc = PyErr_NewException(exctable[i].name, base, dict); + if (!(*exctable[i].exc = PyErr_NewException( + exctable[i].name, base, dict))) { + goto exit; + } + Py_CLEAR(dict); } /* Make pgerror, pgcode and cursor default to None on psycopg @@ -469,78 +534,114 @@ PyObject_SetAttrString(Error, "pgerror", Py_None); PyObject_SetAttrString(Error, "pgcode", Py_None); PyObject_SetAttrString(Error, "cursor", Py_None); + + /* install __reduce_ex__ on Error to make all the subclasses picklable. + * + * Don't install it on Py 2.4: it is not used by the pickle + * protocol, and if called manually fails in an unsettling way, + * probably because the exceptions were old-style classes. */ +#if PY_VERSION_HEX >= 0x02050000 + if (!(descr = PyDescr_NewMethod((PyTypeObject *)Error, + &psyco_error_reduce_ex_def))) { + goto exit; + } + if (0 != PyObject_SetAttrString(Error, + psyco_error_reduce_ex_def.ml_name, descr)) { + goto exit; + } +#endif + + rv = 0; + +exit: + Py_XDECREF(descr); + Py_XDECREF(str); + Py_XDECREF(dict); + return rv; } void psyco_errors_fill(PyObject *dict) { - PyDict_SetItemString(dict, "Error", Error); - PyDict_SetItemString(dict, "Warning", Warning); - PyDict_SetItemString(dict, "InterfaceError", InterfaceError); - PyDict_SetItemString(dict, "DatabaseError", DatabaseError); - PyDict_SetItemString(dict, "InternalError", InternalError); - PyDict_SetItemString(dict, "OperationalError", OperationalError); - PyDict_SetItemString(dict, "ProgrammingError", ProgrammingError); - PyDict_SetItemString(dict, "IntegrityError", IntegrityError); - PyDict_SetItemString(dict, "DataError", DataError); - PyDict_SetItemString(dict, "NotSupportedError", NotSupportedError); -#ifdef PSYCOPG_EXTENSIONS - PyDict_SetItemString(dict, "QueryCanceledError", QueryCanceledError); - PyDict_SetItemString(dict, "TransactionRollbackError", - TransactionRollbackError); -#endif + int i; + char *name; + + for (i = 0; exctable[i].name; i++) { + if (NULL == exctable[i].exc) { continue; } + + /* the name is the part after the last dot */ + name = strrchr(exctable[i].name, '.'); + name = name ? name + 1 : exctable[i].name; + + PyDict_SetItemString(dict, name, *exctable[i].exc); + } } void psyco_errors_set(PyObject *type) { - PyObject_SetAttrString(type, "Error", Error); - PyObject_SetAttrString(type, "Warning", Warning); - PyObject_SetAttrString(type, "InterfaceError", InterfaceError); - PyObject_SetAttrString(type, "DatabaseError", DatabaseError); - PyObject_SetAttrString(type, "InternalError", InternalError); - PyObject_SetAttrString(type, "OperationalError", OperationalError); - PyObject_SetAttrString(type, "ProgrammingError", ProgrammingError); - PyObject_SetAttrString(type, "IntegrityError", IntegrityError); - PyObject_SetAttrString(type, "DataError", DataError); - PyObject_SetAttrString(type, "NotSupportedError", NotSupportedError); -#ifdef PSYCOPG_EXTENSIONS - PyObject_SetAttrString(type, "QueryCanceledError", QueryCanceledError); - PyObject_SetAttrString(type, "TransactionRollbackError", - TransactionRollbackError); -#endif + int i; + char *name; + + for (i = 0; exctable[i].name; i++) { + if (NULL == exctable[i].exc) { continue; } + + /* the name is the part after the last dot */ + name = strrchr(exctable[i].name, '.'); + name = name ? name + 1 : exctable[i].name; + + PyObject_SetAttrString(type, name, *exctable[i].exc); + } } -/* psyco_error_new +/* psyco_set_error Create a new error of the given type with extra attributes. */ -void -psyco_set_error(PyObject *exc, PyObject *curs, const char *msg, +RAISES void +psyco_set_error(PyObject *exc, cursorObject *curs, const char *msg, const char *pgerror, const char *pgcode) { PyObject *t; + PyObject *pymsg; + PyObject *err = NULL; + connectionObject *conn = NULL; - PyObject *err = PyObject_CallFunction(exc, "s", msg); + if (curs) { + conn = ((cursorObject *)curs)->conn; + } + + if ((pymsg = conn_text_from_chars(conn, msg))) { + err = PyObject_CallFunctionObjArgs(exc, pymsg, NULL); + Py_DECREF(pymsg); + } + else { + /* what's better than an error in an error handler in the morning? + * Anyway, some error was set, refcount is ok... get outta here. */ + return; + } if (err) { + if (curs) { + PyObject_SetAttrString(err, "cursor", (PyObject *)curs); + } + if (pgerror) { - t = PyString_FromString(pgerror); - PyObject_SetAttrString(err, "pgerror", t); - Py_DECREF(t); + if ((t = conn_text_from_chars(conn, pgerror))) { + PyObject_SetAttrString(err, "pgerror", t); + Py_DECREF(t); + } } if (pgcode) { - t = PyString_FromString(pgcode); - PyObject_SetAttrString(err, "pgcode", t); - Py_DECREF(t); + if ((t = conn_text_from_chars(conn, pgcode))) { + PyObject_SetAttrString(err, "pgcode", t); + Py_DECREF(t); + } } - if (curs) - PyObject_SetAttrString(err, "cursor", curs); - PyErr_SetObject(exc, err); - Py_DECREF(err); + Py_DECREF(err); } } @@ -575,13 +676,13 @@ the float type. If decimals are not to be used, return NULL. - */ +*/ PyObject * psyco_GetDecimalType(void) { - PyObject *decimalType = NULL; static PyObject *cachedType = NULL; + PyObject *decimalType = NULL; PyObject *decimal; /* Use the cached object if running from the main interpreter. */ @@ -599,12 +700,11 @@ } else { PyErr_Clear(); - decimalType = (PyObject *)&PyFloat_Type; - Py_INCREF(decimalType); + decimalType = NULL; } /* Store the object from future uses. */ - if (can_cache && !cachedType) { + if (can_cache && !cachedType && decimalType) { Py_INCREF(decimalType); cachedType = decimalType; } @@ -613,10 +713,51 @@ } +/* Create a namedtuple for cursor.description items + * + * Return None in case of expected errors (e.g. namedtuples not available) + * NULL in case of errors to propagate. + */ +static PyObject * +psyco_make_description_type(void) +{ + PyObject *nt = NULL; + PyObject *coll = NULL; + PyObject *rv = NULL; + + /* Try to import collections.namedtuple */ + if (!(coll = PyImport_ImportModule("collections"))) { + Dprintf("psyco_make_description_type: collections import failed"); + goto error; + } + if (!(nt = PyObject_GetAttrString(coll, "namedtuple"))) { + Dprintf("psyco_make_description_type: no collections.namedtuple"); + goto error; + } + + /* Build the namedtuple */ + rv = PyObject_CallFunction(nt, "ss", "Column", + "name type_code display_size internal_size precision scale null_ok"); + +exit: + Py_XDECREF(coll); + Py_XDECREF(nt); + + return rv; + +error: + /* controlled error: we will fall back to regular tuples. Return None. */ + PyErr_Clear(); + rv = Py_None; + Py_INCREF(rv); + goto exit; +} + + /** method table and module initialization **/ static PyMethodDef psycopgMethods[] = { - {"connect", (PyCFunction)psyco_connect, + {"_connect", (PyCFunction)psyco_connect, METH_VARARGS|METH_KEYWORDS, psyco_connect_doc}, {"adapt", (PyCFunction)psyco_microprotocols_adapt, METH_VARARGS, psyco_microprotocols_adapt_doc}, @@ -625,15 +766,21 @@ METH_VARARGS, psyco_register_type_doc}, {"new_type", (PyCFunction)typecast_from_python, METH_VARARGS|METH_KEYWORDS, typecast_from_python_doc}, + {"new_array_type", (PyCFunction)typecast_array_from_python, + METH_VARARGS|METH_KEYWORDS, typecast_array_from_python_doc}, {"AsIs", (PyCFunction)psyco_AsIs, METH_VARARGS, psyco_AsIs_doc}, {"QuotedString", (PyCFunction)psyco_QuotedString, METH_VARARGS, psyco_QuotedString_doc}, {"Boolean", (PyCFunction)psyco_Boolean, - METH_VARARGS, psyco_Float_doc}, - {"Float", (PyCFunction)psyco_Float, METH_VARARGS, psyco_Boolean_doc}, + {"Int", (PyCFunction)psyco_Int, + METH_VARARGS, psyco_Int_doc}, + {"Float", (PyCFunction)psyco_Float, + METH_VARARGS, psyco_Float_doc}, + {"Decimal", (PyCFunction)psyco_Decimal, + METH_VARARGS, psyco_Decimal_doc}, {"Binary", (PyCFunction)psyco_Binary, METH_VARARGS, psyco_Binary_doc}, {"Date", (PyCFunction)psyco_Date, @@ -661,6 +808,7 @@ METH_VARARGS, psyco_IntervalFromPy_doc}, #ifdef HAVE_MXDATETIME + /* to be deleted if not found at import time */ {"DateFromMx", (PyCFunction)psyco_DateFromMx, METH_VARARGS, psyco_DateFromMx_doc}, {"TimeFromMx", (PyCFunction)psyco_TimeFromMx, @@ -671,16 +819,39 @@ METH_VARARGS, psyco_IntervalFromMx_doc}, #endif +#ifdef PSYCOPG_EXTENSIONS + {"set_wait_callback", (PyCFunction)psyco_set_wait_callback, + METH_O, psyco_set_wait_callback_doc}, + {"get_wait_callback", (PyCFunction)psyco_get_wait_callback, + METH_NOARGS, psyco_get_wait_callback_doc}, +#endif + {NULL, NULL, 0, NULL} /* Sentinel */ }; +#if PY_MAJOR_VERSION > 2 +static struct PyModuleDef psycopgmodule = { + PyModuleDef_HEAD_INIT, + "_psycopg", + NULL, + -1, + psycopgMethods, + NULL, + NULL, + NULL, + NULL +}; +#endif + PyMODINIT_FUNC -init_psycopg(void) +INIT_MODULE(_psycopg)(void) { +#if PY_VERSION_HEX < 0x03020000 static void *PSYCOPG_API[PSYCOPG_API_pointers]; - - PyObject *module, *dict; PyObject *c_api_object; +#endif + + PyObject *module = NULL, *dict; #ifdef PSYCOPG_DEBUG if (getenv("PSYCOPG_DEBUG")) @@ -690,45 +861,57 @@ Dprintf("initpsycopg: initializing psycopg %s", PSYCOPG_VERSION); /* initialize all the new types and then the module */ - connectionType.ob_type = &PyType_Type; - cursorType.ob_type = &PyType_Type; - typecastType.ob_type = &PyType_Type; - qstringType.ob_type = &PyType_Type; - binaryType.ob_type = &PyType_Type; - isqlquoteType.ob_type = &PyType_Type; - pbooleanType.ob_type = &PyType_Type; - pfloatType.ob_type = &PyType_Type; - asisType.ob_type = &PyType_Type; - listType.ob_type = &PyType_Type; - chunkType.ob_type = &PyType_Type; - - if (PyType_Ready(&connectionType) == -1) return; - if (PyType_Ready(&cursorType) == -1) return; - if (PyType_Ready(&typecastType) == -1) return; - if (PyType_Ready(&qstringType) == -1) return; - if (PyType_Ready(&binaryType) == -1) return; - if (PyType_Ready(&isqlquoteType) == -1) return; - if (PyType_Ready(&pbooleanType) == -1) return; - if (PyType_Ready(&pfloatType) == -1) return; - if (PyType_Ready(&asisType) == -1) return; - if (PyType_Ready(&listType) == -1) return; - if (PyType_Ready(&chunkType) == -1) return; + Py_TYPE(&connectionType) = &PyType_Type; + Py_TYPE(&cursorType) = &PyType_Type; + Py_TYPE(&typecastType) = &PyType_Type; + Py_TYPE(&qstringType) = &PyType_Type; + Py_TYPE(&binaryType) = &PyType_Type; + Py_TYPE(&isqlquoteType) = &PyType_Type; + Py_TYPE(&pbooleanType) = &PyType_Type; + Py_TYPE(&pintType) = &PyType_Type; + Py_TYPE(&pfloatType) = &PyType_Type; + Py_TYPE(&pdecimalType) = &PyType_Type; + Py_TYPE(&asisType) = &PyType_Type; + Py_TYPE(&listType) = &PyType_Type; + Py_TYPE(&chunkType) = &PyType_Type; + Py_TYPE(&NotifyType) = &PyType_Type; + Py_TYPE(&XidType) = &PyType_Type; + + if (PyType_Ready(&connectionType) == -1) goto exit; + if (PyType_Ready(&cursorType) == -1) goto exit; + if (PyType_Ready(&typecastType) == -1) goto exit; + if (PyType_Ready(&qstringType) == -1) goto exit; + if (PyType_Ready(&binaryType) == -1) goto exit; + if (PyType_Ready(&isqlquoteType) == -1) goto exit; + if (PyType_Ready(&pbooleanType) == -1) goto exit; + if (PyType_Ready(&pintType) == -1) goto exit; + if (PyType_Ready(&pfloatType) == -1) goto exit; + if (PyType_Ready(&pdecimalType) == -1) goto exit; + if (PyType_Ready(&asisType) == -1) goto exit; + if (PyType_Ready(&listType) == -1) goto exit; + if (PyType_Ready(&chunkType) == -1) goto exit; + if (PyType_Ready(&NotifyType) == -1) goto exit; + if (PyType_Ready(&XidType) == -1) goto exit; #ifdef PSYCOPG_EXTENSIONS - lobjectType.ob_type = &PyType_Type; - if (PyType_Ready(&lobjectType) == -1) return; + Py_TYPE(&lobjectType) = &PyType_Type; + if (PyType_Ready(&lobjectType) == -1) goto exit; #endif /* import mx.DateTime module, if necessary */ #ifdef HAVE_MXDATETIME - mxdatetimeType.ob_type = &PyType_Type; - if (PyType_Ready(&mxdatetimeType) == -1) return; - if (mxDateTime_ImportModuleAndAPI() != 0) { - Dprintf("initpsycopg: why marc hide mx.DateTime again?!"); - PyErr_SetString(PyExc_ImportError, "can't import mx.DateTime module"); - return; + Py_TYPE(&mxdatetimeType) = &PyType_Type; + if (PyType_Ready(&mxdatetimeType) == -1) goto exit; + if (0 != mxDateTime_ImportModuleAndAPI()) { + PyErr_Clear(); + + /* only fail if the mx typacaster should have been the default */ +#ifdef PSYCOPG_DEFAULT_MXDATETIME + PyErr_SetString(PyExc_ImportError, + "can't import mx.DateTime module (requested as default adapter)"); + goto exit; +#endif } - mxDateTimeP = &mxDateTime; #endif /* import python builtin datetime module, if available */ @@ -736,24 +919,22 @@ if (pyDateTimeModuleP == NULL) { Dprintf("initpsycopg: can't import datetime module"); PyErr_SetString(PyExc_ImportError, "can't import datetime module"); - return; + goto exit; } - pydatetimeType.ob_type = &PyType_Type; - if (PyType_Ready(&pydatetimeType) == -1) return; - /* now we define the datetime types, this is crazy because python should - be doing that, not us! */ - pyDateTypeP = PyObject_GetAttrString(pyDateTimeModuleP, "date"); - pyTimeTypeP = PyObject_GetAttrString(pyDateTimeModuleP, "time"); - pyDateTimeTypeP = PyObject_GetAttrString(pyDateTimeModuleP, "datetime"); - pyDeltaTypeP = PyObject_GetAttrString(pyDateTimeModuleP, "timedelta"); + /* Initialize the PyDateTimeAPI everywhere is used */ + PyDateTime_IMPORT; + if (psyco_adapter_datetime_init()) { goto exit; } + + Py_TYPE(&pydatetimeType) = &PyType_Type; + if (PyType_Ready(&pydatetimeType) == -1) goto exit; /* import psycopg2.tz anyway (TODO: replace with C-level module?) */ pyPsycopgTzModule = PyImport_ImportModule("psycopg2.tz"); if (pyPsycopgTzModule == NULL) { Dprintf("initpsycopg: can't import psycopg2.tz module"); PyErr_SetString(PyExc_ImportError, "can't import psycopg2.tz module"); - return; + goto exit; } pyPsycopgTzLOCAL = PyObject_GetAttrString(pyPsycopgTzModule, "LOCAL"); @@ -761,32 +942,45 @@ PyObject_GetAttrString(pyPsycopgTzModule, "FixedOffsetTimezone"); /* initialize the module and grab module's dictionary */ +#if PY_MAJOR_VERSION < 3 module = Py_InitModule("_psycopg", psycopgMethods); +#else + module = PyModule_Create(&psycopgmodule); +#endif + if (!module) { goto exit; } + dict = PyModule_GetDict(module); /* initialize all the module's exported functions */ /* PyBoxer_API[PyBoxer_Fake_NUM] = (void *)PyBoxer_Fake; */ /* Create a CObject containing the API pointer array's address */ + /* If anybody asks for a PyCapsule we'll deal with it. */ +#if PY_VERSION_HEX < 0x03020000 c_api_object = PyCObject_FromVoidPtr((void *)PSYCOPG_API, NULL); if (c_api_object != NULL) PyModule_AddObject(module, "_C_API", c_api_object); +#endif /* other mixed initializations of module-level variables */ - psycoEncodings = PyDict_New(); - psyco_encodings_fill(psycoEncodings); + if (!(psycoEncodings = PyDict_New())) { goto exit; } + if (0 != psyco_encodings_fill(psycoEncodings)) { goto exit; } + psyco_null = Bytes_FromString("NULL"); + if (!(psyco_DescriptionType = psyco_make_description_type())) { goto exit; } /* set some module's parameters */ PyModule_AddStringConstant(module, "__version__", PSYCOPG_VERSION); PyModule_AddStringConstant(module, "__doc__", "psycopg PostgreSQL driver"); - PyModule_AddObject(module, "apilevel", PyString_FromString(APILEVEL)); + PyModule_AddObject(module, "apilevel", Text_FromUTF8(APILEVEL)); PyModule_AddObject(module, "threadsafety", PyInt_FromLong(THREADSAFETY)); - PyModule_AddObject(module, "paramstyle", PyString_FromString(PARAMSTYLE)); + PyModule_AddObject(module, "paramstyle", Text_FromUTF8(PARAMSTYLE)); /* put new types in module dictionary */ PyModule_AddObject(module, "connection", (PyObject*)&connectionType); PyModule_AddObject(module, "cursor", (PyObject*)&cursorType); PyModule_AddObject(module, "ISQLQuote", (PyObject*)&isqlquoteType); + PyModule_AddObject(module, "Notify", (PyObject*)&NotifyType); + PyModule_AddObject(module, "Xid", (PyObject*)&XidType); #ifdef PSYCOPG_EXTENSIONS PyModule_AddObject(module, "lobject", (PyObject*)&lobjectType); #endif @@ -794,15 +988,25 @@ /* encodings dictionary in module dictionary */ PyModule_AddObject(module, "encodings", psycoEncodings); +#ifdef HAVE_MXDATETIME + /* If we can't find mx.DateTime objects at runtime, + * remove them from the module (and, as consequence, from the adapters). */ + if (0 != psyco_adapter_mxdatetime_init()) { + PyDict_DelItemString(dict, "DateFromMx"); + PyDict_DelItemString(dict, "TimeFromMx"); + PyDict_DelItemString(dict, "TimestampFromMx"); + PyDict_DelItemString(dict, "IntervalFromMx"); + } +#endif /* initialize default set of typecasters */ - typecast_init(dict); + if (0 != typecast_init(dict)) { goto exit; } /* initialize microprotocols layer */ microprotocols_init(dict); - psyco_adapters_init(dict); + if (0 != psyco_adapters_init(dict)) { goto exit; } /* create a standard set of exceptions and add them to the module's dict */ - psyco_errors_init(); + if (0 != psyco_errors_init()) { goto exit; } psyco_errors_fill(dict); /* Solve win32 build issue about non-constant initializer element */ @@ -810,22 +1014,32 @@ binaryType.tp_alloc = PyType_GenericAlloc; isqlquoteType.tp_alloc = PyType_GenericAlloc; pbooleanType.tp_alloc = PyType_GenericAlloc; + pintType.tp_alloc = PyType_GenericAlloc; pfloatType.tp_alloc = PyType_GenericAlloc; + pdecimalType.tp_alloc = PyType_GenericAlloc; connectionType.tp_alloc = PyType_GenericAlloc; asisType.tp_alloc = PyType_GenericAlloc; qstringType.tp_alloc = PyType_GenericAlloc; listType.tp_alloc = PyType_GenericAlloc; chunkType.tp_alloc = PyType_GenericAlloc; pydatetimeType.tp_alloc = PyType_GenericAlloc; + NotifyType.tp_alloc = PyType_GenericAlloc; + XidType.tp_alloc = PyType_GenericAlloc; #ifdef PSYCOPG_EXTENSIONS lobjectType.tp_alloc = PyType_GenericAlloc; #endif - #ifdef HAVE_MXDATETIME mxdatetimeType.tp_alloc = PyType_GenericAlloc; #endif Dprintf("initpsycopg: module initialization complete"); + +exit: +#if PY_MAJOR_VERSION > 2 + return module; +#else + return; +#endif } diff -Nru psycopg2-2.0.13/psycopg/_psycopg.vc9.amd64.manifest psycopg2-2.4.5/psycopg/_psycopg.vc9.amd64.manifest --- psycopg2-2.0.13/psycopg/_psycopg.vc9.amd64.manifest 1970-01-01 00:00:00.000000000 +0000 +++ psycopg2-2.4.5/psycopg/_psycopg.vc9.amd64.manifest 2011-02-13 11:28:18.000000000 +0000 @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff -Nru psycopg2-2.0.13/psycopg/_psycopg.vc9.x86.manifest psycopg2-2.4.5/psycopg/_psycopg.vc9.x86.manifest --- psycopg2-2.0.13/psycopg/_psycopg.vc9.x86.manifest 1970-01-01 00:00:00.000000000 +0000 +++ psycopg2-2.4.5/psycopg/_psycopg.vc9.x86.manifest 2011-02-13 11:28:18.000000000 +0000 @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff -Nru psycopg2-2.0.13/psycopg/python.h psycopg2-2.4.5/psycopg/python.h --- psycopg2-2.0.13/psycopg/python.h 2009-10-04 21:32:35.000000000 +0000 +++ psycopg2-2.4.5/psycopg/python.h 2011-06-13 16:53:48.000000000 +0000 @@ -1,30 +1,35 @@ /* python.h - python version compatibility stuff * - * Copyright (C) 2003 Federico Di Gregorio + * Copyright (C) 2003-2010 Federico Di Gregorio * * This file is part of psycopg. * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2, - * or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * psycopg2 is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * In addition, as a special exception, the copyright holders give + * permission to link this program with the OpenSSL library (or with + * modified versions of OpenSSL that use the same license as OpenSSL), + * and distribute linked combinations including the two. + * + * You must obey the GNU Lesser General Public License in all respects for + * all of the code used other than OpenSSL. + * + * psycopg2 is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. */ #ifndef PSYCOPG_PYTHON_H #define PSYCOPG_PYTHON_H 1 -#define PY_SSIZE_T_CLEAN -#include #include +#if PY_MAJOR_VERSION < 3 +#include +#endif #if PY_VERSION_HEX < 0x02040000 # error "psycopg requires Python >= 2.4" @@ -35,7 +40,10 @@ #define PY_SSIZE_T_MIN INT_MIN #define PY_SSIZE_T_MAX INT_MAX #define PY_FORMAT_SIZE_T "" + #define PyInt_FromSsize_t(x) PyInt_FromLong((x)) + #define lenfunc inquiry + #define ssizeargfunc intargfunc #define readbufferproc getreadbufferproc #define writebufferproc getwritebufferproc #define segcountproc getsegcountproc @@ -46,6 +54,28 @@ #define CONV_CODE_PY_SSIZE_T "n" #endif +/* hash() return size changed around version 3.2a4 on 64bit platforms. Before + * this, the return size was always a long, regardless of arch. ~3.2 + * introduced the Py_hash_t & Py_uhash_t typedefs with the resulting sizes + * based upon arch. */ +#if PY_VERSION_HEX < 0x030200A4 +typedef long Py_hash_t; +typedef unsigned long Py_uhash_t; +#endif + +/* Macros defined in Python 2.6 */ +#ifndef Py_REFCNT +#define Py_REFCNT(ob) (((PyObject*)(ob))->ob_refcnt) +#define Py_TYPE(ob) (((PyObject*)(ob))->ob_type) +#define Py_SIZE(ob) (((PyVarObject*)(ob))->ob_size) +#define PyVarObject_HEAD_INIT(x,n) PyObject_HEAD_INIT(x) n, +#endif + +/* Missing at least in Python 2.4 */ +#ifndef Py_MEMCPY +#define Py_MEMCPY memcpy +#endif + /* FORMAT_CODE_PY_SSIZE_T is for Py_ssize_t: */ #define FORMAT_CODE_PY_SSIZE_T "%" PY_FORMAT_SIZE_T "d" @@ -58,4 +88,76 @@ #define FORMAT_CODE_SIZE_T "%zu" #endif +/* Abstract from text type. Only supported for ASCII and UTF-8 */ +#if PY_MAJOR_VERSION < 3 +#define Text_Type PyString_Type +#define Text_Check(s) PyString_Check(s) +#define Text_Format(f,a) PyString_Format(f,a) +#define Text_FromUTF8(s) PyString_FromString(s) +#define Text_FromUTF8AndSize(s,n) PyString_FromStringAndSize(s,n) +#else +#define Text_Type PyUnicode_Type +#define Text_Check(s) PyUnicode_Check(s) +#define Text_Format(f,a) PyUnicode_Format(f,a) +#define Text_FromUTF8(s) PyUnicode_FromString(s) +#define Text_FromUTF8AndSize(s,n) PyUnicode_FromStringAndSize(s,n) +#endif + +#if PY_MAJOR_VERSION > 2 +#define PyInt_Type PyLong_Type +#define PyInt_Check PyLong_Check +#define PyInt_AsLong PyLong_AsLong +#define PyInt_FromLong PyLong_FromLong +#define PyInt_FromSsize_t PyLong_FromSsize_t +#define PyString_FromFormat PyUnicode_FromFormat +#define Py_TPFLAGS_HAVE_ITER 0L +#define Py_TPFLAGS_HAVE_RICHCOMPARE 0L +#define Py_TPFLAGS_HAVE_WEAKREFS 0L +#ifndef PyNumber_Int +#define PyNumber_Int PyNumber_Long +#endif +#endif /* PY_MAJOR_VERSION > 2 */ + +#if PY_MAJOR_VERSION < 3 +#define Bytes_Type PyString_Type +#define Bytes_Check PyString_Check +#define Bytes_CheckExact PyString_CheckExact +#define Bytes_AS_STRING PyString_AS_STRING +#define Bytes_GET_SIZE PyString_GET_SIZE +#define Bytes_Size PyString_Size +#define Bytes_AsString PyString_AsString +#define Bytes_AsStringAndSize PyString_AsStringAndSize +#define Bytes_FromString PyString_FromString +#define Bytes_FromStringAndSize PyString_FromStringAndSize +#define Bytes_FromFormat PyString_FromFormat +#define Bytes_ConcatAndDel PyString_ConcatAndDel +#define _Bytes_Resize _PyString_Resize + +#else + +#define Bytes_Type PyBytes_Type +#define Bytes_Check PyBytes_Check +#define Bytes_CheckExact PyBytes_CheckExact +#define Bytes_AS_STRING PyBytes_AS_STRING +#define Bytes_GET_SIZE PyBytes_GET_SIZE +#define Bytes_Size PyBytes_Size +#define Bytes_AsString PyBytes_AsString +#define Bytes_AsStringAndSize PyBytes_AsStringAndSize +#define Bytes_FromString PyBytes_FromString +#define Bytes_FromStringAndSize PyBytes_FromStringAndSize +#define Bytes_FromFormat PyBytes_FromFormat +#define Bytes_ConcatAndDel PyBytes_ConcatAndDel +#define _Bytes_Resize _PyBytes_Resize + +#endif + +HIDDEN PyObject *Bytes_Format(PyObject *format, PyObject *args); + +/* Mangle the module name into the name of the module init function */ +#if PY_MAJOR_VERSION > 2 +#define INIT_MODULE(m) PyInit_ ## m +#else +#define INIT_MODULE(m) init ## m +#endif + #endif /* !defined(PSYCOPG_PYTHON_H) */ diff -Nru psycopg2-2.0.13/psycopg/typecast_array.c psycopg2-2.4.5/psycopg/typecast_array.c --- psycopg2-2.0.13/psycopg/typecast_array.c 2009-04-26 12:09:21.000000000 +0000 +++ psycopg2-2.4.5/psycopg/typecast_array.c 2012-03-28 21:09:15.000000000 +0000 @@ -1,22 +1,26 @@ /* typecast_array.c - array typecasters * - * Copyright (C) 2005 Federico Di Gregorio + * Copyright (C) 2005-2010 Federico Di Gregorio * - * This file is part of the psycopg module. + * This file is part of psycopg. * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2, - * or (at your option) any later version. + * psycopg2 is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * In addition, as a special exception, the copyright holders give + * permission to link this program with the OpenSSL library (or with + * modified versions of OpenSSL that use the same license as OpenSSL), + * and distribute linked combinations including the two. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You must obey the GNU Lesser General Public License in all respects for + * all of the code used other than OpenSSL. + * + * psycopg2 is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. */ #define MAX_DIMENSIONS 16 @@ -129,16 +133,18 @@ } if (res == ASCAN_QUOTED) { - Py_ssize_t j; + const char *j, *jj; char *buffer = PyMem_Malloc(l+1); - if (buffer == NULL) return ASCAN_ERROR; + if (buffer == NULL) { + PyErr_NoMemory(); + return ASCAN_ERROR; + } *token = buffer; - for (j = *pos; j < *pos+l; j++) { - if (str[j] != '\\' - || (j > *pos && str[j-1] == '\\')) - *(buffer++) = str[j]; + for (j = str + *pos, jj = j + l; j < jj; ++j) { + if (*j == '\\') { ++j; } + *(buffer++) = *j; } *buffer = '\0'; @@ -160,7 +166,7 @@ return res; } -static int +RAISES_NEG static int typecast_array_scan(const char *str, Py_ssize_t strlength, PyObject *curs, PyObject *base, PyObject *array) { @@ -193,7 +199,7 @@ /* before anything else we free the memory */ if (state == ASCAN_QUOTED) PyMem_Free(token); - if (obj == NULL) return 0; + if (obj == NULL) return -1; PyList_Append(array, obj); Py_DECREF(obj); @@ -201,25 +207,25 @@ else if (state == ASCAN_BEGIN) { PyObject *sub = PyList_New(0); - if (sub == NULL) return 0; + if (sub == NULL) return -1; PyList_Append(array, sub); Py_DECREF(sub); if (stack_index == MAX_DIMENSIONS) - return 0; + return -1; stack[stack_index++] = array; array = sub; } else if (state == ASCAN_ERROR) { - return 0; + return -1; } else if (state == ASCAN_END) { if (--stack_index < 0) - return 0; + return -1; array = stack[stack_index]; } @@ -227,7 +233,7 @@ break; } - return 1; + return 0; } @@ -254,12 +260,11 @@ Dprintf("typecast_GENERIC_ARRAY_cast: str = '%s'," " len = " FORMAT_CODE_PY_SSIZE_T, str, len); - obj = PyList_New(0); + if (!(obj = PyList_New(0))) { return NULL; } /* scan the array skipping the first level of {} */ - if (typecast_array_scan(&str[1], len-2, curs, base, obj) == 0) { - Py_DECREF(obj); - obj = NULL; + if (typecast_array_scan(&str[1], len-2, curs, base, obj) < 0) { + Py_CLEAR(obj); } return obj; diff -Nru psycopg2-2.0.13/psycopg/typecast_basic.c psycopg2-2.4.5/psycopg/typecast_basic.c --- psycopg2-2.0.13/psycopg/typecast_basic.c 2009-08-09 12:51:14.000000000 +0000 +++ psycopg2-2.4.5/psycopg/typecast_basic.c 2012-03-28 21:09:15.000000000 +0000 @@ -1,26 +1,31 @@ /* pgcasts_basic.c - basic typecasting functions to python types * - * Copyright (C) 2001-2003 Federico Di Gregorio + * Copyright (C) 2001-2010 Federico Di Gregorio * - * This file is part of the psycopg module. + * This file is part of psycopg. * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2, - * or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * psycopg2 is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * In addition, as a special exception, the copyright holders give + * permission to link this program with the OpenSSL library (or with + * modified versions of OpenSSL that use the same license as OpenSSL), + * and distribute linked combinations including the two. + * + * You must obey the GNU Lesser General Public License in all respects for + * all of the code used other than OpenSSL. + * + * psycopg2 is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. */ /** INTEGER - cast normal integers (4 bytes) to python int **/ +#if PY_MAJOR_VERSION < 3 static PyObject * typecast_INTEGER_cast(const char *s, Py_ssize_t len, PyObject *curs) { @@ -33,6 +38,9 @@ } return PyInt_FromString((char *)s, NULL, 0); } +#else +#define typecast_INTEGER_cast typecast_LONGINTEGER_cast +#endif /** LONGINTEGER - cast long integers (8 bytes) to python long **/ @@ -55,44 +63,42 @@ typecast_FLOAT_cast(const char *s, Py_ssize_t len, PyObject *curs) { PyObject *str = NULL, *flo = NULL; - char *pend; if (s == NULL) {Py_INCREF(Py_None); return Py_None;} - str = PyString_FromStringAndSize(s, len); - flo = PyFloat_FromString(str, &pend); + if (!(str = Text_FromUTF8AndSize(s, len))) { return NULL; } +#if PY_MAJOR_VERSION < 3 + flo = PyFloat_FromString(str, NULL); +#else + flo = PyFloat_FromString(str); +#endif Py_DECREF(str); return flo; } /** STRING - cast strings of any type to python string **/ +#if PY_MAJOR_VERSION < 3 static PyObject * typecast_STRING_cast(const char *s, Py_ssize_t len, PyObject *curs) { if (s == NULL) {Py_INCREF(Py_None); return Py_None;} return PyString_FromStringAndSize(s, len); } +#else +#define typecast_STRING_cast typecast_UNICODE_cast +#endif /** UNICODE - cast strings of any type to a python unicode object **/ static PyObject * typecast_UNICODE_cast(const char *s, Py_ssize_t len, PyObject *curs) { - PyObject *enc; + char *enc; if (s == NULL) {Py_INCREF(Py_None); return Py_None;} - enc = PyDict_GetItemString(psycoEncodings, - ((cursorObject*)curs)->conn->encoding); - if (enc) { - return PyUnicode_Decode(s, len, PyString_AsString(enc), NULL); - } - else { - PyErr_Format(InterfaceError, - "can't decode into unicode string from %s", - ((cursorObject*)curs)->conn->encoding); - return NULL; - } + enc = ((cursorObject*)curs)->conn->codec; + return PyUnicode_Decode(s, len, enc, NULL); } /** BOOLEAN - cast boolean value into right python object **/ @@ -128,8 +134,14 @@ return PyErr_NoMemory(); strncpy(buffer, s, (size_t) len); buffer[len] = '\0'; decimalType = psyco_GetDecimalType(); - res = PyObject_CallFunction(decimalType, "s", buffer); - Py_DECREF(decimalType); + /* Fall back on float if decimal is not available */ + if (decimalType != NULL) { + res = PyObject_CallFunction(decimalType, "s", buffer); + Py_DECREF(decimalType); + } + else { + res = PyObject_CallFunction((PyObject*)&PyFloat_Type, "s", buffer); + } PyMem_Free(buffer); return res; diff -Nru psycopg2-2.0.13/psycopg/typecast_binary.c psycopg2-2.4.5/psycopg/typecast_binary.c --- psycopg2-2.0.13/psycopg/typecast_binary.c 2009-08-09 12:51:14.000000000 +0000 +++ psycopg2-2.4.5/psycopg/typecast_binary.c 2012-03-28 21:09:15.000000000 +0000 @@ -1,27 +1,30 @@ /* typecast_binary.c - binary typecasting functions to python types * - * Copyright (C) 2001-2003 Federico Di Gregorio + * Copyright (C) 2001-2010 Federico Di Gregorio * - * This file is part of the psycopg module. + * This file is part of psycopg. * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2, - * or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * psycopg2 is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * In addition, as a special exception, the copyright holders give + * permission to link this program with the OpenSSL library (or with + * modified versions of OpenSSL that use the same license as OpenSSL), + * and distribute linked combinations including the two. + * + * You must obey the GNU Lesser General Public License in all respects for + * all of the code used other than OpenSSL. + * + * psycopg2 is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. */ #include "typecast_binary.h" -#include #include @@ -37,8 +40,8 @@ FORMAT_CODE_PY_SSIZE_T, self->base, self->len ); - free(self->base); - self->ob_type->tp_free((PyObject *) self); + PyMem_Free(self->base); + Py_TYPE(self)->tp_free((PyObject *)self); } static PyObject * @@ -50,6 +53,9 @@ ); } +#if PY_MAJOR_VERSION < 3 + +/* XXX support 3.0 buffer protocol */ static Py_ssize_t chunk_getreadbuffer(chunkObject *self, Py_ssize_t segment, void **ptr) { @@ -79,11 +85,26 @@ (charbufferproc) NULL }; +#else + +/* 3.0 buffer interface */ +int chunk_getbuffer(PyObject *_self, Py_buffer *view, int flags) +{ + chunkObject *self = (chunkObject*)_self; + return PyBuffer_FillInfo(view, _self, self->base, self->len, 1, flags); +} +static PyBufferProcs chunk_as_buffer = +{ + chunk_getbuffer, + NULL, +}; + +#endif + #define chunk_doc "memory chunk" PyTypeObject chunkType = { - PyObject_HEAD_INIT(NULL) - 0, /* ob_size */ + PyVarObject_HEAD_INIT(NULL, 0) "psycopg2._psycopg.chunk", /* tp_name */ sizeof(chunkObject), /* tp_basicsize */ 0, /* tp_itemsize */ @@ -106,128 +127,185 @@ chunk_doc /* tp_doc */ }; -/* the function typecast_BINARY_cast_unescape is used when libpq does not - provide PQunescapeBytea: it convert all the \xxx octal sequences to the - proper byte value */ - -#ifdef PSYCOPG_OWN_QUOTING -static unsigned char * -typecast_BINARY_cast_unescape(unsigned char *str, size_t *to_length) -{ - char *dstptr, *dststr; - int len, i; - - len = strlen(str); - dststr = (char*)calloc(len, sizeof(char)); - dstptr = dststr; - - if (dststr == NULL) return NULL; - - Py_BEGIN_ALLOW_THREADS; - - for (i = 0; i < len; i++) { - if (str[i] == '\\') { - if ( ++i < len) { - if (str[i] == '\\') { - *dstptr = '\\'; - } - else { - *dstptr = 0; - *dstptr |= (str[i++] & 7) << 6; - *dstptr |= (str[i++] & 7) << 3; - *dstptr |= (str[i] & 7); - } - } + +static char *psycopg_parse_hex( + const char *bufin, Py_ssize_t sizein, Py_ssize_t *sizeout); +static char *psycopg_parse_escape( + const char *bufin, Py_ssize_t sizein, Py_ssize_t *sizeout); + +/* The function is not static and not hidden as we use ctypes to test it. */ +PyObject * +typecast_BINARY_cast(const char *s, Py_ssize_t l, PyObject *curs) +{ + chunkObject *chunk = NULL; + PyObject *res = NULL; + char *buffer = NULL; + Py_ssize_t len; + + if (s == NULL) {Py_INCREF(Py_None); return Py_None;} + + if (s[0] == '\\' && s[1] == 'x') { + /* This is a buffer escaped in hex format: libpq before 9.0 can't + * parse it and we can't detect reliably the libpq version at runtime. + * So the only robust option is to parse it ourselves - luckily it's + * an easy format. + */ + if (NULL == (buffer = psycopg_parse_hex(s, l, &len))) { + goto exit; } - else { - *dstptr = str[i]; + } + else { + /* This is a buffer in the classic bytea format. So we can handle it + * to the PQunescapeBytea to have it parsed, rignt? ...Wrong. We + * could, but then we'd have to record whether buffer was allocated by + * Python or by the libpq to dispose it properly. Furthermore the + * PQunescapeBytea interface is not the most brilliant as it wants a + * null-terminated string even if we have known its length thus + * requiring a useless memcpy and strlen. + * So we'll just have our better integrated parser, let's finish this + * story. + */ + if (NULL == (buffer = psycopg_parse_escape(s, l, &len))) { + goto exit; } - dstptr++; } - Py_END_ALLOW_THREADS; + chunk = (chunkObject *) PyObject_New(chunkObject, &chunkType); + if (chunk == NULL) goto exit; - *to_length = (size_t)(dstptr-dststr); + /* **Transfer** ownership of buffer's memory to the chunkObject: */ + chunk->base = buffer; + buffer = NULL; + chunk->len = (Py_ssize_t)len; - return dststr; +#if PY_MAJOR_VERSION < 3 + if ((res = PyBuffer_FromObject((PyObject *)chunk, 0, chunk->len)) == NULL) + goto exit; +#else + if ((res = PyMemoryView_FromObject((PyObject*)chunk)) == NULL) + goto exit; +#endif + +exit: + Py_XDECREF((PyObject *)chunk); + PyMem_Free(buffer); + + return res; } -#define PQunescapeBytea typecast_BINARY_cast_unescape -#endif -static PyObject * -typecast_BINARY_cast(const char *s, Py_ssize_t l, PyObject *curs) +static const char hex_lut[128] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, + -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +}; + +/* Parse a bytea output buffer encoded in 'hex' format. + * + * the format is described in + * http://www.postgresql.org/docs/current/static/datatype-binary.html + * + * Parse the buffer in 'bufin', whose length is 'sizein'. + * Return a new buffer allocated by PyMem_Malloc and set 'sizeout' to its size. + * In case of error set an exception and return NULL. + */ +static char * +psycopg_parse_hex(const char *bufin, Py_ssize_t sizein, Py_ssize_t *sizeout) { - chunkObject *chunk = NULL; - PyObject *res = NULL; - char *str = NULL, *buffer = NULL; - size_t len; + char *ret = NULL; + const char *bufend = bufin + sizein; + const char *pi = bufin + 2; /* past the \x */ + char *bufout; + char *po; + + po = bufout = PyMem_Malloc((sizein - 2) >> 1); /* output size upper bound */ + if (NULL == bufout) { + PyErr_NoMemory(); + goto exit; + } - if (s == NULL) {Py_INCREF(Py_None); return Py_None;} + /* Implementation note: we call this function upon database response, not + * user input (because we are parsing the output format of a buffer) so we + * don't expect errors. On bad input we reserve the right to return a bad + * output, not an error. + */ + while (pi < bufend) { + char c; + while (-1 == (c = hex_lut[*pi++ & '\x7f'])) { + if (pi >= bufend) { goto endloop; } + } + *po = c << 4; - /* PQunescapeBytea absolutely wants a 0-terminated string and we don't - want to copy the whole buffer, right? Wrong, but there isn't any other - way */ - if (s[l] != '\0') { - if ((buffer = PyMem_Malloc(l+1)) == NULL) { - PyErr_NoMemory(); - goto fail; - } - /* Py_ssize_t->size_t cast is safe, as long as the Py_ssize_t is - * >= 0: */ - assert (l >= 0); - strncpy(buffer, s, (size_t) l); - - buffer[l] = '\0'; - s = buffer; - } - str = (char*)PQunescapeBytea((unsigned char*)s, &len); - Dprintf("typecast_BINARY_cast: unescaped " FORMAT_CODE_SIZE_T " bytes", - len); - - /* The type of the second parameter to PQunescapeBytea is size_t *, so it's - * possible (especially with Python < 2.5) to get a return value too large - * to fit into a Python container. */ - if (len > (size_t) PY_SSIZE_T_MAX) { - PyErr_SetString(PyExc_IndexError, "PG buffer too large to fit in Python" - " buffer."); - goto fail; + while (-1 == (c = hex_lut[*pi++ & '\x7f'])) { + if (pi >= bufend) { goto endloop; } + } + *po++ |= c; } +endloop: - chunk = (chunkObject *) PyObject_New(chunkObject, &chunkType); - if (chunk == NULL) goto fail; + ret = bufout; + *sizeout = po - bufout; - /* **Transfer** ownership of str's memory to the chunkObject: */ - chunk->base = str; - str = NULL; +exit: + return ret; +} - /* size_t->Py_ssize_t cast was validated above: */ - chunk->len = (Py_ssize_t) len; - if ((res = PyBuffer_FromObject((PyObject *)chunk, 0, chunk->len)) == NULL) - goto fail; - /* PyBuffer_FromObject() created a new reference. We'll release our - * reference held in 'chunk' in the 'cleanup' clause. */ - - goto cleanup; - fail: - assert (PyErr_Occurred()); - if (res != NULL) { - Py_DECREF(res); - res = NULL; - } - /* Fall through to cleanup: */ - cleanup: - if (chunk != NULL) { - Py_DECREF((PyObject *) chunk); - } - if (str != NULL) { - /* str's mem was allocated by PQunescapeBytea; must use free: */ - free(str); - } - if (buffer != NULL) { - /* We allocated buffer with PyMem_Malloc; must use PyMem_Free: */ - PyMem_Free(buffer); - } +/* Parse a bytea output buffer encoded in 'escape' format. + * + * the format is described in + * http://www.postgresql.org/docs/current/static/datatype-binary.html + * + * Parse the buffer in 'bufin', whose length is 'sizein'. + * Return a new buffer allocated by PyMem_Malloc and set 'sizeout' to its size. + * In case of error set an exception and return NULL. + */ +static char * +psycopg_parse_escape(const char *bufin, Py_ssize_t sizein, Py_ssize_t *sizeout) +{ + char *ret = NULL; + const char *bufend = bufin + sizein; + const char *pi = bufin; + char *bufout; + char *po; + + po = bufout = PyMem_Malloc(sizein); /* output size upper bound */ + if (NULL == bufout) { + PyErr_NoMemory(); + goto exit; + } + + while (pi < bufend) { + if (*pi != '\\') { + /* Unescaped char */ + *po++ = *pi++; + continue; + } + if ((pi[1] >= '0' && pi[1] <= '3') && + (pi[2] >= '0' && pi[2] <= '7') && + (pi[3] >= '0' && pi[3] <= '7')) + { + /* Escaped octal value */ + *po++ = ((pi[1] - '0') << 6) | + ((pi[2] - '0') << 3) | + ((pi[3] - '0')); + pi += 4; + } + else { + /* Escaped char */ + *po++ = pi[1]; + pi += 2; + } + } + + ret = bufout; + *sizeout = po - bufout; - return res; +exit: + return ret; } + diff -Nru psycopg2-2.0.13/psycopg/typecast_binary.h psycopg2-2.4.5/psycopg/typecast_binary.h --- psycopg2-2.0.13/psycopg/typecast_binary.h 2009-04-26 12:09:21.000000000 +0000 +++ psycopg2-2.4.5/psycopg/typecast_binary.h 2011-02-06 15:58:34.000000000 +0000 @@ -1,32 +1,31 @@ /* typecast_binary.h - definitions for binary typecaster * - * Copyright (C) 2003 Federico Di Gregorio + * Copyright (C) 2003-2010 Federico Di Gregorio * * This file is part of psycopg. * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2, - * or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * psycopg2 is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * In addition, as a special exception, the copyright holders give + * permission to link this program with the OpenSSL library (or with + * modified versions of OpenSSL that use the same license as OpenSSL), + * and distribute linked combinations including the two. + * + * You must obey the GNU Lesser General Public License in all respects for + * all of the code used other than OpenSSL. + * + * psycopg2 is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. */ #ifndef PSYCOPG_TYPECAST_BINARY_H #define PSYCOPG_TYPECAST_BINARY_H 1 -#define PY_SSIZE_T_CLEAN -#include - -#include "psycopg/config.h" - #ifdef __cplusplus extern "C" { #endif diff -Nru psycopg2-2.0.13/psycopg/typecast_builtins.c psycopg2-2.4.5/psycopg/typecast_builtins.c --- psycopg2-2.0.13/psycopg/typecast_builtins.c 2009-04-26 12:09:21.000000000 +0000 +++ psycopg2-2.4.5/psycopg/typecast_builtins.c 2011-02-27 12:03:48.000000000 +0000 @@ -25,6 +25,7 @@ static long int typecast_INTERVALARRAY_types[] = {1187, 0}; static long int typecast_BINARYARRAY_types[] = {1001, 0}; static long int typecast_ROWIDARRAY_types[] = {1028, 1013, 0}; +static long int typecast_UNKNOWN_types[] = {705, 0}; static typecastObject_initlist typecast_builtins[] = { @@ -55,6 +56,7 @@ {"INTERVALARRAY", typecast_INTERVALARRAY_types, typecast_INTERVALARRAY_cast, "INTERVAL"}, {"BINARYARRAY", typecast_BINARYARRAY_types, typecast_BINARYARRAY_cast, "BINARY"}, {"ROWIDARRAY", typecast_ROWIDARRAY_types, typecast_ROWIDARRAY_cast, "ROWID"}, + {"UNKNOWN", typecast_UNKNOWN_types, typecast_UNKNOWN_cast, NULL}, {NULL, NULL, NULL, NULL} }; diff -Nru psycopg2-2.0.13/psycopg/typecast.c psycopg2-2.4.5/psycopg/typecast.c --- psycopg2-2.0.13/psycopg/typecast.c 2009-08-09 12:51:14.000000000 +0000 +++ psycopg2-2.4.5/psycopg/typecast.c 2012-03-28 21:09:15.000000000 +0000 @@ -1,43 +1,44 @@ /* typecast.c - basic utility functions related to typecasting * - * Copyright (C) 2003 Federico Di Gregorio + * Copyright (C) 2003-2010 Federico Di Gregorio * * This file is part of psycopg. * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2, - * or (at your option) any later version. + * psycopg2 is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * In addition, as a special exception, the copyright holders give + * permission to link this program with the OpenSSL library (or with + * modified versions of OpenSSL that use the same license as OpenSSL), + * and distribute linked combinations including the two. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You must obey the GNU Lesser General Public License in all respects for + * all of the code used other than OpenSSL. + * + * psycopg2 is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. */ -#define PY_SSIZE_T_CLEAN -#include -#include - #define PSYCOPG_MODULE -#include "psycopg/config.h" #include "psycopg/psycopg.h" -#include "psycopg/python.h" + #include "psycopg/typecast.h" #include "psycopg/cursor.h" -/* usefull function used by some typecasters */ +/* useful function used by some typecasters */ +#ifdef HAVE_MXDATETIME static const char * skip_until_space(const char *s) { while (*s && *s != ' ') s++; return s; } +#endif static const char * skip_until_space2(const char *s, Py_ssize_t *len) @@ -176,8 +177,34 @@ #endif #include "psycopg/typecast_array.c" + +static long int typecast_default_DEFAULT[] = {0}; +static typecastObject_initlist typecast_default = { + "DEFAULT", typecast_default_DEFAULT, typecast_STRING_cast}; + +static PyObject * +typecast_UNKNOWN_cast(const char *str, Py_ssize_t len, PyObject *curs) +{ + Dprintf("typecast_UNKNOWN_cast: str = '%s'," + " len = " FORMAT_CODE_PY_SSIZE_T, str, len); + + // PostgreSQL returns {} for empty array without explicit type. We convert + // that to list in order to handle empty lists. + if (len == 2 && str[0] == '{' && str[1] == '}') { + return PyList_New(0); + } + + Dprintf("typecast_UNKNOWN_cast: fallback to default cast"); + + return typecast_default.cast(str, len, curs); +} + #include "psycopg/typecast_builtins.c" +#define typecast_PYDATETIMEARRAY_cast typecast_GENERIC_ARRAY_cast +#define typecast_PYDATEARRAY_cast typecast_GENERIC_ARRAY_cast +#define typecast_PYTIMEARRAY_cast typecast_GENERIC_ARRAY_cast +#define typecast_PYINTERVALARRAY_cast typecast_GENERIC_ARRAY_cast /* a list of initializers, used to make the typecasters accessible anyway */ static typecastObject_initlist typecast_pydatetime[] = { @@ -185,16 +212,29 @@ {"PYTIME", typecast_TIME_types, typecast_PYTIME_cast}, {"PYDATE", typecast_DATE_types, typecast_PYDATE_cast}, {"PYINTERVAL", typecast_INTERVAL_types, typecast_PYINTERVAL_cast}, + {"PYDATETIMEARRAY", typecast_DATETIMEARRAY_types, typecast_PYDATETIMEARRAY_cast, "PYDATETIME"}, + {"PYTIMEARRAY", typecast_TIMEARRAY_types, typecast_PYTIMEARRAY_cast, "PYTIME"}, + {"PYDATEARRAY", typecast_DATEARRAY_types, typecast_PYDATEARRAY_cast, "PYDATE"}, + {"PYINTERVALARRAY", typecast_INTERVALARRAY_types, typecast_PYINTERVALARRAY_cast, "PYINTERVAL"}, {NULL, NULL, NULL} }; -/* a list of initializers, used to make the typecasters accessible anyway */ #ifdef HAVE_MXDATETIME +#define typecast_MXDATETIMEARRAY_cast typecast_GENERIC_ARRAY_cast +#define typecast_MXDATEARRAY_cast typecast_GENERIC_ARRAY_cast +#define typecast_MXTIMEARRAY_cast typecast_GENERIC_ARRAY_cast +#define typecast_MXINTERVALARRAY_cast typecast_GENERIC_ARRAY_cast + +/* a list of initializers, used to make the typecasters accessible anyway */ static typecastObject_initlist typecast_mxdatetime[] = { {"MXDATETIME", typecast_DATETIME_types, typecast_MXDATE_cast}, {"MXTIME", typecast_TIME_types, typecast_MXTIME_cast}, {"MXDATE", typecast_DATE_types, typecast_MXDATE_cast}, {"MXINTERVAL", typecast_INTERVAL_types, typecast_MXINTERVAL_cast}, + {"MXDATETIMEARRAY", typecast_DATETIMEARRAY_types, typecast_MXDATETIMEARRAY_cast, "MXDATETIME"}, + {"MXTIMEARRAY", typecast_TIMEARRAY_types, typecast_MXTIMEARRAY_cast, "MXTIME"}, + {"MXDATEARRAY", typecast_DATEARRAY_types, typecast_MXDATEARRAY_cast, "MXDATE"}, + {"MXINTERVALARRAY", typecast_INTERVALARRAY_types, typecast_MXINTERVALARRAY_cast, "MXINTERVAL"}, {NULL, NULL, NULL} }; #endif @@ -207,41 +247,31 @@ PyObject *psyco_binary_types; PyObject *psyco_default_binary_cast; -static long int typecast_default_DEFAULT[] = {0}; -static typecastObject_initlist typecast_default = { - "DEFAULT", typecast_default_DEFAULT, typecast_STRING_cast}; - /* typecast_init - initialize the dictionary and create default types */ -int +RAISES_NEG int typecast_init(PyObject *dict) { int i; + int rv = -1; + typecastObject *t = NULL; /* create type dictionary and put it in module namespace */ - psyco_types = PyDict_New(); - psyco_binary_types = PyDict_New(); - - if (!psyco_types || !psyco_binary_types) { - Py_XDECREF(psyco_types); - Py_XDECREF(psyco_binary_types); - return -1; - } - + if (!(psyco_types = PyDict_New())) { goto exit; } PyDict_SetItemString(dict, "string_types", psyco_types); + + if (!(psyco_binary_types = PyDict_New())) { goto exit; } PyDict_SetItemString(dict, "binary_types", psyco_binary_types); /* insert the cast types into the 'types' dictionary and register them in the module dictionary */ for (i = 0; typecast_builtins[i].name != NULL; i++) { - typecastObject *t; - Dprintf("typecast_init: initializing %s", typecast_builtins[i].name); t = (typecastObject *)typecast_from_c(&(typecast_builtins[i]), dict); - if (t == NULL) return -1; - if (typecast_add((PyObject *)t, NULL, 0) != 0) return -1; + if (t == NULL) { goto exit; } + if (typecast_add((PyObject *)t, NULL, 0) < 0) { goto exit; } PyDict_SetItem(dict, t->name, (PyObject *)t); @@ -249,6 +279,8 @@ if (typecast_builtins[i].values == typecast_BINARY_types) { psyco_default_binary_cast = (PyObject *)t; } + Py_DECREF((PyObject *)t); + t = NULL; } /* create and save a default cast object (but does not register it) */ @@ -256,27 +288,37 @@ /* register the date/time typecasters with their original names */ #ifdef HAVE_MXDATETIME - for (i = 0; typecast_mxdatetime[i].name != NULL; i++) { - typecastObject *t; - Dprintf("typecast_init: initializing %s", typecast_mxdatetime[i].name); - t = (typecastObject *)typecast_from_c(&(typecast_mxdatetime[i]), dict); - if (t == NULL) return -1; - PyDict_SetItem(dict, t->name, (PyObject *)t); + if (0 == psyco_typecast_mxdatetime_init()) { + for (i = 0; typecast_mxdatetime[i].name != NULL; i++) { + Dprintf("typecast_init: initializing %s", typecast_mxdatetime[i].name); + t = (typecastObject *)typecast_from_c(&(typecast_mxdatetime[i]), dict); + if (t == NULL) { goto exit; } + PyDict_SetItem(dict, t->name, (PyObject *)t); + Py_DECREF((PyObject *)t); + t = NULL; + } } #endif + + if (0 > psyco_typecast_datetime_init()) { goto exit; } for (i = 0; typecast_pydatetime[i].name != NULL; i++) { - typecastObject *t; Dprintf("typecast_init: initializing %s", typecast_pydatetime[i].name); t = (typecastObject *)typecast_from_c(&(typecast_pydatetime[i]), dict); - if (t == NULL) return -1; + if (t == NULL) { goto exit; } PyDict_SetItem(dict, t->name, (PyObject *)t); + Py_DECREF((PyObject *)t); + t = NULL; } - return 0; + rv = 0; + +exit: + Py_XDECREF((PyObject *)t); + return rv; } /* typecast_add - add a type object to the dictionary */ -int +RAISES_NEG int typecast_add(PyObject *obj, PyObject *dict, int binary) { PyObject *val; @@ -286,7 +328,7 @@ Dprintf("typecast_add: object at %p, values refcnt = " FORMAT_CODE_PY_SSIZE_T, - obj, type->values->ob_refcnt + obj, Py_REFCNT(type->values) ); if (dict == NULL) @@ -367,8 +409,8 @@ } static struct PyMemberDef typecastObject_members[] = { - {"name", T_OBJECT, OFFSETOF(name), RO}, - {"values", T_OBJECT, OFFSETOF(values), RO}, + {"name", T_OBJECT, OFFSETOF(name), READONLY}, + {"values", T_OBJECT, OFFSETOF(values), READONLY}, {NULL} }; @@ -377,11 +419,14 @@ { typecastObject *self = (typecastObject*)obj; + PyObject_GC_UnTrack(self); + Py_CLEAR(self->values); Py_CLEAR(self->name); Py_CLEAR(self->pcast); + Py_CLEAR(self->bcast); - obj->ob_type->tp_free(obj); + Py_TYPE(obj)->tp_free(obj); } static int @@ -392,6 +437,7 @@ Py_VISIT(self->values); Py_VISIT(self->name); Py_VISIT(self->pcast); + Py_VISIT(self->bcast); return 0; } @@ -402,22 +448,46 @@ } static PyObject * +typecast_repr(PyObject *self) +{ + PyObject *name = ((typecastObject *)self)->name; + PyObject *rv; + + Py_INCREF(name); + if (!(name = psycopg_ensure_bytes(name))) { + return NULL; + } + + rv = PyString_FromFormat("<%s '%s' at %p>", + Py_TYPE(self)->tp_name, Bytes_AS_STRING(name), self); + + Py_DECREF(name); + return rv; +} + +static PyObject * typecast_call(PyObject *obj, PyObject *args, PyObject *kwargs) { - PyObject *string, *cursor; + const char *string; + Py_ssize_t length; + PyObject *cursor; - if (!PyArg_ParseTuple(args, "OO", &string, &cursor)) { + if (!PyArg_ParseTuple(args, "z#O", &string, &length, &cursor)) { return NULL; } - return typecast_cast(obj, - PyString_AsString(string), PyString_Size(string), - cursor); + // If the string is not a string but a None value we're being called + // from a Python-defined caster. + if (!string) { + Py_INCREF(Py_None); + return Py_None; + } + + return typecast_cast(obj, string, length, cursor); } PyTypeObject typecastType = { - PyObject_HEAD_INIT(NULL) - 0, + PyVarObject_HEAD_INIT(NULL, 0) "psycopg2._psycopg.type", sizeof(typecastObject), 0, @@ -427,7 +497,7 @@ 0, /*tp_getattr*/ 0, /*tp_setattr*/ typecast_cmp, /*tp_compare*/ - 0, /*tp_repr*/ + typecast_repr, /*tp_repr*/ 0, /*tp_as_number*/ 0, /*tp_as_sequence*/ 0, /*tp_as_mapping*/ @@ -485,7 +555,7 @@ if (obj == NULL) return NULL; Dprintf("typecast_new: new type at = %p, refcnt = " FORMAT_CODE_PY_SSIZE_T, - obj, obj->ob_refcnt); + obj, Py_REFCNT(obj)); Py_INCREF(values); obj->values = values; @@ -527,7 +597,7 @@ if (!PyArg_ParseTupleAndKeywords(args, keywds, "O!|O!OO", kwlist, &PyTuple_Type, &v, - &PyString_Type, &name, + &Text_Type, &name, &cast, &base)) { return NULL; } @@ -536,6 +606,29 @@ } PyObject * +typecast_array_from_python(PyObject *self, PyObject *args, PyObject *keywds) +{ + PyObject *values, *name = NULL, *base = NULL; + typecastObject *obj = NULL; + + static char *kwlist[] = {"values", "name", "baseobj", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, keywds, "O!O!O!", kwlist, + &PyTuple_Type, &values, + &Text_Type, &name, + &typecastType, &base)) { + return NULL; + } + + if ((obj = (typecastObject *)typecast_new(name, values, NULL, base))) { + obj->ccast = typecast_GENERIC_ARRAY_cast; + obj->pcast = NULL; + } + + return (PyObject *)obj; +} + +PyObject * typecast_from_c(typecastObject_initlist *type, PyObject *dict) { PyObject *name = NULL, *values = NULL, *base = NULL; @@ -552,7 +645,7 @@ } } - name = PyString_FromString(type->name); + name = Text_FromUTF8(type->name); if (!name) goto end; while (type->values[len] != 0) len++; @@ -583,7 +676,7 @@ PyObject *old, *res = NULL; typecastObject *self = (typecastObject *)obj; - /* we don't incref, the caster *can't* die at this point */ + Py_INCREF(obj); old = ((cursorObject*)curs)->caster; ((cursorObject*)curs)->caster = obj; @@ -591,13 +684,34 @@ res = self->ccast(str, len, curs); } else if (self->pcast) { - res = PyObject_CallFunction(self->pcast, "s#O", str, len, curs); + PyObject *s; + /* XXX we have bytes in the adapters and strings in the typecasters. + * are you sure this is ok? + * Notice that this way it is about impossible to create a python + * typecaster on a binary type. */ + if (str) { +#if PY_MAJOR_VERSION < 3 + s = PyString_FromStringAndSize(str, len); +#else + s = PyUnicode_Decode(str, len, + ((cursorObject *)curs)->conn->codec, NULL); +#endif + } + else { + Py_INCREF(Py_None); + s = Py_None; + } + if (s) { + res = PyObject_CallFunctionObjArgs(self->pcast, s, curs, NULL); + Py_DECREF(s); + } } else { PyErr_SetString(Error, "internal error: no casting function found"); } ((cursorObject*)curs)->caster = old; + Py_DECREF(obj); return res; } diff -Nru psycopg2-2.0.13/psycopg/typecast_datetime.c psycopg2-2.4.5/psycopg/typecast_datetime.c --- psycopg2-2.0.13/psycopg/typecast_datetime.c 2009-04-26 12:09:21.000000000 +0000 +++ psycopg2-2.4.5/psycopg/typecast_datetime.c 2012-03-28 21:09:15.000000000 +0000 @@ -1,35 +1,44 @@ /* typecast_datetime.c - date and time typecasting functions to python types * - * Copyright (C) 2001-2003 Federico Di Gregorio + * Copyright (C) 2001-2010 Federico Di Gregorio * - * This file is part of the psycopg module. + * This file is part of psycopg. * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2, - * or (at your option) any later version. + * psycopg2 is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * In addition, as a special exception, the copyright holders give + * permission to link this program with the OpenSSL library (or with + * modified versions of OpenSSL that use the same license as OpenSSL), + * and distribute linked combinations including the two. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You must obey the GNU Lesser General Public License in all respects for + * all of the code used other than OpenSSL. + * + * psycopg2 is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. */ #include #include "datetime.h" +RAISES_NEG static int +psyco_typecast_datetime_init(void) +{ + Dprintf("psyco_typecast_datetime_init: datetime init"); + + PyDateTime_IMPORT; -/* the pointer to the datetime module API is initialized by the module init - code, we just need to grab it */ -extern HIDDEN PyObject* pyDateTimeModuleP; -extern HIDDEN PyObject *pyDateTypeP; -extern HIDDEN PyObject *pyTimeTypeP; -extern HIDDEN PyObject *pyDateTimeTypeP; -extern HIDDEN PyObject *pyDeltaTypeP; + if (!PyDateTimeAPI) { + PyErr_SetString(PyExc_ImportError, "datetime initialization failed"); + return -1; + } + return 0; +} /** DATE - cast a date into a date python object **/ @@ -43,10 +52,12 @@ if (!strcmp(str, "infinity") || !strcmp(str, "-infinity")) { if (str[0] == '-') { - obj = PyObject_GetAttrString(pyDateTypeP, "min"); + obj = PyObject_GetAttrString( + (PyObject*)PyDateTimeAPI->DateType, "min"); } else { - obj = PyObject_GetAttrString(pyDateTypeP, "max"); + obj = PyObject_GetAttrString( + (PyObject*)PyDateTimeAPI->DateType, "max"); } } @@ -62,7 +73,8 @@ } else { if (y > 9999) y = 9999; - obj = PyObject_CallFunction(pyDateTypeP, "iii", y, m, d); + obj = PyObject_CallFunction( + (PyObject*)PyDateTimeAPI->DateType, "iii", y, m, d); } } return obj; @@ -85,10 +97,12 @@ /* check for infinity */ if (!strcmp(str, "infinity") || !strcmp(str, "-infinity")) { if (str[0] == '-') { - obj = PyObject_GetAttrString(pyDateTimeTypeP, "min"); + obj = PyObject_GetAttrString( + (PyObject*)PyDateTimeAPI->DateTimeType, "min"); } else { - obj = PyObject_GetAttrString(pyDateTimeTypeP, "max"); + obj = PyObject_GetAttrString( + (PyObject*)PyDateTimeAPI->DateTimeType, "max"); } } @@ -130,25 +144,22 @@ Dprintf("typecast_PYDATETIME_cast: UTC offset = %ds", tz); /* The datetime module requires that time zone offsets be - a whole number of minutes, so fail if we have a time - zone with a seconds offset. - */ - if (tz % 60 != 0) { - PyErr_Format(PyExc_ValueError, "time zone offset %d is not " - "a whole number of minutes", tz); - return NULL; - } - tzinfo = PyObject_CallFunction(tzinfo_factory, "i", tz / 60); + a whole number of minutes, so truncate the seconds to the + closest minute. */ + // printf("%d %d %d\n", tz, tzmin, round(tz / 60.0)); + tzinfo = PyObject_CallFunction(tzinfo_factory, "i", + (int)round(tz / 60.0)); } else { Py_INCREF(Py_None); tzinfo = Py_None; } if (tzinfo != NULL) { - obj = PyObject_CallFunction(pyDateTimeTypeP, "iiiiiiiO", - y, m, d, hh, mm, ss, us, tzinfo); + obj = PyObject_CallFunction( + (PyObject*)PyDateTimeAPI->DateTimeType, "iiiiiiiO", + y, m, d, hh, mm, ss, us, tzinfo); Dprintf("typecast_PYDATETIME_cast: tzinfo: %p, refcnt = " FORMAT_CODE_PY_SSIZE_T, - tzinfo, tzinfo->ob_refcnt + tzinfo, Py_REFCNT(tzinfo) ); Py_DECREF(tzinfo); } @@ -188,21 +199,16 @@ Dprintf("typecast_PYTIME_cast: UTC offset = %ds", tz); /* The datetime module requires that time zone offsets be - a whole number of minutes, so fail if we have a time - zone with a seconds offset. - */ - if (tz % 60 != 0) { - PyErr_Format(PyExc_ValueError, "time zone offset %d is not " - "a whole number of minutes", tz); - return NULL; - } - tzinfo = PyObject_CallFunction(tzinfo_factory, "i", tz / 60); + a whole number of minutes, so truncate the seconds to the + closest minute. */ + tzinfo = PyObject_CallFunction(tzinfo_factory, "i", + (int)round(tz / 60.0)); } else { Py_INCREF(Py_None); tzinfo = Py_None; } if (tzinfo != NULL) { - obj = PyObject_CallFunction(pyTimeTypeP, "iiiiO", + obj = PyObject_CallFunction((PyObject*)PyDateTimeAPI->TimeType, "iiiiO", hh, mm, ss, us, tzinfo); Py_DECREF(tzinfo); } @@ -233,7 +239,7 @@ case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': - v = v*10 + (double)*str - (double)'0'; + v = v * 10.0 + (double)(*str - '0'); if (part == 6){ denominator *= 10; } @@ -313,7 +319,7 @@ micro = (seconds - floor(seconds)) * 1000000.0; sec = (int)floor(seconds); - return PyObject_CallFunction(pyDeltaTypeP, "iii", + return PyObject_CallFunction((PyObject*)PyDateTimeAPI->DeltaType, "iii", days, sec, (int)round(micro)); } diff -Nru psycopg2-2.0.13/psycopg/typecast.h psycopg2-2.4.5/psycopg/typecast.h --- psycopg2-2.0.13/psycopg/typecast.h 2009-04-26 12:09:21.000000000 +0000 +++ psycopg2-2.4.5/psycopg/typecast.h 2012-03-28 21:09:15.000000000 +0000 @@ -1,32 +1,31 @@ /* typecast.h - definitions for typecasters * - * Copyright (C) 2003 Federico Di Gregorio + * Copyright (C) 2003-2010 Federico Di Gregorio * * This file is part of psycopg. * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2, - * or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * psycopg2 is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * In addition, as a special exception, the copyright holders give + * permission to link this program with the OpenSSL library (or with + * modified versions of OpenSSL that use the same license as OpenSSL), + * and distribute linked combinations including the two. + * + * You must obey the GNU Lesser General Public License in all respects for + * all of the code used other than OpenSSL. + * + * psycopg2 is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. */ #ifndef PSYCOPG_TYPECAST_H #define PSYCOPG_TYPECAST_H 1 -#define PY_SSIZE_T_CLEAN -#include - -#include "psycopg/config.h" - #ifdef __cplusplus extern "C" { #endif @@ -72,15 +71,17 @@ /** exported functions **/ /* used by module.c to init the type system and register types */ -HIDDEN int typecast_init(PyObject *dict); -HIDDEN int typecast_add(PyObject *obj, PyObject *dict, int binary); +RAISES_NEG HIDDEN int typecast_init(PyObject *dict); +RAISES_NEG HIDDEN int typecast_add(PyObject *obj, PyObject *dict, int binary); /* the C callable typecastObject creator function */ HIDDEN PyObject *typecast_from_c(typecastObject_initlist *type, PyObject *d); -/* the python callable typecast creator function */ +/* the python callable typecast creator functions */ HIDDEN PyObject *typecast_from_python( PyObject *self, PyObject *args, PyObject *keywds); +HIDDEN PyObject *typecast_array_from_python( + PyObject *self, PyObject *args, PyObject *keywds); /* the function used to dispatch typecasting calls */ HIDDEN PyObject *typecast_cast( diff -Nru psycopg2-2.0.13/psycopg/typecast_mxdatetime.c psycopg2-2.4.5/psycopg/typecast_mxdatetime.c --- psycopg2-2.0.13/psycopg/typecast_mxdatetime.c 2009-04-26 12:09:21.000000000 +0000 +++ psycopg2-2.4.5/psycopg/typecast_mxdatetime.c 2012-03-28 21:09:15.000000000 +0000 @@ -1,29 +1,46 @@ /* typecast_mxdatetime.c - date and time typecasting functions to mx types * - * Copyright (C) 2001-2003 Federico Di Gregorio + * Copyright (C) 2001-2010 Federico Di Gregorio * - * This file is part of the psycopg module. + * This file is part of psycopg. * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2, - * or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * psycopg2 is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * In addition, as a special exception, the copyright holders give + * permission to link this program with the OpenSSL library (or with + * modified versions of OpenSSL that use the same license as OpenSSL), + * and distribute linked combinations including the two. + * + * You must obey the GNU Lesser General Public License in all respects for + * all of the code used other than OpenSSL. + * + * psycopg2 is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. */ #include "mxDateTime.h" -/* the pointer to the mxDateTime API is initialized by the module init code, - we just need to grab it */ -extern HIDDEN mxDateTimeModule_APIObject *mxDateTimeP; + +/* Return 0 on success, -1 on failure, but don't set an exception */ + +static int +psyco_typecast_mxdatetime_init(void) +{ + Dprintf("psyco_typecast_mxdatetime_init: mx.DateTime init"); + + if (mxDateTime_ImportModuleAndAPI()) { + Dprintf("psyco_typecast_mxdatetime_init: mx.DateTime initialization failed"); + PyErr_Clear(); + return -1; + } + return 0; +} + /** DATE - cast a date into mx.DateTime python object **/ @@ -41,10 +58,10 @@ /* check for infinity */ if (!strcmp(str, "infinity") || !strcmp(str, "-infinity")) { if (str[0] == '-') { - return mxDateTimeP->DateTime_FromDateAndTime(-999998,1,1, 0,0,0); + return mxDateTime.DateTime_FromDateAndTime(-999998,1,1, 0,0,0); } else { - return mxDateTimeP->DateTime_FromDateAndTime(999999,12,31, 0,0,0); + return mxDateTime.DateTime_FromDateAndTime(999999,12,31, 0,0,0); } } @@ -71,7 +88,7 @@ Dprintf("typecast_MXDATE_cast: fractionary seconds: %lf", (double)ss + (double)us/(double)1000000.0); - return mxDateTimeP->DateTime_FromDateAndTime(y, m, d, hh, mm, + return mxDateTime.DateTime_FromDateAndTime(y, m, d, hh, mm, (double)ss + (double)us/(double)1000000.0); } @@ -98,7 +115,7 @@ Dprintf("typecast_MXTIME_cast: fractionary seconds: %lf", (double)ss + (double)us/(double)1000000.0); - return mxDateTimeP->DateTimeDelta_FromTime(hh, mm, + return mxDateTime.DateTimeDelta_FromTime(hh, mm, (double)ss + (double)us/(double)1000000.0); } @@ -125,7 +142,7 @@ case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': - v = v*10 + (double)*str - (double)'0'; + v = v * 10.0 + (double)(*str - '0'); Dprintf("typecast_MXINTERVAL_cast: v = %f", v); if (part == 6){ denominator *= 10; @@ -221,7 +238,7 @@ Dprintf("typecast_MXINTERVAL_cast: days = %ld, seconds = %f", days, seconds); - return mxDateTimeP->DateTimeDelta_FromDaysAndSeconds(days, seconds); + return mxDateTime.DateTimeDelta_FromDaysAndSeconds(days, seconds); } /* psycopg defaults to using mx types */ diff -Nru psycopg2-2.0.13/psycopg/utils.c psycopg2-2.4.5/psycopg/utils.c --- psycopg2-2.0.13/psycopg/utils.c 2009-04-26 12:09:21.000000000 +0000 +++ psycopg2-2.4.5/psycopg/utils.c 2012-03-28 21:09:15.000000000 +0000 @@ -1,15 +1,35 @@ /* utils.c - miscellaneous utility functions * + * Copyright (C) 2008-2010 Federico Di Gregorio + * + * This file is part of psycopg. + * + * psycopg2 is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * In addition, as a special exception, the copyright holders give + * permission to link this program with the OpenSSL library (or with + * modified versions of OpenSSL that use the same license as OpenSSL), + * and distribute linked combinations including the two. + * + * You must obey the GNU Lesser General Public License in all respects for + * all of the code used other than OpenSSL. + * + * psycopg2 is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. */ -#include -#include - -#include "psycopg/config.h" +#define PSYCOPG_MODULE #include "psycopg/psycopg.h" + #include "psycopg/connection.h" #include "psycopg/pgtypes.h" -#include "psycopg/pgversion.h" + +#include #include char * @@ -29,7 +49,6 @@ return NULL; } - #ifndef PSYCOPG_OWN_QUOTING { #if PG_VERSION_HEX >= 0x080104 int err; @@ -39,37 +58,6 @@ #endif ql = PQescapeString(to+eq+1, from, len); } - #else - { - int i, j; - - for (i=0, j=eq+1; itp_name); + Py_DECREF(obj); /* steal the ref anyway */ + } + + return rv; +} + +/* Take a Python object and return text from it. + * + * On Py3 this means converting bytes to unicode. On Py2 bytes are fine. + * + * The function is ref neutral: steals a ref from obj and adds one to the + * return value. It is safe to call it on NULL. + */ +STEALS(1) PyObject * +psycopg_ensure_text(PyObject *obj) +{ +#if PY_MAJOR_VERSION < 3 + return obj; +#else + if (obj) { + /* bytes to unicode in Py3 */ + PyObject *rv = PyUnicode_FromEncodedObject(obj, "utf8", "replace"); + Py_DECREF(obj); + return rv; + } + else { + return NULL; + } +#endif +} + +/* Check if a file derives from TextIOBase. + * + * Return 1 if it does, else 0, -1 on errors. + */ +int +psycopg_is_text_file(PyObject *f) +{ + /* NULL before any call. + * then io.TextIOBase if exists, else None. */ + static PyObject *base; + + /* Try to import os.TextIOBase */ + if (NULL == base) { + PyObject *m; + Dprintf("psycopg_is_text_file: importing io.TextIOBase"); + if (!(m = PyImport_ImportModule("io"))) { + Dprintf("psycopg_is_text_file: io module not found"); + PyErr_Clear(); + Py_INCREF(Py_None); + base = Py_None; + } + else { + if (!(base = PyObject_GetAttrString(m, "TextIOBase"))) { + Dprintf("psycopg_is_text_file: io.TextIOBase not found"); + PyErr_Clear(); + Py_INCREF(Py_None); + base = Py_None; + } + } + Py_XDECREF(m); + } + + if (base != Py_None) { + return PyObject_IsInstance(f, base); + } else { + return 0; + } +} + diff -Nru psycopg2-2.0.13/psycopg/xid.h psycopg2-2.4.5/psycopg/xid.h --- psycopg2-2.0.13/psycopg/xid.h 1970-01-01 00:00:00.000000000 +0000 +++ psycopg2-2.4.5/psycopg/xid.h 2011-02-06 15:58:34.000000000 +0000 @@ -0,0 +1,51 @@ +/* xid.h - definition for the psycopg Xid type + * + * Copyright (C) 2008 James Henstridge + * Copyright (C) 2010 Daniele Varrazzo + * + * This file is part of psycopg. + * + * psycopg2 is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * In addition, as a special exception, the copyright holders give + * permission to link this program with the OpenSSL library (or with + * modified versions of OpenSSL that use the same license as OpenSSL), + * and distribute linked combinations including the two. + * + * You must obey the GNU Lesser General Public License in all respects for + * all of the code used other than OpenSSL. + * + * psycopg2 is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + */ + +#ifndef PSYCOPG_XID_H +#define PSYCOPG_XID_H 1 + +extern HIDDEN PyTypeObject XidType; + +typedef struct { + PyObject_HEAD + + /* the Python-style three-part transaction ID */ + PyObject *format_id; + PyObject *gtrid; + PyObject *bqual; + + /* Additional information PostgreSQL exposes about prepared transactions */ + PyObject *prepared; + PyObject *owner; + PyObject *database; +} XidObject; + +HIDDEN XidObject *xid_ensure(PyObject *oxid); +HIDDEN XidObject *xid_from_string(PyObject *s); +HIDDEN PyObject *xid_get_tid(XidObject *self); +HIDDEN PyObject *xid_recover(PyObject *conn); + +#endif /* PSYCOPG_XID_H */ diff -Nru psycopg2-2.0.13/psycopg/xid_type.c psycopg2-2.4.5/psycopg/xid_type.c --- psycopg2-2.0.13/psycopg/xid_type.c 1970-01-01 00:00:00.000000000 +0000 +++ psycopg2-2.4.5/psycopg/xid_type.c 2012-03-28 21:09:15.000000000 +0000 @@ -0,0 +1,735 @@ +/* xid_type.c - python interface to Xid objects + * + * Copyright (C) 2008 Canonical Ltd. + * Copyright (C) 2010 Daniele Varrazzo + * + * This file is part of psycopg. + * + * psycopg2 is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * In addition, as a special exception, the copyright holders give + * permission to link this program with the OpenSSL library (or with + * modified versions of OpenSSL that use the same license as OpenSSL), + * and distribute linked combinations including the two. + * + * You must obey the GNU Lesser General Public License in all respects for + * all of the code used other than OpenSSL. + * + * psycopg2 is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + */ + +#define PSYCOPG_MODULE +#include "psycopg/psycopg.h" + +#include "psycopg/xid.h" + + +static const char xid_doc[] = + "A transaction identifier used for two-phase commit.\n\n" + "Usually returned by the connection methods `~connection.xid()` and\n" + "`~connection.tpc_recover()`.\n" + "`!Xid` instances can be unpacked as a 3-item tuples containing the items\n" + ":samp:`({format_id},{gtrid},{bqual})`.\n" + "The `!str()` of the object returns the *transaction ID* used\n" + "in the commands sent to the server.\n\n" + "See :ref:`tpc` for an introduction."; + +static const char format_id_doc[] = + "Format ID in a XA transaction.\n\n" + "A non-negative 32 bit integer.\n" + "`!None` if the transaction doesn't follow the XA standard."; + +static const char gtrid_doc[] = + "Global transaction ID in a XA transaction.\n\n" + "If the transaction doesn't follow the XA standard, it is the plain\n" + "*transaction ID* used in the server commands."; + +static const char bqual_doc[] = + "Branch qualifier of the transaction.\n\n" + "In a XA transaction every resource participating to a transaction\n" + "receives a distinct branch qualifier.\n" + "`!None` if the transaction doesn't follow the XA standard."; + +static const char prepared_doc[] = + "Timestamp (with timezone) in which a recovered transaction was prepared."; + +static const char owner_doc[] = + "Name of the user who prepared a recovered transaction."; + +static const char database_doc[] = + "Database the recovered transaction belongs to."; + +static PyMemberDef xid_members[] = { + { "format_id", T_OBJECT, offsetof(XidObject, format_id), READONLY, (char *)format_id_doc }, + { "gtrid", T_OBJECT, offsetof(XidObject, gtrid), READONLY, (char *)gtrid_doc }, + { "bqual", T_OBJECT, offsetof(XidObject, bqual), READONLY, (char *)bqual_doc }, + { "prepared", T_OBJECT, offsetof(XidObject, prepared), READONLY, (char *)prepared_doc }, + { "owner", T_OBJECT, offsetof(XidObject, owner), READONLY, (char *)owner_doc }, + { "database", T_OBJECT, offsetof(XidObject, database), READONLY, (char *)database_doc }, + { NULL } +}; + +static PyObject * +xid_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) +{ + XidObject *self; + + if (!(self = (XidObject *)type->tp_alloc(type, 0))) { return NULL; } + + Py_INCREF(Py_None); + self->format_id = Py_None; + Py_INCREF(Py_None); + self->gtrid = Py_None; + Py_INCREF(Py_None); + self->bqual = Py_None; + Py_INCREF(Py_None); + self->prepared = Py_None; + Py_INCREF(Py_None); + self->owner = Py_None; + Py_INCREF(Py_None); + self->database = Py_None; + + return (PyObject *)self; +} + +static int +xid_init(XidObject *self, PyObject *args, PyObject *kwargs) +{ + static char *kwlist[] = {"format_id", "gtrid", "bqual", NULL}; + int format_id; + size_t i, gtrid_len, bqual_len; + const char *gtrid, *bqual; + PyObject *tmp; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "iss", kwlist, + &format_id, >rid, &bqual)) + return -1; + + if (format_id < 0 || format_id > 0x7fffffff) { + PyErr_SetString(PyExc_ValueError, + "format_id must be a non-negative 32-bit integer"); + return -1; + } + + /* make sure that gtrid is no more than 64 characters long and + made of printable characters (which we're defining as those + between 0x20 and 0x7f). */ + gtrid_len = strlen(gtrid); + if (gtrid_len > 64) { + PyErr_SetString(PyExc_ValueError, + "gtrid must be a string no longer than 64 characters"); + return -1; + } + for (i = 0; i < gtrid_len; i++) { + if (gtrid[i] < 0x20 || gtrid[i] >= 0x7f) { + PyErr_SetString(PyExc_ValueError, + "gtrid must contain only printable characters."); + return -1; + } + } + /* Same for bqual */ + bqual_len = strlen(bqual); + if (bqual_len > 64) { + PyErr_SetString(PyExc_ValueError, + "bqual must be a string no longer than 64 characters"); + return -1; + } + for (i = 0; i < bqual_len; i++) { + if (bqual[i] < 0x20 || bqual[i] >= 0x7f) { + PyErr_SetString(PyExc_ValueError, + "bqual must contain only printable characters."); + return -1; + } + } + + tmp = self->format_id; + self->format_id = PyInt_FromLong(format_id); + Py_XDECREF(tmp); + + tmp = self->gtrid; + self->gtrid = Text_FromUTF8(gtrid); + Py_XDECREF(tmp); + + tmp = self->bqual; + self->bqual = Text_FromUTF8(bqual); + Py_XDECREF(tmp); + + return 0; +} + +static int +xid_traverse(XidObject *self, visitproc visit, void *arg) +{ + Py_VISIT(self->format_id); + Py_VISIT(self->gtrid); + Py_VISIT(self->bqual); + Py_VISIT(self->prepared); + Py_VISIT(self->owner); + Py_VISIT(self->database); + return 0; +} + +static void +xid_dealloc(XidObject *self) +{ + Py_CLEAR(self->format_id); + Py_CLEAR(self->gtrid); + Py_CLEAR(self->bqual); + Py_CLEAR(self->prepared); + Py_CLEAR(self->owner); + Py_CLEAR(self->database); + + Py_TYPE(self)->tp_free((PyObject *)self); +} + +static void +xid_del(PyObject *self) +{ + PyObject_GC_Del(self); +} + +static Py_ssize_t +xid_len(XidObject *self) +{ + return 3; +} + +static PyObject * +xid_getitem(XidObject *self, Py_ssize_t item) +{ + if (item < 0) + item += 3; + + switch (item) { + case 0: + Py_INCREF(self->format_id); + return self->format_id; + case 1: + Py_INCREF(self->gtrid); + return self->gtrid; + case 2: + Py_INCREF(self->bqual); + return self->bqual; + default: + PyErr_SetString(PyExc_IndexError, "index out of range"); + return NULL; + } +} + +static PyObject * +xid_str(XidObject *self) +{ + return xid_get_tid(self); +} + +static PyObject * +xid_repr(XidObject *self) +{ + PyObject *rv = NULL; + PyObject *format = NULL; + PyObject *args = NULL; + + if (Py_None == self->format_id) { + if (!(format = Text_FromUTF8(""))) { + goto exit; + } + if (!(args = PyTuple_New(1))) { goto exit; } + Py_INCREF(self->gtrid); + PyTuple_SET_ITEM(args, 0, self->gtrid); + } + else { + if (!(format = Text_FromUTF8(""))) { + goto exit; + } + if (!(args = PyTuple_New(3))) { goto exit; } + Py_INCREF(self->format_id); + PyTuple_SET_ITEM(args, 0, self->format_id); + Py_INCREF(self->gtrid); + PyTuple_SET_ITEM(args, 1, self->gtrid); + Py_INCREF(self->bqual); + PyTuple_SET_ITEM(args, 2, self->bqual); + } + + rv = Text_Format(format, args); + +exit: + Py_XDECREF(args); + Py_XDECREF(format); + + return rv; +} + + +static const char xid_from_string_doc[] = + "Create a `!Xid` object from a string representation. Static method.\n\n" + "If *s* is a PostgreSQL transaction ID produced by a XA transaction,\n" + "the returned object will have `format_id`, `gtrid`, `bqual` set to\n" + "the values of the preparing XA id.\n" + "Otherwise only the `!gtrid` is populated with the unparsed string.\n" + "The operation is the inverse of the one performed by `!str(xid)`."; + +static PyObject * +xid_from_string_method(PyObject *cls, PyObject *args) +{ + PyObject *s = NULL; + + if (!PyArg_ParseTuple(args, "O", &s)) { return NULL; } + + return (PyObject *)xid_from_string(s); +} + + +static PySequenceMethods xid_sequence = { + (lenfunc)xid_len, /* sq_length */ + 0, /* sq_concat */ + 0, /* sq_repeat */ + (ssizeargfunc)xid_getitem, /* sq_item */ + 0, /* sq_slice */ + 0, /* sq_ass_item */ + 0, /* sq_ass_slice */ + 0, /* sq_contains */ + 0, /* sq_inplace_concat */ + 0, /* sq_inplace_repeat */ +}; + +static struct PyMethodDef xid_methods[] = { + {"from_string", (PyCFunction)xid_from_string_method, + METH_VARARGS|METH_STATIC, xid_from_string_doc}, + {NULL} +}; + +PyTypeObject XidType = { + PyVarObject_HEAD_INIT(NULL, 0) + "psycopg2.extensions.Xid", + sizeof(XidObject), + 0, + (destructor)xid_dealloc, /* tp_dealloc */ + 0, /*tp_print*/ + + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + + 0, /*tp_compare*/ + + (reprfunc)xid_repr, /*tp_repr*/ + 0, /*tp_as_number*/ + &xid_sequence, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + + 0, /*tp_call*/ + (reprfunc)xid_str, /*tp_str*/ + + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + + Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_GC, /*tp_flags*/ + xid_doc, /*tp_doc*/ + + (traverseproc)xid_traverse, /*tp_traverse*/ + 0, /*tp_clear*/ + + 0, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + + /* Attribute descriptor and subclassing stuff */ + + xid_methods, /*tp_methods*/ + xid_members, /*tp_members*/ + 0, /*tp_getset*/ + 0, /*tp_base*/ + 0, /*tp_dict*/ + + 0, /*tp_descr_get*/ + 0, /*tp_descr_set*/ + 0, /*tp_dictoffset*/ + + (initproc)xid_init, /*tp_init*/ + 0, /*tp_alloc will be set to PyType_GenericAlloc in module init*/ + xid_new, /*tp_new*/ + (freefunc)xid_del, /*tp_free Low-level free-memory routine */ + 0, /*tp_is_gc For PyObject_IS_GC */ + 0, /*tp_bases*/ + 0, /*tp_mro method resolution order */ + 0, /*tp_cache*/ + 0, /*tp_subclasses*/ + 0 /*tp_weaklist*/ +}; + + +/* Convert a Python object into a proper xid. + * + * Return a new reference to the object or set an exception. + * + * The idea is that people can either create a xid from connection.xid + * or use a regular string they have found in PostgreSQL's pg_prepared_xacts + * in order to recover a transaction not generated by psycopg. + */ +XidObject *xid_ensure(PyObject *oxid) +{ + XidObject *rv = NULL; + + if (PyObject_TypeCheck(oxid, &XidType)) { + Py_INCREF(oxid); + rv = (XidObject *)oxid; + } + else { + rv = xid_from_string(oxid); + } + + return rv; +} + + +/* Encode or decode a string in base64. */ + +static PyObject * +_xid_base64_enc_dec(const char *funcname, PyObject *s) +{ + PyObject *base64 = NULL; + PyObject *func = NULL; + PyObject *rv = NULL; + + if (!(base64 = PyImport_ImportModule("base64"))) { goto exit; } + if (!(func = PyObject_GetAttrString(base64, funcname))) { goto exit; } + + Py_INCREF(s); + if (!(s = psycopg_ensure_bytes(s))) { goto exit; } + rv = psycopg_ensure_text(PyObject_CallFunctionObjArgs(func, s, NULL)); + Py_DECREF(s); + +exit: + Py_XDECREF(func); + Py_XDECREF(base64); + + return rv; +} + +/* Return a base64-encoded string. */ + +static PyObject * +_xid_encode64(PyObject *s) +{ + return _xid_base64_enc_dec("b64encode", s); +} + +/* Decode a base64-encoded string */ + +static PyObject * +_xid_decode64(PyObject *s) +{ + return _xid_base64_enc_dec("b64decode", s); +} + + +/* Return the PostgreSQL transaction_id for this XA xid. + * + * PostgreSQL wants just a string, while the DBAPI supports the XA standard + * and thus a triple. We use the same conversion algorithm implemented by JDBC + * in order to allow some form of interoperation. + * + * The function must be called while holding the GIL. + * + * see also: the pgjdbc implementation + * http://cvs.pgfoundry.org/cgi-bin/cvsweb.cgi/jdbc/pgjdbc/org/postgresql/xa/RecoveredXid.java?rev=1.2 + */ +PyObject * +xid_get_tid(XidObject *self) +{ + PyObject *rv = NULL; + PyObject *egtrid = NULL; + PyObject *ebqual = NULL; + PyObject *format = NULL; + PyObject *args = NULL; + + if (Py_None == self->format_id) { + /* Unparsed xid: return the gtrid. */ + Py_INCREF(self->gtrid); + rv = self->gtrid; + } + else { + /* XA xid: mash together the components. */ + if (!(egtrid = _xid_encode64(self->gtrid))) { goto exit; } + if (!(ebqual = _xid_encode64(self->bqual))) { goto exit; } + + /* rv = "%d_%s_%s" % (format_id, egtrid, ebqual) */ + if (!(format = Text_FromUTF8("%d_%s_%s"))) { goto exit; } + + if (!(args = PyTuple_New(3))) { goto exit; } + Py_INCREF(self->format_id); + PyTuple_SET_ITEM(args, 0, self->format_id); + PyTuple_SET_ITEM(args, 1, egtrid); egtrid = NULL; + PyTuple_SET_ITEM(args, 2, ebqual); ebqual = NULL; + + if (!(rv = Text_Format(format, args))) { goto exit; } + } + +exit: + Py_XDECREF(args); + Py_XDECREF(format); + Py_XDECREF(egtrid); + Py_XDECREF(ebqual); + + return rv; +} + + +/* Return the regex object to parse a Xid string. + * + * Return a borrowed reference. */ + +BORROWED static PyObject * +_xid_get_parse_regex(void) { + static PyObject *rv; + + if (!rv) { + PyObject *re_mod = NULL; + PyObject *comp = NULL; + PyObject *regex = NULL; + + Dprintf("compiling regexp to parse transaction id"); + + if (!(re_mod = PyImport_ImportModule("re"))) { goto exit; } + if (!(comp = PyObject_GetAttrString(re_mod, "compile"))) { goto exit; } + if (!(regex = PyObject_CallFunction(comp, "s", + "^(\\d+)_([^_]*)_([^_]*)$"))) { + goto exit; + } + + /* Good, compiled. */ + rv = regex; + regex = NULL; + +exit: + Py_XDECREF(regex); + Py_XDECREF(comp); + Py_XDECREF(re_mod); + } + + return rv; +} + +/* Try to parse a Xid string representation in a Xid object. + * + * + * Return NULL + exception if parsing failed. Else a new Xid object. */ + +static XidObject * +_xid_parse_string(PyObject *str) { + PyObject *regex; + PyObject *m = NULL; + PyObject *group = NULL; + PyObject *item = NULL; + PyObject *format_id = NULL; + PyObject *egtrid = NULL; + PyObject *ebqual = NULL; + PyObject *gtrid = NULL; + PyObject *bqual = NULL; + XidObject *rv = NULL; + + /* check if the string is a possible XA triple with a regexp */ + if (!(regex = _xid_get_parse_regex())) { goto exit; } + if (!(m = PyObject_CallMethod(regex, "match", "O", str))) { goto exit; } + if (m == Py_None) { + PyErr_SetString(PyExc_ValueError, "bad xid format"); + goto exit; + } + + /* Extract the components from the regexp */ + if (!(group = PyObject_GetAttrString(m, "group"))) { goto exit; } + if (!(item = PyObject_CallFunction(group, "i", 1))) { goto exit; } + if (!(format_id = PyObject_CallFunctionObjArgs( + (PyObject *)&PyInt_Type, item, NULL))) { + goto exit; + } + if (!(egtrid = PyObject_CallFunction(group, "i", 2))) { goto exit; } + if (!(gtrid = _xid_decode64(egtrid))) { goto exit; } + + if (!(ebqual = PyObject_CallFunction(group, "i", 3))) { goto exit; } + if (!(bqual = _xid_decode64(ebqual))) { goto exit; } + + /* Try to build the xid with the parsed material */ + rv = (XidObject *)PyObject_CallFunctionObjArgs((PyObject *)&XidType, + format_id, gtrid, bqual, NULL); + +exit: + Py_XDECREF(bqual); + Py_XDECREF(ebqual); + Py_XDECREF(gtrid); + Py_XDECREF(egtrid); + Py_XDECREF(format_id); + Py_XDECREF(item); + Py_XDECREF(group); + Py_XDECREF(m); + + return rv; +} + +/* Return a new Xid object representing a transaction ID not conform to + * the XA specifications. */ + +static XidObject * +_xid_unparsed_from_string(PyObject *str) { + XidObject *xid = NULL; + XidObject *rv = NULL; + PyObject *tmp; + + /* fake args to work around the checks performed by the xid init */ + if (!(xid = (XidObject *)PyObject_CallFunction((PyObject *)&XidType, + "iss", 0, "", ""))) { + goto exit; + } + + /* set xid.gtrid = str */ + tmp = xid->gtrid; + Py_INCREF(str); + xid->gtrid = str; + Py_DECREF(tmp); + + /* set xid.format_id = None */ + tmp = xid->format_id; + Py_INCREF(Py_None); + xid->format_id = Py_None; + Py_DECREF(tmp); + + /* set xid.bqual = None */ + tmp = xid->bqual; + Py_INCREF(Py_None); + xid->bqual = Py_None; + Py_DECREF(tmp); + + /* return the finished object */ + rv = xid; + xid = NULL; + +exit: + Py_XDECREF(xid); + + return rv; +} + +/* Build a Xid from a string representation. + * + * If the xid is in the format generated by Psycopg, unpack the tuple into + * the struct members. Otherwise generate an "unparsed" xid. + */ +XidObject * +xid_from_string(PyObject *str) { + XidObject *rv; + + if (!(Bytes_Check(str) || PyUnicode_Check(str))) { + PyErr_SetString(PyExc_TypeError, "not a valid transaction id"); + return NULL; + } + + /* Try to parse an XA triple from the string. This may fail for several + * reasons, such as the rules stated in Xid.__init__. */ + rv = _xid_parse_string(str); + if (!rv) { + /* If parsing failed, treat the string as an unparsed id */ + PyErr_Clear(); + rv = _xid_unparsed_from_string(str); + } + + return rv; +} + + +/* conn_tpc_recover -- return a list of pending TPC Xid */ + +PyObject * +xid_recover(PyObject *conn) +{ + PyObject *rv = NULL; + PyObject *curs = NULL; + PyObject *xids = NULL; + XidObject *xid = NULL; + PyObject *recs = NULL; + PyObject *rec = NULL; + PyObject *item = NULL; + PyObject *tmp; + Py_ssize_t len, i; + + /* curs = conn.cursor() */ + if (!(curs = PyObject_CallMethod(conn, "cursor", NULL))) { goto exit; } + + /* curs.execute(...) */ + if (!(tmp = PyObject_CallMethod(curs, "execute", "s", + "SELECT gid, prepared, owner, database FROM pg_prepared_xacts"))) + { + goto exit; + } + Py_DECREF(tmp); + + /* recs = curs.fetchall() */ + if (!(recs = PyObject_CallMethod(curs, "fetchall", NULL))) { goto exit; } + + /* curs.close() */ + if (!(tmp = PyObject_CallMethod(curs, "close", NULL))) { goto exit; } + Py_DECREF(tmp); + + /* Build the list with return values. */ + if (0 > (len = PySequence_Size(recs))) { goto exit; } + if (!(xids = PyList_New(len))) { goto exit; } + + /* populate the xids list */ + for (i = 0; i < len; ++i) { + if (!(rec = PySequence_GetItem(recs, i))) { goto exit; } + + /* Get the xid with the XA triple set */ + if (!(item = PySequence_GetItem(rec, 0))) { goto exit; } + if (!(xid = xid_from_string(item))) { goto exit; } + Py_DECREF(item); item = NULL; + + /* set xid.prepared */ + if (!(item = PySequence_GetItem(rec, 1))) { goto exit; } + tmp = xid->prepared; + xid->prepared = item; + Py_DECREF(tmp); + item = NULL; + + /* set xid.owner */ + if (!(item = PySequence_GetItem(rec, 2))) { goto exit; } + tmp = xid->owner; + xid->owner = item; + Py_DECREF(tmp); + item = NULL; + + /* set xid.database */ + if (!(item = PySequence_GetItem(rec, 3))) { goto exit; } + tmp = xid->database; + xid->database = item; + Py_DECREF(tmp); + item = NULL; + + /* xid finished: add it to the returned list */ + PyList_SET_ITEM(xids, i, (PyObject *)xid); + xid = NULL; /* ref stolen */ + + Py_DECREF(rec); rec = NULL; + } + + /* set the return value. */ + rv = xids; + xids = NULL; + +exit: + Py_XDECREF(xids); + Py_XDECREF(xid); + Py_XDECREF(curs); + Py_XDECREF(recs); + Py_XDECREF(rec); + Py_XDECREF(item); + + return rv; +} diff -Nru psycopg2-2.0.13/psycopg2da/adapter.py psycopg2-2.4.5/psycopg2da/adapter.py --- psycopg2-2.0.13/psycopg2da/adapter.py 2009-04-26 12:09:21.000000000 +0000 +++ psycopg2-2.4.5/psycopg2da/adapter.py 2011-02-06 15:58:34.000000000 +0000 @@ -1,35 +1,28 @@ -# psycopg2da # Copyright (C) 2006 Fabio Tranchitella # -# Based on psycopgda: -# -# Copyright (c) 2002-2006 Zope Corporation and Contributors. -# All Rights Reserved. +# psycopg2da is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. # -# This software is subject to the provisions of the Zope Public License, -# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. -# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED -# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS -# FOR A PARTICULAR PURPOSE. +# In addition, as a special exception, the copyright holders give +# permission to link this program with the OpenSSL library (or with +# modified versions of OpenSSL that use the same license as OpenSSL), +# and distribute linked combinations including the two. # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# You must obey the GNU Lesser General Public License in all respects for +# all of the code used other than OpenSSL. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# psycopg2da is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +# License for more details. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# Based on ZPsycopgDA. # # If you prefer you can use this package using the ZPL license as # published on the Zope web site, http://www.zope.org/Resources/ZPL. -# + """PostgreSQL Database Adapter for Zope 3""" from zope.interface import implements diff -Nru psycopg2-2.0.13/psycopg2da/tests.py psycopg2-2.4.5/psycopg2da/tests.py --- psycopg2-2.0.13/psycopg2da/tests.py 2009-04-26 12:09:21.000000000 +0000 +++ psycopg2-2.4.5/psycopg2da/tests.py 2011-02-06 15:58:34.000000000 +0000 @@ -1,20 +1,26 @@ -# psycopg2da # Copyright (C) 2006 Fabio Tranchitella # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or +# psycopg2da is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# In addition, as a special exception, the copyright holders give +# permission to link this program with the OpenSSL library (or with +# modified versions of OpenSSL that use the same license as OpenSSL), +# and distribute linked combinations including the two. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# You must obey the GNU Lesser General Public License in all respects for +# all of the code used other than OpenSSL. # +# psycopg2da is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +# License for more details. +# +# If you prefer you can use this package using the ZPL license as +# published on the Zope web site, http://www.zope.org/Resources/ZPL. + """Unit tests for Psycopg2DA.""" from unittest import TestCase, TestSuite, main, makeSuite diff -Nru psycopg2-2.0.13/README psycopg2-2.4.5/README --- psycopg2-2.0.13/README 2009-04-26 12:09:21.000000000 +0000 +++ psycopg2-2.4.5/README 2011-12-19 10:30:20.000000000 +0000 @@ -1,39 +1,38 @@ -psycopg - Python-PostgreSQL Database Adapter +psycopg2 - Python-PostgreSQL Database Adapter ******************************************** -psycopg is a PostgreSQL database adapter for the Python programming -language. This is version 2, a complete rewrite of the original code to -provide new-style classes for connection and cursor objects and other -sweet candies. Like the original, psycopg 2 was written with the aim of -being very small and fast, and stable as a rock. +psycopg2 is a PostgreSQL database adapter for the Python programming +language. psycopg2 was written with the aim of being very small and fast, +and stable as a rock. -psycopg is different from the other database adapter because it was +psycopg2 is different from the other database adapter because it was designed for heavily multi-threaded applications that create and destroy lots of cursors and make a conspicuous number of concurrent INSERTs or -UPDATEs. psycopg 2 also provide full asycronous operations for the really -brave programmer. +UPDATEs. psycopg2 also provides full asynchronous operations and support +for coroutine libraries. -There are confirmed reports of psycopg 1.x compiling and running on Linux -and FreeBSD on i386, Solaris, MacOS X and win32 architectures. psycopg 2 -does not introduce build-wise incompatible changes so it should be able to -compile on all architectures just as its predecessor did. +psycopg2 can compile and run on Linux, FreeBSD, Solaris, MacOS X and +Windows architecture. It supports Python versions from 2.4 onwards and +PostgreSQL versions from 7.4 onwards. -Now go read the INSTALL file. More information about psycopg extensions to -the DBAPI-2.0 is available in the files located in the doc/ direcory. +psycopg2 is free software ("free as in freedom" but I like beer too.) +It is licensed under the GNU Lesser General Public License, version 3 or +later plus an exception to allow OpenSSL (libpq) linking; see LICENSE for +more details. -psycopg is free software ("free as in freedom" but I like beer too.) -Licensing information is available in the LICENSE file. +Documentation +------------- +Start by reading the INSTALL file. More information about psycopg2 extensions +to the DBAPI-2.0 is available in the files located in the doc/ direcory. +Example code can be found in the examples/ directory. If you make any changes +to the code make sure to run the unit tests localed in tests/. -Contributors ------------- +Online documentation can be found at: http://initd.org/psycopg/ -A short list of contributors to psycopg2 follows (if you feel you belong -to this list and you can't find yourself here just drop me a mail): +If you stumble upon any bugs, please tell us at: http://psycopg.lighthouseapp.com/ - * Kudos to piro for all the documentation work. +Contributors +------------ - * Peter Fein contributed a logging connection/cursor class that even if it - was not used directly heavily influenced the implementation currently in - psycopg2.extras. - +For a list of contributors to the project, see the AUTHORS file. diff -Nru psycopg2-2.0.13/scripts/ext2html.py psycopg2-2.4.5/scripts/ext2html.py --- psycopg2-2.0.13/scripts/ext2html.py 2009-04-26 12:09:21.000000000 +0000 +++ psycopg2-2.4.5/scripts/ext2html.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,169 +0,0 @@ -#!/usr/bin/env python - -# Author: Daniele Varrazzo -# Contact: daniele dot varrazzo at gmail dot com -# Revision: $Revision$ -# Date: $Date$ -# Copyright: This module has been placed in the public domain. - -""" -A minimal front end to the Docutils Publisher, producing HTML. - -Output can refer to Epydoc-generated APIs through the iterpreted text role -"api". -""" - -import types -import sys - -# The url fragment where the api "index.html" resides w.r.t. the generated docs -api_root = "api/" - -try: - import locale - locale.setlocale(locale.LC_ALL, '') -except: - pass - -from docutils.core import publish_cmdline, default_description -from docutils.parsers.rst.roles import register_canonical_role -from docutils import nodes, utils - -# api references are searched for in these modules -api_modules = [ - 'psycopg2', - 'psycopg2._psycopg', - 'psycopg2.extensions', -] - -# name starting with a dot are looking as those objects attributes. -searched_objects = [ - # module_name, object_name - ('psycopg2.extensions', 'connection'), - ('psycopg2.extensions', 'cursor'), -] - -# import all the referenced modules -for modname in api_modules: - __import__(modname) - -class EpydocTarget: - """Representation of an element language.""" - def __init__(self, name, element): - self.name = name - - # The python object described - self.element = element - - # The base name of the page - self.page = None - - # The url fragment - self.fragment = None - - def get_url(self): - # is it a private element? - components = self.page.split('.') - if self.fragment: components.append(self.fragment) - - for component in components: - if component.startswith('_'): - private = True - break - else: - private = False - - ref = api_root + (private and "private/" or "public/") \ - + self.page + "-" + self.get_type() + ".html" - if self.fragment: - ref = ref + "#" + self.fragment - - return ref - - def get_type(self): - # detect the element type - if isinstance(self.element, types.TypeType): - return 'class' - elif isinstance(self.element, types.ModuleType): - return 'module' - else: - raise ValueError("Can't choose a type for '%s'." % self.name) - -def filter_par(name): - """Filter parenthesis away from a name.""" - if name.endswith(")"): - return name.split("(")[0] - else: - return name - -def get_element_target(name): - """Return the life, the death, the miracles about a package element.""" - - name = filter_par(name) - - if name.startswith('.'): - for modname, objname in searched_objects: - if hasattr(getattr(sys.modules[modname], objname), name[1:]): - name = objname + name - break - - # is the element a module? - if name in api_modules: - out = EpydocTarget(name, sys.modules[name]) - out.page = name - return out - - # look for the element in some module - for modname in api_modules: - element = getattr(sys.modules[modname], name, None) - if element is not None: - - # Check if it is a function defined in a module - if isinstance(element, - (int, types.FunctionType, types.BuiltinFunctionType)): - out = EpydocTarget(name, sys.modules[modname]) - out.page = modname - out.fragment = name - else: - out = EpydocTarget(name, element) - out.page = modname + '.' + name - - return out - - # maybe a qualified name? - if '.' in name: - out = get_element_target('.'.join(name.split('.')[:-1])) - if out is not None: - out.fragment = filter_par(name.split('.')[-1]) - return out - - raise ValueError("Can't find '%s' in any provided module." % name) - -def api_role(role, rawtext, text, lineno, inliner, - options={}, content=[]): - try: - target = get_element_target(text) - except Exception, exc: - msg = inliner.reporter.error(str(exc), line=lineno) - prb = inliner.problematic(rawtext, rawtext, msg) - return [prb], [msg] - - ref = target.get_url() - node2 = nodes.literal(rawtext, utils.unescape(text)) - node = nodes.reference(rawtext, '', node2, refuri=ref, - **options) - return [node], [] - - -register_canonical_role('api', api_role) - -# Register the 'api' role as canonical role -from docutils.parsers.rst import roles -roles.DEFAULT_INTERPRETED_ROLE = 'api' - - -description = ('Generates (X)HTML documents from standalone reStructuredText ' - 'sources with links to Epydoc API. ' + default_description) - - -publish_cmdline(writer_name='html', description=description) diff -Nru psycopg2-2.0.13/scripts/fix_b.py psycopg2-2.4.5/scripts/fix_b.py --- psycopg2-2.0.13/scripts/fix_b.py 1970-01-01 00:00:00.000000000 +0000 +++ psycopg2-2.4.5/scripts/fix_b.py 2011-02-06 15:58:34.000000000 +0000 @@ -0,0 +1,20 @@ +"""Fixer to change b('string') into b'string'.""" +# Author: Daniele Varrazzo + +import token +from lib2to3 import fixer_base +from lib2to3.pytree import Leaf + +class FixB(fixer_base.BaseFix): + + PATTERN = """ + power< wrapper='b' trailer< '(' arg=[any] ')' > rest=any* > + """ + + def transform(self, node, results): + arg = results['arg'] + wrapper = results["wrapper"] + if len(arg) == 1 and arg[0].type == token.STRING: + b = Leaf(token.STRING, 'b' + arg[0].value, prefix=wrapper.prefix) + node.children = [ b ] + results['rest'] + diff -Nru psycopg2-2.0.13/scripts/makedocs.py psycopg2-2.4.5/scripts/makedocs.py --- psycopg2-2.0.13/scripts/makedocs.py 2009-04-26 12:09:21.000000000 +0000 +++ psycopg2-2.4.5/scripts/makedocs.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,15 +0,0 @@ -#!/usr/bin/env python -"""Build documentation and api.""" - -import os - -EPYDOC = "python c:/programmi/python23/scripts/epydoc.py" -PSYCOPG = "c:/programmi/python23/lib/site-packages/psycopg2" - -os.system("python ext2html.py ../doc/extensions.rst > ../doc/extensions.html") -os.system("%s " - "-o ../doc/api " - "--css ../doc/api-screen.css " - "--docformat restructuredtext " - "%s" - % (EPYDOC,PSYCOPG,)) diff -Nru psycopg2-2.0.13/scripts/make_errorcodes.py psycopg2-2.4.5/scripts/make_errorcodes.py --- psycopg2-2.0.13/scripts/make_errorcodes.py 1970-01-01 00:00:00.000000000 +0000 +++ psycopg2-2.4.5/scripts/make_errorcodes.py 2011-12-19 10:30:20.000000000 +0000 @@ -0,0 +1,121 @@ +#!/usr/bin/env python +"""Generate the errorcodes module starting from PostgreSQL documentation. + +The script can be run at a new PostgreSQL release to refresh the module. +""" + +# Copyright (C) 2010 Daniele Varrazzo +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2, or (at your option) any later +# version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. + +import sys +import urllib2 +from collections import defaultdict + +from BeautifulSoup import BeautifulSoup as BS + +def main(): + if len(sys.argv) != 2: + print >>sys.stderr, "usage: %s /path/to/errorcodes.py" % sys.argv[0] + return 2 + + filename = sys.argv[1] + + file_start = read_base_file(filename) + classes, errors = fetch_errors(['8.1', '8.2', '8.3', '8.4', '9.0', '9.1']) + + f = open(filename, "w") + for line in file_start: + print >>f, line + for line in generate_module_data(classes, errors): + print >>f, line + +def read_base_file(filename): + rv = [] + for line in open(filename): + rv.append(line.rstrip("\n")) + if line.startswith("# autogenerated"): + return rv + + raise ValueError("can't find the separator. Is this the right file?") + +def parse_errors(url): + page = BS(urllib2.urlopen(url)) + table = page('table')[1]('tbody')[0] + + classes = {} + errors = defaultdict(dict) + + for tr in table('tr'): + if tr.td.get('colspan'): # it's a class + label = ' '.join(' '.join(tr(text=True)).split()) \ + .replace(u'\u2014', '-').encode('ascii') + assert label.startswith('Class') + class_ = label.split()[1] + assert len(class_) == 2 + classes[class_] = label + + else: # it's an error + errcode = tr.tt.string.encode("ascii") + assert len(errcode) == 5 + + tds = tr('td') + if len(tds) == 3: + errlabel = '_'.join(tds[1].string.split()).encode('ascii') + + # double check the columns are equal + cond_name = tds[2].string.strip().upper().encode("ascii") + assert errlabel == cond_name, tr + + elif len(tds) == 2: + # found in PG 9.1 docs + errlabel = tds[1].tt.string.upper().encode("ascii") + + else: + assert False, tr + + errors[class_][errcode] = errlabel + + return classes, errors + +errors_url="http://www.postgresql.org/docs/%s/static/errcodes-appendix.html" + +def fetch_errors(versions): + classes = {} + errors = defaultdict(dict) + + for version in versions: + c1, e1 = parse_errors(errors_url % version) + classes.update(c1) + for c, cerrs in e1.iteritems(): + errors[c].update(cerrs) + + return classes, errors + +def generate_module_data(classes, errors): + yield "" + yield "# Error classes" + for clscode, clslabel in sorted(classes.items()): + err = clslabel.split(" - ")[1].split("(")[0] \ + .strip().replace(" ", "_").replace('/', "_").upper() + yield "CLASS_%s = %r" % (err, clscode) + + for clscode, clslabel in sorted(classes.items()): + yield "" + yield "# %s" % clslabel + + for errcode, errlabel in sorted(errors[clscode].items()): + yield "%s = %r" % (errlabel, errcode) + +if __name__ == '__main__': + sys.exit(main()) + + diff -Nru psycopg2-2.0.13/scripts/refcounter.py psycopg2-2.4.5/scripts/refcounter.py --- psycopg2-2.0.13/scripts/refcounter.py 1970-01-01 00:00:00.000000000 +0000 +++ psycopg2-2.4.5/scripts/refcounter.py 2011-02-06 15:58:34.000000000 +0000 @@ -0,0 +1,111 @@ +#!/usr/bin/env python +"""Detect reference leaks after several unit test runs. + +The script runs the unit test and counts the objects alive after the run. If +the object count differs between the last two runs, a report is printed and the +script exits with error 1. +""" + +# Copyright (C) 2011 Daniele Varrazzo +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +import gc +import sys +import difflib +import unittest +from pprint import pprint +from collections import defaultdict + +def main(): + opt = parse_args() + + import psycopg2.tests + test = psycopg2.tests + if opt.suite: + test = getattr(test, opt.suite) + + sys.stdout.write("test suite %s\n" % test.__name__) + + for i in range(1, opt.nruns + 1): + sys.stdout.write("test suite run %d of %d\n" % (i, opt.nruns)) + runner = unittest.TextTestRunner() + runner.run(test.test_suite()) + dump(i, opt) + + f1 = open('debug-%02d.txt' % (opt.nruns - 1)).readlines() + f2 = open('debug-%02d.txt' % (opt.nruns)).readlines() + for line in difflib.unified_diff(f1, f2, + "run %d" % (opt.nruns - 1), "run %d" % opt.nruns): + sys.stdout.write(line) + + rv = f1 != f2 and 1 or 0 + + if opt.objs: + f1 = open('objs-%02d.txt' % (opt.nruns - 1)).readlines() + f2 = open('objs-%02d.txt' % (opt.nruns)).readlines() + for line in difflib.unified_diff(f1, f2, + "run %d" % (opt.nruns - 1), "run %d" % opt.nruns): + sys.stdout.write(line) + + return rv + +def parse_args(): + import optparse + + parser = optparse.OptionParser(description=__doc__) + parser.add_option('--nruns', type='int', metavar="N", default=3, + help="number of test suite runs [default: %default]") + parser.add_option('--suite', metavar="NAME", + help="the test suite to run (e.g. 'test_cursor'). [default: all]") + parser.add_option('--objs', metavar="TYPE", + help="in case of leaks, print a report of object TYPE " + "(support still incomplete)") + + opt, args = parser.parse_args() + return opt + + +def dump(i, opt): + gc.collect() + objs = gc.get_objects() + + c = defaultdict(int) + for o in objs: + c[type(o)] += 1 + + pprint( + sorted(((v,str(k)) for k,v in c.items()), reverse=True), + stream=open("debug-%02d.txt" % i, "w")) + + if opt.objs: + co = [] + t = getattr(__builtins__, opt.objs) + for o in objs: + if type(o) is t: + co.append(o) + + # TODO: very incomplete + if t is dict: + co.sort(key = lambda d: d.items()) + else: + co.sort() + + pprint(co, stream=open("objs-%02d.txt" % i, "w")) + + +if __name__ == '__main__': + sys.exit(main()) + diff -Nru psycopg2-2.0.13/scripts/ticket58.py psycopg2-2.4.5/scripts/ticket58.py --- psycopg2-2.0.13/scripts/ticket58.py 1970-01-01 00:00:00.000000000 +0000 +++ psycopg2-2.4.5/scripts/ticket58.py 2011-06-13 16:53:48.000000000 +0000 @@ -0,0 +1,75 @@ +""" +A script to reproduce the race condition described in ticket #58 + +from https://bugzilla.redhat.com/show_bug.cgi?id=711095 + +Results in the error: + + python: Modules/gcmodule.c:277: visit_decref: Assertion `gc->gc.gc_refs != 0' + failed. + +on unpatched library. +""" + +import threading +import gc +import time + +import psycopg2 +from StringIO import StringIO + +done = 0 + +class GCThread(threading.Thread): + # A thread that sits in an infinite loop, forcing the garbage collector + # to run + def run(self): + global done + while not done: + gc.collect() + time.sleep(0.1) # give the other thread a chance to run + +gc_thread = GCThread() + + +# This assumes a pre-existing db named "test", with: +# "CREATE TABLE test (id serial PRIMARY KEY, num integer, data varchar);" + +conn = psycopg2.connect("dbname=test user=postgres") +cur = conn.cursor() + +# Start the other thread, running the GC regularly +gc_thread.start() + +# Now do lots of "cursor.copy_from" calls: +print "copy_from" +for i in range(1000): + f = StringIO("42\tfoo\n74\tbar\n") + cur.copy_from(f, 'test', columns=('num', 'data')) + # Assuming the other thread gets a chance to run during this call, expect a + # build of python (with assertions enabled) to bail out here with: + # python: Modules/gcmodule.c:277: visit_decref: Assertion `gc->gc.gc_refs != 0' failed. + +# Also exercise the copy_to code path +print "copy_to" +cur.execute("truncate test") +f = StringIO("42\tfoo\n74\tbar\n") +cur.copy_from(f, 'test', columns=('num', 'data')) +for i in range(1000): + f = StringIO() + cur.copy_to(f, 'test', columns=('num', 'data')) + +# And copy_expert too +print "copy_expert" +cur.execute("truncate test") +for i in range(1000): + f = StringIO("42\tfoo\n74\tbar\n") + cur.copy_expert("copy test to stdout", f) + +# Terminate the GC thread's loop: +done = 1 + +cur.close() +conn.close() + + diff -Nru psycopg2-2.0.13/setup.cfg psycopg2-2.4.5/setup.cfg --- psycopg2-2.0.13/setup.cfg 2009-10-04 21:32:35.000000000 +0000 +++ psycopg2-2.4.5/setup.cfg 2012-03-28 21:09:15.000000000 +0000 @@ -1,18 +1,16 @@ [build_ext] -define=PSYCOPG_EXTENSIONS,PSYCOPG_NEW_BOOLEAN,HAVE_PQFREEMEM,HAVE_PQPROTOCOL3 +define=PSYCOPG_EXTENSIONS,PSYCOPG_NEW_BOOLEAN,HAVE_PQFREEMEM # 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 -# HAVE_PQPROTOCOL3 should be defined on PostgreSQL >= 7.4 # PSYCOPG_DEBUG can be added to enable verbose debug information -# PSYCOPG_OWN_QUOTING can be added, but it is deprecated (will go away in 2.1) # PSYCOPG_NEW_BOOLEAN to format booleans as true/false vs 't'/'f' -# Set to 1 to use Python datatime objects for default date/time representation. +# Set to 1 to use Python datetime objects for default date/time representation. use_pydatetime=1 -# If the build system does not find the mx.DateTime headers, try +# If the build system does not find the mx.DateTime headers, try # uncommenting the following line and setting its value to the right path. #mx_include_dir= @@ -30,14 +28,14 @@ # set it to the pg_config full path. #pg_config= -# If "pg_config" is not available, "include_dirs" can be used to locate +# If "pg_config" is not available, "include_dirs" can be used to locate # postgresql headers and libraries. Some extra checks on sys.platform will # still be done in setup.py. # The next line is the default as used on psycopg author Debian laptop: #include_dirs=/usr/include/postgresql:/usr/include/postgresql/server # Uncomment next line on Mandrake 10.x (and comment previous ones): -#include_dirs=/usr/include/pgsql/8.0:/usr/include/pgsql/8.0/server +#include_dirs=/usr/include/pgsql/8.0:/usr/include/pgsql/8.0/server # Uncomment next line on SUSE 9.3 (and comment previous ones): #include_dirs=/usr/include/pgsql:/usr/include/pgsql/server diff -Nru psycopg2-2.0.13/setup.py psycopg2-2.4.5/setup.py --- psycopg2-2.0.13/setup.py 2009-10-04 21:37:14.000000000 +0000 +++ psycopg2-2.4.5/setup.py 2012-03-28 21:09:15.000000000 +0000 @@ -1,38 +1,37 @@ # setup.py - distutils packaging # -# Copyright (C) 2003-2004 Federico Di Gregorio +# Copyright (C) 2003-2010 Federico Di Gregorio # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by the -# Free Software Foundation; either version 2, or (at your option) any later -# version. +# psycopg2 is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. # -# This program is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY -# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# for more details. +# psycopg2 is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +# License for more details. """Python-PostgreSQL Database Adapter -psycopg is a PostgreSQL database adapter for the Python programming -language. This is version 2, a complete rewrite of the original code to -provide new-style classes for connection and cursor objects and other sweet -candies. Like the original, psycopg 2 was written with the aim of being -very small and fast, and stable as a rock. +psycopg2 is a PostgreSQL database adapter for the Python programming +language. psycopg2 was written with the aim of being very small and fast, +and stable as a rock. -psycopg is different from the other database adapter because it was +psycopg2 is different from the other database adapter because it was designed for heavily multi-threaded applications that create and destroy lots of cursors and make a conspicuous number of concurrent INSERTs or -UPDATEs. psycopg 2 also provide full asycronous operations for the really -brave programmer. +UPDATEs. psycopg2 also provide full asycronous operations and support +for coroutine libraries. """ classifiers = """\ -Development Status :: 4 - Beta +Development Status :: 5 - Production/Stable Intended Audience :: Developers -License :: OSI Approved :: GNU General Public License (GPL) +License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL) License :: OSI Approved :: Zope Public License Programming Language :: Python +Programming Language :: Python :: 3 Programming Language :: C Programming Language :: SQL Topic :: Database @@ -43,33 +42,166 @@ Operating System :: Unix """ +# Note: The setup.py must be compatible with both Python 2 and 3 + import os -import os.path import sys import re import subprocess -import ConfigParser from distutils.core import setup, Extension -from distutils.errors import DistutilsFileError from distutils.command.build_ext import build_ext from distutils.sysconfig import get_python_inc from distutils.ccompiler import get_default_compiler +from distutils.util import get_platform + +try: + from distutils.command.build_py import build_py_2to3 as build_py +except ImportError: + from distutils.command.build_py import build_py +else: + # Configure distutils to run our custom 2to3 fixers as well + from lib2to3.refactor import get_fixers_from_package + build_py.fixer_names = get_fixers_from_package('lib2to3.fixes') + build_py.fixer_names.append('fix_b') + sys.path.insert(0, 'scripts') + +try: + import configparser +except ImportError: + import ConfigParser as configparser + +# Take a look at http://www.python.org/dev/peps/pep-0386/ +# for a consistent versioning pattern. + +PSYCOPG_VERSION = '2.4.5' -PSYCOPG_VERSION = '2.0.13' version_flags = ['dt', 'dec'] PLATFORM_IS_WINDOWS = sys.platform.lower().startswith('win') -def get_pg_config(kind, pg_config="pg_config"): - p = subprocess.Popen([pg_config, "--" + kind], - stdin=subprocess.PIPE, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE) - p.stdin.close() - r = p.stdout.readline().strip() - if not r: - raise Warning(p.stderr.readline()) - return r + +class PostgresConfig: + def __init__(self, build_ext): + self.build_ext = build_ext + self.pg_config_exe = self.build_ext.pg_config + if not self.pg_config_exe: + self.pg_config_exe = self.autodetect_pg_config_path() + if self.pg_config_exe is None: + sys.stderr.write("""\ +Error: pg_config executable not found. + +Please add the directory containing pg_config to the PATH +or specify the full executable path with the option: + + python setup.py build_ext --pg-config /path/to/pg_config build ... + +or with the pg_config option in 'setup.cfg'. +""") + sys.exit(1) + + def query(self, attr_name): + """Spawn the pg_config executable, querying for the given config + name, and return the printed value, sanitized. """ + try: + pg_config_process = subprocess.Popen( + [self.pg_config_exe, "--" + attr_name], + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + except OSError: + raise Warning("Unable to find 'pg_config' file in '%s'" % + self.pg_config_exe) + pg_config_process.stdin.close() + result = pg_config_process.stdout.readline().strip() + if not result: + raise Warning(pg_config_process.stderr.readline()) + if not isinstance(result, str): + result = result.decode('ascii') + return result + + def find_on_path(self, exename, path_directories=None): + if not path_directories: + path_directories = os.environ['PATH'].split(os.pathsep) + for dir_name in path_directories: + fullpath = os.path.join(dir_name, exename) + if os.path.isfile(fullpath): + return fullpath + return None + + def autodetect_pg_config_path(self): + """Find and return the path to the pg_config executable.""" + if PLATFORM_IS_WINDOWS: + return self.autodetect_pg_config_path_windows() + else: + return self.find_on_path('pg_config') + + def autodetect_pg_config_path_windows(self): + """Attempt several different ways of finding the pg_config + executable on Windows, and return its full path, if found.""" + + # This code only runs if they have not specified a pg_config option + # in the config file or via the commandline. + + # First, check for pg_config.exe on the PATH, and use that if found. + pg_config_exe = self.find_on_path('pg_config.exe') + if pg_config_exe: + return pg_config_exe + + # Now, try looking in the Windows Registry to find a PostgreSQL + # installation, and infer the path from that. + pg_config_exe = self._get_pg_config_from_registry() + if pg_config_exe: + return pg_config_exe + + return None + + def _get_pg_config_from_registry(self): + try: + import winreg + except ImportError: + import _winreg as winreg + + reg = winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE) + try: + pg_inst_list_key = winreg.OpenKey(reg, + 'SOFTWARE\\PostgreSQL\\Installations') + except EnvironmentError: + # No PostgreSQL installation, as best as we can tell. + return None + + try: + # Determine the name of the first subkey, if any: + try: + first_sub_key_name = winreg.EnumKey(pg_inst_list_key, 0) + except EnvironmentError: + return None + + pg_first_inst_key = winreg.OpenKey(reg, + 'SOFTWARE\\PostgreSQL\\Installations\\' + + first_sub_key_name) + try: + pg_inst_base_dir = winreg.QueryValueEx( + pg_first_inst_key, 'Base Directory')[0] + finally: + winreg.CloseKey(pg_first_inst_key) + + finally: + winreg.CloseKey(pg_inst_list_key) + + pg_config_path = os.path.join( + pg_inst_base_dir, 'bin', 'pg_config.exe') + if not os.path.exists(pg_config_path): + return None + + # Support unicode paths, if this version of Python provides the + # necessary infrastructure: + if sys.version_info[0] < 3 \ + and hasattr(sys, 'getfilesystemencoding'): + pg_config_path = pg_config_path.encode( + sys.getfilesystemencoding()) + + return pg_config_path + class psycopg_build_ext(build_ext): """Conditionally complement the setup.cfg options file. @@ -96,20 +228,29 @@ boolean_options = build_ext.boolean_options[:] boolean_options.extend(('use-pydatetime', 'have-ssl', 'static-libpq')) - DEFAULT_PG_CONFIG = "pg_config" + def __init__(self, *args, **kwargs): + build_ext.__init__(self, *args, **kwargs) def initialize_options(self): build_ext.initialize_options(self) self.use_pg_dll = 1 self.pgdir = None self.mx_include_dir = None + self.use_pydatetime = 1 + self.have_ssl = have_ssl + self.static_libpq = static_libpq + self.pg_config = None + + def compiler_is_msvc(self): + return self.get_compiler_name().lower().startswith('msvc') - self.pg_config = self.autodetect_pg_config_path() + def compiler_is_mingw(self): + return self.get_compiler_name().lower().startswith('mingw') - def get_compiler(self): + def get_compiler_name(self): """Return the name of the C compiler used to compile extensions. - If a compiler was not explicitely set (on the command line, for + If a compiler was not explicitly set (on the command line, for example), fall back on the default compiler. """ if self.compiler: @@ -123,27 +264,40 @@ name = get_default_compiler() return name - def get_pg_config(self, kind): - return get_pg_config(kind, self.pg_config) + def get_export_symbols(self, extension): + # Fix MSVC seeing two of the same export symbols. + if self.compiler_is_msvc(): + return [] + else: + return build_ext.get_export_symbols(self, extension) + + def build_extension(self, extension): + build_ext.build_extension(self, extension) + sysVer = sys.version_info[:2] + + # For Python versions that use MSVC compiler 2008, re-insert the + # manifest into the resulting .pyd file. + if self.compiler_is_msvc() and sysVer not in ((2, 4), (2, 5)): + platform = get_platform() + # Default to the x86 manifest + manifest = '_psycopg.vc9.x86.manifest' + if platform == 'win-amd64': + manifest = '_psycopg.vc9.amd64.manifest' + self.compiler.spawn( + ['mt.exe', '-nologo', '-manifest', + os.path.join('psycopg', manifest), + '-outputresource:%s;2' % ( + os.path.join(self.build_lib, + 'psycopg2', '_psycopg.pyd'))]) def finalize_win32(self): """Finalize build system configuration on win32 platform.""" - import struct sysVer = sys.version_info[:2] # Add compiler-specific arguments: extra_compiler_args = [] - compiler_name = self.get_compiler().lower() - compiler_is_msvc = compiler_name.startswith('msvc') - compiler_is_mingw = compiler_name.startswith('mingw') - if compiler_is_msvc: - # If we're using MSVC 7.1 or later on a 32-bit platform, add the - # /Wp64 option to generate warnings about Win64 portability - # problems. - if sysVer >= (2,4) and struct.calcsize('P') == 4: - extra_compiler_args.append('/Wp64') - elif compiler_is_mingw: + if self.compiler_is_mingw(): # Default MinGW compilation of Python extensions on Windows uses # only -O: extra_compiler_args.append('-O3') @@ -155,23 +309,23 @@ extra_compiler_args.append('-fno-strict-aliasing') # Force correct C runtime library linkage: - if sysVer <= (2,3): + if sysVer <= (2, 3): # Yes: 'msvcr60', rather than 'msvcrt', is the correct value # on the line below: self.libraries.append('msvcr60') - elif sysVer in ((2,4), (2,5)): + elif sysVer in ((2, 4), (2, 5)): self.libraries.append('msvcr71') # Beyond Python 2.5, we take our chances on the default C runtime # library, because we don't know what compiler those future # versions of Python will use. - for exten in ext: # ext is a global list of Extension objects - exten.extra_compile_args.extend(extra_compiler_args) + for extension in ext: # ext is a global list of Extension objects + extension.extra_compile_args.extend(extra_compiler_args) # End of add-compiler-specific arguments section. self.libraries.append("ws2_32") self.libraries.append("advapi32") - if compiler_is_msvc: + if self.compiler_is_msvc(): # MSVC requires an explicit "libpq" self.libraries.remove("pq") self.libraries.append("secur32") @@ -181,9 +335,10 @@ if os.path.isfile(os.path.join(path, "ms", "libpq.lib")): self.library_dirs.append(os.path.join(path, "ms")) break - if have_ssl: + if self.have_ssl: self.libraries.append("libeay32") self.libraries.append("ssleay32") + self.libraries.append("crypt32") self.libraries.append("user32") self.libraries.append("gdi32") @@ -192,31 +347,44 @@ self.libraries.append('ssl') self.libraries.append('crypto') + def finalize_linux2(self): + """Finalize build system configuration on GNU/Linux platform.""" + # tell piro that GCC is fine and dandy, but not so MS compilers + for extension in self.extensions: + extension.extra_compile_args.append( + '-Wdeclaration-after-statement') + + finalize_linux3 = finalize_linux2 + def finalize_options(self): """Complete the build system configuation.""" build_ext.finalize_options(self) + pg_config_helper = PostgresConfig(self) + self.include_dirs.append(".") - if static_libpq: - if not self.link_objects: self.link_objects = [] + if self.static_libpq: + if not getattr(self, 'link_objects', None): + self.link_objects = [] self.link_objects.append( - os.path.join(self.get_pg_config("libdir"), "libpq.a")) + os.path.join(pg_config_helper.query("libdir"), "libpq.a")) else: self.libraries.append("pq") try: - self.library_dirs.append(self.get_pg_config("libdir")) - self.include_dirs.append(self.get_pg_config("includedir")) - self.include_dirs.append(self.get_pg_config("includedir-server")) + self.library_dirs.append(pg_config_helper.query("libdir")) + self.include_dirs.append(pg_config_helper.query("includedir")) + self.include_dirs.append(pg_config_helper.query("includedir-server")) try: # Here we take a conservative approach: we suppose that # *at least* PostgreSQL 7.4 is available (this is the only # 7.x series supported by psycopg 2) - pgversion = self.get_pg_config("version").split()[1] + pgversion = pg_config_helper.query("version").split()[1] except: pgversion = "7.4.0" - verre = re.compile(r"(\d+)\.(\d+)(?:(?:\.(\d+))|(devel|(beta|rc)\d+))") + verre = re.compile( + r"(\d+)\.(\d+)(?:(?:\.(\d+))|(devel|(alpha|beta|rc)\d+))") m = verre.match(pgversion) if m: pgmajor, pgminor, pgpatch = m.group(1, 2, 3) @@ -230,116 +398,59 @@ define_macros.append(("PG_VERSION_HEX", "0x%02X%02X%02X" % (int(pgmajor), int(pgminor), int(pgpatch)))) - except Warning, w: - if self.pg_config == self.DEFAULT_PG_CONFIG: - sys.stderr.write("Warning: %s" % str(w)) - else: - sys.stderr.write("Error: %s" % str(w)) - sys.exit(1) + except Warning: + w = sys.exc_info()[1] # work around py 2/3 different syntax + sys.stderr.write("Error: %s\n" % w) + sys.exit(1) if hasattr(self, "finalize_" + sys.platform): getattr(self, "finalize_" + sys.platform)() - def autodetect_pg_config_path(self): - res = None - - if PLATFORM_IS_WINDOWS: - res = self.autodetect_pg_config_path_windows() - - return res or self.DEFAULT_PG_CONFIG - - def autodetect_pg_config_path_windows(self): - # Find the first PostgreSQL installation listed in the registry and - # return the full path to its pg_config utility. - # - # This autodetection is performed *only* if the following conditions - # hold: - # - # 1) The pg_config utility is not already available on the PATH: - if os.popen('pg_config').close() is None: # .close()->None == success - return None - # 2) The user has not specified any of the following settings in - # setup.cfg: - # - pg_config - # - include_dirs - # - library_dirs - for settingName in ('pg_config', 'include_dirs', 'library_dirs'): - try: - val = parser.get('build_ext', settingName) - except ConfigParser.NoOptionError: - pass - else: - if val.strip() != '': - return None - # end of guard conditions - - import _winreg - - pg_inst_base_dir = None - pg_config_path = None - - reg = _winreg.ConnectRegistry(None, _winreg.HKEY_LOCAL_MACHINE) - try: - pg_inst_list_key = _winreg.OpenKey(reg, - 'SOFTWARE\\PostgreSQL\\Installations' - ) - except EnvironmentError: - pg_inst_list_key = None - - if pg_inst_list_key is not None: - try: - # Determine the name of the first subkey, if any: - try: - first_sub_key_name = _winreg.EnumKey(pg_inst_list_key, 0) - except EnvironmentError: - first_sub_key_name = None - - if first_sub_key_name is not None: - pg_first_inst_key = _winreg.OpenKey(reg, - 'SOFTWARE\\PostgreSQL\\Installations\\' - + first_sub_key_name - ) - try: - pg_inst_base_dir = _winreg.QueryValueEx( - pg_first_inst_key, 'Base Directory' - )[0] - finally: - _winreg.CloseKey(pg_first_inst_key) - finally: - _winreg.CloseKey(pg_inst_list_key) - - if pg_inst_base_dir and os.path.exists(pg_inst_base_dir): - pg_config_path = os.path.join(pg_inst_base_dir, 'bin', - 'pg_config.exe' - ) - # Support unicode paths, if this version of Python provides the - # necessary infrastructure: - if hasattr(sys, 'getfilesystemencoding'): - pg_config_path = pg_config_path.encode( - sys.getfilesystemencoding() - ) - - return pg_config_path # let's start with macro definitions (the ones not already in setup.cfg) define_macros = [] include_dirs = [] # gather information to build the extension module -ext = [] ; data_files = [] +ext = [] +data_files = [] # sources sources = [ - 'psycopgmodule.c', 'pqpath.c', 'typecast.c', + 'psycopgmodule.c', + 'green.c', 'pqpath.c', 'utils.c', 'bytes_format.c', + + 'connection_int.c', 'connection_type.c', + 'cursor_int.c', 'cursor_type.c', + 'lobject_int.c', 'lobject_type.c', + 'notify_type.c', 'xid_type.c', + + 'adapter_asis.c', 'adapter_binary.c', 'adapter_datetime.c', + 'adapter_list.c', 'adapter_pboolean.c', 'adapter_pdecimal.c', + 'adapter_pint.c', 'adapter_pfloat.c', 'adapter_qstring.c', 'microprotocols.c', 'microprotocols_proto.c', - 'connection_type.c', 'connection_int.c', 'cursor_type.c', 'cursor_int.c', - 'lobject_type.c', 'lobject_int.c', - 'adapter_qstring.c', 'adapter_pboolean.c', 'adapter_binary.c', - 'adapter_asis.c', 'adapter_list.c', 'adapter_datetime.c', 'adapter_pfloat.c', - 'utils.c'] + 'typecast.c', +] + +depends = [ + # headers + 'config.h', 'pgtypes.h', 'psycopg.h', 'python.h', + 'connection.h', 'cursor.h', 'green.h', 'lobject.h', + 'notify.h', 'pqpath.h', 'xid.h', + + 'adapter_asis.h', 'adapter_binary.h', 'adapter_datetime.h', + 'adapter_list.h', 'adapter_pboolean.h', 'adapter_pdecimal.h', + 'adapter_pint.h', 'adapter_pfloat.h', 'adapter_qstring.h', + 'microprotocols.h', 'microprotocols_proto.h', + 'typecast.h', 'typecast_binary.h', + + # included sources + 'typecast_array.c', 'typecast_basic.c', 'typecast_binary.c', + 'typecast_builtins.c', 'typecast_datetime.c', +] -parser = ConfigParser.ConfigParser() +parser = configparser.ConfigParser() parser.read('setup.cfg') # Choose a datetime module @@ -353,43 +464,47 @@ else: mxincludedir = os.path.join(get_python_inc(plat_specific=1), "mx") if os.path.exists(mxincludedir): + # Build the support for mx: we will check at runtime if it can be imported include_dirs.append(mxincludedir) - define_macros.append(('HAVE_MXDATETIME','1')) + define_macros.append(('HAVE_MXDATETIME', '1')) sources.append('adapter_mxdatetime.c') + depends.extend(['adapter_mxdatetime.h', 'typecast_mxdatetime.c']) have_mxdatetime = True version_flags.append('mx') # now decide which package will be the default for date/time typecasts if have_pydatetime and (use_pydatetime or not have_mxdatetime): - define_macros.append(('PSYCOPG_DEFAULT_PYDATETIME','1')) + define_macros.append(('PSYCOPG_DEFAULT_PYDATETIME', '1')) elif have_mxdatetime: - define_macros.append(('PSYCOPG_DEFAULT_MXDATETIME','1')) + define_macros.append(('PSYCOPG_DEFAULT_MXDATETIME', '1')) else: - def e(msg): - sys.stderr.write("error: " + msg + "\n") - e("psycopg requires a datetime module:") - e(" mx.DateTime module not found") - e(" python datetime module not found") - e("Note that psycopg needs the module headers and not just the module") - e("itself. If you installed Python or mx.DateTime from a binary package") - e("you probably need to install its companion -dev or -devel package.") + error_message = """\ +psycopg requires a datetime module: + mx.DateTime module not found + python datetime module not found + +Note that psycopg needs the module headers and not just the module +itself. If you installed Python or mx.DateTime from a binary package +you probably need to install its companion -dev or -devel package.""" + + for line in error_message.split("\n"): + sys.stderr.write("error: " + line) sys.exit(1) # generate a nice version string to avoid confusion when users report bugs +version_flags.append('pq3') # no more a choice for have in parser.get('build_ext', 'define').split(','): if have == 'PSYCOPG_EXTENSIONS': version_flags.append('ext') - elif have == 'HAVE_PQPROTOCOL3': - version_flags.append('pq3') if version_flags: PSYCOPG_VERSION_EX = PSYCOPG_VERSION + " (%s)" % ' '.join(version_flags) else: PSYCOPG_VERSION_EX = PSYCOPG_VERSION if not PLATFORM_IS_WINDOWS: - define_macros.append(('PSYCOPG_VERSION', '"'+PSYCOPG_VERSION_EX+'"')) + define_macros.append(('PSYCOPG_VERSION', '"' + PSYCOPG_VERSION_EX + '"')) else: - define_macros.append(('PSYCOPG_VERSION', '\\"'+PSYCOPG_VERSION_EX+'\\"')) + define_macros.append(('PSYCOPG_VERSION', '\\"' + PSYCOPG_VERSION_EX + '\\"')) if parser.has_option('build_ext', 'have_ssl'): have_ssl = int(parser.get('build_ext', 'have_ssl')) @@ -403,28 +518,40 @@ # build the extension -sources = map(lambda x: os.path.join('psycopg', x), sources) +sources = [ os.path.join('psycopg', x) for x in sources] +depends = [ os.path.join('psycopg', x) for x in depends] ext.append(Extension("psycopg2._psycopg", sources, define_macros=define_macros, include_dirs=include_dirs, + depends=depends, undef_macros=[])) + +# Compute the direct download url. +# Note that the current package installation programs are stupidly intelligent +# and will try to install a beta if they find a link in the homepage instead of +# using these pretty metadata. But that's their problem, not ours. +download_url = ( + "http://initd.org/psycopg/tarballs/PSYCOPG-%s/psycopg2-%s.tar.gz" + % ('-'.join(PSYCOPG_VERSION.split('.')[:2]), PSYCOPG_VERSION)) + setup(name="psycopg2", version=PSYCOPG_VERSION, maintainer="Federico Di Gregorio", 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=download_url, license="GPL with exceptions or ZPL", - platforms = ["any"], + platforms=["any"], description=__doc__.split("\n")[0], long_description="\n".join(__doc__.split("\n")[2:]), - classifiers=filter(None, classifiers.split("\n")), + classifiers=[x for x in classifiers.split("\n") if x], data_files=data_files, - package_dir={'psycopg2':'lib'}, - packages=['psycopg2'], - cmdclass={ 'build_ext': psycopg_build_ext }, + package_dir={'psycopg2': 'lib', 'psycopg2.tests': 'tests'}, + packages=['psycopg2', 'psycopg2.tests'], + cmdclass={ + 'build_ext': psycopg_build_ext, + 'build_py': build_py, }, ext_modules=ext) - diff -Nru psycopg2-2.0.13/tests/bugX000.py psycopg2-2.4.5/tests/bugX000.py --- psycopg2-2.0.13/tests/bugX000.py 2009-04-26 12:09:21.000000000 +0000 +++ psycopg2-2.4.5/tests/bugX000.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,21 +0,0 @@ -#!/usr/bin/env python -import psycopg2 -import time -import unittest - -class DateTimeAllocationBugTestCase(unittest.TestCase): - def test_date_time_allocation_bug(self): - d1 = psycopg2.Date(2002,12,25) - d2 = psycopg2.DateFromTicks(time.mktime((2002,12,25,0,0,0,0,0,0))) - t1 = psycopg2.Time(13,45,30) - t2 = psycopg2.TimeFromTicks(time.mktime((2001,1,1,13,45,30,0,0,0))) - t1 = psycopg2.Timestamp(2002,12,25,13,45,30) - t2 = psycopg2.TimestampFromTicks( - time.mktime((2002,12,25,13,45,30,0,0,0))) - - -def test_suite(): - return unittest.TestLoader().loadTestsFromName(__name__) - -if __name__ == "__main__": - unittest.main() diff -Nru psycopg2-2.0.13/tests/dbapi20.py psycopg2-2.4.5/tests/dbapi20.py --- psycopg2-2.0.13/tests/dbapi20.py 2009-04-26 12:09:21.000000000 +0000 +++ psycopg2-2.4.5/tests/dbapi20.py 2012-03-28 21:09:15.000000000 +0000 @@ -11,14 +11,25 @@ -- Ian Bicking ''' -__rcs_id__ = '$Id: dbapi20.py,v 1.10 2003/10/09 03:14:14 zenzen Exp $' -__version__ = '$Revision: 1.10 $'[11:-2] -__author__ = 'Stuart Bishop ' +__rcs_id__ = '$Id: dbapi20.py,v 1.11 2005/01/02 02:41:01 zenzen Exp $' +__version__ = '$Revision: 1.12 $'[11:-2] +__author__ = 'Stuart Bishop ' import unittest import time +import sys -# $Log: dbapi20.py,v $ + +# Revision 1.12 2009/02/06 03:35:11 kf7xm +# Tested okay with Python 3.0, includes last minute patches from Mark H. +# +# Revision 1.1.1.1.2.1 2008/09/20 19:54:59 rupole +# Include latest changes from main branch +# Updates for py3k +# +# Revision 1.11 2005/01/02 02:41:01 zenzen +# Update author email address +# # Revision 1.10 2003/10/09 03:14:14 zenzen # Add test for DB API 2.0 optional extension, where database exceptions # are exposed as attributes on the Connection object. @@ -60,6 +71,10 @@ # nothing # - Fix bugs in test_setoutputsize_basic and test_setinputsizes # +def str2bytes(sval): + if sys.version_info < (3,0) and isinstance(sval, str): + sval = sval.decode("latin1") + return sval.encode("latin1") class DatabaseAPI20Test(unittest.TestCase): ''' Test a database self.driver for DB API 2.0 compatibility. @@ -174,8 +189,13 @@ def test_Exceptions(self): # Make sure required exceptions exist, and are in the # defined heirarchy. - self.failUnless(issubclass(self.driver.Warning,StandardError)) - self.failUnless(issubclass(self.driver.Error,StandardError)) + if sys.version[0] == '3': #under Python 3 StardardError no longer exists + self.failUnless(issubclass(self.driver.Warning,Exception)) + self.failUnless(issubclass(self.driver.Error,Exception)) + else: + self.failUnless(issubclass(self.driver.Warning,StandardError)) + self.failUnless(issubclass(self.driver.Error,StandardError)) + self.failUnless( issubclass(self.driver.InterfaceError,self.driver.Error) ) @@ -360,7 +380,9 @@ self.assertRaises(self.driver.Error,con.commit) # connection.close should raise an Error if called more than once - self.assertRaises(self.driver.Error,con.close) + # Issue discussed on DB-SIG: consensus seem that close() should not + # raised if called on closed objects. Issue reported back to Stuart. + # self.assertRaises(self.driver.Error,con.close) def test_execute(self): con = self._connect() @@ -703,7 +725,7 @@ that returns two result sets, first the number of rows in booze then "name from booze" ''' - raise NotImplementedError,'Helper not implemented' + raise NotImplementedError('Helper not implemented') #sql=""" # create procedure deleteme as # begin @@ -715,7 +737,7 @@ def help_nextset_tearDown(self,cur): 'If cleaning up is needed after nextSetTest' - raise NotImplementedError,'Helper not implemented' + raise NotImplementedError('Helper not implemented') #cur.execute("drop procedure deleteme") def test_nextset(self): @@ -748,7 +770,7 @@ con.close() def test_nextset(self): - raise NotImplementedError,'Drivers need to override this test' + raise NotImplementedError('Drivers need to override this test') def test_arraysize(self): # Not much here - rest of the tests for this are in test_fetchmany @@ -783,7 +805,7 @@ def test_setoutputsize(self): # Real test for setoutputsize is driver dependant - raise NotImplementedError,'Driver need to override this test' + raise NotImplementedError('Driver needed to override this test') def test_None(self): con = self._connect() @@ -820,8 +842,8 @@ # self.assertEqual(str(t1),str(t2)) def test_Binary(self): - b = self.driver.Binary('Something') - b = self.driver.Binary('') + b = self.driver.Binary(str2bytes('Something')) + b = self.driver.Binary(str2bytes('')) def test_STRING(self): self.failUnless(hasattr(self.driver,'STRING'), diff -Nru psycopg2-2.0.13/tests/dbapi20_tpc.py psycopg2-2.4.5/tests/dbapi20_tpc.py --- psycopg2-2.0.13/tests/dbapi20_tpc.py 1970-01-01 00:00:00.000000000 +0000 +++ psycopg2-2.4.5/tests/dbapi20_tpc.py 2011-02-06 15:58:34.000000000 +0000 @@ -0,0 +1,144 @@ +""" Python DB API 2.0 driver Two Phase Commit compliance test suite. + +""" + +import unittest + + +class TwoPhaseCommitTests(unittest.TestCase): + + driver = None + + def connect(self): + """Make a database connection.""" + raise NotImplementedError + + _last_id = 0 + _global_id_prefix = "dbapi20_tpc:" + + def make_xid(self, con): + id = TwoPhaseCommitTests._last_id + TwoPhaseCommitTests._last_id += 1 + return con.xid(42, "%s%d" % (self._global_id_prefix, id), "qualifier") + + def test_xid(self): + con = self.connect() + try: + xid = con.xid(42, "global", "bqual") + except self.driver.NotSupportedError: + self.fail("Driver does not support transaction IDs.") + + self.assertEquals(xid[0], 42) + self.assertEquals(xid[1], "global") + self.assertEquals(xid[2], "bqual") + + # Try some extremes for the transaction ID: + xid = con.xid(0, "", "") + self.assertEquals(tuple(xid), (0, "", "")) + xid = con.xid(0x7fffffff, "a" * 64, "b" * 64) + self.assertEquals(tuple(xid), (0x7fffffff, "a" * 64, "b" * 64)) + + def test_tpc_begin(self): + con = self.connect() + try: + xid = self.make_xid(con) + try: + con.tpc_begin(xid) + except self.driver.NotSupportedError: + self.fail("Driver does not support tpc_begin()") + finally: + con.close() + + def test_tpc_commit_without_prepare(self): + con = self.connect() + try: + xid = self.make_xid(con) + con.tpc_begin(xid) + cursor = con.cursor() + cursor.execute("SELECT 1") + con.tpc_commit() + finally: + con.close() + + def test_tpc_rollback_without_prepare(self): + con = self.connect() + try: + xid = self.make_xid(con) + con.tpc_begin(xid) + cursor = con.cursor() + cursor.execute("SELECT 1") + con.tpc_rollback() + finally: + con.close() + + def test_tpc_commit_with_prepare(self): + con = self.connect() + try: + xid = self.make_xid(con) + con.tpc_begin(xid) + cursor = con.cursor() + cursor.execute("SELECT 1") + con.tpc_prepare() + con.tpc_commit() + finally: + con.close() + + def test_tpc_rollback_with_prepare(self): + con = self.connect() + try: + xid = self.make_xid(con) + con.tpc_begin(xid) + cursor = con.cursor() + cursor.execute("SELECT 1") + con.tpc_prepare() + con.tpc_rollback() + finally: + con.close() + + def test_tpc_begin_in_transaction_fails(self): + con = self.connect() + try: + xid = self.make_xid(con) + + cursor = con.cursor() + cursor.execute("SELECT 1") + self.assertRaises(self.driver.ProgrammingError, + con.tpc_begin, xid) + finally: + con.close() + + def test_tpc_begin_in_tpc_transaction_fails(self): + con = self.connect() + try: + xid = self.make_xid(con) + + cursor = con.cursor() + cursor.execute("SELECT 1") + self.assertRaises(self.driver.ProgrammingError, + con.tpc_begin, xid) + finally: + con.close() + + def test_commit_in_tpc_fails(self): + # calling commit() within a TPC transaction fails with + # ProgrammingError. + con = self.connect() + try: + xid = self.make_xid(con) + con.tpc_begin(xid) + + self.assertRaises(self.driver.ProgrammingError, con.commit) + finally: + con.close() + + def test_rollback_in_tpc_fails(self): + # calling rollback() within a TPC transaction fails with + # ProgrammingError. + con = self.connect() + try: + xid = self.make_xid(con) + con.tpc_begin(xid) + + self.assertRaises(self.driver.ProgrammingError, con.rollback) + finally: + con.close() diff -Nru psycopg2-2.0.13/tests/extras_dictcursor.py psycopg2-2.4.5/tests/extras_dictcursor.py --- psycopg2-2.0.13/tests/extras_dictcursor.py 2009-10-04 21:32:35.000000000 +0000 +++ psycopg2-2.4.5/tests/extras_dictcursor.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,111 +0,0 @@ -#!/usr/bin/env python -# extras_dictcursor - test if DictCursor extension class works -# -# Copyright (C) 2004 Federico Di Gregorio -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by the -# Free Software Foundation; either version 2, or (at your option) any later -# version. -# -# This program is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY -# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# for more details. - -import psycopg2 -import psycopg2.extras -import unittest - -import tests - - -class ExtrasDictCursorTests(unittest.TestCase): - """Test if DictCursor extension class works.""" - - def setUp(self): - self.conn = psycopg2.connect(tests.dsn) - curs = self.conn.cursor() - curs.execute("CREATE TEMPORARY TABLE ExtrasDictCursorTests (foo text)") - curs.execute("INSERT INTO ExtrasDictCursorTests VALUES ('bar')") - self.conn.commit() - - def tearDown(self): - self.conn.close() - - def testDictCursorWithPlainCursorFetchOne(self): - self._testWithPlainCursor(lambda curs: curs.fetchone()) - - def testDictCursorWithPlainCursorFetchMany(self): - self._testWithPlainCursor(lambda curs: curs.fetchmany(100)[0]) - - def testDictCursorWithPlainCursorFetchAll(self): - self._testWithPlainCursor(lambda curs: curs.fetchall()[0]) - - def testDictCursorWithPlainCursorIter(self): - def getter(curs): - for row in curs: - return row - self._testWithPlainCursor(getter) - - def testDictCursorWithPlainCursorRealFetchOne(self): - self._testWithPlainCursorReal(lambda curs: curs.fetchone()) - - def testDictCursorWithPlainCursorRealFetchMany(self): - self._testWithPlainCursorReal(lambda curs: curs.fetchmany(100)[0]) - - def testDictCursorWithPlainCursorRealFetchAll(self): - self._testWithPlainCursorReal(lambda curs: curs.fetchall()[0]) - - def testDictCursorWithPlainCursorRealIter(self): - def getter(curs): - for row in curs: - return row - self._testWithPlainCursorReal(getter) - - def testDictCursorWithNamedCursorFetchOne(self): - self._testWithNamedCursor(lambda curs: curs.fetchone()) - - def testDictCursorWithNamedCursorFetchMany(self): - self._testWithNamedCursor(lambda curs: curs.fetchmany(100)[0]) - - def testDictCursorWithNamedCursorFetchAll(self): - self._testWithNamedCursor(lambda curs: curs.fetchall()[0]) - - def testDictCursorWithNamedCursorIter(self): - def getter(curs): - for row in curs: - return row - self._testWithNamedCursor(getter) - - def _testWithPlainCursor(self, getter): - curs = self.conn.cursor(cursor_factory=psycopg2.extras.DictCursor) - curs.execute("SELECT * FROM ExtrasDictCursorTests") - row = getter(curs) - self.failUnless(row['foo'] == 'bar') - self.failUnless(row[0] == 'bar') - - def _testWithNamedCursor(self, getter): - curs = self.conn.cursor('aname', cursor_factory=psycopg2.extras.DictCursor) - curs.execute("SELECT * FROM ExtrasDictCursorTests") - row = getter(curs) - self.failUnless(row['foo'] == 'bar') - self.failUnless(row[0] == 'bar') - - def _testWithPlainCursorReal(self, getter): - curs = self.conn.cursor(cursor_factory=psycopg2.extras.RealDictCursor) - curs.execute("SELECT * FROM ExtrasDictCursorTests") - row = getter(curs) - self.failUnless(row['foo'] == 'bar') - - def _testWithNamedCursorReal(self, getter): - curs = self.conn.cursor('aname', cursor_factory=psycopg2.extras.RealDictCursor) - curs.execute("SELECT * FROM ExtrasDictCursorTests") - row = getter(curs) - self.failUnless(row['foo'] == 'bar') - -def test_suite(): - return unittest.TestLoader().loadTestsFromName(__name__) - -if __name__ == "__main__": - unittest.main() diff -Nru psycopg2-2.0.13/tests/__init__.py psycopg2-2.4.5/tests/__init__.py --- psycopg2-2.0.13/tests/__init__.py 2009-10-04 21:32:35.000000000 +0000 +++ psycopg2-2.4.5/tests/__init__.py 2011-12-11 21:18:11.000000000 +0000 @@ -1,44 +1,81 @@ #!/usr/bin/env python -import os -import unittest -dbname = os.environ.get('PSYCOPG2_TESTDB', 'psycopg2_test') -dbhost = os.environ.get('PSYCOPG2_TESTDB_HOST', None) -dbport = os.environ.get('PSYCOPG2_TESTDB_PORT', None) -dbuser = os.environ.get('PSYCOPG2_TESTDB_USER', None) - -# Construct a DSN to connect to the test database: -dsn = 'dbname=%s' % dbname -if dbhost is not None: - dsn += ' host=%s' % dbhost -if dbport is not None: - dsn += ' port=%s' % dbport -if dbuser is not None: - dsn += ' user=%s' % dbuser +# psycopg2 test suite +# +# Copyright (C) 2007-2011 Federico Di Gregorio +# +# psycopg2 is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# In addition, as a special exception, the copyright holders give +# permission to link this program with the OpenSSL library (or with +# modified versions of OpenSSL that use the same license as OpenSSL), +# and distribute linked combinations including the two. +# +# You must obey the GNU Lesser General Public License in all respects for +# all of the code used other than OpenSSL. +# +# psycopg2 is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +# License for more details. -import bugX000 -import extras_dictcursor +import sys +from testconfig import dsn +from testutils import unittest + +import test_async +import test_bugX000 +import test_bug_gc +import test_cancel +import test_connection +import test_copy +import test_cursor import test_dates +import test_extras_dictcursor +import test_green +import test_lobject +import test_module +import test_notify import test_psycopg2_dbapi20 import test_quote -import test_connection import test_transaction -import types_basic -import types_extras -import test_lobject +import test_types_basic +import test_types_extras def test_suite(): + # If connection to test db fails, bail out early. + import psycopg2 + try: + cnn = psycopg2.connect(dsn) + except Exception, e: + print "Failed connection to test db:", e.__class__.__name__, e + print "Please set env vars 'PSYCOPG2_TESTDB*' to valid values." + sys.exit(1) + else: + cnn.close() + suite = unittest.TestSuite() - suite.addTest(bugX000.test_suite()) - suite.addTest(extras_dictcursor.test_suite()) + suite.addTest(test_async.test_suite()) + suite.addTest(test_bugX000.test_suite()) + suite.addTest(test_bug_gc.test_suite()) + suite.addTest(test_cancel.test_suite()) + suite.addTest(test_connection.test_suite()) + suite.addTest(test_copy.test_suite()) + suite.addTest(test_cursor.test_suite()) suite.addTest(test_dates.test_suite()) + suite.addTest(test_extras_dictcursor.test_suite()) + suite.addTest(test_green.test_suite()) + suite.addTest(test_lobject.test_suite()) + suite.addTest(test_module.test_suite()) + suite.addTest(test_notify.test_suite()) suite.addTest(test_psycopg2_dbapi20.test_suite()) suite.addTest(test_quote.test_suite()) - suite.addTest(test_connection.test_suite()) suite.addTest(test_transaction.test_suite()) - suite.addTest(types_basic.test_suite()) - suite.addTest(types_extras.test_suite()) - suite.addTest(test_lobject.test_suite()) + suite.addTest(test_types_basic.test_suite()) + suite.addTest(test_types_extras.test_suite()) return suite if __name__ == '__main__': diff -Nru psycopg2-2.0.13/tests/test_async.py psycopg2-2.4.5/tests/test_async.py --- psycopg2-2.0.13/tests/test_async.py 1970-01-01 00:00:00.000000000 +0000 +++ psycopg2-2.4.5/tests/test_async.py 2012-03-28 21:09:15.000000000 +0000 @@ -0,0 +1,457 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# test_async.py - unit test for asynchronous API +# +# Copyright (C) 2010-2011 Jan Urbański +# +# psycopg2 is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# In addition, as a special exception, the copyright holders give +# permission to link this program with the OpenSSL library (or with +# modified versions of OpenSSL that use the same license as OpenSSL), +# and distribute linked combinations including the two. +# +# You must obey the GNU Lesser General Public License in all respects for +# all of the code used other than OpenSSL. +# +# psycopg2 is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +# License for more details. + +from testutils import unittest, skip_before_postgres + +import psycopg2 +from psycopg2 import extensions + +import time +import select +import StringIO + +from testconfig import dsn + +class PollableStub(object): + """A 'pollable' wrapper allowing analysis of the `poll()` calls.""" + def __init__(self, pollable): + self.pollable = pollable + self.polls = [] + + def fileno(self): + return self.pollable.fileno() + + def poll(self): + rv = self.pollable.poll() + self.polls.append(rv) + return rv + + +class AsyncTests(unittest.TestCase): + + def setUp(self): + self.sync_conn = psycopg2.connect(dsn) + self.conn = psycopg2.connect(dsn, async=True) + + self.wait(self.conn) + + curs = self.conn.cursor() + curs.execute(''' + CREATE TEMPORARY TABLE table1 ( + id int PRIMARY KEY + )''') + self.wait(curs) + + def tearDown(self): + self.sync_conn.close() + self.conn.close() + + def wait(self, cur_or_conn): + pollable = cur_or_conn + if not hasattr(pollable, 'poll'): + pollable = cur_or_conn.connection + while True: + state = pollable.poll() + if state == psycopg2.extensions.POLL_OK: + break + elif state == psycopg2.extensions.POLL_READ: + select.select([pollable], [], []) + elif state == psycopg2.extensions.POLL_WRITE: + select.select([], [pollable], []) + else: + raise Exception("Unexpected result from poll: %r", state) + + def test_connection_setup(self): + cur = self.conn.cursor() + sync_cur = self.sync_conn.cursor() + + self.assert_(self.conn.async) + self.assert_(not self.sync_conn.async) + + # the async connection should be in isolevel 0 + self.assertEquals(self.conn.isolation_level, 0) + + # check other properties to be found on the connection + self.assert_(self.conn.server_version) + self.assert_(self.conn.protocol_version in (2,3)) + self.assert_(self.conn.encoding in psycopg2.extensions.encodings) + + def test_async_named_cursor(self): + self.assertRaises(psycopg2.ProgrammingError, + self.conn.cursor, "name") + + def test_async_select(self): + cur = self.conn.cursor() + self.assertFalse(self.conn.isexecuting()) + cur.execute("select 'a'") + self.assertTrue(self.conn.isexecuting()) + + self.wait(cur) + + self.assertFalse(self.conn.isexecuting()) + self.assertEquals(cur.fetchone()[0], "a") + + @skip_before_postgres(8, 2) + def test_async_callproc(self): + cur = self.conn.cursor() + cur.callproc("pg_sleep", (0.1, )) + self.assertTrue(self.conn.isexecuting()) + + self.wait(cur) + self.assertFalse(self.conn.isexecuting()) + self.assertEquals(cur.fetchall()[0][0], '') + + def test_async_after_async(self): + cur = self.conn.cursor() + cur2 = self.conn.cursor() + + cur.execute("insert into table1 values (1)") + + # an async execute after an async one raises an exception + self.assertRaises(psycopg2.ProgrammingError, + cur.execute, "select * from table1") + # same for callproc + self.assertRaises(psycopg2.ProgrammingError, + cur.callproc, "version") + # but after you've waited it should be good + self.wait(cur) + cur.execute("select * from table1") + self.wait(cur) + + self.assertEquals(cur.fetchall()[0][0], 1) + + cur.execute("delete from table1") + self.wait(cur) + + cur.execute("select * from table1") + self.wait(cur) + + self.assertEquals(cur.fetchone(), None) + + def test_fetch_after_async(self): + cur = self.conn.cursor() + cur.execute("select 'a'") + + # a fetch after an asynchronous query should raise an error + self.assertRaises(psycopg2.ProgrammingError, + cur.fetchall) + # but after waiting it should work + self.wait(cur) + self.assertEquals(cur.fetchall()[0][0], "a") + + def test_rollback_while_async(self): + cur = self.conn.cursor() + + cur.execute("select 'a'") + + # a rollback should not work in asynchronous mode + self.assertRaises(psycopg2.ProgrammingError, self.conn.rollback) + + def test_commit_while_async(self): + cur = self.conn.cursor() + + cur.execute("begin") + self.wait(cur) + + cur.execute("insert into table1 values (1)") + + # a commit should not work in asynchronous mode + self.assertRaises(psycopg2.ProgrammingError, self.conn.commit) + self.assertTrue(self.conn.isexecuting()) + + # but a manual commit should + self.wait(cur) + cur.execute("commit") + self.wait(cur) + + cur.execute("select * from table1") + self.wait(cur) + self.assertEquals(cur.fetchall()[0][0], 1) + + cur.execute("delete from table1") + self.wait(cur) + + cur.execute("select * from table1") + self.wait(cur) + self.assertEquals(cur.fetchone(), None) + + def test_set_parameters_while_async(self): + cur = self.conn.cursor() + + cur.execute("select 'c'") + self.assertTrue(self.conn.isexecuting()) + + # getting transaction status works + self.assertEquals(self.conn.get_transaction_status(), + extensions.TRANSACTION_STATUS_ACTIVE) + self.assertTrue(self.conn.isexecuting()) + + # setting connection encoding should fail + self.assertRaises(psycopg2.ProgrammingError, + self.conn.set_client_encoding, "LATIN1") + + # same for transaction isolation + self.assertRaises(psycopg2.ProgrammingError, + self.conn.set_isolation_level, 1) + + def test_reset_while_async(self): + cur = self.conn.cursor() + cur.execute("select 'c'") + self.assertTrue(self.conn.isexecuting()) + + # a reset should fail + self.assertRaises(psycopg2.ProgrammingError, self.conn.reset) + + def test_async_iter(self): + cur = self.conn.cursor() + + cur.execute("begin") + self.wait(cur) + cur.execute(""" + insert into table1 values (1); + insert into table1 values (2); + insert into table1 values (3); + """) + self.wait(cur) + cur.execute("select id from table1 order by id") + + # iteration fails if a query is underway + self.assertRaises(psycopg2.ProgrammingError, list, cur) + + # but after it's done it should work + self.wait(cur) + self.assertEquals(list(cur), [(1, ), (2, ), (3, )]) + self.assertFalse(self.conn.isexecuting()) + + def test_copy_while_async(self): + cur = self.conn.cursor() + cur.execute("select 'a'") + + # copy should fail + self.assertRaises(psycopg2.ProgrammingError, + cur.copy_from, + StringIO.StringIO("1\n3\n5\n\\.\n"), "table1") + + def test_lobject_while_async(self): + # large objects should be prohibited + self.assertRaises(psycopg2.ProgrammingError, + self.conn.lobject) + + def test_async_executemany(self): + cur = self.conn.cursor() + self.assertRaises( + psycopg2.ProgrammingError, + cur.executemany, "insert into table1 values (%s)", [1, 2, 3]) + + def test_async_scroll(self): + cur = self.conn.cursor() + cur.execute(""" + insert into table1 values (1); + insert into table1 values (2); + insert into table1 values (3); + """) + self.wait(cur) + cur.execute("select id from table1 order by id") + + # scroll should fail if a query is underway + self.assertRaises(psycopg2.ProgrammingError, cur.scroll, 1) + self.assertTrue(self.conn.isexecuting()) + + # but after it's done it should work + self.wait(cur) + cur.scroll(1) + self.assertEquals(cur.fetchall(), [(2, ), (3, )]) + + cur = self.conn.cursor() + cur.execute("select id from table1 order by id") + self.wait(cur) + + cur2 = self.conn.cursor() + self.assertRaises(psycopg2.ProgrammingError, cur2.scroll, 1) + + self.assertRaises(psycopg2.ProgrammingError, cur.scroll, 4) + + cur = self.conn.cursor() + cur.execute("select id from table1 order by id") + self.wait(cur) + cur.scroll(2) + cur.scroll(-1) + self.assertEquals(cur.fetchall(), [(2, ), (3, )]) + + def test_scroll(self): + cur = self.sync_conn.cursor() + cur.execute("create table table1 (id int)") + cur.execute(""" + insert into table1 values (1); + insert into table1 values (2); + insert into table1 values (3); + """) + cur.execute("select id from table1 order by id") + cur.scroll(2) + cur.scroll(-1) + self.assertEquals(cur.fetchall(), [(2, ), (3, )]) + + def test_async_dont_read_all(self): + cur = self.conn.cursor() + cur.execute("select repeat('a', 10000); select repeat('b', 10000)") + + # fetch the result + self.wait(cur) + + # it should be the result of the second query + self.assertEquals(cur.fetchone()[0], "b" * 10000) + + def test_async_subclass(self): + class MyConn(psycopg2.extensions.connection): + def __init__(self, dsn, async=0): + psycopg2.extensions.connection.__init__(self, dsn, async=async) + + conn = psycopg2.connect(dsn, connection_factory=MyConn, async=True) + self.assert_(isinstance(conn, MyConn)) + self.assert_(conn.async) + conn.close() + + def test_flush_on_write(self): + # a very large query requires a flush loop to be sent to the backend + curs = self.conn.cursor() + for mb in 1, 5, 10, 20, 50: + size = mb * 1024 * 1024 + stub = PollableStub(self.conn) + curs.execute("select %s;", ('x' * size,)) + self.wait(stub) + self.assertEqual(size, len(curs.fetchone()[0])) + if stub.polls.count(psycopg2.extensions.POLL_WRITE) > 1: + return + + # This is more a testing glitch than an error: it happens + # on high load on linux: probably because the kernel has more + # buffers ready. A warning may be useful during development, + # but an error is bad during regression testing. + import warnings + warnings.warn("sending a large query didn't trigger block on write.") + + def test_sync_poll(self): + cur = self.sync_conn.cursor() + cur.execute("select 1") + # polling with a sync query works + cur.connection.poll() + self.assertEquals(cur.fetchone()[0], 1) + + def test_notify(self): + cur = self.conn.cursor() + sync_cur = self.sync_conn.cursor() + + sync_cur.execute("listen test_notify") + self.sync_conn.commit() + cur.execute("notify test_notify") + self.wait(cur) + + self.assertEquals(self.sync_conn.notifies, []) + + pid = self.conn.get_backend_pid() + for _ in range(5): + self.wait(self.sync_conn) + if not self.sync_conn.notifies: + time.sleep(0.5) + continue + self.assertEquals(len(self.sync_conn.notifies), 1) + self.assertEquals(self.sync_conn.notifies.pop(), + (pid, "test_notify")) + return + self.fail("No NOTIFY in 2.5 seconds") + + def test_async_fetch_wrong_cursor(self): + cur1 = self.conn.cursor() + cur2 = self.conn.cursor() + cur1.execute("select 1") + + self.wait(cur1) + self.assertFalse(self.conn.isexecuting()) + # fetching from a cursor with no results is an error + self.assertRaises(psycopg2.ProgrammingError, cur2.fetchone) + # fetching from the correct cursor works + self.assertEquals(cur1.fetchone()[0], 1) + + def test_error(self): + cur = self.conn.cursor() + cur.execute("insert into table1 values (%s)", (1, )) + self.wait(cur) + cur.execute("insert into table1 values (%s)", (1, )) + # this should fail + self.assertRaises(psycopg2.IntegrityError, self.wait, cur) + cur.execute("insert into table1 values (%s); " + "insert into table1 values (%s)", (2, 2)) + # this should fail as well + self.assertRaises(psycopg2.IntegrityError, self.wait, cur) + # but this should work + cur.execute("insert into table1 values (%s)", (2, )) + self.wait(cur) + # and the cursor should be usable afterwards + cur.execute("insert into table1 values (%s)", (3, )) + self.wait(cur) + cur.execute("select * from table1 order by id") + self.wait(cur) + self.assertEquals(cur.fetchall(), [(1, ), (2, ), (3, )]) + cur.execute("delete from table1") + self.wait(cur) + + def test_error_two_cursors(self): + cur = self.conn.cursor() + cur2 = self.conn.cursor() + cur.execute("select * from no_such_table") + self.assertRaises(psycopg2.ProgrammingError, self.wait, cur) + cur2.execute("select 1") + self.wait(cur2) + self.assertEquals(cur2.fetchone()[0], 1) + + def test_notices(self): + del self.conn.notices[:] + cur = self.conn.cursor() + cur.execute("create temp table chatty (id serial primary key);") + self.wait(cur) + self.assertEqual("CREATE TABLE", cur.statusmessage) + self.assert_(self.conn.notices) + + def test_async_cursor_gone(self): + import gc + cur = self.conn.cursor() + cur.execute("select 42;"); + del cur + gc.collect() + self.assertRaises(psycopg2.InterfaceError, self.wait, self.conn) + + # The connection is still usable + cur = self.conn.cursor() + cur.execute("select 42;"); + self.wait(self.conn) + self.assertEqual(cur.fetchone(), (42,)) + + +def test_suite(): + return unittest.TestLoader().loadTestsFromName(__name__) + +if __name__ == "__main__": + unittest.main() + diff -Nru psycopg2-2.0.13/tests/test_bug_gc.py psycopg2-2.4.5/tests/test_bug_gc.py --- psycopg2-2.0.13/tests/test_bug_gc.py 1970-01-01 00:00:00.000000000 +0000 +++ psycopg2-2.4.5/tests/test_bug_gc.py 2011-12-11 21:18:11.000000000 +0000 @@ -0,0 +1,57 @@ +#!/usr/bin/env python + +# bug_gc.py - test for refcounting/GC bug +# +# Copyright (C) 2010-2011 Federico Di Gregorio +# +# psycopg2 is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# In addition, as a special exception, the copyright holders give +# permission to link this program with the OpenSSL library (or with +# modified versions of OpenSSL that use the same license as OpenSSL), +# and distribute linked combinations including the two. +# +# You must obey the GNU Lesser General Public License in all respects for +# all of the code used other than OpenSSL. +# +# psycopg2 is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +# License for more details. + +import psycopg2 +import psycopg2.extensions +import time +import unittest +import gc + +from testconfig import dsn + +from testutils import skip_if_no_uuid + +class StolenReferenceTestCase(unittest.TestCase): + def setUp(self): + self.conn = psycopg2.connect(dsn) + + def tearDown(self): + self.conn.close() + + @skip_if_no_uuid + def test_stolen_reference_bug(self): + def fish(val, cur): + gc.collect() + return 42 + UUID = psycopg2.extensions.new_type((2950,), "UUID", fish) + psycopg2.extensions.register_type(UUID, self.conn) + curs = self.conn.cursor() + curs.execute("select 'b5219e01-19ab-4994-b71e-149225dc51e4'::uuid") + curs.fetchone() + +def test_suite(): + return unittest.TestLoader().loadTestsFromName(__name__) + +if __name__ == "__main__": + unittest.main() diff -Nru psycopg2-2.0.13/tests/test_bugX000.py psycopg2-2.4.5/tests/test_bugX000.py --- psycopg2-2.0.13/tests/test_bugX000.py 1970-01-01 00:00:00.000000000 +0000 +++ psycopg2-2.4.5/tests/test_bugX000.py 2011-12-11 21:18:11.000000000 +0000 @@ -0,0 +1,44 @@ +#!/usr/bin/env python + +# bugX000.py - test for DateTime object allocation bug +# +# Copyright (C) 2007-2011 Federico Di Gregorio +# +# psycopg2 is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# In addition, as a special exception, the copyright holders give +# permission to link this program with the OpenSSL library (or with +# modified versions of OpenSSL that use the same license as OpenSSL), +# and distribute linked combinations including the two. +# +# You must obey the GNU Lesser General Public License in all respects for +# all of the code used other than OpenSSL. +# +# psycopg2 is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +# License for more details. + +import psycopg2 +import time +import unittest + +class DateTimeAllocationBugTestCase(unittest.TestCase): + def test_date_time_allocation_bug(self): + d1 = psycopg2.Date(2002,12,25) + d2 = psycopg2.DateFromTicks(time.mktime((2002,12,25,0,0,0,0,0,0))) + t1 = psycopg2.Time(13,45,30) + t2 = psycopg2.TimeFromTicks(time.mktime((2001,1,1,13,45,30,0,0,0))) + t1 = psycopg2.Timestamp(2002,12,25,13,45,30) + t2 = psycopg2.TimestampFromTicks( + time.mktime((2002,12,25,13,45,30,0,0,0))) + + +def test_suite(): + return unittest.TestLoader().loadTestsFromName(__name__) + +if __name__ == "__main__": + unittest.main() diff -Nru psycopg2-2.0.13/tests/test_cancel.py psycopg2-2.4.5/tests/test_cancel.py --- psycopg2-2.0.13/tests/test_cancel.py 1970-01-01 00:00:00.000000000 +0000 +++ psycopg2-2.4.5/tests/test_cancel.py 2011-02-27 12:03:48.000000000 +0000 @@ -0,0 +1,114 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# test_cancel.py - unit test for query cancellation +# +# Copyright (C) 2010-2011 Jan Urbański +# +# psycopg2 is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# In addition, as a special exception, the copyright holders give +# permission to link this program with the OpenSSL library (or with +# modified versions of OpenSSL that use the same license as OpenSSL), +# and distribute linked combinations including the two. +# +# You must obey the GNU Lesser General Public License in all respects for +# all of the code used other than OpenSSL. +# +# psycopg2 is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +# License for more details. + +import time +import threading + +import psycopg2 +import psycopg2.extensions +from psycopg2 import extras + +from testconfig import dsn +from testutils import unittest, skip_before_postgres + +class CancelTests(unittest.TestCase): + + def setUp(self): + self.conn = psycopg2.connect(dsn) + cur = self.conn.cursor() + cur.execute(''' + CREATE TEMPORARY TABLE table1 ( + id int PRIMARY KEY + )''') + self.conn.commit() + + def tearDown(self): + self.conn.close() + + def test_empty_cancel(self): + self.conn.cancel() + + @skip_before_postgres(8, 2) + def test_cancel(self): + errors = [] + + def neverending(conn): + cur = conn.cursor() + try: + self.assertRaises(psycopg2.extensions.QueryCanceledError, + cur.execute, "select pg_sleep(60)") + # make sure the connection still works + conn.rollback() + cur.execute("select 1") + self.assertEqual(cur.fetchall(), [(1, )]) + except Exception, e: + errors.append(e) + raise + + def canceller(conn): + cur = conn.cursor() + try: + conn.cancel() + except Exception, e: + errors.append(e) + raise + + thread1 = threading.Thread(target=neverending, args=(self.conn, )) + # wait a bit to make sure that the other thread is already in + # pg_sleep -- ugly and racy, but the chances are ridiculously low + thread2 = threading.Timer(0.3, canceller, args=(self.conn, )) + thread1.start() + thread2.start() + thread1.join() + thread2.join() + + self.assertEqual(errors, []) + + @skip_before_postgres(8, 2) + def test_async_cancel(self): + async_conn = psycopg2.connect(dsn, async=True) + self.assertRaises(psycopg2.OperationalError, async_conn.cancel) + extras.wait_select(async_conn) + cur = async_conn.cursor() + cur.execute("select pg_sleep(10000)") + self.assertTrue(async_conn.isexecuting()) + async_conn.cancel() + self.assertRaises(psycopg2.extensions.QueryCanceledError, + extras.wait_select, async_conn) + cur.execute("select 1") + extras.wait_select(async_conn) + self.assertEqual(cur.fetchall(), [(1, )]) + + def test_async_connection_cancel(self): + async_conn = psycopg2.connect(dsn, async=True) + async_conn.close() + self.assertTrue(async_conn.closed) + + +def test_suite(): + return unittest.TestLoader().loadTestsFromName(__name__) + +if __name__ == "__main__": + unittest.main() diff -Nru psycopg2-2.0.13/tests/testconfig.py psycopg2-2.4.5/tests/testconfig.py --- psycopg2-2.0.13/tests/testconfig.py 1970-01-01 00:00:00.000000000 +0000 +++ psycopg2-2.4.5/tests/testconfig.py 2011-02-13 11:28:18.000000000 +0000 @@ -0,0 +1,36 @@ +# Configure the test suite from the env variables. + +import os + +dbname = os.environ.get('PSYCOPG2_TESTDB', 'psycopg2_test') +dbhost = os.environ.get('PSYCOPG2_TESTDB_HOST', None) +dbport = os.environ.get('PSYCOPG2_TESTDB_PORT', None) +dbuser = os.environ.get('PSYCOPG2_TESTDB_USER', None) +dbpass = os.environ.get('PSYCOPG2_TESTDB_PASSWORD', None) + +# Check if we want to test psycopg's green path. +green = os.environ.get('PSYCOPG2_TEST_GREEN', None) +if green: + if green == '1': + from psycopg2.extras import wait_select as wait_callback + elif green == 'eventlet': + from eventlet.support.psycopg2_patcher import eventlet_wait_callback \ + as wait_callback + else: + raise ValueError("please set 'PSYCOPG2_TEST_GREEN' to a valid value") + + import psycopg2.extensions + psycopg2.extensions.set_wait_callback(wait_callback) + +# Construct a DSN to connect to the test database: +dsn = 'dbname=%s' % dbname +if dbhost is not None: + dsn += ' host=%s' % dbhost +if dbport is not None: + dsn += ' port=%s' % dbport +if dbuser is not None: + dsn += ' user=%s' % dbuser +if dbpass is not None: + dsn += ' password=%s' % dbpass + + diff -Nru psycopg2-2.0.13/tests/test_connection.py psycopg2-2.4.5/tests/test_connection.py --- psycopg2-2.0.13/tests/test_connection.py 2009-10-04 21:32:35.000000000 +0000 +++ psycopg2-2.4.5/tests/test_connection.py 2012-03-28 21:09:15.000000000 +0000 @@ -1,23 +1,61 @@ #!/usr/bin/env python -import unittest -import psycopg2 -import tests +# test_connection.py - unit test for connection attributes +# +# Copyright (C) 2008-2011 James Henstridge +# +# psycopg2 is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# In addition, as a special exception, the copyright holders give +# permission to link this program with the OpenSSL library (or with +# modified versions of OpenSSL that use the same license as OpenSSL), +# and distribute linked combinations including the two. +# +# You must obey the GNU Lesser General Public License in all respects for +# all of the code used other than OpenSSL. +# +# psycopg2 is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +# License for more details. + +import os +import time +import threading +from testutils import unittest, decorate_all_tests +from testutils import skip_before_postgres, skip_after_postgres +from operator import attrgetter +import psycopg2 +import psycopg2.extensions +from testconfig import dsn, dbname class ConnectionTests(unittest.TestCase): - def connect(self): - return psycopg2.connect(tests.dsn) + def setUp(self): + self.conn = psycopg2.connect(dsn) + + def tearDown(self): + if not self.conn.closed: + self.conn.close() def test_closed_attribute(self): - conn = self.connect() + conn = self.conn self.assertEqual(conn.closed, False) conn.close() self.assertEqual(conn.closed, True) + def test_close_idempotent(self): + conn = self.conn + conn.close() + conn.close() + self.assert_(conn.closed) + def test_cursor_closed_attribute(self): - conn = self.connect() + conn = self.conn curs = conn.cursor() self.assertEqual(curs.closed, False) curs.close() @@ -29,7 +67,7 @@ self.assertEqual(curs.closed, True) def test_reset(self): - conn = self.connect() + conn = self.conn # switch isolation level, then reset level = conn.isolation_level conn.set_isolation_level(0) @@ -38,9 +76,950 @@ # now the isolation level should be equal to saved one self.assertEqual(conn.isolation_level, level) + def test_notices(self): + conn = self.conn + cur = conn.cursor() + cur.execute("create temp table chatty (id serial primary key);") + self.assertEqual("CREATE TABLE", cur.statusmessage) + self.assert_(conn.notices) + + def test_notices_consistent_order(self): + conn = self.conn + cur = conn.cursor() + cur.execute("create temp table table1 (id serial); create temp table table2 (id serial);") + cur.execute("create temp table table3 (id serial); create temp table table4 (id serial);") + self.assertEqual(4, len(conn.notices)) + self.assert_('table1' in conn.notices[0]) + self.assert_('table2' in conn.notices[1]) + self.assert_('table3' in conn.notices[2]) + self.assert_('table4' in conn.notices[3]) + + def test_notices_limited(self): + conn = self.conn + cur = conn.cursor() + for i in range(0, 100, 10): + sql = " ".join(["create temp table table%d (id serial);" % j for j in range(i, i+10)]) + cur.execute(sql) + + self.assertEqual(50, len(conn.notices)) + self.assert_('table50' in conn.notices[0], conn.notices[0]) + self.assert_('table51' in conn.notices[1], conn.notices[1]) + self.assert_('table98' in conn.notices[-2], conn.notices[-2]) + self.assert_('table99' in conn.notices[-1], conn.notices[-1]) + + def test_server_version(self): + self.assert_(self.conn.server_version) + + def test_protocol_version(self): + self.assert_(self.conn.protocol_version in (2,3), + self.conn.protocol_version) + + def test_tpc_unsupported(self): + cnn = self.conn + if cnn.server_version >= 80100: + return self.skipTest("tpc is supported") + + self.assertRaises(psycopg2.NotSupportedError, + cnn.xid, 42, "foo", "bar") + + @skip_before_postgres(8, 2) + def test_concurrent_execution(self): + def slave(): + cnn = psycopg2.connect(dsn) + cur = cnn.cursor() + cur.execute("select pg_sleep(4)") + cur.close() + cnn.close() + + t1 = threading.Thread(target=slave) + t2 = threading.Thread(target=slave) + t0 = time.time() + t1.start() + t2.start() + t1.join() + t2.join() + self.assert_(time.time() - t0 < 7, + "something broken in concurrency") + + def test_encoding_name(self): + self.conn.set_client_encoding("EUC_JP") + # conn.encoding is 'EUCJP' now. + cur = self.conn.cursor() + psycopg2.extensions.register_type(psycopg2.extensions.UNICODE, cur) + cur.execute("select 'foo'::text;") + self.assertEqual(cur.fetchone()[0], u'foo') + + def test_connect_nonnormal_envvar(self): + # We must perform encoding normalization at connection time + self.conn.close() + oldenc = os.environ.get('PGCLIENTENCODING') + os.environ['PGCLIENTENCODING'] = 'utf-8' # malformed spelling + try: + self.conn = psycopg2.connect(dsn) + finally: + if oldenc is not None: + os.environ['PGCLIENTENCODING'] = oldenc + else: + del os.environ['PGCLIENTENCODING'] + + def test_weakref(self): + from weakref import ref + import gc + conn = psycopg2.connect(dsn) + w = ref(conn) + conn.close() + del conn + gc.collect() + self.assert_(w() is None) + + def test_commit_concurrency(self): + # The problem is the one reported in ticket #103. Because of bad + # status check, we commit even when a commit is already on its way. + # We can detect this condition by the warnings. + conn = self.conn + notices = [] + stop = [] + + def committer(): + while not stop: + conn.commit() + while conn.notices: + notices.append((2, conn.notices.pop())) + + cur = conn.cursor() + t1 = threading.Thread(target=committer) + t1.start() + i = 1 + for i in range(1000): + cur.execute("select %s;",(i,)) + conn.commit() + while conn.notices: + notices.append((1, conn.notices.pop())) + + # Stop the committer thread + stop.append(True) + + self.assert_(not notices, "%d notices raised" % len(notices)) + + +class IsolationLevelsTestCase(unittest.TestCase): + + def setUp(self): + self._conns = [] + conn = self.connect() + cur = conn.cursor() + try: + cur.execute("drop table isolevel;") + except psycopg2.ProgrammingError: + conn.rollback() + cur.execute("create table isolevel (id integer);") + conn.commit() + conn.close() + + def tearDown(self): + # close the connections used in the test + for conn in self._conns: + if not conn.closed: + conn.close() + + def connect(self): + conn = psycopg2.connect(dsn) + self._conns.append(conn) + return conn + + def test_isolation_level(self): + conn = self.connect() + self.assertEqual( + conn.isolation_level, + psycopg2.extensions.ISOLATION_LEVEL_READ_COMMITTED) + + def test_encoding(self): + conn = self.connect() + self.assert_(conn.encoding in psycopg2.extensions.encodings) + + def test_set_isolation_level(self): + conn = self.connect() + curs = conn.cursor() + + levels = [ + (None, psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT), + ('read uncommitted', psycopg2.extensions.ISOLATION_LEVEL_READ_UNCOMMITTED), + ('read committed', psycopg2.extensions.ISOLATION_LEVEL_READ_COMMITTED), + ('repeatable read', psycopg2.extensions.ISOLATION_LEVEL_REPEATABLE_READ), + ('serializable', psycopg2.extensions.ISOLATION_LEVEL_SERIALIZABLE), + ] + for name, level in levels: + conn.set_isolation_level(level) + + # the only values available on prehistoric PG versions + if conn.server_version < 80000: + if level in ( + psycopg2.extensions.ISOLATION_LEVEL_READ_UNCOMMITTED, + psycopg2.extensions.ISOLATION_LEVEL_REPEATABLE_READ): + name, level = levels[levels.index((name, level)) + 1] + + self.assertEqual(conn.isolation_level, level) + + curs.execute('show transaction_isolation;') + got_name = curs.fetchone()[0] + + if name is None: + curs.execute('show default_transaction_isolation;') + name = curs.fetchone()[0] + + self.assertEqual(name, got_name) + conn.commit() + + self.assertRaises(ValueError, conn.set_isolation_level, -1) + self.assertRaises(ValueError, conn.set_isolation_level, 5) + + def test_set_isolation_level_abort(self): + conn = self.connect() + cur = conn.cursor() + + self.assertEqual(psycopg2.extensions.TRANSACTION_STATUS_IDLE, + conn.get_transaction_status()) + cur.execute("insert into isolevel values (10);") + self.assertEqual(psycopg2.extensions.TRANSACTION_STATUS_INTRANS, + conn.get_transaction_status()) + + conn.set_isolation_level( + psycopg2.extensions.ISOLATION_LEVEL_SERIALIZABLE) + self.assertEqual(psycopg2.extensions.TRANSACTION_STATUS_IDLE, + conn.get_transaction_status()) + cur.execute("select count(*) from isolevel;") + self.assertEqual(0, cur.fetchone()[0]) + + cur.execute("insert into isolevel values (10);") + self.assertEqual(psycopg2.extensions.TRANSACTION_STATUS_INTRANS, + conn.get_transaction_status()) + conn.set_isolation_level( + psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT) + self.assertEqual(psycopg2.extensions.TRANSACTION_STATUS_IDLE, + conn.get_transaction_status()) + cur.execute("select count(*) from isolevel;") + self.assertEqual(0, cur.fetchone()[0]) + + cur.execute("insert into isolevel values (10);") + self.assertEqual(psycopg2.extensions.TRANSACTION_STATUS_IDLE, + conn.get_transaction_status()) + conn.set_isolation_level( + psycopg2.extensions.ISOLATION_LEVEL_READ_COMMITTED) + self.assertEqual(psycopg2.extensions.TRANSACTION_STATUS_IDLE, + conn.get_transaction_status()) + cur.execute("select count(*) from isolevel;") + self.assertEqual(1, cur.fetchone()[0]) + + def test_isolation_level_autocommit(self): + cnn1 = self.connect() + cnn2 = self.connect() + cnn2.set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT) + + cur1 = cnn1.cursor() + cur1.execute("select count(*) from isolevel;") + self.assertEqual(0, cur1.fetchone()[0]) + cnn1.commit() + + cur2 = cnn2.cursor() + cur2.execute("insert into isolevel values (10);") + + cur1.execute("select count(*) from isolevel;") + self.assertEqual(1, cur1.fetchone()[0]) + + def test_isolation_level_read_committed(self): + cnn1 = self.connect() + cnn2 = self.connect() + cnn2.set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_READ_COMMITTED) + + cur1 = cnn1.cursor() + cur1.execute("select count(*) from isolevel;") + self.assertEqual(0, cur1.fetchone()[0]) + cnn1.commit() + + cur2 = cnn2.cursor() + cur2.execute("insert into isolevel values (10);") + cur1.execute("insert into isolevel values (20);") + + cur2.execute("select count(*) from isolevel;") + self.assertEqual(1, cur2.fetchone()[0]) + cnn1.commit() + cur2.execute("select count(*) from isolevel;") + self.assertEqual(2, cur2.fetchone()[0]) + + cur1.execute("select count(*) from isolevel;") + self.assertEqual(1, cur1.fetchone()[0]) + cnn2.commit() + cur1.execute("select count(*) from isolevel;") + self.assertEqual(2, cur1.fetchone()[0]) + + def test_isolation_level_serializable(self): + cnn1 = self.connect() + cnn2 = self.connect() + cnn2.set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_SERIALIZABLE) + + cur1 = cnn1.cursor() + cur1.execute("select count(*) from isolevel;") + self.assertEqual(0, cur1.fetchone()[0]) + cnn1.commit() + + cur2 = cnn2.cursor() + cur2.execute("insert into isolevel values (10);") + cur1.execute("insert into isolevel values (20);") + + cur2.execute("select count(*) from isolevel;") + self.assertEqual(1, cur2.fetchone()[0]) + cnn1.commit() + cur2.execute("select count(*) from isolevel;") + self.assertEqual(1, cur2.fetchone()[0]) + + cur1.execute("select count(*) from isolevel;") + self.assertEqual(1, cur1.fetchone()[0]) + cnn2.commit() + cur1.execute("select count(*) from isolevel;") + self.assertEqual(2, cur1.fetchone()[0]) + + cur2.execute("select count(*) from isolevel;") + self.assertEqual(2, cur2.fetchone()[0]) + + def test_isolation_level_closed(self): + cnn = self.connect() + cnn.close() + self.assertRaises(psycopg2.InterfaceError, getattr, + cnn, 'isolation_level') + self.assertRaises(psycopg2.InterfaceError, + cnn.set_isolation_level, 0) + self.assertRaises(psycopg2.InterfaceError, + cnn.set_isolation_level, 1) + + +class ConnectionTwoPhaseTests(unittest.TestCase): + def setUp(self): + self._conns = [] + + self.make_test_table() + self.clear_test_xacts() + + def tearDown(self): + self.clear_test_xacts() + + # close the connections used in the test + for conn in self._conns: + if not conn.closed: + conn.close() + + def clear_test_xacts(self): + """Rollback all the prepared transaction in the testing db.""" + cnn = self.connect() + cnn.set_isolation_level(0) + cur = cnn.cursor() + try: + cur.execute( + "select gid from pg_prepared_xacts where database = %s", + (dbname,)) + except psycopg2.ProgrammingError: + cnn.rollback() + cnn.close() + return + + gids = [ r[0] for r in cur ] + for gid in gids: + cur.execute("rollback prepared %s;", (gid,)) + cnn.close() + + def make_test_table(self): + cnn = self.connect() + cur = cnn.cursor() + try: + cur.execute("DROP TABLE test_tpc;") + except psycopg2.ProgrammingError: + cnn.rollback() + cur.execute("CREATE TABLE test_tpc (data text);") + cnn.commit() + cnn.close() + + def count_xacts(self): + """Return the number of prepared xacts currently in the test db.""" + cnn = self.connect() + cur = cnn.cursor() + cur.execute(""" + select count(*) from pg_prepared_xacts + where database = %s;""", + (dbname,)) + rv = cur.fetchone()[0] + cnn.close() + return rv + + def count_test_records(self): + """Return the number of records in the test table.""" + cnn = self.connect() + cur = cnn.cursor() + cur.execute("select count(*) from test_tpc;") + rv = cur.fetchone()[0] + cnn.close() + return rv + + def connect(self): + conn = psycopg2.connect(dsn) + self._conns.append(conn) + return conn + + def test_tpc_commit(self): + cnn = self.connect() + xid = cnn.xid(1, "gtrid", "bqual") + self.assertEqual(cnn.status, psycopg2.extensions.STATUS_READY) + + cnn.tpc_begin(xid) + self.assertEqual(cnn.status, psycopg2.extensions.STATUS_BEGIN) + + cur = cnn.cursor() + cur.execute("insert into test_tpc values ('test_tpc_commit');") + self.assertEqual(0, self.count_xacts()) + self.assertEqual(0, self.count_test_records()) + + cnn.tpc_prepare() + self.assertEqual(cnn.status, psycopg2.extensions.STATUS_PREPARED) + self.assertEqual(1, self.count_xacts()) + self.assertEqual(0, self.count_test_records()) + + cnn.tpc_commit() + self.assertEqual(cnn.status, psycopg2.extensions.STATUS_READY) + self.assertEqual(0, self.count_xacts()) + self.assertEqual(1, self.count_test_records()) + + def test_tpc_commit_one_phase(self): + cnn = self.connect() + xid = cnn.xid(1, "gtrid", "bqual") + self.assertEqual(cnn.status, psycopg2.extensions.STATUS_READY) + + cnn.tpc_begin(xid) + self.assertEqual(cnn.status, psycopg2.extensions.STATUS_BEGIN) + + cur = cnn.cursor() + cur.execute("insert into test_tpc values ('test_tpc_commit_1p');") + self.assertEqual(0, self.count_xacts()) + self.assertEqual(0, self.count_test_records()) + + cnn.tpc_commit() + self.assertEqual(cnn.status, psycopg2.extensions.STATUS_READY) + self.assertEqual(0, self.count_xacts()) + self.assertEqual(1, self.count_test_records()) + + def test_tpc_commit_recovered(self): + cnn = self.connect() + xid = cnn.xid(1, "gtrid", "bqual") + self.assertEqual(cnn.status, psycopg2.extensions.STATUS_READY) + + cnn.tpc_begin(xid) + self.assertEqual(cnn.status, psycopg2.extensions.STATUS_BEGIN) + + cur = cnn.cursor() + cur.execute("insert into test_tpc values ('test_tpc_commit_rec');") + self.assertEqual(0, self.count_xacts()) + self.assertEqual(0, self.count_test_records()) + + cnn.tpc_prepare() + cnn.close() + self.assertEqual(1, self.count_xacts()) + self.assertEqual(0, self.count_test_records()) + + cnn = self.connect() + xid = cnn.xid(1, "gtrid", "bqual") + cnn.tpc_commit(xid) + + self.assertEqual(cnn.status, psycopg2.extensions.STATUS_READY) + self.assertEqual(0, self.count_xacts()) + self.assertEqual(1, self.count_test_records()) + + def test_tpc_rollback(self): + cnn = self.connect() + xid = cnn.xid(1, "gtrid", "bqual") + self.assertEqual(cnn.status, psycopg2.extensions.STATUS_READY) + + cnn.tpc_begin(xid) + self.assertEqual(cnn.status, psycopg2.extensions.STATUS_BEGIN) + + cur = cnn.cursor() + cur.execute("insert into test_tpc values ('test_tpc_rollback');") + self.assertEqual(0, self.count_xacts()) + self.assertEqual(0, self.count_test_records()) + + cnn.tpc_prepare() + self.assertEqual(cnn.status, psycopg2.extensions.STATUS_PREPARED) + self.assertEqual(1, self.count_xacts()) + self.assertEqual(0, self.count_test_records()) + + cnn.tpc_rollback() + self.assertEqual(cnn.status, psycopg2.extensions.STATUS_READY) + self.assertEqual(0, self.count_xacts()) + self.assertEqual(0, self.count_test_records()) + + def test_tpc_rollback_one_phase(self): + cnn = self.connect() + xid = cnn.xid(1, "gtrid", "bqual") + self.assertEqual(cnn.status, psycopg2.extensions.STATUS_READY) + + cnn.tpc_begin(xid) + self.assertEqual(cnn.status, psycopg2.extensions.STATUS_BEGIN) + + cur = cnn.cursor() + cur.execute("insert into test_tpc values ('test_tpc_rollback_1p');") + self.assertEqual(0, self.count_xacts()) + self.assertEqual(0, self.count_test_records()) + + cnn.tpc_rollback() + self.assertEqual(cnn.status, psycopg2.extensions.STATUS_READY) + self.assertEqual(0, self.count_xacts()) + self.assertEqual(0, self.count_test_records()) + + def test_tpc_rollback_recovered(self): + cnn = self.connect() + xid = cnn.xid(1, "gtrid", "bqual") + self.assertEqual(cnn.status, psycopg2.extensions.STATUS_READY) + + cnn.tpc_begin(xid) + self.assertEqual(cnn.status, psycopg2.extensions.STATUS_BEGIN) + + cur = cnn.cursor() + cur.execute("insert into test_tpc values ('test_tpc_commit_rec');") + self.assertEqual(0, self.count_xacts()) + self.assertEqual(0, self.count_test_records()) + + cnn.tpc_prepare() + cnn.close() + self.assertEqual(1, self.count_xacts()) + self.assertEqual(0, self.count_test_records()) + + cnn = self.connect() + xid = cnn.xid(1, "gtrid", "bqual") + cnn.tpc_rollback(xid) + + self.assertEqual(cnn.status, psycopg2.extensions.STATUS_READY) + self.assertEqual(0, self.count_xacts()) + self.assertEqual(0, self.count_test_records()) + + def test_status_after_recover(self): + cnn = self.connect() + self.assertEqual(psycopg2.extensions.STATUS_READY, cnn.status) + xns = cnn.tpc_recover() + self.assertEqual(psycopg2.extensions.STATUS_READY, cnn.status) + + cur = cnn.cursor() + cur.execute("select 1") + self.assertEqual(psycopg2.extensions.STATUS_BEGIN, cnn.status) + xns = cnn.tpc_recover() + self.assertEqual(psycopg2.extensions.STATUS_BEGIN, cnn.status) + + def test_recovered_xids(self): + # insert a few test xns + cnn = self.connect() + cnn.set_isolation_level(0) + cur = cnn.cursor() + cur.execute("begin; prepare transaction '1-foo';") + cur.execute("begin; prepare transaction '2-bar';") + + # read the values to return + cur.execute(""" + select gid, prepared, owner, database + from pg_prepared_xacts + where database = %s;""", + (dbname,)) + okvals = cur.fetchall() + okvals.sort() + + cnn = self.connect() + xids = cnn.tpc_recover() + xids = [ xid for xid in xids if xid.database == dbname ] + xids.sort(key=attrgetter('gtrid')) + + # check the values returned + self.assertEqual(len(okvals), len(xids)) + for (xid, (gid, prepared, owner, database)) in zip (xids, okvals): + self.assertEqual(xid.gtrid, gid) + self.assertEqual(xid.prepared, prepared) + self.assertEqual(xid.owner, owner) + self.assertEqual(xid.database, database) + + def test_xid_encoding(self): + cnn = self.connect() + xid = cnn.xid(42, "gtrid", "bqual") + cnn.tpc_begin(xid) + cnn.tpc_prepare() + + cnn = self.connect() + cur = cnn.cursor() + cur.execute("select gid from pg_prepared_xacts where database = %s;", + (dbname,)) + self.assertEqual('42_Z3RyaWQ=_YnF1YWw=', cur.fetchone()[0]) + + def test_xid_roundtrip(self): + for fid, gtrid, bqual in [ + (0, "", ""), + (42, "gtrid", "bqual"), + (0x7fffffff, "x" * 64, "y" * 64), + ]: + cnn = self.connect() + xid = cnn.xid(fid, gtrid, bqual) + cnn.tpc_begin(xid) + cnn.tpc_prepare() + cnn.close() + + cnn = self.connect() + xids = [ xid for xid in cnn.tpc_recover() + if xid.database == dbname ] + self.assertEqual(1, len(xids)) + xid = xids[0] + self.assertEqual(xid.format_id, fid) + self.assertEqual(xid.gtrid, gtrid) + self.assertEqual(xid.bqual, bqual) + + cnn.tpc_rollback(xid) + + def test_unparsed_roundtrip(self): + for tid in [ + '', + 'hello, world!', + 'x' * 199, # PostgreSQL's limit in transaction id length + ]: + cnn = self.connect() + cnn.tpc_begin(tid) + cnn.tpc_prepare() + cnn.close() + + cnn = self.connect() + xids = [ xid for xid in cnn.tpc_recover() + if xid.database == dbname ] + self.assertEqual(1, len(xids)) + xid = xids[0] + self.assertEqual(xid.format_id, None) + self.assertEqual(xid.gtrid, tid) + self.assertEqual(xid.bqual, None) + + cnn.tpc_rollback(xid) + + def test_xid_construction(self): + from psycopg2.extensions import Xid + + x1 = Xid(74, 'foo', 'bar') + self.assertEqual(74, x1.format_id) + self.assertEqual('foo', x1.gtrid) + self.assertEqual('bar', x1.bqual) + + def test_xid_from_string(self): + from psycopg2.extensions import Xid + + x2 = Xid.from_string('42_Z3RyaWQ=_YnF1YWw=') + self.assertEqual(42, x2.format_id) + self.assertEqual('gtrid', x2.gtrid) + self.assertEqual('bqual', x2.bqual) + + x3 = Xid.from_string('99_xxx_yyy') + self.assertEqual(None, x3.format_id) + self.assertEqual('99_xxx_yyy', x3.gtrid) + self.assertEqual(None, x3.bqual) + + def test_xid_to_string(self): + from psycopg2.extensions import Xid + + x1 = Xid.from_string('42_Z3RyaWQ=_YnF1YWw=') + self.assertEqual(str(x1), '42_Z3RyaWQ=_YnF1YWw=') + + x2 = Xid.from_string('99_xxx_yyy') + self.assertEqual(str(x2), '99_xxx_yyy') + + def test_xid_unicode(self): + cnn = self.connect() + x1 = cnn.xid(10, u'uni', u'code') + cnn.tpc_begin(x1) + cnn.tpc_prepare() + cnn.reset() + xid = [ xid for xid in cnn.tpc_recover() + if xid.database == dbname ][0] + self.assertEqual(10, xid.format_id) + self.assertEqual('uni', xid.gtrid) + self.assertEqual('code', xid.bqual) + + def test_xid_unicode_unparsed(self): + # We don't expect people shooting snowmen as transaction ids, + # so if something explodes in an encode error I don't mind. + # Let's just check uniconde is accepted as type. + cnn = self.connect() + cnn.set_client_encoding('utf8') + cnn.tpc_begin(u"transaction-id") + cnn.tpc_prepare() + cnn.reset() + + xid = [ xid for xid in cnn.tpc_recover() + if xid.database == dbname ][0] + self.assertEqual(None, xid.format_id) + self.assertEqual('transaction-id', xid.gtrid) + self.assertEqual(None, xid.bqual) + + def test_cancel_fails_prepared(self): + cnn = self.connect() + cnn.tpc_begin('cancel') + cnn.tpc_prepare() + self.assertRaises(psycopg2.ProgrammingError, cnn.cancel) + +from testutils import skip_if_tpc_disabled +decorate_all_tests(ConnectionTwoPhaseTests, skip_if_tpc_disabled) + + +class TransactionControlTests(unittest.TestCase): + def setUp(self): + self.conn = psycopg2.connect(dsn) + + def tearDown(self): + if not self.conn.closed: + self.conn.close() + + def test_closed(self): + self.conn.close() + self.assertRaises(psycopg2.InterfaceError, + self.conn.set_session, + psycopg2.extensions.ISOLATION_LEVEL_SERIALIZABLE) + + def test_not_in_transaction(self): + cur = self.conn.cursor() + cur.execute("select 1") + self.assertRaises(psycopg2.ProgrammingError, + self.conn.set_session, + psycopg2.extensions.ISOLATION_LEVEL_SERIALIZABLE) + + def test_set_isolation_level(self): + cur = self.conn.cursor() + self.conn.set_session( + psycopg2.extensions.ISOLATION_LEVEL_SERIALIZABLE) + cur.execute("SHOW default_transaction_isolation;") + self.assertEqual(cur.fetchone()[0], 'serializable') + self.conn.rollback() + + self.conn.set_session( + psycopg2.extensions.ISOLATION_LEVEL_REPEATABLE_READ) + cur.execute("SHOW default_transaction_isolation;") + if self.conn.server_version > 80000: + self.assertEqual(cur.fetchone()[0], 'repeatable read') + else: + self.assertEqual(cur.fetchone()[0], 'serializable') + self.conn.rollback() + + self.conn.set_session( + isolation_level=psycopg2.extensions.ISOLATION_LEVEL_READ_COMMITTED) + cur.execute("SHOW default_transaction_isolation;") + self.assertEqual(cur.fetchone()[0], 'read committed') + self.conn.rollback() + + self.conn.set_session( + isolation_level=psycopg2.extensions.ISOLATION_LEVEL_READ_UNCOMMITTED) + cur.execute("SHOW default_transaction_isolation;") + if self.conn.server_version > 80000: + self.assertEqual(cur.fetchone()[0], 'read uncommitted') + else: + self.assertEqual(cur.fetchone()[0], 'read committed') + self.conn.rollback() + + def test_set_isolation_level_str(self): + cur = self.conn.cursor() + self.conn.set_session("serializable") + cur.execute("SHOW default_transaction_isolation;") + self.assertEqual(cur.fetchone()[0], 'serializable') + self.conn.rollback() + + self.conn.set_session("repeatable read") + cur.execute("SHOW default_transaction_isolation;") + if self.conn.server_version > 80000: + self.assertEqual(cur.fetchone()[0], 'repeatable read') + else: + self.assertEqual(cur.fetchone()[0], 'serializable') + self.conn.rollback() + + self.conn.set_session("read committed") + cur.execute("SHOW default_transaction_isolation;") + self.assertEqual(cur.fetchone()[0], 'read committed') + self.conn.rollback() + + self.conn.set_session("read uncommitted") + cur.execute("SHOW default_transaction_isolation;") + if self.conn.server_version > 80000: + self.assertEqual(cur.fetchone()[0], 'read uncommitted') + else: + self.assertEqual(cur.fetchone()[0], 'read committed') + self.conn.rollback() + + def test_bad_isolation_level(self): + self.assertRaises(ValueError, self.conn.set_session, 0) + self.assertRaises(ValueError, self.conn.set_session, 5) + self.assertRaises(ValueError, self.conn.set_session, 'whatever') + + def test_set_read_only(self): + cur = self.conn.cursor() + self.conn.set_session(readonly=True) + cur.execute("SHOW default_transaction_read_only;") + self.assertEqual(cur.fetchone()[0], 'on') + self.conn.rollback() + cur.execute("SHOW default_transaction_read_only;") + self.assertEqual(cur.fetchone()[0], 'on') + self.conn.rollback() + + cur = self.conn.cursor() + self.conn.set_session(readonly=None) + cur.execute("SHOW default_transaction_read_only;") + self.assertEqual(cur.fetchone()[0], 'on') + self.conn.rollback() + + self.conn.set_session(readonly=False) + cur.execute("SHOW default_transaction_read_only;") + self.assertEqual(cur.fetchone()[0], 'off') + self.conn.rollback() + + def test_set_default(self): + cur = self.conn.cursor() + cur.execute("SHOW default_transaction_isolation;") + default_isolevel = cur.fetchone()[0] + cur.execute("SHOW default_transaction_read_only;") + default_readonly = cur.fetchone()[0] + self.conn.rollback() + + self.conn.set_session(isolation_level='serializable', readonly=True) + self.conn.set_session(isolation_level='default', readonly='default') + + cur.execute("SHOW default_transaction_isolation;") + self.assertEqual(cur.fetchone()[0], default_isolevel) + cur.execute("SHOW default_transaction_read_only;") + self.assertEqual(cur.fetchone()[0], default_readonly) + + @skip_before_postgres(9, 1) + def test_set_deferrable(self): + cur = self.conn.cursor() + self.conn.set_session(readonly=True, deferrable=True) + cur.execute("SHOW default_transaction_read_only;") + self.assertEqual(cur.fetchone()[0], 'on') + cur.execute("SHOW default_transaction_deferrable;") + self.assertEqual(cur.fetchone()[0], 'on') + self.conn.rollback() + cur.execute("SHOW default_transaction_deferrable;") + self.assertEqual(cur.fetchone()[0], 'on') + self.conn.rollback() + + self.conn.set_session(deferrable=False) + cur.execute("SHOW default_transaction_read_only;") + self.assertEqual(cur.fetchone()[0], 'on') + cur.execute("SHOW default_transaction_deferrable;") + self.assertEqual(cur.fetchone()[0], 'off') + self.conn.rollback() + + @skip_after_postgres(9, 1) + def test_set_deferrable_error(self): + self.assertRaises(psycopg2.ProgrammingError, + self.conn.set_session, readonly=True, deferrable=True) + + +class AutocommitTests(unittest.TestCase): + def setUp(self): + self.conn = psycopg2.connect(dsn) + + def tearDown(self): + if not self.conn.closed: + self.conn.close() + + def test_closed(self): + self.conn.close() + self.assertRaises(psycopg2.InterfaceError, + setattr, self.conn, 'autocommit', True) + + # The getter doesn't have a guard. We may change this in future + # to make it consistent with other methods; meanwhile let's just check + # it doesn't explode. + try: + self.assert_(self.conn.autocommit in (True, False)) + except psycopg2.InterfaceError: + pass + + def test_default_no_autocommit(self): + self.assert_(not self.conn.autocommit) + self.assertEqual(self.conn.status, psycopg2.extensions.STATUS_READY) + self.assertEqual(self.conn.get_transaction_status(), + psycopg2.extensions.TRANSACTION_STATUS_IDLE) + + cur = self.conn.cursor() + cur.execute('select 1;') + self.assertEqual(self.conn.status, psycopg2.extensions.STATUS_BEGIN) + self.assertEqual(self.conn.get_transaction_status(), + psycopg2.extensions.TRANSACTION_STATUS_INTRANS) + + self.conn.rollback() + self.assertEqual(self.conn.status, psycopg2.extensions.STATUS_READY) + self.assertEqual(self.conn.get_transaction_status(), + psycopg2.extensions.TRANSACTION_STATUS_IDLE) + + def test_set_autocommit(self): + self.conn.autocommit = True + self.assert_(self.conn.autocommit) + self.assertEqual(self.conn.status, psycopg2.extensions.STATUS_READY) + self.assertEqual(self.conn.get_transaction_status(), + psycopg2.extensions.TRANSACTION_STATUS_IDLE) + + cur = self.conn.cursor() + cur.execute('select 1;') + self.assertEqual(self.conn.status, psycopg2.extensions.STATUS_READY) + self.assertEqual(self.conn.get_transaction_status(), + psycopg2.extensions.TRANSACTION_STATUS_IDLE) + + self.conn.autocommit = False + self.assert_(not self.conn.autocommit) + self.assertEqual(self.conn.status, psycopg2.extensions.STATUS_READY) + self.assertEqual(self.conn.get_transaction_status(), + psycopg2.extensions.TRANSACTION_STATUS_IDLE) + + cur.execute('select 1;') + self.assertEqual(self.conn.status, psycopg2.extensions.STATUS_BEGIN) + self.assertEqual(self.conn.get_transaction_status(), + psycopg2.extensions.TRANSACTION_STATUS_INTRANS) + + def test_set_intrans_error(self): + cur = self.conn.cursor() + cur.execute('select 1;') + self.assertRaises(psycopg2.ProgrammingError, + setattr, self.conn, 'autocommit', True) + + def test_set_session_autocommit(self): + self.conn.set_session(autocommit=True) + self.assert_(self.conn.autocommit) + self.assertEqual(self.conn.status, psycopg2.extensions.STATUS_READY) + self.assertEqual(self.conn.get_transaction_status(), + psycopg2.extensions.TRANSACTION_STATUS_IDLE) + + cur = self.conn.cursor() + cur.execute('select 1;') + self.assertEqual(self.conn.status, psycopg2.extensions.STATUS_READY) + self.assertEqual(self.conn.get_transaction_status(), + psycopg2.extensions.TRANSACTION_STATUS_IDLE) + + self.conn.set_session(autocommit=False) + self.assert_(not self.conn.autocommit) + self.assertEqual(self.conn.status, psycopg2.extensions.STATUS_READY) + self.assertEqual(self.conn.get_transaction_status(), + psycopg2.extensions.TRANSACTION_STATUS_IDLE) + + cur.execute('select 1;') + self.assertEqual(self.conn.status, psycopg2.extensions.STATUS_BEGIN) + self.assertEqual(self.conn.get_transaction_status(), + psycopg2.extensions.TRANSACTION_STATUS_INTRANS) + self.conn.rollback() + + self.conn.set_session('serializable', readonly=True, autocommit=True) + self.assert_(self.conn.autocommit) + cur.execute('select 1;') + self.assertEqual(self.conn.status, psycopg2.extensions.STATUS_READY) + self.assertEqual(self.conn.get_transaction_status(), + psycopg2.extensions.TRANSACTION_STATUS_IDLE) + cur.execute("SHOW default_transaction_isolation;") + self.assertEqual(cur.fetchone()[0], 'serializable') + cur.execute("SHOW default_transaction_read_only;") + self.assertEqual(cur.fetchone()[0], 'on') + def test_suite(): return unittest.TestLoader().loadTestsFromName(__name__) -if __name__ == '__main__': - unittest.main(defaultTest='test_suite') +if __name__ == "__main__": + unittest.main() diff -Nru psycopg2-2.0.13/tests/test_copy.py psycopg2-2.4.5/tests/test_copy.py --- psycopg2-2.0.13/tests/test_copy.py 1970-01-01 00:00:00.000000000 +0000 +++ psycopg2-2.4.5/tests/test_copy.py 2011-12-11 21:18:11.000000000 +0000 @@ -0,0 +1,282 @@ +#!/usr/bin/env python + +# test_copy.py - unit test for COPY support +# +# Copyright (C) 2010-2011 Daniele Varrazzo +# +# psycopg2 is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# In addition, as a special exception, the copyright holders give +# permission to link this program with the OpenSSL library (or with +# modified versions of OpenSSL that use the same license as OpenSSL), +# and distribute linked combinations including the two. +# +# You must obey the GNU Lesser General Public License in all respects for +# all of the code used other than OpenSSL. +# +# psycopg2 is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +# License for more details. + +import os +import sys +import string +from testutils import unittest, decorate_all_tests, skip_if_no_iobase +from cStringIO import StringIO +from itertools import cycle, izip + +import psycopg2 +import psycopg2.extensions +from testconfig import dsn, green + +def skip_if_green(f): + def skip_if_green_(self): + if green: + return self.skipTest("copy in async mode currently not supported") + else: + return f(self) + + return skip_if_green_ + + +if sys.version_info[0] < 3: + _base = object +else: + from io import TextIOBase as _base + +class MinimalRead(_base): + """A file wrapper exposing the minimal interface to copy from.""" + def __init__(self, f): + self.f = f + + def read(self, size): + return self.f.read(size) + + def readline(self): + return self.f.readline() + +class MinimalWrite(_base): + """A file wrapper exposing the minimal interface to copy to.""" + def __init__(self, f): + self.f = f + + def write(self, data): + return self.f.write(data) + + +class CopyTests(unittest.TestCase): + + def setUp(self): + self.conn = psycopg2.connect(dsn) + self._create_temp_table() + + def _create_temp_table(self): + curs = self.conn.cursor() + curs.execute(''' + CREATE TEMPORARY TABLE tcopy ( + id serial PRIMARY KEY, + data text + )''') + + def tearDown(self): + self.conn.close() + + def test_copy_from(self): + curs = self.conn.cursor() + try: + self._copy_from(curs, nrecs=1024, srec=10*1024, copykw={}) + finally: + curs.close() + + def test_copy_from_insane_size(self): + # Trying to trigger a "would block" error + curs = self.conn.cursor() + try: + self._copy_from(curs, nrecs=10*1024, srec=10*1024, + copykw={'size': 20*1024*1024}) + finally: + curs.close() + + def test_copy_from_cols(self): + curs = self.conn.cursor() + f = StringIO() + for i in xrange(10): + f.write("%s\n" % (i,)) + + f.seek(0) + curs.copy_from(MinimalRead(f), "tcopy", columns=['id']) + + curs.execute("select * from tcopy order by id") + self.assertEqual([(i, None) for i in range(10)], curs.fetchall()) + + def test_copy_from_cols_err(self): + curs = self.conn.cursor() + f = StringIO() + for i in xrange(10): + f.write("%s\n" % (i,)) + + f.seek(0) + def cols(): + raise ZeroDivisionError() + yield 'id' + + self.assertRaises(ZeroDivisionError, + curs.copy_from, MinimalRead(f), "tcopy", columns=cols()) + + def test_copy_to(self): + curs = self.conn.cursor() + try: + self._copy_from(curs, nrecs=1024, srec=10*1024, copykw={}) + self._copy_to(curs, srec=10*1024) + finally: + curs.close() + + @skip_if_no_iobase + def test_copy_text(self): + self.conn.set_client_encoding('latin1') + self._create_temp_table() # the above call closed the xn + + if sys.version_info[0] < 3: + abin = ''.join(map(chr, range(32, 127) + range(160, 256))) + about = abin.decode('latin1').replace('\\', '\\\\') + + else: + abin = bytes(range(32, 127) + range(160, 256)).decode('latin1') + about = abin.replace('\\', '\\\\') + + curs = self.conn.cursor() + curs.execute('insert into tcopy values (%s, %s)', + (42, abin)) + + import io + f = io.StringIO() + curs.copy_to(f, 'tcopy', columns=('data',)) + f.seek(0) + self.assertEqual(f.readline().rstrip(), about) + + @skip_if_no_iobase + def test_copy_bytes(self): + self.conn.set_client_encoding('latin1') + self._create_temp_table() # the above call closed the xn + + if sys.version_info[0] < 3: + abin = ''.join(map(chr, range(32, 127) + range(160, 255))) + about = abin.replace('\\', '\\\\') + else: + abin = bytes(range(32, 127) + range(160, 255)).decode('latin1') + about = abin.replace('\\', '\\\\').encode('latin1') + + curs = self.conn.cursor() + curs.execute('insert into tcopy values (%s, %s)', + (42, abin)) + + import io + f = io.BytesIO() + curs.copy_to(f, 'tcopy', columns=('data',)) + f.seek(0) + self.assertEqual(f.readline().rstrip(), about) + + @skip_if_no_iobase + def test_copy_expert_textiobase(self): + self.conn.set_client_encoding('latin1') + self._create_temp_table() # the above call closed the xn + + if sys.version_info[0] < 3: + abin = ''.join(map(chr, range(32, 127) + range(160, 256))) + abin = abin.decode('latin1') + about = abin.replace('\\', '\\\\') + + else: + abin = bytes(range(32, 127) + range(160, 256)).decode('latin1') + about = abin.replace('\\', '\\\\') + + import io + f = io.StringIO() + f.write(about) + f.seek(0) + + curs = self.conn.cursor() + psycopg2.extensions.register_type( + psycopg2.extensions.UNICODE, curs) + + curs.copy_expert('COPY tcopy (data) FROM STDIN', f) + curs.execute("select data from tcopy;") + self.assertEqual(curs.fetchone()[0], abin) + + f = io.StringIO() + curs.copy_expert('COPY tcopy (data) TO STDOUT', f) + f.seek(0) + self.assertEqual(f.readline().rstrip(), about) + + + def _copy_from(self, curs, nrecs, srec, copykw): + f = StringIO() + for i, c in izip(xrange(nrecs), cycle(string.ascii_letters)): + l = c * srec + f.write("%s\t%s\n" % (i,l)) + + f.seek(0) + curs.copy_from(MinimalRead(f), "tcopy", **copykw) + + curs.execute("select count(*) from tcopy") + self.assertEqual(nrecs, curs.fetchone()[0]) + + curs.execute("select data from tcopy where id < %s order by id", + (len(string.ascii_letters),)) + for i, (l,) in enumerate(curs): + self.assertEqual(l, string.ascii_letters[i] * srec) + + def _copy_to(self, curs, srec): + f = StringIO() + curs.copy_to(MinimalWrite(f), "tcopy") + + f.seek(0) + ntests = 0 + for line in f: + n, s = line.split() + if int(n) < len(string.ascii_letters): + self.assertEqual(s, string.ascii_letters[int(n)] * srec) + ntests += 1 + + self.assertEqual(ntests, len(string.ascii_letters)) + + def test_copy_expert_file_refcount(self): + class Whatever(object): + pass + + f = Whatever() + curs = self.conn.cursor() + self.assertRaises(TypeError, + curs.copy_expert, 'COPY tcopy (data) FROM STDIN', f) + + def test_copy_no_column_limit(self): + cols = [ "c%050d" % i for i in range(200) ] + + curs = self.conn.cursor() + curs.execute('CREATE TEMPORARY TABLE manycols (%s)' % ',\n'.join( + [ "%s int" % c for c in cols])) + curs.execute("INSERT INTO manycols DEFAULT VALUES") + + f = StringIO() + curs.copy_to(f, "manycols", columns = cols) + f.seek(0) + self.assertEqual(f.read().split(), ['\\N'] * len(cols)) + + f.seek(0) + curs.copy_from(f, "manycols", columns = cols) + curs.execute("select count(*) from manycols;") + self.assertEqual(curs.fetchone()[0], 2) + + +decorate_all_tests(CopyTests, skip_if_green) + + +def test_suite(): + return unittest.TestLoader().loadTestsFromName(__name__) + +if __name__ == "__main__": + unittest.main() diff -Nru psycopg2-2.0.13/tests/test_cursor.py psycopg2-2.4.5/tests/test_cursor.py --- psycopg2-2.0.13/tests/test_cursor.py 1970-01-01 00:00:00.000000000 +0000 +++ psycopg2-2.4.5/tests/test_cursor.py 2012-03-28 21:09:15.000000000 +0000 @@ -0,0 +1,358 @@ +#!/usr/bin/env python + +# test_cursor.py - unit test for cursor attributes +# +# Copyright (C) 2010-2011 Daniele Varrazzo +# +# psycopg2 is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# In addition, as a special exception, the copyright holders give +# permission to link this program with the OpenSSL library (or with +# modified versions of OpenSSL that use the same license as OpenSSL), +# and distribute linked combinations including the two. +# +# You must obey the GNU Lesser General Public License in all respects for +# all of the code used other than OpenSSL. +# +# psycopg2 is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +# License for more details. + +import time +import psycopg2 +import psycopg2.extensions +from psycopg2.extensions import b +from testconfig import dsn +from testutils import unittest, skip_before_postgres, skip_if_no_namedtuple + +class CursorTests(unittest.TestCase): + + def setUp(self): + self.conn = psycopg2.connect(dsn) + + def tearDown(self): + self.conn.close() + + def test_close_idempotent(self): + cur = self.conn.cursor() + cur.close() + cur.close() + self.assert_(cur.closed) + + def test_empty_query(self): + cur = self.conn.cursor() + self.assertRaises(psycopg2.ProgrammingError, cur.execute, "") + self.assertRaises(psycopg2.ProgrammingError, cur.execute, " ") + self.assertRaises(psycopg2.ProgrammingError, cur.execute, ";") + + def test_executemany_propagate_exceptions(self): + conn = self.conn + cur = conn.cursor() + cur.execute("create temp table test_exc (data int);") + def buggygen(): + yield 1//0 + self.assertRaises(ZeroDivisionError, + cur.executemany, "insert into test_exc values (%s)", buggygen()) + cur.close() + + def test_mogrify_unicode(self): + conn = self.conn + cur = conn.cursor() + + # test consistency between execute and mogrify. + + # unicode query containing only ascii data + cur.execute(u"SELECT 'foo';") + self.assertEqual('foo', cur.fetchone()[0]) + self.assertEqual(b("SELECT 'foo';"), cur.mogrify(u"SELECT 'foo';")) + + conn.set_client_encoding('UTF8') + snowman = u"\u2603" + + # unicode query with non-ascii data + cur.execute(u"SELECT '%s';" % snowman) + self.assertEqual(snowman.encode('utf8'), b(cur.fetchone()[0])) + self.assertEqual(("SELECT '%s';" % snowman).encode('utf8'), + cur.mogrify(u"SELECT '%s';" % snowman).replace(b("E'"), b("'"))) + + # unicode args + cur.execute("SELECT %s;", (snowman,)) + self.assertEqual(snowman.encode("utf-8"), b(cur.fetchone()[0])) + self.assertEqual(("SELECT '%s';" % snowman).encode('utf8'), + cur.mogrify("SELECT %s;", (snowman,)).replace(b("E'"), b("'"))) + + # unicode query and args + cur.execute(u"SELECT %s;", (snowman,)) + self.assertEqual(snowman.encode("utf-8"), b(cur.fetchone()[0])) + self.assertEqual(("SELECT '%s';" % snowman).encode('utf8'), + cur.mogrify(u"SELECT %s;", (snowman,)).replace(b("E'"), b("'"))) + + def test_mogrify_decimal_explodes(self): + # issue #7: explodes on windows with python 2.5 and psycopg 2.2.2 + try: + from decimal import Decimal + except: + return + + conn = self.conn + cur = conn.cursor() + self.assertEqual(b('SELECT 10.3;'), + cur.mogrify("SELECT %s;", (Decimal("10.3"),))) + + def test_mogrify_leak_on_multiple_reference(self): + # issue #81: reference leak when a parameter value is referenced + # more than once from a dict. + cur = self.conn.cursor() + i = lambda x: x + foo = i('foo') * 10 + import sys + nref1 = sys.getrefcount(foo) + cur.mogrify("select %(foo)s, %(foo)s, %(foo)s", {'foo': foo}) + nref2 = sys.getrefcount(foo) + self.assertEqual(nref1, nref2) + + def test_bad_placeholder(self): + cur = self.conn.cursor() + self.assertRaises(psycopg2.ProgrammingError, + cur.mogrify, "select %(foo", {}) + self.assertRaises(psycopg2.ProgrammingError, + cur.mogrify, "select %(foo", {'foo': 1}) + self.assertRaises(psycopg2.ProgrammingError, + cur.mogrify, "select %(foo, %(bar)", {'foo': 1}) + self.assertRaises(psycopg2.ProgrammingError, + cur.mogrify, "select %(foo, %(bar)", {'foo': 1, 'bar': 2}) + + def test_cast(self): + curs = self.conn.cursor() + + self.assertEqual(42, curs.cast(20, '42')) + self.assertAlmostEqual(3.14, curs.cast(700, '3.14')) + + try: + from decimal import Decimal + except ImportError: + self.assertAlmostEqual(123.45, curs.cast(1700, '123.45')) + else: + self.assertEqual(Decimal('123.45'), curs.cast(1700, '123.45')) + + from datetime import date + self.assertEqual(date(2011,1,2), curs.cast(1082, '2011-01-02')) + self.assertEqual("who am i?", curs.cast(705, 'who am i?')) # unknown + + def test_cast_specificity(self): + curs = self.conn.cursor() + self.assertEqual("foo", curs.cast(705, 'foo')) + + D = psycopg2.extensions.new_type((705,), "DOUBLING", lambda v, c: v * 2) + psycopg2.extensions.register_type(D, self.conn) + self.assertEqual("foofoo", curs.cast(705, 'foo')) + + T = psycopg2.extensions.new_type((705,), "TREBLING", lambda v, c: v * 3) + psycopg2.extensions.register_type(T, curs) + self.assertEqual("foofoofoo", curs.cast(705, 'foo')) + + curs2 = self.conn.cursor() + self.assertEqual("foofoo", curs2.cast(705, 'foo')) + + def test_weakref(self): + from weakref import ref + curs = self.conn.cursor() + w = ref(curs) + del curs + self.assert_(w() is None) + + def test_invalid_name(self): + curs = self.conn.cursor() + curs.execute("create temp table invname (data int);") + for i in (10,20,30): + curs.execute("insert into invname values (%s)", (i,)) + curs.close() + + curs = self.conn.cursor(r'1-2-3 \ "test"') + curs.execute("select data from invname order by data") + self.assertEqual(curs.fetchall(), [(10,), (20,), (30,)]) + + def test_withhold(self): + self.assertRaises(psycopg2.ProgrammingError, self.conn.cursor, + withhold=True) + + curs = self.conn.cursor() + try: + curs.execute("drop table withhold") + except psycopg2.ProgrammingError: + self.conn.rollback() + curs.execute("create table withhold (data int)") + for i in (10, 20, 30): + curs.execute("insert into withhold values (%s)", (i,)) + curs.close() + + curs = self.conn.cursor("W") + self.assertEqual(curs.withhold, False); + curs.withhold = True + self.assertEqual(curs.withhold, True); + curs.execute("select data from withhold order by data") + self.conn.commit() + self.assertEqual(curs.fetchall(), [(10,), (20,), (30,)]) + curs.close() + + curs = self.conn.cursor("W", withhold=True) + self.assertEqual(curs.withhold, True); + curs.execute("select data from withhold order by data") + self.conn.commit() + self.assertEqual(curs.fetchall(), [(10,), (20,), (30,)]) + + curs = self.conn.cursor() + curs.execute("drop table withhold") + self.conn.commit() + + @skip_before_postgres(8, 2) + def test_iter_named_cursor_efficient(self): + curs = self.conn.cursor('tmp') + # if these records are fetched in the same roundtrip their + # timestamp will not be influenced by the pause in Python world. + curs.execute("""select clock_timestamp() from generate_series(1,2)""") + i = iter(curs) + t1 = (i.next())[0] # the brackets work around a 2to3 bug + time.sleep(0.2) + t2 = (i.next())[0] + self.assert_((t2 - t1).microseconds * 1e-6 < 0.1, + "named cursor records fetched in 2 roundtrips (delta: %s)" + % (t2 - t1)) + + @skip_before_postgres(8, 0) + def test_iter_named_cursor_default_itersize(self): + curs = self.conn.cursor('tmp') + curs.execute('select generate_series(1,50)') + rv = [ (r[0], curs.rownumber) for r in curs ] + # everything swallowed in one gulp + self.assertEqual(rv, [(i,i) for i in range(1,51)]) + + @skip_before_postgres(8, 0) + def test_iter_named_cursor_itersize(self): + curs = self.conn.cursor('tmp') + curs.itersize = 30 + curs.execute('select generate_series(1,50)') + rv = [ (r[0], curs.rownumber) for r in curs ] + # everything swallowed in two gulps + self.assertEqual(rv, [(i,((i - 1) % 30) + 1) for i in range(1,51)]) + + @skip_before_postgres(8, 0) + def test_iter_named_cursor_rownumber(self): + curs = self.conn.cursor('tmp') + # note: this fails if itersize < dataset: internally we check + # rownumber == rowcount to detect when to read anoter page, so we + # would need an extra attribute to have a monotonic rownumber. + curs.itersize = 20 + curs.execute('select generate_series(1,10)') + for i, rec in enumerate(curs): + self.assertEqual(i + 1, curs.rownumber) + + @skip_if_no_namedtuple + def test_namedtuple_description(self): + curs = self.conn.cursor() + curs.execute("""select + 3.14::decimal(10,2) as pi, + 'hello'::text as hi, + '2010-02-18'::date as now; + """) + self.assertEqual(len(curs.description), 3) + for c in curs.description: + self.assertEqual(len(c), 7) # DBAPI happy + for a in ('name', 'type_code', 'display_size', 'internal_size', + 'precision', 'scale', 'null_ok'): + self.assert_(hasattr(c, a), a) + + c = curs.description[0] + self.assertEqual(c.name, 'pi') + self.assert_(c.type_code in psycopg2.extensions.DECIMAL.values) + self.assert_(c.internal_size > 0) + self.assertEqual(c.precision, 10) + self.assertEqual(c.scale, 2) + + c = curs.description[1] + self.assertEqual(c.name, 'hi') + self.assert_(c.type_code in psycopg2.STRING.values) + self.assert_(c.internal_size < 0) + self.assertEqual(c.precision, None) + self.assertEqual(c.scale, None) + + c = curs.description[2] + self.assertEqual(c.name, 'now') + self.assert_(c.type_code in psycopg2.extensions.DATE.values) + self.assert_(c.internal_size > 0) + self.assertEqual(c.precision, None) + self.assertEqual(c.scale, None) + + @skip_before_postgres(8, 0) + def test_named_cursor_stealing(self): + # you can use a named cursor to iterate on a refcursor created + # somewhere else + cur1 = self.conn.cursor() + cur1.execute("DECLARE test CURSOR WITHOUT HOLD " + " FOR SELECT generate_series(1,7)") + + cur2 = self.conn.cursor('test') + # can call fetch without execute + self.assertEqual((1,), cur2.fetchone()) + self.assertEqual([(2,), (3,), (4,)], cur2.fetchmany(3)) + self.assertEqual([(5,), (6,), (7,)], cur2.fetchall()) + + @skip_before_postgres(8, 0) + def test_scroll(self): + cur = self.conn.cursor() + cur.execute("select generate_series(0,9)") + cur.scroll(2) + self.assertEqual(cur.fetchone(), (2,)) + cur.scroll(2) + self.assertEqual(cur.fetchone(), (5,)) + cur.scroll(2, mode='relative') + self.assertEqual(cur.fetchone(), (8,)) + cur.scroll(-1) + self.assertEqual(cur.fetchone(), (8,)) + cur.scroll(-2) + self.assertEqual(cur.fetchone(), (7,)) + cur.scroll(2, mode='absolute') + self.assertEqual(cur.fetchone(), (2,)) + + # on the boundary + cur.scroll(0, mode='absolute') + self.assertEqual(cur.fetchone(), (0,)) + self.assertRaises((IndexError, psycopg2.ProgrammingError), + cur.scroll, -1, mode='absolute') + cur.scroll(0, mode='absolute') + self.assertRaises((IndexError, psycopg2.ProgrammingError), + cur.scroll, -1) + + cur.scroll(9, mode='absolute') + self.assertEqual(cur.fetchone(), (9,)) + self.assertRaises((IndexError, psycopg2.ProgrammingError), + cur.scroll, 10, mode='absolute') + cur.scroll(9, mode='absolute') + self.assertRaises((IndexError, psycopg2.ProgrammingError), + cur.scroll, 1) + + @skip_before_postgres(8, 0) + def test_scroll_named(self): + cur = self.conn.cursor() + cur.execute("select generate_series(0,9)") + cur.scroll(2) + self.assertEqual(cur.fetchone(), (2,)) + cur.scroll(2) + self.assertEqual(cur.fetchone(), (5,)) + cur.scroll(2, mode='relative') + self.assertEqual(cur.fetchone(), (8,)) + cur.scroll(9, mode='absolute') + self.assertEqual(cur.fetchone(), (9,)) + self.assertRaises((IndexError, psycopg2.ProgrammingError), + cur.scroll, 10, mode='absolute') + + +def test_suite(): + return unittest.TestLoader().loadTestsFromName(__name__) + +if __name__ == "__main__": + unittest.main() diff -Nru psycopg2-2.0.13/tests/test_dates.py psycopg2-2.4.5/tests/test_dates.py --- psycopg2-2.0.13/tests/test_dates.py 2009-10-04 21:32:35.000000000 +0000 +++ psycopg2-2.4.5/tests/test_dates.py 2012-03-28 21:09:15.000000000 +0000 @@ -1,11 +1,32 @@ #!/usr/bin/env python + +# test_dates.py - unit test for dates handling +# +# Copyright (C) 2008-2011 James Henstridge +# +# psycopg2 is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# In addition, as a special exception, the copyright holders give +# permission to link this program with the OpenSSL library (or with +# modified versions of OpenSSL that use the same license as OpenSSL), +# and distribute linked combinations including the two. +# +# You must obey the GNU Lesser General Public License in all respects for +# all of the code used other than OpenSSL. +# +# psycopg2 is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +# License for more details. + import math import unittest - import psycopg2 -from psycopg2.tz import FixedOffsetTimezone -import tests - +from psycopg2.tz import FixedOffsetTimezone, ZERO +from testconfig import dsn class CommonDatetimeTestsMixin: @@ -15,7 +36,7 @@ def test_parse_date(self): value = self.DATE('2007-01-01', self.curs) - self.assertNotEqual(value, None) + self.assert_(value is not None) self.assertEqual(value.year, 2007) self.assertEqual(value.month, 1) self.assertEqual(value.day, 1) @@ -30,7 +51,7 @@ def test_parse_time(self): value = self.TIME('13:30:29', self.curs) - self.assertNotEqual(value, None) + self.assert_(value is not None) self.assertEqual(value.hour, 13) self.assertEqual(value.minute, 30) self.assertEqual(value.second, 29) @@ -45,7 +66,7 @@ def test_parse_datetime(self): value = self.DATETIME('2007-01-01 13:30:29', self.curs) - self.assertNotEqual(value, None) + self.assert_(value is not None) self.assertEqual(value.year, 2007) self.assertEqual(value.month, 1) self.assertEqual(value.day, 1) @@ -57,7 +78,7 @@ value = self.DATETIME(None, self.curs) self.assertEqual(value, None) - def test_parse_incomplete_time(self): + def test_parse_incomplete_datetime(self): self.assertRaises(psycopg2.DataError, self.DATETIME, '2007', self.curs) self.assertRaises(psycopg2.DataError, @@ -76,12 +97,12 @@ """Tests for the datetime based date handling in psycopg2.""" def setUp(self): - self.conn = psycopg2.connect(tests.dsn) + self.conn = psycopg2.connect(dsn) self.curs = self.conn.cursor() - self.DATE = psycopg2._psycopg.PYDATE - self.TIME = psycopg2._psycopg.PYTIME - self.DATETIME = psycopg2._psycopg.PYDATETIME - self.INTERVAL = psycopg2._psycopg.PYINTERVAL + self.DATE = psycopg2.extensions.PYDATE + self.TIME = psycopg2.extensions.PYTIME + self.DATETIME = psycopg2.extensions.PYDATETIME + self.INTERVAL = psycopg2.extensions.PYINTERVAL def tearDown(self): self.conn.close() @@ -125,24 +146,16 @@ self.check_time_tz("+01:15", 4500) self.check_time_tz("-01:15", -4500) # The Python datetime module does not support time zone - # offsets that are not a whole number of minutes, so we get an - # error here. Check that we are generating an understandable - # error message. - try: - self.check_time_tz("+01:15:42", 4542) - except ValueError, exc: - self.assertEqual(str(exc), "time zone offset 4542 is not a " - "whole number of minutes") - else: - self.fail("Expected ValueError") - - try: - self.check_time_tz("-01:15:42", -4542) - except ValueError, exc: - self.assertEqual(str(exc), "time zone offset -4542 is not a " - "whole number of minutes") - else: - self.fail("Expected ValueError") + # offsets that are not a whole number of minutes. + # We round the offset to the nearest minute. + self.check_time_tz("+01:15:00", 60 * (60 + 15)) + self.check_time_tz("+01:15:29", 60 * (60 + 15)) + self.check_time_tz("+01:15:30", 60 * (60 + 16)) + self.check_time_tz("+01:15:59", 60 * (60 + 16)) + self.check_time_tz("-01:15:00", -60 * (60 + 15)) + self.check_time_tz("-01:15:29", -60 * (60 + 15)) + self.check_time_tz("-01:15:30", -60 * (60 + 16)) + self.check_time_tz("-01:15:59", -60 * (60 + 16)) def check_datetime_tz(self, str_offset, offset): from datetime import datetime, timedelta @@ -169,24 +182,16 @@ self.check_datetime_tz("+01:15", 4500) self.check_datetime_tz("-01:15", -4500) # The Python datetime module does not support time zone - # offsets that are not a whole number of minutes, so we get an - # error here. Check that we are generating an understandable - # error message. - try: - self.check_datetime_tz("+01:15:42", 4542) - except ValueError, exc: - self.assertEqual(str(exc), "time zone offset 4542 is not a " - "whole number of minutes") - else: - self.fail("Expected ValueError") - - try: - self.check_datetime_tz("-01:15:42", -4542) - except ValueError, exc: - self.assertEqual(str(exc), "time zone offset -4542 is not a " - "whole number of minutes") - else: - self.fail("Expected ValueError") + # offsets that are not a whole number of minutes. + # We round the offset to the nearest minute. + self.check_datetime_tz("+01:15:00", 60 * (60 + 15)) + self.check_datetime_tz("+01:15:29", 60 * (60 + 15)) + self.check_datetime_tz("+01:15:30", 60 * (60 + 16)) + self.check_datetime_tz("+01:15:59", 60 * (60 + 16)) + self.check_datetime_tz("-01:15:00", -60 * (60 + 15)) + self.check_datetime_tz("-01:15:29", -60 * (60 + 15)) + self.check_datetime_tz("-01:15:30", -60 * (60 + 16)) + self.check_datetime_tz("-01:15:59", -60 * (60 + 16)) def test_parse_time_no_timezone(self): self.assertEqual(self.TIME("13:30:29", self.curs).tzinfo, None) @@ -248,9 +253,61 @@ self.assertEqual(seconds, -3583504) self.assertEqual(int(round((value - seconds) * 1000000)), 123456) + def _test_type_roundtrip(self, o1): + o2 = self.execute("select %s;", (o1,)) + self.assertEqual(type(o1), type(o2)) + return o2 + + def _test_type_roundtrip_array(self, o1): + o1 = [o1] + o2 = self.execute("select %s;", (o1,)) + self.assertEqual(type(o1[0]), type(o2[0])) + + def test_type_roundtrip_date(self): + from datetime import date + self._test_type_roundtrip(date(2010,5,3)) + + def test_type_roundtrip_datetime(self): + from datetime import datetime + dt = self._test_type_roundtrip(datetime(2010,5,3,10,20,30)) + self.assertEqual(None, dt.tzinfo) + + def test_type_roundtrip_datetimetz(self): + from datetime import datetime + import psycopg2.tz + tz = psycopg2.tz.FixedOffsetTimezone(8*60) + dt1 = datetime(2010,5,3,10,20,30, tzinfo=tz) + dt2 = self._test_type_roundtrip(dt1) + self.assertNotEqual(None, dt2.tzinfo) + self.assertEqual(dt1, dt2) + + def test_type_roundtrip_time(self): + from datetime import time + self._test_type_roundtrip(time(10,20,30)) + + def test_type_roundtrip_interval(self): + from datetime import timedelta + self._test_type_roundtrip(timedelta(seconds=30)) + + def test_type_roundtrip_date_array(self): + from datetime import date + self._test_type_roundtrip_array(date(2010,5,3)) + + def test_type_roundtrip_datetime_array(self): + from datetime import datetime + self._test_type_roundtrip_array(datetime(2010,5,3,10,20,30)) + + def test_type_roundtrip_time_array(self): + from datetime import time + self._test_type_roundtrip_array(time(10,20,30)) + + def test_type_roundtrip_interval_array(self): + from datetime import timedelta + self._test_type_roundtrip_array(timedelta(seconds=30)) + # Only run the datetime tests if psycopg was compiled with support. -if not hasattr(psycopg2._psycopg, 'PYDATETIME'): +if not hasattr(psycopg2.extensions, 'PYDATETIME'): del DatetimeTests @@ -258,19 +315,28 @@ """Tests for the mx.DateTime based date handling in psycopg2.""" def setUp(self): - self.conn = psycopg2.connect(tests.dsn) + self.conn = psycopg2.connect(dsn) self.curs = self.conn.cursor() self.DATE = psycopg2._psycopg.MXDATE self.TIME = psycopg2._psycopg.MXTIME self.DATETIME = psycopg2._psycopg.MXDATETIME self.INTERVAL = psycopg2._psycopg.MXINTERVAL + psycopg2.extensions.register_type(self.DATE, self.conn) + psycopg2.extensions.register_type(self.TIME, self.conn) + psycopg2.extensions.register_type(self.DATETIME, self.conn) + psycopg2.extensions.register_type(self.INTERVAL, self.conn) + psycopg2.extensions.register_type(psycopg2.extensions.MXDATEARRAY, self.conn) + psycopg2.extensions.register_type(psycopg2.extensions.MXTIMEARRAY, self.conn) + psycopg2.extensions.register_type(psycopg2.extensions.MXDATETIMEARRAY, self.conn) + psycopg2.extensions.register_type(psycopg2.extensions.MXINTERVALARRAY, self.conn) + def tearDown(self): self.conn.close() def test_parse_bc_date(self): value = self.DATE('00042-01-01 BC', self.curs) - self.assertNotEqual(value, None) + self.assert_(value is not None) # mx.DateTime numbers BC dates from 0 rather than 1. self.assertEqual(value.year, -41) self.assertEqual(value.month, 1) @@ -278,7 +344,7 @@ def test_parse_bc_datetime(self): value = self.DATETIME('00042-01-01 13:30:29 BC', self.curs) - self.assertNotEqual(value, None) + self.assert_(value is not None) # mx.DateTime numbers BC dates from 0 rather than 1. self.assertEqual(value.year, -41) self.assertEqual(value.month, 1) @@ -329,7 +395,7 @@ def test_parse_interval(self): value = self.INTERVAL('42 days 05:50:05', self.curs) - self.assertNotEqual(value, None) + self.assert_(value is not None) self.assertEqual(value.day, 42) self.assertEqual(value.hour, 5) self.assertEqual(value.minute, 50) @@ -351,7 +417,11 @@ from mx.DateTime import DateTime value = self.execute('select (%s)::timestamp::text', [DateTime(-41, 1, 1, 13, 30, 29.123456)]) - self.assertEqual(value, '0042-01-01 13:30:29.123456 BC') + # microsecs for BC timestamps look not available in PG < 8.4 + # but more likely it's determined at compile time. + self.assert_(value in ( + '0042-01-01 13:30:29.123456 BC', + '0042-01-01 13:30:29 BC'), value) def test_adapt_timedelta(self): from mx.DateTime import DateTimeDeltaFrom @@ -371,12 +441,106 @@ self.assertEqual(seconds, -3583504) self.assertEqual(int(round((value - seconds) * 1000000)), 123456) + def _test_type_roundtrip(self, o1): + o2 = self.execute("select %s;", (o1,)) + self.assertEqual(type(o1), type(o2)) + + def _test_type_roundtrip_array(self, o1): + o1 = [o1] + o2 = self.execute("select %s;", (o1,)) + self.assertEqual(type(o1[0]), type(o2[0])) + + def test_type_roundtrip_date(self): + from mx.DateTime import Date + self._test_type_roundtrip(Date(2010,5,3)) + + def test_type_roundtrip_datetime(self): + from mx.DateTime import DateTime + self._test_type_roundtrip(DateTime(2010,5,3,10,20,30)) + + def test_type_roundtrip_time(self): + from mx.DateTime import Time + self._test_type_roundtrip(Time(10,20,30)) + + def test_type_roundtrip_interval(self): + from mx.DateTime import DateTimeDeltaFrom + self._test_type_roundtrip(DateTimeDeltaFrom(seconds=30)) + + def test_type_roundtrip_date_array(self): + from mx.DateTime import Date + self._test_type_roundtrip_array(Date(2010,5,3)) + + def test_type_roundtrip_datetime_array(self): + from mx.DateTime import DateTime + self._test_type_roundtrip_array(DateTime(2010,5,3,10,20,30)) + + def test_type_roundtrip_time_array(self): + from mx.DateTime import Time + self._test_type_roundtrip_array(Time(10,20,30)) + + def test_type_roundtrip_interval_array(self): + from mx.DateTime import DateTimeDeltaFrom + self._test_type_roundtrip_array(DateTimeDeltaFrom(seconds=30)) + # Only run the mx.DateTime tests if psycopg was compiled with support. -if not hasattr(psycopg2._psycopg, 'MXDATETIME'): +try: + if not hasattr(psycopg2._psycopg, 'MXDATETIME'): + del mxDateTimeTests +except AttributeError: del mxDateTimeTests +class FromTicksTestCase(unittest.TestCase): + # bug "TimestampFromTicks() throws ValueError (2-2.0.14)" + # reported by Jozsef Szalay on 2010-05-06 + def test_timestamp_value_error_sec_59_99(self): + from datetime import datetime + s = psycopg2.TimestampFromTicks(1273173119.99992) + self.assertEqual(s.adapted, + datetime(2010, 5, 6, 14, 11, 59, 999920, + tzinfo=FixedOffsetTimezone(-5 * 60))) + + def test_date_value_error_sec_59_99(self): + from datetime import date + s = psycopg2.DateFromTicks(1273173119.99992) + self.assertEqual(s.adapted, date(2010, 5, 6)) + + def test_time_value_error_sec_59_99(self): + from datetime import time + s = psycopg2.TimeFromTicks(1273173119.99992) + self.assertEqual(s.adapted.replace(hour=0), + time(0, 11, 59, 999920)) + + +class FixedOffsetTimezoneTests(unittest.TestCase): + + def test_init_with_no_args(self): + tzinfo = FixedOffsetTimezone() + self.assert_(tzinfo._offset is ZERO) + self.assert_(tzinfo._name is None) + + def test_repr_with_positive_offset(self): + tzinfo = FixedOffsetTimezone(5 * 60) + self.assertEqual(repr(tzinfo), "psycopg2.tz.FixedOffsetTimezone(offset=300, name=None)") + + def test_repr_with_negative_offset(self): + tzinfo = FixedOffsetTimezone(-5 * 60) + self.assertEqual(repr(tzinfo), "psycopg2.tz.FixedOffsetTimezone(offset=-300, name=None)") + + def test_repr_with_name(self): + tzinfo = FixedOffsetTimezone(name="FOO") + self.assertEqual(repr(tzinfo), "psycopg2.tz.FixedOffsetTimezone(offset=0, name='FOO')") + + def test_instance_caching(self): + self.assert_(FixedOffsetTimezone(name="FOO") is FixedOffsetTimezone(name="FOO")) + self.assert_(FixedOffsetTimezone(7 * 60) is FixedOffsetTimezone(7 * 60)) + self.assert_(FixedOffsetTimezone(-9 * 60, 'FOO') is FixedOffsetTimezone(-9 * 60, 'FOO')) + self.assert_(FixedOffsetTimezone(9 * 60) is not FixedOffsetTimezone(9 * 60, 'FOO')) + self.assert_(FixedOffsetTimezone(name='FOO') is not FixedOffsetTimezone(9 * 60, 'FOO')) + def test_suite(): return unittest.TestLoader().loadTestsFromName(__name__) +if __name__ == "__main__": + unittest.main() diff -Nru psycopg2-2.0.13/tests/test_extras_dictcursor.py psycopg2-2.4.5/tests/test_extras_dictcursor.py --- psycopg2-2.0.13/tests/test_extras_dictcursor.py 1970-01-01 00:00:00.000000000 +0000 +++ psycopg2-2.4.5/tests/test_extras_dictcursor.py 2012-03-28 21:09:15.000000000 +0000 @@ -0,0 +1,452 @@ +#!/usr/bin/env python +# +# extras_dictcursor - test if DictCursor extension class works +# +# Copyright (C) 2004-2010 Federico Di Gregorio +# +# psycopg2 is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# psycopg2 is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +# License for more details. + +import time +from datetime import timedelta +import psycopg2 +import psycopg2.extras +from testutils import unittest, skip_before_postgres, skip_if_no_namedtuple +from testconfig import dsn + + +class ExtrasDictCursorTests(unittest.TestCase): + """Test if DictCursor extension class works.""" + + def setUp(self): + self.conn = psycopg2.connect(dsn) + curs = self.conn.cursor() + curs.execute("CREATE TEMPORARY TABLE ExtrasDictCursorTests (foo text)") + curs.execute("INSERT INTO ExtrasDictCursorTests VALUES ('bar')") + self.conn.commit() + + def tearDown(self): + self.conn.close() + + + def testDictCursorWithPlainCursorFetchOne(self): + self._testWithPlainCursor(lambda curs: curs.fetchone()) + + def testDictCursorWithPlainCursorFetchMany(self): + self._testWithPlainCursor(lambda curs: curs.fetchmany(100)[0]) + + def testDictCursorWithPlainCursorFetchManyNoarg(self): + self._testWithPlainCursor(lambda curs: curs.fetchmany()[0]) + + def testDictCursorWithPlainCursorFetchAll(self): + self._testWithPlainCursor(lambda curs: curs.fetchall()[0]) + + def testDictCursorWithPlainCursorIter(self): + def getter(curs): + for row in curs: + return row + self._testWithPlainCursor(getter) + + def testUpdateRow(self): + row = self._testWithPlainCursor(lambda curs: curs.fetchone()) + row['foo'] = 'qux' + self.failUnless(row['foo'] == 'qux') + self.failUnless(row[0] == 'qux') + + @skip_before_postgres(8, 0) + def testDictCursorWithPlainCursorIterRowNumber(self): + curs = self.conn.cursor(cursor_factory=psycopg2.extras.DictCursor) + self._testIterRowNumber(curs) + + def _testWithPlainCursor(self, getter): + curs = self.conn.cursor(cursor_factory=psycopg2.extras.DictCursor) + curs.execute("SELECT * FROM ExtrasDictCursorTests") + row = getter(curs) + self.failUnless(row['foo'] == 'bar') + self.failUnless(row[0] == 'bar') + return row + + + def testDictCursorWithPlainCursorRealFetchOne(self): + self._testWithPlainCursorReal(lambda curs: curs.fetchone()) + + def testDictCursorWithPlainCursorRealFetchMany(self): + self._testWithPlainCursorReal(lambda curs: curs.fetchmany(100)[0]) + + def testDictCursorWithPlainCursorRealFetchManyNoarg(self): + self._testWithPlainCursorReal(lambda curs: curs.fetchmany()[0]) + + def testDictCursorWithPlainCursorRealFetchAll(self): + self._testWithPlainCursorReal(lambda curs: curs.fetchall()[0]) + + def testDictCursorWithPlainCursorRealIter(self): + def getter(curs): + for row in curs: + return row + self._testWithPlainCursorReal(getter) + + @skip_before_postgres(8, 0) + def testDictCursorWithPlainCursorRealIterRowNumber(self): + curs = self.conn.cursor(cursor_factory=psycopg2.extras.RealDictCursor) + self._testIterRowNumber(curs) + + def _testWithPlainCursorReal(self, getter): + curs = self.conn.cursor(cursor_factory=psycopg2.extras.RealDictCursor) + curs.execute("SELECT * FROM ExtrasDictCursorTests") + row = getter(curs) + self.failUnless(row['foo'] == 'bar') + + + def testDictCursorWithNamedCursorFetchOne(self): + self._testWithNamedCursor(lambda curs: curs.fetchone()) + + def testDictCursorWithNamedCursorFetchMany(self): + self._testWithNamedCursor(lambda curs: curs.fetchmany(100)[0]) + + def testDictCursorWithNamedCursorFetchManyNoarg(self): + self._testWithNamedCursor(lambda curs: curs.fetchmany()[0]) + + def testDictCursorWithNamedCursorFetchAll(self): + self._testWithNamedCursor(lambda curs: curs.fetchall()[0]) + + def testDictCursorWithNamedCursorIter(self): + def getter(curs): + for row in curs: + return row + self._testWithNamedCursor(getter) + + @skip_before_postgres(8, 2) + def testDictCursorWithNamedCursorNotGreedy(self): + curs = self.conn.cursor('tmp', cursor_factory=psycopg2.extras.DictCursor) + self._testNamedCursorNotGreedy(curs) + + @skip_before_postgres(8, 0) + def testDictCursorWithNamedCursorIterRowNumber(self): + curs = self.conn.cursor('tmp', cursor_factory=psycopg2.extras.DictCursor) + self._testIterRowNumber(curs) + + def _testWithNamedCursor(self, getter): + curs = self.conn.cursor('aname', cursor_factory=psycopg2.extras.DictCursor) + curs.execute("SELECT * FROM ExtrasDictCursorTests") + row = getter(curs) + self.failUnless(row['foo'] == 'bar') + self.failUnless(row[0] == 'bar') + + + def testDictCursorRealWithNamedCursorFetchOne(self): + self._testWithNamedCursorReal(lambda curs: curs.fetchone()) + + def testDictCursorRealWithNamedCursorFetchMany(self): + self._testWithNamedCursorReal(lambda curs: curs.fetchmany(100)[0]) + + def testDictCursorRealWithNamedCursorFetchManyNoarg(self): + self._testWithNamedCursorReal(lambda curs: curs.fetchmany()[0]) + + def testDictCursorRealWithNamedCursorFetchAll(self): + self._testWithNamedCursorReal(lambda curs: curs.fetchall()[0]) + + def testDictCursorRealWithNamedCursorIter(self): + def getter(curs): + for row in curs: + return row + self._testWithNamedCursorReal(getter) + + @skip_before_postgres(8, 2) + def testDictCursorRealWithNamedCursorNotGreedy(self): + curs = self.conn.cursor('tmp', cursor_factory=psycopg2.extras.RealDictCursor) + self._testNamedCursorNotGreedy(curs) + + @skip_before_postgres(8, 0) + def testDictCursorRealWithNamedCursorIterRowNumber(self): + curs = self.conn.cursor('tmp', cursor_factory=psycopg2.extras.RealDictCursor) + self._testIterRowNumber(curs) + + def _testWithNamedCursorReal(self, getter): + curs = self.conn.cursor('aname', cursor_factory=psycopg2.extras.RealDictCursor) + curs.execute("SELECT * FROM ExtrasDictCursorTests") + row = getter(curs) + self.failUnless(row['foo'] == 'bar') + + + def _testNamedCursorNotGreedy(self, curs): + curs.itersize = 2 + curs.execute("""select clock_timestamp() as ts from generate_series(1,3)""") + recs = [] + for t in curs: + time.sleep(0.01) + recs.append(t) + + # check that the dataset was not fetched in a single gulp + self.assert_(recs[1]['ts'] - recs[0]['ts'] < timedelta(seconds=0.005)) + self.assert_(recs[2]['ts'] - recs[1]['ts'] > timedelta(seconds=0.0099)) + + def _testIterRowNumber(self, curs): + # Only checking for dataset < itersize: + # see CursorTests.test_iter_named_cursor_rownumber + curs.itersize = 20 + curs.execute("""select * from generate_series(1,10)""") + for i, r in enumerate(curs): + self.assertEqual(i + 1, curs.rownumber) + + +class NamedTupleCursorTest(unittest.TestCase): + def setUp(self): + from psycopg2.extras import NamedTupleConnection + + try: + from collections import namedtuple + except ImportError: + self.conn = None + return + + self.conn = psycopg2.connect(dsn, + connection_factory=NamedTupleConnection) + curs = self.conn.cursor() + curs.execute("CREATE TEMPORARY TABLE nttest (i int, s text)") + curs.execute("INSERT INTO nttest VALUES (1, 'foo')") + curs.execute("INSERT INTO nttest VALUES (2, 'bar')") + curs.execute("INSERT INTO nttest VALUES (3, 'baz')") + self.conn.commit() + + def tearDown(self): + if self.conn is not None: + self.conn.close() + + @skip_if_no_namedtuple + def test_fetchone(self): + curs = self.conn.cursor() + curs.execute("select * from nttest order by 1") + t = curs.fetchone() + self.assertEqual(t[0], 1) + self.assertEqual(t.i, 1) + self.assertEqual(t[1], 'foo') + self.assertEqual(t.s, 'foo') + self.assertEqual(curs.rownumber, 1) + self.assertEqual(curs.rowcount, 3) + + @skip_if_no_namedtuple + def test_fetchmany_noarg(self): + curs = self.conn.cursor() + curs.arraysize = 2 + curs.execute("select * from nttest order by 1") + res = curs.fetchmany() + self.assertEqual(2, len(res)) + self.assertEqual(res[0].i, 1) + self.assertEqual(res[0].s, 'foo') + self.assertEqual(res[1].i, 2) + self.assertEqual(res[1].s, 'bar') + self.assertEqual(curs.rownumber, 2) + self.assertEqual(curs.rowcount, 3) + + @skip_if_no_namedtuple + def test_fetchmany(self): + curs = self.conn.cursor() + curs.execute("select * from nttest order by 1") + res = curs.fetchmany(2) + self.assertEqual(2, len(res)) + self.assertEqual(res[0].i, 1) + self.assertEqual(res[0].s, 'foo') + self.assertEqual(res[1].i, 2) + self.assertEqual(res[1].s, 'bar') + self.assertEqual(curs.rownumber, 2) + self.assertEqual(curs.rowcount, 3) + + @skip_if_no_namedtuple + def test_fetchall(self): + curs = self.conn.cursor() + curs.execute("select * from nttest order by 1") + res = curs.fetchall() + self.assertEqual(3, len(res)) + self.assertEqual(res[0].i, 1) + self.assertEqual(res[0].s, 'foo') + self.assertEqual(res[1].i, 2) + self.assertEqual(res[1].s, 'bar') + self.assertEqual(res[2].i, 3) + self.assertEqual(res[2].s, 'baz') + self.assertEqual(curs.rownumber, 3) + self.assertEqual(curs.rowcount, 3) + + @skip_if_no_namedtuple + def test_executemany(self): + curs = self.conn.cursor() + curs.executemany("delete from nttest where i = %s", + [(1,), (2,)]) + curs.execute("select * from nttest order by 1") + res = curs.fetchall() + self.assertEqual(1, len(res)) + self.assertEqual(res[0].i, 3) + self.assertEqual(res[0].s, 'baz') + + @skip_if_no_namedtuple + def test_iter(self): + curs = self.conn.cursor() + curs.execute("select * from nttest order by 1") + i = iter(curs) + self.assertEqual(curs.rownumber, 0) + + t = i.next() + self.assertEqual(t.i, 1) + self.assertEqual(t.s, 'foo') + self.assertEqual(curs.rownumber, 1) + self.assertEqual(curs.rowcount, 3) + + t = i.next() + self.assertEqual(t.i, 2) + self.assertEqual(t.s, 'bar') + self.assertEqual(curs.rownumber, 2) + self.assertEqual(curs.rowcount, 3) + + t = i.next() + self.assertEqual(t.i, 3) + self.assertEqual(t.s, 'baz') + self.assertRaises(StopIteration, i.next) + self.assertEqual(curs.rownumber, 3) + self.assertEqual(curs.rowcount, 3) + + def test_error_message(self): + try: + from collections import namedtuple + except ImportError: + # an import error somewhere + from psycopg2.extras import NamedTupleConnection + try: + if self.conn is not None: + self.conn.close() + self.conn = psycopg2.connect(dsn, + connection_factory=NamedTupleConnection) + curs = self.conn.cursor() + curs.execute("select 1") + curs.fetchone() + except ImportError: + pass + else: + self.fail("expecting ImportError") + else: + # skip the test + pass + + @skip_if_no_namedtuple + def test_record_updated(self): + curs = self.conn.cursor() + curs.execute("select 1 as foo;") + r = curs.fetchone() + self.assertEqual(r.foo, 1) + + curs.execute("select 2 as bar;") + r = curs.fetchone() + self.assertEqual(r.bar, 2) + self.assertRaises(AttributeError, getattr, r, 'foo') + + @skip_if_no_namedtuple + def test_no_result_no_surprise(self): + curs = self.conn.cursor() + curs.execute("update nttest set s = s") + self.assertRaises(psycopg2.ProgrammingError, curs.fetchone) + + curs.execute("update nttest set s = s") + self.assertRaises(psycopg2.ProgrammingError, curs.fetchall) + + @skip_if_no_namedtuple + def test_minimal_generation(self): + # Instrument the class to verify it gets called the minimum number of times. + from psycopg2.extras import NamedTupleCursor + f_orig = NamedTupleCursor._make_nt + calls = [0] + def f_patched(self_): + calls[0] += 1 + return f_orig(self_) + + NamedTupleCursor._make_nt = f_patched + + try: + curs = self.conn.cursor() + curs.execute("select * from nttest order by 1") + curs.fetchone() + curs.fetchone() + curs.fetchone() + self.assertEqual(1, calls[0]) + + curs.execute("select * from nttest order by 1") + curs.fetchone() + curs.fetchall() + self.assertEqual(2, calls[0]) + + curs.execute("select * from nttest order by 1") + curs.fetchone() + curs.fetchmany(1) + self.assertEqual(3, calls[0]) + + finally: + NamedTupleCursor._make_nt = f_orig + + @skip_if_no_namedtuple + @skip_before_postgres(8, 0) + def test_named(self): + curs = self.conn.cursor('tmp') + curs.execute("""select i from generate_series(0,9) i""") + recs = [] + recs.extend(curs.fetchmany(5)) + recs.append(curs.fetchone()) + recs.extend(curs.fetchall()) + self.assertEqual(range(10), [t.i for t in recs]) + + @skip_if_no_namedtuple + def test_named_fetchone(self): + curs = self.conn.cursor('tmp') + curs.execute("""select 42 as i""") + t = curs.fetchone() + self.assertEqual(t.i, 42) + + @skip_if_no_namedtuple + def test_named_fetchmany(self): + curs = self.conn.cursor('tmp') + curs.execute("""select 42 as i""") + recs = curs.fetchmany(10) + self.assertEqual(recs[0].i, 42) + + @skip_if_no_namedtuple + def test_named_fetchall(self): + curs = self.conn.cursor('tmp') + curs.execute("""select 42 as i""") + recs = curs.fetchall() + self.assertEqual(recs[0].i, 42) + + @skip_if_no_namedtuple + @skip_before_postgres(8, 2) + def test_not_greedy(self): + curs = self.conn.cursor('tmp') + curs.itersize = 2 + curs.execute("""select clock_timestamp() as ts from generate_series(1,3)""") + recs = [] + for t in curs: + time.sleep(0.01) + recs.append(t) + + # check that the dataset was not fetched in a single gulp + self.assert_(recs[1].ts - recs[0].ts < timedelta(seconds=0.005)) + self.assert_(recs[2].ts - recs[1].ts > timedelta(seconds=0.0099)) + + @skip_if_no_namedtuple + @skip_before_postgres(8, 0) + def test_named_rownumber(self): + curs = self.conn.cursor('tmp') + # Only checking for dataset < itersize: + # see CursorTests.test_iter_named_cursor_rownumber + curs.itersize = 4 + curs.execute("""select * from generate_series(1,3)""") + for i, t in enumerate(curs): + self.assertEqual(i + 1, curs.rownumber) + + +def test_suite(): + return unittest.TestLoader().loadTestsFromName(__name__) + +if __name__ == "__main__": + unittest.main() diff -Nru psycopg2-2.0.13/tests/test_green.py psycopg2-2.4.5/tests/test_green.py --- psycopg2-2.0.13/tests/test_green.py 1970-01-01 00:00:00.000000000 +0000 +++ psycopg2-2.4.5/tests/test_green.py 2011-02-06 15:58:34.000000000 +0000 @@ -0,0 +1,102 @@ +#!/usr/bin/env python + +# test_green.py - unit test for async wait callback +# +# Copyright (C) 2010-2011 Daniele Varrazzo +# +# psycopg2 is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# In addition, as a special exception, the copyright holders give +# permission to link this program with the OpenSSL library (or with +# modified versions of OpenSSL that use the same license as OpenSSL), +# and distribute linked combinations including the two. +# +# You must obey the GNU Lesser General Public License in all respects for +# all of the code used other than OpenSSL. +# +# psycopg2 is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +# License for more details. + +import unittest +import psycopg2 +import psycopg2.extensions +import psycopg2.extras +from testconfig import dsn + +class ConnectionStub(object): + """A `connection` wrapper allowing analysis of the `poll()` calls.""" + def __init__(self, conn): + self.conn = conn + self.polls = [] + + def fileno(self): + return self.conn.fileno() + + def poll(self): + rv = self.conn.poll() + self.polls.append(rv) + return rv + +class GreenTests(unittest.TestCase): + def setUp(self): + self._cb = psycopg2.extensions.get_wait_callback() + psycopg2.extensions.set_wait_callback(psycopg2.extras.wait_select) + self.conn = psycopg2.connect(dsn) + + def tearDown(self): + self.conn.close() + psycopg2.extensions.set_wait_callback(self._cb) + + def set_stub_wait_callback(self, conn): + stub = ConnectionStub(conn) + psycopg2.extensions.set_wait_callback( + lambda conn: psycopg2.extras.wait_select(stub)) + return stub + + def test_flush_on_write(self): + # a very large query requires a flush loop to be sent to the backend + conn = self.conn + stub = self.set_stub_wait_callback(conn) + curs = conn.cursor() + for mb in 1, 5, 10, 20, 50: + size = mb * 1024 * 1024 + del stub.polls[:] + curs.execute("select %s;", ('x' * size,)) + self.assertEqual(size, len(curs.fetchone()[0])) + if stub.polls.count(psycopg2.extensions.POLL_WRITE) > 1: + return + + # This is more a testing glitch than an error: it happens + # on high load on linux: probably because the kernel has more + # buffers ready. A warning may be useful during development, + # but an error is bad during regression testing. + import warnings + warnings.warn("sending a large query didn't trigger block on write.") + + def test_error_in_callback(self): + conn = self.conn + curs = conn.cursor() + curs.execute("select 1") # have a BEGIN + curs.fetchone() + + # now try to do something that will fail in the callback + psycopg2.extensions.set_wait_callback(lambda conn: 1//0) + self.assertRaises(ZeroDivisionError, curs.execute, "select 2") + + # check that the connection is left in an usable state + psycopg2.extensions.set_wait_callback(psycopg2.extras.wait_select) + conn.rollback() + curs.execute("select 2") + self.assertEqual(2, curs.fetchone()[0]) + + +def test_suite(): + return unittest.TestLoader().loadTestsFromName(__name__) + +if __name__ == "__main__": + unittest.main() diff -Nru psycopg2-2.0.13/tests/test_lobject.py psycopg2-2.4.5/tests/test_lobject.py --- psycopg2-2.0.13/tests/test_lobject.py 2009-10-04 21:32:35.000000000 +0000 +++ psycopg2-2.4.5/tests/test_lobject.py 2012-03-28 21:09:15.000000000 +0000 @@ -1,23 +1,70 @@ +#!/usr/bin/env python + +# test_lobject.py - unit test for large objects support +# +# Copyright (C) 2008-2011 James Henstridge +# +# psycopg2 is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# In addition, as a special exception, the copyright holders give +# permission to link this program with the OpenSSL library (or with +# modified versions of OpenSSL that use the same license as OpenSSL), +# and distribute linked combinations including the two. +# +# You must obey the GNU Lesser General Public License in all respects for +# all of the code used other than OpenSSL. +# +# psycopg2 is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +# License for more details. + import os import shutil import tempfile -import unittest import psycopg2 import psycopg2.extensions -import tests +from psycopg2.extensions import b +from testconfig import dsn, green +from testutils import unittest, decorate_all_tests, skip_if_tpc_disabled + +def skip_if_no_lo(f): + def skip_if_no_lo_(self): + if self.conn.server_version < 80100: + return self.skipTest("large objects only supported from PG 8.1") + else: + return f(self) + + return skip_if_no_lo_ + +def skip_if_green(f): + def skip_if_green_(self): + if green: + return self.skipTest("libpq doesn't support LO in async mode") + else: + return f(self) + return skip_if_green_ -class LargeObjectTests(unittest.TestCase): +class LargeObjectMixin(object): + # doesn't derive from TestCase to avoid repeating tests twice. def setUp(self): - self.conn = psycopg2.connect(tests.dsn) + self.conn = self.connect() self.lo_oid = None self.tmpdir = None def tearDown(self): if self.tmpdir: shutil.rmtree(self.tmpdir, ignore_errors=True) + + if self.conn.closed: + return + if self.lo_oid is not None: self.conn.rollback() try: @@ -28,10 +75,15 @@ lo.unlink() self.conn.close() + def connect(self): + return psycopg2.connect(dsn) + + +class LargeObjectTests(LargeObjectMixin, unittest.TestCase): def test_create(self): lo = self.conn.lobject() self.assertNotEqual(lo, None) - self.assertEqual(lo.mode, "w") + self.assertEqual(lo.mode[0], "w") def test_open_non_existent(self): # By creating then removing a large object, we get an Oid that @@ -45,13 +97,13 @@ lo2 = self.conn.lobject(lo.oid) self.assertNotEqual(lo2, None) self.assertEqual(lo2.oid, lo.oid) - self.assertEqual(lo2.mode, "r") + self.assertEqual(lo2.mode[0], "r") def test_open_for_write(self): lo = self.conn.lobject() lo2 = self.conn.lobject(lo.oid, "w") - self.assertEqual(lo2.mode, "w") - lo2.write("some data") + self.assertEqual(lo2.mode[0], "w") + lo2.write(b("some data")) def test_open_mode_n(self): # Openning an object in mode "n" gives us a closed lobject. @@ -62,6 +114,11 @@ self.assertEqual(lo2.oid, lo.oid) self.assertEqual(lo2.closed, True) + def test_close_connection_gone(self): + lo = self.conn.lobject() + self.conn.close() + lo.close() + def test_create_with_oid(self): # Create and delete a large object to get an unused Oid. lo = self.conn.lobject() @@ -82,7 +139,7 @@ self.tmpdir = tempfile.mkdtemp() filename = os.path.join(self.tmpdir, "data.txt") fp = open(filename, "wb") - fp.write("some data") + fp.write(b("some data")) fp.close() lo = self.conn.lobject(0, "r", 0, filename) @@ -96,7 +153,7 @@ def test_write(self): lo = self.conn.lobject() - self.assertEqual(lo.write("some data"), len("some data")) + self.assertEqual(lo.write(b("some data")), len("some data")) def test_write_large(self): lo = self.conn.lobject() @@ -105,26 +162,54 @@ def test_read(self): lo = self.conn.lobject() - length = lo.write("some data") + length = lo.write(b("some data")) lo.close() lo = self.conn.lobject(lo.oid) - self.assertEqual(lo.read(4), "some") + x = lo.read(4) + self.assertEqual(type(x), type('')) + self.assertEqual(x, "some") self.assertEqual(lo.read(), " data") + def test_read_binary(self): + lo = self.conn.lobject() + length = lo.write(b("some data")) + lo.close() + + lo = self.conn.lobject(lo.oid, "rb") + x = lo.read(4) + self.assertEqual(type(x), type(b(''))) + self.assertEqual(x, b("some")) + self.assertEqual(lo.read(), b(" data")) + + def test_read_text(self): + lo = self.conn.lobject() + snowman = u"\u2603" + length = lo.write(u"some data " + snowman) + lo.close() + + lo = self.conn.lobject(lo.oid, "rt") + x = lo.read(4) + self.assertEqual(type(x), type(u'')) + self.assertEqual(x, u"some") + self.assertEqual(lo.read(), u" data " + snowman) + def test_read_large(self): lo = self.conn.lobject() data = "data" * 1000000 - length = lo.write("some"+data) + length = lo.write("some" + data) lo.close() lo = self.conn.lobject(lo.oid) self.assertEqual(lo.read(4), "some") - self.assertEqual(lo.read(), data) + data1 = lo.read() + # avoid dumping megacraps in the console in case of error + self.assert_(data == data1, + "%r... != %r..." % (data[:100], data1[:100])) def test_seek_tell(self): lo = self.conn.lobject() - length = lo.write("some data") + length = lo.write(b("some data")) self.assertEqual(lo.tell(), length) lo.close() lo = self.conn.lobject(lo.oid) @@ -154,13 +239,17 @@ def test_export(self): lo = self.conn.lobject() - lo.write("some data") + lo.write(b("some data")) self.tmpdir = tempfile.mkdtemp() filename = os.path.join(self.tmpdir, "data.txt") lo.export(filename) self.assertTrue(os.path.exists(filename)) - self.assertEqual(open(filename, "rb").read(), "some data") + f = open(filename, "rb") + try: + self.assertEqual(f.read(), b("some data")) + finally: + f.close() def test_close_twice(self): lo = self.conn.lobject() @@ -170,7 +259,7 @@ def test_write_after_close(self): lo = self.conn.lobject() lo.close() - self.assertRaises(psycopg2.InterfaceError, lo.write, "some data") + self.assertRaises(psycopg2.InterfaceError, lo.write, b("some data")) def test_read_after_close(self): lo = self.conn.lobject() @@ -195,14 +284,18 @@ def test_export_after_close(self): lo = self.conn.lobject() - lo.write("some data") + lo.write(b("some data")) lo.close() self.tmpdir = tempfile.mkdtemp() filename = os.path.join(self.tmpdir, "data.txt") lo.export(filename) self.assertTrue(os.path.exists(filename)) - self.assertEqual(open(filename, "rb").read(), "some data") + f = open(filename, "rb") + try: + self.assertEqual(f.read(), b("some data")) + finally: + f.close() def test_close_after_commit(self): lo = self.conn.lobject() @@ -217,7 +310,7 @@ self.lo_oid = lo.oid self.conn.commit() - self.assertRaises(psycopg2.ProgrammingError, lo.write, "some data") + self.assertRaises(psycopg2.ProgrammingError, lo.write, b("some data")) def test_read_after_commit(self): lo = self.conn.lobject() @@ -250,15 +343,101 @@ def test_export_after_commit(self): lo = self.conn.lobject() - lo.write("some data") + lo.write(b("some data")) self.conn.commit() self.tmpdir = tempfile.mkdtemp() filename = os.path.join(self.tmpdir, "data.txt") lo.export(filename) self.assertTrue(os.path.exists(filename)) - self.assertEqual(open(filename, "rb").read(), "some data") + f = open(filename, "rb") + try: + self.assertEqual(f.read(), b("some data")) + finally: + f.close() + + @skip_if_tpc_disabled + def test_read_after_tpc_commit(self): + self.conn.tpc_begin('test_lobject') + lo = self.conn.lobject() + self.lo_oid = lo.oid + self.conn.tpc_commit() + + self.assertRaises(psycopg2.ProgrammingError, lo.read, 5) + + @skip_if_tpc_disabled + def test_read_after_tpc_prepare(self): + self.conn.tpc_begin('test_lobject') + lo = self.conn.lobject() + self.lo_oid = lo.oid + self.conn.tpc_prepare() + + try: + self.assertRaises(psycopg2.ProgrammingError, lo.read, 5) + finally: + self.conn.tpc_commit() + + +decorate_all_tests(LargeObjectTests, skip_if_no_lo) +decorate_all_tests(LargeObjectTests, skip_if_green) + + +def skip_if_no_truncate(f): + def skip_if_no_truncate_(self): + if self.conn.server_version < 80300: + return self.skipTest( + "the server doesn't support large object truncate") + + if not hasattr(psycopg2.extensions.lobject, 'truncate'): + return self.skipTest( + "psycopg2 has been built against a libpq " + "without large object truncate support.") + + return f(self) + +class LargeObjectTruncateTests(LargeObjectMixin, unittest.TestCase): + def test_truncate(self): + lo = self.conn.lobject() + lo.write(b("some data")) + lo.close() + + lo = self.conn.lobject(lo.oid, "w") + lo.truncate(4) + + # seek position unchanged + self.assertEqual(lo.tell(), 0) + # data truncated + self.assertEqual(lo.read(), b("some")) + + lo.truncate(6) + lo.seek(0) + # large object extended with zeroes + self.assertEqual(lo.read(), b("some\x00\x00")) + + lo.truncate() + lo.seek(0) + # large object empty + self.assertEqual(lo.read(), b("")) + + def test_truncate_after_close(self): + lo = self.conn.lobject() + lo.close() + self.assertRaises(psycopg2.InterfaceError, lo.truncate) + + def test_truncate_after_commit(self): + lo = self.conn.lobject() + self.lo_oid = lo.oid + self.conn.commit() + + self.assertRaises(psycopg2.ProgrammingError, lo.truncate) + +decorate_all_tests(LargeObjectTruncateTests, skip_if_no_lo) +decorate_all_tests(LargeObjectTruncateTests, skip_if_green) +decorate_all_tests(LargeObjectTruncateTests, skip_if_no_truncate) def test_suite(): return unittest.TestLoader().loadTestsFromName(__name__) + +if __name__ == "__main__": + unittest.main() diff -Nru psycopg2-2.0.13/tests/test_module.py psycopg2-2.4.5/tests/test_module.py --- psycopg2-2.0.13/tests/test_module.py 1970-01-01 00:00:00.000000000 +0000 +++ psycopg2-2.4.5/tests/test_module.py 2012-03-28 21:09:15.000000000 +0000 @@ -0,0 +1,169 @@ +#!/usr/bin/env python + +# test_module.py - unit test for the module interface +# +# Copyright (C) 2011 Daniele Varrazzo +# +# psycopg2 is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# In addition, as a special exception, the copyright holders give +# permission to link this program with the OpenSSL library (or with +# modified versions of OpenSSL that use the same license as OpenSSL), +# and distribute linked combinations including the two. +# +# You must obey the GNU Lesser General Public License in all respects for +# all of the code used other than OpenSSL. +# +# psycopg2 is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +# License for more details. + +from testutils import unittest, skip_before_python +from testconfig import dsn + +import psycopg2 + +class ConnectTestCase(unittest.TestCase): + def setUp(self): + self.args = None + def conect_stub(dsn, connection_factory=None, async=False): + self.args = (dsn, connection_factory, async) + + self._connect_orig = psycopg2._connect + psycopg2._connect = conect_stub + + def tearDown(self): + psycopg2._connect = self._connect_orig + + def test_there_has_to_be_something(self): + self.assertRaises(psycopg2.InterfaceError, psycopg2.connect) + self.assertRaises(psycopg2.InterfaceError, psycopg2.connect, + connection_factory=lambda dsn, async=False: None) + self.assertRaises(psycopg2.InterfaceError, psycopg2.connect, + async=True) + + def test_no_keywords(self): + psycopg2.connect('') + self.assertEqual(self.args[0], '') + self.assertEqual(self.args[1], None) + self.assertEqual(self.args[2], False) + + def test_dsn(self): + psycopg2.connect('dbname=blah x=y') + self.assertEqual(self.args[0], 'dbname=blah x=y') + self.assertEqual(self.args[1], None) + self.assertEqual(self.args[2], False) + + def test_supported_keywords(self): + psycopg2.connect(database='foo') + self.assertEqual(self.args[0], 'dbname=foo') + psycopg2.connect(user='postgres') + self.assertEqual(self.args[0], 'user=postgres') + psycopg2.connect(password='secret') + self.assertEqual(self.args[0], 'password=secret') + psycopg2.connect(port=5432) + self.assertEqual(self.args[0], 'port=5432') + psycopg2.connect(sslmode='require') + self.assertEqual(self.args[0], 'sslmode=require') + + psycopg2.connect(database='foo', + user='postgres', password='secret', port=5432) + self.assert_('dbname=foo' in self.args[0]) + self.assert_('user=postgres' in self.args[0]) + self.assert_('password=secret' in self.args[0]) + self.assert_('port=5432' in self.args[0]) + self.assertEqual(len(self.args[0].split()), 4) + + def test_generic_keywords(self): + psycopg2.connect(foo='bar') + self.assertEqual(self.args[0], 'foo=bar') + + def test_factory(self): + def f(dsn, async=False): + pass + + psycopg2.connect(database='foo', bar='baz', connection_factory=f) + self.assertEqual(self.args[0], 'dbname=foo bar=baz') + self.assertEqual(self.args[1], f) + self.assertEqual(self.args[2], False) + + psycopg2.connect("dbname=foo bar=baz", connection_factory=f) + self.assertEqual(self.args[0], 'dbname=foo bar=baz') + self.assertEqual(self.args[1], f) + self.assertEqual(self.args[2], False) + + def test_async(self): + psycopg2.connect(database='foo', bar='baz', async=1) + self.assertEqual(self.args[0], 'dbname=foo bar=baz') + self.assertEqual(self.args[1], None) + self.assert_(self.args[2]) + + psycopg2.connect("dbname=foo bar=baz", async=True) + self.assertEqual(self.args[0], 'dbname=foo bar=baz') + self.assertEqual(self.args[1], None) + self.assert_(self.args[2]) + + def test_empty_param(self): + psycopg2.connect(database='sony', password='') + self.assertEqual(self.args[0], "dbname=sony password=''") + + def test_escape(self): + psycopg2.connect(database='hello world') + self.assertEqual(self.args[0], "dbname='hello world'") + + psycopg2.connect(database=r'back\slash') + self.assertEqual(self.args[0], r"dbname=back\\slash") + + psycopg2.connect(database="quo'te") + self.assertEqual(self.args[0], r"dbname=quo\'te") + + psycopg2.connect(database="with\ttab") + self.assertEqual(self.args[0], "dbname='with\ttab'") + + psycopg2.connect(database=r"\every thing'") + self.assertEqual(self.args[0], r"dbname='\\every thing\''") + + +class ExceptionsTestCase(unittest.TestCase): + def setUp(self): + self.conn = psycopg2.connect(dsn) + + def tearDown(self): + self.conn.close() + + def test_attributes(self): + cur = self.conn.cursor() + try: + cur.execute("select * from nonexist") + except psycopg2.Error, exc: + e = exc + + self.assertEqual(e.pgcode, '42P01') + self.assert_(e.pgerror) + self.assert_(e.cursor is cur) + + @skip_before_python(2, 5) + def test_pickle(self): + import pickle + cur = self.conn.cursor() + try: + cur.execute("select * from nonexist") + except psycopg2.Error, exc: + e = exc + + e1 = pickle.loads(pickle.dumps(e)) + + self.assertEqual(e.pgerror, e1.pgerror) + self.assertEqual(e.pgcode, e1.pgcode) + self.assert_(e1.cursor is None) + + +def test_suite(): + return unittest.TestLoader().loadTestsFromName(__name__) + +if __name__ == "__main__": + unittest.main() diff -Nru psycopg2-2.0.13/tests/test_notify.py psycopg2-2.4.5/tests/test_notify.py --- psycopg2-2.0.13/tests/test_notify.py 1970-01-01 00:00:00.000000000 +0000 +++ psycopg2-2.4.5/tests/test_notify.py 2011-02-27 12:03:48.000000000 +0000 @@ -0,0 +1,206 @@ +#!/usr/bin/env python + +# test_notify.py - unit test for async notifications +# +# Copyright (C) 2010-2011 Daniele Varrazzo +# +# psycopg2 is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# In addition, as a special exception, the copyright holders give +# permission to link this program with the OpenSSL library (or with +# modified versions of OpenSSL that use the same license as OpenSSL), +# and distribute linked combinations including the two. +# +# You must obey the GNU Lesser General Public License in all respects for +# all of the code used other than OpenSSL. +# +# psycopg2 is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +# License for more details. + +from testutils import unittest + +import psycopg2 +from psycopg2 import extensions +from testconfig import dsn +from testutils import script_to_py3 + +import sys +import time +import select +import signal +from subprocess import Popen, PIPE + + +class NotifiesTests(unittest.TestCase): + + def setUp(self): + self.conn = psycopg2.connect(dsn) + + def tearDown(self): + self.conn.close() + + def autocommit(self, conn): + """Set a connection in autocommit mode.""" + conn.set_isolation_level(extensions.ISOLATION_LEVEL_AUTOCOMMIT) + + def listen(self, name): + """Start listening for a name on self.conn.""" + curs = self.conn.cursor() + curs.execute("LISTEN " + name) + curs.close() + + def notify(self, name, sec=0, payload=None): + """Send a notification to the database, eventually after some time.""" + if payload is None: + payload = '' + else: + payload = ", %r" % payload + + script = ("""\ +import time +time.sleep(%(sec)s) +import psycopg2 +import psycopg2.extensions +conn = psycopg2.connect(%(dsn)r) +conn.set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT) +print conn.get_backend_pid() +curs = conn.cursor() +curs.execute("NOTIFY " %(name)r %(payload)r) +curs.close() +conn.close() +""" + % { 'dsn': dsn, 'sec': sec, 'name': name, 'payload': payload}) + + return Popen([sys.executable, '-c', script_to_py3(script)], stdout=PIPE) + + def test_notifies_received_on_poll(self): + self.autocommit(self.conn) + self.listen('foo') + + proc = self.notify('foo', 1) + + t0 = time.time() + ready = select.select([self.conn], [], [], 5) + t1 = time.time() + self.assert_(0.99 < t1 - t0 < 4, t1 - t0) + + pid = int(proc.communicate()[0]) + self.assertEqual(0, len(self.conn.notifies)) + self.assertEqual(extensions.POLL_OK, self.conn.poll()) + self.assertEqual(1, len(self.conn.notifies)) + self.assertEqual(pid, self.conn.notifies[0][0]) + self.assertEqual('foo', self.conn.notifies[0][1]) + + def test_many_notifies(self): + self.autocommit(self.conn) + for name in ['foo', 'bar', 'baz']: + self.listen(name) + + pids = {} + for name in ['foo', 'bar', 'baz', 'qux']: + pids[name] = int(self.notify(name).communicate()[0]) + + self.assertEqual(0, len(self.conn.notifies)) + for i in range(10): + self.assertEqual(extensions.POLL_OK, self.conn.poll()) + self.assertEqual(3, len(self.conn.notifies)) + + names = dict.fromkeys(['foo', 'bar', 'baz']) + for (pid, name) in self.conn.notifies: + self.assertEqual(pids[name], pid) + names.pop(name) # raise if name found twice + + def test_notifies_received_on_execute(self): + self.autocommit(self.conn) + self.listen('foo') + pid = int(self.notify('foo').communicate()[0]) + self.assertEqual(0, len(self.conn.notifies)) + self.conn.cursor().execute('select 1;') + self.assertEqual(1, len(self.conn.notifies)) + self.assertEqual(pid, self.conn.notifies[0][0]) + self.assertEqual('foo', self.conn.notifies[0][1]) + + def test_notify_object(self): + self.autocommit(self.conn) + self.listen('foo') + self.notify('foo').communicate() + time.sleep(0.5) + self.conn.poll() + notify = self.conn.notifies[0] + self.assert_(isinstance(notify, psycopg2.extensions.Notify)) + + def test_notify_attributes(self): + self.autocommit(self.conn) + self.listen('foo') + pid = int(self.notify('foo').communicate()[0]) + time.sleep(0.5) + self.conn.poll() + self.assertEqual(1, len(self.conn.notifies)) + notify = self.conn.notifies[0] + self.assertEqual(pid, notify.pid) + self.assertEqual('foo', notify.channel) + self.assertEqual('', notify.payload) + + def test_notify_payload(self): + if self.conn.server_version < 90000: + return self.skipTest("server version %s doesn't support notify payload" + % self.conn.server_version) + self.autocommit(self.conn) + self.listen('foo') + pid = int(self.notify('foo', payload="Hello, world!").communicate()[0]) + time.sleep(0.5) + self.conn.poll() + self.assertEqual(1, len(self.conn.notifies)) + notify = self.conn.notifies[0] + self.assertEqual(pid, notify.pid) + self.assertEqual('foo', notify.channel) + self.assertEqual('Hello, world!', notify.payload) + + def test_notify_init(self): + n = psycopg2.extensions.Notify(10, 'foo') + self.assertEqual(10, n.pid) + self.assertEqual('foo', n.channel) + self.assertEqual('', n.payload) + (pid, channel) = n + self.assertEqual((pid, channel), (10, 'foo')) + + n = psycopg2.extensions.Notify(42, 'bar', 'baz') + self.assertEqual(42, n.pid) + self.assertEqual('bar', n.channel) + self.assertEqual('baz', n.payload) + (pid, channel) = n + self.assertEqual((pid, channel), (42, 'bar')) + + def test_compare(self): + data = [(10, 'foo'), (20, 'foo'), (10, 'foo', 'bar'), (10, 'foo', 'baz')] + for d1 in data: + for d2 in data: + n1 = psycopg2.extensions.Notify(*d1) + n2 = psycopg2.extensions.Notify(*d2) + self.assertEqual((n1 == n2), (d1 == d2)) + self.assertEqual((n1 != n2), (d1 != d2)) + + def test_compare_tuple(self): + from psycopg2.extensions import Notify + self.assertEqual((10, 'foo'), Notify(10, 'foo')) + self.assertEqual((10, 'foo'), Notify(10, 'foo', 'bar')) + self.assertNotEqual((10, 'foo'), Notify(20, 'foo')) + self.assertNotEqual((10, 'foo'), Notify(10, 'bar')) + + def test_hash(self): + from psycopg2.extensions import Notify + self.assertEqual(hash((10, 'foo')), hash(Notify(10, 'foo'))) + self.assertNotEqual(hash(Notify(10, 'foo', 'bar')), + hash(Notify(10, 'foo'))) + +def test_suite(): + return unittest.TestLoader().loadTestsFromName(__name__) + +if __name__ == "__main__": + unittest.main() + diff -Nru psycopg2-2.0.13/tests/test_psycopg2_dbapi20.py psycopg2-2.4.5/tests/test_psycopg2_dbapi20.py --- psycopg2-2.0.13/tests/test_psycopg2_dbapi20.py 2009-10-04 21:32:35.000000000 +0000 +++ psycopg2-2.4.5/tests/test_psycopg2_dbapi20.py 2011-02-06 15:58:34.000000000 +0000 @@ -1,14 +1,39 @@ #!/usr/bin/env python + +# test_psycopg2_dbapi20.py - DB API conformance test for psycopg2 +# +# Copyright (C) 2006-2011 Federico Di Gregorio +# +# psycopg2 is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# In addition, as a special exception, the copyright holders give +# permission to link this program with the OpenSSL library (or with +# modified versions of OpenSSL that use the same license as OpenSSL), +# and distribute linked combinations including the two. +# +# You must obey the GNU Lesser General Public License in all respects for +# all of the code used other than OpenSSL. +# +# psycopg2 is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +# License for more details. + import dbapi20 -import unittest +import dbapi20_tpc +from testutils import skip_if_tpc_disabled +from testutils import unittest, decorate_all_tests import psycopg2 -import tests +from testconfig import dsn -class Psycopg2TestCase(dbapi20.DatabaseAPI20Test): +class Psycopg2Tests(dbapi20.DatabaseAPI20Test): driver = psycopg2 connect_args = () - connect_kw_args = {'dsn': tests.dsn} + connect_kw_args = {'dsn': dsn} lower_func = 'lower' # For stored procedure test @@ -21,6 +46,15 @@ pass +class Psycopg2TPCTests(dbapi20_tpc.TwoPhaseCommitTests, unittest.TestCase): + driver = psycopg2 + + def connect(self): + return psycopg2.connect(dsn=dsn) + +decorate_all_tests(Psycopg2TPCTests, skip_if_tpc_disabled) + + def test_suite(): return unittest.TestLoader().loadTestsFromName(__name__) diff -Nru psycopg2-2.0.13/tests/test_quote.py psycopg2-2.4.5/tests/test_quote.py --- psycopg2-2.0.13/tests/test_quote.py 2009-10-04 21:32:35.000000000 +0000 +++ psycopg2-2.4.5/tests/test_quote.py 2012-03-28 21:09:15.000000000 +0000 @@ -1,10 +1,34 @@ #!/usr/bin/env python -import unittest -import warnings + +# test_quote.py - unit test for strings quoting +# +# Copyright (C) 2007-2011 Daniele Varrazzo +# +# psycopg2 is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# In addition, as a special exception, the copyright holders give +# permission to link this program with the OpenSSL library (or with +# modified versions of OpenSSL that use the same license as OpenSSL), +# and distribute linked combinations including the two. +# +# You must obey the GNU Lesser General Public License in all respects for +# all of the code used other than OpenSSL. +# +# psycopg2 is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +# License for more details. + +import sys +from testutils import unittest +from testconfig import dsn import psycopg2 import psycopg2.extensions -import tests +from psycopg2.extensions import b class QuotingTestCase(unittest.TestCase): r"""Checks the correct quoting of strings and binary objects. @@ -21,11 +45,11 @@ The tests also check that no warning is raised ('escape_string_warning' should be on). - http://www.postgresql.org/docs/8.1/static/sql-syntax.html#SQL-SYNTAX-STRINGS - http://www.postgresql.org/docs/8.1/static/runtime-config-compatible.html + http://www.postgresql.org/docs/current/static/sql-syntax-lexical.html#SQL-SYNTAX-STRINGS + http://www.postgresql.org/docs/current/static/runtime-config-compatible.html """ def setUp(self): - self.conn = psycopg2.connect(tests.dsn) + self.conn = psycopg2.connect(dsn) def tearDown(self): self.conn.close() @@ -44,14 +68,24 @@ self.assert_(not self.conn.notices) def test_binary(self): - data = """some data with \000\013 binary + data = b("""some data with \000\013 binary stuff into, 'quotes' and \\ a backslash too. - """ - data += "".join(map(chr, range(256))) + """) + if sys.version_info[0] < 3: + data += "".join(map(chr, range(256))) + else: + data += bytes(range(256)) curs = self.conn.cursor() curs.execute("SELECT %s::bytea;", (psycopg2.Binary(data),)) - res = str(curs.fetchone()[0]) + if sys.version_info[0] < 3: + res = str(curs.fetchone()[0]) + else: + res = curs.fetchone()[0].tobytes() + + if res[0] in (b('x'), ord(b('x'))) and self.conn.server_version >= 90000: + return self.skipTest( + "bytea broken with server >= 9.0, libpq < 9") self.assertEqual(res, data) self.assert_(not self.conn.notices) @@ -61,9 +95,9 @@ curs.execute("SHOW server_encoding") server_encoding = curs.fetchone()[0] if server_encoding != "UTF8": - warnings.warn("Unicode test skipped since server encoding is %s" - % server_encoding) - return + return self.skipTest( + "Unicode test skipped since server encoding is %s" + % server_encoding) data = u"""some data with \t chars to escape into, 'quotes', \u20ac euro sign and \\ a backslash too. @@ -72,13 +106,62 @@ if not 0xD800 <= u <= 0xDFFF ])) # surrogate area self.conn.set_client_encoding('UNICODE') - psycopg2.extensions.register_type(psycopg2.extensions.UNICODE) + psycopg2.extensions.register_type(psycopg2.extensions.UNICODE, self.conn) curs.execute("SELECT %s::text;", (data,)) res = curs.fetchone()[0] self.assertEqual(res, data) self.assert_(not self.conn.notices) + def test_latin1(self): + self.conn.set_client_encoding('LATIN1') + curs = self.conn.cursor() + if sys.version_info[0] < 3: + data = ''.join(map(chr, range(32, 127) + range(160, 256))) + else: + data = bytes(range(32, 127) + range(160, 256)).decode('latin1') + + # as string + curs.execute("SELECT %s::text;", (data,)) + res = curs.fetchone()[0] + self.assertEqual(res, data) + self.assert_(not self.conn.notices) + + # as unicode + if sys.version_info[0] < 3: + psycopg2.extensions.register_type(psycopg2.extensions.UNICODE, self.conn) + data = data.decode('latin1') + + curs.execute("SELECT %s::text;", (data,)) + res = curs.fetchone()[0] + self.assertEqual(res, data) + self.assert_(not self.conn.notices) + + def test_koi8(self): + self.conn.set_client_encoding('KOI8') + curs = self.conn.cursor() + if sys.version_info[0] < 3: + data = ''.join(map(chr, range(32, 127) + range(128, 256))) + else: + data = bytes(range(32, 127) + range(128, 256)).decode('koi8_r') + + # as string + curs.execute("SELECT %s::text;", (data,)) + res = curs.fetchone()[0] + self.assertEqual(res, data) + self.assert_(not self.conn.notices) + + # as unicode + if sys.version_info[0] < 3: + psycopg2.extensions.register_type(psycopg2.extensions.UNICODE, self.conn) + data = data.decode('koi8_r') + + curs.execute("SELECT %s::text;", (data,)) + res = curs.fetchone()[0] + self.assertEqual(res, data) + self.assert_(not self.conn.notices) + + def test_suite(): return unittest.TestLoader().loadTestsFromName(__name__) diff -Nru psycopg2-2.0.13/tests/test_transaction.py psycopg2-2.4.5/tests/test_transaction.py --- psycopg2-2.0.13/tests/test_transaction.py 2009-10-04 21:32:35.000000000 +0000 +++ psycopg2-2.4.5/tests/test_transaction.py 2011-02-27 12:03:48.000000000 +0000 @@ -1,17 +1,39 @@ #!/usr/bin/env python + +# test_transaction - unit test on transaction behaviour +# +# Copyright (C) 2007-2011 Federico Di Gregorio +# +# psycopg2 is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# In addition, as a special exception, the copyright holders give +# permission to link this program with the OpenSSL library (or with +# modified versions of OpenSSL that use the same license as OpenSSL), +# and distribute linked combinations including the two. +# +# You must obey the GNU Lesser General Public License in all respects for +# all of the code used other than OpenSSL. +# +# psycopg2 is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +# License for more details. + import threading -import unittest +from testutils import unittest, skip_before_postgres import psycopg2 from psycopg2.extensions import ( ISOLATION_LEVEL_SERIALIZABLE, STATUS_BEGIN, STATUS_READY) -import tests - +from testconfig import dsn class TransactionTests(unittest.TestCase): def setUp(self): - self.conn = psycopg2.connect(tests.dsn) + self.conn = psycopg2.connect(dsn) self.conn.set_isolation_level(ISOLATION_LEVEL_SERIALIZABLE) curs = self.conn.cursor() curs.execute(''' @@ -75,7 +97,7 @@ """Test deadlock and serialization failure errors.""" def connect(self): - conn = psycopg2.connect(tests.dsn) + conn = psycopg2.connect(dsn) conn.set_isolation_level(ISOLATION_LEVEL_SERIALIZABLE) return conn @@ -204,13 +226,17 @@ error, psycopg2.extensions.TransactionRollbackError)) -class QueryCancelationTests(unittest.TestCase): - """Tests for query cancelation.""" +class QueryCancellationTests(unittest.TestCase): + """Tests for query cancellation.""" def setUp(self): - self.conn = psycopg2.connect(tests.dsn) + self.conn = psycopg2.connect(dsn) self.conn.set_isolation_level(ISOLATION_LEVEL_SERIALIZABLE) + def tearDown(self): + self.conn.close() + + @skip_before_postgres(8, 2) def test_statement_timeout(self): curs = self.conn.cursor() # Set a low statement timeout, then sleep for a longer period. @@ -222,3 +248,5 @@ def test_suite(): return unittest.TestLoader().loadTestsFromName(__name__) +if __name__ == "__main__": + unittest.main() diff -Nru psycopg2-2.0.13/tests/test_types_basic.py psycopg2-2.4.5/tests/test_types_basic.py --- psycopg2-2.0.13/tests/test_types_basic.py 1970-01-01 00:00:00.000000000 +0000 +++ psycopg2-2.4.5/tests/test_types_basic.py 2011-12-11 21:18:11.000000000 +0000 @@ -0,0 +1,474 @@ +#!/usr/bin/env python +# +# types_basic.py - tests for basic types conversions +# +# Copyright (C) 2004-2010 Federico Di Gregorio +# +# psycopg2 is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# In addition, as a special exception, the copyright holders give +# permission to link this program with the OpenSSL library (or with +# modified versions of OpenSSL that use the same license as OpenSSL), +# and distribute linked combinations including the two. +# +# You must obey the GNU Lesser General Public License in all respects for +# all of the code used other than OpenSSL. +# +# psycopg2 is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +# License for more details. + +try: + import decimal +except: + pass +import sys +import testutils +from testutils import unittest, decorate_all_tests +from testconfig import dsn + +import psycopg2 +from psycopg2.extensions import b + + +class TypesBasicTests(unittest.TestCase): + """Test that all type conversions are working.""" + + def setUp(self): + self.conn = psycopg2.connect(dsn) + + def tearDown(self): + self.conn.close() + + def execute(self, *args): + curs = self.conn.cursor() + curs.execute(*args) + return curs.fetchone()[0] + + def testQuoting(self): + s = "Quote'this\\! ''ok?''" + self.failUnless(self.execute("SELECT %s AS foo", (s,)) == s, + "wrong quoting: " + s) + + def testUnicode(self): + s = u"Quote'this\\! ''ok?''" + self.failUnless(self.execute("SELECT %s AS foo", (s,)) == s, + "wrong unicode quoting: " + s) + + 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)) + if sys.version_info[0:2] < (2, 4): + s = self.execute("SELECT %s AS foo", (19.10,)) + self.failUnless(abs(s - 19.10) < 0.001, + "wrong float quoting: " + str(s)) + + def testBoolean(self): + x = self.execute("SELECT %s as foo", (False,)) + self.assert_(x is False) + x = self.execute("SELECT %s as foo", (True,)) + self.assert_(x is True) + + def testDecimal(self): + if sys.version_info[0:2] >= (2, 4): + s = self.execute("SELECT %s AS foo", (decimal.Decimal("19.10"),)) + self.failUnless(s - decimal.Decimal("19.10") == 0, + "wrong decimal quoting: " + str(s)) + s = self.execute("SELECT %s AS foo", (decimal.Decimal("NaN"),)) + self.failUnless(str(s) == "NaN", "wrong decimal quoting: " + str(s)) + self.failUnless(type(s) == decimal.Decimal, "wrong decimal conversion: " + repr(s)) + s = self.execute("SELECT %s AS foo", (decimal.Decimal("infinity"),)) + self.failUnless(str(s) == "NaN", "wrong decimal quoting: " + str(s)) + self.failUnless(type(s) == decimal.Decimal, "wrong decimal conversion: " + repr(s)) + s = self.execute("SELECT %s AS foo", (decimal.Decimal("-infinity"),)) + self.failUnless(str(s) == "NaN", "wrong decimal quoting: " + str(s)) + self.failUnless(type(s) == decimal.Decimal, "wrong decimal conversion: " + repr(s)) + else: + return self.skipTest("decimal not available") + + def testFloatNan(self): + try: + float("nan") + except ValueError: + return self.skipTest("nan not available on this platform") + + s = self.execute("SELECT %s AS foo", (float("nan"),)) + self.failUnless(str(s) == "nan", "wrong float quoting: " + str(s)) + self.failUnless(type(s) == float, "wrong float conversion: " + repr(s)) + + def testFloatInf(self): + try: + self.execute("select 'inf'::float") + except psycopg2.DataError: + return self.skipTest("inf::float not available on the server") + except ValueError: + return self.skipTest("inf not available on this platform") + s = self.execute("SELECT %s AS foo", (float("inf"),)) + self.failUnless(str(s) == "inf", "wrong float quoting: " + str(s)) + self.failUnless(type(s) == float, "wrong float conversion: " + repr(s)) + + s = self.execute("SELECT %s AS foo", (float("-inf"),)) + self.failUnless(str(s) == "-inf", "wrong float quoting: " + str(s)) + + def testBinary(self): + if sys.version_info[0] < 3: + s = ''.join([chr(x) for x in range(256)]) + b = psycopg2.Binary(s) + buf = self.execute("SELECT %s::bytea AS foo", (b,)) + self.assertEqual(s, str(buf)) + else: + s = bytes(range(256)) + b = psycopg2.Binary(s) + buf = self.execute("SELECT %s::bytea AS foo", (b,)) + self.assertEqual(s, buf) + + def testBinaryNone(self): + b = psycopg2.Binary(None) + buf = self.execute("SELECT %s::bytea AS foo", (b,)) + self.assertEqual(buf, None) + + def testBinaryEmptyString(self): + # test to make sure an empty Binary is converted to an empty string + if sys.version_info[0] < 3: + b = psycopg2.Binary('') + self.assertEqual(str(b), "''::bytea") + else: + b = psycopg2.Binary(bytes([])) + self.assertEqual(str(b), "''::bytea") + + def testBinaryRoundTrip(self): + # test to make sure buffers returned by psycopg2 are + # understood by execute: + if sys.version_info[0] < 3: + s = ''.join([chr(x) for x in range(256)]) + buf = self.execute("SELECT %s::bytea AS foo", (psycopg2.Binary(s),)) + buf2 = self.execute("SELECT %s::bytea AS foo", (buf,)) + self.assertEqual(s, str(buf2)) + else: + s = bytes(range(256)) + buf = self.execute("SELECT %s::bytea AS foo", (psycopg2.Binary(s),)) + buf2 = self.execute("SELECT %s::bytea AS foo", (buf,)) + self.assertEqual(s, buf2) + + def testArray(self): + s = self.execute("SELECT %s AS foo", ([[1,2],[3,4]],)) + self.failUnlessEqual(s, [[1,2],[3,4]]) + s = self.execute("SELECT %s AS foo", (['one', 'two', 'three'],)) + self.failUnlessEqual(s, ['one', 'two', 'three']) + + def testEmptyArrayRegression(self): + # ticket #42 + import datetime + curs = self.conn.cursor() + curs.execute("create table array_test (id integer, col timestamp without time zone[])") + + curs.execute("insert into array_test values (%s, %s)", (1, [datetime.date(2011,2,14)])) + curs.execute("select col from array_test where id = 1") + self.assertEqual(curs.fetchone()[0], [datetime.datetime(2011, 2, 14, 0, 0)]) + + curs.execute("insert into array_test values (%s, %s)", (2, [])) + curs.execute("select col from array_test where id = 2") + self.assertEqual(curs.fetchone()[0], []) + + def testEmptyArray(self): + s = self.execute("SELECT '{}' AS foo") + self.failUnlessEqual(s, []) + s = self.execute("SELECT '{}'::text[] AS foo") + self.failUnlessEqual(s, []) + s = self.execute("SELECT %s AS foo", ([],)) + self.failUnlessEqual(s, []) + s = self.execute("SELECT 1 != ALL(%s)", ([],)) + self.failUnlessEqual(s, True) + # but don't break the strings :) + s = self.execute("SELECT '{}'::text AS foo") + self.failUnlessEqual(s, "{}") + + def testArrayEscape(self): + ss = ['', '\\', '"', '\\\\', '\\"'] + for s in ss: + r = self.execute("SELECT %s AS foo", (s,)) + self.failUnlessEqual(s, r) + r = self.execute("SELECT %s AS foo", ([s],)) + self.failUnlessEqual([s], r) + + r = self.execute("SELECT %s AS foo", (ss,)) + self.failUnlessEqual(ss, r) + + @testutils.skip_from_python(3) + def testTypeRoundtripBuffer(self): + o1 = buffer("".join(map(chr, range(256)))) + o2 = self.execute("select %s;", (o1,)) + self.assertEqual(type(o1), type(o2)) + + # Test with an empty buffer + o1 = buffer("") + o2 = self.execute("select %s;", (o1,)) + self.assertEqual(type(o1), type(o2)) + self.assertEqual(str(o1), str(o2)) + + @testutils.skip_from_python(3) + def testTypeRoundtripBufferArray(self): + o1 = buffer("".join(map(chr, range(256)))) + o1 = [o1] + o2 = self.execute("select %s;", (o1,)) + self.assertEqual(type(o1[0]), type(o2[0])) + self.assertEqual(str(o1[0]), str(o2[0])) + + @testutils.skip_before_python(3) + def testTypeRoundtripBytes(self): + o1 = bytes(range(256)) + o2 = self.execute("select %s;", (o1,)) + self.assertEqual(memoryview, type(o2)) + + # Test with an empty buffer + o1 = bytes([]) + o2 = self.execute("select %s;", (o1,)) + self.assertEqual(memoryview, type(o2)) + + @testutils.skip_before_python(3) + def testTypeRoundtripBytesArray(self): + o1 = bytes(range(256)) + o1 = [o1] + o2 = self.execute("select %s;", (o1,)) + self.assertEqual(memoryview, type(o2[0])) + + @testutils.skip_before_python(2, 6) + def testAdaptBytearray(self): + o1 = bytearray(range(256)) + o2 = self.execute("select %s;", (o1,)) + + if sys.version_info[0] < 3: + self.assertEqual(buffer, type(o2)) + else: + self.assertEqual(memoryview, type(o2)) + + self.assertEqual(len(o1), len(o2)) + for c1, c2 in zip(o1, o2): + self.assertEqual(c1, ord(c2)) + + # Test with an empty buffer + o1 = bytearray([]) + o2 = self.execute("select %s;", (o1,)) + + self.assertEqual(len(o2), 0) + if sys.version_info[0] < 3: + self.assertEqual(buffer, type(o2)) + else: + self.assertEqual(memoryview, type(o2)) + + @testutils.skip_before_python(2, 7) + def testAdaptMemoryview(self): + o1 = memoryview(bytearray(range(256))) + o2 = self.execute("select %s;", (o1,)) + if sys.version_info[0] < 3: + self.assertEqual(buffer, type(o2)) + else: + self.assertEqual(memoryview, type(o2)) + + # Test with an empty buffer + o1 = memoryview(bytearray([])) + o2 = self.execute("select %s;", (o1,)) + if sys.version_info[0] < 3: + self.assertEqual(buffer, type(o2)) + else: + self.assertEqual(memoryview, type(o2)) + + def testByteaHexCheckFalsePositive(self): + # the check \x -> x to detect bad bytea decode + # may be fooled if the first char is really an 'x' + o1 = psycopg2.Binary(b('x')) + o2 = self.execute("SELECT %s::bytea AS foo", (o1,)) + self.assertEqual(b('x'), o2[0]) + + def testNegNumber(self): + d1 = self.execute("select -%s;", (decimal.Decimal('-1.0'),)) + self.assertEqual(1, d1) + f1 = self.execute("select -%s;", (-1.0,)) + self.assertEqual(1, f1) + i1 = self.execute("select -%s;", (-1,)) + self.assertEqual(1, i1) + l1 = self.execute("select -%s;", (-1L,)) + self.assertEqual(1, l1) + + def testGenericArray(self): + a = self.execute("select '{1,2,3}'::int4[]") + self.assertEqual(a, [1,2,3]) + a = self.execute("select array['a','b','''']::text[]") + self.assertEqual(a, ['a','b',"'"]) + + @testutils.skip_before_postgres(8, 2) + def testGenericArrayNull(self): + def caster(s, cur): + if s is None: return "nada" + return int(s) * 2 + base = psycopg2.extensions.new_type((23,), "INT4", caster) + array = psycopg2.extensions.new_array_type((1007,), "INT4ARRAY", base) + + psycopg2.extensions.register_type(array, self.conn) + a = self.execute("select '{1,2,3}'::int4[]") + self.assertEqual(a, [2,4,6]) + a = self.execute("select '{1,2,NULL}'::int4[]") + self.assertEqual(a, [2,4,'nada']) + + +class AdaptSubclassTest(unittest.TestCase): + def test_adapt_subtype(self): + from psycopg2.extensions import adapt + class Sub(str): pass + s1 = "hel'lo" + s2 = Sub(s1) + self.assertEqual(adapt(s1).getquoted(), adapt(s2).getquoted()) + + def test_adapt_most_specific(self): + from psycopg2.extensions import adapt, register_adapter, AsIs + + class A(object): pass + class B(A): pass + class C(B): pass + + register_adapter(A, lambda a: AsIs("a")) + register_adapter(B, lambda b: AsIs("b")) + try: + self.assertEqual(b('b'), adapt(C()).getquoted()) + finally: + del psycopg2.extensions.adapters[A, psycopg2.extensions.ISQLQuote] + del psycopg2.extensions.adapters[B, psycopg2.extensions.ISQLQuote] + + @testutils.skip_from_python(3) + def test_no_mro_no_joy(self): + from psycopg2.extensions import adapt, register_adapter, AsIs + + class A: pass + class B(A): pass + + register_adapter(A, lambda a: AsIs("a")) + try: + self.assertRaises(psycopg2.ProgrammingError, adapt, B()) + finally: + del psycopg2.extensions.adapters[A, psycopg2.extensions.ISQLQuote] + + + @testutils.skip_before_python(3) + def test_adapt_subtype_3(self): + from psycopg2.extensions import adapt, register_adapter, AsIs + + class A: pass + class B(A): pass + + register_adapter(A, lambda a: AsIs("a")) + try: + self.assertEqual(b("a"), adapt(B()).getquoted()) + finally: + del psycopg2.extensions.adapters[A, psycopg2.extensions.ISQLQuote] + + +class ByteaParserTest(unittest.TestCase): + """Unit test for our bytea format parser.""" + def setUp(self): + try: + self._cast = self._import_cast() + except Exception, e: + self._cast = None + self._exc = e + + def _import_cast(self): + """Use ctypes to access the C function. + + Raise any sort of error: we just support this where ctypes works as + expected. + """ + import ctypes + lib = ctypes.cdll.LoadLibrary(psycopg2._psycopg.__file__) + cast = lib.typecast_BINARY_cast + cast.argtypes = [ctypes.c_char_p, ctypes.c_size_t, ctypes.py_object] + cast.restype = ctypes.py_object + return cast + + def cast(self, buffer): + """Cast a buffer from the output format""" + l = buffer and len(buffer) or 0 + rv = self._cast(buffer, l, None) + + if rv is None: + return None + + if sys.version_info[0] < 3: + return str(rv) + else: + return rv.tobytes() + + def test_null(self): + rv = self.cast(None) + self.assertEqual(rv, None) + + def test_blank(self): + rv = self.cast(b('')) + self.assertEqual(rv, b('')) + + def test_blank_hex(self): + # Reported as problematic in ticket #48 + rv = self.cast(b('\\x')) + self.assertEqual(rv, b('')) + + def test_full_hex(self, upper=False): + buf = ''.join(("%02x" % i) for i in range(256)) + if upper: buf = buf.upper() + buf = '\\x' + buf + rv = self.cast(b(buf)) + if sys.version_info[0] < 3: + self.assertEqual(rv, ''.join(map(chr, range(256)))) + else: + self.assertEqual(rv, bytes(range(256))) + + def test_full_hex_upper(self): + return self.test_full_hex(upper=True) + + def test_full_escaped_octal(self): + buf = ''.join(("\\%03o" % i) for i in range(256)) + rv = self.cast(b(buf)) + if sys.version_info[0] < 3: + self.assertEqual(rv, ''.join(map(chr, range(256)))) + else: + self.assertEqual(rv, bytes(range(256))) + + def test_escaped_mixed(self): + import string + buf = ''.join(("\\%03o" % i) for i in range(32)) + buf += string.ascii_letters + buf += ''.join('\\' + c for c in string.ascii_letters) + buf += '\\\\' + rv = self.cast(b(buf)) + if sys.version_info[0] < 3: + tgt = ''.join(map(chr, range(32))) \ + + string.ascii_letters * 2 + '\\' + else: + tgt = bytes(range(32)) + \ + (string.ascii_letters * 2 + '\\').encode('ascii') + + self.assertEqual(rv, tgt) + +def skip_if_cant_cast(f): + def skip_if_cant_cast_(self, *args, **kwargs): + if self._cast is None: + return self.skipTest("can't test bytea parser: %s - %s" + % (self._exc.__class__.__name__, self._exc)) + + return f(self, *args, **kwargs) + + return skip_if_cant_cast_ + +decorate_all_tests(ByteaParserTest, skip_if_cant_cast) + + +def test_suite(): + return unittest.TestLoader().loadTestsFromName(__name__) + +if __name__ == "__main__": + unittest.main() + diff -Nru psycopg2-2.0.13/tests/test_types_extras.py psycopg2-2.4.5/tests/test_types_extras.py --- psycopg2-2.0.13/tests/test_types_extras.py 1970-01-01 00:00:00.000000000 +0000 +++ psycopg2-2.4.5/tests/test_types_extras.py 2012-03-28 21:09:15.000000000 +0000 @@ -0,0 +1,744 @@ +#!/usr/bin/env python +# +# types_extras.py - tests for extras types conversions +# +# Copyright (C) 2008-2010 Federico Di Gregorio +# +# psycopg2 is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# psycopg2 is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +# License for more details. + +try: + import decimal +except: + pass +import re +import sys +from datetime import date + +from testutils import unittest, skip_if_no_uuid, skip_before_postgres + +import psycopg2 +import psycopg2.extras +from psycopg2.extensions import b + +from testconfig import dsn + + +def filter_scs(conn, s): + if conn.get_parameter_status("standard_conforming_strings") == 'off': + return s + else: + return s.replace(b("E'"), b("'")) + +class TypesExtrasTests(unittest.TestCase): + """Test that all type conversions are working.""" + + def setUp(self): + self.conn = psycopg2.connect(dsn) + + def tearDown(self): + self.conn.close() + + def execute(self, *args): + curs = self.conn.cursor() + curs.execute(*args) + return curs.fetchone()[0] + + @skip_if_no_uuid + def testUUID(self): + import uuid + psycopg2.extras.register_uuid() + u = uuid.UUID('9c6d5a77-7256-457e-9461-347b4358e350') + s = self.execute("SELECT %s AS foo", (u,)) + self.failUnless(u == s) + # must survive NULL cast to a uuid + s = self.execute("SELECT NULL::uuid AS foo") + self.failUnless(s is None) + + @skip_if_no_uuid + def testUUIDARRAY(self): + import uuid + psycopg2.extras.register_uuid() + u = [uuid.UUID('9c6d5a77-7256-457e-9461-347b4358e350'), uuid.UUID('9c6d5a77-7256-457e-9461-347b4358e352')] + s = self.execute("SELECT %s AS foo", (u,)) + self.failUnless(u == s) + # array with a NULL element + u = [uuid.UUID('9c6d5a77-7256-457e-9461-347b4358e350'), None] + s = self.execute("SELECT %s AS foo", (u,)) + self.failUnless(u == s) + # must survive NULL cast to a uuid[] + s = self.execute("SELECT NULL::uuid[] AS foo") + self.failUnless(s is None) + # what about empty arrays? + s = self.execute("SELECT '{}'::uuid[] AS foo") + self.failUnless(type(s) == list and len(s) == 0) + + def testINET(self): + psycopg2.extras.register_inet() + i = psycopg2.extras.Inet("192.168.1.0/24") + s = self.execute("SELECT %s AS foo", (i,)) + self.failUnless(i.addr == s.addr) + # must survive NULL cast to inet + s = self.execute("SELECT NULL::inet AS foo") + self.failUnless(s is None) + + def testINETARRAY(self): + psycopg2.extras.register_inet() + i = psycopg2.extras.Inet("192.168.1.0/24") + s = self.execute("SELECT %s AS foo", ([i],)) + self.failUnless(i.addr == s[0].addr) + # must survive NULL cast to inet + s = self.execute("SELECT NULL::inet[] AS foo") + self.failUnless(s is None) + + def test_inet_conform(self): + from psycopg2.extras import Inet + i = Inet("192.168.1.0/24") + a = psycopg2.extensions.adapt(i) + a.prepare(self.conn) + self.assertEqual( + filter_scs(self.conn, b("E'192.168.1.0/24'::inet")), + a.getquoted()) + + # adapts ok with unicode too + i = Inet(u"192.168.1.0/24") + a = psycopg2.extensions.adapt(i) + a.prepare(self.conn) + self.assertEqual( + filter_scs(self.conn, b("E'192.168.1.0/24'::inet")), + a.getquoted()) + + def test_adapt_fail(self): + class Foo(object): pass + self.assertRaises(psycopg2.ProgrammingError, + psycopg2.extensions.adapt, Foo(), psycopg2.extensions.ISQLQuote, None) + try: + psycopg2.extensions.adapt(Foo(), psycopg2.extensions.ISQLQuote, None) + except psycopg2.ProgrammingError, err: + self.failUnless(str(err) == "can't adapt type 'Foo'") + + +def skip_if_no_hstore(f): + def skip_if_no_hstore_(self): + from psycopg2.extras import HstoreAdapter + oids = HstoreAdapter.get_oids(self.conn) + if oids is None or not oids[0]: + return self.skipTest("hstore not available in test database") + return f(self) + + return skip_if_no_hstore_ + +class HstoreTestCase(unittest.TestCase): + def setUp(self): + self.conn = psycopg2.connect(dsn) + + def tearDown(self): + self.conn.close() + + def test_adapt_8(self): + if self.conn.server_version >= 90000: + return self.skipTest("skipping dict adaptation with PG pre-9 syntax") + + from psycopg2.extras import HstoreAdapter + + o = {'a': '1', 'b': "'", 'c': None} + if self.conn.encoding == 'UTF8': + o['d'] = u'\xe0' + + a = HstoreAdapter(o) + a.prepare(self.conn) + q = a.getquoted() + + self.assert_(q.startswith(b("((")), q) + ii = q[1:-1].split(b("||")) + ii.sort() + + self.assertEqual(len(ii), len(o)) + self.assertEqual(ii[0], filter_scs(self.conn, b("(E'a' => E'1')"))) + self.assertEqual(ii[1], filter_scs(self.conn, b("(E'b' => E'''')"))) + self.assertEqual(ii[2], filter_scs(self.conn, b("(E'c' => NULL)"))) + if 'd' in o: + encc = u'\xe0'.encode(psycopg2.extensions.encodings[self.conn.encoding]) + self.assertEqual(ii[3], filter_scs(self.conn, b("(E'd' => E'") + encc + b("')"))) + + def test_adapt_9(self): + if self.conn.server_version < 90000: + return self.skipTest("skipping dict adaptation with PG 9 syntax") + + from psycopg2.extras import HstoreAdapter + + o = {'a': '1', 'b': "'", 'c': None} + if self.conn.encoding == 'UTF8': + o['d'] = u'\xe0' + + a = HstoreAdapter(o) + a.prepare(self.conn) + q = a.getquoted() + + m = re.match(b(r'hstore\(ARRAY\[([^\]]+)\], ARRAY\[([^\]]+)\]\)'), q) + self.assert_(m, repr(q)) + + kk = m.group(1).split(b(", ")) + vv = m.group(2).split(b(", ")) + ii = zip(kk, vv) + ii.sort() + + def f(*args): + return tuple([filter_scs(self.conn, s) for s in args]) + + self.assertEqual(len(ii), len(o)) + self.assertEqual(ii[0], f(b("E'a'"), b("E'1'"))) + self.assertEqual(ii[1], f(b("E'b'"), b("E''''"))) + self.assertEqual(ii[2], f(b("E'c'"), b("NULL"))) + if 'd' in o: + encc = u'\xe0'.encode(psycopg2.extensions.encodings[self.conn.encoding]) + self.assertEqual(ii[3], f(b("E'd'"), b("E'") + encc + b("'"))) + + def test_parse(self): + from psycopg2.extras import HstoreAdapter + + def ok(s, d): + self.assertEqual(HstoreAdapter.parse(s, None), d) + + ok(None, None) + ok('', {}) + ok('"a"=>"1", "b"=>"2"', {'a': '1', 'b': '2'}) + ok('"a" => "1" ,"b" => "2"', {'a': '1', 'b': '2'}) + ok('"a"=>NULL, "b"=>"2"', {'a': None, 'b': '2'}) + ok(r'"a"=>"\"", "\""=>"2"', {'a': '"', '"': '2'}) + ok('"a"=>"\'", "\'"=>"2"', {'a': "'", "'": '2'}) + ok('"a"=>"1", "b"=>NULL', {'a': '1', 'b': None}) + ok(r'"a\\"=>"1"', {'a\\': '1'}) + ok(r'"a\""=>"1"', {'a"': '1'}) + ok(r'"a\\\""=>"1"', {r'a\"': '1'}) + ok(r'"a\\\\\""=>"1"', {r'a\\"': '1'}) + + def ko(s): + self.assertRaises(psycopg2.InterfaceError, + HstoreAdapter.parse, s, None) + + ko('a') + ko('"a"') + ko(r'"a\\""=>"1"') + ko(r'"a\\\\""=>"1"') + ko('"a=>"1"') + ko('"a"=>"1", "b"=>NUL') + + @skip_if_no_hstore + def test_register_conn(self): + from psycopg2.extras import register_hstore + + register_hstore(self.conn) + cur = self.conn.cursor() + cur.execute("select null::hstore, ''::hstore, 'a => b'::hstore") + t = cur.fetchone() + self.assert_(t[0] is None) + self.assertEqual(t[1], {}) + self.assertEqual(t[2], {'a': 'b'}) + + @skip_if_no_hstore + def test_register_curs(self): + from psycopg2.extras import register_hstore + + cur = self.conn.cursor() + register_hstore(cur) + cur.execute("select null::hstore, ''::hstore, 'a => b'::hstore") + t = cur.fetchone() + self.assert_(t[0] is None) + self.assertEqual(t[1], {}) + self.assertEqual(t[2], {'a': 'b'}) + + @skip_if_no_hstore + def test_register_unicode(self): + from psycopg2.extras import register_hstore + + register_hstore(self.conn, unicode=True) + cur = self.conn.cursor() + cur.execute("select null::hstore, ''::hstore, 'a => b'::hstore") + t = cur.fetchone() + self.assert_(t[0] is None) + self.assertEqual(t[1], {}) + self.assertEqual(t[2], {u'a': u'b'}) + self.assert_(isinstance(t[2].keys()[0], unicode)) + self.assert_(isinstance(t[2].values()[0], unicode)) + + @skip_if_no_hstore + def test_register_globally(self): + from psycopg2.extras import register_hstore, HstoreAdapter + + oids = HstoreAdapter.get_oids(self.conn) + try: + register_hstore(self.conn, globally=True) + conn2 = psycopg2.connect(dsn) + try: + cur2 = self.conn.cursor() + cur2.execute("select 'a => b'::hstore") + r = cur2.fetchone() + self.assert_(isinstance(r[0], dict)) + finally: + conn2.close() + finally: + psycopg2.extensions.string_types.pop(oids[0][0]) + + # verify the caster is not around anymore + cur = self.conn.cursor() + cur.execute("select 'a => b'::hstore") + r = cur.fetchone() + self.assert_(isinstance(r[0], str)) + + @skip_if_no_hstore + def test_roundtrip(self): + from psycopg2.extras import register_hstore + register_hstore(self.conn) + cur = self.conn.cursor() + + def ok(d): + cur.execute("select %s", (d,)) + d1 = cur.fetchone()[0] + self.assertEqual(len(d), len(d1)) + for k in d: + self.assert_(k in d1, k) + self.assertEqual(d[k], d1[k]) + + ok({}) + ok({'a': 'b', 'c': None}) + + ab = map(chr, range(32, 128)) + ok(dict(zip(ab, ab))) + ok({''.join(ab): ''.join(ab)}) + + self.conn.set_client_encoding('latin1') + if sys.version_info[0] < 3: + ab = map(chr, range(32, 127) + range(160, 255)) + else: + ab = bytes(range(32, 127) + range(160, 255)).decode('latin1') + + ok({''.join(ab): ''.join(ab)}) + ok(dict(zip(ab, ab))) + + @skip_if_no_hstore + def test_roundtrip_unicode(self): + from psycopg2.extras import register_hstore + register_hstore(self.conn, unicode=True) + cur = self.conn.cursor() + + def ok(d): + cur.execute("select %s", (d,)) + d1 = cur.fetchone()[0] + self.assertEqual(len(d), len(d1)) + for k, v in d1.iteritems(): + self.assert_(k in d, k) + self.assertEqual(d[k], v) + self.assert_(isinstance(k, unicode)) + self.assert_(v is None or isinstance(v, unicode)) + + ok({}) + ok({'a': 'b', 'c': None, 'd': u'\u20ac', u'\u2603': 'e'}) + + ab = map(unichr, range(1, 1024)) + ok({u''.join(ab): u''.join(ab)}) + ok(dict(zip(ab, ab))) + + @skip_if_no_hstore + def test_oid(self): + cur = self.conn.cursor() + cur.execute("select 'hstore'::regtype::oid") + oid = cur.fetchone()[0] + + # Note: None as conn_or_cursor is just for testing: not public + # interface and it may break in future. + from psycopg2.extras import register_hstore + register_hstore(None, globally=True, oid=oid) + try: + cur.execute("select null::hstore, ''::hstore, 'a => b'::hstore") + t = cur.fetchone() + self.assert_(t[0] is None) + self.assertEqual(t[1], {}) + self.assertEqual(t[2], {'a': 'b'}) + + finally: + psycopg2.extensions.string_types.pop(oid) + + @skip_if_no_hstore + @skip_before_postgres(8, 3) + def test_roundtrip_array(self): + from psycopg2.extras import register_hstore + register_hstore(self.conn) + + ds = [] + ds.append({}) + ds.append({'a': 'b', 'c': None}) + + ab = map(chr, range(32, 128)) + ds.append(dict(zip(ab, ab))) + ds.append({''.join(ab): ''.join(ab)}) + + self.conn.set_client_encoding('latin1') + if sys.version_info[0] < 3: + ab = map(chr, range(32, 127) + range(160, 255)) + else: + ab = bytes(range(32, 127) + range(160, 255)).decode('latin1') + + ds.append({''.join(ab): ''.join(ab)}) + ds.append(dict(zip(ab, ab))) + + cur = self.conn.cursor() + cur.execute("select %s", (ds,)) + ds1 = cur.fetchone()[0] + self.assertEqual(ds, ds1) + + @skip_if_no_hstore + @skip_before_postgres(8, 3) + def test_array_cast(self): + from psycopg2.extras import register_hstore + register_hstore(self.conn) + cur = self.conn.cursor() + cur.execute("select array['a=>1'::hstore, 'b=>2'::hstore];") + a = cur.fetchone()[0] + self.assertEqual(a, [{'a': '1'}, {'b': '2'}]) + + @skip_if_no_hstore + def test_array_cast_oid(self): + cur = self.conn.cursor() + cur.execute("select 'hstore'::regtype::oid, 'hstore[]'::regtype::oid") + oid, aoid = cur.fetchone() + + from psycopg2.extras import register_hstore + register_hstore(None, globally=True, oid=oid, array_oid=aoid) + try: + cur.execute("select null::hstore, ''::hstore, 'a => b'::hstore, '{a=>b}'::hstore[]") + t = cur.fetchone() + self.assert_(t[0] is None) + self.assertEqual(t[1], {}) + self.assertEqual(t[2], {'a': 'b'}) + self.assertEqual(t[3], [{'a': 'b'}]) + + finally: + psycopg2.extensions.string_types.pop(oid) + psycopg2.extensions.string_types.pop(aoid) + +def skip_if_no_composite(f): + def skip_if_no_composite_(self): + if self.conn.server_version < 80000: + return self.skipTest( + "server version %s doesn't support composite types" + % self.conn.server_version) + + return f(self) + + skip_if_no_composite_.__name__ = f.__name__ + return skip_if_no_composite_ + +class AdaptTypeTestCase(unittest.TestCase): + def setUp(self): + self.conn = psycopg2.connect(dsn) + + def tearDown(self): + self.conn.close() + + @skip_if_no_composite + def test_none_in_record(self): + curs = self.conn.cursor() + s = curs.mogrify("SELECT %s;", [(42, None)]) + self.assertEqual(b("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(b("SELECT NULL;"), s) + + finally: + ext.register_adapter(type(None), orig_adapter) + + def test_tokenization(self): + from psycopg2.extras import CompositeCaster + def ok(s, v): + self.assertEqual(CompositeCaster.tokenize(s), v) + + ok("(,)", [None, None]) + ok('(hello,,10.234,2010-11-11)', ['hello', None, '10.234', '2010-11-11']) + ok('(10,"""")', ['10', '"']) + ok('(10,",")', ['10', ',']) + ok(r'(10,"\\")', ['10', '\\']) + ok(r'''(10,"\\',""")''', ['10', '''\\',"''']) + ok('(10,"(20,""(30,40)"")")', ['10', '(20,"(30,40)")']) + ok('(10,"(20,""(30,""""(40,50)"""")"")")', ['10', '(20,"(30,""(40,50)"")")']) + ok('(,"(,""(a\nb\tc)"")")', [None, '(,"(a\nb\tc)")']) + ok('(\x01,\x02,\x03,\x04,\x05,\x06,\x07,\x08,"\t","\n","\x0b",' + '"\x0c","\r",\x0e,\x0f,\x10,\x11,\x12,\x13,\x14,\x15,\x16,' + '\x17,\x18,\x19,\x1a,\x1b,\x1c,\x1d,\x1e,\x1f," ",!,"""",#,' + '$,%,&,\',"(",")",*,+,",",-,.,/,0,1,2,3,4,5,6,7,8,9,:,;,<,=,>,?,' + '@,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,[,"\\\\",],' + '^,_,`,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,{,|,},' + '~,\x7f)', + map(chr, range(1, 128))) + ok('(,"\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f' + '\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !' + '""#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\\\]' + '^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f")', + [None, ''.join(map(chr, range(1, 128)))]) + + @skip_if_no_composite + def test_cast_composite(self): + oid = self._create_type("type_isd", + [('anint', 'integer'), ('astring', 'text'), ('adate', 'date')]) + + t = psycopg2.extras.register_composite("type_isd", self.conn) + self.assertEqual(t.name, 'type_isd') + self.assertEqual(t.oid, oid) + self.assert_(issubclass(t.type, tuple)) + self.assertEqual(t.attnames, ['anint', 'astring', 'adate']) + self.assertEqual(t.atttypes, [23,25,1082]) + + curs = self.conn.cursor() + r = (10, 'hello', date(2011,1,2)) + curs.execute("select %s::type_isd;", (r,)) + v = curs.fetchone()[0] + self.assert_(isinstance(v, t.type)) + self.assertEqual(v[0], 10) + self.assertEqual(v[1], "hello") + self.assertEqual(v[2], date(2011,1,2)) + + try: + from collections import namedtuple + except ImportError: + pass + else: + self.assert_(t.type is not tuple) + self.assertEqual(v.anint, 10) + self.assertEqual(v.astring, "hello") + self.assertEqual(v.adate, date(2011,1,2)) + + @skip_if_no_composite + def test_cast_nested(self): + self._create_type("type_is", + [("anint", "integer"), ("astring", "text")]) + self._create_type("type_r_dt", + [("adate", "date"), ("apair", "type_is")]) + self._create_type("type_r_ft", + [("afloat", "float8"), ("anotherpair", "type_r_dt")]) + + psycopg2.extras.register_composite("type_is", self.conn) + psycopg2.extras.register_composite("type_r_dt", self.conn) + psycopg2.extras.register_composite("type_r_ft", self.conn) + + curs = self.conn.cursor() + r = (0.25, (date(2011,1,2), (42, "hello"))) + curs.execute("select %s::type_r_ft;", (r,)) + v = curs.fetchone()[0] + + self.assertEqual(r, v) + + try: + from collections import namedtuple + except ImportError: + pass + else: + self.assertEqual(v.anotherpair.apair.astring, "hello") + + @skip_if_no_composite + def test_register_on_cursor(self): + self._create_type("type_ii", [("a", "integer"), ("b", "integer")]) + + curs1 = self.conn.cursor() + curs2 = self.conn.cursor() + psycopg2.extras.register_composite("type_ii", curs1) + curs1.execute("select (1,2)::type_ii") + self.assertEqual(curs1.fetchone()[0], (1,2)) + curs2.execute("select (1,2)::type_ii") + self.assertEqual(curs2.fetchone()[0], "(1,2)") + + @skip_if_no_composite + def test_register_on_connection(self): + self._create_type("type_ii", [("a", "integer"), ("b", "integer")]) + + conn1 = psycopg2.connect(dsn) + conn2 = psycopg2.connect(dsn) + try: + psycopg2.extras.register_composite("type_ii", conn1) + curs1 = conn1.cursor() + curs2 = conn2.cursor() + curs1.execute("select (1,2)::type_ii") + self.assertEqual(curs1.fetchone()[0], (1,2)) + curs2.execute("select (1,2)::type_ii") + self.assertEqual(curs2.fetchone()[0], "(1,2)") + finally: + conn1.close() + conn2.close() + + @skip_if_no_composite + def test_register_globally(self): + self._create_type("type_ii", [("a", "integer"), ("b", "integer")]) + + conn1 = psycopg2.connect(dsn) + conn2 = psycopg2.connect(dsn) + try: + t = psycopg2.extras.register_composite("type_ii", conn1, globally=True) + try: + curs1 = conn1.cursor() + curs2 = conn2.cursor() + curs1.execute("select (1,2)::type_ii") + self.assertEqual(curs1.fetchone()[0], (1,2)) + curs2.execute("select (1,2)::type_ii") + self.assertEqual(curs2.fetchone()[0], (1,2)) + finally: + # drop the registered typecasters to help the refcounting + # script to return precise values. + del psycopg2.extensions.string_types[t.typecaster.values[0]] + if t.array_typecaster: + del psycopg2.extensions.string_types[ + t.array_typecaster.values[0]] + + finally: + conn1.close() + conn2.close() + + @skip_if_no_composite + def test_composite_namespace(self): + curs = self.conn.cursor() + curs.execute(""" + select nspname from pg_namespace + where nspname = 'typens'; + """) + if not curs.fetchone(): + curs.execute("create schema typens;") + self.conn.commit() + + self._create_type("typens.typens_ii", + [("a", "integer"), ("b", "integer")]) + t = psycopg2.extras.register_composite( + "typens.typens_ii", self.conn) + curs.execute("select (4,8)::typens.typens_ii") + self.assertEqual(curs.fetchone()[0], (4,8)) + + @skip_if_no_composite + @skip_before_postgres(8, 4) + def test_composite_array(self): + oid = self._create_type("type_isd", + [('anint', 'integer'), ('astring', 'text'), ('adate', 'date')]) + + t = psycopg2.extras.register_composite("type_isd", self.conn) + + curs = self.conn.cursor() + r1 = (10, 'hello', date(2011,1,2)) + r2 = (20, 'world', date(2011,1,3)) + curs.execute("select %s::type_isd[];", ([r1, r2],)) + v = curs.fetchone()[0] + self.assertEqual(len(v), 2) + self.assert_(isinstance(v[0], t.type)) + self.assertEqual(v[0][0], 10) + self.assertEqual(v[0][1], "hello") + self.assertEqual(v[0][2], date(2011,1,2)) + self.assert_(isinstance(v[1], t.type)) + self.assertEqual(v[1][0], 20) + self.assertEqual(v[1][1], "world") + self.assertEqual(v[1][2], date(2011,1,3)) + + @skip_if_no_composite + def test_wrong_schema(self): + oid = self._create_type("type_ii", [("a", "integer"), ("b", "integer")]) + from psycopg2.extras import CompositeCaster + c = CompositeCaster('type_ii', oid, [('a', 23), ('b', 23), ('c', 23)]) + curs = self.conn.cursor() + psycopg2.extensions.register_type(c.typecaster, curs) + curs.execute("select (1,2)::type_ii") + self.assertRaises(psycopg2.DataError, curs.fetchone) + + @skip_if_no_composite + @skip_before_postgres(8, 4) + def test_from_tables(self): + curs = self.conn.cursor() + curs.execute("""create table ctest1 ( + id integer primary key, + temp int, + label varchar + );""") + + curs.execute("""alter table ctest1 drop temp;""") + + curs.execute("""create table ctest2 ( + id serial primary key, + label varchar, + test_id integer references ctest1(id) + );""") + + curs.execute("""insert into ctest1 (id, label) values + (1, 'test1'), + (2, 'test2');""") + curs.execute("""insert into ctest2 (label, test_id) values + ('testa', 1), + ('testb', 1), + ('testc', 2), + ('testd', 2);""") + + psycopg2.extras.register_composite("ctest1", curs) + psycopg2.extras.register_composite("ctest2", curs) + + curs.execute(""" + select ctest1, array_agg(ctest2) as test2s + from ( + select ctest1, ctest2 + from ctest1 inner join ctest2 on ctest1.id = ctest2.test_id + order by ctest1.id, ctest2.label + ) x group by ctest1;""") + + r = curs.fetchone() + self.assertEqual(r[0], (1, 'test1')) + self.assertEqual(r[1], [(1, 'testa', 1), (2, 'testb', 1)]) + r = curs.fetchone() + self.assertEqual(r[0], (2, 'test2')) + self.assertEqual(r[1], [(3, 'testc', 2), (4, 'testd', 2)]) + + def _create_type(self, name, fields): + curs = self.conn.cursor() + try: + curs.execute("drop type %s cascade;" % name) + except psycopg2.ProgrammingError: + self.conn.rollback() + + curs.execute("create type %s as (%s);" % (name, + ", ".join(["%s %s" % p for p in fields]))) + if '.' in name: + schema, name = name.split('.') + else: + schema = 'public' + + curs.execute("""\ + SELECT t.oid + FROM pg_type t JOIN pg_namespace ns ON typnamespace = ns.oid + WHERE typname = %s and nspname = %s; + """, (name, schema)) + oid = curs.fetchone()[0] + self.conn.commit() + return oid + + +def test_suite(): + return unittest.TestLoader().loadTestsFromName(__name__) + +if __name__ == "__main__": + unittest.main() + diff -Nru psycopg2-2.0.13/tests/testutils.py psycopg2-2.4.5/tests/testutils.py --- psycopg2-2.0.13/tests/testutils.py 1970-01-01 00:00:00.000000000 +0000 +++ psycopg2-2.4.5/tests/testutils.py 2011-05-11 07:58:49.000000000 +0000 @@ -0,0 +1,237 @@ +# testutils.py - utility module for psycopg2 testing. + +# +# Copyright (C) 2010-2011 Daniele Varrazzo +# +# psycopg2 is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# In addition, as a special exception, the copyright holders give +# permission to link this program with the OpenSSL library (or with +# modified versions of OpenSSL that use the same license as OpenSSL), +# and distribute linked combinations including the two. +# +# You must obey the GNU Lesser General Public License in all respects for +# all of the code used other than OpenSSL. +# +# psycopg2 is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +# License for more details. + + +# Use unittest2 if available. Otherwise mock a skip facility with warnings. + +import os +import sys + +try: + import unittest2 + unittest = unittest2 +except ImportError: + import unittest + unittest2 = None + +if hasattr(unittest, 'skipIf'): + skip = unittest.skip + skipIf = unittest.skipIf + +else: + import warnings + + def skipIf(cond, msg): + def skipIf_(f): + def skipIf__(self): + if cond: + warnings.warn(msg) + return + else: + return f(self) + return skipIf__ + return skipIf_ + + def skip(msg): + return skipIf(True, msg) + + def skipTest(self, msg): + warnings.warn(msg) + return + + unittest.TestCase.skipTest = skipTest + +# Silence warnings caused by the stubborness of the Python unittest maintainers +# http://bugs.python.org/issue9424 +if not hasattr(unittest.TestCase, 'assert_') \ +or unittest.TestCase.assert_ is not unittest.TestCase.assertTrue: + # mavaff... + unittest.TestCase.assert_ = unittest.TestCase.assertTrue + unittest.TestCase.failUnless = unittest.TestCase.assertTrue + unittest.TestCase.assertEquals = unittest.TestCase.assertEqual + unittest.TestCase.failUnlessEqual = unittest.TestCase.assertEqual + + +def decorate_all_tests(cls, decorator): + """Apply *decorator* to all the tests defined in the TestCase *cls*.""" + for n in dir(cls): + if n.startswith('test'): + setattr(cls, n, decorator(getattr(cls, n))) + + +def skip_if_no_uuid(f): + """Decorator to skip a test if uuid is not supported by Py/PG.""" + def skip_if_no_uuid_(self): + try: + import uuid + except ImportError: + return self.skipTest("uuid not available in this Python version") + + try: + cur = self.conn.cursor() + cur.execute("select typname from pg_type where typname = 'uuid'") + has = cur.fetchone() + finally: + self.conn.rollback() + + if has: + return f(self) + else: + return self.skipTest("uuid type not available on the server") + + return skip_if_no_uuid_ + + +def skip_if_tpc_disabled(f): + """Skip a test if the server has tpc support disabled.""" + def skip_if_tpc_disabled_(self): + from psycopg2 import ProgrammingError + cnn = self.connect() + cur = cnn.cursor() + try: + cur.execute("SHOW max_prepared_transactions;") + except ProgrammingError: + return self.skipTest( + "server too old: two phase transactions not supported.") + else: + mtp = int(cur.fetchone()[0]) + cnn.close() + + if not mtp: + return self.skipTest( + "server not configured for two phase transactions. " + "set max_prepared_transactions to > 0 to run the test") + return f(self) + + skip_if_tpc_disabled_.__name__ = f.__name__ + return skip_if_tpc_disabled_ + + +def skip_if_no_namedtuple(f): + def skip_if_no_namedtuple_(self): + try: + from collections import namedtuple + except ImportError: + return self.skipTest("collections.namedtuple not available") + else: + return f(self) + + skip_if_no_namedtuple_.__name__ = f.__name__ + return skip_if_no_namedtuple_ + + +def skip_if_no_iobase(f): + """Skip a test if io.TextIOBase is not available.""" + def skip_if_no_iobase_(self): + try: + from io import TextIOBase + except ImportError: + return self.skipTest("io.TextIOBase not found.") + else: + return f(self) + + return skip_if_no_iobase_ + + +def skip_before_postgres(*ver): + """Skip a test on PostgreSQL before a certain version.""" + ver = ver + (0,) * (3 - len(ver)) + def skip_before_postgres_(f): + def skip_before_postgres__(self): + if self.conn.server_version < int("%d%02d%02d" % ver): + return self.skipTest("skipped because PostgreSQL %s" + % self.conn.server_version) + else: + return f(self) + + return skip_before_postgres__ + return skip_before_postgres_ + +def skip_after_postgres(*ver): + """Skip a test on PostgreSQL after (including) a certain version.""" + ver = ver + (0,) * (3 - len(ver)) + def skip_after_postgres_(f): + def skip_after_postgres__(self): + if self.conn.server_version >= int("%d%02d%02d" % ver): + return self.skipTest("skipped because PostgreSQL %s" + % self.conn.server_version) + else: + return f(self) + + return skip_after_postgres__ + return skip_after_postgres_ + +def skip_before_python(*ver): + """Skip a test on Python before a certain version.""" + def skip_before_python_(f): + def skip_before_python__(self): + if sys.version_info[:len(ver)] < ver: + return self.skipTest("skipped because Python %s" + % ".".join(map(str, sys.version_info[:len(ver)]))) + else: + return f(self) + + return skip_before_python__ + return skip_before_python_ + +def skip_from_python(*ver): + """Skip a test on Python after (including) a certain version.""" + def skip_from_python_(f): + def skip_from_python__(self): + if sys.version_info[:len(ver)] >= ver: + return self.skipTest("skipped because Python %s" + % ".".join(map(str, sys.version_info[:len(ver)]))) + else: + return f(self) + + return skip_from_python__ + return skip_from_python_ + + +def script_to_py3(script): + """Convert a script to Python3 syntax if required.""" + if sys.version_info[0] < 3: + return script + + import tempfile + f = tempfile.NamedTemporaryFile(suffix=".py", delete=False) + f.write(script.encode()) + f.flush() + filename = f.name + f.close() + + # 2to3 is way too chatty + import logging + logging.basicConfig(filename=os.devnull) + + from lib2to3.main import main + if main("lib2to3.fixes", ['--no-diffs', '-w', '-n', filename]): + raise Exception('py3 conversion failed') + + f2 = open(filename) + try: + return f2.read() + finally: + f2.close() + os.remove(filename) + diff -Nru psycopg2-2.0.13/tests/types_basic.py psycopg2-2.4.5/tests/types_basic.py --- psycopg2-2.0.13/tests/types_basic.py 2009-10-04 21:32:35.000000000 +0000 +++ psycopg2-2.4.5/tests/types_basic.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,103 +0,0 @@ -#!/usr/bin/env python -# types_basic.py - tests for basic types conversions -# -# Copyright (C) 2004 Federico Di Gregorio -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by the -# Free Software Foundation; either version 2, or (at your option) any later -# version. -# -# This program is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY -# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# for more details. - -try: - import decimal -except: - pass -import sys -import unittest - -import psycopg2 -import tests - - -class TypesBasicTests(unittest.TestCase): - """Test that all type conversions are working.""" - - def setUp(self): - self.conn = psycopg2.connect(tests.dsn) - - def execute(self, *args): - curs = self.conn.cursor() - curs.execute(*args) - return curs.fetchone()[0] - - def testQuoting(self): - s = "Quote'this\\! ''ok?''" - self.failUnless(self.execute("SELECT %s AS foo", (s,)) == s, - "wrong quoting: " + s) - - def testUnicode(self): - s = u"Quote'this\\! ''ok?''" - self.failUnless(self.execute("SELECT %s AS foo", (s,)) == s, - "wrong unicode quoting: " + s) - - 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)) - # Python 2.4 defaults to Decimal? - if sys.version_info[0] >= 2 and sys.version_info[1] >= 4: - s = self.execute("SELECT %s AS foo", (19.10,)) - self.failUnless(s - decimal.Decimal("19.10") == 0, - "wrong decimal quoting: " + str(s)) - else: - s = self.execute("SELECT %s AS foo", (19.10,)) - self.failUnless(abs(s - 19.10) < 0.001, - "wrong float quoting: " + str(s)) - - def testFloat(self): - s = self.execute("SELECT %s AS foo", (float("nan"),)) - self.failUnless(str(s) == "nan", "wrong float quoting: " + str(s)) - self.failUnless(type(s) == float, "wrong float conversion: " + repr(s)) - s = self.execute("SELECT %s AS foo", (float("inf"),)) - self.failUnless(str(s) == "inf", "wrong float quoting: " + str(s)) - self.failUnless(type(s) == float, "wrong float conversion: " + repr(s)) - - def testBinary(self): - s = ''.join([chr(x) for x in range(256)]) - b = psycopg2.Binary(s) - buf = self.execute("SELECT %s::bytea AS foo", (b,)) - self.failUnless(str(buf) == s, "wrong binary quoting") - - def testBinaryEmptyString(self): - # test to make sure an empty Binary is converted to an empty string - b = psycopg2.Binary('') - self.assertEqual(str(b), "''") - - def testBinaryRoundTrip(self): - # test to make sure buffers returned by psycopg2 are - # understood by execute: - s = ''.join([chr(x) for x in range(256)]) - buf = self.execute("SELECT %s::bytea AS foo", (psycopg2.Binary(s),)) - buf2 = self.execute("SELECT %s::bytea AS foo", (buf,)) - self.failUnless(str(buf2) == s, "wrong binary quoting") - - def testArray(self): - s = self.execute("SELECT %s AS foo", ([[1,2],[3,4]],)) - self.failUnless(s == [[1,2],[3,4]], "wrong array quoting " + str(s)) - s = self.execute("SELECT %s AS foo", (['one', 'two', 'three'],)) - self.failUnless(s == ['one', 'two', 'three'], - "wrong array quoting " + str(s)) - - -def test_suite(): - return unittest.TestLoader().loadTestsFromName(__name__) - -if __name__ == "__main__": - unittest.main() - diff -Nru psycopg2-2.0.13/tests/types_extras.py psycopg2-2.4.5/tests/types_extras.py --- psycopg2-2.0.13/tests/types_extras.py 2009-10-04 21:32:35.000000000 +0000 +++ psycopg2-2.4.5/tests/types_extras.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,86 +0,0 @@ -#!/usr/bin/env python -# types_extras.py - tests for extras types conversions -# -# Copyright (C) 2008 Federico Di Gregorio -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by the -# Free Software Foundation; either version 2, or (at your option) any later -# version. -# -# This program is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY -# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# for more details. - -try: - import decimal -except: - pass -import sys -import unittest - -import psycopg2 -import psycopg2.extras -import tests - - -class TypesExtrasTests(unittest.TestCase): - """Test that all type conversions are working.""" - - def setUp(self): - self.conn = psycopg2.connect(tests.dsn) - - def execute(self, *args): - curs = self.conn.cursor() - curs.execute(*args) - return curs.fetchone()[0] - - def testUUID(self): - try: - import uuid - psycopg2.extras.register_uuid() - except: - return - u = uuid.UUID('9c6d5a77-7256-457e-9461-347b4358e350') - s = self.execute("SELECT %s AS foo", (u,)) - self.failUnless(u == s) - # must survive NULL cast to a uuid - s = self.execute("SELECT NULL::uuid AS foo") - self.failUnless(s is None) - - def testUUIDARRAY(self): - try: - import uuid - psycopg2.extras.register_uuid() - except: - return - u = [uuid.UUID('9c6d5a77-7256-457e-9461-347b4358e350'), uuid.UUID('9c6d5a77-7256-457e-9461-347b4358e352')] - s = self.execute("SELECT %s AS foo", (u,)) - self.failUnless(u == s) - # array with a NULL element - u = [uuid.UUID('9c6d5a77-7256-457e-9461-347b4358e350'), None] - s = self.execute("SELECT %s AS foo", (u,)) - self.failUnless(u == s) - # must survive NULL cast to a uuid[] - s = self.execute("SELECT NULL::uuid[] AS foo") - self.failUnless(s is None) - # what about empty arrays? - s = self.execute("SELECT '{}'::uuid[] AS foo") - self.failUnless(type(s) == list and len(s) == 0) - - def testINET(self): - psycopg2.extras.register_inet() - i = "192.168.1.0/24"; - s = self.execute("SELECT %s AS foo", (i,)) - self.failUnless(i == s) - # must survive NULL cast to inet - s = self.execute("SELECT NULL::inet AS foo") - self.failUnless(s is None) - -def test_suite(): - return unittest.TestLoader().loadTestsFromName(__name__) - -if __name__ == "__main__": - unittest.main() - diff -Nru psycopg2-2.0.13/ZPsycopgDA/DA.py psycopg2-2.4.5/ZPsycopgDA/DA.py --- psycopg2-2.0.13/ZPsycopgDA/DA.py 2009-10-04 21:32:35.000000000 +0000 +++ psycopg2-2.4.5/ZPsycopgDA/DA.py 2012-03-28 21:09:15.000000000 +0000 @@ -1,24 +1,22 @@ # ZPsycopgDA/DA.py - ZPsycopgDA Zope product: Database Connection # -# Copyright (C) 2004 Federico Di Gregorio +# Copyright (C) 2004-2010 Federico Di Gregorio # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by the -# Free Software Foundation; either version 2, or (at your option) any later -# version. +# psycopg2 is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. # -# Or, at your option this program (ZPsycopgDA) can be distributed under the -# Zope Public License (ZPL) Version 1.0, as published on the Zope web site, -# http://www.zope.org/Resources/ZPL. -# -# This program is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY -# or FITNESS FOR A PARTICULAR PURPOSE. -# -# See the LICENSE file for details. +# psycopg2 is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +# License for more details. + +# Import modules needed by _psycopg to allow tools like py2exe to do +# their work without bothering about the module dependencies. -ALLOWED_PSYCOPG_VERSIONS = ('2.0.7','2.0.8','2.0.9','2.0.10', '2.0.11', '2.0.12', '2.0.13') +ALLOWED_PSYCOPG_VERSIONS = ('2.4-beta1', '2.4-beta2', '2.4', '2.4.1', '2.4.2', '2.4.3', '2.4.4', '2.4.5') import sys import time @@ -196,7 +194,7 @@ # add icons -misc_={'conn': ImageFile('Shared/DC/ZRDB/www/DBAdapterFolder_icon.gif')} +misc_={'conn': ImageFile('icons/DBAdapterFolder_icon.gif', globals())} for icon in ('table', 'view', 'stable', 'what', 'field', 'text', 'bin', 'int', 'float', 'date', 'time', 'datetime'): diff -Nru psycopg2-2.0.13/ZPsycopgDA/db.py psycopg2-2.4.5/ZPsycopgDA/db.py --- psycopg2-2.0.13/ZPsycopgDA/db.py 2009-10-04 21:32:35.000000000 +0000 +++ psycopg2-2.4.5/ZPsycopgDA/db.py 2011-12-19 10:30:20.000000000 +0000 @@ -1,21 +1,19 @@ # ZPsycopgDA/db.py - query execution # -# Copyright (C) 2004 Federico Di Gregorio +# Copyright (C) 2004-2010 Federico Di Gregorio # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by the -# Free Software Foundation; either version 2, or (at your option) any later -# version. +# psycopg2 is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. # -# Or, at your option this program (ZPsycopgDA) can be distributed under the -# Zope Public License (ZPL) Version 1.0, as published on the Zope web site, -# http://www.zope.org/Resources/ZPL. -# -# This program is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY -# or FITNESS FOR A PARTICULAR PURPOSE. -# -# See the LICENSE file for details. +# psycopg2 is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +# License for more details. + +# Import modules needed by _psycopg to allow tools like py2exe to do +# their work without bothering about the module dependencies. from Shared.DC.ZRDB.TM import TM from Shared.DC.ZRDB import dbi_db @@ -41,17 +39,23 @@ self.dsn = dsn self.tilevel = tilevel self.typecasts = typecasts - self.encoding = enc + if enc is None or enc == "": + self.encoding = "utf-8" + else: + self.encoding = enc self.failures = 0 self.calls = 0 self.make_mappings() - def getconn(self, create=True): + def getconn(self, init=True): + # if init is False we are trying to get hold on an already existing + # connection, so we avoid to (re)initialize it risking errors. conn = pool.getconn(self.dsn) - conn.set_isolation_level(int(self.tilevel)) - conn.set_client_encoding(self.encoding) - for tc in self.typecasts: - register_type(tc, conn) + if init: + conn.set_isolation_level(int(self.tilevel)) + conn.set_client_encoding(self.encoding) + for tc in self.typecasts: + register_type(tc, conn) return conn def putconn(self, close=False): @@ -171,15 +175,15 @@ c.execute(qs) except TransactionRollbackError: # Ha, here we have to look like we are the ZODB raising conflict errrors, raising ZPublisher.Publish.Retry just doesn't work - logging.debug("Serialization Error, retrying transaction", exc_info=True) + #logging.debug("Serialization Error, retrying transaction", exc_info=True) raise ConflictError("TransactionRollbackError from psycopg2") except psycopg2.OperationalError: - logging.exception("Operational error on connection, closing it.") + #logging.exception("Operational error on connection, closing it.") try: # Only close our connection self.putconn(True) except: - logging.debug("Something went wrong when we tried to close the pool", exc_info=True) + #logging.debug("Something went wrong when we tried to close the pool", exc_info=True) pass if c.description is not None: nselects += 1 Binary files /tmp/2HyIbqyseQ/psycopg2-2.0.13/ZPsycopgDA/icons/DBAdapterFolder_icon.gif and /tmp/gnadcqLbN0/psycopg2-2.4.5/ZPsycopgDA/icons/DBAdapterFolder_icon.gif differ diff -Nru psycopg2-2.0.13/ZPsycopgDA/__init__.py psycopg2-2.4.5/ZPsycopgDA/__init__.py --- psycopg2-2.0.13/ZPsycopgDA/__init__.py 2009-04-26 12:09:21.000000000 +0000 +++ psycopg2-2.4.5/ZPsycopgDA/__init__.py 2011-02-06 15:58:34.000000000 +0000 @@ -1,21 +1,19 @@ # ZPsycopgDA/__init__.py - ZPsycopgDA Zope product # -# Copyright (C) 2004 Federico Di Gregorio +# Copyright (C) 2004-2010 Federico Di Gregorio # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by the -# Free Software Foundation; either version 2, or (at your option) any later -# version. +# psycopg2 is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. # -# Or, at your option this program (ZPsycopgDA) can be distributed under the -# Zope Public License (ZPL) Version 1.0, as published on the Zope web site, -# http://www.zope.org/Resources/ZPL. -# -# This program is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY -# or FITNESS FOR A PARTICULAR PURPOSE. -# -# See the LICENSE file for details. +# psycopg2 is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +# License for more details. + +# Import modules needed by _psycopg to allow tools like py2exe to do +# their work without bothering about the module dependencies. __doc__ = "ZPsycopg Database Adapter Registration." __version__ = '2.0' @@ -28,4 +26,4 @@ permission = 'Add Z Psycopg 2 Database Connections', constructors = (DA.manage_addZPsycopgConnectionForm, DA.manage_addZPsycopgConnection), - icon = SOFTWARE_HOME + '/Shared/DC/ZRDB/www/DBAdapterFolder_icon.gif') + icon = 'icons/DBAdapterFolder_icon.gif') diff -Nru psycopg2-2.0.13/ZPsycopgDA/pool.py psycopg2-2.4.5/ZPsycopgDA/pool.py --- psycopg2-2.0.13/ZPsycopgDA/pool.py 2009-04-26 12:09:21.000000000 +0000 +++ psycopg2-2.4.5/ZPsycopgDA/pool.py 2011-12-16 09:32:01.000000000 +0000 @@ -1,24 +1,22 @@ # ZPsycopgDA/pool.py - ZPsycopgDA Zope product: connection pooling # -# Copyright (C) 2004 Federico Di Gregorio +# Copyright (C) 2004-2010 Federico Di Gregorio # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by the -# Free Software Foundation; either version 2, or (at your option) any later -# version. +# psycopg2 is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. # -# Or, at your option this program (ZPsycopgDA) can be distributed under the -# Zope Public License (ZPL) Version 1.0, as published on the Zope web site, -# http://www.zope.org/Resources/ZPL. -# -# This program is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY -# or FITNESS FOR A PARTICULAR PURPOSE. -# -# See the LICENSE file for details. +# psycopg2 is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +# License for more details. + +# Import modules needed by _psycopg to allow tools like py2exe to do +# their work without bothering about the module dependencies. -# all the connections are held in a pool of pools, directly accessible by the -# ZPsycopgDA code in db.py +# All the connections are held in a pool of pools, directly accessible by the +# ZPsycopgDA code in db.py. import threading import psycopg2.pool