This is part #1 for of the DEFERRED CONSTRAINT TRIGGER support.
authorJan Wieck
Wed, 29 Sep 1999 16:06:40 +0000 (16:06 +0000)
committerJan Wieck
Wed, 29 Sep 1999 16:06:40 +0000 (16:06 +0000)
Implements the CREATE CONSTRAINT TRIGGER and SET CONSTRAINTS commands.

TODO:
    Generic builtin trigger procedures
    Automatic execution of appropriate CREATE CONSTRAINT... at CREATE TABLE
    Support of new trigger type in pg_dump
    Swapping of huge # of events to disk

Jan

20 files changed:
src/backend/access/transam/xact.c
src/backend/catalog/heap.c
src/backend/catalog/indexing.c
src/backend/commands/trigger.c
src/backend/executor/execMain.c
src/backend/parser/gram.y
src/backend/parser/keywords.c
src/backend/tcop/postgres.c
src/backend/tcop/utility.c
src/include/catalog/indexing.h
src/include/catalog/pg_attribute.h
src/include/catalog/pg_class.h
src/include/catalog/pg_trigger.h
src/include/commands/trigger.h
src/include/nodes/nodes.h
src/include/nodes/parsenodes.h
src/include/utils/rel.h
src/include/utils/tqual.h
src/test/regress/expected/triggers.out
src/test/regress/sql/triggers.sql

index dc5bbcd32eb048e31a6828a544b815c62f9d5fa2..f468e2916b164cb7a9faebe1eba2e06ec0eaf0fe 100644 (file)
@@ -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:
 #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
index f1051cb784b74916e827c25d0c897283de2af499..5ad0f7c413b1965131588b5835c8843b20c0a5b7 100644 (file)
@@ -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
index 75799e55570fddc561657cf3d7a7c28aec1556be..3ac2ecc4d66619800c4360fe3c2ca8fb1ce21528 100644 (file)
@@ -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,
index aa7a0b56c1d0988e2e208b384db0ba2e8599496b..ce7d6f5a2d6e5fffc1cfc357d3fcb7326787bbfc 100644 (file)
@@ -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;
+}
+
+
index f07f8777a2f94b226e353d3746eaf8f959b6e5df..0943e4085becf2834885925ca494b0b2175c7a59 100644 (file)
@@ -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);
 }
 
index c4355de20b9f69b55c98aab3188223dbce2b4948..b6583197be3962902544901404896717d76d0f5f 100644 (file)
@@ -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         opt_database1, opt_database2, location, encoding
 
@@ -146,6 +147,9 @@ Oid param_type(int t); /* used in parse_expr.c */
 
 %type     TriggerActionTime, TriggerForSpec, PLangTrusted
 
+%type    OptConstrTrigDeferrable, OptConstrTrigInitdeferred
+%type         OptConstrFromTable
+
 %type         TriggerEvents, TriggerFuncArg
 
 %type         relation_name, copy_file_name, copy_delimiter, def_name,
@@ -254,6 +258,10 @@ Oid    param_type(int t); /* used in parse_expr.c */
 %type    key_actions, key_action
 %type         key_match, key_reference
 
+%type    constraints_set_list
+%type    constraints_set_namelist
+%type     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"; }
index 17a521337e40ce91cc0915a2d105b08ad6490920..3e30a47d12a9b12085f839700f09e064979fb980 100644 (file)
@@ -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},
index 4947b2913752b53a6fb23825e2316b5098dcc00d..2a3e7e77300ae222f67dca9fdb204aa4ce37f4cd 100644 (file)
@@ -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();
                }
index 9a5b7935db8874a5bd95b1c3457525c758a5db5f..18609201169b324827101573a4df19eed331acfd 100644 (file)
@@ -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 ********************************
index 698342d0815d81b75969cdebf616c3cea4398d8e..39bc8193da2f14e12a2416ecbb952a2f1755020d 100644 (file)
@@ -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 $
  *
  *-------------------------------------------------------------------------
  */
 #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));
 
index a02cc2003366dfa93eaabb687d52927909d3516d..ae2067988daf9ac347130b4b4bd21dc90ece4bae 100644 (file)
@@ -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));
index 5647707a20459c19ee2184bfe303e4c3c90e3ef0..7bf6929d6ce06ed6f00f901426b8418aab0c234f 100644 (file)
@@ -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
index 2d12e73da5fab81e2734044b4faebec01993ec73..be0633eb9859579f28696de4ec8a3343b784317d 100644 (file)
@@ -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)
index 99350d55d1348cae02d58e5715d0941bcab0855b..8ab4342fa7ae31d45eb1c90d48a961d9cdcd9cf5 100644 (file)
@@ -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 */
index da8a06af42393cc0e31cb708ff23204bd22d9023..68d824732447feb70abdf2ab05a5a22551bedd2b 100644 (file)
@@ -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,
index 4bf879137ac26436a34cf149531eda6dbf0a1433..a736c1af67e79a4274eb178d851fc617d1d18fd2 100644 (file)
@@ -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
  *****************************************************************************/
index f2789b39e09d15717749598430fe8049f47d09aa..308f9a6d645bb036ee9553b5e131b0acb6de90d5 100644 (file)
@@ -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;
index 4cb685968b167012d5df6d080d3f1744e7c18129..d8aa6638f6aa109856c4465acced6560bf872ac0 100644 (file)
@@ -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) \
+                   ) \
            ) \
+       ) \
    ) \
 )
 
index c4cf5b12b0f549dd0bb47f266290f0cc576bd9f7..ddb2d795a57862dd6a02c6925b90bccc2cee1d82 100644 (file)
@@ -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,
index f7d2c23a5ae85b6436f6ed45670d841ea03390b1..5740dcac9fb1b18a893e01c7d05bf7ff530efd52 100644 (file)
@@ -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;