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-2019, 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  0, NULL);
149  scan = tidstate->ss.ss_currentScanDesc;
150 
151  /*
152  * We initialize the array with enough slots for the case that all quals
153  * are simple OpExprs or CurrentOfExprs. If there are any
154  * ScalarArrayOpExprs, we may have to enlarge the array.
155  */
156  numAllocTids = list_length(tidstate->tss_tidexprs);
157  tidList = (ItemPointerData *)
158  palloc(numAllocTids * sizeof(ItemPointerData));
159  numTids = 0;
160 
161  foreach(l, tidstate->tss_tidexprs)
162  {
163  TidExpr *tidexpr = (TidExpr *) lfirst(l);
164  ItemPointer itemptr;
165  bool isNull;
166 
167  if (tidexpr->exprstate && !tidexpr->isarray)
168  {
169  itemptr = (ItemPointer)
171  econtext,
172  &isNull));
173  if (isNull)
174  continue;
175 
176  /*
177  * We silently discard any TIDs that the AM considers invalid
178  * (E.g. for heap, they could be out of range at the time of scan
179  * start. Since we hold at least AccessShareLock on the table, it
180  * won't be possible for someone to truncate away the blocks we
181  * intend to visit.).
182  */
183  if (!table_tuple_tid_valid(scan, itemptr))
184  continue;
185 
186  if (numTids >= numAllocTids)
187  {
188  numAllocTids *= 2;
189  tidList = (ItemPointerData *)
190  repalloc(tidList,
191  numAllocTids * sizeof(ItemPointerData));
192  }
193  tidList[numTids++] = *itemptr;
194  }
195  else if (tidexpr->exprstate && tidexpr->isarray)
196  {
197  Datum arraydatum;
198  ArrayType *itemarray;
199  Datum *ipdatums;
200  bool *ipnulls;
201  int ndatums;
202  int i;
203 
204  arraydatum = ExecEvalExprSwitchContext(tidexpr->exprstate,
205  econtext,
206  &isNull);
207  if (isNull)
208  continue;
209  itemarray = DatumGetArrayTypeP(arraydatum);
210  deconstruct_array(itemarray,
211  TIDOID, sizeof(ItemPointerData), false, 's',
212  &ipdatums, &ipnulls, &ndatums);
213  if (numTids + ndatums > numAllocTids)
214  {
215  numAllocTids = numTids + ndatums;
216  tidList = (ItemPointerData *)
217  repalloc(tidList,
218  numAllocTids * sizeof(ItemPointerData));
219  }
220  for (i = 0; i < ndatums; i++)
221  {
222  if (ipnulls[i])
223  continue;
224 
225  itemptr = (ItemPointer) DatumGetPointer(ipdatums[i]);
226 
227  if (!table_tuple_tid_valid(scan, itemptr))
228  continue;
229 
230  tidList[numTids++] = *itemptr;
231  }
232  pfree(ipdatums);
233  pfree(ipnulls);
234  }
235  else
236  {
237  ItemPointerData cursor_tid;
238 
239  Assert(tidexpr->cexpr);
240  if (execCurrentOf(tidexpr->cexpr, econtext,
242  &cursor_tid))
243  {
244  if (numTids >= numAllocTids)
245  {
246  numAllocTids *= 2;
247  tidList = (ItemPointerData *)
248  repalloc(tidList,
249  numAllocTids * sizeof(ItemPointerData));
250  }
251  tidList[numTids++] = cursor_tid;
252  }
253  }
254  }
255 
256  /*
257  * Sort the array of TIDs into order, and eliminate duplicates.
258  * Eliminating duplicates is necessary since we want OR semantics across
259  * the list. Sorting makes it easier to detect duplicates, and as a bonus
260  * ensures that we will visit the heap in the most efficient way.
261  */
262  if (numTids > 1)
263  {
264  /* CurrentOfExpr could never appear OR'd with something else */
265  Assert(!tidstate->tss_isCurrentOf);
266 
267  qsort((void *) tidList, numTids, sizeof(ItemPointerData),
269  numTids = qunique(tidList, numTids, sizeof(ItemPointerData),
271  }
272 
273  tidstate->tss_TidList = tidList;
274  tidstate->tss_NumTids = numTids;
275  tidstate->tss_TidPtr = -1;
276 }
277 
278 /*
279  * qsort comparator for ItemPointerData items
280  */
281 static int
282 itemptr_comparator(const void *a, const void *b)
283 {
284  const ItemPointerData *ipa = (const ItemPointerData *) a;
285  const ItemPointerData *ipb = (const ItemPointerData *) b;
290 
291  if (ba < bb)
292  return -1;
293  if (ba > bb)
294  return 1;
295  if (oa < ob)
296  return -1;
297  if (oa > ob)
298  return 1;
299  return 0;
300 }
301 
302 /* ----------------------------------------------------------------
303  * TidNext
304  *
305  * Retrieve a tuple from the TidScan node's currentRelation
306  * using the tids in the TidScanState information.
307  *
308  * ----------------------------------------------------------------
309  */
310 static TupleTableSlot *
312 {
313  EState *estate;
314  ScanDirection direction;
315  Snapshot snapshot;
316  TableScanDesc scan;
317  Relation heapRelation;
318  TupleTableSlot *slot;
319  ItemPointerData *tidList;
320  int numTids;
321  bool bBackward;
322 
323  /*
324  * extract necessary information from tid scan node
325  */
326  estate = node->ss.ps.state;
327  direction = estate->es_direction;
328  snapshot = estate->es_snapshot;
329  heapRelation = node->ss.ss_currentRelation;
330  slot = node->ss.ss_ScanTupleSlot;
331 
332  /*
333  * First time through, compute the list of TIDs to be visited
334  */
335  if (node->tss_TidList == NULL)
336  TidListEval(node);
337 
338  scan = node->ss.ss_currentScanDesc;
339  tidList = node->tss_TidList;
340  numTids = node->tss_NumTids;
341 
342  /*
343  * Initialize or advance scan position, depending on direction.
344  */
345  bBackward = ScanDirectionIsBackward(direction);
346  if (bBackward)
347  {
348  if (node->tss_TidPtr < 0)
349  {
350  /* initialize for backward scan */
351  node->tss_TidPtr = numTids - 1;
352  }
353  else
354  node->tss_TidPtr--;
355  }
356  else
357  {
358  if (node->tss_TidPtr < 0)
359  {
360  /* initialize for forward scan */
361  node->tss_TidPtr = 0;
362  }
363  else
364  node->tss_TidPtr++;
365  }
366 
367  while (node->tss_TidPtr >= 0 && node->tss_TidPtr < numTids)
368  {
369  ItemPointerData tid = tidList[node->tss_TidPtr];
370 
371  /*
372  * For WHERE CURRENT OF, the tuple retrieved from the cursor might
373  * since have been updated; if so, we should fetch the version that is
374  * current according to our snapshot.
375  */
376  if (node->tss_isCurrentOf)
377  table_tuple_get_latest_tid(scan, &tid);
378 
379  if (table_tuple_fetch_row_version(heapRelation, &tid, snapshot, slot))
380  return slot;
381 
382  /* Bad TID or failed snapshot qual; try next */
383  if (bBackward)
384  node->tss_TidPtr--;
385  else
386  node->tss_TidPtr++;
387 
389  }
390 
391  /*
392  * if we get here it means the tid scan failed so we are at the end of the
393  * scan..
394  */
395  return ExecClearTuple(slot);
396 }
397 
398 /*
399  * TidRecheck -- access method routine to recheck a tuple in EvalPlanQual
400  */
401 static bool
403 {
404  /*
405  * XXX shouldn't we check here to make sure tuple matches TID list? In
406  * runtime-key case this is not certain, is it? However, in the WHERE
407  * CURRENT OF case it might not match anyway ...
408  */
409  return true;
410 }
411 
412 
413 /* ----------------------------------------------------------------
414  * ExecTidScan(node)
415  *
416  * Scans the relation using tids and returns
417  * the next qualifying tuple in the direction specified.
418  * We call the ExecScan() routine and pass it the appropriate
419  * access method functions.
420  *
421  * Conditions:
422  * -- the "cursor" maintained by the AMI is positioned at the tuple
423  * returned previously.
424  *
425  * Initial States:
426  * -- the relation indicated is opened for scanning so that the
427  * "cursor" is positioned before the first qualifying tuple.
428  * -- tss_TidPtr is -1.
429  * ----------------------------------------------------------------
430  */
431 static TupleTableSlot *
433 {
434  TidScanState *node = castNode(TidScanState, pstate);
435 
436  return ExecScan(&node->ss,
439 }
440 
441 /* ----------------------------------------------------------------
442  * ExecReScanTidScan(node)
443  * ----------------------------------------------------------------
444  */
445 void
447 {
448  if (node->tss_TidList)
449  pfree(node->tss_TidList);
450  node->tss_TidList = NULL;
451  node->tss_NumTids = 0;
452  node->tss_TidPtr = -1;
453 
454  /* not really necessary, but seems good form */
455  if (node->ss.ss_currentScanDesc)
456  table_rescan(node->ss.ss_currentScanDesc, NULL);
457 
458  ExecScanReScan(&node->ss);
459 }
460 
461 /* ----------------------------------------------------------------
462  * ExecEndTidScan
463  *
464  * Releases any storage allocated through C routines.
465  * Returns nothing.
466  * ----------------------------------------------------------------
467  */
468 void
470 {
471  if (node->ss.ss_currentScanDesc)
473 
474  /*
475  * Free the exprcontext
476  */
477  ExecFreeExprContext(&node->ss.ps);
478 
479  /*
480  * clear out tuple table slots
481  */
482  if (node->ss.ps.ps_ResultTupleSlot)
485 }
486 
487 /* ----------------------------------------------------------------
488  * ExecInitTidScan
489  *
490  * Initializes the tid scan's state information, creates
491  * scan keys, and opens the base and tid relations.
492  *
493  * Parameters:
494  * node: TidScan node produced by the planner.
495  * estate: the execution state initialized in InitPlan.
496  * ----------------------------------------------------------------
497  */
498 TidScanState *
499 ExecInitTidScan(TidScan *node, EState *estate, int eflags)
500 {
501  TidScanState *tidstate;
502  Relation currentRelation;
503 
504  /*
505  * create state structure
506  */
507  tidstate = makeNode(TidScanState);
508  tidstate->ss.ps.plan = (Plan *) node;
509  tidstate->ss.ps.state = estate;
510  tidstate->ss.ps.ExecProcNode = ExecTidScan;
511 
512  /*
513  * Miscellaneous initialization
514  *
515  * create expression context for node
516  */
517  ExecAssignExprContext(estate, &tidstate->ss.ps);
518 
519  /*
520  * mark tid list as not computed yet
521  */
522  tidstate->tss_TidList = NULL;
523  tidstate->tss_NumTids = 0;
524  tidstate->tss_TidPtr = -1;
525 
526  /*
527  * open the scan relation
528  */
529  currentRelation = ExecOpenScanRelation(estate, node->scan.scanrelid, eflags);
530 
531  tidstate->ss.ss_currentRelation = currentRelation;
532  tidstate->ss.ss_currentScanDesc = NULL; /* no heap scan here */
533 
534  /*
535  * get the scan type from the relation descriptor.
536  */
537  ExecInitScanTupleSlot(estate, &tidstate->ss,
538  RelationGetDescr(currentRelation),
539  table_slot_callbacks(currentRelation));
540 
541  /*
542  * Initialize result type and projection.
543  */
544  ExecInitResultTypeTL(&tidstate->ss.ps);
545  ExecAssignScanProjectionInfo(&tidstate->ss);
546 
547  /*
548  * initialize child expressions
549  */
550  tidstate->ss.ps.qual =
551  ExecInitQual(node->scan.plan.qual, (PlanState *) tidstate);
552 
553  TidExprListCreate(tidstate);
554 
555  /*
556  * all done.
557  */
558  return tidstate;
559 }
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:576
static Datum ExecEvalExprSwitchContext(ExprState *state, ExprContext *econtext, bool *isNull)
Definition: executor.h:300
Index scanrelid
Definition: plannodes.h:345
static TupleTableSlot * TidNext(TidScanState *node)
Definition: nodeTidscan.c:311
static TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition: tuptable.h:425
#define RelationGetDescr(relation)
Definition: rel.h:448
#define castNode(_type_, nodeptr)
Definition: nodes.h:594
TupleTableSlot * ExecScan(ScanState *node, ExecScanAccessMtd accessMtd, ExecScanRecheckMtd recheckMtd)
Definition: execScan.c:158
ExprContext * ps_ExprContext
Definition: execnodes.h:978
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:525
struct TableScanDescData * ss_currentScanDesc
Definition: execnodes.h:1328
static bool TidRecheck(TidScanState *node, TupleTableSlot *slot)
Definition: nodeTidscan.c:402
Snapshot es_snapshot
Definition: execnodes.h:502
TupleTableSlot * ss_ScanTupleSlot
Definition: execnodes.h:1329
uint32 BlockNumber
Definition: block.h:31
Relation ss_currentRelation
Definition: execnodes.h:1327
EState * state
Definition: execnodes.h:941
static void TidListEval(TidScanState *tidstate)
Definition: nodeTidscan.c:130
static void table_rescan(TableScanDesc scan, struct ScanKeyData *key)
Definition: tableam.h:840
void ExecFreeExprContext(PlanState *planstate)
Definition: execUtils.c:614
#define lsecond(l)
Definition: pg_list.h:200
ScanDirection es_direction
Definition: execnodes.h:501
ExprState * ExecInitQual(List *qual, PlanState *parent)
Definition: execExpr.c:207
uint16 OffsetNumber
Definition: off.h:24
ItemPointerData * ItemPointer
Definition: itemptr.h:49
PlanState ps
Definition: execnodes.h:1326
#define ScanDirectionIsBackward(direction)
Definition: sdir.h:41
TupleTableSlot * ps_ResultTupleSlot
Definition: execnodes.h:977
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:499
#define ERROR
Definition: elog.h:43
ScanState ss
Definition: execnodes.h:1614
void ExecAssignScanProjectionInfo(ScanState *node)
Definition: execScan.c:272
static TableScanDesc table_beginscan(Relation rel, Snapshot snapshot, int nkeys, struct ScanKeyData *key)
Definition: tableam.h:736
Relation ExecOpenScanRelation(EState *estate, Index scanrelid, int eflags)
Definition: execUtils.c:693
List * tss_tidexprs
Definition: execnodes.h:1615
ItemPointerData * tss_TidList
Definition: execnodes.h:1619
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:1616
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:469
static bool table_tuple_fetch_row_version(Relation rel, ItemPointer tid, Snapshot snapshot, TupleTableSlot *slot)
Definition: tableam.h:1021
List * lappend(List *list, void *datum)
Definition: list.c:322
bool(* ExecScanRecheckMtd)(ScanState *node, TupleTableSlot *slot)
Definition: executor.h:426
static int itemptr_comparator(const void *a, const void *b)
Definition: nodeTidscan.c:282
static void TidExprListCreate(TidScanState *tidstate)
Definition: nodeTidscan.c:66
void * palloc0(Size size)
Definition: mcxt.c:980
ExecProcNodeMtd ExecProcNode
Definition: execnodes.h:945
uintptr_t Datum
Definition: postgres.h:367
static Node * get_rightop(const void *clause)
Definition: nodeFuncs.h:82
Plan * plan
Definition: execnodes.h:939
#define makeNode(_type_)
Definition: nodes.h:573
#define Assert(condition)
Definition: c.h:739
#define lfirst(lc)
Definition: pg_list.h:190
Scan scan
Definition: plannodes.h:492
void ExecAssignExprContext(EState *estate, PlanState *planstate)
Definition: execUtils.c:444
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:960
#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:3461
static void table_endscan(TableScanDesc scan)
Definition: tableam.h:831
static TupleTableSlot * ExecTidScan(PlanState *pstate)
Definition: nodeTidscan.c:432
void * palloc(Size size)
Definition: mcxt.c:949
#define elog(elevel,...)
Definition: elog.h:228
int i
void ExecScanReScan(ScanState *node)
Definition: execScan.c:299
static bool table_tuple_tid_valid(TableScanDesc scan, ItemPointer tid)
Definition: tableam.h:1038
ExprState * exprstate
Definition: nodeTidscan.c:47
struct TidExpr TidExpr
ExprState * ExecInitExpr(Expr *node, PlanState *parent)
Definition: execExpr.c:121
#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:488
TupleTableSlot *(* ExecScanAccessMtd)(ScanState *node)
Definition: executor.h:425
void ExecReScanTidScan(TidScanState *node)
Definition: nodeTidscan.c:446
#define RelationGetRelid(relation)
Definition: rel.h:422
#define DatumGetArrayTypeP(X)
Definition: array.h:249