mirror of
https://github.com/psycopg/psycopg2.git
synced 2025-01-31 17:34:08 +03:00
Merge remote-tracking branch 'piro/devel' into devel
Conflicts: psycopg/lobject_int.c
This commit is contained in:
commit
550130b19e
2
INSTALL
2
INSTALL
|
@ -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
NEWS
16
NEWS
|
@ -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
|
||||
|
|
|
@ -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::
|
||||
|
||||
|
|
|
@ -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/
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,11 +117,6 @@ available through the following exceptions:
|
|||
if not available. The `~psycopg2.errorcodes` module contains
|
||||
symbolic constants representing PostgreSQL error codes.
|
||||
|
||||
.. extension::
|
||||
|
||||
The `~Error.pgerror` and `~Error.pgcode` attributes are
|
||||
Psycopg extensions.
|
||||
|
||||
.. doctest::
|
||||
:options: +NORMALIZE_WHITESPACE
|
||||
|
||||
|
@ -136,10 +131,14 @@ available through the following exceptions:
|
|||
ERROR: relation "barf" does not exist
|
||||
LINE 1: SELECT * FROM barf
|
||||
^
|
||||
.. attribute:: cursor
|
||||
|
||||
.. versionchanged:: 2.0.7 added `Error.pgerror` and
|
||||
`Error.pgcode` attributes.
|
||||
The cursor the exception was raised from; `None` if not applicable.
|
||||
|
||||
.. extension::
|
||||
|
||||
The `~Error.pgerror`, `~Error.pgcode`, and `~Error.cursor` attributes
|
||||
are Psycopg extensions.
|
||||
|
||||
|
||||
.. exception:: InterfaceError
|
||||
|
|
|
@ -295,8 +295,8 @@ the SQL string that would be sent to the database.
|
|||
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
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -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={}):
|
||||
|
|
|
@ -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)
|
||||
|
@ -526,11 +519,30 @@ class Inet(object):
|
|||
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):
|
||||
|
|
22
lib/tz.py
22
lib/tz.py
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
self->conn = conn;
|
||||
Py_INCREF(self->conn);
|
||||
}
|
||||
|
||||
Py_INCREF(Py_None);
|
||||
return Py_None;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
self->conn = conn;
|
||||
|
||||
Py_INCREF(Py_None);
|
||||
return Py_None;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) */
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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) */
|
||||
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;
|
||||
|
||||
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;
|
||||
|
|
|
@ -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];
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -38,7 +38,7 @@
|
|||
* Return a borrowed reference.
|
||||
*/
|
||||
|
||||
PyObject *
|
||||
BORROWED PyObject *
|
||||
curs_get_cast(cursorObject *self, PyObject *oid)
|
||||
{
|
||||
PyObject *cast;
|
||||
|
|
|
@ -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,22 +432,17 @@ _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:
|
||||
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);
|
||||
|
||||
return res;
|
||||
|
@ -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,10 +686,11 @@ _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)
|
||||
|
@ -696,45 +701,44 @@ _psyco_curs_buildrow_fill(cursorObject *self, PyObject *res,
|
|||
else {
|
||||
err = PySequence_SetItem(res, i, val);
|
||||
Py_DECREF(val);
|
||||
if (err == -1) {
|
||||
Py_DECREF(res);
|
||||
res = NULL;
|
||||
break;
|
||||
if (err == -1) { goto exit; }
|
||||
}
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
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, ¶meters
|
||||
))
|
||||
{ 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);
|
||||
}
|
||||
|
|
|
@ -51,19 +51,19 @@ typedef struct {
|
|||
|
||||
/* functions exported from lobject_int.c */
|
||||
|
||||
HIDDEN int lobject_open(lobjectObject *self, connectionObject *conn,
|
||||
RAISES_NEG HIDDEN int lobject_open(lobjectObject *self, connectionObject *conn,
|
||||
Oid oid, const char *smode, Oid new_oid,
|
||||
const char *new_file);
|
||||
HIDDEN int lobject_unlink(lobjectObject *self);
|
||||
HIDDEN int lobject_export(lobjectObject *self, const char *filename);
|
||||
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)
|
||||
|
|
|
@ -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
|
||||
else {
|
||||
/* Use lo_creat when possible to be more middleware-friendly.
|
||||
See ticket #88. */
|
||||
if (new_oid != InvalidOid)
|
||||
self->oid = lo_create(self->conn->pgconn, new_oid);
|
||||
else
|
||||
self->oid = lo_creat(self->conn->pgconn, INV_READ | INV_WRITE);
|
||||
}
|
||||
|
||||
Dprintf("lobject_open: large object created with oid = %d",
|
||||
self->oid);
|
||||
|
@ -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;
|
||||
|
|
|
@ -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 *
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -267,8 +269,10 @@ pq_resolve_critical(connectionObject *conn, int close)
|
|||
|
||||
/* remember to clear the critical! */
|
||||
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");
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
Py_BEGIN_ALLOW_THREADS;
|
||||
pthread_mutex_lock(&conn->lock);
|
||||
else {
|
||||
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;
|
||||
|
|
|
@ -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) */
|
||||
|
|
|
@ -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."
|
||||
|
|
|
@ -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,16 +627,18 @@ psyco_set_error(PyObject *exc, cursorObject *curs, const char *msg,
|
|||
}
|
||||
|
||||
if (pgerror) {
|
||||
t = conn_text_from_chars(conn, pgerror);
|
||||
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);
|
||||
if ((t = conn_text_from_chars(conn, pgcode))) {
|
||||
PyObject_SetAttrString(err, "pgcode", t);
|
||||
Py_DECREF(t);
|
||||
}
|
||||
}
|
||||
|
||||
PyErr_SetObject(exc, err);
|
||||
Py_DECREF(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 */
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ 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
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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):
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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__)
|
||||
|
||||
|
|
|
@ -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__)
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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__)
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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")
|
||||
|
|
Loading…
Reference in New Issue
Block a user