Line data Source code
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-2017, 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 : #include "optimizer/restrictinfo.h"
47 :
48 :
49 : static bool IsTidEqualClause(OpExpr *node, int varno);
50 : static bool IsTidEqualAnyClause(ScalarArrayOpExpr *node, int varno);
51 : static List *TidQualFromExpr(Node *expr, int varno);
52 : static List *TidQualFromBaseRestrictinfo(RelOptInfo *rel);
53 :
54 :
55 : /*
56 : * Check to see if an opclause is of the form
57 : * CTID = pseudoconstant
58 : * or
59 : * pseudoconstant = CTID
60 : *
61 : * We check that the CTID Var belongs to relation "varno". That is probably
62 : * redundant considering this is only applied to restriction clauses, but
63 : * let's be safe.
64 : */
65 : static bool
66 10839 : IsTidEqualClause(OpExpr *node, int varno)
67 : {
68 : Node *arg1,
69 : *arg2,
70 : *other;
71 : Var *var;
72 :
73 : /* Operator must be tideq */
74 10839 : if (node->opno != TIDEqualOperator)
75 10829 : return false;
76 10 : if (list_length(node->args) != 2)
77 0 : return false;
78 10 : arg1 = linitial(node->args);
79 10 : arg2 = lsecond(node->args);
80 :
81 : /* Look for CTID as either argument */
82 10 : other = NULL;
83 10 : if (arg1 && IsA(arg1, Var))
84 : {
85 8 : var = (Var *) arg1;
86 12 : if (var->varattno == SelfItemPointerAttributeNumber &&
87 8 : var->vartype == TIDOID &&
88 8 : var->varno == varno &&
89 4 : var->varlevelsup == 0)
90 4 : other = arg2;
91 : }
92 10 : if (!other && arg2 && IsA(arg2, Var))
93 : {
94 2 : var = (Var *) arg2;
95 4 : if (var->varattno == SelfItemPointerAttributeNumber &&
96 4 : var->vartype == TIDOID &&
97 4 : var->varno == varno &&
98 2 : var->varlevelsup == 0)
99 2 : other = arg1;
100 : }
101 10 : if (!other)
102 4 : return false;
103 6 : if (exprType(other) != TIDOID)
104 0 : return false; /* probably can't happen */
105 :
106 : /* The other argument must be a pseudoconstant */
107 6 : if (!is_pseudo_constant_clause(other))
108 0 : return false;
109 :
110 6 : return true; /* success */
111 : }
112 :
113 : /*
114 : * Check to see if a clause is of the form
115 : * CTID = ANY (pseudoconstant_array)
116 : */
117 : static bool
118 480 : IsTidEqualAnyClause(ScalarArrayOpExpr *node, int varno)
119 : {
120 : Node *arg1,
121 : *arg2;
122 :
123 : /* Operator must be tideq */
124 480 : if (node->opno != TIDEqualOperator)
125 475 : return false;
126 5 : if (!node->useOr)
127 0 : return false;
128 5 : Assert(list_length(node->args) == 2);
129 5 : arg1 = linitial(node->args);
130 5 : arg2 = lsecond(node->args);
131 :
132 : /* CTID must be first argument */
133 5 : if (arg1 && IsA(arg1, Var))
134 : {
135 5 : Var *var = (Var *) arg1;
136 :
137 10 : if (var->varattno == SelfItemPointerAttributeNumber &&
138 10 : var->vartype == TIDOID &&
139 10 : var->varno == varno &&
140 5 : var->varlevelsup == 0)
141 : {
142 : /* The other argument must be a pseudoconstant */
143 5 : if (is_pseudo_constant_clause(arg2))
144 5 : return true; /* success */
145 : }
146 : }
147 :
148 0 : return false;
149 : }
150 :
151 : /*
152 : * Extract a set of CTID conditions from the given qual expression
153 : *
154 : * Returns a List of CTID qual expressions (with implicit OR semantics
155 : * across the list), or NIL if there are no usable conditions.
156 : *
157 : * If the expression is an AND clause, we can use a CTID condition
158 : * from any sub-clause. If it is an OR clause, we must be able to
159 : * extract a CTID condition from every sub-clause, or we can't use it.
160 : *
161 : * In theory, in the AND case we could get CTID conditions from different
162 : * sub-clauses, in which case we could try to pick the most efficient one.
163 : * In practice, such usage seems very unlikely, so we don't bother; we
164 : * just exit as soon as we find the first candidate.
165 : */
166 : static List *
167 13941 : TidQualFromExpr(Node *expr, int varno)
168 : {
169 13941 : List *rlst = NIL;
170 : ListCell *l;
171 :
172 13941 : if (is_opclause(expr))
173 : {
174 : /* base case: check for tideq opclause */
175 21678 : if (IsTidEqualClause((OpExpr *) expr, varno))
176 6 : rlst = list_make1(expr);
177 : }
178 3102 : else if (expr && IsA(expr, ScalarArrayOpExpr))
179 : {
180 : /* another base case: check for tid = ANY clause */
181 960 : if (IsTidEqualAnyClause((ScalarArrayOpExpr *) expr, varno))
182 5 : rlst = list_make1(expr);
183 : }
184 2622 : else if (expr && IsA(expr, CurrentOfExpr))
185 : {
186 : /* another base case: check for CURRENT OF on this rel */
187 114 : if (((CurrentOfExpr *) expr)->cvarno == varno)
188 57 : rlst = list_make1(expr);
189 : }
190 2565 : else if (and_clause(expr))
191 : {
192 34 : foreach(l, ((BoolExpr *) expr)->args)
193 : {
194 25 : rlst = TidQualFromExpr((Node *) lfirst(l), varno);
195 25 : if (rlst)
196 4 : break;
197 : }
198 : }
199 2552 : else if (or_clause(expr))
200 : {
201 192 : foreach(l, ((BoolExpr *) expr)->args)
202 : {
203 190 : List *frtn = TidQualFromExpr((Node *) lfirst(l), varno);
204 :
205 190 : if (frtn)
206 4 : rlst = list_concat(rlst, frtn);
207 : else
208 : {
209 186 : if (rlst)
210 0 : list_free(rlst);
211 186 : rlst = NIL;
212 186 : break;
213 : }
214 : }
215 : }
216 13941 : return rlst;
217 : }
218 :
219 : /*
220 : * Extract a set of CTID conditions from the rel's baserestrictinfo list
221 : */
222 : static List *
223 15473 : TidQualFromBaseRestrictinfo(RelOptInfo *rel)
224 : {
225 15473 : List *rlst = NIL;
226 : ListCell *l;
227 :
228 29368 : foreach(l, rel->baserestrictinfo)
229 : {
230 13961 : RestrictInfo *rinfo = (RestrictInfo *) lfirst(l);
231 :
232 : /*
233 : * If clause must wait till after some lower-security-level
234 : * restriction clause, reject it.
235 : */
236 13961 : if (!restriction_is_securely_promotable(rinfo, rel))
237 235 : continue;
238 :
239 13726 : rlst = TidQualFromExpr((Node *) rinfo->clause, rel->relid);
240 13726 : if (rlst)
241 66 : break;
242 : }
243 15473 : return rlst;
244 : }
245 :
246 : /*
247 : * create_tidscan_paths
248 : * Create paths corresponding to direct TID scans of the given rel.
249 : *
250 : * Candidate paths are added to the rel's pathlist (using add_path).
251 : */
252 : void
253 15473 : create_tidscan_paths(PlannerInfo *root, RelOptInfo *rel)
254 : {
255 : Relids required_outer;
256 : List *tidquals;
257 :
258 : /*
259 : * We don't support pushing join clauses into the quals of a tidscan, but
260 : * it could still have required parameterization due to LATERAL refs in
261 : * its tlist.
262 : */
263 15473 : required_outer = rel->lateral_relids;
264 :
265 15473 : tidquals = TidQualFromBaseRestrictinfo(rel);
266 :
267 15473 : if (tidquals)
268 66 : add_path(rel, (Path *) create_tidscan_path(root, rel, tidquals,
269 : required_outer));
270 15473 : }
|