PostgreSQL Source Code  git master
nodeTidscan.c
Go to the documentation of this file.
1 /*-------------------------------------------------------------------------
2  *
3  * nodeTidscan.c
4  * Routines to support direct tid scans of relations
5  *
6  * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  *
10  * IDENTIFICATION
11  * src/backend/executor/nodeTidscan.c
12  *
13  *-------------------------------------------------------------------------
14  */
15 /*
16  * INTERFACE ROUTINES
17  *
18  * ExecTidScan scans a relation using tids
19  * ExecInitTidScan creates and initializes state info.
20  * ExecReScanTidScan rescans the tid relation.
21  * ExecEndTidScan releases all storage.
22  */
23 #include "postgres.h"
24 
25 #include "access/sysattr.h"
26 #include "access/tableam.h"
27 #include "catalog/pg_type.h"
28 #include "executor/execdebug.h"
29 #include "executor/nodeTidscan.h"
30 #include "lib/qunique.h"
31 #include "miscadmin.h"
32 #include "nodes/nodeFuncs.h"
33 #include "storage/bufmgr.h"
34 #include "utils/array.h"
35 #include "utils/rel.h"
36 
37 
38 /*
39  * It's sufficient to check varattno to identify the CTID variable, as any
40  * Var in the relation scan qual must be for our table. (Even if it's a
41  * parameterized scan referencing some other table's CTID, the other table's
42  * Var would have become a Param by the time it gets here.)
43  */
44 #define IsCTIDVar(node) \
45  ((node) != NULL && \
46  IsA((node), Var) && \
47  ((Var *) (node))->varattno == SelfItemPointerAttributeNumber)
48 
49 /* one element in tss_tidexprs */
50 typedef struct TidExpr
51 {
52  ExprState *exprstate; /* ExprState for a TID-yielding subexpr */
53  bool isarray; /* if true, it yields tid[] not just tid */
54  CurrentOfExpr *cexpr; /* alternatively, we can have CURRENT OF */
56 
57 static void TidExprListCreate(TidScanState *tidstate);
58 static void TidListEval(TidScanState *tidstate);
59 static int itemptr_comparator(const void *a, const void *b);
60 static TupleTableSlot *TidNext(TidScanState *node);
61 
62 
63 /*
64  * Extract the qual subexpressions that yield TIDs to search for,
65  * and compile them into ExprStates if they're ordinary expressions.
66  *
67  * CURRENT OF is a special case that we can't compile usefully;
68  * just drop it into the TidExpr list as-is.
69  */
70 static void
72 {
73  TidScan *node = (TidScan *) tidstate->ss.ps.plan;
74  ListCell *l;
75 
76  tidstate->tss_tidexprs = NIL;
77  tidstate->tss_isCurrentOf = false;
78 
79  foreach(l, node->tidquals)
80  {
81  Expr *expr = (Expr *) lfirst(l);
82  TidExpr *tidexpr = (TidExpr *) palloc0(sizeof(TidExpr));
83 
84  if (is_opclause(expr))
85  {
86  Node *arg1;
87  Node *arg2;
88 
89  arg1 = get_leftop(expr);
90  arg2 = get_rightop(expr);
91  if (IsCTIDVar(arg1))
92  tidexpr->exprstate = ExecInitExpr((Expr *) arg2,
93  &tidstate->ss.ps);
94  else if (IsCTIDVar(arg2))
95  tidexpr->exprstate = ExecInitExpr((Expr *) arg1,
96  &tidstate->ss.ps);
97  else
98  elog(ERROR, "could not identify CTID variable");
99  tidexpr->isarray = false;
100  }
101  else if (expr && IsA(expr, ScalarArrayOpExpr))
102  {
103  ScalarArrayOpExpr *saex = (ScalarArrayOpExpr *) expr;
104 
105  Assert(IsCTIDVar(linitial(saex->args)));
106  tidexpr->exprstate = ExecInitExpr(lsecond(saex->args),
107  &tidstate->ss.ps);
108  tidexpr->isarray = true;
109  }
110  else if (expr && IsA(expr, CurrentOfExpr))
111  {
112  CurrentOfExpr *cexpr = (CurrentOfExpr *) expr;
113 
114  tidexpr->cexpr = cexpr;
115  tidstate->tss_isCurrentOf = true;
116  }
117  else
118  elog(ERROR, "could not identify CTID expression");
119 
120  tidstate->tss_tidexprs = lappend(tidstate->tss_tidexprs, tidexpr);
121  }
122 
123  /* CurrentOfExpr could never appear OR'd with something else */
124  Assert(list_length(tidstate->tss_tidexprs) == 1 ||
125  !tidstate->tss_isCurrentOf);
126 }
127 
128 /*
129  * Compute the list of TIDs to be visited, by evaluating the expressions
130  * for them.
131  *
132  * (The result is actually an array, not a list.)
133  */
134 static void
136 {
137  ExprContext *econtext = tidstate->ss.ps.ps_ExprContext;
138  TableScanDesc scan;
139  ItemPointerData *tidList;
140  int numAllocTids;
141  int numTids;
142  ListCell *l;
143 
144  /*
145  * Start scan on-demand - initializing a scan isn't free (e.g. heap stats
146  * the size of the table), so it makes sense to delay that until needed -
147  * the node might never get executed.
148  */
149  if (tidstate->ss.ss_currentScanDesc == NULL)
150  tidstate->ss.ss_currentScanDesc =
152  tidstate->ss.ps.state->es_snapshot);
153  scan = tidstate->ss.ss_currentScanDesc;
154 
155  /*
156  * We initialize the array with enough slots for the case that all quals
157  * are simple OpExprs or CurrentOfExprs. If there are any
158  * ScalarArrayOpExprs, we may have to enlarge the array.
159  */
160  numAllocTids = list_length(tidstate->tss_tidexprs);
161  tidList = (ItemPointerData *)
162  palloc(numAllocTids * sizeof(ItemPointerData));
163  numTids = 0;
164 
165  foreach(l, tidstate->tss_tidexprs)
166  {
167  TidExpr *tidexpr = (TidExpr *) lfirst(l);
168  ItemPointer itemptr;
169  bool isNull;
170 
171  if (tidexpr->exprstate && !tidexpr->isarray)
172  {
173  itemptr = (ItemPointer)
175  econtext,
176  &isNull));
177  if (isNull)
178  continue;
179 
180  /*
181  * We silently discard any TIDs that the AM considers invalid
182  * (E.g. for heap, they could be out of range at the time of scan
183  * start. Since we hold at least AccessShareLock on the table, it
184  * won't be possible for someone to truncate away the blocks we
185  * intend to visit.).
186  */
187  if (!table_tuple_tid_valid(scan, itemptr))
188  continue;
189 
190  if (numTids >= numAllocTids)
191  {
192  numAllocTids *= 2;
193  tidList = (ItemPointerData *)
194  repalloc(tidList,
195  numAllocTids * sizeof(ItemPointerData));
196  }
197  tidList[numTids++] = *itemptr;
198  }
199  else if (tidexpr->exprstate && tidexpr->isarray)
200  {
201  Datum arraydatum;
202  ArrayType *itemarray;
203  Datum *ipdatums;
204  bool *ipnulls;
205  int ndatums;
206  int i;
207 
208  arraydatum = ExecEvalExprSwitchContext(tidexpr->exprstate,
209  econtext,
210  &isNull);
211  if (isNull)
212  continue;
213  itemarray = DatumGetArrayTypeP(arraydatum);
214  deconstruct_array_builtin(itemarray, TIDOID, &ipdatums, &ipnulls, &ndatums);
215  if (numTids + ndatums > numAllocTids)
216  {
217  numAllocTids = numTids + ndatums;
218  tidList = (ItemPointerData *)
219  repalloc(tidList,
220  numAllocTids * sizeof(ItemPointerData));
221  }
222  for (i = 0; i < ndatums; i++)
223  {
224  if (ipnulls[i])
225  continue;
226 
227  itemptr = (ItemPointer) DatumGetPointer(ipdatums[i]);
228 
229  if (!table_tuple_tid_valid(scan, itemptr))
230  continue;
231 
232  tidList[numTids++] = *itemptr;
233  }
234  pfree(ipdatums);
235  pfree(ipnulls);
236  }
237  else
238  {
239  ItemPointerData cursor_tid;
240 
241  Assert(tidexpr->cexpr);
242  if (execCurrentOf(tidexpr->cexpr, econtext,
244  &cursor_tid))
245  {
246  if (numTids >= numAllocTids)
247  {
248  numAllocTids *= 2;
249  tidList = (ItemPointerData *)
250  repalloc(tidList,
251  numAllocTids * sizeof(ItemPointerData));
252  }
253  tidList[numTids++] = cursor_tid;
254  }
255  }
256  }
257 
258  /*
259  * Sort the array of TIDs into order, and eliminate duplicates.
260  * Eliminating duplicates is necessary since we want OR semantics across
261  * the list. Sorting makes it easier to detect duplicates, and as a bonus
262  * ensures that we will visit the heap in the most efficient way.
263  */
264  if (numTids > 1)
265  {
266  /* CurrentOfExpr could never appear OR'd with something else */
267  Assert(!tidstate->tss_isCurrentOf);
268 
269  qsort(tidList, numTids, sizeof(ItemPointerData),
271  numTids = qunique(tidList, numTids, sizeof(ItemPointerData),
273  }
274 
275  tidstate->tss_TidList = tidList;
276  tidstate->tss_NumTids = numTids;
277  tidstate->tss_TidPtr = -1;
278 }
279 
280 /*
281  * qsort comparator for ItemPointerData items
282  */
283 static int
284 itemptr_comparator(const void *a, const void *b)
285 {
286  const ItemPointerData *ipa = (const ItemPointerData *) a;
287  const ItemPointerData *ipb = (const ItemPointerData *) b;
292 
293  if (ba < bb)
294  return -1;
295  if (ba > bb)
296  return 1;
297  if (oa < ob)
298  return -1;
299  if (oa > ob)
300  return 1;
301  return 0;
302 }
303 
304 /* ----------------------------------------------------------------
305  * TidNext
306  *
307  * Retrieve a tuple from the TidScan node's currentRelation
308  * using the tids in the TidScanState information.
309  *
310  * ----------------------------------------------------------------
311  */
312 static TupleTableSlot *
314 {
315  EState *estate;
316  ScanDirection direction;
317  Snapshot snapshot;
318  TableScanDesc scan;
319  Relation heapRelation;
320  TupleTableSlot *slot;
321  ItemPointerData *tidList;
322  int numTids;
323  bool bBackward;
324 
325  /*
326  * extract necessary information from tid scan node
327  */
328  estate = node->ss.ps.state;
329  direction = estate->es_direction;
330  snapshot = estate->es_snapshot;
331  heapRelation = node->ss.ss_currentRelation;
332  slot = node->ss.ss_ScanTupleSlot;
333 
334  /*
335  * First time through, compute the list of TIDs to be visited
336  */
337  if (node->tss_TidList == NULL)
338  TidListEval(node);
339 
340  scan = node->ss.ss_currentScanDesc;
341  tidList = node->tss_TidList;
342  numTids = node->tss_NumTids;
343 
344  /*
345  * Initialize or advance scan position, depending on direction.
346  */
347  bBackward = ScanDirectionIsBackward(direction);
348  if (bBackward)
349  {
350  if (node->tss_TidPtr < 0)
351  {
352  /* initialize for backward scan */
353  node->tss_TidPtr = numTids - 1;
354  }
355  else
356  node->tss_TidPtr--;
357  }
358  else
359  {
360  if (node->tss_TidPtr < 0)
361  {
362  /* initialize for forward scan */
363  node->tss_TidPtr = 0;
364  }
365  else
366  node->tss_TidPtr++;
367  }
368 
369  while (node->tss_TidPtr >= 0 && node->tss_TidPtr < numTids)
370  {
371  ItemPointerData tid = tidList[node->tss_TidPtr];
372 
373  /*
374  * For WHERE CURRENT OF, the tuple retrieved from the cursor might
375  * since have been updated; if so, we should fetch the version that is
376  * current according to our snapshot.
377  */
378  if (node->tss_isCurrentOf)
379  table_tuple_get_latest_tid(scan, &tid);
380 
381  if (table_tuple_fetch_row_version(heapRelation, &tid, snapshot, slot))
382  return slot;
383 
384  /* Bad TID or failed snapshot qual; try next */
385  if (bBackward)
386  node->tss_TidPtr--;
387  else
388  node->tss_TidPtr++;
389 
391  }
392 
393  /*
394  * if we get here it means the tid scan failed so we are at the end of the
395  * scan..
396  */
397  return ExecClearTuple(slot);
398 }
399 
400 /*
401  * TidRecheck -- access method routine to recheck a tuple in EvalPlanQual
402  */
403 static bool
405 {
406  /*
407  * XXX shouldn't we check here to make sure tuple matches TID list? In
408  * runtime-key case this is not certain, is it? However, in the WHERE
409  * CURRENT OF case it might not match anyway ...
410  */
411  return true;
412 }
413 
414 
415 /* ----------------------------------------------------------------
416  * ExecTidScan(node)
417  *
418  * Scans the relation using tids and returns
419  * the next qualifying tuple in the direction specified.
420  * We call the ExecScan() routine and pass it the appropriate
421  * access method functions.
422  *
423  * Conditions:
424  * -- the "cursor" maintained by the AMI is positioned at the tuple
425  * returned previously.
426  *
427  * Initial States:
428  * -- the relation indicated is opened for scanning so that the
429  * "cursor" is positioned before the first qualifying tuple.
430  * -- tss_TidPtr is -1.
431  * ----------------------------------------------------------------
432  */
433 static TupleTableSlot *
435 {
436  TidScanState *node = castNode(TidScanState, pstate);
437 
438  return ExecScan(&node->ss,
441 }
442 
443 /* ----------------------------------------------------------------
444  * ExecReScanTidScan(node)
445  * ----------------------------------------------------------------
446  */
447 void
449 {
450  if (node->tss_TidList)
451  pfree(node->tss_TidList);
452  node->tss_TidList = NULL;
453  node->tss_NumTids = 0;
454  node->tss_TidPtr = -1;
455 
456  /* not really necessary, but seems good form */
457  if (node->ss.ss_currentScanDesc)
458  table_rescan(node->ss.ss_currentScanDesc, NULL);
459 
460  ExecScanReScan(&node->ss);
461 }
462 
463 /* ----------------------------------------------------------------
464  * ExecEndTidScan
465  *
466  * Releases any storage allocated through C routines.
467  * Returns nothing.
468  * ----------------------------------------------------------------
469  */
470 void
472 {
473  if (node->ss.ss_currentScanDesc)
475 
476  /*
477  * Free the exprcontext
478  */
479  ExecFreeExprContext(&node->ss.ps);
480 
481  /*
482  * clear out tuple table slots
483  */
484  if (node->ss.ps.ps_ResultTupleSlot)
487 }
488 
489 /* ----------------------------------------------------------------
490  * ExecInitTidScan
491  *
492  * Initializes the tid scan's state information, creates
493  * scan keys, and opens the base and tid relations.
494  *
495  * Parameters:
496  * node: TidScan node produced by the planner.
497  * estate: the execution state initialized in InitPlan.
498  * ----------------------------------------------------------------
499  */
500 TidScanState *
501 ExecInitTidScan(TidScan *node, EState *estate, int eflags)
502 {
503  TidScanState *tidstate;
504  Relation currentRelation;
505 
506  /*
507  * create state structure
508  */
509  tidstate = makeNode(TidScanState);
510  tidstate->ss.ps.plan = (Plan *) node;
511  tidstate->ss.ps.state = estate;
512  tidstate->ss.ps.ExecProcNode = ExecTidScan;
513 
514  /*
515  * Miscellaneous initialization
516  *
517  * create expression context for node
518  */
519  ExecAssignExprContext(estate, &tidstate->ss.ps);
520 
521  /*
522  * mark tid list as not computed yet
523  */
524  tidstate->tss_TidList = NULL;
525  tidstate->tss_NumTids = 0;
526  tidstate->tss_TidPtr = -1;
527 
528  /*
529  * open the scan relation
530  */
531  currentRelation = ExecOpenScanRelation(estate, node->scan.scanrelid, eflags);
532 
533  tidstate->ss.ss_currentRelation = currentRelation;
534  tidstate->ss.ss_currentScanDesc = NULL; /* no heap scan here */
535 
536  /*
537  * get the scan type from the relation descriptor.
538  */
539  ExecInitScanTupleSlot(estate, &tidstate->ss,
540  RelationGetDescr(currentRelation),
541  table_slot_callbacks(currentRelation));
542 
543  /*
544  * Initialize result type and projection.
545  */
546  ExecInitResultTypeTL(&tidstate->ss.ps);
547  ExecAssignScanProjectionInfo(&tidstate->ss);
548 
549  /*
550  * initialize child expressions
551  */
552  tidstate->ss.ps.qual =
553  ExecInitQual(node->scan.plan.qual, (PlanState *) tidstate);
554 
555  TidExprListCreate(tidstate);
556 
557  /*
558  * all done.
559  */
560  return tidstate;
561 }
#define DatumGetArrayTypeP(X)
Definition: array.h:254
void deconstruct_array_builtin(ArrayType *array, Oid elmtype, Datum **elemsp, bool **nullsp, int *nelemsp)
Definition: arrayfuncs.c:3665
uint32 BlockNumber
Definition: block.h:31
#define ERROR
Definition: elog.h:39
bool execCurrentOf(CurrentOfExpr *cexpr, ExprContext *econtext, Oid table_oid, ItemPointer current_tid)
Definition: execCurrent.c:44
ExprState * ExecInitQual(List *qual, PlanState *parent)
Definition: execExpr.c:213
ExprState * ExecInitExpr(Expr *node, PlanState *parent)
Definition: execExpr.c:127
TupleTableSlot * ExecScan(ScanState *node, ExecScanAccessMtd accessMtd, ExecScanRecheckMtd recheckMtd)
Definition: execScan.c:157
void ExecAssignScanProjectionInfo(ScanState *node)
Definition: execScan.c:271
void ExecScanReScan(ScanState *node)
Definition: execScan.c:298
void ExecInitScanTupleSlot(EState *estate, ScanState *scanstate, TupleDesc tupledesc, const TupleTableSlotOps *tts_ops)
Definition: execTuples.c:1812
void ExecInitResultTypeTL(PlanState *planstate)
Definition: execTuples.c:1756
void ExecAssignExprContext(EState *estate, PlanState *planstate)
Definition: execUtils.c:488
Relation ExecOpenScanRelation(EState *estate, Index scanrelid, int eflags)
Definition: execUtils.c:728
void ExecFreeExprContext(PlanState *planstate)
Definition: execUtils.c:658
TupleTableSlot *(* ExecScanAccessMtd)(ScanState *node)
Definition: executor.h:472
bool(* ExecScanRecheckMtd)(ScanState *node, TupleTableSlot *slot)
Definition: executor.h:473
static Datum ExecEvalExprSwitchContext(ExprState *state, ExprContext *econtext, bool *isNull)
Definition: executor.h:347
int b
Definition: isn.c:70
int a
Definition: isn.c:69
int i
Definition: isn.c:73
static OffsetNumber ItemPointerGetOffsetNumber(const ItemPointerData *pointer)
Definition: itemptr.h:124
static BlockNumber ItemPointerGetBlockNumber(const ItemPointerData *pointer)
Definition: itemptr.h:103
ItemPointerData * ItemPointer
Definition: itemptr.h:49
Assert(fmt[strlen(fmt) - 1] !='\n')
List * lappend(List *list, void *datum)
Definition: list.c:338
void pfree(void *pointer)
Definition: mcxt.c:1456
void * palloc0(Size size)
Definition: mcxt.c:1257
void * repalloc(void *pointer, Size size)
Definition: mcxt.c:1476
void * palloc(Size size)
Definition: mcxt.c:1226
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:121
static bool is_opclause(const void *clause)
Definition: nodeFuncs.h:74
static Node * get_rightop(const void *clause)
Definition: nodeFuncs.h:93
static Node * get_leftop(const void *clause)
Definition: nodeFuncs.h:81
static TupleTableSlot * ExecTidScan(PlanState *pstate)
Definition: nodeTidscan.c:434
static int itemptr_comparator(const void *a, const void *b)
Definition: nodeTidscan.c:284
static bool TidRecheck(TidScanState *node, TupleTableSlot *slot)
Definition: nodeTidscan.c:404
void ExecEndTidScan(TidScanState *node)
Definition: nodeTidscan.c:471
void ExecReScanTidScan(TidScanState *node)
Definition: nodeTidscan.c:448
struct TidExpr TidExpr
static void TidListEval(TidScanState *tidstate)
Definition: nodeTidscan.c:135
TidScanState * ExecInitTidScan(TidScan *node, EState *estate, int eflags)
Definition: nodeTidscan.c:501
#define IsCTIDVar(node)
Definition: nodeTidscan.c:44
static TupleTableSlot * TidNext(TidScanState *node)
Definition: nodeTidscan.c:313
static void TidExprListCreate(TidScanState *tidstate)
Definition: nodeTidscan.c:71
#define IsA(nodeptr, _type_)
Definition: nodes.h:179
#define makeNode(_type_)
Definition: nodes.h:176
#define castNode(_type_, nodeptr)
Definition: nodes.h:197
uint16 OffsetNumber
Definition: off.h:24
#define lfirst(lc)
Definition: pg_list.h:172
static int list_length(const List *l)
Definition: pg_list.h:152
#define NIL
Definition: pg_list.h:68
#define linitial(l)
Definition: pg_list.h:178
#define lsecond(l)
Definition: pg_list.h:183
#define qsort(a, b, c, d)
Definition: port.h:445
uintptr_t Datum
Definition: postgres.h:64
static Pointer DatumGetPointer(Datum X)
Definition: postgres.h:312
static size_t qunique(void *array, size_t elements, size_t width, int(*compare)(const void *, const void *))
Definition: qunique.h:21
#define RelationGetRelid(relation)
Definition: rel.h:504
#define RelationGetDescr(relation)
Definition: rel.h:530
#define ScanDirectionIsBackward(direction)
Definition: sdir.h:50
ScanDirection
Definition: sdir.h:25
ScanDirection es_direction
Definition: execnodes.h:615
Snapshot es_snapshot
Definition: execnodes.h:616
Definition: nodes.h:129
ExprState * qual
Definition: execnodes.h:1058
Plan * plan
Definition: execnodes.h:1037
EState * state
Definition: execnodes.h:1039
ExprContext * ps_ExprContext
Definition: execnodes.h:1076
TupleTableSlot * ps_ResultTupleSlot
Definition: execnodes.h:1075
ExecProcNodeMtd ExecProcNode
Definition: execnodes.h:1043
Relation ss_currentRelation
Definition: execnodes.h:1475
TupleTableSlot * ss_ScanTupleSlot
Definition: execnodes.h:1477
PlanState ps
Definition: execnodes.h:1474
struct TableScanDescData * ss_currentScanDesc
Definition: execnodes.h:1476
Index scanrelid
Definition: plannodes.h:387
CurrentOfExpr * cexpr
Definition: nodeTidscan.c:54
ExprState * exprstate
Definition: nodeTidscan.c:52
bool isarray
Definition: nodeTidscan.c:53
ScanState ss
Definition: execnodes.h:1762
bool tss_isCurrentOf
Definition: execnodes.h:1764
ItemPointerData * tss_TidList
Definition: execnodes.h:1767
List * tss_tidexprs
Definition: execnodes.h:1763
Scan scan
Definition: plannodes.h:552
List * tidquals
Definition: plannodes.h:553
const TupleTableSlotOps * table_slot_callbacks(Relation relation)
Definition: tableam.c:58
void table_tuple_get_latest_tid(TableScanDesc scan, ItemPointer tid)
Definition: tableam.c:245
static void table_endscan(TableScanDesc scan)
Definition: tableam.h:1009
static void table_rescan(TableScanDesc scan, struct ScanKeyData *key)
Definition: tableam.h:1018
static bool table_tuple_tid_valid(TableScanDesc scan, ItemPointer tid)
Definition: tableam.h:1309
static bool table_tuple_fetch_row_version(Relation rel, ItemPointer tid, Snapshot snapshot, TupleTableSlot *slot)
Definition: tableam.h:1283
static TableScanDesc table_beginscan_tid(Relation rel, Snapshot snapshot)
Definition: tableam.h:985
static TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition: tuptable.h:433