1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-14 08:21:07 +03:00

Fix constraint exclusion to work in inherited UPDATE/DELETE queries

... in fact, it will be applied now in any query whatsoever.  I'm still
a bit concerned about the cycles that might be expended in failed proof
attempts, but given that CE is turned off by default, it's the user's
choice whether to expend those cycles or not.  (Possibly we should
change the simple bool constraint_exclusion parameter to something
more fine-grained?)
This commit is contained in:
Tom Lane
2006-02-04 23:03:20 +00:00
parent 48d9ad3722
commit 3893127431
6 changed files with 97 additions and 80 deletions

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/path/allpaths.c,v 1.141 2006/02/03 21:08:49 tgl Exp $
* $PostgreSQL: pgsql/src/backend/optimizer/path/allpaths.c,v 1.142 2006/02/04 23:03:20 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -26,7 +26,6 @@
#include "optimizer/paths.h"
#include "optimizer/plancat.h"
#include "optimizer/planner.h"
#include "optimizer/predtest.h"
#include "optimizer/prep.h"
#include "optimizer/var.h"
#include "parser/parsetree.h"
@ -36,14 +35,12 @@
/* These parameters are set by GUC */
bool constraint_exclusion = false;
bool enable_geqo = false; /* just in case GUC doesn't set it */
int geqo_threshold;
static void set_base_rel_pathlists(PlannerInfo *root);
static void set_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
Index rti, RangeTblEntry *rte);
static void set_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, Index rti);
static void set_plain_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
RangeTblEntry *rte);
static void set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
@ -146,8 +143,7 @@ set_base_rel_pathlists(PlannerInfo *root)
if (rel->reloptkind != RELOPT_BASEREL)
continue;
set_rel_pathlist(root, rel, rti,
rt_fetch(rti, root->parse->rtable));
set_rel_pathlist(root, rel, rti);
}
}
@ -156,9 +152,10 @@ set_base_rel_pathlists(PlannerInfo *root)
* Build access paths for a base relation
*/
static void
set_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
Index rti, RangeTblEntry *rte)
set_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, Index rti)
{
RangeTblEntry *rte = rt_fetch(rti, root->parse->rtable);
if (rte->inh)
{
/* It's an "append relation", process accordingly */
@ -207,6 +204,24 @@ set_plain_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
if (create_or_index_quals(root, rel))
set_baserel_size_estimates(root, rel);
/*
* If we can prove we don't need to scan the rel via constraint exclusion,
* set up a single dummy path for it. (Rather than inventing a special
* "dummy" path type, we represent this as an AppendPath with no members.)
*/
if (relation_excluded_by_constraints(rel, rte))
{
/* Reset output-rows estimate to 0 */
rel->rows = 0;
add_path(rel, (Path *) create_append_path(rel, NIL));
/* Select cheapest path (pretty easy in this case...) */
set_cheapest(rel);
return;
}
/*
* Generate paths and add them to the rel's pathlist.
*
@ -273,7 +288,6 @@ set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(l);
int childRTindex;
RelOptInfo *childrel;
RangeTblEntry *childrte;
Path *childpath;
ListCell *parentvars;
ListCell *childvars;
@ -316,53 +330,18 @@ set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
childrel->max_attr);
/*
* If we can prove we don't need to scan this child via constraint
* exclusion, just ignore it. (We have to have converted the
* baserestrictinfo Vars before we can make the test.)
*
* XXX it'd probably be better to give the child some kind of dummy
* cheapest path, or otherwise explicitly mark it as ignorable.
* Currently there is an ugly check in join_before_append() to handle
* excluded children.
*/
childrte = rt_fetch(childRTindex, root->parse->rtable);
if (constraint_exclusion &&
childrte->rtekind == RTE_RELATION)
{
List *constraint_pred;
constraint_pred = get_relation_constraints(childrte->relid,
childrel);
/*
* We do not currently enforce that CHECK constraints contain only
* immutable functions, so it's necessary to check here. We
* daren't draw conclusions from plan-time evaluation of
* non-immutable functions.
*/
if (!contain_mutable_functions((Node *) constraint_pred))
{
/*
* The constraints are effectively ANDed together, so we can
* just try to refute the entire collection at once. This may
* allow us to make proofs that would fail if we took them
* individually.
*/
if (predicate_refuted_by(constraint_pred,
childrel->baserestrictinfo))
continue;
}
}
/*
* Compute the child's access paths, and save the cheapest.
* Compute the child's access paths, and add the cheapest one
* to the Append path we are constructing for the parent.
*
* It's possible that the child is itself an appendrel, in which
* case we can "cut out the middleman" and just add its child
* paths to our own list. (We don't try to do this earlier because
* we need to apply both levels of transformation to the quals.)
* This test also handles the case where the child rel need not
* be scanned because of constraint exclusion: it'll have an
* Append path with no subpaths, and will vanish from our list.
*/
set_rel_pathlist(root, childrel, childRTindex, childrte);
set_rel_pathlist(root, childrel, childRTindex);
childpath = childrel->cheapest_total_path;
if (IsA(childpath, AppendPath))