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