#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)
{
ObjectIdGetDatum(relation->rd_id));
tgrel = heap_openr(TriggerRelationName);
+ RelationSetLockForRead (tgrel);
irel = index_openr(TriggerRelidIndex);
sd = index_beginscan(irel, false, 1, &skey);
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);
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);
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