mirror of
https://github.com/postgres/postgres.git
synced 2025-04-22 23:02:54 +03:00
Modify planner's implied-equality-deduction code so that when a set
of known-equal expressions includes any constant expressions (including Params from outer queries), we actively suppress any 'var = var' clauses that are or could be deduced from the set, generating only the deducible 'var = const' clauses instead. The idea here is to push down the restrictions implied by the equality set to base relations whenever possible. Once we have applied the 'var = const' clauses, the 'var = var' clauses are redundant, and should be suppressed both to save work at execution and to avoid double-counting restrictivity.
This commit is contained in:
parent
ef7422510e
commit
f5e83662d0
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/nodes/list.c,v 1.44 2003/01/20 18:54:47 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/nodes/list.c,v 1.45 2003/01/24 03:58:34 tgl Exp $
|
||||
*
|
||||
* NOTES
|
||||
* XXX a few of the following functions are duplicated to handle
|
||||
@ -357,6 +357,7 @@ set_union(List *l1, List *l2)
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* set_union for integer lists */
|
||||
List *
|
||||
set_unioni(List *l1, List *l2)
|
||||
{
|
||||
@ -371,6 +372,21 @@ set_unioni(List *l1, List *l2)
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* set_union when pointer-equality comparison is sufficient */
|
||||
List *
|
||||
set_ptrUnion(List *l1, List *l2)
|
||||
{
|
||||
List *retval = listCopy(l1);
|
||||
List *i;
|
||||
|
||||
foreach(i, l2)
|
||||
{
|
||||
if (!ptrMember(lfirst(i), retval))
|
||||
retval = lappend(retval, lfirst(i));
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*
|
||||
* Generate the intersection of two lists,
|
||||
* ie, all members of both l1 and l2.
|
||||
|
@ -9,7 +9,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.132 2003/01/20 18:54:49 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.133 2003/01/24 03:58:34 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -1596,12 +1596,14 @@ make_innerjoin_index_path(Query *root,
|
||||
* nconc the two lists; then we might have some restriction
|
||||
* clauses appearing twice, which'd mislead
|
||||
* restrictlist_selectivity into double-counting their
|
||||
* selectivity.)
|
||||
* selectivity. However, since RestrictInfo nodes aren't copied when
|
||||
* linking them into different lists, it should be sufficient to use
|
||||
* pointer comparison to remove duplicates.)
|
||||
*/
|
||||
pathnode->rows = rel->tuples *
|
||||
restrictlist_selectivity(root,
|
||||
set_union(rel->baserestrictinfo,
|
||||
clausegroup),
|
||||
set_ptrUnion(rel->baserestrictinfo,
|
||||
clausegroup),
|
||||
lfirsti(rel->relids));
|
||||
/* Like costsize.c, force estimate to be at least one row */
|
||||
if (pathnode->rows < 1.0)
|
||||
|
@ -11,7 +11,7 @@
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/pathkeys.c,v 1.44 2003/01/15 19:35:40 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/pathkeys.c,v 1.45 2003/01/24 03:58:35 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -23,6 +23,7 @@
|
||||
#include "optimizer/paths.h"
|
||||
#include "optimizer/planmain.h"
|
||||
#include "optimizer/tlist.h"
|
||||
#include "optimizer/var.h"
|
||||
#include "parser/parsetree.h"
|
||||
#include "parser/parse_func.h"
|
||||
#include "utils/lsyscache.h"
|
||||
@ -147,14 +148,30 @@ add_equijoined_keys(Query *root, RestrictInfo *restrictinfo)
|
||||
* generate_implied_equalities
|
||||
* Scan the completed equi_key_list for the query, and generate explicit
|
||||
* qualifications (WHERE clauses) for all the pairwise equalities not
|
||||
* already mentioned in the quals. This is useful because the additional
|
||||
* clauses help the selectivity-estimation code, and in fact it's
|
||||
* *necessary* to ensure that sort keys we think are equivalent really
|
||||
* are (see src/backend/optimizer/README for more info).
|
||||
* already mentioned in the quals; or remove qualifications found to be
|
||||
* redundant.
|
||||
*
|
||||
* Adding deduced equalities is useful because the additional clauses help
|
||||
* the selectivity-estimation code and may allow better joins to be chosen;
|
||||
* and in fact it's *necessary* to ensure that sort keys we think are
|
||||
* equivalent really are (see src/backend/optimizer/README for more info).
|
||||
*
|
||||
* If an equi_key_list set includes any constants then we adopt a different
|
||||
* strategy: we record all the "var = const" deductions we can make, and
|
||||
* actively remove all the "var = var" clauses that are implied by the set
|
||||
* (including the clauses that originally gave rise to the set!). The reason
|
||||
* is that given input like "a = b AND b = 42", once we have deduced "a = 42"
|
||||
* there is no longer any need to apply the clause "a = b"; not only is
|
||||
* it a waste of time to check it, but we will misestimate selectivity if the
|
||||
* clause is left in. So we must remove it. For this purpose, any pathkey
|
||||
* item that mentions no Vars of the current level can be taken as a constant.
|
||||
* (The only case where this would be risky is if the item contains volatile
|
||||
* functions; but we will never consider such an expression to be a pathkey
|
||||
* at all, because check_mergejoinable() will reject it.)
|
||||
*
|
||||
* This routine just walks the equi_key_list to find all pairwise equalities.
|
||||
* We call process_implied_equality (in plan/initsplan.c) to determine whether
|
||||
* each is already known and add it to the proper restrictinfo list if not.
|
||||
* We call process_implied_equality (in plan/initsplan.c) to adjust the
|
||||
* restrictinfo datastructures for each pair.
|
||||
*/
|
||||
void
|
||||
generate_implied_equalities(Query *root)
|
||||
@ -164,35 +181,119 @@ generate_implied_equalities(Query *root)
|
||||
foreach(cursetlink, root->equi_key_list)
|
||||
{
|
||||
List *curset = lfirst(cursetlink);
|
||||
int nitems = length(curset);
|
||||
Relids *relids;
|
||||
bool have_consts;
|
||||
List *ptr1;
|
||||
int i1;
|
||||
|
||||
/*
|
||||
* A set containing only two items cannot imply any equalities
|
||||
* beyond the one that created the set, so we can skip it.
|
||||
*/
|
||||
if (length(curset) < 3)
|
||||
if (nitems < 3)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* Collect info about relids mentioned in each item. For this
|
||||
* routine we only really care whether there are any at all in
|
||||
* each item, but process_implied_equality() needs the exact
|
||||
* lists, so we may as well pull them here.
|
||||
*/
|
||||
relids = (Relids *) palloc(nitems * sizeof(Relids));
|
||||
have_consts = false;
|
||||
i1 = 0;
|
||||
foreach(ptr1, curset)
|
||||
{
|
||||
PathKeyItem *item1 = (PathKeyItem *) lfirst(ptr1);
|
||||
|
||||
relids[i1] = pull_varnos(item1->key);
|
||||
if (relids[i1] == NIL)
|
||||
have_consts = true;
|
||||
i1++;
|
||||
}
|
||||
|
||||
/*
|
||||
* Match each item in the set with all that appear after it (it's
|
||||
* sufficient to generate A=B, need not process B=A too).
|
||||
*/
|
||||
i1 = 0;
|
||||
foreach(ptr1, curset)
|
||||
{
|
||||
PathKeyItem *item1 = (PathKeyItem *) lfirst(ptr1);
|
||||
List *ptr2;
|
||||
int i2 = i1 + 1;
|
||||
|
||||
foreach(ptr2, lnext(ptr1))
|
||||
{
|
||||
PathKeyItem *item2 = (PathKeyItem *) lfirst(ptr2);
|
||||
|
||||
process_implied_equality(root, item1->key, item2->key,
|
||||
item1->sortop, item2->sortop);
|
||||
/*
|
||||
* If it's "const = const" then just ignore it altogether.
|
||||
* There is no place in the restrictinfo structure to store
|
||||
* it. (If the two consts are in fact unequal, then
|
||||
* propagating the comparison to Vars will cause us to
|
||||
* produce zero rows out, as expected.)
|
||||
*/
|
||||
if (relids[i1] != NIL || relids[i2] != NIL)
|
||||
{
|
||||
/*
|
||||
* Tell process_implied_equality to delete the clause,
|
||||
* not add it, if it's "var = var" and we have constants
|
||||
* present in the list.
|
||||
*/
|
||||
bool delete_it = (have_consts &&
|
||||
relids[i1] != NIL &&
|
||||
relids[i2] != NIL);
|
||||
process_implied_equality(root,
|
||||
item1->key, item2->key,
|
||||
item1->sortop, item2->sortop,
|
||||
relids[i1], relids[i2],
|
||||
delete_it);
|
||||
}
|
||||
i2++;
|
||||
}
|
||||
i1++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* exprs_known_equal
|
||||
* Detect whether two expressions are known equal due to equijoin clauses.
|
||||
*
|
||||
* Note: does not bother to check for "equal(item1, item2)"; caller must
|
||||
* check that case if it's possible to pass identical items.
|
||||
*/
|
||||
bool
|
||||
exprs_known_equal(Query *root, Node *item1, Node *item2)
|
||||
{
|
||||
List *cursetlink;
|
||||
|
||||
foreach(cursetlink, root->equi_key_list)
|
||||
{
|
||||
List *curset = lfirst(cursetlink);
|
||||
bool item1member = false;
|
||||
bool item2member = false;
|
||||
List *ptr;
|
||||
|
||||
foreach(ptr, curset)
|
||||
{
|
||||
PathKeyItem *pitem = (PathKeyItem *) lfirst(ptr);
|
||||
|
||||
if (equal(item1, pitem->key))
|
||||
item1member = true;
|
||||
else if (equal(item2, pitem->key))
|
||||
item2member = true;
|
||||
/* Exit as soon as equality is proven */
|
||||
if (item1member && item2member)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* make_canonical_pathkey
|
||||
* Given a PathKeyItem, find the equi_key_list subset it is a member of,
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v 1.82 2003/01/20 18:54:52 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v 1.83 2003/01/24 03:58:43 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -40,8 +40,6 @@ static void distribute_qual_to_rels(Query *root, Node *clause,
|
||||
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);
|
||||
@ -539,7 +537,7 @@ distribute_qual_to_rels(Query *root, Node *clause,
|
||||
/*
|
||||
* Add clause to the join lists of all the relevant relations.
|
||||
*/
|
||||
add_join_info_to_rels(root, restrictinfo, relids);
|
||||
add_join_clause_to_rels(root, restrictinfo, relids);
|
||||
|
||||
/*
|
||||
* Add vars used in the join clause to targetlists of their
|
||||
@ -572,79 +570,96 @@ distribute_qual_to_rels(Query *root, Node *clause,
|
||||
add_equijoined_keys(root, restrictinfo);
|
||||
}
|
||||
|
||||
/*
|
||||
* add_join_info_to_rels
|
||||
* For every relation participating in a join clause, add 'restrictinfo' to
|
||||
* the appropriate joininfo list (creating a new list and adding it to the
|
||||
* appropriate rel node if necessary).
|
||||
*
|
||||
* Note that the same copy of the restrictinfo node is linked to by all the
|
||||
* lists it is in. This allows us to exploit caching of information about
|
||||
* the restriction clause (but we must be careful that the information does
|
||||
* not depend on context).
|
||||
*
|
||||
* 'restrictinfo' describes the join clause
|
||||
* 'join_relids' is the list of relations participating in the join clause
|
||||
*/
|
||||
static void
|
||||
add_join_info_to_rels(Query *root, RestrictInfo *restrictinfo,
|
||||
Relids join_relids)
|
||||
{
|
||||
List *join_relid;
|
||||
|
||||
/* For every relid, find the joininfo, and add the proper join entries */
|
||||
foreach(join_relid, join_relids)
|
||||
{
|
||||
int cur_relid = lfirsti(join_relid);
|
||||
Relids unjoined_relids = NIL;
|
||||
JoinInfo *joininfo;
|
||||
List *otherrel;
|
||||
|
||||
/* Get the relids not equal to the current relid */
|
||||
foreach(otherrel, join_relids)
|
||||
{
|
||||
if (lfirsti(otherrel) != cur_relid)
|
||||
unjoined_relids = lappendi(unjoined_relids, lfirsti(otherrel));
|
||||
}
|
||||
Assert(unjoined_relids != NIL);
|
||||
|
||||
/*
|
||||
* Find or make the joininfo node for this combination of rels,
|
||||
* and add the restrictinfo node to it.
|
||||
*/
|
||||
joininfo = make_joininfo_node(find_base_rel(root, cur_relid),
|
||||
unjoined_relids);
|
||||
joininfo->jinfo_restrictinfo = lappend(joininfo->jinfo_restrictinfo,
|
||||
restrictinfo);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* process_implied_equality
|
||||
* Check to see whether we already have a restrictinfo item that says
|
||||
* item1 = item2, and create one if not. This is a consequence of
|
||||
* transitivity of mergejoin equality: if we have mergejoinable
|
||||
* clauses A = B and B = C, we can deduce A = C (where = is an
|
||||
* appropriate mergejoinable operator).
|
||||
* item1 = item2, and create one if not; or if delete_it is true,
|
||||
* remove any such restrictinfo item.
|
||||
*
|
||||
* This processing is a consequence of transitivity of mergejoin equality:
|
||||
* if we have mergejoinable clauses A = B and B = C, we can deduce A = C
|
||||
* (where = is an appropriate mergejoinable operator). See path/pathkeys.c
|
||||
* for more details.
|
||||
*/
|
||||
void
|
||||
process_implied_equality(Query *root, Node *item1, Node *item2,
|
||||
Oid sortop1, Oid sortop2)
|
||||
process_implied_equality(Query *root,
|
||||
Node *item1, Node *item2,
|
||||
Oid sortop1, Oid sortop2,
|
||||
Relids item1_relids, Relids item2_relids,
|
||||
bool delete_it)
|
||||
{
|
||||
Relids relids;
|
||||
RelOptInfo *rel1;
|
||||
List *restrictlist;
|
||||
List *itm;
|
||||
Oid ltype,
|
||||
rtype;
|
||||
Operator eq_operator;
|
||||
Form_pg_operator pgopform;
|
||||
Expr *clause;
|
||||
|
||||
/* Get list of relids referenced in the two expressions */
|
||||
relids = set_unioni(item1_relids, item2_relids);
|
||||
|
||||
/*
|
||||
* Forget it if this equality is already recorded.
|
||||
*
|
||||
* Note: if only a single relation is involved, we may fall through
|
||||
* here and end up rejecting the equality later on in qual_is_redundant.
|
||||
* This is a tad slow but should be okay.
|
||||
* generate_implied_equalities() shouldn't call me on two constants.
|
||||
*/
|
||||
if (exprs_known_equal(root, item1, item2))
|
||||
Assert(relids != NIL);
|
||||
|
||||
/*
|
||||
* If the exprs involve a single rel, we need to look at that rel's
|
||||
* baserestrictinfo list. If multiple rels, any one will have a
|
||||
* joininfo node for the rest, and we can scan any of 'em.
|
||||
*/
|
||||
rel1 = find_base_rel(root, lfirsti(relids));
|
||||
if (lnext(relids) == NIL)
|
||||
restrictlist = rel1->baserestrictinfo;
|
||||
else
|
||||
{
|
||||
JoinInfo *joininfo = find_joininfo_node(rel1, lnext(relids));
|
||||
|
||||
restrictlist = joininfo ? joininfo->jinfo_restrictinfo : NIL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Scan to see if equality is already known. If so, we're done in
|
||||
* the add case, and done after removing it in the delete case.
|
||||
*/
|
||||
foreach(itm, restrictlist)
|
||||
{
|
||||
RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(itm);
|
||||
Node *left,
|
||||
*right;
|
||||
|
||||
if (restrictinfo->mergejoinoperator == InvalidOid)
|
||||
continue; /* ignore non-mergejoinable clauses */
|
||||
/* We now know the restrictinfo clause is a binary opclause */
|
||||
left = get_leftop(restrictinfo->clause);
|
||||
right = get_rightop(restrictinfo->clause);
|
||||
if ((equal(item1, left) && equal(item2, right)) ||
|
||||
(equal(item2, left) && equal(item1, right)))
|
||||
{
|
||||
/* found a matching clause */
|
||||
if (delete_it)
|
||||
{
|
||||
if (lnext(relids) == NIL)
|
||||
{
|
||||
/* delete it from local restrictinfo list */
|
||||
rel1->baserestrictinfo = lremove(restrictinfo,
|
||||
rel1->baserestrictinfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* let joininfo.c do it */
|
||||
remove_join_clause_from_rels(root, restrictinfo, relids);
|
||||
}
|
||||
}
|
||||
return; /* done */
|
||||
}
|
||||
}
|
||||
|
||||
/* Didn't find it. Done if deletion requested */
|
||||
if (delete_it)
|
||||
return;
|
||||
|
||||
/*
|
||||
@ -692,73 +707,7 @@ process_implied_equality(Query *root, Node *item1, Node *item2,
|
||||
*/
|
||||
distribute_qual_to_rels(root, (Node *) clause,
|
||||
true, false, true,
|
||||
pull_varnos((Node *) clause));
|
||||
}
|
||||
|
||||
/*
|
||||
* exprs_known_equal
|
||||
* Detect whether two expressions are known equal due to equijoin clauses.
|
||||
*
|
||||
* This is not completely accurate since we avoid adding redundant restriction
|
||||
* clauses to individual base rels (see qual_is_redundant). However, after
|
||||
* the implied-equality-deduction phase, it is complete for expressions
|
||||
* involving Vars of multiple rels; that's sufficient for planned uses.
|
||||
*/
|
||||
bool
|
||||
exprs_known_equal(Query *root, Node *item1, Node *item2)
|
||||
{
|
||||
List *relids;
|
||||
RelOptInfo *rel1;
|
||||
List *restrictlist;
|
||||
List *itm;
|
||||
|
||||
/* Get list of relids referenced in the two expressions */
|
||||
relids = set_unioni(pull_varnos(item1), pull_varnos(item2));
|
||||
|
||||
/*
|
||||
* If there are no Vars at all, say "true". This prevents
|
||||
* process_implied_equality from trying to store "const = const"
|
||||
* deductions.
|
||||
*/
|
||||
if (relids == NIL)
|
||||
return true;
|
||||
|
||||
/*
|
||||
* If the exprs involve a single rel, we need to look at that rel's
|
||||
* baserestrictinfo list. If multiple rels, any one will have a
|
||||
* joininfo node for the rest, and we can scan any of 'em.
|
||||
*/
|
||||
rel1 = find_base_rel(root, lfirsti(relids));
|
||||
relids = lnext(relids);
|
||||
if (relids == NIL)
|
||||
restrictlist = rel1->baserestrictinfo;
|
||||
else
|
||||
{
|
||||
JoinInfo *joininfo = find_joininfo_node(rel1, relids);
|
||||
|
||||
restrictlist = joininfo ? joininfo->jinfo_restrictinfo : NIL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Scan to see if equality is known.
|
||||
*/
|
||||
foreach(itm, restrictlist)
|
||||
{
|
||||
RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(itm);
|
||||
Node *left,
|
||||
*right;
|
||||
|
||||
if (restrictinfo->mergejoinoperator == InvalidOid)
|
||||
continue; /* ignore non-mergejoinable clauses */
|
||||
/* We now know the restrictinfo clause is a binary opclause */
|
||||
left = get_leftop(restrictinfo->clause);
|
||||
right = get_rightop(restrictinfo->clause);
|
||||
if ((equal(item1, left) && equal(item2, right)) ||
|
||||
(equal(item2, left) && equal(item1, right)))
|
||||
return true; /* found a matching clause */
|
||||
}
|
||||
|
||||
return false;
|
||||
relids);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -770,19 +719,32 @@ exprs_known_equal(Query *root, Node *item1, Node *item2)
|
||||
* 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.
|
||||
*
|
||||
* Note: quals of the form "var = const" are never considered redundant,
|
||||
* only those of the form "var = var". This is needed because when we
|
||||
* have constants in an implied-equality set, we use a different strategy
|
||||
* that suppresses all "var = var" deductions. We must therefore keep
|
||||
* all the "var = const" quals.
|
||||
*/
|
||||
static bool
|
||||
qual_is_redundant(Query *root,
|
||||
RestrictInfo *restrictinfo,
|
||||
List *restrictlist)
|
||||
{
|
||||
List *oldquals;
|
||||
List *olditem;
|
||||
Node *newleft;
|
||||
Node *newright;
|
||||
List *oldquals;
|
||||
List *olditem;
|
||||
List *equalexprs;
|
||||
bool someadded;
|
||||
|
||||
newleft = get_leftop(restrictinfo->clause);
|
||||
newright = get_rightop(restrictinfo->clause);
|
||||
|
||||
/* Never redundant unless vars appear on both sides */
|
||||
if (!contain_var_clause(newleft) || !contain_var_clause(newright))
|
||||
return false;
|
||||
|
||||
/*
|
||||
* Set cached pathkeys. NB: it is okay to do this now because this
|
||||
* routine is only invoked while we are generating implied equalities.
|
||||
@ -822,8 +784,6 @@ qual_is_redundant(Query *root,
|
||||
* we find we can reach the right-side expr of the new qual, we are
|
||||
* done. We give up when we can't expand the equalexprs list any more.
|
||||
*/
|
||||
newleft = get_leftop(restrictinfo->clause);
|
||||
newright = get_rightop(restrictinfo->clause);
|
||||
equalexprs = makeList1(newleft);
|
||||
do
|
||||
{
|
||||
|
@ -8,13 +8,14 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/joininfo.c,v 1.32 2003/01/20 18:54:56 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/joininfo.c,v 1.33 2003/01/24 03:58:43 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#include "postgres.h"
|
||||
|
||||
#include "optimizer/joininfo.h"
|
||||
#include "optimizer/pathnode.h"
|
||||
|
||||
|
||||
/*
|
||||
@ -63,3 +64,110 @@ make_joininfo_node(RelOptInfo *this_rel, Relids join_relids)
|
||||
}
|
||||
return joininfo;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* add_join_clause_to_rels
|
||||
* For every relation participating in a join clause, add 'restrictinfo' to
|
||||
* the appropriate joininfo list (creating a new list and adding it to the
|
||||
* appropriate rel node if necessary).
|
||||
*
|
||||
* Note that the same copy of the restrictinfo node is linked to by all the
|
||||
* lists it is in. This allows us to exploit caching of information about
|
||||
* the restriction clause (but we must be careful that the information does
|
||||
* not depend on context).
|
||||
*
|
||||
* 'restrictinfo' describes the join clause
|
||||
* 'join_relids' is the list of relations participating in the join clause
|
||||
* (there must be more than one)
|
||||
*/
|
||||
void
|
||||
add_join_clause_to_rels(Query *root,
|
||||
RestrictInfo *restrictinfo,
|
||||
Relids join_relids)
|
||||
{
|
||||
List *join_relid;
|
||||
|
||||
/* For every relid, find the joininfo, and add the proper join entries */
|
||||
foreach(join_relid, join_relids)
|
||||
{
|
||||
int cur_relid = lfirsti(join_relid);
|
||||
Relids unjoined_relids = NIL;
|
||||
JoinInfo *joininfo;
|
||||
List *otherrel;
|
||||
|
||||
/* Get the relids not equal to the current relid */
|
||||
foreach(otherrel, join_relids)
|
||||
{
|
||||
if (lfirsti(otherrel) != cur_relid)
|
||||
unjoined_relids = lappendi(unjoined_relids, lfirsti(otherrel));
|
||||
}
|
||||
Assert(unjoined_relids != NIL);
|
||||
|
||||
/*
|
||||
* Find or make the joininfo node for this combination of rels,
|
||||
* and add the restrictinfo node to it.
|
||||
*/
|
||||
joininfo = make_joininfo_node(find_base_rel(root, cur_relid),
|
||||
unjoined_relids);
|
||||
joininfo->jinfo_restrictinfo = lappend(joininfo->jinfo_restrictinfo,
|
||||
restrictinfo);
|
||||
/*
|
||||
* Can't freeList(unjoined_relids) because new joininfo node may
|
||||
* link to it. We could avoid leaking memory by doing listCopy()
|
||||
* in make_joininfo_node, but for now speed seems better.
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* remove_join_clause_from_rels
|
||||
* Delete 'restrictinfo' from all the joininfo lists it is in
|
||||
*
|
||||
* This reverses the effect of add_join_clause_to_rels. It's used when we
|
||||
* discover that a join clause is redundant.
|
||||
*
|
||||
* 'restrictinfo' describes the join clause
|
||||
* 'join_relids' is the list of relations participating in the join clause
|
||||
* (there must be more than one)
|
||||
*/
|
||||
void
|
||||
remove_join_clause_from_rels(Query *root,
|
||||
RestrictInfo *restrictinfo,
|
||||
Relids join_relids)
|
||||
{
|
||||
List *join_relid;
|
||||
|
||||
/* For every relid, find the joininfo */
|
||||
foreach(join_relid, join_relids)
|
||||
{
|
||||
int cur_relid = lfirsti(join_relid);
|
||||
Relids unjoined_relids = NIL;
|
||||
JoinInfo *joininfo;
|
||||
List *otherrel;
|
||||
|
||||
/* Get the relids not equal to the current relid */
|
||||
foreach(otherrel, join_relids)
|
||||
{
|
||||
if (lfirsti(otherrel) != cur_relid)
|
||||
unjoined_relids = lappendi(unjoined_relids, lfirsti(otherrel));
|
||||
}
|
||||
Assert(unjoined_relids != NIL);
|
||||
|
||||
/*
|
||||
* Find the joininfo node for this combination of rels; it should
|
||||
* exist already, if add_join_clause_to_rels was called.
|
||||
*/
|
||||
joininfo = find_joininfo_node(find_base_rel(root, cur_relid),
|
||||
unjoined_relids);
|
||||
Assert(joininfo);
|
||||
/*
|
||||
* Remove the restrictinfo from the list. Pointer comparison
|
||||
* is sufficient.
|
||||
*/
|
||||
Assert(ptrMember(restrictinfo, joininfo->jinfo_restrictinfo));
|
||||
joininfo->jinfo_restrictinfo = lremove(restrictinfo,
|
||||
joininfo->jinfo_restrictinfo);
|
||||
freeList(unjoined_relids);
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/relnode.c,v 1.44 2003/01/20 18:54:56 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/relnode.c,v 1.45 2003/01/24 03:58:43 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -549,14 +549,19 @@ subbuild_joinrel_joinlist(RelOptInfo *joinrel,
|
||||
/*
|
||||
* These clauses are still join clauses at this level, so find
|
||||
* or make the appropriate JoinInfo item for the joinrel, and
|
||||
* add the clauses to it (eliminating duplicates).
|
||||
* add the clauses to it, eliminating duplicates. (Since
|
||||
* RestrictInfo nodes are normally multiply-linked rather than
|
||||
* copied, pointer equality should be a sufficient test. If
|
||||
* two equal() nodes should happen to sneak in, no great harm
|
||||
* is done --- they'll be detected by redundant-clause testing
|
||||
* when they reach a restriction list.)
|
||||
*/
|
||||
JoinInfo *new_joininfo;
|
||||
|
||||
new_joininfo = make_joininfo_node(joinrel, new_unjoined_relids);
|
||||
new_joininfo->jinfo_restrictinfo =
|
||||
set_union(new_joininfo->jinfo_restrictinfo,
|
||||
joininfo->jinfo_restrictinfo);
|
||||
set_ptrUnion(new_joininfo->jinfo_restrictinfo,
|
||||
joininfo->jinfo_restrictinfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/restrictinfo.c,v 1.15 2002/11/24 21:52:14 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/restrictinfo.c,v 1.16 2003/01/24 03:58:43 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -17,6 +17,7 @@
|
||||
#include "optimizer/clauses.h"
|
||||
#include "optimizer/paths.h"
|
||||
#include "optimizer/restrictinfo.h"
|
||||
#include "optimizer/var.h"
|
||||
|
||||
|
||||
/*
|
||||
@ -101,6 +102,13 @@ get_actual_join_clauses(List *restrictinfo_list,
|
||||
* equality between any set member on the left and any member on the right;
|
||||
* by transitivity, all the rest are then equal.
|
||||
*
|
||||
* However, clauses that are of the form "var expr = const expr" cannot be
|
||||
* eliminated as redundant. This is because when there are const expressions
|
||||
* in a pathkey set, generate_implied_equalities() suppresses "var = var"
|
||||
* clauses in favor of "var = const" clauses. We cannot afford to drop any
|
||||
* of the latter, even though they might seem redundant by the pathkey
|
||||
* membership test.
|
||||
*
|
||||
* Weird special case: if we have two clauses that seem redundant
|
||||
* except one is pushed down into an outer join and the other isn't,
|
||||
* then they're not really redundant, because one constrains the
|
||||
@ -120,7 +128,7 @@ remove_redundant_join_clauses(Query *root, List *restrictinfo_list,
|
||||
{
|
||||
RestrictInfo *rinfo = (RestrictInfo *) lfirst(item);
|
||||
|
||||
/* eliminate duplicates */
|
||||
/* always eliminate duplicates */
|
||||
if (member(rinfo, result))
|
||||
continue;
|
||||
|
||||
@ -132,6 +140,7 @@ remove_redundant_join_clauses(Query *root, List *restrictinfo_list,
|
||||
|
||||
cache_mergeclause_pathkeys(root, rinfo);
|
||||
|
||||
/* do the cheap tests first */
|
||||
foreach(olditem, result)
|
||||
{
|
||||
RestrictInfo *oldrinfo = (RestrictInfo *) lfirst(olditem);
|
||||
@ -148,7 +157,20 @@ remove_redundant_join_clauses(Query *root, List *restrictinfo_list,
|
||||
}
|
||||
|
||||
if (redundant)
|
||||
continue;
|
||||
{
|
||||
/*
|
||||
* It looks redundant, now check for "var = const" case.
|
||||
* If left_relids/right_relids are set, then there are
|
||||
* definitely vars on both sides; else we must check the
|
||||
* hard way.
|
||||
*/
|
||||
if (rinfo->left_relids)
|
||||
continue; /* var = var, so redundant */
|
||||
if (contain_var_clause(get_leftop(rinfo->clause)) &&
|
||||
contain_var_clause(get_rightop(rinfo->clause)))
|
||||
continue; /* var = var, so redundant */
|
||||
/* else var = const, not redundant */
|
||||
}
|
||||
}
|
||||
|
||||
/* otherwise, add it to result list */
|
||||
|
@ -15,7 +15,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/utils/adt/selfuncs.c,v 1.128 2003/01/22 20:16:42 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/utils/adt/selfuncs.c,v 1.129 2003/01/24 03:58:43 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -84,8 +84,8 @@
|
||||
#include "optimizer/clauses.h"
|
||||
#include "optimizer/cost.h"
|
||||
#include "optimizer/pathnode.h"
|
||||
#include "optimizer/paths.h"
|
||||
#include "optimizer/plancat.h"
|
||||
#include "optimizer/planmain.h"
|
||||
#include "optimizer/prep.h"
|
||||
#include "optimizer/tlist.h"
|
||||
#include "optimizer/var.h"
|
||||
|
@ -7,7 +7,7 @@
|
||||
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: pg_list.h,v 1.31 2003/01/20 18:55:04 tgl Exp $
|
||||
* $Id: pg_list.h,v 1.32 2003/01/24 03:58:43 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -141,6 +141,7 @@ extern List *set_differencei(List *list1, List *list2);
|
||||
extern List *lreverse(List *l);
|
||||
extern List *set_union(List *list1, List *list2);
|
||||
extern List *set_unioni(List *list1, List *list2);
|
||||
extern List *set_ptrUnion(List *list1, List *list2);
|
||||
extern List *set_intersecti(List *list1, List *list2);
|
||||
|
||||
extern bool equali(List *list1, List *list2);
|
||||
|
@ -7,7 +7,7 @@
|
||||
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: joininfo.h,v 1.22 2003/01/20 18:55:04 tgl Exp $
|
||||
* $Id: joininfo.h,v 1.23 2003/01/24 03:58:43 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -16,7 +16,15 @@
|
||||
|
||||
#include "nodes/relation.h"
|
||||
|
||||
extern JoinInfo *find_joininfo_node(RelOptInfo *this_rel, List *join_relids);
|
||||
extern JoinInfo *make_joininfo_node(RelOptInfo *this_rel, List *join_relids);
|
||||
|
||||
extern JoinInfo *find_joininfo_node(RelOptInfo *this_rel, Relids join_relids);
|
||||
extern JoinInfo *make_joininfo_node(RelOptInfo *this_rel, Relids join_relids);
|
||||
|
||||
extern void add_join_clause_to_rels(Query *root,
|
||||
RestrictInfo *restrictinfo,
|
||||
Relids join_relids);
|
||||
extern void remove_join_clause_from_rels(Query *root,
|
||||
RestrictInfo *restrictinfo,
|
||||
Relids join_relids);
|
||||
|
||||
#endif /* JOININFO_H */
|
||||
|
@ -8,7 +8,7 @@
|
||||
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: paths.h,v 1.63 2002/12/16 21:30:30 tgl Exp $
|
||||
* $Id: paths.h,v 1.64 2003/01/24 03:58:43 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -17,6 +17,7 @@
|
||||
|
||||
#include "nodes/relation.h"
|
||||
|
||||
|
||||
/* default GEQO threshold (default value for geqo_rels) */
|
||||
/* If you change this, update backend/utils/misc/postgresql.sample.conf */
|
||||
#define DEFAULT_GEQO_RELS 11
|
||||
@ -92,6 +93,7 @@ typedef enum
|
||||
} PathKeysComparison;
|
||||
|
||||
extern void add_equijoined_keys(Query *root, RestrictInfo *restrictinfo);
|
||||
extern bool exprs_known_equal(Query *root, Node *item1, Node *item2);
|
||||
extern void generate_implied_equalities(Query *root);
|
||||
extern List *canonicalize_pathkeys(Query *root, List *pathkeys);
|
||||
extern PathKeysComparison compare_pathkeys(List *keys1, List *keys2);
|
||||
|
@ -7,7 +7,7 @@
|
||||
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: planmain.h,v 1.67 2003/01/20 18:55:05 tgl Exp $
|
||||
* $Id: planmain.h,v 1.68 2003/01/24 03:58:44 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -57,9 +57,11 @@ extern Result *make_result(List *tlist, Node *resconstantqual, Plan *subplan);
|
||||
extern void add_base_rels_to_query(Query *root, Node *jtnode);
|
||||
extern void build_base_rel_tlists(Query *root, List *tlist);
|
||||
extern Relids distribute_quals_to_rels(Query *root, Node *jtnode);
|
||||
extern void process_implied_equality(Query *root, Node *item1, Node *item2,
|
||||
Oid sortop1, Oid sortop2);
|
||||
extern bool exprs_known_equal(Query *root, Node *item1, Node *item2);
|
||||
extern void process_implied_equality(Query *root,
|
||||
Node *item1, Node *item2,
|
||||
Oid sortop1, Oid sortop2,
|
||||
Relids item1_relids, Relids item2_relids,
|
||||
bool delete_it);
|
||||
|
||||
/*
|
||||
* prototypes for plan/setrefs.c
|
||||
|
Loading…
x
Reference in New Issue
Block a user