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