From 7741dd6590073719688891898e85f0cb73453159 Mon Sep 17 00:00:00 2001 From: Tom Lane <tgl@sss.pgh.pa.us> Date: Sat, 24 Sep 2011 19:33:16 -0400 Subject: [PATCH] Recognize self-contradictory restriction clauses for non-table relations. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The constraint exclusion feature checks for contradictions among scan restriction clauses, as well as contradictions between those clauses and a table's CHECK constraints. The first aspect of this testing can be useful for non-table relations (such as subqueries or functions-in-FROM), but the feature was coded with only the CHECK case in mind so we were applying it only to plain-table RTEs. Move the relation_excluded_by_constraints call so that it is applied to all RTEs not just plain tables. With the default setting of constraint_exclusion this results in no extra work, but with constraint_exclusion = ON we will detect optimizations that we missed before (at the cost of more planner cycles than we expended before). Per a gripe from Gunnlaugur Þór Briem. Experimentation with his example also showed we were not being very bright about the case where constraint exclusion is proven within a subquery within UNION ALL, so tweak the code to allow set_append_rel_pathlist to recognize such cases. --- src/backend/optimizer/path/allpaths.c | 58 ++++++++++++++++++--------- src/backend/optimizer/plan/planner.c | 7 ++-- src/include/optimizer/planner.h | 2 + 3 files changed, 45 insertions(+), 22 deletions(-) diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c index b4214818287..815b996a131 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 64b5eb4d9cc..b4982746a2e 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 01fc1fc5d4a..77a5a43421d 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); -- GitLab