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 | * As with indexscans, our definition of "pseudoconstant" is pretty liberal: |
16 | * we allow anything that doesn't involve a volatile function or a Var of |
17 | * the relation under consideration. Vars belonging to other relations of |
18 | * the query are allowed, giving rise to parameterized TID scans. |
19 | * |
20 | * We also support "WHERE CURRENT OF cursor" conditions (CurrentOfExpr), |
21 | * which amount to "CTID = run-time-determined-TID". These could in |
22 | * theory be translated to a simple comparison of CTID to the result of |
23 | * a function, but in practice it works better to keep the special node |
24 | * representation all the way through to execution. |
25 | * |
26 | * |
27 | * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group |
28 | * Portions Copyright (c) 1994, Regents of the University of California |
29 | * |
30 | * |
31 | * IDENTIFICATION |
32 | * src/backend/optimizer/path/tidpath.c |
33 | * |
34 | *------------------------------------------------------------------------- |
35 | */ |
36 | #include "postgres.h" |
37 | |
38 | #include "access/sysattr.h" |
39 | #include "catalog/pg_operator.h" |
40 | #include "catalog/pg_type.h" |
41 | #include "nodes/nodeFuncs.h" |
42 | #include "optimizer/clauses.h" |
43 | #include "optimizer/optimizer.h" |
44 | #include "optimizer/pathnode.h" |
45 | #include "optimizer/paths.h" |
46 | #include "optimizer/restrictinfo.h" |
47 | |
48 | |
49 | /* |
50 | * Does this Var represent the CTID column of the specified baserel? |
51 | */ |
52 | static inline bool |
53 | IsCTIDVar(Var *var, RelOptInfo *rel) |
54 | { |
55 | /* The vartype check is strictly paranoia */ |
56 | if (var->varattno == SelfItemPointerAttributeNumber && |
57 | var->vartype == TIDOID && |
58 | var->varno == rel->relid && |
59 | var->varlevelsup == 0) |
60 | return true; |
61 | return false; |
62 | } |
63 | |
64 | /* |
65 | * Check to see if a RestrictInfo is of the form |
66 | * CTID = pseudoconstant |
67 | * or |
68 | * pseudoconstant = CTID |
69 | * where the CTID Var belongs to relation "rel", and nothing on the |
70 | * other side of the clause does. |
71 | */ |
72 | static bool |
73 | IsTidEqualClause(RestrictInfo *rinfo, RelOptInfo *rel) |
74 | { |
75 | OpExpr *node; |
76 | Node *arg1, |
77 | *arg2, |
78 | *other; |
79 | Relids other_relids; |
80 | |
81 | /* Must be an OpExpr */ |
82 | if (!is_opclause(rinfo->clause)) |
83 | return false; |
84 | node = (OpExpr *) rinfo->clause; |
85 | |
86 | /* Operator must be tideq */ |
87 | if (node->opno != TIDEqualOperator) |
88 | return false; |
89 | Assert(list_length(node->args) == 2); |
90 | arg1 = linitial(node->args); |
91 | arg2 = lsecond(node->args); |
92 | |
93 | /* Look for CTID as either argument */ |
94 | other = NULL; |
95 | other_relids = NULL; |
96 | if (arg1 && IsA(arg1, Var) && |
97 | IsCTIDVar((Var *) arg1, rel)) |
98 | { |
99 | other = arg2; |
100 | other_relids = rinfo->right_relids; |
101 | } |
102 | if (!other && arg2 && IsA(arg2, Var) && |
103 | IsCTIDVar((Var *) arg2, rel)) |
104 | { |
105 | other = arg1; |
106 | other_relids = rinfo->left_relids; |
107 | } |
108 | if (!other) |
109 | return false; |
110 | |
111 | /* The other argument must be a pseudoconstant */ |
112 | if (bms_is_member(rel->relid, other_relids) || |
113 | contain_volatile_functions(other)) |
114 | return false; |
115 | |
116 | return true; /* success */ |
117 | } |
118 | |
119 | /* |
120 | * Check to see if a RestrictInfo is of the form |
121 | * CTID = ANY (pseudoconstant_array) |
122 | * where the CTID Var belongs to relation "rel", and nothing on the |
123 | * other side of the clause does. |
124 | */ |
125 | static bool |
126 | IsTidEqualAnyClause(RestrictInfo *rinfo, RelOptInfo *rel) |
127 | { |
128 | ScalarArrayOpExpr *node; |
129 | Node *arg1, |
130 | *arg2; |
131 | |
132 | /* Must be a ScalarArrayOpExpr */ |
133 | if (!(rinfo->clause && IsA(rinfo->clause, ScalarArrayOpExpr))) |
134 | return false; |
135 | node = (ScalarArrayOpExpr *) rinfo->clause; |
136 | |
137 | /* Operator must be tideq */ |
138 | if (node->opno != TIDEqualOperator) |
139 | return false; |
140 | if (!node->useOr) |
141 | return false; |
142 | Assert(list_length(node->args) == 2); |
143 | arg1 = linitial(node->args); |
144 | arg2 = lsecond(node->args); |
145 | |
146 | /* CTID must be first argument */ |
147 | if (arg1 && IsA(arg1, Var) && |
148 | IsCTIDVar((Var *) arg1, rel)) |
149 | { |
150 | /* The other argument must be a pseudoconstant */ |
151 | if (bms_is_member(rel->relid, pull_varnos(arg2)) || |
152 | contain_volatile_functions(arg2)) |
153 | return false; |
154 | |
155 | return true; /* success */ |
156 | } |
157 | |
158 | return false; |
159 | } |
160 | |
161 | /* |
162 | * Check to see if a RestrictInfo is a CurrentOfExpr referencing "rel". |
163 | */ |
164 | static bool |
165 | IsCurrentOfClause(RestrictInfo *rinfo, RelOptInfo *rel) |
166 | { |
167 | CurrentOfExpr *node; |
168 | |
169 | /* Must be a CurrentOfExpr */ |
170 | if (!(rinfo->clause && IsA(rinfo->clause, CurrentOfExpr))) |
171 | return false; |
172 | node = (CurrentOfExpr *) rinfo->clause; |
173 | |
174 | /* If it references this rel, we're good */ |
175 | if (node->cvarno == rel->relid) |
176 | return true; |
177 | |
178 | return false; |
179 | } |
180 | |
181 | /* |
182 | * Extract a set of CTID conditions from the given RestrictInfo |
183 | * |
184 | * Returns a List of CTID qual RestrictInfos for the specified rel (with |
185 | * implicit OR semantics across the list), or NIL if there are no usable |
186 | * conditions. |
187 | * |
188 | * This function considers only base cases; AND/OR combination is handled |
189 | * below. Therefore the returned List never has more than one element. |
190 | * (Using a List may seem a bit weird, but it simplifies the caller.) |
191 | */ |
192 | static List * |
193 | TidQualFromRestrictInfo(RestrictInfo *rinfo, RelOptInfo *rel) |
194 | { |
195 | /* |
196 | * We may ignore pseudoconstant clauses (they can't contain Vars, so could |
197 | * not match anyway). |
198 | */ |
199 | if (rinfo->pseudoconstant) |
200 | return NIL; |
201 | |
202 | /* |
203 | * If clause must wait till after some lower-security-level restriction |
204 | * clause, reject it. |
205 | */ |
206 | if (!restriction_is_securely_promotable(rinfo, rel)) |
207 | return NIL; |
208 | |
209 | /* |
210 | * Check all base cases. If we get a match, return the clause. |
211 | */ |
212 | if (IsTidEqualClause(rinfo, rel) || |
213 | IsTidEqualAnyClause(rinfo, rel) || |
214 | IsCurrentOfClause(rinfo, rel)) |
215 | return list_make1(rinfo); |
216 | |
217 | return NIL; |
218 | } |
219 | |
220 | /* |
221 | * Extract a set of CTID conditions from implicit-AND List of RestrictInfos |
222 | * |
223 | * Returns a List of CTID qual RestrictInfos for the specified rel (with |
224 | * implicit OR semantics across the list), or NIL if there are no usable |
225 | * conditions. |
226 | * |
227 | * This function is just concerned with handling AND/OR recursion. |
228 | */ |
229 | static List * |
230 | TidQualFromRestrictInfoList(List *rlist, RelOptInfo *rel) |
231 | { |
232 | List *rlst = NIL; |
233 | ListCell *l; |
234 | |
235 | foreach(l, rlist) |
236 | { |
237 | RestrictInfo *rinfo = lfirst_node(RestrictInfo, l); |
238 | |
239 | if (restriction_is_or_clause(rinfo)) |
240 | { |
241 | ListCell *j; |
242 | |
243 | /* |
244 | * We must be able to extract a CTID condition from every |
245 | * sub-clause of an OR, or we can't use it. |
246 | */ |
247 | foreach(j, ((BoolExpr *) rinfo->orclause)->args) |
248 | { |
249 | Node *orarg = (Node *) lfirst(j); |
250 | List *sublist; |
251 | |
252 | /* OR arguments should be ANDs or sub-RestrictInfos */ |
253 | if (is_andclause(orarg)) |
254 | { |
255 | List *andargs = ((BoolExpr *) orarg)->args; |
256 | |
257 | /* Recurse in case there are sub-ORs */ |
258 | sublist = TidQualFromRestrictInfoList(andargs, rel); |
259 | } |
260 | else |
261 | { |
262 | RestrictInfo *rinfo = castNode(RestrictInfo, orarg); |
263 | |
264 | Assert(!restriction_is_or_clause(rinfo)); |
265 | sublist = TidQualFromRestrictInfo(rinfo, rel); |
266 | } |
267 | |
268 | /* |
269 | * If nothing found in this arm, we can't do anything with |
270 | * this OR clause. |
271 | */ |
272 | if (sublist == NIL) |
273 | { |
274 | rlst = NIL; /* forget anything we had */ |
275 | break; /* out of loop over OR args */ |
276 | } |
277 | |
278 | /* |
279 | * OK, continue constructing implicitly-OR'ed result list. |
280 | */ |
281 | rlst = list_concat(rlst, sublist); |
282 | } |
283 | } |
284 | else |
285 | { |
286 | /* Not an OR clause, so handle base cases */ |
287 | rlst = TidQualFromRestrictInfo(rinfo, rel); |
288 | } |
289 | |
290 | /* |
291 | * Stop as soon as we find any usable CTID condition. In theory we |
292 | * could get CTID equality conditions from different AND'ed clauses, |
293 | * in which case we could try to pick the most efficient one. In |
294 | * practice, such usage seems very unlikely, so we don't bother; we |
295 | * just exit as soon as we find the first candidate. |
296 | */ |
297 | if (rlst) |
298 | break; |
299 | } |
300 | |
301 | return rlst; |
302 | } |
303 | |
304 | /* |
305 | * Given a list of join clauses involving our rel, create a parameterized |
306 | * TidPath for each one that is a suitable TidEqual clause. |
307 | * |
308 | * In principle we could combine clauses that reference the same outer rels, |
309 | * but it doesn't seem like such cases would arise often enough to be worth |
310 | * troubling over. |
311 | */ |
312 | static void |
313 | BuildParameterizedTidPaths(PlannerInfo *root, RelOptInfo *rel, List *clauses) |
314 | { |
315 | ListCell *l; |
316 | |
317 | foreach(l, clauses) |
318 | { |
319 | RestrictInfo *rinfo = lfirst_node(RestrictInfo, l); |
320 | List *tidquals; |
321 | Relids required_outer; |
322 | |
323 | /* |
324 | * Validate whether each clause is actually usable; we must check this |
325 | * even when examining clauses generated from an EquivalenceClass, |
326 | * since they might not satisfy the restriction on not having Vars of |
327 | * our rel on the other side, or somebody might've built an operator |
328 | * class that accepts type "tid" but has other operators in it. |
329 | * |
330 | * We currently consider only TidEqual join clauses. In principle we |
331 | * might find a suitable ScalarArrayOpExpr in the rel's joininfo list, |
332 | * but it seems unlikely to be worth expending the cycles to check. |
333 | * And we definitely won't find a CurrentOfExpr here. Hence, we don't |
334 | * use TidQualFromRestrictInfo; but this must match that function |
335 | * otherwise. |
336 | */ |
337 | if (rinfo->pseudoconstant || |
338 | !restriction_is_securely_promotable(rinfo, rel) || |
339 | !IsTidEqualClause(rinfo, rel)) |
340 | continue; |
341 | |
342 | /* |
343 | * Check if clause can be moved to this rel; this is probably |
344 | * redundant when considering EC-derived clauses, but we must check it |
345 | * for "loose" join clauses. |
346 | */ |
347 | if (!join_clause_is_movable_to(rinfo, rel)) |
348 | continue; |
349 | |
350 | /* OK, make list of clauses for this path */ |
351 | tidquals = list_make1(rinfo); |
352 | |
353 | /* Compute required outer rels for this path */ |
354 | required_outer = bms_union(rinfo->required_relids, rel->lateral_relids); |
355 | required_outer = bms_del_member(required_outer, rel->relid); |
356 | |
357 | add_path(rel, (Path *) create_tidscan_path(root, rel, tidquals, |
358 | required_outer)); |
359 | } |
360 | } |
361 | |
362 | /* |
363 | * Test whether an EquivalenceClass member matches our rel's CTID Var. |
364 | * |
365 | * This is a callback for use by generate_implied_equalities_for_column. |
366 | */ |
367 | static bool |
368 | ec_member_matches_ctid(PlannerInfo *root, RelOptInfo *rel, |
369 | EquivalenceClass *ec, EquivalenceMember *em, |
370 | void *arg) |
371 | { |
372 | if (em->em_expr && IsA(em->em_expr, Var) && |
373 | IsCTIDVar((Var *) em->em_expr, rel)) |
374 | return true; |
375 | return false; |
376 | } |
377 | |
378 | /* |
379 | * create_tidscan_paths |
380 | * Create paths corresponding to direct TID scans of the given rel. |
381 | * |
382 | * Candidate paths are added to the rel's pathlist (using add_path). |
383 | */ |
384 | void |
385 | create_tidscan_paths(PlannerInfo *root, RelOptInfo *rel) |
386 | { |
387 | List *tidquals; |
388 | |
389 | /* |
390 | * If any suitable quals exist in the rel's baserestrict list, generate a |
391 | * plain (unparameterized) TidPath with them. |
392 | */ |
393 | tidquals = TidQualFromRestrictInfoList(rel->baserestrictinfo, rel); |
394 | |
395 | if (tidquals) |
396 | { |
397 | /* |
398 | * This path uses no join clauses, but it could still have required |
399 | * parameterization due to LATERAL refs in its tlist. |
400 | */ |
401 | Relids required_outer = rel->lateral_relids; |
402 | |
403 | add_path(rel, (Path *) create_tidscan_path(root, rel, tidquals, |
404 | required_outer)); |
405 | } |
406 | |
407 | /* |
408 | * Try to generate parameterized TidPaths using equality clauses extracted |
409 | * from EquivalenceClasses. (This is important since simple "t1.ctid = |
410 | * t2.ctid" clauses will turn into ECs.) |
411 | */ |
412 | if (rel->has_eclass_joins) |
413 | { |
414 | List *clauses; |
415 | |
416 | /* Generate clauses, skipping any that join to lateral_referencers */ |
417 | clauses = generate_implied_equalities_for_column(root, |
418 | rel, |
419 | ec_member_matches_ctid, |
420 | NULL, |
421 | rel->lateral_referencers); |
422 | |
423 | /* Generate a path for each usable join clause */ |
424 | BuildParameterizedTidPaths(root, rel, clauses); |
425 | } |
426 | |
427 | /* |
428 | * Also consider parameterized TidPaths using "loose" join quals. Quals |
429 | * of the form "t1.ctid = t2.ctid" would turn into these if they are outer |
430 | * join quals, for example. |
431 | */ |
432 | BuildParameterizedTidPaths(root, rel, rel->joininfo); |
433 | } |
434 | |