@@ -487,6 +487,7 @@ static void ValidatePartitionConstraints(List **wqueue, Relation scanrel,
487487 List * scanrel_children ,
488488 List * partConstraint ,
489489 bool validate_default );
490+ static void CloneRowTriggersToPartition (Relation parent , Relation partition );
490491static ObjectAddress ATExecDetachPartition (Relation rel , RangeVar * name );
491492static ObjectAddress ATExecAttachPartitionIdx (List * * wqueue , Relation rel ,
492493 RangeVar * name );
@@ -906,9 +907,11 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
906907 }
907908
908909 /*
909- * If we're creating a partition, create now all the indexes defined in
910- * the parent. We can't do it earlier, because DefineIndex wants to know
911- * the partition key which we just stored.
910+ * If we're creating a partition, create now all the indexes and triggers
911+ * defined in the parent.
912+ *
913+ * We can't do it earlier, because DefineIndex wants to know the partition
914+ * key which we just stored.
912915 */
913916 if (stmt -> partbound )
914917 {
@@ -949,6 +952,14 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
949952 }
950953
951954 list_free (idxlist );
955+
956+ /*
957+ * If there are any row-level triggers, clone them to the new
958+ * partition.
959+ */
960+ if (parent -> trigdesc != NULL )
961+ CloneRowTriggersToPartition (parent , rel );
962+
952963 heap_close (parent , NoLock );
953964 }
954965
@@ -7491,6 +7502,7 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
74917502 fkconstraint -> deferrable ,
74927503 fkconstraint -> initdeferred ,
74937504 fkconstraint -> initially_valid ,
7505+ InvalidOid , /* no parent constraint */
74947506 RelationGetRelid (rel ),
74957507 fkattnum ,
74967508 numfks ,
@@ -8445,7 +8457,7 @@ CreateFKCheckTrigger(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint,
84458457 fk_trigger -> args = NIL ;
84468458
84478459 (void ) CreateTrigger (fk_trigger , NULL , myRelOid , refRelOid , constraintOid ,
8448- indexOid , true);
8460+ indexOid , InvalidOid , InvalidOid , NULL , true, false );
84498461
84508462 /* Make changes-so-far visible */
84518463 CommandCounterIncrement ();
@@ -8519,7 +8531,7 @@ createForeignKeyTriggers(Relation rel, Oid refRelOid, Constraint *fkconstraint,
85198531 fk_trigger -> args = NIL ;
85208532
85218533 (void ) CreateTrigger (fk_trigger , NULL , refRelOid , myRelOid , constraintOid ,
8522- indexOid , true);
8534+ indexOid , InvalidOid , InvalidOid , NULL , true, false );
85238535
85248536 /* Make changes-so-far visible */
85258537 CommandCounterIncrement ();
@@ -8574,7 +8586,7 @@ createForeignKeyTriggers(Relation rel, Oid refRelOid, Constraint *fkconstraint,
85748586 fk_trigger -> args = NIL ;
85758587
85768588 (void ) CreateTrigger (fk_trigger , NULL , refRelOid , myRelOid , constraintOid ,
8577- indexOid , true);
8589+ indexOid , InvalidOid , InvalidOid , NULL , true, false );
85788590
85798591 /* Make changes-so-far visible */
85808592 CommandCounterIncrement ();
@@ -11114,7 +11126,7 @@ static void
1111411126ATExecEnableDisableTrigger (Relation rel , const char * trigname ,
1111511127 char fires_when , bool skip_system , LOCKMODE lockmode )
1111611128{
11117- EnableDisableTrigger (rel , trigname , fires_when , skip_system );
11129+ EnableDisableTrigger (rel , trigname , fires_when , skip_system , lockmode );
1111811130}
1111911131
1112011132/*
@@ -14031,6 +14043,9 @@ ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd)
1403114043 /* Ensure there exists a correct set of indexes in the partition. */
1403214044 AttachPartitionEnsureIndexes (rel , attachrel );
1403314045
14046+ /* and triggers */
14047+ CloneRowTriggersToPartition (rel , attachrel );
14048+
1403414049 /*
1403514050 * Generate partition constraint from the partition bound specification.
1403614051 * If the parent itself is a partition, make sure to include its
@@ -14254,6 +14269,127 @@ AttachPartitionEnsureIndexes(Relation rel, Relation attachrel)
1425414269 MemoryContextDelete (cxt );
1425514270}
1425614271
14272+ /*
14273+ * CloneRowTriggersToPartition
14274+ * subroutine for ATExecAttachPartition/DefineRelation to create row
14275+ * triggers on partitions
14276+ */
14277+ static void
14278+ CloneRowTriggersToPartition (Relation parent , Relation partition )
14279+ {
14280+ Relation pg_trigger ;
14281+ ScanKeyData key ;
14282+ SysScanDesc scan ;
14283+ HeapTuple tuple ;
14284+ MemoryContext oldcxt ,
14285+ perTupCxt ;
14286+
14287+ ScanKeyInit (& key , Anum_pg_trigger_tgrelid , BTEqualStrategyNumber ,
14288+ F_OIDEQ , ObjectIdGetDatum (RelationGetRelid (parent )));
14289+ pg_trigger = heap_open (TriggerRelationId , RowExclusiveLock );
14290+ scan = systable_beginscan (pg_trigger , TriggerRelidNameIndexId ,
14291+ true, NULL , 1 , & key );
14292+
14293+ perTupCxt = AllocSetContextCreate (CurrentMemoryContext ,
14294+ "clone trig" , ALLOCSET_SMALL_SIZES );
14295+ oldcxt = MemoryContextSwitchTo (perTupCxt );
14296+
14297+ while (HeapTupleIsValid (tuple = systable_getnext (scan )))
14298+ {
14299+ Form_pg_trigger trigForm ;
14300+ CreateTrigStmt * trigStmt ;
14301+ Node * qual = NULL ;
14302+ Datum value ;
14303+ bool isnull ;
14304+ List * cols = NIL ;
14305+
14306+ trigForm = (Form_pg_trigger ) GETSTRUCT (tuple );
14307+
14308+ /*
14309+ * Ignore statement-level triggers; those are not cloned.
14310+ */
14311+ if (!TRIGGER_FOR_ROW (trigForm -> tgtype ))
14312+ continue ;
14313+
14314+ /*
14315+ * Complain if we find an unexpected trigger type.
14316+ */
14317+ if (!TRIGGER_FOR_AFTER (trigForm -> tgtype ))
14318+ elog (ERROR , "unexpected trigger \"%s\" found" ,
14319+ NameStr (trigForm -> tgname ));
14320+
14321+ /*
14322+ * If there is a WHEN clause, generate a 'cooked' version of it that's
14323+ * appropriate for the partition.
14324+ */
14325+ value = heap_getattr (tuple , Anum_pg_trigger_tgqual ,
14326+ RelationGetDescr (pg_trigger ), & isnull );
14327+ if (!isnull )
14328+ {
14329+ bool found_whole_row ;
14330+
14331+ qual = stringToNode (TextDatumGetCString (value ));
14332+ qual = (Node * ) map_partition_varattnos ((List * ) qual , PRS2_OLD_VARNO ,
14333+ partition , parent ,
14334+ & found_whole_row );
14335+ if (found_whole_row )
14336+ elog (ERROR , "unexpected whole-row reference found in trigger WHEN clause" );
14337+ qual = (Node * ) map_partition_varattnos ((List * ) qual , PRS2_NEW_VARNO ,
14338+ partition , parent ,
14339+ & found_whole_row );
14340+ if (found_whole_row )
14341+ elog (ERROR , "unexpected whole-row reference found in trigger WHEN clause" );
14342+ }
14343+
14344+ /*
14345+ * If there is a column list, transform it to a list of column names.
14346+ * Note we don't need to map this list in any way ...
14347+ */
14348+ if (trigForm -> tgattr .dim1 > 0 )
14349+ {
14350+ int i ;
14351+
14352+ for (i = 0 ; i < trigForm -> tgattr .dim1 ; i ++ )
14353+ {
14354+ Form_pg_attribute col ;
14355+
14356+ col = TupleDescAttr (parent -> rd_att ,
14357+ trigForm -> tgattr .values [i ] - 1 );
14358+ cols = lappend (cols , makeString (NameStr (col -> attname )));
14359+ }
14360+ }
14361+
14362+ trigStmt = makeNode (CreateTrigStmt );
14363+ trigStmt -> trigname = NameStr (trigForm -> tgname );
14364+ trigStmt -> relation = NULL ;
14365+ trigStmt -> funcname = NULL ; /* passed separately */
14366+ trigStmt -> args = NULL ; /* passed separately */
14367+ trigStmt -> row = true;
14368+ trigStmt -> timing = trigForm -> tgtype & TRIGGER_TYPE_TIMING_MASK ;
14369+ trigStmt -> events = trigForm -> tgtype & TRIGGER_TYPE_EVENT_MASK ;
14370+ trigStmt -> columns = cols ;
14371+ trigStmt -> whenClause = NULL ; /* passed separately */
14372+ trigStmt -> isconstraint = OidIsValid (trigForm -> tgconstraint );
14373+ trigStmt -> transitionRels = NIL ; /* not supported at present */
14374+ trigStmt -> deferrable = trigForm -> tgdeferrable ;
14375+ trigStmt -> initdeferred = trigForm -> tginitdeferred ;
14376+ trigStmt -> constrrel = NULL ; /* passed separately */
14377+
14378+ CreateTrigger (trigStmt , NULL , RelationGetRelid (partition ),
14379+ trigForm -> tgconstrrelid , InvalidOid , InvalidOid ,
14380+ trigForm -> tgfoid , HeapTupleGetOid (tuple ), qual ,
14381+ false, true);
14382+
14383+ MemoryContextReset (perTupCxt );
14384+ }
14385+
14386+ MemoryContextSwitchTo (oldcxt );
14387+ MemoryContextDelete (perTupCxt );
14388+
14389+ systable_endscan (scan );
14390+ heap_close (pg_trigger , RowExclusiveLock );
14391+ }
14392+
1425714393/*
1425814394 * ALTER TABLE DETACH PARTITION
1425914395 *
0 commit comments