Merge branch 'types-arrays' into devel

This commit is contained in:
Daniele Varrazzo 2012-02-24 00:35:21 +00:00
commit b2c61eaa18
7 changed files with 78 additions and 12 deletions

1
NEWS
View File

@ -11,6 +11,7 @@ What's new in psycopg 2.4.5
to Menno Smits (tickets #94, #95). to Menno Smits (tickets #94, #95).
- Fixed 'rownumber' during iteration on cursor subclasses. - Fixed 'rownumber' during iteration on cursor subclasses.
Regression introduced in 2.4.4 (ticket #100). Regression introduced in 2.4.4 (ticket #100).
- Added support for 'inet' arrays.
What's new in psycopg 2.4.4 What's new in psycopg 2.4.4

View File

@ -325,6 +325,20 @@ details.
.. versionadded:: 2.4.3 .. versionadded:: 2.4.3
.. _cast-array-unknown:
.. note::
The function can be used to create a generic array typecaster,
returning a list of strings: just use the `~psycopg2.STRING` as base
typecaster. For instance, if you want to receive from the database an
array of :sql:`macaddr`, each address represented by string, you can
use::
psycopg2.extensions.register_type(
psycopg2.extensions.new_array_type(
(1040,), 'MACADDR[]', psycopg2.STRING))
.. function:: register_type(obj [, scope]) .. function:: register_type(obj [, scope])

View File

@ -250,6 +250,7 @@ UUID data type
^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^
.. versionadded:: 2.0.9 .. versionadded:: 2.0.9
.. versionchanged:: 2.4.5 added inet array support.
.. doctest:: .. doctest::
@ -264,7 +265,7 @@ UUID data type
'192.168.0.1/24' '192.168.0.1/24'
.. autofunction:: register_inet() .. autofunction:: register_inet
.. autoclass:: Inet .. autoclass:: Inet

View File

@ -109,6 +109,13 @@ Transferring binary data from PostgreSQL 9.0 doesn't work.
.. __: http://www.postgresql.org/docs/9.0/static/datatype-binary.html .. __: http://www.postgresql.org/docs/9.0/static/datatype-binary.html
.. __: http://www.postgresql.org/docs/9.0/static/runtime-config-client.html#GUC-BYTEA-OUTPUT .. __: http://www.postgresql.org/docs/9.0/static/runtime-config-client.html#GUC-BYTEA-OUTPUT
Arrays of *TYPE* are not casted to list.
Arrays are only casted to list when their oid is known, and an array
typecaster is registered for them. If there is no typecaster, the array is
returned unparsed from PostgreSQL (e.g. ``{a,b,c}``). It is easy to create
a generic arrays typecaster, returning a list of array: an example is
provided in the `~psycopg2.extensions.new_array_type()` documentation.
Best practices Best practices
-------------- --------------

View File

@ -294,7 +294,7 @@ the SQL string that would be sent to the database.
`bytea_output`__ configuration parameter to ``escape``, either in the `bytea_output`__ configuration parameter to ``escape``, either in the
server configuration file or in the client session (using a query such as server configuration file or in the client session (using a query such as
``SET bytea_output TO escape;``) before receiving binary data. ``SET bytea_output TO escape;``) before receiving binary data.
.. __: http://www.postgresql.org/docs/9.0/static/datatype-binary.html .. __: http://www.postgresql.org/docs/9.0/static/datatype-binary.html
.. __: http://www.postgresql.org/docs/9.0/static/runtime-config-client.html#GUC-BYTEA-OUTPUT .. __: http://www.postgresql.org/docs/9.0/static/runtime-config-client.html#GUC-BYTEA-OUTPUT
@ -334,6 +334,14 @@ the SQL string that would be sent to the database.
>>> cur.mogrify("SELECT %s;", ([10, 20, 30], )) >>> cur.mogrify("SELECT %s;", ([10, 20, 30], ))
'SELECT ARRAY[10, 20, 30];' 'SELECT ARRAY[10, 20, 30];'
.. note::
Reading back from PostgreSQL, arrays are converted to list of Python
objects as expected, but only if the types are known one. Arrays of
unknown types are returned as represented by the database (e.g.
``{a,b,c}``). You can easily create a typecaster for :ref:`array of
unknown types <cast-array-unknown>`.
.. _adapt-tuple: .. _adapt-tuple:
.. index:: .. index::

View File

@ -455,14 +455,21 @@ class UUID_adapter(object):
__str__ = getquoted __str__ = getquoted
def register_uuid(oids=None, conn_or_curs=None): def register_uuid(oids=None, conn_or_curs=None):
"""Create the UUID type and an uuid.UUID adapter.""" """Create the UUID type and an uuid.UUID adapter.
:param oids: oid for the PostgreSQL :sql:`uuid` type, or 2-items sequence
with oids of the type and the array. If not specified, use PostgreSQL
standard oids.
:param conn_or_curs: where to register the typecaster. If not specified,
register it globally.
"""
import uuid import uuid
if not oids: if not oids:
oid1 = 2950 oid1 = 2950
oid2 = 2951 oid2 = 2951
elif type(oids) == list: elif isinstance(oids, (list, tuple)):
oid1, oid2 = oids oid1, oid2 = oids
else: else:
oid1 = oids oid1 = oids
@ -491,13 +498,13 @@ class Inet(object):
""" """
def __init__(self, addr): def __init__(self, addr):
self.addr = addr self.addr = addr
def __repr__(self): def __repr__(self):
return "%s(%r)" % (self.__class__.__name__, self.addr) return "%s(%r)" % (self.__class__.__name__, self.addr)
def prepare(self, conn): def prepare(self, conn):
self._conn = conn self._conn = conn
def getquoted(self): def getquoted(self):
obj = _A(self.addr) obj = _A(self.addr)
if hasattr(obj, 'prepare'): if hasattr(obj, 'prepare'):
@ -510,13 +517,32 @@ class Inet(object):
def __str__(self): def __str__(self):
return str(self.addr) return str(self.addr)
def register_inet(oid=None, conn_or_curs=None): def register_inet(oid=None, conn_or_curs=None):
"""Create the INET type and an Inet adapter.""" """Create the INET type and an Inet adapter.
if not oid: oid = 869
_ext.INET = _ext.new_type((oid, ), "INET", :param oid: oid for the PostgreSQL :sql:`inet` type, or 2-items sequence
with oids of the type and the array. If not specified, use PostgreSQL
standard oids.
:param conn_or_curs: where to register the typecaster. If not specified,
register it globally.
"""
if not oid:
oid1 = 869
oid2 = 1041
elif isinstance(oid, (list, tuple)):
oid1, oid2 = oid
else:
oid1 = oid
oid2 = 1041
_ext.INET = _ext.new_type((oid1, ), "INET",
lambda data, cursor: data and Inet(data) or None) lambda data, cursor: data and Inet(data) or None)
_ext.INETARRAY = _ext.new_array_type((oid2, ), "INETARRAY", _ext.INET)
_ext.register_type(_ext.INET, conn_or_curs) _ext.register_type(_ext.INET, conn_or_curs)
_ext.register_type(_ext.INETARRAY, conn_or_curs)
return _ext.INET return _ext.INET

View File

@ -82,13 +82,22 @@ class TypesExtrasTests(unittest.TestCase):
def testINET(self): def testINET(self):
psycopg2.extras.register_inet() psycopg2.extras.register_inet()
i = "192.168.1.0/24"; i = psycopg2.extras.Inet("192.168.1.0/24")
s = self.execute("SELECT %s AS foo", (i,)) s = self.execute("SELECT %s AS foo", (i,))
self.failUnless(i == s) self.failUnless(i.addr == s.addr)
# must survive NULL cast to inet # must survive NULL cast to inet
s = self.execute("SELECT NULL::inet AS foo") s = self.execute("SELECT NULL::inet AS foo")
self.failUnless(s is None) self.failUnless(s is None)
def testINETARRAY(self):
psycopg2.extras.register_inet()
i = psycopg2.extras.Inet("192.168.1.0/24")
s = self.execute("SELECT %s AS foo", ([i],))
self.failUnless(i.addr == s[0].addr)
# must survive NULL cast to inet
s = self.execute("SELECT NULL::inet[] AS foo")
self.failUnless(s is None)
def test_inet_conform(self): def test_inet_conform(self):
from psycopg2.extras import Inet from psycopg2.extras import Inet
i = Inet("192.168.1.0/24") i = Inet("192.168.1.0/24")