Age Owner TLA Line data Source code
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"
22 : #include "executor/nodeTidrangescan.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 : {
41 : TIDEXPR_UPPER_BOUND,
42 : TIDEXPR_LOWER_BOUND
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 */
51 : } TidOpExpr;
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 *
771 drowley 58 GIC 116 : MakeTidOpExpr(OpExpr *expr, TidRangeScanState *tidstate)
59 : {
60 116 : Node *arg1 = get_leftop((Expr *) expr);
61 116 : Node *arg2 = get_rightop((Expr *) expr);
62 116 : ExprState *exprstate = NULL;
771 drowley 63 CBC 116 : bool invert = false;
64 : TidOpExpr *tidopexpr;
771 drowley 65 ECB :
771 drowley 66 CBC 116 : if (IsCTIDVar(arg1))
67 98 : exprstate = ExecInitExpr((Expr *) arg2, &tidstate->ss.ps);
68 18 : else if (IsCTIDVar(arg2))
69 : {
771 drowley 70 GIC 18 : exprstate = ExecInitExpr((Expr *) arg1, &tidstate->ss.ps);
771 drowley 71 CBC 18 : invert = true;
771 drowley 72 ECB : }
73 : else
771 drowley 74 UIC 0 : elog(ERROR, "could not identify CTID variable");
771 drowley 75 ECB :
771 drowley 76 CBC 116 : tidopexpr = (TidOpExpr *) palloc(sizeof(TidOpExpr));
771 drowley 77 GIC 116 : tidopexpr->inclusive = false; /* for now */
78 :
771 drowley 79 GBC 116 : switch (expr->opno)
80 : {
771 drowley 81 CBC 12 : case TIDLessEqOperator:
82 12 : tidopexpr->inclusive = true;
83 : /* fall through */
84 57 : case TIDLessOperator:
771 drowley 85 GIC 57 : tidopexpr->exprtype = invert ? TIDEXPR_LOWER_BOUND : TIDEXPR_UPPER_BOUND;
771 drowley 86 CBC 57 : break;
87 27 : case TIDGreaterEqOperator:
771 drowley 88 GIC 27 : tidopexpr->inclusive = true;
771 drowley 89 ECB : /* fall through */
771 drowley 90 CBC 59 : case TIDGreaterOperator:
91 59 : tidopexpr->exprtype = invert ? TIDEXPR_UPPER_BOUND : TIDEXPR_LOWER_BOUND;
92 59 : break;
771 drowley 93 LBC 0 : default:
771 drowley 94 UIC 0 : elog(ERROR, "could not identify CTID operator");
771 drowley 95 ECB : }
96 :
771 drowley 97 CBC 116 : tidopexpr->exprstate = exprstate;
771 drowley 98 EUB :
771 drowley 99 GBC 116 : return tidopexpr;
100 : }
101 :
771 drowley 102 ECB : /*
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
771 drowley 107 GIC 101 : TidExprListCreate(TidRangeScanState *tidrangestate)
108 : {
109 101 : TidRangeScan *node = (TidRangeScan *) tidrangestate->ss.ps.plan;
110 101 : List *tidexprs = NIL;
111 : ListCell *l;
771 drowley 112 ECB :
771 drowley 113 GIC 217 : foreach(l, node->tidrangequals)
771 drowley 114 ECB : {
771 drowley 115 CBC 116 : OpExpr *opexpr = lfirst(l);
116 : TidOpExpr *tidopexpr;
117 :
118 116 : if (!IsA(opexpr, OpExpr))
771 drowley 119 UIC 0 : elog(ERROR, "could not identify CTID expression");
771 drowley 120 ECB :
771 drowley 121 GIC 116 : tidopexpr = MakeTidOpExpr(opexpr, tidrangestate);
122 116 : tidexprs = lappend(tidexprs, tidopexpr);
771 drowley 123 ECB : }
771 drowley 124 EUB :
771 drowley 125 GIC 101 : tidrangestate->trss_tidexprs = tidexprs;
771 drowley 126 CBC 101 : }
771 drowley 127 ECB :
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
771 drowley 138 GIC 92 : TidRangeEval(TidRangeScanState *node)
139 : {
140 92 : ExprContext *econtext = node->ss.ps.ps_ExprContext;
141 : ItemPointerData lowerBound;
142 : ItemPointerData upperBound;
771 drowley 143 ECB : 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 : */
771 drowley 150 GIC 92 : ItemPointerSet(&lowerBound, 0, 0);
151 92 : ItemPointerSet(&upperBound, InvalidBlockNumber, PG_UINT16_MAX);
152 :
153 190 : foreach(l, node->trss_tidexprs)
154 : {
771 drowley 155 CBC 101 : TidOpExpr *tidopexpr = (TidOpExpr *) lfirst(l);
771 drowley 156 ECB : ItemPointer itemptr;
157 : bool isNull;
158 :
159 : /* Evaluate this bound. */
160 : itemptr = (ItemPointer)
771 drowley 161 GIC 101 : DatumGetPointer(ExecEvalExprSwitchContext(tidopexpr->exprstate,
162 : econtext,
163 : &isNull));
164 :
165 : /* If the bound is NULL, *nothing* matches the qual. */
771 drowley 166 CBC 101 : if (isNull)
771 drowley 167 GIC 3 : return false;
168 :
169 98 : if (tidopexpr->exprtype == TIDEXPR_LOWER_BOUND)
170 : {
771 drowley 171 ECB : ItemPointerData lb;
172 :
771 drowley 173 GIC 29 : ItemPointerCopy(itemptr, &lb);
771 drowley 174 ECB :
175 : /*
176 : * Normalize non-inclusive ranges to become inclusive. The
177 : * resulting ItemPointer here may not be a valid item pointer.
178 : */
771 drowley 179 GIC 29 : if (!tidopexpr->inclusive)
180 23 : ItemPointerInc(&lb);
181 :
182 : /* Check if we can narrow the range using this qual */
183 29 : if (ItemPointerCompare(&lb, &lowerBound) > 0)
771 drowley 184 CBC 29 : ItemPointerCopy(&lb, &lowerBound);
771 drowley 185 ECB : }
186 :
771 drowley 187 GIC 69 : else if (tidopexpr->exprtype == TIDEXPR_UPPER_BOUND)
771 drowley 188 ECB : {
189 : ItemPointerData ub;
190 :
771 drowley 191 GIC 69 : ItemPointerCopy(itemptr, &ub);
771 drowley 192 ECB :
193 : /*
194 : * Normalize non-inclusive ranges to become inclusive. The
195 : * resulting ItemPointer here may not be a valid item pointer.
196 : */
771 drowley 197 GIC 69 : if (!tidopexpr->inclusive)
198 30 : ItemPointerDec(&ub);
199 :
200 : /* Check if we can narrow the range using this qual */
201 69 : if (ItemPointerCompare(&ub, &upperBound) < 0)
771 drowley 202 CBC 69 : ItemPointerCopy(&ub, &upperBound);
771 drowley 203 ECB : }
204 : }
205 :
771 drowley 206 CBC 89 : ItemPointerCopy(&lowerBound, &node->trss_mintid);
207 89 : ItemPointerCopy(&upperBound, &node->trss_maxtid);
208 :
771 drowley 209 GIC 89 : return true;
210 : }
771 drowley 211 ECB :
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 *
771 drowley 221 GIC 2973 : TidRangeNext(TidRangeScanState *node)
222 : {
223 : TableScanDesc scandesc;
224 : EState *estate;
225 : ScanDirection direction;
771 drowley 226 ECB : TupleTableSlot *slot;
227 :
228 : /*
229 : * extract necessary information from TID scan node
230 : */
771 drowley 231 GIC 2973 : scandesc = node->ss.ss_currentScanDesc;
232 2973 : estate = node->ss.ps.state;
233 2973 : slot = node->ss.ss_ScanTupleSlot;
234 2973 : direction = estate->es_direction;
235 :
771 drowley 236 CBC 2973 : if (!node->trss_inScan)
771 drowley 237 ECB : {
238 : /* First time through, compute TID range to scan */
771 drowley 239 CBC 92 : if (!TidRangeEval(node))
771 drowley 240 GIC 3 : return NULL;
771 drowley 241 ECB :
771 drowley 242 GIC 89 : if (scandesc == NULL)
243 : {
771 drowley 244 CBC 56 : scandesc = table_beginscan_tidrange(node->ss.ss_currentRelation,
771 drowley 245 ECB : estate->es_snapshot,
246 : &node->trss_mintid,
247 : &node->trss_maxtid);
771 drowley 248 GIC 56 : node->ss.ss_currentScanDesc = scandesc;
771 drowley 249 ECB : }
250 : else
251 : {
252 : /* rescan with the updated TID range */
771 drowley 253 CBC 33 : table_rescan_tidrange(scandesc, &node->trss_mintid,
254 : &node->trss_maxtid);
255 : }
256 :
771 drowley 257 GIC 89 : node->trss_inScan = true;
771 drowley 258 ECB : }
259 :
260 : /* Fetch the next tuple. */
771 drowley 261 GIC 2970 : if (!table_scan_getnextslot_tidrange(scandesc, direction, slot))
771 drowley 262 ECB : {
771 drowley 263 GIC 83 : node->trss_inScan = false;
264 83 : ExecClearTuple(slot);
265 : }
771 drowley 266 ECB :
771 drowley 267 GIC 2970 : return slot;
771 drowley 268 ECB : }
269 :
270 : /*
271 : * TidRangeRecheck -- access method routine to recheck a tuple in EvalPlanQual
272 : */
273 : static bool
771 drowley 274 UIC 0 : TidRangeRecheck(TidRangeScanState *node, TupleTableSlot *slot)
275 : {
276 0 : return true;
277 : }
278 :
771 drowley 279 EUB : /* ----------------------------------------------------------------
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 *
771 drowley 295 GIC 2973 : ExecTidRangeScan(PlanState *pstate)
296 : {
297 2973 : TidRangeScanState *node = castNode(TidRangeScanState, pstate);
298 :
299 2973 : return ExecScan(&node->ss,
771 drowley 300 ECB : (ExecScanAccessMtd) TidRangeNext,
301 : (ExecScanRecheckMtd) TidRangeRecheck);
302 : }
303 :
304 : /* ----------------------------------------------------------------
305 : * ExecReScanTidRangeScan(node)
306 : * ----------------------------------------------------------------
307 : */
308 : void
771 drowley 309 GIC 33 : ExecReScanTidRangeScan(TidRangeScanState *node)
310 : {
311 : /* mark scan as not in progress, and tid range list as not computed yet */
312 33 : node->trss_inScan = false;
313 :
771 drowley 314 ECB : /*
315 : * We must wait until TidRangeNext before calling table_rescan_tidrange.
316 : */
771 drowley 317 CBC 33 : ExecScanReScan(&node->ss);
771 drowley 318 GIC 33 : }
319 :
320 : /* ----------------------------------------------------------------
321 : * ExecEndTidRangeScan
771 drowley 322 ECB : *
323 : * Releases any storage allocated through C routines.
324 : * Returns nothing.
325 : * ----------------------------------------------------------------
326 : */
327 : void
771 drowley 328 GIC 101 : ExecEndTidRangeScan(TidRangeScanState *node)
329 : {
330 101 : TableScanDesc scan = node->ss.ss_currentScanDesc;
331 :
332 101 : if (scan != NULL)
771 drowley 333 CBC 56 : table_endscan(scan);
334 :
771 drowley 335 ECB : /*
336 : * Free the exprcontext
337 : */
771 drowley 338 CBC 101 : ExecFreeExprContext(&node->ss.ps);
339 :
340 : /*
341 : * clear out tuple table slots
342 : */
343 101 : if (node->ss.ps.ps_ResultTupleSlot)
771 drowley 344 GIC 95 : ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);
345 101 : ExecClearTuple(node->ss.ss_ScanTupleSlot);
346 101 : }
347 :
771 drowley 348 ECB : /* ----------------------------------------------------------------
349 : * ExecInitTidRangeScan
350 : *
351 : * Initializes the tid range scan's state information, creates
352 : * scan keys, and opens the scan relation.
353 : *
354 : * Parameters:
355 : * node: TidRangeScan node produced by the planner.
356 : * estate: the execution state initialized in InitPlan.
357 : * ----------------------------------------------------------------
358 : */
359 : TidRangeScanState *
771 drowley 360 GIC 101 : ExecInitTidRangeScan(TidRangeScan *node, EState *estate, int eflags)
361 : {
362 : TidRangeScanState *tidrangestate;
363 : Relation currentRelation;
364 :
771 drowley 365 ECB : /*
366 : * create state structure
367 : */
771 drowley 368 GIC 101 : tidrangestate = makeNode(TidRangeScanState);
369 101 : tidrangestate->ss.ps.plan = (Plan *) node;
370 101 : tidrangestate->ss.ps.state = estate;
371 101 : tidrangestate->ss.ps.ExecProcNode = ExecTidRangeScan;
372 :
771 drowley 373 ECB : /*
374 : * Miscellaneous initialization
375 : *
376 : * create expression context for node
377 : */
771 drowley 378 GIC 101 : ExecAssignExprContext(estate, &tidrangestate->ss.ps);
379 :
380 : /*
381 : * mark scan as not in progress, and TID range as not computed yet
382 : */
771 drowley 383 CBC 101 : tidrangestate->trss_inScan = false;
384 :
385 : /*
386 : * open the scan relation
387 : */
388 101 : currentRelation = ExecOpenScanRelation(estate, node->scan.scanrelid, eflags);
389 :
771 drowley 390 GIC 101 : tidrangestate->ss.ss_currentRelation = currentRelation;
391 101 : tidrangestate->ss.ss_currentScanDesc = NULL; /* no table scan here */
392 :
771 drowley 393 ECB : /*
394 : * get the scan type from the relation descriptor.
395 : */
771 drowley 396 CBC 101 : ExecInitScanTupleSlot(estate, &tidrangestate->ss,
397 : RelationGetDescr(currentRelation),
398 : table_slot_callbacks(currentRelation));
399 :
400 : /*
771 drowley 401 ECB : * Initialize result type and projection.
402 : */
771 drowley 403 GIC 101 : ExecInitResultTypeTL(&tidrangestate->ss.ps);
404 101 : ExecAssignScanProjectionInfo(&tidrangestate->ss);
405 :
406 : /*
407 : * initialize child expressions
771 drowley 408 ECB : */
771 drowley 409 CBC 101 : tidrangestate->ss.ps.qual =
771 drowley 410 GIC 101 : ExecInitQual(node->scan.plan.qual, (PlanState *) tidrangestate);
411 :
412 101 : TidExprListCreate(tidrangestate);
413 :
771 drowley 414 ECB : /*
415 : * all done.
416 : */
771 drowley 417 CBC 101 : return tidrangestate;
418 : }
|