PostgreSQL Source Code git master
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
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-2025, 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
38typedef enum
39{
43
44/* Upper or lower range bound for scan */
45typedef 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 */
56static 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 */
105static 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 */
136static 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 {
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 {
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 */
219static 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 */
272static 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 */
293static TupleTableSlot *
295{
297
298 return ExecScan(&node->ss,
301}
302
303/* ----------------------------------------------------------------
304 * ExecReScanTidRangeScan(node)
305 * ----------------------------------------------------------------
306 */
307void
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 */
326void
328{
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 */
347ExecInitTidRangeScan(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:558
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:226
ExprState * ExecInitExpr(Expr *node, PlanState *parent)
Definition: execExpr.c:143
ExprState * ExecInitQual(List *qual, PlanState *parent)
Definition: execExpr.c:229
TupleTableSlot * ExecScan(ScanState *node, ExecScanAccessMtd accessMtd, ExecScanRecheckMtd recheckMtd)
Definition: execScan.c:47
void ExecAssignScanProjectionInfo(ScanState *node)
Definition: execScan.c:81
void ExecScanReScan(ScanState *node)
Definition: execScan.c:108
void ExecInitScanTupleSlot(EState *estate, ScanState *scanstate, TupleDesc tupledesc, const TupleTableSlotOps *tts_ops)
Definition: execTuples.c:2000
void ExecInitResultTypeTL(PlanState *planstate)
Definition: execTuples.c:1944
void ExecAssignExprContext(EState *estate, PlanState *planstate)
Definition: execUtils.c:486
Relation ExecOpenScanRelation(EState *estate, Index scanrelid, int eflags)
Definition: execUtils.c:743
bool(* ExecScanRecheckMtd)(ScanState *node, TupleTableSlot *slot)
Definition: executor.h:602
TupleTableSlot *(* ExecScanAccessMtd)(ScanState *node)
Definition: executor.h:601
static Datum ExecEvalExprSwitchContext(ExprState *state, ExprContext *econtext, bool *isNull)
Definition: executor.h:458
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:1940
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)
void ExecReScanTidRangeScan(TidRangeScanState *node)
void ExecEndTidRangeScan(TidRangeScanState *node)
static bool TidRangeEval(TidRangeScanState *node)
static TidOpExpr * MakeTidOpExpr(OpExpr *expr, TidRangeScanState *tidstate)
TidRangeScanState * ExecInitTidRangeScan(TidRangeScan *node, EState *estate, int eflags)
static TupleTableSlot * TidRangeNext(TidRangeScanState *node)
#define IsCTIDVar(node)
struct TidOpExpr TidOpExpr
TidExprType
@ TIDEXPR_LOWER_BOUND
@ TIDEXPR_UPPER_BOUND
static TupleTableSlot * ExecTidRangeScan(PlanState *pstate)
static bool TidRangeRecheck(TidRangeScanState *node, TupleTableSlot *slot)
#define IsA(nodeptr, _type_)
Definition: nodes.h:164
#define makeNode(_type_)
Definition: nodes.h:161
#define castNode(_type_, nodeptr)
Definition: nodes.h:182
#define lfirst(lc)
Definition: pg_list.h:172
#define NIL
Definition: pg_list.h:68
static Pointer DatumGetPointer(Datum X)
Definition: postgres.h:317
#define RelationGetDescr(relation)
Definition: rel.h:542
ScanDirection
Definition: sdir.h:25
ScanDirection es_direction
Definition: execnodes.h:656
Snapshot es_snapshot
Definition: execnodes.h:657
Definition: pg_list.h:54
Definition: nodes.h:135
Oid opno
Definition: primnodes.h:835
ExprState * qual
Definition: execnodes.h:1180
Plan * plan
Definition: execnodes.h:1159
EState * state
Definition: execnodes.h:1161
ExprContext * ps_ExprContext
Definition: execnodes.h:1198
ExecProcNodeMtd ExecProcNode
Definition: execnodes.h:1165
Relation ss_currentRelation
Definition: execnodes.h:1616
TupleTableSlot * ss_ScanTupleSlot
Definition: execnodes.h:1618
PlanState ps
Definition: execnodes.h:1615
struct TableScanDescData * ss_currentScanDesc
Definition: execnodes.h:1617
Index scanrelid
Definition: plannodes.h:483
ExprState * exprstate
TidExprType exprtype
ItemPointerData trss_maxtid
Definition: execnodes.h:1934
List * trss_tidexprs
Definition: execnodes.h:1932
ItemPointerData trss_mintid
Definition: execnodes.h:1933
List * tidrangequals
Definition: plannodes.h:682
const TupleTableSlotOps * table_slot_callbacks(Relation relation)
Definition: tableam.c:59
static void table_rescan_tidrange(TableScanDesc sscan, ItemPointer mintid, ItemPointer maxtid)
Definition: tableam.h:1065
static void table_endscan(TableScanDesc scan)
Definition: tableam.h:979
static TableScanDesc table_beginscan_tidrange(Relation rel, Snapshot snapshot, ItemPointer mintid, ItemPointer maxtid)
Definition: tableam.h:1044
static bool table_scan_getnextslot_tidrange(TableScanDesc sscan, ScanDirection direction, TupleTableSlot *slot)
Definition: tableam.h:1081
static TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition: tuptable.h:458