diff options
Diffstat (limited to 'src/backend/optimizer/plan/initsplan.c')
-rw-r--r-- | src/backend/optimizer/plan/initsplan.c | 228 |
1 files changed, 94 insertions, 134 deletions
diff --git a/src/backend/optimizer/plan/initsplan.c b/src/backend/optimizer/plan/initsplan.c index 037ed3314cf..3a824d55d72 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 @@ -573,78 +571,95 @@ distribute_qual_to_rels(Query *root, Node *clause, } /* - * 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 { |