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