OSDN Git Service

Before row insertion triggers call.
authorVadim B. Mikheev <vadim4o@yahoo.com>
Thu, 4 Sep 1997 13:19:01 +0000 (13:19 +0000)
committerVadim B. Mikheev <vadim4o@yahoo.com>
Thu, 4 Sep 1997 13:19:01 +0000 (13:19 +0000)
src/backend/commands/copy.c
src/backend/commands/trigger.c

index 4aa9e68..687cd1e 100644 (file)
@@ -6,7 +6,7 @@
  *
  *
  * IDENTIFICATION
- *    $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.28 1997/09/01 07:59:04 vadim Exp $
+ *    $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.29 1997/09/04 13:18:59 vadim Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -608,7 +608,7 @@ CopyFrom(Relation rel, bool binary, bool oids, FILE *fp, char *delim)
        skip_tuple = false;
        /* BEFORE ROW INSERT Triggers */
        if ( rel->trigdesc && 
-               rel->trigdesc->n_before_row[TRIGGER_ACTION_INSERT] > 0 )
+               rel->trigdesc->n_before_row[TRIGGER_EVENT_INSERT] > 0 )
        {
            HeapTuple newtuple;
        
@@ -677,7 +677,7 @@ CopyFrom(Relation rel, bool binary, bool oids, FILE *fp, char *delim)
             }
            /* AFTER ROW INSERT Triggers */
            if ( rel->trigdesc && 
-                   rel->trigdesc->n_after_row[TRIGGER_ACTION_INSERT] > 0 )
+                   rel->trigdesc->n_after_row[TRIGGER_EVENT_INSERT] > 0 )
                ExecARInsertTriggers (rel, tuple);
         }
         
index 2c07e49..2919df4 100644 (file)
@@ -9,7 +9,9 @@
 #include "postgres.h"
 
 #include "nodes/parsenodes.h"
+#include "nodes/memnodes.h"
 #include "commands/trigger.h"
+#include "catalog/catalog.h"
 #include "catalog/catname.h"
 #include "catalog/indexing.h"
 #include "catalog/pg_proc.h"
 #include "catalog/pg_trigger.h"
 #include "access/genam.h"
 #include "access/heapam.h"
+#include "access/xact.h"
+#include "storage/lmgr.h"
 #include "storage/bufmgr.h"
+#include "utils/mcxt.h"
+#include "utils/inval.h"
 #include "utils/builtins.h"
 
+#ifndef NO_SECURITY
+#include "miscadmin.h"
+#include "utils/acl.h"
+#include "utils/syscache.h"
+#endif
+
+TriggerData *CurrentTriggerData = NULL;
+
 void RelationBuildTriggers (Relation relation);
 void FreeTriggerDesc (Relation relation);
 
+static void DescribeTrigger (TriggerDesc *trigdesc, Trigger *trigger);
+
+extern void fmgr_info(Oid procedureId, func_ptr *function, int *nargs);
+extern GlobalMemory CacheCxt;
+
 void
 CreateTrigger (CreateTrigStmt *stmt)
 {
+    int16 tgtype;
+    int16 tgattr[8] = {0};
+    Datum values[Natts_pg_trigger];
+    char nulls[Natts_pg_trigger];
+    Relation rel;
+    Relation tgrel;
+    HeapScanDesc tgscan;
+    ScanKeyData        key;
+    Relation relrdesc;
+    HeapTuple tuple;
+    ItemPointerData oldTID;
+    Relation idescs[Num_pg_trigger_indices];
+    Relation ridescs[Num_pg_class_indices];
+    MemoryContext oldcxt;
+    Oid fargtypes[8];
+    int found = 0;
+    int i;
+    
+    if ( IsSystemRelationName (stmt->relname) )
+       elog(WARN, "CreateTrigger: can't create trigger for system relation %s", stmt->relname);
 
+#ifndef NO_SECURITY
+    if ( !pg_ownercheck (GetPgUserName (), stmt->relname, RELNAME))
+       elog(WARN, "%s: %s", stmt->relname, aclcheck_error_strings[ACLCHECK_NOT_OWNER]);
+#endif
+    
+    rel = heap_openr (stmt->relname);
+    if ( !RelationIsValid (rel) )
+       elog (WARN, "CreateTrigger: there is no relation %s", stmt->relname);
+    
+    RelationSetLockForWrite (rel);
+    
+    TRIGGER_CLEAR_TYPE (tgtype);
+    if ( stmt->before )
+       TRIGGER_SETT_BEFORE (tgtype);
+    if ( stmt->row )
+       TRIGGER_SETT_ROW (tgtype);
+    for (i = 0; i < 3 && stmt->actions[i]; i++)
+    {
+       switch ( stmt->actions[i] )
+       {
+           case 'i': 
+               if ( TRIGGER_FOR_INSERT (tgtype) )
+                   elog (WARN, "CreateTrigger: double INSERT event specified");
+               TRIGGER_SETT_INSERT (tgtype);
+               break;
+           case 'd': 
+               if ( TRIGGER_FOR_DELETE (tgtype) )
+                   elog (WARN, "CreateTrigger: double DELETE event specified");
+               TRIGGER_SETT_DELETE (tgtype);
+               break;
+           case 'u': 
+               if ( TRIGGER_FOR_UPDATE (tgtype) )
+                   elog (WARN, "CreateTrigger: double UPDATE event specified");
+               TRIGGER_SETT_UPDATE (tgtype);
+               break;
+           default: 
+               elog (WARN, "CreateTrigger: unknown event specified");
+               break;
+       }
+    }
+    
+    /* Scan pg_trigger */
+    tgrel = heap_openr (TriggerRelationName);
+    RelationSetLockForWrite (tgrel);
+    ScanKeyEntryInitialize(&key, 0, Anum_pg_trigger_tgrelid,
+                          ObjectIdEqualRegProcedure, rel->rd_id);
+    tgscan = heap_beginscan (tgrel, 0, NowTimeQual, 1, &key);
+    while (tuple = heap_getnext (tgscan, 0, (Buffer *)NULL), PointerIsValid(tuple))
+    {
+       Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT (tuple);
+       if ( namestrcmp (&(pg_trigger->tgname), stmt->trigname) == 0 )
+           elog (WARN, "CreateTrigger: trigger %s already defined on relation %s", 
+                               stmt->trigname, stmt->relname);
+       else
+           found++;
+    }
+    heap_endscan (tgscan);
+    
+    memset (fargtypes, 0, 8 * sizeof(Oid));
+    tuple = SearchSysCacheTuple (PRONAME, 
+                                PointerGetDatum (stmt->funcname),
+                                0, PointerGetDatum (fargtypes), 0);
+    if ( !HeapTupleIsValid (tuple) || 
+               ((Form_pg_proc)GETSTRUCT(tuple))->prorettype != 0 ||
+                       ((Form_pg_proc)GETSTRUCT(tuple))->pronargs != 0 )
+       elog (WARN, "CreateTrigger: function %s () does not exist", stmt->funcname);
+    
+    if ( ((Form_pg_proc)GETSTRUCT(tuple))->prolang != ClanguageId )
+       elog (WARN, "CreateTrigger: only C functions are supported");
+    
+    memset (nulls, ' ', Natts_pg_trigger * sizeof (char));
+    
+    values[Anum_pg_trigger_tgrelid - 1] = ObjectIdGetDatum (rel->rd_id);
+    values[Anum_pg_trigger_tgname - 1] = NameGetDatum (namein (stmt->trigname));
+    values[Anum_pg_trigger_tgfoid - 1] = ObjectIdGetDatum (tuple->t_oid);
+    values[Anum_pg_trigger_tgtype - 1] = Int16GetDatum (tgtype);
+    if ( stmt->args )
+    {
+       List *le;
+       char *args;
+       int16 nargs = length (stmt->args);
+       int len = 0;
+       
+       foreach (le, stmt->args)
+       {
+           char *ar = (char *) lfirst (le);
+           len += strlen (ar) + 4;
+       }
+       args = (char *) palloc (len + 1);
+       args[0] = 0;
+       foreach (le, stmt->args)
+           sprintf (args + strlen (args), "%s\\000", (char *)lfirst (le));
+       values[Anum_pg_trigger_tgnargs - 1] = Int16GetDatum (nargs);
+       values[Anum_pg_trigger_tgargs - 1] = PointerGetDatum (byteain (args));
+    }
+    else
+    {
+       values[Anum_pg_trigger_tgnargs - 1] = Int16GetDatum (0);
+       values[Anum_pg_trigger_tgargs - 1] = PointerGetDatum (byteain (""));
+    }
+    values[Anum_pg_trigger_tgattr - 1] = PointerGetDatum (tgattr);
+    
+    tuple = heap_formtuple (tgrel->rd_att, values, nulls);
+    heap_insert (tgrel, tuple);
+    CatalogOpenIndices (Num_pg_trigger_indices, Name_pg_trigger_indices, idescs);
+    CatalogIndexInsert (idescs, Num_pg_trigger_indices, tgrel, tuple);
+    CatalogCloseIndices (Num_pg_trigger_indices, idescs);
+    pfree (tuple);
+    RelationUnsetLockForWrite (tgrel);
+    heap_close (tgrel);
+    
+    pfree (DatumGetPointer(values[Anum_pg_trigger_tgname - 1]));
+    pfree (DatumGetPointer(values[Anum_pg_trigger_tgargs - 1]));
+    
+    /* update pg_class */
+    relrdesc = heap_openr (RelationRelationName);
+    tuple = ClassNameIndexScan (relrdesc, stmt->relname);
+    if ( !PointerIsValid (tuple) )
+    {
+       heap_close(relrdesc);
+       elog(WARN, "CreateTrigger: relation %s not found in pg_class", stmt->relname);
+    }
+    ((Form_pg_class) GETSTRUCT(tuple))->reltriggers = found + 1;
+    RelationInvalidateHeapTuple (relrdesc, tuple);
+    oldTID = tuple->t_ctid;
+    heap_replace (relrdesc, &oldTID, tuple);
+    CatalogOpenIndices (Num_pg_class_indices, Name_pg_class_indices, ridescs);
+    CatalogIndexInsert (ridescs, Num_pg_class_indices, relrdesc, tuple);
+    CatalogCloseIndices (Num_pg_class_indices, ridescs);
+    pfree(tuple);
+    heap_close(relrdesc);
+    
+    CommandCounterIncrement ();
+    oldcxt = MemoryContextSwitchTo ((MemoryContext)CacheCxt);
+    FreeTriggerDesc (rel);
+    rel->rd_rel->reltriggers = found + 1;
+    RelationBuildTriggers (rel);
+    MemoryContextSwitchTo (oldcxt);
+    heap_close (rel);
     return;
 }
 
 void
 DropTrigger (DropTrigStmt *stmt)
 {
-
+    Relation rel;
+    Relation tgrel;
+    HeapScanDesc tgscan;
+    ScanKeyData        key;
+    Relation relrdesc;
+    HeapTuple tuple;
+    ItemPointerData oldTID;
+    Relation ridescs[Num_pg_class_indices];
+    MemoryContext oldcxt;
+    int found = 0;
+    int tgfound = 0;
+    
+#ifndef NO_SECURITY
+    if ( !pg_ownercheck (GetPgUserName (), stmt->relname, RELNAME))
+       elog(WARN, "%s: %s", stmt->relname, aclcheck_error_strings[ACLCHECK_NOT_OWNER]);
+#endif
+    
+    rel = heap_openr (stmt->relname);
+    if ( !RelationIsValid (rel) )
+       elog (WARN, "DropTrigger: there is no relation %s", stmt->relname);
+    
+    RelationSetLockForWrite (rel);
+    
+    tgrel = heap_openr (TriggerRelationName);
+    RelationSetLockForWrite (tgrel);
+    ScanKeyEntryInitialize(&key, 0, Anum_pg_trigger_tgrelid,
+                          ObjectIdEqualRegProcedure, rel->rd_id);
+    tgscan = heap_beginscan (tgrel, 0, NowTimeQual, 1, &key);
+    while (tuple = heap_getnext (tgscan, 0, (Buffer *)NULL), PointerIsValid(tuple))
+    {
+       Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT (tuple);
+       if ( namestrcmp (&(pg_trigger->tgname), stmt->trigname) == 0 )
+       {
+           heap_delete (tgrel, &tuple->t_ctid);
+           tgfound++;
+       }
+       else
+           found++;
+    }
+    if ( tgfound == 0 )
+       elog (WARN, "DropTrigger: there is no trigger %s on relation %s", 
+                               stmt->trigname, stmt->relname);
+    if ( tgfound > 1 )
+       elog (NOTICE, "DropTrigger: found (and deleted) %d trigger %s on relation %s",
+                               tgfound, stmt->trigname, stmt->relname);
+    heap_endscan (tgscan);
+    RelationUnsetLockForWrite (tgrel);
+    heap_close (tgrel);
+    
+    /* update pg_class */
+    relrdesc = heap_openr (RelationRelationName);
+    tuple = ClassNameIndexScan (relrdesc, stmt->relname);
+    if ( !PointerIsValid (tuple) )
+    {
+       heap_close(relrdesc);
+       elog(WARN, "DropTrigger: relation %s not found in pg_class", stmt->relname);
+    }
+    ((Form_pg_class) GETSTRUCT(tuple))->reltriggers = found;
+    RelationInvalidateHeapTuple (relrdesc, tuple);
+    oldTID = tuple->t_ctid;
+    heap_replace (relrdesc, &oldTID, tuple);
+    CatalogOpenIndices (Num_pg_class_indices, Name_pg_class_indices, ridescs);
+    CatalogIndexInsert (ridescs, Num_pg_class_indices, relrdesc, tuple);
+    CatalogCloseIndices (Num_pg_class_indices, ridescs);
+    pfree(tuple);
+    heap_close(relrdesc);
+    
+    CommandCounterIncrement ();
+    oldcxt = MemoryContextSwitchTo ((MemoryContext)CacheCxt);
+    FreeTriggerDesc (rel);
+    rel->rd_rel->reltriggers = found;
+    if ( found > 0 )
+       RelationBuildTriggers (rel);
+    MemoryContextSwitchTo (oldcxt);
+    heap_close (rel);
     return;
 }
 
+void 
+RelationRemoveTriggers (Relation rel)
+{
+    Relation           tgrel;
+    HeapScanDesc       tgscan;
+    ScanKeyData                key;
+    HeapTuple          tup;
+    
+    tgrel = heap_openr (TriggerRelationName);
+    RelationSetLockForWrite (tgrel);
+    ScanKeyEntryInitialize(&key, 0, Anum_pg_trigger_tgrelid,
+                          ObjectIdEqualRegProcedure, rel->rd_id);
+    
+    tgscan = heap_beginscan (tgrel, 0, NowTimeQual, 1, &key);
+    
+    while (tup = heap_getnext (tgscan, 0, (Buffer *)NULL), PointerIsValid(tup))
+       heap_delete (tgrel, &tup->t_ctid);
+    
+    heap_endscan (tgscan);
+    RelationUnsetLockForWrite (tgrel);
+    heap_close (tgrel);
+
+}
+
 void
 RelationBuildTriggers (Relation relation)
 {
@@ -66,6 +342,7 @@ RelationBuildTriggers (Relation relation)
                           ObjectIdGetDatum(relation->rd_id));
     
     tgrel = heap_openr(TriggerRelationName);
+    RelationSetLockForRead (tgrel);
     irel = index_openr(TriggerRelidIndex);
     sd = index_beginscan(irel, false, 1, &skey);
     
@@ -93,22 +370,12 @@ RelationBuildTriggers (Relation relation)
        build = &(triggers[found]);
        
        build->tgname = nameout (&(pg_trigger->tgname));
-       build->tgfunc = nameout (&(pg_trigger->tgfunc));
-       build->tglang = pg_trigger->tglang;
-       if ( build->tglang != ClanguageId )
-           elog (WARN, "RelationBuildTriggers: unsupported language %u for trigger %s of rel %.*s",
-               build->tglang, build->tgname, NAMEDATALEN, relation->rd_rel->relname.data);
+       build->tgfoid = pg_trigger->tgfoid;
+       build->tgfunc = NULL;
        build->tgtype = pg_trigger->tgtype;
        build->tgnargs = pg_trigger->tgnargs;
        memcpy (build->tgattr, &(pg_trigger->tgattr), 8 * sizeof (int16));
        val = (struct varlena*) fastgetattr (tuple, 
-                                               Anum_pg_trigger_tgtext,
-                                               tgrel->rd_att, &isnull);
-       if ( isnull )
-           elog (WARN, "RelationBuildTriggers: tgtext IS NULL for rel %.*s",
-                   NAMEDATALEN, relation->rd_rel->relname.data);
-       build->tgtext = byteaout (val);
-       val = (struct varlena*) fastgetattr (tuple, 
                                                Anum_pg_trigger_tgargs,
                                                tgrel->rd_att, &isnull);
        if ( isnull )
@@ -134,13 +401,8 @@ RelationBuildTriggers (Relation relation)
                p += strlen (p) + 1;
            }
        }
-       val = (struct varlena*) fastgetattr (tuple, 
-                                               Anum_pg_trigger_tgwhen,
-                                               tgrel->rd_att, &isnull);
-       if ( !isnull )
-           build->tgwhen = textout (val);
        else
-           build->tgwhen = NULL;
+           build->tgargs =  NULL;
        
        found++;
        ReleaseBuffer(buffer);
@@ -154,97 +416,164 @@ RelationBuildTriggers (Relation relation)
     index_endscan (sd);
     pfree (sd);
     index_close (irel);
+    RelationUnsetLockForRead (tgrel);
     heap_close (tgrel);
     
     /* Build trigdesc */
     trigdesc->triggers = triggers;
     for (found = 0; found < ntrigs; found++)
     {
-       uint16 *n;
-       Trigger ***t, ***tp;
-       
        build = &(triggers[found]);
-       
-       if ( TRIGGER_FOR_ROW (build->tgtype) )  /* Is ROW/STATEMENT trigger */
+       DescribeTrigger (trigdesc, build);
+    }
+    
+    relation->trigdesc = trigdesc;
+    
+}
+
+void 
+FreeTriggerDesc (Relation relation)
+{
+    TriggerDesc *trigdesc = relation->trigdesc;
+    Trigger ***t;
+    Trigger *trigger;
+    int i;
+    
+    if ( trigdesc == NULL )
+       return;
+    
+    t = trigdesc->tg_before_statement;
+    for (i = 0; i < 3; i++)
+       if ( t[i] != NULL )
+           pfree (t[i]);
+    t = trigdesc->tg_before_row;
+    for (i = 0; i < 3; i++)
+       if ( t[i] != NULL )
+           pfree (t[i]);
+    t = trigdesc->tg_after_row;
+    for (i = 0; i < 3; i++)
+       if ( t[i] != NULL )
+           pfree (t[i]);
+    t = trigdesc->tg_after_statement;
+    for (i = 0; i < 3; i++)
+       if ( t[i] != NULL )
+           pfree (t[i]);
+    
+    trigger = trigdesc->triggers;
+    for (i = 0; i < relation->rd_rel->reltriggers; i++)
+    {
+       pfree (trigger->tgname);
+       if ( trigger->tgnargs > 0 )
        {
-           if ( TRIGGER_FOR_BEFORE (build->tgtype) )
-           {
-               n = trigdesc->n_before_row;
-               t = trigdesc->tg_before_row;
-           }
-           else
-           {
-               n = trigdesc->n_after_row;
-               t = trigdesc->tg_after_row;
-           }
+           while ( --(trigger->tgnargs) >= 0 )
+               pfree (trigger->tgargs[trigger->tgnargs]);
+           pfree (trigger->tgargs);
        }
-       else                                    /* STATEMENT (NI) */
+       trigger++;
+    }
+    pfree (trigdesc->triggers);
+    pfree (trigdesc);
+    relation->trigdesc = NULL;
+    return;
+}
+
+static void
+DescribeTrigger (TriggerDesc *trigdesc, Trigger *trigger)
+{
+    uint16 *n;
+    Trigger ***t, ***tp;
+       
+    if ( TRIGGER_FOR_ROW (trigger->tgtype) )   /* Is ROW/STATEMENT trigger */
+    {
+       if ( TRIGGER_FOR_BEFORE (trigger->tgtype) )
        {
-           if ( TRIGGER_FOR_BEFORE (build->tgtype) )
-           {
-               n = trigdesc->n_before_statement;
-               t = trigdesc->tg_before_statement;
-           }
-           else
-           {
-               n = trigdesc->n_after_statement;
-               t = trigdesc->tg_after_statement;
-           }
+           n = trigdesc->n_before_row;
+           t = trigdesc->tg_before_row;
        }
-       
-       if ( TRIGGER_FOR_INSERT (build->tgtype) )
+       else
        {
-           tp = &(t[TRIGGER_ACTION_INSERT]);
-           if ( *tp == NULL )
-               *tp = (Trigger **) palloc (sizeof (Trigger *));
-           else
-               *tp = (Trigger **) repalloc (*tp, (n[TRIGGER_ACTION_INSERT] + 1) * 
-                                               sizeof (Trigger *));
-           (*tp)[n[TRIGGER_ACTION_INSERT]] = build;
-           (n[TRIGGER_ACTION_INSERT])++;
+           n = trigdesc->n_after_row;
+           t = trigdesc->tg_after_row;
        }
-       
-       if ( TRIGGER_FOR_DELETE (build->tgtype) )
+    }
+    else                                       /* STATEMENT (NI) */
+    {
+       if ( TRIGGER_FOR_BEFORE (trigger->tgtype) )
        {
-           tp = &(t[TRIGGER_ACTION_DELETE]);
-           if ( *tp == NULL )
-               *tp = (Trigger **) palloc (sizeof (Trigger *));
-           else
-               *tp = (Trigger **) repalloc (*tp, (n[TRIGGER_ACTION_DELETE] + 1) * 
-                                               sizeof (Trigger *));
-           (*tp)[n[TRIGGER_ACTION_DELETE]] = build;
-           (n[TRIGGER_ACTION_DELETE])++;
+           n = trigdesc->n_before_statement;
+           t = trigdesc->tg_before_statement;
        }
-       
-       if ( TRIGGER_FOR_UPDATE (build->tgtype) )
+       else
        {
-           tp = &(t[TRIGGER_ACTION_UPDATE]);
-           if ( *tp == NULL )
-               *tp = (Trigger **) palloc (sizeof (Trigger *));
-           else
-               *tp = (Trigger **) repalloc (*tp, (n[TRIGGER_ACTION_UPDATE] + 1) * 
-                                               sizeof (Trigger *));
-           (*tp)[n[TRIGGER_ACTION_UPDATE]] = build;
-           (n[TRIGGER_ACTION_UPDATE])++;
+           n = trigdesc->n_after_statement;
+           t = trigdesc->tg_after_statement;
        }
     }
-       
-    relation->trigdesc = trigdesc;
     
-}
-
-void 
-FreeTriggerDesc (Relation relation)
-{
+    if ( TRIGGER_FOR_INSERT (trigger->tgtype) )
+    {
+       tp = &(t[TRIGGER_EVENT_INSERT]);
+       if ( *tp == NULL )
+           *tp = (Trigger **) palloc (sizeof (Trigger *));
+       else
+           *tp = (Trigger **) repalloc (*tp, (n[TRIGGER_EVENT_INSERT] + 1) * 
+                                               sizeof (Trigger *));
+       (*tp)[n[TRIGGER_EVENT_INSERT]] = trigger;
+       (n[TRIGGER_EVENT_INSERT])++;
+    }
+    
+    if ( TRIGGER_FOR_DELETE (trigger->tgtype) )
+    {
+       tp = &(t[TRIGGER_EVENT_DELETE]);
+       if ( *tp == NULL )
+           *tp = (Trigger **) palloc (sizeof (Trigger *));
+       else
+           *tp = (Trigger **) repalloc (*tp, (n[TRIGGER_EVENT_DELETE] + 1) * 
+                                               sizeof (Trigger *));
+       (*tp)[n[TRIGGER_EVENT_DELETE]] = trigger;
+       (n[TRIGGER_EVENT_DELETE])++;
+    }
+    
+    if ( TRIGGER_FOR_UPDATE (trigger->tgtype) )
+    {
+       tp = &(t[TRIGGER_EVENT_UPDATE]);
+       if ( *tp == NULL )
+           *tp = (Trigger **) palloc (sizeof (Trigger *));
+       else
+           *tp = (Trigger **) repalloc (*tp, (n[TRIGGER_EVENT_UPDATE] + 1) * 
+                                               sizeof (Trigger *));
+       (*tp)[n[TRIGGER_EVENT_UPDATE]] = trigger;
+       (n[TRIGGER_EVENT_UPDATE])++;
+    }
     
-    return;
 }
 
 HeapTuple 
 ExecBRInsertTriggers (Relation rel, HeapTuple tuple)
 {
+    int ntrigs = rel->trigdesc->n_before_row[TRIGGER_EVENT_INSERT];
+    Trigger **trigger = rel->trigdesc->tg_before_row[TRIGGER_EVENT_INSERT];
+    HeapTuple newtuple = tuple;
+    int nargs;
+    int i;
     
-    return (tuple);
+    CurrentTriggerData = (TriggerData *) palloc (sizeof (TriggerData));
+    CurrentTriggerData->tg_event = TRIGGER_EVENT_INSERT|TRIGGER_EVENT_ROW;
+    CurrentTriggerData->tg_relation = rel;
+    CurrentTriggerData->tg_newtuple = NULL;
+    for (i = 0; i < ntrigs; i++)
+    {
+       CurrentTriggerData->tg_trigtuple = newtuple;
+       CurrentTriggerData->tg_trigger = trigger[i];
+       if ( trigger[i]->tgfunc == NULL )
+           fmgr_info (trigger[i]->tgfoid, &(trigger[i]->tgfunc), &nargs);
+       newtuple = (HeapTuple) ( (*(trigger[i]->tgfunc)) () );
+       if ( newtuple == NULL )
+           break;
+    }
+    pfree (CurrentTriggerData);
+    CurrentTriggerData = NULL;
+    return (newtuple);
 }
 
 void