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
********************************
** Important note: if you plan to use psyopg2 in a multithreaed application
** Important note: if you plan to use psycopg2 in a multithreaded application,
make sure that your libpq has been compiled with the --with-thread-safety
option. psycopg2 will work correctly even with a non-thread-safe libpq but
libpq will leak memory.
@ -16,7 +16,7 @@ then:
to build in the local directory; and:
python setup.py install
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
is required.
First you need to create a libpython2X.a as described in
First you need to create a libpython2X.a as described in
http://starship.python.net/crew/kernr/mingw32/Notes.html. Then run:
python setup.py build_ext --compiler=mingw32 install

16
NEWS
View File

@ -1,8 +1,24 @@
What's new in psycopg 2.4.5
---------------------------
- The close() methods on connections and cursors don't raise exceptions
if called on already closed objects.
- Fixed fetchmany() with no argument in cursor subclasses
(ticket #84).
- Use lo_creat() instead of lo_create() when possible for better
interaction with pgpool-II (ticket #88).
- Error and its subclasses are picklable, useful for multiprocessing
interaction (ticket #90).
- Better efficiency and formatting of timezone offset objects thanks
to Menno Smits (tickets #94, #95).
- Fixed 'rownumber' during iteration on cursor subclasses.
Regression introduced in 2.4.4 (ticket #100).
- Added support for 'inet' arrays.
- Fixed 'commit()' concurrency problem (ticket #103).
- Codebase cleaned up using the GCC Python plugin's static analysis
tool, which has revealed several unchecked return values, possible
NULL dereferences, reference counting problems. Many thanks to David
Malcolm for the useful tool and the assistance provided using it.
What's new in psycopg 2.4.4

View File

@ -143,7 +143,7 @@ geometric type:
.. |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::
@ -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.
.. |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: 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
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| 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
sending a query via `~cursor.execute()` or `~cursor.callproc()`, call
@ -472,7 +472,7 @@ resources about the topic.
.. _gevent: http://www.gevent.org/
.. _SQLAlchemy: http://www.sqlalchemy.org/
.. _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::

View File

@ -111,7 +111,7 @@ rst_epilog = """
.. _DBAPI: http://www.python.org/dev/peps/pep-0249/
.. _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/

View File

@ -185,7 +185,7 @@ The ``connection`` class
.. seealso:: the |PREPARE TRANSACTION|_ PostgreSQL command.
.. |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::
@ -211,7 +211,7 @@ The ``connection`` class
.. seealso:: the |COMMIT PREPARED|_ PostgreSQL command.
.. |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::
@ -233,7 +233,7 @@ The ``connection`` class
.. seealso:: the |ROLLBACK PREPARED|_ PostgreSQL command.
.. |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::
@ -264,7 +264,7 @@ The ``connection`` class
.. seealso:: the |pg_prepared_xacts|_ system view.
.. |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| 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
@ -312,10 +312,10 @@ The ``connection`` class
available for recover.
.. |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`
.. __: 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
@ -336,7 +336,7 @@ The ``connection`` class
the current session. See |SET TRANSACTION|_ for further details.
.. |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
transactions/statements. The value can be one of the
@ -357,7 +357,7 @@ The ``connection`` class
parameter to the server default.
.. _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
function invocation, only the specified parameters are changed.
@ -367,11 +367,11 @@ The ``connection`` class
|default_transaction_read_only|__, |default_transaction_deferrable|__.
.. |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`
.. __: 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`
.. __: 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::
@ -445,7 +445,7 @@ The ``connection`` class
is the encoding defined by the database. It should be one of the
`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::
@ -471,7 +471,7 @@ The ``connection`` class
configuration parameters`__ such as ``log_statement``,
``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
@ -500,7 +500,7 @@ The ``connection`` class
.. 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
@ -521,7 +521,7 @@ The ``connection`` class
.. 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
@ -538,7 +538,7 @@ The ``connection`` class
.. 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::
@ -553,7 +553,7 @@ The ``connection`` class
.. 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
@ -571,7 +571,7 @@ The ``connection`` class
.. 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
@ -606,7 +606,7 @@ The ``connection`` class
`~psycopg2.extensions.lobject` to be instantiated.
.. |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:

View File

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

View File

@ -39,7 +39,7 @@ From PostgreSQL documentation:
.. 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:

View File

@ -82,7 +82,7 @@ functionalities defined by the |DBAPI|_.
The method uses the efficient |lo_export|_ libpq function.
.. |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)
@ -103,7 +103,7 @@ functionalities defined by the |DBAPI|_.
running these versions. It uses the |lo_truncate|_ libpq function.
.. |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()
@ -325,6 +325,20 @@ details.
.. 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])
@ -349,7 +363,7 @@ details.
Used by Psycopg when adapting or casting unicode strings. See
: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
@ -432,7 +446,7 @@ set to one of the following constants:
.. seealso:: `Read Committed Isolation Level`__ in PostgreSQL
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
@ -456,7 +470,7 @@ set to one of the following constants:
.. seealso:: `Repeatable Read Isolation Level`__ in PostgreSQL
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
@ -475,7 +489,7 @@ set to one of the following constants:
.. 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
.. |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.
.. |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::
@ -250,6 +250,7 @@ UUID data type
^^^^^^^^^^^^^^^^^^^^^^
.. versionadded:: 2.0.9
.. versionchanged:: 2.4.5 added inet array support.
.. doctest::
@ -264,7 +265,7 @@ UUID data type
'192.168.0.1/24'
.. autofunction:: register_inet()
.. autofunction:: register_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).
.. |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`?
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;
- 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/9.0/static/runtime-config-client.html#GUC-BYTEA-OUTPUT
.. __: http://www.postgresql.org/docs/current/static/datatype-binary.html
.. __: 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

View File

@ -32,9 +32,9 @@ Psycopg 2 is both Unicode and Python 3 friendly.
.. _PostgreSQL: http://www.postgresql.org/
.. _Python: http://www.python.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`
.. __: http://www.postgresql.org/docs/9.0/static/sql-copy.html
.. __: http://www.postgresql.org/docs/current/static/sql-copy.html
.. 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
using `environment variables`__.
.. __: http://www.postgresql.org/docs/9.1/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-connect.html#LIBPQ-PQCONNECTDBPARAMS
.. __: http://www.postgresql.org/docs/current/static/libpq-envars.html
Using the *connection_factory* parameter a different class or
connections factory can be specified. It should be a callable object
@ -117,31 +117,30 @@ available through the following exceptions:
if not available. The `~psycopg2.errorcodes` module contains
symbolic constants representing PostgreSQL error codes.
.. doctest::
:options: +NORMALIZE_WHITESPACE
>>> try:
... cur.execute("SELECT * FROM barf")
... except Exception, e:
... pass
>>> e.pgcode
'42P01'
>>> print e.pgerror
ERROR: relation "barf" does not exist
LINE 1: SELECT * FROM barf
^
.. attribute:: cursor
The cursor the exception was raised from; `None` if not applicable.
.. extension::
The `~Error.pgerror` and `~Error.pgcode` attributes 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.
The `~Error.pgerror`, `~Error.pgcode`, and `~Error.cursor` attributes
are Psycopg extensions.
.. exception:: InterfaceError
Exception raised for errors that are related to the database interface

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
server configuration file or in the client session (using a query such as
``SET bytea_output TO escape;``) before receiving binary data.
.. __: http://www.postgresql.org/docs/9.0/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/datatype-binary.html
.. __: http://www.postgresql.org/docs/current/static/runtime-config-client.html#GUC-BYTEA-OUTPUT
.. _adapt-date:
@ -334,6 +334,14 @@ the SQL string that would be sent to the database.
>>> cur.mogrify("SELECT %s;", ([10, 20, 30], ))
'SELECT ARRAY[10, 20, 30];'
.. note::
Reading back from PostgreSQL, arrays are converted to list of Python
objects as expected, but only if the types are known one. Arrays of
unknown types are returned as represented by the database (e.g.
``{a,b,c}``). You can easily create a typecaster for :ref:`array of
unknown types <cast-array-unknown>`.
.. _adapt-tuple:
.. index::
@ -378,7 +386,7 @@ the SQL string that would be sent to the database.
further details.
.. |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
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))
.. __: 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
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: 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
*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
:ref:`green-support` for further details.
@ -687,7 +695,7 @@ Please refer to the documentation of the single methods for details and
examples.
.. |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
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
`~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.
.. |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: 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:
#
# 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={}):

View File

@ -88,25 +88,17 @@ class DictCursorBase(_cursor):
def __iter__(self):
if self._prefetch:
res = _cursor.fetchmany(self, self.itersize)
if not res:
return
res = _cursor.__iter__(self)
first = res.next()
if self._query_executed:
self._build_index()
if not self._prefetch:
res = _cursor.fetchmany(self, self.itersize)
res = _cursor.__iter__(self)
first = res.next()
for r in res:
yield r
# the above was the first itersize record. the following are
# in a repeated loop.
yield first
while 1:
res = _cursor.fetchmany(self, self.itersize)
if not res:
return
for r in res:
yield r
yield res.next()
class DictConnection(_connection):
@ -318,14 +310,17 @@ class NamedTupleCursor(_cursor):
return [nt(*t) for t in ts]
def __iter__(self):
# Invoking _cursor.__iter__(self) goes to infinite recursion,
# so we do pagination by hand
it = _cursor.__iter__(self)
t = it.next()
nt = self.Record
if nt is None:
nt = self.Record = self._make_nt()
yield nt(*t)
while 1:
recs = self.fetchmany(self.itersize)
if not recs:
return
for rec in recs:
yield rec
yield nt(*it.next())
try:
from collections import namedtuple
@ -445,7 +440,7 @@ class UUID_adapter(object):
"""Adapt Python's uuid.UUID__ type to PostgreSQL's uuid__.
.. __: 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):
@ -460,31 +455,29 @@ class UUID_adapter(object):
__str__ = getquoted
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
if not oids:
oid1 = 2950
oid2 = 2951
elif type(oids) == list:
elif isinstance(oids, (list, tuple)):
oid1, oid2 = oids
else:
oid1 = oids
oid2 = 2951
def parseUUIDARRAY(data, cursor):
if data is None:
return None
elif data == '{}':
return []
else:
return [((len(x) > 0 and x != 'NULL') and uuid.UUID(x) or None)
for x in data[1:-1].split(',')]
_ext.UUID = _ext.new_type((oid1, ), "UUID",
lambda data, cursor: data and uuid.UUID(data) or None)
_ext.UUIDARRAY = _ext.new_type((oid2,), "UUID[]", parseUUIDARRAY)
_ext.UUIDARRAY = _ext.new_array_type((oid2,), "UUID[]", _ext.UUID)
_ext.register_type(_ext.UUID, conn_or_curs)
_ext.register_type(_ext.UUIDARRAY, conn_or_curs)
@ -505,13 +498,13 @@ class Inet(object):
"""
def __init__(self, addr):
self.addr = addr
def __repr__(self):
return "%s(%r)" % (self.__class__.__name__, self.addr)
def prepare(self, conn):
self._conn = conn
def getquoted(self):
obj = _A(self.addr)
if hasattr(obj, 'prepare'):
@ -524,13 +517,32 @@ class Inet(object):
def __str__(self):
return str(self.addr)
def register_inet(oid=None, conn_or_curs=None):
"""Create the INET type and an Inet adapter."""
if not oid: oid = 869
_ext.INET = _ext.new_type((oid, ), "INET",
"""Create the INET type and an Inet adapter.
:param oid: oid for the PostgreSQL :sql:`inet` type, or 2-items sequence
with oids of the type and the array. If not specified, use PostgreSQL
standard oids.
:param conn_or_curs: where to register the typecaster. If not specified,
register it globally.
"""
if not oid:
oid1 = 869
oid2 = 1041
elif isinstance(oid, (list, tuple)):
oid1, oid2 = oid
else:
oid1 = oid
oid2 = 1041
_ext.INET = _ext.new_type((oid1, ), "INET",
lambda data, cursor: data and Inet(data) or None)
_ext.INETARRAY = _ext.new_array_type((oid2, ), "INETARRAY", _ext.INET)
_ext.register_type(_ext.INET, conn_or_curs)
_ext.register_type(_ext.INETARRAY, conn_or_curs)
return _ext.INET
@ -849,9 +861,9 @@ class CompositeCaster(object):
return self._ctor(*attrs)
_re_tokenize = regex.compile(r"""
\(? ([,\)]) # an empty token, representing NULL
\(? ([,)]) # an empty token, representing NULL
| \(? " ((?: [^"] | "")*) " [,)] # or a quoted string
| \(? ([^",\)]+) [,\)] # or an unquoted string
| \(? ([^",)]+) [,)] # or an unquoted string
""", regex.VERBOSE)
_re_undouble = regex.compile(r'(["\\])\1')
@ -861,7 +873,7 @@ class CompositeCaster(object):
rv = []
for m in self._re_tokenize.finditer(s):
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):
rv.append(None)
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
and a default name in the form ``sHH:MM`` (``s`` is the sign.).
The implementation also caches instances. During creation, if a
FixedOffsetTimezone instance has previously been created with the same
offset and name that instance will be returned. This saves memory and
improves comparability.
.. __: http://docs.python.org/library/datetime.html#datetime-tzinfo
"""
_name = None
_offset = ZERO
_cache = {}
def __init__(self, offset=None, name=None):
if offset is not None:
self._offset = datetime.timedelta(minutes = offset)
if name is not None:
self._name = name
def __new__(cls, offset=None, name=None):
"""Return a suitable instance created earlier if it exists
"""
key = (offset, name)
try:
return cls._cache[key]
except KeyError:
tz = datetime.tzinfo.__new__(cls, offset, name)
tz.__init__(offset, name)
cls._cache[key] = tz
return tz
def __repr__(self):
offset_mins = self._offset.seconds // 60 + self._offset.days * 24 * 60
return "psycopg2.tz.FixedOffsetTimezone(offset=%r, name=%r)" \
% (self._offset.seconds // 60, self._name)
% (offset_mins, self._name)
def utcoffset(self, dt):
return self._offset

View File

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

View File

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

View File

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

View File

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

View File

@ -86,7 +86,7 @@
/* Helpers for formatstring */
Py_LOCAL_INLINE(PyObject *)
BORROWED Py_LOCAL_INLINE(PyObject *)
getnextarg(PyObject *args, Py_ssize_t arglen, Py_ssize_t *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))
#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) */

View File

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

View File

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

View File

@ -122,8 +122,6 @@ psyco_conn_cursor(connectionObject *self, PyObject *args, PyObject *keywds)
static PyObject *
psyco_conn_close(connectionObject *self, PyObject *args)
{
EXC_IF_CONN_CLOSED(self);
Dprintf("psyco_conn_close: closing connection at %p", self);
conn_close(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 (0 != conn_set_session(self,
if (0 > conn_set_session(self,
c_isolevel, c_readonly, c_deferrable, c_autocommit)) {
return NULL;
}
@ -550,7 +548,7 @@ psyco_conn_autocommit_get(connectionObject *self)
return ret;
}
static PyObject *
BORROWED static PyObject *
_psyco_conn_autocommit_set_checks(connectionObject *self)
{
/* 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 (conn_set_client_encoding(self, enc) == 0) {
if (conn_set_client_encoding(self, enc) >= 0) {
Py_INCREF(Py_None);
rv = Py_None;
}
@ -701,8 +699,8 @@ psyco_conn_get_parameter_status(connectionObject *self, PyObject *args)
static PyObject *
psyco_conn_lobject(connectionObject *self, PyObject *args, PyObject *keywds)
{
Oid oid=InvalidOid, new_oid=InvalidOid;
char *new_file = NULL;
int oid = (int)InvalidOid, new_oid = (int)InvalidOid;
const char *new_file = NULL;
const char *smode = "";
PyObject *factory = (PyObject *)&lobjectType;
PyObject *obj;
@ -754,7 +752,7 @@ psyco_conn_lobject(connectionObject *self, PyObject *args, PyObject *keywds)
"get_backend_pid() -- Get backend process id."
static PyObject *
psyco_conn_get_backend_pid(connectionObject *self)
psyco_conn_get_backend_pid(connectionObject *self, PyObject *args)
{
EXC_IF_CONN_CLOSED(self);
@ -767,7 +765,7 @@ psyco_conn_get_backend_pid(connectionObject *self)
"reset() -- Reset current connection to defaults."
static PyObject *
psyco_conn_reset(connectionObject *self)
psyco_conn_reset(connectionObject *self, PyObject *args)
{
int res;
@ -795,7 +793,7 @@ psyco_conn_get_exception(PyObject *self, void *closure)
}
static PyObject *
psyco_conn_poll(connectionObject *self)
psyco_conn_poll(connectionObject *self, PyObject *args)
{
int res;
@ -817,7 +815,7 @@ psyco_conn_poll(connectionObject *self)
"fileno() -> int -- Return file descriptor associated to database connection."
static PyObject *
psyco_conn_fileno(connectionObject *self)
psyco_conn_fileno(connectionObject *self, PyObject *args)
{
long int socket;
@ -836,7 +834,7 @@ psyco_conn_fileno(connectionObject *self)
"executing an asynchronous operation."
static PyObject *
psyco_conn_isexecuting(connectionObject *self)
psyco_conn_isexecuting(connectionObject *self, PyObject *args)
{
/* synchronous connections will always return False */
if (self->async == 0) {
@ -868,7 +866,7 @@ psyco_conn_isexecuting(connectionObject *self)
"cancel() -- cancel the current operation"
static PyObject *
psyco_conn_cancel(connectionObject *self)
psyco_conn_cancel(connectionObject *self, PyObject *args)
{
char errbuf[256];

View File

@ -85,7 +85,7 @@ struct cursorObject {
/* 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);
/* exception-raising macros */

View File

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

View File

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

View File

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

View File

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

View File

@ -373,7 +373,7 @@ lobject_dealloc(PyObject* obj)
static int
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 *new_file = NULL;
PyObject *conn;
@ -383,7 +383,7 @@ lobject_init(PyObject *obj, PyObject *args, PyObject *kwds)
return -1;
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 *

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
microprotocols_add(PyTypeObject *type, PyObject *proto, PyObject *cast)
{
PyObject *key;
PyObject *key = NULL;
int rv = -1;
if (proto == NULL) proto = (PyObject*)&isqlquoteType;
Dprintf("microprotocols_add: cast %p for (%s, ?)", cast, type->tp_name);
key = PyTuple_Pack(2, (PyObject*)type, proto);
PyDict_SetItem(psyco_adapters, key, cast);
Py_DECREF(key);
if (!(key = PyTuple_Pack(2, (PyObject*)type, proto))) { goto exit; }
if (0 != PyDict_SetItem(psyco_adapters, key, cast)) { goto exit; }
return 0;
rv = 0;
exit:
Py_XDECREF(key);
return rv;
}
/* Check if one of `obj` superclasses has an adapter for `proto`.
*
* If it does, return a *borrowed reference* to the adapter, else 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)
{
PyTypeObject *type;
@ -89,14 +95,14 @@ _get_superclass_adapter(PyObject *obj, PyObject *proto)
#endif
type->tp_mro)) {
/* has no mro */
return NULL;
return Py_None;
}
/* Walk the mro from the most specific subclass. */
mro = type->tp_mro;
for (i = 1, ii = PyTuple_GET_SIZE(mro); i < ii; ++i) {
st = PyTuple_GET_ITEM(mro, i);
key = PyTuple_Pack(2, st, proto);
if (!(key = PyTuple_Pack(2, st, proto))) { return NULL; }
adapter = PyDict_GetItem(psyco_adapters, key);
Py_DECREF(key);
@ -119,7 +125,7 @@ _get_superclass_adapter(PyObject *obj, PyObject *proto)
return adapter;
}
}
return NULL;
return Py_None;
}
@ -139,7 +145,7 @@ microprotocols_adapt(PyObject *obj, PyObject *proto, PyObject *alt)
Py_TYPE(obj)->tp_name);
/* 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);
Py_DECREF(key);
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. */
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);
return adapted;
}

View File

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

View File

@ -35,18 +35,18 @@
/* exported functions */
HIDDEN PGresult *pq_get_last_result(connectionObject *conn);
HIDDEN int pq_fetch(cursorObject *curs);
HIDDEN int pq_execute(cursorObject *curs, const char *query, int async);
RAISES_NEG HIDDEN int pq_fetch(cursorObject *curs);
RAISES_NEG HIDDEN int pq_execute(cursorObject *curs, const char *query, int async);
HIDDEN int pq_send_query(connectionObject *conn, const char *query);
HIDDEN int pq_begin_locked(connectionObject *conn, PGresult **pgres,
char **error, PyThreadState **tstate);
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);
HIDDEN int pq_abort(connectionObject *conn);
RAISES_NEG HIDDEN int pq_abort(connectionObject *conn);
HIDDEN int pq_reset_locked(connectionObject *conn, PGresult **pgres,
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,
PGresult **pgres,
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_flush(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);
@ -69,7 +69,7 @@ HIDDEN int pq_execute_command_locked(connectionObject *conn,
const char *query,
PGresult **pgres, char **error,
PyThreadState **tstate);
HIDDEN void pq_complete_error(connectionObject *conn, PGresult **pgres,
RAISES HIDDEN void pq_complete_error(connectionObject *conn, PGresult **pgres,
char **error);
#endif /* !defined(PSYCOPG_PQPATH_H) */

View File

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

View File

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

View File

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

View File

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

View File

@ -65,7 +65,7 @@ typecast_FLOAT_cast(const char *s, Py_ssize_t len, PyObject *curs)
PyObject *str = NULL, *flo = NULL;
if (s == NULL) {Py_INCREF(Py_None); return Py_None;}
str = Text_FromUTF8AndSize(s, len);
if (!(str = Text_FromUTF8AndSize(s, len))) { return NULL; }
#if PY_MAJOR_VERSION < 3
flo = PyFloat_FromString(str, NULL);
#else

View File

@ -208,7 +208,7 @@ static const char hex_lut[128] = {
/* Parse a bytea output buffer encoded in 'hex' format.
*
* 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'.
* 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.
*
* 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'.
* Return a new buffer allocated by PyMem_Malloc and set 'sizeout' to its size.

View File

@ -26,7 +26,7 @@
#include <math.h>
#include "datetime.h"
static int
RAISES_NEG static int
psyco_typecast_datetime_init(void)
{
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 '5': case '6': case '7': case '8': case '9':
v = v*10 + (double)*str - (double)'0';
v = v * 10.0 + (double)(*str - '0');
if (part == 6){
denominator *= 10;
}

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 '5': case '6': case '7': case '8': case '9':
v = v*10 + (double)*str - (double)'0';
v = v * 10.0 + (double)(*str - '0');
Dprintf("typecast_MXINTERVAL_cast: v = %f", v);
if (part == 6){
denominator *= 10;

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.
* '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 *
psycopg_strdup(const char *from, Py_ssize_t len)
RAISES_NEG int
psycopg_strdup(char **to, const char *from, Py_ssize_t len)
{
char *rv;
if (!len) { len = strlen(from); }
if (!(rv = PyMem_Malloc(len + 1))) {
if (!(*to = PyMem_Malloc(len + 1))) {
PyErr_NoMemory();
return NULL;
return -1;
}
strcpy(rv, from);
return rv;
strcpy(*to, from);
return 0;
}
/* 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.
*/
PyObject *
STEALS(1) PyObject *
psycopg_ensure_bytes(PyObject *obj)
{
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
* return value. It is safe to call it on NULL.
*/
PyObject *
STEALS(1) PyObject *
psycopg_ensure_text(PyObject *obj)
{
#if PY_MAJOR_VERSION < 3

View File

@ -78,7 +78,9 @@ static PyMemberDef xid_members[] = {
static PyObject *
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);
self->format_id = Py_None;
@ -486,7 +488,7 @@ exit:
*
* Return a borrowed reference. */
static PyObject *
BORROWED static PyObject *
_xid_get_parse_regex(void) {
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_NEW_BOOLEAN to format booleans as true/false vs 't'/'f'
# Set to 1 to use Python datatime objects for default date/time representation.
# Set to 1 to use Python datetime objects for default date/time representation.
use_pydatetime=1
# If the build system does not find the mx.DateTime headers, try
# If the build system does not find the mx.DateTime headers, try
# uncommenting the following line and setting its value to the right path.
#mx_include_dir=
@ -28,14 +28,14 @@ have_ssl=0
# set it to the pg_config full path.
#pg_config=
# If "pg_config" is not available, "include_dirs" can be used to locate
# If "pg_config" is not available, "include_dirs" can be used to locate
# postgresql headers and libraries. Some extra checks on sys.platform will
# still be done in setup.py.
# The next line is the default as used on psycopg author Debian laptop:
#include_dirs=/usr/include/postgresql:/usr/include/postgresql/server
# Uncomment next line on Mandrake 10.x (and comment previous ones):
#include_dirs=/usr/include/pgsql/8.0:/usr/include/pgsql/8.0/server
#include_dirs=/usr/include/pgsql/8.0:/usr/include/pgsql/8.0/server
# Uncomment next line on SUSE 9.3 (and comment previous ones):
#include_dirs=/usr/include/pgsql:/usr/include/pgsql/server

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -25,13 +25,12 @@
import os
import shutil
import tempfile
from testutils import unittest, decorate_all_tests, skip_if_tpc_disabled
import psycopg2
import psycopg2.extensions
from psycopg2.extensions import b
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_(self):

View File

@ -22,7 +22,8 @@
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
# License for more details.
from testutils import unittest
from testutils import unittest, skip_before_python
from testconfig import dsn
import psycopg2
@ -127,6 +128,40 @@ class ConnectTestCase(unittest.TestCase):
self.assertEqual(self.args[0], r"dbname='\\every thing\''")
class ExceptionsTestCase(unittest.TestCase):
def setUp(self):
self.conn = psycopg2.connect(dsn)
def tearDown(self):
self.conn.close()
def test_attributes(self):
cur = self.conn.cursor()
try:
cur.execute("select * from nonexist")
except psycopg2.Error, exc:
e = exc
self.assertEqual(e.pgcode, '42P01')
self.assert_(e.pgerror)
self.assert_(e.cursor is cur)
@skip_before_python(2, 5)
def test_pickle(self):
import pickle
cur = self.conn.cursor()
try:
cur.execute("select * from nonexist")
except psycopg2.Error, exc:
e = exc
e1 = pickle.loads(pickle.dumps(e))
self.assertEqual(e.pgerror, e1.pgerror)
self.assertEqual(e.pgcode, e1.pgcode)
self.assert_(e1.cursor is None)
def test_suite():
return unittest.TestLoader().loadTestsFromName(__name__)

View File

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

View File

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