]> granicus.if.org Git - postgresql/blobdiff - src/backend/optimizer/path/allpaths.c
Fix PARAM_EXEC assignment mechanism to be safe in the presence of WITH.
[postgresql] / src / backend / optimizer / path / allpaths.c
index dfb0b38448bf2ea8318c2af0cc2d65111cf4e829..458dae0489c029bd743c75c82f8e5102067e89bf 100644 (file)
@@ -268,8 +268,9 @@ set_rel_size(PlannerInfo *root, RelOptInfo *rel,
                        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);
@@ -376,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, NULL));
+       add_path(rel, create_seqscan_path(root, rel, required_outer));
 
        /* Consider index scans */
        create_index_paths(root, rel);
@@ -536,10 +547,10 @@ 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(root,
@@ -610,7 +621,8 @@ 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;
 
@@ -710,7 +722,7 @@ set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
                 * 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
@@ -920,7 +932,6 @@ generate_mergeappend_paths(PlannerInfo *root, RelOptInfo *rel,
                                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);
                        }
 
@@ -1054,17 +1065,10 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel,
 
        /*
         * 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 *)
@@ -1141,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,
@@ -1148,6 +1155,10 @@ 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
@@ -1175,10 +1186,6 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel,
 /*
  * 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)
@@ -1186,18 +1193,11 @@ 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));
@@ -1209,10 +1209,6 @@ set_function_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
 /*
  * 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)
@@ -1220,20 +1216,11 @@ 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));
@@ -1247,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)
@@ -1258,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.
@@ -1296,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);
@@ -1308,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)
@@ -1316,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
@@ -1340,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);