PostgreSQL Source Code  git master
nodeLockRows.c
Go to the documentation of this file.
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  */
37 static TupleTableSlot * /* return: a tuple or NULL */
39 {
40  LockRowsState *node = castNode(LockRowsState, pstate);
41  TupleTableSlot *slot;
42  EState *estate;
44  bool epq_needed;
45  ListCell *lc;
46 
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  */
58 lnext:
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  * Initialize EPQ machinery. Need to do that early because source tuples
69  * are stored in slots initialized therein.
70  */
71  EvalPlanQualBegin(&node->lr_epqstate, estate);
72 
73  /*
74  * Attempt to lock the source tuple(s). (Note we only have locking
75  * rowmarks in lr_arowMarks.)
76  */
77  foreach(lc, node->lr_arowMarks)
78  {
79  ExecAuxRowMark *aerm = (ExecAuxRowMark *) lfirst(lc);
80  ExecRowMark *erm = aerm->rowmark;
81  Datum datum;
82  bool isNull;
83  ItemPointerData tid;
84  TM_FailureData tmfd;
85  LockTupleMode lockmode;
86  int lockflags = 0;
88  TupleTableSlot *markSlot;
89 
90  /* clear any leftover test tuple for this rel */
91  markSlot = EvalPlanQualSlot(&node->lr_epqstate, erm->relation, erm->rti);
92  ExecClearTuple(markSlot);
93 
94  /* if child rel, must check whether it produced this row */
95  if (erm->rti != erm->prti)
96  {
97  Oid tableoid;
98 
99  datum = ExecGetJunkAttribute(slot,
100  aerm->toidAttNo,
101  &isNull);
102  /* shouldn't ever get a null result... */
103  if (isNull)
104  elog(ERROR, "tableoid is NULL");
105  tableoid = DatumGetObjectId(datum);
106 
107  Assert(OidIsValid(erm->relid));
108  if (tableoid != erm->relid)
109  {
110  /* this child is inactive right now */
111  erm->ermActive = false;
113  ExecClearTuple(markSlot);
114  continue;
115  }
116  }
117  erm->ermActive = true;
118 
119  /* fetch the tuple's ctid */
120  datum = ExecGetJunkAttribute(slot,
121  aerm->ctidAttNo,
122  &isNull);
123  /* shouldn't ever get a null result... */
124  if (isNull)
125  elog(ERROR, "ctid is NULL");
126 
127  /* requests for foreign tables must be passed to their FDW */
128  if (erm->relation->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
129  {
130  FdwRoutine *fdwroutine;
131  bool updated = false;
132 
133  fdwroutine = GetFdwRoutineForRelation(erm->relation, false);
134  /* this should have been checked already, but let's be safe */
135  if (fdwroutine->RefetchForeignRow == NULL)
136  ereport(ERROR,
137  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
138  errmsg("cannot lock rows in foreign table \"%s\"",
140 
141  fdwroutine->RefetchForeignRow(estate,
142  erm,
143  datum,
144  markSlot,
145  &updated);
146  if (TupIsNull(markSlot))
147  {
148  /* couldn't get the lock, so skip this row */
149  goto lnext;
150  }
151 
152  /*
153  * if FDW says tuple was updated before getting locked, we need to
154  * perform EPQ testing to see if quals are still satisfied
155  */
156  if (updated)
157  epq_needed = true;
158 
159  continue;
160  }
161 
162  /* okay, try to lock (and fetch) the tuple */
163  tid = *((ItemPointer) DatumGetPointer(datum));
164  switch (erm->markType)
165  {
166  case ROW_MARK_EXCLUSIVE:
167  lockmode = LockTupleExclusive;
168  break;
170  lockmode = LockTupleNoKeyExclusive;
171  break;
172  case ROW_MARK_SHARE:
173  lockmode = LockTupleShare;
174  break;
175  case ROW_MARK_KEYSHARE:
176  lockmode = LockTupleKeyShare;
177  break;
178  default:
179  elog(ERROR, "unsupported rowmark type");
180  lockmode = LockTupleNoKeyExclusive; /* keep compiler quiet */
181  break;
182  }
183 
187 
188  test = table_tuple_lock(erm->relation, &tid, estate->es_snapshot,
189  markSlot, estate->es_output_cid,
190  lockmode, erm->waitPolicy,
191  lockflags,
192  &tmfd);
193 
194  switch (test)
195  {
196  case TM_WouldBlock:
197  /* couldn't lock tuple in SKIP LOCKED mode */
198  goto lnext;
199 
200  case TM_SelfModified:
201 
202  /*
203  * The target tuple was already updated or deleted by the
204  * current command, or by a later command in the current
205  * transaction. We *must* ignore the tuple in the former
206  * case, so as to avoid the "Halloween problem" of repeated
207  * update attempts. In the latter case it might be sensible
208  * to fetch the updated tuple instead, but doing so would
209  * require changing heap_update and heap_delete to not
210  * complain about updating "invisible" tuples, which seems
211  * pretty scary (table_tuple_lock will not complain, but few
212  * callers expect TM_Invisible, and we're not one of them). So
213  * for now, treat the tuple as deleted and do not process.
214  */
215  goto lnext;
216 
217  case TM_Ok:
218 
219  /*
220  * Got the lock successfully, the locked tuple saved in
221  * markSlot for, if needed, EvalPlanQual testing below.
222  */
223  if (tmfd.traversed)
224  epq_needed = true;
225  break;
226 
227  case TM_Updated:
229  ereport(ERROR,
230  (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
231  errmsg("could not serialize access due to concurrent update")));
232  elog(ERROR, "unexpected table_tuple_lock status: %u",
233  test);
234  break;
235 
236  case TM_Deleted:
238  ereport(ERROR,
239  (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
240  errmsg("could not serialize access due to concurrent update")));
241  /* tuple was deleted so don't return it */
242  goto lnext;
243 
244  case TM_Invisible:
245  elog(ERROR, "attempted to lock invisible tuple");
246  break;
247 
248  default:
249  elog(ERROR, "unrecognized table_tuple_lock status: %u",
250  test);
251  }
252 
253  /* Remember locked tuple's TID for EPQ testing and WHERE CURRENT OF */
254  erm->curCtid = tid;
255  }
256 
257  /*
258  * If we need to do EvalPlanQual testing, do so.
259  */
260  if (epq_needed)
261  {
262  /*
263  * Now fetch any non-locked source rows --- the EPQ logic knows how to
264  * do that.
265  */
266  EvalPlanQualSetSlot(&node->lr_epqstate, slot);
268 
269  /*
270  * And finally we can re-evaluate the tuple.
271  */
272  slot = EvalPlanQualNext(&node->lr_epqstate);
273  if (TupIsNull(slot))
274  {
275  /* Updated tuple fails qual, so ignore it and go on */
276  goto lnext;
277  }
278  }
279 
280  /* Got all locks, so return the current tuple */
281  return slot;
282 }
283 
284 /* ----------------------------------------------------------------
285  * ExecInitLockRows
286  *
287  * This initializes the LockRows node state structures and
288  * the node's subplan.
289  * ----------------------------------------------------------------
290  */
292 ExecInitLockRows(LockRows *node, EState *estate, int eflags)
293 {
294  LockRowsState *lrstate;
295  Plan *outerPlan = outerPlan(node);
296  List *epq_arowmarks;
297  ListCell *lc;
298 
299  /* check for unsupported flags */
300  Assert(!(eflags & EXEC_FLAG_MARK));
301 
302  /*
303  * create state structure
304  */
305  lrstate = makeNode(LockRowsState);
306  lrstate->ps.plan = (Plan *) node;
307  lrstate->ps.state = estate;
308  lrstate->ps.ExecProcNode = ExecLockRows;
309 
310  /*
311  * Miscellaneous initialization
312  *
313  * LockRows nodes never call ExecQual or ExecProject, therefore no
314  * ExprContext is needed.
315  */
316 
317  /*
318  * Initialize result type.
319  */
320  ExecInitResultTypeTL(&lrstate->ps);
321 
322  /*
323  * then initialize outer plan
324  */
325  outerPlanState(lrstate) = ExecInitNode(outerPlan, estate, eflags);
326 
327  /* node returns unmodified slots from the outer plan */
328  lrstate->ps.resultopsset = true;
329  lrstate->ps.resultops = ExecGetResultSlotOps(outerPlanState(lrstate),
330  &lrstate->ps.resultopsfixed);
331 
332  /*
333  * LockRows nodes do no projections, so initialize projection info for
334  * this node appropriately
335  */
336  lrstate->ps.ps_ProjInfo = NULL;
337 
338  /*
339  * Locate the ExecRowMark(s) that this node is responsible for, and
340  * construct ExecAuxRowMarks for them. (InitPlan should already have
341  * built the global list of ExecRowMarks.)
342  */
343  lrstate->lr_arowMarks = NIL;
344  epq_arowmarks = NIL;
345  foreach(lc, node->rowMarks)
346  {
348  ExecRowMark *erm;
349  ExecAuxRowMark *aerm;
350 
351  /* ignore "parent" rowmarks; they are irrelevant at runtime */
352  if (rc->isParent)
353  continue;
354 
355  /* find ExecRowMark and build ExecAuxRowMark */
356  erm = ExecFindRowMark(estate, rc->rti, false);
357  aerm = ExecBuildAuxRowMark(erm, outerPlan->targetlist);
358 
359  /*
360  * Only locking rowmarks go into our own list. Non-locking marks are
361  * passed off to the EvalPlanQual machinery. This is because we don't
362  * want to bother fetching non-locked rows unless we actually have to
363  * do an EPQ recheck.
364  */
366  lrstate->lr_arowMarks = lappend(lrstate->lr_arowMarks, aerm);
367  else
368  epq_arowmarks = lappend(epq_arowmarks, aerm);
369  }
370 
371  /* Now we have the info needed to set up EPQ state */
372  EvalPlanQualInit(&lrstate->lr_epqstate, estate,
373  outerPlan, epq_arowmarks, node->epqParam);
374 
375  return lrstate;
376 }
377 
378 /* ----------------------------------------------------------------
379  * ExecEndLockRows
380  *
381  * This shuts down the subplan and frees resources allocated
382  * to this node.
383  * ----------------------------------------------------------------
384  */
385 void
387 {
390 }
391 
392 
393 void
395 {
396  /*
397  * if chgParam of subnode is not null then plan will be re-scanned by
398  * first ExecProcNode.
399  */
400  if (node->ps.lefttree->chgParam == NULL)
401  ExecReScan(node->ps.lefttree);
402 }
#define NIL
Definition: pg_list.h:65
LockTupleMode
Definition: lockoptions.h:49
CommandId es_output_cid
Definition: execnodes.h:517
static void test(void)
ProjectionInfo * ps_ProjInfo
Definition: execnodes.h:985
const TupleTableSlotOps * ExecGetResultSlotOps(PlanState *planstate, bool *isfixed)
Definition: execUtils.c:465
static TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition: tuptable.h:426
EPQState lr_epqstate
Definition: execnodes.h:2271
static ListCell * lnext(const List *l, const ListCell *c)
Definition: pg_list.h:321
#define castNode(_type_, nodeptr)
Definition: nodes.h:593
void ExecEndNode(PlanState *node)
Definition: execProcnode.c:538
Relation relation
Definition: execnodes.h:627
LockRowsState * ExecInitLockRows(LockRows *node, EState *estate, int eflags)
Definition: nodeLockRows.c:292
#define DatumGetObjectId(X)
Definition: postgres.h:500
void ExecReScanLockRows(LockRowsState *node)
Definition: nodeLockRows.c:394
void EvalPlanQualFetchRowMarks(EPQState *epqstate)
Definition: execMain.c:2572
void ExecReScan(PlanState *node)
Definition: execAmi.c:77
void ExecEndLockRows(LockRowsState *node)
Definition: nodeLockRows.c:386
#define IsolationUsesXactSnapshot()
Definition: xact.h:51
static TupleTableSlot * ExecLockRows(PlanState *pstate)
Definition: nodeLockRows.c:38
int errcode(int sqlerrcode)
Definition: elog.c:570
bool ermActive
Definition: execnodes.h:635
int epqParam
Definition: plannodes.h:955
LockWaitPolicy waitPolicy
Definition: execnodes.h:634
Snapshot es_snapshot
Definition: execnodes.h:503
#define TUPLE_LOCK_FLAG_LOCK_UPDATE_IN_PROGRESS
Definition: tableam.h:137
EState * state
Definition: execnodes.h:947
Form_pg_class rd_rel
Definition: rel.h:83
unsigned int Oid
Definition: postgres_ext.h:31
#define OidIsValid(objectId)
Definition: c.h:638
#define RowMarkRequiresRowShareLock(marktype)
Definition: plannodes.h:1013
void EvalPlanQualBegin(EPQState *epqstate, EState *parentestate)
Definition: execMain.c:2702
const TupleTableSlotOps * resultops
Definition: execnodes.h:1020
TupleTableSlot * EvalPlanQualNext(EPQState *epqstate)
Definition: execMain.c:2686
struct PlanState * lefttree
Definition: execnodes.h:967
void EvalPlanQualEnd(EPQState *epqstate)
Definition: execMain.c:2939
ItemPointerData * ItemPointer
Definition: itemptr.h:49
ExecRowMark * rowmark
Definition: execnodes.h:654
ItemPointerData curCtid
Definition: execnodes.h:636
#define ERROR
Definition: elog.h:43
static TM_Result table_tuple_lock(Relation rel, ItemPointer tid, Snapshot snapshot, TupleTableSlot *slot, CommandId cid, LockTupleMode mode, LockWaitPolicy wait_policy, uint8 flags, TM_FailureData *tmfd)
Definition: tableam.h:1301
TupleTableSlot * EvalPlanQualSlot(EPQState *epqstate, Relation relation, Index rti)
Definition: execMain.c:2538
#define lfirst_node(type, lc)
Definition: pg_list.h:193
#define outerPlanState(node)
Definition: execnodes.h:1039
PlanState ps
Definition: execnodes.h:2269
Index rti
Definition: execnodes.h:629
Index prti
Definition: execnodes.h:630
void ExecInitResultTypeTL(PlanState *planstate)
Definition: execTuples.c:1720
#define RelationGetRelationName(relation)
Definition: rel.h:450
#define TupIsNull(slot)
Definition: tuptable.h:293
#define ereport(elevel, rest)
Definition: elog.h:141
bool resultopsset
Definition: execnodes.h:1028
Bitmapset * chgParam
Definition: execnodes.h:977
#define outerPlan(node)
Definition: plannodes.h:170
List * lappend(List *list, void *datum)
Definition: list.c:321
TM_Result
Definition: tableam.h:68
ExecProcNodeMtd ExecProcNode
Definition: execnodes.h:951
uintptr_t Datum
Definition: postgres.h:367
static TupleTableSlot * ExecProcNode(PlanState *node)
Definition: executor.h:235
void EvalPlanQualInit(EPQState *epqstate, EState *estate, Plan *subplan, List *auxrowmarks, int epqParam)
Definition: execMain.c:2505
Plan * plan
Definition: execnodes.h:945
#define makeNode(_type_)
Definition: nodes.h:572
#define Assert(condition)
Definition: c.h:732
#define lfirst(lc)
Definition: pg_list.h:190
#define EXEC_FLAG_MARK
Definition: executor.h:59
Definition: tableam.h:74
List * rowMarks
Definition: plannodes.h:954
RowMarkType markType
Definition: execnodes.h:632
#define TUPLE_LOCK_FLAG_FIND_LAST_VERSION
Definition: tableam.h:139
AttrNumber toidAttNo
Definition: execnodes.h:656
List * targetlist
Definition: plannodes.h:140
#define DatumGetPointer(X)
Definition: postgres.h:549
bool resultopsfixed
Definition: execnodes.h:1024
#define ItemPointerSetInvalid(pointer)
Definition: itemptr.h:172
int errmsg(const char *fmt,...)
Definition: elog.c:784
FdwRoutine * GetFdwRoutineForRelation(Relation relation, bool makecopy)
Definition: foreign.c:427
Datum ExecGetJunkAttribute(TupleTableSlot *slot, AttrNumber attno, bool *isNull)
Definition: execJunk.c:247
#define elog(elevel,...)
Definition: elog.h:226
List * lr_arowMarks
Definition: execnodes.h:2270
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:99
bool isParent
Definition: plannodes.h:1064
bool traversed
Definition: tableam.h:126
PlanState * ExecInitNode(Plan *node, EState *estate, int eflags)
Definition: execProcnode.c:139
Definition: pg_list.h:50
AttrNumber ctidAttNo
Definition: execnodes.h:655
ExecAuxRowMark * ExecBuildAuxRowMark(ExecRowMark *erm, List *targetlist)
Definition: execMain.c:2375
#define EvalPlanQualSetSlot(epqstate, slot)
Definition: executor.h:210
RefetchForeignRow_function RefetchForeignRow
Definition: fdwapi.h:225
ExecRowMark * ExecFindRowMark(EState *estate, Index rti, bool missing_ok)
Definition: execMain.c:2352