Merge branch 'master' into errors-module

This commit is contained in:
Daniele Varrazzo 2018-10-15 00:58:32 +01:00
commit b205764fdd
78 changed files with 1138 additions and 62312 deletions

View File

@ -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"}
@ -21,6 +21,9 @@ environment:
- {PYVER: "35", PYTHON_ARCH: "64"} - {PYVER: "35", PYTHON_ARCH: "64"}
- {PYVER: "36", PYTHON_ARCH: "32"} - {PYVER: "36", PYTHON_ARCH: "32"}
- {PYVER: "36", PYTHON_ARCH: "64"} - {PYVER: "36", PYTHON_ARCH: "64"}
- {PYVER: "37", PYTHON_ARCH: "32"}
- {PYVER: "37", PYTHON_ARCH: "64"}
OPENSSL_VERSION: "1_0_2n" OPENSSL_VERSION: "1_0_2n"
POSTGRES_VERSION: "10_1" POSTGRES_VERSION: "10_1"
@ -64,6 +67,7 @@ init:
- IF "%PYVER%"=="34" SET VS_VER=10.0 - IF "%PYVER%"=="34" SET VS_VER=10.0
- IF "%PYVER%"=="35" SET VS_VER=14.0 - IF "%PYVER%"=="35" SET VS_VER=14.0
- IF "%PYVER%"=="36" SET VS_VER=14.0 - IF "%PYVER%"=="36" SET VS_VER=14.0
- IF "%PYVER%"=="37" SET VS_VER=14.0
- IF "%VS_VER%"=="10.0" IF "%PYTHON_ARCH%"=="64" SET DISTUTILS_USE_SDK=1 - IF "%VS_VER%"=="10.0" IF "%PYTHON_ARCH%"=="64" SET DISTUTILS_USE_SDK=1
@ -78,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

View File

@ -1,15 +1,17 @@
# Travis CI configuration file for psycopg2 # Travis CI configuration file for psycopg2
dist: trusty dist: xenial
sudo: required sudo: required
language: python language: python
python: matrix:
- 2.7 include:
- 3.7-dev - python: 2.7
- 3.6 - python: 3.7
- 3.5 - python: 3.6
- 3.4 - python: 3.5
- python: 3.4
dist: trusty
install: install:
- pip install -U pip setuptools wheel - pip install -U pip setuptools wheel

View File

@ -25,7 +25,7 @@ statement from all source files in the program, then also delete it here.
You should have received a copy of the GNU Lesser General Public License 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

24
NEWS
View File

@ -9,8 +9,18 @@ New features:
- Added `~psycopg2.errors` module. Every PostgreSQL error is converted into - Added `~psycopg2.errors` module. Every PostgreSQL error is converted into
a specific exception class (:ticket:`#682`). 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.host` property (: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:
@ -23,6 +33,18 @@ Other changes:
install``. install``.
What's new in psycopg 2.7.6
^^^^^^^^^^^^^^^^^^^^^^^^^^^
- Close named cursors if exist, even if `~cursor.execute()` wasn't called
(:ticket:`#746`).
- Fixed building on modern FreeBSD versions with Python 3.7 (:ticket:`#755`).
- Fixed hang trying to :sql:`COPY` via `~cursor.execute()` (:ticket:`#781`).
- Fixed segfault accessing the `connection.readonly` and
`connection.deferrable` repeatedly (:ticket:`#790`).
- `~psycopg2.errorcodes` map updated to PostgreSQL 11.
What's new in psycopg 2.7.5 What's new in psycopg 2.7.5
^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -351,7 +373,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

View File

@ -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

View File

@ -61,9 +61,8 @@ except ImportError:
release = version release = version
intersphinx_mapping = { intersphinx_mapping = {
'py': ('https://docs.python.org/2', None), 'py': ('https://docs.python.org/3', None),
'py3': ('https://docs.python.org/3', None), }
}
# Pattern to generate links to the bug tracker # Pattern to generate links to the bug tracker
ticket_url = 'https://github.com/psycopg/psycopg2/issues/%s' ticket_url = 'https://github.com/psycopg/psycopg2/issues/%s'
@ -117,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)`
""" """

View File

@ -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
@ -366,7 +366,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 +376,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 +388,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 +534,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 +568,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
@ -599,6 +599,24 @@ The ``connection`` class
.. versionadded:: 2.5 .. versionadded:: 2.5
.. index::
pair: Backend; Host
.. attribute:: host
The server host name of the active connection.
This can be a host name, an IP address, or a directory path if the
connection is via Unix socket. (The path case can be distinguished
because it will always be an absolute path, beginning with ``/``.)
.. seealso:: libpq docs for `PQhost()`__ for details.
.. __: https://www.postgresql.org/docs/current/static/libpq-status.html#LIBPQ-PQHOST
.. versionadded:: 2.8.0
.. index:: .. index::
pair: Backend; PID pair: Backend; PID
@ -612,7 +630,7 @@ The ``connection`` class
.. seealso:: libpq docs for `PQbackendPID()`__ for details. .. seealso:: libpq docs for `PQbackendPID()`__ for details.
.. __: http://www.postgresql.org/docs/current/static/libpq-status.html#LIBPQ-PQBACKENDPID .. __: https://www.postgresql.org/docs/current/static/libpq-status.html#LIBPQ-PQBACKENDPID
.. versionadded:: 2.0.8 .. versionadded:: 2.0.8
@ -633,7 +651,7 @@ The ``connection`` class
.. seealso:: libpq docs for `PQparameterStatus()`__ for details. .. seealso:: libpq docs for `PQparameterStatus()`__ for details.
.. __: http://www.postgresql.org/docs/current/static/libpq-status.html#LIBPQ-PQPARAMETERSTATUS .. __: https://www.postgresql.org/docs/current/static/libpq-status.html#LIBPQ-PQPARAMETERSTATUS
.. versionadded:: 2.0.12 .. versionadded:: 2.0.12
@ -656,7 +674,7 @@ 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
@ -673,7 +691,7 @@ The ``connection`` class
.. seealso:: libpq docs for `PQtransactionStatus()`__ for details. .. seealso:: libpq docs for `PQtransactionStatus()`__ for details.
.. __: http://www.postgresql.org/docs/current/static/libpq-status.html#LIBPQ-PQTRANSACTIONSTATUS .. __: https://www.postgresql.org/docs/current/static/libpq-status.html#LIBPQ-PQTRANSACTIONSTATUS
.. index:: .. index::
@ -688,7 +706,7 @@ The ``connection`` class
.. seealso:: libpq docs for `PQprotocolVersion()`__ for details. .. seealso:: libpq docs for `PQprotocolVersion()`__ for details.
.. __: http://www.postgresql.org/docs/current/static/libpq-status.html#LIBPQ-PQPROTOCOLVERSION .. __: https://www.postgresql.org/docs/current/static/libpq-status.html#LIBPQ-PQPROTOCOLVERSION
.. versionadded:: 2.0.12 .. versionadded:: 2.0.12
@ -706,7 +724,7 @@ The ``connection`` class
.. seealso:: libpq docs for `PQserverVersion()`__ for details. .. seealso:: libpq docs for `PQserverVersion()`__ for details.
.. __: http://www.postgresql.org/docs/current/static/libpq-status.html#LIBPQ-PQSERVERVERSION .. __: https://www.postgresql.org/docs/current/static/libpq-status.html#LIBPQ-PQSERVERVERSION
.. versionadded:: 2.0.12 .. versionadded:: 2.0.12
@ -743,7 +761,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:

View File

@ -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

View File

@ -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)

View File

@ -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,80 @@ introspection etc.
Close the object and remove it from the database. Close the object and remove it from the database.
.. class:: Column
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 +260,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 +273,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,8 +501,8 @@ 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
@ -497,7 +575,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)
@ -531,7 +609,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::
@ -545,7 +623,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)
@ -559,7 +637,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)
@ -644,7 +722,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
@ -668,7 +746,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
@ -687,7 +765,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

View File

@ -542,10 +542,10 @@ fields to JSON) you can use the `register_json()` function.
The Python :py:mod:`json` module is used by default to convert Python objects 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

View File

@ -40,7 +40,7 @@ I receive the error *current transaction is aborted, commands ignored until end
PostgreSQL supports nested transactions using the |SAVEPOINT|_ command). 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

View File

@ -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

View File

@ -12,11 +12,11 @@ to use Psycopg on a different Python implementation (PyPy, Jython, IronPython)
there is an experimental `porting of Psycopg for Ctypes`__, but it is not as 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
@ -33,7 +33,7 @@ The current `!psycopg2` implementation supports:
NOTE: keep consistent with setup.py and the /features/ page. NOTE: keep consistent with setup.py and the /features/ page.
- Python version 2.7 - Python version 2.7
- Python 3 versions from 3.4 to 3.6 - Python 3 versions from 3.4 to 3.7
- PostgreSQL server versions from 7.4 to 10 - PostgreSQL server versions from 7.4 to 10
- PostgreSQL client library version from 9.1 - PostgreSQL client library version from 9.1
@ -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::
@ -188,7 +188,7 @@ displayed, `psycopg2-binary` has become a separate package, and from 2.8 it
has become the only way to install the binary package. has become the only way to install the binary package.
If you are using psycopg 2.7 and you want to disable the use of wheel binary If you are using psycopg 2.7 and you want to disable the use of wheel binary
packages, relying on the system system libraries available on your client, you packages, relying on the system libraries available on your client, you
can use the :command:`pip` |--no-binary option|__, e.g.: can use the :command:`pip` |--no-binary option|__, e.g.:
.. code-block:: console .. code-block:: console

View File

@ -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

View File

@ -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

View File

@ -198,8 +198,8 @@ called `SQL injection`_ and is known to be one of the most widespread forms of
attack to database servers. Before continuing, please print `this page`__ as a 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/

View File

@ -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.

View File

@ -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."""

View File

@ -26,7 +26,7 @@ This module contains symbolic names for all PostgreSQL error codes.
# #
# Based on: # Based on:
# #
# http://www.postgresql.org/docs/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'

View File

@ -8,7 +8,7 @@ This module holds all the extensions to the DBAPI-2.0 provided by psycopg.
- `adapt()` -- exposes the PEP-246_ compatible adapting mechanism used - `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
# #

View File

@ -404,7 +404,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 +638,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):
@ -759,8 +759,8 @@ def wait_select(conn):
The function is an example of a wait callback to be registered with The function is an example of a wait callback to be registered with
`~psycopg2.extensions.set_wait_callback()`. This function uses `~psycopg2.extensions.set_wait_callback()`. This function uses
:py:func:`~select.select()` to wait for data available. :py:func:`~select.select()` to wait for data to become available, and
therefore is able to handle/receive SIGINT/KeyboardInterrupt.
""" """
import select import select
from psycopg2.extensions import POLL_OK, POLL_READ, POLL_WRITE from psycopg2.extensions import POLL_OK, POLL_READ, POLL_WRITE

View File

@ -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):

View File

@ -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

48
psycopg/column.h Normal file
View File

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

375
psycopg/column_type.c Normal file
View File

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

View File

@ -154,8 +154,7 @@ typedef unsigned __int64 uint64_t;
#endif #endif
/* what's this, we have no round function either? */ /* what's this, we have no round function either? */
#if (defined(__FreeBSD__) && __FreeBSD_version < 503000) \ #if (defined(_WIN32) && !defined(__GNUC__)) \
|| (defined(_WIN32) && !defined(__GNUC__)) \
|| (defined(sun) || defined(__sun__)) \ || (defined(sun) || defined(__sun__)) \
&& (defined(__SunOS_5_8) || defined(__SunOS_5_9)) && (defined(__SunOS_5_8) || defined(__SunOS_5_9))

View File

@ -757,6 +757,7 @@ psyco_conn_readonly_get(connectionObject *self)
break; break;
} }
Py_XINCREF(rv);
return rv; return rv;
} }
@ -803,6 +804,7 @@ psyco_conn_deferrable_get(connectionObject *self)
break; break;
} }
Py_XINCREF(rv);
return rv; return rv;
} }
@ -992,6 +994,25 @@ psyco_conn_get_backend_pid(connectionObject *self)
return PyInt_FromLong((long)PQbackendPID(self->pgconn)); return PyInt_FromLong((long)PQbackendPID(self->pgconn));
} }
/* get the current host */
#define psyco_conn_host_get_doc \
"host -- Get the host name."
static PyObject *
psyco_conn_host_get(connectionObject *self)
{
const char *val = NULL;
EXC_IF_CONN_CLOSED(self);
val = PQhost(self->pgconn);
if (!val) {
Py_RETURN_NONE;
}
return conn_text_from_chars(self, val);
}
/* reset the currect connection */ /* reset the currect connection */
#define psyco_conn_reset_doc \ #define psyco_conn_reset_doc \
@ -1243,6 +1264,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 },
{ "host",
(getter)psyco_conn_host_get, NULL,
psyco_conn_host_get_doc },
{NULL} {NULL}
}; };
#undef EXCEPTION_GETTER #undef EXCEPTION_GETTER

View File

@ -49,21 +49,21 @@
static PyObject * static PyObject *
psyco_curs_close(cursorObject *self) psyco_curs_close(cursorObject *self)
{ {
PyObject *rv = NULL;
char *lname = NULL;
EXC_IF_ASYNC_IN_PROGRESS(self, close); EXC_IF_ASYNC_IN_PROGRESS(self, close);
if (self->closed) { if (self->closed) {
rv = Py_None;
Py_INCREF(rv);
goto exit; goto exit;
} }
if (self->qname != NULL) { if (self->qname != NULL) {
char buffer[128]; char buffer[256];
PGTransactionStatusType status; PGTransactionStatusType status;
if (!self->query) {
Dprintf("skipping named cursor close because unused");
goto close;
}
if (self->conn) { if (self->conn) {
status = PQtransactionStatus(self->conn->pgconn); status = PQtransactionStatus(self->conn->pgconn);
} }
@ -77,17 +77,45 @@ psyco_curs_close(cursorObject *self)
goto close; goto close;
} }
/* We should close a server-side cursor only if exists, or we get an
* error (#716). If we execute()d the cursor should exist alright, but
* if we didn't there is still the expectation that the cursor is
* closed (#746).
*
* So if we didn't execute() check for the cursor existence before
* closing it (the view exists since PG 8.2 according to docs).
*/
if (!self->query && self->conn->server_version >= 80200) {
if (!(lname = psycopg_escape_string(
self->conn, self->name, -1, NULL, NULL))) {
goto exit;
}
PyOS_snprintf(buffer, sizeof(buffer),
"SELECT 1 FROM pg_catalog.pg_cursors where name = %s",
lname);
if (pq_execute(self, buffer, 0, 0, 1) == -1) { goto exit; }
if (self->rowcount == 0) {
Dprintf("skipping named cursor close because not existing");
goto close;
}
}
EXC_IF_NO_MARK(self); EXC_IF_NO_MARK(self);
PyOS_snprintf(buffer, 127, "CLOSE %s", self->qname); PyOS_snprintf(buffer, sizeof(buffer), "CLOSE %s", self->qname);
if (pq_execute(self, buffer, 0, 0, 1) == -1) return NULL; if (pq_execute(self, buffer, 0, 0, 1) == -1) { goto exit; }
} }
close: close:
self->closed = 1; self->closed = 1;
Dprintf("psyco_curs_close: cursor at %p closed", self); Dprintf("psyco_curs_close: cursor at %p closed", self);
rv = Py_None;
Py_INCREF(rv);
exit: exit:
Py_RETURN_NONE; PyMem_Free(lname);
return rv;
} }
@ -742,7 +770,7 @@ psyco_curs_fetchone(cursorObject *self)
EXC_IF_NO_MARK(self); EXC_IF_NO_MARK(self);
EXC_IF_ASYNC_IN_PROGRESS(self, fetchone); EXC_IF_ASYNC_IN_PROGRESS(self, fetchone);
EXC_IF_TPC_PREPARED(self->conn, fetchone); EXC_IF_TPC_PREPARED(self->conn, fetchone);
PyOS_snprintf(buffer, 127, "FETCH FORWARD 1 FROM %s", self->qname); PyOS_snprintf(buffer, sizeof(buffer), "FETCH FORWARD 1 FROM %s", self->qname);
if (pq_execute(self, buffer, 0, 0, self->withhold) == -1) return NULL; if (pq_execute(self, buffer, 0, 0, self->withhold) == -1) return NULL;
if (_psyco_curs_prefetch(self) < 0) return NULL; if (_psyco_curs_prefetch(self) < 0) return NULL;
} }
@ -791,7 +819,7 @@ psyco_curs_next_named(cursorObject *self)
if (self->row >= self->rowcount) { if (self->row >= self->rowcount) {
char buffer[128]; char buffer[128];
PyOS_snprintf(buffer, 127, "FETCH FORWARD %ld FROM %s", PyOS_snprintf(buffer, sizeof(buffer), "FETCH FORWARD %ld FROM %s",
self->itersize, self->qname); self->itersize, self->qname);
if (pq_execute(self, buffer, 0, 0, self->withhold) == -1) return NULL; if (pq_execute(self, buffer, 0, 0, self->withhold) == -1) return NULL;
if (_psyco_curs_prefetch(self) < 0) return NULL; if (_psyco_curs_prefetch(self) < 0) return NULL;
@ -860,7 +888,7 @@ psyco_curs_fetchmany(cursorObject *self, PyObject *args, PyObject *kwords)
EXC_IF_NO_MARK(self); EXC_IF_NO_MARK(self);
EXC_IF_ASYNC_IN_PROGRESS(self, fetchmany); EXC_IF_ASYNC_IN_PROGRESS(self, fetchmany);
EXC_IF_TPC_PREPARED(self->conn, fetchone); EXC_IF_TPC_PREPARED(self->conn, fetchone);
PyOS_snprintf(buffer, 127, "FETCH FORWARD %d FROM %s", PyOS_snprintf(buffer, sizeof(buffer), "FETCH FORWARD %d FROM %s",
(int)size, self->qname); (int)size, self->qname);
if (pq_execute(self, buffer, 0, 0, self->withhold) == -1) { goto exit; } if (pq_execute(self, buffer, 0, 0, self->withhold) == -1) { goto exit; }
if (_psyco_curs_prefetch(self) < 0) { goto exit; } if (_psyco_curs_prefetch(self) < 0) { goto exit; }
@ -936,7 +964,7 @@ psyco_curs_fetchall(cursorObject *self)
EXC_IF_NO_MARK(self); EXC_IF_NO_MARK(self);
EXC_IF_ASYNC_IN_PROGRESS(self, fetchall); EXC_IF_ASYNC_IN_PROGRESS(self, fetchall);
EXC_IF_TPC_PREPARED(self->conn, fetchall); EXC_IF_TPC_PREPARED(self->conn, fetchall);
PyOS_snprintf(buffer, 127, "FETCH FORWARD ALL FROM %s", self->qname); PyOS_snprintf(buffer, sizeof(buffer), "FETCH FORWARD ALL FROM %s", self->qname);
if (pq_execute(self, buffer, 0, 0, self->withhold) == -1) { goto exit; } if (pq_execute(self, buffer, 0, 0, self->withhold) == -1) { goto exit; }
if (_psyco_curs_prefetch(self) < 0) { goto exit; } if (_psyco_curs_prefetch(self) < 0) { goto exit; }
} }
@ -1233,11 +1261,11 @@ psyco_curs_scroll(cursorObject *self, PyObject *args, PyObject *kwargs)
EXC_IF_TPC_PREPARED(self->conn, scroll); EXC_IF_TPC_PREPARED(self->conn, scroll);
if (strcmp(mode, "absolute") == 0) { if (strcmp(mode, "absolute") == 0) {
PyOS_snprintf(buffer, 127, "MOVE ABSOLUTE %d FROM %s", PyOS_snprintf(buffer, sizeof(buffer), "MOVE ABSOLUTE %d FROM %s",
value, self->qname); value, self->qname);
} }
else { else {
PyOS_snprintf(buffer, 127, "MOVE %d FROM %s", value, self->qname); PyOS_snprintf(buffer, sizeof(buffer), "MOVE %d FROM %s", value, self->qname);
} }
if (pq_execute(self, buffer, 0, 0, self->withhold) == -1) return NULL; if (pq_execute(self, buffer, 0, 0, self->withhold) == -1) return NULL;
if (_psyco_curs_prefetch(self) < 0) return NULL; if (_psyco_curs_prefetch(self) < 0) return NULL;

View File

@ -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";

View File

@ -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);

View File

@ -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"
@ -108,7 +109,7 @@ exit:
/* 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)
{ {
@ -1150,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))) {
@ -1168,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;
} }
} }
@ -1245,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)
@ -1287,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 */
@ -1309,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 */
@ -1330,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;
} }

View File

@ -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"
@ -69,9 +70,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 +837,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 +982,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(&notifyType) = &PyType_Type; Py_TYPE(&notifyType) = &PyType_Type;
if (PyType_Ready(&notifyType) == -1) goto exit; if (PyType_Ready(&notifyType) == -1) goto exit;
@ -1119,7 +1063,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,6 +1081,7 @@ 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*)&notifyType); PyModule_AddObject(module, "Notify", (PyObject*)&notifyType);
PyModule_AddObject(module, "Xid", (PyObject*)&xidType); PyModule_AddObject(module, "Xid", (PyObject*)&xidType);
PyModule_AddObject(module, "Diagnostics", (PyObject*)&diagnosticsType); PyModule_AddObject(module, "Diagnostics", (PyObject*)&diagnosticsType);
@ -1150,7 +1094,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);

View File

@ -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.

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -33,7 +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(
['9.1', '9.2', '9.3', '9.4', '9.5', '9.6', '10']) ['9.1', '9.2', '9.3', '9.4', '9.5', '9.6', '10', '11'])
f = open(filename, "w") f = open(filename, "w")
for line in file_start: for line in file_start:
@ -111,7 +111,7 @@ def fetch_errors(versions):
# 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'

View File

@ -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):

View File

@ -38,8 +38,8 @@ create () {
export PGBIN="$PGDIR/bin" export PGBIN="$PGDIR/bin"
# install postgres versions not available on the image # install postgres versions not available on the image
if (( "$VERNUM" < 902 || "$VERNUM" > 906 )); then if [[ ! -d "${PGDIR}" ]]; then
wget -O - http://initd.org/psycopg/upload/postgresql/postgresql-${PACKAGE}.tar.bz2 \ wget -O - http://initd.org/psycopg/upload/postgresql/postgresql-${PACKAGE}-$(lsb_release -cs).tar.bz2 \
| sudo tar xjf - -C /usr/lib/postgresql | sudo tar xjf - -C /usr/lib/postgresql
fi fi
@ -109,7 +109,6 @@ if [[ -z "$DONT_TEST_PRESENT" ]]; then
create 9.6 create 9.6
create 9.5 create 9.5
create 9.4 create 9.4
create 9.3
fi fi
# Unsupported postgres versions that we still support # Unsupported postgres versions that we still support
@ -124,6 +123,7 @@ if [[ -n "$TEST_PAST" ]]; then
create 9.0 create 9.0
create 9.1 create 9.1
create 9.2 create 9.2
create 9.3
fi fi
# Postgres built from master # Postgres built from master

View File

@ -50,12 +50,12 @@ if [[ -z "$DONT_TEST_PRESENT" ]]; then
run_test 9.6 run_test 9.6
run_test 9.5 run_test 9.5
run_test 9.4 run_test 9.4
run_test 9.3
fi fi
# Unsupported postgres versions that we still support # Unsupported postgres versions that we still support
# Images built by https://github.com/psycopg/psycopg2-wheels/tree/build-dinosaurs # Images built by https://github.com/psycopg/psycopg2-wheels/tree/build-dinosaurs
if [[ -n "$TEST_PAST" ]]; then if [[ -n "$TEST_PAST" ]]; then
run_test 9.3
run_test 9.2 run_test 9.2
run_test 9.1 run_test 9.1
run_test 9.0 run_test 9.0

View File

@ -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'
@ -67,6 +64,7 @@ Programming Language :: Python :: 3
Programming Language :: Python :: 3.4 Programming Language :: Python :: 3.4
Programming Language :: Python :: 3.5 Programming Language :: Python :: 3.5
Programming Language :: Python :: 3.6 Programming Language :: Python :: 3.6
Programming Language :: Python :: 3.7
Programming Language :: Python :: Implementation :: CPython Programming Language :: Python :: Implementation :: CPython
Programming Language :: C Programming Language :: C
Programming Language :: SQL Programming Language :: SQL
@ -415,13 +413,7 @@ For further information please check the 'doc/src/install.rst' file (also at
self.library_dirs.append(pg_config_helper.query("libdir")) self.library_dirs.append(pg_config_helper.query("libdir"))
self.include_dirs.append(pg_config_helper.query("includedir")) self.include_dirs.append(pg_config_helper.query("includedir"))
self.include_dirs.append(pg_config_helper.query("includedir-server")) self.include_dirs.append(pg_config_helper.query("includedir-server"))
try:
# Here we take a conservative approach: we suppose that
# *at least* PostgreSQL 7.4 is available (this is the only
# 7.x series supported by psycopg 2)
pgversion = pg_config_helper.query("version").split()[1] pgversion = pg_config_helper.query("version").split()[1]
except Exception:
pgversion = "7.4.0"
verre = re.compile( verre = re.compile(
r"(\d+)(?:\.(\d+))?(?:(?:\.(\d+))|(devel|(?:alpha|beta|rc)\d+))?") r"(\d+)(?:\.(\d+))?(?:(?:\.(\d+))|(devel|(?:alpha|beta|rc)\d+))?")
@ -494,7 +486,7 @@ 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',
@ -516,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',
'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',

View File

@ -450,6 +450,12 @@ 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__)

View File

@ -35,13 +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, unittest, decorate_all_tests, skip_if_no_superuser, skip_before_postgres,
skip_before_postgres, skip_after_postgres, skip_before_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)
libpq_version
)
from .testconfig import dsn, dbname from .testconfig import dbhost, dsn, dbname
class ConnectionTests(ConnectingTestCase): class ConnectionTests(ConnectingTestCase):
@ -805,6 +803,14 @@ 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
class ConnectionTwoPhaseTests(ConnectingTestCase): class ConnectionTwoPhaseTests(ConnectingTestCase):
def setUp(self): def setUp(self):
@ -1411,55 +1417,37 @@ class TransactionControlTests(ConnectingTestCase):
class TestEncryptPassword(ConnectingTestCase): class TestEncryptPassword(ConnectingTestCase):
@skip_before_postgres(10) @skip_before_postgres(10)
def test_encrypt_password_post_9_6(self): def test_encrypt_password_post_9_6(self):
cur = self.conn.cursor()
cur.execute("SHOW password_encryption;")
server_encryption_algorithm = cur.fetchone()[0]
# MD5 algorithm # MD5 algorithm
self.assertEqual( self.assertEqual(
ext.encrypt_password('psycopg2', 'ashesh', self.conn, 'md5'), ext.encrypt_password('psycopg2', 'ashesh', self.conn, 'md5'),
'md594839d658c28a357126f105b9cb14cfc' 'md594839d658c28a357126f105b9cb14cfc')
)
# keywords # keywords
self.assertEqual( self.assertEqual(
ext.encrypt_password( ext.encrypt_password(
password='psycopg2', user='ashesh', password='psycopg2', user='ashesh',
scope=self.conn, algorithm='md5'), scope=self.conn, algorithm='md5'),
'md594839d658c28a357126f105b9cb14cfc' 'md594839d658c28a357126f105b9cb14cfc')
)
if libpq_version() < 100000: @skip_before_postgres(10)
self.assertRaises( def test_encrypt_server(self):
psycopg2.NotSupportedError, cur = self.conn.cursor()
ext.encrypt_password, 'psycopg2', 'ashesh', self.conn, cur.execute("SHOW password_encryption;")
'scram-sha-256' server_encryption_algorithm = cur.fetchone()[0]
)
else:
enc_password = ext.encrypt_password( enc_password = ext.encrypt_password(
'psycopg2', 'ashesh', self.conn 'psycopg2', 'ashesh', self.conn)
)
if server_encryption_algorithm == 'md5': if server_encryption_algorithm == 'md5':
self.assertEqual( self.assertEqual(
enc_password, 'md594839d658c28a357126f105b9cb14cfc' enc_password, 'md594839d658c28a357126f105b9cb14cfc')
)
elif server_encryption_algorithm == 'scram-sha-256': elif server_encryption_algorithm == 'scram-sha-256':
self.assertEqual(enc_password[:14], 'SCRAM-SHA-256$') self.assertEqual(enc_password[:14], 'SCRAM-SHA-256$')
self.assertEqual( self.assertEqual(
ext.encrypt_password( ext.encrypt_password(
'psycopg2', 'ashesh', self.conn, 'scram-sha-256' 'psycopg2', 'ashesh', self.conn, 'scram-sha-256'
)[:14], 'SCRAM-SHA-256$' )[:14], 'SCRAM-SHA-256$')
)
self.assertRaises(psycopg2.ProgrammingError,
ext.encrypt_password, 'psycopg2', 'ashesh', self.conn, 'abc')
@skip_after_postgres(10)
def test_encrypt_password_pre_10(self):
self.assertEqual(
ext.encrypt_password('psycopg2', 'ashesh', self.conn),
'md594839d658c28a357126f105b9cb14cfc'
)
self.assertRaises(psycopg2.ProgrammingError, self.assertRaises(psycopg2.ProgrammingError,
ext.encrypt_password, 'psycopg2', 'ashesh', self.conn, 'abc') ext.encrypt_password, 'psycopg2', 'ashesh', self.conn, 'abc')
@ -1467,16 +1455,27 @@ class TestEncryptPassword(ConnectingTestCase):
def test_encrypt_md5(self): def test_encrypt_md5(self):
self.assertEqual( self.assertEqual(
ext.encrypt_password('psycopg2', 'ashesh', algorithm='md5'), ext.encrypt_password('psycopg2', 'ashesh', algorithm='md5'),
'md594839d658c28a357126f105b9cb14cfc' 'md594839d658c28a357126f105b9cb14cfc')
)
@skip_before_libpq(10)
def test_encrypt_bad_libpq_10(self):
self.assertRaises(psycopg2.ProgrammingError,
ext.encrypt_password, 'psycopg2', 'ashesh', self.conn, 'abc')
@skip_after_libpq(10)
def test_encrypt_bad_before_libpq_10(self):
self.assertRaises(psycopg2.NotSupportedError,
ext.encrypt_password, 'psycopg2', 'ashesh', self.conn, 'abc')
@skip_before_libpq(10)
def test_encrypt_scram(self): def test_encrypt_scram(self):
if libpq_version() >= 100000:
self.assert_( self.assert_(
ext.encrypt_password( ext.encrypt_password(
'psycopg2', 'ashesh', self.conn, 'scram-sha-256') 'psycopg2', 'ashesh', self.conn, 'scram-sha-256')
.startswith('SCRAM-SHA-256$')) .startswith('SCRAM-SHA-256$'))
else:
@skip_after_libpq(10)
def test_encrypt_scram_pre_10(self):
self.assertRaises(psycopg2.NotSupportedError, self.assertRaises(psycopg2.NotSupportedError,
ext.encrypt_password, ext.encrypt_password,
password='psycopg2', user='ashesh', password='psycopg2', user='ashesh',
@ -1691,6 +1690,19 @@ while True:
self.assert_(not err, err) self.assert_(not err, err)
class TestConnectionProps(ConnectingTestCase):
def test_host(self):
self.assertFalse(self.conn.closed)
expected = dbhost if dbhost else "/"
self.assertIn(expected, self.conn.host)
def test_host_readonly(self):
self.assertFalse(self.conn.closed)
with self.assertRaises(AttributeError):
self.conn.host = 'override'
def test_suite(): def test_suite():
return unittest.TestLoader().loadTestsFromName(__name__) return unittest.TestLoader().loadTestsFromName(__name__)

View File

@ -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')
@ -436,11 +457,24 @@ class CursorTests(ConnectingTestCase):
self.assertEqual([(2,), (3,), (4,)], cur2.fetchmany(3)) self.assertEqual([(2,), (3,), (4,)], cur2.fetchmany(3))
self.assertEqual([(5,), (6,), (7,)], cur2.fetchall()) self.assertEqual([(5,), (6,), (7,)], cur2.fetchall())
@skip_before_postgres(8, 0) @skip_before_postgres(8, 2)
def test_named_noop_close(self): def test_named_noop_close(self):
cur = self.conn.cursor('test') cur = self.conn.cursor('test')
cur.close() cur.close()
@skip_before_postgres(8, 2)
def test_stolen_named_cursor_close(self):
cur1 = self.conn.cursor()
cur1.execute("DECLARE test CURSOR WITHOUT HOLD "
" FOR SELECT generate_series(1,7)")
cur2 = self.conn.cursor('test')
cur2.close()
cur1.execute("DECLARE test CURSOR WITHOUT HOLD "
" FOR SELECT generate_series(1,7)")
cur2 = self.conn.cursor('test')
cur2.close()
@skip_before_postgres(8, 0) @skip_before_postgres(8, 0)
def test_scroll(self): def test_scroll(self):
cur = self.conn.cursor() cur = self.conn.cursor()
@ -491,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

View File

@ -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):

View File

@ -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()

View File

@ -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

View File

@ -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'))

View File

@ -167,6 +167,10 @@ 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], [])
# issue #788 (test commented out until issue fixed)
#curs.execute("select null = any(%s)", ([[]], ))
#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, '{}')

View File

@ -1386,6 +1386,52 @@ 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.
Using ``repr`` for all string conversions can be very unreadable for
longer types like ``DateTimeTZRange``.
'''
from psycopg2.extras import Range
# 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 = []
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)
def skip_if_no_range(f): def skip_if_no_range(f):
@wraps(f) @wraps(f)

View File

@ -215,7 +215,7 @@ class WithCursorTestCase(WithTestCase):
else: else:
self.fail("where is my exception?") self.fail("where is my exception?")
@skip_before_postgres(8, 0) @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') as cur:
pass pass

View File

@ -50,7 +50,7 @@ else:
# 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...

View File

@ -1,5 +1,5 @@
[tox] [tox]
envlist = py{27,34,35,36} envlist = py{27,34,35,36,37}
[testenv] [testenv]
commands = make check commands = make check