mirror of
https://github.com/postgres/postgres.git
synced 2025-11-29 23:43:17 +03:00
Support SQL-compliant triggers on columns, ie fire only if certain columns
are named in the UPDATE's SET list. Note: the schema of pg_trigger has not actually changed; we've just started to use a column that was there all along. catversion bumped anyway so that this commit is included in the history of potentially interesting changes to system catalog contents. Itagaki Takahiro
This commit is contained in:
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/catalog/index.c,v 1.322 2009/10/05 19:24:35 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/catalog/index.c,v 1.323 2009/10/14 22:14:21 tgl Exp $
|
||||
*
|
||||
*
|
||||
* INTERFACE ROUTINES
|
||||
@@ -792,6 +792,7 @@ index_create(Oid heapRelationId,
|
||||
trigger->before = false;
|
||||
trigger->row = true;
|
||||
trigger->events = TRIGGER_TYPE_INSERT | TRIGGER_TYPE_UPDATE;
|
||||
trigger->columns = NIL;
|
||||
trigger->isconstraint = true;
|
||||
trigger->deferrable = true;
|
||||
trigger->initdeferred = initdeferred;
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.303 2009/10/13 00:53:07 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.304 2009/10/14 22:14:21 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -5441,6 +5441,7 @@ CreateFKCheckTrigger(RangeVar *myRel, Constraint *fkconstraint,
|
||||
fk_trigger->events = TRIGGER_TYPE_UPDATE;
|
||||
}
|
||||
|
||||
fk_trigger->columns = NIL;
|
||||
fk_trigger->isconstraint = true;
|
||||
fk_trigger->deferrable = fkconstraint->deferrable;
|
||||
fk_trigger->initdeferred = fkconstraint->initdeferred;
|
||||
@@ -5491,6 +5492,7 @@ createForeignKeyTriggers(Relation rel, Constraint *fkconstraint,
|
||||
fk_trigger->before = false;
|
||||
fk_trigger->row = true;
|
||||
fk_trigger->events = TRIGGER_TYPE_DELETE;
|
||||
fk_trigger->columns = NIL;
|
||||
fk_trigger->isconstraint = true;
|
||||
fk_trigger->constrrel = myRel;
|
||||
switch (fkconstraint->fk_del_action)
|
||||
@@ -5543,6 +5545,7 @@ createForeignKeyTriggers(Relation rel, Constraint *fkconstraint,
|
||||
fk_trigger->before = false;
|
||||
fk_trigger->row = true;
|
||||
fk_trigger->events = TRIGGER_TYPE_UPDATE;
|
||||
fk_trigger->columns = NIL;
|
||||
fk_trigger->isconstraint = true;
|
||||
fk_trigger->constrrel = myRel;
|
||||
switch (fkconstraint->fk_upd_action)
|
||||
|
||||
@@ -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))
|
||||
{
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.447 2009/10/13 00:53:08 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.448 2009/10/14 22:14:21 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -3160,6 +3160,7 @@ _copyCreateTrigStmt(CreateTrigStmt *from)
|
||||
COPY_SCALAR_FIELD(before);
|
||||
COPY_SCALAR_FIELD(row);
|
||||
COPY_SCALAR_FIELD(events);
|
||||
COPY_NODE_FIELD(columns);
|
||||
COPY_SCALAR_FIELD(isconstraint);
|
||||
COPY_SCALAR_FIELD(deferrable);
|
||||
COPY_SCALAR_FIELD(initdeferred);
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.369 2009/10/13 00:53:08 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.370 2009/10/14 22:14:21 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -1671,6 +1671,7 @@ _equalCreateTrigStmt(CreateTrigStmt *a, CreateTrigStmt *b)
|
||||
COMPARE_SCALAR_FIELD(before);
|
||||
COMPARE_SCALAR_FIELD(row);
|
||||
COMPARE_SCALAR_FIELD(events);
|
||||
COMPARE_NODE_FIELD(columns);
|
||||
COMPARE_SCALAR_FIELD(isconstraint);
|
||||
COMPARE_SCALAR_FIELD(deferrable);
|
||||
COMPARE_SCALAR_FIELD(initdeferred);
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/optimizer/plan/setrefs.c,v 1.152 2009/10/12 18:10:48 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/optimizer/plan/setrefs.c,v 1.153 2009/10/14 22:14:22 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -193,7 +193,8 @@ set_plan_references(PlannerGlobal *glob, Plan *plan,
|
||||
* needed by the executor; this reduces the storage space and copying cost
|
||||
* for cached plans. We keep only the alias and eref Alias fields, which
|
||||
* are needed by EXPLAIN, and the selectedCols and modifiedCols bitmaps,
|
||||
* which are needed for executor-startup permissions checking.
|
||||
* which are needed for executor-startup permissions checking and for
|
||||
* trigger event checking.
|
||||
*/
|
||||
foreach(lc, rtable)
|
||||
{
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.685 2009/10/12 23:41:43 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.686 2009/10/14 22:14:22 tgl Exp $
|
||||
*
|
||||
* HISTORY
|
||||
* AUTHOR DATE MAJOR EVENT
|
||||
@@ -251,7 +251,7 @@ static TypeName *TableFuncTypeName(List *columns);
|
||||
|
||||
%type <boolean> TriggerActionTime TriggerForSpec opt_trusted opt_restart_seqs
|
||||
|
||||
%type <ival> TriggerEvents TriggerOneEvent
|
||||
%type <list> TriggerEvents TriggerOneEvent
|
||||
%type <value> TriggerFuncArg
|
||||
|
||||
%type <str> relation_name copy_file_name
|
||||
@@ -3240,7 +3240,8 @@ CreateTrigStmt:
|
||||
n->args = $13;
|
||||
n->before = $4;
|
||||
n->row = $8;
|
||||
n->events = $5;
|
||||
n->events = intVal(linitial($5));
|
||||
n->columns = (List *) lsecond($5);
|
||||
n->isconstraint = FALSE;
|
||||
n->deferrable = FALSE;
|
||||
n->initdeferred = FALSE;
|
||||
@@ -3260,7 +3261,8 @@ CreateTrigStmt:
|
||||
n->args = $18;
|
||||
n->before = FALSE;
|
||||
n->row = TRUE;
|
||||
n->events = $6;
|
||||
n->events = intVal(linitial($6));
|
||||
n->columns = (List *) lsecond($6);
|
||||
n->isconstraint = TRUE;
|
||||
n->deferrable = ($10 & 1) != 0;
|
||||
n->initdeferred = ($10 & 2) != 0;
|
||||
@@ -3279,17 +3281,36 @@ TriggerEvents:
|
||||
{ $$ = $1; }
|
||||
| TriggerEvents OR TriggerOneEvent
|
||||
{
|
||||
if ($1 & $3)
|
||||
int events1 = intVal(linitial($1));
|
||||
int events2 = intVal(linitial($3));
|
||||
List *columns1 = (List *) lsecond($1);
|
||||
List *columns2 = (List *) lsecond($3);
|
||||
|
||||
if (events1 & events2)
|
||||
parser_yyerror("duplicate trigger events specified");
|
||||
$$ = $1 | $3;
|
||||
/*
|
||||
* concat'ing the columns lists loses information about
|
||||
* which columns went with which event, but so long as
|
||||
* only UPDATE carries columns and we disallow multiple
|
||||
* UPDATE items, it doesn't matter. Command execution
|
||||
* should just ignore the columns for non-UPDATE events.
|
||||
*/
|
||||
$$ = list_make2(makeInteger(events1 | events2),
|
||||
list_concat(columns1, columns2));
|
||||
}
|
||||
;
|
||||
|
||||
TriggerOneEvent:
|
||||
INSERT { $$ = TRIGGER_TYPE_INSERT; }
|
||||
| DELETE_P { $$ = TRIGGER_TYPE_DELETE; }
|
||||
| UPDATE { $$ = TRIGGER_TYPE_UPDATE; }
|
||||
| TRUNCATE { $$ = TRIGGER_TYPE_TRUNCATE; }
|
||||
INSERT
|
||||
{ $$ = list_make2(makeInteger(TRIGGER_TYPE_INSERT), NIL); }
|
||||
| DELETE_P
|
||||
{ $$ = list_make2(makeInteger(TRIGGER_TYPE_DELETE), NIL); }
|
||||
| UPDATE
|
||||
{ $$ = list_make2(makeInteger(TRIGGER_TYPE_UPDATE), NIL); }
|
||||
| UPDATE OF columnList
|
||||
{ $$ = list_make2(makeInteger(TRIGGER_TYPE_UPDATE), $3); }
|
||||
| TRUNCATE
|
||||
{ $$ = list_make2(makeInteger(TRIGGER_TYPE_TRUNCATE), NIL); }
|
||||
;
|
||||
|
||||
TriggerForSpec:
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.309 2009/10/10 01:43:49 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.310 2009/10/14 22:14:23 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -543,6 +543,23 @@ pg_get_triggerdef_worker(Oid trigid, bool pretty)
|
||||
appendStringInfo(&buf, " OR UPDATE");
|
||||
else
|
||||
appendStringInfo(&buf, " UPDATE");
|
||||
/* tgattr is first var-width field, so OK to access directly */
|
||||
if (trigrec->tgattr.dim1 > 0)
|
||||
{
|
||||
int i;
|
||||
|
||||
appendStringInfoString(&buf, " OF ");
|
||||
for (i = 0; i < trigrec->tgattr.dim1; i++)
|
||||
{
|
||||
char *attname;
|
||||
|
||||
if (i > 0)
|
||||
appendStringInfoString(&buf, ", ");
|
||||
attname = get_relid_attribute_name(trigrec->tgrelid,
|
||||
trigrec->tgattr.values[i]);
|
||||
appendStringInfoString(&buf, quote_identifier(attname));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (TRIGGER_FOR_TRUNCATE(trigrec->tgtype))
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user