Merge branch 'master' into feature-expose-pgconn

This commit is contained in:
Federico Di Gregorio 2018-11-07 14:15:29 +01:00 committed by GitHub
commit 01f8475f53
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
98 changed files with 3946 additions and 62539 deletions

View File

@ -12,7 +12,7 @@ environment:
matrix:
# For Python versions available on Appveyor, see
# http://www.appveyor.com/docs/installed-software#python
# https://www.appveyor.com/docs/build-environment/
- {PYVER: "27", PYTHON_ARCH: "32"}
- {PYVER: "27", PYTHON_ARCH: "64"}
- {PYVER: "34", PYTHON_ARCH: "32"}
@ -82,7 +82,7 @@ init:
- "%PYTHON%\\python -c \"import sys; print('64bit: ' + str(sys.maxsize > 2**32))\""
# Get & Install NASM
#- curl -L -o nasminst.exe http://www.nasm.us/pub/nasm/releasebuilds/2.12.02/win64/nasm-2.12.02-installer-x64.exe && start /wait nasminst.exe /S
#- curl -L -o nasminst.exe https://www.nasm.us/pub/nasm/releasebuilds/2.12.02/win64/nasm-2.12.02-installer-x64.exe && start /wait nasminst.exe /S
#- SET PATH="C:\Program Files (x86)\nasm;%PATH%"
# Fix problem with VS2008 Express and 64bit builds

View File

@ -25,7 +25,7 @@ statement from all source files in the program, then also delete it here.
You should have received a copy of the GNU Lesser General Public License
along with psycopg2 (see the doc/ directory.)
If not, see <http://www.gnu.org/licenses/>.
If not, see <https://www.gnu.org/licenses/>.
Alternative licenses

28
NEWS
View File

@ -6,18 +6,31 @@ What's new in psycopg 2.8
New features:
- Added `~psycopg2.errors` module. Every PostgreSQL error is converted into
a specific exception class (:ticket:`#682`).
- Added `~psycopg2.extensions.encrypt_password()` function (:ticket:`#576`).
- Added `~psycopg2.extensions.Column.table_oid` and
`~psycopg2.extensions.Column.table_column` attributes on `cursor.description`
items (:ticket:`#661`).
- Added `connection.info` object to retrieve various PostgreSQL connection
information (:ticket:`#726`).
- `~psycopg2.sql.Identifier` can represent qualified names in SQL composition
(:ticket:`#732`).
- `!str()` on `~psycopg2.extras.Range` produces a human-readable representation
(:ticket:`#773`).
- `~psycopg2.extras.DictCursor` and `~psycopg2.extras.RealDictCursor` rows
maintain columns order (:ticket:`#177`).
- Added `!severity_nonlocalized` attribute on the
`~psycopg2.extensions.Diagnostics` object (:ticket:`#783`).
Other changes:
- Dropped support for Python 2.6, 3.2, 3.3.
- Dropped `psycopg1` module.
- Dropped deprecated ``register_tstz_w_secs()`` (was previously a no-op).
- Dropped deprecated `!register_tstz_w_secs()` (was previously a no-op).
- No longer use 2to3 during installation for Python 2 & 3 compatability. All
source files are now compatible with Python 2 & 3 as is.
- The ``psycopg2.test`` package is no longer installed by ``python setup.py
- The `!psycopg2.test` package is no longer installed by ``python setup.py
install``.
@ -26,7 +39,14 @@ What's new in psycopg 2.7.6
- Close named cursors if exist, even if `~cursor.execute()` wasn't called
(:ticket:`#746`).
- Fixed building on modern FreeBSD versions with Python 3.7 (:ticket:`#755`)
- Fixed building on modern FreeBSD versions with Python 3.7 (:ticket:`#755`).
- Fixed hang trying to :sql:`COPY` via `~cursor.execute()` (:ticket:`#781`).
- Fixed adaptation of arrays of empty arrays (:ticket:`#788`).
- Fixed segfault accessing the `connection.readonly` and
`connection.deferrable` repeatedly (:ticket:`#790`).
- `~psycopg2.extras.execute_values()` accepts `~psycopg2.sql.Composable`
objects (#794).
- `~psycopg2.errorcodes` map updated to PostgreSQL 11.
What's new in psycopg 2.7.5
@ -357,7 +377,7 @@ Other changes:
- Dropped support for Python 2.4. Please use Psycopg 2.4.x if you need it.
- `~psycopg2.errorcodes` map updated to PostgreSQL 9.2.
- Dropped Zope adapter from source repository. ZPsycopgDA now has its own
project at <http://github.com/psycopg/ZPsycopgDA>.
project at <https://github.com/psycopg/ZPsycopgDA>.
What's new in psycopg 2.4.6

View File

@ -156,7 +156,7 @@ geometric type:
.. |point| replace:: :sql:`point`
.. _point: http://www.postgresql.org/docs/current/static/datatype-geometric.html#DATATYPE-GEOMETRIC
.. _point: https://www.postgresql.org/docs/current/static/datatype-geometric.html#DATATYPE-GEOMETRIC
The above function call results in the SQL command::
@ -259,9 +259,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/current/static/sql-listen.html
.. _LISTEN: https://www.postgresql.org/docs/current/static/sql-listen.html
.. |NOTIFY| replace:: :sql:`NOTIFY`
.. _NOTIFY: http://www.postgresql.org/docs/current/static/sql-notify.html
.. _NOTIFY: https://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
@ -375,7 +375,7 @@ completely non-blocking connection attempt: see the libpq documentation for
|PQconnectStart|_.
.. |PQconnectStart| replace:: `!PQconnectStart()`
.. _PQconnectStart: http://www.postgresql.org/docs/current/static/libpq-connect.html#LIBPQ-PQCONNECTSTARTPARAMS
.. _PQconnectStart: https://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
@ -484,14 +484,14 @@ psycopg2 scope, as the callback can be tied to the libraries' implementation
details. You can check the `psycogreen`_ project for further informations and
resources about the topic.
.. _coroutine: http://en.wikipedia.org/wiki/Coroutine
.. _coroutine: https://en.wikipedia.org/wiki/Coroutine
.. _greenlet: https://pypi.org/project/greenlet/
.. _green threads: http://en.wikipedia.org/wiki/Green_threads
.. _Eventlet: http://eventlet.net/
.. _green threads: https://en.wikipedia.org/wiki/Green_threads
.. _Eventlet: https://eventlet.net/
.. _gevent: http://www.gevent.org/
.. _SQLAlchemy: http://www.sqlalchemy.org/
.. _SQLAlchemy: https://www.sqlalchemy.org/
.. _psycogreen: http://bitbucket.org/dvarrazzo/psycogreen/
.. __: http://www.postgresql.org/docs/current/static/libpq-async.html
.. __: https://www.postgresql.org/docs/current/static/libpq-async.html
.. warning::
@ -536,7 +536,7 @@ Server version 9.4 adds a new feature called *Logical Replication*.
- PostgreSQL `Streaming Replication Protocol`__
.. __: http://www.postgresql.org/docs/current/static/protocol-replication.html
.. __: https://www.postgresql.org/docs/current/static/protocol-replication.html
Logical replication Quick-Start

View File

@ -116,12 +116,12 @@ todo_include_todos = False
rst_epilog = """
.. |DBAPI| replace:: DB API 2.0
.. _DBAPI: http://www.python.org/dev/peps/pep-0249/
.. _DBAPI: https://www.python.org/dev/peps/pep-0249/
.. _transaction isolation level:
http://www.postgresql.org/docs/current/static/transaction-iso.html
https://www.postgresql.org/docs/current/static/transaction-iso.html
.. _mx.DateTime: http://www.egenix.com/products/python/mxBase/mxDateTime/
.. _mx.DateTime: https://www.egenix.com/products/python/mxBase/mxDateTime/
.. |MVCC| replace:: :abbr:`MVCC (Multiversion concurrency control)`
"""

View File

@ -198,7 +198,7 @@ The ``connection`` class
.. seealso:: the |PREPARE TRANSACTION|_ PostgreSQL command.
.. |PREPARE TRANSACTION| replace:: :sql:`PREPARE TRANSACTION`
.. _PREPARE TRANSACTION: http://www.postgresql.org/docs/current/static/sql-prepare-transaction.html
.. _PREPARE TRANSACTION: https://www.postgresql.org/docs/current/static/sql-prepare-transaction.html
.. index::
@ -224,7 +224,7 @@ The ``connection`` class
.. seealso:: the |COMMIT PREPARED|_ PostgreSQL command.
.. |COMMIT PREPARED| replace:: :sql:`COMMIT PREPARED`
.. _COMMIT PREPARED: http://www.postgresql.org/docs/current/static/sql-commit-prepared.html
.. _COMMIT PREPARED: https://www.postgresql.org/docs/current/static/sql-commit-prepared.html
.. index::
@ -246,7 +246,7 @@ The ``connection`` class
.. seealso:: the |ROLLBACK PREPARED|_ PostgreSQL command.
.. |ROLLBACK PREPARED| replace:: :sql:`ROLLBACK PREPARED`
.. _ROLLBACK PREPARED: http://www.postgresql.org/docs/current/static/sql-rollback-prepared.html
.. _ROLLBACK PREPARED: https://www.postgresql.org/docs/current/static/sql-rollback-prepared.html
.. index::
@ -267,7 +267,7 @@ The ``connection`` class
transactions initiated by a program using such driver should be
unpacked correctly.
.. __: http://jdbc.postgresql.org/
.. __: https://jdbc.postgresql.org/
Xids returned by `!tpc_recover()` also have extra attributes
`~psycopg2.extensions.Xid.prepared`, `~psycopg2.extensions.Xid.owner`,
@ -277,7 +277,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/current/static/view-pg-prepared-xacts.html
.. _pg_prepared_xacts: https://www.postgresql.org/docs/current/static/view-pg-prepared-xacts.html
@ -309,7 +309,7 @@ The ``connection`` class
|PQcancel|_.
.. |PQcancel| replace:: `!PQcancel()`
.. _PQcancel: http://www.postgresql.org/docs/current/static/libpq-cancel.html#LIBPQ-PQCANCEL
.. _PQcancel: https://www.postgresql.org/docs/current/static/libpq-cancel.html#LIBPQ-PQCANCEL
.. versionadded:: 2.3
@ -325,10 +325,10 @@ The ``connection`` class
available for recover.
.. |RESET| replace:: :sql:`RESET`
.. _RESET: http://www.postgresql.org/docs/current/static/sql-reset.html
.. _RESET: https://www.postgresql.org/docs/current/static/sql-reset.html
.. |SET SESSION AUTHORIZATION| replace:: :sql:`SET SESSION AUTHORIZATION`
.. __: http://www.postgresql.org/docs/current/static/sql-set-session-authorization.html
.. __: https://www.postgresql.org/docs/current/static/sql-set-session-authorization.html
.. versionadded:: 2.0.12
@ -342,6 +342,9 @@ The ``connection`` class
obscured.
.. rubric:: Transaction control methods and attributes.
.. index::
pair: Transaction; Autocommit
pair: Transaction; Isolation level
@ -366,7 +369,7 @@ The ``connection`` class
`autocommit` attribute.
.. _isolation level:
http://www.postgresql.org/docs/current/static/transaction-iso.html
https://www.postgresql.org/docs/current/static/transaction-iso.html
Arguments set to `!None` (the default for all) will not be changed.
The parameters *isolation_level*, *readonly* and *deferrable* also
@ -376,11 +379,11 @@ The ``connection`` class
|default_transaction_read_only|__, |default_transaction_deferrable|__.
.. |default_transaction_isolation| replace:: :sql:`default_transaction_isolation`
.. __: http://www.postgresql.org/docs/current/static/runtime-config-client.html#GUC-DEFAULT-TRANSACTION-ISOLATION
.. __: https://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/current/static/runtime-config-client.html#GUC-DEFAULT-TRANSACTION-READ-ONLY
.. __: https://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/current/static/runtime-config-client.html#GUC-DEFAULT-TRANSACTION-DEFERRABLE
.. __: https://www.postgresql.org/docs/current/static/runtime-config-client.html#GUC-DEFAULT-TRANSACTION-DEFERRABLE
The function must be invoked with no transaction in progress.
@ -388,7 +391,7 @@ The ``connection`` class
of the transaction parameters in the server.
.. |SET TRANSACTION| replace:: :sql:`SET TRANSACTION`
.. _SET TRANSACTION: http://www.postgresql.org/docs/current/static/sql-set-transaction.html
.. _SET TRANSACTION: https://www.postgresql.org/docs/current/static/sql-set-transaction.html
.. versionadded:: 2.4.2
@ -534,7 +537,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/current/static/multibyte.html
.. __: https://www.postgresql.org/docs/current/static/multibyte.html
.. index::
@ -568,7 +571,7 @@ The ``connection`` class
configuration parameters`__ such as ``log_statement``,
``client_min_messages``, ``log_min_duration_statement`` etc.
.. __: http://www.postgresql.org/docs/current/static/runtime-config-logging.html
.. __: https://www.postgresql.org/docs/current/static/runtime-config-logging.html
.. attribute:: notifies
@ -600,42 +603,14 @@ The ``connection`` class
.. index::
pair: Backend; PID
pair: Connection; Info
.. method:: get_backend_pid()
.. attribute:: info
Returns the process ID (PID) of the backend server process handling
this connection.
A `~psycopg2.extensions.ConnectionInfo` object exposing information
about the native libpq connection.
Note that the PID belongs to a process executing on the database
server host, not the local host!
.. seealso:: libpq docs for `PQbackendPID()`__ for details.
.. __: http://www.postgresql.org/docs/current/static/libpq-status.html#LIBPQ-PQBACKENDPID
.. versionadded:: 2.0.8
.. index::
pair: Server; Parameters
.. method:: get_parameter_status(parameter)
Look up a current parameter setting of the server.
Potential values for ``parameter`` are: ``server_version``,
``server_encoding``, ``client_encoding``, ``is_superuser``,
``session_authorization``, ``DateStyle``, ``TimeZone``,
``integer_datetimes``, and ``standard_conforming_strings``.
If server did not report requested parameter, return `!None`.
.. seealso:: libpq docs for `PQparameterStatus()`__ for details.
.. __: http://www.postgresql.org/docs/current/static/libpq-status.html#LIBPQ-PQPARAMETERSTATUS
.. versionadded:: 2.0.12
.. versionadded:: 2.8.0
.. index::
@ -656,61 +631,11 @@ The ``connection`` class
.. seealso:: libpq docs for `PQconninfo()`__ for details.
.. __: http://www.postgresql.org/docs/current/static/libpq-connect.html#LIBPQ-PQCONNINFO
.. __: https://www.postgresql.org/docs/current/static/libpq-connect.html#LIBPQ-PQCONNINFO
.. versionadded:: 2.7
.. index::
pair: Transaction; Status
.. method:: get_transaction_status()
Return the current session transaction status as an integer. Symbolic
constants for the values are defined in the module
`psycopg2.extensions`: see :ref:`transaction-status-constants`
for the available values.
.. seealso:: libpq docs for `PQtransactionStatus()`__ for details.
.. __: http://www.postgresql.org/docs/current/static/libpq-status.html#LIBPQ-PQTRANSACTIONSTATUS
.. index::
pair: Protocol; Version
.. attribute:: protocol_version
A read-only integer representing frontend/backend protocol being used.
Currently Psycopg supports only protocol 3, which allows connection
to PostgreSQL server from version 7.4. Psycopg versions previous than
2.3 support both protocols 2 and 3.
.. seealso:: libpq docs for `PQprotocolVersion()`__ for details.
.. __: http://www.postgresql.org/docs/current/static/libpq-status.html#LIBPQ-PQPROTOCOLVERSION
.. versionadded:: 2.0.12
.. index::
pair: Server; Version
.. attribute:: server_version
A read-only integer representing the backend version.
The number is formed by converting the major, minor, and revision
numbers into two-decimal-digit numbers and appending them together.
For example, version 8.1.5 will be returned as ``80105``.
.. seealso:: libpq docs for `PQserverVersion()`__ for details.
.. __: http://www.postgresql.org/docs/current/static/libpq-status.html#LIBPQ-PQSERVERVERSION
.. versionadded:: 2.0.12
.. index::
pair: Connection; Status
@ -743,7 +668,7 @@ The ``connection`` class
`~psycopg2.extensions.lobject` to be instantiated.
.. |lo_import| replace:: `!lo_import()`
.. _lo_import: http://www.postgresql.org/docs/current/static/lo-interfaces.html#LO-IMPORT
.. _lo_import: https://www.postgresql.org/docs/current/static/lo-interfaces.html#LO-IMPORT
Available values for *mode* are:
@ -828,6 +753,119 @@ The ``connection`` class
.. versionadded:: 2.8
.. rubric:: informative methods of the native connection
.. note::
These methods are better accessed using the `~connection.info`
attributes and may be dropped in future versions.
.. index::
pair: Transaction; Status
.. method:: get_transaction_status()
Also available as `~connection.info`\ `!.`\
`~psycopg2.extensions.ConnectionInfo.transaction_status`.
Return the current session transaction status as an integer. Symbolic
constants for the values are defined in the module
`psycopg2.extensions`: see :ref:`transaction-status-constants`
for the available values.
.. seealso:: libpq docs for `PQtransactionStatus()`__ for details.
.. __: https://www.postgresql.org/docs/current/static/libpq-status.html#LIBPQ-PQTRANSACTIONSTATUS
.. index::
pair: Protocol; Version
.. attribute:: protocol_version
Also available as `~connection.info`\ `!.`\
`~psycopg2.extensions.ConnectionInfo.protocol_version`.
A read-only integer representing frontend/backend protocol being used.
Currently Psycopg supports only protocol 3, which allows connection
to PostgreSQL server from version 7.4. Psycopg versions previous than
2.3 support both protocols 2 and 3.
.. seealso:: libpq docs for `PQprotocolVersion()`__ for details.
.. __: https://www.postgresql.org/docs/current/static/libpq-status.html#LIBPQ-PQPROTOCOLVERSION
.. versionadded:: 2.0.12
.. index::
pair: Server; Version
.. attribute:: server_version
Also available as `~connection.info`\ `!.`\
`~psycopg2.extensions.ConnectionInfo.server_version`.
A read-only integer representing the backend version.
The number is formed by converting the major, minor, and revision
numbers into two-decimal-digit numbers and appending them together.
For example, version 8.1.5 will be returned as ``80105``.
.. seealso:: libpq docs for `PQserverVersion()`__ for details.
.. __: https://www.postgresql.org/docs/current/static/libpq-status.html#LIBPQ-PQSERVERVERSION
.. versionadded:: 2.0.12
.. index::
pair: Backend; PID
.. method:: get_backend_pid()
Also available as `~connection.info`\ `!.`\
`~psycopg2.extensions.ConnectionInfo.backend_pid`.
Returns the process ID (PID) of the backend server process handling
this connection.
Note that the PID belongs to a process executing on the database
server host, not the local host!
.. seealso:: libpq docs for `PQbackendPID()`__ for details.
.. __: https://www.postgresql.org/docs/current/static/libpq-status.html#LIBPQ-PQBACKENDPID
.. versionadded:: 2.0.8
.. index::
pair: Server; Parameters
.. method:: get_parameter_status(parameter)
Also available as `~connection.info`\ `!.`\
`~psycopg2.extensions.ConnectionInfo.parameter_status()`.
Look up a current parameter setting of the server.
Potential values for ``parameter`` are: ``server_version``,
``server_encoding``, ``client_encoding``, ``is_superuser``,
``session_authorization``, ``DateStyle``, ``TimeZone``,
``integer_datetimes``, and ``standard_conforming_strings``.
If server did not report requested parameter, return `!None`.
.. seealso:: libpq docs for `PQparameterStatus()`__ for details.
.. __: https://www.postgresql.org/docs/current/static/libpq-status.html#LIBPQ-PQPARAMETERSTATUS
.. versionadded:: 2.0.12
.. testcode::
:hide:

View File

@ -37,46 +37,49 @@ The ``cursor`` class
.. attribute:: description
This read-only attribute is a sequence of 7-item sequences.
Read-only attribute describing the result of a query. It is a
sequence of `~psycopg2.extensions.Column` instances, each one
describing one result column in order. The attribute is `!None` for
operations that do not return rows or if the cursor has not had an
operation invoked via the |execute*|_ methods yet.
Each of these sequences is a named tuple (a regular tuple if
:func:`collections.namedtuple` is not available) containing information
describing one result column:
For compatibility with the DB-API, every object can be unpacked as a
7-items sequence: the attributes retuned this way are the following.
For further details and other attributes available check the
`~psycopg2.extensions.Column` documentation.
0. `!name`: the name of the column returned.
1. `!type_code`: the PostgreSQL OID of the column. You can use the
|pg_type|_ system table to get more informations about the type.
This is the value used by Psycopg to decide what Python type use
to represent the value. See also
:ref:`type-casting-from-sql-to-python`.
2. `!display_size`: the actual length of the column in bytes.
Obtaining this value is computationally intensive, so it is
always `!None` unless the :envvar:`PSYCOPG_DISPLAY_SIZE` parameter
is set at compile time. See also PQgetlength_.
3. `!internal_size`: the size in bytes of the column associated to
this column on the server. Set to a negative value for
variable-size types See also PQfsize_.
4. `!precision`: total number of significant digits in columns of
type |NUMERIC|_. `!None` for other types.
5. `!scale`: count of decimal digits in the fractional part in
columns of type |NUMERIC|. `!None` for other types.
6. `!null_ok`: always `!None` as not easy to retrieve from the libpq.
0. `~psycopg2.extensions.Column.name`: the name of the column returned.
This attribute will be `!None` for operations that do not return rows
or if the cursor has not had an operation invoked via the
|execute*|_ methods yet.
1. `~psycopg2.extensions.Column.type_code`: the PostgreSQL OID of the
column.
.. |pg_type| replace:: :sql:`pg_type`
.. _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`
2. `~psycopg2.extensions.Column.display_size`: the actual length of
the column in bytes.
3. `~psycopg2.extensions.Column.internal_size`: the size in bytes of
the column associated to this column on the server.
4. `~psycopg2.extensions.Column.precision`: total number of
significant digits in columns of type |NUMERIC|. `!None`
for other types.
5. `~psycopg2.extensions.Column.scale`: count of decimal digits in
the fractional part in columns of type |NUMERIC|. `!None`
for other types.
6. `~psycopg2.extensions.Column.null_ok`: always `!None` as not easy
to retrieve from the libpq.
.. versionchanged:: 2.4
if possible, columns descriptions are named tuple instead of
regular tuples.
.. versionchanged:: 2.8
columns descriptions are instances of `!Column`, exposing extra
attributes.
.. |NUMERIC| replace:: :sql:`NUMERIC`
.. method:: close()
Close the cursor now (rather than whenever `del` is executed).
@ -129,7 +132,7 @@ The ``cursor`` class
backward scroll (see the |declare-notes|__).
.. |declare-notes| replace:: :sql:`DECLARE` notes
.. __: http://www.postgresql.org/docs/current/static/sql-declare.html#SQL-DECLARE-NOTES
.. __: https://www.postgresql.org/docs/current/static/sql-declare.html#SQL-DECLARE-NOTES
.. note::
@ -430,10 +433,10 @@ The ``cursor`` class
more flexibility.
.. |CREATE-TABLE| replace:: :sql:`CREATE TABLE`
.. __: http://www.postgresql.org/docs/current/static/sql-createtable.html
.. __: https://www.postgresql.org/docs/current/static/sql-createtable.html
.. |INSERT-RETURNING| replace:: :sql:`INSERT ... RETURNING`
.. __: http://www.postgresql.org/docs/current/static/sql-insert.html
.. __: https://www.postgresql.org/docs/current/static/sql-insert.html
.. attribute:: query
@ -620,7 +623,7 @@ The ``cursor`` class
...
.. |COPY| replace:: :sql:`COPY`
.. __: http://www.postgresql.org/docs/current/static/sql-copy.html
.. __: https://www.postgresql.org/docs/current/static/sql-copy.html
.. versionadded:: 2.0.6

View File

@ -39,7 +39,7 @@ From PostgreSQL documentation:
.. seealso:: `PostgreSQL Error Codes table`__
.. __: http://www.postgresql.org/docs/current/static/errcodes-appendix.html#ERRCODES-TABLE
.. __: https://www.postgresql.org/docs/current/static/errcodes-appendix.html#ERRCODES-TABLE
An example of the available constants defined in the module:
@ -50,7 +50,7 @@ An example of the available constants defined in the module:
'42P01'
Constants representing all the error values defined by PostgreSQL versions
between 8.1 and 10 are included in the module.
between 8.1 and 11 are included in the module.
.. autofunction:: lookup(code)

60
doc/src/errors.rst Normal file
View File

@ -0,0 +1,60 @@
`psycopg2.errors` -- Exception classes mapping PostgreSQL errors
================================================================
.. sectionauthor:: Daniele Varrazzo <daniele.varrazzo@gmail.com>
.. index::
single: Error; Class
.. module:: psycopg2.errors
.. versionadded:: 2.8
This module contains the classes psycopg raises upon receiving an error from
the database with a :sql:`SQLSTATE` value attached. The module is generated
from the PostgreSQL source code and includes classes for every error defined
by PostgreSQL in versions between 9.1 and 11.
Every class in the module is named after what referred as "condition name" `in
the documentation`__, converted to CamelCase: e.g. the error 22012,
``division_by_zero`` is exposed by this module as the class `!DivisionByZero`.
.. __: https://www.postgresql.org/docs/current/static/errcodes-appendix.html#ERRCODES-TABLE
Every exception class is a subclass of one of the :ref:`standard DB-API
exception <dbapi-exceptions>` and expose the `~psycopg2.Error` interface.
Each class' superclass is what used to be raised by psycopg in versions before
the introduction of this module, so everything should be compatible with
previously written code catching one the DB-API class: if your code used to
catch `!IntegrityError` to detect a duplicate entry, it will keep on working
even if a more specialised subclass such as `UniqueViolation` is raised.
The new classes allow a more idiomatic way to check and process a specific
error among the many the database may return. For instance, in order to check
that a table is locked, the following code could have been used previously:
.. code-block:: python
try:
cur.execute("LOCK TABLE mytable IN ACCESS EXCLUSIVE MODE NOWAIT")
except psycopg2.OperationalError as e:
if e.pgcode == psycopg2.errorcodes.LOCK_NOT_AVAILABLE:
locked = True
else:
raise
While this method is still available, the specialised class allows for a more
idiomatic error handler:
.. code-block:: python
try:
cur.execute("LOCK TABLE mytable IN ACCESS EXCLUSIVE MODE NOWAIT")
except psycopg2.errors.LockNotAvailable:
locked = True
For completeness, the module also exposes all the DB-API-defined classes and
:ref:`a few psycopg-specific exceptions <extension-exceptions>` previously
exposed by the `!extensions` module. One stop shop for all your mistakes...
.. autofunction:: lookup

View File

@ -94,7 +94,7 @@ introspection etc.
The method uses the efficient |lo_export|_ libpq function.
.. |lo_export| replace:: `!lo_export()`
.. _lo_export: http://www.postgresql.org/docs/current/static/lo-interfaces.html#LO-EXPORT
.. _lo_export: https://www.postgresql.org/docs/current/static/lo-interfaces.html#LO-EXPORT
.. method:: seek(offset, whence=0)
@ -125,7 +125,7 @@ introspection etc.
libpq function.
.. |lo_truncate| replace:: `!lo_truncate()`
.. _lo_truncate: http://www.postgresql.org/docs/current/static/lo-interfaces.html#LO-TRUNCATE
.. _lo_truncate: https://www.postgresql.org/docs/current/static/lo-interfaces.html#LO-TRUNCATE
.. versionadded:: 2.2.0
@ -154,6 +154,119 @@ introspection etc.
Close the object and remove it from the database.
.. autoclass:: ConnectionInfo(connection)
.. versionadded:: 2.8
.. autoattribute:: dbname
.. autoattribute:: user
.. autoattribute:: password
.. autoattribute:: host
.. autoattribute:: port
.. autoattribute:: options
.. autoattribute:: status
.. autoattribute:: transaction_status
.. automethod:: parameter_status(name)
.. autoattribute:: protocol_version
Currently Psycopg supports only protocol 3, which allows connection
to PostgreSQL server from version 7.4. Psycopg versions previous than
2.3 support both protocols 2 and 3.
.. autoattribute:: server_version
The number is formed by converting the major, minor, and revision
numbers into two-decimal-digit numbers and appending them together.
After PostgreSQL 10 the minor version was dropped, so the second group
of digits is always ``00``. For example, version 9.3.5 will be
returned as ``90305``, version 10.2 as ``100002``.
.. autoattribute:: error_message
.. autoattribute:: socket
.. autoattribute:: backend_pid
.. autoattribute:: needs_password
.. autoattribute:: used_password
.. autoattribute:: ssl_in_use
.. automethod:: ssl_attribute(name)
.. autoattribute:: ssl_attribute_names
.. class:: Column(\*args, \*\*kwargs)
Description of one result column, exposed as items of the
`cursor.description` sequence.
.. versionadded:: 2.8
in previous version the `!description` attribute was a sequence of
simple tuples or namedtuples.
.. attribute:: name
The name of the column returned.
.. attribute:: type_code
The PostgreSQL OID of the column. You can use the |pg_type|_ system
table to get more informations about the type. This is the value used
by Psycopg to decide what Python type use to represent the value. See
also :ref:`type-casting-from-sql-to-python`.
.. attribute:: display_size
The actual length of the column in bytes. Obtaining this value is
computationally intensive, so it is always `!None` unless the
:envvar:`PSYCOPG_DISPLAY_SIZE` parameter is set at compile time. See
also PQgetlength_.
.. attribute:: internal_size
The size in bytes of the column associated to this column on the
server. Set to a negative value for variable-size types See also
PQfsize_.
.. attribute:: precision
Total number of significant digits in columns of type |NUMERIC|_.
`!None` for other types.
.. attribute:: scale
Count of decimal digits in the fractional part in columns of type
|NUMERIC|. `!None` for other types.
.. attribute:: null_ok
Always `!None` as not easy to retrieve from the libpq.
.. attribute:: table_oid
The oid of the table from which the column was fetched (matching
:sql:`pg_class.oid`). `!None` if the column is not a simple reference
to a table column. See also PQftable_.
.. versionadded:: 2.8
.. attribute:: table_column
The number of the column (within its table) making up the result
(matching :sql:`pg_attribute.attnum`, so it will start from 1).
`!None` if the column is not a simple reference to a table column. See
also PQftablecol_.
.. versionadded:: 2.8
.. |pg_type| replace:: :sql:`pg_type`
.. _pg_type: https://www.postgresql.org/docs/current/static/catalog-pg-type.html
.. _PQgetlength: https://www.postgresql.org/docs/current/static/libpq-exec.html#LIBPQ-PQGETLENGTH
.. _PQfsize: https://www.postgresql.org/docs/current/static/libpq-exec.html#LIBPQ-PQFSIZE
.. _PQftable: https://www.postgresql.org/docs/current/static/libpq-exec.html#LIBPQ-PQFTABLE
.. _PQftablecol: https://www.postgresql.org/docs/current/static/libpq-exec.html#LIBPQ-PQFTABLECOL
.. _NUMERIC: https://www.postgresql.org/docs/current/static/datatype-numeric.html#DATATYPE-NUMERIC-DECIMAL
.. |NUMERIC| replace:: :sql:`NUMERIC`
.. autoclass:: Notify(pid, channel, payload='')
:members: pid, channel, payload
@ -186,6 +299,7 @@ introspection etc.
message_primary
schema_name
severity
severity_nonlocalized
source_file
source_function
source_line
@ -198,6 +312,9 @@ introspection etc.
not all the fields are available for all the errors and for all the
server versions.
.. versionadded:: 2.8
The `!severity_nonlocalized` attribute.
.. _sql-adaptation-objects:
@ -423,20 +540,27 @@ details.
Used by Psycopg when adapting or casting unicode strings. See
:ref:`unicode-handling`.
.. __: http://www.postgresql.org/docs/current/static/multibyte.html
.. __: http://docs.python.org/library/codecs.html#standard-encodings
.. __: https://www.postgresql.org/docs/current/static/multibyte.html
.. __: https://docs.python.org/library/codecs.html#standard-encodings
.. index::
single: Exceptions; Additional
.. _extension-exceptions:
Additional exceptions
---------------------
The module exports a few exceptions in addition to the :ref:`standard ones
<dbapi-exceptions>` defined by the |DBAPI|_.
.. note::
From psycopg 2.8 these error classes are also exposed by the
`psycopg2.errors` module.
.. exception:: QueryCanceledError
(subclasses `~psycopg2.OperationalError`)
@ -490,7 +614,7 @@ Other functions
.. seealso:: libpq docs for `PQlibVersion()`__.
.. __: http://www.postgresql.org/docs/current/static/libpq-misc.html#LIBPQ-PQLIBVERSION
.. __: https://www.postgresql.org/docs/current/static/libpq-misc.html#LIBPQ-PQLIBVERSION
.. function:: make_dsn(dsn=None, \*\*kwargs)
@ -524,7 +648,7 @@ Other functions
`connection URIs`__ are only supported from libpq 9.2). Raise
`~psycopg2.ProgrammingError` if the *dsn* is not valid.
.. __: http://www.postgresql.org/docs/current/static/libpq-connect.html#LIBPQ-CONNSTRING
.. __: https://www.postgresql.org/docs/current/static/libpq-connect.html#LIBPQ-CONNSTRING
Example::
@ -538,7 +662,7 @@ Other functions
.. seealso:: libpq docs for `PQconninfoParse()`__.
.. __: http://www.postgresql.org/docs/current/static/libpq-connect.html#LIBPQ-PQCONNINFOPARSE
.. __: https://www.postgresql.org/docs/current/static/libpq-connect.html#LIBPQ-PQCONNINFOPARSE
.. function:: quote_ident(str, scope)
@ -552,7 +676,7 @@ Other functions
.. seealso:: libpq docs for `PQescapeIdentifier()`__
.. __: http://www.postgresql.org/docs/current/static/libpq-exec.html#LIBPQ-PQESCAPEIDENTIFIER
.. __: https://www.postgresql.org/docs/current/static/libpq-exec.html#LIBPQ-PQESCAPEIDENTIFIER
.. method:: encrypt_password(password, user, scope=None, algorithm=None)
@ -637,7 +761,7 @@ methods. The level can be set to one of the following constants:
.. seealso:: `Read Committed Isolation Level`__ in PostgreSQL
documentation.
.. __: http://www.postgresql.org/docs/current/static/transaction-iso.html#XACT-READ-COMMITTED
.. __: https://www.postgresql.org/docs/current/static/transaction-iso.html#XACT-READ-COMMITTED
.. data:: ISOLATION_LEVEL_REPEATABLE_READ
@ -661,7 +785,7 @@ methods. The level can be set to one of the following constants:
.. seealso:: `Repeatable Read Isolation Level`__ in PostgreSQL
documentation.
.. __: http://www.postgresql.org/docs/current/static/transaction-iso.html#XACT-REPEATABLE-READ
.. __: https://www.postgresql.org/docs/current/static/transaction-iso.html#XACT-REPEATABLE-READ
.. data:: ISOLATION_LEVEL_SERIALIZABLE
@ -680,7 +804,7 @@ methods. The level can be set to one of the following constants:
.. seealso:: `Serializable Isolation Level`__ in PostgreSQL documentation.
.. __: http://www.postgresql.org/docs/current/static/transaction-iso.html#XACT-SERIALIZABLE
.. __: https://www.postgresql.org/docs/current/static/transaction-iso.html#XACT-SERIALIZABLE
.. data:: ISOLATION_LEVEL_DEFAULT
@ -703,7 +827,7 @@ Transaction status constants
----------------------------
These values represent the possible status of a transaction: the current value
can be read using the `connection.get_transaction_status()` method.
can be read using the `connection.info.transaction_status` property.
.. data:: TRANSACTION_STATUS_IDLE

View File

@ -542,10 +542,10 @@ fields to JSON) you can use the `register_json()` function.
The Python :py:mod:`json` module is used by default to convert Python objects
to JSON and to parse data from the database.
.. _JSON: http://www.json.org/
.. _JSON: https://www.json.org/
.. |pgjson| replace:: :sql:`json`
.. |jsonb| replace:: :sql:`jsonb`
.. _pgjson: http://www.postgresql.org/docs/current/static/datatype-json.html
.. _pgjson: https://www.postgresql.org/docs/current/static/datatype-json.html
In order to pass a Python object to the database as query argument you can use
the `Json` adapter::
@ -664,7 +664,7 @@ can be enabled using the `register_hstore()` function.
.. |hstore| replace:: :sql:`hstore`
.. _hstore: http://www.postgresql.org/docs/current/static/hstore.html
.. _hstore: https://www.postgresql.org/docs/current/static/hstore.html
@ -686,7 +686,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/current/static/sql-createtype.html
.. _CREATE TYPE: https://www.postgresql.org/docs/current/static/sql-createtype.html
.. doctest::
@ -800,7 +800,7 @@ PostgreSQL |range|_ types. Builtin |range| types are supported out-of-the-box;
user-defined |range| types can be adapted using `register_range()`.
.. |range| replace:: :sql:`range`
.. _range: http://www.postgresql.org/docs/current/static/rangetypes.html
.. _range: https://www.postgresql.org/docs/current/static/rangetypes.html
.. autoclass:: Range
@ -809,7 +809,7 @@ user-defined |range| types can be adapted using `register_range()`.
features: it doesn't perform normalization and doesn't implement all the
operators__ supported by the database.
.. __: http://www.postgresql.org/docs/current/static/functions-range.html#RANGE-OPERATORS-TABLE
.. __: https://www.postgresql.org/docs/current/static/functions-range.html#RANGE-OPERATORS-TABLE
`!Range` objects are immutable, hashable, and support the ``in`` operator
(checking if an element is within the range). They can be tested for

View File

@ -40,7 +40,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/current/static/sql-savepoint.html
.. _SAVEPOINT: https://www.postgresql.org/docs/current/static/sql-savepoint.html
.. _faq-transaction-aborted-multiprocess:
@ -184,8 +184,8 @@ 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/current/static/datatype-binary.html
.. __: http://www.postgresql.org/docs/current/static/runtime-config-client.html#GUC-BYTEA-OUTPUT
.. __: https://www.postgresql.org/docs/current/static/datatype-binary.html
.. __: https://www.postgresql.org/docs/current/static/runtime-config-client.html#GUC-BYTEA-OUTPUT
.. _faq-array:
@ -318,7 +318,7 @@ I can't compile `!psycopg2`: the compiler says *error: libpq-fe.h: No such file
:program:`pg_config` at install time and the libpq at runtime.
.. |lo_truncate| replace:: `!lo_truncate()`
.. _lo_truncate: http://www.postgresql.org/docs/current/static/lo-interfaces.html#LO-TRUNCATE
.. _lo_truncate: https://www.postgresql.org/docs/current/static/lo-interfaces.html#LO-TRUNCATE
.. _faq-import-mod_wsgi:
@ -332,5 +332,5 @@ Psycopg raises *ImportError: cannot import name tz* on import in mod_wsgi / ASP,
use the WSGIPythonEggs__ directive.
.. _egg: http://peak.telecommunity.com/DevCenter/PythonEggs
.. __: http://stackoverflow.com/questions/2192323/what-is-the-python-egg-cache-python-egg-cache
.. __: http://code.google.com/p/modwsgi/wiki/ConfigurationDirectives#WSGIPythonEggs
.. __: https://stackoverflow.com/questions/2192323/what-is-the-python-egg-cache-python-egg-cache
.. __: https://modwsgi.readthedocs.io/en/develop/configuration-directives/WSGIPythonEggs.html

View File

@ -24,9 +24,9 @@ Psycopg 2 is both Unicode and Python 3 friendly.
.. _Psycopg: http://initd.org/psycopg/
.. _PostgreSQL: http://www.postgresql.org/
.. _Python: http://www.python.org/
.. _libpq: http://www.postgresql.org/docs/current/static/libpq.html
.. _PostgreSQL: https://www.postgresql.org/
.. _Python: https://www.python.org/
.. _libpq: https://www.postgresql.org/docs/current/static/libpq.html
.. rubric:: Contents
@ -42,6 +42,7 @@ Psycopg 2 is both Unicode and Python 3 friendly.
advanced
extensions
extras
errors
sql
tz
pool

View File

@ -12,11 +12,11 @@ to use Psycopg on a different Python implementation (PyPy, Jython, IronPython)
there is an experimental `porting of Psycopg for Ctypes`__, but it is not as
mature as the C implementation yet.
.. _PostgreSQL: http://www.postgresql.org/
.. _Python: http://www.python.org/
.. _libpq: http://www.postgresql.org/docs/current/static/libpq.html
.. _CPython: http://en.wikipedia.org/wiki/CPython
.. _Ctypes: http://docs.python.org/library/ctypes.html
.. _PostgreSQL: https://www.postgresql.org/
.. _Python: https://www.python.org/
.. _libpq: https://www.postgresql.org/docs/current/static/libpq.html
.. _CPython: https://en.wikipedia.org/wiki/CPython
.. _Ctypes: https://docs.python.org/library/ctypes.html
.. __: https://github.com/mvantellingen/psycopg2-ctypes
@ -152,7 +152,7 @@ using something like ``pip install -U pip``), then you can run:
.. __: PyPI-binary_
.. _PyPI-binary: https://pypi.org/project/psycopg2-binary/
.. _wheel: http://pythonwheels.com/
.. _wheel: https://pythonwheels.com/
.. note::

View File

@ -50,11 +50,11 @@ The module interface respects the standard defined in the |DBAPI|_.
using `environment variables`__.
.. __:
.. _connstring: http://www.postgresql.org/docs/current/static/libpq-connect.html#LIBPQ-CONNSTRING
.. _connstring: https://www.postgresql.org/docs/current/static/libpq-connect.html#LIBPQ-CONNSTRING
.. __:
.. _connparams: http://www.postgresql.org/docs/current/static/libpq-connect.html#LIBPQ-PARAMKEYWORDS
.. _connparams: https://www.postgresql.org/docs/current/static/libpq-connect.html#LIBPQ-PARAMKEYWORDS
.. __:
.. _connenvvars: http://www.postgresql.org/docs/current/static/libpq-envars.html
.. _connenvvars: https://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,9 +117,10 @@ The module interface respects the standard defined in the |DBAPI|_.
Integer constant reporting the version of the ``libpq`` library this
``psycopg2`` module was compiled with (in the same format of
`~connection.server_version`). If this value is greater or equal than
``90100`` then you may query the version of the actually loaded library
using the `~psycopg2.extensions.libpq_version()` function.
`~psycopg2.extensions.ConnectionInfo.server_version`). If this value is
greater or equal than ``90100`` then you may query the version of the
actually loaded library using the `~psycopg2.extensions.libpq_version()`
function.
.. index::
@ -250,13 +251,14 @@ available through the following exceptions:
.. extension::
Psycopg may raise a few other, more specialized, exceptions: currently
`~psycopg2.extensions.QueryCanceledError` and
`~psycopg2.extensions.TransactionRollbackError` are defined. These
exceptions are not exposed by the main `!psycopg2` module but are
made available by the `~psycopg2.extensions` module. All the
additional exceptions are subclasses of standard |DBAPI| exceptions, so
trapping them specifically is not required.
Psycopg actually raises a different exception for each :sql:`SQLSTATE`
error returned by the database: the classes are available in the
`psycopg2.errors` module. Every exception class is a subclass of one of
the exception classes defined here though, so they don't need to be
trapped specifically: trapping `!Error` or `!DatabaseError` is usually
what needed to write a generic error handler; trapping a specific error
such as `!NotNullViolation` can be useful to write specific exception
handlers.
This is the exception inheritance layout:
@ -270,8 +272,6 @@ This is the exception inheritance layout:
\|__ `DatabaseError`
\|__ `DataError`
\|__ `OperationalError`
\| \|__ `psycopg2.extensions.QueryCanceledError`
\| \|__ `psycopg2.extensions.TransactionRollbackError`
\|__ `IntegrityError`
\|__ `InternalError`
\|__ `ProgrammingError`

View File

@ -77,16 +77,26 @@ to cursor methods such as `~cursor.execute()`, `~cursor.executemany()`,
.. autoclass:: Identifier
.. autoattribute:: string
.. versionchanged:: 2.8
added support for multiple strings.
.. autoattribute:: strings
.. versionadded:: 2.8
previous verions only had a `!string` attribute. The attribute
still exists but is deprecate and will only work if the
`!Identifier` wraps a single string.
.. autoclass:: Literal
.. autoattribute:: wrapped
.. autoclass:: Placeholder
.. autoattribute:: name
.. autoclass:: Composed
.. autoattribute:: seq

View File

@ -198,8 +198,8 @@ called `SQL injection`_ and is known to be one of the most widespread forms of
attack to database servers. Before continuing, please print `this page`__ as a
memo and hang it onto your desk.
.. _SQL injection: http://en.wikipedia.org/wiki/SQL_injection
.. __: http://xkcd.com/327/
.. _SQL injection: https://en.wikipedia.org/wiki/SQL_injection
.. __: https://xkcd.com/327/
Psycopg can `automatically convert Python objects to and from SQL
literals`__: using this feature your code will be more robust and
@ -221,7 +221,28 @@ argument of the `~cursor.execute()` method::
>>> cur.execute(SQL, data) # Note: no % operator
Values containing backslashes and LIKE
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Unlike in Python, the backslash (`\\`) is not used as an escape
character *except* in patterns used with `LIKE` and `ILIKE` where they
are needed to escape the `%` and `_` characters.
This can lead to confusing situations::
>>> path = r'C:\Users\Bobby.Tables'
>>> cur.execute('INSERT INTO mytable(path) VALUES (%s)', (path,))
>>> cur.execute('SELECT * FROM mytable WHERE path LIKE %s', (path,))
>>> cur.fetchall()
[]
The solution is to specify an `ESCAPE` character of `''` (empty string)
in your `LIKE` query::
>>> cur.execute("SELECT * FROM mytable WHERE path LIKE %s ESCAPE ''", (path,))
.. index::
single: Adaptation
pair: Objects; Adaptation
@ -351,7 +372,7 @@ converted into `!Decimal`.
This of course may imply a loss of precision.
.. seealso:: `PostgreSQL numeric types
<http://www.postgresql.org/docs/current/static/datatype-numeric.html>`__
<https://www.postgresql.org/docs/current/static/datatype-numeric.html>`__
.. index::
@ -391,8 +412,8 @@ 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/current/static/multibyte.html
.. __: http://docs.python.org/library/codecs.html#standard-encodings
.. __: https://www.postgresql.org/docs/current/static/multibyte.html
.. __: https://docs.python.org/library/codecs.html
When reading data from the database, in Python 2 the strings returned are
usually 8 bit `!str` objects encoded in the database client encoding::
@ -465,7 +486,7 @@ type `!str`). Any object implementing the `Revised Buffer Protocol`__ should
be usable as binary type. Received data is returned as `!buffer` (in Python 2)
or `!memoryview` (in Python 3).
.. __: http://www.python.org/dev/peps/pep-3118/
.. __: https://www.python.org/dev/peps/pep-3118/
.. versionchanged:: 2.4
only strings were supported before.
@ -494,8 +515,8 @@ or `!memoryview` (in Python 3).
server configuration file or in the client session (using a query such as
``SET bytea_output TO escape;``) before receiving binary data.
.. __: http://www.postgresql.org/docs/current/static/datatype-binary.html
.. __: http://www.postgresql.org/docs/current/static/runtime-config-client.html#GUC-BYTEA-OUTPUT
.. __: https://www.postgresql.org/docs/current/static/datatype-binary.html
.. __: https://www.postgresql.org/docs/current/static/runtime-config-client.html#GUC-BYTEA-OUTPUT
.. index::
@ -527,7 +548,7 @@ the same way::
"SELECT '38 days 6027.425337 seconds';"
.. seealso:: `PostgreSQL date/time types
<http://www.postgresql.org/docs/current/static/datatype-datetime.html>`__
<https://www.postgresql.org/docs/current/static/datatype-datetime.html>`__
.. index::
@ -622,7 +643,7 @@ Python lists are converted into PostgreSQL :sql:`ARRAY`\ s::
Furthermore :sql:`ANY` can also work with empty lists, whereas :sql:`IN ()`
is a SQL syntax error.
.. __: http://www.postgresql.org/docs/current/static/functions-subquery.html#FUNCTIONS-SUBQUERY-ANY-SOME
.. __: https://www.postgresql.org/docs/current/static/functions-subquery.html#FUNCTIONS-SUBQUERY-ANY-SOME
.. note::
@ -846,7 +867,7 @@ lifetime extends well after `~connection.commit()`, calling
.. |DECLARE| replace:: :sql:`DECLARE`
.. _DECLARE: http://www.postgresql.org/docs/current/static/sql-declare.html
.. _DECLARE: https://www.postgresql.org/docs/current/static/sql-declare.html
@ -876,7 +897,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/current/static/libpq-connect.html#LIBPQ-CONNECT
.. __: https://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.
@ -920,7 +941,7 @@ Please refer to the documentation of the single methods for details and
examples.
.. |COPY| replace:: :sql:`COPY`
.. __: http://www.postgresql.org/docs/current/static/sql-copy.html
.. __: https://www.postgresql.org/docs/current/static/sql-copy.html
@ -937,7 +958,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/current/static/largeobjects.html
.. __: https://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
@ -948,9 +969,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/current/static/lo-interfaces.html#LO-IMPORT
.. _lo_import: https://www.postgresql.org/docs/current/static/lo-interfaces.html#LO-IMPORT
.. |lo_export| replace:: `!lo_export()`
.. _lo_export: http://www.postgresql.org/docs/current/static/lo-interfaces.html#LO-EXPORT
.. _lo_export: https://www.postgresql.org/docs/current/static/lo-interfaces.html#LO-EXPORT
.. versionchanged:: 2.6
added support for large objects greated than 2GB. Note that the support is
@ -1014,5 +1035,5 @@ transactions produced by a Java program.
For further details see the documentation for the above methods.
.. __: http://www.opengroup.org/bookstore/catalog/c193.htm
.. __: http://jdbc.postgresql.org/
.. __: https://publications.opengroup.org/c193
.. __: https://jdbc.postgresql.org/

View File

@ -8,8 +8,8 @@ small and fast, and stable as a rock.
Homepage: http://initd.org/projects/psycopg2
.. _PostgreSQL: http://www.postgresql.org/
.. _Python: http://www.python.org/
.. _PostgreSQL: https://www.postgresql.org/
.. _Python: https://www.python.org/
:Groups:
* `Connections creation`: connect
@ -43,7 +43,7 @@ Homepage: http://initd.org/projects/psycopg2
# Note: the first internal import should be _psycopg, otherwise the real cause
# of a failed loading of the C module may get hidden, see
# http://archives.postgresql.org/psycopg/2011-02/msg00044.php
# https://archives.postgresql.org/psycopg/2011-02/msg00044.php
# Import the DBAPI-2.0 stuff into top-level module.
@ -72,8 +72,8 @@ _ext.register_adapter(type(None), _ext.NoneAdapter)
# Register the Decimal adapter here instead of in the C layer.
# This way a new class is registered for each sub-interpreter.
# See ticket #52
from decimal import Decimal
from psycopg2._psycopg import Decimal as Adapter
from decimal import Decimal # noqa
from psycopg2._psycopg import Decimal as Adapter # noqa
_ext.register_adapter(Decimal, Adapter)
del Decimal, Adapter

View File

@ -185,7 +185,7 @@ def _get_json_oids(conn_or_curs, name='json'):
conn_status = conn.status
# column typarray not available before PG 8.3
typarray = conn.server_version >= 80300 and "typarray" or "NULL"
typarray = conn.info.server_version >= 80300 and "typarray" or "NULL"
# get the oid for the hstore
curs.execute(

View File

@ -62,6 +62,19 @@ class Range(object):
return "%s(%r, %r, %r)" % (self.__class__.__name__,
self._lower, self._upper, self._bounds)
def __str__(self):
if self._bounds is None:
return 'empty'
items = [
self._bounds[0],
str(self._lower),
', ',
str(self._upper),
self._bounds[1]
]
return ''.join(items)
@property
def lower(self):
"""The lower bound of the range. `!None` if empty or unbound."""
@ -339,9 +352,9 @@ class RangeCaster(object):
from psycopg2.extras import _solve_conn_curs
conn, curs = _solve_conn_curs(conn_or_curs)
if conn.server_version < 90200:
if conn.info.server_version < 90200:
raise ProgrammingError("range types not available in version %s"
% conn.server_version)
% conn.info.server_version)
# Store the transaction status of the connection to revert it after use
conn_status = conn.status
@ -493,10 +506,10 @@ class NumberRangeAdapter(RangeAdapter):
return ("'%s%s,%s%s'" % (
r._bounds[0], lower, upper, r._bounds[1])).encode('ascii')
# TODO: probably won't work with infs, nans and other tricky cases.
register_adapter(NumericRange, NumberRangeAdapter)
# Register globally typecasters and adapters for builtin range types.
# note: the adapter is registered more than once, but this is harmless.

View File

@ -26,7 +26,7 @@ This module contains symbolic names for all PostgreSQL error codes.
#
# Based on:
#
# http://www.postgresql.org/docs/current/static/errcodes-appendix.html
# https://www.postgresql.org/docs/current/static/errcodes-appendix.html
#
@ -182,6 +182,7 @@ INVALID_XML_PROCESSING_INSTRUCTION = '2200T'
INVALID_INDICATOR_PARAMETER_VALUE = '22010'
SUBSTRING_ERROR = '22011'
DIVISION_BY_ZERO = '22012'
INVALID_PRECEDING_OR_FOLLOWING_SIZE = '22013'
INVALID_ARGUMENT_FOR_NTILE_FUNCTION = '22014'
INTERVAL_FIELD_OVERFLOW = '22015'
INVALID_ARGUMENT_FOR_NTH_VALUE_FUNCTION = '22016'

1314
lib/errors.py Normal file

File diff suppressed because it is too large Load Diff

View File

@ -8,7 +8,7 @@ This module holds all the extensions to the DBAPI-2.0 provided by psycopg.
- `adapt()` -- exposes the PEP-246_ compatible adapting mechanism used
by psycopg to adapt Python types to PostgreSQL ones
.. _PEP-246: http://www.python.org/peps/pep-0246.html
.. _PEP-246: https://www.python.org/dev/peps/pep-0246/
"""
# psycopg/extensions.py - DBAPI-2.0 extensions specific to psycopg
#
@ -61,7 +61,7 @@ from psycopg2._psycopg import ( # noqa
adapt, adapters, encodings, connection, cursor,
lobject, Xid, libpq_version, parse_dsn, quote_ident,
string_types, binary_types, new_type, new_array_type, register_type,
ISQLQuote, Notify, Diagnostics, Column,
ISQLQuote, Notify, Diagnostics, Column, ConnectionInfo,
QueryCanceledError, TransactionRollbackError,
set_wait_callback, get_wait_callback, encrypt_password, )

View File

@ -31,10 +31,7 @@ import time as _time
import re as _re
from collections import namedtuple, OrderedDict
try:
import logging as _logging
except:
_logging = None
import logging as _logging
import psycopg2
from psycopg2 import extensions as _ext
@ -189,7 +186,7 @@ class DictRow(list):
def get(self, x, default=None):
try:
return self[x]
except:
except Exception:
return default
def copy(self):
@ -404,7 +401,7 @@ class NamedTupleCursor(_cursor):
class LoggingConnection(_connection):
"""A connection that logs all queries to a file or logger__ object.
.. __: http://docs.python.org/library/logging.html
.. __: https://docs.python.org/library/logging.html
"""
def initialize(self, logobj):
@ -638,8 +635,8 @@ class ReplicationCursor(_replicationCursor):
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/current/static/datatype-uuid.html
.. __: https://docs.python.org/library/uuid.html
.. __: https://www.postgresql.org/docs/current/static/datatype-uuid.html
"""
def __init__(self, uuid):
@ -806,7 +803,7 @@ class HstoreAdapter(object):
self.conn = conn
# use an old-style getquoted implementation if required
if conn.server_version < 90000:
if conn.info.server_version < 90000:
self.getquoted = self._getquoted_8
def _getquoted_8(self):
@ -911,7 +908,7 @@ class HstoreAdapter(object):
conn_status = conn.status
# column typarray not available before PG 8.3
typarray = conn.server_version >= 80300 and "typarray" or "NULL"
typarray = conn.info.server_version >= 80300 and "typarray" or "NULL"
rv0, rv1 = [], []
@ -1097,7 +1094,7 @@ class CompositeCaster(object):
schema = 'public'
# column typarray not available before PG 8.3
typarray = conn.server_version >= 80300 and "typarray" or "NULL"
typarray = conn.info.server_version >= 80300 and "typarray" or "NULL"
# get the type oid and attributes
curs.execute("""\
@ -1257,6 +1254,10 @@ def execute_values(cur, sql, argslist, template=None, page_size=100):
[(1, 20, 3), (4, 50, 6), (7, 8, 9)])
'''
from psycopg2.sql import Composable
if isinstance(sql, Composable):
sql = sql.as_string(cur)
# we can't just use sql % vals because vals is bytes: if sql is bytes
# there will be some decoding error because of stupid codec used, and Py3
# doesn't implement % on bytes.

View File

@ -105,7 +105,7 @@ class AbstractConnectionPool(object):
# Return the connection into a consistent state before putting
# it back into the pool
if not conn.closed:
status = conn.get_transaction_status()
status = conn.info.transaction_status
if status == _ext.TRANSACTION_STATUS_UNKNOWN:
# server connection lost
conn.close()
@ -138,7 +138,7 @@ class AbstractConnectionPool(object):
for conn in self._pool + list(self._used.values()):
try:
conn.close()
except:
except Exception:
pass
self.closed = True

View File

@ -290,7 +290,7 @@ class SQL(Composable):
class Identifier(Composable):
"""
A `Composable` representing an SQL identifer.
A `Composable` representing an SQL identifer or a dot-separated sequence.
Identifiers usually represent names of database objects, such as tables or
fields. PostgreSQL identifiers follow `different rules`__ than SQL string
@ -307,20 +307,50 @@ class Identifier(Composable):
>>> print(sql.SQL(', ').join([t1, t2, t3]).as_string(conn))
"foo", "ba'r", "ba""z"
"""
def __init__(self, string):
if not isinstance(string, string_types):
raise TypeError("SQL identifiers must be strings")
Multiple strings can be passed to the object to represent a qualified name,
i.e. a dot-separated sequence of identifiers.
super(Identifier, self).__init__(string)
Example::
>>> query = sql.SQL("select {} from {}").format(
... sql.Identifier("table", "field"),
... sql.Identifier("schema", "table"))
>>> print(query.as_string(conn))
select "table"."field" from "schema"."table"
"""
def __init__(self, *strings):
if not strings:
raise TypeError("Identifier cannot be empty")
for s in strings:
if not isinstance(s, string_types):
raise TypeError("SQL identifier parts must be strings")
super(Identifier, self).__init__(strings)
@property
def strings(self):
"""A tuple with the strings wrapped by the `Identifier`."""
return self._wrapped
@property
def string(self):
"""The string wrapped by the `Identifier`."""
return self._wrapped
"""The string wrapped by the `Identifier`.
"""
if len(self._wrapped) == 1:
return self._wrapped[0]
else:
raise AttributeError(
"the Identifier wraps more than one than one string")
def __repr__(self):
return "%s(%s)" % (
self.__class__.__name__,
', '.join(map(repr, self._wrapped)))
def as_string(self, context):
return ext.quote_ident(self._wrapped, context)
return '.'.join(ext.quote_ident(s, context) for s in self._wrapped)
class Literal(Composable):

View File

@ -44,7 +44,7 @@ class FixedOffsetTimezone(datetime.tzinfo):
offset and name that instance will be returned. This saves memory and
improves comparability.
.. __: http://docs.python.org/library/datetime.html#datetime-tzinfo
.. __: https://docs.python.org/library/datetime.html
"""
_name = None
_offset = ZERO
@ -132,6 +132,7 @@ class LocalTimezone(datetime.tzinfo):
tt = time.localtime(stamp)
return tt.tm_isdst > 0
LOCAL = LocalTimezone()
# TODO: pre-generate some interesting time zones?

View File

@ -55,6 +55,8 @@ list_quote(listObject *self)
/* empty arrays are converted to NULLs (still searching for a way to
insert an empty array in postgresql */
if (len == 0) {
/* it cannot be ARRAY[] because it would make empty lists unusable
* in any() without a cast. But we may convert it into ARRAY[] below */
res = Bytes_FromString("'{}'");
goto exit;
}
@ -79,7 +81,19 @@ list_quote(listObject *self)
/* Lists of arrays containing only nulls are also not supported
* by the ARRAY construct so we should do some special casing */
if (!PyList_Check(wrapped) || Bytes_AS_STRING(qs[i])[0] == 'A') {
if (PyList_Check(wrapped)) {
if (Bytes_AS_STRING(qs[i])[0] == 'A') {
all_nulls = 0;
}
else if (0 == strcmp(Bytes_AS_STRING(qs[i]), "'{}'")) {
/* case of issue #788: '{{}}' is not supported but
* array[array[]] is */
all_nulls = 0;
Py_CLEAR(qs[i]);
qs[i] = Bytes_FromString("ARRAY[]");
}
}
else {
all_nulls = 0;
}
}

48
psycopg/column.h Normal file
View File

@ -0,0 +1,48 @@
/* column.h - definition for a column in cursor.description type
*
* Copyright (C) 2018 Daniele Varrazzo <daniele.varrazzo@gmail.com>
*
* This file is part of psycopg.
*
* psycopg2 is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* In addition, as a special exception, the copyright holders give
* permission to link this program with the OpenSSL library (or with
* modified versions of OpenSSL that use the same license as OpenSSL),
* and distribute linked combinations including the two.
*
* You must obey the GNU Lesser General Public License in all respects for
* all of the code used other than OpenSSL.
*
* psycopg2 is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*/
#ifndef PSYCOPG_COLUMN_H
#define PSYCOPG_COLUMN_H 1
extern HIDDEN PyTypeObject columnType;
typedef struct {
PyObject_HEAD
PyObject *name;
PyObject *type_code;
PyObject *display_size;
PyObject *internal_size;
PyObject *precision;
PyObject *scale;
PyObject *null_ok;
/* Extensions to the DBAPI */
PyObject *table_oid;
PyObject *table_column;
} columnObject;
#endif /* PSYCOPG_COLUMN_H */

375
psycopg/column_type.c Normal file
View File

@ -0,0 +1,375 @@
/* column_type.c - python interface to cursor.description objects
*
* Copyright (C) 2018 Daniele Varrazzo <daniele.varrazzo@gmail.com>
*
* This file is part of psycopg.
*
* psycopg2 is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* In addition, as a special exception, the copyright holders give
* permission to link this program with the OpenSSL library (or with
* modified versions of OpenSSL that use the same license as OpenSSL),
* and distribute linked combinations including the two.
*
* You must obey the GNU Lesser General Public License in all respects for
* all of the code used other than OpenSSL.
*
* psycopg2 is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*/
#define PSYCOPG_MODULE
#include "psycopg/psycopg.h"
#include "psycopg/column.h"
static const char column_doc[] =
"Description of a column returned by a query.\n\n"
"The DBAPI demands this object to be a 7-items sequence. This object\n"
"respects this interface, but adds names for the exposed attributes\n"
"and adds attribute not requested by the DBAPI.";
static const char name_doc[] =
"The name of the column returned.";
static const char type_code_doc[] =
"The PostgreSQL OID of the column.\n\n"
"You can use the pg_type system table to get more informations about the\n"
"type. This is the value used by Psycopg to decide what Python type use\n"
"to represent the value";
static const char display_size_doc[] =
"The actual length of the column in bytes.\n\n"
"Obtaining this value is computationally intensive, so it is always None\n"
"unless the PSYCOPG_DISPLAY_SIZE parameter is set at compile time.";
static const char internal_size_doc[] =
"The size in bytes of the column associated to this column on the server.\n\n"
"Set to a negative value for variable-size types.";
static const char precision_doc[] =
"Total number of significant digits in columns of type NUMERIC.\n\n"
"None for other types.";
static const char scale_doc[] =
"Count of decimal digits in the fractional part in columns of type NUMERIC.\n\n"
"None for other types.";
static const char null_ok_doc[] =
"Always none.";
static const char table_oid_doc[] =
"The OID of the table from which the column was fetched.\n\n"
"None if not available";
static const char table_column_doc[] =
"The number (within its table) of the column making up the result\n\n"
"None if not available. Note that PostgreSQL column numbers start at 1";
static PyMemberDef column_members[] = {
{ "name", T_OBJECT, offsetof(columnObject, name), READONLY, (char *)name_doc },
{ "type_code", T_OBJECT, offsetof(columnObject, type_code), READONLY, (char *)type_code_doc },
{ "display_size", T_OBJECT, offsetof(columnObject, display_size), READONLY, (char *)display_size_doc },
{ "internal_size", T_OBJECT, offsetof(columnObject, internal_size), READONLY, (char *)internal_size_doc },
{ "precision", T_OBJECT, offsetof(columnObject, precision), READONLY, (char *)precision_doc },
{ "scale", T_OBJECT, offsetof(columnObject, scale), READONLY, (char *)scale_doc },
{ "null_ok", T_OBJECT, offsetof(columnObject, null_ok), READONLY, (char *)null_ok_doc },
{ "table_oid", T_OBJECT, offsetof(columnObject, table_oid), READONLY, (char *)table_oid_doc },
{ "table_column", T_OBJECT, offsetof(columnObject, table_column), READONLY, (char *)table_column_doc },
{ NULL }
};
static PyObject *
column_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
{
return type->tp_alloc(type, 0);
}
static int
column_init(columnObject *self, PyObject *args, PyObject *kwargs)
{
static char *kwlist[] = {
"name", "type_code", "display_size", "internal_size",
"precision", "scale", "null_ok", "table_oid", "table_column", NULL};
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|OOOOOOOOO", kwlist,
&self->name, &self->type_code, &self->display_size,
&self->internal_size, &self->precision, &self->scale,
&self->null_ok, &self->table_oid, &self->table_column)) {
return -1;
}
return 0;
}
static void
column_dealloc(columnObject *self)
{
Py_CLEAR(self->name);
Py_CLEAR(self->type_code);
Py_CLEAR(self->display_size);
Py_CLEAR(self->internal_size);
Py_CLEAR(self->precision);
Py_CLEAR(self->scale);
Py_CLEAR(self->null_ok);
Py_CLEAR(self->table_oid);
Py_CLEAR(self->table_column);
Py_TYPE(self)->tp_free((PyObject *)self);
}
static PyObject*
column_repr(columnObject *self)
{
PyObject *rv = NULL;
PyObject *format = NULL;
PyObject *args = NULL;
PyObject *tmp;
if (!(format = Text_FromUTF8("Column(name=%r, type_code=%r)"))) {
goto exit;
}
if (!(args = PyTuple_New(2))) { goto exit; }
tmp = self->name ? self->name : Py_None;
Py_INCREF(tmp);
PyTuple_SET_ITEM(args, 0, tmp);
tmp = self->type_code ? self->type_code : Py_None;
Py_INCREF(tmp);
PyTuple_SET_ITEM(args, 1, tmp);
rv = Text_Format(format, args);
exit:
Py_XDECREF(args);
Py_XDECREF(format);
return rv;
}
static PyObject *
column_richcompare(columnObject *self, PyObject *other, int op)
{
PyObject *rv = NULL;
PyObject *tself = NULL;
if (!(tself = PyObject_CallFunctionObjArgs(
(PyObject *)&PyTuple_Type, (PyObject *)self, NULL))) {
goto exit;
}
rv = PyObject_RichCompare(tself, other, op);
exit:
Py_XDECREF(tself);
return rv;
}
/* column description can be accessed as a 7 items tuple for DBAPI compatibility */
static Py_ssize_t
column_len(columnObject *self)
{
return 7;
}
static PyObject *
column_getitem(columnObject *self, Py_ssize_t item)
{
PyObject *rv = NULL;
if (item < 0)
item += 7;
switch (item) {
case 0:
rv = self->name;
break;
case 1:
rv = self->type_code;
break;
case 2:
rv = self->display_size;
break;
case 3:
rv = self->internal_size;
break;
case 4:
rv = self->precision;
break;
case 5:
rv = self->scale;
break;
case 6:
rv = self->null_ok;
break;
default:
PyErr_SetString(PyExc_IndexError, "index out of range");
return NULL;
}
if (!rv) {
rv = Py_None;
}
Py_INCREF(rv);
return rv;
}
static PySequenceMethods column_sequence = {
(lenfunc)column_len, /* sq_length */
0, /* sq_concat */
0, /* sq_repeat */
(ssizeargfunc)column_getitem, /* sq_item */
0, /* sq_slice */
0, /* sq_ass_item */
0, /* sq_ass_slice */
0, /* sq_contains */
0, /* sq_inplace_concat */
0, /* sq_inplace_repeat */
};
static PyObject *
column_getstate(columnObject *self)
{
return PyObject_CallFunctionObjArgs(
(PyObject *)&PyTuple_Type, (PyObject *)self, NULL);
}
PyObject *
column_setstate(columnObject *self, PyObject *state)
{
Py_ssize_t size;
PyObject *rv = NULL;
if (state == Py_None) {
goto exit;
}
if (!PyTuple_Check(state)) {
PyErr_SetString(PyExc_TypeError, "state is not a tuple");
goto error;
}
size = PyTuple_GET_SIZE(state);
if (size > 0) {
Py_CLEAR(self->name);
self->name = PyTuple_GET_ITEM(state, 0);
Py_INCREF(self->name);
}
if (size > 1) {
Py_CLEAR(self->type_code);
self->type_code = PyTuple_GET_ITEM(state, 1);
Py_INCREF(self->type_code);
}
if (size > 2) {
Py_CLEAR(self->display_size);
self->display_size = PyTuple_GET_ITEM(state, 2);
Py_INCREF(self->display_size);
}
if (size > 3) {
Py_CLEAR(self->internal_size);
self->internal_size = PyTuple_GET_ITEM(state, 3);
Py_INCREF(self->internal_size);
}
if (size > 4) {
Py_CLEAR(self->precision);
self->precision = PyTuple_GET_ITEM(state, 4);
Py_INCREF(self->precision);
}
if (size > 5) {
Py_CLEAR(self->scale);
self->scale = PyTuple_GET_ITEM(state, 5);
Py_INCREF(self->scale);
}
if (size > 6) {
Py_CLEAR(self->null_ok);
self->null_ok = PyTuple_GET_ITEM(state, 6);
Py_INCREF(self->null_ok);
}
if (size > 7) {
Py_CLEAR(self->table_oid);
self->table_oid = PyTuple_GET_ITEM(state, 7);
Py_INCREF(self->table_oid);
}
if (size > 8) {
Py_CLEAR(self->table_column);
self->table_column = PyTuple_GET_ITEM(state, 8);
Py_INCREF(self->table_column);
}
exit:
rv = Py_None;
Py_INCREF(rv);
error:
return rv;
}
static PyMethodDef column_methods[] = {
/* Make Column picklable. */
{"__getstate__", (PyCFunction)column_getstate, METH_NOARGS },
{"__setstate__", (PyCFunction)column_setstate, METH_O },
{NULL}
};
PyTypeObject columnType = {
PyVarObject_HEAD_INIT(NULL, 0)
"psycopg2.extensions.Column",
sizeof(columnObject), 0,
(destructor)column_dealloc, /* tp_dealloc */
0, /*tp_print*/
0, /*tp_getattr*/
0, /*tp_setattr*/
0, /*tp_compare*/
(reprfunc)column_repr, /*tp_repr*/
0, /*tp_as_number*/
&column_sequence, /*tp_as_sequence*/
0, /*tp_as_mapping*/
0, /*tp_hash */
0, /*tp_call*/
0, /*tp_str*/
0, /*tp_getattro*/
0, /*tp_setattro*/
0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /*tp_flags*/
column_doc, /*tp_doc*/
0, /*tp_traverse*/
0, /*tp_clear*/
(richcmpfunc)column_richcompare, /*tp_richcompare*/
0, /*tp_weaklistoffset*/
0, /*tp_iter*/
0, /*tp_iternext*/
column_methods, /*tp_methods*/
column_members, /*tp_members*/
0, /*tp_getset*/
0, /*tp_base*/
0, /*tp_dict*/
0, /*tp_descr_get*/
0, /*tp_descr_set*/
0, /*tp_dictoffset*/
(initproc)column_init, /*tp_init*/
0, /*tp_alloc*/
column_new, /*tp_new*/
};

View File

@ -29,6 +29,7 @@
#include "psycopg/connection.h"
#include "psycopg/cursor.h"
#include "psycopg/pqpath.h"
#include "psycopg/conninfo.h"
#include "psycopg/lobject.h"
#include "psycopg/green.h"
#include "psycopg/xid.h"
@ -757,6 +758,7 @@ psyco_conn_readonly_get(connectionObject *self)
break;
}
Py_XINCREF(rv);
return rv;
}
@ -803,6 +805,7 @@ psyco_conn_deferrable_get(connectionObject *self)
break;
}
Py_XINCREF(rv);
return rv;
}
@ -1005,6 +1008,20 @@ psyco_conn_get_backend_pid(connectionObject *self)
return PyInt_FromLong((long)PQbackendPID(self->pgconn));
}
/* get info about the connection */
#define psyco_conn_info_get_doc \
"info -- Get connection info."
static PyObject *
psyco_conn_info_get(connectionObject *self)
{
return PyObject_CallFunctionObjArgs(
(PyObject *)&connInfoType, (PyObject *)self, NULL);
}
/* reset the currect connection */
#define psyco_conn_reset_doc \
@ -1258,6 +1275,9 @@ static struct PyGetSetDef connectionObject_getsets[] = {
(getter)psyco_conn_deferrable_get,
(setter)psyco_conn_deferrable_set,
psyco_conn_deferrable_doc },
{ "info",
(getter)psyco_conn_info_get, NULL,
psyco_conn_info_get_doc },
{NULL}
};
#undef EXCEPTION_GETTER

40
psycopg/conninfo.h Normal file
View File

@ -0,0 +1,40 @@
/* connection.h - definition for the psycopg ConnectionInfo type
*
* Copyright (C) 2018 Daniele Varrazzo <daniele.varrazzo@gmail.com>
*
* This file is part of psycopg.
*
* psycopg2 is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* In addition, as a special exception, the copyright holders give
* permission to link this program with the OpenSSL library (or with
* modified versions of OpenSSL that use the same license as OpenSSL),
* and distribute linked combinations including the two.
*
* You must obey the GNU Lesser General Public License in all respects for
* all of the code used other than OpenSSL.
*
* psycopg2 is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*/
#ifndef PSYCOPG_CONNINFO_H
#define PSYCOPG_CONNINFO_H 1
#include "psycopg/connection.h"
extern HIDDEN PyTypeObject connInfoType;
typedef struct {
PyObject_HEAD
connectionObject *conn;
} connInfoObject;
#endif /* PSYCOPG_CONNINFO_H */

614
psycopg/conninfo_type.c Normal file
View File

@ -0,0 +1,614 @@
/* conninfo_type.c - present information about the libpq connection
*
* Copyright (C) 2018 Daniele Varrazzo <daniele.varrazzo@gmail.com>
*
* This file is part of psycopg.
*
* psycopg2 is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* In addition, as a special exception, the copyright holders give
* permission to link this program with the OpenSSL library (or with
* modified versions of OpenSSL that use the same license as OpenSSL),
* and distribute linked combinations including the two.
*
* You must obey the GNU Lesser General Public License in all respects for
* all of the code used other than OpenSSL.
*
* psycopg2 is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*/
#define PSYCOPG_MODULE
#include "psycopg/psycopg.h"
#include "psycopg/conninfo.h"
static const char connInfoType_doc[] =
"Details about the native PostgreSQL database connection.\n"
"\n"
"This class exposes several `informative functions`__ about the status\n"
"of the libpq connection.\n"
"\n"
"Objects of this class are exposed as the `connection.info` attribute.\n"
"\n"
".. __: https://www.postgresql.org/docs/current/static/libpq-status.html";
static const char dbname_doc[] =
"The database name of the connection.\n"
"\n"
".. seealso:: libpq docs for `PQdb()`__ for details.\n"
".. __: https://www.postgresql.org/docs/current/static/libpq-status.html"
"#LIBPQ-PQDB";
static PyObject *
dbname_get(connInfoObject *self)
{
const char *val;
val = PQdb(self->conn->pgconn);
if (!val) {
Py_RETURN_NONE;
}
return conn_text_from_chars(self->conn, val);
}
static const char user_doc[] =
"The user name of the connection.\n"
"\n"
".. seealso:: libpq docs for `PQuser()`__ for details.\n"
".. __: https://www.postgresql.org/docs/current/static/libpq-status.html"
"#LIBPQ-PQUSER";
static PyObject *
user_get(connInfoObject *self)
{
const char *val;
val = PQuser(self->conn->pgconn);
if (!val) {
Py_RETURN_NONE;
}
return conn_text_from_chars(self->conn, val);
}
static const char password_doc[] =
"The password of the connection.\n"
"\n"
".. seealso:: libpq docs for `PQpass()`__ for details.\n"
".. __: https://www.postgresql.org/docs/current/static/libpq-status.html"
"#LIBPQ-PQPASS";
static PyObject *
password_get(connInfoObject *self)
{
const char *val;
val = PQpass(self->conn->pgconn);
if (!val) {
Py_RETURN_NONE;
}
return conn_text_from_chars(self->conn, val);
}
static const char host_doc[] =
"The server host name of the connection.\n"
"\n"
"This can be a host name, an IP address, or a directory path if the\n"
"connection is via Unix socket. (The path case can be distinguished\n"
"because it will always be an absolute path, beginning with ``/``.)\n"
"\n"
".. seealso:: libpq docs for `PQhost()`__ for details.\n"
".. __: https://www.postgresql.org/docs/current/static/libpq-status.html"
"#LIBPQ-PQHOST";
static PyObject *
host_get(connInfoObject *self)
{
const char *val;
val = PQhost(self->conn->pgconn);
if (!val) {
Py_RETURN_NONE;
}
return conn_text_from_chars(self->conn, val);
}
static const char port_doc[] =
"The port of the connection.\n"
"\n"
":type: `!int`\n"
"\n"
".. seealso:: libpq docs for `PQport()`__ for details.\n"
".. __: https://www.postgresql.org/docs/current/static/libpq-status.html"
"#LIBPQ-PQPORT";
static PyObject *
port_get(connInfoObject *self)
{
const char *val;
val = PQport(self->conn->pgconn);
if (!val || !val[0]) {
Py_RETURN_NONE;
}
return PyInt_FromString((char *)val, NULL, 10);
}
static const char options_doc[] =
"The command-line options passed in the the connection request.\n"
"\n"
".. seealso:: libpq docs for `PQoptions()`__ for details.\n"
".. __: https://www.postgresql.org/docs/current/static/libpq-status.html"
"#LIBPQ-PQOPTIONS";
static PyObject *
options_get(connInfoObject *self)
{
const char *val;
val = PQoptions(self->conn->pgconn);
if (!val) {
Py_RETURN_NONE;
}
return conn_text_from_chars(self->conn, val);
}
static const char status_doc[] =
"The status of the connection.\n"
"\n"
":type: `!int`\n"
"\n"
".. seealso:: libpq docs for `PQstatus()`__ for details.\n"
".. __: https://www.postgresql.org/docs/current/static/libpq-status.html"
"#LIBPQ-PQSTATUS";
static PyObject *
status_get(connInfoObject *self)
{
ConnStatusType val;
val = PQstatus(self->conn->pgconn);
return PyInt_FromLong((long)val);
}
static const char transaction_status_doc[] =
"The current in-transaction status of the connection.\n"
"\n"
"Symbolic constants for the values are defined in the module\n"
"`psycopg2.extensions`: see :ref:`transaction-status-constants` for the\n"
"available values.\n"
"\n"
":type: `!int`\n"
"\n"
".. seealso:: libpq docs for `PQtransactionStatus()`__ for details.\n"
".. __: https://www.postgresql.org/docs/current/static/libpq-status.html"
"#LIBPQ-PQTRANSACTIONSTATUS";
static PyObject *
transaction_status_get(connInfoObject *self)
{
PGTransactionStatusType val;
val = PQtransactionStatus(self->conn->pgconn);
return PyInt_FromLong((long)val);
}
static const char parameter_status_doc[] =
"Looks up a current parameter setting of the server.\n"
"\n"
":param name: The name of the parameter to return.\n"
":type name: `!str`\n"
":return: The parameter value, `!None` if the parameter is unknown.\n"
":rtype: `!str`\n"
"\n"
".. seealso:: libpq docs for `PQparameterStatus()`__ for details.\n"
".. __: https://www.postgresql.org/docs/current/static/libpq-status.html"
"#LIBPQ-PQPARAMETERSTATUS";
static PyObject *
parameter_status(connInfoObject *self, PyObject *args, PyObject *kwargs)
{
static char *kwlist[] = {"name", NULL};
const char *name;
const char *val;
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s", kwlist, &name)) {
return NULL;
}
val = PQparameterStatus(self->conn->pgconn, name);
if (!val) {
Py_RETURN_NONE;
}
else {
return conn_text_from_chars(self->conn, val);
}
}
static const char protocol_version_doc[] =
"The frontend/backend protocol being used.\n"
"\n"
":type: `!int`\n"
"\n"
".. seealso:: libpq docs for `PQprotocolVersion()`__ for details.\n"
".. __: https://www.postgresql.org/docs/current/static/libpq-status.html"
"#LIBPQ-PQPROTOCOLVERSION";
static PyObject *
protocol_version_get(connInfoObject *self)
{
int val;
val = PQprotocolVersion(self->conn->pgconn);
return PyInt_FromLong((long)val);
}
static const char server_version_doc[] =
"Returns an integer representing the server version.\n"
"\n"
":type: `!int`\n"
"\n"
".. seealso:: libpq docs for `PQserverVersion()`__ for details.\n"
".. __: https://www.postgresql.org/docs/current/static/libpq-status.html"
"#LIBPQ-PQSERVERVERSION";
static PyObject *
server_version_get(connInfoObject *self)
{
int val;
val = PQserverVersion(self->conn->pgconn);
return PyInt_FromLong((long)val);
}
static const char error_message_doc[] =
"The error message most recently generated by an operation on the connection.\n"
"\n"
"`!None` if there is no current message.\n"
"\n"
".. seealso:: libpq docs for `PQerrorMessage()`__ for details.\n"
".. __: https://www.postgresql.org/docs/current/static/libpq-status.html"
"#LIBPQ-PQERRORMESSAGE";
static PyObject *
error_message_get(connInfoObject *self)
{
const char *val;
val = PQerrorMessage(self->conn->pgconn);
if (!val || !val[0]) {
Py_RETURN_NONE;
}
return conn_text_from_chars(self->conn, val);
}
static const char socket_doc[] =
"The file descriptor number of the connection socket to the server.\n"
"\n"
":type: `!int`\n"
"\n"
".. seealso:: libpq docs for `PQsocket()`__ for details.\n"
".. __: https://www.postgresql.org/docs/current/static/libpq-status.html"
"#LIBPQ-PQSOCKET";
static PyObject *
socket_get(connInfoObject *self)
{
int val;
val = PQsocket(self->conn->pgconn);
return PyInt_FromLong((long)val);
}
static const char backend_pid_doc[] =
"The process ID (PID) of the backend process handling this connection.\n"
"\n"
":type: `!int`\n"
"\n"
".. seealso:: libpq docs for `PQbackendPID()`__ for details.\n"
".. __: https://www.postgresql.org/docs/current/static/libpq-status.html"
"#LIBPQ-PQBACKENDPID";
static PyObject *
backend_pid_get(connInfoObject *self)
{
int val;
val = PQbackendPID(self->conn->pgconn);
return PyInt_FromLong((long)val);
}
static const char needs_password_doc[] =
"The connection authentication method required a password, but none was available.\n"
"\n"
":type: `!bool`\n"
"\n"
".. seealso:: libpq docs for `PQconnectionNeedsPassword()`__ for details.\n"
".. __: https://www.postgresql.org/docs/current/static/libpq-status.html"
"#LIBPQ-PQCONNECTIONNEEDSPASSWORD";
static PyObject *
needs_password_get(connInfoObject *self)
{
PyObject *rv;
rv = PQconnectionNeedsPassword(self->conn->pgconn) ? Py_True : Py_False;
Py_INCREF(rv);
return rv;
}
static const char used_password_doc[] =
"The connection authentication method used a password.\n"
"\n"
":type: `!bool`\n"
"\n"
".. seealso:: libpq docs for `PQconnectionUsedPassword()`__ for details.\n"
".. __: https://www.postgresql.org/docs/current/static/libpq-status.html"
"#LIBPQ-PQCONNECTIONUSEDPASSWORD";
static PyObject *
used_password_get(connInfoObject *self)
{
PyObject *rv;
rv = PQconnectionUsedPassword(self->conn->pgconn) ? Py_True : Py_False;
Py_INCREF(rv);
return rv;
}
static const char ssl_in_use_doc[] =
"`!True` if the connection uses SSL, `!False` if not.\n"
"\n"
"Only available if psycopg was built with libpq >= 9.5; raise\n"
"`~psycopg2.NotSupportedError` otherwise.\n"
"\n"
":type: `!bool`\n"
"\n"
".. seealso:: libpq docs for `PQsslInUse()`__ for details.\n"
".. __: https://www.postgresql.org/docs/current/static/libpq-status.html"
"#LIBPQ-PQSSLINUSE";
static PyObject *
ssl_in_use_get(connInfoObject *self)
{
PyObject *rv = NULL;
#if PG_VERSION_NUM >= 90500
rv = PQsslInUse(self->conn->pgconn) ? Py_True : Py_False;
Py_INCREF(rv);
#else
PyErr_SetString(NotSupportedError,
"'ssl_in_use' not available in libpq < 9.5");
#endif
return rv;
}
static const char ssl_attribute_doc[] =
"Returns SSL-related information about the connection.\n"
"\n"
":param name: The name of the attribute to return.\n"
":type name: `!str`\n"
":return: The attribute value, `!None` if unknown.\n"
":rtype: `!str`\n"
"\n"
"Only available if psycopg was built with libpq >= 9.5; raise\n"
"`~psycopg2.NotSupportedError` otherwise.\n"
"\n"
"Valid names are available in `ssl_attribute_names`.\n"
"\n"
".. seealso:: libpq docs for `PQsslAttribute()`__ for details.\n"
".. __: https://www.postgresql.org/docs/current/static/libpq-status.html"
"#LIBPQ-PQSSLATTRIBUTE";
static PyObject *
ssl_attribute(connInfoObject *self, PyObject *args, PyObject *kwargs)
{
static char *kwlist[] = {"name", NULL};
const char *name;
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s", kwlist, &name)) {
return NULL;
}
#if PG_VERSION_NUM >= 90500
{
const char *val;
val = PQsslAttribute(self->conn->pgconn, name);
if (!val) {
Py_RETURN_NONE;
}
else {
return conn_text_from_chars(self->conn, val);
}
}
#else
PyErr_SetString(NotSupportedError,
"'ssl_attribute()' not available in libpq < 9.5");
return NULL;
#endif
}
static const char ssl_attribute_names_doc[] =
"The list of the SSL attribute names available.\n"
"\n"
":type: `!list` of `!str`\n"
"\n"
"Only available if psycopg was built with libpq >= 9.5; raise\n"
"`~psycopg2.NotSupportedError` otherwise.\n"
"\n"
".. seealso:: libpq docs for `PQsslAttributeNames()`__ for details.\n"
".. __: https://www.postgresql.org/docs/current/static/libpq-status.html"
"#LIBPQ-PQSSLATTRIBUTENAMES";
static PyObject *
ssl_attribute_names_get(connInfoObject *self)
{
#if PG_VERSION_NUM >= 90500
const char* const* names;
int i;
PyObject *l = NULL, *s = NULL, *rv = NULL;
names = PQsslAttributeNames(self->conn->pgconn);
if (!(l = PyList_New(0))) { goto exit; }
for (i = 0; names[i]; i++) {
if (!(s = conn_text_from_chars(self->conn, names[i]))) { goto exit; }
if (0 != PyList_Append(l, s)) { goto exit; }
Py_CLEAR(s);
}
rv = l;
l = NULL;
exit:
Py_XDECREF(l);
Py_XDECREF(s);
return rv;
#else
PyErr_SetString(NotSupportedError,
"'ssl_attribute_names not available in libpq < 9.5");
return NULL;
#endif
}
static struct PyGetSetDef connInfoObject_getsets[] = {
{ "dbname", (getter)dbname_get, NULL, (char *)dbname_doc },
{ "user", (getter)user_get, NULL, (char *)user_doc },
{ "password", (getter)password_get, NULL, (char *)password_doc },
{ "host", (getter)host_get, NULL, (char *)host_doc },
{ "port", (getter)port_get, NULL, (char *)port_doc },
{ "options", (getter)options_get, NULL, (char *)options_doc },
{ "status", (getter)status_get, NULL, (char *)status_doc },
{ "transaction_status", (getter)transaction_status_get, NULL,
(char *)transaction_status_doc },
{ "protocol_version", (getter)protocol_version_get, NULL,
(char *)protocol_version_doc },
{ "server_version", (getter)server_version_get, NULL,
(char *)server_version_doc },
{ "error_message", (getter)error_message_get, NULL,
(char *)error_message_doc },
{ "socket", (getter)socket_get, NULL, (char *)socket_doc },
{ "backend_pid", (getter)backend_pid_get, NULL, (char *)backend_pid_doc },
{ "used_password", (getter)used_password_get, NULL,
(char *)used_password_doc },
{ "needs_password", (getter)needs_password_get, NULL,
(char *)needs_password_doc },
{ "ssl_in_use", (getter)ssl_in_use_get, NULL,
(char *)ssl_in_use_doc },
{ "ssl_attribute_names", (getter)ssl_attribute_names_get, NULL,
(char *)ssl_attribute_names_doc },
{NULL}
};
static struct PyMethodDef connInfoObject_methods[] = {
{"ssl_attribute", (PyCFunction)ssl_attribute,
METH_VARARGS|METH_KEYWORDS, ssl_attribute_doc},
{"parameter_status", (PyCFunction)parameter_status,
METH_VARARGS|METH_KEYWORDS, parameter_status_doc},
{NULL}
};
/* initialization and finalization methods */
static PyObject *
conninfo_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
return type->tp_alloc(type, 0);
}
static int
conninfo_init(connInfoObject *self, PyObject *args, PyObject *kwds)
{
PyObject *conn = NULL;
if (!PyArg_ParseTuple(args, "O", &conn))
return -1;
if (!PyObject_TypeCheck(conn, &connectionType)) {
PyErr_SetString(PyExc_TypeError,
"The argument must be a psycopg2 connection");
return -1;
}
Py_INCREF(conn);
self->conn = (connectionObject *)conn;
return 0;
}
static void
conninfo_dealloc(connInfoObject* self)
{
Py_CLEAR(self->conn);
Py_TYPE(self)->tp_free((PyObject *)self);
}
/* object type */
PyTypeObject connInfoType = {
PyVarObject_HEAD_INIT(NULL, 0)
"psycopg2.extensions.ConnectionInfo",
sizeof(connInfoObject), 0,
(destructor)conninfo_dealloc, /*tp_dealloc*/
0, /*tp_print*/
0, /*tp_getattr*/
0, /*tp_setattr*/
0, /*tp_compare*/
0, /*tp_repr*/
0, /*tp_as_number*/
0, /*tp_as_sequence*/
0, /*tp_as_mapping*/
0, /*tp_hash */
0, /*tp_call*/
0, /*tp_str*/
0, /*tp_getattro*/
0, /*tp_setattro*/
0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /*tp_flags*/
connInfoType_doc, /*tp_doc*/
0, /*tp_traverse*/
0, /*tp_clear*/
0, /*tp_richcompare*/
0, /*tp_weaklistoffset*/
0, /*tp_iter*/
0, /*tp_iternext*/
connInfoObject_methods, /*tp_methods*/
0, /*tp_members*/
connInfoObject_getsets, /*tp_getset*/
0, /*tp_base*/
0, /*tp_dict*/
0, /*tp_descr_get*/
0, /*tp_descr_set*/
0, /*tp_dictoffset*/
(initproc)conninfo_init, /*tp_init*/
0, /*tp_alloc*/
conninfo_new, /*tp_new*/
};

View File

@ -29,8 +29,11 @@
#include "psycopg/diagnostics.h"
#include "psycopg/error.h"
/* These are new in PostgreSQL 9.3. Defining them here so that psycopg2 can
* use them with a 9.3+ server even if compiled against pre-9.3 headers. */
/* These constants are defined in src/include/postgres_ext.h but some may not
* be available with the libpq we currently support at compile time. */
/* Available from PG 9.3 */
#ifndef PG_DIAG_SCHEMA_NAME
#define PG_DIAG_SCHEMA_NAME 's'
#endif
@ -47,6 +50,11 @@
#define PG_DIAG_CONSTRAINT_NAME 'n'
#endif
/* Available from PG 9.6 */
#ifndef PG_DIAG_SEVERITY_NONLOCALIZED
#define PG_DIAG_SEVERITY_NONLOCALIZED 'V'
#endif
/* Retrieve an error string from the exception's cursor.
*
@ -70,6 +78,8 @@ psyco_diagnostics_get_field(diagnosticsObject *self, void *closure)
static struct PyGetSetDef diagnosticsObject_getsets[] = {
{ "severity", (getter)psyco_diagnostics_get_field, NULL,
NULL, (void*) PG_DIAG_SEVERITY },
{ "severity_nonlocalized", (getter)psyco_diagnostics_get_field, NULL,
NULL, (void*) PG_DIAG_SEVERITY_NONLOCALIZED },
{ "sqlstate", (getter)psyco_diagnostics_get_field, NULL,
NULL, (void*) PG_DIAG_SQLSTATE },
{ "message_primary", (getter)psyco_diagnostics_get_field, NULL,
@ -152,7 +162,7 @@ static const char diagnosticsType_doc[] =
"Please refer to the `PostgreSQL documentation`__ for the meaning of all"
" the attributes.\n\n"
".. |PQresultErrorField| replace:: `!PQresultErrorField()`\n"
".. _PQresultErrorField: http://www.postgresql.org/docs/current/static/"
".. _PQresultErrorField: https://www.postgresql.org/docs/current/static/"
"libpq-exec.html#LIBPQ-PQRESULTERRORFIELD\n"
".. __: PQresultErrorField_\n";

View File

@ -48,7 +48,7 @@ extern "C" {
"See `~psycopg2.extras.wait_select()` for an example of a wait callback\n" \
"implementation.\n" \
"\n" \
".. _Eventlet: http://eventlet.net/\n" \
".. _Eventlet: https://eventlet.net/\n" \
".. _gevent: http://www.gevent.org/\n"
HIDDEN PyObject *psyco_set_wait_callback(PyObject *self, PyObject *obj);

View File

@ -40,7 +40,7 @@ static const char notify_doc[] =
static const char pid_doc[] =
"The ID of the backend process that sent the notification.\n\n"
"Note: if the sending session was handled by Psycopg, you can use\n"
"`~connection.get_backend_pid()` to know its PID.";
"`~connection.info.backend_pid` to know its PID.";
static const char channel_doc[] =
"The name of the channel to which the notification was sent.";

View File

@ -41,6 +41,7 @@
#include "psycopg/typecast.h"
#include "psycopg/pgtypes.h"
#include "psycopg/error.h"
#include "psycopg/column.h"
#include "psycopg/libpq_support.h"
#include "libpq-fe.h"
@ -76,13 +77,57 @@ strip_severity(const char *msg)
return msg;
}
/* Return a Python exception from a SQLSTATE from psycopg2.errors */
BORROWED static PyObject *
exception_from_module(const char *sqlstate)
{
PyObject *rv = NULL;
PyObject *m = NULL;
PyObject *map = NULL;
if (!(m = PyImport_ImportModule("psycopg2.errors"))) { goto exit; }
if (!(map = PyObject_GetAttrString(m, "_by_sqlstate"))) { goto exit; }
if (!PyDict_Check(map)) {
Dprintf("'psycopg2.errors._by_sqlstate' is not a dict!");
goto exit;
}
/* get the sqlstate class (borrowed reference), or fail trying. */
rv = PyDict_GetItemString(map, sqlstate);
exit:
/* We exit with a borrowed object, or a NULL but no error
* If an error did happen in this function, we don't want to clobber the
* database error. So better reporting it, albeit with the wrong class. */
PyErr_Clear();
Py_XDECREF(map);
Py_XDECREF(m);
return rv;
}
/* Returns the Python exception corresponding to an SQLSTATE error
code. A list of error codes can be found at:
http://www.postgresql.org/docs/current/static/errcodes-appendix.html */
https://www.postgresql.org/docs/current/static/errcodes-appendix.html */
BORROWED static PyObject *
exception_from_sqlstate(const char *sqlstate)
{
PyObject *exc;
/* First look up an exception of the proper class from the Python module */
exc = exception_from_module(sqlstate);
if (exc) {
return exc;
}
else {
PyErr_Clear();
}
/*
* IMPORTANT: if you change anything in this function you should change
* make_errors.py accordingly.
*/
switch (sqlstate[0]) {
case '0':
switch (sqlstate[1]) {
@ -1106,12 +1151,13 @@ pq_send_query(connectionObject *conn, const char *query)
* The function will block only if a command is active and the
* necessary response data has not yet been read by PQconsumeInput.
*
* The result should be disposed using PQclear()
* The result should be disposed of using PQclear()
*/
PGresult *
pq_get_last_result(connectionObject *conn)
{
PGresult *result = NULL, *res;
ExecStatusType status;
/* Read until PQgetResult gives a NULL */
while (NULL != (res = PQgetResult(conn->pgconn))) {
@ -1124,11 +1170,15 @@ pq_get_last_result(connectionObject *conn)
PQclear(result);
}
result = res;
status = PQresultStatus(result);
Dprintf("pq_get_last_result: got result %s", PQresStatus(status));
/* After entering copy both mode, libpq will make a phony
/* After entering copy mode, libpq will make a phony
* PGresult for us every time we query for it, so we need to
* break out of this endless loop. */
if (PQresultStatus(result) == PGRES_COPY_BOTH) {
if (status == PGRES_COPY_BOTH
|| status == PGRES_COPY_OUT
|| status == PGRES_COPY_IN) {
break;
}
}
@ -1201,12 +1251,17 @@ _pq_fetch_tuples(cursorObject *curs)
Oid ftype = PQftype(curs->pgres, i);
int fsize = PQfsize(curs->pgres, i);
int fmod = PQfmod(curs->pgres, i);
Oid ftable = PQftable(curs->pgres, i);
int ftablecol = PQftablecol(curs->pgres, i);
PyObject *dtitem = NULL;
columnObject *column = NULL;
PyObject *type = NULL;
PyObject *cast = NULL;
if (!(dtitem = PyTuple_New(7))) { goto exit; }
if (!(column = (columnObject *)PyObject_CallObject(
(PyObject *)&columnType, NULL))) {
goto exit;
}
/* fill the right cast function by accessing three different dictionaries:
- the per-cursor dictionary, if available (can be NULL or None)
@ -1243,20 +1298,16 @@ _pq_fetch_tuples(cursorObject *curs)
curs->conn, PQfname(curs->pgres, i)))) {
goto err_for;
}
PyTuple_SET_ITEM(dtitem, 0, tmp);
column->name = tmp;
}
PyTuple_SET_ITEM(dtitem, 1, type);
column->type_code = type;
type = NULL;
/* 2/ display size is the maximum size of this field result tuples. */
if (dsize && dsize[i] >= 0) {
PyObject *tmp;
if (!(tmp = PyInt_FromLong(dsize[i]))) { goto err_for; }
PyTuple_SET_ITEM(dtitem, 2, tmp);
}
else {
Py_INCREF(Py_None);
PyTuple_SET_ITEM(dtitem, 2, Py_None);
column->display_size = tmp;
}
/* 3/ size on the backend */
@ -1265,18 +1316,18 @@ _pq_fetch_tuples(cursorObject *curs)
if (ftype == NUMERICOID) {
PyObject *tmp;
if (!(tmp = PyInt_FromLong((fmod >> 16)))) { goto err_for; }
PyTuple_SET_ITEM(dtitem, 3, tmp);
column->internal_size = tmp;
}
else { /* If variable length record, return maximum size */
PyObject *tmp;
if (!(tmp = PyInt_FromLong(fmod))) { goto err_for; }
PyTuple_SET_ITEM(dtitem, 3, tmp);
column->internal_size = tmp;
}
}
else {
PyObject *tmp;
if (!(tmp = PyInt_FromLong(fsize))) { goto err_for; }
PyTuple_SET_ITEM(dtitem, 3, tmp);
column->internal_size = tmp;
}
/* 4,5/ scale and precision */
@ -1286,40 +1337,35 @@ _pq_fetch_tuples(cursorObject *curs)
if (!(tmp = PyInt_FromLong((fmod >> 16) & 0xFFFF))) {
goto err_for;
}
PyTuple_SET_ITEM(dtitem, 4, tmp);
column->precision = tmp;
if (!(tmp = PyInt_FromLong(fmod & 0xFFFF))) {
PyTuple_SET_ITEM(dtitem, 5, tmp);
goto err_for;
}
PyTuple_SET_ITEM(dtitem, 5, tmp);
}
else {
Py_INCREF(Py_None);
PyTuple_SET_ITEM(dtitem, 4, Py_None);
Py_INCREF(Py_None);
PyTuple_SET_ITEM(dtitem, 5, Py_None);
column->scale = tmp;
}
/* 6/ FIXME: null_ok??? */
Py_INCREF(Py_None);
PyTuple_SET_ITEM(dtitem, 6, Py_None);
/* Convert into a namedtuple if available */
if (Py_None != psyco_DescriptionType) {
PyObject *tmp = dtitem;
dtitem = PyObject_CallObject(psyco_DescriptionType, tmp);
Py_DECREF(tmp);
if (NULL == dtitem) { goto err_for; }
/* table_oid, table_column */
if (ftable != InvalidOid) {
PyObject *tmp;
if (!(tmp = PyInt_FromLong((long)ftable))) { goto err_for; }
column->table_oid = tmp;
}
PyTuple_SET_ITEM(description, i, dtitem);
dtitem = NULL;
if (ftablecol > 0) {
PyObject *tmp;
if (!(tmp = PyInt_FromLong((long)ftablecol))) { goto err_for; }
column->table_column = tmp;
}
PyTuple_SET_ITEM(description, i, (PyObject *)column);
column = NULL;
continue;
err_for:
Py_XDECREF(type);
Py_XDECREF(dtitem);
Py_XDECREF(column);
goto exit;
}

View File

@ -32,6 +32,7 @@
#include "psycopg/replication_cursor.h"
#include "psycopg/replication_message.h"
#include "psycopg/green.h"
#include "psycopg/column.h"
#include "psycopg/lobject.h"
#include "psycopg/notify.h"
#include "psycopg/xid.h"
@ -39,6 +40,7 @@
#include "psycopg/microprotocols.h"
#include "psycopg/microprotocols_proto.h"
#include "psycopg/error.h"
#include "psycopg/conninfo.h"
#include "psycopg/diagnostics.h"
#include "psycopg/adapter_qstring.h"
@ -69,9 +71,6 @@ HIDDEN int psycopg_debug_enabled = 0;
/* Python representation of SQL NULL */
HIDDEN PyObject *psyco_null = NULL;
/* The type of the cursor.description items */
HIDDEN PyObject *psyco_DescriptionType = NULL;
/* macro trick to stringify a macro expansion */
#define xstr(s) str(s)
#define str(s) #s
@ -839,63 +838,6 @@ psyco_GetDecimalType(void)
}
/* Create a namedtuple for cursor.description items
*
* Return None in case of expected errors (e.g. namedtuples not available)
* NULL in case of errors to propagate.
*/
static PyObject *
psyco_make_description_type(void)
{
PyObject *coll = NULL;
PyObject *nt = NULL;
PyTypeObject *t = NULL;
PyObject *s = NULL;
PyObject *rv = NULL;
/* Try to import collections.namedtuple */
if (!(coll = PyImport_ImportModule("collections"))) {
Dprintf("psyco_make_description_type: collections import failed");
goto error;
}
if (!(nt = PyObject_GetAttrString(coll, "namedtuple"))) {
Dprintf("psyco_make_description_type: no collections.namedtuple");
goto error;
}
/* Build the namedtuple */
if(!(t = (PyTypeObject *)PyObject_CallFunction(nt, "ss", "Column",
"name type_code display_size internal_size precision scale null_ok"))) {
goto exit;
}
/* Export the tuple on the extensions module
* Required to guarantee picklability on Py > 3.3 (see Python issue 21374)
* for previous Py version the module is psycopg2 anyway but for consistency
* we'd rather expose it from the extensions module. */
if (!(s = Text_FromUTF8("psycopg2.extensions"))) { goto exit; }
if (0 > PyDict_SetItemString(t->tp_dict, "__module__", s)) { goto exit; }
rv = (PyObject *)t;
t = NULL;
exit:
Py_XDECREF(coll);
Py_XDECREF(nt);
Py_XDECREF((PyObject *)t);
Py_XDECREF(s);
return rv;
error:
/* controlled error: we will fall back to regular tuples. Return None. */
PyErr_Clear();
rv = Py_None;
Py_INCREF(rv);
goto exit;
}
/** method table and module initialization **/
static PyMethodDef psycopgMethods[] = {
@ -1041,6 +983,9 @@ INIT_MODULE(_psycopg)(void)
Py_TYPE(&chunkType) = &PyType_Type;
if (PyType_Ready(&chunkType) == -1) goto exit;
Py_TYPE(&columnType) = &PyType_Type;
if (PyType_Ready(&columnType) == -1) goto exit;
Py_TYPE(&notifyType) = &PyType_Type;
if (PyType_Ready(&notifyType) == -1) goto exit;
@ -1051,6 +996,9 @@ INIT_MODULE(_psycopg)(void)
errorType.tp_base = (PyTypeObject *)PyExc_StandardError;
if (PyType_Ready(&errorType) == -1) goto exit;
Py_TYPE(&connInfoType) = &PyType_Type;
if (PyType_Ready(&connInfoType) == -1) goto exit;
Py_TYPE(&diagnosticsType) = &PyType_Type;
if (PyType_Ready(&diagnosticsType) == -1) goto exit;
@ -1119,7 +1067,6 @@ INIT_MODULE(_psycopg)(void)
if (!(psycoEncodings = PyDict_New())) { goto exit; }
if (0 != psyco_encodings_fill(psycoEncodings)) { goto exit; }
psyco_null = Bytes_FromString("NULL");
if (!(psyco_DescriptionType = psyco_make_description_type())) { goto exit; }
/* set some module's parameters */
PyModule_AddStringConstant(module, "__version__", xstr(PSYCOPG_VERSION));
@ -1138,8 +1085,10 @@ INIT_MODULE(_psycopg)(void)
PyModule_AddObject(module, "ReplicationCursor", (PyObject*)&replicationCursorType);
PyModule_AddObject(module, "ReplicationMessage", (PyObject*)&replicationMessageType);
PyModule_AddObject(module, "ISQLQuote", (PyObject*)&isqlquoteType);
PyModule_AddObject(module, "Column", (PyObject*)&columnType);
PyModule_AddObject(module, "Notify", (PyObject*)&notifyType);
PyModule_AddObject(module, "Xid", (PyObject*)&xidType);
PyModule_AddObject(module, "ConnectionInfo", (PyObject*)&connInfoType);
PyModule_AddObject(module, "Diagnostics", (PyObject*)&diagnosticsType);
PyModule_AddObject(module, "AsIs", (PyObject*)&asisType);
PyModule_AddObject(module, "Binary", (PyObject*)&binaryType);
@ -1150,7 +1099,6 @@ INIT_MODULE(_psycopg)(void)
PyModule_AddObject(module, "List", (PyObject*)&listType);
PyModule_AddObject(module, "QuotedString", (PyObject*)&qstringType);
PyModule_AddObject(module, "lobject", (PyObject*)&lobjectType);
PyModule_AddObject(module, "Column", psyco_DescriptionType);
/* encodings dictionary in module dictionary */
PyModule_AddObject(module, "encodings", psycoEncodings);

View File

@ -78,6 +78,7 @@ typedef unsigned long Py_uhash_t;
#define PyInt_Check PyLong_Check
#define PyInt_AsLong PyLong_AsLong
#define PyInt_FromLong PyLong_FromLong
#define PyInt_FromString PyLong_FromString
#define PyInt_FromSsize_t PyLong_FromSsize_t
#define PyExc_StandardError PyExc_Exception
#define PyString_FromFormat PyUnicode_FromFormat

View File

@ -212,7 +212,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/current/static/datatype-binary.html
* https://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.
@ -262,7 +262,7 @@ exit:
/* Parse a bytea output buffer encoded in 'escape' format.
*
* the format is described in
* http://www.postgresql.org/docs/current/static/datatype-binary.html
* https://www.postgresql.org/docs/current/static/datatype-binary.html
*
* Parse the buffer in 'bufin', whose length is 'sizein'.
* Return a new buffer allocated by PyMem_Malloc and set 'sizeout' to its size.

View File

@ -1,30 +0,0 @@
import psycopg2
conn = psycopg2.connect("port=5433 dbname=test")
curs = conn.cursor()
#curs.execute("SELECT ARRAY[1,2,3] AS foo")
#print curs.fetchone()[0]
#curs.execute("SELECT ARRAY['1','2','3'] AS foo")
#print curs.fetchone()[0]
#curs.execute("""SELECT ARRAY[',','"','\\\\'] AS foo""")
#d = curs.fetchone()[0]
#print d, '->', d[0], d[1], d[2]
#curs.execute("SELECT ARRAY[ARRAY[1,2],ARRAY[3,4]] AS foo")
#print curs.fetchone()[0]
#curs.execute("SELECT ARRAY[ARRAY[now(), now()], ARRAY[now(), now()]] AS foo")
#print curs.description
#print curs.fetchone()[0]
#curs.execute("SELECT 1 AS foo, ARRAY[1,2] AS bar")
#print curs.fetchone()
#curs.execute("SELECT * FROM test()")
#print curs.fetchone()
curs.execute("SELECT %s", ([1,2,None],))
print(curs.fetchone())

View File

@ -1,35 +0,0 @@
import datetime
import time
import psycopg2
#d = datetime.timedelta(12, 100, 9876)
#print d.days, d.seconds, d.microseconds
#print psycopg.adapt(d).getquoted()
conn = psycopg2.connect("dbname=test_unicode")
conn.set_client_encoding("xxx")
curs = conn.cursor()
#curs.execute("SELECT 1.0 AS foo")
#print curs.fetchmany(2)
#print curs.fetchall()
def sleep(curs):
while not curs.isready():
print(".")
time.sleep(.1)
#curs.execute("""
# DECLARE zz INSENSITIVE SCROLL CURSOR WITH HOLD FOR
# SELECT now();
# FOR READ ONLY;""", async = 1)
curs.execute("SELECT now() AS foo", async=1)
sleep(curs)
print(curs.fetchall())
#curs.execute("""
# FETCH FORWARD 1 FROM zz;""", async = 1)
curs.execute("SELECT now() AS bar", async=1)
print(curs.fetchall())
curs.execute("SELECT now() AS bar")
sleep(curs)

View File

@ -1,25 +0,0 @@
#!/usr/bin/env python
#import psycopg as db
import psycopg2 as db
import threading
import time
import sys
def query_worker(dsn):
conn = db.connect(dsn)
cursor = conn.cursor()
while True:
cursor.execute("select * from pg_class")
while True:
row = cursor.fetchone()
if row is None:
break
if len(sys.argv) != 2:
print('usage: %s DSN' % sys.argv[0])
sys.exit(1)
th = threading.Thread(target=query_worker, args=(sys.argv[1],))
th.setDaemon(True)
th.start()
time.sleep(1)

View File

@ -1,15 +0,0 @@
import psycopg2
import psycopg2.extensions
DEC2FLOAT = psycopg2.extensions.new_type(
psycopg2._psycopg.DECIMAL.values,
'DEC2FLOAT',
psycopg2.extensions.FLOAT)
psycopg2.extensions.register_type(DEC2FLOAT)
o = psycopg2.connect("dbname=test")
c = o.cursor()
c.execute("SELECT NULL::decimal(10,2)")
n = c.fetchone()[0]
print(n, type(n))

View File

@ -1,18 +0,0 @@
import psycopg2
con = psycopg2.connect("dbname=test")
cur = con.cursor()
cur.execute("SELECT %s::regtype::oid", ('bytea', ))
print(cur.fetchone()[0])
# 17
cur.execute("CREATE DOMAIN thing AS bytea")
cur.execute("SELECT %s::regtype::oid", ('thing', ))
print(cur.fetchone()[0])
#62148
cur.execute("CREATE TABLE thingrel (thingcol thing)")
cur.execute("SELECT * FROM thingrel")
print(cur.description)
#(('thingcol', 17, None, -1, None, None, None),)

View File

@ -1,18 +0,0 @@
import psycopg2
o = psycopg2.connect("dbname=test")
c = o.cursor()
def sql():
c.execute("SELECT 1.23 AS foo")
print(1, c.fetchone())
#print c.description
c.execute("SELECT 1.23::float AS foo")
print(2, c.fetchone())
#print c.description
print("BEFORE")
sql()
import gtk
print("AFTER")
sql()

View File

@ -1,13 +0,0 @@
import psycopg2
import psycopg2.extras
conn = psycopg2.connect("dbname=test")
curs = conn.cursor(cursor_factory=psycopg2.extras.DictCursor)
curs.execute("SELECT '2005-2-12'::date AS foo, 'boo!' as bar")
for x in curs.fetchall():
print(type(x), x[0], x[1], x['foo'], x['bar'])
curs.execute("SELECT '2005-2-12'::date AS foo, 'boo!' as bar")
for x in curs:
print(type(x), x[0], x[1], x['foo'], x['bar'])

View File

@ -1,82 +0,0 @@
"""
script: test_leak.py
This script attempts to repeatedly insert the same list of rows into
the database table, causing a duplicate key error to occur. It will
then roll back the transaction and try again.
Database table schema:
-- CREATE TABLE t (foo TEXT PRIMARY KEY);
There are two ways to run the script, which will launch one of the
two functions:
# leak() will cause increasingly more RAM to be used by the script.
$ python <script_nam> leak
# noleak() does not have the RAM usage problem. The only difference
# between it and leak() is that 'rows' is created once, before the loop.
$ python <script_name> noleak
Use Control-C to quit the script.
"""
import sys
import psycopg2
DB_NAME = 'test'
connection = psycopg2.connect(database=DB_NAME)
cursor = connection.cursor()
# Uncomment the following if table 't' does not exist
create_table = """CREATE TABLE t (foo TEXT PRIMARY KEY)"""
cursor.execute(create_table)
insert = """INSERT INTO t VALUES (%(foo)s)"""
def leak():
"""rows created in each loop run"""
count = 0
while 1:
try:
rows = []
for i in range(1, 100):
row = {'foo': i}
rows.append(row)
count += 1
print("loop count:", count)
cursor.executemany(insert, rows)
connection.commit()
except psycopg2.IntegrityError:
connection.rollback()
def noleak():
"""rows created once, before the loop"""
rows = []
for i in range(1, 100):
row = {'foo': i}
rows.append(row)
count = 0
while 1:
try:
count += 1
print("loop count:", count)
cursor.executemany(insert, rows)
connection.commit()
except psycopg2.IntegrityError:
connection.rollback()
usage = "%s requires one argument: 'leak' or 'noleak'" % sys.argv[0]
try:
if 'leak' == sys.argv[1]:
run_function = leak
elif 'noleak' == sys.argv[1]:
run_function = noleak
else:
print(usage)
sys.exit()
except IndexError:
print(usage)
sys.exit()
# Run leak() or noleak(), whichever was indicated on the command line
run_function()

View File

@ -1,43 +0,0 @@
#!/usr/bin/env python
"""
Test if the arguments object can be used with both positional and keyword
arguments.
"""
class O(object):
def __init__(self, *args, **kwds):
self.args = args
self.kwds = kwds
def __getitem__(self, k):
if isinstance(k, int):
return self.args[k]
else:
return self.kwds[k]
o = O('R%', second='S%')
print(o[0])
print(o['second'])
#-------------------------------------------------------------------------------
import psycopg2 as dbapi
conn = dbapi.connect(database='test')
cursor = conn.cursor()
cursor.execute("""
SELECT * FROM location_pretty
WHERE keyname LIKE %s OR keyname LIKE %(second)s
""", (o,))
for row in cursor:
print(row)

View File

@ -1,31 +0,0 @@
import psycopg2
import psycopg2.extensions
class Portal(psycopg2.extensions.cursor):
def __init__(self, name, curs):
psycopg2.extensions.cursor.__init__(
self, curs.connection, '"'+name+'"')
CURSOR = psycopg2.extensions.new_type((1790,), "CURSOR", Portal)
psycopg2.extensions.register_type(CURSOR)
conn = psycopg2.connect("dbname=test")
curs = conn.cursor()
curs.execute("SELECT reffunc2()")
portal = curs.fetchone()[0]
print(portal.fetchone())
print(portal.fetchmany(2))
portal.scroll(0, 'absolute')
print(portal.fetchall())
#print curs.rowcount
#print curs.statusmessage
#print curs.fetchone()
#print curs.rowcount
#print curs.statusmessage
#print curs.fetchone()
#print curs.rowcount
#print curs.statusmessage

View File

@ -1,12 +0,0 @@
class B(object):
def __init__(self, x):
if x: self._o = True
else: self._o = False
def __getattribute__(self, attr):
print("ga called", attr)
return object.__getattribute__(self, attr)
def _sqlquote(self):
if self._o:
return 'It is True'
else:
return 'It is False'

View File

@ -1,11 +0,0 @@
import psycopg2
import threading, os, time, gc
for i in range(20000):
conn = psycopg2.connect('dbname=test')
del conn
if i%200 == 0:
datafile = os.popen('ps -p %s -o rss' % os.getpid())
line = datafile.readlines(2)[1].strip()
datafile.close()
print(str(i) + '\t' + line)

View File

@ -1,42 +0,0 @@
import psycopg2
import threading, os, time, gc
super_lock = threading.Lock()
def f():
try:
conn = psycopg2.connect('dbname=testx')
#c = db.cursor()
#c.close()
#conn.close()
del conn
except:
pass
#print "ERROR"
def g():
n = 30
k = 0
i = 1
while i > 0:
while n > 0:
threading.Thread(target=f).start()
time.sleep(0.001)
threading.Thread(target=f).start()
time.sleep(0.001)
threading.Thread(target=f).start()
n -= 1
while threading.activeCount() > 1:
time.sleep(0.01)
datafile = os.popen('ps -p %s -o rss' % os.getpid())
line = datafile.readlines(2)[1].strip()
datafile.close()
n = 30
print(str(k*n) + '\t' + line)
k += 1
while threading.activeCount()>1:
pass
g()

View File

@ -1,49 +0,0 @@
import datetime
import time
import psycopg2
#d = datetime.timedelta(12, 100, 9876)
#print d.days, d.seconds, d.microseconds
#print psycopg.adapt(d).getquoted()
conn = psycopg2.connect("dbname=test")
#conn.set_client_encoding("xxx")
curs = conn.cursor()
curs.execute("SELECT '2005-2-12'::date AS foo")
print(curs.fetchall())
curs.execute("SELECT '10:23:60'::time AS foo")
print(curs.fetchall())
curs.execute("SELECT '10:23:59.895342'::time AS foo")
print(curs.fetchall())
curs.execute("SELECT '0:0:12.31423'::time with time zone AS foo")
print(curs.fetchall())
curs.execute("SELECT '0:0:12+01:30'::time with time zone AS foo")
print(curs.fetchall())
curs.execute("SELECT '2005-2-12 10:23:59.895342'::timestamp AS foo")
print(curs.fetchall())
curs.execute("SELECT '2005-2-12 10:23:59.895342'::timestamp with time zone AS foo")
print(curs.fetchall())
#print curs.fetchmany(2)
#print curs.fetchall()
def sleep(curs):
while not curs.isready():
print(".")
time.sleep(.1)
#curs.execute("""
# DECLARE zz INSENSITIVE SCROLL CURSOR WITH HOLD FOR
# SELECT now();
# FOR READ ONLY;""", async = 1)
#curs.execute("SELECT now() AS foo", async=1);
#sleep(curs)
#print curs.fetchall()
#curs.execute("""
# FETCH FORWARD 1 FROM zz;""", async = 1)
#curs.execute("SELECT now() AS bar", async=1);
#print curs.fetchall()
#curs.execute("SELECT now() AS bar");
#sleep(curs)

View File

@ -1,8 +0,0 @@
import psycopg2
import psycopg2.extras
conn = psycopg2.connect("dbname=test")
curs = conn.cursor()
curs.execute("SELECT true AS foo WHERE 'a' in %s", (("aa", "bb"),))
print(curs.fetchall())
print(curs.query)

File diff suppressed because it is too large Load Diff

View File

@ -1,42 +0,0 @@
import psycopg2
dbconn = psycopg2.connect(database="test",host="localhost",port="5432")
query = """
CREATE TEMP TABLE data (
field01 char,
field02 varchar,
field03 varchar,
field04 varchar,
field05 varchar,
field06 varchar,
field07 varchar,
field08 varchar,
field09 numeric,
field10 integer,
field11 numeric,
field12 numeric,
field13 numeric,
field14 numeric,
field15 numeric,
field16 numeric,
field17 char,
field18 char,
field19 char,
field20 varchar,
field21 varchar,
field22 integer,
field23 char,
field24 char
);
"""
cursor = dbconn.cursor()
cursor.execute(query)
f = open('test_copy2.csv')
cursor.copy_from(f, 'data', sep='|')
f.close()
dbconn.commit()
cursor.close()
dbconn.close()

View File

@ -1,81 +0,0 @@
#!/usr/bin/env python
"""Test for issue #113: test with error during green processing
"""
DSN = 'dbname=test'
import eventlet.patcher
eventlet.patcher.monkey_patch()
import os
import signal
from time import sleep
import psycopg2
from psycopg2 import extensions
from eventlet.hubs import trampoline
# register a test wait callback that fails if SIGHUP is received
panic = []
def wait_cb(conn):
"""A wait callback useful to allow eventlet to work with Psycopg."""
while 1:
if panic:
raise Exception('whatever')
state = conn.poll()
if state == extensions.POLL_OK:
break
elif state == extensions.POLL_READ:
trampoline(conn.fileno(), read=True)
elif state == extensions.POLL_WRITE:
trampoline(conn.fileno(), write=True)
else:
raise psycopg2.OperationalError(
"Bad result from poll: %r" % state)
extensions.set_wait_callback(wait_cb)
# SIGHUP handler to inject a fail in the callback
def handler(signum, frame):
panic.append(True)
signal.signal(signal.SIGHUP, handler)
# Simulate another green thread working
def worker():
while 1:
print("I'm working")
sleep(1)
eventlet.spawn(worker)
# You can unplug the network cable etc. here.
# Kill -HUP will raise an exception in the callback.
print("PID", os.getpid())
conn = psycopg2.connect(DSN)
curs = conn.cursor()
try:
for i in range(1000):
curs.execute("select %s, pg_sleep(1)", (i,))
r = curs.fetchone()
print("selected", r)
except BaseException, e:
print("got exception:", e.__class__.__name__, e)
if conn.closed:
print("the connection is closed")
else:
conn.rollback()
curs.execute("select 1")
print(curs.fetchone())

View File

@ -1,31 +0,0 @@
import gc
import sys
import os
import signal
import warnings
import psycopg2
print("Testing psycopg2 version %s" % psycopg2.__version__)
dbname = os.environ.get('PSYCOPG2_TESTDB', 'psycopg2_test')
conn = psycopg2.connect("dbname=%s" % dbname)
curs = conn.cursor()
curs.isready()
print("Now restart the test postgresql server to drop all connections, press enter when done.")
raw_input()
try:
curs.isready() # No need to test return value
curs.isready()
except:
print("Test passed")
sys.exit(0)
if curs.isready():
print("Warning: looks like the connection didn't get killed. This test is probably in-effective")
print("Test inconclusive")
sys.exit(1)
gc.collect() # used to error here
print("Test Passed")

View File

@ -1,12 +0,0 @@
def test():
import sys, os, thread, psycopg2
def test2():
while True:
for filename in map(lambda m: getattr(m, "__file__", None), sys.modules.values()):
os.stat("/dev/null")
connection = psycopg2.connect(database="test")
cursor = connection.cursor()
thread.start_new_thread(test2, ())
while True:
cursor.execute("COMMIT")
test()

View File

@ -1,8 +0,0 @@
import gtk
import psycopg2
o = psycopg2.connect("dbname=test")
c = o.cursor()
c.execute("SELECT 1.23::float AS foo")
x = c.fetchone()[0]
print(x, type(x))

View File

@ -1,73 +0,0 @@
"""
A script to reproduce the race condition described in ticket #58
from https://bugzilla.redhat.com/show_bug.cgi?id=711095
Results in the error:
python: Modules/gcmodule.c:277: visit_decref: Assertion `gc->gc.gc_refs != 0'
failed.
on unpatched library.
"""
import threading
import gc
import time
import psycopg2
from StringIO import StringIO
done = 0
class GCThread(threading.Thread):
# A thread that sits in an infinite loop, forcing the garbage collector
# to run
def run(self):
global done
while not done:
gc.collect()
time.sleep(0.1) # give the other thread a chance to run
gc_thread = GCThread()
# This assumes a pre-existing db named "test", with:
# "CREATE TABLE test (id serial PRIMARY KEY, num integer, data varchar);"
conn = psycopg2.connect("dbname=test user=postgres")
cur = conn.cursor()
# Start the other thread, running the GC regularly
gc_thread.start()
# Now do lots of "cursor.copy_from" calls:
print("copy_from")
for i in range(1000):
f = StringIO("42\tfoo\n74\tbar\n")
cur.copy_from(f, 'test', columns=('num', 'data'))
# Assuming the other thread gets a chance to run during this call, expect a
# build of python (with assertions enabled) to bail out here with:
# python: Modules/gcmodule.c:277: visit_decref: Assertion `gc->gc.gc_refs != 0' failed.
# Also exercise the copy_to code path
print("copy_to")
cur.execute("truncate test")
f = StringIO("42\tfoo\n74\tbar\n")
cur.copy_from(f, 'test', columns=('num', 'data'))
for i in range(1000):
f = StringIO()
cur.copy_to(f, 'test', columns=('num', 'data'))
# And copy_expert too
print("copy_expert")
cur.execute("truncate test")
for i in range(1000):
f = StringIO("42\tfoo\n74\tbar\n")
cur.copy_expert("copy test to stdout", f)
# Terminate the GC thread's loop:
done = 1
cur.close()
conn.close()

View File

@ -1,44 +0,0 @@
import psycopg2
import traceback
# Change the table here to something the user can create tables in ...
db = psycopg2.connect('dbname=test')
cursor = db.cursor()
print('Creating tables and sample data')
cursor.execute('''
CREATE TEMPORARY TABLE foo (
id int PRIMARY KEY
)''')
cursor.execute('''
CREATE TEMPORARY TABLE bar (
id int PRIMARY KEY,
foo_id int,
CONSTRAINT bar_foo_fk FOREIGN KEY (foo_id) REFERENCES foo(id) DEFERRABLE
)''')
cursor.execute('INSERT INTO foo VALUES (1)')
cursor.execute('INSERT INTO bar VALUES (1, 1)')
db.commit()
print('Deferring constraint and breaking referential integrity')
cursor.execute('SET CONSTRAINTS bar_foo_fk DEFERRED')
cursor.execute('UPDATE bar SET foo_id = 42 WHERE id = 1')
print('Committing (this should fail)')
try:
db.commit()
except:
traceback.print_exc()
print('Rolling back connection')
db.rollback()
print('Running a trivial query')
try:
cursor.execute('SELECT TRUE')
except:
traceback.print_exc()
print('db.closed:', db.closed)

View File

@ -1,63 +0,0 @@
from __future__ import print_function
import psycopg2, psycopg2.extensions
import threading
import gc
import time
import sys
# inherit psycopg2 connection class just so that
# garbage collector enters the tp_clear code path
# in delete_garbage()
class my_connection(psycopg2.extensions.connection):
pass
class db_user(threading.Thread):
def run(self):
conn2 = psycopg2.connect(sys.argv[1], connection_factory=my_connection)
cursor = conn2.cursor()
cursor.execute("UPDATE test_psycopg2_dealloc SET a = 3", async=1)
# the conn2 desctructor will block indefinitely
# on the completion of the query
# (and it will not be holding the GIL during that time)
print("begin conn2 del", file=sys.stderr)
del cursor, conn2
print("end conn2 del", file=sys.stderr)
def main():
# lock out a db row
conn1 = psycopg2.connect(sys.argv[1], connection_factory=my_connection)
cursor = conn1.cursor()
cursor.execute("DROP TABLE IF EXISTS test_psycopg2_dealloc")
cursor.execute("CREATE TABLE test_psycopg2_dealloc (a int)")
cursor.execute("INSERT INTO test_psycopg2_dealloc VALUES (1)")
conn1.commit()
cursor.execute("UPDATE test_psycopg2_dealloc SET a = 2", async=1)
# concurrent thread trying to access the locked row
db_user().start()
# eventually, a gc.collect run will happen
# while the conn2 is inside conn_close()
# but this second dealloc won't get blocked
# as it will avoid conn_close()
for i in range(10):
if gc.collect():
print("garbage collection done", file=sys.stderr)
break
time.sleep(1)
# we now unlock the row by invoking
# the desctructor of conn1. This will permit the
# concurrent thread destructor of conn2 to
# continue and it will end up trying to free
# self->dsn a second time.
print("begin conn1 del", file=sys.stderr)
del cursor, conn1
print("end conn1 del", file=sys.stderr)
if __name__ == '__main__':
main()

View File

@ -1,7 +0,0 @@
import psycopg2.extensions
print(dir(psycopg2._psycopg))
print(psycopg2.extensions.new_type(
(600,), "POINT", lambda oids, name, fun: None))
print("ciccia ciccia")
print(psycopg2._psycopg)

View File

@ -1,9 +0,0 @@
import datetime
import time
import psycopg2
conn = psycopg2.connect("dbname=test")
curs = conn.cursor()
curs.execute("set timezone = 'Asia/Calcutta'")
curs.execute("SELECT now()")
print(curs.fetchone()[0])

View File

@ -1,486 +0,0 @@
#
# This is a valgrind suppression file that should be used when using valgrind.
#
# Here's an example of running valgrind:
#
# cd python/dist/src
# valgrind --tool=memcheck --suppressions=Misc/valgrind-python.supp \
# ./python -E -tt ./Lib/test/regrtest.py -u bsddb,network
#
# You must edit Objects/obmalloc.c and uncomment Py_USING_MEMORY_DEBUGGER
# to use the preferred suppressions with Py_ADDRESS_IN_RANGE.
#
# If you do not want to recompile Python, you can uncomment
# suppressions for PyObject_Free and PyObject_Realloc.
#
# See Misc/README.valgrind for more information.
# all tool names: Addrcheck,Memcheck,cachegrind,helgrind,massif
{
ADDRESS_IN_RANGE/Invalid read of size 4
Memcheck:Addr4
fun:Py_ADDRESS_IN_RANGE
}
{
ADDRESS_IN_RANGE/Invalid read of size 4
Memcheck:Value4
fun:Py_ADDRESS_IN_RANGE
}
{
ADDRESS_IN_RANGE/Invalid read of size 8 (x86_64)
Memcheck:Value8
fun:Py_ADDRESS_IN_RANGE
}
{
ADDRESS_IN_RANGE/Conditional jump or move depends on uninitialised value
Memcheck:Cond
fun:Py_ADDRESS_IN_RANGE
}
{
ADDRESS_IN_RANGE/Invalid read of size 4
Memcheck:Addr4
fun:PyObject_Free
}
{
ADDRESS_IN_RANGE/Invalid read of size 4
Memcheck:Value4
fun:PyObject_Free
}
{
ADDRESS_IN_RANGE/Conditional jump or move depends on uninitialised value
Memcheck:Cond
fun:PyObject_Free
}
{
ADDRESS_IN_RANGE/Invalid read of size 4
Memcheck:Addr4
fun:PyObject_Realloc
}
{
ADDRESS_IN_RANGE/Invalid read of size 4
Memcheck:Value4
fun:PyObject_Realloc
}
{
ADDRESS_IN_RANGE/Conditional jump or move depends on uninitialised value
Memcheck:Cond
fun:PyObject_Realloc
}
###
### All the suppressions below are for errors that occur within libraries
### that Python uses. The problems to not appear to be related to Python's
### use of the libraries.
###
{
GDBM problems, see test_gdbm
Memcheck:Param
write(buf)
fun:write
fun:gdbm_open
}
{
Avoid problem in libc on gentoo
Memcheck:Cond
obj:/lib/ld-2.3.5.so
obj:/lib/ld-2.3.5.so
obj:/lib/ld-2.3.5.so
obj:/lib/ld-2.3.5.so
obj:/lib/ld-2.3.5.so
}
{
Avoid problem in glibc on gentoo
Memcheck:Addr8
obj:/lib/ld-2.3.5.so
obj:/lib/ld-2.3.5.so
obj:/lib/ld-2.3.5.so
obj:/lib/ld-2.3.5.so
obj:/lib/libc-2.3.5.so
obj:/lib/ld-2.3.5.so
fun:_dl_open
obj:/lib/libdl-2.3.5.so
obj:/lib/ld-2.3.5.so
obj:/lib/libdl-2.3.5.so
fun:dlopen
}
{
Avoid problem in glibc on gentoo
Memcheck:Addr8
obj:/lib/ld-2.3.5.so
obj:/lib/ld-2.3.5.so
obj:/lib/ld-2.3.5.so
obj:/lib/ld-2.3.5.so
obj:/lib/ld-2.3.5.so
obj:/lib/ld-2.3.5.so
obj:/lib/ld-2.3.5.so
obj:/lib/libc-2.3.5.so
obj:/lib/ld-2.3.5.so
fun:_dl_open
obj:/lib/libdl-2.3.5.so
obj:/lib/ld-2.3.5.so
obj:/lib/libdl-2.3.5.so
fun:dlopen
}
{
Avoid problem in glibc on gentoo
Memcheck:Cond
obj:/lib/ld-2.3.5.so
obj:/lib/libc-2.3.5.so
obj:/lib/ld-2.3.5.so
fun:_dl_open
obj:/lib/libdl-2.3.5.so
obj:/lib/ld-2.3.5.so
obj:/lib/libdl-2.3.5.so
fun:dlopen
}
{
Avoid problem in glibc on gentoo
Memcheck:Cond
obj:/lib/ld-2.3.5.so
obj:/lib/ld-2.3.5.so
obj:/lib/ld-2.3.5.so
obj:/lib/libc-2.3.5.so
obj:/lib/ld-2.3.5.so
fun:_dl_open
obj:/lib/libdl-2.3.5.so
obj:/lib/ld-2.3.5.so
obj:/lib/libdl-2.3.5.so
fun:dlopen
}
{
Avoid problems w/readline doing a putenv and leaking on exit
Memcheck:Leak
fun:malloc
fun:xmalloc
fun:sh_set_lines_and_columns
fun:_rl_get_screen_size
fun:_rl_init_terminal_io
obj:/lib/libreadline.so.4.3
fun:rl_initialize
fun:setup_readline
fun:initreadline
fun:_PyImport_LoadDynamicModule
fun:load_module
fun:import_submodule
fun:load_next
fun:import_module_ex
fun:PyImport_ImportModuleEx
}
{
Mysterious leak that seems to deal w/pthreads
Memcheck:Leak
fun:calloc
obj:/lib/ld-2.3.5.so
obj:/lib/ld-2.3.5.so
fun:_dl_allocate_tls
fun:__pthread_initialize_minimal
}
{
Mysterious leak that seems to deal w/pthreads
Memcheck:Leak
fun:memalign
obj:/lib/ld-2.3.5.so
fun:_dl_allocate_tls
fun:__pthread_initialize_minimal
}
###
### These occur from somewhere within the SSL, when running
### test_socket_sll. They are too general to leave on by default.
###
###{
### somewhere in SSL stuff
### Memcheck:Cond
### fun:memset
###}
###{
### somewhere in SSL stuff
### Memcheck:Value4
### fun:memset
###}
###
###{
### somewhere in SSL stuff
### Memcheck:Cond
### fun:MD5_Update
###}
###
###{
### somewhere in SSL stuff
### Memcheck:Value4
### fun:MD5_Update
###}
#
# All of these problems come from using test_socket_ssl
#
{
from test_socket_ssl
Memcheck:Cond
fun:BN_bin2bn
}
{
from test_socket_ssl
Memcheck:Cond
fun:BN_num_bits_word
}
{
from test_socket_ssl
Memcheck:Value4
fun:BN_num_bits_word
}
{
from test_socket_ssl
Memcheck:Cond
fun:BN_mod_exp_mont_word
}
{
from test_socket_ssl
Memcheck:Cond
fun:BN_mod_exp_mont
}
{
from test_socket_ssl
Memcheck:Param
write(buf)
fun:write
obj:/usr/lib/libcrypto.so.0.9.7
}
{
from test_socket_ssl
Memcheck:Cond
fun:RSA_verify
}
{
from test_socket_ssl
Memcheck:Value4
fun:RSA_verify
}
{
from test_socket_ssl
Memcheck:Value4
fun:DES_set_key_unchecked
}
{
from test_socket_ssl
Memcheck:Value4
fun:DES_encrypt2
}
{
from test_socket_ssl
Memcheck:Cond
obj:/usr/lib/libssl.so.0.9.7
}
{
from test_socket_ssl
Memcheck:Value4
obj:/usr/lib/libssl.so.0.9.7
}
{
from test_socket_ssl
Memcheck:Cond
fun:BUF_MEM_grow_clean
}
{
from test_socket_ssl
Memcheck:Cond
fun:memcpy
fun:ssl3_read_bytes
}
{
from test_socket_ssl
Memcheck:Cond
fun:SHA1_Update
}
{
from test_socket_ssl
Memcheck:Value4
fun:SHA1_Update
}
{
Debian unstable with libc-i686 suppressions
Memcheck:Cond
obj:/lib/ld-2.3.5.so
obj:/lib/ld-2.3.5.so
obj:/lib/tls/i686/cmov/libc-2.3.5.so
obj:/lib/ld-2.3.5.so
fun:_dl_open
obj:/lib/tls/i686/cmov/libdl-2.3.5.so
obj:/lib/ld-2.3.5.so
obj:/lib/tls/i686/cmov/libdl-2.3.5.so
fun:dlopen
fun:_PyImport_GetDynLoadFunc
fun:_PyImport_LoadDynamicModule
}
{
Debian unstable with libc-i686 suppressions
Memcheck:Cond
obj:/lib/ld-2.3.5.so
obj:/lib/ld-2.3.5.so
obj:/lib/ld-2.3.5.so
obj:/lib/tls/i686/cmov/libc-2.3.5.so
obj:/lib/ld-2.3.5.so
fun:_dl_open
obj:/lib/tls/i686/cmov/libdl-2.3.5.so
obj:/lib/ld-2.3.5.so
obj:/lib/tls/i686/cmov/libdl-2.3.5.so
fun:dlopen
fun:_PyImport_GetDynLoadFunc
fun:_PyImport_LoadDynamicModule
}
{
Debian unstable with libc-i686 suppressions
Memcheck:Addr4
obj:/lib/ld-2.3.5.so
obj:/lib/ld-2.3.5.so
obj:/lib/ld-2.3.5.so
obj:/lib/tls/i686/cmov/libc-2.3.5.so
obj:/lib/ld-2.3.5.so
fun:_dl_open
obj:/lib/tls/i686/cmov/libdl-2.3.5.so
obj:/lib/ld-2.3.5.so
obj:/lib/tls/i686/cmov/libdl-2.3.5.so
fun:dlopen
fun:_PyImport_GetDynLoadFunc
fun:_PyImport_LoadDynamicModule
}
{
Debian unstable with libc-i686 suppressions
Memcheck:Addr4
obj:/lib/ld-2.3.5.so
obj:/lib/ld-2.3.5.so
obj:/lib/ld-2.3.5.so
obj:/lib/ld-2.3.5.so
obj:/lib/ld-2.3.5.so
obj:/lib/ld-2.3.5.so
obj:/lib/tls/i686/cmov/libc-2.3.5.so
obj:/lib/ld-2.3.5.so
fun:_dl_open
obj:/lib/tls/i686/cmov/libdl-2.3.5.so
obj:/lib/ld-2.3.5.so
obj:/lib/tls/i686/cmov/libdl-2.3.5.so
}
{
Debian unstable with libc-i686 suppressions
Memcheck:Cond
obj:/lib/ld-2.3.5.so
obj:/lib/tls/i686/cmov/libc-2.3.5.so
obj:/lib/ld-2.3.5.so
fun:_dl_open
obj:/lib/tls/i686/cmov/libdl-2.3.5.so
obj:/lib/ld-2.3.5.so
obj:/lib/tls/i686/cmov/libdl-2.3.5.so
fun:dlopen
fun:_PyImport_GetDynLoadFunc
fun:_PyImport_LoadDynamicModule
obj:/usr/bin/python2.3
obj:/usr/bin/python2.3
}
{
Debian unstable with libc-i686 suppressions
Memcheck:Cond
obj:/lib/ld-2.3.5.so
obj:/lib/tls/i686/cmov/libc-2.3.5.so
obj:/lib/ld-2.3.5.so
fun:_dl_open
obj:/lib/tls/i686/cmov/libc-2.3.5.so
obj:/lib/ld-2.3.5.so
fun:__libc_dlopen_mode
fun:__nss_lookup_function
obj:/lib/tls/i686/cmov/libc-2.3.5.so
fun:__nss_passwd_lookup
fun:getpwuid_r
fun:pqGetpwuid
}
{
Debian unstable with libc-i686 suppressions
Memcheck:Cond
obj:/lib/ld-2.3.5.so
obj:/lib/ld-2.3.5.so
obj:/lib/ld-2.3.5.so
obj:/lib/tls/i686/cmov/libc-2.3.5.so
obj:/lib/ld-2.3.5.so
fun:_dl_open
obj:/lib/tls/i686/cmov/libc-2.3.5.so
obj:/lib/ld-2.3.5.so
fun:__libc_dlopen_mode
fun:__nss_lookup_function
obj:/lib/tls/i686/cmov/libc-2.3.5.so
fun:__nss_passwd_lookup
}
{
Debian unstable with libc-i686 suppressions
Memcheck:Addr4
obj:/lib/ld-2.3.5.so
obj:/lib/ld-2.3.5.so
obj:/lib/ld-2.3.5.so
obj:/lib/tls/i686/cmov/libc-2.3.5.so
obj:/lib/ld-2.3.5.so
fun:_dl_open
obj:/lib/tls/i686/cmov/libc-2.3.5.so
obj:/lib/ld-2.3.5.so
fun:__libc_dlopen_mode
fun:__nss_lookup_function
obj:/lib/tls/i686/cmov/libnss_compat-2.3.5.so
fun:_nss_compat_getpwuid_r
}
{
Debian unstable with libc-i686 suppressions
Memcheck:Cond
obj:/lib/ld-2.3.5.so
obj:/lib/tls/i686/cmov/libc-2.3.5.so
obj:/lib/ld-2.3.5.so
fun:_dl_open
obj:/lib/tls/i686/cmov/libdl-2.3.5.so
obj:/lib/ld-2.3.5.so
obj:/lib/tls/i686/cmov/libdl-2.3.5.so
fun:dlopen
fun:_PyImport_GetDynLoadFunc
fun:_PyImport_LoadDynamicModule
obj:/usr/bin/python2.4
obj:/usr/bin/python2.4
}

View File

@ -22,8 +22,6 @@ import sys
import urllib2
from collections import defaultdict
from BeautifulSoup import BeautifulSoup as BS
def main():
if len(sys.argv) != 2:
@ -35,8 +33,7 @@ def main():
file_start = read_base_file(filename)
# If you add a version to the list fix the docs (in errorcodes.rst)
classes, errors = fetch_errors(
['8.1', '8.2', '8.3', '8.4', '9.0', '9.1', '9.2', '9.3', '9.4', '9.5',
'9.6', '10'])
['9.1', '9.2', '9.3', '9.4', '9.5', '9.6', '10', '11'])
f = open(filename, "w")
for line in file_start:
@ -90,48 +87,6 @@ def parse_errors_txt(url):
return classes, errors
def parse_errors_sgml(url):
page = BS(urllib2.urlopen(url))
table = page('table')[1]('tbody')[0]
classes = {}
errors = defaultdict(dict)
for tr in table('tr'):
if tr.td.get('colspan'): # it's a class
label = ' '.join(' '.join(tr(text=True)).split()) \
.replace(u'\u2014', '-').encode('ascii')
assert label.startswith('Class')
class_ = label.split()[1]
assert len(class_) == 2
classes[class_] = label
else: # it's an error
errcode = tr.tt.string.encode("ascii")
assert len(errcode) == 5
tds = tr('td')
if len(tds) == 3:
errlabel = '_'.join(tds[1].string.split()).encode('ascii')
# double check the columns are equal
cond_name = tds[2].string.strip().upper().encode("ascii")
assert errlabel == cond_name, tr
elif len(tds) == 2:
# found in PG 9.1 docs
errlabel = tds[1].tt.string.upper().encode("ascii")
else:
assert False, tr
errors[class_][errcode] = errlabel
return classes, errors
errors_sgml_url = \
"http://www.postgresql.org/docs/%s/static/errcodes-appendix.html"
errors_txt_url = \
"http://git.postgresql.org/gitweb/?p=postgresql.git;a=blob_plain;" \
"f=src/backend/utils/errcodes.txt;hb=%s"
@ -144,18 +99,19 @@ def fetch_errors(versions):
for version in versions:
print(version, file=sys.stderr)
tver = tuple(map(int, version.split()[0].split('.')))
if tver < (9, 1):
c1, e1 = parse_errors_sgml(errors_sgml_url % version)
else:
tag = '%s%s_STABLE' % (
(tver[0] >= 10 and 'REL_' or 'REL'),
version.replace('.', '_'))
c1, e1 = parse_errors_txt(errors_txt_url % tag)
tag = '%s%s_STABLE' % (
(tver[0] >= 10 and 'REL_' or 'REL'),
version.replace('.', '_'))
c1, e1 = parse_errors_txt(errors_txt_url % tag)
classes.update(c1)
# This error was in old server versions but probably never used
# https://github.com/postgres/postgres/commit/12f87b2c82
errors['22']['22020'] = 'INVALID_LIMIT_VALUE'
# TODO: this error was added in PG 10 beta 1 but dropped in the
# final release. It doesn't harm leaving it in the file. Check if it
# will be added back in PG 11.
# will be added back in PG 12.
# https://github.com/postgres/postgres/commit/28e0727076
errors['55']['55P04'] = 'UNSAFE_NEW_ENUM_VALUE_USAGE'

230
scripts/make_errors.py Executable file
View File

@ -0,0 +1,230 @@
#!/usr/bin/env python
"""Generate the errors module from PostgreSQL source code.
The script can be run at a new PostgreSQL release to refresh the module.
"""
# Copyright (C) 2018 Daniele Varrazzo <daniele.varrazzo@gmail.com>
#
# psycopg2 is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# psycopg2 is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
# License for more details.
from __future__ import print_function
import re
import sys
import urllib2
from collections import defaultdict
def main():
if len(sys.argv) != 2:
print("usage: %s /path/to/errors.py" % sys.argv[0], file=sys.stderr)
return 2
filename = sys.argv[1]
file_start = read_base_file(filename)
# If you add a version to the list fix the docs (in errors.rst)
classes, errors = fetch_errors(
['9.1', '9.2', '9.3', '9.4', '9.5', '9.6', '10', '11'])
f = open(filename, "w")
for line in file_start:
print(line, file=f)
for line in generate_module_data(classes, errors):
print(line, file=f)
def read_base_file(filename):
rv = []
for line in open(filename):
rv.append(line.rstrip("\n"))
if line.startswith("# autogenerated"):
return rv
raise ValueError("can't find the separator. Is this the right file?")
def parse_errors_txt(url):
classes = {}
errors = defaultdict(dict)
page = urllib2.urlopen(url)
for line in page:
# Strip comments and skip blanks
line = line.split('#')[0].strip()
if not line:
continue
# Parse a section
m = re.match(r"Section: (Class (..) - .+)", line)
if m:
label, class_ = m.groups()
classes[class_] = label
continue
# Parse an error
m = re.match(r"(.....)\s+(?:E|W|S)\s+ERRCODE_(\S+)(?:\s+(\S+))?$", line)
if m:
errcode, macro, spec = m.groups()
# skip errcodes without specs as they are not publically visible
if not spec:
continue
errlabel = spec.upper()
errors[class_][errcode] = errlabel
continue
# We don't expect anything else
raise ValueError("unexpected line:\n%s" % line)
return classes, errors
errors_txt_url = \
"http://git.postgresql.org/gitweb/?p=postgresql.git;a=blob_plain;" \
"f=src/backend/utils/errcodes.txt;hb=%s"
def fetch_errors(versions):
classes = {}
errors = defaultdict(dict)
for version in versions:
print(version, file=sys.stderr)
tver = tuple(map(int, version.split()[0].split('.')))
tag = '%s%s_STABLE' % (
(tver[0] >= 10 and 'REL_' or 'REL'),
version.replace('.', '_'))
c1, e1 = parse_errors_txt(errors_txt_url % tag)
classes.update(c1)
for c, cerrs in e1.items():
errors[c].update(cerrs)
return classes, errors
def generate_module_data(classes, errors):
tmpl = """
@for_sqlstate(%(errcode)r)
class %(cls)s(%(base)s):
pass\
"""
specific = {
'38002': 'ModifyingSqlDataNotPermittedExt',
'38003': 'ProhibitedSqlStatementAttemptedExt',
'38004': 'ReadingSqlDataNotPermittedExt',
'39004': 'NullValueNotAllowedExt',
'XX000': 'InternalError_',
}
seen = set("""
Error Warning InterfaceError DataError DatabaseError ProgrammingError
IntegrityError InternalError NotSupportedError OperationalError
QueryCanceledError TransactionRollbackError
""".split())
for clscode, clslabel in sorted(classes.items()):
if clscode in ('00', '01'):
# success and warning - never raised
continue
yield "\n\n# %s" % clslabel
for errcode, errlabel in sorted(errors[clscode].items()):
if errcode in specific:
clsname = specific[errcode]
else:
clsname = errlabel.title().replace('_', '')
if clsname in seen:
raise Exception("class already existing: %s" % clsname)
seen.add(clsname)
yield tmpl % {
'cls': clsname,
'base': get_base_class_name(errcode),
'errcode': errcode
}
def get_base_class_name(errcode):
"""
This is a python porting of exception_from_sqlstate code in pqpath.c
"""
if errcode[0] == '0':
if errcode[1] == 'A': # Class 0A - Feature Not Supported
return 'NotSupportedError'
elif errcode[0] == '2':
if errcode[1] in '01':
# Class 20 - Case Not Found
# Class 21 - Cardinality Violation
return 'ProgrammingError'
elif errcode[1] == '2': # Class 22 - Data Exception
return 'DataError'
elif errcode[1] == '3': # Class 23 - Integrity Constraint Violation
return 'IntegrityError'
elif errcode[1] in '45':
# Class 24 - Invalid Cursor State
# Class 25 - Invalid Transaction State
return 'InternalError'
elif errcode[1] in '678':
# Class 26 - Invalid SQL Statement Name
# Class 27 - Triggered Data Change Violation
# Class 28 - Invalid Authorization Specification
return 'OperationalError'
elif errcode[1] in 'BDF':
# Class 2B - Dependent Privilege Descriptors Still Exist
# Class 2D - Invalid Transaction Termination
# Class 2F - SQL Routine Exception
return 'InternalError'
elif errcode[0] == '3':
if errcode[1] == '4': # Class 34 - Invalid Cursor Name
return 'OperationalError'
if errcode[1] in '89B':
# Class 38 - External Routine Exception
# Class 39 - External Routine Invocation Exception
# Class 3B - Savepoint Exception
return 'InternalError'
if errcode[1] in 'DF':
# Class 3D - Invalid Catalog Name
# Class 3F - Invalid Schema Name
return 'ProgrammingError'
elif errcode[0] == '4':
if errcode[1] == '0': # Class 40 - Transaction Rollback
return 'TransactionRollbackError'
if errcode[1] in '24':
# Class 42 - Syntax Error or Access Rule Violation
# Class 44 - WITH CHECK OPTION Violation
return 'ProgrammingError'
elif errcode[0] == '5':
if errcode == "57014":
return 'QueryCanceledError'
# Class 53 - Insufficient Resources
# Class 54 - Program Limit Exceeded
# Class 55 - Object Not In Prerequisite State
# Class 57 - Operator Intervention
# Class 58 - System Error (errors external to PostgreSQL itself)
else:
return 'OperationalError'
elif errcode[0] == 'F': # Class F0 - Configuration File Error
return 'InternalError'
elif errcode[0] == 'H': # Class HV - Foreign Data Wrapper Error (SQL/MED)
return 'OperationalError'
elif errcode[0] == 'P': # Class P0 - PL/pgSQL Error
return 'InternalError'
elif errcode[0] == 'X': # Class XX - Internal Error
return 'InternalError'
return 'DatabaseError'
if __name__ == '__main__':
sys.exit(main())

View File

@ -19,6 +19,7 @@ script exits with error 1.
# License for more details.
from __future__ import print_function
import argparse
import gc
import sys
import difflib
@ -62,19 +63,16 @@ def main():
def parse_args():
import optparse
parser = optparse.OptionParser(description=__doc__)
parser.add_option('--nruns', type='int', metavar="N", default=3,
help="number of test suite runs [default: %default]")
parser.add_option('--suite', metavar="NAME",
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--nruns', type=int, metavar="N", default=3,
help="number of test suite runs [default: %(default)d]")
parser.add_argument('--suite', metavar="NAME",
help="the test suite to run (e.g. 'test_cursor'). [default: all]")
parser.add_option('--objs', metavar="TYPE",
parser.add_argument('--objs', metavar="TYPE",
help="in case of leaks, print a report of object TYPE "
"(support still incomplete)")
opt, args = parser.parse_args()
return opt
return parser.parse_args()
def dump(i, opt):

View File

@ -32,10 +32,7 @@ import os
import sys
import re
import subprocess
try:
from setuptools import setup, Extension
except ImportError:
from distutils.core import setup, Extension
from setuptools import setup, Extension
from distutils.command.build_ext import build_ext
from distutils.sysconfig import get_python_inc
from distutils.ccompiler import get_default_compiler
@ -47,7 +44,7 @@ try:
except ImportError:
import ConfigParser as configparser
# Take a look at http://www.python.org/dev/peps/pep-0440/
# Take a look at https://www.python.org/dev/peps/pep-0440/
# for a consistent versioning pattern.
PSYCOPG_VERSION = '2.8.dev0'
@ -489,11 +486,11 @@ sources = [
'libpq_support.c', 'win32_support.c', 'solaris_support.c',
'connection_int.c', 'connection_type.c',
'cursor_int.c', 'cursor_type.c',
'cursor_int.c', 'cursor_type.c', 'column_type.c',
'replication_connection_type.c',
'replication_cursor_type.c',
'replication_message_type.c',
'diagnostics_type.c', 'error_type.c',
'diagnostics_type.c', 'error_type.c', 'conninfo_type.c',
'lobject_int.c', 'lobject_type.c',
'notify_type.c', 'xid_type.c',
@ -511,7 +508,7 @@ depends = [
'replication_connection.h',
'replication_cursor.h',
'replication_message.h',
'notify.h', 'pqpath.h', 'xid.h',
'notify.h', 'pqpath.h', 'xid.h', 'column.h', 'conninfo.h',
'libpq_support.h', 'win32_support.h',
'adapter_asis.h', 'adapter_binary.h', 'adapter_datetime.h',

View File

@ -40,6 +40,7 @@ from . import test_copy
from . import test_cursor
from . import test_dates
from . import test_errcodes
from . import test_errors
from . import test_extras_dictcursor
from . import test_fast_executemany
from . import test_green
@ -84,6 +85,7 @@ def test_suite():
suite.addTest(test_cursor.test_suite())
suite.addTest(test_dates.test_suite())
suite.addTest(test_errcodes.test_suite())
suite.addTest(test_errors.test_suite())
suite.addTest(test_extras_dictcursor.test_suite())
suite.addTest(test_fast_executemany.test_suite())
suite.addTest(test_green.test_suite())

View File

@ -192,7 +192,7 @@ class AsyncTests(ConnectingTestCase):
self.assertTrue(self.conn.isexecuting())
# getting transaction status works
self.assertEquals(self.conn.get_transaction_status(),
self.assertEquals(self.conn.info.transaction_status,
ext.TRANSACTION_STATUS_ACTIVE)
self.assertTrue(self.conn.isexecuting())
@ -359,7 +359,7 @@ class AsyncTests(ConnectingTestCase):
self.assertEquals(self.sync_conn.notifies, [])
pid = self.conn.get_backend_pid()
pid = self.conn.info.backend_pid
for _ in range(5):
self.wait(self.sync_conn)
if not self.sync_conn.notifies:
@ -418,7 +418,7 @@ class AsyncTests(ConnectingTestCase):
def test_notices(self):
del self.conn.notices[:]
cur = self.conn.cursor()
if self.conn.server_version >= 90300:
if self.conn.info.server_version >= 90300:
cur.execute("set client_min_messages=debug1")
self.wait(cur)
cur.execute("create temp table chatty (id serial primary key);")
@ -450,9 +450,16 @@ class AsyncTests(ConnectingTestCase):
else:
self.fail("no exception raised")
@skip_before_postgres(8, 2)
def test_copy_no_hang(self):
cur = self.conn.cursor()
cur.execute("copy (select 1) to stdout")
self.assertRaises(psycopg2.ProgrammingError, self.wait, self.conn)
def test_suite():
return unittest.TestLoader().loadTestsFromName(__name__)
if __name__ == "__main__":
unittest.main()

View File

@ -218,5 +218,6 @@ class AsyncReplicationTest(ReplicationTestCase):
def test_suite():
return unittest.TestLoader().loadTestsFromName(__name__)
if __name__ == "__main__":
unittest.main()

View File

@ -42,5 +42,6 @@ class DateTimeAllocationBugTestCase(unittest.TestCase):
def test_suite():
return unittest.TestLoader().loadTestsFromName(__name__)
if __name__ == "__main__":
unittest.main()

View File

@ -114,5 +114,6 @@ class CancelTests(ConnectingTestCase):
def test_suite():
return unittest.TestLoader().loadTestsFromName(__name__)
if __name__ == "__main__":
unittest.main()

View File

@ -35,11 +35,11 @@ import psycopg2.errorcodes
from psycopg2 import extensions as ext
from .testutils import (
unittest, decorate_all_tests, skip_if_no_superuser, skip_before_postgres,
unittest, skip_if_no_superuser, skip_before_postgres,
skip_after_postgres, skip_before_libpq, skip_after_libpq,
ConnectingTestCase, skip_if_tpc_disabled, skip_if_windows, slow)
from .testconfig import dsn, dbname
from .testconfig import dbhost, dsn, dbname
class ConnectionTests(ConnectingTestCase):
@ -87,13 +87,13 @@ class ConnectionTests(ConnectingTestCase):
conn.autocommit = True
conn.isolation_level = 'serializable'
conn.readonly = True
if self.conn.server_version >= 90100:
if self.conn.info.server_version >= 90100:
conn.deferrable = False
self.assert_(conn.autocommit)
self.assertEqual(conn.isolation_level, ext.ISOLATION_LEVEL_SERIALIZABLE)
self.assert_(conn.readonly is True)
if self.conn.server_version >= 90100:
if self.conn.info.server_version >= 90100:
self.assert_(conn.deferrable is False)
conn.reset()
@ -101,13 +101,13 @@ class ConnectionTests(ConnectingTestCase):
self.assert_(not conn.autocommit)
self.assertEqual(conn.isolation_level, ext.ISOLATION_LEVEL_DEFAULT)
self.assert_(conn.readonly is None)
if self.conn.server_version >= 90100:
if self.conn.info.server_version >= 90100:
self.assert_(conn.deferrable is None)
def test_notices(self):
conn = self.conn
cur = conn.cursor()
if self.conn.server_version >= 90300:
if self.conn.info.server_version >= 90300:
cur.execute("set client_min_messages=debug1")
cur.execute("create temp table chatty (id serial primary key);")
self.assertEqual("CREATE TABLE", cur.statusmessage)
@ -116,7 +116,7 @@ class ConnectionTests(ConnectingTestCase):
def test_notices_consistent_order(self):
conn = self.conn
cur = conn.cursor()
if self.conn.server_version >= 90300:
if self.conn.info.server_version >= 90300:
cur.execute("set client_min_messages=debug1")
cur.execute("""
create temp table table1 (id serial);
@ -136,7 +136,7 @@ class ConnectionTests(ConnectingTestCase):
def test_notices_limited(self):
conn = self.conn
cur = conn.cursor()
if self.conn.server_version >= 90300:
if self.conn.info.server_version >= 90300:
cur.execute("set client_min_messages=debug1")
for i in range(0, 100, 10):
sql = " ".join(["create temp table table%d (id serial);" % j
@ -153,7 +153,7 @@ class ConnectionTests(ConnectingTestCase):
conn = self.conn
self.conn.notices = deque()
cur = conn.cursor()
if self.conn.server_version >= 90300:
if self.conn.info.server_version >= 90300:
cur.execute("set client_min_messages=debug1")
cur.execute("""
@ -183,7 +183,7 @@ class ConnectionTests(ConnectingTestCase):
conn = self.conn
self.conn.notices = None # will make an error swallowes ok
cur = conn.cursor()
if self.conn.server_version >= 90300:
if self.conn.info.server_version >= 90300:
cur.execute("set client_min_messages=debug1")
cur.execute("create temp table table1 (id serial);")
@ -199,7 +199,7 @@ class ConnectionTests(ConnectingTestCase):
def test_tpc_unsupported(self):
cnn = self.conn
if cnn.server_version >= 80100:
if cnn.info.server_version >= 80100:
return self.skipTest("tpc is supported")
self.assertRaises(psycopg2.NotSupportedError,
@ -538,7 +538,7 @@ class IsolationLevelsTestCase(ConnectingTestCase):
conn.set_isolation_level(level)
# the only values available on prehistoric PG versions
if conn.server_version < 80000:
if conn.info.server_version < 80000:
if level in (
ext.ISOLATION_LEVEL_READ_UNCOMMITTED,
ext.ISOLATION_LEVEL_REPEATABLE_READ):
@ -594,35 +594,35 @@ class IsolationLevelsTestCase(ConnectingTestCase):
cur = conn.cursor()
self.assertEqual(ext.TRANSACTION_STATUS_IDLE,
conn.get_transaction_status())
conn.info.transaction_status)
cur.execute("insert into isolevel values (10);")
self.assertEqual(ext.TRANSACTION_STATUS_INTRANS,
conn.get_transaction_status())
conn.info.transaction_status)
conn.set_isolation_level(
psycopg2.extensions.ISOLATION_LEVEL_SERIALIZABLE)
self.assertEqual(psycopg2.extensions.TRANSACTION_STATUS_IDLE,
conn.get_transaction_status())
conn.info.transaction_status)
cur.execute("select count(*) from isolevel;")
self.assertEqual(0, cur.fetchone()[0])
cur.execute("insert into isolevel values (10);")
self.assertEqual(psycopg2.extensions.TRANSACTION_STATUS_INTRANS,
conn.get_transaction_status())
conn.info.transaction_status)
conn.set_isolation_level(
psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT)
self.assertEqual(psycopg2.extensions.TRANSACTION_STATUS_IDLE,
conn.get_transaction_status())
conn.info.transaction_status)
cur.execute("select count(*) from isolevel;")
self.assertEqual(0, cur.fetchone()[0])
cur.execute("insert into isolevel values (10);")
self.assertEqual(psycopg2.extensions.TRANSACTION_STATUS_IDLE,
conn.get_transaction_status())
conn.info.transaction_status)
conn.set_isolation_level(
psycopg2.extensions.ISOLATION_LEVEL_READ_COMMITTED)
self.assertEqual(psycopg2.extensions.TRANSACTION_STATUS_IDLE,
conn.get_transaction_status())
conn.info.transaction_status)
cur.execute("select count(*) from isolevel;")
self.assertEqual(1, cur.fetchone()[0])
self.assertEqual(conn.isolation_level,
@ -718,7 +718,7 @@ class IsolationLevelsTestCase(ConnectingTestCase):
self.conn.isolation_level = ext.ISOLATION_LEVEL_REPEATABLE_READ
cur.execute("SHOW transaction_isolation;")
if self.conn.server_version > 80000:
if self.conn.info.server_version > 80000:
self.assertEqual(self.conn.isolation_level,
ext.ISOLATION_LEVEL_REPEATABLE_READ)
self.assertEqual(cur.fetchone()[0], 'repeatable read')
@ -737,7 +737,7 @@ class IsolationLevelsTestCase(ConnectingTestCase):
self.conn.isolation_level = ext.ISOLATION_LEVEL_READ_UNCOMMITTED
cur.execute("SHOW transaction_isolation;")
if self.conn.server_version > 80000:
if self.conn.info.server_version > 80000:
self.assertEqual(self.conn.isolation_level,
ext.ISOLATION_LEVEL_READ_UNCOMMITTED)
self.assertEqual(cur.fetchone()[0], 'read uncommitted')
@ -766,7 +766,7 @@ class IsolationLevelsTestCase(ConnectingTestCase):
self.conn.isolation_level = "repeatable read"
cur.execute("SHOW transaction_isolation;")
if self.conn.server_version > 80000:
if self.conn.info.server_version > 80000:
self.assertEqual(self.conn.isolation_level,
ext.ISOLATION_LEVEL_REPEATABLE_READ)
self.assertEqual(cur.fetchone()[0], 'repeatable read')
@ -785,7 +785,7 @@ class IsolationLevelsTestCase(ConnectingTestCase):
self.conn.isolation_level = "read uncommitted"
cur.execute("SHOW transaction_isolation;")
if self.conn.server_version > 80000:
if self.conn.info.server_version > 80000:
self.assertEqual(self.conn.isolation_level,
ext.ISOLATION_LEVEL_READ_UNCOMMITTED)
self.assertEqual(cur.fetchone()[0], 'read uncommitted')
@ -808,7 +808,16 @@ class IsolationLevelsTestCase(ConnectingTestCase):
self.assertRaises(ValueError, setattr, self.conn, 'isolation_level', 5)
self.assertRaises(ValueError, setattr, self.conn, 'isolation_level', 'bah')
def test_attribs_segfault(self):
# bug #790
for i in range(10000):
self.conn.autocommit
self.conn.readonly
self.conn.deferrable
self.conn.isolation_level
@skip_if_tpc_disabled
class ConnectionTwoPhaseTests(ConnectingTestCase):
def setUp(self):
ConnectingTestCase.setUp(self)
@ -1180,9 +1189,6 @@ class ConnectionTwoPhaseTests(ConnectingTestCase):
self.assertEqual(None, xid.bqual)
decorate_all_tests(ConnectionTwoPhaseTests, skip_if_tpc_disabled)
class TransactionControlTests(ConnectingTestCase):
def test_closed(self):
self.conn.close()
@ -1208,7 +1214,7 @@ class TransactionControlTests(ConnectingTestCase):
self.conn.set_session(
ext.ISOLATION_LEVEL_REPEATABLE_READ)
cur.execute("SHOW transaction_isolation;")
if self.conn.server_version > 80000:
if self.conn.info.server_version > 80000:
self.assertEqual(cur.fetchone()[0], 'repeatable read')
else:
self.assertEqual(cur.fetchone()[0], 'serializable')
@ -1223,7 +1229,7 @@ class TransactionControlTests(ConnectingTestCase):
self.conn.set_session(
isolation_level=ext.ISOLATION_LEVEL_READ_UNCOMMITTED)
cur.execute("SHOW transaction_isolation;")
if self.conn.server_version > 80000:
if self.conn.info.server_version > 80000:
self.assertEqual(cur.fetchone()[0], 'read uncommitted')
else:
self.assertEqual(cur.fetchone()[0], 'read committed')
@ -1238,7 +1244,7 @@ class TransactionControlTests(ConnectingTestCase):
self.conn.set_session("repeatable read")
cur.execute("SHOW transaction_isolation;")
if self.conn.server_version > 80000:
if self.conn.info.server_version > 80000:
self.assertEqual(cur.fetchone()[0], 'repeatable read')
else:
self.assertEqual(cur.fetchone()[0], 'serializable')
@ -1251,7 +1257,7 @@ class TransactionControlTests(ConnectingTestCase):
self.conn.set_session("read uncommitted")
cur.execute("SHOW transaction_isolation;")
if self.conn.server_version > 80000:
if self.conn.info.server_version > 80000:
self.assertEqual(cur.fetchone()[0], 'read uncommitted')
else:
self.assertEqual(cur.fetchone()[0], 'read committed')
@ -1507,42 +1513,42 @@ class AutocommitTests(ConnectingTestCase):
def test_default_no_autocommit(self):
self.assert_(not self.conn.autocommit)
self.assertEqual(self.conn.status, ext.STATUS_READY)
self.assertEqual(self.conn.get_transaction_status(),
self.assertEqual(self.conn.info.transaction_status,
ext.TRANSACTION_STATUS_IDLE)
cur = self.conn.cursor()
cur.execute('select 1;')
self.assertEqual(self.conn.status, ext.STATUS_BEGIN)
self.assertEqual(self.conn.get_transaction_status(),
self.assertEqual(self.conn.info.transaction_status,
ext.TRANSACTION_STATUS_INTRANS)
self.conn.rollback()
self.assertEqual(self.conn.status, ext.STATUS_READY)
self.assertEqual(self.conn.get_transaction_status(),
self.assertEqual(self.conn.info.transaction_status,
ext.TRANSACTION_STATUS_IDLE)
def test_set_autocommit(self):
self.conn.autocommit = True
self.assert_(self.conn.autocommit)
self.assertEqual(self.conn.status, ext.STATUS_READY)
self.assertEqual(self.conn.get_transaction_status(),
self.assertEqual(self.conn.info.transaction_status,
ext.TRANSACTION_STATUS_IDLE)
cur = self.conn.cursor()
cur.execute('select 1;')
self.assertEqual(self.conn.status, ext.STATUS_READY)
self.assertEqual(self.conn.get_transaction_status(),
self.assertEqual(self.conn.info.transaction_status,
ext.TRANSACTION_STATUS_IDLE)
self.conn.autocommit = False
self.assert_(not self.conn.autocommit)
self.assertEqual(self.conn.status, ext.STATUS_READY)
self.assertEqual(self.conn.get_transaction_status(),
self.assertEqual(self.conn.info.transaction_status,
ext.TRANSACTION_STATUS_IDLE)
cur.execute('select 1;')
self.assertEqual(self.conn.status, ext.STATUS_BEGIN)
self.assertEqual(self.conn.get_transaction_status(),
self.assertEqual(self.conn.info.transaction_status,
ext.TRANSACTION_STATUS_INTRANS)
def test_set_intrans_error(self):
@ -1555,24 +1561,24 @@ class AutocommitTests(ConnectingTestCase):
self.conn.set_session(autocommit=True)
self.assert_(self.conn.autocommit)
self.assertEqual(self.conn.status, ext.STATUS_READY)
self.assertEqual(self.conn.get_transaction_status(),
self.assertEqual(self.conn.info.transaction_status,
ext.TRANSACTION_STATUS_IDLE)
cur = self.conn.cursor()
cur.execute('select 1;')
self.assertEqual(self.conn.status, ext.STATUS_READY)
self.assertEqual(self.conn.get_transaction_status(),
self.assertEqual(self.conn.info.transaction_status,
ext.TRANSACTION_STATUS_IDLE)
self.conn.set_session(autocommit=False)
self.assert_(not self.conn.autocommit)
self.assertEqual(self.conn.status, ext.STATUS_READY)
self.assertEqual(self.conn.get_transaction_status(),
self.assertEqual(self.conn.info.transaction_status,
ext.TRANSACTION_STATUS_IDLE)
cur.execute('select 1;')
self.assertEqual(self.conn.status, ext.STATUS_BEGIN)
self.assertEqual(self.conn.get_transaction_status(),
self.assertEqual(self.conn.info.transaction_status,
ext.TRANSACTION_STATUS_INTRANS)
self.conn.rollback()
@ -1580,7 +1586,7 @@ class AutocommitTests(ConnectingTestCase):
self.assert_(self.conn.autocommit)
cur.execute('select 1;')
self.assertEqual(self.conn.status, ext.STATUS_READY)
self.assertEqual(self.conn.get_transaction_status(),
self.assertEqual(self.conn.info.transaction_status,
ext.TRANSACTION_STATUS_IDLE)
cur.execute("SHOW transaction_isolation;")
self.assertEqual(cur.fetchone()[0], 'serializable')
@ -1687,8 +1693,161 @@ while True:
self.assert_(not err, err)
class TestConnectionInfo(ConnectingTestCase):
def setUp(self):
ConnectingTestCase.setUp(self)
class BrokenConn(psycopg2.extensions.connection):
def __init__(self, *args, **kwargs):
# don't call superclass
pass
# A "broken" connection
self.bconn = self.connect(connection_factory=BrokenConn)
def test_dbname(self):
self.assert_(isinstance(self.conn.info.dbname, str))
self.assert_(self.bconn.info.dbname is None)
def test_user(self):
cur = self.conn.cursor()
cur.execute("select user")
self.assertEqual(self.conn.info.user, cur.fetchone()[0])
self.assert_(self.bconn.info.user is None)
def test_password(self):
self.assert_(isinstance(self.conn.info.password, str))
self.assert_(self.bconn.info.password is None)
def test_host(self):
expected = dbhost if dbhost else "/"
self.assertIn(expected, self.conn.info.host)
self.assert_(self.bconn.info.host is None)
def test_host_readonly(self):
with self.assertRaises(AttributeError):
self.conn.info.host = 'override'
def test_port(self):
self.assert_(isinstance(self.conn.info.port, int))
self.assert_(self.bconn.info.port is None)
def test_options(self):
self.assert_(isinstance(self.conn.info.options, str))
self.assert_(self.bconn.info.options is None)
def test_status(self):
self.assertEqual(self.conn.info.status, 0)
self.assertEqual(self.bconn.info.status, 1)
def test_transaction_status(self):
self.assertEqual(self.conn.info.transaction_status, 0)
cur = self.conn.cursor()
cur.execute("select 1")
self.assertEqual(self.conn.info.transaction_status, 2)
self.assertEqual(self.bconn.info.transaction_status, 4)
def test_parameter_status(self):
cur = self.conn.cursor()
try:
cur.execute("show server_version")
except psycopg2.DatabaseError:
self.assertIsInstance(
self.conn.info.parameter_status('server_version'), str)
else:
self.assertEqual(
self.conn.info.parameter_status('server_version'),
cur.fetchone()[0])
self.assertIsNone(self.conn.info.parameter_status('wat'))
self.assertIsNone(self.bconn.info.parameter_status('server_version'))
def test_protocol_version(self):
self.assertEqual(self.conn.info.protocol_version, 3)
self.assertEqual(self.bconn.info.protocol_version, 0)
def test_server_version(self):
cur = self.conn.cursor()
try:
cur.execute("show server_version_num")
except psycopg2.DatabaseError:
self.assert_(isinstance(self.conn.info.server_version, int))
else:
self.assertEqual(
self.conn.info.server_version, int(cur.fetchone()[0]))
self.assertEqual(self.bconn.info.server_version, 0)
def test_error_message(self):
self.assertIsNone(self.conn.info.error_message)
self.assertIsNotNone(self.bconn.info.error_message)
cur = self.conn.cursor()
try:
cur.execute("select 1 from nosuchtable")
except psycopg2.DatabaseError:
pass
self.assert_('nosuchtable' in self.conn.info.error_message)
def test_socket(self):
self.assert_(self.conn.info.socket >= 0)
self.assert_(self.bconn.info.socket < 0)
def test_backend_pid(self):
cur = self.conn.cursor()
try:
cur.execute("select pg_backend_pid()")
except psycopg2.DatabaseError:
self.assert_(self.conn.info.backend_pid > 0)
else:
self.assertEqual(
self.conn.info.backend_pid, int(cur.fetchone()[0]))
self.assert_(self.bconn.info.backend_pid == 0)
def test_needs_password(self):
self.assertIs(self.conn.info.needs_password, False)
self.assertIs(self.bconn.info.needs_password, False)
def test_used_password(self):
self.assertIsInstance(self.conn.info.used_password, bool)
self.assertIs(self.bconn.info.used_password, False)
@skip_before_libpq(9, 5)
def test_ssl_in_use(self):
self.assertIsInstance(self.conn.info.ssl_in_use, bool)
self.assertIs(self.bconn.info.ssl_in_use, False)
@skip_after_libpq(9, 5)
def test_ssl_not_supported(self):
with self.assertRaises(psycopg2.NotSupportedError):
self.conn.info.ssl_in_use
with self.assertRaises(psycopg2.NotSupportedError):
self.conn.info.ssl_attribute_names
with self.assertRaises(psycopg2.NotSupportedError):
self.conn.info.ssl_attribute('wat')
@skip_before_libpq(9, 5)
def test_ssl_attribute(self):
attribs = self.conn.info.ssl_attribute_names
self.assert_(attribs)
if self.conn.info.ssl_in_use:
for attrib in attribs:
self.assertIsInstance(self.conn.info.ssl_attribute(attrib), str)
else:
for attrib in attribs:
self.assertIsNone(self.conn.info.ssl_attribute(attrib))
self.assertIsNone(self.conn.info.ssl_attribute('wat'))
for attrib in attribs:
self.assertIsNone(self.bconn.info.ssl_attribute(attrib))
def test_suite():
return unittest.TestLoader().loadTestsFromName(__name__)
if __name__ == "__main__":
unittest.main()

View File

@ -25,8 +25,7 @@
import sys
import string
import unittest
from .testutils import (ConnectingTestCase, decorate_all_tests,
skip_before_postgres, slow, StringIO)
from .testutils import (ConnectingTestCase, skip_before_postgres, slow, StringIO)
from itertools import cycle
from subprocess import Popen, PIPE
@ -63,6 +62,7 @@ class MinimalWrite(_base):
return self.f.write(data)
@skip_copy_if_green
class CopyTests(ConnectingTestCase):
def setUp(self):
@ -140,7 +140,8 @@ class CopyTests(ConnectingTestCase):
about = abin.decode('latin1').replace('\\', '\\\\')
else:
abin = bytes(list(range(32, 127)) + list(range(160, 256))).decode('latin1')
abin = bytes(list(range(32, 127))
+ list(range(160, 256))).decode('latin1')
about = abin.replace('\\', '\\\\')
curs = self.conn.cursor()
@ -161,7 +162,8 @@ class CopyTests(ConnectingTestCase):
abin = ''.join(map(chr, range(32, 127) + range(160, 255)))
about = abin.replace('\\', '\\\\')
else:
abin = bytes(list(range(32, 127)) + list(range(160, 255))).decode('latin1')
abin = bytes(list(range(32, 127))
+ list(range(160, 255))).decode('latin1')
about = abin.replace('\\', '\\\\').encode('latin1')
curs = self.conn.cursor()
@ -184,7 +186,8 @@ class CopyTests(ConnectingTestCase):
about = abin.replace('\\', '\\\\')
else:
abin = bytes(list(range(32, 127)) + list(range(160, 256))).decode('latin1')
abin = bytes(list(range(32, 127))
+ list(range(160, 256))).decode('latin1')
about = abin.replace('\\', '\\\\')
import io
@ -375,11 +378,9 @@ conn.close()
curs.copy_to, BrokenWrite(), "tcopy")
decorate_all_tests(CopyTests, skip_copy_if_green)
def test_suite():
return unittest.TestLoader().loadTestsFromName(__name__)
if __name__ == "__main__":
unittest.main()

View File

@ -229,22 +229,22 @@ class CursorTests(ConnectingTestCase):
curs.execute("select data from withhold order by data")
self.assertEqual(curs.fetchone(), (10,))
self.assertEqual(self.conn.status, psycopg2.extensions.STATUS_BEGIN)
self.assertEqual(self.conn.get_transaction_status(),
self.assertEqual(self.conn.info.transaction_status,
psycopg2.extensions.TRANSACTION_STATUS_INTRANS)
self.conn.commit()
self.assertEqual(self.conn.status, psycopg2.extensions.STATUS_READY)
self.assertEqual(self.conn.get_transaction_status(),
self.assertEqual(self.conn.info.transaction_status,
psycopg2.extensions.TRANSACTION_STATUS_IDLE)
self.assertEqual(curs.fetchone(), (20,))
self.assertEqual(self.conn.status, psycopg2.extensions.STATUS_READY)
self.assertEqual(self.conn.get_transaction_status(),
self.assertEqual(self.conn.info.transaction_status,
psycopg2.extensions.TRANSACTION_STATUS_IDLE)
curs.close()
self.assertEqual(self.conn.status, psycopg2.extensions.STATUS_READY)
self.assertEqual(self.conn.get_transaction_status(),
self.assertEqual(self.conn.info.transaction_status,
psycopg2.extensions.TRANSACTION_STATUS_IDLE)
def test_withhold_autocommit(self):
@ -256,17 +256,17 @@ class CursorTests(ConnectingTestCase):
self.assertEqual(curs.fetchone(), (10,))
self.assertEqual(self.conn.status, psycopg2.extensions.STATUS_READY)
self.assertEqual(self.conn.get_transaction_status(),
self.assertEqual(self.conn.info.transaction_status,
psycopg2.extensions.TRANSACTION_STATUS_IDLE)
self.conn.commit()
self.assertEqual(self.conn.status, psycopg2.extensions.STATUS_READY)
self.assertEqual(self.conn.get_transaction_status(),
self.assertEqual(self.conn.info.transaction_status,
psycopg2.extensions.TRANSACTION_STATUS_IDLE)
curs.close()
self.assertEqual(self.conn.status, psycopg2.extensions.STATUS_READY)
self.assertEqual(self.conn.get_transaction_status(),
self.assertEqual(self.conn.info.transaction_status,
psycopg2.extensions.TRANSACTION_STATUS_IDLE)
def test_scrollable(self):
@ -377,7 +377,7 @@ class CursorTests(ConnectingTestCase):
for i, rec in enumerate(curs):
self.assertEqual(i + 1, curs.rownumber)
def test_namedtuple_description(self):
def test_description_attribs(self):
curs = self.conn.cursor()
curs.execute("""select
3.14::decimal(10,2) as pi,
@ -412,6 +412,27 @@ class CursorTests(ConnectingTestCase):
self.assertEqual(c.precision, None)
self.assertEqual(c.scale, None)
def test_description_extra_attribs(self):
curs = self.conn.cursor()
curs.execute("""
create table testcol (
pi decimal(10,2),
hi text)
""")
curs.execute("select oid from pg_class where relname = %s", ('testcol',))
oid = curs.fetchone()[0]
curs.execute("insert into testcol values (3.14, 'hello')")
curs.execute("select hi, pi, 42 from testcol")
self.assertEqual(curs.description[0].table_oid, oid)
self.assertEqual(curs.description[0].table_column, 2)
self.assertEqual(curs.description[1].table_oid, oid)
self.assertEqual(curs.description[1].table_column, 1)
self.assertEqual(curs.description[2].table_oid, None)
self.assertEqual(curs.description[2].table_column, None)
def test_pickle_description(self):
curs = self.conn.cursor()
curs.execute('SELECT 1 AS foo')
@ -504,7 +525,7 @@ class CursorTests(ConnectingTestCase):
def test_bad_subclass(self):
# check that we get an error message instead of a segfault
# for badly written subclasses.
# see http://stackoverflow.com/questions/22019341/
# see https://stackoverflow.com/questions/22019341/
class StupidCursor(psycopg2.extensions.cursor):
def __init__(self, *args, **kwargs):
# I am stupid so not calling superclass init
@ -568,7 +589,10 @@ class CursorTests(ConnectingTestCase):
# psycopg2 noticing.
control_conn = self.conn
connect_func = self.connect
wait_func = lambda conn: None
def wait_func(conn):
pass
self._test_external_close(control_conn, connect_func, wait_func)
@skip_if_no_superuser
@ -578,7 +602,10 @@ class CursorTests(ConnectingTestCase):
# Issue #443 is in the async code too. Since the fix is duplicated,
# so is the test.
control_conn = self.conn
connect_func = lambda: self.connect(async_=True)
def connect_func():
return self.connect(async_=True)
wait_func = psycopg2.extras.wait_select
self._test_external_close(control_conn, connect_func, wait_func)
@ -627,5 +654,6 @@ class CursorTests(ConnectingTestCase):
def test_suite():
return unittest.TestLoader().loadTestsFromName(__name__)
if __name__ == "__main__":
unittest.main()

View File

@ -713,5 +713,6 @@ class FixedOffsetTimezoneTests(unittest.TestCase):
def test_suite():
return unittest.TestLoader().loadTestsFromName(__name__)
if __name__ == "__main__":
unittest.main()

View File

@ -68,5 +68,6 @@ class ErrocodeTests(ConnectingTestCase):
def test_suite():
return unittest.TestLoader().loadTestsFromName(__name__)
if __name__ == "__main__":
unittest.main()

71
tests/test_errors.py Executable file
View File

@ -0,0 +1,71 @@
#!/usr/bin/env python
# test_errors.py - unit test for psycopg2.errors module
#
# Copyright (C) 2018 Daniele Varrazzo <daniele.varrazzo@gmail.com>
#
# psycopg2 is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# In addition, as a special exception, the copyright holders give
# permission to link this program with the OpenSSL library (or with
# modified versions of OpenSSL that use the same license as OpenSSL),
# and distribute linked combinations including the two.
#
# You must obey the GNU Lesser General Public License in all respects for
# all of the code used other than OpenSSL.
#
# psycopg2 is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
# License for more details.
import unittest
from .testutils import ConnectingTestCase
import psycopg2
class ErrorsTests(ConnectingTestCase):
def test_exception_class(self):
cur = self.conn.cursor()
try:
cur.execute("select * from nonexist")
except psycopg2.Error as exc:
e = exc
from psycopg2.errors import UndefinedTable
self.assert_(isinstance(e, UndefinedTable), type(e))
self.assert_(isinstance(e, self.conn.ProgrammingError))
def test_exception_class_fallback(self):
cur = self.conn.cursor()
from psycopg2 import errors
x = errors._by_sqlstate.pop('42P01')
try:
cur.execute("select * from nonexist")
except psycopg2.Error as exc:
e = exc
finally:
errors._by_sqlstate['42P01'] = x
self.assertEqual(type(e), self.conn.ProgrammingError)
def test_lookup(self):
from psycopg2 import errors
self.assertIs(errors.lookup('42P01'), errors.UndefinedTable)
with self.assertRaises(KeyError):
errors.lookup('XXXXX')
def test_suite():
return unittest.TestLoader().loadTestsFromName(__name__)
if __name__ == "__main__":
unittest.main()

View File

@ -582,5 +582,6 @@ class NamedTupleCursorTest(ConnectingTestCase):
def test_suite():
return unittest.TestLoader().loadTestsFromName(__name__)
if __name__ == "__main__":
unittest.main()

View File

@ -83,6 +83,16 @@ class TestExecuteBatch(FastExecuteTestMixin, testutils.ConnectingTestCase):
cur.execute("select id, val from testfast order by id")
self.assertEqual(cur.fetchall(), [(i, i * 10) for i in range(1000)])
def test_composed(self):
from psycopg2 import sql
cur = self.conn.cursor()
psycopg2.extras.execute_batch(cur,
sql.SQL("insert into {0} (id, val) values (%s, %s)")
.format(sql.Identifier('testfast')),
((i, i * 10) for i in range(1000)))
cur.execute("select id, val from testfast order by id")
self.assertEqual(cur.fetchall(), [(i, i * 10) for i in range(1000)])
def test_pages(self):
cur = self.conn.cursor()
psycopg2.extras.execute_batch(cur,
@ -124,6 +134,7 @@ class TestExecuteBatch(FastExecuteTestMixin, testutils.ConnectingTestCase):
self.assertEqual(cur.fetchone(), (3, snowman))
@testutils.skip_before_postgres(8, 2)
class TestExecuteValues(FastExecuteTestMixin, testutils.ConnectingTestCase):
def test_empty(self):
cur = self.conn.cursor()
@ -169,6 +180,16 @@ class TestExecuteValues(FastExecuteTestMixin, testutils.ConnectingTestCase):
cur.execute("select id, val from testfast order by id")
self.assertEqual(cur.fetchall(), [(i, i * 10) for i in range(1000)])
def test_composed(self):
from psycopg2 import sql
cur = self.conn.cursor()
psycopg2.extras.execute_values(cur,
sql.SQL("insert into {0} (id, val) values %s")
.format(sql.Identifier('testfast')),
((i, i * 10) for i in range(1000)))
cur.execute("select id, val from testfast order by id")
self.assertEqual(cur.fetchall(), [(i, i * 10) for i in range(1000)])
def test_pages(self):
cur = self.conn.cursor()
psycopg2.extras.execute_values(cur,
@ -231,12 +252,9 @@ class TestExecuteValues(FastExecuteTestMixin, testutils.ConnectingTestCase):
self.assertEqual(cur.fetchall(), [(1, 'hi')])
testutils.decorate_all_tests(TestExecuteValues,
testutils.skip_before_postgres(8, 2))
def test_suite():
return unittest.TestLoader().loadTestsFromName(__name__)
if __name__ == "__main__":
unittest.main()

View File

@ -27,7 +27,7 @@ import psycopg2
import psycopg2.extensions
import psycopg2.extras
from .testutils import ConnectingTestCase, slow
from .testutils import ConnectingTestCase, skip_before_postgres, slow
class ConnectionStub(object):
@ -111,6 +111,12 @@ class GreenTestCase(ConnectingTestCase):
curs.execute("select 1")
self.assertEqual(curs.fetchone()[0], 1)
@skip_before_postgres(8, 2)
def test_copy_no_hang(self):
cur = self.conn.cursor()
self.assertRaises(psycopg2.ProgrammingError,
cur.execute, "copy (select 1) to stdout")
class CallbackErrorTestCase(ConnectingTestCase):
def setUp(self):
@ -200,5 +206,6 @@ class CallbackErrorTestCase(ConnectingTestCase):
def test_suite():
return unittest.TestLoader().loadTestsFromName(__name__)
if __name__ == "__main__":
unittest.main()

View File

@ -26,6 +26,7 @@ import psycopg2
import psycopg2.extras
@testutils.decorate_all_tests
def skip_if_no_ipaddress(f):
@wraps(f)
def skip_if_no_ipaddress_(self):
@ -40,6 +41,7 @@ def skip_if_no_ipaddress(f):
return skip_if_no_ipaddress_
@skip_if_no_ipaddress
class NetworkingTestCase(testutils.ConnectingTestCase):
def test_inet_cast(self):
import ipaddress as ip
@ -125,8 +127,6 @@ class NetworkingTestCase(testutils.ConnectingTestCase):
cur.execute("select %s", [ip.ip_network('::ffff:102:300/128')])
self.assertEquals(cur.fetchone()[0], '::ffff:102:300/128')
testutils.decorate_all_tests(NetworkingTestCase, skip_if_no_ipaddress)
def test_suite():
return unittest.TestLoader().loadTestsFromName(__name__)

View File

@ -31,18 +31,11 @@ import psycopg2
import psycopg2.extensions
import unittest
from .testutils import (decorate_all_tests, skip_if_tpc_disabled,
ConnectingTestCase, skip_if_green, slow)
skip_before_postgres, ConnectingTestCase, skip_if_green, slow)
def skip_if_no_lo(f):
@wraps(f)
def skip_if_no_lo_(self):
if self.conn.server_version < 80100:
return self.skipTest("large objects only supported from PG 8.1")
else:
return f(self)
return skip_if_no_lo_
skip_if_no_lo = skip_before_postgres(8, 1,
"large objects only supported from PG 8.1")
skip_lo_if_green = skip_if_green("libpq doesn't support LO in async mode")
@ -72,6 +65,8 @@ class LargeObjectTestCase(ConnectingTestCase):
ConnectingTestCase.tearDown(self)
@skip_if_no_lo
@skip_lo_if_green
class LargeObjectTests(LargeObjectTestCase):
def test_create(self):
lo = self.conn.lobject()
@ -397,13 +392,12 @@ class LargeObjectTests(LargeObjectTestCase):
lo = self.conn.lobject(lobject_factory=lobject_subclass)
self.assert_(isinstance(lo, lobject_subclass))
decorate_all_tests(LargeObjectTests, skip_if_no_lo, skip_lo_if_green)
@decorate_all_tests
def skip_if_no_truncate(f):
@wraps(f)
def skip_if_no_truncate_(self):
if self.conn.server_version < 80300:
if self.conn.info.server_version < 80300:
return self.skipTest(
"the server doesn't support large object truncate")
@ -417,6 +411,9 @@ def skip_if_no_truncate(f):
return skip_if_no_truncate_
@skip_if_no_lo
@skip_lo_if_green
@skip_if_no_truncate
class LargeObjectTruncateTests(LargeObjectTestCase):
def test_truncate(self):
lo = self.conn.lobject()
@ -453,15 +450,12 @@ class LargeObjectTruncateTests(LargeObjectTestCase):
self.assertRaises(psycopg2.ProgrammingError, lo.truncate)
decorate_all_tests(LargeObjectTruncateTests,
skip_if_no_lo, skip_lo_if_green, skip_if_no_truncate)
def _has_lo64(conn):
"""Return (bool, msg) about the lo64 support"""
if conn.server_version < 90300:
if conn.info.server_version < 90300:
return (False, "server version %s doesn't support the lo64 API"
% conn.server_version)
% conn.info.server_version)
if 'lo64' not in psycopg2.__version__:
return False, "this psycopg build doesn't support the lo64 API"
@ -469,6 +463,7 @@ def _has_lo64(conn):
return True, "this server and build support the lo64 API"
@decorate_all_tests
def skip_if_no_lo64(f):
@wraps(f)
def skip_if_no_lo64_(self):
@ -481,6 +476,10 @@ def skip_if_no_lo64(f):
return skip_if_no_lo64_
@skip_if_no_lo
@skip_lo_if_green
@skip_if_no_truncate
@skip_if_no_lo64
class LargeObject64Tests(LargeObjectTestCase):
def test_seek_tell_truncate_greater_than_2gb(self):
lo = self.conn.lobject()
@ -491,10 +490,8 @@ class LargeObject64Tests(LargeObjectTestCase):
self.assertEqual(lo.seek(length, 0), length)
self.assertEqual(lo.tell(), length)
decorate_all_tests(LargeObject64Tests,
skip_if_no_lo, skip_lo_if_green, skip_if_no_truncate, skip_if_no_lo64)
@decorate_all_tests
def skip_if_lo64(f):
@wraps(f)
def skip_if_lo64_(self):
@ -507,6 +504,10 @@ def skip_if_lo64(f):
return skip_if_lo64_
@skip_if_no_lo
@skip_lo_if_green
@skip_if_no_truncate
@skip_if_lo64
class LargeObjectNot64Tests(LargeObjectTestCase):
def test_seek_larger_than_2gb(self):
lo = self.conn.lobject()
@ -522,12 +523,10 @@ class LargeObjectNot64Tests(LargeObjectTestCase):
(OverflowError, psycopg2.InterfaceError, psycopg2.NotSupportedError),
lo.truncate, length)
decorate_all_tests(LargeObjectNot64Tests,
skip_if_no_lo, skip_lo_if_green, skip_if_no_truncate, skip_if_lo64)
def test_suite():
return unittest.TestLoader().loadTestsFromName(__name__)
if __name__ == "__main__":
unittest.main()

View File

@ -173,8 +173,8 @@ class ExceptionsTestCase(ConnectingTestCase):
'column_name', 'constraint_name', 'context', 'datatype_name',
'internal_position', 'internal_query', 'message_detail',
'message_hint', 'message_primary', 'schema_name', 'severity',
'source_file', 'source_function', 'source_line', 'sqlstate',
'statement_position', 'table_name', ]:
'severity_nonlocalized', 'source_file', 'source_function',
'source_line', 'sqlstate', 'statement_position', 'table_name', ]:
v = getattr(diag, attr)
if v is not None:
self.assert_(isinstance(v, str))
@ -276,6 +276,15 @@ class ExceptionsTestCase(ConnectingTestCase):
self.assertEqual(e.diag.constraint_name, "chk_eq1")
self.assertEqual(e.diag.datatype_name, None)
@skip_before_postgres(9, 6)
def test_9_6_diagnostics(self):
cur = self.conn.cursor()
try:
cur.execute("select 1 from nosuchtable")
except psycopg2.Error as exc:
e = exc
self.assertEqual(e.diag.severity_nonlocalized, 'ERROR')
def test_pickle(self):
import pickle
cur = self.conn.cursor()
@ -339,5 +348,6 @@ class TestVersionDiscovery(unittest.TestCase):
def test_suite():
return unittest.TestLoader().loadTestsFromName(__name__)
if __name__ == "__main__":
unittest.main()

View File

@ -61,7 +61,7 @@ import %(module)s as psycopg2
import %(module)s.extensions as ext
conn = psycopg2.connect(%(dsn)r)
conn.set_isolation_level(ext.ISOLATION_LEVEL_AUTOCOMMIT)
print(conn.get_backend_pid())
print(conn.info.backend_pid)
curs = conn.cursor()
curs.execute("NOTIFY " %(name)r %(payload)r)
curs.close()
@ -147,9 +147,9 @@ conn.close()
@slow
def test_notify_payload(self):
if self.conn.server_version < 90000:
if self.conn.info.server_version < 90000:
return self.skipTest("server version %s doesn't support notify payload"
% self.conn.server_version)
% self.conn.info.server_version)
self.autocommit(self.conn)
self.listen('foo')
pid = int(self.notify('foo', payload="Hello, world!").communicate()[0])

View File

@ -26,7 +26,6 @@ from . import dbapi20
from . import dbapi20_tpc
from .testutils import skip_if_tpc_disabled
import unittest
from .testutils import decorate_all_tests
import psycopg2
from .testconfig import dsn
@ -37,7 +36,7 @@ class Psycopg2Tests(dbapi20.DatabaseAPI20Test):
connect_args = ()
connect_kw_args = {'dsn': dsn}
lower_func = 'lower' # For stored procedure test
lower_func = 'lower' # For stored procedure test
def test_callproc(self):
# Until DBAPI 2.0 compliance, callproc should return None or it's just
@ -50,16 +49,14 @@ class Psycopg2Tests(dbapi20.DatabaseAPI20Test):
con = self._connect()
try:
cur = con.cursor()
if self.lower_func and hasattr(cur,'callproc'):
cur.callproc(self.lower_func,('FOO',))
if self.lower_func and hasattr(cur, 'callproc'):
cur.callproc(self.lower_func, ('FOO',))
r = cur.fetchall()
self.assertEqual(len(r),1,'callproc produced no result set')
self.assertEqual(len(r[0]),1,
'callproc produced invalid result set'
)
self.assertEqual(r[0][0],'foo',
'callproc produced invalid results'
)
self.assertEqual(len(r), 1, 'callproc produced no result set')
self.assertEqual(len(r[0]), 1,
'callproc produced invalid result set')
self.assertEqual(r[0][0], 'foo',
'callproc produced invalid results')
finally:
con.close()
@ -72,17 +69,17 @@ class Psycopg2Tests(dbapi20.DatabaseAPI20Test):
pass
@skip_if_tpc_disabled
class Psycopg2TPCTests(dbapi20_tpc.TwoPhaseCommitTests, unittest.TestCase):
driver = psycopg2
def connect(self):
return psycopg2.connect(dsn=dsn)
decorate_all_tests(Psycopg2TPCTests, skip_if_tpc_disabled)
def test_suite():
return unittest.TestLoader().loadTestsFromName(__name__)
if __name__ == '__main__':
unittest.main()

View File

@ -46,8 +46,8 @@ class QuotingTestCase(ConnectingTestCase):
The tests also check that no warning is raised ('escape_string_warning'
should be on).
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
https://www.postgresql.org/docs/current/static/sql-syntax-lexical.html#SQL-SYNTAX-STRINGS
https://www.postgresql.org/docs/current/static/runtime-config-compatible.html
"""
def test_string(self):
data = """some data with \t chars
@ -90,7 +90,7 @@ class QuotingTestCase(ConnectingTestCase):
else:
res = curs.fetchone()[0].tobytes()
if res[0] in (b'x', ord(b'x')) and self.conn.server_version >= 90000:
if res[0] in (b'x', ord(b'x')) and self.conn.info.server_version >= 90000:
return self.skipTest(
"bytea broken with server >= 9.0, libpq < 9")
@ -126,7 +126,8 @@ class QuotingTestCase(ConnectingTestCase):
if sys.version_info[0] < 3:
data = ''.join(map(chr, range(32, 127) + range(160, 256)))
else:
data = bytes(list(range(32, 127)) + list(range(160, 256))).decode('latin1')
data = bytes(list(range(32, 127))
+ list(range(160, 256))).decode('latin1')
# as string
curs.execute("SELECT %s::text;", (data,))
@ -150,7 +151,8 @@ class QuotingTestCase(ConnectingTestCase):
if sys.version_info[0] < 3:
data = ''.join(map(chr, range(32, 127) + range(128, 256)))
else:
data = bytes(list(range(32, 127)) + list(range(128, 256))).decode('koi8_r')
data = bytes(list(range(32, 127))
+ list(range(128, 256))).decode('koi8_r')
# as string
curs.execute("SELECT %s::text;", (data,))
@ -252,5 +254,6 @@ class TestStringAdapter(ConnectingTestCase):
def test_suite():
return unittest.TestLoader().loadTestsFromName(__name__)
if __name__ == "__main__":
unittest.main()

View File

@ -24,9 +24,8 @@
import datetime as dt
import unittest
from .testutils import (ConnectingTestCase,
skip_before_postgres, skip_before_python, skip_copy_if_green,
StringIO)
from .testutils import (
ConnectingTestCase, skip_before_postgres, skip_copy_if_green, StringIO)
import psycopg2
from psycopg2 import sql
@ -181,26 +180,43 @@ class IdentifierTests(ConnectingTestCase):
def test_init(self):
self.assert_(isinstance(sql.Identifier('foo'), sql.Identifier))
self.assert_(isinstance(sql.Identifier(u'foo'), sql.Identifier))
self.assert_(isinstance(sql.Identifier('foo', 'bar', 'baz'), sql.Identifier))
self.assertRaises(TypeError, sql.Identifier)
self.assertRaises(TypeError, sql.Identifier, 10)
self.assertRaises(TypeError, sql.Identifier, dt.date(2016, 12, 31))
def test_string(self):
def test_strings(self):
self.assertEqual(sql.Identifier('foo').strings, ('foo',))
self.assertEqual(sql.Identifier('foo', 'bar').strings, ('foo', 'bar'))
# Legacy method
self.assertEqual(sql.Identifier('foo').string, 'foo')
self.assertRaises(AttributeError,
getattr, sql.Identifier('foo', 'bar'), 'string')
def test_repr(self):
obj = sql.Identifier("fo'o")
self.assertEqual(repr(obj), 'Identifier("fo\'o")')
self.assertEqual(repr(obj), str(obj))
obj = sql.Identifier("fo'o", 'ba"r')
self.assertEqual(repr(obj), 'Identifier("fo\'o", \'ba"r\')')
self.assertEqual(repr(obj), str(obj))
def test_eq(self):
self.assert_(sql.Identifier('foo') == sql.Identifier('foo'))
self.assert_(sql.Identifier('foo', 'bar') == sql.Identifier('foo', 'bar'))
self.assert_(sql.Identifier('foo') != sql.Identifier('bar'))
self.assert_(sql.Identifier('foo') != 'foo')
self.assert_(sql.Identifier('foo') != sql.SQL('foo'))
def test_as_str(self):
self.assertEqual(sql.Identifier('foo').as_string(self.conn), '"foo"')
self.assertEqual(sql.Identifier("fo'o").as_string(self.conn), '"fo\'o"')
self.assertEqual(
sql.Identifier('foo').as_string(self.conn), '"foo"')
self.assertEqual(
sql.Identifier('foo', 'bar').as_string(self.conn), '"foo"."bar"')
self.assertEqual(
sql.Identifier("fo'o", 'ba"r').as_string(self.conn), '"fo\'o"."ba""r"')
def test_join(self):
self.assert_(not hasattr(sql.Identifier('foo'), 'join'))
@ -396,5 +412,6 @@ class ValuesTest(ConnectingTestCase):
def test_suite():
return unittest.TestLoader().loadTestsFromName(__name__)
if __name__ == "__main__":
unittest.main()

View File

@ -249,5 +249,6 @@ class QueryCancellationTests(ConnectingTestCase):
def test_suite():
return unittest.TestLoader().loadTestsFromName(__name__)
if __name__ == "__main__":
unittest.main()

View File

@ -167,6 +167,13 @@ class TypesBasicTests(ConnectingTestCase):
curs.execute("select col from array_test where id = 2")
self.assertEqual(curs.fetchone()[0], [])
@testutils.skip_before_postgres(8, 4)
def testNestedEmptyArray(self):
# issue #788
curs = self.conn.cursor()
curs.execute("select 10 = any(%s::int[])", ([[]], ))
self.assertFalse(curs.fetchone()[0])
def testEmptyArrayNoCast(self):
s = self.execute("SELECT '{}' AS foo")
self.assertEqual(s, '{}')
@ -228,9 +235,9 @@ class TypesBasicTests(ConnectingTestCase):
curs.execute("insert into na (textaa) values (%s)", ([['a', None]],))
curs.execute("insert into na (textaa) values (%s)", ([[None, None]],))
curs.execute("insert into na (intaa) values (%s)", ([[None]],))
curs.execute("insert into na (intaa) values (%s)", ([[None]],))
curs.execute("insert into na (intaa) values (%s)", ([[42, None]],))
curs.execute("insert into na (intaa) values (%s)", ([[None, None]],))
curs.execute("insert into na (intaa) values (%s)", ([[None, None]],))
curs.execute("insert into na (boolaa) values (%s)", ([[None]],))
curs.execute("insert into na (boolaa) values (%s)", ([[True, None]],))
@ -452,6 +459,20 @@ class AdaptSubclassTest(unittest.TestCase):
self.assertEqual(ext.adapt(foo((1, 2, 3))).getquoted(), 'bar')
@decorate_all_tests
def skip_if_cant_cast(f):
@wraps(f)
def skip_if_cant_cast_(self, *args, **kwargs):
if self._cast is None:
return self.skipTest("can't test bytea parser: %s - %s"
% (self._exc.__class__.__name__, self._exc))
return f(self, *args, **kwargs)
return skip_if_cant_cast_
@skip_if_cant_cast
class ByteaParserTest(unittest.TestCase):
"""Unit test for our bytea format parser."""
def setUp(self):
@ -539,22 +560,9 @@ class ByteaParserTest(unittest.TestCase):
self.assertEqual(rv, tgt)
def skip_if_cant_cast(f):
@wraps(f)
def skip_if_cant_cast_(self, *args, **kwargs):
if self._cast is None:
return self.skipTest("can't test bytea parser: %s - %s"
% (self._exc.__class__.__name__, self._exc))
return f(self, *args, **kwargs)
return skip_if_cant_cast_
decorate_all_tests(ByteaParserTest, skip_if_cant_cast)
def test_suite():
return unittest.TestLoader().loadTestsFromName(__name__)
if __name__ == "__main__":
unittest.main()

View File

@ -24,8 +24,7 @@ from pickle import dumps, loads
import unittest
from .testutils import (skip_if_no_uuid, skip_before_postgres,
ConnectingTestCase, decorate_all_tests, py3_raises_typeerror, slow,
skip_from_python)
ConnectingTestCase, py3_raises_typeerror, slow, skip_from_python)
import psycopg2
import psycopg2.extras
@ -123,6 +122,7 @@ class TypesExtrasTests(ConnectingTestCase):
s = self.execute("""SELECT '{"(1,2)","(3,4)"}' AS foo""")
self.failUnless(s == """{"(1,2)","(3,4)"}""")
def skip_if_no_hstore(f):
@wraps(f)
def skip_if_no_hstore_(self):
@ -137,7 +137,7 @@ def skip_if_no_hstore(f):
class HstoreTestCase(ConnectingTestCase):
def test_adapt_8(self):
if self.conn.server_version >= 90000:
if self.conn.info.server_version >= 90000:
return self.skipTest("skipping dict adaptation with PG pre-9 syntax")
from psycopg2.extras import HstoreAdapter
@ -163,7 +163,7 @@ class HstoreTestCase(ConnectingTestCase):
self.assertQuotedEqual(ii[3], b"('d' => '" + encc + b"')")
def test_adapt_9(self):
if self.conn.server_version < 90000:
if self.conn.info.server_version < 90000:
return self.skipTest("skipping dict adaptation with PG 9 syntax")
from psycopg2.extras import HstoreAdapter
@ -448,10 +448,10 @@ class HstoreTestCase(ConnectingTestCase):
def skip_if_no_composite(f):
@wraps(f)
def skip_if_no_composite_(self):
if self.conn.server_version < 80000:
if self.conn.info.server_version < 80000:
return self.skipTest(
"server version %s doesn't support composite types"
% self.conn.server_version)
% self.conn.info.server_version)
return f(self)
@ -1048,6 +1048,7 @@ def skip_if_no_jsonb_type(f):
return skip_before_postgres(9, 4)(f)
@skip_if_no_jsonb_type
class JsonbTestCase(ConnectingTestCase):
@staticmethod
def myloads(s):
@ -1137,8 +1138,6 @@ class JsonbTestCase(ConnectingTestCase):
curs.execute("""select NULL::jsonb[]""")
self.assertEqual(curs.fetchone()[0], None)
decorate_all_tests(JsonbTestCase, skip_if_no_jsonb_type)
class RangeTestCase(unittest.TestCase):
def test_noparam(self):
@ -1386,20 +1385,54 @@ class RangeTestCase(unittest.TestCase):
r = Range(0, 4)
self.assertEqual(loads(dumps(r)), r)
def test_str(self):
'''
Range types should have a short and readable ``str`` implementation.
def skip_if_no_range(f):
@wraps(f)
def skip_if_no_range_(self):
if self.conn.server_version < 90200:
return self.skipTest(
"server version %s doesn't support range types"
% self.conn.server_version)
Using ``repr`` for all string conversions can be very unreadable for
longer types like ``DateTimeTZRange``.
'''
from psycopg2.extras import Range
return f(self)
# Using the "u" prefix to make sure we have the proper return types in
# Python2
expected = [
u'(0, 4)',
u'[0, 4]',
u'(0, 4]',
u'[0, 4)',
u'empty',
]
results = []
return skip_if_no_range_
converter = unicode if sys.version_info < (3, 0) else str
for bounds in ('()', '[]', '(]', '[)'):
r = Range(0, 4, bounds=bounds)
results.append(converter(r))
r = Range(empty=True)
results.append(converter(r))
self.assertEqual(results, expected)
def test_str_datetime(self):
'''
Date-Time ranges should return a human-readable string as well on
string conversion.
'''
from psycopg2.extras import DateTimeTZRange
from datetime import datetime
from psycopg2.tz import FixedOffsetTimezone
converter = unicode if sys.version_info < (3, 0) else str
tz = FixedOffsetTimezone(-5 * 60, "EST")
r = DateTimeTZRange(datetime(2010, 1, 1, tzinfo=tz),
datetime(2011, 1, 1, tzinfo=tz))
expected = u'[2010-01-01 00:00:00-05:00, 2011-01-01 00:00:00-05:00)'
result = converter(r)
self.assertEqual(result, expected)
@skip_before_postgres(9, 2, "range not supported before postgres 9.2")
class RangeCasterTestCase(ConnectingTestCase):
builtin_ranges = ('int4range', 'int8range', 'numrange',
@ -1717,8 +1750,6 @@ class RangeCasterTestCase(ConnectingTestCase):
for r in [ra1, ra2, rars2, rars3]:
del ext.adapters[r.range, ext.ISQLQuote]
decorate_all_tests(RangeCasterTestCase, skip_if_no_range)
def test_suite():
return unittest.TestLoader().loadTestsFromName(__name__)

View File

@ -217,12 +217,13 @@ class WithCursorTestCase(WithTestCase):
@skip_before_postgres(8, 2)
def test_named_with_noop(self):
with self.conn.cursor('named') as cur:
with self.conn.cursor('named'):
pass
def test_suite():
return unittest.TestLoader().loadTestsFromName(__name__)
if __name__ == "__main__":
unittest.main()

View File

@ -25,6 +25,7 @@
import re
import os
import sys
import types
import select
import platform
import unittest
@ -42,15 +43,15 @@ if sys.version_info[0] == 2:
unichr = unichr
else:
# Python 3
from io import StringIO
from importlib import reload
from io import StringIO # noqa
from importlib import reload # noqa
long = int
unichr = chr
# Silence warnings caused by the stubbornness of the Python unittest
# maintainers
# http://bugs.python.org/issue9424
# https://bugs.python.org/issue9424
if (not hasattr(unittest.TestCase, 'assert_')
or unittest.TestCase.assert_ is not unittest.TestCase.assertTrue):
# mavaff...
@ -64,6 +65,7 @@ def assertDsnEqual(self, dsn1, dsn2, msg=None):
"""Check that two conninfo string have the same content"""
self.assertEqual(set(dsn1.split()), set(dsn2.split()), msg)
unittest.TestCase.assertDsnEqual = assertDsnEqual
@ -173,16 +175,31 @@ class ConnectingTestCase(unittest.TestCase):
raise Exception("Unexpected result from poll: %r", state)
def decorate_all_tests(cls, *decorators):
def decorate_all_tests(obj, *decorators):
"""
Apply all the *decorators* to all the tests defined in the TestCase *cls*.
Apply all the *decorators* to all the tests defined in the TestCase *obj*.
The decorator can also be applied to a decorator: if *obj* is a function,
return a new decorator which can be applied either to a method or to a
class, in which case it will decorate all the tests.
"""
for n in dir(cls):
if isinstance(obj, types.FunctionType):
def decorator(func_or_cls):
if isinstance(func_or_cls, types.FunctionType):
return obj(func_or_cls)
else:
decorate_all_tests(func_or_cls, obj)
return func_or_cls
return decorator
for n in dir(obj):
if n.startswith('test'):
for d in decorators:
setattr(cls, n, d(getattr(cls, n)))
setattr(obj, n, d(getattr(obj, n)))
@decorate_all_tests
def skip_if_no_uuid(f):
"""Decorator to skip a test if uuid is not supported by Py/PG."""
@wraps(f)
@ -202,6 +219,7 @@ def skip_if_no_uuid(f):
return skip_if_no_uuid_
@decorate_all_tests
def skip_if_tpc_disabled(f):
"""Skip a test if the server has tpc support disabled."""
@wraps(f)
@ -229,14 +247,20 @@ def skip_if_tpc_disabled(f):
def skip_before_postgres(*ver):
"""Skip a test on PostgreSQL before a certain version."""
reason = None
if isinstance(ver[-1], str):
ver, reason = ver[:-1], ver[-1]
ver = ver + (0,) * (3 - len(ver))
@decorate_all_tests
def skip_before_postgres_(f):
@wraps(f)
def skip_before_postgres__(self):
if self.conn.server_version < int("%d%02d%02d" % ver):
return self.skipTest("skipped because PostgreSQL %s"
% self.conn.server_version)
if self.conn.info.server_version < int("%d%02d%02d" % ver):
return self.skipTest(
reason or "skipped because PostgreSQL %s"
% self.conn.info.server_version)
else:
return f(self)
@ -248,12 +272,13 @@ def skip_after_postgres(*ver):
"""Skip a test on PostgreSQL after (including) a certain version."""
ver = ver + (0,) * (3 - len(ver))
@decorate_all_tests
def skip_after_postgres_(f):
@wraps(f)
def skip_after_postgres__(self):
if self.conn.server_version >= int("%d%02d%02d" % ver):
if self.conn.info.server_version >= int("%d%02d%02d" % ver):
return self.skipTest("skipped because PostgreSQL %s"
% self.conn.server_version)
% self.conn.info.server_version)
else:
return f(self)
@ -273,6 +298,7 @@ def skip_before_libpq(*ver):
"""Skip a test if libpq we're linked to is older than a certain version."""
ver = ver + (0,) * (3 - len(ver))
@decorate_all_tests
def skip_before_libpq_(f):
@wraps(f)
def skip_before_libpq__(self):
@ -290,6 +316,7 @@ def skip_after_libpq(*ver):
"""Skip a test if libpq we're linked to is newer than a certain version."""
ver = ver + (0,) * (3 - len(ver))
@decorate_all_tests
def skip_after_libpq_(f):
@wraps(f)
def skip_after_libpq__(self):
@ -305,6 +332,7 @@ def skip_after_libpq(*ver):
def skip_before_python(*ver):
"""Skip a test on Python before a certain version."""
@decorate_all_tests
def skip_before_python_(f):
@wraps(f)
def skip_before_python__(self):
@ -320,6 +348,7 @@ def skip_before_python(*ver):
def skip_from_python(*ver):
"""Skip a test on Python after (including) a certain version."""
@decorate_all_tests
def skip_from_python_(f):
@wraps(f)
def skip_from_python__(self):
@ -333,6 +362,7 @@ def skip_from_python(*ver):
return skip_from_python_
@decorate_all_tests
def skip_if_no_superuser(f):
"""Skip a test if the database user running the test is not a superuser"""
@wraps(f)
@ -351,6 +381,7 @@ def skip_if_no_superuser(f):
def skip_if_green(reason):
@decorate_all_tests
def skip_if_green_(f):
@wraps(f)
def skip_if_green__(self):
@ -363,9 +394,11 @@ def skip_if_green(reason):
return skip_if_green__
return skip_if_green_
skip_copy_if_green = skip_if_green("copy in async mode currently not supported")
@decorate_all_tests
def skip_if_no_getrefcount(f):
@wraps(f)
def skip_if_no_getrefcount_(self):
@ -376,6 +409,7 @@ def skip_if_no_getrefcount(f):
return skip_if_no_getrefcount_
@decorate_all_tests
def skip_if_windows(f):
"""Skip a test if run on windows"""
@wraps(f)

View File

@ -7,5 +7,5 @@ whitelist_externals = make
[flake8]
max-line-length = 85
ignore = E128, W503
exclude = build, doc, sandbox, examples, tests/dbapi20.py
ignore = E128, W503, E741
exclude = build, doc, examples, tests/dbapi20.py