v2-0001-refactor-CreateTrigger-and-CreateTriggerFiringOn.patch
text/x-patch
Filename: v2-0001-refactor-CreateTrigger-and-CreateTriggerFiringOn.patch
Type: text/x-patch
Part: 0
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: format-patch
Series: patch v2-0001
Subject: refactor CreateTrigger and CreateTriggerFiringOn
| File | + | − |
|---|---|---|
| src/backend/catalog/index.c | 2 | 1 |
| src/backend/commands/tablecmds.c | 9 | 5 |
| src/backend/commands/trigger.c | 49 | 131 |
| src/backend/parser/gram.y | 2 | 0 |
| src/backend/parser/parse_utilcmd.c | 144 | 0 |
| src/backend/tcop/utility.c | 1 | 1 |
| src/include/commands/trigger.h | 2 | 2 |
| src/include/nodes/parsenodes.h | 1 | 0 |
| src/include/parser/parse_utilcmd.h | 2 | 0 |
From 396b47a1d6083f474db2e7abc51abef6093f3786 Mon Sep 17 00:00:00 2001
From: jian he <jian.universality@gmail.com>
Date: Thu, 25 Sep 2025 16:41:36 +0800
Subject: [PATCH v2 1/2] refactor CreateTrigger and CreateTriggerFiringOn
discussion: https://postgr.es/m/
---
src/backend/catalog/index.c | 3 +-
src/backend/commands/tablecmds.c | 14 ++-
src/backend/commands/trigger.c | 180 ++++++++---------------------
src/backend/parser/gram.y | 2 +
src/backend/parser/parse_utilcmd.c | 144 +++++++++++++++++++++++
src/backend/tcop/utility.c | 2 +-
src/include/commands/trigger.h | 4 +-
src/include/nodes/parsenodes.h | 1 +
src/include/parser/parse_utilcmd.h | 2 +
9 files changed, 212 insertions(+), 140 deletions(-)
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 5d9db167e59..8d09d61cae2 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -2039,10 +2039,11 @@ index_constraint_create(Relation heapRelation,
trigger->deferrable = true;
trigger->initdeferred = initdeferred;
trigger->constrrel = NULL;
+ trigger->transformed = true;
(void) CreateTrigger(trigger, NULL, RelationGetRelid(heapRelation),
InvalidOid, conOid, indexRelationId, InvalidOid,
- InvalidOid, NULL, true, false);
+ InvalidOid, true, false);
}
/*
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index fc89352b661..bf8120c12f2 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -13802,10 +13802,11 @@ CreateFKCheckTrigger(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint,
fk_trigger->deferrable = fkconstraint->deferrable;
fk_trigger->initdeferred = fkconstraint->initdeferred;
fk_trigger->constrrel = NULL;
+ fk_trigger->transformed = true;
trigAddress = CreateTrigger(fk_trigger, NULL, myRelOid, refRelOid,
constraintOid, indexOid, InvalidOid,
- parentTrigOid, NULL, true, false);
+ parentTrigOid, true, false);
/* Make changes-so-far visible */
CommandCounterIncrement();
@@ -13847,6 +13848,7 @@ createForeignKeyActionTriggers(Oid myRelOid, Oid refRelOid, Constraint *fkconstr
fk_trigger->whenClause = NULL;
fk_trigger->transitionRels = NIL;
fk_trigger->constrrel = NULL;
+ fk_trigger->transformed = true;
switch (fkconstraint->fk_del_action)
{
@@ -13883,7 +13885,7 @@ createForeignKeyActionTriggers(Oid myRelOid, Oid refRelOid, Constraint *fkconstr
trigAddress = CreateTrigger(fk_trigger, NULL, refRelOid, myRelOid,
constraintOid, indexOid, InvalidOid,
- parentDelTrigger, NULL, true, false);
+ parentDelTrigger, true, false);
if (deleteTrigOid)
*deleteTrigOid = trigAddress.objectId;
@@ -13907,6 +13909,7 @@ createForeignKeyActionTriggers(Oid myRelOid, Oid refRelOid, Constraint *fkconstr
fk_trigger->whenClause = NULL;
fk_trigger->transitionRels = NIL;
fk_trigger->constrrel = NULL;
+ fk_trigger->transformed = true;
switch (fkconstraint->fk_upd_action)
{
@@ -13943,7 +13946,7 @@ createForeignKeyActionTriggers(Oid myRelOid, Oid refRelOid, Constraint *fkconstr
trigAddress = CreateTrigger(fk_trigger, NULL, refRelOid, myRelOid,
constraintOid, indexOid, InvalidOid,
- parentUpdTrigger, NULL, true, false);
+ parentUpdTrigger, true, false);
if (updateTrigOid)
*updateTrigOid = trigAddress.objectId;
}
@@ -20823,15 +20826,16 @@ CloneRowTriggersToPartition(Relation parent, Relation partition)
trigStmt->timing = trigForm->tgtype & TRIGGER_TYPE_TIMING_MASK;
trigStmt->events = trigForm->tgtype & TRIGGER_TYPE_EVENT_MASK;
trigStmt->columns = cols;
- trigStmt->whenClause = NULL; /* passed separately */
+ trigStmt->whenClause = qual;
trigStmt->transitionRels = NIL; /* not supported at present */
trigStmt->deferrable = trigForm->tgdeferrable;
trigStmt->initdeferred = trigForm->tginitdeferred;
trigStmt->constrrel = NULL; /* passed separately */
+ trigStmt->transformed = true; /* whenClause alerady transformed */
CreateTriggerFiringOn(trigStmt, NULL, RelationGetRelid(partition),
trigForm->tgconstrrelid, InvalidOid, InvalidOid,
- trigForm->tgfoid, trigForm->oid, qual,
+ trigForm->tgfoid, trigForm->oid,
false, true, trigForm->tgenabled);
MemoryContextSwitchTo(oldcxt);
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index 579ac8d76ae..f1431d99a56 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -40,6 +40,7 @@
#include "parser/parse_collate.h"
#include "parser/parse_func.h"
#include "parser/parse_relation.h"
+#include "parser/parse_utilcmd.h"
#include "partitioning/partdesc.h"
#include "pgstat.h"
#include "rewrite/rewriteHandler.h"
@@ -139,9 +140,6 @@ static HeapTuple check_modified_virtual_generated(TupleDesc tupdesc, HeapTuple t
* create the trigger on partitions, 2) when creating child foreign key
* triggers; see CreateFKCheckTrigger() and createForeignKeyActionTriggers().
*
- * If whenClause is passed, it is an already-transformed expression for
- * WHEN. In this case, we ignore any that may come in stmt->whenClause.
- *
* If isInternal is true then this is an internally-generated trigger.
* This argument sets the tgisinternal field of the pg_trigger entry, and
* if true causes us to modify the given trigger name to ensure uniqueness.
@@ -159,13 +157,13 @@ static HeapTuple check_modified_virtual_generated(TupleDesc tupdesc, HeapTuple t
ObjectAddress
CreateTrigger(CreateTrigStmt *stmt, const char *queryString,
Oid relOid, Oid refRelOid, Oid constraintOid, Oid indexOid,
- Oid funcoid, Oid parentTriggerOid, Node *whenClause,
+ Oid funcoid, Oid parentTriggerOid,
bool isInternal, bool in_partition)
{
return
CreateTriggerFiringOn(stmt, queryString, relOid, refRelOid,
constraintOid, indexOid, funcoid,
- parentTriggerOid, whenClause, isInternal,
+ parentTriggerOid, isInternal,
in_partition, TRIGGER_FIRES_ON_ORIGIN);
}
@@ -177,15 +175,15 @@ ObjectAddress
CreateTriggerFiringOn(CreateTrigStmt *stmt, const char *queryString,
Oid relOid, Oid refRelOid, Oid constraintOid,
Oid indexOid, Oid funcoid, Oid parentTriggerOid,
- Node *whenClause, bool isInternal, bool in_partition,
+ bool isInternal, bool in_partition,
char trigger_fires_when)
{
int16 tgtype;
int ncolumns;
int16 *columns;
int2vector *tgattr;
- List *whenRtable;
- char *qual;
+ List *whenRtable = NIL;
+ char *qual = NULL;
Datum values[Natts_pg_trigger];
bool nulls[Natts_pg_trigger];
Relation rel;
@@ -207,6 +205,7 @@ CreateTriggerFiringOn(CreateTrigStmt *stmt, const char *queryString,
Oid existing_constraint_oid = InvalidOid;
bool existing_isInternal = false;
bool existing_isClone = false;
+ Node *whenClause = NULL;
if (OidIsValid(relOid))
rel = table_open(relOid, ShareRowExclusiveLock);
@@ -557,133 +556,21 @@ CreateTriggerFiringOn(CreateTrigStmt *stmt, const char *queryString,
/*
* Parse the WHEN clause, if any and we weren't passed an already
* transformed one.
- *
- * Note that as a side effect, we fill whenRtable when parsing. If we got
- * an already parsed clause, this does not occur, which is what we want --
- * no point in adding redundant dependencies below.
*/
- if (!whenClause && stmt->whenClause)
+ if (stmt->whenClause)
{
- ParseState *pstate;
- ParseNamespaceItem *nsitem;
- List *varList;
- ListCell *lc;
-
- /* Set up a pstate to parse with */
- pstate = make_parsestate(NULL);
- pstate->p_sourcetext = queryString;
-
- /*
- * Set up nsitems for OLD and NEW references.
- *
- * 'OLD' must always have varno equal to 1 and 'NEW' equal to 2.
- */
- nsitem = addRangeTableEntryForRelation(pstate, rel,
- AccessShareLock,
- makeAlias("old", NIL),
- false, false);
- addNSItemToQuery(pstate, nsitem, false, true, true);
- nsitem = addRangeTableEntryForRelation(pstate, rel,
- AccessShareLock,
- makeAlias("new", NIL),
- false, false);
- addNSItemToQuery(pstate, nsitem, false, true, true);
-
- /* Transform expression. Copy to be sure we don't modify original */
- whenClause = transformWhereClause(pstate,
- copyObject(stmt->whenClause),
- EXPR_KIND_TRIGGER_WHEN,
- "WHEN");
- /* we have to fix its collations too */
- assign_expr_collations(pstate, whenClause);
-
- /*
- * Check for disallowed references to OLD/NEW.
- *
- * NB: pull_var_clause is okay here only because we don't allow
- * subselects in WHEN clauses; it would fail to examine the contents
- * of subselects.
- */
- varList = pull_var_clause(whenClause, 0);
- foreach(lc, varList)
+ if (!stmt->transformed)
{
- Var *var = (Var *) lfirst(lc);
+ stmt = transformCreateTriggerStmt(RelationGetRelid(rel), stmt,
+ queryString);
- switch (var->varno)
- {
- case PRS2_OLD_VARNO:
- if (!TRIGGER_FOR_ROW(tgtype))
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
- errmsg("statement trigger's WHEN condition cannot reference column values"),
- parser_errposition(pstate, var->location)));
- if (TRIGGER_FOR_INSERT(tgtype))
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
- errmsg("INSERT trigger's WHEN condition cannot reference OLD values"),
- parser_errposition(pstate, var->location)));
- /* system columns are okay here */
- break;
- case PRS2_NEW_VARNO:
- if (!TRIGGER_FOR_ROW(tgtype))
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
- errmsg("statement trigger's WHEN condition cannot reference column values"),
- parser_errposition(pstate, var->location)));
- if (TRIGGER_FOR_DELETE(tgtype))
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
- errmsg("DELETE trigger's WHEN condition cannot reference NEW values"),
- parser_errposition(pstate, var->location)));
- if (var->varattno < 0 && TRIGGER_FOR_BEFORE(tgtype))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("BEFORE trigger's WHEN condition cannot reference NEW system columns"),
- parser_errposition(pstate, var->location)));
- if (TRIGGER_FOR_BEFORE(tgtype) &&
- var->varattno == 0 &&
- RelationGetDescr(rel)->constr &&
- (RelationGetDescr(rel)->constr->has_generated_stored ||
- RelationGetDescr(rel)->constr->has_generated_virtual))
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
- errmsg("BEFORE trigger's WHEN condition cannot reference NEW generated columns"),
- errdetail("A whole-row reference is used and the table contains generated columns."),
- parser_errposition(pstate, var->location)));
- if (TRIGGER_FOR_BEFORE(tgtype) &&
- var->varattno > 0 &&
- TupleDescAttr(RelationGetDescr(rel), var->varattno - 1)->attgenerated)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
- errmsg("BEFORE trigger's WHEN condition cannot reference NEW generated columns"),
- errdetail("Column \"%s\" is a generated column.",
- NameStr(TupleDescAttr(RelationGetDescr(rel), var->varattno - 1)->attname)),
- parser_errposition(pstate, var->location)));
- break;
- default:
- /* can't happen without add_missing_from, so just elog */
- elog(ERROR, "trigger WHEN condition cannot contain references to other relations");
- break;
- }
+ whenClause = stmt->whenClause;
+ Assert(whenClause != NULL);
}
+ else
+ whenClause = stmt->whenClause;
- /* we'll need the rtable for recordDependencyOnExpr */
- whenRtable = pstate->p_rtable;
-
- qual = nodeToString(whenClause);
-
- free_parsestate(pstate);
- }
- else if (!whenClause)
- {
- whenClause = NULL;
- whenRtable = NIL;
- qual = NULL;
- }
- else
- {
qual = nodeToString(whenClause);
- whenRtable = NIL;
}
/*
@@ -1129,10 +1016,40 @@ CreateTriggerFiringOn(CreateTrigStmt *stmt, const char *queryString,
* If it has a WHEN clause, add dependencies on objects mentioned in the
* expression (eg, functions, as well as any columns used).
*/
- if (whenRtable != NIL)
+ if (whenClause != NULL)
+ {
+ ParseState *pstate;
+ ParseNamespaceItem *nsitem;
+
+ /* Set up a pstate to parse with */
+ pstate = make_parsestate(NULL);
+ pstate->p_sourcetext = queryString;
+
+ /*
+ * Set up nsitems for OLD and NEW references.
+ *
+ * 'OLD' must always have varno equal to 1 and 'NEW' equal to 2.
+ */
+ nsitem = addRangeTableEntryForRelation(pstate, rel,
+ AccessShareLock,
+ makeAlias("old", NIL),
+ false, false);
+ addNSItemToQuery(pstate, nsitem, false, true, true);
+
+ nsitem = addRangeTableEntryForRelation(pstate, rel,
+ AccessShareLock,
+ makeAlias("new", NIL),
+ false, false);
+ addNSItemToQuery(pstate, nsitem, false, true, true);
+
+ /* we'll need the rtable for recordDependencyOnExpr */
+ whenRtable = pstate->p_rtable;
recordDependencyOnExpr(&myself, whenClause, whenRtable,
DEPENDENCY_NORMAL);
+ free_parsestate(pstate);
+ }
+
/* Post creation hook for new trigger */
InvokeObjectPostCreateHookArg(TriggerRelationId, trigoid, 0,
isInternal);
@@ -1175,7 +1092,6 @@ CreateTriggerFiringOn(CreateTrigStmt *stmt, const char *queryString,
*/
childStmt = copyObject(stmt);
childStmt->funcname = NIL;
- childStmt->whenClause = NULL;
/* If there is a WHEN clause, create a modified copy of it */
qual = copyObject(whenClause);
@@ -1185,11 +1101,13 @@ CreateTriggerFiringOn(CreateTrigStmt *stmt, const char *queryString,
qual = (Node *)
map_partition_varattnos((List *) qual, PRS2_NEW_VARNO,
childTbl, rel);
+ childStmt->whenClause = qual;
+ childStmt->transformed = true;
CreateTriggerFiringOn(childStmt, queryString,
partdesc->oids[i], refRelOid,
InvalidOid, InvalidOid,
- funcoid, trigoid, qual,
+ funcoid, trigoid,
isInternal, true, trigger_fires_when);
table_close(childTbl, NoLock);
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index f1def67ac7c..1993f76f5c1 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -6071,6 +6071,7 @@ CreateTrigStmt:
n->deferrable = false;
n->initdeferred = false;
n->constrrel = NULL;
+ n->transformed = false;
$$ = (Node *) n;
}
| CREATE opt_or_replace CONSTRAINT TRIGGER name AFTER TriggerEvents ON
@@ -6121,6 +6122,7 @@ CreateTrigStmt:
&n->deferrable, &n->initdeferred, &dummy,
NULL, NULL, yyscanner);
n->constrrel = $10;
+ n->transformed = false;
$$ = (Node *) n;
}
;
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index e96b38a59d5..fd6d5c922ec 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -38,6 +38,7 @@
#include "catalog/pg_opclass.h"
#include "catalog/pg_operator.h"
#include "catalog/pg_statistic_ext.h"
+#include "catalog/pg_trigger.h"
#include "catalog/pg_type.h"
#include "commands/comment.h"
#include "commands/defrem.h"
@@ -3133,6 +3134,149 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
return stmt;
}
+/*
+ * transformCreateTriggerStmt - parse analysis for CREATE TRIGGER
+ *
+ * Note: This is for parse analysis CreateTrigStmt->whenClause only, other
+ * CreateTrigStmt error checking happen in CreateTriggerFiringOn.
+ *
+ * To avoid race conditions, it's important that this function relies only on
+ * the passed-in relid (and not on stmt->relation) to determine the target
+ * relation.
+ */
+CreateTrigStmt *
+transformCreateTriggerStmt(Oid relid, CreateTrigStmt *stmt, const char *queryString)
+{
+ int16 tgtype;
+ ParseState *pstate;
+ ParseNamespaceItem *nsitem;
+ List *varList;
+ Relation rel;
+
+ /* Nothing to do if statement already transformed. */
+ if (stmt->transformed)
+ return stmt;
+
+ /* Compute tgtype */
+ TRIGGER_CLEAR_TYPE(tgtype);
+ if (stmt->row)
+ TRIGGER_SETT_ROW(tgtype);
+ tgtype |= stmt->timing;
+ tgtype |= stmt->events;
+
+ /* Set up a pstate to parse with */
+ pstate = make_parsestate(NULL);
+ pstate->p_sourcetext = queryString;
+
+ /*
+ * Put the parent table into the rtable so that the expressions can refer
+ * to its fields without qualification. Caller is responsible for locking
+ * relation, but we still need to open it.
+ */
+ rel = relation_open(relid, NoLock);
+
+ /*
+ * Set up nsitems for OLD and NEW references.
+ *
+ * 'OLD' must always have varno equal to 1 and 'NEW' equal to 2.
+ */
+ nsitem = addRangeTableEntryForRelation(pstate, rel,
+ AccessShareLock,
+ makeAlias("old", NIL),
+ false, false);
+ addNSItemToQuery(pstate, nsitem, false, true, true);
+ nsitem = addRangeTableEntryForRelation(pstate, rel,
+ AccessShareLock,
+ makeAlias("new", NIL),
+ false, false);
+ addNSItemToQuery(pstate, nsitem, false, true, true);
+
+ stmt->whenClause = transformWhereClause(pstate,
+ stmt->whenClause,
+ EXPR_KIND_TRIGGER_WHEN,
+ "WHEN");
+ /* we have to fix its collations too */
+ assign_expr_collations(pstate, stmt->whenClause);
+
+ /*
+ * Check for disallowed references to OLD/NEW.
+ *
+ * NB: pull_var_clause is okay here only because we don't allow
+ * subselects in WHEN clauses; it would fail to examine the contents
+ * of subselects.
+ */
+ varList = pull_var_clause(stmt->whenClause, 0);
+ foreach_node(Var, var, varList)
+ {
+ switch (var->varno)
+ {
+ case PRS2_OLD_VARNO:
+ if (!TRIGGER_FOR_ROW(tgtype))
+ ereport(ERROR,
+ errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("statement trigger's WHEN condition cannot reference column values"),
+ parser_errposition(pstate, var->location));
+ if (TRIGGER_FOR_INSERT(tgtype))
+ ereport(ERROR,
+ errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("INSERT trigger's WHEN condition cannot reference OLD values"),
+ parser_errposition(pstate, var->location));
+ /* system columns are okay here */
+ break;
+ case PRS2_NEW_VARNO:
+ if (!TRIGGER_FOR_ROW(tgtype))
+ ereport(ERROR,
+ errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("statement trigger's WHEN condition cannot reference column values"),
+ parser_errposition(pstate, var->location));
+ if (TRIGGER_FOR_DELETE(tgtype))
+ ereport(ERROR,
+ errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("DELETE trigger's WHEN condition cannot reference NEW values"),
+ parser_errposition(pstate, var->location));
+ if (var->varattno < 0 && TRIGGER_FOR_BEFORE(tgtype))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("BEFORE trigger's WHEN condition cannot reference NEW system columns"),
+ parser_errposition(pstate, var->location));
+ if (TRIGGER_FOR_BEFORE(tgtype) &&
+ var->varattno == 0 &&
+ RelationGetDescr(rel)->constr &&
+ (RelationGetDescr(rel)->constr->has_generated_stored ||
+ RelationGetDescr(rel)->constr->has_generated_virtual))
+ ereport(ERROR,
+ errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("BEFORE trigger's WHEN condition cannot reference NEW generated columns"),
+ errdetail("A whole-row reference is used and the table contains generated columns."),
+ parser_errposition(pstate, var->location));
+ if (TRIGGER_FOR_BEFORE(tgtype) &&
+ var->varattno > 0 &&
+ TupleDescAttr(RelationGetDescr(rel), var->varattno - 1)->attgenerated)
+ ereport(ERROR,
+ errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("BEFORE trigger's WHEN condition cannot reference NEW generated columns"),
+ errdetail("Column \"%s\" is a generated column.",
+ NameStr(TupleDescAttr(RelationGetDescr(rel), var->varattno - 1)->attname)),
+ parser_errposition(pstate, var->location));
+ break;
+ default:
+ /* can't happen without add_missing_from, so just elog */
+ elog(ERROR, "trigger WHEN condition cannot contain references to other relations");
+ break;
+ }
+ }
+
+ free_parsestate(pstate);
+
+ /* Close relation */
+ table_close(rel, NoLock);
+
+ /* Mark statement as successfully transformed */
+ stmt->transformed = true;
+
+ return stmt;
+}
+
/*
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 918db53dd5e..73d47b5ebf2 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1695,7 +1695,7 @@ ProcessUtilitySlow(ParseState *pstate,
address = CreateTrigger((CreateTrigStmt *) parsetree,
queryString, InvalidOid, InvalidOid,
InvalidOid, InvalidOid, InvalidOid,
- InvalidOid, NULL, false, false);
+ InvalidOid, false, false);
break;
case T_CreatePLangStmt:
diff --git a/src/include/commands/trigger.h b/src/include/commands/trigger.h
index cfd7daa20ed..3f4951bca61 100644
--- a/src/include/commands/trigger.h
+++ b/src/include/commands/trigger.h
@@ -153,12 +153,12 @@ extern PGDLLIMPORT int SessionReplicationRole;
extern ObjectAddress CreateTrigger(CreateTrigStmt *stmt, const char *queryString,
Oid relOid, Oid refRelOid, Oid constraintOid, Oid indexOid,
- Oid funcoid, Oid parentTriggerOid, Node *whenClause,
+ Oid funcoid, Oid parentTriggerOid,
bool isInternal, bool in_partition);
extern ObjectAddress CreateTriggerFiringOn(CreateTrigStmt *stmt, const char *queryString,
Oid relOid, Oid refRelOid, Oid constraintOid,
Oid indexOid, Oid funcoid, Oid parentTriggerOid,
- Node *whenClause, bool isInternal, bool in_partition,
+ bool isInternal, bool in_partition,
char trigger_fires_when);
extern void TriggerSetParentTrigger(Relation trigRel,
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index ac0e02a1db7..7039ef25ab1 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -3124,6 +3124,7 @@ typedef struct CreateTrigStmt
bool deferrable; /* [NOT] DEFERRABLE */
bool initdeferred; /* INITIALLY {DEFERRED|IMMEDIATE} */
RangeVar *constrrel; /* opposite relation, if RI trigger */
+ bool transformed; /* true when transformCreateTriggerStmt is finished */
} CreateTrigStmt;
/* ----------------------
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 4965fac4495..5482719d997 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -28,6 +28,8 @@ extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
const char *queryString);
+extern CreateTrigStmt *transformCreateTriggerStmt(Oid relid, CreateTrigStmt *stmt,
+ const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
extern List *transformCreateSchemaStmtElements(List *schemaElts,
--
2.34.1