diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index dc5bbcd32eb048e31a6828a544b815c62f9d5fa2..f468e2916b164cb7a9faebe1eba2e06ec0eaf0fe 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/access/transam/xact.c,v 1.54 1999/09/28 11:41:03 vadim Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/access/transam/xact.c,v 1.55 1999/09/29 16:05:55 wieck Exp $
  *
  * NOTES
  *		Transaction aborts can now occur two ways:
@@ -149,6 +149,7 @@
 #include "commands/async.h"
 #include "commands/sequence.h"
 #include "commands/vacuum.h"
+#include "commands/trigger.h"
 #include "libpq/be-fsstubs.h"
 #include "storage/proc.h"
 #include "storage/sinval.h"
@@ -865,6 +866,12 @@ StartTransaction()
 	 */
 	InitNoNameRelList();
 
+	/* ----------------
+	 *	Tell the trigger manager to we're starting a transaction
+	 * ----------------
+	 */
+	DeferredTriggerBeginXact();
+
 	/* ----------------
 	 *	done with start processing, set current transaction
 	 *	state to "in progress"
@@ -904,6 +911,14 @@ CommitTransaction()
 	if (s->state != TRANS_INPROGRESS)
 		elog(NOTICE, "CommitTransaction and not in in-progress state ");
 
+	/* ----------------
+	 *	Tell the trigger manager that this transaction is about to be
+	 *	committed. He'll invoke all trigger deferred until XACT before
+	 *	we really start on committing the transaction. 
+	 * ----------------
+	 */
+	DeferredTriggerEndXact();
+
 	/* ----------------
 	 *	set the current transaction state information
 	 *	appropriately during the abort processing
@@ -992,6 +1007,13 @@ AbortTransaction()
 	if (s->state != TRANS_INPROGRESS)
 		elog(NOTICE, "AbortTransaction and not in in-progress state ");
 
+	/* ----------------
+	 *	Tell the trigger manager that this transaction is about to be
+	 *	aborted. 
+	 * ----------------
+	 */
+	DeferredTriggerAbortXact();
+
 	/* ----------------
 	 *	set the current transaction state information
 	 *	appropriately during the abort processing
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index f1051cb784b74916e827c25d0c897283de2af499..5ad0f7c413b1965131588b5835c8843b20c0a5b7 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.98 1999/09/24 00:24:11 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.99 1999/09/29 16:05:56 wieck Exp $
  *
  *
  * INTERFACE ROUTINES
@@ -1468,8 +1468,7 @@ heap_destroy_with_catalog(char *relname)
 		RelationRemoveRules(rid);
 
 	/* triggers */
-	if (rel->rd_rel->reltriggers > 0)
-		RelationRemoveTriggers(rel);
+	RelationRemoveTriggers(rel);
 
 	/* ----------------
 	 *	delete attribute tuples
diff --git a/src/backend/catalog/indexing.c b/src/backend/catalog/indexing.c
index 75799e55570fddc561657cf3d7a7c28aec1556be..3ac2ecc4d66619800c4360fe3c2ca8fb1ce21528 100644
--- a/src/backend/catalog/indexing.c
+++ b/src/backend/catalog/indexing.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/catalog/indexing.c,v 1.45 1999/09/18 19:06:33 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/catalog/indexing.c,v 1.46 1999/09/29 16:05:56 wieck Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -52,7 +52,9 @@ char	   *Name_pg_attrdef_indices[Num_pg_attrdef_indices] = {AttrDefaultIndex};
 
 char	   *Name_pg_relcheck_indices[Num_pg_relcheck_indices] = {RelCheckIndex};
 
-char	   *Name_pg_trigger_indices[Num_pg_trigger_indices] = {TriggerRelidIndex};
+char	   *Name_pg_trigger_indices[Num_pg_trigger_indices] = {TriggerRelidIndex,
+	TriggerConstrNameIndex,
+	TriggerConstrRelidIndex};
 
 
 static HeapTuple CatalogIndexFetchTuple(Relation heapRelation,
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index aa7a0b56c1d0988e2e208b384db0ba2e8599496b..ce7d6f5a2d6e5fffc1cfc357d3fcb7326787bbfc 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -54,6 +54,9 @@ CreateTrigger(CreateTrigStmt *stmt)
 	Oid			fargtypes[8];
 	int			found = 0;
 	int			i;
+	char		constrtrigname[NAMEDATALEN];
+	char	   *constrname = "";
+	Oid			constrrelid = 0;
 
 	if (!allowSystemTableMods && IsSystemRelationName(stmt->relname))
 		elog(ERROR, "CreateTrigger: can't create trigger for system relation %s", stmt->relname);
@@ -63,6 +66,30 @@ CreateTrigger(CreateTrigStmt *stmt)
 		elog(ERROR, "%s: %s", stmt->relname, aclcheck_error_strings[ACLCHECK_NOT_OWNER]);
 #endif
 
+	/* ----------
+	 * If trigger is a constraint, user trigger name as constraint
+	 * name and build a unique trigger name instead.
+	 * ----------
+	 */
+	if (stmt->isconstraint)
+	{
+		constrname = stmt->trigname;
+		stmt->trigname = constrtrigname;
+		sprintf(constrtrigname, "RI_ConstraintTrigger_%d", newoid());
+
+		if (strcmp(stmt->constrrelname, "") == 0)
+			constrrelid = 0;
+		else
+		{
+			rel = heap_openr(stmt->constrrelname, NoLock);
+			if (rel == NULL)
+				elog(ERROR, "table \"%s\" does not exist",
+							stmt->constrrelname);
+			constrrelid = rel->rd_id;
+			heap_close(rel, NoLock);
+		}
+	}
+
 	rel = heap_openr(stmt->relname, AccessExclusiveLock);
 
 	TRIGGER_CLEAR_TYPE(tgtype);
@@ -148,6 +175,14 @@ CreateTrigger(CreateTrigStmt *stmt)
 	values[Anum_pg_trigger_tgname - 1] = NameGetDatum(namein(stmt->trigname));
 	values[Anum_pg_trigger_tgfoid - 1] = ObjectIdGetDatum(tuple->t_data->t_oid);
 	values[Anum_pg_trigger_tgtype - 1] = Int16GetDatum(tgtype);
+
+	values[Anum_pg_trigger_tgenabled - 1]		= true;
+	values[Anum_pg_trigger_tgisconstraint - 1]	= stmt->isconstraint;
+	values[Anum_pg_trigger_tgconstrname - 1]	= PointerGetDatum(constrname);;
+	values[Anum_pg_trigger_tgconstrrelid - 1]	= constrrelid;
+	values[Anum_pg_trigger_tgdeferrable - 1]	= stmt->deferrable;
+	values[Anum_pg_trigger_tginitdeferred - 1]	= stmt->initdeferred;
+
 	if (stmt->args)
 	{
 		List	   *le;
@@ -311,6 +346,7 @@ RelationRemoveTriggers(Relation rel)
 	HeapScanDesc tgscan;
 	ScanKeyData key;
 	HeapTuple	tup;
+	Form_pg_trigger	pg_trigger;
 
 	tgrel = heap_openr(TriggerRelationName, RowExclusiveLock);
 	ScanKeyEntryInitialize(&key, 0, Anum_pg_trigger_tgrelid,
@@ -322,6 +358,36 @@ RelationRemoveTriggers(Relation rel)
 		heap_delete(tgrel, &tup->t_self, NULL);
 
 	heap_endscan(tgscan);
+
+
+	/* ----------
+	 * Also drop all constraint triggers referencing this relation
+	 * ----------
+	 */
+	ScanKeyEntryInitialize(&key, 0, Anum_pg_trigger_tgconstrrelid,
+							F_OIDEQ, RelationGetRelid(rel));
+
+	tgscan = heap_beginscan(tgrel, 0, SnapshotNow, 1, &key);
+	while (HeapTupleIsValid(tup = heap_getnext(tgscan, 0)))
+	{
+		Relation		refrel;
+		DropTrigStmt	stmt;
+
+		pg_trigger = (Form_pg_trigger) GETSTRUCT(tup);
+		refrel = heap_open(pg_trigger->tgrelid, NoLock);
+
+		stmt.relname = nameout(&(refrel->rd_rel->relname));
+		stmt.trigname = nameout(&(pg_trigger->tgname));
+
+		DropTrigger(&stmt);
+
+		pfree(stmt.relname);
+		pfree(stmt.trigname);
+
+		heap_close(refrel, NoLock);
+	}
+	heap_endscan(tgscan);
+
 	heap_close(tgrel, RowExclusiveLock);
 }
 
@@ -379,10 +445,15 @@ RelationBuildTriggers(Relation relation)
 			triggers = (Trigger *) repalloc(triggers, (found + 1) * sizeof(Trigger));
 		build = &(triggers[found]);
 
+		build->tgoid = tuple.t_data->t_oid;
 		build->tgname = nameout(&(pg_trigger->tgname));
 		build->tgfoid = pg_trigger->tgfoid;
 		build->tgfunc.fn_addr = NULL;
 		build->tgtype = pg_trigger->tgtype;
+		build->tgenabled = pg_trigger->tgenabled;
+		build->tgisconstraint = pg_trigger->tgisconstraint;
+		build->tgdeferrable = pg_trigger->tgdeferrable;
+		build->tginitdeferred = pg_trigger->tginitdeferred;
 		build->tgnargs = pg_trigger->tgnargs;
 		memcpy(build->tgattr, &(pg_trigger->tgattr), 8 * sizeof(int16));
 		val = (struct varlena *) fastgetattr(&tuple,
@@ -592,6 +663,8 @@ ExecBRInsertTriggers(Relation rel, HeapTuple trigtuple)
 	SaveTriggerData->tg_newtuple = NULL;
 	for (i = 0; i < ntrigs; i++)
 	{
+		if (!trigger[i]->tgenabled)
+			continue;
 		CurrentTriggerData = SaveTriggerData;
 		CurrentTriggerData->tg_trigtuple = oldtuple = newtuple;
 		CurrentTriggerData->tg_trigger = trigger[i];
@@ -609,24 +682,7 @@ ExecBRInsertTriggers(Relation rel, HeapTuple trigtuple)
 void
 ExecARInsertTriggers(Relation rel, HeapTuple trigtuple)
 {
-	TriggerData *SaveTriggerData;
-	int			ntrigs = rel->trigdesc->n_after_row[TRIGGER_EVENT_INSERT];
-	Trigger   **trigger = rel->trigdesc->tg_after_row[TRIGGER_EVENT_INSERT];
-	int			i;
-
-	SaveTriggerData = (TriggerData *) palloc(sizeof(TriggerData));
-	SaveTriggerData->tg_event = TRIGGER_EVENT_INSERT | TRIGGER_EVENT_ROW;
-	SaveTriggerData->tg_relation = rel;
-	SaveTriggerData->tg_newtuple = NULL;
-	for (i = 0; i < ntrigs; i++)
-	{
-		CurrentTriggerData = SaveTriggerData;
-		CurrentTriggerData->tg_trigtuple = trigtuple;
-		CurrentTriggerData->tg_trigger = trigger[i];
-		ExecCallTriggerFunc(trigger[i]);
-	}
-	CurrentTriggerData = NULL;
-	pfree(SaveTriggerData);
+	DeferredTriggerSaveEvent(rel, TRIGGER_EVENT_INSERT, NULL, trigtuple);
 	return;
 }
 
@@ -652,6 +708,8 @@ ExecBRDeleteTriggers(EState *estate, ItemPointer tupleid)
 	SaveTriggerData->tg_newtuple = NULL;
 	for (i = 0; i < ntrigs; i++)
 	{
+		if (!trigger[i]->tgenabled)
+			continue;
 		CurrentTriggerData = SaveTriggerData;
 		CurrentTriggerData->tg_trigtuple = trigtuple;
 		CurrentTriggerData->tg_trigger = trigger[i];
@@ -672,29 +730,9 @@ void
 ExecARDeleteTriggers(EState *estate, ItemPointer tupleid)
 {
 	Relation	rel = estate->es_result_relation_info->ri_RelationDesc;
-	TriggerData *SaveTriggerData;
-	int			ntrigs = rel->trigdesc->n_after_row[TRIGGER_EVENT_DELETE];
-	Trigger   **trigger = rel->trigdesc->tg_after_row[TRIGGER_EVENT_DELETE];
-	HeapTuple	trigtuple;
-	int			i;
-
-	trigtuple = GetTupleForTrigger(estate, tupleid, NULL);
-	Assert(trigtuple != NULL);
+	HeapTuple	trigtuple = GetTupleForTrigger(estate, tupleid, NULL);
 
-	SaveTriggerData = (TriggerData *) palloc(sizeof(TriggerData));
-	SaveTriggerData->tg_event = TRIGGER_EVENT_DELETE | TRIGGER_EVENT_ROW;
-	SaveTriggerData->tg_relation = rel;
-	SaveTriggerData->tg_newtuple = NULL;
-	for (i = 0; i < ntrigs; i++)
-	{
-		CurrentTriggerData = SaveTriggerData;
-		CurrentTriggerData->tg_trigtuple = trigtuple;
-		CurrentTriggerData->tg_trigger = trigger[i];
-		ExecCallTriggerFunc(trigger[i]);
-	}
-	CurrentTriggerData = NULL;
-	pfree(SaveTriggerData);
-	pfree(trigtuple);
+	DeferredTriggerSaveEvent(rel, TRIGGER_EVENT_DELETE, trigtuple, NULL);
 	return;
 }
 
@@ -727,6 +765,8 @@ ExecBRUpdateTriggers(EState *estate, ItemPointer tupleid, HeapTuple newtuple)
 	SaveTriggerData->tg_relation = rel;
 	for (i = 0; i < ntrigs; i++)
 	{
+		if (!trigger[i]->tgenabled)
+			continue;
 		CurrentTriggerData = SaveTriggerData;
 		CurrentTriggerData->tg_trigtuple = trigtuple;
 		CurrentTriggerData->tg_newtuple = oldtuple = newtuple;
@@ -747,29 +787,9 @@ void
 ExecARUpdateTriggers(EState *estate, ItemPointer tupleid, HeapTuple newtuple)
 {
 	Relation	rel = estate->es_result_relation_info->ri_RelationDesc;
-	TriggerData *SaveTriggerData;
-	int			ntrigs = rel->trigdesc->n_after_row[TRIGGER_EVENT_UPDATE];
-	Trigger   **trigger = rel->trigdesc->tg_after_row[TRIGGER_EVENT_UPDATE];
-	HeapTuple	trigtuple;
-	int			i;
-
-	trigtuple = GetTupleForTrigger(estate, tupleid, NULL);
-	Assert(trigtuple != NULL);
+	HeapTuple	trigtuple = GetTupleForTrigger(estate, tupleid, NULL);
 
-	SaveTriggerData = (TriggerData *) palloc(sizeof(TriggerData));
-	SaveTriggerData->tg_event = TRIGGER_EVENT_UPDATE | TRIGGER_EVENT_ROW;
-	SaveTriggerData->tg_relation = rel;
-	for (i = 0; i < ntrigs; i++)
-	{
-		CurrentTriggerData = SaveTriggerData;
-		CurrentTriggerData->tg_trigtuple = trigtuple;
-		CurrentTriggerData->tg_newtuple = newtuple;
-		CurrentTriggerData->tg_trigger = trigger[i];
-		ExecCallTriggerFunc(trigger[i]);
-	}
-	CurrentTriggerData = NULL;
-	pfree(SaveTriggerData);
-	pfree(trigtuple);
+	DeferredTriggerSaveEvent(rel, TRIGGER_EVENT_UPDATE, trigtuple, newtuple);
 	return;
 }
 
@@ -858,3 +878,998 @@ ltrmark:;
 
 	return result;
 }
+
+
+/* ----------
+ * Deferred trigger stuff
+ * ----------
+ */
+
+
+/* ----------
+ * Internal data to the deferred trigger mechanism is held
+ * during entire session in a global memor created at startup and
+ * over statements/commands in a separate global memory which
+ * is created at transaction start and destroyed at transaction
+ * end.
+ * ----------
+ */
+static GlobalMemory		deftrig_gcxt = NULL;
+static GlobalMemory		deftrig_cxt = NULL;
+
+/* ----------
+ * Global data that tells which triggers are actually in
+ * state IMMEDIATE or DEFERRED.
+ * ----------
+ */
+static bool				deftrig_dfl_all_isset = false;
+static bool				deftrig_dfl_all_isdeferred = false;
+static List			   *deftrig_dfl_trigstates = NIL;
+
+static bool				deftrig_all_isset;
+static bool				deftrig_all_isdeferred;
+static List			   *deftrig_trigstates;
+
+/* ----------
+ * The list of events during the entire transaction.
+ *
+ * XXX This must finally be held in a file because of the huge
+ *     number of events that could occur in the real world.
+ * ----------
+ */
+static int				deftrig_n_events;
+static List			   *deftrig_events;
+
+
+/* ----------
+ * deferredTriggerCheckState()
+ *
+ *	Returns true if the trigger identified by tgoid is actually
+ *	in state DEFERRED.
+ * ----------
+ */
+static bool
+deferredTriggerCheckState(Oid tgoid, int32 itemstate)
+{
+	MemoryContext			oldcxt;
+	List				   *sl;
+	DeferredTriggerStatus	trigstate;
+
+	/* ----------
+	 * Not deferrable triggers (i.e. normal AFTER ROW triggers
+	 * and constraints declared NOT DEFERRABLE, the state is
+	 * allways false.
+	 * ----------
+	 */
+	if ((itemstate & TRIGGER_DEFERRED_DEFERRABLE) == 0)
+		return false;
+
+	/* ----------
+	 * Lookup if we know an individual state for this trigger
+	 * ----------
+	 */
+	foreach (sl, deftrig_trigstates)
+	{
+		trigstate = (DeferredTriggerStatus) lfirst(sl);
+		if (trigstate->dts_tgoid == tgoid)
+			return trigstate->dts_tgisdeferred;
+	}
+
+	/* ----------
+	 * No individual state known - so if the user issued a
+	 * SET CONSTRAINT ALL ..., we return that instead of the
+	 * triggers default state.
+	 * ----------
+	 */
+	if (deftrig_all_isset)
+		return deftrig_all_isdeferred;
+
+	/* ----------
+	 * No ALL state known either, remember the default state
+	 * as the current and return that.
+	 * ----------
+	 */
+	oldcxt = MemoryContextSwitchTo((MemoryContext) deftrig_cxt);
+
+	trigstate = (DeferredTriggerStatus)
+			palloc(sizeof(DeferredTriggerStatusData));
+	trigstate->dts_tgoid		= tgoid;
+	trigstate->dts_tgisdeferred	= 
+			((itemstate & TRIGGER_DEFERRED_INITDEFERRED) != 0);
+	deftrig_trigstates = lappend(deftrig_trigstates, trigstate);
+
+	MemoryContextSwitchTo(oldcxt);
+
+	return trigstate->dts_tgisdeferred;
+}
+
+
+/* ----------
+ * deferredTriggerAddEvent()
+ *
+ *	Add a new trigger event to the queue.
+ * ----------
+ */
+static void 
+deferredTriggerAddEvent(DeferredTriggerEvent event)
+{
+	deftrig_events = lappend(deftrig_events, event);
+	deftrig_n_events++;
+
+	return;
+}
+
+
+/* ----------
+ * deferredTriggerGetPreviousEvent()
+ *
+ *	Backward scan the eventlist to find the event a given OLD tuple
+ *	resulted from in the same transaction.
+ * ----------
+ */
+static DeferredTriggerEvent
+deferredTriggerGetPreviousEvent(Oid relid, ItemPointer ctid)
+{
+	DeferredTriggerEvent	previous;
+	int						n;
+
+	for (n = deftrig_n_events - 1; n >= 0; n--)
+	{
+		previous = (DeferredTriggerEvent) nth(n, deftrig_events);
+
+		if (previous->dte_relid != relid)
+			continue;
+		if (previous->dte_event & TRIGGER_DEFERRED_CANCELED)
+			continue;
+
+		if (ItemPointerGetBlockNumber(ctid) == 
+					ItemPointerGetBlockNumber(&(previous->dte_newctid)) &&
+					ItemPointerGetOffsetNumber(ctid) ==
+					ItemPointerGetOffsetNumber(&(previous->dte_newctid)))
+			return previous;
+	}
+
+	elog(ERROR, 
+		"deferredTriggerGetPreviousEvent(): event for tuple %s not found",
+		tidout(ctid));
+	return NULL;
+}
+
+
+/* ----------
+ * deferredTriggerCancelEvent()
+ *
+ *	Mark an event in the eventlist as cancelled because it isn't
+ *	required anymore (replaced by anoter event).
+ * ----------
+ */
+static void
+deferredTriggerCancelEvent(DeferredTriggerEvent event)
+{
+	event->dte_event |= TRIGGER_DEFERRED_CANCELED;
+}
+
+
+/* ----------
+ * deferredTriggerExecute()
+ *
+ *	Fetch the required tuples back from the heap and fire one
+ *	single trigger function.
+ * ----------
+ */
+static void
+deferredTriggerExecute(DeferredTriggerEvent event, int itemno)
+{
+	Relation		rel;
+	TriggerData		SaveTriggerData;
+	HeapTupleData	oldtuple;
+	HeapTupleData	newtuple;
+	HeapTuple		rettuple;
+	Buffer			oldbuffer;
+	Buffer			newbuffer;
+
+	/* ----------
+	 * Open the heap and fetch the required OLD and NEW tuples.
+	 * ----------
+	 */
+	rel = heap_open(event->dte_relid, NoLock);
+
+	if (ItemPointerIsValid(&(event->dte_oldctid)))
+	{
+		ItemPointerCopy(&(event->dte_oldctid), &(oldtuple.t_self));
+		heap_fetch(rel, SnapshotAny, &oldtuple, &oldbuffer);
+		if (!oldtuple.t_data)
+			elog(ERROR, "deferredTriggerExecute(): failed to fetch old tuple");
+	}
+
+	if (ItemPointerIsValid(&(event->dte_newctid)))
+	{
+		ItemPointerCopy(&(event->dte_newctid), &(newtuple.t_self));
+		heap_fetch(rel, SnapshotAny, &newtuple, &newbuffer);
+		if (!newtuple.t_data)
+			elog(ERROR, "deferredTriggerExecute(): failed to fetch new tuple");
+	}
+
+	/* ----------
+	 * Setup the trigger information
+	 * ----------
+	 */
+	SaveTriggerData.tg_event    = event->dte_event | TRIGGER_EVENT_ROW;
+	SaveTriggerData.tg_relation = rel;
+
+	switch (event->dte_event)
+	{
+		case TRIGGER_EVENT_INSERT:
+			SaveTriggerData.tg_trigtuple = &newtuple;
+			SaveTriggerData.tg_newtuple  = NULL;
+			SaveTriggerData.tg_trigger   = 
+					rel->trigdesc->tg_after_row[TRIGGER_EVENT_INSERT][itemno];
+			break;
+
+		case TRIGGER_EVENT_UPDATE:
+			SaveTriggerData.tg_trigtuple = &oldtuple;
+			SaveTriggerData.tg_newtuple  = &newtuple;
+			SaveTriggerData.tg_trigger   = 
+					rel->trigdesc->tg_after_row[TRIGGER_EVENT_UPDATE][itemno];
+			break;
+
+		case TRIGGER_EVENT_DELETE:
+			SaveTriggerData.tg_trigtuple = &oldtuple;
+			SaveTriggerData.tg_newtuple  = NULL;
+			SaveTriggerData.tg_trigger   = 
+					rel->trigdesc->tg_after_row[TRIGGER_EVENT_DELETE][itemno];
+			break;
+
+		default:
+	} 
+
+	/* ----------
+	 * Call the trigger and throw away an eventually returned
+	 * updated tuple.
+	 * ----------
+	 */
+	CurrentTriggerData = &SaveTriggerData;
+	rettuple = ExecCallTriggerFunc(SaveTriggerData.tg_trigger);
+	CurrentTriggerData = NULL;
+	if (rettuple != NULL && rettuple != &oldtuple && rettuple != &newtuple)
+		pfree(rettuple);
+
+	/* ----------
+	 * Release buffers and close the relation
+	 * ----------
+	 */
+	if (ItemPointerIsValid(&(event->dte_oldctid)))
+		ReleaseBuffer(oldbuffer);
+	if (ItemPointerIsValid(&(event->dte_newctid)))
+		ReleaseBuffer(newbuffer);
+
+	heap_close(rel, NoLock);
+
+	return;
+}
+
+
+/* ----------
+ * deferredTriggerInvokeEvents()
+ *
+ *	Scan the event queue for not yet invoked triggers. Check if they
+ *	should be invoked now and do so.
+ * ----------
+ */
+static void
+deferredTriggerInvokeEvents(bool immediate_only)
+{
+	List					*el;
+	DeferredTriggerEvent	event;
+	int						still_deferred_ones;
+	int						eventno = -1;
+	int						i;
+
+	/* ----------
+	 * For now we process all events - to speedup transaction blocks
+	 * we need to remember the actual end of the queue at EndQuery
+	 * and process only events that are newer. On state changes we
+	 * simply reset the position to the beginning of the queue and
+	 * process all events once with the new states when the
+	 * SET CONSTRAINTS ... command finishes and calls EndQuery.
+	 * ----------
+	 */
+	foreach (el, deftrig_events)
+	{
+		eventno++;
+
+		/* ----------
+		 * Get the event and check if it is completely done.
+		 * ----------
+		 */
+		event = (DeferredTriggerEvent) lfirst(el);
+		if (event->dte_event & (TRIGGER_DEFERRED_DONE | 
+								TRIGGER_DEFERRED_CANCELED))
+			continue;
+	
+		/* ----------
+		 * Check each trigger item in the event.
+		 * ----------
+		 */
+		still_deferred_ones = false;
+		for (i = 0; i < event->dte_n_items; i++)
+		{
+			if (event->dte_item[i].dti_state & TRIGGER_DEFERRED_DONE)
+				continue;
+
+			/* ----------
+			 * This trigger item hasn't been called yet. Check if
+			 * we should call it now.
+			 * ----------
+			 */
+			if (immediate_only && deferredTriggerCheckState(
+								event->dte_item[i].dti_tgoid, 
+								event->dte_item[i].dti_state))
+			{
+				still_deferred_ones = true;
+				continue;
+			}
+
+			/* ----------
+			 * So let's fire it...
+			 * ----------
+			 */
+			deferredTriggerExecute(event, i);
+			event->dte_item[i].dti_state |= TRIGGER_DEFERRED_DONE;
+		}
+
+		/* ----------
+		 * Remember in the event itself if all trigger items are
+		 * done.
+		 * ----------
+		 */
+		if (!still_deferred_ones)
+			event->dte_event |= TRIGGER_DEFERRED_DONE;
+	}
+}
+
+
+/* ----------
+ * DeferredTriggerInit()
+ *
+ *	Initialize the deferred trigger mechanism. This is called during
+ *	backend startup and is guaranteed to be before the first of all
+ *	transactions.
+ * ----------
+ */
+int
+DeferredTriggerInit(void)
+{
+	deftrig_gcxt = CreateGlobalMemory("DeferredTriggerSession");
+	return 0;
+}
+
+
+/* ----------
+ * DeferredTriggerBeginXact()
+ *
+ *	Called at transaction start (either BEGIN or implicit for single
+ *	statement outside of transaction block).
+ * ----------
+ */
+void
+DeferredTriggerBeginXact(void)
+{
+	MemoryContext			oldcxt;
+	List					*l;
+	DeferredTriggerStatus	dflstat;
+	DeferredTriggerStatus	stat;
+
+	if (deftrig_cxt != NULL)
+		elog(FATAL, 
+			"DeferredTriggerBeginXact() called while inside transaction");
+
+	/* ----------
+	 * Create the per transaction memory context and copy all states
+	 * from the per session context to here.
+	 * ----------
+	 */
+	deftrig_cxt				= CreateGlobalMemory("DeferredTriggerXact");
+	oldcxt = MemoryContextSwitchTo((MemoryContext)deftrig_cxt);
+
+	deftrig_all_isset		= deftrig_dfl_all_isset;
+	deftrig_all_isdeferred	= deftrig_dfl_all_isdeferred;
+	
+	deftrig_trigstates		= NIL;
+	foreach (l, deftrig_dfl_trigstates)
+	{
+		dflstat = (DeferredTriggerStatus) lfirst(l);
+		stat    = (DeferredTriggerStatus) 
+								palloc(sizeof(DeferredTriggerStatusData));
+
+		stat->dts_tgoid        = dflstat->dts_tgoid;
+		stat->dts_tgisdeferred = dflstat->dts_tgisdeferred;
+
+		deftrig_trigstates = lappend(deftrig_trigstates, stat);
+	}
+
+	MemoryContextSwitchTo(oldcxt);
+
+	deftrig_n_events	= 0;
+	deftrig_events		= NIL;
+}
+
+
+/* ----------
+ * DeferredTriggerEndQuery()
+ *
+ *	Called after one query sent down by the user has completely been
+ *	processed. At this time we invoke all outstanding IMMEDIATE triggers.
+ * ----------
+ */
+void
+DeferredTriggerEndQuery(void)
+{
+	/* ----------
+	 * Ignore call if we aren't in a transaction.
+	 * ----------
+	 */
+	if (deftrig_cxt == NULL)
+		return;
+
+	deferredTriggerInvokeEvents(true);
+}
+
+
+/* ----------
+ * DeferredTriggerEndXact()
+ *
+ *	Called just before the current transaction is committed. At this
+ *	time we invoke all DEFERRED triggers and tidy up.
+ * ----------
+ */
+void
+DeferredTriggerEndXact(void)
+{
+	/* ----------
+	 * Ignore call if we aren't in a transaction.
+	 * ----------
+	 */
+	if (deftrig_cxt == NULL)
+		return;
+
+	deferredTriggerInvokeEvents(false);
+
+	GlobalMemoryDestroy(deftrig_cxt);
+	deftrig_cxt = NULL;
+}
+
+
+/* ----------
+ * DeferredTriggerAbortXact()
+ *
+ *	The current transaction has entered the abort state.
+ *	All outstanding triggers are canceled so we simply throw
+ *	away anything we know.
+ * ----------
+ */
+void
+DeferredTriggerAbortXact(void)
+{
+	/* ----------
+	 * Ignore call if we aren't in a transaction.
+	 * ----------
+	 */
+	if (deftrig_cxt == NULL)
+		return;
+
+	GlobalMemoryDestroy(deftrig_cxt);
+	deftrig_cxt = NULL;
+}
+
+
+/* ----------
+ * DeferredTriggerSetState()
+ *
+ *	Called for the users SET CONSTRAINTS ... utility command.
+ * ----------
+ */
+void
+DeferredTriggerSetState(ConstraintsSetStmt *stmt)
+{
+	Relation				tgrel;
+	Relation				irel;
+	List					*l;
+	List					*ls;
+	List					*lnext;
+	List					*loid = NIL;
+	MemoryContext			oldcxt;
+	bool					found;
+	DeferredTriggerStatus	state;
+
+	/* ----------
+	 * Handle SET CONSTRAINTS ALL ...
+	 * ----------
+	 */
+	if (stmt->constraints == NIL) {
+		if (!IsTransactionBlock())
+		{
+			/* ----------
+			 * ... outside of a transaction block
+			 * ----------
+			 */
+			oldcxt = MemoryContextSwitchTo((MemoryContext) deftrig_gcxt);
+
+			/* ----------
+			 * Drop all information about individual trigger states per
+			 * session.
+			 * ----------
+			 */
+			l = deftrig_dfl_trigstates;
+			while (l != NIL)
+			{
+				lnext = lnext(l);
+				pfree(lfirst(l));
+				pfree(l);
+				l = lnext;
+			}
+			deftrig_dfl_trigstates = NIL;
+
+			/* ----------
+			 * Set the session ALL state to known.
+			 * ----------
+			 */
+			deftrig_dfl_all_isset      = true;
+			deftrig_dfl_all_isdeferred = stmt->deferred;
+
+			MemoryContextSwitchTo(oldcxt);
+
+			return;
+		} else {
+			/* ----------
+			 * ... inside of a transaction block
+			 * ----------
+			 */
+			oldcxt = MemoryContextSwitchTo((MemoryContext) deftrig_cxt);
+
+			/* ----------
+			 * Drop all information about individual trigger states per
+			 * transaction.
+			 * ----------
+			 */
+			l = deftrig_trigstates;
+			while (l != NIL)
+			{
+				lnext = lnext(l);
+				pfree(lfirst(l));
+				pfree(l);
+				l = lnext;
+			}
+			deftrig_trigstates = NIL;
+
+			/* ----------
+			 * Set the per transaction ALL state to known.
+			 * ----------
+			 */
+			deftrig_all_isset      = true;
+			deftrig_all_isdeferred = stmt->deferred;
+
+			MemoryContextSwitchTo(oldcxt);
+
+			return;
+		}
+	}
+
+	/* ----------
+	 * Handle SET CONSTRAINTS constraint-name [, ...]
+	 * First lookup all trigger Oid's for the constraint names.
+	 * ----------
+	 */
+	tgrel = heap_openr(TriggerRelationName, AccessShareLock);
+	irel = index_openr(TriggerConstrNameIndex);
+
+	foreach (l, stmt->constraints)
+	{
+		ScanKeyData			skey;
+		HeapTupleData		tuple;
+		IndexScanDesc		sd;
+		RetrieveIndexResult	indexRes;
+		Buffer				buffer;
+		Form_pg_trigger		pg_trigger;
+		Oid					constr_oid;
+
+		/* ----------
+		 * Check that only named constraints are set explicitly
+		 * ----------
+		 */
+		if (strcmp((char *)lfirst(l), "") == 0)
+			elog(ERROR, "unnamed constraints cannot be set explicitly");
+
+		/* ----------
+		 * Setup to scan pg_trigger by tgconstrname ...
+		 * ----------
+		 */
+		ScanKeyEntryInitialize(&skey,
+							   (bits16) 0x0,
+							   (AttrNumber) 1,
+							   (RegProcedure) F_NAMEEQ,
+							   PointerGetDatum((char *)lfirst(l)));
+
+		sd = index_beginscan(irel, false, 1, &skey);
+
+		/* ----------
+		 * ... and search for the constraint trigger row
+		 * ----------
+		 */
+		found = false;
+		for (;;)
+		{
+			indexRes = index_getnext(sd, ForwardScanDirection);
+			if (!indexRes)
+				break;
+
+			tuple.t_self = indexRes->heap_iptr;
+			heap_fetch(tgrel, SnapshotNow, &tuple, &buffer);
+			pfree(indexRes);
+			if (!tuple.t_data)
+			{
+				ReleaseBuffer(buffer);
+				continue;
+			}
+
+			/* ----------
+			 * If we found some, check that they fit the deferrability
+			 * ----------
+			 */
+			pg_trigger = (Form_pg_trigger) GETSTRUCT(&tuple);
+			if (stmt->deferred & !pg_trigger->tgdeferrable)
+				elog(ERROR, "Constraint '%s' is not deferrable", 
+									(char *)lfirst(l));
+
+			constr_oid = tuple.t_data->t_oid;
+			loid = lappend(loid, (Node *)constr_oid);
+			found = true;
+
+			ReleaseBuffer(buffer);
+		}
+
+		/* ----------
+		 * Not found ?
+		 * ----------
+		 */
+		if (!found)
+			elog(ERROR, "Constraint '%s' does not exist", (char *)lfirst(l));
+
+		index_endscan(sd);
+
+	}
+	index_close(irel);
+	heap_close(tgrel, AccessShareLock);
+
+
+	if (!IsTransactionBlock())
+	{
+		/* ----------
+		 * Outside of a transaction block set the trigger
+		 * states of individual triggers on session level.
+		 * ----------
+		 */
+		oldcxt = MemoryContextSwitchTo((MemoryContext) deftrig_gcxt);
+
+		foreach (l, loid)
+		{
+			found = false;
+			foreach (ls, deftrig_dfl_trigstates)
+			{
+				state = (DeferredTriggerStatus) lfirst(ls);
+				if (state->dts_tgoid == (Oid) lfirst(l))
+				{
+					state->dts_tgisdeferred = stmt->deferred;
+					found = true;
+					break;
+				}
+			}
+			if (!found)
+			{
+				state = (DeferredTriggerStatus)
+									palloc(sizeof(DeferredTriggerStatusData));
+				state->dts_tgoid        = (Oid) lfirst(l);
+				state->dts_tgisdeferred = stmt->deferred;
+
+				deftrig_dfl_trigstates = 
+									lappend(deftrig_dfl_trigstates, state);
+			}
+		}
+
+		MemoryContextSwitchTo(oldcxt);
+
+		return;
+	} else {
+		/* ----------
+		 * Inside of a transaction block set the trigger
+		 * states of individual triggers on transaction level.
+		 * ----------
+		 */
+		oldcxt = MemoryContextSwitchTo((MemoryContext) deftrig_cxt);
+
+		foreach (l, loid)
+		{
+			found = false;
+			foreach (ls, deftrig_trigstates)
+			{
+				state = (DeferredTriggerStatus) lfirst(ls);
+				if (state->dts_tgoid == (Oid) lfirst(l))
+				{
+					state->dts_tgisdeferred = stmt->deferred;
+					found = true;
+					break;
+				}
+			}
+			if (!found)
+			{
+				state = (DeferredTriggerStatus)
+									palloc(sizeof(DeferredTriggerStatusData));
+				state->dts_tgoid        = (Oid) lfirst(l);
+				state->dts_tgisdeferred = stmt->deferred;
+
+				deftrig_trigstates = 
+									lappend(deftrig_trigstates, state);
+			}
+		}
+
+		MemoryContextSwitchTo(oldcxt);
+
+		return;
+	}
+}
+
+
+/* ----------
+ * DeferredTriggerSaveEvent()
+ *
+ *	Called by ExecAR...Triggers() to add the event to the queue.
+ * ----------
+ */
+void
+DeferredTriggerSaveEvent(Relation rel, int event,
+					HeapTuple oldtup, HeapTuple newtup)
+{
+	MemoryContext			oldcxt;
+	DeferredTriggerEvent	new_event;
+	DeferredTriggerEvent	prev_event;
+	bool					prev_done = false;
+	int						new_size;
+	int						i;
+	int						ntriggers;
+	Trigger				  **triggers;
+	ItemPointerData			oldctid;
+	ItemPointerData			newctid;
+
+	if (deftrig_cxt == NULL)
+		elog(ERROR, 
+			"DeferredTriggerSaveEvent() called outside of transaction");
+
+	/* ----------
+	 * Check if we're interested in this row at all
+	 * ----------
+	 */
+	if (rel->trigdesc->n_after_row[TRIGGER_EVENT_INSERT] == 0 &&
+				rel->trigdesc->n_after_row[TRIGGER_EVENT_UPDATE] == 0 &&
+				rel->trigdesc->n_after_row[TRIGGER_EVENT_DELETE] == 0 &&
+				rel->trigdesc->n_before_row[TRIGGER_EVENT_INSERT] == 0 &&
+				rel->trigdesc->n_before_row[TRIGGER_EVENT_UPDATE] == 0 &&
+				rel->trigdesc->n_before_row[TRIGGER_EVENT_DELETE] == 0)
+		return;
+
+	/* ----------
+	 * Get the CTID's of OLD and NEW
+	 * ----------
+	 */
+	if (oldtup != NULL)
+		ItemPointerCopy(&(oldtup->t_self), &(oldctid));
+	else
+		ItemPointerSetInvalid(&(oldctid));
+	if (newtup != NULL)
+		ItemPointerCopy(&(newtup->t_self), &(newctid));
+	else
+		ItemPointerSetInvalid(&(newctid));
+
+	/* ----------
+	 * Eventually modify the event and do some general RI violation checks
+	 * ----------
+	 */
+	switch (event)
+	{
+		case TRIGGER_EVENT_INSERT:
+			/* ----------
+			 * Don't know how to (surely) check if another tuple with
+			 * this meaning (from all FK's point of view) got deleted
+			 * in the same transaction. Thus not handled yet.
+			 * ----------
+			 */
+			break;
+
+		case TRIGGER_EVENT_UPDATE:
+			/* ----------
+			 * On UPDATE check if the tuple updated is a result
+			 * of the same transaction.
+			 * ----------
+			 */
+			if (oldtup->t_data->t_xmin != GetCurrentTransactionId())
+				break;
+
+			/* ----------
+			 * Look at the previous event to the same tuple if
+			 * any of it's triggers has already been executed.
+			 * Such a situation would potentially violate RI
+			 * so we abort the transaction.
+			 * ----------
+			 */
+			prev_event = deferredTriggerGetPreviousEvent(rel->rd_id, &oldctid);
+			if (prev_event->dte_event & TRIGGER_DEFERRED_HAS_BEFORE	||
+					(prev_event->dte_n_items != 0 &&
+					 prev_event->dte_event & TRIGGER_DEFERRED_DONE))
+				prev_done = true;
+			else
+				for (i = 0; i < prev_event->dte_n_items; i++)
+				{
+					if (prev_event->dte_item[i].dti_state &
+									TRIGGER_DEFERRED_DONE)
+					{
+						prev_done = true;
+						break;
+					}
+				}
+
+			if (prev_done)
+			{
+				elog(NOTICE, "UPDATE of row inserted/updated in same "
+						"transaction violates");
+				elog(NOTICE, "referential integrity semantics. Other "
+						"triggers or IMMEDIATE ");
+				elog(ERROR, " constraints have already been executed.");
+			}
+
+			/* ----------
+			 * Anything's fine so far - i.e. none of the previous
+			 * events triggers has been executed up to now. Let's
+			 * the REAL event that happened so far.
+			 * ----------
+			 */
+			switch (prev_event->dte_event & TRIGGER_EVENT_OPMASK)
+			{
+				case TRIGGER_EVENT_INSERT:
+					/* ----------
+					 * The previous operation was an insert.
+					 * So the REAL new event is an INSERT of
+					 * the new tuple.
+					 * ----------
+					 */
+					event = TRIGGER_EVENT_INSERT;
+					ItemPointerSetInvalid(&oldctid);
+					deferredTriggerCancelEvent(prev_event);
+					break;
+
+				case TRIGGER_EVENT_UPDATE:
+					/* ----------
+					 * The previous operation was an UPDATE.
+					 * So the REAL new event is still an UPDATE
+					 * but from the original tuple to this new one.
+					 * ----------
+					 */
+					event = TRIGGER_EVENT_UPDATE;
+					ItemPointerCopy(&(prev_event->dte_oldctid), &oldctid);
+					deferredTriggerCancelEvent(prev_event);
+					break;
+			}
+
+			break;
+
+		case TRIGGER_EVENT_DELETE:
+			/* ----------
+			 * On DELETE check if the tuple updated is a result
+			 * of the same transaction.
+			 * ----------
+			 */
+			if (oldtup->t_data->t_xmin != GetCurrentTransactionId())
+				break;
+
+			/* ----------
+			 * Look at the previous event to the same tuple if
+			 * any of it's triggers has already been executed.
+			 * Such a situation would potentially violate RI
+			 * so we abort the transaction.
+			 * ----------
+			 */
+			prev_event = deferredTriggerGetPreviousEvent(rel->rd_id, &oldctid);
+			if (prev_event->dte_event & TRIGGER_DEFERRED_HAS_BEFORE	||
+					(prev_event->dte_n_items != 0 &&
+					 prev_event->dte_event & TRIGGER_DEFERRED_DONE))
+				prev_done = true;
+			else
+				for (i = 0; i < prev_event->dte_n_items; i++)
+				{
+					if (prev_event->dte_item[i].dti_state &
+									TRIGGER_DEFERRED_DONE)
+					{
+						prev_done = true;
+						break;
+					}
+				}
+
+			if (prev_done)
+			{
+				elog(NOTICE, "DELETE of row inserted/updated in same "
+						"transaction violates");
+				elog(NOTICE, "referential integrity semantics. Other "
+						"triggers or IMMEDIATE ");
+				elog(ERROR, " constraints have already been executed.");
+			}
+
+			/* ----------
+			 * Anything's fine so far - i.e. none of the previous
+			 * events triggers has been executed up to now. Let's
+			 * the REAL event that happened so far.
+			 * ----------
+			 */
+			switch (prev_event->dte_event & TRIGGER_EVENT_OPMASK)
+			{
+				case TRIGGER_EVENT_INSERT:
+					/* ----------
+					 * The previous operation was an insert.
+					 * So the REAL new event is nothing.
+					 * ----------
+					 */
+					deferredTriggerCancelEvent(prev_event);
+					return;
+
+				case TRIGGER_EVENT_UPDATE:
+					/* ----------
+					 * The previous operation was an UPDATE.
+					 * So the REAL new event is a DELETE
+					 * but from the original tuple.
+					 * ----------
+					 */
+					event = TRIGGER_EVENT_DELETE;
+					ItemPointerCopy(&(prev_event->dte_oldctid), &oldctid);
+					deferredTriggerCancelEvent(prev_event);
+					break;
+			}
+
+			break;
+	}
+	
+	/* ----------
+	 * Create a new event and save it.
+	 * ----------
+	 */
+	oldcxt = MemoryContextSwitchTo((MemoryContext) deftrig_cxt);
+
+	ntriggers = rel->trigdesc->n_after_row[event];
+	triggers  = rel->trigdesc->tg_after_row[event];
+
+	new_size  = sizeof(DeferredTriggerEventData) +
+				ntriggers * sizeof(DeferredTriggerEventItem);
+				
+	new_event = (DeferredTriggerEvent) palloc(new_size);
+	new_event->dte_event	= event;
+	new_event->dte_relid	= rel->rd_id;
+	ItemPointerCopy(&oldctid, &(new_event->dte_oldctid));
+	ItemPointerCopy(&newctid, &(new_event->dte_newctid));
+	new_event->dte_n_items = ntriggers;
+	new_event->dte_item[ntriggers].dti_state = new_size;
+	for (i = 0; i < ntriggers; i++)
+	{
+		new_event->dte_item[i].dti_tgoid = triggers[i]->tgoid;
+		new_event->dte_item[i].dti_state = 
+			((triggers[i]->tgdeferrable) ? 
+						TRIGGER_DEFERRED_DEFERRABLE : 0)	|
+			((triggers[i]->tginitdeferred) ? 
+						TRIGGER_DEFERRED_INITDEFERRED : 0)	|
+			((rel->trigdesc->n_before_row[event] > 0) ?
+						TRIGGER_DEFERRED_HAS_BEFORE : 0);
+	}
+
+	deferredTriggerAddEvent(new_event);
+
+	MemoryContextSwitchTo(oldcxt);
+
+	return;
+}
+
+
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index f07f8777a2f94b226e353d3746eaf8f959b6e5df..0943e4085becf2834885925ca494b0b2175c7a59 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -26,7 +26,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.95 1999/09/24 00:24:23 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.96 1999/09/29 16:06:02 wieck Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1190,8 +1190,7 @@ ExecAppend(TupleTableSlot *slot,
 	estate->es_lastoid = newId;
 
 	/* AFTER ROW INSERT Triggers */
-	if (resultRelationDesc->trigdesc &&
-	 resultRelationDesc->trigdesc->n_after_row[TRIGGER_EVENT_INSERT] > 0)
+	if (resultRelationDesc->trigdesc)
 		ExecARInsertTriggers(resultRelationDesc, tuple);
 }
 
@@ -1277,8 +1276,7 @@ ldelete:;
 	 */
 
 	/* AFTER ROW DELETE Triggers */
-	if (resultRelationDesc->trigdesc &&
-	 resultRelationDesc->trigdesc->n_after_row[TRIGGER_EVENT_DELETE] > 0)
+	if (resultRelationDesc->trigdesc)
 		ExecARDeleteTriggers(estate, tupleid);
 
 }
@@ -1420,8 +1418,7 @@ lreplace:;
 		ExecInsertIndexTuples(slot, &(tuple->t_self), estate, true);
 
 	/* AFTER ROW UPDATE Triggers */
-	if (resultRelationDesc->trigdesc &&
-	 resultRelationDesc->trigdesc->n_after_row[TRIGGER_EVENT_UPDATE] > 0)
+	if (resultRelationDesc->trigdesc)
 		ExecARUpdateTriggers(estate, tupleid, tuple);
 }
 
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index c4355de20b9f69b55c98aab3188223dbce2b4948..b6583197be3962902544901404896717d76d0f5f 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -10,7 +10,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.103 1999/09/28 14:49:36 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.104 1999/09/29 16:06:06 wieck Exp $
  *
  * HISTORY
  *	  AUTHOR			DATE			MAJOR EVENT
@@ -132,7 +132,8 @@ Oid	param_type(int t); /* used in parse_expr.c */
 		CreatedbStmt, DestroydbStmt, VacuumStmt, CursorStmt, SubSelect,
 		UpdateStmt, InsertStmt, select_clause, SelectStmt, NotifyStmt, DeleteStmt, 
 		ClusterStmt, ExplainStmt, VariableSetStmt, VariableShowStmt, VariableResetStmt,
-		CreateUserStmt, AlterUserStmt, DropUserStmt, RuleActionStmt
+		CreateUserStmt, AlterUserStmt, DropUserStmt, RuleActionStmt,
+		ConstraintsSetStmt,
 
 %type <str>		opt_database1, opt_database2, location, encoding
 
@@ -146,6 +147,9 @@ Oid	param_type(int t); /* used in parse_expr.c */
 
 %type <boolean>	TriggerActionTime, TriggerForSpec, PLangTrusted
 
+%type <ival>	OptConstrTrigDeferrable, OptConstrTrigInitdeferred
+%type <str>		OptConstrFromTable
+
 %type <str>		TriggerEvents, TriggerFuncArg
 
 %type <str>		relation_name, copy_file_name, copy_delimiter, def_name,
@@ -254,6 +258,10 @@ Oid	param_type(int t); /* used in parse_expr.c */
 %type <list>	key_actions, key_action
 %type <str>		key_match, key_reference
 
+%type <list>	constraints_set_list
+%type <list>	constraints_set_namelist
+%type <boolean>	constraints_set_mode
+
 /*
  * If you make any token changes, remember to:
  *		- use "yacc -d" and update parse.h
@@ -274,8 +282,8 @@ Oid	param_type(int t); /* used in parse_expr.c */
 		BEGIN_TRANS, BETWEEN, BOTH, BY,
 		CASCADE, CASE, CAST, CHAR, CHARACTER, CHECK, CLOSE,
 		COALESCE, COLLATE, COLUMN, COMMIT,
-		CONSTRAINT, CREATE, CROSS, CURRENT, CURRENT_DATE, CURRENT_TIME, 
-		CURRENT_TIMESTAMP, CURRENT_USER, CURSOR,
+		CONSTRAINT, CONSTRAINTS, CREATE, CROSS, CURRENT, CURRENT_DATE,
+		CURRENT_TIME, CURRENT_TIMESTAMP, CURRENT_USER, CURSOR,
 		DAY_P, DECIMAL, DECLARE, DEFAULT, DELETE, DESC, DISTINCT, DOUBLE, DROP,
 		ELSE, END_TRANS, EXCEPT, EXECUTE, EXISTS, EXTRACT,
 		FALSE_P, FETCH, FLOAT, FOR, FOREIGN, FROM, FULL,
@@ -295,7 +303,11 @@ Oid	param_type(int t); /* used in parse_expr.c */
 		WHEN, WHERE, WITH, WORK, YEAR_P, ZONE
 
 /* Keywords (in SQL3 reserved words) */
-%token	TRIGGER
+%token	DEFERRABLE, DEFERRED,
+		IMMEDIATE, INITIALLY,
+		PENDANT,
+		RESTRICT,
+		TRIGGER
 
 /* Keywords (in SQL92 non-reserved words) */
 %token	COMMITTED, SERIALIZABLE, TYPE_P
@@ -415,6 +427,7 @@ stmt :	  AddAttrStmt
 		| VariableSetStmt
 		| VariableShowStmt
 		| VariableResetStmt
+		| ConstraintsSetStmt
 		;
 
 /*****************************************************************************
@@ -630,6 +643,49 @@ VariableResetStmt:	RESET ColId
 		;
 
 
+ConstraintsSetStmt:	SET CONSTRAINTS constraints_set_list constraints_set_mode
+				{
+					ConstraintsSetStmt *n = makeNode(ConstraintsSetStmt);
+					n->constraints = $3;
+					n->deferred    = $4;
+					$$ = (Node *) n;
+				}
+		;
+
+
+constraints_set_list:	ALL
+				{
+					$$ = NIL;
+				}
+		| constraints_set_namelist
+				{
+					$$ = $1;
+				}
+		;
+
+
+constraints_set_namelist:	IDENT
+				{
+					$$ = lappend(NIL, $1);
+				}
+		| constraints_set_namelist ',' IDENT
+				{
+					$$ = lappend($1, $3);
+				}
+		;
+
+
+constraints_set_mode:	DEFERRED
+				{
+					$$ = true;
+				}
+		| IMMEDIATE
+				{
+					$$ = false;
+				}
+		;
+
+
 /*****************************************************************************
  *
  *		QUERY :
@@ -1434,6 +1490,54 @@ CreateTrigStmt:  CREATE TRIGGER name TriggerActionTime TriggerEvents ON
 					n->before = $4;
 					n->row = $8;
 					memcpy (n->actions, $5, 4);
+					n->lang = NULL;		/* unused */
+					n->text = NULL;		/* unused */
+					n->attr = NULL;		/* unused */
+					n->when = NULL;		/* unused */
+
+					n->isconstraint  = false;
+					n->deferrable    = false;
+					n->initdeferred  = false;
+					n->constrrelname = NULL;
+					$$ = (Node *)n;
+				}
+		| CREATE CONSTRAINT TRIGGER name AFTER TriggerOneEvent ON
+				relation_name OptConstrFromTable 
+				OptConstrTrigDeferrable OptConstrTrigInitdeferred
+				FOR EACH ROW EXECUTE PROCEDURE name '(' TriggerFuncArgs ')'
+				{
+					CreateTrigStmt *n = makeNode(CreateTrigStmt);
+					n->trigname = $4;
+					n->relname = $8;
+					n->funcname = $17;
+					n->args = $19;
+					n->before = false;
+					n->row = true;
+					n->actions[0] = $6;
+					n->actions[1] = '\0';
+					n->lang = NULL;		/* unused */
+					n->text = NULL;		/* unused */
+					n->attr = NULL;		/* unused */
+					n->when = NULL;		/* unused */
+
+					/*
+					 * Check that the DEFERRABLE and INITIALLY combination
+					 * makes sense
+					 */
+					n->isconstraint  = true;
+					if ($11 == 1)
+					{
+						if ($10 == 0)
+							elog(ERROR, "INITIALLY DEFERRED constraint "
+										"cannot be NOT DEFERRABLE");
+						n->deferrable = true;
+						n->initdeferred = true;
+					} else {
+						n->deferrable = ($10 == 1);
+						n->initdeferred = false;
+					}
+
+					n->constrrelname = $9;
 					$$ = (Node *)n;
 				}
 		;
@@ -1503,6 +1607,44 @@ TriggerFuncArg:  ICONST
 			| IDENT							{  $$ = $1; }
 		;
 
+OptConstrFromTable:			/* Empty */
+				{
+					$$ = "";
+				}
+		| FROM relation_name
+				{
+					$$ = $2;
+				}
+		;
+
+OptConstrTrigDeferrable:	/* Empty */
+				{
+					$$ = -1;
+				}
+		| DEFERRABLE
+				{
+					$$ = 1;
+				}
+		| NOT DEFERRABLE
+				{
+					$$ = 0;
+				}
+		;
+
+OptConstrTrigInitdeferred:	/* Empty */
+				{
+					$$ = -1;
+				}
+		| INITIALLY DEFERRED
+				{
+					$$ = 1;
+				}
+		| INITIALLY IMMEDIATE
+				{
+					$$ = 0;
+				}
+		;
+
 DropTrigStmt:  DROP TRIGGER name ON relation_name
 				{
 					DropTrigStmt *n = makeNode(DropTrigStmt);
@@ -5080,10 +5222,13 @@ ColId:  IDENT							{ $$ = $1; }
 		| BEFORE						{ $$ = "before"; }
 		| CACHE							{ $$ = "cache"; }
 		| COMMITTED						{ $$ = "committed"; }
+		| CONSTRAINTS					{ $$ = "constraints"; }
 		| CREATEDB						{ $$ = "createdb"; }
 		| CREATEUSER					{ $$ = "createuser"; }
 		| CYCLE							{ $$ = "cycle"; }
 		| DATABASE						{ $$ = "database"; }
+		| DEFERRABLE					{ $$ = "deferrable"; }
+		| DEFERRED						{ $$ = "deferred"; }
 		| DELIMITERS					{ $$ = "delimiters"; }
 		| DOUBLE						{ $$ = "double"; }
 		| EACH							{ $$ = "each"; }
@@ -5092,9 +5237,11 @@ ColId:  IDENT							{ $$ = $1; }
 		| FORWARD						{ $$ = "forward"; }
 		| FUNCTION						{ $$ = "function"; }
 		| HANDLER						{ $$ = "handler"; }
+		| IMMEDIATE						{ $$ = "immediate"; }
 		| INCREMENT						{ $$ = "increment"; }
 		| INDEX							{ $$ = "index"; }
 		| INHERITS						{ $$ = "inherits"; }
+		| INITIALLY						{ $$ = "initially"; }
 		| INSENSITIVE					{ $$ = "insensitive"; }
 		| INSTEAD						{ $$ = "instead"; }
 		| ISNULL						{ $$ = "isnull"; }
@@ -5119,12 +5266,14 @@ ColId:  IDENT							{ $$ = $1; }
 		| OPERATOR						{ $$ = "operator"; }
 		| OPTION						{ $$ = "option"; }
 		| PASSWORD						{ $$ = "password"; }
+		| PENDANT						{ $$ = "pendant"; }
 		| PRIOR							{ $$ = "prior"; }
 		| PRIVILEGES					{ $$ = "privileges"; }
 		| PROCEDURAL					{ $$ = "procedural"; }
 		| READ							{ $$ = "read"; }
 		| RELATIVE						{ $$ = "relative"; }
 		| RENAME						{ $$ = "rename"; }
+		| RESTRICT						{ $$ = "restrict"; }
 		| RETURNS						{ $$ = "returns"; }
 		| ROW							{ $$ = "row"; }
 		| RULE							{ $$ = "rule"; }
diff --git a/src/backend/parser/keywords.c b/src/backend/parser/keywords.c
index 17a521337e40ce91cc0915a2d105b08ad6490920..3e30a47d12a9b12085f839700f09e064979fb980 100644
--- a/src/backend/parser/keywords.c
+++ b/src/backend/parser/keywords.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/parser/keywords.c,v 1.61 1999/09/23 17:02:46 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/keywords.c,v 1.62 1999/09/29 16:06:08 wieck Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -63,6 +63,7 @@ static ScanKeyword ScanKeywords[] = {
 	{"commit", COMMIT},
 	{"committed", COMMITTED},
 	{"constraint", CONSTRAINT},
+	{"constraints", CONSTRAINTS},
 	{"copy", COPY},
 	{"create", CREATE},
 	{"createdb", CREATEDB},
@@ -79,6 +80,8 @@ static ScanKeyword ScanKeywords[] = {
 	{"decimal", DECIMAL},
 	{"declare", DECLARE},
 	{"default", DEFAULT},
+	{"deferrable", DEFERRABLE},
+	{"deferred", DEFERRED},
 	{"delete", DELETE},
 	{"delimiters", DELIMITERS},
 	{"desc", DESC},
@@ -112,10 +115,12 @@ static ScanKeyword ScanKeywords[] = {
 	{"handler", HANDLER},
 	{"having", HAVING},
 	{"hour", HOUR_P},
+	{"immediate", IMMEDIATE},
 	{"in", IN},
 	{"increment", INCREMENT},
 	{"index", INDEX},
 	{"inherits", INHERITS},
+	{"initially", INITIALLY},
 	{"inner", INNER_P},
 	{"insensitive", INSENSITIVE},
 	{"insert", INSERT},
@@ -177,6 +182,7 @@ static ScanKeyword ScanKeywords[] = {
 	{"outer", OUTER_P},
 	{"partial", PARTIAL},
 	{"password", PASSWORD},
+	{"pendant", PENDANT},
 	{"position", POSITION},
 	{"precision", PRECISION},
 	{"primary", PRIMARY},
@@ -190,6 +196,7 @@ static ScanKeyword ScanKeywords[] = {
 	{"relative", RELATIVE},
 	{"rename", RENAME},
 	{"reset", RESET},
+	{"restrict", RESTRICT},
 	{"returns", RETURNS},
 	{"revoke", REVOKE},
 	{"right", RIGHT},
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 4947b2913752b53a6fb23825e2316b5098dcc00d..2a3e7e77300ae222f67dca9fdb204aa4ce37f4cd 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.129 1999/09/24 00:24:52 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.130 1999/09/29 16:06:10 wieck Exp $
  *
  * NOTES
  *	  this is the "main" module of the postgres backend and
@@ -44,6 +44,7 @@
 #endif
 
 #include "commands/async.h"
+#include "commands/trigger.h"
 #include "libpq/libpq.h"
 #include "libpq/pqformat.h"
 #include "libpq/pqsignal.h"
@@ -1484,9 +1485,16 @@ PostgresMain(int argc, char *argv[], int real_argc, char *real_argv[])
 	if (!IsUnderPostmaster)
 	{
 		puts("\nPOSTGRES backend interactive interface ");
-		puts("$Revision: 1.129 $ $Date: 1999/09/24 00:24:52 $\n");
+		puts("$Revision: 1.130 $ $Date: 1999/09/29 16:06:10 $\n");
 	}
 
+	/* ----------------
+	 * Initialize the deferred trigger manager
+	 * ----------------
+	 */
+	if (DeferredTriggerInit() != 0)
+		ExitPostgres(1);
+
 	/* ----------------
 	 *	POSTGRES main processing loop begins here
 	 *
@@ -1609,6 +1617,12 @@ PostgresMain(int argc, char *argv[], int real_argc, char *real_argv[])
 
 					pg_exec_query(parser_input->data);
 
+					/*
+					 * Invoke IMMEDIATE constraint triggers
+					 *
+					 */
+					DeferredTriggerEndQuery();
+
 					if (ShowStats)
 						ShowUsage();
 				}
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 9a5b7935db8874a5bd95b1c3457525c758a5db5f..18609201169b324827101573a4df19eed331acfd 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.67 1999/09/27 15:47:54 vadim Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.68 1999/09/29 16:06:11 wieck Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -785,6 +785,13 @@ ProcessUtility(Node *parsetree,
 			LockTableCommand((LockStmt *) parsetree);
 			break;
 
+		case T_ConstraintsSetStmt:
+			PS_SET_STATUS(commandTag = "SET CONSTRAINTS");
+			CHECK_IF_ABORTED();
+
+			DeferredTriggerSetState((ConstraintsSetStmt *) parsetree);
+			break;
+
 
 			/*
 			 * ******************************** default ********************************
diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h
index 698342d0815d81b75969cdebf616c3cea4398d8e..39bc8193da2f14e12a2416ecbb952a2f1755020d 100644
--- a/src/include/catalog/indexing.h
+++ b/src/include/catalog/indexing.h
@@ -7,7 +7,7 @@
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- * $Id: indexing.h,v 1.23 1999/07/20 17:14:07 momjian Exp $
+ * $Id: indexing.h,v 1.24 1999/09/29 16:06:14 wieck Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -25,27 +25,29 @@
 #define Num_pg_class_indices	2
 #define Num_pg_attrdef_indices	1
 #define Num_pg_relcheck_indices 1
-#define Num_pg_trigger_indices	1
+#define Num_pg_trigger_indices	3
 #define Num_pg_description_indices	1
 
 
 /*
  * Names of indices on system catalogs
  */
-#define AttributeNameIndex "pg_attribute_relid_attnam_index"
-#define AttributeNumIndex  "pg_attribute_relid_attnum_index"
-#define AttributeRelidIndex "pg_attribute_attrelid_index"
-#define ProcedureOidIndex  "pg_proc_oid_index"
-#define ProcedureNameIndex "pg_proc_proname_narg_type_index"
-#define ProcedureSrcIndex  "pg_proc_prosrc_index"
-#define TypeOidIndex	   "pg_type_oid_index"
-#define TypeNameIndex	   "pg_type_typname_index"
-#define ClassOidIndex	   "pg_class_oid_index"
-#define ClassNameIndex	   "pg_class_relname_index"
-#define AttrDefaultIndex   "pg_attrdef_adrelid_index"
-#define RelCheckIndex	   "pg_relcheck_rcrelid_index"
-#define TriggerRelidIndex  "pg_trigger_tgrelid_index"
-#define DescriptionObjIndex "pg_description_objoid_index"
+#define AttributeNameIndex		"pg_attribute_relid_attnam_index"
+#define AttributeNumIndex		"pg_attribute_relid_attnum_index"
+#define AttributeRelidIndex		"pg_attribute_attrelid_index"
+#define ProcedureOidIndex		"pg_proc_oid_index"
+#define ProcedureNameIndex		"pg_proc_proname_narg_type_index"
+#define ProcedureSrcIndex		"pg_proc_prosrc_index"
+#define TypeOidIndex			"pg_type_oid_index"
+#define TypeNameIndex			"pg_type_typname_index"
+#define ClassOidIndex			"pg_class_oid_index"
+#define ClassNameIndex			"pg_class_relname_index"
+#define AttrDefaultIndex		"pg_attrdef_adrelid_index"
+#define RelCheckIndex			"pg_relcheck_rcrelid_index"
+#define TriggerRelidIndex		"pg_trigger_tgrelid_index"
+#define TriggerConstrNameIndex	"pg_trigger_tgconstrname_index"
+#define TriggerConstrRelidIndex	"pg_trigger_tgconstrrelid_index"
+#define DescriptionObjIndex		"pg_description_objoid_index"
 
 extern char *Name_pg_attr_indices[];
 extern char *Name_pg_proc_indices[];
@@ -114,6 +116,8 @@ DECLARE_INDEX(pg_attrdef_adrelid_index on pg_attrdef using btree(adrelid oid_ops
 DECLARE_INDEX(pg_relcheck_rcrelid_index on pg_relcheck using btree(rcrelid oid_ops));
 
 DECLARE_INDEX(pg_trigger_tgrelid_index on pg_trigger using btree(tgrelid oid_ops));
+DECLARE_INDEX(pg_trigger_tgconstrname_index on pg_trigger using btree(tgconstrname name_ops));
+DECLARE_INDEX(pg_trigger_tgconstrrelid_index on pg_trigger using btree(tgconstrrelid oid_ops));
 
 DECLARE_INDEX(pg_description_objoid_index on pg_description using btree(objoid oid_ops));
 
diff --git a/src/include/catalog/pg_attribute.h b/src/include/catalog/pg_attribute.h
index a02cc2003366dfa93eaabb687d52927909d3516d..ae2067988daf9ac347130b4b4bd21dc90ece4bae 100644
--- a/src/include/catalog/pg_attribute.h
+++ b/src/include/catalog/pg_attribute.h
@@ -7,7 +7,7 @@
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- * $Id: pg_attribute.h,v 1.49 1999/08/09 02:45:56 tgl Exp $
+ * $Id: pg_attribute.h,v 1.50 1999/09/29 16:06:16 wieck Exp $
  *
  * NOTES
  *	  the genbki.sh script reads this file and generates .bki
@@ -447,9 +447,16 @@ DATA(insert OID = 0 ( 1219 tgrelid			26 0  4   1 0 -1 -1 t f i f f));
 DATA(insert OID = 0 ( 1219 tgname			19 0  NAMEDATALEN  2 0 -1 -1 f f i f f));
 DATA(insert OID = 0 ( 1219 tgfoid			26 0  4   3 0 -1 -1 t f i f f));
 DATA(insert OID = 0 ( 1219 tgtype			21 0  2   4 0 -1 -1 t f s f f));
-DATA(insert OID = 0 ( 1219 tgnargs			21 0  2   5 0 -1 -1 t f s f f));
-DATA(insert OID = 0 ( 1219 tgattr			22 0 16   6 0 -1 -1 f f i f f));
-DATA(insert OID = 0 ( 1219 tgargs			17 0 -1   7 0 -1 -1 f f i f f));
+DATA(insert OID = 0 ( 1219 tgenabled		16 0  1   5 0 -1 -1 t f c f f));
+DATA(insert OID = 0 ( 1219 tgisconstraint	16 0  1   6 0 -1 -1 t f c f f));
+DATA(insert OID = 0 ( 1219 tgconstrname		19 0  NAMEDATALEN  7 0 -1 -1 f f i f f));
+DATA(insert OID = 0 ( 1219 tgconstrrelid	26 0  4   8 0 -1 -1 t f i f f));
+
+DATA(insert OID = 0 ( 1219 tgdeferrable		16 0  1   9 0 -1 -1 t f c f f));
+DATA(insert OID = 0 ( 1219 tginitdeferred	16 0  1   10 0 -1 -1 t f c f f));
+DATA(insert OID = 0 ( 1219 tgnargs			21 0  2   11 0 -1 -1 t f s f f));
+DATA(insert OID = 0 ( 1219 tgattr			22 0 16   12 0 -1 -1 f f i f f));
+DATA(insert OID = 0 ( 1219 tgargs			17 0 -1   13 0 -1 -1 f f i f f));
 DATA(insert OID = 0 ( 1219 ctid				27 0  6  -1 0 -1 -1 f f i f f));
 DATA(insert OID = 0 ( 1219 oid				26 0  4  -2 0 -1 -1 t f i f f));
 DATA(insert OID = 0 ( 1219 xmin				28 0  4  -3 0 -1 -1 t f i f f));
diff --git a/src/include/catalog/pg_class.h b/src/include/catalog/pg_class.h
index 5647707a20459c19ee2184bfe303e4c3c90e3ef0..7bf6929d6ce06ed6f00f901426b8418aab0c234f 100644
--- a/src/include/catalog/pg_class.h
+++ b/src/include/catalog/pg_class.h
@@ -7,7 +7,7 @@
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- * $Id: pg_class.h,v 1.29 1999/05/25 16:13:44 momjian Exp $
+ * $Id: pg_class.h,v 1.30 1999/09/29 16:06:16 wieck Exp $
  *
  * NOTES
  *	  ``pg_relation'' is being replaced by ``pg_class''.  currently
@@ -150,7 +150,7 @@ DATA(insert OID = 1215 (  pg_attrdef 109	  PGUID 0 0 0 t t r 4  0 0 0 0 0 f f _n
 DESCR("");
 DATA(insert OID = 1216 (  pg_relcheck 110	  PGUID 0 0 0 t t r 4  0 0 0 0 0 f f _null_ ));
 DESCR("");
-DATA(insert OID = 1219 (  pg_trigger 111	  PGUID 0 0 0 t t r 7  0 0 0 0 0 f f _null_ ));
+DATA(insert OID = 1219 (  pg_trigger 111	  PGUID 0 0 0 t t r 13  0 0 0 0 0 f f _null_ ));
 DESCR("");
 
 #define RelOid_pg_type			1247
diff --git a/src/include/catalog/pg_trigger.h b/src/include/catalog/pg_trigger.h
index 2d12e73da5fab81e2734044b4faebec01993ec73..be0633eb9859579f28696de4ec8a3343b784317d 100644
--- a/src/include/catalog/pg_trigger.h
+++ b/src/include/catalog/pg_trigger.h
@@ -33,6 +33,13 @@ CATALOG(pg_trigger) BOOTSTRAP
 	Oid			tgfoid;			/* OID of function to be called */
 	int2		tgtype;			/* BEFORE/AFTER UPDATE/DELETE/INSERT
 								 * ROW/STATEMENT */
+	bool		tgenabled;		/* trigger is enabled/disabled */
+	bool		tgisconstraint;	/* trigger is a RI constraint */
+	NameData	tgconstrname;	/* RI constraint name */
+	Oid			tgconstrrelid;	/* RI table of foreign key definition */
+								/* in the case of ON DELETE or ON UPDATE */
+	bool		tgdeferrable;	/* RI trigger is deferrable */
+	bool		tginitdeferred;	/* RI trigger is deferred initially */
 	int2		tgnargs;		/* # of extra arguments in tgargs */
 	int28		tgattr;			/* UPDATE of attr1, attr2 ... (NI) */
 	bytea		tgargs;			/* first\000second\000tgnargs\000 */
@@ -49,14 +56,20 @@ typedef FormData_pg_trigger *Form_pg_trigger;
  *		compiler constants for pg_trigger
  * ----------------
  */
-#define Natts_pg_trigger				7
+#define Natts_pg_trigger				13
 #define Anum_pg_trigger_tgrelid			1
 #define Anum_pg_trigger_tgname			2
 #define Anum_pg_trigger_tgfoid			3
 #define Anum_pg_trigger_tgtype			4
-#define Anum_pg_trigger_tgnargs			5
-#define Anum_pg_trigger_tgattr			6
-#define Anum_pg_trigger_tgargs			7
+#define Anum_pg_trigger_tgenabled		5
+#define Anum_pg_trigger_tgisconstraint	6
+#define Anum_pg_trigger_tgconstrname	7
+#define Anum_pg_trigger_tgconstrrelid	8
+#define Anum_pg_trigger_tgdeferrable	9
+#define Anum_pg_trigger_tginitdeferred	10
+#define Anum_pg_trigger_tgnargs			11
+#define Anum_pg_trigger_tgattr			12
+#define Anum_pg_trigger_tgargs			13
 
 #define TRIGGER_TYPE_ROW				(1 << 0)
 #define TRIGGER_TYPE_BEFORE				(1 << 1)
diff --git a/src/include/commands/trigger.h b/src/include/commands/trigger.h
index 99350d55d1348cae02d58e5715d0941bcab0855b..8ab4342fa7ae31d45eb1c90d48a961d9cdcd9cf5 100644
--- a/src/include/commands/trigger.h
+++ b/src/include/commands/trigger.h
@@ -32,6 +32,13 @@ extern DLLIMPORT TriggerData *CurrentTriggerData;
 #define TRIGGER_EVENT_ROW				0x00000004
 #define TRIGGER_EVENT_BEFORE			0x00000008
 
+#define TRIGGER_DEFERRED_DONE			0x00000010
+#define TRIGGER_DEFERRED_CANCELED		0x00000020
+#define TRIGGER_DEFERRED_DEFERRABLE		0x00000040
+#define TRIGGER_DEFERRED_INITDEFERRED	0x00000080
+#define TRIGGER_DEFERRED_HAS_BEFORE		0x00000100
+#define TRIGGER_DEFERRED_MASK			0x000001F0
+
 #define TRIGGER_FIRED_BY_INSERT(event)	\
 		(((TriggerEvent) (event) & TRIGGER_EVENT_OPMASK) == \
 												TRIGGER_EVENT_INSERT)
@@ -68,4 +75,46 @@ extern void ExecARDeleteTriggers(EState *estate, ItemPointer tupleid);
 extern HeapTuple ExecBRUpdateTriggers(EState *estate, ItemPointer tupleid, HeapTuple tuple);
 extern void ExecARUpdateTriggers(EState *estate, ItemPointer tupleid, HeapTuple tuple);
 
+
+
+/* ----------
+ * Deferred trigger stuff
+ * ----------
+ */
+typedef struct DeferredTriggerStatusData {
+	Oid				dts_tgoid;
+	bool			dts_tgisdeferred;
+} DeferredTriggerStatusData;
+typedef struct DeferredTriggerStatusData *DeferredTriggerStatus;
+
+
+typedef struct DeferredTriggerEventItem {
+	Oid				dti_tgoid;
+	int32			dti_state;
+} DeferredTriggerEventItem;
+
+
+typedef struct DeferredTriggerEventData {
+	int32			dte_event;
+	Oid				dte_relid;
+	ItemPointerData	dte_oldctid;
+	ItemPointerData	dte_newctid;
+	int32			dte_n_items;
+	DeferredTriggerEventItem	dte_item[1];
+} DeferredTriggerEventData;
+typedef struct DeferredTriggerEventData *DeferredTriggerEvent;
+
+
+extern int  DeferredTriggerInit(void);
+extern void DeferredTriggerBeginXact(void);
+extern void DeferredTriggerEndQuery(void);
+extern void DeferredTriggerEndXact(void);
+extern void DeferredTriggerAbortXact(void);
+
+extern void DeferredTriggerSetState(ConstraintsSetStmt *stmt);
+
+extern void DeferredTriggerSaveEvent(Relation rel, int event,
+					HeapTuple oldtup, HeapTuple newtup);
+
+
 #endif	 /* TRIGGER_H */
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index da8a06af42393cc0e31cb708ff23204bd22d9023..68d824732447feb70abdf2ab05a5a22551bedd2b 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -6,7 +6,7 @@
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- * $Id: nodes.h,v 1.52 1999/09/23 17:03:21 momjian Exp $
+ * $Id: nodes.h,v 1.53 1999/09/29 16:06:23 wieck Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -184,6 +184,7 @@ typedef enum NodeTag
 	T_AlterUserStmt,
 	T_DropUserStmt,
 	T_LockStmt,
+	T_ConstraintsSetStmt,
 
 	T_A_Expr = 700,
 	T_Attr,
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 4bf879137ac26436a34cf149531eda6dbf0a1433..a736c1af67e79a4274eb178d851fc617d1d18fd2 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -6,7 +6,7 @@
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- * $Id: parsenodes.h,v 1.80 1999/09/28 04:34:50 momjian Exp $
+ * $Id: parsenodes.h,v 1.81 1999/09/29 16:06:23 wieck Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -180,6 +180,13 @@ typedef struct CreateTrigStmt
 	char	   *text;			/* AS 'text' */
 	List	   *attr;			/* UPDATE OF a, b,... (NI) or NULL */
 	char	   *when;			/* WHEN 'a > 10 ...' (NI) or NULL */
+
+								/* The following are used for referential */
+								/* integrity constraint triggers */
+	bool		isconstraint;	/* This is an RI trigger */
+	bool		deferrable;		/* [NOT] DEFERRABLE */
+	bool		initdeferred;	/* INITIALLY {DEFERRED|IMMEDIATE} */
+	char	   *constrrelname;	/* opposite relation */
 } CreateTrigStmt;
 
 typedef struct DropTrigStmt
@@ -602,6 +609,19 @@ typedef struct LockStmt
 	int			mode;			/* lock mode */
 } LockStmt;
 
+
+/* ----------------------
+ *		SET CONSTRAINTS Statement
+ * ----------------------
+ */
+typedef struct ConstraintsSetStmt
+{
+	NodeTag		type;
+	List		*constraints;
+	bool		deferred;
+} ConstraintsSetStmt;
+
+
 /*****************************************************************************
  *		Optimizable Statements
  *****************************************************************************/
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index f2789b39e09d15717749598430fe8049f47d09aa..308f9a6d645bb036ee9553b5e131b0acb6de90d5 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -6,7 +6,7 @@
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- * $Id: rel.h,v 1.26 1999/09/18 19:08:25 tgl Exp $
+ * $Id: rel.h,v 1.27 1999/09/29 16:06:28 wieck Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -42,10 +42,15 @@ typedef LockInfoData *LockInfo;
 
 typedef struct Trigger
 {
+	Oid			tgoid;
 	char	   *tgname;
 	Oid			tgfoid;
 	FmgrInfo	tgfunc;
 	int16		tgtype;
+	bool		tgenabled;
+	bool		tgisconstraint;
+	bool		tgdeferrable;
+	bool		tginitdeferred;
 	int16		tgnargs;
 	int16		tgattr[8];
 	char	  **tgargs;
diff --git a/src/include/utils/tqual.h b/src/include/utils/tqual.h
index 4cb685968b167012d5df6d080d3f1744e7c18129..d8aa6638f6aa109856c4465acced6560bf872ac0 100644
--- a/src/include/utils/tqual.h
+++ b/src/include/utils/tqual.h
@@ -7,7 +7,7 @@
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- * $Id: tqual.h,v 1.24 1999/07/15 23:04:24 momjian Exp $
+ * $Id: tqual.h,v 1.25 1999/09/29 16:06:28 wieck Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -30,6 +30,7 @@ typedef SnapshotData *Snapshot;
 
 #define SnapshotNow					((Snapshot) 0x0)
 #define SnapshotSelf				((Snapshot) 0x1)
+#define SnapshotAny					((Snapshot) 0x2)
 
 extern Snapshot SnapshotDirty;
 extern Snapshot QuerySnapshot;
@@ -37,6 +38,7 @@ extern Snapshot SerializableSnapshot;
 
 #define IsSnapshotNow(snapshot)		((Snapshot) snapshot == SnapshotNow)
 #define IsSnapshotSelf(snapshot)	((Snapshot) snapshot == SnapshotSelf)
+#define IsSnapshotAny(snapshot)		((Snapshot) snapshot == SnapshotAny)
 #define IsSnapshotDirty(snapshot)	((Snapshot) snapshot == SnapshotDirty)
 
 extern TransactionId HeapSpecialTransactionId;
@@ -55,18 +57,22 @@ extern CommandId HeapSpecialCommandId;
 		false \
 	: \
 	( \
-		(IsSnapshotSelf(snapshot) || heapisoverride()) ? \
-			HeapTupleSatisfiesItself((tuple)->t_data) \
+		(IsSnapshotAny(snapshot) || heapisoverride()) ? \
+			true \
 		: \
-			((IsSnapshotDirty(snapshot)) ? \
-				HeapTupleSatisfiesDirty((tuple)->t_data) \
+			((IsSnapshotSelf(snapshot) || heapisoverride()) ? \
+				HeapTupleSatisfiesItself((tuple)->t_data) \
 			: \
-				((IsSnapshotNow(snapshot)) ? \
-					HeapTupleSatisfiesNow((tuple)->t_data) \
+				((IsSnapshotDirty(snapshot)) ? \
+					HeapTupleSatisfiesDirty((tuple)->t_data) \
 				: \
-					HeapTupleSatisfiesSnapshot((tuple)->t_data, snapshot) \
-				) \
+					((IsSnapshotNow(snapshot)) ? \
+						HeapTupleSatisfiesNow((tuple)->t_data) \
+					: \
+						HeapTupleSatisfiesSnapshot((tuple)->t_data, snapshot) \
+					) \
 			) \
+		) \
 	) \
 )
 
diff --git a/src/test/regress/expected/triggers.out b/src/test/regress/expected/triggers.out
index c4cf5b12b0f549dd0bb47f266290f0cc576bd9f7..ddb2d795a57862dd6a02c6925b90bccc2cee1d82 100644
--- a/src/test/regress/expected/triggers.out
+++ b/src/test/regress/expected/triggers.out
@@ -64,101 +64,6 @@ NOTICE:  check_pkeys_fkey_cascade: 1 tuple(s) of fkeys2 are deleted
 QUERY: DROP TABLE pkeys;
 QUERY: DROP TABLE fkeys;
 QUERY: DROP TABLE fkeys2;
-QUERY: create table dup17 (x int4);
-QUERY: create trigger dup17_before
-	before insert on dup17
-	for each row
-	execute procedure
-	funny_dup17 ()
-;
-QUERY: insert into dup17 values (17);
-NOTICE:  funny_dup17 (fired BEFORE) on level   1: 0/0 tuples inserted/selected
-QUERY: select count(*) from dup17;
-count
------
-    1
-(1 row)
-
-QUERY: insert into dup17 values (17);
-NOTICE:  funny_dup17 (fired BEFORE) on level  17: 1/2 tuples inserted/selected
-NOTICE:  funny_dup17 (fired BEFORE) on level  16: 1/3 tuples inserted/selected
-NOTICE:  funny_dup17 (fired BEFORE) on level  15: 1/4 tuples inserted/selected
-NOTICE:  funny_dup17 (fired BEFORE) on level  14: 1/5 tuples inserted/selected
-NOTICE:  funny_dup17 (fired BEFORE) on level  13: 1/6 tuples inserted/selected
-NOTICE:  funny_dup17 (fired BEFORE) on level  12: 1/7 tuples inserted/selected
-NOTICE:  funny_dup17 (fired BEFORE) on level  11: 1/8 tuples inserted/selected
-NOTICE:  funny_dup17 (fired BEFORE) on level  10: 1/9 tuples inserted/selected
-NOTICE:  funny_dup17 (fired BEFORE) on level   9: 1/10 tuples inserted/selected
-NOTICE:  funny_dup17 (fired BEFORE) on level   8: 1/11 tuples inserted/selected
-NOTICE:  funny_dup17 (fired BEFORE) on level   7: 1/12 tuples inserted/selected
-NOTICE:  funny_dup17 (fired BEFORE) on level   6: 1/13 tuples inserted/selected
-NOTICE:  funny_dup17 (fired BEFORE) on level   5: 1/14 tuples inserted/selected
-NOTICE:  funny_dup17 (fired BEFORE) on level   4: 1/15 tuples inserted/selected
-NOTICE:  funny_dup17 (fired BEFORE) on level   3: 1/16 tuples inserted/selected
-NOTICE:  funny_dup17 (fired BEFORE) on level   2: 1/17 tuples inserted/selected
-NOTICE:  funny_dup17 (fired BEFORE) on level   1: 1/18 tuples inserted/selected
-QUERY: select count(*) from dup17;
-count
------
-   19
-(1 row)
-
-QUERY: drop trigger dup17_before on dup17;
-QUERY: create trigger dup17_after
-	after insert on dup17
-	for each row
-	execute procedure
-	funny_dup17 ()
-;
-QUERY: insert into dup17 values (13);
-NOTICE:  funny_dup17 (fired AFTER ) on level  17: 17/34 tuples inserted/selected
-NOTICE:  funny_dup17 (fired AFTER ) on level  16: 16/49 tuples inserted/selected
-NOTICE:  funny_dup17 (fired AFTER ) on level  15: 15/63 tuples inserted/selected
-NOTICE:  funny_dup17 (fired AFTER ) on level  14: 14/76 tuples inserted/selected
-NOTICE:  funny_dup17 (fired AFTER ) on level  13: 13/88 tuples inserted/selected
-NOTICE:  funny_dup17 (fired AFTER ) on level  12: 12/99 tuples inserted/selected
-NOTICE:  funny_dup17 (fired AFTER ) on level  11: 11/109 tuples inserted/selected
-NOTICE:  funny_dup17 (fired AFTER ) on level  10: 10/118 tuples inserted/selected
-NOTICE:  funny_dup17 (fired AFTER ) on level   9: 9/126 tuples inserted/selected
-NOTICE:  funny_dup17 (fired AFTER ) on level   8: 8/133 tuples inserted/selected
-NOTICE:  funny_dup17 (fired AFTER ) on level   7: 7/139 tuples inserted/selected
-NOTICE:  funny_dup17 (fired AFTER ) on level   6: 6/144 tuples inserted/selected
-NOTICE:  funny_dup17 (fired AFTER ) on level   5: 5/148 tuples inserted/selected
-NOTICE:  funny_dup17 (fired AFTER ) on level   4: 4/151 tuples inserted/selected
-NOTICE:  funny_dup17 (fired AFTER ) on level   3: 3/153 tuples inserted/selected
-NOTICE:  funny_dup17 (fired AFTER ) on level   2: 2/154 tuples inserted/selected
-NOTICE:  funny_dup17 (fired AFTER ) on level   1: 1/154 tuples inserted/selected
-QUERY: select count(*) from dup17 where x = 13;
-count
------
-  154
-(1 row)
-
-QUERY: insert into dup17 values (13);
-NOTICE:  funny_dup17 (fired AFTER ) on level  17: 171/342 tuples inserted/selected
-NOTICE:  funny_dup17 (fired AFTER ) on level  16: 170/511 tuples inserted/selected
-NOTICE:  funny_dup17 (fired AFTER ) on level  15: 169/679 tuples inserted/selected
-NOTICE:  funny_dup17 (fired AFTER ) on level  14: 168/846 tuples inserted/selected
-NOTICE:  funny_dup17 (fired AFTER ) on level  13: 167/1012 tuples inserted/selected
-NOTICE:  funny_dup17 (fired AFTER ) on level  12: 166/1177 tuples inserted/selected
-NOTICE:  funny_dup17 (fired AFTER ) on level  11: 165/1341 tuples inserted/selected
-NOTICE:  funny_dup17 (fired AFTER ) on level  10: 164/1504 tuples inserted/selected
-NOTICE:  funny_dup17 (fired AFTER ) on level   9: 163/1666 tuples inserted/selected
-NOTICE:  funny_dup17 (fired AFTER ) on level   8: 162/1827 tuples inserted/selected
-NOTICE:  funny_dup17 (fired AFTER ) on level   7: 161/1987 tuples inserted/selected
-NOTICE:  funny_dup17 (fired AFTER ) on level   6: 160/2146 tuples inserted/selected
-NOTICE:  funny_dup17 (fired AFTER ) on level   5: 159/2304 tuples inserted/selected
-NOTICE:  funny_dup17 (fired AFTER ) on level   4: 158/2461 tuples inserted/selected
-NOTICE:  funny_dup17 (fired AFTER ) on level   3: 157/2617 tuples inserted/selected
-NOTICE:  funny_dup17 (fired AFTER ) on level   2: 156/2772 tuples inserted/selected
-NOTICE:  funny_dup17 (fired AFTER ) on level   1: 155/2926 tuples inserted/selected
-QUERY: select count(*) from dup17 where x = 13;
-count
------
- 2926
-(1 row)
-
-QUERY: DROP TABLE dup17;
 QUERY: create sequence ttdummy_seq increment 10 start 0 minvalue 0;
 QUERY: create table tttest (
 	price_id	int4,
diff --git a/src/test/regress/sql/triggers.sql b/src/test/regress/sql/triggers.sql
index f7d2c23a5ae85b6436f6ed45670d841ea03390b1..5740dcac9fb1b18a893e01c7d05bf7ff530efd52 100644
--- a/src/test/regress/sql/triggers.sql
+++ b/src/test/regress/sql/triggers.sql
@@ -88,34 +88,40 @@ DROP TABLE pkeys;
 DROP TABLE fkeys;
 DROP TABLE fkeys2;
 
-create table dup17 (x int4);
-
-create trigger dup17_before 
-	before insert on dup17
-	for each row 
-	execute procedure 
-	funny_dup17 ()
-;
-
-insert into dup17 values (17);
-select count(*) from dup17;
-insert into dup17 values (17);
-select count(*) from dup17;
-
-drop trigger dup17_before on dup17;
-
-create trigger dup17_after
-	after insert on dup17
-	for each row 
-	execute procedure 
-	funny_dup17 ()
-;
-insert into dup17 values (13);
-select count(*) from dup17 where x = 13;
-insert into dup17 values (13);
-select count(*) from dup17 where x = 13;
-
-DROP TABLE dup17;
+-- -- I've disabled the funny_dup17 test because the new semantics
+-- -- of AFTER ROW triggers, which get now fired at the end of a
+-- -- query allways, cause funny_dup17 to enter an endless loop.
+-- --
+-- --      Jan
+--
+-- create table dup17 (x int4);
+-- 
+-- create trigger dup17_before 
+-- 	before insert on dup17
+-- 	for each row 
+-- 	execute procedure 
+-- 	funny_dup17 ()
+-- ;
+-- 
+-- insert into dup17 values (17);
+-- select count(*) from dup17;
+-- insert into dup17 values (17);
+-- select count(*) from dup17;
+-- 
+-- drop trigger dup17_before on dup17;
+-- 
+-- create trigger dup17_after
+-- 	after insert on dup17
+-- 	for each row 
+-- 	execute procedure 
+-- 	funny_dup17 ()
+-- ;
+-- insert into dup17 values (13);
+-- select count(*) from dup17 where x = 13;
+-- insert into dup17 values (13);
+-- select count(*) from dup17 where x = 13;
+-- 
+-- DROP TABLE dup17;
 
 create sequence ttdummy_seq increment 10 start 0 minvalue 0;