pg_trigger.tgparentid

Started by Alvaro Herreraalmost 6 years ago10 messages
#1Alvaro Herrera
alvherre@2ndquadrant.com
1 attachment(s)

As mentioned in /messages/by-id/20191231194759.GA24692@alvherre.pgsql
I propose to add a new column to pg_trigger, which allows us to remove a
pg_depend scan when cloning triggers when adding/attaching partitions.
(It's not that I think the scan is a performance problem, but rather
than notionally we try not to depend on pg_depend contents for this kind
of semantic derivation.)

--
�lvaro Herrera 39�49'30"S 73�17'W

Attachments:

0001-Record-parents-of-triggers.patchtext/x-diff; charset=us-asciiDownload
From 8f1be4b2c24385bbfe2ecba941545ffa84f60b47 Mon Sep 17 00:00:00 2001
From: Alvaro Herrera <alvherre@alvh.no-ip.org>
Date: Mon, 17 Feb 2020 17:34:47 -0300
Subject: [PATCH] Record parents of triggers

This let us get rid of a recently introduced ugly hack (commit
1fa846f1c9af).
---
 doc/src/sgml/catalogs.sgml       |  7 +++++
 src/backend/commands/tablecmds.c | 52 ++------------------------------
 src/backend/commands/trigger.c   |  1 +
 src/include/catalog/pg_trigger.h |  1 +
 4 files changed, 11 insertions(+), 50 deletions(-)

diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index a10b66569b..0bba96ee52 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -6951,6 +6951,13 @@ SCRAM-SHA-256$<replaceable>&lt;iteration count&gt;</replaceable>:<replaceable>&l
       <entry>The table this trigger is on</entry>
      </row>
 
+     <row>
+      <entry><structfield>tgparentid</structfield></entry>
+      <entry><type>oid</type></entry>
+      <entry><literal><link linkend="catalog-pg-trigger"><structname>pg_trigger</structname></link>.oid</literal></entry>
+      <entry>Parent trigger of this trigger, if any</entry>
+     </row>
+
      <row>
       <entry><structfield>tgname</structfield></entry>
       <entry><type>name</type></entry>
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index b7c8d663fc..ae39bf1056 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -16447,54 +16447,6 @@ out:
 	MemoryContextDelete(cxt);
 }
 
-/*
- * isPartitionTrigger
- *		Subroutine for CloneRowTriggersToPartition: determine whether
- *		the given trigger has been cloned from another one.
- *
- * We use pg_depend as a proxy for this, since we don't have any direct
- * evidence.  This is an ugly hack to cope with a catalog deficiency.
- * Keep away from children.  Do not stare with naked eyes.  Do not propagate.
- */
-static bool
-isPartitionTrigger(Oid trigger_oid)
-{
-	Relation	pg_depend;
-	ScanKeyData key[2];
-	SysScanDesc scan;
-	HeapTuple	tup;
-	bool		found = false;
-
-	pg_depend = table_open(DependRelationId, AccessShareLock);
-
-	ScanKeyInit(&key[0], Anum_pg_depend_classid,
-				BTEqualStrategyNumber,
-				F_OIDEQ,
-				ObjectIdGetDatum(TriggerRelationId));
-	ScanKeyInit(&key[1], Anum_pg_depend_objid,
-				BTEqualStrategyNumber,
-				F_OIDEQ,
-				ObjectIdGetDatum(trigger_oid));
-
-	scan = systable_beginscan(pg_depend, DependDependerIndexId,
-							  true, NULL, 2, key);
-	while ((tup = systable_getnext(scan)) != NULL)
-	{
-		Form_pg_depend dep = (Form_pg_depend) GETSTRUCT(tup);
-
-		if (dep->refclassid == TriggerRelationId)
-		{
-			found = true;
-			break;
-		}
-	}
-
-	systable_endscan(scan);
-	table_close(pg_depend, AccessShareLock);
-
-	return found;
-}
-
 /*
  * CloneRowTriggersToPartition
  *		subroutine for ATExecAttachPartition/DefineRelation to create row
@@ -16541,7 +16493,7 @@ CloneRowTriggersToPartition(Relation parent, Relation partition)
 		 *
 		 * However, if our parent is a partitioned relation, there might be
 		 * internal triggers that need cloning.  In that case, we must skip
-		 * clone it if the trigger on parent depends on another trigger.
+		 * cloning it if the trigger on parent depends on another trigger.
 		 *
 		 * Note we dare not verify that the other trigger belongs to an
 		 * ancestor relation of our parent, because that creates deadlock
@@ -16549,7 +16501,7 @@ CloneRowTriggersToPartition(Relation parent, Relation partition)
 		 */
 		if (trigForm->tgisinternal &&
 			(!parent->rd_rel->relispartition ||
-			 !isPartitionTrigger(trigForm->oid)))
+			 !OidIsValid(trigForm->tgparentid)))
 			continue;
 
 		/*
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index b9b1262e30..6e8b7223fe 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -847,6 +847,7 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString,
 
 	values[Anum_pg_trigger_oid - 1] = ObjectIdGetDatum(trigoid);
 	values[Anum_pg_trigger_tgrelid - 1] = ObjectIdGetDatum(RelationGetRelid(rel));
+	values[Anum_pg_trigger_tgparentid - 1] = ObjectIdGetDatum(parentTriggerOid);
 	values[Anum_pg_trigger_tgname - 1] = DirectFunctionCall1(namein,
 															 CStringGetDatum(trigname));
 	values[Anum_pg_trigger_tgfoid - 1] = ObjectIdGetDatum(funcoid);
diff --git a/src/include/catalog/pg_trigger.h b/src/include/catalog/pg_trigger.h
index 2f30c7c2f9..9612b9bdd6 100644
--- a/src/include/catalog/pg_trigger.h
+++ b/src/include/catalog/pg_trigger.h
@@ -35,6 +35,7 @@ CATALOG(pg_trigger,2620,TriggerRelationId)
 {
 	Oid			oid;			/* oid */
 	Oid			tgrelid;		/* relation trigger is attached to */
+	Oid			tgparentid;		/* OID of parent trigger, if any */
 	NameData	tgname;			/* trigger's name */
 	Oid			tgfoid;			/* OID of function to be called */
 	int16		tgtype;			/* BEFORE/AFTER/INSTEAD, UPDATE/DELETE/INSERT,
-- 
2.20.1

#2Tom Lane
tgl@sss.pgh.pa.us
In reply to: Alvaro Herrera (#1)
Re: pg_trigger.tgparentid

Alvaro Herrera <alvherre@2ndquadrant.com> writes:

As mentioned in /messages/by-id/20191231194759.GA24692@alvherre.pgsql
I propose to add a new column to pg_trigger, which allows us to remove a
pg_depend scan when cloning triggers when adding/attaching partitions.
(It's not that I think the scan is a performance problem, but rather
than notionally we try not to depend on pg_depend contents for this kind
of semantic derivation.)

It'd be nice if the term "parent trigger" were defined somewhere in
this. Seems all right otherwise.

regards, tom lane

#3Amit Langote
amitlangote09@gmail.com
In reply to: Alvaro Herrera (#1)
Re: pg_trigger.tgparentid

On Tue, Feb 18, 2020 at 6:56 AM Alvaro Herrera <alvherre@2ndquadrant.com> wrote:

As mentioned in /messages/by-id/20191231194759.GA24692@alvherre.pgsql
I propose to add a new column to pg_trigger, which allows us to remove a
pg_depend scan when cloning triggers when adding/attaching partitions.
(It's not that I think the scan is a performance problem, but rather
than notionally we try not to depend on pg_depend contents for this kind
of semantic derivation.)

@@ -16541,7 +16493,7 @@ CloneRowTriggersToPartition(Relation parent,
Relation partition)
*
* However, if our parent is a partitioned relation, there might be

This is existing text, but should really be:

However, if our parent is a *partition* itself, there might be

(Sorry, I forgot to report this when the bug-fix went in couple months ago.)

  * internal triggers that need cloning.  In that case, we must skip
- * clone it if the trigger on parent depends on another trigger.
+ * cloning it if the trigger on parent depends on another trigger.

2nd sentence seems unclear to me. Does the following say what needs
to be said here:

* However, if our parent is a partition itself, there might be
* internal triggers that need cloning. For example, triggers on the
* parent that were in turn cloned from its own parent are marked
* internal, which too must be cloned to the partition.

Thanks,
Amit

#4Amit Langote
amitlangote09@gmail.com
In reply to: Amit Langote (#3)
Re: pg_trigger.tgparentid

On Tue, Feb 18, 2020 at 1:11 PM Amit Langote <amitlangote09@gmail.com> wrote:

On Tue, Feb 18, 2020 at 6:56 AM Alvaro Herrera <alvherre@2ndquadrant.com> wrote:
@@ -16541,7 +16493,7 @@ CloneRowTriggersToPartition(Relation parent,
Relation partition)
*
* However, if our parent is a partitioned relation, there might be

This is existing text, but should really be:

However, if our parent is a *partition* itself, there might be

(Sorry, I forgot to report this when the bug-fix went in couple months ago.)

* internal triggers that need cloning.  In that case, we must skip
- * clone it if the trigger on parent depends on another trigger.
+ * cloning it if the trigger on parent depends on another trigger.

2nd sentence seems unclear to me. Does the following say what needs
to be said here:

* However, if our parent is a partition itself, there might be
* internal triggers that need cloning. For example, triggers on the
* parent that were in turn cloned from its own parent are marked
* internal, which too must be cloned to the partition.

Or:

* However, if our parent is a partition itself, there might be
* internal triggers that must not be skipped. For example, triggers
* on the parent that were in turn cloned from its own parent are
* marked internal, which must be cloned to the partition.

Thanks,
Amit

#5Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Amit Langote (#4)
1 attachment(s)
Re: pg_trigger.tgparentid

On 2020-Feb-19, Amit Langote wrote:

Or:

* However, if our parent is a partition itself, there might be
* internal triggers that must not be skipped. For example, triggers
* on the parent that were in turn cloned from its own parent are
* marked internal, which must be cloned to the partition.

Thanks for pointing this out -- I agree it needed rewording. I slightly
adjusted your text like this:

* Internal triggers require careful examination. Ideally, we don't
* clone them. However, if our parent is itself a partition, there
* might be internal triggers that must not be skipped; for example,
* triggers on our parent that are in turn clones from its parent (our
* grandparent) are marked internal, yet they are to be cloned.

Is this okay for you?

Tom Lane wrote:

It'd be nice if the term "parent trigger" were defined somewhere in
this. Seems all right otherwise.

Fair point. I propose to patch catalog.sgml like this

<entry>
Parent trigger that this trigger is cloned from, zero if not a clone;
this happens when partitions are created or attached to a partitioned
table.
</entry>

It's perhaps not great to have to explain the parentage concept in the
catalog docs, so I'm going to go over the other documentation pages
(trigger.sgml and ref/create_trigger.sgml) to see whether they need any
patching; it's possible that we neglected to update them properly when
the partitioning-related commits went it.

--
�lvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

Attachments:

v2-0001-Record-parents-of-triggers.patchtext/x-diff; charset=us-asciiDownload
From ff13d7004c9ee77ba411ace405224188aa0c353c Mon Sep 17 00:00:00 2001
From: Alvaro Herrera <alvherre@alvh.no-ip.org>
Date: Mon, 17 Feb 2020 17:34:47 -0300
Subject: [PATCH v2] Record parents of triggers

This let us get rid of a recently introduced ugly hack (commit
1fa846f1c9af).
---
 doc/src/sgml/catalogs.sgml       | 11 ++++++
 src/backend/commands/tablecmds.c | 59 +++-----------------------------
 src/backend/commands/trigger.c   |  1 +
 src/include/catalog/pg_trigger.h |  1 +
 4 files changed, 18 insertions(+), 54 deletions(-)

diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index a10b66569b..34bc0d0526 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -6951,6 +6951,17 @@ SCRAM-SHA-256$<replaceable>&lt;iteration count&gt;</replaceable>:<replaceable>&l
       <entry>The table this trigger is on</entry>
      </row>
 
+     <row>
+      <entry><structfield>tgparentid</structfield></entry>
+      <entry><type>oid</type></entry>
+      <entry><literal><link linkend="catalog-pg-trigger"><structname>pg_trigger</structname></link>.oid</literal></entry>
+      <entry>
+       Parent trigger that this trigger is cloned from, zero if not a clone;
+       this happens when partitions are created or attached to a partitioned
+       table.
+      </entry>
+     </row>
+
      <row>
       <entry><structfield>tgname</structfield></entry>
       <entry><type>name</type></entry>
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index b7c8d663fc..02a7c04fdb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -16447,54 +16447,6 @@ out:
 	MemoryContextDelete(cxt);
 }
 
-/*
- * isPartitionTrigger
- *		Subroutine for CloneRowTriggersToPartition: determine whether
- *		the given trigger has been cloned from another one.
- *
- * We use pg_depend as a proxy for this, since we don't have any direct
- * evidence.  This is an ugly hack to cope with a catalog deficiency.
- * Keep away from children.  Do not stare with naked eyes.  Do not propagate.
- */
-static bool
-isPartitionTrigger(Oid trigger_oid)
-{
-	Relation	pg_depend;
-	ScanKeyData key[2];
-	SysScanDesc scan;
-	HeapTuple	tup;
-	bool		found = false;
-
-	pg_depend = table_open(DependRelationId, AccessShareLock);
-
-	ScanKeyInit(&key[0], Anum_pg_depend_classid,
-				BTEqualStrategyNumber,
-				F_OIDEQ,
-				ObjectIdGetDatum(TriggerRelationId));
-	ScanKeyInit(&key[1], Anum_pg_depend_objid,
-				BTEqualStrategyNumber,
-				F_OIDEQ,
-				ObjectIdGetDatum(trigger_oid));
-
-	scan = systable_beginscan(pg_depend, DependDependerIndexId,
-							  true, NULL, 2, key);
-	while ((tup = systable_getnext(scan)) != NULL)
-	{
-		Form_pg_depend dep = (Form_pg_depend) GETSTRUCT(tup);
-
-		if (dep->refclassid == TriggerRelationId)
-		{
-			found = true;
-			break;
-		}
-	}
-
-	systable_endscan(scan);
-	table_close(pg_depend, AccessShareLock);
-
-	return found;
-}
-
 /*
  * CloneRowTriggersToPartition
  *		subroutine for ATExecAttachPartition/DefineRelation to create row
@@ -16537,11 +16489,10 @@ CloneRowTriggersToPartition(Relation parent, Relation partition)
 
 		/*
 		 * Internal triggers require careful examination.  Ideally, we don't
-		 * clone them.
-		 *
-		 * However, if our parent is a partitioned relation, there might be
-		 * internal triggers that need cloning.  In that case, we must skip
-		 * clone it if the trigger on parent depends on another trigger.
+		 * clone them.  However, if our parent is itself a partition, there
+		 * might be internal triggers that must not be skipped; for example,
+		 * triggers on our parent that are in turn clones from its parent (our
+		 * grandparent) are marked internal, yet they are to be cloned.
 		 *
 		 * Note we dare not verify that the other trigger belongs to an
 		 * ancestor relation of our parent, because that creates deadlock
@@ -16549,7 +16500,7 @@ CloneRowTriggersToPartition(Relation parent, Relation partition)
 		 */
 		if (trigForm->tgisinternal &&
 			(!parent->rd_rel->relispartition ||
-			 !isPartitionTrigger(trigForm->oid)))
+			 !OidIsValid(trigForm->tgparentid)))
 			continue;
 
 		/*
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index b9b1262e30..6e8b7223fe 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -847,6 +847,7 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString,
 
 	values[Anum_pg_trigger_oid - 1] = ObjectIdGetDatum(trigoid);
 	values[Anum_pg_trigger_tgrelid - 1] = ObjectIdGetDatum(RelationGetRelid(rel));
+	values[Anum_pg_trigger_tgparentid - 1] = ObjectIdGetDatum(parentTriggerOid);
 	values[Anum_pg_trigger_tgname - 1] = DirectFunctionCall1(namein,
 															 CStringGetDatum(trigname));
 	values[Anum_pg_trigger_tgfoid - 1] = ObjectIdGetDatum(funcoid);
diff --git a/src/include/catalog/pg_trigger.h b/src/include/catalog/pg_trigger.h
index 2f30c7c2f9..9612b9bdd6 100644
--- a/src/include/catalog/pg_trigger.h
+++ b/src/include/catalog/pg_trigger.h
@@ -35,6 +35,7 @@ CATALOG(pg_trigger,2620,TriggerRelationId)
 {
 	Oid			oid;			/* oid */
 	Oid			tgrelid;		/* relation trigger is attached to */
+	Oid			tgparentid;		/* OID of parent trigger, if any */
 	NameData	tgname;			/* trigger's name */
 	Oid			tgfoid;			/* OID of function to be called */
 	int16		tgtype;			/* BEFORE/AFTER/INSTEAD, UPDATE/DELETE/INSERT,
-- 
2.20.1

#6Amit Langote
amitlangote09@gmail.com
In reply to: Alvaro Herrera (#5)
Re: pg_trigger.tgparentid

Hi Alvaro,

On Tue, Feb 25, 2020 at 3:58 AM Alvaro Herrera <alvherre@2ndquadrant.com> wrote:

On 2020-Feb-19, Amit Langote wrote:

Or:

* However, if our parent is a partition itself, there might be
* internal triggers that must not be skipped. For example, triggers
* on the parent that were in turn cloned from its own parent are
* marked internal, which must be cloned to the partition.

Thanks for pointing this out -- I agree it needed rewording. I slightly
adjusted your text like this:

* Internal triggers require careful examination. Ideally, we don't
* clone them. However, if our parent is itself a partition, there
* might be internal triggers that must not be skipped; for example,
* triggers on our parent that are in turn clones from its parent (our
* grandparent) are marked internal, yet they are to be cloned.

Is this okay for you?

Thanks. Your revised text looks good, except there is a typo:

in turn clones -> in turn cloned

Thanks,
Amit

#7Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Amit Langote (#6)
Re: pg_trigger.tgparentid

On 2020-Feb-25, Amit Langote wrote:

On Tue, Feb 25, 2020 at 3:58 AM Alvaro Herrera <alvherre@2ndquadrant.com> wrote:

Thanks for pointing this out -- I agree it needed rewording. I slightly
adjusted your text like this:

* Internal triggers require careful examination. Ideally, we don't
* clone them. However, if our parent is itself a partition, there
* might be internal triggers that must not be skipped; for example,
* triggers on our parent that are in turn clones from its parent (our
* grandparent) are marked internal, yet they are to be cloned.

Is this okay for you?

Thanks. Your revised text looks good, except there is a typo:

in turn clones -> in turn cloned

Actually, that was on purpose ... (I also changed "were" to "are" to match.)

--
�lvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

#8Amit Langote
amitlangote09@gmail.com
In reply to: Alvaro Herrera (#7)
Re: pg_trigger.tgparentid

On Tue, Feb 25, 2020 at 11:01 PM Alvaro Herrera
<alvherre@2ndquadrant.com> wrote:

On 2020-Feb-25, Amit Langote wrote:

On Tue, Feb 25, 2020 at 3:58 AM Alvaro Herrera <alvherre@2ndquadrant.com> wrote:

Thanks for pointing this out -- I agree it needed rewording. I slightly
adjusted your text like this:

* Internal triggers require careful examination. Ideally, we don't
* clone them. However, if our parent is itself a partition, there
* might be internal triggers that must not be skipped; for example,
* triggers on our parent that are in turn clones from its parent (our
* grandparent) are marked internal, yet they are to be cloned.

Is this okay for you?

Thanks. Your revised text looks good, except there is a typo:

in turn clones -> in turn cloned

Actually, that was on purpose ... (I also changed "were" to "are" to match.)

Ah, got it.

Thanks,
Amit

#9Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Amit Langote (#8)
Re: pg_trigger.tgparentid

Thanks both -- pushed. I also changed regress/sql/triggers to leave
tables around that have a non-zero tgparentid. This ensures that the
pg_upgrade test sees such objects, as well as findoidjoins.

I refrained from doing the findoidjoins dance itself, though; I got a
large number of false positives that I think are caused by some pg12-era
hacking.

--
�lvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

#10Tom Lane
tgl@sss.pgh.pa.us
In reply to: Alvaro Herrera (#9)
Re: pg_trigger.tgparentid

Alvaro Herrera <alvherre@2ndquadrant.com> writes:

Thanks both -- pushed. I also changed regress/sql/triggers to leave
tables around that have a non-zero tgparentid. This ensures that the
pg_upgrade test sees such objects, as well as findoidjoins.

I refrained from doing the findoidjoins dance itself, though; I got a
large number of false positives that I think are caused by some pg12-era
hacking.

Generally I try to update findoidjoins once per release cycle, after
feature freeze. I don't think it's worth messing with it more often
than that. But thanks for making sure there'll be data for it to find.

regards, tom lane