1/*-------------------------------------------------------------------------
2 *
3 * nodeLockRows.c
4 * Routines to handle FOR UPDATE/FOR SHARE row locking
5 *
6 * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
8 *
9 *
10 * IDENTIFICATION
11 * src/backend/executor/nodeLockRows.c
12 *
13 *-------------------------------------------------------------------------
14 */
15/*
16 * INTERFACE ROUTINES
17 * ExecLockRows - fetch locked rows
18 * ExecInitLockRows - initialize node and subnodes..
19 * ExecEndLockRows - shutdown node and subnodes
20 */
21
22#include "postgres.h"
23
24#include "access/tableam.h"
25#include "access/xact.h"
26#include "executor/executor.h"
27#include "executor/nodeLockRows.h"
28#include "foreign/fdwapi.h"
29#include "miscadmin.h"
30#include "utils/rel.h"
31
32
33/* ----------------------------------------------------------------
34 * ExecLockRows
35 * ----------------------------------------------------------------
36 */
37static TupleTableSlot * /* return: a tuple or NULL */
38ExecLockRows(PlanState *pstate)
39{
40 LockRowsState *node = castNode(LockRowsState, pstate);
41 TupleTableSlot *slot;
42 EState *estate;
43 PlanState *outerPlan;
44 bool epq_needed;
45 ListCell *lc;
46
47 CHECK_FOR_INTERRUPTS();
48
49 /*
50 * get information from the node
51 */
52 estate = node->ps.state;
53 outerPlan = outerPlanState(node);
54
55 /*
56 * Get next tuple from subplan, if any.
57 */
58lnext:
59 slot = ExecProcNode(outerPlan);
60
61 if (TupIsNull(slot))
62 return NULL;
63
64 /* We don't need EvalPlanQual unless we get updated tuple version(s) */
65 epq_needed = false;
66
67 /*
68 * Attempt to lock the source tuple(s). (Note we only have locking
69 * rowmarks in lr_arowMarks.)
70 */
71 foreach(lc, node->lr_arowMarks)
72 {
73 ExecAuxRowMark *aerm = (ExecAuxRowMark *) lfirst(lc);
74 ExecRowMark *erm = aerm->rowmark;
75 Datum datum;
76 bool isNull;
77 ItemPointerData tid;
78 TM_FailureData tmfd;
79 LockTupleMode lockmode;
80 int lockflags = 0;
81 TM_Result test;
82 TupleTableSlot *markSlot;
83
84 /* clear any leftover test tuple for this rel */
85 markSlot = EvalPlanQualSlot(&node->lr_epqstate, erm->relation, erm->rti);
86 ExecClearTuple(markSlot);
87
88 /* if child rel, must check whether it produced this row */
89 if (erm->rti != erm->prti)
90 {
91 Oid tableoid;
92
93 datum = ExecGetJunkAttribute(slot,
94 aerm->toidAttNo,
95 &isNull);
96 /* shouldn't ever get a null result... */
97 if (isNull)
98 elog(ERROR, "tableoid is NULL");
99 tableoid = DatumGetObjectId(datum);
100
101 Assert(OidIsValid(erm->relid));
102 if (tableoid != erm->relid)
103 {
104 /* this child is inactive right now */
105 erm->ermActive = false;
106 ItemPointerSetInvalid(&(erm->curCtid));
107 ExecClearTuple(markSlot);
108 continue;
109 }
110 }
111 erm->ermActive = true;
112
113 /* fetch the tuple's ctid */
114 datum = ExecGetJunkAttribute(slot,
115 aerm->ctidAttNo,
116 &isNull);
117 /* shouldn't ever get a null result... */
118 if (isNull)
119 elog(ERROR, "ctid is NULL");
120
121 /* requests for foreign tables must be passed to their FDW */
122 if (erm->relation->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
123 {
124 FdwRoutine *fdwroutine;
125 bool updated = false;
126
127 fdwroutine = GetFdwRoutineForRelation(erm->relation, false);
128 /* this should have been checked already, but let's be safe */
129 if (fdwroutine->RefetchForeignRow == NULL)
130 ereport(ERROR,
131 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
132 errmsg("cannot lock rows in foreign table \"%s\"",
133 RelationGetRelationName(erm->relation))));
134
135 fdwroutine->RefetchForeignRow(estate,
136 erm,
137 datum,
138 markSlot,
139 &updated);
140 if (TupIsNull(markSlot))
141 {
142 /* couldn't get the lock, so skip this row */
143 goto lnext;
144 }
145
146 /*
147 * if FDW says tuple was updated before getting locked, we need to
148 * perform EPQ testing to see if quals are still satisfied
149 */
150 if (updated)
151 epq_needed = true;
152
153 continue;
154 }
155
156 /* okay, try to lock (and fetch) the tuple */
157 tid = *((ItemPointer) DatumGetPointer(datum));
158 switch (erm->markType)
159 {
160 case ROW_MARK_EXCLUSIVE:
161 lockmode = LockTupleExclusive;
162 break;
163 case ROW_MARK_NOKEYEXCLUSIVE:
164 lockmode = LockTupleNoKeyExclusive;
165 break;
166 case ROW_MARK_SHARE:
167 lockmode = LockTupleShare;
168 break;
169 case ROW_MARK_KEYSHARE:
170 lockmode = LockTupleKeyShare;
171 break;
172 default:
173 elog(ERROR, "unsupported rowmark type");
174 lockmode = LockTupleNoKeyExclusive; /* keep compiler quiet */
175 break;
176 }
177
178 lockflags = TUPLE_LOCK_FLAG_LOCK_UPDATE_IN_PROGRESS;
179 if (!IsolationUsesXactSnapshot())
180 lockflags |= TUPLE_LOCK_FLAG_FIND_LAST_VERSION;
181
182 test = table_tuple_lock(erm->relation, &tid, estate->es_snapshot,
183 markSlot, estate->es_output_cid,
184 lockmode, erm->waitPolicy,
185 lockflags,
186 &tmfd);
187
188 switch (test)
189 {
190 case TM_WouldBlock:
191 /* couldn't lock tuple in SKIP LOCKED mode */
192 goto lnext;
193
194 case TM_SelfModified:
195
196 /*
197 * The target tuple was already updated or deleted by the
198 * current command, or by a later command in the current
199 * transaction. We *must* ignore the tuple in the former
200 * case, so as to avoid the "Halloween problem" of repeated
201 * update attempts. In the latter case it might be sensible
202 * to fetch the updated tuple instead, but doing so would
203 * require changing heap_update and heap_delete to not
204 * complain about updating "invisible" tuples, which seems
205 * pretty scary (table_tuple_lock will not complain, but few
206 * callers expect TM_Invisible, and we're not one of them). So
207 * for now, treat the tuple as deleted and do not process.
208 */
209 goto lnext;
210
211 case TM_Ok:
212
213 /*
214 * Got the lock successfully, the locked tuple saved in
215 * markSlot for, if needed, EvalPlanQual testing below.
216 */
217 if (tmfd.traversed)
218 epq_needed = true;
219 break;
220
221 case TM_Updated:
222 if (IsolationUsesXactSnapshot())
223 ereport(ERROR,
224 (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
225 errmsg("could not serialize access due to concurrent update")));
226 elog(ERROR, "unexpected table_tuple_lock status: %u",
227 test);
228 break;
229
230 case TM_Deleted:
231 if (IsolationUsesXactSnapshot())
232 ereport(ERROR,
233 (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
234 errmsg("could not serialize access due to concurrent update")));
235 /* tuple was deleted so don't return it */
236 goto lnext;
237
238 case TM_Invisible:
239 elog(ERROR, "attempted to lock invisible tuple");
240 break;
241
242 default:
243 elog(ERROR, "unrecognized table_tuple_lock status: %u",
244 test);
245 }
246
247 /* Remember locked tuple's TID for EPQ testing and WHERE CURRENT OF */
248 erm->curCtid = tid;
249 }
250
251 /*
252 * If we need to do EvalPlanQual testing, do so.
253 */
254 if (epq_needed)
255 {
256 /* Initialize EPQ machinery */
257 EvalPlanQualBegin(&node->lr_epqstate);
258
259 /*
260 * To fetch non-locked source rows the EPQ logic needs to access junk
261 * columns from the tuple being tested.
262 */
263 EvalPlanQualSetSlot(&node->lr_epqstate, slot);
264
265 /*
266 * And finally we can re-evaluate the tuple.
267 */
268 slot = EvalPlanQualNext(&node->lr_epqstate);
269 if (TupIsNull(slot))
270 {
271 /* Updated tuple fails qual, so ignore it and go on */
272 goto lnext;
273 }
274 }
275
276 /* Got all locks, so return the current tuple */
277 return slot;
278}
279
280/* ----------------------------------------------------------------
281 * ExecInitLockRows
282 *
283 * This initializes the LockRows node state structures and
284 * the node's subplan.
285 * ----------------------------------------------------------------
286 */
287LockRowsState *
288ExecInitLockRows(LockRows *node, EState *estate, int eflags)
289{
290 LockRowsState *lrstate;
291 Plan *outerPlan = outerPlan(node);
292 List *epq_arowmarks;
293 ListCell *lc;
294
295 /* check for unsupported flags */
296 Assert(!(eflags & EXEC_FLAG_MARK));
297
298 /*
299 * create state structure
300 */
301 lrstate = makeNode(LockRowsState);
302 lrstate->ps.plan = (Plan *) node;
303 lrstate->ps.state = estate;
304 lrstate->ps.ExecProcNode = ExecLockRows;
305
306 /*
307 * Miscellaneous initialization
308 *
309 * LockRows nodes never call ExecQual or ExecProject, therefore no
310 * ExprContext is needed.
311 */
312
313 /*
314 * Initialize result type.
315 */
316 ExecInitResultTypeTL(&lrstate->ps);
317
318 /*
319 * then initialize outer plan
320 */
321 outerPlanState(lrstate) = ExecInitNode(outerPlan, estate, eflags);
322
323 /* node returns unmodified slots from the outer plan */
324 lrstate->ps.resultopsset = true;
325 lrstate->ps.resultops = ExecGetResultSlotOps(outerPlanState(lrstate),
326 &lrstate->ps.resultopsfixed);
327
328 /*
329 * LockRows nodes do no projections, so initialize projection info for
330 * this node appropriately
331 */
332 lrstate->ps.ps_ProjInfo = NULL;
333
334 /*
335 * Locate the ExecRowMark(s) that this node is responsible for, and
336 * construct ExecAuxRowMarks for them. (InitPlan should already have
337 * built the global list of ExecRowMarks.)
338 */
339 lrstate->lr_arowMarks = NIL;
340 epq_arowmarks = NIL;
341 foreach(lc, node->rowMarks)
342 {
343 PlanRowMark *rc = lfirst_node(PlanRowMark, lc);
344 ExecRowMark *erm;
345 ExecAuxRowMark *aerm;
346
347 /* ignore "parent" rowmarks; they are irrelevant at runtime */
348 if (rc->isParent)
349 continue;
350
351 /* find ExecRowMark and build ExecAuxRowMark */
352 erm = ExecFindRowMark(estate, rc->rti, false);
353 aerm = ExecBuildAuxRowMark(erm, outerPlan->targetlist);
354
355 /*
356 * Only locking rowmarks go into our own list. Non-locking marks are
357 * passed off to the EvalPlanQual machinery. This is because we don't
358 * want to bother fetching non-locked rows unless we actually have to
359 * do an EPQ recheck.
360 */
361 if (RowMarkRequiresRowShareLock(erm->markType))
362 lrstate->lr_arowMarks = lappend(lrstate->lr_arowMarks, aerm);
363 else
364 epq_arowmarks = lappend(epq_arowmarks, aerm);
365 }
366
367 /* Now we have the info needed to set up EPQ state */
368 EvalPlanQualInit(&lrstate->lr_epqstate, estate,
369 outerPlan, epq_arowmarks, node->epqParam);
370
371 return lrstate;
372}
373
374/* ----------------------------------------------------------------
375 * ExecEndLockRows
376 *
377 * This shuts down the subplan and frees resources allocated
378 * to this node.
379 * ----------------------------------------------------------------
380 */
381void
382ExecEndLockRows(LockRowsState *node)
383{
384 EvalPlanQualEnd(&node->lr_epqstate);
385 ExecEndNode(outerPlanState(node));
386}
387
388
389void
390ExecReScanLockRows(LockRowsState *node)
391{
392 /*
393 * if chgParam of subnode is not null then plan will be re-scanned by
394 * first ExecProcNode.
395 */
396 if (node->ps.lefttree->chgParam == NULL)
397 ExecReScan(node->ps.lefttree);
398}
399