mirror of
https://github.com/psycopg/psycopg2.git
synced 2025-07-31 02:20:02 +03:00
Compare commits
51 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
5afb2ce803 | ||
|
41137e4e11 | ||
|
8dc26b86fa | ||
|
a4fcbfed0f | ||
|
e42e07258c | ||
|
6ba6cf27ed | ||
|
485649a3d1 | ||
|
e5da79fcc8 | ||
|
a5762a4e4b | ||
|
8f6a26b911 | ||
|
4460166961 | ||
|
a84c9723b4 | ||
|
06ea808599 | ||
|
0a148dbe01 | ||
|
5eb4185afc | ||
|
7e16a37476 | ||
|
76765aeb39 | ||
|
ac114d2188 | ||
|
8aaf8f1902 | ||
|
ba50548b30 | ||
|
f3685fe5ac | ||
|
7be099bd80 | ||
|
bf58f3b194 | ||
|
9c5bf36791 | ||
|
1477482e59 | ||
|
54cd0c66af | ||
|
b99c8ab2de | ||
|
632cfe0617 | ||
|
ae227effe2 | ||
|
72ed62dc8c | ||
|
b8a9469f45 | ||
|
a5fd594ea4 | ||
|
ad7fd52f30 | ||
|
5f0d411d11 | ||
|
a5f0036a8e | ||
|
1619bae1e3 | ||
|
74059a0dbe | ||
|
a1831ef498 | ||
|
afb42e7625 | ||
|
b4e658d29b | ||
|
296abf735e | ||
|
5ddac80cec | ||
|
858bc3d42a | ||
|
390e43fcb1 | ||
|
7a2dd85caa | ||
|
13b0b9d3e3 | ||
|
5983b96c55 | ||
|
d88d8f9619 | ||
|
9614e7241b | ||
|
4f1505857b | ||
|
f54783ae6e |
|
@ -13,52 +13,19 @@ 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
|
# http://www.appveyor.com/docs/installed-software#python
|
||||||
|
- {PYVER: "27", PYTHON_ARCH: "32"}
|
||||||
|
- {PYVER: "27", PYTHON_ARCH: "64"}
|
||||||
|
- {PYVER: "33", PYTHON_ARCH: "32"}
|
||||||
|
- {PYVER: "33", PYTHON_ARCH: "64"}
|
||||||
|
- {PYVER: "34", PYTHON_ARCH: "32"}
|
||||||
|
- {PYVER: "34", PYTHON_ARCH: "64"}
|
||||||
|
- {PYVER: "35", PYTHON_ARCH: "32"}
|
||||||
|
- {PYVER: "35", PYTHON_ARCH: "64"}
|
||||||
|
- {PYVER: "36", PYTHON_ARCH: "32"}
|
||||||
|
- {PYVER: "36", PYTHON_ARCH: "64"}
|
||||||
|
|
||||||
# Py 2.7 = VS Ver. 9.0 (VS 2008)
|
OPENSSL_VERSION: "1_0_2n"
|
||||||
# Py 3.3, 3.4 = VS Ver. 10.0 (VS 2010)
|
POSTGRES_VERSION: "10_1"
|
||||||
# Py 3.5, 3.6 = VS Ver. 14.0 (VS 2015)
|
|
||||||
|
|
||||||
- PYTHON: C:\Python27-x64
|
|
||||||
PYTHON_ARCH: 64
|
|
||||||
VS_VER: 9.0
|
|
||||||
|
|
||||||
- PYTHON: C:\Python27
|
|
||||||
PYTHON_ARCH: 32
|
|
||||||
VS_VER: 9.0
|
|
||||||
|
|
||||||
- PYTHON: C:\Python36-x64
|
|
||||||
PYTHON_ARCH: 64
|
|
||||||
VS_VER: 14.0
|
|
||||||
|
|
||||||
- PYTHON: C:\Python36
|
|
||||||
PYTHON_ARCH: 32
|
|
||||||
VS_VER: 14.0
|
|
||||||
|
|
||||||
- PYTHON: C:\Python35-x64
|
|
||||||
PYTHON_ARCH: 64
|
|
||||||
VS_VER: 14.0
|
|
||||||
|
|
||||||
- PYTHON: C:\Python35
|
|
||||||
PYTHON_ARCH: 32
|
|
||||||
VS_VER: 14.0
|
|
||||||
|
|
||||||
- PYTHON: C:\Python34-x64
|
|
||||||
DISTUTILS_USE_SDK: '1'
|
|
||||||
PYTHON_ARCH: 64
|
|
||||||
VS_VER: 10.0
|
|
||||||
|
|
||||||
- PYTHON: C:\Python34
|
|
||||||
PYTHON_ARCH: 32
|
|
||||||
VS_VER: 10.0
|
|
||||||
|
|
||||||
- PYTHON: C:\Python33-x64
|
|
||||||
DISTUTILS_USE_SDK: '1'
|
|
||||||
PYTHON_ARCH: 64
|
|
||||||
VS_VER: 10.0
|
|
||||||
|
|
||||||
- PYTHON: C:\Python33
|
|
||||||
PYTHON_ARCH: 32
|
|
||||||
VS_VER: 10.0
|
|
||||||
|
|
||||||
PSYCOPG2_TESTDB: psycopg2_test
|
PSYCOPG2_TESTDB: psycopg2_test
|
||||||
PSYCOPG2_TESTDB_USER: postgres
|
PSYCOPG2_TESTDB_USER: postgres
|
||||||
|
@ -86,7 +53,22 @@ cache:
|
||||||
init:
|
init:
|
||||||
# Uncomment next line to get RDP access during the build.
|
# Uncomment next line to get RDP access during the build.
|
||||||
#- ps: iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1'))
|
#- ps: iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1'))
|
||||||
#
|
|
||||||
|
# Set env variable according to the build environment
|
||||||
|
- SET PYTHON=C:\Python%PYVER%
|
||||||
|
- IF "%PYTHON_ARCH%"=="64" SET PYTHON=%PYTHON%-x64
|
||||||
|
|
||||||
|
# Py 2.7 = VS Ver. 9.0 (VS 2008)
|
||||||
|
# Py 3.3, 3.4 = VS Ver. 10.0 (VS 2010)
|
||||||
|
# Py 3.5, 3.6 = VS Ver. 14.0 (VS 2015)
|
||||||
|
- IF "%PYVER%"=="27" SET VS_VER=9.0
|
||||||
|
- IF "%PYVER%"=="33" SET VS_VER=10.0
|
||||||
|
- IF "%PYVER%"=="34" SET VS_VER=10.0
|
||||||
|
- IF "%PYVER%"=="35" SET VS_VER=14.0
|
||||||
|
- IF "%PYVER%"=="36" SET VS_VER=14.0
|
||||||
|
|
||||||
|
- IF "%VS_VER%"=="10.0" IF "%PYTHON_ARCH%"=="64" SET DISTUTILS_USE_SDK=1
|
||||||
|
|
||||||
# Set Python to the path
|
# Set Python to the path
|
||||||
- SET PATH=%PYTHON%;%PYTHON%\Scripts;C:\Program Files\Git\mingw64\bin;%PATH%
|
- SET PATH=%PYTHON%;%PYTHON%\Scripts;C:\Program Files\Git\mingw64\bin;%PATH%
|
||||||
|
|
||||||
|
@ -162,8 +144,8 @@ install:
|
||||||
}
|
}
|
||||||
# Download OpenSSL source
|
# Download OpenSSL source
|
||||||
- CD C:\Others
|
- CD C:\Others
|
||||||
- IF NOT EXIST OpenSSL_1_0_2l.zip (
|
- IF NOT EXIST OpenSSL_%OPENSSL_VERSION%.zip (
|
||||||
curl -fsSL -o OpenSSL_1_0_2l.zip https://github.com/openssl/openssl/archive/OpenSSL_1_0_2l.zip
|
curl -fsSL -o OpenSSL_%OPENSSL_VERSION%.zip https://github.com/openssl/openssl/archive/OpenSSL_%OPENSSL_VERSION%.zip
|
||||||
)
|
)
|
||||||
|
|
||||||
# To use OpenSSL >= 1.1.0, both libpq and psycopg build environments have
|
# To use OpenSSL >= 1.1.0, both libpq and psycopg build environments have
|
||||||
|
@ -175,15 +157,15 @@ install:
|
||||||
# - nmake build_libs install_dev
|
# - nmake build_libs install_dev
|
||||||
- IF NOT EXIST %OPENSSLTOP%\lib\ssleay32.lib (
|
- IF NOT EXIST %OPENSSLTOP%\lib\ssleay32.lib (
|
||||||
CD %BUILD_DIR% &&
|
CD %BUILD_DIR% &&
|
||||||
7z x C:\Others\OpenSSL_1_0_2l.zip &&
|
7z x C:\Others\OpenSSL_%OPENSSL_VERSION%.zip &&
|
||||||
CD openssl-OpenSSL_1_0_2l &&
|
CD openssl-OpenSSL_%OPENSSL_VERSION% &&
|
||||||
perl Configure %TARGET% no-asm no-shared no-zlib --prefix=%OPENSSLTOP% --openssldir=%OPENSSLTOP% &&
|
perl Configure %TARGET% no-asm no-shared no-zlib --prefix=%OPENSSLTOP% --openssldir=%OPENSSLTOP% &&
|
||||||
CALL ms\%DO% &&
|
CALL ms\%DO% &&
|
||||||
nmake -f ms\nt.mak init headers lib &&
|
nmake -f ms\nt.mak init headers lib &&
|
||||||
COPY inc32\openssl\*.h %OPENSSLTOP%\include\openssl &&
|
COPY inc32\openssl\*.h %OPENSSLTOP%\include\openssl &&
|
||||||
COPY out32\*.lib %OPENSSLTOP%\lib &&
|
COPY out32\*.lib %OPENSSLTOP%\lib &&
|
||||||
CD %BASE_DIR% &&
|
CD %BASE_DIR% &&
|
||||||
RMDIR /S /Q %BUILD_DIR%\openssl-OpenSSL_1_0_2l
|
RMDIR /S /Q %BUILD_DIR%\openssl-OpenSSL_%OPENSSL_VERSION%
|
||||||
)
|
)
|
||||||
|
|
||||||
# Setup directories for building PostgreSQL librarires
|
# Setup directories for building PostgreSQL librarires
|
||||||
|
@ -197,8 +179,8 @@ install:
|
||||||
|
|
||||||
# Download PostgreSQL source
|
# Download PostgreSQL source
|
||||||
- CD C:\Others
|
- CD C:\Others
|
||||||
- IF NOT EXIST postgres-REL_10_0.zip (
|
- IF NOT EXIST postgres-REL_%POSTGRES_VERSION%.zip (
|
||||||
curl -fsSL -o postgres-REL_10_0.zip https://github.com/postgres/postgres/archive/REL_10_0.zip
|
curl -fsSL -o postgres-REL_%POSTGRES_VERSION%.zip https://github.com/postgres/postgres/archive/REL_%POSTGRES_VERSION%.zip
|
||||||
)
|
)
|
||||||
|
|
||||||
# Setup build config file (config.pl)
|
# Setup build config file (config.pl)
|
||||||
|
@ -209,11 +191,11 @@ install:
|
||||||
# Prepare local include directory for building from
|
# Prepare local include directory for building from
|
||||||
# Build pg_config in place
|
# Build pg_config in place
|
||||||
# NOTE: Cannot set and use the same variable inside an IF
|
# NOTE: Cannot set and use the same variable inside an IF
|
||||||
- SET PGBUILD=%BUILD_DIR%\postgres-REL_10_0
|
- SET PGBUILD=%BUILD_DIR%\postgres-REL_%POSTGRES_VERSION%
|
||||||
- IF NOT EXIST %PGTOP%\lib\libpq.lib (
|
- IF NOT EXIST %PGTOP%\lib\libpq.lib (
|
||||||
CD %BUILD_DIR% &&
|
CD %BUILD_DIR% &&
|
||||||
7z x C:\Others\postgres-REL_10_0.zip &&
|
7z x C:\Others\postgres-REL_%POSTGRES_VERSION%.zip &&
|
||||||
CD postgres-REL_10_0\src\tools\msvc &&
|
CD postgres-REL_%POSTGRES_VERSION%\src\tools\msvc &&
|
||||||
ECHO $config-^>{ldap} = 0; > config.pl &&
|
ECHO $config-^>{ldap} = 0; > config.pl &&
|
||||||
ECHO $config-^>{openssl} = "%OPENSSLTOP:\=\\%"; >> config.pl &&
|
ECHO $config-^>{openssl} = "%OPENSSLTOP:\=\\%"; >> config.pl &&
|
||||||
ECHO.>> config.pl &&
|
ECHO.>> config.pl &&
|
||||||
|
|
36
NEWS
36
NEWS
|
@ -4,21 +4,39 @@ Current release
|
||||||
What's new in psycopg 2.7.4
|
What's new in psycopg 2.7.4
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
- Fixed `MinTimeLoggingCursor` on Python 3 (:ticket:`#609`).
|
- Moving away from installing the wheel package by default.
|
||||||
|
Packages installed from wheel raise a warning on import. Added package
|
||||||
|
``psycopg2-binary`` to install from wheel instead (:ticket:`#543`).
|
||||||
|
- Convert fields names into valid Python identifiers in
|
||||||
|
`~psycopg2.extras.NamedTupleCursor` (:ticket:`#211`).
|
||||||
|
- Fixed Solaris 10 support (:ticket:`#532`).
|
||||||
|
- `cursor.mogrify()` can be called on closed cursors (:ticket:`#579`).
|
||||||
|
- Fixed setting session characteristics in corner cases on autocommit
|
||||||
|
connections (:ticket:`#580`).
|
||||||
|
- Fixed `~psycopg2.extras.MinTimeLoggingCursor` on Python 3 (:ticket:`#609`).
|
||||||
|
- Fixed parsing of array of points as floats (:ticket:`#613`).
|
||||||
|
- Fixed `~psycopg2.__libpq_version__` building with libpq >= 10.1
|
||||||
|
(:ticket:`632`).
|
||||||
|
- Fixed `~cursor.rowcount` after `~cursor.executemany()` with :sql:`RETURNING`
|
||||||
|
statements (:ticket:`633`).
|
||||||
|
- Fixed compatibility problem with pypy3 (:ticket:`#649`).
|
||||||
|
- Wheel packages compiled against PostgreSQL 10.1 libpq and OpenSSL 1.0.2n.
|
||||||
|
- Wheel packages for Python 2.6 no more available (support dropped from
|
||||||
|
wheel building infrastructure).
|
||||||
|
|
||||||
|
|
||||||
What's new in psycopg 2.7.3.2
|
What's new in psycopg 2.7.3.2
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
- Wheel package compiled against PostgreSQL 10.0 libpq and OpenSSL 1.0.2l
|
- Wheel package compiled against PostgreSQL 10.0 libpq and OpenSSL 1.0.2l
|
||||||
(:tickets:`#601, #602`)
|
(:tickets:`#601, #602`).
|
||||||
|
|
||||||
|
|
||||||
What's new in psycopg 2.7.3.1
|
What's new in psycopg 2.7.3.1
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
- Dropped libresolv from wheel package to avoid incompatibility with
|
- Dropped libresolv from wheel package to avoid incompatibility with
|
||||||
glibc 2.26 (wheels ticket #2)
|
glibc 2.26 (wheels ticket #2).
|
||||||
|
|
||||||
|
|
||||||
What's new in psycopg 2.7.3
|
What's new in psycopg 2.7.3
|
||||||
|
@ -113,9 +131,13 @@ New features:
|
||||||
|
|
||||||
Bug fixes:
|
Bug fixes:
|
||||||
|
|
||||||
|
- Throw an exception trying to pass ``NULL`` chars as parameters
|
||||||
|
(:ticket:`#420`).
|
||||||
- Fixed error caused by missing decoding `~psycopg2.extras.LoggingConnection`
|
- Fixed error caused by missing decoding `~psycopg2.extras.LoggingConnection`
|
||||||
(:ticket:`#483`).
|
(:ticket:`#483`).
|
||||||
- Fixed integer overflow in :sql:`interval` seconds (:ticket:`#512`).
|
- Fixed integer overflow in :sql:`interval` seconds (:ticket:`#512`).
|
||||||
|
- Make `~psycopg2.extras.Range` objects picklable (:ticket:`#462`).
|
||||||
|
- Fixed version parsing and building with PostgreSQL 10 (:ticket:`#489`).
|
||||||
|
|
||||||
Other changes:
|
Other changes:
|
||||||
|
|
||||||
|
@ -129,14 +151,6 @@ Other changes:
|
||||||
(:ticket:`#506`)
|
(:ticket:`#506`)
|
||||||
|
|
||||||
|
|
||||||
What's new in psycopg 2.6.3
|
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
- Throw an exception trying to pass ``NULL`` chars as parameters
|
|
||||||
(:ticket:`#420`).
|
|
||||||
- Make `~psycopg2.extras.Range` objects picklable (:ticket:`#462`).
|
|
||||||
|
|
||||||
|
|
||||||
What's new in psycopg 2.6.2
|
What's new in psycopg 2.6.2
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
|
33
README.rst
33
README.rst
|
@ -25,29 +25,40 @@ Documentation is included in the ``doc`` directory and is `available online`__.
|
||||||
|
|
||||||
.. __: http://initd.org/psycopg/docs/
|
.. __: http://initd.org/psycopg/docs/
|
||||||
|
|
||||||
|
For any other resource (source code repository, bug tracker, mailing list)
|
||||||
|
please check the `project homepage`__.
|
||||||
|
|
||||||
|
|
||||||
Installation
|
Installation
|
||||||
------------
|
------------
|
||||||
|
|
||||||
If your ``pip`` version supports wheel_ packages it should be possible to
|
Building Psycopg requires a few prerequisites (a C compiler, some development
|
||||||
install a binary version of Psycopg including all the dependencies from PyPI_.
|
packages): please check the install_ and the faq_ documents in the ``doc`` dir
|
||||||
Just run::
|
or online for the details.
|
||||||
|
|
||||||
|
If prerequisites are met, you can install psycopg like any other Python
|
||||||
|
package, using ``pip`` to download it from PyPI_::
|
||||||
|
|
||||||
$ pip install -U pip # make sure your pip is up-to-date
|
|
||||||
$ pip install psycopg2
|
$ pip install psycopg2
|
||||||
|
|
||||||
If you want to build Psycopg from source you will need some prerequisites (a C
|
or using ``setup.py`` if you have downloaded the source package locally::
|
||||||
compiler, development packages): please check the install_ and the faq_
|
|
||||||
documents in the ``doc`` dir for the details.
|
$ python setup.py build
|
||||||
|
$ sudo python setup.py install
|
||||||
|
|
||||||
|
You can also obtain a stand-alone package, not requiring a compiler or
|
||||||
|
external libraries, by installing the `psycopg2-binary`_ package from PyPI::
|
||||||
|
|
||||||
|
$ pip install psycopg2-binary
|
||||||
|
|
||||||
|
The binary package is a practical choice for development and testing but in
|
||||||
|
production it is advised to use the package built from sources.
|
||||||
|
|
||||||
.. _wheel: http://pythonwheels.com/
|
|
||||||
.. _PyPI: https://pypi.python.org/pypi/psycopg2
|
.. _PyPI: https://pypi.python.org/pypi/psycopg2
|
||||||
|
.. _psycopg2-binary: https://pypi.python.org/pypi/psycopg2-binary
|
||||||
.. _install: http://initd.org/psycopg/docs/install.html#install-from-source
|
.. _install: http://initd.org/psycopg/docs/install.html#install-from-source
|
||||||
.. _faq: http://initd.org/psycopg/docs/faq.html#faq-compile
|
.. _faq: http://initd.org/psycopg/docs/faq.html#faq-compile
|
||||||
|
|
||||||
For any other resource (source code repository, bug tracker, mailing list)
|
|
||||||
please check the `project homepage`__.
|
|
||||||
|
|
||||||
.. __: http://initd.org/psycopg/
|
.. __: http://initd.org/psycopg/
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -295,7 +295,9 @@ something to read::
|
||||||
print "Got NOTIFY:", notify.pid, notify.channel, notify.payload
|
print "Got NOTIFY:", notify.pid, notify.channel, notify.payload
|
||||||
|
|
||||||
Running the script and executing a command such as :sql:`NOTIFY test, 'hello'`
|
Running the script and executing a command such as :sql:`NOTIFY test, 'hello'`
|
||||||
in a separate :program:`psql` shell, the output may look similar to::
|
in a separate :program:`psql` shell, the output may look similar to:
|
||||||
|
|
||||||
|
.. code-block:: none
|
||||||
|
|
||||||
Waiting for notifications on channel 'test'
|
Waiting for notifications on channel 'test'
|
||||||
Timeout
|
Timeout
|
||||||
|
|
|
@ -101,6 +101,10 @@ default_role = 'obj'
|
||||||
# output. They are ignored by default.
|
# output. They are ignored by default.
|
||||||
#show_authors = False
|
#show_authors = False
|
||||||
|
|
||||||
|
# Using 'python' instead of the default gives warnings if parsing an example
|
||||||
|
# fails, instead of defaulting to none
|
||||||
|
highlight_language = 'python'
|
||||||
|
|
||||||
# The name of the Pygments (syntax highlighting) style to use.
|
# The name of the Pygments (syntax highlighting) style to use.
|
||||||
pygments_style = 'sphinx'
|
pygments_style = 'sphinx'
|
||||||
|
|
||||||
|
|
|
@ -41,11 +41,6 @@ The ``connection`` class
|
||||||
previously only valid PostgreSQL identifiers were accepted as
|
previously only valid PostgreSQL identifiers were accepted as
|
||||||
cursor name.
|
cursor name.
|
||||||
|
|
||||||
.. warning::
|
|
||||||
It is unsafe to expose the *name* to an untrusted source, for
|
|
||||||
instance you shouldn't allow *name* to be read from a HTML form.
|
|
||||||
Consider it as part of the query, not as a query parameter.
|
|
||||||
|
|
||||||
The *cursor_factory* argument can be used to create non-standard
|
The *cursor_factory* argument can be used to create non-standard
|
||||||
cursors. The class returned must be a subclass of
|
cursors. The class returned must be a subclass of
|
||||||
`psycopg2.extensions.cursor`. See :ref:`subclassing-cursor` for
|
`psycopg2.extensions.cursor`. See :ref:`subclassing-cursor` for
|
||||||
|
|
|
@ -99,20 +99,6 @@ Real dictionary cursor
|
||||||
|
|
||||||
.. versionadded:: 2.3
|
.. versionadded:: 2.3
|
||||||
|
|
||||||
These objects require :py:func:`collections.namedtuple` to be found, so it is
|
|
||||||
available out-of-the-box only from Python 2.6. Anyway, the namedtuple
|
|
||||||
implementation is compatible with previous Python versions, so all you
|
|
||||||
have to do is to `download it`__ and make it available where we
|
|
||||||
expect it to be... ::
|
|
||||||
|
|
||||||
from somewhere import namedtuple
|
|
||||||
import collections
|
|
||||||
collections.namedtuple = namedtuple
|
|
||||||
from psycopg.extras import NamedTupleConnection
|
|
||||||
# ...
|
|
||||||
|
|
||||||
.. __: http://code.activestate.com/recipes/500261-named-tuples/
|
|
||||||
|
|
||||||
.. autoclass:: NamedTupleCursor
|
.. autoclass:: NamedTupleCursor
|
||||||
|
|
||||||
.. autoclass:: NamedTupleConnection
|
.. autoclass:: NamedTupleConnection
|
||||||
|
@ -403,7 +389,7 @@ The individual messages in the replication stream are represented by
|
||||||
|
|
||||||
class LogicalStreamConsumer(object):
|
class LogicalStreamConsumer(object):
|
||||||
|
|
||||||
...
|
# ...
|
||||||
|
|
||||||
def __call__(self, msg):
|
def __call__(self, msg):
|
||||||
self.process_message(msg.payload)
|
self.process_message(msg.payload)
|
||||||
|
@ -501,7 +487,7 @@ The individual messages in the replication stream are represented by
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
def consume(msg):
|
def consume(msg):
|
||||||
...
|
# ...
|
||||||
|
|
||||||
keepalive_interval = 10.0
|
keepalive_interval = 10.0
|
||||||
while True:
|
while True:
|
||||||
|
|
|
@ -306,7 +306,9 @@ I can't compile `!psycopg2`: the compiler says *error: libpq-fe.h: No such file
|
||||||
API support (*i.e.* the libpq used at compile time was at least 9.3) but
|
API support (*i.e.* the libpq used at compile time was at least 9.3) but
|
||||||
at runtime an older libpq dynamic library is found.
|
at runtime an older libpq dynamic library is found.
|
||||||
|
|
||||||
You can use::
|
You can use:
|
||||||
|
|
||||||
|
.. code-block:: shell
|
||||||
|
|
||||||
$ ldd /path/to/packages/psycopg2/_psycopg.so | grep libpq
|
$ ldd /path/to/packages/psycopg2/_psycopg.so | grep libpq
|
||||||
|
|
||||||
|
|
|
@ -12,16 +12,6 @@ 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.
|
||||||
|
|
||||||
The current `!psycopg2` implementation supports:
|
|
||||||
|
|
||||||
..
|
|
||||||
NOTE: keep consistent with setup.py and the /features/ page.
|
|
||||||
|
|
||||||
- Python 2 versions from 2.6 to 2.7
|
|
||||||
- Python 3 versions from 3.2 to 3.6
|
|
||||||
- PostgreSQL server versions from 7.4 to 9.6
|
|
||||||
- PostgreSQL client library version from 9.1
|
|
||||||
|
|
||||||
.. _PostgreSQL: http://www.postgresql.org/
|
.. _PostgreSQL: http://www.postgresql.org/
|
||||||
.. _Python: http://www.python.org/
|
.. _Python: http://www.python.org/
|
||||||
.. _libpq: http://www.postgresql.org/docs/current/static/libpq.html
|
.. _libpq: http://www.postgresql.org/docs/current/static/libpq.html
|
||||||
|
@ -32,94 +22,20 @@ The current `!psycopg2` implementation supports:
|
||||||
|
|
||||||
|
|
||||||
.. index::
|
.. index::
|
||||||
single: Install; from PyPI
|
single: Prerequisites
|
||||||
single: Install; wheel
|
|
||||||
single: Wheel
|
|
||||||
|
|
||||||
Binary install from PyPI
|
Prerequisites
|
||||||
------------------------
|
-------------
|
||||||
|
|
||||||
`!psycopg2` is `available on PyPI`__ in the form of wheel_ packages for the
|
The current `!psycopg2` implementation supports:
|
||||||
most common platform (Linux, OSX, Windows): this should make you able to
|
|
||||||
install a binary version of the module including all the dependencies simply
|
|
||||||
using:
|
|
||||||
|
|
||||||
.. code-block:: console
|
..
|
||||||
|
NOTE: keep consistent with setup.py and the /features/ page.
|
||||||
|
|
||||||
$ pip install psycopg2
|
- Python 2 versions from 2.6 to 2.7
|
||||||
|
- Python 3 versions from 3.2 to 3.6
|
||||||
Make sure to use an up-to-date version of :program:`pip` (you can upgrade it
|
- PostgreSQL server versions from 7.4 to 10
|
||||||
using something like ``pip install -U pip``)
|
- PostgreSQL client library version from 9.1
|
||||||
|
|
||||||
.. __: PyPI_
|
|
||||||
.. _PyPI: https://pypi.python.org/pypi/psycopg2/
|
|
||||||
.. _wheel: http://pythonwheels.com/
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
The binary packages come with their own versions of a few C libraries,
|
|
||||||
among which ``libpq`` and ``libssl``, which will be used regardless of other
|
|
||||||
libraries available on the client: upgrading the system libraries will not
|
|
||||||
upgrade the libraries used by `!psycopg2`. Please build `!psycopg2` from
|
|
||||||
source if you want to maintain binary upgradeability.
|
|
||||||
|
|
||||||
.. warning::
|
|
||||||
|
|
||||||
Because the `!psycopg` wheel package uses its own ``libssl`` binary, it is
|
|
||||||
incompatible with other extension modules binding with ``libssl`` as well,
|
|
||||||
for instance the Python `ssl` module: the result will likely be a
|
|
||||||
segfault. If you need using both `!psycopg2` and other libraries using
|
|
||||||
``libssl`` please :ref:`disable the use of wheel packages for Psycopg
|
|
||||||
<disable-wheel>`.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.. index::
|
|
||||||
single: Install; disable wheel
|
|
||||||
single: Wheel; disable
|
|
||||||
|
|
||||||
.. _disable-wheel:
|
|
||||||
|
|
||||||
Disabling wheel packages
|
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
If you want to disable the use of wheel binary packages and use the system
|
|
||||||
system libraries available on your client you can use the :command:`pip`
|
|
||||||
|--no-binary option|__:
|
|
||||||
|
|
||||||
.. code-block:: console
|
|
||||||
|
|
||||||
$ pip install --no-binary psycopg2
|
|
||||||
|
|
||||||
.. |--no-binary option| replace:: ``--no-binary`` option
|
|
||||||
.. __: https://pip.pypa.io/en/stable/reference/pip_install/#install-no-binary
|
|
||||||
|
|
||||||
which can be specified in your :file:`requirements.txt` files too, e.g. use:
|
|
||||||
|
|
||||||
.. code-block:: none
|
|
||||||
|
|
||||||
psycopg2>=2.7,<2.8 --no-binary psycopg2
|
|
||||||
|
|
||||||
to use the last bugfix release of the `!psycopg2` 2.7 package, specifying to
|
|
||||||
always compile it from source. Of course in this case you will have to meet
|
|
||||||
the :ref:`build prerequisites <build-prerequisites>`.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.. index::
|
|
||||||
single: Install; from source
|
|
||||||
|
|
||||||
.. _install-from-source:
|
|
||||||
|
|
||||||
Install from source
|
|
||||||
-------------------
|
|
||||||
|
|
||||||
.. _source-package:
|
|
||||||
|
|
||||||
You can download a copy of Psycopg source files from the `Psycopg download
|
|
||||||
page`__ or from PyPI_.
|
|
||||||
|
|
||||||
.. __: http://initd.org/psycopg/download/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -128,8 +44,8 @@ page`__ or from PyPI_.
|
||||||
Build prerequisites
|
Build prerequisites
|
||||||
^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
These notes illustrate how to compile Psycopg on Linux. If you want to compile
|
The build prerequisites are to be met in order to install Psycopg from source
|
||||||
Psycopg on other platforms you may have to adjust some details accordingly.
|
code, either from a source distribution package or from PyPI.
|
||||||
|
|
||||||
Psycopg is a C wrapper around the libpq_ PostgreSQL client library. To install
|
Psycopg is a C wrapper around the libpq_ PostgreSQL client library. To install
|
||||||
it from sources you will need:
|
it from sources you will need:
|
||||||
|
@ -161,6 +77,12 @@ it from sources you will need:
|
||||||
|
|
||||||
Once everything is in place it's just a matter of running the standard:
|
Once everything is in place it's just a matter of running the standard:
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
$ pip install psycopg2
|
||||||
|
|
||||||
|
or, from the directory containing the source code:
|
||||||
|
|
||||||
.. code-block:: console
|
.. code-block:: console
|
||||||
|
|
||||||
$ python setup.py build
|
$ python setup.py build
|
||||||
|
@ -197,12 +119,92 @@ which is OS-dependent (for instance setting a suitable
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.. index::
|
||||||
|
single: Install; from PyPI
|
||||||
|
single: Install; wheel
|
||||||
|
single: Wheel
|
||||||
|
|
||||||
|
Binary install from PyPI
|
||||||
|
------------------------
|
||||||
|
|
||||||
|
`!psycopg2` is also `available on PyPI`__ in the form of wheel_ packages for
|
||||||
|
the most common platform (Linux, OSX, Windows): this should make you able to
|
||||||
|
install a binary version of the module, not requiring the above build or
|
||||||
|
runtime prerequisites, simply using:
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
$ pip install psycopg2-binary
|
||||||
|
|
||||||
|
Make sure to use an up-to-date version of :program:`pip` (you can upgrade it
|
||||||
|
using something like ``pip install -U pip``)
|
||||||
|
|
||||||
|
.. __: PyPI-binary_
|
||||||
|
.. _PyPI-binary: https://pypi.python.org/pypi/psycopg2-binary/
|
||||||
|
.. _wheel: http://pythonwheels.com/
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
The binary packages come with their own versions of a few C libraries,
|
||||||
|
among which ``libpq`` and ``libssl``, which will be used regardless of other
|
||||||
|
libraries available on the client: upgrading the system libraries will not
|
||||||
|
upgrade the libraries used by `!psycopg2`. Please build `!psycopg2` from
|
||||||
|
source if you want to maintain binary upgradeability.
|
||||||
|
|
||||||
|
.. warning::
|
||||||
|
|
||||||
|
The `!psycopg2` wheel package comes packaged, among the others, with its
|
||||||
|
own ``libssl`` binary. This may create conflicts with other extension
|
||||||
|
modules binding with ``libssl`` as well, for instance with the Python
|
||||||
|
`ssl` module: in some cases, under concurrency, the interaction between
|
||||||
|
the two libraries may result in a segfault. In case of doubts you are
|
||||||
|
advised to use a package built from source.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.. index::
|
||||||
|
single: Install; disable wheel
|
||||||
|
single: Wheel; disable
|
||||||
|
|
||||||
|
.. _disable-wheel:
|
||||||
|
|
||||||
|
Disabling wheel packages for Psycopg 2.7
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
In version 2.7.x, `pip install psycopg2` would have tried to install the wheel
|
||||||
|
binary package of Psycopg. Because of the problems the wheel package have
|
||||||
|
displayed, `psycopg2-binary` has become a separate package, and from 2.8 it
|
||||||
|
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
|
||||||
|
packages, relying on the system system libraries available on your client, you
|
||||||
|
can use the :command:`pip` |--no-binary option|__:
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
$ pip install --no-binary psycopg2
|
||||||
|
|
||||||
|
.. |--no-binary option| replace:: ``--no-binary`` option
|
||||||
|
.. __: https://pip.pypa.io/en/stable/reference/pip_install/#install-no-binary
|
||||||
|
|
||||||
|
which can be specified in your :file:`requirements.txt` files too, e.g. use:
|
||||||
|
|
||||||
|
.. code-block:: none
|
||||||
|
|
||||||
|
psycopg2>=2.7,<2.8 --no-binary psycopg2
|
||||||
|
|
||||||
|
to use the last bugfix release of the `!psycopg2` 2.7 package, specifying to
|
||||||
|
always compile it from source. Of course in this case you will have to meet
|
||||||
|
the :ref:`build prerequisites <build-prerequisites>`.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.. index::
|
.. index::
|
||||||
single: setup.py
|
single: setup.py
|
||||||
single: setup.cfg
|
single: setup.cfg
|
||||||
|
|
||||||
Non-standard builds
|
Non-standard builds
|
||||||
^^^^^^^^^^^^^^^^^^^
|
-------------------
|
||||||
|
|
||||||
If you have less standard requirements such as:
|
If you have less standard requirements such as:
|
||||||
|
|
||||||
|
@ -242,7 +244,7 @@ order to create a debug package:
|
||||||
- Edit the ``setup.cfg`` file adding the ``PSYCOPG_DEBUG`` flag to the
|
- Edit the ``setup.cfg`` file adding the ``PSYCOPG_DEBUG`` flag to the
|
||||||
``define`` option.
|
``define`` option.
|
||||||
|
|
||||||
- :ref:`Compile and install <source-package>` the package.
|
- :ref:`Compile and install <build-prerequisites>` the package.
|
||||||
|
|
||||||
- Set the :envvar:`PSYCOPG_DEBUG` environment variable:
|
- Set the :envvar:`PSYCOPG_DEBUG` environment variable:
|
||||||
|
|
||||||
|
|
|
@ -24,13 +24,18 @@ directly in the client application.
|
||||||
|
|
||||||
.. method:: getconn(key=None)
|
.. method:: getconn(key=None)
|
||||||
|
|
||||||
Get a free connection and assign it to *key* if not `!None`.
|
Get a free connection from the pool.
|
||||||
|
|
||||||
|
The *key* parameter is optional: if used, the connection will be
|
||||||
|
associated to the key and calling `!getconn()` with the same key again
|
||||||
|
will return the same connection.
|
||||||
|
|
||||||
.. method:: putconn(conn, key=None, close=False)
|
.. method:: putconn(conn, key=None, close=False)
|
||||||
|
|
||||||
Put away a connection.
|
Put away a connection.
|
||||||
|
|
||||||
If *close* is `!True`, discard the connection from the pool.
|
If *close* is `!True`, discard the connection from the pool.
|
||||||
|
*key* should be used consistently with `getconn()`.
|
||||||
|
|
||||||
.. method:: closeall
|
.. method:: closeall
|
||||||
|
|
||||||
|
|
|
@ -48,7 +48,7 @@ The main entry points of Psycopg are:
|
||||||
|
|
||||||
- The class `connection` encapsulates a database session. It allows to:
|
- The class `connection` encapsulates a database session. It allows to:
|
||||||
|
|
||||||
- create new `cursor`\s using the `~connection.cursor()` method to
|
- create new `cursor` instances using the `~connection.cursor()` method to
|
||||||
execute database commands and queries,
|
execute database commands and queries,
|
||||||
|
|
||||||
- terminate transactions using the methods `~connection.commit()` or
|
- terminate transactions using the methods `~connection.commit()` or
|
||||||
|
@ -73,70 +73,97 @@ The main entry points of Psycopg are:
|
||||||
Passing parameters to SQL queries
|
Passing parameters to SQL queries
|
||||||
---------------------------------
|
---------------------------------
|
||||||
|
|
||||||
Psycopg casts Python variables to SQL literals by type. Many standard Python types
|
Psycopg converts Python variables to SQL values using their types: the Python
|
||||||
are already `adapted to the correct SQL representation`__.
|
type determines the function used to convert the object into a string
|
||||||
|
representation suitable for PostgreSQL. Many standard Python types are
|
||||||
|
already `adapted to the correct SQL representation`__.
|
||||||
|
|
||||||
.. __: python-types-adaptation_
|
.. __: python-types-adaptation_
|
||||||
|
|
||||||
Example: the Python function call::
|
Passing parameters to an SQL statement happens in functions such as
|
||||||
|
`cursor.execute()` by using ``%s`` placeholders in the SQL statement, and
|
||||||
|
passing a sequence of values as the second argument of the function. For
|
||||||
|
example the Python function call::
|
||||||
|
|
||||||
>>> cur.execute(
|
>>> cur.execute("""
|
||||||
... """INSERT INTO some_table (an_int, a_date, a_string)
|
... INSERT INTO some_table (an_int, a_date, a_string)
|
||||||
... VALUES (%s, %s, %s);""",
|
... VALUES (%s, %s, %s);
|
||||||
|
... """,
|
||||||
... (10, datetime.date(2005, 11, 18), "O'Reilly"))
|
... (10, datetime.date(2005, 11, 18), "O'Reilly"))
|
||||||
|
|
||||||
is converted into the SQL command::
|
is converted into a SQL command similar to:
|
||||||
|
|
||||||
|
.. code-block:: sql
|
||||||
|
|
||||||
INSERT INTO some_table (an_int, a_date, a_string)
|
INSERT INTO some_table (an_int, a_date, a_string)
|
||||||
VALUES (10, '2005-11-18', 'O''Reilly');
|
VALUES (10, '2005-11-18', 'O''Reilly');
|
||||||
|
|
||||||
Named arguments are supported too using :samp:`%({name})s` placeholders.
|
Named arguments are supported too using :samp:`%({name})s` placeholders in the
|
||||||
Using named arguments the values can be passed to the query in any order and
|
query and specifying the values into a mapping. Using named arguments allows
|
||||||
many placeholders can use the same values::
|
to specify the values in any order and to repeat the same value in several
|
||||||
|
places in the query::
|
||||||
|
|
||||||
>>> cur.execute(
|
>>> cur.execute("""
|
||||||
... """INSERT INTO some_table (an_int, a_date, another_date, a_string)
|
... INSERT INTO some_table (an_int, a_date, another_date, a_string)
|
||||||
... VALUES (%(int)s, %(date)s, %(date)s, %(str)s);""",
|
... VALUES (%(int)s, %(date)s, %(date)s, %(str)s);
|
||||||
|
... """,
|
||||||
... {'int': 10, 'str': "O'Reilly", 'date': datetime.date(2005, 11, 18)})
|
... {'int': 10, 'str': "O'Reilly", 'date': datetime.date(2005, 11, 18)})
|
||||||
|
|
||||||
|
Using characters ``%``, ``(``, ``)`` in the argument names is not supported.
|
||||||
|
|
||||||
When parameters are used, in order to include a literal ``%`` in the query you
|
When parameters are used, in order to include a literal ``%`` in the query you
|
||||||
can use the ``%%`` string. Using characters ``%``, ``(``, ``)`` in the
|
can use the ``%%`` string::
|
||||||
argument names is not supported.
|
|
||||||
|
>>> cur.execute("SELECT (%s % 2) = 0 AS even", (10,)) # WRONG
|
||||||
|
>>> cur.execute("SELECT (%s %% 2) = 0 AS even", (10,)) # correct
|
||||||
|
|
||||||
While the mechanism resembles regular Python strings manipulation, there are a
|
While the mechanism resembles regular Python strings manipulation, there are a
|
||||||
few subtle differences you should care about when passing parameters to a
|
few subtle differences you should care about when passing parameters to a
|
||||||
query:
|
query.
|
||||||
|
|
||||||
- The Python string operator ``%`` is not used: the `~cursor.execute()`
|
- The Python string operator ``%`` *must not be used*: the `~cursor.execute()`
|
||||||
method accepts a tuple or dictionary of values as second parameter.
|
method accepts a tuple or dictionary of values as second parameter.
|
||||||
|sql-warn|__.
|
|sql-warn|__:
|
||||||
|
|
||||||
.. |sql-warn| replace:: **Never** use ``%`` or ``+`` to merge values
|
.. |sql-warn| replace:: **Never** use ``%`` or ``+`` to merge values
|
||||||
into queries
|
into queries
|
||||||
|
|
||||||
.. __: sql-injection_
|
.. __: sql-injection_
|
||||||
|
|
||||||
- The variables placeholder must *always be a* ``%s``, even if a different
|
>>> cur.execute("INSERT INTO numbers VALUES (%s, %s)" % (10, 20)) # WRONG
|
||||||
placeholder (such as a ``%d`` for integers or ``%f`` for floats) may look
|
>>> cur.execute("INSERT INTO numbers VALUES (%s, %s)", (10, 20)) # correct
|
||||||
more appropriate::
|
|
||||||
|
|
||||||
>>> cur.execute("INSERT INTO numbers VALUES (%d)", (42,)) # WRONG
|
|
||||||
>>> cur.execute("INSERT INTO numbers VALUES (%s)", (42,)) # correct
|
|
||||||
|
|
||||||
- For positional variables binding, *the second argument must always be a
|
- For positional variables binding, *the second argument must always be a
|
||||||
sequence*, even if it contains a single variable. And remember that Python
|
sequence*, even if it contains a single variable (remember that Python
|
||||||
requires a comma to create a single element tuple::
|
requires a comma to create a single element tuple)::
|
||||||
|
|
||||||
>>> cur.execute("INSERT INTO foo VALUES (%s)", "bar") # WRONG
|
>>> cur.execute("INSERT INTO foo VALUES (%s)", "bar") # WRONG
|
||||||
>>> cur.execute("INSERT INTO foo VALUES (%s)", ("bar")) # WRONG
|
>>> cur.execute("INSERT INTO foo VALUES (%s)", ("bar")) # WRONG
|
||||||
>>> cur.execute("INSERT INTO foo VALUES (%s)", ("bar",)) # correct
|
>>> cur.execute("INSERT INTO foo VALUES (%s)", ("bar",)) # correct
|
||||||
>>> cur.execute("INSERT INTO foo VALUES (%s)", ["bar"]) # correct
|
>>> cur.execute("INSERT INTO foo VALUES (%s)", ["bar"]) # correct
|
||||||
|
|
||||||
- Only query values should be bound via this method: it shouldn't be used to
|
- The placeholder *must not be quoted*. Psycopg will add quotes where needed::
|
||||||
merge table or field names to the query. If you need to generate dynamically
|
|
||||||
an SQL query (for instance choosing dynamically a table name) you can use
|
|
||||||
the facilities provided by the `psycopg2.sql` module.
|
|
||||||
|
|
||||||
|
>>> cur.execute("INSERT INTO numbers VALUES ('%s')", (10,)) # WRONG
|
||||||
|
>>> cur.execute("INSERT INTO numbers VALUES (%s)", (10,)) # correct
|
||||||
|
|
||||||
|
- The variables placeholder *must always be a* ``%s``, even if a different
|
||||||
|
placeholder (such as a ``%d`` for integers or ``%f`` for floats) may look
|
||||||
|
more appropriate::
|
||||||
|
|
||||||
|
>>> cur.execute("INSERT INTO numbers VALUES (%d)", (10,)) # WRONG
|
||||||
|
>>> cur.execute("INSERT INTO numbers VALUES (%s)", (10,)) # correct
|
||||||
|
|
||||||
|
- Only query values should be bound via this method: it shouldn't be used to
|
||||||
|
merge table or field names to the query (Psycopg will try quoting the table
|
||||||
|
name as a string value, generating invalid SQL). If you need to generate
|
||||||
|
dynamically SQL queries (for instance choosing dynamically a table name)
|
||||||
|
you can use the facilities provided by the `psycopg2.sql` module::
|
||||||
|
|
||||||
|
>>> cur.execute("INSERT INTO %s VALUES (%s)", ('numbers', 10)) # WRONG
|
||||||
|
>>> cur.execute( # correct
|
||||||
|
... SQL("INSERT INTO {} VALUES (%s)").format(Identifier('numbers')),
|
||||||
|
... (10,))
|
||||||
|
|
||||||
|
|
||||||
.. index:: Security, SQL injection
|
.. index:: Security, SQL injection
|
||||||
|
@ -792,7 +819,9 @@ lifetime extends well after `~connection.commit()`, calling
|
||||||
It is also possible to use a named cursor to consume a cursor created
|
It is also possible to use a named cursor to consume a cursor created
|
||||||
in some other way than using the |DECLARE| executed by
|
in some other way than using the |DECLARE| executed by
|
||||||
`~cursor.execute()`. For example, you may have a PL/pgSQL function
|
`~cursor.execute()`. For example, you may have a PL/pgSQL function
|
||||||
returning a cursor::
|
returning a cursor:
|
||||||
|
|
||||||
|
.. code-block:: postgres
|
||||||
|
|
||||||
CREATE FUNCTION reffunc(refcursor) RETURNS refcursor AS $$
|
CREATE FUNCTION reffunc(refcursor) RETURNS refcursor AS $$
|
||||||
BEGIN
|
BEGIN
|
||||||
|
|
|
@ -363,12 +363,23 @@ class NamedTupleCursor(_cursor):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
except ImportError, _exc:
|
except ImportError as _exc:
|
||||||
def _make_nt(self):
|
def _make_nt(self):
|
||||||
raise self._exc
|
raise self._exc
|
||||||
else:
|
else:
|
||||||
def _make_nt(self, namedtuple=namedtuple):
|
def _make_nt(self, namedtuple=namedtuple):
|
||||||
return namedtuple("Record", [d[0] for d in self.description or ()])
|
def f(s):
|
||||||
|
# NOTE: Python 3 actually allows unicode chars in fields
|
||||||
|
s = _re.sub('[^a-zA-Z0-9_]', '_', s)
|
||||||
|
# Python identifier cannot start with numbers, namedtuple fields
|
||||||
|
# cannot start with underscore. So...
|
||||||
|
if _re.match('^[0-9_]', s):
|
||||||
|
s = 'f' + s
|
||||||
|
|
||||||
|
return s
|
||||||
|
|
||||||
|
return namedtuple(
|
||||||
|
"Record", [f(d[0]) for d in self.description or ()])
|
||||||
|
|
||||||
|
|
||||||
class LoggingConnection(_connection):
|
class LoggingConnection(_connection):
|
||||||
|
|
|
@ -100,7 +100,7 @@ _pydatetime_string_delta(pydatetimeObject *self)
|
||||||
|
|
||||||
char buffer[8];
|
char buffer[8];
|
||||||
int i;
|
int i;
|
||||||
int a = obj->microseconds;
|
int a = PyDateTime_DELTA_GET_MICROSECONDS(obj);
|
||||||
|
|
||||||
for (i=0; i < 6 ; i++) {
|
for (i=0; i < 6 ; i++) {
|
||||||
buffer[5-i] = '0' + (a % 10);
|
buffer[5-i] = '0' + (a % 10);
|
||||||
|
@ -109,7 +109,9 @@ _pydatetime_string_delta(pydatetimeObject *self)
|
||||||
buffer[6] = '\0';
|
buffer[6] = '\0';
|
||||||
|
|
||||||
return Bytes_FromFormat("'%d days %d.%s seconds'::interval",
|
return Bytes_FromFormat("'%d days %d.%s seconds'::interval",
|
||||||
obj->days, obj->seconds, buffer);
|
PyDateTime_DELTA_GET_DAYS(obj),
|
||||||
|
PyDateTime_DELTA_GET_SECONDS(obj),
|
||||||
|
buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
|
|
|
@ -67,6 +67,9 @@ const char *srv_state_guc[] = {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const int SRV_STATE_UNCHANGED = -1;
|
||||||
|
|
||||||
|
|
||||||
/* Return a new "string" from a char* from the database.
|
/* Return a new "string" from a char* from the database.
|
||||||
*
|
*
|
||||||
* On Py2 just get a string, on Py3 decode it in the connection codec.
|
* On Py2 just get a string, on Py3 decode it in the connection codec.
|
||||||
|
@ -1188,8 +1191,10 @@ conn_set_session(connectionObject *self, int autocommit,
|
||||||
int rv = -1;
|
int rv = -1;
|
||||||
PGresult *pgres = NULL;
|
PGresult *pgres = NULL;
|
||||||
char *error = NULL;
|
char *error = NULL;
|
||||||
|
int want_autocommit = autocommit == SRV_STATE_UNCHANGED ?
|
||||||
|
self->autocommit : autocommit;
|
||||||
|
|
||||||
if (deferrable != self->deferrable && self->server_version < 90100) {
|
if (deferrable != SRV_STATE_UNCHANGED && self->server_version < 90100) {
|
||||||
PyErr_SetString(ProgrammingError,
|
PyErr_SetString(ProgrammingError,
|
||||||
"the 'deferrable' setting is only available"
|
"the 'deferrable' setting is only available"
|
||||||
" from PostgreSQL 9.1");
|
" from PostgreSQL 9.1");
|
||||||
|
@ -1209,24 +1214,24 @@ conn_set_session(connectionObject *self, int autocommit,
|
||||||
Py_BEGIN_ALLOW_THREADS;
|
Py_BEGIN_ALLOW_THREADS;
|
||||||
pthread_mutex_lock(&self->lock);
|
pthread_mutex_lock(&self->lock);
|
||||||
|
|
||||||
if (autocommit) {
|
if (want_autocommit) {
|
||||||
/* we are in autocommit state, so no BEGIN will be issued:
|
/* we are or are going in autocommit state, so no BEGIN will be issued:
|
||||||
* configure the session with the characteristics requested */
|
* configure the session with the characteristics requested */
|
||||||
if (isolevel != self->isolevel) {
|
if (isolevel != SRV_STATE_UNCHANGED) {
|
||||||
if (0 > pq_set_guc_locked(self,
|
if (0 > pq_set_guc_locked(self,
|
||||||
"default_transaction_isolation", srv_isolevels[isolevel],
|
"default_transaction_isolation", srv_isolevels[isolevel],
|
||||||
&pgres, &error, &_save)) {
|
&pgres, &error, &_save)) {
|
||||||
goto endlock;
|
goto endlock;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (readonly != self->readonly) {
|
if (readonly != SRV_STATE_UNCHANGED) {
|
||||||
if (0 > pq_set_guc_locked(self,
|
if (0 > pq_set_guc_locked(self,
|
||||||
"default_transaction_read_only", srv_state_guc[readonly],
|
"default_transaction_read_only", srv_state_guc[readonly],
|
||||||
&pgres, &error, &_save)) {
|
&pgres, &error, &_save)) {
|
||||||
goto endlock;
|
goto endlock;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (deferrable != self->deferrable) {
|
if (deferrable != SRV_STATE_UNCHANGED) {
|
||||||
if (0 > pq_set_guc_locked(self,
|
if (0 > pq_set_guc_locked(self,
|
||||||
"default_transaction_deferrable", srv_state_guc[deferrable],
|
"default_transaction_deferrable", srv_state_guc[deferrable],
|
||||||
&pgres, &error, &_save)) {
|
&pgres, &error, &_save)) {
|
||||||
|
@ -1251,7 +1256,7 @@ conn_set_session(connectionObject *self, int autocommit,
|
||||||
goto endlock;
|
goto endlock;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (self->deferrable != STATE_DEFAULT) {
|
if (self->server_version >= 90100 && self->deferrable != STATE_DEFAULT) {
|
||||||
if (0 > pq_set_guc_locked(self,
|
if (0 > pq_set_guc_locked(self,
|
||||||
"default_transaction_deferrable", "default",
|
"default_transaction_deferrable", "default",
|
||||||
&pgres, &error, &_save)) {
|
&pgres, &error, &_save)) {
|
||||||
|
@ -1260,10 +1265,18 @@ conn_set_session(connectionObject *self, int autocommit,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self->autocommit = autocommit;
|
if (autocommit != SRV_STATE_UNCHANGED) {
|
||||||
self->isolevel = isolevel;
|
self->autocommit = autocommit;
|
||||||
self->readonly = readonly;
|
}
|
||||||
self->deferrable = deferrable;
|
if (isolevel != SRV_STATE_UNCHANGED) {
|
||||||
|
self->isolevel = isolevel;
|
||||||
|
}
|
||||||
|
if (readonly != SRV_STATE_UNCHANGED) {
|
||||||
|
self->readonly = readonly;
|
||||||
|
}
|
||||||
|
if (deferrable != SRV_STATE_UNCHANGED) {
|
||||||
|
self->deferrable = deferrable;
|
||||||
|
}
|
||||||
rv = 0;
|
rv = 0;
|
||||||
|
|
||||||
endlock:
|
endlock:
|
||||||
|
|
|
@ -39,6 +39,7 @@
|
||||||
extern HIDDEN const char *srv_isolevels[];
|
extern HIDDEN const char *srv_isolevels[];
|
||||||
extern HIDDEN const char *srv_readonly[];
|
extern HIDDEN const char *srv_readonly[];
|
||||||
extern HIDDEN const char *srv_deferrable[];
|
extern HIDDEN const char *srv_deferrable[];
|
||||||
|
extern HIDDEN const int SRV_STATE_UNCHANGED;
|
||||||
|
|
||||||
/** DBAPI methods **/
|
/** DBAPI methods **/
|
||||||
|
|
||||||
|
@ -561,10 +562,10 @@ psyco_conn_set_session(connectionObject *self, PyObject *args, PyObject *kwargs)
|
||||||
PyObject *deferrable = Py_None;
|
PyObject *deferrable = Py_None;
|
||||||
PyObject *autocommit = Py_None;
|
PyObject *autocommit = Py_None;
|
||||||
|
|
||||||
int c_isolevel = self->isolevel;
|
int c_isolevel = SRV_STATE_UNCHANGED;
|
||||||
int c_readonly = self->readonly;
|
int c_readonly = SRV_STATE_UNCHANGED;
|
||||||
int c_deferrable = self->deferrable;
|
int c_deferrable = SRV_STATE_UNCHANGED;
|
||||||
int c_autocommit = self->autocommit;
|
int c_autocommit = SRV_STATE_UNCHANGED;
|
||||||
|
|
||||||
static char *kwlist[] =
|
static char *kwlist[] =
|
||||||
{"isolation_level", "readonly", "deferrable", "autocommit", NULL};
|
{"isolation_level", "readonly", "deferrable", "autocommit", NULL};
|
||||||
|
@ -637,7 +638,7 @@ psyco_conn_autocommit_set(connectionObject *self, PyObject *pyvalue)
|
||||||
if (!_psyco_set_session_check_setter_wrapper(self)) { return -1; }
|
if (!_psyco_set_session_check_setter_wrapper(self)) { return -1; }
|
||||||
if (-1 == (value = PyObject_IsTrue(pyvalue))) { return -1; }
|
if (-1 == (value = PyObject_IsTrue(pyvalue))) { return -1; }
|
||||||
if (0 > conn_set_session(self, value,
|
if (0 > conn_set_session(self, value,
|
||||||
self->isolevel, self->readonly, self->deferrable)) {
|
SRV_STATE_UNCHANGED, SRV_STATE_UNCHANGED, SRV_STATE_UNCHANGED)) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -668,8 +669,8 @@ psyco_conn_isolation_level_set(connectionObject *self, PyObject *pyvalue)
|
||||||
|
|
||||||
if (!_psyco_set_session_check_setter_wrapper(self)) { return -1; }
|
if (!_psyco_set_session_check_setter_wrapper(self)) { return -1; }
|
||||||
if (0 > (value = _psyco_conn_parse_isolevel(pyvalue))) { return -1; }
|
if (0 > (value = _psyco_conn_parse_isolevel(pyvalue))) { return -1; }
|
||||||
if (0 > conn_set_session(self, self->autocommit,
|
if (0 > conn_set_session(self, SRV_STATE_UNCHANGED,
|
||||||
value, self->readonly, self->deferrable)) {
|
value, SRV_STATE_UNCHANGED, SRV_STATE_UNCHANGED)) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -715,13 +716,13 @@ psyco_conn_set_isolation_level(connectionObject *self, PyObject *args)
|
||||||
|
|
||||||
if (level == 0) {
|
if (level == 0) {
|
||||||
if (0 > conn_set_session(self, 1,
|
if (0 > conn_set_session(self, 1,
|
||||||
self->isolevel, self->readonly, self->deferrable)) {
|
SRV_STATE_UNCHANGED, SRV_STATE_UNCHANGED, SRV_STATE_UNCHANGED)) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (0 > conn_set_session(self, 0,
|
if (0 > conn_set_session(self, 0,
|
||||||
level, self->readonly, self->deferrable)) {
|
level, SRV_STATE_UNCHANGED, SRV_STATE_UNCHANGED)) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -767,8 +768,8 @@ psyco_conn_readonly_set(connectionObject *self, PyObject *pyvalue)
|
||||||
|
|
||||||
if (!_psyco_set_session_check_setter_wrapper(self)) { return -1; }
|
if (!_psyco_set_session_check_setter_wrapper(self)) { return -1; }
|
||||||
if (0 > (value = _psyco_conn_parse_onoff(pyvalue))) { return -1; }
|
if (0 > (value = _psyco_conn_parse_onoff(pyvalue))) { return -1; }
|
||||||
if (0 > conn_set_session(self, self->autocommit,
|
if (0 > conn_set_session(self, SRV_STATE_UNCHANGED,
|
||||||
self->isolevel, value, self->deferrable)) {
|
SRV_STATE_UNCHANGED, value, SRV_STATE_UNCHANGED)) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -813,8 +814,8 @@ psyco_conn_deferrable_set(connectionObject *self, PyObject *pyvalue)
|
||||||
|
|
||||||
if (!_psyco_set_session_check_setter_wrapper(self)) { return -1; }
|
if (!_psyco_set_session_check_setter_wrapper(self)) { return -1; }
|
||||||
if (0 > (value = _psyco_conn_parse_onoff(pyvalue))) { return -1; }
|
if (0 > (value = _psyco_conn_parse_onoff(pyvalue))) { return -1; }
|
||||||
if (0 > conn_set_session(self, self->autocommit,
|
if (0 > conn_set_session(self, SRV_STATE_UNCHANGED,
|
||||||
self->isolevel, self->readonly, value)) {
|
SRV_STATE_UNCHANGED, SRV_STATE_UNCHANGED, value)) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -592,8 +592,6 @@ psyco_curs_mogrify(cursorObject *self, PyObject *args, PyObject *kwargs)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
EXC_IF_CURS_CLOSED(self);
|
|
||||||
|
|
||||||
return _psyco_curs_mogrify(self, operation, vars);
|
return _psyco_curs_mogrify(self, operation, vars);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -52,6 +52,10 @@
|
||||||
#include "win32_support.h"
|
#include "win32_support.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined(__sun) && defined(__SVR4)
|
||||||
|
#include "solaris_support.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
extern HIDDEN PyObject *psyco_DescriptionType;
|
extern HIDDEN PyObject *psyco_DescriptionType;
|
||||||
extern HIDDEN const char *srv_isolevels[];
|
extern HIDDEN const char *srv_isolevels[];
|
||||||
extern HIDDEN const char *srv_readonly[];
|
extern HIDDEN const char *srv_readonly[];
|
||||||
|
@ -1948,8 +1952,9 @@ pq_fetch(cursorObject *curs, int no_result)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Dprintf("pq_fetch: got tuples, discarding them");
|
Dprintf("pq_fetch: got tuples, discarding them");
|
||||||
|
/* TODO: is there any case in which PQntuples == PQcmdTuples? */
|
||||||
|
_read_rowcount(curs);
|
||||||
CLEARPGRES(curs->pgres);
|
CLEARPGRES(curs->pgres);
|
||||||
curs->rowcount = -1;
|
|
||||||
ex = 0;
|
ex = 0;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -93,6 +93,7 @@ typedef unsigned long Py_uhash_t;
|
||||||
#ifndef PyNumber_Int
|
#ifndef PyNumber_Int
|
||||||
#define PyNumber_Int PyNumber_Long
|
#define PyNumber_Int PyNumber_Long
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif /* PY_MAJOR_VERSION > 2 */
|
#endif /* PY_MAJOR_VERSION > 2 */
|
||||||
|
|
||||||
#if PY_MAJOR_VERSION < 3
|
#if PY_MAJOR_VERSION < 3
|
||||||
|
@ -128,6 +129,12 @@ typedef unsigned long Py_uhash_t;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef PyDateTime_DELTA_GET_DAYS
|
||||||
|
#define PyDateTime_DELTA_GET_DAYS(o) (((PyDateTime_Delta*)o)->days)
|
||||||
|
#define PyDateTime_DELTA_GET_SECONDS(o) (((PyDateTime_Delta*)o)->seconds)
|
||||||
|
#define PyDateTime_DELTA_GET_MICROSECONDS(o) (((PyDateTime_Delta*)o)->microseconds)
|
||||||
|
#endif
|
||||||
|
|
||||||
HIDDEN PyObject *Bytes_Format(PyObject *format, PyObject *args);
|
HIDDEN PyObject *Bytes_Format(PyObject *format, PyObject *args);
|
||||||
|
|
||||||
/* Mangle the module name into the name of the module init function */
|
/* Mangle the module name into the name of the module init function */
|
||||||
|
|
54
psycopg/solaris_support.c
Normal file
54
psycopg/solaris_support.c
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
/* solaris_support.c - emulate functions missing on Solaris
|
||||||
|
*
|
||||||
|
* Copyright (C) 2017 My Karlsson <mk@acc.umu.se>
|
||||||
|
*
|
||||||
|
* 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/solaris_support.h"
|
||||||
|
|
||||||
|
#if defined(__sun) && defined(__SVR4)
|
||||||
|
/* timeradd is missing on Solaris */
|
||||||
|
void
|
||||||
|
timeradd(struct timeval *a, struct timeval *b, struct timeval *c)
|
||||||
|
{
|
||||||
|
c->tv_sec = a->tv_sec + b->tv_sec;
|
||||||
|
c->tv_usec = a->tv_usec + b->tv_usec;
|
||||||
|
if (c->tv_usec >= 1000000) {
|
||||||
|
c->tv_usec -= 1000000;
|
||||||
|
c->tv_sec += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* timersub is missing on Solaris */
|
||||||
|
void
|
||||||
|
timersub(struct timeval *a, struct timeval *b, struct timeval *c)
|
||||||
|
{
|
||||||
|
c->tv_sec = a->tv_sec - b->tv_sec;
|
||||||
|
c->tv_usec = a->tv_usec - b->tv_usec;
|
||||||
|
if (c->tv_usec < 0) {
|
||||||
|
c->tv_usec += 1000000;
|
||||||
|
c->tv_sec -= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif /* defined(__sun) && defined(__SVR4) */
|
37
psycopg/solaris_support.h
Normal file
37
psycopg/solaris_support.h
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
/* solaris_support.h - definitions for solaris_support.c
|
||||||
|
*
|
||||||
|
* Copyright (C) 2017 My Karlsson <mk@acc.umu.se>
|
||||||
|
*
|
||||||
|
* 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_SOLARIS_SUPPORT_H
|
||||||
|
#define PSYCOPG_SOLARIS_SUPPORT_H
|
||||||
|
|
||||||
|
#include "psycopg/config.h"
|
||||||
|
|
||||||
|
#if defined(__sun) && defined(__SVR4)
|
||||||
|
#include <sys/time.h>
|
||||||
|
|
||||||
|
extern HIDDEN void timeradd(struct timeval *a, struct timeval *b, struct timeval *c);
|
||||||
|
extern HIDDEN void timersub(struct timeval *a, struct timeval *b, struct timeval *c);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* !defined(PSYCOPG_SOLARIS_SUPPORT_H) */
|
|
@ -15,7 +15,7 @@ static long int typecast_BINARY_types[] = {17, 0};
|
||||||
static long int typecast_ROWID_types[] = {26, 0};
|
static long int typecast_ROWID_types[] = {26, 0};
|
||||||
static long int typecast_LONGINTEGERARRAY_types[] = {1016, 0};
|
static long int typecast_LONGINTEGERARRAY_types[] = {1016, 0};
|
||||||
static long int typecast_INTEGERARRAY_types[] = {1005, 1006, 1007, 0};
|
static long int typecast_INTEGERARRAY_types[] = {1005, 1006, 1007, 0};
|
||||||
static long int typecast_FLOATARRAY_types[] = {1017, 1021, 1022, 0};
|
static long int typecast_FLOATARRAY_types[] = {1021, 1022, 0};
|
||||||
static long int typecast_DECIMALARRAY_types[] = {1231, 0};
|
static long int typecast_DECIMALARRAY_types[] = {1231, 0};
|
||||||
static long int typecast_UNICODEARRAY_types[] = {1002, 1003, 1009, 1014, 1015, 0};
|
static long int typecast_UNICODEARRAY_types[] = {1002, 1003, 1009, 1014, 1015, 0};
|
||||||
static long int typecast_STRINGARRAY_types[] = {1002, 1003, 1009, 1014, 1015, 0};
|
static long int typecast_STRINGARRAY_types[] = {1002, 1003, 1009, 1014, 1015, 0};
|
||||||
|
|
|
@ -9,10 +9,10 @@ To invalidate the cache, update this file and check it into git.
|
||||||
Currently used modules built in the cache:
|
Currently used modules built in the cache:
|
||||||
|
|
||||||
OpenSSL
|
OpenSSL
|
||||||
Version: 1.0.2l
|
Version: 1.0.2n
|
||||||
|
|
||||||
PostgreSQL
|
PostgreSQL
|
||||||
Version: 10.0
|
Version: 10.1
|
||||||
|
|
||||||
|
|
||||||
NOTE: to zap the cache manually you can also use:
|
NOTE: to zap the cache manually you can also use:
|
||||||
|
|
|
@ -27,3 +27,6 @@ static_libpq=0
|
||||||
|
|
||||||
# Add here eventual extra libraries required to link the module.
|
# Add here eventual extra libraries required to link the module.
|
||||||
libraries=
|
libraries=
|
||||||
|
|
||||||
|
[metadata]
|
||||||
|
license_file = LICENSE
|
||||||
|
|
60
setup.py
60
setup.py
|
@ -39,6 +39,7 @@ except ImportError:
|
||||||
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
|
||||||
|
from distutils.errors import CompileError
|
||||||
from distutils.util import get_platform
|
from distutils.util import get_platform
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -64,7 +65,7 @@ except ImportError:
|
||||||
# Take a look at http://www.python.org/dev/peps/pep-0440/
|
# Take a look at http://www.python.org/dev/peps/pep-0440/
|
||||||
# for a consistent versioning pattern.
|
# for a consistent versioning pattern.
|
||||||
|
|
||||||
PSYCOPG_VERSION = '2.7.4.dev0'
|
PSYCOPG_VERSION = '2.7.4'
|
||||||
|
|
||||||
|
|
||||||
# note: if you are changing the list of supported Python version please fix
|
# note: if you are changing the list of supported Python version please fix
|
||||||
|
@ -75,6 +76,7 @@ Intended Audience :: Developers
|
||||||
License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)
|
License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)
|
||||||
License :: OSI Approved :: Zope Public License
|
License :: OSI Approved :: Zope Public License
|
||||||
Programming Language :: Python
|
Programming Language :: Python
|
||||||
|
Programming Language :: Python :: 2
|
||||||
Programming Language :: Python :: 2.6
|
Programming Language :: Python :: 2.6
|
||||||
Programming Language :: Python :: 2.7
|
Programming Language :: Python :: 2.7
|
||||||
Programming Language :: Python :: 3
|
Programming Language :: Python :: 3
|
||||||
|
@ -83,6 +85,7 @@ Programming Language :: Python :: 3.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 :: Implementation :: CPython
|
||||||
Programming Language :: C
|
Programming Language :: C
|
||||||
Programming Language :: SQL
|
Programming Language :: SQL
|
||||||
Topic :: Database
|
Topic :: Database
|
||||||
|
@ -105,15 +108,23 @@ class PostgresConfig:
|
||||||
if not self.pg_config_exe:
|
if not self.pg_config_exe:
|
||||||
self.pg_config_exe = self.autodetect_pg_config_path()
|
self.pg_config_exe = self.autodetect_pg_config_path()
|
||||||
if self.pg_config_exe is None:
|
if self.pg_config_exe is None:
|
||||||
sys.stderr.write("""\
|
sys.stderr.write("""
|
||||||
Error: pg_config executable not found.
|
Error: pg_config executable not found.
|
||||||
|
|
||||||
Please add the directory containing pg_config to the PATH
|
pg_config is required to build psycopg2 from source. Please add the directory
|
||||||
or specify the full executable path with the option:
|
containing pg_config to the $PATH or specify the full executable path with the
|
||||||
|
option:
|
||||||
|
|
||||||
python setup.py build_ext --pg-config /path/to/pg_config build ...
|
python setup.py build_ext --pg-config /path/to/pg_config build ...
|
||||||
|
|
||||||
or with the pg_config option in 'setup.cfg'.
|
or with the pg_config option in 'setup.cfg'.
|
||||||
|
|
||||||
|
If you prefer to avoid building psycopg2 from source, please install the PyPI
|
||||||
|
'psycopg2-binary' package instead.
|
||||||
|
|
||||||
|
For further information please check the 'doc/src/install.rst' file (also at
|
||||||
|
<http://initd.org/psycopg/docs/install.html>).
|
||||||
|
|
||||||
""")
|
""")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
@ -195,8 +206,7 @@ or with the pg_config option in 'setup.cfg'.
|
||||||
return None
|
return None
|
||||||
|
|
||||||
pg_first_inst_key = winreg.OpenKey(reg,
|
pg_first_inst_key = winreg.OpenKey(reg,
|
||||||
'SOFTWARE\\PostgreSQL\\Installations\\'
|
'SOFTWARE\\PostgreSQL\\Installations\\' + first_sub_key_name)
|
||||||
+ first_sub_key_name)
|
|
||||||
try:
|
try:
|
||||||
pg_inst_base_dir = winreg.QueryValueEx(
|
pg_inst_base_dir = winreg.QueryValueEx(
|
||||||
pg_first_inst_key, 'Base Directory')[0]
|
pg_first_inst_key, 'Base Directory')[0]
|
||||||
|
@ -289,8 +299,37 @@ class psycopg_build_ext(build_ext):
|
||||||
else:
|
else:
|
||||||
return build_ext.get_export_symbols(self, extension)
|
return build_ext.get_export_symbols(self, extension)
|
||||||
|
|
||||||
|
built_files = 0
|
||||||
|
|
||||||
def build_extension(self, extension):
|
def build_extension(self, extension):
|
||||||
build_ext.build_extension(self, extension)
|
# Count files compiled to print the binary blurb only if the first fails
|
||||||
|
compile_orig = getattr(self.compiler, '_compile', None)
|
||||||
|
if compile_orig is not None:
|
||||||
|
def _compile(*args, **kwargs):
|
||||||
|
rv = compile_orig(*args, **kwargs)
|
||||||
|
psycopg_build_ext.built_files += 1
|
||||||
|
return rv
|
||||||
|
|
||||||
|
self.compiler._compile = _compile
|
||||||
|
|
||||||
|
try:
|
||||||
|
build_ext.build_extension(self, extension)
|
||||||
|
psycopg_build_ext.built_files += 1
|
||||||
|
except CompileError:
|
||||||
|
if self.built_files == 0:
|
||||||
|
sys.stderr.write("""
|
||||||
|
It appears you are missing some prerequisite to build the package from source.
|
||||||
|
|
||||||
|
You may install a binary package by installing 'psycopg2-binary' from PyPI.
|
||||||
|
If you want to install psycopg2 from source, please install the packages
|
||||||
|
required for the build and try again.
|
||||||
|
|
||||||
|
For further information please check the 'doc/src/install.rst' file (also at
|
||||||
|
<http://initd.org/psycopg/docs/install.html>).
|
||||||
|
|
||||||
|
""")
|
||||||
|
raise
|
||||||
|
|
||||||
sysVer = sys.version_info[:2]
|
sysVer = sys.version_info[:2]
|
||||||
|
|
||||||
# For Python versions that use MSVC compiler 2008, re-insert the
|
# For Python versions that use MSVC compiler 2008, re-insert the
|
||||||
|
@ -420,11 +459,14 @@ class psycopg_build_ext(build_ext):
|
||||||
m = verre.match(pgversion)
|
m = verre.match(pgversion)
|
||||||
if m:
|
if m:
|
||||||
pgmajor, pgminor, pgpatch = m.group(1, 2, 3)
|
pgmajor, pgminor, pgpatch = m.group(1, 2, 3)
|
||||||
|
# Postgres >= 10 doesn't have pgminor anymore.
|
||||||
|
pgmajor = int(pgmajor)
|
||||||
|
if pgmajor >= 10:
|
||||||
|
pgminor, pgpatch = None, pgminor
|
||||||
if pgminor is None or not pgminor.isdigit():
|
if pgminor is None or not pgminor.isdigit():
|
||||||
pgminor = 0
|
pgminor = 0
|
||||||
if pgpatch is None or not pgpatch.isdigit():
|
if pgpatch is None or not pgpatch.isdigit():
|
||||||
pgpatch = 0
|
pgpatch = 0
|
||||||
pgmajor = int(pgmajor)
|
|
||||||
pgminor = int(pgminor)
|
pgminor = int(pgminor)
|
||||||
pgpatch = int(pgpatch)
|
pgpatch = int(pgpatch)
|
||||||
else:
|
else:
|
||||||
|
@ -480,7 +522,7 @@ data_files = []
|
||||||
sources = [
|
sources = [
|
||||||
'psycopgmodule.c',
|
'psycopgmodule.c',
|
||||||
'green.c', 'pqpath.c', 'utils.c', 'bytes_format.c',
|
'green.c', 'pqpath.c', 'utils.c', 'bytes_format.c',
|
||||||
'libpq_support.c', 'win32_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',
|
||||||
|
|
|
@ -65,7 +65,7 @@ def test_suite():
|
||||||
import psycopg2
|
import psycopg2
|
||||||
try:
|
try:
|
||||||
cnn = psycopg2.connect(dsn)
|
cnn = psycopg2.connect(dsn)
|
||||||
except Exception, e:
|
except Exception as e:
|
||||||
print "Failed connection to test db:", e.__class__.__name__, e
|
print "Failed connection to test db:", e.__class__.__name__, e
|
||||||
print "Please set env vars 'PSYCOPG2_TESTDB*' to valid values."
|
print "Please set env vars 'PSYCOPG2_TESTDB*' to valid values."
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
|
@ -444,7 +444,7 @@ class AsyncTests(ConnectingTestCase):
|
||||||
try:
|
try:
|
||||||
cnn = psycopg2.connect('dbname=thisdatabasedoesntexist', async_=True)
|
cnn = psycopg2.connect('dbname=thisdatabasedoesntexist', async_=True)
|
||||||
self.wait(cnn)
|
self.wait(cnn)
|
||||||
except psycopg2.Error, e:
|
except psycopg2.Error as e:
|
||||||
self.assertNotEqual(str(e), "asynchronous connection failed",
|
self.assertNotEqual(str(e), "asynchronous connection failed",
|
||||||
"connection error reason lost")
|
"connection error reason lost")
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -81,7 +81,7 @@ class AsyncTests(ConnectingTestCase):
|
||||||
try:
|
try:
|
||||||
cnn = psycopg2.connect('dbname=thisdatabasedoesntexist', async=True)
|
cnn = psycopg2.connect('dbname=thisdatabasedoesntexist', async=True)
|
||||||
self.wait(cnn)
|
self.wait(cnn)
|
||||||
except psycopg2.Error, e:
|
except psycopg2.Error as e:
|
||||||
self.assertNotEqual(str(e), "asynchronous connection failed",
|
self.assertNotEqual(str(e), "asynchronous connection failed",
|
||||||
"connection error reason lost")
|
"connection error reason lost")
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -63,7 +63,7 @@ class CancelTests(ConnectingTestCase):
|
||||||
conn.rollback()
|
conn.rollback()
|
||||||
cur.execute("select 1")
|
cur.execute("select 1")
|
||||||
self.assertEqual(cur.fetchall(), [(1, )])
|
self.assertEqual(cur.fetchall(), [(1, )])
|
||||||
except Exception, e:
|
except Exception as e:
|
||||||
errors.append(e)
|
errors.append(e)
|
||||||
raise
|
raise
|
||||||
|
|
||||||
|
@ -71,7 +71,7 @@ class CancelTests(ConnectingTestCase):
|
||||||
cur = conn.cursor()
|
cur = conn.cursor()
|
||||||
try:
|
try:
|
||||||
conn.cancel()
|
conn.cancel()
|
||||||
except Exception, e:
|
except Exception as e:
|
||||||
errors.append(e)
|
errors.append(e)
|
||||||
raise
|
raise
|
||||||
del cur
|
del cur
|
||||||
|
|
|
@ -358,7 +358,7 @@ class ParseDsnTestCase(ConnectingTestCase):
|
||||||
try:
|
try:
|
||||||
# unterminated quote after dbname:
|
# unterminated quote after dbname:
|
||||||
ext.parse_dsn("dbname='test 2 user=tester password=secret")
|
ext.parse_dsn("dbname='test 2 user=tester password=secret")
|
||||||
except ProgrammingError, e:
|
except ProgrammingError as e:
|
||||||
raised = True
|
raised = True
|
||||||
self.assertTrue(str(e).find('secret') < 0,
|
self.assertTrue(str(e).find('secret') < 0,
|
||||||
"DSN was not exposed in error message")
|
"DSN was not exposed in error message")
|
||||||
|
@ -376,7 +376,7 @@ class ParseDsnTestCase(ConnectingTestCase):
|
||||||
try:
|
try:
|
||||||
# extra '=' after port value
|
# extra '=' after port value
|
||||||
ext.parse_dsn(dsn='postgresql://tester:secret@/test?port=1111=x')
|
ext.parse_dsn(dsn='postgresql://tester:secret@/test?port=1111=x')
|
||||||
except psycopg2.ProgrammingError, e:
|
except psycopg2.ProgrammingError as e:
|
||||||
raised = True
|
raised = True
|
||||||
self.assertTrue(str(e).find('secret') < 0,
|
self.assertTrue(str(e).find('secret') < 0,
|
||||||
"URI was not exposed in error message")
|
"URI was not exposed in error message")
|
||||||
|
@ -1382,6 +1382,16 @@ class TransactionControlTests(ConnectingTestCase):
|
||||||
cur.execute("SHOW default_transaction_read_only;")
|
cur.execute("SHOW default_transaction_read_only;")
|
||||||
self.assertEqual(cur.fetchone()[0], 'off')
|
self.assertEqual(cur.fetchone()[0], 'off')
|
||||||
|
|
||||||
|
def test_idempotence_check(self):
|
||||||
|
self.conn.autocommit = False
|
||||||
|
self.conn.readonly = True
|
||||||
|
self.conn.autocommit = True
|
||||||
|
self.conn.readonly = True
|
||||||
|
|
||||||
|
cur = self.conn.cursor()
|
||||||
|
cur.execute("SHOW transaction_read_only")
|
||||||
|
self.assertEqual(cur.fetchone()[0], 'on')
|
||||||
|
|
||||||
|
|
||||||
class AutocommitTests(ConnectingTestCase):
|
class AutocommitTests(ConnectingTestCase):
|
||||||
def test_closed(self):
|
def test_closed(self):
|
||||||
|
@ -1540,9 +1550,13 @@ import os
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
import signal
|
import signal
|
||||||
|
import warnings
|
||||||
import threading
|
import threading
|
||||||
|
|
||||||
import psycopg2
|
# ignore wheel deprecation warning
|
||||||
|
with warnings.catch_warnings():
|
||||||
|
warnings.simplefilter('ignore')
|
||||||
|
import psycopg2
|
||||||
|
|
||||||
def handle_sigabort(sig, frame):
|
def handle_sigabort(sig, frame):
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
|
@ -364,7 +364,7 @@ conn.close()
|
||||||
# curs.copy_from, BrokenRead(), "tcopy")
|
# curs.copy_from, BrokenRead(), "tcopy")
|
||||||
try:
|
try:
|
||||||
curs.copy_from(BrokenRead(), "tcopy")
|
curs.copy_from(BrokenRead(), "tcopy")
|
||||||
except Exception, e:
|
except Exception as e:
|
||||||
self.assert_('ZeroDivisionError' in str(e))
|
self.assert_('ZeroDivisionError' in str(e))
|
||||||
|
|
||||||
def test_copy_to_propagate_error(self):
|
def test_copy_to_propagate_error(self):
|
||||||
|
|
|
@ -121,6 +121,12 @@ class CursorTests(ConnectingTestCase):
|
||||||
nref2 = sys.getrefcount(foo)
|
nref2 = sys.getrefcount(foo)
|
||||||
self.assertEqual(nref1, nref2)
|
self.assertEqual(nref1, nref2)
|
||||||
|
|
||||||
|
def test_modify_closed(self):
|
||||||
|
cur = self.conn.cursor()
|
||||||
|
cur.close()
|
||||||
|
sql = cur.mogrify("select %s", (10,))
|
||||||
|
self.assertEqual(sql, b"select 10")
|
||||||
|
|
||||||
def test_bad_placeholder(self):
|
def test_bad_placeholder(self):
|
||||||
cur = self.conn.cursor()
|
cur = self.conn.cursor()
|
||||||
self.assertRaises(psycopg2.ProgrammingError,
|
self.assertRaises(psycopg2.ProgrammingError,
|
||||||
|
@ -592,6 +598,20 @@ class CursorTests(ConnectingTestCase):
|
||||||
|
|
||||||
self.assertEqual(victim_conn.closed, 2)
|
self.assertEqual(victim_conn.closed, 2)
|
||||||
|
|
||||||
|
@skip_before_postgres(8, 2)
|
||||||
|
def test_rowcount_on_executemany_returning(self):
|
||||||
|
cur = self.conn.cursor()
|
||||||
|
cur.execute("create table execmany(id serial primary key, data int)")
|
||||||
|
cur.executemany(
|
||||||
|
"insert into execmany (data) values (%s)",
|
||||||
|
[(i,) for i in range(4)])
|
||||||
|
self.assertEqual(cur.rowcount, 4)
|
||||||
|
|
||||||
|
cur.executemany(
|
||||||
|
"insert into execmany (data) values (%s) returning data",
|
||||||
|
[(i,) for i in range(5)])
|
||||||
|
self.assertEqual(cur.rowcount, 5)
|
||||||
|
|
||||||
|
|
||||||
def test_suite():
|
def test_suite():
|
||||||
return unittest.TestLoader().loadTestsFromName(__name__)
|
return unittest.TestLoader().loadTestsFromName(__name__)
|
||||||
|
|
|
@ -638,7 +638,8 @@ class FromTicksTestCase(unittest.TestCase):
|
||||||
def test_date_value_error_sec_59_99(self):
|
def test_date_value_error_sec_59_99(self):
|
||||||
from datetime import date
|
from datetime import date
|
||||||
s = psycopg2.DateFromTicks(1273173119.99992)
|
s = psycopg2.DateFromTicks(1273173119.99992)
|
||||||
self.assertEqual(s.adapted, date(2010, 5, 6))
|
# The returned date is local
|
||||||
|
self.assert_(s.adapted in [date(2010, 5, 6), date(2010, 5, 7)])
|
||||||
|
|
||||||
def test_time_value_error_sec_59_99(self):
|
def test_time_value_error_sec_59_99(self):
|
||||||
from datetime import time
|
from datetime import time
|
||||||
|
|
|
@ -48,7 +48,7 @@ class ErrocodeTests(ConnectingTestCase):
|
||||||
def f(pg_code='40001'):
|
def f(pg_code='40001'):
|
||||||
try:
|
try:
|
||||||
errorcodes.lookup(pg_code)
|
errorcodes.lookup(pg_code)
|
||||||
except Exception, e:
|
except Exception as e:
|
||||||
errs.append(e)
|
errs.append(e)
|
||||||
|
|
||||||
for __ in xrange(MAX_CYCLES):
|
for __ in xrange(MAX_CYCLES):
|
||||||
|
|
|
@ -382,6 +382,15 @@ class NamedTupleCursorTest(ConnectingTestCase):
|
||||||
curs.execute("update nttest set s = s")
|
curs.execute("update nttest set s = s")
|
||||||
self.assertRaises(psycopg2.ProgrammingError, curs.fetchall)
|
self.assertRaises(psycopg2.ProgrammingError, curs.fetchall)
|
||||||
|
|
||||||
|
@skip_if_no_namedtuple
|
||||||
|
def test_bad_col_names(self):
|
||||||
|
curs = self.conn.cursor()
|
||||||
|
curs.execute('select 1 as "foo.bar_baz", 2 as "?column?", 3 as "3"')
|
||||||
|
rv = curs.fetchone()
|
||||||
|
self.assertEqual(rv.foo_bar_baz, 1)
|
||||||
|
self.assertEqual(rv.f_column_, 2)
|
||||||
|
self.assertEqual(rv.f3, 3)
|
||||||
|
|
||||||
@skip_if_no_namedtuple
|
@skip_if_no_namedtuple
|
||||||
def test_minimal_generation(self):
|
def test_minimal_generation(self):
|
||||||
# Instrument the class to verify it gets called the minimum number of times.
|
# Instrument the class to verify it gets called the minimum number of times.
|
||||||
|
|
|
@ -152,7 +152,7 @@ class ExceptionsTestCase(ConnectingTestCase):
|
||||||
cur = self.conn.cursor()
|
cur = self.conn.cursor()
|
||||||
try:
|
try:
|
||||||
cur.execute("select * from nonexist")
|
cur.execute("select * from nonexist")
|
||||||
except psycopg2.Error, exc:
|
except psycopg2.Error as exc:
|
||||||
e = exc
|
e = exc
|
||||||
|
|
||||||
self.assertEqual(e.pgcode, '42P01')
|
self.assertEqual(e.pgcode, '42P01')
|
||||||
|
@ -163,7 +163,7 @@ class ExceptionsTestCase(ConnectingTestCase):
|
||||||
cur = self.conn.cursor()
|
cur = self.conn.cursor()
|
||||||
try:
|
try:
|
||||||
cur.execute("select * from nonexist")
|
cur.execute("select * from nonexist")
|
||||||
except psycopg2.Error, exc:
|
except psycopg2.Error as exc:
|
||||||
e = exc
|
e = exc
|
||||||
|
|
||||||
diag = e.diag
|
diag = e.diag
|
||||||
|
@ -182,7 +182,7 @@ class ExceptionsTestCase(ConnectingTestCase):
|
||||||
cur = self.conn.cursor()
|
cur = self.conn.cursor()
|
||||||
try:
|
try:
|
||||||
cur.execute("select * from nonexist")
|
cur.execute("select * from nonexist")
|
||||||
except psycopg2.Error, exc:
|
except psycopg2.Error as exc:
|
||||||
e = exc
|
e = exc
|
||||||
|
|
||||||
self.assertEqual(e.diag.sqlstate, '42P01')
|
self.assertEqual(e.diag.sqlstate, '42P01')
|
||||||
|
@ -196,7 +196,7 @@ class ExceptionsTestCase(ConnectingTestCase):
|
||||||
cur = self.conn.cursor()
|
cur = self.conn.cursor()
|
||||||
try:
|
try:
|
||||||
cur.execute("select * from nonexist")
|
cur.execute("select * from nonexist")
|
||||||
except psycopg2.Error, exc:
|
except psycopg2.Error as exc:
|
||||||
return cur, exc
|
return cur, exc
|
||||||
|
|
||||||
cur, e = tmp()
|
cur, e = tmp()
|
||||||
|
@ -221,7 +221,7 @@ class ExceptionsTestCase(ConnectingTestCase):
|
||||||
cur = self.conn.cursor()
|
cur = self.conn.cursor()
|
||||||
try:
|
try:
|
||||||
cur.copy_to(f, 'nonexist')
|
cur.copy_to(f, 'nonexist')
|
||||||
except psycopg2.Error, exc:
|
except psycopg2.Error as exc:
|
||||||
diag = exc.diag
|
diag = exc.diag
|
||||||
|
|
||||||
self.assertEqual(diag.sqlstate, '42P01')
|
self.assertEqual(diag.sqlstate, '42P01')
|
||||||
|
@ -230,14 +230,14 @@ class ExceptionsTestCase(ConnectingTestCase):
|
||||||
cur = self.conn.cursor()
|
cur = self.conn.cursor()
|
||||||
try:
|
try:
|
||||||
cur.execute("l'acqua e' poca e 'a papera nun galleggia")
|
cur.execute("l'acqua e' poca e 'a papera nun galleggia")
|
||||||
except Exception, exc:
|
except Exception as exc:
|
||||||
diag1 = exc.diag
|
diag1 = exc.diag
|
||||||
|
|
||||||
self.conn.rollback()
|
self.conn.rollback()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
cur.execute("select level from water where ducks > 1")
|
cur.execute("select level from water where ducks > 1")
|
||||||
except psycopg2.Error, exc:
|
except psycopg2.Error as exc:
|
||||||
diag2 = exc.diag
|
diag2 = exc.diag
|
||||||
|
|
||||||
self.assertEqual(diag1.sqlstate, '42601')
|
self.assertEqual(diag1.sqlstate, '42601')
|
||||||
|
@ -254,7 +254,7 @@ class ExceptionsTestCase(ConnectingTestCase):
|
||||||
cur.execute("insert into test_deferred values (1,2)")
|
cur.execute("insert into test_deferred values (1,2)")
|
||||||
try:
|
try:
|
||||||
self.conn.commit()
|
self.conn.commit()
|
||||||
except psycopg2.Error, exc:
|
except psycopg2.Error as exc:
|
||||||
e = exc
|
e = exc
|
||||||
self.assertEqual(e.diag.sqlstate, '23503')
|
self.assertEqual(e.diag.sqlstate, '23503')
|
||||||
|
|
||||||
|
@ -267,7 +267,7 @@ class ExceptionsTestCase(ConnectingTestCase):
|
||||||
)""")
|
)""")
|
||||||
try:
|
try:
|
||||||
cur.execute("insert into test_exc values(2)")
|
cur.execute("insert into test_exc values(2)")
|
||||||
except psycopg2.Error, exc:
|
except psycopg2.Error as exc:
|
||||||
e = exc
|
e = exc
|
||||||
self.assertEqual(e.pgcode, '23514')
|
self.assertEqual(e.pgcode, '23514')
|
||||||
self.assertEqual(e.diag.schema_name[:7], "pg_temp")
|
self.assertEqual(e.diag.schema_name[:7], "pg_temp")
|
||||||
|
@ -282,7 +282,7 @@ class ExceptionsTestCase(ConnectingTestCase):
|
||||||
cur = self.conn.cursor()
|
cur = self.conn.cursor()
|
||||||
try:
|
try:
|
||||||
cur.execute("select * from nonexist")
|
cur.execute("select * from nonexist")
|
||||||
except psycopg2.Error, exc:
|
except psycopg2.Error as exc:
|
||||||
e = exc
|
e = exc
|
||||||
|
|
||||||
e1 = pickle.loads(pickle.dumps(e))
|
e1 = pickle.loads(pickle.dumps(e))
|
||||||
|
@ -297,7 +297,7 @@ class ExceptionsTestCase(ConnectingTestCase):
|
||||||
import pickle
|
import pickle
|
||||||
try:
|
try:
|
||||||
psycopg2.connect('dbname=nosuchdatabasemate')
|
psycopg2.connect('dbname=nosuchdatabasemate')
|
||||||
except psycopg2.Error, exc:
|
except psycopg2.Error as exc:
|
||||||
e = exc
|
e = exc
|
||||||
|
|
||||||
e1 = pickle.loads(pickle.dumps(e))
|
e1 = pickle.loads(pickle.dumps(e))
|
||||||
|
|
|
@ -145,7 +145,7 @@ class DeadlockSerializationTests(ConnectingTestCase):
|
||||||
step1.set()
|
step1.set()
|
||||||
step2.wait()
|
step2.wait()
|
||||||
curs.execute("LOCK table2 IN ACCESS EXCLUSIVE MODE")
|
curs.execute("LOCK table2 IN ACCESS EXCLUSIVE MODE")
|
||||||
except psycopg2.DatabaseError, exc:
|
except psycopg2.DatabaseError as exc:
|
||||||
self.thread1_error = exc
|
self.thread1_error = exc
|
||||||
step1.set()
|
step1.set()
|
||||||
conn.close()
|
conn.close()
|
||||||
|
@ -158,7 +158,7 @@ class DeadlockSerializationTests(ConnectingTestCase):
|
||||||
curs.execute("LOCK table2 IN ACCESS EXCLUSIVE MODE")
|
curs.execute("LOCK table2 IN ACCESS EXCLUSIVE MODE")
|
||||||
step2.set()
|
step2.set()
|
||||||
curs.execute("LOCK table1 IN ACCESS EXCLUSIVE MODE")
|
curs.execute("LOCK table1 IN ACCESS EXCLUSIVE MODE")
|
||||||
except psycopg2.DatabaseError, exc:
|
except psycopg2.DatabaseError as exc:
|
||||||
self.thread2_error = exc
|
self.thread2_error = exc
|
||||||
step2.set()
|
step2.set()
|
||||||
conn.close()
|
conn.close()
|
||||||
|
@ -195,7 +195,7 @@ class DeadlockSerializationTests(ConnectingTestCase):
|
||||||
step2.wait()
|
step2.wait()
|
||||||
curs.execute("UPDATE table1 SET name='task1' WHERE id = 1")
|
curs.execute("UPDATE table1 SET name='task1' WHERE id = 1")
|
||||||
conn.commit()
|
conn.commit()
|
||||||
except psycopg2.DatabaseError, exc:
|
except psycopg2.DatabaseError as exc:
|
||||||
self.thread1_error = exc
|
self.thread1_error = exc
|
||||||
step1.set()
|
step1.set()
|
||||||
conn.close()
|
conn.close()
|
||||||
|
@ -207,7 +207,7 @@ class DeadlockSerializationTests(ConnectingTestCase):
|
||||||
step1.wait()
|
step1.wait()
|
||||||
curs.execute("UPDATE table1 SET name='task2' WHERE id = 1")
|
curs.execute("UPDATE table1 SET name='task2' WHERE id = 1")
|
||||||
conn.commit()
|
conn.commit()
|
||||||
except psycopg2.DatabaseError, exc:
|
except psycopg2.DatabaseError as exc:
|
||||||
self.thread2_error = exc
|
self.thread2_error = exc
|
||||||
step2.set()
|
step2.set()
|
||||||
conn.close()
|
conn.close()
|
||||||
|
|
|
@ -443,7 +443,7 @@ class ByteaParserTest(unittest.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
try:
|
try:
|
||||||
self._cast = self._import_cast()
|
self._cast = self._import_cast()
|
||||||
except Exception, e:
|
except Exception as e:
|
||||||
self._cast = None
|
self._cast = None
|
||||||
self._exc = e
|
self._exc = e
|
||||||
|
|
||||||
|
|
|
@ -113,9 +113,14 @@ class TypesExtrasTests(ConnectingTestCase):
|
||||||
psycopg2.extensions.adapt, Foo(), ext.ISQLQuote, None)
|
psycopg2.extensions.adapt, Foo(), ext.ISQLQuote, None)
|
||||||
try:
|
try:
|
||||||
psycopg2.extensions.adapt(Foo(), ext.ISQLQuote, None)
|
psycopg2.extensions.adapt(Foo(), ext.ISQLQuote, None)
|
||||||
except psycopg2.ProgrammingError, err:
|
except psycopg2.ProgrammingError as err:
|
||||||
self.failUnless(str(err) == "can't adapt type 'Foo'")
|
self.failUnless(str(err) == "can't adapt type 'Foo'")
|
||||||
|
|
||||||
|
def test_point_array(self):
|
||||||
|
# make sure a point array is never casted to a float array,
|
||||||
|
# see https://github.com/psycopg/psycopg2/issues/613
|
||||||
|
s = self.execute("""SELECT '{"(1,2)","(3,4)"}' AS foo""")
|
||||||
|
self.failUnless(s == """{"(1,2)","(3,4)"}""")
|
||||||
|
|
||||||
def skip_if_no_hstore(f):
|
def skip_if_no_hstore(f):
|
||||||
@wraps(f)
|
@wraps(f)
|
||||||
|
|
|
@ -212,7 +212,7 @@ class WithCursorTestCase(WithTestCase):
|
||||||
with conn.cursor('named') as cur:
|
with conn.cursor('named') as cur:
|
||||||
cur.execute("select 1/0")
|
cur.execute("select 1/0")
|
||||||
cur.fetchone()
|
cur.fetchone()
|
||||||
except psycopg2.DataError, e:
|
except psycopg2.DataError as e:
|
||||||
self.assertEqual(e.pgcode, '22012')
|
self.assertEqual(e.pgcode, '22012')
|
||||||
else:
|
else:
|
||||||
self.fail("where is my exception?")
|
self.fail("where is my exception?")
|
||||||
|
|
|
@ -116,7 +116,7 @@ class ConnectingTestCase(unittest.TestCase):
|
||||||
def connect(self, **kwargs):
|
def connect(self, **kwargs):
|
||||||
try:
|
try:
|
||||||
self._conns
|
self._conns
|
||||||
except AttributeError, e:
|
except AttributeError as e:
|
||||||
raise AttributeError(
|
raise AttributeError(
|
||||||
"%s (did you forget to call ConnectingTestCase.setUp()?)"
|
"%s (did you forget to call ConnectingTestCase.setUp()?)"
|
||||||
% e)
|
% e)
|
||||||
|
@ -149,7 +149,7 @@ class ConnectingTestCase(unittest.TestCase):
|
||||||
conn = self.connect(**kwargs)
|
conn = self.connect(**kwargs)
|
||||||
if conn.async_ == 1:
|
if conn.async_ == 1:
|
||||||
self.wait(conn)
|
self.wait(conn)
|
||||||
except psycopg2.OperationalError, e:
|
except psycopg2.OperationalError as e:
|
||||||
# If pgcode is not set it is a genuine connection error
|
# If pgcode is not set it is a genuine connection error
|
||||||
# Otherwise we tried to run some bad operation in the connection
|
# Otherwise we tried to run some bad operation in the connection
|
||||||
# (e.g. bug #482) and we'd rather know that.
|
# (e.g. bug #482) and we'd rather know that.
|
||||||
|
@ -388,7 +388,7 @@ def skip_if_no_superuser(f):
|
||||||
from psycopg2 import ProgrammingError
|
from psycopg2 import ProgrammingError
|
||||||
try:
|
try:
|
||||||
return f(self)
|
return f(self)
|
||||||
except ProgrammingError, e:
|
except ProgrammingError as e:
|
||||||
import psycopg2.errorcodes
|
import psycopg2.errorcodes
|
||||||
if e.pgcode == psycopg2.errorcodes.INSUFFICIENT_PRIVILEGE:
|
if e.pgcode == psycopg2.errorcodes.INSUFFICIENT_PRIVILEGE:
|
||||||
self.skipTest("skipped because not superuser")
|
self.skipTest("skipped because not superuser")
|
||||||
|
|
Loading…
Reference in New Issue
Block a user