X-Git-Url: https://granicus.if.org/sourcecode?a=blobdiff_plain;f=src%2Fbackend%2Foptimizer%2Fpath%2Fallpaths.c;h=458dae0489c029bd743c75c82f8e5102067e89bf;hb=46c508fbcf98ac334f1e831d21021d731c882fbb;hp=19bf7340ad454cda081c85d83272f66e6de43c4e;hpb=e2fa76d80ba571d4de8992de6386536867250474;p=postgresql diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c index 19bf7340ad..458dae0489 100644 --- a/src/backend/optimizer/path/allpaths.c +++ b/src/backend/optimizer/path/allpaths.c @@ -18,6 +18,7 @@ #include #include "catalog/pg_class.h" +#include "foreign/fdwapi.h" #include "nodes/nodeFuncs.h" #ifdef OPTIMIZER_DEBUG #include "nodes/print.h" @@ -49,25 +50,24 @@ join_search_hook_type join_search_hook = NULL; static void set_base_rel_sizes(PlannerInfo *root); static void set_base_rel_pathlists(PlannerInfo *root); static void set_rel_size(PlannerInfo *root, RelOptInfo *rel, - Index rti, RangeTblEntry *rte); + Index rti, RangeTblEntry *rte); static void set_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, Index rti, RangeTblEntry *rte); static void set_plain_rel_size(PlannerInfo *root, RelOptInfo *rel, - RangeTblEntry *rte); + RangeTblEntry *rte); static void set_plain_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte); static void set_foreign_size(PlannerInfo *root, RelOptInfo *rel, - RangeTblEntry *rte); + RangeTblEntry *rte); static void set_foreign_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte); static void set_append_rel_size(PlannerInfo *root, RelOptInfo *rel, - Index rti, RangeTblEntry *rte); + Index rti, RangeTblEntry *rte); static void set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, Index rti, RangeTblEntry *rte); static void generate_mergeappend_paths(PlannerInfo *root, RelOptInfo *rel, List *live_childrels, - List *all_child_pathkeys, - Relids required_outer); + List *all_child_pathkeys); static List *accumulate_append_subpath(List *subpaths, Path *path); static void set_dummy_rel_pathlist(RelOptInfo *rel); static void set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel, @@ -118,7 +118,7 @@ make_one_rel(PlannerInfo *root, List *joinlist) if (brel == NULL) continue; - Assert(brel->relid == rti); /* sanity check on array */ + Assert(brel->relid == rti); /* sanity check on array */ /* ignore RTEs that are "other rels" */ if (brel->reloptkind != RELOPT_BASEREL) @@ -211,7 +211,7 @@ set_base_rel_pathlists(PlannerInfo *root) */ static void set_rel_size(PlannerInfo *root, RelOptInfo *rel, - Index rti, RangeTblEntry *rte) + Index rti, RangeTblEntry *rte) { if (rel->reloptkind == RELOPT_BASEREL && relation_excluded_by_constraints(root, rel, rte)) @@ -251,9 +251,11 @@ set_rel_size(PlannerInfo *root, RelOptInfo *rel, } break; case RTE_SUBQUERY: + /* - * Subqueries don't support parameterized paths, so just go - * ahead and build their paths immediately. + * Subqueries don't support making a choice between + * parameterized and unparameterized paths, so just go ahead + * and build their paths immediately. */ set_subquery_pathlist(root, rel, rti, rte); break; @@ -264,9 +266,11 @@ set_rel_size(PlannerInfo *root, RelOptInfo *rel, set_values_size_estimates(root, rel); break; case RTE_CTE: + /* - * CTEs don't support parameterized paths, so just go ahead - * and build their paths immediately. + * CTEs don't support making a choice between parameterized + * and unparameterized paths, so just go ahead and build their + * paths immediately. */ if (rte->self_reference) set_worktable_pathlist(root, rel, rte); @@ -373,8 +377,18 @@ set_plain_rel_size(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte) static void set_plain_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte) { + Relids required_outer; + + /* + * We don't support pushing join clauses into the quals of a seqscan, but + * it could still have required parameterization due to LATERAL refs in + * its tlist. (That can only happen if the seqscan is on a relation + * pulled up out of a UNION ALL appendrel.) + */ + required_outer = rel->lateral_relids; + /* Consider sequential scan */ - add_path(rel, create_seqscan_path(root, rel)); + add_path(rel, create_seqscan_path(root, rel, required_outer)); /* Consider index scans */ create_index_paths(root, rel); @@ -395,19 +409,25 @@ set_foreign_size(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte) { /* Mark rel with estimated output rows, width, etc */ set_foreign_size_estimates(root, rel); + + /* Get FDW routine pointers for the rel */ + rel->fdwroutine = GetFdwRoutineByRelId(rte->relid); + + /* Let FDW adjust the size estimates, if it can */ + rel->fdwroutine->GetForeignRelSize(root, rel, rte->relid); } /* * set_foreign_pathlist - * Build the (single) access path for a foreign table RTE + * Build access paths for a foreign table RTE */ static void set_foreign_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte) { - /* Generate appropriate path */ - add_path(rel, (Path *) create_foreignscan_path(root, rel)); + /* Call the FDW's GetForeignPaths function to generate path(s) */ + rel->fdwroutine->GetForeignPaths(root, rel, rte->relid); - /* Select cheapest path (pretty easy in this case...) */ + /* Select cheapest path */ set_cheapest(rel); } @@ -492,7 +512,8 @@ set_append_rel_size(PlannerInfo *root, RelOptInfo *rel, * reconstitute the RestrictInfo layer. */ childquals = get_all_actual_clauses(rel->baserestrictinfo); - childquals = (List *) adjust_appendrel_attrs((Node *) childquals, + childquals = (List *) adjust_appendrel_attrs(root, + (Node *) childquals, appinfo); childqual = eval_const_expressions(root, (Node *) make_ands_explicit(childquals)); @@ -526,16 +547,18 @@ set_append_rel_size(PlannerInfo *root, RelOptInfo *rel, * CE failed, so finish copying/modifying targetlist and join quals. * * Note: the resulting childrel->reltargetlist may contain arbitrary - * expressions, which normally would not occur in a reltargetlist. - * That is okay because nothing outside of this routine will look at - * the child rel's reltargetlist. We do have to cope with the case - * while constructing attr_widths estimates below, though. + * expressions, which otherwise would not occur in a reltargetlist. + * Code that might be looking at an appendrel child must cope with + * such. Note in particular that "arbitrary expression" can include + * "Var belonging to another relation", due to LATERAL references. */ childrel->joininfo = (List *) - adjust_appendrel_attrs((Node *) rel->joininfo, + adjust_appendrel_attrs(root, + (Node *) rel->joininfo, appinfo); childrel->reltargetlist = (List *) - adjust_appendrel_attrs((Node *) rel->reltargetlist, + adjust_appendrel_attrs(root, + (Node *) rel->reltargetlist, appinfo); /* @@ -565,8 +588,8 @@ set_append_rel_size(PlannerInfo *root, RelOptInfo *rel, /* * It is possible that constraint exclusion detected a contradiction - * within a child subquery, even though we didn't prove one above. - * If so, we can skip this child. + * within a child subquery, even though we didn't prove one above. If + * so, we can skip this child. */ if (IS_DUMMY_REL(childrel)) continue; @@ -581,7 +604,7 @@ set_append_rel_size(PlannerInfo *root, RelOptInfo *rel, /* * Accumulate per-column estimates too. We need not do anything - * for PlaceHolderVars in the parent list. If child expression + * for PlaceHolderVars in the parent list. If child expression * isn't a Var, or we didn't record a width estimate for it, we * have to fall back on a datatype-based estimate. * @@ -598,9 +621,10 @@ set_append_rel_size(PlannerInfo *root, RelOptInfo *rel, int pndx = parentvar->varattno - rel->min_attr; int32 child_width = 0; - if (IsA(childvar, Var)) + if (IsA(childvar, Var) && + ((Var *) childvar)->varno == childrel->relid) { - int cndx = ((Var *) childvar)->varattno - childrel->min_attr; + int cndx = ((Var *) childvar)->varattno - childrel->min_attr; child_width = childrel->attr_widths[cndx]; } @@ -649,13 +673,14 @@ set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, int parentRTindex = rti; List *live_childrels = NIL; List *subpaths = NIL; + bool subpaths_valid = true; List *all_child_pathkeys = NIL; List *all_child_outers = NIL; ListCell *l; /* * Generate access paths for each member relation, and remember the - * cheapest path for each one. Also, identify all pathkeys (orderings) + * cheapest path for each one. Also, identify all pathkeys (orderings) * and parameterizations (required_outer sets) available for the member * relations. */ @@ -688,18 +713,24 @@ set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, continue; /* - * Child is live, so add its cheapest access path to the Append path - * we are constructing for the parent. + * Child is live, so add it to the live_childrels list for use below. */ - subpaths = accumulate_append_subpath(subpaths, - childrel->cheapest_total_path); - - /* Remember which childrels are live, for logic below */ live_childrels = lappend(live_childrels, childrel); + /* + * If child has an unparameterized cheapest-total path, add that to + * the unparameterized Append path we are constructing for the parent. + * If not, there's no workable unparameterized path. + */ + if (childrel->cheapest_total_path->param_info == NULL) + subpaths = accumulate_append_subpath(subpaths, + childrel->cheapest_total_path); + else + subpaths_valid = false; + /* * Collect lists of all the available path orderings and - * parameterizations for all the children. We use these as a + * parameterizations for all the children. We use these as a * heuristic to indicate which sort orderings and parameterizations we * should build Append and MergeAppend paths for. */ @@ -707,7 +738,7 @@ set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, { Path *childpath = (Path *) lfirst(lcp); List *childkeys = childpath->pathkeys; - Relids childouter = childpath->required_outer; + Relids childouter = PATH_REQ_OUTER(childpath); /* Unsorted paths don't contribute to pathkey list */ if (childkeys != NIL) @@ -744,7 +775,7 @@ set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, /* Have we already seen this param set? */ foreach(lco, all_child_outers) { - Relids existing_outers = (Relids) lfirst(lco); + Relids existing_outers = (Relids) lfirst(lco); if (bms_equal(existing_outers, childouter)) { @@ -763,33 +794,42 @@ set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, } /* - * Next, build an unordered, unparameterized Append path for the rel. - * (Note: this is correct even if we have zero or one live subpath due to - * constraint exclusion.) + * If we found unparameterized paths for all children, build an unordered, + * unparameterized Append path for the rel. (Note: this is correct even + * if we have zero or one live subpath due to constraint exclusion.) */ - add_path(rel, (Path *) create_append_path(rel, subpaths)); + if (subpaths_valid) + add_path(rel, (Path *) create_append_path(rel, subpaths, NULL)); /* - * Build unparameterized MergeAppend paths based on the collected list of - * child pathkeys. + * Also build unparameterized MergeAppend paths based on the collected + * list of child pathkeys. */ - generate_mergeappend_paths(root, rel, live_childrels, - all_child_pathkeys, NULL); + if (subpaths_valid) + generate_mergeappend_paths(root, rel, live_childrels, + all_child_pathkeys); /* - * Build Append and MergeAppend paths for each parameterization seen - * among the child rels. (This may look pretty expensive, but in most - * cases of practical interest, the child relations will tend to expose - * the same parameterizations and pathkeys, so that not that many cases - * actually get considered here.) + * Build Append paths for each parameterization seen among the child rels. + * (This may look pretty expensive, but in most cases of practical + * interest, the child rels will expose mostly the same parameterizations, + * so that not that many cases actually get considered here.) + * + * The Append node itself cannot enforce quals, so all qual checking must + * be done in the child paths. This means that to have a parameterized + * Append path, we must have the exact same parameterization for each + * child path; otherwise some children might be failing to check the + * moved-down quals. To make them match up, we can try to increase the + * parameterization of lesser-parameterized paths. */ foreach(l, all_child_outers) { - Relids required_outer = (Relids) lfirst(l); + Relids required_outer = (Relids) lfirst(l); ListCell *lcr; /* Select the child paths for an Append with this parameterization */ subpaths = NIL; + subpaths_valid = true; foreach(lcr, live_childrels) { RelOptInfo *childrel = (RelOptInfo *) lfirst(lcr); @@ -802,13 +842,24 @@ set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, TOTAL_COST); Assert(cheapest_total != NULL); + /* Children must have exactly the desired parameterization */ + if (!bms_equal(PATH_REQ_OUTER(cheapest_total), required_outer)) + { + cheapest_total = reparameterize_path(root, cheapest_total, + required_outer, 1.0); + if (cheapest_total == NULL) + { + subpaths_valid = false; + break; + } + } + subpaths = accumulate_append_subpath(subpaths, cheapest_total); } - add_path(rel, (Path *) create_append_path(rel, subpaths)); - /* And build parameterized MergeAppend paths */ - generate_mergeappend_paths(root, rel, live_childrels, - all_child_pathkeys, required_outer); + if (subpaths_valid) + add_path(rel, (Path *) + create_append_path(rel, subpaths, required_outer)); } /* Select cheapest paths */ @@ -820,18 +871,28 @@ set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, * Generate MergeAppend paths for an append relation * * Generate a path for each ordering (pathkey list) appearing in - * all_child_pathkeys. If required_outer isn't NULL, accept paths having - * those relations as required outer relations. + * all_child_pathkeys. * * We consider both cheapest-startup and cheapest-total cases, ie, for each * interesting ordering, collect all the cheapest startup subpaths and all the * cheapest total paths, and build a MergeAppend path for each case. + * + * We don't currently generate any parameterized MergeAppend paths. While + * it would not take much more code here to do so, it's very unclear that it + * is worth the planning cycles to investigate such paths: there's little + * use for an ordered path on the inside of a nestloop. In fact, it's likely + * that the current coding of add_path would reject such paths out of hand, + * because add_path gives no credit for sort ordering of parameterized paths, + * and a parameterized MergeAppend is going to be more expensive than the + * corresponding parameterized Append path. If we ever try harder to support + * parameterized mergejoin plans, it might be worth adding support for + * parameterized MergeAppends to feed such joins. (See notes in + * optimizer/README for why that might not ever happen, though.) */ static void generate_mergeappend_paths(PlannerInfo *root, RelOptInfo *rel, List *live_childrels, - List *all_child_pathkeys, - Relids required_outer) + List *all_child_pathkeys) { ListCell *lcp; @@ -854,31 +915,24 @@ generate_mergeappend_paths(PlannerInfo *root, RelOptInfo *rel, cheapest_startup = get_cheapest_path_for_pathkeys(childrel->pathlist, pathkeys, - required_outer, + NULL, STARTUP_COST); cheapest_total = get_cheapest_path_for_pathkeys(childrel->pathlist, pathkeys, - required_outer, + NULL, TOTAL_COST); /* * If we can't find any paths with the right order just use the - * cheapest-total path; we'll have to sort it later. We can - * use the cheapest path for the parameterization, though. + * cheapest-total path; we'll have to sort it later. */ if (cheapest_startup == NULL || cheapest_total == NULL) { - if (required_outer) - cheapest_startup = cheapest_total = - get_cheapest_path_for_pathkeys(childrel->pathlist, - NIL, - required_outer, - TOTAL_COST); - else - cheapest_startup = cheapest_total = - childrel->cheapest_total_path; - Assert(cheapest_total != NULL); + cheapest_startup = cheapest_total = + childrel->cheapest_total_path; + /* Assert we do have an unparameterized path for this child */ + Assert(cheapest_total->param_info == NULL); } /* @@ -899,12 +953,14 @@ generate_mergeappend_paths(PlannerInfo *root, RelOptInfo *rel, add_path(rel, (Path *) create_merge_append_path(root, rel, startup_subpaths, - pathkeys)); + pathkeys, + NULL)); if (startup_neq_total) add_path(rel, (Path *) create_merge_append_path(root, rel, total_subpaths, - pathkeys)); + pathkeys, + NULL)); } } @@ -948,7 +1004,7 @@ set_dummy_rel_pathlist(RelOptInfo *rel) /* Discard any pre-existing paths; no further need for them */ rel->pathlist = NIL; - add_path(rel, (Path *) create_append_path(rel, NIL)); + add_path(rel, (Path *) create_append_path(rel, NIL, NULL)); /* Select cheapest path (pretty easy in this case...) */ set_cheapest(rel); @@ -980,8 +1036,13 @@ has_multiple_baserels(PlannerInfo *root) * set_subquery_pathlist * Build the (single) access path for a subquery RTE * - * There's no need for a separate set_subquery_size phase, since we don't - * support parameterized paths for subqueries. + * We don't currently support generating parameterized paths for subqueries + * by pushing join clauses down into them; it seems too expensive to re-plan + * the subquery multiple times to consider different alternatives. So the + * subquery will have exactly one path. (The path will be parameterized + * if the subquery contains LATERAL references, otherwise not.) Since there's + * no freedom of action here, there's no need for a separate set_subquery_size + * phase: we just make the path right away. */ static void set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel, @@ -989,6 +1050,7 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel, { Query *parse = root->parse; Query *subquery = rte->subquery; + Relids required_outer; bool *differentTypes; double tuple_fraction; PlannerInfo *subroot; @@ -1001,6 +1063,13 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel, */ subquery = copyObject(subquery); + /* + * If it's a LATERAL subquery, it might contain some Vars of the current + * query level, requiring it to be treated as parameterized, even though + * we don't support pushing down join quals into subqueries. + */ + required_outer = rel->lateral_relids; + /* We need a workspace for keeping track of set-op type coercions */ differentTypes = (bool *) palloc0((list_length(subquery->targetList) + 1) * sizeof(bool)); @@ -1019,10 +1088,9 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel, * pseudoconstant clauses; better to have the gating node above the * subquery. * - * Also, if the sub-query has "security_barrier" flag, it means the + * Also, if the sub-query has the "security_barrier" flag, it means the * sub-query originated from a view that must enforce row-level security. - * We must not push down quals in order to avoid information leaks, either - * via side-effects or error output. + * Then we must not push down quals that contain leaky functions. * * Non-pushed-down clauses will get evaluated as qpquals of the * SubqueryScan node. @@ -1042,16 +1110,9 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel, RestrictInfo *rinfo = (RestrictInfo *) lfirst(l); Node *clause = (Node *) rinfo->clause; - /* - * XXX. You might wonder why we're testing rte->security_barrier - * qual-by-qual here rather than hoisting the test up into the - * surrounding if statement; after all, the answer will be the - * same for all quals. The answer is that we expect to shortly - * change this logic to allow pushing down some quals that use only - * "leakproof" operators even through a security barrier. - */ if (!rinfo->pseudoconstant && - !rte->security_barrier && + (!rte->security_barrier || + !contain_leaky_functions(clause)) && qual_is_pushdown_safe(subquery, rti, clause, differentTypes)) { /* Push it down */ @@ -1084,6 +1145,9 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel, else tuple_fraction = root->tuple_fraction; + /* plan_params should not be in use in current query level */ + Assert(root->plan_params == NIL); + /* Generate the plan for the subquery */ rel->subplan = subquery_planner(root->glob, subquery, root, @@ -1091,10 +1155,14 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel, &subroot); rel->subroot = subroot; + /* Isolate the params needed by this specific subplan */ + rel->subplan_params = root->plan_params; + root->plan_params = NIL; + /* - * It's possible that constraint exclusion proved the subquery empty. - * If so, it's convenient to turn it back into a dummy path so that we - * will recognize appropriate optimizations at this level. + * It's possible that constraint exclusion proved the subquery empty. If + * so, it's convenient to turn it back into a dummy path so that we will + * recognize appropriate optimizations at this level. */ if (is_dummy_plan(rel->subplan)) { @@ -1109,7 +1177,7 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel, pathkeys = convert_subquery_pathkeys(root, rel, subroot->query_pathkeys); /* Generate appropriate path */ - add_path(rel, create_subqueryscan_path(rel, pathkeys)); + add_path(rel, create_subqueryscan_path(root, rel, pathkeys, required_outer)); /* Select cheapest path (pretty easy in this case...) */ set_cheapest(rel); @@ -1122,8 +1190,17 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel, static void set_function_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte) { + Relids required_outer; + + /* + * We don't support pushing join clauses into the quals of a function + * scan, but it could still have required parameterization due to LATERAL + * refs in the function expression. + */ + required_outer = rel->lateral_relids; + /* Generate appropriate path */ - add_path(rel, create_functionscan_path(root, rel)); + add_path(rel, create_functionscan_path(root, rel, required_outer)); /* Select cheapest path (pretty easy in this case...) */ set_cheapest(rel); @@ -1136,8 +1213,17 @@ set_function_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte) static void set_values_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte) { + Relids required_outer; + + /* + * We don't support pushing join clauses into the quals of a values scan, + * but it could still have required parameterization due to LATERAL refs + * in the values expressions. + */ + required_outer = rel->lateral_relids; + /* Generate appropriate path */ - add_path(rel, create_valuesscan_path(root, rel)); + add_path(rel, create_valuesscan_path(root, rel, required_outer)); /* Select cheapest path (pretty easy in this case...) */ set_cheapest(rel); @@ -1148,7 +1234,7 @@ set_values_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte) * Build the (single) access path for a non-self-reference CTE RTE * * There's no need for a separate set_cte_size phase, since we don't - * support parameterized paths for CTEs. + * support join-qual-parameterized paths for CTEs. */ static void set_cte_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte) @@ -1159,6 +1245,7 @@ set_cte_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte) int ndx; ListCell *lc; int plan_id; + Relids required_outer; /* * Find the referenced CTE, and locate the plan previously made for it. @@ -1197,8 +1284,16 @@ set_cte_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte) /* Mark rel with estimated output rows, width, etc */ set_cte_size_estimates(root, rel, cteplan); + /* + * We don't support pushing join clauses into the quals of a CTE scan, but + * it could still have required parameterization due to LATERAL refs in + * its tlist. (That can only happen if the CTE scan is on a relation + * pulled up out of a UNION ALL appendrel.) + */ + required_outer = rel->lateral_relids; + /* Generate appropriate path */ - add_path(rel, create_ctescan_path(root, rel)); + add_path(rel, create_ctescan_path(root, rel, required_outer)); /* Select cheapest path (pretty easy in this case...) */ set_cheapest(rel); @@ -1209,7 +1304,7 @@ set_cte_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte) * Build the (single) access path for a self-reference CTE RTE * * There's no need for a separate set_worktable_size phase, since we don't - * support parameterized paths for CTEs. + * support join-qual-parameterized paths for CTEs. */ static void set_worktable_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte) @@ -1217,6 +1312,7 @@ set_worktable_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte) Plan *cteplan; PlannerInfo *cteroot; Index levelsup; + Relids required_outer; /* * We need to find the non-recursive term's plan, which is in the plan @@ -1241,8 +1337,18 @@ set_worktable_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte) /* Mark rel with estimated output rows, width, etc */ set_cte_size_estimates(root, rel, cteplan); + /* + * We don't support pushing join clauses into the quals of a worktable + * scan, but it could still have required parameterization due to LATERAL + * refs in its tlist. (That can only happen if the worktable scan is on a + * relation pulled up out of a UNION ALL appendrel. I'm not sure this is + * actually possible given the restrictions on recursive references, but + * it's easy enough to support.) + */ + required_outer = rel->lateral_relids; + /* Generate appropriate path */ - add_path(rel, create_worktablescan_path(root, rel)); + add_path(rel, create_worktablescan_path(root, rel, required_outer)); /* Select cheapest path (pretty easy in this case...) */ set_cheapest(rel); @@ -1616,7 +1722,7 @@ qual_is_pushdown_safe(Query *subquery, Index rti, Node *qual, /* * It would be unsafe to push down window function calls, but at least for - * the moment we could never see any in a qual anyhow. (The same applies + * the moment we could never see any in a qual anyhow. (The same applies * to aggregates, which we check for in pull_var_clause below.) */ Assert(!contain_window_function(qual)); @@ -1963,10 +2069,16 @@ debug_print_rel(PlannerInfo *root, RelOptInfo *rel) printf("\tpath list:\n"); foreach(l, rel->pathlist) print_path(root, lfirst(l), 1); - printf("\n\tcheapest startup path:\n"); - print_path(root, rel->cheapest_startup_path, 1); - printf("\n\tcheapest total path:\n"); - print_path(root, rel->cheapest_total_path, 1); + if (rel->cheapest_startup_path) + { + printf("\n\tcheapest startup path:\n"); + print_path(root, rel->cheapest_startup_path, 1); + } + if (rel->cheapest_total_path) + { + printf("\n\tcheapest total path:\n"); + print_path(root, rel->cheapest_total_path, 1); + } printf("\n"); fflush(stdout); }