Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Lane2021-09-14 19:11:21 +0000
committerTom Lane2021-09-14 19:11:21 +0000
commite8638d78a2cb94efba11a5dfbf3e7cd746d0af3e (patch)
treec57aaae614316217597539a6e4c61e45218bdacb /src/backend
parentbdeb2c4ec2700bfa561061ccd19181326ee01c3f (diff)
Fix planner error with multiple copies of an AlternativeSubPlan.
It's possible for us to copy an AlternativeSubPlan expression node into multiple places, for example the scan quals of several partition children. Then it's possible that we choose a different one of the alternatives as optimal in each place. Commit 41efb8340 failed to consider this scenario, so its attempt to remove "unused" subplans could remove subplans that were still used elsewhere. Fix by delaying the removal logic until we've examined all the AlternativeSubPlans in a given query level. (This does assume that AlternativeSubPlans couldn't get copied to other query levels, but for the foreseeable future that's fine; cf qual_is_pushdown_safe.) Per report from Rajkumar Raghuwanshi. Back-patch to v14 where the faulty logic came in. Discussion: https://postgr.es/m/CAKcux6==O3NNZC3bZ2prRYv3cjm3_Zw1GfzmOjEVqYN4jub2+Q@mail.gmail.com
Diffstat (limited to 'src/backend')
-rw-r--r--src/backend/optimizer/plan/setrefs.c71
1 files changed, 47 insertions, 24 deletions
diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c
index e50624c465e..3d23a2e5ac7 100644
--- a/src/backend/optimizer/plan/setrefs.c
+++ b/src/backend/optimizer/plan/setrefs.c
@@ -249,6 +249,7 @@ static List *set_returning_clause_references(PlannerInfo *root,
Plan *
set_plan_references(PlannerInfo *root, Plan *plan)
{
+ Plan *result;
PlannerGlobal *glob = root->glob;
int rtoffset = list_length(glob->finalrtable);
ListCell *lc;
@@ -301,8 +302,44 @@ set_plan_references(PlannerInfo *root, Plan *plan)
glob->appendRelations = lappend(glob->appendRelations, appinfo);
}
+ /* If needed, create workspace for processing AlternativeSubPlans */
+ if (root->hasAlternativeSubPlans)
+ {
+ root->isAltSubplan = (bool *)
+ palloc0(list_length(glob->subplans) * sizeof(bool));
+ root->isUsedSubplan = (bool *)
+ palloc0(list_length(glob->subplans) * sizeof(bool));
+ }
+
/* Now fix the Plan tree */
- return set_plan_refs(root, plan, rtoffset);
+ result = set_plan_refs(root, plan, rtoffset);
+
+ /*
+ * If we have AlternativeSubPlans, it is likely that we now have some
+ * unreferenced subplans in glob->subplans. To avoid expending cycles on
+ * those subplans later, get rid of them by setting those list entries to
+ * NULL. (Note: we can't do this immediately upon processing an
+ * AlternativeSubPlan, because there may be multiple copies of the
+ * AlternativeSubPlan, and they can get resolved differently.)
+ */
+ if (root->hasAlternativeSubPlans)
+ {
+ foreach(lc, glob->subplans)
+ {
+ int ndx = foreach_current_index(lc);
+
+ /*
+ * If it was used by some AlternativeSubPlan in this query level,
+ * but wasn't selected as best by any AlternativeSubPlan, then we
+ * don't need it. Do not touch subplans that aren't parts of
+ * AlternativeSubPlans.
+ */
+ if (root->isAltSubplan[ndx] && !root->isUsedSubplan[ndx])
+ lfirst(lc) = NULL;
+ }
+ }
+
+ return result;
}
/*
@@ -1765,8 +1802,7 @@ fix_param_node(PlannerInfo *root, Param *p)
* Note: caller must still recurse into the result!
*
* We don't make any attempt to fix up cost estimates in the parent plan
- * node or higher-level nodes. However, we do remove the rejected subplan(s)
- * from root->glob->subplans, to minimize cycles expended on them later.
+ * node or higher-level nodes.
*/
static Node *
fix_alternative_subplan(PlannerInfo *root, AlternativeSubPlan *asplan,
@@ -1778,9 +1814,8 @@ fix_alternative_subplan(PlannerInfo *root, AlternativeSubPlan *asplan,
/*
* Compute the estimated cost of each subplan assuming num_exec
- * executions, and keep the cheapest one. Replace discarded subplans with
- * NULL pointers in the global subplans list. In event of exact equality
- * of estimates, we prefer the later plan; this is a bit arbitrary, but in
+ * executions, and keep the cheapest one. In event of exact equality of
+ * estimates, we prefer the later plan; this is a bit arbitrary, but in
* current usage it biases us to break ties against fast-start subplans.
*/
Assert(asplan->subplans != NIL);
@@ -1791,31 +1826,19 @@ fix_alternative_subplan(PlannerInfo *root, AlternativeSubPlan *asplan,
Cost curcost;
curcost = curplan->startup_cost + num_exec * curplan->per_call_cost;
- if (bestplan == NULL)
- {
- bestplan = curplan;
- bestcost = curcost;
- }
- else if (curcost <= bestcost)
+ if (bestplan == NULL || curcost <= bestcost)
{
- /* drop old bestplan */
- ListCell *lc2 = list_nth_cell(root->glob->subplans,
- bestplan->plan_id - 1);
-
- lfirst(lc2) = NULL;
bestplan = curplan;
bestcost = curcost;
}
- else
- {
- /* drop curplan */
- ListCell *lc2 = list_nth_cell(root->glob->subplans,
- curplan->plan_id - 1);
- lfirst(lc2) = NULL;
- }
+ /* Also mark all subplans that are in AlternativeSubPlans */
+ root->isAltSubplan[curplan->plan_id - 1] = true;
}
+ /* Mark the subplan we selected */
+ root->isUsedSubplan[bestplan->plan_id - 1] = true;
+
return (Node *) bestplan;
}