Age Owner 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-2023, 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/clauses.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
1561 tgl 56 CBC 340363 : IsCTIDVar(Var *var, RelOptInfo *rel)
57 : {
58 : /* The vartype check is strictly paranoia */
59 340363 : if (var->varattno == SelfItemPointerAttributeNumber &&
60 710 : var->vartype == TIDOID &&
61 710 : var->varno == rel->relid &&
69 tgl 62 GNC 653 : var->varnullingrels == NULL &&
1561 tgl 63 CBC 653 : var->varlevelsup == 0)
64 653 : return true;
65 339710 : return false;
1561 tgl 66 ECB : }
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
771 drowley 77 GIC 340427 : IsBinaryTidClause(RestrictInfo *rinfo, RelOptInfo *rel)
8538 bruce 78 ECB : {
79 : OpExpr *node;
80 : Node *arg1,
81 : *arg2,
82 : *other;
83 : Relids other_relids;
84 :
85 : /* Must be an OpExpr */
1561 tgl 86 GIC 340427 : if (!is_opclause(rinfo->clause))
1561 tgl 87 CBC 70386 : return false;
88 270041 : node = (OpExpr *) rinfo->clause;
8538 bruce 89 ECB :
90 : /* OpExpr must have two arguments */
771 drowley 91 GIC 270041 : if (list_length(node->args) != 2)
6343 tgl 92 CBC 24 : return false;
6892 neilc 93 270017 : arg1 = linitial(node->args);
8538 bruce 94 270017 : arg2 = lsecond(node->args);
8538 bruce 95 ECB :
96 : /* Look for CTID as either argument */
6438 tgl 97 GIC 270017 : other = NULL;
1561 tgl 98 CBC 270017 : other_relids = NULL;
99 525563 : if (arg1 && IsA(arg1, Var) &&
100 255546 : IsCTIDVar((Var *) arg1, rel))
8538 bruce 101 ECB : {
1561 tgl 102 GIC 461 : other = arg2;
1561 tgl 103 CBC 461 : other_relids = rinfo->right_relids;
8538 bruce 104 ECB : }
1561 tgl 105 GIC 315483 : if (!other && arg2 && IsA(arg2, Var) &&
1561 tgl 106 CBC 45466 : IsCTIDVar((Var *) arg2, rel))
8538 bruce 107 ECB : {
1561 tgl 108 GIC 111 : other = arg1;
1561 tgl 109 CBC 111 : other_relids = rinfo->left_relids;
8538 bruce 110 ECB : }
6438 tgl 111 GIC 270017 : if (!other)
6343 tgl 112 CBC 269445 : return false;
6438 tgl 113 ECB :
114 : /* The other argument must be a pseudoconstant */
1561 tgl 115 GIC 1144 : if (bms_is_member(rel->relid, other_relids) ||
1561 tgl 116 CBC 572 : contain_volatile_functions(other))
6343 tgl 117 LBC 0 : return false;
6343 tgl 118 EUB :
6343 tgl 119 GIC 572 : return true; /* success */
6343 tgl 120 ECB : }
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
771 drowley 131 GIC 191925 : IsTidEqualClause(RestrictInfo *rinfo, RelOptInfo *rel)
771 drowley 132 ECB : {
771 drowley 133 GIC 191925 : if (!IsBinaryTidClause(rinfo, rel))
771 drowley 134 CBC 191558 : return false;
771 drowley 135 ECB :
771 drowley 136 GIC 367 : if (((OpExpr *) rinfo->clause)->opno == TIDEqualOperator)
771 drowley 137 CBC 185 : return true;
771 drowley 138 ECB :
771 drowley 139 GIC 182 : return false;
771 drowley 140 ECB : }
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
771 drowley 151 GIC 148502 : IsTidRangeClause(RestrictInfo *rinfo, RelOptInfo *rel)
771 drowley 152 ECB : {
153 : Oid opno;
154 :
771 drowley 155 GIC 148502 : if (!IsBinaryTidClause(rinfo, rel))
771 drowley 156 CBC 148297 : return false;
157 205 : opno = ((OpExpr *) rinfo->clause)->opno;
771 drowley 158 ECB :
771 drowley 159 GIC 205 : if (opno == TIDLessOperator || opno == TIDLessEqOperator ||
771 drowley 160 CBC 116 : opno == TIDGreaterOperator || opno == TIDGreaterEqOperator)
161 116 : return true;
771 drowley 162 ECB :
771 drowley 163 GIC 89 : return false;
771 drowley 164 ECB : }
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
808 tgl 173 GIC 145416 : IsTidEqualAnyClause(PlannerInfo *root, RestrictInfo *rinfo, RelOptInfo *rel)
6343 tgl 174 ECB : {
175 : ScalarArrayOpExpr *node;
176 : Node *arg1,
177 : *arg2;
178 :
179 : /* Must be a ScalarArrayOpExpr */
1561 tgl 180 GIC 145416 : if (!(rinfo->clause && IsA(rinfo->clause, ScalarArrayOpExpr)))
1561 tgl 181 CBC 138250 : return false;
182 7166 : node = (ScalarArrayOpExpr *) rinfo->clause;
1561 tgl 183 ECB :
184 : /* Operator must be tideq */
6343 tgl 185 GIC 7166 : if (node->opno != TIDEqualOperator)
6343 tgl 186 CBC 7151 : return false;
187 15 : if (!node->useOr)
6343 tgl 188 LBC 0 : return false;
6343 tgl 189 GBC 15 : Assert(list_length(node->args) == 2);
6343 tgl 190 CBC 15 : arg1 = linitial(node->args);
191 15 : arg2 = lsecond(node->args);
6343 tgl 192 ECB :
193 : /* CTID must be first argument */
1561 tgl 194 GIC 30 : if (arg1 && IsA(arg1, Var) &&
1561 tgl 195 CBC 15 : IsCTIDVar((Var *) arg1, rel))
6343 tgl 196 ECB : {
197 : /* The other argument must be a pseudoconstant */
808 tgl 198 GIC 30 : if (bms_is_member(rel->relid, pull_varnos(root, arg2)) ||
1561 tgl 199 CBC 15 : contain_volatile_functions(arg2))
1561 tgl 200 LBC 0 : return false;
6438 tgl 201 EUB :
1561 tgl 202 GIC 15 : return true; /* success */
6343 tgl 203 ECB : }
204 :
6343 tgl 205 UIC 0 : return false;
8538 bruce 206 EUB : }
207 :
208 : /*
209 : * Check to see if a RestrictInfo is a CurrentOfExpr referencing "rel".
210 : */
211 : static bool
1561 tgl 212 GIC 145401 : IsCurrentOfClause(RestrictInfo *rinfo, RelOptInfo *rel)
1561 tgl 213 ECB : {
214 : CurrentOfExpr *node;
215 :
216 : /* Must be a CurrentOfExpr */
1561 tgl 217 GIC 145401 : if (!(rinfo->clause && IsA(rinfo->clause, CurrentOfExpr)))
1561 tgl 218 CBC 145205 : return false;
219 196 : node = (CurrentOfExpr *) rinfo->clause;
1561 tgl 220 ECB :
221 : /* If it references this rel, we're good */
1561 tgl 222 GIC 196 : if (node->cvarno == rel->relid)
1561 tgl 223 CBC 196 : return true;
1561 tgl 224 ECB :
1561 tgl 225 UIC 0 : return false;
1561 tgl 226 EUB : }
227 :
228 : /*
229 : * Extract a set of CTID conditions from the given RestrictInfo
230 : *
231 : * Returns a List of CTID qual RestrictInfos for the specified rel (with
232 : * implicit OR semantics across the list), or NIL if there are no usable
233 : * conditions.
234 : *
235 : * This function considers only base cases; AND/OR combination is handled
236 : * below. Therefore the returned List never has more than one element.
237 : * (Using a List may seem a bit weird, but it simplifies the caller.)
238 : */
239 : static List *
808 tgl 240 GIC 148878 : TidQualFromRestrictInfo(PlannerInfo *root, RestrictInfo *rinfo, RelOptInfo *rel)
1561 tgl 241 ECB : {
242 : /*
243 : * We may ignore pseudoconstant clauses (they can't contain Vars, so could
244 : * not match anyway).
245 : */
1561 tgl 246 GIC 148878 : if (rinfo->pseudoconstant)
1561 tgl 247 CBC 2541 : return NIL;
1561 tgl 248 ECB :
249 : /*
250 : * If clause must wait till after some lower-security-level restriction
251 : * clause, reject it.
252 : */
1561 tgl 253 GIC 146337 : if (!restriction_is_securely_promotable(rinfo, rel))
1561 tgl 254 CBC 814 : return NIL;
1561 tgl 255 ECB :
256 : /*
257 : * Check all base cases. If we get a match, return the clause.
258 : */
1561 tgl 259 GIC 290939 : if (IsTidEqualClause(rinfo, rel) ||
808 tgl 260 CBC 290817 : IsTidEqualAnyClause(root, rinfo, rel) ||
1561 261 145401 : IsCurrentOfClause(rinfo, rel))
262 318 : return list_make1(rinfo);
1561 tgl 263 ECB :
1561 tgl 264 GIC 145205 : return NIL;
1561 tgl 265 ECB : }
266 :
267 : /*
268 : * Extract a set of CTID conditions from implicit-AND List of RestrictInfos
269 : *
270 : * Returns a List of CTID qual RestrictInfos for the specified rel (with
271 : * implicit OR semantics across the list), or NIL if there are no usable
272 : * equality conditions.
273 : *
274 : * This function is just concerned with handling AND/OR recursion.
275 : */
276 : static List *
808 tgl 277 GIC 157672 : TidQualFromRestrictInfoList(PlannerInfo *root, List *rlist, RelOptInfo *rel)
8538 bruce 278 ECB : {
6343 tgl 279 GIC 157672 : List *rlst = NIL;
6892 neilc 280 ECB : ListCell *l;
281 :
1561 tgl 282 GIC 306541 : foreach(l, rlist)
8538 bruce 283 ECB : {
1561 tgl 284 GIC 149187 : RestrictInfo *rinfo = lfirst_node(RestrictInfo, l);
1561 tgl 285 ECB :
1561 tgl 286 GIC 149187 : if (restriction_is_or_clause(rinfo))
8538 bruce 287 ECB : {
288 : ListCell *j;
289 :
290 : /*
291 : * We must be able to extract a CTID condition from every
292 : * sub-clause of an OR, or we can't use it.
293 : */
1561 tgl 294 GIC 1647 : foreach(j, ((BoolExpr *) rinfo->orclause)->args)
8538 bruce 295 ECB : {
1561 tgl 296 GIC 1635 : Node *orarg = (Node *) lfirst(j);
1561 tgl 297 ECB : List *sublist;
298 :
299 : /* OR arguments should be ANDs or sub-RestrictInfos */
1531 tgl 300 GIC 1635 : if (is_andclause(orarg))
1561 tgl 301 ECB : {
1561 tgl 302 GIC 321 : List *andargs = ((BoolExpr *) orarg)->args;
1561 tgl 303 ECB :
304 : /* Recurse in case there are sub-ORs */
808 tgl 305 GIC 321 : sublist = TidQualFromRestrictInfoList(root, andargs, rel);
1561 tgl 306 ECB : }
307 : else
308 : {
186 drowley 309 GNC 1314 : RestrictInfo *ri = castNode(RestrictInfo, orarg);
1561 tgl 310 ECB :
186 drowley 311 GNC 1314 : Assert(!restriction_is_or_clause(ri));
312 1314 : sublist = TidQualFromRestrictInfo(root, ri, rel);
1561 tgl 313 ECB : }
314 :
315 : /*
316 : * If nothing found in this arm, we can't do anything with
317 : * this OR clause.
318 : */
1561 tgl 319 GIC 1635 : if (sublist == NIL)
1561 tgl 320 ECB : {
1561 tgl 321 GIC 1611 : rlst = NIL; /* forget anything we had */
1561 tgl 322 CBC 1611 : break; /* out of loop over OR args */
1561 tgl 323 ECB : }
324 :
325 : /*
326 : * OK, continue constructing implicitly-OR'ed result list.
327 : */
1561 tgl 328 GIC 24 : rlst = list_concat(rlst, sublist);
8538 bruce 329 ECB : }
330 : }
331 : else
332 : {
333 : /* Not an OR clause, so handle base cases */
808 tgl 334 GIC 147564 : rlst = TidQualFromRestrictInfo(root, rinfo, rel);
1561 tgl 335 ECB : }
336 :
337 : /*
338 : * Stop as soon as we find any usable CTID condition. In theory we
339 : * could get CTID equality conditions from different AND'ed clauses,
340 : * in which case we could try to pick the most efficient one. In
341 : * practice, such usage seems very unlikely, so we don't bother; we
342 : * just exit as soon as we find the first candidate.
343 : */
1561 tgl 344 GIC 149187 : if (rlst)
1561 tgl 345 CBC 318 : break;
8538 bruce 346 ECB : }
347 :
8538 bruce 348 GIC 157672 : return rlst;
8397 bruce 349 ECB : }
350 :
351 : /*
352 : * Extract a set of CTID range conditions from implicit-AND List of RestrictInfos
353 : *
354 : * Returns a List of CTID range qual RestrictInfos for the specified rel
355 : * (with implicit AND semantics across the list), or NIL if there are no
356 : * usable range conditions or if the rel's table AM does not support TID range
357 : * scans.
358 : */
359 : static List *
771 drowley 360 GIC 157351 : TidRangeQualFromRestrictInfoList(List *rlist, RelOptInfo *rel)
771 drowley 361 ECB : {
771 drowley 362 GIC 157351 : List *rlst = NIL;
771 drowley 363 ECB : ListCell *l;
364 :
771 drowley 365 GIC 157351 : if ((rel->amflags & AMFLAG_HAS_TID_RANGE) == 0)
771 drowley 366 LBC 0 : return NIL;
771 drowley 367 EUB :
771 drowley 368 GIC 305853 : foreach(l, rlist)
771 drowley 369 ECB : {
771 drowley 370 GIC 148502 : RestrictInfo *rinfo = lfirst_node(RestrictInfo, l);
771 drowley 371 ECB :
771 drowley 372 GIC 148502 : if (IsTidRangeClause(rinfo, rel))
771 drowley 373 CBC 116 : rlst = lappend(rlst, rinfo);
771 drowley 374 ECB : }
375 :
771 drowley 376 GIC 157351 : return rlst;
771 drowley 377 ECB : }
378 :
379 : /*
380 : * Given a list of join clauses involving our rel, create a parameterized
381 : * TidPath for each one that is a suitable TidEqual clause.
382 : *
383 : * In principle we could combine clauses that reference the same outer rels,
384 : * but it doesn't seem like such cases would arise often enough to be worth
385 : * troubling over.
386 : */
387 : static void
1561 tgl 388 GIC 201481 : BuildParameterizedTidPaths(PlannerInfo *root, RelOptInfo *rel, List *clauses)
8538 bruce 389 ECB : {
390 : ListCell *l;
391 :
1561 tgl 392 GIC 251546 : foreach(l, clauses)
8538 bruce 393 ECB : {
1561 tgl 394 GIC 50065 : RestrictInfo *rinfo = lfirst_node(RestrictInfo, l);
1561 tgl 395 ECB : List *tidquals;
396 : Relids required_outer;
397 :
398 : /*
399 : * Validate whether each clause is actually usable; we must check this
400 : * even when examining clauses generated from an EquivalenceClass,
401 : * since they might not satisfy the restriction on not having Vars of
402 : * our rel on the other side, or somebody might've built an operator
403 : * class that accepts type "tid" but has other operators in it.
404 : *
405 : * We currently consider only TidEqual join clauses. In principle we
406 : * might find a suitable ScalarArrayOpExpr in the rel's joininfo list,
407 : * but it seems unlikely to be worth expending the cycles to check.
408 : * And we definitely won't find a CurrentOfExpr here. Hence, we don't
409 : * use TidQualFromRestrictInfo; but this must match that function
410 : * otherwise.
411 : */
1560 tgl 412 GIC 50065 : if (rinfo->pseudoconstant ||
1560 tgl 413 CBC 46402 : !restriction_is_securely_promotable(rinfo, rel) ||
414 46402 : !IsTidEqualClause(rinfo, rel))
2272 415 49993 : continue;
2272 tgl 416 ECB :
417 : /*
418 : * Check if clause can be moved to this rel; this is probably
419 : * redundant when considering EC-derived clauses, but we must check it
420 : * for "loose" join clauses.
421 : */
1561 tgl 422 GIC 78 : if (!join_clause_is_movable_to(rinfo, rel))
1561 tgl 423 CBC 6 : continue;
1561 tgl 424 ECB :
425 : /* OK, make list of clauses for this path */
1561 tgl 426 GIC 72 : tidquals = list_make1(rinfo);
1561 tgl 427 ECB :
428 : /* Compute required outer rels for this path */
1561 tgl 429 GIC 72 : required_outer = bms_union(rinfo->required_relids, rel->lateral_relids);
1561 tgl 430 CBC 72 : required_outer = bms_del_member(required_outer, rel->relid);
1561 tgl 431 ECB :
1561 tgl 432 GIC 72 : add_path(rel, (Path *) create_tidscan_path(root, rel, tidquals,
1561 tgl 433 ECB : required_outer));
434 : }
1561 tgl 435 GIC 201481 : }
1561 tgl 436 ECB :
437 : /*
438 : * Test whether an EquivalenceClass member matches our rel's CTID Var.
439 : *
440 : * This is a callback for use by generate_implied_equalities_for_column.
441 : */
442 : static bool
1561 tgl 443 GIC 40534 : ec_member_matches_ctid(PlannerInfo *root, RelOptInfo *rel,
1561 tgl 444 ECB : EquivalenceClass *ec, EquivalenceMember *em,
445 : void *arg)
446 : {
1561 tgl 447 GIC 79870 : if (em->em_expr && IsA(em->em_expr, Var) &&
1561 tgl 448 CBC 39336 : IsCTIDVar((Var *) em->em_expr, rel))
449 66 : return true;
450 40468 : return false;
8538 bruce 451 ECB : }
452 :
453 : /*
454 : * create_tidscan_paths
455 : * Create paths corresponding to direct TID scans of the given rel.
456 : *
457 : * Candidate paths are added to the rel's pathlist (using add_path).
458 : */
459 : void
6517 tgl 460 GIC 157351 : create_tidscan_paths(PlannerInfo *root, RelOptInfo *rel)
8538 bruce 461 ECB : {
462 : List *tidquals;
463 : List *tidrangequals;
464 :
465 : /*
466 : * If any suitable quals exist in the rel's baserestrict list, generate a
467 : * plain (unparameterized) TidPath with them.
468 : */
808 tgl 469 GIC 157351 : tidquals = TidQualFromRestrictInfoList(root, rel->baserestrictinfo, rel);
8397 bruce 470 ECB :
771 drowley 471 GIC 157351 : if (tidquals != NIL)
1561 tgl 472 ECB : {
473 : /*
474 : * This path uses no join clauses, but it could still have required
475 : * parameterization due to LATERAL refs in its tlist.
476 : */
1561 tgl 477 GIC 306 : Relids required_outer = rel->lateral_relids;
1561 tgl 478 ECB :
3878 tgl 479 GIC 306 : add_path(rel, (Path *) create_tidscan_path(root, rel, tidquals,
3878 tgl 480 ECB : required_outer));
481 : }
482 :
483 : /*
484 : * If there are range quals in the baserestrict list, generate a
485 : * TidRangePath.
486 : */
771 drowley 487 GIC 157351 : tidrangequals = TidRangeQualFromRestrictInfoList(rel->baserestrictinfo,
771 drowley 488 ECB : rel);
489 :
771 drowley 490 GIC 157351 : if (tidrangequals != NIL)
771 drowley 491 ECB : {
492 : /*
493 : * This path uses no join clauses, but it could still have required
494 : * parameterization due to LATERAL refs in its tlist.
495 : */
771 drowley 496 GIC 101 : Relids required_outer = rel->lateral_relids;
771 drowley 497 ECB :
771 drowley 498 GIC 101 : add_path(rel, (Path *) create_tidrangescan_path(root, rel,
771 drowley 499 ECB : tidrangequals,
500 : required_outer));
501 : }
502 :
503 : /*
504 : * Try to generate parameterized TidPaths using equality clauses extracted
505 : * from EquivalenceClasses. (This is important since simple "t1.ctid =
506 : * t2.ctid" clauses will turn into ECs.)
507 : */
1561 tgl 508 GIC 157351 : if (rel->has_eclass_joins)
1561 tgl 509 ECB : {
510 : List *clauses;
511 :
512 : /* Generate clauses, skipping any that join to lateral_referencers */
1561 tgl 513 GIC 44130 : clauses = generate_implied_equalities_for_column(root,
1561 tgl 514 ECB : rel,
515 : ec_member_matches_ctid,
516 : NULL,
517 : rel->lateral_referencers);
518 :
519 : /* Generate a path for each usable join clause */
1561 tgl 520 GIC 44130 : BuildParameterizedTidPaths(root, rel, clauses);
1561 tgl 521 ECB : }
522 :
523 : /*
524 : * Also consider parameterized TidPaths using "loose" join quals. Quals
525 : * of the form "t1.ctid = t2.ctid" would turn into these if they are outer
526 : * join quals, for example.
527 : */
1561 tgl 528 GIC 157351 : BuildParameterizedTidPaths(root, rel, rel->joininfo);
8538 bruce 529 CBC 157351 : }
|