diff options
Diffstat (limited to 'src/backend/optimizer/util')
-rw-r--r-- | src/backend/optimizer/util/clauses.c | 11 | ||||
-rw-r--r-- | src/backend/optimizer/util/relnode.c | 150 | ||||
-rw-r--r-- | src/backend/optimizer/util/var.c | 375 |
3 files changed, 496 insertions, 40 deletions
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c index 8a4bcf4d9c4..6585fb1905d 100644 --- a/src/backend/optimizer/util/clauses.c +++ b/src/backend/optimizer/util/clauses.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.93 2002/01/03 18:01:59 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.94 2002/03/12 00:51:50 tgl Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -1808,12 +1808,8 @@ expression_tree_walker(Node *node, return true; if (walker(join->quals, context)) return true; - if (walker((Node *) join->colvars, context)) - return true; - /* - * alias clause, using list, colnames list are deemed - * uninteresting. + * alias clause, using list are deemed uninteresting. */ } break; @@ -2186,8 +2182,7 @@ expression_tree_mutator(Node *node, MUTATE(newnode->larg, join->larg, Node *); MUTATE(newnode->rarg, join->rarg, Node *); MUTATE(newnode->quals, join->quals, Node *); - MUTATE(newnode->colvars, join->colvars, List *); - /* We do not mutate alias, using, or colnames by default */ + /* We do not mutate alias or using by default */ return (Node *) newnode; } break; diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c index 457826f5d31..0fad16fbdd0 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.35 2001/10/25 05:49:34 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/util/relnode.c,v 1.36 2002/03/12 00:51:51 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -40,26 +40,26 @@ static void subbuild_joinrel_joinlist(RelOptInfo *joinrel, /* * build_base_rel - * Returns relation entry corresponding to 'relid', creating a new one - * if necessary. This is for base relations. + * Construct a new base relation RelOptInfo, and put it in the query's + * base_rel_list. */ -RelOptInfo * +void build_base_rel(Query *root, int relid) { List *rels; RelOptInfo *rel; - /* Already made? */ + /* Rel should not exist already */ foreach(rels, root->base_rel_list) { rel = (RelOptInfo *) lfirst(rels); /* length(rel->relids) == 1 for all members of base_rel_list */ if (lfirsti(rel->relids) == relid) - return rel; + elog(ERROR, "build_base_rel: rel already exists"); } - /* It should not exist as an "other" rel */ + /* It should not exist as an "other" rel, either */ foreach(rels, root->other_rel_list) { rel = (RelOptInfo *) lfirst(rels); @@ -73,14 +73,12 @@ build_base_rel(Query *root, int relid) /* and add it to the list */ root->base_rel_list = lcons(rel, root->base_rel_list); - - return rel; } /* * build_other_rel * Returns relation entry corresponding to 'relid', creating a new one - * if necessary. This is for 'other' relations, which are just like + * if necessary. This is for 'other' relations, which are much like * base relations except that they live in a different list. */ RelOptInfo * @@ -111,6 +109,10 @@ build_other_rel(Query *root, int relid) /* No existing RelOptInfo for this other rel, so make a new one */ rel = make_base_rel(root, relid); + /* if it's not a join rel, must be a child rel */ + if (rel->reloptkind == RELOPT_BASEREL) + rel->reloptkind = RELOPT_OTHER_CHILD_REL; + /* and add it to the list */ root->other_rel_list = lcons(rel, root->other_rel_list); @@ -127,8 +129,9 @@ static RelOptInfo * make_base_rel(Query *root, int relid) { RelOptInfo *rel = makeNode(RelOptInfo); - Oid relationObjectId; + RangeTblEntry *rte = rt_fetch(relid, root->rtable); + rel->reloptkind = RELOPT_BASEREL; rel->relids = makeListi1(relid); rel->rows = 0; rel->width = 0; @@ -142,29 +145,40 @@ make_base_rel(Query *root, int relid) rel->pages = 0; rel->tuples = 0; rel->subplan = NULL; + rel->joinrti = 0; + rel->joinrteids = NIL; rel->baserestrictinfo = NIL; rel->baserestrictcost = 0; rel->outerjoinset = NIL; rel->joininfo = NIL; rel->innerjoin = NIL; - /* Check rtable to see if it's a plain relation or a subquery */ - relationObjectId = getrelid(relid, root->rtable); - - if (relationObjectId != InvalidOid) - { - /* Plain relation --- retrieve statistics from the system catalogs */ - bool indexed; - - get_relation_info(relationObjectId, - &indexed, &rel->pages, &rel->tuples); - if (indexed) - rel->indexlist = find_secondary_indexes(relationObjectId); - } - else + /* Check type of rtable entry */ + switch (rte->rtekind) { - /* subquery --- mark it as such for later processing */ - rel->issubquery = true; + case RTE_RELATION: + { + /* Table --- retrieve statistics from the system catalogs */ + bool indexed; + + get_relation_info(rte->relid, + &indexed, &rel->pages, &rel->tuples); + if (indexed) + rel->indexlist = find_secondary_indexes(rte->relid); + break; + } + case RTE_SUBQUERY: + /* Subquery --- mark it as such for later processing */ + rel->issubquery = true; + break; + case RTE_JOIN: + /* Join --- must be an otherrel */ + rel->reloptkind = RELOPT_OTHER_JOIN_REL; + break; + default: + elog(ERROR, "make_base_rel: unsupported RTE kind %d", + (int) rte->rtekind); + break; } return rel; @@ -204,6 +218,47 @@ find_base_rel(Query *root, int relid) } /* + * find_other_rel + * Find an otherrel entry, if one exists for the given relid. + * Return NULL if no entry. + */ +RelOptInfo * +find_other_rel(Query *root, int relid) +{ + List *rels; + + foreach(rels, root->other_rel_list) + { + RelOptInfo *rel = (RelOptInfo *) lfirst(rels); + + if (lfirsti(rel->relids) == relid) + return rel; + } + return NULL; +} + +/* + * find_other_rel_for_join + * Look for an otherrel for a join RTE matching the given baserel set. + * Return NULL if no entry. + */ +RelOptInfo * +find_other_rel_for_join(Query *root, List *relids) +{ + List *rels; + + foreach(rels, root->other_rel_list) + { + RelOptInfo *rel = (RelOptInfo *) lfirst(rels); + + if (rel->reloptkind == RELOPT_OTHER_JOIN_REL + && sameseti(relids, rel->outerjoinset)) + return rel; + } + return NULL; +} + +/* * find_join_rel * Returns relation entry corresponding to 'relids' (a list of RT indexes), * or NULL if none exists. This is for join relations. @@ -252,6 +307,7 @@ build_join_rel(Query *root, { List *joinrelids; RelOptInfo *joinrel; + RelOptInfo *joinrterel; List *restrictlist; List *new_outer_tlist; List *new_inner_tlist; @@ -286,6 +342,7 @@ build_join_rel(Query *root, * Nope, so make one. */ joinrel = makeNode(RelOptInfo); + joinrel->reloptkind = RELOPT_JOINREL; joinrel->relids = joinrelids; joinrel->rows = 0; joinrel->width = 0; @@ -299,24 +356,38 @@ build_join_rel(Query *root, joinrel->pages = 0; joinrel->tuples = 0; joinrel->subplan = NULL; + joinrel->joinrti = 0; + joinrel->joinrteids = nconc(listCopy(outer_rel->joinrteids), + inner_rel->joinrteids); joinrel->baserestrictinfo = NIL; joinrel->baserestrictcost = 0; joinrel->outerjoinset = NIL; joinrel->joininfo = NIL; joinrel->innerjoin = NIL; + /* Is there a join RTE matching this join? */ + joinrterel = find_other_rel_for_join(root, joinrelids); + if (joinrterel) + { + /* Yes, remember its RT index */ + joinrel->joinrti = lfirsti(joinrterel->relids); + joinrel->joinrteids = lconsi(joinrel->joinrti, joinrel->joinrteids); + } + /* * Create a new tlist by removing irrelevant elements from both tlists * of the outer and inner join relations and then merging the results * together. * + * XXX right now we don't remove any irrelevant elements, we just + * append the two tlists together. Someday consider pruning vars from the + * join's targetlist if they are needed only to evaluate restriction + * clauses of this join, and will never be accessed at higher levels of + * the plantree. + * * NOTE: the tlist order for a join rel will depend on which pair of * outer and inner rels we first try to build it from. But the * contents should be the same regardless. - * - * XXX someday: consider pruning vars from the join's targetlist if they - * are needed only to evaluate restriction clauses of this join, and - * will never be accessed at higher levels of the plantree. */ new_outer_tlist = new_join_tlist(outer_rel->targetlist, 1); new_inner_tlist = new_join_tlist(inner_rel->targetlist, @@ -324,6 +395,23 @@ build_join_rel(Query *root, joinrel->targetlist = nconc(new_outer_tlist, new_inner_tlist); /* + * If there are any alias variables attached to the matching join RTE, + * attach them to the tlist too, so that they will be evaluated for use + * at higher plan levels. + */ + if (joinrterel) + { + List *jrtetl; + + foreach(jrtetl, joinrterel->targetlist) + { + TargetEntry *jrtete = lfirst(jrtetl); + + add_var_to_tlist(joinrel, (Var *) jrtete->expr); + } + } + + /* * Construct restrict and join clause lists for the new joinrel. (The * caller might or might not need the restrictlist, but I need it * anyway for set_joinrel_size_estimates().) diff --git a/src/backend/optimizer/util/var.c b/src/backend/optimizer/util/var.c index 2521ffb2b6c..568e024d20b 100644 --- a/src/backend/optimizer/util/var.c +++ b/src/backend/optimizer/util/var.c @@ -8,15 +8,18 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/util/var.c,v 1.33 2001/10/25 05:49:34 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/util/var.c,v 1.34 2002/03/12 00:51:51 tgl Exp $ * *------------------------------------------------------------------------- */ #include "postgres.h" +#include "nodes/makefuncs.h" #include "nodes/plannodes.h" #include "optimizer/clauses.h" #include "optimizer/var.h" +#include "parser/parsetree.h" +#include "parser/parse_coerce.h" typedef struct @@ -38,6 +41,12 @@ typedef struct bool includeUpperVars; } pull_var_clause_context; +typedef struct +{ + Query *root; + int expandRTI; +} flatten_join_alias_vars_context; + static bool pull_varnos_walker(Node *node, pull_varnos_context *context); static bool contain_var_reference_walker(Node *node, @@ -45,6 +54,10 @@ static bool contain_var_reference_walker(Node *node, static bool contain_var_clause_walker(Node *node, void *context); static bool pull_var_clause_walker(Node *node, pull_var_clause_context *context); +static Node *flatten_join_alias_vars_mutator(Node *node, + flatten_join_alias_vars_context *context); +static Node *flatten_join_alias_var(Var *var, Query *root, int expandRTI); +static Node *find_jointree_item(Node *jtnode, int rtindex); /* @@ -297,3 +310,363 @@ pull_var_clause_walker(Node *node, pull_var_clause_context *context) return expression_tree_walker(node, pull_var_clause_walker, (void *) context); } + + +/* + * flatten_join_alias_vars + * Whereever possible, replace Vars that reference JOIN outputs with + * references to the original relation variables instead. This allows + * quals involving such vars to be pushed down. Vars that cannot be + * simplified to non-join Vars are replaced by COALESCE expressions + * if they have varno = expandRTI, and are left as JOIN RTE references + * otherwise. (Pass expandRTI = 0 to prevent all COALESCE expansion.) + * + * Upper-level vars (with varlevelsup > 0) are ignored; normally there + * should not be any by the time this routine is called. + * + * Does not examine subqueries, therefore must only be used after reduction + * of sublinks to subplans! + */ +Node * +flatten_join_alias_vars(Node *node, Query *root, int expandRTI) +{ + flatten_join_alias_vars_context context; + + context.root = root; + context.expandRTI = expandRTI; + + return flatten_join_alias_vars_mutator(node, &context); +} + +static Node * +flatten_join_alias_vars_mutator(Node *node, + flatten_join_alias_vars_context *context) +{ + if (node == NULL) + return NULL; + if (IsA(node, Var)) + { + Var *var = (Var *) node; + + if (var->varlevelsup != 0) + return node; /* no need to copy, really */ + return flatten_join_alias_var(var, context->root, context->expandRTI); + } + return expression_tree_mutator(node, flatten_join_alias_vars_mutator, + (void *) context); +} + +static Node * +flatten_join_alias_var(Var *var, Query *root, int expandRTI) +{ + Index varno = var->varno; + AttrNumber varattno = var->varattno; + Oid vartype = var->vartype; + int32 vartypmod = var->vartypmod; + JoinExpr *jexpr = NULL; + + /* + * Loop to cope with joins of joins + */ + for (;;) + { + RangeTblEntry *rte = rt_fetch(varno, root->rtable); + Index leftrti, + rightrti; + AttrNumber leftattno, + rightattno; + RangeTblEntry *subrte; + Oid subtype; + int32 subtypmod; + + if (rte->rtekind != RTE_JOIN) + break; /* reached a non-join RTE */ + /* + * Find the RT indexes of the left and right children of the + * join node. We have to search the join tree to do this, + * which is a major pain in the neck --- but keeping RT indexes + * in other RT entries is worse, because it makes modifying + * querytrees difficult. (Perhaps we can improve on the + * rangetable/jointree datastructure someday.) One thing we + * can do is avoid repeated searches while tracing a single + * variable down to its baserel. + */ + if (jexpr == NULL) + jexpr = (JoinExpr *) + find_jointree_item((Node *) root->jointree, varno); + if (jexpr == NULL || + !IsA(jexpr, JoinExpr) || + jexpr->rtindex != varno) + elog(ERROR, "flatten_join_alias_var: failed to find JoinExpr"); + if (IsA(jexpr->larg, RangeTblRef)) + leftrti = ((RangeTblRef *) jexpr->larg)->rtindex; + else if (IsA(jexpr->larg, JoinExpr)) + leftrti = ((JoinExpr *) jexpr->larg)->rtindex; + else + { + elog(ERROR, "flatten_join_alias_var: unexpected subtree type"); + leftrti = 0; /* keep compiler quiet */ + } + if (IsA(jexpr->rarg, RangeTblRef)) + rightrti = ((RangeTblRef *) jexpr->rarg)->rtindex; + else if (IsA(jexpr->rarg, JoinExpr)) + rightrti = ((JoinExpr *) jexpr->rarg)->rtindex; + else + { + elog(ERROR, "flatten_join_alias_var: unexpected subtree type"); + rightrti = 0; /* keep compiler quiet */ + } + /* + * See if the join var is from the left side, the right side, + * or both (ie, it is a USING/NATURAL JOIN merger column). + */ + Assert(varattno > 0); + leftattno = (AttrNumber) nthi(varattno-1, rte->joinleftcols); + rightattno = (AttrNumber) nthi(varattno-1, rte->joinrightcols); + if (leftattno && rightattno) + { + /* + * Var is a merge var. If a left or right join, we can replace + * it by the left or right input var respectively; we only need + * a COALESCE for a full join. However, beware of the possibility + * that there's been a type promotion to make the input vars + * compatible; do not replace a var by one of a different type! + */ + if (rte->jointype == JOIN_INNER || + rte->jointype == JOIN_LEFT) + { + subrte = rt_fetch(leftrti, root->rtable); + get_rte_attribute_type(subrte, leftattno, + &subtype, &subtypmod); + if (vartype == subtype && vartypmod == subtypmod) + { + varno = leftrti; + varattno = leftattno; + jexpr = (JoinExpr *) jexpr->larg; + continue; + } + } + if (rte->jointype == JOIN_INNER || + rte->jointype == JOIN_RIGHT) + { + subrte = rt_fetch(rightrti, root->rtable); + get_rte_attribute_type(subrte, rightattno, + &subtype, &subtypmod); + if (vartype == subtype && vartypmod == subtypmod) + { + varno = rightrti; + varattno = rightattno; + jexpr = (JoinExpr *) jexpr->rarg; + continue; + } + } + /* + * This var cannot be substituted directly, only with a COALESCE. + * Do so only if it belongs to the particular join indicated by + * the caller. + */ + if (varno != expandRTI) + break; + { + Node *l_var, + *r_var; + CaseExpr *c = makeNode(CaseExpr); + CaseWhen *w = makeNode(CaseWhen); + NullTest *n = makeNode(NullTest); + + subrte = rt_fetch(leftrti, root->rtable); + get_rte_attribute_type(subrte, leftattno, + &subtype, &subtypmod); + l_var = (Node *) makeVar(leftrti, + leftattno, + subtype, + subtypmod, + 0); + if (subtype != vartype) + { + l_var = coerce_type(NULL, l_var, subtype, + vartype, vartypmod); + l_var = coerce_type_typmod(NULL, l_var, + vartype, vartypmod); + } + else if (subtypmod != vartypmod) + l_var = coerce_type_typmod(NULL, l_var, + vartype, vartypmod); + + subrte = rt_fetch(rightrti, root->rtable); + get_rte_attribute_type(subrte, rightattno, + &subtype, &subtypmod); + r_var = (Node *) makeVar(rightrti, + rightattno, + subtype, + subtypmod, + 0); + if (subtype != vartype) + { + r_var = coerce_type(NULL, r_var, subtype, + vartype, vartypmod); + r_var = coerce_type_typmod(NULL, r_var, + vartype, vartypmod); + } + else if (subtypmod != vartypmod) + r_var = coerce_type_typmod(NULL, r_var, + vartype, vartypmod); + + n->arg = l_var; + n->nulltesttype = IS_NOT_NULL; + w->expr = (Node *) n; + w->result = l_var; + c->casetype = vartype; + c->args = makeList1(w); + c->defresult = r_var; + return (Node *) c; + } + } + else if (leftattno) + { + /* Here we do not need to check the type */ + varno = leftrti; + varattno = leftattno; + jexpr = (JoinExpr *) jexpr->larg; + } + else + { + Assert(rightattno); + /* Here we do not need to check the type */ + varno = rightrti; + varattno = rightattno; + jexpr = (JoinExpr *) jexpr->rarg; + } + } + + /* + * When we fall out of the loop, we've reached the base Var. + */ + return (Node *) makeVar(varno, + varattno, + vartype, + vartypmod, + 0); +} + +/* + * Given a join alias Var, construct Vars for the two input vars it directly + * depends on. Note that this should *only* be called for merger alias Vars. + * In practice it is only used for Vars that got past flatten_join_alias_vars. + */ +void +build_join_alias_subvars(Query *root, Var *aliasvar, + Var **leftsubvar, Var **rightsubvar) +{ + Index varno = aliasvar->varno; + AttrNumber varattno = aliasvar->varattno; + RangeTblEntry *rte; + JoinExpr *jexpr; + Index leftrti, + rightrti; + AttrNumber leftattno, + rightattno; + RangeTblEntry *subrte; + Oid subtype; + int32 subtypmod; + + Assert(aliasvar->varlevelsup == 0); + rte = rt_fetch(varno, root->rtable); + Assert(rte->rtekind == RTE_JOIN); + + /* + * Find the RT indexes of the left and right children of the + * join node. + */ + jexpr = (JoinExpr *) find_jointree_item((Node *) root->jointree, varno); + if (jexpr == NULL || + !IsA(jexpr, JoinExpr) || + jexpr->rtindex != varno) + elog(ERROR, "build_join_alias_subvars: failed to find JoinExpr"); + if (IsA(jexpr->larg, RangeTblRef)) + leftrti = ((RangeTblRef *) jexpr->larg)->rtindex; + else if (IsA(jexpr->larg, JoinExpr)) + leftrti = ((JoinExpr *) jexpr->larg)->rtindex; + else + { + elog(ERROR, "build_join_alias_subvars: unexpected subtree type"); + leftrti = 0; /* keep compiler quiet */ + } + if (IsA(jexpr->rarg, RangeTblRef)) + rightrti = ((RangeTblRef *) jexpr->rarg)->rtindex; + else if (IsA(jexpr->rarg, JoinExpr)) + rightrti = ((JoinExpr *) jexpr->rarg)->rtindex; + else + { + elog(ERROR, "build_join_alias_subvars: unexpected subtree type"); + rightrti = 0; /* keep compiler quiet */ + } + + Assert(varattno > 0); + leftattno = (AttrNumber) nthi(varattno-1, rte->joinleftcols); + rightattno = (AttrNumber) nthi(varattno-1, rte->joinrightcols); + if (!(leftattno && rightattno)) + elog(ERROR, "build_join_alias_subvars: non-merger variable"); + + subrte = rt_fetch(leftrti, root->rtable); + get_rte_attribute_type(subrte, leftattno, + &subtype, &subtypmod); + *leftsubvar = makeVar(leftrti, + leftattno, + subtype, + subtypmod, + 0); + + subrte = rt_fetch(rightrti, root->rtable); + get_rte_attribute_type(subrte, rightattno, + &subtype, &subtypmod); + *rightsubvar = makeVar(rightrti, + rightattno, + subtype, + subtypmod, + 0); +} + +/* + * Find jointree item matching the specified RT index + */ +static Node * +find_jointree_item(Node *jtnode, int rtindex) +{ + if (jtnode == NULL) + return NULL; + if (IsA(jtnode, RangeTblRef)) + { + if (((RangeTblRef *) jtnode)->rtindex == rtindex) + return jtnode; + } + else if (IsA(jtnode, FromExpr)) + { + FromExpr *f = (FromExpr *) jtnode; + List *l; + + foreach(l, f->fromlist) + { + jtnode = find_jointree_item(lfirst(l), rtindex); + if (jtnode) + return jtnode; + } + } + else if (IsA(jtnode, JoinExpr)) + { + JoinExpr *j = (JoinExpr *) jtnode; + + if (j->rtindex == rtindex) + return jtnode; + jtnode = find_jointree_item(j->larg, rtindex); + if (jtnode) + return jtnode; + jtnode = find_jointree_item(j->rarg, rtindex); + if (jtnode) + return jtnode; + } + else + elog(ERROR, "find_jointree_item: unexpected node type %d", + nodeTag(jtnode)); + return NULL; +} |