1 /*-------------------------------------------------------------------------
4 * Routines to handle FOR UPDATE/FOR SHARE row locking
6 * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
11 * src/backend/executor/nodeLockRows.c
13 *-------------------------------------------------------------------------
17 * ExecLockRows - fetch locked rows
18 * ExecInitLockRows - initialize node and subnodes..
19 * ExecEndLockRows - shutdown node and subnodes
24 #include "access/xact.h"
25 #include "executor/executor.h"
26 #include "executor/nodeLockRows.h"
27 #include "storage/bufmgr.h"
28 #include "utils/tqual.h"
31 /* ----------------------------------------------------------------
33 * ----------------------------------------------------------------
35 TupleTableSlot * /* return: a tuple or NULL */
36 ExecLockRows(LockRowsState *node)
45 * get information from the node
47 estate = node->ps.state;
48 outerPlan = outerPlanState(node);
51 * Get next tuple from subplan, if any.
54 slot = ExecProcNode(outerPlan);
60 * Attempt to lock the source tuple(s). (Note we only have locking
61 * rowmarks in lr_arowMarks.)
64 foreach(lc, node->lr_arowMarks)
66 ExecAuxRowMark *aerm = (ExecAuxRowMark *) lfirst(lc);
67 ExecRowMark *erm = aerm->rowmark;
72 ItemPointerData update_ctid;
73 TransactionId update_xmax;
74 LockTupleMode lockmode;
78 /* clear any leftover test tuple for this rel */
79 if (node->lr_epqstate.estate != NULL)
80 EvalPlanQualSetTuple(&node->lr_epqstate, erm->rti, NULL);
82 /* if child rel, must check whether it produced this row */
83 if (erm->rti != erm->prti)
87 datum = ExecGetJunkAttribute(slot,
90 /* shouldn't ever get a null result... */
92 elog(ERROR, "tableoid is NULL");
93 tableoid = DatumGetObjectId(datum);
95 if (tableoid != RelationGetRelid(erm->relation))
97 /* this child is inactive right now */
98 ItemPointerSetInvalid(&(erm->curCtid));
103 /* fetch the tuple's ctid */
104 datum = ExecGetJunkAttribute(slot,
107 /* shouldn't ever get a null result... */
109 elog(ERROR, "ctid is NULL");
110 tuple.t_self = *((ItemPointer) DatumGetPointer(datum));
112 /* okay, try to lock the tuple */
113 if (erm->markType == ROW_MARK_EXCLUSIVE)
114 lockmode = LockTupleExclusive;
116 lockmode = LockTupleShared;
118 test = heap_lock_tuple(erm->relation, &tuple, &buffer,
119 &update_ctid, &update_xmax,
120 estate->es_output_cid,
121 lockmode, erm->noWait);
122 ReleaseBuffer(buffer);
125 case HeapTupleSelfUpdated:
126 /* treat it as deleted; do not process */
129 case HeapTupleMayBeUpdated:
130 /* got the lock successfully */
133 case HeapTupleUpdated:
134 if (IsolationUsesXactSnapshot())
136 (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
137 errmsg("could not serialize access due to concurrent update")));
138 if (ItemPointerEquals(&update_ctid,
141 /* Tuple was deleted, so don't return it */
145 /* updated, so fetch and lock the updated version */
146 copyTuple = EvalPlanQualFetch(estate, erm->relation, lockmode,
147 &update_ctid, update_xmax);
149 if (copyTuple == NULL)
151 /* Tuple was deleted, so don't return it */
154 /* remember the actually locked tuple's TID */
155 tuple.t_self = copyTuple->t_self;
158 * Need to run a recheck subquery. Initialize EPQ state if we
159 * didn't do so already.
163 EvalPlanQualBegin(&node->lr_epqstate, estate);
167 /* Store target tuple for relation's scan node */
168 EvalPlanQualSetTuple(&node->lr_epqstate, erm->rti, copyTuple);
170 /* Continue loop until we have all target tuples */
174 elog(ERROR, "unrecognized heap_lock_tuple status: %u",
178 /* Remember locked tuple's TID for WHERE CURRENT OF */
179 erm->curCtid = tuple.t_self;
183 * If we need to do EvalPlanQual testing, do so.
188 * First, fetch a copy of any rows that were successfully locked
189 * without any update having occurred. (We do this in a separate pass
190 * so as to avoid overhead in the common case where there are no
191 * concurrent updates.)
193 foreach(lc, node->lr_arowMarks)
195 ExecAuxRowMark *aerm = (ExecAuxRowMark *) lfirst(lc);
196 ExecRowMark *erm = aerm->rowmark;
200 /* ignore non-active child tables */
201 if (!ItemPointerIsValid(&(erm->curCtid)))
203 Assert(erm->rti != erm->prti); /* check it's child table */
207 if (EvalPlanQualGetTuple(&node->lr_epqstate, erm->rti) != NULL)
208 continue; /* it was updated and fetched above */
210 /* okay, fetch the tuple */
211 tuple.t_self = erm->curCtid;
212 if (!heap_fetch(erm->relation, SnapshotAny, &tuple, &buffer,
214 elog(ERROR, "failed to fetch tuple for EvalPlanQual recheck");
216 /* successful, copy and store tuple */
217 EvalPlanQualSetTuple(&node->lr_epqstate, erm->rti,
218 heap_copytuple(&tuple));
219 ReleaseBuffer(buffer);
223 * Now fetch any non-locked source rows --- the EPQ logic knows how to
226 EvalPlanQualSetSlot(&node->lr_epqstate, slot);
227 EvalPlanQualFetchRowMarks(&node->lr_epqstate);
230 * And finally we can re-evaluate the tuple.
232 slot = EvalPlanQualNext(&node->lr_epqstate);
235 /* Updated tuple fails qual, so ignore it and go on */
240 /* Got all locks, so return the current tuple */
244 /* ----------------------------------------------------------------
247 * This initializes the LockRows node state structures and
248 * the node's subplan.
249 * ----------------------------------------------------------------
252 ExecInitLockRows(LockRows *node, EState *estate, int eflags)
254 LockRowsState *lrstate;
255 Plan *outerPlan = outerPlan(node);
259 /* check for unsupported flags */
260 Assert(!(eflags & EXEC_FLAG_MARK));
263 * create state structure
265 lrstate = makeNode(LockRowsState);
266 lrstate->ps.plan = (Plan *) node;
267 lrstate->ps.state = estate;
270 * Miscellaneous initialization
272 * LockRows nodes never call ExecQual or ExecProject.
276 * Tuple table initialization (XXX not actually used...)
278 ExecInitResultTupleSlot(estate, &lrstate->ps);
281 * then initialize outer plan
283 outerPlanState(lrstate) = ExecInitNode(outerPlan, estate, eflags);
286 * LockRows nodes do no projections, so initialize projection info for
287 * this node appropriately
289 ExecAssignResultTypeFromTL(&lrstate->ps);
290 lrstate->ps.ps_ProjInfo = NULL;
293 * Locate the ExecRowMark(s) that this node is responsible for, and
294 * construct ExecAuxRowMarks for them. (InitPlan should already have
295 * built the global list of ExecRowMarks.)
297 lrstate->lr_arowMarks = NIL;
299 foreach(lc, node->rowMarks)
301 PlanRowMark *rc = (PlanRowMark *) lfirst(lc);
303 ExecAuxRowMark *aerm;
305 Assert(IsA(rc, PlanRowMark));
307 /* ignore "parent" rowmarks; they are irrelevant at runtime */
311 /* find ExecRowMark and build ExecAuxRowMark */
312 erm = ExecFindRowMark(estate, rc->rti);
313 aerm = ExecBuildAuxRowMark(erm, outerPlan->targetlist);
316 * Only locking rowmarks go into our own list. Non-locking marks are
317 * passed off to the EvalPlanQual machinery. This is because we don't
318 * want to bother fetching non-locked rows unless we actually have to
321 if (RowMarkRequiresRowShareLock(erm->markType))
322 lrstate->lr_arowMarks = lappend(lrstate->lr_arowMarks, aerm);
324 epq_arowmarks = lappend(epq_arowmarks, aerm);
327 /* Now we have the info needed to set up EPQ state */
328 EvalPlanQualInit(&lrstate->lr_epqstate, estate,
329 outerPlan, epq_arowmarks, node->epqParam);
334 /* ----------------------------------------------------------------
337 * This shuts down the subplan and frees resources allocated
339 * ----------------------------------------------------------------
342 ExecEndLockRows(LockRowsState *node)
344 EvalPlanQualEnd(&node->lr_epqstate);
345 ExecEndNode(outerPlanState(node));
350 ExecReScanLockRows(LockRowsState *node)
353 * if chgParam of subnode is not null then plan will be re-scanned by
354 * first ExecProcNode.
356 if (node->ps.lefttree->chgParam == NULL)
357 ExecReScan(node->ps.lefttree);