From f08e5e92e8d494d8d90c1b0f6584b576c01da75f Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Thu, 18 Jun 2009 01:27:02 +0000
Subject: [PATCH] Fix the just-reported problem that you can't specify all four
 trigger event types in CREATE TRIGGER.  While at it, clean up the amazingly
 tedious and inextensible way that the trigger event type list was handled. 
 Per report from Greg Sabino Mullane.

---
 src/backend/commands/tablecmds.c | 14 +++------
 src/backend/commands/trigger.c   | 52 +++++---------------------------
 src/backend/nodes/copyfuncs.c    |  4 +--
 src/backend/nodes/equalfuncs.c   |  5 ++-
 src/backend/parser/gram.y        | 39 +++++++++---------------
 src/include/nodes/parsenodes.h   |  5 +--
 6 files changed, 33 insertions(+), 86 deletions(-)

diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index f75261160e7..f22e7be5d88 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.287 2009/06/11 20:46:11 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.288 2009/06/18 01:27:02 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -5206,14 +5206,13 @@ CreateFKCheckTrigger(RangeVar *myRel, FkConstraint *fkconstraint,
 	if (on_insert)
 	{
 		fk_trigger->funcname = SystemFuncName("RI_FKey_check_ins");
-		fk_trigger->actions[0] = 'i';
+		fk_trigger->events = TRIGGER_TYPE_INSERT;
 	}
 	else
 	{
 		fk_trigger->funcname = SystemFuncName("RI_FKey_check_upd");
-		fk_trigger->actions[0] = 'u';
+		fk_trigger->events = TRIGGER_TYPE_UPDATE;
 	}
-	fk_trigger->actions[1] = '\0';
 
 	fk_trigger->isconstraint = true;
 	fk_trigger->deferrable = fkconstraint->deferrable;
@@ -5263,9 +5262,7 @@ createForeignKeyTriggers(Relation rel, FkConstraint *fkconstraint,
 	fk_trigger->relation = fkconstraint->pktable;
 	fk_trigger->before = false;
 	fk_trigger->row = true;
-	fk_trigger->actions[0] = 'd';
-	fk_trigger->actions[1] = '\0';
-
+	fk_trigger->events = TRIGGER_TYPE_DELETE;
 	fk_trigger->isconstraint = true;
 	fk_trigger->constrrel = myRel;
 	switch (fkconstraint->fk_del_action)
@@ -5316,8 +5313,7 @@ createForeignKeyTriggers(Relation rel, FkConstraint *fkconstraint,
 	fk_trigger->relation = fkconstraint->pktable;
 	fk_trigger->before = false;
 	fk_trigger->row = true;
-	fk_trigger->actions[0] = 'u';
-	fk_trigger->actions[1] = '\0';
+	fk_trigger->events = TRIGGER_TYPE_UPDATE;
 	fk_trigger->isconstraint = true;
 	fk_trigger->constrrel = myRel;
 	switch (fkconstraint->fk_upd_action)
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index 08d9593de91..bb628ed80a3 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/commands/trigger.c,v 1.247 2009/06/11 14:48:56 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/commands/trigger.c,v 1.248 2009/06/18 01:27:02 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -100,7 +100,6 @@ CreateTrigger(CreateTrigStmt *stmt, Oid constraintOid, bool checkPermissions)
 	Oid			funcoid;
 	Oid			funcrettype;
 	Oid			trigoid;
-	int			i;
 	char		constrtrigname[NAMEDATALEN];
 	char	   *trigname;
 	char	   *constrname;
@@ -150,50 +149,13 @@ CreateTrigger(CreateTrigStmt *stmt, Oid constraintOid, bool checkPermissions)
 		TRIGGER_SETT_BEFORE(tgtype);
 	if (stmt->row)
 		TRIGGER_SETT_ROW(tgtype);
+	tgtype |= stmt->events;
 
-	for (i = 0; stmt->actions[i]; i++)
-	{
-		switch (stmt->actions[i])
-		{
-			case 'i':
-				if (TRIGGER_FOR_INSERT(tgtype))
-					ereport(ERROR,
-							(errcode(ERRCODE_SYNTAX_ERROR),
-							 errmsg("multiple INSERT events specified")));
-				TRIGGER_SETT_INSERT(tgtype);
-				break;
-			case 'd':
-				if (TRIGGER_FOR_DELETE(tgtype))
-					ereport(ERROR,
-							(errcode(ERRCODE_SYNTAX_ERROR),
-							 errmsg("multiple DELETE events specified")));
-				TRIGGER_SETT_DELETE(tgtype);
-				break;
-			case 'u':
-				if (TRIGGER_FOR_UPDATE(tgtype))
-					ereport(ERROR,
-							(errcode(ERRCODE_SYNTAX_ERROR),
-							 errmsg("multiple UPDATE events specified")));
-				TRIGGER_SETT_UPDATE(tgtype);
-				break;
-			case 't':
-				if (TRIGGER_FOR_TRUNCATE(tgtype))
-					ereport(ERROR,
-							(errcode(ERRCODE_SYNTAX_ERROR),
-							 errmsg("multiple TRUNCATE events specified")));
-				TRIGGER_SETT_TRUNCATE(tgtype);
-				/* Disallow ROW-level TRUNCATE triggers */
-				if (stmt->row)
-					ereport(ERROR,
-							(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-							 errmsg("TRUNCATE FOR EACH ROW triggers are not supported")));
-				break;
-			default:
-				elog(ERROR, "unrecognized trigger event: %d",
-					 (int) stmt->actions[i]);
-				break;
-		}
-	}
+	/* Disallow ROW-level TRUNCATE triggers */
+	if (TRIGGER_FOR_ROW(tgtype) && TRIGGER_FOR_TRUNCATE(tgtype))
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("TRUNCATE FOR EACH ROW triggers are not supported")));
 
 	/*
 	 * Find and validate the trigger function.
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 4917986e891..72c9877ffd5 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -15,7 +15,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.431 2009/06/11 14:48:58 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.432 2009/06/18 01:27:02 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -3081,7 +3081,7 @@ _copyCreateTrigStmt(CreateTrigStmt *from)
 	COPY_NODE_FIELD(args);
 	COPY_SCALAR_FIELD(before);
 	COPY_SCALAR_FIELD(row);
-	strcpy(newnode->actions, from->actions);	/* in-line string field */
+	COPY_SCALAR_FIELD(events);
 	COPY_SCALAR_FIELD(isconstraint);
 	COPY_SCALAR_FIELD(deferrable);
 	COPY_SCALAR_FIELD(initdeferred);
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 78756e6e788..041b96971c6 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -22,7 +22,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.354 2009/06/11 14:48:58 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.355 2009/06/18 01:27:02 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1639,8 +1639,7 @@ _equalCreateTrigStmt(CreateTrigStmt *a, CreateTrigStmt *b)
 	COMPARE_NODE_FIELD(args);
 	COMPARE_SCALAR_FIELD(before);
 	COMPARE_SCALAR_FIELD(row);
-	if (strcmp(a->actions, b->actions) != 0)	/* in-line string field */
-		return false;
+	COMPARE_SCALAR_FIELD(events);
 	COMPARE_SCALAR_FIELD(isconstraint);
 	COMPARE_SCALAR_FIELD(deferrable);
 	COMPARE_SCALAR_FIELD(initdeferred);
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 544b69246ed..280443074f5 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -11,7 +11,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.664 2009/05/27 20:42:29 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.665 2009/06/18 01:27:02 tgl Exp $
  *
  * HISTORY
  *	  AUTHOR			DATE			MAJOR EVENT
@@ -53,6 +53,7 @@
 
 #include "catalog/index.h"
 #include "catalog/namespace.h"
+#include "catalog/pg_trigger.h"
 #include "commands/defrem.h"
 #include "nodes/makefuncs.h"
 #include "nodes/nodeFuncs.h"
@@ -244,7 +245,7 @@ static TypeName *TableFuncTypeName(List *columns);
 %type <boolean> TriggerActionTime TriggerForSpec opt_trusted opt_restart_seqs
 %type <str>		opt_lancompiler
 
-%type <str>		TriggerEvents
+%type <ival>	TriggerEvents TriggerOneEvent
 %type <value>	TriggerFuncArg
 
 %type <str>		relation_name copy_file_name
@@ -266,7 +267,6 @@ static TypeName *TableFuncTypeName(List *columns);
 %type <privtarget> privilege_target
 %type <funwithargs> function_with_argtypes
 %type <list>	function_with_argtypes_list
-%type <chr> 	TriggerOneEvent
 
 %type <list>	stmtblock stmtmulti
 				OptTableElementList TableElementList OptInherit definition
@@ -3133,7 +3133,7 @@ CreateTrigStmt:
 					n->args = $13;
 					n->before = $4;
 					n->row = $8;
-					memcpy(n->actions, $5, 4);
+					n->events = $5;
 					n->isconstraint  = FALSE;
 					n->deferrable	 = FALSE;
 					n->initdeferred  = FALSE;
@@ -3153,11 +3153,10 @@ CreateTrigStmt:
 					n->args = $18;
 					n->before = FALSE;
 					n->row = TRUE;
-					memcpy(n->actions, $6, 4);
+					n->events = $6;
 					n->isconstraint  = TRUE;
 					n->deferrable = ($10 & 1) != 0;
 					n->initdeferred = ($10 & 2) != 0;
-
 					n->constrrel = $9;
 					$$ = (Node *)n;
 				}
@@ -3170,30 +3169,20 @@ TriggerActionTime:
 
 TriggerEvents:
 			TriggerOneEvent
+				{ $$ = $1; }
+			| TriggerEvents OR TriggerOneEvent
 				{
-					char *e = palloc(4);
-					e[0] = $1; e[1] = '\0';
-					$$ = e;
-				}
-			| TriggerOneEvent OR TriggerOneEvent
-				{
-					char *e = palloc(4);
-					e[0] = $1; e[1] = $3; e[2] = '\0';
-					$$ = e;
-				}
-			| TriggerOneEvent OR TriggerOneEvent OR TriggerOneEvent
-				{
-					char *e = palloc(4);
-					e[0] = $1; e[1] = $3; e[2] = $5; e[3] = '\0';
-					$$ = e;
+					if ($1 & $3)
+						yyerror("duplicate trigger events specified");
+					$$ = $1 | $3;
 				}
 		;
 
 TriggerOneEvent:
-			INSERT									{ $$ = 'i'; }
-			| DELETE_P								{ $$ = 'd'; }
-			| UPDATE								{ $$ = 'u'; }
-			| TRUNCATE								{ $$ = 't'; }
+			INSERT								{ $$ = TRIGGER_TYPE_INSERT; }
+			| DELETE_P							{ $$ = TRIGGER_TYPE_DELETE; }
+			| UPDATE							{ $$ = TRIGGER_TYPE_UPDATE; }
+			| TRUNCATE							{ $$ = TRIGGER_TYPE_TRUNCATE; }
 		;
 
 TriggerForSpec:
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 54f10078183..7793f66f20f 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -13,7 +13,7 @@
  * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.394 2009/06/11 14:49:11 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.395 2009/06/18 01:27:02 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1551,7 +1551,8 @@ typedef struct CreateTrigStmt
 	List	   *args;			/* list of (T_String) Values or NIL */
 	bool		before;			/* BEFORE/AFTER */
 	bool		row;			/* ROW/STATEMENT */
-	char		actions[4];		/* 1 to 3 of 'i', 'u', 'd', + trailing \0 */
+	/* events uses the TRIGGER_TYPE bits defined in catalog/pg_trigger.h */
+	int16		events;			/* INSERT/UPDATE/DELETE/TRUNCATE */
 
 	/* The following are used for referential */
 	/* integrity constraint triggers */
-- 
GitLab