Skip to content

Commit 54ec77c

Browse files
QuantumTMrohitsanj
authored andcommitted
Domain basetypes are introspected (MagicStack#886) (MagicStack#887)
1 parent d6f0fed commit 54ec77c

File tree

2 files changed

+259
-15
lines changed

2 files changed

+259
-15
lines changed

asyncpg/introspection.py

Lines changed: 236 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,242 @@
55
# the Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0
66

77

8-
INTRO_LOOKUP_TYPES_CRDB = """\
9-
SELECT
10-
t.oid AS oid,
11-
ns.nspname AS ns,
12-
t.typname AS name,
13-
t.typtype AS kind,
14-
NULL AS basetype,
15-
t.typelem AS elemtype,
16-
NULL AS elemdelim,
17-
NULL AS range_subtype,
18-
NULL AS attrtypoids,
19-
NULL AS attrnames,
20-
NULL AS basetype_name,
21-
'-' AS elemtype_name,
22-
NULL AS range_subtype_name
8+
_TYPEINFO_13 = '''\
9+
(
10+
SELECT
11+
t.oid AS oid,
12+
ns.nspname AS ns,
13+
t.typname AS name,
14+
t.typtype AS kind,
15+
(CASE WHEN t.typtype = 'd' THEN
16+
(WITH RECURSIVE typebases(oid, depth) AS (
17+
SELECT
18+
t2.typbasetype AS oid,
19+
0 AS depth
20+
FROM
21+
pg_type t2
22+
WHERE
23+
t2.oid = t.oid
24+
25+
UNION ALL
26+
27+
SELECT
28+
t2.typbasetype AS oid,
29+
tb.depth + 1 AS depth
30+
FROM
31+
pg_type t2,
32+
typebases tb
33+
WHERE
34+
tb.oid = t2.oid
35+
AND t2.typbasetype != 0
36+
) SELECT oid FROM typebases ORDER BY depth DESC LIMIT 1)
37+
38+
ELSE NULL
39+
END) AS basetype,
40+
t.typelem AS elemtype,
41+
elem_t.typdelim AS elemdelim,
42+
range_t.rngsubtype AS range_subtype,
43+
(CASE WHEN t.typtype = 'c' THEN
44+
(SELECT
45+
array_agg(ia.atttypid ORDER BY ia.attnum)
46+
FROM
47+
pg_attribute ia
48+
INNER JOIN pg_class c
49+
ON (ia.attrelid = c.oid)
50+
WHERE
51+
ia.attnum > 0 AND NOT ia.attisdropped
52+
AND c.reltype = t.oid)
53+
54+
ELSE NULL
55+
END) AS attrtypoids,
56+
(CASE WHEN t.typtype = 'c' THEN
57+
(SELECT
58+
array_agg(ia.attname::text ORDER BY ia.attnum)
59+
FROM
60+
pg_attribute ia
61+
INNER JOIN pg_class c
62+
ON (ia.attrelid = c.oid)
63+
WHERE
64+
ia.attnum > 0 AND NOT ia.attisdropped
65+
AND c.reltype = t.oid)
66+
67+
ELSE NULL
68+
END) AS attrnames
69+
FROM
70+
pg_catalog.pg_type AS t
71+
INNER JOIN pg_catalog.pg_namespace ns ON (
72+
ns.oid = t.typnamespace)
73+
LEFT JOIN pg_type elem_t ON (
74+
t.typlen = -1 AND
75+
t.typelem != 0 AND
76+
t.typelem = elem_t.oid
77+
)
78+
LEFT JOIN pg_range range_t ON (
79+
t.oid = range_t.rngtypid
80+
)
81+
)
82+
'''
83+
84+
85+
INTRO_LOOKUP_TYPES_13 = '''\
86+
WITH RECURSIVE typeinfo_tree(
87+
oid, ns, name, kind, basetype, elemtype, elemdelim,
88+
range_subtype, attrtypoids, attrnames, depth)
89+
AS (
90+
SELECT
91+
ti.oid, ti.ns, ti.name, ti.kind, ti.basetype,
92+
ti.elemtype, ti.elemdelim, ti.range_subtype,
93+
ti.attrtypoids, ti.attrnames, 0
94+
FROM
95+
{typeinfo} AS ti
96+
WHERE
97+
ti.oid = any($1::oid[])
98+
99+
UNION ALL
100+
101+
SELECT
102+
ti.oid, ti.ns, ti.name, ti.kind, ti.basetype,
103+
ti.elemtype, ti.elemdelim, ti.range_subtype,
104+
ti.attrtypoids, ti.attrnames, tt.depth + 1
105+
FROM
106+
{typeinfo} ti,
107+
typeinfo_tree tt
108+
WHERE
109+
(tt.elemtype IS NOT NULL AND ti.oid = tt.elemtype)
110+
OR (tt.attrtypoids IS NOT NULL AND ti.oid = any(tt.attrtypoids))
111+
OR (tt.range_subtype IS NOT NULL AND ti.oid = tt.range_subtype)
112+
OR (tt.basetype IS NOT NULL AND ti.oid = tt.basetype)
113+
)
114+
115+
SELECT DISTINCT
116+
*,
117+
basetype::regtype::text AS basetype_name,
118+
elemtype::regtype::text AS elemtype_name,
119+
range_subtype::regtype::text AS range_subtype_name
120+
FROM
121+
typeinfo_tree
122+
ORDER BY
123+
depth DESC
124+
'''.format(typeinfo=_TYPEINFO_13)
125+
126+
127+
_TYPEINFO = '''\
128+
(
129+
SELECT
130+
t.oid AS oid,
131+
ns.nspname AS ns,
132+
t.typname AS name,
133+
t.typtype AS kind,
134+
(CASE WHEN t.typtype = 'd' THEN
135+
(WITH RECURSIVE typebases(oid, depth) AS (
136+
SELECT
137+
t2.typbasetype AS oid,
138+
0 AS depth
139+
FROM
140+
pg_type t2
141+
WHERE
142+
t2.oid = t.oid
143+
144+
UNION ALL
145+
146+
SELECT
147+
t2.typbasetype AS oid,
148+
tb.depth + 1 AS depth
149+
FROM
150+
pg_type t2,
151+
typebases tb
152+
WHERE
153+
tb.oid = t2.oid
154+
AND t2.typbasetype != 0
155+
) SELECT oid FROM typebases ORDER BY depth DESC LIMIT 1)
156+
157+
ELSE NULL
158+
END) AS basetype,
159+
t.typelem AS elemtype,
160+
elem_t.typdelim AS elemdelim,
161+
COALESCE(
162+
range_t.rngsubtype,
163+
multirange_t.rngsubtype) AS range_subtype,
164+
(CASE WHEN t.typtype = 'c' THEN
165+
(SELECT
166+
array_agg(ia.atttypid ORDER BY ia.attnum)
167+
FROM
168+
pg_attribute ia
169+
INNER JOIN pg_class c
170+
ON (ia.attrelid = c.oid)
171+
WHERE
172+
ia.attnum > 0 AND NOT ia.attisdropped
173+
AND c.reltype = t.oid)
174+
175+
ELSE NULL
176+
END) AS attrtypoids,
177+
(CASE WHEN t.typtype = 'c' THEN
178+
(SELECT
179+
array_agg(ia.attname::text ORDER BY ia.attnum)
180+
FROM
181+
pg_attribute ia
182+
INNER JOIN pg_class c
183+
ON (ia.attrelid = c.oid)
184+
WHERE
185+
ia.attnum > 0 AND NOT ia.attisdropped
186+
AND c.reltype = t.oid)
187+
188+
ELSE NULL
189+
END) AS attrnames
190+
FROM
191+
pg_catalog.pg_type AS t
192+
INNER JOIN pg_catalog.pg_namespace ns ON (
193+
ns.oid = t.typnamespace)
194+
LEFT JOIN pg_type elem_t ON (
195+
t.typlen = -1 AND
196+
t.typelem != 0 AND
197+
t.typelem = elem_t.oid
198+
)
199+
LEFT JOIN pg_range range_t ON (
200+
t.oid = range_t.rngtypid
201+
)
202+
LEFT JOIN pg_range multirange_t ON (
203+
t.oid = multirange_t.rngmultitypid
204+
)
205+
)
206+
'''
207+
208+
209+
INTRO_LOOKUP_TYPES = '''\
210+
WITH RECURSIVE typeinfo_tree(
211+
oid, ns, name, kind, basetype, elemtype, elemdelim,
212+
range_subtype, attrtypoids, attrnames, depth)
213+
AS (
214+
SELECT
215+
ti.oid, ti.ns, ti.name, ti.kind, ti.basetype,
216+
ti.elemtype, ti.elemdelim, ti.range_subtype,
217+
ti.attrtypoids, ti.attrnames, 0
218+
FROM
219+
{typeinfo} AS ti
220+
WHERE
221+
ti.oid = any($1::oid[])
222+
223+
UNION ALL
224+
225+
SELECT
226+
ti.oid, ti.ns, ti.name, ti.kind, ti.basetype,
227+
ti.elemtype, ti.elemdelim, ti.range_subtype,
228+
ti.attrtypoids, ti.attrnames, tt.depth + 1
229+
FROM
230+
{typeinfo} ti,
231+
typeinfo_tree tt
232+
WHERE
233+
(tt.elemtype IS NOT NULL AND ti.oid = tt.elemtype)
234+
OR (tt.attrtypoids IS NOT NULL AND ti.oid = any(tt.attrtypoids))
235+
OR (tt.range_subtype IS NOT NULL AND ti.oid = tt.range_subtype)
236+
OR (tt.basetype IS NOT NULL AND ti.oid = tt.basetype)
237+
)
238+
239+
SELECT DISTINCT
240+
*,
241+
basetype::regtype::text AS basetype_name,
242+
elemtype::regtype::text AS elemtype_name,
243+
range_subtype::regtype::text AS range_subtype_name
23244
FROM
24245
pg_catalog.pg_type AS t
25246
JOIN pg_catalog.pg_namespace ns

tests/test_introspection.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,3 +190,26 @@ async def wait_and_drop():
190190
DROP DOMAIN intro_2_t;
191191
''')
192192
await slow_intro_conn.close()
193+
194+
@tb.with_connection_options(database='asyncpg_intro_test')
195+
async def test_introspection_loads_basetypes_of_domains(self):
196+
# Test that basetypes of domains are loaded to the
197+
# client encode/decode cache
198+
await self.con.execute('''
199+
DROP TABLE IF EXISTS test;
200+
DROP DOMAIN IF EXISTS num_array;
201+
CREATE DOMAIN num_array numeric[];
202+
CREATE TABLE test (
203+
num num_array
204+
);
205+
''')
206+
207+
try:
208+
# if domain basetypes are not loaded, this insert will fail
209+
await self.con.execute(
210+
'INSERT INTO test (num) VALUES ($1)', ([1, 2],))
211+
finally:
212+
await self.con.execute('''
213+
DROP TABLE IF EXISTS test;
214+
DROP DOMAIN IF EXISTS num_array;
215+
''')

0 commit comments

Comments
 (0)