fix: handle types in the search path in register_composite()

Fix #1487.
This commit is contained in:
Daniele Varrazzo 2022-10-06 01:02:27 +01:00
parent 31a80410db
commit d88e4c2a3c
3 changed files with 82 additions and 2 deletions

6
NEWS
View File

@ -1,3 +1,9 @@
What's new in psycopg 2.9.4 (unreleased)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- Fix `register_composite()` with customized search_path (:ticket:`#1487`).
Current release
---------------

View File

@ -1098,9 +1098,40 @@ ORDER BY attnum;
recs = curs.fetchall()
if not recs:
# The above algorithm doesn't work for customized seach_path
# (#1487) The implementation below works better, but, to guarantee
# backwards compatibility, use it only if the original one failed.
try:
savepoint = False
# Because we executed statements earlier, we are either INTRANS
# or we are IDLE only if the transaction is autocommit, in
# which case we don't need the savepoint anyway.
if conn.status == _ext.STATUS_IN_TRANSACTION:
curs.execute("SAVEPOINT register_type")
savepoint = True
curs.execute("""\
SELECT t.oid, %s, attname, atttypid, nspname
FROM pg_type t
JOIN pg_namespace ns ON typnamespace = ns.oid
JOIN pg_attribute a ON attrelid = typrelid
WHERE t.oid = %%s::regtype
AND attnum > 0 AND NOT attisdropped
ORDER BY attnum;
""" % typarray, (name, ))
except psycopg2.ProgrammingError:
pass
else:
recs = curs.fetchall()
if recs:
schema = recs[0][4]
finally:
if savepoint:
curs.execute("ROLLBACK TO SAVEPOINT register_type")
# revert the status of the connection as before the command
if (conn_status != _ext.STATUS_IN_TRANSACTION
and not conn.autocommit):
if conn_status != _ext.STATUS_IN_TRANSACTION and not conn.autocommit:
conn.rollback()
if not recs:

View File

@ -584,6 +584,49 @@ class AdaptTypeTestCase(ConnectingTestCase):
curs.execute("select (4,8)::typens.typens_ii")
self.assertEqual(curs.fetchone()[0], (4, 8))
@skip_if_no_composite
def test_composite_namespace_path(self):
curs = self.conn.cursor()
curs.execute("""
select nspname from pg_namespace
where nspname = 'typens';
""")
if not curs.fetchone():
curs.execute("create schema typens;")
self.conn.commit()
self._create_type("typens.typensp_ii",
[("a", "integer"), ("b", "integer")])
curs.execute("set search_path=typens,public")
t = psycopg2.extras.register_composite(
"typensp_ii", self.conn)
self.assertEqual(t.schema, 'typens')
curs.execute("select (4,8)::typensp_ii")
self.assertEqual(curs.fetchone()[0], (4, 8))
@skip_if_no_composite
def test_composite_not_found(self):
self.assertRaises(
psycopg2.ProgrammingError, psycopg2.extras.register_composite,
"nosuchtype", self.conn)
self.assertEqual(self.conn.status, ext.STATUS_READY)
cur = self.conn.cursor()
cur.execute("select 1")
self.assertRaises(
psycopg2.ProgrammingError, psycopg2.extras.register_composite,
"nosuchtype", self.conn)
self.assertEqual(self.conn.status, ext.STATUS_IN_TRANSACTION)
self.conn.rollback()
self.conn.autocommit = True
self.assertRaises(
psycopg2.ProgrammingError, psycopg2.extras.register_composite,
"nosuchtype", self.conn)
self.assertEqual(self.conn.status, ext.STATUS_READY)
@skip_if_no_composite
@skip_before_postgres(8, 4)
def test_composite_array(self):