diff --git a/src/backend/nodes/list.c b/src/backend/nodes/list.c index e896b479018b718d2094e8d6d8a53c45e25e35ec..bf9e5c10d6f0c3f6531a71643275b733d9126259 100644 --- a/src/backend/nodes/list.c +++ b/src/backend/nodes/list.c @@ -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. diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c index 02a92fd99605d5ef26dc13c0e50dc3b288d45b10..443d54c64733ba0ed20edaf1151257e5460e6a49 100644 --- a/src/backend/optimizer/path/indxpath.c +++ b/src/backend/optimizer/path/indxpath.c @@ -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) diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c index 194bdddc2f09c1b151b3f64053331e9c3b0e7157..b99a44a440420d99b0c995fa8ea04ac20e486999 100644 --- a/src/backend/optimizer/path/pathkeys.c +++ b/src/backend/optimizer/path/pathkeys.c @@ -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, diff --git a/src/backend/optimizer/plan/initsplan.c b/src/backend/optimizer/plan/initsplan.c index 037ed3314cf866e6087164187c6a36fa4587f7e9..3a824d55d72b7bd1c6d2d06bd33f96770efb118f 100644 --- a/src/backend/optimizer/plan/initsplan.c +++ b/src/backend/optimizer/plan/initsplan.c @@ -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. + */ + 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. */ - if (exprs_known_equal(root, item1, item2)) + 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 { diff --git a/src/backend/optimizer/util/joininfo.c b/src/backend/optimizer/util/joininfo.c index c202615b1f5d8304edfd1fe3f6f9976cad80000a..79a9f7a3bac35d7d67dc21f228324c60fcdc3678 100644 --- a/src/backend/optimizer/util/joininfo.c +++ b/src/backend/optimizer/util/joininfo.c @@ -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); + } +} diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c index 144fac75501947bfd5c511489f26e7eceb934319..06a73bf4e9e3a26021dfde3d6490c579feb26dc7 100644 --- a/src/backend/optimizer/util/relnode.c +++ b/src/backend/optimizer/util/relnode.c @@ -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); } } } diff --git a/src/backend/optimizer/util/restrictinfo.c b/src/backend/optimizer/util/restrictinfo.c index bc1fcc36464207c76fe16b177384c9174939d28f..bdcc338d609a22fa50f24fb409cdcc896a52b9e1 100644 --- a/src/backend/optimizer/util/restrictinfo.c +++ b/src/backend/optimizer/util/restrictinfo.c @@ -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 */ diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c index 20d353a0a5099f88efd517e2d50c4d6bdb390459..62e0b8b32a9c2f5e8b94fe09d215265678484dc4 100644 --- a/src/backend/utils/adt/selfuncs.c +++ b/src/backend/utils/adt/selfuncs.c @@ -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" diff --git a/src/include/nodes/pg_list.h b/src/include/nodes/pg_list.h index d3b01b7fed0ad00f4c7bc5ca06ae077d38303ac8..56a6640916113066d9053378be3f569a923c4f19 100644 --- a/src/include/nodes/pg_list.h +++ b/src/include/nodes/pg_list.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); diff --git a/src/include/optimizer/joininfo.h b/src/include/optimizer/joininfo.h index 37131b722d2799567e9182c7320f17d1f4c93ffd..6fd806bbaf1bb571a6302abbf29171ccebb19f05 100644 --- a/src/include/optimizer/joininfo.h +++ b/src/include/optimizer/joininfo.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: 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 */ diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h index 7ed5b403a87c1bc1f2b1798b412c7c35f01d5a4b..76285bac408c63d53bde46e517fd935f2ac7b304 100644 --- a/src/include/optimizer/paths.h +++ b/src/include/optimizer/paths.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); diff --git a/src/include/optimizer/planmain.h b/src/include/optimizer/planmain.h index cf9c2ddeb64315b0e8d2e2b69fab1318075c6b43..399b3bb1310eaafd512c1f33a21cedae04c9dacd 100644 --- a/src/include/optimizer/planmain.h +++ b/src/include/optimizer/planmain.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: 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