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-2024, 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/executor.h"
23 #include "nodes/nodeFuncs.h"
24 #include "utils/rel.h"
25 
26 
27 /*
28  * It's sufficient to check varattno to identify the CTID variable, as any
29  * Var in the relation scan qual must be for our table. (Even if it's a
30  * parameterized scan referencing some other table's CTID, the other table's
31  * Var would have become a Param by the time it gets here.)
32  */
33 #define IsCTIDVar(node) \
34  ((node) != NULL && \
35  IsA((node), Var) && \
36  ((Var *) (node))->varattno == SelfItemPointerAttributeNumber)
37 
38 typedef enum
39 {
42 } TidExprType;
43 
44 /* Upper or lower range bound for scan */
45 typedef struct TidOpExpr
46 {
47  TidExprType exprtype; /* type of op; lower or upper */
48  ExprState *exprstate; /* ExprState for a TID-yielding subexpr */
49  bool inclusive; /* whether op is inclusive */
51 
52 /*
53  * For the given 'expr', build and return an appropriate TidOpExpr taking into
54  * account the expr's operator and operand order.
55  */
56 static TidOpExpr *
58 {
59  Node *arg1 = get_leftop((Expr *) expr);
60  Node *arg2 = get_rightop((Expr *) expr);
61  ExprState *exprstate = NULL;
62  bool invert = false;
63  TidOpExpr *tidopexpr;
64 
65  if (IsCTIDVar(arg1))
66  exprstate = ExecInitExpr((Expr *) arg2, &tidstate->ss.ps);
67  else if (IsCTIDVar(arg2))
68  {
69  exprstate = ExecInitExpr((Expr *) arg1, &tidstate->ss.ps);
70  invert = true;
71  }
72  else
73  elog(ERROR, "could not identify CTID variable");
74 
75  tidopexpr = (TidOpExpr *) palloc(sizeof(TidOpExpr));
76  tidopexpr->inclusive = false; /* for now */
77 
78  switch (expr->opno)
79  {
80  case TIDLessEqOperator:
81  tidopexpr->inclusive = true;
82  /* fall through */
83  case TIDLessOperator:
84  tidopexpr->exprtype = invert ? TIDEXPR_LOWER_BOUND : TIDEXPR_UPPER_BOUND;
85  break;
86  case TIDGreaterEqOperator:
87  tidopexpr->inclusive = true;
88  /* fall through */
89  case TIDGreaterOperator:
90  tidopexpr->exprtype = invert ? TIDEXPR_UPPER_BOUND : TIDEXPR_LOWER_BOUND;
91  break;
92  default:
93  elog(ERROR, "could not identify CTID operator");
94  }
95 
96  tidopexpr->exprstate = exprstate;
97 
98  return tidopexpr;
99 }
100 
101 /*
102  * Extract the qual subexpressions that yield TIDs to search for,
103  * and compile them into ExprStates if they're ordinary expressions.
104  */
105 static void
107 {
108  TidRangeScan *node = (TidRangeScan *) tidrangestate->ss.ps.plan;
109  List *tidexprs = NIL;
110  ListCell *l;
111 
112  foreach(l, node->tidrangequals)
113  {
114  OpExpr *opexpr = lfirst(l);
115  TidOpExpr *tidopexpr;
116 
117  if (!IsA(opexpr, OpExpr))
118  elog(ERROR, "could not identify CTID expression");
119 
120  tidopexpr = MakeTidOpExpr(opexpr, tidrangestate);
121  tidexprs = lappend(tidexprs, tidopexpr);
122  }
123 
124  tidrangestate->trss_tidexprs = tidexprs;
125 }
126 
127 /* ----------------------------------------------------------------
128  * TidRangeEval
129  *
130  * Compute and set node's block and offset range to scan by evaluating
131  * the trss_tidexprs. Returns false if we detect the range cannot
132  * contain any tuples. Returns true if it's possible for the range to
133  * contain tuples.
134  * ----------------------------------------------------------------
135  */
136 static bool
138 {
139  ExprContext *econtext = node->ss.ps.ps_ExprContext;
140  ItemPointerData lowerBound;
141  ItemPointerData upperBound;
142  ListCell *l;
143 
144  /*
145  * Set the upper and lower bounds to the absolute limits of the range of
146  * the ItemPointer type. Below we'll try to narrow this range on either
147  * side by looking at the TidOpExprs.
148  */
149  ItemPointerSet(&lowerBound, 0, 0);
151 
152  foreach(l, node->trss_tidexprs)
153  {
154  TidOpExpr *tidopexpr = (TidOpExpr *) lfirst(l);
155  ItemPointer itemptr;
156  bool isNull;
157 
158  /* Evaluate this bound. */
159  itemptr = (ItemPointer)
161  econtext,
162  &isNull));
163 
164  /* If the bound is NULL, *nothing* matches the qual. */
165  if (isNull)
166  return false;
167 
168  if (tidopexpr->exprtype == TIDEXPR_LOWER_BOUND)
169  {
170  ItemPointerData lb;
171 
172  ItemPointerCopy(itemptr, &lb);
173 
174  /*
175  * Normalize non-inclusive ranges to become inclusive. The
176  * resulting ItemPointer here may not be a valid item pointer.
177  */
178  if (!tidopexpr->inclusive)
179  ItemPointerInc(&lb);
180 
181  /* Check if we can narrow the range using this qual */
182  if (ItemPointerCompare(&lb, &lowerBound) > 0)
183  ItemPointerCopy(&lb, &lowerBound);
184  }
185 
186  else if (tidopexpr->exprtype == TIDEXPR_UPPER_BOUND)
187  {
188  ItemPointerData ub;
189 
190  ItemPointerCopy(itemptr, &ub);
191 
192  /*
193  * Normalize non-inclusive ranges to become inclusive. The
194  * resulting ItemPointer here may not be a valid item pointer.
195  */
196  if (!tidopexpr->inclusive)
197  ItemPointerDec(&ub);
198 
199  /* Check if we can narrow the range using this qual */
200  if (ItemPointerCompare(&ub, &upperBound) < 0)
201  ItemPointerCopy(&ub, &upperBound);
202  }
203  }
204 
205  ItemPointerCopy(&lowerBound, &node->trss_mintid);
206  ItemPointerCopy(&upperBound, &node->trss_maxtid);
207 
208  return true;
209 }
210 
211 /* ----------------------------------------------------------------
212  * TidRangeNext
213  *
214  * Retrieve a tuple from the TidRangeScan node's currentRelation
215  * using the TIDs in the TidRangeScanState information.
216  *
217  * ----------------------------------------------------------------
218  */
219 static TupleTableSlot *
221 {
222  TableScanDesc scandesc;
223  EState *estate;
224  ScanDirection direction;
225  TupleTableSlot *slot;
226 
227  /*
228  * extract necessary information from TID scan node
229  */
230  scandesc = node->ss.ss_currentScanDesc;
231  estate = node->ss.ps.state;
232  slot = node->ss.ss_ScanTupleSlot;
233  direction = estate->es_direction;
234 
235  if (!node->trss_inScan)
236  {
237  /* First time through, compute TID range to scan */
238  if (!TidRangeEval(node))
239  return NULL;
240 
241  if (scandesc == NULL)
242  {
244  estate->es_snapshot,
245  &node->trss_mintid,
246  &node->trss_maxtid);
247  node->ss.ss_currentScanDesc = scandesc;
248  }
249  else
250  {
251  /* rescan with the updated TID range */
252  table_rescan_tidrange(scandesc, &node->trss_mintid,
253  &node->trss_maxtid);
254  }
255 
256  node->trss_inScan = true;
257  }
258 
259  /* Fetch the next tuple. */
260  if (!table_scan_getnextslot_tidrange(scandesc, direction, slot))
261  {
262  node->trss_inScan = false;
263  ExecClearTuple(slot);
264  }
265 
266  return slot;
267 }
268 
269 /*
270  * TidRangeRecheck -- access method routine to recheck a tuple in EvalPlanQual
271  */
272 static bool
274 {
275  return true;
276 }
277 
278 /* ----------------------------------------------------------------
279  * ExecTidRangeScan(node)
280  *
281  * Scans the relation using tids and returns the next qualifying tuple.
282  * We call the ExecScan() routine and pass it the appropriate
283  * access method functions.
284  *
285  * Conditions:
286  * -- the "cursor" maintained by the AMI is positioned at the tuple
287  * returned previously.
288  *
289  * Initial States:
290  * -- the relation indicated is opened for TID range scanning.
291  * ----------------------------------------------------------------
292  */
293 static TupleTableSlot *
295 {
297 
298  return ExecScan(&node->ss,
301 }
302 
303 /* ----------------------------------------------------------------
304  * ExecReScanTidRangeScan(node)
305  * ----------------------------------------------------------------
306  */
307 void
309 {
310  /* mark scan as not in progress, and tid range list as not computed yet */
311  node->trss_inScan = false;
312 
313  /*
314  * We must wait until TidRangeNext before calling table_rescan_tidrange.
315  */
316  ExecScanReScan(&node->ss);
317 }
318 
319 /* ----------------------------------------------------------------
320  * ExecEndTidRangeScan
321  *
322  * Releases any storage allocated through C routines.
323  * Returns nothing.
324  * ----------------------------------------------------------------
325  */
326 void
328 {
329  TableScanDesc scan = node->ss.ss_currentScanDesc;
330 
331  if (scan != NULL)
332  table_endscan(scan);
333 }
334 
335 /* ----------------------------------------------------------------
336  * ExecInitTidRangeScan
337  *
338  * Initializes the tid range scan's state information, creates
339  * scan keys, and opens the scan relation.
340  *
341  * Parameters:
342  * node: TidRangeScan node produced by the planner.
343  * estate: the execution state initialized in InitPlan.
344  * ----------------------------------------------------------------
345  */
347 ExecInitTidRangeScan(TidRangeScan *node, EState *estate, int eflags)
348 {
349  TidRangeScanState *tidrangestate;
350  Relation currentRelation;
351 
352  /*
353  * create state structure
354  */
355  tidrangestate = makeNode(TidRangeScanState);
356  tidrangestate->ss.ps.plan = (Plan *) node;
357  tidrangestate->ss.ps.state = estate;
358  tidrangestate->ss.ps.ExecProcNode = ExecTidRangeScan;
359 
360  /*
361  * Miscellaneous initialization
362  *
363  * create expression context for node
364  */
365  ExecAssignExprContext(estate, &tidrangestate->ss.ps);
366 
367  /*
368  * mark scan as not in progress, and TID range as not computed yet
369  */
370  tidrangestate->trss_inScan = false;
371 
372  /*
373  * open the scan relation
374  */
375  currentRelation = ExecOpenScanRelation(estate, node->scan.scanrelid, eflags);
376 
377  tidrangestate->ss.ss_currentRelation = currentRelation;
378  tidrangestate->ss.ss_currentScanDesc = NULL; /* no table scan here */
379 
380  /*
381  * get the scan type from the relation descriptor.
382  */
383  ExecInitScanTupleSlot(estate, &tidrangestate->ss,
384  RelationGetDescr(currentRelation),
385  table_slot_callbacks(currentRelation));
386 
387  /*
388  * Initialize result type and projection.
389  */
390  ExecInitResultTypeTL(&tidrangestate->ss.ps);
391  ExecAssignScanProjectionInfo(&tidrangestate->ss);
392 
393  /*
394  * initialize child expressions
395  */
396  tidrangestate->ss.ps.qual =
397  ExecInitQual(node->scan.plan.qual, (PlanState *) tidrangestate);
398 
399  TidExprListCreate(tidrangestate);
400 
401  /*
402  * all done.
403  */
404  return tidrangestate;
405 }
#define InvalidBlockNumber
Definition: block.h:33
#define PG_UINT16_MAX
Definition: c.h:541
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:225
ExprState * ExecInitQual(List *qual, PlanState *parent)
Definition: execExpr.c:224
ExprState * ExecInitExpr(Expr *node, PlanState *parent)
Definition: execExpr.c:138
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:485
Relation ExecOpenScanRelation(EState *estate, Index scanrelid, int eflags)
Definition: execUtils.c:699
TupleTableSlot *(* ExecScanAccessMtd)(ScanState *node)
Definition: executor.h:484
bool(* ExecScanRecheckMtd)(ScanState *node, TupleTableSlot *slot)
Definition: executor.h:485
static Datum ExecEvalExprSwitchContext(ExprState *state, ExprContext *econtext, bool *isNull)
Definition: executor.h:359
void ItemPointerDec(ItemPointer pointer)
Definition: itemptr.c:114
void ItemPointerInc(ItemPointer pointer)
Definition: itemptr.c:84
int32 ItemPointerCompare(ItemPointer arg1, ItemPointer arg2)
Definition: itemptr.c:51
static void ItemPointerSet(ItemPointerData *pointer, BlockNumber blockNumber, OffsetNumber offNum)
Definition: itemptr.h:135
ItemPointerData * ItemPointer
Definition: itemptr.h:49
static void ItemPointerCopy(const ItemPointerData *fromPointer, ItemPointerData *toPointer)
Definition: itemptr.h:172
List * lappend(List *list, void *datum)
Definition: list.c:339
void * palloc(Size size)
Definition: mcxt.c:1317
static Node * get_rightop(const void *clause)
Definition: nodeFuncs.h:95
static Node * get_leftop(const void *clause)
Definition: nodeFuncs.h:83
static void TidExprListCreate(TidRangeScanState *tidrangestate)
TidRangeScanState * ExecInitTidRangeScan(TidRangeScan *node, EState *estate, int eflags)
void ExecReScanTidRangeScan(TidRangeScanState *node)
void ExecEndTidRangeScan(TidRangeScanState *node)
static bool TidRangeEval(TidRangeScanState *node)
static TupleTableSlot * ExecTidRangeScan(PlanState *pstate)
static TupleTableSlot * TidRangeNext(TidRangeScanState *node)
static TidOpExpr * MakeTidOpExpr(OpExpr *expr, TidRangeScanState *tidstate)
#define IsCTIDVar(node)
struct TidOpExpr TidOpExpr
TidExprType
@ TIDEXPR_LOWER_BOUND
@ TIDEXPR_UPPER_BOUND
static bool TidRangeRecheck(TidRangeScanState *node, TupleTableSlot *slot)
#define IsA(nodeptr, _type_)
Definition: nodes.h:158
#define makeNode(_type_)
Definition: nodes.h:155
#define castNode(_type_, nodeptr)
Definition: nodes.h:176
#define lfirst(lc)
Definition: pg_list.h:172
#define NIL
Definition: pg_list.h:68
static Pointer DatumGetPointer(Datum X)
Definition: postgres.h:312
#define RelationGetDescr(relation)
Definition: rel.h:531
ScanDirection
Definition: sdir.h:25
ScanDirection es_direction
Definition: execnodes.h:631
Snapshot es_snapshot
Definition: execnodes.h:632
Definition: pg_list.h:54
Definition: nodes.h:129
Oid opno
Definition: primnodes.h:818
ExprState * qual
Definition: execnodes.h:1148
Plan * plan
Definition: execnodes.h:1127
EState * state
Definition: execnodes.h:1129
ExprContext * ps_ExprContext
Definition: execnodes.h:1166
ExecProcNodeMtd ExecProcNode
Definition: execnodes.h:1133
Relation ss_currentRelation
Definition: execnodes.h:1575
TupleTableSlot * ss_ScanTupleSlot
Definition: execnodes.h:1577
PlanState ps
Definition: execnodes.h:1574
struct TableScanDescData * ss_currentScanDesc
Definition: execnodes.h:1576
Index scanrelid
Definition: plannodes.h:390
ExprState * exprstate
TidExprType exprtype
ItemPointerData trss_maxtid
Definition: execnodes.h:1904
List * trss_tidexprs
Definition: execnodes.h:1902
ItemPointerData trss_mintid
Definition: execnodes.h:1903
List * tidrangequals
Definition: plannodes.h:569
const TupleTableSlotOps * table_slot_callbacks(Relation relation)
Definition: tableam.c:58
static void table_rescan_tidrange(TableScanDesc sscan, ItemPointer mintid, ItemPointer maxtid)
Definition: tableam.h:1114
static void table_endscan(TableScanDesc scan)
Definition: tableam.h:1028
static TableScanDesc table_beginscan_tidrange(Relation rel, Snapshot snapshot, ItemPointer mintid, ItemPointer maxtid)
Definition: tableam.h:1093
static bool table_scan_getnextslot_tidrange(TableScanDesc sscan, ScanDirection direction, TupleTableSlot *slot)
Definition: tableam.h:1130
static TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition: tuptable.h:454