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