Pulled down changes from dvarrazzo branch on gh

Pulled the master branch from of Daniele's psycopg branch on github and
merged the changes.
This commit is contained in:
Jason Erickson 2011-02-10 15:59:31 -07:00
parent 8d28509f49
commit b075017ad9
84 changed files with 3160 additions and 1834 deletions

View File

@ -1,11 +1,27 @@
2010-12-18 Daniele Varrazzo <daniele.varrazzo@gmail.com>
* connection.h: added codec attribute to avoid repeated codec name
lookups during unicode query/params manipulations.
* setup.py: bumped to version 2.3.2.dev0
* psycopg/connection_int.c: applied patch from Marti Raudsepp to close
ticket #24. Fixed segfault in connection when DateStyle not available
(e.g. pgbouncer appars not passing it to the client)
2010-12-15 Daniele Varrazzo <daniele.varrazzo@gmail.com>
* psycopg/utils.c: Added psycopg_strdup function.
2010-12-14 Daniele Varrazzo <daniele.varrazzo@gmail.com>
* psycopg/connection_type.c: No need to put connection fields to zero.
* lib/extensions.py: Improved mapping from PG to Py encodings.
* psycopg/psycopgmodule.c: Added a few missing encodings: EUC_CN,
EUC_JIS_2004, ISO885910, ISO885916, LATIN10, SHIFT_JIS_2004.
2010-12-04 Daniele Varrazzo <daniele.varrazzo@gmail.com>
* setup.py: bumped to version 2.3.1.dev0

View File

@ -19,7 +19,7 @@
# make check # this requires setting up a test database with the correct user
PYTHON := python$(PYTHON_VERSION)
PYTHON_VERSION ?= $(shell $(PYTHON) -c 'import sys; print "%d.%d" % sys.version_info[:2]')
PYTHON_VERSION ?= $(shell $(PYTHON) -c 'import sys; print ("%d.%d" % sys.version_info[:2])')
BUILD_DIR = $(shell pwd)/build/lib.$(PYTHON_VERSION)
ENV_DIR = $(shell pwd)/env/py-$(PYTHON_VERSION)
ENV_BIN = $(ENV_DIR)/bin
@ -50,11 +50,11 @@ SDIST := dist/psycopg2-$(VERSION).tar.gz
EASY_INSTALL = PYTHONPATH=$(ENV_LIB) $(ENV_BIN)/easy_install-$(PYTHON_VERSION) -d $(ENV_LIB) -s $(ENV_BIN)
EZ_SETUP = $(ENV_BIN)/ez_setup.py
.PHONY: env check runtests clean
.PHONY: env check clean
default: package
all: package runtests sdist
all: package sdist
package: $(PLATLIB) $(PURELIB)
@ -87,7 +87,7 @@ ez_setup:
wget -O $(EZ_SETUP) http://peak.telecommunity.com/dist/ez_setup.py
check:
PYTHONPATH=$(BUILD_DIR):.:$(PYTHONPATH) $(PYTHON) tests/__init__.py --verbose
PYTHONPATH=$(BUILD_DIR):$(PYTHONPATH) $(PYTHON) -c "from psycopg2 import tests; tests.unittest.main(defaultTest='tests.test_suite')" --verbose
testdb:
@echo "* Creating $(TESTDB)"

View File

@ -1,3 +1,143 @@
What's new in psycopg 2.4
-------------------------
* New features and changes:
- Added `register_composite()` function to cast PostgreSQL composite types
into Python tuples/namedtuples.
- More efficient iteration on named cursors.
- The build script refuses to guess values if pg_config is not found.
- Connections and cursors are weakly referenceable.
- Added 'b' and 't' mode to large objects: write can deal with both bytes
strings and unicode; read can return either bytes strings or decoded
unicode.
- COPY sends Unicode data to files implementing io.TextIOBase.
- The build script refuses to guess values if pg_config is not found.
- Improved PostgreSQL-Python encodings mapping. Added a few
missing encodings: EUC_CN, EUC_JIS_2004, ISO885910, ISO885916,
LATIN10, SHIFT_JIS_2004.
- Dropped repeated dictionary lookups with unicode query/parameters.
- Empty lists correctly roundtrip Python -> PostgreSQL -> Python.
* Bug fixes:
- Fixed adaptation of None in composite types (ticket #26). Bug report by
Karsten Hilbert.
- Fixed several reference leaks in less common code paths.
- Fixed segfault when a large object is closed and its connection no more
available.
- Added missing icon to ZPsycopgDA package, not available in Zope 2.12.9
(ticket #30). Bug report and patch by Pumukel.
What's new in psycopg 2.3.2
---------------------------
- Fixed segfault with middleware not passing DateStyle to the client
(ticket #24). Bug report and patch by Marti Raudsepp.
What's new in psycopg 2.3.1
---------------------------
- Fixed build problem on CentOS 5.5 x86_64 (ticket #23).
What's new in psycopg 2.3.0
---------------------------
psycopg 2.3 aims to expose some new features introduced in PostgreSQL 9.0.
* Main new features:
- `dict` to `hstore` adapter and `hstore` to `dict` typecaster, using both
9.0 and pre-9.0 syntax.
- Two-phase commit protocol support as per DBAPI specification.
- Support for payload in notifications received from the backed.
- `namedtuple`-returning cursor.
- Query execution cancel.
* Other features and changes:
- Dropped support for protocol 2: Psycopg 2.3 can only connect to PostgreSQL
servers with version at least 7.4.
- Don't issue a query at every connection to detect the client encoding
and to set the datestyle to ISO if it is already compatible with what
expected.
- `mogrify()` now supports unicode queries.
- Subclasses of a type that can be adapted are adapted as the superclass.
- `errorcodes` knows a couple of new codes introduced in PostgreSQL 9.0.
- Dropped deprecated Psycopg "own quoting".
- Never issue a ROLLBACK on close/GC. This behaviour was introduced as a bug
in release 2.2, but trying to send a command while being destroyed has been
considered not safe.
* Bug fixes:
- Fixed use of `PQfreemem` instead of `free` in binary typecaster.
- Fixed access to freed memory in `conn_get_isolation_level()`.
- Fixed crash during Decimal adaptation with a few 2.5.x Python versions
(ticket #7).
- Fixed notices order (ticket #9).
What's new in psycopg 2.2.2
---------------------------
* Bux fixes:
- the call to logging.basicConfig() in pool.py has been dropped: it was
messing with some projects using logging (and a library should not
initialize the logging system anyway.)
- psycopg now correctly handles time zones with seconds in the UTC offset.
The old register_tstz_w_secs() function is deprecated and will raise a
warning if called.
- Exceptions raised by the column iterator are propagated.
- Exceptions raised by executemany() interators are propagated.
What's new in psycopg 2.2.1
---------------------------
* Bux fixes:
- psycopg now builds again on MS Windows.
What's new in psycopg 2.2.0
---------------------------
This is the first release of the new 2.2 series, supporting not just one but
two different ways of executing asynchronous queries, thanks to Jan and Daniele
(with a little help from me and others, but they did 99% of the work so they
deserve their names here in the news.)
psycopg now supports both classic select() loops and "green" coroutine
libraries. It is all in the documentation, so just point your browser to
doc/html/advanced.html.
* Other new features:
- truncate() method for lobjects.
- COPY functions are now a little bit faster.
- All builtin PostgreSQL to Python typecasters are now available from the
psycopg2.extensions module.
- Notifications from the backend are now available right after the execute()
call (before client code needed to call isbusy() to ensure NOTIFY
reception.)
- Better timezone support.
- Lots of documentation updates.
* Bug fixes:
- Fixed some gc/refcounting problems.
- Fixed reference leak in NOTIFY reception.
- Fixed problem with PostgreSQL not casting string literals to the correct
types in some situations: psycopg now add an explicit cast to dates, times
and bytea representations.
- Fixed TimestampFromTicks() and TimeFromTicks() for seconds >= 59.5.
- Fixed spurious exception raised when calling C typecasters from Python
ones.
What's new in psycopg 2.0.14
----------------------------

129
NEWS-2.3
View File

@ -1,129 +0,0 @@
What's new in psycopg 2.3.3
---------------------------
* New features and changes:
- Added `register_composite()` function to cast PostgreSQL composite types
into Python tuples/namedtuples.
- The build script refuses to guess values if pg_config is not found.
- Connections and cursors are weakly referenceable.
* Bug fixes:
- Fixed adaptation of None in composite types (ticket #26). Bug report by
Karsten Hilbert.
- Fixed several reference leaks in less common code paths.
- Fixed segfault when a large object is closed and its connection no more
available.
- Added missing icon to ZPsycopgDA package, not available in Zope 2.12.9
(ticket #30). Bug report and patch by Pumukel.
What's new in psycopg 2.3.2
---------------------------
- Fixed segfault with middleware not passing DateStyle to the client
(ticket #24). Bug report and patch by Marti Raudsepp.
What's new in psycopg 2.3.1
---------------------------
- Fixed build problem on CentOS 5.5 x86_64 (ticket #23).
What's new in psycopg 2.3.0
---------------------------
psycopg 2.3 aims to expose some new features introduced in PostgreSQL 9.0.
* Main new features:
- `dict` to `hstore` adapter and `hstore` to `dict` typecaster, using both
9.0 and pre-9.0 syntax.
- Two-phase commit protocol support as per DBAPI specification.
- Support for payload in notifications received from the backed.
- `namedtuple`-returning cursor.
- Query execution cancel.
* Other features and changes:
- Dropped support for protocol 2: Psycopg 2.3 can only connect to PostgreSQL
servers with version at least 7.4.
- Don't issue a query at every connection to detect the client encoding
and to set the datestyle to ISO if it is already compatible with what
expected.
- `mogrify()` now supports unicode queries.
- Subclasses of a type that can be adapted are adapted as the superclass.
- `errorcodes` knows a couple of new codes introduced in PostgreSQL 9.0.
- Dropped deprecated Psycopg "own quoting".
- Never issue a ROLLBACK on close/GC. This behaviour was introduced as a bug
in release 2.2, but trying to send a command while being destroyed has been
considered not safe.
* Bug fixes:
- Fixed use of `PQfreemem` instead of `free` in binary typecaster.
- Fixed access to freed memory in `conn_get_isolation_level()`.
- Fixed crash during Decimal adaptation with a few 2.5.x Python versions
(ticket #7).
- Fixed notices order (ticket #9).
What's new in psycopg 2.2.2
---------------------------
* Bux fixes:
- the call to logging.basicConfig() in pool.py has been dropped: it was
messing with some projects using logging (and a library should not
initialize the logging system anyway.)
- psycopg now correctly handles time zones with seconds in the UTC offset.
The old register_tstz_w_secs() function is deprecated and will raise a
warning if called.
- Exceptions raised by the column iterator are propagated.
- Exceptions raised by executemany() interators are propagated.
What's new in psycopg 2.2.1
---------------------------
* Bux fixes:
- psycopg now builds again on MS Windows.
What's new in psycopg 2.2.0
---------------------------
This is the first release of the new 2.2 series, supporting not just one but
two different ways of executing asynchronous queries, thanks to Jan and Daniele
(with a little help from me and others, but they did 99% of the work so they
deserve their names here in the news.)
psycopg now supports both classic select() loops and "green" coroutine
libraries. It is all in the documentation, so just point your browser to
doc/html/advanced.html.
* Other new features:
- truncate() method for lobjects.
- COPY functions are now a little bit faster.
- All builtin PostgreSQL to Python typecasters are now available from the
psycopg2.extensions module.
- Notifications from the backend are now available right after the execute()
call (before client code needed to call isbusy() to ensure NOTIFY
reception.)
- Better timezone support.
- Lots of documentation updates.
* Bug fixes:
- Fixed some gc/refcounting problems.
- Fixed reference leak in NOTIFY reception.
- Fixed problem with PostgreSQL not casting string literals to the correct
types in some situations: psycopg now add an explicit cast to dates, times
and bytea representations.
- Fixed TimestampFromTicks() and TimeFromTicks() for seconds >= 59.5.
- Fixed spurious exception raised when calling C typecasters from Python
ones.

View File

@ -16,7 +16,7 @@
# their work without bothering about the module dependencies.
ALLOWED_PSYCOPG_VERSIONS = ('2.3.0','2.3.1','2.3.2')
ALLOWED_PSYCOPG_VERSIONS = ('2.4-beta1',)
import sys
import time

View File

@ -490,13 +490,14 @@ The ``connection`` class
.. method:: lobject([oid [, mode [, new_oid [, new_file [, lobject_factory]]]]])
Return a new database large object. See :ref:`large-objects` for an
overview.
Return a new database large object as a `~psycopg2.extensions.lobject`
instance.
See :ref:`large-objects` for an overview.
:param oid: The OID of the object to read or write. 0 to create
a new large object and and have its OID assigned automatically.
:param mode: Access mode to the object: can be ``r``, ``w``,
``rw`` or ``n`` (meaning don't open it).
:param mode: Access mode to the object, see below.
:param new_oid: Create a new object using the specified OID. The
function raises `OperationalError` if the OID is already in
use. Default is 0, meaning assign a new one automatically.
@ -504,13 +505,31 @@ The ``connection`` class
(using the |lo_import|_ function)
:param lobject_factory: Subclass of
`~psycopg2.extensions.lobject` to be instantiated.
:rtype: `~psycopg2.extensions.lobject`
.. |lo_import| replace:: `!lo_import()`
.. _lo_import: http://www.postgresql.org/docs/9.0/static/lo-interfaces.html#LO-IMPORT
Available values for *mode* are:
======= =========
*mode* meaning
======= =========
``r`` Open for read only
``w`` Open for write only
``rw`` Open for read/write
``n`` Don't open the file
``b`` Don't decode read data (return data as `str` in Python 2 or `bytes` in Python 3)
``t`` Decode read data according to `connection.encoding` (return data as `unicode` in Python 2 or `str` in Python 3)
======= =========
``b`` and ``t`` can be specified together with a read/write mode. If
neither ``b`` nor ``t`` is specified, the default is ``b`` in Python 2
and ``t`` in Python 3.
.. versionadded:: 2.0.8
.. versionchanged:: 2.3.3 added ``b`` and ``t`` mode and unicode
support.
.. rubric:: Methods related to asynchronous support.

View File

@ -208,6 +208,11 @@ The ``cursor`` class
(2, None, 'dada')
(3, 42, 'bar')
.. versionchanged:: 2.3.3
iterating over a :ref:`named cursor <server-side-cursors>`
fetches `~cursor.arraysize` records at time from the backend.
Previously only one record was fetched per roundtrip, resulting
in unefficient iteration.
.. method:: fetchone()
@ -300,6 +305,18 @@ The ``cursor`` class
This read/write attribute specifies the number of rows to fetch at a
time with `~cursor.fetchmany()`. It defaults to 1 meaning to fetch
a single row at a time.
The attribute is also used when iterating a :ref:`named cursor
<server-side-cursors>`: when syntax such as ``for i in cursor:`` is
used, in order to avoid an excessive number of network roundtrips, the
cursor will actually fetch `!arraysize` records at time from the
backend. For this task the default value of 1 is a poor value: if
`!arraysize` is 1, a default value of 2000 will be used instead. If
you really want to retrieve one record at time from the backend use
`fetchone()` in a loop.
.. versionchanged:: 2.3.3
`!arraysize` used in named cursor iteration.
.. attribute:: rowcount

View File

@ -51,17 +51,29 @@ functionalities defined by the |DBAPI|_.
.. attribute:: mode
The mode the database was open (``r``, ``w``, ``rw`` or ``n``).
The mode the database was open. See `connection.lobject()` for a
description of the available modes.
.. method:: read(bytes=-1)
Read a chunk of data from the current file position. If -1 (default)
read all the remaining data.
The result is an Unicode string (decoded according to
`connection.encoding`) if the file was open in ``t`` mode, a bytes
string for ``b`` mode.
.. versionchanged:: 2.3.3
added Unicode support.
.. method:: write(str)
Write a string to the large object. Return the number of bytes
written.
written. Unicode strings are encoded in the `connection.encoding`
before writing.
.. versionchanged:: 2.3.3
added Unicode support.
.. method:: export(file_name)

View File

@ -574,7 +574,8 @@ whole.
Psycopg allows access to the large object using the
`~psycopg2.extensions.lobject` class. Objects are generated using the
`connection.lobject()` factory method.
`connection.lobject()` factory method. Data can be retrieved either as bytes
or as Unicode strings.
Psycopg large object support efficient import/export with file system files
using the |lo_import|_ and |lo_export|_ libpq functions.

View File

@ -66,17 +66,17 @@ from psycopg2 import tz
# Import the DBAPI-2.0 stuff into top-level module.
from _psycopg import BINARY, NUMBER, STRING, DATETIME, ROWID
from psycopg2._psycopg import BINARY, NUMBER, STRING, DATETIME, ROWID
from _psycopg import Binary, Date, Time, Timestamp
from _psycopg import DateFromTicks, TimeFromTicks, TimestampFromTicks
from psycopg2._psycopg import Binary, Date, Time, Timestamp
from psycopg2._psycopg import DateFromTicks, TimeFromTicks, TimestampFromTicks
from _psycopg import Error, Warning, DataError, DatabaseError, ProgrammingError
from _psycopg import IntegrityError, InterfaceError, InternalError
from _psycopg import NotSupportedError, OperationalError
from psycopg2._psycopg import Error, Warning, DataError, DatabaseError, ProgrammingError
from psycopg2._psycopg import IntegrityError, InterfaceError, InternalError
from psycopg2._psycopg import NotSupportedError, OperationalError
from _psycopg import connect, apilevel, threadsafety, paramstyle
from _psycopg import __version__
from psycopg2._psycopg import connect, apilevel, threadsafety, paramstyle
from psycopg2._psycopg import __version__
# Register default adapters.

View File

@ -32,38 +32,38 @@ 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
# License for more details.
from _psycopg import UNICODE, INTEGER, LONGINTEGER, BOOLEAN, FLOAT
from _psycopg import TIME, DATE, INTERVAL, DECIMAL
from _psycopg import BINARYARRAY, BOOLEANARRAY, DATEARRAY, DATETIMEARRAY
from _psycopg import DECIMALARRAY, FLOATARRAY, INTEGERARRAY, INTERVALARRAY
from _psycopg import LONGINTEGERARRAY, ROWIDARRAY, STRINGARRAY, TIMEARRAY
from _psycopg import UNICODEARRAY
from psycopg2._psycopg import UNICODE, INTEGER, LONGINTEGER, BOOLEAN, FLOAT
from psycopg2._psycopg import TIME, DATE, INTERVAL, DECIMAL
from psycopg2._psycopg import BINARYARRAY, BOOLEANARRAY, DATEARRAY, DATETIMEARRAY
from psycopg2._psycopg import DECIMALARRAY, FLOATARRAY, INTEGERARRAY, INTERVALARRAY
from psycopg2._psycopg import LONGINTEGERARRAY, ROWIDARRAY, STRINGARRAY, TIMEARRAY
from psycopg2._psycopg import UNICODEARRAY
from _psycopg import Binary, Boolean, Float, QuotedString, AsIs
from psycopg2._psycopg import Binary, Boolean, Float, QuotedString, AsIs
try:
from _psycopg import MXDATE, MXDATETIME, MXINTERVAL, MXTIME
from _psycopg import MXDATEARRAY, MXDATETIMEARRAY, MXINTERVALARRAY, MXTIMEARRAY
from _psycopg import DateFromMx, TimeFromMx, TimestampFromMx
from _psycopg import IntervalFromMx
except:
from psycopg2._psycopg import MXDATE, MXDATETIME, MXINTERVAL, MXTIME
from psycopg2._psycopg import MXDATEARRAY, MXDATETIMEARRAY, MXINTERVALARRAY, MXTIMEARRAY
from psycopg2._psycopg import DateFromMx, TimeFromMx, TimestampFromMx
from psycopg2._psycopg import IntervalFromMx
except ImportError:
pass
try:
from _psycopg import PYDATE, PYDATETIME, PYINTERVAL, PYTIME
from _psycopg import PYDATEARRAY, PYDATETIMEARRAY, PYINTERVALARRAY, PYTIMEARRAY
from _psycopg import DateFromPy, TimeFromPy, TimestampFromPy
from _psycopg import IntervalFromPy
except:
from psycopg2._psycopg import PYDATE, PYDATETIME, PYINTERVAL, PYTIME
from psycopg2._psycopg import PYDATEARRAY, PYDATETIMEARRAY, PYINTERVALARRAY, PYTIMEARRAY
from psycopg2._psycopg import DateFromPy, TimeFromPy, TimestampFromPy
from psycopg2._psycopg import IntervalFromPy
except ImportError:
pass
from _psycopg import adapt, adapters, encodings, connection, cursor, lobject, Xid
from _psycopg import string_types, binary_types, new_type, register_type
from _psycopg import ISQLQuote, Notify
from psycopg2._psycopg import adapt, adapters, encodings, connection, cursor, lobject, Xid
from psycopg2._psycopg import string_types, binary_types, new_type, register_type
from psycopg2._psycopg import ISQLQuote, Notify
from _psycopg import QueryCanceledError, TransactionRollbackError
from psycopg2._psycopg import QueryCanceledError, TransactionRollbackError
try:
from _psycopg import set_wait_callback, get_wait_callback
from psycopg2._psycopg import set_wait_callback, get_wait_callback
except ImportError:
pass
@ -100,6 +100,16 @@ TRANSACTION_STATUS_INTRANS = 2
TRANSACTION_STATUS_INERROR = 3
TRANSACTION_STATUS_UNKNOWN = 4
import sys as _sys
# Return bytes from a string
if _sys.version_info[0] < 3:
def b(s):
return s
else:
def b(s):
return s.encode('utf8')
def register_adapter(typ, callable):
"""Register 'callable' as an ISQLQuote adapter for type 'typ'."""
adapters[(typ, ISQLQuote)] = callable
@ -122,10 +132,11 @@ class SQL_IN(object):
for obj in pobjs:
if hasattr(obj, 'prepare'):
obj.prepare(self._conn)
qobjs = [str(o.getquoted()) for o in pobjs]
return '(' + ', '.join(qobjs) + ')'
qobjs = [o.getquoted() for o in pobjs]
return b('(') + b(', ').join(qobjs) + b(')')
__str__ = getquoted
def __str__(self):
return str(self.getquoted())
class NoneAdapter(object):
@ -137,8 +148,17 @@ class NoneAdapter(object):
def __init__(self, obj):
pass
def getquoted(self):
return "NULL"
def getquoted(self, _null=b("NULL")):
return _null
# Add the "cleaned" version of the encodings to the key.
# When the encoding is set its name is cleaned up from - and _ and turned
# uppercase, so an encoding not respecting these rules wouldn't be found in the
# encodings keys and would raise an exception with the unicode typecaster
for k, v in encodings.items():
k = k.replace('_', '').replace('-', '').upper()
encodings[k] = v
__all__ = filter(lambda k: not k.startswith('_'), locals().keys())

View File

@ -26,6 +26,7 @@ and classes untill a better place in the distribution is found.
# License for more details.
import os
import sys
import time
import codecs
import warnings
@ -41,13 +42,14 @@ from psycopg2 import extensions as _ext
from psycopg2.extensions import cursor as _cursor
from psycopg2.extensions import connection as _connection
from psycopg2.extensions import adapt as _A
from psycopg2.extensions import b
class DictCursorBase(_cursor):
"""Base class for all dict-like cursors."""
def __init__(self, *args, **kwargs):
if kwargs.has_key('row_factory'):
if 'row_factory' in kwargs:
row_factory = kwargs['row_factory']
del kwargs['row_factory']
else:
@ -140,20 +142,17 @@ class DictRow(list):
self[:] = [None] * len(cursor.description)
def __getitem__(self, x):
if type(x) != int:
if not isinstance(x, (int, slice)):
x = self._index[x]
return list.__getitem__(self, x)
def __setitem__(self, x, v):
if type(x) != int:
if not isinstance(x, (int, slice)):
x = self._index[x]
list.__setitem__(self, x, v)
def items(self):
res = []
for n, v in self._index.items():
res.append((n, list.__getitem__(self, v)))
return res
return list(self.iteritems())
def keys(self):
return self._index.keys()
@ -162,7 +161,7 @@ class DictRow(list):
return tuple(self[:])
def has_key(self, x):
return self._index.has_key(x)
return x in self._index
def get(self, x, default=None):
try:
@ -171,7 +170,7 @@ class DictRow(list):
return default
def iteritems(self):
for n, v in self._index.items():
for n, v in self._index.iteritems():
yield n, list.__getitem__(self, v)
def iterkeys(self):
@ -181,10 +180,18 @@ class DictRow(list):
return list.__iter__(self)
def copy(self):
return dict(self.items())
return dict(self.iteritems())
def __contains__(self, x):
return self._index.__contains__(x)
return x in self._index
# grop the crusty Py2 methods
if sys.version_info[0] > 2:
items = iteritems; del iteritems
keys = iterkeys; del iterkeys
values = itervalues; del itervalues
del has_key
class RealDictConnection(_connection):
"""A connection that uses `RealDictCursor` automatically."""
@ -501,7 +508,7 @@ class Inet(object):
obj = _A(self.addr)
if hasattr(obj, 'prepare'):
obj.prepare(self._conn)
return obj.getquoted()+"::inet"
return obj.getquoted() + b("::inet")
def __conform__(self, foo):
if foo is _ext.ISQLQuote:
@ -568,7 +575,7 @@ class HstoreAdapter(object):
def _getquoted_8(self):
"""Use the operators available in PG pre-9.0."""
if not self.wrapped:
return "''::hstore"
return b("''::hstore")
adapt = _ext.adapt
rv = []
@ -582,22 +589,23 @@ class HstoreAdapter(object):
v.prepare(self.conn)
v = v.getquoted()
else:
v = 'NULL'
v = b('NULL')
rv.append("(%s => %s)" % (k, v))
# XXX this b'ing is painfully inefficient!
rv.append(b("(") + k + b(" => ") + v + b(")"))
return "(" + '||'.join(rv) + ")"
return b("(") + b('||').join(rv) + b(")")
def _getquoted_9(self):
"""Use the hstore(text[], text[]) function."""
if not self.wrapped:
return "''::hstore"
return b("''::hstore")
k = _ext.adapt(self.wrapped.keys())
k.prepare(self.conn)
v = _ext.adapt(self.wrapped.values())
v.prepare(self.conn)
return "hstore(%s, %s)" % (k.getquoted(), v.getquoted())
return b("hstore(") + k.getquoted() + b(", ") + v.getquoted() + b(")")
getquoted = _getquoted_9
@ -614,10 +622,8 @@ class HstoreAdapter(object):
(?:\s*,\s*|$) # pairs separated by comma or end of string.
""", regex.VERBOSE)
# backslash decoder
_bsdec = codecs.getdecoder("string_escape")
def parse(self, s, cur, _decoder=_bsdec):
@classmethod
def parse(self, s, cur, _bsdec=regex.compile(r"\\(.)")):
"""Parse an hstore representation in a Python string.
The hstore is represented as something like::
@ -635,10 +641,10 @@ class HstoreAdapter(object):
if m is None or m.start() != start:
raise psycopg2.InterfaceError(
"error parsing hstore pair at char %d" % start)
k = _decoder(m.group(1))[0]
k = _bsdec.sub(r'\1', m.group(1))
v = m.group(2)
if v is not None:
v = _decoder(v)[0]
v = _bsdec.sub(r'\1', v)
rv[k] = v
start = m.end()
@ -649,16 +655,14 @@ class HstoreAdapter(object):
return rv
parse = classmethod(parse)
@classmethod
def parse_unicode(self, s, cur):
"""Parse an hstore returning unicode keys and values."""
codec = codecs.getdecoder(_ext.encodings[cur.connection.encoding])
bsdec = self._bsdec
decoder = lambda s: codec(bsdec(s)[0])
return self.parse(s, cur, _decoder=decoder)
if s is None:
return None
parse_unicode = classmethod(parse_unicode)
s = s.decode(_ext.encodings[cur.connection.encoding])
return self.parse(s, cur)
@classmethod
def get_oids(self, conn_or_curs):
@ -704,11 +708,11 @@ def register_hstore(conn_or_curs, globally=False, unicode=False):
uses a single database you can pass *globally*\=True to have the typecaster
registered on all the connections.
By default the returned dicts will have `str` objects as keys and values:
On Python 2, by default the returned dicts will have `str` objects as keys and values:
use *unicode*\=True to return `unicode` objects instead. When adapting a
dictionary both `str` and `unicode` keys and values are handled (the
`unicode` values will be converted according to the current
`~connection.encoding`).
`~connection.encoding`). The option is not available on Python 3.
The |hstore| contrib module must be already installed in the database
(executing the ``hstore.sql`` script in your ``contrib`` directory).
@ -721,7 +725,7 @@ def register_hstore(conn_or_curs, globally=False, unicode=False):
"please install it from your 'contrib/hstore.sql' file")
# create and register the typecaster
if unicode:
if sys.version_info[0] < 3 and unicode:
cast = HstoreAdapter.parse_unicode
else:
cast = HstoreAdapter.parse

View File

@ -100,7 +100,7 @@ class AbstractConnectionPool(object):
if self.closed: raise PoolError("connection pool is closed")
if key is None: key = self._getkey()
if self._used.has_key(key):
if key in self._used:
return self._used[key]
if self._pool:

View File

@ -22,37 +22,44 @@
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*/
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <structmember.h>
#include <stringobject.h>
#include <string.h>
#define PSYCOPG_MODULE
#include "psycopg/config.h"
#include "psycopg/python.h"
#include "psycopg/psycopg.h"
#include "psycopg/adapter_asis.h"
#include "psycopg/microprotocols_proto.h"
#include <string.h>
/** the AsIs object **/
static PyObject *
asis_str(asisObject *self)
asis_getquoted(asisObject *self, PyObject *args)
{
PyObject *rv;
if (self->wrapped == Py_None) {
return PyString_FromString("NULL");
rv = Bytes_FromString("NULL");
}
else {
return PyObject_Str(self->wrapped);
rv = PyObject_Str(self->wrapped);
#if PY_MAJOR_VERSION > 2
/* unicode to bytes in Py3 */
if (rv) {
PyObject *tmp = PyUnicode_AsUTF8String(rv);
Py_DECREF(rv);
rv = tmp;
}
#endif
}
return rv;
}
static PyObject *
asis_getquoted(asisObject *self, PyObject *args)
asis_str(asisObject *self)
{
return asis_str(self);
return psycopg_ensure_text(asis_getquoted(self, NULL));
}
static PyObject *
@ -76,7 +83,7 @@ asis_conform(asisObject *self, PyObject *args)
/* object member list */
static struct PyMemberDef asisObject_members[] = {
{"adapted", T_OBJECT, offsetof(asisObject, wrapped), RO},
{"adapted", T_OBJECT, offsetof(asisObject, wrapped), READONLY},
{NULL}
};
@ -96,7 +103,7 @@ asis_setup(asisObject *self, PyObject *obj)
{
Dprintf("asis_setup: init asis object at %p, refcnt = "
FORMAT_CODE_PY_SSIZE_T,
self, ((PyObject *)self)->ob_refcnt
self, Py_REFCNT(self)
);
Py_INCREF(obj);
@ -104,7 +111,7 @@ asis_setup(asisObject *self, PyObject *obj)
Dprintf("asis_setup: good asis object at %p, refcnt = "
FORMAT_CODE_PY_SSIZE_T,
self, ((PyObject *)self)->ob_refcnt
self, Py_REFCNT(self)
);
return 0;
}
@ -125,10 +132,10 @@ asis_dealloc(PyObject* obj)
Dprintf("asis_dealloc: deleted asis object at %p, refcnt = "
FORMAT_CODE_PY_SSIZE_T,
obj, obj->ob_refcnt
obj, Py_REFCNT(obj)
);
obj->ob_type->tp_free(obj);
Py_TYPE(obj)->tp_free(obj);
}
static int
@ -167,8 +174,7 @@ asis_repr(asisObject *self)
"AsIs(str) -> new AsIs adapter object"
PyTypeObject asisType = {
PyObject_HEAD_INIT(NULL)
0,
PyVarObject_HEAD_INIT(NULL, 0)
"psycopg2._psycopg.AsIs",
sizeof(asisObject),
0,

View File

@ -26,11 +26,6 @@
#ifndef PSYCOPG_ASIS_H
#define PSYCOPG_ASIS_H 1
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include "psycopg/config.h"
#ifdef __cplusplus
extern "C" {
#endif

View File

@ -23,21 +23,15 @@
* License for more details.
*/
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <structmember.h>
#include <stringobject.h>
#include <libpq-fe.h>
#include <string.h>
#define PSYCOPG_MODULE
#include "psycopg/config.h"
#include "psycopg/python.h"
#include "psycopg/psycopg.h"
#include "psycopg/connection.h"
#include "psycopg/adapter_binary.h"
#include "psycopg/microprotocols_proto.h"
#include "psycopg/connection.h"
#include <string.h>
/** the quoting code */
@ -64,7 +58,14 @@ binary_quote(binaryObject *self)
size_t len = 0;
/* if we got a plain string or a buffer we escape it and save the buffer */
if (PyString_Check(self->wrapped) || PyBuffer_Check(self->wrapped)) {
if (Bytes_Check(self->wrapped)
#if PY_MAJOR_VERSION < 3
|| PyBuffer_Check(self->wrapped)
#else
|| PyByteArray_Check(self->wrapped)
|| PyMemoryView_Check(self->wrapped)
#endif
) {
/* escape and build quoted buffer */
if (PyObject_AsReadBuffer(self->wrapped, (const void **)&buffer,
&buffer_len) < 0)
@ -78,11 +79,11 @@ binary_quote(binaryObject *self)
}
if (len > 0)
self->buffer = PyString_FromFormat(
self->buffer = Bytes_FromFormat(
(self->conn && ((connectionObject*)self->conn)->equote)
? "E'%s'::bytea" : "'%s'::bytea" , to);
else
self->buffer = PyString_FromString("''::bytea");
self->buffer = Bytes_FromString("''::bytea");
PQfreemem(to);
}
@ -99,19 +100,21 @@ binary_quote(binaryObject *self)
/* binary_str, binary_getquoted - return result of quoting */
static PyObject *
binary_str(binaryObject *self)
binary_getquoted(binaryObject *self, PyObject *args)
{
if (self->buffer == NULL) {
binary_quote(self);
if (!(binary_quote(self))) {
return NULL;
}
}
Py_XINCREF(self->buffer);
Py_INCREF(self->buffer);
return self->buffer;
}
static PyObject *
binary_getquoted(binaryObject *self, PyObject *args)
binary_str(binaryObject *self)
{
return binary_str(self);
return psycopg_ensure_text(binary_getquoted(self, NULL));
}
static PyObject *
@ -153,8 +156,8 @@ binary_conform(binaryObject *self, PyObject *args)
/* object member list */
static struct PyMemberDef binaryObject_members[] = {
{"adapted", T_OBJECT, offsetof(binaryObject, wrapped), RO},
{"buffer", T_OBJECT, offsetof(binaryObject, buffer), RO},
{"adapted", T_OBJECT, offsetof(binaryObject, wrapped), READONLY},
{"buffer", T_OBJECT, offsetof(binaryObject, buffer), READONLY},
{NULL}
};
@ -176,7 +179,7 @@ binary_setup(binaryObject *self, PyObject *str)
{
Dprintf("binary_setup: init binary object at %p, refcnt = "
FORMAT_CODE_PY_SSIZE_T,
self, ((PyObject *)self)->ob_refcnt
self, Py_REFCNT(self)
);
self->buffer = NULL;
@ -186,7 +189,7 @@ binary_setup(binaryObject *self, PyObject *str)
Dprintf("binary_setup: good binary object at %p, refcnt = "
FORMAT_CODE_PY_SSIZE_T,
self, ((PyObject *)self)->ob_refcnt);
self, Py_REFCNT(self));
return 0;
}
@ -212,10 +215,10 @@ binary_dealloc(PyObject* obj)
Dprintf("binary_dealloc: deleted binary object at %p, refcnt = "
FORMAT_CODE_PY_SSIZE_T,
obj, obj->ob_refcnt
obj, Py_REFCNT(obj)
);
obj->ob_type->tp_free(obj);
Py_TYPE(obj)->tp_free(obj);
}
static int
@ -253,8 +256,7 @@ binary_repr(binaryObject *self)
"Binary(buffer) -> new binary object"
PyTypeObject binaryType = {
PyObject_HEAD_INIT(NULL)
0,
PyVarObject_HEAD_INIT(NULL, 0)
"psycopg2._psycopg.Binary",
sizeof(binaryObject),
0,

View File

@ -26,12 +26,6 @@
#ifndef PSYCOPG_BINARY_H
#define PSYCOPG_BINARY_H 1
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <libpq-fe.h>
#include "psycopg/config.h"
#ifdef __cplusplus
extern "C" {
#endif

View File

@ -23,21 +23,17 @@
* License for more details.
*/
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <structmember.h>
#include <stringobject.h>
#define PSYCOPG_MODULE
#include "psycopg/psycopg.h"
#include "psycopg/adapter_datetime.h"
#include "psycopg/microprotocols_proto.h"
#include <datetime.h>
#include <time.h>
#include <string.h>
#define PSYCOPG_MODULE
#include "psycopg/config.h"
#include "psycopg/python.h"
#include "psycopg/psycopg.h"
#include "psycopg/adapter_datetime.h"
#include "psycopg/microprotocols_proto.h"
extern HIDDEN PyObject *pyPsycopgTzModule;
extern HIDDEN PyObject *pyPsycopgTzLOCAL;
@ -59,59 +55,78 @@ psyco_adapter_datetime_init(void)
/* datetime_str, datetime_getquoted - return result of quoting */
static PyObject *
pydatetime_str(pydatetimeObject *self)
_pydatetime_string_date_time(pydatetimeObject *self)
{
PyObject *res = NULL;
PyObject *iso;
if (self->type <= PSYCO_DATETIME_TIMESTAMP) {
PyObject *tz;
PyObject *rv = NULL;
PyObject *iso = NULL;
PyObject *tz;
/* Select the right PG type to cast into. */
char *fmt = NULL;
switch (self->type) {
case PSYCO_DATETIME_TIME:
fmt = "'%s'::time";
break;
case PSYCO_DATETIME_DATE:
fmt = "'%s'::date";
break;
case PSYCO_DATETIME_TIMESTAMP:
tz = PyObject_GetAttrString(self->wrapped, "tzinfo");
if (!tz) { return NULL; }
fmt = (tz == Py_None) ? "'%s'::timestamp" : "'%s'::timestamptz";
Py_DECREF(tz);
break;
}
iso = PyObject_CallMethod(self->wrapped, "isoformat", NULL);
if (iso) {
res = PyString_FromFormat(fmt, PyString_AsString(iso));
Py_DECREF(iso);
}
return res;
/* Select the right PG type to cast into. */
char *fmt = NULL;
switch (self->type) {
case PSYCO_DATETIME_TIME:
fmt = "'%s'::time";
break;
case PSYCO_DATETIME_DATE:
fmt = "'%s'::date";
break;
case PSYCO_DATETIME_TIMESTAMP:
tz = PyObject_GetAttrString(self->wrapped, "tzinfo");
if (!tz) { goto error; }
fmt = (tz == Py_None) ? "'%s'::timestamp" : "'%s'::timestamptz";
Py_DECREF(tz);
break;
}
else {
PyDateTime_Delta *obj = (PyDateTime_Delta*)self->wrapped;
char buffer[8];
int i;
int a = obj->microseconds;
for (i=0; i < 6 ; i++) {
buffer[5-i] = '0' + (a % 10);
a /= 10;
}
buffer[6] = '\0';
return PyString_FromFormat("'%d days %d.%s seconds'::interval",
obj->days, obj->seconds, buffer);
if (!(iso = psycopg_ensure_bytes(
PyObject_CallMethod(self->wrapped, "isoformat", NULL)))) {
goto error;
}
rv = Bytes_FromFormat(fmt, Bytes_AsString(iso));
Py_DECREF(iso);
return rv;
error:
Py_XDECREF(iso);
return rv;
}
static PyObject *
_pydatetime_string_delta(pydatetimeObject *self)
{
PyDateTime_Delta *obj = (PyDateTime_Delta*)self->wrapped;
char buffer[8];
int i;
int a = obj->microseconds;
for (i=0; i < 6 ; i++) {
buffer[5-i] = '0' + (a % 10);
a /= 10;
}
buffer[6] = '\0';
return Bytes_FromFormat("'%d days %d.%s seconds'::interval",
obj->days, obj->seconds, buffer);
}
static PyObject *
pydatetime_getquoted(pydatetimeObject *self, PyObject *args)
{
return pydatetime_str(self);
if (self->type <= PSYCO_DATETIME_TIMESTAMP) {
return _pydatetime_string_date_time(self);
}
else {
return _pydatetime_string_delta(self);
}
}
static PyObject *
pydatetime_str(pydatetimeObject *self)
{
return psycopg_ensure_text(pydatetime_getquoted(self, NULL));
}
static PyObject *
@ -135,8 +150,8 @@ pydatetime_conform(pydatetimeObject *self, PyObject *args)
/* object member list */
static struct PyMemberDef pydatetimeObject_members[] = {
{"adapted", T_OBJECT, offsetof(pydatetimeObject, wrapped), RO},
{"type", T_INT, offsetof(pydatetimeObject, type), RO},
{"adapted", T_OBJECT, offsetof(pydatetimeObject, wrapped), READONLY},
{"type", T_INT, offsetof(pydatetimeObject, type), READONLY},
{NULL}
};
@ -156,7 +171,7 @@ pydatetime_setup(pydatetimeObject *self, PyObject *obj, int type)
{
Dprintf("pydatetime_setup: init datetime object at %p, refcnt = "
FORMAT_CODE_PY_SSIZE_T,
self, ((PyObject *)self)->ob_refcnt);
self, Py_REFCNT(self));
self->type = type;
Py_INCREF(obj);
@ -164,7 +179,7 @@ pydatetime_setup(pydatetimeObject *self, PyObject *obj, int type)
Dprintf("pydatetime_setup: good pydatetime object at %p, refcnt = "
FORMAT_CODE_PY_SSIZE_T,
self, ((PyObject *)self)->ob_refcnt);
self, Py_REFCNT(self));
return 0;
}
@ -185,9 +200,9 @@ pydatetime_dealloc(PyObject* obj)
Py_CLEAR(self->wrapped);
Dprintf("mpydatetime_dealloc: deleted pydatetime object at %p, "
"refcnt = " FORMAT_CODE_PY_SSIZE_T, obj, obj->ob_refcnt);
"refcnt = " FORMAT_CODE_PY_SSIZE_T, obj, Py_REFCNT(obj));
obj->ob_type->tp_free(obj);
Py_TYPE(obj)->tp_free(obj);
}
static int
@ -227,8 +242,7 @@ pydatetime_repr(pydatetimeObject *self)
"datetime(datetime, type) -> new datetime wrapper object"
PyTypeObject pydatetimeType = {
PyObject_HEAD_INIT(NULL)
0,
PyVarObject_HEAD_INIT(NULL, 0)
"psycopg2._psycopg.datetime",
sizeof(pydatetimeObject),
0,

View File

@ -26,11 +26,6 @@
#ifndef PSYCOPG_DATETIME_H
#define PSYCOPG_DATETIME_H 1
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include "psycopg/config.h"
#ifdef __cplusplus
extern "C" {
#endif

View File

@ -23,15 +23,9 @@
* License for more details.
*/
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <structmember.h>
#include <stringobject.h>
#define PSYCOPG_MODULE
#include "psycopg/config.h"
#include "psycopg/python.h"
#include "psycopg/psycopg.h"
#include "psycopg/adapter_list.h"
#include "psycopg/microprotocols.h"
#include "psycopg/microprotocols_proto.h"
@ -51,7 +45,7 @@ list_quote(listObject *self)
/* empty arrays are converted to NULLs (still searching for a way to
insert an empty array in postgresql */
if (len == 0) return PyString_FromString("'{}'");
if (len == 0) return Bytes_FromString("'{}'::text[]");
tmp = PyTuple_New(len);
@ -59,7 +53,7 @@ list_quote(listObject *self)
PyObject *quoted;
PyObject *wrapped = PyList_GET_ITEM(self->wrapped, i);
if (wrapped == Py_None)
quoted = PyString_FromString("NULL");
quoted = Bytes_FromString("NULL");
else
quoted = microprotocol_getquoted(wrapped,
(connectionObject*)self->connection);
@ -73,11 +67,11 @@ list_quote(listObject *self)
/* now that we have a tuple of adapted objects we just need to join them
and put "ARRAY[] around the result */
str = PyString_FromString(", ");
str = Bytes_FromString(", ");
joined = PyObject_CallMethod(str, "join", "(O)", tmp);
if (joined == NULL) goto error;
res = PyString_FromFormat("ARRAY[%s]", PyString_AsString(joined));
res = Bytes_FromFormat("ARRAY[%s]", Bytes_AsString(joined));
error:
Py_XDECREF(tmp);
@ -89,7 +83,7 @@ list_quote(listObject *self)
static PyObject *
list_str(listObject *self)
{
return list_quote(self);
return psycopg_ensure_text(list_quote(self));
}
static PyObject *
@ -139,7 +133,7 @@ list_conform(listObject *self, PyObject *args)
/* object member list */
static struct PyMemberDef listObject_members[] = {
{"adapted", T_OBJECT, offsetof(listObject, wrapped), RO},
{"adapted", T_OBJECT, offsetof(listObject, wrapped), READONLY},
{NULL}
};
@ -161,7 +155,7 @@ list_setup(listObject *self, PyObject *obj, const char *enc)
{
Dprintf("list_setup: init list object at %p, refcnt = "
FORMAT_CODE_PY_SSIZE_T,
self, ((PyObject *)self)->ob_refcnt
self, Py_REFCNT(self)
);
if (!PyList_Check(obj))
@ -176,7 +170,7 @@ list_setup(listObject *self, PyObject *obj, const char *enc)
Dprintf("list_setup: good list object at %p, refcnt = "
FORMAT_CODE_PY_SSIZE_T,
self, ((PyObject *)self)->ob_refcnt
self, Py_REFCNT(self)
);
return 0;
}
@ -201,9 +195,9 @@ list_dealloc(PyObject* obj)
if (self->encoding) free(self->encoding);
Dprintf("list_dealloc: deleted list object at %p, "
"refcnt = " FORMAT_CODE_PY_SSIZE_T, obj, obj->ob_refcnt);
"refcnt = " FORMAT_CODE_PY_SSIZE_T, obj, Py_REFCNT(obj));
obj->ob_type->tp_free(obj);
Py_TYPE(obj)->tp_free(obj);
}
static int
@ -242,8 +236,7 @@ list_repr(listObject *self)
"List(list) -> new list wrapper object"
PyTypeObject listType = {
PyObject_HEAD_INIT(NULL)
0,
PyVarObject_HEAD_INIT(NULL, 0)
"psycopg2._psycopg.List",
sizeof(listObject),
0,

View File

@ -26,11 +26,6 @@
#ifndef PSYCOPG_LIST_H
#define PSYCOPG_LIST_H 1
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include "psycopg/config.h"
#ifdef __cplusplus
extern "C" {
#endif

View File

@ -23,19 +23,16 @@
* License for more details.
*/
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <structmember.h>
#include <stringobject.h>
#define PSYCOPG_MODULE
#include "psycopg/psycopg.h"
/* TODO: check if still compiles ok: I have no mx on this box */
#include "psycopg/adapter_mxdatetime.h"
#include "psycopg/microprotocols_proto.h"
#include <mxDateTime.h>
#include <string.h>
#define PSYCOPG_MODULE
#include "psycopg/config.h"
#include "psycopg/python.h"
#include "psycopg/psycopg.h"
#include "psycopg/adapter_mxdatetime.h"
#include "psycopg/microprotocols_proto.h"
int
psyco_adapter_mxdatetime_init(void)
@ -138,8 +135,8 @@ mxdatetime_conform(mxdatetimeObject *self, PyObject *args)
/* object member list */
static struct PyMemberDef mxdatetimeObject_members[] = {
{"adapted", T_OBJECT, offsetof(mxdatetimeObject, wrapped), RO},
{"type", T_INT, offsetof(mxdatetimeObject, type), RO},
{"adapted", T_OBJECT, offsetof(mxdatetimeObject, wrapped), READONLY},
{"type", T_INT, offsetof(mxdatetimeObject, type), READONLY},
{NULL}
};
@ -159,7 +156,7 @@ mxdatetime_setup(mxdatetimeObject *self, PyObject *obj, int type)
{
Dprintf("mxdatetime_setup: init mxdatetime object at %p, refcnt = "
FORMAT_CODE_PY_SSIZE_T,
self, ((PyObject *)self)->ob_refcnt
self, Py_REFCNT(self)
);
self->type = type;
@ -168,7 +165,7 @@ mxdatetime_setup(mxdatetimeObject *self, PyObject *obj, int type)
Dprintf("mxdatetime_setup: good mxdatetime object at %p, refcnt = "
FORMAT_CODE_PY_SSIZE_T,
self, ((PyObject *)self)->ob_refcnt
self, Py_REFCNT(self)
);
return 0;
}
@ -191,10 +188,10 @@ mxdatetime_dealloc(PyObject* obj)
Dprintf("mxdatetime_dealloc: deleted mxdatetime object at %p, refcnt = "
FORMAT_CODE_PY_SSIZE_T,
obj, obj->ob_refcnt
obj, Py_REFCNT(obj)
);
obj->ob_type->tp_free(obj);
Py_TYPE(obj)->tp_free(obj);
}
static int
@ -234,8 +231,7 @@ mxdatetime_repr(mxdatetimeObject *self)
"MxDateTime(mx, type) -> new mx.DateTime wrapper object"
PyTypeObject mxdatetimeType = {
PyObject_HEAD_INIT(NULL)
0,
PyVarObject_HEAD_INIT(NULL, 0)
"psycopg2._psycopg.MxDateTime",
sizeof(mxdatetimeObject),
0,

View File

@ -26,11 +26,6 @@
#ifndef PSYCOPG_MXDATETIME_H
#define PSYCOPG_MXDATETIME_H 1
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include "psycopg/config.h"
#ifdef __cplusplus
extern "C" {
#endif

View File

@ -23,46 +23,41 @@
* License for more details.
*/
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <structmember.h>
#include <stringobject.h>
#include <string.h>
#define PSYCOPG_MODULE
#include "psycopg/config.h"
#include "psycopg/python.h"
#include "psycopg/psycopg.h"
#include "psycopg/adapter_pboolean.h"
#include "psycopg/microprotocols_proto.h"
#include <string.h>
/** the Boolean object **/
static PyObject *
pboolean_str(pbooleanObject *self)
pboolean_getquoted(pbooleanObject *self, PyObject *args)
{
#ifdef PSYCOPG_NEW_BOOLEAN
if (PyObject_IsTrue(self->wrapped)) {
return PyString_FromString("true");
return Bytes_FromString("true");
}
else {
return PyString_FromString("false");
return Bytes_FromString("false");
}
#else
if (PyObject_IsTrue(self->wrapped)) {
return PyString_FromString("'t'");
return Bytes_FromString("'t'");
}
else {
return PyString_FromString("'f'");
return Bytes_FromString("'f'");
}
#endif
}
static PyObject *
pboolean_getquoted(pbooleanObject *self, PyObject *args)
pboolean_str(pbooleanObject *self)
{
return pboolean_str(self);
return psycopg_ensure_text(pboolean_getquoted(self, NULL));
}
static PyObject *
@ -86,7 +81,7 @@ pboolean_conform(pbooleanObject *self, PyObject *args)
/* object member list */
static struct PyMemberDef pbooleanObject_members[] = {
{"adapted", T_OBJECT, offsetof(pbooleanObject, wrapped), RO},
{"adapted", T_OBJECT, offsetof(pbooleanObject, wrapped), READONLY},
{NULL}
};
@ -106,7 +101,7 @@ pboolean_setup(pbooleanObject *self, PyObject *obj)
{
Dprintf("pboolean_setup: init pboolean object at %p, refcnt = "
FORMAT_CODE_PY_SSIZE_T,
self, ((PyObject *)self)->ob_refcnt
self, Py_REFCNT(self)
);
Py_INCREF(obj);
@ -114,7 +109,7 @@ pboolean_setup(pbooleanObject *self, PyObject *obj)
Dprintf("pboolean_setup: good pboolean object at %p, refcnt = "
FORMAT_CODE_PY_SSIZE_T,
self, ((PyObject *)self)->ob_refcnt
self, Py_REFCNT(self)
);
return 0;
}
@ -137,10 +132,10 @@ pboolean_dealloc(PyObject* obj)
Dprintf("pboolean_dealloc: deleted pboolean object at %p, refcnt = "
FORMAT_CODE_PY_SSIZE_T,
obj, obj->ob_refcnt
obj, Py_REFCNT(obj)
);
obj->ob_type->tp_free(obj);
Py_TYPE(obj)->tp_free(obj);
}
static int
@ -180,8 +175,7 @@ pboolean_repr(pbooleanObject *self)
"Boolean(str) -> new Boolean adapter object"
PyTypeObject pbooleanType = {
PyObject_HEAD_INIT(NULL)
0,
PyVarObject_HEAD_INIT(NULL, 0)
"psycopg2._psycopg.Boolean",
sizeof(pbooleanObject),
0,

View File

@ -26,11 +26,6 @@
#ifndef PSYCOPG_PBOOLEAN_H
#define PSYCOPG_PBOOLEAN_H 1
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include "psycopg/config.h"
#ifdef __cplusplus
extern "C" {
#endif

View File

@ -23,24 +23,20 @@
* License for more details.
*/
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <structmember.h>
#include <floatobject.h>
#include <math.h>
#define PSYCOPG_MODULE
#include "psycopg/config.h"
#include "psycopg/python.h"
#include "psycopg/psycopg.h"
#include "psycopg/adapter_pdecimal.h"
#include "psycopg/microprotocols_proto.h"
#include <floatobject.h>
#include <math.h>
/** the Decimal object **/
static PyObject *
pdecimal_str(pdecimalObject *self)
pdecimal_getquoted(pdecimalObject *self, PyObject *args)
{
PyObject *check, *res = NULL;
check = PyObject_CallMethod(self->wrapped, "is_finite", NULL);
@ -49,7 +45,7 @@ pdecimal_str(pdecimalObject *self)
goto end;
}
else if (check) {
res = PyString_FromString("'NaN'::numeric");
res = Bytes_FromString("'NaN'::numeric");
goto end;
}
@ -61,7 +57,7 @@ pdecimal_str(pdecimalObject *self)
goto end;
}
if (PyObject_IsTrue(check)) {
res = PyString_FromString("'NaN'::numeric");
res = Bytes_FromString("'NaN'::numeric");
goto end;
}
@ -70,11 +66,19 @@ pdecimal_str(pdecimalObject *self)
goto end;
}
if (PyObject_IsTrue(check)) {
res = PyString_FromString("'NaN'::numeric");
res = Bytes_FromString("'NaN'::numeric");
goto end;
}
res = PyObject_Str(self->wrapped);
#if PY_MAJOR_VERSION > 2
/* unicode to bytes in Py3 */
if (res) {
PyObject *tmp = PyUnicode_AsUTF8String(res);
Py_DECREF(res);
res = tmp;
}
#endif
end:
Py_XDECREF(check);
@ -82,9 +86,9 @@ end:
}
static PyObject *
pdecimal_getquoted(pdecimalObject *self, PyObject *args)
pdecimal_str(pdecimalObject *self)
{
return pdecimal_str(self);
return psycopg_ensure_text(pdecimal_getquoted(self, NULL));
}
static PyObject *
@ -108,7 +112,7 @@ pdecimal_conform(pdecimalObject *self, PyObject *args)
/* object member list */
static struct PyMemberDef pdecimalObject_members[] = {
{"adapted", T_OBJECT, offsetof(pdecimalObject, wrapped), RO},
{"adapted", T_OBJECT, offsetof(pdecimalObject, wrapped), READONLY},
{NULL}
};
@ -128,7 +132,7 @@ pdecimal_setup(pdecimalObject *self, PyObject *obj)
{
Dprintf("pdecimal_setup: init pdecimal object at %p, refcnt = "
FORMAT_CODE_PY_SSIZE_T,
self, ((PyObject *)self)->ob_refcnt
self, Py_REFCNT(self)
);
Py_INCREF(obj);
@ -136,7 +140,7 @@ pdecimal_setup(pdecimalObject *self, PyObject *obj)
Dprintf("pdecimal_setup: good pdecimal object at %p, refcnt = "
FORMAT_CODE_PY_SSIZE_T,
self, ((PyObject *)self)->ob_refcnt
self, Py_REFCNT(self)
);
return 0;
}
@ -159,10 +163,10 @@ pdecimal_dealloc(PyObject* obj)
Dprintf("pdecimal_dealloc: deleted pdecimal object at %p, refcnt = "
FORMAT_CODE_PY_SSIZE_T,
obj, obj->ob_refcnt
obj, Py_REFCNT(obj)
);
obj->ob_type->tp_free(obj);
Py_TYPE(obj)->tp_free(obj);
}
static int
@ -202,8 +206,7 @@ pdecimal_repr(pdecimalObject *self)
"Decimal(str) -> new Decimal adapter object"
PyTypeObject pdecimalType = {
PyObject_HEAD_INIT(NULL)
0,
PyVarObject_HEAD_INIT(NULL, 0)
"psycopg2._psycopg.Decimal",
sizeof(pdecimalObject),
0,

View File

@ -26,11 +26,6 @@
#ifndef PSYCOPG_PDECIMAL_H
#define PSYCOPG_PDECIMAL_H 1
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include "psycopg/config.h"
#ifdef __cplusplus
extern "C" {
#endif

View File

@ -23,38 +23,47 @@
* License for more details.
*/
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <structmember.h>
#include <floatobject.h>
#include <math.h>
#define PSYCOPG_MODULE
#include "psycopg/config.h"
#include "psycopg/python.h"
#include "psycopg/psycopg.h"
#include "psycopg/adapter_pfloat.h"
#include "psycopg/microprotocols_proto.h"
#include <floatobject.h>
#include <math.h>
/** the Float object **/
static PyObject *
pfloat_str(pfloatObject *self)
pfloat_getquoted(pfloatObject *self, PyObject *args)
{
PyObject *rv;
double n = PyFloat_AsDouble(self->wrapped);
if (isnan(n))
return PyString_FromString("'NaN'::float");
rv = Bytes_FromString("'NaN'::float");
else if (isinf(n))
return PyString_FromString("'Infinity'::float");
else
return PyObject_Repr(self->wrapped);
rv = Bytes_FromString("'Infinity'::float");
else {
rv = PyObject_Repr(self->wrapped);
#if PY_MAJOR_VERSION > 2
/* unicode to bytes in Py3 */
if (rv) {
PyObject *tmp = PyUnicode_AsUTF8String(rv);
Py_DECREF(rv);
rv = tmp;
}
#endif
}
return rv;
}
static PyObject *
pfloat_getquoted(pfloatObject *self, PyObject *args)
pfloat_str(pfloatObject *self)
{
return pfloat_str(self);
return psycopg_ensure_text(pfloat_getquoted(self, NULL));
}
static PyObject *
@ -78,7 +87,7 @@ pfloat_conform(pfloatObject *self, PyObject *args)
/* object member list */
static struct PyMemberDef pfloatObject_members[] = {
{"adapted", T_OBJECT, offsetof(pfloatObject, wrapped), RO},
{"adapted", T_OBJECT, offsetof(pfloatObject, wrapped), READONLY},
{NULL}
};
@ -98,7 +107,7 @@ pfloat_setup(pfloatObject *self, PyObject *obj)
{
Dprintf("pfloat_setup: init pfloat object at %p, refcnt = "
FORMAT_CODE_PY_SSIZE_T,
self, ((PyObject *)self)->ob_refcnt
self, Py_REFCNT(self)
);
Py_INCREF(obj);
@ -106,7 +115,7 @@ pfloat_setup(pfloatObject *self, PyObject *obj)
Dprintf("pfloat_setup: good pfloat object at %p, refcnt = "
FORMAT_CODE_PY_SSIZE_T,
self, ((PyObject *)self)->ob_refcnt
self, Py_REFCNT(self)
);
return 0;
}
@ -129,10 +138,10 @@ pfloat_dealloc(PyObject* obj)
Dprintf("pfloat_dealloc: deleted pfloat object at %p, refcnt = "
FORMAT_CODE_PY_SSIZE_T,
obj, obj->ob_refcnt
obj, Py_REFCNT(obj)
);
obj->ob_type->tp_free(obj);
Py_TYPE(obj)->tp_free(obj);
}
static int
@ -172,8 +181,7 @@ pfloat_repr(pfloatObject *self)
"Float(str) -> new Float adapter object"
PyTypeObject pfloatType = {
PyObject_HEAD_INIT(NULL)
0,
PyVarObject_HEAD_INIT(NULL, 0)
"psycopg2._psycopg.Float",
sizeof(pfloatObject),
0,

View File

@ -26,11 +26,6 @@
#ifndef PSYCOPG_PFLOAT_H
#define PSYCOPG_PFLOAT_H 1
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include "psycopg/config.h"
#ifdef __cplusplus
extern "C" {
#endif

View File

@ -23,22 +23,15 @@
* License for more details.
*/
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <structmember.h>
#include <stringobject.h>
#include <libpq-fe.h>
#include <string.h>
#define PSYCOPG_MODULE
#include "psycopg/config.h"
#include "psycopg/python.h"
#include "psycopg/psycopg.h"
#include "psycopg/connection.h"
#include "psycopg/adapter_qstring.h"
#include "psycopg/microprotocols_proto.h"
#include <string.h>
/* qstring_quote - do the quote process on plain and unicode strings */
@ -56,24 +49,12 @@ qstring_quote(qstringObject *self)
Dprintf("qstring_quote: encoding to %s", self->encoding);
if (PyUnicode_Check(self->wrapped) && self->encoding) {
PyObject *enc = PyDict_GetItemString(psycoEncodings, self->encoding);
/* note that enc is a borrowed reference */
if (enc) {
const char *s = PyString_AsString(enc);
Dprintf("qstring_quote: encoding unicode object to %s", s);
str = PyUnicode_AsEncodedString(self->wrapped, s, NULL);
Dprintf("qstring_quote: got encoded object at %p", str);
if (str == NULL) return NULL;
}
else {
/* can't find the right encoder, raise exception */
PyErr_Format(InterfaceError,
"can't encode unicode string to %s", self->encoding);
return NULL;
}
str = PyUnicode_AsEncodedString(self->wrapped, self->encoding, NULL);
Dprintf("qstring_quote: got encoded object at %p", str);
if (str == NULL) return NULL;
}
#if PY_MAJOR_VERSION < 3
/* if the wrapped object is a simple string, we don't know how to
(re)encode it, so we pass it as-is */
else if (PyString_Check(self->wrapped)) {
@ -81,6 +62,7 @@ qstring_quote(qstringObject *self)
/* INCREF to make it ref-wise identical to unicode one */
Py_INCREF(str);
}
#endif
/* if the wrapped object is not a string, this is an error */
else {
@ -90,7 +72,7 @@ qstring_quote(qstringObject *self)
}
/* encode the string into buffer */
PyString_AsStringAndSize(str, &s, &len);
Bytes_AsStringAndSize(str, &s, &len);
/* Call qstring_escape with the GIL released, then reacquire the GIL
before verifying that the results can fit into a Python string; raise
@ -113,8 +95,8 @@ qstring_quote(qstringObject *self)
Py_DECREF(str);
return NULL;
}
self->buffer = PyString_FromStringAndSize(buffer, qlen);
self->buffer = Bytes_FromStringAndSize(buffer, qlen);
PyMem_Free(buffer);
Py_DECREF(str);
@ -124,7 +106,7 @@ qstring_quote(qstringObject *self)
/* qstring_str, qstring_getquoted - return result of quoting */
static PyObject *
qstring_str(qstringObject *self)
qstring_getquoted(qstringObject *self, PyObject *args)
{
if (self->buffer == NULL) {
qstring_quote(self);
@ -134,9 +116,9 @@ qstring_str(qstringObject *self)
}
static PyObject *
qstring_getquoted(qstringObject *self, PyObject *args)
qstring_str(qstringObject *self)
{
return qstring_str(self);
return psycopg_ensure_text(qstring_getquoted(self, NULL));
}
static PyObject *
@ -151,8 +133,8 @@ qstring_prepare(qstringObject *self, PyObject *args)
we don't need the encoding if that's not the case */
if (PyUnicode_Check(self->wrapped)) {
if (self->encoding) free(self->encoding);
self->encoding = strdup(conn->encoding);
Dprintf("qstring_prepare: set encoding to %s", conn->encoding);
self->encoding = strdup(conn->codec);
Dprintf("qstring_prepare: set encoding to %s", conn->codec);
}
Py_CLEAR(self->conn);
@ -186,9 +168,9 @@ qstring_conform(qstringObject *self, PyObject *args)
/* object member list */
static struct PyMemberDef qstringObject_members[] = {
{"adapted", T_OBJECT, offsetof(qstringObject, wrapped), RO},
{"buffer", T_OBJECT, offsetof(qstringObject, buffer), RO},
{"encoding", T_STRING, offsetof(qstringObject, encoding), RO},
{"adapted", T_OBJECT, offsetof(qstringObject, wrapped), READONLY},
{"buffer", T_OBJECT, offsetof(qstringObject, buffer), READONLY},
{"encoding", T_STRING, offsetof(qstringObject, encoding), READONLY},
{NULL}
};
@ -210,7 +192,7 @@ qstring_setup(qstringObject *self, PyObject *str, const char *enc)
{
Dprintf("qstring_setup: init qstring object at %p, refcnt = "
FORMAT_CODE_PY_SSIZE_T,
self, ((PyObject *)self)->ob_refcnt
self, Py_REFCNT(self)
);
self->buffer = NULL;
@ -224,7 +206,7 @@ qstring_setup(qstringObject *self, PyObject *str, const char *enc)
Dprintf("qstring_setup: good qstring object at %p, refcnt = "
FORMAT_CODE_PY_SSIZE_T,
self, ((PyObject *)self)->ob_refcnt
self, Py_REFCNT(self)
);
return 0;
}
@ -253,10 +235,10 @@ qstring_dealloc(PyObject* obj)
Dprintf("qstring_dealloc: deleted qstring object at %p, refcnt = "
FORMAT_CODE_PY_SSIZE_T,
obj, obj->ob_refcnt
obj, Py_REFCNT(obj)
);
obj->ob_type->tp_free(obj);
Py_TYPE(obj)->tp_free(obj);
}
static int
@ -296,8 +278,7 @@ qstring_repr(qstringObject *self)
"QuotedString(str, enc) -> new quoted object with 'enc' encoding"
PyTypeObject qstringType = {
PyObject_HEAD_INIT(NULL)
0,
PyVarObject_HEAD_INIT(NULL, 0)
"psycopg2._psycopg.QuotedString",
sizeof(qstringObject),
0,

View File

@ -26,11 +26,6 @@
#ifndef PSYCOPG_QSTRING_H
#define PSYCOPG_QSTRING_H 1
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include "psycopg/config.h"
#ifdef __cplusplus
extern "C" {
#endif
@ -42,6 +37,10 @@ typedef struct {
PyObject *wrapped;
PyObject *buffer;
/* NOTE: this used to be a PostgreSQL encoding: changed in 2.3.2 to be a
* Python codec name. I don't expect there has been any user for this
* object other than adapting str/unicode, so I don't expect client code
* broken for this reason. */
char *encoding;
PyObject *conn;

299
psycopg/bytes_format.c Normal file
View File

@ -0,0 +1,299 @@
/* bytes_format.c - bytes-oriented version of PyString_Format
*
* Copyright (C) 2010 Daniele Varrazzo <daniele.varrazzo@gmail.com>
*
* This file is part of psycopg.
*
* psycopg2 is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* In addition, as a special exception, the copyright holders give
* permission to link this program with the OpenSSL library (or with
* modified versions of OpenSSL that use the same license as OpenSSL),
* and distribute linked combinations including the two.
*
* You must obey the GNU Lesser General Public License in all respects for
* all of the code used other than OpenSSL.
*
* psycopg2 is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*/
/* This implementation is based on the PyString_Format function available in
* Python 2.7.1. The function is altered to be used with both Python 2 strings
* and Python 3 bytes and is stripped of the support of formats different than
* 's'. Original license follows.
*
* PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
* --------------------------------------------
*
* 1. This LICENSE AGREEMENT is between the Python Software Foundation
* ("PSF"), and the Individual or Organization ("Licensee") accessing and
* otherwise using this software ("Python") in source or binary form and
* its associated documentation.
*
* 2. Subject to the terms and conditions of this License Agreement, PSF hereby
* grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce,
* analyze, test, perform and/or display publicly, prepare derivative works,
* distribute, and otherwise use Python alone or in any derivative version,
* provided, however, that PSF's License Agreement and PSF's notice of copyright,
* i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
* Python Software Foundation; All Rights Reserved" are retained in Python alone or
* in any derivative version prepared by Licensee.
*
* 3. In the event Licensee prepares a derivative work that is based on
* or incorporates Python or any part thereof, and wants to make
* the derivative work available to others as provided herein, then
* Licensee hereby agrees to include in any such work a brief summary of
* the changes made to Python.
*
* 4. PSF is making Python available to Licensee on an "AS IS"
* basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
* IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND
* DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
* FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT
* INFRINGE ANY THIRD PARTY RIGHTS.
*
* 5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON
* FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
* A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON,
* OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
*
* 6. This License Agreement will automatically terminate upon a material
* breach of its terms and conditions.
*
* 7. Nothing in this License Agreement shall be deemed to create any
* relationship of agency, partnership, or joint venture between PSF and
* Licensee. This License Agreement does not grant permission to use PSF
* trademarks or trade name in a trademark sense to endorse or promote
* products or services of Licensee, or any third party.
*
* 8. By copying, installing or otherwise using Python, Licensee
* agrees to be bound by the terms and conditions of this License
* Agreement.
*/
#define PSYCOPG_MODULE
#include "psycopg/psycopg.h"
#ifndef Py_LOCAL_INLINE
#define Py_LOCAL_INLINE(type) static type
#endif
/* Helpers for formatstring */
Py_LOCAL_INLINE(PyObject *)
getnextarg(PyObject *args, Py_ssize_t arglen, Py_ssize_t *p_argidx)
{
Py_ssize_t argidx = *p_argidx;
if (argidx < arglen) {
(*p_argidx)++;
if (arglen < 0)
return args;
else
return PyTuple_GetItem(args, argidx);
}
PyErr_SetString(PyExc_TypeError,
"not enough arguments for format string");
return NULL;
}
/* fmt%(v1,v2,...) is roughly equivalent to sprintf(fmt, v1, v2, ...) */
PyObject *
Bytes_Format(PyObject *format, PyObject *args)
{
char *fmt, *res;
Py_ssize_t arglen, argidx;
Py_ssize_t reslen, rescnt, fmtcnt;
int args_owned = 0;
PyObject *result, *orig_args;
PyObject *dict = NULL;
if (format == NULL || !Bytes_Check(format) || args == NULL) {
PyErr_BadInternalCall();
return NULL;
}
orig_args = args;
fmt = Bytes_AS_STRING(format);
fmtcnt = Bytes_GET_SIZE(format);
reslen = rescnt = fmtcnt + 100;
result = Bytes_FromStringAndSize((char *)NULL, reslen);
if (result == NULL)
return NULL;
res = Bytes_AsString(result);
if (PyTuple_Check(args)) {
arglen = PyTuple_GET_SIZE(args);
argidx = 0;
}
else {
arglen = -1;
argidx = -2;
}
if (Py_TYPE(args)->tp_as_mapping && !PyTuple_Check(args) &&
!PyObject_TypeCheck(args, &Bytes_Type))
dict = args;
while (--fmtcnt >= 0) {
if (*fmt != '%') {
if (--rescnt < 0) {
rescnt = fmtcnt + 100;
reslen += rescnt;
if (_Bytes_Resize(&result, reslen))
return NULL;
res = Bytes_AS_STRING(result)
+ reslen - rescnt;
--rescnt;
}
*res++ = *fmt++;
}
else {
/* Got a format specifier */
Py_ssize_t width = -1;
int c = '\0';
PyObject *v = NULL;
PyObject *temp = NULL;
char *pbuf;
Py_ssize_t len;
fmt++;
if (*fmt == '(') {
char *keystart;
Py_ssize_t keylen;
PyObject *key;
int pcount = 1;
if (dict == NULL) {
PyErr_SetString(PyExc_TypeError,
"format requires a mapping");
goto error;
}
++fmt;
--fmtcnt;
keystart = fmt;
/* Skip over balanced parentheses */
while (pcount > 0 && --fmtcnt >= 0) {
if (*fmt == ')')
--pcount;
else if (*fmt == '(')
++pcount;
fmt++;
}
keylen = fmt - keystart - 1;
if (fmtcnt < 0 || pcount > 0) {
PyErr_SetString(PyExc_ValueError,
"incomplete format key");
goto error;
}
key = Text_FromUTF8AndSize(keystart, keylen);
if (key == NULL)
goto error;
if (args_owned) {
Py_DECREF(args);
args_owned = 0;
}
args = PyObject_GetItem(dict, key);
Py_DECREF(key);
if (args == NULL) {
goto error;
}
args_owned = 1;
arglen = -1;
argidx = -2;
}
while (--fmtcnt >= 0) {
c = *fmt++;
break;
}
if (fmtcnt < 0) {
PyErr_SetString(PyExc_ValueError,
"incomplete format");
goto error;
}
if (c != '%') {
v = getnextarg(args, arglen, &argidx);
if (v == NULL)
goto error;
}
switch (c) {
case '%':
pbuf = "%";
len = 1;
break;
case 's':
/* only bytes! */
if (!Bytes_CheckExact(v)) {
PyErr_Format(PyExc_ValueError,
"only bytes values expected, got %s",
Py_TYPE(v)->tp_name);
goto error;
}
temp = v;
Py_INCREF(v);
pbuf = Bytes_AS_STRING(temp);
len = Bytes_GET_SIZE(temp);
break;
default:
PyErr_Format(PyExc_ValueError,
"unsupported format character '%c' (0x%x) "
"at index " FORMAT_CODE_PY_SSIZE_T,
c, c,
(Py_ssize_t)(fmt - 1 -
Bytes_AsString(format)));
goto error;
}
if (width < len)
width = len;
if (rescnt < width) {
reslen -= rescnt;
rescnt = width + fmtcnt + 100;
reslen += rescnt;
if (reslen < 0) {
Py_DECREF(result);
Py_XDECREF(temp);
return PyErr_NoMemory();
}
if (_Bytes_Resize(&result, reslen)) {
Py_XDECREF(temp);
return NULL;
}
res = Bytes_AS_STRING(result)
+ reslen - rescnt;
}
Py_MEMCPY(res, pbuf, len);
res += len;
rescnt -= len;
while (--width >= len) {
--rescnt;
*res++ = ' ';
}
if (dict && (argidx < arglen) && c != '%') {
PyErr_SetString(PyExc_TypeError,
"not all arguments converted during string formatting");
Py_XDECREF(temp);
goto error;
}
Py_XDECREF(temp);
} /* '%' */
} /* until end */
if (argidx < arglen && !dict) {
PyErr_SetString(PyExc_TypeError,
"not all arguments converted during string formatting");
goto error;
}
if (args_owned) {
Py_DECREF(args);
}
if (_Bytes_Resize(&result, reslen - rescnt))
return NULL;
return result;
error:
Py_DECREF(result);
if (args_owned) {
Py_DECREF(args);
}
return NULL;
}

View File

@ -26,11 +26,6 @@
#ifndef PSYCOPG_CONNECTION_H
#define PSYCOPG_CONNECTION_H 1
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <libpq-fe.h>
#include "psycopg/config.h"
#include "psycopg/xid.h"
#ifdef __cplusplus
@ -88,6 +83,7 @@ typedef struct {
char *dsn; /* data source name */
char *critical; /* critical error on this connection */
char *encoding; /* current backend encoding */
char *codec; /* python codec name for encoding */
long int closed; /* 1 means connection has been closed;
2 that something horrible happened */
@ -124,6 +120,7 @@ typedef struct {
} connectionObject;
/* C-callable functions in connection_int.c and connection_ext.c */
HIDDEN PyObject *conn_text_from_chars(connectionObject *pgconn, const char *str);
HIDDEN int conn_get_standard_conforming_strings(PGconn *pgconn);
HIDDEN int conn_get_isolation_level(PGresult *pgres);
HIDDEN int conn_get_protocol_version(PGconn *pgconn);

View File

@ -23,19 +23,35 @@
* License for more details.
*/
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <string.h>
#define PSYCOPG_MODULE
#include "psycopg/config.h"
#include "psycopg/psycopg.h"
#include "psycopg/connection.h"
#include "psycopg/cursor.h"
#include "psycopg/pqpath.h"
#include "psycopg/green.h"
#include "psycopg/notify.h"
#include <string.h>
/* Return a new "string" from a char* from the database.
*
* On Py2 just get a string, on Py3 decode it in the connection codec.
*
* Use a fallback if the connection is NULL.
*/
PyObject *
conn_text_from_chars(connectionObject *self, const char *str)
{
#if PY_MAJOR_VERSION < 3
return PyString_FromString(str);
#else
const char *codec = self ? self->codec : "ascii";
return PyUnicode_Decode(str, strlen(str), codec, "replace");
#endif
}
/* conn_notice_callback - process notices */
static void
@ -77,8 +93,7 @@ conn_notice_process(connectionObject *self)
while (notice != NULL) {
PyObject *msg;
msg = PyString_FromString(notice->message);
msg = conn_text_from_chars(self, notice->message);
Dprintf("conn_notice_process: %s", notice->message);
/* Respect the order in which notices were produced,
@ -146,8 +161,8 @@ conn_notifies_process(connectionObject *self)
(int) pgn->be_pid, pgn->relname);
if (!(pid = PyInt_FromLong((long)pgn->be_pid))) { goto error; }
if (!(channel = PyString_FromString(pgn->relname))) { goto error; }
if (!(payload = PyString_FromString(pgn->extra))) { goto error; }
if (!(channel = conn_text_from_chars(self, pgn->relname))) { goto error; }
if (!(payload = conn_text_from_chars(self, pgn->extra))) { goto error; }
if (!(notify = PyObject_CallFunctionObjArgs((PyObject *)&NotifyType,
pid, channel, payload, NULL))) {
@ -213,38 +228,97 @@ conn_get_standard_conforming_strings(PGconn *pgconn)
return equote;
}
/* Return a string containing the client_encoding setting.
/* Convert a PostgreSQL encoding to a Python codec.
*
* Return a new string allocated by malloc(): use free() to free it.
* Return NULL in case of failure.
* Return a new copy of the codec name allocated on the Python heap,
* NULL with exception in case of error.
*/
static char *
conn_get_encoding(PGconn *pgconn)
conn_encoding_to_codec(const char *enc)
{
const char *tmp, *i;
char *encoding, *j;
char *tmp;
Py_ssize_t size;
PyObject *pyenc = NULL;
char *rv = NULL;
/* Find the Py codec name from the PG encoding */
if (!(pyenc = PyDict_GetItemString(psycoEncodings, enc))) {
PyErr_Format(OperationalError,
"no Python codec for client encoding '%s'", enc);
goto exit;
}
/* Convert the codec in a bytes string to extract the c string. */
Py_INCREF(pyenc);
if (!(pyenc = psycopg_ensure_bytes(pyenc))) {
goto exit;
}
if (-1 == Bytes_AsStringAndSize(pyenc, &tmp, &size)) {
goto exit;
}
/* have our own copy of the python codec name */
rv = psycopg_strdup(tmp, size);
exit:
Py_XDECREF(pyenc);
return rv;
}
/* Read the client encoding from the connection.
*
* Store the encoding in the pgconn->encoding field and the name of the
* matching python codec in codec. The buffers are allocated on the Python
* heap.
*
* Return 0 on success, else nonzero.
*/
static int
conn_read_encoding(connectionObject *self, PGconn *pgconn)
{
char *enc = NULL, *codec = NULL, *j;
const char *tmp;
int rv = -1;
tmp = PQparameterStatus(pgconn, "client_encoding");
Dprintf("conn_connect: client encoding: %s", tmp ? tmp : "(none)");
if (!tmp) {
PyErr_SetString(OperationalError,
"server didn't return client encoding");
return NULL;
goto exit;
}
encoding = malloc(strlen(tmp)+1);
if (encoding == NULL) {
if (!(enc = PyMem_Malloc(strlen(tmp)+1))) {
PyErr_NoMemory();
return NULL;
goto exit;
}
/* return in uppercase */
i = tmp;
j = encoding;
while (*i) { *j++ = toupper(*i++); }
/* turn encoding in uppercase */
j = enc;
while (*tmp) { *j++ = toupper(*tmp++); }
*j = '\0';
return encoding;
/* Look for this encoding in Python codecs. */
if (!(codec = conn_encoding_to_codec(enc))) {
goto exit;
}
/* Good, success: store the encoding/codec in the connection. */
PyMem_Free(self->encoding);
self->encoding = enc;
enc = NULL;
PyMem_Free(self->codec);
self->codec = codec;
codec = NULL;
rv = 0;
exit:
PyMem_Free(enc);
PyMem_Free(codec);
return rv;
}
int
@ -324,9 +398,8 @@ conn_setup(connectionObject *self, PGconn *pgconn)
PyErr_SetString(InterfaceError, "only protocol 3 supported");
return -1;
}
/* conn_get_encoding returns a malloc'd string */
self->encoding = conn_get_encoding(pgconn);
if (self->encoding == NULL) {
if (conn_read_encoding(self, pgconn)) {
return -1;
}
@ -656,9 +729,7 @@ _conn_poll_setup_async(connectionObject *self)
PyErr_SetString(InterfaceError, "only protocol 3 supported");
break;
}
/* conn_get_encoding returns a malloc'd string */
self->encoding = conn_get_encoding(self->pgconn);
if (self->encoding == NULL) {
if (conn_read_encoding(self, self->pgconn)) {
break;
}
self->cancel = conn_get_cancel(self->pgconn);
@ -887,11 +958,15 @@ conn_set_client_encoding(connectionObject *self, const char *enc)
char *error = NULL;
char query[48];
int res = 0;
char *codec;
/* If the current encoding is equal to the requested one we don't
issue any query to the backend */
if (strcmp(self->encoding, enc) == 0) return 0;
/* We must know what python codec this encoding is. */
if (!(codec = conn_encoding_to_codec(enc))) { return -1; }
Py_BEGIN_ALLOW_THREADS;
pthread_mutex_lock(&self->lock);
@ -900,19 +975,29 @@ conn_set_client_encoding(connectionObject *self, const char *enc)
/* abort the current transaction, to set the encoding ouside of
transactions */
res = pq_abort_locked(self, &pgres, &error, &_save);
if (res == 0) {
res = pq_execute_command_locked(self, query, &pgres, &error, &_save);
if (res == 0) {
/* no error, we can proceeed and store the new encoding */
if (self->encoding) free(self->encoding);
self->encoding = strdup(enc);
Dprintf("conn_set_client_encoding: set encoding to %s",
self->encoding);
}
if ((res = pq_abort_locked(self, &pgres, &error, &_save))) {
goto endlock;
}
if ((res = pq_execute_command_locked(self, query, &pgres, &error, &_save))) {
goto endlock;
}
/* no error, we can proceeed and store the new encoding */
PyMem_Free(self->encoding);
if (!(self->encoding = psycopg_strdup(enc, 0))) {
res = 1; /* don't call pq_complete_error below */
goto endlock;
}
/* Store the python codec too. */
PyMem_Free(self->codec);
self->codec = codec;
Dprintf("conn_set_client_encoding: set encoding to %s (codec: %s)",
self->encoding, self->codec);
endlock:
pthread_mutex_unlock(&self->lock);
Py_END_ALLOW_THREADS;
@ -978,8 +1063,8 @@ conn_tpc_command(connectionObject *self, const char *cmd, XidObject *xid)
Dprintf("conn_tpc_command: %s", cmd);
/* convert the xid into PostgreSQL transaction id while keeping the GIL */
if (!(tid = xid_get_tid(xid))) { goto exit; }
if (!(ctid = PyString_AsString(tid))) { goto exit; }
if (!(tid = psycopg_ensure_bytes(xid_get_tid(xid)))) { goto exit; }
if (!(ctid = Bytes_AsString(tid))) { goto exit; }
Py_BEGIN_ALLOW_THREADS;
pthread_mutex_lock(&self->lock);

View File

@ -23,18 +23,9 @@
* License for more details.
*/
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <structmember.h>
#include <stringobject.h>
#include <string.h>
#include <ctype.h>
#define PSYCOPG_MODULE
#include "psycopg/config.h"
#include "psycopg/python.h"
#include "psycopg/psycopg.h"
#include "psycopg/connection.h"
#include "psycopg/cursor.h"
#include "psycopg/pqpath.h"
@ -42,6 +33,10 @@
#include "psycopg/green.h"
#include "psycopg/xid.h"
#include <string.h>
#include <ctype.h>
/** DBAPI methods **/
/* cursor method - allocate a new cursor */
@ -103,7 +98,7 @@ psyco_conn_cursor(connectionObject *self, PyObject *args, PyObject *keywds)
Dprintf("psyco_conn_cursor: new cursor at %p: refcnt = "
FORMAT_CODE_PY_SSIZE_T,
obj, obj->ob_refcnt
obj, Py_REFCNT(obj)
);
return obj;
}
@ -427,35 +422,38 @@ psyco_conn_set_isolation_level(connectionObject *self, PyObject *args)
static PyObject *
psyco_conn_set_client_encoding(connectionObject *self, PyObject *args)
{
const char *enc = NULL;
char *buffer;
size_t i, j;
const char *enc;
char *buffer, *dest;
PyObject *rv = NULL;
Py_ssize_t len;
EXC_IF_CONN_CLOSED(self);
EXC_IF_CONN_ASYNC(self, set_client_encoding);
EXC_IF_TPC_PREPARED(self, set_client_encoding);
if (!PyArg_ParseTuple(args, "s", &enc)) return NULL;
if (!PyArg_ParseTuple(args, "s#", &enc, &len)) return NULL;
/* convert to upper case and remove '-' and '_' from string */
buffer = PyMem_Malloc(strlen(enc)+1);
for (i=j=0 ; i < strlen(enc) ; i++) {
if (enc[i] == '_' || enc[i] == '-')
continue;
else
buffer[j++] = toupper(enc[i]);
if (!(dest = buffer = PyMem_Malloc(len+1))) {
return PyErr_NoMemory();
}
buffer[j] = '\0';
while (*enc) {
if (*enc == '_' || *enc == '-') {
++enc;
}
else {
*dest++ = toupper(*enc++);
}
}
*dest = '\0';
if (conn_set_client_encoding(self, buffer) == 0) {
PyMem_Free(buffer);
Py_INCREF(Py_None);
return Py_None;
}
else {
PyMem_Free(buffer);
return NULL;
rv = Py_None;
}
PyMem_Free(buffer);
return rv;
}
/* get_transaction_status method - Get backend transaction status */
@ -497,7 +495,7 @@ psyco_conn_get_parameter_status(connectionObject *self, PyObject *args)
Py_INCREF(Py_None);
return Py_None;
}
return PyString_FromString(val);
return conn_text_from_chars(self, val);
}
@ -516,15 +514,16 @@ static PyObject *
psyco_conn_lobject(connectionObject *self, PyObject *args, PyObject *keywds)
{
Oid oid=InvalidOid, new_oid=InvalidOid;
char *smode = NULL, *new_file = NULL;
int mode=0;
PyObject *obj, *factory = NULL;
char *new_file = NULL;
const char *smode = "";
PyObject *factory = (PyObject *)&lobjectType;
PyObject *obj;
static char *kwlist[] = {"oid", "mode", "new_oid", "new_file",
"cursor_factory", NULL};
if (!PyArg_ParseTupleAndKeywords(args, keywds, "|izizO", kwlist,
&oid, &smode, &new_oid, &new_file,
&oid, &smode, &new_oid, &new_file,
&factory)) {
return NULL;
}
@ -539,33 +538,13 @@ psyco_conn_lobject(connectionObject *self, PyObject *args, PyObject *keywds)
oid, smode);
Dprintf("psyco_conn_lobject: parameters: new_oid = %d, new_file = %s",
new_oid, new_file);
/* build a mode number out of the mode string: right now we only accept
'r', 'w' and 'rw' (but note that 'w' implies 'rw' because PostgreSQL
backend does that. */
if (smode) {
if (strncmp("rw", smode, 2) == 0)
mode = INV_READ+INV_WRITE;
else if (smode[0] == 'r')
mode = INV_READ;
else if (smode[0] == 'w')
mode = INV_WRITE;
else if (smode[0] == 'n')
mode = -1;
else {
PyErr_SetString(PyExc_TypeError,
"mode should be one of 'r', 'w' or 'rw'");
return NULL;
}
}
if (factory == NULL) factory = (PyObject *)&lobjectType;
if (new_file)
obj = PyObject_CallFunction(factory, "Oiiis",
self, oid, mode, new_oid, new_file);
obj = PyObject_CallFunction(factory, "Oisis",
self, oid, smode, new_oid, new_file);
else
obj = PyObject_CallFunction(factory, "Oiii",
self, oid, mode, new_oid);
obj = PyObject_CallFunction(factory, "Oisi",
self, oid, smode, new_oid);
if (obj == NULL) return NULL;
if (PyObject_IsInstance(obj, (PyObject *)&lobjectType) == 0) {
@ -574,10 +553,10 @@ psyco_conn_lobject(connectionObject *self, PyObject *args, PyObject *keywds)
Py_DECREF(obj);
return NULL;
}
Dprintf("psyco_conn_lobject: new lobject at %p: refcnt = "
FORMAT_CODE_PY_SSIZE_T,
obj, obj->ob_refcnt);
obj, Py_REFCNT(obj));
return obj;
}
@ -786,31 +765,31 @@ static struct PyMethodDef connectionObject_methods[] = {
static struct PyMemberDef connectionObject_members[] = {
#ifdef PSYCOPG_EXTENSIONS
{"closed", T_LONG, offsetof(connectionObject, closed), RO,
{"closed", T_LONG, offsetof(connectionObject, closed), READONLY,
"True if the connection is closed."},
{"isolation_level", T_LONG,
offsetof(connectionObject, isolation_level), RO,
offsetof(connectionObject, isolation_level), READONLY,
"The current isolation level."},
{"encoding", T_STRING, offsetof(connectionObject, encoding), RO,
{"encoding", T_STRING, offsetof(connectionObject, encoding), READONLY,
"The current client encoding."},
{"notices", T_OBJECT, offsetof(connectionObject, notice_list), RO},
{"notifies", T_OBJECT, offsetof(connectionObject, notifies), RO},
{"dsn", T_STRING, offsetof(connectionObject, dsn), RO,
{"notices", T_OBJECT, offsetof(connectionObject, notice_list), READONLY},
{"notifies", T_OBJECT, offsetof(connectionObject, notifies), READONLY},
{"dsn", T_STRING, offsetof(connectionObject, dsn), READONLY,
"The current connection string."},
{"async", T_LONG, offsetof(connectionObject, async), RO,
{"async", T_LONG, offsetof(connectionObject, async), READONLY,
"True if the connection is asynchronous."},
{"status", T_INT,
offsetof(connectionObject, status), RO,
offsetof(connectionObject, status), READONLY,
"The current transaction status."},
{"string_types", T_OBJECT, offsetof(connectionObject, string_types), RO,
{"string_types", T_OBJECT, offsetof(connectionObject, string_types), READONLY,
"A set of typecasters to convert textual values."},
{"binary_types", T_OBJECT, offsetof(connectionObject, binary_types), RO,
{"binary_types", T_OBJECT, offsetof(connectionObject, binary_types), READONLY,
"A set of typecasters to convert binary values."},
{"protocol_version", T_INT,
offsetof(connectionObject, protocol), RO,
offsetof(connectionObject, protocol), READONLY,
"Protocol version used for this connection. Currently always 3."},
{"server_version", T_INT,
offsetof(connectionObject, server_version), RO,
offsetof(connectionObject, server_version), READONLY,
"Server version."},
#endif
{NULL}
@ -845,26 +824,18 @@ connection_setup(connectionObject *self, const char *dsn, long int async)
Dprintf("connection_setup: init connection object at %p, "
"async %ld, refcnt = " FORMAT_CODE_PY_SSIZE_T,
self, async, ((PyObject *)self)->ob_refcnt
self, async, Py_REFCNT(self)
);
self->dsn = strdup(dsn);
self->notice_list = PyList_New(0);
self->notifies = PyList_New(0);
self->closed = 0;
self->async = async;
self->status = CONN_STATUS_SETUP;
self->critical = NULL;
self->async_cursor = NULL;
self->async_status = ASYNC_DONE;
self->pgconn = NULL;
self->cancel = NULL;
self->mark = 0;
self->string_types = PyDict_New();
self->binary_types = PyDict_New();
self->notice_pending = NULL;
self->encoding = NULL;
self->weakreflist = NULL;
/* other fields have been zeroed by tp_alloc */
pthread_mutex_init(&(self->lock), NULL);
@ -875,7 +846,7 @@ connection_setup(connectionObject *self, const char *dsn, long int async)
else {
Dprintf("connection_setup: good connection object at %p, refcnt = "
FORMAT_CODE_PY_SSIZE_T,
self, ((PyObject *)self)->ob_refcnt
self, Py_REFCNT(self)
);
res = 0;
}
@ -906,7 +877,8 @@ connection_dealloc(PyObject* obj)
conn_notice_clean(self);
if (self->dsn) free(self->dsn);
if (self->encoding) free(self->encoding);
PyMem_Free(self->encoding);
PyMem_Free(self->codec);
if (self->critical) free(self->critical);
Py_CLEAR(self->tpc_xid);
@ -921,10 +893,10 @@ connection_dealloc(PyObject* obj)
Dprintf("connection_dealloc: deleted connection object at %p, refcnt = "
FORMAT_CODE_PY_SSIZE_T,
obj, obj->ob_refcnt
obj, Py_REFCNT(obj)
);
obj->ob_type->tp_free(obj);
Py_TYPE(obj)->tp_free(obj);
}
static int
@ -984,8 +956,7 @@ connection_traverse(connectionObject *self, visitproc visit, void *arg)
" ProgrammingError, IntegrityError, DataError, NotSupportedError"
PyTypeObject connectionType = {
PyObject_HEAD_INIT(NULL)
0,
PyVarObject_HEAD_INIT(NULL, 0)
"psycopg2._psycopg.connection",
sizeof(connectionObject),
0,

View File

@ -26,11 +26,6 @@
#ifndef PSYCOPG_CURSOR_H
#define PSYCOPG_CURSOR_H 1
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <libpq-fe.h>
#include "psycopg/config.h"
#include "psycopg/connection.h"
#ifdef __cplusplus

View File

@ -23,13 +23,9 @@
* License for more details.
*/
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <string.h>
#define PSYCOPG_MODULE
#include "psycopg/config.h"
#include "psycopg/psycopg.h"
#include "psycopg/cursor.h"
#include "psycopg/pqpath.h"
#include "psycopg/typecast.h"
@ -68,6 +64,9 @@ curs_get_cast(cursorObject *self, PyObject *oid)
return psyco_default_cast;
}
#include <string.h>
/* curs_reset - reset the cursor to a clean state */
void

View File

@ -23,15 +23,9 @@
* License for more details.
*/
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <structmember.h>
#include <string.h>
#define PSYCOPG_MODULE
#include "psycopg/config.h"
#include "psycopg/python.h"
#include "psycopg/psycopg.h"
#include "psycopg/cursor.h"
#include "psycopg/connection.h"
#include "psycopg/green.h"
@ -39,9 +33,12 @@
#include "psycopg/typecast.h"
#include "psycopg/microprotocols.h"
#include "psycopg/microprotocols_proto.h"
#include "pgversion.h"
#include <string.h>
#include <stdlib.h>
extern PyObject *pyPsycopgTzFixedOffsetTimezone;
@ -90,7 +87,7 @@ _mogrify(PyObject *var, PyObject *fmt, connectionObject *conn, PyObject **new)
just before returning. we also init *new to NULL to exit with an error
if we can't complete the mogrification */
n = *new = NULL;
c = PyString_AsString(fmt);
c = Bytes_AsString(fmt);
while(*c) {
/* handle plain percent symbol in format string */
@ -119,7 +116,7 @@ _mogrify(PyObject *var, PyObject *fmt, connectionObject *conn, PyObject **new)
for (d = c + 2; *d && *d != ')'; d++);
if (*d == ')') {
key = PyString_FromStringAndSize(c+2, (Py_ssize_t) (d-c-2));
key = Text_FromUTF8AndSize(c+2, (Py_ssize_t) (d-c-2));
value = PyObject_GetItem(var, key);
/* key has refcnt 1, value the original value + 1 */
@ -132,7 +129,7 @@ _mogrify(PyObject *var, PyObject *fmt, connectionObject *conn, PyObject **new)
}
Dprintf("_mogrify: value refcnt: "
FORMAT_CODE_PY_SSIZE_T " (+1)", value->ob_refcnt);
FORMAT_CODE_PY_SSIZE_T " (+1)", Py_REFCNT(value));
if (n == NULL) {
n = PyDict_New();
@ -147,7 +144,7 @@ _mogrify(PyObject *var, PyObject *fmt, connectionObject *conn, PyObject **new)
optimization over the adapting code and can go away in
the future if somebody finds a None adapter usefull. */
if (value == Py_None) {
t = PyString_FromString("NULL");
t = Bytes_FromString("NULL");
PyDict_SetItem(n, key, t);
/* t is a new object, refcnt = 1, key is at 2 */
@ -185,7 +182,7 @@ _mogrify(PyObject *var, PyObject *fmt, connectionObject *conn, PyObject **new)
Py_DECREF(key); /* key has the original refcnt now */
Dprintf("_mogrify: after value refcnt: "
FORMAT_CODE_PY_SSIZE_T,
value->ob_refcnt
Py_REFCNT(value)
);
}
c = d;
@ -223,7 +220,7 @@ _mogrify(PyObject *var, PyObject *fmt, connectionObject *conn, PyObject **new)
d = c+1;
if (value == Py_None) {
PyTuple_SET_ITEM(n, index, PyString_FromString("NULL"));
PyTuple_SET_ITEM(n, index, Bytes_FromString("NULL"));
while (*d && !isalpha(*d)) d++;
if (*d) *d = 's';
Py_DECREF(value);
@ -270,26 +267,16 @@ static PyObject *_psyco_curs_validate_sql_basic(
goto fail;
}
if (PyString_Check(sql)) {
if (Bytes_Check(sql)) {
/* Necessary for ref-count symmetry with the unicode case: */
Py_INCREF(sql);
}
else if (PyUnicode_Check(sql)) {
PyObject *enc = PyDict_GetItemString(psycoEncodings,
self->conn->encoding);
/* enc is a borrowed reference; we won't decref it */
if (enc) {
sql = PyUnicode_AsEncodedString(sql, PyString_AsString(enc), NULL);
/* if there was an error during the encoding from unicode to the
target encoding, we just let the exception propagate */
if (sql == NULL) { goto fail; }
} else {
PyErr_Format(InterfaceError,
"can't encode unicode SQL statement to %s",
self->conn->encoding);
goto fail;
}
char *enc = self->conn->codec;
sql = PyUnicode_AsEncodedString(sql, enc, NULL);
/* if there was an error during the encoding from unicode to the
target encoding, we just let the exception propagate */
if (sql == NULL) { goto fail; }
}
else {
/* the is not unicode or string, raise an error */
@ -327,7 +314,7 @@ _psyco_curs_merge_query_args(cursorObject *self,
the curren exception (we will later restore it if the type or the
strings do not match.) */
if (!(fquery = PyString_Format(query, args))) {
if (!(fquery = Bytes_Format(query, args))) {
PyObject *err, *arg, *trace;
int pe = 0;
@ -340,7 +327,7 @@ _psyco_curs_merge_query_args(cursorObject *self,
if (PyObject_HasAttrString(arg, "args")) {
PyObject *args = PyObject_GetAttrString(arg, "args");
PyObject *str = PySequence_GetItem(args, 0);
const char *s = PyString_AS_STRING(str);
const char *s = Bytes_AS_STRING(str);
Dprintf("psyco_curs_execute: -> %s", s);
@ -410,9 +397,9 @@ _psyco_curs_execute(cursorObject *self,
}
if (self->name != NULL) {
self->query = PyString_FromFormat(
self->query = Bytes_FromFormat(
"DECLARE %s CURSOR WITHOUT HOLD FOR %s",
self->name, PyString_AS_STRING(fquery));
self->name, Bytes_AS_STRING(fquery));
Py_DECREF(fquery);
}
else {
@ -421,9 +408,9 @@ _psyco_curs_execute(cursorObject *self,
}
else {
if (self->name != NULL) {
self->query = PyString_FromFormat(
self->query = Bytes_FromFormat(
"DECLARE %s CURSOR WITHOUT HOLD FOR %s",
self->name, PyString_AS_STRING(operation));
self->name, Bytes_AS_STRING(operation));
}
else {
/* Transfer reference ownership of the str in operation to
@ -436,7 +423,7 @@ _psyco_curs_execute(cursorObject *self,
/* At this point, the SQL statement must be str, not unicode */
res = pq_execute(self, PyString_AS_STRING(self->query), async);
res = pq_execute(self, Bytes_AS_STRING(self->query), async);
Dprintf("psyco_curs_execute: res = %d, pgres = %p", res, self->pgres);
if (res == -1) { goto fail; }
@ -596,7 +583,7 @@ _psyco_curs_mogrify(cursorObject *self,
Dprintf("psyco_curs_mogrify: cvt->refcnt = " FORMAT_CODE_PY_SSIZE_T
", fquery->refcnt = " FORMAT_CODE_PY_SSIZE_T,
cvt->ob_refcnt, fquery->ob_refcnt);
Py_REFCNT(cvt), Py_REFCNT(fquery));
}
else {
fquery = operation;
@ -704,7 +691,7 @@ _psyco_curs_buildrow_fill(cursorObject *self, PyObject *res,
if (val) {
Dprintf("_psyco_curs_buildrow: val->refcnt = "
FORMAT_CODE_PY_SSIZE_T,
val->ob_refcnt
Py_REFCNT(val)
);
if (istuple) {
PyTuple_SET_ITEM(res, i, val);
@ -799,6 +786,61 @@ psyco_curs_fetchone(cursorObject *self, PyObject *args)
return res;
}
/* Efficient cursor.next() implementation for named cursors.
*
* Fetch several records at time. Return NULL when the cursor is exhausted.
*/
static PyObject *
psyco_curs_next_named(cursorObject *self)
{
PyObject *res;
Dprintf("psyco_curs_next_named");
EXC_IF_CURS_CLOSED(self);
EXC_IF_ASYNC_IN_PROGRESS(self, next);
if (_psyco_curs_prefetch(self) < 0) return NULL;
EXC_IF_NO_TUPLES(self);
EXC_IF_NO_MARK(self);
EXC_IF_TPC_PREPARED(self->conn, next);
Dprintf("psyco_curs_next_named: row %ld", self->row);
Dprintf("psyco_curs_next_named: rowcount = %ld", self->rowcount);
if (self->row >= self->rowcount) {
char buffer[128];
/* fetch 'arraysize' records, but shun the default value of 1 */
long int size = self->arraysize;
if (size == 1) { size = 2000L; }
PyOS_snprintf(buffer, 127, "FETCH FORWARD %ld FROM %s",
size, self->name);
if (pq_execute(self, buffer, 0) == -1) return NULL;
if (_psyco_curs_prefetch(self) < 0) return NULL;
}
/* We exhausted the data: return NULL to stop iteration. */
if (self->row >= self->rowcount) {
return NULL;
}
if (self->tuple_factory == Py_None)
res = _psyco_curs_buildrow(self, self->row);
else
res = _psyco_curs_buildrow_with_factory(self, self->row);
self->row++; /* move the counter to next line */
/* if the query was async aggresively free pgres, to allow
successive requests to reallocate it */
if (self->row >= self->rowcount
&& self->conn->async_cursor
&& PyWeakref_GetObject(self->conn->async_cursor) == (PyObject*)self)
IFCLEARPGRES(self->pgres);
return res;
}
/* fetch many - fetch some results */
@ -989,7 +1031,7 @@ psyco_curs_callproc(cursorObject *self, PyObject *args, PyObject *kwargs)
sql[sl-2] = ')';
sql[sl-1] = '\0';
operation = PyString_FromString(sql);
operation = Bytes_FromString(sql);
PyMem_Free((void*)sql);
if (_psyco_curs_execute(self, operation, parameters, self->conn->async)) {
@ -1144,14 +1186,11 @@ static int _psyco_curs_copy_columns(PyObject *columns, char *columnlist)
columnlist[0] = '(';
while ((col = PyIter_Next(coliter)) != NULL) {
if (!PyString_Check(col)) {
Py_DECREF(col);
if (!(col = psycopg_ensure_bytes(col))) {
Py_DECREF(coliter);
PyErr_SetString(PyExc_ValueError,
"elements in column list must be strings");
return -1;
}
PyString_AsStringAndSize(col, &colname, &collen);
Bytes_AsStringAndSize(col, &colname, &collen);
if (offset + collen > DEFAULT_COPYBUFF - 2) {
Py_DECREF(col);
Py_DECREF(coliter);
@ -1456,7 +1495,7 @@ psyco_curs_copy_expert(cursorObject *self, PyObject *args, PyObject *kwargs)
self->copyfile = file;
/* At this point, the SQL statement must be str, not unicode */
if (pq_execute(self, PyString_AS_STRING(sql), 0) != 1) { goto fail; }
if (pq_execute(self, Bytes_AS_STRING(sql), 0) != 1) { goto fail; }
res = Py_None;
Py_INCREF(res);
@ -1510,14 +1549,20 @@ cursor_next(PyObject *self)
{
PyObject *res;
/* we don't parse arguments: psyco_curs_fetchone will do that for us */
res = psyco_curs_fetchone((cursorObject*)self, NULL);
if (NULL == ((cursorObject*)self)->name) {
/* we don't parse arguments: psyco_curs_fetchone will do that for us */
res = psyco_curs_fetchone((cursorObject*)self, NULL);
/* convert a None to NULL to signal the end of iteration */
if (res && res == Py_None) {
Py_DECREF(res);
res = NULL;
/* convert a None to NULL to signal the end of iteration */
if (res && res == Py_None) {
Py_DECREF(res);
res = NULL;
}
}
else {
res = psyco_curs_next_named((cursorObject*)self);
}
return res;
}
@ -1570,29 +1615,29 @@ static struct PyMethodDef cursorObject_methods[] = {
static struct PyMemberDef cursorObject_members[] = {
/* DBAPI-2.0 basics */
{"rowcount", T_LONG, OFFSETOF(rowcount), RO,
{"rowcount", T_LONG, OFFSETOF(rowcount), READONLY,
"Number of rows read from the backend in the last command."},
{"arraysize", T_LONG, OFFSETOF(arraysize), 0,
"Number of records `fetchmany()` must fetch if not explicitly " \
"specified."},
{"description", T_OBJECT, OFFSETOF(description), RO,
{"description", T_OBJECT, OFFSETOF(description), READONLY,
"Cursor description as defined in DBAPI-2.0."},
{"lastrowid", T_LONG, OFFSETOF(lastoid), RO,
{"lastrowid", T_LONG, OFFSETOF(lastoid), READONLY,
"The ``oid`` of the last row inserted by the cursor."},
/* DBAPI-2.0 extensions */
{"rownumber", T_LONG, OFFSETOF(row), RO,
{"rownumber", T_LONG, OFFSETOF(row), READONLY,
"The current row position."},
{"connection", T_OBJECT, OFFSETOF(conn), RO,
{"connection", T_OBJECT, OFFSETOF(conn), READONLY,
"The connection where the cursor comes from."},
#ifdef PSYCOPG_EXTENSIONS
{"name", T_STRING, OFFSETOF(name), RO},
{"statusmessage", T_OBJECT, OFFSETOF(pgstatus), RO,
{"name", T_STRING, OFFSETOF(name), READONLY},
{"statusmessage", T_OBJECT, OFFSETOF(pgstatus), READONLY,
"The return message of the last command."},
{"query", T_OBJECT, OFFSETOF(query), RO,
{"query", T_OBJECT, OFFSETOF(query), READONLY,
"The last query text sent to the backend."},
{"row_factory", T_OBJECT, OFFSETOF(tuple_factory), 0},
{"tzinfo_factory", T_OBJECT, OFFSETOF(tzinfo_factory), 0},
{"typecaster", T_OBJECT, OFFSETOF(caster), RO},
{"typecaster", T_OBJECT, OFFSETOF(caster), READONLY},
{"string_types", T_OBJECT, OFFSETOF(string_types), 0},
{"binary_types", T_OBJECT, OFFSETOF(binary_types), 0},
#endif
@ -1662,7 +1707,7 @@ cursor_setup(cursorObject *self, connectionObject *conn, const char *name)
Dprintf("cursor_setup: good cursor object at %p, refcnt = "
FORMAT_CODE_PY_SSIZE_T,
self, ((PyObject *)self)->ob_refcnt
self, Py_REFCNT(self)
);
return 0;
}
@ -1694,9 +1739,9 @@ cursor_dealloc(PyObject* obj)
Dprintf("cursor_dealloc: deleted cursor object at %p, refcnt = "
FORMAT_CODE_PY_SSIZE_T,
obj, obj->ob_refcnt);
obj, Py_REFCNT(obj));
obj->ob_type->tp_free(obj);
Py_TYPE(obj)->tp_free(obj);
}
static int
@ -1754,8 +1799,7 @@ cursor_traverse(cursorObject *self, visitproc visit, void *arg)
"A database cursor."
PyTypeObject cursorType = {
PyObject_HEAD_INIT(NULL)
0,
PyVarObject_HEAD_INIT(NULL, 0)
"psycopg2._psycopg.cursor",
sizeof(cursorObject),
0,

View File

@ -24,13 +24,13 @@
*/
#define PSYCOPG_MODULE
#include "psycopg/python.h"
#include "psycopg/psycopg.h"
#include "psycopg/config.h"
#include "psycopg/green.h"
#include "psycopg/connection.h"
#include "psycopg/pqpath.h"
HIDDEN PyObject *wait_callback = NULL;
static PyObject *have_wait_callback(void);

View File

@ -26,11 +26,8 @@
#ifndef PSYCOPG_LOBJECT_H
#define PSYCOPG_LOBJECT_H 1
#include <Python.h>
#include <libpq-fe.h>
#include <libpq/libpq-fs.h>
#include "psycopg/config.h"
#include "psycopg/connection.h"
#ifdef __cplusplus
@ -45,7 +42,8 @@ typedef struct {
connectionObject *conn; /* connection owning the lobject */
long int mark; /* copied from conn->mark */
const char *smode; /* string mode if lobject was opened */
char *smode; /* string mode if lobject was opened */
int mode; /* numeric version of smode */
int fd; /* the file descriptor for file-like ops */
Oid oid; /* the oid for this lobject */
@ -54,7 +52,7 @@ typedef struct {
/* functions exported from lobject_int.c */
HIDDEN int lobject_open(lobjectObject *self, connectionObject *conn,
Oid oid, int mode, Oid new_oid,
Oid oid, const char *smode, Oid new_oid,
const char *new_file);
HIDDEN int lobject_unlink(lobjectObject *self);
HIDDEN int lobject_export(lobjectObject *self, const char *filename);
@ -90,6 +88,12 @@ if (self->conn->mark != self->mark) { \
return NULL; \
}
/* Values for the lobject mode */
#define LOBJECT_READ 1
#define LOBJECT_WRITE 2
#define LOBJECT_BINARY 4
#define LOBJECT_TEXT 8
#ifdef __cplusplus
}
#endif

View File

@ -23,17 +23,15 @@
* License for more details.
*/
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <string.h>
#define PSYCOPG_MODULE
#include "psycopg/config.h"
#include "psycopg/psycopg.h"
#include "psycopg/connection.h"
#include "psycopg/lobject.h"
#include "psycopg/connection.h"
#include "psycopg/pqpath.h"
#include <string.h>
#ifdef PSYCOPG_EXTENSIONS
static void
@ -45,15 +43,118 @@ collect_error(connectionObject *conn, char **error)
*error = strdup(msg);
}
/* Check if the mode passed to the large object is valid.
* In case of success return a value >= 0
* On error return a value < 0 and set an exception.
*
* Valid mode are [r|w|rw|n][t|b]
*/
static int
_lobject_parse_mode(const char *mode)
{
int rv = 0;
size_t pos = 0;
if (0 == strncmp("rw", mode, 2)) {
rv |= LOBJECT_READ | LOBJECT_WRITE;
pos += 2;
}
else {
switch (mode[0]) {
case 'r':
rv |= LOBJECT_READ;
pos += 1;
break;
case 'w':
rv |= LOBJECT_WRITE;
pos += 1;
break;
case 'n':
pos += 1;
break;
default:
rv |= LOBJECT_READ;
break;
}
}
switch (mode[pos]) {
case 't':
rv |= LOBJECT_TEXT;
pos += 1;
break;
case 'b':
rv |= LOBJECT_BINARY;
pos += 1;
break;
default:
#if PY_MAJOR_VERSION < 3
rv |= LOBJECT_BINARY;
#else
rv |= LOBJECT_TEXT;
#endif
break;
}
if (pos != strlen(mode)) {
PyErr_Format(PyExc_ValueError,
"bad mode for lobject: '%s'", mode);
rv = -1;
}
return rv;
}
/* Return a string representing the lobject mode.
*
* The return value is a new string allocated on the Python heap.
*/
static char *
_lobject_unparse_mode(int mode)
{
char *buf;
char *c;
/* the longest is 'rwt' */
c = buf = PyMem_Malloc(4);
if (mode & LOBJECT_READ) { *c++ = 'r'; }
if (mode & LOBJECT_WRITE) { *c++ = 'w'; }
if (buf == c) {
/* neither read nor write */
*c++ = 'n';
}
else {
if (mode & LOBJECT_TEXT) {
*c++ = 't';
}
else {
*c++ = 'b';
}
}
*c = '\0';
return buf;
}
/* lobject_open - create a new/open an existing lo */
int
lobject_open(lobjectObject *self, connectionObject *conn,
Oid oid, int mode, Oid new_oid, const char *new_file)
Oid oid, const char *smode, Oid new_oid, const char *new_file)
{
int retvalue = -1;
PGresult *pgres = NULL;
char *error = NULL;
int pgmode = 0;
int mode;
if (0 > (mode = _lobject_parse_mode(smode))) {
return -1;
}
Py_BEGIN_ALLOW_THREADS;
pthread_mutex_lock(&(self->conn->lock));
@ -80,19 +181,19 @@ lobject_open(lobjectObject *self, connectionObject *conn,
goto end;
}
mode = INV_WRITE;
mode = (mode & ~LOBJECT_READ) | LOBJECT_WRITE;
}
else {
self->oid = oid;
if (mode == 0) mode = INV_READ;
}
/* if the oid is a real one we try to open with the given mode,
unless the mode is -1, meaning "don't open!" */
if (mode != -1) {
self->fd = lo_open(self->conn->pgconn, self->oid, mode);
Dprintf("lobject_open: large object opened with fd = %d",
self->fd);
/* if the oid is a real one we try to open with the given mode */
if (mode & LOBJECT_READ) { pgmode |= INV_READ; }
if (mode & LOBJECT_WRITE) { pgmode |= INV_WRITE; }
if (pgmode) {
self->fd = lo_open(self->conn->pgconn, self->oid, pgmode);
Dprintf("lobject_open: large object opened with mode = %i fd = %d",
pgmode, self->fd);
if (self->fd == -1) {
collect_error(self->conn, &error);
@ -100,17 +201,10 @@ lobject_open(lobjectObject *self, connectionObject *conn,
goto end;
}
}
/* set the mode for future reference */
switch (mode) {
case -1:
self->smode = "n"; break;
case INV_READ:
self->smode = "r"; break;
case INV_WRITE:
self->smode = "w"; break;
case INV_READ+INV_WRITE:
self->smode = "rw"; break;
}
self->mode = mode;
self->smode = _lobject_unparse_mode(mode);
retvalue = 0;
end:
@ -220,7 +314,7 @@ lobject_write(lobjectObject *self, const char *buf, size_t len)
PGresult *pgres = NULL;
char *error = NULL;
Dprintf("lobject_writing: fd = %d, len = " FORMAT_CODE_PY_SSIZE_T,
Dprintf("lobject_writing: fd = %d, len = " FORMAT_CODE_SIZE_T,
self->fd, len);
Py_BEGIN_ALLOW_THREADS;
@ -355,7 +449,7 @@ lobject_truncate(lobjectObject *self, size_t len)
PGresult *pgres = NULL;
char *error = NULL;
Dprintf("lobject_truncate: fd = %d, len = " FORMAT_CODE_PY_SSIZE_T,
Dprintf("lobject_truncate: fd = %d, len = " FORMAT_CODE_SIZE_T,
self->fd, len);
Py_BEGIN_ALLOW_THREADS;

View File

@ -23,20 +23,16 @@
* License for more details.
*/
#include <Python.h>
#include <structmember.h>
#include <string.h>
#define PSYCOPG_MODULE
#include "psycopg/config.h"
#include "psycopg/python.h"
#include "psycopg/psycopg.h"
#include "psycopg/lobject.h"
#include "psycopg/connection.h"
#include "psycopg/microprotocols.h"
#include "psycopg/microprotocols_proto.h"
#include "psycopg/pqpath.h"
#include "pgversion.h"
#include <string.h>
#ifdef PSYCOPG_EXTENSIONS
@ -75,18 +71,48 @@ psyco_lobj_close(lobjectObject *self, PyObject *args)
static PyObject *
psyco_lobj_write(lobjectObject *self, PyObject *args)
{
int len, res=0;
const char *buffer;
char *buffer;
Py_ssize_t len;
Py_ssize_t res;
PyObject *obj;
PyObject *data = NULL;
PyObject *rv = NULL;
if (!PyArg_ParseTuple(args, "s#", &buffer, &len)) return NULL;
if (!PyArg_ParseTuple(args, "O", &obj)) return NULL;
EXC_IF_LOBJ_CLOSED(self);
EXC_IF_LOBJ_LEVEL0(self);
EXC_IF_LOBJ_UNMARKED(self);
if ((res = lobject_write(self, buffer, len)) < 0) return NULL;
if (Bytes_Check(obj)) {
Py_INCREF(obj);
data = obj;
}
else if (PyUnicode_Check(obj)) {
if (!(data = PyUnicode_AsEncodedString(obj, self->conn->codec, NULL))) {
goto exit;
}
}
else {
PyErr_Format(PyExc_TypeError,
"lobject.write requires a string; got %s instead",
Py_TYPE(obj)->tp_name);
goto exit;
}
return PyInt_FromLong((long)res);
if (-1 == Bytes_AsStringAndSize(data, &buffer, &len)) {
goto exit;
}
if (0 > (res = lobject_write(self, buffer, (size_t)len))) {
goto exit;
}
rv = PyInt_FromLong((long)res);
exit:
Py_XDECREF(data);
return rv;
}
/* read method - read data from the lobject */
@ -123,9 +149,13 @@ psyco_lobj_read(lobjectObject *self, PyObject *args)
return NULL;
}
res = PyString_FromStringAndSize(buffer, size);
if (self->mode & LOBJECT_BINARY) {
res = Bytes_FromStringAndSize(buffer, size);
} else {
res = PyUnicode_Decode(buffer, size, self->conn->codec, NULL);
}
PyMem_Free(buffer);
return res;
}
@ -277,10 +307,10 @@ static struct PyMethodDef lobjectObject_methods[] = {
/* object member list */
static struct PyMemberDef lobjectObject_members[] = {
{"oid", T_UINT, offsetof(lobjectObject, oid), RO,
{"oid", T_UINT, offsetof(lobjectObject, oid), READONLY,
"The backend OID associated to this lobject."},
{"mode", T_STRING, offsetof(lobjectObject, smode), RO,
"Open mode ('r', 'w', 'rw' or 'n')."},
{"mode", T_STRING, offsetof(lobjectObject, smode), READONLY,
"Open mode."},
{NULL}
};
@ -296,7 +326,7 @@ static struct PyGetSetDef lobjectObject_getsets[] = {
static int
lobject_setup(lobjectObject *self, connectionObject *conn,
Oid oid, int mode, Oid new_oid, const char *new_file)
Oid oid, const char *smode, Oid new_oid, const char *new_file)
{
Dprintf("lobject_setup: init lobject object at %p", self);
@ -314,11 +344,11 @@ lobject_setup(lobjectObject *self, connectionObject *conn,
self->fd = -1;
self->oid = InvalidOid;
if (lobject_open(self, conn, oid, mode, new_oid, new_file) == -1)
if (lobject_open(self, conn, oid, smode, new_oid, new_file) == -1)
return -1;
Dprintf("lobject_setup: good lobject object at %p, refcnt = "
FORMAT_CODE_PY_SSIZE_T, self, ((PyObject *)self)->ob_refcnt);
FORMAT_CODE_PY_SSIZE_T, self, Py_REFCNT(self));
Dprintf("lobject_setup: oid = %d, fd = %d", self->oid, self->fd);
return 0;
}
@ -331,27 +361,28 @@ lobject_dealloc(PyObject* obj)
if (lobject_close(self) < 0)
PyErr_Print();
Py_XDECREF((PyObject*)self->conn);
PyMem_Free(self->smode);
Dprintf("lobject_dealloc: deleted lobject object at %p, refcnt = "
FORMAT_CODE_PY_SSIZE_T, obj, obj->ob_refcnt);
FORMAT_CODE_PY_SSIZE_T, obj, Py_REFCNT(obj));
obj->ob_type->tp_free(obj);
Py_TYPE(obj)->tp_free(obj);
}
static int
lobject_init(PyObject *obj, PyObject *args, PyObject *kwds)
{
Oid oid=InvalidOid, new_oid=InvalidOid;
int mode=0;
const char *smode = "";
const char *new_file = NULL;
PyObject *conn;
if (!PyArg_ParseTuple(args, "O|iiis",
&conn, &oid, &mode, &new_oid, &new_file))
if (!PyArg_ParseTuple(args, "O|iziz",
&conn, &oid, &smode, &new_oid, &new_file))
return -1;
return lobject_setup((lobjectObject *)obj,
(connectionObject *)conn, oid, mode, new_oid, new_file);
(connectionObject *)conn, oid, smode, new_oid, new_file);
}
static PyObject *
@ -380,8 +411,7 @@ lobject_repr(lobjectObject *self)
"A database large object."
PyTypeObject lobjectType = {
PyObject_HEAD_INIT(NULL)
0,
PyVarObject_HEAD_INIT(NULL, 0)
"psycopg2._psycopg.lobject",
sizeof(lobjectObject),
0,

View File

@ -23,18 +23,13 @@
* License for more details.
*/
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <structmember.h>
#define PSYCOPG_MODULE
#include "psycopg/config.h"
#include "psycopg/python.h"
#include "psycopg/psycopg.h"
#include "psycopg/cursor.h"
#include "psycopg/connection.h"
#include "psycopg/microprotocols.h"
#include "psycopg/microprotocols_proto.h"
#include "psycopg/cursor.h"
#include "psycopg/connection.h"
/** the adapters registry **/
@ -87,8 +82,12 @@ _get_superclass_adapter(PyObject *obj, PyObject *proto)
PyObject *key, *adapter;
Py_ssize_t i, ii;
type = (PyTypeObject *)Py_TYPE(obj);
if (!((Py_TPFLAGS_HAVE_CLASS & type->tp_flags) && type->tp_mro)) {
type = Py_TYPE(obj);
if (!(
#if PY_MAJOR_VERSION < 3
(Py_TPFLAGS_HAVE_CLASS & type->tp_flags) &&
#endif
type->tp_mro)) {
/* has no mro */
return NULL;
}
@ -136,7 +135,8 @@ microprotocols_adapt(PyObject *obj, PyObject *proto, PyObject *alt)
because the ISQLQuote type is abstract and there is no way to get a
quotable object to be its instance */
Dprintf("microprotocols_adapt: trying to adapt %s", obj->ob_type->tp_name);
Dprintf("microprotocols_adapt: trying to adapt %s",
Py_TYPE(obj)->tp_name);
/* look for an adapter in the registry */
key = PyTuple_Pack(2, Py_TYPE(obj), proto);
@ -192,12 +192,16 @@ microprotocols_adapt(PyObject *obj, PyObject *proto, PyObject *alt)
}
/* else set the right exception and return NULL */
PyOS_snprintf(buffer, 255, "can't adapt type '%s'", obj->ob_type->tp_name);
PyOS_snprintf(buffer, 255, "can't adapt type '%s'",
Py_TYPE(obj)->tp_name);
psyco_set_error(ProgrammingError, NULL, buffer, NULL, NULL);
return NULL;
}
/* microprotocol_getquoted - utility function that adapt and call getquoted */
/* microprotocol_getquoted - utility function that adapt and call getquoted.
*
* Return a bytes string, NULL on error.
*/
PyObject *
microprotocol_getquoted(PyObject *obj, connectionObject *conn)
@ -211,7 +215,7 @@ microprotocol_getquoted(PyObject *obj, connectionObject *conn)
}
Dprintf("microprotocol_getquoted: adapted to %s",
adapted->ob_type->tp_name);
Py_TYPE(adapted)->tp_name);
/* if requested prepare the object passing it the connection */
if (conn) {
@ -235,6 +239,16 @@ microprotocol_getquoted(PyObject *obj, connectionObject *conn)
adapted to the right protocol) */
res = PyObject_CallMethod(adapted, "getquoted", NULL);
/* Convert to bytes. */
if (res && PyUnicode_CheckExact(res)) {
PyObject *b;
const char *codec;
codec = (conn && conn->codec) ? conn->codec : "utf8";
b = PyUnicode_AsEncodedString(res, codec, NULL);
Py_DECREF(res);
res = b;
}
exit:
Py_XDECREF(adapted);
Py_XDECREF(prepare);

View File

@ -26,9 +26,6 @@
#ifndef PSYCOPG_MICROPROTOCOLS_H
#define PSYCOPG_MICROPROTOCOLS_H 1
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include "psycopg/config.h"
#include "psycopg/connection.h"
#include "psycopg/cursor.h"

View File

@ -23,19 +23,13 @@
* License for more details.
*/
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <structmember.h>
#include <stringobject.h>
#define PSYCOPG_MODULE
#include "psycopg/psycopg.h"
#include "psycopg/microprotocols_proto.h"
#include <string.h>
#define PSYCOPG_MODULE
#include "psycopg/config.h"
#include "psycopg/python.h"
#include "psycopg/psycopg.h"
#include "psycopg/microprotocols_proto.h"
/** void protocol implementation **/
@ -99,7 +93,7 @@ static struct PyMethodDef isqlquoteObject_methods[] = {
static struct PyMemberDef isqlquoteObject_members[] = {
/* DBAPI-2.0 extensions (exception objects) */
{"_wrapped", T_OBJECT, offsetof(isqlquoteObject, wrapped), RO},
{"_wrapped", T_OBJECT, offsetof(isqlquoteObject, wrapped), READONLY},
{NULL}
};
@ -121,7 +115,7 @@ isqlquote_dealloc(PyObject* obj)
Py_XDECREF(self->wrapped);
obj->ob_type->tp_free(obj);
Py_TYPE(obj)->tp_free(obj);
}
static int
@ -156,8 +150,7 @@ isqlquote_del(PyObject* self)
"returning the SQL representation of the object.\n\n"
PyTypeObject isqlquoteType = {
PyObject_HEAD_INIT(NULL)
0,
PyVarObject_HEAD_INIT(NULL, 0)
"psycopg2._psycopg.ISQLQuote",
sizeof(isqlquoteObject),
0,

View File

@ -26,12 +26,6 @@
#ifndef PSYCOPG_ISQLQUOTE_H
#define PSYCOPG_ISQLQUOTE_H 1
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <libpq-fe.h>
#include "psycopg/config.h"
#ifdef __cplusplus
extern "C" {
#endif

View File

@ -26,10 +26,6 @@
#ifndef PSYCOPG_NOTIFY_H
#define PSYCOPG_NOTIFY_H 1
#include <Python.h>
#include "psycopg/config.h"
extern HIDDEN PyTypeObject NotifyType;
typedef struct {

View File

@ -23,16 +23,12 @@
* License for more details.
*/
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <structmember.h>
#define PSYCOPG_MODULE
#include "psycopg/config.h"
#include "psycopg/python.h"
#include "psycopg/psycopg.h"
#include "psycopg/notify.h"
static const char notify_doc[] =
"A notification received from the backend.\n\n"
"`!Notify` instances are made available upon reception on the\n"
@ -56,9 +52,9 @@ static const char payload_doc[] =
"of the server this member is always the empty string.";
static PyMemberDef notify_members[] = {
{ "pid", T_OBJECT, offsetof(NotifyObject, pid), RO, (char *)pid_doc },
{ "channel", T_OBJECT, offsetof(NotifyObject, channel), RO, (char *)channel_doc },
{ "payload", T_OBJECT, offsetof(NotifyObject, payload), RO, (char *)payload_doc },
{ "pid", T_OBJECT, offsetof(NotifyObject, pid), READONLY, (char *)pid_doc },
{ "channel", T_OBJECT, offsetof(NotifyObject, channel), READONLY, (char *)channel_doc },
{ "payload", T_OBJECT, offsetof(NotifyObject, payload), READONLY, (char *)payload_doc },
{ NULL }
};
@ -82,7 +78,7 @@ notify_init(NotifyObject *self, PyObject *args, PyObject *kwargs)
}
if (!payload) {
payload = PyString_FromStringAndSize("", 0);
payload = Text_FromUTF8("");
}
Py_CLEAR(self->pid);
@ -116,7 +112,7 @@ notify_dealloc(NotifyObject *self)
Py_CLEAR(self->channel);
Py_CLEAR(self->payload);
self->ob_type->tp_free((PyObject *)self);
Py_TYPE(self)->tp_free((PyObject *)self);
}
static void
@ -217,7 +213,7 @@ notify_repr(NotifyObject *self)
PyObject *format = NULL;
PyObject *args = NULL;
if (!(format = PyString_FromString("Notify(%r, %r, %r)"))) {
if (!(format = Text_FromUTF8("Notify(%r, %r, %r)"))) {
goto exit;
}
@ -229,7 +225,7 @@ notify_repr(NotifyObject *self)
Py_INCREF(self->payload);
PyTuple_SET_ITEM(args, 2, self->payload);
rv = PyString_Format(format, args);
rv = Text_Format(format, args);
exit:
Py_XDECREF(args);
@ -280,8 +276,7 @@ static PySequenceMethods notify_sequence = {
PyTypeObject NotifyType = {
PyObject_HEAD_INIT(NULL)
0,
PyVarObject_HEAD_INIT(NULL, 0)
"psycopg2.extensions.Notify",
sizeof(NotifyObject),
0,

View File

@ -1,2 +0,0 @@
#define PG_VERSION_MAJOR 7
#define PG_VERSION_MINOR 4

View File

@ -29,21 +29,17 @@
connection.
*/
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <string.h>
#define PSYCOPG_MODULE
#include "psycopg/config.h"
#include "psycopg/python.h"
#include "psycopg/psycopg.h"
#include "psycopg/pqpath.h"
#include "psycopg/connection.h"
#include "psycopg/cursor.h"
#include "psycopg/green.h"
#include "psycopg/typecast.h"
#include "psycopg/pgtypes.h"
#include "psycopg/pgversion.h"
#include <string.h>
/* Strip off the severity from a Postgres error message. */
@ -976,14 +972,14 @@ _pq_fetch_tuples(cursorObject *curs)
}
Dprintf("_pq_fetch_tuples: using cast at %p (%s) for type %d",
cast, PyString_AS_STRING(((typecastObject*)cast)->name),
cast, Bytes_AS_STRING(((typecastObject*)cast)->name),
PQftype(curs->pgres,i));
Py_INCREF(cast);
PyTuple_SET_ITEM(curs->casts, i, cast);
/* 1/ fill the other fields */
PyTuple_SET_ITEM(dtitem, 0,
PyString_FromString(PQfname(curs->pgres, i)));
conn_text_from_chars(curs->conn, PQfname(curs->pgres, i)));
PyTuple_SET_ITEM(dtitem, 1, type);
/* 2/ display size is the maximum size of this field result tuples. */
@ -1061,19 +1057,50 @@ _pq_copy_in_v3(cursorObject *curs)
}
while (1) {
o = PyObject_CallFunctionObjArgs(func, size, NULL);
if (!(o && PyString_Check(o) && (length = PyString_GET_SIZE(o)) != -1)) {
if (!(o = PyObject_CallFunctionObjArgs(func, size, NULL))) {
Dprintf("_pq_copy_in_v3: read() failed");
error = 1;
break;
}
/* a file may return unicode in Py3: encode in client encoding. */
#if PY_MAJOR_VERSION > 2
if (PyUnicode_Check(o)) {
PyObject *tmp;
if (!(tmp = PyUnicode_AsEncodedString(o, curs->conn->codec, NULL))) {
Dprintf("_pq_copy_in_v3: encoding() failed");
error = 1;
break;
}
Py_DECREF(o);
o = tmp;
}
#endif
if (!Bytes_Check(o)) {
Dprintf("_pq_copy_in_v3: got %s instead of bytes",
Py_TYPE(o)->tp_name);
error = 1;
break;
}
if (0 == (length = Bytes_GET_SIZE(o))) {
break;
}
if (length > INT_MAX) {
Dprintf("_pq_copy_in_v3: bad length: " FORMAT_CODE_PY_SSIZE_T,
length);
error = 1;
break;
}
if (length == 0 || length > INT_MAX || error == 1) break;
Py_BEGIN_ALLOW_THREADS;
res = PQputCopyData(curs->conn->pgconn, PyString_AS_STRING(o),
res = PQputCopyData(curs->conn->pgconn, Bytes_AS_STRING(o),
/* Py_ssize_t->int cast was validated above */
(int) length);
Dprintf("_pq_copy_in_v3: sent %d bytes of data; res = %d",
(int) length, res);
Dprintf("_pq_copy_in_v3: sent " FORMAT_CODE_PY_SSIZE_T " bytes of data; res = %d",
length, res);
if (res == 0) {
/* FIXME: in theory this should not happen but adding a check
here would be a nice idea */
@ -1101,6 +1128,7 @@ _pq_copy_in_v3(cursorObject *curs)
else if (error == 2)
res = PQputCopyEnd(curs->conn->pgconn, "error in PQputCopyData() call");
else
/* XXX would be nice to propagate the exeption */
res = PQputCopyEnd(curs->conn->pgconn, "error in .read() call");
IFCLEARPGRES(curs->pgres);
@ -1135,7 +1163,9 @@ static int
_pq_copy_out_v3(cursorObject *curs)
{
PyObject *tmp = NULL, *func;
PyObject *obj = NULL;
int ret = -1;
int is_text;
char *buffer;
Py_ssize_t len;
@ -1145,14 +1175,28 @@ _pq_copy_out_v3(cursorObject *curs)
goto exit;
}
/* if the file is text we must pass it unicode. */
if (-1 == (is_text = psycopg_is_text_file(curs->copyfile))) {
goto exit;
}
while (1) {
Py_BEGIN_ALLOW_THREADS;
len = PQgetCopyData(curs->conn->pgconn, &buffer, 0);
Py_END_ALLOW_THREADS;
if (len > 0 && buffer) {
tmp = PyObject_CallFunction(func, "s#", buffer, len);
if (is_text) {
obj = PyUnicode_Decode(buffer, len, curs->conn->codec, NULL);
} else {
obj = Bytes_FromStringAndSize(buffer, len);
}
PQfreemem(buffer);
if (!obj) { goto exit; }
tmp = PyObject_CallFunctionObjArgs(func, obj, NULL);
Py_DECREF(obj);
if (tmp == NULL) {
goto exit;
} else {
@ -1215,7 +1259,7 @@ pq_fetch(cursorObject *curs)
/* backend status message */
Py_XDECREF(curs->pgstatus);
curs->pgstatus = PyString_FromString(PQcmdStatus(curs->pgres));
curs->pgstatus = conn_text_from_chars(curs->conn, PQcmdStatus(curs->pgres));
switch(pgstatus) {

View File

@ -26,7 +26,6 @@
#ifndef PSYCOPG_PQPATH_H
#define PSYCOPG_PQPATH_H 1
#include "psycopg/config.h"
#include "psycopg/cursor.h"
#include "psycopg/connection.h"

View File

@ -120,6 +120,11 @@ HIDDEN void psyco_set_error(PyObject *exc, PyObject *curs, const char *msg,
HIDDEN char *psycopg_escape_string(PyObject *conn,
const char *from, Py_ssize_t len, char *to, Py_ssize_t *tolen);
HIDDEN char *psycopg_strdup(const char *from, Py_ssize_t len);
HIDDEN PyObject * psycopg_ensure_bytes(PyObject *obj);
HIDDEN PyObject * psycopg_ensure_text(PyObject *obj);
HIDDEN int psycopg_is_text_file(PyObject *f);
/* Exceptions docstrings */
#define Error_doc \
"Base class for error exceptions."

View File

@ -23,13 +23,9 @@
* License for more details.
*/
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#define PSYCOPG_MODULE
#include "psycopg/config.h"
#include "psycopg/python.h"
#include "psycopg/psycopg.h"
#include "psycopg/connection.h"
#include "psycopg/cursor.h"
#include "psycopg/green.h"
@ -130,17 +126,38 @@ psyco_connect(PyObject *self, PyObject *args, PyObject *keywds)
return NULL;
}
#if PY_MAJOR_VERSION < 3
if (pyport && PyString_Check(pyport)) {
PyObject *pyint = PyInt_FromString(PyString_AsString(pyport), NULL, 10);
if (!pyint) goto fail;
/* Must use PyInt_AsLong rather than PyInt_AS_LONG, because
* PyInt_FromString can return a PyLongObject: */
iport = PyInt_AsLong(pyint);
Py_DECREF(pyint);
PyObject *pyint = PyInt_FromString(PyString_AsString(pyport), NULL, 10);
if (!pyint) goto fail;
/* Must use PyInt_AsLong rather than PyInt_AS_LONG, because
* PyInt_FromString can return a PyLongObject: */
iport = PyInt_AsLong(pyint);
Py_DECREF(pyint);
if (iport == -1 && PyErr_Occurred())
goto fail;
}
else if (pyport && PyInt_Check(pyport)) {
iport = PyInt_AsLong(pyport);
iport = PyInt_AsLong(pyport);
if (iport == -1 && PyErr_Occurred())
goto fail;
}
#else
if (pyport && PyUnicode_Check(pyport)) {
PyObject *pyint = PyObject_CallFunction((PyObject*)&PyLong_Type,
"Oi", pyport, 10);
if (!pyint) goto fail;
iport = PyLong_AsLong(pyint);
Py_DECREF(pyint);
if (iport == -1 && PyErr_Occurred())
goto fail;
}
else if (pyport && PyLong_Check(pyport)) {
iport = PyLong_AsLong(pyport);
if (iport == -1 && PyErr_Occurred())
goto fail;
}
#endif
else if (pyport != NULL) {
PyErr_SetString(PyExc_TypeError, "port must be a string or int");
goto fail;
@ -292,13 +309,23 @@ psyco_adapters_init(PyObject *mod)
PyTypeObject *type;
microprotocols_add(&PyFloat_Type, NULL, (PyObject*)&pfloatType);
#if PY_MAJOR_VERSION < 3
microprotocols_add(&PyInt_Type, NULL, (PyObject*)&asisType);
#endif
microprotocols_add(&PyLong_Type, NULL, (PyObject*)&asisType);
microprotocols_add(&PyBool_Type, NULL, (PyObject*)&pbooleanType);
#if PY_MAJOR_VERSION < 3
microprotocols_add(&PyString_Type, NULL, (PyObject*)&qstringType);
#endif
microprotocols_add(&PyUnicode_Type, NULL, (PyObject*)&qstringType);
#if PY_MAJOR_VERSION < 3
microprotocols_add(&PyBuffer_Type, NULL, (PyObject*)&binaryType);
#else
microprotocols_add(&PyBytes_Type, NULL, (PyObject*)&binaryType);
microprotocols_add(&PyByteArray_Type, NULL, (PyObject*)&binaryType);
microprotocols_add(&PyMemoryView_Type, NULL, (PyObject*)&binaryType);
#endif
microprotocols_add(&PyList_Type, NULL, (PyObject*)&listType);
if ((type = (PyTypeObject*)psyco_GetDecimalType()) != NULL)
@ -329,7 +356,32 @@ psyco_adapters_init(PyObject *mod)
Fill the module's postgresql<->python encoding table */
static encodingPair encodings[] = {
{"SQL_ASCII", "ascii"},
{"ABC", "cp1258"},
{"ALT", "cp866"},
{"BIG5", "big5"},
{"EUC_CN", "euccn"},
{"EUC_JIS_2004", "euc_jis_2004"},
{"EUC_JP", "euc_jp"},
{"EUC_KR", "euc_kr"},
{"GB18030", "gb18030"},
{"GBK", "gbk"},
{"ISO_8859_1", "iso8859_1"},
{"ISO_8859_2", "iso8859_2"},
{"ISO_8859_3", "iso8859_3"},
{"ISO_8859_5", "iso8859_5"},
{"ISO_8859_6", "iso8859_6"},
{"ISO_8859_7", "iso8859_7"},
{"ISO_8859_8", "iso8859_8"},
{"ISO_8859_9", "iso8859_9"},
{"ISO_8859_10", "iso8859_10"},
{"ISO_8859_13", "iso8859_13"},
{"ISO_8859_14", "iso8859_14"},
{"ISO_8859_15", "iso8859_15"},
{"ISO_8859_16", "iso8859_16"},
{"JOHAB", "johab"},
{"KOI8", "koi8_r"},
{"KOI8R", "koi8_r"},
{"KOI8U", "koi8_u"},
{"LATIN1", "iso8859_1"},
{"LATIN2", "iso8859_2"},
{"LATIN3", "iso8859_3"},
@ -339,46 +391,30 @@ static encodingPair encodings[] = {
{"LATIN7", "iso8859_13"},
{"LATIN8", "iso8859_14"},
{"LATIN9", "iso8859_15"},
{"ISO88591", "iso8859_1"},
{"ISO88592", "iso8859_2"},
{"ISO88593", "iso8859_3"},
{"ISO88595", "iso8859_5"},
{"ISO88596", "iso8859_6"},
{"ISO88597", "iso8859_7"},
{"ISO885913", "iso8859_13"},
{"ISO88598", "iso8859_8"},
{"ISO88599", "iso8859_9"},
{"ISO885914", "iso8859_14"},
{"ISO885915", "iso8859_15"},
{"UNICODE", "utf_8"}, /* Not valid in 8.2, backward compatibility */
{"UTF8", "utf_8"},
{"WIN950", "cp950"},
{"Windows950", "cp950"},
{"BIG5", "big5"},
{"EUC_JP", "euc_jp"},
{"EUC_KR", "euc_kr"},
{"GB18030", "gb18030"},
{"GBK", "gbk"},
{"WIN936", "gbk"},
{"Windows936", "gbk"},
{"JOHAB", "johab"},
{"KOI8", "koi8_r"}, /* in PG: KOI8 == KOI8R == KOI8-R == KOI8-U
but in Python there is koi8_r AND koi8_u */
{"KOI8R", "koi8_r"},
{"SJIS", "cp932"},
{"LATIN10", "iso8859_16"},
{"Mskanji", "cp932"},
{"ShiftJIS", "cp932"},
{"WIN932", "cp932"},
{"Windows932", "cp932"},
{"SHIFT_JIS_2004", "shift_jis_2004"},
{"SJIS", "cp932"},
{"SQL_ASCII", "ascii"}, /* XXX this is wrong: SQL_ASCII means "no
* encoding" we should fix the unicode
* typecaster to return a str or bytes in Py3
*/
{"TCVN", "cp1258"},
{"TCVN5712", "cp1258"},
{"UHC", "cp949"},
{"WIN949", "cp949"},
{"Windows949", "cp949"},
{"UNICODE", "utf_8"}, /* Not valid in 8.2, backward compatibility */
{"UTF8", "utf_8"},
{"VSCII", "cp1258"},
{"WIN", "cp1251"},
{"WIN866", "cp866"},
{"ALT", "cp866"},
{"WIN874", "cp874"},
{"WIN932", "cp932"},
{"WIN936", "gbk"},
{"WIN949", "cp949"},
{"WIN950", "cp950"},
{"WIN1250", "cp1250"},
{"WIN1251", "cp1251"},
{"WIN", "cp1251"},
{"WIN1252", "cp1252"},
{"WIN1253", "cp1253"},
{"WIN1254", "cp1254"},
@ -386,16 +422,13 @@ static encodingPair encodings[] = {
{"WIN1256", "cp1256"},
{"WIN1257", "cp1257"},
{"WIN1258", "cp1258"},
{"ABC", "cp1258"},
{"TCVN", "cp1258"},
{"TCVN5712", "cp1258"},
{"VSCII", "cp1258"},
{"Windows932", "cp932"},
{"Windows936", "gbk"},
{"Windows949", "cp949"},
{"Windows950", "cp950"},
/* those are missing from Python: */
/* {"EUC_CN", "?"}, */
/* {"EUC_TW", "?"}, */
/* {"LATIN10", "?"}, */
/* {"ISO885916", "?"}, */
/* {"MULE_INTERNAL", "?"}, */
{NULL, NULL}
};
@ -405,7 +438,7 @@ static void psyco_encodings_fill(PyObject *dict)
encodingPair *enc;
for (enc = encodings; enc->pgenc != NULL; enc++) {
PyObject *value = PyString_FromString(enc->pyenc);
PyObject *value = Text_FromUTF8(enc->pyenc);
PyDict_SetItemString(dict, enc->pgenc, value);
Py_DECREF(value);
}
@ -470,12 +503,18 @@ psyco_errors_init(void)
dict = PyDict_New();
if (exctable[i].docstr) {
str = PyString_FromString(exctable[i].docstr);
str = Text_FromUTF8(exctable[i].docstr);
PyDict_SetItemString(dict, "__doc__", str);
}
if (exctable[i].base == 0)
if (exctable[i].base == 0) {
#if PY_MAJOR_VERSION < 3
base = PyExc_StandardError;
#else
/* StandardError is gone in 3.0 */
base = NULL;
#endif
}
else
base = *exctable[i].base;
@ -543,23 +582,26 @@ psyco_set_error(PyObject *exc, PyObject *curs, const char *msg,
PyObject *err = PyObject_CallFunction(exc, "s", msg);
if (err) {
connectionObject *conn = NULL;
if (curs) {
PyObject_SetAttrString(err, "cursor", curs);
conn = ((cursorObject *)curs)->conn;
}
if (pgerror) {
t = PyString_FromString(pgerror);
t = conn_text_from_chars(conn, pgerror);
PyObject_SetAttrString(err, "pgerror", t);
Py_DECREF(t);
}
if (pgcode) {
t = PyString_FromString(pgcode);
t = conn_text_from_chars(conn, pgcode);
PyObject_SetAttrString(err, "pgcode", t);
Py_DECREF(t);
}
if (curs)
PyObject_SetAttrString(err, "cursor", curs);
PyErr_SetObject(exc, err);
Py_DECREF(err);
Py_DECREF(err);
}
}
@ -701,13 +743,29 @@ static PyMethodDef psycopgMethods[] = {
{NULL, NULL, 0, NULL} /* Sentinel */
};
PyMODINIT_FUNC
init_psycopg(void)
{
static void *PSYCOPG_API[PSYCOPG_API_pointers];
#if PY_MAJOR_VERSION > 2
static struct PyModuleDef psycopgmodule = {
PyModuleDef_HEAD_INIT,
"_psycopg",
NULL,
-1,
psycopgMethods,
NULL,
NULL,
NULL,
NULL
};
#endif
PyObject *module, *dict;
PyMODINIT_FUNC
INIT_MODULE(_psycopg)(void)
{
#if PY_VERSION_HEX < 0x03020000
static void *PSYCOPG_API[PSYCOPG_API_pointers];
PyObject *c_api_object;
#endif
PyObject *module = NULL, *dict;
#ifdef PSYCOPG_DEBUG
if (getenv("PSYCOPG_DEBUG"))
@ -717,51 +775,51 @@ init_psycopg(void)
Dprintf("initpsycopg: initializing psycopg %s", PSYCOPG_VERSION);
/* initialize all the new types and then the module */
connectionType.ob_type = &PyType_Type;
cursorType.ob_type = &PyType_Type;
typecastType.ob_type = &PyType_Type;
qstringType.ob_type = &PyType_Type;
binaryType.ob_type = &PyType_Type;
isqlquoteType.ob_type = &PyType_Type;
pbooleanType.ob_type = &PyType_Type;
pfloatType.ob_type = &PyType_Type;
pdecimalType.ob_type = &PyType_Type;
asisType.ob_type = &PyType_Type;
listType.ob_type = &PyType_Type;
chunkType.ob_type = &PyType_Type;
NotifyType.ob_type = &PyType_Type;
XidType.ob_type = &PyType_Type;
Py_TYPE(&connectionType) = &PyType_Type;
Py_TYPE(&cursorType) = &PyType_Type;
Py_TYPE(&typecastType) = &PyType_Type;
Py_TYPE(&qstringType) = &PyType_Type;
Py_TYPE(&binaryType) = &PyType_Type;
Py_TYPE(&isqlquoteType) = &PyType_Type;
Py_TYPE(&pbooleanType) = &PyType_Type;
Py_TYPE(&pfloatType) = &PyType_Type;
Py_TYPE(&pdecimalType) = &PyType_Type;
Py_TYPE(&asisType) = &PyType_Type;
Py_TYPE(&listType) = &PyType_Type;
Py_TYPE(&chunkType) = &PyType_Type;
Py_TYPE(&NotifyType) = &PyType_Type;
Py_TYPE(&XidType) = &PyType_Type;
if (PyType_Ready(&connectionType) == -1) return;
if (PyType_Ready(&cursorType) == -1) return;
if (PyType_Ready(&typecastType) == -1) return;
if (PyType_Ready(&qstringType) == -1) return;
if (PyType_Ready(&binaryType) == -1) return;
if (PyType_Ready(&isqlquoteType) == -1) return;
if (PyType_Ready(&pbooleanType) == -1) return;
if (PyType_Ready(&pfloatType) == -1) return;
if (PyType_Ready(&pdecimalType) == -1) return;
if (PyType_Ready(&asisType) == -1) return;
if (PyType_Ready(&listType) == -1) return;
if (PyType_Ready(&chunkType) == -1) return;
if (PyType_Ready(&NotifyType) == -1) return;
if (PyType_Ready(&XidType) == -1) return;
if (PyType_Ready(&connectionType) == -1) goto exit;
if (PyType_Ready(&cursorType) == -1) goto exit;
if (PyType_Ready(&typecastType) == -1) goto exit;
if (PyType_Ready(&qstringType) == -1) goto exit;
if (PyType_Ready(&binaryType) == -1) goto exit;
if (PyType_Ready(&isqlquoteType) == -1) goto exit;
if (PyType_Ready(&pbooleanType) == -1) goto exit;
if (PyType_Ready(&pfloatType) == -1) goto exit;
if (PyType_Ready(&pdecimalType) == -1) goto exit;
if (PyType_Ready(&asisType) == -1) goto exit;
if (PyType_Ready(&listType) == -1) goto exit;
if (PyType_Ready(&chunkType) == -1) goto exit;
if (PyType_Ready(&NotifyType) == -1) goto exit;
if (PyType_Ready(&XidType) == -1) goto exit;
#ifdef PSYCOPG_EXTENSIONS
lobjectType.ob_type = &PyType_Type;
if (PyType_Ready(&lobjectType) == -1) return;
Py_TYPE(&lobjectType) = &PyType_Type;
if (PyType_Ready(&lobjectType) == -1) goto exit;
#endif
/* import mx.DateTime module, if necessary */
#ifdef HAVE_MXDATETIME
mxdatetimeType.ob_type = &PyType_Type;
if (PyType_Ready(&mxdatetimeType) == -1) return;
Py_TYPE(&mxdatetimeType) = &PyType_Type;
if (PyType_Ready(&mxdatetimeType) == -1) goto exit;
if (mxDateTime_ImportModuleAndAPI() != 0) {
Dprintf("initpsycopg: why marc hide mx.DateTime again?!");
PyErr_SetString(PyExc_ImportError, "can't import mx.DateTime module");
return;
goto exit;
}
if (psyco_adapter_mxdatetime_init()) { return; }
if (psyco_adapter_mxdatetime_init()) { goto exit; }
#endif
/* import python builtin datetime module, if available */
@ -769,22 +827,22 @@ init_psycopg(void)
if (pyDateTimeModuleP == NULL) {
Dprintf("initpsycopg: can't import datetime module");
PyErr_SetString(PyExc_ImportError, "can't import datetime module");
return;
goto exit;
}
/* Initialize the PyDateTimeAPI everywhere is used */
PyDateTime_IMPORT;
if (psyco_adapter_datetime_init()) { return; }
if (psyco_adapter_datetime_init()) { goto exit; }
pydatetimeType.ob_type = &PyType_Type;
if (PyType_Ready(&pydatetimeType) == -1) return;
Py_TYPE(&pydatetimeType) = &PyType_Type;
if (PyType_Ready(&pydatetimeType) == -1) goto exit;
/* import psycopg2.tz anyway (TODO: replace with C-level module?) */
pyPsycopgTzModule = PyImport_ImportModule("psycopg2.tz");
if (pyPsycopgTzModule == NULL) {
Dprintf("initpsycopg: can't import psycopg2.tz module");
PyErr_SetString(PyExc_ImportError, "can't import psycopg2.tz module");
return;
goto exit;
}
pyPsycopgTzLOCAL =
PyObject_GetAttrString(pyPsycopgTzModule, "LOCAL");
@ -792,16 +850,25 @@ init_psycopg(void)
PyObject_GetAttrString(pyPsycopgTzModule, "FixedOffsetTimezone");
/* initialize the module and grab module's dictionary */
#if PY_MAJOR_VERSION < 3
module = Py_InitModule("_psycopg", psycopgMethods);
#else
module = PyModule_Create(&psycopgmodule);
#endif
if (!module) { goto exit; }
dict = PyModule_GetDict(module);
/* initialize all the module's exported functions */
/* PyBoxer_API[PyBoxer_Fake_NUM] = (void *)PyBoxer_Fake; */
/* Create a CObject containing the API pointer array's address */
/* If anybody asks for a PyCapsule we'll deal with it. */
#if PY_VERSION_HEX < 0x03020000
c_api_object = PyCObject_FromVoidPtr((void *)PSYCOPG_API, NULL);
if (c_api_object != NULL)
PyModule_AddObject(module, "_C_API", c_api_object);
#endif
/* other mixed initializations of module-level variables */
psycoEncodings = PyDict_New();
@ -810,9 +877,9 @@ init_psycopg(void)
/* set some module's parameters */
PyModule_AddStringConstant(module, "__version__", PSYCOPG_VERSION);
PyModule_AddStringConstant(module, "__doc__", "psycopg PostgreSQL driver");
PyModule_AddObject(module, "apilevel", PyString_FromString(APILEVEL));
PyModule_AddObject(module, "apilevel", Text_FromUTF8(APILEVEL));
PyModule_AddObject(module, "threadsafety", PyInt_FromLong(THREADSAFETY));
PyModule_AddObject(module, "paramstyle", PyString_FromString(PARAMSTYLE));
PyModule_AddObject(module, "paramstyle", Text_FromUTF8(PARAMSTYLE));
/* put new types in module dictionary */
PyModule_AddObject(module, "connection", (PyObject*)&connectionType);
@ -864,4 +931,11 @@ init_psycopg(void)
#endif
Dprintf("initpsycopg: module initialization complete");
exit:
#if PY_MAJOR_VERSION > 2
return module;
#else
return;
#endif
}

View File

@ -26,9 +26,10 @@
#ifndef PSYCOPG_PYTHON_H
#define PSYCOPG_PYTHON_H 1
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <structmember.h>
#if PY_MAJOR_VERSION < 3
#include <stringobject.h>
#endif
#if PY_VERSION_HEX < 0x02040000
# error "psycopg requires Python >= 2.4"
@ -53,8 +54,17 @@
#define CONV_CODE_PY_SSIZE_T "n"
#endif
#ifndef Py_TYPE
#define Py_TYPE(o) (((PyObject*)(o))->ob_type)
/* Macros defined in Python 2.6 */
#ifndef Py_REFCNT
#define Py_REFCNT(ob) (((PyObject*)(ob))->ob_refcnt)
#define Py_TYPE(ob) (((PyObject*)(ob))->ob_type)
#define Py_SIZE(ob) (((PyVarObject*)(ob))->ob_size)
#define PyVarObject_HEAD_INIT(x,n) PyObject_HEAD_INIT(x) n,
#endif
/* Missing at least in Python 2.4 */
#ifndef Py_MEMCPY
#define Py_MEMCPY memcpy
#endif
/* FORMAT_CODE_PY_SSIZE_T is for Py_ssize_t: */
@ -69,4 +79,73 @@
#define FORMAT_CODE_SIZE_T "%zu"
#endif
/* Abstract from text type. Only supported for ASCII and UTF-8 */
#if PY_MAJOR_VERSION < 3
#define Text_Type PyString_Type
#define Text_Check(s) PyString_Check(s)
#define Text_Format(f,a) PyString_Format(f,a)
#define Text_FromUTF8(s) PyString_FromString(s)
#define Text_FromUTF8AndSize(s,n) PyString_FromStringAndSize(s,n)
#else
#define Text_Type PyUnicode_Type
#define Text_Check(s) PyUnicode_Check(s)
#define Text_Format(f,a) PyUnicode_Format(f,a)
#define Text_FromUTF8(s) PyUnicode_FromString(s)
#define Text_FromUTF8AndSize(s,n) PyUnicode_FromStringAndSize(s,n)
#endif
#if PY_MAJOR_VERSION > 2
#define PyInt_Type PyLong_Type
#define PyInt_AsLong PyLong_AsLong
#define PyInt_FromLong PyLong_FromLong
#define PyInt_FromSsize_t PyLong_FromSsize_t
#define PyString_FromFormat PyUnicode_FromFormat
#define Py_TPFLAGS_HAVE_ITER 0L
#define Py_TPFLAGS_HAVE_RICHCOMPARE 0L
#define Py_TPFLAGS_HAVE_WEAKREFS 0L
#ifndef PyNumber_Int
#define PyNumber_Int PyNumber_Long
#endif
#endif /* PY_MAJOR_VERSION > 2 */
#if PY_MAJOR_VERSION < 3
#define Bytes_Type PyString_Type
#define Bytes_Check PyString_Check
#define Bytes_CheckExact PyString_CheckExact
#define Bytes_AS_STRING PyString_AS_STRING
#define Bytes_GET_SIZE PyString_GET_SIZE
#define Bytes_Size PyString_Size
#define Bytes_AsString PyString_AsString
#define Bytes_AsStringAndSize PyString_AsStringAndSize
#define Bytes_FromString PyString_FromString
#define Bytes_FromStringAndSize PyString_FromStringAndSize
#define Bytes_FromFormat PyString_FromFormat
#define _Bytes_Resize _PyString_Resize
#else
#define Bytes_Type PyBytes_Type
#define Bytes_Check PyBytes_Check
#define Bytes_CheckExact PyBytes_CheckExact
#define Bytes_AS_STRING PyBytes_AS_STRING
#define Bytes_GET_SIZE PyBytes_GET_SIZE
#define Bytes_Size PyBytes_Size
#define Bytes_AsString PyBytes_AsString
#define Bytes_AsStringAndSize PyBytes_AsStringAndSize
#define Bytes_FromString PyBytes_FromString
#define Bytes_FromStringAndSize PyBytes_FromStringAndSize
#define Bytes_FromFormat PyBytes_FromFormat
#define _Bytes_Resize _PyBytes_Resize
#endif
HIDDEN PyObject *Bytes_Format(PyObject *format, PyObject *args);
/* Mangle the module name into the name of the module init function */
#if PY_MAJOR_VERSION > 2
#define INIT_MODULE(m) PyInit_ ## m
#else
#define INIT_MODULE(m) init ## m
#endif
#endif /* !defined(PSYCOPG_PYTHON_H) */

View File

@ -23,14 +23,9 @@
* License for more details.
*/
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <structmember.h>
#define PSYCOPG_MODULE
#include "psycopg/config.h"
#include "psycopg/psycopg.h"
#include "psycopg/python.h"
#include "psycopg/typecast.h"
#include "psycopg/cursor.h"
@ -312,7 +307,7 @@ typecast_add(PyObject *obj, PyObject *dict, int binary)
Dprintf("typecast_add: object at %p, values refcnt = "
FORMAT_CODE_PY_SSIZE_T,
obj, type->values->ob_refcnt
obj, Py_REFCNT(type->values)
);
if (dict == NULL)
@ -393,8 +388,8 @@ typecast_richcompare(PyObject *obj1, PyObject* obj2, int opid)
}
static struct PyMemberDef typecastObject_members[] = {
{"name", T_OBJECT, OFFSETOF(name), RO},
{"values", T_OBJECT, OFFSETOF(values), RO},
{"name", T_OBJECT, OFFSETOF(name), READONLY},
{"values", T_OBJECT, OFFSETOF(values), READONLY},
{NULL}
};
@ -410,7 +405,7 @@ typecast_dealloc(PyObject *obj)
Py_CLEAR(self->pcast);
Py_CLEAR(self->bcast);
obj->ob_type->tp_free(obj);
Py_TYPE(obj)->tp_free(obj);
}
static int
@ -431,32 +426,47 @@ typecast_del(void *self)
PyObject_GC_Del(self);
}
static PyObject *
typecast_repr(PyObject *self)
{
PyObject *name = ((typecastObject *)self)->name;
PyObject *rv;
Py_INCREF(name);
if (!(name = psycopg_ensure_bytes(name))) {
return NULL;
}
rv = PyString_FromFormat("<%s '%s' at %p>",
Py_TYPE(self)->tp_name, Bytes_AS_STRING(name), self);
Py_DECREF(name);
return rv;
}
static PyObject *
typecast_call(PyObject *obj, PyObject *args, PyObject *kwargs)
{
PyObject *string, *cursor;
char *string;
Py_ssize_t length;
PyObject *cursor;
if (!PyArg_ParseTuple(args, "OO", &string, &cursor)) {
if (!PyArg_ParseTuple(args, "z#O", &string, &length, &cursor)) {
return NULL;
}
// If the string is not a string but a None value we're being called
// from a Python-defined caster. There is no need to convert, just
// return it.
if (string == Py_None) {
Py_INCREF(string);
return string;
// from a Python-defined caster.
if (!string) {
Py_INCREF(Py_None);
return Py_None;
}
return typecast_cast(obj,
PyString_AsString(string), PyString_Size(string),
cursor);
return typecast_cast(obj, string, length, cursor);
}
PyTypeObject typecastType = {
PyObject_HEAD_INIT(NULL)
0,
PyVarObject_HEAD_INIT(NULL, 0)
"psycopg2._psycopg.type",
sizeof(typecastObject),
0,
@ -466,7 +476,7 @@ PyTypeObject typecastType = {
0, /*tp_getattr*/
0, /*tp_setattr*/
typecast_cmp, /*tp_compare*/
0, /*tp_repr*/
typecast_repr, /*tp_repr*/
0, /*tp_as_number*/
0, /*tp_as_sequence*/
0, /*tp_as_mapping*/
@ -524,7 +534,7 @@ typecast_new(PyObject *name, PyObject *values, PyObject *cast, PyObject *base)
if (obj == NULL) return NULL;
Dprintf("typecast_new: new type at = %p, refcnt = " FORMAT_CODE_PY_SSIZE_T,
obj, obj->ob_refcnt);
obj, Py_REFCNT(obj));
Py_INCREF(values);
obj->values = values;
@ -566,7 +576,7 @@ typecast_from_python(PyObject *self, PyObject *args, PyObject *keywds)
if (!PyArg_ParseTupleAndKeywords(args, keywds, "O!|O!OO", kwlist,
&PyTuple_Type, &v,
&PyString_Type, &name,
&Text_Type, &name,
&cast, &base)) {
return NULL;
}
@ -591,7 +601,7 @@ typecast_from_c(typecastObject_initlist *type, PyObject *dict)
}
}
name = PyString_FromString(type->name);
name = Text_FromUTF8(type->name);
if (!name) goto end;
while (type->values[len] != 0) len++;
@ -630,7 +640,27 @@ typecast_cast(PyObject *obj, const char *str, Py_ssize_t len, PyObject *curs)
res = self->ccast(str, len, curs);
}
else if (self->pcast) {
res = PyObject_CallFunction(self->pcast, "s#O", str, len, curs);
PyObject *s;
/* XXX we have bytes in the adapters and strings in the typecasters.
* are you sure this is ok?
* Notice that this way it is about impossible to create a python
* typecaster on a binary type. */
if (str) {
#if PY_MAJOR_VERSION < 3
s = PyString_FromStringAndSize(str, len);
#else
s = PyUnicode_Decode(str, len,
((cursorObject *)curs)->conn->codec, NULL);
#endif
}
else {
Py_INCREF(Py_None);
s = Py_None;
}
if (s) {
res = PyObject_CallFunctionObjArgs(self->pcast, s, curs, NULL);
Py_DECREF(s);
}
}
else {
PyErr_SetString(Error, "internal error: no casting function found");

View File

@ -26,11 +26,6 @@
#ifndef PSYCOPG_TYPECAST_H
#define PSYCOPG_TYPECAST_H 1
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include "psycopg/config.h"
#ifdef __cplusplus
extern "C" {
#endif

View File

@ -25,6 +25,7 @@
/** INTEGER - cast normal integers (4 bytes) to python int **/
#if PY_MAJOR_VERSION < 3
static PyObject *
typecast_INTEGER_cast(const char *s, Py_ssize_t len, PyObject *curs)
{
@ -37,6 +38,9 @@ typecast_INTEGER_cast(const char *s, Py_ssize_t len, PyObject *curs)
}
return PyInt_FromString((char *)s, NULL, 0);
}
#else
#define typecast_INTEGER_cast typecast_LONGINTEGER_cast
#endif
/** LONGINTEGER - cast long integers (8 bytes) to python long **/
@ -59,44 +63,42 @@ static PyObject *
typecast_FLOAT_cast(const char *s, Py_ssize_t len, PyObject *curs)
{
PyObject *str = NULL, *flo = NULL;
char *pend;
if (s == NULL) {Py_INCREF(Py_None); return Py_None;}
str = PyString_FromStringAndSize(s, len);
flo = PyFloat_FromString(str, &pend);
str = Text_FromUTF8AndSize(s, len);
#if PY_MAJOR_VERSION < 3
flo = PyFloat_FromString(str, NULL);
#else
flo = PyFloat_FromString(str);
#endif
Py_DECREF(str);
return flo;
}
/** STRING - cast strings of any type to python string **/
#if PY_MAJOR_VERSION < 3
static PyObject *
typecast_STRING_cast(const char *s, Py_ssize_t len, PyObject *curs)
{
if (s == NULL) {Py_INCREF(Py_None); return Py_None;}
return PyString_FromStringAndSize(s, len);
}
#else
#define typecast_STRING_cast typecast_UNICODE_cast
#endif
/** UNICODE - cast strings of any type to a python unicode object **/
static PyObject *
typecast_UNICODE_cast(const char *s, Py_ssize_t len, PyObject *curs)
{
PyObject *enc;
char *enc;
if (s == NULL) {Py_INCREF(Py_None); return Py_None;}
enc = PyDict_GetItemString(psycoEncodings,
((cursorObject*)curs)->conn->encoding);
if (enc) {
return PyUnicode_Decode(s, len, PyString_AsString(enc), NULL);
}
else {
PyErr_Format(InterfaceError,
"can't decode into unicode string from %s",
((cursorObject*)curs)->conn->encoding);
return NULL;
}
enc = ((cursorObject*)curs)->conn->codec;
return PyUnicode_Decode(s, len, enc, NULL);
}
/** BOOLEAN - cast boolean value into right python object **/

View File

@ -25,7 +25,6 @@
#include "typecast_binary.h"
#include <libpq-fe.h>
#include <stdlib.h>
@ -42,7 +41,7 @@ chunk_dealloc(chunkObject *self)
self->base, self->len
);
PQfreemem(self->base);
self->ob_type->tp_free((PyObject *) self);
Py_TYPE(self)->tp_free((PyObject *)self);
}
static PyObject *
@ -54,6 +53,9 @@ chunk_repr(chunkObject *self)
);
}
#if PY_MAJOR_VERSION < 3
/* XXX support 3.0 buffer protocol */
static Py_ssize_t
chunk_getreadbuffer(chunkObject *self, Py_ssize_t segment, void **ptr)
{
@ -83,11 +85,26 @@ static PyBufferProcs chunk_as_buffer =
(charbufferproc) NULL
};
#else
/* 3.0 buffer interface */
int chunk_getbuffer(PyObject *_self, Py_buffer *view, int flags)
{
chunkObject *self = (chunkObject*)_self;
return PyBuffer_FillInfo(view, _self, self->base, self->len, 1, flags);
}
static PyBufferProcs chunk_as_buffer =
{
chunk_getbuffer,
NULL,
};
#endif
#define chunk_doc "memory chunk"
PyTypeObject chunkType = {
PyObject_HEAD_INIT(NULL)
0, /* ob_size */
PyVarObject_HEAD_INIT(NULL, 0)
"psycopg2._psycopg.chunk", /* tp_name */
sizeof(chunkObject), /* tp_basicsize */
0, /* tp_itemsize */
@ -158,8 +175,13 @@ typecast_BINARY_cast(const char *s, Py_ssize_t l, PyObject *curs)
/* size_t->Py_ssize_t cast was validated above: */
chunk->len = (Py_ssize_t) len;
#if PY_MAJOR_VERSION < 3
if ((res = PyBuffer_FromObject((PyObject *)chunk, 0, chunk->len)) == NULL)
goto fail;
#else
if ((res = PyMemoryView_FromObject((PyObject*)chunk)) == NULL)
goto fail;
#endif
/* PyBuffer_FromObject() created a new reference. We'll release our
* reference held in 'chunk' in the 'cleanup' clause. */

View File

@ -26,11 +26,6 @@
#ifndef PSYCOPG_TYPECAST_BINARY_H
#define PSYCOPG_TYPECAST_BINARY_H 1
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include "psycopg/config.h"
#ifdef __cplusplus
extern "C" {
#endif

View File

@ -159,7 +159,7 @@ typecast_PYDATETIME_cast(const char *str, Py_ssize_t len, PyObject *curs)
y, m, d, hh, mm, ss, us, tzinfo);
Dprintf("typecast_PYDATETIME_cast: tzinfo: %p, refcnt = "
FORMAT_CODE_PY_SSIZE_T,
tzinfo, tzinfo->ob_refcnt
tzinfo, Py_REFCNT(tzinfo)
);
Py_DECREF(tzinfo);
}

View File

@ -23,15 +23,13 @@
* License for more details.
*/
#include <Python.h>
#include <string.h>
#define PSYCOPG_MODULE
#include "psycopg/config.h"
#include "psycopg/psycopg.h"
#include "psycopg/connection.h"
#include "psycopg/pgtypes.h"
#include "psycopg/pgversion.h"
#include <string.h>
#include <stdlib.h>
char *
@ -72,3 +70,122 @@ psycopg_escape_string(PyObject *obj, const char *from, Py_ssize_t len,
return to;
}
/* Duplicate a string.
*
* Allocate a new buffer on the Python heap containing the new string.
* 'len' is optional: if 0 the length is calculated.
*
* Return NULL and set an exception in case of error.
*/
char *
psycopg_strdup(const char *from, Py_ssize_t len)
{
char *rv;
if (!len) { len = strlen(from); }
if (!(rv = PyMem_Malloc(len + 1))) {
PyErr_NoMemory();
return NULL;
}
strcpy(rv, from);
return rv;
}
/* Ensure a Python object is a bytes string.
*
* Useful when a char * is required out of it.
*
* The function is ref neutral: steals a ref from obj and adds one to the
* return value. This also means that you shouldn't call the function on a
* borrowed ref, if having the object unallocated is not what you want.
*
* It is safe to call the function on NULL.
*/
PyObject *
psycopg_ensure_bytes(PyObject *obj)
{
PyObject *rv = NULL;
if (!obj) { return NULL; }
if (PyUnicode_CheckExact(obj)) {
rv = PyUnicode_AsUTF8String(obj);
Py_DECREF(obj);
}
else if (Bytes_CheckExact(obj)) {
rv = obj;
}
else {
PyErr_Format(PyExc_TypeError,
"Expected bytes or unicode string, got %s instead",
Py_TYPE(obj)->tp_name);
Py_DECREF(obj); /* steal the ref anyway */
}
return rv;
}
/* Take a Python object and return text from it.
*
* On Py3 this means converting bytes to unicode. On Py2 bytes are fine.
*
* The function is ref neutral: steals a ref from obj and adds one to the
* return value. It is safe to call it on NULL.
*/
PyObject *
psycopg_ensure_text(PyObject *obj)
{
#if PY_MAJOR_VERSION < 3
return obj;
#else
if (obj) {
/* bytes to unicode in Py3 */
PyObject *rv = PyUnicode_FromEncodedObject(obj, "utf8", "replace");
Py_DECREF(obj);
return rv;
}
else {
return NULL;
}
#endif
}
/* Check if a file derives from TextIOBase.
*
* Return 1 if it does, else 0, -1 on errors.
*/
int
psycopg_is_text_file(PyObject *f)
{
/* NULL before any call.
* then io.TextIOBase if exists, else None. */
static PyObject *base;
/* Try to import os.TextIOBase */
if (NULL == base) {
PyObject *m;
Dprintf("psycopg_is_text_file: importing io.TextIOBase");
if (!(m = PyImport_ImportModule("io"))) {
Dprintf("psycopg_is_text_file: io module not found");
PyErr_Clear();
Py_INCREF(Py_None);
base = Py_None;
}
else {
if (!(base = PyObject_GetAttrString(m, "TextIOBase"))) {
Dprintf("psycopg_is_text_file: io.TextIOBase not found");
PyErr_Clear();
Py_INCREF(Py_None);
base = Py_None;
}
}
Py_XDECREF(m);
}
if (base != Py_None) {
return PyObject_IsInstance(f, base);
} else {
return 0;
}
}

View File

@ -27,10 +27,6 @@
#ifndef PSYCOPG_XID_H
#define PSYCOPG_XID_H 1
#include <Python.h>
#include "psycopg/config.h"
extern HIDDEN PyTypeObject XidType;
typedef struct {

View File

@ -24,16 +24,12 @@
* License for more details.
*/
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <structmember.h>
#define PSYCOPG_MODULE
#include "psycopg/config.h"
#include "psycopg/python.h"
#include "psycopg/psycopg.h"
#include "psycopg/xid.h"
static const char xid_doc[] =
"A transaction identifier used for two-phase commit.\n\n"
"Usually returned by the connection methods `~connection.xid()` and\n"
@ -70,12 +66,12 @@ static const char database_doc[] =
"Database the recovered transaction belongs to.";
static PyMemberDef xid_members[] = {
{ "format_id", T_OBJECT, offsetof(XidObject, format_id), RO, (char *)format_id_doc },
{ "gtrid", T_OBJECT, offsetof(XidObject, gtrid), RO, (char *)gtrid_doc },
{ "bqual", T_OBJECT, offsetof(XidObject, bqual), RO, (char *)bqual_doc },
{ "prepared", T_OBJECT, offsetof(XidObject, prepared), RO, (char *)prepared_doc },
{ "owner", T_OBJECT, offsetof(XidObject, owner), RO, (char *)owner_doc },
{ "database", T_OBJECT, offsetof(XidObject, database), RO, (char *)database_doc },
{ "format_id", T_OBJECT, offsetof(XidObject, format_id), READONLY, (char *)format_id_doc },
{ "gtrid", T_OBJECT, offsetof(XidObject, gtrid), READONLY, (char *)gtrid_doc },
{ "bqual", T_OBJECT, offsetof(XidObject, bqual), READONLY, (char *)bqual_doc },
{ "prepared", T_OBJECT, offsetof(XidObject, prepared), READONLY, (char *)prepared_doc },
{ "owner", T_OBJECT, offsetof(XidObject, owner), READONLY, (char *)owner_doc },
{ "database", T_OBJECT, offsetof(XidObject, database), READONLY, (char *)database_doc },
{ NULL }
};
@ -154,11 +150,11 @@ xid_init(XidObject *self, PyObject *args, PyObject *kwargs)
Py_XDECREF(tmp);
tmp = self->gtrid;
self->gtrid = PyString_FromString(gtrid);
self->gtrid = Text_FromUTF8(gtrid);
Py_XDECREF(tmp);
tmp = self->bqual;
self->bqual = PyString_FromString(bqual);
self->bqual = Text_FromUTF8(bqual);
Py_XDECREF(tmp);
return 0;
@ -186,7 +182,7 @@ xid_dealloc(XidObject *self)
Py_CLEAR(self->owner);
Py_CLEAR(self->database);
self->ob_type->tp_free((PyObject *)self);
Py_TYPE(self)->tp_free((PyObject *)self);
}
static void
@ -237,7 +233,7 @@ xid_repr(XidObject *self)
PyObject *args = NULL;
if (Py_None == self->format_id) {
if (!(format = PyString_FromString("<Xid: %r (unparsed)>"))) {
if (!(format = Text_FromUTF8("<Xid: %r (unparsed)>"))) {
goto exit;
}
if (!(args = PyTuple_New(1))) { goto exit; }
@ -245,7 +241,7 @@ xid_repr(XidObject *self)
PyTuple_SET_ITEM(args, 0, self->gtrid);
}
else {
if (!(format = PyString_FromString("<Xid: (%r, %r, %r)>"))) {
if (!(format = Text_FromUTF8("<Xid: (%r, %r, %r)>"))) {
goto exit;
}
if (!(args = PyTuple_New(3))) { goto exit; }
@ -257,7 +253,7 @@ xid_repr(XidObject *self)
PyTuple_SET_ITEM(args, 2, self->bqual);
}
rv = PyString_Format(format, args);
rv = Text_Format(format, args);
exit:
Py_XDECREF(args);
@ -306,8 +302,7 @@ static struct PyMethodDef xid_methods[] = {
};
PyTypeObject XidType = {
PyObject_HEAD_INIT(NULL)
0,
PyVarObject_HEAD_INIT(NULL, 0)
"psycopg2.extensions.Xid",
sizeof(XidObject),
0,
@ -404,7 +399,11 @@ _xid_base64_enc_dec(const char *funcname, PyObject *s)
if (!(base64 = PyImport_ImportModule("base64"))) { goto exit; }
if (!(func = PyObject_GetAttrString(base64, funcname))) { goto exit; }
rv = PyObject_CallFunctionObjArgs(func, s, NULL);
Py_INCREF(s);
if (!(s = psycopg_ensure_bytes(s))) { goto exit; }
rv = psycopg_ensure_text(PyObject_CallFunctionObjArgs(func, s, NULL));
Py_DECREF(s);
exit:
Py_XDECREF(func);
@ -462,7 +461,7 @@ xid_get_tid(XidObject *self)
if (!(ebqual = _xid_encode64(self->bqual))) { goto exit; }
/* rv = "%d_%s_%s" % (format_id, egtrid, ebqual) */
if (!(format = PyString_FromString("%d_%s_%s"))) { goto exit; }
if (!(format = Text_FromUTF8("%d_%s_%s"))) { goto exit; }
if (!(args = PyTuple_New(3))) { goto exit; }
Py_INCREF(self->format_id);
@ -470,7 +469,7 @@ xid_get_tid(XidObject *self)
PyTuple_SET_ITEM(args, 1, egtrid); egtrid = NULL;
PyTuple_SET_ITEM(args, 2, ebqual); ebqual = NULL;
if (!(rv = PyString_Format(format, args))) { goto exit; }
if (!(rv = Text_Format(format, args))) { goto exit; }
}
exit:
@ -626,7 +625,7 @@ XidObject *
xid_from_string(PyObject *str) {
XidObject *rv;
if (!(PyString_Check(str) || PyUnicode_Check(str))) {
if (!(Bytes_Check(str) || PyUnicode_Check(str))) {
PyErr_SetString(PyExc_TypeError, "not a valid transaction id");
return NULL;
}

20
scripts/fix_b.py Normal file
View File

@ -0,0 +1,20 @@
"""Fixer to change b('string') into b'string'."""
# Author: Daniele Varrazzo
import token
from lib2to3 import fixer_base
from lib2to3.pytree import Leaf
class FixB(fixer_base.BaseFix):
PATTERN = """
power< wrapper='b' trailer< '(' arg=[any] ')' > rest=any* >
"""
def transform(self, node, results):
arg = results['arg']
wrapper = results["wrapper"]
if len(arg) == 1 and arg[0].type == token.STRING:
b = Leaf(token.STRING, 'b' + arg[0].value, prefix=wrapper.prefix)
node.children = [ b ] + results['rest']

1006
setup.py

File diff suppressed because it is too large Load Diff

View File

@ -1,111 +1,81 @@
#!/usr/bin/env python
# psycopg2 test suite
#
# Copyright (C) 2007-2011 Federico Di Gregorio <fog@debian.org>
#
# 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.
import os
import sys
from testutils import unittest
dbname = os.environ.get('PSYCOPG2_TESTDB', 'psycopg2_test')
dbhost = os.environ.get('PSYCOPG2_TESTDB_HOST', None)
dbport = os.environ.get('PSYCOPG2_TESTDB_PORT', None)
dbuser = os.environ.get('PSYCOPG2_TESTDB_USER', None)
dbpass = os.environ.get('PSYCOPG2_TESTDB_PASSWORD', None)
# Check if we want to test psycopg's green path.
green = os.environ.get('PSYCOPG2_TEST_GREEN', None)
if green:
if green == '1':
from psycopg2.extras import wait_select as wait_callback
elif green == 'eventlet':
from eventlet.support.psycopg2_patcher import eventlet_wait_callback \
as wait_callback
else:
raise ValueError("please set 'PSYCOPG2_TEST_GREEN' to a valid value")
import psycopg2.extensions
psycopg2.extensions.set_wait_callback(wait_callback)
# Construct a DSN to connect to the test database:
dsn = 'dbname=%s' % dbname
if dbhost is not None:
dsn += ' host=%s' % dbhost
if dbport is not None:
dsn += ' port=%s' % dbport
if dbuser is not None:
dsn += ' user=%s' % dbuser
if dbpass is not None:
dsn += ' password=%s' % dbpass
# If connection to test db fails, bail out early.
import psycopg2
try:
cnn = psycopg2.connect(dsn)
except Exception, e:
print "Failed connection to test db:", e.__class__.__name__, e
print "Please set env vars 'PSYCOPG2_TESTDB*' to valid values."
sys.exit(1)
else:
cnn.close()
import bug_gc
import bugX000
import extras_dictcursor
import test_dates
import test_psycopg2_dbapi20
import test_quote
import test_connection
import test_cursor
import test_transaction
import types_basic
import types_extras
import test_lobject
import test_copy
import test_notify
import test_async
import test_green
import test_cancel
def test_suite():
suite = unittest.TestSuite()
suite.addTest(bug_gc.test_suite())
suite.addTest(bugX000.test_suite())
suite.addTest(extras_dictcursor.test_suite())
suite.addTest(test_dates.test_suite())
suite.addTest(test_psycopg2_dbapi20.test_suite())
suite.addTest(test_quote.test_suite())
suite.addTest(test_connection.test_suite())
suite.addTest(test_cursor.test_suite())
suite.addTest(test_transaction.test_suite())
suite.addTest(types_basic.test_suite())
suite.addTest(types_extras.test_suite())
suite.addTest(test_lobject.test_suite())
suite.addTest(test_copy.test_suite())
suite.addTest(test_notify.test_suite())
suite.addTest(test_async.test_suite())
suite.addTest(test_green.test_suite())
suite.addTest(test_cancel.test_suite())
return suite
if __name__ == '__main__':
unittest.main(defaultTest='test_suite')
#!/usr/bin/env python
# psycopg2 test suite
#
# Copyright (C) 2007-2011 Federico Di Gregorio <fog@debian.org>
#
# 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.
import os
import sys
from testconfig import dsn
from testutils import unittest
# If connection to test db fails, bail out early.
import psycopg2
try:
cnn = psycopg2.connect(dsn)
except Exception, e:
print "Failed connection to test db:", e.__class__.__name__, e
print "Please set env vars 'PSYCOPG2_TESTDB*' to valid values."
sys.exit(1)
else:
cnn.close()
import bug_gc
import bugX000
import extras_dictcursor
import test_dates
import test_psycopg2_dbapi20
import test_quote
import test_connection
import test_cursor
import test_transaction
import types_basic
import types_extras
import test_lobject
import test_copy
import test_notify
import test_async
import test_green
import test_cancel
def test_suite():
suite = unittest.TestSuite()
suite.addTest(bug_gc.test_suite())
suite.addTest(bugX000.test_suite())
suite.addTest(extras_dictcursor.test_suite())
suite.addTest(test_dates.test_suite())
suite.addTest(test_psycopg2_dbapi20.test_suite())
suite.addTest(test_quote.test_suite())
suite.addTest(test_connection.test_suite())
suite.addTest(test_cursor.test_suite())
suite.addTest(test_transaction.test_suite())
suite.addTest(types_basic.test_suite())
suite.addTest(types_extras.test_suite())
suite.addTest(test_lobject.test_suite())
suite.addTest(test_copy.test_suite())
suite.addTest(test_notify.test_suite())
suite.addTest(test_async.test_suite())
suite.addTest(test_green.test_suite())
suite.addTest(test_cancel.test_suite())
return suite
if __name__ == '__main__':
unittest.main(defaultTest='test_suite')

View File

@ -28,17 +28,13 @@ import time
import unittest
import gc
import sys
if sys.version_info < (3,):
import tests
else:
import py3tests as tests
from testconfig import dsn
from testutils import skip_if_no_uuid
class StolenReferenceTestCase(unittest.TestCase):
def setUp(self):
self.conn = psycopg2.connect(tests.dsn)
self.conn = psycopg2.connect(dsn)
def tearDown(self):
self.conn.close()

View File

@ -17,15 +17,14 @@
import psycopg2
import psycopg2.extras
from testutils import unittest
import tests
from testconfig import dsn
class ExtrasDictCursorTests(unittest.TestCase):
"""Test if DictCursor extension class works."""
def setUp(self):
self.conn = psycopg2.connect(tests.dsn)
self.conn = psycopg2.connect(dsn)
curs = self.conn.cursor()
curs.execute("CREATE TEMPORARY TABLE ExtrasDictCursorTests (foo text)")
curs.execute("INSERT INTO ExtrasDictCursorTests VALUES ('bar')")
@ -135,7 +134,7 @@ class NamedTupleCursorTest(unittest.TestCase):
self.conn = None
return
self.conn = psycopg2.connect(tests.dsn,
self.conn = psycopg2.connect(dsn,
connection_factory=NamedTupleConnection)
curs = self.conn.cursor()
curs.execute("CREATE TEMPORARY TABLE nttest (i int, s text)")
@ -207,7 +206,7 @@ class NamedTupleCursorTest(unittest.TestCase):
try:
if self.conn is not None:
self.conn.close()
self.conn = psycopg2.connect(tests.dsn,
self.conn = psycopg2.connect(dsn,
connection_factory=NamedTupleConnection)
curs = self.conn.cursor()
curs.execute("select 1")

View File

@ -32,12 +32,7 @@ import time
import select
import StringIO
import sys
if sys.version_info < (3,):
import tests
else:
import py3tests as tests
from testconfig import dsn
class PollableStub(object):
"""A 'pollable' wrapper allowing analysis of the `poll()` calls."""
@ -57,8 +52,8 @@ class PollableStub(object):
class AsyncTests(unittest.TestCase):
def setUp(self):
self.sync_conn = psycopg2.connect(tests.dsn)
self.conn = psycopg2.connect(tests.dsn, async=True)
self.sync_conn = psycopg2.connect(dsn)
self.conn = psycopg2.connect(dsn, async=True)
self.wait(self.conn)
@ -333,7 +328,7 @@ class AsyncTests(unittest.TestCase):
def __init__(self, dsn, async=0):
psycopg2.extensions.connection.__init__(self, dsn, async=async)
conn = psycopg2.connect(tests.dsn, connection_factory=MyConn, async=True)
conn = psycopg2.connect(dsn, connection_factory=MyConn, async=True)
self.assert_(isinstance(conn, MyConn))
self.assert_(conn.async)
conn.close()

View File

@ -25,18 +25,18 @@
import time
import threading
from testutils import unittest, skip_if_no_pg_sleep
import tests
import psycopg2
import psycopg2.extensions
from psycopg2 import extras
from testconfig import dsn
from testutils import unittest, skip_if_no_pg_sleep
class CancelTests(unittest.TestCase):
def setUp(self):
self.conn = psycopg2.connect(tests.dsn)
self.conn = psycopg2.connect(dsn)
cur = self.conn.cursor()
cur.execute('''
CREATE TEMPORARY TABLE table1 (
@ -88,7 +88,7 @@ class CancelTests(unittest.TestCase):
@skip_if_no_pg_sleep('conn')
def test_async_cancel(self):
async_conn = psycopg2.connect(tests.dsn, async=True)
async_conn = psycopg2.connect(dsn, async=True)
self.assertRaises(psycopg2.OperationalError, async_conn.cancel)
extras.wait_select(async_conn)
cur = async_conn.cursor()
@ -102,7 +102,7 @@ class CancelTests(unittest.TestCase):
self.assertEqual(cur.fetchall(), [(1, )])
def test_async_connection_cancel(self):
async_conn = psycopg2.connect(tests.dsn, async=True)
async_conn = psycopg2.connect(dsn, async=True)
async_conn.close()
self.assertTrue(async_conn.closed)

View File

@ -29,12 +29,12 @@ from operator import attrgetter
import psycopg2
import psycopg2.extensions
import tests
from testconfig import dsn, dbname
class ConnectionTests(unittest.TestCase):
def setUp(self):
self.conn = psycopg2.connect(tests.dsn)
self.conn = psycopg2.connect(dsn)
def tearDown(self):
if not self.conn.closed:
@ -117,7 +117,7 @@ class ConnectionTests(unittest.TestCase):
@skip_if_no_pg_sleep('conn')
def test_concurrent_execution(self):
def slave():
cnn = psycopg2.connect(tests.dsn)
cnn = psycopg2.connect(dsn)
cur = cnn.cursor()
cur.execute("select pg_sleep(2)")
cur.close()
@ -133,9 +133,17 @@ class ConnectionTests(unittest.TestCase):
self.assert_(time.time() - t0 < 3,
"something broken in concurrency")
def test_encoding_name(self):
self.conn.set_client_encoding("EUC_JP")
# conn.encoding is 'EUCJP' now.
cur = self.conn.cursor()
psycopg2.extensions.register_type(psycopg2.extensions.UNICODE, cur)
cur.execute("select 'foo'::text;")
self.assertEqual(cur.fetchone()[0], u'foo')
def test_weakref(self):
from weakref import ref
conn = psycopg2.connect(tests.dsn)
conn = psycopg2.connect(dsn)
w = ref(conn)
conn.close()
del conn
@ -163,7 +171,7 @@ class IsolationLevelsTestCase(unittest.TestCase):
conn.close()
def connect(self):
conn = psycopg2.connect(tests.dsn)
conn = psycopg2.connect(dsn)
self._conns.append(conn)
return conn
@ -330,7 +338,7 @@ class ConnectionTwoPhaseTests(unittest.TestCase):
try:
cur.execute(
"select gid from pg_prepared_xacts where database = %s",
(tests.dbname,))
(dbname,))
except psycopg2.ProgrammingError:
cnn.rollback()
cnn.close()
@ -359,7 +367,7 @@ class ConnectionTwoPhaseTests(unittest.TestCase):
cur.execute("""
select count(*) from pg_prepared_xacts
where database = %s;""",
(tests.dbname,))
(dbname,))
rv = cur.fetchone()[0]
cnn.close()
return rv
@ -374,7 +382,7 @@ class ConnectionTwoPhaseTests(unittest.TestCase):
return rv
def connect(self):
conn = psycopg2.connect(tests.dsn)
conn = psycopg2.connect(dsn)
self._conns.append(conn)
return conn
@ -537,13 +545,13 @@ class ConnectionTwoPhaseTests(unittest.TestCase):
select gid, prepared, owner, database
from pg_prepared_xacts
where database = %s;""",
(tests.dbname,))
(dbname,))
okvals = cur.fetchall()
okvals.sort()
cnn = self.connect()
xids = cnn.tpc_recover()
xids = [ xid for xid in xids if xid.database == tests.dbname ]
xids = [ xid for xid in xids if xid.database == dbname ]
xids.sort(key=attrgetter('gtrid'))
# check the values returned
@ -563,7 +571,7 @@ class ConnectionTwoPhaseTests(unittest.TestCase):
cnn = self.connect()
cur = cnn.cursor()
cur.execute("select gid from pg_prepared_xacts where database = %s;",
(tests.dbname,))
(dbname,))
self.assertEqual('42_Z3RyaWQ=_YnF1YWw=', cur.fetchone()[0])
def test_xid_roundtrip(self):
@ -580,7 +588,7 @@ class ConnectionTwoPhaseTests(unittest.TestCase):
cnn = self.connect()
xids = [ xid for xid in cnn.tpc_recover()
if xid.database == tests.dbname ]
if xid.database == dbname ]
self.assertEqual(1, len(xids))
xid = xids[0]
self.assertEqual(xid.format_id, fid)
@ -602,7 +610,7 @@ class ConnectionTwoPhaseTests(unittest.TestCase):
cnn = self.connect()
xids = [ xid for xid in cnn.tpc_recover()
if xid.database == tests.dbname ]
if xid.database == dbname ]
self.assertEqual(1, len(xids))
xid = xids[0]
self.assertEqual(xid.format_id, None)
@ -648,7 +656,7 @@ class ConnectionTwoPhaseTests(unittest.TestCase):
cnn.tpc_prepare()
cnn.reset()
xid = [ xid for xid in cnn.tpc_recover()
if xid.database == tests.dbname ][0]
if xid.database == dbname ][0]
self.assertEqual(10, xid.format_id)
self.assertEqual('uni', xid.gtrid)
self.assertEqual('code', xid.bqual)
@ -664,7 +672,7 @@ class ConnectionTwoPhaseTests(unittest.TestCase):
cnn.reset()
xid = [ xid for xid in cnn.tpc_recover()
if xid.database == tests.dbname ][0]
if xid.database == dbname ][0]
self.assertEqual(None, xid.format_id)
self.assertEqual('transaction-id', xid.gtrid)
self.assertEqual(None, xid.bqual)

View File

@ -23,18 +23,19 @@
# License for more details.
import os
import sys
import string
from testutils import unittest, decorate_all_tests
from testutils import unittest, decorate_all_tests, skip_if_no_iobase
from cStringIO import StringIO
from itertools import cycle, izip
import psycopg2
import psycopg2.extensions
import tests
from testconfig import dsn, green
def skip_if_green(f):
def skip_if_green_(self):
if tests.green:
if green:
return self.skipTest("copy in async mode currently not supported")
else:
return f(self)
@ -42,7 +43,12 @@ def skip_if_green(f):
return skip_if_green_
class MinimalRead(object):
if sys.version_info[0] < 3:
_base = object
else:
from io import TextIOBase as _base
class MinimalRead(_base):
"""A file wrapper exposing the minimal interface to copy from."""
def __init__(self, f):
self.f = f
@ -53,7 +59,7 @@ class MinimalRead(object):
def readline(self):
return self.f.readline()
class MinimalWrite(object):
class MinimalWrite(_base):
"""A file wrapper exposing the minimal interface to copy to."""
def __init__(self, f):
self.f = f
@ -65,7 +71,10 @@ class MinimalWrite(object):
class CopyTests(unittest.TestCase):
def setUp(self):
self.conn = psycopg2.connect(tests.dsn)
self.conn = psycopg2.connect(dsn)
self._create_temp_table()
def _create_temp_table(self):
curs = self.conn.cursor()
curs.execute('''
CREATE TEMPORARY TABLE tcopy (
@ -126,9 +135,54 @@ class CopyTests(unittest.TestCase):
finally:
curs.close()
@skip_if_no_iobase
def test_copy_text(self):
self.conn.set_client_encoding('latin1')
self._create_temp_table() # the above call closed the xn
if sys.version_info[0] < 3:
abin = ''.join(map(chr, range(32, 127) + range(160, 256)))
about = abin.decode('latin1').replace('\\', '\\\\')
else:
abin = bytes(range(32, 127) + range(160, 256)).decode('latin1')
about = abin.replace('\\', '\\\\')
curs = self.conn.cursor()
curs.execute('insert into tcopy values (%s, %s)',
(42, abin))
import io
f = io.StringIO()
curs.copy_to(f, 'tcopy', columns=('data',))
f.seek(0)
self.assertEqual(f.readline().rstrip(), about)
@skip_if_no_iobase
def test_copy_bytes(self):
self.conn.set_client_encoding('latin1')
self._create_temp_table() # the above call closed the xn
if sys.version_info[0] < 3:
abin = ''.join(map(chr, range(32, 127) + range(160, 255)))
about = abin.replace('\\', '\\\\')
else:
abin = bytes(range(32, 127) + range(160, 255)).decode('latin1')
about = abin.replace('\\', '\\\\').encode('latin1')
curs = self.conn.cursor()
curs.execute('insert into tcopy values (%s, %s)',
(42, abin))
import io
f = io.BytesIO()
curs.copy_to(f, 'tcopy', columns=('data',))
f.seek(0)
self.assertEqual(f.readline().rstrip(), about)
def _copy_from(self, curs, nrecs, srec, copykw):
f = StringIO()
for i, c in izip(xrange(nrecs), cycle(string.letters)):
for i, c in izip(xrange(nrecs), cycle(string.ascii_letters)):
l = c * srec
f.write("%s\t%s\n" % (i,l))
@ -139,9 +193,9 @@ class CopyTests(unittest.TestCase):
self.assertEqual(nrecs, curs.fetchone()[0])
curs.execute("select data from tcopy where id < %s order by id",
(len(string.letters),))
(len(string.ascii_letters),))
for i, (l,) in enumerate(curs):
self.assertEqual(l, string.letters[i] * srec)
self.assertEqual(l, string.ascii_letters[i] * srec)
def _copy_to(self, curs, srec):
f = StringIO()
@ -151,11 +205,11 @@ class CopyTests(unittest.TestCase):
ntests = 0
for line in f:
n, s = line.split()
if int(n) < len(string.letters):
self.assertEqual(s, string.letters[int(n)] * srec)
if int(n) < len(string.ascii_letters):
self.assertEqual(s, string.ascii_letters[int(n)] * srec)
ntests += 1
self.assertEqual(ntests, len(string.letters))
self.assertEqual(ntests, len(string.ascii_letters))
decorate_all_tests(CopyTests, skip_if_green)

View File

@ -22,15 +22,17 @@
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
# License for more details.
import time
import unittest
import psycopg2
import psycopg2.extensions
import tests
from psycopg2.extensions import b
from testconfig import dsn
class CursorTests(unittest.TestCase):
def setUp(self):
self.conn = psycopg2.connect(tests.dsn)
self.conn = psycopg2.connect(dsn)
def tearDown(self):
self.conn.close()
@ -40,7 +42,7 @@ class CursorTests(unittest.TestCase):
cur = conn.cursor()
cur.execute("create temp table test_exc (data int);")
def buggygen():
yield 1/0
yield 1//0
self.assertRaises(ZeroDivisionError,
cur.executemany, "insert into test_exc values (%s)", buggygen())
cur.close()
@ -54,28 +56,28 @@ class CursorTests(unittest.TestCase):
# unicode query containing only ascii data
cur.execute(u"SELECT 'foo';")
self.assertEqual('foo', cur.fetchone()[0])
self.assertEqual("SELECT 'foo';", cur.mogrify(u"SELECT 'foo';"))
self.assertEqual(b("SELECT 'foo';"), cur.mogrify(u"SELECT 'foo';"))
conn.set_client_encoding('UTF8')
snowman = u"\u2603"
# unicode query with non-ascii data
cur.execute(u"SELECT '%s';" % snowman)
self.assertEqual(snowman.encode('utf8'), cur.fetchone()[0])
self.assertEqual("SELECT '%s';" % snowman.encode('utf8'),
cur.mogrify(u"SELECT '%s';" % snowman).replace("E'", "'"))
self.assertEqual(snowman.encode('utf8'), b(cur.fetchone()[0]))
self.assertEqual(("SELECT '%s';" % snowman).encode('utf8'),
cur.mogrify(u"SELECT '%s';" % snowman).replace(b("E'"), b("'")))
# unicode args
cur.execute("SELECT %s;", (snowman,))
self.assertEqual(snowman.encode("utf-8"), cur.fetchone()[0])
self.assertEqual("SELECT '%s';" % snowman.encode('utf8'),
cur.mogrify("SELECT %s;", (snowman,)).replace("E'", "'"))
self.assertEqual(snowman.encode("utf-8"), b(cur.fetchone()[0]))
self.assertEqual(("SELECT '%s';" % snowman).encode('utf8'),
cur.mogrify("SELECT %s;", (snowman,)).replace(b("E'"), b("'")))
# unicode query and args
cur.execute(u"SELECT %s;", (snowman,))
self.assertEqual(snowman.encode("utf-8"), cur.fetchone()[0])
self.assertEqual("SELECT '%s';" % snowman.encode('utf8'),
cur.mogrify(u"SELECT %s;", (snowman,)).replace("E'", "'"))
self.assertEqual(snowman.encode("utf-8"), b(cur.fetchone()[0]))
self.assertEqual(("SELECT '%s';" % snowman).encode('utf8'),
cur.mogrify(u"SELECT %s;", (snowman,)).replace(b("E'"), b("'")))
def test_mogrify_decimal_explodes(self):
# issue #7: explodes on windows with python 2.5 and psycopg 2.2.2
@ -86,7 +88,7 @@ class CursorTests(unittest.TestCase):
conn = self.conn
cur = conn.cursor()
self.assertEqual('SELECT 10.3;',
self.assertEqual(b('SELECT 10.3;'),
cur.mogrify("SELECT %s;", (Decimal("10.3"),)))
def test_cast(self):
@ -128,6 +130,34 @@ class CursorTests(unittest.TestCase):
del curs
self.assert_(w() is None)
def test_iter_named_cursor_efficient(self):
curs = self.conn.cursor('tmp')
# if these records are fetched in the same roundtrip their
# timestamp will not be influenced by the pause in Python world.
curs.execute("""select clock_timestamp() from generate_series(1,2)""")
i = iter(curs)
t1 = i.next()[0]
time.sleep(0.2)
t2 = i.next()[0]
self.assert_((t2 - t1).microseconds * 1e-6 < 0.1,
"named cursor records fetched in 2 roundtrips (delta: %s)"
% (t2 - t1))
def test_iter_named_cursor_default_arraysize(self):
curs = self.conn.cursor('tmp')
curs.execute('select generate_series(1,50)')
rv = [ (r[0], curs.rownumber) for r in curs ]
# everything swallowed in one gulp
self.assertEqual(rv, [(i,i) for i in range(1,51)])
def test_iter_named_cursor_arraysize(self):
curs = self.conn.cursor('tmp')
curs.arraysize = 30
curs.execute('select generate_series(1,50)')
rv = [ (r[0], curs.rownumber) for r in curs ]
# everything swallowed in two gulps
self.assertEqual(rv, [(i,((i - 1) % 30) + 1) for i in range(1,51)])
def test_suite():
return unittest.TestLoader().loadTestsFromName(__name__)

View File

@ -24,9 +24,9 @@
import math
import unittest
import tests
import psycopg2
from psycopg2.tz import FixedOffsetTimezone
from testconfig import dsn
class CommonDatetimeTestsMixin:
@ -97,7 +97,7 @@ class DatetimeTests(unittest.TestCase, CommonDatetimeTestsMixin):
"""Tests for the datetime based date handling in psycopg2."""
def setUp(self):
self.conn = psycopg2.connect(tests.dsn)
self.conn = psycopg2.connect(dsn)
self.curs = self.conn.cursor()
self.DATE = psycopg2._psycopg.PYDATE
self.TIME = psycopg2._psycopg.PYTIME
@ -265,18 +265,18 @@ class DatetimeTests(unittest.TestCase, CommonDatetimeTestsMixin):
def test_type_roundtrip_date(self):
from datetime import date
self._test_type_roundtrip(date(2010,05,03))
self._test_type_roundtrip(date(2010,5,3))
def test_type_roundtrip_datetime(self):
from datetime import datetime
dt = self._test_type_roundtrip(datetime(2010,05,03,10,20,30))
dt = self._test_type_roundtrip(datetime(2010,5,3,10,20,30))
self.assertEqual(None, dt.tzinfo)
def test_type_roundtrip_datetimetz(self):
from datetime import datetime
import psycopg2.tz
tz = psycopg2.tz.FixedOffsetTimezone(8*60)
dt1 = datetime(2010,05,03,10,20,30, tzinfo=tz)
dt1 = datetime(2010,5,3,10,20,30, tzinfo=tz)
dt2 = self._test_type_roundtrip(dt1)
self.assertNotEqual(None, dt2.tzinfo)
self.assertEqual(dt1, dt2)
@ -291,11 +291,11 @@ class DatetimeTests(unittest.TestCase, CommonDatetimeTestsMixin):
def test_type_roundtrip_date_array(self):
from datetime import date
self._test_type_roundtrip_array(date(2010,05,03))
self._test_type_roundtrip_array(date(2010,5,3))
def test_type_roundtrip_datetime_array(self):
from datetime import datetime
self._test_type_roundtrip_array(datetime(2010,05,03,10,20,30))
self._test_type_roundtrip_array(datetime(2010,5,3,10,20,30))
def test_type_roundtrip_time_array(self):
from datetime import time
@ -315,7 +315,7 @@ class mxDateTimeTests(unittest.TestCase, CommonDatetimeTestsMixin):
"""Tests for the mx.DateTime based date handling in psycopg2."""
def setUp(self):
self.conn = psycopg2.connect(tests.dsn)
self.conn = psycopg2.connect(dsn)
self.curs = self.conn.cursor()
self.DATE = psycopg2._psycopg.MXDATE
self.TIME = psycopg2._psycopg.MXTIME
@ -448,11 +448,11 @@ class mxDateTimeTests(unittest.TestCase, CommonDatetimeTestsMixin):
def test_type_roundtrip_date(self):
from mx.DateTime import Date
self._test_type_roundtrip(Date(2010,05,03))
self._test_type_roundtrip(Date(2010,5,3))
def test_type_roundtrip_datetime(self):
from mx.DateTime import DateTime
self._test_type_roundtrip(DateTime(2010,05,03,10,20,30))
self._test_type_roundtrip(DateTime(2010,5,3,10,20,30))
def test_type_roundtrip_time(self):
from mx.DateTime import Time
@ -464,11 +464,11 @@ class mxDateTimeTests(unittest.TestCase, CommonDatetimeTestsMixin):
def test_type_roundtrip_date_array(self):
from mx.DateTime import Date
self._test_type_roundtrip_array(Date(2010,05,03))
self._test_type_roundtrip_array(Date(2010,5,3))
def test_type_roundtrip_datetime_array(self):
from mx.DateTime import DateTime
self._test_type_roundtrip_array(DateTime(2010,05,03,10,20,30))
self._test_type_roundtrip_array(DateTime(2010,5,3,10,20,30))
def test_type_roundtrip_time_array(self):
from mx.DateTime import Time

View File

@ -26,7 +26,7 @@ import unittest
import psycopg2
import psycopg2.extensions
import psycopg2.extras
import tests
from testconfig import dsn
class ConnectionStub(object):
"""A `connection` wrapper allowing analysis of the `poll()` calls."""
@ -46,7 +46,7 @@ class GreenTests(unittest.TestCase):
def setUp(self):
self._cb = psycopg2.extensions.get_wait_callback()
psycopg2.extensions.set_wait_callback(psycopg2.extras.wait_select)
self.conn = psycopg2.connect(tests.dsn)
self.conn = psycopg2.connect(dsn)
def tearDown(self):
self.conn.close()
@ -85,7 +85,7 @@ class GreenTests(unittest.TestCase):
curs.fetchone()
# now try to do something that will fail in the callback
psycopg2.extensions.set_wait_callback(lambda conn: 1/0)
psycopg2.extensions.set_wait_callback(lambda conn: 1//0)
self.assertRaises(ZeroDivisionError, curs.execute, "select 2")
# check that the connection is left in an usable state

View File

@ -29,7 +29,9 @@ from testutils import unittest, decorate_all_tests, skip_if_tpc_disabled
import psycopg2
import psycopg2.extensions
import tests
from psycopg2.extensions import b
from testconfig import dsn, green
from testutils import unittest, decorate_all_tests
def skip_if_no_lo(f):
def skip_if_no_lo_(self):
@ -42,7 +44,7 @@ def skip_if_no_lo(f):
def skip_if_green(f):
def skip_if_green_(self):
if tests.green:
if green:
return self.skipTest("libpq doesn't support LO in async mode")
else:
return f(self)
@ -75,14 +77,14 @@ class LargeObjectMixin(object):
self.conn.close()
def connect(self):
return psycopg2.connect(tests.dsn)
return psycopg2.connect(dsn)
class LargeObjectTests(LargeObjectMixin, unittest.TestCase):
def test_create(self):
lo = self.conn.lobject()
self.assertNotEqual(lo, None)
self.assertEqual(lo.mode, "w")
self.assertEqual(lo.mode[0], "w")
def test_open_non_existent(self):
# By creating then removing a large object, we get an Oid that
@ -96,13 +98,13 @@ class LargeObjectTests(LargeObjectMixin, unittest.TestCase):
lo2 = self.conn.lobject(lo.oid)
self.assertNotEqual(lo2, None)
self.assertEqual(lo2.oid, lo.oid)
self.assertEqual(lo2.mode, "r")
self.assertEqual(lo2.mode[0], "r")
def test_open_for_write(self):
lo = self.conn.lobject()
lo2 = self.conn.lobject(lo.oid, "w")
self.assertEqual(lo2.mode, "w")
lo2.write("some data")
self.assertEqual(lo2.mode[0], "w")
lo2.write(b("some data"))
def test_open_mode_n(self):
# Openning an object in mode "n" gives us a closed lobject.
@ -138,7 +140,7 @@ class LargeObjectTests(LargeObjectMixin, unittest.TestCase):
self.tmpdir = tempfile.mkdtemp()
filename = os.path.join(self.tmpdir, "data.txt")
fp = open(filename, "wb")
fp.write("some data")
fp.write(b("some data"))
fp.close()
lo = self.conn.lobject(0, "r", 0, filename)
@ -152,7 +154,7 @@ class LargeObjectTests(LargeObjectMixin, unittest.TestCase):
def test_write(self):
lo = self.conn.lobject()
self.assertEqual(lo.write("some data"), len("some data"))
self.assertEqual(lo.write(b("some data")), len("some data"))
def test_write_large(self):
lo = self.conn.lobject()
@ -161,26 +163,54 @@ class LargeObjectTests(LargeObjectMixin, unittest.TestCase):
def test_read(self):
lo = self.conn.lobject()
length = lo.write("some data")
length = lo.write(b("some data"))
lo.close()
lo = self.conn.lobject(lo.oid)
self.assertEqual(lo.read(4), "some")
x = lo.read(4)
self.assertEqual(type(x), type(''))
self.assertEqual(x, "some")
self.assertEqual(lo.read(), " data")
def test_read_binary(self):
lo = self.conn.lobject()
length = lo.write(b("some data"))
lo.close()
lo = self.conn.lobject(lo.oid, "rb")
x = lo.read(4)
self.assertEqual(type(x), type(b('')))
self.assertEqual(x, b("some"))
self.assertEqual(lo.read(), b(" data"))
def test_read_text(self):
lo = self.conn.lobject()
snowman = u"\u2603"
length = lo.write(u"some data " + snowman)
lo.close()
lo = self.conn.lobject(lo.oid, "rt")
x = lo.read(4)
self.assertEqual(type(x), type(u''))
self.assertEqual(x, u"some")
self.assertEqual(lo.read(), u" data " + snowman)
def test_read_large(self):
lo = self.conn.lobject()
data = "data" * 1000000
length = lo.write("some"+data)
length = lo.write("some" + data)
lo.close()
lo = self.conn.lobject(lo.oid)
self.assertEqual(lo.read(4), "some")
self.assertEqual(lo.read(), data)
data1 = lo.read()
# avoid dumping megacraps in the console in case of error
self.assert_(data == data1,
"%r... != %r..." % (data[:100], data1[:100]))
def test_seek_tell(self):
lo = self.conn.lobject()
length = lo.write("some data")
length = lo.write(b("some data"))
self.assertEqual(lo.tell(), length)
lo.close()
lo = self.conn.lobject(lo.oid)
@ -210,13 +240,17 @@ class LargeObjectTests(LargeObjectMixin, unittest.TestCase):
def test_export(self):
lo = self.conn.lobject()
lo.write("some data")
lo.write(b("some data"))
self.tmpdir = tempfile.mkdtemp()
filename = os.path.join(self.tmpdir, "data.txt")
lo.export(filename)
self.assertTrue(os.path.exists(filename))
self.assertEqual(open(filename, "rb").read(), "some data")
f = open(filename, "rb")
try:
self.assertEqual(f.read(), b("some data"))
finally:
f.close()
def test_close_twice(self):
lo = self.conn.lobject()
@ -226,7 +260,7 @@ class LargeObjectTests(LargeObjectMixin, unittest.TestCase):
def test_write_after_close(self):
lo = self.conn.lobject()
lo.close()
self.assertRaises(psycopg2.InterfaceError, lo.write, "some data")
self.assertRaises(psycopg2.InterfaceError, lo.write, b("some data"))
def test_read_after_close(self):
lo = self.conn.lobject()
@ -251,14 +285,18 @@ class LargeObjectTests(LargeObjectMixin, unittest.TestCase):
def test_export_after_close(self):
lo = self.conn.lobject()
lo.write("some data")
lo.write(b("some data"))
lo.close()
self.tmpdir = tempfile.mkdtemp()
filename = os.path.join(self.tmpdir, "data.txt")
lo.export(filename)
self.assertTrue(os.path.exists(filename))
self.assertEqual(open(filename, "rb").read(), "some data")
f = open(filename, "rb")
try:
self.assertEqual(f.read(), b("some data"))
finally:
f.close()
def test_close_after_commit(self):
lo = self.conn.lobject()
@ -273,7 +311,7 @@ class LargeObjectTests(LargeObjectMixin, unittest.TestCase):
self.lo_oid = lo.oid
self.conn.commit()
self.assertRaises(psycopg2.ProgrammingError, lo.write, "some data")
self.assertRaises(psycopg2.ProgrammingError, lo.write, b("some data"))
def test_read_after_commit(self):
lo = self.conn.lobject()
@ -306,14 +344,18 @@ class LargeObjectTests(LargeObjectMixin, unittest.TestCase):
def test_export_after_commit(self):
lo = self.conn.lobject()
lo.write("some data")
lo.write(b("some data"))
self.conn.commit()
self.tmpdir = tempfile.mkdtemp()
filename = os.path.join(self.tmpdir, "data.txt")
lo.export(filename)
self.assertTrue(os.path.exists(filename))
self.assertEqual(open(filename, "rb").read(), "some data")
f = open(filename, "rb")
try:
self.assertEqual(f.read(), b("some data"))
finally:
f.close()
@skip_if_tpc_disabled
def test_read_after_tpc_commit(self):
@ -357,7 +399,7 @@ def skip_if_no_truncate(f):
class LargeObjectTruncateTests(LargeObjectMixin, unittest.TestCase):
def test_truncate(self):
lo = self.conn.lobject()
lo.write("some data")
lo.write(b("some data"))
lo.close()
lo = self.conn.lobject(lo.oid, "w")
@ -366,17 +408,17 @@ class LargeObjectTruncateTests(LargeObjectMixin, unittest.TestCase):
# seek position unchanged
self.assertEqual(lo.tell(), 0)
# data truncated
self.assertEqual(lo.read(), "some")
self.assertEqual(lo.read(), b("some"))
lo.truncate(6)
lo.seek(0)
# large object extended with zeroes
self.assertEqual(lo.read(), "some\x00\x00")
self.assertEqual(lo.read(), b("some\x00\x00"))
lo.truncate()
lo.seek(0)
# large object empty
self.assertEqual(lo.read(), "")
self.assertEqual(lo.read(), b(""))
def test_truncate_after_close(self):
lo = self.conn.lobject()

View File

@ -26,23 +26,20 @@ from testutils import unittest
import psycopg2
from psycopg2 import extensions
from testconfig import dsn
from testutils import script_to_py3
import sys
import time
import select
import signal
from subprocess import Popen, PIPE
import sys
if sys.version_info < (3,):
import tests
else:
import py3tests as tests
class NotifiesTests(unittest.TestCase):
def setUp(self):
self.conn = psycopg2.connect(tests.dsn)
self.conn = psycopg2.connect(dsn)
def tearDown(self):
self.conn.close()
@ -77,9 +74,9 @@ curs.execute("NOTIFY " %(name)r %(payload)r)
curs.close()
conn.close()
"""
% { 'dsn': tests.dsn, 'sec': sec, 'name': name, 'payload': payload})
% { 'dsn': dsn, 'sec': sec, 'name': name, 'payload': payload})
return Popen([sys.executable, '-c', script], stdout=PIPE)
return Popen([sys.executable, '-c', script_to_py3(script)], stdout=PIPE)
def test_notifies_received_on_poll(self):
self.autocommit(self.conn)

View File

@ -28,12 +28,12 @@ from testutils import skip_if_tpc_disabled
from testutils import unittest, decorate_all_tests
import psycopg2
import tests
from testconfig import dsn
class Psycopg2Tests(dbapi20.DatabaseAPI20Test):
driver = psycopg2
connect_args = ()
connect_kw_args = {'dsn': tests.dsn}
connect_kw_args = {'dsn': dsn}
lower_func = 'lower' # For stored procedure test
@ -50,7 +50,7 @@ class Psycopg2TPCTests(dbapi20_tpc.TwoPhaseCommitTests, unittest.TestCase):
driver = psycopg2
def connect(self):
return psycopg2.connect(dsn=tests.dsn)
return psycopg2.connect(dsn=dsn)
decorate_all_tests(Psycopg2TPCTests, skip_if_tpc_disabled)

View File

@ -22,11 +22,13 @@
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
# License for more details.
import sys
from testutils import unittest
from testconfig import dsn
import psycopg2
import psycopg2.extensions
import tests
from psycopg2.extensions import b
class QuotingTestCase(unittest.TestCase):
r"""Checks the correct quoting of strings and binary objects.
@ -47,7 +49,7 @@ class QuotingTestCase(unittest.TestCase):
http://www.postgresql.org/docs/8.1/static/runtime-config-compatible.html
"""
def setUp(self):
self.conn = psycopg2.connect(tests.dsn)
self.conn = psycopg2.connect(dsn)
def tearDown(self):
self.conn.close()
@ -66,14 +68,20 @@ class QuotingTestCase(unittest.TestCase):
self.assert_(not self.conn.notices)
def test_binary(self):
data = """some data with \000\013 binary
data = b("""some data with \000\013 binary
stuff into, 'quotes' and \\ a backslash too.
"""
data += "".join(map(chr, range(256)))
""")
if sys.version_info[0] < 3:
data += "".join(map(chr, range(256)))
else:
data += bytes(range(256))
curs = self.conn.cursor()
curs.execute("SELECT %s::bytea;", (psycopg2.Binary(data),))
res = str(curs.fetchone()[0])
if sys.version_info[0] < 3:
res = str(curs.fetchone()[0])
else:
res = curs.fetchone()[0].tobytes()
self.assertEqual(res, data)
self.assert_(not self.conn.notices)
@ -101,6 +109,55 @@ class QuotingTestCase(unittest.TestCase):
self.assertEqual(res, data)
self.assert_(not self.conn.notices)
def test_latin1(self):
self.conn.set_client_encoding('LATIN1')
curs = self.conn.cursor()
if sys.version_info[0] < 3:
data = ''.join(map(chr, range(32, 127) + range(160, 256)))
else:
data = bytes(range(32, 127) + range(160, 256)).decode('latin1')
# as string
curs.execute("SELECT %s::text;", (data,))
res = curs.fetchone()[0]
self.assertEqual(res, data)
self.assert_(not self.conn.notices)
# as unicode
if sys.version_info[0] < 3:
psycopg2.extensions.register_type(psycopg2.extensions.UNICODE, self.conn)
data = data.decode('latin1')
curs.execute("SELECT %s::text;", (data,))
res = curs.fetchone()[0]
self.assertEqual(res, data)
self.assert_(not self.conn.notices)
def test_koi8(self):
self.conn.set_client_encoding('KOI8')
curs = self.conn.cursor()
if sys.version_info[0] < 3:
data = ''.join(map(chr, range(32, 127) + range(128, 256)))
else:
data = bytes(range(32, 127) + range(128, 256)).decode('koi8_r')
# as string
curs.execute("SELECT %s::text;", (data,))
res = curs.fetchone()[0]
self.assertEqual(res, data)
self.assert_(not self.conn.notices)
# as unicode
if sys.version_info[0] < 3:
psycopg2.extensions.register_type(psycopg2.extensions.UNICODE, self.conn)
data = data.decode('koi8_r')
curs.execute("SELECT %s::text;", (data,))
res = curs.fetchone()[0]
self.assertEqual(res, data)
self.assert_(not self.conn.notices)
def test_suite():
return unittest.TestLoader().loadTestsFromName(__name__)

View File

@ -28,13 +28,12 @@ from testutils import unittest, skip_if_no_pg_sleep
import psycopg2
from psycopg2.extensions import (
ISOLATION_LEVEL_SERIALIZABLE, STATUS_BEGIN, STATUS_READY)
import tests
from testconfig import dsn
class TransactionTests(unittest.TestCase):
def setUp(self):
self.conn = psycopg2.connect(tests.dsn)
self.conn = psycopg2.connect(dsn)
self.conn.set_isolation_level(ISOLATION_LEVEL_SERIALIZABLE)
curs = self.conn.cursor()
curs.execute('''
@ -98,7 +97,7 @@ class DeadlockSerializationTests(unittest.TestCase):
"""Test deadlock and serialization failure errors."""
def connect(self):
conn = psycopg2.connect(tests.dsn)
conn = psycopg2.connect(dsn)
conn.set_isolation_level(ISOLATION_LEVEL_SERIALIZABLE)
return conn
@ -231,7 +230,7 @@ class QueryCancellationTests(unittest.TestCase):
"""Tests for query cancellation."""
def setUp(self):
self.conn = psycopg2.connect(tests.dsn)
self.conn = psycopg2.connect(dsn)
self.conn.set_isolation_level(ISOLATION_LEVEL_SERIALIZABLE)
def tearDown(self):

36
tests/testconfig.py Normal file
View File

@ -0,0 +1,36 @@
# Configure the test suite from the env variables.
import os
dbname = os.environ.get('PSYCOPG2_TESTDB', 'psycopg2_test')
dbhost = os.environ.get('PSYCOPG2_TESTDB_HOST', None)
dbport = os.environ.get('PSYCOPG2_TESTDB_PORT', None)
dbuser = os.environ.get('PSYCOPG2_TESTDB_USER', None)
dbpass = os.environ.get('PSYCOPG2_TESTDB_PASSWORD', None)
# Check if we want to test psycopg's green path.
green = os.environ.get('PSYCOPG2_TEST_GREEN', None)
if green:
if green == '1':
from psycopg2.extras import wait_select as wait_callback
elif green == 'eventlet':
from eventlet.support.psycopg2_patcher import eventlet_wait_callback \
as wait_callback
else:
raise ValueError("please set 'PSYCOPG2_TEST_GREEN' to a valid value")
import psycopg2.extensions
psycopg2.extensions.set_wait_callback(wait_callback)
# Construct a DSN to connect to the test database:
dsn = 'dbname=%s' % dbname
if dbhost is not None:
dsn += ' host=%s' % dbhost
if dbport is not None:
dsn += ' port=%s' % dbport
if dbuser is not None:
dsn += ' user=%s' % dbuser
if dbpass is not None:
dsn += ' password=%s' % dbpass

View File

@ -23,6 +23,9 @@
# Use unittest2 if available. Otherwise mock a skip facility with warnings.
import os
import sys
try:
import unittest2
unittest = unittest2
@ -57,6 +60,16 @@ else:
unittest.TestCase.skipTest = skipTest
# Silence warnings caused by the stubborness of the Python unittest maintainers
# http://bugs.python.org/issue9424
if not hasattr(unittest.TestCase, 'assert_') \
or unittest.TestCase.assert_ is not unittest.TestCase.assertTrue:
# mavaff...
unittest.TestCase.assert_ = unittest.TestCase.assertTrue
unittest.TestCase.failUnless = unittest.TestCase.assertTrue
unittest.TestCase.assertEquals = unittest.TestCase.assertEqual
unittest.TestCase.failUnlessEqual = unittest.TestCase.assertEqual
def decorate_all_tests(cls, decorator):
"""Apply *decorator* to all the tests defined in the TestCase *cls*."""
@ -138,3 +151,60 @@ def skip_if_tpc_disabled(f):
return skip_if_tpc_disabled_
def skip_if_no_iobase(f):
"""Skip a test if io.TextIOBase is not available."""
def skip_if_no_iobase_(self):
try:
from io import TextIOBase
except ImportError:
return self.skipTest("io.TextIOBase not found.")
else:
return f(self)
return skip_if_no_iobase_
def skip_on_python2(f):
"""Skip a test on Python 3 and following."""
def skip_on_python2_(self):
if sys.version_info[0] < 3:
return self.skipTest("skipped because Python 2")
else:
return f(self)
return skip_on_python2_
def skip_on_python3(f):
"""Skip a test on Python 3 and following."""
def skip_on_python3_(self):
if sys.version_info[0] >= 3:
return self.skipTest("skipped because Python 3")
else:
return f(self)
return skip_on_python3_
def script_to_py3(script):
"""Convert a script to Python3 syntax if required."""
if sys.version_info[0] < 3:
return script
import tempfile
f = tempfile.NamedTemporaryFile(suffix=".py")
f.write(script.encode())
f.flush()
# 2to3 is way too chatty
import logging
logging.basicConfig(filename=os.devnull)
from lib2to3.main import main
if main("lib2to3.fixes", ['--no-diffs', '-w', '-n', f.name]):
raise Exception('py3 conversion failed')
f2 = open(f.name)
try:
return f2.read()
finally:
f2.close()

View File

@ -27,17 +27,19 @@ try:
except:
pass
import sys
import testutils
from testutils import unittest
from testconfig import dsn
import psycopg2
import tests
from psycopg2.extensions import b
class TypesBasicTests(unittest.TestCase):
"""Test that all type conversions are working."""
def setUp(self):
self.conn = psycopg2.connect(tests.dsn)
self.conn = psycopg2.connect(dsn)
def tearDown(self):
self.conn.close()
@ -62,13 +64,19 @@ class TypesBasicTests(unittest.TestCase):
self.failUnless(s == 1971, "wrong integer quoting: " + str(s))
s = self.execute("SELECT %s AS foo", (1971L,))
self.failUnless(s == 1971L, "wrong integer quoting: " + str(s))
if sys.version_info[0] < 2 or sys.version_info[1] < 4:
if sys.version_info[0:2] < (2, 4):
s = self.execute("SELECT %s AS foo", (19.10,))
self.failUnless(abs(s - 19.10) < 0.001,
"wrong float quoting: " + str(s))
def testBoolean(self):
x = self.execute("SELECT %s as foo", (False,))
self.assert_(x is False)
x = self.execute("SELECT %s as foo", (True,))
self.assert_(x is True)
def testDecimal(self):
if sys.version_info[0] >= 2 and sys.version_info[1] >= 4:
if sys.version_info[0:2] >= (2, 4):
s = self.execute("SELECT %s AS foo", (decimal.Decimal("19.10"),))
self.failUnless(s - decimal.Decimal("19.10") == 0,
"wrong decimal quoting: " + str(s))
@ -101,38 +109,55 @@ class TypesBasicTests(unittest.TestCase):
return self.skipTest("inf::float not available on the server")
except ValueError:
return self.skipTest("inf not available on this platform")
s = self.execute("SELECT %s AS foo", (float("inf"),))
self.failUnless(str(s) == "inf", "wrong float quoting: " + str(s))
self.failUnless(type(s) == float, "wrong float conversion: " + repr(s))
def testBinary(self):
s = ''.join([chr(x) for x in range(256)])
b = psycopg2.Binary(s)
buf = self.execute("SELECT %s::bytea AS foo", (b,))
self.failUnless(str(buf) == s, "wrong binary quoting")
if sys.version_info[0] < 3:
s = ''.join([chr(x) for x in range(256)])
b = psycopg2.Binary(s)
buf = self.execute("SELECT %s::bytea AS foo", (b,))
self.assertEqual(s, str(buf))
else:
s = bytes(range(256))
b = psycopg2.Binary(s)
buf = self.execute("SELECT %s::bytea AS foo", (b,))
self.assertEqual(s, buf)
def testBinaryEmptyString(self):
# test to make sure an empty Binary is converted to an empty string
b = psycopg2.Binary('')
self.assertEqual(str(b), "''::bytea")
if sys.version_info[0] < 3:
b = psycopg2.Binary('')
self.assertEqual(str(b), "''::bytea")
else:
b = psycopg2.Binary(bytes([]))
self.assertEqual(str(b), "''::bytea")
def testBinaryRoundTrip(self):
# test to make sure buffers returned by psycopg2 are
# understood by execute:
s = ''.join([chr(x) for x in range(256)])
buf = self.execute("SELECT %s::bytea AS foo", (psycopg2.Binary(s),))
buf2 = self.execute("SELECT %s::bytea AS foo", (buf,))
self.failUnless(str(buf2) == s, "wrong binary quoting")
if sys.version_info[0] < 3:
s = ''.join([chr(x) for x in range(256)])
buf = self.execute("SELECT %s::bytea AS foo", (psycopg2.Binary(s),))
buf2 = self.execute("SELECT %s::bytea AS foo", (buf,))
self.assertEqual(s, str(buf2))
else:
s = bytes(range(256))
buf = self.execute("SELECT %s::bytea AS foo", (psycopg2.Binary(s),))
buf2 = self.execute("SELECT %s::bytea AS foo", (buf,))
self.assertEqual(s, buf2)
def testArray(self):
s = self.execute("SELECT %s AS foo", ([],))
self.failUnlessEqual(s, [])
s = self.execute("SELECT %s AS foo", ([[1,2],[3,4]],))
self.failUnless(s == [[1,2],[3,4]], "wrong array quoting " + str(s))
self.failUnlessEqual(s, [[1,2],[3,4]])
s = self.execute("SELECT %s AS foo", (['one', 'two', 'three'],))
self.failUnless(s == ['one', 'two', 'three'],
"wrong array quoting " + str(s))
self.failUnlessEqual(s, ['one', 'two', 'three'])
def testTypeRoundtripBinary(self):
@testutils.skip_on_python3
def testTypeRoundtripBuffer(self):
o1 = buffer("".join(map(chr, range(256))))
o2 = self.execute("select %s;", (o1,))
self.assertEqual(type(o1), type(o2))
@ -142,12 +167,53 @@ class TypesBasicTests(unittest.TestCase):
o2 = self.execute("select %s;", (o1,))
self.assertEqual(type(o1), type(o2))
def testTypeRoundtripBinaryArray(self):
@testutils.skip_on_python3
def testTypeRoundtripBufferArray(self):
o1 = buffer("".join(map(chr, range(256))))
o1 = [o1]
o2 = self.execute("select %s;", (o1,))
self.assertEqual(type(o1[0]), type(o2[0]))
@testutils.skip_on_python2
def testTypeRoundtripBytes(self):
o1 = bytes(range(256))
o2 = self.execute("select %s;", (o1,))
self.assertEqual(memoryview, type(o2))
# Test with an empty buffer
o1 = bytes([])
o2 = self.execute("select %s;", (o1,))
self.assertEqual(memoryview, type(o2))
@testutils.skip_on_python2
def testTypeRoundtripBytesArray(self):
o1 = bytes(range(256))
o1 = [o1]
o2 = self.execute("select %s;", (o1,))
self.assertEqual(memoryview, type(o2[0]))
@testutils.skip_on_python2
def testAdaptBytearray(self):
o1 = bytearray(range(256))
o2 = self.execute("select %s;", (o1,))
self.assertEqual(memoryview, type(o2))
# Test with an empty buffer
o1 = bytearray([])
o2 = self.execute("select %s;", (o1,))
self.assertEqual(memoryview, type(o2))
@testutils.skip_on_python2
def testAdaptMemoryview(self):
o1 = memoryview(bytes(range(256)))
o2 = self.execute("select %s;", (o1,))
self.assertEqual(memoryview, type(o2))
# Test with an empty buffer
o1 = memoryview(bytes([]))
o2 = self.execute("select %s;", (o1,))
self.assertEqual(memoryview, type(o2))
class AdaptSubclassTest(unittest.TestCase):
def test_adapt_subtype(self):
@ -167,11 +233,12 @@ class AdaptSubclassTest(unittest.TestCase):
register_adapter(A, lambda a: AsIs("a"))
register_adapter(B, lambda b: AsIs("b"))
try:
self.assertEqual('b', adapt(C()).getquoted())
self.assertEqual(b('b'), adapt(C()).getquoted())
finally:
del psycopg2.extensions.adapters[A, psycopg2.extensions.ISQLQuote]
del psycopg2.extensions.adapters[B, psycopg2.extensions.ISQLQuote]
@testutils.skip_on_python3
def test_no_mro_no_joy(self):
from psycopg2.extensions import adapt, register_adapter, AsIs
@ -185,6 +252,20 @@ class AdaptSubclassTest(unittest.TestCase):
del psycopg2.extensions.adapters[A, psycopg2.extensions.ISQLQuote]
@testutils.skip_on_python2
def test_adapt_subtype_3(self):
from psycopg2.extensions import adapt, register_adapter, AsIs
class A: pass
class B(A): pass
register_adapter(A, lambda a: AsIs("a"))
try:
self.assertEqual(b("a"), adapt(B()).getquoted())
finally:
del psycopg2.extensions.adapters[A, psycopg2.extensions.ISQLQuote]
def test_suite():
return unittest.TestLoader().loadTestsFromName(__name__)

View File

@ -26,20 +26,22 @@ from testutils import unittest, skip_if_no_uuid
import psycopg2
import psycopg2.extras
import tests
from psycopg2.extensions import b
from testconfig import dsn
def filter_scs(conn, s):
if conn.get_parameter_status("standard_conforming_strings") == 'off':
return s
else:
return s.replace("E'", "'")
return s.replace(b("E'"), b("'"))
class TypesExtrasTests(unittest.TestCase):
"""Test that all type conversions are working."""
def setUp(self):
self.conn = psycopg2.connect(tests.dsn)
self.conn = psycopg2.connect(dsn)
def tearDown(self):
self.conn.close()
@ -93,7 +95,7 @@ class TypesExtrasTests(unittest.TestCase):
a = psycopg2.extensions.adapt(i)
a.prepare(self.conn)
self.assertEqual(
filter_scs(self.conn, "E'192.168.1.0/24'::inet"),
filter_scs(self.conn, b("E'192.168.1.0/24'::inet")),
a.getquoted())
# adapts ok with unicode too
@ -101,7 +103,7 @@ class TypesExtrasTests(unittest.TestCase):
a = psycopg2.extensions.adapt(i)
a.prepare(self.conn)
self.assertEqual(
filter_scs(self.conn, "E'192.168.1.0/24'::inet"),
filter_scs(self.conn, b("E'192.168.1.0/24'::inet")),
a.getquoted())
def test_adapt_fail(self):
@ -126,7 +128,7 @@ def skip_if_no_hstore(f):
class HstoreTestCase(unittest.TestCase):
def setUp(self):
self.conn = psycopg2.connect(tests.dsn)
self.conn = psycopg2.connect(dsn)
def tearDown(self):
self.conn.close()
@ -145,18 +147,17 @@ class HstoreTestCase(unittest.TestCase):
a.prepare(self.conn)
q = a.getquoted()
self.assert_(q.startswith("(("), q)
self.assert_(q.endswith("))"), q)
ii = q[1:-1].split("||")
self.assert_(q.startswith(b("((")), q)
ii = q[1:-1].split(b("||"))
ii.sort()
self.assertEqual(len(ii), len(o))
self.assertEqual(ii[0], filter_scs(self.conn, "(E'a' => E'1')"))
self.assertEqual(ii[1], filter_scs(self.conn, "(E'b' => E'''')"))
self.assertEqual(ii[2], filter_scs(self.conn, "(E'c' => NULL)"))
self.assertEqual(ii[0], filter_scs(self.conn, b("(E'a' => E'1')")))
self.assertEqual(ii[1], filter_scs(self.conn, b("(E'b' => E'''')")))
self.assertEqual(ii[2], filter_scs(self.conn, b("(E'c' => NULL)")))
if 'd' in o:
encc = u'\xe0'.encode(psycopg2.extensions.encodings[self.conn.encoding])
self.assertEqual(ii[3], filter_scs(self.conn, "(E'd' => E'%s')" % encc))
self.assertEqual(ii[3], filter_scs(self.conn, b("(E'd' => E'") + encc + b("')")))
def test_adapt_9(self):
if self.conn.server_version < 90000:
@ -172,11 +173,11 @@ class HstoreTestCase(unittest.TestCase):
a.prepare(self.conn)
q = a.getquoted()
m = re.match(r'hstore\(ARRAY\[([^\]]+)\], ARRAY\[([^\]]+)\]\)', q)
m = re.match(b(r'hstore\(ARRAY\[([^\]]+)\], ARRAY\[([^\]]+)\]\)'), q)
self.assert_(m, repr(q))
kk = m.group(1).split(", ")
vv = m.group(2).split(", ")
kk = m.group(1).split(b(", "))
vv = m.group(2).split(b(", "))
ii = zip(kk, vv)
ii.sort()
@ -184,12 +185,12 @@ class HstoreTestCase(unittest.TestCase):
return tuple([filter_scs(self.conn, s) for s in args])
self.assertEqual(len(ii), len(o))
self.assertEqual(ii[0], f("E'a'", "E'1'"))
self.assertEqual(ii[1], f("E'b'", "E''''"))
self.assertEqual(ii[2], f("E'c'", "NULL"))
self.assertEqual(ii[0], f(b("E'a'"), b("E'1'")))
self.assertEqual(ii[1], f(b("E'b'"), b("E''''")))
self.assertEqual(ii[2], f(b("E'c'"), b("NULL")))
if 'd' in o:
encc = u'\xe0'.encode(psycopg2.extensions.encodings[self.conn.encoding])
self.assertEqual(ii[3], f("E'd'", "E'%s'" % encc))
self.assertEqual(ii[3], f(b("E'd'"), b("E'") + encc + b("'")))
def test_parse(self):
from psycopg2.extras import HstoreAdapter
@ -266,7 +267,7 @@ class HstoreTestCase(unittest.TestCase):
oids = HstoreAdapter.get_oids(self.conn)
try:
register_hstore(self.conn, globally=True)
conn2 = psycopg2.connect(tests.dsn)
conn2 = psycopg2.connect(dsn)
try:
cur2 = self.conn.cursor()
cur2.execute("select 'a => b'::hstore")
@ -305,7 +306,11 @@ class HstoreTestCase(unittest.TestCase):
ok({''.join(ab): ''.join(ab)})
self.conn.set_client_encoding('latin1')
ab = map(chr, range(1, 256))
if sys.version_info[0] < 3:
ab = map(chr, range(32, 127) + range(160, 255))
else:
ab = bytes(range(32, 127) + range(160, 255)).decode('latin1')
ok({''.join(ab): ''.join(ab)})
ok(dict(zip(ab, ab)))
@ -347,7 +352,7 @@ def skip_if_no_composite(f):
class AdaptTypeTestCase(unittest.TestCase):
def setUp(self):
self.conn = psycopg2.connect(tests.dsn)
self.conn = psycopg2.connect(dsn)
def tearDown(self):
self.conn.close()
@ -356,7 +361,7 @@ class AdaptTypeTestCase(unittest.TestCase):
def test_none_in_record(self):
curs = self.conn.cursor()
s = curs.mogrify("SELECT %s;", [(42, None)])
self.assertEqual("SELECT (42, NULL);", s)
self.assertEqual(b("SELECT (42, NULL);"), s)
curs.execute("SELECT %s;", [(42, None)])
d = curs.fetchone()[0]
self.assertEqual("(42,)", d)
@ -377,7 +382,7 @@ class AdaptTypeTestCase(unittest.TestCase):
self.assertEqual(ext.adapt(None).getquoted(), "NOPE!")
s = curs.mogrify("SELECT %s;", (None,))
self.assertEqual("SELECT NULL;", s)
self.assertEqual(b("SELECT NULL;"), s)
finally:
ext.register_adapter(type(None), orig_adapter)
@ -484,8 +489,8 @@ class AdaptTypeTestCase(unittest.TestCase):
def test_register_on_connection(self):
self._create_type("type_ii", [("a", "integer"), ("b", "integer")])
conn1 = psycopg2.connect(tests.dsn)
conn2 = psycopg2.connect(tests.dsn)
conn1 = psycopg2.connect(dsn)
conn2 = psycopg2.connect(dsn)
try:
psycopg2.extras.register_composite("type_ii", conn1)
curs1 = conn1.cursor()
@ -502,8 +507,8 @@ class AdaptTypeTestCase(unittest.TestCase):
def test_register_globally(self):
self._create_type("type_ii", [("a", "integer"), ("b", "integer")])
conn1 = psycopg2.connect(tests.dsn)
conn2 = psycopg2.connect(tests.dsn)
conn1 = psycopg2.connect(dsn)
conn2 = psycopg2.connect(dsn)
try:
t = psycopg2.extras.register_composite("type_ii", conn1, globally=True)
try: