PostgreSQL Source Code  git master
nodeLockRows.c File Reference
#include "postgres.h"
#include "access/tableam.h"
#include "access/xact.h"
#include "executor/executor.h"
#include "executor/nodeLockRows.h"
#include "foreign/fdwapi.h"
#include "miscadmin.h"
#include "utils/rel.h"
Include dependency graph for nodeLockRows.c:

Go to the source code of this file.

Functions

static TupleTableSlotExecLockRows (PlanState *pstate)
 
LockRowsStateExecInitLockRows (LockRows *node, EState *estate, int eflags)
 
void ExecEndLockRows (LockRowsState *node)
 
void ExecReScanLockRows (LockRowsState *node)
 

Function Documentation

◆ ExecEndLockRows()

void ExecEndLockRows ( LockRowsState node)

Definition at line 382 of file nodeLockRows.c.

References EvalPlanQualEnd(), ExecEndNode(), LockRowsState::lr_epqstate, and outerPlanState.

Referenced by ExecEndNode().

383 {
386 }
EPQState lr_epqstate
Definition: execnodes.h:2322
void ExecEndNode(PlanState *node)
Definition: execProcnode.c:538
void EvalPlanQualEnd(EPQState *epqstate)
Definition: execMain.c:2953
#define outerPlanState(node)
Definition: execnodes.h:1034

◆ ExecInitLockRows()

LockRowsState* ExecInitLockRows ( LockRows node,
EState estate,
int  eflags 
)

Definition at line 288 of file nodeLockRows.c.

References Assert, LockRows::epqParam, EvalPlanQualInit(), EXEC_FLAG_MARK, ExecBuildAuxRowMark(), ExecFindRowMark(), ExecGetResultSlotOps(), ExecInitNode(), ExecInitResultTypeTL(), ExecLockRows(), PlanState::ExecProcNode, PlanRowMark::isParent, lappend(), lfirst_node, LockRowsState::lr_arowMarks, LockRowsState::lr_epqstate, makeNode, ExecRowMark::markType, NIL, outerPlan, outerPlanState, PlanState::plan, LockRowsState::ps, PlanState::ps_ProjInfo, PlanState::resultops, PlanState::resultopsfixed, PlanState::resultopsset, RowMarkRequiresRowShareLock, LockRows::rowMarks, PlanRowMark::rti, PlanState::state, and Plan::targetlist.

Referenced by ExecInitNode().

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  {
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  */
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 }
#define NIL
Definition: pg_list.h:65
ProjectionInfo * ps_ProjInfo
Definition: execnodes.h:980
const TupleTableSlotOps * ExecGetResultSlotOps(PlanState *planstate, bool *isfixed)
Definition: execUtils.c:463
EPQState lr_epqstate
Definition: execnodes.h:2322
static TupleTableSlot * ExecLockRows(PlanState *pstate)
Definition: nodeLockRows.c:38
int epqParam
Definition: plannodes.h:955
EState * state
Definition: execnodes.h:942
#define RowMarkRequiresRowShareLock(marktype)
Definition: plannodes.h:1013
const TupleTableSlotOps * resultops
Definition: execnodes.h:1015
#define lfirst_node(type, lc)
Definition: pg_list.h:193
#define outerPlanState(node)
Definition: execnodes.h:1034
PlanState ps
Definition: execnodes.h:2320
void ExecInitResultTypeTL(PlanState *planstate)
Definition: execTuples.c:1720
bool resultopsset
Definition: execnodes.h:1023
#define outerPlan(node)
Definition: plannodes.h:170
List * lappend(List *list, void *datum)
Definition: list.c:322
void EvalPlanQualInit(EPQState *epqstate, EState *parentestate, Plan *subplan, List *auxrowmarks, int epqParam)
Definition: execMain.c:2493
ExecProcNodeMtd ExecProcNode
Definition: execnodes.h:946
Plan * plan
Definition: execnodes.h:940
#define makeNode(_type_)
Definition: nodes.h:573
#define Assert(condition)
Definition: c.h:732
#define EXEC_FLAG_MARK
Definition: executor.h:59
List * rowMarks
Definition: plannodes.h:954
RowMarkType markType
Definition: execnodes.h:627
List * targetlist
Definition: plannodes.h:140
bool resultopsfixed
Definition: execnodes.h:1019
List * lr_arowMarks
Definition: execnodes.h:2321
bool isParent
Definition: plannodes.h:1064
PlanState * ExecInitNode(Plan *node, EState *estate, int eflags)
Definition: execProcnode.c:139
Definition: pg_list.h:50
ExecAuxRowMark * ExecBuildAuxRowMark(ExecRowMark *erm, List *targetlist)
Definition: execMain.c:2369
ExecRowMark * ExecFindRowMark(EState *estate, Index rti, bool missing_ok)
Definition: execMain.c:2346

◆ ExecLockRows()

static TupleTableSlot* ExecLockRows ( PlanState pstate)
static

Definition at line 38 of file nodeLockRows.c.

References Assert, castNode, CHECK_FOR_INTERRUPTS, ExecAuxRowMark::ctidAttNo, ExecRowMark::curCtid, DatumGetObjectId, DatumGetPointer, elog, ereport, ExecRowMark::ermActive, errcode(), errmsg(), ERROR, EState::es_output_cid, EState::es_snapshot, EvalPlanQualBegin(), EvalPlanQualNext(), EvalPlanQualSetSlot, EvalPlanQualSlot(), ExecClearTuple(), ExecGetJunkAttribute(), ExecProcNode(), GetFdwRoutineForRelation(), IsolationUsesXactSnapshot, ItemPointerSetInvalid, lfirst, lnext(), LockTupleExclusive, LockTupleKeyShare, LockTupleNoKeyExclusive, LockTupleShare, LockRowsState::lr_arowMarks, LockRowsState::lr_epqstate, ExecRowMark::markType, OidIsValid, outerPlan, outerPlanState, ExecRowMark::prti, LockRowsState::ps, RelationData::rd_rel, FdwRoutine::RefetchForeignRow, ExecRowMark::relation, RelationGetRelationName, ExecRowMark::relid, ROW_MARK_EXCLUSIVE, ROW_MARK_KEYSHARE, ROW_MARK_NOKEYEXCLUSIVE, ROW_MARK_SHARE, ExecAuxRowMark::rowmark, ExecRowMark::rti, PlanState::state, table_tuple_lock(), test(), TM_Deleted, TM_Invisible, TM_Ok, TM_SelfModified, TM_Updated, TM_WouldBlock, ExecAuxRowMark::toidAttNo, TM_FailureData::traversed, TupIsNull, TUPLE_LOCK_FLAG_FIND_LAST_VERSION, TUPLE_LOCK_FLAG_LOCK_UPDATE_IN_PROGRESS, and ExecRowMark::waitPolicy.

Referenced by ExecInitLockRows().

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  * 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;
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;
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\"",
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;
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 
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:
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:
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 */
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 }
LockTupleMode
Definition: lockoptions.h:49
CommandId es_output_cid
Definition: execnodes.h:517
static void test(void)
static TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition: tuptable.h:426
EPQState lr_epqstate
Definition: execnodes.h:2322
static ListCell * lnext(const List *l, const ListCell *c)
Definition: pg_list.h:321
#define castNode(_type_, nodeptr)
Definition: nodes.h:594
Relation relation
Definition: execnodes.h:622
#define DatumGetObjectId(X)
Definition: postgres.h:500
#define IsolationUsesXactSnapshot()
Definition: xact.h:51
int errcode(int sqlerrcode)
Definition: elog.c:570
bool ermActive
Definition: execnodes.h:630
LockWaitPolicy waitPolicy
Definition: execnodes.h:629
Snapshot es_snapshot
Definition: execnodes.h:503
#define TUPLE_LOCK_FLAG_LOCK_UPDATE_IN_PROGRESS
Definition: tableam.h:137
EState * state
Definition: execnodes.h:942
Form_pg_class rd_rel
Definition: rel.h:83
unsigned int Oid
Definition: postgres_ext.h:31
#define OidIsValid(objectId)
Definition: c.h:638
TupleTableSlot * EvalPlanQualNext(EPQState *epqstate)
Definition: execMain.c:2685
ItemPointerData * ItemPointer
Definition: itemptr.h:49
ExecRowMark * rowmark
Definition: execnodes.h:649
ItemPointerData curCtid
Definition: execnodes.h:631
#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:2548
#define outerPlanState(node)
Definition: execnodes.h:1034
PlanState ps
Definition: execnodes.h:2320
Index rti
Definition: execnodes.h:624
Index prti
Definition: execnodes.h:625
#define RelationGetRelationName(relation)
Definition: rel.h:453
#define TupIsNull(slot)
Definition: tuptable.h:293
#define ereport(elevel, rest)
Definition: elog.h:141
#define outerPlan(node)
Definition: plannodes.h:170
TM_Result
Definition: tableam.h:68
uintptr_t Datum
Definition: postgres.h:367
static TupleTableSlot * ExecProcNode(PlanState *node)
Definition: executor.h:235
#define Assert(condition)
Definition: c.h:732
#define lfirst(lc)
Definition: pg_list.h:190
Definition: tableam.h:74
RowMarkType markType
Definition: execnodes.h:627
#define TUPLE_LOCK_FLAG_FIND_LAST_VERSION
Definition: tableam.h:139
AttrNumber toidAttNo
Definition: execnodes.h:651
#define DatumGetPointer(X)
Definition: postgres.h:549
#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:2321
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:99
bool traversed
Definition: tableam.h:126
void EvalPlanQualBegin(EPQState *epqstate)
Definition: execMain.c:2701
AttrNumber ctidAttNo
Definition: execnodes.h:650
#define EvalPlanQualSetSlot(epqstate, slot)
Definition: executor.h:210
RefetchForeignRow_function RefetchForeignRow
Definition: fdwapi.h:225

◆ ExecReScanLockRows()

void ExecReScanLockRows ( LockRowsState node)

Definition at line 390 of file nodeLockRows.c.

References PlanState::chgParam, ExecReScan(), PlanState::lefttree, and LockRowsState::ps.

Referenced by ExecReScan().

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 }
void ExecReScan(PlanState *node)
Definition: execAmi.c:77
struct PlanState * lefttree
Definition: execnodes.h:962
PlanState ps
Definition: execnodes.h:2320
Bitmapset * chgParam
Definition: execnodes.h:972