Compare commits

...

51 Commits

Author SHA1 Message Date
Daniele Varrazzo
5afb2ce803 Version number bumped to 2.7.4 2018-02-06 01:24:25 +00:00
Daniele Varrazzo
41137e4e11 Autocommit shouldn't change deferrable on servers not supporting it
Regression on unsupported Postgres versions after fixing bug #580
2018-02-06 01:24:25 +00:00
Daniele Varrazzo
8dc26b86fa Report Python 2.6 wheels no more available
[skip ci]
2018-01-29 12:56:50 +00:00
Daniele Varrazzo
a4fcbfed0f Dropped recipe to install namedtuple on Python < 2.6
We don't support such Python versions anymore.
2018-01-29 03:10:56 +00:00
Daniele Varrazzo
e42e07258c Link psycopg2-binary package to PyPI in readme
Note: the package doesn't exist yet...
2018-01-29 03:09:27 +00:00
Daniele Varrazzo
6ba6cf27ed Merge branch 'openssl-1.0.2n' into maint_2_7 2018-01-29 02:57:14 +00:00
Daniele Varrazzo
485649a3d1 Merge branch 'namedtuple-invalid-identifiers' into maint_2_7 2018-01-29 02:56:53 +00:00
Daniele Varrazzo
e5da79fcc8 Convert fields names into valid Python identifiers in NamedTupleCursor
Close #211.
2018-01-29 02:41:44 +00:00
Daniele Varrazzo
a5762a4e4b Build using OpenSSL 1.0.2n 2018-01-29 01:58:37 +00:00
Daniele Varrazzo
8f6a26b911 Define openssl and libpq versions in vars in appveyor build 2018-01-29 01:57:17 +00:00
Daniele Varrazzo
4460166961 Build env vars on windows less verbose
Copied from psycopg2-wheels
2018-01-29 01:56:10 +00:00
Daniele Varrazzo
a84c9723b4 Merge branch 'separate-binary' into maint_2_7 2018-01-29 01:38:17 +00:00
Daniele Varrazzo
06ea808599 Mention new wheel packages in news file
Close #543.
2018-01-29 01:38:03 +00:00
Daniele Varrazzo
0a148dbe01 Silence warning on import failing a test 2018-01-29 00:51:05 +00:00
Daniele Varrazzo
5eb4185afc Dropped warning about unsafe cursor names
It was long made secure
2018-01-25 21:58:05 +00:00
Daniele Varrazzo
7e16a37476 Document the psycopg2-binary package 2018-01-16 18:28:27 +00:00
Daniele Varrazzo
76765aeb39 Print info about the binary package on build failed
The idea is to release a package 'psycopg2-binary' to allow installing
binary, and leave the psycopg2 package to be source only, to avoid
pushing the unreliability of the wheel pacakge by default (see issue #543).

Version number bumped to test with new packages.
2018-01-13 20:11:18 +00:00
Daniele Varrazzo
ac114d2188 Merge branch 'fix-idempotence-check' into maint_2_7 2018-01-11 02:23:06 +00:00
Daniele Varrazzo
8aaf8f1902 Fixed idempotence check changing connection characteristics 2018-01-11 02:08:27 +00:00
Daniele Varrazzo
ba50548b30 'key' docs in getconn() improved
Fix #569.
2018-01-11 00:16:49 +00:00
Daniele Varrazzo
f3685fe5ac Fixed test in asian time zones
Fix #652
2018-01-11 08:53:57 +09:00
Daniele Varrazzo
7be099bd80 Merge branch 'mogrify-on-closed-cursor' into maint_2_7 2018-01-11 08:53:52 +09:00
Daniele Varrazzo
bf58f3b194 Merge branch 'datetime-macro-accessors' into maint_2_7 2018-01-10 23:26:48 +00:00
Daniele Varrazzo
9c5bf36791 'cursor.mogrify()' can be called on closed cursors
Fix #579.
2018-01-10 23:26:11 +00:00
Daniele Varrazzo
1477482e59 Fixed pydatetime macros for Python 3.2 2018-01-10 23:12:01 +00:00
Daniele Varrazzo
54cd0c66af pypi3 fix noted in the news file 2018-01-10 22:54:03 +00:00
Daniele Varrazzo
b99c8ab2de Moved datatime compatibility macros with others 2018-01-10 22:53:56 +00:00
Glyph
632cfe0617 define a "polyfill" inline for python 2 compatibility 2018-01-10 22:53:43 +00:00
Glyph
ae227effe2 use accessor macros for pypy3 compatibility 2018-01-10 22:53:37 +00:00
Daniele Varrazzo
72ed62dc8c Fixed NEWS file entries
- 2.6.3 has not been released (yet). Fixes for bug #420, bug #462 were
  relased in 2.7.
- Added missing report for bug #489 fixed in 2.7.
2017-12-01 16:24:59 +00:00
Daniele Varrazzo
b8a9469f45 Merge branch 'bug-633' into maint_2_7 2017-11-29 15:38:32 +00:00
Daniele Varrazzo
a5fd594ea4 Collect rowcount in executemany even when discarding results
Closes #633.
2017-11-29 15:38:02 +00:00
Daniele Varrazzo
ad7fd52f30 Build and test packages with libpq 10.1 and OpenSSL 1.0.2m 2017-11-28 17:16:53 +00:00
Daniele Varrazzo
5f0d411d11 Merge branch 'fix-libpq-version' into maint_2_7 2017-11-28 17:16:49 +00:00
Daniele Varrazzo
a5f0036a8e Fixed __libpq_version__ for Postgres >= 10.1
The version should be considered as 10.0.1; the number was generated as
10.1.0 instead.

Version number bumped to test building new wheels packages.

Fix #632.
2017-11-28 16:53:23 +00:00
Daniele Varrazzo
1619bae1e3 Merge branch 'python3_mintimeloggingconnection' into maint_2_7 2017-11-28 03:24:09 +00:00
Daniele Varrazzo
74059a0dbe Merge branch 'solaris-support' into maint_2_7 2017-11-28 03:14:58 +00:00
Daniele Varrazzo
a1831ef498 Mention solaris support in NEWS
Close #532.
2017-11-28 03:13:25 +00:00
Daniele Varrazzo
afb42e7625 Merge remote-tracking branch 'jdufresne/py2' 2017-11-28 02:59:17 +00:00
Daniele Varrazzo
b4e658d29b Merge remote-tracking branch 'jdufresne/license' 2017-11-28 02:39:54 +00:00
Jon Dufresne
296abf735e Add some missing trove classifiers for general Python support 2017-11-27 07:30:50 -08:00
Jon Dufresne
5ddac80cec Include license file in the generated wheel package
The wheel package format supports including the license file. This is
done using the [metadata] section in the setup.cfg file. For additional
information on this feature, see:

https://wheel.readthedocs.io/en/stable/index.html#including-the-license-in-the-generated-wheel-file
2017-11-26 10:42:30 -08:00
Daniele Varrazzo
858bc3d42a
Merge pull request #616 from jdufresne/modern-exceptions
Use modern except syntax throughout project
2017-11-21 11:45:34 +00:00
Jon Dufresne
390e43fcb1 Use modern except syntax throughout project
The syntax "except Exception, exc:" is deprecated. All Python versions
supported by psycopg2 support the newer, modern syntax. Forward
compatible with future Python versions.
2017-11-20 20:00:35 -08:00
Daniele Varrazzo
7a2dd85caa NEWS updated after last bugfix. 2017-11-16 16:07:34 +00:00
Federico Di Gregorio
13b0b9d3e3
Merge pull request #614 from fogzot/fix-613
Don't cast point arrays to float arrays (fixes: #613)
2017-11-16 10:09:28 +01:00
Federico Di Gregorio
5983b96c55 Don't cast point arrays to float arrays (fixes: #613) 2017-11-16 10:07:27 +01:00
Daniele Varrazzo
d88d8f9619 Added PostgreSQL 10 in the list of supported servers 2017-11-06 18:38:22 +00:00
Daniele Varrazzo
9614e7241b Further docs cleanup
Recent Sphinx versions seem overly aggressive in autodetecting python,
or I just didn't notice the errors, so be explicit in what language to
use with code examples.
2017-11-06 18:34:23 +00:00
Daniele Varrazzo
4f1505857b Parameters passing docs improved
Every point has an example and all the example show wrong/correct. Nice
rhythm.

Among the improvements, added point saying explicitly "thou shall not
quote placeholders".  Quoted placeholders will just fail except in the
most contrived cases (a statement raising an exception with all the
strings except with the attack ones...), and an example in the following
section explicitly notes "no quotes", but apparenty someone still thinks
this is not documented enough? (see issue #611) so let's just write it
plain and clear into the list of commandments.
2017-11-06 17:31:35 +00:00
My Karlsson
f54783ae6e Emulate timeradd and timersub on Solaris
Solaris does not have timeradd and timersub. Add solaris_support.c which
provides emulated versions of them on Solaris.
2017-10-26 20:21:30 +02:00
40 changed files with 562 additions and 308 deletions

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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
View 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
View 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) */

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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