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:
# For Python versions available on Appveyor, see
# 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)
# Py 3.3, 3.4 = VS Ver. 10.0 (VS 2010)
# 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
OPENSSL_VERSION: "1_0_2n"
POSTGRES_VERSION: "10_1"
PSYCOPG2_TESTDB: psycopg2_test
PSYCOPG2_TESTDB_USER: postgres
@ -86,7 +53,22 @@ cache:
init:
# 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'))
#
# 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 PATH=%PYTHON%;%PYTHON%\Scripts;C:\Program Files\Git\mingw64\bin;%PATH%
@ -162,8 +144,8 @@ install:
}
# Download OpenSSL source
- CD C:\Others
- IF NOT EXIST OpenSSL_1_0_2l.zip (
curl -fsSL -o OpenSSL_1_0_2l.zip https://github.com/openssl/openssl/archive/OpenSSL_1_0_2l.zip
- IF NOT EXIST OpenSSL_%OPENSSL_VERSION%.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
@ -175,15 +157,15 @@ install:
# - nmake build_libs install_dev
- IF NOT EXIST %OPENSSLTOP%\lib\ssleay32.lib (
CD %BUILD_DIR% &&
7z x C:\Others\OpenSSL_1_0_2l.zip &&
CD openssl-OpenSSL_1_0_2l &&
7z x C:\Others\OpenSSL_%OPENSSL_VERSION%.zip &&
CD openssl-OpenSSL_%OPENSSL_VERSION% &&
perl Configure %TARGET% no-asm no-shared no-zlib --prefix=%OPENSSLTOP% --openssldir=%OPENSSLTOP% &&
CALL ms\%DO% &&
nmake -f ms\nt.mak init headers lib &&
COPY inc32\openssl\*.h %OPENSSLTOP%\include\openssl &&
COPY out32\*.lib %OPENSSLTOP%\lib &&
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
@ -197,8 +179,8 @@ install:
# Download PostgreSQL source
- CD C:\Others
- IF NOT EXIST postgres-REL_10_0.zip (
curl -fsSL -o postgres-REL_10_0.zip https://github.com/postgres/postgres/archive/REL_10_0.zip
- IF NOT EXIST postgres-REL_%POSTGRES_VERSION%.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)
@ -209,11 +191,11 @@ install:
# Prepare local include directory for building from
# Build pg_config in place
# 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 (
CD %BUILD_DIR% &&
7z x C:\Others\postgres-REL_10_0.zip &&
CD postgres-REL_10_0\src\tools\msvc &&
7z x C:\Others\postgres-REL_%POSTGRES_VERSION%.zip &&
CD postgres-REL_%POSTGRES_VERSION%\src\tools\msvc &&
ECHO $config-^>{ldap} = 0; > config.pl &&
ECHO $config-^>{openssl} = "%OPENSSLTOP:\=\\%"; >> config.pl &&
ECHO.>> config.pl &&

36
NEWS
View File

@ -4,21 +4,39 @@ Current release
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
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- 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
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- 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
@ -113,9 +131,13 @@ New features:
Bug fixes:
- Throw an exception trying to pass ``NULL`` chars as parameters
(:ticket:`#420`).
- Fixed error caused by missing decoding `~psycopg2.extras.LoggingConnection`
(:ticket:`#483`).
- 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:
@ -129,14 +151,6 @@ Other changes:
(: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
^^^^^^^^^^^^^^^^^^^^^^^^^^^

View File

@ -25,29 +25,40 @@ Documentation is included in the ``doc`` directory and is `available online`__.
.. __: http://initd.org/psycopg/docs/
For any other resource (source code repository, bug tracker, mailing list)
please check the `project homepage`__.
Installation
------------
If your ``pip`` version supports wheel_ packages it should be possible to
install a binary version of Psycopg including all the dependencies from PyPI_.
Just run::
Building Psycopg requires a few prerequisites (a C compiler, some development
packages): please check the install_ and the faq_ documents in the ``doc`` dir
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
If you want to build Psycopg from source you will need some prerequisites (a C
compiler, development packages): please check the install_ and the faq_
documents in the ``doc`` dir for the details.
or using ``setup.py`` if you have downloaded the source package locally::
$ 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
.. _psycopg2-binary: https://pypi.python.org/pypi/psycopg2-binary
.. _install: http://initd.org/psycopg/docs/install.html#install-from-source
.. _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/

View File

@ -295,7 +295,9 @@ something to read::
print "Got NOTIFY:", notify.pid, notify.channel, notify.payload
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'
Timeout

View File

@ -101,6 +101,10 @@ default_role = 'obj'
# output. They are ignored by default.
#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.
pygments_style = 'sphinx'

View File

@ -41,11 +41,6 @@ The ``connection`` class
previously only valid PostgreSQL identifiers were accepted as
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
cursors. The class returned must be a subclass of
`psycopg2.extensions.cursor`. See :ref:`subclassing-cursor` for

View File

@ -99,20 +99,6 @@ Real dictionary cursor
.. 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:: NamedTupleConnection
@ -403,7 +389,7 @@ The individual messages in the replication stream are represented by
class LogicalStreamConsumer(object):
...
# ...
def __call__(self, msg):
self.process_message(msg.payload)
@ -501,7 +487,7 @@ The individual messages in the replication stream are represented by
from datetime import datetime
def consume(msg):
...
# ...
keepalive_interval = 10.0
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
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

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
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/
.. _Python: http://www.python.org/
.. _libpq: http://www.postgresql.org/docs/current/static/libpq.html
@ -32,94 +22,20 @@ The current `!psycopg2` implementation supports:
.. index::
single: Install; from PyPI
single: Install; wheel
single: Wheel
single: Prerequisites
Binary install from PyPI
------------------------
Prerequisites
-------------
`!psycopg2` is `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 including all the dependencies simply
using:
The current `!psycopg2` implementation supports:
.. code-block:: console
..
NOTE: keep consistent with setup.py and the /features/ page.
$ pip install psycopg2
Make sure to use an up-to-date version of :program:`pip` (you can upgrade it
using something like ``pip install -U pip``)
.. __: 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/
- 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 10
- PostgreSQL client library version from 9.1
@ -128,8 +44,8 @@ page`__ or from PyPI_.
Build prerequisites
^^^^^^^^^^^^^^^^^^^
These notes illustrate how to compile Psycopg on Linux. If you want to compile
Psycopg on other platforms you may have to adjust some details accordingly.
The build prerequisites are to be met in order to install Psycopg from source
code, either from a source distribution package or from PyPI.
Psycopg is a C wrapper around the libpq_ PostgreSQL client library. To install
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:
.. code-block:: console
$ pip install psycopg2
or, from the directory containing the source code:
.. code-block:: console
$ 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::
single: setup.py
single: setup.cfg
Non-standard builds
^^^^^^^^^^^^^^^^^^^
-------------------
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
``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:

View File

@ -24,13 +24,18 @@ directly in the client application.
.. 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)
Put away a connection.
If *close* is `!True`, discard the connection from the pool.
*key* should be used consistently with `getconn()`.
.. 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:
- 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,
- terminate transactions using the methods `~connection.commit()` or
@ -73,70 +73,97 @@ The main entry points of Psycopg are:
Passing parameters to SQL queries
---------------------------------
Psycopg casts Python variables to SQL literals by type. Many standard Python types
are already `adapted to the correct SQL representation`__.
Psycopg converts Python variables to SQL values using their types: the Python
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_
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(
... """INSERT INTO some_table (an_int, a_date, a_string)
... VALUES (%s, %s, %s);""",
>>> cur.execute("""
... INSERT INTO some_table (an_int, a_date, a_string)
... VALUES (%s, %s, %s);
... """,
... (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)
VALUES (10, '2005-11-18', 'O''Reilly');
VALUES (10, '2005-11-18', 'O''Reilly');
Named arguments are supported too using :samp:`%({name})s` placeholders.
Using named arguments the values can be passed to the query in any order and
many placeholders can use the same values::
Named arguments are supported too using :samp:`%({name})s` placeholders in the
query and specifying the values into a mapping. Using named arguments allows
to specify the values in any order and to repeat the same value in several
places in the query::
>>> cur.execute(
... """INSERT INTO some_table (an_int, a_date, another_date, a_string)
... VALUES (%(int)s, %(date)s, %(date)s, %(str)s);""",
>>> cur.execute("""
... INSERT INTO some_table (an_int, a_date, another_date, a_string)
... VALUES (%(int)s, %(date)s, %(date)s, %(str)s);
... """,
... {'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
can use the ``%%`` string. Using characters ``%``, ``(``, ``)`` in the
argument names is not supported.
can use the ``%%`` string::
>>> 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
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.
|sql-warn|__.
|sql-warn|__:
.. |sql-warn| replace:: **Never** use ``%`` or ``+`` to merge values
into queries
.. __: sql-injection_
- 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)", (42,)) # WRONG
>>> cur.execute("INSERT INTO numbers VALUES (%s)", (42,)) # correct
>>> cur.execute("INSERT INTO numbers VALUES (%s, %s)" % (10, 20)) # WRONG
>>> cur.execute("INSERT INTO numbers VALUES (%s, %s)", (10, 20)) # correct
- For positional variables binding, *the second argument must always be a
sequence*, even if it contains a single variable. And remember that Python
requires a comma to create a single element tuple::
sequence*, even if it contains a single variable (remember that Python
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",)) # 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
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.
- The placeholder *must not be quoted*. Psycopg will add quotes where needed::
>>> 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
@ -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
in some other way than using the |DECLARE| executed by
`~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 $$
BEGIN

View File

@ -363,12 +363,23 @@ class NamedTupleCursor(_cursor):
try:
from collections import namedtuple
except ImportError, _exc:
except ImportError as _exc:
def _make_nt(self):
raise self._exc
else:
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):

View File

@ -100,7 +100,7 @@ _pydatetime_string_delta(pydatetimeObject *self)
char buffer[8];
int i;
int a = obj->microseconds;
int a = PyDateTime_DELTA_GET_MICROSECONDS(obj);
for (i=0; i < 6 ; i++) {
buffer[5-i] = '0' + (a % 10);
@ -109,7 +109,9 @@ _pydatetime_string_delta(pydatetimeObject *self)
buffer[6] = '\0';
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 *

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.
*
* 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;
PGresult *pgres = 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,
"the 'deferrable' setting is only available"
" from PostgreSQL 9.1");
@ -1209,24 +1214,24 @@ conn_set_session(connectionObject *self, int autocommit,
Py_BEGIN_ALLOW_THREADS;
pthread_mutex_lock(&self->lock);
if (autocommit) {
/* we are in autocommit state, so no BEGIN will be issued:
if (want_autocommit) {
/* we are or are going in autocommit state, so no BEGIN will be issued:
* configure the session with the characteristics requested */
if (isolevel != self->isolevel) {
if (isolevel != SRV_STATE_UNCHANGED) {
if (0 > pq_set_guc_locked(self,
"default_transaction_isolation", srv_isolevels[isolevel],
&pgres, &error, &_save)) {
goto endlock;
}
}
if (readonly != self->readonly) {
if (readonly != SRV_STATE_UNCHANGED) {
if (0 > pq_set_guc_locked(self,
"default_transaction_read_only", srv_state_guc[readonly],
&pgres, &error, &_save)) {
goto endlock;
}
}
if (deferrable != self->deferrable) {
if (deferrable != SRV_STATE_UNCHANGED) {
if (0 > pq_set_guc_locked(self,
"default_transaction_deferrable", srv_state_guc[deferrable],
&pgres, &error, &_save)) {
@ -1251,7 +1256,7 @@ conn_set_session(connectionObject *self, int autocommit,
goto endlock;
}
}
if (self->deferrable != STATE_DEFAULT) {
if (self->server_version >= 90100 && self->deferrable != STATE_DEFAULT) {
if (0 > pq_set_guc_locked(self,
"default_transaction_deferrable", "default",
&pgres, &error, &_save)) {
@ -1260,10 +1265,18 @@ conn_set_session(connectionObject *self, int autocommit,
}
}
self->autocommit = autocommit;
self->isolevel = isolevel;
self->readonly = readonly;
self->deferrable = deferrable;
if (autocommit != SRV_STATE_UNCHANGED) {
self->autocommit = autocommit;
}
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;
endlock:

View File

@ -39,6 +39,7 @@
extern HIDDEN const char *srv_isolevels[];
extern HIDDEN const char *srv_readonly[];
extern HIDDEN const char *srv_deferrable[];
extern HIDDEN const int SRV_STATE_UNCHANGED;
/** DBAPI methods **/
@ -561,10 +562,10 @@ psyco_conn_set_session(connectionObject *self, PyObject *args, PyObject *kwargs)
PyObject *deferrable = Py_None;
PyObject *autocommit = Py_None;
int c_isolevel = self->isolevel;
int c_readonly = self->readonly;
int c_deferrable = self->deferrable;
int c_autocommit = self->autocommit;
int c_isolevel = SRV_STATE_UNCHANGED;
int c_readonly = SRV_STATE_UNCHANGED;
int c_deferrable = SRV_STATE_UNCHANGED;
int c_autocommit = SRV_STATE_UNCHANGED;
static char *kwlist[] =
{"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 (-1 == (value = PyObject_IsTrue(pyvalue))) { return -1; }
if (0 > conn_set_session(self, value,
self->isolevel, self->readonly, self->deferrable)) {
SRV_STATE_UNCHANGED, SRV_STATE_UNCHANGED, SRV_STATE_UNCHANGED)) {
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 (0 > (value = _psyco_conn_parse_isolevel(pyvalue))) { return -1; }
if (0 > conn_set_session(self, self->autocommit,
value, self->readonly, self->deferrable)) {
if (0 > conn_set_session(self, SRV_STATE_UNCHANGED,
value, SRV_STATE_UNCHANGED, SRV_STATE_UNCHANGED)) {
return -1;
}
@ -715,13 +716,13 @@ psyco_conn_set_isolation_level(connectionObject *self, PyObject *args)
if (level == 0) {
if (0 > conn_set_session(self, 1,
self->isolevel, self->readonly, self->deferrable)) {
SRV_STATE_UNCHANGED, SRV_STATE_UNCHANGED, SRV_STATE_UNCHANGED)) {
return NULL;
}
}
else {
if (0 > conn_set_session(self, 0,
level, self->readonly, self->deferrable)) {
level, SRV_STATE_UNCHANGED, SRV_STATE_UNCHANGED)) {
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 (0 > (value = _psyco_conn_parse_onoff(pyvalue))) { return -1; }
if (0 > conn_set_session(self, self->autocommit,
self->isolevel, value, self->deferrable)) {
if (0 > conn_set_session(self, SRV_STATE_UNCHANGED,
SRV_STATE_UNCHANGED, value, SRV_STATE_UNCHANGED)) {
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 (0 > (value = _psyco_conn_parse_onoff(pyvalue))) { return -1; }
if (0 > conn_set_session(self, self->autocommit,
self->isolevel, self->readonly, value)) {
if (0 > conn_set_session(self, SRV_STATE_UNCHANGED,
SRV_STATE_UNCHANGED, SRV_STATE_UNCHANGED, value)) {
return -1;
}

View File

@ -592,8 +592,6 @@ psyco_curs_mogrify(cursorObject *self, PyObject *args, PyObject *kwargs)
return NULL;
}
EXC_IF_CURS_CLOSED(self);
return _psyco_curs_mogrify(self, operation, vars);
}

View File

@ -52,6 +52,10 @@
#include "win32_support.h"
#endif
#if defined(__sun) && defined(__SVR4)
#include "solaris_support.h"
#endif
extern HIDDEN PyObject *psyco_DescriptionType;
extern HIDDEN const char *srv_isolevels[];
extern HIDDEN const char *srv_readonly[];
@ -1948,8 +1952,9 @@ pq_fetch(cursorObject *curs, int no_result)
}
else {
Dprintf("pq_fetch: got tuples, discarding them");
/* TODO: is there any case in which PQntuples == PQcmdTuples? */
_read_rowcount(curs);
CLEARPGRES(curs->pgres);
curs->rowcount = -1;
ex = 0;
}
break;

View File

@ -93,6 +93,7 @@ typedef unsigned long Py_uhash_t;
#ifndef PyNumber_Int
#define PyNumber_Int PyNumber_Long
#endif
#endif /* PY_MAJOR_VERSION > 2 */
#if PY_MAJOR_VERSION < 3
@ -128,6 +129,12 @@ typedef unsigned long Py_uhash_t;
#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);
/* 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_LONGINTEGERARRAY_types[] = {1016, 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_UNICODEARRAY_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:
OpenSSL
Version: 1.0.2l
Version: 1.0.2n
PostgreSQL
Version: 10.0
Version: 10.1
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.
libraries=
[metadata]
license_file = LICENSE

View File

@ -39,6 +39,7 @@ except ImportError:
from distutils.command.build_ext import build_ext
from distutils.sysconfig import get_python_inc
from distutils.ccompiler import get_default_compiler
from distutils.errors import CompileError
from distutils.util import get_platform
try:
@ -64,7 +65,7 @@ except ImportError:
# Take a look at http://www.python.org/dev/peps/pep-0440/
# 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
@ -75,6 +76,7 @@ Intended Audience :: Developers
License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)
License :: OSI Approved :: Zope Public License
Programming Language :: Python
Programming Language :: Python :: 2
Programming Language :: Python :: 2.6
Programming Language :: Python :: 2.7
Programming Language :: Python :: 3
@ -83,6 +85,7 @@ Programming Language :: Python :: 3.3
Programming Language :: Python :: 3.4
Programming Language :: Python :: 3.5
Programming Language :: Python :: 3.6
Programming Language :: Python :: Implementation :: CPython
Programming Language :: C
Programming Language :: SQL
Topic :: Database
@ -105,15 +108,23 @@ class PostgresConfig:
if not self.pg_config_exe:
self.pg_config_exe = self.autodetect_pg_config_path()
if self.pg_config_exe is None:
sys.stderr.write("""\
sys.stderr.write("""
Error: pg_config executable not found.
Please add the directory containing pg_config to the PATH
or specify the full executable path with the option:
pg_config is required to build psycopg2 from source. Please add the directory
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 ...
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)
@ -195,8 +206,7 @@ or with the pg_config option in 'setup.cfg'.
return None
pg_first_inst_key = winreg.OpenKey(reg,
'SOFTWARE\\PostgreSQL\\Installations\\'
+ first_sub_key_name)
'SOFTWARE\\PostgreSQL\\Installations\\' + first_sub_key_name)
try:
pg_inst_base_dir = winreg.QueryValueEx(
pg_first_inst_key, 'Base Directory')[0]
@ -289,8 +299,37 @@ class psycopg_build_ext(build_ext):
else:
return build_ext.get_export_symbols(self, extension)
built_files = 0
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]
# 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)
if m:
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():
pgminor = 0
if pgpatch is None or not pgpatch.isdigit():
pgpatch = 0
pgmajor = int(pgmajor)
pgminor = int(pgminor)
pgpatch = int(pgpatch)
else:
@ -480,7 +522,7 @@ data_files = []
sources = [
'psycopgmodule.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',
'cursor_int.c', 'cursor_type.c',

View File

@ -65,7 +65,7 @@ def test_suite():
import psycopg2
try:
cnn = psycopg2.connect(dsn)
except Exception, e:
except Exception as e:
print "Failed connection to test db:", e.__class__.__name__, e
print "Please set env vars 'PSYCOPG2_TESTDB*' to valid values."
sys.exit(1)

View File

@ -444,7 +444,7 @@ class AsyncTests(ConnectingTestCase):
try:
cnn = psycopg2.connect('dbname=thisdatabasedoesntexist', async_=True)
self.wait(cnn)
except psycopg2.Error, e:
except psycopg2.Error as e:
self.assertNotEqual(str(e), "asynchronous connection failed",
"connection error reason lost")
else:

View File

@ -81,7 +81,7 @@ class AsyncTests(ConnectingTestCase):
try:
cnn = psycopg2.connect('dbname=thisdatabasedoesntexist', async=True)
self.wait(cnn)
except psycopg2.Error, e:
except psycopg2.Error as e:
self.assertNotEqual(str(e), "asynchronous connection failed",
"connection error reason lost")
else:

View File

@ -63,7 +63,7 @@ class CancelTests(ConnectingTestCase):
conn.rollback()
cur.execute("select 1")
self.assertEqual(cur.fetchall(), [(1, )])
except Exception, e:
except Exception as e:
errors.append(e)
raise
@ -71,7 +71,7 @@ class CancelTests(ConnectingTestCase):
cur = conn.cursor()
try:
conn.cancel()
except Exception, e:
except Exception as e:
errors.append(e)
raise
del cur

View File

@ -358,7 +358,7 @@ class ParseDsnTestCase(ConnectingTestCase):
try:
# unterminated quote after dbname:
ext.parse_dsn("dbname='test 2 user=tester password=secret")
except ProgrammingError, e:
except ProgrammingError as e:
raised = True
self.assertTrue(str(e).find('secret') < 0,
"DSN was not exposed in error message")
@ -376,7 +376,7 @@ class ParseDsnTestCase(ConnectingTestCase):
try:
# extra '=' after port value
ext.parse_dsn(dsn='postgresql://tester:secret@/test?port=1111=x')
except psycopg2.ProgrammingError, e:
except psycopg2.ProgrammingError as e:
raised = True
self.assertTrue(str(e).find('secret') < 0,
"URI was not exposed in error message")
@ -1382,6 +1382,16 @@ class TransactionControlTests(ConnectingTestCase):
cur.execute("SHOW default_transaction_read_only;")
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):
def test_closed(self):
@ -1540,9 +1550,13 @@ import os
import sys
import time
import signal
import warnings
import threading
import psycopg2
# ignore wheel deprecation warning
with warnings.catch_warnings():
warnings.simplefilter('ignore')
import psycopg2
def handle_sigabort(sig, frame):
sys.exit(1)

View File

@ -364,7 +364,7 @@ conn.close()
# curs.copy_from, BrokenRead(), "tcopy")
try:
curs.copy_from(BrokenRead(), "tcopy")
except Exception, e:
except Exception as e:
self.assert_('ZeroDivisionError' in str(e))
def test_copy_to_propagate_error(self):

View File

@ -121,6 +121,12 @@ class CursorTests(ConnectingTestCase):
nref2 = sys.getrefcount(foo)
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):
cur = self.conn.cursor()
self.assertRaises(psycopg2.ProgrammingError,
@ -592,6 +598,20 @@ class CursorTests(ConnectingTestCase):
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():
return unittest.TestLoader().loadTestsFromName(__name__)

View File

@ -638,7 +638,8 @@ class FromTicksTestCase(unittest.TestCase):
def test_date_value_error_sec_59_99(self):
from datetime import date
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):
from datetime import time

View File

@ -48,7 +48,7 @@ class ErrocodeTests(ConnectingTestCase):
def f(pg_code='40001'):
try:
errorcodes.lookup(pg_code)
except Exception, e:
except Exception as e:
errs.append(e)
for __ in xrange(MAX_CYCLES):

View File

@ -382,6 +382,15 @@ class NamedTupleCursorTest(ConnectingTestCase):
curs.execute("update nttest set s = s")
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
def test_minimal_generation(self):
# 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()
try:
cur.execute("select * from nonexist")
except psycopg2.Error, exc:
except psycopg2.Error as exc:
e = exc
self.assertEqual(e.pgcode, '42P01')
@ -163,7 +163,7 @@ class ExceptionsTestCase(ConnectingTestCase):
cur = self.conn.cursor()
try:
cur.execute("select * from nonexist")
except psycopg2.Error, exc:
except psycopg2.Error as exc:
e = exc
diag = e.diag
@ -182,7 +182,7 @@ class ExceptionsTestCase(ConnectingTestCase):
cur = self.conn.cursor()
try:
cur.execute("select * from nonexist")
except psycopg2.Error, exc:
except psycopg2.Error as exc:
e = exc
self.assertEqual(e.diag.sqlstate, '42P01')
@ -196,7 +196,7 @@ class ExceptionsTestCase(ConnectingTestCase):
cur = self.conn.cursor()
try:
cur.execute("select * from nonexist")
except psycopg2.Error, exc:
except psycopg2.Error as exc:
return cur, exc
cur, e = tmp()
@ -221,7 +221,7 @@ class ExceptionsTestCase(ConnectingTestCase):
cur = self.conn.cursor()
try:
cur.copy_to(f, 'nonexist')
except psycopg2.Error, exc:
except psycopg2.Error as exc:
diag = exc.diag
self.assertEqual(diag.sqlstate, '42P01')
@ -230,14 +230,14 @@ class ExceptionsTestCase(ConnectingTestCase):
cur = self.conn.cursor()
try:
cur.execute("l'acqua e' poca e 'a papera nun galleggia")
except Exception, exc:
except Exception as exc:
diag1 = exc.diag
self.conn.rollback()
try:
cur.execute("select level from water where ducks > 1")
except psycopg2.Error, exc:
except psycopg2.Error as exc:
diag2 = exc.diag
self.assertEqual(diag1.sqlstate, '42601')
@ -254,7 +254,7 @@ class ExceptionsTestCase(ConnectingTestCase):
cur.execute("insert into test_deferred values (1,2)")
try:
self.conn.commit()
except psycopg2.Error, exc:
except psycopg2.Error as exc:
e = exc
self.assertEqual(e.diag.sqlstate, '23503')
@ -267,7 +267,7 @@ class ExceptionsTestCase(ConnectingTestCase):
)""")
try:
cur.execute("insert into test_exc values(2)")
except psycopg2.Error, exc:
except psycopg2.Error as exc:
e = exc
self.assertEqual(e.pgcode, '23514')
self.assertEqual(e.diag.schema_name[:7], "pg_temp")
@ -282,7 +282,7 @@ class ExceptionsTestCase(ConnectingTestCase):
cur = self.conn.cursor()
try:
cur.execute("select * from nonexist")
except psycopg2.Error, exc:
except psycopg2.Error as exc:
e = exc
e1 = pickle.loads(pickle.dumps(e))
@ -297,7 +297,7 @@ class ExceptionsTestCase(ConnectingTestCase):
import pickle
try:
psycopg2.connect('dbname=nosuchdatabasemate')
except psycopg2.Error, exc:
except psycopg2.Error as exc:
e = exc
e1 = pickle.loads(pickle.dumps(e))

View File

@ -145,7 +145,7 @@ class DeadlockSerializationTests(ConnectingTestCase):
step1.set()
step2.wait()
curs.execute("LOCK table2 IN ACCESS EXCLUSIVE MODE")
except psycopg2.DatabaseError, exc:
except psycopg2.DatabaseError as exc:
self.thread1_error = exc
step1.set()
conn.close()
@ -158,7 +158,7 @@ class DeadlockSerializationTests(ConnectingTestCase):
curs.execute("LOCK table2 IN ACCESS EXCLUSIVE MODE")
step2.set()
curs.execute("LOCK table1 IN ACCESS EXCLUSIVE MODE")
except psycopg2.DatabaseError, exc:
except psycopg2.DatabaseError as exc:
self.thread2_error = exc
step2.set()
conn.close()
@ -195,7 +195,7 @@ class DeadlockSerializationTests(ConnectingTestCase):
step2.wait()
curs.execute("UPDATE table1 SET name='task1' WHERE id = 1")
conn.commit()
except psycopg2.DatabaseError, exc:
except psycopg2.DatabaseError as exc:
self.thread1_error = exc
step1.set()
conn.close()
@ -207,7 +207,7 @@ class DeadlockSerializationTests(ConnectingTestCase):
step1.wait()
curs.execute("UPDATE table1 SET name='task2' WHERE id = 1")
conn.commit()
except psycopg2.DatabaseError, exc:
except psycopg2.DatabaseError as exc:
self.thread2_error = exc
step2.set()
conn.close()

View File

@ -443,7 +443,7 @@ class ByteaParserTest(unittest.TestCase):
def setUp(self):
try:
self._cast = self._import_cast()
except Exception, e:
except Exception as e:
self._cast = None
self._exc = e

View File

@ -113,9 +113,14 @@ class TypesExtrasTests(ConnectingTestCase):
psycopg2.extensions.adapt, Foo(), ext.ISQLQuote, None)
try:
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'")
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):
@wraps(f)

View File

@ -212,7 +212,7 @@ class WithCursorTestCase(WithTestCase):
with conn.cursor('named') as cur:
cur.execute("select 1/0")
cur.fetchone()
except psycopg2.DataError, e:
except psycopg2.DataError as e:
self.assertEqual(e.pgcode, '22012')
else:
self.fail("where is my exception?")

View File

@ -116,7 +116,7 @@ class ConnectingTestCase(unittest.TestCase):
def connect(self, **kwargs):
try:
self._conns
except AttributeError, e:
except AttributeError as e:
raise AttributeError(
"%s (did you forget to call ConnectingTestCase.setUp()?)"
% e)
@ -149,7 +149,7 @@ class ConnectingTestCase(unittest.TestCase):
conn = self.connect(**kwargs)
if conn.async_ == 1:
self.wait(conn)
except psycopg2.OperationalError, e:
except psycopg2.OperationalError as e:
# If pgcode is not set it is a genuine connection error
# Otherwise we tried to run some bad operation in the connection
# (e.g. bug #482) and we'd rather know that.
@ -388,7 +388,7 @@ def skip_if_no_superuser(f):
from psycopg2 import ProgrammingError
try:
return f(self)
except ProgrammingError, e:
except ProgrammingError as e:
import psycopg2.errorcodes
if e.pgcode == psycopg2.errorcodes.INSUFFICIENT_PRIVILEGE:
self.skipTest("skipped because not superuser")