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