From dc13d5d307196e9933dd5189a6504a1170f44e49 Mon Sep 17 00:00:00 2001 From: "Vadim B. Mikheev" Date: Thu, 4 Sep 1997 13:19:01 +0000 Subject: [PATCH] Before row insertion triggers call. --- src/backend/commands/copy.c | 6 +- src/backend/commands/trigger.c | 497 +++++++++++++++++++++++++++------ 2 files changed, 416 insertions(+), 87 deletions(-) diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c index 4aa9e6896d..687cd1eb12 100644 --- a/src/backend/commands/copy.c +++ b/src/backend/commands/copy.c @@ -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); } diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c index 2c07e49f21..2919df473f 100644 --- a/src/backend/commands/trigger.c +++ b/src/backend/commands/trigger.c @@ -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" @@ -17,26 +19,300 @@ #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,21 +370,11 @@ 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); @@ -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 -- 2.40.0