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