@@ -71,6 +71,12 @@ intSessionReplicationRole = SESSION_REPLICATION_ROLE_ORIGIN;
7171static int MyTriggerDepth = 0 ;
7272
7373/* Local function prototypes */
74+ static void renametrig_internal (Relation tgrel ,Relation targetrel ,
75+ HeapTuple trigtup ,const char * newname ,
76+ const char * expected_name );
77+ static void renametrig_partition (Relation tgrel ,Oid partitionId ,
78+ Oid parentTriggerOid ,const char * newname ,
79+ const char * expected_name );
7480static void SetTriggerFlags (TriggerDesc * trigdesc ,Trigger * trigger );
7581static bool GetTupleForTrigger (EState * estate ,
7682EPQState * epqstate ,
@@ -1442,38 +1448,16 @@ renametrig(RenameStmt *stmt)
14421448targetrel = relation_open (relid ,NoLock );
14431449
14441450/*
1445- * Scan pg_trigger twice for existing triggers on relation. We do this in
1446- * order to ensure a trigger does not exist with newname (The unique index
1447- * on tgrelid/tgname would complain anyway) and to ensure a trigger does
1448- * exist with oldname.
1449- *
1450- * NOTE that this is cool only because we have AccessExclusiveLock on the
1451- * relation, so the trigger set won't be changing underneath us.
1451+ * On partitioned tables, this operation recurses to partitions. Lock all
1452+ * tables upfront.
14521453 */
1453- tgrel = table_open (TriggerRelationId ,RowExclusiveLock );
1454+ if (targetrel -> rd_rel -> relkind == RELKIND_PARTITIONED_TABLE )
1455+ (void )find_all_inheritors (relid ,AccessExclusiveLock ,NULL );
14541456
1455- /*
1456- * First pass -- look for name conflict
1457- */
1458- ScanKeyInit (& key [0 ],
1459- Anum_pg_trigger_tgrelid ,
1460- BTEqualStrategyNumber ,F_OIDEQ ,
1461- ObjectIdGetDatum (relid ));
1462- ScanKeyInit (& key [1 ],
1463- Anum_pg_trigger_tgname ,
1464- BTEqualStrategyNumber ,F_NAMEEQ ,
1465- PointerGetDatum (stmt -> newname ));
1466- tgscan = systable_beginscan (tgrel ,TriggerRelidNameIndexId , true,
1467- NULL ,2 ,key );
1468- if (HeapTupleIsValid (tuple = systable_getnext (tgscan )))
1469- ereport (ERROR ,
1470- (errcode (ERRCODE_DUPLICATE_OBJECT ),
1471- errmsg ("trigger \"%s\" for relation \"%s\" already exists" ,
1472- stmt -> newname ,RelationGetRelationName (targetrel ))));
1473- systable_endscan (tgscan );
1457+ tgrel = table_open (TriggerRelationId ,RowExclusiveLock );
14741458
14751459/*
1476- *Second pass -- look for triggerexisting with oldname and update
1460+ *Search forthe triggerto modify.
14771461 */
14781462ScanKeyInit (& key [0 ],
14791463Anum_pg_trigger_tgrelid ,
@@ -1489,27 +1473,40 @@ renametrig(RenameStmt *stmt)
14891473{
14901474Form_pg_trigger trigform ;
14911475
1492- /*
1493- * Update pg_trigger tuple with new tgname.
1494- */
1495- tuple = heap_copytuple (tuple );/* need a modifiable copy */
14961476trigform = (Form_pg_trigger )GETSTRUCT (tuple );
14971477tgoid = trigform -> oid ;
14981478
1499- namestrcpy (& trigform -> tgname ,
1500- stmt -> newname );
1479+ /*
1480+ * If the trigger descends from a trigger on a parent partitioned
1481+ * table, reject the rename. We don't allow a trigger in a partition
1482+ * to differ in name from that of its parent: that would lead to an
1483+ * inconsistency that pg_dump would not reproduce.
1484+ */
1485+ if (OidIsValid (trigform -> tgparentid ))
1486+ ereport (ERROR ,
1487+ errmsg ("cannot rename trigger \"%s\" on table \"%s\"" ,
1488+ stmt -> subname ,RelationGetRelationName (targetrel )),
1489+ errhint ("Rename trigger on partitioned table \"%s\" instead." ,
1490+ get_rel_name (get_partition_parent (relid , false))));
15011491
1502- CatalogTupleUpdate (tgrel ,& tuple -> t_self ,tuple );
15031492
1504- InvokeObjectPostAlterHook (TriggerRelationId ,
1505- tgoid ,0 );
1493+ /* Rename the trigger on this relation ... */
1494+ renametrig_internal (tgrel ,targetrel ,tuple ,stmt -> newname ,
1495+ stmt -> subname );
15061496
1507- /*
1508- * Invalidate relation's relcache entry so that other backends (and
1509- * this one too!) are sent SI message to make them rebuild relcache
1510- * entries. (Ideally this should happen automatically...)
1511- */
1512- CacheInvalidateRelcache (targetrel );
1497+ /* ... and if it is partitioned, recurse to its partitions */
1498+ if (targetrel -> rd_rel -> relkind == RELKIND_PARTITIONED_TABLE )
1499+ {
1500+ PartitionDesc partdesc = RelationGetPartitionDesc (targetrel , true);
1501+
1502+ for (int i = 0 ;i < partdesc -> nparts ;i ++ )
1503+ {
1504+ Oid partitionId = partdesc -> oids [i ];
1505+
1506+ renametrig_partition (tgrel ,partitionId ,trigform -> oid ,
1507+ stmt -> newname ,stmt -> subname );
1508+ }
1509+ }
15131510}
15141511else
15151512{
@@ -1533,6 +1530,137 @@ renametrig(RenameStmt *stmt)
15331530return address ;
15341531}
15351532
1533+ /*
1534+ * Subroutine for renametrig -- perform the actual work of renaming one
1535+ * trigger on one table.
1536+ *
1537+ * If the trigger has a name different from the expected one, raise a
1538+ * NOTICE about it.
1539+ */
1540+ static void
1541+ renametrig_internal (Relation tgrel ,Relation targetrel ,HeapTuple trigtup ,
1542+ const char * newname ,const char * expected_name )
1543+ {
1544+ HeapTuple tuple ;
1545+ Form_pg_trigger tgform ;
1546+ ScanKeyData key [2 ];
1547+ SysScanDesc tgscan ;
1548+
1549+ /* If the trigger already has the new name, nothing to do. */
1550+ tgform = (Form_pg_trigger )GETSTRUCT (trigtup );
1551+ if (strcmp (NameStr (tgform -> tgname ),newname )== 0 )
1552+ return ;
1553+
1554+ /*
1555+ * Before actually trying the rename, search for triggers with the same
1556+ * name. The update would fail with an ugly message in that case, and it
1557+ * is better to throw a nicer error.
1558+ */
1559+ ScanKeyInit (& key [0 ],
1560+ Anum_pg_trigger_tgrelid ,
1561+ BTEqualStrategyNumber ,F_OIDEQ ,
1562+ ObjectIdGetDatum (RelationGetRelid (targetrel )));
1563+ ScanKeyInit (& key [1 ],
1564+ Anum_pg_trigger_tgname ,
1565+ BTEqualStrategyNumber ,F_NAMEEQ ,
1566+ PointerGetDatum (newname ));
1567+ tgscan = systable_beginscan (tgrel ,TriggerRelidNameIndexId , true,
1568+ NULL ,2 ,key );
1569+ if (HeapTupleIsValid (tuple = systable_getnext (tgscan )))
1570+ ereport (ERROR ,
1571+ (errcode (ERRCODE_DUPLICATE_OBJECT ),
1572+ errmsg ("trigger \"%s\" for relation \"%s\" already exists" ,
1573+ newname ,RelationGetRelationName (targetrel ))));
1574+ systable_endscan (tgscan );
1575+
1576+ /*
1577+ * The target name is free; update the existing pg_trigger tuple with it.
1578+ */
1579+ tuple = heap_copytuple (trigtup );/* need a modifiable copy */
1580+ tgform = (Form_pg_trigger )GETSTRUCT (tuple );
1581+
1582+ /*
1583+ * If the trigger has a name different from what we expected, let the user
1584+ * know. (We can proceed anyway, since we must have reached here following
1585+ * a tgparentid link.)
1586+ */
1587+ if (strcmp (NameStr (tgform -> tgname ),expected_name )!= 0 )
1588+ ereport (NOTICE ,
1589+ errmsg ("renamed trigger \"%s\" on relation \"%s\"" ,
1590+ NameStr (tgform -> tgname ),
1591+ RelationGetRelationName (targetrel )));
1592+
1593+ namestrcpy (& tgform -> tgname ,newname );
1594+
1595+ CatalogTupleUpdate (tgrel ,& tuple -> t_self ,tuple );
1596+
1597+ InvokeObjectPostAlterHook (TriggerRelationId ,tgform -> oid ,0 );
1598+
1599+ /*
1600+ * Invalidate relation's relcache entry so that other backends (and this
1601+ * one too!) are sent SI message to make them rebuild relcache entries.
1602+ * (Ideally this should happen automatically...)
1603+ */
1604+ CacheInvalidateRelcache (targetrel );
1605+ }
1606+
1607+ /*
1608+ * Subroutine for renametrig -- Helper for recursing to partitions when
1609+ * renaming triggers on a partitioned table.
1610+ */
1611+ static void
1612+ renametrig_partition (Relation tgrel ,Oid partitionId ,Oid parentTriggerOid ,
1613+ const char * newname ,const char * expected_name )
1614+ {
1615+ SysScanDesc tgscan ;
1616+ ScanKeyData key ;
1617+ HeapTuple tuple ;
1618+ int found PG_USED_FOR_ASSERTS_ONLY = 0 ;
1619+
1620+ /*
1621+ * Given a relation and the OID of a trigger on parent relation, find the
1622+ * corresponding trigger in the child and rename that trigger to the given
1623+ * name.
1624+ */
1625+ ScanKeyInit (& key ,
1626+ Anum_pg_trigger_tgrelid ,
1627+ BTEqualStrategyNumber ,F_OIDEQ ,
1628+ ObjectIdGetDatum (partitionId ));
1629+ tgscan = systable_beginscan (tgrel ,TriggerRelidNameIndexId , true,
1630+ NULL ,1 ,& key );
1631+ while (HeapTupleIsValid (tuple = systable_getnext (tgscan )))
1632+ {
1633+ Form_pg_trigger tgform = (Form_pg_trigger )GETSTRUCT (tuple );
1634+ Relation partitionRel ;
1635+
1636+ if (tgform -> tgparentid != parentTriggerOid )
1637+ continue ;/* not our trigger */
1638+
1639+ Assert (found ++ <=0 );
1640+
1641+ partitionRel = table_open (partitionId ,NoLock );
1642+
1643+ /* Rename the trigger on this partition */
1644+ renametrig_internal (tgrel ,partitionRel ,tuple ,newname ,expected_name );
1645+
1646+ /* And if this relation is partitioned, recurse to its partitions */
1647+ if (partitionRel -> rd_rel -> relkind == RELKIND_PARTITIONED_TABLE )
1648+ {
1649+ PartitionDesc partdesc = RelationGetPartitionDesc (partitionRel ,
1650+ true);
1651+
1652+ for (int i = 0 ;i < partdesc -> nparts ;i ++ )
1653+ {
1654+ Oid partitionId = partdesc -> oids [i ];
1655+
1656+ renametrig_partition (tgrel ,partitionId ,tgform -> oid ,newname ,
1657+ NameStr (tgform -> tgname ));
1658+ }
1659+ }
1660+ table_close (partitionRel ,NoLock );
1661+ }
1662+ systable_endscan (tgscan );
1663+ }
15361664
15371665/*
15381666 * EnableDisableTrigger()