From 5211e1474b93ad33a4a9b4202d287a303c0b67f0 Mon Sep 17 00:00:00 2001 From: Daniele Varrazzo Date: Fri, 25 Feb 2011 00:19:49 +0000 Subject: [PATCH] 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. --- NEWS | 2 ++ doc/src/extras.rst | 4 ++-- lib/extras.py | 23 ++++++++++++++++------- tests/types_extras.py | 2 +- 4 files changed, 21 insertions(+), 10 deletions(-) diff --git a/NEWS b/NEWS index bb2d69ec..b9e5397b 100644 --- a/NEWS +++ b/NEWS @@ -28,6 +28,8 @@ What's new in psycopg 2.4 - Adapt types 'bytearray' (from Python 2.6), 'memoryview' (from Python 2.7) and other objects implementing the "Revised Buffer Protocol" to 'bytea' 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 in 'hex' format and the client libpq can't parse them. - Empty lists correctly roundtrip Python -> PostgreSQL -> Python. diff --git a/doc/src/extras.rst b/doc/src/extras.rst index f36a54fb..5f85d616 100644 --- a/doc/src/extras.rst +++ b/doc/src/extras.rst @@ -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. 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 -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. .. autofunction:: register_hstore diff --git a/lib/extras.py b/lib/extras.py index e1e94030..1e8d528d 100644 --- a/lib/extras.py +++ b/lib/extras.py @@ -663,7 +663,7 @@ class HstoreAdapter(object): @classmethod 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. """ @@ -680,28 +680,32 @@ class HstoreAdapter(object): # column typarray not available before PG 8.3 typarray = conn.server_version >= 80300 and "typarray" or "NULL" + rv0, rv1 = [], [] + # get the oid for the hstore curs.execute("""\ SELECT t.oid, %s FROM pg_type t JOIN pg_namespace ns ON typnamespace = ns.oid -WHERE typname = 'hstore' and nspname = 'public'; +WHERE typname = 'hstore'; """ % 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 if (conn_status != _ext.STATUS_IN_TRANSACTION and conn.isolation_level != _ext.ISOLATION_LEVEL_AUTOCOMMIT): conn.rollback() - return oids + return tuple(rv0), tuple(rv1) def register_hstore(conn_or_curs, globally=False, unicode=False, oid=None): """Register adapter and typecaster for `!dict`\-\ |hstore| conversions. :param conn_or_curs: a connection or cursor: the typecaster will be 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 will be `!unicode` instead of `!str`. The option is not available on 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. .. 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: oid = HstoreAdapter.get_oids(conn_or_curs) @@ -735,13 +741,16 @@ def register_hstore(conn_or_curs, globally=False, unicode=False, oid=None): else: oid = oid[0] # for the moment we don't have a HSTOREARRAY + if isinstance(oid, int): + oid = (oid,) + # create and register the typecaster if sys.version_info[0] < 3 and unicode: cast = HstoreAdapter.parse_unicode else: 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_adapter(dict, HstoreAdapter) diff --git a/tests/types_extras.py b/tests/types_extras.py index 85c3cfab..5c558cfc 100755 --- a/tests/types_extras.py +++ b/tests/types_extras.py @@ -276,7 +276,7 @@ class HstoreTestCase(unittest.TestCase): finally: conn2.close() finally: - psycopg2.extensions.string_types.pop(oids[0]) + psycopg2.extensions.string_types.pop(oids[0][0]) # verify the caster is not around anymore cur = self.conn.cursor()