Commit 7741dd65 authored by Tom Lane's avatar Tom Lane

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.
parent 337c0b03
...@@ -171,7 +171,18 @@ static void ...@@ -171,7 +171,18 @@ static void
set_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, set_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
Index rti, RangeTblEntry *rte) 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 */ /* It's an "append relation", process accordingly */
set_append_rel_pathlist(root, rel, rti, rte); set_append_rel_pathlist(root, rel, rti, rte);
...@@ -229,19 +240,6 @@ set_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, ...@@ -229,19 +240,6 @@ set_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
static void static void
set_plain_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte) 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 * Test any partial indexes of rel for applicability. We must do this
* first since partial unique indexes can affect size estimates. * first since partial unique indexes can affect size estimates.
...@@ -439,18 +437,29 @@ set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, ...@@ -439,18 +437,29 @@ set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
* otherrels. So we just leave the child's attr_needed empty. * 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 * Compute the child's access paths.
* Append path we are constructing for the parent.
*/ */
set_rel_pathlist(root, childrel, childRTindex, childRTE); 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, subpaths = accumulate_append_subpath(subpaths,
childrel->cheapest_total_path); 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 * Collect a list of all the available path orderings for all the
* children. We use this as a heuristic to indicate which sort * children. We use this as a heuristic to indicate which sort
...@@ -793,6 +802,17 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel, ...@@ -793,6 +802,17 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel,
&subroot); &subroot);
rel->subroot = 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 */ /* Mark rel with estimated output rows, width, etc */
set_subquery_size_estimates(root, rel); set_subquery_size_estimates(root, rel);
......
...@@ -60,7 +60,6 @@ static Node *preprocess_expression(PlannerInfo *root, Node *expr, int kind); ...@@ -60,7 +60,6 @@ static Node *preprocess_expression(PlannerInfo *root, Node *expr, int kind);
static void preprocess_qual_conditions(PlannerInfo *root, Node *jtnode); static void preprocess_qual_conditions(PlannerInfo *root, Node *jtnode);
static Plan *inheritance_planner(PlannerInfo *root); static Plan *inheritance_planner(PlannerInfo *root);
static Plan *grouping_planner(PlannerInfo *root, double tuple_fraction); static Plan *grouping_planner(PlannerInfo *root, double tuple_fraction);
static bool is_dummy_plan(Plan *plan);
static void preprocess_rowmarks(PlannerInfo *root); static void preprocess_rowmarks(PlannerInfo *root);
static double preprocess_limit(PlannerInfo *root, static double preprocess_limit(PlannerInfo *root,
double tuple_fraction, double tuple_fraction,
...@@ -1841,9 +1840,11 @@ grouping_planner(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. * is deemed not to need scanning due to constraint exclusion.
* *
* Currently, such dummy plans are Result nodes with constant FALSE * 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) is_dummy_plan(Plan *plan)
{ {
if (IsA(plan, Result)) if (IsA(plan, Result))
......
...@@ -35,6 +35,8 @@ extern Plan *subquery_planner(PlannerGlobal *glob, Query *parse, ...@@ -35,6 +35,8 @@ extern Plan *subquery_planner(PlannerGlobal *glob, Query *parse,
bool hasRecursion, double tuple_fraction, bool hasRecursion, double tuple_fraction,
PlannerInfo **subroot); PlannerInfo **subroot);
extern bool is_dummy_plan(Plan *plan);
extern Expr *expression_planner(Expr *expr); extern Expr *expression_planner(Expr *expr);
extern bool plan_cluster_use_sort(Oid tableOid, Oid indexOid); extern bool plan_cluster_use_sort(Oid tableOid, Oid indexOid);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment