1 /*-------------------------------------------------------------------------
2  *
3  * tidpath.c
4  *	  Routines to determine which TID conditions are usable for scanning
5  *	  a given relation, and create TidPaths accordingly.
6  *
7  * What we are looking for here is WHERE conditions of the form
8  * "CTID = pseudoconstant", which can be implemented by just fetching
9  * the tuple directly via heap_fetch().  We can also handle OR'd conditions
10  * such as (CTID = const1) OR (CTID = const2), as well as ScalarArrayOpExpr
11  * conditions of the form CTID = ANY(pseudoconstant_array).  In particular
12  * this allows
13  *		WHERE ctid IN (tid1, tid2, ...)
14  *
15  * We also support "WHERE CURRENT OF cursor" conditions (CurrentOfExpr),
16  * which amount to "CTID = run-time-determined-TID".  These could in
17  * theory be translated to a simple comparison of CTID to the result of
18  * a function, but in practice it works better to keep the special node
19  * representation all the way through to execution.
20  *
21  * There is currently no special support for joins involving CTID; in
22  * particular nothing corresponding to best_inner_indexscan().  Since it's
23  * not very useful to store TIDs of one table in another table, there
24  * doesn't seem to be enough use-case to justify adding a lot of code
25  * for that.
26  *
27  *
28  * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
29  * Portions Copyright (c) 1994, Regents of the University of California
30  *
31  *
32  * IDENTIFICATION
33  *	  src/backend/optimizer/path/tidpath.c
34  *
35  *-------------------------------------------------------------------------
36  */
37 #include "postgres.h"
38 
39 #include "access/sysattr.h"
40 #include "catalog/pg_operator.h"
41 #include "catalog/pg_type.h"
42 #include "nodes/nodeFuncs.h"
43 #include "optimizer/clauses.h"
44 #include "optimizer/pathnode.h"
45 #include "optimizer/paths.h"
46 
47 
48 static bool IsTidEqualClause(OpExpr *node, int varno);
49 static bool IsTidEqualAnyClause(ScalarArrayOpExpr *node, int varno);
50 static List *TidQualFromExpr(Node *expr, int varno);
51 static List *TidQualFromRestrictinfo(List *restrictinfo, int varno);
52 
53 
54 /*
55  * Check to see if an opclause is of the form
56  *		CTID = pseudoconstant
57  * or
58  *		pseudoconstant = CTID
59  *
60  * We check that the CTID Var belongs to relation "varno".  That is probably
61  * redundant considering this is only applied to restriction clauses, but
62  * let's be safe.
63  */
64 static bool
IsTidEqualClause(OpExpr * node,int varno)65 IsTidEqualClause(OpExpr *node, int varno)
66 {
67 	Node	   *arg1,
68 			   *arg2,
69 			   *other;
70 	Var		   *var;
71 
72 	/* Operator must be tideq */
73 	if (node->opno != TIDEqualOperator)
74 		return false;
75 	if (list_length(node->args) != 2)
76 		return false;
77 	arg1 = linitial(node->args);
78 	arg2 = lsecond(node->args);
79 
80 	/* Look for CTID as either argument */
81 	other = NULL;
82 	if (arg1 && IsA(arg1, Var))
83 	{
84 		var = (Var *) arg1;
85 		if (var->varattno == SelfItemPointerAttributeNumber &&
86 			var->vartype == TIDOID &&
87 			var->varno == varno &&
88 			var->varlevelsup == 0)
89 			other = arg2;
90 	}
91 	if (!other && arg2 && IsA(arg2, Var))
92 	{
93 		var = (Var *) arg2;
94 		if (var->varattno == SelfItemPointerAttributeNumber &&
95 			var->vartype == TIDOID &&
96 			var->varno == varno &&
97 			var->varlevelsup == 0)
98 			other = arg1;
99 	}
100 	if (!other)
101 		return false;
102 	if (exprType(other) != TIDOID)
103 		return false;			/* probably can't happen */
104 
105 	/* The other argument must be a pseudoconstant */
106 	if (!is_pseudo_constant_clause(other))
107 		return false;
108 
109 	return true;				/* success */
110 }
111 
112 /*
113  * Check to see if a clause is of the form
114  *		CTID = ANY (pseudoconstant_array)
115  */
116 static bool
IsTidEqualAnyClause(ScalarArrayOpExpr * node,int varno)117 IsTidEqualAnyClause(ScalarArrayOpExpr *node, int varno)
118 {
119 	Node	   *arg1,
120 			   *arg2;
121 
122 	/* Operator must be tideq */
123 	if (node->opno != TIDEqualOperator)
124 		return false;
125 	if (!node->useOr)
126 		return false;
127 	Assert(list_length(node->args) == 2);
128 	arg1 = linitial(node->args);
129 	arg2 = lsecond(node->args);
130 
131 	/* CTID must be first argument */
132 	if (arg1 && IsA(arg1, Var))
133 	{
134 		Var		   *var = (Var *) arg1;
135 
136 		if (var->varattno == SelfItemPointerAttributeNumber &&
137 			var->vartype == TIDOID &&
138 			var->varno == varno &&
139 			var->varlevelsup == 0)
140 		{
141 			/* The other argument must be a pseudoconstant */
142 			if (is_pseudo_constant_clause(arg2))
143 				return true;	/* success */
144 		}
145 	}
146 
147 	return false;
148 }
149 
150 /*
151  *	Extract a set of CTID conditions from the given qual expression
152  *
153  *	Returns a List of CTID qual expressions (with implicit OR semantics
154  *	across the list), or NIL if there are no usable conditions.
155  *
156  *	If the expression is an AND clause, we can use a CTID condition
157  *	from any sub-clause.  If it is an OR clause, we must be able to
158  *	extract a CTID condition from every sub-clause, or we can't use it.
159  *
160  *	In theory, in the AND case we could get CTID conditions from different
161  *	sub-clauses, in which case we could try to pick the most efficient one.
162  *	In practice, such usage seems very unlikely, so we don't bother; we
163  *	just exit as soon as we find the first candidate.
164  */
165 static List *
TidQualFromExpr(Node * expr,int varno)166 TidQualFromExpr(Node *expr, int varno)
167 {
168 	List	   *rlst = NIL;
169 	ListCell   *l;
170 
171 	if (is_opclause(expr))
172 	{
173 		/* base case: check for tideq opclause */
174 		if (IsTidEqualClause((OpExpr *) expr, varno))
175 			rlst = list_make1(expr);
176 	}
177 	else if (expr && IsA(expr, ScalarArrayOpExpr))
178 	{
179 		/* another base case: check for tid = ANY clause */
180 		if (IsTidEqualAnyClause((ScalarArrayOpExpr *) expr, varno))
181 			rlst = list_make1(expr);
182 	}
183 	else if (expr && IsA(expr, CurrentOfExpr))
184 	{
185 		/* another base case: check for CURRENT OF on this rel */
186 		if (((CurrentOfExpr *) expr)->cvarno == varno)
187 			rlst = list_make1(expr);
188 	}
189 	else if (and_clause(expr))
190 	{
191 		foreach(l, ((BoolExpr *) expr)->args)
192 		{
193 			rlst = TidQualFromExpr((Node *) lfirst(l), varno);
194 			if (rlst)
195 				break;
196 		}
197 	}
198 	else if (or_clause(expr))
199 	{
200 		foreach(l, ((BoolExpr *) expr)->args)
201 		{
202 			List	   *frtn = TidQualFromExpr((Node *) lfirst(l), varno);
203 
204 			if (frtn)
205 				rlst = list_concat(rlst, frtn);
206 			else
207 			{
208 				if (rlst)
209 					list_free(rlst);
210 				rlst = NIL;
211 				break;
212 			}
213 		}
214 	}
215 	return rlst;
216 }
217 
218 /*
219  *	Extract a set of CTID conditions from the given restrictinfo list
220  *
221  *	This is essentially identical to the AND case of TidQualFromExpr,
222  *	except for the format of the input.
223  */
224 static List *
TidQualFromRestrictinfo(List * restrictinfo,int varno)225 TidQualFromRestrictinfo(List *restrictinfo, int varno)
226 {
227 	List	   *rlst = NIL;
228 	ListCell   *l;
229 
230 	foreach(l, restrictinfo)
231 	{
232 		RestrictInfo *rinfo = (RestrictInfo *) lfirst(l);
233 
234 		if (!IsA(rinfo, RestrictInfo))
235 			continue;			/* probably should never happen */
236 		rlst = TidQualFromExpr((Node *) rinfo->clause, varno);
237 		if (rlst)
238 			break;
239 	}
240 	return rlst;
241 }
242 
243 /*
244  * create_tidscan_paths
245  *	  Create paths corresponding to direct TID scans of the given rel.
246  *
247  *	  Candidate paths are added to the rel's pathlist (using add_path).
248  */
249 void
create_tidscan_paths(PlannerInfo * root,RelOptInfo * rel)250 create_tidscan_paths(PlannerInfo *root, RelOptInfo *rel)
251 {
252 	Relids		required_outer;
253 	List	   *tidquals;
254 
255 	/*
256 	 * We don't support pushing join clauses into the quals of a tidscan, but
257 	 * it could still have required parameterization due to LATERAL refs in
258 	 * its tlist.
259 	 */
260 	required_outer = rel->lateral_relids;
261 
262 	tidquals = TidQualFromRestrictinfo(rel->baserestrictinfo, rel->relid);
263 
264 	if (tidquals)
265 		add_path(rel, (Path *) create_tidscan_path(root, rel, tidquals,
266 												   required_outer));
267 }
268