Don't limit the hstore search to the public schema only

Looks like there is a case for installing hstore somewhere else (see
ticket #45). And after all the typecaster can be registered on a list of
OIDs, so let's grab them all.
This commit is contained in:
Daniele Varrazzo 2011-02-25 00:19:49 +00:00
parent 29ac03ef04
commit 5211e1474b
4 changed files with 21 additions and 10 deletions

2
NEWS
View File

@ -28,6 +28,8 @@ What's new in psycopg 2.4
- Adapt types 'bytearray' (from Python 2.6), 'memoryview' (from Python 2.7) - Adapt types 'bytearray' (from Python 2.6), 'memoryview' (from Python 2.7)
and other objects implementing the "Revised Buffer Protocol" to 'bytea' and other objects implementing the "Revised Buffer Protocol" to 'bytea'
data type. data type.
- The 'hstore' adapter can work even when the data type is not installed
in the 'public' namespace.
- Raise a clean exception instead of returning bad data when receiving bytea - Raise a clean exception instead of returning bad data when receiving bytea
in 'hex' format and the client libpq can't parse them. in 'hex' format and the client libpq can't parse them.
- Empty lists correctly roundtrip Python -> PostgreSQL -> Python. - Empty lists correctly roundtrip Python -> PostgreSQL -> Python.

View File

@ -145,9 +145,9 @@ key/value pairs as well as regular BTree indexes for equality, uniqueness etc.
Psycopg can convert Python `!dict` objects to and from |hstore| structures. Psycopg can convert Python `!dict` objects to and from |hstore| structures.
Only dictionaries with string/unicode keys and values are supported. `!None` Only dictionaries with string/unicode keys and values are supported. `!None`
is also allowed as value. Psycopg uses a more efficient |hstore| is also allowed as value but not as a key. Psycopg uses a more efficient |hstore|
representation when dealing with PostgreSQL 9.0 but previous server versions representation when dealing with PostgreSQL 9.0 but previous server versions
are supportes as well. By default the adapter/typecaster are disabled: they are supported as well. By default the adapter/typecaster are disabled: they
can be enabled using the `register_hstore()` function. can be enabled using the `register_hstore()` function.
.. autofunction:: register_hstore .. autofunction:: register_hstore

View File

@ -663,7 +663,7 @@ class HstoreAdapter(object):
@classmethod @classmethod
def get_oids(self, conn_or_curs): def get_oids(self, conn_or_curs):
"""Return the oid of the hstore and hstore[] types. """Return the lists of OID of the hstore and hstore[] types.
Return None if hstore is not available. Return None if hstore is not available.
""" """
@ -680,28 +680,32 @@ class HstoreAdapter(object):
# column typarray not available before PG 8.3 # column typarray not available before PG 8.3
typarray = conn.server_version >= 80300 and "typarray" or "NULL" typarray = conn.server_version >= 80300 and "typarray" or "NULL"
rv0, rv1 = [], []
# get the oid for the hstore # get the oid for the hstore
curs.execute("""\ curs.execute("""\
SELECT t.oid, %s SELECT t.oid, %s
FROM pg_type t JOIN pg_namespace ns FROM pg_type t JOIN pg_namespace ns
ON typnamespace = ns.oid ON typnamespace = ns.oid
WHERE typname = 'hstore' and nspname = 'public'; WHERE typname = 'hstore';
""" % typarray) """ % typarray)
oids = curs.fetchone() for oids in curs:
rv0.append(oids[0])
rv1.append(oids[1])
# revert the status of the connection as before the command # revert the status of the connection as before the command
if (conn_status != _ext.STATUS_IN_TRANSACTION if (conn_status != _ext.STATUS_IN_TRANSACTION
and conn.isolation_level != _ext.ISOLATION_LEVEL_AUTOCOMMIT): and conn.isolation_level != _ext.ISOLATION_LEVEL_AUTOCOMMIT):
conn.rollback() conn.rollback()
return oids return tuple(rv0), tuple(rv1)
def register_hstore(conn_or_curs, globally=False, unicode=False, oid=None): def register_hstore(conn_or_curs, globally=False, unicode=False, oid=None):
"""Register adapter and typecaster for `!dict`\-\ |hstore| conversions. """Register adapter and typecaster for `!dict`\-\ |hstore| conversions.
:param conn_or_curs: a connection or cursor: the typecaster will be :param conn_or_curs: a connection or cursor: the typecaster will be
registered only on this object unless *globally* is set to `!True` registered only on this object unless *globally* is set to `!True`
:param globally: register the adapter globally not only on *conn_or_curs* :param globally: register the adapter globally, not only on *conn_or_curs*
:param unicode: if `!True`, keys and values returned from the database :param unicode: if `!True`, keys and values returned from the database
will be `!unicode` instead of `!str`. The option is not available on will be `!unicode` instead of `!str`. The option is not available on
Python 3 Python 3
@ -724,7 +728,9 @@ def register_hstore(conn_or_curs, globally=False, unicode=False, oid=None):
Raise `~psycopg2.ProgrammingError` if the type is not found. Raise `~psycopg2.ProgrammingError` if the type is not found.
.. versionchanged:: 2.4 .. versionchanged:: 2.4
added the *oid* parameter. added the *oid* parameter. If not specified, the typecaster is
installed also if |hstore| is not installed in the :sql:`public`
schema.
""" """
if oid is None: if oid is None:
oid = HstoreAdapter.get_oids(conn_or_curs) oid = HstoreAdapter.get_oids(conn_or_curs)
@ -735,13 +741,16 @@ def register_hstore(conn_or_curs, globally=False, unicode=False, oid=None):
else: else:
oid = oid[0] # for the moment we don't have a HSTOREARRAY oid = oid[0] # for the moment we don't have a HSTOREARRAY
if isinstance(oid, int):
oid = (oid,)
# create and register the typecaster # create and register the typecaster
if sys.version_info[0] < 3 and unicode: if sys.version_info[0] < 3 and unicode:
cast = HstoreAdapter.parse_unicode cast = HstoreAdapter.parse_unicode
else: else:
cast = HstoreAdapter.parse cast = HstoreAdapter.parse
HSTORE = _ext.new_type((oid,), "HSTORE", cast) HSTORE = _ext.new_type(oid, "HSTORE", cast)
_ext.register_type(HSTORE, not globally and conn_or_curs or None) _ext.register_type(HSTORE, not globally and conn_or_curs or None)
_ext.register_adapter(dict, HstoreAdapter) _ext.register_adapter(dict, HstoreAdapter)

View File

@ -276,7 +276,7 @@ class HstoreTestCase(unittest.TestCase):
finally: finally:
conn2.close() conn2.close()
finally: finally:
psycopg2.extensions.string_types.pop(oids[0]) psycopg2.extensions.string_types.pop(oids[0][0])
# verify the caster is not around anymore # verify the caster is not around anymore
cur = self.conn.cursor() cur = self.conn.cursor()