summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--.travis.yml13
-rw-r--r--ChangeLog2455
-rw-r--r--NEWS968
-rw-r--r--ZPsycopgDA/DA.py360
-rw-r--r--ZPsycopgDA/__init__.py29
-rw-r--r--ZPsycopgDA/db.py209
-rw-r--r--ZPsycopgDA/dtml/add.dtml108
-rw-r--r--ZPsycopgDA/dtml/browse.dtml11
-rw-r--r--ZPsycopgDA/dtml/edit.dtml84
-rw-r--r--ZPsycopgDA/dtml/table_info.dtml7
-rwxr-xr-xZPsycopgDA/icons/DBAdapterFolder_icon.gifbin897 -> 0 bytes
-rw-r--r--ZPsycopgDA/icons/bin.gifbin924 -> 0 bytes
-rw-r--r--ZPsycopgDA/icons/date.gifbin930 -> 0 bytes
-rw-r--r--ZPsycopgDA/icons/datetime.gifbin925 -> 0 bytes
-rw-r--r--ZPsycopgDA/icons/field.gifbin915 -> 0 bytes
-rw-r--r--ZPsycopgDA/icons/float.gifbin929 -> 0 bytes
-rw-r--r--ZPsycopgDA/icons/int.gifbin918 -> 0 bytes
-rw-r--r--ZPsycopgDA/icons/stable.gifbin884 -> 0 bytes
-rw-r--r--ZPsycopgDA/icons/table.gifbin878 -> 0 bytes
-rw-r--r--ZPsycopgDA/icons/text.gifbin918 -> 0 bytes
-rw-r--r--ZPsycopgDA/icons/time.gifbin926 -> 0 bytes
-rw-r--r--ZPsycopgDA/icons/view.gifbin893 -> 0 bytes
-rw-r--r--ZPsycopgDA/icons/what.gifbin894 -> 0 bytes
-rw-r--r--ZPsycopgDA/pool.py193
-rw-r--r--debian/README.zpsycopgda27
-rw-r--r--debian/changelog676
-rw-r--r--debian/compat1
-rw-r--r--debian/control64
-rw-r--r--debian/copyright112
-rw-r--r--debian/pycompat1
-rw-r--r--debian/python-psycopg2da.dzproduct3
-rwxr-xr-xdebian/rules93
-rw-r--r--debian/watch2
-rw-r--r--debian/zope-psycopgda2.dzproduct4
-rw-r--r--doc/src/_static/psycopg.css4
-rw-r--r--doc/src/advanced.rst19
-rw-r--r--doc/src/conf.py7
-rw-r--r--doc/src/connection.rst39
-rw-r--r--doc/src/cursor.rst62
-rw-r--r--doc/src/errorcodes.rst2
-rw-r--r--doc/src/extensions.rst37
-rw-r--r--doc/src/extras.rst296
-rw-r--r--doc/src/faq.rst8
-rw-r--r--doc/src/index.rst28
-rw-r--r--doc/src/install.rst258
-rw-r--r--doc/src/module.rst99
-rw-r--r--doc/src/news.rst4
-rw-r--r--doc/src/tools/lib/ticket_role.py39
-rw-r--r--doc/src/usage.rst471
-rw-r--r--lib/__init__.py30
-rw-r--r--lib/_json.py194
-rw-r--r--lib/_range.py468
-rw-r--r--lib/errorcodes.py7
-rw-r--r--lib/extensions.py19
-rw-r--r--lib/extras.py149
-rw-r--r--lib/pool.py24
-rw-r--r--psycopg/adapter_asis.c43
-rw-r--r--psycopg/adapter_binary.c48
-rw-r--r--psycopg/adapter_datetime.c43
-rw-r--r--psycopg/adapter_list.c81
-rw-r--r--psycopg/adapter_list.h1
-rw-r--r--psycopg/adapter_mxdatetime.c41
-rw-r--r--psycopg/adapter_pboolean.c45
-rw-r--r--psycopg/adapter_pdecimal.c45
-rw-r--r--psycopg/adapter_pfloat.c45
-rw-r--r--psycopg/adapter_pint.c45
-rw-r--r--psycopg/adapter_qstring.c145
-rw-r--r--psycopg/adapter_qstring.h7
-rw-r--r--psycopg/connection.h13
-rw-r--r--psycopg/connection_int.c43
-rw-r--r--psycopg/connection_type.c229
-rw-r--r--psycopg/cursor.h9
-rw-r--r--psycopg/cursor_int.c12
-rw-r--r--psycopg/cursor_type.c293
-rw-r--r--psycopg/diagnostics.h40
-rw-r--r--psycopg/diagnostics_type.c197
-rw-r--r--psycopg/error.h43
-rw-r--r--psycopg/error_type.c276
-rw-r--r--psycopg/green.c3
-rw-r--r--psycopg/lobject.h4
-rw-r--r--psycopg/lobject_type.c50
-rw-r--r--psycopg/microprotocols.c2
-rw-r--r--psycopg/microprotocols_proto.c37
-rw-r--r--psycopg/notify.h4
-rw-r--r--psycopg/notify_type.c81
-rw-r--r--psycopg/pqpath.c88
-rw-r--r--psycopg/pqpath.h5
-rw-r--r--psycopg/psycopg.h8
-rw-r--r--psycopg/psycopgmodule.c236
-rw-r--r--psycopg/python.h34
-rw-r--r--psycopg/typecast.c65
-rw-r--r--psycopg/typecast_array.c2
-rw-r--r--psycopg/typecast_basic.c14
-rw-r--r--psycopg/typecast_binary.c16
-rw-r--r--psycopg/typecast_datetime.c8
-rw-r--r--psycopg/typecast_mxdatetime.c6
-rw-r--r--psycopg/utils.c42
-rw-r--r--psycopg/xid.h10
-rw-r--r--psycopg/xid_type.c177
-rw-r--r--psycopg2da/DEPENDENCIES.cfg2
-rw-r--r--psycopg2da/PACKAGE.cfg24
-rw-r--r--psycopg2da/PUBLICATION.cfg9
-rw-r--r--psycopg2da/README.txt79
-rw-r--r--psycopg2da/__init__.py1
-rw-r--r--psycopg2da/adapter.py408
-rw-r--r--psycopg2da/configure.zcml51
-rw-r--r--psycopg2da/psycopg2da-configure.zcml1
-rw-r--r--psycopg2da/tests.py395
-rwxr-xr-xscripts/make_errorcodes.py3
-rw-r--r--setup.cfg29
-rw-r--r--setup.py22
-rwxr-xr-xtests/__init__.py7
-rwxr-xr-xtests/test_async.py19
-rwxr-xr-xtests/test_bug_gc.py13
-rwxr-xr-xtests/test_cancel.py11
-rwxr-xr-xtests/test_connection.py122
-rwxr-xr-xtests/test_copy.py25
-rwxr-xr-xtests/test_cursor.py77
-rwxr-xr-xtests/test_dates.py15
-rwxr-xr-xtests/test_extras_dictcursor.py31
-rwxr-xr-xtests/test_green.py9
-rwxr-xr-xtests/test_lobject.py45
-rwxr-xr-xtests/test_module.py128
-rwxr-xr-xtests/test_notify.py11
-rwxr-xr-xtests/test_quote.py21
-rwxr-xr-xtests/test_transaction.py27
-rwxr-xr-xtests/test_types_basic.py54
-rwxr-xr-xtests/test_types_extras.py814
-rwxr-xr-xtests/test_with.py208
-rw-r--r--tests/testutils.py95
-rw-r--r--tox.ini10
132 files changed, 5217 insertions, 7850 deletions
diff --git a/.gitignore b/.gitignore
index 065afbb..1b63d53 100644
--- a/.gitignore
+++ b/.gitignore
@@ -10,3 +10,4 @@ doc/src/_build/*
doc/html/*
doc/psycopg2.txt
env
+.tox
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..1aa2541
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,13 @@
+language: python
+
+python:
+ - 2.6
+ - 2.7
+
+before_script:
+ - psql -c 'create database psycopg2_test;' -U postgres
+
+install:
+ - python setup.py install
+
+script: make check
diff --git a/ChangeLog b/ChangeLog
deleted file mode 100644
index 57b803b..0000000
--- a/ChangeLog
+++ /dev/null
@@ -1,2455 +0,0 @@
-2010-12-18 Daniele Varrazzo <daniele.varrazzo@gmail.com>
-
- * 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 <daniele.varrazzo@gmail.com>
-
- * psycopg/utils.c: Added psycopg_strdup function.
-
-2010-12-14 Daniele Varrazzo <daniele.varrazzo@gmail.com>
-
- * 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 <daniele.varrazzo@gmail.com>
-
- * 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 <daniele.varrazzo@gmail.com>
-
- * lib/extras.py: DictRow items can be updated. Patch by Alex Aster.
-
-2010-11-28 Daniele Varrazzo <daniele.varrazzo@gmail.com>
-
- * Cancel patch from Jan integrated.
-
- * psycopg/connection_type.c: can't cancel a prepared connection
-
-2010-11-22 Daniele Varrazzo <daniele.varrazzo@gmail.com>
-
- * psycopg/connection_int.c: dropped notices hack to get COPY errors from
- V2 protocol.
-
-2010-11-18 Daniele Varrazzo <daniele.varrazzo@gmail.com>
-
- * 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 <daniele.varrazzo@gmail.com>
-
- * psycopg/connection_type.c: don't clobber exception if
- isolation_level_switch fails.
-
-2010-11-16 Daniele Varrazzo <daniele.varrazzo@gmail.com>
-
- * 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 <daniele.varrazzo@gmail.com>
-
- * 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 <daniele.varrazzo@gmail.com>
-
- * psycopg/green.c: functions unused outside the module marked static.
-
-2010-11-09 Daniele Varrazzo <daniele.varrazzo@gmail.com>
-
- * 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 <daniele.varrazzo@gmail.com>
-
- * 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 <daniele.varrazzo@gmail.com>
-
- * lib/extras.py: added NamedTupleCursor.
-
-2010-11-05 Daniele Varrazzo <daniele.varrazzo@gmail.com>
-
- * setup.py: bumped to version 2.3.dev0
-
- * merged dev branches for 2 phase commit, notify payload, hstore adapter
-
-2010-10-22 Daniele Varrazzo <daniele.varrazzo@gmail.com>
-
- * MANIFEST.in: Dropped reference to removed TODO file: it breaks 'pip'.
-
-2010-10-08 Daniele Varrazzo <daniele.varrazzo@gmail.com>
-
- * 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 <daniele.varrazzo@gmail.com>
-
- * Merged James Henstridge work on two-phase commit support.
-
- 2008-07-24 James Henstridge <james@jamesh.id.au>
-
- * 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 <james@jamesh.id.au>
-
- * 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 <james@jamesh.id.au>
-
- * beginnings of a TPC test harness
-
-2010-10-05 Daniele Varrazzo <daniele.varrazzo@gmail.com>
-
- * 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 <daniele.varrazzo@gmail.com>
-
- * lib/errorcodes.py: Added PostgreSQL 9.0 error codes.
-
-2010-08-05 Daniele Varrazzo <daniele.varrazzo@gmail.com>
-
- * 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 <fog@initd.org>
-
- * Release 2.2.2.
-
-2010-07-09 Daniele Varrazzo <daniele.varrazzo@gmail.com>
-
- * 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 <daniele.varrazzo@gmail.com>
-
- * 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 <fog@initd.org>
-
- * Release 2.2.1.
-
- * Builds again on Windows.
-
-2010-05-16 Federico Di Gregorio <fog@initd.org>
-
- * Release 2.2.0.
-
-2010-05-15 Federico Di Gregorio <fog@initd.org>
-
- * typecast.c: Fixed problem related to receiving None from Python
- when a string was expected.
-
-2010-05-07 Daniele Varrazzo <daniele.varrazzo@gmail.com>
-
- * 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 <daniele.varrazzo@gmail.com>
-
- * 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 <daniele.varrazzo@gmail.com>
-
- * 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 <daniele.varrazzo@gmail.com>
-
- * lib/pqpath.c: Fixed reference leak in notify reception.
-
- * Notifies are collected if available after every query execution.
-
-2010-04-13 Daniele Varrazzo <daniele.varrazzo@gmail.com>
-
- * lib/extensions.py: DECIMAL typecaster imported from _psycopg.
-
- * lib/extensions.py: PY* and MX* time typecaster imported from _psycopg.
-
-2010-04-11 Daniele Varrazzo <daniele.varrazzo@gmail.com>
-
- * psycopg/connection_type.c: Correctly parse keywords in connect().
-
-2010-04-07 Daniele Varrazzo <daniele.varrazzo@gmail.com>
-
- * 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 <fog@initd.org>
-
- * Fixed problem with asynchronous NOTIFYs.
-
- * Integrated async pacthes from Jan's git tree.
-
-2010-03-13 Federico Di Gregorio <fog@initd.org>
-
- * Release 2.0.14.
-
-2010-03-11 Federico Di Gregorio <fog@initd.org>
-
- * setup.py: one-liner from Devrim GÜNDÜZ to build with PostgreSQL
- alpha.
-
-2010-02-28 Federico Di Gregorio <fog@initd.org>
-
- * 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 <fog@initd.org>
-
- * Added new Decimal adapter that correctly converts NaN and infinity
- to PostgreSQL NaN numeric values. Also added tests.
-
-2010-02-15 Daniele Varrazzo <daniele.varrazzo@gmail.com>
-
- * lib/errorcodes.py: Updated to PostgreSQL 8.4; added lookup() function.
-
-2010-02-12 Daniele Varrazzo <daniele.varrazzo@gmail.com>
-
- * 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 <fog@initd.org>
-
- * 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 <fog@initd.org>
-
- * License migration: psycopg2 is now LGPL3 + OpenSSL exception.
-
- * TODO file was never updated so lets remove it.
-
-2010-02-10 Federico Di Gregorio <fog@initd.org>
-
- * lib/extras.py: fixed register_tstz_w_secs() error as reported by
- Karsten Hilbert.
-
-2009-11-25 Federico Di Gregorio <fog@initd.org>
-
- * 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 <fog@initd.org>
-
- * psycopg/adapter_pfloat.c: applied patch from Remy Blankto fix float
- loss of precision.
-
-2009-10-04 Federico Di Gregorio <fog@initd.org>
-
- * Release 2.0.13.
-
- * setup.py: applied patch from Christian Jacobsen to link to static
- version of libpq.
-
- * lib/extras.py: added support for UUID arrays.
-
- * psycopg/connection_int.c: applied patch from Richard Davies to avoid
- deadlocks with multiple threads using the same connection.
-
-2009-08-08 Federico Di Gregorio <fog@initd.org>
-
- * Release 2.0.12.
-
- * psycopg/lobject_int.c: fixed problem with writing large data using
- lo_write: apparently the large objects code does not like non-blocking
- connections.
-
- * setup.py: fixed version detection for PostgreSQL rc, as
- suggested by Sok Ann Yap.
-
- * ZPsycopgDA/db.py: applied serialization error retry from Brian
- Sutherland.
-
- * Implemented connection.reset() method to reset the connection to
- well-know default parameters. This is much faster than closing and
- reopening the connection. (Suggested by a bug report by Glenn
- Maynard.)
-
- * psycopg/cursor_type.c: unified size macro definitions in COPY TO
- and COPY FROM operations: now the buffer for column names is 8192
- bytes that should be enough even for very large tables.
-
-2009-05-19 Federico Di Gregorio <fog@initd.org>
-
- * Applied patch from Robert Munro to fix version check
- in ZPsycopgDA.
-
-2009-05-10 Federico Di Gregorio <fog@initd.org>
-
- * Release 2.0.11.
-
- * lib/extras.py: fixed crash in fetchone() when prefetching using
- a RealDictCursor.
-
- * psycopg/cursor_ext.c: now raise correct exception when fetching
- using a custom row factory results in an error.
-
- * lib/extras.py: applied DictRow "diet" patch from Marko Kreen.
-
-2009-04-21 Federico Di Gregorio <fog@initd.org>
-
- * setup.py: applied patch from Elvis Pranskevichus to make
- PostgreSQL version detection more robust.
-
-2009-04-20 Federico Di Gregorio <fog@initd.org>
-
- * Release 2.0.10.
-
- * psycopg/cursor_type.c: patch from Gangadharan to avoid double
- free of the cursor when the connection that creates it is closed
- implicitly by dealloc.
-
-2009-04-19 Federico Di Gregorio <fog@initd.org>
-
- * psycopg/connection_type.c: patch from Gangadharan to avoid double
- free of the connection object when calling close() implicitly.
-
- * psycopg/connection_type.c: patch from Marko Kreen to implement
- get_parameter_status().
-
- * psycopg/connection.*: exposed protocol_version and
- server_version attributes on the connection object.
-
- * lib/extras.py: patch from Marko Kreen to implement missing dict
- methods in DictRow.
-
-2009-04-04 Federico Di Gregorio <fog@initd.org>
-
- * connection_int.c(conn_notice_callback): removed all Python
- calls because conn_notice_callback() can be called without a lock
- on the GIL. Moved processing and cleanup of notices into their
- own functions that are called while holding the GIL.
-
-2009-04-01 Federico Di Gregorio <fog@initd.org>
-
- * Applied patch from Menno Smits to fix failures in test_dates
- when executed in older version of Python.
-
- * Applied patch from Menno Smits to fix lobject test failures.
-
-2009-03-08 Federico Di Gregorio <fog@initd.org>
-
- * microprotocols.c: None is always adapted to NULL to avoid
- errors when calling adapt on nested types without first checking
- for None.
-
-2009-03-02 Federico Di Gregorio <fog@initd.org>
-
- * Applied modified patch from Karsten Hilbert to provide a
- type-caster able to parse times with seconds in the time zone.
-
- * Applied patch from Menno Smits to avoid problems with DictCursor
- when the query is executed by a named cursor. Also added Menno's
- tests and uniformed other DictCursor tests.
-
- * psycopg/psycopgmodule.c: fixed unwanted exception when passing an
- explicit None to register_type().
-
- * psycopg/config.h: applied patch from Jason Erickson to build
- with MSVC. Solaris version of isinf() is now a macro.
-
-2009-02-27 Federico Di Gregorio <fog@initd.org>
-
- * psycopg/config.h: added check to provide compatible isinf()
- for Solaris (thanks to Jeremy Mason.)
-
-2009-02-23 Federico Di Gregorio <fog@initd.org>
-
- * Release 2.0.9.
-
-2009-02-17 James Henstridge <james@jamesh.id.au>
-
- * psycopg/utils.c (psycopg_escape_string): same here.
-
- * psycopg/adapter_binary.c (binary_escape): simplify PostgreSQL
- version check.
-
- * setup.py (psycopg_build_ext.finalize_options): use a single
- define of the PostgreSQL version in a form that can easily be used
- by #ifdefs.
-
- * tests/test_dates.py (DatetimeTests, mxDateTimeTests): full test
- coverage for datetime and time strings with and without time zone
- information.
-
- * psycopg/typecast_datetime.c (typecast_PYDATETIME_cast): adjust
- to handle the changes in typecast_parse_time.
- (typecast_PYTIME_cast): add support for time zone aware time
- values.
-
- * psycopg/typecast_mxdatetime.c (typecast_MXDATE_cast): make sure
- that values with time zones are correctly processed (even though
- that means ignoring the time zone value).
- (typecast_MXTIME_cast): same here.
-
- * psycopg/typecast.c (typecast_parse_time): Update method to parse
- second resolution timezone offsets.
-
- * psycopg/typecast.c (typecast_parse_time): Fix up handling of
- negative timezone offsets with a non-zero minutes field.
-
- * tests/test_dates.py (DatetimeTests): Add tests for time zone
- parsing. The test for HH:MM:SS time zones is disabled because we
- don't currently support it.
-
-2009-02-16 Federico Di Gregorio <fog@initd.org>
-
- * FreeBSD now has round(). Modified config.h as suggested by
- Jeroen Ruigrok van der Werven.
-
-2009-02-06 Federico Di Gregorio <fog@initd.org>
-
- * Applied patch by Markus Demleitner to make executemany() return
- the sum of modified rows by executed statements or -1 if any one
- statement returns it. This is still DBAPI-2.0 compliant.
-
-2009-01-23 Federico Di Gregorio <fog@initd.org>
-
- * Fixed problem mailed by Markus Demleitner about Python to
- PostgreSQL conversions of "nan" and "inf" floats resulting in
- backend errors. Added the new psycopg2.extensions.Float adapter
- that correctly handle both values and unit tests. (The opposite
- conversion was already working.)
-
-2009-01-20 Federico Di Gregorio <fog@initd.org>
-
- * Fixed problem reported by Lawrence Oluyede where
- register_type() didn't work on connection and cursors
- suclasses.
-
-2009-01-10 Federico Di Gregorio <fog@initd.org>
-
- * psycopg/cursor_type.c: cursor.isready() now raise an exception
- when the libpq PQconsumeInput() call fails.
-
-2008-12-04 Federico Di Gregorio <fog@initd.org>
-
- * psycopg/lobject_type.c: fixed memory leak. The patch was kindly
- sent from a psycopg user but I wrongly deleted the email so no
- kudos (and I had to fix the problem by myself!)
-
-2008-11-25 Federico Di Gregorio <fog@initd.org>
-
- * psycopg/cursor_type.c: integrated patch from Alejandro Dubrovsky.
- Note that the statically allocated buffer should probably go away
- in favor of always allocating the buffer dinamically.
-
- * psycopg/utils.c: modified patch from Alejandro Dubrovsky to
- support quoted separators in COPY queries: now all the string
- quoting code is in utils.c and the same function is used by
- qstrings and everything else (like the COPY code.)
-
-2008-09-24 Federico Di Gregorio <fog@initd.org>
-
- * lib/extras.py: added inet support and related tests.
-
-2008-09-23 James Henstridge <james@jamesh.id.au>
-
- * psycopg/psycopg.h (NotSupportedError_doc): clean up
- spelling/grammar a bit, using exception description from the PEP.
-
-2008-09-23 Federico Di Gregorio <fog@initd.org>
-
- * Applied patch from Brian Sutherland that fixes NULL
- valus in UUID support.
-
-2008-09-19 Federico Di Gregorio <fog@initd.org>
-
- * lib/extras.py: added UUID support, modeled after the code
- sent by Brian Sutherland.
-
-2008-09-16 Federico Di Gregorio <fog@initd.org>
-
- * Release 2.0.8.
-
-2008-07-26 Federico Di Gregorio <fog@initd.org>
-
- * psycopg/connection_type.c: merged get_backend_pid() method
- by Casey Duncan.
-
-2008-07-23 James Henstridge <james@jamesh.id.au>
-
- * psycopg/lobject_type.c (lobject_setup): use
- FORMAT_CODE_PY_SSIZE_T in Dprintf() call for 64-bit compatibility
- when using Python 2.5 or later.
- (lobject_dealloc): same here.
-
-2008-07-18 James Henstridge <james@jamesh.id.au>
-
- * psycopg/adapter_qstring.c (qstring_traverse): add cyclic GC
- traversal for quoted string adapters.
-
- * psycopg/adapter_pboolean.c (pboolean_traverse): add cyclic GC
- traversal for boolean adapters.
-
- * psycopg/adapter_mxdatetime.c (mxdatetime_traverse): add cyclic
- GC traversal for mxdatetime adapters.
-
- * psycopg/adapter_datetime.c (pydatetime_traverse): add cyclic GC
- traversal for datetime adapters.
-
-2008-07-01 James Henstridge <james@jamesh.id.au>
-
- * psycopg/adapter_binary.c (binary_traverse): add cyclic GC
- traversal for binary adapters.
-
- * psycopg/adapter_asis.c (asis_traverse): add cyclic GC traversal
- for AsIs adapters.
-
- * psycopg/adapter_list.c (list_traverse): add cyclic GC traversal
- for list adapters.
-
-2008-05-28 James Henstridge <james@jamesh.id.au>
-
- * psycopg/cursor_type.c (cursor_setup): incref before setting
- attributes, to make things GC-safe.
-
- * psycopg/cursor_int.c (curs_reset): make clearing of description
- and casts attributes GC-safe.
-
- * psycopg/typecast.c (typecast_traverse): implement cyclic GC
- traversal for typecasters.
-
- * psycopg/connection_type.c:
- * psycopg/cursor_type.c: add support for cyclic GC traversal (no
- support for clearing).
-
- * psycopg/python.h: add definitions for Py_CLEAR() and Py_VISIT()
- for compatibility with old versions of Python.
-
-2008-06-28 Federico Di Gregorio <fog@initd.org>
-
- * setup.py: fixed problem with spaces in pg_config path.
-
-2008-05-27 Federico Di Gregorio <fog@initd.org>
-
- * psycopg/pqpath.c: better error checks in _pq_copy_in_v3 to
- avoid calling blocking libpq functions when the connection to
- the server has been broken
-
-2008-05-19 Federico Di Gregorio <fog@initd.org>
-
- * psycopg/cursor_type.c: fixed memory leak in .executemany(); on
- error "iter" was not dec'reffed.
-
-2008-05-06 James Henstridge <james@jamesh.id.au>
-
- * psycopg/lobject.h (lobjectObject): remove "mode" struct member,
- since it was unused.
-
- * psycopg/lobject_*.c: replace uses of the closed struct member,
- and change the Python level attribute to a getset.
-
- * psycopg/lobject.h (lobjectObject): remove the closed member,
- since "fd < 0" gives us the same information. Reorder the struct
- members for better packing.
-
- * psycopg/lobject*: const'ify the code.
-
- * tests/test_lobject.py (LargeObjectTests): add more tests,
- including behaviour on closed lobjects and stale lobjects.
-
- * psycopg/lobject_type.c (psyco_lobj_close): don't mark the
- connection closed here because it is done by
- lobject_close_locked().
-
- * psycopg/lobject_int.c (lobject_open): mark objects as not closed
- if we successfully open them.
- (lobject_close_locked): mark the lobject closed here.
- (lobject_export): ensure we are in a transaction, since
- lo_export() issues multiple queries.
-
- * psycopg/lobject_type.c (lobject_setup): make lobjects start closed.
-
-2008-05-05 James Henstridge <james@jamesh.id.au>
-
- * psycopg/lobject.h: don't export the lobjectType symbol.
-
-2008-05-05 Federico Di Gregorio <fog@initd.org>
-
- * Fixed sun build problem by adding "sun" to config.h checks.
-
-2008-05-05 James Henstridge <james@jamesh.id.au>
-
- * psycopg/pqpath.c (pq_complete_error): get rid of double free
- error in case when pgres is NULL.
-
- * tests/test_lobject.py: add some basic tests for large object
- code.
-
- * psycopg/lobject*.[ch]: port the large object code to work with
- current psycopg code.
-
-2006-09-01 Federico Di Gregorio <fog@initd.org>
-
- * psycopg/connection_type.c: merged in double mutex destroy patch
- from Joerg Sonnenberger.
-
- * Implemented large objects support.
-
- * psycopg/connection_int.c: removed increment of self->mark,
- now it is done directly in pqpath.c to make sure even the
- large object support gets it.
-
- * Starting 2.1 development.
-
-2008-04-21 James Henstridge <james@jamesh.id.au>
-
- * tests/test_quote.py (QuotingTestCase.test_unicode): If the
- server encoding is not UTF8, skip the unicode test and emit a
- warning.
-
-2008-04-21 Jorgen Austvik <Jorgen.Austvik@sun.com>
-
- * tests/*.py: use the DSN constructed in tests/__init__.py.
-
- * tests/__init__.py: allow setting the host, port and user for the
- DSN used by the tests through the environment.
-
-2008-03-17 Federico Di Gregorio <fog@initd.org>
-
- * Release 2.0.7.
-
-2008-03-31 James Henstridge <james@jamesh.id.au>
-
- * psycopg/connection_type.c (connection_dealloc): free
- connection->encoding with free() instead of PyMem_Free().
-
- * psycopg/connection_int.c (conn_connect): use malloc() to
- allocate connection->encoding instead of PyMem_Malloc(), since it
- is freed in other places with free() and assigned to with
- strdup().
-
-2008-03-26 James Henstridge <james@jamesh.id.au>
-
- * psycopg/typecast.c (typecast_from_c): fix up some reference
- leaks. This leak affected a bounded set of objects, so doesn't
- account for any gradual leaks.
-
-2008-03-19 James Henstridge <james@jamesh.id.au>
-
- * psycopg/connection_int.c (conn_notice_callback): don't leak
- notice messages.
-
-2008-03-17 Federico Di Gregorio <fog@initd.org>
-
- * psycopg/adapter_datetime.c: fixed double decref when using
- a PyObject as a parameter in a nested call (line 415).
-
-2008-03-17 James Henstridge <james@jamesh.id.au>
-
- * psycopg/typecast.c (typecast_parse_time): give the correct
- return value for partially parsed time values.
-
- * psycopg/typecast_mxdatetime.c (typecast_MXDATE_cast): return
- NULL after setting DataError. Also, don't treat it as an error if
- typecast_parse_time() returns 0 (as might happen if the remainder
- of the string is " BC").
-
- * psycopg/typecast_datetime.c (typecast_PYDATE_cast): return NULL
- after setting DataError.
- (typecast_PYDATETIME_cast): same here.
- (typecast_PYTIME_cast): same here.
-
- * tests/test_dates.py
- (CommonDatetimeTestsMixin.test_parse_incomplete_date): test that
- parsing incomplete date values results in DataError.
- (CommonDatetimeTestsMixin.test_parse_incomplete_time): same for
- times.
- (CommonDatetimeTestsMixin.test_parse_incomplete_time): same for
- datetimes.
-
-2008-03-07 Jason Erickson <jerickso@stickpeople.com>
-
- * psycopg/pqpath.c (pq_raise): if PSYCOPG_EXTENSIONS is not
- defined, raise OperationalError rather than
- TransactionRollbackError for deadlock or serialisation errors for
- protocol versions less than 3.
-
- * psycopg/psycopgmodule.c (psyco_connect): fix off by one error in
- calculating the length of the DSN.
-
-2008-03-07 James Henstridge <james@jamesh.id.au>
-
- * psycopg/pqpath.c (_pq_fetch_tuples): Don't call Python APIs
- without holding the GIL.
-
-2008-02-27 James Henstridge <james@jamesh.id.au>
-
- * NEWS: add some draft NEWS items for a 2.0.7 release.
-
- * runtests.py: add a harness to run all the psycopg tests against
- the version built by distutils.
-
-2008-01-22 James Henstridge <james@jamesh.id.au>
-
- * psycopg/typecast.c (typecast_pydatetime): make array static.
- (typecast_mxdatetime): same here.
-
- * psycopg/typecast_builtins.c (typecast_builtins): make array
- static.
-
- * psycopg/psycopgmodule.c: add hidden visibility to a bunch of
- global variables here.
-
- * psycopg/psycopg.h: add set QueryCanceledError and
- TransactionRollbackError to hidden visibility.
-
- * psycopg/*.[ch]: add const qualifier to various string arguments
- to functions (typecast functions and conn_switch_isolation_level).
-
-2008-01-21 James Henstridge <james@jamesh.id.au>
-
- * setup.cfg (define): remove PSYCOPG_DISPLAY_SIZE from default
- list of defines, as discussed on mailing list. It slows down
- queries with very little benefit.
-
- * psycypg/*.h: apply HIDDEN to all global variables and functions
- that should not be exported from the module. This results in a 5%
- reduction in code size and shortens the dynamic symbol table.
-
- * psycopg/config.h: If GCC >= 4.0 is installed, define the HIDDEN
- symbol to apply the "hidden" visibility attribute.
-
-2008-01-19 James Henstridge <james@jamesh.id.au>
-
- * tests/test_connection.py (ConnectionTests): add simple tests for
- the Connection and Cursor "closed" attributes.
-
- * psycopg/cursor_type.c (psyco_curs_get_closed): add a "closed"
- attribute to cursors. It will be True if either the cursor or its
- associated connection are closed. This fixes bug #164.
-
- * psycopg/pqpath.c (pq_raise): remove unused arguments to
- function, and simplify.
- (pq_resolve_critical): make function static, since it isn't being
- used outside of pqpath.c any more.
-
-2008-01-17 James Henstridge <james@jamesh.id.au>
-
- * ZPsycopgDA/DA.py (Connection.__init__): Default the encoding to
- UTF-8, fixing bug #190.
- (App.ImageFile): simplify ImageFile import using patch from
- chrism, fixing bug #198.
-
-2008-01-16 James Henstridge <james@jamesh.id.au>
-
- * tests/test_transaction.py (DeadlockSerializationTestCase): port
- over some tests for serialisation and deadlock errors,
- demonstrating that TransactionRollbackError is generated.
- (QueryCancelationTests): add a test to show that
- QueryCanceledError is raised on statement timeouts.
-
- * psycopg2da/adapter.py (_handle_psycopg_exception): rather than
- checking exception messages, check for TransactionRollbackError.
-
- * psycopg/pqpath.c (exception_from_sqlstate): return
- TransactionRollbackError for 40xxx errors, and QueryCanceledError
- for 57014 errors.
- (pq_raise): If we are using an old server, use
- TransactionRollbackError if the error message contains "could not
- serialize" or "deadlock detected".
-
- * psycopg/psycopgmodule.c (_psyco_connect_fill_exc): remove
- function, since we no longer need to store pointers to the
- exceptions in the connection. This also fixes a reference leak.
- (psyco_connect): remove _psyco_connect_fill_exc() function call.
-
- * psycopg/connection.h (connectionObject): remove exception
- members from struct.
-
- * psycopg/connection_type.c (connectionObject_getsets): modify the
- exception attributes on the connection object from members to
- getsets. This reduces the size of the struct.
-
- * lib/extensions.py: import the two new extensions.
-
- * psycopg/psycopgmodule.c (exctable): add new QueryCanceledError
- and TransactionRollbackError exceptions.
-
-2008-01-16 James Henstridge <james@jamesh.id.au>
-
- * tests/__init__.py (test_suite): add date tests to test suite.
-
- * tests/test_dates.py: add tests for date/time typecasting and
- adaption.
-
- * psycopg/adapter_mxdatetime.c (mxdatetime_str): add support for
- outputting BC dates (which involves switching them to one-based
- dates). Also remove broken handling of microseconds.
-
- * psycopg/typecast.c (typecast_parse_date): if the string ends
- with "BC" adjust the year value to be a zero-based BC value as
- used by mx.DateTime (datetime doesn't support BC dates).
- (typecast_parse_time): ignore ' ', 'B' and 'C' in time strings
- rather than treating them as part of the seconds part of the time.
-
-2008-01-14 James Henstridge <james@jamesh.id.au>
-
- * psycopg/typecast_array.c (typecast_array_scan): set an initial
- value for quotes to keep gcc happy.
-
- * psycopg/*.c: add missing static modifier on many functions.
-
-2008-01-12 James Henstridge <james@jamesh.id.au>
-
- * tests/test_transaction.py
- (TransactionTestCase.test_failed_commit): Expect IntegrityError
- instead of OperationalError.
-
- * psycopg/pqpath.c (exception_from_sqlstate): new function that
- converts an SQLSTATE error code to the corresponding exception
- class.
- (pq_raise): use exception_from_sqlstate() to pick which exception
- to use when working with protocol version 3.
- (pq_complete_error): Let pq_raise() pick an appropriate exception
- rather than forcing OperationalError.
-
-2008-01-11 James Henstridge <james@jamesh.id.au>
-
- * psycopg/adapter_binary.c (binary_quote): apply Brandon Rhodes'
- patch from ticket #209 to check return value from
- PyObject_AsCharBuffer(). This fixes the segfault.
- (binary_quote): switch from PyObject_AsCharBuffer() to
- PyObject_AsReadBuffer() to support buffer objects that don't
- implement the bf_getcharbuf protocol.
-
- * tests/types_basic.py (TypesBasicTests.testBinary): Test round
- tripping of bytea buffers. Currently segfaults.
-
- * psycopg/connection_int.c (conn_close): fix for new
- pq_abort_locked() prototype.
- (conn_switch_isolation_level): fix for new pq_abort_locked()
- prototype, and use pq_complete_error() to show error message.
- (conn_set_client_encoding): same here.
-
- * psycopg/pqpath.c (pq_execute_command_locked): remove static
- modifier.
- (pq_complete_error): same here.
- (pq_abort_locked): add pgres and error arguments.
- (pq_abort): call pq_abort_locked() to reduce code duplication.
-
-2007-12-23 James Henstridge <james@jamesh.id.au>
-
- * psycopg/pqpath.c (pq_execute_command_locked): add an error
- argument to hold an error when no PGresult is returned by PQexec,
- rather than using pq_set_critical().
- (pq_complete_error): new function that converts the error returned
- by pq_execute_command_locked() to a Python exception.
- (pq_begin_locked): add error argument.
- (pq_commit): use pq_complete_error().
- (pq_abort): use pq_complete_error().
- (pq_abort_locked): always call pq_set_critical() on error, and
- clear the error message from pq_execute_command_locked().
- (pq_execute): use pq_complete_error() to handle the error from
- pq_begin_locked().
-
- * psycopg/pqpath.c (pq_begin): remove unused function.
-
- * psycopg/connection_type.c (psyco_conn_commit): if conn_commit()
- raises an error, just return NULL, since it is now setting an
- exception itself.
- (psyco_conn_rollback): same here.
-
- * psycopg/connection_int.c (conn_commit): don't drop GIL and lock
- connection before calling pq_commit().
- (conn_rollback): same here.
- (conn_close): use pq_abort_locked().
- (conn_switch_isolation_level): same here.
- (conn_set_client_encoding): same here.
-
- * psycopg/pqpath.h: add prototype for pq_abort_locked().
-
- * psycopg/pqpath.c (pq_commit): convert function to run with GIL
- held, and handle errors appropriately.
- (pq_abort): same here.
- (pq_abort_locked): new function to abort a locked connection.
-
-2007-12-22 James Henstridge <james@jamesh.id.au>
-
- * psycopg/pqpath.c (pq_raise): add a "pgres" argument so we can
- generate nice errors not related to a particular cursor.
- (pq_execute): use pq_begin_locked() rather than pq_begin(). Use
- pq_raise() to handle any errors from it.
-
- * psycopg/pqpath.c (pq_execute_command_locked): helper function
- used to execute a command-style query on a locked connection.
- (pq_begin_locked): a variant of pq_begin() that uses
- pq_execute_command_locked().
- (pq_begin): rewrite to use pq_begin_locked().
-
-2007-12-22 James Henstridge <james@jamesh.id.au>
-
- * psycopg/config.h: only print debug messages if
- psycopg_debug_enabled is true.
-
- * psycopg/psycopgmodule.c (init_psycopg): set
- psycopg_debug_enabled to true if the $PSYCOPG_DEBUG environment
- variable is set.
-
-2007-12-21 Federico Di Gregorio <fog@initd.org>
-
- * Applied win32 patch from Jason Erickson.
-
- * Applied patch from Thomas Lotze to use correct version
- for new Zopes.
-
-2007-12-20 James Henstridge <james@jamesh.id.au>
-
- * psycopg/pqpath.c (pq_execute): uncomment the "curs->pgres ==
- NULL" error handler after the PQexec() call. This is needed to
- catch database disconnects (and probably other errors). According
- to Federico, it was commented out to avoid a spurious error, so we
- should watch for problems.
-
-2007-12-20 James Henstridge <james@jamesh.id.au>
-
- * psycopg/pqpath.c (pq_raise): only remove the first 8 characters
- of the exception message if it actually gives the severity.
-
- * psycopg/pqpath.h (pq_resolve_critical): add prototype, since
- this function is being used from connection_int.c.
-
- * psycopg/psycopg.h: update psyco_set_error() prototype.
-
- * psycopg/psycopgmodule.c (psyco_errors_init): set pgerror, pgcode
- and cursor class attributes to None on psycopg2.Error so that the
- attributes will always be available (simplifies error handling).
- (psyco_set_error): add const qualifiers to msg, pgerror and pgcode
- arguments.
- Don't bother setting pgerror, pgcode or cursor to None if they are
- not provided -- the class defaults take care of this.
-
-2007-11-11 Daniele Varrazzo <daniele.varrazzo@gmail.com>
-
- * Use escape string syntax for string escape if connected to a
- server requiring it.
-
-2007-11-09 Daniele Varrazzo <daniele.varrazzo@gmail.com>
-
- * Use escape string syntax for binary escape if connected to a
- server requiring it.
-
- * Put a limit on the number of notices stored in the connection.
-
-2007-10-21 Daniele Varrazzo <daniele.varrazzo@gmail.com>
-
- * Fixed bug #192 (Decimal support not safe for use with multiple sub
- interpreters) as proposed by Graham Dumpleton.
-
-2007-09-08 Federico Di Gregorio <fog@initd.org>
-
- * Added MonoDevelop project, yahi!
-
-2007-09-06 Federico Di Gregorio <fog@initd.org>
-
- * Fixed bug #194.
-
-2007-09-01 Federico Di Gregorio <fog@initd.org>
-
- * Added "name" parameter to all .cursor() calls in extras.py.
-
-2007-05-29 Federico Di Gregorio <fog@initd.org>
-
- * Release 2.0.6.
-
- * ZPsycopgDA/DA.py: removed call to now obsolete .set_type_casts().
-
- * Applied patch from mkz (ticket #187) to add error handling when
- calling conn_commit() and conn_rollback(). Fixes #187.
-
- * cursor.copy_expert() implementation by David Rushby (copy_expert
- set 5/5.)
-
- * SQL validation refactor patch from David Rushby (copy_expert
- set 4/5.)
-
- * Reference count leak fix from David Rushby (copy_expert set 3/5.)
-
- * 64 bit fix patch from David Rushby (copy_expert set 2/5.)
-
- * Applied whitespace normalization patch from David Rushby (copy_expert
- set 1/5.)
-
-2007-04-25 Federico Di Gregorio <fog@initd.org>
-
- * psycopg/connection_type.c: added support for a new method
- .get_transaction_status() that returns the backend transaction
- status as libpq knows. Also added some symbolic constants to
- psycopg.extensions module (TRANSACTION_STATUS_XXX).
-
-2007-04-14 Federico Di Gregorio <fog@initd.org>
-
- * psycopg/psycopg.h: fixed probable typo in definition of
- CONV_CODE_PY_SSIZE_T: d -> i for Python < 2.5.
-
- * Fixed some of the examples.
-
-2007-04-13 Federico Di Gregorio <fog@initd.org>
-
- * Applied slighly modified patch from daniel (#176). Made
- buffer size a compile-time parameter.
-
- * Applied patch from David Rushby: typecast_binary.c cleanup.
-
- * Applied patch from David Rushby for int->size_t transition.
-
-2007-04-12 Federico Di Gregorio <fog@initd.org>
-
- * Applied patch from Jason Erickson to fix win32 build glitches.
-
-2007-04-11 Federico Di Gregorio <fog@initd.org>
-
- * Release 2.0.6b2.
-
- * psycopg/cursor_type.c: added check to raise an error when
- some crazy programmer tries to use different argument formats
- in the same query string. Fixes #162.
-
- * Applied patch from David Rushby to fix win32 builds.
-
-2007-04-10 Federico Di Gregorio <fog@initd.org>
-
- * Applied patch from David Rushby to fix mem and ref leaks in
- psycopg2.connect().
-
- * Applied super-patch from David Rushby to fix Python 2.5 and 64
- bit problems (all of them, kudos!)
-
-2007-02-22 Federico Di Gregorio <fog@initd.org>
-
- * Added support for per-connection and per-cursor typecasters.
-
-2007-02-11 Federico Di Gregorio <fog@initd.org>
-
- * psycopg/pqpath.c: ported psycopg1 patch from #135.
-
- * psycopg/connection_type.c: now the password is obfuscated after we try
- to connect to the backend. This really fixes #147.
-
-2007-01-20 Federico Di Gregorio <fog@initd.org>
-
- * Release 2.0.6b1.
-
- * Added encodings patch from Karsten Hilbert.
-
-2007-01-19 Federico Di Gregorio <fog@initd.org>
-
- * psycopg/adapt_list.c: added support for None. And this closes
- the other half.
-
- * psycopg/typecast_array.c: added support for detecting the NULL
- special string and converting it as a real NULL if not enclosed in
- quotes. This closes half of #154.
-
-2007-01-16 Federico Di Gregorio <fog@initd.org>
-
- * psycopg/connection_type.c: .set_client_encoding() now converts the
- argument to upper case to make sure it has the same case of the entries
- in the PostgreSQL -> Python encoding conversion table.
-
- * lib/extras.py: merged DictCursor from #143 and renamed it
- RealDictCursor because allows access by cursor keys _only_.
- Also cleaned up a little bit the implementation of both DictCursor
- and RealDictCursor by introducing DictCursorBase.
-
- * psycopg/pqpath.cs: new checks for NULL values (meaning an
- exception was raised) in all method calls (fixes #134).
-
- * psycopg/psycopgmodule.c: applied LATIN patch from #148.
-
- * psycopg/connection_type.c: obfuscate password after using it.
-
- * lib/extras.py: moved SQL_IN to extensions.py; we're now officially
- adapting tuples.
-
- * psycopg/adapt_mxdatetime.c: fixed #137 by accessing the 'date' and
- 'time' attributes of the mx.DateTime instance: they are always in ISO
- format.
-
- * psycopg/typecast_datetime.c: fixed problem with year > 9999.
-
-2006-10-29 Federico Di Gregorio <fog@initd.org>
-
- * Applied patch from Jason Erickson to make psycopg build
- again on win32 (closes: #132).
-
-2006-09-30 Federico Di Gregorio <fog@initd.org>
-
- * ZpsycopgDA/DA.py: applied the infinity patch from 1.1 (fixes #122).
-
- * psycopg/adapter_datetime.py: fixed conversion problem with seconds
- in the (59,60) range (fixes #131).
-
- * ZpsycopgDA/DA.py: we now split on GMT+, GMT-, + or -. This should
- fix bug #129.
-
- * psycopg/adapter_datetime.py: now TimeFromTicks and
- TimestampFromTicks both accept fractionary seconds (fixes #130).
-
-2006-09-23 Federico Di Gregorio <fog@initd.org>
-
- * lib/errorcodes.py: added list of all PostgreSQL error codes
- compiled by Johan Dahlin.
-
- * psycopg/psycopg.h: applied compatibility macros from PEP 353.
-
- * Applied patch 1/3 from Piet Delport; from his email:
-
- psycopg2-Py_ssize_t-input.diff adjusts variables used for parameters
- and return values. These changes only prevent overflowing on values
- greater than 32-bits, so they're not as critical as the other two
- patches. I tried to leave places unchanged where the input size is
- already constrained to sizeof(int), but i might have missed a few
- either way, not being too familiar with the codebase.
-
- * Applied patch 2/3 from Piet Delport; from his email:
-
- psycopg2-Py_ssize_t-output.diff adjusts variables used as outputs
- from CPython API calls: without it the calls try to write 64 bits
- to 32 bit locations, trampling over adjacent values/pointers,
- and segfaulting later.
-
- * Applied patch 1/3 from Piet Delport; from his email:
-
- psycopg2-PyObject_HEAD.diff adds missing underscores to several
- "PyObject_HEAD" declarations. As far as i can tell from gdb, the
- "PyObject HEAD" versions end up accidentally meaning almost exactly
- the same, but get aligned differently on AMD64, resulting in wrong
- size calculation and memory corruption later.
-
-2006-09-11 Federico Di Gregorio <fog@initd.org>
-
- * Fixed syntax error in lib/extras.py (see #123)
-
-2006-09-02 Federico Di Gregorio <fog@initd.org>
-
- * psycopg/adapter_mxdatetime.c, psycopg/psycopgmodule.c: fixed last
- problem with non-constant initializers.
-
- * psycopg/connection_type.c: applied patch from Joerg Sonnenberger
- to fix a double mutex destroy.
-
- * Release 2.0.5.1.
-
- * psycopg/cursor_type.c: applied patch from Jason Erickson to
- build on MSVC and older gcc.
-
-2006-09-01 Federico Di Gregorio <fog@initd.org>
-
- * Release 2.0.5.
-
- * Fixed patch from #119, see tracker for details.
-
- * Preparing release 2.0.5.
-
- * psycopg/psycopgmodule.c: fixed filling of connection errors
- to include OperationalError.
-
- * setup.py: removed pydatetime option from initialize_options
- to make sure that the value in setup.cfg is used.
-
- * psycopg/psycopgmodule.c: applied patch from jdahlin (#120)
- to have .connect() accept either a string or int as the port
- parameter.
-
- * psycopg/adapter_binary.c: applied patch from jdahlin (#119)
- to fix the segfault on empty binary buffers.
-
- * psycopg/connection_type.c: added .status attribute to expose
- the internal status.
-
- * psycopg/pqpath.c: applied patch from intgr (#117) to fix
- segfault on null queries.
-
- * psycopg/cursor_type.c: applied patch from intgr (#116) to
- fix bad keyword naming and segfault in .executemany().
-
- * ZPsycopgDA/DA.py: applied ImageFile patch from Charlie
- Clark.
-
- * lib/pool.py: applied logging patch from Charlie Clark.
- It will probably get a makeup and be moved to the top-level
- module later.
-
-2006-08-02 Federico Di Gregorio <fog@initd.org>
-
- * Release 2.0.4.
-
- * Fixed bug in float conversion (check for NULL string was
- erroneously removed in 2.0.3!)
-
-2006-07-31 Federico Di Gregorio <fog@initd.org>
-
- * Release 2.0.3.
-
- * psycopg/cursor_type.c: applied patch from jbellis (#113) to
- allow column selection in .copy_from().
-
- * psycopg/psycopgmodule.c: fixed memory leak in custom exceptions
- (applied patch from #114).
-
-2006-07-26 Federico Di Gregorio <fog@initd.org>
-
- * psycopg/adapter_datetime.c (pydatetime_str): fixed error
- in conversion of microseconds for intervals and better algo
- (thanks to Mario Frasca.)
-
-2006-06-18 Federico Di Gregorio <fog@initd.org>
-
- * psycopg/adapter_binary.c: same as below.
-
- * psycopg/adapter_qstring.c: does not segfault anymore if
- .getquoted() is called without preparing the qstring with
- the connection.
-
-2006-06-15 Federico Di Gregorio <fog@initd.org>
-
- * psycopg/typecast_basic.c: fixed problem with bogus
- conversion when importing gtk (that was crazy, I didn't
- understand why it happened but the new code just fixes it.)
-
- * ZPsycopgDA/db.py: better type analisys, using an hash
- instead of a series of if (variation on patch from Charlie
- Clark.)
-
-2006-06-11 Federico Di Gregorio <fog@initd.org>
-
- * Release 2.0.2.
-
- * psycopg/typecast_array.c (typecast_array_cleanup): fixed a
- problem with typecast_array_cleanup always returning the new
- string length shorter by 1 (Closes: #93).
-
- * psycopg/adapter_binary.c: as below.
-
- * psycopg/adapter_qstring.c: wrapped #warning in #ifdef __GCC__
- because other compilers don't have it and it will just break
- compilation (patch from jason, our great win32 builder).
-
- * psycopg/adapter_list.c: applied patch to adapt an empty list
- into an empty array and not to NULL (from iGGy, closes: #108).
-
- * psycopg/cursor_type.c: applied patch from wkv to avoid
- under-allocating query space when the parameters are not of the
- right type (Closes: #110).
-
- * psycopg/connection_int.c: applied patch from wkv to avoid off
- by one allocation of connection encoding string (Closes: #109).
-
-2006-06-09 Federico Di Gregorio <fog@initd.org>
-
- * Release 2.0.1.
-
- * Fixed some buglets in ZPsycopgDA (was unable to load due
- to shorter version number in psycopg module.)
-
-2006-06-08 Federico Di Gregorio <fog@initd.org>
-
- * Release 2.0.
-
- * ZPsycopgDA/DA.py: removed Browse table for 2.0 release; we'll
- add it back later.
-
-2006-05-26 Federico Di Gregorio <fog@initd.org>
-
- * Applied better PostgreSQL patch from AA.
-
-2006-05-24 Federico Di Gregorio <fog@initd.org>
-
- * Enabled 8.1.4 security fix only when the version is >= 8.1.4, fall
- back to old code otherwise.
-
- * psycopg/adapter_qstring.c: now quote using PQescapeStringConn if
- available.
-
- * psycopg/adapter_binary.c: now quote using PQescapeByteaConn if
- available.
-
-2006-04-38 Federico Di Gregorio <fog@initd.org>
-
- * setup.py: fixed little problem with mx_include_dir as suggested
- by kvc (this closes #102).
-
-2006-04-24 Federico Di Gregorio <fog@initd.org>
-
- * psycopg/adapter_pboolean.c: added the possibility to format boolean
- values as "true" and "false" instead of "'t'" and "'f'".
-
-2006-03-08 Federico Di Gregorio <fog@initd.org>
-
- * lib/extras.py: added .next() to DictCursot to support iteration.
-
-2006-03-02 Federico Di Gregorio <fog@initd.org>
-
- * psycopg/typecast_array.c (typecast_array_tokenize): removed cast
- to build without warnings on 64 bit arches.
-
-2006-02-11 Federico Di Gregorio <fog@initd.org>
-
- * Release 2.0 beta 8.
-
- * psycopg/config.h: applied patch from Jason to fix handle leak on
- win32, as documented in #92.
-
-2006-02-11 Federico Di Gregorio <fog@initd.org>
-
- * Release 2.0 beta 7.
-
- * psycopg/psycopgmodule.c: applied fix for memory overflow in
- connect() (reported by solt, #91.)
-
- * setup.py: applied patch from lbruno.
-
-2006-01-11 Federico Di Gregorio <fog@initd.org>
-
- * setup.py: does not report an error in pg_config unless the pg_config
- was explicitly set (allows for building with old options.)
-
-2006-01-06 Daniele Varrazzo <daniele.varrazzo@gmail.com>
-
- * setup.py: libpq.dll not used anymore. win32 setup uses pg_config too.
-
-2006-01-05 Federico Di Gregorio <fog@initd.org>
-
- * psycopg/psycopgmodule.c (psyco_set_error): added function to set extra
- parameters on ProgrammingError instances. Also modified all occurances of
- PyErr_SetString(ProgrammingError,...) to psycopg_set_error().
-
- * setup.{cfg,py}: we now use pg_config to locate PostgreSQL libraries
- and headers (modified patch from lbruno, see #70.)
-
-2006-01-01 Federico Di Gregorio <fog@initd.org>
-
- * Preparing release 2 beta 7.
-
- * MANIFEST.in: we now distrbute pre-built documentation (still need
- to add to setup.py the code necessary to build docs as part of the
- build process.)
-
- * psycopg/connection_int.c: PostgreSQL encoding names are now force
- uppercase (after all PostgreSQL documentation reports them this way.)
-
-2005-12-11 Federico Di Gregorio <fog@initd.org>
-
- * psycopg/typecast_array.c (typecast_array_cleanup): added functio
- to cleanup the "[...]=" part of an array result. This probably will
- need some more work for nested arrays but it fixed every test I was
- able to write. (Closes: #80)
-
-2005-12-11 Federico Di Gregorio <fog@initd.org>
-
- * setup.py: half-applied patch from Tavis to specify mx_include_dir
- in setup.cfg.
-
- * psycopg/typecast.c (typecast_parse_time): cz limit in the while
- loop is 6, not 5. This solve the problem with "fractionary" time zones
- and fixes #78.
-
-2005-12-06 Federico Di Gregorio <fog@initd.org>
-
- * lib/extras.py: added .callproc() to DictCursor as suggested
- by Philip Semanchuk.
-
-2005-11-29 Federico Di Gregorio <fog@initd.org>
-
- * MANIFEST.in: added docs/async.txt. (Closes: #75)
-
-2005-11-26 Daniele Varrazzo <daniele.varrazzo@gmail.com>
-
- * psycopg/psycopgmodule.c: fixed exceptions refcount.
-
- * Fixed lots of doctrings and added Epydoc-generated docs support.
-
-2005-11-24 Federico Di Gregorio <fog@initd.org>
-
- * sandbox: added all the test and creash-me files to the repository.
-
- * psycopg/typecast.c (typecast_dealloc): now directly calls
- PyObject_Del to avoid to segfault.
-
-2005-11-20 Federico Di Gregorio <fog@initd.org>
-
- * psycopg/typecast.c: fixed problem with microseconds conversion by
- applying slightly modified patch from Ronnie Mackay.
-
-2005-11-19 Federico Di Gregorio <fog@initd.org>
-
- * lib/extensions.py: COMMITED -> COMMITTED. (Closes: #73)
-
- * doc/extensions.rst: included Daniele's work after minor cosmetic changes
- like using the new constants instead of numbers for transaction isolation
- levels.
-
-2005-11-17 Federico Di Gregorio <fog@initd.org>
-
- * ZPsycopgDA/pool.py: fixed connections leak by using the new name
- (PersistentConnectionPool) for the old connection pool class.
-
-2005-11-16 Federico Di Gregorio <fog@initd.org>
-
- * Preparing release 2.0 beta 6.
-
- * psycopg/adapter_mxdatetime.c: fixed all problems with mx conversions.
-
- * psycopg/typecast.c: now the timezone is set correctly even if there
- are no microseconds and/or the offset is 0;
-
- * examples/encoding.py: fixed example by using python utf8 encoding for
- the whole file.
-
- * lib/__init__.py: very nice hack from Harald Armin Massa to allow
- py2exe and similar tools to do their work without problems.
-
-2005-11-15 Federico Di Gregorio <fog@initd.org>
-
- * psycopg/psycopgmodule.c: now bails out with correct exception when one
- of the needed modules can't be imported (should fix #32.)
-
-2005-11-14 Federico Di Gregorio <fog@initd.org>
-
- * psycopg/typecast.c: added typecast_parse_date and typecast_parse_time
- functions to do locale-safe date/time parsing. This would probably also
- speed-up psycopg a little bit.
-
-2005-11-07 Federico Di Gregorio <fog@initd.org>
-
- * psycopg/pqpath.c: fixed problem with uninitialized value (all this was
- started by replacing calloc() calls with PyMem_Malloc().)
-
-2005-11-04 Federico Di Gregorio <fog@initd.org>
-
- * psycopg/typecast.c: a lot of changes:
- - made typecast a new-style type
- - removed coerce code and implemented the richcompare protocol that
- allows to compare objects of different types
- - much better __cmp__ method that allows to compare two typecast
- objects and returns True if any two of the mapped oids match
- - any object that can be used as an int works as right-hand operand
- in __cmp__ operations
-
- * psycopg/typecast_datetime.c: now typecast_PYINTERVAL_cast limit the
- scan to 'len' characters in the string (should fix #65.)
-
-2005-11-03 Federico Di Gregorio <fog@initd.org>
-
- * Applied patch from Daniele Varazzo to enable Decimal on Python
- 2.3 when the module is available (run-time check, nice.)
-
-2005-10-26 Federico Di Gregorio <fog@initd.org>
-
- * setup.cfg: added include_dirs line for SUSE 9.3.
-
-2005-10-22 Federico Di Gregorio <fog@initd.org>
-
- * psycopg/cursor_type.c: added support for named cursors:
- - .fetchXXX() methods now execute a FETCH if the cursor is named
- - .execute() executes a DECLARE if the cursor is named
- - .execute() fails if a named cursor is used in autocommit
- - .executemany() can't be called on named cursors
- - .scroll() executes a MOVE if the cursor is named
- - .close() executes a CLOSE if the cursor is named
- Also, a "transaction mark" was added to both the connection and the
- cursor and an exception is raised when using a named cursor unless the
- two marks correspond.
-
- * psycopg/connection_int.c: snprintf->PyOS_snprintf.
-
- * psycopg/psycopgmodule.c: snprintf->PyOS_snprintf.
-
- * psycopg/cursor_type.c: changed self->query type from C string to
- PyObject* to better manage queries in named cursors.
-
- * psycopg/psycopgmodule.c: cleaned up exception names (now the errors
- is printed as psycopg2.Error and not as the confusing
- psycopg2._psycopg.Error.)
-
-2005-10-20 Federico Di Gregorio <fog@initd.org>
-
- * lib/pool.py: renamed ThreadedConnectionPool to PersistentConnectionPool
- and added a connection pool that allows multiple connections per thread
- as ThreadedConnectionPool (courtesy of Daniele Varrazzo.)
-
-2005-10-19 Federico Di Gregorio <fog@initd.org>
-
- * Releasing 2.0 beta 5.
-
- * psycopg/adapter_mxdatetime.c: reverted to old strftime method to format
- mx.DateTime objects; the new method didn't worked in some corner-cases.
- This makes impossible to have more than 2 decimal places for seconds but
- at least we get the time right every time.
-
-2005-10-18 Federico Di Gregorio <fog@initd.org>
-
- * NOTIFY is back end working.
-
- * psycopg/connection_type.c: fixed problem with initialization of
- notifies list (also fixed small memory leak in connection dealloc.)
-
- * examples/notify.py: added NOTIFY example.
-
- * psycopg/cursor_type.c: added per-cursor type-casters dictionaries.
-
-2005-10-18 Federico Di Gregorio <fog@initd.org>
-
- * psycopg/typecast.c: temporary fix to typecasting objects to return
- False for any comparaison except an integer in self.values (i.e., we
- don't raise an exception anymore on a coerce error.) Epydoc is now
- happy.
-
- * psycopg/config.h: ZETA config.h patch from Charlie Clark.
-
- * examples/threads.py: fixed small typo: psycopg -> psycopg2.
-
- * Big cleanup of unsigned chars to tame gcc 4.
-
- * Big cleanup of module names (i.e., psycopg2._psycopg everywhere.)
-
- * psycopg/config.h: added fake localtime_r for platforms missing it
-
- * psycopg/cursor_type.c: cursors now have a FixedOffsetTimezone
- tzinfo_factory by default.
-
- * psycopg/adapter_datetime.c: added tzinfo argument to psycopg2.Time and
- psycopg2.Timestamp. Also now TimestampFromTicks sets the tzinfo object
- to psycopg2.tz.LOCAL.
-
-2005-10-17 Federico Di Gregorio <fog@initd.org>
-
- * psycopg/adapter_datetime.c: we now use localtime() instead of gmtime()
- to accound for the local time-zone in timestamps.
-
- * psycopg/connection_type.c: fixed docstring for .cursor().
-
- * psycopg/psycopgmodule.c: added useful docstring for .connect().
-
-2005-10-08 Federico Di Gregorio <fog@initd.org>
-
- * psycopg/connection_type.c: isolation level upper bound set to 2.
-
- * lib/psycopg1.py: explicitly set isolation level to 2 on .connect()
- to mimic psycopg 1 behaviour.
-
- * psycopg/connection_int.c: now set isolation level from
- default_transaction_isolation backend environment value.
-
- * psycopg/pqpath.c: removed serialization level 3: now everybody
- (except me) has to use the mnemonics defined in psycopg2.extensions.
-
- * lib/extensions.py: Added mnemonics for serialization levels.
-
-2005-10-02 Federico Di Gregorio <fog@debian.org>
-
- * psycopg/cursor_type.c (psyco_curs_callproc): applied callproc
- patch from Matt Goodall (added a check on _psyco_curs_execute
- return value and substituted malloc/free with PyMem versions.)
-
-2005-10-01 Federico Di Gregorio <fog@debian.org>
-
- * psycopg/connection_int.c: fixed segfault by moving PyErr_Format
- after GIL acquisition (closes: #50).
-
- * psycopg/connection_type.c: applied patch from Matt Goodall to
- fix some doc strings.
-
-2005-09-23 Federico Di Gregorio <fog@debian.org>
-
- * lib/pool.py: applied patch from piro to avoid the scan of the
- whole connection array on getconn().
-
-2005-09-12 Federico Di Gregorio <fog@initd.org>
-
- * lib/pool.py: Applied psycopg->psycopg2 patch to from bug #35.
-
- * ZpsycopgDA/db.py: fixed problem with OperationalError that
- resulted in cryptic message to Zope users ("'OperationalError' is
- not defined".)
-
-2005-08-23 Federico Di Gregorio <fog@debian.org>
-
- * setup.py: applied patch from Daniele Varrazzo to avoid segfaults
- when compiling with migw for Python 2.4.x.
-
- * psycopg/adapter_mxdatetime.c (mxdatetime_str): ported code from 1.1.x
- to convert mxDateTime object preserving the precision of fractional
- seconds.
-
-2005-08-22 Federico Di Gregorio <fog@debian.org>
-
- * ZPsycopgDA/*.py: psycopg -> psycopg2.
-
- * setup.py: modified to install the module components under
- psycopg2 on windows too (thanks to Daniele Varrazzo.)
-
-2005-08-07 Federico Di Gregorio <fog@debian.org>
-
- * psycopg/config.h: added __sun__ to the symbols checked for round()
-
-2005-07-21 Federico Di Gregorio <fog@debian.org>
-
- * psycopg/adapter_datetime.c (psyco_XXXFromTicks): fixed the 1900
- years offset reported by Jeroen van Dongen (see ticket #33).
-
-2005-07-17 Federico Di Gregorio <fog@debian.org>
-
- * Release 2.0 beta 4.
-
- * lib/extras.py (DictConnection.cursor): added DictConnection to
- make easier to retrieve data in DictRows.
-
-2005-06-24 Federico Di Gregorio <fog@initd.org>
-
- * psycopg/typecast_datetime.c (typecast_PYINTERVAL_cast): applied patch
- from Geert Jansen to fix interval bug due to overflow.
-
-2005-06-18 Federico Di Gregorio <fog@initd.org>
-
- * setup.cfg: some clarifications and include_dirs example for Mandrake.
-
- * ZPsycopgDA/DA.py: DTMLFile -> HTMLFile everywhere to fix zope
- cut&paste problems.
-
- * MANIFEST.in: added missing files to do bdist_rpm.
-
- * lib/psycopg1.py: fixed .dictfetchrow() to return None if fetchone()
- returns None instead of raising an exception.
-
- * ZPsycopgDA/icons: replaced corrupted icons with good ones.
-
-2005-06-13 Federico Di Gregorio <fog@initd.org>
-
- * psycopg/psycopgmodule.c (psyco_connect): changed the port keyword
- parameter type to int (instead of string); this should fix #23.
-
- * psycopg/cursor_type.c (_psyco_curs_execute): now checks for
- empty queries and raise a ProgrammingError if appropriate (closes:
- #24).
-
- * setup.py: psycopg module renamed to psycopg2.
-
-2005-06-02 Federico Di Gregorio <fog@debian.org>
-
- * psycopg/cursor_type.c (_psyco_curs_execute): fixed segfault when
- not passing string or unicode to .execute().
-
-2005-06-01 Federico Di Gregorio <fog@debian.org>
-
- * examples/fetch.py: added example about using DECLARE CURSOR.
-
- * psycopg/adapter_datetime.c (psyco_TimestampFromTicks): "Hmmm,
- looks like someone forgot that C expects months to start counting
- from 0, but the Python date routines start counting from 1." That
- was me: fixed.
-
-2005-05-31 Federico Di Gregorio <fog@debian.org>
-
- * psycopg/cursor_type.c (_psyco_curs_execute): if a
- UnicodeEncodeError is raised during the converion of a unicode
- query we let it propagate insead of segfaulting.
-
-2005-5-27 Federico Di Gregorio, <fog@lana.initd.org>
-
- * tests/types_basic.py: fixed float and binary tests.
-
-2005-05-26 Federico Di Gregorio <fog@debian.org>
-
- * Release 2.0b3.
-
- * ZPsycopgDA/db.py (DB.convert_description): isolated description
- conversion (and fixed the conversion as per #18).
-
- * ZPsycopgDA/DA.py: fixed again; this time Zope should work for
- real. :/ Also fixed the type-casters (psycopg 2 added the extra
- cursor parameter) as reported in #18.
-
- * psycopg/psycopgmodule.c (init_psycopg): fixed Python 2.2 build.
-
-2005-05-19 Federico Di Gregorio <fog@debian.org>
-
- * Release 2.0b2.
-
- * lib/extras.py (DictRow): Some extra methods for DictRow.
-
- * psycopg/cursor_type.c (_psyco_curs_execute): added explict check
- to avoid using None as bound variables (very importand for cursor
- subclasses calling cursor.execute(self, query, None).
-
-2005-05-18 Federico Di Gregorio <fog@debian.org>
-
- * ZPsycopgDA/DA.py (ALLOWED_PSYCOPG_VERSIONS): updated to work
- with 2.0b2 only (will support only the exact version untill final
- 2.0 release.)
-
- * setup.py: Applied combined patch from Daniele Varrazzo and Jason
- Erickson to build on win32 using MSVC or mingw.
-
-2005-05-15 Federico Di Gregorio <fog@debian.org>
-
- * psycopg/microprotocols.c (microprotocols_adapt): fixed memory
- leak on None as suggested by gh (closes: #16).
-
-2005-05-10 Federico Di Gregorio <fog@debian.org>
-
- * lib/extras.py (DictRow): we now save a reference to the index
- itself and not to the cursor to avoid problems while accessing
- DictRow objects after reusing the cursor for a different query
- (using Kevin Jacobs db_row would be much better but DictRow is
- just an example, right?)
-
-2005-05-09 Federico Di Gregorio <fog@debian.org>
-
- * Release 2.0 beta 1.
-
- * psycopg/typecast_datetime.c (typecast_PYDATETIME_cast): fixed a
- typo (pyDateTimeModuleP->pyDateTimeTypeP) that was causing errors
- with infinite datetime values.
-
- * psycopg/adapter_binary.c (binary_str): Py_XINCREF on the buffer
- that can be NULL on error.
-
- * psycopg/typecast_binary.*: applied slightly modified
- chunk/buffer object patch to allow round-trip of buffer objects
- (BYTEA columns.)
-
- * psycopg/cursor_type.c (psyco_curs_executemany): applied slightly
- fixed patch from wrobell to allow iterators in .executemany().
-
-2005-04-18 Federico Di Gregorio <fog@debian.org>
-
- * MANIFEST.in: included debian directory.
-
-2005-04-10 Federico Di Gregorio <fog@debian.org>
-
- * psycopg/adapter_list.*: added list adapter.
-
- * psycopg/microprotocols.c (microprotocol_getquoted): moved
- _mogrify_getquoted into utility function in the microprotocols
- library.
-
- * setup.py: Added extensive error message on missing datetime
- headers.
-
- * Applied mingw patch from Daniele Varazzo.
-
-2005-04-03 Federico Di Gregorio <fog@debian.org>
-
- * lib/psycopg1.py (connection.autocommit): added compatibility
- .autocommit() method.
-
- * psycopg/psycopgmodule.c (psyco_connect): factory ->
- connection_factory.
-
- * lib/psycopg1.py: added psycopg 1.1.x compatibility module.
-
-2005-03-29 Federico Di Gregorio <fog@debian.org>
-
- * Applied patch to fix tuple count.
-
- * psycopg/pqpath.c (pq_is_busy): Staring from bug report from
- Jason Erickson fixed segfaults due to calling Python function
- without holding the GIL.
-
-2005-03-24 Federico Di Gregorio <fog@debian.org>
-
- * psycopg/adapter_binary.c (binary_escape): propagated Andrea's
- fix to binary adapter.
-
- * psycopg/adapter_qstring.c (qstring_quote): applied patch from
- Andrea Arcangeli to fix allocation failures (>4Gb) on 64 bit
- arches.
-
- * psycopg/typecast_array.c (typecast_array_tokenize): much better
- tokenization code.
-
-2005-03-23 Federico Di Gregorio <fog@debian.org>
-
- * psycopg/typecast_basic.c: all the basic casters now respect the
- passed string length.
-
- * psycopg/typecast.c (typecast_cast): set curs->caster to self
- during the type-casting.
-
- * psycopg/cursor_type.c: added "typecaster" attribute to the
- cursor (this is safe, cursors can't be shared among threads and
- the attribute is RO.)
-
-2005-03-22 Federico Di Gregorio <fog@debian.org>
-
- * psycopg/typecast_array.c: added some more structure to implement
- array typecasting.
-
- * scripts/buildtypes.py: new version to include array data.
-
-2005-03-15 Federico Di Gregorio <fog@debian.org>
-
- * lib/extensions.py: Added AsIs import.
-
-2005-03-12 Federico Di Gregorio <fog@debian.org>
-
- * psycopg/cursor.h: removed "qattr", not used anymore and added
- "cast", holding the typecaster currently in use.
-
- * Release 1.99.13.
-
- * psycopg/cursor_type.c (psyco_curs_executemany): implemented as a
- wrapper to extract python arguments and then call
- _psyco_curs_execute().
-
- * psycopg/cursor_type.c (_psyco_curs_execute): splitted away
- python argument parsing from the real execute code, to later allow
- for .executemany().
-
- * psycopg/cursor_type.c (_psyco_curs_buildrow_fill): modified to
- call typecast_cast().
-
- * psycopg/typecast.c (typecast_call/typecast_cast): modified
- typecast_call to use the new typecast_cast that avoids one string
- conversion on every cast.
-
-2005-03-04 Federico Di Gregorio <fog@initd.org>
-
- * Release 1.99.12.1.
-
- * psycopg/adapter_asis.c (asis_str): changed call to PyObject_Repr
- to PyObject_Str to avoid problems with long integers.
-
-2005-03-03 Federico Di Gregorio <fog@debian.org>
-
- * psycopg/typecast.h: added array casting functions.
-
- * scripts/maketypes.sh: does not generate pgversion.h anymore.
-
- * Updated all examples for the release.
-
-2005-03-02 Federico Di Gregorio <fog@debian.org>
-
- * Release 1.99.12.
-
- * psycopg/adapter_*.c: added __conform__ to all adapters.
-
- * psycopg/adapter_qstring.c (qstring_quote): we now use
- PyString_AsStringAndSize() instead of strlen() that would stop at
- the first embedded \0 (but note that libpq quoting function will
- truncate the string anyway!)
-
- * COPY TO implemented using both old and new (v3) protocol.
-
- * psycopg/pqpath.c (_pq_copy_out_v3): implemented and working.
-
- * psycopg/cursor_type.c (psyco_curs_copy_to): added cursor object
- interface for copy_to.
-
- * COPY FROM implemented using both old and new (v3) protocol.
-
- * psycopg/config.h (Dprintf): declaration for asprintf is gone.
-
- * psycopg/pqpath.c (_pq_copy_in_v3): implemented.
-
-2005-03-01 Federico Di Gregorio <fog@debian.org>
-
- * setup.py: now we generate a slighly more verbose version string
- that embeds some of the compile options, to facilitate users' bug
- reports.
-
- * psycopg/cursor_type.c (psyco_curs_copy_from): we now use
- PyOS_snprintf instead of asprintf. On some platforms this can be
- bad (win32).. if that's your case, get a better platform. :/
-
- * psycopg/microprotocols.c (microprotocols_adapt): fixed small
- typo that made adaptation using __conform__ impossible.
-
-2005-02-28 Federico Di Gregorio <fog@debian.org>
-
- * lib/extras.py: removed AsIs adapter (now a built-in); also
- removed prepare() method from the adapters that don't use it to
- avoid an extra method call at mogrification time.
-
- * psycopg/psycopgmodule.c (psyco_adapters_init): added
- initialization of the AsIs adapter (adapts int, long, float and
- *wonder* None!)
-
- * psycopg/cursor_type.c (_mogrify_getquoted): reorganized the code
- to adapt and then call .getquoted() to obtain the quoted data into
- this new function.
-
-2005-2-27 Federico Di Gregorio <fog@initd.org>
-
- * examples/myfirstrecipe.py: fixed adapter registration.
-
-2005-2-7 Federico Di Gregorio <fog@initd.org>
-
- * setup.py: added patch by Valentino Volonghi to build on MacOS X.
-
-2005-01-29 Federico Di Gregorio <fog@debian.org>
-
- * psycopg/pqpath.c (_pq_fetch_tuples): fixed scale-related
- segfault (*fourth* mail from Andrea. Another couple like this and
- psycopg 2 will exit alpha at warp speed.)
-
- * psycopg/pqpath.c (pq_fetch): _pq_copy_out_3 -> _pq_copy_out_v3
- (second and third mail from Andrea. :/)
-
- * psycopg/cursor_type.c (_psyco_curs_has_write_check): added check
- on .write() attribute, fixed compilation problems (first mail from
- Andrea Arcangeli.)
-
-2005-01-20 Federico Di Gregorio <fog@debian.org>
-
- * lib/extensions.py (register_adapter): added register_adapter
- function, exported ISQLQuote in psycopg.extensions.
-
-2005-01-18 Federico Di Gregorio <fog@debian.org>
-
- * psycopg/pqpath.c (_pq_fetch_tuples): ported scale/precision fix
- from psycopg 1.1.
-
- * LICENSE: detailed licensing information. Re-licensed some parts
- under BSD-like to allow integration is pysqlite.
-
-2005-01-13 Federico Di Gregorio <fog@debian.org>
-
- * ZPsycopgDA/db.py (DB.query
- ): ported ZPsycopgDA connection fix
- from psycopg 1.1.
-
- * lib/*.py: added pydoc-friendly messages.
-
-2005-01-12 Federico Di Gregorio <fog@debian.org>
-
- * Added debian directory (thanks to W. Borgert who sent initial
- patch based on cdbs.)
-
-2004-12-20 Federico Di Gregorio <fog@debian.org>
-
- * psycopg/pqpath.c (pq_execute): removed multiple calls to
- pq_fetch in syncronous DBAPI compatibility mode to solve rowcount
- problem.
-
-2004-12-14 Federico Di Gregorio <fog@debian.org>
-
- * Mm.. release 1.99.11.
-
- * psycopg/cursor_type.c (_psyco_curs_prefetch): fixed bug in
- interaction between the .isready() method and
- _psyco_curs_prefetch: isready now store away the pgres but leave
- prefetch do its work.
-
- * psycopg/*.c: changed the names of most of the psycopg's built-in
- types to replect their position in the psycopg._psycopg module.
-
-2004-12-10 Federico Di Gregorio <fog@debian.org>
-
- * psycopg/cursor_type.c: now *all* write or async accesses to the
- connection object are arbitrated using the connection lock.
-
- * psycopg/cursor_type.c (psyco_curs_isready): now we reset the
- current async cursor if it is ready, to allow other cursors to
- .execute() without raising the "transaction in progress" error.
-
- * psycopg/pqpath.c (pq_is_busy): gained status of high-level
- function with its own blocking and locking.
-
- * psycopg/cursor.h (EXC_IF_CURS_CLOSED): also checks the
- connection (a closed connection implies a closed cursor.)
-
- * psycopg/cursor_type.c: cursor's connection is correctly
- INCREFfed and DECREFfed.
-
- * psycopg/connection_type.c: removed the cursors list from the
- connection object. It is not necessary anymore for the connection
- to know about the cursors and the reference counting will keep the
- connection alive (but possibly closed) until all cursors are
- garbage collected.
-
-2004-11-20 Federico Di Gregorio <fog@initd.org>
-
- * psycopg/cursor_type.c (_mogrify): ported %% fix from 1.1.15.
-
-2004-11-20 Federico Di Gregorio <fog@initd.org>
-
- * psycopg/cursor_type.c (psyco_curs_execute): added check to raise an
- exception if a cursor tries to .execute() while an async query is
- already in execution froma different cursor.
-
-2004-11-20 Federico Di Gregorio <fog@debian.org>
-
- * psycopg/connection_type.c (psyco_conn_cursor): renamed 'cursor'
- argument to 'cursor_factory'.
-
-2004-11-19 Federico Di Gregorio <fog@debian.org>
-
- * psycopg/cursor_type.c (_psyco_curs_buildrow_fill): now standard
- tuples are filled using PyTuple_SET_ITEM while extended types
- (created via row_factory) are filled using PySequence_SetItem.
-
- * psycopg/cursor_type.c: changed cursor attribute name from
- tuple_factory to row_factory.
-
-2004-10-14 Federico Di Gregorio <fog@debian.org>
-
- * psycopg/cursor_type.c (_psyco_curs_buildrow_fill): now we use
- PySequence_SetItem to avoid problems with containers created from
- cursor's .tuple_factory attribute.
-
- * lib/extras.py (DictCursor.execute): fixed stupid bug with cursor
- setting self.tuplefactory instead of self.tuple_factory.
-
-2004-10-02 Federico Di Gregorio <fog@debian.org>
-
- * Release 1.99.10.
-
- * psycopg/cursor_type.c (_psyco_curs_buildrow_*): unified normal
- and factory code into the _psyco_curs_buildrow_fill function; no
- more memory leaks here.
-
- * psycopg/config.h (round): added check for __FreeBSD__ (that
- should be defined when compiling with gcc, I hope.)
-
- * setup.py: removed a lot of code now in setup.cfg.
-
-2004-09-24 Federico Di Gregorio <fog@debian.org>
-
- * psycopg/cursor_type.c (cursor_dealloc): fixed small memory leak
- due to missing disposal of self->pgres.
-
-2004-9-14 Federico Di Gregorio <fog@initd.org>
-
- * examples/dialtone.py: Added adapt() example by Valentino
- Volonghi.
-
-2004-09-14 Federico Di Gregorio <fog@debian.org>
-
- * psycopg/microprotocols.c (microprotocols_adapt): lots of changes
- to the microprotocols layer (it is not micro anymore);
- implementing almost all the PEP 246. The adapter registry is now
- indexed by (type, protocol) and not by type alone.
-
-2004-09-13 Federico Di Gregorio <fog@debian.org>
-
- * psycopg/cursor_type.c (_mogrify): and qattr is gone.
-
-2004-09-05 Federico Di Gregorio <fog@debian.org>
-
- * Release 1.99.9 (or, the "twisting by the pool" release).
-
- * psycopg/pqpath.c (_pq_fetch_tuples): changed to "static void"
- instead of "static int", no ways for this function to fail.
-
-2004-09-04 Federico Di Gregorio <fog@debian.org>
-
- * psycopg/pqpath.c (_pq_fetch_tuples): ported rowcount fix from
- 1.1.15.
-
- * ZPsycopgDA/*: ZPsycopgDA back in action, using the new pooling
- code.
-
-2004-08-29 Federico Di Gregorio <fog@debian.org>
-
- * psycopg/typecast_basic.c (typecast_DECIMAL_cast): added DECIMAL
- typecaster; it even works :).
-
- * scripts/buildtypes.py (basic_types): added DECIMAL typecaster
- for the NUMERIC oid.
-
- * examples/threads.py: updated threads example to use pooling code.
-
- * lib/pool.py: added very simple and thread-safe connection
- pooling class.
-
- * psycopg/cursor_type.c (psyco_curs_fetchmany): fixed problem with
- .fetchall() and .fetchmany() returning None instead of [] on empty
- result sets.
-
- * Release 1.99.8.
-
-2004-08-28 Federico Di Gregorio <fog@debian.org>
-
- * psycopg/cursor_type.c (psyco_curs_execute): added processing of
- unicode queries.
-
- * examples/encoding.py: much better encoding example, also using
- the new UNICODE typecaster.
-
- * psycopg/typecast_basic.c (typecast_UNICODE_cast): added UNICODE
- typecaster.
-
- * lib/extensions.py: the encodings dictionary is not available by
- default but can be accessed from the psycopg.extensions module.
-
- * psycopg/adapter_qstring.h: remove encoding information from
- qstring adapter and moved it into psycopg module.
-
-2004-08-26 Federico Di Gregorio <fog@debian.org>
-
- * psycopg/cursor_type.c (_psyco_curs_prefetch): added check for
- asynchronous fetch by wrong cursor.
-
- * psycopg/pqpath.c (pq_fetch): fixed backend status message (bug
- reported by Daniele Varrazzo.)
-
-2004-07-29 Federico Di Gregorio <fog@debian.org>
-
- * psycopg/typecast_basic.c (typecast_BINARY_cast): reverted to
- using strings instead of buffers when converting postgresql binary
- objects (should *temporarily* fix corruption bug reported on
- win32.)
-
-2004-07-21 Federico Di Gregorio <fog@debian.org>
-
- * psycopg/cursor_type.c: removed __iter__ and next methods from
- object methods and moved them where they do belong (tp_iter and
- tp_iternext.) Bug reported by Daniele Varrazzo (again!)
-
-2004-07-19 Federico Di Gregorio <fog@debian.org>
-
- * psycopg/typecast_datetime.c (typecast_PYINTERVAL_cast): replaced
- round() with micro() when rounding seconds (fixes bugs reported by
- Daniele Varrazzo.)
-
-2004-07-16 Federico Di Gregorio <fog@debian.org>
-
- * psycopg/pqpath.c (pq_set_critical): allow for a custom message
- insted of the one from PQerrorMessage.
- (pq_resolve_critical): added argument to specify if connection is
- to be closed (used to not close it during COPY FROM/TO criticals.)
-
- * psycopg/cursor_type.c (psyco_curs_fileno, psyco_curs_isready):
- added extension methods related to async queries.
-
-2004-07-15 Federico Di Gregorio <fog@debian.org>
-
- * Release 1.99.7.
-
- * examples/tz.py: added example about time zones.
-
- * psycopg/typecast_datetime.c (typecast_PYDATETIME_cast): create
- FixedOffsetTimezone for postgresql "timestamp with time zone"
- types.
-
- * lib/tz.py: added (even more than) needed tzinfo classes.
-
- * psycopg/typecast.c (typecast_call): changed typecast call code
- to take the additional cursor parameter, needed for
- cursor-dependent type casting (tzinfo & friends.)
-
- * psycopg/cursor_type.c (_psyco_curs_buildrow_with_factory): added
- use of tuple factories to fetcXXX methods.
-
- * lib/extras.py: little extra goodies for psycopg.
-
-2004-07-14 Federico Di Gregorio <fog@debian.org>
-
- * Release 1.99.6.
-
- * psycopg/connection_type.c: added .dsn attribute to connection
- objects.
-
- * psycopg/cursor_type.c (psyco_curs_mogrify): added .mogrify()
- method.
-
- * psycopg/adapter_qstring.c: copy the connection encoding only if
- wrapped object is unicode and added table of encodings.
-
-2004-07-13 Federico Di Gregorio <fog@debian.org>
-
- * psycopg/cursor_type.c (_mogrify): moved Dprintf statement to
- avoid dereferencing empty pointer (from 1.1.x)
- (psyco_curs_execute): now we save the query in self->query instead
- of freeing the memory ASAP.
- (cursorObject_members): and we finally export the saved query
- through the cursor members interface. that's all folks.
-
- * lib/extensions.py: added extensions module to clearly separate
- psycopg own extensions from DBAPI-2.0
-
-2004-07-10 Federico Di Gregorio <fog@debian.org>
-
- * psycopg/typecast_datetime.c: ported interval fix from 1.1.x.
-
-2004-05-16 Federico Di Gregorio <fog@debian.org>
-
- * psycopg/typecast_datetime.c (typecast_*_cast): fixed Value error
- when seconds > 59 by setting minutes += 1 and seconds -= 60
- (reported by Marcel Gsteiger.)
-
-2004-04-24 Federico Di Gregorio <fog@debian.org>
-
- * ported time interval patch by Ross Cohen from 1.1.12.
-
-2004-04-19 Federico Di Gregorio <fog@debian.org>
-
- * psycopg/typecast_datetime.c (typecast_PYDATE_cast): applied
- patch from Jason Erickson: min and max taken from datetime.Date
- type.
-
-2004-04-18 Federico Di Gregorio <fog@debian.org>
-
- * Applied changes from Jason Erickson to build on win32; see his
- (slightly edited) entry below. (Still builds on Linux :)
-
- * psycopg/*.c: removed inclusion of pthread.h from all files
- except psycopg/config.h to build on win32 without faking the file.
-
-2004-04-15 Jason Erickson <jerickso@stickpeople.com>
-
- * setup.py: Various changes. The critical ones:
- - Make an empty pthread.h file so all the code doing an
- #include <pthread.h> will find something.
- - Appended the winsock2 library and the PostgreSQL library to
- the library path.
- - Setup the include path.
- - Have the PSYCOPG_VERSION macro be included with quotes.
-
- * config.h: Added/Cleaned up Win32 includes, defines, stub functions.
-
- * typecast.h: Removed ';' after PyObject_HEAD in the
- typecastObject structure since Microsoft Visual Studio does not
- like it.
-
-2004-04-15 Federico Di Gregorio <fog@debian.org>
-
- * Release 1.99.5 (bug-fixing and reorganization)
-
- * setup.py et al.: moved psycopg to psycopg._psycopg to make
- easier to provide high level python-only utilities (like the
- promised pooling code). psycopg/__init__.py imports _psycopg and
- make all the default DBAPI-2.0 stuff available.
-
-2004-04-14 Federico Di Gregorio <fog@debian.org>
-
- * psycopg/psycopgmodule.c (initpsycopg): wrapped initialization of
- date/time adapters in #ifdefs to have psycopg compile without mx
- or builtin datetime.
-
-2004-04-10 Federico Di Gregorio <fog@debian.org>
-
- * Release 1.99.4.
-
-2004-04-09 Federico Di Gregorio <fog@debian.org>
-
- * psycopg/typecast_builtins.c: changed DATE to not include
- DATETIME types anymore.
-
- * psycopg/adapter_datetime.c (pydatetime_str): switched from
- strftime to isoformat to preserve fractional seconds.
-
-2004-04-08 Federico Di Gregorio <fog@debian.org>
-
- * psycopg/psycopgmodule.c (psyco_connect): ported sslmode
- parameter from 1.1 branch.
-
- * psycopg/adapter_datetime.*: added python built-in datetime
- adapters. also added the datetime typecasters (still using mx as
- default).
-
- * psycopg/typecast.h: removed aliases, they now live in the right
- typecast_xxx.c file.
-
-2004-03-08 Federico Di Gregorio <fog@debian.org>
-
- * Release 1.99.3 (alpha 4).
-
- * examples/lastrowid.py: and the .lastrowid example is in.
-
- * psycopg/cursor_type.c (_mogrify): added call to .prepare()
- method in both dict and sequence path.
-
- * psycopg/connection_int.c (conn_set_client_encoding): added
- encoding-change code.
-
- * psycopg/adapter_qstring.c (qstring_quote): added hard-coded
- support for utf8 and latin1 encodings.
-
-2004-03-01 Federico Di Gregorio <fog@debian.org>
-
- * psycopg/connection_int.c (conn_close): does not use libpq
- functions on NULL pgconn (this can happen when conn_close is
- called after a failed PQconnect.)
-
-2004-02-29 Federico Di Gregorio <fog@debian.org>
-
- * Release 1.99.2 (alpha 3).
-
- * psycopg/cursor_type.c: added .rownumber and .connection
- attributes. Also added .scroll(), .next() and .__iter__() methods
- (see DBAPI2-.0 extensions on PEP.)
-
- * psycopg/connection_type.c (psyco_conn_set_isolation_level):
- added connection method .set_isolation_level(). Also added all
- error objects to the connection (see DBAPI2-.0 extensions on PEP.)
-
- * psycopg/connection_int.c (conn_switch_isolation_level): added
- isolation level switching code.
-
- * setup.py: removed all references to PSYCOPG_NEWSTYLE: support
- for python < 2.2 has been dropped.
-
- * typecast_basic.c (typecast_BINARY_cast): now binary objects are
- returned as true buffers.
-
- * adapter_binary.*: added adapter for buffers and binary (bytea)
- objects.
-
- * Release 1.99.1 (alpha 2).
-
- * adapter_mxdatetime.*: added adapters for all mx.DateTime types.
-
-2004-02-28 Federico Di Gregorio <fog@debian.org>
-
- * cursor_type.c (_mogrify): complete rework of the mogrification
- code to use the microprotocols_adapt function.
-
- * typecast_basic.c (typecast_BOOLEAN_cast): we now return real
- Py_True and Py_False values.
-
- * microprotocols.h: added very simple microprotocols
- implementation to allow for python->postgresql types registry.
-
-2004-01-05 Federico Di Gregorio <fog@debian.org>
-
- * connection_int.c (conn_commit/conn_rollback): added code to
- commit/rollback and connection methods.
-
-2004-01-04 Federico Di Gregorio <fog@debian.org>
-
- * cursor_type.c (psyco_curs_fetchone): added fetchone method.
-
-2004-01-03 Federico Di Gregorio <fog@debian.org>
-
- * added (empty) INSTALL file.
-
- * cursor_type.c (cursor_dealloc): added qattr for custom object
- quoting using a callable attribute.
- (_mogrify): ported new, fixed mogrification code from 1.1.12.
-
-2003-08-01 Federico Di Gregorio <fog@debian.org>
-
- * cursor_type.c (_mogrify_sequence): added sequence mogrification,
- can be done better, on the dict model.
-
-2003-07-28 Federico Di Gregorio <fog@debian.org>
-
- * typeobj_qstring.c: added quoted strings (can use both own code,
- like psycopg 1.x or PQescapeString from lipq.)
-
-2003-07-21 Federico Di Gregorio <fog@debian.org>
-
- * connection_type.c (psyco_conn_close): added .close()
- method. wow.
-
- * cursor_*.c: added basic cursor interface (new-style.)
-
-2003-07-20 Federico Di Gregorio <fog@debian.org>
-
- * psycopg/*: beginning of new source layout. if you think this
- changelog is somewhat empty, you're right. look at
- doc/ChangeLog-1.x for psycopg 1.x changelog just before the
- branch.
-
-
diff --git a/NEWS b/NEWS
index 2dfe49d..ca013f1 100644
--- a/NEWS
+++ b/NEWS
@@ -1,264 +1,302 @@
+What's new in psycopg 2.5
+-------------------------
+
+New features:
+
+- Added :ref:`JSON adaptation <adapt-json>`.
+- Added :ref:`support for PostgreSQL 9.2 range types <adapt-range>`.
+- `connection` and `cursor` objects can be used in ``with`` statements
+ as context managers as specified by recent |DBAPI|_ extension.
+- Added `~psycopg2.extensions.Diagnostics` object to get extended info
+ from a database error. Many thanks to Matthew Woodcraft for the
+ implementation (:ticket:`#149`).
+- Added `connection.cursor_factory` attribute to customize the default
+ object returned by `~connection.cursor()`.
+- Added support for backward scrollable cursors. Thanks to Jon Nelson
+ for the initial patch (:ticket:`#108`).
+- Added a simple way to :ref:`customize casting of composite types
+ <adapt-composite>` into Python objects other than namedtuples.
+ Many thanks to Ronan Dunklau and Tobias Oberstein for the feature
+ development.
+- `connection.reset()` implemented using :sql:`DISCARD ALL` on server
+ versions supporting it.
+
+Bug fixes:
+
+- Properly cleanup memory of broken connections (:ticket:`#148`).
+- Fixed bad interaction of ``setup.py`` with other dependencies in
+ Distribute projects on Python 3 (:ticket:`#153`).
+
+Other changes:
+
+- Added support for Python 3.3.
+- Dropped support for Python 2.4. Please use Psycopg 2.4.x if you need it.
+- `~psycopg2.errorcodes` map updated to PostgreSQL 9.2.
+- Dropped Zope adapter from source repository. ZPsycopgDA now has its own
+ project at <http://github.com/psycopg/ZPsycopgDA>.
+
+
What's new in psycopg 2.4.6
----------------------------
-
- - Fixed 'cursor()' arguments propagation in connection subclasses
- and overriding of the 'cursor_factory' argument. Thanks to
- Corry Haines for the report and the initial patch (ticket #105).
- - Dropped GIL release during string adaptation around a function call
- invoking a Python API function, which could cause interpreter crash.
- Thanks to Manu Cupcic for the report (ticket #110).
- - Close a green connection if there is an error in the callback.
- Maybe a harsh solution but it leaves the program responsive
- (ticket #113).
- - 'register_hstore()', 'register_composite()', 'tpc_recover()' work with
- RealDictConnection and Cursor (ticket #114).
- - Fixed broken pool for Zope and connections re-init across ZSQL methods
- in the same request (tickets #123, #125, #142).
- - connect() raises an exception instead of swallowing keyword arguments
- when a connection string is specified as well (ticket #131).
- - Discard any result produced by 'executemany()' (ticket #133).
- - Fixed pickling of FixedOffsetTimezone objects (ticket #135).
- - Release the GIL around PQgetResult calls after COPY (ticket #140).
- - Fixed empty strings handling in composite caster (ticket #141).
- - Fixed pickling of DictRow and RealDictRow objects.
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+- Fixed 'cursor()' arguments propagation in connection subclasses
+ and overriding of the 'cursor_factory' argument. Thanks to
+ Corry Haines for the report and the initial patch (:ticket:`#105`).
+- Dropped GIL release during string adaptation around a function call
+ invoking a Python API function, which could cause interpreter crash.
+ Thanks to Manu Cupcic for the report (:ticket:`#110`).
+- Close a green connection if there is an error in the callback.
+ Maybe a harsh solution but it leaves the program responsive
+ (:ticket:`#113`).
+- 'register_hstore()', 'register_composite()', 'tpc_recover()' work with
+ RealDictConnection and Cursor (:ticket:`#114`).
+- Fixed broken pool for Zope and connections re-init across ZSQL methods
+ in the same request (tickets #123, #125, #142).
+- connect() raises an exception instead of swallowing keyword arguments
+ when a connection string is specified as well (:ticket:`#131`).
+- Discard any result produced by 'executemany()' (:ticket:`#133`).
+- Fixed pickling of FixedOffsetTimezone objects (:ticket:`#135`).
+- Release the GIL around PQgetResult calls after COPY (:ticket:`#140`).
+- Fixed empty strings handling in composite caster (:ticket:`#141`).
+- Fixed pickling of DictRow and RealDictRow objects.
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.
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+- 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).
+- '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.
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+- 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!
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+- 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).
+- 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:
+New features and changes:
- - Added support for Python 3.1 and 3.2. The conversion has also
- brought several improvements:
+- 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.
+ - 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:
+- 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.
+ - 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:
+- 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.
+ - 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:
+- 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.
+ - '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:
+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.
+- 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.
+- 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).
+- Fixed build problem on CentOS 5.5 x86_64 (:ticket:`#23`).
-What's new in psycopg 2.3.0
----------------------------
+What's new in psycopg 2.3
+-------------------------
psycopg 2.3 aims to expose some new features introduced in PostgreSQL 9.0.
-* Main new features:
+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.
+- `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:
+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.
+- 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:
+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).
+- 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:
+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.
+- 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:
+Bux fixes:
- - psycopg now builds again on MS Windows.
+- psycopg now builds again on MS Windows.
-What's new in psycopg 2.2.0
----------------------------
+What's new in psycopg 2.2
+-------------------------
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
@@ -269,262 +307,288 @@ 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:
+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.
+- 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:
+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.
+- 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:
-* 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.
+- 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.
+Bug fixes:
-* 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.)
+- 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:
-* New features:
- - Support for UUID arrays.
- - It is now possible to build psycopg linking to a static libpq
- library.
+- 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.
+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:
-* New features:
- - The connection object now has a reset() method that can be used to
- reset the connection to its default state.
+- 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.
+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:
-* 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.
+- 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.
+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.
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+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:
-* 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.
+- 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.
-
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+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:
-* 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.
+- 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.
-* 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.
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+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
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+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()
@@ -535,17 +599,17 @@ What's new in psycopg 2.0.5
* 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.
@@ -556,7 +620,7 @@ What's new in psycopg 2.0.2
version is issued only if __GCC__ is defined.)
What's new in psycopg 2.0.1
----------------------------
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
* ZPsycopgDA now actually loads.
@@ -572,7 +636,7 @@ What's new in psycopg 2.0
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.)
@@ -580,18 +644,18 @@ What's new in psycopg 2.0 beta 7
* 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).
@@ -604,13 +668,13 @@ What's new in psycopg 2.0 beta 6
* 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.
@@ -619,20 +683,20 @@ What's new in psycopg 2.0 beta 5
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.
@@ -642,14 +706,14 @@ What's new in psycopg 2.0 beta 4
* 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.)
@@ -662,7 +726,7 @@ What's new in psycopg 2.0 beta 2
* 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.)
@@ -689,9 +753,9 @@ What's new in psycopg 2.0 beta 1
* 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.
@@ -699,7 +763,7 @@ What's new in psycopg 1.99.13
faster than before.)
What's new in psycopg 1.99.12
------------------------------
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
* .rowcount should be ok and in sync with psycopg 1.
@@ -712,9 +776,9 @@ What's new in psycopg 1.99.12
* 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' argument in .cursor() connection method renamed to
'cursor_factory'.
* changed 'tuple_factory' cursor attribute name to 'row_factory'.
@@ -725,7 +789,7 @@ What's new in psycopg 1.99.11
* 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
@@ -738,7 +802,7 @@ What's new in psycopg 1.99.10
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.
@@ -753,69 +817,54 @@ What's new in psycopg 1.99.9
* 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)
-* 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
+ 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
@@ -823,36 +872,33 @@ What's new in psycopg 1.99.6
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
@@ -860,7 +906,7 @@ What's new in psycopg 1.99.2
- 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;
@@ -870,7 +916,7 @@ What's new in psycopg 1.99.1
What's new in psycopg 1.99.0
-----------------------------
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
* reorganized the whole source tree;
diff --git a/ZPsycopgDA/DA.py b/ZPsycopgDA/DA.py
deleted file mode 100644
index f5489ce..0000000
--- a/ZPsycopgDA/DA.py
+++ /dev/null
@@ -1,360 +0,0 @@
-# ZPsycopgDA/DA.py - ZPsycopgDA Zope product: Database Connection
-#
-# Copyright (C) 2004-2010 Federico Di Gregorio <fog@debian.org>
-#
-# 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 modules needed by _psycopg to allow tools like py2exe to do
-# their work without bothering about the module dependencies.
-
-
-ALLOWED_PSYCOPG_VERSIONS = ('2.4', '2.4.1', '2.4.4', '2.4.5', '2.4.6')
-
-import sys
-import time
-import db
-import re
-
-import Acquisition
-import Shared.DC.ZRDB.Connection
-
-from db import DB
-from Globals import HTMLFile
-from ExtensionClass import Base
-from App.Dialogs import MessageDialog
-from DateTime import DateTime
-
-# ImageFile is deprecated in Zope >= 2.9
-try:
- from App.ImageFile import ImageFile
-except ImportError:
- # Zope < 2.9. If PIL's installed with a .pth file, we're probably
- # hosed.
- from ImageFile import ImageFile
-
-# import psycopg and functions/singletons needed for date/time conversions
-
-import psycopg2
-from psycopg2 import NUMBER, STRING, ROWID, DATETIME
-from psycopg2.extensions import INTEGER, LONGINTEGER, FLOAT, BOOLEAN, DATE
-from psycopg2.extensions import TIME, INTERVAL
-from psycopg2.extensions import new_type, register_type
-
-
-# add a new connection to a folder
-
-manage_addZPsycopgConnectionForm = HTMLFile('dtml/add',globals())
-
-def manage_addZPsycopgConnection(self, id, title, connection_string,
- zdatetime=None, tilevel=2,
- encoding='', check=None, REQUEST=None):
- """Add a DB connection to a folder."""
- self._setObject(id, Connection(id, title, connection_string,
- zdatetime, check, tilevel, encoding))
- if REQUEST is not None: return self.manage_main(self, REQUEST)
-
-
-# the connection object
-
-class Connection(Shared.DC.ZRDB.Connection.Connection):
- """ZPsycopg Connection."""
- _isAnSQLConnection = 1
-
- id = 'Psycopg2_database_connection'
- database_type = 'Psycopg2'
- meta_type = title = 'Z Psycopg 2 Database Connection'
- icon = 'misc_/conn'
-
- def __init__(self, id, title, connection_string,
- zdatetime, check=None, tilevel=2, encoding='UTF-8'):
- self.zdatetime = zdatetime
- self.id = str(id)
- self.edit(title, connection_string, zdatetime,
- check=check, tilevel=tilevel, encoding=encoding)
-
- def factory(self):
- return DB
-
- ## connection parameters editing ##
-
- def edit(self, title, connection_string,
- zdatetime, check=None, tilevel=2, encoding='UTF-8'):
- self.title = title
- self.connection_string = connection_string
- self.zdatetime = zdatetime
- self.tilevel = tilevel
- self.encoding = encoding
-
- if check: self.connect(self.connection_string)
-
- manage_properties = HTMLFile('dtml/edit', globals())
-
- def manage_edit(self, title, connection_string,
- zdatetime=None, check=None, tilevel=2, encoding='UTF-8',
- REQUEST=None):
- """Edit the DB connection."""
- self.edit(title, connection_string, zdatetime,
- check=check, tilevel=tilevel, encoding=encoding)
- if REQUEST is not None:
- msg = "Connection edited."
- return self.manage_main(self,REQUEST,manage_tabs_message=msg)
-
- def connect(self, s):
- try:
- self._v_database_connection.close()
- except:
- pass
-
- # check psycopg version and raise exception if does not match
- if psycopg2.__version__.split(' ')[0] not in ALLOWED_PSYCOPG_VERSIONS:
- raise ImportError("psycopg version mismatch (imported %s)" %
- psycopg2.__version__)
-
- self._v_connected = ''
- dbf = self.factory()
-
- # TODO: let the psycopg exception propagate, or not?
- self._v_database_connection = dbf(
- self.connection_string, self.tilevel, self.get_type_casts(), self.encoding)
- self._v_database_connection.open()
- self._v_connected = DateTime()
-
- return self
-
- def get_type_casts(self):
- # note that in both cases order *is* important
- if self.zdatetime:
- return ZDATETIME, ZDATE, ZTIME
- else:
- return DATETIME, DATE, TIME
-
- ## browsing and table/column management ##
-
- manage_options = Shared.DC.ZRDB.Connection.Connection.manage_options
- # + (
- # {'label': 'Browse', 'action':'manage_browse'},)
-
- #manage_tables = HTMLFile('dtml/tables', globals())
- #manage_browse = HTMLFile('dtml/browse', globals())
-
- info = None
-
- def table_info(self):
- return self._v_database_connection.table_info()
-
-
- def __getitem__(self, name):
- if name == 'tableNamed':
- if not hasattr(self, '_v_tables'): self.tpValues()
- return self._v_tables.__of__(self)
- raise KeyError, name
-
- def tpValues(self):
- res = []
- conn = self._v_database_connection
- for d in conn.tables(rdb=0):
- try:
- name = d['TABLE_NAME']
- b = TableBrowser()
- b.__name__ = name
- b._d = d
- b._c = c
- try:
- b.icon = table_icons[d['TABLE_TYPE']]
- except:
- pass
- r.append(b)
- except:
- pass
- return res
-
-
-## database connection registration data ##
-
-classes = (Connection,)
-
-meta_types = ({'name':'Z Psycopg 2 Database Connection',
- 'action':'manage_addZPsycopgConnectionForm'},)
-
-folder_methods = {
- 'manage_addZPsycopgConnection': manage_addZPsycopgConnection,
- 'manage_addZPsycopgConnectionForm': manage_addZPsycopgConnectionForm}
-
-__ac_permissions__ = (
- ('Add Z Psycopg Database Connections',
- ('manage_addZPsycopgConnectionForm', 'manage_addZPsycopgConnection')),)
-
-# add icons
-
-misc_={'conn': ImageFile('icons/DBAdapterFolder_icon.gif', globals())}
-
-for icon in ('table', 'view', 'stable', 'what', 'field', 'text', 'bin',
- 'int', 'float', 'date', 'time', 'datetime'):
- misc_[icon] = ImageFile('icons/%s.gif' % icon, globals())
-
-
-## zope-specific psycopg typecasters ##
-
-# convert an ISO timestamp string from postgres to a Zope DateTime object
-def _cast_DateTime(iso, curs):
- if iso:
- if iso in ['-infinity', 'infinity']:
- return iso
- else:
- return DateTime(iso)
-
-# convert an ISO date string from postgres to a Zope DateTime object
-def _cast_Date(iso, curs):
- if iso:
- if iso in ['-infinity', 'infinity']:
- return iso
- else:
- return DateTime(iso)
-
-# Convert a time string from postgres to a Zope DateTime object.
-# NOTE: we set the day as today before feeding to DateTime so
-# that it has the same DST settings.
-def _cast_Time(iso, curs):
- if iso:
- if iso in ['-infinity', 'infinity']:
- return iso
- else:
- return DateTime(time.strftime('%Y-%m-%d %H:%M:%S',
- time.localtime(time.time())[:3]+
- time.strptime(iso[:8], "%H:%M:%S")[3:]))
-
-# NOTE: we don't cast intervals anymore because they are passed
-# untouched to Zope.
-def _cast_Interval(iso, curs):
- return iso
-
-ZDATETIME = new_type((1184, 1114), "ZDATETIME", _cast_DateTime)
-ZINTERVAL = new_type((1186,), "ZINTERVAL", _cast_Interval)
-ZDATE = new_type((1082,), "ZDATE", _cast_Date)
-ZTIME = new_type((1083,), "ZTIME", _cast_Time)
-
-
-## table browsing helpers ##
-
-class TableBrowserCollection(Acquisition.Implicit):
- pass
-
-class Browser(Base):
- def __getattr__(self, name):
- try:
- return self._d[name]
- except KeyError:
- raise AttributeError, name
-
-class values:
- def len(self):
- return 1
-
- def __getitem__(self, i):
- try:
- return self._d[i]
- except AttributeError:
- pass
- self._d = self._f()
- return self._d[i]
-
-class TableBrowser(Browser, Acquisition.Implicit):
- icon = 'what'
- Description = check = ''
- info = HTMLFile('table_info', globals())
- menu = HTMLFile('table_menu', globals())
-
- def tpValues(self):
- v = values()
- v._f = self.tpValues_
- return v
-
- def tpValues_(self):
- r=[]
- tname=self.__name__
- for d in self._c.columns(tname):
- b=ColumnBrowser()
- b._d=d
- try: b.icon=field_icons[d['Type']]
- except: pass
- b.TABLE_NAME=tname
- r.append(b)
- return r
-
- def tpId(self): return self._d['TABLE_NAME']
- def tpURL(self): return "Table/%s" % self._d['TABLE_NAME']
- def Name(self): return self._d['TABLE_NAME']
- def Type(self): return self._d['TABLE_TYPE']
-
- manage_designInput=HTMLFile('designInput',globals())
- def manage_buildInput(self, id, source, default, REQUEST=None):
- "Create a database method for an input form"
- args=[]
- values=[]
- names=[]
- columns=self._columns
- for i in range(len(source)):
- s=source[i]
- if s=='Null': continue
- c=columns[i]
- d=default[i]
- t=c['Type']
- n=c['Name']
- names.append(n)
- if s=='Argument':
- values.append("<dtml-sqlvar %s type=%s>'" %
- (n, vartype(t)))
- a='%s%s' % (n, boboType(t))
- if d: a="%s=%s" % (a,d)
- args.append(a)
- elif s=='Property':
- values.append("<dtml-sqlvar %s type=%s>'" %
- (n, vartype(t)))
- else:
- if isStringType(t):
- if find(d,"\'") >= 0: d=join(split(d,"\'"),"''")
- values.append("'%s'" % d)
- elif d:
- values.append(str(d))
- else:
- raise ValueError, (
- 'no default was given for <em>%s</em>' % n)
-
-class ColumnBrowser(Browser):
- icon='field'
-
- def check(self):
- return ('\t<input type=checkbox name="%s.%s">' %
- (self.TABLE_NAME, self._d['Name']))
- def tpId(self): return self._d['Name']
- def tpURL(self): return "Column/%s" % self._d['Name']
- def Description(self):
- d=self._d
- if d['Scale']:
- return " %(Type)s(%(Precision)s,%(Scale)s) %(Nullable)s" % d
- else:
- return " %(Type)s(%(Precision)s) %(Nullable)s" % d
-
-table_icons={
- 'TABLE': 'table',
- 'VIEW':'view',
- 'SYSTEM_TABLE': 'stable',
- }
-
-field_icons={
- NUMBER.name: 'i',
- STRING.name: 'text',
- DATETIME.name: 'date',
- INTEGER.name: 'int',
- FLOAT.name: 'float',
- BOOLEAN.name: 'bin',
- ROWID.name: 'int'
- }
diff --git a/ZPsycopgDA/__init__.py b/ZPsycopgDA/__init__.py
deleted file mode 100644
index 118c4fe..0000000
--- a/ZPsycopgDA/__init__.py
+++ /dev/null
@@ -1,29 +0,0 @@
-# ZPsycopgDA/__init__.py - ZPsycopgDA Zope product
-#
-# Copyright (C) 2004-2010 Federico Di Gregorio <fog@debian.org>
-#
-# 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 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'
-
-import DA
-
-def initialize(context):
- context.registerClass(
- DA.Connection,
- permission = 'Add Z Psycopg 2 Database Connections',
- constructors = (DA.manage_addZPsycopgConnectionForm,
- DA.manage_addZPsycopgConnection),
- icon = 'icons/DBAdapterFolder_icon.gif')
diff --git a/ZPsycopgDA/db.py b/ZPsycopgDA/db.py
deleted file mode 100644
index b594b3f..0000000
--- a/ZPsycopgDA/db.py
+++ /dev/null
@@ -1,209 +0,0 @@
-# ZPsycopgDA/db.py - query execution
-#
-# Copyright (C) 2004-2010 Federico Di Gregorio <fog@debian.org>
-#
-# 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 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
-
-from ZODB.POSException import ConflictError
-
-import site
-import pool
-
-import psycopg2
-from psycopg2.extensions import INTEGER, LONGINTEGER, FLOAT, BOOLEAN, DATE, TIME
-from psycopg2.extensions import TransactionRollbackError, register_type
-from psycopg2 import NUMBER, STRING, ROWID, DATETIME
-
-
-# the DB object, managing all the real query work
-
-class DB(TM, dbi_db.DB):
-
- _p_oid = _p_changed = _registered = None
-
- def __init__(self, dsn, tilevel, typecasts, enc='utf-8'):
- self.dsn = dsn
- self.tilevel = tilevel
- self.typecasts = typecasts
- 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, 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)
- if init:
- # use set_session where available as in these versions
- # set_isolation_level generates an extra query.
- if psycopg2.__version__ >= '2.4.2':
- conn.set_session(isolation_level=int(self.tilevel))
- else:
- 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):
- try:
- conn = pool.getconn(self.dsn, False)
- except AttributeError:
- pass
- pool.putconn(self.dsn, conn, close)
-
- def getcursor(self):
- conn = self.getconn(False)
- return conn.cursor()
-
- def _finish(self, *ignored):
- try:
- conn = self.getconn(False)
- conn.commit()
- self.putconn()
- except AttributeError:
- pass
-
- def _abort(self, *ignored):
- try:
- conn = self.getconn(False)
- conn.rollback()
- self.putconn()
- except AttributeError:
- pass
-
- def open(self):
- # this will create a new pool for our DSN if not already existing,
- # then get and immediately release a connection
- self.getconn()
- self.putconn()
-
- def close(self):
- # FIXME: if this connection is closed we flush all the pool associated
- # with the current DSN; does this makes sense?
- pool.flushpool(self.dsn)
-
- def sortKey(self):
- return 1
-
- def make_mappings(self):
- """Generate the mappings used later by self.convert_description()."""
- self.type_mappings = {}
- for t, s in [(INTEGER,'i'), (LONGINTEGER, 'i'), (NUMBER, 'n'),
- (BOOLEAN,'n'), (ROWID, 'i'),
- (DATETIME, 'd'), (DATE, 'd'), (TIME, 'd')]:
- for v in t.values:
- self.type_mappings[v] = (t, s)
-
- def convert_description(self, desc, use_psycopg_types=False):
- """Convert DBAPI-2.0 description field to Zope format."""
- items = []
- for name, typ, width, ds, p, scale, null_ok in desc:
- m = self.type_mappings.get(typ, (STRING, 's'))
- items.append({
- 'name': name,
- 'type': use_psycopg_types and m[0] or m[1],
- 'width': width,
- 'precision': p,
- 'scale': scale,
- 'null': null_ok,
- })
- return items
-
- ## tables and rows ##
-
- def tables(self, rdb=0, _care=('TABLE', 'VIEW')):
- self._register()
- c = self.getcursor()
- c.execute(
- "SELECT t.tablename AS NAME, 'TABLE' AS TYPE "
- " FROM pg_tables t WHERE tableowner <> 'postgres' "
- "UNION SELECT v.viewname AS NAME, 'VIEW' AS TYPE "
- " FROM pg_views v WHERE viewowner <> 'postgres' "
- "UNION SELECT t.tablename AS NAME, 'SYSTEM_TABLE\' AS TYPE "
- " FROM pg_tables t WHERE tableowner = 'postgres' "
- "UNION SELECT v.viewname AS NAME, 'SYSTEM_TABLE' AS TYPE "
- "FROM pg_views v WHERE viewowner = 'postgres'")
- res = []
- for name, typ in c.fetchall():
- if typ in _care:
- res.append({'TABLE_NAME': name, 'TABLE_TYPE': typ})
- self.putconn()
- return res
-
- def columns(self, table_name):
- self._register()
- c = self.getcursor()
- try:
- r = c.execute('SELECT * FROM "%s" WHERE 1=0' % table_name)
- except:
- return ()
- self.putconn()
- return self.convert_description(c.description, True)
-
- ## query execution ##
-
- def query(self, query_string, max_rows=None, query_data=None):
- self._register()
- self.calls = self.calls+1
-
- desc = ()
- res = []
- nselects = 0
-
- c = self.getcursor()
-
- try:
- for qs in [x for x in query_string.split('\0') if x]:
- try:
- if query_data:
- c.execute(qs, query_data)
- else:
- 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)
- raise ConflictError("TransactionRollbackError from psycopg2")
- except psycopg2.OperationalError:
- #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)
- pass
- if c.description is not None:
- nselects += 1
- if c.description != desc and nselects > 1:
- raise psycopg2.ProgrammingError(
- 'multiple selects in single query not allowed')
- if max_rows:
- res = c.fetchmany(max_rows)
- else:
- res = c.fetchall()
- desc = c.description
- self.failures = 0
-
- except StandardError, err:
- self._abort()
- raise err
-
- return self.convert_description(desc), res
diff --git a/ZPsycopgDA/dtml/add.dtml b/ZPsycopgDA/dtml/add.dtml
deleted file mode 100644
index 330a001..0000000
--- a/ZPsycopgDA/dtml/add.dtml
+++ /dev/null
@@ -1,108 +0,0 @@
-<dtml-var manage_page_header>
-
-<dtml-var "manage_form_title(this(), _,
- form_title='Add Z Psycopg 2 Database Connection',
- help_product='ZPsycopgDA',
- help_topic='ZPsycopgDA-Method-Add.stx'
- )">
-
-<p class="form-help">
-A Zope Psycopg 2 Database Connection is used to connect and execute
-queries on a PostgreSQL database.
-</p>
-
-<p class="form-help">
-In the form below <em>Connection String</em> (also called the Data Source Name
-or DSN for short) is a string... (TODO: finish docs)
-</p>
-
-<form action="manage_addZPsycopgConnection" method="POST">
-<table cellspacing="0" cellpadding="2" border="0">
- <tr>
- <td align="left" valign="top">
- <div class="form-label">
- Id
- </div>
- </td>
- <td align="left" valign="top">
- <input type="text" name="id" size="40"
- value="Psycopg2_database_connection" />
- </td>
- </tr>
- <tr>
- <td align="left" valign="top">
- <div class="form-optional">
- Title
- </div>
- </td>
- <td align="left" valign="top">
- <input type="text" name="title" size="40"
- value="Z Psycopg 2 Database Connection"/>
- </td>
- </tr>
- <tr>
- <td align="left" valign="top">
- <div class="form-label">
- Connection string
- </div>
- </td>
- <td align="left" valign="top">
- <input type="text" name="connection_string" size="40" value="" />
- </td>
- </tr>
- <tr>
- <td align="left" valign="top">
- <div class="form-label">
- Connect immediately
- </div>
- </td>
- <td align="left" valign="top">
- <input type="checkbox" name="check" value="YES" checked="YES" />
- </td>
- </tr>
- <tr>
- <td align="left" valign="top">
- <div class="form-label">
- Use Zope's internal DateTime
- </div>
- </td>
- <td align="left" valign="top">
- <input type="checkbox" name="zdatetime" value="YES" checked="YES" />
- </td>
- </tr>
- <tr>
- <td align="left" valign="top">
- <div class="form-label">
- Transaction isolation level
- </div>
- </td>
- <td align="left" valign="top">
- <select name="tilevel:int">
- <option value="4">Read uncommitted</option>
- <option value="1">Read committed</option>
- <option value="2" selected="YES">Repeatable read</option>
- <option value="3">Serializable</option>
- </select>
- </td>
- </tr>
- <tr>
- <td align="left" valign="top">
- <div class="form-label">
- Encoding
- </div>
- </td>
- <td align="left" valign="top">
- <input type="text" name="encoding" size="40" value="" />
- </td>
- </tr>
- <tr>
- <td align="left" valign="top" colspan="2">
- <div class="form-element">
- <input class="form-element" type="submit" name="submit" value=" Add " />
- </div>
- </td>
- </tr>
-</table>
-</form>
-
-<dtml-var manage_page_footer>
diff --git a/ZPsycopgDA/dtml/browse.dtml b/ZPsycopgDA/dtml/browse.dtml
deleted file mode 100644
index deffd0a..0000000
--- a/ZPsycopgDA/dtml/browse.dtml
+++ /dev/null
@@ -1,11 +0,0 @@
-<html>
- <head><title><dtml-var title_or_id >tables</title></head>
- <body bgcolor="#FFFFFF" link="#000099" vlink="#555555" alink="#77003B">
- <dtml-var manage_tabs>
- <dtml-tree header="info">
- <IMG SRC="<dtml-var SCRIPT_NAME >/misc_/ZPsycopgDA/<dtml-var icon>"
- ALT="<dtml-var Type>" BORDER="0">
- <dtml-var Name><dtml-var Description>
- </dtml-tree>
- </body>
-</html>
diff --git a/ZPsycopgDA/dtml/edit.dtml b/ZPsycopgDA/dtml/edit.dtml
deleted file mode 100644
index cffb43b..0000000
--- a/ZPsycopgDA/dtml/edit.dtml
+++ /dev/null
@@ -1,84 +0,0 @@
-<dtml-var manage_page_header>
-<dtml-var manage_tabs>
-
-<form action="manage_edit" method="POST">
-<table cellspacing="0" cellpadding="2" border="0">
- <tr>
- <td align="left" valign="top">
- <div class="form-optional">
- Title
- </div>
- </td>
- <td align="left" valign="top">
- <input type="text" name="title" size="40"
- value="&dtml-title;"/>
- </td>
- </tr>
- <tr>
- <td align="left" valign="top">
- <div class="form-label">
- Connection string
- </div>
- </td>
- <td align="left" valign="top">
- <input type="text" name="connection_string" size="40"
- value="&dtml-connection_string;" />
- </td>
- </tr>
- <tr>
- <td align="left" valign="top">
- <div class="form-label">
- Use Zope's internal DateTime
- </div>
- </td>
- <td align="left" valign="top">
- <input type="checkbox" name="zdatetime" value="YES"
- <dtml-if expr="zdatetime">checked="YES"</dtml-if> />
- </td>
- </tr>
- <tr>
- <td align="left" valign="top">
- <div class="form-label">
- Transaction isolation level
- </div>
- </td>
- <td align="left" valign="top">
- <select name="tilevel:int">
- <option value="4"
- <dtml-if expr="tilevel==4">selected="YES"</dtml-if>>
- Read uncommitted</option>
- <option value="1"
- <dtml-if expr="tilevel==1">selected="YES"</dtml-if>>
- Read committed</option>
- <option value="2"
- <dtml-if expr="tilevel==2">selected="YES"</dtml-if>>
- Repeatable read</option>
- <option value="3"
- <dtml-if expr="tilevel==3">selected="YES"</dtml-if>>
- Serializable</option>
- </select>
- </td>
- </tr>
- <tr>
- <td align="left" valign="top">
- <div class="form-label">
- Encoding
- </div>
- </td>
- <td align="left" valign="top">
- <input type="text" name="encoding" size="40"
- value="&dtml-encoding;" />
- </td>
- </tr>
- <tr>
- <td align="left" valign="top" colspan="2">
- <div class="form-element">
- <input class="form-element" type="submit" name="submit"
- value=" Save Changes " />
- </div>
- </td>
- </tr>
-</table>
-</form>
-
-<dtml-var manage_page_footer>
diff --git a/ZPsycopgDA/dtml/table_info.dtml b/ZPsycopgDA/dtml/table_info.dtml
deleted file mode 100644
index 639c23f..0000000
--- a/ZPsycopgDA/dtml/table_info.dtml
+++ /dev/null
@@ -1,7 +0,0 @@
-<dtml-var standard_html_header>
-
-<dtml-var TABLE_TYPE><dtml-if TABLE_OWNER>
- owned by <dtml-var TABLE_OWNER></dtml-if>
-<dtml-if REMARKS><br><dtml-var REMARKS></dtml-if>
-
-<dtml-var standard_html_footer>
diff --git a/ZPsycopgDA/icons/DBAdapterFolder_icon.gif b/ZPsycopgDA/icons/DBAdapterFolder_icon.gif
deleted file mode 100755
index ced0ef2..0000000
--- a/ZPsycopgDA/icons/DBAdapterFolder_icon.gif
+++ /dev/null
Binary files differ
diff --git a/ZPsycopgDA/icons/bin.gif b/ZPsycopgDA/icons/bin.gif
deleted file mode 100644
index e469126..0000000
--- a/ZPsycopgDA/icons/bin.gif
+++ /dev/null
Binary files differ
diff --git a/ZPsycopgDA/icons/date.gif b/ZPsycopgDA/icons/date.gif
deleted file mode 100644
index 0d88a57..0000000
--- a/ZPsycopgDA/icons/date.gif
+++ /dev/null
Binary files differ
diff --git a/ZPsycopgDA/icons/datetime.gif b/ZPsycopgDA/icons/datetime.gif
deleted file mode 100644
index faa540b..0000000
--- a/ZPsycopgDA/icons/datetime.gif
+++ /dev/null
Binary files differ
diff --git a/ZPsycopgDA/icons/field.gif b/ZPsycopgDA/icons/field.gif
deleted file mode 100644
index 9bf8692..0000000
--- a/ZPsycopgDA/icons/field.gif
+++ /dev/null
Binary files differ
diff --git a/ZPsycopgDA/icons/float.gif b/ZPsycopgDA/icons/float.gif
deleted file mode 100644
index dd42729..0000000
--- a/ZPsycopgDA/icons/float.gif
+++ /dev/null
Binary files differ
diff --git a/ZPsycopgDA/icons/int.gif b/ZPsycopgDA/icons/int.gif
deleted file mode 100644
index ef2c5e3..0000000
--- a/ZPsycopgDA/icons/int.gif
+++ /dev/null
Binary files differ
diff --git a/ZPsycopgDA/icons/stable.gif b/ZPsycopgDA/icons/stable.gif
deleted file mode 100644
index acdd37d..0000000
--- a/ZPsycopgDA/icons/stable.gif
+++ /dev/null
Binary files differ
diff --git a/ZPsycopgDA/icons/table.gif b/ZPsycopgDA/icons/table.gif
deleted file mode 100644
index cce83be..0000000
--- a/ZPsycopgDA/icons/table.gif
+++ /dev/null
Binary files differ
diff --git a/ZPsycopgDA/icons/text.gif b/ZPsycopgDA/icons/text.gif
deleted file mode 100644
index a2e5aab..0000000
--- a/ZPsycopgDA/icons/text.gif
+++ /dev/null
Binary files differ
diff --git a/ZPsycopgDA/icons/time.gif b/ZPsycopgDA/icons/time.gif
deleted file mode 100644
index 6d08915..0000000
--- a/ZPsycopgDA/icons/time.gif
+++ /dev/null
Binary files differ
diff --git a/ZPsycopgDA/icons/view.gif b/ZPsycopgDA/icons/view.gif
deleted file mode 100644
index 71b30de..0000000
--- a/ZPsycopgDA/icons/view.gif
+++ /dev/null
Binary files differ
diff --git a/ZPsycopgDA/icons/what.gif b/ZPsycopgDA/icons/what.gif
deleted file mode 100644
index 8b5516e..0000000
--- a/ZPsycopgDA/icons/what.gif
+++ /dev/null
Binary files differ
diff --git a/ZPsycopgDA/pool.py b/ZPsycopgDA/pool.py
deleted file mode 100644
index b47f46c..0000000
--- a/ZPsycopgDA/pool.py
+++ /dev/null
@@ -1,193 +0,0 @@
-# ZPsycopgDA/pool.py - ZPsycopgDA Zope product: connection pooling
-#
-# Copyright (C) 2004-2010 Federico Di Gregorio <fog@debian.org>
-#
-# 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 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.
-
-import threading
-import psycopg2
-from psycopg2.pool import PoolError
-
-
-class AbstractConnectionPool(object):
- """Generic key-based pooling code."""
-
- def __init__(self, minconn, maxconn, *args, **kwargs):
- """Initialize the connection pool.
-
- New 'minconn' connections are created immediately calling 'connfunc'
- with given parameters. The connection pool will support a maximum of
- about 'maxconn' connections.
- """
- self.minconn = minconn
- self.maxconn = maxconn
- self.closed = False
-
- self._args = args
- self._kwargs = kwargs
-
- self._pool = []
- self._used = {}
- self._rused = {} # id(conn) -> key map
- self._keys = 0
-
- for i in range(self.minconn):
- self._connect()
-
- def _connect(self, key=None):
- """Create a new connection and assign it to 'key' if not None."""
- conn = psycopg2.connect(*self._args, **self._kwargs)
- if key is not None:
- self._used[key] = conn
- self._rused[id(conn)] = key
- else:
- self._pool.append(conn)
- return conn
-
- def _getkey(self):
- """Return a new unique key."""
- self._keys += 1
- return self._keys
-
- def _getconn(self, key=None):
- """Get a free connection and assign it to 'key' if not None."""
- if self.closed: raise PoolError("connection pool is closed")
- if key is None: key = self._getkey()
-
- if key in self._used:
- return self._used[key]
-
- if self._pool:
- self._used[key] = conn = self._pool.pop()
- self._rused[id(conn)] = key
- return conn
- else:
- if len(self._used) == self.maxconn:
- raise PoolError("connection pool exausted")
- return self._connect(key)
-
- 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 not key:
- raise PoolError("trying to put unkeyed connection")
-
- if len(self._pool) < self.minconn and not close:
- self._pool.append(conn)
- else:
- conn.close()
-
- # here we check for the presence of key because it can happen that a
- # thread tries to put back a connection after a call to close
- if not self.closed or key in self._used:
- del self._used[key]
- del self._rused[id(conn)]
-
- def _closeall(self):
- """Close all connections.
-
- Note that this can lead to some code fail badly when trying to use
- an already closed connection. If you call .closeall() make sure
- your code can deal with it.
- """
- if self.closed: raise PoolError("connection pool is closed")
- for conn in self._pool + list(self._used.values()):
- try:
- conn.close()
- except:
- pass
- self.closed = True
-
-
-class PersistentConnectionPool(AbstractConnectionPool):
- """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.
- """
-
- def __init__(self, minconn, maxconn, *args, **kwargs):
- """Initialize the threading lock."""
- import threading
- AbstractConnectionPool.__init__(
- self, minconn, maxconn, *args, **kwargs)
- self._lock = threading.Lock()
-
- # we we'll need the thread module, to determine thread ids, so we
- # import it here and copy it in an instance variable
- import thread
- self.__thread = thread
-
- def getconn(self):
- """Generate thread id and return a connection."""
- key = self.__thread.get_ident()
- self._lock.acquire()
- try:
- return self._getconn(key)
- finally:
- self._lock.release()
-
- def putconn(self, conn=None, close=False):
- """Put away an unused connection."""
- key = self.__thread.get_ident()
- self._lock.acquire()
- try:
- if not conn: conn = self._used[key]
- self._putconn(conn, key, close)
- finally:
- self._lock.release()
-
- def closeall(self):
- """Close all connections (even the one currently in use.)"""
- self._lock.acquire()
- try:
- self._closeall()
- finally:
- self._lock.release()
-
-
-_connections_pool = {}
-_connections_lock = threading.Lock()
-
-def getpool(dsn, create=True):
- _connections_lock.acquire()
- try:
- if not _connections_pool.has_key(dsn) and create:
- _connections_pool[dsn] = \
- PersistentConnectionPool(4, 200, dsn)
- finally:
- _connections_lock.release()
- return _connections_pool[dsn]
-
-def flushpool(dsn):
- _connections_lock.acquire()
- try:
- _connections_pool[dsn].closeall()
- del _connections_pool[dsn]
- finally:
- _connections_lock.release()
-
-def getconn(dsn, create=True):
- return getpool(dsn, create=create).getconn()
-
-def putconn(dsn, conn, close=False):
- getpool(dsn).putconn(conn, close=close)
diff --git a/debian/README.zpsycopgda2 b/debian/README.zpsycopgda2
deleted file mode 100644
index 53028f7..0000000
--- a/debian/README.zpsycopgda2
+++ /dev/null
@@ -1,7 +0,0 @@
-ZPsycopgDA (in the Debian zope-psycopgda package) is a Zope Database
-Adapter based on the psycopg Python/PostgreSQL driver. You'll find
-more information and documentation in the pythonX.Y-psycopg package,
-where X.Y is the version of your installed Python.
-
-Details for ZPsycopgDA for Zope are found in the documentation of
-the python2.3-psycopg package.
diff --git a/debian/changelog b/debian/changelog
deleted file mode 100644
index c7abebf..0000000
--- a/debian/changelog
+++ /dev/null
@@ -1,676 +0,0 @@
-psycopg2 (2.0.7-2) unstable; urgency=medium
-
- * ZPsycopgDA/DA.py: updated the patch. (Closes: #478860)
-
- -- Fabio Tranchitella <kobold@debian.org> Thu, 01 May 2008 17:43:54 +0200
-
-psycopg2 (2.0.7-1) unstable; urgency=low
-
- * New upstream release. (Closes: #476101)
- * debian/control: bumped Standard-Versions to 3.7.3.
-
- -- Fabio Tranchitella <kobold@debian.org> Tue, 15 Apr 2008 10:05:51 +0200
-
-psycopg2 (2.0.6-4) unstable; urgency=low
-
- [ Sandro Tosi ]
- * debian/control
- - uniforming Vcs-Browser field
-
- [ Fabio Tranchitella ]
- * Provides a encoding parameter when adding a ZPsycopgDA instance using the
- ZMI. (Closes: #475123)
-
- -- Fabio Tranchitella <kobold@debian.org> Wed, 09 Apr 2008 19:51:10 +0200
-
-psycopg2 (2.0.6-3) unstable; urgency=low
-
- [ Piotr Ożarowski ]
- * XS-Vcs-Svn field renamed to Vcs-Svn
- * Vcs-Browser field added
-
- [ Fabio Tranchitella ]
- * Mention DB-API 2.0 compatibility in the long description.
- (Closes: #430763)
-
- -- Fabio Tranchitella <kobold@debian.org> Thu, 08 Nov 2007 15:07:05 +0100
-
-psycopg2 (2.0.6-2) unstable; urgency=low
-
- * Build a python-psycopg-dbg package
-
- -- Scott Kitterman <scott@kitterman.com> Tue, 03 Jul 2007 16:55:48 -0400
-
-psycopg2 (2.0.6-1) unstable; urgency=low
-
- * New upstream release.
- * psycopg2da: removed, merged upstream.
-
- -- Fabio Tranchitella <kobold@debian.org> Sat, 09 Jun 2007 22:38:23 +0200
-
-psycopg2 (2.0.5.1-7) UNRELEASED; urgency=low
-
- * debian/watch: added.
-
- -- Fabio Tranchitella <kobold@debian.org> Fri, 9 Feb 2007 12:35:55 +0100
-
-psycopg2 (2.0.5.1-6) unstable; urgency=high
-
- * debian/zope-psycopgda2.dzproduct: requires Zope 2.9 or higher: previous
- versions use python2.3 which is not supported anymore in psycopg.
-
- -- Fabio Tranchitella <kobold@debian.org> Mon, 15 Jan 2007 11:39:15 +0100
-
-psycopg2 (2.0.5.1-5) unstable; urgency=medium
-
- * Do not run dh_pycentral on zope-psycopgda2. (Closes: #400846)
-
- -- Fabio Tranchitella <kobold@debian.org> Wed, 29 Nov 2006 09:04:09 +0100
-
-psycopg2 (2.0.5.1-4) unstable; urgency=medium
-
- * Fixed a bug in psycopg2da.
- * debian/control: bumped build-dependency on zope-debhelper.
- * Added XS-Vcs-Svn field
-
- -- Fabio Tranchitella <kobold@debian.org> Fri, 24 Nov 2006 13:50:11 +0100
-
-psycopg2 (2.0.5.1-3) unstable; urgency=low
-
- * psycopgda: imported upstream psycopg2da database adapter from SVN, which
- builds a new binary package, python-psycopg2da.
-
- -- Fabio Tranchitella <kobold@debian.org> Fri, 10 Nov 2006 08:56:05 +0100
-
-psycopg2 (2.0.5.1-2) unstable; urgency=low
-
- * debian/control: added again a dependency on python-egenix-mxdatetime.
- (Closes: #389636)
-
- -- Fabio Tranchitella <kobold@debian.org> Tue, 3 Oct 2006 10:25:22 +0200
-
-psycopg2 (2.0.5.1-1) unstable; urgency=low
-
- * New upstream release.
-
- -- Fabio Tranchitella <kobold@debian.org> Tue, 19 Sep 2006 08:22:36 +0200
-
-psycopg2 (2.0.4-1) unstable; urgency=low
-
- * New upstream release.
- * debian/control:
- + removed dependency on python-egenix-mxdatetime.
- + added ${shlibs:Depends} for the python-psycopg2 package.
- (Closes: #381462)
-
- -- Fabio Tranchitella <kobold@debian.org> Wed, 9 Aug 2006 10:28:30 +0200
-
-psycopg2 (2.0.2-1) unstable; urgency=low
-
- * New upstream major release, new source package. (Closes: #377956)
-
- -- Fabio Tranchitella <kobold@debian.org> Sun, 16 Jul 2006 21:43:41 +0200
-
-psycopg (1.1.21-8) unstable; urgency=low
-
- * debian/zope-psycopgda.dzproduct: added 2.9 to the list of supported
- zope versions. (Closes: #376538)
-
- -- Fabio Tranchitella <kobold@debian.org> Fri, 14 Jul 2006 10:19:54 +0200
-
-psycopg (1.1.21-7) unstable; urgency=low
-
- * Moved dh_installzope within an arch-indep target. (Closes: #373842)
-
- -- Fabio Tranchitella <kobold@debian.org> Fri, 16 Jun 2006 09:37:23 +0200
-
-psycopg (1.1.21-6) unstable; urgency=low
-
- * Python policy transition. (Closes: #373482)
-
- -- Fabio Tranchitella <kobold@debian.org> Thu, 15 Jun 2006 19:09:36 +0200
-
-psycopg (1.1.21-5) unstable; urgency=high
-
- * ypemod.c, new_psyco_bufferobject():
- - Escape quotes psycopg.Binary() results as '', not as \', since the
- latter does not work any more with some client encodings with the latest
- PostgreSQL (in some multi-byte encodings you can exploit \' escaping to
- inject SQL code, see CVE-2006-2314). (Closes: #369230)
- Thanks to Martin Pitt and Ubuntu security team for the patch.
-
- -- Fabio Tranchitella <kobold@debian.org> Tue, 30 May 2006 22:15:06 +0200
-
-psycopg (1.1.21-4) unstable; urgency=low
-
- * debian/rules: remove *.o in the clean target. (Closes: #352835)
-
- -- Fabio Tranchitella <kobold@debian.org> Thu, 16 Feb 2006 12:06:53 +0000
-
-psycopg (1.1.21-3) unstable; urgency=low
-
- * debian/control: removed build-dependency on postgresql-server-dev-8.0,
- as suggested by Martin Pitt. (Closes: #339640)
-
- -- Fabio Tranchitella <kobold@debian.org> Fri, 18 Nov 2005 08:44:26 +0000
-
-psycopg (1.1.21-2) unstable; urgency=low
-
- * debian/control: zope-psycopgda should depend on the same version of the
- psycopg python module. (Closes: #336765)
-
- -- Fabio Tranchitella <kobold@debian.org> Wed, 2 Nov 2005 12:07:33 +0000
-
-psycopg (1.1.21-1) unstable; urgency=low
-
- * New maintainer; Thanks Federico for your work, and be sure that I'll
- take care of this package.
- * New upstream release (Closes: #321592, #320618, #333638)
- * debian/python2.4-psycopg.dirs: added. (Closes: #319509, #329115)
- * debian/control: dropped support for python2.1 and
- python2.2. (Closes: #333639)
- * debian/control: Standards-Version bumped to 3.6.2, no changes required.
- * debian/rules: make use of dh_installzope from zope-debhelper to build the
- zope-psycopgda package.
- (Closes: #158669, #323599, #268975, #292247, #327415)
- * debian/control: added build-depends on postgresql-server-dev-8.0.
- (Closes: #333638)
- * Re-packaged upstream tarball replacing some broken images.
- (Closes: #292008, #305392)
-
- -- Fabio Tranchitella <kobold@debian.org> Fri, 28 Oct 2005 11:24:37 +0000
-
-psycopg (1.1.19-1) unstable; urgency=low
-
- * New upstream release.
- * Applied patch from Martin Krafft to build Zope 2.7 packages.
- * Modified to use the new PostgreSQL packages.
- * Added python 2.4 package (Closes: #301403).
- * Upstream applied various Ubuntu patches (Closes: #301947, #300893).
-
- -- Federico Di Gregorio <fog@debian.org> Sat, 16 Jul 2005 20:47:08 +0200
-
-psycopg (1.1.18-1) unstable; urgency=low
-
- * New upstream release.
- * 1.1.16 fixed rowcount bug (closes: #266299).
-
- -- Federico Di Gregorio <fog@debian.org> Wed, 5 Jan 2005 21:05:15 +0100
-
-psycopg (1.1.17-1) unstable; urgency=high
-
- * Urgency is still high because 1.1.16 was never uploaded.
- * New upstream release.
-
- -- Federico Di Gregorio <fog@debian.org> Thu, 19 Nov 2004 01:14:30 +0200
-
-psycopg (1.1.16-1) unstable; urgency=high
-
- * New upstream release.
- * Tagged with urgency=high because fix a grave bug (rowcount) introduced
- in 1.1.15.
- * Upstream fix: does not segfault when using COPY TO/COPY FROM in
- .execute() (closes: #279222).
-
- -- Federico Di Gregorio <fog@debian.org> Sat, 30 Oct 2004 02:35:30 +0200
-
-psycopg (1.1.15-1) unstable; urgency=low
-
- * New upstream release.
- * Definitely fixed (ah ah) time interval problems (closes: #259213).
-
- -- Federico Di Gregorio <fog@initd.org> Thu, 29 Jul 2004 23:43:59 +0200
-
-psycopg (1.1.14-1) unstable; urgency=low
-
- * New upstream release.
- * Don't put two copies of changelog in every package anymore
- (closes: #256662).
- * Updated test script works as expected (closes: #231391).
- * Changes from NMU incorporated in 1.1.12:
- - zpsycopgda depends on python2.2-psycopg (closes: #227420, #227147).
- - compiled with postgresql in unstable (close: #220527).
-
- -- Federico Di Gregorio <fog@initd.org> Fri, 9 Jul 2004 23:01:40 +0200
-
-psycopg (1.1.13-1) unstable; urgency=low
-
- * New upstream release.
-
- -- Federico Di Gregorio <fog@debian.org> Fri, 21 May 2004 10:33:54 +0200
-
-psycopg (1.1.12-1) unstable; urgency=low
-
- * New upstream release (the "martin you won't have this package"
- release.)
- * Integrated changes from NMU releases.
-
- -- Federico Di Gregorio <fog@debian.org> Sun, 16 May 2004 10:14:47 +0200
-
-psycopg (1.1.10-1.2) unstable; urgency=low
-
- * Non-maintainer upload. Thinking about taking this package over...
- * Changed dependency on pyscopgda Python module to Python version 2.2.
- (closes: #227147, #227420)
- * Added Lintian overrides for image-in-/usr/lib warnings -- Zope needs
- these images...
-
- -- martin f. krafft <madduck@debian.org> Thu, 15 Apr 2004 23:30:40 +0200
-
-psycopg (1.1.10-1.1) unstable; urgency=low
-
- * Non-maintainer upload.
- * No changes - this upload is simply a rebuild against the current unstable
- instead of experimental postgresql-dev.
- (closes: #219927, #220141, #220173, #220527)
-
- -- Peter Hawkins <peterh@debian.org> Sun, 28 Dec 2003 10:57:30 +1100
-
-psycopg (1.1.10-1) unstable; urgency=low
-
- * Added download location to debian/copyright file (Closes: #215880).
-
- -- Federico Di Gregorio <fog@initd.org> Sat, 8 Nov 2003 23:32:40 +0100
-
-psycopg (1.1.9-1) unstable; urgency=low
-
- * New upstream release.
- * Bug was agains an old 1.0.x version of psycopg (Closes: #208702).
-
- -- Federico Di Gregorio <fog@initd.org> Wed, 10 Sep 2003 13:04:42 +0200
-
-psycopg (1.1.8-1) unstable; urgency=low
-
- * New upstream release.
- * Integrated NMU from Matthias Klose (closes: #205746).
-
- -- Federico Di Gregorio <fog@debian.org> Fri, 1 Aug 2003 11:50:57 +0200
-
-psycopg (1.1.5.1-1.1) unstable; urgency=low
-
- * NMU
- * Update for python2.3 as the default python version (closes: #205746).
-
- -- Matthias Klose <doko@debian.org> Fri, 22 Aug 2003 00:02:25 +0200
-
-psycopg (1.1.7-1) unstable; urgency=low
-
- * New upstream release.
-
- -- Federico Di Gregorio <fog@debian.org> Sat, 26 Jul 2003 15:03:39 +0200
-
-psycopg (1.1.6-1) unstable; urgency=low
-
- * New upstream release.
- * Upstream applied patch from BTS (Closes: #200161).
-
- -- Federico Di Gregorio <fog@initd.org> Sun, 13 Jul 2003 23:36:04 +0200
-
-psycopg (1.1.5.1-1) unstable; urgency=low
-
- * New upstream release.
-
- -- Federico Di Gregorio <fog@initd.org> Mon, 23 Jun 2003 00:37:33 +0200
-
-psycopg (1.1.5-1) unstable; urgency=low
-
- * New upstream release.
-
- -- Federico Di Gregorio <fog@initd.org> Sun, 22 Jun 2003 21:30:01 +0200
-
-psycopg (1.1.4-1) unstable; urgency=low
-
- * New upstream release.
-
- -- Federico Di Gregorio <fog@debian.org> Wed, 7 May 2003 15:21:31 +0200
-
-psycopg (1.1.3-1) unstable; urgency=low
-
- * New upstream release.
- * Changed section in debian/control (-> python).
-
- -- Federico Di Gregorio <fog@debian.org> Wed, 2 Apr 2003 10:33:36 +0200
-
-psycopg (1.1.2-1) unstable; urgency=low
-
- * New upstream release.
- * Started to track the 1.1.x branch.
-
- -- Federico Di Gregorio <fog@debian.org> Tue, 25 Feb 2003 01:06:08 +0100
-
-psycopg (1.0.15.1-1) unstable; urgency=low
-
- * New upstream release.
-
- -- Federico Di Gregorio <fog@debian.org> Fri, 14 Feb 2003 16:09:50 +0100
-
-psycopg (1.0.15-1) unstable; urgency=low
-
- * New upstream release.
-
- -- Federico Di Gregorio <fog@debian.org> Wed, 12 Feb 2003 23:49:51 +0100
-
-psycopg (1.0.14-1) unstable; urgency=low
-
- * Applied patch from John Goerzen to fix memory leak in executemany()
- and callproc() (Closes: #169284).
- * New upstream release (Closes: #170297).
-
- -- Federico Di Gregorio <fog@debian.org> Mon, 25 Nov 2002 16:50:37 +0100
-
-psycopg (1.0.13-1) unstable; urgency=low
-
- * New upstream release.
- * Python 2.3 package added (Closes: #160831)
- * IntegrityError raised when needed (upstream, Closes: #165791)
- * Packages are lintian clean again.
-
- -- Federico Di Gregorio <fog@debian.org> Fri, 25 Oct 2002 11:54:19 +0200
-
-psycopg (1.0.12-1) unstable; urgency=low
-
- * New upstream release.
- * Fixed wrong url in RELEASE-1.0. (Closes: #153840)
-
- -- Federico Di Gregorio <fog@debian.org> Fri, 13 Sep 2002 13:16:36 +0200
-
-psycopg (1.0.11.1-1) unstable; urgency=low
-
- * New upstream release.
-
- -- Federico Di Gregorio <fog@debian.org> Mon, 26 Aug 2002 10:41:54 +0200
-
-psycopg (1.0.11-1) unstable; urgency=low
-
- * New upstream release.
- * The dummy python-psycopg package now depends on the new default debian
- python (2.2) and on python2.2-psycopg.
- * Removed support for python 1.5 (support for 2.3 has to wait for egenix
- packages.)
-
- -- Federico Di Gregorio <fog@debian.org> Fri, 23 Aug 2002 11:25:01 +0200
-
-psycopg (1.0.10-1) unstable; urgency=low
-
- * New upstream release.
-
- -- Federico Di Gregorio <fog@initd.org> Mon, 22 Jul 2002 02:04:59 +0200
-
-psycopg (1.0.9-1) unstable; urgency=low
-
- * Resolved section override (main->interpreters).
- * New upstream release.
-
- -- Federico Di Gregorio <fog@debian.org> Thu, 20 Jun 2002 14:00:42 +0200
-
-psycopg (1.0.8-1) unstable; urgency=low
-
- * New upstream release.
-
- -- Federico Di Gregorio <fog@debian.org> Tue, 23 Apr 2002 22:42:22 +0200
-
-psycopg (1.0.7.1-2) unstable; urgency=low
-
- * Moved to main.
-
- -- Federico Di Gregorio <fog@debian.org> Fri, 19 Apr 2002 10:06:58 +0200
-
-psycopg (1.0.7.1-1) unstable; urgency=low
-
- * New upstream release.
- * Fixed a bug in ./configure; closes: #141774.
-
- -- Federico Di Gregorio <fog@debian.org> Mon, 8 Apr 2002 18:54:24 +0200
-
-psycopg (1.0.7-1) unstable; urgency=low
-
- * New upstream release.
-
- -- Federico Di Gregorio <fog@debian.org> Fri, 29 Mar 2002 14:24:45 +0100
-
-psycopg (1.0.6-1) unstable; urgency=low
-
- * New upstream release.
- * Builds with new libpq libraries and header layout.
-
- -- Federico Di Gregorio <fog@debian.org> Thu, 7 Mar 2002 11:59:40 +0100
-
-psycopg (1.0.5-1) unstable; urgency=low
-
- * New upstream release.
-
- -- Federico Di Gregorio <fog@debian.org> Mon, 4 Mar 2002 14:43:13 +0100
-
-psycopg (1.0.4-1) unstable; urgency=low
-
- * New upstream release.
-
- -- Federico Di Gregorio <fog@debian.org> Wed, 20 Feb 2002 20:37:16 +0100
-
-psycopg (1.0.3-1) unstable; urgency=low
-
- * New upstream release.
-
- -- Federico Di Gregorio <fog@debian.org> Fri, 8 Feb 2002 15:17:44 +0100
-
-psycopg (1.0.2-1) unstable; urgency=low
-
- * New upstream release.
- * Added package for python2.2 (Closes: #132650).
-
- -- Federico Di Gregorio <fog@debian.org> Fri, 8 Feb 2002 00:45:07 +0100
-
-psycopg (1.0.1-1) unstable; urgency=low
-
- * New upstream release.
-
- -- Federico Di Gregorio <fog@debian.org> Sun, 20 Jan 2002 18:27:22 +0100
-
-psycopg (1.0-4) unstable; urgency=low
-
- * Added build depend on plain python, to really close the %£$! #121229
- bug this time (Closes: #121229).
-
- -- Federico Di Gregorio <fog@debian.org> Wed, 28 Nov 2001 10:50:06 +0100
-
-psycopg (1.0-3) unstable; urgency=low
-
- * Added explicit build depends on python 1.5 & 2.1 (Closes: #121229).
- * Fixed bad dependency on python1.5-egenix-mxdatetime.
-
- -- Federico Di Gregorio <fog@debian.org> Mon, 26 Nov 2001 17:18:41 +0100
-
-psycopg (1.0-2) unstable; urgency=low
-
- * Fixed dependencies as per python policy.
- * Added default, unversioned psycopg package (python-psycopg).
- * Added non-US/main and rebuilt after REJECT.
-
- -- Federico Di Gregorio <fog@debian.org> Fri, 16 Nov 2001 01:14:54 +0100
-
-psycopg (1.0-1) unstable; urgency=low
-
- * New upstream release. 1.0!
- * Now we build versioned packages for python 1.5 and 2.1.
-
- -- Federico Di Gregorio <fog@debian.org> Tue, 13 Nov 2001 19:24:39 +0100
-
-psycopg (0.99.7-1) unstable; urgency=low
-
- * New upstream release fixing some little bugs.
- * This version requires the mx DateTime packages that are not yet in
- debian... waiting for them I'll distribute both psycopg and unofficial
- packages on the initd psycopg page.
-
- -- Federico Di Gregorio <fog@debian.org> Tue, 18 Sep 2001 23:28:51 +0200
-
-psycopg (0.99.6-2) unstable; urgency=low
-
- * Added suggested build-depends (Closes: #112112).
- * Applied patch by Michael Weber to configure.in, to look for a compiler
- (Closes: #112024).
-
- -- Federico Di Gregorio <fog@debian.org> Thu, 13 Sep 2001 10:49:37 +0200
-
-psycopg (0.99.6-1) unstable; urgency=low
-
- * Added Build-depends line (Closes: #89798).
- * Now zope-psycopgda requires python-psycopg, zope on debian still runs
- with python 1.x only (Closes: #108890).
- * Moved package to non-US (psycopg depends on postgresql that is in
- non-US, sic).
-
- -- Federico Di Gregorio <fog@debian.org> Mon, 3 Sep 2001 13:02:11 +0200
-
-psycopg (0.99.5-1) unstable; urgency=low
-
- * New upstream release with bound variables quoting (Closes: #102843).
- * The build process set the correct path to DateTime module
- (Closes: #102838).
- * Removes .pyc files in prerm (Closes: #104382)
-
- -- Federico Di Gregorio <fog@debian.org> Thu, 12 Jul 2001 12:56:38 +0200
-
-psycopg (0.99.4-1) unstable; urgency=low
-
- * New upstream release.
-
- -- Federico Di Gregorio <fog@debian.org> Mon, 2 Jul 2001 15:33:29 +0200
-
-psycopg (0.99.3-1) unstable; urgency=low
-
- * New upstream release.
-
- -- Federico Di Gregorio <fog@debian.org> Wed, 20 Jun 2001 12:55:47 +0200
-
-psycopg (0.99.2-1) unstable; urgency=low
-
- * New upstream release.
-
- -- Federico Di Gregorio <fog@debian.org> Tue, 5 Jun 2001 15:37:50 +0200
-
-psycopg (0.99.1-1) unstable; urgency=low
-
- * New upstream release.
-
- -- Federico Di Gregorio <fog@debian.org> Tue, 5 Jun 2001 12:46:18 +0200
-
-psycopg (0.5.5-1) unstable; urgency=low
-
- * New upstream release (ok, *we* are the upstream authors, but after
- putting the -1 in the version i am supposed to say "new upstream
- version" when the non-debian versions changes, right? ouch...)
-
- -- Federico Di Gregorio <fog@debian.org> Fri, 1 Jun 2001 17:18:52 +0200
-
-psycopg (0.5.4-1) unstable; urgency=low
-
- * Another bugfixing release.
- * Added debian revision to be able to release multiple versions with the
- same upstream version.
-
- -- Federico Di Gregorio <fog@debian.org> Fri, 18 May 2001 19:32:59 +0200
-
-psycopg (0.5.3) unstable; urgency=low
-
- * Some bugs fixed, new release.
-
- -- Federico Di Gregorio <fog@debian.org> Fri, 4 May 2001 16:19:09 +0200
-
-psycopg (0.5.2) unstable; urgency=low
-
- * New bugfixing release.
-
- -- Federico Di Gregorio <fog@debian.org> Fri, 27 Apr 2001 09:52:16 +0200
-
-psycopg (0.5.1) unstable; urgency=low
-
- * New bugfixing release.
-
- -- Federico Di Gregorio <fog@debian.org> Tue, 3 Apr 2001 11:13:26 +0200
-
-psycopg (0.5.0) unstable; urgency=low
-
- * New release.
-
- -- Federico Di Gregorio <fog@debian.org> Fri, 30 Mar 2001 12:54:42 +0200
-
-psycopg (0.4.7) unstable; urgency=low
-
- * New release.
- * Lots of small bug fixes (see detailed ChangeLog.)
- * Includes beginning of DBAPI-2.0 testsuite.
-
- -- Federico Di Gregorio <fog@debian.org> Fri, 16 Mar 2001 18:29:03 +0100
-
-psycopg (0.4.6) unstable; urgency=low
-
- * New release.
- * Fixed a little bug in debian/rules (does not create an examples
- directory inside examples.)
-
- -- Federico Di Gregorio <fog@debian.org> Wed, 14 Mar 2001 01:00:26 +0100
-
-psycopg (0.4.5) unstable; urgency=low
-
- * New upstream (mmm... but one of the upstream authors it is
- *me*... mmm...) release.
-
- -- Federico Di Gregorio <fog@debian.org> Mon, 12 Mar 2001 11:41:42 +0100
-
-psycopg (0.4.4) unstable; urgency=low
-
- * New release.
- * Fixed Sections in debian/control.
-
- -- Federico Di Gregorio <fog@debian.org> Fri, 9 Mar 2001 10:11:02 +0100
-
-psycopg (0.4.3) unstable; urgency=low
-
- * New release.
- * Fixed typo in connectionAdd.dtml (Closes: #88817)
-
- -- Federico Di Gregorio <fog@debian.org> Wed, 7 Mar 2001 15:54:35 +0100
-
-psycopg (0.4.2) unstable; urgency=low
-
- * New release (fixes bugs in ZPsycopgDA.)
-
- -- Federico Di Gregorio <fog@debian.org> Mon, 5 Mar 2001 13:33:39 +0100
-
-psycopg (0.4.1) unstable; urgency=low
-
- * New release.
- * we now create packages for both versions of python in debian
- (1.5 and 2.0, packages python-* and python2-*)
-
- -- Federico Di Gregorio <fog@debian.org> Fri, 2 Mar 2001 12:10:52 +0100
-
-psycopg (0.4) unstable; urgency=low
-
- * News release.
- * Now debian/rules build the Zope Database Adapter zope-psycopgda too.
- * Source name changed from python-psycopg to psycopg.
-
- -- Federico Di Gregorio <fog@debian.org> Tue, 27 Feb 2001 15:11:04 +0100
-
-python-psycopg (0.3) unstable; urgency=low
-
- * New release. Tons of bugs fixed and new features, see ChangeLog for
- details.
-
- -- Federico Di Gregorio <fog@debian.org> Mon, 26 Feb 2001 21:22:23 +0100
-
-python-psycopg (0.2) unstable; urgency=low
-
- * New release. Fixed lots of bugs and memory leaks.
-
- -- Federico Di Gregorio <fog@debian.org> Fri, 16 Feb 2001 11:04:17 +0100
-
-python-psycopg (0.1) unstable; urgency=low
-
- * Initial release.
-
- -- Federico Di Gregorio <fog@debian.org> Mon, 12 Feb 2001 14:46:53 +0100
-
-
diff --git a/debian/compat b/debian/compat
deleted file mode 100644
index 7ed6ff8..0000000
--- a/debian/compat
+++ /dev/null
@@ -1 +0,0 @@
-5
diff --git a/debian/control b/debian/control
deleted file mode 100644
index 7651de5..0000000
--- a/debian/control
+++ /dev/null
@@ -1,64 +0,0 @@
-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)
-Maintainer: Fabio Tranchitella <kobold@debian.org>
-Standards-Version: 3.7.3
-XS-Python-Version: all
-Vcs-Svn: svn://svn.debian.org/python-modules/packages/psycopg2/trunk/
-Vcs-Browser: http://svn.debian.org/wsvn/python-modules/packages/psycopg2/trunk/?op=log
-
-Package: python-psycopg2
-Architecture: any
-Section: python
-Depends: ${python:Depends}, ${shlibs:Depends}, python-egenix-mxdatetime
-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
- 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 asycronous 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: python-psycopg2-dbg
-Priority: extra
-Architecture: any
-Section: python
-Depends: python-psycopg2 (= ${binary:Version}), python-dbg, python-egenix-mxdatetime-dbg, ${shlibs: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
- 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: zope-psycopgda2
-Architecture: all
-Section: python
-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: python-psycopg2da
-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.
diff --git a/debian/copyright b/debian/copyright
deleted file mode 100644
index 0f2211d..0000000
--- a/debian/copyright
+++ /dev/null
@@ -1,112 +0,0 @@
-This package was debianized by Fabio Tranchitella <kobold@debian.org> on
-Sun, 16 Jul 2006 21:10:01 +0200.
-
-psycopg2 can be downloaded from its homepage:
-
- http://initd.org/projects/psycopg
-
-The tarball has been re-packed to get rid of the upstream debian/ directory:
-no other changes have been made to the tarball.
-
-
-Copyright:
-
- Copyright (C) 2001-2006 Federico Di Gregorio <fog@debian.org>
- Copyright (C) 2001 Michele Comitini <mcm@initd.org>
-
- For psycopg2da:
- Copyright (C) 2006 Fabio Tranchitella <kobold@debian.org>
-
- For the files doc/copy_from.py and doc/copy_to.py:
- Copyright (C) 2001-2005 Federico Di Gregorio <fog@debian.org>
- Copyright (C) 2002 Tom Jenkins <tjenkins@devis.com>
-
- For the file tests/dbapi20.py:
- Copyright (C) 2003 Ian Bicking <ianb@colorstudy.com>
-
- For the file scripts/ext2html.py:
- Copyright (C) 2003 Daniele Varrazzo <daniele.varrazzo@gmail.com>
-
-
-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:
-
- 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.)
-
-
-License for the files tests/dbapi20.py and scripts/ext2html.py:
-
- These modules have been placed in the public domain.
-
-
-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.
-
-
-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.
-
-
-Alternative licenses for psycopg/adapter*.{j,c} and
-psycopg/microprotocol*.{h.c}:
-
- 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.
-
-
-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.
-
- Please contact the upstream author (Federico Di Gregorio <fog@debian.org>)
- for more information about this license.
-
diff --git a/debian/pycompat b/debian/pycompat
deleted file mode 100644
index 0cfbf08..0000000
--- a/debian/pycompat
+++ /dev/null
@@ -1 +0,0 @@
-2
diff --git a/debian/python-psycopg2da.dzproduct b/debian/python-psycopg2da.dzproduct
deleted file mode 100644
index d7d9206..0000000
--- a/debian/python-psycopg2da.dzproduct
+++ /dev/null
@@ -1,3 +0,0 @@
-Name: psycopg2da
-ZopeVersions: 3
-Global: yes
diff --git a/debian/rules b/debian/rules
deleted file mode 100755
index f33dd46..0000000
--- a/debian/rules
+++ /dev/null
@@ -1,93 +0,0 @@
-#!/usr/bin/make -f
-# Sample debian/rules that uses debhelper.
-# GNU copyright 1997 to 1999 by Joey Hess.
-
-PYVERS=$(shell pyversions -r debian/control)
-
-configure: configure-stamp
-configure-stamp:
- dh_testdir
- rm -f configure
- touch configure-stamp
-
-build: configure build-stamp
-build-stamp:
- dh_testdir
- for python in $(PYVERS); do \
- $$python setup.py build ; \
- done
- for python in $(PYVERS); do \
- $$python-dbg setup.py build ; \
- done
- touch build-stamp
-
-clean: configure
- dh_testdir
- dh_testroot
- rm -fr *-stamp build
- for python in $(PYVERS); do \
- $$python setup.py clean ; \
- done
- dh_clean
-
-install-arch: build
- dh_testdir
- dh_testroot
- dh_clean -k
- dh_installdirs
- # psycopg2
- for python in $(PYVERS); do \
- $$python setup.py install \
- --root=$(CURDIR)/debian/python-psycopg2 --no-compile; \
- done
- for python in $(PYVERS); do \
- $$python-dbg setup.py install \
- --root=$(CURDIR)/debian/python-psycopg2-dbg --no-compile; \
- 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
-
-# Build architecture-independent files here.
-binary-indep: build install-indep
- dh_testdir
- dh_testroot
- dh_installdocs -i AUTHORS
- dh_installchangelogs -i
- dh_link -i
- dh_compress -i
- dh_fixperms -i
- dh_pycentral -p python-psycopg2da
- dh_installdeb -i
- dh_gencontrol -i
- dh_md5sums -i
- dh_builddeb -i
-
-# Build architecture-dependent files here.
-binary-arch: build install-arch
- dh_testdir
- dh_testroot
- dh_installdocs -a README AUTHORS doc tests
- 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_fixperms -a
- dh_makeshlibs -a
- dh_pycentral -a
- dh_python -a
- dh_installdeb -a
- dh_shlibdeps -a
- dh_gencontrol -a
- dh_md5sums -a
- dh_builddeb -a
-
-binary: binary-indep binary-arch
-.PHONY: build clean binary-indep binary-arch binary install configure
diff --git a/debian/watch b/debian/watch
deleted file mode 100644
index 7441da9..0000000
--- a/debian/watch
+++ /dev/null
@@ -1,2 +0,0 @@
-version=3
-http://www.initd.org/pub/software/psycopg/psycopg2-([0-9][0-9\.\-]*).tar.gz debian uupdate
diff --git a/debian/zope-psycopgda2.dzproduct b/debian/zope-psycopgda2.dzproduct
deleted file mode 100644
index e5634e4..0000000
--- a/debian/zope-psycopgda2.dzproduct
+++ /dev/null
@@ -1,4 +0,0 @@
-Name: ZPsycopgDA
-Directory: ZPsycopgDA:2
-Package: zope-psycopgda2
-ZopeVersions: >= 2.9
diff --git a/doc/src/_static/psycopg.css b/doc/src/_static/psycopg.css
index c4a2af6..a5d5b3a 100644
--- a/doc/src/_static/psycopg.css
+++ b/doc/src/_static/psycopg.css
@@ -26,3 +26,7 @@ a > tt.sql:hover {
dl.faq dt {
font-weight: bold;
}
+
+table.data-types div.line-block {
+ margin-bottom: 0;
+}
diff --git a/doc/src/advanced.rst b/doc/src/advanced.rst
index a7b406a..dafb1f5 100644
--- a/doc/src/advanced.rst
+++ b/doc/src/advanced.rst
@@ -27,6 +27,7 @@ More advanced topics
wait(aconn)
acurs = aconn.cursor()
+
.. index::
double: Subclassing; Cursor
double: Subclassing; Connection
@@ -45,6 +46,16 @@ 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.
+The `~psycopg2.extras` module contains several examples of :ref:`connection
+and cursor sublcasses <cursor-subclasses>`.
+
+.. note::
+
+ If you only need a customized cursor class, since Psycopg 2.5 you can use
+ the `~connection.cursor_factory` parameter of a regular connection instead
+ of creating a new `!connection` subclass.
+
+
.. index::
single: Example; Cursor subclass
@@ -403,13 +414,13 @@ this will be probably implemented in a future release.
.. _green-support:
-Support to coroutine libraries
-------------------------------
+Support for coroutine libraries
+-------------------------------
.. versionadded:: 2.2.0
-Psycopg can be used together with coroutine_\-based libraries, and participate
-to cooperative multithreading.
+Psycopg can be used together with coroutine_\-based libraries and participate
+in 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
diff --git a/doc/src/conf.py b/doc/src/conf.py
index 7105907..5937a7b 100644
--- a/doc/src/conf.py
+++ b/doc/src/conf.py
@@ -26,7 +26,7 @@ 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' ]
+extensions += [ 'dbapi_extension', 'sql_role', 'ticket_role' ]
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
@@ -42,7 +42,7 @@ master_doc = 'index'
# General information about the project.
project = u'Psycopg'
-copyright = u'2001-2011, Federico Di Gregorio. Documentation by Daniele Varrazzo'
+copyright = u'2001-2013, 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
@@ -65,6 +65,9 @@ intersphinx_mapping = {
'py3': ('http://docs.python.org/3.2', None),
}
+# Pattern to generate links to the bug tracker
+ticket_url = 'http://psycopg.lighthouseapp.com/projects/62710/tickets/%s'
+
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#language = None
diff --git a/doc/src/connection.rst b/doc/src/connection.rst
index e0b34ab..2828ab8 100644
--- a/doc/src/connection.rst
+++ b/doc/src/connection.rst
@@ -21,16 +21,17 @@ The ``connection`` class
Connections are thread safe and can be shared among many threads. See
:ref:`thread-safety` for details.
- .. method:: cursor([name] [, cursor_factory] [, withhold])
+ .. method:: cursor(name=None, cursor_factory=None, scrollable=None, withhold=False)
Return a new `cursor` object using the connection.
If *name* is specified, the returned cursor will be a :ref:`server
side cursor <server-side-cursors>` (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`.
+ Otherwise it will be a regular *client side* cursor. By default a
+ named cursor is declared without :sql:`SCROLL` option and
+ :sql:`WITHOUT HOLD`: set the argument or property `~cursor.scrollable`
+ to `!True`/`!False` and or `~cursor.withhold` to `!True` to change the
+ declaration.
The name can be a string not valid as a PostgreSQL identifier: for
example it may start with a digit and contain non-alphanumeric
@@ -46,14 +47,17 @@ The ``connection`` class
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
+ cursors. The class returned must be a subclass of
`psycopg2.extensions.cursor`. See :ref:`subclassing-cursor` for
- details.
+ details. A default factory for the connection can also be specified
+ using the `~connection.cursor_factory` attribute.
+
+ .. versionchanged:: 2.4.3 added the *withhold* argument.
+ .. versionchanged:: 2.5 added the *scrollable* argument.
.. extension::
- The `name` and `cursor_factory` parameters are Psycopg
- extensions to the |DBAPI|.
+ All the function arguments are Psycopg extensions to the |DBAPI|.
.. index::
@@ -71,6 +75,10 @@ The ``connection`` class
automatically open, commands have immediate effect. See
:ref:`transactions-control` for details.
+ .. versionchanged:: 2.5 if the connection is used in a ``with``
+ statement, the method is automatically called if no exception is
+ raised in the ``with`` block.
+
.. index::
pair: Transaction; Rollback
@@ -81,6 +89,10 @@ The ``connection`` class
connection without committing the changes first will cause an implicit
rollback to be performed.
+ .. versionchanged:: 2.5 if the connection is used in a ``with``
+ statement, the method is automatically called if an exception is
+ raised in the ``with`` block.
+
.. method:: close()
@@ -493,6 +505,15 @@ The ``connection`` class
the payload was not accessible. To keep backward compatibility,
`!Notify` objects can still be accessed as 2 items tuples.
+
+ .. attribute:: cursor_factory
+
+ The default cursor factory used by `~connection.cursor()` if the
+ parameter is not specified.
+
+ .. versionadded:: 2.5
+
+
.. index::
pair: Backend; PID
diff --git a/doc/src/cursor.rst b/doc/src/cursor.rst
index 4b8495a..62be5e3 100644
--- a/doc/src/cursor.rst
+++ b/doc/src/cursor.rst
@@ -83,6 +83,11 @@ The ``cursor`` class
The cursor will be unusable from this point forward; an
`~psycopg2.InterfaceError` will be raised if any operation is
attempted with the cursor.
+
+ .. versionchanged:: 2.5 if the cursor is used in a ``with`` statement,
+ the method is automatically called at the end of the ``with``
+ block.
+
.. attribute:: closed
@@ -114,20 +119,51 @@ The ``cursor`` class
The `name` attribute is a Psycopg extension to the |DBAPI|.
+ .. attribute:: scrollable
+
+ Read/write attribute: specifies if a named cursor is declared
+ :sql:`SCROLL`, hence is capable to scroll backwards (using
+ `~cursor.scroll()`). If `!True`, the cursor can be scrolled backwards,
+ if `!False` it is never scrollable. If `!None` (default) the cursor
+ scroll option is not specified, usually but not always meaning no
+ backward scroll (see the |declare-notes|__).
+
+ .. |declare-notes| replace:: :sql:`DECLARE` notes
+ .. __: http://www.postgresql.org/docs/current/static/sql-declare.html#SQL-DECLARE-NOTES
+
+ .. note::
+
+ set the value before calling `~cursor.execute()` or use the
+ `connection.cursor()` *scrollable* parameter, otherwise the value
+ will have no effect.
+
+ .. versionadded:: 2.5
+
+ .. extension::
+
+ The `scrollable` 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
+ fetch from the cursor even after a `connection.commit()` (but not after
a `connection.rollback()`). See :ref:`server-side-cursors`
+ .. note::
+
+ set the value before calling `~cursor.execute()` or use the
+ `connection.cursor()` *withhold* parameter, otherwise the value
+ will have no effect.
+
.. versionadded:: 2.4.3
-
+
.. extension::
The `withhold` attribute is a Psycopg extension to the |DBAPI|.
-
-
+
+
.. |execute*| replace:: `execute*()`
.. _execute*:
@@ -297,7 +333,8 @@ The ``cursor`` class
not changed.
The method can be used both for client-side cursors and
- :ref:`server-side cursors <server-side-cursors>`.
+ :ref:`server-side cursors <server-side-cursors>`. Server-side cursors
+ can usually scroll backwards only if declared `~cursor.scrollable`.
.. note::
@@ -527,10 +564,19 @@ The ``cursor`` class
|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 file: a file-like object to read or write (according to *sql*).
:param size: size of the read buffer to be used in :sql:`COPY FROM`.
+ The *sql* statement should be in the form :samp:`COPY {table} TO
+ STDOUT` to export :samp:`{table}` to the *file* object passed as
+ argument or :samp:`COPY {table} FROM STDIN` to import the content of
+ the *file* object into :samp:`{table}`.
+
+ *file* must be a readable file-like object (as required by
+ `~cursor.copy_from()`) for *sql* statement :sql:`COPY ... FROM STDIN`
+ or a writable one (as required by `~cursor.copy_to()`) for :sql:`COPY
+ ... TO STDOUT`.
+
Example:
>>> cur.copy_expert("COPY test TO STDOUT WITH CSV HEADER", sys.stdout)
diff --git a/doc/src/errorcodes.rst b/doc/src/errorcodes.rst
index f69dbe1..bfaaeb4 100644
--- a/doc/src/errorcodes.rst
+++ b/doc/src/errorcodes.rst
@@ -50,7 +50,7 @@ An example of the available constants defined in the module:
'42P01'
Constants representing all the error values documented by PostgreSQL versions
-between 8.1 and 9.1 are included in the module.
+between 8.1 and 9.2 are included in the module.
.. autofunction:: lookup(code)
diff --git a/doc/src/extensions.rst b/doc/src/extensions.rst
index b0a68ce..9465789 100644
--- a/doc/src/extensions.rst
+++ b/doc/src/extensions.rst
@@ -13,7 +13,7 @@ The module contains a few objects and function extending the minimum set of
functionalities defined by the |DBAPI|_.
-.. class:: connection
+.. class:: connection(dsn, async=False)
Is the class usually returned by the `~psycopg2.connect()` function.
It is exposed by the `extensions` module in order to allow
@@ -21,11 +21,9 @@ functionalities defined by the |DBAPI|_.
`!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
+.. class:: cursor(conn, name=None)
It is the class usually returnded by the `connection.cursor()`
method. It is exposed by the `extensions` module in order to allow
@@ -139,6 +137,37 @@ functionalities defined by the |DBAPI|_.
.. automethod:: from_string(s)
+.. autoclass:: Diagnostics(exception)
+
+ .. versionadded:: 2.5
+
+ The attributes currently available are:
+
+ .. attribute::
+ column_name
+ constraint_name
+ context
+ datatype_name
+ internal_position
+ internal_query
+ message_detail
+ message_hint
+ message_primary
+ schema_name
+ severity
+ source_file
+ source_function
+ source_line
+ sqlstate
+ statement_position
+ table_name
+
+ A string with the error field if available; `!None` if not available.
+ The attribute value is available only if the error sent by the server:
+ not all the fields are available for all the errors and for all the
+ server versions.
+
+
.. autofunction:: set_wait_callback(f)
.. versionadded:: 2.2.0
diff --git a/doc/src/extras.rst b/doc/src/extras.rst
index ce0e831..2a82af8 100644
--- a/doc/src/extras.rst
+++ b/doc/src/extras.rst
@@ -16,22 +16,27 @@ 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:
-
+.. _cursor-subclasses:
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`.
+modify the object behavior in some other way. Typically `!cursor` subclasses
+are passed as *cursor_factory* argument to `~psycopg2.connect()` so that the
+connection's `~connection.cursor()` method will generate objects of this
+class. Alternatively a `!cursor` subclass can be used one-off by passing it
+as the *cursor_factory* argument to the `!cursor()` method.
+
+If you want to use a `!connection` subclass you can pass it as the
+*connection_factory* argument of the `!connect()` function.
+
+
+.. index::
+ pair: Cursor; Dictionary
+
+.. _dict-cursor:
+
Dictionary-like cursor
^^^^^^^^^^^^^^^^^^^^^^
@@ -61,6 +66,11 @@ The records still support indexing as the original tuple:
.. autoclass:: DictConnection
+ .. note::
+
+ Not very useful since Psycopg 2.5: you can use `psycopg2.connect`\
+ ``(dsn, cursor_factory=DictCursor)`` instead of `!DictConnection`.
+
.. autoclass:: DictRow
@@ -71,6 +81,12 @@ Real dictionary cursor
.. autoclass:: RealDictConnection
+ .. note::
+
+ Not very useful since Psycopg 2.5: you can use `psycopg2.connect`\
+ ``(dsn, cursor_factory=RealDictCursor)`` instead of
+ `!RealDictConnection`.
+
.. autoclass:: RealDictRow
@@ -101,6 +117,12 @@ expect it to be... ::
.. autoclass:: NamedTupleConnection
+ .. note::
+
+ Not very useful since Psycopg 2.5: you can use `psycopg2.connect`\
+ ``(dsn, cursor_factory=NamedTupleCursor)`` instead of
+ `!NamedTupleConnection`.
+
.. index::
pair: Cursor; Logging
@@ -128,12 +150,100 @@ Additional data types
---------------------
-.. _adapt-hstore:
+.. index::
+ pair: JSON; Data types
+ pair: JSON; Adaptation
+
+.. _adapt-json:
+
+JSON_ adaptation
+^^^^^^^^^^^^^^^^
+
+.. versionadded:: 2.5
+
+Psycopg can adapt Python objects to and from the PostgreSQL |pgjson|_ type.
+With PostgreSQL 9.2 adaptation is available out-of-the-box. To use JSON data
+with previous database versions (either with the `9.1 json extension`__, but
+even if you want to convert text fields to JSON) you can use
+`register_json()`.
+
+.. __: http://people.planetpostgresql.org/andrew/index.php?/archives/255-JSON-for-PG-9.2-...-and-now-for-9.1!.html
+
+The Python library used to convert Python objects to JSON depends on the
+language version: with Python 2.6 and following the :py:mod:`json` module from
+the standard library is used; with previous versions the `simplejson`_ module
+is used if available. Note that the last `!simplejson` version supporting
+Python 2.4 is the 2.0.9.
+
+.. _JSON: http://www.json.org/
+.. |pgjson| replace:: :sql:`json`
+.. _pgjson: http://www.postgresql.org/docs/current/static/datatype-json.html
+.. _simplejson: http://pypi.python.org/pypi/simplejson/
+
+In order to pass a Python object to the database as query argument you can use
+the `Json` adapter::
+
+ curs.execute("insert into mytable (jsondata) values (%s)",
+ [Json({'a': 100})])
+
+Reading from the database, |pgjson| values will be automatically converted to
+Python objects.
+
+.. note::
+
+ You can use `~psycopg2.extensions.register_adapter()` to adapt any Python
+ dictionary to JSON, either registering `Json` or any subclass or factory
+ creating a compatible adapter::
+
+ psycopg2.extensions.register_adapter(dict, psycopg2.extras.Json)
+
+ This setting is global though, so it is not compatible with similar
+ adapters such as the one registered by `register_hstore()`. Any other
+ object supported by JSON can be registered the same way, but this will
+ clobber the default adaptation rule, so be careful to unwanted side
+ effects.
+
+If you want to customize the adaptation from Python to PostgreSQL you can
+either provide a custom `!dumps()` function to `!Json`::
+
+ curs.execute("insert into mytable (jsondata) values (%s)",
+ [Json({'a': 100}, dumps=simplejson.dumps)])
+
+or you can subclass it overriding the `~Json.dumps()` method::
+
+ class MyJson(Json):
+ def dumps(self, obj):
+ return simplejson.dumps(obj)
+
+ curs.execute("insert into mytable (jsondata) values (%s)",
+ [MyJson({'a': 100})])
+
+Customizing the conversion from PostgreSQL to Python can be done passing a
+custom `!loads()` function to `register_json()` (or `register_default_json()`
+for PostgreSQL 9.2). For example, if you want to convert the float values
+from :sql:`json` into :py:class:`~decimal.Decimal` you can use::
+
+ loads = lambda x: json.loads(x, parse_float=Decimal)
+ psycopg2.extras.register_json(conn, loads=loads)
+
+
+
+.. autoclass:: Json
+
+ .. automethod:: dumps
+
+.. autofunction:: register_json
+
+.. autofunction:: register_default_json
+
+
.. index::
pair: hstore; Data types
pair: dict; Adaptation
+.. _adapt-hstore:
+
Hstore data type
^^^^^^^^^^^^^^^^
@@ -141,7 +251,7 @@ Hstore data type
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
+been greatly improved in capacity and usefulness with the addition 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.
@@ -168,13 +278,13 @@ can be enabled using the `register_hstore()` function.
-.. _adapt-composite:
-
.. index::
pair: Composite types; Data types
pair: tuple; Adaptation
pair: namedtuple; Adaptation
+.. _adapt-composite:
+
Composite types casting
^^^^^^^^^^^^^^^^^^^^^^^
@@ -198,8 +308,8 @@ after a table row type) into a Python named tuple, or into a regular tuple if
>>> 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.
+Nested composite types are handled as expected, provided that the type of the
+composite components are registered as well.
.. doctest::
@@ -214,15 +324,167 @@ components must be registered as well.
Adaptation from Python tuples to composite types is automatic instead and
requires no adapter registration.
+
+.. _custom-composite:
+
+.. Note::
+
+ If you want to convert PostgreSQL composite types into something different
+ than a `!namedtuple` you can subclass the `CompositeCaster` overriding
+ `~CompositeCaster.make()`. For example, if you want to convert your type
+ into a Python dictionary you can use::
+
+ >>> class DictComposite(psycopg2.extras.CompositeCaster):
+ ... def make(self, values):
+ ... return dict(zip(self.attnames, values))
+
+ >>> psycopg2.extras.register_composite('card', cur,
+ ... factory=DictComposite)
+
+ >>> cur.execute("select (8, 'hearts')::card")
+ >>> cur.fetchone()[0]
+ {'suit': 'hearts', 'value': 8}
+
+
.. autofunction:: register_composite
+ .. versionchanged:: 2.4.3
+ added support for array of composite types
+ .. versionchanged:: 2.5
+ added the *factory* parameter
+
+
.. autoclass:: CompositeCaster
+ .. automethod:: make
+
+ .. versionadded:: 2.5
+
+ Object attributes:
+
+ .. attribute:: name
+
+ The name of the PostgreSQL type.
+
+ .. attribute:: schema
+
+ The schema where the type is defined.
+
+ .. versionadded:: 2.5
+
+ .. 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.
+
+
+.. index::
+ pair: range; Data types
+
+.. _adapt-range:
+
+Range data types
+^^^^^^^^^^^^^^^^
+
+.. versionadded:: 2.5
+
+Psycopg offers a `Range` Python type and supports adaptation between them and
+PostgreSQL |range|_ types. Builtin |range| types are supported out-of-the-box;
+user-defined |range| types can be adapted using `register_range()`.
+
+.. |range| replace:: :sql:`range`
+.. _range: http://www.postgresql.org/docs/current/static/rangetypes.html
+
+.. autoclass:: Range
+
+ This Python type is only used to pass and retrieve range values to and
+ from PostgreSQL and doesn't attempt to replicate the PostgreSQL range
+ features: it doesn't perform normalization and doesn't implement all the
+ operators__ supported by the database.
+
+ .. __: http://www.postgresql.org/docs/current/static/functions-range.html#RANGE-OPERATORS-TABLE
+
+ `!Range` objects are immutable, hashable, and support the ``in`` operator
+ (checking if an element is within the range). They can be tested for
+ equivalence but not for ordering. Empty ranges evaluate to `!False` in
+ boolean context, nonempty evaluate to `!True`.
+
+ Although it is possible to instantiate `!Range` objects, the class doesn't
+ have an adapter registered, so you cannot normally pass these instances as
+ query arguments. To use range objects as query arguments you can either
+ use one of the provided subclasses, such as `NumericRange` or create a
+ custom subclass using `register_range()`.
+
+ Object attributes:
+
+ .. autoattribute:: isempty
+ .. autoattribute:: lower
+ .. autoattribute:: upper
+ .. autoattribute:: lower_inc
+ .. autoattribute:: upper_inc
+ .. autoattribute:: lower_inf
+ .. autoattribute:: upper_inf
+
+
+The following `Range` subclasses map builtin PostgreSQL |range| types to
+Python objects: they have an adapter registered so their instances can be
+passed as query arguments. |range| values read from database queries are
+automatically casted into instances of these classes.
+
+.. autoclass:: NumericRange
+.. autoclass:: DateRange
+.. autoclass:: DateTimeRange
+.. autoclass:: DateTimeTZRange
+
+Custom |range| types (created with |CREATE TYPE|_ :sql:`... AS RANGE`) can be
+adapted to a custom `Range` subclass:
+
+.. autofunction:: register_range
+
+.. autoclass:: RangeCaster
+
+ Object attributes:
+
+ .. attribute:: range
+
+ The `!Range` subclass adapted.
+
+ .. attribute:: adapter
+
+ The `~psycopg2.extensions.ISQLQuote` responsible to adapt `!range`.
+
+ .. attribute:: typecaster
+
+ The object responsible for casting.
+
+ .. attribute:: array_typecaster
+
+ The object responsible to cast arrays, if available, else `!None`.
+
.. index::
pair: UUID; Data types
+.. _adapt-uuid:
+
UUID data type
^^^^^^^^^^^^^^
diff --git a/doc/src/faq.rst b/doc/src/faq.rst
index 01527ca..12aa99c 100644
--- a/doc/src/faq.rst
+++ b/doc/src/faq.rst
@@ -7,6 +7,8 @@ Here are a few gotchas you may encounter using `psycopg2`. Feel free to
suggest new entries!
+.. _faq-transactions:
+
Problems with transactions handling
-----------------------------------
@@ -51,6 +53,8 @@ Why do I get the error *current transaction is aborted, commands ignored until e
informations.
+.. _faq-types:
+
Problems with type conversions
------------------------------
@@ -151,6 +155,8 @@ Arrays of *TYPE* are not casted to list.
provided in the `~psycopg2.extensions.new_array_type()` documentation.
+.. _faq-best-practices:
+
Best practices
--------------
@@ -191,6 +197,8 @@ What are the advantages or disadvantages of using named cursors?
little memory on the client and to skip or discard parts of the result set.
+.. _faq-compile:
+
Problems compiling and deploying psycopg2
-----------------------------------------
diff --git a/doc/src/index.rst b/doc/src/index.rst
index e15e7bb..595c236 100644
--- a/doc/src/index.rst
+++ b/doc/src/index.rst
@@ -4,12 +4,12 @@ Psycopg -- PostgreSQL database adapter for Python
.. sectionauthor:: Daniele Varrazzo <daniele.varrazzo@gmail.com>
-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_ is the most popular PostgreSQL_ database adapter for the Python_
+programming language. Its main features are the complete implementation of
+the Python |DBAPI|_ specification and the thread safety (several threads can
+share the same connection). 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.
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
@@ -18,12 +18,13 @@ both efficient and secure. It features client-side and :ref:`server-side
support, and a flexible :ref:`objects adaptation system
<python-types-adaptation>`. 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 <adapt-binary>`. Also available are mappings between lists
-and PostgreSQL arrays of any supported type, between :ref:`dictionaries and
-PostgreSQL hstores <adapt-hstore>`, and between :ref:`tuples/namedtuples and
-PostgreSQL composite types <adapt-composite>`.
+(both byte strings and Unicode), numbers (ints, longs, floats, decimals),
+booleans and date/time objects (both built-in and `mx.DateTime`_), several
+types of :ref:`binary objects <adapt-binary>`. Also available are mappings
+between lists and PostgreSQL arrays of any supported type, between
+:ref:`dictionaries and PostgreSQL hstore <adapt-hstore>`, between
+:ref:`tuples/namedtuples and PostgreSQL composite types <adapt-composite>`,
+and between Python objects and :ref:`JSON <adapt-json>`.
Psycopg 2 is both Unicode and Python 3 friendly.
@@ -31,7 +32,6 @@ 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
@@ -42,6 +42,7 @@ Psycopg 2 is both Unicode and Python 3 friendly.
.. toctree::
:maxdepth: 2
+ install
usage
module
connection
@@ -53,6 +54,7 @@ Psycopg 2 is both Unicode and Python 3 friendly.
extras
errorcodes
faq
+ news
.. ifconfig:: builder != 'text'
diff --git a/doc/src/install.rst b/doc/src/install.rst
new file mode 100644
index 0000000..017be95
--- /dev/null
+++ b/doc/src/install.rst
@@ -0,0 +1,258 @@
+Introduction
+============
+
+.. sectionauthor:: Daniele Varrazzo <daniele.varrazzo@gmail.com>
+
+Psycopg is a PostgreSQL_ adapter for the Python_ programming language. It is a
+wrapper for the libpq_, the official PostgreSQL client library.
+
+The `psycopg2` package is the current mature implementation of the adapter: it
+is a C extension and as such it is only compatible with CPython_. If you want
+to use Psycopg on a different Python implementation (PyPy, Jython, IronPython)
+there is an experimental `porting of Psycopg for Ctypes`__, but it is not as
+mature as the C implementation yet.
+
+The current `!psycopg2` implementation supports:
+
+- Python 2 versions from 2.5 to 2.7
+- Python 3 versions from 3.1 to 3.3
+- PostgreSQL versions from 7.4 to 9.2
+
+.. _PostgreSQL: http://www.postgresql.org/
+.. _Python: http://www.python.org/
+.. _libpq: http://www.postgresql.org/docs/current/static/libpq.html
+.. _CPython: http://en.wikipedia.org/wiki/CPython
+.. _Ctypes: http://docs.python.org/library/ctypes.html
+.. __: https://github.com/mvantellingen/psycopg2-ctypes
+
+
+.. note::
+
+ `!psycopg2` usually depends at runtime on the libpq dynamic library.
+ However it can connect to PostgreSQL servers of any supported version,
+ independently of the version of the libpq used: just install the most
+ recent libpq version or the most practical, without trying to match it to
+ the version of the PostgreSQL server you will have to connect to.
+
+
+Installation
+============
+
+If possible, and usually it is, please :ref:`install Psycopg from a package
+<install-from-package>` available for your distribution or operating system.
+
+Compiling from source is a very easy task, however `!psycopg2` is a C
+extension module and as such it requires a few more things in place respect to
+a pure Python module. So, if you don't have experience compiling Python
+extension packages, *above all if you are a Windows or a Mac OS user*, please
+use a pre-compiled package and go straight to the :ref:`module usage <usage>`
+avoid bothering with the gory details.
+
+
+
+.. _install-from-package:
+
+Install from a package
+----------------------
+
+.. index::
+ pair: Install; Linux
+
+**Linux**
+ Psycopg is available already packaged in many Linux distributions: look
+ for a package such as ``python-psycopg2`` using the package manager of
+ your choice.
+
+ On Debian, Ubuntu and other deb-based distributions you should just need::
+
+ sudo apt-get install python-psycopg2
+
+ to install the package with all its dependencies.
+
+
+.. index::
+ pair: Install; Mac OS X
+
+**Mac OS X**
+ Psycopg is available as a `fink package`__ in the *unstable* tree: you may
+ install it with::
+
+ fink install psycopg2-py27
+
+ .. __: http://pdb.finkproject.org/pdb/package.php/psycopg2-py27
+
+ The library is also available on `MacPorts`__ try::
+
+ sudo port install py27-psycopg2
+
+ .. __: http://www.macports.org/
+
+
+.. index::
+ pair: Install; Windows
+
+**Microsoft Windows**
+ Jason Erickson maintains a packaged `Windows port of Psycopg`__ with
+ installation executable. Download. Double click. Done.
+
+ .. __: http://www.stickpeople.com/projects/python/win-psycopg/
+
+
+
+.. index::
+ single: Install; from source
+
+.. _install-from-source:
+
+Install from source
+-------------------
+
+These notes illustrate how to compile Psycopg on Linux. If you want to compile
+Psycopg on other platforms you may have to adjust some details accordingly.
+
+.. _requirements:
+
+Psycopg is a C wrapper to the libpq PostgreSQL client library. To install it
+from sources you will need:
+
+- A C compiler.
+
+- The Python header files. They are usually installed in a package such as
+ **python-dev**. A message such as *error: Python.h: No such file or
+ directory* is an indication that the Python headers are missing.
+
+- The libpq header files. They are usually installed in a package such as
+ **libpq-dev**. If you get an *error: libpq-fe.h: No such file or directory*
+ you are missing them.
+
+- The :program:`pg_config` program: it is usually installed by the
+ **libpq-dev** package but sometimes it is not in a :envvar:`PATH` directory.
+ Having it in the :envvar:`PATH` greatly streamlines the installation, so try
+ running ``pg_config --version``: if it returns an error or an unexpected
+ version number then locate the directory containing the :program:`pg_config`
+ shipped with the right libpq version (usually
+ ``/usr/lib/postgresql/X.Y/bin/``) and add it to the :envvar:`PATH`::
+
+ $ export PATH=/usr/lib/postgresql/X.Y/bin/:$PATH
+
+ You only need it to compile and install `!psycopg2`, not for its regular
+ usage.
+
+.. note::
+
+ The libpq header files used to compile `!psycopg2` should match the
+ version of the library linked at runtime. If you get errors about missing
+ or mismatching libraries when importing `!psycopg2` check (e.g. using
+ :program:`ldd`) if the module ``psycopg2/_psycopg.so`` is linked to the
+ right ``libpq.so``.
+
+
+
+.. index::
+ single: Install; from PyPI
+
+.. _package-manager:
+
+Use a Python package manager
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+If the above requirements are satisfied, you can use :program:`easy_install`,
+:program:`pip` or whatever the Python package manager of the week::
+
+ $ pip install psycopg2
+
+Please refer to your package manager documentation about performing a local or
+global installation, :program:`virtualenv` (fully supported by recent Psycopg
+versions), using different Python versions and other nuances.
+
+
+.. index::
+ single: setup.py
+ single: setup.cfg
+
+.. _source-package:
+
+Use the source package
+^^^^^^^^^^^^^^^^^^^^^^
+
+You can download a copy of Psycopg source files from the `Psycopg download
+page`__. Once unpackaged, to compile and install the package you can run::
+
+ $ python setup.py build
+ $ sudo python setup.py install
+
+If you have less standard requirements such as:
+
+- creating a :ref:`debug build <debug-build>`,
+- using :program:`pg_config` not in the :envvar:`PATH`,
+- supporting ``mx.DateTime``,
+
+then take a look at the ``setup.cfg`` file.
+
+Some of the options available in ``setup.cfg`` are also available as command
+line arguments of the ``build_ext`` sub-command. For instance you can specify
+an alternate :program:`pg_config` version using::
+
+ $ python setup.py build_ext --pg-config /path/to/pg_config build
+
+Use ``python setup.py build_ext --help`` to get a list of the options
+supported.
+
+.. __: http://initd.org/psycopg/download/
+
+
+
+.. index::
+ single: debug
+ single: PSYCOPG_DEBUG
+
+.. _debug-build:
+
+Creating a debug build
+----------------------
+
+In case of problems, Psycopg can be configured to emit detailed debug
+messages, which can be very useful for diagnostics and to report a bug. In
+order to create a debug package:
+
+- `Download`__ and unpack the Psycopg source package.
+
+- Edit the ``setup.cfg`` file adding the ``PSYCOPG_DEBUG`` flag to the
+ ``define`` option.
+
+- :ref:`Compile and install <source-package>` the package.
+
+- Set the :envvar:`PSYCOPG_DEBUG` variable::
+
+ $ export PSYCOPG_DEBUG=1
+
+- Run your program (making sure that the `!psycopg2` package imported is the
+ one you just compiled and not e.g. the system one): you will have a copious
+ stream of informations printed on stdout.
+
+.. __: http://initd.org/psycopg/download/
+
+
+
+.. _other-problems:
+
+If you still have problems
+--------------------------
+
+Try the following. *In order:*
+
+- Read again the :ref:`requirements <requirements>`.
+
+- Read the :ref:`FAQ <faq-compile>`.
+
+- Google for `!psycopg2` *your error message*. Especially useful the week
+ after the release of a new OS X version.
+
+- Write to the `Mailing List`__.
+
+- Complain on your blog or on Twitter that `!psycopg2` is the worst package
+ ever and about the quality time you have wasted figuring out the correct
+ :envvar:`ARCHFLAGS`. Especially useful from the Starbucks near you.
+
+.. __: http://mail.postgresql.org/mj/mj_wwwusr/domain=postgresql.org?func=lists-long-full&extra=psycopg
+
diff --git a/doc/src/module.rst b/doc/src/module.rst
index 35292ba..feaef51 100644
--- a/doc/src/module.rst
+++ b/doc/src/module.rst
@@ -7,7 +7,7 @@ The `psycopg2` module content
The module interface respects the standard defined in the |DBAPI|_.
-.. index::
+.. index::
single: Connection string
double: Connection; Parameters
single: Username; Connection
@@ -16,11 +16,14 @@ The module interface respects the standard defined in the |DBAPI|_.
single: Port; Connection
single: DSN (Database Source Name)
-.. function:: connect(dsn or params [, connection_factory] [, async=0])
+.. function::
+ connect(dsn, connection_factory=None, cursor_factory=None, async=False)
+ connect(\*\*kwargs, connection_factory=None, cursor_factory=None, async=False)
Create a new database session and return a new `connection` object.
- The connection parameters can be specified either as a string::
+ The connection parameters can be specified either as a `libpq connection
+ string`__ using the *dsn* parameter::
conn = psycopg2.connect("dbname=test user=postgres password=secret")
@@ -28,9 +31,15 @@ The module interface respects the standard defined in the |DBAPI|_.
conn = psycopg2.connect(database="test", user="postgres", password="secret")
+ The two call styles are mutually exclusive: you cannot specify connection
+ parameters as keyword arguments together with a connection string; only
+ the parameters not needed for the database connection (*i.e.*
+ *connection_factory*, *cursor_factory*, and *async*) are supported
+ together with the *dsn* argument.
+
The basic connection parameters are:
- - `!dbname` -- the database name (only in dsn string)
+ - `!dbname` -- the database name (only in the *dsn* string)
- `!database` -- the database name (only as keyword argument)
- `!user` -- user name used to authenticate
- `!password` -- password used to authenticate
@@ -38,26 +47,45 @@ The module interface respects the standard defined in the |DBAPI|_.
- `!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`__.
+ be passed either in the connection string or as keywords. The PostgreSQL
+ documentation contains the complete list of the `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
+ .. __:
+ .. _connstring: http://www.postgresql.org/docs/current/static/libpq-connect.html#LIBPQ-CONNSTRING
+ .. __:
+ .. _connparams: http://www.postgresql.org/docs/current/static/libpq-connect.html#LIBPQ-PARAMKEYWORDS
+ .. __:
+ .. _connenvvars: 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.
+ taking a *dsn* string argument. See :ref:`subclassing-connection` for
+ details. If a *cursor_factory* is specified, the connection's
+ `~connection.cursor_factory` is set to it. If you only need customized
+ cursors you can use this parameter instead of subclassing a connection.
- Using *async*\=1 an asynchronous connection will be created: see
+ Using *async*\=\ `!True` 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.
+ .. versionchanged:: 2.5
+ added the *cursor_factory* parameter.
+
+ .. seealso::
+
+ - libpq `connection string syntax`__
+ - libpq supported `connection parameters`__
+ - libpq supported `environment variables`__
+
+ .. __: connstring_
+ .. __: connparams_
+ .. __: connenvvars_
+
.. extension::
The parameters *connection_factory* and *async* are Psycopg extensions
@@ -135,10 +163,27 @@ available through the following exceptions:
The cursor the exception was raised from; `None` if not applicable.
+ .. attribute:: diag
+
+ A `~psycopg2.extensions.Diagnostics` object containing further
+ information about the error. ::
+
+ >>> try:
+ ... cur.execute("SELECT * FROM barf")
+ ... except Exception, e:
+ ... pass
+
+ >>> e.diag.severity
+ 'ERROR'
+ >>> e.diag.message_primary
+ 'relation "barf" does not exist'
+
+ .. versionadded:: 2.5
+
.. extension::
- The `~Error.pgerror`, `~Error.pgcode`, and `~Error.cursor` attributes
- are Psycopg extensions.
+ The `~Error.pgerror`, `~Error.pgcode`, `~Error.cursor`, and
+ `~Error.diag` attributes are Psycopg extensions.
.. exception:: InterfaceError
@@ -258,15 +303,15 @@ 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.
@@ -278,11 +323,11 @@ The module exports the following constructors and singletons:
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
@@ -290,10 +335,16 @@ The module exports the following constructors and singletons:
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.
-
+
+.. note::
+
+ All the adapters returned by the module level factories (`!Binary`,
+ `!Date`, `!Time`, `!Timestamp` and the `!*FromTicks` variants) expose the
+ wrapped object (a regular Python object such as `!datetime`) in an
+ `!adapted` attribute.
.. data:: STRING
@@ -304,17 +355,17 @@ The module exports the following constructors and singletons:
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.
diff --git a/doc/src/news.rst b/doc/src/news.rst
new file mode 100644
index 0000000..d5b11a6
--- /dev/null
+++ b/doc/src/news.rst
@@ -0,0 +1,4 @@
+Release notes
+=============
+
+.. include:: ../../NEWS
diff --git a/doc/src/tools/lib/ticket_role.py b/doc/src/tools/lib/ticket_role.py
new file mode 100644
index 0000000..f8ceea1
--- /dev/null
+++ b/doc/src/tools/lib/ticket_role.py
@@ -0,0 +1,39 @@
+# -*- coding: utf-8 -*-
+"""
+ ticket role
+ ~~~~~~~~~~~
+
+ An interpreted text role to link docs to lighthouse issues.
+
+ :copyright: Copyright 2013 by Daniele Varrazzo.
+"""
+
+from docutils import nodes, utils
+from docutils.parsers.rst import roles
+
+def ticket_role(name, rawtext, text, lineno, inliner, options={}, content=[]):
+ try:
+ num = int(text.replace('#', ''))
+ except ValueError:
+ msg = inliner.reporter.error(
+ "ticket number must be... a number, got '%s'" % text)
+ prb = inliner.problematic(rawtext, rawtext, msg)
+ return [prb], [msg]
+
+ url_pattern = inliner.document.settings.env.app.config.ticket_url
+ if url_pattern is None:
+ msg = inliner.reporter.warning(
+ "ticket not configured: please configure ticket_url in conf.py")
+ prb = inliner.problematic(rawtext, rawtext, msg)
+ return [prb], [msg]
+
+ url = url_pattern % num
+ roles.set_classes(options)
+ node = nodes.reference(rawtext, 'ticket ' + utils.unescape(text),
+ refuri=url, **options)
+ return [node], []
+
+def setup(app):
+ app.add_config_value('ticket_url', None, 'env')
+ app.add_role('ticket', ticket_role)
+
diff --git a/doc/src/usage.rst b/doc/src/usage.rst
index 1ab6419..f1f2a1a 100644
--- a/doc/src/usage.rst
+++ b/doc/src/usage.rst
@@ -1,3 +1,5 @@
+.. _usage:
+
Basic module usage
==================
@@ -49,7 +51,7 @@ The main entry points of Psycopg are:
- create new `cursor`\s using the `~connection.cursor()` method to
execute database commands and queries,
- - terminate the session using the methods `~connection.commit()` or
+ - terminate transactions using the methods `~connection.commit()` or
`~connection.rollback()`.
- The class `cursor` allows interaction with the database:
@@ -202,28 +204,88 @@ Adaptation of Python values to SQL types
Many standard 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.
+The following table shows the default mapping between Python and PostgreSQL
+types:
+
+..
+ TODO: The table is not rendered in text output
+
+.. only:: html
+
+ .. table::
+ :class: data-types
+
+ +--------------------+-------------------------+--------------------------+
+ | Python | PostgreSQL | See also |
+ +====================+=========================+==========================+
+ | `!None` | :sql:`NULL` | :ref:`adapt-consts` |
+ +--------------------+-------------------------+ |
+ | `!bool` | :sql:`bool` | |
+ +--------------------+-------------------------+--------------------------+
+ | `!float` | | :sql:`real` | :ref:`adapt-numbers` |
+ | | | :sql:`double` | |
+ +--------------------+-------------------------+ |
+ | | `!int` | | :sql:`smallint` | |
+ | | `!long` | | :sql:`integer` | |
+ | | | :sql:`bigint` | |
+ +--------------------+-------------------------+ |
+ | `~decimal.Decimal` | :sql:`numeric` | |
+ +--------------------+-------------------------+--------------------------+
+ | | `!str` | | :sql:`varchar` | :ref:`adapt-string` |
+ | | `!unicode` | | :sql:`text` | |
+ +--------------------+-------------------------+--------------------------+
+ | | `buffer` | :sql:`bytea` | :ref:`adapt-binary` |
+ | | `memoryview` | | |
+ | | `bytearray` | | |
+ | | `bytes` | | |
+ | | Buffer protocol | | |
+ +--------------------+-------------------------+--------------------------+
+ | `!date` | :sql:`date` | :ref:`adapt-date` |
+ +--------------------+-------------------------+ |
+ | `!time` | :sql:`time` | |
+ +--------------------+-------------------------+ |
+ | `!datetime` | | :sql:`timestamp` | |
+ | | | :sql:`timestamptz` | |
+ +--------------------+-------------------------+ |
+ | `!timedelta` | :sql:`interval` | |
+ +--------------------+-------------------------+--------------------------+
+ | `!list` | :sql:`ARRAY` | :ref:`adapt-list` |
+ +--------------------+-------------------------+--------------------------+
+ | | `!tuple` | | Composite types | | :ref:`adapt-tuple` |
+ | | `!namedtuple` | | :sql:`IN` syntax | | :ref:`adapt-composite` |
+ +--------------------+-------------------------+--------------------------+
+ | `!dict` | :sql:`hstore` | :ref:`adapt-hstore` |
+ +--------------------+-------------------------+--------------------------+
+ | Psycopg's `!Range` | :sql:`range` | :ref:`adapt-range` |
+ +--------------------+-------------------------+--------------------------+
+ | Anything\ |tm| | :sql:`json` | :ref:`adapt-json` |
+ +--------------------+-------------------------+--------------------------+
+ | `uuid` | :sql:`uuid` | :ref:`adapt-uuid` |
+ +--------------------+-------------------------+--------------------------+
+
+.. |tm| unicode:: U+2122
+
+The mapping is fairly customizable: 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.
-.. _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::
+.. _adapt-consts:
+
+Constants 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;'
+ 'SELECT NULL, true, false;'
-.. _adapt-numbers:
.. index::
single: Adaptation; numbers
@@ -231,168 +293,48 @@ the SQL string that would be sent to the database.
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 <cast-array-unknown>`.
-
-.. _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::
+.. _adapt-numbers:
- >>> cur.mogrify("SELECT %s IN %s;", (10, (10, 20, 30)))
- 'SELECT 10 IN (10, 20, 30);'
+Numbers adaptation
+^^^^^^^^^^^^^^^^^^
- .. note::
+Numeric objects: `int`, `long`, `float`, `~decimal.Decimal` are converted in
+the PostgreSQL numerical representation::
- SQL doesn't allow an empty list in the IN operator, so your code should
- guard against empty tuples.
+ >>> cur.mogrify("SELECT %s, %s, %s, %s;", (10, 10L, 10.0, Decimal("10.00")))
+ 'SELECT 10, 10, 10.0, 10.00;'
- If you want PostgreSQL composite types to be converted into a Python
- tuple/namedtuple you can use the `~psycopg2.extras.register_composite()`
- function.
+Reading from the database, integer types are converted into `!int`, floating
+point types are converted into `!float`, :sql:`numeric`\/\ :sql:`decimal` are
+converted into `!Decimal`.
- .. versionadded:: 2.0.6
- the tuple :sql:`IN` adaptation.
+.. note::
- .. 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.
+ Sometimes you may prefer to receive :sql:`numeric` data as `!float`
+ insted, for performance reason or ease of manipulation: you can configure
+ an adapter to :ref:`cast PostgreSQL numeric to Python float <faq-float>`.
+ This of course may imply a loss of precision.
- .. versionchanged:: 2.3
- `~collections.namedtuple` instances are adapted like regular tuples and
- can thus be used to represent composite types.
+.. seealso:: `PostgreSQL numeric types
+ <http://www.postgresql.org/docs/current/static/datatype-numeric.html>`__
-.. _adapt-dict:
.. index::
- single: dict; Adaptation
- single: hstore; Adaptation
+ pair: Strings; Adaptation
+ single: Unicode; 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.
+.. _adapt-string:
- .. |hstore| replace:: :sql:`hstore`
- .. _hstore: http://www.postgresql.org/docs/current/static/hstore.html
+Strings adaptation
+^^^^^^^^^^^^^^^^^^
- .. versionadded:: 2.3
- the :sql:`hstore` adaptation.
+Python `str` and `unicode` are converted into the SQL string syntax.
+`!unicode` objects (`!str` in Python 3) are encoded in the connection
+`~connection.encoding` before sending to the backend: trying to send a
+character not supported by the encoding will result in an error. Data is
+usually received as `!str` (*i.e.* it is *decoded* on Python 3, left *encoded*
+on Python 2). However it is possible to receive `!unicode` on Python 2 too:
+see :ref:`unicode-handling`.
.. index::
@@ -401,7 +343,7 @@ the SQL string that would be sent to the database.
.. _unicode-handling:
Unicode handling
-^^^^^^^^^^^^^^^^
+''''''''''''''''
Psycopg can exchange Unicode data with a PostgreSQL database. Python
`!unicode` objects are automatically *encoded* in the client encoding
@@ -464,20 +406,108 @@ the connection or globally: see the function
psycopg2.extensions.register_type(psycopg2.extensions.UNICODE)
psycopg2.extensions.register_type(psycopg2.extensions.UNICODEARRAY)
- and then forget about this story.
+ and forget about this story.
+
+
+.. index::
+ single: Buffer; Adaptation
+ single: bytea; Adaptation
+ single: bytes; Adaptation
+ single: bytearray; Adaptation
+ single: memoryview; Adaptation
+ single: Binary string
+
+.. _adapt-binary:
+
+Binary adaptation
+^^^^^^^^^^^^^^^^^
+
+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
.. index::
+ single: Adaptation; Date/Time objects
+ single: Date objects; Adaptation
+ single: Time objects; Adaptation
+ single: Interval objects; Adaptation
+ single: mx.DateTime; Adaptation
+
+.. _adapt-date:
+
+Date/Time objects adaptation
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Date and time objects: builtin `~datetime.datetime`, `~datetime.date`,
+`~datetime.time`, `~datetime.timedelta` are converted into PostgreSQL's
+:sql:`timestamp[tz]`, :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';"
+
+.. seealso:: `PostgreSQL date/time types
+ <http://www.postgresql.org/docs/current/static/datatype-datetime.html>`__
+
+.. 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.
+The PostgreSQL type :sql:`timestamp with time zone` (a.k.a.
+:sql:`timestamptz`) 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;")
@@ -500,6 +530,81 @@ rounded to the nearest minute, with an error of up to 30 seconds.
versions use `psycopg2.extras.register_tstz_w_secs()`.
+.. _adapt-list:
+
+Lists adaptation
+^^^^^^^^^^^^^^^^
+
+.. 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::
+
+ You can use a Python list as the argument of the :sql:`IN` operator using
+ `the PostgreSQL ANY operator`__. ::
+
+ ids = [10, 20, 30]
+ cur.execute("SELECT * FROM data WHERE id = ANY(%s);", (ids,))
+
+ Furthermore :sql:`ANY` can also work with empty lists, whereas :sql:`IN ()`
+ is a SQL syntax error.
+
+ .. __: http://www.postgresql.org/docs/current/static/functions-subquery.html#FUNCTIONS-SUBQUERY-ANY-SOME
+
+.. note::
+
+ Reading back from PostgreSQL, arrays are converted to lists of Python
+ objects as expected, but only if the items are of a known known type.
+ Arrays of unknown types are returned as represented by the database (e.g.
+ ``{a,b,c}``). If you want to convert the items into Python objects you can
+ easily create a typecaster for :ref:`array of unknown types
+ <cast-array-unknown>`.
+
+
+.. _adapt-tuple:
+
+Tuples adaptation
+^^^^^^^^^^^^^^^^^^
+
+.. 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 :sql:`IN` operator, so your code
+ should guard against empty tuples. Alternatively you can :ref:`use a
+ Python list <adapt-list>`.
+
+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.
+
+
.. index:: Transaction, Begin, Commit, Rollback, Autocommit, Read only
.. _transactions-control:
@@ -527,7 +632,7 @@ 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
+Psycopg, the connection must be in autocommit mode: you can use the
`~connection.autocommit` property (`~connection.set_isolation_level()` in
older versions).
@@ -547,6 +652,30 @@ the details.
.. index::
+ single: with statement
+
+``with`` statement
+^^^^^^^^^^^^^^^^^^
+
+Starting from version 2.5, psycopg2's connections and cursors are *context
+managers* and can be used with the ``with`` statement::
+
+ with psycopg2.connect(DSN) as conn:
+ with conn.cursor() as curs:
+ curs.execute(SQL)
+
+When a connection exits the ``with`` block, if no exception has been raised by
+the block, the transaction is committed. In case of exception the transaction
+is rolled back. In no case the connection is closed: a connection can be used
+in more than a ``with`` statement and each ``with`` block is effectively
+wrapped in a transaction.
+
+When a cursor exits the ``with`` block it is closed, releasing any resource
+eventually associated with it. The state of the transaction is not affected.
+
+
+
+.. index::
pair: Server side; Cursor
pair: Named; Cursor
pair: DECLARE; SQL command
@@ -576,7 +705,9 @@ 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.
+`~cursor.fetchmany()` methods. Normally you can only scroll forward in a
+cursor: if you need to scroll backwards you should declare your cursor
+`~cursor.scrollable`.
Named cursors are also :ref:`iterable <cursor-iterable>` like regular cursors.
Note however that before Psycopg 2.4 iteration was performed fetching one
diff --git a/lib/__init__.py b/lib/__init__.py
index 892d803..cf8c06a 100644
--- a/lib/__init__.py
+++ b/lib/__init__.py
@@ -41,23 +41,6 @@ Homepage: http://initd.org/projects/psycopg2
# Import modules needed by _psycopg to allow tools like py2exe to do
# their work without bothering about the module dependencies.
-import sys, warnings
-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 >= (2, 4):
- try:
- import decimal as _psycopg_needs_decimal
- except:
- warnings.warn(
- "can't import decimal module probably needed by _psycopg",
- RuntimeWarning)
-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
@@ -118,7 +101,7 @@ del re
def connect(dsn=None,
database=None, user=None, password=None, host=None, port=None,
- connection_factory=None, async=False, **kwargs):
+ connection_factory=None, cursor_factory=None, async=False, **kwargs):
"""
Create a new database connection.
@@ -143,6 +126,9 @@ def connect(dsn=None,
factory can be specified. It should be a callable object taking a dsn
argument.
+ Using the *cursor_factory* parameter, a new default cursor factory will be
+ used by cursor().
+
Using *async*=True an asynchronous connection will be created.
Any other keyword parameter will be passed to the underlying client
@@ -175,8 +161,8 @@ def connect(dsn=None,
dsn = " ".join(["%s=%s" % (k, _param_escape(str(v)))
for (k, v) in items])
- return _connect(dsn, connection_factory=connection_factory, async=async)
-
-
-__all__ = filter(lambda k: not k.startswith('_'), locals().keys())
+ conn = _connect(dsn, connection_factory=connection_factory, async=async)
+ if cursor_factory is not None:
+ conn.cursor_factory = cursor_factory
+ return conn
diff --git a/lib/_json.py b/lib/_json.py
new file mode 100644
index 0000000..536dd58
--- /dev/null
+++ b/lib/_json.py
@@ -0,0 +1,194 @@
+"""Implementation of the JSON adaptation objects
+
+This module exists to avoid a circular import problem: pyscopg2.extras depends
+on psycopg2.extension, so I can't create the default JSON typecasters in
+extensions importing register_json from extras.
+"""
+
+# psycopg/_json.py - Implementation of the JSON adaptation objects
+#
+# Copyright (C) 2012 Daniele Varrazzo <daniele.varrazzo@gmail.com>
+#
+# 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 psycopg2._psycopg import ISQLQuote, QuotedString
+from psycopg2._psycopg import new_type, new_array_type, register_type
+
+
+# import the best json implementation available
+if sys.version_info[:2] >= (2,6):
+ import json
+else:
+ try:
+ import simplejson as json
+ except ImportError:
+ json = None
+
+
+# oids from PostgreSQL 9.2
+JSON_OID = 114
+JSONARRAY_OID = 199
+
+class Json(object):
+ """
+ An `~psycopg2.extensions.ISQLQuote` wrapper to adapt a Python object to
+ :sql:`json` data type.
+
+ `!Json` can be used to wrap any object supported by the provided *dumps*
+ function. If none is provided, the standard :py:func:`json.dumps()` is
+ used (`!simplejson` for Python < 2.6;
+ `~psycopg2.extensions.ISQLQuote.getquoted()` will raise `!ImportError` if
+ the module is not available).
+
+ """
+ def __init__(self, adapted, dumps=None):
+ self.adapted = adapted
+
+ if dumps is not None:
+ self._dumps = dumps
+ elif json is not None:
+ self._dumps = json.dumps
+ else:
+ self._dumps = None
+
+ def __conform__(self, proto):
+ if proto is ISQLQuote:
+ return self
+
+ def dumps(self, obj):
+ """Serialize *obj* in JSON format.
+
+ The default is to call `!json.dumps()` or the *dumps* function
+ provided in the constructor. You can override this method to create a
+ customized JSON wrapper.
+ """
+ dumps = self._dumps
+ if dumps is not None:
+ return dumps(obj)
+ else:
+ raise ImportError(
+ "json module not available: "
+ "you should provide a dumps function")
+
+ def getquoted(self):
+ s = self.dumps(self.adapted)
+ return QuotedString(s).getquoted()
+
+
+def register_json(conn_or_curs=None, globally=False, loads=None,
+ oid=None, array_oid=None):
+ """Create and register typecasters converting :sql:`json` type to Python objects.
+
+ :param conn_or_curs: a connection or cursor used to find the :sql:`json`
+ and :sql:`json[]` oids; the typecasters are registered in a scope
+ limited to this object, unless *globally* is set to `!True`. It can be
+ `!None` if the oids are provided
+ :param globally: if `!False` register the typecasters only on
+ *conn_or_curs*, otherwise register them globally
+ :param loads: the function used to parse the data into a Python object. If
+ `!None` use `!json.loads()`, where `!json` is the module chosen
+ according to the Python version (see above)
+ :param oid: the OID of the :sql:`json` type if known; If not, it will be
+ queried on *conn_or_curs*
+ :param array_oid: the OID of the :sql:`json[]` 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 :sql:`json` type. No query is
+ performed if *oid* and *array_oid* are provided. Raise
+ `~psycopg2.ProgrammingError` if the type is not found.
+
+ """
+ if oid is None:
+ oid, array_oid = _get_json_oids(conn_or_curs)
+
+ JSON, JSONARRAY = _create_json_typecasters(oid, array_oid, loads)
+
+ register_type(JSON, not globally and conn_or_curs or None)
+
+ if JSONARRAY is not None:
+ register_type(JSONARRAY, not globally and conn_or_curs or None)
+
+ return JSON, JSONARRAY
+
+def register_default_json(conn_or_curs=None, globally=False, loads=None):
+ """
+ Create and register :sql:`json` typecasters for PostgreSQL 9.2 and following.
+
+ Since PostgreSQL 9.2 :sql:`json` is a builtin type, hence its oid is known
+ and fixed. This function allows specifying a customized *loads* function
+ for the default :sql:`json` type without querying the database.
+ All the parameters have the same meaning of `register_json()`.
+ """
+ return register_json(conn_or_curs=conn_or_curs, globally=globally,
+ loads=loads, oid=JSON_OID, array_oid=JSONARRAY_OID)
+
+def _create_json_typecasters(oid, array_oid, loads=None):
+ """Create typecasters for json data type."""
+ if loads is None:
+ if json is None:
+ raise ImportError("no json module available")
+ else:
+ loads = json.loads
+
+ def typecast_json(s, cur):
+ if s is None:
+ return None
+ return loads(s)
+
+ JSON = new_type((oid, ), 'JSON', typecast_json)
+ if array_oid is not None:
+ JSONARRAY = new_array_type((array_oid, ), "JSONARRAY", JSON)
+ else:
+ JSONARRAY = None
+
+ return JSON, JSONARRAY
+
+def _get_json_oids(conn_or_curs):
+ # lazy imports
+ from psycopg2.extensions import STATUS_IN_TRANSACTION
+ from psycopg2.extras import _solve_conn_curs
+
+ conn, curs = _solve_conn_curs(conn_or_curs)
+
+ # 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"
+
+ # get the oid for the hstore
+ curs.execute(
+ "SELECT t.oid, %s FROM pg_type t WHERE t.typname = 'json';"
+ % typarray)
+ r = curs.fetchone()
+
+ # revert the status of the connection as before the command
+ if (conn_status != STATUS_IN_TRANSACTION and not conn.autocommit):
+ conn.rollback()
+
+ if not r:
+ raise conn.ProgrammingError("json data type not found")
+
+ return r
+
+
+
diff --git a/lib/_range.py b/lib/_range.py
new file mode 100644
index 0000000..25600d8
--- /dev/null
+++ b/lib/_range.py
@@ -0,0 +1,468 @@
+"""Implementation of the Range type and adaptation
+
+"""
+
+# psycopg/_range.py - Implementation of the Range type and adaptation
+#
+# Copyright (C) 2012 Daniele Varrazzo <daniele.varrazzo@gmail.com>
+#
+# 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 re
+
+from psycopg2._psycopg import ProgrammingError, InterfaceError
+from psycopg2.extensions import ISQLQuote, adapt, register_adapter, b
+from psycopg2.extensions import new_type, new_array_type, register_type
+
+class Range(object):
+ """Python representation for a PostgreSQL |range|_ type.
+
+ :param lower: lower bound for the range. `!None` means unbound
+ :param upper: upper bound for the range. `!None` means unbound
+ :param bounds: one of the literal strings ``()``, ``[)``, ``(]``, ``[]``,
+ representing whether the lower or upper bounds are included
+ :param empty: if `!True`, the range is empty
+
+ """
+ __slots__ = ('_lower', '_upper', '_bounds')
+
+ def __init__(self, lower=None, upper=None, bounds='[)', empty=False):
+ if not empty:
+ if bounds not in ('[)', '(]', '()', '[]'):
+ raise ValueError("bound flags not valid: %r" % bounds)
+
+ self._lower = lower
+ self._upper = upper
+ self._bounds = bounds
+ else:
+ self._lower = self._upper = self._bounds = None
+
+ def __repr__(self):
+ if self._bounds is None:
+ return "%s(empty=True)" % self.__class__.__name__
+ else:
+ return "%s(%r, %r, %r)" % (self.__class__.__name__,
+ self._lower, self._upper, self._bounds)
+
+ @property
+ def lower(self):
+ """The lower bound of the range. `!None` if empty or unbound."""
+ return self._lower
+
+ @property
+ def upper(self):
+ """The upper bound of the range. `!None` if empty or unbound."""
+ return self._upper
+
+ @property
+ def isempty(self):
+ """`!True` if the range is empty."""
+ return self._bounds is None
+
+ @property
+ def lower_inf(self):
+ """`!True` if the range doesn't have a lower bound."""
+ if self._bounds is None: return False
+ return self._lower is None
+
+ @property
+ def upper_inf(self):
+ """`!True` if the range doesn't have an upper bound."""
+ if self._bounds is None: return False
+ return self._upper is None
+
+ @property
+ def lower_inc(self):
+ """`!True` if the lower bound is included in the range."""
+ if self._bounds is None: return False
+ if self._lower is None: return False
+ return self._bounds[0] == '['
+
+ @property
+ def upper_inc(self):
+ """`!True` if the upper bound is included in the range."""
+ if self._bounds is None: return False
+ if self._upper is None: return False
+ return self._bounds[1] == ']'
+
+ def __contains__(self, x):
+ if self._bounds is None: return False
+ if self._lower is not None:
+ if self._bounds[0] == '[':
+ if x < self._lower: return False
+ else:
+ if x <= self._lower: return False
+
+ if self._upper is not None:
+ if self._bounds[1] == ']':
+ if x > self._upper: return False
+ else:
+ if x >= self._upper: return False
+
+ return True
+
+ def __nonzero__(self):
+ return self._bounds is not None
+
+ def __eq__(self, other):
+ return (self._lower == other._lower
+ and self._upper == other._upper
+ and self._bounds == other._bounds)
+
+ def __ne__(self, other):
+ return not self.__eq__(other)
+
+ def __hash__(self):
+ return hash((self._lower, self._upper, self._bounds))
+
+ def __lt__(self, other):
+ raise TypeError(
+ 'Range objects cannot be ordered; please refer to the PostgreSQL'
+ ' documentation to perform this operation in the database')
+
+ __le__ = __gt__ = __ge__ = __lt__
+
+
+def register_range(pgrange, pyrange, conn_or_curs, globally=False):
+ """Create and register an adapter and the typecasters to convert between
+ a PostgreSQL |range|_ type and a PostgreSQL `Range` subclass.
+
+ :param pgrange: the name of the PostgreSQL |range| type. Can be
+ schema-qualified
+ :param pyrange: a `Range` strict subclass, or just a name to give to a new
+ class
+ :param conn_or_curs: a connection or cursor used to find the oid of the
+ range and its subtype; 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: `RangeCaster` instance responsible for the conversion
+
+ If a string is passed to *pyrange*, a new `Range` subclass is created
+ with such name and will be available as the `~RangeCaster.range` attribute
+ of the returned `RangeCaster` object.
+
+ The function queries the database on *conn_or_curs* to inspect the
+ *pgrange* type and raises `~psycopg2.ProgrammingError` if the type is not
+ found. If querying the database is not advisable, use directly the
+ `RangeCaster` class and register the adapter and typecasters using the
+ provided functions.
+
+ """
+ caster = RangeCaster._from_db(pgrange, pyrange, conn_or_curs)
+ caster._register(not globally and conn_or_curs or None)
+ return caster
+
+
+class RangeAdapter(object):
+ """`ISQLQuote` adapter for `Range` subclasses.
+
+ This is an abstract class: concrete classes must set a `name` class
+ attribute or override `getquoted()`.
+ """
+ name = None
+
+ def __init__(self, adapted):
+ self.adapted = adapted
+
+ def __conform__(self, proto):
+ if self._proto is ISQLQuote:
+ return self
+
+ def prepare(self, conn):
+ self._conn = conn
+
+ def getquoted(self):
+ if self.name is None:
+ raise NotImplementedError(
+ 'RangeAdapter must be subclassed overriding its name '
+ 'or the getquoted() method')
+
+ r = self.adapted
+ if r.isempty:
+ return b("'empty'::" + self.name)
+
+ if r.lower is not None:
+ a = adapt(r.lower)
+ if hasattr(a, 'prepare'):
+ a.prepare(self._conn)
+ lower = a.getquoted()
+ else:
+ lower = b('NULL')
+
+ if r.upper is not None:
+ a = adapt(r.upper)
+ if hasattr(a, 'prepare'):
+ a.prepare(self._conn)
+ upper = a.getquoted()
+ else:
+ upper = b('NULL')
+
+ return b(self.name + '(') + lower + b(', ') + upper \
+ + b(", '%s')" % r._bounds)
+
+
+class RangeCaster(object):
+ """Helper class to convert between `Range` and PostgreSQL range types.
+
+ Objects of this class are usually created by `register_range()`. Manual
+ creation could be useful if querying the database is not advisable: in
+ this case the oids must be provided.
+ """
+ def __init__(self, pgrange, pyrange, oid, subtype_oid, array_oid=None):
+ self.subtype_oid = subtype_oid
+ self._create_ranges(pgrange, pyrange)
+
+ name = self.adapter.name or self.adapter.__class__.__name__
+
+ self.typecaster = new_type((oid,), name, self.parse)
+
+ if array_oid is not None:
+ self.array_typecaster = new_array_type(
+ (array_oid,), name + "ARRAY", self.typecaster)
+ else:
+ self.array_typecaster = None
+
+ def _create_ranges(self, pgrange, pyrange):
+ """Create Range and RangeAdapter classes if needed."""
+ # if got a string create a new RangeAdapter concrete type (with a name)
+ # else take it as an adapter. Passing an adapter should be considered
+ # an implementation detail and is not documented. It is currently used
+ # for the numeric ranges.
+ self.adapter = None
+ if isinstance(pgrange, basestring):
+ self.adapter = type(pgrange, (RangeAdapter,), {})
+ self.adapter.name = pgrange
+ else:
+ try:
+ if issubclass(pgrange, RangeAdapter) and pgrange is not RangeAdapter:
+ self.adapter = pgrange
+ except TypeError:
+ pass
+
+ if self.adapter is None:
+ raise TypeError(
+ 'pgrange must be a string or a RangeAdapter strict subclass')
+
+ self.range = None
+ try:
+ if isinstance(pyrange, basestring):
+ self.range = type(pyrange, (Range,), {})
+ if issubclass(pyrange, Range) and pyrange is not Range:
+ self.range = pyrange
+ except TypeError:
+ pass
+
+ if self.range is None:
+ raise TypeError(
+ 'pyrange must be a type or a Range strict subclass')
+
+ @classmethod
+ def _from_db(self, name, pyrange, conn_or_curs):
+ """Return a `RangeCaster` instance for the type *pgrange*.
+
+ Raise `ProgrammingError` if the type is not found.
+ """
+ from psycopg2.extensions import STATUS_IN_TRANSACTION
+ from psycopg2.extras import _solve_conn_curs
+ conn, curs = _solve_conn_curs(conn_or_curs)
+
+ if conn.server_version < 90200:
+ raise ProgrammingError("range types not available in version %s"
+ % conn.server_version)
+
+ # 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'
+
+ # get the type oid and attributes
+ try:
+ curs.execute("""\
+select rngtypid, rngsubtype,
+ (select typarray from pg_type where oid = rngtypid)
+from pg_range r
+join pg_type t on t.oid = rngtypid
+join pg_namespace ns on ns.oid = typnamespace
+where typname = %s and ns.nspname = %s;
+""", (tname, schema))
+
+ except ProgrammingError:
+ if not conn.autocommit:
+ conn.rollback()
+ raise
+ else:
+ rec = curs.fetchone()
+
+ # revert the status of the connection as before the command
+ if (conn_status != STATUS_IN_TRANSACTION
+ and not conn.autocommit):
+ conn.rollback()
+
+ if not rec:
+ raise ProgrammingError(
+ "PostgreSQL type '%s' not found" % name)
+
+ type, subtype, array = rec
+
+ return RangeCaster(name, pyrange,
+ oid=type, subtype_oid=subtype, array_oid=array)
+
+ _re_range = re.compile(r"""
+ ( \(|\[ ) # lower bound flag
+ (?: # lower bound:
+ " ( (?: [^"] | "")* ) " # - a quoted string
+ | ( [^",]+ ) # - or an unquoted string
+ )? # - or empty (not catched)
+ ,
+ (?: # upper bound:
+ " ( (?: [^"] | "")* ) " # - a quoted string
+ | ( [^"\)\]]+ ) # - or an unquoted string
+ )? # - or empty (not catched)
+ ( \)|\] ) # upper bound flag
+ """, re.VERBOSE)
+
+ _re_undouble = re.compile(r'(["\\])\1')
+
+ def parse(self, s, cur=None):
+ if s is None:
+ return None
+
+ if s == 'empty':
+ return self.range(empty=True)
+
+ m = self._re_range.match(s)
+ if m is None:
+ raise InterfaceError("failed to parse range: %s")
+
+ lower = m.group(3)
+ if lower is None:
+ lower = m.group(2)
+ if lower is not None:
+ lower = self._re_undouble.sub(r"\1", lower)
+
+ upper = m.group(5)
+ if upper is None:
+ upper = m.group(4)
+ if upper is not None:
+ upper = self._re_undouble.sub(r"\1", upper)
+
+ if cur is not None:
+ lower = cur.cast(self.subtype_oid, lower)
+ upper = cur.cast(self.subtype_oid, upper)
+
+ bounds = m.group(1) + m.group(6)
+
+ return self.range(lower, upper, bounds)
+
+ def _register(self, scope=None):
+ register_type(self.typecaster, scope)
+ if self.array_typecaster is not None:
+ register_type(self.array_typecaster, scope)
+
+ register_adapter(self.range, self.adapter)
+
+
+class NumericRange(Range):
+ """A `Range` suitable to pass Python numeric types to a PostgreSQL range.
+
+ PostgreSQL types :sql:`int4range`, :sql:`int8range`, :sql:`numrange` are
+ casted into `!NumericRange` instances.
+ """
+ pass
+
+class DateRange(Range):
+ """Represents :sql:`daterange` values."""
+ pass
+
+class DateTimeRange(Range):
+ """Represents :sql:`tsrange` values."""
+ pass
+
+class DateTimeTZRange(Range):
+ """Represents :sql:`tstzrange` values."""
+ pass
+
+
+# Special adaptation for NumericRange. Allows to pass number range regardless
+# of whether they are ints, floats and what size of ints are, which are
+# pointless in Python world. On the way back, no numeric range is casted to
+# NumericRange, but only to their subclasses
+
+class NumberRangeAdapter(RangeAdapter):
+ """Adapt a range if the subtype doesn't need quotes."""
+ def getquoted(self):
+ r = self.adapted
+ if r.isempty:
+ return "'empty'"
+
+ if not r.lower_inf:
+ # not exactly: we are relying that none of these object is really
+ # quoted (they are numbers). Also, I'm lazy and not preparing the
+ # adapter because I assume encoding doesn't matter for these
+ # objects.
+ lower = adapt(r.lower).getquoted().decode('ascii')
+ else:
+ lower = ''
+
+ if not r.upper_inf:
+ upper = adapt(r.upper).getquoted().decode('ascii')
+ else:
+ upper = ''
+
+ return b("'%s%s,%s%s'" % (
+ r._bounds[0], lower, upper, r._bounds[1]))
+
+# TODO: probably won't work with infs, nans and other tricky cases.
+register_adapter(NumericRange, NumberRangeAdapter)
+
+
+# Register globally typecasters and adapters for builtin range types.
+
+# note: the adapter is registered more than once, but this is harmless.
+int4range_caster = RangeCaster(NumberRangeAdapter, NumericRange,
+ oid=3904, subtype_oid=23, array_oid=3905)
+int4range_caster._register()
+
+int8range_caster = RangeCaster(NumberRangeAdapter, NumericRange,
+ oid=3926, subtype_oid=20, array_oid=3927)
+int8range_caster._register()
+
+numrange_caster = RangeCaster(NumberRangeAdapter, NumericRange,
+ oid=3906, subtype_oid=1700, array_oid=3907)
+numrange_caster._register()
+
+daterange_caster = RangeCaster('daterange', DateRange,
+ oid=3912, subtype_oid=1082, array_oid=3913)
+daterange_caster._register()
+
+tsrange_caster = RangeCaster('tsrange', DateTimeRange,
+ oid=3908, subtype_oid=1114, array_oid=3909)
+tsrange_caster._register()
+
+tstzrange_caster = RangeCaster('tstzrange', DateTimeTZRange,
+ oid=3910, subtype_oid=1184, array_oid=3911)
+tstzrange_caster._register()
+
+
diff --git a/lib/errorcodes.py b/lib/errorcodes.py
index 4a4ee1c..12c300f 100644
--- a/lib/errorcodes.py
+++ b/lib/errorcodes.py
@@ -59,6 +59,7 @@ CLASS_INVALID_TRANSACTION_INITIATION = '0B'
CLASS_LOCATOR_EXCEPTION = '0F'
CLASS_INVALID_GRANTOR = '0L'
CLASS_INVALID_ROLE_SPECIFICATION = '0P'
+CLASS_DIAGNOSTICS_EXCEPTION = '0Z'
CLASS_CASE_NOT_FOUND = '20'
CLASS_CARDINALITY_VIOLATION = '21'
CLASS_DATA_EXCEPTION = '22'
@@ -139,6 +140,10 @@ INVALID_GRANT_OPERATION = '0LP01'
# Class 0P - Invalid Role Specification
INVALID_ROLE_SPECIFICATION = '0P000'
+# Class 0Z - Diagnostics Exception
+DIAGNOSTICS_EXCEPTION = '0Z000'
+STACKED_DIAGNOSTICS_ACCESSED_WITHOUT_ACTIVE_HANDLER = '0Z002'
+
# Class 20 - Case Not Found
CASE_NOT_FOUND = '20000'
@@ -331,6 +336,7 @@ INSUFFICIENT_RESOURCES = '53000'
DISK_FULL = '53100'
OUT_OF_MEMORY = '53200'
TOO_MANY_CONNECTIONS = '53300'
+CONFIGURATION_LIMIT_EXCEEDED = '53400'
# Class 54 - Program Limit Exceeded
PROGRAM_LIMIT_EXCEEDED = '54000'
@@ -353,6 +359,7 @@ CANNOT_CONNECT_NOW = '57P03'
DATABASE_DROPPED = '57P04'
# Class 58 - System Error (errors external to PostgreSQL itself)
+SYSTEM_ERROR = '58000'
IO_ERROR = '58030'
UNDEFINED_FILE = '58P01'
DUPLICATE_FILE = '58P02'
diff --git a/lib/extensions.py b/lib/extensions.py
index bdcfdf2..f499e48 100644
--- a/lib/extensions.py
+++ b/lib/extensions.py
@@ -58,7 +58,7 @@ except ImportError:
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 psycopg2._psycopg import ISQLQuote, Notify, Diagnostics
from psycopg2._psycopg import QueryCanceledError, TransactionRollbackError
@@ -151,6 +151,21 @@ class NoneAdapter(object):
return _null
+# Create default json typecasters for PostgreSQL 9.2 oids
+from psycopg2._json import register_default_json
+
+try:
+ JSON, JSONARRAY = register_default_json()
+except ImportError:
+ pass
+
+del register_default_json
+
+
+# Create default Range typecasters
+from psycopg2. _range import Range
+del Range
+
# 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
@@ -160,5 +175,3 @@ for k, v in encodings.items():
encodings[k] = v
del k, v
-
-__all__ = filter(lambda k: not k.startswith('_'), locals().keys())
diff --git a/lib/extras.py b/lib/extras.py
index 214f528..33dd83d 100644
--- a/lib/extras.py
+++ b/lib/extras.py
@@ -25,16 +25,15 @@ and classes untill a better place in the distribution is found.
# 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
+import os as _os
+import sys as _sys
+import time as _time
+import re as _re
try:
- import logging
+ import logging as _logging
except:
- logging = None
+ _logging = None
import psycopg2
from psycopg2 import extensions as _ext
@@ -192,7 +191,7 @@ class DictRow(list):
self._index = data[1]
# drop the crusty Py2 methods
- if sys.version_info[0] > 2:
+ if _sys.version_info[0] > 2:
items = iteritems; del iteritems
keys = iterkeys; del iterkeys
values = itervalues; del itervalues
@@ -302,21 +301,21 @@ class NamedTupleCursor(_cursor):
nt = self.Record
if nt is None:
nt = self.Record = self._make_nt()
- return nt(*t)
+ return nt._make(t)
def fetchmany(self, size=None):
ts = super(NamedTupleCursor, self).fetchmany(size)
nt = self.Record
if nt is None:
nt = self.Record = self._make_nt()
- return [nt(*t) for t in ts]
+ return map(nt._make, ts)
def fetchall(self):
ts = super(NamedTupleCursor, self).fetchall()
nt = self.Record
if nt is None:
nt = self.Record = self._make_nt()
- return [nt(*t) for t in ts]
+ return map(nt._make, ts)
def __iter__(self):
it = super(NamedTupleCursor, self).__iter__()
@@ -326,10 +325,10 @@ class NamedTupleCursor(_cursor):
if nt is None:
nt = self.Record = self._make_nt()
- yield nt(*t)
+ yield nt._make(t)
while 1:
- yield nt(*it.next())
+ yield nt._make(it.next())
try:
from collections import namedtuple
@@ -354,7 +353,7 @@ class LoggingConnection(_connection):
instance from the standard logging module.
"""
self._logobj = logobj
- if logging and isinstance(logobj, logging.Logger):
+ if _logging and isinstance(logobj, _logging.Logger):
self.log = self._logtologger
else:
self.log = self._logtofile
@@ -370,7 +369,7 @@ class LoggingConnection(_connection):
def _logtofile(self, msg, curs):
msg = self.filter(msg, curs)
- if msg: self._logobj.write(msg + os.linesep)
+ if msg: self._logobj.write(msg + _os.linesep)
def _logtologger(self, msg, curs):
msg = self.filter(msg, curs)
@@ -418,9 +417,9 @@ class MinTimeLoggingConnection(LoggingConnection):
self._mintime = mintime
def filter(self, msg, curs):
- t = (time.time() - curs.timestamp) * 1000
+ t = (_time.time() - curs.timestamp) * 1000
if t > self._mintime:
- return msg + os.linesep + " (execution time: %d ms)" % t
+ return msg + _os.linesep + " (execution time: %d ms)" % t
def cursor(self, *args, **kwargs):
kwargs.setdefault('cursor_factory', MinTimeLoggingCursor)
@@ -430,11 +429,11 @@ class MinTimeLoggingCursor(LoggingCursor):
"""The cursor sub-class companion to `MinTimeLoggingConnection`."""
def execute(self, query, vars=None):
- self.timestamp = time.time()
+ self.timestamp = _time.time()
return LoggingCursor.execute(self, query, vars)
def callproc(self, procname, vars=None):
- self.timestamp = time.time()
+ self.timestamp = _time.time()
return LoggingCursor.execute(self, procname, vars)
@@ -558,20 +557,21 @@ def register_tstz_w_secs(oids=None, conn_or_curs=None):
These are now correctly handled by the default type caster, so currently
the function doesn't do anything.
"""
+ import warnings
warnings.warn("deprecated", DeprecationWarning)
-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.
+ `~psycopg2.extensions.set_wait_callback()`. This function uses
+ :py:func:`~select.select()` to wait for data available.
+
"""
+ import select
+ from psycopg2.extensions import POLL_OK, POLL_READ, POLL_WRITE
+
while 1:
state = conn.poll()
if state == POLL_OK:
@@ -581,11 +581,14 @@ def wait_select(conn):
elif state == POLL_WRITE:
select.select([], [conn.fileno()], [])
else:
- raise OperationalError("bad state from poll: %s" % state)
+ raise conn.OperationalError("bad state from poll: %s" % state)
def _solve_conn_curs(conn_or_curs):
"""Return the connection and a DBAPI cursor from a connection or cursor."""
+ if conn_or_curs is None:
+ raise psycopg2.ProgrammingError("no connection or cursor provided")
+
if hasattr(conn_or_curs, 'execute'):
conn = conn_or_curs.connection
curs = conn.cursor(cursor_factory=_cursor)
@@ -645,7 +648,7 @@ class HstoreAdapter(object):
getquoted = _getquoted_9
- _re_hstore = regex.compile(r"""
+ _re_hstore = _re.compile(r"""
# hstore key:
# a string of normal or escaped chars
"((?: [^"\\] | \\. )*)"
@@ -656,10 +659,10 @@ class HstoreAdapter(object):
| "((?: [^"\\] | \\. )*)"
)
(?:\s*,\s*|$) # pairs separated by comma or end of string.
- """, regex.VERBOSE)
+ """, _re.VERBOSE)
@classmethod
- def parse(self, s, cur, _bsdec=regex.compile(r"\\(.)")):
+ def parse(self, s, cur, _bsdec=_re.compile(r"\\(.)")):
"""Parse an hstore representation in a Python string.
The hstore is represented as something like::
@@ -783,7 +786,7 @@ def register_hstore(conn_or_curs, globally=False, unicode=False,
array_oid = tuple([x for x in array_oid if x])
# create and register the typecaster
- if sys.version_info[0] < 3 and unicode:
+ if _sys.version_info[0] < 3 and unicode:
cast = HstoreAdapter.parse_unicode
else:
cast = HstoreAdapter.parse
@@ -805,35 +808,10 @@ class CompositeCaster(object):
querying the database at registration time is not desirable (such as when
using an :ref:`asynchronous connections <async-support>`).
- .. 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):
+ def __init__(self, name, oid, attrs, array_oid=None, schema=None):
self.name = name
+ self.schema = schema
self.oid = oid
self.array_oid = array_oid
@@ -857,17 +835,30 @@ class CompositeCaster(object):
"expecting %d components for the type %s, %d found instead" %
(len(self.atttypes), self.name, len(tokens)))
- attrs = [ curs.cast(oid, token)
+ values = [ curs.cast(oid, token)
for oid, token in zip(self.atttypes, tokens) ]
- return self._ctor(*attrs)
- _re_tokenize = regex.compile(r"""
+ return self.make(values)
+
+ def make(self, values):
+ """Return a new Python object representing the data being casted.
+
+ *values* is the list of attributes, already casted into their Python
+ representation.
+
+ You can subclass this method to :ref:`customize the composite cast
+ <custom-composite>`.
+ """
+
+ return self._ctor(values)
+
+ _re_tokenize = _re.compile(r"""
\(? ([,)]) # an empty token, representing NULL
| \(? " ((?: [^"] | "")*) " [,)] # or a quoted string
| \(? ([^",)]+) [,)] # or an unquoted string
- """, regex.VERBOSE)
+ """, _re.VERBOSE)
- _re_undouble = regex.compile(r'(["\\])\1')
+ _re_undouble = _re.compile(r'(["\\])\1')
@classmethod
def tokenize(self, s):
@@ -889,10 +880,10 @@ class CompositeCaster(object):
from collections import namedtuple
except ImportError:
self.type = tuple
- self._ctor = lambda *args: tuple(args)
+ self._ctor = self.type
else:
self.type = namedtuple(name, attnames)
- self._ctor = self.type
+ self._ctor = self.type._make
@classmethod
def _from_db(self, name, conn_or_curs):
@@ -941,10 +932,10 @@ ORDER BY attnum;
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)
+ return self(tname, type_oid, type_attrs,
+ array_oid=array_oid, schema=schema)
-def register_composite(name, conn_or_curs, globally=False):
+def register_composite(name, conn_or_curs, globally=False, factory=None):
"""Register a typecaster to convert a composite type into a tuple.
:param name: the name of a PostgreSQL composite type, e.g. created using
@@ -954,14 +945,15 @@ def register_composite(name, conn_or_curs, globally=False):
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
-
+ :param factory: if specified it should be a `CompositeCaster` subclass: use
+ it to :ref:`customize how to cast composite types <custom-composite>`
+ :return: the registered `CompositeCaster` or *factory* instance
+ responsible for the conversion
"""
- caster = CompositeCaster._from_db(name, conn_or_curs)
+ if factory is None:
+ factory = CompositeCaster
+
+ caster = factory._from_db(name, conn_or_curs)
_ext.register_type(caster.typecaster, not globally and conn_or_curs or None)
if caster.array_typecaster is not None:
@@ -970,4 +962,11 @@ def register_composite(name, conn_or_curs, globally=False):
return caster
-__all__ = filter(lambda k: not k.startswith('_'), locals().keys())
+# expose the json adaptation stuff into the module
+from psycopg2._json import json, Json, register_json, register_default_json
+
+
+# Expose range-related objects
+from psycopg2._range import Range, NumericRange
+from psycopg2._range import DateRange, DateTimeRange, DateTimeTZRange
+from psycopg2._range import register_range, RangeAdapter, RangeCaster
diff --git a/lib/pool.py b/lib/pool.py
index f05767a..3b41c80 100644
--- a/lib/pool.py
+++ b/lib/pool.py
@@ -27,30 +27,6 @@ This module implements thread-safe (and not) connection pools.
import psycopg2
import psycopg2.extensions as _ext
-try:
- import logging
- # create logger object for psycopg2 module and sub-modules
- _logger = logging.getLogger("psycopg2")
- def dbg(*args):
- _logger.debug("psycopg2", ' '.join([str(x) for x in args]))
- try:
- import App # does this make sure that we're running in Zope?
- _logger.info("installed. Logging using Python logging module")
- except:
- _logger.debug("installed. Logging using Python logging module")
-
-except ImportError:
- from zLOG import LOG, DEBUG, INFO
- def dbg(*args):
- LOG('ZPsycopgDA', DEBUG, "",
- ' '.join([str(x) for x in args])+'\n')
- LOG('ZPsycopgDA', INFO, "Installed", "Logging using Zope's zLOG\n")
-
-except:
- import sys
- def dbg(*args):
- sys.stderr.write(' '.join(args)+'\n')
-
class PoolError(psycopg2.Error):
pass
diff --git a/psycopg/adapter_asis.c b/psycopg/adapter_asis.c
index f194e1d..a2a8f4d 100644
--- a/psycopg/adapter_asis.c
+++ b/psycopg/adapter_asis.c
@@ -117,13 +117,6 @@ asis_setup(asisObject *self, PyObject *obj)
return 0;
}
-static int
-asis_traverse(asisObject *self, visitproc visit, void *arg)
-{
- Py_VISIT(self->wrapped);
- return 0;
-}
-
static void
asis_dealloc(PyObject* obj)
{
@@ -156,12 +149,6 @@ asis_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
return type->tp_alloc(type, 0);
}
-static void
-asis_del(PyObject* self)
-{
- PyObject_GC_Del(self);
-}
-
static PyObject *
asis_repr(asisObject *self)
{
@@ -177,63 +164,41 @@ asis_repr(asisObject *self)
PyTypeObject asisType = {
PyVarObject_HEAD_INIT(NULL, 0)
"psycopg2._psycopg.AsIs",
- sizeof(asisObject),
- 0,
+ sizeof(asisObject), 0,
asis_dealloc, /*tp_dealloc*/
0, /*tp_print*/
-
0, /*tp_getattr*/
0, /*tp_setattr*/
-
0, /*tp_compare*/
-
(reprfunc)asis_repr, /*tp_repr*/
0, /*tp_as_number*/
0, /*tp_as_sequence*/
0, /*tp_as_mapping*/
0, /*tp_hash */
-
0, /*tp_call*/
(reprfunc)asis_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*/
+ Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /*tp_flags*/
asisType_doc, /*tp_doc*/
-
- (traverseproc)asis_traverse, /*tp_traverse*/
+ 0, /*tp_traverse*/
0, /*tp_clear*/
-
0, /*tp_richcompare*/
0, /*tp_weaklistoffset*/
-
0, /*tp_iter*/
0, /*tp_iternext*/
-
- /* Attribute descriptor and subclassing stuff */
-
asisObject_methods, /*tp_methods*/
asisObject_members, /*tp_members*/
0, /*tp_getset*/
0, /*tp_base*/
0, /*tp_dict*/
-
0, /*tp_descr_get*/
0, /*tp_descr_set*/
0, /*tp_dictoffset*/
-
asis_init, /*tp_init*/
- 0, /*tp_alloc will be set to PyType_GenericAlloc in module init*/
+ 0, /*tp_alloc*/
asis_new, /*tp_new*/
- (freefunc)asis_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 --git a/psycopg/adapter_binary.c b/psycopg/adapter_binary.c
index b08c144..3edcea8 100644
--- a/psycopg/adapter_binary.c
+++ b/psycopg/adapter_binary.c
@@ -159,8 +159,7 @@ binary_prepare(binaryObject *self, PyObject *args)
self->conn = conn;
Py_INCREF(self->conn);
- Py_INCREF(Py_None);
- return Py_None;
+ Py_RETURN_NONE;
}
static PyObject *
@@ -221,17 +220,6 @@ binary_setup(binaryObject *self, PyObject *str)
return 0;
}
-static int
-binary_traverse(PyObject *obj, visitproc visit, void *arg)
-{
- binaryObject *self = (binaryObject *)obj;
-
- Py_VISIT(self->wrapped);
- Py_VISIT(self->buffer);
- Py_VISIT(self->conn);
- return 0;
-}
-
static void
binary_dealloc(PyObject* obj)
{
@@ -266,12 +254,6 @@ binary_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
return type->tp_alloc(type, 0);
}
-static void
-binary_del(PyObject* self)
-{
- PyObject_GC_Del(self);
-}
-
static PyObject *
binary_repr(binaryObject *self)
{
@@ -286,61 +268,41 @@ binary_repr(binaryObject *self)
PyTypeObject binaryType = {
PyVarObject_HEAD_INIT(NULL, 0)
"psycopg2._psycopg.Binary",
- sizeof(binaryObject),
- 0,
+ sizeof(binaryObject), 0,
binary_dealloc, /*tp_dealloc*/
0, /*tp_print*/
0, /*tp_getattr*/
0, /*tp_setattr*/
-
0, /*tp_compare*/
(reprfunc)binary_repr, /*tp_repr*/
0, /*tp_as_number*/
0, /*tp_as_sequence*/
0, /*tp_as_mapping*/
0, /*tp_hash */
-
0, /*tp_call*/
(reprfunc)binary_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*/
-
+ Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /*tp_flags*/
binaryType_doc, /*tp_doc*/
-
- binary_traverse, /*tp_traverse*/
+ 0, /*tp_traverse*/
0, /*tp_clear*/
-
0, /*tp_richcompare*/
0, /*tp_weaklistoffset*/
-
0, /*tp_iter*/
0, /*tp_iternext*/
-
- /* Attribute descriptor and subclassing stuff */
-
binaryObject_methods, /*tp_methods*/
binaryObject_members, /*tp_members*/
0, /*tp_getset*/
0, /*tp_base*/
0, /*tp_dict*/
-
0, /*tp_descr_get*/
0, /*tp_descr_set*/
0, /*tp_dictoffset*/
-
binary_init, /*tp_init*/
- 0, /*tp_alloc will be set to PyType_GenericAlloc in module init*/
+ 0, /*tp_alloc*/
binary_new, /*tp_new*/
- (freefunc)binary_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 --git a/psycopg/adapter_datetime.c b/psycopg/adapter_datetime.c
index bf31cfa..67697fb 100644
--- a/psycopg/adapter_datetime.c
+++ b/psycopg/adapter_datetime.c
@@ -183,15 +183,6 @@ pydatetime_setup(pydatetimeObject *self, PyObject *obj, int type)
return 0;
}
-static int
-pydatetime_traverse(PyObject *obj, visitproc visit, void *arg)
-{
- pydatetimeObject *self = (pydatetimeObject *)obj;
-
- Py_VISIT(self->wrapped);
- return 0;
-}
-
static void
pydatetime_dealloc(PyObject* obj)
{
@@ -223,12 +214,6 @@ pydatetime_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
return type->tp_alloc(type, 0);
}
-static void
-pydatetime_del(PyObject* self)
-{
- PyObject_GC_Del(self);
-}
-
static PyObject *
pydatetime_repr(pydatetimeObject *self)
{
@@ -244,61 +229,41 @@ pydatetime_repr(pydatetimeObject *self)
PyTypeObject pydatetimeType = {
PyVarObject_HEAD_INIT(NULL, 0)
"psycopg2._psycopg.datetime",
- sizeof(pydatetimeObject),
- 0,
+ sizeof(pydatetimeObject), 0,
pydatetime_dealloc, /*tp_dealloc*/
0, /*tp_print*/
0, /*tp_getattr*/
0, /*tp_setattr*/
-
0, /*tp_compare*/
(reprfunc)pydatetime_repr, /*tp_repr*/
0, /*tp_as_number*/
0, /*tp_as_sequence*/
0, /*tp_as_mapping*/
0, /*tp_hash */
-
0, /*tp_call*/
(reprfunc)pydatetime_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*/
-
+ Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /*tp_flags*/
pydatetimeType_doc, /*tp_doc*/
-
- pydatetime_traverse, /*tp_traverse*/
+ 0, /*tp_traverse*/
0, /*tp_clear*/
-
0, /*tp_richcompare*/
0, /*tp_weaklistoffset*/
-
0, /*tp_iter*/
0, /*tp_iternext*/
-
- /* Attribute descriptor and subclassing stuff */
-
pydatetimeObject_methods, /*tp_methods*/
pydatetimeObject_members, /*tp_members*/
0, /*tp_getset*/
0, /*tp_base*/
0, /*tp_dict*/
-
0, /*tp_descr_get*/
0, /*tp_descr_set*/
0, /*tp_dictoffset*/
-
pydatetime_init, /*tp_init*/
- 0, /*tp_alloc will be set to PyType_GenericAlloc in module init*/
+ 0, /*tp_alloc*/
pydatetime_new, /*tp_new*/
- (freefunc)pydatetime_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 --git a/psycopg/adapter_list.c b/psycopg/adapter_list.c
index d97ecfb..1198a81 100644
--- a/psycopg/adapter_list.c
+++ b/psycopg/adapter_list.c
@@ -103,16 +103,11 @@ list_prepare(listObject *self, PyObject *args)
if (!PyArg_ParseTuple(args, "O!", &connectionType, &conn))
return NULL;
- /* note that we don't copy the encoding from the connection, but take a
- reference to it; we'll need it during the recursive adapt() call (the
- encoding is here for a future expansion that will make .getquoted()
- work even without a connection to the backend. */
Py_CLEAR(self->connection);
Py_INCREF(conn);
self->connection = conn;
- Py_INCREF(Py_None);
- return Py_None;
+ Py_RETURN_NONE;
}
static PyObject *
@@ -154,7 +149,7 @@ static PyMethodDef listObject_methods[] = {
/* initialization and finalization methods */
static int
-list_setup(listObject *self, PyObject *obj, const char *enc)
+list_setup(listObject *self, PyObject *obj)
{
Dprintf("list_setup: init list object at %p, refcnt = "
FORMAT_CODE_PY_SSIZE_T,
@@ -164,9 +159,6 @@ list_setup(listObject *self, PyObject *obj, const char *enc)
if (!PyList_Check(obj))
return -1;
- /* FIXME: remove this orrible strdup */
- if (enc) self->encoding = strdup(enc);
-
self->connection = NULL;
Py_INCREF(obj);
self->wrapped = obj;
@@ -179,40 +171,42 @@ list_setup(listObject *self, PyObject *obj, const char *enc)
}
static int
-list_traverse(PyObject *obj, visitproc visit, void *arg)
+list_traverse(listObject *self, visitproc visit, void *arg)
{
- listObject *self = (listObject *)obj;
-
Py_VISIT(self->wrapped);
Py_VISIT(self->connection);
return 0;
}
-static void
-list_dealloc(PyObject* obj)
+static int
+list_clear(listObject *self)
{
- listObject *self = (listObject *)obj;
-
Py_CLEAR(self->wrapped);
Py_CLEAR(self->connection);
- if (self->encoding) free(self->encoding);
+ return 0;
+}
+
+static void
+list_dealloc(listObject* self)
+{
+ PyObject_GC_UnTrack((PyObject *)self);
+ list_clear(self);
Dprintf("list_dealloc: deleted list object at %p, "
- "refcnt = " FORMAT_CODE_PY_SSIZE_T, obj, Py_REFCNT(obj));
+ "refcnt = " FORMAT_CODE_PY_SSIZE_T, self, Py_REFCNT(self));
- Py_TYPE(obj)->tp_free(obj);
+ Py_TYPE(self)->tp_free((PyObject *)self);
}
static int
list_init(PyObject *obj, PyObject *args, PyObject *kwds)
{
PyObject *l;
- const char *enc = "latin-1"; /* default encoding as in Python */
- if (!PyArg_ParseTuple(args, "O|s", &l, &enc))
+ if (!PyArg_ParseTuple(args, "O", &l))
return -1;
- return list_setup((listObject *)obj, l, enc);
+ return list_setup((listObject *)obj, l);
}
static PyObject *
@@ -221,12 +215,6 @@ list_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
return type->tp_alloc(type, 0);
}
-static void
-list_del(PyObject* self)
-{
- PyObject_GC_Del(self);
-}
-
static PyObject *
list_repr(listObject *self)
{
@@ -241,61 +229,41 @@ list_repr(listObject *self)
PyTypeObject listType = {
PyVarObject_HEAD_INIT(NULL, 0)
"psycopg2._psycopg.List",
- sizeof(listObject),
- 0,
- list_dealloc, /*tp_dealloc*/
+ sizeof(listObject), 0,
+ (destructor)list_dealloc, /*tp_dealloc*/
0, /*tp_print*/
0, /*tp_getattr*/
0, /*tp_setattr*/
-
0, /*tp_compare*/
(reprfunc)list_repr, /*tp_repr*/
0, /*tp_as_number*/
0, /*tp_as_sequence*/
0, /*tp_as_mapping*/
0, /*tp_hash */
-
0, /*tp_call*/
(reprfunc)list_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*/
-
listType_doc, /*tp_doc*/
-
- list_traverse, /*tp_traverse*/
- 0, /*tp_clear*/
-
+ (traverseproc)list_traverse, /*tp_traverse*/
+ (inquiry)list_clear, /*tp_clear*/
0, /*tp_richcompare*/
0, /*tp_weaklistoffset*/
-
0, /*tp_iter*/
0, /*tp_iternext*/
-
- /* Attribute descriptor and subclassing stuff */
-
listObject_methods, /*tp_methods*/
listObject_members, /*tp_members*/
0, /*tp_getset*/
0, /*tp_base*/
0, /*tp_dict*/
-
0, /*tp_descr_get*/
0, /*tp_descr_set*/
0, /*tp_dictoffset*/
-
list_init, /*tp_init*/
- 0, /*tp_alloc will be set to PyType_GenericAlloc in module init*/
+ 0, /*tp_alloc*/
list_new, /*tp_new*/
- (freefunc)list_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*/
};
@@ -305,10 +273,9 @@ PyObject *
psyco_List(PyObject *module, PyObject *args)
{
PyObject *str;
- const char *enc = "latin-1"; /* default encoding as in Python */
- if (!PyArg_ParseTuple(args, "O|s", &str, &enc))
+ if (!PyArg_ParseTuple(args, "O", &str))
return NULL;
- return PyObject_CallFunction((PyObject *)&listType, "Os", str, enc);
+ return PyObject_CallFunctionObjArgs((PyObject *)&listType, "O", str, NULL);
}
diff --git a/psycopg/adapter_list.h b/psycopg/adapter_list.h
index d713610..8ec20f6 100644
--- a/psycopg/adapter_list.h
+++ b/psycopg/adapter_list.h
@@ -37,7 +37,6 @@ typedef struct {
PyObject *wrapped;
PyObject *connection;
- char *encoding;
} listObject;
HIDDEN PyObject *psyco_List(PyObject *module, PyObject *args);
diff --git a/psycopg/adapter_mxdatetime.c b/psycopg/adapter_mxdatetime.c
index abe73f8..4696a9d 100644
--- a/psycopg/adapter_mxdatetime.c
+++ b/psycopg/adapter_mxdatetime.c
@@ -172,15 +172,6 @@ mxdatetime_setup(mxdatetimeObject *self, PyObject *obj, int type)
return 0;
}
-static int
-mxdatetime_traverse(PyObject *obj, visitproc visit, void *arg)
-{
- mxdatetimeObject *self = (mxdatetimeObject *)obj;
-
- Py_VISIT(self->wrapped);
- return 0;
-}
-
static void
mxdatetime_dealloc(PyObject* obj)
{
@@ -214,12 +205,6 @@ mxdatetime_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
return type->tp_alloc(type, 0);
}
-static void
-mxdatetime_del(PyObject* self)
-{
- PyObject_GC_Del(self);
-}
-
static PyObject *
mxdatetime_repr(mxdatetimeObject *self)
{
@@ -235,61 +220,41 @@ mxdatetime_repr(mxdatetimeObject *self)
PyTypeObject mxdatetimeType = {
PyVarObject_HEAD_INIT(NULL, 0)
"psycopg2._psycopg.MxDateTime",
- sizeof(mxdatetimeObject),
- 0,
+ sizeof(mxdatetimeObject), 0,
mxdatetime_dealloc, /*tp_dealloc*/
0, /*tp_print*/
0, /*tp_getattr*/
0, /*tp_setattr*/
-
0, /*tp_compare*/
(reprfunc)mxdatetime_repr, /*tp_repr*/
0, /*tp_as_number*/
0, /*tp_as_sequence*/
0, /*tp_as_mapping*/
0, /*tp_hash */
-
0, /*tp_call*/
(reprfunc)mxdatetime_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*/
-
+ Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /*tp_flags*/
mxdatetimeType_doc, /*tp_doc*/
-
- mxdatetime_traverse, /*tp_traverse*/
+ 0, /*tp_traverse*/
0, /*tp_clear*/
-
0, /*tp_richcompare*/
0, /*tp_weaklistoffset*/
-
0, /*tp_iter*/
0, /*tp_iternext*/
-
- /* Attribute descriptor and subclassing stuff */
-
mxdatetimeObject_methods, /*tp_methods*/
mxdatetimeObject_members, /*tp_members*/
0, /*tp_getset*/
0, /*tp_base*/
0, /*tp_dict*/
-
0, /*tp_descr_get*/
0, /*tp_descr_set*/
0, /*tp_dictoffset*/
-
mxdatetime_init, /*tp_init*/
0, /*tp_alloc*/
mxdatetime_new, /*tp_new*/
- (freefunc)mxdatetime_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 --git a/psycopg/adapter_pboolean.c b/psycopg/adapter_pboolean.c
index 4e2c446..f8cd995 100644
--- a/psycopg/adapter_pboolean.c
+++ b/psycopg/adapter_pboolean.c
@@ -114,15 +114,6 @@ pboolean_setup(pbooleanObject *self, PyObject *obj)
return 0;
}
-static int
-pboolean_traverse(PyObject *obj, visitproc visit, void *arg)
-{
- pbooleanObject *self = (pbooleanObject *)obj;
-
- Py_VISIT(self->wrapped);
- return 0;
-}
-
static void
pboolean_dealloc(PyObject* obj)
{
@@ -155,12 +146,6 @@ pboolean_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
return type->tp_alloc(type, 0);
}
-static void
-pboolean_del(PyObject* self)
-{
- PyObject_GC_Del(self);
-}
-
static PyObject *
pboolean_repr(pbooleanObject *self)
{
@@ -177,63 +162,41 @@ pboolean_repr(pbooleanObject *self)
PyTypeObject pbooleanType = {
PyVarObject_HEAD_INIT(NULL, 0)
"psycopg2._psycopg.Boolean",
- sizeof(pbooleanObject),
- 0,
+ sizeof(pbooleanObject), 0,
pboolean_dealloc, /*tp_dealloc*/
0, /*tp_print*/
-
0, /*tp_getattr*/
0, /*tp_setattr*/
-
0, /*tp_compare*/
-
(reprfunc)pboolean_repr, /*tp_repr*/
0, /*tp_as_number*/
0, /*tp_as_sequence*/
0, /*tp_as_mapping*/
0, /*tp_hash */
-
0, /*tp_call*/
(reprfunc)pboolean_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*/
+ Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /*tp_flags*/
pbooleanType_doc, /*tp_doc*/
-
- pboolean_traverse, /*tp_traverse*/
+ 0, /*tp_traverse*/
0, /*tp_clear*/
-
0, /*tp_richcompare*/
0, /*tp_weaklistoffset*/
-
0, /*tp_iter*/
0, /*tp_iternext*/
-
- /* Attribute descriptor and subclassing stuff */
-
pbooleanObject_methods, /*tp_methods*/
pbooleanObject_members, /*tp_members*/
0, /*tp_getset*/
0, /*tp_base*/
0, /*tp_dict*/
-
0, /*tp_descr_get*/
0, /*tp_descr_set*/
0, /*tp_dictoffset*/
-
pboolean_init, /*tp_init*/
- 0, /*tp_alloc will be set to PyType_GenericAlloc in module init*/
+ 0, /*tp_alloc*/
pboolean_new, /*tp_new*/
- (freefunc)pboolean_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 --git a/psycopg/adapter_pdecimal.c b/psycopg/adapter_pdecimal.c
index e14e769..3572141 100644
--- a/psycopg/adapter_pdecimal.c
+++ b/psycopg/adapter_pdecimal.c
@@ -170,15 +170,6 @@ pdecimal_setup(pdecimalObject *self, PyObject *obj)
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)
{
@@ -211,12 +202,6 @@ 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)
{
@@ -233,63 +218,41 @@ pdecimal_repr(pdecimalObject *self)
PyTypeObject pdecimalType = {
PyVarObject_HEAD_INIT(NULL, 0)
"psycopg2._psycopg.Decimal",
- sizeof(pdecimalObject),
- 0,
+ 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*/
+ Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /*tp_flags*/
pdecimalType_doc, /*tp_doc*/
-
- pdecimal_traverse, /*tp_traverse*/
+ 0, /*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*/
+ 0, /*tp_alloc*/
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*/
};
diff --git a/psycopg/adapter_pfloat.c b/psycopg/adapter_pfloat.c
index 1b8074f..7bb7a46 100644
--- a/psycopg/adapter_pfloat.c
+++ b/psycopg/adapter_pfloat.c
@@ -143,15 +143,6 @@ pfloat_setup(pfloatObject *self, PyObject *obj)
return 0;
}
-static int
-pfloat_traverse(PyObject *obj, visitproc visit, void *arg)
-{
- pfloatObject *self = (pfloatObject *)obj;
-
- Py_VISIT(self->wrapped);
- return 0;
-}
-
static void
pfloat_dealloc(PyObject* obj)
{
@@ -184,12 +175,6 @@ pfloat_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
return type->tp_alloc(type, 0);
}
-static void
-pfloat_del(PyObject* self)
-{
- PyObject_GC_Del(self);
-}
-
static PyObject *
pfloat_repr(pfloatObject *self)
{
@@ -206,63 +191,41 @@ pfloat_repr(pfloatObject *self)
PyTypeObject pfloatType = {
PyVarObject_HEAD_INIT(NULL, 0)
"psycopg2._psycopg.Float",
- sizeof(pfloatObject),
- 0,
+ sizeof(pfloatObject), 0,
pfloat_dealloc, /*tp_dealloc*/
0, /*tp_print*/
-
0, /*tp_getattr*/
0, /*tp_setattr*/
-
0, /*tp_compare*/
-
(reprfunc)pfloat_repr, /*tp_repr*/
0, /*tp_as_number*/
0, /*tp_as_sequence*/
0, /*tp_as_mapping*/
0, /*tp_hash */
-
0, /*tp_call*/
(reprfunc)pfloat_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*/
+ Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /*tp_flags*/
pfloatType_doc, /*tp_doc*/
-
- pfloat_traverse, /*tp_traverse*/
+ 0, /*tp_traverse*/
0, /*tp_clear*/
-
0, /*tp_richcompare*/
0, /*tp_weaklistoffset*/
-
0, /*tp_iter*/
0, /*tp_iternext*/
-
- /* Attribute descriptor and subclassing stuff */
-
pfloatObject_methods, /*tp_methods*/
pfloatObject_members, /*tp_members*/
0, /*tp_getset*/
0, /*tp_base*/
0, /*tp_dict*/
-
0, /*tp_descr_get*/
0, /*tp_descr_set*/
0, /*tp_dictoffset*/
-
pfloat_init, /*tp_init*/
- 0, /*tp_alloc will be set to PyType_GenericAlloc in module init*/
+ 0, /*tp_alloc*/
pfloat_new, /*tp_new*/
- (freefunc)pfloat_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 --git a/psycopg/adapter_pint.c b/psycopg/adapter_pint.c
index ad89a06..6465ace 100644
--- a/psycopg/adapter_pint.c
+++ b/psycopg/adapter_pint.c
@@ -129,15 +129,6 @@ pint_setup(pintObject *self, PyObject *obj)
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)
{
@@ -170,12 +161,6 @@ 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)
{
@@ -192,63 +177,41 @@ pint_repr(pintObject *self)
PyTypeObject pintType = {
PyVarObject_HEAD_INIT(NULL, 0)
"psycopg2._psycopg.Int",
- sizeof(pintObject),
- 0,
+ 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*/
+ Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /*tp_flags*/
pintType_doc, /*tp_doc*/
-
- pint_traverse, /*tp_traverse*/
+ 0, /*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*/
+ 0, /*tp_alloc*/
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*/
};
diff --git a/psycopg/adapter_qstring.c b/psycopg/adapter_qstring.c
index 53a8760..91c1467 100644
--- a/psycopg/adapter_qstring.c
+++ b/psycopg/adapter_qstring.c
@@ -32,26 +32,32 @@
#include <string.h>
+static const char *default_encoding = "latin1";
/* qstring_quote - do the quote process on plain and unicode strings */
-BORROWED static PyObject *
+static PyObject *
qstring_quote(qstringObject *self)
{
- PyObject *str;
- char *s, *buffer;
+ PyObject *str = NULL;
+ char *s, *buffer = NULL;
Py_ssize_t len, qlen;
+ const char *encoding = default_encoding;
+ PyObject *rv = NULL;
/* if the wrapped object is an unicode object we can encode it to match
- self->encoding but if the encoding is not specified we don't know what
+ conn->encoding but if the encoding is not specified we don't know what
to do and we raise an exception */
+ if (self->conn) {
+ encoding = self->conn->codec;
+ }
- Dprintf("qstring_quote: encoding to %s", self->encoding);
+ Dprintf("qstring_quote: encoding to %s", encoding);
- if (PyUnicode_Check(self->wrapped) && self->encoding) {
- str = PyUnicode_AsEncodedString(self->wrapped, self->encoding, NULL);
+ if (PyUnicode_Check(self->wrapped) && encoding) {
+ str = PyUnicode_AsEncodedString(self->wrapped, encoding, NULL);
Dprintf("qstring_quote: got encoded object at %p", str);
- if (str == NULL) return NULL;
+ if (str == NULL) goto exit;
}
#if PY_MAJOR_VERSION < 3
@@ -68,30 +74,28 @@ qstring_quote(qstringObject *self)
else {
PyErr_SetString(PyExc_TypeError,
"can't quote non-string object (or missing encoding)");
- return NULL;
+ goto exit;
}
/* encode the string into buffer */
Bytes_AsStringAndSize(str, &s, &len);
if (!(buffer = psycopg_escape_string(self->conn, s, len, NULL, &qlen))) {
- Py_DECREF(str);
- PyErr_NoMemory();
- return NULL;
+ goto exit;
}
if (qlen > (size_t) PY_SSIZE_T_MAX) {
PyErr_SetString(PyExc_IndexError,
"PG buffer too large to fit in Python buffer.");
- PyMem_Free(buffer);
- Py_DECREF(str);
- return NULL;
+ goto exit;
}
- self->buffer = Bytes_FromStringAndSize(buffer, qlen);
+ rv = Bytes_FromStringAndSize(buffer, qlen);
+
+exit:
PyMem_Free(buffer);
- Py_DECREF(str);
+ Py_XDECREF(str);
- return self->buffer;
+ return rv;
}
/* qstring_str, qstring_getquoted - return result of quoting */
@@ -100,7 +104,7 @@ static PyObject *
qstring_getquoted(qstringObject *self, PyObject *args)
{
if (self->buffer == NULL) {
- qstring_quote(self);
+ self->buffer = qstring_quote(self);
}
Py_XINCREF(self->buffer);
return self->buffer;
@@ -115,25 +119,16 @@ qstring_str(qstringObject *self)
static PyObject *
qstring_prepare(qstringObject *self, PyObject *args)
{
- PyObject *conn;
+ connectionObject *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(((connectionObject *)conn)->codec);
- Dprintf("qstring_prepare: set encoding to %s", self->encoding);
- }
-
Py_CLEAR(self->conn);
Py_INCREF(conn);
self->conn = conn;
- Py_INCREF(Py_None);
- return Py_None;
+ Py_RETURN_NONE;
}
static PyObject *
@@ -152,6 +147,18 @@ qstring_conform(qstringObject *self, PyObject *args)
return res;
}
+static PyObject *
+qstring_get_encoding(qstringObject *self)
+{
+ const char *encoding = default_encoding;
+
+ if (self->conn) {
+ encoding = self->conn->codec;
+ }
+
+ return Text_FromUTF8(encoding);
+}
+
/** the QuotedString object **/
/* object member list */
@@ -159,7 +166,6 @@ qstring_conform(qstringObject *self, PyObject *args)
static struct PyMemberDef qstringObject_members[] = {
{"adapted", T_OBJECT, offsetof(qstringObject, wrapped), READONLY},
{"buffer", T_OBJECT, offsetof(qstringObject, buffer), READONLY},
- {"encoding", T_STRING, offsetof(qstringObject, encoding), READONLY},
{NULL}
};
@@ -174,22 +180,24 @@ static PyMethodDef qstringObject_methods[] = {
{NULL} /* Sentinel */
};
+static PyGetSetDef qstringObject_getsets[] = {
+ { "encoding",
+ (getter)qstring_get_encoding,
+ (setter)NULL,
+ "current encoding of the adapter" },
+ {NULL}
+};
+
/* initialization and finalization methods */
static int
-qstring_setup(qstringObject *self, PyObject *str, const char *enc)
+qstring_setup(qstringObject *self, PyObject *str)
{
Dprintf("qstring_setup: init qstring object at %p, refcnt = "
FORMAT_CODE_PY_SSIZE_T,
self, Py_REFCNT(self)
);
- self->buffer = NULL;
- self->conn = NULL;
-
- /* FIXME: remove this orrible strdup */
- if (enc) self->encoding = strdup(enc);
-
Py_INCREF(str);
self->wrapped = str;
@@ -200,17 +208,6 @@ qstring_setup(qstringObject *self, PyObject *str, const char *enc)
return 0;
}
-static int
-qstring_traverse(PyObject *obj, visitproc visit, void *arg)
-{
- qstringObject *self = (qstringObject *)obj;
-
- Py_VISIT(self->wrapped);
- Py_VISIT(self->buffer);
- Py_VISIT(self->conn);
- return 0;
-}
-
static void
qstring_dealloc(PyObject* obj)
{
@@ -220,8 +217,6 @@ qstring_dealloc(PyObject* obj)
Py_CLEAR(self->buffer);
Py_CLEAR(self->conn);
- if (self->encoding) free(self->encoding);
-
Dprintf("qstring_dealloc: deleted qstring object at %p, refcnt = "
FORMAT_CODE_PY_SSIZE_T,
obj, Py_REFCNT(obj)
@@ -234,12 +229,11 @@ static int
qstring_init(PyObject *obj, PyObject *args, PyObject *kwds)
{
PyObject *str;
- const char *enc = "latin-1"; /* default encoding as in Python */
- if (!PyArg_ParseTuple(args, "O|s", &str, &enc))
+ if (!PyArg_ParseTuple(args, "O", &str))
return -1;
- return qstring_setup((qstringObject *)obj, str, enc);
+ return qstring_setup((qstringObject *)obj, str);
}
static PyObject *
@@ -248,12 +242,6 @@ qstring_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
return type->tp_alloc(type, 0);
}
-static void
-qstring_del(PyObject* self)
-{
- PyObject_GC_Del(self);
-}
-
static PyObject *
qstring_repr(qstringObject *self)
{
@@ -264,66 +252,46 @@ qstring_repr(qstringObject *self)
/* object type */
#define qstringType_doc \
-"QuotedString(str, enc) -> new quoted object with 'enc' encoding"
+"QuotedString(str) -> new quoted object"
PyTypeObject qstringType = {
PyVarObject_HEAD_INIT(NULL, 0)
"psycopg2._psycopg.QuotedString",
- sizeof(qstringObject),
- 0,
+ sizeof(qstringObject), 0,
qstring_dealloc, /*tp_dealloc*/
0, /*tp_print*/
0, /*tp_getattr*/
0, /*tp_setattr*/
-
0, /*tp_compare*/
(reprfunc)qstring_repr, /*tp_repr*/
0, /*tp_as_number*/
0, /*tp_as_sequence*/
0, /*tp_as_mapping*/
0, /*tp_hash */
-
0, /*tp_call*/
(reprfunc)qstring_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*/
-
+ Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /*tp_flags*/
qstringType_doc, /*tp_doc*/
-
- qstring_traverse, /*tp_traverse*/
+ 0, /*tp_traverse*/
0, /*tp_clear*/
-
0, /*tp_richcompare*/
0, /*tp_weaklistoffset*/
-
0, /*tp_iter*/
0, /*tp_iternext*/
-
- /* Attribute descriptor and subclassing stuff */
-
qstringObject_methods, /*tp_methods*/
qstringObject_members, /*tp_members*/
- 0, /*tp_getset*/
+ qstringObject_getsets, /*tp_getset*/
0, /*tp_base*/
0, /*tp_dict*/
-
0, /*tp_descr_get*/
0, /*tp_descr_set*/
0, /*tp_dictoffset*/
-
qstring_init, /*tp_init*/
- 0, /*tp_alloc will be set to PyType_GenericAlloc in module init*/
+ 0, /*tp_alloc*/
qstring_new, /*tp_new*/
- (freefunc)qstring_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*/
};
@@ -333,10 +301,9 @@ PyObject *
psyco_QuotedString(PyObject *module, PyObject *args)
{
PyObject *str;
- const char *enc = "latin-1"; /* default encoding as in Python */
- if (!PyArg_ParseTuple(args, "O|s", &str, &enc))
+ if (!PyArg_ParseTuple(args, "O", &str))
return NULL;
- return PyObject_CallFunction((PyObject *)&qstringType, "Os", str, enc);
+ return PyObject_CallFunctionObjArgs((PyObject *)&qstringType, str, NULL);
}
diff --git a/psycopg/adapter_qstring.h b/psycopg/adapter_qstring.h
index d825fe0..0446f27 100644
--- a/psycopg/adapter_qstring.h
+++ b/psycopg/adapter_qstring.h
@@ -37,13 +37,8 @@ typedef struct {
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;
+ connectionObject *conn;
} qstringObject;
/* functions exported to psycopgmodule.c */
diff --git a/psycopg/connection.h b/psycopg/connection.h
index 01cc6a4..07dfe2e 100644
--- a/psycopg/connection.h
+++ b/psycopg/connection.h
@@ -74,7 +74,8 @@ struct connectionObject_notice {
const char *message;
};
-typedef struct {
+/* the typedef is forward-declared in psycopg.h */
+struct connectionObject {
PyObject_HEAD
pthread_mutex_t lock; /* the global connection lock */
@@ -88,7 +89,7 @@ typedef struct {
2 that something horrible happened */
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 */
+ xidObject *tpc_xid; /* Transaction ID in two-phase commit */
long int async; /* 1 means the connection is async */
int protocol; /* protocol version */
@@ -120,7 +121,8 @@ typedef struct {
int autocommit;
-} connectionObject;
+ PyObject *cursor_factory; /* default cursor factory from cursor() */
+};
/* map isolation level values into a numeric const */
typedef struct {
@@ -134,7 +136,6 @@ 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 void conn_notifies_process(connectionObject *self);
@@ -151,9 +152,9 @@ 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_begin(connectionObject *self, xidObject *xid);
RAISES_NEG HIDDEN int conn_tpc_command(connectionObject *self,
- const char *cmd, XidObject *xid);
+ const char *cmd, xidObject *xid);
HIDDEN PyObject *conn_tpc_recover(connectionObject *self);
/* exception-raising macros */
diff --git a/psycopg/connection_int.c b/psycopg/connection_int.c
index ad49575..51c2381 100644
--- a/psycopg/connection_int.c
+++ b/psycopg/connection_int.c
@@ -183,7 +183,7 @@ conn_notifies_process(connectionObject *self)
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,
+ if (!(notify = PyObject_CallFunctionObjArgs((PyObject *)&notifyType,
pid, channel, payload, NULL))) {
goto error;
}
@@ -439,10 +439,22 @@ conn_get_server_version(PGconn *pgconn)
return (int)PQserverVersion(pgconn);
}
-PGcancel *
-conn_get_cancel(PGconn *pgconn)
+/* set up the cancel key of the connection.
+ * On success return 0, else set an exception and return -1
+ */
+RAISES_NEG static int
+conn_setup_cancel(connectionObject *self, PGconn *pgconn)
{
- return PQgetCancel(pgconn);
+ if (self->cancel) {
+ PQfreeCancel(self->cancel);
+ }
+
+ if (!(self->cancel = PQgetCancel(self->pgconn))) {
+ PyErr_SetString(OperationalError, "can't get cancellation key");
+ return -1;
+ }
+
+ return 0;
}
@@ -486,9 +498,7 @@ conn_setup(connectionObject *self, PGconn *pgconn)
return -1;
}
- self->cancel = conn_get_cancel(self->pgconn);
- if (self->cancel == NULL) {
- PyErr_SetString(OperationalError, "can't get cancellation key");
+ if (0 > conn_setup_cancel(self, pgconn)) {
return -1;
}
@@ -788,10 +798,8 @@ _conn_poll_setup_async(connectionObject *self)
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;
+ if (0 > conn_setup_cancel(self, self->pgconn)) {
+ return -1;
}
/* asynchronous connections always use isolation level 0, the user is
@@ -890,7 +898,7 @@ conn_poll(connectionObject *self)
}
curs = (cursorObject *)py_curs;
- IFCLEARPGRES(curs->pgres);
+ CLEARPGRES(curs->pgres);
curs->pgres = pq_get_last_result(self);
/* fetch the tuples (if there are any) and build the result. We
@@ -918,7 +926,8 @@ conn_poll(connectionObject *self)
void
conn_close(connectionObject *self)
{
- if (self->closed) {
+ /* a connection with closed == 2 still requires cleanup */
+ if (self->closed == 1) {
return;
}
@@ -936,7 +945,7 @@ conn_close(connectionObject *self)
void conn_close_locked(connectionObject *self)
{
- if (self->closed) {
+ if (self->closed == 1) {
return;
}
@@ -957,8 +966,6 @@ void conn_close_locked(connectionObject *self)
PQfinish(self->pgconn);
self->pgconn = NULL;
Dprintf("conn_close: PQfinish called");
- PQfreeCancel(self->cancel);
- self->cancel = NULL;
}
}
@@ -1213,7 +1220,7 @@ exit:
* until PREPARE. */
RAISES_NEG int
-conn_tpc_begin(connectionObject *self, XidObject *xid)
+conn_tpc_begin(connectionObject *self, xidObject *xid)
{
PGresult *pgres = NULL;
char *error = NULL;
@@ -1247,7 +1254,7 @@ conn_tpc_begin(connectionObject *self, XidObject *xid)
* for many commands and for recovered transactions. */
RAISES_NEG int
-conn_tpc_command(connectionObject *self, const char *cmd, XidObject *xid)
+conn_tpc_command(connectionObject *self, const char *cmd, xidObject *xid)
{
PGresult *pgres = NULL;
char *error = NULL;
diff --git a/psycopg/connection_type.c b/psycopg/connection_type.c
index c4b88b3..e854fa5 100644
--- a/psycopg/connection_type.c
+++ b/psycopg/connection_type.c
@@ -52,63 +52,74 @@
static PyObject *
psyco_conn_cursor(connectionObject *self, PyObject *args, PyObject *kwargs)
{
- PyObject *obj;
+ PyObject *obj = NULL;
+ PyObject *rv = NULL;
PyObject *name = Py_None;
PyObject *factory = (PyObject *)&cursorType;
PyObject *withhold = Py_False;
+ PyObject *scrollable = Py_None;
- static char *kwlist[] = {"name", "cursor_factory", "withhold", NULL};
+ static char *kwlist[] = {
+ "name", "cursor_factory", "withhold", "scrollable", NULL};
- if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|OOO", kwlist,
- &name, &factory, &withhold)) {
- return NULL;
- }
+ EXC_IF_CONN_CLOSED(self);
- if (PyObject_IsTrue(withhold) && (name == Py_None)) {
- PyErr_SetString(ProgrammingError,
- "'withhold=True can be specified only for named cursors");
- return NULL;
+ if (self->cursor_factory && self->cursor_factory != Py_None) {
+ factory = self->cursor_factory;
}
- EXC_IF_CONN_CLOSED(self);
+ if (!PyArg_ParseTupleAndKeywords(
+ args, kwargs, "|OOOO", kwlist,
+ &name, &factory, &withhold, &scrollable)) {
+ goto exit;
+ }
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;
+ goto exit;
}
if (name != Py_None && self->async == 1) {
PyErr_SetString(ProgrammingError,
"asynchronous connections "
"cannot produce named cursors");
- return NULL;
+ goto exit;
}
Dprintf("psyco_conn_cursor: new %s cursor for connection at %p",
(name == Py_None ? "unnamed" : "named"), self);
if (!(obj = PyObject_CallFunctionObjArgs(factory, self, name, NULL))) {
- return NULL;
+ goto exit;
}
if (PyObject_IsInstance(obj, (PyObject *)&cursorType) == 0) {
PyErr_SetString(PyExc_TypeError,
"cursor factory must be subclass of psycopg2._psycopg.cursor");
- Py_DECREF(obj);
- return NULL;
+ goto exit;
}
- if (PyObject_IsTrue(withhold))
- ((cursorObject*)obj)->withhold = 1;
+ if (0 != psyco_curs_withhold_set((cursorObject *)obj, withhold)) {
+ goto exit;
+ }
+ if (0 != psyco_curs_scrollable_set((cursorObject *)obj, scrollable)) {
+ goto exit;
+ }
Dprintf("psyco_conn_cursor: new cursor at %p: refcnt = "
FORMAT_CODE_PY_SSIZE_T,
obj, Py_REFCNT(obj)
- );
- return obj;
+ );
+
+ rv = obj;
+ obj = NULL;
+
+exit:
+ Py_XDECREF(obj);
+ return rv;
}
@@ -117,14 +128,13 @@ psyco_conn_cursor(connectionObject *self, PyObject *args, PyObject *kwargs)
#define psyco_conn_close_doc "close() -- Close the connection."
static PyObject *
-psyco_conn_close(connectionObject *self, PyObject *args)
+psyco_conn_close(connectionObject *self)
{
Dprintf("psyco_conn_close: closing connection at %p", self);
conn_close(self);
Dprintf("psyco_conn_close: connection at %p closed", self);
- Py_INCREF(Py_None);
- return Py_None;
+ Py_RETURN_NONE;
}
@@ -133,7 +143,7 @@ psyco_conn_close(connectionObject *self, PyObject *args)
#define psyco_conn_commit_doc "commit() -- Commit all changes to database."
static PyObject *
-psyco_conn_commit(connectionObject *self, PyObject *args)
+psyco_conn_commit(connectionObject *self)
{
EXC_IF_CONN_CLOSED(self);
EXC_IF_CONN_ASYNC(self, commit);
@@ -142,8 +152,7 @@ psyco_conn_commit(connectionObject *self, PyObject *args)
if (conn_commit(self) < 0)
return NULL;
- Py_INCREF(Py_None);
- return Py_None;
+ Py_RETURN_NONE;
}
@@ -153,7 +162,7 @@ psyco_conn_commit(connectionObject *self, PyObject *args)
"rollback() -- Roll back all changes done to database."
static PyObject *
-psyco_conn_rollback(connectionObject *self, PyObject *args)
+psyco_conn_rollback(connectionObject *self)
{
EXC_IF_CONN_CLOSED(self);
EXC_IF_CONN_ASYNC(self, rollback);
@@ -162,8 +171,7 @@ psyco_conn_rollback(connectionObject *self, PyObject *args)
if (conn_rollback(self) < 0)
return NULL;
- Py_INCREF(Py_None);
- return Py_None;
+ Py_RETURN_NONE;
}
@@ -176,7 +184,7 @@ 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);
+ return PyObject_Call((PyObject *)&xidType, args, kwargs);
}
@@ -187,7 +195,7 @@ static PyObject *
psyco_conn_tpc_begin(connectionObject *self, PyObject *args)
{
PyObject *rv = NULL;
- XidObject *xid = NULL;
+ xidObject *xid = NULL;
PyObject *oxid;
EXC_IF_CONN_CLOSED(self);
@@ -227,7 +235,7 @@ exit:
"tpc_prepare() -- perform the first phase of a two-phase transaction."
static PyObject *
-psyco_conn_tpc_prepare(connectionObject *self, PyObject *args)
+psyco_conn_tpc_prepare(connectionObject *self)
{
EXC_IF_CONN_CLOSED(self);
EXC_IF_CONN_ASYNC(self, tpc_prepare);
@@ -247,8 +255,7 @@ psyco_conn_tpc_prepare(connectionObject *self, PyObject *args)
* can be performed until commit. */
self->status = CONN_STATUS_PREPARED;
- Py_INCREF(Py_None);
- return Py_None;
+ Py_RETURN_NONE;
}
@@ -279,7 +286,7 @@ _psyco_conn_tpc_finish(connectionObject *self, PyObject *args,
_finish_f opc_f, char *tpc_cmd)
{
PyObject *oxid = NULL;
- XidObject *xid = NULL;
+ xidObject *xid = NULL;
PyObject *rv = NULL;
if (!PyArg_ParseTuple(args, "|O", &oxid)) { goto exit; }
@@ -371,7 +378,7 @@ psyco_conn_tpc_rollback(connectionObject *self, PyObject *args)
"tpc_recover() -- returns a list of pending transaction IDs."
static PyObject *
-psyco_conn_tpc_recover(connectionObject *self, PyObject *args)
+psyco_conn_tpc_recover(connectionObject *self)
{
EXC_IF_CONN_CLOSED(self);
EXC_IF_CONN_ASYNC(self, tpc_recover);
@@ -382,6 +389,54 @@ psyco_conn_tpc_recover(connectionObject *self, PyObject *args)
}
+#define psyco_conn_enter_doc \
+"__enter__ -> self"
+
+static PyObject *
+psyco_conn_enter(connectionObject *self)
+{
+ EXC_IF_CONN_CLOSED(self);
+
+ Py_INCREF(self);
+ return (PyObject *)self;
+}
+
+
+#define psyco_conn_exit_doc \
+"__exit__ -- commit if no exception, else roll back"
+
+static PyObject *
+psyco_conn_exit(connectionObject *self, PyObject *args)
+{
+ PyObject *type, *name, *tb;
+ PyObject *tmp = NULL;
+ PyObject *rv = NULL;
+
+ if (!PyArg_ParseTuple(args, "OOO", &type, &name, &tb)) {
+ goto exit;
+ }
+
+ if (type == Py_None) {
+ if (!(tmp = PyObject_CallMethod((PyObject *)self, "commit", NULL))) {
+ goto exit;
+ }
+ } else {
+ if (!(tmp = PyObject_CallMethod((PyObject *)self, "rollback", NULL))) {
+ goto exit;
+ }
+ }
+
+ /* success (of the commit or rollback, there may have been an exception in
+ * the block). Return None to avoid swallowing the exception */
+ rv = Py_None;
+ Py_INCREF(rv);
+
+exit:
+ Py_XDECREF(tmp);
+ return rv;
+}
+
+
#ifdef PSYCOPG_EXTENSIONS
@@ -528,8 +583,7 @@ psyco_conn_set_session(connectionObject *self, PyObject *args, PyObject *kwargs)
return NULL;
}
- Py_INCREF(Py_None);
- return Py_None;
+ Py_RETURN_NONE;
}
@@ -611,8 +665,7 @@ psyco_conn_set_isolation_level(connectionObject *self, PyObject *args)
return NULL;
}
- Py_INCREF(Py_None);
- return Py_None;
+ Py_RETURN_NONE;
}
/* set_client_encoding method - set client encoding */
@@ -645,7 +698,7 @@ psyco_conn_set_client_encoding(connectionObject *self, PyObject *args)
"get_transaction_status() -- Get backend transaction status."
static PyObject *
-psyco_conn_get_transaction_status(connectionObject *self, PyObject *args)
+psyco_conn_get_transaction_status(connectionObject *self)
{
EXC_IF_CONN_CLOSED(self);
@@ -675,8 +728,7 @@ psyco_conn_get_parameter_status(connectionObject *self, PyObject *args)
val = PQparameterStatus(self->pgconn, param);
if (!val) {
- Py_INCREF(Py_None);
- return Py_None;
+ Py_RETURN_NONE;
}
return conn_text_from_chars(self, val);
}
@@ -749,7 +801,7 @@ psyco_conn_lobject(connectionObject *self, PyObject *args, PyObject *keywds)
"get_backend_pid() -- Get backend process id."
static PyObject *
-psyco_conn_get_backend_pid(connectionObject *self, PyObject *args)
+psyco_conn_get_backend_pid(connectionObject *self)
{
EXC_IF_CONN_CLOSED(self);
@@ -762,7 +814,7 @@ psyco_conn_get_backend_pid(connectionObject *self, PyObject *args)
"reset() -- Reset current connection to defaults."
static PyObject *
-psyco_conn_reset(connectionObject *self, PyObject *args)
+psyco_conn_reset(connectionObject *self)
{
int res;
@@ -776,8 +828,7 @@ psyco_conn_reset(connectionObject *self, PyObject *args)
if (res < 0)
return NULL;
- Py_INCREF(Py_None);
- return Py_None;
+ Py_RETURN_NONE;
}
static PyObject *
@@ -790,7 +841,7 @@ psyco_conn_get_exception(PyObject *self, void *closure)
}
static PyObject *
-psyco_conn_poll(connectionObject *self, PyObject *args)
+psyco_conn_poll(connectionObject *self)
{
int res;
@@ -812,7 +863,7 @@ psyco_conn_poll(connectionObject *self, PyObject *args)
"fileno() -> int -- Return file descriptor associated to database connection."
static PyObject *
-psyco_conn_fileno(connectionObject *self, PyObject *args)
+psyco_conn_fileno(connectionObject *self)
{
long int socket;
@@ -831,7 +882,7 @@ psyco_conn_fileno(connectionObject *self, PyObject *args)
"executing an asynchronous operation."
static PyObject *
-psyco_conn_isexecuting(connectionObject *self, PyObject *args)
+psyco_conn_isexecuting(connectionObject *self)
{
/* synchronous connections will always return False */
if (self->async == 0) {
@@ -863,7 +914,7 @@ psyco_conn_isexecuting(connectionObject *self, PyObject *args)
"cancel() -- cancel the current operation"
static PyObject *
-psyco_conn_cancel(connectionObject *self, PyObject *args)
+psyco_conn_cancel(connectionObject *self)
{
char errbuf[256];
@@ -884,8 +935,7 @@ psyco_conn_cancel(connectionObject *self, PyObject *args)
PyErr_SetString(OperationalError, errbuf);
return NULL;
}
- Py_INCREF(Py_None);
- return Py_None;
+ Py_RETURN_NONE;
}
#endif /* PSYCOPG_EXTENSIONS */
@@ -917,6 +967,10 @@ static struct PyMethodDef connectionObject_methods[] = {
METH_VARARGS, psyco_conn_tpc_rollback_doc},
{"tpc_recover", (PyCFunction)psyco_conn_tpc_recover,
METH_NOARGS, psyco_conn_tpc_recover_doc},
+ {"__enter__", (PyCFunction)psyco_conn_enter,
+ METH_NOARGS, psyco_conn_enter_doc},
+ {"__exit__", (PyCFunction)psyco_conn_exit,
+ METH_VARARGS, psyco_conn_exit_doc},
#ifdef PSYCOPG_EXTENSIONS
{"set_session", (PyCFunction)psyco_conn_set_session,
METH_VARARGS|METH_KEYWORDS, psyco_conn_set_session_doc},
@@ -963,6 +1017,8 @@ static struct PyMemberDef connectionObject_members[] = {
{"status", T_INT,
offsetof(connectionObject, status), READONLY,
"The current transaction status."},
+ {"cursor_factory", T_OBJECT, offsetof(connectionObject, cursor_factory), 0,
+ "Default cursor_factory for cursor()."},
{"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), READONLY,
@@ -1019,10 +1075,7 @@ connection_setup(connectionObject *self, const char *dsn, long int async)
self, async, Py_REFCNT(self)
);
- if (!(self->dsn = strdup(dsn))) {
- PyErr_NoMemory();
- goto exit;
- }
+ if (0 > psycopg_strdup(&self->dsn, dsn, 0)) { goto exit; }
if (!(self->notice_list = PyList_New(0))) { goto exit; }
if (!(self->notifies = PyList_New(0))) { goto exit; }
self->async = async;
@@ -1057,33 +1110,41 @@ exit:
return res;
}
+static int
+connection_clear(connectionObject *self)
+{
+ Py_CLEAR(self->tpc_xid);
+ Py_CLEAR(self->async_cursor);
+ Py_CLEAR(self->notice_list);
+ Py_CLEAR(self->notice_filter);
+ Py_CLEAR(self->notifies);
+ Py_CLEAR(self->string_types);
+ Py_CLEAR(self->binary_types);
+ return 0;
+}
+
static void
connection_dealloc(PyObject* obj)
{
connectionObject *self = (connectionObject *)obj;
- if (self->weakreflist) {
- PyObject_ClearWeakRefs(obj);
- }
+ conn_close(self);
PyObject_GC_UnTrack(self);
- if (self->closed == 0) conn_close(self);
+ if (self->weakreflist) {
+ PyObject_ClearWeakRefs(obj);
+ }
conn_notice_clean(self);
- if (self->dsn) free(self->dsn);
+ PyMem_Free(self->dsn);
PyMem_Free(self->encoding);
PyMem_Free(self->codec);
if (self->critical) free(self->critical);
+ if (self->cancel) PQfreeCancel(self->cancel);
- Py_CLEAR(self->tpc_xid);
- Py_CLEAR(self->async_cursor);
- Py_CLEAR(self->notice_list);
- Py_CLEAR(self->notice_filter);
- Py_CLEAR(self->notifies);
- Py_CLEAR(self->string_types);
- Py_CLEAR(self->binary_types);
+ connection_clear(self);
pthread_mutex_destroy(&(self->lock));
@@ -1114,12 +1175,6 @@ connection_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
return type->tp_alloc(type, 0);
}
-static void
-connection_del(PyObject* self)
-{
- PyObject_GC_Del(self);
-}
-
static PyObject *
connection_repr(connectionObject *self)
{
@@ -1154,8 +1209,7 @@ connection_traverse(connectionObject *self, visitproc visit, void *arg)
PyTypeObject connectionType = {
PyVarObject_HEAD_INIT(NULL, 0)
"psycopg2._psycopg.connection",
- sizeof(connectionObject),
- 0,
+ sizeof(connectionObject), 0,
connection_dealloc, /*tp_dealloc*/
0, /*tp_print*/
0, /*tp_getattr*/
@@ -1166,47 +1220,30 @@ PyTypeObject connectionType = {
0, /*tp_as_sequence*/
0, /*tp_as_mapping*/
0, /*tp_hash */
-
0, /*tp_call*/
(reprfunc)connection_repr, /*tp_str*/
0, /*tp_getattro*/
0, /*tp_setattro*/
0, /*tp_as_buffer*/
-
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*/
-
+ (inquiry)connection_clear, /*tp_clear*/
0, /*tp_richcompare*/
offsetof(connectionObject, weakreflist), /* tp_weaklistoffset */
-
0, /*tp_iter*/
0, /*tp_iternext*/
-
- /* Attribute descriptor and subclassing stuff */
-
connectionObject_methods, /*tp_methods*/
connectionObject_members, /*tp_members*/
connectionObject_getsets, /*tp_getset*/
0, /*tp_base*/
0, /*tp_dict*/
-
0, /*tp_descr_get*/
0, /*tp_descr_set*/
0, /*tp_dictoffset*/
-
connection_init, /*tp_init*/
- 0, /*tp_alloc will be set to PyType_GenericAlloc in module init*/
+ 0, /*tp_alloc*/
connection_new, /*tp_new*/
- (freefunc)connection_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 --git a/psycopg/cursor.h b/psycopg/cursor.h
index 723bd17..7940e7b 100644
--- a/psycopg/cursor.h
+++ b/psycopg/cursor.h
@@ -44,6 +44,11 @@ struct cursorObject {
int notuples:1; /* 1 if the command was not a SELECT query */
int withhold:1; /* 1 if the cursor is named and uses WITH HOLD */
+ int scrollable; /* 1 if the cursor is named and SCROLLABLE,
+ 0 if not scrollable
+ -1 if undefined (PG may decide scrollable or not)
+ */
+
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 */
@@ -84,9 +89,11 @@ struct cursorObject {
};
-/* C-callable functions in cursor_int.c and cursor_ext.c */
+/* C-callable functions in cursor_int.c and cursor_type.c */
BORROWED HIDDEN PyObject *curs_get_cast(cursorObject *self, PyObject *oid);
HIDDEN void curs_reset(cursorObject *self);
+HIDDEN int psyco_curs_withhold_set(cursorObject *self, PyObject *pyvalue);
+HIDDEN int psyco_curs_scrollable_set(cursorObject *self, PyObject *pyvalue);
/* exception-raising macros */
#define EXC_IF_CURS_CLOSED(self) \
diff --git a/psycopg/cursor_int.c b/psycopg/cursor_int.c
index 1ac3f55..dd4c0d7 100644
--- a/psycopg/cursor_int.c
+++ b/psycopg/cursor_int.c
@@ -72,19 +72,11 @@ curs_get_cast(cursorObject *self, PyObject *oid)
void
curs_reset(cursorObject *self)
{
- PyObject *tmp;
-
/* initialize some variables to default values */
self->notuples = 1;
self->rowcount = -1;
self->row = 0;
- tmp = self->description;
- Py_INCREF(Py_None);
- self->description = Py_None;
- Py_XDECREF(tmp);
-
- tmp = self->casts;
- self->casts = NULL;
- Py_XDECREF(tmp);
+ Py_CLEAR(self->description);
+ Py_CLEAR(self->casts);
}
diff --git a/psycopg/cursor_type.c b/psycopg/cursor_type.c
index 3f4d045..8fc32d8 100644
--- a/psycopg/cursor_type.c
+++ b/psycopg/cursor_type.c
@@ -50,7 +50,7 @@ extern PyObject *pyPsycopgTzFixedOffsetTimezone;
"close() -- Close the cursor."
static PyObject *
-psyco_curs_close(cursorObject *self, PyObject *args)
+psyco_curs_close(cursorObject *self)
{
EXC_IF_ASYNC_IN_PROGRESS(self, close);
@@ -70,8 +70,7 @@ psyco_curs_close(cursorObject *self, PyObject *args)
Dprintf("psyco_curs_close: cursor at %p closed", self);
exit:
- Py_INCREF(Py_None);
- return Py_None;
+ Py_RETURN_NONE;
}
@@ -118,7 +117,7 @@ _mogrify(PyObject *var, PyObject *fmt, cursorObject *curs, PyObject **new)
if (kind == 2) {
Py_XDECREF(n);
psyco_set_error(ProgrammingError, curs,
- "argument formats can't be mixed", NULL, NULL);
+ "argument formats can't be mixed");
return -1;
}
kind = 1;
@@ -190,7 +189,7 @@ _mogrify(PyObject *var, PyObject *fmt, cursorObject *curs, PyObject **new)
/* we found %( but not a ) */
Py_XDECREF(n);
psyco_set_error(ProgrammingError, curs,
- "incomplete placeholder: '%(' without ')'", NULL, NULL);
+ "incomplete placeholder: '%(' without ')'");
return -1;
}
c = d + 1; /* after the ) */
@@ -205,7 +204,7 @@ _mogrify(PyObject *var, PyObject *fmt, cursorObject *curs, PyObject **new)
if (kind == 1) {
Py_XDECREF(n);
psyco_set_error(ProgrammingError, curs,
- "argument formats can't be mixed", NULL, NULL);
+ "argument formats can't be mixed");
return -1;
}
kind = 2;
@@ -267,7 +266,7 @@ static PyObject *_psyco_curs_validate_sql_basic(
if (!sql || !PyObject_IsTrue(sql)) {
psyco_set_error(ProgrammingError, self,
- "can't execute an empty query", NULL, NULL);
+ "can't execute an empty query");
goto fail;
}
@@ -338,8 +337,7 @@ _psyco_curs_merge_query_args(cursorObject *self,
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);
+ psyco_set_error(ProgrammingError, self, s);
pe = 1;
}
@@ -371,6 +369,7 @@ _psyco_curs_execute(cursorObject *self,
int res = -1;
int tmp;
PyObject *fquery, *cvt = NULL;
+ const char *scroll;
operation = _psyco_curs_validate_sql_basic(self, operation);
@@ -379,13 +378,8 @@ _psyco_curs_execute(cursorObject *self,
if (operation == NULL) { goto exit; }
- IFCLEARPGRES(self->pgres);
-
- if (self->query) {
- Py_DECREF(self->query);
- self->query = NULL;
- }
-
+ CLEARPGRES(self->pgres);
+ Py_CLEAR(self->query);
Dprintf("psyco_curs_execute: starting execution of new query");
/* here we are, and we have a sequence or a dictionary filled with
@@ -397,6 +391,21 @@ _psyco_curs_execute(cursorObject *self,
if (0 > _mogrify(vars, operation, self, &cvt)) { goto exit; }
}
+ switch (self->scrollable) {
+ case -1:
+ scroll = "";
+ break;
+ case 0:
+ scroll = "NO SCROLL ";
+ break;
+ case 1:
+ scroll = "SCROLL ";
+ break;
+ default:
+ PyErr_SetString(InternalError, "unexpected scrollable value");
+ goto exit;
+ }
+
if (vars && cvt) {
if (!(fquery = _psyco_curs_merge_query_args(self, operation, cvt))) {
goto exit;
@@ -404,8 +413,9 @@ _psyco_curs_execute(cursorObject *self,
if (self->name != NULL) {
self->query = Bytes_FromFormat(
- "DECLARE \"%s\" CURSOR %s HOLD FOR %s",
+ "DECLARE \"%s\" %sCURSOR %s HOLD FOR %s",
self->name,
+ scroll,
self->withhold ? "WITH" : "WITHOUT",
Bytes_AS_STRING(fquery));
Py_DECREF(fquery);
@@ -417,8 +427,9 @@ _psyco_curs_execute(cursorObject *self,
else {
if (self->name != NULL) {
self->query = Bytes_FromFormat(
- "DECLARE \"%s\" CURSOR %s HOLD FOR %s",
+ "DECLARE \"%s\" %sCURSOR %s HOLD FOR %s",
self->name,
+ scroll,
self->withhold ? "WITH" : "WITHOUT",
Bytes_AS_STRING(operation));
}
@@ -462,15 +473,14 @@ psyco_curs_execute(cursorObject *self, PyObject *args, PyObject *kwargs)
}
if (self->name != NULL) {
- if (self->query != Py_None) {
+ if (self->query) {
psyco_set_error(ProgrammingError, self,
- "can't call .execute() on named cursors more than once",
- NULL, NULL);
+ "can't call .execute() on named cursors more than once");
return NULL;
}
if (self->conn->autocommit) {
psyco_set_error(ProgrammingError, self,
- "can't use a named cursor outside of transactions", NULL, NULL);
+ "can't use a named cursor outside of transactions");
return NULL;
}
EXC_IF_NO_MARK(self);
@@ -485,8 +495,7 @@ psyco_curs_execute(cursorObject *self, PyObject *args, PyObject *kwargs)
}
/* success */
- Py_INCREF(Py_None);
- return Py_None;
+ Py_RETURN_NONE;
}
#define psyco_curs_executemany_doc \
@@ -515,7 +524,7 @@ psyco_curs_executemany(cursorObject *self, PyObject *args, PyObject *kwargs)
if (self->name != NULL) {
psyco_set_error(ProgrammingError, self,
- "can't call .executemany() on named cursors", NULL, NULL);
+ "can't call .executemany() on named cursors");
return NULL;
}
@@ -542,8 +551,7 @@ psyco_curs_executemany(cursorObject *self, PyObject *args, PyObject *kwargs)
self->rowcount = rowcount;
if (!PyErr_Occurred()) {
- Py_INCREF(Py_None);
- return Py_None;
+ Py_RETURN_NONE;
}
else {
return NULL;
@@ -743,7 +751,7 @@ exit:
}
static PyObject *
-psyco_curs_fetchone(cursorObject *self, PyObject *args)
+psyco_curs_fetchone(cursorObject *self)
{
PyObject *res;
@@ -767,8 +775,7 @@ psyco_curs_fetchone(cursorObject *self, PyObject *args)
if (self->row >= self->rowcount) {
/* we exausted available data: return None */
- Py_INCREF(Py_None);
- return Py_None;
+ Py_RETURN_NONE;
}
res = _psyco_curs_buildrow(self, self->row);
@@ -779,7 +786,7 @@ psyco_curs_fetchone(cursorObject *self, PyObject *args)
if (self->row >= self->rowcount
&& self->conn->async_cursor
&& PyWeakref_GetObject(self->conn->async_cursor) == (PyObject*)self)
- IFCLEARPGRES(self->pgres);
+ CLEARPGRES(self->pgres);
return res;
}
@@ -826,7 +833,7 @@ psyco_curs_next_named(cursorObject *self)
if (self->row >= self->rowcount
&& self->conn->async_cursor
&& PyWeakref_GetObject(self->conn->async_cursor) == (PyObject*)self)
- IFCLEARPGRES(self->pgres);
+ CLEARPGRES(self->pgres);
return res;
}
@@ -911,7 +918,7 @@ psyco_curs_fetchmany(cursorObject *self, PyObject *args, PyObject *kwords)
if (self->row >= self->rowcount
&& self->conn->async_cursor
&& PyWeakref_GetObject(self->conn->async_cursor) == (PyObject*)self)
- IFCLEARPGRES(self->pgres);
+ CLEARPGRES(self->pgres);
/* success */
rv = list;
@@ -935,7 +942,7 @@ exit:
"Return `!None` when no more data is available.\n"
static PyObject *
-psyco_curs_fetchall(cursorObject *self, PyObject *args)
+psyco_curs_fetchall(cursorObject *self)
{
int i, size;
PyObject *list = NULL;
@@ -980,7 +987,7 @@ psyco_curs_fetchall(cursorObject *self, PyObject *args)
if (self->row >= self->rowcount
&& self->conn->async_cursor
&& PyWeakref_GetObject(self->conn->async_cursor) == (PyObject*)self)
- IFCLEARPGRES(self->pgres);
+ CLEARPGRES(self->pgres);
/* success */
rv = list;
@@ -1020,7 +1027,7 @@ psyco_curs_callproc(cursorObject *self, PyObject *args)
if (self->name != NULL) {
psyco_set_error(ProgrammingError, self,
- "can't call .callproc() on named cursors", NULL, NULL);
+ "can't call .callproc() on named cursors");
goto exit;
}
@@ -1067,7 +1074,7 @@ exit:
"sets) and will raise a NotSupportedError exception."
static PyObject *
-psyco_curs_nextset(cursorObject *self, PyObject *args)
+psyco_curs_nextset(cursorObject *self)
{
EXC_IF_CURS_CLOSED(self);
@@ -1092,8 +1099,7 @@ psyco_curs_setinputsizes(cursorObject *self, PyObject *args)
EXC_IF_CURS_CLOSED(self);
- Py_INCREF(Py_None);
- return Py_None;
+ Py_RETURN_NONE;
}
@@ -1113,8 +1119,7 @@ psyco_curs_setoutputsize(cursorObject *self, PyObject *args)
EXC_IF_CURS_CLOSED(self);
- Py_INCREF(Py_None);
- return Py_None;
+ Py_RETURN_NONE;
}
@@ -1147,13 +1152,13 @@ psyco_curs_scroll(cursorObject *self, PyObject *args, PyObject *kwargs)
newpos = value;
} else {
psyco_set_error(ProgrammingError, self,
- "scroll mode must be 'relative' or 'absolute'", NULL, NULL);
+ "scroll mode must be 'relative' or 'absolute'");
return NULL;
}
if (newpos < 0 || newpos >= self->rowcount ) {
psyco_set_error(ProgrammingError, self,
- "scroll destination out of bounds", NULL, NULL);
+ "scroll destination out of bounds");
return NULL;
}
@@ -1178,8 +1183,43 @@ psyco_curs_scroll(cursorObject *self, PyObject *args, PyObject *kwargs)
if (_psyco_curs_prefetch(self) < 0) return NULL;
}
- Py_INCREF(Py_None);
- return Py_None;
+ Py_RETURN_NONE;
+}
+
+
+#define psyco_curs_enter_doc \
+"__enter__ -> self"
+
+static PyObject *
+psyco_curs_enter(cursorObject *self)
+{
+ Py_INCREF(self);
+ return (PyObject *)self;
+}
+
+#define psyco_curs_exit_doc \
+"__exit__ -- close the cursor"
+
+static PyObject *
+psyco_curs_exit(cursorObject *self, PyObject *args)
+{
+ PyObject *tmp = NULL;
+ PyObject *rv = NULL;
+
+ /* don't care about the arguments here: don't need to parse them */
+
+ if (!(tmp = PyObject_CallMethod((PyObject *)self, "close", ""))) {
+ goto exit;
+ }
+
+ /* success (of curs.close()).
+ * Return None to avoid swallowing the exception */
+ rv = Py_None;
+ Py_INCREF(rv);
+
+exit:
+ Py_XDECREF(tmp);
+ return rv;
}
@@ -1311,7 +1351,7 @@ psyco_curs_copy_from(cursorObject *self, PyObject *args, PyObject *kwargs)
PyObject *file, *columns = NULL, *res = NULL;
if (!PyArg_ParseTupleAndKeywords(args, kwargs,
- "O&s|ss" CONV_CODE_PY_SSIZE_T "O", kwlist,
+ "O&s|ssnO", kwlist,
_psyco_curs_has_read_check, &file, &table_name, &sep, &null, &bufsize,
&columns))
{
@@ -1327,14 +1367,12 @@ psyco_curs_copy_from(cursorObject *self, PyObject *args, PyObject *kwargs)
goto exit;
if (!(quoted_delimiter = psycopg_escape_string(
- (PyObject*)self->conn, sep, 0, NULL, NULL))) {
- PyErr_NoMemory();
+ self->conn, sep, 0, NULL, NULL))) {
goto exit;
}
if (!(quoted_null = psycopg_escape_string(
- (PyObject*)self->conn, null, 0, NULL, NULL))) {
- PyErr_NoMemory();
+ self->conn, null, 0, NULL, NULL))) {
goto exit;
}
@@ -1423,14 +1461,12 @@ psyco_curs_copy_to(cursorObject *self, PyObject *args, PyObject *kwargs)
goto exit;
if (!(quoted_delimiter = psycopg_escape_string(
- (PyObject*)self->conn, sep, 0, NULL, NULL))) {
- PyErr_NoMemory();
+ self->conn, sep, 0, NULL, NULL))) {
goto exit;
}
if (!(quoted_null = psycopg_escape_string(
- (PyObject*)self->conn, null, 0, NULL, NULL))) {
- PyErr_NoMemory();
+ self->conn, null, 0, NULL, NULL))) {
goto exit;
}
@@ -1488,7 +1524,7 @@ psyco_curs_copy_expert(cursorObject *self, PyObject *args, PyObject *kwargs)
static char *kwlist[] = {"sql", "file", "size", NULL};
if (!PyArg_ParseTupleAndKeywords(args, kwargs,
- "OO|" CONV_CODE_PY_SSIZE_T, kwlist, &sql, &file, &bufsize))
+ "OO|n", kwlist, &sql, &file, &bufsize))
{ return NULL; }
EXC_IF_CURS_CLOSED(self);
@@ -1567,22 +1603,70 @@ psyco_curs_withhold_get(cursorObject *self)
return ret;
}
-static int
+int
psyco_curs_withhold_set(cursorObject *self, PyObject *pyvalue)
{
int value;
- if (self->name == NULL) {
+ if (pyvalue != Py_False && self->name == NULL) {
PyErr_SetString(ProgrammingError,
"trying to set .withhold on unnamed cursor");
return -1;
}
-
- if ((value = PyObject_IsTrue(pyvalue)) == -1)
+
+ if ((value = PyObject_IsTrue(pyvalue)) == -1)
return -1;
self->withhold = value;
-
+
+ return 0;
+}
+
+#define psyco_curs_scrollable_doc \
+"Set or return cursor use of SCROLL"
+
+static PyObject *
+psyco_curs_scrollable_get(cursorObject *self)
+{
+ PyObject *ret = NULL;
+
+ switch (self->scrollable) {
+ case -1:
+ ret = Py_None;
+ break;
+ case 0:
+ ret = Py_False;
+ break;
+ case 1:
+ ret = Py_True;
+ break;
+ default:
+ PyErr_SetString(InternalError, "unexpected scrollable value");
+ }
+
+ Py_XINCREF(ret);
+ return ret;
+}
+
+int
+psyco_curs_scrollable_set(cursorObject *self, PyObject *pyvalue)
+{
+ int value;
+
+ if (pyvalue != Py_None && self->name == NULL) {
+ PyErr_SetString(ProgrammingError,
+ "trying to set .scrollable on unnamed cursor");
+ return -1;
+ }
+
+ if (pyvalue == Py_None) {
+ value = -1;
+ } else if ((value = PyObject_IsTrue(pyvalue)) == -1) {
+ return -1;
+ }
+
+ self->scrollable = value;
+
return 0;
}
@@ -1608,7 +1692,7 @@ cursor_next(PyObject *self)
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);
+ res = psyco_curs_fetchone((cursorObject*)self);
/* convert a None to NULL to signal the end of iteration */
if (res && res == Py_None) {
@@ -1650,6 +1734,10 @@ static struct PyMethodDef cursorObject_methods[] = {
/* DBAPI-2.0 extensions */
{"scroll", (PyCFunction)psyco_curs_scroll,
METH_VARARGS|METH_KEYWORDS, psyco_curs_scroll_doc},
+ {"__enter__", (PyCFunction)psyco_curs_enter,
+ METH_NOARGS, psyco_curs_enter_doc},
+ {"__exit__", (PyCFunction)psyco_curs_exit,
+ METH_VARARGS, psyco_curs_exit_doc},
/* psycopg extensions */
#ifdef PSYCOPG_EXTENSIONS
{"cast", (PyCFunction)psyco_curs_cast,
@@ -1712,6 +1800,10 @@ static struct PyGetSetDef cursorObject_getsets[] = {
(getter)psyco_curs_withhold_get,
(setter)psyco_curs_withhold_set,
psyco_curs_withhold_doc, NULL },
+ { "scrollable",
+ (getter)psyco_curs_scrollable_get,
+ (setter)psyco_curs_scrollable_set,
+ psyco_curs_scrollable_doc, NULL },
#endif
{NULL}
};
@@ -1740,31 +1832,15 @@ cursor_setup(cursorObject *self, connectionObject *conn, const char *name)
Py_INCREF(conn);
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;
- self->casts = NULL;
- self->notice = NULL;
-
- self->string_types = NULL;
- self->binary_types = NULL;
- self->weakreflist = NULL;
-
- Py_INCREF(Py_None);
- self->description = Py_None;
- Py_INCREF(Py_None);
- self->pgstatus = Py_None;
Py_INCREF(Py_None);
self->tuple_factory = Py_None;
- Py_INCREF(Py_None);
- self->query = Py_None;
/* default tzinfo factory */
Py_INCREF(pyPsycopgTzFixedOffsetTimezone);
@@ -1777,30 +1853,39 @@ cursor_setup(cursorObject *self, connectionObject *conn, const char *name)
return 0;
}
+static int
+cursor_clear(cursorObject *self)
+{
+ Py_CLEAR(self->conn);
+ Py_CLEAR(self->description);
+ Py_CLEAR(self->pgstatus);
+ Py_CLEAR(self->casts);
+ Py_CLEAR(self->caster);
+ Py_CLEAR(self->copyfile);
+ Py_CLEAR(self->tuple_factory);
+ Py_CLEAR(self->tzinfo_factory);
+ Py_CLEAR(self->query);
+ Py_CLEAR(self->string_types);
+ Py_CLEAR(self->binary_types);
+ return 0;
+}
+
static void
cursor_dealloc(PyObject* obj)
{
cursorObject *self = (cursorObject *)obj;
+ PyObject_GC_UnTrack(self);
+
if (self->weakreflist) {
PyObject_ClearWeakRefs(obj);
}
- PyObject_GC_UnTrack(self);
+ cursor_clear(self);
PyMem_Free(self->name);
- Py_CLEAR(self->conn);
- Py_CLEAR(self->casts);
- Py_CLEAR(self->description);
- Py_CLEAR(self->pgstatus);
- Py_CLEAR(self->tuple_factory);
- Py_CLEAR(self->tzinfo_factory);
- Py_CLEAR(self->query);
- Py_CLEAR(self->string_types);
- Py_CLEAR(self->binary_types);
-
- IFCLEARPGRES(self->pgres);
+ CLEARPGRES(self->pgres);
Dprintf("cursor_dealloc: deleted cursor object at %p, refcnt = "
FORMAT_CODE_PY_SSIZE_T,
@@ -1847,12 +1932,6 @@ cursor_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
return type->tp_alloc(type, 0);
}
-static void
-cursor_del(PyObject* self)
-{
- PyObject_GC_Del(self);
-}
-
static PyObject *
cursor_repr(cursorObject *self)
{
@@ -1886,8 +1965,7 @@ cursor_traverse(cursorObject *self, visitproc visit, void *arg)
PyTypeObject cursorType = {
PyVarObject_HEAD_INIT(NULL, 0)
"psycopg2._psycopg.cursor",
- sizeof(cursorObject),
- 0,
+ sizeof(cursorObject), 0,
cursor_dealloc, /*tp_dealloc*/
0, /*tp_print*/
0, /*tp_getattr*/
@@ -1898,47 +1976,30 @@ PyTypeObject cursorType = {
0, /*tp_as_sequence*/
0, /*tp_as_mapping*/
0, /*tp_hash */
-
0, /*tp_call*/
(reprfunc)cursor_repr, /*tp_str*/
0, /*tp_getattro*/
0, /*tp_setattro*/
0, /*tp_as_buffer*/
-
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_ITER |
Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_HAVE_WEAKREFS ,
/*tp_flags*/
cursorType_doc, /*tp_doc*/
-
(traverseproc)cursor_traverse, /*tp_traverse*/
- 0, /*tp_clear*/
-
+ (inquiry)cursor_clear, /*tp_clear*/
0, /*tp_richcompare*/
offsetof(cursorObject, weakreflist), /*tp_weaklistoffset*/
-
cursor_iter, /*tp_iter*/
cursor_next, /*tp_iternext*/
-
- /* Attribute descriptor and subclassing stuff */
-
cursorObject_methods, /*tp_methods*/
cursorObject_members, /*tp_members*/
cursorObject_getsets, /*tp_getset*/
0, /*tp_base*/
0, /*tp_dict*/
-
0, /*tp_descr_get*/
0, /*tp_descr_set*/
0, /*tp_dictoffset*/
-
cursor_init, /*tp_init*/
- 0, /*tp_alloc Will be set to PyType_GenericAlloc in module init*/
+ 0, /*tp_alloc*/
cursor_new, /*tp_new*/
- (freefunc)cursor_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 --git a/psycopg/diagnostics.h b/psycopg/diagnostics.h
new file mode 100644
index 0000000..9187c50
--- /dev/null
+++ b/psycopg/diagnostics.h
@@ -0,0 +1,40 @@
+/* diagnostics.c - definition for the psycopg Diagnostics type
+ *
+ * Copyright (C) 2013 Matthew Woodcraft <matthew@woodcraft.me.uk>
+ *
+ * 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_DIAGNOSTICS_H
+#define PSYCOPG_DIAGNOSTICS_H 1
+
+#include "psycopg/error.h"
+
+extern HIDDEN PyTypeObject diagnosticsType;
+
+typedef struct {
+ PyObject_HEAD
+
+ errorObject *err; /* exception to retrieve the diagnostics from */
+
+} diagnosticsObject;
+
+#endif /* PSYCOPG_DIAGNOSTICS_H */
diff --git a/psycopg/diagnostics_type.c b/psycopg/diagnostics_type.c
new file mode 100644
index 0000000..dbcbf38
--- /dev/null
+++ b/psycopg/diagnostics_type.c
@@ -0,0 +1,197 @@
+/* diagnostics.c - present information from libpq error responses
+ *
+ * Copyright (C) 2013 Matthew Woodcraft <matthew@woodcraft.me.uk>
+ *
+ * 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/diagnostics.h"
+#include "psycopg/error.h"
+
+/* These are new in PostgreSQL 9.3. Defining them here so that psycopg2 can
+ * use them with a 9.3+ server even if compiled against pre-9.3 headers. */
+#ifndef PG_DIAG_SCHEMA_NAME
+#define PG_DIAG_SCHEMA_NAME 's'
+#endif
+#ifndef PG_DIAG_TABLE_NAME
+#define PG_DIAG_TABLE_NAME 't'
+#endif
+#ifndef PG_DIAG_COLUMN_NAME
+#define PG_DIAG_COLUMN_NAME 'c'
+#endif
+#ifndef PG_DIAG_DATATYPE_NAME
+#define PG_DIAG_DATATYPE_NAME 'd'
+#endif
+#ifndef PG_DIAG_CONSTRAINT_NAME
+#define PG_DIAG_CONSTRAINT_NAME 'n'
+#endif
+
+
+/* Retrieve an error string from the exception's cursor.
+ *
+ * If the cursor or its result isn't available, return None.
+ */
+static PyObject *
+psyco_diagnostics_get_field(diagnosticsObject *self, void *closure)
+{
+ const char *errortext;
+
+ if (!self->err->pgres) {
+ Py_RETURN_NONE;
+ }
+
+ errortext = PQresultErrorField(self->err->pgres, (Py_intptr_t) closure);
+ return error_text_from_chars(self->err, errortext);
+}
+
+
+/* object calculated member list */
+static struct PyGetSetDef diagnosticsObject_getsets[] = {
+ { "severity", (getter)psyco_diagnostics_get_field, NULL,
+ NULL, (void*) PG_DIAG_SEVERITY },
+ { "sqlstate", (getter)psyco_diagnostics_get_field, NULL,
+ NULL, (void*) PG_DIAG_SQLSTATE },
+ { "message_primary", (getter)psyco_diagnostics_get_field, NULL,
+ NULL, (void*) PG_DIAG_MESSAGE_PRIMARY },
+ { "message_detail", (getter)psyco_diagnostics_get_field, NULL,
+ NULL, (void*) PG_DIAG_MESSAGE_DETAIL },
+ { "message_hint", (getter)psyco_diagnostics_get_field, NULL,
+ NULL, (void*) PG_DIAG_MESSAGE_HINT },
+ { "statement_position", (getter)psyco_diagnostics_get_field, NULL,
+ NULL, (void*) PG_DIAG_STATEMENT_POSITION },
+ { "internal_position", (getter)psyco_diagnostics_get_field, NULL,
+ NULL, (void*) PG_DIAG_INTERNAL_POSITION },
+ { "internal_query", (getter)psyco_diagnostics_get_field, NULL,
+ NULL, (void*) PG_DIAG_INTERNAL_QUERY },
+ { "context", (getter)psyco_diagnostics_get_field, NULL,
+ NULL, (void*) PG_DIAG_CONTEXT },
+ { "schema_name", (getter)psyco_diagnostics_get_field, NULL,
+ NULL, (void*) PG_DIAG_SCHEMA_NAME },
+ { "table_name", (getter)psyco_diagnostics_get_field, NULL,
+ NULL, (void*) PG_DIAG_TABLE_NAME },
+ { "column_name", (getter)psyco_diagnostics_get_field, NULL,
+ NULL, (void*) PG_DIAG_COLUMN_NAME },
+ { "datatype_name", (getter)psyco_diagnostics_get_field, NULL,
+ NULL, (void*) PG_DIAG_DATATYPE_NAME },
+ { "constraint_name", (getter)psyco_diagnostics_get_field, NULL,
+ NULL, (void*) PG_DIAG_CONSTRAINT_NAME },
+ { "source_file", (getter)psyco_diagnostics_get_field, NULL,
+ NULL, (void*) PG_DIAG_SOURCE_FILE },
+ { "source_line", (getter)psyco_diagnostics_get_field, NULL,
+ NULL, (void*) PG_DIAG_SOURCE_LINE },
+ { "source_function", (getter)psyco_diagnostics_get_field, NULL,
+ NULL, (void*) PG_DIAG_SOURCE_FUNCTION },
+ {NULL}
+};
+
+/* initialization and finalization methods */
+
+static PyObject *
+diagnostics_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+ return type->tp_alloc(type, 0);
+}
+
+static int
+diagnostics_init(diagnosticsObject *self, PyObject *args, PyObject *kwds)
+{
+ PyObject *err = NULL;
+
+ if (!PyArg_ParseTuple(args, "O", &err))
+ return -1;
+
+ if (!PyObject_TypeCheck(err, &errorType)) {
+ PyErr_SetString(PyExc_TypeError,
+ "The argument must be a psycopg2.Error");
+ return -1;
+ }
+
+ Py_INCREF(err);
+ self->err = (errorObject *)err;
+ return 0;
+}
+
+static void
+diagnostics_dealloc(diagnosticsObject* self)
+{
+ Py_CLEAR(self->err);
+ Py_TYPE(self)->tp_free((PyObject *)self);
+}
+
+
+/* object type */
+
+static const char diagnosticsType_doc[] =
+ "Details from a database error report.\n\n"
+ "The object is returned by the `~psycopg2.Error.diag` attribute of the\n"
+ "`!Error` object.\n"
+ "All the information available from the |PQresultErrorField|_ function\n"
+ "are exposed as attributes by the object, e.g. the `!severity` attribute\n"
+ "returns the `!PG_DIAG_SEVERITY` code. "
+ "Please refer to the `PostgreSQL documentation`__ for the meaning of all"
+ " the attributes.\n\n"
+ ".. |PQresultErrorField| replace:: `!PQresultErrorField()`\n"
+ ".. _PQresultErrorField: http://www.postgresql.org/docs/current/static/"
+ "libpq-exec.html#LIBPQ-PQRESULTERRORFIELD\n"
+ ".. __: PQresultErrorField_\n";
+
+PyTypeObject diagnosticsType = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "psycopg2._psycopg.Diagnostics",
+ sizeof(diagnosticsObject), 0,
+ (destructor)diagnostics_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ diagnosticsType_doc, /*tp_doc*/
+ 0, /*tp_traverse*/
+ 0, /*tp_clear*/
+ 0, /*tp_richcompare*/
+ 0, /*tp_weaklistoffset*/
+ 0, /*tp_iter*/
+ 0, /*tp_iternext*/
+ 0, /*tp_methods*/
+ 0, /*tp_members*/
+ diagnosticsObject_getsets, /*tp_getset*/
+ 0, /*tp_base*/
+ 0, /*tp_dict*/
+ 0, /*tp_descr_get*/
+ 0, /*tp_descr_set*/
+ 0, /*tp_dictoffset*/
+ (initproc)diagnostics_init, /*tp_init*/
+ 0, /*tp_alloc*/
+ diagnostics_new, /*tp_new*/
+};
diff --git a/psycopg/error.h b/psycopg/error.h
new file mode 100644
index 0000000..9ae6dbd
--- /dev/null
+++ b/psycopg/error.h
@@ -0,0 +1,43 @@
+/* error.h - definition for the psycopg base Error type
+ *
+ * Copyright (C) 2013 Daniele Varrazzo <daniele.varrazzo@gmail.com>
+ *
+ * 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_ERROR_H
+#define PSYCOPG_ERROR_H 1
+
+extern HIDDEN PyTypeObject errorType;
+
+typedef struct {
+ PyBaseExceptionObject exc;
+
+ PyObject *pgerror;
+ PyObject *pgcode;
+ cursorObject *cursor;
+ char *codec;
+ PGresult *pgres;
+} errorObject;
+
+HIDDEN PyObject *error_text_from_chars(errorObject *self, const char *str);
+
+#endif /* PSYCOPG_ERROR_H */
diff --git a/psycopg/error_type.c b/psycopg/error_type.c
new file mode 100644
index 0000000..106b87a
--- /dev/null
+++ b/psycopg/error_type.c
@@ -0,0 +1,276 @@
+/* error_type.c - python interface to the Error objects
+ *
+ * Copyright (C) 2013 Daniele Varrazzo <daniele.varrazzo@gmail.com>
+ *
+ * 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/error.h"
+#include "psycopg/diagnostics.h"
+#include "psycopg/pqpath.h"
+
+
+PyObject *
+error_text_from_chars(errorObject *self, const char *str)
+{
+ if (str == NULL) {
+ Py_INCREF(Py_None);
+ return (Py_None);
+ }
+
+#if PY_MAJOR_VERSION < 3
+ return PyString_FromString(str);
+#else
+ return PyUnicode_Decode(str, strlen(str),
+ self->codec ? self->codec : "ascii", "replace");
+#endif
+}
+
+
+static const char pgerror_doc[] =
+ "The error message returned by the backend, if available, else None";
+
+static const char pgcode_doc[] =
+ "The error code returned by the backend, if available, else None";
+
+static const char cursor_doc[] =
+ "The cursor that raised the exception, if available, else None";
+
+static const char diag_doc[] =
+ "A Diagnostics object to get further information about the error";
+
+static PyMemberDef error_members[] = {
+ { "pgerror", T_OBJECT, offsetof(errorObject, pgerror),
+ READONLY, (char *)pgerror_doc },
+ { "pgcode", T_OBJECT, offsetof(errorObject, pgcode),
+ READONLY, (char *)pgcode_doc },
+ { "cursor", T_OBJECT, offsetof(errorObject, cursor),
+ READONLY, (char *)cursor_doc },
+ { NULL }
+};
+
+static PyObject *
+error_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
+{
+ return ((PyTypeObject *)PyExc_StandardError)->tp_new(
+ type, args, kwargs);
+}
+
+static int
+error_init(errorObject *self, PyObject *args, PyObject *kwargs)
+{
+ if (((PyTypeObject *)PyExc_StandardError)->tp_init(
+ (PyObject *)self, args, kwargs) < 0) {
+ return -1;
+ }
+ return 0;
+}
+
+static int
+error_traverse(errorObject *self, visitproc visit, void *arg)
+{
+ Py_VISIT(self->pgerror);
+ Py_VISIT(self->pgcode);
+ Py_VISIT(self->cursor);
+
+ return ((PyTypeObject *)PyExc_StandardError)->tp_traverse(
+ (PyObject *)self, visit, arg);
+}
+
+static int
+error_clear(errorObject *self)
+{
+ Py_CLEAR(self->pgerror);
+ Py_CLEAR(self->pgcode);
+ Py_CLEAR(self->cursor);
+
+ return ((PyTypeObject *)PyExc_StandardError)->tp_clear((PyObject *)self);
+}
+
+static void
+error_dealloc(errorObject *self)
+{
+ PyObject_GC_UnTrack((PyObject *)self);
+ error_clear(self);
+ PyMem_Free(self->codec);
+ CLEARPGRES(self->pgres);
+
+ Py_TYPE(self)->tp_free((PyObject *)self);
+}
+
+
+static PyObject *
+error_get_diag(errorObject *self, void *closure)
+{
+ return PyObject_CallFunctionObjArgs(
+ (PyObject *)&diagnosticsType, (PyObject *)self, NULL);
+}
+
+static struct PyGetSetDef error_getsets[] = {
+ { "diag", (getter)error_get_diag, NULL, (char *)diag_doc },
+ { NULL }
+};
+
+
+/* Error.__reduce__
+ *
+ * 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(errorObject *self)
+{
+ PyObject *meth = NULL;
+ PyObject *tuple = NULL;
+ PyObject *dict = NULL;
+ PyObject *rv = NULL;
+
+ if (!(meth = PyObject_GetAttrString(PyExc_StandardError, "__reduce__"))) {
+ goto error;
+ }
+ if (!(tuple = PyObject_CallFunctionObjArgs(meth, self, NULL))) {
+ goto error;
+ }
+
+ /* tuple is (type, args): convert it to (type, args, dict)
+ * with our extra items in the dict.
+ *
+ * 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 (2 != PyTuple_GET_SIZE(tuple)) { goto exit; }
+
+ if (!(dict = PyDict_New())) { goto error; }
+ if (0 != PyDict_SetItemString(dict, "pgerror", self->pgerror)) { goto error; }
+ if (0 != PyDict_SetItemString(dict, "pgcode", self->pgcode)) { goto error; }
+
+ {
+ PyObject *newtuple;
+ if (!(newtuple = PyTuple_Pack(3,
+ PyTuple_GET_ITEM(tuple, 0),
+ PyTuple_GET_ITEM(tuple, 1),
+ dict))) {
+ goto error;
+ }
+ Py_DECREF(tuple);
+ tuple = newtuple;
+ }
+
+exit:
+ rv = tuple;
+ tuple = NULL;
+
+error:
+ Py_XDECREF(dict);
+ Py_XDECREF(tuple);
+ Py_XDECREF(meth);
+
+ return rv;
+}
+
+PyObject *
+psyco_error_setstate(errorObject *self, PyObject *state)
+{
+ PyObject *rv = NULL;
+
+ /* we don't call the StandartError's setstate as it would try to load the
+ * dict content as attributes */
+
+ if (state == Py_None) {
+ goto exit;
+ }
+ if (!PyDict_Check(state)) {
+ PyErr_SetString(PyExc_TypeError, "state is not a dictionary");
+ goto error;
+ }
+
+ /* load the dict content in the structure */
+ Py_CLEAR(self->pgerror);
+ self->pgerror = PyDict_GetItemString(state, "pgerror");
+ Py_XINCREF(self->pgerror);
+
+ Py_CLEAR(self->pgcode);
+ self->pgcode = PyDict_GetItemString(state, "pgcode");
+ Py_XINCREF(self->pgcode);
+
+ Py_CLEAR(self->cursor);
+ /* We never expect a cursor in the state as it's not picklable.
+ * at most there could be a None here, coming from Psycopg < 2.5 */
+
+exit:
+ rv = Py_None;
+ Py_INCREF(rv);
+
+error:
+ return rv;
+}
+
+static PyMethodDef error_methods[] = {
+ /* Make Error and all its subclasses picklable. */
+ {"__reduce__", (PyCFunction)psyco_error_reduce, METH_NOARGS },
+ {"__setstate__", (PyCFunction)psyco_error_setstate, METH_O },
+ {NULL}
+};
+
+
+PyTypeObject errorType = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "psycopg2.Error",
+ sizeof(errorObject), 0,
+ (destructor)error_dealloc, /* tp_dealloc */
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_HAVE_GC, /*tp_flags*/
+ Error_doc, /*tp_doc*/
+ (traverseproc)error_traverse, /*tp_traverse*/
+ (inquiry)error_clear, /*tp_clear*/
+ 0, /*tp_richcompare*/
+ 0, /*tp_weaklistoffset*/
+ 0, /*tp_iter*/
+ 0, /*tp_iternext*/
+ error_methods, /*tp_methods*/
+ error_members, /*tp_members*/
+ error_getsets, /*tp_getset*/
+ 0, /*tp_base Will be set to StandardError in module init */
+ 0, /*tp_dict*/
+ 0, /*tp_descr_get*/
+ 0, /*tp_descr_set*/
+ 0, /*tp_dictoffset*/
+ (initproc)error_init, /*tp_init*/
+ 0, /*tp_alloc*/
+ error_new, /*tp_new*/
+};
diff --git a/psycopg/green.c b/psycopg/green.c
index 3ffa810..e760407 100644
--- a/psycopg/green.c
+++ b/psycopg/green.c
@@ -53,8 +53,7 @@ psyco_set_wait_callback(PyObject *self, PyObject *obj)
wait_callback = NULL;
}
- Py_INCREF(Py_None);
- return Py_None;
+ Py_RETURN_NONE;
}
diff --git a/psycopg/lobject.h b/psycopg/lobject.h
index 6587198..56f9ead 100644
--- a/psycopg/lobject.h
+++ b/psycopg/lobject.h
@@ -78,13 +78,13 @@ RAISES_NEG HIDDEN int lobject_close(lobjectObject *self);
#define EXC_IF_LOBJ_LEVEL0(self) \
if (self->conn->autocommit) { \
psyco_set_error(ProgrammingError, NULL, \
- "can't use a lobject outside of transactions", NULL, NULL); \
+ "can't use a lobject outside of transactions"); \
return NULL; \
}
#define EXC_IF_LOBJ_UNMARKED(self) \
if (self->conn->mark != self->mark) { \
psyco_set_error(ProgrammingError, NULL, \
- "lobject isn't valid anymore", NULL, NULL); \
+ "lobject isn't valid anymore"); \
return NULL; \
}
diff --git a/psycopg/lobject_type.c b/psycopg/lobject_type.c
index 625a293..fee11c4 100644
--- a/psycopg/lobject_type.c
+++ b/psycopg/lobject_type.c
@@ -59,8 +59,7 @@ psyco_lobj_close(lobjectObject *self, PyObject *args)
return NULL;
}
- Py_INCREF(Py_None);
- return Py_None;
+ Py_RETURN_NONE;
}
/* write method - write data to the lobject */
@@ -128,7 +127,7 @@ psyco_lobj_read(lobjectObject *self, PyObject *args)
Py_ssize_t size = -1;
char *buffer;
- if (!PyArg_ParseTuple(args, "|" CONV_CODE_PY_SSIZE_T, &size)) return NULL;
+ if (!PyArg_ParseTuple(args, "|n", &size)) return NULL;
EXC_IF_LOBJ_CLOSED(self);
EXC_IF_LOBJ_LEVEL0(self);
@@ -213,10 +212,9 @@ static PyObject *
psyco_lobj_unlink(lobjectObject *self, PyObject *args)
{
if (lobject_unlink(self) < 0)
- return NULL;
+ return NULL;
- Py_INCREF(Py_None);
- return Py_None;
+ Py_RETURN_NONE;
}
/* export method - export lobject's content to given file */
@@ -230,15 +228,14 @@ psyco_lobj_export(lobjectObject *self, PyObject *args)
const char *filename;
if (!PyArg_ParseTuple(args, "s", &filename))
- return NULL;
+ return NULL;
EXC_IF_LOBJ_LEVEL0(self);
if (lobject_export(self, filename) < 0)
- return NULL;
+ return NULL;
- Py_INCREF(Py_None);
- return Py_None;
+ Py_RETURN_NONE;
}
@@ -272,8 +269,7 @@ psyco_lobj_truncate(lobjectObject *self, PyObject *args)
if (lobject_truncate(self, len) < 0)
return NULL;
- Py_INCREF(Py_None);
- return Py_None;
+ Py_RETURN_NONE;
}
#endif /* PG_VERSION_HEX >= 0x080300 */
@@ -333,7 +329,7 @@ lobject_setup(lobjectObject *self, connectionObject *conn,
if (conn->autocommit) {
psyco_set_error(ProgrammingError, NULL,
- "can't use a lobject outside of transactions", NULL, NULL);
+ "can't use a lobject outside of transactions");
return -1;
}
@@ -392,12 +388,6 @@ lobject_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
return type->tp_alloc(type, 0);
}
-static void
-lobject_del(PyObject* self)
-{
- PyObject_Del(self);
-}
-
static PyObject *
lobject_repr(lobjectObject *self)
{
@@ -414,8 +404,7 @@ lobject_repr(lobjectObject *self)
PyTypeObject lobjectType = {
PyVarObject_HEAD_INIT(NULL, 0)
"psycopg2._psycopg.lobject",
- sizeof(lobjectObject),
- 0,
+ sizeof(lobjectObject), 0,
lobject_dealloc, /*tp_dealloc*/
0, /*tp_print*/
0, /*tp_getattr*/
@@ -426,47 +415,30 @@ PyTypeObject lobjectType = {
0, /*tp_as_sequence*/
0, /*tp_as_mapping*/
0, /*tp_hash */
-
0, /*tp_call*/
(reprfunc)lobject_repr, /*tp_str*/
0, /*tp_getattro*/
0, /*tp_setattro*/
0, /*tp_as_buffer*/
-
Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_HAVE_ITER, /*tp_flags*/
lobjectType_doc, /*tp_doc*/
-
0, /*tp_traverse*/
0, /*tp_clear*/
-
0, /*tp_richcompare*/
0, /*tp_weaklistoffset*/
-
0, /*tp_iter*/
0, /*tp_iternext*/
-
- /* Attribute descriptor and subclassing stuff */
-
lobjectObject_methods, /*tp_methods*/
lobjectObject_members, /*tp_members*/
lobjectObject_getsets, /*tp_getset*/
0, /*tp_base*/
0, /*tp_dict*/
-
0, /*tp_descr_get*/
0, /*tp_descr_set*/
0, /*tp_dictoffset*/
-
lobject_init, /*tp_init*/
- 0, /*tp_alloc Will be set to PyType_GenericAlloc in module init*/
+ 0, /*tp_alloc*/
lobject_new, /*tp_new*/
- (freefunc)lobject_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*/
};
#endif
diff --git a/psycopg/microprotocols.c b/psycopg/microprotocols.c
index 23f1279..1687bc2 100644
--- a/psycopg/microprotocols.c
+++ b/psycopg/microprotocols.c
@@ -203,7 +203,7 @@ microprotocols_adapt(PyObject *obj, PyObject *proto, PyObject *alt)
/* else set the right exception and return NULL */
PyOS_snprintf(buffer, 255, "can't adapt type '%s'",
Py_TYPE(obj)->tp_name);
- psyco_set_error(ProgrammingError, NULL, buffer, NULL, NULL);
+ psyco_set_error(ProgrammingError, NULL, buffer);
return NULL;
}
diff --git a/psycopg/microprotocols_proto.c b/psycopg/microprotocols_proto.c
index 775889d..f30da3f 100644
--- a/psycopg/microprotocols_proto.c
+++ b/psycopg/microprotocols_proto.c
@@ -42,8 +42,7 @@
static PyObject *
psyco_isqlquote_getquoted(isqlquoteObject *self, PyObject *args)
{
- Py_INCREF(Py_None);
- return Py_None;
+ Py_RETURN_NONE;
}
/* getbinary - return quoted representation for object */
@@ -54,8 +53,7 @@ psyco_isqlquote_getquoted(isqlquoteObject *self, PyObject *args)
static PyObject *
psyco_isqlquote_getbinary(isqlquoteObject *self, PyObject *args)
{
- Py_INCREF(Py_None);
- return Py_None;
+ Py_RETURN_NONE;
}
/* getbuffer - return quoted representation for object */
@@ -66,8 +64,7 @@ psyco_isqlquote_getbinary(isqlquoteObject *self, PyObject *args)
static PyObject *
psyco_isqlquote_getbuffer(isqlquoteObject *self, PyObject *args)
{
- Py_INCREF(Py_None);
- return Py_None;
+ Py_RETURN_NONE;
}
@@ -135,12 +132,6 @@ isqlquote_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
return type->tp_alloc(type, 0);
}
-static void
-isqlquote_del(PyObject* self)
-{
- PyObject_Del(self);
-}
-
/* object type */
@@ -152,8 +143,7 @@ isqlquote_del(PyObject* self)
PyTypeObject isqlquoteType = {
PyVarObject_HEAD_INIT(NULL, 0)
"psycopg2._psycopg.ISQLQuote",
- sizeof(isqlquoteObject),
- 0,
+ sizeof(isqlquoteObject), 0,
isqlquote_dealloc, /*tp_dealloc*/
0, /*tp_print*/
0, /*tp_getattr*/
@@ -164,45 +154,28 @@ PyTypeObject isqlquoteType = {
0, /*tp_as_sequence*/
0, /*tp_as_mapping*/
0, /*tp_hash */
-
0, /*tp_call*/
0, /*tp_str*/
0, /*tp_getattro*/
0, /*tp_setattro*/
0, /*tp_as_buffer*/
-
Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /*tp_flags*/
isqlquoteType_doc, /*tp_doc*/
-
0, /*tp_traverse*/
0, /*tp_clear*/
-
0, /*tp_richcompare*/
0, /*tp_weaklistoffset*/
-
0, /*tp_iter*/
0, /*tp_iternext*/
-
- /* Attribute descriptor and subclassing stuff */
-
isqlquoteObject_methods, /*tp_methods*/
isqlquoteObject_members, /*tp_members*/
0, /*tp_getset*/
0, /*tp_base*/
0, /*tp_dict*/
-
0, /*tp_descr_get*/
0, /*tp_descr_set*/
0, /*tp_dictoffset*/
-
isqlquote_init, /*tp_init*/
- 0, /*tp_alloc will be set to PyType_GenericAlloc in module init*/
+ 0, /*tp_alloc*/
isqlquote_new, /*tp_new*/
- (freefunc)isqlquote_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 --git a/psycopg/notify.h b/psycopg/notify.h
index 645fdd7..2f4c20f 100644
--- a/psycopg/notify.h
+++ b/psycopg/notify.h
@@ -26,7 +26,7 @@
#ifndef PSYCOPG_NOTIFY_H
#define PSYCOPG_NOTIFY_H 1
-extern HIDDEN PyTypeObject NotifyType;
+extern HIDDEN PyTypeObject notifyType;
typedef struct {
PyObject_HEAD
@@ -35,6 +35,6 @@ typedef struct {
PyObject *channel;
PyObject *payload;
-} NotifyObject;
+} notifyObject;
#endif /* PSYCOPG_NOTIFY_H */
diff --git a/psycopg/notify_type.c b/psycopg/notify_type.c
index f97a5af..e2589a6 100644
--- a/psycopg/notify_type.c
+++ b/psycopg/notify_type.c
@@ -52,22 +52,20 @@ static const char payload_doc[] =
"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 },
+ { "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;
+ return type->tp_alloc(type, 0);
}
static int
-notify_init(NotifyObject *self, PyObject *args, PyObject *kwargs)
+notify_init(notifyObject *self, PyObject *args, PyObject *kwargs)
{
static char *kwlist[] = {"pid", "channel", "payload", NULL};
PyObject *pid = NULL, *channel = NULL, *payload = NULL;
@@ -81,32 +79,20 @@ notify_init(NotifyObject *self, PyObject *args, PyObject *kwargs)
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)
+notify_dealloc(notifyObject *self)
{
Py_CLEAR(self->pid);
Py_CLEAR(self->channel);
@@ -115,16 +101,10 @@ notify_dealloc(NotifyObject *self)
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)
+notify_astuple(notifyObject *self, int with_payload)
{
PyObject *tself;
if (!(tself = PyTuple_New(with_payload ? 3 : 2))) { return NULL; }
@@ -162,15 +142,15 @@ notify_astuple(NotifyObject *self, int with_payload)
* the (pid, channel) pair is no more equivalent as dict key to the Notify.
*/
static PyObject *
-notify_richcompare(NotifyObject *self, PyObject *other, int op)
+notify_richcompare(notifyObject *self, PyObject *other, int op)
{
PyObject *rv = NULL;
PyObject *tself = NULL;
PyObject *tother = NULL;
- if (Py_TYPE(other) == &NotifyType) {
+ if (Py_TYPE(other) == &notifyType) {
if (!(tself = notify_astuple(self, 1))) { goto exit; }
- if (!(tother = notify_astuple((NotifyObject *)other, 1))) { goto exit; }
+ if (!(tother = notify_astuple((notifyObject *)other, 1))) { goto exit; }
rv = PyObject_RichCompare(tself, tother, op);
}
else if (PyTuple_Check(other)) {
@@ -190,7 +170,7 @@ exit:
static Py_hash_t
-notify_hash(NotifyObject *self)
+notify_hash(notifyObject *self)
{
Py_hash_t rv = -1L;
PyObject *tself = NULL;
@@ -207,7 +187,7 @@ exit:
static PyObject*
-notify_repr(NotifyObject *self)
+notify_repr(notifyObject *self)
{
PyObject *rv = NULL;
PyObject *format = NULL;
@@ -237,13 +217,13 @@ exit:
/* Notify can be accessed as a 2 items tuple for backward compatibility */
static Py_ssize_t
-notify_len(NotifyObject *self)
+notify_len(notifyObject *self)
{
return 2;
}
static PyObject *
-notify_getitem(NotifyObject *self, Py_ssize_t item)
+notify_getitem(notifyObject *self, Py_ssize_t item)
{
if (item < 0)
item += 2;
@@ -275,66 +255,45 @@ static PySequenceMethods notify_sequence = {
};
-PyTypeObject NotifyType = {
+PyTypeObject notifyType = {
PyVarObject_HEAD_INIT(NULL, 0)
"psycopg2.extensions.Notify",
- sizeof(NotifyObject),
- 0,
+ 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*/
&notify_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 is not GC as it only has string attributes */
+ Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /*tp_flags*/
notify_doc, /*tp_doc*/
-
- (traverseproc)notify_traverse, /*tp_traverse*/
+ 0, /*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*/
+ 0, /*tp_alloc*/
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 --git a/psycopg/pqpath.c b/psycopg/pqpath.c
index ea8bac0..521fc46 100644
--- a/psycopg/pqpath.c
+++ b/psycopg/pqpath.c
@@ -38,6 +38,7 @@
#include "psycopg/green.h"
#include "psycopg/typecast.h"
#include "psycopg/pgtypes.h"
+#include "psycopg/error.h"
#include <string.h>
@@ -149,15 +150,20 @@ exception_from_sqlstate(const char *sqlstate)
/* pq_raise - raise a python exception of the right kind
- This function should be called while holding the GIL. */
+ This function should be called while holding the GIL.
+
+ The function passes the ownership of the pgres to the returned exception,
+ wherer the pgres was the explicit argument or taken from the cursor.
+ So, after calling it curs->pgres will be set to null */
RAISES static void
-pq_raise(connectionObject *conn, cursorObject *curs, PGresult *pgres)
+pq_raise(connectionObject *conn, cursorObject *curs, PGresult **pgres)
{
PyObject *exc = NULL;
const char *err = NULL;
const char *err2 = NULL;
const char *code = NULL;
+ PyObject *pyerr = NULL;
if (conn == NULL) {
PyErr_SetString(DatabaseError,
@@ -171,13 +177,13 @@ pq_raise(connectionObject *conn, cursorObject *curs, PGresult *pgres)
conn->closed = 2;
if (pgres == NULL && curs != NULL)
- pgres = curs->pgres;
+ pgres = &curs->pgres;
- if (pgres) {
- err = PQresultErrorMessage(pgres);
+ if (pgres && *pgres) {
+ err = PQresultErrorMessage(*pgres);
if (err != NULL) {
Dprintf("pq_raise: PQresultErrorMessage: err=%s", err);
- code = PQresultErrorField(pgres, PG_DIAG_SQLSTATE);
+ code = PQresultErrorField(*pgres, PG_DIAG_SQLSTATE);
}
}
if (err == NULL) {
@@ -210,7 +216,26 @@ pq_raise(connectionObject *conn, cursorObject *curs, PGresult *pgres)
err2 = strip_severity(err);
Dprintf("pq_raise: err2=%s", err2);
- psyco_set_error(exc, curs, err2, err, code);
+ pyerr = psyco_set_error(exc, curs, err2);
+
+ if (pyerr && PyObject_TypeCheck(pyerr, &errorType)) {
+ errorObject *perr = (errorObject *)pyerr;
+
+ PyMem_Free(perr->codec);
+ psycopg_strdup(&perr->codec, conn->codec, 0);
+
+ Py_CLEAR(perr->pgerror);
+ perr->pgerror = error_text_from_chars(perr, err);
+
+ Py_CLEAR(perr->pgcode);
+ perr->pgcode = error_text_from_chars(perr, code);
+
+ CLEARPGRES(perr->pgres);
+ if (pgres && *pgres) {
+ perr->pgres = *pgres;
+ *pgres = NULL;
+ }
+ }
}
/* pq_set_critical, pq_resolve_critical - manage critical errors
@@ -369,7 +394,7 @@ pq_execute_command_locked(connectionObject *conn, const char *query,
}
retvalue = 0;
- IFCLEARPGRES(*pgres);
+ CLEARPGRES(*pgres);
cleanup:
return retvalue;
@@ -388,14 +413,16 @@ pq_complete_error(connectionObject *conn, PGresult **pgres, char **error)
{
Dprintf("pq_complete_error: pgconn = %p, pgres = %p, error = %s",
conn->pgconn, *pgres, *error ? *error : "(null)");
- if (*pgres != NULL)
- pq_raise(conn, NULL, *pgres);
+ if (*pgres != NULL) {
+ pq_raise(conn, NULL, pgres);
+ /* now *pgres is null */
+ }
else if (*error != NULL) {
PyErr_SetString(OperationalError, *error);
} else {
PyErr_SetString(OperationalError, "unknown error");
}
- IFCLEARPGRES(*pgres);
+
if (*error) {
free(*error);
*error = NULL;
@@ -557,12 +584,18 @@ pq_reset_locked(connectionObject *conn, PGresult **pgres, char **error,
if (retvalue != 0) return retvalue;
}
- retvalue = pq_execute_command_locked(conn, "RESET ALL", pgres, error, tstate);
- if (retvalue != 0) return retvalue;
+ if (conn->server_version >= 80300) {
+ retvalue = pq_execute_command_locked(conn, "DISCARD ALL", pgres, error, tstate);
+ if (retvalue != 0) return retvalue;
+ }
+ else {
+ 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, tstate);
- if (retvalue != 0) return retvalue;
+ retvalue = pq_execute_command_locked(conn,
+ "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;
@@ -717,7 +750,7 @@ pq_tpc_command_locked(connectionObject *conn, const char *cmd, const char *tid,
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)))
+ if (!(etid = psycopg_escape_string(conn, tid, 0, NULL, NULL)))
{ goto exit; }
/* prepare the command to the server */
@@ -869,7 +902,7 @@ pq_execute(cursorObject *curs, const char *query, int async, int no_result)
}
if (async == 0) {
- IFCLEARPGRES(curs->pgres);
+ CLEARPGRES(curs->pgres);
Dprintf("pq_execute: executing SYNC query: pgconn = %p", curs->conn->pgconn);
Dprintf(" %-.200s", query);
if (!psyco_green()) {
@@ -908,7 +941,7 @@ pq_execute(cursorObject *curs, const char *query, int async, int no_result)
Dprintf("pq_execute: executing ASYNC query: pgconn = %p", curs->conn->pgconn);
Dprintf(" %-.200s", query);
- IFCLEARPGRES(curs->pgres);
+ CLEARPGRES(curs->pgres);
if (PQsendQuery(curs->conn->pgconn, query) == 0) {
pthread_mutex_unlock(&(curs->conn->lock));
Py_BLOCK_THREADS;
@@ -1305,7 +1338,7 @@ _pq_copy_in_v3(cursorObject *curs)
/* XXX would be nice to propagate the exeption */
res = PQputCopyEnd(curs->conn->pgconn, "error in .read() call");
- IFCLEARPGRES(curs->pgres);
+ CLEARPGRES(curs->pgres);
Dprintf("_pq_copy_in_v3: copy ended; res = %d", res);
@@ -1329,7 +1362,7 @@ _pq_copy_in_v3(cursorObject *curs)
break;
if (PQresultStatus(curs->pgres) == PGRES_FATAL_ERROR)
pq_raise(curs->conn, curs, NULL);
- IFCLEARPGRES(curs->pgres);
+ CLEARPGRES(curs->pgres);
}
}
@@ -1395,7 +1428,7 @@ _pq_copy_out_v3(cursorObject *curs)
}
/* and finally we grab the operation result from the backend */
- IFCLEARPGRES(curs->pgres);
+ CLEARPGRES(curs->pgres);
for (;;) {
Py_BEGIN_ALLOW_THREADS;
curs->pgres = PQgetResult(curs->conn->pgconn);
@@ -1405,7 +1438,7 @@ _pq_copy_out_v3(cursorObject *curs)
break;
if (PQresultStatus(curs->pgres) == PGRES_FATAL_ERROR)
pq_raise(curs->conn, curs, NULL);
- IFCLEARPGRES(curs->pgres);
+ CLEARPGRES(curs->pgres);
}
ret = 1;
@@ -1466,7 +1499,7 @@ pq_fetch(cursorObject *curs, int no_result)
curs->rowcount = -1;
/* error caught by out glorious notice handler */
if (PyErr_Occurred()) ex = -1;
- IFCLEARPGRES(curs->pgres);
+ CLEARPGRES(curs->pgres);
break;
case PGRES_COPY_IN:
@@ -1475,7 +1508,7 @@ pq_fetch(cursorObject *curs, int no_result)
curs->rowcount = -1;
/* error caught by out glorious notice handler */
if (PyErr_Occurred()) ex = -1;
- IFCLEARPGRES(curs->pgres);
+ CLEARPGRES(curs->pgres);
break;
case PGRES_TUPLES_OK:
@@ -1487,7 +1520,7 @@ pq_fetch(cursorObject *curs, int no_result)
}
else {
Dprintf("pq_fetch: got tuples, discarding them");
- IFCLEARPGRES(curs->pgres);
+ CLEARPGRES(curs->pgres);
curs->rowcount = -1;
ex = 0;
}
@@ -1496,14 +1529,13 @@ pq_fetch(cursorObject *curs, int no_result)
case PGRES_EMPTY_QUERY:
PyErr_SetString(ProgrammingError,
"can't execute an empty query");
- IFCLEARPGRES(curs->pgres);
+ CLEARPGRES(curs->pgres);
ex = -1;
break;
default:
Dprintf("pq_fetch: uh-oh, something FAILED: pgconn = %p", curs->conn);
pq_raise(curs->conn, curs, NULL);
- IFCLEARPGRES(curs->pgres);
ex = -1;
break;
}
diff --git a/psycopg/pqpath.h b/psycopg/pqpath.h
index d697e48..40beea1 100644
--- a/psycopg/pqpath.h
+++ b/psycopg/pqpath.h
@@ -29,9 +29,8 @@
#include "psycopg/cursor.h"
#include "psycopg/connection.h"
-/* macros to clean the pg result */
-#define IFCLEARPGRES(pgres) if (pgres) {PQclear(pgres); pgres = NULL;}
-#define CLEARPGRES(pgres) PQclear(pgres); pgres = NULL
+/* macro to clean the pg result */
+#define CLEARPGRES(pgres) do { PQclear(pgres); pgres = NULL; } while (0)
/* exported functions */
HIDDEN PGresult *pq_get_last_result(connectionObject *conn);
diff --git a/psycopg/psycopg.h b/psycopg/psycopg.h
index 2e86dca..5a6bf24 100644
--- a/psycopg/psycopg.h
+++ b/psycopg/psycopg.h
@@ -116,14 +116,14 @@ typedef struct {
/* the Decimal type, used by the DECIMAL typecaster */
HIDDEN PyObject *psyco_GetDecimalType(void);
-/* forward declaration */
+/* forward declarations */
typedef struct cursorObject cursorObject;
+typedef struct connectionObject connectionObject;
/* some utility functions */
-RAISES HIDDEN void psyco_set_error(PyObject *exc, cursorObject *curs, const char *msg,
- const char *pgerror, const char *pgcode);
+RAISES HIDDEN PyObject *psyco_set_error(PyObject *exc, cursorObject *curs, const char *msg);
-HIDDEN char *psycopg_escape_string(PyObject *conn,
+HIDDEN char *psycopg_escape_string(connectionObject *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);
diff --git a/psycopg/psycopgmodule.c b/psycopg/psycopgmodule.c
index b1b4979..206a788 100644
--- a/psycopg/psycopgmodule.c
+++ b/psycopg/psycopgmodule.c
@@ -35,6 +35,8 @@
#include "psycopg/typecast.h"
#include "psycopg/microprotocols.h"
#include "psycopg/microprotocols_proto.h"
+#include "psycopg/error.h"
+#include "psycopg/diagnostics.h"
#include "psycopg/adapter_qstring.h"
#include "psycopg/adapter_binary.h"
@@ -175,8 +177,7 @@ psyco_register_type(PyObject *self, PyObject *args)
if (0 > typecast_add(type, NULL, 0)) { return NULL; }
}
- Py_INCREF(Py_None);
- return Py_None;
+ Py_RETURN_NONE;
}
@@ -408,8 +409,8 @@ static struct {
PyObject **base;
const char *docstr;
} exctable[] = {
- { "psycopg2.Error", &Error, 0, Error_doc },
- { "psycopg2.Warning", &Warning, 0, Warning_doc },
+ { "psycopg2.Error", &Error, NULL, Error_doc },
+ { "psycopg2.Warning", &Warning, NULL, Warning_doc },
{ "psycopg2.InterfaceError", &InterfaceError, &Error, InterfaceError_doc },
{ "psycopg2.DatabaseError", &DatabaseError, &Error, DatabaseError_doc },
{ "psycopg2.InternalError", &InternalError, &DatabaseError, InternalError_doc },
@@ -433,61 +434,6 @@ static struct {
};
-#if PY_VERSION_HEX >= 0x02050000
-
-/* 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;
-}
-
-#endif /* PY_VERSION_HEX >= 0x02050000 */
-
static int
psyco_errors_init(void)
{
@@ -497,17 +443,13 @@ psyco_errors_init(void)
int i;
PyObject *dict = NULL;
- PyObject *base;
PyObject *str = NULL;
- PyObject *descr = NULL;
int rv = -1;
-#if PY_VERSION_HEX >= 0x02050000
- static PyMethodDef psyco_error_reduce_ex_def =
- {"__reduce_ex__", psyco_error_reduce_ex, METH_VARARGS, "pickle helper"};
-#endif
+ /* 'Error' has been defined elsewhere: only init the other classes */
+ Error = (PyObject *)&errorType;
- for (i=0; exctable[i].name; i++) {
+ for (i = 1; exctable[i].name; i++) {
if (!(dict = PyDict_New())) { goto exit; }
if (exctable[i].docstr) {
@@ -516,51 +458,20 @@ psyco_errors_init(void)
Py_CLEAR(str);
}
- 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;
-
+ /* can't put PyExc_StandardError in the static exctable:
+ * windows build will fail */
if (!(*exctable[i].exc = PyErr_NewException(
- exctable[i].name, base, dict))) {
+ exctable[i].name,
+ exctable[i].base ? *exctable[i].base : PyExc_StandardError,
+ dict))) {
goto exit;
}
Py_CLEAR(dict);
}
- /* Make pgerror, pgcode and cursor default to None on psycopg
- error objects. This simplifies error handling code that checks
- these attributes. */
- 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;
@@ -604,11 +515,10 @@ psyco_errors_set(PyObject *type)
Create a new error of the given type with extra attributes. */
-RAISES void
-psyco_set_error(PyObject *exc, cursorObject *curs, const char *msg,
- const char *pgerror, const char *pgcode)
+/* TODO: may have been changed to BORROWED */
+RAISES PyObject *
+psyco_set_error(PyObject *exc, cursorObject *curs, const char *msg)
{
- PyObject *t;
PyObject *pymsg;
PyObject *err = NULL;
connectionObject *conn = NULL;
@@ -624,31 +534,24 @@ psyco_set_error(PyObject *exc, cursorObject *curs, const char *msg,
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;
+ return NULL;
}
- if (err) {
+ if (err && PyObject_TypeCheck(err, &errorType)) {
+ errorObject *perr = (errorObject *)err;
if (curs) {
- PyObject_SetAttrString(err, "cursor", (PyObject *)curs);
- }
-
- if (pgerror) {
- if ((t = conn_text_from_chars(conn, pgerror))) {
- PyObject_SetAttrString(err, "pgerror", t);
- Py_DECREF(t);
- }
- }
-
- if (pgcode) {
- if ((t = conn_text_from_chars(conn, pgcode))) {
- PyObject_SetAttrString(err, "pgcode", t);
- Py_DECREF(t);
- }
+ Py_CLEAR(perr->cursor);
+ Py_INCREF(curs);
+ perr->cursor = curs;
}
+ }
+ if (err) {
PyErr_SetObject(exc, err);
Py_DECREF(err);
}
+
+ return err;
}
@@ -868,39 +771,59 @@ INIT_MODULE(_psycopg)(void)
/* initialize all the new types and then the module */
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;
+
+ Py_TYPE(&cursorType) = &PyType_Type;
if (PyType_Ready(&cursorType) == -1) goto exit;
+
+ Py_TYPE(&typecastType) = &PyType_Type;
if (PyType_Ready(&typecastType) == -1) goto exit;
+
+ Py_TYPE(&qstringType) = &PyType_Type;
if (PyType_Ready(&qstringType) == -1) goto exit;
+
+ Py_TYPE(&binaryType) = &PyType_Type;
if (PyType_Ready(&binaryType) == -1) goto exit;
+
+ Py_TYPE(&isqlquoteType) = &PyType_Type;
if (PyType_Ready(&isqlquoteType) == -1) goto exit;
+
+ Py_TYPE(&pbooleanType) = &PyType_Type;
if (PyType_Ready(&pbooleanType) == -1) goto exit;
+
+ Py_TYPE(&pintType) = &PyType_Type;
if (PyType_Ready(&pintType) == -1) goto exit;
+
+ Py_TYPE(&pfloatType) = &PyType_Type;
if (PyType_Ready(&pfloatType) == -1) goto exit;
+
+ Py_TYPE(&pdecimalType) = &PyType_Type;
if (PyType_Ready(&pdecimalType) == -1) goto exit;
+
+ Py_TYPE(&asisType) = &PyType_Type;
if (PyType_Ready(&asisType) == -1) goto exit;
+
+ Py_TYPE(&listType) = &PyType_Type;
if (PyType_Ready(&listType) == -1) goto exit;
+
+ Py_TYPE(&chunkType) = &PyType_Type;
if (PyType_Ready(&chunkType) == -1) goto exit;
- if (PyType_Ready(&NotifyType) == -1) goto exit;
- if (PyType_Ready(&XidType) == -1) goto exit;
+
+ Py_TYPE(&notifyType) = &PyType_Type;
+ if (PyType_Ready(&notifyType) == -1) goto exit;
+
+ Py_TYPE(&xidType) = &PyType_Type;
+ if (PyType_Ready(&xidType) == -1) goto exit;
+
+ Py_TYPE(&errorType) = &PyType_Type;
+ errorType.tp_base = (PyTypeObject *)PyExc_StandardError;
+ if (PyType_Ready(&errorType) == -1) goto exit;
+
+ Py_TYPE(&diagnosticsType) = &PyType_Type;
+ if (PyType_Ready(&diagnosticsType) == -1) goto exit;
#ifdef PSYCOPG_EXTENSIONS
- Py_TYPE(&lobjectType) = &PyType_Type;
+ Py_TYPE(&lobjectType) = &PyType_Type;
if (PyType_Ready(&lobjectType) == -1) goto exit;
#endif
@@ -908,6 +831,7 @@ INIT_MODULE(_psycopg)(void)
#ifdef HAVE_MXDATETIME
Py_TYPE(&mxdatetimeType) = &PyType_Type;
if (PyType_Ready(&mxdatetimeType) == -1) goto exit;
+
if (0 != mxDateTime_ImportModuleAndAPI()) {
PyErr_Clear();
@@ -985,8 +909,9 @@ INIT_MODULE(_psycopg)(void)
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);
+ PyModule_AddObject(module, "Notify", (PyObject*)&notifyType);
+ PyModule_AddObject(module, "Xid", (PyObject*)&xidType);
+ PyModule_AddObject(module, "Diagnostics", (PyObject*)&diagnosticsType);
#ifdef PSYCOPG_EXTENSIONS
PyModule_AddObject(module, "lobject", (PyObject*)&lobjectType);
#endif
@@ -1015,31 +940,6 @@ INIT_MODULE(_psycopg)(void)
if (0 != psyco_errors_init()) { goto exit; }
psyco_errors_fill(dict);
- /* Solve win32 build issue about non-constant initializer element */
- cursorType.tp_alloc = PyType_GenericAlloc;
- 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:
diff --git a/psycopg/python.h b/psycopg/python.h
index f6d6be0..90c8251 100644
--- a/psycopg/python.h
+++ b/psycopg/python.h
@@ -31,36 +31,12 @@
#include <stringobject.h>
#endif
-#if PY_VERSION_HEX < 0x02040000
-# error "psycopg requires Python >= 2.4"
-#endif
-
#if PY_VERSION_HEX < 0x02050000
-/* Function missing in Py 2.4 */
-#define PyErr_WarnEx(cat,msg,lvl) PyErr_Warn(cat,msg)
-#endif
-
-#if PY_VERSION_HEX < 0x02050000 && !defined(PY_SSIZE_T_MIN)
- typedef int Py_ssize_t;
- #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
- #define charbufferproc getcharbufferproc
-
- #define CONV_CODE_PY_SSIZE_T "i"
-#else
- #define CONV_CODE_PY_SSIZE_T "n"
+# error "psycopg requires Python >= 2.5"
#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
+ * 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
@@ -76,11 +52,6 @@ typedef unsigned long Py_uhash_t;
#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"
@@ -114,6 +85,7 @@ typedef unsigned long Py_uhash_t;
#define PyInt_AsLong PyLong_AsLong
#define PyInt_FromLong PyLong_FromLong
#define PyInt_FromSsize_t PyLong_FromSsize_t
+#define PyExc_StandardError PyExc_Exception
#define PyString_FromFormat PyUnicode_FromFormat
#define Py_TPFLAGS_HAVE_ITER 0L
#define Py_TPFLAGS_HAVE_RICHCOMPARE 0L
diff --git a/psycopg/typecast.c b/psycopg/typecast.c
index 8504631..9678a36 100644
--- a/psycopg/typecast.c
+++ b/psycopg/typecast.c
@@ -414,26 +414,27 @@ static struct PyMemberDef typecastObject_members[] = {
{NULL}
};
-static void
-typecast_dealloc(PyObject *obj)
+static int
+typecast_clear(typecastObject *self)
{
- typecastObject *self = (typecastObject*)obj;
-
- PyObject_GC_UnTrack(self);
-
Py_CLEAR(self->values);
Py_CLEAR(self->name);
Py_CLEAR(self->pcast);
Py_CLEAR(self->bcast);
+ return 0;
+}
- Py_TYPE(obj)->tp_free(obj);
+static void
+typecast_dealloc(typecastObject *self)
+{
+ PyObject_GC_UnTrack(self);
+ typecast_clear(self);
+ Py_TYPE(self)->tp_free((PyObject *)self);
}
static int
-typecast_traverse(PyObject *obj, visitproc visit, void *arg)
+typecast_traverse(typecastObject *self, visitproc visit, void *arg)
{
- typecastObject *self = (typecastObject*)obj;
-
Py_VISIT(self->values);
Py_VISIT(self->name);
Py_VISIT(self->pcast);
@@ -441,12 +442,6 @@ typecast_traverse(PyObject *obj, visitproc visit, void *arg)
return 0;
}
-static void
-typecast_del(void *self)
-{
- PyObject_GC_Del(self);
-}
-
static PyObject *
typecast_repr(PyObject *self)
{
@@ -479,8 +474,7 @@ typecast_call(PyObject *obj, PyObject *args, PyObject *kwargs)
// 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;
+ Py_RETURN_NONE;
}
return typecast_cast(obj, string, length, cursor);
@@ -489,10 +483,8 @@ typecast_call(PyObject *obj, PyObject *args, PyObject *kwargs)
PyTypeObject typecastType = {
PyVarObject_HEAD_INIT(NULL, 0)
"psycopg2._psycopg.type",
- sizeof(typecastObject),
- 0,
-
- typecast_dealloc, /*tp_dealloc*/
+ sizeof(typecastObject), 0,
+ (destructor)typecast_dealloc, /*tp_dealloc*/
0, /*tp_print*/
0, /*tp_getattr*/
0, /*tp_setattr*/
@@ -502,48 +494,31 @@ PyTypeObject typecastType = {
0, /*tp_as_sequence*/
0, /*tp_as_mapping*/
0, /*tp_hash */
-
typecast_call, /*tp_call*/
0, /*tp_str*/
0, /*tp_getattro*/
0, /*tp_setattro*/
0, /*tp_as_buffer*/
-
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_RICHCOMPARE |
Py_TPFLAGS_HAVE_GC, /*tp_flags*/
"psycopg type-casting object", /*tp_doc*/
-
- typecast_traverse, /*tp_traverse*/
- 0, /*tp_clear*/
-
+ (traverseproc)typecast_traverse, /*tp_traverse*/
+ (inquiry)typecast_clear, /*tp_clear*/
typecast_richcompare, /*tp_richcompare*/
0, /*tp_weaklistoffset*/
-
0, /*tp_iter*/
0, /*tp_iternext*/
-
- /* Attribute descriptor and subclassing stuff */
-
- 0, /*tp_methods*/
+ 0, /*tp_methods*/
typecastObject_members, /*tp_members*/
0, /*tp_getset*/
0, /*tp_base*/
0, /*tp_dict*/
-
0, /*tp_descr_get*/
0, /*tp_descr_set*/
0, /*tp_dictoffset*/
-
- 0, /*tp_init*/
- 0, /*tp_alloc will be set to PyType_GenericAlloc in module init*/
- 0, /*tp_new*/
- typecast_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*/
+ 0, /*tp_init*/
+ 0, /*tp_alloc*/
+ 0, /*tp_new*/
};
static PyObject *
diff --git a/psycopg/typecast_array.c b/psycopg/typecast_array.c
index 21e2aff..adf07ee 100644
--- a/psycopg/typecast_array.c
+++ b/psycopg/typecast_array.c
@@ -253,7 +253,7 @@ typecast_GENERIC_ARRAY_cast(const char *str, Py_ssize_t len, PyObject *curs)
Dprintf("typecast_GENERIC_ARRAY_cast: str = '%s',"
" len = " FORMAT_CODE_PY_SSIZE_T, str, len);
- if (str == NULL) {Py_INCREF(Py_None); return Py_None;}
+ if (str == NULL) { Py_RETURN_NONE; }
if (str[0] == '[')
typecast_array_cleanup(&str, &len);
if (str[0] != '{') {
diff --git a/psycopg/typecast_basic.c b/psycopg/typecast_basic.c
index 5e2a93e..a31047f 100644
--- a/psycopg/typecast_basic.c
+++ b/psycopg/typecast_basic.c
@@ -31,7 +31,7 @@ typecast_INTEGER_cast(const char *s, Py_ssize_t len, PyObject *curs)
{
char buffer[12];
- if (s == NULL) {Py_INCREF(Py_None); return Py_None;}
+ if (s == NULL) { Py_RETURN_NONE; }
if (s[len] != '\0') {
strncpy(buffer, s, (size_t) len); buffer[len] = '\0';
s = buffer;
@@ -49,7 +49,7 @@ typecast_LONGINTEGER_cast(const char *s, Py_ssize_t len, PyObject *curs)
{
char buffer[24];
- if (s == NULL) {Py_INCREF(Py_None); return Py_None;}
+ if (s == NULL) { Py_RETURN_NONE; }
if (s[len] != '\0') {
strncpy(buffer, s, (size_t) len); buffer[len] = '\0';
s = buffer;
@@ -64,7 +64,7 @@ typecast_FLOAT_cast(const char *s, Py_ssize_t len, PyObject *curs)
{
PyObject *str = NULL, *flo = NULL;
- if (s == NULL) {Py_INCREF(Py_None); return Py_None;}
+ if (s == NULL) { Py_RETURN_NONE; }
if (!(str = Text_FromUTF8AndSize(s, len))) { return NULL; }
#if PY_MAJOR_VERSION < 3
flo = PyFloat_FromString(str, NULL);
@@ -81,7 +81,7 @@ typecast_FLOAT_cast(const char *s, Py_ssize_t len, PyObject *curs)
static PyObject *
typecast_STRING_cast(const char *s, Py_ssize_t len, PyObject *curs)
{
- if (s == NULL) {Py_INCREF(Py_None); return Py_None;}
+ if (s == NULL) { Py_RETURN_NONE; }
return PyString_FromStringAndSize(s, len);
}
#else
@@ -95,7 +95,7 @@ typecast_UNICODE_cast(const char *s, Py_ssize_t len, PyObject *curs)
{
char *enc;
- if (s == NULL) {Py_INCREF(Py_None); return Py_None;}
+ if (s == NULL) { Py_RETURN_NONE; }
enc = ((cursorObject*)curs)->conn->codec;
return PyUnicode_Decode(s, len, enc, NULL);
@@ -108,7 +108,7 @@ typecast_BOOLEAN_cast(const char *s, Py_ssize_t len, PyObject *curs)
{
PyObject *res;
- if (s == NULL) {Py_INCREF(Py_None); return Py_None;}
+ if (s == NULL) { Py_RETURN_NONE; }
if (s[0] == 't')
res = Py_True;
@@ -128,7 +128,7 @@ typecast_DECIMAL_cast(const char *s, Py_ssize_t len, PyObject *curs)
PyObject *decimalType;
char *buffer;
- if (s == NULL) {Py_INCREF(Py_None); return Py_None;}
+ if (s == NULL) { Py_RETURN_NONE; }
if ((buffer = PyMem_Malloc(len+1)) == NULL)
return PyErr_NoMemory();
diff --git a/psycopg/typecast_binary.c b/psycopg/typecast_binary.c
index 49eb547..ce68fb8 100644
--- a/psycopg/typecast_binary.c
+++ b/psycopg/typecast_binary.c
@@ -55,7 +55,6 @@ chunk_repr(chunkObject *self)
#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)
{
@@ -90,9 +89,15 @@ static PyBufferProcs chunk_as_buffer =
/* 3.0 buffer interface */
int chunk_getbuffer(PyObject *_self, Py_buffer *view, int flags)
{
+ int rv;
chunkObject *self = (chunkObject*)_self;
- return PyBuffer_FillInfo(view, _self, self->base, self->len, 1, flags);
+ rv = PyBuffer_FillInfo(view, _self, self->base, self->len, 1, flags);
+ if (rv == 0) {
+ view->format = "c";
+ }
+ return rv;
}
+
static PyBufferProcs chunk_as_buffer =
{
chunk_getbuffer,
@@ -105,9 +110,8 @@ static PyBufferProcs chunk_as_buffer =
PyTypeObject chunkType = {
PyVarObject_HEAD_INIT(NULL, 0)
- "psycopg2._psycopg.chunk", /* tp_name */
- sizeof(chunkObject), /* tp_basicsize */
- 0, /* tp_itemsize */
+ "psycopg2._psycopg.chunk",
+ sizeof(chunkObject), 0,
(destructor) chunk_dealloc, /* tp_dealloc*/
0, /* tp_print */
0, /* tp_getattr */
@@ -142,7 +146,7 @@ typecast_BINARY_cast(const char *s, Py_ssize_t l, PyObject *curs)
char *buffer = NULL;
Py_ssize_t len;
- if (s == NULL) {Py_INCREF(Py_None); return Py_None;}
+ if (s == NULL) { Py_RETURN_NONE; }
if (s[0] == '\\' && s[1] == 'x') {
/* This is a buffer escaped in hex format: libpq before 9.0 can't
diff --git a/psycopg/typecast_datetime.c b/psycopg/typecast_datetime.c
index 18e9d0d..ad74101 100644
--- a/psycopg/typecast_datetime.c
+++ b/psycopg/typecast_datetime.c
@@ -48,7 +48,7 @@ typecast_PYDATE_cast(const char *str, Py_ssize_t len, PyObject *curs)
PyObject* obj = NULL;
int n, y=0, m=0, d=0;
- if (str == NULL) {Py_INCREF(Py_None); return Py_None;}
+ if (str == NULL) { Py_RETURN_NONE; }
if (!strcmp(str, "infinity") || !strcmp(str, "-infinity")) {
if (str[0] == '-') {
@@ -92,7 +92,7 @@ typecast_PYDATETIME_cast(const char *str, Py_ssize_t len, PyObject *curs)
int hh=0, mm=0, ss=0, us=0, tz=0;
const char *tp = NULL;
- if (str == NULL) {Py_INCREF(Py_None); return Py_None;}
+ if (str == NULL) { Py_RETURN_NONE; }
/* check for infinity */
if (!strcmp(str, "infinity") || !strcmp(str, "-infinity")) {
@@ -177,7 +177,7 @@ typecast_PYTIME_cast(const char *str, Py_ssize_t len, PyObject *curs)
PyObject *tzinfo_factory;
int n, hh=0, mm=0, ss=0, us=0, tz=0;
- if (str == NULL) {Py_INCREF(Py_None); return Py_None;}
+ if (str == NULL) { Py_RETURN_NONE; }
n = typecast_parse_time(str, NULL, &len, &hh, &mm, &ss, &us, &tz);
Dprintf("typecast_PYTIME_cast: n = %d, len = " FORMAT_CODE_PY_SSIZE_T ", "
@@ -226,7 +226,7 @@ typecast_PYINTERVAL_cast(const char *str, Py_ssize_t len, PyObject *curs)
int part = 0, sec;
double micro;
- if (str == NULL) {Py_INCREF(Py_None); return Py_None;}
+ if (str == NULL) { Py_RETURN_NONE; }
Dprintf("typecast_PYINTERVAL_cast: s = %s", str);
diff --git a/psycopg/typecast_mxdatetime.c b/psycopg/typecast_mxdatetime.c
index bf69af5..4b03d15 100644
--- a/psycopg/typecast_mxdatetime.c
+++ b/psycopg/typecast_mxdatetime.c
@@ -51,7 +51,7 @@ typecast_MXDATE_cast(const char *str, Py_ssize_t len, PyObject *curs)
int hh=0, mm=0, ss=0, us=0, tz=0;
const char *tp = NULL;
- if (str == NULL) {Py_INCREF(Py_None); return Py_None;}
+ if (str == NULL) { Py_RETURN_NONE; }
Dprintf("typecast_MXDATE_cast: s = %s", str);
@@ -99,7 +99,7 @@ typecast_MXTIME_cast(const char *str, Py_ssize_t len, PyObject *curs)
{
int n, hh=0, mm=0, ss=0, us=0, tz=0;
- if (str == NULL) {Py_INCREF(Py_None); return Py_None;}
+ if (str == NULL) { Py_RETURN_NONE; }
Dprintf("typecast_MXTIME_cast: s = %s", str);
@@ -129,7 +129,7 @@ typecast_MXINTERVAL_cast(const char *str, Py_ssize_t len, PyObject *curs)
double v = 0.0, sign = 1.0;
int part = 0;
- if (str == NULL) {Py_INCREF(Py_None); return Py_None;}
+ if (str == NULL) { Py_RETURN_NONE; }
Dprintf("typecast_MXINTERVAL_cast: s = %s", str);
diff --git a/psycopg/utils.c b/psycopg/utils.c
index 57586c5..6b035cf 100644
--- a/psycopg/utils.c
+++ b/psycopg/utils.c
@@ -32,21 +32,33 @@
#include <string.h>
#include <stdlib.h>
+/* Escape a string for sql inclusion.
+ *
+ * The function must be called holding the GIL.
+ *
+ * Return a pointer to a new string on the Python heap on success, else NULL
+ * and set an exception. The returned string includes quotes and leading E if
+ * needed.
+ *
+ * If tolen is set, it will contain the length of the escaped string,
+ * including quotes.
+ */
char *
-psycopg_escape_string(PyObject *obj, const char *from, Py_ssize_t len,
+psycopg_escape_string(connectionObject *conn, const char *from, Py_ssize_t len,
char *to, Py_ssize_t *tolen)
{
Py_ssize_t ql;
- connectionObject *conn = (connectionObject*)obj;
- int eq = (conn && (conn->equote)) ? 1 : 0;
+ int eq = (conn && (conn->equote)) ? 1 : 0;
if (len == 0)
len = strlen(from);
-
+
if (to == NULL) {
to = (char *)PyMem_Malloc((len * 2 + 4) * sizeof(char));
- if (to == NULL)
+ if (to == NULL) {
+ PyErr_NoMemory();
return NULL;
+ }
}
{
@@ -59,15 +71,19 @@ psycopg_escape_string(PyObject *obj, const char *from, Py_ssize_t len,
ql = PQescapeString(to+eq+1, from, len);
}
- if (eq)
+ if (eq) {
to[0] = 'E';
- to[eq] = '\'';
- to[ql+eq+1] = '\'';
- to[ql+eq+2] = '\0';
+ to[1] = to[ql+2] = '\'';
+ to[ql+3] = '\0';
+ }
+ else {
+ to[0] = to[ql+1] = '\'';
+ to[ql+2] = '\0';
+ }
if (tolen)
*tolen = ql+eq+2;
-
+
return to;
}
@@ -115,10 +131,16 @@ psycopg_escape_identifier_easy(const char *from, Py_ssize_t len)
*
* Store the return in 'to' and return 0 in case of success, else return -1
* and raise an exception.
+ *
+ * If from is null, store null into to.
*/
RAISES_NEG int
psycopg_strdup(char **to, const char *from, Py_ssize_t len)
{
+ if (!from) {
+ *to = NULL;
+ return 0;
+ }
if (!len) { len = strlen(from); }
if (!(*to = PyMem_Malloc(len + 1))) {
PyErr_NoMemory();
diff --git a/psycopg/xid.h b/psycopg/xid.h
index e63db61..6f641be 100644
--- a/psycopg/xid.h
+++ b/psycopg/xid.h
@@ -27,7 +27,7 @@
#ifndef PSYCOPG_XID_H
#define PSYCOPG_XID_H 1
-extern HIDDEN PyTypeObject XidType;
+extern HIDDEN PyTypeObject xidType;
typedef struct {
PyObject_HEAD
@@ -41,11 +41,11 @@ typedef struct {
PyObject *prepared;
PyObject *owner;
PyObject *database;
-} XidObject;
+} xidObject;
-HIDDEN XidObject *xid_ensure(PyObject *oxid);
-HIDDEN XidObject *xid_from_string(PyObject *s);
-HIDDEN PyObject *xid_get_tid(XidObject *self);
+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 --git a/psycopg/xid_type.c b/psycopg/xid_type.c
index b38d914..ab6c33e 100644
--- a/psycopg/xid_type.c
+++ b/psycopg/xid_type.c
@@ -67,46 +67,28 @@ 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 },
+ { "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;
+ return type->tp_alloc(type, 0);
}
static int
-xid_init(XidObject *self, PyObject *args, PyObject *kwargs)
+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, &gtrid, &bqual))
@@ -149,35 +131,18 @@ xid_init(XidObject *self, PyObject *args, PyObject *kwargs)
}
}
- 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);
+ if (!(self->format_id = PyInt_FromLong(format_id))) { return -1; }
+ if (!(self->gtrid = Text_FromUTF8(gtrid))) { return -1; }
+ if (!(self->bqual = Text_FromUTF8(bqual))) { return -1; }
+ 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 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)
+xid_dealloc(xidObject *self)
{
Py_CLEAR(self->format_id);
Py_CLEAR(self->gtrid);
@@ -189,20 +154,14 @@ xid_dealloc(XidObject *self)
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)
+xid_len(xidObject *self)
{
return 3;
}
static PyObject *
-xid_getitem(XidObject *self, Py_ssize_t item)
+xid_getitem(xidObject *self, Py_ssize_t item)
{
if (item < 0)
item += 3;
@@ -224,13 +183,13 @@ xid_getitem(XidObject *self, Py_ssize_t item)
}
static PyObject *
-xid_str(XidObject *self)
+xid_str(xidObject *self)
{
return xid_get_tid(self);
}
static PyObject *
-xid_repr(XidObject *self)
+xid_repr(xidObject *self)
{
PyObject *rv = NULL;
PyObject *format = NULL;
@@ -305,66 +264,45 @@ static struct PyMethodDef xid_methods[] = {
{NULL}
};
-PyTypeObject XidType = {
+PyTypeObject xidType = {
PyVarObject_HEAD_INIT(NULL, 0)
"psycopg2.extensions.Xid",
- sizeof(XidObject),
- 0,
+ 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*/
+ /* Notify is not GC as it only has string attributes */
+ Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /*tp_flags*/
xid_doc, /*tp_doc*/
-
- (traverseproc)xid_traverse, /*tp_traverse*/
+ 0, /*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*/
+ 0, /*tp_alloc*/
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*/
};
@@ -376,13 +314,13 @@ PyTypeObject XidType = {
* 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 *xid_ensure(PyObject *oxid)
{
- XidObject *rv = NULL;
+ xidObject *rv = NULL;
- if (PyObject_TypeCheck(oxid, &XidType)) {
+ if (PyObject_TypeCheck(oxid, &xidType)) {
Py_INCREF(oxid);
- rv = (XidObject *)oxid;
+ rv = (xidObject *)oxid;
}
else {
rv = xid_from_string(oxid);
@@ -445,7 +383,7 @@ _xid_decode64(PyObject *s)
* http://cvs.pgfoundry.org/cgi-bin/cvsweb.cgi/jdbc/pgjdbc/org/postgresql/xa/RecoveredXid.java?rev=1.2
*/
PyObject *
-xid_get_tid(XidObject *self)
+xid_get_tid(xidObject *self)
{
PyObject *rv = NULL;
PyObject *egtrid = NULL;
@@ -525,7 +463,7 @@ exit:
*
* Return NULL + exception if parsing failed. Else a new Xid object. */
-static XidObject *
+static xidObject *
_xid_parse_string(PyObject *str) {
PyObject *regex;
PyObject *m = NULL;
@@ -536,7 +474,7 @@ _xid_parse_string(PyObject *str) {
PyObject *ebqual = NULL;
PyObject *gtrid = NULL;
PyObject *bqual = NULL;
- XidObject *rv = NULL;
+ xidObject *rv = NULL;
/* check if the string is a possible XA triple with a regexp */
if (!(regex = _xid_get_parse_regex())) { goto exit; }
@@ -560,7 +498,7 @@ _xid_parse_string(PyObject *str) {
if (!(bqual = _xid_decode64(ebqual))) { goto exit; }
/* Try to build the xid with the parsed material */
- rv = (XidObject *)PyObject_CallFunctionObjArgs((PyObject *)&XidType,
+ rv = (xidObject *)PyObject_CallFunctionObjArgs((PyObject *)&xidType,
format_id, gtrid, bqual, NULL);
exit:
@@ -579,35 +517,31 @@ exit:
/* Return a new Xid object representing a transaction ID not conform to
* the XA specifications. */
-static XidObject *
+static xidObject *
_xid_unparsed_from_string(PyObject *str) {
- XidObject *xid = NULL;
- XidObject *rv = NULL;
- PyObject *tmp;
+ xidObject *xid = NULL;
+ xidObject *rv = NULL;
/* fake args to work around the checks performed by the xid init */
- if (!(xid = (XidObject *)PyObject_CallFunction((PyObject *)&XidType,
+ if (!(xid = (xidObject *)PyObject_CallFunction((PyObject *)&xidType,
"iss", 0, "", ""))) {
goto exit;
}
/* set xid.gtrid = str */
- tmp = xid->gtrid;
+ Py_CLEAR(xid->gtrid);
Py_INCREF(str);
xid->gtrid = str;
- Py_DECREF(tmp);
/* set xid.format_id = None */
- tmp = xid->format_id;
+ Py_CLEAR(xid->format_id);
Py_INCREF(Py_None);
xid->format_id = Py_None;
- Py_DECREF(tmp);
/* set xid.bqual = None */
- tmp = xid->bqual;
+ Py_CLEAR(xid->bqual);
Py_INCREF(Py_None);
xid->bqual = Py_None;
- Py_DECREF(tmp);
/* return the finished object */
rv = xid;
@@ -624,9 +558,9 @@ exit:
* If the xid is in the format generated by Psycopg, unpack the tuple into
* the struct members. Otherwise generate an "unparsed" xid.
*/
-XidObject *
+xidObject *
xid_from_string(PyObject *str) {
- XidObject *rv;
+ xidObject *rv;
if (!(Bytes_Check(str) || PyUnicode_Check(str))) {
PyErr_SetString(PyExc_TypeError, "not a valid transaction id");
@@ -654,7 +588,7 @@ xid_recover(PyObject *conn)
PyObject *rv = NULL;
PyObject *curs = NULL;
PyObject *xids = NULL;
- XidObject *xid = NULL;
+ xidObject *xid = NULL;
PyObject *recs = NULL;
PyObject *rec = NULL;
PyObject *item = NULL;
@@ -693,34 +627,25 @@ xid_recover(PyObject *conn)
/* 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;
+ Py_CLEAR(item);
/* set xid.prepared */
- if (!(item = PySequence_GetItem(rec, 1))) { goto exit; }
- tmp = xid->prepared;
- xid->prepared = item;
- Py_DECREF(tmp);
- item = NULL;
+ Py_CLEAR(xid->prepared);
+ if (!(xid->prepared = PySequence_GetItem(rec, 1))) { goto exit; }
/* set xid.owner */
- if (!(item = PySequence_GetItem(rec, 2))) { goto exit; }
- tmp = xid->owner;
- xid->owner = item;
- Py_DECREF(tmp);
- item = NULL;
+ Py_CLEAR(xid->owner);
+ if (!(xid->owner = PySequence_GetItem(rec, 2))) { goto exit; }
/* set xid.database */
- if (!(item = PySequence_GetItem(rec, 3))) { goto exit; }
- tmp = xid->database;
- xid->database = item;
- Py_DECREF(tmp);
- item = NULL;
+ Py_CLEAR(xid->database);
+ if (!(xid->database = PySequence_GetItem(rec, 3))) { goto exit; }
/* xid finished: add it to the returned list */
PyList_SET_ITEM(xids, i, (PyObject *)xid);
xid = NULL; /* ref stolen */
- Py_DECREF(rec); rec = NULL;
+ Py_CLEAR(rec);
}
/* set the return value. */
diff --git a/psycopg2da/DEPENDENCIES.cfg b/psycopg2da/DEPENDENCIES.cfg
deleted file mode 100644
index f4edbac..0000000
--- a/psycopg2da/DEPENDENCIES.cfg
+++ /dev/null
@@ -1,2 +0,0 @@
-psycopg2
-zope.app
diff --git a/psycopg2da/PACKAGE.cfg b/psycopg2da/PACKAGE.cfg
deleted file mode 100644
index 7e1bbfc..0000000
--- a/psycopg2da/PACKAGE.cfg
+++ /dev/null
@@ -1,24 +0,0 @@
-# Load the license from an external source, so we don't have to keep a
-# copy of it sitting around:
-<load>
- LICENSE.txt http://svn.zope.org/*checkout*/Zope3/trunk/ZopePublicLicense.txt?rev=25177
-</load>
-
-# Add a few things to the distribution root.
-<distribution>
- README.txt
-</distribution>
-
-# Specify what is included in the component.
-<collection>
-
- # Documentation files of the package:
- *.txt
-
- # Python modules from the package:
- *.py
-
- # Configuration files of the package:
- *.zcml
-
-</collection>
diff --git a/psycopg2da/PUBLICATION.cfg b/psycopg2da/PUBLICATION.cfg
deleted file mode 100644
index d408314..0000000
--- a/psycopg2da/PUBLICATION.cfg
+++ /dev/null
@@ -1,9 +0,0 @@
-Metadata-Version: 1.0
-Name: psycopg2da
-Summary: Psycopg2 Database Adapter for Zope 3
-Author: Fabio Tranchitella
-Author-email: kobold@debian.org
-License: ZPL 2.1
-Description:
- This package allows Zope 3 to connect to any PostGreSQL database via
- the common Zope 3 RDB connection facilities.
diff --git a/psycopg2da/README.txt b/psycopg2da/README.txt
deleted file mode 100644
index 48d2fdd..0000000
--- a/psycopg2da/README.txt
+++ /dev/null
@@ -1,79 +0,0 @@
-==========
-psycopg2da
-==========
-
-This file outlines the basics of using Zope3 with PostgreSQL via PsycopgDA.
-
-Installing PsycopgDA
---------------------
-
-1. Check out the psycopg2da package into a directory in your
- PYTHONPATH. INSTANCE_HOME/lib/python or Zope3/src is usually the
- most convenient place:
-
-
- svn co svn://svn.zope.org/repos/main/psycopg2da/trunk psycopg2da
-
-
-2. Copy `psycopg2-configure.zcml` to the `package-includes` directory
- of your Zope instance.
-
-
-Creating Database Connections
-------------------------------
-
-It is time to add some connections. A connection in Zope 3 is
-registered as a utility.
-
-3. Open a web browser on your Zope root folder (http://localhost:8080/
- if you use the default settings in zope.conf.in).
-
-4. Click on the 'Manage Site' action on the right side of the
- screen. You should see a screen which reads 'Common Site Management
- Tasks'
-
-5. Around the middle of that page, you should see a link named 'Add
- Utility'. Click on it.
-
-6. Select 'Psycopg DA' and type in a name at the bottom of the page.
-
-7. Enter the database connection string. It looks like this:
-
- dbi://username:password@host:port/databasename
-
-8. Click on the 'Add' button.
-
-9. You should be on a page which reads 'Add Database Connection
- Registration'. There you can configure the permission needed to use
- the database connection, the name of the registration and the
- registration status. You can use any name for 'Register As' field,
- as long as it doesn't clash with an existing one. Choose a
- permission. Choose between 'Registered' and 'Active' for the
- 'Registration Status'. Only one component of a kind can be 'Active'
- at a time, so be careful.
-
-10. You should be redirected to the 'Edit' screen of the connection
- utility.
-
-11. If you want to, you can go to the Test page and execute arbitrary
- SQL queries to see whether the connection is working as expected.
-
-
-Using SQL Scripts
------------------
-
-You can create SQL Scripts in the content space. For example:
-
-12. Go to Zope root.
-
-13. Add an SQL script (you can use the Common Tasks box on the left,
- or the Add action on the right).
-
-14. Click on the name of your new SQL script.
-
-15. Choose a connection name (the one you entered in step 29) from the
- drop-down.
-
-16. Enter your query and click on the 'Save Changes' button.
-
-17. You can test the script in the -- surprise! -- Test page.
diff --git a/psycopg2da/__init__.py b/psycopg2da/__init__.py
deleted file mode 100644
index fa81ada..0000000
--- a/psycopg2da/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-# empty file
diff --git a/psycopg2da/adapter.py b/psycopg2da/adapter.py
deleted file mode 100644
index 2ddc0a2..0000000
--- a/psycopg2da/adapter.py
+++ /dev/null
@@ -1,408 +0,0 @@
-# Copyright (C) 2006 Fabio Tranchitella <fabio@tranchitella.it>
-#
-# 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.
-#
-# 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.
-#
-# 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.
-#
-# 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
-from zope.rdb import ZopeDatabaseAdapter, parseDSN, ZopeConnection, ZopeCursor
-from zope.rdb.interfaces import DatabaseException, IZopeConnection
-from zope.publisher.interfaces import Retry
-
-from datetime import date, time, datetime, timedelta
-
-import psycopg2
-import psycopg2.extensions
-import re
-import sys
-
-# OIDs from psycopg/pgtypes.h
-DATE_OID = 1082
-TIME_OID = 1083
-TIMETZ_OID = 1266
-TIMESTAMP_OID = 1114
-TIMESTAMPTZ_OID = 1184
-INTERVAL_OID = 1186
-CHAR_OID = 18
-TEXT_OID = 25
-BPCHAR_OID = 1042
-VARCHAR_OID = 1043
-
-# date/time parsing functions
-_dateFmt = re.compile(r"^(\d\d\d\d)-?([01]\d)-?([0-3]\d)$")
-
-def parse_date(s):
- """Parses ISO-8601 compliant dates and returns a tuple (year, month,
- day).
-
- The following formats are accepted:
- YYYY-MM-DD (extended format)
- YYYYMMDD (basic format)
- """
- m = _dateFmt.match(s)
- if m is None:
- raise ValueError, 'invalid date string: %s' % s
- year, month, day = m.groups()
- return int(year), int(month), int(day)
-
-
-_timeFmt = re.compile(
- r"^([0-2]\d)(?::?([0-5]\d)(?::?([0-5]\d)(?:[.,](\d+))?)?)?$")
-
-def parse_time(s):
- """Parses ISO-8601 compliant times and returns a tuple (hour, minute,
- second).
-
- The following formats are accepted:
- HH:MM:SS.ssss or HHMMSS.ssss
- HH:MM:SS,ssss or HHMMSS,ssss
- HH:MM:SS or HHMMSS
- HH:MM or HHMM
- HH
- """
- m = _timeFmt.match(s)
- if m is None:
- raise ValueError, 'invalid time string: %s' % s
- hr, mn, sc, msc = m.groups(0)
- if msc != 0:
- sc = float("%s.%s" % (sc, msc))
- else:
- sc = int(sc)
- return int(hr), int(mn), sc
-
-
-_tzFmt = re.compile(r"^([+-])([0-2]\d)(?::?([0-5]\d))?$")
-
-def parse_tz(s):
- """Parses ISO-8601 timezones and returns the offset east of UTC in
- minutes.
-
- The following formats are accepted:
- +/-HH:MM
- +/-HHMM
- +/-HH
- Z (equivalent to +0000)
- """
- if s == 'Z':
- return 0
- m = _tzFmt.match(s)
- if m is None:
- raise ValueError, 'invalid time zone: %s' % s
- d, hoff, moff = m.groups(0)
- if d == "-":
- return - int(hoff) * 60 - int(moff)
- return int(hoff) * 60 + int(moff)
-
-
-_tzPos = re.compile(r"[Z+-]")
-
-def parse_timetz(s):
- """Parses ISO-8601 compliant times that may include timezone information
- and returns a tuple (hour, minute, second, tzoffset).
-
- tzoffset is the offset east of UTC in minutes. It will be None if s does
- not include time zone information.
-
- Formats accepted are those listed in the descriptions of parse_time() and
- parse_tz(). Time zone should immediatelly follow time without intervening
- spaces.
- """
- m = _tzPos.search(s)
- if m is None:
- return parse_time(s) + (None,)
- pos = m.start()
- return parse_time(s[:pos]) + (parse_tz(s[pos:]),)
-
-
-_datetimeFmt = re.compile(r"[T ]")
-
-def _split_datetime(s):
- """Split date and time parts of ISO-8601 compliant timestamp and
- return a tuple (date, time).
-
- ' ' or 'T' used to separate date and time parts.
- """
- m = _datetimeFmt.search(s)
- if m is None:
- raise ValueError, 'time part of datetime missing: %s' % s
- pos = m.start()
- return s[:pos], s[pos + 1:]
-
-
-def parse_datetime(s):
- """Parses ISO-8601 compliant timestamp and returns a tuple (year, month,
- day, hour, minute, second).
-
- Formats accepted are those listed in the descriptions of parse_date() and
- parse_time() with ' ' or 'T' used to separate date and time parts.
- """
- dt, tm = _split_datetime(s)
- return parse_date(dt) + parse_time(tm)
-
-
-def parse_datetimetz(s):
- """Parses ISO-8601 compliant timestamp that may include timezone
- information and returns a tuple (year, month, day, hour, minute, second,
- tzoffset).
-
- tzoffset is the offset east of UTC in minutes. It will be None if s does
- not include time zone information.
-
- Formats accepted are those listed in the descriptions of parse_date() and
- parse_timetz() with ' ' or 'T' used to separate date and time parts.
- """
- dt, tm = _split_datetime(s)
- return parse_date(dt) + parse_timetz(tm)
-
-
-def parse_interval(s):
- """Parses PostgreSQL interval notation and returns a tuple (years, months,
- days, hours, minutes, seconds).
-
- Values accepted:
- interval ::= date
- | time
- | date time
- date ::= date_comp
- | date date_comp
- date_comp ::= 1 'day'
- | number 'days'
- | 1 'month'
- | 1 'mon'
- | number 'months'
- | number 'mons'
- | 1 'year'
- | number 'years'
- time ::= number ':' number
- | number ':' number ':' number
- | number ':' number ':' number '.' fraction
- """
- years = months = days = 0
- hours = minutes = seconds = 0
- elements = s.split()
- # Tests with 7.4.6 on Ubuntu 5.4 interval output returns 'mon' and 'mons'
- # and not 'month' or 'months' as expected. I've fixed this and left
- # the original matches there too in case this is dependant on
- # OS or PostgreSQL release.
- for i in range(0, len(elements) - 1, 2):
- count, unit = elements[i:i+2]
- if unit == 'day' and count == '1':
- days += 1
- elif unit == 'days':
- days += int(count)
- elif unit == 'month' and count == '1':
- months += 1
- elif unit == 'mon' and count == '1':
- months += 1
- elif unit == 'months':
- months += int(count)
- elif unit == 'mons':
- months += int(count)
- elif unit == 'year' and count == '1':
- years += 1
- elif unit == 'years':
- years += int(count)
- else:
- raise ValueError, 'unknown time interval %s %s' % (count, unit)
- if len(elements) % 2 == 1:
- hours, minutes, seconds = parse_time(elements[-1])
- return (years, months, days, hours, minutes, seconds)
-
-
-# Type conversions
-def _conv_date(s, cursor):
- if s:
- return date(*parse_date(s))
-
-def _conv_time(s, cursor):
- if s:
- hr, mn, sc = parse_time(s)
- sc, micro = divmod(sc, 1.0)
- micro = round(micro * 1000000)
- return time(hr, mn, int(sc), int(micro))
-
-def _conv_timetz(s, cursor):
- if s:
- from zope.datetime import tzinfo
- hr, mn, sc, tz = parse_timetz(s)
- sc, micro = divmod(sc, 1.0)
- micro = round(micro * 1000000)
- if tz: tz = tzinfo(tz)
- return time(hr, mn, int(sc), int(micro), tz)
-
-def _conv_timestamp(s, cursor):
- if s:
- y, m, d, hr, mn, sc = parse_datetime(s)
- sc, micro = divmod(sc, 1.0)
- micro = round(micro * 1000000)
- return datetime(y, m, d, hr, mn, int(sc), int(micro))
-
-def _conv_timestamptz(s, cursor):
- if s:
- from zope.datetime import tzinfo
- y, m, d, hr, mn, sc, tz = parse_datetimetz(s)
- sc, micro = divmod(sc, 1.0)
- micro = round(micro * 1000000)
- if tz: tz = tzinfo(tz)
- return datetime(y, m, d, hr, mn, int(sc), int(micro), tz)
-
-def _conv_interval(s, cursor):
- if s:
- y, m, d, hr, mn, sc = parse_interval(s)
- if (y, m) != (0, 0):
- # XXX: Currently there's no way to represent years and months as
- # timedeltas
- return s
- else:
- return timedelta(days=d, hours=hr, minutes=mn, seconds=sc)
-
-def _get_string_conv(encoding):
- def _conv_string(s, cursor):
- if s is not None:
- s = s.decode(encoding)
- return s
- return _conv_string
-
-# User-defined types
-DATE = psycopg2.extensions.new_type((DATE_OID,), "ZDATE", _conv_date)
-TIME = psycopg2.extensions.new_type((TIME_OID,), "ZTIME", _conv_time)
-TIMETZ = psycopg2.extensions.new_type((TIMETZ_OID,), "ZTIMETZ", _conv_timetz)
-TIMESTAMP = psycopg2.extensions.new_type((TIMESTAMP_OID,), "ZTIMESTAMP", _conv_timestamp)
-TIMESTAMPTZ = psycopg2.extensions.new_type((TIMESTAMPTZ_OID,), "ZTIMESTAMPTZ", _conv_timestamptz)
-INTERVAL = psycopg2.extensions.new_type((INTERVAL_OID,), "ZINTERVAL", _conv_interval)
-
-def registerTypes(encoding):
- """Register type conversions for psycopg"""
- psycopg2.extensions.register_type(DATE)
- psycopg2.extensions.register_type(TIME)
- psycopg2.extensions.register_type(TIMETZ)
- psycopg2.extensions.register_type(TIMESTAMP)
- psycopg2.extensions.register_type(TIMESTAMPTZ)
- psycopg2.extensions.register_type(INTERVAL)
- STRING = psycopg2.extensions.new_type((CHAR_OID, TEXT_OID, BPCHAR_OID, VARCHAR_OID), "ZSTRING", _get_string_conv(encoding))
- psycopg2.extensions.register_type(STRING)
- psycopg2.extensions.register_type(psycopg2.extensions.UNICODEARRAY)
-
-
-dsn2option_mapping = {'host': 'host',
- 'port': 'port',
- 'dbname': 'dbname',
- 'username': 'user',
- 'password': 'password'}
-
-class Psycopg2Adapter(ZopeDatabaseAdapter):
- """A psycopg2 adapter for Zope3.
-
- The following type conversions are performed:
-
- DATE -> datetime.date
- TIME -> datetime.time
- TIMETZ -> datetime.time
- TIMESTAMP -> datetime.datetime
- TIMESTAMPTZ -> datetime.datetime
-
- XXX: INTERVAL cannot be represented exactly as datetime.timedelta since
- it might be something like '1 month', which is a variable number of days.
- """
-
- def connect(self):
- if not self.isConnected():
- try:
- self._v_connection = Psycopg2Connection(
- self._connection_factory(), self
- )
- except psycopg2.Error, error:
- raise DatabaseException, str(error)
-
- def registerTypes(self):
- registerTypes(self.getEncoding())
-
- def _connection_factory(self):
- """Create a psycopg2 DBI connection based on the DSN"""
- self.registerTypes()
- conn_info = parseDSN(self.dsn)
- conn_list = []
- for dsnname, optname in dsn2option_mapping.iteritems():
- if conn_info[dsnname]:
- conn_list.append('%s=%s' % (optname, conn_info[dsnname]))
- conn_str = ' '.join(conn_list)
- connection = psycopg2.connect(conn_str)
- connection.set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_SERIALIZABLE)
- return connection
-
- def disconnect(self):
- if self.isConnected():
- try:
- self._v_connection.close()
- except psycopg2.InterfaceError:
- pass
- self._v_connection = None
-
-
-def _handle_psycopg_exception(error):
- """Called from a exception handler for psycopg2.Error.
-
- If we have a serialization exception or a deadlock, we should retry the
- transaction by raising a Retry exception. Otherwise, we reraise.
- """
- if isinstance(error, psycopg2.extensions.TransactionRollbackError):
- raise Retry(sys.exc_info())
- raise
-
-
-class IPsycopg2ZopeConnection(IZopeConnection):
- """A marker interface stating that this connection uses PostgreSQL."""
-
-
-class Psycopg2Connection(ZopeConnection):
-
- implements(IPsycopg2ZopeConnection)
-
- def cursor(self):
- """See IZopeConnection"""
- return Psycopg2Cursor(self.conn.cursor(), self)
-
- def commit(self):
- try:
- ZopeConnection.commit(self)
- except psycopg2.Error, error:
- _handle_psycopg_exception(error)
-
-
-class Psycopg2Cursor(ZopeCursor):
-
- def execute(self, operation, parameters=None):
- """See IZopeCursor"""
- try:
- return ZopeCursor.execute(self, operation, parameters)
- except psycopg2.Error, error:
- _handle_psycopg_exception(error)
-
- def executemany(operation, seq_of_parameters=None):
- """See IZopeCursor"""
- raise RuntimeError, 'Oos'
- try:
- return ZopeCursor.execute(self, operation, seq_of_parameters)
- except psycopg2.Error, error:
- _handle_psycopg_exception(error)
diff --git a/psycopg2da/configure.zcml b/psycopg2da/configure.zcml
deleted file mode 100644
index 7671fb7..0000000
--- a/psycopg2da/configure.zcml
+++ /dev/null
@@ -1,51 +0,0 @@
-<configure
- xmlns="http://namespaces.zope.org/zope"
- xmlns:browser="http://namespaces.zope.org/browser"
- i18n_domain="psycopg2da">
-
- <class class=".adapter.Psycopg2Adapter">
- <factory id="zope.da.Psycopg2DA" />
- <require
- permission="zope.rdb.Use"
- interface="zope.rdb.interfaces.IZopeDatabaseAdapter"
- />
- <require
- permission="zope.ManageServices"
- interface="zope.rdb.interfaces.IZopeDatabaseAdapterManagement"
- />
- </class>
-
- <class class=".adapter.Psycopg2Connection">
- <require
- permission="zope.rdb.Use"
- interface="zope.rdb.interfaces.IZopeConnection"
- />
- </class>
-
- <class class=".adapter.Psycopg2Cursor">
- <require
- permission="zope.rdb.Use"
- interface="zope.rdb.interfaces.IZopeCursor"
- />
- </class>
-
- <browser:addform
- name="AddPsycopg2DA"
- schema="zope.rdb.interfaces.IManageableZopeDatabaseAdapter"
- label="Add Psycopg2 (PostGreSQL) Database Adapter"
- content_factory=".adapter.Psycopg2Adapter"
- arguments="dsn"
- fields="dsn"
- permission="zope.ManageContent"
- />
-
- <!-- Menu entry for "add utility" menu -->
- <browser:addMenuItem
- class=".adapter.Psycopg2Adapter"
- title="Psycopg2 DA"
- description="A PostgreSQL Database Adapter using the Psycopg2 driver"
- permission="zope.ManageApplication"
- view="AddPsycopg2DA"
- />
-
-</configure>
diff --git a/psycopg2da/psycopg2da-configure.zcml b/psycopg2da/psycopg2da-configure.zcml
deleted file mode 100644
index 2a39543..0000000
--- a/psycopg2da/psycopg2da-configure.zcml
+++ /dev/null
@@ -1 +0,0 @@
-<include package="psycopg2da" />
diff --git a/psycopg2da/tests.py b/psycopg2da/tests.py
deleted file mode 100644
index f8620cb..0000000
--- a/psycopg2da/tests.py
+++ /dev/null
@@ -1,395 +0,0 @@
-# Copyright (C) 2006 Fabio Tranchitella <fabio@tranchitella.it>
-#
-# 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.
-#
-# 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.
-#
-# 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
-from datetime import tzinfo, timedelta
-
-import psycopg2
-import psycopg2.extensions
-
-
-class Stub(object):
-
- def __init__(self, **kw):
- self.__dict__.update(kw)
-
-class TZStub(tzinfo):
-
- def __init__(self, h, m):
- self.offset = h * 60 + m
-
- def utcoffset(self, dt):
- return timedelta(minutes=self.offset)
-
- def dst(self, dt):
- return 0
-
- def tzname(self, dt):
- return ''
-
- def __repr__(self):
- return 'tzinfo(%d)' % self.offset
-
- def __reduce__(self):
- return type(self), (), self.__dict__
-
-class ConnectionStub(object):
-
- def set_isolation_level(self, level):
- pass
-
-class Psycopg2Stub(object):
-
- __shared_state = {} # 'Borg' design pattern
-
- DATE = psycopg2.extensions.DATE
- TIME = psycopg2.extensions.TIME
- DATETIME = psycopg2.DATETIME
- INTERVAL = psycopg2.extensions.INTERVAL
- ISOLATION_LEVEL_SERIALIZABLE = psycopg2.extensions.ISOLATION_LEVEL_SERIALIZABLE
-
- def __init__(self):
- self.__dict__ = self.__shared_state
- self.types = {}
-
- def connect(self, connection_string):
- self.last_connection_string = connection_string
- return ConnectionStub()
-
- def new_type(self, values, name, converter):
- return Stub(name=name, values=values)
-
- def register_type(self, type):
- for typeid in type.values:
- self.types[typeid] = type
-
- def getExtensions(self):
- return self
- extensions = property(getExtensions)
-
-
-class TestPsycopg2TypeConversion(TestCase):
-
- def test_conv_date(self):
- from psycopg2da.adapter import _conv_date
- from datetime import date
- def c(s):
- return _conv_date(s, None)
- self.assertEquals(c(''), None)
- self.assertEquals(c('2001-03-02'), date(2001, 3, 2))
-
- def test_conv_time(self):
- from psycopg2da.adapter import _conv_time
- from datetime import time
- def c(s):
- return _conv_time(s, None)
- self.assertEquals(c(''), None)
- self.assertEquals(c('23:17:57'), time(23, 17, 57))
- self.assertEquals(c('23:17:57.037'), time(23, 17, 57, 37000))
-
- def test_conv_timetz(self):
- from psycopg2da.adapter import _conv_timetz
- from datetime import time
- def c(s):
- return _conv_timetz(s, None)
- self.assertEquals(c(''), None)
- self.assertEquals(c('12:44:01+01:00'), time(12, 44, 01, 0, TZStub(1,0)))
- self.assertEquals(c('12:44:01.037-00:30'), time(12, 44, 01, 37000, TZStub(0,-30)))
-
- def test_conv_timestamp(self):
- from psycopg2da.adapter import _conv_timestamp
- from datetime import datetime
- def c(s):
- return _conv_timestamp(s, None)
- self.assertEquals(c(''), None)
- self.assertEquals(c('2001-03-02 12:44:01'),
- datetime(2001, 3, 2, 12, 44, 01))
- self.assertEquals(c('2001-03-02 12:44:01.037'),
- datetime(2001, 3, 2, 12, 44, 01, 37000))
- self.assertEquals(c('2001-03-02 12:44:01.000001'),
- datetime(2001, 3, 2, 12, 44, 01, 1))
-
- def test_conv_timestamptz(self):
- from psycopg2da.adapter import _conv_timestamptz
- from datetime import datetime
- def c(s):
- return _conv_timestamptz(s, None)
- self.assertEquals(c(''), None)
-
- self.assertEquals(c('2001-03-02 12:44:01+01:00'),
- datetime(2001, 3, 2, 12, 44, 01, 0, TZStub(1,0)))
- self.assertEquals(c('2001-03-02 12:44:01.037-00:30'),
- datetime(2001, 3, 2, 12, 44, 01, 37000, TZStub(0,-30)))
- self.assertEquals(c('2001-03-02 12:44:01.000001+12:00'),
- datetime(2001, 3, 2, 12, 44, 01, 1, TZStub(12,0)))
- self.assertEquals(c('2001-06-25 12:14:00-07'),
- datetime(2001, 6, 25, 12, 14, 00, 0, TZStub(-7,0)))
-
- def test_conv_interval(self):
- from psycopg2da.adapter import _conv_interval
- from datetime import timedelta
- def c(s):
- return _conv_interval(s, None)
-
- self.assertEquals(c(''), None)
- self.assertEquals(c('01:00'), timedelta(hours=1))
- self.assertEquals(c('00:15'), timedelta(minutes=15))
- self.assertEquals(c('00:00:47'), timedelta(seconds=47))
- self.assertEquals(c('00:00:00.037'), timedelta(microseconds=37000))
- self.assertEquals(c('00:00:00.111111'), timedelta(microseconds=111111))
- self.assertEquals(c('1 day'), timedelta(days=1))
- self.assertEquals(c('2 days'), timedelta(days=2))
- self.assertEquals(c('374 days'), timedelta(days=374))
- self.assertEquals(c('2 days 03:20:15.123456'),
- timedelta(days=2, hours=3, minutes=20,
- seconds=15, microseconds=123456))
- # XXX There's a problem with years and months. Currently timedelta
- # cannot represent them accurately
- self.assertEquals(c('1 month'), '1 month')
- self.assertEquals(c('2 months'), '2 months')
- self.assertEquals(c('1 mon'), '1 mon')
- self.assertEquals(c('2 mons'), '2 mons')
- self.assertEquals(c('1 year'), '1 year')
- self.assertEquals(c('3 years'), '3 years')
- # Later we might be able to use
- ## self.assertEquals(c('1 month'), timedelta(months=1))
- ## self.assertEquals(c('2 months'), timedelta(months=2))
- ## self.assertEquals(c('1 year'), timedelta(years=1))
- ## self.assertEquals(c('3 years'), timedelta(years=3))
-
- self.assertRaises(ValueError, c, '2 day')
- self.assertRaises(ValueError, c, '2days')
- self.assertRaises(ValueError, c, '123')
-
- def test_conv_string(self):
- from psycopg2da.adapter import _get_string_conv
- _conv_string = _get_string_conv("utf-8")
- def c(s):
- return _conv_string(s, None)
- self.assertEquals(c(None), None)
- self.assertEquals(c(''), u'')
- self.assertEquals(c('c'), u'c')
- self.assertEquals(c('\xc3\x82\xc2\xa2'), u'\xc2\xa2')
- self.assertEquals(c('c\xc3\x82\xc2\xa2'), u'c\xc2\xa2')
-
-class TestPsycopg2Adapter(TestCase):
-
- def setUp(self):
- import psycopg2da.adapter as adapter
- self.real_psycopg2 = adapter.psycopg2
- adapter.psycopg2 = self.psycopg2_stub = Psycopg2Stub()
-
- def tearDown(self):
- import psycopg2da.adapter as adapter
- adapter.psycopg2 = self.real_psycopg2
-
- def test_connection_factory(self):
- from psycopg2da.adapter import Psycopg2Adapter
- a = Psycopg2Adapter('dbi://username:password@hostname:port/dbname;junk=ignored')
- c = a._connection_factory()
- args = self.psycopg2_stub.last_connection_string.split()
- args.sort()
- self.assertEquals(args, ['dbname=dbname', 'host=hostname',
- 'password=password', 'port=port',
- 'user=username'])
-
- def test_registerTypes(self):
- import psycopg2da.adapter as adapter
- from psycopg2da.adapter import Psycopg2Adapter
- a = Psycopg2Adapter('dbi://')
- a.registerTypes()
- for typename in ('DATE', 'TIME', 'TIMETZ', 'TIMESTAMP',
- 'TIMESTAMPTZ', 'INTERVAL'):
- typeid = getattr(adapter, '%s_OID' % typename)
- result = self.psycopg2_stub.types.get(typeid, None)
- if not result:
- # comparing None with psycopg2.type object segfaults
- self.fail("did not register %s (%d): got None, not Z%s"
- % (typename, typeid, typename))
- else:
- result_name = getattr(result, 'name', 'None')
- self.assertEquals(result, getattr(adapter, typename),
- "did not register %s (%d): got %s, not Z%s"
- % (typename, typeid, result_name, typename))
-
-
-class TestISODateTime(TestCase):
-
- # Test if date/time parsing functions accept a sensible subset of ISO-8601
- # compliant date/time strings.
- #
- # Resources:
- # http://www.cl.cam.ac.uk/~mgk25/iso-time.html
- # http://www.mcs.vuw.ac.nz/technical/software/SGML/doc/iso8601/ISO8601.html
- # http://www.w3.org/TR/NOTE-datetime
- # http://www.ietf.org/rfc/rfc3339.txt
-
- basic_dates = (('20020304', (2002, 03, 04)),
- ('20000229', (2000, 02, 29)))
-
- extended_dates = (('2002-03-04', (2002, 03, 04)),
- ('2000-02-29', (2000, 02, 29)))
-
- basic_times = (('12', (12, 0, 0)),
- ('1234', (12, 34, 0)),
- ('123417', (12, 34, 17)),
- ('123417.5', (12, 34, 17.5)),
- ('123417,5', (12, 34, 17.5)))
-
- extended_times = (('12', (12, 0, 0)),
- ('12:34', (12, 34, 0)),
- ('12:34:17', (12, 34, 17)),
- ('12:34:17.5', (12, 34, 17.5)),
- ('12:34:17,5', (12, 34, 17.5)))
-
- basic_tzs = (('Z', 0),
- ('+02', 2*60),
- ('+1130', 11*60+30),
- ('-05', -5*60),
- ('-0030', -30))
-
- extended_tzs = (('Z', 0),
- ('+02', 2*60),
- ('+11:30', 11*60+30),
- ('-05', -5*60),
- ('-00:30', -30))
-
- time_separators = (' ', 'T')
-
- bad_dates = ('', 'foo', 'XXXXXXXX', 'XXXX-XX-XX', '2001-2-29',
- '1990/13/14')
-
- bad_times = ('', 'foo', 'XXXXXX', '12:34,5', '12:34:56,')
-
- bad_timetzs = ('12+12 34', '15:45 +1234', '18:00-12:34:56', '18:00+123', '18:00Q')
-
- bad_datetimes = ('', 'foo', '2002-03-0412:33')
-
- bad_datetimetzs = ('', 'foo', '2002-03-04T12:33 +1200')
-
- exception_type = ValueError
-
- # We need the following funcions:
- # parse_date -> (year, month, day)
- # parse_time -> (hour, minute, second)
- # parse_timetz -> (hour, minute, second, tzoffset)
- # parse_datetime -> (year, month, day, hour, minute, second)
- # parse_datetimetz -> (year, month, day, hour, minute, second, tzoffset)
- # second can be a float, all other values are ints
- # tzoffset is offset in minutes east of UTC
-
- def setUp(self):
- from psycopg2da.adapter import parse_date, parse_time, \
- parse_timetz, parse_datetime, parse_datetimetz
- self.parse_date = parse_date
- self.parse_time = parse_time
- self.parse_timetz = parse_timetz
- self.parse_datetime = parse_datetime
- self.parse_datetimetz = parse_datetimetz
-
- def test_basic_date(self):
- for s, d in self.basic_dates:
- self.assertEqual(self.parse_date(s), d)
-
- def test_extended_date(self):
- for s, d in self.extended_dates:
- self.assertEqual(self.parse_date(s), d)
-
- def test_bad_date(self):
- for s in self.bad_dates:
- self.assertRaises(self.exception_type, self.parse_date, s)
-
- def test_basic_time(self):
- for s, t in self.basic_times:
- self.assertEqual(self.parse_time(s), t)
-
- def test_extended_time(self):
- for s, t in self.extended_times:
- self.assertEqual(self.parse_time(s), t)
-
- def test_bad_time(self):
- for s in self.bad_times:
- self.assertRaises(self.exception_type, self.parse_time, s)
-
- def test_basic_timetz(self):
- for s, t in self.basic_times:
- for tz, off in self.basic_tzs:
- self.assertEqual(self.parse_timetz(s+tz), t + (off,))
-
- def test_extended_timetz(self):
- for s, t in self.extended_times:
- for tz, off in self.extended_tzs:
- self.assertEqual(self.parse_timetz(s+tz), t + (off,))
-
- def test_bad_timetzs(self):
- for s in self.bad_timetzs:
- self.assertRaises(self.exception_type, self.parse_timetz, s)
-
- def test_basic_datetime(self):
- for ds, d in self.basic_dates:
- for ts, t in self.basic_times:
- for sep in self.time_separators:
- self.assertEqual(self.parse_datetime(ds+sep+ts), d + t)
-
- def test_extended_datetime(self):
- for ds, d in self.extended_dates:
- for ts, t in self.extended_times:
- for sep in self.time_separators:
- self.assertEqual(self.parse_datetime(ds+sep+ts), d + t)
-
- def test_bad_datetimes(self):
- for s in self.bad_datetimes:
- self.assertRaises(self.exception_type, self.parse_datetime, s)
-
- def test_basic_datetimetz(self):
- for ds, d in self.basic_dates:
- for ts, t in self.basic_times:
- for tz, off in self.basic_tzs:
- for sep in self.time_separators:
- self.assertEqual(self.parse_datetimetz(ds+sep+ts+tz),
- d + t + (off,))
-
- def test_extended_datetimetz(self):
- for ds, d in self.extended_dates:
- for ts, t in self.extended_times:
- for tz, off in self.extended_tzs:
- for sep in self.time_separators:
- self.assertEqual(self.parse_datetimetz(ds+sep+ts+tz),
- d + t + (off,))
-
- def test_bad_datetimetzs(self):
- for s in self.bad_datetimetzs:
- self.assertRaises(self.exception_type, self.parse_datetimetz, s)
-
-
-def test_suite():
- return TestSuite((
- makeSuite(TestPsycopg2TypeConversion),
- makeSuite(TestPsycopg2Adapter),
- makeSuite(TestISODateTime),
- ))
-
-if __name__=='__main__':
- main(defaultTest='test_suite')
diff --git a/scripts/make_errorcodes.py b/scripts/make_errorcodes.py
index 01fbf90..fa37fd8 100755
--- a/scripts/make_errorcodes.py
+++ b/scripts/make_errorcodes.py
@@ -30,7 +30,8 @@ def main():
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'])
+ classes, errors = fetch_errors(
+ ['8.1', '8.2', '8.3', '8.4', '9.0', '9.1', '9.2'])
f = open(filename, "w")
for line in file_start:
diff --git a/setup.cfg b/setup.cfg
index db4e771..637ed41 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -7,6 +7,12 @@ define=PSYCOPG_EXTENSIONS,PSYCOPG_NEW_BOOLEAN,HAVE_PQFREEMEM
# PSYCOPG_DEBUG can be added to enable verbose debug information
# PSYCOPG_NEW_BOOLEAN to format booleans as true/false vs 't'/'f'
+# "pg_config" is required to locate PostgreSQL headers and libraries needed to
+# build psycopg2. If pg_config is not in the path or is installed under a
+# different name uncomment the following option and set it to the pg_config
+# full path.
+#pg_config=
+
# Set to 1 to use Python datetime objects for default date/time representation.
use_pydatetime=1
@@ -22,26 +28,5 @@ have_ssl=0
# Statically link against the postgresql client library.
#static_libpq=1
-# "pg_config" is the preferred method to locate PostgreSQL headers and
-# libraries needed to build psycopg2. If pg_config is not in the path or
-# is installed under a different name uncomment the following option and
-# set it to the pg_config full path.
-#pg_config=
-
-# 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
-
-# Uncomment next line on SUSE 9.3 (and comment previous ones):
-#include_dirs=/usr/include/pgsql:/usr/include/pgsql/server
-
-# If postgresql is installed somewhere weird (i.e., not in your runtime library
-# path like /usr/lib), just add the right path in "library_dirs" and any extra
-# libraries required to link in "libraries".
-#library_dirs=
+# Add here eventual extra libreries required to link the module.
#libraries=
diff --git a/setup.py b/setup.py
index 9ff7736..9ef421c 100644
--- a/setup.py
+++ b/setup.py
@@ -55,14 +55,18 @@ 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
+ from distutils.command.build_py import build_py_2to3
except ImportError:
from distutils.command.build_py import build_py
else:
+ class build_py(build_py_2to3):
+ # workaround subclass for ticket #153
+ pass
+
# 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')
+ build_py.fixer_names = get_fixers_from_package('lib2to3.fixes') \
+ + [ 'fix_b' ]
sys.path.insert(0, 'scripts')
try:
@@ -73,7 +77,7 @@ except ImportError:
# Take a look at http://www.python.org/dev/peps/pep-0386/
# for a consistent versioning pattern.
-PSYCOPG_VERSION = '2.4.6'
+PSYCOPG_VERSION = '2.5'
version_flags = ['dt', 'dec']
@@ -347,14 +351,15 @@ class psycopg_build_ext(build_ext):
self.libraries.append('ssl')
self.libraries.append('crypto')
- def finalize_linux2(self):
+ def finalize_linux(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
+ finalize_linux2 = finalize_linux
+ finalize_linux3 = finalize_linux
def finalize_options(self):
"""Complete the build system configuation."""
@@ -423,6 +428,7 @@ sources = [
'connection_int.c', 'connection_type.c',
'cursor_int.c', 'cursor_type.c',
+ 'diagnostics_type.c', 'error_type.c',
'lobject_int.c', 'lobject_type.c',
'notify_type.c', 'xid_type.c',
@@ -435,8 +441,8 @@ sources = [
depends = [
# headers
- 'config.h', 'pgtypes.h', 'psycopg.h', 'python.h',
- 'connection.h', 'cursor.h', 'green.h', 'lobject.h',
+ 'config.h', 'pgtypes.h', 'psycopg.h', 'python.h', 'connection.h',
+ 'cursor.h', 'diagnostics.h', 'error.h', 'green.h', 'lobject.h',
'notify.h', 'pqpath.h', 'xid.h',
'adapter_asis.h', 'adapter_binary.h', 'adapter_datetime.h',
diff --git a/tests/__init__.py b/tests/__init__.py
index df8e8cd..3e677d8 100755
--- a/tests/__init__.py
+++ b/tests/__init__.py
@@ -45,6 +45,11 @@ import test_transaction
import test_types_basic
import test_types_extras
+if sys.version_info[:2] >= (2, 5):
+ import test_with
+else:
+ test_with = None
+
def test_suite():
# If connection to test db fails, bail out early.
import psycopg2
@@ -76,6 +81,8 @@ def test_suite():
suite.addTest(test_transaction.test_suite())
suite.addTest(test_types_basic.test_suite())
suite.addTest(test_types_extras.test_suite())
+ if test_with:
+ suite.addTest(test_with.test_suite())
return suite
if __name__ == '__main__':
diff --git a/tests/test_async.py b/tests/test_async.py
index 08113c4..09604b5 100755
--- a/tests/test_async.py
+++ b/tests/test_async.py
@@ -32,7 +32,7 @@ import time
import select
import StringIO
-from testconfig import dsn
+from testutils import ConnectingTestCase
class PollableStub(object):
"""A 'pollable' wrapper allowing analysis of the `poll()` calls."""
@@ -49,11 +49,13 @@ class PollableStub(object):
return rv
-class AsyncTests(unittest.TestCase):
+class AsyncTests(ConnectingTestCase):
def setUp(self):
- self.sync_conn = psycopg2.connect(dsn)
- self.conn = psycopg2.connect(dsn, async=True)
+ ConnectingTestCase.setUp(self)
+
+ self.sync_conn = self.conn
+ self.conn = self.connect(async=True)
self.wait(self.conn)
@@ -64,10 +66,6 @@ class AsyncTests(unittest.TestCase):
)''')
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'):
@@ -328,7 +326,7 @@ class AsyncTests(unittest.TestCase):
def __init__(self, dsn, async=0):
psycopg2.extensions.connection.__init__(self, dsn, async=async)
- conn = psycopg2.connect(dsn, connection_factory=MyConn, async=True)
+ conn = self.connect(connection_factory=MyConn, async=True)
self.assert_(isinstance(conn, MyConn))
self.assert_(conn.async)
conn.close()
@@ -429,6 +427,9 @@ class AsyncTests(unittest.TestCase):
def test_notices(self):
del self.conn.notices[:]
cur = self.conn.cursor()
+ if self.conn.server_version >= 90300:
+ cur.execute("set client_min_messages=debug1")
+ self.wait(cur)
cur.execute("create temp table chatty (id serial primary key);")
self.wait(cur)
self.assertEqual("CREATE TABLE", cur.statusmessage)
diff --git a/tests/test_bug_gc.py b/tests/test_bug_gc.py
index 798b7e7..1551dc4 100755
--- a/tests/test_bug_gc.py
+++ b/tests/test_bug_gc.py
@@ -24,21 +24,12 @@
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()
+from testutils import ConnectingTestCase, skip_if_no_uuid
+class StolenReferenceTestCase(ConnectingTestCase):
@skip_if_no_uuid
def test_stolen_reference_bug(self):
def fish(val, cur):
diff --git a/tests/test_cancel.py b/tests/test_cancel.py
index 6d58ddc..0ffa742 100755
--- a/tests/test_cancel.py
+++ b/tests/test_cancel.py
@@ -23,7 +23,6 @@
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
# License for more details.
-import time
import threading
import psycopg2
@@ -31,12 +30,13 @@ import psycopg2.extensions
from psycopg2 import extras
from testconfig import dsn
-from testutils import unittest, skip_before_postgres
+from testutils import unittest, ConnectingTestCase, skip_before_postgres
-class CancelTests(unittest.TestCase):
+class CancelTests(ConnectingTestCase):
def setUp(self):
- self.conn = psycopg2.connect(dsn)
+ ConnectingTestCase.setUp(self)
+
cur = self.conn.cursor()
cur.execute('''
CREATE TEMPORARY TABLE table1 (
@@ -44,9 +44,6 @@ class CancelTests(unittest.TestCase):
)''')
self.conn.commit()
- def tearDown(self):
- self.conn.close()
-
def test_empty_cancel(self):
self.conn.cancel()
diff --git a/tests/test_connection.py b/tests/test_connection.py
index e4b7d83..26ad932 100755
--- a/tests/test_connection.py
+++ b/tests/test_connection.py
@@ -25,23 +25,19 @@
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.errorcodes
import psycopg2.extensions
-from testconfig import dsn, dbname
-
-class ConnectionTests(unittest.TestCase):
- def setUp(self):
- self.conn = psycopg2.connect(dsn)
+from testutils import unittest, decorate_all_tests, skip_if_no_superuser
+from testutils import skip_before_postgres, skip_after_postgres
+from testutils import ConnectingTestCase, skip_if_tpc_disabled
+from testconfig import dsn, dbname
- def tearDown(self):
- if not self.conn.closed:
- self.conn.close()
+class ConnectionTests(ConnectingTestCase):
def test_closed_attribute(self):
conn = self.conn
self.assertEqual(conn.closed, False)
@@ -66,6 +62,27 @@ class ConnectionTests(unittest.TestCase):
conn.close()
self.assertEqual(curs.closed, True)
+ @skip_before_postgres(8, 4)
+ @skip_if_no_superuser
+ def test_cleanup_on_badconn_close(self):
+ # ticket #148
+ conn = self.conn
+ cur = conn.cursor()
+ try:
+ cur.execute("select pg_terminate_backend(pg_backend_pid())")
+ except psycopg2.OperationalError, e:
+ if e.pgcode != psycopg2.errorcodes.ADMIN_SHUTDOWN:
+ raise
+ except psycopg2.DatabaseError, e:
+ # curiously when disconnected in green mode we get a DatabaseError
+ # without pgcode.
+ if e.pgcode is not None:
+ raise
+
+ self.assertEqual(conn.closed, 2)
+ conn.close()
+ self.assertEqual(conn.closed, 1)
+
def test_reset(self):
conn = self.conn
# switch isolation level, then reset
@@ -79,6 +96,8 @@ class ConnectionTests(unittest.TestCase):
def test_notices(self):
conn = self.conn
cur = conn.cursor()
+ if self.conn.server_version >= 90300:
+ cur.execute("set client_min_messages=debug1")
cur.execute("create temp table chatty (id serial primary key);")
self.assertEqual("CREATE TABLE", cur.statusmessage)
self.assert_(conn.notices)
@@ -86,6 +105,8 @@ class ConnectionTests(unittest.TestCase):
def test_notices_consistent_order(self):
conn = self.conn
cur = conn.cursor()
+ if self.conn.server_version >= 90300:
+ cur.execute("set client_min_messages=debug1")
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))
@@ -97,6 +118,8 @@ class ConnectionTests(unittest.TestCase):
def test_notices_limited(self):
conn = self.conn
cur = conn.cursor()
+ if self.conn.server_version >= 90300:
+ cur.execute("set client_min_messages=debug1")
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)
@@ -125,7 +148,7 @@ class ConnectionTests(unittest.TestCase):
@skip_before_postgres(8, 2)
def test_concurrent_execution(self):
def slave():
- cnn = psycopg2.connect(dsn)
+ cnn = self.connect()
cur = cnn.cursor()
cur.execute("select pg_sleep(4)")
cur.close()
@@ -155,7 +178,7 @@ class ConnectionTests(unittest.TestCase):
oldenc = os.environ.get('PGCLIENTENCODING')
os.environ['PGCLIENTENCODING'] = 'utf-8' # malformed spelling
try:
- self.conn = psycopg2.connect(dsn)
+ self.conn = self.connect()
finally:
if oldenc is not None:
os.environ['PGCLIENTENCODING'] = oldenc
@@ -201,11 +224,37 @@ class ConnectionTests(unittest.TestCase):
self.assert_(not notices, "%d notices raised" % len(notices))
+ def test_connect_cursor_factory(self):
+ import psycopg2.extras
+ conn = self.connect(cursor_factory=psycopg2.extras.DictCursor)
+ cur = conn.cursor()
+ cur.execute("select 1 as a")
+ self.assertEqual(cur.fetchone()['a'], 1)
+
+ def test_cursor_factory(self):
+ self.assertEqual(self.conn.cursor_factory, None)
+ cur = self.conn.cursor()
+ cur.execute("select 1 as a")
+ self.assertRaises(TypeError, (lambda r: r['a']), cur.fetchone())
+
+ self.conn.cursor_factory = psycopg2.extras.DictCursor
+ self.assertEqual(self.conn.cursor_factory, psycopg2.extras.DictCursor)
+ cur = self.conn.cursor()
+ cur.execute("select 1 as a")
+ self.assertEqual(cur.fetchone()['a'], 1)
+
+ self.conn.cursor_factory = None
+ self.assertEqual(self.conn.cursor_factory, None)
+ cur = self.conn.cursor()
+ cur.execute("select 1 as a")
+ self.assertRaises(TypeError, (lambda r: r['a']), cur.fetchone())
+
-class IsolationLevelsTestCase(unittest.TestCase):
+class IsolationLevelsTestCase(ConnectingTestCase):
def setUp(self):
- self._conns = []
+ ConnectingTestCase.setUp(self)
+
conn = self.connect()
cur = conn.cursor()
try:
@@ -216,17 +265,6 @@ class IsolationLevelsTestCase(unittest.TestCase):
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(
@@ -392,20 +430,16 @@ class IsolationLevelsTestCase(unittest.TestCase):
cnn.set_isolation_level, 1)
-class ConnectionTwoPhaseTests(unittest.TestCase):
+class ConnectionTwoPhaseTests(ConnectingTestCase):
def setUp(self):
- self._conns = []
+ ConnectingTestCase.setUp(self)
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()
+ ConnectingTestCase.tearDown(self)
def clear_test_xacts(self):
"""Rollback all the prepared transaction in the testing db."""
@@ -458,11 +492,6 @@ class ConnectionTwoPhaseTests(unittest.TestCase):
cnn.close()
return rv
- def connect(self, **kwargs):
- conn = psycopg2.connect(dsn, **kwargs)
- self._conns.append(conn)
- return conn
-
def test_tpc_commit(self):
cnn = self.connect()
xid = cnn.xid(1, "gtrid", "bqual")
@@ -774,18 +803,10 @@ class ConnectionTwoPhaseTests(unittest.TestCase):
self.assertEqual(None, xid.bqual)
-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()
-
+class TransactionControlTests(ConnectingTestCase):
def test_closed(self):
self.conn.close()
self.assertRaises(psycopg2.InterfaceError,
@@ -927,14 +948,7 @@ class TransactionControlTests(unittest.TestCase):
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()
-
+class AutocommitTests(ConnectingTestCase):
def test_closed(self):
self.conn.close()
self.assertRaises(psycopg2.InterfaceError,
diff --git a/tests/test_copy.py b/tests/test_copy.py
index 4ed253e..fd6c14a 100755
--- a/tests/test_copy.py
+++ b/tests/test_copy.py
@@ -22,26 +22,16 @@
# 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 testutils import unittest, ConnectingTestCase, decorate_all_tests
+from testutils import 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_
-
+from testutils import skip_copy_if_green
if sys.version_info[0] < 3:
_base = object
@@ -68,10 +58,10 @@ class MinimalWrite(_base):
return self.f.write(data)
-class CopyTests(unittest.TestCase):
+class CopyTests(ConnectingTestCase):
def setUp(self):
- self.conn = psycopg2.connect(dsn)
+ ConnectingTestCase.setUp(self)
self._create_temp_table()
def _create_temp_table(self):
@@ -82,9 +72,6 @@ class CopyTests(unittest.TestCase):
data text
)''')
- def tearDown(self):
- self.conn.close()
-
def test_copy_from(self):
curs = self.conn.cursor()
try:
@@ -272,7 +259,7 @@ class CopyTests(unittest.TestCase):
self.assertEqual(curs.fetchone()[0], 2)
-decorate_all_tests(CopyTests, skip_if_green)
+decorate_all_tests(CopyTests, skip_copy_if_green)
def test_suite():
diff --git a/tests/test_cursor.py b/tests/test_cursor.py
index b1c5c5c..893f397 100755
--- a/tests/test_cursor.py
+++ b/tests/test_cursor.py
@@ -26,16 +26,10 @@ 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
+from testutils import unittest, ConnectingTestCase, skip_before_postgres
+from testutils import skip_if_no_namedtuple
-class CursorTests(unittest.TestCase):
-
- def setUp(self):
- self.conn = psycopg2.connect(dsn)
-
- def tearDown(self):
- self.conn.close()
+class CursorTests(ConnectingTestCase):
def test_close_idempotent(self):
cur = self.conn.cursor()
@@ -213,6 +207,71 @@ class CursorTests(unittest.TestCase):
curs.execute("drop table withhold")
self.conn.commit()
+ def test_scrollable(self):
+ self.assertRaises(psycopg2.ProgrammingError, self.conn.cursor,
+ scrollable=True)
+
+ curs = self.conn.cursor()
+ curs.execute("create table scrollable (data int)")
+ curs.executemany("insert into scrollable values (%s)",
+ [ (i,) for i in range(100) ])
+ curs.close()
+
+ for t in range(2):
+ if not t:
+ curs = self.conn.cursor("S")
+ self.assertEqual(curs.scrollable, None);
+ curs.scrollable = True
+ else:
+ curs = self.conn.cursor("S", scrollable=True)
+
+ self.assertEqual(curs.scrollable, True);
+ curs.itersize = 10
+
+ # complex enough to make postgres cursors declare without
+ # scroll/no scroll to fail
+ curs.execute("""
+ select x.data
+ from scrollable x
+ join scrollable y on x.data = y.data
+ order by y.data""")
+ for i, (n,) in enumerate(curs):
+ self.assertEqual(i, n)
+
+ curs.scroll(-1)
+ for i in range(99, -1, -1):
+ curs.scroll(-1)
+ self.assertEqual(i, curs.fetchone()[0])
+ curs.scroll(-1)
+
+ curs.close()
+
+ def test_not_scrollable(self):
+ self.assertRaises(psycopg2.ProgrammingError, self.conn.cursor,
+ scrollable=False)
+
+ curs = self.conn.cursor()
+ curs.execute("create table scrollable (data int)")
+ curs.executemany("insert into scrollable values (%s)",
+ [ (i,) for i in range(100) ])
+ curs.close()
+
+ curs = self.conn.cursor("S") # default scrollability
+ curs.execute("select * from scrollable")
+ self.assertEqual(curs.scrollable, None)
+ curs.scroll(2)
+ try:
+ curs.scroll(-1)
+ except psycopg2.OperationalError:
+ return self.skipTest("can't evaluate non-scrollable cursor")
+ curs.close()
+
+ curs = self.conn.cursor("S", scrollable=False)
+ self.assertEqual(curs.scrollable, False)
+ curs.execute("select * from scrollable")
+ curs.scroll(2)
+ self.assertRaises(psycopg2.OperationalError, curs.scroll, -1)
+
@skip_before_postgres(8, 2)
def test_iter_named_cursor_efficient(self):
curs = self.conn.cursor('tmp')
diff --git a/tests/test_dates.py b/tests/test_dates.py
index 0d18f66..cc183c5 100755
--- a/tests/test_dates.py
+++ b/tests/test_dates.py
@@ -23,10 +23,9 @@
# License for more details.
import math
-import unittest
import psycopg2
from psycopg2.tz import FixedOffsetTimezone, ZERO
-from testconfig import dsn
+from testutils import unittest, ConnectingTestCase
class CommonDatetimeTestsMixin:
@@ -93,20 +92,17 @@ class CommonDatetimeTestsMixin:
self.assertEqual(value, None)
-class DatetimeTests(unittest.TestCase, CommonDatetimeTestsMixin):
+class DatetimeTests(ConnectingTestCase, CommonDatetimeTestsMixin):
"""Tests for the datetime based date handling in psycopg2."""
def setUp(self):
- self.conn = psycopg2.connect(dsn)
+ ConnectingTestCase.setUp(self)
self.curs = self.conn.cursor()
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()
-
def test_parse_bc_date(self):
# datetime does not support BC dates
self.assertRaises(ValueError, self.DATE, '00042-01-01 BC', self.curs)
@@ -311,11 +307,11 @@ if not hasattr(psycopg2.extensions, 'PYDATETIME'):
del DatetimeTests
-class mxDateTimeTests(unittest.TestCase, CommonDatetimeTestsMixin):
+class mxDateTimeTests(ConnectingTestCase, CommonDatetimeTestsMixin):
"""Tests for the mx.DateTime based date handling in psycopg2."""
def setUp(self):
- self.conn = psycopg2.connect(dsn)
+ ConnectingTestCase.setUp(self)
self.curs = self.conn.cursor()
self.DATE = psycopg2._psycopg.MXDATE
self.TIME = psycopg2._psycopg.MXTIME
@@ -557,6 +553,7 @@ class FixedOffsetTimezoneTests(unittest.TestCase):
self.assertEqual(tz11, tz21)
self.assertEqual(tz12, tz22)
+
def test_suite():
return unittest.TestLoader().loadTestsFromName(__name__)
diff --git a/tests/test_extras_dictcursor.py b/tests/test_extras_dictcursor.py
index 4846dea..f2fefff 100755
--- a/tests/test_extras_dictcursor.py
+++ b/tests/test_extras_dictcursor.py
@@ -18,26 +18,23 @@ 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
+from testutils import unittest, ConnectingTestCase, skip_before_postgres
+from testutils import skip_if_no_namedtuple
-class ExtrasDictCursorTests(unittest.TestCase):
+class ExtrasDictCursorTests(ConnectingTestCase):
"""Test if DictCursor extension class works."""
def setUp(self):
- self.conn = psycopg2.connect(dsn)
+ ConnectingTestCase.setUp(self)
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 testDictConnCursorArgs(self):
self.conn.close()
- self.conn = psycopg2.connect(dsn, connection_factory=psycopg2.extras.DictConnection)
+ self.conn = self.connect(connection_factory=psycopg2.extras.DictConnection)
cur = self.conn.cursor()
self.assert_(isinstance(cur, psycopg2.extras.DictCursor))
self.assertEqual(cur.name, None)
@@ -232,18 +229,17 @@ class ExtrasDictCursorTests(unittest.TestCase):
self.assertEqual(r._column_mapping, r1._column_mapping)
-class NamedTupleCursorTest(unittest.TestCase):
+class NamedTupleCursorTest(ConnectingTestCase):
def setUp(self):
+ ConnectingTestCase.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)
+ self.conn = self.connect(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')")
@@ -251,10 +247,6 @@ class NamedTupleCursorTest(unittest.TestCase):
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_cursor_args(self):
cur = self.conn.cursor('foo', cursor_factory=psycopg2.extras.DictCursor)
@@ -359,9 +351,7 @@ class NamedTupleCursorTest(unittest.TestCase):
# an import error somewhere
from psycopg2.extras import NamedTupleConnection
try:
- if self.conn is not None:
- self.conn.close()
- self.conn = psycopg2.connect(dsn,
+ self.conn = self.connect(
connection_factory=NamedTupleConnection)
curs = self.conn.cursor()
curs.execute("select 1")
@@ -371,8 +361,7 @@ class NamedTupleCursorTest(unittest.TestCase):
else:
self.fail("expecting ImportError")
else:
- # skip the test
- pass
+ return self.skipTest("namedtuple available")
@skip_if_no_namedtuple
def test_record_updated(self):
diff --git a/tests/test_green.py b/tests/test_green.py
index e0cd57d..506b38f 100755
--- a/tests/test_green.py
+++ b/tests/test_green.py
@@ -26,7 +26,8 @@ import unittest
import psycopg2
import psycopg2.extensions
import psycopg2.extras
-from testconfig import dsn
+
+from testutils import ConnectingTestCase
class ConnectionStub(object):
"""A `connection` wrapper allowing analysis of the `poll()` calls."""
@@ -42,14 +43,14 @@ class ConnectionStub(object):
self.polls.append(rv)
return rv
-class GreenTests(unittest.TestCase):
+class GreenTestCase(ConnectingTestCase):
def setUp(self):
self._cb = psycopg2.extensions.get_wait_callback()
psycopg2.extensions.set_wait_callback(psycopg2.extras.wait_select)
- self.conn = psycopg2.connect(dsn)
+ ConnectingTestCase.setUp(self)
def tearDown(self):
- self.conn.close()
+ ConnectingTestCase.tearDown(self)
psycopg2.extensions.set_wait_callback(self._cb)
def set_stub_wait_callback(self, conn):
diff --git a/tests/test_lobject.py b/tests/test_lobject.py
index 9c1c44f..83e9e73 100755
--- a/tests/test_lobject.py
+++ b/tests/test_lobject.py
@@ -25,14 +25,16 @@
import os
import shutil
import tempfile
+from functools import wraps
import psycopg2
import psycopg2.extensions
from psycopg2.extensions import b
-from testconfig import dsn, green
from testutils import unittest, decorate_all_tests, skip_if_tpc_disabled
+from testutils import ConnectingTestCase, skip_if_green
def skip_if_no_lo(f):
+ @wraps(f)
def skip_if_no_lo_(self):
if self.conn.server_version < 80100:
return self.skipTest("large objects only supported from PG 8.1")
@@ -41,20 +43,12 @@ def skip_if_no_lo(f):
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_
+skip_lo_if_green = skip_if_green("libpq doesn't support LO in async mode")
-class LargeObjectMixin(object):
- # doesn't derive from TestCase to avoid repeating tests twice.
+class LargeObjectTestCase(ConnectingTestCase):
def setUp(self):
- self.conn = self.connect()
+ ConnectingTestCase.setUp(self)
self.lo_oid = None
self.tmpdir = None
@@ -73,13 +67,11 @@ class LargeObjectMixin(object):
pass
else:
lo.unlink()
- self.conn.close()
- def connect(self):
- return psycopg2.connect(dsn)
+ ConnectingTestCase.tearDown(self)
-class LargeObjectTests(LargeObjectMixin, unittest.TestCase):
+class LargeObjectTests(LargeObjectTestCase):
def test_create(self):
lo = self.conn.lobject()
self.assertNotEqual(lo, None)
@@ -378,11 +370,11 @@ class LargeObjectTests(LargeObjectMixin, unittest.TestCase):
self.conn.tpc_commit()
-decorate_all_tests(LargeObjectTests, skip_if_no_lo)
-decorate_all_tests(LargeObjectTests, skip_if_green)
+decorate_all_tests(LargeObjectTests, skip_if_no_lo, skip_lo_if_green)
def skip_if_no_truncate(f):
+ @wraps(f)
def skip_if_no_truncate_(self):
if self.conn.server_version < 80300:
return self.skipTest(
@@ -395,10 +387,12 @@ def skip_if_no_truncate(f):
return f(self)
-class LargeObjectTruncateTests(LargeObjectMixin, unittest.TestCase):
+ return skip_if_no_truncate_
+
+class LargeObjectTruncateTests(LargeObjectTestCase):
def test_truncate(self):
lo = self.conn.lobject()
- lo.write(b("some data"))
+ lo.write("some data")
lo.close()
lo = self.conn.lobject(lo.oid, "w")
@@ -407,17 +401,17 @@ class LargeObjectTruncateTests(LargeObjectMixin, unittest.TestCase):
# seek position unchanged
self.assertEqual(lo.tell(), 0)
# data truncated
- self.assertEqual(lo.read(), b("some"))
+ self.assertEqual(lo.read(), "some")
lo.truncate(6)
lo.seek(0)
# large object extended with zeroes
- self.assertEqual(lo.read(), b("some\x00\x00"))
+ self.assertEqual(lo.read(), "some\x00\x00")
lo.truncate()
lo.seek(0)
# large object empty
- self.assertEqual(lo.read(), b(""))
+ self.assertEqual(lo.read(), "")
def test_truncate_after_close(self):
lo = self.conn.lobject()
@@ -431,9 +425,8 @@ class LargeObjectTruncateTests(LargeObjectMixin, unittest.TestCase):
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)
+decorate_all_tests(LargeObjectTruncateTests,
+ skip_if_no_lo, skip_lo_if_green, skip_if_no_truncate)
def test_suite():
diff --git a/tests/test_module.py b/tests/test_module.py
index 4083c36..e6d1a3d 100755
--- a/tests/test_module.py
+++ b/tests/test_module.py
@@ -22,8 +22,8 @@
# 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
+from testutils import unittest, skip_before_python, skip_before_postgres
+from testutils import ConnectingTestCase, skip_copy_if_green
import psycopg2
@@ -136,13 +136,7 @@ class ConnectTestCase(unittest.TestCase):
psycopg2.connect, 'dbname=foo', no_such_param='meh')
-class ExceptionsTestCase(unittest.TestCase):
- def setUp(self):
- self.conn = psycopg2.connect(dsn)
-
- def tearDown(self):
- self.conn.close()
-
+class ExceptionsTestCase(ConnectingTestCase):
def test_attributes(self):
cur = self.conn.cursor()
try:
@@ -154,6 +148,122 @@ class ExceptionsTestCase(unittest.TestCase):
self.assert_(e.pgerror)
self.assert_(e.cursor is cur)
+ def test_diagnostics_attributes(self):
+ cur = self.conn.cursor()
+ try:
+ cur.execute("select * from nonexist")
+ except psycopg2.Error, exc:
+ e = exc
+
+ diag = e.diag
+ self.assert_(isinstance(diag, psycopg2.extensions.Diagnostics))
+ for attr in [
+ 'column_name', 'constraint_name', 'context', 'datatype_name',
+ 'internal_position', 'internal_query', 'message_detail',
+ 'message_hint', 'message_primary', 'schema_name', 'severity',
+ 'source_file', 'source_function', 'source_line', 'sqlstate',
+ 'statement_position', 'table_name', ]:
+ v = getattr(diag, attr)
+ if v is not None:
+ self.assert_(isinstance(v, str))
+
+ def test_diagnostics_values(self):
+ cur = self.conn.cursor()
+ try:
+ cur.execute("select * from nonexist")
+ except psycopg2.Error, exc:
+ e = exc
+
+ self.assertEqual(e.diag.sqlstate, '42P01')
+ self.assertEqual(e.diag.severity, 'ERROR')
+
+ def test_diagnostics_life(self):
+ import gc
+ from weakref import ref
+
+ def tmp():
+ cur = self.conn.cursor()
+ try:
+ cur.execute("select * from nonexist")
+ except psycopg2.Error, exc:
+ return cur, exc
+
+ cur, e = tmp()
+ diag = e.diag
+ w = ref(cur)
+
+ del e, cur
+ gc.collect()
+ assert(w() is not None)
+
+ self.assertEqual(diag.sqlstate, '42P01')
+
+ del diag
+ gc.collect()
+ assert(w() is None)
+
+ @skip_copy_if_green
+ def test_diagnostics_copy(self):
+ from StringIO import StringIO
+ f = StringIO()
+ cur = self.conn.cursor()
+ try:
+ cur.copy_to(f, 'nonexist')
+ except psycopg2.Error, exc:
+ diag = exc.diag
+
+ self.assertEqual(diag.sqlstate, '42P01')
+
+ def test_diagnostics_independent(self):
+ cur = self.conn.cursor()
+ try:
+ cur.execute("l'acqua e' poca e 'a papera nun galleggia")
+ except Exception, exc:
+ diag1 = exc.diag
+
+ self.conn.rollback()
+
+ try:
+ cur.execute("select level from water where ducks > 1")
+ except psycopg2.Error, exc:
+ diag2 = exc.diag
+
+ self.assertEqual(diag1.sqlstate, '42601')
+ self.assertEqual(diag2.sqlstate, '42P01')
+
+ def test_diagnostics_from_commit(self):
+ cur = self.conn.cursor()
+ cur.execute("""
+ create temp table test_deferred (
+ data int primary key,
+ ref int references test_deferred (data)
+ deferrable initially deferred)
+ """)
+ cur.execute("insert into test_deferred values (1,2)")
+ try:
+ self.conn.commit()
+ except psycopg2.Error, exc:
+ e = exc
+ self.assertEqual(e.diag.sqlstate, '23503')
+
+ @skip_before_postgres(9, 3)
+ def test_9_3_diagnostics(self):
+ cur = self.conn.cursor()
+ cur.execute("""
+ create temp table test_exc (
+ data int constraint chk_eq1 check (data = 1)
+ )""")
+ try:
+ cur.execute("insert into test_exc values(2)")
+ except psycopg2.Error, exc:
+ e = exc
+ self.assertEqual(e.pgcode, '23514')
+ self.assertEqual(e.diag.schema_name[:7], "pg_temp")
+ self.assertEqual(e.diag.table_name, "test_exc")
+ self.assertEqual(e.diag.column_name, None)
+ self.assertEqual(e.diag.constraint_name, "chk_eq1")
+ self.assertEqual(e.diag.datatype_name, None)
+
@skip_before_python(2, 5)
def test_pickle(self):
import pickle
diff --git a/tests/test_notify.py b/tests/test_notify.py
index 5b1b112..d048241 100755
--- a/tests/test_notify.py
+++ b/tests/test_notify.py
@@ -26,23 +26,16 @@ from testutils import unittest
import psycopg2
from psycopg2 import extensions
+from testutils import ConnectingTestCase, script_to_py3
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()
+class NotifiesTests(ConnectingTestCase):
def autocommit(self, conn):
"""Set a connection in autocommit mode."""
diff --git a/tests/test_quote.py b/tests/test_quote.py
index 4b44a86..e7b3c31 100755
--- a/tests/test_quote.py
+++ b/tests/test_quote.py
@@ -23,14 +23,13 @@
# License for more details.
import sys
-from testutils import unittest
-from testconfig import dsn
+from testutils import unittest, ConnectingTestCase
import psycopg2
import psycopg2.extensions
from psycopg2.extensions import b
-class QuotingTestCase(unittest.TestCase):
+class QuotingTestCase(ConnectingTestCase):
r"""Checks the correct quoting of strings and binary objects.
Since ver. 8.1, PostgreSQL is moving towards SQL standard conforming
@@ -48,12 +47,6 @@ class QuotingTestCase(unittest.TestCase):
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(dsn)
-
- def tearDown(self):
- self.conn.close()
-
def test_string(self):
data = """some data with \t chars
to escape into, 'quotes' and \\ a backslash too.
@@ -162,6 +155,16 @@ class QuotingTestCase(unittest.TestCase):
self.assert_(not self.conn.notices)
+class TestQuotedString(ConnectingTestCase):
+ def test_encoding(self):
+ q = psycopg2.extensions.QuotedString('hi')
+ self.assertEqual(q.encoding, 'latin1')
+
+ self.conn.set_client_encoding('utf_8')
+ q.prepare(self.conn)
+ self.assertEqual(q.encoding, 'utf_8')
+
+
def test_suite():
return unittest.TestLoader().loadTestsFromName(__name__)
diff --git a/tests/test_transaction.py b/tests/test_transaction.py
index 90e159a..724d0d8 100755
--- a/tests/test_transaction.py
+++ b/tests/test_transaction.py
@@ -23,17 +23,16 @@
# License for more details.
import threading
-from testutils import unittest, skip_before_postgres
+from testutils import unittest, ConnectingTestCase, skip_before_postgres
import psycopg2
from psycopg2.extensions import (
ISOLATION_LEVEL_SERIALIZABLE, STATUS_BEGIN, STATUS_READY)
-from testconfig import dsn
-class TransactionTests(unittest.TestCase):
+class TransactionTests(ConnectingTestCase):
def setUp(self):
- self.conn = psycopg2.connect(dsn)
+ ConnectingTestCase.setUp(self)
self.conn.set_isolation_level(ISOLATION_LEVEL_SERIALIZABLE)
curs = self.conn.cursor()
curs.execute('''
@@ -51,9 +50,6 @@ class TransactionTests(unittest.TestCase):
curs.execute('INSERT INTO table2 VALUES (1, 1)')
self.conn.commit()
- def tearDown(self):
- self.conn.close()
-
def test_rollback(self):
# Test that rollback undoes changes
curs = self.conn.cursor()
@@ -93,16 +89,17 @@ class TransactionTests(unittest.TestCase):
self.assertEqual(curs.fetchone()[0], 1)
-class DeadlockSerializationTests(unittest.TestCase):
+class DeadlockSerializationTests(ConnectingTestCase):
"""Test deadlock and serialization failure errors."""
def connect(self):
- conn = psycopg2.connect(dsn)
+ conn = ConnectingTestCase.connect(self)
conn.set_isolation_level(ISOLATION_LEVEL_SERIALIZABLE)
return conn
def setUp(self):
- self.conn = self.connect()
+ ConnectingTestCase.setUp(self)
+
curs = self.conn.cursor()
# Drop table if it already exists
try:
@@ -130,7 +127,8 @@ class DeadlockSerializationTests(unittest.TestCase):
curs.execute("DROP TABLE table1")
curs.execute("DROP TABLE table2")
self.conn.commit()
- self.conn.close()
+
+ ConnectingTestCase.tearDown(self)
def test_deadlock(self):
self.thread1_error = self.thread2_error = None
@@ -226,16 +224,13 @@ class DeadlockSerializationTests(unittest.TestCase):
error, psycopg2.extensions.TransactionRollbackError))
-class QueryCancellationTests(unittest.TestCase):
+class QueryCancellationTests(ConnectingTestCase):
"""Tests for query cancellation."""
def setUp(self):
- self.conn = psycopg2.connect(dsn)
+ ConnectingTestCase.setUp(self)
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()
diff --git a/tests/test_types_basic.py b/tests/test_types_basic.py
index e442fc6..6c4cc97 100755
--- a/tests/test_types_basic.py
+++ b/tests/test_types_basic.py
@@ -22,28 +22,20 @@
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
# License for more details.
-try:
- import decimal
-except:
- pass
+import decimal
+
import sys
+from functools import wraps
import testutils
-from testutils import unittest, decorate_all_tests
-from testconfig import dsn
+from testutils import unittest, ConnectingTestCase, decorate_all_tests
import psycopg2
from psycopg2.extensions import b
-class TypesBasicTests(unittest.TestCase):
+class TypesBasicTests(ConnectingTestCase):
"""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)
@@ -64,10 +56,6 @@ class TypesBasicTests(unittest.TestCase):
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,))
@@ -76,21 +64,18 @@ class TypesBasicTests(unittest.TestCase):
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")
+ 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))
def testFloatNan(self):
try:
@@ -126,7 +111,7 @@ class TypesBasicTests(unittest.TestCase):
s = bytes(range(256))
b = psycopg2.Binary(s)
buf = self.execute("SELECT %s::bytea AS foo", (b,))
- self.assertEqual(s, buf)
+ self.assertEqual(s, buf.tobytes())
def testBinaryNone(self):
b = psycopg2.Binary(None)
@@ -154,7 +139,7 @@ class TypesBasicTests(unittest.TestCase):
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)
+ self.assertEqual(s, buf2.tobytes())
def testArray(self):
s = self.execute("SELECT %s AS foo", ([[1,2],[3,4]],))
@@ -461,6 +446,7 @@ class ByteaParserTest(unittest.TestCase):
self.assertEqual(rv, tgt)
def skip_if_cant_cast(f):
+ @wraps(f)
def skip_if_cant_cast_(self, *args, **kwargs):
if self._cast is None:
return self.skipTest("can't test bytea parser: %s - %s"
diff --git a/tests/test_types_extras.py b/tests/test_types_extras.py
index c4483f8..98fbbad 100755
--- a/tests/test_types_extras.py
+++ b/tests/test_types_extras.py
@@ -14,22 +14,19 @@
# 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 decimal import Decimal
+from datetime import date, datetime
+from functools import wraps
from testutils import unittest, skip_if_no_uuid, skip_before_postgres
+from testutils import ConnectingTestCase, decorate_all_tests
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':
@@ -37,15 +34,9 @@ def filter_scs(conn, s):
else:
return s.replace(b("E'"), b("'"))
-class TypesExtrasTests(unittest.TestCase):
+class TypesExtrasTests(ConnectingTestCase):
"""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)
@@ -126,6 +117,7 @@ class TypesExtrasTests(unittest.TestCase):
def skip_if_no_hstore(f):
+ @wraps(f)
def skip_if_no_hstore_(self):
from psycopg2.extras import HstoreAdapter
oids = HstoreAdapter.get_oids(self.conn)
@@ -135,13 +127,7 @@ def skip_if_no_hstore(f):
return skip_if_no_hstore_
-class HstoreTestCase(unittest.TestCase):
- def setUp(self):
- self.conn = psycopg2.connect(dsn)
-
- def tearDown(self):
- self.conn.close()
-
+class HstoreTestCase(ConnectingTestCase):
def test_adapt_8(self):
if self.conn.server_version >= 90000:
return self.skipTest("skipping dict adaptation with PG pre-9 syntax")
@@ -276,7 +262,7 @@ class HstoreTestCase(unittest.TestCase):
oids = HstoreAdapter.get_oids(self.conn)
try:
register_hstore(self.conn, globally=True)
- conn2 = psycopg2.connect(dsn)
+ conn2 = self.connect()
try:
cur2 = self.conn.cursor()
cur2.execute("select 'a => b'::hstore")
@@ -429,7 +415,7 @@ class HstoreTestCase(unittest.TestCase):
from psycopg2.extras import RealDictConnection
from psycopg2.extras import register_hstore
- conn = psycopg2.connect(dsn, connection_factory=RealDictConnection)
+ conn = self.connect(connection_factory=RealDictConnection)
try:
register_hstore(conn)
curs = conn.cursor()
@@ -438,7 +424,7 @@ class HstoreTestCase(unittest.TestCase):
finally:
conn.close()
- conn = psycopg2.connect(dsn, connection_factory=RealDictConnection)
+ conn = self.connect(connection_factory=RealDictConnection)
try:
curs = conn.cursor()
register_hstore(curs)
@@ -449,6 +435,7 @@ class HstoreTestCase(unittest.TestCase):
def skip_if_no_composite(f):
+ @wraps(f)
def skip_if_no_composite_(self):
if self.conn.server_version < 80000:
return self.skipTest(
@@ -457,16 +444,9 @@ def skip_if_no_composite(f):
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()
-
+class AdaptTypeTestCase(ConnectingTestCase):
@skip_if_no_composite
def test_none_in_record(self):
curs = self.conn.cursor()
@@ -533,6 +513,7 @@ class AdaptTypeTestCase(unittest.TestCase):
t = psycopg2.extras.register_composite("type_isd", self.conn)
self.assertEqual(t.name, 'type_isd')
+ self.assertEqual(t.schema, 'public')
self.assertEqual(t.oid, oid)
self.assert_(issubclass(t.type, tuple))
self.assertEqual(t.attnames, ['anint', 'astring', 'adate'])
@@ -620,8 +601,8 @@ class AdaptTypeTestCase(unittest.TestCase):
def test_register_on_connection(self):
self._create_type("type_ii", [("a", "integer"), ("b", "integer")])
- conn1 = psycopg2.connect(dsn)
- conn2 = psycopg2.connect(dsn)
+ conn1 = self.connect()
+ conn2 = self.connect()
try:
psycopg2.extras.register_composite("type_ii", conn1)
curs1 = conn1.cursor()
@@ -638,8 +619,8 @@ class AdaptTypeTestCase(unittest.TestCase):
def test_register_globally(self):
self._create_type("type_ii", [("a", "integer"), ("b", "integer")])
- conn1 = psycopg2.connect(dsn)
- conn2 = psycopg2.connect(dsn)
+ conn1 = self.connect()
+ conn2 = self.connect()
try:
t = psycopg2.extras.register_composite("type_ii", conn1, globally=True)
try:
@@ -676,6 +657,7 @@ class AdaptTypeTestCase(unittest.TestCase):
[("a", "integer"), ("b", "integer")])
t = psycopg2.extras.register_composite(
"typens.typens_ii", self.conn)
+ self.assertEqual(t.schema, 'typens')
curs.execute("select (4,8)::typens.typens_ii")
self.assertEqual(curs.fetchone()[0], (4,8))
@@ -757,13 +739,13 @@ class AdaptTypeTestCase(unittest.TestCase):
self.assertEqual(r[0], (2, 'test2'))
self.assertEqual(r[1], [(3, 'testc', 2), (4, 'testd', 2)])
- @skip_if_no_hstore
+ @skip_if_no_composite
def test_non_dbapi_connection(self):
from psycopg2.extras import RealDictConnection
from psycopg2.extras import register_composite
self._create_type("type_ii", [("a", "integer"), ("b", "integer")])
- conn = psycopg2.connect(dsn, connection_factory=RealDictConnection)
+ conn = self.connect(connection_factory=RealDictConnection)
try:
register_composite('type_ii', conn)
curs = conn.cursor()
@@ -772,7 +754,7 @@ class AdaptTypeTestCase(unittest.TestCase):
finally:
conn.close()
- conn = psycopg2.connect(dsn, connection_factory=RealDictConnection)
+ conn = self.connect(connection_factory=RealDictConnection)
try:
curs = conn.cursor()
register_composite('type_ii', conn)
@@ -781,6 +763,31 @@ class AdaptTypeTestCase(unittest.TestCase):
finally:
conn.close()
+ @skip_if_no_composite
+ def test_subclass(self):
+ oid = self._create_type("type_isd",
+ [('anint', 'integer'), ('astring', 'text'), ('adate', 'date')])
+
+ from psycopg2.extras import register_composite, CompositeCaster
+
+ class DictComposite(CompositeCaster):
+ def make(self, values):
+ return dict(zip(self.attnames, values))
+
+ t = register_composite('type_isd', self.conn, factory=DictComposite)
+
+ self.assertEqual(t.name, 'type_isd')
+ self.assertEqual(t.oid, oid)
+
+ 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, dict))
+ self.assertEqual(v['anint'], 10)
+ self.assertEqual(v['astring'], "hello")
+ self.assertEqual(v['adate'], date(2011,1,2))
+
def _create_type(self, name, fields):
curs = self.conn.cursor()
try:
@@ -805,6 +812,735 @@ class AdaptTypeTestCase(unittest.TestCase):
return oid
+def skip_if_json_module(f):
+ """Skip a test if a Python json module *is* available"""
+ @wraps(f)
+ def skip_if_json_module_(self):
+ if psycopg2.extras.json is not None:
+ return self.skipTest("json module is available")
+
+ return f(self)
+
+ return skip_if_json_module_
+
+def skip_if_no_json_module(f):
+ """Skip a test if no Python json module is available"""
+ @wraps(f)
+ def skip_if_no_json_module_(self):
+ if psycopg2.extras.json is None:
+ return self.skipTest("json module not available")
+
+ return f(self)
+
+ return skip_if_no_json_module_
+
+def skip_if_no_json_type(f):
+ """Skip a test if PostgreSQL json type is not available"""
+ @wraps(f)
+ def skip_if_no_json_type_(self):
+ curs = self.conn.cursor()
+ curs.execute("select oid from pg_type where typname = 'json'")
+ if not curs.fetchone():
+ return self.skipTest("json not available in test database")
+
+ return f(self)
+
+ return skip_if_no_json_type_
+
+class JsonTestCase(ConnectingTestCase):
+ @skip_if_json_module
+ def test_module_not_available(self):
+ from psycopg2.extras import Json
+ self.assertRaises(ImportError, Json(None).getquoted)
+
+ @skip_if_json_module
+ def test_customizable_with_module_not_available(self):
+ from psycopg2.extras import Json
+ class MyJson(Json):
+ def dumps(self, obj):
+ assert obj is None
+ return "hi"
+
+ self.assertEqual(MyJson(None).getquoted(), "'hi'")
+
+ @skip_if_no_json_module
+ def test_adapt(self):
+ from psycopg2.extras import json, Json
+
+ objs = [None, "te'xt", 123, 123.45,
+ u'\xe0\u20ac', ['a', 100], {'a': 100} ]
+
+ curs = self.conn.cursor()
+ for obj in enumerate(objs):
+ self.assertEqual(curs.mogrify("%s", (Json(obj),)),
+ psycopg2.extensions.QuotedString(json.dumps(obj)).getquoted())
+
+ @skip_if_no_json_module
+ def test_adapt_dumps(self):
+ from psycopg2.extras import json, Json
+
+ class DecimalEncoder(json.JSONEncoder):
+ def default(self, obj):
+ if isinstance(obj, Decimal):
+ return float(obj)
+ return json.JSONEncoder.default(self, obj)
+
+ curs = self.conn.cursor()
+ obj = Decimal('123.45')
+ dumps = lambda obj: json.dumps(obj, cls=DecimalEncoder)
+ self.assertEqual(curs.mogrify("%s", (Json(obj, dumps=dumps),)),
+ b("'123.45'"))
+
+ @skip_if_no_json_module
+ def test_adapt_subclass(self):
+ from psycopg2.extras import json, Json
+
+ class DecimalEncoder(json.JSONEncoder):
+ def default(self, obj):
+ if isinstance(obj, Decimal):
+ return float(obj)
+ return json.JSONEncoder.default(self, obj)
+
+ class MyJson(Json):
+ def dumps(self, obj):
+ return json.dumps(obj, cls=DecimalEncoder)
+
+ curs = self.conn.cursor()
+ obj = Decimal('123.45')
+ self.assertEqual(curs.mogrify("%s", (MyJson(obj),)),
+ b("'123.45'"))
+
+ @skip_if_no_json_module
+ def test_register_on_dict(self):
+ from psycopg2.extras import Json
+ psycopg2.extensions.register_adapter(dict, Json)
+
+ try:
+ curs = self.conn.cursor()
+ obj = {'a': 123}
+ self.assertEqual(curs.mogrify("%s", (obj,)),
+ b("""'{"a": 123}'"""))
+ finally:
+ del psycopg2.extensions.adapters[dict, psycopg2.extensions.ISQLQuote]
+
+
+ def test_type_not_available(self):
+ curs = self.conn.cursor()
+ curs.execute("select oid from pg_type where typname = 'json'")
+ if curs.fetchone():
+ return self.skipTest("json available in test database")
+
+ self.assertRaises(psycopg2.ProgrammingError,
+ psycopg2.extras.register_json, self.conn)
+
+ @skip_if_no_json_module
+ @skip_before_postgres(9, 2)
+ def test_default_cast(self):
+ curs = self.conn.cursor()
+
+ curs.execute("""select '{"a": 100.0, "b": null}'::json""")
+ self.assertEqual(curs.fetchone()[0], {'a': 100.0, 'b': None})
+
+ curs.execute("""select array['{"a": 100.0, "b": null}']::json[]""")
+ self.assertEqual(curs.fetchone()[0], [{'a': 100.0, 'b': None}])
+
+ @skip_if_no_json_module
+ @skip_if_no_json_type
+ def test_register_on_connection(self):
+ psycopg2.extras.register_json(self.conn)
+ curs = self.conn.cursor()
+ curs.execute("""select '{"a": 100.0, "b": null}'::json""")
+ self.assertEqual(curs.fetchone()[0], {'a': 100.0, 'b': None})
+
+ @skip_if_no_json_module
+ @skip_if_no_json_type
+ def test_register_on_cursor(self):
+ curs = self.conn.cursor()
+ psycopg2.extras.register_json(curs)
+ curs.execute("""select '{"a": 100.0, "b": null}'::json""")
+ self.assertEqual(curs.fetchone()[0], {'a': 100.0, 'b': None})
+
+ @skip_if_no_json_module
+ @skip_if_no_json_type
+ def test_register_globally(self):
+ old = psycopg2.extensions.string_types.get(114)
+ olda = psycopg2.extensions.string_types.get(199)
+ try:
+ new, newa = psycopg2.extras.register_json(self.conn, globally=True)
+ curs = self.conn.cursor()
+ curs.execute("""select '{"a": 100.0, "b": null}'::json""")
+ self.assertEqual(curs.fetchone()[0], {'a': 100.0, 'b': None})
+ finally:
+ psycopg2.extensions.string_types.pop(new.values[0])
+ psycopg2.extensions.string_types.pop(newa.values[0])
+ if old:
+ psycopg2.extensions.register_type(old)
+ if olda:
+ psycopg2.extensions.register_type(olda)
+
+ @skip_if_no_json_module
+ @skip_if_no_json_type
+ def test_loads(self):
+ json = psycopg2.extras.json
+ loads = lambda x: json.loads(x, parse_float=Decimal)
+ psycopg2.extras.register_json(self.conn, loads=loads)
+ curs = self.conn.cursor()
+ curs.execute("""select '{"a": 100.0, "b": null}'::json""")
+ data = curs.fetchone()[0]
+ self.assert_(isinstance(data['a'], Decimal))
+ self.assertEqual(data['a'], Decimal('100.0'))
+
+ @skip_if_no_json_module
+ @skip_if_no_json_type
+ def test_no_conn_curs(self):
+ from psycopg2._json import _get_json_oids
+ oid, array_oid = _get_json_oids(self.conn)
+
+ old = psycopg2.extensions.string_types.get(114)
+ olda = psycopg2.extensions.string_types.get(199)
+ loads = lambda x: psycopg2.extras.json.loads(x, parse_float=Decimal)
+ try:
+ new, newa = psycopg2.extras.register_json(
+ loads=loads, oid=oid, array_oid=array_oid)
+ curs = self.conn.cursor()
+ curs.execute("""select '{"a": 100.0, "b": null}'::json""")
+ data = curs.fetchone()[0]
+ self.assert_(isinstance(data['a'], Decimal))
+ self.assertEqual(data['a'], Decimal('100.0'))
+ finally:
+ psycopg2.extensions.string_types.pop(new.values[0])
+ psycopg2.extensions.string_types.pop(newa.values[0])
+ if old:
+ psycopg2.extensions.register_type(old)
+ if olda:
+ psycopg2.extensions.register_type(olda)
+
+ @skip_if_no_json_module
+ @skip_before_postgres(9, 2)
+ def test_register_default(self):
+ curs = self.conn.cursor()
+
+ loads = lambda x: psycopg2.extras.json.loads(x, parse_float=Decimal)
+ psycopg2.extras.register_default_json(curs, loads=loads)
+
+ curs.execute("""select '{"a": 100.0, "b": null}'::json""")
+ data = curs.fetchone()[0]
+ self.assert_(isinstance(data['a'], Decimal))
+ self.assertEqual(data['a'], Decimal('100.0'))
+
+ curs.execute("""select array['{"a": 100.0, "b": null}']::json[]""")
+ data = curs.fetchone()[0]
+ self.assert_(isinstance(data[0]['a'], Decimal))
+ self.assertEqual(data[0]['a'], Decimal('100.0'))
+
+ @skip_if_no_json_module
+ @skip_if_no_json_type
+ def test_null(self):
+ psycopg2.extras.register_json(self.conn)
+ curs = self.conn.cursor()
+ curs.execute("""select NULL::json""")
+ self.assertEqual(curs.fetchone()[0], None)
+ curs.execute("""select NULL::json[]""")
+ self.assertEqual(curs.fetchone()[0], None)
+
+ @skip_if_no_json_module
+ def test_no_array_oid(self):
+ curs = self.conn.cursor()
+ t1, t2 = psycopg2.extras.register_json(curs, oid=25)
+ self.assertEqual(t1.values[0], 25)
+ self.assertEqual(t2, None)
+
+ curs.execute("""select '{"a": 100.0, "b": null}'::text""")
+ data = curs.fetchone()[0]
+ self.assertEqual(data['a'], 100)
+ self.assertEqual(data['b'], None)
+
+
+class RangeTestCase(unittest.TestCase):
+ def test_noparam(self):
+ from psycopg2.extras import Range
+ r = Range()
+
+ self.assert_(not r.isempty)
+ self.assertEqual(r.lower, None)
+ self.assertEqual(r.upper, None)
+ self.assert_(r.lower_inf)
+ self.assert_(r.upper_inf)
+ self.assert_(not r.lower_inc)
+ self.assert_(not r.upper_inc)
+
+ def test_empty(self):
+ from psycopg2.extras import Range
+ r = Range(empty=True)
+
+ self.assert_(r.isempty)
+ self.assertEqual(r.lower, None)
+ self.assertEqual(r.upper, None)
+ self.assert_(not r.lower_inf)
+ self.assert_(not r.upper_inf)
+ self.assert_(not r.lower_inc)
+ self.assert_(not r.upper_inc)
+
+ def test_nobounds(self):
+ from psycopg2.extras import Range
+ r = Range(10, 20)
+ self.assertEqual(r.lower, 10)
+ self.assertEqual(r.upper, 20)
+ self.assert_(not r.isempty)
+ self.assert_(not r.lower_inf)
+ self.assert_(not r.upper_inf)
+ self.assert_(r.lower_inc)
+ self.assert_(not r.upper_inc)
+
+ def test_bounds(self):
+ from psycopg2.extras import Range
+ for bounds, lower_inc, upper_inc in [
+ ('[)', True, False),
+ ('(]', False, True),
+ ('()', False, False),
+ ('[]', True, True),]:
+ r = Range(10, 20, bounds)
+ self.assertEqual(r.lower, 10)
+ self.assertEqual(r.upper, 20)
+ self.assert_(not r.isempty)
+ self.assert_(not r.lower_inf)
+ self.assert_(not r.upper_inf)
+ self.assertEqual(r.lower_inc, lower_inc)
+ self.assertEqual(r.upper_inc, upper_inc)
+
+ def test_keywords(self):
+ from psycopg2.extras import Range
+ r = Range(upper=20)
+ self.assertEqual(r.lower, None)
+ self.assertEqual(r.upper, 20)
+ self.assert_(not r.isempty)
+ self.assert_(r.lower_inf)
+ self.assert_(not r.upper_inf)
+ self.assert_(not r.lower_inc)
+ self.assert_(not r.upper_inc)
+
+ r = Range(lower=10, bounds='(]')
+ self.assertEqual(r.lower, 10)
+ self.assertEqual(r.upper, None)
+ self.assert_(not r.isempty)
+ self.assert_(not r.lower_inf)
+ self.assert_(r.upper_inf)
+ self.assert_(not r.lower_inc)
+ self.assert_(not r.upper_inc)
+
+ def test_bad_bounds(self):
+ from psycopg2.extras import Range
+ self.assertRaises(ValueError, Range, bounds='(')
+ self.assertRaises(ValueError, Range, bounds='[}')
+
+ def test_in(self):
+ from psycopg2.extras import Range
+ r = Range(empty=True)
+ self.assert_(10 not in r)
+
+ r = Range()
+ self.assert_(10 in r)
+
+ r = Range(lower=10, bounds='[)')
+ self.assert_(9 not in r)
+ self.assert_(10 in r)
+ self.assert_(11 in r)
+
+ r = Range(lower=10, bounds='()')
+ self.assert_(9 not in r)
+ self.assert_(10 not in r)
+ self.assert_(11 in r)
+
+ r = Range(upper=20, bounds='()')
+ self.assert_(19 in r)
+ self.assert_(20 not in r)
+ self.assert_(21 not in r)
+
+ r = Range(upper=20, bounds='(]')
+ self.assert_(19 in r)
+ self.assert_(20 in r)
+ self.assert_(21 not in r)
+
+ r = Range(10, 20)
+ self.assert_(9 not in r)
+ self.assert_(10 in r)
+ self.assert_(11 in r)
+ self.assert_(19 in r)
+ self.assert_(20 not in r)
+ self.assert_(21 not in r)
+
+ r = Range(10, 20, '(]')
+ self.assert_(9 not in r)
+ self.assert_(10 not in r)
+ self.assert_(11 in r)
+ self.assert_(19 in r)
+ self.assert_(20 in r)
+ self.assert_(21 not in r)
+
+ r = Range(20, 10)
+ self.assert_(9 not in r)
+ self.assert_(10 not in r)
+ self.assert_(11 not in r)
+ self.assert_(19 not in r)
+ self.assert_(20 not in r)
+ self.assert_(21 not in r)
+
+ def test_nonzero(self):
+ from psycopg2.extras import Range
+ self.assert_(Range())
+ self.assert_(Range(10, 20))
+ self.assert_(not Range(empty=True))
+
+ def test_eq_hash(self):
+ from psycopg2.extras import Range
+ def assert_equal(r1, r2):
+ self.assert_(r1 == r2)
+ self.assert_(hash(r1) == hash(r2))
+
+ assert_equal(Range(empty=True), Range(empty=True))
+ assert_equal(Range(), Range())
+ assert_equal(Range(10, None), Range(10, None))
+ assert_equal(Range(10, 20), Range(10, 20))
+ assert_equal(Range(10, 20), Range(10, 20, '[)'))
+ assert_equal(Range(10, 20, '[]'), Range(10, 20, '[]'))
+
+ def assert_not_equal(r1, r2):
+ self.assert_(r1 != r2)
+ self.assert_(hash(r1) != hash(r2))
+
+ assert_not_equal(Range(10, 20), Range(10, 21))
+ assert_not_equal(Range(10, 20), Range(11, 20))
+ assert_not_equal(Range(10, 20, '[)'), Range(10, 20, '[]'))
+
+ def test_not_ordered(self):
+ from psycopg2.extras import Range
+ self.assertRaises(TypeError, lambda: Range(empty=True) < Range(0,4))
+ self.assertRaises(TypeError, lambda: Range(1,2) > Range(0,4))
+ self.assertRaises(TypeError, lambda: Range(1,2) <= Range())
+ self.assertRaises(TypeError, lambda: Range(1,2) >= Range())
+
+
+def skip_if_no_range(f):
+ @wraps(f)
+ def skip_if_no_range_(self):
+ if self.conn.server_version < 90200:
+ return self.skipTest(
+ "server version %s doesn't support range types"
+ % self.conn.server_version)
+
+ return f(self)
+
+ return skip_if_no_range_
+
+
+class RangeCasterTestCase(ConnectingTestCase):
+
+ builtin_ranges = ('int4range', 'int8range', 'numrange',
+ 'daterange', 'tsrange', 'tstzrange')
+
+ def test_cast_null(self):
+ cur = self.conn.cursor()
+ for type in self.builtin_ranges:
+ cur.execute("select NULL::%s" % type)
+ r = cur.fetchone()[0]
+ self.assertEqual(r, None)
+
+ def test_cast_empty(self):
+ from psycopg2.extras import Range
+ cur = self.conn.cursor()
+ for type in self.builtin_ranges:
+ cur.execute("select 'empty'::%s" % type)
+ r = cur.fetchone()[0]
+ self.assert_(isinstance(r, Range), type)
+ self.assert_(r.isempty)
+
+ def test_cast_inf(self):
+ from psycopg2.extras import Range
+ cur = self.conn.cursor()
+ for type in self.builtin_ranges:
+ cur.execute("select '(,)'::%s" % type)
+ r = cur.fetchone()[0]
+ self.assert_(isinstance(r, Range), type)
+ self.assert_(not r.isempty)
+ self.assert_(r.lower_inf)
+ self.assert_(r.upper_inf)
+
+ def test_cast_numbers(self):
+ from psycopg2.extras import NumericRange
+ cur = self.conn.cursor()
+ for type in ('int4range', 'int8range'):
+ cur.execute("select '(10,20)'::%s" % type)
+ r = cur.fetchone()[0]
+ self.assert_(isinstance(r, NumericRange))
+ self.assert_(not r.isempty)
+ self.assertEqual(r.lower, 11)
+ self.assertEqual(r.upper, 20)
+ self.assert_(not r.lower_inf)
+ self.assert_(not r.upper_inf)
+ self.assert_(r.lower_inc)
+ self.assert_(not r.upper_inc)
+
+ cur.execute("select '(10.2,20.6)'::numrange")
+ r = cur.fetchone()[0]
+ self.assert_(isinstance(r, NumericRange))
+ self.assert_(not r.isempty)
+ self.assertEqual(r.lower, Decimal('10.2'))
+ self.assertEqual(r.upper, Decimal('20.6'))
+ self.assert_(not r.lower_inf)
+ self.assert_(not r.upper_inf)
+ self.assert_(not r.lower_inc)
+ self.assert_(not r.upper_inc)
+
+ def test_cast_date(self):
+ from psycopg2.extras import DateRange
+ cur = self.conn.cursor()
+ cur.execute("select '(2000-01-01,2012-12-31)'::daterange")
+ r = cur.fetchone()[0]
+ self.assert_(isinstance(r, DateRange))
+ self.assert_(not r.isempty)
+ self.assertEqual(r.lower, date(2000,1,2))
+ self.assertEqual(r.upper, date(2012,12,31))
+ self.assert_(not r.lower_inf)
+ self.assert_(not r.upper_inf)
+ self.assert_(r.lower_inc)
+ self.assert_(not r.upper_inc)
+
+ def test_cast_timestamp(self):
+ from psycopg2.extras import DateTimeRange
+ cur = self.conn.cursor()
+ ts1 = datetime(2000,1,1)
+ ts2 = datetime(2000,12,31,23,59,59,999)
+ cur.execute("select tsrange(%s, %s, '()')", (ts1, ts2))
+ r = cur.fetchone()[0]
+ self.assert_(isinstance(r, DateTimeRange))
+ self.assert_(not r.isempty)
+ self.assertEqual(r.lower, ts1)
+ self.assertEqual(r.upper, ts2)
+ self.assert_(not r.lower_inf)
+ self.assert_(not r.upper_inf)
+ self.assert_(not r.lower_inc)
+ self.assert_(not r.upper_inc)
+
+ def test_cast_timestamptz(self):
+ from psycopg2.extras import DateTimeTZRange
+ from psycopg2.tz import FixedOffsetTimezone
+ cur = self.conn.cursor()
+ ts1 = datetime(2000,1,1, tzinfo=FixedOffsetTimezone(600))
+ ts2 = datetime(2000,12,31,23,59,59,999, tzinfo=FixedOffsetTimezone(600))
+ cur.execute("select tstzrange(%s, %s, '[]')", (ts1, ts2))
+ r = cur.fetchone()[0]
+ self.assert_(isinstance(r, DateTimeTZRange))
+ self.assert_(not r.isempty)
+ self.assertEqual(r.lower, ts1)
+ self.assertEqual(r.upper, ts2)
+ self.assert_(not r.lower_inf)
+ self.assert_(not r.upper_inf)
+ self.assert_(r.lower_inc)
+ self.assert_(r.upper_inc)
+
+ def test_adapt_number_range(self):
+ from psycopg2.extras import NumericRange
+ cur = self.conn.cursor()
+
+ r = NumericRange(empty=True)
+ cur.execute("select %s::int4range", (r,))
+ r1 = cur.fetchone()[0]
+ self.assert_(isinstance(r1, NumericRange))
+ self.assert_(r1.isempty)
+
+ r = NumericRange(10, 20)
+ cur.execute("select %s::int8range", (r,))
+ r1 = cur.fetchone()[0]
+ self.assert_(isinstance(r1, NumericRange))
+ self.assertEqual(r1.lower, 10)
+ self.assertEqual(r1.upper, 20)
+ self.assert_(r1.lower_inc)
+ self.assert_(not r1.upper_inc)
+
+ r = NumericRange(Decimal('10.2'), Decimal('20.5'), '(]')
+ cur.execute("select %s::numrange", (r,))
+ r1 = cur.fetchone()[0]
+ self.assert_(isinstance(r1, NumericRange))
+ self.assertEqual(r1.lower, Decimal('10.2'))
+ self.assertEqual(r1.upper, Decimal('20.5'))
+ self.assert_(not r1.lower_inc)
+ self.assert_(r1.upper_inc)
+
+ def test_adapt_numeric_range(self):
+ from psycopg2.extras import NumericRange
+ cur = self.conn.cursor()
+
+ r = NumericRange(empty=True)
+ cur.execute("select %s::int4range", (r,))
+ r1 = cur.fetchone()[0]
+ self.assert_(isinstance(r1, NumericRange), r1)
+ self.assert_(r1.isempty)
+
+ r = NumericRange(10, 20)
+ cur.execute("select %s::int8range", (r,))
+ r1 = cur.fetchone()[0]
+ self.assert_(isinstance(r1, NumericRange))
+ self.assertEqual(r1.lower, 10)
+ self.assertEqual(r1.upper, 20)
+ self.assert_(r1.lower_inc)
+ self.assert_(not r1.upper_inc)
+
+ r = NumericRange(Decimal('10.2'), Decimal('20.5'), '(]')
+ cur.execute("select %s::numrange", (r,))
+ r1 = cur.fetchone()[0]
+ self.assert_(isinstance(r1, NumericRange))
+ self.assertEqual(r1.lower, Decimal('10.2'))
+ self.assertEqual(r1.upper, Decimal('20.5'))
+ self.assert_(not r1.lower_inc)
+ self.assert_(r1.upper_inc)
+
+ def test_adapt_date_range(self):
+ from psycopg2.extras import DateRange, DateTimeRange, DateTimeTZRange
+ from psycopg2.tz import FixedOffsetTimezone
+ cur = self.conn.cursor()
+
+ d1 = date(2012, 01, 01)
+ d2 = date(2012, 12, 31)
+ r = DateRange(d1, d2)
+ cur.execute("select %s", (r,))
+ r1 = cur.fetchone()[0]
+ self.assert_(isinstance(r1, DateRange))
+ self.assertEqual(r1.lower, d1)
+ self.assertEqual(r1.upper, d2)
+ self.assert_(r1.lower_inc)
+ self.assert_(not r1.upper_inc)
+
+ r = DateTimeRange(empty=True)
+ cur.execute("select %s", (r,))
+ r1 = cur.fetchone()[0]
+ self.assert_(isinstance(r1, DateTimeRange))
+ self.assert_(r1.isempty)
+
+ ts1 = datetime(2000,1,1, tzinfo=FixedOffsetTimezone(600))
+ ts2 = datetime(2000,12,31,23,59,59,999, tzinfo=FixedOffsetTimezone(600))
+ r = DateTimeTZRange(ts1, ts2, '(]')
+ cur.execute("select %s", (r,))
+ r1 = cur.fetchone()[0]
+ self.assert_(isinstance(r1, DateTimeTZRange))
+ self.assertEqual(r1.lower, ts1)
+ self.assertEqual(r1.upper, ts2)
+ self.assert_(not r1.lower_inc)
+ self.assert_(r1.upper_inc)
+
+ def test_register_range_adapter(self):
+ from psycopg2.extras import Range, register_range
+ cur = self.conn.cursor()
+ cur.execute("create type textrange as range (subtype=text)")
+ rc = register_range('textrange', 'TextRange', cur)
+
+ TextRange = rc.range
+ self.assert_(issubclass(TextRange, Range))
+ self.assertEqual(TextRange.__name__, 'TextRange')
+
+ r = TextRange('a', 'b', '(]')
+ cur.execute("select %s", (r,))
+ r1 = cur.fetchone()[0]
+ self.assertEqual(r1.lower, 'a')
+ self.assertEqual(r1.upper, 'b')
+ self.assert_(not r1.lower_inc)
+ self.assert_(r1.upper_inc)
+
+ cur.execute("select %s", ([r,r,r],))
+ rs = cur.fetchone()[0]
+ self.assertEqual(len(rs), 3)
+ for r1 in rs:
+ self.assertEqual(r1.lower, 'a')
+ self.assertEqual(r1.upper, 'b')
+ self.assert_(not r1.lower_inc)
+ self.assert_(r1.upper_inc)
+
+ def test_range_escaping(self):
+ from psycopg2.extras import register_range
+ cur = self.conn.cursor()
+ cur.execute("create type textrange as range (subtype=text)")
+ rc = register_range('textrange', 'TextRange', cur)
+
+ TextRange = rc.range
+ cur.execute("""
+ create table rangetest (
+ id integer primary key,
+ range textrange)""")
+
+ bounds = [ '[)', '(]', '()', '[]' ]
+ ranges = [ TextRange(low, up, bounds[i % 4])
+ for i, (low, up) in enumerate(zip(
+ [None] + map(chr, range(1, 128)),
+ map(chr, range(1,128)) + [None],
+ ))]
+ ranges.append(TextRange())
+ ranges.append(TextRange(empty=True))
+
+ errs = 0
+ for i, r in enumerate(ranges):
+ # not all the ranges make sense:
+ # fun fact: select ascii('#') < ascii('$'), '#' < '$'
+ # yelds... t, f! At least in en_GB.UTF-8 collation.
+ # which seems suggesting a supremacy of the pound on the dollar.
+ # So some of these ranges will fail to insert. Be prepared but...
+ try:
+ cur.execute("""
+ savepoint x;
+ insert into rangetest (id, range) values (%s, %s);
+ """, (i, r))
+ except psycopg2.DataError:
+ errs += 1
+ cur.execute("rollback to savepoint x;")
+
+ # ...not too many errors! in the above collate there are 17 errors:
+ # assume in other collates we won't find more than 30
+ self.assert_(errs < 30,
+ "too many collate errors. Is the test working?")
+
+ cur.execute("select id, range from rangetest order by id")
+ for i, r in cur:
+ self.assertEqual(ranges[i].lower, r.lower)
+ self.assertEqual(ranges[i].upper, r.upper)
+ self.assertEqual(ranges[i].lower_inc, r.lower_inc)
+ self.assertEqual(ranges[i].upper_inc, r.upper_inc)
+ self.assertEqual(ranges[i].lower_inf, r.lower_inf)
+ self.assertEqual(ranges[i].upper_inf, r.upper_inf)
+
+ def test_range_not_found(self):
+ from psycopg2.extras import register_range
+ cur = self.conn.cursor()
+ self.assertRaises(psycopg2.ProgrammingError,
+ register_range, 'nosuchrange', 'FailRange', cur)
+
+ def test_schema_range(self):
+ cur = self.conn.cursor()
+ cur.execute("create schema rs")
+ cur.execute("create type r1 as range (subtype=text)")
+ cur.execute("create type r2 as range (subtype=text)")
+ cur.execute("create type rs.r2 as range (subtype=text)")
+ cur.execute("create type rs.r3 as range (subtype=text)")
+ cur.execute("savepoint x")
+
+ from psycopg2.extras import register_range
+ ra1 = register_range('r1', 'r1', cur)
+ ra2 = register_range('r2', 'r2', cur)
+ rars2 = register_range('rs.r2', 'r2', cur)
+ rars3 = register_range('rs.r3', 'r3', cur)
+
+ self.assertNotEqual(
+ ra2.typecaster.values[0],
+ rars2.typecaster.values[0])
+
+ self.assertRaises(psycopg2.ProgrammingError,
+ register_range, 'r3', 'FailRange', cur)
+ cur.execute("rollback to savepoint x;")
+
+ self.assertRaises(psycopg2.ProgrammingError,
+ register_range, 'rs.r1', 'FailRange', cur)
+ cur.execute("rollback to savepoint x;")
+
+decorate_all_tests(RangeCasterTestCase, skip_if_no_range)
+
+
def test_suite():
return unittest.TestLoader().loadTestsFromName(__name__)
diff --git a/tests/test_with.py b/tests/test_with.py
new file mode 100755
index 0000000..d39016c
--- /dev/null
+++ b/tests/test_with.py
@@ -0,0 +1,208 @@
+#!/usr/bin/env python
+
+# test_ctxman.py - unit test for connection and cursor used as context manager
+#
+# Copyright (C) 2012 Daniele Varrazzo <daniele.varrazzo@gmail.com>
+#
+# 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 __future__ import with_statement
+
+import psycopg2
+import psycopg2.extensions as ext
+
+from testutils import unittest, ConnectingTestCase
+
+class WithTestCase(ConnectingTestCase):
+ def setUp(self):
+ ConnectingTestCase.setUp(self)
+ curs = self.conn.cursor()
+ try:
+ curs.execute("delete from test_with")
+ self.conn.commit()
+ except psycopg2.ProgrammingError:
+ # assume table doesn't exist
+ self.conn.rollback()
+ curs.execute("create table test_with (id integer primary key)")
+ self.conn.commit()
+
+
+class WithConnectionTestCase(WithTestCase):
+ def test_with_ok(self):
+ with self.conn as conn:
+ self.assert_(self.conn is conn)
+ self.assertEqual(conn.status, ext.STATUS_READY)
+ curs = conn.cursor()
+ curs.execute("insert into test_with values (1)")
+ self.assertEqual(conn.status, ext.STATUS_BEGIN)
+
+ self.assertEqual(self.conn.status, ext.STATUS_READY)
+ self.assert_(not self.conn.closed)
+
+ curs = self.conn.cursor()
+ curs.execute("select * from test_with")
+ self.assertEqual(curs.fetchall(), [(1,)])
+
+ def test_with_connect_idiom(self):
+ with self.connect() as conn:
+ self.assertEqual(conn.status, ext.STATUS_READY)
+ curs = conn.cursor()
+ curs.execute("insert into test_with values (2)")
+ self.assertEqual(conn.status, ext.STATUS_BEGIN)
+
+ self.assertEqual(self.conn.status, ext.STATUS_READY)
+ self.assert_(not self.conn.closed)
+
+ curs = self.conn.cursor()
+ curs.execute("select * from test_with")
+ self.assertEqual(curs.fetchall(), [(2,)])
+
+ def test_with_error_db(self):
+ def f():
+ with self.conn as conn:
+ curs = conn.cursor()
+ curs.execute("insert into test_with values ('a')")
+
+ self.assertRaises(psycopg2.DataError, f)
+ self.assertEqual(self.conn.status, ext.STATUS_READY)
+ self.assert_(not self.conn.closed)
+
+ curs = self.conn.cursor()
+ curs.execute("select * from test_with")
+ self.assertEqual(curs.fetchall(), [])
+
+ def test_with_error_python(self):
+ def f():
+ with self.conn as conn:
+ curs = conn.cursor()
+ curs.execute("insert into test_with values (3)")
+ 1/0
+
+ self.assertRaises(ZeroDivisionError, f)
+ self.assertEqual(self.conn.status, ext.STATUS_READY)
+ self.assert_(not self.conn.closed)
+
+ curs = self.conn.cursor()
+ curs.execute("select * from test_with")
+ self.assertEqual(curs.fetchall(), [])
+
+ def test_with_closed(self):
+ def f():
+ with self.conn:
+ pass
+
+ self.conn.close()
+ self.assertRaises(psycopg2.InterfaceError, f)
+
+ def test_subclass_commit(self):
+ commits = []
+ class MyConn(ext.connection):
+ def commit(self):
+ commits.append(None)
+ super(MyConn, self).commit()
+
+ with self.connect(connection_factory=MyConn) as conn:
+ curs = conn.cursor()
+ curs.execute("insert into test_with values (10)")
+
+ self.assertEqual(conn.status, ext.STATUS_READY)
+ self.assert_(commits)
+
+ curs = self.conn.cursor()
+ curs.execute("select * from test_with")
+ self.assertEqual(curs.fetchall(), [(10,)])
+
+ def test_subclass_rollback(self):
+ rollbacks = []
+ class MyConn(ext.connection):
+ def rollback(self):
+ rollbacks.append(None)
+ super(MyConn, self).rollback()
+
+ try:
+ with self.connect(connection_factory=MyConn) as conn:
+ curs = conn.cursor()
+ curs.execute("insert into test_with values (11)")
+ 1/0
+ except ZeroDivisionError:
+ pass
+ else:
+ self.assert_("exception not raised")
+
+ self.assertEqual(conn.status, ext.STATUS_READY)
+ self.assert_(rollbacks)
+
+ curs = conn.cursor()
+ curs.execute("select * from test_with")
+ self.assertEqual(curs.fetchall(), [])
+
+
+class WithCursorTestCase(WithTestCase):
+ def test_with_ok(self):
+ with self.conn as conn:
+ with conn.cursor() as curs:
+ curs.execute("insert into test_with values (4)")
+ self.assert_(not curs.closed)
+ self.assertEqual(self.conn.status, ext.STATUS_BEGIN)
+ self.assert_(curs.closed)
+
+ self.assertEqual(self.conn.status, ext.STATUS_READY)
+ self.assert_(not self.conn.closed)
+
+ curs = self.conn.cursor()
+ curs.execute("select * from test_with")
+ self.assertEqual(curs.fetchall(), [(4,)])
+
+ def test_with_error(self):
+ try:
+ with self.conn as conn:
+ with conn.cursor() as curs:
+ curs.execute("insert into test_with values (5)")
+ 1/0
+ except ZeroDivisionError:
+ pass
+
+ self.assertEqual(self.conn.status, ext.STATUS_READY)
+ self.assert_(not self.conn.closed)
+ self.assert_(curs.closed)
+
+ curs = self.conn.cursor()
+ curs.execute("select * from test_with")
+ self.assertEqual(curs.fetchall(), [])
+
+ def test_subclass(self):
+ closes = []
+ class MyCurs(ext.cursor):
+ def close(self):
+ closes.append(None)
+ super(MyCurs, self).close()
+
+ with self.conn.cursor(cursor_factory=MyCurs) as curs:
+ self.assert_(isinstance(curs, MyCurs))
+
+ self.assert_(curs.closed)
+ self.assert_(closes)
+
+
+def test_suite():
+ return unittest.TestLoader().loadTestsFromName(__name__)
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/tests/testutils.py b/tests/testutils.py
index 26551d4..1e2e150 100644
--- a/tests/testutils.py
+++ b/tests/testutils.py
@@ -26,6 +26,8 @@
import os
import sys
+from functools import wraps
+from testconfig import dsn
try:
import unittest2
@@ -43,6 +45,7 @@ else:
def skipIf(cond, msg):
def skipIf_(f):
+ @wraps(f)
def skipIf__(self):
if cond:
warnings.warn(msg)
@@ -72,15 +75,62 @@ or unittest.TestCase.assert_ is not unittest.TestCase.assertTrue:
unittest.TestCase.failUnlessEqual = unittest.TestCase.assertEqual
-def decorate_all_tests(cls, decorator):
- """Apply *decorator* to all the tests defined in the TestCase *cls*."""
+class ConnectingTestCase(unittest.TestCase):
+ """A test case providing connections for tests.
+
+ A connection for the test is always available as `self.conn`. Others can be
+ created with `self.connect()`. All are closed on tearDown.
+
+ Subclasses needing to customize setUp and tearDown should remember to call
+ the base class implementations.
+ """
+ def setUp(self):
+ self._conns = []
+
+ def tearDown(self):
+ # close the connections used in the test
+ for conn in self._conns:
+ if not conn.closed:
+ conn.close()
+
+ def connect(self, **kwargs):
+ try:
+ self._conns
+ except AttributeError, e:
+ raise AttributeError(
+ "%s (did you remember calling ConnectingTestCase.setUp()?)"
+ % e)
+
+ import psycopg2
+ conn = psycopg2.connect(dsn, **kwargs)
+ self._conns.append(conn)
+ return conn
+
+ def _get_conn(self):
+ if not hasattr(self, '_the_conn'):
+ self._the_conn = self.connect()
+
+ return self._the_conn
+
+ def _set_conn(self, conn):
+ self._the_conn = conn
+
+ conn = property(_get_conn, _set_conn)
+
+
+def decorate_all_tests(cls, *decorators):
+ """
+ Apply all the *decorators* 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)))
+ for d in decorators:
+ setattr(cls, n, d(getattr(cls, n)))
def skip_if_no_uuid(f):
"""Decorator to skip a test if uuid is not supported by Py/PG."""
+ @wraps(f)
def skip_if_no_uuid_(self):
try:
import uuid
@@ -104,6 +154,7 @@ def skip_if_no_uuid(f):
def skip_if_tpc_disabled(f):
"""Skip a test if the server has tpc support disabled."""
+ @wraps(f)
def skip_if_tpc_disabled_(self):
from psycopg2 import ProgrammingError
cnn = self.connect()
@@ -123,11 +174,11 @@ def skip_if_tpc_disabled(f):
"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):
+ @wraps(f)
def skip_if_no_namedtuple_(self):
try:
from collections import namedtuple
@@ -136,12 +187,12 @@ def skip_if_no_namedtuple(f):
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."""
+ @wraps(f)
def skip_if_no_iobase_(self):
try:
from io import TextIOBase
@@ -157,6 +208,7 @@ 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):
+ @wraps(f)
def skip_before_postgres__(self):
if self.conn.server_version < int("%d%02d%02d" % ver):
return self.skipTest("skipped because PostgreSQL %s"
@@ -171,6 +223,7 @@ 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):
+ @wraps(f)
def skip_after_postgres__(self):
if self.conn.server_version >= int("%d%02d%02d" % ver):
return self.skipTest("skipped because PostgreSQL %s"
@@ -184,6 +237,7 @@ def skip_after_postgres(*ver):
def skip_before_python(*ver):
"""Skip a test on Python before a certain version."""
def skip_before_python_(f):
+ @wraps(f)
def skip_before_python__(self):
if sys.version_info[:len(ver)] < ver:
return self.skipTest("skipped because Python %s"
@@ -197,6 +251,7 @@ def skip_before_python(*ver):
def skip_from_python(*ver):
"""Skip a test on Python after (including) a certain version."""
def skip_from_python_(f):
+ @wraps(f)
def skip_from_python__(self):
if sys.version_info[:len(ver)] >= ver:
return self.skipTest("skipped because Python %s"
@@ -207,6 +262,36 @@ def skip_from_python(*ver):
return skip_from_python__
return skip_from_python_
+def skip_if_no_superuser(f):
+ """Skip a test if the database user running the test is not a superuser"""
+ @wraps(f)
+ def skip_if_no_superuser_(self):
+ from psycopg2 import ProgrammingError
+ try:
+ return f(self)
+ except ProgrammingError, e:
+ import psycopg2.errorcodes
+ if e.pgcode == psycopg2.errorcodes.INSUFFICIENT_PRIVILEGE:
+ self.skipTest("skipped because not superuser")
+ else:
+ raise
+
+ return skip_if_no_superuser_
+
+def skip_if_green(reason):
+ def skip_if_green_(f):
+ @wraps(f)
+ def skip_if_green__(self):
+ from testconfig import green
+ if green:
+ return self.skipTest(reason)
+ else:
+ return f(self)
+
+ return skip_if_green__
+ return skip_if_green_
+
+skip_copy_if_green = skip_if_green("copy in async mode currently not supported")
def script_to_py3(script):
"""Convert a script to Python3 syntax if required."""
diff --git a/tox.ini b/tox.ini
new file mode 100644
index 0000000..f27f3f1
--- /dev/null
+++ b/tox.ini
@@ -0,0 +1,10 @@
+# Tox (http://tox.testrun.org/) is a tool for running tests
+# in multiple virtualenvs. This configuration file will run the
+# test suite on all supported python versions. To use it, "pip install tox"
+# and then run "tox" from this directory.
+
+[tox]
+envlist = py26, py27
+
+[testenv]
+commands = make check