aboutsummaryrefslogtreecommitdiff
path: root/src/backend/commands/trigger.c
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2009-10-26 02:26:45 +0000
committerTom Lane <tgl@sss.pgh.pa.us>2009-10-26 02:26:45 +0000
commit9f2ee8f287098fb8067593b38da0650df458b20a (patch)
tree8998549ba80c6f5b397ad1e77dc6f03aefee00c2 /src/backend/commands/trigger.c
parent76d8883c8e3647ac2f7ff3c48226a25b1fd7888b (diff)
downloadpostgresql-9f2ee8f287098fb8067593b38da0650df458b20a.tar.gz
postgresql-9f2ee8f287098fb8067593b38da0650df458b20a.zip
Re-implement EvalPlanQual processing to improve its performance and eliminate
a lot of strange behaviors that occurred in join cases. We now identify the "current" row for every joined relation in UPDATE, DELETE, and SELECT FOR UPDATE/SHARE queries. If an EvalPlanQual recheck is necessary, we jam the appropriate row into each scan node in the rechecking plan, forcing it to emit only that one row. The former behavior could rescan the whole of each joined relation for each recheck, which was terrible for performance, and what's much worse could result in duplicated output tuples. Also, the original implementation of EvalPlanQual could not re-use the recheck execution tree --- it had to go through a full executor init and shutdown for every row to be tested. To avoid this overhead, I've associated a special runtime Param with each LockRows or ModifyTable plan node, and arranged to make every scan node below such a node depend on that Param. Thus, by signaling a change in that Param, the EPQ machinery can just rescan the already-built test plan. This patch also adds a prohibition on set-returning functions in the targetlist of SELECT FOR UPDATE/SHARE. This is needed to avoid the duplicate-output-tuple problem. It seems fairly reasonable since the other restrictions on SELECT FOR UPDATE are meant to ensure that there is a unique correspondence between source tuples and result tuples, which an output SRF destroys as much as anything else does.
Diffstat (limited to 'src/backend/commands/trigger.c')
-rw-r--r--src/backend/commands/trigger.c32
1 files changed, 20 insertions, 12 deletions
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index dd526f6db19..cdd545eeaa6 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.254 2009/10/14 22:14:21 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/trigger.c,v 1.255 2009/10/26 02:26:28 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -61,7 +61,7 @@ int SessionReplicationRole = SESSION_REPLICATION_ROLE_ORIGIN;
static void ConvertTriggerToFK(CreateTrigStmt *stmt, Oid funcoid);
static void InsertTrigger(TriggerDesc *trigdesc, Trigger *trigger, int indx);
static HeapTuple GetTupleForTrigger(EState *estate,
- PlanState *subplanstate,
+ EPQState *epqstate,
ResultRelInfo *relinfo,
ItemPointer tid,
TupleTableSlot **newSlot);
@@ -1828,7 +1828,7 @@ ExecASDeleteTriggers(EState *estate, ResultRelInfo *relinfo)
}
bool
-ExecBRDeleteTriggers(EState *estate, PlanState *subplanstate,
+ExecBRDeleteTriggers(EState *estate, EPQState *epqstate,
ResultRelInfo *relinfo,
ItemPointer tupleid)
{
@@ -1842,7 +1842,7 @@ ExecBRDeleteTriggers(EState *estate, PlanState *subplanstate,
TupleTableSlot *newSlot;
int i;
- trigtuple = GetTupleForTrigger(estate, subplanstate, relinfo, tupleid,
+ trigtuple = GetTupleForTrigger(estate, epqstate, relinfo, tupleid,
&newSlot);
if (trigtuple == NULL)
return false;
@@ -1964,7 +1964,7 @@ ExecASUpdateTriggers(EState *estate, ResultRelInfo *relinfo)
}
HeapTuple
-ExecBRUpdateTriggers(EState *estate, PlanState *subplanstate,
+ExecBRUpdateTriggers(EState *estate, EPQState *epqstate,
ResultRelInfo *relinfo,
ItemPointer tupleid, HeapTuple newtuple)
{
@@ -1979,7 +1979,7 @@ ExecBRUpdateTriggers(EState *estate, PlanState *subplanstate,
int i;
Bitmapset *modifiedCols;
- trigtuple = GetTupleForTrigger(estate, subplanstate, relinfo, tupleid,
+ trigtuple = GetTupleForTrigger(estate, epqstate, relinfo, tupleid,
&newSlot);
if (trigtuple == NULL)
return NULL;
@@ -2107,7 +2107,7 @@ ExecASTruncateTriggers(EState *estate, ResultRelInfo *relinfo)
static HeapTuple
GetTupleForTrigger(EState *estate,
- PlanState *subplanstate,
+ EPQState *epqstate,
ResultRelInfo *relinfo,
ItemPointer tid,
TupleTableSlot **newSlot)
@@ -2125,8 +2125,8 @@ GetTupleForTrigger(EState *estate,
*newSlot = NULL;
- /* caller must pass a subplanstate if EvalPlanQual is possible */
- Assert(subplanstate != NULL);
+ /* caller must pass an epqstate if EvalPlanQual is possible */
+ Assert(epqstate != NULL);
/*
* lock tuple for update
@@ -2153,27 +2153,35 @@ ltrmark:;
ereport(ERROR,
(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
errmsg("could not serialize access due to concurrent update")));
- else if (!ItemPointerEquals(&update_ctid, &tuple.t_self))
+ if (!ItemPointerEquals(&update_ctid, &tuple.t_self))
{
/* it was updated, so look at the updated version */
TupleTableSlot *epqslot;
epqslot = EvalPlanQual(estate,
+ epqstate,
+ relation,
relinfo->ri_RangeTableIndex,
- subplanstate,
&update_ctid,
update_xmax);
if (!TupIsNull(epqslot))
{
*tid = update_ctid;
*newSlot = epqslot;
+
+ /*
+ * EvalPlanQual already locked the tuple, but we
+ * re-call heap_lock_tuple anyway as an easy way
+ * of re-fetching the correct tuple. Speed is
+ * hardly a criterion in this path anyhow.
+ */
goto ltrmark;
}
}
/*
* if tuple was deleted or PlanQual failed for updated tuple -
- * we have not process this tuple!
+ * we must not process this tuple!
*/
return NULL;