@@ -71,6 +71,12 @@ int SessionReplicationRole = SESSION_REPLICATION_ROLE_ORIGIN;
71
71
static int MyTriggerDepth = 0 ;
72
72
73
73
/* 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 );
74
80
static void SetTriggerFlags (TriggerDesc * trigdesc , Trigger * trigger );
75
81
static bool GetTupleForTrigger (EState * estate ,
76
82
EPQState * epqstate ,
@@ -1442,38 +1448,16 @@ renametrig(RenameStmt *stmt)
1442
1448
targetrel = relation_open (relid , NoLock );
1443
1449
1444
1450
/*
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.
1452
1453
*/
1453
- tgrel = table_open (TriggerRelationId , RowExclusiveLock );
1454
+ if (targetrel -> rd_rel -> relkind == RELKIND_PARTITIONED_TABLE )
1455
+ (void ) find_all_inheritors (relid , AccessExclusiveLock , NULL );
1454
1456
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 );
1474
1458
1475
1459
/*
1476
- * Second pass -- look for trigger existing with oldname and update
1460
+ * Search for the trigger to modify.
1477
1461
*/
1478
1462
ScanKeyInit (& key [0 ],
1479
1463
Anum_pg_trigger_tgrelid ,
@@ -1489,27 +1473,40 @@ renametrig(RenameStmt *stmt)
1489
1473
{
1490
1474
Form_pg_trigger trigform ;
1491
1475
1492
- /*
1493
- * Update pg_trigger tuple with new tgname.
1494
- */
1495
- tuple = heap_copytuple (tuple ); /* need a modifiable copy */
1496
1476
trigform = (Form_pg_trigger ) GETSTRUCT (tuple );
1497
1477
tgoid = trigform -> oid ;
1498
1478
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))));
1501
1491
1502
- CatalogTupleUpdate (tgrel , & tuple -> t_self , tuple );
1503
1492
1504
- InvokeObjectPostAlterHook (TriggerRelationId ,
1505
- tgoid , 0 );
1493
+ /* Rename the trigger on this relation ... */
1494
+ renametrig_internal (tgrel , targetrel , tuple , stmt -> newname ,
1495
+ stmt -> subname );
1506
1496
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
+ }
1513
1510
}
1514
1511
else
1515
1512
{
@@ -1533,6 +1530,137 @@ renametrig(RenameStmt *stmt)
1533
1530
return address ;
1534
1531
}
1535
1532
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
+ }
1536
1664
1537
1665
/*
1538
1666
* EnableDisableTrigger()
0 commit comments