summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Lane2011-09-24 23:33:16 +0000
committerTom Lane2011-09-24 23:33:16 +0000
commit7741dd6590073719688891898e85f0cb73453159 (patch)
tree98b57a56460a9b9313609e598efe1b7cf11a6f13
parent337c0b03614c45516f2c3ec956405713bb264d54 (diff)
Recognize self-contradictory restriction clauses for non-table relations.
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.
-rw-r--r--src/backend/optimizer/path/allpaths.c58
-rw-r--r--src/backend/optimizer/plan/planner.c7
-rw-r--r--src/include/optimizer/planner.h2
3 files changed, 45 insertions, 22 deletions
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c
index b421481828..815b996a13 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);
@@ -230,19 +241,6 @@ 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 64b5eb4d9c..b4982746a2 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 01fc1fc5d4..77a5a43421 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);