mirror of
https://github.com/psycopg/psycopg2.git
synced 2025-02-07 12:50:32 +03:00
Merge branch 'master' into feature-expose-pgconn
This commit is contained in:
commit
01f8475f53
|
@ -12,7 +12,7 @@ environment:
|
||||||
|
|
||||||
matrix:
|
matrix:
|
||||||
# For Python versions available on Appveyor, see
|
# 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: "32"}
|
||||||
- {PYVER: "27", PYTHON_ARCH: "64"}
|
- {PYVER: "27", PYTHON_ARCH: "64"}
|
||||||
- {PYVER: "34", PYTHON_ARCH: "32"}
|
- {PYVER: "34", PYTHON_ARCH: "32"}
|
||||||
|
@ -82,7 +82,7 @@ init:
|
||||||
- "%PYTHON%\\python -c \"import sys; print('64bit: ' + str(sys.maxsize > 2**32))\""
|
- "%PYTHON%\\python -c \"import sys; print('64bit: ' + str(sys.maxsize > 2**32))\""
|
||||||
|
|
||||||
# Get & Install NASM
|
# 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%"
|
#- SET PATH="C:\Program Files (x86)\nasm;%PATH%"
|
||||||
|
|
||||||
# Fix problem with VS2008 Express and 64bit builds
|
# Fix problem with VS2008 Express and 64bit builds
|
||||||
|
|
2
LICENSE
2
LICENSE
|
@ -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
|
You should have received a copy of the GNU Lesser General Public License
|
||||||
along with psycopg2 (see the doc/ directory.)
|
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
|
Alternative licenses
|
||||||
|
|
28
NEWS
28
NEWS
|
@ -6,18 +6,31 @@ What's new in psycopg 2.8
|
||||||
|
|
||||||
New features:
|
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.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
|
- `~psycopg2.extras.DictCursor` and `~psycopg2.extras.RealDictCursor` rows
|
||||||
maintain columns order (:ticket:`#177`).
|
maintain columns order (:ticket:`#177`).
|
||||||
|
- Added `!severity_nonlocalized` attribute on the
|
||||||
|
`~psycopg2.extensions.Diagnostics` object (:ticket:`#783`).
|
||||||
|
|
||||||
Other changes:
|
Other changes:
|
||||||
|
|
||||||
- Dropped support for Python 2.6, 3.2, 3.3.
|
- Dropped support for Python 2.6, 3.2, 3.3.
|
||||||
- Dropped `psycopg1` module.
|
- 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
|
- No longer use 2to3 during installation for Python 2 & 3 compatability. All
|
||||||
source files are now compatible with Python 2 & 3 as is.
|
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``.
|
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
|
- Close named cursors if exist, even if `~cursor.execute()` wasn't called
|
||||||
(:ticket:`#746`).
|
(: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
|
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.
|
- Dropped support for Python 2.4. Please use Psycopg 2.4.x if you need it.
|
||||||
- `~psycopg2.errorcodes` map updated to PostgreSQL 9.2.
|
- `~psycopg2.errorcodes` map updated to PostgreSQL 9.2.
|
||||||
- Dropped Zope adapter from source repository. ZPsycopgDA now has its own
|
- 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
|
What's new in psycopg 2.4.6
|
||||||
|
|
|
@ -156,7 +156,7 @@ geometric type:
|
||||||
|
|
||||||
|
|
||||||
.. |point| replace:: :sql:`point`
|
.. |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::
|
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.
|
mode if you wish to receive or send notifications in a timely manner.
|
||||||
|
|
||||||
.. |LISTEN| replace:: :sql:`LISTEN`
|
.. |LISTEN| replace:: :sql:`LISTEN`
|
||||||
.. _LISTEN: http://www.postgresql.org/docs/current/static/sql-listen.html
|
.. _LISTEN: https://www.postgresql.org/docs/current/static/sql-listen.html
|
||||||
.. |NOTIFY| replace:: :sql:`NOTIFY`
|
.. |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
|
Notifications are received after every query execution. If the user is
|
||||||
interested in receiving notifications but not in performing any query, the
|
interested in receiving notifications but not in performing any query, the
|
||||||
|
@ -375,7 +375,7 @@ completely non-blocking connection attempt: see the libpq documentation for
|
||||||
|PQconnectStart|_.
|
|PQconnectStart|_.
|
||||||
|
|
||||||
.. |PQconnectStart| replace:: `!PQconnectStart()`
|
.. |PQconnectStart| replace:: `!PQconnectStart()`
|
||||||
.. _PQconnectStart: http://www.postgresql.org/docs/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
|
The same loop should be also used to perform nonblocking queries: after
|
||||||
sending a query via `~cursor.execute()` or `~cursor.callproc()`, call
|
sending a query via `~cursor.execute()` or `~cursor.callproc()`, call
|
||||||
|
@ -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
|
details. You can check the `psycogreen`_ project for further informations and
|
||||||
resources about the topic.
|
resources about the topic.
|
||||||
|
|
||||||
.. _coroutine: http://en.wikipedia.org/wiki/Coroutine
|
.. _coroutine: https://en.wikipedia.org/wiki/Coroutine
|
||||||
.. _greenlet: https://pypi.org/project/greenlet/
|
.. _greenlet: https://pypi.org/project/greenlet/
|
||||||
.. _green threads: http://en.wikipedia.org/wiki/Green_threads
|
.. _green threads: https://en.wikipedia.org/wiki/Green_threads
|
||||||
.. _Eventlet: http://eventlet.net/
|
.. _Eventlet: https://eventlet.net/
|
||||||
.. _gevent: http://www.gevent.org/
|
.. _gevent: http://www.gevent.org/
|
||||||
.. _SQLAlchemy: http://www.sqlalchemy.org/
|
.. _SQLAlchemy: https://www.sqlalchemy.org/
|
||||||
.. _psycogreen: http://bitbucket.org/dvarrazzo/psycogreen/
|
.. _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::
|
.. warning::
|
||||||
|
|
||||||
|
@ -536,7 +536,7 @@ Server version 9.4 adds a new feature called *Logical Replication*.
|
||||||
|
|
||||||
- PostgreSQL `Streaming Replication Protocol`__
|
- 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
|
Logical replication Quick-Start
|
||||||
|
|
|
@ -116,12 +116,12 @@ todo_include_todos = False
|
||||||
rst_epilog = """
|
rst_epilog = """
|
||||||
.. |DBAPI| replace:: DB API 2.0
|
.. |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:
|
.. _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)`
|
.. |MVCC| replace:: :abbr:`MVCC (Multiversion concurrency control)`
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -198,7 +198,7 @@ The ``connection`` class
|
||||||
.. seealso:: the |PREPARE TRANSACTION|_ PostgreSQL command.
|
.. seealso:: the |PREPARE TRANSACTION|_ PostgreSQL command.
|
||||||
|
|
||||||
.. |PREPARE TRANSACTION| replace:: :sql:`PREPARE TRANSACTION`
|
.. |PREPARE TRANSACTION| replace:: :sql:`PREPARE TRANSACTION`
|
||||||
.. _PREPARE TRANSACTION: http://www.postgresql.org/docs/current/static/sql-prepare-transaction.html
|
.. _PREPARE TRANSACTION: https://www.postgresql.org/docs/current/static/sql-prepare-transaction.html
|
||||||
|
|
||||||
|
|
||||||
.. index::
|
.. index::
|
||||||
|
@ -224,7 +224,7 @@ The ``connection`` class
|
||||||
.. seealso:: the |COMMIT PREPARED|_ PostgreSQL command.
|
.. seealso:: the |COMMIT PREPARED|_ PostgreSQL command.
|
||||||
|
|
||||||
.. |COMMIT PREPARED| replace:: :sql:`COMMIT PREPARED`
|
.. |COMMIT PREPARED| replace:: :sql:`COMMIT PREPARED`
|
||||||
.. _COMMIT PREPARED: http://www.postgresql.org/docs/current/static/sql-commit-prepared.html
|
.. _COMMIT PREPARED: https://www.postgresql.org/docs/current/static/sql-commit-prepared.html
|
||||||
|
|
||||||
|
|
||||||
.. index::
|
.. index::
|
||||||
|
@ -246,7 +246,7 @@ The ``connection`` class
|
||||||
.. seealso:: the |ROLLBACK PREPARED|_ PostgreSQL command.
|
.. seealso:: the |ROLLBACK PREPARED|_ PostgreSQL command.
|
||||||
|
|
||||||
.. |ROLLBACK PREPARED| replace:: :sql:`ROLLBACK PREPARED`
|
.. |ROLLBACK PREPARED| replace:: :sql:`ROLLBACK PREPARED`
|
||||||
.. _ROLLBACK PREPARED: http://www.postgresql.org/docs/current/static/sql-rollback-prepared.html
|
.. _ROLLBACK PREPARED: https://www.postgresql.org/docs/current/static/sql-rollback-prepared.html
|
||||||
|
|
||||||
|
|
||||||
.. index::
|
.. index::
|
||||||
|
@ -267,7 +267,7 @@ The ``connection`` class
|
||||||
transactions initiated by a program using such driver should be
|
transactions initiated by a program using such driver should be
|
||||||
unpacked correctly.
|
unpacked correctly.
|
||||||
|
|
||||||
.. __: http://jdbc.postgresql.org/
|
.. __: https://jdbc.postgresql.org/
|
||||||
|
|
||||||
Xids returned by `!tpc_recover()` also have extra attributes
|
Xids returned by `!tpc_recover()` also have extra attributes
|
||||||
`~psycopg2.extensions.Xid.prepared`, `~psycopg2.extensions.Xid.owner`,
|
`~psycopg2.extensions.Xid.prepared`, `~psycopg2.extensions.Xid.owner`,
|
||||||
|
@ -277,7 +277,7 @@ The ``connection`` class
|
||||||
.. seealso:: the |pg_prepared_xacts|_ system view.
|
.. seealso:: the |pg_prepared_xacts|_ system view.
|
||||||
|
|
||||||
.. |pg_prepared_xacts| replace:: `pg_prepared_xacts`
|
.. |pg_prepared_xacts| replace:: `pg_prepared_xacts`
|
||||||
.. _pg_prepared_xacts: http://www.postgresql.org/docs/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|_.
|
||||||
|
|
||||||
.. |PQcancel| replace:: `!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
|
.. versionadded:: 2.3
|
||||||
|
|
||||||
|
@ -325,10 +325,10 @@ The ``connection`` class
|
||||||
available for recover.
|
available for recover.
|
||||||
|
|
||||||
.. |RESET| replace:: :sql:`RESET`
|
.. |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`
|
.. |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
|
.. versionadded:: 2.0.12
|
||||||
|
|
||||||
|
@ -342,6 +342,9 @@ The ``connection`` class
|
||||||
obscured.
|
obscured.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.. rubric:: Transaction control methods and attributes.
|
||||||
|
|
||||||
.. index::
|
.. index::
|
||||||
pair: Transaction; Autocommit
|
pair: Transaction; Autocommit
|
||||||
pair: Transaction; Isolation level
|
pair: Transaction; Isolation level
|
||||||
|
@ -366,7 +369,7 @@ The ``connection`` class
|
||||||
`autocommit` attribute.
|
`autocommit` attribute.
|
||||||
|
|
||||||
.. _isolation level:
|
.. _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.
|
Arguments set to `!None` (the default for all) will not be changed.
|
||||||
The parameters *isolation_level*, *readonly* and *deferrable* also
|
The parameters *isolation_level*, *readonly* and *deferrable* also
|
||||||
|
@ -376,11 +379,11 @@ The ``connection`` class
|
||||||
|default_transaction_read_only|__, |default_transaction_deferrable|__.
|
|default_transaction_read_only|__, |default_transaction_deferrable|__.
|
||||||
|
|
||||||
.. |default_transaction_isolation| replace:: :sql:`default_transaction_isolation`
|
.. |default_transaction_isolation| replace:: :sql:`default_transaction_isolation`
|
||||||
.. __: http://www.postgresql.org/docs/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`
|
.. |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`
|
.. |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.
|
The function must be invoked with no transaction in progress.
|
||||||
|
|
||||||
|
@ -388,7 +391,7 @@ The ``connection`` class
|
||||||
of the transaction parameters in the server.
|
of the transaction parameters in the server.
|
||||||
|
|
||||||
.. |SET TRANSACTION| replace:: :sql:`SET TRANSACTION`
|
.. |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
|
.. versionadded:: 2.4.2
|
||||||
|
|
||||||
|
@ -534,7 +537,7 @@ The ``connection`` class
|
||||||
is the encoding defined by the database. It should be one of the
|
is the encoding defined by the database. It should be one of the
|
||||||
`characters set supported by PostgreSQL`__
|
`characters set supported by PostgreSQL`__
|
||||||
|
|
||||||
.. __: http://www.postgresql.org/docs/current/static/multibyte.html
|
.. __: https://www.postgresql.org/docs/current/static/multibyte.html
|
||||||
|
|
||||||
|
|
||||||
.. index::
|
.. index::
|
||||||
|
@ -568,7 +571,7 @@ The ``connection`` class
|
||||||
configuration parameters`__ such as ``log_statement``,
|
configuration parameters`__ such as ``log_statement``,
|
||||||
``client_min_messages``, ``log_min_duration_statement`` etc.
|
``client_min_messages``, ``log_min_duration_statement`` etc.
|
||||||
|
|
||||||
.. __: http://www.postgresql.org/docs/current/static/runtime-config-logging.html
|
.. __: https://www.postgresql.org/docs/current/static/runtime-config-logging.html
|
||||||
|
|
||||||
|
|
||||||
.. attribute:: notifies
|
.. attribute:: notifies
|
||||||
|
@ -600,42 +603,14 @@ The ``connection`` class
|
||||||
|
|
||||||
|
|
||||||
.. index::
|
.. index::
|
||||||
pair: Backend; PID
|
pair: Connection; Info
|
||||||
|
|
||||||
.. method:: get_backend_pid()
|
.. attribute:: info
|
||||||
|
|
||||||
Returns the process ID (PID) of the backend server process handling
|
A `~psycopg2.extensions.ConnectionInfo` object exposing information
|
||||||
this connection.
|
about the native libpq connection.
|
||||||
|
|
||||||
Note that the PID belongs to a process executing on the database
|
.. versionadded:: 2.8.0
|
||||||
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
|
|
||||||
|
|
||||||
|
|
||||||
.. index::
|
.. index::
|
||||||
|
@ -656,61 +631,11 @@ The ``connection`` class
|
||||||
|
|
||||||
.. seealso:: libpq docs for `PQconninfo()`__ for details.
|
.. 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
|
.. 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::
|
.. index::
|
||||||
pair: Connection; Status
|
pair: Connection; Status
|
||||||
|
|
||||||
|
@ -743,7 +668,7 @@ The ``connection`` class
|
||||||
`~psycopg2.extensions.lobject` to be instantiated.
|
`~psycopg2.extensions.lobject` to be instantiated.
|
||||||
|
|
||||||
.. |lo_import| replace:: `!lo_import()`
|
.. |lo_import| replace:: `!lo_import()`
|
||||||
.. _lo_import: http://www.postgresql.org/docs/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:
|
Available values for *mode* are:
|
||||||
|
|
||||||
|
@ -828,6 +753,119 @@ The ``connection`` class
|
||||||
.. versionadded:: 2.8
|
.. 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::
|
.. testcode::
|
||||||
:hide:
|
:hide:
|
||||||
|
|
||||||
|
|
|
@ -37,46 +37,49 @@ The ``cursor`` class
|
||||||
|
|
||||||
.. attribute:: description
|
.. 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
|
For compatibility with the DB-API, every object can be unpacked as a
|
||||||
:func:`collections.namedtuple` is not available) containing information
|
7-items sequence: the attributes retuned this way are the following.
|
||||||
describing one result column:
|
For further details and other attributes available check the
|
||||||
|
`~psycopg2.extensions.Column` documentation.
|
||||||
|
|
||||||
0. `!name`: the name of the column returned.
|
0. `~psycopg2.extensions.Column.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.
|
|
||||||
|
|
||||||
This attribute will be `!None` for operations that do not return rows
|
1. `~psycopg2.extensions.Column.type_code`: the PostgreSQL OID of the
|
||||||
or if the cursor has not had an operation invoked via the
|
column.
|
||||||
|execute*|_ methods yet.
|
|
||||||
|
|
||||||
.. |pg_type| replace:: :sql:`pg_type`
|
2. `~psycopg2.extensions.Column.display_size`: the actual length of
|
||||||
.. _pg_type: http://www.postgresql.org/docs/current/static/catalog-pg-type.html
|
the column in bytes.
|
||||||
.. _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
|
3. `~psycopg2.extensions.Column.internal_size`: the size in bytes of
|
||||||
.. _NUMERIC: http://www.postgresql.org/docs/current/static/datatype-numeric.html#DATATYPE-NUMERIC-DECIMAL
|
the column associated to this column on the server.
|
||||||
.. |NUMERIC| replace:: :sql:`NUMERIC`
|
|
||||||
|
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
|
.. versionchanged:: 2.4
|
||||||
if possible, columns descriptions are named tuple instead of
|
if possible, columns descriptions are named tuple instead of
|
||||||
regular tuples.
|
regular tuples.
|
||||||
|
|
||||||
|
.. versionchanged:: 2.8
|
||||||
|
columns descriptions are instances of `!Column`, exposing extra
|
||||||
|
attributes.
|
||||||
|
|
||||||
|
.. |NUMERIC| replace:: :sql:`NUMERIC`
|
||||||
|
|
||||||
.. method:: close()
|
.. method:: close()
|
||||||
|
|
||||||
Close the cursor now (rather than whenever `del` is executed).
|
Close the cursor now (rather than whenever `del` is executed).
|
||||||
|
@ -129,7 +132,7 @@ The ``cursor`` class
|
||||||
backward scroll (see the |declare-notes|__).
|
backward scroll (see the |declare-notes|__).
|
||||||
|
|
||||||
.. |declare-notes| replace:: :sql:`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::
|
.. note::
|
||||||
|
|
||||||
|
@ -430,10 +433,10 @@ The ``cursor`` class
|
||||||
more flexibility.
|
more flexibility.
|
||||||
|
|
||||||
.. |CREATE-TABLE| replace:: :sql:`CREATE TABLE`
|
.. |CREATE-TABLE| replace:: :sql:`CREATE TABLE`
|
||||||
.. __: http://www.postgresql.org/docs/current/static/sql-createtable.html
|
.. __: https://www.postgresql.org/docs/current/static/sql-createtable.html
|
||||||
|
|
||||||
.. |INSERT-RETURNING| replace:: :sql:`INSERT ... RETURNING`
|
.. |INSERT-RETURNING| replace:: :sql:`INSERT ... RETURNING`
|
||||||
.. __: http://www.postgresql.org/docs/current/static/sql-insert.html
|
.. __: https://www.postgresql.org/docs/current/static/sql-insert.html
|
||||||
|
|
||||||
|
|
||||||
.. attribute:: query
|
.. attribute:: query
|
||||||
|
@ -620,7 +623,7 @@ The ``cursor`` class
|
||||||
...
|
...
|
||||||
|
|
||||||
.. |COPY| replace:: :sql:`COPY`
|
.. |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
|
.. versionadded:: 2.0.6
|
||||||
|
|
||||||
|
|
|
@ -39,7 +39,7 @@ From PostgreSQL documentation:
|
||||||
|
|
||||||
.. seealso:: `PostgreSQL Error Codes table`__
|
.. 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:
|
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'
|
'42P01'
|
||||||
|
|
||||||
Constants representing all the error values defined by PostgreSQL versions
|
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)
|
.. autofunction:: lookup(code)
|
||||||
|
|
60
doc/src/errors.rst
Normal file
60
doc/src/errors.rst
Normal 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
|
|
@ -94,7 +94,7 @@ introspection etc.
|
||||||
The method uses the efficient |lo_export|_ libpq function.
|
The method uses the efficient |lo_export|_ libpq function.
|
||||||
|
|
||||||
.. |lo_export| replace:: `!lo_export()`
|
.. |lo_export| replace:: `!lo_export()`
|
||||||
.. _lo_export: http://www.postgresql.org/docs/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)
|
.. method:: seek(offset, whence=0)
|
||||||
|
@ -125,7 +125,7 @@ introspection etc.
|
||||||
libpq function.
|
libpq function.
|
||||||
|
|
||||||
.. |lo_truncate| replace:: `!lo_truncate()`
|
.. |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
|
.. versionadded:: 2.2.0
|
||||||
|
|
||||||
|
@ -154,6 +154,119 @@ introspection etc.
|
||||||
Close the object and remove it from the database.
|
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='')
|
.. autoclass:: Notify(pid, channel, payload='')
|
||||||
:members: pid, channel, payload
|
:members: pid, channel, payload
|
||||||
|
|
||||||
|
@ -186,6 +299,7 @@ introspection etc.
|
||||||
message_primary
|
message_primary
|
||||||
schema_name
|
schema_name
|
||||||
severity
|
severity
|
||||||
|
severity_nonlocalized
|
||||||
source_file
|
source_file
|
||||||
source_function
|
source_function
|
||||||
source_line
|
source_line
|
||||||
|
@ -198,6 +312,9 @@ introspection etc.
|
||||||
not all the fields are available for all the errors and for all the
|
not all the fields are available for all the errors and for all the
|
||||||
server versions.
|
server versions.
|
||||||
|
|
||||||
|
.. versionadded:: 2.8
|
||||||
|
The `!severity_nonlocalized` attribute.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.. _sql-adaptation-objects:
|
.. _sql-adaptation-objects:
|
||||||
|
@ -423,20 +540,27 @@ details.
|
||||||
Used by Psycopg when adapting or casting unicode strings. See
|
Used by Psycopg when adapting or casting unicode strings. See
|
||||||
:ref:`unicode-handling`.
|
:ref:`unicode-handling`.
|
||||||
|
|
||||||
.. __: http://www.postgresql.org/docs/current/static/multibyte.html
|
.. __: https://www.postgresql.org/docs/current/static/multibyte.html
|
||||||
.. __: http://docs.python.org/library/codecs.html#standard-encodings
|
.. __: https://docs.python.org/library/codecs.html#standard-encodings
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.. index::
|
.. index::
|
||||||
single: Exceptions; Additional
|
single: Exceptions; Additional
|
||||||
|
|
||||||
|
.. _extension-exceptions:
|
||||||
|
|
||||||
Additional exceptions
|
Additional exceptions
|
||||||
---------------------
|
---------------------
|
||||||
|
|
||||||
The module exports a few exceptions in addition to the :ref:`standard ones
|
The module exports a few exceptions in addition to the :ref:`standard ones
|
||||||
<dbapi-exceptions>` defined by the |DBAPI|_.
|
<dbapi-exceptions>` defined by the |DBAPI|_.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
From psycopg 2.8 these error classes are also exposed by the
|
||||||
|
`psycopg2.errors` module.
|
||||||
|
|
||||||
|
|
||||||
.. exception:: QueryCanceledError
|
.. exception:: QueryCanceledError
|
||||||
|
|
||||||
(subclasses `~psycopg2.OperationalError`)
|
(subclasses `~psycopg2.OperationalError`)
|
||||||
|
@ -490,7 +614,7 @@ Other functions
|
||||||
|
|
||||||
.. seealso:: libpq docs for `PQlibVersion()`__.
|
.. 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)
|
.. function:: make_dsn(dsn=None, \*\*kwargs)
|
||||||
|
@ -524,7 +648,7 @@ Other functions
|
||||||
`connection URIs`__ are only supported from libpq 9.2). Raise
|
`connection URIs`__ are only supported from libpq 9.2). Raise
|
||||||
`~psycopg2.ProgrammingError` if the *dsn* is not valid.
|
`~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::
|
Example::
|
||||||
|
|
||||||
|
@ -538,7 +662,7 @@ Other functions
|
||||||
|
|
||||||
.. seealso:: libpq docs for `PQconninfoParse()`__.
|
.. 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)
|
.. function:: quote_ident(str, scope)
|
||||||
|
@ -552,7 +676,7 @@ Other functions
|
||||||
|
|
||||||
.. seealso:: libpq docs for `PQescapeIdentifier()`__
|
.. 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)
|
.. 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
|
.. seealso:: `Read Committed Isolation Level`__ in PostgreSQL
|
||||||
documentation.
|
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
|
.. 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
|
.. seealso:: `Repeatable Read Isolation Level`__ in PostgreSQL
|
||||||
documentation.
|
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
|
.. 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.
|
.. 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
|
.. data:: ISOLATION_LEVEL_DEFAULT
|
||||||
|
|
||||||
|
@ -703,7 +827,7 @@ Transaction status constants
|
||||||
----------------------------
|
----------------------------
|
||||||
|
|
||||||
These values represent the possible status of a transaction: the current value
|
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
|
.. data:: TRANSACTION_STATUS_IDLE
|
||||||
|
|
||||||
|
|
|
@ -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
|
The Python :py:mod:`json` module is used by default to convert Python objects
|
||||||
to JSON and to parse data from the database.
|
to JSON and to parse data from the database.
|
||||||
|
|
||||||
.. _JSON: http://www.json.org/
|
.. _JSON: https://www.json.org/
|
||||||
.. |pgjson| replace:: :sql:`json`
|
.. |pgjson| replace:: :sql:`json`
|
||||||
.. |jsonb| replace:: :sql:`jsonb`
|
.. |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
|
In order to pass a Python object to the database as query argument you can use
|
||||||
the `Json` adapter::
|
the `Json` adapter::
|
||||||
|
@ -664,7 +664,7 @@ can be enabled using the `register_hstore()` function.
|
||||||
|
|
||||||
|
|
||||||
.. |hstore| replace:: :sql:`hstore`
|
.. |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.
|
:py:func:`collections.namedtuple` is not found.
|
||||||
|
|
||||||
.. |CREATE TYPE| replace:: :sql:`CREATE TYPE`
|
.. |CREATE TYPE| replace:: :sql:`CREATE TYPE`
|
||||||
.. _CREATE TYPE: http://www.postgresql.org/docs/current/static/sql-createtype.html
|
.. _CREATE TYPE: https://www.postgresql.org/docs/current/static/sql-createtype.html
|
||||||
|
|
||||||
.. doctest::
|
.. 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()`.
|
user-defined |range| types can be adapted using `register_range()`.
|
||||||
|
|
||||||
.. |range| replace:: :sql:`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
|
.. 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
|
features: it doesn't perform normalization and doesn't implement all the
|
||||||
operators__ supported by the database.
|
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
|
`!Range` objects are immutable, hashable, and support the ``in`` operator
|
||||||
(checking if an element is within the range). They can be tested for
|
(checking if an element is within the range). They can be tested for
|
||||||
|
|
|
@ -40,7 +40,7 @@ I receive the error *current transaction is aborted, commands ignored until end
|
||||||
PostgreSQL supports nested transactions using the |SAVEPOINT|_ command).
|
PostgreSQL supports nested transactions using the |SAVEPOINT|_ command).
|
||||||
|
|
||||||
.. |SAVEPOINT| replace:: :sql:`SAVEPOINT`
|
.. |SAVEPOINT| replace:: :sql:`SAVEPOINT`
|
||||||
.. _SAVEPOINT: http://www.postgresql.org/docs/current/static/sql-savepoint.html
|
.. _SAVEPOINT: https://www.postgresql.org/docs/current/static/sql-savepoint.html
|
||||||
|
|
||||||
|
|
||||||
.. _faq-transaction-aborted-multiprocess:
|
.. _faq-transaction-aborted-multiprocess:
|
||||||
|
@ -184,8 +184,8 @@ Transferring binary data from PostgreSQL 9.0 doesn't work.
|
||||||
session before reading binary data;
|
session before reading binary data;
|
||||||
- upgrade the libpq library on the client to at least 9.0.
|
- upgrade the libpq library on the client to at least 9.0.
|
||||||
|
|
||||||
.. __: http://www.postgresql.org/docs/current/static/datatype-binary.html
|
.. __: https://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/runtime-config-client.html#GUC-BYTEA-OUTPUT
|
||||||
|
|
||||||
|
|
||||||
.. _faq-array:
|
.. _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.
|
:program:`pg_config` at install time and the libpq at runtime.
|
||||||
|
|
||||||
.. |lo_truncate| replace:: `!lo_truncate()`
|
.. |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:
|
.. _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.
|
use the WSGIPythonEggs__ directive.
|
||||||
|
|
||||||
.. _egg: http://peak.telecommunity.com/DevCenter/PythonEggs
|
.. _egg: http://peak.telecommunity.com/DevCenter/PythonEggs
|
||||||
.. __: http://stackoverflow.com/questions/2192323/what-is-the-python-egg-cache-python-egg-cache
|
.. __: https://stackoverflow.com/questions/2192323/what-is-the-python-egg-cache-python-egg-cache
|
||||||
.. __: http://code.google.com/p/modwsgi/wiki/ConfigurationDirectives#WSGIPythonEggs
|
.. __: https://modwsgi.readthedocs.io/en/develop/configuration-directives/WSGIPythonEggs.html
|
||||||
|
|
|
@ -24,9 +24,9 @@ Psycopg 2 is both Unicode and Python 3 friendly.
|
||||||
|
|
||||||
|
|
||||||
.. _Psycopg: http://initd.org/psycopg/
|
.. _Psycopg: http://initd.org/psycopg/
|
||||||
.. _PostgreSQL: http://www.postgresql.org/
|
.. _PostgreSQL: https://www.postgresql.org/
|
||||||
.. _Python: http://www.python.org/
|
.. _Python: https://www.python.org/
|
||||||
.. _libpq: http://www.postgresql.org/docs/current/static/libpq.html
|
.. _libpq: https://www.postgresql.org/docs/current/static/libpq.html
|
||||||
|
|
||||||
|
|
||||||
.. rubric:: Contents
|
.. rubric:: Contents
|
||||||
|
@ -42,6 +42,7 @@ Psycopg 2 is both Unicode and Python 3 friendly.
|
||||||
advanced
|
advanced
|
||||||
extensions
|
extensions
|
||||||
extras
|
extras
|
||||||
|
errors
|
||||||
sql
|
sql
|
||||||
tz
|
tz
|
||||||
pool
|
pool
|
||||||
|
|
|
@ -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
|
there is an experimental `porting of Psycopg for Ctypes`__, but it is not as
|
||||||
mature as the C implementation yet.
|
mature as the C implementation yet.
|
||||||
|
|
||||||
.. _PostgreSQL: http://www.postgresql.org/
|
.. _PostgreSQL: https://www.postgresql.org/
|
||||||
.. _Python: http://www.python.org/
|
.. _Python: https://www.python.org/
|
||||||
.. _libpq: http://www.postgresql.org/docs/current/static/libpq.html
|
.. _libpq: https://www.postgresql.org/docs/current/static/libpq.html
|
||||||
.. _CPython: http://en.wikipedia.org/wiki/CPython
|
.. _CPython: https://en.wikipedia.org/wiki/CPython
|
||||||
.. _Ctypes: http://docs.python.org/library/ctypes.html
|
.. _Ctypes: https://docs.python.org/library/ctypes.html
|
||||||
.. __: https://github.com/mvantellingen/psycopg2-ctypes
|
.. __: 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_
|
||||||
.. _PyPI-binary: https://pypi.org/project/psycopg2-binary/
|
.. _PyPI-binary: https://pypi.org/project/psycopg2-binary/
|
||||||
.. _wheel: http://pythonwheels.com/
|
.. _wheel: https://pythonwheels.com/
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
|
||||||
|
|
|
@ -50,11 +50,11 @@ The module interface respects the standard defined in the |DBAPI|_.
|
||||||
using `environment variables`__.
|
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
|
Using the *connection_factory* parameter a different class or
|
||||||
connections factory can be specified. It should be a callable object
|
connections factory can be specified. It should be a callable object
|
||||||
|
@ -117,9 +117,10 @@ The module interface respects the standard defined in the |DBAPI|_.
|
||||||
|
|
||||||
Integer constant reporting the version of the ``libpq`` library this
|
Integer constant reporting the version of the ``libpq`` library this
|
||||||
``psycopg2`` module was compiled with (in the same format of
|
``psycopg2`` module was compiled with (in the same format of
|
||||||
`~connection.server_version`). If this value is greater or equal than
|
`~psycopg2.extensions.ConnectionInfo.server_version`). If this value is
|
||||||
``90100`` then you may query the version of the actually loaded library
|
greater or equal than ``90100`` then you may query the version of the
|
||||||
using the `~psycopg2.extensions.libpq_version()` function.
|
actually loaded library using the `~psycopg2.extensions.libpq_version()`
|
||||||
|
function.
|
||||||
|
|
||||||
|
|
||||||
.. index::
|
.. index::
|
||||||
|
@ -250,13 +251,14 @@ available through the following exceptions:
|
||||||
|
|
||||||
.. extension::
|
.. extension::
|
||||||
|
|
||||||
Psycopg may raise a few other, more specialized, exceptions: currently
|
Psycopg actually raises a different exception for each :sql:`SQLSTATE`
|
||||||
`~psycopg2.extensions.QueryCanceledError` and
|
error returned by the database: the classes are available in the
|
||||||
`~psycopg2.extensions.TransactionRollbackError` are defined. These
|
`psycopg2.errors` module. Every exception class is a subclass of one of
|
||||||
exceptions are not exposed by the main `!psycopg2` module but are
|
the exception classes defined here though, so they don't need to be
|
||||||
made available by the `~psycopg2.extensions` module. All the
|
trapped specifically: trapping `!Error` or `!DatabaseError` is usually
|
||||||
additional exceptions are subclasses of standard |DBAPI| exceptions, so
|
what needed to write a generic error handler; trapping a specific error
|
||||||
trapping them specifically is not required.
|
such as `!NotNullViolation` can be useful to write specific exception
|
||||||
|
handlers.
|
||||||
|
|
||||||
|
|
||||||
This is the exception inheritance layout:
|
This is the exception inheritance layout:
|
||||||
|
@ -270,8 +272,6 @@ This is the exception inheritance layout:
|
||||||
\|__ `DatabaseError`
|
\|__ `DatabaseError`
|
||||||
\|__ `DataError`
|
\|__ `DataError`
|
||||||
\|__ `OperationalError`
|
\|__ `OperationalError`
|
||||||
\| \|__ `psycopg2.extensions.QueryCanceledError`
|
|
||||||
\| \|__ `psycopg2.extensions.TransactionRollbackError`
|
|
||||||
\|__ `IntegrityError`
|
\|__ `IntegrityError`
|
||||||
\|__ `InternalError`
|
\|__ `InternalError`
|
||||||
\|__ `ProgrammingError`
|
\|__ `ProgrammingError`
|
||||||
|
|
|
@ -77,16 +77,26 @@ to cursor methods such as `~cursor.execute()`, `~cursor.executemany()`,
|
||||||
|
|
||||||
.. autoclass:: Identifier
|
.. 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
|
.. autoclass:: Literal
|
||||||
|
|
||||||
.. autoattribute:: wrapped
|
.. autoattribute:: wrapped
|
||||||
|
|
||||||
|
|
||||||
.. autoclass:: Placeholder
|
.. autoclass:: Placeholder
|
||||||
|
|
||||||
.. autoattribute:: name
|
.. autoattribute:: name
|
||||||
|
|
||||||
|
|
||||||
.. autoclass:: Composed
|
.. autoclass:: Composed
|
||||||
|
|
||||||
.. autoattribute:: seq
|
.. autoattribute:: seq
|
||||||
|
|
|
@ -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
|
attack to database servers. Before continuing, please print `this page`__ as a
|
||||||
memo and hang it onto your desk.
|
memo and hang it onto your desk.
|
||||||
|
|
||||||
.. _SQL injection: http://en.wikipedia.org/wiki/SQL_injection
|
.. _SQL injection: https://en.wikipedia.org/wiki/SQL_injection
|
||||||
.. __: http://xkcd.com/327/
|
.. __: https://xkcd.com/327/
|
||||||
|
|
||||||
Psycopg can `automatically convert Python objects to and from SQL
|
Psycopg can `automatically convert Python objects to and from SQL
|
||||||
literals`__: using this feature your code will be more robust and
|
literals`__: using this feature your code will be more robust and
|
||||||
|
@ -221,6 +221,27 @@ argument of the `~cursor.execute()` method::
|
||||||
>>> cur.execute(SQL, data) # Note: no % operator
|
>>> 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::
|
.. index::
|
||||||
single: Adaptation
|
single: Adaptation
|
||||||
|
@ -351,7 +372,7 @@ converted into `!Decimal`.
|
||||||
This of course may imply a loss of precision.
|
This of course may imply a loss of precision.
|
||||||
|
|
||||||
.. seealso:: `PostgreSQL numeric types
|
.. 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::
|
.. 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))
|
>>> cur.execute("INSERT INTO test (num, data) VALUES (%s,%s);", (74, u))
|
||||||
|
|
||||||
.. __: http://www.postgresql.org/docs/current/static/multibyte.html
|
.. __: https://www.postgresql.org/docs/current/static/multibyte.html
|
||||||
.. __: http://docs.python.org/library/codecs.html#standard-encodings
|
.. __: https://docs.python.org/library/codecs.html
|
||||||
|
|
||||||
When reading data from the database, in Python 2 the strings returned are
|
When reading data from the database, in Python 2 the strings returned are
|
||||||
usually 8 bit `!str` objects encoded in the database client encoding::
|
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)
|
be usable as binary type. Received data is returned as `!buffer` (in Python 2)
|
||||||
or `!memoryview` (in Python 3).
|
or `!memoryview` (in Python 3).
|
||||||
|
|
||||||
.. __: http://www.python.org/dev/peps/pep-3118/
|
.. __: https://www.python.org/dev/peps/pep-3118/
|
||||||
|
|
||||||
.. versionchanged:: 2.4
|
.. versionchanged:: 2.4
|
||||||
only strings were supported before.
|
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
|
server configuration file or in the client session (using a query such as
|
||||||
``SET bytea_output TO escape;``) before receiving binary data.
|
``SET bytea_output TO escape;``) before receiving binary data.
|
||||||
|
|
||||||
.. __: http://www.postgresql.org/docs/current/static/datatype-binary.html
|
.. __: https://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/runtime-config-client.html#GUC-BYTEA-OUTPUT
|
||||||
|
|
||||||
|
|
||||||
.. index::
|
.. index::
|
||||||
|
@ -527,7 +548,7 @@ the same way::
|
||||||
"SELECT '38 days 6027.425337 seconds';"
|
"SELECT '38 days 6027.425337 seconds';"
|
||||||
|
|
||||||
.. seealso:: `PostgreSQL date/time types
|
.. 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::
|
.. 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 ()`
|
Furthermore :sql:`ANY` can also work with empty lists, whereas :sql:`IN ()`
|
||||||
is a SQL syntax error.
|
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::
|
.. note::
|
||||||
|
|
||||||
|
@ -846,7 +867,7 @@ lifetime extends well after `~connection.commit()`, calling
|
||||||
|
|
||||||
|
|
||||||
.. |DECLARE| replace:: :sql:`DECLARE`
|
.. |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
|
forking web deploy method such as FastCGI make sure to create the connections
|
||||||
*after* the fork.
|
*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
|
Connections shouldn't be shared either by different green threads: see
|
||||||
:ref:`green-support` for further details.
|
:ref:`green-support` for further details.
|
||||||
|
@ -920,7 +941,7 @@ Please refer to the documentation of the single methods for details and
|
||||||
examples.
|
examples.
|
||||||
|
|
||||||
.. |COPY| replace:: :sql:`COPY`
|
.. |COPY| replace:: :sql:`COPY`
|
||||||
.. __: http://www.postgresql.org/docs/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
|
are useful with data values too large to be manipulated conveniently as a
|
||||||
whole.
|
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
|
Psycopg allows access to the large object using the
|
||||||
`~psycopg2.extensions.lobject` class. Objects are generated using the
|
`~psycopg2.extensions.lobject` class. Objects are generated using the
|
||||||
|
@ -948,9 +969,9 @@ Psycopg large object support efficient import/export with file system files
|
||||||
using the |lo_import|_ and |lo_export|_ libpq functions.
|
using the |lo_import|_ and |lo_export|_ libpq functions.
|
||||||
|
|
||||||
.. |lo_import| replace:: `!lo_import()`
|
.. |lo_import| replace:: `!lo_import()`
|
||||||
.. _lo_import: http://www.postgresql.org/docs/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| 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
|
.. versionchanged:: 2.6
|
||||||
added support for large objects greated than 2GB. Note that the support is
|
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.
|
For further details see the documentation for the above methods.
|
||||||
|
|
||||||
.. __: http://www.opengroup.org/bookstore/catalog/c193.htm
|
.. __: https://publications.opengroup.org/c193
|
||||||
.. __: http://jdbc.postgresql.org/
|
.. __: https://jdbc.postgresql.org/
|
||||||
|
|
|
@ -8,8 +8,8 @@ small and fast, and stable as a rock.
|
||||||
|
|
||||||
Homepage: http://initd.org/projects/psycopg2
|
Homepage: http://initd.org/projects/psycopg2
|
||||||
|
|
||||||
.. _PostgreSQL: http://www.postgresql.org/
|
.. _PostgreSQL: https://www.postgresql.org/
|
||||||
.. _Python: http://www.python.org/
|
.. _Python: https://www.python.org/
|
||||||
|
|
||||||
:Groups:
|
:Groups:
|
||||||
* `Connections creation`: connect
|
* `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
|
# Note: the first internal import should be _psycopg, otherwise the real cause
|
||||||
# of a failed loading of the C module may get hidden, see
|
# of a failed loading of the C module may get hidden, see
|
||||||
# http://archives.postgresql.org/psycopg/2011-02/msg00044.php
|
# https://archives.postgresql.org/psycopg/2011-02/msg00044.php
|
||||||
|
|
||||||
# Import the DBAPI-2.0 stuff into top-level module.
|
# 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.
|
# Register the Decimal adapter here instead of in the C layer.
|
||||||
# This way a new class is registered for each sub-interpreter.
|
# This way a new class is registered for each sub-interpreter.
|
||||||
# See ticket #52
|
# See ticket #52
|
||||||
from decimal import Decimal
|
from decimal import Decimal # noqa
|
||||||
from psycopg2._psycopg import Decimal as Adapter
|
from psycopg2._psycopg import Decimal as Adapter # noqa
|
||||||
_ext.register_adapter(Decimal, Adapter)
|
_ext.register_adapter(Decimal, Adapter)
|
||||||
del Decimal, Adapter
|
del Decimal, Adapter
|
||||||
|
|
||||||
|
|
|
@ -185,7 +185,7 @@ def _get_json_oids(conn_or_curs, name='json'):
|
||||||
conn_status = conn.status
|
conn_status = conn.status
|
||||||
|
|
||||||
# column typarray not available before PG 8.3
|
# 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
|
# get the oid for the hstore
|
||||||
curs.execute(
|
curs.execute(
|
||||||
|
|
|
@ -62,6 +62,19 @@ class Range(object):
|
||||||
return "%s(%r, %r, %r)" % (self.__class__.__name__,
|
return "%s(%r, %r, %r)" % (self.__class__.__name__,
|
||||||
self._lower, self._upper, self._bounds)
|
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
|
@property
|
||||||
def lower(self):
|
def lower(self):
|
||||||
"""The lower bound of the range. `!None` if empty or unbound."""
|
"""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
|
from psycopg2.extras import _solve_conn_curs
|
||||||
conn, curs = _solve_conn_curs(conn_or_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"
|
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
|
# Store the transaction status of the connection to revert it after use
|
||||||
conn_status = conn.status
|
conn_status = conn.status
|
||||||
|
@ -493,10 +506,10 @@ class NumberRangeAdapter(RangeAdapter):
|
||||||
return ("'%s%s,%s%s'" % (
|
return ("'%s%s,%s%s'" % (
|
||||||
r._bounds[0], lower, upper, r._bounds[1])).encode('ascii')
|
r._bounds[0], lower, upper, r._bounds[1])).encode('ascii')
|
||||||
|
|
||||||
|
|
||||||
# TODO: probably won't work with infs, nans and other tricky cases.
|
# TODO: probably won't work with infs, nans and other tricky cases.
|
||||||
register_adapter(NumericRange, NumberRangeAdapter)
|
register_adapter(NumericRange, NumberRangeAdapter)
|
||||||
|
|
||||||
|
|
||||||
# Register globally typecasters and adapters for builtin range types.
|
# Register globally typecasters and adapters for builtin range types.
|
||||||
|
|
||||||
# note: the adapter is registered more than once, but this is harmless.
|
# note: the adapter is registered more than once, but this is harmless.
|
||||||
|
|
|
@ -26,7 +26,7 @@ This module contains symbolic names for all PostgreSQL error codes.
|
||||||
#
|
#
|
||||||
# Based on:
|
# 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'
|
INVALID_INDICATOR_PARAMETER_VALUE = '22010'
|
||||||
SUBSTRING_ERROR = '22011'
|
SUBSTRING_ERROR = '22011'
|
||||||
DIVISION_BY_ZERO = '22012'
|
DIVISION_BY_ZERO = '22012'
|
||||||
|
INVALID_PRECEDING_OR_FOLLOWING_SIZE = '22013'
|
||||||
INVALID_ARGUMENT_FOR_NTILE_FUNCTION = '22014'
|
INVALID_ARGUMENT_FOR_NTILE_FUNCTION = '22014'
|
||||||
INTERVAL_FIELD_OVERFLOW = '22015'
|
INTERVAL_FIELD_OVERFLOW = '22015'
|
||||||
INVALID_ARGUMENT_FOR_NTH_VALUE_FUNCTION = '22016'
|
INVALID_ARGUMENT_FOR_NTH_VALUE_FUNCTION = '22016'
|
||||||
|
|
1314
lib/errors.py
Normal file
1314
lib/errors.py
Normal file
File diff suppressed because it is too large
Load Diff
|
@ -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
|
- `adapt()` -- exposes the PEP-246_ compatible adapting mechanism used
|
||||||
by psycopg to adapt Python types to PostgreSQL ones
|
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
|
# psycopg/extensions.py - DBAPI-2.0 extensions specific to psycopg
|
||||||
#
|
#
|
||||||
|
@ -61,7 +61,7 @@ from psycopg2._psycopg import ( # noqa
|
||||||
adapt, adapters, encodings, connection, cursor,
|
adapt, adapters, encodings, connection, cursor,
|
||||||
lobject, Xid, libpq_version, parse_dsn, quote_ident,
|
lobject, Xid, libpq_version, parse_dsn, quote_ident,
|
||||||
string_types, binary_types, new_type, new_array_type, register_type,
|
string_types, binary_types, new_type, new_array_type, register_type,
|
||||||
ISQLQuote, Notify, Diagnostics, Column,
|
ISQLQuote, Notify, Diagnostics, Column, ConnectionInfo,
|
||||||
QueryCanceledError, TransactionRollbackError,
|
QueryCanceledError, TransactionRollbackError,
|
||||||
set_wait_callback, get_wait_callback, encrypt_password, )
|
set_wait_callback, get_wait_callback, encrypt_password, )
|
||||||
|
|
||||||
|
|
|
@ -31,10 +31,7 @@ import time as _time
|
||||||
import re as _re
|
import re as _re
|
||||||
from collections import namedtuple, OrderedDict
|
from collections import namedtuple, OrderedDict
|
||||||
|
|
||||||
try:
|
|
||||||
import logging as _logging
|
import logging as _logging
|
||||||
except:
|
|
||||||
_logging = None
|
|
||||||
|
|
||||||
import psycopg2
|
import psycopg2
|
||||||
from psycopg2 import extensions as _ext
|
from psycopg2 import extensions as _ext
|
||||||
|
@ -189,7 +186,7 @@ class DictRow(list):
|
||||||
def get(self, x, default=None):
|
def get(self, x, default=None):
|
||||||
try:
|
try:
|
||||||
return self[x]
|
return self[x]
|
||||||
except:
|
except Exception:
|
||||||
return default
|
return default
|
||||||
|
|
||||||
def copy(self):
|
def copy(self):
|
||||||
|
@ -404,7 +401,7 @@ class NamedTupleCursor(_cursor):
|
||||||
class LoggingConnection(_connection):
|
class LoggingConnection(_connection):
|
||||||
"""A connection that logs all queries to a file or logger__ object.
|
"""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):
|
def initialize(self, logobj):
|
||||||
|
@ -638,8 +635,8 @@ class ReplicationCursor(_replicationCursor):
|
||||||
class UUID_adapter(object):
|
class UUID_adapter(object):
|
||||||
"""Adapt Python's uuid.UUID__ type to PostgreSQL's uuid__.
|
"""Adapt Python's uuid.UUID__ type to PostgreSQL's uuid__.
|
||||||
|
|
||||||
.. __: http://docs.python.org/library/uuid.html
|
.. __: https://docs.python.org/library/uuid.html
|
||||||
.. __: http://www.postgresql.org/docs/current/static/datatype-uuid.html
|
.. __: https://www.postgresql.org/docs/current/static/datatype-uuid.html
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, uuid):
|
def __init__(self, uuid):
|
||||||
|
@ -806,7 +803,7 @@ class HstoreAdapter(object):
|
||||||
self.conn = conn
|
self.conn = conn
|
||||||
|
|
||||||
# use an old-style getquoted implementation if required
|
# use an old-style getquoted implementation if required
|
||||||
if conn.server_version < 90000:
|
if conn.info.server_version < 90000:
|
||||||
self.getquoted = self._getquoted_8
|
self.getquoted = self._getquoted_8
|
||||||
|
|
||||||
def _getquoted_8(self):
|
def _getquoted_8(self):
|
||||||
|
@ -911,7 +908,7 @@ class HstoreAdapter(object):
|
||||||
conn_status = conn.status
|
conn_status = conn.status
|
||||||
|
|
||||||
# column typarray not available before PG 8.3
|
# 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 = [], []
|
rv0, rv1 = [], []
|
||||||
|
|
||||||
|
@ -1097,7 +1094,7 @@ class CompositeCaster(object):
|
||||||
schema = 'public'
|
schema = 'public'
|
||||||
|
|
||||||
# column typarray not available before PG 8.3
|
# 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
|
# get the type oid and attributes
|
||||||
curs.execute("""\
|
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)])
|
[(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
|
# 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
|
# there will be some decoding error because of stupid codec used, and Py3
|
||||||
# doesn't implement % on bytes.
|
# doesn't implement % on bytes.
|
||||||
|
|
|
@ -105,7 +105,7 @@ class AbstractConnectionPool(object):
|
||||||
# Return the connection into a consistent state before putting
|
# Return the connection into a consistent state before putting
|
||||||
# it back into the pool
|
# it back into the pool
|
||||||
if not conn.closed:
|
if not conn.closed:
|
||||||
status = conn.get_transaction_status()
|
status = conn.info.transaction_status
|
||||||
if status == _ext.TRANSACTION_STATUS_UNKNOWN:
|
if status == _ext.TRANSACTION_STATUS_UNKNOWN:
|
||||||
# server connection lost
|
# server connection lost
|
||||||
conn.close()
|
conn.close()
|
||||||
|
@ -138,7 +138,7 @@ class AbstractConnectionPool(object):
|
||||||
for conn in self._pool + list(self._used.values()):
|
for conn in self._pool + list(self._used.values()):
|
||||||
try:
|
try:
|
||||||
conn.close()
|
conn.close()
|
||||||
except:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
self.closed = True
|
self.closed = True
|
||||||
|
|
||||||
|
|
48
lib/sql.py
48
lib/sql.py
|
@ -290,7 +290,7 @@ class SQL(Composable):
|
||||||
|
|
||||||
class Identifier(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
|
Identifiers usually represent names of database objects, such as tables or
|
||||||
fields. PostgreSQL identifiers follow `different rules`__ than SQL string
|
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))
|
>>> print(sql.SQL(', ').join([t1, t2, t3]).as_string(conn))
|
||||||
"foo", "ba'r", "ba""z"
|
"foo", "ba'r", "ba""z"
|
||||||
|
|
||||||
"""
|
Multiple strings can be passed to the object to represent a qualified name,
|
||||||
def __init__(self, string):
|
i.e. a dot-separated sequence of identifiers.
|
||||||
if not isinstance(string, string_types):
|
|
||||||
raise TypeError("SQL identifiers must be strings")
|
|
||||||
|
|
||||||
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
|
@property
|
||||||
def string(self):
|
def string(self):
|
||||||
"""The string wrapped by the `Identifier`."""
|
"""The string wrapped by the `Identifier`.
|
||||||
return self._wrapped
|
"""
|
||||||
|
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):
|
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):
|
class Literal(Composable):
|
||||||
|
|
|
@ -44,7 +44,7 @@ class FixedOffsetTimezone(datetime.tzinfo):
|
||||||
offset and name that instance will be returned. This saves memory and
|
offset and name that instance will be returned. This saves memory and
|
||||||
improves comparability.
|
improves comparability.
|
||||||
|
|
||||||
.. __: http://docs.python.org/library/datetime.html#datetime-tzinfo
|
.. __: https://docs.python.org/library/datetime.html
|
||||||
"""
|
"""
|
||||||
_name = None
|
_name = None
|
||||||
_offset = ZERO
|
_offset = ZERO
|
||||||
|
@ -132,6 +132,7 @@ class LocalTimezone(datetime.tzinfo):
|
||||||
tt = time.localtime(stamp)
|
tt = time.localtime(stamp)
|
||||||
return tt.tm_isdst > 0
|
return tt.tm_isdst > 0
|
||||||
|
|
||||||
|
|
||||||
LOCAL = LocalTimezone()
|
LOCAL = LocalTimezone()
|
||||||
|
|
||||||
# TODO: pre-generate some interesting time zones?
|
# TODO: pre-generate some interesting time zones?
|
||||||
|
|
|
@ -55,6 +55,8 @@ list_quote(listObject *self)
|
||||||
/* empty arrays are converted to NULLs (still searching for a way to
|
/* empty arrays are converted to NULLs (still searching for a way to
|
||||||
insert an empty array in postgresql */
|
insert an empty array in postgresql */
|
||||||
if (len == 0) {
|
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("'{}'");
|
res = Bytes_FromString("'{}'");
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
@ -79,7 +81,19 @@ list_quote(listObject *self)
|
||||||
|
|
||||||
/* Lists of arrays containing only nulls are also not supported
|
/* Lists of arrays containing only nulls are also not supported
|
||||||
* by the ARRAY construct so we should do some special casing */
|
* 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;
|
all_nulls = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
48
psycopg/column.h
Normal file
48
psycopg/column.h
Normal 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
375
psycopg/column_type.c
Normal 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*/
|
||||||
|
};
|
|
@ -29,6 +29,7 @@
|
||||||
#include "psycopg/connection.h"
|
#include "psycopg/connection.h"
|
||||||
#include "psycopg/cursor.h"
|
#include "psycopg/cursor.h"
|
||||||
#include "psycopg/pqpath.h"
|
#include "psycopg/pqpath.h"
|
||||||
|
#include "psycopg/conninfo.h"
|
||||||
#include "psycopg/lobject.h"
|
#include "psycopg/lobject.h"
|
||||||
#include "psycopg/green.h"
|
#include "psycopg/green.h"
|
||||||
#include "psycopg/xid.h"
|
#include "psycopg/xid.h"
|
||||||
|
@ -757,6 +758,7 @@ psyco_conn_readonly_get(connectionObject *self)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Py_XINCREF(rv);
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -803,6 +805,7 @@ psyco_conn_deferrable_get(connectionObject *self)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Py_XINCREF(rv);
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1005,6 +1008,20 @@ psyco_conn_get_backend_pid(connectionObject *self)
|
||||||
return PyInt_FromLong((long)PQbackendPID(self->pgconn));
|
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 */
|
/* reset the currect connection */
|
||||||
|
|
||||||
#define psyco_conn_reset_doc \
|
#define psyco_conn_reset_doc \
|
||||||
|
@ -1258,6 +1275,9 @@ static struct PyGetSetDef connectionObject_getsets[] = {
|
||||||
(getter)psyco_conn_deferrable_get,
|
(getter)psyco_conn_deferrable_get,
|
||||||
(setter)psyco_conn_deferrable_set,
|
(setter)psyco_conn_deferrable_set,
|
||||||
psyco_conn_deferrable_doc },
|
psyco_conn_deferrable_doc },
|
||||||
|
{ "info",
|
||||||
|
(getter)psyco_conn_info_get, NULL,
|
||||||
|
psyco_conn_info_get_doc },
|
||||||
{NULL}
|
{NULL}
|
||||||
};
|
};
|
||||||
#undef EXCEPTION_GETTER
|
#undef EXCEPTION_GETTER
|
||||||
|
|
40
psycopg/conninfo.h
Normal file
40
psycopg/conninfo.h
Normal 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
614
psycopg/conninfo_type.c
Normal 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*/
|
||||||
|
};
|
|
@ -29,8 +29,11 @@
|
||||||
#include "psycopg/diagnostics.h"
|
#include "psycopg/diagnostics.h"
|
||||||
#include "psycopg/error.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
|
#ifndef PG_DIAG_SCHEMA_NAME
|
||||||
#define PG_DIAG_SCHEMA_NAME 's'
|
#define PG_DIAG_SCHEMA_NAME 's'
|
||||||
#endif
|
#endif
|
||||||
|
@ -47,6 +50,11 @@
|
||||||
#define PG_DIAG_CONSTRAINT_NAME 'n'
|
#define PG_DIAG_CONSTRAINT_NAME 'n'
|
||||||
#endif
|
#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.
|
/* 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[] = {
|
static struct PyGetSetDef diagnosticsObject_getsets[] = {
|
||||||
{ "severity", (getter)psyco_diagnostics_get_field, NULL,
|
{ "severity", (getter)psyco_diagnostics_get_field, NULL,
|
||||||
NULL, (void*) PG_DIAG_SEVERITY },
|
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,
|
{ "sqlstate", (getter)psyco_diagnostics_get_field, NULL,
|
||||||
NULL, (void*) PG_DIAG_SQLSTATE },
|
NULL, (void*) PG_DIAG_SQLSTATE },
|
||||||
{ "message_primary", (getter)psyco_diagnostics_get_field, NULL,
|
{ "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"
|
"Please refer to the `PostgreSQL documentation`__ for the meaning of all"
|
||||||
" the attributes.\n\n"
|
" the attributes.\n\n"
|
||||||
".. |PQresultErrorField| replace:: `!PQresultErrorField()`\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"
|
"libpq-exec.html#LIBPQ-PQRESULTERRORFIELD\n"
|
||||||
".. __: PQresultErrorField_\n";
|
".. __: PQresultErrorField_\n";
|
||||||
|
|
||||||
|
|
|
@ -48,7 +48,7 @@ extern "C" {
|
||||||
"See `~psycopg2.extras.wait_select()` for an example of a wait callback\n" \
|
"See `~psycopg2.extras.wait_select()` for an example of a wait callback\n" \
|
||||||
"implementation.\n" \
|
"implementation.\n" \
|
||||||
"\n" \
|
"\n" \
|
||||||
".. _Eventlet: http://eventlet.net/\n" \
|
".. _Eventlet: https://eventlet.net/\n" \
|
||||||
".. _gevent: http://www.gevent.org/\n"
|
".. _gevent: http://www.gevent.org/\n"
|
||||||
HIDDEN PyObject *psyco_set_wait_callback(PyObject *self, PyObject *obj);
|
HIDDEN PyObject *psyco_set_wait_callback(PyObject *self, PyObject *obj);
|
||||||
|
|
||||||
|
|
|
@ -40,7 +40,7 @@ static const char notify_doc[] =
|
||||||
static const char pid_doc[] =
|
static const char pid_doc[] =
|
||||||
"The ID of the backend process that sent the notification.\n\n"
|
"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"
|
"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[] =
|
static const char channel_doc[] =
|
||||||
"The name of the channel to which the notification was sent.";
|
"The name of the channel to which the notification was sent.";
|
||||||
|
|
122
psycopg/pqpath.c
122
psycopg/pqpath.c
|
@ -41,6 +41,7 @@
|
||||||
#include "psycopg/typecast.h"
|
#include "psycopg/typecast.h"
|
||||||
#include "psycopg/pgtypes.h"
|
#include "psycopg/pgtypes.h"
|
||||||
#include "psycopg/error.h"
|
#include "psycopg/error.h"
|
||||||
|
#include "psycopg/column.h"
|
||||||
|
|
||||||
#include "psycopg/libpq_support.h"
|
#include "psycopg/libpq_support.h"
|
||||||
#include "libpq-fe.h"
|
#include "libpq-fe.h"
|
||||||
|
@ -76,13 +77,57 @@ strip_severity(const char *msg)
|
||||||
return 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
|
/* Returns the Python exception corresponding to an SQLSTATE error
|
||||||
code. A list of error codes can be found at:
|
code. A list of error codes can be found at:
|
||||||
|
|
||||||
http://www.postgresql.org/docs/current/static/errcodes-appendix.html */
|
https://www.postgresql.org/docs/current/static/errcodes-appendix.html */
|
||||||
BORROWED static PyObject *
|
BORROWED static PyObject *
|
||||||
exception_from_sqlstate(const char *sqlstate)
|
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]) {
|
switch (sqlstate[0]) {
|
||||||
case '0':
|
case '0':
|
||||||
switch (sqlstate[1]) {
|
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
|
* The function will block only if a command is active and the
|
||||||
* necessary response data has not yet been read by PQconsumeInput.
|
* 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 *
|
PGresult *
|
||||||
pq_get_last_result(connectionObject *conn)
|
pq_get_last_result(connectionObject *conn)
|
||||||
{
|
{
|
||||||
PGresult *result = NULL, *res;
|
PGresult *result = NULL, *res;
|
||||||
|
ExecStatusType status;
|
||||||
|
|
||||||
/* Read until PQgetResult gives a NULL */
|
/* Read until PQgetResult gives a NULL */
|
||||||
while (NULL != (res = PQgetResult(conn->pgconn))) {
|
while (NULL != (res = PQgetResult(conn->pgconn))) {
|
||||||
|
@ -1124,11 +1170,15 @@ pq_get_last_result(connectionObject *conn)
|
||||||
PQclear(result);
|
PQclear(result);
|
||||||
}
|
}
|
||||||
result = res;
|
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
|
* PGresult for us every time we query for it, so we need to
|
||||||
* break out of this endless loop. */
|
* 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;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1201,12 +1251,17 @@ _pq_fetch_tuples(cursorObject *curs)
|
||||||
Oid ftype = PQftype(curs->pgres, i);
|
Oid ftype = PQftype(curs->pgres, i);
|
||||||
int fsize = PQfsize(curs->pgres, i);
|
int fsize = PQfsize(curs->pgres, i);
|
||||||
int fmod = PQfmod(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 *type = NULL;
|
||||||
PyObject *cast = 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:
|
/* fill the right cast function by accessing three different dictionaries:
|
||||||
- the per-cursor dictionary, if available (can be NULL or None)
|
- 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)))) {
|
curs->conn, PQfname(curs->pgres, i)))) {
|
||||||
goto err_for;
|
goto err_for;
|
||||||
}
|
}
|
||||||
PyTuple_SET_ITEM(dtitem, 0, tmp);
|
column->name = tmp;
|
||||||
}
|
}
|
||||||
PyTuple_SET_ITEM(dtitem, 1, type);
|
column->type_code = type;
|
||||||
type = NULL;
|
type = NULL;
|
||||||
|
|
||||||
/* 2/ display size is the maximum size of this field result tuples. */
|
/* 2/ display size is the maximum size of this field result tuples. */
|
||||||
if (dsize && dsize[i] >= 0) {
|
if (dsize && dsize[i] >= 0) {
|
||||||
PyObject *tmp;
|
PyObject *tmp;
|
||||||
if (!(tmp = PyInt_FromLong(dsize[i]))) { goto err_for; }
|
if (!(tmp = PyInt_FromLong(dsize[i]))) { goto err_for; }
|
||||||
PyTuple_SET_ITEM(dtitem, 2, tmp);
|
column->display_size = tmp;
|
||||||
}
|
|
||||||
else {
|
|
||||||
Py_INCREF(Py_None);
|
|
||||||
PyTuple_SET_ITEM(dtitem, 2, Py_None);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 3/ size on the backend */
|
/* 3/ size on the backend */
|
||||||
|
@ -1265,18 +1316,18 @@ _pq_fetch_tuples(cursorObject *curs)
|
||||||
if (ftype == NUMERICOID) {
|
if (ftype == NUMERICOID) {
|
||||||
PyObject *tmp;
|
PyObject *tmp;
|
||||||
if (!(tmp = PyInt_FromLong((fmod >> 16)))) { goto err_for; }
|
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 */
|
else { /* If variable length record, return maximum size */
|
||||||
PyObject *tmp;
|
PyObject *tmp;
|
||||||
if (!(tmp = PyInt_FromLong(fmod))) { goto err_for; }
|
if (!(tmp = PyInt_FromLong(fmod))) { goto err_for; }
|
||||||
PyTuple_SET_ITEM(dtitem, 3, tmp);
|
column->internal_size = tmp;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
PyObject *tmp;
|
PyObject *tmp;
|
||||||
if (!(tmp = PyInt_FromLong(fsize))) { goto err_for; }
|
if (!(tmp = PyInt_FromLong(fsize))) { goto err_for; }
|
||||||
PyTuple_SET_ITEM(dtitem, 3, tmp);
|
column->internal_size = tmp;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 4,5/ scale and precision */
|
/* 4,5/ scale and precision */
|
||||||
|
@ -1286,40 +1337,35 @@ _pq_fetch_tuples(cursorObject *curs)
|
||||||
if (!(tmp = PyInt_FromLong((fmod >> 16) & 0xFFFF))) {
|
if (!(tmp = PyInt_FromLong((fmod >> 16) & 0xFFFF))) {
|
||||||
goto err_for;
|
goto err_for;
|
||||||
}
|
}
|
||||||
PyTuple_SET_ITEM(dtitem, 4, tmp);
|
column->precision = tmp;
|
||||||
|
|
||||||
if (!(tmp = PyInt_FromLong(fmod & 0xFFFF))) {
|
if (!(tmp = PyInt_FromLong(fmod & 0xFFFF))) {
|
||||||
PyTuple_SET_ITEM(dtitem, 5, tmp);
|
goto err_for;
|
||||||
}
|
}
|
||||||
PyTuple_SET_ITEM(dtitem, 5, tmp);
|
column->scale = tmp;
|
||||||
}
|
|
||||||
else {
|
|
||||||
Py_INCREF(Py_None);
|
|
||||||
PyTuple_SET_ITEM(dtitem, 4, Py_None);
|
|
||||||
Py_INCREF(Py_None);
|
|
||||||
PyTuple_SET_ITEM(dtitem, 5, Py_None);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 6/ FIXME: null_ok??? */
|
/* table_oid, table_column */
|
||||||
Py_INCREF(Py_None);
|
if (ftable != InvalidOid) {
|
||||||
PyTuple_SET_ITEM(dtitem, 6, Py_None);
|
PyObject *tmp;
|
||||||
|
if (!(tmp = PyInt_FromLong((long)ftable))) { goto err_for; }
|
||||||
/* Convert into a namedtuple if available */
|
column->table_oid = tmp;
|
||||||
if (Py_None != psyco_DescriptionType) {
|
|
||||||
PyObject *tmp = dtitem;
|
|
||||||
dtitem = PyObject_CallObject(psyco_DescriptionType, tmp);
|
|
||||||
Py_DECREF(tmp);
|
|
||||||
if (NULL == dtitem) { goto err_for; }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PyTuple_SET_ITEM(description, i, dtitem);
|
if (ftablecol > 0) {
|
||||||
dtitem = NULL;
|
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;
|
continue;
|
||||||
|
|
||||||
err_for:
|
err_for:
|
||||||
Py_XDECREF(type);
|
Py_XDECREF(type);
|
||||||
Py_XDECREF(dtitem);
|
Py_XDECREF(column);
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,6 +32,7 @@
|
||||||
#include "psycopg/replication_cursor.h"
|
#include "psycopg/replication_cursor.h"
|
||||||
#include "psycopg/replication_message.h"
|
#include "psycopg/replication_message.h"
|
||||||
#include "psycopg/green.h"
|
#include "psycopg/green.h"
|
||||||
|
#include "psycopg/column.h"
|
||||||
#include "psycopg/lobject.h"
|
#include "psycopg/lobject.h"
|
||||||
#include "psycopg/notify.h"
|
#include "psycopg/notify.h"
|
||||||
#include "psycopg/xid.h"
|
#include "psycopg/xid.h"
|
||||||
|
@ -39,6 +40,7 @@
|
||||||
#include "psycopg/microprotocols.h"
|
#include "psycopg/microprotocols.h"
|
||||||
#include "psycopg/microprotocols_proto.h"
|
#include "psycopg/microprotocols_proto.h"
|
||||||
#include "psycopg/error.h"
|
#include "psycopg/error.h"
|
||||||
|
#include "psycopg/conninfo.h"
|
||||||
#include "psycopg/diagnostics.h"
|
#include "psycopg/diagnostics.h"
|
||||||
|
|
||||||
#include "psycopg/adapter_qstring.h"
|
#include "psycopg/adapter_qstring.h"
|
||||||
|
@ -69,9 +71,6 @@ HIDDEN int psycopg_debug_enabled = 0;
|
||||||
/* Python representation of SQL NULL */
|
/* Python representation of SQL NULL */
|
||||||
HIDDEN PyObject *psyco_null = 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 */
|
/* macro trick to stringify a macro expansion */
|
||||||
#define xstr(s) str(s)
|
#define xstr(s) str(s)
|
||||||
#define str(s) #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 **/
|
/** method table and module initialization **/
|
||||||
|
|
||||||
static PyMethodDef psycopgMethods[] = {
|
static PyMethodDef psycopgMethods[] = {
|
||||||
|
@ -1041,6 +983,9 @@ INIT_MODULE(_psycopg)(void)
|
||||||
Py_TYPE(&chunkType) = &PyType_Type;
|
Py_TYPE(&chunkType) = &PyType_Type;
|
||||||
if (PyType_Ready(&chunkType) == -1) goto exit;
|
if (PyType_Ready(&chunkType) == -1) goto exit;
|
||||||
|
|
||||||
|
Py_TYPE(&columnType) = &PyType_Type;
|
||||||
|
if (PyType_Ready(&columnType) == -1) goto exit;
|
||||||
|
|
||||||
Py_TYPE(¬ifyType) = &PyType_Type;
|
Py_TYPE(¬ifyType) = &PyType_Type;
|
||||||
if (PyType_Ready(¬ifyType) == -1) goto exit;
|
if (PyType_Ready(¬ifyType) == -1) goto exit;
|
||||||
|
|
||||||
|
@ -1051,6 +996,9 @@ INIT_MODULE(_psycopg)(void)
|
||||||
errorType.tp_base = (PyTypeObject *)PyExc_StandardError;
|
errorType.tp_base = (PyTypeObject *)PyExc_StandardError;
|
||||||
if (PyType_Ready(&errorType) == -1) goto exit;
|
if (PyType_Ready(&errorType) == -1) goto exit;
|
||||||
|
|
||||||
|
Py_TYPE(&connInfoType) = &PyType_Type;
|
||||||
|
if (PyType_Ready(&connInfoType) == -1) goto exit;
|
||||||
|
|
||||||
Py_TYPE(&diagnosticsType) = &PyType_Type;
|
Py_TYPE(&diagnosticsType) = &PyType_Type;
|
||||||
if (PyType_Ready(&diagnosticsType) == -1) goto exit;
|
if (PyType_Ready(&diagnosticsType) == -1) goto exit;
|
||||||
|
|
||||||
|
@ -1119,7 +1067,6 @@ INIT_MODULE(_psycopg)(void)
|
||||||
if (!(psycoEncodings = PyDict_New())) { goto exit; }
|
if (!(psycoEncodings = PyDict_New())) { goto exit; }
|
||||||
if (0 != psyco_encodings_fill(psycoEncodings)) { goto exit; }
|
if (0 != psyco_encodings_fill(psycoEncodings)) { goto exit; }
|
||||||
psyco_null = Bytes_FromString("NULL");
|
psyco_null = Bytes_FromString("NULL");
|
||||||
if (!(psyco_DescriptionType = psyco_make_description_type())) { goto exit; }
|
|
||||||
|
|
||||||
/* set some module's parameters */
|
/* set some module's parameters */
|
||||||
PyModule_AddStringConstant(module, "__version__", xstr(PSYCOPG_VERSION));
|
PyModule_AddStringConstant(module, "__version__", xstr(PSYCOPG_VERSION));
|
||||||
|
@ -1138,8 +1085,10 @@ INIT_MODULE(_psycopg)(void)
|
||||||
PyModule_AddObject(module, "ReplicationCursor", (PyObject*)&replicationCursorType);
|
PyModule_AddObject(module, "ReplicationCursor", (PyObject*)&replicationCursorType);
|
||||||
PyModule_AddObject(module, "ReplicationMessage", (PyObject*)&replicationMessageType);
|
PyModule_AddObject(module, "ReplicationMessage", (PyObject*)&replicationMessageType);
|
||||||
PyModule_AddObject(module, "ISQLQuote", (PyObject*)&isqlquoteType);
|
PyModule_AddObject(module, "ISQLQuote", (PyObject*)&isqlquoteType);
|
||||||
|
PyModule_AddObject(module, "Column", (PyObject*)&columnType);
|
||||||
PyModule_AddObject(module, "Notify", (PyObject*)¬ifyType);
|
PyModule_AddObject(module, "Notify", (PyObject*)¬ifyType);
|
||||||
PyModule_AddObject(module, "Xid", (PyObject*)&xidType);
|
PyModule_AddObject(module, "Xid", (PyObject*)&xidType);
|
||||||
|
PyModule_AddObject(module, "ConnectionInfo", (PyObject*)&connInfoType);
|
||||||
PyModule_AddObject(module, "Diagnostics", (PyObject*)&diagnosticsType);
|
PyModule_AddObject(module, "Diagnostics", (PyObject*)&diagnosticsType);
|
||||||
PyModule_AddObject(module, "AsIs", (PyObject*)&asisType);
|
PyModule_AddObject(module, "AsIs", (PyObject*)&asisType);
|
||||||
PyModule_AddObject(module, "Binary", (PyObject*)&binaryType);
|
PyModule_AddObject(module, "Binary", (PyObject*)&binaryType);
|
||||||
|
@ -1150,7 +1099,6 @@ INIT_MODULE(_psycopg)(void)
|
||||||
PyModule_AddObject(module, "List", (PyObject*)&listType);
|
PyModule_AddObject(module, "List", (PyObject*)&listType);
|
||||||
PyModule_AddObject(module, "QuotedString", (PyObject*)&qstringType);
|
PyModule_AddObject(module, "QuotedString", (PyObject*)&qstringType);
|
||||||
PyModule_AddObject(module, "lobject", (PyObject*)&lobjectType);
|
PyModule_AddObject(module, "lobject", (PyObject*)&lobjectType);
|
||||||
PyModule_AddObject(module, "Column", psyco_DescriptionType);
|
|
||||||
|
|
||||||
/* encodings dictionary in module dictionary */
|
/* encodings dictionary in module dictionary */
|
||||||
PyModule_AddObject(module, "encodings", psycoEncodings);
|
PyModule_AddObject(module, "encodings", psycoEncodings);
|
||||||
|
|
|
@ -78,6 +78,7 @@ typedef unsigned long Py_uhash_t;
|
||||||
#define PyInt_Check PyLong_Check
|
#define PyInt_Check PyLong_Check
|
||||||
#define PyInt_AsLong PyLong_AsLong
|
#define PyInt_AsLong PyLong_AsLong
|
||||||
#define PyInt_FromLong PyLong_FromLong
|
#define PyInt_FromLong PyLong_FromLong
|
||||||
|
#define PyInt_FromString PyLong_FromString
|
||||||
#define PyInt_FromSsize_t PyLong_FromSsize_t
|
#define PyInt_FromSsize_t PyLong_FromSsize_t
|
||||||
#define PyExc_StandardError PyExc_Exception
|
#define PyExc_StandardError PyExc_Exception
|
||||||
#define PyString_FromFormat PyUnicode_FromFormat
|
#define PyString_FromFormat PyUnicode_FromFormat
|
||||||
|
|
|
@ -212,7 +212,7 @@ static const char hex_lut[128] = {
|
||||||
/* Parse a bytea output buffer encoded in 'hex' format.
|
/* Parse a bytea output buffer encoded in 'hex' format.
|
||||||
*
|
*
|
||||||
* the format is described in
|
* the format is described in
|
||||||
* http://www.postgresql.org/docs/current/static/datatype-binary.html
|
* https://www.postgresql.org/docs/current/static/datatype-binary.html
|
||||||
*
|
*
|
||||||
* Parse the buffer in 'bufin', whose length is 'sizein'.
|
* Parse the buffer in 'bufin', whose length is 'sizein'.
|
||||||
* Return a new buffer allocated by PyMem_Malloc and set 'sizeout' to its size.
|
* Return a new buffer allocated by PyMem_Malloc and set 'sizeout' to its size.
|
||||||
|
@ -262,7 +262,7 @@ exit:
|
||||||
/* Parse a bytea output buffer encoded in 'escape' format.
|
/* Parse a bytea output buffer encoded in 'escape' format.
|
||||||
*
|
*
|
||||||
* the format is described in
|
* the format is described in
|
||||||
* http://www.postgresql.org/docs/current/static/datatype-binary.html
|
* https://www.postgresql.org/docs/current/static/datatype-binary.html
|
||||||
*
|
*
|
||||||
* Parse the buffer in 'bufin', whose length is 'sizein'.
|
* Parse the buffer in 'bufin', whose length is 'sizein'.
|
||||||
* Return a new buffer allocated by PyMem_Malloc and set 'sizeout' to its size.
|
* Return a new buffer allocated by PyMem_Malloc and set 'sizeout' to its size.
|
||||||
|
|
|
@ -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())
|
|
|
@ -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)
|
|
|
@ -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)
|
|
|
@ -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))
|
|
|
@ -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),)
|
|
|
@ -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()
|
|
|
@ -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'])
|
|
|
@ -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()
|
|
|
@ -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)
|
|
|
@ -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
|
|
|
@ -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'
|
|
|
@ -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)
|
|
|
@ -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()
|
|
|
@ -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)
|
|
|
@ -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)
|
|
60639
sandbox/test_copy2.csv
60639
sandbox/test_copy2.csv
File diff suppressed because it is too large
Load Diff
|
@ -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()
|
|
|
@ -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())
|
|
|
@ -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")
|
|
|
@ -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()
|
|
|
@ -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))
|
|
|
@ -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()
|
|
|
@ -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)
|
|
|
@ -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()
|
|
|
@ -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)
|
|
|
@ -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])
|
|
|
@ -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
|
|
||||||
}
|
|
|
@ -22,8 +22,6 @@ import sys
|
||||||
import urllib2
|
import urllib2
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
|
|
||||||
from BeautifulSoup import BeautifulSoup as BS
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
if len(sys.argv) != 2:
|
if len(sys.argv) != 2:
|
||||||
|
@ -35,8 +33,7 @@ def main():
|
||||||
file_start = read_base_file(filename)
|
file_start = read_base_file(filename)
|
||||||
# If you add a version to the list fix the docs (in errorcodes.rst)
|
# If you add a version to the list fix the docs (in errorcodes.rst)
|
||||||
classes, errors = fetch_errors(
|
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.1', '9.2', '9.3', '9.4', '9.5', '9.6', '10', '11'])
|
||||||
'9.6', '10'])
|
|
||||||
|
|
||||||
f = open(filename, "w")
|
f = open(filename, "w")
|
||||||
for line in file_start:
|
for line in file_start:
|
||||||
|
@ -90,48 +87,6 @@ def parse_errors_txt(url):
|
||||||
return classes, errors
|
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 = \
|
errors_txt_url = \
|
||||||
"http://git.postgresql.org/gitweb/?p=postgresql.git;a=blob_plain;" \
|
"http://git.postgresql.org/gitweb/?p=postgresql.git;a=blob_plain;" \
|
||||||
"f=src/backend/utils/errcodes.txt;hb=%s"
|
"f=src/backend/utils/errcodes.txt;hb=%s"
|
||||||
|
@ -144,18 +99,19 @@ def fetch_errors(versions):
|
||||||
for version in versions:
|
for version in versions:
|
||||||
print(version, file=sys.stderr)
|
print(version, file=sys.stderr)
|
||||||
tver = tuple(map(int, version.split()[0].split('.')))
|
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' % (
|
tag = '%s%s_STABLE' % (
|
||||||
(tver[0] >= 10 and 'REL_' or 'REL'),
|
(tver[0] >= 10 and 'REL_' or 'REL'),
|
||||||
version.replace('.', '_'))
|
version.replace('.', '_'))
|
||||||
c1, e1 = parse_errors_txt(errors_txt_url % tag)
|
c1, e1 = parse_errors_txt(errors_txt_url % tag)
|
||||||
classes.update(c1)
|
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
|
# 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
|
# 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
|
# https://github.com/postgres/postgres/commit/28e0727076
|
||||||
errors['55']['55P04'] = 'UNSAFE_NEW_ENUM_VALUE_USAGE'
|
errors['55']['55P04'] = 'UNSAFE_NEW_ENUM_VALUE_USAGE'
|
||||||
|
|
||||||
|
|
230
scripts/make_errors.py
Executable file
230
scripts/make_errors.py
Executable 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())
|
|
@ -19,6 +19,7 @@ script exits with error 1.
|
||||||
# License for more details.
|
# License for more details.
|
||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
|
|
||||||
|
import argparse
|
||||||
import gc
|
import gc
|
||||||
import sys
|
import sys
|
||||||
import difflib
|
import difflib
|
||||||
|
@ -62,19 +63,16 @@ def main():
|
||||||
|
|
||||||
|
|
||||||
def parse_args():
|
def parse_args():
|
||||||
import optparse
|
parser = argparse.ArgumentParser(description=__doc__)
|
||||||
|
parser.add_argument('--nruns', type=int, metavar="N", default=3,
|
||||||
parser = optparse.OptionParser(description=__doc__)
|
help="number of test suite runs [default: %(default)d]")
|
||||||
parser.add_option('--nruns', type='int', metavar="N", default=3,
|
parser.add_argument('--suite', metavar="NAME",
|
||||||
help="number of test suite runs [default: %default]")
|
|
||||||
parser.add_option('--suite', metavar="NAME",
|
|
||||||
help="the test suite to run (e.g. 'test_cursor'). [default: all]")
|
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 "
|
help="in case of leaks, print a report of object TYPE "
|
||||||
"(support still incomplete)")
|
"(support still incomplete)")
|
||||||
|
|
||||||
opt, args = parser.parse_args()
|
return parser.parse_args()
|
||||||
return opt
|
|
||||||
|
|
||||||
|
|
||||||
def dump(i, opt):
|
def dump(i, opt):
|
||||||
|
|
11
setup.py
11
setup.py
|
@ -32,10 +32,7 @@ import os
|
||||||
import sys
|
import sys
|
||||||
import re
|
import re
|
||||||
import subprocess
|
import subprocess
|
||||||
try:
|
|
||||||
from setuptools import setup, Extension
|
from setuptools import setup, Extension
|
||||||
except ImportError:
|
|
||||||
from distutils.core import setup, Extension
|
|
||||||
from distutils.command.build_ext import build_ext
|
from distutils.command.build_ext import build_ext
|
||||||
from distutils.sysconfig import get_python_inc
|
from distutils.sysconfig import get_python_inc
|
||||||
from distutils.ccompiler import get_default_compiler
|
from distutils.ccompiler import get_default_compiler
|
||||||
|
@ -47,7 +44,7 @@ try:
|
||||||
except ImportError:
|
except ImportError:
|
||||||
import ConfigParser as configparser
|
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.
|
# for a consistent versioning pattern.
|
||||||
|
|
||||||
PSYCOPG_VERSION = '2.8.dev0'
|
PSYCOPG_VERSION = '2.8.dev0'
|
||||||
|
@ -489,11 +486,11 @@ sources = [
|
||||||
'libpq_support.c', 'win32_support.c', 'solaris_support.c',
|
'libpq_support.c', 'win32_support.c', 'solaris_support.c',
|
||||||
|
|
||||||
'connection_int.c', 'connection_type.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_connection_type.c',
|
||||||
'replication_cursor_type.c',
|
'replication_cursor_type.c',
|
||||||
'replication_message_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',
|
'lobject_int.c', 'lobject_type.c',
|
||||||
'notify_type.c', 'xid_type.c',
|
'notify_type.c', 'xid_type.c',
|
||||||
|
|
||||||
|
@ -511,7 +508,7 @@ depends = [
|
||||||
'replication_connection.h',
|
'replication_connection.h',
|
||||||
'replication_cursor.h',
|
'replication_cursor.h',
|
||||||
'replication_message.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',
|
'libpq_support.h', 'win32_support.h',
|
||||||
|
|
||||||
'adapter_asis.h', 'adapter_binary.h', 'adapter_datetime.h',
|
'adapter_asis.h', 'adapter_binary.h', 'adapter_datetime.h',
|
||||||
|
|
|
@ -40,6 +40,7 @@ from . import test_copy
|
||||||
from . import test_cursor
|
from . import test_cursor
|
||||||
from . import test_dates
|
from . import test_dates
|
||||||
from . import test_errcodes
|
from . import test_errcodes
|
||||||
|
from . import test_errors
|
||||||
from . import test_extras_dictcursor
|
from . import test_extras_dictcursor
|
||||||
from . import test_fast_executemany
|
from . import test_fast_executemany
|
||||||
from . import test_green
|
from . import test_green
|
||||||
|
@ -84,6 +85,7 @@ def test_suite():
|
||||||
suite.addTest(test_cursor.test_suite())
|
suite.addTest(test_cursor.test_suite())
|
||||||
suite.addTest(test_dates.test_suite())
|
suite.addTest(test_dates.test_suite())
|
||||||
suite.addTest(test_errcodes.test_suite())
|
suite.addTest(test_errcodes.test_suite())
|
||||||
|
suite.addTest(test_errors.test_suite())
|
||||||
suite.addTest(test_extras_dictcursor.test_suite())
|
suite.addTest(test_extras_dictcursor.test_suite())
|
||||||
suite.addTest(test_fast_executemany.test_suite())
|
suite.addTest(test_fast_executemany.test_suite())
|
||||||
suite.addTest(test_green.test_suite())
|
suite.addTest(test_green.test_suite())
|
||||||
|
|
|
@ -192,7 +192,7 @@ class AsyncTests(ConnectingTestCase):
|
||||||
self.assertTrue(self.conn.isexecuting())
|
self.assertTrue(self.conn.isexecuting())
|
||||||
|
|
||||||
# getting transaction status works
|
# getting transaction status works
|
||||||
self.assertEquals(self.conn.get_transaction_status(),
|
self.assertEquals(self.conn.info.transaction_status,
|
||||||
ext.TRANSACTION_STATUS_ACTIVE)
|
ext.TRANSACTION_STATUS_ACTIVE)
|
||||||
self.assertTrue(self.conn.isexecuting())
|
self.assertTrue(self.conn.isexecuting())
|
||||||
|
|
||||||
|
@ -359,7 +359,7 @@ class AsyncTests(ConnectingTestCase):
|
||||||
|
|
||||||
self.assertEquals(self.sync_conn.notifies, [])
|
self.assertEquals(self.sync_conn.notifies, [])
|
||||||
|
|
||||||
pid = self.conn.get_backend_pid()
|
pid = self.conn.info.backend_pid
|
||||||
for _ in range(5):
|
for _ in range(5):
|
||||||
self.wait(self.sync_conn)
|
self.wait(self.sync_conn)
|
||||||
if not self.sync_conn.notifies:
|
if not self.sync_conn.notifies:
|
||||||
|
@ -418,7 +418,7 @@ class AsyncTests(ConnectingTestCase):
|
||||||
def test_notices(self):
|
def test_notices(self):
|
||||||
del self.conn.notices[:]
|
del self.conn.notices[:]
|
||||||
cur = self.conn.cursor()
|
cur = self.conn.cursor()
|
||||||
if self.conn.server_version >= 90300:
|
if self.conn.info.server_version >= 90300:
|
||||||
cur.execute("set client_min_messages=debug1")
|
cur.execute("set client_min_messages=debug1")
|
||||||
self.wait(cur)
|
self.wait(cur)
|
||||||
cur.execute("create temp table chatty (id serial primary key);")
|
cur.execute("create temp table chatty (id serial primary key);")
|
||||||
|
@ -450,9 +450,16 @@ class AsyncTests(ConnectingTestCase):
|
||||||
else:
|
else:
|
||||||
self.fail("no exception raised")
|
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():
|
def test_suite():
|
||||||
return unittest.TestLoader().loadTestsFromName(__name__)
|
return unittest.TestLoader().loadTestsFromName(__name__)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
|
@ -218,5 +218,6 @@ class AsyncReplicationTest(ReplicationTestCase):
|
||||||
def test_suite():
|
def test_suite():
|
||||||
return unittest.TestLoader().loadTestsFromName(__name__)
|
return unittest.TestLoader().loadTestsFromName(__name__)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
|
@ -42,5 +42,6 @@ class DateTimeAllocationBugTestCase(unittest.TestCase):
|
||||||
def test_suite():
|
def test_suite():
|
||||||
return unittest.TestLoader().loadTestsFromName(__name__)
|
return unittest.TestLoader().loadTestsFromName(__name__)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
|
@ -114,5 +114,6 @@ class CancelTests(ConnectingTestCase):
|
||||||
def test_suite():
|
def test_suite():
|
||||||
return unittest.TestLoader().loadTestsFromName(__name__)
|
return unittest.TestLoader().loadTestsFromName(__name__)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
|
@ -35,11 +35,11 @@ import psycopg2.errorcodes
|
||||||
from psycopg2 import extensions as ext
|
from psycopg2 import extensions as ext
|
||||||
|
|
||||||
from .testutils import (
|
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,
|
skip_after_postgres, skip_before_libpq, skip_after_libpq,
|
||||||
ConnectingTestCase, skip_if_tpc_disabled, skip_if_windows, slow)
|
ConnectingTestCase, skip_if_tpc_disabled, skip_if_windows, slow)
|
||||||
|
|
||||||
from .testconfig import dsn, dbname
|
from .testconfig import dbhost, dsn, dbname
|
||||||
|
|
||||||
|
|
||||||
class ConnectionTests(ConnectingTestCase):
|
class ConnectionTests(ConnectingTestCase):
|
||||||
|
@ -87,13 +87,13 @@ class ConnectionTests(ConnectingTestCase):
|
||||||
conn.autocommit = True
|
conn.autocommit = True
|
||||||
conn.isolation_level = 'serializable'
|
conn.isolation_level = 'serializable'
|
||||||
conn.readonly = True
|
conn.readonly = True
|
||||||
if self.conn.server_version >= 90100:
|
if self.conn.info.server_version >= 90100:
|
||||||
conn.deferrable = False
|
conn.deferrable = False
|
||||||
|
|
||||||
self.assert_(conn.autocommit)
|
self.assert_(conn.autocommit)
|
||||||
self.assertEqual(conn.isolation_level, ext.ISOLATION_LEVEL_SERIALIZABLE)
|
self.assertEqual(conn.isolation_level, ext.ISOLATION_LEVEL_SERIALIZABLE)
|
||||||
self.assert_(conn.readonly is True)
|
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)
|
self.assert_(conn.deferrable is False)
|
||||||
|
|
||||||
conn.reset()
|
conn.reset()
|
||||||
|
@ -101,13 +101,13 @@ class ConnectionTests(ConnectingTestCase):
|
||||||
self.assert_(not conn.autocommit)
|
self.assert_(not conn.autocommit)
|
||||||
self.assertEqual(conn.isolation_level, ext.ISOLATION_LEVEL_DEFAULT)
|
self.assertEqual(conn.isolation_level, ext.ISOLATION_LEVEL_DEFAULT)
|
||||||
self.assert_(conn.readonly is None)
|
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)
|
self.assert_(conn.deferrable is None)
|
||||||
|
|
||||||
def test_notices(self):
|
def test_notices(self):
|
||||||
conn = self.conn
|
conn = self.conn
|
||||||
cur = conn.cursor()
|
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("set client_min_messages=debug1")
|
||||||
cur.execute("create temp table chatty (id serial primary key);")
|
cur.execute("create temp table chatty (id serial primary key);")
|
||||||
self.assertEqual("CREATE TABLE", cur.statusmessage)
|
self.assertEqual("CREATE TABLE", cur.statusmessage)
|
||||||
|
@ -116,7 +116,7 @@ class ConnectionTests(ConnectingTestCase):
|
||||||
def test_notices_consistent_order(self):
|
def test_notices_consistent_order(self):
|
||||||
conn = self.conn
|
conn = self.conn
|
||||||
cur = conn.cursor()
|
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("set client_min_messages=debug1")
|
||||||
cur.execute("""
|
cur.execute("""
|
||||||
create temp table table1 (id serial);
|
create temp table table1 (id serial);
|
||||||
|
@ -136,7 +136,7 @@ class ConnectionTests(ConnectingTestCase):
|
||||||
def test_notices_limited(self):
|
def test_notices_limited(self):
|
||||||
conn = self.conn
|
conn = self.conn
|
||||||
cur = conn.cursor()
|
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("set client_min_messages=debug1")
|
||||||
for i in range(0, 100, 10):
|
for i in range(0, 100, 10):
|
||||||
sql = " ".join(["create temp table table%d (id serial);" % j
|
sql = " ".join(["create temp table table%d (id serial);" % j
|
||||||
|
@ -153,7 +153,7 @@ class ConnectionTests(ConnectingTestCase):
|
||||||
conn = self.conn
|
conn = self.conn
|
||||||
self.conn.notices = deque()
|
self.conn.notices = deque()
|
||||||
cur = conn.cursor()
|
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("set client_min_messages=debug1")
|
||||||
|
|
||||||
cur.execute("""
|
cur.execute("""
|
||||||
|
@ -183,7 +183,7 @@ class ConnectionTests(ConnectingTestCase):
|
||||||
conn = self.conn
|
conn = self.conn
|
||||||
self.conn.notices = None # will make an error swallowes ok
|
self.conn.notices = None # will make an error swallowes ok
|
||||||
cur = conn.cursor()
|
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("set client_min_messages=debug1")
|
||||||
|
|
||||||
cur.execute("create temp table table1 (id serial);")
|
cur.execute("create temp table table1 (id serial);")
|
||||||
|
@ -199,7 +199,7 @@ class ConnectionTests(ConnectingTestCase):
|
||||||
|
|
||||||
def test_tpc_unsupported(self):
|
def test_tpc_unsupported(self):
|
||||||
cnn = self.conn
|
cnn = self.conn
|
||||||
if cnn.server_version >= 80100:
|
if cnn.info.server_version >= 80100:
|
||||||
return self.skipTest("tpc is supported")
|
return self.skipTest("tpc is supported")
|
||||||
|
|
||||||
self.assertRaises(psycopg2.NotSupportedError,
|
self.assertRaises(psycopg2.NotSupportedError,
|
||||||
|
@ -538,7 +538,7 @@ class IsolationLevelsTestCase(ConnectingTestCase):
|
||||||
conn.set_isolation_level(level)
|
conn.set_isolation_level(level)
|
||||||
|
|
||||||
# the only values available on prehistoric PG versions
|
# the only values available on prehistoric PG versions
|
||||||
if conn.server_version < 80000:
|
if conn.info.server_version < 80000:
|
||||||
if level in (
|
if level in (
|
||||||
ext.ISOLATION_LEVEL_READ_UNCOMMITTED,
|
ext.ISOLATION_LEVEL_READ_UNCOMMITTED,
|
||||||
ext.ISOLATION_LEVEL_REPEATABLE_READ):
|
ext.ISOLATION_LEVEL_REPEATABLE_READ):
|
||||||
|
@ -594,35 +594,35 @@ class IsolationLevelsTestCase(ConnectingTestCase):
|
||||||
cur = conn.cursor()
|
cur = conn.cursor()
|
||||||
|
|
||||||
self.assertEqual(ext.TRANSACTION_STATUS_IDLE,
|
self.assertEqual(ext.TRANSACTION_STATUS_IDLE,
|
||||||
conn.get_transaction_status())
|
conn.info.transaction_status)
|
||||||
cur.execute("insert into isolevel values (10);")
|
cur.execute("insert into isolevel values (10);")
|
||||||
self.assertEqual(ext.TRANSACTION_STATUS_INTRANS,
|
self.assertEqual(ext.TRANSACTION_STATUS_INTRANS,
|
||||||
conn.get_transaction_status())
|
conn.info.transaction_status)
|
||||||
|
|
||||||
conn.set_isolation_level(
|
conn.set_isolation_level(
|
||||||
psycopg2.extensions.ISOLATION_LEVEL_SERIALIZABLE)
|
psycopg2.extensions.ISOLATION_LEVEL_SERIALIZABLE)
|
||||||
self.assertEqual(psycopg2.extensions.TRANSACTION_STATUS_IDLE,
|
self.assertEqual(psycopg2.extensions.TRANSACTION_STATUS_IDLE,
|
||||||
conn.get_transaction_status())
|
conn.info.transaction_status)
|
||||||
cur.execute("select count(*) from isolevel;")
|
cur.execute("select count(*) from isolevel;")
|
||||||
self.assertEqual(0, cur.fetchone()[0])
|
self.assertEqual(0, cur.fetchone()[0])
|
||||||
|
|
||||||
cur.execute("insert into isolevel values (10);")
|
cur.execute("insert into isolevel values (10);")
|
||||||
self.assertEqual(psycopg2.extensions.TRANSACTION_STATUS_INTRANS,
|
self.assertEqual(psycopg2.extensions.TRANSACTION_STATUS_INTRANS,
|
||||||
conn.get_transaction_status())
|
conn.info.transaction_status)
|
||||||
conn.set_isolation_level(
|
conn.set_isolation_level(
|
||||||
psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT)
|
psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT)
|
||||||
self.assertEqual(psycopg2.extensions.TRANSACTION_STATUS_IDLE,
|
self.assertEqual(psycopg2.extensions.TRANSACTION_STATUS_IDLE,
|
||||||
conn.get_transaction_status())
|
conn.info.transaction_status)
|
||||||
cur.execute("select count(*) from isolevel;")
|
cur.execute("select count(*) from isolevel;")
|
||||||
self.assertEqual(0, cur.fetchone()[0])
|
self.assertEqual(0, cur.fetchone()[0])
|
||||||
|
|
||||||
cur.execute("insert into isolevel values (10);")
|
cur.execute("insert into isolevel values (10);")
|
||||||
self.assertEqual(psycopg2.extensions.TRANSACTION_STATUS_IDLE,
|
self.assertEqual(psycopg2.extensions.TRANSACTION_STATUS_IDLE,
|
||||||
conn.get_transaction_status())
|
conn.info.transaction_status)
|
||||||
conn.set_isolation_level(
|
conn.set_isolation_level(
|
||||||
psycopg2.extensions.ISOLATION_LEVEL_READ_COMMITTED)
|
psycopg2.extensions.ISOLATION_LEVEL_READ_COMMITTED)
|
||||||
self.assertEqual(psycopg2.extensions.TRANSACTION_STATUS_IDLE,
|
self.assertEqual(psycopg2.extensions.TRANSACTION_STATUS_IDLE,
|
||||||
conn.get_transaction_status())
|
conn.info.transaction_status)
|
||||||
cur.execute("select count(*) from isolevel;")
|
cur.execute("select count(*) from isolevel;")
|
||||||
self.assertEqual(1, cur.fetchone()[0])
|
self.assertEqual(1, cur.fetchone()[0])
|
||||||
self.assertEqual(conn.isolation_level,
|
self.assertEqual(conn.isolation_level,
|
||||||
|
@ -718,7 +718,7 @@ class IsolationLevelsTestCase(ConnectingTestCase):
|
||||||
|
|
||||||
self.conn.isolation_level = ext.ISOLATION_LEVEL_REPEATABLE_READ
|
self.conn.isolation_level = ext.ISOLATION_LEVEL_REPEATABLE_READ
|
||||||
cur.execute("SHOW transaction_isolation;")
|
cur.execute("SHOW transaction_isolation;")
|
||||||
if self.conn.server_version > 80000:
|
if self.conn.info.server_version > 80000:
|
||||||
self.assertEqual(self.conn.isolation_level,
|
self.assertEqual(self.conn.isolation_level,
|
||||||
ext.ISOLATION_LEVEL_REPEATABLE_READ)
|
ext.ISOLATION_LEVEL_REPEATABLE_READ)
|
||||||
self.assertEqual(cur.fetchone()[0], '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
|
self.conn.isolation_level = ext.ISOLATION_LEVEL_READ_UNCOMMITTED
|
||||||
cur.execute("SHOW transaction_isolation;")
|
cur.execute("SHOW transaction_isolation;")
|
||||||
if self.conn.server_version > 80000:
|
if self.conn.info.server_version > 80000:
|
||||||
self.assertEqual(self.conn.isolation_level,
|
self.assertEqual(self.conn.isolation_level,
|
||||||
ext.ISOLATION_LEVEL_READ_UNCOMMITTED)
|
ext.ISOLATION_LEVEL_READ_UNCOMMITTED)
|
||||||
self.assertEqual(cur.fetchone()[0], 'read uncommitted')
|
self.assertEqual(cur.fetchone()[0], 'read uncommitted')
|
||||||
|
@ -766,7 +766,7 @@ class IsolationLevelsTestCase(ConnectingTestCase):
|
||||||
|
|
||||||
self.conn.isolation_level = "repeatable read"
|
self.conn.isolation_level = "repeatable read"
|
||||||
cur.execute("SHOW transaction_isolation;")
|
cur.execute("SHOW transaction_isolation;")
|
||||||
if self.conn.server_version > 80000:
|
if self.conn.info.server_version > 80000:
|
||||||
self.assertEqual(self.conn.isolation_level,
|
self.assertEqual(self.conn.isolation_level,
|
||||||
ext.ISOLATION_LEVEL_REPEATABLE_READ)
|
ext.ISOLATION_LEVEL_REPEATABLE_READ)
|
||||||
self.assertEqual(cur.fetchone()[0], 'repeatable read')
|
self.assertEqual(cur.fetchone()[0], 'repeatable read')
|
||||||
|
@ -785,7 +785,7 @@ class IsolationLevelsTestCase(ConnectingTestCase):
|
||||||
|
|
||||||
self.conn.isolation_level = "read uncommitted"
|
self.conn.isolation_level = "read uncommitted"
|
||||||
cur.execute("SHOW transaction_isolation;")
|
cur.execute("SHOW transaction_isolation;")
|
||||||
if self.conn.server_version > 80000:
|
if self.conn.info.server_version > 80000:
|
||||||
self.assertEqual(self.conn.isolation_level,
|
self.assertEqual(self.conn.isolation_level,
|
||||||
ext.ISOLATION_LEVEL_READ_UNCOMMITTED)
|
ext.ISOLATION_LEVEL_READ_UNCOMMITTED)
|
||||||
self.assertEqual(cur.fetchone()[0], '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', 5)
|
||||||
self.assertRaises(ValueError, setattr, self.conn, 'isolation_level', 'bah')
|
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):
|
class ConnectionTwoPhaseTests(ConnectingTestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
ConnectingTestCase.setUp(self)
|
ConnectingTestCase.setUp(self)
|
||||||
|
@ -1180,9 +1189,6 @@ class ConnectionTwoPhaseTests(ConnectingTestCase):
|
||||||
self.assertEqual(None, xid.bqual)
|
self.assertEqual(None, xid.bqual)
|
||||||
|
|
||||||
|
|
||||||
decorate_all_tests(ConnectionTwoPhaseTests, skip_if_tpc_disabled)
|
|
||||||
|
|
||||||
|
|
||||||
class TransactionControlTests(ConnectingTestCase):
|
class TransactionControlTests(ConnectingTestCase):
|
||||||
def test_closed(self):
|
def test_closed(self):
|
||||||
self.conn.close()
|
self.conn.close()
|
||||||
|
@ -1208,7 +1214,7 @@ class TransactionControlTests(ConnectingTestCase):
|
||||||
self.conn.set_session(
|
self.conn.set_session(
|
||||||
ext.ISOLATION_LEVEL_REPEATABLE_READ)
|
ext.ISOLATION_LEVEL_REPEATABLE_READ)
|
||||||
cur.execute("SHOW transaction_isolation;")
|
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')
|
self.assertEqual(cur.fetchone()[0], 'repeatable read')
|
||||||
else:
|
else:
|
||||||
self.assertEqual(cur.fetchone()[0], 'serializable')
|
self.assertEqual(cur.fetchone()[0], 'serializable')
|
||||||
|
@ -1223,7 +1229,7 @@ class TransactionControlTests(ConnectingTestCase):
|
||||||
self.conn.set_session(
|
self.conn.set_session(
|
||||||
isolation_level=ext.ISOLATION_LEVEL_READ_UNCOMMITTED)
|
isolation_level=ext.ISOLATION_LEVEL_READ_UNCOMMITTED)
|
||||||
cur.execute("SHOW transaction_isolation;")
|
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')
|
self.assertEqual(cur.fetchone()[0], 'read uncommitted')
|
||||||
else:
|
else:
|
||||||
self.assertEqual(cur.fetchone()[0], 'read committed')
|
self.assertEqual(cur.fetchone()[0], 'read committed')
|
||||||
|
@ -1238,7 +1244,7 @@ class TransactionControlTests(ConnectingTestCase):
|
||||||
|
|
||||||
self.conn.set_session("repeatable read")
|
self.conn.set_session("repeatable read")
|
||||||
cur.execute("SHOW transaction_isolation;")
|
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')
|
self.assertEqual(cur.fetchone()[0], 'repeatable read')
|
||||||
else:
|
else:
|
||||||
self.assertEqual(cur.fetchone()[0], 'serializable')
|
self.assertEqual(cur.fetchone()[0], 'serializable')
|
||||||
|
@ -1251,7 +1257,7 @@ class TransactionControlTests(ConnectingTestCase):
|
||||||
|
|
||||||
self.conn.set_session("read uncommitted")
|
self.conn.set_session("read uncommitted")
|
||||||
cur.execute("SHOW transaction_isolation;")
|
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')
|
self.assertEqual(cur.fetchone()[0], 'read uncommitted')
|
||||||
else:
|
else:
|
||||||
self.assertEqual(cur.fetchone()[0], 'read committed')
|
self.assertEqual(cur.fetchone()[0], 'read committed')
|
||||||
|
@ -1507,42 +1513,42 @@ class AutocommitTests(ConnectingTestCase):
|
||||||
def test_default_no_autocommit(self):
|
def test_default_no_autocommit(self):
|
||||||
self.assert_(not self.conn.autocommit)
|
self.assert_(not self.conn.autocommit)
|
||||||
self.assertEqual(self.conn.status, ext.STATUS_READY)
|
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)
|
ext.TRANSACTION_STATUS_IDLE)
|
||||||
|
|
||||||
cur = self.conn.cursor()
|
cur = self.conn.cursor()
|
||||||
cur.execute('select 1;')
|
cur.execute('select 1;')
|
||||||
self.assertEqual(self.conn.status, ext.STATUS_BEGIN)
|
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)
|
ext.TRANSACTION_STATUS_INTRANS)
|
||||||
|
|
||||||
self.conn.rollback()
|
self.conn.rollback()
|
||||||
self.assertEqual(self.conn.status, ext.STATUS_READY)
|
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)
|
ext.TRANSACTION_STATUS_IDLE)
|
||||||
|
|
||||||
def test_set_autocommit(self):
|
def test_set_autocommit(self):
|
||||||
self.conn.autocommit = True
|
self.conn.autocommit = True
|
||||||
self.assert_(self.conn.autocommit)
|
self.assert_(self.conn.autocommit)
|
||||||
self.assertEqual(self.conn.status, ext.STATUS_READY)
|
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)
|
ext.TRANSACTION_STATUS_IDLE)
|
||||||
|
|
||||||
cur = self.conn.cursor()
|
cur = self.conn.cursor()
|
||||||
cur.execute('select 1;')
|
cur.execute('select 1;')
|
||||||
self.assertEqual(self.conn.status, ext.STATUS_READY)
|
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)
|
ext.TRANSACTION_STATUS_IDLE)
|
||||||
|
|
||||||
self.conn.autocommit = False
|
self.conn.autocommit = False
|
||||||
self.assert_(not self.conn.autocommit)
|
self.assert_(not self.conn.autocommit)
|
||||||
self.assertEqual(self.conn.status, ext.STATUS_READY)
|
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)
|
ext.TRANSACTION_STATUS_IDLE)
|
||||||
|
|
||||||
cur.execute('select 1;')
|
cur.execute('select 1;')
|
||||||
self.assertEqual(self.conn.status, ext.STATUS_BEGIN)
|
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)
|
ext.TRANSACTION_STATUS_INTRANS)
|
||||||
|
|
||||||
def test_set_intrans_error(self):
|
def test_set_intrans_error(self):
|
||||||
|
@ -1555,24 +1561,24 @@ class AutocommitTests(ConnectingTestCase):
|
||||||
self.conn.set_session(autocommit=True)
|
self.conn.set_session(autocommit=True)
|
||||||
self.assert_(self.conn.autocommit)
|
self.assert_(self.conn.autocommit)
|
||||||
self.assertEqual(self.conn.status, ext.STATUS_READY)
|
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)
|
ext.TRANSACTION_STATUS_IDLE)
|
||||||
|
|
||||||
cur = self.conn.cursor()
|
cur = self.conn.cursor()
|
||||||
cur.execute('select 1;')
|
cur.execute('select 1;')
|
||||||
self.assertEqual(self.conn.status, ext.STATUS_READY)
|
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)
|
ext.TRANSACTION_STATUS_IDLE)
|
||||||
|
|
||||||
self.conn.set_session(autocommit=False)
|
self.conn.set_session(autocommit=False)
|
||||||
self.assert_(not self.conn.autocommit)
|
self.assert_(not self.conn.autocommit)
|
||||||
self.assertEqual(self.conn.status, ext.STATUS_READY)
|
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)
|
ext.TRANSACTION_STATUS_IDLE)
|
||||||
|
|
||||||
cur.execute('select 1;')
|
cur.execute('select 1;')
|
||||||
self.assertEqual(self.conn.status, ext.STATUS_BEGIN)
|
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)
|
ext.TRANSACTION_STATUS_INTRANS)
|
||||||
self.conn.rollback()
|
self.conn.rollback()
|
||||||
|
|
||||||
|
@ -1580,7 +1586,7 @@ class AutocommitTests(ConnectingTestCase):
|
||||||
self.assert_(self.conn.autocommit)
|
self.assert_(self.conn.autocommit)
|
||||||
cur.execute('select 1;')
|
cur.execute('select 1;')
|
||||||
self.assertEqual(self.conn.status, ext.STATUS_READY)
|
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)
|
ext.TRANSACTION_STATUS_IDLE)
|
||||||
cur.execute("SHOW transaction_isolation;")
|
cur.execute("SHOW transaction_isolation;")
|
||||||
self.assertEqual(cur.fetchone()[0], 'serializable')
|
self.assertEqual(cur.fetchone()[0], 'serializable')
|
||||||
|
@ -1687,8 +1693,161 @@ while True:
|
||||||
self.assert_(not err, err)
|
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():
|
def test_suite():
|
||||||
return unittest.TestLoader().loadTestsFromName(__name__)
|
return unittest.TestLoader().loadTestsFromName(__name__)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
|
@ -25,8 +25,7 @@
|
||||||
import sys
|
import sys
|
||||||
import string
|
import string
|
||||||
import unittest
|
import unittest
|
||||||
from .testutils import (ConnectingTestCase, decorate_all_tests,
|
from .testutils import (ConnectingTestCase, skip_before_postgres, slow, StringIO)
|
||||||
skip_before_postgres, slow, StringIO)
|
|
||||||
from itertools import cycle
|
from itertools import cycle
|
||||||
from subprocess import Popen, PIPE
|
from subprocess import Popen, PIPE
|
||||||
|
|
||||||
|
@ -63,6 +62,7 @@ class MinimalWrite(_base):
|
||||||
return self.f.write(data)
|
return self.f.write(data)
|
||||||
|
|
||||||
|
|
||||||
|
@skip_copy_if_green
|
||||||
class CopyTests(ConnectingTestCase):
|
class CopyTests(ConnectingTestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
@ -140,7 +140,8 @@ class CopyTests(ConnectingTestCase):
|
||||||
about = abin.decode('latin1').replace('\\', '\\\\')
|
about = abin.decode('latin1').replace('\\', '\\\\')
|
||||||
|
|
||||||
else:
|
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('\\', '\\\\')
|
about = abin.replace('\\', '\\\\')
|
||||||
|
|
||||||
curs = self.conn.cursor()
|
curs = self.conn.cursor()
|
||||||
|
@ -161,7 +162,8 @@ class CopyTests(ConnectingTestCase):
|
||||||
abin = ''.join(map(chr, range(32, 127) + range(160, 255)))
|
abin = ''.join(map(chr, range(32, 127) + range(160, 255)))
|
||||||
about = abin.replace('\\', '\\\\')
|
about = abin.replace('\\', '\\\\')
|
||||||
else:
|
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')
|
about = abin.replace('\\', '\\\\').encode('latin1')
|
||||||
|
|
||||||
curs = self.conn.cursor()
|
curs = self.conn.cursor()
|
||||||
|
@ -184,7 +186,8 @@ class CopyTests(ConnectingTestCase):
|
||||||
about = abin.replace('\\', '\\\\')
|
about = abin.replace('\\', '\\\\')
|
||||||
|
|
||||||
else:
|
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('\\', '\\\\')
|
about = abin.replace('\\', '\\\\')
|
||||||
|
|
||||||
import io
|
import io
|
||||||
|
@ -375,11 +378,9 @@ conn.close()
|
||||||
curs.copy_to, BrokenWrite(), "tcopy")
|
curs.copy_to, BrokenWrite(), "tcopy")
|
||||||
|
|
||||||
|
|
||||||
decorate_all_tests(CopyTests, skip_copy_if_green)
|
|
||||||
|
|
||||||
|
|
||||||
def test_suite():
|
def test_suite():
|
||||||
return unittest.TestLoader().loadTestsFromName(__name__)
|
return unittest.TestLoader().loadTestsFromName(__name__)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
|
@ -229,22 +229,22 @@ class CursorTests(ConnectingTestCase):
|
||||||
curs.execute("select data from withhold order by data")
|
curs.execute("select data from withhold order by data")
|
||||||
self.assertEqual(curs.fetchone(), (10,))
|
self.assertEqual(curs.fetchone(), (10,))
|
||||||
self.assertEqual(self.conn.status, psycopg2.extensions.STATUS_BEGIN)
|
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)
|
psycopg2.extensions.TRANSACTION_STATUS_INTRANS)
|
||||||
|
|
||||||
self.conn.commit()
|
self.conn.commit()
|
||||||
self.assertEqual(self.conn.status, psycopg2.extensions.STATUS_READY)
|
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)
|
psycopg2.extensions.TRANSACTION_STATUS_IDLE)
|
||||||
|
|
||||||
self.assertEqual(curs.fetchone(), (20,))
|
self.assertEqual(curs.fetchone(), (20,))
|
||||||
self.assertEqual(self.conn.status, psycopg2.extensions.STATUS_READY)
|
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)
|
psycopg2.extensions.TRANSACTION_STATUS_IDLE)
|
||||||
|
|
||||||
curs.close()
|
curs.close()
|
||||||
self.assertEqual(self.conn.status, psycopg2.extensions.STATUS_READY)
|
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)
|
psycopg2.extensions.TRANSACTION_STATUS_IDLE)
|
||||||
|
|
||||||
def test_withhold_autocommit(self):
|
def test_withhold_autocommit(self):
|
||||||
|
@ -256,17 +256,17 @@ class CursorTests(ConnectingTestCase):
|
||||||
|
|
||||||
self.assertEqual(curs.fetchone(), (10,))
|
self.assertEqual(curs.fetchone(), (10,))
|
||||||
self.assertEqual(self.conn.status, psycopg2.extensions.STATUS_READY)
|
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)
|
psycopg2.extensions.TRANSACTION_STATUS_IDLE)
|
||||||
|
|
||||||
self.conn.commit()
|
self.conn.commit()
|
||||||
self.assertEqual(self.conn.status, psycopg2.extensions.STATUS_READY)
|
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)
|
psycopg2.extensions.TRANSACTION_STATUS_IDLE)
|
||||||
|
|
||||||
curs.close()
|
curs.close()
|
||||||
self.assertEqual(self.conn.status, psycopg2.extensions.STATUS_READY)
|
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)
|
psycopg2.extensions.TRANSACTION_STATUS_IDLE)
|
||||||
|
|
||||||
def test_scrollable(self):
|
def test_scrollable(self):
|
||||||
|
@ -377,7 +377,7 @@ class CursorTests(ConnectingTestCase):
|
||||||
for i, rec in enumerate(curs):
|
for i, rec in enumerate(curs):
|
||||||
self.assertEqual(i + 1, curs.rownumber)
|
self.assertEqual(i + 1, curs.rownumber)
|
||||||
|
|
||||||
def test_namedtuple_description(self):
|
def test_description_attribs(self):
|
||||||
curs = self.conn.cursor()
|
curs = self.conn.cursor()
|
||||||
curs.execute("""select
|
curs.execute("""select
|
||||||
3.14::decimal(10,2) as pi,
|
3.14::decimal(10,2) as pi,
|
||||||
|
@ -412,6 +412,27 @@ class CursorTests(ConnectingTestCase):
|
||||||
self.assertEqual(c.precision, None)
|
self.assertEqual(c.precision, None)
|
||||||
self.assertEqual(c.scale, 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):
|
def test_pickle_description(self):
|
||||||
curs = self.conn.cursor()
|
curs = self.conn.cursor()
|
||||||
curs.execute('SELECT 1 AS foo')
|
curs.execute('SELECT 1 AS foo')
|
||||||
|
@ -504,7 +525,7 @@ class CursorTests(ConnectingTestCase):
|
||||||
def test_bad_subclass(self):
|
def test_bad_subclass(self):
|
||||||
# check that we get an error message instead of a segfault
|
# check that we get an error message instead of a segfault
|
||||||
# for badly written subclasses.
|
# for badly written subclasses.
|
||||||
# see http://stackoverflow.com/questions/22019341/
|
# see https://stackoverflow.com/questions/22019341/
|
||||||
class StupidCursor(psycopg2.extensions.cursor):
|
class StupidCursor(psycopg2.extensions.cursor):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
# I am stupid so not calling superclass init
|
# I am stupid so not calling superclass init
|
||||||
|
@ -568,7 +589,10 @@ class CursorTests(ConnectingTestCase):
|
||||||
# psycopg2 noticing.
|
# psycopg2 noticing.
|
||||||
control_conn = self.conn
|
control_conn = self.conn
|
||||||
connect_func = self.connect
|
connect_func = self.connect
|
||||||
wait_func = lambda conn: None
|
|
||||||
|
def wait_func(conn):
|
||||||
|
pass
|
||||||
|
|
||||||
self._test_external_close(control_conn, connect_func, wait_func)
|
self._test_external_close(control_conn, connect_func, wait_func)
|
||||||
|
|
||||||
@skip_if_no_superuser
|
@skip_if_no_superuser
|
||||||
|
@ -578,7 +602,10 @@ class CursorTests(ConnectingTestCase):
|
||||||
# Issue #443 is in the async code too. Since the fix is duplicated,
|
# Issue #443 is in the async code too. Since the fix is duplicated,
|
||||||
# so is the test.
|
# so is the test.
|
||||||
control_conn = self.conn
|
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
|
wait_func = psycopg2.extras.wait_select
|
||||||
self._test_external_close(control_conn, connect_func, wait_func)
|
self._test_external_close(control_conn, connect_func, wait_func)
|
||||||
|
|
||||||
|
@ -627,5 +654,6 @@ class CursorTests(ConnectingTestCase):
|
||||||
def test_suite():
|
def test_suite():
|
||||||
return unittest.TestLoader().loadTestsFromName(__name__)
|
return unittest.TestLoader().loadTestsFromName(__name__)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
|
@ -713,5 +713,6 @@ class FixedOffsetTimezoneTests(unittest.TestCase):
|
||||||
def test_suite():
|
def test_suite():
|
||||||
return unittest.TestLoader().loadTestsFromName(__name__)
|
return unittest.TestLoader().loadTestsFromName(__name__)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
|
@ -68,5 +68,6 @@ class ErrocodeTests(ConnectingTestCase):
|
||||||
def test_suite():
|
def test_suite():
|
||||||
return unittest.TestLoader().loadTestsFromName(__name__)
|
return unittest.TestLoader().loadTestsFromName(__name__)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
71
tests/test_errors.py
Executable file
71
tests/test_errors.py
Executable 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()
|
|
@ -582,5 +582,6 @@ class NamedTupleCursorTest(ConnectingTestCase):
|
||||||
def test_suite():
|
def test_suite():
|
||||||
return unittest.TestLoader().loadTestsFromName(__name__)
|
return unittest.TestLoader().loadTestsFromName(__name__)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
|
@ -83,6 +83,16 @@ class TestExecuteBatch(FastExecuteTestMixin, testutils.ConnectingTestCase):
|
||||||
cur.execute("select id, val from testfast order by id")
|
cur.execute("select id, val from testfast order by id")
|
||||||
self.assertEqual(cur.fetchall(), [(i, i * 10) for i in range(1000)])
|
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):
|
def test_pages(self):
|
||||||
cur = self.conn.cursor()
|
cur = self.conn.cursor()
|
||||||
psycopg2.extras.execute_batch(cur,
|
psycopg2.extras.execute_batch(cur,
|
||||||
|
@ -124,6 +134,7 @@ class TestExecuteBatch(FastExecuteTestMixin, testutils.ConnectingTestCase):
|
||||||
self.assertEqual(cur.fetchone(), (3, snowman))
|
self.assertEqual(cur.fetchone(), (3, snowman))
|
||||||
|
|
||||||
|
|
||||||
|
@testutils.skip_before_postgres(8, 2)
|
||||||
class TestExecuteValues(FastExecuteTestMixin, testutils.ConnectingTestCase):
|
class TestExecuteValues(FastExecuteTestMixin, testutils.ConnectingTestCase):
|
||||||
def test_empty(self):
|
def test_empty(self):
|
||||||
cur = self.conn.cursor()
|
cur = self.conn.cursor()
|
||||||
|
@ -169,6 +180,16 @@ class TestExecuteValues(FastExecuteTestMixin, testutils.ConnectingTestCase):
|
||||||
cur.execute("select id, val from testfast order by id")
|
cur.execute("select id, val from testfast order by id")
|
||||||
self.assertEqual(cur.fetchall(), [(i, i * 10) for i in range(1000)])
|
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):
|
def test_pages(self):
|
||||||
cur = self.conn.cursor()
|
cur = self.conn.cursor()
|
||||||
psycopg2.extras.execute_values(cur,
|
psycopg2.extras.execute_values(cur,
|
||||||
|
@ -231,12 +252,9 @@ class TestExecuteValues(FastExecuteTestMixin, testutils.ConnectingTestCase):
|
||||||
self.assertEqual(cur.fetchall(), [(1, 'hi')])
|
self.assertEqual(cur.fetchall(), [(1, 'hi')])
|
||||||
|
|
||||||
|
|
||||||
testutils.decorate_all_tests(TestExecuteValues,
|
|
||||||
testutils.skip_before_postgres(8, 2))
|
|
||||||
|
|
||||||
|
|
||||||
def test_suite():
|
def test_suite():
|
||||||
return unittest.TestLoader().loadTestsFromName(__name__)
|
return unittest.TestLoader().loadTestsFromName(__name__)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
|
@ -27,7 +27,7 @@ import psycopg2
|
||||||
import psycopg2.extensions
|
import psycopg2.extensions
|
||||||
import psycopg2.extras
|
import psycopg2.extras
|
||||||
|
|
||||||
from .testutils import ConnectingTestCase, slow
|
from .testutils import ConnectingTestCase, skip_before_postgres, slow
|
||||||
|
|
||||||
|
|
||||||
class ConnectionStub(object):
|
class ConnectionStub(object):
|
||||||
|
@ -111,6 +111,12 @@ class GreenTestCase(ConnectingTestCase):
|
||||||
curs.execute("select 1")
|
curs.execute("select 1")
|
||||||
self.assertEqual(curs.fetchone()[0], 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):
|
class CallbackErrorTestCase(ConnectingTestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
@ -200,5 +206,6 @@ class CallbackErrorTestCase(ConnectingTestCase):
|
||||||
def test_suite():
|
def test_suite():
|
||||||
return unittest.TestLoader().loadTestsFromName(__name__)
|
return unittest.TestLoader().loadTestsFromName(__name__)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
|
@ -26,6 +26,7 @@ import psycopg2
|
||||||
import psycopg2.extras
|
import psycopg2.extras
|
||||||
|
|
||||||
|
|
||||||
|
@testutils.decorate_all_tests
|
||||||
def skip_if_no_ipaddress(f):
|
def skip_if_no_ipaddress(f):
|
||||||
@wraps(f)
|
@wraps(f)
|
||||||
def skip_if_no_ipaddress_(self):
|
def skip_if_no_ipaddress_(self):
|
||||||
|
@ -40,6 +41,7 @@ def skip_if_no_ipaddress(f):
|
||||||
return skip_if_no_ipaddress_
|
return skip_if_no_ipaddress_
|
||||||
|
|
||||||
|
|
||||||
|
@skip_if_no_ipaddress
|
||||||
class NetworkingTestCase(testutils.ConnectingTestCase):
|
class NetworkingTestCase(testutils.ConnectingTestCase):
|
||||||
def test_inet_cast(self):
|
def test_inet_cast(self):
|
||||||
import ipaddress as ip
|
import ipaddress as ip
|
||||||
|
@ -125,8 +127,6 @@ class NetworkingTestCase(testutils.ConnectingTestCase):
|
||||||
cur.execute("select %s", [ip.ip_network('::ffff:102:300/128')])
|
cur.execute("select %s", [ip.ip_network('::ffff:102:300/128')])
|
||||||
self.assertEquals(cur.fetchone()[0], '::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():
|
def test_suite():
|
||||||
return unittest.TestLoader().loadTestsFromName(__name__)
|
return unittest.TestLoader().loadTestsFromName(__name__)
|
||||||
|
|
|
@ -31,18 +31,11 @@ import psycopg2
|
||||||
import psycopg2.extensions
|
import psycopg2.extensions
|
||||||
import unittest
|
import unittest
|
||||||
from .testutils import (decorate_all_tests, skip_if_tpc_disabled,
|
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):
|
skip_if_no_lo = skip_before_postgres(8, 1,
|
||||||
@wraps(f)
|
"large objects only supported from PG 8.1")
|
||||||
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_lo_if_green = skip_if_green("libpq doesn't support LO in async mode")
|
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)
|
ConnectingTestCase.tearDown(self)
|
||||||
|
|
||||||
|
|
||||||
|
@skip_if_no_lo
|
||||||
|
@skip_lo_if_green
|
||||||
class LargeObjectTests(LargeObjectTestCase):
|
class LargeObjectTests(LargeObjectTestCase):
|
||||||
def test_create(self):
|
def test_create(self):
|
||||||
lo = self.conn.lobject()
|
lo = self.conn.lobject()
|
||||||
|
@ -397,13 +392,12 @@ class LargeObjectTests(LargeObjectTestCase):
|
||||||
lo = self.conn.lobject(lobject_factory=lobject_subclass)
|
lo = self.conn.lobject(lobject_factory=lobject_subclass)
|
||||||
self.assert_(isinstance(lo, 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):
|
def skip_if_no_truncate(f):
|
||||||
@wraps(f)
|
@wraps(f)
|
||||||
def skip_if_no_truncate_(self):
|
def skip_if_no_truncate_(self):
|
||||||
if self.conn.server_version < 80300:
|
if self.conn.info.server_version < 80300:
|
||||||
return self.skipTest(
|
return self.skipTest(
|
||||||
"the server doesn't support large object truncate")
|
"the server doesn't support large object truncate")
|
||||||
|
|
||||||
|
@ -417,6 +411,9 @@ def skip_if_no_truncate(f):
|
||||||
return skip_if_no_truncate_
|
return skip_if_no_truncate_
|
||||||
|
|
||||||
|
|
||||||
|
@skip_if_no_lo
|
||||||
|
@skip_lo_if_green
|
||||||
|
@skip_if_no_truncate
|
||||||
class LargeObjectTruncateTests(LargeObjectTestCase):
|
class LargeObjectTruncateTests(LargeObjectTestCase):
|
||||||
def test_truncate(self):
|
def test_truncate(self):
|
||||||
lo = self.conn.lobject()
|
lo = self.conn.lobject()
|
||||||
|
@ -453,15 +450,12 @@ class LargeObjectTruncateTests(LargeObjectTestCase):
|
||||||
|
|
||||||
self.assertRaises(psycopg2.ProgrammingError, lo.truncate)
|
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):
|
def _has_lo64(conn):
|
||||||
"""Return (bool, msg) about the lo64 support"""
|
"""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"
|
return (False, "server version %s doesn't support the lo64 API"
|
||||||
% conn.server_version)
|
% conn.info.server_version)
|
||||||
|
|
||||||
if 'lo64' not in psycopg2.__version__:
|
if 'lo64' not in psycopg2.__version__:
|
||||||
return False, "this psycopg build doesn't support the lo64 API"
|
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"
|
return True, "this server and build support the lo64 API"
|
||||||
|
|
||||||
|
|
||||||
|
@decorate_all_tests
|
||||||
def skip_if_no_lo64(f):
|
def skip_if_no_lo64(f):
|
||||||
@wraps(f)
|
@wraps(f)
|
||||||
def skip_if_no_lo64_(self):
|
def skip_if_no_lo64_(self):
|
||||||
|
@ -481,6 +476,10 @@ def skip_if_no_lo64(f):
|
||||||
return skip_if_no_lo64_
|
return skip_if_no_lo64_
|
||||||
|
|
||||||
|
|
||||||
|
@skip_if_no_lo
|
||||||
|
@skip_lo_if_green
|
||||||
|
@skip_if_no_truncate
|
||||||
|
@skip_if_no_lo64
|
||||||
class LargeObject64Tests(LargeObjectTestCase):
|
class LargeObject64Tests(LargeObjectTestCase):
|
||||||
def test_seek_tell_truncate_greater_than_2gb(self):
|
def test_seek_tell_truncate_greater_than_2gb(self):
|
||||||
lo = self.conn.lobject()
|
lo = self.conn.lobject()
|
||||||
|
@ -491,10 +490,8 @@ class LargeObject64Tests(LargeObjectTestCase):
|
||||||
self.assertEqual(lo.seek(length, 0), length)
|
self.assertEqual(lo.seek(length, 0), length)
|
||||||
self.assertEqual(lo.tell(), 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):
|
def skip_if_lo64(f):
|
||||||
@wraps(f)
|
@wraps(f)
|
||||||
def skip_if_lo64_(self):
|
def skip_if_lo64_(self):
|
||||||
|
@ -507,6 +504,10 @@ def skip_if_lo64(f):
|
||||||
return skip_if_lo64_
|
return skip_if_lo64_
|
||||||
|
|
||||||
|
|
||||||
|
@skip_if_no_lo
|
||||||
|
@skip_lo_if_green
|
||||||
|
@skip_if_no_truncate
|
||||||
|
@skip_if_lo64
|
||||||
class LargeObjectNot64Tests(LargeObjectTestCase):
|
class LargeObjectNot64Tests(LargeObjectTestCase):
|
||||||
def test_seek_larger_than_2gb(self):
|
def test_seek_larger_than_2gb(self):
|
||||||
lo = self.conn.lobject()
|
lo = self.conn.lobject()
|
||||||
|
@ -522,12 +523,10 @@ class LargeObjectNot64Tests(LargeObjectTestCase):
|
||||||
(OverflowError, psycopg2.InterfaceError, psycopg2.NotSupportedError),
|
(OverflowError, psycopg2.InterfaceError, psycopg2.NotSupportedError),
|
||||||
lo.truncate, length)
|
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():
|
def test_suite():
|
||||||
return unittest.TestLoader().loadTestsFromName(__name__)
|
return unittest.TestLoader().loadTestsFromName(__name__)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
|
@ -173,8 +173,8 @@ class ExceptionsTestCase(ConnectingTestCase):
|
||||||
'column_name', 'constraint_name', 'context', 'datatype_name',
|
'column_name', 'constraint_name', 'context', 'datatype_name',
|
||||||
'internal_position', 'internal_query', 'message_detail',
|
'internal_position', 'internal_query', 'message_detail',
|
||||||
'message_hint', 'message_primary', 'schema_name', 'severity',
|
'message_hint', 'message_primary', 'schema_name', 'severity',
|
||||||
'source_file', 'source_function', 'source_line', 'sqlstate',
|
'severity_nonlocalized', 'source_file', 'source_function',
|
||||||
'statement_position', 'table_name', ]:
|
'source_line', 'sqlstate', 'statement_position', 'table_name', ]:
|
||||||
v = getattr(diag, attr)
|
v = getattr(diag, attr)
|
||||||
if v is not None:
|
if v is not None:
|
||||||
self.assert_(isinstance(v, str))
|
self.assert_(isinstance(v, str))
|
||||||
|
@ -276,6 +276,15 @@ class ExceptionsTestCase(ConnectingTestCase):
|
||||||
self.assertEqual(e.diag.constraint_name, "chk_eq1")
|
self.assertEqual(e.diag.constraint_name, "chk_eq1")
|
||||||
self.assertEqual(e.diag.datatype_name, None)
|
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):
|
def test_pickle(self):
|
||||||
import pickle
|
import pickle
|
||||||
cur = self.conn.cursor()
|
cur = self.conn.cursor()
|
||||||
|
@ -339,5 +348,6 @@ class TestVersionDiscovery(unittest.TestCase):
|
||||||
def test_suite():
|
def test_suite():
|
||||||
return unittest.TestLoader().loadTestsFromName(__name__)
|
return unittest.TestLoader().loadTestsFromName(__name__)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
|
@ -61,7 +61,7 @@ import %(module)s as psycopg2
|
||||||
import %(module)s.extensions as ext
|
import %(module)s.extensions as ext
|
||||||
conn = psycopg2.connect(%(dsn)r)
|
conn = psycopg2.connect(%(dsn)r)
|
||||||
conn.set_isolation_level(ext.ISOLATION_LEVEL_AUTOCOMMIT)
|
conn.set_isolation_level(ext.ISOLATION_LEVEL_AUTOCOMMIT)
|
||||||
print(conn.get_backend_pid())
|
print(conn.info.backend_pid)
|
||||||
curs = conn.cursor()
|
curs = conn.cursor()
|
||||||
curs.execute("NOTIFY " %(name)r %(payload)r)
|
curs.execute("NOTIFY " %(name)r %(payload)r)
|
||||||
curs.close()
|
curs.close()
|
||||||
|
@ -147,9 +147,9 @@ conn.close()
|
||||||
|
|
||||||
@slow
|
@slow
|
||||||
def test_notify_payload(self):
|
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"
|
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.autocommit(self.conn)
|
||||||
self.listen('foo')
|
self.listen('foo')
|
||||||
pid = int(self.notify('foo', payload="Hello, world!").communicate()[0])
|
pid = int(self.notify('foo', payload="Hello, world!").communicate()[0])
|
||||||
|
|
|
@ -26,7 +26,6 @@ from . import dbapi20
|
||||||
from . import dbapi20_tpc
|
from . import dbapi20_tpc
|
||||||
from .testutils import skip_if_tpc_disabled
|
from .testutils import skip_if_tpc_disabled
|
||||||
import unittest
|
import unittest
|
||||||
from .testutils import decorate_all_tests
|
|
||||||
import psycopg2
|
import psycopg2
|
||||||
|
|
||||||
from .testconfig import dsn
|
from .testconfig import dsn
|
||||||
|
@ -55,11 +54,9 @@ class Psycopg2Tests(dbapi20.DatabaseAPI20Test):
|
||||||
r = cur.fetchall()
|
r = cur.fetchall()
|
||||||
self.assertEqual(len(r), 1, 'callproc produced no result set')
|
self.assertEqual(len(r), 1, 'callproc produced no result set')
|
||||||
self.assertEqual(len(r[0]), 1,
|
self.assertEqual(len(r[0]), 1,
|
||||||
'callproc produced invalid result set'
|
'callproc produced invalid result set')
|
||||||
)
|
|
||||||
self.assertEqual(r[0][0], 'foo',
|
self.assertEqual(r[0][0], 'foo',
|
||||||
'callproc produced invalid results'
|
'callproc produced invalid results')
|
||||||
)
|
|
||||||
finally:
|
finally:
|
||||||
con.close()
|
con.close()
|
||||||
|
|
||||||
|
@ -72,17 +69,17 @@ class Psycopg2Tests(dbapi20.DatabaseAPI20Test):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@skip_if_tpc_disabled
|
||||||
class Psycopg2TPCTests(dbapi20_tpc.TwoPhaseCommitTests, unittest.TestCase):
|
class Psycopg2TPCTests(dbapi20_tpc.TwoPhaseCommitTests, unittest.TestCase):
|
||||||
driver = psycopg2
|
driver = psycopg2
|
||||||
|
|
||||||
def connect(self):
|
def connect(self):
|
||||||
return psycopg2.connect(dsn=dsn)
|
return psycopg2.connect(dsn=dsn)
|
||||||
|
|
||||||
decorate_all_tests(Psycopg2TPCTests, skip_if_tpc_disabled)
|
|
||||||
|
|
||||||
|
|
||||||
def test_suite():
|
def test_suite():
|
||||||
return unittest.TestLoader().loadTestsFromName(__name__)
|
return unittest.TestLoader().loadTestsFromName(__name__)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
|
@ -46,8 +46,8 @@ class QuotingTestCase(ConnectingTestCase):
|
||||||
The tests also check that no warning is raised ('escape_string_warning'
|
The tests also check that no warning is raised ('escape_string_warning'
|
||||||
should be on).
|
should be on).
|
||||||
|
|
||||||
http://www.postgresql.org/docs/current/static/sql-syntax-lexical.html#SQL-SYNTAX-STRINGS
|
https://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/runtime-config-compatible.html
|
||||||
"""
|
"""
|
||||||
def test_string(self):
|
def test_string(self):
|
||||||
data = """some data with \t chars
|
data = """some data with \t chars
|
||||||
|
@ -90,7 +90,7 @@ class QuotingTestCase(ConnectingTestCase):
|
||||||
else:
|
else:
|
||||||
res = curs.fetchone()[0].tobytes()
|
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(
|
return self.skipTest(
|
||||||
"bytea broken with server >= 9.0, libpq < 9")
|
"bytea broken with server >= 9.0, libpq < 9")
|
||||||
|
|
||||||
|
@ -126,7 +126,8 @@ class QuotingTestCase(ConnectingTestCase):
|
||||||
if sys.version_info[0] < 3:
|
if sys.version_info[0] < 3:
|
||||||
data = ''.join(map(chr, range(32, 127) + range(160, 256)))
|
data = ''.join(map(chr, range(32, 127) + range(160, 256)))
|
||||||
else:
|
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
|
# as string
|
||||||
curs.execute("SELECT %s::text;", (data,))
|
curs.execute("SELECT %s::text;", (data,))
|
||||||
|
@ -150,7 +151,8 @@ class QuotingTestCase(ConnectingTestCase):
|
||||||
if sys.version_info[0] < 3:
|
if sys.version_info[0] < 3:
|
||||||
data = ''.join(map(chr, range(32, 127) + range(128, 256)))
|
data = ''.join(map(chr, range(32, 127) + range(128, 256)))
|
||||||
else:
|
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
|
# as string
|
||||||
curs.execute("SELECT %s::text;", (data,))
|
curs.execute("SELECT %s::text;", (data,))
|
||||||
|
@ -252,5 +254,6 @@ class TestStringAdapter(ConnectingTestCase):
|
||||||
def test_suite():
|
def test_suite():
|
||||||
return unittest.TestLoader().loadTestsFromName(__name__)
|
return unittest.TestLoader().loadTestsFromName(__name__)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
|
@ -24,9 +24,8 @@
|
||||||
|
|
||||||
import datetime as dt
|
import datetime as dt
|
||||||
import unittest
|
import unittest
|
||||||
from .testutils import (ConnectingTestCase,
|
from .testutils import (
|
||||||
skip_before_postgres, skip_before_python, skip_copy_if_green,
|
ConnectingTestCase, skip_before_postgres, skip_copy_if_green, StringIO)
|
||||||
StringIO)
|
|
||||||
|
|
||||||
import psycopg2
|
import psycopg2
|
||||||
from psycopg2 import sql
|
from psycopg2 import sql
|
||||||
|
@ -181,26 +180,43 @@ class IdentifierTests(ConnectingTestCase):
|
||||||
def test_init(self):
|
def test_init(self):
|
||||||
self.assert_(isinstance(sql.Identifier('foo'), sql.Identifier))
|
self.assert_(isinstance(sql.Identifier('foo'), sql.Identifier))
|
||||||
self.assert_(isinstance(sql.Identifier(u'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, 10)
|
||||||
self.assertRaises(TypeError, sql.Identifier, dt.date(2016, 12, 31))
|
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.assertEqual(sql.Identifier('foo').string, 'foo')
|
||||||
|
self.assertRaises(AttributeError,
|
||||||
|
getattr, sql.Identifier('foo', 'bar'), 'string')
|
||||||
|
|
||||||
def test_repr(self):
|
def test_repr(self):
|
||||||
obj = sql.Identifier("fo'o")
|
obj = sql.Identifier("fo'o")
|
||||||
self.assertEqual(repr(obj), 'Identifier("fo\'o")')
|
self.assertEqual(repr(obj), 'Identifier("fo\'o")')
|
||||||
self.assertEqual(repr(obj), str(obj))
|
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):
|
def test_eq(self):
|
||||||
self.assert_(sql.Identifier('foo') == sql.Identifier('foo'))
|
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') != sql.Identifier('bar'))
|
||||||
self.assert_(sql.Identifier('foo') != 'foo')
|
self.assert_(sql.Identifier('foo') != 'foo')
|
||||||
self.assert_(sql.Identifier('foo') != sql.SQL('foo'))
|
self.assert_(sql.Identifier('foo') != sql.SQL('foo'))
|
||||||
|
|
||||||
def test_as_str(self):
|
def test_as_str(self):
|
||||||
self.assertEqual(sql.Identifier('foo').as_string(self.conn), '"foo"')
|
self.assertEqual(
|
||||||
self.assertEqual(sql.Identifier("fo'o").as_string(self.conn), '"fo\'o"')
|
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):
|
def test_join(self):
|
||||||
self.assert_(not hasattr(sql.Identifier('foo'), 'join'))
|
self.assert_(not hasattr(sql.Identifier('foo'), 'join'))
|
||||||
|
@ -396,5 +412,6 @@ class ValuesTest(ConnectingTestCase):
|
||||||
def test_suite():
|
def test_suite():
|
||||||
return unittest.TestLoader().loadTestsFromName(__name__)
|
return unittest.TestLoader().loadTestsFromName(__name__)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
|
@ -249,5 +249,6 @@ class QueryCancellationTests(ConnectingTestCase):
|
||||||
def test_suite():
|
def test_suite():
|
||||||
return unittest.TestLoader().loadTestsFromName(__name__)
|
return unittest.TestLoader().loadTestsFromName(__name__)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
|
@ -167,6 +167,13 @@ class TypesBasicTests(ConnectingTestCase):
|
||||||
curs.execute("select col from array_test where id = 2")
|
curs.execute("select col from array_test where id = 2")
|
||||||
self.assertEqual(curs.fetchone()[0], [])
|
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):
|
def testEmptyArrayNoCast(self):
|
||||||
s = self.execute("SELECT '{}' AS foo")
|
s = self.execute("SELECT '{}' AS foo")
|
||||||
self.assertEqual(s, '{}')
|
self.assertEqual(s, '{}')
|
||||||
|
@ -452,6 +459,20 @@ class AdaptSubclassTest(unittest.TestCase):
|
||||||
self.assertEqual(ext.adapt(foo((1, 2, 3))).getquoted(), 'bar')
|
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):
|
class ByteaParserTest(unittest.TestCase):
|
||||||
"""Unit test for our bytea format parser."""
|
"""Unit test for our bytea format parser."""
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
@ -539,22 +560,9 @@ class ByteaParserTest(unittest.TestCase):
|
||||||
self.assertEqual(rv, tgt)
|
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():
|
def test_suite():
|
||||||
return unittest.TestLoader().loadTestsFromName(__name__)
|
return unittest.TestLoader().loadTestsFromName(__name__)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
|
@ -24,8 +24,7 @@ from pickle import dumps, loads
|
||||||
|
|
||||||
import unittest
|
import unittest
|
||||||
from .testutils import (skip_if_no_uuid, skip_before_postgres,
|
from .testutils import (skip_if_no_uuid, skip_before_postgres,
|
||||||
ConnectingTestCase, decorate_all_tests, py3_raises_typeerror, slow,
|
ConnectingTestCase, py3_raises_typeerror, slow, skip_from_python)
|
||||||
skip_from_python)
|
|
||||||
|
|
||||||
import psycopg2
|
import psycopg2
|
||||||
import psycopg2.extras
|
import psycopg2.extras
|
||||||
|
@ -123,6 +122,7 @@ class TypesExtrasTests(ConnectingTestCase):
|
||||||
s = self.execute("""SELECT '{"(1,2)","(3,4)"}' AS foo""")
|
s = self.execute("""SELECT '{"(1,2)","(3,4)"}' AS foo""")
|
||||||
self.failUnless(s == """{"(1,2)","(3,4)"}""")
|
self.failUnless(s == """{"(1,2)","(3,4)"}""")
|
||||||
|
|
||||||
|
|
||||||
def skip_if_no_hstore(f):
|
def skip_if_no_hstore(f):
|
||||||
@wraps(f)
|
@wraps(f)
|
||||||
def skip_if_no_hstore_(self):
|
def skip_if_no_hstore_(self):
|
||||||
|
@ -137,7 +137,7 @@ def skip_if_no_hstore(f):
|
||||||
|
|
||||||
class HstoreTestCase(ConnectingTestCase):
|
class HstoreTestCase(ConnectingTestCase):
|
||||||
def test_adapt_8(self):
|
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")
|
return self.skipTest("skipping dict adaptation with PG pre-9 syntax")
|
||||||
|
|
||||||
from psycopg2.extras import HstoreAdapter
|
from psycopg2.extras import HstoreAdapter
|
||||||
|
@ -163,7 +163,7 @@ class HstoreTestCase(ConnectingTestCase):
|
||||||
self.assertQuotedEqual(ii[3], b"('d' => '" + encc + b"')")
|
self.assertQuotedEqual(ii[3], b"('d' => '" + encc + b"')")
|
||||||
|
|
||||||
def test_adapt_9(self):
|
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")
|
return self.skipTest("skipping dict adaptation with PG 9 syntax")
|
||||||
|
|
||||||
from psycopg2.extras import HstoreAdapter
|
from psycopg2.extras import HstoreAdapter
|
||||||
|
@ -448,10 +448,10 @@ class HstoreTestCase(ConnectingTestCase):
|
||||||
def skip_if_no_composite(f):
|
def skip_if_no_composite(f):
|
||||||
@wraps(f)
|
@wraps(f)
|
||||||
def skip_if_no_composite_(self):
|
def skip_if_no_composite_(self):
|
||||||
if self.conn.server_version < 80000:
|
if self.conn.info.server_version < 80000:
|
||||||
return self.skipTest(
|
return self.skipTest(
|
||||||
"server version %s doesn't support composite types"
|
"server version %s doesn't support composite types"
|
||||||
% self.conn.server_version)
|
% self.conn.info.server_version)
|
||||||
|
|
||||||
return f(self)
|
return f(self)
|
||||||
|
|
||||||
|
@ -1048,6 +1048,7 @@ def skip_if_no_jsonb_type(f):
|
||||||
return skip_before_postgres(9, 4)(f)
|
return skip_before_postgres(9, 4)(f)
|
||||||
|
|
||||||
|
|
||||||
|
@skip_if_no_jsonb_type
|
||||||
class JsonbTestCase(ConnectingTestCase):
|
class JsonbTestCase(ConnectingTestCase):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def myloads(s):
|
def myloads(s):
|
||||||
|
@ -1137,8 +1138,6 @@ class JsonbTestCase(ConnectingTestCase):
|
||||||
curs.execute("""select NULL::jsonb[]""")
|
curs.execute("""select NULL::jsonb[]""")
|
||||||
self.assertEqual(curs.fetchone()[0], None)
|
self.assertEqual(curs.fetchone()[0], None)
|
||||||
|
|
||||||
decorate_all_tests(JsonbTestCase, skip_if_no_jsonb_type)
|
|
||||||
|
|
||||||
|
|
||||||
class RangeTestCase(unittest.TestCase):
|
class RangeTestCase(unittest.TestCase):
|
||||||
def test_noparam(self):
|
def test_noparam(self):
|
||||||
|
@ -1386,20 +1385,54 @@ class RangeTestCase(unittest.TestCase):
|
||||||
r = Range(0, 4)
|
r = Range(0, 4)
|
||||||
self.assertEqual(loads(dumps(r)), r)
|
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):
|
Using ``repr`` for all string conversions can be very unreadable for
|
||||||
@wraps(f)
|
longer types like ``DateTimeTZRange``.
|
||||||
def skip_if_no_range_(self):
|
'''
|
||||||
if self.conn.server_version < 90200:
|
from psycopg2.extras import Range
|
||||||
return self.skipTest(
|
|
||||||
"server version %s doesn't support range types"
|
|
||||||
% self.conn.server_version)
|
|
||||||
|
|
||||||
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):
|
class RangeCasterTestCase(ConnectingTestCase):
|
||||||
|
|
||||||
builtin_ranges = ('int4range', 'int8range', 'numrange',
|
builtin_ranges = ('int4range', 'int8range', 'numrange',
|
||||||
|
@ -1717,8 +1750,6 @@ class RangeCasterTestCase(ConnectingTestCase):
|
||||||
for r in [ra1, ra2, rars2, rars3]:
|
for r in [ra1, ra2, rars2, rars3]:
|
||||||
del ext.adapters[r.range, ext.ISQLQuote]
|
del ext.adapters[r.range, ext.ISQLQuote]
|
||||||
|
|
||||||
decorate_all_tests(RangeCasterTestCase, skip_if_no_range)
|
|
||||||
|
|
||||||
|
|
||||||
def test_suite():
|
def test_suite():
|
||||||
return unittest.TestLoader().loadTestsFromName(__name__)
|
return unittest.TestLoader().loadTestsFromName(__name__)
|
||||||
|
|
|
@ -217,12 +217,13 @@ class WithCursorTestCase(WithTestCase):
|
||||||
|
|
||||||
@skip_before_postgres(8, 2)
|
@skip_before_postgres(8, 2)
|
||||||
def test_named_with_noop(self):
|
def test_named_with_noop(self):
|
||||||
with self.conn.cursor('named') as cur:
|
with self.conn.cursor('named'):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def test_suite():
|
def test_suite():
|
||||||
return unittest.TestLoader().loadTestsFromName(__name__)
|
return unittest.TestLoader().loadTestsFromName(__name__)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
import re
|
import re
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
import types
|
||||||
import select
|
import select
|
||||||
import platform
|
import platform
|
||||||
import unittest
|
import unittest
|
||||||
|
@ -42,15 +43,15 @@ if sys.version_info[0] == 2:
|
||||||
unichr = unichr
|
unichr = unichr
|
||||||
else:
|
else:
|
||||||
# Python 3
|
# Python 3
|
||||||
from io import StringIO
|
from io import StringIO # noqa
|
||||||
from importlib import reload
|
from importlib import reload # noqa
|
||||||
long = int
|
long = int
|
||||||
unichr = chr
|
unichr = chr
|
||||||
|
|
||||||
|
|
||||||
# Silence warnings caused by the stubbornness of the Python unittest
|
# Silence warnings caused by the stubbornness of the Python unittest
|
||||||
# maintainers
|
# maintainers
|
||||||
# http://bugs.python.org/issue9424
|
# https://bugs.python.org/issue9424
|
||||||
if (not hasattr(unittest.TestCase, 'assert_')
|
if (not hasattr(unittest.TestCase, 'assert_')
|
||||||
or unittest.TestCase.assert_ is not unittest.TestCase.assertTrue):
|
or unittest.TestCase.assert_ is not unittest.TestCase.assertTrue):
|
||||||
# mavaff...
|
# mavaff...
|
||||||
|
@ -64,6 +65,7 @@ def assertDsnEqual(self, dsn1, dsn2, msg=None):
|
||||||
"""Check that two conninfo string have the same content"""
|
"""Check that two conninfo string have the same content"""
|
||||||
self.assertEqual(set(dsn1.split()), set(dsn2.split()), msg)
|
self.assertEqual(set(dsn1.split()), set(dsn2.split()), msg)
|
||||||
|
|
||||||
|
|
||||||
unittest.TestCase.assertDsnEqual = assertDsnEqual
|
unittest.TestCase.assertDsnEqual = assertDsnEqual
|
||||||
|
|
||||||
|
|
||||||
|
@ -173,16 +175,31 @@ class ConnectingTestCase(unittest.TestCase):
|
||||||
raise Exception("Unexpected result from poll: %r", state)
|
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'):
|
if n.startswith('test'):
|
||||||
for d in decorators:
|
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):
|
def skip_if_no_uuid(f):
|
||||||
"""Decorator to skip a test if uuid is not supported by Py/PG."""
|
"""Decorator to skip a test if uuid is not supported by Py/PG."""
|
||||||
@wraps(f)
|
@wraps(f)
|
||||||
|
@ -202,6 +219,7 @@ def skip_if_no_uuid(f):
|
||||||
return skip_if_no_uuid_
|
return skip_if_no_uuid_
|
||||||
|
|
||||||
|
|
||||||
|
@decorate_all_tests
|
||||||
def skip_if_tpc_disabled(f):
|
def skip_if_tpc_disabled(f):
|
||||||
"""Skip a test if the server has tpc support disabled."""
|
"""Skip a test if the server has tpc support disabled."""
|
||||||
@wraps(f)
|
@wraps(f)
|
||||||
|
@ -229,14 +247,20 @@ def skip_if_tpc_disabled(f):
|
||||||
|
|
||||||
def skip_before_postgres(*ver):
|
def skip_before_postgres(*ver):
|
||||||
"""Skip a test on PostgreSQL before a certain version."""
|
"""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))
|
ver = ver + (0,) * (3 - len(ver))
|
||||||
|
|
||||||
|
@decorate_all_tests
|
||||||
def skip_before_postgres_(f):
|
def skip_before_postgres_(f):
|
||||||
@wraps(f)
|
@wraps(f)
|
||||||
def skip_before_postgres__(self):
|
def skip_before_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"
|
return self.skipTest(
|
||||||
% self.conn.server_version)
|
reason or "skipped because PostgreSQL %s"
|
||||||
|
% self.conn.info.server_version)
|
||||||
else:
|
else:
|
||||||
return f(self)
|
return f(self)
|
||||||
|
|
||||||
|
@ -248,12 +272,13 @@ def skip_after_postgres(*ver):
|
||||||
"""Skip a test on PostgreSQL after (including) a certain version."""
|
"""Skip a test on PostgreSQL after (including) a certain version."""
|
||||||
ver = ver + (0,) * (3 - len(ver))
|
ver = ver + (0,) * (3 - len(ver))
|
||||||
|
|
||||||
|
@decorate_all_tests
|
||||||
def skip_after_postgres_(f):
|
def skip_after_postgres_(f):
|
||||||
@wraps(f)
|
@wraps(f)
|
||||||
def skip_after_postgres__(self):
|
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"
|
return self.skipTest("skipped because PostgreSQL %s"
|
||||||
% self.conn.server_version)
|
% self.conn.info.server_version)
|
||||||
else:
|
else:
|
||||||
return f(self)
|
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."""
|
"""Skip a test if libpq we're linked to is older than a certain version."""
|
||||||
ver = ver + (0,) * (3 - len(ver))
|
ver = ver + (0,) * (3 - len(ver))
|
||||||
|
|
||||||
|
@decorate_all_tests
|
||||||
def skip_before_libpq_(f):
|
def skip_before_libpq_(f):
|
||||||
@wraps(f)
|
@wraps(f)
|
||||||
def skip_before_libpq__(self):
|
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."""
|
"""Skip a test if libpq we're linked to is newer than a certain version."""
|
||||||
ver = ver + (0,) * (3 - len(ver))
|
ver = ver + (0,) * (3 - len(ver))
|
||||||
|
|
||||||
|
@decorate_all_tests
|
||||||
def skip_after_libpq_(f):
|
def skip_after_libpq_(f):
|
||||||
@wraps(f)
|
@wraps(f)
|
||||||
def skip_after_libpq__(self):
|
def skip_after_libpq__(self):
|
||||||
|
@ -305,6 +332,7 @@ def skip_after_libpq(*ver):
|
||||||
|
|
||||||
def skip_before_python(*ver):
|
def skip_before_python(*ver):
|
||||||
"""Skip a test on Python before a certain version."""
|
"""Skip a test on Python before a certain version."""
|
||||||
|
@decorate_all_tests
|
||||||
def skip_before_python_(f):
|
def skip_before_python_(f):
|
||||||
@wraps(f)
|
@wraps(f)
|
||||||
def skip_before_python__(self):
|
def skip_before_python__(self):
|
||||||
|
@ -320,6 +348,7 @@ def skip_before_python(*ver):
|
||||||
|
|
||||||
def skip_from_python(*ver):
|
def skip_from_python(*ver):
|
||||||
"""Skip a test on Python after (including) a certain version."""
|
"""Skip a test on Python after (including) a certain version."""
|
||||||
|
@decorate_all_tests
|
||||||
def skip_from_python_(f):
|
def skip_from_python_(f):
|
||||||
@wraps(f)
|
@wraps(f)
|
||||||
def skip_from_python__(self):
|
def skip_from_python__(self):
|
||||||
|
@ -333,6 +362,7 @@ def skip_from_python(*ver):
|
||||||
return skip_from_python_
|
return skip_from_python_
|
||||||
|
|
||||||
|
|
||||||
|
@decorate_all_tests
|
||||||
def skip_if_no_superuser(f):
|
def skip_if_no_superuser(f):
|
||||||
"""Skip a test if the database user running the test is not a superuser"""
|
"""Skip a test if the database user running the test is not a superuser"""
|
||||||
@wraps(f)
|
@wraps(f)
|
||||||
|
@ -351,6 +381,7 @@ def skip_if_no_superuser(f):
|
||||||
|
|
||||||
|
|
||||||
def skip_if_green(reason):
|
def skip_if_green(reason):
|
||||||
|
@decorate_all_tests
|
||||||
def skip_if_green_(f):
|
def skip_if_green_(f):
|
||||||
@wraps(f)
|
@wraps(f)
|
||||||
def skip_if_green__(self):
|
def skip_if_green__(self):
|
||||||
|
@ -363,9 +394,11 @@ def skip_if_green(reason):
|
||||||
return skip_if_green__
|
return skip_if_green__
|
||||||
return skip_if_green_
|
return skip_if_green_
|
||||||
|
|
||||||
|
|
||||||
skip_copy_if_green = skip_if_green("copy in async mode currently not supported")
|
skip_copy_if_green = skip_if_green("copy in async mode currently not supported")
|
||||||
|
|
||||||
|
|
||||||
|
@decorate_all_tests
|
||||||
def skip_if_no_getrefcount(f):
|
def skip_if_no_getrefcount(f):
|
||||||
@wraps(f)
|
@wraps(f)
|
||||||
def skip_if_no_getrefcount_(self):
|
def skip_if_no_getrefcount_(self):
|
||||||
|
@ -376,6 +409,7 @@ def skip_if_no_getrefcount(f):
|
||||||
return skip_if_no_getrefcount_
|
return skip_if_no_getrefcount_
|
||||||
|
|
||||||
|
|
||||||
|
@decorate_all_tests
|
||||||
def skip_if_windows(f):
|
def skip_if_windows(f):
|
||||||
"""Skip a test if run on windows"""
|
"""Skip a test if run on windows"""
|
||||||
@wraps(f)
|
@wraps(f)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user