diff options
Diffstat (limited to 'src/backend/executor/nodeLockRows.c')
-rw-r--r-- | src/backend/executor/nodeLockRows.c | 342 |
1 files changed, 342 insertions, 0 deletions
diff --git a/src/backend/executor/nodeLockRows.c b/src/backend/executor/nodeLockRows.c new file mode 100644 index 00000000000..80f7e3cdafb --- /dev/null +++ b/src/backend/executor/nodeLockRows.c @@ -0,0 +1,342 @@ +/*------------------------------------------------------------------------- + * + * nodeLockRows.c + * Routines to handle FOR UPDATE/FOR SHARE row locking + * + * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $PostgreSQL: pgsql/src/backend/executor/nodeLockRows.c,v 1.1 2009/10/12 18:10:43 tgl Exp $ + * + *------------------------------------------------------------------------- + */ +/* + * INTERFACE ROUTINES + * ExecLockRows - fetch locked rows + * ExecInitLockRows - initialize node and subnodes.. + * ExecEndLockRows - shutdown node and subnodes + */ + +#include "postgres.h" + +#include "access/xact.h" +#include "executor/executor.h" +#include "executor/nodeLockRows.h" +#include "storage/bufmgr.h" + + +/* ---------------------------------------------------------------- + * ExecLockRows + * ---------------------------------------------------------------- + */ +TupleTableSlot * /* return: a tuple or NULL */ +ExecLockRows(LockRowsState *node) +{ + TupleTableSlot *slot; + EState *estate; + PlanState *outerPlan; + bool epq_pushed; + ListCell *lc; + + /* + * get information from the node + */ + estate = node->ps.state; + outerPlan = outerPlanState(node); + + /* + * Get next tuple from subplan, if any; but if we are evaluating + * an EvalPlanQual substitution, first finish that. + */ +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); + + if (TupIsNull(slot)) + return NULL; + + /* + * Attempt to lock the source tuple(s). + */ + epq_pushed = false; + foreach(lc, node->lr_rowMarks) + { + ExecRowMark *erm = (ExecRowMark *) lfirst(lc); + Datum datum; + bool isNull; + HeapTupleData tuple; + Buffer buffer; + ItemPointerData update_ctid; + TransactionId update_xmax; + LockTupleMode lockmode; + HTSU_Result test; + HeapTuple copyTuple; + + /* if child rel, must check whether it produced this row */ + if (erm->rti != erm->prti) + { + Oid tableoid; + + datum = ExecGetJunkAttribute(slot, + erm->toidAttNo, + &isNull); + /* shouldn't ever get a null result... */ + if (isNull) + elog(ERROR, "tableoid is NULL"); + tableoid = DatumGetObjectId(datum); + + if (tableoid != RelationGetRelid(erm->relation)) + { + /* this child is inactive right now */ + ItemPointerSetInvalid(&(erm->curCtid)); + continue; + } + } + + /* fetch the tuple's ctid */ + datum = ExecGetJunkAttribute(slot, + erm->ctidAttNo, + &isNull); + /* shouldn't ever get a null result... */ + if (isNull) + elog(ERROR, "ctid is NULL"); + tuple.t_self = *((ItemPointer) DatumGetPointer(datum)); + + /* okay, try to lock the tuple */ + if (erm->forUpdate) + lockmode = LockTupleExclusive; + else + lockmode = LockTupleShared; + + test = heap_lock_tuple(erm->relation, &tuple, &buffer, + &update_ctid, &update_xmax, + estate->es_output_cid, + lockmode, erm->noWait); + ReleaseBuffer(buffer); + switch (test) + { + case HeapTupleSelfUpdated: + /* treat it as deleted; do not process */ + if (epq_pushed) + EvalPlanQualPop(estate, outerPlan); + goto lnext; + + case HeapTupleMayBeUpdated: + /* got the lock successfully */ + break; + + case HeapTupleUpdated: + if (IsXactIsoLevelSerializable) + ereport(ERROR, + (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE), + errmsg("could not serialize access due to concurrent update"))); + if (ItemPointerEquals(&update_ctid, + &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, + &update_ctid, update_xmax); + + if (copyTuple == NULL) + { + /* Tuple was deleted, so don't return it */ + if (epq_pushed) + EvalPlanQualPop(estate, outerPlan); + goto lnext; + } + + /* + * Need to run a recheck subquery. + * Find or create a PQ stack entry. + */ + if (!epq_pushed) + { + EvalPlanQualPush(estate, erm->rti, outerPlan); + epq_pushed = true; + } + + /* Store target tuple for relation's scan node */ + EvalPlanQualSetTuple(estate, erm->rti, copyTuple); + + /* Continue loop until we have all target tuples */ + break; + + default: + elog(ERROR, "unrecognized heap_lock_tuple status: %u", + test); + } + + /* Remember locked tuple's TID for WHERE CURRENT OF */ + erm->curCtid = tuple.t_self; + } + + /* If we need to do EvalPlanQual testing, loop back to do that */ + if (epq_pushed) + { + node->lr_useEvalPlan = true; + goto lnext; + } + + /* Got all locks, so return the current tuple */ + return slot; +} + +/* ---------------------------------------------------------------- + * ExecInitLockRows + * + * This initializes the LockRows node state structures and + * the node's subplan. + * ---------------------------------------------------------------- + */ +LockRowsState * +ExecInitLockRows(LockRows *node, EState *estate, int eflags) +{ + LockRowsState *lrstate; + Plan *outerPlan; + JunkFilter *j; + ListCell *lc; + + /* check for unsupported flags */ + Assert(!(eflags & EXEC_FLAG_MARK)); + + /* + * create state structure + */ + lrstate = makeNode(LockRowsState); + lrstate->ps.plan = (Plan *) node; + lrstate->ps.state = estate; + lrstate->lr_useEvalPlan = false; + + /* + * Miscellaneous initialization + * + * LockRows nodes never call ExecQual or ExecProject. + */ + + /* + * Tuple table initialization (XXX not actually used...) + */ + ExecInitResultTupleSlot(estate, &lrstate->ps); + + /* + * then initialize outer plan + */ + outerPlan = outerPlan(node); + outerPlanState(lrstate) = ExecInitNode(outerPlan, estate, eflags); + + /* + * LockRows nodes do no projections, so initialize projection info for this + * node appropriately + */ + ExecAssignResultTypeFromTL(&lrstate->ps); + 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); + ExecRowMark *erm = NULL; + char resname[32]; + ListCell *lce; + + /* ignore "parent" rowmarks; they are irrelevant at runtime */ + if (rc->isParent) + continue; + + foreach(lce, estate->es_rowMarks) + { + erm = (ExecRowMark *) lfirst(lce); + if (erm->rti == rc->rti && + erm->prti == rc->prti && + erm->rowmarkId == rc->rowmarkId) + 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); + } + + return lrstate; +} + +/* ---------------------------------------------------------------- + * ExecEndLockRows + * + * This shuts down the subplan and frees resources allocated + * to this node. + * ---------------------------------------------------------------- + */ +void +ExecEndLockRows(LockRowsState *node) +{ + ExecEndNode(outerPlanState(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. + */ + if (((PlanState *) node)->lefttree->chgParam == NULL) + ExecReScan(((PlanState *) node)->lefttree, exprCtxt); +} |