Find all composite type component types using WITH RECURSIVE
-
03-07-2021 - |
Domanda
Given the typname
of a composite type, how do I find the type oids
of all component types recursively?
Example:
CREATE TYPE t_station AS (x INT,
y INT,
label VARCHAR);
CREATE TYPE t_address AS (city VARCHAR,
street VARCHAR,
no INT,
stations t_station[]);
CREATE TYPE t_employee AS (name VARCHAR,
age INT,
coins INT[],
notes VARCHAR,
address t_address);
I can get the type oids
of the members of t_employee
:
SELECT
t.typname, t.oid, a.attname, a.atttypid
FROM
pg_attribute a INNER JOIN pg_type t ON a.attrelid = t.typrelid
AND t.typname = 't_employee'
But I need to recurse that, which I guess can be done using WITH RECURSIVE
:
WITH RECURSIVE allattrs(typname, oid, attname, atttypid) AS (
select t.typname, t.oid, a.attname, a.atttypid from pg_attribute a inner join pg_type t on a.attrelid = t.typrelid and t.typname = 't_employee'
union all
select z.* from
(select t.typname, t.oid, a.attname, a.atttypid from pg_attribute a inner join pg_type t on a.attrelid = t.typrelid) z,
allattrs y where y.atttypid = z.oid
)
SELECT * FROM allattrs limit 100
;
But that does not find the inner array of t_station
composite type.
Soluzione
Array types break the simple chain you are following. In case of an array type you have to resolve pg_type.typelem
to get to the base type.
WITH RECURSIVE cte(typname, type_oid, attname, atttypid, typelem) AS (
SELECT t.typname, t.oid, a.attname, a.atttypid, t.typelem
FROM pg_type t
LEFT JOIN pg_attribute a ON a.attrelid = t.typrelid
AND a.attnum > 0
AND NOT a.attisdropped
WHERE t.typrelid = 't_employee'::regclass
UNION ALL
SELECT t.typname, t.oid
,COALESCE(a.attname, t.typelem::regtype::text)
,COALESCE(a.atttypid, t.typelem), t.typelem
FROM cte c
JOIN pg_type t ON t.oid = c.atttypid AND (t.typtype = 'c' OR t.typelem > 0)
LEFT JOIN pg_attribute a ON a.attrelid = t.typrelid
AND a.attnum > 0
AND NOT a.attisdropped
)
SELECT typname, type_oid, attname, atttypid
FROM cte
WHERE typelem = 0 -- filter out rows for array types
If you want to include extra rows for array types in the result, remove the final WHERE condition ..
This JOIN condition only follows composite types or arrays:
AND (t.typtype = 'c' OR t.typelem > 0)
I also added conditions to exclude system columns and dead columns:
AND a.attnum > 0
AND NOT a.attisdropped
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow