diff options
author | Tom Lane | 2022-01-18 02:18:49 +0000 |
---|---|---|
committer | Tom Lane | 2022-01-18 02:19:02 +0000 |
commit | 9007d4ea774edd815efeb0e0ef954127841beb2f (patch) | |
tree | 970027a68a5249445150e75f202e4cf49b582e0e /src/bin/psql/describe.c | |
parent | 839f9636b374d67952fdb2b048f055393bc5a8f4 (diff) |
Fix psql \d's query for identifying parent triggers.
The original coding (from c33869cc3) failed with "more than one row
returned by a subquery used as an expression" if there were unrelated
triggers of the same tgname on parent partitioned tables. (That's
possible because statement-level triggers don't get inherited.) Fix
by applying LIMIT 1 after sorting the candidates by inheritance level.
Also, wrap the subquery in a CASE so that we don't have to execute it at
all when the trigger is visibly non-inherited. Aside from saving some
cycles, this avoids the need for a confusing and undocumented NULLIF().
While here, tweak the format of the emitted query to look a bit
nicer for "psql -E", and add some explanation of this subquery,
because it badly needs it.
Report and patch by Justin Pryzby (with some editing by me).
Back-patch to v13 where the faulty code came in.
Discussion: https://postgr.es/m/[email protected]
Diffstat (limited to 'src/bin/psql/describe.c')
-rw-r--r-- | src/bin/psql/describe.c | 38 |
1 files changed, 30 insertions, 8 deletions
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 8587b19160d..40433e32fa0 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -2990,16 +2990,38 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&buf, "SELECT t.tgname, " "pg_catalog.pg_get_triggerdef(t.oid, true), " - "t.tgenabled, t.tgisinternal, %s\n" + "t.tgenabled, t.tgisinternal,\n"); + + /* + * Detect whether each trigger is inherited, and if so, get the name + * of the topmost table it's inherited from. We have no easy way to + * do that pre-v13, for lack of the tgparentid column. Even with + * tgparentid, a straightforward search for the topmost parent would + * require a recursive CTE, which seems unduly expensive. We cheat a + * bit by assuming parent triggers will match by tgname; then, joining + * with pg_partition_ancestors() allows the planner to make use of + * pg_trigger_tgrelid_tgname_index if it wishes. We ensure we find + * the correct topmost parent by stopping at the first-in-partition- + * ancestry-order trigger that has tgparentid = 0. (There might be + * unrelated, non-inherited triggers with the same name further up the + * stack, so this is important.) + */ + if (pset.sversion >= 130000) + appendPQExpBufferStr(&buf, + " CASE WHEN t.tgparentid != 0 THEN\n" + " (SELECT u.tgrelid::pg_catalog.regclass\n" + " FROM pg_catalog.pg_trigger AS u,\n" + " pg_catalog.pg_partition_ancestors(t.tgrelid) WITH ORDINALITY AS a(relid, depth)\n" + " WHERE u.tgname = t.tgname AND u.tgrelid = a.relid\n" + " AND u.tgparentid = 0\n" + " ORDER BY a.depth LIMIT 1)\n" + " END AS parent\n"); + else + appendPQExpBufferStr(&buf, " NULL AS parent\n"); + + appendPQExpBuffer(&buf, "FROM pg_catalog.pg_trigger t\n" "WHERE t.tgrelid = '%s' AND ", - (pset.sversion >= 130000 ? - "(SELECT (NULLIF(a.relid, t.tgrelid))::pg_catalog.regclass" - " FROM pg_catalog.pg_trigger AS u, " - " pg_catalog.pg_partition_ancestors(t.tgrelid) AS a" - " WHERE u.tgname = t.tgname AND u.tgrelid = a.relid" - " AND u.tgparentid = 0) AS parent" : - "NULL AS parent"), oid); /* |