diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c index b42148182876d388de8487080b7c33c7f64ac748..815b996a131d82baa419a2228ac1d7fb51f075d1 100644 --- a/src/backend/optimizer/path/allpaths.c +++ b/src/backend/optimizer/path/allpaths.c @@ -171,7 +171,18 @@ static void set_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, Index rti, RangeTblEntry *rte) { - if (rte->inh) + if (rel->reloptkind == RELOPT_BASEREL && + relation_excluded_by_constraints(root, rel, rte)) + { + /* + * We proved we don't need to scan the rel via constraint exclusion, + * so set up a single dummy path for it. Here we only check this for + * regular baserels; if it's an otherrel, CE was already checked in + * set_append_rel_pathlist(). + */ + set_dummy_rel_pathlist(rel); + } + else if (rte->inh) { /* It's an "append relation", process accordingly */ set_append_rel_pathlist(root, rel, rti, rte); @@ -229,19 +240,6 @@ set_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, static void set_plain_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte) { - /* - * If we can prove we don't need to scan the rel via constraint exclusion, - * set up a single dummy path for it. We only need to check for regular - * baserels; if it's an otherrel, CE was already checked in - * set_append_rel_pathlist(). - */ - if (rel->reloptkind == RELOPT_BASEREL && - relation_excluded_by_constraints(root, rel, rte)) - { - set_dummy_rel_pathlist(rel); - return; - } - /* * Test any partial indexes of rel for applicability. We must do this * first since partial unique indexes can affect size estimates. @@ -439,18 +437,29 @@ set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, * otherrels. So we just leave the child's attr_needed empty. */ - /* Remember which childrels are live, for MergeAppend logic below */ - live_childrels = lappend(live_childrels, childrel); - /* - * Compute the child's access paths, and add the cheapest one to the - * Append path we are constructing for the parent. + * Compute the child's access paths. */ set_rel_pathlist(root, childrel, childRTindex, childRTE); + /* + * It is possible that constraint exclusion detected a contradiction + * within a child subquery, even though we didn't prove one above. + * If what we got back was a dummy path, we can skip this child. + */ + if (IS_DUMMY_PATH(childrel->cheapest_total_path)) + continue; + + /* + * Child is live, so add its cheapest access path to the Append path + * we are constructing for the parent. + */ subpaths = accumulate_append_subpath(subpaths, childrel->cheapest_total_path); + /* Remember which childrels are live, for MergeAppend logic below */ + live_childrels = lappend(live_childrels, childrel); + /* * Collect a list of all the available path orderings for all the * children. We use this as a heuristic to indicate which sort @@ -793,6 +802,17 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel, &subroot); rel->subroot = subroot; + /* + * 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)) + { + set_dummy_rel_pathlist(rel); + return; + } + /* Mark rel with estimated output rows, width, etc */ set_subquery_size_estimates(root, rel); diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index 64b5eb4d9cc3fc50b3238940d9d2e8331086bf0c..b4982746a2e3fe27e8e59804f3e2473205e6aa17 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -60,7 +60,6 @@ static Node *preprocess_expression(PlannerInfo *root, Node *expr, int kind); static void preprocess_qual_conditions(PlannerInfo *root, Node *jtnode); static Plan *inheritance_planner(PlannerInfo *root); static Plan *grouping_planner(PlannerInfo *root, double tuple_fraction); -static bool is_dummy_plan(Plan *plan); static void preprocess_rowmarks(PlannerInfo *root); static double preprocess_limit(PlannerInfo *root, double tuple_fraction, @@ -1841,9 +1840,11 @@ grouping_planner(PlannerInfo *root, double tuple_fraction) * is deemed not to need scanning due to constraint exclusion. * * Currently, such dummy plans are Result nodes with constant FALSE - * filter quals. + * filter quals (see set_dummy_rel_pathlist and create_append_plan). + * + * XXX this probably ought to be somewhere else, but not clear where. */ -static bool +bool is_dummy_plan(Plan *plan) { if (IsA(plan, Result)) diff --git a/src/include/optimizer/planner.h b/src/include/optimizer/planner.h index 01fc1fc5d4a997e20192787395581d5a411ad998..77a5a43421d9845f109cfdeaccc4a7d03a9566bd 100644 --- a/src/include/optimizer/planner.h +++ b/src/include/optimizer/planner.h @@ -35,6 +35,8 @@ extern Plan *subquery_planner(PlannerGlobal *glob, Query *parse, bool hasRecursion, double tuple_fraction, PlannerInfo **subroot); +extern bool is_dummy_plan(Plan *plan); + extern Expr *expression_planner(Expr *expr); extern bool plan_cluster_use_sort(Oid tableOid, Oid indexOid);