aboutsummaryrefslogtreecommitdiff
path: root/src/backend/executor/nodeLockRows.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/executor/nodeLockRows.c')
-rw-r--r--src/backend/executor/nodeLockRows.c170
1 files changed, 88 insertions, 82 deletions
diff --git a/src/backend/executor/nodeLockRows.c b/src/backend/executor/nodeLockRows.c
index 80f7e3cdafb..f38d34a0475 100644
--- a/src/backend/executor/nodeLockRows.c
+++ b/src/backend/executor/nodeLockRows.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/executor/nodeLockRows.c,v 1.1 2009/10/12 18:10:43 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/executor/nodeLockRows.c,v 1.2 2009/10/26 02:26:31 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -25,6 +25,7 @@
#include "executor/executor.h"
#include "executor/nodeLockRows.h"
#include "storage/bufmgr.h"
+#include "utils/tqual.h"
/* ----------------------------------------------------------------
@@ -37,7 +38,7 @@ ExecLockRows(LockRowsState *node)
TupleTableSlot *slot;
EState *estate;
PlanState *outerPlan;
- bool epq_pushed;
+ bool epq_started;
ListCell *lc;
/*
@@ -47,30 +48,19 @@ ExecLockRows(LockRowsState *node)
outerPlan = outerPlanState(node);
/*
- * Get next tuple from subplan, if any; but if we are evaluating
- * an EvalPlanQual substitution, first finish that.
+ * Get next tuple from subplan, if any.
*/
lnext:
- if (node->lr_useEvalPlan)
- {
- slot = EvalPlanQualNext(estate);
- if (TupIsNull(slot))
- {
- EvalPlanQualPop(estate, outerPlan);
- node->lr_useEvalPlan = false;
- slot = ExecProcNode(outerPlan);
- }
- }
- else
- slot = ExecProcNode(outerPlan);
+ slot = ExecProcNode(outerPlan);
if (TupIsNull(slot))
return NULL;
/*
- * Attempt to lock the source tuple(s).
+ * Attempt to lock the source tuple(s). (Note we only have locking
+ * rowmarks in lr_rowMarks.)
*/
- epq_pushed = false;
+ epq_started = false;
foreach(lc, node->lr_rowMarks)
{
ExecRowMark *erm = (ExecRowMark *) lfirst(lc);
@@ -84,6 +74,10 @@ lnext:
HTSU_Result test;
HeapTuple copyTuple;
+ /* clear any leftover test tuple for this rel */
+ if (node->lr_epqstate.estate != NULL)
+ EvalPlanQualSetTuple(&node->lr_epqstate, erm->rti, NULL);
+
/* if child rel, must check whether it produced this row */
if (erm->rti != erm->prti)
{
@@ -115,7 +109,7 @@ lnext:
tuple.t_self = *((ItemPointer) DatumGetPointer(datum));
/* okay, try to lock the tuple */
- if (erm->forUpdate)
+ if (erm->markType == ROW_MARK_EXCLUSIVE)
lockmode = LockTupleExclusive;
else
lockmode = LockTupleShared;
@@ -129,8 +123,6 @@ lnext:
{
case HeapTupleSelfUpdated:
/* treat it as deleted; do not process */
- if (epq_pushed)
- EvalPlanQualPop(estate, outerPlan);
goto lnext;
case HeapTupleMayBeUpdated:
@@ -146,35 +138,33 @@ lnext:
&tuple.t_self))
{
/* Tuple was deleted, so don't return it */
- if (epq_pushed)
- EvalPlanQualPop(estate, outerPlan);
goto lnext;
}
- /* updated, so look at updated version */
- copyTuple = EvalPlanQualFetch(estate, erm->rti,
+ /* updated, so fetch and lock the updated version */
+ copyTuple = EvalPlanQualFetch(estate, erm->relation, lockmode,
&update_ctid, update_xmax);
if (copyTuple == NULL)
{
/* Tuple was deleted, so don't return it */
- if (epq_pushed)
- EvalPlanQualPop(estate, outerPlan);
goto lnext;
}
+ /* remember the actually locked tuple's TID */
+ tuple.t_self = copyTuple->t_self;
/*
- * Need to run a recheck subquery.
- * Find or create a PQ stack entry.
+ * Need to run a recheck subquery. Initialize EPQ state
+ * if we didn't do so already.
*/
- if (!epq_pushed)
+ if (!epq_started)
{
- EvalPlanQualPush(estate, erm->rti, outerPlan);
- epq_pushed = true;
+ EvalPlanQualBegin(&node->lr_epqstate, estate);
+ epq_started = true;
}
/* Store target tuple for relation's scan node */
- EvalPlanQualSetTuple(estate, erm->rti, copyTuple);
+ EvalPlanQualSetTuple(&node->lr_epqstate, erm->rti, copyTuple);
/* Continue loop until we have all target tuples */
break;
@@ -188,11 +178,52 @@ lnext:
erm->curCtid = tuple.t_self;
}
- /* If we need to do EvalPlanQual testing, loop back to do that */
- if (epq_pushed)
+ /*
+ * If we need to do EvalPlanQual testing, do so.
+ */
+ if (epq_started)
{
- node->lr_useEvalPlan = true;
- goto lnext;
+ /*
+ * First, fetch a copy of any rows that were successfully locked
+ * without any update having occurred. (We do this in a separate
+ * pass so as to avoid overhead in the common case where there are
+ * no concurrent updates.)
+ */
+ foreach(lc, node->lr_rowMarks)
+ {
+ ExecRowMark *erm = (ExecRowMark *) lfirst(lc);
+ HeapTupleData tuple;
+ Buffer buffer;
+
+ if (EvalPlanQualGetTuple(&node->lr_epqstate, erm->rti) != NULL)
+ continue; /* it was updated and fetched above */
+
+ /* okay, fetch the tuple */
+ tuple.t_self = erm->curCtid;
+ if (!heap_fetch(erm->relation, SnapshotAny, &tuple, &buffer,
+ false, NULL))
+ elog(ERROR, "failed to fetch tuple for EvalPlanQual recheck");
+
+ /* successful, copy and store tuple */
+ EvalPlanQualSetTuple(&node->lr_epqstate, erm->rti,
+ heap_copytuple(&tuple));
+ ReleaseBuffer(buffer);
+ }
+ /*
+ * Now fetch any non-locked source rows --- the EPQ logic knows
+ * how to do that.
+ */
+ EvalPlanQualSetSlot(&node->lr_epqstate, slot);
+ EvalPlanQualFetchRowMarks(&node->lr_epqstate);
+ /*
+ * And finally we can re-evaluate the tuple.
+ */
+ slot = EvalPlanQualNext(&node->lr_epqstate);
+ if (TupIsNull(slot))
+ {
+ /* Updated tuple fails qual, so ignore it and go on */
+ goto lnext;
+ }
}
/* Got all locks, so return the current tuple */
@@ -210,8 +241,7 @@ LockRowsState *
ExecInitLockRows(LockRows *node, EState *estate, int eflags)
{
LockRowsState *lrstate;
- Plan *outerPlan;
- JunkFilter *j;
+ Plan *outerPlan = outerPlan(node);
ListCell *lc;
/* check for unsupported flags */
@@ -223,7 +253,7 @@ ExecInitLockRows(LockRows *node, EState *estate, int eflags)
lrstate = makeNode(LockRowsState);
lrstate->ps.plan = (Plan *) node;
lrstate->ps.state = estate;
- lrstate->lr_useEvalPlan = false;
+ EvalPlanQualInit(&lrstate->lr_epqstate, estate, outerPlan, node->epqParam);
/*
* Miscellaneous initialization
@@ -239,7 +269,6 @@ ExecInitLockRows(LockRows *node, EState *estate, int eflags)
/*
* then initialize outer plan
*/
- outerPlan = outerPlan(node);
outerPlanState(lrstate) = ExecInitNode(outerPlan, estate, eflags);
/*
@@ -250,28 +279,18 @@ ExecInitLockRows(LockRows *node, EState *estate, int eflags)
lrstate->ps.ps_ProjInfo = NULL;
/*
- * Initialize a junkfilter that we'll use to extract the ctid junk
- * attributes. (We won't actually apply the filter to remove the
- * junk, we just pass the rows on as-is. This is because the
- * junkfilter isn't smart enough to not remove junk attrs that
- * might be needed further up.)
- */
- j = ExecInitJunkFilter(outerPlan->targetlist, false,
- ExecInitExtraTupleSlot(estate));
- lrstate->lr_junkFilter = j;
-
- /*
* Locate the ExecRowMark(s) that this node is responsible for.
* (InitPlan should already have built the global list of ExecRowMarks.)
*/
lrstate->lr_rowMarks = NIL;
foreach(lc, node->rowMarks)
{
- RowMarkClause *rc = (RowMarkClause *) lfirst(lc);
+ PlanRowMark *rc = (PlanRowMark *) lfirst(lc);
ExecRowMark *erm = NULL;
- char resname[32];
ListCell *lce;
+ Assert(IsA(rc, PlanRowMark));
+
/* ignore "parent" rowmarks; they are irrelevant at runtime */
if (rc->isParent)
continue;
@@ -279,36 +298,24 @@ ExecInitLockRows(LockRows *node, EState *estate, int eflags)
foreach(lce, estate->es_rowMarks)
{
erm = (ExecRowMark *) lfirst(lce);
- if (erm->rti == rc->rti &&
- erm->prti == rc->prti &&
- erm->rowmarkId == rc->rowmarkId)
+ if (erm->rti == rc->rti)
break;
erm = NULL;
}
if (erm == NULL)
- elog(ERROR, "failed to find ExecRowMark for RowMarkClause");
- if (AttributeNumberIsValid(erm->ctidAttNo))
- elog(ERROR, "ExecRowMark is already claimed");
-
- /* Locate the junk attribute columns in the subplan output */
-
- /* always need the ctid */
- snprintf(resname, sizeof(resname), "ctid%u", erm->rowmarkId);
- erm->ctidAttNo = ExecFindJunkAttribute(j, resname);
- if (!AttributeNumberIsValid(erm->ctidAttNo))
- elog(ERROR, "could not find junk \"%s\" column",
- resname);
- /* if child relation, need tableoid too */
- if (erm->rti != erm->prti)
- {
- snprintf(resname, sizeof(resname), "tableoid%u", erm->rowmarkId);
- erm->toidAttNo = ExecFindJunkAttribute(j, resname);
- if (!AttributeNumberIsValid(erm->toidAttNo))
- elog(ERROR, "could not find junk \"%s\" column",
- resname);
- }
-
- lrstate->lr_rowMarks = lappend(lrstate->lr_rowMarks, erm);
+ elog(ERROR, "failed to find ExecRowMark for PlanRowMark %u",
+ rc->rti);
+
+ /*
+ * Only locking rowmarks go into our own list. Non-locking marks
+ * are passed off to the EvalPlanQual machinery. This is because
+ * we don't want to bother fetching non-locked rows unless we
+ * actually have to do an EPQ recheck.
+ */
+ if (RowMarkRequiresRowShareLock(erm->markType))
+ lrstate->lr_rowMarks = lappend(lrstate->lr_rowMarks, erm);
+ else
+ EvalPlanQualAddRowMark(&lrstate->lr_epqstate, erm);
}
return lrstate;
@@ -324,6 +331,7 @@ ExecInitLockRows(LockRows *node, EState *estate, int eflags)
void
ExecEndLockRows(LockRowsState *node)
{
+ EvalPlanQualEnd(&node->lr_epqstate);
ExecEndNode(outerPlanState(node));
}
@@ -331,8 +339,6 @@ ExecEndLockRows(LockRowsState *node)
void
ExecReScanLockRows(LockRowsState *node, ExprContext *exprCtxt)
{
- node->lr_useEvalPlan = false;
-
/*
* if chgParam of subnode is not null then plan will be re-scanned by
* first ExecProcNode.