@@ -487,6 +487,7 @@ static void ValidatePartitionConstraints(List **wqueue, Relation scanrel,
487487List * scanrel_children ,
488488List * partConstraint ,
489489bool validate_default );
490+ static void CloneRowTriggersToPartition (Relation parent ,Relation partition );
490491static ObjectAddress ATExecDetachPartition (Relation rel ,RangeVar * name );
491492static ObjectAddress ATExecAttachPartitionIdx (List * * wqueue ,Relation rel ,
492493RangeVar * 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 */
913916if (stmt -> partbound )
914917{
@@ -949,6 +952,14 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
949952}
950953
951954list_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+
952963heap_close (parent ,NoLock );
953964}
954965
@@ -7491,6 +7502,7 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
74917502fkconstraint -> deferrable ,
74927503fkconstraint -> initdeferred ,
74937504fkconstraint -> initially_valid ,
7505+ InvalidOid ,/* no parent constraint */
74947506RelationGetRelid (rel ),
74957507fkattnum ,
74967508numfks ,
@@ -8445,7 +8457,7 @@ CreateFKCheckTrigger(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint,
84458457fk_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 */
84518463CommandCounterIncrement ();
@@ -8519,7 +8531,7 @@ createForeignKeyTriggers(Relation rel, Oid refRelOid, Constraint *fkconstraint,
85198531fk_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 */
85258537CommandCounterIncrement ();
@@ -8574,7 +8586,7 @@ createForeignKeyTriggers(Relation rel, Oid refRelOid, Constraint *fkconstraint,
85748586fk_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 */
85808592CommandCounterIncrement ();
@@ -11114,7 +11126,7 @@ static void
1111411126ATExecEnableDisableTrigger (Relation rel ,const char * trigname ,
1111511127char 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. */
1403214044AttachPartitionEnsureIndexes (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)
1425414269MemoryContextDelete (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 *