mirror of
https://github.com/postgres/postgres.git
synced 2025-08-31 17:02:12 +03:00
This patch implements FOR EACH STATEMENT triggers, per my email to
-hackers a couple days ago. Notes/caveats: - added regression tests for the new functionality, all regression tests pass on my machine - added pg_dump support - updated PL/PgSQL to support per-statement triggers; didn't look at the other procedural languages. - there's (even) more code duplication in trigger.c than there was previously. Any suggestions on how to refactor the ExecXXXTriggers() functions to reuse more code would be welcome -- I took a brief look at it, but couldn't see an easy way to do it (there are several subtly-different versions of the code in question) - updated the documentation. I also took the liberty of removing a big chunk of duplicated syntax documentation in the Programmer's Guide on triggers, and moving that information to the CREATE TRIGGER reference page. - I also included some spelling fixes and similar small cleanups I noticed while making the changes. If you'd like me to split those into a separate patch, let me know. Neil Conway
This commit is contained in:
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/access/transam/xact.c,v 1.139 2002/11/18 01:17:39 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/access/transam/xact.c,v 1.140 2002/11/23 03:59:06 momjian Exp $
|
||||
*
|
||||
* NOTES
|
||||
* Transaction aborts can now occur two ways:
|
||||
@@ -901,18 +901,6 @@ StartTransaction(void)
|
||||
|
||||
}
|
||||
|
||||
#ifdef NOT_USED
|
||||
/* ---------------
|
||||
* Tell me if we are currently in progress
|
||||
* ---------------
|
||||
*/
|
||||
bool
|
||||
CurrentXactInProgress(void)
|
||||
{
|
||||
return CurrentTransactionState->state == TRANS_INPROGRESS;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* --------------------------------
|
||||
* CommitTransaction
|
||||
* --------------------------------
|
||||
|
@@ -7,7 +7,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.180 2002/11/13 00:39:46 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.181 2002/11/23 03:59:07 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -877,6 +877,15 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Check BEFORE STATEMENT insertion triggers. It's debateable
|
||||
* whether we should do this for COPY, since it's not really an
|
||||
* "INSERT" statement as such. However, executing these triggers
|
||||
* maintains consistency with the EACH ROW triggers that we already
|
||||
* fire on COPY.
|
||||
*/
|
||||
ExecBSInsertTriggers(estate, resultRelInfo);
|
||||
|
||||
if (!binary)
|
||||
{
|
||||
file_has_oids = oids; /* must rely on user to tell us this... */
|
||||
@@ -1223,8 +1232,7 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
|
||||
ExecInsertIndexTuples(slot, &(tuple->t_self), estate, false);
|
||||
|
||||
/* AFTER ROW INSERT Triggers */
|
||||
if (resultRelInfo->ri_TrigDesc)
|
||||
ExecARInsertTriggers(estate, resultRelInfo, tuple);
|
||||
ExecARInsertTriggers(estate, resultRelInfo, tuple);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1233,6 +1241,11 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
|
||||
*/
|
||||
copy_lineno = 0;
|
||||
|
||||
/*
|
||||
* Execute AFTER STATEMENT insertion triggers
|
||||
*/
|
||||
ExecASInsertTriggers(estate, resultRelInfo);
|
||||
|
||||
MemoryContextSwitchTo(oldcontext);
|
||||
|
||||
pfree(values);
|
||||
|
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/tablecmds.c,v 1.54 2002/11/15 02:50:05 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/tablecmds.c,v 1.55 2002/11/23 03:59:07 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -3321,11 +3321,7 @@ createForeignKeyTriggers(Relation rel, FkConstraint *fkconstraint,
|
||||
fk_trigger->actions[0] = 'i';
|
||||
fk_trigger->actions[1] = 'u';
|
||||
fk_trigger->actions[2] = '\0';
|
||||
fk_trigger->lang = NULL;
|
||||
fk_trigger->text = NULL;
|
||||
|
||||
fk_trigger->attr = NIL;
|
||||
fk_trigger->when = NULL;
|
||||
fk_trigger->isconstraint = true;
|
||||
fk_trigger->deferrable = fkconstraint->deferrable;
|
||||
fk_trigger->initdeferred = fkconstraint->initdeferred;
|
||||
@@ -3374,11 +3370,7 @@ createForeignKeyTriggers(Relation rel, FkConstraint *fkconstraint,
|
||||
fk_trigger->row = true;
|
||||
fk_trigger->actions[0] = 'd';
|
||||
fk_trigger->actions[1] = '\0';
|
||||
fk_trigger->lang = NULL;
|
||||
fk_trigger->text = NULL;
|
||||
|
||||
fk_trigger->attr = NIL;
|
||||
fk_trigger->when = NULL;
|
||||
fk_trigger->isconstraint = true;
|
||||
fk_trigger->deferrable = fkconstraint->deferrable;
|
||||
fk_trigger->initdeferred = fkconstraint->initdeferred;
|
||||
@@ -3445,11 +3437,6 @@ createForeignKeyTriggers(Relation rel, FkConstraint *fkconstraint,
|
||||
fk_trigger->row = true;
|
||||
fk_trigger->actions[0] = 'u';
|
||||
fk_trigger->actions[1] = '\0';
|
||||
fk_trigger->lang = NULL;
|
||||
fk_trigger->text = NULL;
|
||||
|
||||
fk_trigger->attr = NIL;
|
||||
fk_trigger->when = NULL;
|
||||
fk_trigger->isconstraint = true;
|
||||
fk_trigger->deferrable = fkconstraint->deferrable;
|
||||
fk_trigger->initdeferred = fkconstraint->initdeferred;
|
||||
|
@@ -7,7 +7,7 @@
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/trigger.c,v 1.139 2002/11/13 00:39:46 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/trigger.c,v 1.140 2002/11/23 03:59:07 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -47,7 +47,7 @@ static HeapTuple ExecCallTriggerFunc(TriggerData *trigdata,
|
||||
FmgrInfo *finfo,
|
||||
MemoryContext per_tuple_context);
|
||||
static void DeferredTriggerSaveEvent(ResultRelInfo *relinfo, int event,
|
||||
HeapTuple oldtup, HeapTuple newtup);
|
||||
bool row_trigger, HeapTuple oldtup, HeapTuple newtup);
|
||||
static void DeferredTriggerExecute(DeferredTriggerEvent event, int itemno,
|
||||
Relation rel, TriggerDesc *trigdesc, FmgrInfo *finfo,
|
||||
MemoryContext per_tuple_context);
|
||||
@@ -147,12 +147,14 @@ CreateTrigger(CreateTrigStmt *stmt, bool forConstraint)
|
||||
{
|
||||
/* foreign key constraint trigger */
|
||||
|
||||
aclresult = pg_class_aclcheck(RelationGetRelid(rel), GetUserId(), ACL_REFERENCES);
|
||||
aclresult = pg_class_aclcheck(RelationGetRelid(rel), GetUserId(),
|
||||
ACL_REFERENCES);
|
||||
if (aclresult != ACLCHECK_OK)
|
||||
aclcheck_error(aclresult, RelationGetRelationName(rel));
|
||||
if (constrrelid != InvalidOid)
|
||||
{
|
||||
aclresult = pg_class_aclcheck(constrrelid, GetUserId(), ACL_REFERENCES);
|
||||
aclresult = pg_class_aclcheck(constrrelid, GetUserId(),
|
||||
ACL_REFERENCES);
|
||||
if (aclresult != ACLCHECK_OK)
|
||||
aclcheck_error(aclresult, get_rel_name(constrrelid));
|
||||
}
|
||||
@@ -160,7 +162,8 @@ CreateTrigger(CreateTrigStmt *stmt, bool forConstraint)
|
||||
else
|
||||
{
|
||||
/* real trigger */
|
||||
aclresult = pg_class_aclcheck(RelationGetRelid(rel), GetUserId(), ACL_TRIGGER);
|
||||
aclresult = pg_class_aclcheck(RelationGetRelid(rel), GetUserId(),
|
||||
ACL_TRIGGER);
|
||||
if (aclresult != ACLCHECK_OK)
|
||||
aclcheck_error(aclresult, RelationGetRelationName(rel));
|
||||
}
|
||||
@@ -195,10 +198,8 @@ CreateTrigger(CreateTrigStmt *stmt, bool forConstraint)
|
||||
TRIGGER_SETT_BEFORE(tgtype);
|
||||
if (stmt->row)
|
||||
TRIGGER_SETT_ROW(tgtype);
|
||||
else
|
||||
elog(ERROR, "CreateTrigger: STATEMENT triggers are unimplemented, yet");
|
||||
|
||||
for (i = 0; i < 3 && stmt->actions[i]; i++)
|
||||
for (i = 0; i < 2 && stmt->actions[i]; i++)
|
||||
{
|
||||
switch (stmt->actions[i])
|
||||
{
|
||||
@@ -1131,6 +1132,64 @@ ExecCallTriggerFunc(TriggerData *trigdata,
|
||||
return (HeapTuple) DatumGetPointer(result);
|
||||
}
|
||||
|
||||
void
|
||||
ExecBSInsertTriggers(EState *estate, ResultRelInfo *relinfo)
|
||||
{
|
||||
TriggerDesc *trigdesc;
|
||||
int ntrigs;
|
||||
int *tgindx;
|
||||
int i;
|
||||
TriggerData LocTriggerData;
|
||||
|
||||
trigdesc = relinfo->ri_TrigDesc;
|
||||
|
||||
if (trigdesc == NULL)
|
||||
return;
|
||||
|
||||
ntrigs = trigdesc->n_before_statement[TRIGGER_EVENT_INSERT];
|
||||
tgindx = trigdesc->tg_before_statement[TRIGGER_EVENT_INSERT];
|
||||
|
||||
if (ntrigs == 0)
|
||||
return;
|
||||
|
||||
/* Allocate cache space for fmgr lookup info, if not done yet */
|
||||
if (relinfo->ri_TrigFunctions == NULL)
|
||||
relinfo->ri_TrigFunctions = (FmgrInfo *)
|
||||
palloc0(trigdesc->numtriggers * sizeof(FmgrInfo));
|
||||
|
||||
LocTriggerData.type = T_TriggerData;
|
||||
LocTriggerData.tg_event = TRIGGER_EVENT_INSERT |
|
||||
TRIGGER_EVENT_BEFORE;
|
||||
LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
|
||||
LocTriggerData.tg_newtuple = NULL;
|
||||
LocTriggerData.tg_trigtuple = NULL;
|
||||
for (i = 0; i < ntrigs; i++)
|
||||
{
|
||||
Trigger *trigger = &trigdesc->triggers[tgindx[i]];
|
||||
HeapTuple newtuple;
|
||||
|
||||
if (!trigger->tgenabled)
|
||||
continue;
|
||||
LocTriggerData.tg_trigger = trigger;
|
||||
newtuple = ExecCallTriggerFunc(&LocTriggerData,
|
||||
relinfo->ri_TrigFunctions + tgindx[i],
|
||||
GetPerTupleMemoryContext(estate));
|
||||
|
||||
if (newtuple)
|
||||
elog(ERROR, "BEFORE STATEMENT trigger cannot return a value.");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ExecASInsertTriggers(EState *estate, ResultRelInfo *relinfo)
|
||||
{
|
||||
TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
|
||||
|
||||
if (trigdesc && trigdesc->n_after_statement[TRIGGER_EVENT_INSERT] > 0)
|
||||
DeferredTriggerSaveEvent(relinfo, TRIGGER_EVENT_INSERT,
|
||||
false, NULL, NULL);
|
||||
}
|
||||
|
||||
HeapTuple
|
||||
ExecBRInsertTriggers(EState *estate, ResultRelInfo *relinfo,
|
||||
HeapTuple trigtuple)
|
||||
@@ -1149,7 +1208,9 @@ ExecBRInsertTriggers(EState *estate, ResultRelInfo *relinfo,
|
||||
palloc0(trigdesc->numtriggers * sizeof(FmgrInfo));
|
||||
|
||||
LocTriggerData.type = T_TriggerData;
|
||||
LocTriggerData.tg_event = TRIGGER_EVENT_INSERT | TRIGGER_EVENT_ROW | TRIGGER_EVENT_BEFORE;
|
||||
LocTriggerData.tg_event = TRIGGER_EVENT_INSERT |
|
||||
TRIGGER_EVENT_ROW |
|
||||
TRIGGER_EVENT_BEFORE;
|
||||
LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
|
||||
LocTriggerData.tg_newtuple = NULL;
|
||||
for (i = 0; i < ntrigs; i++)
|
||||
@@ -1177,9 +1238,67 @@ ExecARInsertTriggers(EState *estate, ResultRelInfo *relinfo,
|
||||
{
|
||||
TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
|
||||
|
||||
if (trigdesc->n_after_row[TRIGGER_EVENT_INSERT] > 0)
|
||||
if (trigdesc && trigdesc->n_after_row[TRIGGER_EVENT_INSERT] > 0)
|
||||
DeferredTriggerSaveEvent(relinfo, TRIGGER_EVENT_INSERT,
|
||||
NULL, trigtuple);
|
||||
true, NULL, trigtuple);
|
||||
}
|
||||
|
||||
void
|
||||
ExecBSDeleteTriggers(EState *estate, ResultRelInfo *relinfo)
|
||||
{
|
||||
TriggerDesc *trigdesc;
|
||||
int ntrigs;
|
||||
int *tgindx;
|
||||
int i;
|
||||
TriggerData LocTriggerData;
|
||||
|
||||
trigdesc = relinfo->ri_TrigDesc;
|
||||
|
||||
if (trigdesc == NULL)
|
||||
return;
|
||||
|
||||
ntrigs = trigdesc->n_before_statement[TRIGGER_EVENT_DELETE];
|
||||
tgindx = trigdesc->tg_before_statement[TRIGGER_EVENT_DELETE];
|
||||
|
||||
if (ntrigs == 0)
|
||||
return;
|
||||
|
||||
/* Allocate cache space for fmgr lookup info, if not done yet */
|
||||
if (relinfo->ri_TrigFunctions == NULL)
|
||||
relinfo->ri_TrigFunctions = (FmgrInfo *)
|
||||
palloc0(trigdesc->numtriggers * sizeof(FmgrInfo));
|
||||
|
||||
LocTriggerData.type = T_TriggerData;
|
||||
LocTriggerData.tg_event = TRIGGER_EVENT_DELETE |
|
||||
TRIGGER_EVENT_BEFORE;
|
||||
LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
|
||||
LocTriggerData.tg_newtuple = NULL;
|
||||
LocTriggerData.tg_trigtuple = NULL;
|
||||
for (i = 0; i < ntrigs; i++)
|
||||
{
|
||||
Trigger *trigger = &trigdesc->triggers[tgindx[i]];
|
||||
HeapTuple newtuple;
|
||||
|
||||
if (!trigger->tgenabled)
|
||||
continue;
|
||||
LocTriggerData.tg_trigger = trigger;
|
||||
newtuple = ExecCallTriggerFunc(&LocTriggerData,
|
||||
relinfo->ri_TrigFunctions + tgindx[i],
|
||||
GetPerTupleMemoryContext(estate));
|
||||
|
||||
if (newtuple)
|
||||
elog(ERROR, "BEFORE STATEMENT trigger cannot return a value.");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ExecASDeleteTriggers(EState *estate, ResultRelInfo *relinfo)
|
||||
{
|
||||
TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
|
||||
|
||||
if (trigdesc && trigdesc->n_after_statement[TRIGGER_EVENT_DELETE] > 0)
|
||||
DeferredTriggerSaveEvent(relinfo, TRIGGER_EVENT_DELETE,
|
||||
false, NULL, NULL);
|
||||
}
|
||||
|
||||
bool
|
||||
@@ -1205,7 +1324,9 @@ ExecBRDeleteTriggers(EState *estate, ResultRelInfo *relinfo,
|
||||
palloc0(trigdesc->numtriggers * sizeof(FmgrInfo));
|
||||
|
||||
LocTriggerData.type = T_TriggerData;
|
||||
LocTriggerData.tg_event = TRIGGER_EVENT_DELETE | TRIGGER_EVENT_ROW | TRIGGER_EVENT_BEFORE;
|
||||
LocTriggerData.tg_event = TRIGGER_EVENT_DELETE |
|
||||
TRIGGER_EVENT_ROW |
|
||||
TRIGGER_EVENT_BEFORE;
|
||||
LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
|
||||
LocTriggerData.tg_newtuple = NULL;
|
||||
for (i = 0; i < ntrigs; i++)
|
||||
@@ -1235,17 +1356,75 @@ ExecARDeleteTriggers(EState *estate, ResultRelInfo *relinfo,
|
||||
{
|
||||
TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
|
||||
|
||||
if (trigdesc->n_after_row[TRIGGER_EVENT_DELETE] > 0)
|
||||
if (trigdesc && trigdesc->n_after_row[TRIGGER_EVENT_DELETE] > 0)
|
||||
{
|
||||
HeapTuple trigtuple = GetTupleForTrigger(estate, relinfo,
|
||||
tupleid, NULL);
|
||||
|
||||
DeferredTriggerSaveEvent(relinfo, TRIGGER_EVENT_DELETE,
|
||||
trigtuple, NULL);
|
||||
true, trigtuple, NULL);
|
||||
heap_freetuple(trigtuple);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ExecBSUpdateTriggers(EState *estate, ResultRelInfo *relinfo)
|
||||
{
|
||||
TriggerDesc *trigdesc;
|
||||
int ntrigs;
|
||||
int *tgindx;
|
||||
int i;
|
||||
TriggerData LocTriggerData;
|
||||
|
||||
trigdesc = relinfo->ri_TrigDesc;
|
||||
|
||||
if (trigdesc == NULL)
|
||||
return;
|
||||
|
||||
ntrigs = trigdesc->n_before_statement[TRIGGER_EVENT_UPDATE];
|
||||
tgindx = trigdesc->tg_before_statement[TRIGGER_EVENT_UPDATE];
|
||||
|
||||
if (ntrigs == 0)
|
||||
return;
|
||||
|
||||
/* Allocate cache space for fmgr lookup info, if not done yet */
|
||||
if (relinfo->ri_TrigFunctions == NULL)
|
||||
relinfo->ri_TrigFunctions = (FmgrInfo *)
|
||||
palloc0(trigdesc->numtriggers * sizeof(FmgrInfo));
|
||||
|
||||
LocTriggerData.type = T_TriggerData;
|
||||
LocTriggerData.tg_event = TRIGGER_EVENT_UPDATE |
|
||||
TRIGGER_EVENT_BEFORE;
|
||||
LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
|
||||
LocTriggerData.tg_newtuple = NULL;
|
||||
LocTriggerData.tg_trigtuple = NULL;
|
||||
for (i = 0; i < ntrigs; i++)
|
||||
{
|
||||
Trigger *trigger = &trigdesc->triggers[tgindx[i]];
|
||||
HeapTuple newtuple;
|
||||
|
||||
if (!trigger->tgenabled)
|
||||
continue;
|
||||
LocTriggerData.tg_trigger = trigger;
|
||||
newtuple = ExecCallTriggerFunc(&LocTriggerData,
|
||||
relinfo->ri_TrigFunctions + tgindx[i],
|
||||
GetPerTupleMemoryContext(estate));
|
||||
|
||||
if (newtuple)
|
||||
elog(ERROR, "BEFORE STATEMENT trigger cannot return a value.");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ExecASUpdateTriggers(EState *estate, ResultRelInfo *relinfo)
|
||||
{
|
||||
TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
|
||||
|
||||
if (trigdesc && trigdesc->n_after_statement[TRIGGER_EVENT_UPDATE] > 0)
|
||||
DeferredTriggerSaveEvent(relinfo, TRIGGER_EVENT_UPDATE,
|
||||
false, NULL, NULL);
|
||||
}
|
||||
|
||||
HeapTuple
|
||||
ExecBRUpdateTriggers(EState *estate, ResultRelInfo *relinfo,
|
||||
ItemPointer tupleid, HeapTuple newtuple)
|
||||
@@ -1265,8 +1444,8 @@ ExecBRUpdateTriggers(EState *estate, ResultRelInfo *relinfo,
|
||||
return NULL;
|
||||
|
||||
/*
|
||||
* In READ COMMITTED isolevel it's possible that newtuple was changed
|
||||
* due to concurrent update.
|
||||
* In READ COMMITTED isolation level it's possible that newtuple was
|
||||
* changed due to concurrent update.
|
||||
*/
|
||||
if (newSlot != NULL)
|
||||
intuple = newtuple = ExecRemoveJunk(estate->es_junkFilter, newSlot);
|
||||
@@ -1306,13 +1485,13 @@ ExecARUpdateTriggers(EState *estate, ResultRelInfo *relinfo,
|
||||
{
|
||||
TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
|
||||
|
||||
if (trigdesc->n_after_row[TRIGGER_EVENT_UPDATE] > 0)
|
||||
if (trigdesc && trigdesc->n_after_row[TRIGGER_EVENT_UPDATE] > 0)
|
||||
{
|
||||
HeapTuple trigtuple = GetTupleForTrigger(estate, relinfo,
|
||||
tupleid, NULL);
|
||||
|
||||
DeferredTriggerSaveEvent(relinfo, TRIGGER_EVENT_UPDATE,
|
||||
trigtuple, newtuple);
|
||||
true, trigtuple, newtuple);
|
||||
heap_freetuple(trigtuple);
|
||||
}
|
||||
}
|
||||
@@ -1344,7 +1523,7 @@ ltrmark:;
|
||||
case HeapTupleSelfUpdated:
|
||||
/* treat it as deleted; do not process */
|
||||
ReleaseBuffer(buffer);
|
||||
return (NULL);
|
||||
return NULL;
|
||||
|
||||
case HeapTupleMayBeUpdated:
|
||||
break;
|
||||
@@ -1371,12 +1550,12 @@ ltrmark:;
|
||||
* if tuple was deleted or PlanQual failed for updated
|
||||
* tuple - we have not process this tuple!
|
||||
*/
|
||||
return (NULL);
|
||||
return NULL;
|
||||
|
||||
default:
|
||||
ReleaseBuffer(buffer);
|
||||
elog(ERROR, "Unknown status %u from heap_mark4update", test);
|
||||
return (NULL);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -1466,7 +1645,7 @@ deferredTriggerCheckState(Oid tgoid, int32 itemstate)
|
||||
|
||||
/*
|
||||
* Not deferrable triggers (i.e. normal AFTER ROW triggers and
|
||||
* constraints declared NOT DEFERRABLE, the state is allways false.
|
||||
* constraints declared NOT DEFERRABLE, the state is always false.
|
||||
*/
|
||||
if ((itemstate & TRIGGER_DEFERRED_DEFERRABLE) == 0)
|
||||
return false;
|
||||
@@ -1590,7 +1769,7 @@ DeferredTriggerExecute(DeferredTriggerEvent event, int itemno,
|
||||
*/
|
||||
LocTriggerData.type = T_TriggerData;
|
||||
LocTriggerData.tg_event = (event->dte_event & TRIGGER_EVENT_OPMASK) |
|
||||
TRIGGER_EVENT_ROW;
|
||||
(event->dte_event & TRIGGER_EVENT_ROW);
|
||||
LocTriggerData.tg_relation = rel;
|
||||
|
||||
LocTriggerData.tg_trigger = NULL;
|
||||
@@ -2139,7 +2318,7 @@ DeferredTriggerSetState(ConstraintsSetStmt *stmt)
|
||||
* ----------
|
||||
*/
|
||||
static void
|
||||
DeferredTriggerSaveEvent(ResultRelInfo *relinfo, int event,
|
||||
DeferredTriggerSaveEvent(ResultRelInfo *relinfo, int event, bool row_trigger,
|
||||
HeapTuple oldtup, HeapTuple newtup)
|
||||
{
|
||||
Relation rel = relinfo->ri_RelationDesc;
|
||||
@@ -2152,7 +2331,6 @@ DeferredTriggerSaveEvent(ResultRelInfo *relinfo, int event,
|
||||
int *tgindx;
|
||||
ItemPointerData oldctid;
|
||||
ItemPointerData newctid;
|
||||
TriggerData LocTriggerData;
|
||||
|
||||
if (deftrig_cxt == NULL)
|
||||
elog(ERROR,
|
||||
@@ -2175,14 +2353,25 @@ DeferredTriggerSaveEvent(ResultRelInfo *relinfo, int event,
|
||||
*/
|
||||
oldcxt = MemoryContextSwitchTo(deftrig_cxt);
|
||||
|
||||
ntriggers = trigdesc->n_after_row[event];
|
||||
tgindx = trigdesc->tg_after_row[event];
|
||||
if (row_trigger)
|
||||
{
|
||||
ntriggers = trigdesc->n_after_row[event];
|
||||
tgindx = trigdesc->tg_after_row[event];
|
||||
}
|
||||
else
|
||||
{
|
||||
ntriggers = trigdesc->n_after_statement[event];
|
||||
tgindx = trigdesc->tg_after_statement[event];
|
||||
}
|
||||
|
||||
new_size = offsetof(DeferredTriggerEventData, dte_item[0]) +
|
||||
ntriggers * sizeof(DeferredTriggerEventItem);
|
||||
|
||||
new_event = (DeferredTriggerEvent) palloc(new_size);
|
||||
new_event->dte_next = NULL;
|
||||
new_event->dte_event = event & TRIGGER_EVENT_OPMASK;
|
||||
if (row_trigger)
|
||||
new_event->dte_event |= TRIGGER_EVENT_ROW;
|
||||
new_event->dte_relid = rel->rd_id;
|
||||
ItemPointerCopy(&oldctid, &(new_event->dte_oldctid));
|
||||
ItemPointerCopy(&newctid, &(new_event->dte_newctid));
|
||||
@@ -2190,15 +2379,21 @@ DeferredTriggerSaveEvent(ResultRelInfo *relinfo, int event,
|
||||
for (i = 0; i < ntriggers; i++)
|
||||
{
|
||||
Trigger *trigger = &trigdesc->triggers[tgindx[i]];
|
||||
DeferredTriggerEventItem *ev_item = &(new_event->dte_item[i]);
|
||||
|
||||
new_event->dte_item[i].dti_tgoid = trigger->tgoid;
|
||||
new_event->dte_item[i].dti_state =
|
||||
ev_item->dti_tgoid = trigger->tgoid;
|
||||
ev_item->dti_state =
|
||||
((trigger->tgdeferrable) ?
|
||||
TRIGGER_DEFERRED_DEFERRABLE : 0) |
|
||||
((trigger->tginitdeferred) ?
|
||||
TRIGGER_DEFERRED_INITDEFERRED : 0) |
|
||||
((trigdesc->n_before_row[event] > 0) ?
|
||||
TRIGGER_DEFERRED_HAS_BEFORE : 0);
|
||||
TRIGGER_DEFERRED_INITDEFERRED : 0);
|
||||
|
||||
if (row_trigger && (trigdesc->n_before_row[event] > 0))
|
||||
ev_item->dti_state |= TRIGGER_DEFERRED_HAS_BEFORE;
|
||||
else if (!row_trigger && (trigdesc->n_before_statement[event] > 0))
|
||||
{
|
||||
ev_item->dti_state |= TRIGGER_DEFERRED_HAS_BEFORE;
|
||||
}
|
||||
}
|
||||
|
||||
MemoryContextSwitchTo(oldcxt);
|
||||
@@ -2219,6 +2414,7 @@ DeferredTriggerSaveEvent(ResultRelInfo *relinfo, int event,
|
||||
Trigger *trigger = &trigdesc->triggers[tgindx[i]];
|
||||
bool is_ri_trigger;
|
||||
bool key_unchanged;
|
||||
TriggerData LocTriggerData;
|
||||
|
||||
/*
|
||||
* We are interested in RI_FKEY triggers only.
|
||||
|
@@ -13,7 +13,7 @@
|
||||
*
|
||||
* These three procedures are the external interfaces to the executor.
|
||||
* In each case, the query descriptor and the execution state is required
|
||||
* as arguments
|
||||
* as arguments
|
||||
*
|
||||
* ExecutorStart() must be called at the beginning of any execution of any
|
||||
* query plan and ExecutorEnd() should always be called at the end of
|
||||
@@ -27,7 +27,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.186 2002/11/13 00:44:08 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.187 2002/11/23 03:59:07 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -908,12 +908,12 @@ ExecutePlan(EState *estate,
|
||||
ScanDirection direction,
|
||||
DestReceiver *destfunc)
|
||||
{
|
||||
JunkFilter *junkfilter;
|
||||
TupleTableSlot *slot;
|
||||
ItemPointer tupleid = NULL;
|
||||
ItemPointerData tuple_ctid;
|
||||
long current_tuple_count;
|
||||
TupleTableSlot *result;
|
||||
JunkFilter *junkfilter;
|
||||
TupleTableSlot *slot;
|
||||
ItemPointer tupleid = NULL;
|
||||
ItemPointerData tuple_ctid;
|
||||
long current_tuple_count;
|
||||
TupleTableSlot *result;
|
||||
|
||||
/*
|
||||
* initialize local variables
|
||||
@@ -927,6 +927,24 @@ ExecutePlan(EState *estate,
|
||||
*/
|
||||
estate->es_direction = direction;
|
||||
|
||||
/*
|
||||
* Process BEFORE EACH STATEMENT triggers
|
||||
*/
|
||||
switch (operation)
|
||||
{
|
||||
case CMD_UPDATE:
|
||||
ExecBSUpdateTriggers(estate, estate->es_result_relation_info);
|
||||
break;
|
||||
case CMD_DELETE:
|
||||
ExecBSDeleteTriggers(estate, estate->es_result_relation_info);
|
||||
break;
|
||||
case CMD_INSERT:
|
||||
ExecBSInsertTriggers(estate, estate->es_result_relation_info);
|
||||
break;
|
||||
default:
|
||||
/* do nothing */
|
||||
}
|
||||
|
||||
/*
|
||||
* Loop until we've processed the proper number of tuples from the
|
||||
* plan.
|
||||
@@ -1124,6 +1142,24 @@ lnext: ;
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Process AFTER EACH STATEMENT triggers
|
||||
*/
|
||||
switch (operation)
|
||||
{
|
||||
case CMD_UPDATE:
|
||||
ExecASUpdateTriggers(estate, estate->es_result_relation_info);
|
||||
break;
|
||||
case CMD_DELETE:
|
||||
ExecASDeleteTriggers(estate, estate->es_result_relation_info);
|
||||
break;
|
||||
case CMD_INSERT:
|
||||
ExecASInsertTriggers(estate, estate->es_result_relation_info);
|
||||
break;
|
||||
default:
|
||||
/* do nothing */
|
||||
}
|
||||
|
||||
/*
|
||||
* here, result is either a slot containing a tuple in the case of a
|
||||
* SELECT or NULL otherwise.
|
||||
@@ -1205,7 +1241,7 @@ ExecInsert(TupleTableSlot *slot,
|
||||
|
||||
/* BEFORE ROW INSERT Triggers */
|
||||
if (resultRelInfo->ri_TrigDesc &&
|
||||
resultRelInfo->ri_TrigDesc->n_before_row[TRIGGER_EVENT_INSERT] > 0)
|
||||
resultRelInfo->ri_TrigDesc->n_before_row[TRIGGER_EVENT_INSERT] > 0)
|
||||
{
|
||||
HeapTuple newtuple;
|
||||
|
||||
@@ -1256,8 +1292,7 @@ ExecInsert(TupleTableSlot *slot,
|
||||
ExecInsertIndexTuples(slot, &(tuple->t_self), estate, false);
|
||||
|
||||
/* AFTER ROW INSERT Triggers */
|
||||
if (resultRelInfo->ri_TrigDesc)
|
||||
ExecARInsertTriggers(estate, resultRelInfo, tuple);
|
||||
ExecARInsertTriggers(estate, resultRelInfo, tuple);
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
@@ -1346,8 +1381,7 @@ ldelete:;
|
||||
*/
|
||||
|
||||
/* AFTER ROW DELETE Triggers */
|
||||
if (resultRelInfo->ri_TrigDesc)
|
||||
ExecARDeleteTriggers(estate, resultRelInfo, tupleid);
|
||||
ExecARDeleteTriggers(estate, resultRelInfo, tupleid);
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
@@ -1498,8 +1532,7 @@ lreplace:;
|
||||
ExecInsertIndexTuples(slot, &(tuple->t_self), estate, false);
|
||||
|
||||
/* AFTER ROW UPDATE Triggers */
|
||||
if (resultRelInfo->ri_TrigDesc)
|
||||
ExecARUpdateTriggers(estate, resultRelInfo, tupleid, tuple);
|
||||
ExecARUpdateTriggers(estate, resultRelInfo, tupleid, tuple);
|
||||
}
|
||||
|
||||
static char *
|
||||
|
@@ -15,7 +15,7 @@
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.219 2002/11/19 23:21:58 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.220 2002/11/23 03:59:07 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -2482,14 +2482,6 @@ _copyCreateTrigStmt(CreateTrigStmt *from)
|
||||
newnode->before = from->before;
|
||||
newnode->row = from->row;
|
||||
memcpy(newnode->actions, from->actions, sizeof(from->actions));
|
||||
if (from->lang)
|
||||
newnode->lang = pstrdup(from->lang);
|
||||
if (from->text)
|
||||
newnode->text = pstrdup(from->text);
|
||||
|
||||
Node_Copy(from, newnode, attr);
|
||||
if (from->when)
|
||||
newnode->when = pstrdup(from->when);
|
||||
newnode->isconstraint = from->isconstraint;
|
||||
newnode->deferrable = from->deferrable;
|
||||
newnode->initdeferred = from->initdeferred;
|
||||
|
@@ -20,7 +20,7 @@
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.165 2002/11/19 23:21:58 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.166 2002/11/23 03:59:07 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -1291,14 +1291,6 @@ _equalCreateTrigStmt(CreateTrigStmt *a, CreateTrigStmt *b)
|
||||
return false;
|
||||
if (strcmp(a->actions, b->actions) != 0)
|
||||
return false;
|
||||
if (!equalstr(a->lang, b->lang))
|
||||
return false;
|
||||
if (!equalstr(a->text, b->text))
|
||||
return false;
|
||||
if (!equal(a->attr, b->attr))
|
||||
return false;
|
||||
if (!equalstr(a->when, b->when))
|
||||
return false;
|
||||
if (a->isconstraint != b->isconstraint)
|
||||
return false;
|
||||
if (a->deferrable != b->deferrable)
|
||||
|
@@ -11,7 +11,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.380 2002/11/18 17:12:07 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.381 2002/11/23 03:59:08 momjian Exp $
|
||||
*
|
||||
* HISTORY
|
||||
* AUTHOR DATE MAJOR EVENT
|
||||
@@ -1371,7 +1371,7 @@ opt_using:
|
||||
/*****************************************************************************
|
||||
*
|
||||
* QUERY :
|
||||
* CREATE relname
|
||||
* CREATE TABLE relname
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
@@ -2028,11 +2028,6 @@ CreateTrigStmt:
|
||||
n->before = $4;
|
||||
n->row = $8;
|
||||
memcpy (n->actions, $5, 4);
|
||||
n->lang = NULL; /* unused */
|
||||
n->text = NULL; /* unused */
|
||||
n->attr = NULL; /* unused */
|
||||
n->when = NULL; /* unused */
|
||||
|
||||
n->isconstraint = FALSE;
|
||||
n->deferrable = FALSE;
|
||||
n->initdeferred = FALSE;
|
||||
@@ -2053,11 +2048,6 @@ CreateTrigStmt:
|
||||
n->before = FALSE;
|
||||
n->row = TRUE;
|
||||
memcpy (n->actions, $6, 4);
|
||||
n->lang = NULL; /* unused */
|
||||
n->text = NULL; /* unused */
|
||||
n->attr = NULL; /* unused */
|
||||
n->when = NULL; /* unused */
|
||||
|
||||
n->isconstraint = TRUE;
|
||||
n->deferrable = ($10 & 1) != 0;
|
||||
n->initdeferred = ($10 & 2) != 0;
|
||||
@@ -2075,17 +2065,17 @@ TriggerActionTime:
|
||||
TriggerEvents:
|
||||
TriggerOneEvent
|
||||
{
|
||||
char *e = palloc (4);
|
||||
char *e = palloc(4);
|
||||
e[0] = $1; e[1] = 0; $$ = e;
|
||||
}
|
||||
| TriggerOneEvent OR TriggerOneEvent
|
||||
{
|
||||
char *e = palloc (4);
|
||||
char *e = palloc(4);
|
||||
e[0] = $1; e[1] = $3; e[2] = 0; $$ = e;
|
||||
}
|
||||
| TriggerOneEvent OR TriggerOneEvent OR TriggerOneEvent
|
||||
{
|
||||
char *e = palloc (4);
|
||||
char *e = palloc(4);
|
||||
e[0] = $1; e[1] = $3; e[2] = $5; e[3] = 0;
|
||||
$$ = e;
|
||||
}
|
||||
@@ -2102,6 +2092,14 @@ TriggerForSpec:
|
||||
{
|
||||
$$ = $3;
|
||||
}
|
||||
| /* EMPTY */
|
||||
{
|
||||
/*
|
||||
* If ROW/STATEMENT not specified, default to
|
||||
* STATEMENT, per SQL
|
||||
*/
|
||||
$$ = FALSE;
|
||||
}
|
||||
;
|
||||
|
||||
TriggerForOpt:
|
||||
@@ -2124,7 +2122,7 @@ TriggerFuncArg:
|
||||
ICONST
|
||||
{
|
||||
char buf[64];
|
||||
snprintf (buf, sizeof(buf), "%d", $1);
|
||||
snprintf(buf, sizeof(buf), "%d", $1);
|
||||
$$ = makeString(pstrdup(buf));
|
||||
}
|
||||
| FCONST { $$ = makeString($1); }
|
||||
|
@@ -1,7 +1,7 @@
|
||||
/* ----------
|
||||
* pg_lzcompress.c -
|
||||
*
|
||||
* $Header: /cvsroot/pgsql/src/backend/utils/adt/pg_lzcompress.c,v 1.15 2002/09/04 20:31:28 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/utils/adt/pg_lzcompress.c,v 1.16 2002/11/23 03:59:08 momjian Exp $
|
||||
*
|
||||
* This is an implementation of LZ compression for PostgreSQL.
|
||||
* It uses a simple history table and generates 2-3 byte tags
|
||||
@@ -87,7 +87,7 @@
|
||||
* OOOO LLLL OOOO OOOO
|
||||
*
|
||||
* This limits the offset to 1-4095 (12 bits) and the length
|
||||
* to 3-18 (4 bits) because 3 is allways added to it. To emit
|
||||
* to 3-18 (4 bits) because 3 is always added to it. To emit
|
||||
* a tag of 2 bytes with a length of 2 only saves one control
|
||||
* bit. But we lose one byte in the possible length of a tag.
|
||||
*
|
||||
@@ -230,7 +230,7 @@ static PGLZ_Strategy strategy_default_data = {
|
||||
PGLZ_Strategy *PGLZ_strategy_default = &strategy_default_data;
|
||||
|
||||
|
||||
static PGLZ_Strategy strategy_allways_data = {
|
||||
static PGLZ_Strategy strategy_always_data = {
|
||||
0, /* Chunks of any size are compressed */
|
||||
0, /* */
|
||||
0, /* We want to save at least one single
|
||||
@@ -239,7 +239,7 @@ static PGLZ_Strategy strategy_allways_data = {
|
||||
* bytes is found */
|
||||
6 /* Look harder for a good match. */
|
||||
};
|
||||
PGLZ_Strategy *PGLZ_strategy_allways = &strategy_allways_data;
|
||||
PGLZ_Strategy *PGLZ_strategy_always = &strategy_always_data;
|
||||
|
||||
|
||||
static PGLZ_Strategy strategy_never_data = {
|
||||
@@ -247,7 +247,7 @@ static PGLZ_Strategy strategy_never_data = {
|
||||
0, /* */
|
||||
0, /* */
|
||||
0, /* Zero indicates "store uncompressed
|
||||
* allways" */
|
||||
* always" */
|
||||
0 /* */
|
||||
};
|
||||
PGLZ_Strategy *PGLZ_strategy_never = &strategy_never_data;
|
||||
@@ -716,7 +716,7 @@ pglz_decompress(PGLZ_Header *source, char *dest)
|
||||
|
||||
/*
|
||||
* Now we copy the bytes specified by the tag from OUTPUT
|
||||
* to OUTPUT. It is dangerous and platform dependant to
|
||||
* to OUTPUT. It is dangerous and platform dependent to
|
||||
* use memcpy() here, because the copied areas could
|
||||
* overlap extremely!
|
||||
*/
|
||||
|
Reference in New Issue
Block a user