Age Owner Branch data TLA 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 and TidRangePaths accordingly.
6 : : *
7 : : * For TidPaths, we look for 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 : : * Additionally, TidRangePaths may be created for conditions of the form
27 : : * "CTID relop pseudoconstant", where relop is one of >,>=,<,<=, and
28 : : * AND-clauses composed of such conditions.
29 : : *
30 : : * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
31 : : * Portions Copyright (c) 1994, Regents of the University of California
32 : : *
33 : : *
34 : : * IDENTIFICATION
35 : : * src/backend/optimizer/path/tidpath.c
36 : : *
37 : : *-------------------------------------------------------------------------
38 : : */
39 : : #include "postgres.h"
40 : :
41 : : #include "access/sysattr.h"
42 : : #include "catalog/pg_operator.h"
43 : : #include "catalog/pg_type.h"
44 : : #include "nodes/nodeFuncs.h"
45 : : #include "optimizer/cost.h"
46 : : #include "optimizer/optimizer.h"
47 : : #include "optimizer/pathnode.h"
48 : : #include "optimizer/paths.h"
49 : : #include "optimizer/restrictinfo.h"
50 : :
51 : :
52 : : /*
53 : : * Does this Var represent the CTID column of the specified baserel?
54 : : */
55 : : static inline bool
2544 tgl@sss.pgh.pa.us 56 :CBC 487404 : IsCTIDVar(Var *var, RelOptInfo *rel)
57 : : {
58 : : /* The vartype check is strictly paranoia */
59 [ + + ]: 487404 : if (var->varattno == SelfItemPointerAttributeNumber &&
60 [ + - ]: 2637 : var->vartype == TIDOID &&
61 [ + + ]: 2637 : var->varno == rel->relid &&
1052 62 [ + - ]: 2577 : var->varnullingrels == NULL &&
2544 63 [ + - ]: 2577 : var->varlevelsup == 0)
64 : 2577 : return true;
65 : 484827 : return false;
66 : : }
67 : :
68 : : /*
69 : : * Check to see if a RestrictInfo is of the form
70 : : * CTID OP pseudoconstant
71 : : * or
72 : : * pseudoconstant OP CTID
73 : : * where OP is a binary operation, the CTID Var belongs to relation "rel",
74 : : * and nothing on the other side of the clause does.
75 : : */
76 : : static bool
1754 drowley@postgresql.o 77 : 462610 : IsBinaryTidClause(RestrictInfo *rinfo, RelOptInfo *rel)
78 : : {
79 : : OpExpr *node;
80 : : Node *arg1,
81 : : *arg2,
82 : : *other;
83 : : Relids other_relids;
84 : :
85 : : /* Must be an OpExpr */
2544 tgl@sss.pgh.pa.us 86 [ + + ]: 462610 : if (!is_opclause(rinfo->clause))
87 : 85876 : return false;
88 : 376734 : node = (OpExpr *) rinfo->clause;
89 : :
90 : : /* OpExpr must have two arguments */
1754 drowley@postgresql.o 91 [ + + ]: 376734 : if (list_length(node->args) != 2)
7326 tgl@sss.pgh.pa.us 92 : 24 : return false;
7875 neilc@samurai.com 93 : 376710 : arg1 = linitial(node->args);
9521 bruce@momjian.us 94 : 376710 : arg2 = lsecond(node->args);
95 : :
96 : : /* Look for CTID as either argument */
7421 tgl@sss.pgh.pa.us 97 : 376710 : other = NULL;
2544 98 : 376710 : other_relids = NULL;
99 [ + - + + : 736897 : if (arg1 && IsA(arg1, Var) &&
+ + ]
100 : 360187 : IsCTIDVar((Var *) arg1, rel))
101 : : {
102 : 2366 : other = arg2;
103 : 2366 : other_relids = rinfo->right_relids;
104 : : }
105 [ + + + - : 433424 : if (!other && arg2 && IsA(arg2, Var) &&
+ + + + ]
106 : 56714 : IsCTIDVar((Var *) arg2, rel))
107 : : {
108 : 114 : other = arg1;
109 : 114 : other_relids = rinfo->left_relids;
110 : : }
7421 111 [ + + ]: 376710 : if (!other)
7326 112 : 374230 : return false;
113 : :
114 : : /* The other argument must be a pseudoconstant */
2544 115 [ + - - + ]: 4960 : if (bms_is_member(rel->relid, other_relids) ||
116 : 2480 : contain_volatile_functions(other))
7326 tgl@sss.pgh.pa.us 117 :UBC 0 : return false;
118 : :
7326 tgl@sss.pgh.pa.us 119 :CBC 2480 : return true; /* success */
120 : : }
121 : :
122 : : /*
123 : : * Check to see if a RestrictInfo is of the form
124 : : * CTID = pseudoconstant
125 : : * or
126 : : * pseudoconstant = CTID
127 : : * where the CTID Var belongs to relation "rel", and nothing on the
128 : : * other side of the clause does.
129 : : */
130 : : static bool
1754 drowley@postgresql.o 131 : 258669 : IsTidEqualClause(RestrictInfo *rinfo, RelOptInfo *rel)
132 : : {
133 [ + + ]: 258669 : if (!IsBinaryTidClause(rinfo, rel))
134 : 257344 : return false;
135 : :
136 [ + + ]: 1325 : if (((OpExpr *) rinfo->clause)->opno == TIDEqualOperator)
137 : 230 : return true;
138 : :
139 : 1095 : return false;
140 : : }
141 : :
142 : : /*
143 : : * Check to see if a RestrictInfo is of the form
144 : : * CTID OP pseudoconstant
145 : : * or
146 : : * pseudoconstant OP CTID
147 : : * where OP is a range operator such as <, <=, >, or >=, the CTID Var belongs
148 : : * to relation "rel", and nothing on the other side of the clause does.
149 : : */
150 : : static bool
151 : 203941 : IsTidRangeClause(RestrictInfo *rinfo, RelOptInfo *rel)
152 : : {
153 : : Oid opno;
154 : :
155 [ + + ]: 203941 : if (!IsBinaryTidClause(rinfo, rel))
156 : 202786 : return false;
157 : 1155 : opno = ((OpExpr *) rinfo->clause)->opno;
158 : :
159 [ + + + + : 1155 : if (opno == TIDLessOperator || opno == TIDLessEqOperator ||
+ + ]
160 [ + + ]: 1028 : opno == TIDGreaterOperator || opno == TIDGreaterEqOperator)
161 : 1023 : return true;
162 : :
163 : 132 : return false;
164 : : }
165 : :
166 : : /*
167 : : * Check to see if a RestrictInfo is of the form
168 : : * CTID = ANY (pseudoconstant_array)
169 : : * where the CTID Var belongs to relation "rel", and nothing on the
170 : : * other side of the clause does.
171 : : */
172 : : static bool
1791 tgl@sss.pgh.pa.us 173 : 200173 : IsTidEqualAnyClause(PlannerInfo *root, RestrictInfo *rinfo, RelOptInfo *rel)
174 : : {
175 : : ScalarArrayOpExpr *node;
176 : : Node *arg1,
177 : : *arg2;
178 : :
179 : : /* Must be a ScalarArrayOpExpr */
2544 180 [ + - + + ]: 200173 : if (!(rinfo->clause && IsA(rinfo->clause, ScalarArrayOpExpr)))
181 : 190885 : return false;
182 : 9288 : node = (ScalarArrayOpExpr *) rinfo->clause;
183 : :
184 : : /* Operator must be tideq */
7326 185 [ + + ]: 9288 : if (node->opno != TIDEqualOperator)
186 : 9257 : return false;
187 [ - + ]: 31 : if (!node->useOr)
7326 tgl@sss.pgh.pa.us 188 :UBC 0 : return false;
7326 tgl@sss.pgh.pa.us 189 [ - + ]:CBC 31 : Assert(list_length(node->args) == 2);
190 : 31 : arg1 = linitial(node->args);
191 : 31 : arg2 = lsecond(node->args);
192 : :
193 : : /* CTID must be first argument */
2544 194 [ + - + - : 62 : if (arg1 && IsA(arg1, Var) &&
+ - ]
195 : 31 : IsCTIDVar((Var *) arg1, rel))
196 : : {
197 : : /* The other argument must be a pseudoconstant */
1791 198 [ + - - + ]: 62 : if (bms_is_member(rel->relid, pull_varnos(root, arg2)) ||
2544 199 : 31 : contain_volatile_functions(arg2))
2544 tgl@sss.pgh.pa.us 200 :UBC 0 : return false;
201 : :
2544 tgl@sss.pgh.pa.us 202 :CBC 31 : return true; /* success */
203 : : }
204 : :
7326 tgl@sss.pgh.pa.us 205 :UBC 0 : return false;
206 : : }
207 : :
208 : : /*
209 : : * Check to see if a RestrictInfo is a CurrentOfExpr referencing "rel".
210 : : */
211 : : static bool
2544 tgl@sss.pgh.pa.us 212 :CBC 200513 : IsCurrentOfClause(RestrictInfo *rinfo, RelOptInfo *rel)
213 : : {
214 : : CurrentOfExpr *node;
215 : :
216 : : /* Must be a CurrentOfExpr */
217 [ + - + + ]: 200513 : if (!(rinfo->clause && IsA(rinfo->clause, CurrentOfExpr)))
218 : 200109 : return false;
219 : 404 : node = (CurrentOfExpr *) rinfo->clause;
220 : :
221 : : /* If it references this rel, we're good */
222 [ + - ]: 404 : if (node->cvarno == rel->relid)
223 : 404 : return true;
224 : :
2544 tgl@sss.pgh.pa.us 225 :UBC 0 : return false;
226 : : }
227 : :
228 : : /*
229 : : * Is the RestrictInfo usable as a CTID qual for the specified rel?
230 : : *
231 : : * This function considers only base cases; AND/OR combination is handled
232 : : * below.
233 : : */
234 : : static bool
589 tgl@sss.pgh.pa.us 235 :CBC 204754 : RestrictInfoIsTidQual(PlannerInfo *root, RestrictInfo *rinfo, RelOptInfo *rel)
236 : : {
237 : : /*
238 : : * We may ignore pseudoconstant clauses (they can't contain Vars, so could
239 : : * not match anyway).
240 : : */
2544 241 [ + + ]: 204754 : if (rinfo->pseudoconstant)
589 242 : 3498 : return false;
243 : :
244 : : /*
245 : : * If clause must wait till after some lower-security-level restriction
246 : : * clause, reject it.
247 : : */
2544 248 [ + + ]: 201256 : if (!restriction_is_securely_promotable(rinfo, rel))
589 249 : 931 : return false;
250 : :
251 : : /*
252 : : * Check all base cases.
253 : : */
2544 254 [ + + + + ]: 400498 : if (IsTidEqualClause(rinfo, rel) ||
1791 255 [ + + ]: 400315 : IsTidEqualAnyClause(root, rinfo, rel) ||
2544 256 : 200142 : IsCurrentOfClause(rinfo, rel))
589 257 : 385 : return true;
258 : :
259 : 199940 : return false;
260 : : }
261 : :
262 : : /*
263 : : * Extract a set of CTID conditions from implicit-AND List of RestrictInfos
264 : : *
265 : : * Returns a List of CTID qual RestrictInfos for the specified rel (with
266 : : * implicit OR semantics across the list), or NIL if there are no usable
267 : : * equality conditions.
268 : : *
269 : : * This function is mainly concerned with handling AND/OR recursion.
270 : : * However, we do have a special rule to enforce: if there is a CurrentOfExpr
271 : : * qual, we *must* return that and only that, else the executor may fail.
272 : : * Ordinarily a CurrentOfExpr would be all alone anyway because of grammar
273 : : * restrictions, but it is possible for RLS quals to appear AND'ed with it.
274 : : * It's even possible (if fairly useless) for the RLS quals to be CTID quals.
275 : : * So we must scan the whole rlist to see if there's a CurrentOfExpr. Since
276 : : * we have to do that, we also apply some very-trivial preference rules about
277 : : * which of the other possibilities should be chosen, in the unlikely event
278 : : * that there's more than one choice.
279 : : */
280 : : static List *
513 rhaas@postgresql.org 281 : 210284 : TidQualFromRestrictInfoList(PlannerInfo *root, List *rlist, RelOptInfo *rel,
282 : : bool *isCurrentOf)
283 : : {
589 tgl@sss.pgh.pa.us 284 : 210284 : RestrictInfo *tidclause = NULL; /* best simple CTID qual so far */
285 : 210284 : List *orlist = NIL; /* best OR'ed CTID qual so far */
286 : : ListCell *l;
287 : :
513 rhaas@postgresql.org 288 : 210284 : *isCurrentOf = false;
289 : :
2544 tgl@sss.pgh.pa.us 290 [ + + + + : 415244 : foreach(l, rlist)
+ + ]
291 : : {
292 : 205162 : RestrictInfo *rinfo = lfirst_node(RestrictInfo, l);
293 : :
294 [ + + ]: 205162 : if (restriction_is_or_clause(rinfo))
295 : : {
589 296 : 2341 : List *rlst = NIL;
297 : : ListCell *j;
298 : :
299 : : /*
300 : : * We must be able to extract a CTID condition from every
301 : : * sub-clause of an OR, or we can't use it.
302 : : */
2544 303 [ + - + + : 2367 : foreach(j, ((BoolExpr *) rinfo->orclause)->args)
+ + ]
304 : : {
305 : 2354 : Node *orarg = (Node *) lfirst(j);
306 : : List *sublist;
307 : :
308 : : /* OR arguments should be ANDs or sub-RestrictInfos */
2514 309 [ + + ]: 2354 : if (is_andclause(orarg))
310 : : {
2544 311 : 421 : List *andargs = ((BoolExpr *) orarg)->args;
312 : : bool sublistIsCurrentOf;
313 : :
314 : : /* Recurse in case there are sub-ORs */
513 rhaas@postgresql.org 315 : 421 : sublist = TidQualFromRestrictInfoList(root, andargs, rel,
316 : : &sublistIsCurrentOf);
317 [ - + ]: 421 : if (sublistIsCurrentOf)
513 rhaas@postgresql.org 318 [ # # ]:UBC 0 : elog(ERROR, "IS CURRENT OF within OR clause");
319 : : }
320 : : else
321 : : {
1169 drowley@postgresql.o 322 :CBC 1933 : RestrictInfo *ri = castNode(RestrictInfo, orarg);
323 : :
324 [ - + ]: 1933 : Assert(!restriction_is_or_clause(ri));
589 tgl@sss.pgh.pa.us 325 [ + + ]: 1933 : if (RestrictInfoIsTidQual(root, ri, rel))
326 : 14 : sublist = list_make1(ri);
327 : : else
328 : 1919 : sublist = NIL;
329 : : }
330 : :
331 : : /*
332 : : * If nothing found in this arm, we can't do anything with
333 : : * this OR clause.
334 : : */
2544 335 [ + + ]: 2354 : if (sublist == NIL)
336 : : {
337 : 2328 : rlst = NIL; /* forget anything we had */
338 : 2328 : break; /* out of loop over OR args */
339 : : }
340 : :
341 : : /*
342 : : * OK, continue constructing implicitly-OR'ed result list.
343 : : */
344 : 26 : rlst = list_concat(rlst, sublist);
345 : : }
346 : :
589 347 [ + + ]: 2341 : if (rlst)
348 : : {
349 : : /*
350 : : * Accept the OR'ed list if it's the first one, or if it's
351 : : * shorter than the previous one.
352 : : */
353 [ - + - - ]: 13 : if (orlist == NIL || list_length(rlst) < list_length(orlist))
354 : 13 : orlist = rlst;
355 : : }
356 : : }
357 : : else
358 : : {
359 : : /* Not an OR clause, so handle base cases */
360 [ + + ]: 202821 : if (RestrictInfoIsTidQual(root, rinfo, rel))
361 : : {
362 : : /* We can stop immediately if it's a CurrentOfExpr */
363 [ + + ]: 371 : if (IsCurrentOfClause(rinfo, rel))
364 : : {
513 rhaas@postgresql.org 365 : 202 : *isCurrentOf = true;
589 tgl@sss.pgh.pa.us 366 : 202 : return list_make1(rinfo);
367 : : }
368 : :
369 : : /*
370 : : * Otherwise, remember the first non-OR CTID qual. We could
371 : : * try to apply some preference order if there's more than
372 : : * one, but such usage seems very unlikely, so don't bother.
373 : : */
374 [ + - ]: 169 : if (tidclause == NULL)
375 : 169 : tidclause = rinfo;
376 : : }
377 : : }
378 : : }
379 : :
380 : : /*
381 : : * Prefer any singleton CTID qual to an OR'ed list. Again, it seems
382 : : * unlikely to be worth thinking harder than that.
383 : : */
384 [ + + ]: 210082 : if (tidclause)
385 : 163 : return list_make1(tidclause);
386 : 209919 : return orlist;
387 : : }
388 : :
389 : : /*
390 : : * Extract a set of CTID range conditions from implicit-AND List of RestrictInfos
391 : : *
392 : : * Returns a List of CTID range qual RestrictInfos for the specified rel
393 : : * (with implicit AND semantics across the list), or NIL if there are no
394 : : * usable range conditions or if the rel's table AM does not support TID range
395 : : * scans.
396 : : */
397 : : static List *
1754 drowley@postgresql.o 398 : 209661 : TidRangeQualFromRestrictInfoList(List *rlist, RelOptInfo *rel)
399 : : {
400 : 209661 : List *rlst = NIL;
401 : : ListCell *l;
402 : :
403 [ - + ]: 209661 : if ((rel->amflags & AMFLAG_HAS_TID_RANGE) == 0)
1754 drowley@postgresql.o 404 :UBC 0 : return NIL;
405 : :
1754 drowley@postgresql.o 406 [ + + + + :CBC 413602 : foreach(l, rlist)
+ + ]
407 : : {
408 : 203941 : RestrictInfo *rinfo = lfirst_node(RestrictInfo, l);
409 : :
410 [ + + ]: 203941 : if (IsTidRangeClause(rinfo, rel))
411 : 1023 : rlst = lappend(rlst, rinfo);
412 : : }
413 : :
414 : 209661 : return rlst;
415 : : }
416 : :
417 : : /*
418 : : * Given a list of join clauses involving our rel, create a parameterized
419 : : * TidPath for each one that is a suitable TidEqual clause.
420 : : *
421 : : * In principle we could combine clauses that reference the same outer rels,
422 : : * but it doesn't seem like such cases would arise often enough to be worth
423 : : * troubling over.
424 : : */
425 : : static void
2544 tgl@sss.pgh.pa.us 426 : 280702 : BuildParameterizedTidPaths(PlannerInfo *root, RelOptInfo *rel, List *clauses)
427 : : {
428 : : ListCell *l;
429 : :
430 [ + + + + : 344158 : foreach(l, clauses)
+ + ]
431 : : {
432 : 63456 : RestrictInfo *rinfo = lfirst_node(RestrictInfo, l);
433 : : List *tidquals;
434 : : Relids required_outer;
435 : :
436 : : /*
437 : : * Validate whether each clause is actually usable; we must check this
438 : : * even when examining clauses generated from an EquivalenceClass,
439 : : * since they might not satisfy the restriction on not having Vars of
440 : : * our rel on the other side, or somebody might've built an operator
441 : : * class that accepts type "tid" but has other operators in it.
442 : : *
443 : : * We currently consider only TidEqual join clauses. In principle we
444 : : * might find a suitable ScalarArrayOpExpr in the rel's joininfo list,
445 : : * but it seems unlikely to be worth expending the cycles to check.
446 : : * And we definitely won't find a CurrentOfExpr here. Hence, we don't
447 : : * use RestrictInfoIsTidQual; but this must match that function
448 : : * otherwise.
449 : : */
2543 450 [ + + ]: 63456 : if (rinfo->pseudoconstant ||
451 [ + - ]: 58344 : !restriction_is_securely_promotable(rinfo, rel) ||
452 [ + + ]: 58344 : !IsTidEqualClause(rinfo, rel))
3255 453 : 63384 : continue;
454 : :
455 : : /*
456 : : * Check if clause can be moved to this rel; this is probably
457 : : * redundant when considering EC-derived clauses, but we must check it
458 : : * for "loose" join clauses.
459 : : */
2544 460 [ + + ]: 78 : if (!join_clause_is_movable_to(rinfo, rel))
461 : 6 : continue;
462 : :
463 : : /* OK, make list of clauses for this path */
464 : 72 : tidquals = list_make1(rinfo);
465 : :
466 : : /* Compute required outer rels for this path */
467 : 72 : required_outer = bms_union(rinfo->required_relids, rel->lateral_relids);
468 : 72 : required_outer = bms_del_member(required_outer, rel->relid);
469 : :
470 : 72 : add_path(rel, (Path *) create_tidscan_path(root, rel, tidquals,
471 : : required_outer));
472 : : }
473 : 280702 : }
474 : :
475 : : /*
476 : : * Test whether an EquivalenceClass member matches our rel's CTID Var.
477 : : *
478 : : * This is a callback for use by generate_implied_equalities_for_column.
479 : : */
480 : : static bool
481 : 72346 : ec_member_matches_ctid(PlannerInfo *root, RelOptInfo *rel,
482 : : EquivalenceClass *ec, EquivalenceMember *em,
483 : : void *arg)
484 : : {
485 [ + - + + : 142818 : if (em->em_expr && IsA(em->em_expr, Var) &&
+ + ]
486 : 70472 : IsCTIDVar((Var *) em->em_expr, rel))
487 : 66 : return true;
488 : 72280 : return false;
489 : : }
490 : :
491 : : /*
492 : : * create_tidscan_paths
493 : : * Create paths corresponding to direct TID scans of the given rel and add
494 : : * them to the corresponding path list via add_path or add_partial_path.
495 : : */
496 : : bool
7500 497 : 209863 : create_tidscan_paths(PlannerInfo *root, RelOptInfo *rel)
498 : : {
499 : : List *tidquals;
500 : : List *tidrangequals;
501 : : bool isCurrentOf;
502 : :
503 : : /*
504 : : * If any suitable quals exist in the rel's baserestrict list, generate a
505 : : * plain (unparameterized) TidPath with them.
506 : : *
507 : : * We skip this when enable_tidscan = false, except when the qual is
508 : : * CurrentOfExpr. In that case, a TID scan is the only correct path.
509 : : */
513 rhaas@postgresql.org 510 : 209863 : tidquals = TidQualFromRestrictInfoList(root, rel->baserestrictinfo, rel,
511 : : &isCurrentOf);
512 : :
513 [ + + - + : 209863 : if (tidquals != NIL && (enable_tidscan || isCurrentOf))
- - ]
514 : : {
515 : : /*
516 : : * This path uses no join clauses, but it could still have required
517 : : * parameterization due to LATERAL refs in its tlist.
518 : : */
2544 tgl@sss.pgh.pa.us 519 : 366 : Relids required_outer = rel->lateral_relids;
520 : :
4861 521 : 366 : add_path(rel, (Path *) create_tidscan_path(root, rel, tidquals,
522 : : required_outer));
523 : :
524 : : /*
525 : : * When the qual is CurrentOfExpr, the path that we just added is the
526 : : * only one the executor can handle, so we should return before adding
527 : : * any others. Returning true lets the caller know not to add any
528 : : * others, either.
529 : : */
513 rhaas@postgresql.org 530 [ + + ]: 366 : if (isCurrentOf)
531 : 202 : return true;
532 : : }
533 : :
534 : : /* Skip the rest if TID scans are disabled. */
535 [ - + ]: 209661 : if (!enable_tidscan)
513 rhaas@postgresql.org 536 :UBC 0 : return false;
537 : :
538 : : /*
539 : : * If there are range quals in the baserestrict list, generate a
540 : : * TidRangePath.
541 : : */
1754 drowley@postgresql.o 542 :CBC 209661 : tidrangequals = TidRangeQualFromRestrictInfoList(rel->baserestrictinfo,
543 : : rel);
544 : :
545 [ + + ]: 209661 : if (tidrangequals != NIL)
546 : : {
547 : : /*
548 : : * This path uses no join clauses, but it could still have required
549 : : * parameterization due to LATERAL refs in its tlist.
550 : : */
551 : 1000 : Relids required_outer = rel->lateral_relids;
552 : :
553 : 1000 : add_path(rel, (Path *) create_tidrangescan_path(root, rel,
554 : : tidrangequals,
555 : : required_outer,
556 : : 0));
557 : :
558 : : /* If appropriate, consider parallel tid range scan. */
20 drowley@postgresql.o 559 [ + + + - ]:GNC 1000 : if (rel->consider_parallel && required_outer == NULL)
560 : : {
561 : : int parallel_workers;
562 : :
563 : 111 : parallel_workers = compute_parallel_worker(rel, rel->pages, -1,
564 : : max_parallel_workers_per_gather);
565 : :
566 [ + + ]: 111 : if (parallel_workers > 0)
567 : 24 : add_partial_path(rel, (Path *) create_tidrangescan_path(root,
568 : : rel,
569 : : tidrangequals,
570 : : required_outer,
571 : : parallel_workers));
572 : : }
573 : : }
574 : :
575 : : /*
576 : : * Try to generate parameterized TidPaths using equality clauses extracted
577 : : * from EquivalenceClasses. (This is important since simple "t1.ctid =
578 : : * t2.ctid" clauses will turn into ECs.)
579 : : */
2544 tgl@sss.pgh.pa.us 580 [ + + ]:CBC 209661 : if (rel->has_eclass_joins)
581 : : {
582 : : List *clauses;
583 : :
584 : : /* Generate clauses, skipping any that join to lateral_referencers */
585 : 71041 : clauses = generate_implied_equalities_for_column(root,
586 : : rel,
587 : : ec_member_matches_ctid,
588 : : NULL,
589 : : rel->lateral_referencers);
590 : :
591 : : /* Generate a path for each usable join clause */
592 : 71041 : BuildParameterizedTidPaths(root, rel, clauses);
593 : : }
594 : :
595 : : /*
596 : : * Also consider parameterized TidPaths using "loose" join quals. Quals
597 : : * of the form "t1.ctid = t2.ctid" would turn into these if they are outer
598 : : * join quals, for example.
599 : : */
600 : 209661 : BuildParameterizedTidPaths(root, rel, rel->joininfo);
601 : :
513 rhaas@postgresql.org 602 : 209661 : return false;
603 : : }
|