mirror of
https://github.com/postgres/postgres.git
synced 2025-07-17 06:41:09 +03:00
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.
This commit is contained in:
@ -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,
|
|||||||
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,
|
|||||||
* 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,
|
|||||||
&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);
|
|||||||
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)
|
|||||||
* 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,
|
|||||||
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);
|
||||||
|
Reference in New Issue
Block a user