mirror of
https://github.com/psycopg/psycopg2.git
synced 2024-11-22 08:56:34 +03:00
Merge branch 'master' into feature/replication-protocol-c-connection-object
This commit is contained in:
commit
cb7032554e
6
NEWS
6
NEWS
|
@ -6,7 +6,10 @@ What's new in psycopg 2.7
|
||||||
|
|
||||||
New features:
|
New features:
|
||||||
|
|
||||||
- Added `~psycopg2.extensions.parse_dsn()` function (:ticket:`#321`).
|
- Added `~psycopg2.extensions.parse_dsn()` and
|
||||||
|
`~psycopg2.extensions.make_dsn()` functions (:tickets:`#321, #363`).
|
||||||
|
`~psycopg2.connect()` now can take both *dsn* and keyword arguments, merging
|
||||||
|
them together.
|
||||||
- Added `~psycopg2.__libpq_version__` and
|
- Added `~psycopg2.__libpq_version__` and
|
||||||
`~psycopg2.extensions.libpq_version()` to inspect the version of the
|
`~psycopg2.extensions.libpq_version()` to inspect the version of the
|
||||||
``libpq`` library the module was compiled/loaded with
|
``libpq`` library the module was compiled/loaded with
|
||||||
|
@ -27,6 +30,7 @@ What's new in psycopg 2.6.2
|
||||||
- Raise `!NotSupportedError` on unhandled server response status
|
- Raise `!NotSupportedError` on unhandled server response status
|
||||||
(:ticket:`#352`).
|
(:ticket:`#352`).
|
||||||
- Fixed `!PersistentConnectionPool` on Python 3 (:ticket:`#348`).
|
- Fixed `!PersistentConnectionPool` on Python 3 (:ticket:`#348`).
|
||||||
|
- Fixed `!errorcodes.lookup` initialization thread-safety (:ticket:`#382`).
|
||||||
|
|
||||||
|
|
||||||
What's new in psycopg 2.6.1
|
What's new in psycopg 2.6.1
|
||||||
|
|
|
@ -270,7 +270,7 @@ wasting resources.
|
||||||
|
|
||||||
A simple application could poll the connection from time to time to check if
|
A simple application could poll the connection from time to time to check if
|
||||||
something new has arrived. A better strategy is to use some I/O completion
|
something new has arrived. A better strategy is to use some I/O completion
|
||||||
function such as :py:func:`~select.select` to sleep until awaken from the kernel when there is
|
function such as :py:func:`~select.select` to sleep until awakened by the kernel when there is
|
||||||
some data to read on the connection, thereby using no CPU unless there is
|
some data to read on the connection, thereby using no CPU unless there is
|
||||||
something to read::
|
something to read::
|
||||||
|
|
||||||
|
|
|
@ -12,17 +12,12 @@
|
||||||
The module contains a few objects and function extending the minimum set of
|
The module contains a few objects and function extending the minimum set of
|
||||||
functionalities defined by the |DBAPI|_.
|
functionalities defined by the |DBAPI|_.
|
||||||
|
|
||||||
.. function:: parse_dsn(dsn)
|
Classes definitions
|
||||||
|
-------------------
|
||||||
|
|
||||||
Parse connection string into a dictionary of keywords and values.
|
Instances of these classes are usually returned by factory functions or
|
||||||
|
attributes. Their definitions are exposed here to allow subclassing,
|
||||||
Uses libpq's ``PQconninfoParse`` to parse the string according to
|
introspection etc.
|
||||||
accepted format(s) and check for supported keywords.
|
|
||||||
|
|
||||||
Example::
|
|
||||||
|
|
||||||
>>> psycopg2.extensions.parse_dsn('dbname=test user=postgres password=secret')
|
|
||||||
{'password': 'secret', 'user': 'postgres', 'dbname': 'test'}
|
|
||||||
|
|
||||||
.. function:: make_dsn(**kwargs)
|
.. function:: make_dsn(**kwargs)
|
||||||
|
|
||||||
|
@ -56,6 +51,7 @@ functionalities defined by the |DBAPI|_.
|
||||||
|
|
||||||
For a complete description of the class, see `connection`.
|
For a complete description of the class, see `connection`.
|
||||||
|
|
||||||
|
|
||||||
.. class:: cursor(conn, name=None)
|
.. class:: cursor(conn, name=None)
|
||||||
|
|
||||||
It is the class usually returned by the `connection.cursor()`
|
It is the class usually returned by the `connection.cursor()`
|
||||||
|
@ -66,6 +62,7 @@ functionalities defined by the |DBAPI|_.
|
||||||
|
|
||||||
For a complete description of the class, see `cursor`.
|
For a complete description of the class, see `cursor`.
|
||||||
|
|
||||||
|
|
||||||
.. class:: lobject(conn [, oid [, mode [, new_oid [, new_file ]]]])
|
.. class:: lobject(conn [, oid [, mode [, new_oid [, new_file ]]]])
|
||||||
|
|
||||||
Wrapper for a PostgreSQL large object. See :ref:`large-objects` for an
|
Wrapper for a PostgreSQL large object. See :ref:`large-objects` for an
|
||||||
|
@ -222,39 +219,6 @@ functionalities defined by the |DBAPI|_.
|
||||||
server versions.
|
server versions.
|
||||||
|
|
||||||
|
|
||||||
.. autofunction:: set_wait_callback(f)
|
|
||||||
|
|
||||||
.. versionadded:: 2.2.0
|
|
||||||
|
|
||||||
.. autofunction:: get_wait_callback()
|
|
||||||
|
|
||||||
.. versionadded:: 2.2.0
|
|
||||||
|
|
||||||
.. function:: libpq_version()
|
|
||||||
|
|
||||||
Return the version number of the ``libpq`` dynamic library loaded as an
|
|
||||||
integer, in the same format of `~connection.server_version`.
|
|
||||||
|
|
||||||
Raise `~psycopg2.NotSupportedError` if the ``psycopg2`` module was
|
|
||||||
compiled with a ``libpq`` version lesser than 9.1 (which can be detected
|
|
||||||
by the `~psycopg2.__libpq_version__` constant).
|
|
||||||
|
|
||||||
.. seealso:: libpq docs for `PQlibVersion()`__.
|
|
||||||
|
|
||||||
.. __: http://www.postgresql.org/docs/current/static/libpq-misc.html#LIBPQ-PQLIBVERSION
|
|
||||||
|
|
||||||
.. function:: quote_ident(str, scope)
|
|
||||||
|
|
||||||
Return quoted identifier according to PostgreSQL quoting rules.
|
|
||||||
|
|
||||||
The *scope* must be a `connection` or a `cursor`, the underlying
|
|
||||||
connection encoding is used for any necessary character conversion.
|
|
||||||
|
|
||||||
Requires libpq >= 9.0.
|
|
||||||
|
|
||||||
.. seealso:: libpq docs for `PQescapeIdentifier()`__
|
|
||||||
|
|
||||||
.. __: http://www.postgresql.org/docs/current/static/libpq-exec.html#LIBPQ-PQESCAPEIDENTIFIER
|
|
||||||
|
|
||||||
.. _sql-adaptation-objects:
|
.. _sql-adaptation-objects:
|
||||||
|
|
||||||
|
@ -514,6 +478,106 @@ The module exports a few exceptions in addition to the :ref:`standard ones
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.. _coroutines-functions:
|
||||||
|
|
||||||
|
Coroutines support functions
|
||||||
|
----------------------------
|
||||||
|
|
||||||
|
These functions are used to set and retrieve the callback function for
|
||||||
|
:ref:`cooperation with coroutine libraries <green-support>`.
|
||||||
|
|
||||||
|
.. versionadded:: 2.2.0
|
||||||
|
|
||||||
|
.. autofunction:: set_wait_callback(f)
|
||||||
|
|
||||||
|
.. autofunction:: get_wait_callback()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Other functions
|
||||||
|
---------------
|
||||||
|
|
||||||
|
.. function:: libpq_version()
|
||||||
|
|
||||||
|
Return the version number of the ``libpq`` dynamic library loaded as an
|
||||||
|
integer, in the same format of `~connection.server_version`.
|
||||||
|
|
||||||
|
Raise `~psycopg2.NotSupportedError` if the ``psycopg2`` module was
|
||||||
|
compiled with a ``libpq`` version lesser than 9.1 (which can be detected
|
||||||
|
by the `~psycopg2.__libpq_version__` constant).
|
||||||
|
|
||||||
|
.. versionadded:: 2.7
|
||||||
|
|
||||||
|
.. seealso:: libpq docs for `PQlibVersion()`__.
|
||||||
|
|
||||||
|
.. __: http://www.postgresql.org/docs/current/static/libpq-misc.html#LIBPQ-PQLIBVERSION
|
||||||
|
|
||||||
|
|
||||||
|
.. function:: make_dsn(dsn=None, \*\*kwargs)
|
||||||
|
|
||||||
|
Create a valid connection string from arguments.
|
||||||
|
|
||||||
|
Put together the arguments in *kwargs* into a connection string. If *dsn*
|
||||||
|
is specified too, merge the arguments coming from both the sources. If the
|
||||||
|
same argument name is specified in both the sources, the *kwargs* value
|
||||||
|
overrides the *dsn* value.
|
||||||
|
|
||||||
|
The input arguments are validated: the output should always be a valid
|
||||||
|
connection string (as far as `parse_dsn()` is concerned). If not raise
|
||||||
|
`~psycopg2.ProgrammingError`.
|
||||||
|
|
||||||
|
Example::
|
||||||
|
|
||||||
|
>>> from psycopg2.extensions import make_dsn
|
||||||
|
>>> make_dsn('dbname=foo host=example.com', password="s3cr3t")
|
||||||
|
'host=example.com password=s3cr3t dbname=foo'
|
||||||
|
|
||||||
|
.. versionadded:: 2.7
|
||||||
|
|
||||||
|
|
||||||
|
.. function:: parse_dsn(dsn)
|
||||||
|
|
||||||
|
Parse connection string into a dictionary of keywords and values.
|
||||||
|
|
||||||
|
Parsing is delegated to the libpq: different versions of the client
|
||||||
|
library may support different formats or parameters (for example,
|
||||||
|
`connection URIs`__ are only supported from libpq 9.2). Raise
|
||||||
|
`~psycopg2.ProgrammingError` if the *dsn* is not valid.
|
||||||
|
|
||||||
|
.. __: http://www.postgresql.org/docs/current/static/libpq-connect.html#LIBPQ-CONNSTRING
|
||||||
|
|
||||||
|
Example::
|
||||||
|
|
||||||
|
>>> from psycopg2.extensions import parse_dsn
|
||||||
|
>>> parse_dsn('dbname=test user=postgres password=secret')
|
||||||
|
{'password': 'secret', 'user': 'postgres', 'dbname': 'test'}
|
||||||
|
>>> parse_dsn("postgresql://someone@example.com/somedb?connect_timeout=10")
|
||||||
|
{'host': 'example.com', 'user': 'someone', 'dbname': 'somedb', 'connect_timeout': '10'}
|
||||||
|
|
||||||
|
.. versionadded:: 2.7
|
||||||
|
|
||||||
|
.. seealso:: libpq docs for `PQconninfoParse()`__.
|
||||||
|
|
||||||
|
.. __: http://www.postgresql.org/docs/current/static/libpq-connect.html#LIBPQ-PQCONNINFOPARSE
|
||||||
|
|
||||||
|
|
||||||
|
.. function:: quote_ident(str, scope)
|
||||||
|
|
||||||
|
Return quoted identifier according to PostgreSQL quoting rules.
|
||||||
|
|
||||||
|
The *scope* must be a `connection` or a `cursor`, the underlying
|
||||||
|
connection encoding is used for any necessary character conversion.
|
||||||
|
|
||||||
|
Requires libpq >= 9.0.
|
||||||
|
|
||||||
|
.. versionadded:: 2.7
|
||||||
|
|
||||||
|
.. seealso:: libpq docs for `PQescapeIdentifier()`__
|
||||||
|
|
||||||
|
.. __: http://www.postgresql.org/docs/current/static/libpq-exec.html#LIBPQ-PQESCAPEIDENTIFIER
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.. index::
|
.. index::
|
||||||
pair: Isolation level; Constants
|
pair: Isolation level; Constants
|
||||||
|
|
||||||
|
|
|
@ -17,37 +17,34 @@ The module interface respects the standard defined in the |DBAPI|_.
|
||||||
single: DSN (Database Source Name)
|
single: DSN (Database Source Name)
|
||||||
|
|
||||||
.. function::
|
.. function::
|
||||||
connect(dsn, connection_factory=None, cursor_factory=None, async=False)
|
connect(dsn=None, connection_factory=None, cursor_factory=None, async=False, \*\*kwargs)
|
||||||
connect(\*\*kwargs, connection_factory=None, cursor_factory=None, async=False)
|
|
||||||
|
|
||||||
Create a new database session and return a new `connection` object.
|
Create a new database session and return a new `connection` object.
|
||||||
|
|
||||||
The connection parameters can be specified either as a `libpq connection
|
The connection parameters can be specified as a `libpq connection
|
||||||
string`__ using the *dsn* parameter::
|
string`__ using the *dsn* parameter::
|
||||||
|
|
||||||
conn = psycopg2.connect("dbname=test user=postgres password=secret")
|
conn = psycopg2.connect("dbname=test user=postgres password=secret")
|
||||||
|
|
||||||
or using a set of keyword arguments::
|
or using a set of keyword arguments::
|
||||||
|
|
||||||
conn = psycopg2.connect(database="test", user="postgres", password="secret")
|
conn = psycopg2.connect(dbname"test", user="postgres", password="secret")
|
||||||
|
|
||||||
The two call styles are mutually exclusive: you cannot specify connection
|
or using a mix of both: if the same parameter name is specified in both
|
||||||
parameters as keyword arguments together with a connection string; only
|
sources, the *kwargs* value will have precedence over the *dsn* value.
|
||||||
the parameters not needed for the database connection (*i.e.*
|
Note that either the *dsn* or at least one connection-related keyword
|
||||||
*connection_factory*, *cursor_factory*, and *async*) are supported
|
argument is required.
|
||||||
together with the *dsn* argument.
|
|
||||||
|
|
||||||
The basic connection parameters are:
|
The basic connection parameters are:
|
||||||
|
|
||||||
- `!dbname` -- the database name (only in the *dsn* string)
|
- `!dbname` -- the database name (`!database` is a deprecated alias)
|
||||||
- `!database` -- the database name (only as keyword argument)
|
|
||||||
- `!user` -- user name used to authenticate
|
- `!user` -- user name used to authenticate
|
||||||
- `!password` -- password used to authenticate
|
- `!password` -- password used to authenticate
|
||||||
- `!host` -- database host address (defaults to UNIX socket if not provided)
|
- `!host` -- database host address (defaults to UNIX socket if not provided)
|
||||||
- `!port` -- connection port number (defaults to 5432 if not provided)
|
- `!port` -- connection port number (defaults to 5432 if not provided)
|
||||||
|
|
||||||
Any other connection parameter supported by the client library/server can
|
Any other connection parameter supported by the client library/server can
|
||||||
be passed either in the connection string or as keywords. The PostgreSQL
|
be passed either in the connection string or as a keyword. The PostgreSQL
|
||||||
documentation contains the complete list of the `supported parameters`__.
|
documentation contains the complete list of the `supported parameters`__.
|
||||||
Also note that the same parameters can be passed to the client library
|
Also note that the same parameters can be passed to the client library
|
||||||
using `environment variables`__.
|
using `environment variables`__.
|
||||||
|
@ -76,6 +73,9 @@ The module interface respects the standard defined in the |DBAPI|_.
|
||||||
.. versionchanged:: 2.5
|
.. versionchanged:: 2.5
|
||||||
added the *cursor_factory* parameter.
|
added the *cursor_factory* parameter.
|
||||||
|
|
||||||
|
.. versionchanged:: 2.7
|
||||||
|
both *dsn* and keyword arguments can be specified.
|
||||||
|
|
||||||
.. seealso::
|
.. seealso::
|
||||||
|
|
||||||
- `~psycopg2.extensions.parse_dsn`
|
- `~psycopg2.extensions.parse_dsn`
|
||||||
|
@ -89,8 +89,8 @@ The module interface respects the standard defined in the |DBAPI|_.
|
||||||
|
|
||||||
.. extension::
|
.. extension::
|
||||||
|
|
||||||
The parameters *connection_factory* and *async* are Psycopg extensions
|
The non-connection-related keyword parameters are Psycopg extensions
|
||||||
to the |DBAPI|.
|
to the |DBAPI|_.
|
||||||
|
|
||||||
.. data:: apilevel
|
.. data:: apilevel
|
||||||
|
|
||||||
|
|
|
@ -81,12 +81,12 @@ else:
|
||||||
del Decimal, Adapter
|
del Decimal, Adapter
|
||||||
|
|
||||||
|
|
||||||
def connect(dsn=None,
|
def connect(dsn=None, connection_factory=None, cursor_factory=None,
|
||||||
connection_factory=None, cursor_factory=None, async=False, **kwargs):
|
async=False, **kwargs):
|
||||||
"""
|
"""
|
||||||
Create a new database connection.
|
Create a new database connection.
|
||||||
|
|
||||||
The connection parameters can be specified either as a string:
|
The connection parameters can be specified as a string:
|
||||||
|
|
||||||
conn = psycopg2.connect("dbname=test user=postgres password=secret")
|
conn = psycopg2.connect("dbname=test user=postgres password=secret")
|
||||||
|
|
||||||
|
@ -94,9 +94,9 @@ def connect(dsn=None,
|
||||||
|
|
||||||
conn = psycopg2.connect(database="test", user="postgres", password="secret")
|
conn = psycopg2.connect(database="test", user="postgres", password="secret")
|
||||||
|
|
||||||
The basic connection parameters are:
|
Or as a mix of both. The basic connection parameters are:
|
||||||
|
|
||||||
- *dbname*: the database name (only in dsn string)
|
- *dbname*: the database name
|
||||||
- *database*: the database name (only as keyword argument)
|
- *database*: the database name (only as keyword argument)
|
||||||
- *user*: user name used to authenticate
|
- *user*: user name used to authenticate
|
||||||
- *password*: password used to authenticate
|
- *password*: password used to authenticate
|
||||||
|
@ -116,7 +116,11 @@ def connect(dsn=None,
|
||||||
library: the list of supported parameters depends on the library version.
|
library: the list of supported parameters depends on the library version.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
conn = _connect(dsn, connection_factory, async, **kwargs)
|
if dsn is None and not kwargs:
|
||||||
|
raise TypeError('missing dsn and no parameters')
|
||||||
|
|
||||||
|
dsn = _ext.make_dsn(dsn, **kwargs)
|
||||||
|
conn = _connect(dsn, connection_factory=connection_factory, async=async)
|
||||||
if cursor_factory is not None:
|
if cursor_factory is not None:
|
||||||
conn.cursor_factory = cursor_factory
|
conn.cursor_factory = cursor_factory
|
||||||
|
|
||||||
|
|
|
@ -38,11 +38,17 @@ def lookup(code, _cache={}):
|
||||||
return _cache[code]
|
return _cache[code]
|
||||||
|
|
||||||
# Generate the lookup map at first usage.
|
# Generate the lookup map at first usage.
|
||||||
|
tmp = {}
|
||||||
for k, v in globals().iteritems():
|
for k, v in globals().iteritems():
|
||||||
if isinstance(v, str) and len(v) in (2, 5):
|
if isinstance(v, str) and len(v) in (2, 5):
|
||||||
_cache[v] = k
|
tmp[v] = k
|
||||||
|
|
||||||
return lookup(code)
|
assert tmp
|
||||||
|
|
||||||
|
# Atomic update, to avoid race condition on import (bug #382)
|
||||||
|
_cache.update(tmp)
|
||||||
|
|
||||||
|
return _cache[code]
|
||||||
|
|
||||||
|
|
||||||
# autogenerated data: do not edit below this point.
|
# autogenerated data: do not edit below this point.
|
||||||
|
|
|
@ -7,7 +7,7 @@ This module holds all the extensions to the DBAPI-2.0 provided by psycopg.
|
||||||
- `lobject` -- the new-type inheritable large object class
|
- `lobject` -- the new-type inheritable large object class
|
||||||
- `adapt()` -- exposes the PEP-246_ compatible adapting mechanism used
|
- `adapt()` -- exposes the PEP-246_ compatible adapting mechanism used
|
||||||
by psycopg to adapt Python types to PostgreSQL ones
|
by psycopg to adapt Python types to PostgreSQL ones
|
||||||
|
|
||||||
.. _PEP-246: http://www.python.org/peps/pep-0246.html
|
.. _PEP-246: http://www.python.org/peps/pep-0246.html
|
||||||
"""
|
"""
|
||||||
# psycopg/extensions.py - DBAPI-2.0 extensions specific to psycopg
|
# psycopg/extensions.py - DBAPI-2.0 extensions specific to psycopg
|
||||||
|
@ -32,6 +32,9 @@ This module holds all the extensions to the DBAPI-2.0 provided by psycopg.
|
||||||
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
||||||
# License for more details.
|
# License for more details.
|
||||||
|
|
||||||
|
import re as _re
|
||||||
|
import sys as _sys
|
||||||
|
|
||||||
from psycopg2._psycopg import UNICODE, INTEGER, LONGINTEGER, BOOLEAN, FLOAT
|
from psycopg2._psycopg import UNICODE, INTEGER, LONGINTEGER, BOOLEAN, FLOAT
|
||||||
from psycopg2._psycopg import TIME, DATE, INTERVAL, DECIMAL
|
from psycopg2._psycopg import TIME, DATE, INTERVAL, DECIMAL
|
||||||
from psycopg2._psycopg import BINARYARRAY, BOOLEANARRAY, DATEARRAY, DATETIMEARRAY
|
from psycopg2._psycopg import BINARYARRAY, BOOLEANARRAY, DATEARRAY, DATETIMEARRAY
|
||||||
|
@ -56,8 +59,8 @@ try:
|
||||||
except ImportError:
|
except ImportError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
from psycopg2._psycopg import adapt, adapters, encodings, connection, cursor, lobject, Xid, libpq_version
|
from psycopg2._psycopg import adapt, adapters, encodings, connection, cursor
|
||||||
from psycopg2._psycopg import parse_dsn, make_dsn, quote_ident
|
from psycopg2._psycopg import lobject, Xid, libpq_version, parse_dsn, quote_ident
|
||||||
from psycopg2._psycopg import string_types, binary_types, new_type, new_array_type, register_type
|
from psycopg2._psycopg import string_types, binary_types, new_type, new_array_type, register_type
|
||||||
from psycopg2._psycopg import ISQLQuote, Notify, Diagnostics, Column
|
from psycopg2._psycopg import ISQLQuote, Notify, Diagnostics, Column
|
||||||
|
|
||||||
|
@ -101,7 +104,6 @@ TRANSACTION_STATUS_INTRANS = 2
|
||||||
TRANSACTION_STATUS_INERROR = 3
|
TRANSACTION_STATUS_INERROR = 3
|
||||||
TRANSACTION_STATUS_UNKNOWN = 4
|
TRANSACTION_STATUS_UNKNOWN = 4
|
||||||
|
|
||||||
import sys as _sys
|
|
||||||
|
|
||||||
# Return bytes from a string
|
# Return bytes from a string
|
||||||
if _sys.version_info[0] < 3:
|
if _sys.version_info[0] < 3:
|
||||||
|
@ -111,6 +113,7 @@ else:
|
||||||
def b(s):
|
def b(s):
|
||||||
return s.encode('utf8')
|
return s.encode('utf8')
|
||||||
|
|
||||||
|
|
||||||
def register_adapter(typ, callable):
|
def register_adapter(typ, callable):
|
||||||
"""Register 'callable' as an ISQLQuote adapter for type 'typ'."""
|
"""Register 'callable' as an ISQLQuote adapter for type 'typ'."""
|
||||||
adapters[(typ, ISQLQuote)] = callable
|
adapters[(typ, ISQLQuote)] = callable
|
||||||
|
@ -154,6 +157,53 @@ class NoneAdapter(object):
|
||||||
return _null
|
return _null
|
||||||
|
|
||||||
|
|
||||||
|
def make_dsn(dsn=None, **kwargs):
|
||||||
|
"""Convert a set of keywords into a connection strings."""
|
||||||
|
if dsn is None and not kwargs:
|
||||||
|
return ''
|
||||||
|
|
||||||
|
# If no kwarg is specified don't mung the dsn, but verify it
|
||||||
|
if not kwargs:
|
||||||
|
parse_dsn(dsn)
|
||||||
|
return dsn
|
||||||
|
|
||||||
|
# Override the dsn with the parameters
|
||||||
|
if 'database' in kwargs:
|
||||||
|
if 'dbname' in kwargs:
|
||||||
|
raise TypeError(
|
||||||
|
"you can't specify both 'database' and 'dbname' arguments")
|
||||||
|
kwargs['dbname'] = kwargs.pop('database')
|
||||||
|
|
||||||
|
if dsn is not None:
|
||||||
|
tmp = parse_dsn(dsn)
|
||||||
|
tmp.update(kwargs)
|
||||||
|
kwargs = tmp
|
||||||
|
|
||||||
|
dsn = " ".join(["%s=%s" % (k, _param_escape(str(v)))
|
||||||
|
for (k, v) in kwargs.iteritems()])
|
||||||
|
|
||||||
|
# verify that the returned dsn is valid
|
||||||
|
parse_dsn(dsn)
|
||||||
|
|
||||||
|
return dsn
|
||||||
|
|
||||||
|
|
||||||
|
def _param_escape(s,
|
||||||
|
re_escape=_re.compile(r"([\\'])"),
|
||||||
|
re_space=_re.compile(r'\s')):
|
||||||
|
"""
|
||||||
|
Apply the escaping rule required by PQconnectdb
|
||||||
|
"""
|
||||||
|
if not s:
|
||||||
|
return "''"
|
||||||
|
|
||||||
|
s = re_escape.sub(r'\\\1', s)
|
||||||
|
if re_space.search(s):
|
||||||
|
s = "'" + s + "'"
|
||||||
|
|
||||||
|
return s
|
||||||
|
|
||||||
|
|
||||||
# Create default json typecasters for PostgreSQL 9.2 oids
|
# Create default json typecasters for PostgreSQL 9.2 oids
|
||||||
from psycopg2._json import register_default_json, register_default_jsonb
|
from psycopg2._json import register_default_json, register_default_jsonb
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,7 @@ old code while porting to psycopg 2. Import it as follows::
|
||||||
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
||||||
# License for more details.
|
# License for more details.
|
||||||
|
|
||||||
import _psycopg as _2psycopg
|
import psycopg2._psycopg as _2psycopg
|
||||||
from psycopg2.extensions import cursor as _2cursor
|
from psycopg2.extensions import cursor as _2cursor
|
||||||
from psycopg2.extensions import connection as _2connection
|
from psycopg2.extensions import connection as _2connection
|
||||||
|
|
||||||
|
|
|
@ -199,7 +199,8 @@ exit:
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#define psyco_parse_dsn_doc "parse_dsn(dsn) -> dict"
|
#define psyco_parse_dsn_doc \
|
||||||
|
"parse_dsn(dsn) -> dict -- parse a connection string into parameters"
|
||||||
|
|
||||||
PyObject *
|
PyObject *
|
||||||
psyco_parse_dsn(PyObject *self, PyObject *args, PyObject *kwargs)
|
psyco_parse_dsn(PyObject *self, PyObject *args, PyObject *kwargs)
|
||||||
|
@ -219,7 +220,7 @@ psyco_parse_dsn(PyObject *self, PyObject *args, PyObject *kwargs)
|
||||||
options = PQconninfoParse(Bytes_AS_STRING(dsn), &err);
|
options = PQconninfoParse(Bytes_AS_STRING(dsn), &err);
|
||||||
if (options == NULL) {
|
if (options == NULL) {
|
||||||
if (err != NULL) {
|
if (err != NULL) {
|
||||||
PyErr_Format(ProgrammingError, "error parsing the dsn: %s", err);
|
PyErr_Format(ProgrammingError, "invalid dsn: %s", err);
|
||||||
PQfreemem(err);
|
PQfreemem(err);
|
||||||
} else {
|
} else {
|
||||||
PyErr_SetString(OperationalError, "PQconninfoParse() failed");
|
PyErr_SetString(OperationalError, "PQconninfoParse() failed");
|
||||||
|
|
|
@ -35,6 +35,7 @@ import test_replication
|
||||||
import test_copy
|
import test_copy
|
||||||
import test_cursor
|
import test_cursor
|
||||||
import test_dates
|
import test_dates
|
||||||
|
import test_errcodes
|
||||||
import test_extras_dictcursor
|
import test_extras_dictcursor
|
||||||
import test_green
|
import test_green
|
||||||
import test_lobject
|
import test_lobject
|
||||||
|
@ -73,6 +74,7 @@ def test_suite():
|
||||||
suite.addTest(test_copy.test_suite())
|
suite.addTest(test_copy.test_suite())
|
||||||
suite.addTest(test_cursor.test_suite())
|
suite.addTest(test_cursor.test_suite())
|
||||||
suite.addTest(test_dates.test_suite())
|
suite.addTest(test_dates.test_suite())
|
||||||
|
suite.addTest(test_errcodes.test_suite())
|
||||||
suite.addTest(test_extras_dictcursor.test_suite())
|
suite.addTest(test_extras_dictcursor.test_suite())
|
||||||
suite.addTest(test_green.test_suite())
|
suite.addTest(test_green.test_suite())
|
||||||
suite.addTest(test_lobject.test_suite())
|
suite.addTest(test_lobject.test_suite())
|
||||||
|
|
|
@ -32,6 +32,7 @@ from StringIO import StringIO
|
||||||
import psycopg2
|
import psycopg2
|
||||||
import psycopg2.errorcodes
|
import psycopg2.errorcodes
|
||||||
import psycopg2.extensions
|
import psycopg2.extensions
|
||||||
|
ext = psycopg2.extensions
|
||||||
|
|
||||||
from testutils import unittest, decorate_all_tests, skip_if_no_superuser
|
from testutils import unittest, decorate_all_tests, skip_if_no_superuser
|
||||||
from testutils import skip_before_postgres, skip_after_postgres, skip_before_libpq
|
from testutils import skip_before_postgres, skip_after_postgres, skip_before_libpq
|
||||||
|
@ -125,7 +126,7 @@ class ConnectionTests(ConnectingTestCase):
|
||||||
if self.conn.server_version >= 90300:
|
if self.conn.server_version >= 90300:
|
||||||
cur.execute("set client_min_messages=debug1")
|
cur.execute("set client_min_messages=debug1")
|
||||||
for i in range(0, 100, 10):
|
for i in range(0, 100, 10):
|
||||||
sql = " ".join(["create temp table table%d (id serial);" % j for j in range(i, i+10)])
|
sql = " ".join(["create temp table table%d (id serial);" % j for j in range(i, i + 10)])
|
||||||
cur.execute(sql)
|
cur.execute(sql)
|
||||||
|
|
||||||
self.assertEqual(50, len(conn.notices))
|
self.assertEqual(50, len(conn.notices))
|
||||||
|
@ -151,7 +152,7 @@ class ConnectionTests(ConnectingTestCase):
|
||||||
|
|
||||||
# not limited, but no error
|
# not limited, but no error
|
||||||
for i in range(0, 100, 10):
|
for i in range(0, 100, 10):
|
||||||
sql = " ".join(["create temp table table2_%d (id serial);" % j for j in range(i, i+10)])
|
sql = " ".join(["create temp table table2_%d (id serial);" % j for j in range(i, i + 10)])
|
||||||
cur.execute(sql)
|
cur.execute(sql)
|
||||||
|
|
||||||
self.assertEqual(len([n for n in conn.notices if 'CREATE TABLE' in n]),
|
self.assertEqual(len([n for n in conn.notices if 'CREATE TABLE' in n]),
|
||||||
|
@ -172,7 +173,7 @@ class ConnectionTests(ConnectingTestCase):
|
||||||
self.assert_(self.conn.server_version)
|
self.assert_(self.conn.server_version)
|
||||||
|
|
||||||
def test_protocol_version(self):
|
def test_protocol_version(self):
|
||||||
self.assert_(self.conn.protocol_version in (2,3),
|
self.assert_(self.conn.protocol_version in (2, 3),
|
||||||
self.conn.protocol_version)
|
self.conn.protocol_version)
|
||||||
|
|
||||||
def test_tpc_unsupported(self):
|
def test_tpc_unsupported(self):
|
||||||
|
@ -252,7 +253,7 @@ class ConnectionTests(ConnectingTestCase):
|
||||||
t1.start()
|
t1.start()
|
||||||
i = 1
|
i = 1
|
||||||
for i in range(1000):
|
for i in range(1000):
|
||||||
cur.execute("select %s;",(i,))
|
cur.execute("select %s;", (i,))
|
||||||
conn.commit()
|
conn.commit()
|
||||||
while conn.notices:
|
while conn.notices:
|
||||||
notices.append((1, conn.notices.pop()))
|
notices.append((1, conn.notices.pop()))
|
||||||
|
@ -313,16 +314,15 @@ class ConnectionTests(ConnectingTestCase):
|
||||||
class ParseDsnTestCase(ConnectingTestCase):
|
class ParseDsnTestCase(ConnectingTestCase):
|
||||||
def test_parse_dsn(self):
|
def test_parse_dsn(self):
|
||||||
from psycopg2 import ProgrammingError
|
from psycopg2 import ProgrammingError
|
||||||
from psycopg2.extensions import parse_dsn
|
|
||||||
|
|
||||||
self.assertEqual(parse_dsn('dbname=test user=tester password=secret'),
|
self.assertEqual(ext.parse_dsn('dbname=test user=tester password=secret'),
|
||||||
dict(user='tester', password='secret', dbname='test'),
|
dict(user='tester', password='secret', dbname='test'),
|
||||||
"simple DSN parsed")
|
"simple DSN parsed")
|
||||||
|
|
||||||
self.assertRaises(ProgrammingError, parse_dsn,
|
self.assertRaises(ProgrammingError, ext.parse_dsn,
|
||||||
"dbname=test 2 user=tester password=secret")
|
"dbname=test 2 user=tester password=secret")
|
||||||
|
|
||||||
self.assertEqual(parse_dsn("dbname='test 2' user=tester password=secret"),
|
self.assertEqual(ext.parse_dsn("dbname='test 2' user=tester password=secret"),
|
||||||
dict(user='tester', password='secret', dbname='test 2'),
|
dict(user='tester', password='secret', dbname='test 2'),
|
||||||
"DSN with quoting parsed")
|
"DSN with quoting parsed")
|
||||||
|
|
||||||
|
@ -332,7 +332,7 @@ class ParseDsnTestCase(ConnectingTestCase):
|
||||||
raised = False
|
raised = False
|
||||||
try:
|
try:
|
||||||
# unterminated quote after dbname:
|
# unterminated quote after dbname:
|
||||||
parse_dsn("dbname='test 2 user=tester password=secret")
|
ext.parse_dsn("dbname='test 2 user=tester password=secret")
|
||||||
except ProgrammingError, e:
|
except ProgrammingError, e:
|
||||||
raised = True
|
raised = True
|
||||||
self.assertTrue(str(e).find('secret') < 0,
|
self.assertTrue(str(e).find('secret') < 0,
|
||||||
|
@ -343,16 +343,14 @@ class ParseDsnTestCase(ConnectingTestCase):
|
||||||
|
|
||||||
@skip_before_libpq(9, 2)
|
@skip_before_libpq(9, 2)
|
||||||
def test_parse_dsn_uri(self):
|
def test_parse_dsn_uri(self):
|
||||||
from psycopg2.extensions import parse_dsn
|
self.assertEqual(ext.parse_dsn('postgresql://tester:secret@/test'),
|
||||||
|
|
||||||
self.assertEqual(parse_dsn('postgresql://tester:secret@/test'),
|
|
||||||
dict(user='tester', password='secret', dbname='test'),
|
dict(user='tester', password='secret', dbname='test'),
|
||||||
"valid URI dsn parsed")
|
"valid URI dsn parsed")
|
||||||
|
|
||||||
raised = False
|
raised = False
|
||||||
try:
|
try:
|
||||||
# extra '=' after port value
|
# extra '=' after port value
|
||||||
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, e:
|
||||||
raised = True
|
raised = True
|
||||||
self.assertTrue(str(e).find('secret') < 0,
|
self.assertTrue(str(e).find('secret') < 0,
|
||||||
|
@ -362,24 +360,91 @@ class ParseDsnTestCase(ConnectingTestCase):
|
||||||
self.assertTrue(raised, "ProgrammingError raised due to invalid URI")
|
self.assertTrue(raised, "ProgrammingError raised due to invalid URI")
|
||||||
|
|
||||||
def test_unicode_value(self):
|
def test_unicode_value(self):
|
||||||
from psycopg2.extensions import parse_dsn
|
|
||||||
snowman = u"\u2603"
|
snowman = u"\u2603"
|
||||||
d = parse_dsn('dbname=' + snowman)
|
d = ext.parse_dsn('dbname=' + snowman)
|
||||||
if sys.version_info[0] < 3:
|
if sys.version_info[0] < 3:
|
||||||
self.assertEqual(d['dbname'], snowman.encode('utf8'))
|
self.assertEqual(d['dbname'], snowman.encode('utf8'))
|
||||||
else:
|
else:
|
||||||
self.assertEqual(d['dbname'], snowman)
|
self.assertEqual(d['dbname'], snowman)
|
||||||
|
|
||||||
def test_unicode_key(self):
|
def test_unicode_key(self):
|
||||||
from psycopg2.extensions import parse_dsn
|
|
||||||
snowman = u"\u2603"
|
snowman = u"\u2603"
|
||||||
self.assertRaises(psycopg2.ProgrammingError, parse_dsn,
|
self.assertRaises(psycopg2.ProgrammingError, ext.parse_dsn,
|
||||||
snowman + '=' + snowman)
|
snowman + '=' + snowman)
|
||||||
|
|
||||||
def test_bad_param(self):
|
def test_bad_param(self):
|
||||||
from psycopg2.extensions import parse_dsn
|
self.assertRaises(TypeError, ext.parse_dsn, None)
|
||||||
self.assertRaises(TypeError, parse_dsn, None)
|
self.assertRaises(TypeError, ext.parse_dsn, 42)
|
||||||
self.assertRaises(TypeError, parse_dsn, 42)
|
|
||||||
|
|
||||||
|
class MakeDsnTestCase(ConnectingTestCase):
|
||||||
|
def assertDsnEqual(self, dsn1, dsn2):
|
||||||
|
self.assertEqual(set(dsn1.split()), set(dsn2.split()))
|
||||||
|
|
||||||
|
def test_empty_arguments(self):
|
||||||
|
self.assertEqual(ext.make_dsn(), '')
|
||||||
|
|
||||||
|
def test_empty_string(self):
|
||||||
|
dsn = ext.make_dsn('')
|
||||||
|
self.assertEqual(dsn, '')
|
||||||
|
|
||||||
|
def test_params_validation(self):
|
||||||
|
self.assertRaises(psycopg2.ProgrammingError,
|
||||||
|
ext.make_dsn, 'dbnamo=a')
|
||||||
|
self.assertRaises(psycopg2.ProgrammingError,
|
||||||
|
ext.make_dsn, dbnamo='a')
|
||||||
|
self.assertRaises(psycopg2.ProgrammingError,
|
||||||
|
ext.make_dsn, 'dbname=a', nosuchparam='b')
|
||||||
|
|
||||||
|
def test_empty_param(self):
|
||||||
|
dsn = ext.make_dsn(dbname='sony', password='')
|
||||||
|
self.assertDsnEqual(dsn, "dbname=sony password=''")
|
||||||
|
|
||||||
|
def test_escape(self):
|
||||||
|
dsn = ext.make_dsn(dbname='hello world')
|
||||||
|
self.assertEqual(dsn, "dbname='hello world'")
|
||||||
|
|
||||||
|
dsn = ext.make_dsn(dbname=r'back\slash')
|
||||||
|
self.assertEqual(dsn, r"dbname=back\\slash")
|
||||||
|
|
||||||
|
dsn = ext.make_dsn(dbname="quo'te")
|
||||||
|
self.assertEqual(dsn, r"dbname=quo\'te")
|
||||||
|
|
||||||
|
dsn = ext.make_dsn(dbname="with\ttab")
|
||||||
|
self.assertEqual(dsn, "dbname='with\ttab'")
|
||||||
|
|
||||||
|
dsn = ext.make_dsn(dbname=r"\every thing'")
|
||||||
|
self.assertEqual(dsn, r"dbname='\\every thing\''")
|
||||||
|
|
||||||
|
def test_database_is_a_keyword(self):
|
||||||
|
self.assertEqual(ext.make_dsn(database='sigh'), "dbname=sigh")
|
||||||
|
|
||||||
|
def test_params_merging(self):
|
||||||
|
dsn = ext.make_dsn('dbname=foo host=bar', host='baz')
|
||||||
|
self.assertDsnEqual(dsn, 'dbname=foo host=baz')
|
||||||
|
|
||||||
|
dsn = ext.make_dsn('dbname=foo', user='postgres')
|
||||||
|
self.assertDsnEqual(dsn, 'dbname=foo user=postgres')
|
||||||
|
|
||||||
|
def test_no_dsn_munging(self):
|
||||||
|
dsnin = 'dbname=a host=b user=c password=d'
|
||||||
|
dsn = ext.make_dsn(dsnin)
|
||||||
|
self.assertEqual(dsn, dsnin)
|
||||||
|
|
||||||
|
@skip_before_libpq(9, 2)
|
||||||
|
def test_url_is_cool(self):
|
||||||
|
url = 'postgresql://tester:secret@/test?application_name=wat'
|
||||||
|
dsn = ext.make_dsn(url)
|
||||||
|
self.assertEqual(dsn, url)
|
||||||
|
|
||||||
|
dsn = ext.make_dsn(url, application_name='woot')
|
||||||
|
self.assertDsnEqual(dsn,
|
||||||
|
'dbname=test user=tester password=secret application_name=woot')
|
||||||
|
|
||||||
|
self.assertRaises(psycopg2.ProgrammingError,
|
||||||
|
ext.make_dsn, 'postgresql://tester:secret@/test?nosuch=param')
|
||||||
|
self.assertRaises(psycopg2.ProgrammingError,
|
||||||
|
ext.make_dsn, url, nosuch="param")
|
||||||
|
|
||||||
|
|
||||||
class IsolationLevelsTestCase(ConnectingTestCase):
|
class IsolationLevelsTestCase(ConnectingTestCase):
|
||||||
|
@ -587,7 +652,7 @@ class ConnectionTwoPhaseTests(ConnectingTestCase):
|
||||||
cnn.close()
|
cnn.close()
|
||||||
return
|
return
|
||||||
|
|
||||||
gids = [ r[0] for r in cur ]
|
gids = [r[0] for r in cur]
|
||||||
for gid in gids:
|
for gid in gids:
|
||||||
cur.execute("rollback prepared %s;", (gid,))
|
cur.execute("rollback prepared %s;", (gid,))
|
||||||
cnn.close()
|
cnn.close()
|
||||||
|
@ -761,13 +826,13 @@ class ConnectionTwoPhaseTests(ConnectingTestCase):
|
||||||
def test_status_after_recover(self):
|
def test_status_after_recover(self):
|
||||||
cnn = self.connect()
|
cnn = self.connect()
|
||||||
self.assertEqual(psycopg2.extensions.STATUS_READY, cnn.status)
|
self.assertEqual(psycopg2.extensions.STATUS_READY, cnn.status)
|
||||||
xns = cnn.tpc_recover()
|
cnn.tpc_recover()
|
||||||
self.assertEqual(psycopg2.extensions.STATUS_READY, cnn.status)
|
self.assertEqual(psycopg2.extensions.STATUS_READY, cnn.status)
|
||||||
|
|
||||||
cur = cnn.cursor()
|
cur = cnn.cursor()
|
||||||
cur.execute("select 1")
|
cur.execute("select 1")
|
||||||
self.assertEqual(psycopg2.extensions.STATUS_BEGIN, cnn.status)
|
self.assertEqual(psycopg2.extensions.STATUS_BEGIN, cnn.status)
|
||||||
xns = cnn.tpc_recover()
|
cnn.tpc_recover()
|
||||||
self.assertEqual(psycopg2.extensions.STATUS_BEGIN, cnn.status)
|
self.assertEqual(psycopg2.extensions.STATUS_BEGIN, cnn.status)
|
||||||
|
|
||||||
def test_recovered_xids(self):
|
def test_recovered_xids(self):
|
||||||
|
@ -789,12 +854,12 @@ class ConnectionTwoPhaseTests(ConnectingTestCase):
|
||||||
|
|
||||||
cnn = self.connect()
|
cnn = self.connect()
|
||||||
xids = cnn.tpc_recover()
|
xids = cnn.tpc_recover()
|
||||||
xids = [ xid for xid in xids if xid.database == dbname ]
|
xids = [xid for xid in xids if xid.database == dbname]
|
||||||
xids.sort(key=attrgetter('gtrid'))
|
xids.sort(key=attrgetter('gtrid'))
|
||||||
|
|
||||||
# check the values returned
|
# check the values returned
|
||||||
self.assertEqual(len(okvals), len(xids))
|
self.assertEqual(len(okvals), len(xids))
|
||||||
for (xid, (gid, prepared, owner, database)) in zip (xids, okvals):
|
for (xid, (gid, prepared, owner, database)) in zip(xids, okvals):
|
||||||
self.assertEqual(xid.gtrid, gid)
|
self.assertEqual(xid.gtrid, gid)
|
||||||
self.assertEqual(xid.prepared, prepared)
|
self.assertEqual(xid.prepared, prepared)
|
||||||
self.assertEqual(xid.owner, owner)
|
self.assertEqual(xid.owner, owner)
|
||||||
|
@ -825,8 +890,7 @@ class ConnectionTwoPhaseTests(ConnectingTestCase):
|
||||||
cnn.close()
|
cnn.close()
|
||||||
|
|
||||||
cnn = self.connect()
|
cnn = self.connect()
|
||||||
xids = [ xid for xid in cnn.tpc_recover()
|
xids = [x for x in cnn.tpc_recover() if x.database == dbname]
|
||||||
if xid.database == dbname ]
|
|
||||||
self.assertEqual(1, len(xids))
|
self.assertEqual(1, len(xids))
|
||||||
xid = xids[0]
|
xid = xids[0]
|
||||||
self.assertEqual(xid.format_id, fid)
|
self.assertEqual(xid.format_id, fid)
|
||||||
|
@ -847,8 +911,7 @@ class ConnectionTwoPhaseTests(ConnectingTestCase):
|
||||||
cnn.close()
|
cnn.close()
|
||||||
|
|
||||||
cnn = self.connect()
|
cnn = self.connect()
|
||||||
xids = [ xid for xid in cnn.tpc_recover()
|
xids = [x for x in cnn.tpc_recover() if x.database == dbname]
|
||||||
if xid.database == dbname ]
|
|
||||||
self.assertEqual(1, len(xids))
|
self.assertEqual(1, len(xids))
|
||||||
xid = xids[0]
|
xid = xids[0]
|
||||||
self.assertEqual(xid.format_id, None)
|
self.assertEqual(xid.format_id, None)
|
||||||
|
@ -893,8 +956,7 @@ class ConnectionTwoPhaseTests(ConnectingTestCase):
|
||||||
cnn.tpc_begin(x1)
|
cnn.tpc_begin(x1)
|
||||||
cnn.tpc_prepare()
|
cnn.tpc_prepare()
|
||||||
cnn.reset()
|
cnn.reset()
|
||||||
xid = [ xid for xid in cnn.tpc_recover()
|
xid = [x for x in cnn.tpc_recover() if x.database == dbname][0]
|
||||||
if xid.database == dbname ][0]
|
|
||||||
self.assertEqual(10, xid.format_id)
|
self.assertEqual(10, xid.format_id)
|
||||||
self.assertEqual('uni', xid.gtrid)
|
self.assertEqual('uni', xid.gtrid)
|
||||||
self.assertEqual('code', xid.bqual)
|
self.assertEqual('code', xid.bqual)
|
||||||
|
@ -909,8 +971,7 @@ class ConnectionTwoPhaseTests(ConnectingTestCase):
|
||||||
cnn.tpc_prepare()
|
cnn.tpc_prepare()
|
||||||
cnn.reset()
|
cnn.reset()
|
||||||
|
|
||||||
xid = [ xid for xid in cnn.tpc_recover()
|
xid = [x for x in cnn.tpc_recover() if x.database == dbname][0]
|
||||||
if xid.database == dbname ][0]
|
|
||||||
self.assertEqual(None, xid.format_id)
|
self.assertEqual(None, xid.format_id)
|
||||||
self.assertEqual('transaction-id', xid.gtrid)
|
self.assertEqual('transaction-id', xid.gtrid)
|
||||||
self.assertEqual(None, xid.bqual)
|
self.assertEqual(None, xid.bqual)
|
||||||
|
@ -929,7 +990,7 @@ class ConnectionTwoPhaseTests(ConnectingTestCase):
|
||||||
cnn.reset()
|
cnn.reset()
|
||||||
|
|
||||||
xids = cnn.tpc_recover()
|
xids = cnn.tpc_recover()
|
||||||
xid = [ xid for xid in xids if xid.database == dbname ][0]
|
xid = [x for x in xids if x.database == dbname][0]
|
||||||
self.assertEqual(None, xid.format_id)
|
self.assertEqual(None, xid.format_id)
|
||||||
self.assertEqual('dict-connection', xid.gtrid)
|
self.assertEqual('dict-connection', xid.gtrid)
|
||||||
self.assertEqual(None, xid.bqual)
|
self.assertEqual(None, xid.bqual)
|
||||||
|
|
65
tests/test_errcodes.py
Executable file
65
tests/test_errcodes.py
Executable file
|
@ -0,0 +1,65 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
# test_errcodes.py - unit test for psycopg2.errcodes module
|
||||||
|
#
|
||||||
|
# Copyright (C) 2015 Daniele Varrazzo <daniele.varrazzo@gmail.com>
|
||||||
|
#
|
||||||
|
# psycopg2 is free software: you can redistribute it and/or modify it
|
||||||
|
# under the terms of the GNU Lesser General Public License as published
|
||||||
|
# by the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# In addition, as a special exception, the copyright holders give
|
||||||
|
# permission to link this program with the OpenSSL library (or with
|
||||||
|
# modified versions of OpenSSL that use the same license as OpenSSL),
|
||||||
|
# and distribute linked combinations including the two.
|
||||||
|
#
|
||||||
|
# You must obey the GNU Lesser General Public License in all respects for
|
||||||
|
# all of the code used other than OpenSSL.
|
||||||
|
#
|
||||||
|
# psycopg2 is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
||||||
|
# License for more details.
|
||||||
|
|
||||||
|
from testutils import unittest, ConnectingTestCase
|
||||||
|
|
||||||
|
try:
|
||||||
|
reload
|
||||||
|
except NameError:
|
||||||
|
from imp import reload
|
||||||
|
|
||||||
|
from threading import Thread
|
||||||
|
from psycopg2 import errorcodes
|
||||||
|
|
||||||
|
class ErrocodeTests(ConnectingTestCase):
|
||||||
|
def test_lookup_threadsafe(self):
|
||||||
|
|
||||||
|
# Increase if it does not fail with KeyError
|
||||||
|
MAX_CYCLES = 2000
|
||||||
|
|
||||||
|
errs = []
|
||||||
|
def f(pg_code='40001'):
|
||||||
|
try:
|
||||||
|
errorcodes.lookup(pg_code)
|
||||||
|
except Exception, e:
|
||||||
|
errs.append(e)
|
||||||
|
|
||||||
|
for __ in xrange(MAX_CYCLES):
|
||||||
|
reload(errorcodes)
|
||||||
|
(t1, t2) = (Thread(target=f), Thread(target=f))
|
||||||
|
(t1.start(), t2.start())
|
||||||
|
(t1.join(), t2.join())
|
||||||
|
|
||||||
|
if errs:
|
||||||
|
self.fail(
|
||||||
|
"raised %s errors in %s cycles (first is %s %s)" % (
|
||||||
|
len(errs), MAX_CYCLES,
|
||||||
|
errs[0].__class__.__name__, errs[0]))
|
||||||
|
|
||||||
|
|
||||||
|
def test_suite():
|
||||||
|
return unittest.TestLoader().loadTestsFromName(__name__)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
|
@ -31,11 +31,13 @@ from testutils import ConnectingTestCase, skip_copy_if_green, script_to_py3
|
||||||
|
|
||||||
import psycopg2
|
import psycopg2
|
||||||
|
|
||||||
|
|
||||||
class ConnectTestCase(unittest.TestCase):
|
class ConnectTestCase(unittest.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.args = None
|
self.args = None
|
||||||
def connect_stub(*args, **kwargs):
|
|
||||||
self.args = psycopg2.parse_args(*args, **kwargs)
|
def conect_stub(dsn, connection_factory=None, async=False):
|
||||||
|
self.args = (dsn, connection_factory, async)
|
||||||
|
|
||||||
self._connect_orig = psycopg2._connect
|
self._connect_orig = psycopg2._connect
|
||||||
psycopg2._connect = connect_stub
|
psycopg2._connect = connect_stub
|
||||||
|
@ -43,6 +45,9 @@ class ConnectTestCase(unittest.TestCase):
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
psycopg2._connect = self._connect_orig
|
psycopg2._connect = self._connect_orig
|
||||||
|
|
||||||
|
def assertDsnEqual(self, dsn1, dsn2):
|
||||||
|
self.assertEqual(set(dsn1.split()), set(dsn2.split()))
|
||||||
|
|
||||||
def test_there_has_to_be_something(self):
|
def test_there_has_to_be_something(self):
|
||||||
self.assertRaises(TypeError, psycopg2.connect)
|
self.assertRaises(TypeError, psycopg2.connect)
|
||||||
self.assertRaises(TypeError, psycopg2.connect,
|
self.assertRaises(TypeError, psycopg2.connect,
|
||||||
|
@ -57,8 +62,8 @@ class ConnectTestCase(unittest.TestCase):
|
||||||
self.assertEqual(self.args[2], False)
|
self.assertEqual(self.args[2], False)
|
||||||
|
|
||||||
def test_dsn(self):
|
def test_dsn(self):
|
||||||
psycopg2.connect('dbname=blah x=y')
|
psycopg2.connect('dbname=blah host=y')
|
||||||
self.assertEqual(self.args[0], 'dbname=blah x=y')
|
self.assertEqual(self.args[0], 'dbname=blah host=y')
|
||||||
self.assertEqual(self.args[1], None)
|
self.assertEqual(self.args[1], None)
|
||||||
self.assertEqual(self.args[2], False)
|
self.assertEqual(self.args[2], False)
|
||||||
|
|
||||||
|
@ -83,39 +88,31 @@ class ConnectTestCase(unittest.TestCase):
|
||||||
self.assertEqual(len(self.args[0].split()), 4)
|
self.assertEqual(len(self.args[0].split()), 4)
|
||||||
|
|
||||||
def test_generic_keywords(self):
|
def test_generic_keywords(self):
|
||||||
psycopg2.connect(foo='bar')
|
psycopg2.connect(options='stuff')
|
||||||
self.assertEqual(self.args[0], 'foo=bar')
|
self.assertEqual(self.args[0], 'options=stuff')
|
||||||
|
|
||||||
def test_factory(self):
|
def test_factory(self):
|
||||||
def f(dsn, async=False):
|
def f(dsn, async=False):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
psycopg2.connect(database='foo', bar='baz', connection_factory=f)
|
psycopg2.connect(database='foo', host='baz', connection_factory=f)
|
||||||
dsn = " %s " % self.args[0]
|
self.assertDsnEqual(self.args[0], 'dbname=foo host=baz')
|
||||||
self.assertIn(" dbname=foo ", dsn)
|
|
||||||
self.assertIn(" bar=baz ", dsn)
|
|
||||||
self.assertEqual(self.args[1], f)
|
self.assertEqual(self.args[1], f)
|
||||||
self.assertEqual(self.args[2], False)
|
self.assertEqual(self.args[2], False)
|
||||||
|
|
||||||
psycopg2.connect("dbname=foo bar=baz", connection_factory=f)
|
psycopg2.connect("dbname=foo host=baz", connection_factory=f)
|
||||||
dsn = " %s " % self.args[0]
|
self.assertDsnEqual(self.args[0], 'dbname=foo host=baz')
|
||||||
self.assertIn(" dbname=foo ", dsn)
|
|
||||||
self.assertIn(" bar=baz ", dsn)
|
|
||||||
self.assertEqual(self.args[1], f)
|
self.assertEqual(self.args[1], f)
|
||||||
self.assertEqual(self.args[2], False)
|
self.assertEqual(self.args[2], False)
|
||||||
|
|
||||||
def test_async(self):
|
def test_async(self):
|
||||||
psycopg2.connect(database='foo', bar='baz', async=1)
|
psycopg2.connect(database='foo', host='baz', async=1)
|
||||||
dsn = " %s " % self.args[0]
|
self.assertDsnEqual(self.args[0], 'dbname=foo host=baz')
|
||||||
self.assertIn(" dbname=foo ", dsn)
|
|
||||||
self.assertIn(" bar=baz ", dsn)
|
|
||||||
self.assertEqual(self.args[1], None)
|
self.assertEqual(self.args[1], None)
|
||||||
self.assert_(self.args[2])
|
self.assert_(self.args[2])
|
||||||
|
|
||||||
psycopg2.connect("dbname=foo bar=baz", async=True)
|
psycopg2.connect("dbname=foo host=baz", async=True)
|
||||||
dsn = " %s " % self.args[0]
|
self.assertDsnEqual(self.args[0], 'dbname=foo host=baz')
|
||||||
self.assertIn(" dbname=foo ", dsn)
|
|
||||||
self.assertIn(" bar=baz ", dsn)
|
|
||||||
self.assertEqual(self.args[1], None)
|
self.assertEqual(self.args[1], None)
|
||||||
self.assert_(self.args[2])
|
self.assert_(self.args[2])
|
||||||
|
|
||||||
|
@ -127,9 +124,7 @@ class ConnectTestCase(unittest.TestCase):
|
||||||
|
|
||||||
def test_empty_param(self):
|
def test_empty_param(self):
|
||||||
psycopg2.connect(database='sony', password='')
|
psycopg2.connect(database='sony', password='')
|
||||||
dsn = " %s " % self.args[0]
|
self.assertDsnEqual(self.args[0], "dbname=sony password=''")
|
||||||
self.assertIn(" dbname=sony ", dsn)
|
|
||||||
self.assertIn(" password='' ", dsn)
|
|
||||||
|
|
||||||
def test_escape(self):
|
def test_escape(self):
|
||||||
psycopg2.connect(database='hello world')
|
psycopg2.connect(database='hello world')
|
||||||
|
@ -147,13 +142,12 @@ class ConnectTestCase(unittest.TestCase):
|
||||||
psycopg2.connect(database=r"\every thing'")
|
psycopg2.connect(database=r"\every thing'")
|
||||||
self.assertEqual(self.args[0], r"dbname='\\every thing\''")
|
self.assertEqual(self.args[0], r"dbname='\\every thing\''")
|
||||||
|
|
||||||
def test_no_kwargs_swallow(self):
|
def test_params_merging(self):
|
||||||
self.assertRaises(TypeError,
|
psycopg2.connect('dbname=foo', database='bar')
|
||||||
psycopg2.connect, 'dbname=foo', database='foo')
|
self.assertEqual(self.args[0], 'dbname=bar')
|
||||||
self.assertRaises(TypeError,
|
|
||||||
psycopg2.connect, 'dbname=foo', user='postgres')
|
psycopg2.connect('dbname=foo', user='postgres')
|
||||||
self.assertRaises(TypeError,
|
self.assertDsnEqual(self.args[0], 'dbname=foo user=postgres')
|
||||||
psycopg2.connect, 'dbname=foo', no_such_param='meh')
|
|
||||||
|
|
||||||
|
|
||||||
class ExceptionsTestCase(ConnectingTestCase):
|
class ExceptionsTestCase(ConnectingTestCase):
|
||||||
|
@ -219,7 +213,8 @@ class ExceptionsTestCase(ConnectingTestCase):
|
||||||
self.assertEqual(diag.sqlstate, '42P01')
|
self.assertEqual(diag.sqlstate, '42P01')
|
||||||
|
|
||||||
del diag
|
del diag
|
||||||
gc.collect(); gc.collect()
|
gc.collect()
|
||||||
|
gc.collect()
|
||||||
assert(w() is None)
|
assert(w() is None)
|
||||||
|
|
||||||
@skip_copy_if_green
|
@skip_copy_if_green
|
||||||
|
@ -341,7 +336,7 @@ class TestVersionDiscovery(unittest.TestCase):
|
||||||
self.assertTrue(type(psycopg2.__libpq_version__) is int)
|
self.assertTrue(type(psycopg2.__libpq_version__) is int)
|
||||||
try:
|
try:
|
||||||
self.assertTrue(type(psycopg2.extensions.libpq_version()) is int)
|
self.assertTrue(type(psycopg2.extensions.libpq_version()) is int)
|
||||||
except NotSupportedError:
|
except psycopg2.NotSupportedError:
|
||||||
self.assertTrue(psycopg2.__libpq_version__ < 90100)
|
self.assertTrue(psycopg2.__libpq_version__ < 90100)
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user