mirror of
https://github.com/postgres/postgres.git
synced 2025-11-29 23:43:17 +03:00
Extend code that deduces implied equality clauses to detect whether a
clause being added to a particular restriction-clause list is redundant with those already in the list. This avoids useless work at runtime, and (perhaps more importantly) keeps the selectivity estimation routines from generating too-small estimates of numbers of output rows. Also some minor improvements in OPTIMIZER_DEBUG displays.
This commit is contained in:
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v 1.63 2001/06/05 05:26:04 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v 1.64 2001/10/18 16:11:41 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -40,10 +40,13 @@ static void mark_baserels_for_outer_join(Query *root, Relids rels,
|
||||
static void distribute_qual_to_rels(Query *root, Node *clause,
|
||||
bool ispusheddown,
|
||||
bool isouterjoin,
|
||||
bool isdeduced,
|
||||
Relids qualscope);
|
||||
static void add_join_info_to_rels(Query *root, RestrictInfo *restrictinfo,
|
||||
Relids join_relids);
|
||||
static void add_vars_to_targetlist(Query *root, List *vars);
|
||||
static bool qual_is_redundant(Query *root, RestrictInfo *restrictinfo,
|
||||
List *restrictlist);
|
||||
static void check_mergejoinable(RestrictInfo *restrictinfo);
|
||||
static void check_hashjoinable(RestrictInfo *restrictinfo);
|
||||
|
||||
@@ -219,7 +222,7 @@ distribute_quals_to_rels(Query *root, Node *jtnode)
|
||||
*/
|
||||
foreach(qual, (List *) f->quals)
|
||||
distribute_qual_to_rels(root, (Node *) lfirst(qual),
|
||||
true, false, result);
|
||||
true, false, false, result);
|
||||
}
|
||||
else if (IsA(jtnode, JoinExpr))
|
||||
{
|
||||
@@ -279,7 +282,7 @@ distribute_quals_to_rels(Query *root, Node *jtnode)
|
||||
|
||||
foreach(qual, (List *) j->quals)
|
||||
distribute_qual_to_rels(root, (Node *) lfirst(qual),
|
||||
false, isouterjoin, result);
|
||||
false, isouterjoin, false, result);
|
||||
}
|
||||
else
|
||||
elog(ERROR, "distribute_quals_to_rels: unexpected node type %d",
|
||||
@@ -339,6 +342,7 @@ mark_baserels_for_outer_join(Query *root, Relids rels, Relids outerrels)
|
||||
* 'ispusheddown': if TRUE, force the clause to be marked 'ispusheddown'
|
||||
* (this indicates the clause came from a FromExpr, not a JoinExpr)
|
||||
* 'isouterjoin': TRUE if the qual came from an OUTER JOIN's ON-clause
|
||||
* 'isdeduced': TRUE if the qual came from implied-equality deduction
|
||||
* 'qualscope': list of baserels the qual's syntactic scope covers
|
||||
*
|
||||
* 'qualscope' identifies what level of JOIN the qual came from. For a top
|
||||
@@ -349,6 +353,7 @@ static void
|
||||
distribute_qual_to_rels(Query *root, Node *clause,
|
||||
bool ispusheddown,
|
||||
bool isouterjoin,
|
||||
bool isdeduced,
|
||||
Relids qualscope)
|
||||
{
|
||||
RestrictInfo *restrictinfo = makeNode(RestrictInfo);
|
||||
@@ -405,8 +410,17 @@ distribute_qual_to_rels(Query *root, Node *clause,
|
||||
* clause's own reference list. At the time we are called, the
|
||||
* outerjoinset list of each baserel will show exactly those outer
|
||||
* joins that are below the qual in the join tree.
|
||||
*
|
||||
* If the qual came from implied-equality deduction, we can evaluate
|
||||
* the qual at its natural semantic level.
|
||||
*
|
||||
*/
|
||||
if (isouterjoin)
|
||||
if (isdeduced)
|
||||
{
|
||||
Assert(sameseti(relids, qualscope));
|
||||
can_be_equijoin = true;
|
||||
}
|
||||
else if (isouterjoin)
|
||||
{
|
||||
relids = qualscope;
|
||||
can_be_equijoin = false;
|
||||
@@ -461,9 +475,6 @@ distribute_qual_to_rels(Query *root, Node *clause,
|
||||
*/
|
||||
RelOptInfo *rel = build_base_rel(root, lfirsti(relids));
|
||||
|
||||
rel->baserestrictinfo = lappend(rel->baserestrictinfo,
|
||||
restrictinfo);
|
||||
|
||||
/*
|
||||
* Check for a "mergejoinable" clause even though it's not a join
|
||||
* clause. This is so that we can recognize that "a.x = a.y"
|
||||
@@ -474,10 +485,26 @@ distribute_qual_to_rels(Query *root, Node *clause,
|
||||
*/
|
||||
if (can_be_equijoin)
|
||||
check_mergejoinable(restrictinfo);
|
||||
|
||||
/*
|
||||
* If the clause was deduced from implied equality, check to see
|
||||
* whether it is redundant with restriction clauses we already have
|
||||
* for this rel. Note we cannot apply this check to user-written
|
||||
* clauses, since we haven't found the canonical pathkey sets yet
|
||||
* while processing user clauses. (NB: no comparable check is done
|
||||
* in the join-clause case; redundancy will be detected when the
|
||||
* join clause is moved into a join rel's restriction list.)
|
||||
*/
|
||||
if (!isdeduced ||
|
||||
!qual_is_redundant(root, restrictinfo, rel->baserestrictinfo))
|
||||
{
|
||||
/* Add clause to rel's restriction list */
|
||||
rel->baserestrictinfo = lappend(rel->baserestrictinfo,
|
||||
restrictinfo);
|
||||
}
|
||||
}
|
||||
else if (relids != NIL)
|
||||
{
|
||||
|
||||
/*
|
||||
* 'clause' is a join clause, since there is more than one rel in
|
||||
* the relid list. Set additional RestrictInfo fields for
|
||||
@@ -524,9 +551,11 @@ distribute_qual_to_rels(Query *root, Node *clause,
|
||||
* the two sides represent equivalent PathKeyItems for path keys: any
|
||||
* path that is sorted by one side will also be sorted by the other
|
||||
* (as soon as the two rels are joined, that is). Record the key
|
||||
* equivalence for future use.
|
||||
* equivalence for future use. (We can skip this for a deduced clause,
|
||||
* since the keys are already known equivalent in that case.)
|
||||
*/
|
||||
if (can_be_equijoin && restrictinfo->mergejoinoperator != InvalidOid)
|
||||
if (can_be_equijoin && restrictinfo->mergejoinoperator != InvalidOid &&
|
||||
!isdeduced)
|
||||
add_equijoined_keys(root, restrictinfo);
|
||||
}
|
||||
|
||||
@@ -684,18 +713,106 @@ process_implied_equality(Query *root, Node *item1, Node *item2,
|
||||
|
||||
/*
|
||||
* Note: we mark the qual "pushed down" to ensure that it can never be
|
||||
* taken for an original JOIN/ON clause. We also claim it is an
|
||||
* outer- join clause, which it isn't, but that keeps
|
||||
* distribute_qual_to_rels from examining the outerjoinsets of the
|
||||
* relevant rels (which are no longer of interest, but could keep the
|
||||
* qual from being pushed down to where it should be). It'll also
|
||||
* save a useless call to add_equijoined keys...
|
||||
* taken for an original JOIN/ON clause.
|
||||
*/
|
||||
distribute_qual_to_rels(root, (Node *) clause,
|
||||
true, true,
|
||||
true, false, true,
|
||||
pull_varnos((Node *) clause));
|
||||
}
|
||||
|
||||
/*
|
||||
* qual_is_redundant
|
||||
* Detect whether an implied-equality qual that turns out to be a
|
||||
* restriction clause for a single base relation is redundant with
|
||||
* already-known restriction clauses for that rel. This occurs with,
|
||||
* for example,
|
||||
* SELECT * FROM tab WHERE f1 = f2 AND f2 = f3;
|
||||
* We need to suppress the redundant condition to avoid computing
|
||||
* too-small selectivity, not to mention wasting time at execution.
|
||||
*/
|
||||
static bool
|
||||
qual_is_redundant(Query *root,
|
||||
RestrictInfo *restrictinfo,
|
||||
List *restrictlist)
|
||||
{
|
||||
List *oldquals;
|
||||
List *olditem;
|
||||
Node *newleft;
|
||||
Node *newright;
|
||||
List *equalvars;
|
||||
bool someadded;
|
||||
|
||||
/*
|
||||
* Set cached pathkeys. NB: it is okay to do this now because this
|
||||
* routine is only invoked while we are generating implied equalities.
|
||||
* Therefore, the equi_key_list is already complete and so we can
|
||||
* correctly determine canonical pathkeys.
|
||||
*/
|
||||
cache_mergeclause_pathkeys(root, restrictinfo);
|
||||
/* If different, say "not redundant" (should never happen) */
|
||||
if (restrictinfo->left_pathkey != restrictinfo->right_pathkey)
|
||||
return false;
|
||||
/*
|
||||
* Scan existing quals to find those referencing same pathkeys.
|
||||
* Usually there will be few, if any, so build a list of just the
|
||||
* interesting ones.
|
||||
*/
|
||||
oldquals = NIL;
|
||||
foreach(olditem, restrictlist)
|
||||
{
|
||||
RestrictInfo *oldrinfo = (RestrictInfo *) lfirst(olditem);
|
||||
|
||||
if (oldrinfo->mergejoinoperator != InvalidOid)
|
||||
{
|
||||
cache_mergeclause_pathkeys(root, oldrinfo);
|
||||
if (restrictinfo->left_pathkey == oldrinfo->left_pathkey &&
|
||||
restrictinfo->right_pathkey == oldrinfo->right_pathkey)
|
||||
oldquals = lcons(oldrinfo, oldquals);
|
||||
}
|
||||
}
|
||||
if (oldquals == NIL)
|
||||
return false;
|
||||
/*
|
||||
* Now, we want to develop a list of Vars that are known equal to the
|
||||
* left side of the new qual. We traverse the old-quals list repeatedly
|
||||
* to transitively expand the Vars list. If at any point we find we
|
||||
* can reach the right-side Var of the new qual, we are done. We give
|
||||
* up when we can't expand the equalvars list any more.
|
||||
*/
|
||||
newleft = (Node *) get_leftop(restrictinfo->clause);
|
||||
newright = (Node *) get_rightop(restrictinfo->clause);
|
||||
equalvars = makeList1(newleft);
|
||||
do {
|
||||
someadded = false;
|
||||
foreach(olditem, oldquals)
|
||||
{
|
||||
RestrictInfo *oldrinfo = (RestrictInfo *) lfirst(olditem);
|
||||
Node *oldleft = (Node *) get_leftop(oldrinfo->clause);
|
||||
Node *oldright = (Node *) get_rightop(oldrinfo->clause);
|
||||
Node *newguy = NULL;
|
||||
|
||||
if (member(oldleft, equalvars))
|
||||
newguy = oldright;
|
||||
else if (member(oldright, equalvars))
|
||||
newguy = oldleft;
|
||||
else
|
||||
continue;
|
||||
if (equal(newguy, newright))
|
||||
return true; /* we proved new clause is redundant */
|
||||
equalvars = lcons(newguy, equalvars);
|
||||
someadded = true;
|
||||
/*
|
||||
* Remove this qual from list, since we don't need it anymore.
|
||||
* Note this doesn't break the foreach() loop, since lremove
|
||||
* doesn't touch the next-link of the removed cons cell.
|
||||
*/
|
||||
oldquals = lremove(oldrinfo, oldquals);
|
||||
}
|
||||
} while (someadded);
|
||||
|
||||
return false; /* it's not redundant */
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user