PostgreSQL Source Code  git master
nodeTidrangescan.c
Go to the documentation of this file.
1 /*-------------------------------------------------------------------------
2  *
3  * nodeTidrangescan.c
4  * Routines to support TID range scans of relations
5  *
6  * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  *
10  * IDENTIFICATION
11  * src/backend/executor/nodeTidrangescan.c
12  *
13  *-------------------------------------------------------------------------
14  */
15 #include "postgres.h"
16 
17 #include "access/relscan.h"
18 #include "access/sysattr.h"
19 #include "access/tableam.h"
20 #include "catalog/pg_operator.h"
21 #include "executor/execdebug.h"
23 #include "nodes/nodeFuncs.h"
24 #include "storage/bufmgr.h"
25 #include "utils/rel.h"
26 
27 
28 #define IsCTIDVar(node) \
29  ((node) != NULL && \
30  IsA((node), Var) && \
31  ((Var *) (node))->varattno == SelfItemPointerAttributeNumber && \
32  ((Var *) (node))->varlevelsup == 0)
33 
34 typedef enum
35 {
38 } TidExprType;
39 
40 /* Upper or lower range bound for scan */
41 typedef struct TidOpExpr
42 {
43  TidExprType exprtype; /* type of op; lower or upper */
44  ExprState *exprstate; /* ExprState for a TID-yielding subexpr */
45  bool inclusive; /* whether op is inclusive */
46 } TidOpExpr;
47 
48 /*
49  * For the given 'expr', build and return an appropriate TidOpExpr taking into
50  * account the expr's operator and operand order.
51  */
52 static TidOpExpr *
54 {
55  Node *arg1 = get_leftop((Expr *) expr);
56  Node *arg2 = get_rightop((Expr *) expr);
57  ExprState *exprstate = NULL;
58  bool invert = false;
59  TidOpExpr *tidopexpr;
60 
61  if (IsCTIDVar(arg1))
62  exprstate = ExecInitExpr((Expr *) arg2, &tidstate->ss.ps);
63  else if (IsCTIDVar(arg2))
64  {
65  exprstate = ExecInitExpr((Expr *) arg1, &tidstate->ss.ps);
66  invert = true;
67  }
68  else
69  elog(ERROR, "could not identify CTID variable");
70 
71  tidopexpr = (TidOpExpr *) palloc(sizeof(TidOpExpr));
72  tidopexpr->inclusive = false; /* for now */
73 
74  switch (expr->opno)
75  {
76  case TIDLessEqOperator:
77  tidopexpr->inclusive = true;
78  /* fall through */
79  case TIDLessOperator:
80  tidopexpr->exprtype = invert ? TIDEXPR_LOWER_BOUND : TIDEXPR_UPPER_BOUND;
81  break;
82  case TIDGreaterEqOperator:
83  tidopexpr->inclusive = true;
84  /* fall through */
85  case TIDGreaterOperator:
86  tidopexpr->exprtype = invert ? TIDEXPR_UPPER_BOUND : TIDEXPR_LOWER_BOUND;
87  break;
88  default:
89  elog(ERROR, "could not identify CTID operator");
90  }
91 
92  tidopexpr->exprstate = exprstate;
93 
94  return tidopexpr;
95 }
96 
97 /*
98  * Extract the qual subexpressions that yield TIDs to search for,
99  * and compile them into ExprStates if they're ordinary expressions.
100  */
101 static void
103 {
104  TidRangeScan *node = (TidRangeScan *) tidrangestate->ss.ps.plan;
105  List *tidexprs = NIL;
106  ListCell *l;
107 
108  foreach(l, node->tidrangequals)
109  {
110  OpExpr *opexpr = lfirst(l);
111  TidOpExpr *tidopexpr;
112 
113  if (!IsA(opexpr, OpExpr))
114  elog(ERROR, "could not identify CTID expression");
115 
116  tidopexpr = MakeTidOpExpr(opexpr, tidrangestate);
117  tidexprs = lappend(tidexprs, tidopexpr);
118  }
119 
120  tidrangestate->trss_tidexprs = tidexprs;
121 }
122 
123 /* ----------------------------------------------------------------
124  * TidRangeEval
125  *
126  * Compute and set node's block and offset range to scan by evaluating
127  * the trss_tidexprs. Returns false if we detect the range cannot
128  * contain any tuples. Returns true if it's possible for the range to
129  * contain tuples.
130  * ----------------------------------------------------------------
131  */
132 static bool
134 {
135  ExprContext *econtext = node->ss.ps.ps_ExprContext;
136  ItemPointerData lowerBound;
137  ItemPointerData upperBound;
138  ListCell *l;
139 
140  /*
141  * Set the upper and lower bounds to the absolute limits of the range of
142  * the ItemPointer type. Below we'll try to narrow this range on either
143  * side by looking at the TidOpExprs.
144  */
145  ItemPointerSet(&lowerBound, 0, 0);
147 
148  foreach(l, node->trss_tidexprs)
149  {
150  TidOpExpr *tidopexpr = (TidOpExpr *) lfirst(l);
151  ItemPointer itemptr;
152  bool isNull;
153 
154  /* Evaluate this bound. */
155  itemptr = (ItemPointer)
157  econtext,
158  &isNull));
159 
160  /* If the bound is NULL, *nothing* matches the qual. */
161  if (isNull)
162  return false;
163 
164  if (tidopexpr->exprtype == TIDEXPR_LOWER_BOUND)
165  {
166  ItemPointerData lb;
167 
168  ItemPointerCopy(itemptr, &lb);
169 
170  /*
171  * Normalize non-inclusive ranges to become inclusive. The
172  * resulting ItemPointer here may not be a valid item pointer.
173  */
174  if (!tidopexpr->inclusive)
175  ItemPointerInc(&lb);
176 
177  /* Check if we can narrow the range using this qual */
178  if (ItemPointerCompare(&lb, &lowerBound) > 0)
179  ItemPointerCopy(&lb, &lowerBound);
180  }
181 
182  else if (tidopexpr->exprtype == TIDEXPR_UPPER_BOUND)
183  {
184  ItemPointerData ub;
185 
186  ItemPointerCopy(itemptr, &ub);
187 
188  /*
189  * Normalize non-inclusive ranges to become inclusive. The
190  * resulting ItemPointer here may not be a valid item pointer.
191  */
192  if (!tidopexpr->inclusive)
193  ItemPointerDec(&ub);
194 
195  /* Check if we can narrow the range using this qual */
196  if (ItemPointerCompare(&ub, &upperBound) < 0)
197  ItemPointerCopy(&ub, &upperBound);
198  }
199  }
200 
201  ItemPointerCopy(&lowerBound, &node->trss_mintid);
202  ItemPointerCopy(&upperBound, &node->trss_maxtid);
203 
204  return true;
205 }
206 
207 /* ----------------------------------------------------------------
208  * TidRangeNext
209  *
210  * Retrieve a tuple from the TidRangeScan node's currentRelation
211  * using the TIDs in the TidRangeScanState information.
212  *
213  * ----------------------------------------------------------------
214  */
215 static TupleTableSlot *
217 {
218  TableScanDesc scandesc;
219  EState *estate;
220  ScanDirection direction;
221  TupleTableSlot *slot;
222 
223  /*
224  * extract necessary information from TID scan node
225  */
226  scandesc = node->ss.ss_currentScanDesc;
227  estate = node->ss.ps.state;
228  slot = node->ss.ss_ScanTupleSlot;
229  direction = estate->es_direction;
230 
231  if (!node->trss_inScan)
232  {
233  /* First time through, compute TID range to scan */
234  if (!TidRangeEval(node))
235  return NULL;
236 
237  if (scandesc == NULL)
238  {
240  estate->es_snapshot,
241  &node->trss_mintid,
242  &node->trss_maxtid);
243  node->ss.ss_currentScanDesc = scandesc;
244  }
245  else
246  {
247  /* rescan with the updated TID range */
248  table_rescan_tidrange(scandesc, &node->trss_mintid,
249  &node->trss_maxtid);
250  }
251 
252  node->trss_inScan = true;
253  }
254 
255  /* Fetch the next tuple. */
256  if (!table_scan_getnextslot_tidrange(scandesc, direction, slot))
257  {
258  node->trss_inScan = false;
259  ExecClearTuple(slot);
260  }
261 
262  return slot;
263 }
264 
265 /*
266  * TidRangeRecheck -- access method routine to recheck a tuple in EvalPlanQual
267  */
268 static bool
270 {
271  return true;
272 }
273 
274 /* ----------------------------------------------------------------
275  * ExecTidRangeScan(node)
276  *
277  * Scans the relation using tids and returns the next qualifying tuple.
278  * We call the ExecScan() routine and pass it the appropriate
279  * access method functions.
280  *
281  * Conditions:
282  * -- the "cursor" maintained by the AMI is positioned at the tuple
283  * returned previously.
284  *
285  * Initial States:
286  * -- the relation indicated is opened for TID range scanning.
287  * ----------------------------------------------------------------
288  */
289 static TupleTableSlot *
291 {
293 
294  return ExecScan(&node->ss,
297 }
298 
299 /* ----------------------------------------------------------------
300  * ExecReScanTidRangeScan(node)
301  * ----------------------------------------------------------------
302  */
303 void
305 {
306  /* mark scan as not in progress, and tid range list as not computed yet */
307  node->trss_inScan = false;
308 
309  /*
310  * We must wait until TidRangeNext before calling table_rescan_tidrange.
311  */
312  ExecScanReScan(&node->ss);
313 }
314 
315 /* ----------------------------------------------------------------
316  * ExecEndTidRangeScan
317  *
318  * Releases any storage allocated through C routines.
319  * Returns nothing.
320  * ----------------------------------------------------------------
321  */
322 void
324 {
325  TableScanDesc scan = node->ss.ss_currentScanDesc;
326 
327  if (scan != NULL)
328  table_endscan(scan);
329 
330  /*
331  * Free the exprcontext
332  */
333  ExecFreeExprContext(&node->ss.ps);
334 
335  /*
336  * clear out tuple table slots
337  */
338  if (node->ss.ps.ps_ResultTupleSlot)
341 }
342 
343 /* ----------------------------------------------------------------
344  * ExecInitTidRangeScan
345  *
346  * Initializes the tid range scan's state information, creates
347  * scan keys, and opens the scan relation.
348  *
349  * Parameters:
350  * node: TidRangeScan node produced by the planner.
351  * estate: the execution state initialized in InitPlan.
352  * ----------------------------------------------------------------
353  */
355 ExecInitTidRangeScan(TidRangeScan *node, EState *estate, int eflags)
356 {
357  TidRangeScanState *tidrangestate;
358  Relation currentRelation;
359 
360  /*
361  * create state structure
362  */
363  tidrangestate = makeNode(TidRangeScanState);
364  tidrangestate->ss.ps.plan = (Plan *) node;
365  tidrangestate->ss.ps.state = estate;
366  tidrangestate->ss.ps.ExecProcNode = ExecTidRangeScan;
367 
368  /*
369  * Miscellaneous initialization
370  *
371  * create expression context for node
372  */
373  ExecAssignExprContext(estate, &tidrangestate->ss.ps);
374 
375  /*
376  * mark scan as not in progress, and TID range as not computed yet
377  */
378  tidrangestate->trss_inScan = false;
379 
380  /*
381  * open the scan relation
382  */
383  currentRelation = ExecOpenScanRelation(estate, node->scan.scanrelid, eflags);
384 
385  tidrangestate->ss.ss_currentRelation = currentRelation;
386  tidrangestate->ss.ss_currentScanDesc = NULL; /* no table scan here */
387 
388  /*
389  * get the scan type from the relation descriptor.
390  */
391  ExecInitScanTupleSlot(estate, &tidrangestate->ss,
392  RelationGetDescr(currentRelation),
393  table_slot_callbacks(currentRelation));
394 
395  /*
396  * Initialize result type and projection.
397  */
398  ExecInitResultTypeTL(&tidrangestate->ss.ps);
399  ExecAssignScanProjectionInfo(&tidrangestate->ss);
400 
401  /*
402  * initialize child expressions
403  */
404  tidrangestate->ss.ps.qual =
405  ExecInitQual(node->scan.plan.qual, (PlanState *) tidrangestate);
406 
407  TidExprListCreate(tidrangestate);
408 
409  /*
410  * all done.
411  */
412  return tidrangestate;
413 }
List * tidrangequals
Definition: plannodes.h:508
int32 ItemPointerCompare(ItemPointer arg1, ItemPointer arg2)
Definition: itemptr.c:52
static bool table_scan_getnextslot_tidrange(TableScanDesc sscan, ScanDirection direction, TupleTableSlot *slot)
Definition: tableam.h:1094
#define NIL
Definition: pg_list.h:65
List * trss_tidexprs
Definition: execnodes.h:1686
List * qual
Definition: plannodes.h:142
Plan plan
Definition: plannodes.h:343
#define IsA(nodeptr, _type_)
Definition: nodes.h:587
static Datum ExecEvalExprSwitchContext(ExprState *state, ExprContext *econtext, bool *isNull)
Definition: executor.h:331
Index scanrelid
Definition: plannodes.h:344
static TableScanDesc table_beginscan_tidrange(Relation rel, Snapshot snapshot, ItemPointer mintid, ItemPointer maxtid)
Definition: tableam.h:1057
static TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition: tuptable.h:425
#define RelationGetDescr(relation)
Definition: rel.h:503
#define castNode(_type_, nodeptr)
Definition: nodes.h:605
ExprState * exprstate
TupleTableSlot * ExecScan(ScanState *node, ExecScanAccessMtd accessMtd, ExecScanRecheckMtd recheckMtd)
Definition: execScan.c:158
ExprContext * ps_ExprContext
Definition: execnodes.h:1005
const TupleTableSlotOps * table_slot_callbacks(Relation relation)
Definition: tableam.c:58
#define IsCTIDVar(node)
ItemPointerData trss_maxtid
Definition: execnodes.h:1688
Definition: nodes.h:536
struct TableScanDescData * ss_currentScanDesc
Definition: execnodes.h:1379
Snapshot es_snapshot
Definition: execnodes.h:558
TupleTableSlot * ss_ScanTupleSlot
Definition: execnodes.h:1380
Relation ss_currentRelation
Definition: execnodes.h:1378
EState * state
Definition: execnodes.h:968
void ExecFreeExprContext(PlanState *planstate)
Definition: execUtils.c:650
ScanDirection es_direction
Definition: execnodes.h:557
ExprState * ExecInitQual(List *qual, PlanState *parent)
Definition: execExpr.c:209
ItemPointerData * ItemPointer
Definition: itemptr.h:49
static TidOpExpr * MakeTidOpExpr(OpExpr *expr, TidRangeScanState *tidstate)
PlanState ps
Definition: execnodes.h:1377
static void table_rescan_tidrange(TableScanDesc sscan, ItemPointer mintid, ItemPointer maxtid)
Definition: tableam.h:1078
ItemPointerData trss_mintid
Definition: execnodes.h:1687
TupleTableSlot * ps_ResultTupleSlot
Definition: execnodes.h:1004
#define ERROR
Definition: elog.h:46
#define PG_UINT16_MAX
Definition: c.h:522
void ExecAssignScanProjectionInfo(ScanState *node)
Definition: execScan.c:272
Relation ExecOpenScanRelation(EState *estate, Index scanrelid, int eflags)
Definition: execUtils.c:720
void ExecInitScanTupleSlot(EState *estate, ScanState *scanstate, TupleDesc tupledesc, const TupleTableSlotOps *tts_ops)
Definition: execTuples.c:1811
void ExecInitResultTypeTL(PlanState *planstate)
Definition: execTuples.c:1755
ScanDirection
Definition: sdir.h:22
void ExecReScanTidRangeScan(TidRangeScanState *node)
TidExprType exprtype
static Node * get_leftop(const void *clause)
Definition: nodeFuncs.h:73
static TupleTableSlot * ExecTidRangeScan(PlanState *pstate)
List * lappend(List *list, void *datum)
Definition: list.c:336
bool(* ExecScanRecheckMtd)(ScanState *node, TupleTableSlot *slot)
Definition: executor.h:457
void ItemPointerDec(ItemPointer pointer)
Definition: itemptr.c:115
TidRangeScanState * ExecInitTidRangeScan(TidRangeScan *node, EState *estate, int eflags)
ExecProcNodeMtd ExecProcNode
Definition: execnodes.h:972
void ItemPointerInc(ItemPointer pointer)
Definition: itemptr.c:85
static Node * get_rightop(const void *clause)
Definition: nodeFuncs.h:85
Plan * plan
Definition: execnodes.h:966
#define makeNode(_type_)
Definition: nodes.h:584
#define lfirst(lc)
Definition: pg_list.h:169
static TupleTableSlot * TidRangeNext(TidRangeScanState *node)
void ExecAssignExprContext(EState *estate, PlanState *planstate)
Definition: execUtils.c:480
#define InvalidBlockNumber
Definition: block.h:33
static bool TidRangeRecheck(TidRangeScanState *node, TupleTableSlot *slot)
ExprState * qual
Definition: execnodes.h:987
#define DatumGetPointer(X)
Definition: postgres.h:593
static void table_endscan(TableScanDesc scan)
Definition: tableam.h:991
TidExprType
void * palloc(Size size)
Definition: mcxt.c:1062
struct TidOpExpr TidOpExpr
static void TidExprListCreate(TidRangeScanState *tidrangestate)
#define elog(elevel,...)
Definition: elog.h:232
void ExecScanReScan(ScanState *node)
Definition: execScan.c:299
ExprState * ExecInitExpr(Expr *node, PlanState *parent)
Definition: execExpr.c:123
static bool TidRangeEval(TidRangeScanState *node)
TupleTableSlot *(* ExecScanAccessMtd)(ScanState *node)
Definition: executor.h:456
void ExecEndTidRangeScan(TidRangeScanState *node)
Definition: pg_list.h:50
#define ItemPointerSet(pointer, blockNumber, offNum)
Definition: itemptr.h:127
#define ItemPointerCopy(fromPointer, toPointer)
Definition: itemptr.h:161