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