Line data Source code
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"
22 : #include "executor/nodeTidrangescan.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 : {
40 : TIDEXPR_UPPER_BOUND,
41 : TIDEXPR_LOWER_BOUND,
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 */
50 : } TidOpExpr;
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 *
57 232 : MakeTidOpExpr(OpExpr *expr, TidRangeScanState *tidstate)
58 : {
59 232 : Node *arg1 = get_leftop((Expr *) expr);
60 232 : Node *arg2 = get_rightop((Expr *) expr);
61 232 : ExprState *exprstate = NULL;
62 232 : bool invert = false;
63 : TidOpExpr *tidopexpr;
64 :
65 232 : if (IsCTIDVar(arg1))
66 196 : exprstate = ExecInitExpr((Expr *) arg2, &tidstate->ss.ps);
67 36 : else if (IsCTIDVar(arg2))
68 : {
69 36 : exprstate = ExecInitExpr((Expr *) arg1, &tidstate->ss.ps);
70 36 : invert = true;
71 : }
72 : else
73 0 : elog(ERROR, "could not identify CTID variable");
74 :
75 232 : tidopexpr = (TidOpExpr *) palloc(sizeof(TidOpExpr));
76 232 : tidopexpr->inclusive = false; /* for now */
77 :
78 232 : switch (expr->opno)
79 : {
80 24 : case TIDLessEqOperator:
81 24 : tidopexpr->inclusive = true;
82 : /* fall through */
83 114 : case TIDLessOperator:
84 114 : tidopexpr->exprtype = invert ? TIDEXPR_LOWER_BOUND : TIDEXPR_UPPER_BOUND;
85 114 : break;
86 54 : case TIDGreaterEqOperator:
87 54 : tidopexpr->inclusive = true;
88 : /* fall through */
89 118 : case TIDGreaterOperator:
90 118 : tidopexpr->exprtype = invert ? TIDEXPR_UPPER_BOUND : TIDEXPR_LOWER_BOUND;
91 118 : break;
92 0 : default:
93 0 : elog(ERROR, "could not identify CTID operator");
94 : }
95 :
96 232 : tidopexpr->exprstate = exprstate;
97 :
98 232 : 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
106 202 : TidExprListCreate(TidRangeScanState *tidrangestate)
107 : {
108 202 : TidRangeScan *node = (TidRangeScan *) tidrangestate->ss.ps.plan;
109 202 : List *tidexprs = NIL;
110 : ListCell *l;
111 :
112 434 : foreach(l, node->tidrangequals)
113 : {
114 232 : OpExpr *opexpr = lfirst(l);
115 : TidOpExpr *tidopexpr;
116 :
117 232 : if (!IsA(opexpr, OpExpr))
118 0 : elog(ERROR, "could not identify CTID expression");
119 :
120 232 : tidopexpr = MakeTidOpExpr(opexpr, tidrangestate);
121 232 : tidexprs = lappend(tidexprs, tidopexpr);
122 : }
123 :
124 202 : tidrangestate->trss_tidexprs = tidexprs;
125 202 : }
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
137 184 : TidRangeEval(TidRangeScanState *node)
138 : {
139 184 : 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 184 : ItemPointerSet(&lowerBound, 0, 0);
150 184 : ItemPointerSet(&upperBound, InvalidBlockNumber, PG_UINT16_MAX);
151 :
152 380 : foreach(l, node->trss_tidexprs)
153 : {
154 202 : TidOpExpr *tidopexpr = (TidOpExpr *) lfirst(l);
155 : ItemPointer itemptr;
156 : bool isNull;
157 :
158 : /* Evaluate this bound. */
159 : itemptr = (ItemPointer)
160 202 : DatumGetPointer(ExecEvalExprSwitchContext(tidopexpr->exprstate,
161 : econtext,
162 : &isNull));
163 :
164 : /* If the bound is NULL, *nothing* matches the qual. */
165 202 : if (isNull)
166 6 : return false;
167 :
168 196 : if (tidopexpr->exprtype == TIDEXPR_LOWER_BOUND)
169 : {
170 : ItemPointerData lb;
171 :
172 58 : 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 58 : if (!tidopexpr->inclusive)
179 46 : ItemPointerInc(&lb);
180 :
181 : /* Check if we can narrow the range using this qual */
182 58 : if (ItemPointerCompare(&lb, &lowerBound) > 0)
183 58 : ItemPointerCopy(&lb, &lowerBound);
184 : }
185 :
186 138 : else if (tidopexpr->exprtype == TIDEXPR_UPPER_BOUND)
187 : {
188 : ItemPointerData ub;
189 :
190 138 : 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 138 : if (!tidopexpr->inclusive)
197 60 : ItemPointerDec(&ub);
198 :
199 : /* Check if we can narrow the range using this qual */
200 138 : if (ItemPointerCompare(&ub, &upperBound) < 0)
201 138 : ItemPointerCopy(&ub, &upperBound);
202 : }
203 : }
204 :
205 178 : ItemPointerCopy(&lowerBound, &node->trss_mintid);
206 178 : ItemPointerCopy(&upperBound, &node->trss_maxtid);
207 :
208 178 : 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 *
220 5946 : TidRangeNext(TidRangeScanState *node)
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 5946 : scandesc = node->ss.ss_currentScanDesc;
231 5946 : estate = node->ss.ps.state;
232 5946 : slot = node->ss.ss_ScanTupleSlot;
233 5946 : direction = estate->es_direction;
234 :
235 5946 : if (!node->trss_inScan)
236 : {
237 : /* First time through, compute TID range to scan */
238 184 : if (!TidRangeEval(node))
239 6 : return NULL;
240 :
241 178 : if (scandesc == NULL)
242 : {
243 112 : scandesc = table_beginscan_tidrange(node->ss.ss_currentRelation,
244 : estate->es_snapshot,
245 : &node->trss_mintid,
246 : &node->trss_maxtid);
247 112 : node->ss.ss_currentScanDesc = scandesc;
248 : }
249 : else
250 : {
251 : /* rescan with the updated TID range */
252 66 : table_rescan_tidrange(scandesc, &node->trss_mintid,
253 : &node->trss_maxtid);
254 : }
255 :
256 178 : node->trss_inScan = true;
257 : }
258 :
259 : /* Fetch the next tuple. */
260 5940 : if (!table_scan_getnextslot_tidrange(scandesc, direction, slot))
261 : {
262 166 : node->trss_inScan = false;
263 166 : ExecClearTuple(slot);
264 : }
265 :
266 5940 : return slot;
267 : }
268 :
269 : /*
270 : * TidRangeRecheck -- access method routine to recheck a tuple in EvalPlanQual
271 : */
272 : static bool
273 0 : TidRangeRecheck(TidRangeScanState *node, TupleTableSlot *slot)
274 : {
275 0 : 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 *
294 5946 : ExecTidRangeScan(PlanState *pstate)
295 : {
296 5946 : TidRangeScanState *node = castNode(TidRangeScanState, pstate);
297 :
298 5946 : return ExecScan(&node->ss,
299 : (ExecScanAccessMtd) TidRangeNext,
300 : (ExecScanRecheckMtd) TidRangeRecheck);
301 : }
302 :
303 : /* ----------------------------------------------------------------
304 : * ExecReScanTidRangeScan(node)
305 : * ----------------------------------------------------------------
306 : */
307 : void
308 66 : ExecReScanTidRangeScan(TidRangeScanState *node)
309 : {
310 : /* mark scan as not in progress, and tid range list as not computed yet */
311 66 : node->trss_inScan = false;
312 :
313 : /*
314 : * We must wait until TidRangeNext before calling table_rescan_tidrange.
315 : */
316 66 : ExecScanReScan(&node->ss);
317 66 : }
318 :
319 : /* ----------------------------------------------------------------
320 : * ExecEndTidRangeScan
321 : *
322 : * Releases any storage allocated through C routines.
323 : * Returns nothing.
324 : * ----------------------------------------------------------------
325 : */
326 : void
327 202 : ExecEndTidRangeScan(TidRangeScanState *node)
328 : {
329 202 : TableScanDesc scan = node->ss.ss_currentScanDesc;
330 :
331 202 : if (scan != NULL)
332 112 : table_endscan(scan);
333 202 : }
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 : */
346 : TidRangeScanState *
347 202 : ExecInitTidRangeScan(TidRangeScan *node, EState *estate, int eflags)
348 : {
349 : TidRangeScanState *tidrangestate;
350 : Relation currentRelation;
351 :
352 : /*
353 : * create state structure
354 : */
355 202 : tidrangestate = makeNode(TidRangeScanState);
356 202 : tidrangestate->ss.ps.plan = (Plan *) node;
357 202 : tidrangestate->ss.ps.state = estate;
358 202 : tidrangestate->ss.ps.ExecProcNode = ExecTidRangeScan;
359 :
360 : /*
361 : * Miscellaneous initialization
362 : *
363 : * create expression context for node
364 : */
365 202 : ExecAssignExprContext(estate, &tidrangestate->ss.ps);
366 :
367 : /*
368 : * mark scan as not in progress, and TID range as not computed yet
369 : */
370 202 : tidrangestate->trss_inScan = false;
371 :
372 : /*
373 : * open the scan relation
374 : */
375 202 : currentRelation = ExecOpenScanRelation(estate, node->scan.scanrelid, eflags);
376 :
377 202 : tidrangestate->ss.ss_currentRelation = currentRelation;
378 202 : tidrangestate->ss.ss_currentScanDesc = NULL; /* no table scan here */
379 :
380 : /*
381 : * get the scan type from the relation descriptor.
382 : */
383 202 : ExecInitScanTupleSlot(estate, &tidrangestate->ss,
384 : RelationGetDescr(currentRelation),
385 : table_slot_callbacks(currentRelation));
386 :
387 : /*
388 : * Initialize result type and projection.
389 : */
390 202 : ExecInitResultTypeTL(&tidrangestate->ss.ps);
391 202 : ExecAssignScanProjectionInfo(&tidrangestate->ss);
392 :
393 : /*
394 : * initialize child expressions
395 : */
396 202 : tidrangestate->ss.ps.qual =
397 202 : ExecInitQual(node->scan.plan.qual, (PlanState *) tidrangestate);
398 :
399 202 : TidExprListCreate(tidrangestate);
400 :
401 : /*
402 : * all done.
403 : */
404 202 : return tidrangestate;
405 : }
|