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);
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, NULL));
+ add_path(rel, create_seqscan_path(root, rel, required_outer));
/* Consider index scans */
create_index_paths(root, 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(root,
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;
* the unparameterized Append path we are constructing for the parent.
* If not, there's no workable unparameterized path.
*/
- if (childrel->cheapest_total_path)
+ if (childrel->cheapest_total_path->param_info == NULL)
subpaths = accumulate_append_subpath(subpaths,
childrel->cheapest_total_path);
else
cheapest_startup = cheapest_total =
childrel->cheapest_total_path;
/* Assert we do have an unparameterized path for this child */
- Assert(cheapest_total != NULL);
Assert(cheapest_total->param_info == NULL);
}
/*
* If it's a LATERAL subquery, it might contain some Vars of the current
- * query level, requiring it to be treated as parameterized.
+ * query level, requiring it to be treated as parameterized, even though
+ * we don't support pushing down join quals into subqueries.
*/
- if (rte->lateral)
- {
- required_outer = pull_varnos_of_level((Node *) subquery, 1);
- /* Enforce convention that empty required_outer is exactly NULL */
- if (bms_is_empty(required_outer))
- required_outer = NULL;
- }
- else
- required_outer = NULL;
+ required_outer = rel->lateral_relids;
/* We need a workspace for keeping track of set-op type coercions */
differentTypes = (bool *)
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,
&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
/*
* set_function_pathlist
* Build the (single) access path for a function RTE
- *
- * As with subqueries, a function RTE's path might be parameterized due to
- * LATERAL references, but that's inherent in the function expression and
- * not a result of pushing down join quals.
*/
static void
set_function_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
Relids required_outer;
/*
- * If it's a LATERAL function, it might contain some Vars of the current
- * query level, requiring it to be treated as parameterized.
+ * 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.
*/
- if (rte->lateral)
- {
- required_outer = pull_varnos_of_level(rte->funcexpr, 0);
- /* Enforce convention that empty required_outer is exactly NULL */
- if (bms_is_empty(required_outer))
- required_outer = NULL;
- }
- else
- required_outer = NULL;
+ required_outer = rel->lateral_relids;
/* Generate appropriate path */
add_path(rel, create_functionscan_path(root, rel, required_outer));
/*
* set_values_pathlist
* Build the (single) access path for a VALUES RTE
- *
- * As with subqueries, a VALUES RTE's path might be parameterized due to
- * LATERAL references, but that's inherent in the values expressions and
- * not a result of pushing down join quals.
*/
static void
set_values_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
Relids required_outer;
/*
- * If it's a LATERAL RTE, it might contain some Vars of the current query
- * level, requiring it to be treated as parameterized. (NB: even though
- * the parser never marks VALUES RTEs as LATERAL, they could be so marked
- * by now, as a result of subquery pullup.)
+ * 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.
*/
- if (rte->lateral)
- {
- required_outer = pull_varnos_of_level((Node *) rte->values_lists, 0);
- /* Enforce convention that empty required_outer is exactly NULL */
- if (bms_is_empty(required_outer))
- required_outer = NULL;
- }
- else
- required_outer = NULL;
+ required_outer = rel->lateral_relids;
/* Generate appropriate path */
add_path(rel, create_valuesscan_path(root, rel, required_outer));
* 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)
int ndx;
ListCell *lc;
int plan_id;
+ Relids required_outer;
/*
* Find the referenced CTE, and locate the plan previously made for it.
/* 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);
* 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)
Plan *cteplan;
PlannerInfo *cteroot;
Index levelsup;
+ Relids required_outer;
/*
* We need to find the non-recursive term's plan, which is in the plan
/* 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);