diff options
-rw-r--r-- | src/backend/optimizer/path/pathkeys.c | 83 | ||||
-rw-r--r-- | src/backend/optimizer/plan/initsplan.c | 113 | ||||
-rw-r--r-- | src/backend/optimizer/plan/planmain.c | 14 | ||||
-rw-r--r-- | src/include/optimizer/paths.h | 3 | ||||
-rw-r--r-- | src/include/optimizer/planmain.h | 4 |
5 files changed, 207 insertions, 10 deletions
diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c index 580675a85b7..6c507cfd452 100644 --- a/src/backend/optimizer/path/pathkeys.c +++ b/src/backend/optimizer/path/pathkeys.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/path/pathkeys.c,v 1.21 2000/04/12 17:15:20 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/path/pathkeys.c,v 1.21.2.1 2000/09/23 23:50:47 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -19,6 +19,7 @@ #include "optimizer/joininfo.h" #include "optimizer/pathnode.h" #include "optimizer/paths.h" +#include "optimizer/planmain.h" #include "optimizer/tlist.h" #include "optimizer/var.h" #include "parser/parsetree.h" @@ -227,36 +228,108 @@ add_equijoined_keys(Query *root, RestrictInfo *restrictinfo) * into our new set. When done, we add the new set to the front of * equi_key_list. * + * It may well be that the two items we're given are already known to + * be equijoin-equivalent, in which case we don't need to change our + * data structure. If we find both of them in the same equivalence + * set to start with, we can quit immediately. + * * This is a standard UNION-FIND problem, for which there exist better * data structures than simple lists. If this code ever proves to be * a bottleneck then it could be sped up --- but for now, simple is * beautiful. */ - newset = lcons(item1, lcons(item2, NIL)); + newset = NIL; foreach(cursetlink, root->equi_key_list) { List *curset = lfirst(cursetlink); + bool item1here = member(item1, curset); + bool item2here = member(item2, curset); - if (member(item1, curset) || member(item2, curset)) + if (item1here || item2here) { + /* If find both in same equivalence set, no need to do any more */ + if (item1here && item2here) + { + /* Better not have seen only one in an earlier set... */ + Assert(newset == NIL); + return; + } + + /* Build the new set only when we know we must */ + if (newset == NIL) + newset = lcons(item1, lcons(item2, NIL)); + /* Found a set to merge into our new set */ newset = LispUnion(newset, curset); /* * Remove old set from equi_key_list. NOTE this does not - * change lnext(cursetlink), so the outer foreach doesn't - * break. + * change lnext(cursetlink), so the foreach loop doesn't break. */ root->equi_key_list = lremove(curset, root->equi_key_list); freeList(curset); /* might as well recycle old cons cells */ } } + /* Build the new set only when we know we must */ + if (newset == NIL) + newset = lcons(item1, lcons(item2, NIL)); + root->equi_key_list = lcons(newset, root->equi_key_list); } /* + * 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). + * + * 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. + */ +void +generate_implied_equalities(Query *root) +{ + List *cursetlink; + + foreach(cursetlink, root->equi_key_list) + { + List *curset = lfirst(cursetlink); + List *ptr1; + + /* + * 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) + continue; + + /* + * 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). + */ + foreach(ptr1, curset) + { + PathKeyItem *item1 = (PathKeyItem *) lfirst(ptr1); + List *ptr2; + + foreach(ptr2, lnext(ptr1)) + { + PathKeyItem *item2 = (PathKeyItem *) lfirst(ptr2); + + process_implied_equality(root, item1->key, item2->key, + item1->sortop, item2->sortop); + } + } + } +} + +/* * make_canonical_pathkey * Given a PathKeyItem, find the equi_key_list subset it is a member of, * if any. If so, return a pointer to that sublist, which is the diff --git a/src/backend/optimizer/plan/initsplan.c b/src/backend/optimizer/plan/initsplan.c index 207981b527f..6025b61be69 100644 --- a/src/backend/optimizer/plan/initsplan.c +++ b/src/backend/optimizer/plan/initsplan.c @@ -8,13 +8,14 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v 1.46 2000/04/12 17:15:21 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v 1.46.2.1 2000/09/23 23:50:47 tgl Exp $ * *------------------------------------------------------------------------- */ #include <sys/types.h> #include "postgres.h" +#include "catalog/pg_operator.h" #include "catalog/pg_type.h" #include "nodes/makefuncs.h" #include "optimizer/clauses.h" @@ -25,6 +26,9 @@ #include "optimizer/planmain.h" #include "optimizer/tlist.h" #include "optimizer/var.h" +#include "parser/parse_expr.h" +#include "parser/parse_oper.h" +#include "parser/parse_type.h" #include "utils/lsyscache.h" @@ -280,6 +284,113 @@ add_join_info_to_rels(Query *root, 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). + */ +void +process_implied_equality(Query *root, Node *item1, Node *item2, + Oid sortop1, Oid sortop2) +{ + Index irel1; + Index irel2; + RelOptInfo *rel1; + List *restrictlist; + List *itm; + Oid ltype, + rtype; + Operator eq_operator; + Form_pg_operator pgopform; + Expr *clause; + + /* + * Currently, since check_mergejoinable only accepts Var = Var clauses, + * we should only see Var nodes here. Would have to work a little + * harder to locate the right rel(s) if more-general mergejoin clauses + * were accepted. + */ + Assert(IsA(item1, Var)); + irel1 = ((Var *) item1)->varno; + Assert(IsA(item2, Var)); + irel2 = ((Var *) item2)->varno; + /* + * If both vars belong to same rel, we need to look at that rel's + * baserestrictinfo list. If different rels, each will have a + * joininfo node for the other, and we can scan either list. + */ + rel1 = get_base_rel(root, irel1); + if (irel1 == irel2) + restrictlist = rel1->baserestrictinfo; + else + { + JoinInfo *joininfo = find_joininfo_node(rel1, + lconsi(irel2, NIL)); + + restrictlist = joininfo->jinfo_restrictinfo; + } + /* + * Scan to see if equality is already 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 = (Node *) get_leftop(restrictinfo->clause); + right = (Node *) get_rightop(restrictinfo->clause); + if ((equal(item1, left) && equal(item2, right)) || + (equal(item2, left) && equal(item1, right))) + return; /* found a matching clause */ + } + /* + * This equality is new information, so construct a clause + * representing it to add to the query data structures. + */ + ltype = exprType(item1); + rtype = exprType(item2); + eq_operator = oper("=", ltype, rtype, true); + if (!HeapTupleIsValid(eq_operator)) + { + /* + * Would it be safe to just not add the equality to the query if + * we have no suitable equality operator for the combination of + * datatypes? NO, because sortkey selection may screw up anyway. + */ + elog(ERROR, "Unable to identify an equality operator for types '%s' and '%s'", + typeidTypeName(ltype), typeidTypeName(rtype)); + } + pgopform = (Form_pg_operator) GETSTRUCT(eq_operator); + /* + * Let's just make sure this appears to be a compatible operator. + */ + if (pgopform->oprlsortop != sortop1 || + pgopform->oprrsortop != sortop2 || + pgopform->oprresult != BOOLOID) + elog(ERROR, "Equality operator for types '%s' and '%s' should be mergejoinable, but isn't", + typeidTypeName(ltype), typeidTypeName(rtype)); + + clause = makeNode(Expr); + clause->typeOid = BOOLOID; + clause->opType = OP_EXPR; + clause->oper = (Node *) makeOper(oprid(eq_operator), /* opno */ + InvalidOid, /* opid */ + BOOLOID, /* operator result type */ + 0, + NULL); + clause->args = lcons(item1, lcons(item2, NIL)); + + add_restrict_and_join_to_rel(root, (Node *) clause); +} + + /***************************************************************************** * * CHECKS FOR MERGEJOINABLE AND HASHJOINABLE CLAUSES diff --git a/src/backend/optimizer/plan/planmain.c b/src/backend/optimizer/plan/planmain.c index 0e05c945380..5e0619a3d8d 100644 --- a/src/backend/optimizer/plan/planmain.c +++ b/src/backend/optimizer/plan/planmain.c @@ -14,7 +14,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planmain.c,v 1.55 2000/04/12 17:15:22 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planmain.c,v 1.55.2.1 2000/09/23 23:50:47 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -184,7 +184,7 @@ subplanner(Query *root, * base_rel_list as relation references are found (e.g., in the * qualification, the targetlist, etc.). Restrict and join clauses * are added to appropriate lists belonging to the mentioned - * relations, and we also build lists of equijoined keys for pathkey + * relations. We also build lists of equijoined keys for pathkey * construction. */ root->base_rel_list = NIL; @@ -193,9 +193,19 @@ subplanner(Query *root, make_var_only_tlist(root, flat_tlist); add_restrict_and_join_to_rels(root, qual); + + /* + * Make sure we have RelOptInfo nodes for all relations used. + */ add_missing_rels_to_query(root); /* + * Use the completed lists of equijoined keys to deduce any implied + * but unstated equalities (for example, A=B and B=C imply A=C). + */ + generate_implied_equalities(root); + + /* * We should now have all the pathkey equivalence sets built, so it's * now possible to convert the requested query_pathkeys to canonical * form. diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h index 0a2f56db63a..0c45fc897f1 100644 --- a/src/include/optimizer/paths.h +++ b/src/include/optimizer/paths.h @@ -8,7 +8,7 @@ * Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: paths.h,v 1.44 2000/04/12 17:16:42 momjian Exp $ + * $Id: paths.h,v 1.44.2.1 2000/09/23 23:50:46 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -90,6 +90,7 @@ typedef enum } PathKeysComparison; extern void add_equijoined_keys(Query *root, RestrictInfo *restrictinfo); +extern void generate_implied_equalities(Query *root); extern List *canonicalize_pathkeys(Query *root, List *pathkeys); extern PathKeysComparison compare_pathkeys(List *keys1, List *keys2); extern bool pathkeys_contained_in(List *keys1, List *keys2); diff --git a/src/include/optimizer/planmain.h b/src/include/optimizer/planmain.h index e0a0d84bc8a..12e9d119578 100644 --- a/src/include/optimizer/planmain.h +++ b/src/include/optimizer/planmain.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: planmain.h,v 1.39 2000/04/12 17:16:42 momjian Exp $ + * $Id: planmain.h,v 1.39.2.1 2000/09/23 23:50:46 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -43,6 +43,8 @@ extern Result *make_result(List *tlist, Node *resconstantqual, Plan *subplan); extern void make_var_only_tlist(Query *root, List *tlist); extern void add_restrict_and_join_to_rels(Query *root, List *clauses); extern void add_missing_rels_to_query(Query *root); +extern void process_implied_equality(Query *root, Node *item1, Node *item2, + Oid sortop1, Oid sortop2); /* * prototypes for plan/setrefs.c |