Merge remote-tracking branch 'piro/devel' into devel

Conflicts:
	psycopg/lobject_int.c
This commit is contained in:
Federico Di Gregorio 2012-03-08 12:28:52 +01:00
commit 550130b19e
55 changed files with 1112 additions and 652 deletions

View File

@ -1,7 +1,7 @@
Compiling and installing psycopg Compiling and installing psycopg
******************************** ********************************
** Important note: if you plan to use psyopg2 in a multithreaed application ** Important note: if you plan to use psycopg2 in a multithreaded application,
make sure that your libpq has been compiled with the --with-thread-safety make sure that your libpq has been compiled with the --with-thread-safety
option. psycopg2 will work correctly even with a non-thread-safe libpq but option. psycopg2 will work correctly even with a non-thread-safe libpq but
libpq will leak memory. libpq will leak memory.
@ -16,7 +16,7 @@ then:
to build in the local directory; and: to build in the local directory; and:
python setup.py install python setup.py install
to install system-wide. to install system-wide.
@ -96,7 +96,7 @@ Dev-C++ (http://www.bloodshed.net/devcpp.html) and Code::Blocks
You need a PostgreSQL with include and libary files installed. At least v8.0 You need a PostgreSQL with include and libary files installed. At least v8.0
is required. is required.
First you need to create a libpython2X.a as described in First you need to create a libpython2X.a as described in
http://starship.python.net/crew/kernr/mingw32/Notes.html. Then run: http://starship.python.net/crew/kernr/mingw32/Notes.html. Then run:
python setup.py build_ext --compiler=mingw32 install python setup.py build_ext --compiler=mingw32 install

16
NEWS
View File

@ -1,8 +1,24 @@
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
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
interaction with pgpool-II (ticket #88).
- Error and its subclasses are picklable, useful for multiprocessing
interaction (ticket #90).
- Better efficiency and formatting of timezone offset objects thanks
to Menno Smits (tickets #94, #95).
- Fixed 'rownumber' during iteration on cursor subclasses.
Regression introduced in 2.4.4 (ticket #100).
- Added support for 'inet' arrays.
- Fixed 'commit()' concurrency problem (ticket #103).
- Codebase cleaned up using the GCC Python plugin's static analysis
tool, which has revealed several unchecked return values, possible
NULL dereferences, reference counting problems. Many thanks to David
Malcolm for the useful tool and the assistance provided using it.
What's new in psycopg 2.4.4 What's new in psycopg 2.4.4

View File

@ -143,7 +143,7 @@ geometric type:
.. |point| replace:: :sql:`point` .. |point| replace:: :sql:`point`
.. _point: http://www.postgresql.org/docs/9.0/static/datatype-geometric.html#DATATYPE-GEOMETRIC .. _point: http://www.postgresql.org/docs/current/static/datatype-geometric.html#DATATYPE-GEOMETRIC
The above function call results in the SQL command:: The above function call results in the SQL command::
@ -246,9 +246,9 @@ documentation), you should keep the connection in `~connection.autocommit`
mode if you wish to receive or send notifications in a timely manner. mode if you wish to receive or send notifications in a timely manner.
.. |LISTEN| replace:: :sql:`LISTEN` .. |LISTEN| replace:: :sql:`LISTEN`
.. _LISTEN: http://www.postgresql.org/docs/9.0/static/sql-listen.html .. _LISTEN: http://www.postgresql.org/docs/current/static/sql-listen.html
.. |NOTIFY| replace:: :sql:`NOTIFY` .. |NOTIFY| replace:: :sql:`NOTIFY`
.. _NOTIFY: http://www.postgresql.org/docs/9.0/static/sql-notify.html .. _NOTIFY: http://www.postgresql.org/docs/current/static/sql-notify.html
Notifications are received after every query execution. If the user is Notifications are received after every query execution. If the user is
interested in receiving notifications but not in performing any query, the interested in receiving notifications but not in performing any query, the
@ -356,7 +356,7 @@ completely non-blocking connection attempt: see the libpq documentation for
|PQconnectStart|_. |PQconnectStart|_.
.. |PQconnectStart| replace:: `!PQconnectStart()` .. |PQconnectStart| replace:: `!PQconnectStart()`
.. _PQconnectStart: http://www.postgresql.org/docs/9.0/static/libpq-connect.html#LIBPQ-PQCONNECTSTART .. _PQconnectStart: http://www.postgresql.org/docs/current/static/libpq-connect.html#LIBPQ-PQCONNECTSTARTPARAMS
The same loop should be also used to perform nonblocking queries: after The same loop should be also used to perform nonblocking queries: after
sending a query via `~cursor.execute()` or `~cursor.callproc()`, call sending a query via `~cursor.execute()` or `~cursor.callproc()`, call
@ -472,7 +472,7 @@ resources about the topic.
.. _gevent: http://www.gevent.org/ .. _gevent: http://www.gevent.org/
.. _SQLAlchemy: http://www.sqlalchemy.org/ .. _SQLAlchemy: http://www.sqlalchemy.org/
.. _psycogreen: http://bitbucket.org/dvarrazzo/psycogreen/ .. _psycogreen: http://bitbucket.org/dvarrazzo/psycogreen/
.. __: http://www.postgresql.org/docs/9.0/static/libpq-async.html .. __: http://www.postgresql.org/docs/current/static/libpq-async.html
.. warning:: .. warning::

View File

@ -111,7 +111,7 @@ rst_epilog = """
.. _DBAPI: http://www.python.org/dev/peps/pep-0249/ .. _DBAPI: http://www.python.org/dev/peps/pep-0249/
.. _transaction isolation level: .. _transaction isolation level:
http://www.postgresql.org/docs/9.1/static/transaction-iso.html http://www.postgresql.org/docs/current/static/transaction-iso.html
.. _mx.DateTime: http://www.egenix.com/products/python/mxBase/mxDateTime/ .. _mx.DateTime: http://www.egenix.com/products/python/mxBase/mxDateTime/

View File

@ -185,7 +185,7 @@ The ``connection`` class
.. seealso:: the |PREPARE TRANSACTION|_ PostgreSQL command. .. seealso:: the |PREPARE TRANSACTION|_ PostgreSQL command.
.. |PREPARE TRANSACTION| replace:: :sql:`PREPARE TRANSACTION` .. |PREPARE TRANSACTION| replace:: :sql:`PREPARE TRANSACTION`
.. _PREPARE TRANSACTION: http://www.postgresql.org/docs/9.0/static/sql-prepare-transaction.html .. _PREPARE TRANSACTION: http://www.postgresql.org/docs/current/static/sql-prepare-transaction.html
.. index:: .. index::
@ -211,7 +211,7 @@ The ``connection`` class
.. seealso:: the |COMMIT PREPARED|_ PostgreSQL command. .. seealso:: the |COMMIT PREPARED|_ PostgreSQL command.
.. |COMMIT PREPARED| replace:: :sql:`COMMIT PREPARED` .. |COMMIT PREPARED| replace:: :sql:`COMMIT PREPARED`
.. _COMMIT PREPARED: http://www.postgresql.org/docs/9.0/static/sql-commit-prepared.html .. _COMMIT PREPARED: http://www.postgresql.org/docs/current/static/sql-commit-prepared.html
.. index:: .. index::
@ -233,7 +233,7 @@ The ``connection`` class
.. seealso:: the |ROLLBACK PREPARED|_ PostgreSQL command. .. seealso:: the |ROLLBACK PREPARED|_ PostgreSQL command.
.. |ROLLBACK PREPARED| replace:: :sql:`ROLLBACK PREPARED` .. |ROLLBACK PREPARED| replace:: :sql:`ROLLBACK PREPARED`
.. _ROLLBACK PREPARED: http://www.postgresql.org/docs/9.0/static/sql-rollback-prepared.html .. _ROLLBACK PREPARED: http://www.postgresql.org/docs/current/static/sql-rollback-prepared.html
.. index:: .. index::
@ -264,7 +264,7 @@ The ``connection`` class
.. seealso:: the |pg_prepared_xacts|_ system view. .. seealso:: the |pg_prepared_xacts|_ system view.
.. |pg_prepared_xacts| replace:: `pg_prepared_xacts` .. |pg_prepared_xacts| replace:: `pg_prepared_xacts`
.. _pg_prepared_xacts: http://www.postgresql.org/docs/9.0/static/view-pg-prepared-xacts.html .. _pg_prepared_xacts: http://www.postgresql.org/docs/current/static/view-pg-prepared-xacts.html
@ -296,7 +296,7 @@ The ``connection`` class
|PQcancel|_. |PQcancel|_.
.. |PQcancel| replace:: `!PQcancel()` .. |PQcancel| replace:: `!PQcancel()`
.. _PQcancel: http://www.postgresql.org/docs/8.4/static/libpq-cancel.html#AEN34765 .. _PQcancel: http://www.postgresql.org/docs/current/static/libpq-cancel.html#LIBPQ-PQCANCEL
.. versionadded:: 2.3 .. versionadded:: 2.3
@ -312,10 +312,10 @@ The ``connection`` class
available for recover. available for recover.
.. |RESET| replace:: :sql:`RESET` .. |RESET| replace:: :sql:`RESET`
.. _RESET: http://www.postgresql.org/docs/9.0/static/sql-reset.html .. _RESET: http://www.postgresql.org/docs/current/static/sql-reset.html
.. |SET SESSION AUTHORIZATION| replace:: :sql:`SET SESSION AUTHORIZATION` .. |SET SESSION AUTHORIZATION| replace:: :sql:`SET SESSION AUTHORIZATION`
.. __: http://www.postgresql.org/docs/9.0/static/sql-set-session-authorization.html .. __: http://www.postgresql.org/docs/current/static/sql-set-session-authorization.html
.. versionadded:: 2.0.12 .. versionadded:: 2.0.12
@ -336,7 +336,7 @@ The ``connection`` class
the current session. See |SET TRANSACTION|_ for further details. the current session. See |SET TRANSACTION|_ for further details.
.. |SET TRANSACTION| replace:: :sql:`SET TRANSACTION` .. |SET TRANSACTION| replace:: :sql:`SET TRANSACTION`
.. _SET TRANSACTION: http://www.postgresql.org/docs/9.1/static/sql-set-transaction.html .. _SET TRANSACTION: http://www.postgresql.org/docs/current/static/sql-set-transaction.html
:param isolation_level: set the `isolation level`_ for the next :param isolation_level: set the `isolation level`_ for the next
transactions/statements. The value can be one of the transactions/statements. The value can be one of the
@ -357,7 +357,7 @@ The ``connection`` class
parameter to the server default. parameter to the server default.
.. _isolation level: .. _isolation level:
http://www.postgresql.org/docs/9.1/static/transaction-iso.html http://www.postgresql.org/docs/current/static/transaction-iso.html
The function must be invoked with no transaction in progress. At every The function must be invoked with no transaction in progress. At every
function invocation, only the specified parameters are changed. function invocation, only the specified parameters are changed.
@ -367,11 +367,11 @@ The ``connection`` class
|default_transaction_read_only|__, |default_transaction_deferrable|__. |default_transaction_read_only|__, |default_transaction_deferrable|__.
.. |default_transaction_isolation| replace:: :sql:`default_transaction_isolation` .. |default_transaction_isolation| replace:: :sql:`default_transaction_isolation`
.. __: http://www.postgresql.org/docs/9.1/static/runtime-config-client.html#GUC-DEFAULT-TRANSACTION-ISOLATION .. __: http://www.postgresql.org/docs/current/static/runtime-config-client.html#GUC-DEFAULT-TRANSACTION-ISOLATION
.. |default_transaction_read_only| replace:: :sql:`default_transaction_read_only` .. |default_transaction_read_only| replace:: :sql:`default_transaction_read_only`
.. __: http://www.postgresql.org/docs/9.1/static/runtime-config-client.html#GUC-DEFAULT-TRANSACTION-READ-ONLY .. __: http://www.postgresql.org/docs/current/static/runtime-config-client.html#GUC-DEFAULT-TRANSACTION-READ-ONLY
.. |default_transaction_deferrable| replace:: :sql:`default_transaction_deferrable` .. |default_transaction_deferrable| replace:: :sql:`default_transaction_deferrable`
.. __: http://www.postgresql.org/docs/9.1/static/runtime-config-client.html#GUC-DEFAULT-TRANSACTION-DEFERRABLE .. __: http://www.postgresql.org/docs/current/static/runtime-config-client.html#GUC-DEFAULT-TRANSACTION-DEFERRABLE
.. note:: .. note::
@ -445,7 +445,7 @@ The ``connection`` class
is the encoding defined by the database. It should be one of the is the encoding defined by the database. It should be one of the
`characters set supported by PostgreSQL`__ `characters set supported by PostgreSQL`__
.. __: http://www.postgresql.org/docs/9.0/static/multibyte.html .. __: http://www.postgresql.org/docs/current/static/multibyte.html
.. index:: .. index::
@ -471,7 +471,7 @@ The ``connection`` class
configuration parameters`__ such as ``log_statement``, configuration parameters`__ such as ``log_statement``,
``client_min_messages``, ``log_min_duration_statement`` etc. ``client_min_messages``, ``log_min_duration_statement`` etc.
.. __: http://www.postgresql.org/docs/9.0/static/runtime-config-logging.html .. __: http://www.postgresql.org/docs/current/static/runtime-config-logging.html
.. attribute:: notifies .. attribute:: notifies
@ -500,7 +500,7 @@ The ``connection`` class
.. seealso:: libpq docs for `PQbackendPID()`__ for details. .. seealso:: libpq docs for `PQbackendPID()`__ for details.
.. __: http://www.postgresql.org/docs/9.0/static/libpq-status.html#LIBPQ-PQBACKENDPID .. __: http://www.postgresql.org/docs/current/static/libpq-status.html#LIBPQ-PQBACKENDPID
.. versionadded:: 2.0.8 .. versionadded:: 2.0.8
@ -521,7 +521,7 @@ The ``connection`` class
.. seealso:: libpq docs for `PQparameterStatus()`__ for details. .. seealso:: libpq docs for `PQparameterStatus()`__ for details.
.. __: http://www.postgresql.org/docs/9.0/static/libpq-status.html#LIBPQ-PQPARAMETERSTATUS .. __: http://www.postgresql.org/docs/current/static/libpq-status.html#LIBPQ-PQPARAMETERSTATUS
.. versionadded:: 2.0.12 .. versionadded:: 2.0.12
@ -538,7 +538,7 @@ The ``connection`` class
.. seealso:: libpq docs for `PQtransactionStatus()`__ for details. .. seealso:: libpq docs for `PQtransactionStatus()`__ for details.
.. __: http://www.postgresql.org/docs/9.0/static/libpq-status.html#LIBPQ-PQTRANSACTIONSTATUS .. __: http://www.postgresql.org/docs/current/static/libpq-status.html#LIBPQ-PQTRANSACTIONSTATUS
.. index:: .. index::
@ -553,7 +553,7 @@ The ``connection`` class
.. seealso:: libpq docs for `PQprotocolVersion()`__ for details. .. seealso:: libpq docs for `PQprotocolVersion()`__ for details.
.. __: http://www.postgresql.org/docs/9.0/static/libpq-status.html#LIBPQ-PQPROTOCOLVERSION .. __: http://www.postgresql.org/docs/current/static/libpq-status.html#LIBPQ-PQPROTOCOLVERSION
.. versionadded:: 2.0.12 .. versionadded:: 2.0.12
@ -571,7 +571,7 @@ The ``connection`` class
.. seealso:: libpq docs for `PQserverVersion()`__ for details. .. seealso:: libpq docs for `PQserverVersion()`__ for details.
.. __: http://www.postgresql.org/docs/9.0/static/libpq-status.html#LIBPQ-PQSERVERVERSION .. __: http://www.postgresql.org/docs/current/static/libpq-status.html#LIBPQ-PQSERVERVERSION
.. versionadded:: 2.0.12 .. versionadded:: 2.0.12
@ -606,7 +606,7 @@ The ``connection`` class
`~psycopg2.extensions.lobject` to be instantiated. `~psycopg2.extensions.lobject` to be instantiated.
.. |lo_import| replace:: `!lo_import()` .. |lo_import| replace:: `!lo_import()`
.. _lo_import: http://www.postgresql.org/docs/9.0/static/lo-interfaces.html#LO-IMPORT .. _lo_import: http://www.postgresql.org/docs/current/static/lo-interfaces.html#LO-IMPORT
Available values for *mode* are: Available values for *mode* are:

View File

@ -67,10 +67,10 @@ The ``cursor`` class
|execute*|_ methods yet. |execute*|_ methods yet.
.. |pg_type| replace:: :sql:`pg_type` .. |pg_type| replace:: :sql:`pg_type`
.. _pg_type: http://www.postgresql.org/docs/9.0/static/catalog-pg-type.html .. _pg_type: http://www.postgresql.org/docs/current/static/catalog-pg-type.html
.. _PQgetlength: http://www.postgresql.org/docs/9.0/static/libpq-exec.html#LIBPQ-PQGETLENGTH .. _PQgetlength: http://www.postgresql.org/docs/current/static/libpq-exec.html#LIBPQ-PQGETLENGTH
.. _PQfsize: http://www.postgresql.org/docs/9.0/static/libpq-exec.html#LIBPQ-PQFSIZE .. _PQfsize: http://www.postgresql.org/docs/current/static/libpq-exec.html#LIBPQ-PQFSIZE
.. _NUMERIC: http://www.postgresql.org/docs/9.0/static/datatype-numeric.html#DATATYPE-NUMERIC-DECIMAL .. _NUMERIC: http://www.postgresql.org/docs/current/static/datatype-numeric.html#DATATYPE-NUMERIC-DECIMAL
.. |NUMERIC| replace:: :sql:`NUMERIC` .. |NUMERIC| replace:: :sql:`NUMERIC`
.. versionchanged:: 2.4 .. versionchanged:: 2.4
@ -378,10 +378,10 @@ The ``cursor`` class
more flexibility. more flexibility.
.. |CREATE-TABLE| replace:: :sql:`CREATE TABLE` .. |CREATE-TABLE| replace:: :sql:`CREATE TABLE`
.. __: http://www.postgresql.org/docs/9.0/static/sql-createtable.html .. __: http://www.postgresql.org/docs/current/static/sql-createtable.html
.. |INSERT-RETURNING| replace:: :sql:`INSERT ... RETURNING` .. |INSERT-RETURNING| replace:: :sql:`INSERT ... RETURNING`
.. __: http://www.postgresql.org/docs/9.0/static/sql-insert.html .. __: http://www.postgresql.org/docs/current/static/sql-insert.html
.. attribute:: query .. attribute:: query
@ -467,7 +467,7 @@ The ``cursor`` class
:param table: name of the table to copy data into. :param table: name of the table to copy data into.
:param sep: columns separator expected in the file. Defaults to a tab. :param sep: columns separator expected in the file. Defaults to a tab.
:param null: textual representation of :sql:`NULL` in the file. :param null: textual representation of :sql:`NULL` in the file.
The default is the two character string ``\N``. The default is the two characters string ``\N``.
:param size: size of the buffer used to read from the file. :param size: size of the buffer used to read from the file.
:param columns: iterable with name of the columns to import. :param columns: iterable with name of the columns to import.
The length and types should match the content of the file to read. The length and types should match the content of the file to read.
@ -500,7 +500,7 @@ The ``cursor`` class
:param table: name of the table to copy data from. :param table: name of the table to copy data from.
:param sep: columns separator expected in the file. Defaults to a tab. :param sep: columns separator expected in the file. Defaults to a tab.
:param null: textual representation of :sql:`NULL` in the file. :param null: textual representation of :sql:`NULL` in the file.
The default is the two character string ``\N``. The default is the two characters string ``\N``.
:param columns: iterable with name of the columns to export. :param columns: iterable with name of the columns to export.
If not specified, export all the columns. If not specified, export all the columns.
@ -540,7 +540,7 @@ The ``cursor`` class
... ...
.. |COPY| replace:: :sql:`COPY` .. |COPY| replace:: :sql:`COPY`
.. __: http://www.postgresql.org/docs/9.0/static/sql-copy.html .. __: http://www.postgresql.org/docs/current/static/sql-copy.html
.. versionadded:: 2.0.6 .. versionadded:: 2.0.6

View File

@ -39,7 +39,7 @@ From PostgreSQL documentation:
.. seealso:: `PostgreSQL Error Codes table`__ .. seealso:: `PostgreSQL Error Codes table`__
.. __: http://www.postgresql.org/docs/9.0/static/errcodes-appendix.html#ERRCODES-TABLE .. __: http://www.postgresql.org/docs/current/static/errcodes-appendix.html#ERRCODES-TABLE
An example of the available constants defined in the module: An example of the available constants defined in the module:

View File

@ -82,7 +82,7 @@ functionalities defined by the |DBAPI|_.
The method uses the efficient |lo_export|_ libpq function. The method uses the efficient |lo_export|_ libpq function.
.. |lo_export| replace:: `!lo_export()` .. |lo_export| replace:: `!lo_export()`
.. _lo_export: http://www.postgresql.org/docs/9.0/static/lo-interfaces.html#LO-EXPORT .. _lo_export: http://www.postgresql.org/docs/current/static/lo-interfaces.html#LO-EXPORT
.. method:: seek(offset, whence=0) .. method:: seek(offset, whence=0)
@ -103,7 +103,7 @@ functionalities defined by the |DBAPI|_.
running these versions. It uses the |lo_truncate|_ libpq function. running these versions. It uses the |lo_truncate|_ libpq function.
.. |lo_truncate| replace:: `!lo_truncate()` .. |lo_truncate| replace:: `!lo_truncate()`
.. _lo_truncate: http://www.postgresql.org/docs/9.0/static/lo-interfaces.html#LO-TRUNCATE .. _lo_truncate: http://www.postgresql.org/docs/current/static/lo-interfaces.html#LO-TRUNCATE
.. method:: close() .. method:: close()
@ -325,6 +325,20 @@ details.
.. versionadded:: 2.4.3 .. versionadded:: 2.4.3
.. _cast-array-unknown:
.. note::
The function can be used to create a generic array typecaster,
returning a list of strings: just use the `~psycopg2.STRING` as base
typecaster. For instance, if you want to receive from the database an
array of :sql:`macaddr`, each address represented by string, you can
use::
psycopg2.extensions.register_type(
psycopg2.extensions.new_array_type(
(1040,), 'MACADDR[]', psycopg2.STRING))
.. function:: register_type(obj [, scope]) .. function:: register_type(obj [, scope])
@ -349,7 +363,7 @@ details.
Used by Psycopg when adapting or casting unicode strings. See Used by Psycopg when adapting or casting unicode strings. See
:ref:`unicode-handling`. :ref:`unicode-handling`.
.. __: http://www.postgresql.org/docs/9.0/static/multibyte.html .. __: http://www.postgresql.org/docs/current/static/multibyte.html
.. __: http://docs.python.org/library/codecs.html#standard-encodings .. __: http://docs.python.org/library/codecs.html#standard-encodings
@ -432,7 +446,7 @@ set to one of the following constants:
.. seealso:: `Read Committed Isolation Level`__ in PostgreSQL .. seealso:: `Read Committed Isolation Level`__ in PostgreSQL
documentation. documentation.
.. __: http://www.postgresql.org/docs/9.1/static/transaction-iso.html#XACT-READ-COMMITTED .. __: http://www.postgresql.org/docs/current/static/transaction-iso.html#XACT-READ-COMMITTED
.. data:: ISOLATION_LEVEL_REPEATABLE_READ .. data:: ISOLATION_LEVEL_REPEATABLE_READ
@ -456,7 +470,7 @@ set to one of the following constants:
.. seealso:: `Repeatable Read Isolation Level`__ in PostgreSQL .. seealso:: `Repeatable Read Isolation Level`__ in PostgreSQL
documentation. documentation.
.. __: http://www.postgresql.org/docs/9.1/static/transaction-iso.html#XACT-REPEATABLE-READ .. __: http://www.postgresql.org/docs/current/static/transaction-iso.html#XACT-REPEATABLE-READ
.. data:: ISOLATION_LEVEL_SERIALIZABLE .. data:: ISOLATION_LEVEL_SERIALIZABLE
@ -475,7 +489,7 @@ set to one of the following constants:
.. seealso:: `Serializable Isolation Level`__ in PostgreSQL documentation. .. seealso:: `Serializable Isolation Level`__ in PostgreSQL documentation.
.. __: http://www.postgresql.org/docs/9.1/static/transaction-iso.html#XACT-SERIALIZABLE .. __: http://www.postgresql.org/docs/current/static/transaction-iso.html#XACT-SERIALIZABLE

View File

@ -155,7 +155,7 @@ can be enabled using the `register_hstore()` function.
.. autofunction:: register_hstore .. autofunction:: register_hstore
.. |hstore| replace:: :sql:`hstore` .. |hstore| replace:: :sql:`hstore`
.. _hstore: http://www.postgresql.org/docs/9.0/static/hstore.html .. _hstore: http://www.postgresql.org/docs/current/static/hstore.html
@ -177,7 +177,7 @@ after a table row type) into a Python named tuple, or into a regular tuple if
:py:func:`collections.namedtuple` is not found. :py:func:`collections.namedtuple` is not found.
.. |CREATE TYPE| replace:: :sql:`CREATE TYPE` .. |CREATE TYPE| replace:: :sql:`CREATE TYPE`
.. _CREATE TYPE: http://www.postgresql.org/docs/9.0/static/sql-createtype.html .. _CREATE TYPE: http://www.postgresql.org/docs/current/static/sql-createtype.html
.. doctest:: .. doctest::
@ -250,6 +250,7 @@ UUID data type
^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^
.. versionadded:: 2.0.9 .. versionadded:: 2.0.9
.. versionchanged:: 2.4.5 added inet array support.
.. doctest:: .. doctest::
@ -264,7 +265,7 @@ UUID data type
'192.168.0.1/24' '192.168.0.1/24'
.. autofunction:: register_inet() .. autofunction:: register_inet
.. autoclass:: Inet .. autoclass:: Inet

View File

@ -33,7 +33,7 @@ I receive the error *current transaction is aborted, commands ignored until end
PostgreSQL supports nested transactions using the |SAVEPOINT|_ command). PostgreSQL supports nested transactions using the |SAVEPOINT|_ command).
.. |SAVEPOINT| replace:: :sql:`SAVEPOINT` .. |SAVEPOINT| replace:: :sql:`SAVEPOINT`
.. _SAVEPOINT: http://www.postgresql.org/docs/9.0/static/sql-savepoint.html .. _SAVEPOINT: http://www.postgresql.org/docs/current/static/sql-savepoint.html
Why do I get the error *current transaction is aborted, commands ignored until end of transaction block* when I use `!multiprocessing` (or any other forking system) and not when use `!threading`? Why do I get the error *current transaction is aborted, commands ignored until end of transaction block* when I use `!multiprocessing` (or any other forking system) and not when use `!threading`?
Psycopg's connections can't be shared across processes (but are thread Psycopg's connections can't be shared across processes (but are thread
@ -106,8 +106,15 @@ Transferring binary data from PostgreSQL 9.0 doesn't work.
session before reading binary data; session before reading binary data;
- upgrade the libpq library on the client to at least 9.0. - upgrade the libpq library on the client to at least 9.0.
.. __: http://www.postgresql.org/docs/9.0/static/datatype-binary.html .. __: http://www.postgresql.org/docs/current/static/datatype-binary.html
.. __: http://www.postgresql.org/docs/9.0/static/runtime-config-client.html#GUC-BYTEA-OUTPUT .. __: http://www.postgresql.org/docs/current/static/runtime-config-client.html#GUC-BYTEA-OUTPUT
Arrays of *TYPE* are not casted to list.
Arrays are only casted to list when their oid is known, and an array
typecaster is registered for them. If there is no typecaster, the array is
returned unparsed from PostgreSQL (e.g. ``{a,b,c}``). It is easy to create
a generic arrays typecaster, returning a list of array: an example is
provided in the `~psycopg2.extensions.new_array_type()` documentation.
Best practices Best practices

View File

@ -32,9 +32,9 @@ Psycopg 2 is both Unicode and Python 3 friendly.
.. _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/ .. _Zope: http://www.zope.org/
.. _libpq: http://www.postgresql.org/docs/9.0/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/9.0/static/sql-copy.html .. __: http://www.postgresql.org/docs/current/static/sql-copy.html
.. rubric:: Contents .. rubric:: Contents

View File

@ -43,8 +43,8 @@ The module interface respects the standard defined in the |DBAPI|_.
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/9.1/static/libpq-connect.html#LIBPQ-PQCONNECTDBPARAMS .. __: http://www.postgresql.org/docs/current/static/libpq-connect.html#LIBPQ-PQCONNECTDBPARAMS
.. __: http://www.postgresql.org/docs/9.1/static/libpq-envars.html .. __: 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
@ -117,31 +117,30 @@ available through the following exceptions:
if not available. The `~psycopg2.errorcodes` module contains if not available. The `~psycopg2.errorcodes` module contains
symbolic constants representing PostgreSQL error codes. symbolic constants representing PostgreSQL error codes.
.. doctest::
:options: +NORMALIZE_WHITESPACE
>>> try:
... cur.execute("SELECT * FROM barf")
... except Exception, e:
... pass
>>> e.pgcode
'42P01'
>>> print e.pgerror
ERROR: relation "barf" does not exist
LINE 1: SELECT * FROM barf
^
.. attribute:: cursor
The cursor the exception was raised from; `None` if not applicable.
.. extension:: .. extension::
The `~Error.pgerror` and `~Error.pgcode` attributes are The `~Error.pgerror`, `~Error.pgcode`, and `~Error.cursor` attributes
Psycopg extensions. are Psycopg extensions.
.. doctest::
:options: +NORMALIZE_WHITESPACE
>>> try:
... cur.execute("SELECT * FROM barf")
... except Exception, e:
... pass
>>> e.pgcode
'42P01'
>>> print e.pgerror
ERROR: relation "barf" does not exist
LINE 1: SELECT * FROM barf
^
.. versionchanged:: 2.0.7 added `Error.pgerror` and
`Error.pgcode` attributes.
.. exception:: InterfaceError .. exception:: InterfaceError
Exception raised for errors that are related to the database interface Exception raised for errors that are related to the database interface

View File

@ -294,9 +294,9 @@ the SQL string that would be sent to the database.
`bytea_output`__ configuration parameter to ``escape``, either in the `bytea_output`__ configuration parameter to ``escape``, either in the
server configuration file or in the client session (using a query such as server configuration file or in the client session (using a query such as
``SET bytea_output TO escape;``) before receiving binary data. ``SET bytea_output TO escape;``) before receiving binary data.
.. __: http://www.postgresql.org/docs/9.0/static/datatype-binary.html .. __: http://www.postgresql.org/docs/current/static/datatype-binary.html
.. __: http://www.postgresql.org/docs/9.0/static/runtime-config-client.html#GUC-BYTEA-OUTPUT .. __: http://www.postgresql.org/docs/current/static/runtime-config-client.html#GUC-BYTEA-OUTPUT
.. _adapt-date: .. _adapt-date:
@ -334,6 +334,14 @@ the SQL string that would be sent to the database.
>>> cur.mogrify("SELECT %s;", ([10, 20, 30], )) >>> cur.mogrify("SELECT %s;", ([10, 20, 30], ))
'SELECT ARRAY[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: .. _adapt-tuple:
.. index:: .. index::
@ -378,7 +386,7 @@ the SQL string that would be sent to the database.
further details. further details.
.. |hstore| replace:: :sql:`hstore` .. |hstore| replace:: :sql:`hstore`
.. _hstore: http://www.postgresql.org/docs/9.0/static/hstore.html .. _hstore: http://www.postgresql.org/docs/current/static/hstore.html
.. versionadded:: 2.3 .. versionadded:: 2.3
the :sql:`hstore` adaptation. the :sql:`hstore` adaptation.
@ -403,7 +411,7 @@ defined on the database connection (the `PostgreSQL encoding`__, available in
>>> cur.execute("INSERT INTO test (num, data) VALUES (%s,%s);", (74, u)) >>> cur.execute("INSERT INTO test (num, data) VALUES (%s,%s);", (74, u))
.. __: http://www.postgresql.org/docs/9.0/static/multibyte.html .. __: http://www.postgresql.org/docs/current/static/multibyte.html
.. __: http://docs.python.org/library/codecs.html#standard-encodings .. __: http://docs.python.org/library/codecs.html#standard-encodings
When reading data from the database, in Python 2 the strings returned are When reading data from the database, in Python 2 the strings returned are
@ -621,7 +629,7 @@ lifetime extends well after `~connection.commit()`, calling
.. |DECLARE| replace:: :sql:`DECLARE` .. |DECLARE| replace:: :sql:`DECLARE`
.. _DECLARE: http://www.postgresql.org/docs/9.0/static/sql-declare.html .. _DECLARE: http://www.postgresql.org/docs/current/static/sql-declare.html
@ -651,7 +659,7 @@ forked processes`__, so when using a module such as `multiprocessing` or a
forking web deploy method such as FastCGI make sure to create the connections forking web deploy method such as FastCGI make sure to create the connections
*after* the fork. *after* the fork.
.. __: http://www.postgresql.org/docs/9.0/static/libpq-connect.html#LIBPQ-CONNECT .. __: http://www.postgresql.org/docs/current/static/libpq-connect.html#LIBPQ-CONNECT
Connections shouldn't be shared either by different green threads: see Connections shouldn't be shared either by different green threads: see
:ref:`green-support` for further details. :ref:`green-support` for further details.
@ -687,7 +695,7 @@ Please refer to the documentation of the single methods for details and
examples. examples.
.. |COPY| replace:: :sql:`COPY` .. |COPY| replace:: :sql:`COPY`
.. __: http://www.postgresql.org/docs/9.0/static/sql-copy.html .. __: http://www.postgresql.org/docs/current/static/sql-copy.html
@ -704,7 +712,7 @@ access to user data that is stored in a special large-object structure. They
are useful with data values too large to be manipulated conveniently as a are useful with data values too large to be manipulated conveniently as a
whole. whole.
.. __: http://www.postgresql.org/docs/9.0/static/largeobjects.html .. __: http://www.postgresql.org/docs/current/static/largeobjects.html
Psycopg allows access to the large object using the Psycopg allows access to the large object using the
`~psycopg2.extensions.lobject` class. Objects are generated using the `~psycopg2.extensions.lobject` class. Objects are generated using the
@ -715,9 +723,9 @@ Psycopg large object support efficient import/export with file system files
using the |lo_import|_ and |lo_export|_ libpq functions. using the |lo_import|_ and |lo_export|_ libpq functions.
.. |lo_import| replace:: `!lo_import()` .. |lo_import| replace:: `!lo_import()`
.. _lo_import: http://www.postgresql.org/docs/9.0/static/lo-interfaces.html#LO-IMPORT .. _lo_import: http://www.postgresql.org/docs/current/static/lo-interfaces.html#LO-IMPORT
.. |lo_export| replace:: `!lo_export()` .. |lo_export| replace:: `!lo_export()`
.. _lo_export: http://www.postgresql.org/docs/9.0/static/lo-interfaces.html#LO-EXPORT .. _lo_export: http://www.postgresql.org/docs/current/static/lo-interfaces.html#LO-EXPORT

View File

@ -26,7 +26,7 @@ This module contains symbolic names for all PostgreSQL error codes.
# #
# Based on: # Based on:
# #
# http://www.postgresql.org/docs/8.4/static/errcodes-appendix.html # http://www.postgresql.org/docs/current/static/errcodes-appendix.html
# #
def lookup(code, _cache={}): def lookup(code, _cache={}):

View File

@ -88,25 +88,17 @@ class DictCursorBase(_cursor):
def __iter__(self): def __iter__(self):
if self._prefetch: if self._prefetch:
res = _cursor.fetchmany(self, self.itersize) res = _cursor.__iter__(self)
if not res: first = res.next()
return
if self._query_executed: if self._query_executed:
self._build_index() self._build_index()
if not self._prefetch: if not self._prefetch:
res = _cursor.fetchmany(self, self.itersize) res = _cursor.__iter__(self)
first = res.next()
for r in res: yield first
yield r
# the above was the first itersize record. the following are
# in a repeated loop.
while 1: while 1:
res = _cursor.fetchmany(self, self.itersize) yield res.next()
if not res:
return
for r in res:
yield r
class DictConnection(_connection): class DictConnection(_connection):
@ -318,14 +310,17 @@ class NamedTupleCursor(_cursor):
return [nt(*t) for t in ts] return [nt(*t) for t in ts]
def __iter__(self): def __iter__(self):
# Invoking _cursor.__iter__(self) goes to infinite recursion, it = _cursor.__iter__(self)
# so we do pagination by hand t = it.next()
nt = self.Record
if nt is None:
nt = self.Record = self._make_nt()
yield nt(*t)
while 1: while 1:
recs = self.fetchmany(self.itersize) yield nt(*it.next())
if not recs:
return
for rec in recs:
yield rec
try: try:
from collections import namedtuple from collections import namedtuple
@ -445,7 +440,7 @@ class UUID_adapter(object):
"""Adapt Python's uuid.UUID__ type to PostgreSQL's uuid__. """Adapt Python's uuid.UUID__ type to PostgreSQL's uuid__.
.. __: http://docs.python.org/library/uuid.html .. __: http://docs.python.org/library/uuid.html
.. __: http://www.postgresql.org/docs/8.4/static/datatype-uuid.html .. __: http://www.postgresql.org/docs/current/static/datatype-uuid.html
""" """
def __init__(self, uuid): def __init__(self, uuid):
@ -460,31 +455,29 @@ class UUID_adapter(object):
__str__ = getquoted __str__ = getquoted
def register_uuid(oids=None, conn_or_curs=None): def register_uuid(oids=None, conn_or_curs=None):
"""Create the UUID type and an uuid.UUID adapter.""" """Create the UUID type and an uuid.UUID adapter.
:param oids: oid for the PostgreSQL :sql:`uuid` type, or 2-items sequence
with oids of the type and the array. If not specified, use PostgreSQL
standard oids.
:param conn_or_curs: where to register the typecaster. If not specified,
register it globally.
"""
import uuid import uuid
if not oids: if not oids:
oid1 = 2950 oid1 = 2950
oid2 = 2951 oid2 = 2951
elif type(oids) == list: elif isinstance(oids, (list, tuple)):
oid1, oid2 = oids oid1, oid2 = oids
else: else:
oid1 = oids oid1 = oids
oid2 = 2951 oid2 = 2951
def parseUUIDARRAY(data, cursor):
if data is None:
return None
elif data == '{}':
return []
else:
return [((len(x) > 0 and x != 'NULL') and uuid.UUID(x) or None)
for x in data[1:-1].split(',')]
_ext.UUID = _ext.new_type((oid1, ), "UUID", _ext.UUID = _ext.new_type((oid1, ), "UUID",
lambda data, cursor: data and uuid.UUID(data) or None) lambda data, cursor: data and uuid.UUID(data) or None)
_ext.UUIDARRAY = _ext.new_type((oid2,), "UUID[]", parseUUIDARRAY) _ext.UUIDARRAY = _ext.new_array_type((oid2,), "UUID[]", _ext.UUID)
_ext.register_type(_ext.UUID, conn_or_curs) _ext.register_type(_ext.UUID, conn_or_curs)
_ext.register_type(_ext.UUIDARRAY, conn_or_curs) _ext.register_type(_ext.UUIDARRAY, conn_or_curs)
@ -505,13 +498,13 @@ class Inet(object):
""" """
def __init__(self, addr): def __init__(self, addr):
self.addr = addr self.addr = addr
def __repr__(self): def __repr__(self):
return "%s(%r)" % (self.__class__.__name__, self.addr) return "%s(%r)" % (self.__class__.__name__, self.addr)
def prepare(self, conn): def prepare(self, conn):
self._conn = conn self._conn = conn
def getquoted(self): def getquoted(self):
obj = _A(self.addr) obj = _A(self.addr)
if hasattr(obj, 'prepare'): if hasattr(obj, 'prepare'):
@ -524,13 +517,32 @@ class Inet(object):
def __str__(self): def __str__(self):
return str(self.addr) return str(self.addr)
def register_inet(oid=None, conn_or_curs=None): def register_inet(oid=None, conn_or_curs=None):
"""Create the INET type and an Inet adapter.""" """Create the INET type and an Inet adapter.
if not oid: oid = 869
_ext.INET = _ext.new_type((oid, ), "INET", :param oid: oid for the PostgreSQL :sql:`inet` type, or 2-items sequence
with oids of the type and the array. If not specified, use PostgreSQL
standard oids.
:param conn_or_curs: where to register the typecaster. If not specified,
register it globally.
"""
if not oid:
oid1 = 869
oid2 = 1041
elif isinstance(oid, (list, tuple)):
oid1, oid2 = oid
else:
oid1 = oid
oid2 = 1041
_ext.INET = _ext.new_type((oid1, ), "INET",
lambda data, cursor: data and Inet(data) or None) lambda data, cursor: data and Inet(data) or None)
_ext.INETARRAY = _ext.new_array_type((oid2, ), "INETARRAY", _ext.INET)
_ext.register_type(_ext.INET, conn_or_curs) _ext.register_type(_ext.INET, conn_or_curs)
_ext.register_type(_ext.INETARRAY, conn_or_curs)
return _ext.INET return _ext.INET
@ -849,9 +861,9 @@ class CompositeCaster(object):
return self._ctor(*attrs) return self._ctor(*attrs)
_re_tokenize = regex.compile(r""" _re_tokenize = regex.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) """, regex.VERBOSE)
_re_undouble = regex.compile(r'(["\\])\1') _re_undouble = regex.compile(r'(["\\])\1')
@ -861,7 +873,7 @@ class CompositeCaster(object):
rv = [] rv = []
for m in self._re_tokenize.finditer(s): for m in self._re_tokenize.finditer(s):
if m is None: if m is None:
raise psycopg2.InterfaceError("can't parse type: %r", s) raise psycopg2.InterfaceError("can't parse type: %r" % s)
if m.group(1): if m.group(1):
rv.append(None) rv.append(None)
elif m.group(2): elif m.group(2):

View File

@ -38,20 +38,40 @@ class FixedOffsetTimezone(datetime.tzinfo):
with a small change to the `!__init__()` method to allow for pickling with a small change to the `!__init__()` method to allow for pickling
and a default name in the form ``sHH:MM`` (``s`` is the sign.). and a default name in the form ``sHH:MM`` (``s`` is the sign.).
The implementation also caches instances. During creation, if a
FixedOffsetTimezone instance has previously been created with the same
offset and name that instance will be returned. This saves memory and
improves comparability.
.. __: http://docs.python.org/library/datetime.html#datetime-tzinfo .. __: http://docs.python.org/library/datetime.html#datetime-tzinfo
""" """
_name = None _name = None
_offset = ZERO _offset = ZERO
_cache = {}
def __init__(self, offset=None, name=None): def __init__(self, offset=None, name=None):
if offset is not None: if offset is not None:
self._offset = datetime.timedelta(minutes = offset) self._offset = datetime.timedelta(minutes = offset)
if name is not None: if name is not None:
self._name = name self._name = name
def __new__(cls, offset=None, name=None):
"""Return a suitable instance created earlier if it exists
"""
key = (offset, name)
try:
return cls._cache[key]
except KeyError:
tz = datetime.tzinfo.__new__(cls, offset, name)
tz.__init__(offset, name)
cls._cache[key] = tz
return tz
def __repr__(self): def __repr__(self):
offset_mins = self._offset.seconds // 60 + self._offset.days * 24 * 60
return "psycopg2.tz.FixedOffsetTimezone(offset=%r, name=%r)" \ return "psycopg2.tz.FixedOffsetTimezone(offset=%r, name=%r)" \
% (self._offset.seconds // 60, self._name) % (offset_mins, self._name)
def utcoffset(self, dt): def utcoffset(self, dt):
return self._offset return self._offset

View File

@ -65,6 +65,13 @@ binary_quote(binaryObject *self)
int got_view = 0; int got_view = 0;
#endif #endif
/* Allow Binary(None) to work */
if (self->wrapped == Py_None) {
Py_INCREF(psyco_null);
rv = psyco_null;
goto exit;
}
/* if we got a plain string or a buffer we escape it and save the buffer */ /* if we got a plain string or a buffer we escape it and save the buffer */
#if HAS_MEMORYVIEW #if HAS_MEMORYVIEW
@ -93,7 +100,7 @@ binary_quote(binaryObject *self)
/* escape and build quoted buffer */ /* escape and build quoted buffer */
to = (char *)binary_escape((unsigned char*)buffer, (size_t) buffer_len, to = (char *)binary_escape((unsigned char*)buffer, (size_t)buffer_len,
&len, self->conn ? ((connectionObject*)self->conn)->pgconn : NULL); &len, self->conn ? ((connectionObject*)self->conn)->pgconn : NULL);
if (to == NULL) { if (to == NULL) {
PyErr_NoMemory(); PyErr_NoMemory();
@ -113,12 +120,6 @@ exit:
if (got_view) { PyBuffer_Release(&view); } if (got_view) { PyBuffer_Release(&view); }
#endif #endif
/* Allow Binary(None) to work */
if (self->wrapped == Py_None) {
Py_INCREF(psyco_null);
rv = psyco_null;
}
/* if the wrapped object is not bytes or a buffer, this is an error */ /* if the wrapped object is not bytes or a buffer, this is an error */
if (!rv && !PyErr_Occurred()) { if (!rv && !PyErr_Occurred()) {
PyErr_Format(PyExc_TypeError, "can't escape %s to binary", PyErr_Format(PyExc_TypeError, "can't escape %s to binary",
@ -149,16 +150,14 @@ binary_str(binaryObject *self)
static PyObject * static PyObject *
binary_prepare(binaryObject *self, PyObject *args) binary_prepare(binaryObject *self, PyObject *args)
{ {
connectionObject *conn; PyObject *conn;
if (!PyArg_ParseTuple(args, "O", &conn)) if (!PyArg_ParseTuple(args, "O!", &connectionType, &conn))
return NULL; return NULL;
Py_XDECREF(self->conn); Py_XDECREF(self->conn);
if (conn) { self->conn = conn;
self->conn = (PyObject*)conn; Py_INCREF(self->conn);
Py_INCREF(self->conn);
}
Py_INCREF(Py_None); Py_INCREF(Py_None);
return Py_None; return Py_None;

View File

@ -427,6 +427,10 @@ psyco_DateFromTicks(PyObject *self, PyObject *args)
Py_DECREF(args); Py_DECREF(args);
} }
} }
else {
PyErr_SetString(InterfaceError, "failed localtime call");
}
return res; return res;
} }
@ -451,6 +455,10 @@ psyco_TimeFromTicks(PyObject *self, PyObject *args)
Py_DECREF(args); Py_DECREF(args);
} }
} }
else {
PyErr_SetString(InterfaceError, "failed localtime call");
}
return res; return res;
} }
@ -473,6 +481,9 @@ psyco_TimestampFromTicks(PyObject *self, PyObject *args)
tm.tm_hour, tm.tm_min, (double)tm.tm_sec + ticks, tm.tm_hour, tm.tm_min, (double)tm.tm_sec + ticks,
pyPsycopgTzLOCAL); pyPsycopgTzLOCAL);
} }
else {
PyErr_SetString(InterfaceError, "failed localtime call");
}
return res; return res;
} }

View File

@ -98,9 +98,9 @@ list_getquoted(listObject *self, PyObject *args)
static PyObject * static PyObject *
list_prepare(listObject *self, PyObject *args) list_prepare(listObject *self, PyObject *args)
{ {
connectionObject *conn; PyObject *conn;
if (!PyArg_ParseTuple(args, "O", &conn)) if (!PyArg_ParseTuple(args, "O!", &connectionType, &conn))
return NULL; return NULL;
/* note that we don't copy the encoding from the connection, but take a /* note that we don't copy the encoding from the connection, but take a
@ -109,7 +109,7 @@ list_prepare(listObject *self, PyObject *args)
work even without a connection to the backend. */ work even without a connection to the backend. */
Py_CLEAR(self->connection); Py_CLEAR(self->connection);
Py_INCREF(conn); Py_INCREF(conn);
self->connection = (PyObject*)conn; self->connection = conn;
Py_INCREF(Py_None); Py_INCREF(Py_None);
return Py_None; return Py_None;

View File

@ -35,7 +35,7 @@
/* qstring_quote - do the quote process on plain and unicode strings */ /* qstring_quote - do the quote process on plain and unicode strings */
static PyObject * BORROWED static PyObject *
qstring_quote(qstringObject *self) qstring_quote(qstringObject *self)
{ {
PyObject *str; PyObject *str;
@ -124,24 +124,22 @@ qstring_str(qstringObject *self)
static PyObject * static PyObject *
qstring_prepare(qstringObject *self, PyObject *args) qstring_prepare(qstringObject *self, PyObject *args)
{ {
connectionObject *conn; PyObject *conn;
if (!PyArg_ParseTuple(args, "O", &conn)) if (!PyArg_ParseTuple(args, "O!", &connectionType, &conn))
return NULL; return NULL;
/* we bother copying the encoding only if the wrapped string is unicode, /* we bother copying the encoding only if the wrapped string is unicode,
we don't need the encoding if that's not the case */ we don't need the encoding if that's not the case */
if (PyUnicode_Check(self->wrapped)) { if (PyUnicode_Check(self->wrapped)) {
if (self->encoding) free(self->encoding); if (self->encoding) free(self->encoding);
self->encoding = strdup(conn->codec); self->encoding = strdup(((connectionObject *)conn)->codec);
Dprintf("qstring_prepare: set encoding to %s", conn->codec); Dprintf("qstring_prepare: set encoding to %s", self->encoding);
} }
Py_CLEAR(self->conn); Py_CLEAR(self->conn);
if (conn) { Py_INCREF(conn);
Py_INCREF(conn); self->conn = conn;
self->conn = (PyObject*)conn;
}
Py_INCREF(Py_None); Py_INCREF(Py_None);
return Py_None; return Py_None;

View File

@ -86,7 +86,7 @@
/* Helpers for formatstring */ /* Helpers for formatstring */
Py_LOCAL_INLINE(PyObject *) BORROWED Py_LOCAL_INLINE(PyObject *)
getnextarg(PyObject *args, Py_ssize_t arglen, Py_ssize_t *p_argidx) getnextarg(PyObject *args, Py_ssize_t arglen, Py_ssize_t *p_argidx)
{ {
Py_ssize_t argidx = *p_argidx; Py_ssize_t argidx = *p_argidx;

View File

@ -160,4 +160,33 @@ static double round(double num)
#define isinf(x) (!finite((x)) && (x)==(x)) #define isinf(x) (!finite((x)) && (x)==(x))
#endif #endif
/* decorators for the gcc cpychecker plugin */
#if defined(WITH_CPYCHECKER_RETURNS_BORROWED_REF_ATTRIBUTE)
#define BORROWED \
__attribute__((cpychecker_returns_borrowed_ref))
#else
#define BORROWED
#endif
#if defined(WITH_CPYCHECKER_STEALS_REFERENCE_TO_ARG_ATTRIBUTE)
#define STEALS(n) \
__attribute__((cpychecker_steals_reference_to_arg(n)))
#else
#define STEALS(n)
#endif
#if defined(WITH_CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION_ATTRIBUTE)
#define RAISES_NEG \
__attribute__((cpychecker_negative_result_sets_exception))
#else
#define RAISES_NEG
#endif
#if defined(WITH_CPYCHECKER_SETS_EXCEPTION_ATTRIBUTE)
#define RAISES \
__attribute__((cpychecker_sets_exception))
#else
#define RAISES
#endif
#endif /* !defined(PSYCOPG_CONFIG_H) */ #endif /* !defined(PSYCOPG_CONFIG_H) */

View File

@ -131,27 +131,27 @@ typedef struct {
/* C-callable functions in connection_int.c and connection_ext.c */ /* C-callable functions in connection_int.c and connection_ext.c */
HIDDEN PyObject *conn_text_from_chars(connectionObject *pgconn, const char *str); HIDDEN PyObject *conn_text_from_chars(connectionObject *pgconn, const char *str);
HIDDEN int conn_get_standard_conforming_strings(PGconn *pgconn); HIDDEN int conn_get_standard_conforming_strings(PGconn *pgconn);
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 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);
HIDDEN int conn_setup(connectionObject *self, PGconn *pgconn); RAISES_NEG HIDDEN int conn_setup(connectionObject *self, PGconn *pgconn);
HIDDEN int conn_connect(connectionObject *self, long int async); HIDDEN int conn_connect(connectionObject *self, long int async);
HIDDEN void conn_close(connectionObject *self); HIDDEN void conn_close(connectionObject *self);
HIDDEN int conn_commit(connectionObject *self); RAISES_NEG HIDDEN int conn_commit(connectionObject *self);
HIDDEN int conn_rollback(connectionObject *self); RAISES_NEG HIDDEN int conn_rollback(connectionObject *self);
HIDDEN int conn_set_session(connectionObject *self, const char *isolevel, RAISES_NEG HIDDEN int conn_set_session(connectionObject *self, const char *isolevel,
const char *readonly, const char *deferrable, const char *readonly, const char *deferrable,
int autocommit); int autocommit);
HIDDEN int conn_set_autocommit(connectionObject *self, int value); HIDDEN int conn_set_autocommit(connectionObject *self, int value);
HIDDEN int conn_switch_isolation_level(connectionObject *self, int level); RAISES_NEG HIDDEN int conn_switch_isolation_level(connectionObject *self, int level);
HIDDEN int conn_set_client_encoding(connectionObject *self, const char *enc); RAISES_NEG HIDDEN int conn_set_client_encoding(connectionObject *self, const char *enc);
HIDDEN int conn_poll(connectionObject *self); HIDDEN int conn_poll(connectionObject *self);
HIDDEN int conn_tpc_begin(connectionObject *self, XidObject *xid); RAISES_NEG HIDDEN int conn_tpc_begin(connectionObject *self, XidObject *xid);
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);

View File

@ -120,8 +120,16 @@ conn_notice_process(connectionObject *self)
/* Respect the order in which notices were produced, /* Respect the order in which notices were produced,
because in notice_list they are reversed (see ticket #9) */ because in notice_list they are reversed (see ticket #9) */
PyList_Insert(self->notice_list, nnotices, msg); if (msg) {
Py_DECREF(msg); PyList_Insert(self->notice_list, nnotices, msg);
Py_DECREF(msg);
}
else {
/* We don't really have a way to report errors, so gulp it.
* The function should only fail for out of memory, so we are
* likely going to die anyway. */
PyErr_Clear();
}
notice = notice->next; notice = notice->next;
} }
@ -242,19 +250,20 @@ conn_get_standard_conforming_strings(PGconn *pgconn)
/* Remove irrelevant chars from encoding name and turn it uppercase. /* Remove irrelevant chars from encoding name and turn it uppercase.
* *
* Return a buffer allocated on Python heap, * Return a buffer allocated on Python heap into 'clean' and return 0 on
* NULL and set an exception on error. * success, otherwise return -1 and set an exception.
*/ */
static char * RAISES_NEG static int
clean_encoding_name(const char *enc) clear_encoding_name(const char *enc, char **clean)
{ {
const char *i = enc; const char *i = enc;
char *rv, *j; char *j, *buf;
int rv = -1;
/* convert to upper case and remove '-' and '_' from string */ /* convert to upper case and remove '-' and '_' from string */
if (!(j = rv = PyMem_Malloc(strlen(enc) + 1))) { if (!(j = buf = PyMem_Malloc(strlen(enc) + 1))) {
PyErr_NoMemory(); PyErr_NoMemory();
return NULL; goto exit;
} }
while (*i) { while (*i) {
@ -267,25 +276,28 @@ clean_encoding_name(const char *enc)
} }
*j = '\0'; *j = '\0';
Dprintf("clean_encoding_name: %s -> %s", enc, rv); Dprintf("clear_encoding_name: %s -> %s", enc, buf);
*clean = buf;
rv = 0;
exit:
return rv; return rv;
} }
/* Convert a PostgreSQL encoding to a Python codec. /* Convert a PostgreSQL encoding to a Python codec.
* *
* Return a new copy of the codec name allocated on the Python heap, * Set 'codec' to a new copy of the codec name allocated on the Python heap.
* NULL with exception in case of error. * Return 0 in case of success, else -1 and set an exception.
* *
* 'enc' should be already normalized (uppercase, no - or _). * 'enc' should be already normalized (uppercase, no - or _).
*/ */
static char * RAISES_NEG static int
conn_encoding_to_codec(const char *enc) conn_encoding_to_codec(const char *enc, char **codec)
{ {
char *tmp; char *tmp;
Py_ssize_t size; Py_ssize_t size;
PyObject *pyenc = NULL; PyObject *pyenc = NULL;
char *rv = NULL; int rv = -1;
/* Find the Py codec name from the PG encoding */ /* Find the Py codec name from the PG encoding */
if (!(pyenc = PyDict_GetItemString(psycoEncodings, enc))) { if (!(pyenc = PyDict_GetItemString(psycoEncodings, enc))) {
@ -305,7 +317,7 @@ conn_encoding_to_codec(const char *enc)
} }
/* have our own copy of the python codec name */ /* have our own copy of the python codec name */
rv = psycopg_strdup(tmp, size); rv = psycopg_strdup(codec, tmp, size);
exit: exit:
Py_XDECREF(pyenc); Py_XDECREF(pyenc);
@ -320,7 +332,7 @@ exit:
* *
* Return 0 on success, else nonzero. * Return 0 on success, else nonzero.
*/ */
static int RAISES_NEG static int
conn_read_encoding(connectionObject *self, PGconn *pgconn) conn_read_encoding(connectionObject *self, PGconn *pgconn)
{ {
char *enc = NULL, *codec = NULL; char *enc = NULL, *codec = NULL;
@ -335,12 +347,12 @@ conn_read_encoding(connectionObject *self, PGconn *pgconn)
goto exit; goto exit;
} }
if (!(enc = clean_encoding_name(tmp))) { if (0 > clear_encoding_name(tmp, &enc)) {
goto exit; goto exit;
} }
/* Look for this encoding in Python codecs. */ /* Look for this encoding in Python codecs. */
if (!(codec = conn_encoding_to_codec(enc))) { if (0 > conn_encoding_to_codec(enc, &codec)) {
goto exit; goto exit;
} }
@ -362,7 +374,7 @@ exit:
} }
int RAISES_NEG int
conn_get_isolation_level(connectionObject *self) conn_get_isolation_level(connectionObject *self)
{ {
PGresult *pgres = NULL; PGresult *pgres = NULL;
@ -456,7 +468,7 @@ conn_is_datestyle_ok(PGconn *pgconn)
/* conn_setup - setup and read basic information about the connection */ /* conn_setup - setup and read basic information about the connection */
int RAISES_NEG int
conn_setup(connectionObject *self, PGconn *pgconn) conn_setup(connectionObject *self, PGconn *pgconn)
{ {
PGresult *pgres = NULL; PGresult *pgres = NULL;
@ -470,7 +482,7 @@ conn_setup(connectionObject *self, PGconn *pgconn)
return -1; return -1;
} }
if (conn_read_encoding(self, pgconn)) { if (0 > conn_read_encoding(self, pgconn)) {
return -1; return -1;
} }
@ -484,7 +496,7 @@ conn_setup(connectionObject *self, PGconn *pgconn)
pthread_mutex_lock(&self->lock); pthread_mutex_lock(&self->lock);
Py_BLOCK_THREADS; Py_BLOCK_THREADS;
if (psyco_green() && (pq_set_non_blocking(self, 1, 1) != 0)) { if (psyco_green() && (0 > pq_set_non_blocking(self, 1))) {
return -1; return -1;
} }
@ -762,7 +774,7 @@ _conn_poll_setup_async(connectionObject *self)
switch (self->status) { switch (self->status) {
case CONN_STATUS_CONNECTING: case CONN_STATUS_CONNECTING:
/* Set the connection to nonblocking now. */ /* Set the connection to nonblocking now. */
if (pq_set_non_blocking(self, 1, 1) != 0) { if (pq_set_non_blocking(self, 1) != 0) {
break; break;
} }
@ -773,7 +785,7 @@ _conn_poll_setup_async(connectionObject *self)
PyErr_SetString(InterfaceError, "only protocol 3 supported"); PyErr_SetString(InterfaceError, "only protocol 3 supported");
break; break;
} }
if (conn_read_encoding(self, self->pgconn)) { if (0 > conn_read_encoding(self, self->pgconn)) {
break; break;
} }
self->cancel = conn_get_cancel(self->pgconn); self->cancel = conn_get_cancel(self->pgconn);
@ -906,6 +918,10 @@ conn_poll(connectionObject *self)
void void
conn_close(connectionObject *self) conn_close(connectionObject *self)
{ {
if (self->closed) {
return;
}
/* sets this connection as closed even for other threads; also note that /* sets this connection as closed even for other threads; also note that
we need to check the value of pgconn, because we get called even when we need to check the value of pgconn, because we get called even when
the connection fails! */ the connection fails! */
@ -922,14 +938,13 @@ conn_close(connectionObject *self)
* closed only in status CONN_STATUS_READY. * closed only in status CONN_STATUS_READY.
*/ */
if (self->closed == 0) self->closed = 1;
self->closed = 1;
if (self->pgconn) { if (self->pgconn) {
PQfinish(self->pgconn); PQfinish(self->pgconn);
PQfreeCancel(self->cancel);
Dprintf("conn_close: PQfinish called");
self->pgconn = NULL; self->pgconn = NULL;
Dprintf("conn_close: PQfinish called");
PQfreeCancel(self->cancel);
self->cancel = NULL; self->cancel = NULL;
} }
@ -939,7 +954,7 @@ conn_close(connectionObject *self)
/* conn_commit - commit on a connection */ /* conn_commit - commit on a connection */
int RAISES_NEG int
conn_commit(connectionObject *self) conn_commit(connectionObject *self)
{ {
int res; int res;
@ -950,7 +965,7 @@ conn_commit(connectionObject *self)
/* conn_rollback - rollback a connection */ /* conn_rollback - rollback a connection */
int RAISES_NEG int
conn_rollback(connectionObject *self) conn_rollback(connectionObject *self)
{ {
int res; int res;
@ -959,7 +974,7 @@ conn_rollback(connectionObject *self)
return res; return res;
} }
int RAISES_NEG int
conn_set_session(connectionObject *self, conn_set_session(connectionObject *self,
const char *isolevel, const char *readonly, const char *deferrable, const char *isolevel, const char *readonly, const char *deferrable,
int autocommit) int autocommit)
@ -1032,7 +1047,7 @@ conn_set_autocommit(connectionObject *self, int value)
/* conn_switch_isolation_level - switch isolation level on the connection */ /* conn_switch_isolation_level - switch isolation level on the connection */
int RAISES_NEG int
conn_switch_isolation_level(connectionObject *self, int level) conn_switch_isolation_level(connectionObject *self, int level)
{ {
PGresult *pgres = NULL; PGresult *pgres = NULL;
@ -1114,12 +1129,12 @@ endlock:
/* conn_set_client_encoding - switch client encoding on connection */ /* conn_set_client_encoding - switch client encoding on connection */
int RAISES_NEG int
conn_set_client_encoding(connectionObject *self, const char *enc) conn_set_client_encoding(connectionObject *self, const char *enc)
{ {
PGresult *pgres = NULL; PGresult *pgres = NULL;
char *error = NULL; char *error = NULL;
int res = 1; int res = -1;
char *codec = NULL; char *codec = NULL;
char *clean_enc = NULL; char *clean_enc = NULL;
@ -1128,8 +1143,8 @@ conn_set_client_encoding(connectionObject *self, const char *enc)
if (strcmp(self->encoding, enc) == 0) return 0; if (strcmp(self->encoding, enc) == 0) return 0;
/* We must know what python codec this encoding is. */ /* We must know what python codec this encoding is. */
if (!(clean_enc = clean_encoding_name(enc))) { goto exit; } if (0 > clear_encoding_name(enc, &clean_enc)) { goto exit; }
if (!(codec = conn_encoding_to_codec(clean_enc))) { goto exit; } if (0 > conn_encoding_to_codec(clean_enc, &codec)) { goto exit; }
Py_BEGIN_ALLOW_THREADS; Py_BEGIN_ALLOW_THREADS;
pthread_mutex_lock(&self->lock); pthread_mutex_lock(&self->lock);
@ -1187,7 +1202,7 @@ exit:
* in regular transactions, as PostgreSQL won't even know we are in a TPC * in regular transactions, as PostgreSQL won't even know we are in a TPC
* until PREPARE. */ * until PREPARE. */
int RAISES_NEG int
conn_tpc_begin(connectionObject *self, XidObject *xid) conn_tpc_begin(connectionObject *self, XidObject *xid)
{ {
PGresult *pgres = NULL; PGresult *pgres = NULL;
@ -1221,7 +1236,7 @@ conn_tpc_begin(connectionObject *self, XidObject *xid)
* The function doesn't change the connection state as it can be used * The function doesn't change the connection state as it can be used
* for many commands and for recovered transactions. */ * for many commands and for recovered transactions. */
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;

View File

@ -122,8 +122,6 @@ psyco_conn_cursor(connectionObject *self, PyObject *args, PyObject *keywds)
static PyObject * static PyObject *
psyco_conn_close(connectionObject *self, PyObject *args) psyco_conn_close(connectionObject *self, PyObject *args)
{ {
EXC_IF_CONN_CLOSED(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);
@ -528,7 +526,7 @@ psyco_conn_set_session(connectionObject *self, PyObject *args, PyObject *kwargs)
if (-1 == c_autocommit) { return NULL; } if (-1 == c_autocommit) { return NULL; }
} }
if (0 != conn_set_session(self, if (0 > conn_set_session(self,
c_isolevel, c_readonly, c_deferrable, c_autocommit)) { c_isolevel, c_readonly, c_deferrable, c_autocommit)) {
return NULL; return NULL;
} }
@ -550,7 +548,7 @@ psyco_conn_autocommit_get(connectionObject *self)
return ret; return ret;
} }
static PyObject * BORROWED static PyObject *
_psyco_conn_autocommit_set_checks(connectionObject *self) _psyco_conn_autocommit_set_checks(connectionObject *self)
{ {
/* wrapper to use the EXC_IF macros. /* wrapper to use the EXC_IF macros.
@ -637,7 +635,7 @@ psyco_conn_set_client_encoding(connectionObject *self, PyObject *args)
if (!PyArg_ParseTuple(args, "s", &enc)) return NULL; if (!PyArg_ParseTuple(args, "s", &enc)) return NULL;
if (conn_set_client_encoding(self, enc) == 0) { if (conn_set_client_encoding(self, enc) >= 0) {
Py_INCREF(Py_None); Py_INCREF(Py_None);
rv = Py_None; rv = Py_None;
} }
@ -701,8 +699,8 @@ psyco_conn_get_parameter_status(connectionObject *self, PyObject *args)
static PyObject * static PyObject *
psyco_conn_lobject(connectionObject *self, PyObject *args, PyObject *keywds) psyco_conn_lobject(connectionObject *self, PyObject *args, PyObject *keywds)
{ {
Oid oid=InvalidOid, new_oid=InvalidOid; int oid = (int)InvalidOid, new_oid = (int)InvalidOid;
char *new_file = NULL; const char *new_file = NULL;
const char *smode = ""; const char *smode = "";
PyObject *factory = (PyObject *)&lobjectType; PyObject *factory = (PyObject *)&lobjectType;
PyObject *obj; PyObject *obj;
@ -754,7 +752,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) psyco_conn_get_backend_pid(connectionObject *self, PyObject *args)
{ {
EXC_IF_CONN_CLOSED(self); EXC_IF_CONN_CLOSED(self);
@ -767,7 +765,7 @@ psyco_conn_get_backend_pid(connectionObject *self)
"reset() -- Reset current connection to defaults." "reset() -- Reset current connection to defaults."
static PyObject * static PyObject *
psyco_conn_reset(connectionObject *self) psyco_conn_reset(connectionObject *self, PyObject *args)
{ {
int res; int res;
@ -795,7 +793,7 @@ psyco_conn_get_exception(PyObject *self, void *closure)
} }
static PyObject * static PyObject *
psyco_conn_poll(connectionObject *self) psyco_conn_poll(connectionObject *self, PyObject *args)
{ {
int res; int res;
@ -817,7 +815,7 @@ psyco_conn_poll(connectionObject *self)
"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) psyco_conn_fileno(connectionObject *self, PyObject *args)
{ {
long int socket; long int socket;
@ -836,7 +834,7 @@ psyco_conn_fileno(connectionObject *self)
"executing an asynchronous operation." "executing an asynchronous operation."
static PyObject * static PyObject *
psyco_conn_isexecuting(connectionObject *self) psyco_conn_isexecuting(connectionObject *self, PyObject *args)
{ {
/* synchronous connections will always return False */ /* synchronous connections will always return False */
if (self->async == 0) { if (self->async == 0) {
@ -868,7 +866,7 @@ psyco_conn_isexecuting(connectionObject *self)
"cancel() -- cancel the current operation" "cancel() -- cancel the current operation"
static PyObject * static PyObject *
psyco_conn_cancel(connectionObject *self) psyco_conn_cancel(connectionObject *self, PyObject *args)
{ {
char errbuf[256]; char errbuf[256];

View File

@ -85,7 +85,7 @@ struct cursorObject {
/* C-callable functions in cursor_int.c and cursor_ext.c */ /* C-callable functions in cursor_int.c and cursor_ext.c */
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);
/* exception-raising macros */ /* exception-raising macros */

View File

@ -38,7 +38,7 @@
* Return a borrowed reference. * Return a borrowed reference.
*/ */
PyObject * BORROWED PyObject *
curs_get_cast(cursorObject *self, PyObject *oid) curs_get_cast(cursorObject *self, PyObject *oid)
{ {
PyObject *cast; PyObject *cast;

View File

@ -52,9 +52,12 @@ extern PyObject *pyPsycopgTzFixedOffsetTimezone;
static PyObject * static PyObject *
psyco_curs_close(cursorObject *self, PyObject *args) psyco_curs_close(cursorObject *self, PyObject *args)
{ {
EXC_IF_CURS_CLOSED(self);
EXC_IF_ASYNC_IN_PROGRESS(self, close); EXC_IF_ASYNC_IN_PROGRESS(self, close);
if (self->closed) {
goto exit;
}
if (self->name != NULL) { if (self->name != NULL) {
char buffer[128]; char buffer[128];
@ -66,6 +69,7 @@ psyco_curs_close(cursorObject *self, PyObject *args)
self->closed = 1; self->closed = 1;
Dprintf("psyco_curs_close: cursor at %p closed", self); Dprintf("psyco_curs_close: cursor at %p closed", self);
exit:
Py_INCREF(Py_None); Py_INCREF(Py_None);
return Py_None; return Py_None;
} }
@ -75,7 +79,7 @@ psyco_curs_close(cursorObject *self, PyObject *args)
/* mogrify a query string and build argument array or dict */ /* mogrify a query string and build argument array or dict */
static int RAISES_NEG static int
_mogrify(PyObject *var, PyObject *fmt, cursorObject *curs, PyObject **new) _mogrify(PyObject *var, PyObject *fmt, cursorObject *curs, PyObject **new)
{ {
PyObject *key, *value, *n; PyObject *key, *value, *n;
@ -217,7 +221,10 @@ _mogrify(PyObject *var, PyObject *fmt, cursorObject *curs, PyObject **new)
} }
if (n == NULL) { if (n == NULL) {
n = PyTuple_New(PyObject_Length(var)); if (!(n = PyTuple_New(PyObject_Length(var)))) {
Py_DECREF(value);
return -1;
}
} }
/* let's have d point just after the '%' */ /* let's have d point just after the '%' */
@ -356,11 +363,12 @@ _psyco_curs_merge_query_args(cursorObject *self,
#define psyco_curs_execute_doc \ #define psyco_curs_execute_doc \
"execute(query, vars=None) -- Execute query with bound vars." "execute(query, vars=None) -- Execute query with bound vars."
static int RAISES_NEG static int
_psyco_curs_execute(cursorObject *self, _psyco_curs_execute(cursorObject *self,
PyObject *operation, PyObject *vars, long int async) PyObject *operation, PyObject *vars, long int async)
{ {
int res = 0; int res = -1;
int tmp;
PyObject *fquery, *cvt = NULL; PyObject *fquery, *cvt = NULL;
operation = _psyco_curs_validate_sql_basic(self, operation); operation = _psyco_curs_validate_sql_basic(self, operation);
@ -368,7 +376,7 @@ _psyco_curs_execute(cursorObject *self,
/* Any failure from here forward should 'goto fail' rather than 'return 0' /* Any failure from here forward should 'goto fail' rather than 'return 0'
directly. */ directly. */
if (operation == NULL) { goto fail; } if (operation == NULL) { goto exit; }
IFCLEARPGRES(self->pgres); IFCLEARPGRES(self->pgres);
@ -385,12 +393,12 @@ _psyco_curs_execute(cursorObject *self,
if (vars && vars != Py_None) if (vars && vars != Py_None)
{ {
if(_mogrify(vars, operation, self, &cvt) == -1) { goto fail; } if (0 > _mogrify(vars, operation, self, &cvt)) { 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 fail; goto exit;
} }
if (self->name != NULL) { if (self->name != NULL) {
@ -424,25 +432,20 @@ _psyco_curs_execute(cursorObject *self,
/* At this point, the SQL statement must be str, not unicode */ /* At this point, the SQL statement must be str, not unicode */
res = pq_execute(self, Bytes_AS_STRING(self->query), async); tmp = pq_execute(self, Bytes_AS_STRING(self->query), async);
Dprintf("psyco_curs_execute: res = %d, pgres = %p", res, self->pgres); Dprintf("psyco_curs_execute: res = %d, pgres = %p", tmp, self->pgres);
if (res == -1) { goto fail; } if (tmp < 0) { goto exit; }
res = 1; /* Success */ res = 0; /* Success */
goto cleanup;
fail: exit:
res = 0; /* Py_XDECREF(operation) is safe because the original reference passed
/* Fall through to cleanup */ by the caller was overwritten with either NULL or a new
cleanup: reference */
/* Py_XDECREF(operation) is safe because the original reference passed Py_XDECREF(operation);
by the caller was overwritten with either NULL or a new Py_XDECREF(cvt);
reference */
Py_XDECREF(operation);
Py_XDECREF(cvt); return res;
return res;
} }
static PyObject * static PyObject *
@ -476,13 +479,13 @@ psyco_curs_execute(cursorObject *self, PyObject *args, PyObject *kwargs)
EXC_IF_ASYNC_IN_PROGRESS(self, execute); EXC_IF_ASYNC_IN_PROGRESS(self, execute);
EXC_IF_TPC_PREPARED(self->conn, execute); EXC_IF_TPC_PREPARED(self->conn, execute);
if (_psyco_curs_execute(self, operation, vars, self->conn->async)) { if (0 > _psyco_curs_execute(self, operation, vars, self->conn->async)) {
Py_INCREF(Py_None);
return Py_None;
}
else {
return NULL; return NULL;
} }
/* success */
Py_INCREF(Py_None);
return Py_None;
} }
#define psyco_curs_executemany_doc \ #define psyco_curs_executemany_doc \
@ -521,7 +524,7 @@ psyco_curs_executemany(cursorObject *self, PyObject *args, PyObject *kwargs)
} }
while ((v = PyIter_Next(vars)) != NULL) { while ((v = PyIter_Next(vars)) != NULL) {
if (_psyco_curs_execute(self, operation, v, 0) == 0) { if (0 > _psyco_curs_execute(self, operation, v, 0)) {
Py_DECREF(v); Py_DECREF(v);
Py_XDECREF(iter); Py_XDECREF(iter);
return NULL; return NULL;
@ -568,7 +571,7 @@ _psyco_curs_mogrify(cursorObject *self,
if (vars && vars != Py_None) if (vars && vars != Py_None)
{ {
if (_mogrify(vars, operation, self, &cvt) == -1) { if (0 > _mogrify(vars, operation, self, &cvt)) {
goto cleanup; goto cleanup;
} }
} }
@ -644,7 +647,7 @@ psyco_curs_cast(cursorObject *self, PyObject *args)
"default) or using the sequence factory previously set in the\n" \ "default) or using the sequence factory previously set in the\n" \
"`row_factory` attribute. Return `!None` when no more data is available.\n" "`row_factory` attribute. Return `!None` when no more data is available.\n"
static int RAISES_NEG static int
_psyco_curs_prefetch(cursorObject *self) _psyco_curs_prefetch(cursorObject *self)
{ {
int i = 0; int i = 0;
@ -661,13 +664,14 @@ _psyco_curs_prefetch(cursorObject *self)
return i; return i;
} }
static PyObject * RAISES_NEG static int
_psyco_curs_buildrow_fill(cursorObject *self, PyObject *res, _psyco_curs_buildrow_fill(cursorObject *self, PyObject *res,
int row, int n, int istuple) int row, int n, int istuple)
{ {
int i, len, err; int i, len, err;
const char *str; const char *str;
PyObject *val; PyObject *val;
int rv = -1;
for (i=0; i < n; i++) { for (i=0; i < n; i++) {
if (PQgetisnull(self->pgres, row, i)) { if (PQgetisnull(self->pgres, row, i)) {
@ -682,59 +686,59 @@ _psyco_curs_buildrow_fill(cursorObject *self, PyObject *res,
Dprintf("_psyco_curs_buildrow: row %ld, element %d, len %d", Dprintf("_psyco_curs_buildrow: row %ld, element %d, len %d",
self->row, i, len); self->row, i, len);
val = typecast_cast(PyTuple_GET_ITEM(self->casts, i), str, len, if (!(val = typecast_cast(PyTuple_GET_ITEM(self->casts, i), str, len,
(PyObject*)self); (PyObject*)self))) {
goto exit;
}
if (val) { Dprintf("_psyco_curs_buildrow: val->refcnt = "
Dprintf("_psyco_curs_buildrow: val->refcnt = " FORMAT_CODE_PY_SSIZE_T,
FORMAT_CODE_PY_SSIZE_T, Py_REFCNT(val)
Py_REFCNT(val) );
); if (istuple) {
if (istuple) { PyTuple_SET_ITEM(res, i, val);
PyTuple_SET_ITEM(res, i, val);
}
else {
err = PySequence_SetItem(res, i, val);
Py_DECREF(val);
if (err == -1) {
Py_DECREF(res);
res = NULL;
break;
}
}
} }
else { else {
/* an error occurred in the type system, we return NULL to raise err = PySequence_SetItem(res, i, val);
an exception. the typecast code should already have set the Py_DECREF(val);
exception type and text */ if (err == -1) { goto exit; }
Py_DECREF(res);
res = NULL;
break;
} }
} }
return res;
rv = 0;
exit:
return rv;
} }
static PyObject * static PyObject *
_psyco_curs_buildrow(cursorObject *self, int row) _psyco_curs_buildrow(cursorObject *self, int row)
{ {
int n; int n;
int istuple;
PyObject *t = NULL;
PyObject *rv = NULL;
n = PQnfields(self->pgres); n = PQnfields(self->pgres);
return _psyco_curs_buildrow_fill(self, PyTuple_New(n), row, n, 1); istuple = (self->tuple_factory == Py_None);
}
static PyObject * if (istuple) {
_psyco_curs_buildrow_with_factory(cursorObject *self, int row) t = PyTuple_New(n);
{ }
int n; else {
PyObject *res; t = PyObject_CallFunctionObjArgs(self->tuple_factory, self, NULL);
}
if (!t) { goto exit; }
n = PQnfields(self->pgres); if (0 <= _psyco_curs_buildrow_fill(self, t, row, n, istuple)) {
if (!(res = PyObject_CallFunctionObjArgs(self->tuple_factory, self, NULL))) rv = t;
return NULL; t = NULL;
}
exit:
Py_XDECREF(t);
return rv;
return _psyco_curs_buildrow_fill(self, res, row, n, 0);
} }
static PyObject * static PyObject *
@ -766,11 +770,7 @@ psyco_curs_fetchone(cursorObject *self, PyObject *args)
return Py_None; return Py_None;
} }
if (self->tuple_factory == Py_None) res = _psyco_curs_buildrow(self, self->row);
res = _psyco_curs_buildrow(self, self->row);
else
res = _psyco_curs_buildrow_with_factory(self, self->row);
self->row++; /* move the counter to next line */ self->row++; /* move the counter to next line */
/* if the query was async aggresively free pgres, to allow /* if the query was async aggresively free pgres, to allow
@ -817,11 +817,7 @@ psyco_curs_next_named(cursorObject *self)
return NULL; return NULL;
} }
if (self->tuple_factory == Py_None) res = _psyco_curs_buildrow(self, self->row);
res = _psyco_curs_buildrow(self, self->row);
else
res = _psyco_curs_buildrow_with_factory(self, self->row);
self->row++; /* move the counter to next line */ self->row++; /* move the counter to next line */
/* if the query was async aggresively free pgres, to allow /* if the query was async aggresively free pgres, to allow
@ -848,7 +844,9 @@ static PyObject *
psyco_curs_fetchmany(cursorObject *self, PyObject *args, PyObject *kwords) psyco_curs_fetchmany(cursorObject *self, PyObject *args, PyObject *kwords)
{ {
int i; int i;
PyObject *list, *res; PyObject *list = NULL;
PyObject *row = NULL;
PyObject *rv = NULL;
PyObject *pysize = NULL; PyObject *pysize = NULL;
long int size = self->arraysize; long int size = self->arraysize;
@ -879,8 +877,8 @@ psyco_curs_fetchmany(cursorObject *self, PyObject *args, PyObject *kwords)
EXC_IF_TPC_PREPARED(self->conn, fetchone); EXC_IF_TPC_PREPARED(self->conn, fetchone);
PyOS_snprintf(buffer, 127, "FETCH FORWARD %d FROM \"%s\"", PyOS_snprintf(buffer, 127, "FETCH FORWARD %d FROM \"%s\"",
(int)size, self->name); (int)size, self->name);
if (pq_execute(self, buffer, 0) == -1) return NULL; if (pq_execute(self, buffer, 0) == -1) { goto exit; }
if (_psyco_curs_prefetch(self) < 0) return NULL; if (_psyco_curs_prefetch(self) < 0) { goto exit; }
} }
/* make sure size is not > than the available number of rows */ /* make sure size is not > than the available number of rows */
@ -891,26 +889,21 @@ psyco_curs_fetchmany(cursorObject *self, PyObject *args, PyObject *kwords)
Dprintf("psyco_curs_fetchmany: size = %ld", size); Dprintf("psyco_curs_fetchmany: size = %ld", size);
if (size <= 0) { if (size <= 0) {
return PyList_New(0); rv = PyList_New(0);
goto exit;
} }
list = PyList_New(size); if (!(list = PyList_New(size))) { goto exit; }
for (i = 0; i < size; i++) { for (i = 0; i < size; i++) {
if (self->tuple_factory == Py_None) row = _psyco_curs_buildrow(self, self->row);
res = _psyco_curs_buildrow(self, self->row);
else
res = _psyco_curs_buildrow_with_factory(self, self->row);
self->row++; self->row++;
if (res == NULL) { if (row == NULL) { goto exit; }
Py_DECREF(list);
return NULL;
}
PyList_SET_ITEM(list, i, res); PyList_SET_ITEM(list, i, row);
} }
row = NULL;
/* if the query was async aggresively free pgres, to allow /* if the query was async aggresively free pgres, to allow
successive requests to reallocate it */ successive requests to reallocate it */
@ -919,7 +912,15 @@ psyco_curs_fetchmany(cursorObject *self, PyObject *args, PyObject *kwords)
&& PyWeakref_GetObject(self->conn->async_cursor) == (PyObject*)self) && PyWeakref_GetObject(self->conn->async_cursor) == (PyObject*)self)
IFCLEARPGRES(self->pgres); IFCLEARPGRES(self->pgres);
return list; /* success */
rv = list;
list = NULL;
exit:
Py_XDECREF(list);
Py_XDECREF(row);
return rv;
} }
@ -936,7 +937,9 @@ static PyObject *
psyco_curs_fetchall(cursorObject *self, PyObject *args) psyco_curs_fetchall(cursorObject *self, PyObject *args)
{ {
int i, size; int i, size;
PyObject *list, *res; PyObject *list = NULL;
PyObject *row = NULL;
PyObject *rv = NULL;
EXC_IF_CURS_CLOSED(self); EXC_IF_CURS_CLOSED(self);
if (_psyco_curs_prefetch(self) < 0) return NULL; if (_psyco_curs_prefetch(self) < 0) return NULL;
@ -949,33 +952,27 @@ psyco_curs_fetchall(cursorObject *self, PyObject *args)
EXC_IF_ASYNC_IN_PROGRESS(self, fetchall); EXC_IF_ASYNC_IN_PROGRESS(self, fetchall);
EXC_IF_TPC_PREPARED(self->conn, fetchall); EXC_IF_TPC_PREPARED(self->conn, fetchall);
PyOS_snprintf(buffer, 127, "FETCH FORWARD ALL FROM \"%s\"", self->name); PyOS_snprintf(buffer, 127, "FETCH FORWARD ALL FROM \"%s\"", self->name);
if (pq_execute(self, buffer, 0) == -1) return NULL; if (pq_execute(self, buffer, 0) == -1) { goto exit; }
if (_psyco_curs_prefetch(self) < 0) return NULL; if (_psyco_curs_prefetch(self) < 0) { goto exit; }
} }
size = self->rowcount - self->row; size = self->rowcount - self->row;
if (size <= 0) { if (size <= 0) {
return PyList_New(0); rv = PyList_New(0);
goto exit;
} }
list = PyList_New(size); if (!(list = PyList_New(size))) { goto exit; }
for (i = 0; i < size; i++) { for (i = 0; i < size; i++) {
if (self->tuple_factory == Py_None) row = _psyco_curs_buildrow(self, self->row);
res = _psyco_curs_buildrow(self, self->row);
else
res = _psyco_curs_buildrow_with_factory(self, self->row);
self->row++; self->row++;
if (row == NULL) { goto exit; }
if (res == NULL) { PyList_SET_ITEM(list, i, row);
Py_DECREF(list);
return NULL;
}
PyList_SET_ITEM(list, i, res);
} }
row = NULL;
/* if the query was async aggresively free pgres, to allow /* if the query was async aggresively free pgres, to allow
successive requests to reallocate it */ successive requests to reallocate it */
@ -984,7 +981,15 @@ psyco_curs_fetchall(cursorObject *self, PyObject *args)
&& PyWeakref_GetObject(self->conn->async_cursor) == (PyObject*)self) && PyWeakref_GetObject(self->conn->async_cursor) == (PyObject*)self)
IFCLEARPGRES(self->pgres); IFCLEARPGRES(self->pgres);
return list; /* success */
rv = list;
list = NULL;
exit:
Py_XDECREF(list);
Py_XDECREF(row);
return rv;
} }
@ -994,7 +999,7 @@ psyco_curs_fetchall(cursorObject *self, PyObject *args)
"callproc(procname, parameters=None) -- Execute stored procedure." "callproc(procname, parameters=None) -- Execute stored procedure."
static PyObject * static PyObject *
psyco_curs_callproc(cursorObject *self, PyObject *args, PyObject *kwargs) psyco_curs_callproc(cursorObject *self, PyObject *args)
{ {
const char *procname = NULL; const char *procname = NULL;
char *sql = NULL; char *sql = NULL;
@ -1006,7 +1011,7 @@ psyco_curs_callproc(cursorObject *self, PyObject *args, PyObject *kwargs)
if (!PyArg_ParseTuple(args, "s#|O", if (!PyArg_ParseTuple(args, "s#|O",
&procname, &procname_len, &parameters &procname, &procname_len, &parameters
)) ))
{ return NULL; } { goto exit; }
EXC_IF_CURS_CLOSED(self); EXC_IF_CURS_CLOSED(self);
EXC_IF_ASYNC_IN_PROGRESS(self, callproc); EXC_IF_ASYNC_IN_PROGRESS(self, callproc);
@ -1015,10 +1020,10 @@ psyco_curs_callproc(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 .callproc() on named cursors", NULL, NULL); "can't call .callproc() on named cursors", NULL, NULL);
return NULL; goto exit;
} }
if(parameters != Py_None) { if (parameters != Py_None) {
nparameters = PyObject_Length(parameters); nparameters = PyObject_Length(parameters);
if (nparameters < 0) nparameters = 0; if (nparameters < 0) nparameters = 0;
} }
@ -1027,7 +1032,8 @@ psyco_curs_callproc(cursorObject *self, PyObject *args, PyObject *kwargs)
sl = procname_len + 17 + nparameters*3 - (nparameters ? 1 : 0); sl = procname_len + 17 + nparameters*3 - (nparameters ? 1 : 0);
sql = (char*)PyMem_Malloc(sl); sql = (char*)PyMem_Malloc(sl);
if (sql == NULL) { if (sql == NULL) {
return PyErr_NoMemory(); PyErr_NoMemory();
goto exit;
} }
sprintf(sql, "SELECT * FROM %s(", procname); sprintf(sql, "SELECT * FROM %s(", procname);
@ -1037,15 +1043,16 @@ psyco_curs_callproc(cursorObject *self, PyObject *args, PyObject *kwargs)
sql[sl-2] = ')'; sql[sl-2] = ')';
sql[sl-1] = '\0'; sql[sl-1] = '\0';
operation = Bytes_FromString(sql); if (!(operation = Bytes_FromString(sql))) { goto exit; }
PyMem_Free((void*)sql);
if (_psyco_curs_execute(self, operation, parameters, self->conn->async)) { if (0 <= _psyco_curs_execute(self, operation, parameters, self->conn->async)) {
Py_INCREF(parameters); Py_INCREF(parameters);
res = parameters; res = parameters;
} }
Py_DECREF(operation); exit:
Py_XDECREF(operation);
PyMem_Free((void*)sql);
return res; return res;
} }
@ -1202,6 +1209,7 @@ static char *_psyco_curs_copy_columns(PyObject *columns)
} }
if (NULL == (columnlist = PyMem_Malloc(bufsize))) { if (NULL == (columnlist = PyMem_Malloc(bufsize))) {
Py_DECREF(coliter);
PyErr_NoMemory(); PyErr_NoMemory();
goto error; goto error;
} }
@ -1258,8 +1266,8 @@ exit:
#define psyco_curs_copy_from_doc \ #define psyco_curs_copy_from_doc \
"copy_from(file, table, sep='\\t', null='\\\\N', size=8192, columns=None) -- Copy table from file." "copy_from(file, table, sep='\\t', null='\\\\N', size=8192, columns=None) -- Copy table from file."
static int STEALS(1) static int
_psyco_curs_has_read_check(PyObject* o, void* var) _psyco_curs_has_read_check(PyObject *o, PyObject **var)
{ {
if (PyObject_HasAttrString(o, "readline") if (PyObject_HasAttrString(o, "readline")
&& PyObject_HasAttrString(o, "read")) { && PyObject_HasAttrString(o, "read")) {
@ -1269,7 +1277,7 @@ _psyco_curs_has_read_check(PyObject* o, void* var)
* which could invoke the garbage collector. We thus need an * which could invoke the garbage collector. We thus need an
* INCREF/DECREF pair if we store this pointer in a GC object, such as * INCREF/DECREF pair if we store this pointer in a GC object, such as
* a cursorObject */ * a cursorObject */
*((PyObject**)var) = o; *var = o;
return 1; return 1;
} }
else { else {
@ -1344,7 +1352,7 @@ psyco_curs_copy_from(cursorObject *self, PyObject *args, PyObject *kwargs)
Py_INCREF(file); Py_INCREF(file);
self->copyfile = file; self->copyfile = file;
if (pq_execute(self, query, 0) == 1) { if (pq_execute(self, query, 0) >= 0) {
res = Py_None; res = Py_None;
Py_INCREF(Py_None); Py_INCREF(Py_None);
} }
@ -1365,11 +1373,11 @@ exit:
#define psyco_curs_copy_to_doc \ #define psyco_curs_copy_to_doc \
"copy_to(file, table, sep='\\t', null='\\\\N', columns=None) -- Copy table to file." "copy_to(file, table, sep='\\t', null='\\\\N', columns=None) -- Copy table to file."
static int STEALS(1) static int
_psyco_curs_has_write_check(PyObject* o, void* var) _psyco_curs_has_write_check(PyObject *o, PyObject **var)
{ {
if (PyObject_HasAttrString(o, "write")) { if (PyObject_HasAttrString(o, "write")) {
*((PyObject**)var) = o; *var = o;
return 1; return 1;
} }
else { else {
@ -1440,7 +1448,7 @@ psyco_curs_copy_to(cursorObject *self, PyObject *args, PyObject *kwargs)
Py_INCREF(file); Py_INCREF(file);
self->copyfile = file; self->copyfile = file;
if (pq_execute(self, query, 0) == 1) { if (pq_execute(self, query, 0) >= 0) {
res = Py_None; res = Py_None;
Py_INCREF(Py_None); Py_INCREF(Py_None);
} }
@ -1514,7 +1522,7 @@ psyco_curs_copy_expert(cursorObject *self, PyObject *args, PyObject *kwargs)
self->copyfile = file; self->copyfile = file;
/* At this point, the SQL statement must be str, not unicode */ /* At this point, the SQL statement must be str, not unicode */
if (pq_execute(self, Bytes_AS_STRING(sql), 0) == 1) { if (pq_execute(self, Bytes_AS_STRING(sql), 0) >= 0) {
res = Py_None; res = Py_None;
Py_INCREF(res); Py_INCREF(res);
} }

View File

@ -51,19 +51,19 @@ typedef struct {
/* functions exported from lobject_int.c */ /* functions exported from lobject_int.c */
HIDDEN int lobject_open(lobjectObject *self, connectionObject *conn, RAISES_NEG HIDDEN int lobject_open(lobjectObject *self, connectionObject *conn,
Oid oid, const char *smode, Oid new_oid, Oid oid, const char *smode, Oid new_oid,
const char *new_file); const char *new_file);
HIDDEN int lobject_unlink(lobjectObject *self); RAISES_NEG HIDDEN int lobject_unlink(lobjectObject *self);
HIDDEN int lobject_export(lobjectObject *self, const char *filename); RAISES_NEG HIDDEN int lobject_export(lobjectObject *self, const char *filename);
HIDDEN Py_ssize_t lobject_read(lobjectObject *self, char *buf, size_t len); RAISES_NEG HIDDEN Py_ssize_t lobject_read(lobjectObject *self, char *buf, size_t len);
HIDDEN Py_ssize_t lobject_write(lobjectObject *self, const char *buf, RAISES_NEG HIDDEN Py_ssize_t lobject_write(lobjectObject *self, const char *buf,
size_t len); size_t len);
HIDDEN int lobject_seek(lobjectObject *self, int pos, int whence); RAISES_NEG HIDDEN int lobject_seek(lobjectObject *self, int pos, int whence);
HIDDEN int lobject_tell(lobjectObject *self); RAISES_NEG HIDDEN int lobject_tell(lobjectObject *self);
HIDDEN int lobject_truncate(lobjectObject *self, size_t len); RAISES_NEG HIDDEN int lobject_truncate(lobjectObject *self, size_t len);
HIDDEN int lobject_close(lobjectObject *self); RAISES_NEG HIDDEN int lobject_close(lobjectObject *self);
#define lobject_is_closed(self) \ #define lobject_is_closed(self) \
((self)->fd < 0 || !(self)->conn || (self)->conn->closed) ((self)->fd < 0 || !(self)->conn || (self)->conn->closed)

View File

@ -50,7 +50,7 @@ collect_error(connectionObject *conn, char **error)
* *
* Valid mode are [r|w|rw|n][t|b] * Valid mode are [r|w|rw|n][t|b]
*/ */
static int RAISES_NEG static int
_lobject_parse_mode(const char *mode) _lobject_parse_mode(const char *mode)
{ {
int rv = 0; int rv = 0;
@ -147,7 +147,7 @@ _lobject_unparse_mode(int mode)
/* lobject_open - create a new/open an existing lo */ /* lobject_open - create a new/open an existing lo */
int RAISES_NEG int
lobject_open(lobjectObject *self, connectionObject *conn, lobject_open(lobjectObject *self, connectionObject *conn,
Oid oid, const char *smode, Oid new_oid, const char *new_file) Oid oid, const char *smode, Oid new_oid, const char *new_file)
{ {
@ -170,15 +170,18 @@ lobject_open(lobjectObject *self, connectionObject *conn,
/* if the oid is InvalidOid we create a new lob before opening it /* if the oid is InvalidOid we create a new lob before opening it
or we import a file from the FS, depending on the value of or we import a file from the FS, depending on the value of
new_name */ new_file */
if (oid == InvalidOid) { if (oid == InvalidOid) {
if (new_file) if (new_file)
self->oid = lo_import(self->conn->pgconn, new_file); self->oid = lo_import(self->conn->pgconn, new_file);
else else {
if (new_oid != InvalidOid) /* Use lo_creat when possible to be more middleware-friendly.
See ticket #88. */
if (new_oid != InvalidOid)
self->oid = lo_create(self->conn->pgconn, new_oid); self->oid = lo_create(self->conn->pgconn, new_oid);
else else
self->oid = lo_creat(self->conn->pgconn, INV_READ | INV_WRITE); self->oid = lo_creat(self->conn->pgconn, INV_READ | INV_WRITE);
}
Dprintf("lobject_open: large object created with oid = %d", Dprintf("lobject_open: large object created with oid = %d",
self->oid); self->oid);
@ -235,7 +238,7 @@ lobject_open(lobjectObject *self, connectionObject *conn,
/* lobject_close - close an existing lo */ /* lobject_close - close an existing lo */
static int RAISES_NEG static int
lobject_close_locked(lobjectObject *self, char **error) lobject_close_locked(lobjectObject *self, char **error)
{ {
int retvalue; int retvalue;
@ -268,7 +271,7 @@ lobject_close_locked(lobjectObject *self, char **error)
return retvalue; return retvalue;
} }
int RAISES_NEG int
lobject_close(lobjectObject *self) lobject_close(lobjectObject *self)
{ {
PGresult *pgres = NULL; PGresult *pgres = NULL;
@ -290,7 +293,7 @@ lobject_close(lobjectObject *self)
/* lobject_unlink - remove an lo from database */ /* lobject_unlink - remove an lo from database */
int RAISES_NEG int
lobject_unlink(lobjectObject *self) lobject_unlink(lobjectObject *self)
{ {
PGresult *pgres = NULL; PGresult *pgres = NULL;
@ -324,7 +327,7 @@ lobject_unlink(lobjectObject *self)
/* lobject_write - write bytes to a lo */ /* lobject_write - write bytes to a lo */
Py_ssize_t RAISES_NEG Py_ssize_t
lobject_write(lobjectObject *self, const char *buf, size_t len) lobject_write(lobjectObject *self, const char *buf, size_t len)
{ {
Py_ssize_t written; Py_ssize_t written;
@ -351,7 +354,7 @@ lobject_write(lobjectObject *self, const char *buf, size_t len)
/* lobject_read - read bytes from a lo */ /* lobject_read - read bytes from a lo */
Py_ssize_t RAISES_NEG Py_ssize_t
lobject_read(lobjectObject *self, char *buf, size_t len) lobject_read(lobjectObject *self, char *buf, size_t len)
{ {
Py_ssize_t n_read; Py_ssize_t n_read;
@ -375,7 +378,7 @@ lobject_read(lobjectObject *self, char *buf, size_t len)
/* lobject_seek - move the current position in the lo */ /* lobject_seek - move the current position in the lo */
int RAISES_NEG int
lobject_seek(lobjectObject *self, int pos, int whence) lobject_seek(lobjectObject *self, int pos, int whence)
{ {
PGresult *pgres = NULL; PGresult *pgres = NULL;
@ -403,7 +406,7 @@ lobject_seek(lobjectObject *self, int pos, int whence)
/* lobject_tell - tell the current position in the lo */ /* lobject_tell - tell the current position in the lo */
int RAISES_NEG int
lobject_tell(lobjectObject *self) lobject_tell(lobjectObject *self)
{ {
PGresult *pgres = NULL; PGresult *pgres = NULL;
@ -430,7 +433,7 @@ lobject_tell(lobjectObject *self)
/* lobject_export - export to a local file */ /* lobject_export - export to a local file */
int RAISES_NEG int
lobject_export(lobjectObject *self, const char *filename) lobject_export(lobjectObject *self, const char *filename)
{ {
PGresult *pgres = NULL; PGresult *pgres = NULL;
@ -459,7 +462,7 @@ lobject_export(lobjectObject *self, const char *filename)
#if PG_VERSION_HEX >= 0x080300 #if PG_VERSION_HEX >= 0x080300
int RAISES_NEG int
lobject_truncate(lobjectObject *self, size_t len) lobject_truncate(lobjectObject *self, size_t len)
{ {
int retvalue; int retvalue;

View File

@ -373,7 +373,7 @@ lobject_dealloc(PyObject* obj)
static int static int
lobject_init(PyObject *obj, PyObject *args, PyObject *kwds) lobject_init(PyObject *obj, PyObject *args, PyObject *kwds)
{ {
Oid oid=InvalidOid, new_oid=InvalidOid; int oid = (int)InvalidOid, new_oid = (int)InvalidOid;
const char *smode = ""; const char *smode = "";
const char *new_file = NULL; const char *new_file = NULL;
PyObject *conn; PyObject *conn;
@ -383,7 +383,7 @@ lobject_init(PyObject *obj, PyObject *args, PyObject *kwds)
return -1; return -1;
return lobject_setup((lobjectObject *)obj, return lobject_setup((lobjectObject *)obj,
(connectionObject *)conn, oid, smode, new_oid, new_file); (connectionObject *)conn, (Oid)oid, smode, (Oid)new_oid, new_file);
} }
static PyObject * static PyObject *

View File

@ -52,29 +52,35 @@ microprotocols_init(PyObject *dict)
} }
/* microprotocols_add - add a reverse type-caster to the dictionary */ /* microprotocols_add - add a reverse type-caster to the dictionary
*
* Return 0 on success, else -1 and set an exception.
*/
int int
microprotocols_add(PyTypeObject *type, PyObject *proto, PyObject *cast) microprotocols_add(PyTypeObject *type, PyObject *proto, PyObject *cast)
{ {
PyObject *key; PyObject *key = NULL;
int rv = -1;
if (proto == NULL) proto = (PyObject*)&isqlquoteType; if (proto == NULL) proto = (PyObject*)&isqlquoteType;
Dprintf("microprotocols_add: cast %p for (%s, ?)", cast, type->tp_name); Dprintf("microprotocols_add: cast %p for (%s, ?)", cast, type->tp_name);
key = PyTuple_Pack(2, (PyObject*)type, proto); if (!(key = PyTuple_Pack(2, (PyObject*)type, proto))) { goto exit; }
PyDict_SetItem(psyco_adapters, key, cast); if (0 != PyDict_SetItem(psyco_adapters, key, cast)) { goto exit; }
Py_DECREF(key);
return 0; rv = 0;
exit:
Py_XDECREF(key);
return rv;
} }
/* Check if one of `obj` superclasses has an adapter for `proto`. /* Check if one of `obj` superclasses has an adapter for `proto`.
* *
* If it does, return a *borrowed reference* to the adapter, else NULL. * If it does, return a *borrowed reference* to the adapter, else to None.
*/ */
static PyObject * BORROWED static PyObject *
_get_superclass_adapter(PyObject *obj, PyObject *proto) _get_superclass_adapter(PyObject *obj, PyObject *proto)
{ {
PyTypeObject *type; PyTypeObject *type;
@ -89,14 +95,14 @@ _get_superclass_adapter(PyObject *obj, PyObject *proto)
#endif #endif
type->tp_mro)) { type->tp_mro)) {
/* has no mro */ /* has no mro */
return NULL; return Py_None;
} }
/* Walk the mro from the most specific subclass. */ /* Walk the mro from the most specific subclass. */
mro = type->tp_mro; mro = type->tp_mro;
for (i = 1, ii = PyTuple_GET_SIZE(mro); i < ii; ++i) { for (i = 1, ii = PyTuple_GET_SIZE(mro); i < ii; ++i) {
st = PyTuple_GET_ITEM(mro, i); st = PyTuple_GET_ITEM(mro, i);
key = PyTuple_Pack(2, st, proto); if (!(key = PyTuple_Pack(2, st, proto))) { return NULL; }
adapter = PyDict_GetItem(psyco_adapters, key); adapter = PyDict_GetItem(psyco_adapters, key);
Py_DECREF(key); Py_DECREF(key);
@ -119,7 +125,7 @@ _get_superclass_adapter(PyObject *obj, PyObject *proto)
return adapter; return adapter;
} }
} }
return NULL; return Py_None;
} }
@ -139,7 +145,7 @@ microprotocols_adapt(PyObject *obj, PyObject *proto, PyObject *alt)
Py_TYPE(obj)->tp_name); Py_TYPE(obj)->tp_name);
/* look for an adapter in the registry */ /* look for an adapter in the registry */
key = PyTuple_Pack(2, Py_TYPE(obj), proto); if (!(key = PyTuple_Pack(2, Py_TYPE(obj), proto))) { return NULL; }
adapter = PyDict_GetItem(psyco_adapters, key); adapter = PyDict_GetItem(psyco_adapters, key);
Py_DECREF(key); Py_DECREF(key);
if (adapter) { if (adapter) {
@ -148,7 +154,10 @@ microprotocols_adapt(PyObject *obj, PyObject *proto, PyObject *alt)
} }
/* Check if a superclass can be adapted and use the same adapter. */ /* Check if a superclass can be adapted and use the same adapter. */
if (NULL != (adapter = _get_superclass_adapter(obj, proto))) { if (!(adapter = _get_superclass_adapter(obj, proto))) {
return NULL;
}
if (Py_None != adapter) {
adapted = PyObject_CallFunctionObjArgs(adapter, obj, NULL); adapted = PyObject_CallFunctionObjArgs(adapter, obj, NULL);
return adapted; return adapted;
} }

View File

@ -64,7 +64,7 @@ strip_severity(const char *msg)
code. A list of error codes can be found at: code. A list of error codes can be found at:
http://www.postgresql.org/docs/current/static/errcodes-appendix.html */ http://www.postgresql.org/docs/current/static/errcodes-appendix.html */
static PyObject * BORROWED static PyObject *
exception_from_sqlstate(const char *sqlstate) exception_from_sqlstate(const char *sqlstate)
{ {
switch (sqlstate[0]) { switch (sqlstate[0]) {
@ -151,7 +151,7 @@ exception_from_sqlstate(const char *sqlstate)
This function should be called while holding the GIL. */ This function should be called while holding the GIL. */
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;
@ -164,7 +164,7 @@ pq_raise(connectionObject *conn, cursorObject *curs, PGresult *pgres)
"psycopg went psycotic and raised a null error"); "psycopg went psycotic and raised a null error");
return; return;
} }
/* if the connection has somehow beed broken, we mark the connection /* if the connection has somehow beed broken, we mark the connection
object as closed but requiring cleanup */ object as closed but requiring cleanup */
if (conn->pgconn != NULL && PQstatus(conn->pgconn) == CONNECTION_BAD) if (conn->pgconn != NULL && PQstatus(conn->pgconn) == CONNECTION_BAD)
@ -249,7 +249,9 @@ pq_clear_critical(connectionObject *conn)
} }
} }
static PyObject * /* return -1 if the exception is set (i.e. if conn->critical is set),
* else 0 */
RAISES_NEG static int
pq_resolve_critical(connectionObject *conn, int close) pq_resolve_critical(connectionObject *conn, int close)
{ {
Dprintf("pq_resolve_critical: resolving %s", conn->critical); Dprintf("pq_resolve_critical: resolving %s", conn->critical);
@ -264,11 +266,13 @@ pq_resolve_critical(connectionObject *conn, int close)
/* we don't want to destroy this connection but just close it */ /* we don't want to destroy this connection but just close it */
if (close == 1) conn_close(conn); if (close == 1) conn_close(conn);
/* remember to clear the critical! */ /* remember to clear the critical! */
pq_clear_critical(conn); pq_clear_critical(conn);
return -1;
} }
return NULL; return 0;
} }
/* pq_clear_async - clear the effects of a previous async query /* pq_clear_async - clear the effects of a previous async query
@ -300,19 +304,16 @@ pq_clear_async(connectionObject *conn)
Accepted arg values are 1 (nonblocking) and 0 (blocking). Accepted arg values are 1 (nonblocking) and 0 (blocking).
Return 0 if everything ok, else nonzero. Return 0 if everything ok, else < 0 and set an exception.
In case of error, if pyerr is nonzero, set a Python exception.
*/ */
int RAISES_NEG int
pq_set_non_blocking(connectionObject *conn, int arg, int pyerr) pq_set_non_blocking(connectionObject *conn, int arg)
{ {
int ret = PQsetnonblocking(conn->pgconn, arg); int ret = PQsetnonblocking(conn->pgconn, arg);
if (0 != ret) { if (0 != ret) {
Dprintf("PQsetnonblocking(%d) FAILED", arg); Dprintf("PQsetnonblocking(%d) FAILED", arg);
if (pyerr) { PyErr_SetString(OperationalError, "PQsetnonblocking() failed");
PyErr_SetString(OperationalError, "PQsetnonblocking() failed"); ret = -1;
}
} }
return ret; return ret;
} }
@ -382,7 +383,7 @@ cleanup:
This function should be called while holding the global interpreter This function should be called while holding the global interpreter
lock. lock.
*/ */
void RAISES void
pq_complete_error(connectionObject *conn, PGresult **pgres, char **error) 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",
@ -444,38 +445,39 @@ pq_commit(connectionObject *conn)
PGresult *pgres = NULL; PGresult *pgres = NULL;
char *error = NULL; char *error = NULL;
Py_BEGIN_ALLOW_THREADS;
pthread_mutex_lock(&conn->lock);
Dprintf("pq_commit: pgconn = %p, autocommit = %d, status = %d", Dprintf("pq_commit: pgconn = %p, autocommit = %d, status = %d",
conn->pgconn, conn->autocommit, conn->status); conn->pgconn, conn->autocommit, conn->status);
if (conn->autocommit || conn->status != CONN_STATUS_BEGIN) { if (conn->autocommit || conn->status != CONN_STATUS_BEGIN) {
Dprintf("pq_commit: no transaction to commit"); Dprintf("pq_commit: no transaction to commit");
return 0; retvalue = 0;
}
else {
conn->mark += 1;
retvalue = pq_execute_command_locked(conn, "COMMIT", &pgres, &error, &_save);
} }
Py_BEGIN_ALLOW_THREADS;
pthread_mutex_lock(&conn->lock);
conn->mark += 1;
retvalue = pq_execute_command_locked(conn, "COMMIT", &pgres, &error, &_save);
Py_BLOCK_THREADS; Py_BLOCK_THREADS;
conn_notice_process(conn); conn_notice_process(conn);
Py_UNBLOCK_THREADS; Py_UNBLOCK_THREADS;
/* Even if an error occurred, the connection will be rolled back,
so we unconditionally set the connection status here. */
conn->status = CONN_STATUS_READY;
pthread_mutex_unlock(&conn->lock); pthread_mutex_unlock(&conn->lock);
Py_END_ALLOW_THREADS; Py_END_ALLOW_THREADS;
if (retvalue < 0) if (retvalue < 0)
pq_complete_error(conn, &pgres, &error); pq_complete_error(conn, &pgres, &error);
/* Even if an error occurred, the connection will be rolled back,
so we unconditionally set the connection status here. */
conn->status = CONN_STATUS_READY;
return retvalue; return retvalue;
} }
int RAISES_NEG int
pq_abort_locked(connectionObject *conn, PGresult **pgres, char **error, pq_abort_locked(connectionObject *conn, PGresult **pgres, char **error,
PyThreadState **tstate) PyThreadState **tstate)
{ {
@ -502,7 +504,7 @@ pq_abort_locked(connectionObject *conn, PGresult **pgres, char **error,
This function should be called while holding the global interpreter This function should be called while holding the global interpreter
lock. */ lock. */
int RAISES_NEG int
pq_abort(connectionObject *conn) pq_abort(connectionObject *conn)
{ {
int retvalue = -1; int retvalue = -1;
@ -512,11 +514,6 @@ pq_abort(connectionObject *conn)
Dprintf("pq_abort: pgconn = %p, autocommit = %d, status = %d", Dprintf("pq_abort: pgconn = %p, autocommit = %d, status = %d",
conn->pgconn, conn->autocommit, conn->status); conn->pgconn, conn->autocommit, conn->status);
if (conn->autocommit || conn->status != CONN_STATUS_BEGIN) {
Dprintf("pq_abort: no transaction to abort");
return 0;
}
Py_BEGIN_ALLOW_THREADS; Py_BEGIN_ALLOW_THREADS;
pthread_mutex_lock(&conn->lock); pthread_mutex_lock(&conn->lock);
@ -544,7 +541,7 @@ pq_abort(connectionObject *conn)
connection without holding the global interpreter lock. connection without holding the global interpreter lock.
*/ */
int RAISES_NEG int
pq_reset_locked(connectionObject *conn, PGresult **pgres, char **error, pq_reset_locked(connectionObject *conn, PGresult **pgres, char **error,
PyThreadState **tstate) PyThreadState **tstate)
{ {
@ -836,7 +833,7 @@ pq_flush(connectionObject *conn)
this fucntion locks the connection object this fucntion locks the connection object
this function call Py_*_ALLOW_THREADS macros */ this function call Py_*_ALLOW_THREADS macros */
int RAISES_NEG int
pq_execute(cursorObject *curs, const char *query, int async) pq_execute(cursorObject *curs, const char *query, int async)
{ {
PGresult *pgres = NULL; PGresult *pgres = NULL;
@ -846,8 +843,7 @@ pq_execute(cursorObject *curs, const char *query, int async)
/* if the status of the connection is critical raise an exception and /* if the status of the connection is critical raise an exception and
definitely close the connection */ definitely close the connection */
if (curs->conn->critical) { if (curs->conn->critical) {
pq_resolve_critical(curs->conn, 1); return pq_resolve_critical(curs->conn, 1);
return -1;
} }
/* check status of connection, raise error if not OK */ /* check status of connection, raise error if not OK */
@ -942,12 +938,13 @@ pq_execute(cursorObject *curs, const char *query, int async)
to respect the old DBAPI-2.0 compatible behaviour */ to respect the old DBAPI-2.0 compatible behaviour */
if (async == 0) { if (async == 0) {
Dprintf("pq_execute: entering syncronous DBAPI compatibility mode"); Dprintf("pq_execute: entering syncronous DBAPI compatibility mode");
if (pq_fetch(curs) == -1) return -1; if (pq_fetch(curs) < 0) return -1;
} }
else { else {
PyObject *tmp;
curs->conn->async_status = async_status; curs->conn->async_status = async_status;
curs->conn->async_cursor = PyWeakref_NewRef((PyObject *)curs, NULL); curs->conn->async_cursor = tmp = PyWeakref_NewRef((PyObject *)curs, NULL);
if (!curs->conn->async_cursor) { if (!tmp) {
/* weakref creation failed */ /* weakref creation failed */
return -1; return -1;
} }
@ -1016,7 +1013,7 @@ pq_get_last_result(connectionObject *conn)
1 - result from backend (possibly data is ready) 1 - result from backend (possibly data is ready)
*/ */
static int RAISES_NEG static int
_pq_fetch_tuples(cursorObject *curs) _pq_fetch_tuples(cursorObject *curs)
{ {
int i, *dsize = NULL; int i, *dsize = NULL;
@ -1416,8 +1413,7 @@ pq_fetch(cursorObject *curs)
Dprintf("pq_fetch: got a NULL pgres, checking for critical"); Dprintf("pq_fetch: got a NULL pgres, checking for critical");
pq_set_critical(curs->conn); pq_set_critical(curs->conn);
if (curs->conn->critical) { if (curs->conn->critical) {
pq_resolve_critical(curs->conn); return pq_resolve_critical(curs->conn);
return -1;
} }
else { else {
return 0; return 0;
@ -1493,13 +1489,7 @@ pq_fetch(cursorObject *curs)
raise the exception but we avoid to close the connection) */ raise the exception but we avoid to close the connection) */
Dprintf("pq_fetch: fetching done; check for critical errors"); Dprintf("pq_fetch: fetching done; check for critical errors");
if (curs->conn->critical) { if (curs->conn->critical) {
if (ex == -1) { return pq_resolve_critical(curs->conn, ex == -1 ? 1 : 0);
pq_resolve_critical(curs->conn, 1);
}
else {
pq_resolve_critical(curs->conn, 0);
}
return -1;
} }
return ex; return ex;

View File

@ -35,18 +35,18 @@
/* exported functions */ /* exported functions */
HIDDEN PGresult *pq_get_last_result(connectionObject *conn); HIDDEN PGresult *pq_get_last_result(connectionObject *conn);
HIDDEN int pq_fetch(cursorObject *curs); RAISES_NEG HIDDEN int pq_fetch(cursorObject *curs);
HIDDEN int pq_execute(cursorObject *curs, const char *query, int async); RAISES_NEG HIDDEN int pq_execute(cursorObject *curs, const char *query, int async);
HIDDEN int pq_send_query(connectionObject *conn, const char *query); HIDDEN int pq_send_query(connectionObject *conn, const char *query);
HIDDEN int pq_begin_locked(connectionObject *conn, PGresult **pgres, HIDDEN int pq_begin_locked(connectionObject *conn, PGresult **pgres,
char **error, PyThreadState **tstate); char **error, PyThreadState **tstate);
HIDDEN int pq_commit(connectionObject *conn); HIDDEN int pq_commit(connectionObject *conn);
HIDDEN int pq_abort_locked(connectionObject *conn, PGresult **pgres, RAISES_NEG HIDDEN int pq_abort_locked(connectionObject *conn, PGresult **pgres,
char **error, PyThreadState **tstate); char **error, PyThreadState **tstate);
HIDDEN int pq_abort(connectionObject *conn); RAISES_NEG HIDDEN int pq_abort(connectionObject *conn);
HIDDEN int pq_reset_locked(connectionObject *conn, PGresult **pgres, HIDDEN int pq_reset_locked(connectionObject *conn, PGresult **pgres,
char **error, PyThreadState **tstate); char **error, PyThreadState **tstate);
HIDDEN int pq_reset(connectionObject *conn); RAISES_NEG HIDDEN int pq_reset(connectionObject *conn);
HIDDEN char *pq_get_guc_locked(connectionObject *conn, const char *param, HIDDEN char *pq_get_guc_locked(connectionObject *conn, const char *param,
PGresult **pgres, PGresult **pgres,
char **error, PyThreadState **tstate); char **error, PyThreadState **tstate);
@ -61,7 +61,7 @@ HIDDEN int pq_is_busy(connectionObject *conn);
HIDDEN int pq_is_busy_locked(connectionObject *conn); HIDDEN int pq_is_busy_locked(connectionObject *conn);
HIDDEN int pq_flush(connectionObject *conn); HIDDEN int pq_flush(connectionObject *conn);
HIDDEN void pq_clear_async(connectionObject *conn); HIDDEN void pq_clear_async(connectionObject *conn);
HIDDEN int pq_set_non_blocking(connectionObject *conn, int arg, int pyerr); RAISES_NEG HIDDEN int pq_set_non_blocking(connectionObject *conn, int arg);
HIDDEN void pq_set_critical(connectionObject *conn, const char *msg); HIDDEN void pq_set_critical(connectionObject *conn, const char *msg);
@ -69,7 +69,7 @@ HIDDEN int pq_execute_command_locked(connectionObject *conn,
const char *query, const char *query,
PGresult **pgres, char **error, PGresult **pgres, char **error,
PyThreadState **tstate); PyThreadState **tstate);
HIDDEN void pq_complete_error(connectionObject *conn, PGresult **pgres, RAISES HIDDEN void pq_complete_error(connectionObject *conn, PGresult **pgres,
char **error); char **error);
#endif /* !defined(PSYCOPG_PQPATH_H) */ #endif /* !defined(PSYCOPG_PQPATH_H) */

View File

@ -120,17 +120,19 @@ HIDDEN PyObject *psyco_GetDecimalType(void);
typedef struct cursorObject cursorObject; typedef struct cursorObject cursorObject;
/* some utility functions */ /* some utility functions */
HIDDEN void psyco_set_error(PyObject *exc, cursorObject *curs, const char *msg, RAISES HIDDEN void psyco_set_error(PyObject *exc, cursorObject *curs, const char *msg,
const char *pgerror, const char *pgcode); const char *pgerror, const char *pgcode);
HIDDEN char *psycopg_escape_string(PyObject *conn, HIDDEN char *psycopg_escape_string(PyObject *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 char *psycopg_strdup(const char *from, Py_ssize_t len); HIDDEN int psycopg_strdup(char **to, const char *from, Py_ssize_t len);
HIDDEN PyObject * psycopg_ensure_bytes(PyObject *obj);
HIDDEN PyObject * psycopg_ensure_text(PyObject *obj);
HIDDEN int psycopg_is_text_file(PyObject *f); HIDDEN int psycopg_is_text_file(PyObject *f);
STEALS(1) HIDDEN PyObject * psycopg_ensure_bytes(PyObject *obj);
STEALS(1) HIDDEN PyObject * psycopg_ensure_text(PyObject *obj);
/* Exceptions docstrings */ /* Exceptions docstrings */
#define Error_doc \ #define Error_doc \
"Base class for error exceptions." "Base class for error exceptions."

View File

@ -143,14 +143,6 @@ psyco_connect(PyObject *self, PyObject *args, PyObject *keywds)
" * `name`: Name for the new type\n" \ " * `name`: Name for the new type\n" \
" * `baseobj`: Adapter to perform type conversion of a single array item." " * `baseobj`: Adapter to perform type conversion of a single array item."
static void
_psyco_register_type_set(PyObject **dict, PyObject *type)
{
if (*dict == NULL)
*dict = PyDict_New();
typecast_add(type, *dict, 0);
}
static PyObject * static PyObject *
psyco_register_type(PyObject *self, PyObject *args) psyco_register_type(PyObject *self, PyObject *args)
{ {
@ -162,10 +154,16 @@ psyco_register_type(PyObject *self, PyObject *args)
if (obj != NULL && obj != Py_None) { if (obj != NULL && obj != Py_None) {
if (PyObject_TypeCheck(obj, &cursorType)) { if (PyObject_TypeCheck(obj, &cursorType)) {
_psyco_register_type_set(&(((cursorObject*)obj)->string_types), type); PyObject **dict = &(((cursorObject*)obj)->string_types);
if (*dict == NULL) {
if (!(*dict = PyDict_New())) { return NULL; }
}
if (0 > typecast_add(type, *dict, 0)) { return NULL; }
} }
else if (PyObject_TypeCheck(obj, &connectionType)) { else if (PyObject_TypeCheck(obj, &connectionType)) {
typecast_add(type, ((connectionObject*)obj)->string_types, 0); if (0 > typecast_add(type, ((connectionObject*)obj)->string_types, 0)) {
return NULL;
}
} }
else { else {
PyErr_SetString(PyExc_TypeError, PyErr_SetString(PyExc_TypeError,
@ -174,7 +172,7 @@ psyco_register_type(PyObject *self, PyObject *args)
} }
} }
else { else {
typecast_add(type, NULL, 0); if (0 > typecast_add(type, NULL, 0)) { return NULL; }
} }
Py_INCREF(Py_None); Py_INCREF(Py_None);
@ -182,66 +180,108 @@ psyco_register_type(PyObject *self, PyObject *args)
} }
/* default adapters */ /* Initialize the default adapters map
*
static void * Return 0 on success, else -1 and set an exception.
*/
static int
psyco_adapters_init(PyObject *mod) psyco_adapters_init(PyObject *mod)
{ {
PyObject *call; PyObject *call = NULL;
int rv = -1;
microprotocols_add(&PyFloat_Type, NULL, (PyObject*)&pfloatType); if (0 != microprotocols_add(&PyFloat_Type, NULL, (PyObject*)&pfloatType)) {
goto exit;
}
#if PY_MAJOR_VERSION < 3 #if PY_MAJOR_VERSION < 3
microprotocols_add(&PyInt_Type, NULL, (PyObject*)&pintType); if (0 != microprotocols_add(&PyInt_Type, NULL, (PyObject*)&pintType)) {
goto exit;
}
#endif #endif
microprotocols_add(&PyLong_Type, NULL, (PyObject*)&pintType); if (0 != microprotocols_add(&PyLong_Type, NULL, (PyObject*)&pintType)) {
microprotocols_add(&PyBool_Type, NULL, (PyObject*)&pbooleanType); goto exit;
}
if (0 != microprotocols_add(&PyBool_Type, NULL, (PyObject*)&pbooleanType)) {
goto exit;
}
/* strings */ /* strings */
#if PY_MAJOR_VERSION < 3 #if PY_MAJOR_VERSION < 3
microprotocols_add(&PyString_Type, NULL, (PyObject*)&qstringType); if (0 != microprotocols_add(&PyString_Type, NULL, (PyObject*)&qstringType)) {
goto exit;
}
#endif #endif
microprotocols_add(&PyUnicode_Type, NULL, (PyObject*)&qstringType); if (0 != microprotocols_add(&PyUnicode_Type, NULL, (PyObject*)&qstringType)) {
goto exit;
}
/* binary */ /* binary */
#if PY_MAJOR_VERSION < 3 #if PY_MAJOR_VERSION < 3
microprotocols_add(&PyBuffer_Type, NULL, (PyObject*)&binaryType); if (0 != microprotocols_add(&PyBuffer_Type, NULL, (PyObject*)&binaryType)) {
goto exit;
}
#else #else
microprotocols_add(&PyBytes_Type, NULL, (PyObject*)&binaryType); if (0 != microprotocols_add(&PyBytes_Type, NULL, (PyObject*)&binaryType)) {
goto exit;
}
#endif #endif
#if PY_MAJOR_VERSION >= 3 || PY_MINOR_VERSION >= 6 #if PY_MAJOR_VERSION >= 3 || PY_MINOR_VERSION >= 6
microprotocols_add(&PyByteArray_Type, NULL, (PyObject*)&binaryType); if (0 != microprotocols_add(&PyByteArray_Type, NULL, (PyObject*)&binaryType)) {
goto exit;
}
#endif #endif
#if PY_MAJOR_VERSION >= 3 || PY_MINOR_VERSION >= 7 #if PY_MAJOR_VERSION >= 3 || PY_MINOR_VERSION >= 7
microprotocols_add(&PyMemoryView_Type, NULL, (PyObject*)&binaryType); if (0 != microprotocols_add(&PyMemoryView_Type, NULL, (PyObject*)&binaryType)) {
goto exit;
}
#endif #endif
microprotocols_add(&PyList_Type, NULL, (PyObject*)&listType); if (0 != microprotocols_add(&PyList_Type, NULL, (PyObject*)&listType)) {
goto exit;
}
/* the module has already been initialized, so we can obtain the callable /* the module has already been initialized, so we can obtain the callable
objects directly from its dictionary :) */ objects directly from its dictionary :) */
call = PyMapping_GetItemString(mod, "DateFromPy"); if (!(call = PyMapping_GetItemString(mod, "DateFromPy"))) { goto exit; }
microprotocols_add(PyDateTimeAPI->DateType, NULL, call); if (0 != microprotocols_add(PyDateTimeAPI->DateType, NULL, call)) { goto exit; }
call = PyMapping_GetItemString(mod, "TimeFromPy"); Py_CLEAR(call);
microprotocols_add(PyDateTimeAPI->TimeType, NULL, call);
call = PyMapping_GetItemString(mod, "TimestampFromPy"); if (!(call = PyMapping_GetItemString(mod, "TimeFromPy"))) { goto exit; }
microprotocols_add(PyDateTimeAPI->DateTimeType, NULL, call); if (0 != microprotocols_add(PyDateTimeAPI->TimeType, NULL, call)) { goto exit; }
call = PyMapping_GetItemString(mod, "IntervalFromPy"); Py_CLEAR(call);
microprotocols_add(PyDateTimeAPI->DeltaType, NULL, call);
if (!(call = PyMapping_GetItemString(mod, "TimestampFromPy"))) { goto exit; }
if (0 != microprotocols_add(PyDateTimeAPI->DateTimeType, NULL, call)) { goto exit; }
Py_CLEAR(call);
if (!(call = PyMapping_GetItemString(mod, "IntervalFromPy"))) { goto exit; }
if (0 != microprotocols_add(PyDateTimeAPI->DeltaType, NULL, call)) { goto exit; }
Py_CLEAR(call);
#ifdef HAVE_MXDATETIME #ifdef HAVE_MXDATETIME
/* as above, we use the callable objects from the psycopg module */ /* as above, we use the callable objects from the psycopg module */
if (NULL != (call = PyMapping_GetItemString(mod, "TimestampFromMx"))) { if (NULL != (call = PyMapping_GetItemString(mod, "TimestampFromMx"))) {
microprotocols_add(mxDateTime.DateTime_Type, NULL, call); if (0 != microprotocols_add(mxDateTime.DateTime_Type, NULL, call)) { goto exit; }
Py_CLEAR(call);
/* if we found the above, we have this too. */ /* if we found the above, we have this too. */
call = PyMapping_GetItemString(mod, "TimeFromMx"); if (!(call = PyMapping_GetItemString(mod, "TimeFromMx"))) { goto exit; }
microprotocols_add(mxDateTime.DateTimeDelta_Type, NULL, call); if (0 != microprotocols_add(mxDateTime.DateTimeDelta_Type, NULL, call)) { goto exit; }
Py_CLEAR(call);
} }
else { else {
PyErr_Clear(); PyErr_Clear();
} }
#endif #endif
/* Success! */
rv = 0;
exit:
Py_XDECREF(call);
return rv;
} }
/* psyco_encodings_fill /* psyco_encodings_fill
@ -326,15 +366,27 @@ static encodingPair encodings[] = {
{NULL, NULL} {NULL, NULL}
}; };
static void psyco_encodings_fill(PyObject *dict) /* Initialize the encodings table.
*
* Return 0 on success, else -1 and set an exception.
*/
static int psyco_encodings_fill(PyObject *dict)
{ {
PyObject *value = NULL;
encodingPair *enc; encodingPair *enc;
int rv = -1;
for (enc = encodings; enc->pgenc != NULL; enc++) { for (enc = encodings; enc->pgenc != NULL; enc++) {
PyObject *value = Text_FromUTF8(enc->pyenc); if (!(value = Text_FromUTF8(enc->pyenc))) { goto exit; }
PyDict_SetItemString(dict, enc->pgenc, value); if (0 != PyDict_SetItemString(dict, enc->pgenc, value)) { goto exit; }
Py_DECREF(value); Py_CLEAR(value);
} }
rv = 0;
exit:
Py_XDECREF(value);
return rv;
} }
/* psyco_errors_init, psyco_errors_fill (callable from C) /* psyco_errors_init, psyco_errors_fill (callable from C)
@ -380,7 +432,59 @@ static struct {
{NULL} /* Sentinel */ {NULL} /* Sentinel */
}; };
static void
/* Error.__reduce_ex__
*
* The method is required to make exceptions picklable: set the cursor
* attribute to None. Only working from Py 2.5: previous versions
* would require implementing __getstate__, and as of 2012 it's a little
* bit too late to care. */
static PyObject *
psyco_error_reduce_ex(PyObject *self, PyObject *args)
{
PyObject *proto = NULL;
PyObject *super = NULL;
PyObject *tuple = NULL;
PyObject *dict = NULL;
PyObject *rv = NULL;
/* tuple = Exception.__reduce_ex__(self, proto) */
if (!PyArg_ParseTuple(args, "O", &proto)) {
goto error;
}
if (!(super = PyObject_GetAttrString(PyExc_Exception, "__reduce_ex__"))) {
goto error;
}
if (!(tuple = PyObject_CallFunctionObjArgs(super, self, proto, NULL))) {
goto error;
}
/* tuple[2]['cursor'] = None
*
* If these checks fail, we can still return a valid object. Pickle
* will likely fail downstream, but there's nothing else we can do here */
if (!PyTuple_Check(tuple)) { goto exit; }
if (3 > PyTuple_GET_SIZE(tuple)) { goto exit; }
dict = PyTuple_GET_ITEM(tuple, 2); /* borrowed */
if (!PyDict_Check(dict)) { goto exit; }
/* Modify the tuple inplace and return it */
if (0 != PyDict_SetItemString(dict, "cursor", Py_None)) {
goto error;
}
exit:
rv = tuple;
tuple = NULL;
error:
Py_XDECREF(tuple);
Py_XDECREF(super);
return rv;
}
static int
psyco_errors_init(void) psyco_errors_init(void)
{ {
/* the names of the exceptions here reflect the oranization of the /* the names of the exceptions here reflect the oranization of the
@ -388,16 +492,22 @@ psyco_errors_init(void)
live in _psycopg */ live in _psycopg */
int i; int i;
PyObject *dict; PyObject *dict = NULL;
PyObject *base; PyObject *base;
PyObject *str; PyObject *str = NULL;
PyObject *descr = NULL;
int rv = -1;
static PyMethodDef psyco_error_reduce_ex_def =
{"__reduce_ex__", psyco_error_reduce_ex, METH_VARARGS, "pickle helper"};
for (i=0; exctable[i].name; i++) { for (i=0; exctable[i].name; i++) {
dict = PyDict_New(); if (!(dict = PyDict_New())) { goto exit; }
if (exctable[i].docstr) { if (exctable[i].docstr) {
str = Text_FromUTF8(exctable[i].docstr); if (!(str = Text_FromUTF8(exctable[i].docstr))) { goto exit; }
PyDict_SetItemString(dict, "__doc__", str); if (0 != PyDict_SetItemString(dict, "__doc__", str)) { goto exit; }
Py_CLEAR(str);
} }
if (exctable[i].base == 0) { if (exctable[i].base == 0) {
@ -411,7 +521,11 @@ psyco_errors_init(void)
else else
base = *exctable[i].base; base = *exctable[i].base;
*exctable[i].exc = PyErr_NewException(exctable[i].name, base, dict); if (!(*exctable[i].exc = PyErr_NewException(
exctable[i].name, base, dict))) {
goto exit;
}
Py_CLEAR(dict);
} }
/* Make pgerror, pgcode and cursor default to None on psycopg /* Make pgerror, pgcode and cursor default to None on psycopg
@ -420,53 +534,71 @@ psyco_errors_init(void)
PyObject_SetAttrString(Error, "pgerror", Py_None); PyObject_SetAttrString(Error, "pgerror", Py_None);
PyObject_SetAttrString(Error, "pgcode", Py_None); PyObject_SetAttrString(Error, "pgcode", Py_None);
PyObject_SetAttrString(Error, "cursor", Py_None); PyObject_SetAttrString(Error, "cursor", Py_None);
/* install __reduce_ex__ on Error to make all the subclasses picklable.
*
* Don't install it on Py 2.4: it is not used by the pickle
* protocol, and if called manually fails in an unsettling way,
* probably because the exceptions were old-style classes. */
#if PY_VERSION_HEX >= 0x02050000
if (!(descr = PyDescr_NewMethod((PyTypeObject *)Error,
&psyco_error_reduce_ex_def))) {
goto exit;
}
if (0 != PyObject_SetAttrString(Error,
psyco_error_reduce_ex_def.ml_name, descr)) {
goto exit;
}
#endif
rv = 0;
exit:
Py_XDECREF(descr);
Py_XDECREF(str);
Py_XDECREF(dict);
return rv;
} }
void void
psyco_errors_fill(PyObject *dict) psyco_errors_fill(PyObject *dict)
{ {
PyDict_SetItemString(dict, "Error", Error); int i;
PyDict_SetItemString(dict, "Warning", Warning); char *name;
PyDict_SetItemString(dict, "InterfaceError", InterfaceError);
PyDict_SetItemString(dict, "DatabaseError", DatabaseError); for (i = 0; exctable[i].name; i++) {
PyDict_SetItemString(dict, "InternalError", InternalError); if (NULL == exctable[i].exc) { continue; }
PyDict_SetItemString(dict, "OperationalError", OperationalError);
PyDict_SetItemString(dict, "ProgrammingError", ProgrammingError); /* the name is the part after the last dot */
PyDict_SetItemString(dict, "IntegrityError", IntegrityError); name = strrchr(exctable[i].name, '.');
PyDict_SetItemString(dict, "DataError", DataError); name = name ? name + 1 : exctable[i].name;
PyDict_SetItemString(dict, "NotSupportedError", NotSupportedError);
#ifdef PSYCOPG_EXTENSIONS PyDict_SetItemString(dict, name, *exctable[i].exc);
PyDict_SetItemString(dict, "QueryCanceledError", QueryCanceledError); }
PyDict_SetItemString(dict, "TransactionRollbackError",
TransactionRollbackError);
#endif
} }
void void
psyco_errors_set(PyObject *type) psyco_errors_set(PyObject *type)
{ {
PyObject_SetAttrString(type, "Error", Error); int i;
PyObject_SetAttrString(type, "Warning", Warning); char *name;
PyObject_SetAttrString(type, "InterfaceError", InterfaceError);
PyObject_SetAttrString(type, "DatabaseError", DatabaseError); for (i = 0; exctable[i].name; i++) {
PyObject_SetAttrString(type, "InternalError", InternalError); if (NULL == exctable[i].exc) { continue; }
PyObject_SetAttrString(type, "OperationalError", OperationalError);
PyObject_SetAttrString(type, "ProgrammingError", ProgrammingError); /* the name is the part after the last dot */
PyObject_SetAttrString(type, "IntegrityError", IntegrityError); name = strrchr(exctable[i].name, '.');
PyObject_SetAttrString(type, "DataError", DataError); name = name ? name + 1 : exctable[i].name;
PyObject_SetAttrString(type, "NotSupportedError", NotSupportedError);
#ifdef PSYCOPG_EXTENSIONS PyObject_SetAttrString(type, name, *exctable[i].exc);
PyObject_SetAttrString(type, "QueryCanceledError", QueryCanceledError); }
PyObject_SetAttrString(type, "TransactionRollbackError",
TransactionRollbackError);
#endif
} }
/* psyco_error_new /* psyco_set_error
Create a new error of the given type with extra attributes. */ Create a new error of the given type with extra attributes. */
void RAISES void
psyco_set_error(PyObject *exc, cursorObject *curs, const char *msg, psyco_set_error(PyObject *exc, cursorObject *curs, const char *msg,
const char *pgerror, const char *pgcode) const char *pgerror, const char *pgcode)
{ {
@ -495,15 +627,17 @@ psyco_set_error(PyObject *exc, cursorObject *curs, const char *msg,
} }
if (pgerror) { if (pgerror) {
t = conn_text_from_chars(conn, pgerror); if ((t = conn_text_from_chars(conn, pgerror))) {
PyObject_SetAttrString(err, "pgerror", t); PyObject_SetAttrString(err, "pgerror", t);
Py_DECREF(t); Py_DECREF(t);
}
} }
if (pgcode) { if (pgcode) {
t = conn_text_from_chars(conn, pgcode); if ((t = conn_text_from_chars(conn, pgcode))) {
PyObject_SetAttrString(err, "pgcode", t); PyObject_SetAttrString(err, "pgcode", t);
Py_DECREF(t); Py_DECREF(t);
}
} }
PyErr_SetObject(exc, err); PyErr_SetObject(exc, err);
@ -570,7 +704,7 @@ psyco_GetDecimalType(void)
} }
/* Store the object from future uses. */ /* Store the object from future uses. */
if (can_cache && !cachedType) { if (can_cache && !cachedType && decimalType) {
Py_INCREF(decimalType); Py_INCREF(decimalType);
cachedType = decimalType; cachedType = decimalType;
} }
@ -594,15 +728,11 @@ psyco_make_description_type(void)
/* Try to import collections.namedtuple */ /* Try to import collections.namedtuple */
if (!(coll = PyImport_ImportModule("collections"))) { if (!(coll = PyImport_ImportModule("collections"))) {
Dprintf("psyco_make_description_type: collections import failed"); Dprintf("psyco_make_description_type: collections import failed");
PyErr_Clear(); goto error;
rv = Py_None;
goto exit;
} }
if (!(nt = PyObject_GetAttrString(coll, "namedtuple"))) { if (!(nt = PyObject_GetAttrString(coll, "namedtuple"))) {
Dprintf("psyco_make_description_type: no collections.namedtuple"); Dprintf("psyco_make_description_type: no collections.namedtuple");
PyErr_Clear(); goto error;
rv = Py_None;
goto exit;
} }
/* Build the namedtuple */ /* Build the namedtuple */
@ -614,6 +744,13 @@ exit:
Py_XDECREF(nt); Py_XDECREF(nt);
return rv; return rv;
error:
/* controlled error: we will fall back to regular tuples. Return None. */
PyErr_Clear();
rv = Py_None;
Py_INCREF(rv);
goto exit;
} }
@ -826,10 +963,10 @@ INIT_MODULE(_psycopg)(void)
#endif #endif
/* other mixed initializations of module-level variables */ /* other mixed initializations of module-level variables */
psycoEncodings = PyDict_New(); if (!(psycoEncodings = PyDict_New())) { goto exit; }
psyco_encodings_fill(psycoEncodings); if (0 != psyco_encodings_fill(psycoEncodings)) { goto exit; }
psyco_null = Bytes_FromString("NULL"); psyco_null = Bytes_FromString("NULL");
psyco_DescriptionType = psyco_make_description_type(); if (!(psyco_DescriptionType = psyco_make_description_type())) { goto exit; }
/* set some module's parameters */ /* set some module's parameters */
PyModule_AddStringConstant(module, "__version__", PSYCOPG_VERSION); PyModule_AddStringConstant(module, "__version__", PSYCOPG_VERSION);
@ -862,14 +999,14 @@ INIT_MODULE(_psycopg)(void)
} }
#endif #endif
/* initialize default set of typecasters */ /* initialize default set of typecasters */
typecast_init(dict); if (0 != typecast_init(dict)) { goto exit; }
/* initialize microprotocols layer */ /* initialize microprotocols layer */
microprotocols_init(dict); microprotocols_init(dict);
psyco_adapters_init(dict); if (0 != psyco_adapters_init(dict)) { goto exit; }
/* create a standard set of exceptions and add them to the module's dict */ /* create a standard set of exceptions and add them to the module's dict */
psyco_errors_init(); if (0 != psyco_errors_init()) { goto exit; }
psyco_errors_fill(dict); psyco_errors_fill(dict);
/* Solve win32 build issue about non-constant initializer element */ /* Solve win32 build issue about non-constant initializer element */

View File

@ -250,34 +250,28 @@ PyObject *psyco_default_binary_cast;
/* typecast_init - initialize the dictionary and create default types */ /* typecast_init - initialize the dictionary and create default types */
int RAISES_NEG int
typecast_init(PyObject *dict) typecast_init(PyObject *dict)
{ {
int i; int i;
int rv = -1;
typecastObject *t = NULL;
/* create type dictionary and put it in module namespace */ /* create type dictionary and put it in module namespace */
psyco_types = PyDict_New(); if (!(psyco_types = PyDict_New())) { goto exit; }
psyco_binary_types = PyDict_New();
if (!psyco_types || !psyco_binary_types) {
Py_XDECREF(psyco_types);
Py_XDECREF(psyco_binary_types);
return -1;
}
PyDict_SetItemString(dict, "string_types", psyco_types); PyDict_SetItemString(dict, "string_types", psyco_types);
if (!(psyco_binary_types = PyDict_New())) { goto exit; }
PyDict_SetItemString(dict, "binary_types", psyco_binary_types); PyDict_SetItemString(dict, "binary_types", psyco_binary_types);
/* insert the cast types into the 'types' dictionary and register them in /* insert the cast types into the 'types' dictionary and register them in
the module dictionary */ the module dictionary */
for (i = 0; typecast_builtins[i].name != NULL; i++) { for (i = 0; typecast_builtins[i].name != NULL; i++) {
typecastObject *t;
Dprintf("typecast_init: initializing %s", typecast_builtins[i].name); Dprintf("typecast_init: initializing %s", typecast_builtins[i].name);
t = (typecastObject *)typecast_from_c(&(typecast_builtins[i]), dict); t = (typecastObject *)typecast_from_c(&(typecast_builtins[i]), dict);
if (t == NULL) return -1; if (t == NULL) { goto exit; }
if (typecast_add((PyObject *)t, NULL, 0) != 0) return -1; if (typecast_add((PyObject *)t, NULL, 0) < 0) { goto exit; }
PyDict_SetItem(dict, t->name, (PyObject *)t); PyDict_SetItem(dict, t->name, (PyObject *)t);
@ -285,6 +279,8 @@ typecast_init(PyObject *dict)
if (typecast_builtins[i].values == typecast_BINARY_types) { if (typecast_builtins[i].values == typecast_BINARY_types) {
psyco_default_binary_cast = (PyObject *)t; psyco_default_binary_cast = (PyObject *)t;
} }
Py_DECREF((PyObject *)t);
t = NULL;
} }
/* create and save a default cast object (but does not register it) */ /* create and save a default cast object (but does not register it) */
@ -294,29 +290,35 @@ typecast_init(PyObject *dict)
#ifdef HAVE_MXDATETIME #ifdef HAVE_MXDATETIME
if (0 == psyco_typecast_mxdatetime_init()) { if (0 == psyco_typecast_mxdatetime_init()) {
for (i = 0; typecast_mxdatetime[i].name != NULL; i++) { for (i = 0; typecast_mxdatetime[i].name != NULL; i++) {
typecastObject *t;
Dprintf("typecast_init: initializing %s", typecast_mxdatetime[i].name); Dprintf("typecast_init: initializing %s", typecast_mxdatetime[i].name);
t = (typecastObject *)typecast_from_c(&(typecast_mxdatetime[i]), dict); t = (typecastObject *)typecast_from_c(&(typecast_mxdatetime[i]), dict);
if (t == NULL) return -1; if (t == NULL) { goto exit; }
PyDict_SetItem(dict, t->name, (PyObject *)t); PyDict_SetItem(dict, t->name, (PyObject *)t);
Py_DECREF((PyObject *)t);
t = NULL;
} }
} }
#endif #endif
if (psyco_typecast_datetime_init()) { return -1; } if (0 > psyco_typecast_datetime_init()) { goto exit; }
for (i = 0; typecast_pydatetime[i].name != NULL; i++) { for (i = 0; typecast_pydatetime[i].name != NULL; i++) {
typecastObject *t;
Dprintf("typecast_init: initializing %s", typecast_pydatetime[i].name); Dprintf("typecast_init: initializing %s", typecast_pydatetime[i].name);
t = (typecastObject *)typecast_from_c(&(typecast_pydatetime[i]), dict); t = (typecastObject *)typecast_from_c(&(typecast_pydatetime[i]), dict);
if (t == NULL) return -1; if (t == NULL) { goto exit; }
PyDict_SetItem(dict, t->name, (PyObject *)t); PyDict_SetItem(dict, t->name, (PyObject *)t);
Py_DECREF((PyObject *)t);
t = NULL;
} }
return 0; rv = 0;
exit:
Py_XDECREF((PyObject *)t);
return rv;
} }
/* typecast_add - add a type object to the dictionary */ /* typecast_add - add a type object to the dictionary */
int RAISES_NEG int
typecast_add(PyObject *obj, PyObject *dict, int binary) typecast_add(PyObject *obj, PyObject *dict, int binary)
{ {
PyObject *val; PyObject *val;
@ -466,7 +468,7 @@ typecast_repr(PyObject *self)
static PyObject * static PyObject *
typecast_call(PyObject *obj, PyObject *args, PyObject *kwargs) typecast_call(PyObject *obj, PyObject *args, PyObject *kwargs)
{ {
char *string; const char *string;
Py_ssize_t length; Py_ssize_t length;
PyObject *cursor; PyObject *cursor;

View File

@ -71,8 +71,8 @@ extern HIDDEN PyObject *psyco_default_binary_cast;
/** exported functions **/ /** exported functions **/
/* used by module.c to init the type system and register types */ /* used by module.c to init the type system and register types */
HIDDEN int typecast_init(PyObject *dict); RAISES_NEG HIDDEN int typecast_init(PyObject *dict);
HIDDEN int typecast_add(PyObject *obj, PyObject *dict, int binary); RAISES_NEG HIDDEN int typecast_add(PyObject *obj, PyObject *dict, int binary);
/* the C callable typecastObject creator function */ /* the C callable typecastObject creator function */
HIDDEN PyObject *typecast_from_c(typecastObject_initlist *type, PyObject *d); HIDDEN PyObject *typecast_from_c(typecastObject_initlist *type, PyObject *d);

View File

@ -166,7 +166,7 @@ typecast_array_tokenize(const char *str, Py_ssize_t strlength,
return res; return res;
} }
static int RAISES_NEG static int
typecast_array_scan(const char *str, Py_ssize_t strlength, typecast_array_scan(const char *str, Py_ssize_t strlength,
PyObject *curs, PyObject *base, PyObject *array) PyObject *curs, PyObject *base, PyObject *array)
{ {
@ -199,7 +199,7 @@ typecast_array_scan(const char *str, Py_ssize_t strlength,
/* before anything else we free the memory */ /* before anything else we free the memory */
if (state == ASCAN_QUOTED) PyMem_Free(token); if (state == ASCAN_QUOTED) PyMem_Free(token);
if (obj == NULL) return 0; if (obj == NULL) return -1;
PyList_Append(array, obj); PyList_Append(array, obj);
Py_DECREF(obj); Py_DECREF(obj);
@ -207,25 +207,25 @@ typecast_array_scan(const char *str, Py_ssize_t strlength,
else if (state == ASCAN_BEGIN) { else if (state == ASCAN_BEGIN) {
PyObject *sub = PyList_New(0); PyObject *sub = PyList_New(0);
if (sub == NULL) return 0; if (sub == NULL) return -1;
PyList_Append(array, sub); PyList_Append(array, sub);
Py_DECREF(sub); Py_DECREF(sub);
if (stack_index == MAX_DIMENSIONS) if (stack_index == MAX_DIMENSIONS)
return 0; return -1;
stack[stack_index++] = array; stack[stack_index++] = array;
array = sub; array = sub;
} }
else if (state == ASCAN_ERROR) { else if (state == ASCAN_ERROR) {
return 0; return -1;
} }
else if (state == ASCAN_END) { else if (state == ASCAN_END) {
if (--stack_index < 0) if (--stack_index < 0)
return 0; return -1;
array = stack[stack_index]; array = stack[stack_index];
} }
@ -233,7 +233,7 @@ typecast_array_scan(const char *str, Py_ssize_t strlength,
break; break;
} }
return 1; return 0;
} }
@ -260,12 +260,11 @@ 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);
obj = PyList_New(0); if (!(obj = PyList_New(0))) { return NULL; }
/* scan the array skipping the first level of {} */ /* scan the array skipping the first level of {} */
if (typecast_array_scan(&str[1], len-2, curs, base, obj) == 0) { if (typecast_array_scan(&str[1], len-2, curs, base, obj) < 0) {
Py_DECREF(obj); Py_CLEAR(obj);
obj = NULL;
} }
return obj; return obj;

View File

@ -65,7 +65,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_INCREF(Py_None); return Py_None;}
str = Text_FromUTF8AndSize(s, len); 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);
#else #else

View File

@ -208,7 +208,7 @@ static const char hex_lut[128] = {
/* Parse a bytea output buffer encoded in 'hex' format. /* Parse a bytea output buffer encoded in 'hex' format.
* *
* the format is described in * the format is described in
* http://www.postgresql.org/docs/9.0/static/datatype-binary.html * http://www.postgresql.org/docs/current/static/datatype-binary.html
* *
* Parse the buffer in 'bufin', whose length is 'sizein'. * Parse the buffer in 'bufin', whose length is 'sizein'.
* Return a new buffer allocated by PyMem_Malloc and set 'sizeout' to its size. * Return a new buffer allocated by PyMem_Malloc and set 'sizeout' to its size.
@ -258,7 +258,7 @@ exit:
/* Parse a bytea output buffer encoded in 'escape' format. /* Parse a bytea output buffer encoded in 'escape' format.
* *
* the format is described in * the format is described in
* http://www.postgresql.org/docs/9.0/static/datatype-binary.html * http://www.postgresql.org/docs/current/static/datatype-binary.html
* *
* Parse the buffer in 'bufin', whose length is 'sizein'. * Parse the buffer in 'bufin', whose length is 'sizein'.
* Return a new buffer allocated by PyMem_Malloc and set 'sizeout' to its size. * Return a new buffer allocated by PyMem_Malloc and set 'sizeout' to its size.

View File

@ -26,7 +26,7 @@
#include <math.h> #include <math.h>
#include "datetime.h" #include "datetime.h"
static int RAISES_NEG static int
psyco_typecast_datetime_init(void) psyco_typecast_datetime_init(void)
{ {
Dprintf("psyco_typecast_datetime_init: datetime init"); Dprintf("psyco_typecast_datetime_init: datetime init");
@ -239,7 +239,7 @@ typecast_PYINTERVAL_cast(const char *str, Py_ssize_t len, PyObject *curs)
case '0': case '1': case '2': case '3': case '4': case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9': case '5': case '6': case '7': case '8': case '9':
v = v*10 + (double)*str - (double)'0'; v = v * 10.0 + (double)(*str - '0');
if (part == 6){ if (part == 6){
denominator *= 10; denominator *= 10;
} }

View File

@ -142,7 +142,7 @@ typecast_MXINTERVAL_cast(const char *str, Py_ssize_t len, PyObject *curs)
case '0': case '1': case '2': case '3': case '4': case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9': case '5': case '6': case '7': case '8': case '9':
v = v*10 + (double)*str - (double)'0'; v = v * 10.0 + (double)(*str - '0');
Dprintf("typecast_MXINTERVAL_cast: v = %f", v); Dprintf("typecast_MXINTERVAL_cast: v = %f", v);
if (part == 6){ if (part == 6){
denominator *= 10; denominator *= 10;

View File

@ -113,20 +113,19 @@ psycopg_escape_identifier_easy(const char *from, Py_ssize_t len)
* Allocate a new buffer on the Python heap containing the new string. * Allocate a new buffer on the Python heap containing the new string.
* 'len' is optional: if 0 the length is calculated. * 'len' is optional: if 0 the length is calculated.
* *
* Return NULL and set an exception in case of error. * Store the return in 'to' and return 0 in case of success, else return -1
* and raise an exception.
*/ */
char * RAISES_NEG int
psycopg_strdup(const char *from, Py_ssize_t len) psycopg_strdup(char **to, const char *from, Py_ssize_t len)
{ {
char *rv;
if (!len) { len = strlen(from); } if (!len) { len = strlen(from); }
if (!(rv = PyMem_Malloc(len + 1))) { if (!(*to = PyMem_Malloc(len + 1))) {
PyErr_NoMemory(); PyErr_NoMemory();
return NULL; return -1;
} }
strcpy(rv, from); strcpy(*to, from);
return rv; return 0;
} }
/* Ensure a Python object is a bytes string. /* Ensure a Python object is a bytes string.
@ -139,7 +138,7 @@ psycopg_strdup(const char *from, Py_ssize_t len)
* *
* It is safe to call the function on NULL. * It is safe to call the function on NULL.
*/ */
PyObject * STEALS(1) PyObject *
psycopg_ensure_bytes(PyObject *obj) psycopg_ensure_bytes(PyObject *obj)
{ {
PyObject *rv = NULL; PyObject *rv = NULL;
@ -169,7 +168,7 @@ psycopg_ensure_bytes(PyObject *obj)
* The function is ref neutral: steals a ref from obj and adds one to the * The function is ref neutral: steals a ref from obj and adds one to the
* return value. It is safe to call it on NULL. * return value. It is safe to call it on NULL.
*/ */
PyObject * STEALS(1) PyObject *
psycopg_ensure_text(PyObject *obj) psycopg_ensure_text(PyObject *obj)
{ {
#if PY_MAJOR_VERSION < 3 #if PY_MAJOR_VERSION < 3

View File

@ -78,7 +78,9 @@ static PyMemberDef xid_members[] = {
static PyObject * static PyObject *
xid_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) xid_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
{ {
XidObject *self = (XidObject *)type->tp_alloc(type, 0); XidObject *self;
if (!(self = (XidObject *)type->tp_alloc(type, 0))) { return NULL; }
Py_INCREF(Py_None); Py_INCREF(Py_None);
self->format_id = Py_None; self->format_id = Py_None;
@ -486,7 +488,7 @@ exit:
* *
* Return a borrowed reference. */ * Return a borrowed reference. */
static PyObject * BORROWED static PyObject *
_xid_get_parse_regex(void) { _xid_get_parse_regex(void) {
static PyObject *rv; static PyObject *rv;

View File

@ -7,10 +7,10 @@ define=PSYCOPG_EXTENSIONS,PSYCOPG_NEW_BOOLEAN,HAVE_PQFREEMEM
# PSYCOPG_DEBUG can be added to enable verbose debug information # PSYCOPG_DEBUG can be added to enable verbose debug information
# PSYCOPG_NEW_BOOLEAN to format booleans as true/false vs 't'/'f' # PSYCOPG_NEW_BOOLEAN to format booleans as true/false vs 't'/'f'
# Set to 1 to use Python datatime objects for default date/time representation. # Set to 1 to use Python datetime objects for default date/time representation.
use_pydatetime=1 use_pydatetime=1
# If the build system does not find the mx.DateTime headers, try # If the build system does not find the mx.DateTime headers, try
# uncommenting the following line and setting its value to the right path. # uncommenting the following line and setting its value to the right path.
#mx_include_dir= #mx_include_dir=
@ -28,14 +28,14 @@ have_ssl=0
# set it to the pg_config full path. # set it to the pg_config full path.
#pg_config= #pg_config=
# If "pg_config" is not available, "include_dirs" can be used to locate # If "pg_config" is not available, "include_dirs" can be used to locate
# postgresql headers and libraries. Some extra checks on sys.platform will # postgresql headers and libraries. Some extra checks on sys.platform will
# still be done in setup.py. # still be done in setup.py.
# The next line is the default as used on psycopg author Debian laptop: # The next line is the default as used on psycopg author Debian laptop:
#include_dirs=/usr/include/postgresql:/usr/include/postgresql/server #include_dirs=/usr/include/postgresql:/usr/include/postgresql/server
# Uncomment next line on Mandrake 10.x (and comment previous ones): # Uncomment next line on Mandrake 10.x (and comment previous ones):
#include_dirs=/usr/include/pgsql/8.0:/usr/include/pgsql/8.0/server #include_dirs=/usr/include/pgsql/8.0:/usr/include/pgsql/8.0/server
# Uncomment next line on SUSE 9.3 (and comment previous ones): # Uncomment next line on SUSE 9.3 (and comment previous ones):
#include_dirs=/usr/include/pgsql:/usr/include/pgsql/server #include_dirs=/usr/include/pgsql:/usr/include/pgsql/server

View File

@ -380,7 +380,9 @@ class DatabaseAPI20Test(unittest.TestCase):
self.assertRaises(self.driver.Error,con.commit) self.assertRaises(self.driver.Error,con.commit)
# connection.close should raise an Error if called more than once # connection.close should raise an Error if called more than once
self.assertRaises(self.driver.Error,con.close) # Issue discussed on DB-SIG: consensus seem that close() should not
# raised if called on closed objects. Issue reported back to Stuart.
# self.assertRaises(self.driver.Error,con.close)
def test_execute(self): def test_execute(self):
con = self._connect() con = self._connect()

View File

@ -48,6 +48,12 @@ class ConnectionTests(unittest.TestCase):
conn.close() conn.close()
self.assertEqual(conn.closed, True) self.assertEqual(conn.closed, True)
def test_close_idempotent(self):
conn = self.conn
conn.close()
conn.close()
self.assert_(conn.closed)
def test_cursor_closed_attribute(self): def test_cursor_closed_attribute(self):
conn = self.conn conn = self.conn
curs = conn.cursor() curs = conn.cursor()
@ -166,6 +172,35 @@ class ConnectionTests(unittest.TestCase):
gc.collect() gc.collect()
self.assert_(w() is None) self.assert_(w() is None)
def test_commit_concurrency(self):
# The problem is the one reported in ticket #103. Because of bad
# status check, we commit even when a commit is already on its way.
# We can detect this condition by the warnings.
conn = self.conn
notices = []
stop = []
def committer():
while not stop:
conn.commit()
while conn.notices:
notices.append((2, conn.notices.pop()))
cur = conn.cursor()
t1 = threading.Thread(target=committer)
t1.start()
i = 1
for i in range(1000):
cur.execute("select %s;",(i,))
conn.commit()
while conn.notices:
notices.append((1, conn.notices.pop()))
# Stop the committer thread
stop.append(True)
self.assert_(not notices, "%d notices raised" % len(notices))
class IsolationLevelsTestCase(unittest.TestCase): class IsolationLevelsTestCase(unittest.TestCase):

View File

@ -37,6 +37,12 @@ class CursorTests(unittest.TestCase):
def tearDown(self): def tearDown(self):
self.conn.close() self.conn.close()
def test_close_idempotent(self):
cur = self.conn.cursor()
cur.close()
cur.close()
self.assert_(cur.closed)
def test_empty_query(self): def test_empty_query(self):
cur = self.conn.cursor() cur = self.conn.cursor()
self.assertRaises(psycopg2.ProgrammingError, cur.execute, "") self.assertRaises(psycopg2.ProgrammingError, cur.execute, "")
@ -234,6 +240,17 @@ class CursorTests(unittest.TestCase):
# everything swallowed in two gulps # everything swallowed in two gulps
self.assertEqual(rv, [(i,((i - 1) % 30) + 1) for i in range(1,51)]) self.assertEqual(rv, [(i,((i - 1) % 30) + 1) for i in range(1,51)])
@skip_before_postgres(8, 0)
def test_iter_named_cursor_rownumber(self):
curs = self.conn.cursor('tmp')
# note: this fails if itersize < dataset: internally we check
# rownumber == rowcount to detect when to read anoter page, so we
# would need an extra attribute to have a monotonic rownumber.
curs.itersize = 20
curs.execute('select generate_series(1,10)')
for i, rec in enumerate(curs):
self.assertEqual(i + 1, curs.rownumber)
@skip_if_no_namedtuple @skip_if_no_namedtuple
def test_namedtuple_description(self): def test_namedtuple_description(self):
curs = self.conn.cursor() curs = self.conn.cursor()

View File

@ -25,7 +25,7 @@
import math import math
import unittest import unittest
import psycopg2 import psycopg2
from psycopg2.tz import FixedOffsetTimezone from psycopg2.tz import FixedOffsetTimezone, ZERO
from testconfig import dsn from testconfig import dsn
class CommonDatetimeTestsMixin: class CommonDatetimeTestsMixin:
@ -78,7 +78,7 @@ class CommonDatetimeTestsMixin:
value = self.DATETIME(None, self.curs) value = self.DATETIME(None, self.curs)
self.assertEqual(value, None) self.assertEqual(value, None)
def test_parse_incomplete_time(self): def test_parse_incomplete_datetime(self):
self.assertRaises(psycopg2.DataError, self.assertRaises(psycopg2.DataError,
self.DATETIME, '2007', self.curs) self.DATETIME, '2007', self.curs)
self.assertRaises(psycopg2.DataError, self.assertRaises(psycopg2.DataError,
@ -513,6 +513,32 @@ class FromTicksTestCase(unittest.TestCase):
time(0, 11, 59, 999920)) time(0, 11, 59, 999920))
class FixedOffsetTimezoneTests(unittest.TestCase):
def test_init_with_no_args(self):
tzinfo = FixedOffsetTimezone()
self.assert_(tzinfo._offset is ZERO)
self.assert_(tzinfo._name is None)
def test_repr_with_positive_offset(self):
tzinfo = FixedOffsetTimezone(5 * 60)
self.assertEqual(repr(tzinfo), "psycopg2.tz.FixedOffsetTimezone(offset=300, name=None)")
def test_repr_with_negative_offset(self):
tzinfo = FixedOffsetTimezone(-5 * 60)
self.assertEqual(repr(tzinfo), "psycopg2.tz.FixedOffsetTimezone(offset=-300, name=None)")
def test_repr_with_name(self):
tzinfo = FixedOffsetTimezone(name="FOO")
self.assertEqual(repr(tzinfo), "psycopg2.tz.FixedOffsetTimezone(offset=0, name='FOO')")
def test_instance_caching(self):
self.assert_(FixedOffsetTimezone(name="FOO") is FixedOffsetTimezone(name="FOO"))
self.assert_(FixedOffsetTimezone(7 * 60) is FixedOffsetTimezone(7 * 60))
self.assert_(FixedOffsetTimezone(-9 * 60, 'FOO') is FixedOffsetTimezone(-9 * 60, 'FOO'))
self.assert_(FixedOffsetTimezone(9 * 60) is not FixedOffsetTimezone(9 * 60, 'FOO'))
self.assert_(FixedOffsetTimezone(name='FOO') is not FixedOffsetTimezone(9 * 60, 'FOO'))
def test_suite(): def test_suite():
return unittest.TestLoader().loadTestsFromName(__name__) return unittest.TestLoader().loadTestsFromName(__name__)

View File

@ -35,6 +35,7 @@ class ExtrasDictCursorTests(unittest.TestCase):
def tearDown(self): def tearDown(self):
self.conn.close() self.conn.close()
def testDictCursorWithPlainCursorFetchOne(self): def testDictCursorWithPlainCursorFetchOne(self):
self._testWithPlainCursor(lambda curs: curs.fetchone()) self._testWithPlainCursor(lambda curs: curs.fetchone())
@ -53,6 +54,26 @@ class ExtrasDictCursorTests(unittest.TestCase):
return row return row
self._testWithPlainCursor(getter) self._testWithPlainCursor(getter)
def testUpdateRow(self):
row = self._testWithPlainCursor(lambda curs: curs.fetchone())
row['foo'] = 'qux'
self.failUnless(row['foo'] == 'qux')
self.failUnless(row[0] == 'qux')
@skip_before_postgres(8, 0)
def testDictCursorWithPlainCursorIterRowNumber(self):
curs = self.conn.cursor(cursor_factory=psycopg2.extras.DictCursor)
self._testIterRowNumber(curs)
def _testWithPlainCursor(self, getter):
curs = self.conn.cursor(cursor_factory=psycopg2.extras.DictCursor)
curs.execute("SELECT * FROM ExtrasDictCursorTests")
row = getter(curs)
self.failUnless(row['foo'] == 'bar')
self.failUnless(row[0] == 'bar')
return row
def testDictCursorWithPlainCursorRealFetchOne(self): def testDictCursorWithPlainCursorRealFetchOne(self):
self._testWithPlainCursorReal(lambda curs: curs.fetchone()) self._testWithPlainCursorReal(lambda curs: curs.fetchone())
@ -71,6 +92,17 @@ class ExtrasDictCursorTests(unittest.TestCase):
return row return row
self._testWithPlainCursorReal(getter) self._testWithPlainCursorReal(getter)
@skip_before_postgres(8, 0)
def testDictCursorWithPlainCursorRealIterRowNumber(self):
curs = self.conn.cursor(cursor_factory=psycopg2.extras.RealDictCursor)
self._testIterRowNumber(curs)
def _testWithPlainCursorReal(self, getter):
curs = self.conn.cursor(cursor_factory=psycopg2.extras.RealDictCursor)
curs.execute("SELECT * FROM ExtrasDictCursorTests")
row = getter(curs)
self.failUnless(row['foo'] == 'bar')
def testDictCursorWithNamedCursorFetchOne(self): def testDictCursorWithNamedCursorFetchOne(self):
self._testWithNamedCursor(lambda curs: curs.fetchone()) self._testWithNamedCursor(lambda curs: curs.fetchone())
@ -95,6 +127,18 @@ class ExtrasDictCursorTests(unittest.TestCase):
curs = self.conn.cursor('tmp', cursor_factory=psycopg2.extras.DictCursor) curs = self.conn.cursor('tmp', cursor_factory=psycopg2.extras.DictCursor)
self._testNamedCursorNotGreedy(curs) self._testNamedCursorNotGreedy(curs)
@skip_before_postgres(8, 0)
def testDictCursorWithNamedCursorIterRowNumber(self):
curs = self.conn.cursor('tmp', cursor_factory=psycopg2.extras.DictCursor)
self._testIterRowNumber(curs)
def _testWithNamedCursor(self, getter):
curs = self.conn.cursor('aname', cursor_factory=psycopg2.extras.DictCursor)
curs.execute("SELECT * FROM ExtrasDictCursorTests")
row = getter(curs)
self.failUnless(row['foo'] == 'bar')
self.failUnless(row[0] == 'bar')
def testDictCursorRealWithNamedCursorFetchOne(self): def testDictCursorRealWithNamedCursorFetchOne(self):
self._testWithNamedCursorReal(lambda curs: curs.fetchone()) self._testWithNamedCursorReal(lambda curs: curs.fetchone())
@ -119,27 +163,10 @@ class ExtrasDictCursorTests(unittest.TestCase):
curs = self.conn.cursor('tmp', cursor_factory=psycopg2.extras.RealDictCursor) curs = self.conn.cursor('tmp', cursor_factory=psycopg2.extras.RealDictCursor)
self._testNamedCursorNotGreedy(curs) self._testNamedCursorNotGreedy(curs)
@skip_before_postgres(8, 0)
def _testWithPlainCursor(self, getter): def testDictCursorRealWithNamedCursorIterRowNumber(self):
curs = self.conn.cursor(cursor_factory=psycopg2.extras.DictCursor) curs = self.conn.cursor('tmp', cursor_factory=psycopg2.extras.RealDictCursor)
curs.execute("SELECT * FROM ExtrasDictCursorTests") self._testIterRowNumber(curs)
row = getter(curs)
self.failUnless(row['foo'] == 'bar')
self.failUnless(row[0] == 'bar')
return row
def _testWithNamedCursor(self, getter):
curs = self.conn.cursor('aname', cursor_factory=psycopg2.extras.DictCursor)
curs.execute("SELECT * FROM ExtrasDictCursorTests")
row = getter(curs)
self.failUnless(row['foo'] == 'bar')
self.failUnless(row[0] == 'bar')
def _testWithPlainCursorReal(self, getter):
curs = self.conn.cursor(cursor_factory=psycopg2.extras.RealDictCursor)
curs.execute("SELECT * FROM ExtrasDictCursorTests")
row = getter(curs)
self.failUnless(row['foo'] == 'bar')
def _testWithNamedCursorReal(self, getter): def _testWithNamedCursorReal(self, getter):
curs = self.conn.cursor('aname', cursor_factory=psycopg2.extras.RealDictCursor) curs = self.conn.cursor('aname', cursor_factory=psycopg2.extras.RealDictCursor)
@ -147,11 +174,6 @@ class ExtrasDictCursorTests(unittest.TestCase):
row = getter(curs) row = getter(curs)
self.failUnless(row['foo'] == 'bar') self.failUnless(row['foo'] == 'bar')
def testUpdateRow(self):
row = self._testWithPlainCursor(lambda curs: curs.fetchone())
row['foo'] = 'qux'
self.failUnless(row['foo'] == 'qux')
self.failUnless(row[0] == 'qux')
def _testNamedCursorNotGreedy(self, curs): def _testNamedCursorNotGreedy(self, curs):
curs.itersize = 2 curs.itersize = 2
@ -165,6 +187,14 @@ class ExtrasDictCursorTests(unittest.TestCase):
self.assert_(recs[1]['ts'] - recs[0]['ts'] < timedelta(seconds=0.005)) self.assert_(recs[1]['ts'] - recs[0]['ts'] < timedelta(seconds=0.005))
self.assert_(recs[2]['ts'] - recs[1]['ts'] > timedelta(seconds=0.0099)) self.assert_(recs[2]['ts'] - recs[1]['ts'] > timedelta(seconds=0.0099))
def _testIterRowNumber(self, curs):
# Only checking for dataset < itersize:
# see CursorTests.test_iter_named_cursor_rownumber
curs.itersize = 20
curs.execute("""select * from generate_series(1,10)""")
for i, r in enumerate(curs):
self.assertEqual(i + 1, curs.rownumber)
class NamedTupleCursorTest(unittest.TestCase): class NamedTupleCursorTest(unittest.TestCase):
def setUp(self): def setUp(self):
@ -192,12 +222,14 @@ class NamedTupleCursorTest(unittest.TestCase):
@skip_if_no_namedtuple @skip_if_no_namedtuple
def test_fetchone(self): def test_fetchone(self):
curs = self.conn.cursor() curs = self.conn.cursor()
curs.execute("select * from nttest where i = 1") curs.execute("select * from nttest order by 1")
t = curs.fetchone() t = curs.fetchone()
self.assertEqual(t[0], 1) self.assertEqual(t[0], 1)
self.assertEqual(t.i, 1) self.assertEqual(t.i, 1)
self.assertEqual(t[1], 'foo') self.assertEqual(t[1], 'foo')
self.assertEqual(t.s, 'foo') self.assertEqual(t.s, 'foo')
self.assertEqual(curs.rownumber, 1)
self.assertEqual(curs.rowcount, 3)
@skip_if_no_namedtuple @skip_if_no_namedtuple
def test_fetchmany_noarg(self): def test_fetchmany_noarg(self):
@ -210,6 +242,8 @@ class NamedTupleCursorTest(unittest.TestCase):
self.assertEqual(res[0].s, 'foo') self.assertEqual(res[0].s, 'foo')
self.assertEqual(res[1].i, 2) self.assertEqual(res[1].i, 2)
self.assertEqual(res[1].s, 'bar') self.assertEqual(res[1].s, 'bar')
self.assertEqual(curs.rownumber, 2)
self.assertEqual(curs.rowcount, 3)
@skip_if_no_namedtuple @skip_if_no_namedtuple
def test_fetchmany(self): def test_fetchmany(self):
@ -221,6 +255,8 @@ class NamedTupleCursorTest(unittest.TestCase):
self.assertEqual(res[0].s, 'foo') self.assertEqual(res[0].s, 'foo')
self.assertEqual(res[1].i, 2) self.assertEqual(res[1].i, 2)
self.assertEqual(res[1].s, 'bar') self.assertEqual(res[1].s, 'bar')
self.assertEqual(curs.rownumber, 2)
self.assertEqual(curs.rowcount, 3)
@skip_if_no_namedtuple @skip_if_no_namedtuple
def test_fetchall(self): def test_fetchall(self):
@ -234,6 +270,8 @@ class NamedTupleCursorTest(unittest.TestCase):
self.assertEqual(res[1].s, 'bar') self.assertEqual(res[1].s, 'bar')
self.assertEqual(res[2].i, 3) self.assertEqual(res[2].i, 3)
self.assertEqual(res[2].s, 'baz') self.assertEqual(res[2].s, 'baz')
self.assertEqual(curs.rownumber, 3)
self.assertEqual(curs.rowcount, 3)
@skip_if_no_namedtuple @skip_if_no_namedtuple
def test_executemany(self): def test_executemany(self):
@ -251,16 +289,26 @@ class NamedTupleCursorTest(unittest.TestCase):
curs = self.conn.cursor() curs = self.conn.cursor()
curs.execute("select * from nttest order by 1") curs.execute("select * from nttest order by 1")
i = iter(curs) i = iter(curs)
self.assertEqual(curs.rownumber, 0)
t = i.next() t = i.next()
self.assertEqual(t.i, 1) self.assertEqual(t.i, 1)
self.assertEqual(t.s, 'foo') self.assertEqual(t.s, 'foo')
self.assertEqual(curs.rownumber, 1)
self.assertEqual(curs.rowcount, 3)
t = i.next() t = i.next()
self.assertEqual(t.i, 2) self.assertEqual(t.i, 2)
self.assertEqual(t.s, 'bar') self.assertEqual(t.s, 'bar')
self.assertEqual(curs.rownumber, 2)
self.assertEqual(curs.rowcount, 3)
t = i.next() t = i.next()
self.assertEqual(t.i, 3) self.assertEqual(t.i, 3)
self.assertEqual(t.s, 'baz') self.assertEqual(t.s, 'baz')
self.assertRaises(StopIteration, i.next) self.assertRaises(StopIteration, i.next)
self.assertEqual(curs.rownumber, 3)
self.assertEqual(curs.rowcount, 3)
def test_error_message(self): def test_error_message(self):
try: try:
@ -385,6 +433,17 @@ class NamedTupleCursorTest(unittest.TestCase):
self.assert_(recs[1].ts - recs[0].ts < timedelta(seconds=0.005)) self.assert_(recs[1].ts - recs[0].ts < timedelta(seconds=0.005))
self.assert_(recs[2].ts - recs[1].ts > timedelta(seconds=0.0099)) self.assert_(recs[2].ts - recs[1].ts > timedelta(seconds=0.0099))
@skip_if_no_namedtuple
@skip_before_postgres(8, 0)
def test_named_rownumber(self):
curs = self.conn.cursor('tmp')
# Only checking for dataset < itersize:
# see CursorTests.test_iter_named_cursor_rownumber
curs.itersize = 4
curs.execute("""select * from generate_series(1,3)""")
for i, t in enumerate(curs):
self.assertEqual(i + 1, curs.rownumber)
def test_suite(): def test_suite():
return unittest.TestLoader().loadTestsFromName(__name__) return unittest.TestLoader().loadTestsFromName(__name__)

View File

@ -25,13 +25,12 @@
import os import os
import shutil import shutil
import tempfile import tempfile
from testutils import unittest, decorate_all_tests, skip_if_tpc_disabled
import psycopg2 import psycopg2
import psycopg2.extensions import psycopg2.extensions
from psycopg2.extensions import b from psycopg2.extensions import b
from testconfig import dsn, green from testconfig import dsn, green
from testutils import unittest, decorate_all_tests from testutils import unittest, decorate_all_tests, skip_if_tpc_disabled
def skip_if_no_lo(f): def skip_if_no_lo(f):
def skip_if_no_lo_(self): def skip_if_no_lo_(self):

View File

@ -22,7 +22,8 @@
# 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.
from testutils import unittest from testutils import unittest, skip_before_python
from testconfig import dsn
import psycopg2 import psycopg2
@ -127,6 +128,40 @@ class ConnectTestCase(unittest.TestCase):
self.assertEqual(self.args[0], r"dbname='\\every thing\''") self.assertEqual(self.args[0], r"dbname='\\every thing\''")
class ExceptionsTestCase(unittest.TestCase):
def setUp(self):
self.conn = psycopg2.connect(dsn)
def tearDown(self):
self.conn.close()
def test_attributes(self):
cur = self.conn.cursor()
try:
cur.execute("select * from nonexist")
except psycopg2.Error, exc:
e = exc
self.assertEqual(e.pgcode, '42P01')
self.assert_(e.pgerror)
self.assert_(e.cursor is cur)
@skip_before_python(2, 5)
def test_pickle(self):
import pickle
cur = self.conn.cursor()
try:
cur.execute("select * from nonexist")
except psycopg2.Error, exc:
e = exc
e1 = pickle.loads(pickle.dumps(e))
self.assertEqual(e.pgerror, e1.pgerror)
self.assertEqual(e.pgcode, e1.pgcode)
self.assert_(e1.cursor is None)
def test_suite(): def test_suite():
return unittest.TestLoader().loadTestsFromName(__name__) return unittest.TestLoader().loadTestsFromName(__name__)

View File

@ -45,8 +45,8 @@ class QuotingTestCase(unittest.TestCase):
The tests also check that no warning is raised ('escape_string_warning' The tests also check that no warning is raised ('escape_string_warning'
should be on). should be on).
http://www.postgresql.org/docs/8.1/static/sql-syntax.html#SQL-SYNTAX-STRINGS http://www.postgresql.org/docs/current/static/sql-syntax-lexical.html#SQL-SYNTAX-STRINGS
http://www.postgresql.org/docs/8.1/static/runtime-config-compatible.html http://www.postgresql.org/docs/current/static/runtime-config-compatible.html
""" """
def setUp(self): def setUp(self):
self.conn = psycopg2.connect(dsn) self.conn = psycopg2.connect(dsn)

View File

@ -82,13 +82,22 @@ class TypesExtrasTests(unittest.TestCase):
def testINET(self): def testINET(self):
psycopg2.extras.register_inet() psycopg2.extras.register_inet()
i = "192.168.1.0/24"; i = psycopg2.extras.Inet("192.168.1.0/24")
s = self.execute("SELECT %s AS foo", (i,)) s = self.execute("SELECT %s AS foo", (i,))
self.failUnless(i == s) self.failUnless(i.addr == s.addr)
# must survive NULL cast to inet # must survive NULL cast to inet
s = self.execute("SELECT NULL::inet AS foo") s = self.execute("SELECT NULL::inet AS foo")
self.failUnless(s is None) self.failUnless(s is None)
def testINETARRAY(self):
psycopg2.extras.register_inet()
i = psycopg2.extras.Inet("192.168.1.0/24")
s = self.execute("SELECT %s AS foo", ([i],))
self.failUnless(i.addr == s[0].addr)
# must survive NULL cast to inet
s = self.execute("SELECT NULL::inet[] AS foo")
self.failUnless(s is None)
def test_inet_conform(self): def test_inet_conform(self):
from psycopg2.extras import Inet from psycopg2.extras import Inet
i = Inet("192.168.1.0/24") i = Inet("192.168.1.0/24")