@@ -71,6 +71,12 @@ intSessionReplicationRole = 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 triggerexisting with oldname and update
1460
+ *Search forthe triggerto 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()