Merge branch 'devel'

This commit is contained in:
Daniele Varrazzo 2013-04-07 17:43:35 +01:00
commit 80e105c74d
132 changed files with 5217 additions and 7850 deletions

1
.gitignore vendored
View File

@ -10,3 +10,4 @@ doc/src/_build/*
doc/html/* doc/html/*
doc/psycopg2.txt doc/psycopg2.txt
env env
.tox

13
.travis.yml Normal file
View File

@ -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

2455
ChangeLog

File diff suppressed because it is too large Load Diff

340
NEWS
View File

@ -1,45 +1,83 @@
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 What's new in psycopg 2.4.6
--------------------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^
- Fixed 'cursor()' arguments propagation in connection subclasses - Fixed 'cursor()' arguments propagation in connection subclasses
and overriding of the 'cursor_factory' argument. Thanks to and overriding of the 'cursor_factory' argument. Thanks to
Corry Haines for the report and the initial patch (ticket #105). Corry Haines for the report and the initial patch (:ticket:`#105`).
- Dropped GIL release during string adaptation around a function call - Dropped GIL release during string adaptation around a function call
invoking a Python API function, which could cause interpreter crash. invoking a Python API function, which could cause interpreter crash.
Thanks to Manu Cupcic for the report (ticket #110). Thanks to Manu Cupcic for the report (:ticket:`#110`).
- Close a green connection if there is an error in the callback. - Close a green connection if there is an error in the callback.
Maybe a harsh solution but it leaves the program responsive Maybe a harsh solution but it leaves the program responsive
(ticket #113). (:ticket:`#113`).
- 'register_hstore()', 'register_composite()', 'tpc_recover()' work with - 'register_hstore()', 'register_composite()', 'tpc_recover()' work with
RealDictConnection and Cursor (ticket #114). RealDictConnection and Cursor (:ticket:`#114`).
- Fixed broken pool for Zope and connections re-init across ZSQL methods - Fixed broken pool for Zope and connections re-init across ZSQL methods
in the same request (tickets #123, #125, #142). in the same request (tickets #123, #125, #142).
- connect() raises an exception instead of swallowing keyword arguments - connect() raises an exception instead of swallowing keyword arguments
when a connection string is specified as well (ticket #131). when a connection string is specified as well (:ticket:`#131`).
- Discard any result produced by 'executemany()' (ticket #133). - Discard any result produced by 'executemany()' (:ticket:`#133`).
- Fixed pickling of FixedOffsetTimezone objects (ticket #135). - Fixed pickling of FixedOffsetTimezone objects (:ticket:`#135`).
- Release the GIL around PQgetResult calls after COPY (ticket #140). - Release the GIL around PQgetResult calls after COPY (:ticket:`#140`).
- Fixed empty strings handling in composite caster (ticket #141). - Fixed empty strings handling in composite caster (:ticket:`#141`).
- Fixed pickling of DictRow and RealDictRow objects. - Fixed pickling of DictRow and RealDictRow objects.
What's new in psycopg 2.4.5 What's new in psycopg 2.4.5
--------------------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^
- The close() methods on connections and cursors don't raise exceptions - The close() methods on connections and cursors don't raise exceptions
if called on already closed objects. if called on already closed objects.
- Fixed fetchmany() with no argument in cursor subclasses - Fixed fetchmany() with no argument in cursor subclasses
(ticket #84). (:ticket:`#84`).
- Use lo_creat() instead of lo_create() when possible for better - Use lo_creat() instead of lo_create() when possible for better
interaction with pgpool-II (ticket #88). interaction with pgpool-II (:ticket:`#88`).
- Error and its subclasses are picklable, useful for multiprocessing - Error and its subclasses are picklable, useful for multiprocessing
interaction (ticket #90). interaction (:ticket:`#90`).
- Better efficiency and formatting of timezone offset objects thanks - Better efficiency and formatting of timezone offset objects thanks
to Menno Smits (tickets #94, #95). to Menno Smits (tickets #94, #95).
- Fixed 'rownumber' during iteration on cursor subclasses. - Fixed 'rownumber' during iteration on cursor subclasses.
Regression introduced in 2.4.4 (ticket #100). Regression introduced in 2.4.4 (:ticket:`#100`).
- Added support for 'inet' arrays. - Added support for 'inet' arrays.
- Fixed 'commit()' concurrency problem (ticket #103). - Fixed 'commit()' concurrency problem (:ticket:`#103`).
- Codebase cleaned up using the GCC Python plugin's static analysis - Codebase cleaned up using the GCC Python plugin's static analysis
tool, which has revealed several unchecked return values, possible tool, which has revealed several unchecked return values, possible
NULL dereferences, reference counting problems. Many thanks to David NULL dereferences, reference counting problems. Many thanks to David
@ -47,48 +85,48 @@ What's new in psycopg 2.4.5
What's new in psycopg 2.4.4 What's new in psycopg 2.4.4
--------------------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^
- 'register_composite()' also works with the types implicitly defined - 'register_composite()' also works with the types implicitly defined
after a table row, not only with the ones created by 'CREATE TYPE'. after a table row, not only with the ones created by 'CREATE TYPE'.
- Values for the isolation level symbolic constants restored to what - Values for the isolation level symbolic constants restored to what
they were before release 2.4.2 to avoid breaking apps using the they were before release 2.4.2 to avoid breaking apps using the
values instead of the constants. values instead of the constants.
- Named DictCursor/RealDictCursor honour itersize (ticket #80). - Named DictCursor/RealDictCursor honour itersize (:ticket:`#80`).
- Fixed rollback on error on Zope (ticket #73). - Fixed rollback on error on Zope (:ticket:`#73`).
- Raise 'DatabaseError' instead of 'Error' with empty libpq errors, - Raise 'DatabaseError' instead of 'Error' with empty libpq errors,
consistently with other disconnection-related errors: regression consistently with other disconnection-related errors: regression
introduced in release 2.4.1 (ticket #82). introduced in release 2.4.1 (:ticket:`#82`).
What's new in psycopg 2.4.3 What's new in psycopg 2.4.3
--------------------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^
- connect() supports all the keyword arguments supported by the - connect() supports all the keyword arguments supported by the
database database
- Added 'new_array_type()' function for easy creation of array - Added 'new_array_type()' function for easy creation of array
typecasters. typecasters.
- Added support for arrays of hstores and composite types (ticket #66). - Added support for arrays of hstores and composite types (:ticket:`#66`).
- Fixed segfault in case of transaction started with connection lost - Fixed segfault in case of transaction started with connection lost
(and possibly other events). (and possibly other events).
- Fixed adaptation of Decimal type in sub-interpreters, such as in - Fixed adaptation of Decimal type in sub-interpreters, such as in
certain mod_wsgi configurations (ticket #52). certain mod_wsgi configurations (:ticket:`#52`).
- Rollback connections in transaction or in error before putting them - Rollback connections in transaction or in error before putting them
back into a pool. Also discard broken connections (ticket #62). back into a pool. Also discard broken connections (:ticket:`#62`).
- Lazy import of the slow uuid module, thanks to Marko Kreen. - Lazy import of the slow uuid module, thanks to Marko Kreen.
- Fixed NamedTupleCursor.executemany() (ticket #65). - Fixed NamedTupleCursor.executemany() (:ticket:`#65`).
- Fixed --static-libpq setup option (ticket #64). - Fixed --static-libpq setup option (:ticket:`#64`).
- Fixed interaction between RealDictCursor and named cursors - Fixed interaction between RealDictCursor and named cursors
(ticket #67). (:ticket:`#67`).
- Dropped limit on the columns length in COPY operations (ticket #68). - Dropped limit on the columns length in COPY operations (:ticket:`#68`).
- Fixed reference leak with arguments referenced more than once - Fixed reference leak with arguments referenced more than once
in queries (ticket #81). in queries (:ticket:`#81`).
- Fixed typecasting of arrays containing consecutive backslashes. - Fixed typecasting of arrays containing consecutive backslashes.
- 'errorcodes' map updated to PostgreSQL 9.1. - 'errorcodes' map updated to PostgreSQL 9.1.
What's new in psycopg 2.4.2 What's new in psycopg 2.4.2
--------------------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^
- Added 'set_session()' method and 'autocommit' property to the - Added 'set_session()' method and 'autocommit' property to the
connection. Added support for read-only sessions and, for PostgreSQL connection. Added support for read-only sessions and, for PostgreSQL
@ -97,13 +135,13 @@ What's new in psycopg 2.4.2
- Psycopg doesn't execute queries at connection time to find the - Psycopg doesn't execute queries at connection time to find the
default isolation level. default isolation level.
- Fixed bug with multithread code potentially causing loss of sync - Fixed bug with multithread code potentially causing loss of sync
with the server communication or lock of the client (ticket #55). 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 - Don't fail import if mx.DateTime module can't be found, even if its
support was built (ticket #53). support was built (:ticket:`#53`).
- Fixed escape for negative numbers prefixed by minus operator - Fixed escape for negative numbers prefixed by minus operator
(ticket #57). (:ticket:`#57`).
- Fixed refcount issue during copy. Reported and fixed by Dave - Fixed refcount issue during copy. Reported and fixed by Dave
Malcolm (ticket #58, Red Hat Bug 711095). Malcolm (:ticket:`#58`, Red Hat Bug 711095).
- Trying to execute concurrent operations on the same connection - Trying to execute concurrent operations on the same connection
through concurrent green thread results in an error instead of a through concurrent green thread results in an error instead of a
deadlock. deadlock.
@ -112,24 +150,24 @@ What's new in psycopg 2.4.2
What's new in psycopg 2.4.1 What's new in psycopg 2.4.1
--------------------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^
- Use own parser for bytea output, not requiring anymore the libpq 9.0 - Use own parser for bytea output, not requiring anymore the libpq 9.0
to parse the hex format. to parse the hex format.
- Don't fail connection if the client encoding is a non-normalized - Don't fail connection if the client encoding is a non-normalized
variant. Issue reported by Peter Eisentraut. variant. Issue reported by Peter Eisentraut.
- Correctly detect an empty query sent to the backend (ticket #46). - Correctly detect an empty query sent to the backend (:ticket:`#46`).
- Fixed a SystemError clobbering libpq errors raised without SQLSTATE. - Fixed a SystemError clobbering libpq errors raised without SQLSTATE.
Bug vivisectioned by Eric Snow. Bug vivisectioned by Eric Snow.
- Fixed interaction between NamedTuple and server-side cursors. - Fixed interaction between NamedTuple and server-side cursors.
- Allow to specify --static-libpq on setup.py command line instead of - Allow to specify --static-libpq on setup.py command line instead of
just in 'setup.cfg'. Patch provided by Matthew Ryan (ticket #48). just in 'setup.cfg'. Patch provided by Matthew Ryan (:ticket:`#48`).
What's new in psycopg 2.4 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 - Added support for Python 3.1 and 3.2. The conversion has also
brought several improvements: brought several improvements:
@ -170,38 +208,38 @@ What's new in psycopg 2.4
found. found.
- Connections and cursors are weakly referenceable. - Connections and cursors are weakly referenceable.
* Bug fixes: Bug fixes:
- Fixed adaptation of None in composite types (ticket #26). Bug - Fixed adaptation of None in composite types (:ticket:`#26`). Bug
report by Karsten Hilbert. report by Karsten Hilbert.
- Fixed several reference leaks in less common code paths. - Fixed several reference leaks in less common code paths.
- Fixed segfault when a large object is closed and its connection no - Fixed segfault when a large object is closed and its connection no
more available. more available.
- Added missing icon to ZPsycopgDA package, not available in Zope - Added missing icon to ZPsycopgDA package, not available in Zope
2.12.9 (ticket #30). Bug report and patch by Pumukel. 2.12.9 (:ticket:`#30`). Bug report and patch by Pumukel.
- Fixed conversion of negative infinity (ticket #40). Bug report and - Fixed conversion of negative infinity (:ticket:`#40`). Bug report and
patch by Marti Raudsepp. patch by Marti Raudsepp.
What's new in psycopg 2.3.2 What's new in psycopg 2.3.2
--------------------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^
- Fixed segfault with middleware not passing DateStyle to the client - Fixed segfault with middleware not passing DateStyle to the client
(ticket #24). Bug report and patch by Marti Raudsepp. (:ticket:`#24`). Bug report and patch by Marti Raudsepp.
What's new in psycopg 2.3.1 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. 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 - `dict` to `hstore` adapter and `hstore` to `dict` typecaster, using both
9.0 and pre-9.0 syntax. 9.0 and pre-9.0 syntax.
@ -210,7 +248,7 @@ psycopg 2.3 aims to expose some new features introduced in PostgreSQL 9.0.
- `namedtuple`-returning cursor. - `namedtuple`-returning cursor.
- Query execution cancel. - Query execution cancel.
* Other features and changes: Other features and changes:
- Dropped support for protocol 2: Psycopg 2.3 can only connect to PostgreSQL - Dropped support for protocol 2: Psycopg 2.3 can only connect to PostgreSQL
servers with version at least 7.4. servers with version at least 7.4.
@ -225,19 +263,19 @@ psycopg 2.3 aims to expose some new features introduced in PostgreSQL 9.0.
in release 2.2, but trying to send a command while being destroyed has been in release 2.2, but trying to send a command while being destroyed has been
considered not safe. considered not safe.
* Bug fixes: Bug fixes:
- Fixed use of `PQfreemem` instead of `free` in binary typecaster. - Fixed use of `PQfreemem` instead of `free` in binary typecaster.
- Fixed access to freed memory in `conn_get_isolation_level()`. - Fixed access to freed memory in `conn_get_isolation_level()`.
- Fixed crash during Decimal adaptation with a few 2.5.x Python versions - Fixed crash during Decimal adaptation with a few 2.5.x Python versions
(ticket #7). (:ticket:`#7`).
- Fixed notices order (ticket #9). - Fixed notices order (:ticket:`#9`).
What's new in psycopg 2.2.2 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 - the call to logging.basicConfig() in pool.py has been dropped: it was
messing with some projects using logging (and a library should not messing with some projects using logging (and a library should not
@ -250,15 +288,15 @@ What's new in psycopg 2.2.2
What's new in psycopg 2.2.1 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 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 two different ways of executing asynchronous queries, thanks to Jan and Daniele
@ -269,7 +307,7 @@ psycopg now supports both classic select() loops and "green" coroutine
libraries. It is all in the documentation, so just point your browser to libraries. It is all in the documentation, so just point your browser to
doc/html/advanced.html. doc/html/advanced.html.
* Other new features: Other new features:
- truncate() method for lobjects. - truncate() method for lobjects.
- COPY functions are now a little bit faster. - COPY functions are now a little bit faster.
@ -281,7 +319,7 @@ doc/html/advanced.html.
- Better timezone support. - Better timezone support.
- Lots of documentation updates. - Lots of documentation updates.
* Bug fixes: Bug fixes:
- Fixed some gc/refcounting problems. - Fixed some gc/refcounting problems.
- Fixed reference leak in NOTIFY reception. - Fixed reference leak in NOTIFY reception.
@ -294,49 +332,55 @@ doc/html/advanced.html.
What's new in psycopg 2.0.14 What's new in psycopg 2.0.14
---------------------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
New features:
* New features:
- Support for adapting tuples to PostgreSQL arrays is now enabled by - Support for adapting tuples to PostgreSQL arrays is now enabled by
default and does not require importing psycopg2.extensions anymore. default and does not require importing psycopg2.extensions anymore.
- "can't adapt" error message now includes full type information. - "can't adapt" error message now includes full type information.
- Thank to Daniele Varrazzo (piro) psycopg2's source package now includes - Thank to Daniele Varrazzo (piro) psycopg2's source package now includes
full documentation in HTML and plain text format. full documentation in HTML and plain text format.
* Bug fixes: Bug fixes:
- No loss of precision when using floats anymore. - No loss of precision when using floats anymore.
- decimal.Decimal "nan" and "infinity" correctly converted to PostgreSQL - decimal.Decimal "nan" and "infinity" correctly converted to PostgreSQL
numeric NaN values (note that PostgreSQL numeric type does not support numeric NaN values (note that PostgreSQL numeric type does not support
infinity but just NaNs.) infinity but just NaNs.)
- psycopg2.extensions now includes Binary. - psycopg2.extensions now includes Binary.
* It seems we're good citizens of the free software ecosystem and that big 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 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.) we'll now not dislike us. *g* (See LICENSE file for the details.)
What's new in psycopg 2.0.13 What's new in psycopg 2.0.13
---------------------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
New features:
* New features:
- Support for UUID arrays. - Support for UUID arrays.
- It is now possible to build psycopg linking to a static libpq - It is now possible to build psycopg linking to a static libpq
library. library.
* Bug fixes: Bug fixes:
- Fixed a deadlock related to using the same connection with - Fixed a deadlock related to using the same connection with
multiple cursors from different threads. multiple cursors from different threads.
- Builds again with MSVC. - Builds again with MSVC.
What's new in psycopg 2.0.12 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 - The connection object now has a reset() method that can be used to
reset the connection to its default state. reset the connection to its default state.
* Bug fixes: Bug fixes:
- copy_to() and copy_from() now accept a much larger number of columns. - copy_to() and copy_from() now accept a much larger number of columns.
- Fixed PostgreSQL version detection. - Fixed PostgreSQL version detection.
- Fixed ZPsycopgDA version check. - Fixed ZPsycopgDA version check.
@ -347,23 +391,26 @@ What's new in psycopg 2.0.12
What's new in psycopg 2.0.11 What's new in psycopg 2.0.11
---------------------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
New features:
* New features:
- DictRow and RealDictRow now use less memory. If you inherit on them - DictRow and RealDictRow now use less memory. If you inherit on them
remember to set __slots__ for your new attributes or be prepare to remember to set __slots__ for your new attributes or be prepare to
go back to old memory usage. go back to old memory usage.
* Bug fixes: Bug fixes:
- Fixed exeception in setup.py. - Fixed exeception in setup.py.
- More robust detection of PostgreSQL development versions. - More robust detection of PostgreSQL development versions.
- Fixed exception in RealDictCursor, introduced in 2.0.10. - Fixed exception in RealDictCursor, introduced in 2.0.10.
What's new in psycopg 2.0.10 What's new in psycopg 2.0.10
---------------------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
New features:
* New features:
- A specialized type-caster that can parse time zones with seconds is - A specialized type-caster that can parse time zones with seconds is
now available. Note that after enabling it (see extras.py) "wrong" now available. Note that after enabling it (see extras.py) "wrong"
time zones will be parsed without raising an exception but the time zones will be parsed without raising an exception but the
@ -376,7 +423,8 @@ What's new in psycopg 2.0.10
- The connection object has a .get_parameter_status() methods that - The connection object has a .get_parameter_status() methods that
can be used to obtain useful information from the server. can be used to obtain useful information from the server.
* Bug fixes: Bug fixes:
- None is now correctly always adapted to NULL. - None is now correctly always adapted to NULL.
- Two double memory free errors provoked by multithreading and - Two double memory free errors provoked by multithreading and
garbage collection are now fixed. garbage collection are now fixed.
@ -390,22 +438,25 @@ What's new in psycopg 2.0.10
What's new in psycopg 2.0.9 What's new in psycopg 2.0.9
--------------------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^
New features:
* New features:
- "import psycopg2.extras" to get some support for handling times - "import psycopg2.extras" to get some support for handling times
and timestamps with seconds in the time zone offset. and timestamps with seconds in the time zone offset.
- DictCursors can now be used as named cursors. - DictCursors can now be used as named cursors.
* Bug fixes: Bug fixes:
- register_type() now accept an explicit None as its second parameter. - register_type() now accept an explicit None as its second parameter.
- psycopg2 should build again on MSVC and Solaris. - psycopg2 should build again on MSVC and Solaris.
What's new in psycopg 2.0.9 What's new in psycopg 2.0.9
--------------------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^
New features:
* New features:
- COPY TO/COPY FROM queries now can be of any size and psycopg will - COPY TO/COPY FROM queries now can be of any size and psycopg will
correctly quote separators. correctly quote separators.
- float values Inf and NaN are now correctly handled and can - float values Inf and NaN are now correctly handled and can
@ -418,30 +469,35 @@ What's new in psycopg 2.0.9
- copy_from() and copy_to() can now use quoted separators. - copy_from() and copy_to() can now use quoted separators.
- "import psycopg2.extras" to get UUID support. - "import psycopg2.extras" to get UUID support.
* Bug fixes: Bug fixes:
- register_type() now works on connection and cursor subclasses. - register_type() now works on connection and cursor subclasses.
- fixed a memory leak when using lobjects. - fixed a memory leak when using lobjects.
What's new in psycopg 2.0.8 What's new in psycopg 2.0.8
--------------------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^
New features:
* New features:
- The connection object now has a get_backend_pid() method that - The connection object now has a get_backend_pid() method that
returns the current PostgreSQL connection backend process PID. returns the current PostgreSQL connection backend process PID.
- The PostgreSQL large object API has been exposed through the - The PostgreSQL large object API has been exposed through the
Cursor.lobject() method. Cursor.lobject() method.
* Bug fixes: Bug fixes:
- Some fixes to ZPsycopgDA have been merged from the Debian package. - Some fixes to ZPsycopgDA have been merged from the Debian package.
- A memory leak was fixed in Cursor.executemany(). - A memory leak was fixed in Cursor.executemany().
- A double free was fixed in pq_complete_error(), that caused crashes - A double free was fixed in pq_complete_error(), that caused crashes
under some error conditions. under some error conditions.
What's new in psycopg 2.0.7
---------------------------
* Improved error handling: What's new in psycopg 2.0.7
^^^^^^^^^^^^^^^^^^^^^^^^^^^
Improved error handling:
- All instances of psycopg2.Error subclasses now have pgerror, - All instances of psycopg2.Error subclasses now have pgerror,
pgcode and cursor attributes. They will be set to None if no pgcode and cursor attributes. They will be set to None if no
value is available. value is available.
@ -455,14 +511,16 @@ What's new in psycopg 2.0.7
raised at that point rather than resulting in "ProgrammingError: raised at that point rather than resulting in "ProgrammingError:
no results to fetch" later on. (#186) no results to fetch" later on. (#186)
* Better PostgreSQL compatibility: Better PostgreSQL compatibility:
- If the server uses standard_conforming_strings, perform - If the server uses standard_conforming_strings, perform
appropriate quoting. appropriate quoting.
- BC dates are now handled if psycopg is compiled with mxDateTime - BC dates are now handled if psycopg is compiled with mxDateTime
support. If using datetime, an appropriate ValueError is support. If using datetime, an appropriate ValueError is
raised. (#203) raised. (#203)
* Other bug fixes: Other bug fixes:
- If multiple sub-interpreters are in use, do not share the Decimal - If multiple sub-interpreters are in use, do not share the Decimal
type between them. (#192) type between them. (#192)
- Buffer objects obtained from psycopg are now accepted by psycopg - Buffer objects obtained from psycopg are now accepted by psycopg
@ -470,7 +528,8 @@ What's new in psycopg 2.0.7
- A few small changes were made to improve DB-API compatibility. - A few small changes were made to improve DB-API compatibility.
All the dbapi20 tests now pass. All the dbapi20 tests now pass.
* Miscellaneous: Miscellaneous:
- The PSYCOPG_DISPLAY_SIZE option is now off by default. This means - The PSYCOPG_DISPLAY_SIZE option is now off by default. This means
that display size will always be set to "None" in that display size will always be set to "None" in
cursor.description. Calculating the display size was expensive, cursor.description. Calculating the display size was expensive,
@ -483,10 +542,12 @@ What's new in psycopg 2.0.7
to set the PSYCOPG_DEBUG environment variable to turn on debug to set the PSYCOPG_DEBUG environment variable to turn on debug
spew. spew.
What's new in psycopg 2.0.6
---------------------------
* Better support for PostgreSQL, Python and win32: 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 - full support for PostgreSQL 8.2, including NULLs in arrays
- support for almost all existing PostgreSQL encodings - support for almost all existing PostgreSQL encodings
- full list of PostgreSQL error codes available by importing the - full list of PostgreSQL error codes available by importing the
@ -494,19 +555,20 @@ What's new in psycopg 2.0.6
- full support for Python 2.5 and 64 bit architectures - full support for Python 2.5 and 64 bit architectures
- better build support on win32 platform - better build support on win32 platform
* Support for per-connection type-casters (used by ZPsycopgDA too, this Support for per-connection type-casters (used by ZPsycopgDA too, this
fixes a long standing bug that made different connections use a random fixes a long standing bug that made different connections use a random
set of date/time type-casters instead of the configured one.) set of date/time type-casters instead of the configured one.)
* Better management of times and dates both from Python and in Zope. Better management of times and dates both from Python and in Zope.
* copy_to and copy_from now take an extra "columns" parameter. copy_to and copy_from now take an extra "columns" parameter.
* Python tuples are now adapted to SQL sequences that can be used with Python tuples are now adapted to SQL sequences that can be used with
the "IN" operator by default if the psycopg2.extensions module is the "IN" operator by default if the psycopg2.extensions module is
imported (i.e., the SQL_IN adapter was moved from extras to extensions.) imported (i.e., the SQL_IN adapter was moved from extras to extensions.)
* Fixed some small buglets and build glitches: Fixed some small buglets and build glitches:
- removed double mutex destroy - removed double mutex destroy
- removed all non-constant initializers - removed all non-constant initializers
- fixed PyObject_HEAD declarations to avoid memory corruption - fixed PyObject_HEAD declarations to avoid memory corruption
@ -516,15 +578,17 @@ What's new in psycopg 2.0.6
- now using more than one argument format raise an error instead of - now using more than one argument format raise an error instead of
a segfault a segfault
What's new in psycopg 2.0.5.1 What's new in psycopg 2.0.5.1
­---------------------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
* Now it really, really builds on MSVC and older gcc versions. * Now it really, really builds on MSVC and older gcc versions.
What's new in psycopg 2.0.5 What's new in psycopg 2.0.5
­-------------------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^
* Fixed various buglets such as: * Fixed various buglets such as:
- segfault when passing an empty string to Binary() - segfault when passing an empty string to Binary()
- segfault on null queries - segfault on null queries
- segfault and bad keyword naming in .executemany() - 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 * connect() now accept both integers and strings as port parameter
What's new in psycopg 2.0.4 What's new in psycopg 2.0.4
--------------------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^
* Fixed float conversion bug introduced in 2.0.3. * Fixed float conversion bug introduced in 2.0.3.
What's new in psycopg 2.0.3 What's new in psycopg 2.0.3
--------------------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^
* Fixed various buglets and a memory leak (see ChangeLog for details) * Fixed various buglets and a memory leak (see ChangeLog for details)
What's new in psycopg 2.0.2 What's new in psycopg 2.0.2
--------------------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^
* Fixed a bug in array typecasting that sometimes made psycopg forget about * Fixed a bug in array typecasting that sometimes made psycopg forget about
the last element in the array. 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.) version is issued only if __GCC__ is defined.)
What's new in psycopg 2.0.1 What's new in psycopg 2.0.1
--------------------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^
* ZPsycopgDA now actually loads. * 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.. :) so that you all stop grumbling about psycopg 2 is still in beta.. :)
What's new in psycopg 2.0 beta 7 What's new in psycopg 2.0 beta 7
-------------------------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
* Ironed out last problems with times and date (should be quite solid now.) * Ironed out last problems with times and date (should be quite solid now.)
@ -591,7 +655,7 @@ What's new in psycopg 2.0 beta 7
* Documentation in the doc/ directory! (With many kudos to piro.) * Documentation in the doc/ directory! (With many kudos to piro.)
What's new in psycopg 2.0 beta 6 What's new in psycopg 2.0 beta 6
-------------------------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
* Support for named cursors (see examples/fetch.py). * Support for named cursors (see examples/fetch.py).
@ -604,7 +668,7 @@ What's new in psycopg 2.0 beta 6
* The "decimal" module is now used if available under Python 2.3. * The "decimal" module is now used if available under Python 2.3.
What's new in psycopg 2.0 beta 5 What's new in psycopg 2.0 beta 5
-------------------------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
* Fixed all known bugs. * Fixed all known bugs.
@ -626,7 +690,7 @@ What's new in psycopg 2.0 beta 5
* NOTIFY is correctly trapped (see examples/notify.py for example code.) * NOTIFY is correctly trapped (see examples/notify.py for example code.)
What's new in psycopg 2.0 beta 4 What's new in psycopg 2.0 beta 4
-------------------------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
* psycopg module is now named psycopg2. * psycopg module is now named psycopg2.
@ -642,14 +706,14 @@ What's new in psycopg 2.0 beta 4
* Fixed lots of small bugs, see ChangeLog for details. * Fixed lots of small bugs, see ChangeLog for details.
What's new in psycopg 2.0 beta 3 What's new in psycopg 2.0 beta 3
-------------------------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
* ZPsycopgDA now works (except table browsing.) * ZPsycopgDA now works (except table browsing.)
* psycopg build again on Python 2.2. * psycopg build again on Python 2.2.
What's new in psycopg 2.0 beta 2 What's new in psycopg 2.0 beta 2
-------------------------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
* Fixed ZPsycopgDA version check (ZPsycopgDA can now be imported in * Fixed ZPsycopgDA version check (ZPsycopgDA can now be imported in
Zope.) Zope.)
@ -662,7 +726,7 @@ What's new in psycopg 2.0 beta 2
* Generic fixed and memory leaks plugs. * Generic fixed and memory leaks plugs.
What's new in psycopg 2.0 beta 1 What's new in psycopg 2.0 beta 1
-------------------------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
* Officially in beta (i.e., no new features will be added.) * Officially in beta (i.e., no new features will be added.)
@ -691,7 +755,7 @@ What's new in psycopg 2.0 beta 1
* A lot of bugfixes (binary, datetime, 64 bit arches, GIL, .executemany()) * A lot of bugfixes (binary, datetime, 64 bit arches, GIL, .executemany())
What's new in psycopg 1.99.13 What's new in psycopg 1.99.13
----------------------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
* Added missing .executemany() method. * Added missing .executemany() method.
@ -699,7 +763,7 @@ What's new in psycopg 1.99.13
faster than before.) faster than before.)
What's new in psycopg 1.99.12 What's new in psycopg 1.99.12
----------------------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
* .rowcount should be ok and in sync with psycopg 1. * .rowcount should be ok and in sync with psycopg 1.
@ -712,7 +776,7 @@ What's new in psycopg 1.99.12
* getquoted() called for real by the mogrification code. * getquoted() called for real by the mogrification code.
What's new in psycopg 1.99.11 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'. 'cursor_factory'.
@ -725,7 +789,7 @@ What's new in psycopg 1.99.11
* fixes to the async core. * fixes to the async core.
What's new in psycopg 1.99.10 What's new in psycopg 1.99.10
----------------------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
* The adapt() function now fully supports the adaptation protocol * The adapt() function now fully supports the adaptation protocol
described in PEP 246. Note that the adapters registry now is indexed 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.) fetching (.fetchXXX() methods.)
What's new in psycopg 1.99.9 What's new in psycopg 1.99.9
---------------------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
* Added simple pooling code (psycopg.pool module); see the reworked * Added simple pooling code (psycopg.pool module); see the reworked
examples/threads.py for example code. examples/threads.py for example code.
@ -753,11 +817,10 @@ What's new in psycopg 1.99.9
* Isn't that enough? :) * Isn't that enough? :)
What's new in psycopg 1.99.8 What's new in psycopg 1.99.8
---------------------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
* added support for UNICODE queries. * added support for UNICODE queries.
* added UNICODE typecaster; to activate it just do::
* added UNICODE typecaster; to activate it just do:
psycopg.extensions.register_type(psycopg.extensions.UNICODE) psycopg.extensions.register_type(psycopg.extensions.UNICODE)
@ -765,57 +828,43 @@ What's new in psycopg 1.99.8
not activated by default. not activated by default.
* cursors now really support the iterator protocol. * cursors now really support the iterator protocol.
* solved the rounding errors in time conversions. * solved the rounding errors in time conversions.
* now cursors support .fileno() and .isready() methods, to be used in * now cursors support .fileno() and .isready() methods, to be used in
select() calls. select() calls.
* .copy_from() and .copy_in() methods are back in (still using the old * .copy_from() and .copy_in() methods are back in (still using the old
protocol, will be updated to use new one in next releasae.) protocol, will be updated to use new one in next releasae.)
* fixed memory corruption bug reported on win32 platform. * fixed memory corruption bug reported on win32 platform.
What's new in psycopg 1.99.7 What's new in psycopg 1.99.7
---------------------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
* added support for tuple factories in cursor objects (removed factory * added support for tuple factories in cursor objects (removed factory
argument in favor of a .tuple_factory attribute on the cursor object); argument in favor of a .tuple_factory attribute on the cursor object);
see the new module psycopg.extras for a cursor (DictCursor) that see the new module psycopg.extras for a cursor (DictCursor) that
return rows as objects that support indexing both by position and return rows as objects that support indexing both by position and
column name. column name.
* added support for tzinfo objects in datetime.timestamp objects: the * 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. datetime.timestamp with a FixedOffsetTimezone initialized as necessary.
What's new in psycopg 1.99.6 What's new in psycopg 1.99.6
---------------------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
* sslmode parameter from 1.1.x * sslmode parameter from 1.1.x
* various datetime conversion improvements. * various datetime conversion improvements.
* now psycopg should compile without mx or without native datetime * now psycopg should compile without mx or without native datetime
(not both, obviously.) (not both, obviously.)
* included various win32/MSVC fixes (pthread.h changes, winsock2 * included various win32/MSVC fixes (pthread.h changes, winsock2
library, include path in setup.py, etc.) library, include path in setup.py, etc.)
* ported interval fixes from 1.1.14/1.1.15. * ported interval fixes from 1.1.14/1.1.15.
* the last query executed by a cursor is now available in the * the last query executed by a cursor is now available in the
.query attribute. .query attribute.
* conversion of unicode strings to backend encoding now uses a table * conversion of unicode strings to backend encoding now uses a table
(that still need to be filled.) (that still need to be filled.)
* cursors now have a .mogrify() method that return the query string * cursors now have a .mogrify() method that return the query string
instead of executing it. instead of executing it.
* connection objects now have a .dsn read-only attribute that holds the * connection objects now have a .dsn read-only attribute that holds the
connection string. connection string.
* moved psycopg C module to _psycopg and made psycopg a python module: * 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 this allows for a neat separation of DBAPI-2.0 functionality and psycopg
extensions; the psycopg namespace will be also used to provide 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.) functions and the like.)
What's new in psycopg 1.99.3 What's new in psycopg 1.99.3
---------------------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
* added support for python 2.3 datetime types (both ways) and made datetime * added support for python 2.3 datetime types (both ways) and made datetime
the default set of typecasters when available. the default set of typecasters when available.
* added example: dt.py. * added example: dt.py.
What's new in psycopg 1.99.3 What's new in psycopg 1.99.3
---------------------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
* initial working support for unicode bound variables: UTF-8 and latin-1 * initial working support for unicode bound variables: UTF-8 and latin-1
backend encodings are natively supported (and the encoding.py example even backend encodings are natively supported (and the encoding.py example even
works!) works!)
* added .set_client_encoding() method on the connection object. * added .set_client_encoding() method on the connection object.
* added examples: encoding.py, binary.py, lastrowid.py. * added examples: encoding.py, binary.py, lastrowid.py.
What's new in psycopg 1.99.2 What's new in psycopg 1.99.2
---------------------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
* better typecasting: * better typecasting:
- DateTimeDelta used for postgresql TIME (merge from 1.1) - DateTimeDelta used for postgresql TIME (merge from 1.1)
- BYTEA now is converted to a real buffer object, not to a string - BYTEA now is converted to a real buffer object, not to a string
* buffer objects are now adapted into Binary objects automatically. * buffer objects are now adapted into Binary objects automatically.
* ported scroll method from 1.1 (DBAPI-2.0 extension for cursors) * ported scroll method from 1.1 (DBAPI-2.0 extension for cursors)
* initial support for some DBAPI-2.0 extensions: * initial support for some DBAPI-2.0 extensions:
- .rownumber attribute for cursors - .rownumber attribute for cursors
- .connection attribute for cursors - .connection attribute for cursors
- .next() and .__iter__() methods to have cursors support the iterator - .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 - all exception objects are exported to the connection object
What's new in psycopg 1.99.1 What's new in psycopg 1.99.1
---------------------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
* implemented microprotocols to adapt arbitrary types to the interface used by * implemented microprotocols to adapt arbitrary types to the interface used by
psycopg to bind variables in execute; 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 What's new in psycopg 1.99.0
---------------------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
* reorganized the whole source tree; * reorganized the whole source tree;

View File

@ -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'
}

View File

@ -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')

View File

@ -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

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 897 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 924 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 930 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 925 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 915 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 929 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 918 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 884 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 878 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 918 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 926 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 893 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 894 B

View File

@ -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)

View File

@ -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.

676
debian/changelog vendored
View File

@ -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

1
debian/compat vendored
View File

@ -1 +0,0 @@
5

64
debian/control vendored
View File

@ -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.

112
debian/copyright vendored
View File

@ -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.

1
debian/pycompat vendored
View File

@ -1 +0,0 @@
2

View File

@ -1,3 +0,0 @@
Name: psycopg2da
ZopeVersions: 3
Global: yes

93
debian/rules vendored
View File

@ -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

2
debian/watch vendored
View File

@ -1,2 +0,0 @@
version=3
http://www.initd.org/pub/software/psycopg/psycopg2-([0-9][0-9\.\-]*).tar.gz debian uupdate

View File

@ -1,4 +0,0 @@
Name: ZPsycopgDA
Directory: ZPsycopgDA:2
Package: zope-psycopgda2
ZopeVersions: >= 2.9

View File

@ -26,3 +26,7 @@ a > tt.sql:hover {
dl.faq dt { dl.faq dt {
font-weight: bold; font-weight: bold;
} }
table.data-types div.line-block {
margin-bottom: 0;
}

View File

@ -27,6 +27,7 @@ More advanced topics
wait(aconn) wait(aconn)
acurs = aconn.cursor() acurs = aconn.cursor()
.. index:: .. index::
double: Subclassing; Cursor double: Subclassing; Cursor
double: Subclassing; Connection 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 it is the class where query building, execution and result type-casting into
Python variables happens. 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:: .. index::
single: Example; Cursor subclass single: Example; Cursor subclass
@ -403,13 +414,13 @@ this will be probably implemented in a future release.
.. _green-support: .. _green-support:
Support to coroutine libraries Support for coroutine libraries
------------------------------ -------------------------------
.. versionadded:: 2.2.0 .. versionadded:: 2.2.0
Psycopg can be used together with coroutine_\-based libraries, and participate Psycopg can be used together with coroutine_\-based libraries and participate
to cooperative multithreading. in cooperative multithreading.
Coroutine-based libraries (such as Eventlet_ or gevent_) can usually patch the 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 Python standard library in order to enable a coroutine switch in the presence of

View File

@ -26,7 +26,7 @@ extensions = ['sphinx.ext.autodoc', 'sphinx.ext.todo', 'sphinx.ext.ifconfig',
'sphinx.ext.doctest', 'sphinx.ext.intersphinx' ] 'sphinx.ext.doctest', 'sphinx.ext.intersphinx' ]
# Specific extensions for Psycopg documentation. # 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. # Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates'] templates_path = ['_templates']
@ -42,7 +42,7 @@ master_doc = 'index'
# General information about the project. # General information about the project.
project = u'Psycopg' 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 # The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the # |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), '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 # The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages. # for a list of supported languages.
#language = None #language = None

View File

@ -21,16 +21,17 @@ The ``connection`` class
Connections are thread safe and can be shared among many threads. See Connections are thread safe and can be shared among many threads. See
:ref:`thread-safety` for details. :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. Return a new `cursor` object using the connection.
If *name* is specified, the returned cursor will be a :ref:`server If *name* is specified, the returned cursor will be a :ref:`server
side cursor <server-side-cursors>` (also known as *named cursor*). side cursor <server-side-cursors>` (also known as *named cursor*).
Otherwise it will be a regular *client side* cursor. By default a Otherwise it will be a regular *client side* cursor. By default a
:sql:`WITHOUT HOLD` cursor is created; to create a :sql:`WITH HOLD` named cursor is declared without :sql:`SCROLL` option and
cursor, pass a `!True` value as the *withhold* parameter. See :sql:`WITHOUT HOLD`: set the argument or property `~cursor.scrollable`
:ref:`server-side-cursors`. 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 The name can be a string not valid as a PostgreSQL identifier: for
example it may start with a digit and contain non-alphanumeric 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. Consider it as part of the query, not as a query parameter.
The *cursor_factory* argument can be used to create non-standard 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 `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:: .. extension::
The `name` and `cursor_factory` parameters are Psycopg All the function arguments are Psycopg extensions to the |DBAPI|.
extensions to the |DBAPI|.
.. index:: .. index::
@ -71,6 +75,10 @@ The ``connection`` class
automatically open, commands have immediate effect. See automatically open, commands have immediate effect. See
:ref:`transactions-control` for details. :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:: .. index::
pair: Transaction; Rollback pair: Transaction; Rollback
@ -81,6 +89,10 @@ The ``connection`` class
connection without committing the changes first will cause an implicit connection without committing the changes first will cause an implicit
rollback to be performed. 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() .. method:: close()
@ -493,6 +505,15 @@ The ``connection`` class
the payload was not accessible. To keep backward compatibility, the payload was not accessible. To keep backward compatibility,
`!Notify` objects can still be accessed as 2 items tuples. `!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:: .. index::
pair: Backend; PID pair: Backend; PID

View File

@ -84,6 +84,11 @@ The ``cursor`` class
`~psycopg2.InterfaceError` will be raised if any operation is `~psycopg2.InterfaceError` will be raised if any operation is
attempted with the cursor. 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 .. attribute:: closed
Read-only boolean attribute: specifies if the cursor is closed Read-only boolean attribute: specifies if the cursor is closed
@ -114,13 +119,44 @@ The ``cursor`` class
The `name` attribute is a Psycopg extension to the |DBAPI|. 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 .. attribute:: withhold
Read/write attribute: specifies if a named cursor lifetime should Read/write attribute: specifies if a named cursor lifetime should
extend outside of the current transaction, i.e., it is possible to 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` 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 .. versionadded:: 2.4.3
.. extension:: .. extension::
@ -297,7 +333,8 @@ The ``cursor`` class
not changed. not changed.
The method can be used both for client-side cursors and 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:: .. note::
@ -527,10 +564,19 @@ The ``cursor`` class
|COPY|__ command documentation). |COPY|__ command documentation).
:param sql: the :sql:`COPY` statement to execute. :param sql: the :sql:`COPY` statement to execute.
:param file: a file-like object; must be a readable file for :param file: a file-like object to read or write (according to *sql*).
:sql:`COPY FROM` or an writable file for :sql:`COPY TO`.
:param size: size of the read buffer to be used in :sql:`COPY FROM`. :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: Example:
>>> cur.copy_expert("COPY test TO STDOUT WITH CSV HEADER", sys.stdout) >>> cur.copy_expert("COPY test TO STDOUT WITH CSV HEADER", sys.stdout)

View File

@ -50,7 +50,7 @@ An example of the available constants defined in the module:
'42P01' '42P01'
Constants representing all the error values documented by PostgreSQL versions 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) .. autofunction:: lookup(code)

View File

@ -13,7 +13,7 @@ The module contains a few objects and function extending the minimum set of
functionalities defined by the |DBAPI|_. functionalities defined by the |DBAPI|_.
.. class:: connection .. class:: connection(dsn, async=False)
Is the class usually returned by the `~psycopg2.connect()` function. Is the class usually returned by the `~psycopg2.connect()` function.
It is exposed by the `extensions` module in order to allow 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. `!connect()` function using the `connection_factory` parameter.
See also :ref:`subclassing-connection`. See also :ref:`subclassing-connection`.
Subclasses should have constructor signature :samp:`({dsn}, {async}=0)`.
For a complete description of the class, see `connection`. 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()` It is the class usually returnded by the `connection.cursor()`
method. It is exposed by the `extensions` module in order to allow 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) .. 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) .. autofunction:: set_wait_callback(f)
.. versionadded:: 2.2.0 .. versionadded:: 2.2.0

View File

@ -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. classes until a better place in the distribution is found.
.. index:: .. _cursor-subclasses:
pair: Cursor; Dictionary
.. _dict-cursor:
Connection and cursor subclasses Connection and cursor subclasses
-------------------------------- --------------------------------
A few objects that change the way the results are returned by the cursor or 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` modify the object behavior in some other way. Typically `!cursor` subclasses
subclasses are passed as *connection_factory* argument to are passed as *cursor_factory* argument to `~psycopg2.connect()` so that the
`~psycopg2.connect()` so that the connection will generate the matching connection's `~connection.cursor()` method will generate objects of this
`!cursor` subclass. Alternatively a `!cursor` subclass can be used one-off by class. Alternatively a `!cursor` subclass can be used one-off by passing it
passing it as the *cursor_factory* argument to the `~connection.cursor()` as the *cursor_factory* argument to the `!cursor()` method.
method of a regular `!connection`.
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 Dictionary-like cursor
^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^
@ -61,6 +66,11 @@ The records still support indexing as the original tuple:
.. autoclass:: DictConnection .. autoclass:: DictConnection
.. note::
Not very useful since Psycopg 2.5: you can use `psycopg2.connect`\
``(dsn, cursor_factory=DictCursor)`` instead of `!DictConnection`.
.. autoclass:: DictRow .. autoclass:: DictRow
@ -71,6 +81,12 @@ Real dictionary cursor
.. autoclass:: RealDictConnection .. autoclass:: RealDictConnection
.. note::
Not very useful since Psycopg 2.5: you can use `psycopg2.connect`\
``(dsn, cursor_factory=RealDictCursor)`` instead of
`!RealDictConnection`.
.. autoclass:: RealDictRow .. autoclass:: RealDictRow
@ -101,6 +117,12 @@ expect it to be... ::
.. autoclass:: NamedTupleConnection .. autoclass:: NamedTupleConnection
.. note::
Not very useful since Psycopg 2.5: you can use `psycopg2.connect`\
``(dsn, cursor_factory=NamedTupleCursor)`` instead of
`!NamedTupleConnection`.
.. index:: .. index::
pair: Cursor; Logging 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:: .. index::
pair: hstore; Data types pair: hstore; Data types
pair: dict; Adaptation pair: dict; Adaptation
.. _adapt-hstore:
Hstore data type Hstore data type
^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^
@ -141,7 +251,7 @@ Hstore data type
The |hstore|_ data type is a key-value store embedded in PostgreSQL. It has 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 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 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. 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:: .. index::
pair: Composite types; Data types pair: Composite types; Data types
pair: tuple; Adaptation pair: tuple; Adaptation
pair: namedtuple; Adaptation pair: namedtuple; Adaptation
.. _adapt-composite:
Composite types casting 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] >>> cur.fetchone()[0]
card(value=8, suit='hearts') card(value=8, suit='hearts')
Nested composite types are handled as expected, but the type of the composite Nested composite types are handled as expected, provided that the type of the
components must be registered as well. composite components are registered as well.
.. doctest:: .. doctest::
@ -214,15 +324,167 @@ components must be registered as well.
Adaptation from Python tuples to composite types is automatic instead and Adaptation from Python tuples to composite types is automatic instead and
requires no adapter registration. 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 .. autofunction:: register_composite
.. versionchanged:: 2.4.3
added support for array of composite types
.. versionchanged:: 2.5
added the *factory* parameter
.. autoclass:: CompositeCaster .. 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:: .. index::
pair: UUID; Data types pair: UUID; Data types
.. _adapt-uuid:
UUID data type UUID data type
^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^

View File

@ -7,6 +7,8 @@ Here are a few gotchas you may encounter using `psycopg2`. Feel free to
suggest new entries! suggest new entries!
.. _faq-transactions:
Problems with transactions handling Problems with transactions handling
----------------------------------- -----------------------------------
@ -51,6 +53,8 @@ Why do I get the error *current transaction is aborted, commands ignored until e
informations. informations.
.. _faq-types:
Problems with type conversions 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. provided in the `~psycopg2.extensions.new_array_type()` documentation.
.. _faq-best-practices:
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. little memory on the client and to skip or discard parts of the result set.
.. _faq-compile:
Problems compiling and deploying psycopg2 Problems compiling and deploying psycopg2
----------------------------------------- -----------------------------------------

View File

@ -4,12 +4,12 @@ Psycopg -- PostgreSQL database adapter for Python
.. sectionauthor:: Daniele Varrazzo <daniele.varrazzo@gmail.com> .. sectionauthor:: Daniele Varrazzo <daniele.varrazzo@gmail.com>
Psycopg_ is a PostgreSQL_ database adapter for the Python_ programming Psycopg_ is the most popular PostgreSQL_ database adapter for the Python_
language. Its main features are that it supports the full Python |DBAPI|_ programming language. Its main features are the complete implementation of
and it is thread safe (threads can share the connections). It was designed for the Python |DBAPI|_ specification and the thread safety (several threads can
heavily multi-threaded applications that create and destroy lots of cursors and share the same connection). It was designed for heavily multi-threaded
make a large number of concurrent :sql:`INSERT`\ s or :sql:`UPDATE`\ s. applications that create and destroy lots of cursors and make a large number
The Psycopg distribution includes ZPsycopgDA, a Zope_ Database Adapter. of concurrent :sql:`INSERT`\s or :sql:`UPDATE`\s.
Psycopg 2 is mostly implemented in C as a libpq_ wrapper, resulting in being 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 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 support, and a flexible :ref:`objects adaptation system
<python-types-adaptation>`. Many basic Python types are supported <python-types-adaptation>`. Many basic Python types are supported
out-of-the-box and mapped to matching PostgreSQL data types, such as strings 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 (both byte strings and Unicode), numbers (ints, longs, floats, decimals),
datetime objects (both built-in and `mx.DateTime`_), several types of booleans and date/time objects (both built-in and `mx.DateTime`_), several
:ref:`binary objects <adapt-binary>`. Also available are mappings between lists types of :ref:`binary objects <adapt-binary>`. Also available are mappings
and PostgreSQL arrays of any supported type, between :ref:`dictionaries and between lists and PostgreSQL arrays of any supported type, between
PostgreSQL hstores <adapt-hstore>`, and between :ref:`tuples/namedtuples and :ref:`dictionaries and PostgreSQL hstore <adapt-hstore>`, between
PostgreSQL composite types <adapt-composite>`. :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. 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/ .. _Psycopg: http://initd.org/psycopg/
.. _PostgreSQL: http://www.postgresql.org/ .. _PostgreSQL: http://www.postgresql.org/
.. _Python: http://www.python.org/ .. _Python: http://www.python.org/
.. _Zope: http://www.zope.org/
.. _libpq: http://www.postgresql.org/docs/current/static/libpq.html .. _libpq: http://www.postgresql.org/docs/current/static/libpq.html
.. |COPY-TO-FROM| replace:: :sql:`COPY TO/COPY FROM` .. |COPY-TO-FROM| replace:: :sql:`COPY TO/COPY FROM`
.. __: http://www.postgresql.org/docs/current/static/sql-copy.html .. __: http://www.postgresql.org/docs/current/static/sql-copy.html
@ -42,6 +42,7 @@ Psycopg 2 is both Unicode and Python 3 friendly.
.. toctree:: .. toctree::
:maxdepth: 2 :maxdepth: 2
install
usage usage
module module
connection connection
@ -53,6 +54,7 @@ Psycopg 2 is both Unicode and Python 3 friendly.
extras extras
errorcodes errorcodes
faq faq
news
.. ifconfig:: builder != 'text' .. ifconfig:: builder != 'text'

258
doc/src/install.rst Normal file
View File

@ -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

View File

@ -16,11 +16,14 @@ The module interface respects the standard defined in the |DBAPI|_.
single: Port; Connection single: Port; Connection
single: DSN (Database Source Name) 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. 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") 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") 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: 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) - `!database` -- the database name (only as keyword argument)
- `!user` -- user name used to authenticate - `!user` -- user name used to authenticate
- `!password` -- password 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) - `!port` -- connection port number (defaults to 5432 if not provided)
Any other connection parameter supported by the client library/server can Any other connection parameter supported by the client library/server can
be passed either in the connection string or as keyword. See the be passed either in the connection string or as keywords. The PostgreSQL
PostgreSQL documentation for a complete `list of supported parameters`__. documentation contains the complete list of the `supported parameters`__.
Also note that the same parameters can be passed to the client library Also note that the same parameters can be passed to the client library
using `environment variables`__. 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 Using the *connection_factory* parameter a different class or
connections factory can be specified. It should be a callable object connections factory can be specified. It should be a callable object
taking a *dsn* argument. See :ref:`subclassing-connection` for taking a *dsn* string argument. See :ref:`subclassing-connection` for
details. 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. :ref:`async-support` to know about advantages and limitations.
.. versionchanged:: 2.4.3 .. versionchanged:: 2.4.3
any keyword argument is passed to the connection. Previously only the any keyword argument is passed to the connection. Previously only the
basic parameters (plus `!sslmode`) were supported as keywords. 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:: .. extension::
The parameters *connection_factory* and *async* are Psycopg extensions 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. 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:: .. extension::
The `~Error.pgerror`, `~Error.pgcode`, and `~Error.cursor` attributes The `~Error.pgerror`, `~Error.pgcode`, `~Error.cursor`, and
are Psycopg extensions. `~Error.diag` attributes are Psycopg extensions.
.. exception:: InterfaceError .. exception:: InterfaceError
@ -294,6 +339,12 @@ The module exports the following constructors and singletons:
This function constructs an object capable of holding a binary (long) This function constructs an object capable of holding a binary (long)
string value. 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 .. data:: STRING

4
doc/src/news.rst Normal file
View File

@ -0,0 +1,4 @@
Release notes
=============
.. include:: ../../NEWS

View File

@ -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)

View File

@ -1,3 +1,5 @@
.. _usage:
Basic module 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 - create new `cursor`\s using the `~connection.cursor()` method to
execute database commands and queries, execute database commands and queries,
- terminate the session using the methods `~connection.commit()` or - terminate transactions using the methods `~connection.commit()` or
`~connection.rollback()`. `~connection.rollback()`.
- The class `cursor` allows interaction with the database: - 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 Many standard Python types are adapted into SQL and returned as Python
objects when a query is executed. objects when a query is executed.
If you need to convert other Python types to and from PostgreSQL data types, The following table shows the default mapping between Python and PostgreSQL
see :ref:`adapting-new-types` and :ref:`type-casting-from-sql-to-python`. You types:
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. 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:: .. index::
pair: None; Adaptation pair: None; Adaptation
single: NULL; Adaptation single: NULL; Adaptation
pair: Boolean; Adaptation pair: Boolean; Adaptation
- Python `None` and boolean values `True` and `False` are converted into the .. _adapt-consts:
Constants adaptation
^^^^^^^^^^^^^^^^^^^^
Python `None` and boolean values `True` and `False` are converted into the
proper SQL literals:: proper SQL literals::
>>> cur.mogrify("SELECT %s, %s, %s;", (None, True, False)) >>> cur.mogrify("SELECT %s, %s, %s;", (None, True, False))
>>> 'SELECT NULL, true, false;' 'SELECT NULL, true, false;'
.. _adapt-numbers:
.. index:: .. index::
single: Adaptation; numbers single: Adaptation; numbers
@ -231,168 +293,48 @@ the SQL string that would be sent to the database.
single: Float; Adaptation single: Float; Adaptation
single: Decimal; Adaptation single: Decimal; Adaptation
- Numeric objects: `int`, `long`, `float`, `~decimal.Decimal` are converted in .. _adapt-numbers:
Numbers adaptation
^^^^^^^^^^^^^^^^^^
Numeric objects: `int`, `long`, `float`, `~decimal.Decimal` are converted in
the PostgreSQL numerical representation:: the PostgreSQL numerical representation::
>>> cur.mogrify("SELECT %s, %s, %s, %s;", (10, 10L, 10.0, Decimal("10.00"))) >>> cur.mogrify("SELECT %s, %s, %s, %s;", (10, 10L, 10.0, Decimal("10.00")))
>>> 'SELECT 10, 10, 10.0, 10.00;' 'SELECT 10, 10, 10.0, 10.00;'
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`.
.. note::
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.
.. seealso:: `PostgreSQL numeric types
<http://www.postgresql.org/docs/current/static/datatype-numeric.html>`__
.. _adapt-string:
.. index:: .. index::
pair: Strings; Adaptation pair: Strings; Adaptation
single: Unicode; Adaptation single: Unicode; Adaptation
- String types: `str`, `unicode` are converted in SQL string syntax. .. _adapt-string:
Strings adaptation
^^^^^^^^^^^^^^^^^^
Python `str` and `unicode` are converted into the SQL string syntax.
`!unicode` objects (`!str` in Python 3) are encoded in the connection `!unicode` objects (`!str` in Python 3) are encoded in the connection
`~connection.encoding` to be sent to the backend: trying to send a character `~connection.encoding` before sending to the backend: trying to send a
not supported by the encoding will result in an error. Received data can be character not supported by the encoding will result in an error. Data is
converted either as `!str` or `!unicode`: see :ref:`unicode-handling`. 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:
.. _adapt-binary: see :ref:`unicode-handling`.
.. 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::
>>> cur.mogrify("SELECT %s IN %s;", (10, (10, 20, 30)))
'SELECT 10 IN (10, 20, 30);'
.. note::
SQL doesn't allow an empty list in the IN operator, so your code should
guard against empty tuples.
If you want PostgreSQL composite types to be converted into a Python
tuple/namedtuple you can use the `~psycopg2.extras.register_composite()`
function.
.. versionadded:: 2.0.6
the tuple :sql:`IN` adaptation.
.. versionchanged:: 2.0.14
the tuple :sql:`IN` adapter is always active. In previous releases it
was necessary to import the `~psycopg2.extensions` module to have it
registered.
.. versionchanged:: 2.3
`~collections.namedtuple` instances are adapted like regular tuples and
can thus be used to represent composite types.
.. _adapt-dict:
.. index::
single: dict; Adaptation
single: hstore; Adaptation
- Python dictionaries are converted into the |hstore|_ data type. By default
the adapter is not enabled: see `~psycopg2.extras.register_hstore()` for
further details.
.. |hstore| replace:: :sql:`hstore`
.. _hstore: http://www.postgresql.org/docs/current/static/hstore.html
.. versionadded:: 2.3
the :sql:`hstore` adaptation.
.. index:: .. index::
@ -401,7 +343,7 @@ the SQL string that would be sent to the database.
.. _unicode-handling: .. _unicode-handling:
Unicode handling Unicode handling
^^^^^^^^^^^^^^^^ ''''''''''''''''
Psycopg can exchange Unicode data with a PostgreSQL database. Python Psycopg can exchange Unicode data with a PostgreSQL database. Python
`!unicode` objects are automatically *encoded* in the client encoding `!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.UNICODE)
psycopg2.extensions.register_type(psycopg2.extensions.UNICODEARRAY) 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:: .. index::
single: Time Zones single: Time Zones
.. _tz-handling: .. _tz-handling:
Time zones handling Time zones handling
^^^^^^^^^^^^^^^^^^^ '''''''''''''''''''
The PostgreSQL type :sql:`timestamp with time zone` is converted into Python The PostgreSQL type :sql:`timestamp with time zone` (a.k.a.
`~datetime.datetime` objects with a `~datetime.datetime.tzinfo` attribute set :sql:`timestamptz`) is converted into Python `~datetime.datetime` objects with
to a `~psycopg2.tz.FixedOffsetTimezone` instance. a `~datetime.datetime.tzinfo` attribute set to a
`~psycopg2.tz.FixedOffsetTimezone` instance.
>>> cur.execute("SET TIME ZONE 'Europe/Rome';") # UTC + 1 hour >>> cur.execute("SET TIME ZONE 'Europe/Rome';") # UTC + 1 hour
>>> cur.execute("SELECT '2010-01-01 10:30:45'::timestamptz;") >>> 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()`. 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 .. index:: Transaction, Begin, Commit, Rollback, Autocommit, Read only
.. _transactions-control: .. _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 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 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 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 `~connection.autocommit` property (`~connection.set_isolation_level()` in
older versions). older versions).
@ -546,6 +651,30 @@ change the isolation level. See the `~connection.set_session()` method for all
the details. 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:: .. index::
pair: Server side; Cursor pair: Server side; Cursor
pair: Named; Cursor pair: Named; Cursor
@ -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, *name* parameter. Such cursor will behave mostly like a regular cursor,
allowing the user to move in the dataset using the `~cursor.scroll()` allowing the user to move in the dataset using the `~cursor.scroll()`
method and to read the data using `~cursor.fetchone()` and 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. Named cursors are also :ref:`iterable <cursor-iterable>` like regular cursors.
Note however that before Psycopg 2.4 iteration was performed fetching one Note however that before Psycopg 2.4 iteration was performed fetching one

View File

@ -41,23 +41,6 @@ Homepage: http://initd.org/projects/psycopg2
# Import modules needed by _psycopg to allow tools like py2exe to do # Import modules needed by _psycopg to allow tools like py2exe to do
# their work without bothering about the module dependencies. # 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 # Note: the first internal import should be _psycopg, otherwise the real cause
# of a failed loading of the C module may get hidden, see # of a failed loading of the C module may get hidden, see
# http://archives.postgresql.org/psycopg/2011-02/msg00044.php # http://archives.postgresql.org/psycopg/2011-02/msg00044.php
@ -118,7 +101,7 @@ del re
def connect(dsn=None, def connect(dsn=None,
database=None, user=None, password=None, host=None, port=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. 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 factory can be specified. It should be a callable object taking a dsn
argument. 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. Using *async*=True an asynchronous connection will be created.
Any other keyword parameter will be passed to the underlying client 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))) dsn = " ".join(["%s=%s" % (k, _param_escape(str(v)))
for (k, v) in items]) for (k, v) in items])
return _connect(dsn, connection_factory=connection_factory, async=async) conn = _connect(dsn, connection_factory=connection_factory, async=async)
if cursor_factory is not None:
conn.cursor_factory = cursor_factory
__all__ = filter(lambda k: not k.startswith('_'), locals().keys())
return conn

194
lib/_json.py Normal file
View File

@ -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

468
lib/_range.py Normal file
View File

@ -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()

View File

@ -59,6 +59,7 @@ CLASS_INVALID_TRANSACTION_INITIATION = '0B'
CLASS_LOCATOR_EXCEPTION = '0F' CLASS_LOCATOR_EXCEPTION = '0F'
CLASS_INVALID_GRANTOR = '0L' CLASS_INVALID_GRANTOR = '0L'
CLASS_INVALID_ROLE_SPECIFICATION = '0P' CLASS_INVALID_ROLE_SPECIFICATION = '0P'
CLASS_DIAGNOSTICS_EXCEPTION = '0Z'
CLASS_CASE_NOT_FOUND = '20' CLASS_CASE_NOT_FOUND = '20'
CLASS_CARDINALITY_VIOLATION = '21' CLASS_CARDINALITY_VIOLATION = '21'
CLASS_DATA_EXCEPTION = '22' CLASS_DATA_EXCEPTION = '22'
@ -139,6 +140,10 @@ INVALID_GRANT_OPERATION = '0LP01'
# Class 0P - Invalid Role Specification # Class 0P - Invalid Role Specification
INVALID_ROLE_SPECIFICATION = '0P000' INVALID_ROLE_SPECIFICATION = '0P000'
# Class 0Z - Diagnostics Exception
DIAGNOSTICS_EXCEPTION = '0Z000'
STACKED_DIAGNOSTICS_ACCESSED_WITHOUT_ACTIVE_HANDLER = '0Z002'
# Class 20 - Case Not Found # Class 20 - Case Not Found
CASE_NOT_FOUND = '20000' CASE_NOT_FOUND = '20000'
@ -331,6 +336,7 @@ INSUFFICIENT_RESOURCES = '53000'
DISK_FULL = '53100' DISK_FULL = '53100'
OUT_OF_MEMORY = '53200' OUT_OF_MEMORY = '53200'
TOO_MANY_CONNECTIONS = '53300' TOO_MANY_CONNECTIONS = '53300'
CONFIGURATION_LIMIT_EXCEEDED = '53400'
# Class 54 - Program Limit Exceeded # Class 54 - Program Limit Exceeded
PROGRAM_LIMIT_EXCEEDED = '54000' PROGRAM_LIMIT_EXCEEDED = '54000'
@ -353,6 +359,7 @@ CANNOT_CONNECT_NOW = '57P03'
DATABASE_DROPPED = '57P04' DATABASE_DROPPED = '57P04'
# Class 58 - System Error (errors external to PostgreSQL itself) # Class 58 - System Error (errors external to PostgreSQL itself)
SYSTEM_ERROR = '58000'
IO_ERROR = '58030' IO_ERROR = '58030'
UNDEFINED_FILE = '58P01' UNDEFINED_FILE = '58P01'
DUPLICATE_FILE = '58P02' DUPLICATE_FILE = '58P02'

View File

@ -58,7 +58,7 @@ except ImportError:
from psycopg2._psycopg import adapt, adapters, encodings, connection, cursor, lobject, Xid 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 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 from psycopg2._psycopg import QueryCanceledError, TransactionRollbackError
@ -151,6 +151,21 @@ class NoneAdapter(object):
return _null 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. # Add the "cleaned" version of the encodings to the key.
# When the encoding is set its name is cleaned up from - and _ and turned # 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 # 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 encodings[k] = v
del k, v del k, v
__all__ = filter(lambda k: not k.startswith('_'), locals().keys())

View File

@ -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 # FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
# License for more details. # License for more details.
import os import os as _os
import sys import sys as _sys
import time import time as _time
import warnings import re as _re
import re as regex
try: try:
import logging import logging as _logging
except: except:
logging = None _logging = None
import psycopg2 import psycopg2
from psycopg2 import extensions as _ext from psycopg2 import extensions as _ext
@ -192,7 +191,7 @@ class DictRow(list):
self._index = data[1] self._index = data[1]
# drop the crusty Py2 methods # drop the crusty Py2 methods
if sys.version_info[0] > 2: if _sys.version_info[0] > 2:
items = iteritems; del iteritems items = iteritems; del iteritems
keys = iterkeys; del iterkeys keys = iterkeys; del iterkeys
values = itervalues; del itervalues values = itervalues; del itervalues
@ -302,21 +301,21 @@ class NamedTupleCursor(_cursor):
nt = self.Record nt = self.Record
if nt is None: if nt is None:
nt = self.Record = self._make_nt() nt = self.Record = self._make_nt()
return nt(*t) return nt._make(t)
def fetchmany(self, size=None): def fetchmany(self, size=None):
ts = super(NamedTupleCursor, self).fetchmany(size) ts = super(NamedTupleCursor, self).fetchmany(size)
nt = self.Record nt = self.Record
if nt is None: if nt is None:
nt = self.Record = self._make_nt() nt = self.Record = self._make_nt()
return [nt(*t) for t in ts] return map(nt._make, ts)
def fetchall(self): def fetchall(self):
ts = super(NamedTupleCursor, self).fetchall() ts = super(NamedTupleCursor, self).fetchall()
nt = self.Record nt = self.Record
if nt is None: if nt is None:
nt = self.Record = self._make_nt() nt = self.Record = self._make_nt()
return [nt(*t) for t in ts] return map(nt._make, ts)
def __iter__(self): def __iter__(self):
it = super(NamedTupleCursor, self).__iter__() it = super(NamedTupleCursor, self).__iter__()
@ -326,10 +325,10 @@ class NamedTupleCursor(_cursor):
if nt is None: if nt is None:
nt = self.Record = self._make_nt() nt = self.Record = self._make_nt()
yield nt(*t) yield nt._make(t)
while 1: while 1:
yield nt(*it.next()) yield nt._make(it.next())
try: try:
from collections import namedtuple from collections import namedtuple
@ -354,7 +353,7 @@ class LoggingConnection(_connection):
instance from the standard logging module. instance from the standard logging module.
""" """
self._logobj = logobj self._logobj = logobj
if logging and isinstance(logobj, logging.Logger): if _logging and isinstance(logobj, _logging.Logger):
self.log = self._logtologger self.log = self._logtologger
else: else:
self.log = self._logtofile self.log = self._logtofile
@ -370,7 +369,7 @@ class LoggingConnection(_connection):
def _logtofile(self, msg, curs): def _logtofile(self, msg, curs):
msg = self.filter(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): def _logtologger(self, msg, curs):
msg = self.filter(msg, curs) msg = self.filter(msg, curs)
@ -418,9 +417,9 @@ class MinTimeLoggingConnection(LoggingConnection):
self._mintime = mintime self._mintime = mintime
def filter(self, msg, curs): def filter(self, msg, curs):
t = (time.time() - curs.timestamp) * 1000 t = (_time.time() - curs.timestamp) * 1000
if t > self._mintime: 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): def cursor(self, *args, **kwargs):
kwargs.setdefault('cursor_factory', MinTimeLoggingCursor) kwargs.setdefault('cursor_factory', MinTimeLoggingCursor)
@ -430,11 +429,11 @@ class MinTimeLoggingCursor(LoggingCursor):
"""The cursor sub-class companion to `MinTimeLoggingConnection`.""" """The cursor sub-class companion to `MinTimeLoggingConnection`."""
def execute(self, query, vars=None): def execute(self, query, vars=None):
self.timestamp = time.time() self.timestamp = _time.time()
return LoggingCursor.execute(self, query, vars) return LoggingCursor.execute(self, query, vars)
def callproc(self, procname, vars=None): def callproc(self, procname, vars=None):
self.timestamp = time.time() self.timestamp = _time.time()
return LoggingCursor.execute(self, procname, vars) 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 These are now correctly handled by the default type caster, so currently
the function doesn't do anything. the function doesn't do anything.
""" """
import warnings
warnings.warn("deprecated", DeprecationWarning) warnings.warn("deprecated", DeprecationWarning)
import select
from psycopg2.extensions import POLL_OK, POLL_READ, POLL_WRITE
from psycopg2 import OperationalError
def wait_select(conn): def wait_select(conn):
"""Wait until a connection or cursor has data available. """Wait until a connection or cursor has data available.
The function is an example of a wait callback to be registered with The function is an example of a wait callback to be registered with
`~psycopg2.extensions.set_wait_callback()`. This function uses `!select()` `~psycopg2.extensions.set_wait_callback()`. This function uses
to wait for data available. :py:func:`~select.select()` to wait for data available.
""" """
import select
from psycopg2.extensions import POLL_OK, POLL_READ, POLL_WRITE
while 1: while 1:
state = conn.poll() state = conn.poll()
if state == POLL_OK: if state == POLL_OK:
@ -581,11 +581,14 @@ def wait_select(conn):
elif state == POLL_WRITE: elif state == POLL_WRITE:
select.select([], [conn.fileno()], []) select.select([], [conn.fileno()], [])
else: 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): def _solve_conn_curs(conn_or_curs):
"""Return the connection and a DBAPI cursor from a connection or cursor.""" """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'): if hasattr(conn_or_curs, 'execute'):
conn = conn_or_curs.connection conn = conn_or_curs.connection
curs = conn.cursor(cursor_factory=_cursor) curs = conn.cursor(cursor_factory=_cursor)
@ -645,7 +648,7 @@ class HstoreAdapter(object):
getquoted = _getquoted_9 getquoted = _getquoted_9
_re_hstore = regex.compile(r""" _re_hstore = _re.compile(r"""
# hstore key: # hstore key:
# a string of normal or escaped chars # a string of normal or escaped chars
"((?: [^"\\] | \\. )*)" "((?: [^"\\] | \\. )*)"
@ -656,10 +659,10 @@ class HstoreAdapter(object):
| "((?: [^"\\] | \\. )*)" | "((?: [^"\\] | \\. )*)"
) )
(?:\s*,\s*|$) # pairs separated by comma or end of string. (?:\s*,\s*|$) # pairs separated by comma or end of string.
""", regex.VERBOSE) """, _re.VERBOSE)
@classmethod @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. """Parse an hstore representation in a Python string.
The hstore is represented as something like:: 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]) array_oid = tuple([x for x in array_oid if x])
# create and register the typecaster # 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 cast = HstoreAdapter.parse_unicode
else: else:
cast = HstoreAdapter.parse cast = HstoreAdapter.parse
@ -805,35 +808,10 @@ class CompositeCaster(object):
querying the database at registration time is not desirable (such as when querying the database at registration time is not desirable (such as when
using an :ref:`asynchronous connections <async-support>`). 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.name = name
self.schema = schema
self.oid = oid self.oid = oid
self.array_oid = array_oid self.array_oid = array_oid
@ -857,17 +835,30 @@ class CompositeCaster(object):
"expecting %d components for the type %s, %d found instead" % "expecting %d components for the type %s, %d found instead" %
(len(self.atttypes), self.name, len(tokens))) (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) ] 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 \(? ([,)]) # an empty token, representing NULL
| \(? " ((?: [^"] | "")*) " [,)] # or a quoted string | \(? " ((?: [^"] | "")*) " [,)] # or a quoted string
| \(? ([^",)]+) [,)] # or an unquoted string | \(? ([^",)]+) [,)] # or an unquoted string
""", regex.VERBOSE) """, _re.VERBOSE)
_re_undouble = regex.compile(r'(["\\])\1') _re_undouble = _re.compile(r'(["\\])\1')
@classmethod @classmethod
def tokenize(self, s): def tokenize(self, s):
@ -889,10 +880,10 @@ class CompositeCaster(object):
from collections import namedtuple from collections import namedtuple
except ImportError: except ImportError:
self.type = tuple self.type = tuple
self._ctor = lambda *args: tuple(args) self._ctor = self.type
else: else:
self.type = namedtuple(name, attnames) self.type = namedtuple(name, attnames)
self._ctor = self.type self._ctor = self.type._make
@classmethod @classmethod
def _from_db(self, name, conn_or_curs): def _from_db(self, name, conn_or_curs):
@ -941,10 +932,10 @@ ORDER BY attnum;
array_oid = recs[0][1] array_oid = recs[0][1]
type_attrs = [ (r[2], r[3]) for r in recs ] type_attrs = [ (r[2], r[3]) for r in recs ]
return CompositeCaster(tname, type_oid, type_attrs, return self(tname, type_oid, type_attrs,
array_oid=array_oid) 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. """Register a typecaster to convert a composite type into a tuple.
:param name: the name of a PostgreSQL composite type, e.g. created using :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` object, unless *globally* is set to `!True`
:param globally: if `!False` (default) register the typecaster only on :param globally: if `!False` (default) register the typecaster only on
*conn_or_curs*, otherwise register it globally *conn_or_curs*, otherwise register it globally
:return: the registered `CompositeCaster` instance responsible for the :param factory: if specified it should be a `CompositeCaster` subclass: use
conversion it to :ref:`customize how to cast composite types <custom-composite>`
:return: the registered `CompositeCaster` or *factory* instance
.. versionchanged:: 2.4.3 responsible for the conversion
added support for array of composite types
""" """
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) _ext.register_type(caster.typecaster, not globally and conn_or_curs or None)
if caster.array_typecaster is not None: if caster.array_typecaster is not None:
@ -970,4 +962,11 @@ def register_composite(name, conn_or_curs, globally=False):
return caster 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

View File

@ -27,30 +27,6 @@ This module implements thread-safe (and not) connection pools.
import psycopg2 import psycopg2
import psycopg2.extensions as _ext 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): class PoolError(psycopg2.Error):
pass pass

View File

@ -117,13 +117,6 @@ asis_setup(asisObject *self, PyObject *obj)
return 0; return 0;
} }
static int
asis_traverse(asisObject *self, visitproc visit, void *arg)
{
Py_VISIT(self->wrapped);
return 0;
}
static void static void
asis_dealloc(PyObject* obj) asis_dealloc(PyObject* obj)
{ {
@ -156,12 +149,6 @@ asis_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
return type->tp_alloc(type, 0); return type->tp_alloc(type, 0);
} }
static void
asis_del(PyObject* self)
{
PyObject_GC_Del(self);
}
static PyObject * static PyObject *
asis_repr(asisObject *self) asis_repr(asisObject *self)
{ {
@ -177,63 +164,41 @@ asis_repr(asisObject *self)
PyTypeObject asisType = { PyTypeObject asisType = {
PyVarObject_HEAD_INIT(NULL, 0) PyVarObject_HEAD_INIT(NULL, 0)
"psycopg2._psycopg.AsIs", "psycopg2._psycopg.AsIs",
sizeof(asisObject), sizeof(asisObject), 0,
0,
asis_dealloc, /*tp_dealloc*/ asis_dealloc, /*tp_dealloc*/
0, /*tp_print*/ 0, /*tp_print*/
0, /*tp_getattr*/ 0, /*tp_getattr*/
0, /*tp_setattr*/ 0, /*tp_setattr*/
0, /*tp_compare*/ 0, /*tp_compare*/
(reprfunc)asis_repr, /*tp_repr*/ (reprfunc)asis_repr, /*tp_repr*/
0, /*tp_as_number*/ 0, /*tp_as_number*/
0, /*tp_as_sequence*/ 0, /*tp_as_sequence*/
0, /*tp_as_mapping*/ 0, /*tp_as_mapping*/
0, /*tp_hash */ 0, /*tp_hash */
0, /*tp_call*/ 0, /*tp_call*/
(reprfunc)asis_str, /*tp_str*/ (reprfunc)asis_str, /*tp_str*/
0, /*tp_getattro*/ 0, /*tp_getattro*/
0, /*tp_setattro*/ 0, /*tp_setattro*/
0, /*tp_as_buffer*/ 0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /*tp_flags*/
Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_HAVE_GC, /*tp_flags*/
asisType_doc, /*tp_doc*/ asisType_doc, /*tp_doc*/
0, /*tp_traverse*/
(traverseproc)asis_traverse, /*tp_traverse*/
0, /*tp_clear*/ 0, /*tp_clear*/
0, /*tp_richcompare*/ 0, /*tp_richcompare*/
0, /*tp_weaklistoffset*/ 0, /*tp_weaklistoffset*/
0, /*tp_iter*/ 0, /*tp_iter*/
0, /*tp_iternext*/ 0, /*tp_iternext*/
/* Attribute descriptor and subclassing stuff */
asisObject_methods, /*tp_methods*/ asisObject_methods, /*tp_methods*/
asisObject_members, /*tp_members*/ asisObject_members, /*tp_members*/
0, /*tp_getset*/ 0, /*tp_getset*/
0, /*tp_base*/ 0, /*tp_base*/
0, /*tp_dict*/ 0, /*tp_dict*/
0, /*tp_descr_get*/ 0, /*tp_descr_get*/
0, /*tp_descr_set*/ 0, /*tp_descr_set*/
0, /*tp_dictoffset*/ 0, /*tp_dictoffset*/
asis_init, /*tp_init*/ asis_init, /*tp_init*/
0, /*tp_alloc will be set to PyType_GenericAlloc in module init*/ 0, /*tp_alloc*/
asis_new, /*tp_new*/ 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*/
}; };

View File

@ -159,8 +159,7 @@ binary_prepare(binaryObject *self, PyObject *args)
self->conn = conn; self->conn = conn;
Py_INCREF(self->conn); Py_INCREF(self->conn);
Py_INCREF(Py_None); Py_RETURN_NONE;
return Py_None;
} }
static PyObject * static PyObject *
@ -221,17 +220,6 @@ binary_setup(binaryObject *self, PyObject *str)
return 0; 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 static void
binary_dealloc(PyObject* obj) binary_dealloc(PyObject* obj)
{ {
@ -266,12 +254,6 @@ binary_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
return type->tp_alloc(type, 0); return type->tp_alloc(type, 0);
} }
static void
binary_del(PyObject* self)
{
PyObject_GC_Del(self);
}
static PyObject * static PyObject *
binary_repr(binaryObject *self) binary_repr(binaryObject *self)
{ {
@ -286,61 +268,41 @@ binary_repr(binaryObject *self)
PyTypeObject binaryType = { PyTypeObject binaryType = {
PyVarObject_HEAD_INIT(NULL, 0) PyVarObject_HEAD_INIT(NULL, 0)
"psycopg2._psycopg.Binary", "psycopg2._psycopg.Binary",
sizeof(binaryObject), sizeof(binaryObject), 0,
0,
binary_dealloc, /*tp_dealloc*/ binary_dealloc, /*tp_dealloc*/
0, /*tp_print*/ 0, /*tp_print*/
0, /*tp_getattr*/ 0, /*tp_getattr*/
0, /*tp_setattr*/ 0, /*tp_setattr*/
0, /*tp_compare*/ 0, /*tp_compare*/
(reprfunc)binary_repr, /*tp_repr*/ (reprfunc)binary_repr, /*tp_repr*/
0, /*tp_as_number*/ 0, /*tp_as_number*/
0, /*tp_as_sequence*/ 0, /*tp_as_sequence*/
0, /*tp_as_mapping*/ 0, /*tp_as_mapping*/
0, /*tp_hash */ 0, /*tp_hash */
0, /*tp_call*/ 0, /*tp_call*/
(reprfunc)binary_str, /*tp_str*/ (reprfunc)binary_str, /*tp_str*/
0, /*tp_getattro*/ 0, /*tp_getattro*/
0, /*tp_setattro*/ 0, /*tp_setattro*/
0, /*tp_as_buffer*/ 0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /*tp_flags*/
Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_HAVE_GC, /*tp_flags*/
binaryType_doc, /*tp_doc*/ binaryType_doc, /*tp_doc*/
0, /*tp_traverse*/
binary_traverse, /*tp_traverse*/
0, /*tp_clear*/ 0, /*tp_clear*/
0, /*tp_richcompare*/ 0, /*tp_richcompare*/
0, /*tp_weaklistoffset*/ 0, /*tp_weaklistoffset*/
0, /*tp_iter*/ 0, /*tp_iter*/
0, /*tp_iternext*/ 0, /*tp_iternext*/
/* Attribute descriptor and subclassing stuff */
binaryObject_methods, /*tp_methods*/ binaryObject_methods, /*tp_methods*/
binaryObject_members, /*tp_members*/ binaryObject_members, /*tp_members*/
0, /*tp_getset*/ 0, /*tp_getset*/
0, /*tp_base*/ 0, /*tp_base*/
0, /*tp_dict*/ 0, /*tp_dict*/
0, /*tp_descr_get*/ 0, /*tp_descr_get*/
0, /*tp_descr_set*/ 0, /*tp_descr_set*/
0, /*tp_dictoffset*/ 0, /*tp_dictoffset*/
binary_init, /*tp_init*/ binary_init, /*tp_init*/
0, /*tp_alloc will be set to PyType_GenericAlloc in module init*/ 0, /*tp_alloc*/
binary_new, /*tp_new*/ 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*/
}; };

View File

@ -183,15 +183,6 @@ pydatetime_setup(pydatetimeObject *self, PyObject *obj, int type)
return 0; return 0;
} }
static int
pydatetime_traverse(PyObject *obj, visitproc visit, void *arg)
{
pydatetimeObject *self = (pydatetimeObject *)obj;
Py_VISIT(self->wrapped);
return 0;
}
static void static void
pydatetime_dealloc(PyObject* obj) pydatetime_dealloc(PyObject* obj)
{ {
@ -223,12 +214,6 @@ pydatetime_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
return type->tp_alloc(type, 0); return type->tp_alloc(type, 0);
} }
static void
pydatetime_del(PyObject* self)
{
PyObject_GC_Del(self);
}
static PyObject * static PyObject *
pydatetime_repr(pydatetimeObject *self) pydatetime_repr(pydatetimeObject *self)
{ {
@ -244,61 +229,41 @@ pydatetime_repr(pydatetimeObject *self)
PyTypeObject pydatetimeType = { PyTypeObject pydatetimeType = {
PyVarObject_HEAD_INIT(NULL, 0) PyVarObject_HEAD_INIT(NULL, 0)
"psycopg2._psycopg.datetime", "psycopg2._psycopg.datetime",
sizeof(pydatetimeObject), sizeof(pydatetimeObject), 0,
0,
pydatetime_dealloc, /*tp_dealloc*/ pydatetime_dealloc, /*tp_dealloc*/
0, /*tp_print*/ 0, /*tp_print*/
0, /*tp_getattr*/ 0, /*tp_getattr*/
0, /*tp_setattr*/ 0, /*tp_setattr*/
0, /*tp_compare*/ 0, /*tp_compare*/
(reprfunc)pydatetime_repr, /*tp_repr*/ (reprfunc)pydatetime_repr, /*tp_repr*/
0, /*tp_as_number*/ 0, /*tp_as_number*/
0, /*tp_as_sequence*/ 0, /*tp_as_sequence*/
0, /*tp_as_mapping*/ 0, /*tp_as_mapping*/
0, /*tp_hash */ 0, /*tp_hash */
0, /*tp_call*/ 0, /*tp_call*/
(reprfunc)pydatetime_str, /*tp_str*/ (reprfunc)pydatetime_str, /*tp_str*/
0, /*tp_getattro*/ 0, /*tp_getattro*/
0, /*tp_setattro*/ 0, /*tp_setattro*/
0, /*tp_as_buffer*/ 0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /*tp_flags*/
Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_HAVE_GC, /*tp_flags*/
pydatetimeType_doc, /*tp_doc*/ pydatetimeType_doc, /*tp_doc*/
0, /*tp_traverse*/
pydatetime_traverse, /*tp_traverse*/
0, /*tp_clear*/ 0, /*tp_clear*/
0, /*tp_richcompare*/ 0, /*tp_richcompare*/
0, /*tp_weaklistoffset*/ 0, /*tp_weaklistoffset*/
0, /*tp_iter*/ 0, /*tp_iter*/
0, /*tp_iternext*/ 0, /*tp_iternext*/
/* Attribute descriptor and subclassing stuff */
pydatetimeObject_methods, /*tp_methods*/ pydatetimeObject_methods, /*tp_methods*/
pydatetimeObject_members, /*tp_members*/ pydatetimeObject_members, /*tp_members*/
0, /*tp_getset*/ 0, /*tp_getset*/
0, /*tp_base*/ 0, /*tp_base*/
0, /*tp_dict*/ 0, /*tp_dict*/
0, /*tp_descr_get*/ 0, /*tp_descr_get*/
0, /*tp_descr_set*/ 0, /*tp_descr_set*/
0, /*tp_dictoffset*/ 0, /*tp_dictoffset*/
pydatetime_init, /*tp_init*/ pydatetime_init, /*tp_init*/
0, /*tp_alloc will be set to PyType_GenericAlloc in module init*/ 0, /*tp_alloc*/
pydatetime_new, /*tp_new*/ 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*/
}; };

View File

@ -103,16 +103,11 @@ list_prepare(listObject *self, PyObject *args)
if (!PyArg_ParseTuple(args, "O!", &connectionType, &conn)) if (!PyArg_ParseTuple(args, "O!", &connectionType, &conn))
return NULL; 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_CLEAR(self->connection);
Py_INCREF(conn); Py_INCREF(conn);
self->connection = conn; self->connection = conn;
Py_INCREF(Py_None); Py_RETURN_NONE;
return Py_None;
} }
static PyObject * static PyObject *
@ -154,7 +149,7 @@ static PyMethodDef listObject_methods[] = {
/* initialization and finalization methods */ /* initialization and finalization methods */
static int 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 = " Dprintf("list_setup: init list object at %p, refcnt = "
FORMAT_CODE_PY_SSIZE_T, FORMAT_CODE_PY_SSIZE_T,
@ -164,9 +159,6 @@ list_setup(listObject *self, PyObject *obj, const char *enc)
if (!PyList_Check(obj)) if (!PyList_Check(obj))
return -1; return -1;
/* FIXME: remove this orrible strdup */
if (enc) self->encoding = strdup(enc);
self->connection = NULL; self->connection = NULL;
Py_INCREF(obj); Py_INCREF(obj);
self->wrapped = obj; self->wrapped = obj;
@ -179,40 +171,42 @@ list_setup(listObject *self, PyObject *obj, const char *enc)
} }
static int 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->wrapped);
Py_VISIT(self->connection); Py_VISIT(self->connection);
return 0; return 0;
} }
static void static int
list_dealloc(PyObject* obj) list_clear(listObject *self)
{ {
listObject *self = (listObject *)obj;
Py_CLEAR(self->wrapped); Py_CLEAR(self->wrapped);
Py_CLEAR(self->connection); 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, " 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 static int
list_init(PyObject *obj, PyObject *args, PyObject *kwds) list_init(PyObject *obj, PyObject *args, PyObject *kwds)
{ {
PyObject *l; 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 -1;
return list_setup((listObject *)obj, l, enc); return list_setup((listObject *)obj, l);
} }
static PyObject * static PyObject *
@ -221,12 +215,6 @@ list_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
return type->tp_alloc(type, 0); return type->tp_alloc(type, 0);
} }
static void
list_del(PyObject* self)
{
PyObject_GC_Del(self);
}
static PyObject * static PyObject *
list_repr(listObject *self) list_repr(listObject *self)
{ {
@ -241,61 +229,41 @@ list_repr(listObject *self)
PyTypeObject listType = { PyTypeObject listType = {
PyVarObject_HEAD_INIT(NULL, 0) PyVarObject_HEAD_INIT(NULL, 0)
"psycopg2._psycopg.List", "psycopg2._psycopg.List",
sizeof(listObject), sizeof(listObject), 0,
0, (destructor)list_dealloc, /*tp_dealloc*/
list_dealloc, /*tp_dealloc*/
0, /*tp_print*/ 0, /*tp_print*/
0, /*tp_getattr*/ 0, /*tp_getattr*/
0, /*tp_setattr*/ 0, /*tp_setattr*/
0, /*tp_compare*/ 0, /*tp_compare*/
(reprfunc)list_repr, /*tp_repr*/ (reprfunc)list_repr, /*tp_repr*/
0, /*tp_as_number*/ 0, /*tp_as_number*/
0, /*tp_as_sequence*/ 0, /*tp_as_sequence*/
0, /*tp_as_mapping*/ 0, /*tp_as_mapping*/
0, /*tp_hash */ 0, /*tp_hash */
0, /*tp_call*/ 0, /*tp_call*/
(reprfunc)list_str, /*tp_str*/ (reprfunc)list_str, /*tp_str*/
0, /*tp_getattro*/ 0, /*tp_getattro*/
0, /*tp_setattro*/ 0, /*tp_setattro*/
0, /*tp_as_buffer*/ 0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_HAVE_GC, /*tp_flags*/ Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_HAVE_GC, /*tp_flags*/
listType_doc, /*tp_doc*/ listType_doc, /*tp_doc*/
(traverseproc)list_traverse, /*tp_traverse*/
list_traverse, /*tp_traverse*/ (inquiry)list_clear, /*tp_clear*/
0, /*tp_clear*/
0, /*tp_richcompare*/ 0, /*tp_richcompare*/
0, /*tp_weaklistoffset*/ 0, /*tp_weaklistoffset*/
0, /*tp_iter*/ 0, /*tp_iter*/
0, /*tp_iternext*/ 0, /*tp_iternext*/
/* Attribute descriptor and subclassing stuff */
listObject_methods, /*tp_methods*/ listObject_methods, /*tp_methods*/
listObject_members, /*tp_members*/ listObject_members, /*tp_members*/
0, /*tp_getset*/ 0, /*tp_getset*/
0, /*tp_base*/ 0, /*tp_base*/
0, /*tp_dict*/ 0, /*tp_dict*/
0, /*tp_descr_get*/ 0, /*tp_descr_get*/
0, /*tp_descr_set*/ 0, /*tp_descr_set*/
0, /*tp_dictoffset*/ 0, /*tp_dictoffset*/
list_init, /*tp_init*/ list_init, /*tp_init*/
0, /*tp_alloc will be set to PyType_GenericAlloc in module init*/ 0, /*tp_alloc*/
list_new, /*tp_new*/ 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) psyco_List(PyObject *module, PyObject *args)
{ {
PyObject *str; 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 NULL;
return PyObject_CallFunction((PyObject *)&listType, "Os", str, enc); return PyObject_CallFunctionObjArgs((PyObject *)&listType, "O", str, NULL);
} }

View File

@ -37,7 +37,6 @@ typedef struct {
PyObject *wrapped; PyObject *wrapped;
PyObject *connection; PyObject *connection;
char *encoding;
} listObject; } listObject;
HIDDEN PyObject *psyco_List(PyObject *module, PyObject *args); HIDDEN PyObject *psyco_List(PyObject *module, PyObject *args);

View File

@ -172,15 +172,6 @@ mxdatetime_setup(mxdatetimeObject *self, PyObject *obj, int type)
return 0; return 0;
} }
static int
mxdatetime_traverse(PyObject *obj, visitproc visit, void *arg)
{
mxdatetimeObject *self = (mxdatetimeObject *)obj;
Py_VISIT(self->wrapped);
return 0;
}
static void static void
mxdatetime_dealloc(PyObject* obj) mxdatetime_dealloc(PyObject* obj)
{ {
@ -214,12 +205,6 @@ mxdatetime_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
return type->tp_alloc(type, 0); return type->tp_alloc(type, 0);
} }
static void
mxdatetime_del(PyObject* self)
{
PyObject_GC_Del(self);
}
static PyObject * static PyObject *
mxdatetime_repr(mxdatetimeObject *self) mxdatetime_repr(mxdatetimeObject *self)
{ {
@ -235,61 +220,41 @@ mxdatetime_repr(mxdatetimeObject *self)
PyTypeObject mxdatetimeType = { PyTypeObject mxdatetimeType = {
PyVarObject_HEAD_INIT(NULL, 0) PyVarObject_HEAD_INIT(NULL, 0)
"psycopg2._psycopg.MxDateTime", "psycopg2._psycopg.MxDateTime",
sizeof(mxdatetimeObject), sizeof(mxdatetimeObject), 0,
0,
mxdatetime_dealloc, /*tp_dealloc*/ mxdatetime_dealloc, /*tp_dealloc*/
0, /*tp_print*/ 0, /*tp_print*/
0, /*tp_getattr*/ 0, /*tp_getattr*/
0, /*tp_setattr*/ 0, /*tp_setattr*/
0, /*tp_compare*/ 0, /*tp_compare*/
(reprfunc)mxdatetime_repr, /*tp_repr*/ (reprfunc)mxdatetime_repr, /*tp_repr*/
0, /*tp_as_number*/ 0, /*tp_as_number*/
0, /*tp_as_sequence*/ 0, /*tp_as_sequence*/
0, /*tp_as_mapping*/ 0, /*tp_as_mapping*/
0, /*tp_hash */ 0, /*tp_hash */
0, /*tp_call*/ 0, /*tp_call*/
(reprfunc)mxdatetime_str, /*tp_str*/ (reprfunc)mxdatetime_str, /*tp_str*/
0, /*tp_getattro*/ 0, /*tp_getattro*/
0, /*tp_setattro*/ 0, /*tp_setattro*/
0, /*tp_as_buffer*/ 0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /*tp_flags*/
Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_HAVE_GC, /*tp_flags*/
mxdatetimeType_doc, /*tp_doc*/ mxdatetimeType_doc, /*tp_doc*/
0, /*tp_traverse*/
mxdatetime_traverse, /*tp_traverse*/
0, /*tp_clear*/ 0, /*tp_clear*/
0, /*tp_richcompare*/ 0, /*tp_richcompare*/
0, /*tp_weaklistoffset*/ 0, /*tp_weaklistoffset*/
0, /*tp_iter*/ 0, /*tp_iter*/
0, /*tp_iternext*/ 0, /*tp_iternext*/
/* Attribute descriptor and subclassing stuff */
mxdatetimeObject_methods, /*tp_methods*/ mxdatetimeObject_methods, /*tp_methods*/
mxdatetimeObject_members, /*tp_members*/ mxdatetimeObject_members, /*tp_members*/
0, /*tp_getset*/ 0, /*tp_getset*/
0, /*tp_base*/ 0, /*tp_base*/
0, /*tp_dict*/ 0, /*tp_dict*/
0, /*tp_descr_get*/ 0, /*tp_descr_get*/
0, /*tp_descr_set*/ 0, /*tp_descr_set*/
0, /*tp_dictoffset*/ 0, /*tp_dictoffset*/
mxdatetime_init, /*tp_init*/ mxdatetime_init, /*tp_init*/
0, /*tp_alloc*/ 0, /*tp_alloc*/
mxdatetime_new, /*tp_new*/ 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*/
}; };

View File

@ -114,15 +114,6 @@ pboolean_setup(pbooleanObject *self, PyObject *obj)
return 0; return 0;
} }
static int
pboolean_traverse(PyObject *obj, visitproc visit, void *arg)
{
pbooleanObject *self = (pbooleanObject *)obj;
Py_VISIT(self->wrapped);
return 0;
}
static void static void
pboolean_dealloc(PyObject* obj) pboolean_dealloc(PyObject* obj)
{ {
@ -155,12 +146,6 @@ pboolean_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
return type->tp_alloc(type, 0); return type->tp_alloc(type, 0);
} }
static void
pboolean_del(PyObject* self)
{
PyObject_GC_Del(self);
}
static PyObject * static PyObject *
pboolean_repr(pbooleanObject *self) pboolean_repr(pbooleanObject *self)
{ {
@ -177,63 +162,41 @@ pboolean_repr(pbooleanObject *self)
PyTypeObject pbooleanType = { PyTypeObject pbooleanType = {
PyVarObject_HEAD_INIT(NULL, 0) PyVarObject_HEAD_INIT(NULL, 0)
"psycopg2._psycopg.Boolean", "psycopg2._psycopg.Boolean",
sizeof(pbooleanObject), sizeof(pbooleanObject), 0,
0,
pboolean_dealloc, /*tp_dealloc*/ pboolean_dealloc, /*tp_dealloc*/
0, /*tp_print*/ 0, /*tp_print*/
0, /*tp_getattr*/ 0, /*tp_getattr*/
0, /*tp_setattr*/ 0, /*tp_setattr*/
0, /*tp_compare*/ 0, /*tp_compare*/
(reprfunc)pboolean_repr, /*tp_repr*/ (reprfunc)pboolean_repr, /*tp_repr*/
0, /*tp_as_number*/ 0, /*tp_as_number*/
0, /*tp_as_sequence*/ 0, /*tp_as_sequence*/
0, /*tp_as_mapping*/ 0, /*tp_as_mapping*/
0, /*tp_hash */ 0, /*tp_hash */
0, /*tp_call*/ 0, /*tp_call*/
(reprfunc)pboolean_str, /*tp_str*/ (reprfunc)pboolean_str, /*tp_str*/
0, /*tp_getattro*/ 0, /*tp_getattro*/
0, /*tp_setattro*/ 0, /*tp_setattro*/
0, /*tp_as_buffer*/ 0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /*tp_flags*/
Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_HAVE_GC, /*tp_flags*/
pbooleanType_doc, /*tp_doc*/ pbooleanType_doc, /*tp_doc*/
0, /*tp_traverse*/
pboolean_traverse, /*tp_traverse*/
0, /*tp_clear*/ 0, /*tp_clear*/
0, /*tp_richcompare*/ 0, /*tp_richcompare*/
0, /*tp_weaklistoffset*/ 0, /*tp_weaklistoffset*/
0, /*tp_iter*/ 0, /*tp_iter*/
0, /*tp_iternext*/ 0, /*tp_iternext*/
/* Attribute descriptor and subclassing stuff */
pbooleanObject_methods, /*tp_methods*/ pbooleanObject_methods, /*tp_methods*/
pbooleanObject_members, /*tp_members*/ pbooleanObject_members, /*tp_members*/
0, /*tp_getset*/ 0, /*tp_getset*/
0, /*tp_base*/ 0, /*tp_base*/
0, /*tp_dict*/ 0, /*tp_dict*/
0, /*tp_descr_get*/ 0, /*tp_descr_get*/
0, /*tp_descr_set*/ 0, /*tp_descr_set*/
0, /*tp_dictoffset*/ 0, /*tp_dictoffset*/
pboolean_init, /*tp_init*/ pboolean_init, /*tp_init*/
0, /*tp_alloc will be set to PyType_GenericAlloc in module init*/ 0, /*tp_alloc*/
pboolean_new, /*tp_new*/ 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*/
}; };

View File

@ -170,15 +170,6 @@ pdecimal_setup(pdecimalObject *self, PyObject *obj)
return 0; return 0;
} }
static int
pdecimal_traverse(PyObject *obj, visitproc visit, void *arg)
{
pdecimalObject *self = (pdecimalObject *)obj;
Py_VISIT(self->wrapped);
return 0;
}
static void static void
pdecimal_dealloc(PyObject* obj) pdecimal_dealloc(PyObject* obj)
{ {
@ -211,12 +202,6 @@ pdecimal_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
return type->tp_alloc(type, 0); return type->tp_alloc(type, 0);
} }
static void
pdecimal_del(PyObject* self)
{
PyObject_GC_Del(self);
}
static PyObject * static PyObject *
pdecimal_repr(pdecimalObject *self) pdecimal_repr(pdecimalObject *self)
{ {
@ -233,63 +218,41 @@ pdecimal_repr(pdecimalObject *self)
PyTypeObject pdecimalType = { PyTypeObject pdecimalType = {
PyVarObject_HEAD_INIT(NULL, 0) PyVarObject_HEAD_INIT(NULL, 0)
"psycopg2._psycopg.Decimal", "psycopg2._psycopg.Decimal",
sizeof(pdecimalObject), sizeof(pdecimalObject), 0,
0,
pdecimal_dealloc, /*tp_dealloc*/ pdecimal_dealloc, /*tp_dealloc*/
0, /*tp_print*/ 0, /*tp_print*/
0, /*tp_getattr*/ 0, /*tp_getattr*/
0, /*tp_setattr*/ 0, /*tp_setattr*/
0, /*tp_compare*/ 0, /*tp_compare*/
(reprfunc)pdecimal_repr, /*tp_repr*/ (reprfunc)pdecimal_repr, /*tp_repr*/
0, /*tp_as_number*/ 0, /*tp_as_number*/
0, /*tp_as_sequence*/ 0, /*tp_as_sequence*/
0, /*tp_as_mapping*/ 0, /*tp_as_mapping*/
0, /*tp_hash */ 0, /*tp_hash */
0, /*tp_call*/ 0, /*tp_call*/
(reprfunc)pdecimal_str, /*tp_str*/ (reprfunc)pdecimal_str, /*tp_str*/
0, /*tp_getattro*/ 0, /*tp_getattro*/
0, /*tp_setattro*/ 0, /*tp_setattro*/
0, /*tp_as_buffer*/ 0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /*tp_flags*/
Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_HAVE_GC, /*tp_flags*/
pdecimalType_doc, /*tp_doc*/ pdecimalType_doc, /*tp_doc*/
0, /*tp_traverse*/
pdecimal_traverse, /*tp_traverse*/
0, /*tp_clear*/ 0, /*tp_clear*/
0, /*tp_richcompare*/ 0, /*tp_richcompare*/
0, /*tp_weaklistoffset*/ 0, /*tp_weaklistoffset*/
0, /*tp_iter*/ 0, /*tp_iter*/
0, /*tp_iternext*/ 0, /*tp_iternext*/
/* Attribute descriptor and subclassing stuff */
pdecimalObject_methods, /*tp_methods*/ pdecimalObject_methods, /*tp_methods*/
pdecimalObject_members, /*tp_members*/ pdecimalObject_members, /*tp_members*/
0, /*tp_getset*/ 0, /*tp_getset*/
0, /*tp_base*/ 0, /*tp_base*/
0, /*tp_dict*/ 0, /*tp_dict*/
0, /*tp_descr_get*/ 0, /*tp_descr_get*/
0, /*tp_descr_set*/ 0, /*tp_descr_set*/
0, /*tp_dictoffset*/ 0, /*tp_dictoffset*/
pdecimal_init, /*tp_init*/ pdecimal_init, /*tp_init*/
0, /*tp_alloc will be set to PyType_GenericAlloc in module init*/ 0, /*tp_alloc*/
pdecimal_new, /*tp_new*/ 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*/
}; };

View File

@ -143,15 +143,6 @@ pfloat_setup(pfloatObject *self, PyObject *obj)
return 0; return 0;
} }
static int
pfloat_traverse(PyObject *obj, visitproc visit, void *arg)
{
pfloatObject *self = (pfloatObject *)obj;
Py_VISIT(self->wrapped);
return 0;
}
static void static void
pfloat_dealloc(PyObject* obj) pfloat_dealloc(PyObject* obj)
{ {
@ -184,12 +175,6 @@ pfloat_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
return type->tp_alloc(type, 0); return type->tp_alloc(type, 0);
} }
static void
pfloat_del(PyObject* self)
{
PyObject_GC_Del(self);
}
static PyObject * static PyObject *
pfloat_repr(pfloatObject *self) pfloat_repr(pfloatObject *self)
{ {
@ -206,63 +191,41 @@ pfloat_repr(pfloatObject *self)
PyTypeObject pfloatType = { PyTypeObject pfloatType = {
PyVarObject_HEAD_INIT(NULL, 0) PyVarObject_HEAD_INIT(NULL, 0)
"psycopg2._psycopg.Float", "psycopg2._psycopg.Float",
sizeof(pfloatObject), sizeof(pfloatObject), 0,
0,
pfloat_dealloc, /*tp_dealloc*/ pfloat_dealloc, /*tp_dealloc*/
0, /*tp_print*/ 0, /*tp_print*/
0, /*tp_getattr*/ 0, /*tp_getattr*/
0, /*tp_setattr*/ 0, /*tp_setattr*/
0, /*tp_compare*/ 0, /*tp_compare*/
(reprfunc)pfloat_repr, /*tp_repr*/ (reprfunc)pfloat_repr, /*tp_repr*/
0, /*tp_as_number*/ 0, /*tp_as_number*/
0, /*tp_as_sequence*/ 0, /*tp_as_sequence*/
0, /*tp_as_mapping*/ 0, /*tp_as_mapping*/
0, /*tp_hash */ 0, /*tp_hash */
0, /*tp_call*/ 0, /*tp_call*/
(reprfunc)pfloat_str, /*tp_str*/ (reprfunc)pfloat_str, /*tp_str*/
0, /*tp_getattro*/ 0, /*tp_getattro*/
0, /*tp_setattro*/ 0, /*tp_setattro*/
0, /*tp_as_buffer*/ 0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /*tp_flags*/
Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_HAVE_GC, /*tp_flags*/
pfloatType_doc, /*tp_doc*/ pfloatType_doc, /*tp_doc*/
0, /*tp_traverse*/
pfloat_traverse, /*tp_traverse*/
0, /*tp_clear*/ 0, /*tp_clear*/
0, /*tp_richcompare*/ 0, /*tp_richcompare*/
0, /*tp_weaklistoffset*/ 0, /*tp_weaklistoffset*/
0, /*tp_iter*/ 0, /*tp_iter*/
0, /*tp_iternext*/ 0, /*tp_iternext*/
/* Attribute descriptor and subclassing stuff */
pfloatObject_methods, /*tp_methods*/ pfloatObject_methods, /*tp_methods*/
pfloatObject_members, /*tp_members*/ pfloatObject_members, /*tp_members*/
0, /*tp_getset*/ 0, /*tp_getset*/
0, /*tp_base*/ 0, /*tp_base*/
0, /*tp_dict*/ 0, /*tp_dict*/
0, /*tp_descr_get*/ 0, /*tp_descr_get*/
0, /*tp_descr_set*/ 0, /*tp_descr_set*/
0, /*tp_dictoffset*/ 0, /*tp_dictoffset*/
pfloat_init, /*tp_init*/ pfloat_init, /*tp_init*/
0, /*tp_alloc will be set to PyType_GenericAlloc in module init*/ 0, /*tp_alloc*/
pfloat_new, /*tp_new*/ 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*/
}; };

View File

@ -129,15 +129,6 @@ pint_setup(pintObject *self, PyObject *obj)
return 0; return 0;
} }
static int
pint_traverse(PyObject *obj, visitproc visit, void *arg)
{
pintObject *self = (pintObject *)obj;
Py_VISIT(self->wrapped);
return 0;
}
static void static void
pint_dealloc(PyObject* obj) pint_dealloc(PyObject* obj)
{ {
@ -170,12 +161,6 @@ pint_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
return type->tp_alloc(type, 0); return type->tp_alloc(type, 0);
} }
static void
pint_del(PyObject* self)
{
PyObject_GC_Del(self);
}
static PyObject * static PyObject *
pint_repr(pintObject *self) pint_repr(pintObject *self)
{ {
@ -192,63 +177,41 @@ pint_repr(pintObject *self)
PyTypeObject pintType = { PyTypeObject pintType = {
PyVarObject_HEAD_INIT(NULL, 0) PyVarObject_HEAD_INIT(NULL, 0)
"psycopg2._psycopg.Int", "psycopg2._psycopg.Int",
sizeof(pintObject), sizeof(pintObject), 0,
0,
pint_dealloc, /*tp_dealloc*/ pint_dealloc, /*tp_dealloc*/
0, /*tp_print*/ 0, /*tp_print*/
0, /*tp_getattr*/ 0, /*tp_getattr*/
0, /*tp_setattr*/ 0, /*tp_setattr*/
0, /*tp_compare*/ 0, /*tp_compare*/
(reprfunc)pint_repr, /*tp_repr*/ (reprfunc)pint_repr, /*tp_repr*/
0, /*tp_as_number*/ 0, /*tp_as_number*/
0, /*tp_as_sequence*/ 0, /*tp_as_sequence*/
0, /*tp_as_mapping*/ 0, /*tp_as_mapping*/
0, /*tp_hash */ 0, /*tp_hash */
0, /*tp_call*/ 0, /*tp_call*/
(reprfunc)pint_str, /*tp_str*/ (reprfunc)pint_str, /*tp_str*/
0, /*tp_getattro*/ 0, /*tp_getattro*/
0, /*tp_setattro*/ 0, /*tp_setattro*/
0, /*tp_as_buffer*/ 0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /*tp_flags*/
Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_HAVE_GC, /*tp_flags*/
pintType_doc, /*tp_doc*/ pintType_doc, /*tp_doc*/
0, /*tp_traverse*/
pint_traverse, /*tp_traverse*/
0, /*tp_clear*/ 0, /*tp_clear*/
0, /*tp_richcompare*/ 0, /*tp_richcompare*/
0, /*tp_weaklistoffset*/ 0, /*tp_weaklistoffset*/
0, /*tp_iter*/ 0, /*tp_iter*/
0, /*tp_iternext*/ 0, /*tp_iternext*/
/* Attribute descriptor and subclassing stuff */
pintObject_methods, /*tp_methods*/ pintObject_methods, /*tp_methods*/
pintObject_members, /*tp_members*/ pintObject_members, /*tp_members*/
0, /*tp_getset*/ 0, /*tp_getset*/
0, /*tp_base*/ 0, /*tp_base*/
0, /*tp_dict*/ 0, /*tp_dict*/
0, /*tp_descr_get*/ 0, /*tp_descr_get*/
0, /*tp_descr_set*/ 0, /*tp_descr_set*/
0, /*tp_dictoffset*/ 0, /*tp_dictoffset*/
pint_init, /*tp_init*/ pint_init, /*tp_init*/
0, /*tp_alloc will be set to PyType_GenericAlloc in module init*/ 0, /*tp_alloc*/
pint_new, /*tp_new*/ 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*/
}; };

View File

@ -32,26 +32,32 @@
#include <string.h> #include <string.h>
static const char *default_encoding = "latin1";
/* qstring_quote - do the quote process on plain and unicode strings */ /* qstring_quote - do the quote process on plain and unicode strings */
BORROWED static PyObject * static PyObject *
qstring_quote(qstringObject *self) qstring_quote(qstringObject *self)
{ {
PyObject *str; PyObject *str = NULL;
char *s, *buffer; char *s, *buffer = NULL;
Py_ssize_t len, qlen; 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 /* 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 */ 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) { if (PyUnicode_Check(self->wrapped) && encoding) {
str = PyUnicode_AsEncodedString(self->wrapped, self->encoding, NULL); str = PyUnicode_AsEncodedString(self->wrapped, encoding, NULL);
Dprintf("qstring_quote: got encoded object at %p", str); Dprintf("qstring_quote: got encoded object at %p", str);
if (str == NULL) return NULL; if (str == NULL) goto exit;
} }
#if PY_MAJOR_VERSION < 3 #if PY_MAJOR_VERSION < 3
@ -68,30 +74,28 @@ qstring_quote(qstringObject *self)
else { else {
PyErr_SetString(PyExc_TypeError, PyErr_SetString(PyExc_TypeError,
"can't quote non-string object (or missing encoding)"); "can't quote non-string object (or missing encoding)");
return NULL; goto exit;
} }
/* encode the string into buffer */ /* encode the string into buffer */
Bytes_AsStringAndSize(str, &s, &len); Bytes_AsStringAndSize(str, &s, &len);
if (!(buffer = psycopg_escape_string(self->conn, s, len, NULL, &qlen))) { if (!(buffer = psycopg_escape_string(self->conn, s, len, NULL, &qlen))) {
Py_DECREF(str); goto exit;
PyErr_NoMemory();
return NULL;
} }
if (qlen > (size_t) PY_SSIZE_T_MAX) { if (qlen > (size_t) PY_SSIZE_T_MAX) {
PyErr_SetString(PyExc_IndexError, PyErr_SetString(PyExc_IndexError,
"PG buffer too large to fit in Python buffer."); "PG buffer too large to fit in Python buffer.");
PyMem_Free(buffer); goto exit;
Py_DECREF(str);
return NULL;
} }
self->buffer = Bytes_FromStringAndSize(buffer, qlen); rv = Bytes_FromStringAndSize(buffer, qlen);
PyMem_Free(buffer);
Py_DECREF(str);
return self->buffer; exit:
PyMem_Free(buffer);
Py_XDECREF(str);
return rv;
} }
/* qstring_str, qstring_getquoted - return result of quoting */ /* qstring_str, qstring_getquoted - return result of quoting */
@ -100,7 +104,7 @@ static PyObject *
qstring_getquoted(qstringObject *self, PyObject *args) qstring_getquoted(qstringObject *self, PyObject *args)
{ {
if (self->buffer == NULL) { if (self->buffer == NULL) {
qstring_quote(self); self->buffer = qstring_quote(self);
} }
Py_XINCREF(self->buffer); Py_XINCREF(self->buffer);
return self->buffer; return self->buffer;
@ -115,25 +119,16 @@ qstring_str(qstringObject *self)
static PyObject * static PyObject *
qstring_prepare(qstringObject *self, PyObject *args) qstring_prepare(qstringObject *self, PyObject *args)
{ {
PyObject *conn; connectionObject *conn;
if (!PyArg_ParseTuple(args, "O!", &connectionType, &conn)) if (!PyArg_ParseTuple(args, "O!", &connectionType, &conn))
return NULL; 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_CLEAR(self->conn);
Py_INCREF(conn); Py_INCREF(conn);
self->conn = conn; self->conn = conn;
Py_INCREF(Py_None); Py_RETURN_NONE;
return Py_None;
} }
static PyObject * static PyObject *
@ -152,6 +147,18 @@ qstring_conform(qstringObject *self, PyObject *args)
return res; 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 **/ /** the QuotedString object **/
/* object member list */ /* object member list */
@ -159,7 +166,6 @@ qstring_conform(qstringObject *self, PyObject *args)
static struct PyMemberDef qstringObject_members[] = { static struct PyMemberDef qstringObject_members[] = {
{"adapted", T_OBJECT, offsetof(qstringObject, wrapped), READONLY}, {"adapted", T_OBJECT, offsetof(qstringObject, wrapped), READONLY},
{"buffer", T_OBJECT, offsetof(qstringObject, buffer), READONLY}, {"buffer", T_OBJECT, offsetof(qstringObject, buffer), READONLY},
{"encoding", T_STRING, offsetof(qstringObject, encoding), READONLY},
{NULL} {NULL}
}; };
@ -174,22 +180,24 @@ static PyMethodDef qstringObject_methods[] = {
{NULL} /* Sentinel */ {NULL} /* Sentinel */
}; };
static PyGetSetDef qstringObject_getsets[] = {
{ "encoding",
(getter)qstring_get_encoding,
(setter)NULL,
"current encoding of the adapter" },
{NULL}
};
/* initialization and finalization methods */ /* initialization and finalization methods */
static int 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 = " Dprintf("qstring_setup: init qstring object at %p, refcnt = "
FORMAT_CODE_PY_SSIZE_T, FORMAT_CODE_PY_SSIZE_T,
self, Py_REFCNT(self) self, Py_REFCNT(self)
); );
self->buffer = NULL;
self->conn = NULL;
/* FIXME: remove this orrible strdup */
if (enc) self->encoding = strdup(enc);
Py_INCREF(str); Py_INCREF(str);
self->wrapped = str; self->wrapped = str;
@ -200,17 +208,6 @@ qstring_setup(qstringObject *self, PyObject *str, const char *enc)
return 0; 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 static void
qstring_dealloc(PyObject* obj) qstring_dealloc(PyObject* obj)
{ {
@ -220,8 +217,6 @@ qstring_dealloc(PyObject* obj)
Py_CLEAR(self->buffer); Py_CLEAR(self->buffer);
Py_CLEAR(self->conn); Py_CLEAR(self->conn);
if (self->encoding) free(self->encoding);
Dprintf("qstring_dealloc: deleted qstring object at %p, refcnt = " Dprintf("qstring_dealloc: deleted qstring object at %p, refcnt = "
FORMAT_CODE_PY_SSIZE_T, FORMAT_CODE_PY_SSIZE_T,
obj, Py_REFCNT(obj) obj, Py_REFCNT(obj)
@ -234,12 +229,11 @@ static int
qstring_init(PyObject *obj, PyObject *args, PyObject *kwds) qstring_init(PyObject *obj, PyObject *args, PyObject *kwds)
{ {
PyObject *str; 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 -1;
return qstring_setup((qstringObject *)obj, str, enc); return qstring_setup((qstringObject *)obj, str);
} }
static PyObject * static PyObject *
@ -248,12 +242,6 @@ qstring_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
return type->tp_alloc(type, 0); return type->tp_alloc(type, 0);
} }
static void
qstring_del(PyObject* self)
{
PyObject_GC_Del(self);
}
static PyObject * static PyObject *
qstring_repr(qstringObject *self) qstring_repr(qstringObject *self)
{ {
@ -264,66 +252,46 @@ qstring_repr(qstringObject *self)
/* object type */ /* object type */
#define qstringType_doc \ #define qstringType_doc \
"QuotedString(str, enc) -> new quoted object with 'enc' encoding" "QuotedString(str) -> new quoted object"
PyTypeObject qstringType = { PyTypeObject qstringType = {
PyVarObject_HEAD_INIT(NULL, 0) PyVarObject_HEAD_INIT(NULL, 0)
"psycopg2._psycopg.QuotedString", "psycopg2._psycopg.QuotedString",
sizeof(qstringObject), sizeof(qstringObject), 0,
0,
qstring_dealloc, /*tp_dealloc*/ qstring_dealloc, /*tp_dealloc*/
0, /*tp_print*/ 0, /*tp_print*/
0, /*tp_getattr*/ 0, /*tp_getattr*/
0, /*tp_setattr*/ 0, /*tp_setattr*/
0, /*tp_compare*/ 0, /*tp_compare*/
(reprfunc)qstring_repr, /*tp_repr*/ (reprfunc)qstring_repr, /*tp_repr*/
0, /*tp_as_number*/ 0, /*tp_as_number*/
0, /*tp_as_sequence*/ 0, /*tp_as_sequence*/
0, /*tp_as_mapping*/ 0, /*tp_as_mapping*/
0, /*tp_hash */ 0, /*tp_hash */
0, /*tp_call*/ 0, /*tp_call*/
(reprfunc)qstring_str, /*tp_str*/ (reprfunc)qstring_str, /*tp_str*/
0, /*tp_getattro*/ 0, /*tp_getattro*/
0, /*tp_setattro*/ 0, /*tp_setattro*/
0, /*tp_as_buffer*/ 0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /*tp_flags*/
Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_HAVE_GC, /*tp_flags*/
qstringType_doc, /*tp_doc*/ qstringType_doc, /*tp_doc*/
0, /*tp_traverse*/
qstring_traverse, /*tp_traverse*/
0, /*tp_clear*/ 0, /*tp_clear*/
0, /*tp_richcompare*/ 0, /*tp_richcompare*/
0, /*tp_weaklistoffset*/ 0, /*tp_weaklistoffset*/
0, /*tp_iter*/ 0, /*tp_iter*/
0, /*tp_iternext*/ 0, /*tp_iternext*/
/* Attribute descriptor and subclassing stuff */
qstringObject_methods, /*tp_methods*/ qstringObject_methods, /*tp_methods*/
qstringObject_members, /*tp_members*/ qstringObject_members, /*tp_members*/
0, /*tp_getset*/ qstringObject_getsets, /*tp_getset*/
0, /*tp_base*/ 0, /*tp_base*/
0, /*tp_dict*/ 0, /*tp_dict*/
0, /*tp_descr_get*/ 0, /*tp_descr_get*/
0, /*tp_descr_set*/ 0, /*tp_descr_set*/
0, /*tp_dictoffset*/ 0, /*tp_dictoffset*/
qstring_init, /*tp_init*/ qstring_init, /*tp_init*/
0, /*tp_alloc will be set to PyType_GenericAlloc in module init*/ 0, /*tp_alloc*/
qstring_new, /*tp_new*/ 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) psyco_QuotedString(PyObject *module, PyObject *args)
{ {
PyObject *str; 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 NULL;
return PyObject_CallFunction((PyObject *)&qstringType, "Os", str, enc); return PyObject_CallFunctionObjArgs((PyObject *)&qstringType, str, NULL);
} }

View File

@ -37,13 +37,8 @@ typedef struct {
PyObject *wrapped; PyObject *wrapped;
PyObject *buffer; 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; } qstringObject;
/* functions exported to psycopgmodule.c */ /* functions exported to psycopgmodule.c */

View File

@ -74,7 +74,8 @@ struct connectionObject_notice {
const char *message; const char *message;
}; };
typedef struct { /* the typedef is forward-declared in psycopg.h */
struct connectionObject {
PyObject_HEAD PyObject_HEAD
pthread_mutex_t lock; /* the global connection lock */ pthread_mutex_t lock; /* the global connection lock */
@ -88,7 +89,7 @@ typedef struct {
2 that something horrible happened */ 2 that something horrible happened */
long int mark; /* number of commits/rollbacks done so far */ long int mark; /* number of commits/rollbacks done so far */
int status; /* status of the connection */ 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 */ long int async; /* 1 means the connection is async */
int protocol; /* protocol version */ int protocol; /* protocol version */
@ -120,7 +121,8 @@ typedef struct {
int autocommit; int autocommit;
} connectionObject; PyObject *cursor_factory; /* default cursor factory from cursor() */
};
/* map isolation level values into a numeric const */ /* map isolation level values into a numeric const */
typedef struct { 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); RAISES_NEG HIDDEN int conn_get_isolation_level(connectionObject *self);
HIDDEN int conn_get_protocol_version(PGconn *pgconn); HIDDEN int conn_get_protocol_version(PGconn *pgconn);
HIDDEN int conn_get_server_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_process(connectionObject *self);
HIDDEN void conn_notice_clean(connectionObject *self); HIDDEN void conn_notice_clean(connectionObject *self);
HIDDEN void conn_notifies_process(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_switch_isolation_level(connectionObject *self, int level);
RAISES_NEG HIDDEN int conn_set_client_encoding(connectionObject *self, const char *enc); RAISES_NEG HIDDEN int conn_set_client_encoding(connectionObject *self, const char *enc);
HIDDEN int conn_poll(connectionObject *self); 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, 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); HIDDEN PyObject *conn_tpc_recover(connectionObject *self);
/* exception-raising macros */ /* exception-raising macros */

View File

@ -183,7 +183,7 @@ conn_notifies_process(connectionObject *self)
if (!(channel = conn_text_from_chars(self, pgn->relname))) { goto error; } if (!(channel = conn_text_from_chars(self, pgn->relname))) { goto error; }
if (!(payload = conn_text_from_chars(self, pgn->extra))) { goto error; } if (!(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))) { pid, channel, payload, NULL))) {
goto error; goto error;
} }
@ -439,10 +439,22 @@ conn_get_server_version(PGconn *pgconn)
return (int)PQserverVersion(pgconn); return (int)PQserverVersion(pgconn);
} }
PGcancel * /* set up the cancel key of the connection.
conn_get_cancel(PGconn *pgconn) * 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; return -1;
} }
self->cancel = conn_get_cancel(self->pgconn); if (0 > conn_setup_cancel(self, pgconn)) {
if (self->cancel == NULL) {
PyErr_SetString(OperationalError, "can't get cancellation key");
return -1; return -1;
} }
@ -788,10 +798,8 @@ _conn_poll_setup_async(connectionObject *self)
if (0 > conn_read_encoding(self, self->pgconn)) { if (0 > conn_read_encoding(self, self->pgconn)) {
break; break;
} }
self->cancel = conn_get_cancel(self->pgconn); if (0 > conn_setup_cancel(self, self->pgconn)) {
if (self->cancel == NULL) { return -1;
PyErr_SetString(OperationalError, "can't get cancellation key");
break;
} }
/* asynchronous connections always use isolation level 0, the user is /* asynchronous connections always use isolation level 0, the user is
@ -890,7 +898,7 @@ conn_poll(connectionObject *self)
} }
curs = (cursorObject *)py_curs; curs = (cursorObject *)py_curs;
IFCLEARPGRES(curs->pgres); CLEARPGRES(curs->pgres);
curs->pgres = pq_get_last_result(self); curs->pgres = pq_get_last_result(self);
/* fetch the tuples (if there are any) and build the result. We /* fetch the tuples (if there are any) and build the result. We
@ -918,7 +926,8 @@ conn_poll(connectionObject *self)
void void
conn_close(connectionObject *self) conn_close(connectionObject *self)
{ {
if (self->closed) { /* a connection with closed == 2 still requires cleanup */
if (self->closed == 1) {
return; return;
} }
@ -936,7 +945,7 @@ conn_close(connectionObject *self)
void conn_close_locked(connectionObject *self) void conn_close_locked(connectionObject *self)
{ {
if (self->closed) { if (self->closed == 1) {
return; return;
} }
@ -957,8 +966,6 @@ void conn_close_locked(connectionObject *self)
PQfinish(self->pgconn); PQfinish(self->pgconn);
self->pgconn = NULL; self->pgconn = NULL;
Dprintf("conn_close: PQfinish called"); Dprintf("conn_close: PQfinish called");
PQfreeCancel(self->cancel);
self->cancel = NULL;
} }
} }
@ -1213,7 +1220,7 @@ exit:
* until PREPARE. */ * until PREPARE. */
RAISES_NEG int RAISES_NEG int
conn_tpc_begin(connectionObject *self, XidObject *xid) conn_tpc_begin(connectionObject *self, xidObject *xid)
{ {
PGresult *pgres = NULL; PGresult *pgres = NULL;
char *error = NULL; char *error = NULL;
@ -1247,7 +1254,7 @@ conn_tpc_begin(connectionObject *self, XidObject *xid)
* for many commands and for recovered transactions. */ * for many commands and for recovered transactions. */
RAISES_NEG int 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; PGresult *pgres = NULL;
char *error = NULL; char *error = NULL;

View File

@ -52,63 +52,74 @@
static PyObject * static PyObject *
psyco_conn_cursor(connectionObject *self, PyObject *args, PyObject *kwargs) psyco_conn_cursor(connectionObject *self, PyObject *args, PyObject *kwargs)
{ {
PyObject *obj; PyObject *obj = NULL;
PyObject *rv = NULL;
PyObject *name = Py_None; PyObject *name = Py_None;
PyObject *factory = (PyObject *)&cursorType; PyObject *factory = (PyObject *)&cursorType;
PyObject *withhold = Py_False; 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;
}
if (PyObject_IsTrue(withhold) && (name == Py_None)) {
PyErr_SetString(ProgrammingError,
"'withhold=True can be specified only for named cursors");
return NULL;
}
EXC_IF_CONN_CLOSED(self); EXC_IF_CONN_CLOSED(self);
if (self->cursor_factory && self->cursor_factory != Py_None) {
factory = self->cursor_factory;
}
if (!PyArg_ParseTupleAndKeywords(
args, kwargs, "|OOOO", kwlist,
&name, &factory, &withhold, &scrollable)) {
goto exit;
}
if (self->status != CONN_STATUS_READY && if (self->status != CONN_STATUS_READY &&
self->status != CONN_STATUS_BEGIN && self->status != CONN_STATUS_BEGIN &&
self->status != CONN_STATUS_PREPARED) { self->status != CONN_STATUS_PREPARED) {
PyErr_SetString(OperationalError, PyErr_SetString(OperationalError,
"asynchronous connection attempt underway"); "asynchronous connection attempt underway");
return NULL; goto exit;
} }
if (name != Py_None && self->async == 1) { if (name != Py_None && self->async == 1) {
PyErr_SetString(ProgrammingError, PyErr_SetString(ProgrammingError,
"asynchronous connections " "asynchronous connections "
"cannot produce named cursors"); "cannot produce named cursors");
return NULL; goto exit;
} }
Dprintf("psyco_conn_cursor: new %s cursor for connection at %p", Dprintf("psyco_conn_cursor: new %s cursor for connection at %p",
(name == Py_None ? "unnamed" : "named"), self); (name == Py_None ? "unnamed" : "named"), self);
if (!(obj = PyObject_CallFunctionObjArgs(factory, self, name, NULL))) { if (!(obj = PyObject_CallFunctionObjArgs(factory, self, name, NULL))) {
return NULL; goto exit;
} }
if (PyObject_IsInstance(obj, (PyObject *)&cursorType) == 0) { if (PyObject_IsInstance(obj, (PyObject *)&cursorType) == 0) {
PyErr_SetString(PyExc_TypeError, PyErr_SetString(PyExc_TypeError,
"cursor factory must be subclass of psycopg2._psycopg.cursor"); "cursor factory must be subclass of psycopg2._psycopg.cursor");
Py_DECREF(obj); goto exit;
return NULL;
} }
if (PyObject_IsTrue(withhold)) if (0 != psyco_curs_withhold_set((cursorObject *)obj, withhold)) {
((cursorObject*)obj)->withhold = 1; goto exit;
}
if (0 != psyco_curs_scrollable_set((cursorObject *)obj, scrollable)) {
goto exit;
}
Dprintf("psyco_conn_cursor: new cursor at %p: refcnt = " Dprintf("psyco_conn_cursor: new cursor at %p: refcnt = "
FORMAT_CODE_PY_SSIZE_T, FORMAT_CODE_PY_SSIZE_T,
obj, Py_REFCNT(obj) 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." #define psyco_conn_close_doc "close() -- Close the connection."
static PyObject * static PyObject *
psyco_conn_close(connectionObject *self, PyObject *args) psyco_conn_close(connectionObject *self)
{ {
Dprintf("psyco_conn_close: closing connection at %p", self); Dprintf("psyco_conn_close: closing connection at %p", self);
conn_close(self); conn_close(self);
Dprintf("psyco_conn_close: connection at %p closed", self); Dprintf("psyco_conn_close: connection at %p closed", self);
Py_INCREF(Py_None); Py_RETURN_NONE;
return Py_None;
} }
@ -133,7 +143,7 @@ psyco_conn_close(connectionObject *self, PyObject *args)
#define psyco_conn_commit_doc "commit() -- Commit all changes to database." #define psyco_conn_commit_doc "commit() -- Commit all changes to database."
static PyObject * static PyObject *
psyco_conn_commit(connectionObject *self, PyObject *args) psyco_conn_commit(connectionObject *self)
{ {
EXC_IF_CONN_CLOSED(self); EXC_IF_CONN_CLOSED(self);
EXC_IF_CONN_ASYNC(self, commit); EXC_IF_CONN_ASYNC(self, commit);
@ -142,8 +152,7 @@ psyco_conn_commit(connectionObject *self, PyObject *args)
if (conn_commit(self) < 0) if (conn_commit(self) < 0)
return NULL; return NULL;
Py_INCREF(Py_None); Py_RETURN_NONE;
return Py_None;
} }
@ -153,7 +162,7 @@ psyco_conn_commit(connectionObject *self, PyObject *args)
"rollback() -- Roll back all changes done to database." "rollback() -- Roll back all changes done to database."
static PyObject * static PyObject *
psyco_conn_rollback(connectionObject *self, PyObject *args) psyco_conn_rollback(connectionObject *self)
{ {
EXC_IF_CONN_CLOSED(self); EXC_IF_CONN_CLOSED(self);
EXC_IF_CONN_ASYNC(self, rollback); EXC_IF_CONN_ASYNC(self, rollback);
@ -162,8 +171,7 @@ psyco_conn_rollback(connectionObject *self, PyObject *args)
if (conn_rollback(self) < 0) if (conn_rollback(self) < 0)
return NULL; return NULL;
Py_INCREF(Py_None); Py_RETURN_NONE;
return Py_None;
} }
@ -176,7 +184,7 @@ psyco_conn_xid(connectionObject *self, PyObject *args, PyObject *kwargs)
EXC_IF_CONN_CLOSED(self); EXC_IF_CONN_CLOSED(self);
EXC_IF_TPC_NOT_SUPPORTED(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) psyco_conn_tpc_begin(connectionObject *self, PyObject *args)
{ {
PyObject *rv = NULL; PyObject *rv = NULL;
XidObject *xid = NULL; xidObject *xid = NULL;
PyObject *oxid; PyObject *oxid;
EXC_IF_CONN_CLOSED(self); EXC_IF_CONN_CLOSED(self);
@ -227,7 +235,7 @@ exit:
"tpc_prepare() -- perform the first phase of a two-phase transaction." "tpc_prepare() -- perform the first phase of a two-phase transaction."
static PyObject * static PyObject *
psyco_conn_tpc_prepare(connectionObject *self, PyObject *args) psyco_conn_tpc_prepare(connectionObject *self)
{ {
EXC_IF_CONN_CLOSED(self); EXC_IF_CONN_CLOSED(self);
EXC_IF_CONN_ASYNC(self, tpc_prepare); EXC_IF_CONN_ASYNC(self, tpc_prepare);
@ -247,8 +255,7 @@ psyco_conn_tpc_prepare(connectionObject *self, PyObject *args)
* can be performed until commit. */ * can be performed until commit. */
self->status = CONN_STATUS_PREPARED; self->status = CONN_STATUS_PREPARED;
Py_INCREF(Py_None); Py_RETURN_NONE;
return Py_None;
} }
@ -279,7 +286,7 @@ _psyco_conn_tpc_finish(connectionObject *self, PyObject *args,
_finish_f opc_f, char *tpc_cmd) _finish_f opc_f, char *tpc_cmd)
{ {
PyObject *oxid = NULL; PyObject *oxid = NULL;
XidObject *xid = NULL; xidObject *xid = NULL;
PyObject *rv = NULL; PyObject *rv = NULL;
if (!PyArg_ParseTuple(args, "|O", &oxid)) { goto exit; } 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." "tpc_recover() -- returns a list of pending transaction IDs."
static PyObject * static PyObject *
psyco_conn_tpc_recover(connectionObject *self, PyObject *args) psyco_conn_tpc_recover(connectionObject *self)
{ {
EXC_IF_CONN_CLOSED(self); EXC_IF_CONN_CLOSED(self);
EXC_IF_CONN_ASYNC(self, tpc_recover); 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 #ifdef PSYCOPG_EXTENSIONS
@ -528,8 +583,7 @@ psyco_conn_set_session(connectionObject *self, PyObject *args, PyObject *kwargs)
return NULL; return NULL;
} }
Py_INCREF(Py_None); Py_RETURN_NONE;
return Py_None;
} }
@ -611,8 +665,7 @@ psyco_conn_set_isolation_level(connectionObject *self, PyObject *args)
return NULL; return NULL;
} }
Py_INCREF(Py_None); Py_RETURN_NONE;
return Py_None;
} }
/* set_client_encoding method - set client encoding */ /* 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." "get_transaction_status() -- Get backend transaction status."
static PyObject * static PyObject *
psyco_conn_get_transaction_status(connectionObject *self, PyObject *args) psyco_conn_get_transaction_status(connectionObject *self)
{ {
EXC_IF_CONN_CLOSED(self); EXC_IF_CONN_CLOSED(self);
@ -675,8 +728,7 @@ psyco_conn_get_parameter_status(connectionObject *self, PyObject *args)
val = PQparameterStatus(self->pgconn, param); val = PQparameterStatus(self->pgconn, param);
if (!val) { if (!val) {
Py_INCREF(Py_None); Py_RETURN_NONE;
return Py_None;
} }
return conn_text_from_chars(self, val); 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." "get_backend_pid() -- Get backend process id."
static PyObject * static PyObject *
psyco_conn_get_backend_pid(connectionObject *self, PyObject *args) psyco_conn_get_backend_pid(connectionObject *self)
{ {
EXC_IF_CONN_CLOSED(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." "reset() -- Reset current connection to defaults."
static PyObject * static PyObject *
psyco_conn_reset(connectionObject *self, PyObject *args) psyco_conn_reset(connectionObject *self)
{ {
int res; int res;
@ -776,8 +828,7 @@ psyco_conn_reset(connectionObject *self, PyObject *args)
if (res < 0) if (res < 0)
return NULL; return NULL;
Py_INCREF(Py_None); Py_RETURN_NONE;
return Py_None;
} }
static PyObject * static PyObject *
@ -790,7 +841,7 @@ psyco_conn_get_exception(PyObject *self, void *closure)
} }
static PyObject * static PyObject *
psyco_conn_poll(connectionObject *self, PyObject *args) psyco_conn_poll(connectionObject *self)
{ {
int res; int res;
@ -812,7 +863,7 @@ psyco_conn_poll(connectionObject *self, PyObject *args)
"fileno() -> int -- Return file descriptor associated to database connection." "fileno() -> int -- Return file descriptor associated to database connection."
static PyObject * static PyObject *
psyco_conn_fileno(connectionObject *self, PyObject *args) psyco_conn_fileno(connectionObject *self)
{ {
long int socket; long int socket;
@ -831,7 +882,7 @@ psyco_conn_fileno(connectionObject *self, PyObject *args)
"executing an asynchronous operation." "executing an asynchronous operation."
static PyObject * static PyObject *
psyco_conn_isexecuting(connectionObject *self, PyObject *args) psyco_conn_isexecuting(connectionObject *self)
{ {
/* synchronous connections will always return False */ /* synchronous connections will always return False */
if (self->async == 0) { if (self->async == 0) {
@ -863,7 +914,7 @@ psyco_conn_isexecuting(connectionObject *self, PyObject *args)
"cancel() -- cancel the current operation" "cancel() -- cancel the current operation"
static PyObject * static PyObject *
psyco_conn_cancel(connectionObject *self, PyObject *args) psyco_conn_cancel(connectionObject *self)
{ {
char errbuf[256]; char errbuf[256];
@ -884,8 +935,7 @@ psyco_conn_cancel(connectionObject *self, PyObject *args)
PyErr_SetString(OperationalError, errbuf); PyErr_SetString(OperationalError, errbuf);
return NULL; return NULL;
} }
Py_INCREF(Py_None); Py_RETURN_NONE;
return Py_None;
} }
#endif /* PSYCOPG_EXTENSIONS */ #endif /* PSYCOPG_EXTENSIONS */
@ -917,6 +967,10 @@ static struct PyMethodDef connectionObject_methods[] = {
METH_VARARGS, psyco_conn_tpc_rollback_doc}, METH_VARARGS, psyco_conn_tpc_rollback_doc},
{"tpc_recover", (PyCFunction)psyco_conn_tpc_recover, {"tpc_recover", (PyCFunction)psyco_conn_tpc_recover,
METH_NOARGS, psyco_conn_tpc_recover_doc}, 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 #ifdef PSYCOPG_EXTENSIONS
{"set_session", (PyCFunction)psyco_conn_set_session, {"set_session", (PyCFunction)psyco_conn_set_session,
METH_VARARGS|METH_KEYWORDS, psyco_conn_set_session_doc}, METH_VARARGS|METH_KEYWORDS, psyco_conn_set_session_doc},
@ -963,6 +1017,8 @@ static struct PyMemberDef connectionObject_members[] = {
{"status", T_INT, {"status", T_INT,
offsetof(connectionObject, status), READONLY, offsetof(connectionObject, status), READONLY,
"The current transaction status."}, "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, {"string_types", T_OBJECT, offsetof(connectionObject, string_types), READONLY,
"A set of typecasters to convert textual values."}, "A set of typecasters to convert textual values."},
{"binary_types", T_OBJECT, offsetof(connectionObject, binary_types), READONLY, {"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) self, async, Py_REFCNT(self)
); );
if (!(self->dsn = strdup(dsn))) { if (0 > psycopg_strdup(&self->dsn, dsn, 0)) { goto exit; }
PyErr_NoMemory();
goto exit;
}
if (!(self->notice_list = PyList_New(0))) { goto exit; } if (!(self->notice_list = PyList_New(0))) { goto exit; }
if (!(self->notifies = PyList_New(0))) { goto exit; } if (!(self->notifies = PyList_New(0))) { goto exit; }
self->async = async; self->async = async;
@ -1057,26 +1110,9 @@ exit:
return res; return res;
} }
static void static int
connection_dealloc(PyObject* obj) connection_clear(connectionObject *self)
{ {
connectionObject *self = (connectionObject *)obj;
if (self->weakreflist) {
PyObject_ClearWeakRefs(obj);
}
PyObject_GC_UnTrack(self);
if (self->closed == 0) conn_close(self);
conn_notice_clean(self);
if (self->dsn) free(self->dsn);
PyMem_Free(self->encoding);
PyMem_Free(self->codec);
if (self->critical) free(self->critical);
Py_CLEAR(self->tpc_xid); Py_CLEAR(self->tpc_xid);
Py_CLEAR(self->async_cursor); Py_CLEAR(self->async_cursor);
Py_CLEAR(self->notice_list); Py_CLEAR(self->notice_list);
@ -1084,6 +1120,31 @@ connection_dealloc(PyObject* obj)
Py_CLEAR(self->notifies); Py_CLEAR(self->notifies);
Py_CLEAR(self->string_types); Py_CLEAR(self->string_types);
Py_CLEAR(self->binary_types); Py_CLEAR(self->binary_types);
return 0;
}
static void
connection_dealloc(PyObject* obj)
{
connectionObject *self = (connectionObject *)obj;
conn_close(self);
PyObject_GC_UnTrack(self);
if (self->weakreflist) {
PyObject_ClearWeakRefs(obj);
}
conn_notice_clean(self);
PyMem_Free(self->dsn);
PyMem_Free(self->encoding);
PyMem_Free(self->codec);
if (self->critical) free(self->critical);
if (self->cancel) PQfreeCancel(self->cancel);
connection_clear(self);
pthread_mutex_destroy(&(self->lock)); pthread_mutex_destroy(&(self->lock));
@ -1114,12 +1175,6 @@ connection_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
return type->tp_alloc(type, 0); return type->tp_alloc(type, 0);
} }
static void
connection_del(PyObject* self)
{
PyObject_GC_Del(self);
}
static PyObject * static PyObject *
connection_repr(connectionObject *self) connection_repr(connectionObject *self)
{ {
@ -1154,8 +1209,7 @@ connection_traverse(connectionObject *self, visitproc visit, void *arg)
PyTypeObject connectionType = { PyTypeObject connectionType = {
PyVarObject_HEAD_INIT(NULL, 0) PyVarObject_HEAD_INIT(NULL, 0)
"psycopg2._psycopg.connection", "psycopg2._psycopg.connection",
sizeof(connectionObject), sizeof(connectionObject), 0,
0,
connection_dealloc, /*tp_dealloc*/ connection_dealloc, /*tp_dealloc*/
0, /*tp_print*/ 0, /*tp_print*/
0, /*tp_getattr*/ 0, /*tp_getattr*/
@ -1166,47 +1220,30 @@ PyTypeObject connectionType = {
0, /*tp_as_sequence*/ 0, /*tp_as_sequence*/
0, /*tp_as_mapping*/ 0, /*tp_as_mapping*/
0, /*tp_hash */ 0, /*tp_hash */
0, /*tp_call*/ 0, /*tp_call*/
(reprfunc)connection_repr, /*tp_str*/ (reprfunc)connection_repr, /*tp_str*/
0, /*tp_getattro*/ 0, /*tp_getattro*/
0, /*tp_setattro*/ 0, /*tp_setattro*/
0, /*tp_as_buffer*/ 0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC |
Py_TPFLAGS_HAVE_WEAKREFS, Py_TPFLAGS_HAVE_WEAKREFS,
/*tp_flags*/ /*tp_flags*/
connectionType_doc, /*tp_doc*/ connectionType_doc, /*tp_doc*/
(traverseproc)connection_traverse, /*tp_traverse*/ (traverseproc)connection_traverse, /*tp_traverse*/
0, /*tp_clear*/ (inquiry)connection_clear, /*tp_clear*/
0, /*tp_richcompare*/ 0, /*tp_richcompare*/
offsetof(connectionObject, weakreflist), /* tp_weaklistoffset */ offsetof(connectionObject, weakreflist), /* tp_weaklistoffset */
0, /*tp_iter*/ 0, /*tp_iter*/
0, /*tp_iternext*/ 0, /*tp_iternext*/
/* Attribute descriptor and subclassing stuff */
connectionObject_methods, /*tp_methods*/ connectionObject_methods, /*tp_methods*/
connectionObject_members, /*tp_members*/ connectionObject_members, /*tp_members*/
connectionObject_getsets, /*tp_getset*/ connectionObject_getsets, /*tp_getset*/
0, /*tp_base*/ 0, /*tp_base*/
0, /*tp_dict*/ 0, /*tp_dict*/
0, /*tp_descr_get*/ 0, /*tp_descr_get*/
0, /*tp_descr_set*/ 0, /*tp_descr_set*/
0, /*tp_dictoffset*/ 0, /*tp_dictoffset*/
connection_init, /*tp_init*/ connection_init, /*tp_init*/
0, /*tp_alloc will be set to PyType_GenericAlloc in module init*/ 0, /*tp_alloc*/
connection_new, /*tp_new*/ 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*/
}; };

View File

@ -44,6 +44,11 @@ struct cursorObject {
int notuples:1; /* 1 if the command was not a SELECT query */ 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 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 rowcount; /* number of rows affected by last execute */
long int columns; /* number of columns fetched from the db */ long int columns; /* number of columns fetched from the db */
long int arraysize; /* how many rows should fetchmany() return */ 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); BORROWED HIDDEN PyObject *curs_get_cast(cursorObject *self, PyObject *oid);
HIDDEN void curs_reset(cursorObject *self); 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 */ /* exception-raising macros */
#define EXC_IF_CURS_CLOSED(self) \ #define EXC_IF_CURS_CLOSED(self) \

View File

@ -72,19 +72,11 @@ curs_get_cast(cursorObject *self, PyObject *oid)
void void
curs_reset(cursorObject *self) curs_reset(cursorObject *self)
{ {
PyObject *tmp;
/* initialize some variables to default values */ /* initialize some variables to default values */
self->notuples = 1; self->notuples = 1;
self->rowcount = -1; self->rowcount = -1;
self->row = 0; self->row = 0;
tmp = self->description; Py_CLEAR(self->description);
Py_INCREF(Py_None); Py_CLEAR(self->casts);
self->description = Py_None;
Py_XDECREF(tmp);
tmp = self->casts;
self->casts = NULL;
Py_XDECREF(tmp);
} }

View File

@ -50,7 +50,7 @@ extern PyObject *pyPsycopgTzFixedOffsetTimezone;
"close() -- Close the cursor." "close() -- Close the cursor."
static PyObject * static PyObject *
psyco_curs_close(cursorObject *self, PyObject *args) psyco_curs_close(cursorObject *self)
{ {
EXC_IF_ASYNC_IN_PROGRESS(self, close); 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); Dprintf("psyco_curs_close: cursor at %p closed", self);
exit: exit:
Py_INCREF(Py_None); Py_RETURN_NONE;
return Py_None;
} }
@ -118,7 +117,7 @@ _mogrify(PyObject *var, PyObject *fmt, cursorObject *curs, PyObject **new)
if (kind == 2) { if (kind == 2) {
Py_XDECREF(n); Py_XDECREF(n);
psyco_set_error(ProgrammingError, curs, psyco_set_error(ProgrammingError, curs,
"argument formats can't be mixed", NULL, NULL); "argument formats can't be mixed");
return -1; return -1;
} }
kind = 1; kind = 1;
@ -190,7 +189,7 @@ _mogrify(PyObject *var, PyObject *fmt, cursorObject *curs, PyObject **new)
/* we found %( but not a ) */ /* we found %( but not a ) */
Py_XDECREF(n); Py_XDECREF(n);
psyco_set_error(ProgrammingError, curs, psyco_set_error(ProgrammingError, curs,
"incomplete placeholder: '%(' without ')'", NULL, NULL); "incomplete placeholder: '%(' without ')'");
return -1; return -1;
} }
c = d + 1; /* after the ) */ c = d + 1; /* after the ) */
@ -205,7 +204,7 @@ _mogrify(PyObject *var, PyObject *fmt, cursorObject *curs, PyObject **new)
if (kind == 1) { if (kind == 1) {
Py_XDECREF(n); Py_XDECREF(n);
psyco_set_error(ProgrammingError, curs, psyco_set_error(ProgrammingError, curs,
"argument formats can't be mixed", NULL, NULL); "argument formats can't be mixed");
return -1; return -1;
} }
kind = 2; kind = 2;
@ -267,7 +266,7 @@ static PyObject *_psyco_curs_validate_sql_basic(
if (!sql || !PyObject_IsTrue(sql)) { if (!sql || !PyObject_IsTrue(sql)) {
psyco_set_error(ProgrammingError, self, psyco_set_error(ProgrammingError, self,
"can't execute an empty query", NULL, NULL); "can't execute an empty query");
goto fail; goto fail;
} }
@ -338,8 +337,7 @@ _psyco_curs_merge_query_args(cursorObject *self,
if (!strcmp(s, "not enough arguments for format string") if (!strcmp(s, "not enough arguments for format string")
|| !strcmp(s, "not all arguments converted")) { || !strcmp(s, "not all arguments converted")) {
Dprintf("psyco_curs_execute: -> got a match"); Dprintf("psyco_curs_execute: -> got a match");
psyco_set_error(ProgrammingError, self, psyco_set_error(ProgrammingError, self, s);
s, NULL, NULL);
pe = 1; pe = 1;
} }
@ -371,6 +369,7 @@ _psyco_curs_execute(cursorObject *self,
int res = -1; int res = -1;
int tmp; int tmp;
PyObject *fquery, *cvt = NULL; PyObject *fquery, *cvt = NULL;
const char *scroll;
operation = _psyco_curs_validate_sql_basic(self, operation); operation = _psyco_curs_validate_sql_basic(self, operation);
@ -379,13 +378,8 @@ _psyco_curs_execute(cursorObject *self,
if (operation == NULL) { goto exit; } if (operation == NULL) { goto exit; }
IFCLEARPGRES(self->pgres); CLEARPGRES(self->pgres);
Py_CLEAR(self->query);
if (self->query) {
Py_DECREF(self->query);
self->query = NULL;
}
Dprintf("psyco_curs_execute: starting execution of new query"); Dprintf("psyco_curs_execute: starting execution of new query");
/* here we are, and we have a sequence or a dictionary filled with /* 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; } 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 (vars && cvt) {
if (!(fquery = _psyco_curs_merge_query_args(self, operation, cvt))) { if (!(fquery = _psyco_curs_merge_query_args(self, operation, cvt))) {
goto exit; goto exit;
@ -404,8 +413,9 @@ _psyco_curs_execute(cursorObject *self,
if (self->name != NULL) { if (self->name != NULL) {
self->query = Bytes_FromFormat( self->query = Bytes_FromFormat(
"DECLARE \"%s\" CURSOR %s HOLD FOR %s", "DECLARE \"%s\" %sCURSOR %s HOLD FOR %s",
self->name, self->name,
scroll,
self->withhold ? "WITH" : "WITHOUT", self->withhold ? "WITH" : "WITHOUT",
Bytes_AS_STRING(fquery)); Bytes_AS_STRING(fquery));
Py_DECREF(fquery); Py_DECREF(fquery);
@ -417,8 +427,9 @@ _psyco_curs_execute(cursorObject *self,
else { else {
if (self->name != NULL) { if (self->name != NULL) {
self->query = Bytes_FromFormat( self->query = Bytes_FromFormat(
"DECLARE \"%s\" CURSOR %s HOLD FOR %s", "DECLARE \"%s\" %sCURSOR %s HOLD FOR %s",
self->name, self->name,
scroll,
self->withhold ? "WITH" : "WITHOUT", self->withhold ? "WITH" : "WITHOUT",
Bytes_AS_STRING(operation)); Bytes_AS_STRING(operation));
} }
@ -462,15 +473,14 @@ psyco_curs_execute(cursorObject *self, PyObject *args, PyObject *kwargs)
} }
if (self->name != NULL) { if (self->name != NULL) {
if (self->query != Py_None) { if (self->query) {
psyco_set_error(ProgrammingError, self, psyco_set_error(ProgrammingError, self,
"can't call .execute() on named cursors more than once", "can't call .execute() on named cursors more than once");
NULL, NULL);
return NULL; return NULL;
} }
if (self->conn->autocommit) { if (self->conn->autocommit) {
psyco_set_error(ProgrammingError, self, 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; return NULL;
} }
EXC_IF_NO_MARK(self); EXC_IF_NO_MARK(self);
@ -485,8 +495,7 @@ psyco_curs_execute(cursorObject *self, PyObject *args, PyObject *kwargs)
} }
/* success */ /* success */
Py_INCREF(Py_None); Py_RETURN_NONE;
return Py_None;
} }
#define psyco_curs_executemany_doc \ #define psyco_curs_executemany_doc \
@ -515,7 +524,7 @@ psyco_curs_executemany(cursorObject *self, PyObject *args, PyObject *kwargs)
if (self->name != NULL) { if (self->name != NULL) {
psyco_set_error(ProgrammingError, self, psyco_set_error(ProgrammingError, self,
"can't call .executemany() on named cursors", NULL, NULL); "can't call .executemany() on named cursors");
return NULL; return NULL;
} }
@ -542,8 +551,7 @@ psyco_curs_executemany(cursorObject *self, PyObject *args, PyObject *kwargs)
self->rowcount = rowcount; self->rowcount = rowcount;
if (!PyErr_Occurred()) { if (!PyErr_Occurred()) {
Py_INCREF(Py_None); Py_RETURN_NONE;
return Py_None;
} }
else { else {
return NULL; return NULL;
@ -743,7 +751,7 @@ exit:
} }
static PyObject * static PyObject *
psyco_curs_fetchone(cursorObject *self, PyObject *args) psyco_curs_fetchone(cursorObject *self)
{ {
PyObject *res; PyObject *res;
@ -767,8 +775,7 @@ psyco_curs_fetchone(cursorObject *self, PyObject *args)
if (self->row >= self->rowcount) { if (self->row >= self->rowcount) {
/* we exausted available data: return None */ /* we exausted available data: return None */
Py_INCREF(Py_None); Py_RETURN_NONE;
return Py_None;
} }
res = _psyco_curs_buildrow(self, self->row); res = _psyco_curs_buildrow(self, self->row);
@ -779,7 +786,7 @@ psyco_curs_fetchone(cursorObject *self, PyObject *args)
if (self->row >= self->rowcount if (self->row >= self->rowcount
&& self->conn->async_cursor && self->conn->async_cursor
&& PyWeakref_GetObject(self->conn->async_cursor) == (PyObject*)self) && PyWeakref_GetObject(self->conn->async_cursor) == (PyObject*)self)
IFCLEARPGRES(self->pgres); CLEARPGRES(self->pgres);
return res; return res;
} }
@ -826,7 +833,7 @@ psyco_curs_next_named(cursorObject *self)
if (self->row >= self->rowcount if (self->row >= self->rowcount
&& self->conn->async_cursor && self->conn->async_cursor
&& PyWeakref_GetObject(self->conn->async_cursor) == (PyObject*)self) && PyWeakref_GetObject(self->conn->async_cursor) == (PyObject*)self)
IFCLEARPGRES(self->pgres); CLEARPGRES(self->pgres);
return res; return res;
} }
@ -911,7 +918,7 @@ psyco_curs_fetchmany(cursorObject *self, PyObject *args, PyObject *kwords)
if (self->row >= self->rowcount if (self->row >= self->rowcount
&& self->conn->async_cursor && self->conn->async_cursor
&& PyWeakref_GetObject(self->conn->async_cursor) == (PyObject*)self) && PyWeakref_GetObject(self->conn->async_cursor) == (PyObject*)self)
IFCLEARPGRES(self->pgres); CLEARPGRES(self->pgres);
/* success */ /* success */
rv = list; rv = list;
@ -935,7 +942,7 @@ exit:
"Return `!None` when no more data is available.\n" "Return `!None` when no more data is available.\n"
static PyObject * static PyObject *
psyco_curs_fetchall(cursorObject *self, PyObject *args) psyco_curs_fetchall(cursorObject *self)
{ {
int i, size; int i, size;
PyObject *list = NULL; PyObject *list = NULL;
@ -980,7 +987,7 @@ psyco_curs_fetchall(cursorObject *self, PyObject *args)
if (self->row >= self->rowcount if (self->row >= self->rowcount
&& self->conn->async_cursor && self->conn->async_cursor
&& PyWeakref_GetObject(self->conn->async_cursor) == (PyObject*)self) && PyWeakref_GetObject(self->conn->async_cursor) == (PyObject*)self)
IFCLEARPGRES(self->pgres); CLEARPGRES(self->pgres);
/* success */ /* success */
rv = list; rv = list;
@ -1020,7 +1027,7 @@ psyco_curs_callproc(cursorObject *self, PyObject *args)
if (self->name != NULL) { if (self->name != NULL) {
psyco_set_error(ProgrammingError, self, psyco_set_error(ProgrammingError, self,
"can't call .callproc() on named cursors", NULL, NULL); "can't call .callproc() on named cursors");
goto exit; goto exit;
} }
@ -1067,7 +1074,7 @@ exit:
"sets) and will raise a NotSupportedError exception." "sets) and will raise a NotSupportedError exception."
static PyObject * static PyObject *
psyco_curs_nextset(cursorObject *self, PyObject *args) psyco_curs_nextset(cursorObject *self)
{ {
EXC_IF_CURS_CLOSED(self); EXC_IF_CURS_CLOSED(self);
@ -1092,8 +1099,7 @@ psyco_curs_setinputsizes(cursorObject *self, PyObject *args)
EXC_IF_CURS_CLOSED(self); EXC_IF_CURS_CLOSED(self);
Py_INCREF(Py_None); Py_RETURN_NONE;
return Py_None;
} }
@ -1113,8 +1119,7 @@ psyco_curs_setoutputsize(cursorObject *self, PyObject *args)
EXC_IF_CURS_CLOSED(self); EXC_IF_CURS_CLOSED(self);
Py_INCREF(Py_None); Py_RETURN_NONE;
return Py_None;
} }
@ -1147,13 +1152,13 @@ psyco_curs_scroll(cursorObject *self, PyObject *args, PyObject *kwargs)
newpos = value; newpos = value;
} else { } else {
psyco_set_error(ProgrammingError, self, psyco_set_error(ProgrammingError, self,
"scroll mode must be 'relative' or 'absolute'", NULL, NULL); "scroll mode must be 'relative' or 'absolute'");
return NULL; return NULL;
} }
if (newpos < 0 || newpos >= self->rowcount ) { if (newpos < 0 || newpos >= self->rowcount ) {
psyco_set_error(ProgrammingError, self, psyco_set_error(ProgrammingError, self,
"scroll destination out of bounds", NULL, NULL); "scroll destination out of bounds");
return NULL; return NULL;
} }
@ -1178,8 +1183,43 @@ psyco_curs_scroll(cursorObject *self, PyObject *args, PyObject *kwargs)
if (_psyco_curs_prefetch(self) < 0) return NULL; if (_psyco_curs_prefetch(self) < 0) return NULL;
} }
Py_INCREF(Py_None); Py_RETURN_NONE;
return Py_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; PyObject *file, *columns = NULL, *res = NULL;
if (!PyArg_ParseTupleAndKeywords(args, kwargs, 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, _psyco_curs_has_read_check, &file, &table_name, &sep, &null, &bufsize,
&columns)) &columns))
{ {
@ -1327,14 +1367,12 @@ psyco_curs_copy_from(cursorObject *self, PyObject *args, PyObject *kwargs)
goto exit; goto exit;
if (!(quoted_delimiter = psycopg_escape_string( if (!(quoted_delimiter = psycopg_escape_string(
(PyObject*)self->conn, sep, 0, NULL, NULL))) { self->conn, sep, 0, NULL, NULL))) {
PyErr_NoMemory();
goto exit; goto exit;
} }
if (!(quoted_null = psycopg_escape_string( if (!(quoted_null = psycopg_escape_string(
(PyObject*)self->conn, null, 0, NULL, NULL))) { self->conn, null, 0, NULL, NULL))) {
PyErr_NoMemory();
goto exit; goto exit;
} }
@ -1423,14 +1461,12 @@ psyco_curs_copy_to(cursorObject *self, PyObject *args, PyObject *kwargs)
goto exit; goto exit;
if (!(quoted_delimiter = psycopg_escape_string( if (!(quoted_delimiter = psycopg_escape_string(
(PyObject*)self->conn, sep, 0, NULL, NULL))) { self->conn, sep, 0, NULL, NULL))) {
PyErr_NoMemory();
goto exit; goto exit;
} }
if (!(quoted_null = psycopg_escape_string( if (!(quoted_null = psycopg_escape_string(
(PyObject*)self->conn, null, 0, NULL, NULL))) { self->conn, null, 0, NULL, NULL))) {
PyErr_NoMemory();
goto exit; goto exit;
} }
@ -1488,7 +1524,7 @@ psyco_curs_copy_expert(cursorObject *self, PyObject *args, PyObject *kwargs)
static char *kwlist[] = {"sql", "file", "size", NULL}; static char *kwlist[] = {"sql", "file", "size", NULL};
if (!PyArg_ParseTupleAndKeywords(args, kwargs, if (!PyArg_ParseTupleAndKeywords(args, kwargs,
"OO|" CONV_CODE_PY_SSIZE_T, kwlist, &sql, &file, &bufsize)) "OO|n", kwlist, &sql, &file, &bufsize))
{ return NULL; } { return NULL; }
EXC_IF_CURS_CLOSED(self); EXC_IF_CURS_CLOSED(self);
@ -1567,12 +1603,12 @@ psyco_curs_withhold_get(cursorObject *self)
return ret; return ret;
} }
static int int
psyco_curs_withhold_set(cursorObject *self, PyObject *pyvalue) psyco_curs_withhold_set(cursorObject *self, PyObject *pyvalue)
{ {
int value; int value;
if (self->name == NULL) { if (pyvalue != Py_False && self->name == NULL) {
PyErr_SetString(ProgrammingError, PyErr_SetString(ProgrammingError,
"trying to set .withhold on unnamed cursor"); "trying to set .withhold on unnamed cursor");
return -1; return -1;
@ -1586,6 +1622,54 @@ psyco_curs_withhold_set(cursorObject *self, PyObject *pyvalue)
return 0; 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;
}
#endif #endif
@ -1608,7 +1692,7 @@ cursor_next(PyObject *self)
if (NULL == ((cursorObject*)self)->name) { if (NULL == ((cursorObject*)self)->name) {
/* we don't parse arguments: psyco_curs_fetchone will do that for us */ /* 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 */ /* convert a None to NULL to signal the end of iteration */
if (res && res == Py_None) { if (res && res == Py_None) {
@ -1650,6 +1734,10 @@ static struct PyMethodDef cursorObject_methods[] = {
/* DBAPI-2.0 extensions */ /* DBAPI-2.0 extensions */
{"scroll", (PyCFunction)psyco_curs_scroll, {"scroll", (PyCFunction)psyco_curs_scroll,
METH_VARARGS|METH_KEYWORDS, psyco_curs_scroll_doc}, 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 */ /* psycopg extensions */
#ifdef PSYCOPG_EXTENSIONS #ifdef PSYCOPG_EXTENSIONS
{"cast", (PyCFunction)psyco_curs_cast, {"cast", (PyCFunction)psyco_curs_cast,
@ -1712,6 +1800,10 @@ static struct PyGetSetDef cursorObject_getsets[] = {
(getter)psyco_curs_withhold_get, (getter)psyco_curs_withhold_get,
(setter)psyco_curs_withhold_set, (setter)psyco_curs_withhold_set,
psyco_curs_withhold_doc, NULL }, psyco_curs_withhold_doc, NULL },
{ "scrollable",
(getter)psyco_curs_scrollable_get,
(setter)psyco_curs_scrollable_set,
psyco_curs_scrollable_doc, NULL },
#endif #endif
{NULL} {NULL}
}; };
@ -1740,31 +1832,15 @@ cursor_setup(cursorObject *self, connectionObject *conn, const char *name)
Py_INCREF(conn); Py_INCREF(conn);
self->conn = conn; self->conn = conn;
self->closed = 0;
self->withhold = 0;
self->mark = conn->mark; self->mark = conn->mark;
self->pgres = NULL;
self->notuples = 1; self->notuples = 1;
self->arraysize = 1; self->arraysize = 1;
self->itersize = 2000; self->itersize = 2000;
self->rowcount = -1; self->rowcount = -1;
self->lastoid = InvalidOid; 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); Py_INCREF(Py_None);
self->tuple_factory = Py_None; self->tuple_factory = Py_None;
Py_INCREF(Py_None);
self->query = Py_None;
/* default tzinfo factory */ /* default tzinfo factory */
Py_INCREF(pyPsycopgTzFixedOffsetTimezone); Py_INCREF(pyPsycopgTzFixedOffsetTimezone);
@ -1777,30 +1853,39 @@ cursor_setup(cursorObject *self, connectionObject *conn, const char *name)
return 0; return 0;
} }
static void static int
cursor_dealloc(PyObject* obj) cursor_clear(cursorObject *self)
{ {
cursorObject *self = (cursorObject *)obj;
if (self->weakreflist) {
PyObject_ClearWeakRefs(obj);
}
PyObject_GC_UnTrack(self);
PyMem_Free(self->name);
Py_CLEAR(self->conn); Py_CLEAR(self->conn);
Py_CLEAR(self->casts);
Py_CLEAR(self->description); Py_CLEAR(self->description);
Py_CLEAR(self->pgstatus); Py_CLEAR(self->pgstatus);
Py_CLEAR(self->casts);
Py_CLEAR(self->caster);
Py_CLEAR(self->copyfile);
Py_CLEAR(self->tuple_factory); Py_CLEAR(self->tuple_factory);
Py_CLEAR(self->tzinfo_factory); Py_CLEAR(self->tzinfo_factory);
Py_CLEAR(self->query); Py_CLEAR(self->query);
Py_CLEAR(self->string_types); Py_CLEAR(self->string_types);
Py_CLEAR(self->binary_types); Py_CLEAR(self->binary_types);
return 0;
}
IFCLEARPGRES(self->pgres); static void
cursor_dealloc(PyObject* obj)
{
cursorObject *self = (cursorObject *)obj;
PyObject_GC_UnTrack(self);
if (self->weakreflist) {
PyObject_ClearWeakRefs(obj);
}
cursor_clear(self);
PyMem_Free(self->name);
CLEARPGRES(self->pgres);
Dprintf("cursor_dealloc: deleted cursor object at %p, refcnt = " Dprintf("cursor_dealloc: deleted cursor object at %p, refcnt = "
FORMAT_CODE_PY_SSIZE_T, FORMAT_CODE_PY_SSIZE_T,
@ -1847,12 +1932,6 @@ cursor_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
return type->tp_alloc(type, 0); return type->tp_alloc(type, 0);
} }
static void
cursor_del(PyObject* self)
{
PyObject_GC_Del(self);
}
static PyObject * static PyObject *
cursor_repr(cursorObject *self) cursor_repr(cursorObject *self)
{ {
@ -1886,8 +1965,7 @@ cursor_traverse(cursorObject *self, visitproc visit, void *arg)
PyTypeObject cursorType = { PyTypeObject cursorType = {
PyVarObject_HEAD_INIT(NULL, 0) PyVarObject_HEAD_INIT(NULL, 0)
"psycopg2._psycopg.cursor", "psycopg2._psycopg.cursor",
sizeof(cursorObject), sizeof(cursorObject), 0,
0,
cursor_dealloc, /*tp_dealloc*/ cursor_dealloc, /*tp_dealloc*/
0, /*tp_print*/ 0, /*tp_print*/
0, /*tp_getattr*/ 0, /*tp_getattr*/
@ -1898,47 +1976,30 @@ PyTypeObject cursorType = {
0, /*tp_as_sequence*/ 0, /*tp_as_sequence*/
0, /*tp_as_mapping*/ 0, /*tp_as_mapping*/
0, /*tp_hash */ 0, /*tp_hash */
0, /*tp_call*/ 0, /*tp_call*/
(reprfunc)cursor_repr, /*tp_str*/ (reprfunc)cursor_repr, /*tp_str*/
0, /*tp_getattro*/ 0, /*tp_getattro*/
0, /*tp_setattro*/ 0, /*tp_setattro*/
0, /*tp_as_buffer*/ 0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_ITER | Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_ITER |
Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_HAVE_WEAKREFS , Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_HAVE_WEAKREFS ,
/*tp_flags*/ /*tp_flags*/
cursorType_doc, /*tp_doc*/ cursorType_doc, /*tp_doc*/
(traverseproc)cursor_traverse, /*tp_traverse*/ (traverseproc)cursor_traverse, /*tp_traverse*/
0, /*tp_clear*/ (inquiry)cursor_clear, /*tp_clear*/
0, /*tp_richcompare*/ 0, /*tp_richcompare*/
offsetof(cursorObject, weakreflist), /*tp_weaklistoffset*/ offsetof(cursorObject, weakreflist), /*tp_weaklistoffset*/
cursor_iter, /*tp_iter*/ cursor_iter, /*tp_iter*/
cursor_next, /*tp_iternext*/ cursor_next, /*tp_iternext*/
/* Attribute descriptor and subclassing stuff */
cursorObject_methods, /*tp_methods*/ cursorObject_methods, /*tp_methods*/
cursorObject_members, /*tp_members*/ cursorObject_members, /*tp_members*/
cursorObject_getsets, /*tp_getset*/ cursorObject_getsets, /*tp_getset*/
0, /*tp_base*/ 0, /*tp_base*/
0, /*tp_dict*/ 0, /*tp_dict*/
0, /*tp_descr_get*/ 0, /*tp_descr_get*/
0, /*tp_descr_set*/ 0, /*tp_descr_set*/
0, /*tp_dictoffset*/ 0, /*tp_dictoffset*/
cursor_init, /*tp_init*/ cursor_init, /*tp_init*/
0, /*tp_alloc Will be set to PyType_GenericAlloc in module init*/ 0, /*tp_alloc*/
cursor_new, /*tp_new*/ 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*/
}; };

40
psycopg/diagnostics.h Normal file
View File

@ -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 */

197
psycopg/diagnostics_type.c Normal file
View File

@ -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*/
};

43
psycopg/error.h Normal file
View File

@ -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 */

276
psycopg/error_type.c Normal file
View File

@ -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*/
};

View File

@ -53,8 +53,7 @@ psyco_set_wait_callback(PyObject *self, PyObject *obj)
wait_callback = NULL; wait_callback = NULL;
} }
Py_INCREF(Py_None); Py_RETURN_NONE;
return Py_None;
} }

View File

@ -78,13 +78,13 @@ RAISES_NEG HIDDEN int lobject_close(lobjectObject *self);
#define EXC_IF_LOBJ_LEVEL0(self) \ #define EXC_IF_LOBJ_LEVEL0(self) \
if (self->conn->autocommit) { \ if (self->conn->autocommit) { \
psyco_set_error(ProgrammingError, NULL, \ 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; \ return NULL; \
} }
#define EXC_IF_LOBJ_UNMARKED(self) \ #define EXC_IF_LOBJ_UNMARKED(self) \
if (self->conn->mark != self->mark) { \ if (self->conn->mark != self->mark) { \
psyco_set_error(ProgrammingError, NULL, \ psyco_set_error(ProgrammingError, NULL, \
"lobject isn't valid anymore", NULL, NULL); \ "lobject isn't valid anymore"); \
return NULL; \ return NULL; \
} }

View File

@ -59,8 +59,7 @@ psyco_lobj_close(lobjectObject *self, PyObject *args)
return NULL; return NULL;
} }
Py_INCREF(Py_None); Py_RETURN_NONE;
return Py_None;
} }
/* write method - write data to the lobject */ /* write method - write data to the lobject */
@ -128,7 +127,7 @@ psyco_lobj_read(lobjectObject *self, PyObject *args)
Py_ssize_t size = -1; Py_ssize_t size = -1;
char *buffer; 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_CLOSED(self);
EXC_IF_LOBJ_LEVEL0(self); EXC_IF_LOBJ_LEVEL0(self);
@ -215,8 +214,7 @@ psyco_lobj_unlink(lobjectObject *self, PyObject *args)
if (lobject_unlink(self) < 0) if (lobject_unlink(self) < 0)
return NULL; return NULL;
Py_INCREF(Py_None); Py_RETURN_NONE;
return Py_None;
} }
/* export method - export lobject's content to given file */ /* export method - export lobject's content to given file */
@ -237,8 +235,7 @@ psyco_lobj_export(lobjectObject *self, PyObject *args)
if (lobject_export(self, filename) < 0) if (lobject_export(self, filename) < 0)
return NULL; return NULL;
Py_INCREF(Py_None); Py_RETURN_NONE;
return Py_None;
} }
@ -272,8 +269,7 @@ psyco_lobj_truncate(lobjectObject *self, PyObject *args)
if (lobject_truncate(self, len) < 0) if (lobject_truncate(self, len) < 0)
return NULL; return NULL;
Py_INCREF(Py_None); Py_RETURN_NONE;
return Py_None;
} }
#endif /* PG_VERSION_HEX >= 0x080300 */ #endif /* PG_VERSION_HEX >= 0x080300 */
@ -333,7 +329,7 @@ lobject_setup(lobjectObject *self, connectionObject *conn,
if (conn->autocommit) { if (conn->autocommit) {
psyco_set_error(ProgrammingError, NULL, 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; return -1;
} }
@ -392,12 +388,6 @@ lobject_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
return type->tp_alloc(type, 0); return type->tp_alloc(type, 0);
} }
static void
lobject_del(PyObject* self)
{
PyObject_Del(self);
}
static PyObject * static PyObject *
lobject_repr(lobjectObject *self) lobject_repr(lobjectObject *self)
{ {
@ -414,8 +404,7 @@ lobject_repr(lobjectObject *self)
PyTypeObject lobjectType = { PyTypeObject lobjectType = {
PyVarObject_HEAD_INIT(NULL, 0) PyVarObject_HEAD_INIT(NULL, 0)
"psycopg2._psycopg.lobject", "psycopg2._psycopg.lobject",
sizeof(lobjectObject), sizeof(lobjectObject), 0,
0,
lobject_dealloc, /*tp_dealloc*/ lobject_dealloc, /*tp_dealloc*/
0, /*tp_print*/ 0, /*tp_print*/
0, /*tp_getattr*/ 0, /*tp_getattr*/
@ -426,47 +415,30 @@ PyTypeObject lobjectType = {
0, /*tp_as_sequence*/ 0, /*tp_as_sequence*/
0, /*tp_as_mapping*/ 0, /*tp_as_mapping*/
0, /*tp_hash */ 0, /*tp_hash */
0, /*tp_call*/ 0, /*tp_call*/
(reprfunc)lobject_repr, /*tp_str*/ (reprfunc)lobject_repr, /*tp_str*/
0, /*tp_getattro*/ 0, /*tp_getattro*/
0, /*tp_setattro*/ 0, /*tp_setattro*/
0, /*tp_as_buffer*/ 0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_HAVE_ITER, /*tp_flags*/ Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_HAVE_ITER, /*tp_flags*/
lobjectType_doc, /*tp_doc*/ lobjectType_doc, /*tp_doc*/
0, /*tp_traverse*/ 0, /*tp_traverse*/
0, /*tp_clear*/ 0, /*tp_clear*/
0, /*tp_richcompare*/ 0, /*tp_richcompare*/
0, /*tp_weaklistoffset*/ 0, /*tp_weaklistoffset*/
0, /*tp_iter*/ 0, /*tp_iter*/
0, /*tp_iternext*/ 0, /*tp_iternext*/
/* Attribute descriptor and subclassing stuff */
lobjectObject_methods, /*tp_methods*/ lobjectObject_methods, /*tp_methods*/
lobjectObject_members, /*tp_members*/ lobjectObject_members, /*tp_members*/
lobjectObject_getsets, /*tp_getset*/ lobjectObject_getsets, /*tp_getset*/
0, /*tp_base*/ 0, /*tp_base*/
0, /*tp_dict*/ 0, /*tp_dict*/
0, /*tp_descr_get*/ 0, /*tp_descr_get*/
0, /*tp_descr_set*/ 0, /*tp_descr_set*/
0, /*tp_dictoffset*/ 0, /*tp_dictoffset*/
lobject_init, /*tp_init*/ lobject_init, /*tp_init*/
0, /*tp_alloc Will be set to PyType_GenericAlloc in module init*/ 0, /*tp_alloc*/
lobject_new, /*tp_new*/ 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 #endif

View File

@ -203,7 +203,7 @@ microprotocols_adapt(PyObject *obj, PyObject *proto, PyObject *alt)
/* else set the right exception and return NULL */ /* else set the right exception and return NULL */
PyOS_snprintf(buffer, 255, "can't adapt type '%s'", PyOS_snprintf(buffer, 255, "can't adapt type '%s'",
Py_TYPE(obj)->tp_name); Py_TYPE(obj)->tp_name);
psyco_set_error(ProgrammingError, NULL, buffer, NULL, NULL); psyco_set_error(ProgrammingError, NULL, buffer);
return NULL; return NULL;
} }

View File

@ -42,8 +42,7 @@
static PyObject * static PyObject *
psyco_isqlquote_getquoted(isqlquoteObject *self, PyObject *args) psyco_isqlquote_getquoted(isqlquoteObject *self, PyObject *args)
{ {
Py_INCREF(Py_None); Py_RETURN_NONE;
return Py_None;
} }
/* getbinary - return quoted representation for object */ /* getbinary - return quoted representation for object */
@ -54,8 +53,7 @@ psyco_isqlquote_getquoted(isqlquoteObject *self, PyObject *args)
static PyObject * static PyObject *
psyco_isqlquote_getbinary(isqlquoteObject *self, PyObject *args) psyco_isqlquote_getbinary(isqlquoteObject *self, PyObject *args)
{ {
Py_INCREF(Py_None); Py_RETURN_NONE;
return Py_None;
} }
/* getbuffer - return quoted representation for object */ /* getbuffer - return quoted representation for object */
@ -66,8 +64,7 @@ psyco_isqlquote_getbinary(isqlquoteObject *self, PyObject *args)
static PyObject * static PyObject *
psyco_isqlquote_getbuffer(isqlquoteObject *self, PyObject *args) psyco_isqlquote_getbuffer(isqlquoteObject *self, PyObject *args)
{ {
Py_INCREF(Py_None); Py_RETURN_NONE;
return Py_None;
} }
@ -135,12 +132,6 @@ isqlquote_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
return type->tp_alloc(type, 0); return type->tp_alloc(type, 0);
} }
static void
isqlquote_del(PyObject* self)
{
PyObject_Del(self);
}
/* object type */ /* object type */
@ -152,8 +143,7 @@ isqlquote_del(PyObject* self)
PyTypeObject isqlquoteType = { PyTypeObject isqlquoteType = {
PyVarObject_HEAD_INIT(NULL, 0) PyVarObject_HEAD_INIT(NULL, 0)
"psycopg2._psycopg.ISQLQuote", "psycopg2._psycopg.ISQLQuote",
sizeof(isqlquoteObject), sizeof(isqlquoteObject), 0,
0,
isqlquote_dealloc, /*tp_dealloc*/ isqlquote_dealloc, /*tp_dealloc*/
0, /*tp_print*/ 0, /*tp_print*/
0, /*tp_getattr*/ 0, /*tp_getattr*/
@ -164,45 +154,28 @@ PyTypeObject isqlquoteType = {
0, /*tp_as_sequence*/ 0, /*tp_as_sequence*/
0, /*tp_as_mapping*/ 0, /*tp_as_mapping*/
0, /*tp_hash */ 0, /*tp_hash */
0, /*tp_call*/ 0, /*tp_call*/
0, /*tp_str*/ 0, /*tp_str*/
0, /*tp_getattro*/ 0, /*tp_getattro*/
0, /*tp_setattro*/ 0, /*tp_setattro*/
0, /*tp_as_buffer*/ 0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /*tp_flags*/ Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /*tp_flags*/
isqlquoteType_doc, /*tp_doc*/ isqlquoteType_doc, /*tp_doc*/
0, /*tp_traverse*/ 0, /*tp_traverse*/
0, /*tp_clear*/ 0, /*tp_clear*/
0, /*tp_richcompare*/ 0, /*tp_richcompare*/
0, /*tp_weaklistoffset*/ 0, /*tp_weaklistoffset*/
0, /*tp_iter*/ 0, /*tp_iter*/
0, /*tp_iternext*/ 0, /*tp_iternext*/
/* Attribute descriptor and subclassing stuff */
isqlquoteObject_methods, /*tp_methods*/ isqlquoteObject_methods, /*tp_methods*/
isqlquoteObject_members, /*tp_members*/ isqlquoteObject_members, /*tp_members*/
0, /*tp_getset*/ 0, /*tp_getset*/
0, /*tp_base*/ 0, /*tp_base*/
0, /*tp_dict*/ 0, /*tp_dict*/
0, /*tp_descr_get*/ 0, /*tp_descr_get*/
0, /*tp_descr_set*/ 0, /*tp_descr_set*/
0, /*tp_dictoffset*/ 0, /*tp_dictoffset*/
isqlquote_init, /*tp_init*/ isqlquote_init, /*tp_init*/
0, /*tp_alloc will be set to PyType_GenericAlloc in module init*/ 0, /*tp_alloc*/
isqlquote_new, /*tp_new*/ 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*/
}; };

View File

@ -26,7 +26,7 @@
#ifndef PSYCOPG_NOTIFY_H #ifndef PSYCOPG_NOTIFY_H
#define PSYCOPG_NOTIFY_H 1 #define PSYCOPG_NOTIFY_H 1
extern HIDDEN PyTypeObject NotifyType; extern HIDDEN PyTypeObject notifyType;
typedef struct { typedef struct {
PyObject_HEAD PyObject_HEAD
@ -35,6 +35,6 @@ typedef struct {
PyObject *channel; PyObject *channel;
PyObject *payload; PyObject *payload;
} NotifyObject; } notifyObject;
#endif /* PSYCOPG_NOTIFY_H */ #endif /* PSYCOPG_NOTIFY_H */

View File

@ -52,22 +52,20 @@ static const char payload_doc[] =
"of the server this member is always the empty string."; "of the server this member is always the empty string.";
static PyMemberDef notify_members[] = { static PyMemberDef notify_members[] = {
{ "pid", T_OBJECT, offsetof(NotifyObject, pid), READONLY, (char *)pid_doc }, { "pid", T_OBJECT, offsetof(notifyObject, pid), READONLY, (char *)pid_doc },
{ "channel", T_OBJECT, offsetof(NotifyObject, channel), READONLY, (char *)channel_doc }, { "channel", T_OBJECT, offsetof(notifyObject, channel), READONLY, (char *)channel_doc },
{ "payload", T_OBJECT, offsetof(NotifyObject, payload), READONLY, (char *)payload_doc }, { "payload", T_OBJECT, offsetof(notifyObject, payload), READONLY, (char *)payload_doc },
{ NULL } { NULL }
}; };
static PyObject * static PyObject *
notify_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) notify_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
{ {
NotifyObject *self = (NotifyObject *)type->tp_alloc(type, 0); return type->tp_alloc(type, 0);
return (PyObject *)self;
} }
static int 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}; static char *kwlist[] = {"pid", "channel", "payload", NULL};
PyObject *pid = NULL, *channel = NULL, *payload = NULL; PyObject *pid = NULL, *channel = NULL, *payload = NULL;
@ -81,32 +79,20 @@ notify_init(NotifyObject *self, PyObject *args, PyObject *kwargs)
payload = Text_FromUTF8(""); payload = Text_FromUTF8("");
} }
Py_CLEAR(self->pid);
Py_INCREF(pid); Py_INCREF(pid);
self->pid = pid; self->pid = pid;
Py_CLEAR(self->channel);
Py_INCREF(channel); Py_INCREF(channel);
self->channel = channel; self->channel = channel;
Py_CLEAR(self->payload);
Py_INCREF(payload); Py_INCREF(payload);
self->payload = payload; self->payload = payload;
return 0; 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 static void
notify_dealloc(NotifyObject *self) notify_dealloc(notifyObject *self)
{ {
Py_CLEAR(self->pid); Py_CLEAR(self->pid);
Py_CLEAR(self->channel); Py_CLEAR(self->channel);
@ -115,16 +101,10 @@ notify_dealloc(NotifyObject *self)
Py_TYPE(self)->tp_free((PyObject *)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. */ /* Convert a notify into a 2 or 3 items tuple. */
static PyObject * static PyObject *
notify_astuple(NotifyObject *self, int with_payload) notify_astuple(notifyObject *self, int with_payload)
{ {
PyObject *tself; PyObject *tself;
if (!(tself = PyTuple_New(with_payload ? 3 : 2))) { return NULL; } 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. * the (pid, channel) pair is no more equivalent as dict key to the Notify.
*/ */
static PyObject * static PyObject *
notify_richcompare(NotifyObject *self, PyObject *other, int op) notify_richcompare(notifyObject *self, PyObject *other, int op)
{ {
PyObject *rv = NULL; PyObject *rv = NULL;
PyObject *tself = NULL; PyObject *tself = NULL;
PyObject *tother = NULL; PyObject *tother = NULL;
if (Py_TYPE(other) == &NotifyType) { if (Py_TYPE(other) == &notifyType) {
if (!(tself = notify_astuple(self, 1))) { goto exit; } 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); rv = PyObject_RichCompare(tself, tother, op);
} }
else if (PyTuple_Check(other)) { else if (PyTuple_Check(other)) {
@ -190,7 +170,7 @@ exit:
static Py_hash_t static Py_hash_t
notify_hash(NotifyObject *self) notify_hash(notifyObject *self)
{ {
Py_hash_t rv = -1L; Py_hash_t rv = -1L;
PyObject *tself = NULL; PyObject *tself = NULL;
@ -207,7 +187,7 @@ exit:
static PyObject* static PyObject*
notify_repr(NotifyObject *self) notify_repr(notifyObject *self)
{ {
PyObject *rv = NULL; PyObject *rv = NULL;
PyObject *format = NULL; PyObject *format = NULL;
@ -237,13 +217,13 @@ exit:
/* Notify can be accessed as a 2 items tuple for backward compatibility */ /* Notify can be accessed as a 2 items tuple for backward compatibility */
static Py_ssize_t static Py_ssize_t
notify_len(NotifyObject *self) notify_len(notifyObject *self)
{ {
return 2; return 2;
} }
static PyObject * static PyObject *
notify_getitem(NotifyObject *self, Py_ssize_t item) notify_getitem(notifyObject *self, Py_ssize_t item)
{ {
if (item < 0) if (item < 0)
item += 2; item += 2;
@ -275,66 +255,45 @@ static PySequenceMethods notify_sequence = {
}; };
PyTypeObject NotifyType = { PyTypeObject notifyType = {
PyVarObject_HEAD_INIT(NULL, 0) PyVarObject_HEAD_INIT(NULL, 0)
"psycopg2.extensions.Notify", "psycopg2.extensions.Notify",
sizeof(NotifyObject), sizeof(notifyObject), 0,
0,
(destructor)notify_dealloc, /* tp_dealloc */ (destructor)notify_dealloc, /* tp_dealloc */
0, /*tp_print*/ 0, /*tp_print*/
0, /*tp_getattr*/ 0, /*tp_getattr*/
0, /*tp_setattr*/ 0, /*tp_setattr*/
0, /*tp_compare*/ 0, /*tp_compare*/
(reprfunc)notify_repr, /*tp_repr*/ (reprfunc)notify_repr, /*tp_repr*/
0, /*tp_as_number*/ 0, /*tp_as_number*/
&notify_sequence, /*tp_as_sequence*/ &notify_sequence, /*tp_as_sequence*/
0, /*tp_as_mapping*/ 0, /*tp_as_mapping*/
(hashfunc)notify_hash, /*tp_hash */ (hashfunc)notify_hash, /*tp_hash */
0, /*tp_call*/ 0, /*tp_call*/
0, /*tp_str*/ 0, /*tp_str*/
0, /*tp_getattro*/ 0, /*tp_getattro*/
0, /*tp_setattro*/ 0, /*tp_setattro*/
0, /*tp_as_buffer*/ 0, /*tp_as_buffer*/
/* Notify is not GC as it only has string attributes */
Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_GC, /*tp_flags*/ Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /*tp_flags*/
notify_doc, /*tp_doc*/ notify_doc, /*tp_doc*/
0, /*tp_traverse*/
(traverseproc)notify_traverse, /*tp_traverse*/
0, /*tp_clear*/ 0, /*tp_clear*/
(richcmpfunc)notify_richcompare, /*tp_richcompare*/ (richcmpfunc)notify_richcompare, /*tp_richcompare*/
0, /*tp_weaklistoffset*/ 0, /*tp_weaklistoffset*/
0, /*tp_iter*/ 0, /*tp_iter*/
0, /*tp_iternext*/ 0, /*tp_iternext*/
/* Attribute descriptor and subclassing stuff */
0, /*tp_methods*/ 0, /*tp_methods*/
notify_members, /*tp_members*/ notify_members, /*tp_members*/
0, /*tp_getset*/ 0, /*tp_getset*/
0, /*tp_base*/ 0, /*tp_base*/
0, /*tp_dict*/ 0, /*tp_dict*/
0, /*tp_descr_get*/ 0, /*tp_descr_get*/
0, /*tp_descr_set*/ 0, /*tp_descr_set*/
0, /*tp_dictoffset*/ 0, /*tp_dictoffset*/
(initproc)notify_init, /*tp_init*/ (initproc)notify_init, /*tp_init*/
0, /*tp_alloc will be set to PyType_GenericAlloc in module init*/ 0, /*tp_alloc*/
notify_new, /*tp_new*/ 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*/
}; };

View File

@ -38,6 +38,7 @@
#include "psycopg/green.h" #include "psycopg/green.h"
#include "psycopg/typecast.h" #include "psycopg/typecast.h"
#include "psycopg/pgtypes.h" #include "psycopg/pgtypes.h"
#include "psycopg/error.h"
#include <string.h> #include <string.h>
@ -149,15 +150,20 @@ exception_from_sqlstate(const char *sqlstate)
/* pq_raise - raise a python exception of the right kind /* 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 RAISES static void
pq_raise(connectionObject *conn, cursorObject *curs, PGresult *pgres) pq_raise(connectionObject *conn, cursorObject *curs, PGresult **pgres)
{ {
PyObject *exc = NULL; PyObject *exc = NULL;
const char *err = NULL; const char *err = NULL;
const char *err2 = NULL; const char *err2 = NULL;
const char *code = NULL; const char *code = NULL;
PyObject *pyerr = NULL;
if (conn == NULL) { if (conn == NULL) {
PyErr_SetString(DatabaseError, PyErr_SetString(DatabaseError,
@ -171,13 +177,13 @@ pq_raise(connectionObject *conn, cursorObject *curs, PGresult *pgres)
conn->closed = 2; conn->closed = 2;
if (pgres == NULL && curs != NULL) if (pgres == NULL && curs != NULL)
pgres = curs->pgres; pgres = &curs->pgres;
if (pgres) { if (pgres && *pgres) {
err = PQresultErrorMessage(pgres); err = PQresultErrorMessage(*pgres);
if (err != NULL) { if (err != NULL) {
Dprintf("pq_raise: PQresultErrorMessage: err=%s", err); Dprintf("pq_raise: PQresultErrorMessage: err=%s", err);
code = PQresultErrorField(pgres, PG_DIAG_SQLSTATE); code = PQresultErrorField(*pgres, PG_DIAG_SQLSTATE);
} }
} }
if (err == NULL) { if (err == NULL) {
@ -210,7 +216,26 @@ pq_raise(connectionObject *conn, cursorObject *curs, PGresult *pgres)
err2 = strip_severity(err); err2 = strip_severity(err);
Dprintf("pq_raise: err2=%s", err2); 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 /* pq_set_critical, pq_resolve_critical - manage critical errors
@ -369,7 +394,7 @@ pq_execute_command_locked(connectionObject *conn, const char *query,
} }
retvalue = 0; retvalue = 0;
IFCLEARPGRES(*pgres); CLEARPGRES(*pgres);
cleanup: cleanup:
return retvalue; 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", Dprintf("pq_complete_error: pgconn = %p, pgres = %p, error = %s",
conn->pgconn, *pgres, *error ? *error : "(null)"); conn->pgconn, *pgres, *error ? *error : "(null)");
if (*pgres != NULL) if (*pgres != NULL) {
pq_raise(conn, NULL, *pgres); pq_raise(conn, NULL, pgres);
/* now *pgres is null */
}
else if (*error != NULL) { else if (*error != NULL) {
PyErr_SetString(OperationalError, *error); PyErr_SetString(OperationalError, *error);
} else { } else {
PyErr_SetString(OperationalError, "unknown error"); PyErr_SetString(OperationalError, "unknown error");
} }
IFCLEARPGRES(*pgres);
if (*error) { if (*error) {
free(*error); free(*error);
*error = NULL; *error = NULL;
@ -557,12 +584,18 @@ pq_reset_locked(connectionObject *conn, PGresult **pgres, char **error,
if (retvalue != 0) return retvalue; 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); retvalue = pq_execute_command_locked(conn, "RESET ALL", pgres, error, tstate);
if (retvalue != 0) return retvalue; if (retvalue != 0) return retvalue;
retvalue = pq_execute_command_locked(conn, retvalue = pq_execute_command_locked(conn,
"SET SESSION AUTHORIZATION DEFAULT", pgres, error, tstate); "SET SESSION AUTHORIZATION DEFAULT", pgres, error, tstate);
if (retvalue != 0) return retvalue; if (retvalue != 0) return retvalue;
}
/* should set the tpc xid to null: postponed until we get the GIL again */ /* should set the tpc xid to null: postponed until we get the GIL again */
conn->status = CONN_STATUS_READY; conn->status = CONN_STATUS_READY;
@ -717,7 +750,7 @@ pq_tpc_command_locked(connectionObject *conn, const char *cmd, const char *tid,
PyEval_RestoreThread(*tstate); PyEval_RestoreThread(*tstate);
/* convert the xid into the postgres transaction_id and quote it. */ /* 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; } { goto exit; }
/* prepare the command to the server */ /* 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) { if (async == 0) {
IFCLEARPGRES(curs->pgres); CLEARPGRES(curs->pgres);
Dprintf("pq_execute: executing SYNC query: pgconn = %p", curs->conn->pgconn); Dprintf("pq_execute: executing SYNC query: pgconn = %p", curs->conn->pgconn);
Dprintf(" %-.200s", query); Dprintf(" %-.200s", query);
if (!psyco_green()) { 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("pq_execute: executing ASYNC query: pgconn = %p", curs->conn->pgconn);
Dprintf(" %-.200s", query); Dprintf(" %-.200s", query);
IFCLEARPGRES(curs->pgres); CLEARPGRES(curs->pgres);
if (PQsendQuery(curs->conn->pgconn, query) == 0) { if (PQsendQuery(curs->conn->pgconn, query) == 0) {
pthread_mutex_unlock(&(curs->conn->lock)); pthread_mutex_unlock(&(curs->conn->lock));
Py_BLOCK_THREADS; Py_BLOCK_THREADS;
@ -1305,7 +1338,7 @@ _pq_copy_in_v3(cursorObject *curs)
/* XXX would be nice to propagate the exeption */ /* XXX would be nice to propagate the exeption */
res = PQputCopyEnd(curs->conn->pgconn, "error in .read() call"); 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); Dprintf("_pq_copy_in_v3: copy ended; res = %d", res);
@ -1329,7 +1362,7 @@ _pq_copy_in_v3(cursorObject *curs)
break; break;
if (PQresultStatus(curs->pgres) == PGRES_FATAL_ERROR) if (PQresultStatus(curs->pgres) == PGRES_FATAL_ERROR)
pq_raise(curs->conn, curs, NULL); 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 */ /* and finally we grab the operation result from the backend */
IFCLEARPGRES(curs->pgres); CLEARPGRES(curs->pgres);
for (;;) { for (;;) {
Py_BEGIN_ALLOW_THREADS; Py_BEGIN_ALLOW_THREADS;
curs->pgres = PQgetResult(curs->conn->pgconn); curs->pgres = PQgetResult(curs->conn->pgconn);
@ -1405,7 +1438,7 @@ _pq_copy_out_v3(cursorObject *curs)
break; break;
if (PQresultStatus(curs->pgres) == PGRES_FATAL_ERROR) if (PQresultStatus(curs->pgres) == PGRES_FATAL_ERROR)
pq_raise(curs->conn, curs, NULL); pq_raise(curs->conn, curs, NULL);
IFCLEARPGRES(curs->pgres); CLEARPGRES(curs->pgres);
} }
ret = 1; ret = 1;
@ -1466,7 +1499,7 @@ pq_fetch(cursorObject *curs, int no_result)
curs->rowcount = -1; curs->rowcount = -1;
/* error caught by out glorious notice handler */ /* error caught by out glorious notice handler */
if (PyErr_Occurred()) ex = -1; if (PyErr_Occurred()) ex = -1;
IFCLEARPGRES(curs->pgres); CLEARPGRES(curs->pgres);
break; break;
case PGRES_COPY_IN: case PGRES_COPY_IN:
@ -1475,7 +1508,7 @@ pq_fetch(cursorObject *curs, int no_result)
curs->rowcount = -1; curs->rowcount = -1;
/* error caught by out glorious notice handler */ /* error caught by out glorious notice handler */
if (PyErr_Occurred()) ex = -1; if (PyErr_Occurred()) ex = -1;
IFCLEARPGRES(curs->pgres); CLEARPGRES(curs->pgres);
break; break;
case PGRES_TUPLES_OK: case PGRES_TUPLES_OK:
@ -1487,7 +1520,7 @@ pq_fetch(cursorObject *curs, int no_result)
} }
else { else {
Dprintf("pq_fetch: got tuples, discarding them"); Dprintf("pq_fetch: got tuples, discarding them");
IFCLEARPGRES(curs->pgres); CLEARPGRES(curs->pgres);
curs->rowcount = -1; curs->rowcount = -1;
ex = 0; ex = 0;
} }
@ -1496,14 +1529,13 @@ pq_fetch(cursorObject *curs, int no_result)
case PGRES_EMPTY_QUERY: case PGRES_EMPTY_QUERY:
PyErr_SetString(ProgrammingError, PyErr_SetString(ProgrammingError,
"can't execute an empty query"); "can't execute an empty query");
IFCLEARPGRES(curs->pgres); CLEARPGRES(curs->pgres);
ex = -1; ex = -1;
break; break;
default: default:
Dprintf("pq_fetch: uh-oh, something FAILED: pgconn = %p", curs->conn); Dprintf("pq_fetch: uh-oh, something FAILED: pgconn = %p", curs->conn);
pq_raise(curs->conn, curs, NULL); pq_raise(curs->conn, curs, NULL);
IFCLEARPGRES(curs->pgres);
ex = -1; ex = -1;
break; break;
} }

View File

@ -29,9 +29,8 @@
#include "psycopg/cursor.h" #include "psycopg/cursor.h"
#include "psycopg/connection.h" #include "psycopg/connection.h"
/* macros to clean the pg result */ /* macro to clean the pg result */
#define IFCLEARPGRES(pgres) if (pgres) {PQclear(pgres); pgres = NULL;} #define CLEARPGRES(pgres) do { PQclear(pgres); pgres = NULL; } while (0)
#define CLEARPGRES(pgres) PQclear(pgres); pgres = NULL
/* exported functions */ /* exported functions */
HIDDEN PGresult *pq_get_last_result(connectionObject *conn); HIDDEN PGresult *pq_get_last_result(connectionObject *conn);

View File

@ -116,14 +116,14 @@ typedef struct {
/* the Decimal type, used by the DECIMAL typecaster */ /* the Decimal type, used by the DECIMAL typecaster */
HIDDEN PyObject *psyco_GetDecimalType(void); HIDDEN PyObject *psyco_GetDecimalType(void);
/* forward declaration */ /* forward declarations */
typedef struct cursorObject cursorObject; typedef struct cursorObject cursorObject;
typedef struct connectionObject connectionObject;
/* some utility functions */ /* some utility functions */
RAISES HIDDEN void psyco_set_error(PyObject *exc, cursorObject *curs, const char *msg, RAISES HIDDEN PyObject *psyco_set_error(PyObject *exc, cursorObject *curs, const char *msg);
const char *pgerror, const char *pgcode);
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); 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 char *psycopg_escape_identifier_easy(const char *from, Py_ssize_t len);
HIDDEN int psycopg_strdup(char **to, const char *from, Py_ssize_t len); HIDDEN int psycopg_strdup(char **to, const char *from, Py_ssize_t len);

View File

@ -35,6 +35,8 @@
#include "psycopg/typecast.h" #include "psycopg/typecast.h"
#include "psycopg/microprotocols.h" #include "psycopg/microprotocols.h"
#include "psycopg/microprotocols_proto.h" #include "psycopg/microprotocols_proto.h"
#include "psycopg/error.h"
#include "psycopg/diagnostics.h"
#include "psycopg/adapter_qstring.h" #include "psycopg/adapter_qstring.h"
#include "psycopg/adapter_binary.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; } if (0 > typecast_add(type, NULL, 0)) { return NULL; }
} }
Py_INCREF(Py_None); Py_RETURN_NONE;
return Py_None;
} }
@ -408,8 +409,8 @@ static struct {
PyObject **base; PyObject **base;
const char *docstr; const char *docstr;
} exctable[] = { } exctable[] = {
{ "psycopg2.Error", &Error, 0, Error_doc }, { "psycopg2.Error", &Error, NULL, Error_doc },
{ "psycopg2.Warning", &Warning, 0, Warning_doc }, { "psycopg2.Warning", &Warning, NULL, Warning_doc },
{ "psycopg2.InterfaceError", &InterfaceError, &Error, InterfaceError_doc }, { "psycopg2.InterfaceError", &InterfaceError, &Error, InterfaceError_doc },
{ "psycopg2.DatabaseError", &DatabaseError, &Error, DatabaseError_doc }, { "psycopg2.DatabaseError", &DatabaseError, &Error, DatabaseError_doc },
{ "psycopg2.InternalError", &InternalError, &DatabaseError, InternalError_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 static int
psyco_errors_init(void) psyco_errors_init(void)
{ {
@ -497,17 +443,13 @@ psyco_errors_init(void)
int i; int i;
PyObject *dict = NULL; PyObject *dict = NULL;
PyObject *base;
PyObject *str = NULL; PyObject *str = NULL;
PyObject *descr = NULL;
int rv = -1; int rv = -1;
#if PY_VERSION_HEX >= 0x02050000 /* 'Error' has been defined elsewhere: only init the other classes */
static PyMethodDef psyco_error_reduce_ex_def = Error = (PyObject *)&errorType;
{"__reduce_ex__", psyco_error_reduce_ex, METH_VARARGS, "pickle helper"};
#endif
for (i=0; exctable[i].name; i++) { for (i = 1; exctable[i].name; i++) {
if (!(dict = PyDict_New())) { goto exit; } if (!(dict = PyDict_New())) { goto exit; }
if (exctable[i].docstr) { if (exctable[i].docstr) {
@ -516,51 +458,20 @@ psyco_errors_init(void)
Py_CLEAR(str); Py_CLEAR(str);
} }
if (exctable[i].base == 0) { /* can't put PyExc_StandardError in the static exctable:
#if PY_MAJOR_VERSION < 3 * windows build will fail */
base = PyExc_StandardError;
#else
/* StandardError is gone in 3.0 */
base = NULL;
#endif
}
else
base = *exctable[i].base;
if (!(*exctable[i].exc = PyErr_NewException( 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; goto exit;
} }
Py_CLEAR(dict); 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; rv = 0;
exit: exit:
Py_XDECREF(descr);
Py_XDECREF(str); Py_XDECREF(str);
Py_XDECREF(dict); Py_XDECREF(dict);
return rv; return rv;
@ -604,11 +515,10 @@ psyco_errors_set(PyObject *type)
Create a new error of the given type with extra attributes. */ Create a new error of the given type with extra attributes. */
RAISES void /* TODO: may have been changed to BORROWED */
psyco_set_error(PyObject *exc, cursorObject *curs, const char *msg, RAISES PyObject *
const char *pgerror, const char *pgcode) psyco_set_error(PyObject *exc, cursorObject *curs, const char *msg)
{ {
PyObject *t;
PyObject *pymsg; PyObject *pymsg;
PyObject *err = NULL; PyObject *err = NULL;
connectionObject *conn = NULL; connectionObject *conn = NULL;
@ -624,31 +534,24 @@ psyco_set_error(PyObject *exc, cursorObject *curs, const char *msg,
else { else {
/* what's better than an error in an error handler in the morning? /* what's better than an error in an error handler in the morning?
* Anyway, some error was set, refcount is ok... get outta here. */ * Anyway, some error was set, refcount is ok... get outta here. */
return; return NULL;
}
if (err && PyObject_TypeCheck(err, &errorType)) {
errorObject *perr = (errorObject *)err;
if (curs) {
Py_CLEAR(perr->cursor);
Py_INCREF(curs);
perr->cursor = curs;
}
} }
if (err) { if (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);
}
}
PyErr_SetObject(exc, err); PyErr_SetObject(exc, err);
Py_DECREF(err); Py_DECREF(err);
} }
return err;
} }
@ -868,36 +771,56 @@ INIT_MODULE(_psycopg)(void)
/* initialize all the new types and then the module */ /* initialize all the new types and then the module */
Py_TYPE(&connectionType) = &PyType_Type; Py_TYPE(&connectionType) = &PyType_Type;
Py_TYPE(&cursorType) = &PyType_Type;
Py_TYPE(&typecastType) = &PyType_Type;
Py_TYPE(&qstringType) = &PyType_Type;
Py_TYPE(&binaryType) = &PyType_Type;
Py_TYPE(&isqlquoteType) = &PyType_Type;
Py_TYPE(&pbooleanType) = &PyType_Type;
Py_TYPE(&pintType) = &PyType_Type;
Py_TYPE(&pfloatType) = &PyType_Type;
Py_TYPE(&pdecimalType) = &PyType_Type;
Py_TYPE(&asisType) = &PyType_Type;
Py_TYPE(&listType) = &PyType_Type;
Py_TYPE(&chunkType) = &PyType_Type;
Py_TYPE(&NotifyType) = &PyType_Type;
Py_TYPE(&XidType) = &PyType_Type;
if (PyType_Ready(&connectionType) == -1) goto exit; if (PyType_Ready(&connectionType) == -1) goto exit;
Py_TYPE(&cursorType) = &PyType_Type;
if (PyType_Ready(&cursorType) == -1) goto exit; if (PyType_Ready(&cursorType) == -1) goto exit;
Py_TYPE(&typecastType) = &PyType_Type;
if (PyType_Ready(&typecastType) == -1) goto exit; if (PyType_Ready(&typecastType) == -1) goto exit;
Py_TYPE(&qstringType) = &PyType_Type;
if (PyType_Ready(&qstringType) == -1) goto exit; if (PyType_Ready(&qstringType) == -1) goto exit;
Py_TYPE(&binaryType) = &PyType_Type;
if (PyType_Ready(&binaryType) == -1) goto exit; if (PyType_Ready(&binaryType) == -1) goto exit;
Py_TYPE(&isqlquoteType) = &PyType_Type;
if (PyType_Ready(&isqlquoteType) == -1) goto exit; if (PyType_Ready(&isqlquoteType) == -1) goto exit;
Py_TYPE(&pbooleanType) = &PyType_Type;
if (PyType_Ready(&pbooleanType) == -1) goto exit; if (PyType_Ready(&pbooleanType) == -1) goto exit;
Py_TYPE(&pintType) = &PyType_Type;
if (PyType_Ready(&pintType) == -1) goto exit; if (PyType_Ready(&pintType) == -1) goto exit;
Py_TYPE(&pfloatType) = &PyType_Type;
if (PyType_Ready(&pfloatType) == -1) goto exit; if (PyType_Ready(&pfloatType) == -1) goto exit;
Py_TYPE(&pdecimalType) = &PyType_Type;
if (PyType_Ready(&pdecimalType) == -1) goto exit; if (PyType_Ready(&pdecimalType) == -1) goto exit;
Py_TYPE(&asisType) = &PyType_Type;
if (PyType_Ready(&asisType) == -1) goto exit; if (PyType_Ready(&asisType) == -1) goto exit;
Py_TYPE(&listType) = &PyType_Type;
if (PyType_Ready(&listType) == -1) goto exit; if (PyType_Ready(&listType) == -1) goto exit;
Py_TYPE(&chunkType) = &PyType_Type;
if (PyType_Ready(&chunkType) == -1) goto exit; 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 #ifdef PSYCOPG_EXTENSIONS
Py_TYPE(&lobjectType) = &PyType_Type; Py_TYPE(&lobjectType) = &PyType_Type;
@ -908,6 +831,7 @@ INIT_MODULE(_psycopg)(void)
#ifdef HAVE_MXDATETIME #ifdef HAVE_MXDATETIME
Py_TYPE(&mxdatetimeType) = &PyType_Type; Py_TYPE(&mxdatetimeType) = &PyType_Type;
if (PyType_Ready(&mxdatetimeType) == -1) goto exit; if (PyType_Ready(&mxdatetimeType) == -1) goto exit;
if (0 != mxDateTime_ImportModuleAndAPI()) { if (0 != mxDateTime_ImportModuleAndAPI()) {
PyErr_Clear(); PyErr_Clear();
@ -985,8 +909,9 @@ INIT_MODULE(_psycopg)(void)
PyModule_AddObject(module, "connection", (PyObject*)&connectionType); PyModule_AddObject(module, "connection", (PyObject*)&connectionType);
PyModule_AddObject(module, "cursor", (PyObject*)&cursorType); PyModule_AddObject(module, "cursor", (PyObject*)&cursorType);
PyModule_AddObject(module, "ISQLQuote", (PyObject*)&isqlquoteType); PyModule_AddObject(module, "ISQLQuote", (PyObject*)&isqlquoteType);
PyModule_AddObject(module, "Notify", (PyObject*)&NotifyType); PyModule_AddObject(module, "Notify", (PyObject*)&notifyType);
PyModule_AddObject(module, "Xid", (PyObject*)&XidType); PyModule_AddObject(module, "Xid", (PyObject*)&xidType);
PyModule_AddObject(module, "Diagnostics", (PyObject*)&diagnosticsType);
#ifdef PSYCOPG_EXTENSIONS #ifdef PSYCOPG_EXTENSIONS
PyModule_AddObject(module, "lobject", (PyObject*)&lobjectType); PyModule_AddObject(module, "lobject", (PyObject*)&lobjectType);
#endif #endif
@ -1015,31 +940,6 @@ INIT_MODULE(_psycopg)(void)
if (0 != psyco_errors_init()) { goto exit; } if (0 != psyco_errors_init()) { goto exit; }
psyco_errors_fill(dict); 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"); Dprintf("initpsycopg: module initialization complete");
exit: exit:

View File

@ -31,32 +31,8 @@
#include <stringobject.h> #include <stringobject.h>
#endif #endif
#if PY_VERSION_HEX < 0x02040000
# error "psycopg requires Python >= 2.4"
#endif
#if PY_VERSION_HEX < 0x02050000 #if PY_VERSION_HEX < 0x02050000
/* Function missing in Py 2.4 */ # error "psycopg requires Python >= 2.5"
#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"
#endif #endif
/* hash() return size changed around version 3.2a4 on 64bit platforms. Before /* hash() return size changed around version 3.2a4 on 64bit platforms. Before
@ -76,11 +52,6 @@ typedef unsigned long Py_uhash_t;
#define PyVarObject_HEAD_INIT(x,n) PyObject_HEAD_INIT(x) n, #define PyVarObject_HEAD_INIT(x,n) PyObject_HEAD_INIT(x) n,
#endif #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: */ /* FORMAT_CODE_PY_SSIZE_T is for Py_ssize_t: */
#define FORMAT_CODE_PY_SSIZE_T "%" PY_FORMAT_SIZE_T "d" #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_AsLong PyLong_AsLong
#define PyInt_FromLong PyLong_FromLong #define PyInt_FromLong PyLong_FromLong
#define PyInt_FromSsize_t PyLong_FromSsize_t #define PyInt_FromSsize_t PyLong_FromSsize_t
#define PyExc_StandardError PyExc_Exception
#define PyString_FromFormat PyUnicode_FromFormat #define PyString_FromFormat PyUnicode_FromFormat
#define Py_TPFLAGS_HAVE_ITER 0L #define Py_TPFLAGS_HAVE_ITER 0L
#define Py_TPFLAGS_HAVE_RICHCOMPARE 0L #define Py_TPFLAGS_HAVE_RICHCOMPARE 0L

View File

@ -414,26 +414,27 @@ static struct PyMemberDef typecastObject_members[] = {
{NULL} {NULL}
}; };
static void static int
typecast_dealloc(PyObject *obj) typecast_clear(typecastObject *self)
{ {
typecastObject *self = (typecastObject*)obj;
PyObject_GC_UnTrack(self);
Py_CLEAR(self->values); Py_CLEAR(self->values);
Py_CLEAR(self->name); Py_CLEAR(self->name);
Py_CLEAR(self->pcast); Py_CLEAR(self->pcast);
Py_CLEAR(self->bcast); 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 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->values);
Py_VISIT(self->name); Py_VISIT(self->name);
Py_VISIT(self->pcast); Py_VISIT(self->pcast);
@ -441,12 +442,6 @@ typecast_traverse(PyObject *obj, visitproc visit, void *arg)
return 0; return 0;
} }
static void
typecast_del(void *self)
{
PyObject_GC_Del(self);
}
static PyObject * static PyObject *
typecast_repr(PyObject *self) 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 // If the string is not a string but a None value we're being called
// from a Python-defined caster. // from a Python-defined caster.
if (!string) { if (!string) {
Py_INCREF(Py_None); Py_RETURN_NONE;
return Py_None;
} }
return typecast_cast(obj, string, length, cursor); return typecast_cast(obj, string, length, cursor);
@ -489,10 +483,8 @@ typecast_call(PyObject *obj, PyObject *args, PyObject *kwargs)
PyTypeObject typecastType = { PyTypeObject typecastType = {
PyVarObject_HEAD_INIT(NULL, 0) PyVarObject_HEAD_INIT(NULL, 0)
"psycopg2._psycopg.type", "psycopg2._psycopg.type",
sizeof(typecastObject), sizeof(typecastObject), 0,
0, (destructor)typecast_dealloc, /*tp_dealloc*/
typecast_dealloc, /*tp_dealloc*/
0, /*tp_print*/ 0, /*tp_print*/
0, /*tp_getattr*/ 0, /*tp_getattr*/
0, /*tp_setattr*/ 0, /*tp_setattr*/
@ -502,48 +494,31 @@ PyTypeObject typecastType = {
0, /*tp_as_sequence*/ 0, /*tp_as_sequence*/
0, /*tp_as_mapping*/ 0, /*tp_as_mapping*/
0, /*tp_hash */ 0, /*tp_hash */
typecast_call, /*tp_call*/ typecast_call, /*tp_call*/
0, /*tp_str*/ 0, /*tp_str*/
0, /*tp_getattro*/ 0, /*tp_getattro*/
0, /*tp_setattro*/ 0, /*tp_setattro*/
0, /*tp_as_buffer*/ 0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_RICHCOMPARE | Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_RICHCOMPARE |
Py_TPFLAGS_HAVE_GC, /*tp_flags*/ Py_TPFLAGS_HAVE_GC, /*tp_flags*/
"psycopg type-casting object", /*tp_doc*/ "psycopg type-casting object", /*tp_doc*/
(traverseproc)typecast_traverse, /*tp_traverse*/
typecast_traverse, /*tp_traverse*/ (inquiry)typecast_clear, /*tp_clear*/
0, /*tp_clear*/
typecast_richcompare, /*tp_richcompare*/ typecast_richcompare, /*tp_richcompare*/
0, /*tp_weaklistoffset*/ 0, /*tp_weaklistoffset*/
0, /*tp_iter*/ 0, /*tp_iter*/
0, /*tp_iternext*/ 0, /*tp_iternext*/
/* Attribute descriptor and subclassing stuff */
0, /*tp_methods*/ 0, /*tp_methods*/
typecastObject_members, /*tp_members*/ typecastObject_members, /*tp_members*/
0, /*tp_getset*/ 0, /*tp_getset*/
0, /*tp_base*/ 0, /*tp_base*/
0, /*tp_dict*/ 0, /*tp_dict*/
0, /*tp_descr_get*/ 0, /*tp_descr_get*/
0, /*tp_descr_set*/ 0, /*tp_descr_set*/
0, /*tp_dictoffset*/ 0, /*tp_dictoffset*/
0, /*tp_init*/ 0, /*tp_init*/
0, /*tp_alloc will be set to PyType_GenericAlloc in module init*/ 0, /*tp_alloc*/
0, /*tp_new*/ 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*/
}; };
static PyObject * static PyObject *

View File

@ -253,7 +253,7 @@ typecast_GENERIC_ARRAY_cast(const char *str, Py_ssize_t len, PyObject *curs)
Dprintf("typecast_GENERIC_ARRAY_cast: str = '%s'," Dprintf("typecast_GENERIC_ARRAY_cast: str = '%s',"
" len = " FORMAT_CODE_PY_SSIZE_T, str, len); " 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] == '[') if (str[0] == '[')
typecast_array_cleanup(&str, &len); typecast_array_cleanup(&str, &len);
if (str[0] != '{') { if (str[0] != '{') {

View File

@ -31,7 +31,7 @@ typecast_INTEGER_cast(const char *s, Py_ssize_t len, PyObject *curs)
{ {
char buffer[12]; char buffer[12];
if (s == NULL) {Py_INCREF(Py_None); return Py_None;} if (s == NULL) { Py_RETURN_NONE; }
if (s[len] != '\0') { if (s[len] != '\0') {
strncpy(buffer, s, (size_t) len); buffer[len] = '\0'; strncpy(buffer, s, (size_t) len); buffer[len] = '\0';
s = buffer; s = buffer;
@ -49,7 +49,7 @@ typecast_LONGINTEGER_cast(const char *s, Py_ssize_t len, PyObject *curs)
{ {
char buffer[24]; char buffer[24];
if (s == NULL) {Py_INCREF(Py_None); return Py_None;} if (s == NULL) { Py_RETURN_NONE; }
if (s[len] != '\0') { if (s[len] != '\0') {
strncpy(buffer, s, (size_t) len); buffer[len] = '\0'; strncpy(buffer, s, (size_t) len); buffer[len] = '\0';
s = buffer; s = buffer;
@ -64,7 +64,7 @@ typecast_FLOAT_cast(const char *s, Py_ssize_t len, PyObject *curs)
{ {
PyObject *str = NULL, *flo = NULL; 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 (!(str = Text_FromUTF8AndSize(s, len))) { return NULL; }
#if PY_MAJOR_VERSION < 3 #if PY_MAJOR_VERSION < 3
flo = PyFloat_FromString(str, NULL); flo = PyFloat_FromString(str, NULL);
@ -81,7 +81,7 @@ typecast_FLOAT_cast(const char *s, Py_ssize_t len, PyObject *curs)
static PyObject * static PyObject *
typecast_STRING_cast(const char *s, Py_ssize_t len, PyObject *curs) 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); return PyString_FromStringAndSize(s, len);
} }
#else #else
@ -95,7 +95,7 @@ typecast_UNICODE_cast(const char *s, Py_ssize_t len, PyObject *curs)
{ {
char *enc; char *enc;
if (s == NULL) {Py_INCREF(Py_None); return Py_None;} if (s == NULL) { Py_RETURN_NONE; }
enc = ((cursorObject*)curs)->conn->codec; enc = ((cursorObject*)curs)->conn->codec;
return PyUnicode_Decode(s, len, enc, NULL); 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; PyObject *res;
if (s == NULL) {Py_INCREF(Py_None); return Py_None;} if (s == NULL) { Py_RETURN_NONE; }
if (s[0] == 't') if (s[0] == 't')
res = Py_True; res = Py_True;
@ -128,7 +128,7 @@ typecast_DECIMAL_cast(const char *s, Py_ssize_t len, PyObject *curs)
PyObject *decimalType; PyObject *decimalType;
char *buffer; 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) if ((buffer = PyMem_Malloc(len+1)) == NULL)
return PyErr_NoMemory(); return PyErr_NoMemory();

View File

@ -55,7 +55,6 @@ chunk_repr(chunkObject *self)
#if PY_MAJOR_VERSION < 3 #if PY_MAJOR_VERSION < 3
/* XXX support 3.0 buffer protocol */
static Py_ssize_t static Py_ssize_t
chunk_getreadbuffer(chunkObject *self, Py_ssize_t segment, void **ptr) chunk_getreadbuffer(chunkObject *self, Py_ssize_t segment, void **ptr)
{ {
@ -90,9 +89,15 @@ static PyBufferProcs chunk_as_buffer =
/* 3.0 buffer interface */ /* 3.0 buffer interface */
int chunk_getbuffer(PyObject *_self, Py_buffer *view, int flags) int chunk_getbuffer(PyObject *_self, Py_buffer *view, int flags)
{ {
int rv;
chunkObject *self = (chunkObject*)_self; 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 = static PyBufferProcs chunk_as_buffer =
{ {
chunk_getbuffer, chunk_getbuffer,
@ -105,9 +110,8 @@ static PyBufferProcs chunk_as_buffer =
PyTypeObject chunkType = { PyTypeObject chunkType = {
PyVarObject_HEAD_INIT(NULL, 0) PyVarObject_HEAD_INIT(NULL, 0)
"psycopg2._psycopg.chunk", /* tp_name */ "psycopg2._psycopg.chunk",
sizeof(chunkObject), /* tp_basicsize */ sizeof(chunkObject), 0,
0, /* tp_itemsize */
(destructor) chunk_dealloc, /* tp_dealloc*/ (destructor) chunk_dealloc, /* tp_dealloc*/
0, /* tp_print */ 0, /* tp_print */
0, /* tp_getattr */ 0, /* tp_getattr */
@ -142,7 +146,7 @@ typecast_BINARY_cast(const char *s, Py_ssize_t l, PyObject *curs)
char *buffer = NULL; char *buffer = NULL;
Py_ssize_t len; 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') { if (s[0] == '\\' && s[1] == 'x') {
/* This is a buffer escaped in hex format: libpq before 9.0 can't /* This is a buffer escaped in hex format: libpq before 9.0 can't

View File

@ -48,7 +48,7 @@ typecast_PYDATE_cast(const char *str, Py_ssize_t len, PyObject *curs)
PyObject* obj = NULL; PyObject* obj = NULL;
int n, y=0, m=0, d=0; 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 (!strcmp(str, "infinity") || !strcmp(str, "-infinity")) {
if (str[0] == '-') { 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; int hh=0, mm=0, ss=0, us=0, tz=0;
const char *tp = NULL; const char *tp = NULL;
if (str == NULL) {Py_INCREF(Py_None); return Py_None;} if (str == NULL) { Py_RETURN_NONE; }
/* check for infinity */ /* check for infinity */
if (!strcmp(str, "infinity") || !strcmp(str, "-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; PyObject *tzinfo_factory;
int n, hh=0, mm=0, ss=0, us=0, tz=0; 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); n = typecast_parse_time(str, NULL, &len, &hh, &mm, &ss, &us, &tz);
Dprintf("typecast_PYTIME_cast: n = %d, len = " FORMAT_CODE_PY_SSIZE_T ", " 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; int part = 0, sec;
double micro; 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); Dprintf("typecast_PYINTERVAL_cast: s = %s", str);

View File

@ -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; int hh=0, mm=0, ss=0, us=0, tz=0;
const char *tp = NULL; 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); 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; 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); 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; double v = 0.0, sign = 1.0;
int part = 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); Dprintf("typecast_MXINTERVAL_cast: s = %s", str);

View File

@ -32,12 +32,22 @@
#include <string.h> #include <string.h>
#include <stdlib.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 * 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) char *to, Py_ssize_t *tolen)
{ {
Py_ssize_t ql; 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) if (len == 0)
@ -45,9 +55,11 @@ psycopg_escape_string(PyObject *obj, const char *from, Py_ssize_t len,
if (to == NULL) { if (to == NULL) {
to = (char *)PyMem_Malloc((len * 2 + 4) * sizeof(char)); to = (char *)PyMem_Malloc((len * 2 + 4) * sizeof(char));
if (to == NULL) if (to == NULL) {
PyErr_NoMemory();
return NULL; return NULL;
} }
}
{ {
#if PG_VERSION_HEX >= 0x080104 #if PG_VERSION_HEX >= 0x080104
@ -59,11 +71,15 @@ psycopg_escape_string(PyObject *obj, const char *from, Py_ssize_t len,
ql = PQescapeString(to+eq+1, from, len); ql = PQescapeString(to+eq+1, from, len);
} }
if (eq) if (eq) {
to[0] = 'E'; to[0] = 'E';
to[eq] = '\''; to[1] = to[ql+2] = '\'';
to[ql+eq+1] = '\''; to[ql+3] = '\0';
to[ql+eq+2] = '\0'; }
else {
to[0] = to[ql+1] = '\'';
to[ql+2] = '\0';
}
if (tolen) if (tolen)
*tolen = ql+eq+2; *tolen = ql+eq+2;
@ -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 * Store the return in 'to' and return 0 in case of success, else return -1
* and raise an exception. * and raise an exception.
*
* If from is null, store null into to.
*/ */
RAISES_NEG int RAISES_NEG int
psycopg_strdup(char **to, const char *from, Py_ssize_t len) psycopg_strdup(char **to, const char *from, Py_ssize_t len)
{ {
if (!from) {
*to = NULL;
return 0;
}
if (!len) { len = strlen(from); } if (!len) { len = strlen(from); }
if (!(*to = PyMem_Malloc(len + 1))) { if (!(*to = PyMem_Malloc(len + 1))) {
PyErr_NoMemory(); PyErr_NoMemory();

View File

@ -27,7 +27,7 @@
#ifndef PSYCOPG_XID_H #ifndef PSYCOPG_XID_H
#define PSYCOPG_XID_H 1 #define PSYCOPG_XID_H 1
extern HIDDEN PyTypeObject XidType; extern HIDDEN PyTypeObject xidType;
typedef struct { typedef struct {
PyObject_HEAD PyObject_HEAD
@ -41,11 +41,11 @@ typedef struct {
PyObject *prepared; PyObject *prepared;
PyObject *owner; PyObject *owner;
PyObject *database; PyObject *database;
} XidObject; } xidObject;
HIDDEN XidObject *xid_ensure(PyObject *oxid); HIDDEN xidObject *xid_ensure(PyObject *oxid);
HIDDEN XidObject *xid_from_string(PyObject *s); HIDDEN xidObject *xid_from_string(PyObject *s);
HIDDEN PyObject *xid_get_tid(XidObject *self); HIDDEN PyObject *xid_get_tid(xidObject *self);
HIDDEN PyObject *xid_recover(PyObject *conn); HIDDEN PyObject *xid_recover(PyObject *conn);
#endif /* PSYCOPG_XID_H */ #endif /* PSYCOPG_XID_H */

View File

@ -67,46 +67,28 @@ static const char database_doc[] =
"Database the recovered transaction belongs to."; "Database the recovered transaction belongs to.";
static PyMemberDef xid_members[] = { static PyMemberDef xid_members[] = {
{ "format_id", T_OBJECT, offsetof(XidObject, format_id), READONLY, (char *)format_id_doc }, { "format_id", T_OBJECT, offsetof(xidObject, format_id), READONLY, (char *)format_id_doc },
{ "gtrid", T_OBJECT, offsetof(XidObject, gtrid), READONLY, (char *)gtrid_doc }, { "gtrid", T_OBJECT, offsetof(xidObject, gtrid), READONLY, (char *)gtrid_doc },
{ "bqual", T_OBJECT, offsetof(XidObject, bqual), READONLY, (char *)bqual_doc }, { "bqual", T_OBJECT, offsetof(xidObject, bqual), READONLY, (char *)bqual_doc },
{ "prepared", T_OBJECT, offsetof(XidObject, prepared), READONLY, (char *)prepared_doc }, { "prepared", T_OBJECT, offsetof(xidObject, prepared), READONLY, (char *)prepared_doc },
{ "owner", T_OBJECT, offsetof(XidObject, owner), READONLY, (char *)owner_doc }, { "owner", T_OBJECT, offsetof(xidObject, owner), READONLY, (char *)owner_doc },
{ "database", T_OBJECT, offsetof(XidObject, database), READONLY, (char *)database_doc }, { "database", T_OBJECT, offsetof(xidObject, database), READONLY, (char *)database_doc },
{ NULL } { NULL }
}; };
static PyObject * static PyObject *
xid_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) xid_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
{ {
XidObject *self; return type->tp_alloc(type, 0);
if (!(self = (XidObject *)type->tp_alloc(type, 0))) { return NULL; }
Py_INCREF(Py_None);
self->format_id = Py_None;
Py_INCREF(Py_None);
self->gtrid = Py_None;
Py_INCREF(Py_None);
self->bqual = Py_None;
Py_INCREF(Py_None);
self->prepared = Py_None;
Py_INCREF(Py_None);
self->owner = Py_None;
Py_INCREF(Py_None);
self->database = Py_None;
return (PyObject *)self;
} }
static int 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}; static char *kwlist[] = {"format_id", "gtrid", "bqual", NULL};
int format_id; int format_id;
size_t i, gtrid_len, bqual_len; size_t i, gtrid_len, bqual_len;
const char *gtrid, *bqual; const char *gtrid, *bqual;
PyObject *tmp;
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "iss", kwlist, if (!PyArg_ParseTupleAndKeywords(args, kwargs, "iss", kwlist,
&format_id, &gtrid, &bqual)) &format_id, &gtrid, &bqual))
@ -149,35 +131,18 @@ xid_init(XidObject *self, PyObject *args, PyObject *kwargs)
} }
} }
tmp = self->format_id; if (!(self->format_id = PyInt_FromLong(format_id))) { return -1; }
self->format_id = PyInt_FromLong(format_id); if (!(self->gtrid = Text_FromUTF8(gtrid))) { return -1; }
Py_XDECREF(tmp); 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;
tmp = self->gtrid;
self->gtrid = Text_FromUTF8(gtrid);
Py_XDECREF(tmp);
tmp = self->bqual;
self->bqual = Text_FromUTF8(bqual);
Py_XDECREF(tmp);
return 0;
}
static int
xid_traverse(XidObject *self, visitproc visit, void *arg)
{
Py_VISIT(self->format_id);
Py_VISIT(self->gtrid);
Py_VISIT(self->bqual);
Py_VISIT(self->prepared);
Py_VISIT(self->owner);
Py_VISIT(self->database);
return 0; return 0;
} }
static void static void
xid_dealloc(XidObject *self) xid_dealloc(xidObject *self)
{ {
Py_CLEAR(self->format_id); Py_CLEAR(self->format_id);
Py_CLEAR(self->gtrid); Py_CLEAR(self->gtrid);
@ -189,20 +154,14 @@ xid_dealloc(XidObject *self)
Py_TYPE(self)->tp_free((PyObject *)self); Py_TYPE(self)->tp_free((PyObject *)self);
} }
static void
xid_del(PyObject *self)
{
PyObject_GC_Del(self);
}
static Py_ssize_t static Py_ssize_t
xid_len(XidObject *self) xid_len(xidObject *self)
{ {
return 3; return 3;
} }
static PyObject * static PyObject *
xid_getitem(XidObject *self, Py_ssize_t item) xid_getitem(xidObject *self, Py_ssize_t item)
{ {
if (item < 0) if (item < 0)
item += 3; item += 3;
@ -224,13 +183,13 @@ xid_getitem(XidObject *self, Py_ssize_t item)
} }
static PyObject * static PyObject *
xid_str(XidObject *self) xid_str(xidObject *self)
{ {
return xid_get_tid(self); return xid_get_tid(self);
} }
static PyObject * static PyObject *
xid_repr(XidObject *self) xid_repr(xidObject *self)
{ {
PyObject *rv = NULL; PyObject *rv = NULL;
PyObject *format = NULL; PyObject *format = NULL;
@ -305,66 +264,45 @@ static struct PyMethodDef xid_methods[] = {
{NULL} {NULL}
}; };
PyTypeObject XidType = { PyTypeObject xidType = {
PyVarObject_HEAD_INIT(NULL, 0) PyVarObject_HEAD_INIT(NULL, 0)
"psycopg2.extensions.Xid", "psycopg2.extensions.Xid",
sizeof(XidObject), sizeof(xidObject), 0,
0,
(destructor)xid_dealloc, /* tp_dealloc */ (destructor)xid_dealloc, /* tp_dealloc */
0, /*tp_print*/ 0, /*tp_print*/
0, /*tp_getattr*/ 0, /*tp_getattr*/
0, /*tp_setattr*/ 0, /*tp_setattr*/
0, /*tp_compare*/ 0, /*tp_compare*/
(reprfunc)xid_repr, /*tp_repr*/ (reprfunc)xid_repr, /*tp_repr*/
0, /*tp_as_number*/ 0, /*tp_as_number*/
&xid_sequence, /*tp_as_sequence*/ &xid_sequence, /*tp_as_sequence*/
0, /*tp_as_mapping*/ 0, /*tp_as_mapping*/
0, /*tp_hash */ 0, /*tp_hash */
0, /*tp_call*/ 0, /*tp_call*/
(reprfunc)xid_str, /*tp_str*/ (reprfunc)xid_str, /*tp_str*/
0, /*tp_getattro*/ 0, /*tp_getattro*/
0, /*tp_setattro*/ 0, /*tp_setattro*/
0, /*tp_as_buffer*/ 0, /*tp_as_buffer*/
/* Notify is not GC as it only has string attributes */
Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_GC, /*tp_flags*/ Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /*tp_flags*/
xid_doc, /*tp_doc*/ xid_doc, /*tp_doc*/
0, /*tp_traverse*/
(traverseproc)xid_traverse, /*tp_traverse*/
0, /*tp_clear*/ 0, /*tp_clear*/
0, /*tp_richcompare*/ 0, /*tp_richcompare*/
0, /*tp_weaklistoffset*/ 0, /*tp_weaklistoffset*/
0, /*tp_iter*/ 0, /*tp_iter*/
0, /*tp_iternext*/ 0, /*tp_iternext*/
/* Attribute descriptor and subclassing stuff */
xid_methods, /*tp_methods*/ xid_methods, /*tp_methods*/
xid_members, /*tp_members*/ xid_members, /*tp_members*/
0, /*tp_getset*/ 0, /*tp_getset*/
0, /*tp_base*/ 0, /*tp_base*/
0, /*tp_dict*/ 0, /*tp_dict*/
0, /*tp_descr_get*/ 0, /*tp_descr_get*/
0, /*tp_descr_set*/ 0, /*tp_descr_set*/
0, /*tp_dictoffset*/ 0, /*tp_dictoffset*/
(initproc)xid_init, /*tp_init*/ (initproc)xid_init, /*tp_init*/
0, /*tp_alloc will be set to PyType_GenericAlloc in module init*/ 0, /*tp_alloc*/
xid_new, /*tp_new*/ 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 * or use a regular string they have found in PostgreSQL's pg_prepared_xacts
* in order to recover a transaction not generated by psycopg. * 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); Py_INCREF(oxid);
rv = (XidObject *)oxid; rv = (xidObject *)oxid;
} }
else { else {
rv = xid_from_string(oxid); 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 * http://cvs.pgfoundry.org/cgi-bin/cvsweb.cgi/jdbc/pgjdbc/org/postgresql/xa/RecoveredXid.java?rev=1.2
*/ */
PyObject * PyObject *
xid_get_tid(XidObject *self) xid_get_tid(xidObject *self)
{ {
PyObject *rv = NULL; PyObject *rv = NULL;
PyObject *egtrid = NULL; PyObject *egtrid = NULL;
@ -525,7 +463,7 @@ exit:
* *
* Return NULL + exception if parsing failed. Else a new Xid object. */ * Return NULL + exception if parsing failed. Else a new Xid object. */
static XidObject * static xidObject *
_xid_parse_string(PyObject *str) { _xid_parse_string(PyObject *str) {
PyObject *regex; PyObject *regex;
PyObject *m = NULL; PyObject *m = NULL;
@ -536,7 +474,7 @@ _xid_parse_string(PyObject *str) {
PyObject *ebqual = NULL; PyObject *ebqual = NULL;
PyObject *gtrid = NULL; PyObject *gtrid = NULL;
PyObject *bqual = NULL; PyObject *bqual = NULL;
XidObject *rv = NULL; xidObject *rv = NULL;
/* check if the string is a possible XA triple with a regexp */ /* check if the string is a possible XA triple with a regexp */
if (!(regex = _xid_get_parse_regex())) { goto exit; } if (!(regex = _xid_get_parse_regex())) { goto exit; }
@ -560,7 +498,7 @@ _xid_parse_string(PyObject *str) {
if (!(bqual = _xid_decode64(ebqual))) { goto exit; } if (!(bqual = _xid_decode64(ebqual))) { goto exit; }
/* Try to build the xid with the parsed material */ /* 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); format_id, gtrid, bqual, NULL);
exit: exit:
@ -579,35 +517,31 @@ exit:
/* Return a new Xid object representing a transaction ID not conform to /* Return a new Xid object representing a transaction ID not conform to
* the XA specifications. */ * the XA specifications. */
static XidObject * static xidObject *
_xid_unparsed_from_string(PyObject *str) { _xid_unparsed_from_string(PyObject *str) {
XidObject *xid = NULL; xidObject *xid = NULL;
XidObject *rv = NULL; xidObject *rv = NULL;
PyObject *tmp;
/* fake args to work around the checks performed by the xid init */ /* 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, "", ""))) { "iss", 0, "", ""))) {
goto exit; goto exit;
} }
/* set xid.gtrid = str */ /* set xid.gtrid = str */
tmp = xid->gtrid; Py_CLEAR(xid->gtrid);
Py_INCREF(str); Py_INCREF(str);
xid->gtrid = str; xid->gtrid = str;
Py_DECREF(tmp);
/* set xid.format_id = None */ /* set xid.format_id = None */
tmp = xid->format_id; Py_CLEAR(xid->format_id);
Py_INCREF(Py_None); Py_INCREF(Py_None);
xid->format_id = Py_None; xid->format_id = Py_None;
Py_DECREF(tmp);
/* set xid.bqual = None */ /* set xid.bqual = None */
tmp = xid->bqual; Py_CLEAR(xid->bqual);
Py_INCREF(Py_None); Py_INCREF(Py_None);
xid->bqual = Py_None; xid->bqual = Py_None;
Py_DECREF(tmp);
/* return the finished object */ /* return the finished object */
rv = xid; rv = xid;
@ -624,9 +558,9 @@ exit:
* If the xid is in the format generated by Psycopg, unpack the tuple into * If the xid is in the format generated by Psycopg, unpack the tuple into
* the struct members. Otherwise generate an "unparsed" xid. * the struct members. Otherwise generate an "unparsed" xid.
*/ */
XidObject * xidObject *
xid_from_string(PyObject *str) { xid_from_string(PyObject *str) {
XidObject *rv; xidObject *rv;
if (!(Bytes_Check(str) || PyUnicode_Check(str))) { if (!(Bytes_Check(str) || PyUnicode_Check(str))) {
PyErr_SetString(PyExc_TypeError, "not a valid transaction id"); PyErr_SetString(PyExc_TypeError, "not a valid transaction id");
@ -654,7 +588,7 @@ xid_recover(PyObject *conn)
PyObject *rv = NULL; PyObject *rv = NULL;
PyObject *curs = NULL; PyObject *curs = NULL;
PyObject *xids = NULL; PyObject *xids = NULL;
XidObject *xid = NULL; xidObject *xid = NULL;
PyObject *recs = NULL; PyObject *recs = NULL;
PyObject *rec = NULL; PyObject *rec = NULL;
PyObject *item = NULL; PyObject *item = NULL;
@ -693,34 +627,25 @@ xid_recover(PyObject *conn)
/* Get the xid with the XA triple set */ /* Get the xid with the XA triple set */
if (!(item = PySequence_GetItem(rec, 0))) { goto exit; } if (!(item = PySequence_GetItem(rec, 0))) { goto exit; }
if (!(xid = xid_from_string(item))) { goto exit; } if (!(xid = xid_from_string(item))) { goto exit; }
Py_DECREF(item); item = NULL; Py_CLEAR(item);
/* set xid.prepared */ /* set xid.prepared */
if (!(item = PySequence_GetItem(rec, 1))) { goto exit; } Py_CLEAR(xid->prepared);
tmp = xid->prepared; if (!(xid->prepared = PySequence_GetItem(rec, 1))) { goto exit; }
xid->prepared = item;
Py_DECREF(tmp);
item = NULL;
/* set xid.owner */ /* set xid.owner */
if (!(item = PySequence_GetItem(rec, 2))) { goto exit; } Py_CLEAR(xid->owner);
tmp = xid->owner; if (!(xid->owner = PySequence_GetItem(rec, 2))) { goto exit; }
xid->owner = item;
Py_DECREF(tmp);
item = NULL;
/* set xid.database */ /* set xid.database */
if (!(item = PySequence_GetItem(rec, 3))) { goto exit; } Py_CLEAR(xid->database);
tmp = xid->database; if (!(xid->database = PySequence_GetItem(rec, 3))) { goto exit; }
xid->database = item;
Py_DECREF(tmp);
item = NULL;
/* xid finished: add it to the returned list */ /* xid finished: add it to the returned list */
PyList_SET_ITEM(xids, i, (PyObject *)xid); PyList_SET_ITEM(xids, i, (PyObject *)xid);
xid = NULL; /* ref stolen */ xid = NULL; /* ref stolen */
Py_DECREF(rec); rec = NULL; Py_CLEAR(rec);
} }
/* set the return value. */ /* set the return value. */

Some files were not shown because too many files have changed in this diff Show More