Prevent creating child triggers on partitions when adding new node

Pre PG15, tgisinternal is true for a "child" trigger on a partition
cloned from the trigger on the parent.
In PG15, tgisinternal is false in that case. However, we don't want to
create this trigger on the partition since it will create a conflict
when we try to attach the partition to the parent table:
ERROR: trigger "..." for relation "{partition_name}" already exists

Relevant PG commit:
f4566345cf40b068368cb5617e61318da60676ec
version-15-socket
naisila 2022-08-04 17:39:31 +03:00
parent b0349996cd
commit c27af16c38
1 changed files with 76 additions and 0 deletions

View File

@ -26,6 +26,9 @@
#include "distributed/metadata/distobject.h"
#include "distributed/metadata_cache.h"
#include "distributed/metadata_sync.h"
#if PG_VERSION_NUM >= PG_VERSION_15
#include "distributed/multi_partitioning_utils.h"
#endif
#include "distributed/namespace_utils.h"
#include "distributed/shard_utils.h"
#include "distributed/worker_protocol.h"
@ -53,6 +56,9 @@ static void ExtractDropStmtTriggerAndRelationName(DropStmt *dropTriggerStmt,
char **relationName);
static void ErrorIfDropStmtDropsMultipleTriggers(DropStmt *dropTriggerStmt);
static int16 GetTriggerTypeById(Oid triggerId);
#if PG_VERSION_NUM >= PG_VERSION_15
static bool TriggerExists(Oid relationId, char *triggerName);
#endif
/* GUC that overrides trigger checks for distributed tables and reference tables */
@ -158,6 +164,15 @@ GetTriggerTupleById(Oid triggerId, bool missingOk)
List *
GetExplicitTriggerIdList(Oid relationId)
{
#if PG_VERSION_NUM >= PG_VERSION_15
bool partitionTable = false;
Oid parentRelationId = InvalidOid;
if (PartitionTable(relationId))
{
partitionTable = true;
parentRelationId = PartitionParentOid(relationId);
}
#endif
List *triggerIdList = NIL;
Relation pgTrigger = table_open(TriggerRelationId, AccessShareLock);
@ -183,7 +198,22 @@ GetExplicitTriggerIdList(Oid relationId)
* internal. Hence, below we discard citus_truncate_trigger as well as
* the implicit triggers created by postgres for foreign key validation.
*/
#if PG_VERSION_NUM >= PG_VERSION_15
/*
* Pre PG15, tgisinternal is true for a "child" trigger on a partition
* cloned from the trigger on the parent.
* In PG15, tgisinternal is false in that case. However, we don't want to
* create this trigger on the partition since it will create a conflict
* when we try to attach the partition to the parent table:
* ERROR: trigger "..." for relation "{partition_name}" already exists
*/
bool isChildTrigger = partitionTable && TriggerExists(parentRelationId, NameStr(
triggerForm->tgname));
if (!triggerForm->tgisinternal && !isChildTrigger)
#else
if (!triggerForm->tgisinternal)
#endif
{
triggerIdList = lappend_oid(triggerIdList, triggerForm->oid);
}
@ -198,6 +228,52 @@ GetExplicitTriggerIdList(Oid relationId)
}
#if PG_VERSION_NUM >= PG_VERSION_15
/*
* ChildTrigger returns
* true: if the given trigger is a child trigger on the given partition
* cloned from the trigger on the parent
* false: otherwise
* It makes use of pg_trigger_tgrelid_tgname_index index on pg_trigger
*/
static bool
TriggerExists(Oid relationId, char *triggerName)
{
bool relationTriggerExists = false;
Relation pgTrigger = table_open(TriggerRelationId, AccessShareLock);
int scanKeyCount = 2;
ScanKeyData scanKey[2];
ScanKeyInit(&scanKey[0], Anum_pg_trigger_tgrelid,
BTEqualStrategyNumber, F_OIDEQ, relationId);
ScanKeyInit(&scanKey[1], Anum_pg_trigger_tgname,
BTEqualStrategyNumber, F_NAMEEQ, CStringGetDatum(triggerName));
bool useIndex = true;
SysScanDesc scanDescriptor = systable_beginscan(pgTrigger, TriggerRelidNameIndexId,
useIndex, NULL, scanKeyCount,
scanKey);
HeapTuple heapTuple = systable_getnext(scanDescriptor);
while (HeapTupleIsValid(heapTuple))
{
relationTriggerExists = true;
heapTuple = systable_getnext(scanDescriptor);
}
systable_endscan(scanDescriptor);
table_close(pgTrigger, NoLock);
return relationTriggerExists;
}
#endif
/*
* PostprocessCreateTriggerStmt is called after a CREATE TRIGGER command has
* been executed by standard process utility. This function errors out for