diff options
Diffstat (limited to 'src/backend/commands/trigger.c')
-rw-r--r-- | src/backend/commands/trigger.c | 261 |
1 files changed, 151 insertions, 110 deletions
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c index 47cf0c470d6..dd526f6db19 100644 --- a/src/backend/commands/trigger.c +++ b/src/backend/commands/trigger.c @@ -7,7 +7,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/trigger.c,v 1.253 2009/10/10 01:43:45 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/commands/trigger.c,v 1.254 2009/10/14 22:14:21 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -30,8 +30,11 @@ #include "executor/executor.h" #include "executor/instrument.h" #include "miscadmin.h" +#include "nodes/bitmapset.h" #include "nodes/makefuncs.h" #include "parser/parse_func.h" +#include "parser/parse_relation.h" +#include "parser/parsetree.h" #include "pgstat.h" #include "storage/bufmgr.h" #include "tcop/utility.h" @@ -51,6 +54,9 @@ int SessionReplicationRole = SESSION_REPLICATION_ROLE_ORIGIN; +#define GetModifiedColumns(relinfo, estate) \ + (rt_fetch((relinfo)->ri_RangeTableIndex, (estate)->es_range_table)->modifiedCols) + /* Local function prototypes */ static void ConvertTriggerToFK(CreateTrigStmt *stmt, Oid funcoid); static void InsertTrigger(TriggerDesc *trigdesc, Trigger *trigger, int indx); @@ -59,6 +65,8 @@ static HeapTuple GetTupleForTrigger(EState *estate, ResultRelInfo *relinfo, ItemPointer tid, TupleTableSlot **newSlot); +static bool TriggerEnabled(Trigger *trigger, TriggerEvent event, + Bitmapset *modifiedCols); static HeapTuple ExecCallTriggerFunc(TriggerData *trigdata, int tgindx, FmgrInfo *finfo, @@ -66,7 +74,7 @@ static HeapTuple ExecCallTriggerFunc(TriggerData *trigdata, MemoryContext per_tuple_context); static void AfterTriggerSaveEvent(ResultRelInfo *relinfo, int event, bool row_trigger, HeapTuple oldtup, HeapTuple newtup, - List *recheckIndexes); + List *recheckIndexes, Bitmapset *modifiedCols); /* @@ -98,6 +106,8 @@ CreateTrigger(CreateTrigStmt *stmt, bool checkPermissions) { int16 tgtype; + int ncolumns; + int2 *columns; int2vector *tgattr; Datum values[Natts_pg_trigger]; bool nulls[Natts_pg_trigger]; @@ -337,8 +347,44 @@ CreateTrigger(CreateTrigStmt *stmt, CStringGetDatum("")); } - /* tgattr is currently always a zero-length array */ - tgattr = buildint2vector(NULL, 0); + /* build column number array if it's a column-specific trigger */ + ncolumns = list_length(stmt->columns); + if (ncolumns == 0) + columns = NULL; + else + { + ListCell *cell; + int i = 0; + + columns = (int2 *) palloc(ncolumns * sizeof(int2)); + foreach(cell, stmt->columns) + { + char *name = strVal(lfirst(cell)); + int2 attnum; + int j; + + /* Lookup column name. System columns are not allowed */ + attnum = attnameAttNum(rel, name, false); + if (attnum == InvalidAttrNumber) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_COLUMN), + errmsg("column \"%s\" of relation \"%s\" does not exist", + name, RelationGetRelationName(rel)))); + + /* Check for duplicates */ + for (j = i - 1; j >= 0; j--) + { + if (columns[j] == attnum) + ereport(ERROR, + (errcode(ERRCODE_DUPLICATE_COLUMN), + errmsg("column \"%s\" specified more than once", + name))); + } + + columns[i++] = attnum; + } + } + tgattr = buildint2vector(columns, ncolumns); values[Anum_pg_trigger_tgattr - 1] = PointerGetDatum(tgattr); tuple = heap_form_tuple(tgrel->rd_att, values, nulls); @@ -358,6 +404,7 @@ CreateTrigger(CreateTrigStmt *stmt, pfree(DatumGetPointer(values[Anum_pg_trigger_tgname - 1])); pfree(DatumGetPointer(values[Anum_pg_trigger_tgargs - 1])); + pfree(DatumGetPointer(values[Anum_pg_trigger_tgattr - 1])); /* * Update relation's pg_class entry. Crucial side-effect: other backends @@ -434,6 +481,20 @@ CreateTrigger(CreateTrigStmt *stmt, Assert(!OidIsValid(indexOid)); } + /* If column-specific trigger, add normal dependencies on columns */ + if (columns != NULL) + { + int i; + + referenced.classId = RelationRelationId; + referenced.objectId = RelationGetRelid(rel); + for (i = 0; i < ncolumns; i++) + { + referenced.objectSubId = columns[i]; + recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); + } + } + /* Keep lock on target rel until end of xact */ heap_close(rel, NoLock); @@ -1626,18 +1687,9 @@ ExecBSInsertTriggers(EState *estate, ResultRelInfo *relinfo) Trigger *trigger = &trigdesc->triggers[tgindx[i]]; HeapTuple newtuple; - if (SessionReplicationRole == SESSION_REPLICATION_ROLE_REPLICA) - { - if (trigger->tgenabled == TRIGGER_FIRES_ON_ORIGIN || - trigger->tgenabled == TRIGGER_DISABLED) - continue; - } - else /* ORIGIN or LOCAL role */ - { - if (trigger->tgenabled == TRIGGER_FIRES_ON_REPLICA || - trigger->tgenabled == TRIGGER_DISABLED) - continue; - } + if (!TriggerEnabled(trigger, LocTriggerData.tg_event, NULL)) + continue; + LocTriggerData.tg_trigger = trigger; newtuple = ExecCallTriggerFunc(&LocTriggerData, tgindx[i], @@ -1659,7 +1711,7 @@ ExecASInsertTriggers(EState *estate, ResultRelInfo *relinfo) if (trigdesc && trigdesc->n_after_statement[TRIGGER_EVENT_INSERT] > 0) AfterTriggerSaveEvent(relinfo, TRIGGER_EVENT_INSERT, - false, NULL, NULL, NIL); + false, NULL, NULL, NIL, NULL); } HeapTuple @@ -1685,18 +1737,9 @@ ExecBRInsertTriggers(EState *estate, ResultRelInfo *relinfo, { Trigger *trigger = &trigdesc->triggers[tgindx[i]]; - if (SessionReplicationRole == SESSION_REPLICATION_ROLE_REPLICA) - { - if (trigger->tgenabled == TRIGGER_FIRES_ON_ORIGIN || - trigger->tgenabled == TRIGGER_DISABLED) - continue; - } - else /* ORIGIN or LOCAL role */ - { - if (trigger->tgenabled == TRIGGER_FIRES_ON_REPLICA || - trigger->tgenabled == TRIGGER_DISABLED) - continue; - } + if (!TriggerEnabled(trigger, LocTriggerData.tg_event, NULL)) + continue; + LocTriggerData.tg_trigtuple = oldtuple = newtuple; LocTriggerData.tg_trigtuplebuf = InvalidBuffer; LocTriggerData.tg_trigger = trigger; @@ -1721,7 +1764,7 @@ ExecARInsertTriggers(EState *estate, ResultRelInfo *relinfo, if (trigdesc && trigdesc->n_after_row[TRIGGER_EVENT_INSERT] > 0) AfterTriggerSaveEvent(relinfo, TRIGGER_EVENT_INSERT, - true, NULL, trigtuple, recheckIndexes); + true, NULL, trigtuple, recheckIndexes, NULL); } void @@ -1757,18 +1800,9 @@ ExecBSDeleteTriggers(EState *estate, ResultRelInfo *relinfo) Trigger *trigger = &trigdesc->triggers[tgindx[i]]; HeapTuple newtuple; - if (SessionReplicationRole == SESSION_REPLICATION_ROLE_REPLICA) - { - if (trigger->tgenabled == TRIGGER_FIRES_ON_ORIGIN || - trigger->tgenabled == TRIGGER_DISABLED) - continue; - } - else /* ORIGIN or LOCAL role */ - { - if (trigger->tgenabled == TRIGGER_FIRES_ON_REPLICA || - trigger->tgenabled == TRIGGER_DISABLED) - continue; - } + if (!TriggerEnabled(trigger, LocTriggerData.tg_event, NULL)) + continue; + LocTriggerData.tg_trigger = trigger; newtuple = ExecCallTriggerFunc(&LocTriggerData, tgindx[i], @@ -1790,7 +1824,7 @@ ExecASDeleteTriggers(EState *estate, ResultRelInfo *relinfo) if (trigdesc && trigdesc->n_after_statement[TRIGGER_EVENT_DELETE] > 0) AfterTriggerSaveEvent(relinfo, TRIGGER_EVENT_DELETE, - false, NULL, NULL, NIL); + false, NULL, NULL, NIL, NULL); } bool @@ -1824,18 +1858,9 @@ ExecBRDeleteTriggers(EState *estate, PlanState *subplanstate, { Trigger *trigger = &trigdesc->triggers[tgindx[i]]; - if (SessionReplicationRole == SESSION_REPLICATION_ROLE_REPLICA) - { - if (trigger->tgenabled == TRIGGER_FIRES_ON_ORIGIN || - trigger->tgenabled == TRIGGER_DISABLED) - continue; - } - else /* ORIGIN or LOCAL role */ - { - if (trigger->tgenabled == TRIGGER_FIRES_ON_REPLICA || - trigger->tgenabled == TRIGGER_DISABLED) - continue; - } + if (!TriggerEnabled(trigger, LocTriggerData.tg_event, NULL)) + continue; + LocTriggerData.tg_trigtuple = trigtuple; LocTriggerData.tg_trigtuplebuf = InvalidBuffer; LocTriggerData.tg_trigger = trigger; @@ -1869,7 +1894,7 @@ ExecARDeleteTriggers(EState *estate, ResultRelInfo *relinfo, tupleid, NULL); AfterTriggerSaveEvent(relinfo, TRIGGER_EVENT_DELETE, - true, trigtuple, NULL, NIL); + true, trigtuple, NULL, NIL, NULL); heap_freetuple(trigtuple); } } @@ -1882,6 +1907,7 @@ ExecBSUpdateTriggers(EState *estate, ResultRelInfo *relinfo) int *tgindx; int i; TriggerData LocTriggerData; + Bitmapset *modifiedCols; trigdesc = relinfo->ri_TrigDesc; @@ -1894,6 +1920,8 @@ ExecBSUpdateTriggers(EState *estate, ResultRelInfo *relinfo) if (ntrigs == 0) return; + modifiedCols = GetModifiedColumns(relinfo, estate); + LocTriggerData.type = T_TriggerData; LocTriggerData.tg_event = TRIGGER_EVENT_UPDATE | TRIGGER_EVENT_BEFORE; @@ -1907,18 +1935,9 @@ ExecBSUpdateTriggers(EState *estate, ResultRelInfo *relinfo) Trigger *trigger = &trigdesc->triggers[tgindx[i]]; HeapTuple newtuple; - if (SessionReplicationRole == SESSION_REPLICATION_ROLE_REPLICA) - { - if (trigger->tgenabled == TRIGGER_FIRES_ON_ORIGIN || - trigger->tgenabled == TRIGGER_DISABLED) - continue; - } - else /* ORIGIN or LOCAL role */ - { - if (trigger->tgenabled == TRIGGER_FIRES_ON_REPLICA || - trigger->tgenabled == TRIGGER_DISABLED) - continue; - } + if (!TriggerEnabled(trigger, LocTriggerData.tg_event, modifiedCols)) + continue; + LocTriggerData.tg_trigger = trigger; newtuple = ExecCallTriggerFunc(&LocTriggerData, tgindx[i], @@ -1940,7 +1959,8 @@ ExecASUpdateTriggers(EState *estate, ResultRelInfo *relinfo) if (trigdesc && trigdesc->n_after_statement[TRIGGER_EVENT_UPDATE] > 0) AfterTriggerSaveEvent(relinfo, TRIGGER_EVENT_UPDATE, - false, NULL, NULL, NIL); + false, NULL, NULL, NIL, + GetModifiedColumns(relinfo, estate)); } HeapTuple @@ -1957,6 +1977,7 @@ ExecBRUpdateTriggers(EState *estate, PlanState *subplanstate, HeapTuple intuple = newtuple; TupleTableSlot *newSlot; int i; + Bitmapset *modifiedCols; trigtuple = GetTupleForTrigger(estate, subplanstate, relinfo, tupleid, &newSlot); @@ -1971,6 +1992,8 @@ ExecBRUpdateTriggers(EState *estate, PlanState *subplanstate, if (newSlot != NULL) intuple = newtuple = ExecRemoveJunk(relinfo->ri_junkFilter, newSlot); + modifiedCols = GetModifiedColumns(relinfo, estate); + LocTriggerData.type = T_TriggerData; LocTriggerData.tg_event = TRIGGER_EVENT_UPDATE | TRIGGER_EVENT_ROW | @@ -1980,18 +2003,9 @@ ExecBRUpdateTriggers(EState *estate, PlanState *subplanstate, { Trigger *trigger = &trigdesc->triggers[tgindx[i]]; - if (SessionReplicationRole == SESSION_REPLICATION_ROLE_REPLICA) - { - if (trigger->tgenabled == TRIGGER_FIRES_ON_ORIGIN || - trigger->tgenabled == TRIGGER_DISABLED) - continue; - } - else /* ORIGIN or LOCAL role */ - { - if (trigger->tgenabled == TRIGGER_FIRES_ON_REPLICA || - trigger->tgenabled == TRIGGER_DISABLED) - continue; - } + if (!TriggerEnabled(trigger, LocTriggerData.tg_event, modifiedCols)) + continue; + LocTriggerData.tg_trigtuple = trigtuple; LocTriggerData.tg_newtuple = oldtuple = newtuple; LocTriggerData.tg_trigtuplebuf = InvalidBuffer; @@ -2024,7 +2038,8 @@ ExecARUpdateTriggers(EState *estate, ResultRelInfo *relinfo, tupleid, NULL); AfterTriggerSaveEvent(relinfo, TRIGGER_EVENT_UPDATE, - true, trigtuple, newtuple, recheckIndexes); + true, trigtuple, newtuple, recheckIndexes, + GetModifiedColumns(relinfo, estate)); heap_freetuple(trigtuple); } } @@ -2062,18 +2077,9 @@ ExecBSTruncateTriggers(EState *estate, ResultRelInfo *relinfo) Trigger *trigger = &trigdesc->triggers[tgindx[i]]; HeapTuple newtuple; - if (SessionReplicationRole == SESSION_REPLICATION_ROLE_REPLICA) - { - if (trigger->tgenabled == TRIGGER_FIRES_ON_ORIGIN || - trigger->tgenabled == TRIGGER_DISABLED) - continue; - } - else /* ORIGIN or LOCAL role */ - { - if (trigger->tgenabled == TRIGGER_FIRES_ON_REPLICA || - trigger->tgenabled == TRIGGER_DISABLED) - continue; - } + if (!TriggerEnabled(trigger, LocTriggerData.tg_event, NULL)) + continue; + LocTriggerData.tg_trigger = trigger; newtuple = ExecCallTriggerFunc(&LocTriggerData, tgindx[i], @@ -2095,7 +2101,7 @@ ExecASTruncateTriggers(EState *estate, ResultRelInfo *relinfo) if (trigdesc && trigdesc->n_after_statement[TRIGGER_EVENT_TRUNCATE] > 0) AfterTriggerSaveEvent(relinfo, TRIGGER_EVENT_TRUNCATE, - false, NULL, NULL, NIL); + false, NULL, NULL, NIL, NULL); } @@ -2201,6 +2207,52 @@ ltrmark:; return result; } +/* + * Is trigger enabled to fire? + */ +static bool +TriggerEnabled(Trigger *trigger, TriggerEvent event, Bitmapset *modifiedCols) +{ + /* Check replication-role-dependent enable state */ + if (SessionReplicationRole == SESSION_REPLICATION_ROLE_REPLICA) + { + if (trigger->tgenabled == TRIGGER_FIRES_ON_ORIGIN || + trigger->tgenabled == TRIGGER_DISABLED) + return false; + } + else /* ORIGIN or LOCAL role */ + { + if (trigger->tgenabled == TRIGGER_FIRES_ON_REPLICA || + trigger->tgenabled == TRIGGER_DISABLED) + return false; + } + + /* + * Check for column-specific trigger (only possible for UPDATE, and in + * fact we *must* ignore tgattr for other event types) + */ + if (trigger->tgnattr > 0 && TRIGGER_FIRED_BY_UPDATE(event)) + { + int i; + bool modified; + + modified = false; + for (i = 0; i < trigger->tgnattr; i++) + { + if (bms_is_member(trigger->tgattr[i] - FirstLowInvalidHeapAttributeNumber, + modifiedCols)) + { + modified = true; + break; + } + } + if (!modified) + return false; + } + + return true; +} + /* ---------- * After-trigger stuff @@ -3825,7 +3877,7 @@ AfterTriggerPendingOnRel(Oid relid) static void AfterTriggerSaveEvent(ResultRelInfo *relinfo, int event, bool row_trigger, HeapTuple oldtup, HeapTuple newtup, - List *recheckIndexes) + List *recheckIndexes, Bitmapset *modifiedCols) { Relation rel = relinfo->ri_RelationDesc; TriggerDesc *trigdesc = relinfo->ri_TrigDesc; @@ -3927,26 +3979,15 @@ AfterTriggerSaveEvent(ResultRelInfo *relinfo, int event, bool row_trigger, { Trigger *trigger = &trigdesc->triggers[tgindx[i]]; - /* Ignore disabled triggers */ - if (SessionReplicationRole == SESSION_REPLICATION_ROLE_REPLICA) - { - if (trigger->tgenabled == TRIGGER_FIRES_ON_ORIGIN || - trigger->tgenabled == TRIGGER_DISABLED) - continue; - } - else /* ORIGIN or LOCAL role */ - { - if (trigger->tgenabled == TRIGGER_FIRES_ON_REPLICA || - trigger->tgenabled == TRIGGER_DISABLED) - continue; - } + if (!TriggerEnabled(trigger, event, modifiedCols)) + continue; /* * If this is an UPDATE of a PK table or FK table that does not change * the PK or FK respectively, we can skip queuing the event: there is * no need to fire the trigger. */ - if ((event & TRIGGER_EVENT_OPMASK) == TRIGGER_EVENT_UPDATE) + if (TRIGGER_FIRED_BY_UPDATE(event)) { switch (RI_FKey_trigger_type(trigger->tgfoid)) { |