aboutsummaryrefslogtreecommitdiff
path: root/src/backend/commands/trigger.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/commands/trigger.c')
-rw-r--r--src/backend/commands/trigger.c261
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))
{