Skip to content

Commit 7741dd6

Browse files
committed
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.
1 parent 337c0b0 commit 7741dd6

File tree

3 files changed

+45
-22
lines changed

3 files changed

+45
-22
lines changed

src/backend/optimizer/path/allpaths.c

Lines changed: 39 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,18 @@ static void
171171
set_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
172172
Index rti, RangeTblEntry *rte)
173173
{
174-
if (rte->inh)
174+
if (rel->reloptkind == RELOPT_BASEREL &&
175+
relation_excluded_by_constraints(root, rel, rte))
176+
{
177+
/*
178+
* We proved we don't need to scan the rel via constraint exclusion,
179+
* so set up a single dummy path for it. Here we only check this for
180+
* regular baserels; if it's an otherrel, CE was already checked in
181+
* set_append_rel_pathlist().
182+
*/
183+
set_dummy_rel_pathlist(rel);
184+
}
185+
else if (rte->inh)
175186
{
176187
/* It's an "append relation", process accordingly */
177188
set_append_rel_pathlist(root, rel, rti, rte);
@@ -229,19 +240,6 @@ set_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
229240
static void
230241
set_plain_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
231242
{
232-
/*
233-
* If we can prove we don't need to scan the rel via constraint exclusion,
234-
* set up a single dummy path for it. We only need to check for regular
235-
* baserels; if it's an otherrel, CE was already checked in
236-
* set_append_rel_pathlist().
237-
*/
238-
if (rel->reloptkind == RELOPT_BASEREL &&
239-
relation_excluded_by_constraints(root, rel, rte))
240-
{
241-
set_dummy_rel_pathlist(rel);
242-
return;
243-
}
244-
245243
/*
246244
* Test any partial indexes of rel for applicability. We must do this
247245
* first since partial unique indexes can affect size estimates.
@@ -439,18 +437,29 @@ set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
439437
* otherrels. So we just leave the child's attr_needed empty.
440438
*/
441439

442-
/* Remember which childrels are live, for MergeAppend logic below */
443-
live_childrels = lappend(live_childrels, childrel);
444-
445440
/*
446-
* Compute the child's access paths, and add the cheapest one to the
447-
* Append path we are constructing for the parent.
441+
* Compute the child's access paths.
448442
*/
449443
set_rel_pathlist(root, childrel, childRTindex, childRTE);
450444

445+
/*
446+
* It is possible that constraint exclusion detected a contradiction
447+
* within a child subquery, even though we didn't prove one above.
448+
* If what we got back was a dummy path, we can skip this child.
449+
*/
450+
if (IS_DUMMY_PATH(childrel->cheapest_total_path))
451+
continue;
452+
453+
/*
454+
* Child is live, so add its cheapest access path to the Append path
455+
* we are constructing for the parent.
456+
*/
451457
subpaths = accumulate_append_subpath(subpaths,
452458
childrel->cheapest_total_path);
453459

460+
/* Remember which childrels are live, for MergeAppend logic below */
461+
live_childrels = lappend(live_childrels, childrel);
462+
454463
/*
455464
* Collect a list of all the available path orderings for all the
456465
* children. We use this as a heuristic to indicate which sort
@@ -793,6 +802,17 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel,
793802
&subroot);
794803
rel->subroot = subroot;
795804

805+
/*
806+
* It's possible that constraint exclusion proved the subquery empty.
807+
* If so, it's convenient to turn it back into a dummy path so that we
808+
* will recognize appropriate optimizations at this level.
809+
*/
810+
if (is_dummy_plan(rel->subplan))
811+
{
812+
set_dummy_rel_pathlist(rel);
813+
return;
814+
}
815+
796816
/* Mark rel with estimated output rows, width, etc */
797817
set_subquery_size_estimates(root, rel);
798818

src/backend/optimizer/plan/planner.c

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,6 @@ static Node *preprocess_expression(PlannerInfo *root, Node *expr, int kind);
6060
static void preprocess_qual_conditions(PlannerInfo *root, Node *jtnode);
6161
static Plan *inheritance_planner(PlannerInfo *root);
6262
static Plan *grouping_planner(PlannerInfo *root, double tuple_fraction);
63-
static bool is_dummy_plan(Plan *plan);
6463
static void preprocess_rowmarks(PlannerInfo *root);
6564
static double preprocess_limit(PlannerInfo *root,
6665
double tuple_fraction,
@@ -1841,9 +1840,11 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
18411840
* is deemed not to need scanning due to constraint exclusion.
18421841
*
18431842
* Currently, such dummy plans are Result nodes with constant FALSE
1844-
* filter quals.
1843+
* filter quals (see set_dummy_rel_pathlist and create_append_plan).
1844+
*
1845+
* XXX this probably ought to be somewhere else, but not clear where.
18451846
*/
1846-
static bool
1847+
bool
18471848
is_dummy_plan(Plan *plan)
18481849
{
18491850
if (IsA(plan, Result))

src/include/optimizer/planner.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ extern Plan *subquery_planner(PlannerGlobal *glob, Query *parse,
3535
bool hasRecursion, double tuple_fraction,
3636
PlannerInfo **subroot);
3737

38+
extern bool is_dummy_plan(Plan *plan);
39+
3840
extern Expr *expression_planner(Expr *expr);
3941

4042
extern bool plan_cluster_use_sort(Oid tableOid, Oid indexOid);

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy