trigger-when_20091113.patch

application/octet-stream

Filename: trigger-when_20091113.patch
Type: application/octet-stream
Part: 0
Message: Re: TRIGGER with WHEN clause

Patch

Format: context
File+
doc/src/sgml/ref/create_trigger.sgml 15 0
src/backend/catalog/index.c 1 1
src/backend/commands/tablecmds.c 5 3
src/backend/commands/trigger.c 178 20
src/backend/executor/execMain.c 1 0
src/backend/executor/execQual.c 19 34
src/backend/executor/execUtils.c 1 0
src/backend/executor/nodeModifyTable.c 4 0
src/backend/nodes/copyfuncs.c 1 0
src/backend/nodes/equalfuncs.c 1 0
src/backend/parser/gram.y 11 4
src/backend/tcop/utility.c 1 1
src/backend/utils/adt/ruleutils.c 51 6
src/include/catalog/pg_trigger.h 3 1
src/include/commands/trigger.h 1 1
src/include/nodes/execnodes.h 2 1
src/include/nodes/parsenodes.h 1 0
src/include/utils/rel.h 1 0
src/test/regress/expected/triggers.out 65 0
src/test/regress/sql/triggers.sql 19 0
diff -cprN head/doc/src/sgml/ref/create_trigger.sgml work/doc/src/sgml/ref/create_trigger.sgml
*** head/doc/src/sgml/ref/create_trigger.sgml	2009-10-15 07:14:21.000000000 +0900
--- work/doc/src/sgml/ref/create_trigger.sgml	2009-11-13 12:04:10.402827933 +0900
*************** PostgreSQL documentation
*** 23,28 ****
--- 23,29 ----
  <synopsis>
  CREATE TRIGGER <replaceable class="PARAMETER">name</replaceable> { BEFORE | AFTER } { <replaceable class="PARAMETER">event</replaceable> [ OR ... ] }
      ON <replaceable class="PARAMETER">table</replaceable> [ FOR [ EACH ] { ROW | STATEMENT } ]
+     WHEN ( <replaceable class="parameter">condition</replaceable> )
      EXECUTE PROCEDURE <replaceable class="PARAMETER">function_name</replaceable> ( <replaceable class="PARAMETER">arguments</replaceable> )
  </synopsis>
   </refsynopsisdiv>
*************** UPDATE OF <replaceable>column_name1</rep
*** 160,165 ****
--- 161,180 ----
     </varlistentry>
  
     <varlistentry>
+     <term><replaceable class="parameter">condition</replaceable></term>
+     <listitem>
+      <para>
+       Any <acronym>SQL</acronym> conditional expression (returning
+       <type>boolean</type>). Only <literal>FOR EACH ROW</literal> triggers
+       can refer <literal>NEW</> and <literal>OLD</> tuples.
+       <literal>INSERT</literal> trigger can refer <literal>NEW</>,
+       <literal>DELETE</literal> trigger can refer <literal>OLD</>,
+       and <literal>UPDATE</literal> trigger can refer both of them.
+      </para>
+     </listitem>
+    </varlistentry>
+ 
+    <varlistentry>
      <term><replaceable class="parameter">function_name</replaceable></term>
      <listitem>
       <para>
diff -cprN head/src/backend/catalog/index.c work/src/backend/catalog/index.c
*** head/src/backend/catalog/index.c	2009-10-15 07:14:21.000000000 +0900
--- work/src/backend/catalog/index.c	2009-11-13 12:04:10.403865029 +0900
*************** index_create(Oid heapRelationId,
*** 798,804 ****
  				trigger->initdeferred = initdeferred;
  				trigger->constrrel = NULL;
  
! 				(void) CreateTrigger(trigger, conOid, indexRelationId,
  									 isprimary ? "PK_ConstraintTrigger" :
  									 "Unique_ConstraintTrigger",
  									 false);
--- 798,804 ----
  				trigger->initdeferred = initdeferred;
  				trigger->constrrel = NULL;
  
! 				(void) CreateTrigger(trigger, NULL, conOid, indexRelationId,
  									 isprimary ? "PK_ConstraintTrigger" :
  									 "Unique_ConstraintTrigger",
  									 false);
diff -cprN head/src/backend/commands/tablecmds.c work/src/backend/commands/tablecmds.c
*** head/src/backend/commands/tablecmds.c	2009-11-04 21:24:23.000000000 +0900
--- work/src/backend/commands/tablecmds.c	2009-11-13 12:04:10.405853692 +0900
*************** CreateFKCheckTrigger(RangeVar *myRel, Co
*** 5482,5488 ****
  	fk_trigger->constrrel = fkconstraint->pktable;
  	fk_trigger->args = NIL;
  
! 	(void) CreateTrigger(fk_trigger, constraintOid, indexOid,
  						 "RI_ConstraintTrigger", false);
  
  	/* Make changes-so-far visible */
--- 5482,5488 ----
  	fk_trigger->constrrel = fkconstraint->pktable;
  	fk_trigger->args = NIL;
  
! 	(void) CreateTrigger(fk_trigger, NULL, constraintOid, indexOid,
  						 "RI_ConstraintTrigger", false);
  
  	/* Make changes-so-far visible */
*************** createForeignKeyTriggers(Relation rel, C
*** 5523,5528 ****
--- 5523,5529 ----
  	fk_trigger = makeNode(CreateTrigStmt);
  	fk_trigger->trigname = fkconstraint->conname;
  	fk_trigger->relation = fkconstraint->pktable;
+ 	fk_trigger->whenClause = NULL;
  	fk_trigger->before = false;
  	fk_trigger->row = true;
  	fk_trigger->events = TRIGGER_TYPE_DELETE;
*************** createForeignKeyTriggers(Relation rel, C
*** 5563,5569 ****
  	}
  	fk_trigger->args = NIL;
  
! 	(void) CreateTrigger(fk_trigger, constraintOid, indexOid,
  						 "RI_ConstraintTrigger", false);
  
  	/* Make changes-so-far visible */
--- 5564,5570 ----
  	}
  	fk_trigger->args = NIL;
  
! 	(void) CreateTrigger(fk_trigger, NULL, constraintOid, indexOid,
  						 "RI_ConstraintTrigger", false);
  
  	/* Make changes-so-far visible */
*************** createForeignKeyTriggers(Relation rel, C
*** 5576,5581 ****
--- 5577,5583 ----
  	fk_trigger = makeNode(CreateTrigStmt);
  	fk_trigger->trigname = fkconstraint->conname;
  	fk_trigger->relation = fkconstraint->pktable;
+ 	fk_trigger->whenClause = NULL;
  	fk_trigger->before = false;
  	fk_trigger->row = true;
  	fk_trigger->events = TRIGGER_TYPE_UPDATE;
*************** createForeignKeyTriggers(Relation rel, C
*** 5616,5622 ****
  	}
  	fk_trigger->args = NIL;
  
! 	(void) CreateTrigger(fk_trigger, constraintOid, indexOid,
  						 "RI_ConstraintTrigger", false);
  }
  
--- 5618,5624 ----
  	}
  	fk_trigger->args = NIL;
  
! 	(void) CreateTrigger(fk_trigger, NULL, constraintOid, indexOid,
  						 "RI_ConstraintTrigger", false);
  }
  
diff -cprN head/src/backend/commands/trigger.c work/src/backend/commands/trigger.c
*** head/src/backend/commands/trigger.c	2009-10-28 05:14:27.000000000 +0900
--- work/src/backend/commands/trigger.c	2009-11-13 12:17:03.294907695 +0900
***************
*** 32,41 ****
--- 32,43 ----
  #include "miscadmin.h"
  #include "nodes/bitmapset.h"
  #include "nodes/makefuncs.h"
+ #include "parser/parse_clause.h"
  #include "parser/parse_func.h"
  #include "parser/parse_relation.h"
  #include "parser/parsetree.h"
  #include "pgstat.h"
+ #include "rewrite/rewriteManip.h"
  #include "storage/bufmgr.h"
  #include "tcop/utility.h"
  #include "utils/acl.h"
*************** static HeapTuple GetTupleForTrigger(ESta
*** 66,78 ****
  				   ItemPointer tid,
  				   TupleTableSlot **newSlot);
  static bool TriggerEnabled(Trigger *trigger, TriggerEvent event,
! 						   Bitmapset *modifiedCols);
  static HeapTuple ExecCallTriggerFunc(TriggerData *trigdata,
  					int tgindx,
  					FmgrInfo *finfo,
  					Instrumentation *instr,
  					MemoryContext per_tuple_context);
! static void AfterTriggerSaveEvent(ResultRelInfo *relinfo, int event,
  					  bool row_trigger, HeapTuple oldtup, HeapTuple newtup,
  					  List *recheckIndexes, Bitmapset *modifiedCols);
  
--- 68,81 ----
  				   ItemPointer tid,
  				   TupleTableSlot **newSlot);
  static bool TriggerEnabled(Trigger *trigger, TriggerEvent event,
! 					Bitmapset *modifiedCols, EState *estate,
! 					TupleDesc tupdesc, HeapTuple oldtup, HeapTuple newtup);
  static HeapTuple ExecCallTriggerFunc(TriggerData *trigdata,
  					int tgindx,
  					FmgrInfo *finfo,
  					Instrumentation *instr,
  					MemoryContext per_tuple_context);
! static void AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo, int event,
  					  bool row_trigger, HeapTuple oldtup, HeapTuple newtup,
  					  List *recheckIndexes, Bitmapset *modifiedCols);
  
*************** static void AfterTriggerSaveEvent(Result
*** 101,107 ****
   * but a foreign-key constraint.  This is a kluge for backwards compatibility.
   */
  Oid
! CreateTrigger(CreateTrigStmt *stmt,
  			  Oid constraintOid, Oid indexOid, const char *prefix,
  			  bool checkPermissions)
  {
--- 104,110 ----
   * but a foreign-key constraint.  This is a kluge for backwards compatibility.
   */
  Oid
! CreateTrigger(CreateTrigStmt *stmt, const char *queryString,
  			  Oid constraintOid, Oid indexOid, const char *prefix,
  			  bool checkPermissions)
  {
*************** CreateTrigger(CreateTrigStmt *stmt,
*** 128,133 ****
--- 131,137 ----
  	Oid			constrrelid = InvalidOid;
  	ObjectAddress myself,
  				referenced;
+ 	char	   *qual;
  
  	rel = heap_openrv(stmt->relation, AccessExclusiveLock);
  
*************** CreateTrigger(CreateTrigStmt *stmt,
*** 224,229 ****
--- 228,295 ----
  		return InvalidOid;
  	}
  
+ 	if (stmt->whenClause == NULL)
+ 		qual = NULL;
+ 	else
+ 	{
+ 		ParseState	   *pstate;
+ 		Node		   *whenClause;
+ 		int				num_rte;
+ 
+ 		/* Set up pstate */
+ 		pstate = make_parsestate(NULL);
+ 		pstate->p_sourcetext = queryString;
+ 
+ 		/*
+ 		 * NOTE: 'OLD' must always have a varno equal to 1 and 'NEW' equal to 2.
+ 		 * Set up their RTEs in the main pstate for use in parsing the rule
+ 		 * qualification.
+ 		 */
+ 		if (TRIGGER_FOR_ROW(tgtype))
+ 		{
+ 			RangeTblEntry *rte;
+ 
+ 			if ((TRIGGER_FOR_DELETE(tgtype) || TRIGGER_FOR_UPDATE(tgtype)) &&
+ 				!TRIGGER_FOR_INSERT(tgtype))
+ 			{
+ 				rte = addRangeTableEntryForRelation(pstate, rel,
+ 										makeAlias("old", NIL), false, false);
+ 				rte->requiredPerms = 0;
+ 				addRTEtoQuery(pstate, rte, false, true, true);
+ 			}
+ 
+ 			if ((TRIGGER_FOR_INSERT(tgtype) || TRIGGER_FOR_UPDATE(tgtype)) &&
+ 				!TRIGGER_FOR_DELETE(tgtype))
+ 			{
+ 				rte = addRangeTableEntryForRelation(pstate, rel,
+ 										makeAlias("new", NIL), false, false);
+ 				rte->requiredPerms = 0;
+ 				addRTEtoQuery(pstate, rte, false, true, true);
+ 			}
+ 		}
+ 		num_rte = list_length(pstate->p_rtable);
+ 
+ 		/* take care of the when clause */
+ 		whenClause = transformWhereClause(pstate,
+ 			(Node *) copyObject(stmt->whenClause), "WHEN");
+ 
+ 		if (list_length(pstate->p_rtable) != num_rte)
+ 			ereport(ERROR,
+ 				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ 				 errmsg("trigger WHEN condition cannot contain references to other relations")));
+ 
+ 		/*
+ 		 * adjust varno of NEW reference to PRS2_NEW_VARNO.
+ 		 * XXX: Are there any better ways?
+ 		 */
+ 		if (num_rte == 1 && TRIGGER_FOR_INSERT(tgtype))
+ 			ChangeVarNodes(whenClause, 1, PRS2_NEW_VARNO, 0);
+ 
+ 		free_parsestate(pstate);
+ 
+ 		qual = nodeToString(whenClause);
+ 	}
+ 
  	/*
  	 * Generate the trigger's OID now, so that we can use it in the name if
  	 * needed.
*************** CreateTrigger(CreateTrigStmt *stmt,
*** 387,392 ****
--- 453,469 ----
  	tgattr = buildint2vector(columns, ncolumns);
  	values[Anum_pg_trigger_tgattr - 1] = PointerGetDatum(tgattr);
  
+ 	/* set tgqual if trigger has WHEN clause */
+ 	if (qual)
+ 	{
+ 		values[Anum_pg_trigger_tgqual - 1] = CStringGetTextDatum(qual);
+ 	}
+ 	else
+ 	{
+ 		values[Anum_pg_trigger_tgqual - 1] = InvalidOid;
+ 		nulls[Anum_pg_trigger_tgqual - 1] = true;
+ 	}
+ 
  	tuple = heap_form_tuple(tgrel->rd_att, values, nulls);
  
  	/* force tuple to have the desired OID */
*************** RelationBuildTriggers(Relation relation)
*** 1184,1189 ****
--- 1261,1268 ----
  	{
  		Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(htup);
  		Trigger    *build;
+ 		Datum		qual_datum;
+ 		bool		isnull;
  
  		if (numtrigs >= maxtrigs)
  		{
*************** RelationBuildTriggers(Relation relation)
*** 1238,1243 ****
--- 1317,1338 ----
  		else
  			build->tgargs = NULL;
  
+ 		qual_datum = heap_getattr(htup, Anum_pg_trigger_tgqual,
+ 								  RelationGetDescr(tgrel), &isnull);
+ 		if (!isnull)
+ 		{
+ 			char *qual_str = TextDatumGetCString(qual_datum);
+ 
+ 			/* Fix references to OLD and NEW to INNER and OUTER */
+ 			build->tgqual = stringToNode(qual_str);
+ 			ChangeVarNodes(build->tgqual, PRS2_OLD_VARNO, INNER, 0);
+ 			ChangeVarNodes(build->tgqual, PRS2_NEW_VARNO, OUTER, 0);
+ 
+ 			pfree(qual_str);
+ 		}
+ 		else
+ 			build->tgqual = NULL;
+ 
  		numtrigs++;
  	}
  
*************** CopyTriggerDesc(TriggerDesc *trigdesc)
*** 1396,1401 ****
--- 1491,1497 ----
  				newargs[j] = pstrdup(trigger->tgargs[j]);
  			trigger->tgargs = newargs;
  		}
+ 		trigger->tgqual = copyObject(trigger->tgqual);
  		trigger++;
  	}
  
*************** ExecBSInsertTriggers(EState *estate, Res
*** 1687,1693 ****
  		Trigger    *trigger = &trigdesc->triggers[tgindx[i]];
  		HeapTuple	newtuple;
  
! 		if (!TriggerEnabled(trigger, LocTriggerData.tg_event, NULL))
  			continue;
  
  		LocTriggerData.tg_trigger = trigger;
--- 1783,1790 ----
  		Trigger    *trigger = &trigdesc->triggers[tgindx[i]];
  		HeapTuple	newtuple;
  
! 		if (!TriggerEnabled(trigger, LocTriggerData.tg_event, NULL,
! 							NULL, NULL, NULL, NULL))
  			continue;
  
  		LocTriggerData.tg_trigger = trigger;
*************** ExecASInsertTriggers(EState *estate, Res
*** 1710,1716 ****
  	TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
  
  	if (trigdesc && trigdesc->n_after_statement[TRIGGER_EVENT_INSERT] > 0)
! 		AfterTriggerSaveEvent(relinfo, TRIGGER_EVENT_INSERT,
  							  false, NULL, NULL, NIL, NULL);
  }
  
--- 1807,1813 ----
  	TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
  
  	if (trigdesc && trigdesc->n_after_statement[TRIGGER_EVENT_INSERT] > 0)
! 		AfterTriggerSaveEvent(estate, relinfo, TRIGGER_EVENT_INSERT,
  							  false, NULL, NULL, NIL, NULL);
  }
  
*************** ExecBRInsertTriggers(EState *estate, Res
*** 1737,1743 ****
  	{
  		Trigger    *trigger = &trigdesc->triggers[tgindx[i]];
  
! 		if (!TriggerEnabled(trigger, LocTriggerData.tg_event, NULL))
  			continue;
  
  		LocTriggerData.tg_trigtuple = oldtuple = newtuple;
--- 1834,1842 ----
  	{
  		Trigger    *trigger = &trigdesc->triggers[tgindx[i]];
  
! 		if (!TriggerEnabled(trigger, LocTriggerData.tg_event, NULL,
! 							estate, RelationGetDescr(relinfo->ri_RelationDesc),
! 							NULL, newtuple))
  			continue;
  
  		LocTriggerData.tg_trigtuple = oldtuple = newtuple;
*************** ExecARInsertTriggers(EState *estate, Res
*** 1763,1769 ****
  	TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
  
  	if (trigdesc && trigdesc->n_after_row[TRIGGER_EVENT_INSERT] > 0)
! 		AfterTriggerSaveEvent(relinfo, TRIGGER_EVENT_INSERT,
  							  true, NULL, trigtuple, recheckIndexes, NULL);
  }
  
--- 1862,1868 ----
  	TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
  
  	if (trigdesc && trigdesc->n_after_row[TRIGGER_EVENT_INSERT] > 0)
! 		AfterTriggerSaveEvent(estate, relinfo, TRIGGER_EVENT_INSERT,
  							  true, NULL, trigtuple, recheckIndexes, NULL);
  }
  
*************** ExecBSDeleteTriggers(EState *estate, Res
*** 1800,1806 ****
  		Trigger    *trigger = &trigdesc->triggers[tgindx[i]];
  		HeapTuple	newtuple;
  
! 		if (!TriggerEnabled(trigger, LocTriggerData.tg_event, NULL))
  			continue;
  
  		LocTriggerData.tg_trigger = trigger;
--- 1899,1906 ----
  		Trigger    *trigger = &trigdesc->triggers[tgindx[i]];
  		HeapTuple	newtuple;
  
! 		if (!TriggerEnabled(trigger, LocTriggerData.tg_event, NULL,
! 							NULL, NULL, NULL, NULL))
  			continue;
  
  		LocTriggerData.tg_trigger = trigger;
*************** ExecASDeleteTriggers(EState *estate, Res
*** 1823,1829 ****
  	TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
  
  	if (trigdesc && trigdesc->n_after_statement[TRIGGER_EVENT_DELETE] > 0)
! 		AfterTriggerSaveEvent(relinfo, TRIGGER_EVENT_DELETE,
  							  false, NULL, NULL, NIL, NULL);
  }
  
--- 1923,1929 ----
  	TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
  
  	if (trigdesc && trigdesc->n_after_statement[TRIGGER_EVENT_DELETE] > 0)
! 		AfterTriggerSaveEvent(estate, relinfo, TRIGGER_EVENT_DELETE,
  							  false, NULL, NULL, NIL, NULL);
  }
  
*************** ExecBRDeleteTriggers(EState *estate, EPQ
*** 1858,1864 ****
  	{
  		Trigger    *trigger = &trigdesc->triggers[tgindx[i]];
  
! 		if (!TriggerEnabled(trigger, LocTriggerData.tg_event, NULL))
  			continue;
  
  		LocTriggerData.tg_trigtuple = trigtuple;
--- 1958,1966 ----
  	{
  		Trigger    *trigger = &trigdesc->triggers[tgindx[i]];
  
! 		if (!TriggerEnabled(trigger, LocTriggerData.tg_event, NULL,
! 							estate, RelationGetDescr(relinfo->ri_RelationDesc),
! 							trigtuple, NULL))
  			continue;
  
  		LocTriggerData.tg_trigtuple = trigtuple;
*************** ExecARDeleteTriggers(EState *estate, Res
*** 1893,1899 ****
  		HeapTuple	trigtuple = GetTupleForTrigger(estate, NULL, relinfo,
  												   tupleid, NULL);
  
! 		AfterTriggerSaveEvent(relinfo, TRIGGER_EVENT_DELETE,
  							  true, trigtuple, NULL, NIL, NULL);
  		heap_freetuple(trigtuple);
  	}
--- 1995,2001 ----
  		HeapTuple	trigtuple = GetTupleForTrigger(estate, NULL, relinfo,
  												   tupleid, NULL);
  
! 		AfterTriggerSaveEvent(estate, relinfo, TRIGGER_EVENT_DELETE,
  							  true, trigtuple, NULL, NIL, NULL);
  		heap_freetuple(trigtuple);
  	}
*************** ExecBSUpdateTriggers(EState *estate, Res
*** 1935,1941 ****
  		Trigger    *trigger = &trigdesc->triggers[tgindx[i]];
  		HeapTuple	newtuple;
  
! 		if (!TriggerEnabled(trigger, LocTriggerData.tg_event, modifiedCols))
  			continue;
  
  		LocTriggerData.tg_trigger = trigger;
--- 2037,2044 ----
  		Trigger    *trigger = &trigdesc->triggers[tgindx[i]];
  		HeapTuple	newtuple;
  
! 		if (!TriggerEnabled(trigger, LocTriggerData.tg_event, modifiedCols,
! 							NULL, NULL, NULL, NULL))
  			continue;
  
  		LocTriggerData.tg_trigger = trigger;
*************** ExecASUpdateTriggers(EState *estate, Res
*** 1958,1964 ****
  	TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
  
  	if (trigdesc && trigdesc->n_after_statement[TRIGGER_EVENT_UPDATE] > 0)
! 		AfterTriggerSaveEvent(relinfo, TRIGGER_EVENT_UPDATE,
  							  false, NULL, NULL, NIL,
  							  GetModifiedColumns(relinfo, estate));
  }
--- 2061,2067 ----
  	TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
  
  	if (trigdesc && trigdesc->n_after_statement[TRIGGER_EVENT_UPDATE] > 0)
! 		AfterTriggerSaveEvent(estate, relinfo, TRIGGER_EVENT_UPDATE,
  							  false, NULL, NULL, NIL,
  							  GetModifiedColumns(relinfo, estate));
  }
*************** ExecBRUpdateTriggers(EState *estate, EPQ
*** 2003,2009 ****
  	{
  		Trigger    *trigger = &trigdesc->triggers[tgindx[i]];
  
! 		if (!TriggerEnabled(trigger, LocTriggerData.tg_event, modifiedCols))
  			continue;
  
  		LocTriggerData.tg_trigtuple = trigtuple;
--- 2106,2114 ----
  	{
  		Trigger    *trigger = &trigdesc->triggers[tgindx[i]];
  
! 		if (!TriggerEnabled(trigger, LocTriggerData.tg_event, modifiedCols,
! 							estate, RelationGetDescr(relinfo->ri_RelationDesc),
! 							trigtuple, newtuple))
  			continue;
  
  		LocTriggerData.tg_trigtuple = trigtuple;
*************** ExecARUpdateTriggers(EState *estate, Res
*** 2037,2043 ****
  		HeapTuple	trigtuple = GetTupleForTrigger(estate, NULL, relinfo,
  												   tupleid, NULL);
  
! 		AfterTriggerSaveEvent(relinfo, TRIGGER_EVENT_UPDATE,
  							  true, trigtuple, newtuple, recheckIndexes,
  							  GetModifiedColumns(relinfo, estate));
  		heap_freetuple(trigtuple);
--- 2142,2148 ----
  		HeapTuple	trigtuple = GetTupleForTrigger(estate, NULL, relinfo,
  												   tupleid, NULL);
  
! 		AfterTriggerSaveEvent(estate, relinfo, TRIGGER_EVENT_UPDATE,
  							  true, trigtuple, newtuple, recheckIndexes,
  							  GetModifiedColumns(relinfo, estate));
  		heap_freetuple(trigtuple);
*************** ExecBSTruncateTriggers(EState *estate, R
*** 2077,2083 ****
  		Trigger    *trigger = &trigdesc->triggers[tgindx[i]];
  		HeapTuple	newtuple;
  
! 		if (!TriggerEnabled(trigger, LocTriggerData.tg_event, NULL))
  			continue;
  
  		LocTriggerData.tg_trigger = trigger;
--- 2182,2189 ----
  		Trigger    *trigger = &trigdesc->triggers[tgindx[i]];
  		HeapTuple	newtuple;
  
! 		if (!TriggerEnabled(trigger, LocTriggerData.tg_event, NULL,
! 							NULL, NULL, NULL, NULL))
  			continue;
  
  		LocTriggerData.tg_trigger = trigger;
*************** ExecASTruncateTriggers(EState *estate, R
*** 2100,2106 ****
  	TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
  
  	if (trigdesc && trigdesc->n_after_statement[TRIGGER_EVENT_TRUNCATE] > 0)
! 		AfterTriggerSaveEvent(relinfo, TRIGGER_EVENT_TRUNCATE,
  							  false, NULL, NULL, NIL, NULL);
  }
  
--- 2206,2212 ----
  	TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
  
  	if (trigdesc && trigdesc->n_after_statement[TRIGGER_EVENT_TRUNCATE] > 0)
! 		AfterTriggerSaveEvent(estate, relinfo, TRIGGER_EVENT_TRUNCATE,
  							  false, NULL, NULL, NIL, NULL);
  }
  
*************** ltrmark:;
*** 2219,2225 ****
   * Is trigger enabled to fire?
   */
  static bool
! TriggerEnabled(Trigger *trigger, TriggerEvent event, Bitmapset *modifiedCols)
  {
  	/* Check replication-role-dependent enable state */
  	if (SessionReplicationRole == SESSION_REPLICATION_ROLE_REPLICA)
--- 2325,2332 ----
   * Is trigger enabled to fire?
   */
  static bool
! TriggerEnabled(Trigger *trigger, TriggerEvent event, Bitmapset *modifiedCols,
! 		EState *estate, TupleDesc tupdesc, HeapTuple oldtup, HeapTuple newtup)
  {
  	/* Check replication-role-dependent enable state */
  	if (SessionReplicationRole == SESSION_REPLICATION_ROLE_REPLICA)
*************** TriggerEnabled(Trigger *trigger, Trigger
*** 2258,2263 ****
--- 2365,2419 ----
  			return false;
  	}
  
+ 	/* Check for WHEN clause */
+ 	if (trigger->tgqual)
+ 	{
+ 		ExprContext		   *econtext;		
+ 		List			   *predicate;
+ 		TupleTableSlot	   *oldslot = NULL;
+ 		TupleTableSlot	   *newslot = NULL;
+ 
+ 		econtext = GetPerTupleExprContext(estate);
+ 
+ 		predicate = list_make1(ExecPrepareExpr((Expr *) trigger->tgqual, estate));
+ 
+ 		/* set OLD and NEW tuples into tupleslots */
+ 		if (TRIGGER_FIRED_FOR_ROW(event))
+ 		{
+ 			Assert(tupdesc != NULL);
+ 
+ 			if (TRIGGER_FIRED_BY_DELETE(event) || TRIGGER_FIRED_BY_UPDATE(event))
+ 			{
+ 				Assert(oldtup != NULL);
+ 
+ 				if (estate->es_trig_oldtup_slot == NULL)
+ 					estate->es_trig_oldtup_slot = ExecInitExtraTupleSlot(estate);
+ 
+ 				oldslot = estate->es_trig_oldtup_slot;
+ 				if (oldslot->tts_tupleDescriptor != tupdesc)
+ 					ExecSetSlotDescriptor(oldslot, tupdesc);
+ 				if (oldslot->tts_tuple != oldtup)
+ 					ExecStoreTuple(oldtup, oldslot, InvalidBuffer, false);
+ 			}
+ 			if (TRIGGER_FIRED_BY_INSERT(event) || TRIGGER_FIRED_BY_UPDATE(event))
+ 			{
+ 				Assert(newtup != NULL);
+ 				Assert(estate->es_trig_tuple_slot != NULL);
+ 
+ 				newslot = estate->es_trig_tuple_slot;
+ 				if (newslot->tts_tupleDescriptor != tupdesc)
+ 					ExecSetSlotDescriptor(newslot, tupdesc);
+ 				if (newslot->tts_tuple != newtup)
+ 					ExecStoreTuple(newtup, newslot, InvalidBuffer, false);
+ 			}
+ 		}
+ 
+ 		econtext->ecxt_innertuple = oldslot;
+ 		econtext->ecxt_outertuple = newslot;
+ 		if (!ExecQual(predicate, econtext, false))
+ 			return false;
+ 	}
+ 
  	return true;
  }
  
*************** AfterTriggerPendingOnRel(Oid relid)
*** 3883,3889 ****
   * ----------
   */
  static void
! AfterTriggerSaveEvent(ResultRelInfo *relinfo, int event, bool row_trigger,
  					  HeapTuple oldtup, HeapTuple newtup,
  					  List *recheckIndexes, Bitmapset *modifiedCols)
  {
--- 4039,4045 ----
   * ----------
   */
  static void
! AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo, int event, bool row_trigger,
  					  HeapTuple oldtup, HeapTuple newtup,
  					  List *recheckIndexes, Bitmapset *modifiedCols)
  {
*************** AfterTriggerSaveEvent(ResultRelInfo *rel
*** 3993,3999 ****
  	{
  		Trigger    *trigger = &trigdesc->triggers[tgindx[i]];
  
! 		if (!TriggerEnabled(trigger, event, modifiedCols))
  			continue;
  
  		/*
--- 4149,4157 ----
  	{
  		Trigger    *trigger = &trigdesc->triggers[tgindx[i]];
  
! 		if (!TriggerEnabled(trigger, event, modifiedCols,
! 							estate, RelationGetDescr(relinfo->ri_RelationDesc),
! 							oldtup, newtup))
  			continue;
  
  		/*
diff -cprN head/src/backend/executor/execMain.c work/src/backend/executor/execMain.c
*** head/src/backend/executor/execMain.c	2009-10-26 11:26:29.000000000 +0900
--- work/src/backend/executor/execMain.c	2009-11-13 12:04:10.406801346 +0900
*************** InitPlan(QueryDesc *queryDesc, int eflag
*** 752,757 ****
--- 752,758 ----
  	 */
  	estate->es_tupleTable = NIL;
  	estate->es_trig_tuple_slot = NULL;
+ 	estate->es_trig_oldtup_slot = NULL;
  
  	/* mark EvalPlanQual not active */
  	estate->es_epqTuple = NULL;
diff -cprN head/src/backend/executor/execQual.c work/src/backend/executor/execQual.c
*** head/src/backend/executor/execQual.c	2009-11-05 07:26:05.000000000 +0900
--- work/src/backend/executor/execQual.c	2009-11-13 12:04:10.407841338 +0900
*************** static Datum ExecEvalArrayCoerceExpr(Arr
*** 170,175 ****
--- 170,176 ----
  						bool *isNull, ExprDoneCond *isDone);
  static Datum ExecEvalCurrentOfExpr(ExprState *exprstate, ExprContext *econtext,
  					  bool *isNull, ExprDoneCond *isDone);
+ static TupleTableSlot *get_slot(ExprContext *econtext, Index varno);
  
  
  /* ----------------------------------------------------------------
*************** ExecEvalVar(ExprState *exprstate, ExprCo
*** 501,523 ****
  	 */
  	attnum = variable->varattno;
  
! 	switch (variable->varno)
! 	{
! 		case INNER:				/* get the tuple from the inner node */
! 			slot = econtext->ecxt_innertuple;
! 			Assert(attnum > 0);
! 			break;
! 
! 		case OUTER:				/* get the tuple from the outer node */
! 			slot = econtext->ecxt_outertuple;
! 			Assert(attnum > 0);
! 			break;
! 
! 		default:				/* get the tuple from the relation being
! 								 * scanned */
! 			slot = econtext->ecxt_scantuple;
! 			break;
! 	}
  
  	if (attnum != InvalidAttrNumber)
  	{
--- 502,508 ----
  	 */
  	attnum = variable->varattno;
  
! 	slot = get_slot(econtext, variable->varno);
  
  	if (attnum != InvalidAttrNumber)
  	{
*************** ExecEvalScalarVar(ExprState *exprstate, 
*** 682,702 ****
  		*isDone = ExprSingleResult;
  
  	/* Get the input slot and attribute number we want */
! 	switch (variable->varno)
! 	{
! 		case INNER:				/* get the tuple from the inner node */
! 			slot = econtext->ecxt_innertuple;
! 			break;
! 
! 		case OUTER:				/* get the tuple from the outer node */
! 			slot = econtext->ecxt_outertuple;
! 			break;
! 
! 		default:				/* get the tuple from the relation being
! 								 * scanned */
! 			slot = econtext->ecxt_scantuple;
! 			break;
! 	}
  
  	attnum = variable->varattno;
  
--- 667,673 ----
  		*isDone = ExprSingleResult;
  
  	/* Get the input slot and attribute number we want */
! 	slot = get_slot(econtext, variable->varno);
  
  	attnum = variable->varattno;
  
*************** ExecEvalWholeRowVar(ExprState *exprstate
*** 715,721 ****
  					bool *isNull, ExprDoneCond *isDone)
  {
  	Var		   *variable = (Var *) exprstate->expr;
! 	TupleTableSlot *slot = econtext->ecxt_scantuple;
  	HeapTuple	tuple;
  	TupleDesc	tupleDesc;
  	HeapTupleHeader dtuple;
--- 686,692 ----
  					bool *isNull, ExprDoneCond *isDone)
  {
  	Var		   *variable = (Var *) exprstate->expr;
! 	TupleTableSlot *slot = get_slot(econtext, variable->varno);
  	HeapTuple	tuple;
  	TupleDesc	tupleDesc;
  	HeapTupleHeader dtuple;
*************** ExecEvalWholeRowSlow(ExprState *exprstat
*** 766,772 ****
  					 bool *isNull, ExprDoneCond *isDone)
  {
  	Var		   *variable = (Var *) exprstate->expr;
! 	TupleTableSlot *slot = econtext->ecxt_scantuple;
  	HeapTuple	tuple;
  	TupleDesc	var_tupdesc;
  	HeapTupleHeader dtuple;
--- 737,743 ----
  					 bool *isNull, ExprDoneCond *isDone)
  {
  	Var		   *variable = (Var *) exprstate->expr;
! 	TupleTableSlot *slot = get_slot(econtext, variable->varno);
  	HeapTuple	tuple;
  	TupleDesc	var_tupdesc;
  	HeapTupleHeader dtuple;
*************** ExecProject(ProjectionInfo *projInfo, Ex
*** 5179,5181 ****
--- 5150,5166 ----
  	 */
  	return ExecStoreVirtualTuple(slot);
  }
+ 
+ static TupleTableSlot *
+ get_slot(ExprContext *econtext, Index varno)
+ {
+ 	switch (varno)
+ 	{
+ 		case INNER:	/* get the tuple from the inner node */
+ 			return econtext->ecxt_innertuple;
+ 		case OUTER:	/* get the tuple from the outer node */
+ 			return econtext->ecxt_outertuple;
+ 		default:	/* get the tuple from the relation being scanned */
+ 			return econtext->ecxt_scantuple;
+ 	}
+ }
diff -cprN head/src/backend/executor/execUtils.c work/src/backend/executor/execUtils.c
*** head/src/backend/executor/execUtils.c	2009-10-26 11:26:29.000000000 +0900
--- work/src/backend/executor/execUtils.c	2009-11-13 12:04:10.408874678 +0900
*************** CreateExecutorState(void)
*** 117,122 ****
--- 117,123 ----
  
  	estate->es_trig_target_relations = NIL;
  	estate->es_trig_tuple_slot = NULL;
+ 	estate->es_trig_oldtup_slot = NULL;
  
  	estate->es_param_list_info = NULL;
  	estate->es_param_exec_vals = NULL;
diff -cprN head/src/backend/executor/nodeModifyTable.c work/src/backend/executor/nodeModifyTable.c
*** head/src/backend/executor/nodeModifyTable.c	2009-10-26 11:26:31.000000000 +0900
--- work/src/backend/executor/nodeModifyTable.c	2009-11-13 12:04:10.408874678 +0900
*************** ExecModifyTable(ModifyTableState *node)
*** 752,757 ****
--- 752,761 ----
  			estate->es_result_relation_info = NULL;
  			return slot;
  		}
+ 
+ 		/* clear slot used by trigger to avoid dangling-reference to tuple */
+ 		if (estate->es_trig_oldtup_slot)
+ 			ExecClearTuple(estate->es_trig_oldtup_slot);
  	}
  
  	/* Reset es_result_relation_info before exiting */
diff -cprN head/src/backend/nodes/copyfuncs.c work/src/backend/nodes/copyfuncs.c
*** head/src/backend/nodes/copyfuncs.c	2009-10-28 23:55:38.000000000 +0900
--- work/src/backend/nodes/copyfuncs.c	2009-11-13 12:04:10.409885877 +0900
*************** _copyCreateTrigStmt(CreateTrigStmt *from
*** 3183,3188 ****
--- 3183,3189 ----
  	COPY_SCALAR_FIELD(row);
  	COPY_SCALAR_FIELD(events);
  	COPY_NODE_FIELD(columns);
+ 	COPY_NODE_FIELD(whenClause);
  	COPY_SCALAR_FIELD(isconstraint);
  	COPY_SCALAR_FIELD(deferrable);
  	COPY_SCALAR_FIELD(initdeferred);
diff -cprN head/src/backend/nodes/equalfuncs.c work/src/backend/nodes/equalfuncs.c
*** head/src/backend/nodes/equalfuncs.c	2009-10-28 23:55:38.000000000 +0900
--- work/src/backend/nodes/equalfuncs.c	2009-11-13 12:04:10.409885877 +0900
*************** _equalCreateTrigStmt(CreateTrigStmt *a, 
*** 1673,1678 ****
--- 1673,1679 ----
  	COMPARE_SCALAR_FIELD(row);
  	COMPARE_SCALAR_FIELD(events);
  	COMPARE_NODE_FIELD(columns);
+ 	COMPARE_NODE_FIELD(whenClause);
  	COMPARE_SCALAR_FIELD(isconstraint);
  	COMPARE_SCALAR_FIELD(deferrable);
  	COMPARE_SCALAR_FIELD(initdeferred);
diff -cprN head/src/backend/parser/gram.y work/src/backend/parser/gram.y
*** head/src/backend/parser/gram.y	2009-11-12 05:31:26.000000000 +0900
--- work/src/backend/parser/gram.y	2009-11-13 12:10:46.729862473 +0900
*************** static TypeName *TableFuncTypeName(List 
*** 248,253 ****
--- 248,254 ----
  
  %type <list>	TriggerEvents TriggerOneEvent
  %type <value>	TriggerFuncArg
+ %type <node>	TriggerWhen
  
  %type <str>		copy_file_name
  				database_name access_method_clause access_method attr_name
*************** AlterUserMappingStmt: ALTER USER MAPPING
*** 3226,3239 ****
  
  CreateTrigStmt:
  			CREATE TRIGGER name TriggerActionTime TriggerEvents ON
! 			qualified_name TriggerForSpec EXECUTE PROCEDURE
! 			func_name '(' TriggerFuncArgs ')'
  				{
  					CreateTrigStmt *n = makeNode(CreateTrigStmt);
  					n->trigname = $3;
  					n->relation = $7;
! 					n->funcname = $11;
! 					n->args = $13;
  					n->before = $4;
  					n->row = $8;
  					n->events = intVal(linitial($5));
--- 3227,3241 ----
  
  CreateTrigStmt:
  			CREATE TRIGGER name TriggerActionTime TriggerEvents ON
! 			qualified_name TriggerForSpec
! 			TriggerWhen EXECUTE PROCEDURE func_name '(' TriggerFuncArgs ')'
  				{
  					CreateTrigStmt *n = makeNode(CreateTrigStmt);
  					n->trigname = $3;
  					n->relation = $7;
! 					n->whenClause = $9;
! 					n->funcname = $12;
! 					n->args = $14;
  					n->before = $4;
  					n->row = $8;
  					n->events = intVal(linitial($5));
*************** TriggerFuncArg:
*** 3354,3359 ****
--- 3356,3366 ----
  			| ColId									{ $$ = makeString($1); }
  		;
  
+ TriggerWhen:
+ 			WHEN '(' a_expr ')'						{ $$ = $3; }
+ 			| /*EMPTY*/								{ $$ = NULL; }
+ 		;
+ 
  OptConstrFromTable:
  			FROM qualified_name						{ $$ = $2; }
  			| /*EMPTY*/								{ $$ = NULL; }
diff -cprN head/src/backend/tcop/utility.c work/src/backend/tcop/utility.c
*** head/src/backend/tcop/utility.c	2009-10-26 11:26:40.000000000 +0900
--- work/src/backend/tcop/utility.c	2009-11-13 12:04:10.412858953 +0900
*************** ProcessUtility(Node *parsetree,
*** 939,945 ****
  			break;
  
  		case T_CreateTrigStmt:
! 			CreateTrigger((CreateTrigStmt *) parsetree,
  						  InvalidOid, InvalidOid, NULL, true);
  			break;
  
--- 939,945 ----
  			break;
  
  		case T_CreateTrigStmt:
! 			CreateTrigger((CreateTrigStmt *) parsetree, queryString,
  						  InvalidOid, InvalidOid, NULL, true);
  			break;
  
diff -cprN head/src/backend/utils/adt/ruleutils.c work/src/backend/utils/adt/ruleutils.c
*** head/src/backend/utils/adt/ruleutils.c	2009-11-06 08:24:25.000000000 +0900
--- work/src/backend/utils/adt/ruleutils.c	2009-11-13 12:17:03.352877598 +0900
***************
*** 42,47 ****
--- 42,48 ----
  #include "parser/keywords.h"
  #include "parser/parse_func.h"
  #include "parser/parse_oper.h"
+ #include "parser/parse_relation.h"
  #include "parser/parser.h"
  #include "parser/parsetree.h"
  #include "rewrite/rewriteHandler.h"
*************** pg_get_triggerdef_worker(Oid trigid, boo
*** 487,492 ****
--- 488,495 ----
  	SysScanDesc tgscan;
  	int			findx = 0;
  	char	   *tgname;
+ 	Datum		value;
+ 	bool		isnull;
  
  	/*
  	 * Fetch the pg_trigger tuple by the Oid of the trigger
*************** pg_get_triggerdef_worker(Oid trigid, boo
*** 596,618 ****
  		appendStringInfo(&buf, "FOR EACH STATEMENT");
  	appendStringInfoString(&buf, pretty ? "\n    " : " ");
  
  	appendStringInfo(&buf, "EXECUTE PROCEDURE %s(",
  					 generate_function_name(trigrec->tgfoid, 0,
  											NIL, NULL, NULL));
  
  	if (trigrec->tgnargs > 0)
  	{
- 		bytea	   *val;
- 		bool		isnull;
  		char	   *p;
  		int			i;
  
! 		val = DatumGetByteaP(fastgetattr(ht_trig,
! 										 Anum_pg_trigger_tgargs,
! 										 tgrel->rd_att, &isnull));
  		if (isnull)
  			elog(ERROR, "tgargs is null for trigger %u", trigid);
! 		p = (char *) VARDATA(val);
  		for (i = 0; i < trigrec->tgnargs; i++)
  		{
  			if (i > 0)
--- 599,663 ----
  		appendStringInfo(&buf, "FOR EACH STATEMENT");
  	appendStringInfoString(&buf, pretty ? "\n    " : " ");
  
+ 	/* If the trigger has an event qualification, add it */
+ 	value = fastgetattr(ht_trig, Anum_pg_trigger_tgqual,
+ 						tgrel->rd_att, &isnull);
+ 	if (!isnull)
+ 	{
+ 		Node			   *qual;
+ 		deparse_context		context;
+ 		deparse_namespace	dpns;
+ 		RangeTblEntry	   *oldrte;
+ 		RangeTblEntry	   *newrte;
+ 
+ 		appendStringInfo(&buf, "WHEN (");
+ 
+ 		qual = stringToNode(TextDatumGetCString(value));
+ 
+ 		context.buf = &buf;
+ 		context.namespaces = list_make1(&dpns);
+ 		context.windowClause = NIL;
+ 		context.windowTList = NIL;
+ 		context.varprefix = true;
+ 		context.prettyFlags = PRETTYFLAG_PAREN | (pretty ? PRETTYFLAG_INDENT : 0);
+ 		context.indentLevel = PRETTYINDENT_STD;
+ 
+ 		/* Build a minimal NEW and OLD RTEs for the rel */
+ 		oldrte = makeNode(RangeTblEntry);
+ 		oldrte->rtekind = RTE_RELATION;
+ 		oldrte->alias = oldrte->eref = makeAlias("old", NIL);
+ 		oldrte->relid = trigrec->tgrelid;
+ 
+ 		newrte = makeNode(RangeTblEntry);
+ 		newrte->rtekind = RTE_RELATION;
+ 		newrte->alias = newrte->eref = makeAlias("new", NIL);
+ 		newrte->relid = trigrec->tgrelid;
+ 
+ 		/* Build two-elements rtable */
+ 		dpns.rtable = list_make2(oldrte, newrte);
+ 		dpns.ctes = NIL;
+ 		dpns.subplans = NIL;
+ 		dpns.outer_plan = dpns.inner_plan = NULL;
+ 
+ 		get_rule_expr(qual, &context, false);
+ 
+ 		appendStringInfo(&buf, ")%s", pretty ? "\n    " : " ");
+ 	}
+ 
  	appendStringInfo(&buf, "EXECUTE PROCEDURE %s(",
  					 generate_function_name(trigrec->tgfoid, 0,
  											NIL, NULL, NULL));
  
  	if (trigrec->tgnargs > 0)
  	{
  		char	   *p;
  		int			i;
  
! 		value = fastgetattr(ht_trig, Anum_pg_trigger_tgargs,
! 							tgrel->rd_att, &isnull);
  		if (isnull)
  			elog(ERROR, "tgargs is null for trigger %u", trigid);
! 		p = (char *) VARDATA(DatumGetByteaP(value));
  		for (i = 0; i < trigrec->tgnargs; i++)
  		{
  			if (i > 0)
diff -cprN head/src/include/catalog/pg_trigger.h work/src/include/catalog/pg_trigger.h
*** head/src/include/catalog/pg_trigger.h	2009-10-15 07:14:24.000000000 +0900
--- work/src/include/catalog/pg_trigger.h	2009-11-13 12:04:10.413811964 +0900
*************** CATALOG(pg_trigger,2620)
*** 55,60 ****
--- 55,61 ----
  	/* VARIABLE LENGTH FIELDS (note: these are not supposed to be null) */
  	int2vector	tgattr;			/* column numbers, if trigger is on columns */
  	bytea		tgargs;			/* first\000second\000tgnargs\000 */
+ 	text		tgqual;			/* WHEN clause */
  } FormData_pg_trigger;
  
  /* ----------------
*************** typedef FormData_pg_trigger *Form_pg_tri
*** 68,74 ****
   *		compiler constants for pg_trigger
   * ----------------
   */
! #define Natts_pg_trigger				15
  #define Anum_pg_trigger_tgrelid			1
  #define Anum_pg_trigger_tgname			2
  #define Anum_pg_trigger_tgfoid			3
--- 69,75 ----
   *		compiler constants for pg_trigger
   * ----------------
   */
! #define Natts_pg_trigger				16
  #define Anum_pg_trigger_tgrelid			1
  #define Anum_pg_trigger_tgname			2
  #define Anum_pg_trigger_tgfoid			3
*************** typedef FormData_pg_trigger *Form_pg_tri
*** 84,89 ****
--- 85,91 ----
  #define Anum_pg_trigger_tgnargs			13
  #define Anum_pg_trigger_tgattr			14
  #define Anum_pg_trigger_tgargs			15
+ #define Anum_pg_trigger_tgqual			16
  
  /* Bits within tgtype */
  #define TRIGGER_TYPE_ROW				(1 << 0)
diff -cprN head/src/include/commands/trigger.h work/src/include/commands/trigger.h
*** head/src/include/commands/trigger.h	2009-10-26 11:26:41.000000000 +0900
--- work/src/include/commands/trigger.h	2009-11-13 12:04:10.414837433 +0900
*************** extern PGDLLIMPORT int SessionReplicatio
*** 104,110 ****
  #define TRIGGER_FIRES_ON_REPLICA			'R'
  #define TRIGGER_DISABLED					'D'
  
! extern Oid CreateTrigger(CreateTrigStmt *stmt,
  			  Oid constraintOid, Oid indexOid, const char *prefix,
  			  bool checkPermissions);
  
--- 104,110 ----
  #define TRIGGER_FIRES_ON_REPLICA			'R'
  #define TRIGGER_DISABLED					'D'
  
! extern Oid CreateTrigger(CreateTrigStmt *stmt, const char *queryString,
  			  Oid constraintOid, Oid indexOid, const char *prefix,
  			  bool checkPermissions);
  
diff -cprN head/src/include/nodes/execnodes.h work/src/include/nodes/execnodes.h
*** head/src/include/nodes/execnodes.h	2009-10-26 11:26:41.000000000 +0900
--- work/src/include/nodes/execnodes.h	2009-11-13 12:04:10.414837433 +0900
*************** typedef struct EState
*** 345,351 ****
  
  	/* Stuff used for firing triggers: */
  	List	   *es_trig_target_relations;		/* trigger-only ResultRelInfos */
! 	TupleTableSlot *es_trig_tuple_slot; /* for trigger output tuples */
  
  	/* Parameter info: */
  	ParamListInfo es_param_list_info;	/* values of external params */
--- 345,352 ----
  
  	/* Stuff used for firing triggers: */
  	List	   *es_trig_target_relations;		/* trigger-only ResultRelInfos */
! 	TupleTableSlot *es_trig_tuple_slot;		/* for trigger output tuples */
! 	TupleTableSlot *es_trig_oldtup_slot;	/* for trigger old tuples */
  
  	/* Parameter info: */
  	ParamListInfo es_param_list_info;	/* values of external params */
diff -cprN head/src/include/nodes/parsenodes.h work/src/include/nodes/parsenodes.h
*** head/src/include/nodes/parsenodes.h	2009-11-07 06:57:57.000000000 +0900
--- work/src/include/nodes/parsenodes.h	2009-11-13 12:04:10.414837433 +0900
*************** typedef struct CreateTrigStmt
*** 1571,1576 ****
--- 1571,1577 ----
  	/* events uses the TRIGGER_TYPE bits defined in catalog/pg_trigger.h */
  	int16		events;			/* INSERT/UPDATE/DELETE/TRUNCATE */
  	List	   *columns;		/* column names, or NIL for all columns */
+ 	Node	   *whenClause;		/* qualifications */
  
  	/* The following are used for constraint triggers (RI and unique checks) */
  	bool		isconstraint;	/* This is a constraint trigger */
diff -cprN head/src/include/utils/rel.h work/src/include/utils/rel.h
*** head/src/include/utils/rel.h	2009-07-28 11:56:31.000000000 +0900
--- work/src/include/utils/rel.h	2009-11-13 12:04:10.415848373 +0900
*************** typedef struct Trigger
*** 66,71 ****
--- 66,72 ----
  	int16		tgnattr;
  	int16	   *tgattr;
  	char	  **tgargs;
+ 	Node	   *tgqual;
  } Trigger;
  
  typedef struct TriggerDesc
diff -cprN head/src/test/regress/expected/triggers.out work/src/test/regress/expected/triggers.out
*** head/src/test/regress/expected/triggers.out	2009-10-15 07:14:25.000000000 +0900
--- work/src/test/regress/expected/triggers.out	2009-11-13 12:18:25.559859777 +0900
*************** SELECT * FROM main_table ORDER BY a, b;
*** 322,327 ****
--- 322,377 ----
      |   
  (8 rows)
  
+ -- Trigger with WHEN clause
+ CREATE TRIGGER modified_a BEFORE UPDATE OF a ON main_table
+ FOR EACH ROW WHEN (OLD.a <> NEW.a) EXECUTE PROCEDURE trigger_func('modified_a');
+ CREATE TRIGGER modified_any BEFORE UPDATE OF a ON main_table
+ FOR EACH ROW WHEN (OLD.* IS DISTINCT FROM NEW.*) EXECUTE PROCEDURE trigger_func('modified_any');
+ UPDATE main_table SET a = 50, b = 60;
+ NOTICE:  trigger_func(modified_any) called: action = UPDATE, when = BEFORE, level = ROW
+ NOTICE:  trigger_func(modified_any) called: action = UPDATE, when = BEFORE, level = ROW
+ NOTICE:  trigger_func(modified_a) called: action = UPDATE, when = BEFORE, level = ROW
+ NOTICE:  trigger_func(modified_a) called: action = UPDATE, when = BEFORE, level = ROW
+ NOTICE:  trigger_func(modified_a) called: action = UPDATE, when = BEFORE, level = ROW
+ NOTICE:  trigger_func(modified_a) called: action = UPDATE, when = BEFORE, level = ROW
+ NOTICE:  trigger_func(modified_a) called: action = UPDATE, when = BEFORE, level = ROW
+ NOTICE:  trigger_func(after_upd_row) called: action = UPDATE, when = AFTER, level = ROW
+ NOTICE:  trigger_func(after_upd_stmt) called: action = UPDATE, when = AFTER, level = STATEMENT
+ SELECT * FROM main_table ORDER BY a, b;
+  a  | b  
+ ----+----
+   6 | 10
+  21 | 20
+  30 | 40
+  31 | 10
+  50 | 35
+  50 | 60
+  81 | 15
+     |   
+ (8 rows)
+ 
+ SELECT pg_get_triggerdef(oid, true) FROM pg_trigger WHERE tgrelid = 'main_table'::regclass AND tgname = 'modified_a';
+                 pg_get_triggerdef                 
+ --------------------------------------------------
+  CREATE TRIGGER modified_a
+      BEFORE UPDATE OF a ON main_table
+      FOR EACH ROW
+      WHEN (old.a <> new.a)
+      EXECUTE PROCEDURE trigger_func('modified_a')
+ (1 row)
+ 
+ SELECT pg_get_triggerdef(oid, true) FROM pg_trigger WHERE tgrelid = 'main_table'::regclass AND tgname = 'modified_any';
+                  pg_get_triggerdef                  
+ ----------------------------------------------------
+  CREATE TRIGGER modified_any
+      BEFORE UPDATE OF a ON main_table
+      FOR EACH ROW
+      WHEN (old.* IS DISTINCT FROM new.*)
+      EXECUTE PROCEDURE trigger_func('modified_any')
+ (1 row)
+ 
+ DROP TRIGGER modified_a ON main_table;
+ DROP TRIGGER modified_any ON main_table;
  -- Test column-level triggers
  DROP TRIGGER after_upd_row_trig ON main_table;
  CREATE TRIGGER before_upd_a_row_trig BEFORE UPDATE OF a ON main_table
*************** FOR EACH ROW EXECUTE PROCEDURE trigger_f
*** 393,398 ****
--- 443,463 ----
  ERROR:  syntax error at or near "OF"
  LINE 1: CREATE TRIGGER error_ins_a BEFORE INSERT OF a ON main_table
                                                   ^
+ CREATE TRIGGER error_ins_when BEFORE INSERT OR UPDATE ON main_table
+ FOR EACH ROW WHEN (OLD.a <> NEW.a) EXECUTE PROCEDURE trigger_func('error_ins_when');
+ ERROR:  missing FROM-clause entry for table "old"
+ LINE 2: FOR EACH ROW WHEN (OLD.a <> NEW.a) EXECUTE PROCEDURE trigger...
+                            ^
+ CREATE TRIGGER error_del_when BEFORE DELETE OR UPDATE ON main_table
+ FOR EACH ROW WHEN (OLD.a <> NEW.a) EXECUTE PROCEDURE trigger_func('error_del_when');
+ ERROR:  missing FROM-clause entry for table "new"
+ LINE 2: FOR EACH ROW WHEN (OLD.a <> NEW.a) EXECUTE PROCEDURE trigger...
+                                     ^
+ CREATE TRIGGER error_stmt_when BEFORE UPDATE OF a ON main_table
+ FOR EACH STATEMENT WHEN (OLD.* IS DISTINCT FROM NEW.*) EXECUTE PROCEDURE trigger_func('error_stmt_when');
+ ERROR:  missing FROM-clause entry for table "old"
+ LINE 2: FOR EACH STATEMENT WHEN (OLD.* IS DISTINCT FROM NEW.*) EXECU...
+                                  ^
  -- check dependency restrictions
  ALTER TABLE main_table DROP COLUMN b;
  ERROR:  cannot drop table main_table column b because other objects depend on it
diff -cprN head/src/test/regress/sql/triggers.sql work/src/test/regress/sql/triggers.sql
*** head/src/test/regress/sql/triggers.sql	2009-10-15 07:14:25.000000000 +0900
--- work/src/test/regress/sql/triggers.sql	2009-11-13 12:04:10.415848373 +0900
*************** COPY main_table (a, b) FROM stdin;
*** 254,259 ****
--- 254,271 ----
  
  SELECT * FROM main_table ORDER BY a, b;
  
+ -- Trigger with WHEN clause
+ CREATE TRIGGER modified_a BEFORE UPDATE OF a ON main_table
+ FOR EACH ROW WHEN (OLD.a <> NEW.a) EXECUTE PROCEDURE trigger_func('modified_a');
+ CREATE TRIGGER modified_any BEFORE UPDATE OF a ON main_table
+ FOR EACH ROW WHEN (OLD.* IS DISTINCT FROM NEW.*) EXECUTE PROCEDURE trigger_func('modified_any');
+ UPDATE main_table SET a = 50, b = 60;
+ SELECT * FROM main_table ORDER BY a, b;
+ SELECT pg_get_triggerdef(oid, true) FROM pg_trigger WHERE tgrelid = 'main_table'::regclass AND tgname = 'modified_a';
+ SELECT pg_get_triggerdef(oid, true) FROM pg_trigger WHERE tgrelid = 'main_table'::regclass AND tgname = 'modified_any';
+ DROP TRIGGER modified_a ON main_table;
+ DROP TRIGGER modified_any ON main_table;
+ 
  -- Test column-level triggers
  DROP TRIGGER after_upd_row_trig ON main_table;
  
*************** FOR EACH ROW EXECUTE PROCEDURE trigger_f
*** 283,288 ****
--- 295,307 ----
  CREATE TRIGGER error_ins_a BEFORE INSERT OF a ON main_table
  FOR EACH ROW EXECUTE PROCEDURE trigger_func('error_ins_a');
  
+ CREATE TRIGGER error_ins_when BEFORE INSERT OR UPDATE ON main_table
+ FOR EACH ROW WHEN (OLD.a <> NEW.a) EXECUTE PROCEDURE trigger_func('error_ins_when');
+ CREATE TRIGGER error_del_when BEFORE DELETE OR UPDATE ON main_table
+ FOR EACH ROW WHEN (OLD.a <> NEW.a) EXECUTE PROCEDURE trigger_func('error_del_when');
+ CREATE TRIGGER error_stmt_when BEFORE UPDATE OF a ON main_table
+ FOR EACH STATEMENT WHEN (OLD.* IS DISTINCT FROM NEW.*) EXECUTE PROCEDURE trigger_func('error_stmt_when');
+ 
  -- check dependency restrictions
  ALTER TABLE main_table DROP COLUMN b;
  -- this should succeed, but we'll roll it back to keep the triggers around