diff options
Diffstat (limited to 'src/backend/optimizer')
| -rw-r--r-- | src/backend/optimizer/plan/createplan.c | 68 | ||||
| -rw-r--r-- | src/backend/optimizer/plan/initsplan.c | 20 | ||||
| -rw-r--r-- | src/backend/optimizer/plan/planner.c | 150 | ||||
| -rw-r--r-- | src/backend/optimizer/plan/setrefs.c | 23 | ||||
| -rw-r--r-- | src/backend/optimizer/util/clauses.c | 66 | ||||
| -rw-r--r-- | src/backend/optimizer/util/var.c | 360 |
6 files changed, 217 insertions, 470 deletions
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index 79d90bf5a3..c20abacca3 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -10,7 +10,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.112 2002/03/12 00:51:45 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.113 2002/04/28 19:54:28 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -80,18 +80,18 @@ static TidScan *make_tidscan(List *qptlist, List *qpqual, Index scanrelid, static NestLoop *make_nestloop(List *tlist, List *joinclauses, List *otherclauses, Plan *lefttree, Plan *righttree, - JoinType jointype, Index joinrti); + JoinType jointype); static HashJoin *make_hashjoin(List *tlist, List *joinclauses, List *otherclauses, List *hashclauses, Plan *lefttree, Plan *righttree, - JoinType jointype, Index joinrti); + JoinType jointype); static Hash *make_hash(List *tlist, Node *hashkey, Plan *lefttree); static MergeJoin *make_mergejoin(List *tlist, List *joinclauses, List *otherclauses, List *mergeclauses, Plan *lefttree, Plan *righttree, - JoinType jointype, Index joinrti); + JoinType jointype); /* * create_plan @@ -591,7 +591,6 @@ create_nestloop_plan(Query *root, List *inner_tlist) { NestLoop *join_plan; - Index joinrti = best_path->path.parent->joinrti; if (IsA(inner_plan, IndexScan)) { @@ -639,22 +638,19 @@ create_nestloop_plan(Query *root, root, outer_tlist, NIL, - innerrel, - joinrti); + innerrel); innerscan->indxqual = join_references(innerscan->indxqual, root, outer_tlist, NIL, - innerrel, - joinrti); + innerrel); /* fix the inner qpqual too, if it has join clauses */ if (NumRelids((Node *) inner_plan->qual) > 1) inner_plan->qual = join_references(inner_plan->qual, root, outer_tlist, NIL, - innerrel, - joinrti); + innerrel); } } else if (IsA(inner_plan, TidScan)) @@ -665,8 +661,7 @@ create_nestloop_plan(Query *root, root, outer_tlist, inner_tlist, - innerscan->scan.scanrelid, - joinrti); + innerscan->scan.scanrelid); } else if (IsA_Join(inner_plan)) { @@ -688,22 +683,19 @@ create_nestloop_plan(Query *root, root, outer_tlist, inner_tlist, - (Index) 0, - joinrti); + (Index) 0); otherclauses = join_references(otherclauses, root, outer_tlist, inner_tlist, - (Index) 0, - joinrti); + (Index) 0); join_plan = make_nestloop(tlist, joinclauses, otherclauses, outer_plan, inner_plan, - best_path->jointype, - joinrti); + best_path->jointype); copy_path_costsize(&join_plan->join.plan, &best_path->path); @@ -723,7 +715,6 @@ create_mergejoin_plan(Query *root, { List *mergeclauses; MergeJoin *join_plan; - Index joinrti = best_path->jpath.path.parent->joinrti; mergeclauses = get_actual_clauses(best_path->path_mergeclauses); @@ -736,8 +727,7 @@ create_mergejoin_plan(Query *root, root, outer_tlist, inner_tlist, - (Index) 0, - joinrti); + (Index) 0); /* * Fix the additional qpquals too. @@ -746,8 +736,7 @@ create_mergejoin_plan(Query *root, root, outer_tlist, inner_tlist, - (Index) 0, - joinrti); + (Index) 0); /* * Now set the references in the mergeclauses and rearrange them so @@ -757,8 +746,7 @@ create_mergejoin_plan(Query *root, root, outer_tlist, inner_tlist, - (Index) 0, - joinrti)); + (Index) 0)); /* * Create explicit sort nodes for the outer and inner join paths if @@ -824,8 +812,7 @@ create_mergejoin_plan(Query *root, mergeclauses, outer_plan, inner_plan, - best_path->jpath.jointype, - joinrti); + best_path->jpath.jointype); copy_path_costsize(&join_plan->join.plan, &best_path->jpath.path); @@ -847,7 +834,6 @@ create_hashjoin_plan(Query *root, HashJoin *join_plan; Hash *hash_plan; Node *innerhashkey; - Index joinrti = best_path->jpath.path.parent->joinrti; /* * NOTE: there will always be exactly one hashclause in the list @@ -866,8 +852,7 @@ create_hashjoin_plan(Query *root, root, outer_tlist, inner_tlist, - (Index) 0, - joinrti); + (Index) 0); /* * Fix the additional qpquals too. @@ -876,8 +861,7 @@ create_hashjoin_plan(Query *root, root, outer_tlist, inner_tlist, - (Index) 0, - joinrti); + (Index) 0); /* * Now set the references in the hashclauses and rearrange them so @@ -887,8 +871,7 @@ create_hashjoin_plan(Query *root, root, outer_tlist, inner_tlist, - (Index) 0, - joinrti)); + (Index) 0)); /* Now the righthand op of the sole hashclause is the inner hash key. */ innerhashkey = (Node *) get_rightop(lfirst(hashclauses)); @@ -903,8 +886,7 @@ create_hashjoin_plan(Query *root, hashclauses, outer_plan, (Plan *) hash_plan, - best_path->jpath.jointype, - joinrti); + best_path->jpath.jointype); copy_path_costsize(&join_plan->join.plan, &best_path->jpath.path); @@ -1363,8 +1345,7 @@ make_nestloop(List *tlist, List *otherclauses, Plan *lefttree, Plan *righttree, - JoinType jointype, - Index joinrti) + JoinType jointype) { NestLoop *node = makeNode(NestLoop); Plan *plan = &node->join.plan; @@ -1377,7 +1358,6 @@ make_nestloop(List *tlist, plan->righttree = righttree; node->join.jointype = jointype; node->join.joinqual = joinclauses; - node->join.joinrti = joinrti; return node; } @@ -1389,8 +1369,7 @@ make_hashjoin(List *tlist, List *hashclauses, Plan *lefttree, Plan *righttree, - JoinType jointype, - Index joinrti) + JoinType jointype) { HashJoin *node = makeNode(HashJoin); Plan *plan = &node->join.plan; @@ -1404,7 +1383,6 @@ make_hashjoin(List *tlist, node->hashclauses = hashclauses; node->join.jointype = jointype; node->join.joinqual = joinclauses; - node->join.joinrti = joinrti; return node; } @@ -1439,8 +1417,7 @@ make_mergejoin(List *tlist, List *mergeclauses, Plan *lefttree, Plan *righttree, - JoinType jointype, - Index joinrti) + JoinType jointype) { MergeJoin *node = makeNode(MergeJoin); Plan *plan = &node->join.plan; @@ -1454,7 +1431,6 @@ make_mergejoin(List *tlist, node->mergeclauses = mergeclauses; node->join.jointype = jointype; node->join.joinqual = joinclauses; - node->join.joinrti = joinrti; return node; } diff --git a/src/backend/optimizer/plan/initsplan.c b/src/backend/optimizer/plan/initsplan.c index 39ac5ba886..2410651d68 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.68 2002/04/16 23:08:10 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v 1.69 2002/04/28 19:54:28 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -173,16 +173,14 @@ add_vars_to_targetlist(Query *root, List *vars) if (rel->reloptkind == RELOPT_OTHER_JOIN_REL) { /* Var is an alias */ - Var *leftsubvar, - *rightsubvar; - - build_join_alias_subvars(root, var, - &leftsubvar, &rightsubvar); - - rel = find_base_rel(root, leftsubvar->varno); - add_var_to_tlist(rel, leftsubvar); - rel = find_base_rel(root, rightsubvar->varno); - add_var_to_tlist(rel, rightsubvar); + Node *expansion; + List *varsused; + + expansion = flatten_join_alias_vars((Node *) var, + root, true); + varsused = pull_var_clause(expansion, false); + add_vars_to_targetlist(root, varsused); + freeList(varsused); } } } diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index 1df2cd2940..5eb17f3b05 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.115 2002/03/12 00:51:47 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.116 2002/04/28 19:54:28 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -41,8 +41,10 @@ #define EXPRKIND_HAVING 2 -static Node *pull_up_subqueries(Query *parse, Node *jtnode); +static Node *pull_up_subqueries(Query *parse, Node *jtnode, + bool below_outer_join); static bool is_simple_subquery(Query *subquery); +static bool has_nullable_targetlist(Query *subquery); static void resolvenew_in_jointree(Node *jtnode, int varno, List *subtlist); static Node *preprocess_jointree(Query *parse, Node *jtnode); static Node *preprocess_expression(Query *parse, Node *expr, int kind); @@ -153,7 +155,7 @@ subquery_planner(Query *parse, double tuple_fraction) * this query. */ parse->jointree = (FromExpr *) - pull_up_subqueries(parse, (Node *) parse->jointree); + pull_up_subqueries(parse, (Node *) parse->jointree, false); /* * If so, we may have created opportunities to simplify the jointree. @@ -264,6 +266,9 @@ subquery_planner(Query *parse, double tuple_fraction) * the parent query. If the subquery has no special features like * grouping/aggregation then we can merge it into the parent's jointree. * + * below_outer_join is true if this jointree node is within the nullable + * side of an outer join. This restricts what we can do. + * * A tricky aspect of this code is that if we pull up a subquery we have * to replace Vars that reference the subquery's outputs throughout the * parent query, including quals attached to jointree nodes above the one @@ -274,7 +279,7 @@ subquery_planner(Query *parse, double tuple_fraction) * copy of the tree; we have to invoke it just on the quals, instead. */ static Node * -pull_up_subqueries(Query *parse, Node *jtnode) +pull_up_subqueries(Query *parse, Node *jtnode, bool below_outer_join) { if (jtnode == NULL) return NULL; @@ -288,16 +293,26 @@ pull_up_subqueries(Query *parse, Node *jtnode) * Is this a subquery RTE, and if so, is the subquery simple * enough to pull up? (If not, do nothing at this node.) * + * If we are inside an outer join, only pull up subqueries whose + * targetlists are nullable --- otherwise substituting their tlist + * entries for upper Var references would do the wrong thing + * (the results wouldn't become NULL when they're supposed to). + * XXX This could be improved by generating pseudo-variables for + * such expressions; we'd have to figure out how to get the pseudo- + * variables evaluated at the right place in the modified plan tree. + * Fix it someday. + * * Note: even if the subquery itself is simple enough, we can't pull - * it up if there is a reference to its whole tuple result. + * it up if there is a reference to its whole tuple result. Perhaps + * a pseudo-variable is the answer here too. */ - if (subquery && is_simple_subquery(subquery) && + if (rte->rtekind == RTE_SUBQUERY && is_simple_subquery(subquery) && + (!below_outer_join || has_nullable_targetlist(subquery)) && !contain_whole_tuple_var((Node *) parse, varno, 0)) { int rtoffset; - Node *subjointree; List *subtlist; - List *l; + List *rt; /* * First, recursively pull up the subquery's subqueries, so @@ -311,50 +326,62 @@ pull_up_subqueries(Query *parse, Node *jtnode) * having chunks of structure multiply linked. */ subquery->jointree = (FromExpr *) - pull_up_subqueries(subquery, (Node *) subquery->jointree); + pull_up_subqueries(subquery, (Node *) subquery->jointree, + below_outer_join); /* - * Append the subquery's rangetable to mine (currently, no - * adjustments will be needed in the subquery's rtable). + * Now make a modifiable copy of the subquery that we can + * run OffsetVarNodes on. */ - rtoffset = length(parse->rtable); - parse->rtable = nconc(parse->rtable, - copyObject(subquery->rtable)); + subquery = copyObject(subquery); /* - * Make copies of the subquery's jointree and targetlist with - * varnos adjusted to match the merged rangetable. + * Adjust varnos in subquery so that we can append its + * rangetable to upper query's. */ - subjointree = copyObject(subquery->jointree); - OffsetVarNodes(subjointree, rtoffset, 0); - subtlist = copyObject(subquery->targetList); - OffsetVarNodes((Node *) subtlist, rtoffset, 0); + rtoffset = length(parse->rtable); + OffsetVarNodes((Node *) subquery, rtoffset, 0); /* * Replace all of the top query's references to the subquery's * outputs with copies of the adjusted subtlist items, being * careful not to replace any of the jointree structure. + * (This'd be a lot cleaner if we could use query_tree_mutator.) */ + subtlist = subquery->targetList; parse->targetList = (List *) ResolveNew((Node *) parse->targetList, varno, 0, subtlist, CMD_SELECT, 0); resolvenew_in_jointree((Node *) parse->jointree, varno, subtlist); + Assert(parse->setOperations == NULL); parse->havingQual = ResolveNew(parse->havingQual, varno, 0, subtlist, CMD_SELECT, 0); - /* - * Pull up any FOR UPDATE markers, too. - */ - foreach(l, subquery->rowMarks) + foreach(rt, parse->rtable) { - int submark = lfirsti(l); + RangeTblEntry *rte = (RangeTblEntry *) lfirst(rt); - parse->rowMarks = lappendi(parse->rowMarks, - submark + rtoffset); + if (rte->rtekind == RTE_JOIN) + rte->joinaliasvars = (List *) + ResolveNew((Node *) rte->joinaliasvars, + varno, 0, subtlist, CMD_SELECT, 0); } /* + * Now append the adjusted rtable entries to upper query. + * (We hold off until after fixing the upper rtable entries; + * no point in running that code on the subquery ones too.) + */ + parse->rtable = nconc(parse->rtable, subquery->rtable); + + /* + * Pull up any FOR UPDATE markers, too. (OffsetVarNodes + * already adjusted the marker values, so just nconc the list.) + */ + parse->rowMarks = nconc(parse->rowMarks, subquery->rowMarks); + + /* * Miscellaneous housekeeping. */ parse->hasSubLinks |= subquery->hasSubLinks; @@ -364,7 +391,7 @@ pull_up_subqueries(Query *parse, Node *jtnode) * Return the adjusted subquery jointree to replace the * RangeTblRef entry in my jointree. */ - return subjointree; + return (Node *) subquery->jointree; } } else if (IsA(jtnode, FromExpr)) @@ -373,35 +400,39 @@ pull_up_subqueries(Query *parse, Node *jtnode) List *l; foreach(l, f->fromlist) - lfirst(l) = pull_up_subqueries(parse, lfirst(l)); + lfirst(l) = pull_up_subqueries(parse, lfirst(l), + below_outer_join); } else if (IsA(jtnode, JoinExpr)) { JoinExpr *j = (JoinExpr *) jtnode; - /* - * At the moment, we can't pull up subqueries that are inside the - * nullable side of an outer join, because substituting their - * target list entries for upper Var references wouldn't do the - * right thing (the entries wouldn't go to NULL when they're - * supposed to). Suppressing the pullup is an ugly, - * performance-losing hack, but I see no alternative for now. Find - * a better way to handle this when we redesign query trees --- - * tgl 4/30/01. - */ + /* Recurse, being careful to tell myself when inside outer join */ switch (j->jointype) { case JOIN_INNER: - j->larg = pull_up_subqueries(parse, j->larg); - j->rarg = pull_up_subqueries(parse, j->rarg); + j->larg = pull_up_subqueries(parse, j->larg, + below_outer_join); + j->rarg = pull_up_subqueries(parse, j->rarg, + below_outer_join); break; case JOIN_LEFT: - j->larg = pull_up_subqueries(parse, j->larg); + j->larg = pull_up_subqueries(parse, j->larg, + below_outer_join); + j->rarg = pull_up_subqueries(parse, j->rarg, + true); break; case JOIN_FULL: + j->larg = pull_up_subqueries(parse, j->larg, + true); + j->rarg = pull_up_subqueries(parse, j->rarg, + true); break; case JOIN_RIGHT: - j->rarg = pull_up_subqueries(parse, j->rarg); + j->larg = pull_up_subqueries(parse, j->larg, + true); + j->rarg = pull_up_subqueries(parse, j->rarg, + below_outer_join); break; case JOIN_UNION: @@ -485,6 +516,37 @@ is_simple_subquery(Query *subquery) } /* + * has_nullable_targetlist + * Check a subquery in the range table to see if all the non-junk + * targetlist items are simple variables (and, hence, will correctly + * go to NULL when examined above the point of an outer join). + * + * A possible future extension is to accept strict functions of simple + * variables, eg, "x + 1". + */ +static bool +has_nullable_targetlist(Query *subquery) +{ + List *l; + + foreach(l, subquery->targetList) + { + TargetEntry *tle = (TargetEntry *) lfirst(l); + + /* ignore resjunk columns */ + if (tle->resdom->resjunk) + continue; + + /* Okay if tlist item is a simple Var */ + if (tle->expr && IsA(tle->expr, Var)) + continue; + + return false; + } + return true; +} + +/* * Helper routine for pull_up_subqueries: do ResolveNew on every expression * in the jointree, without changing the jointree structure itself. Ugly, * but there's no other way... @@ -675,7 +737,7 @@ preprocess_expression(Query *parse, Node *expr, int kind) } } if (has_join_rtes) - expr = flatten_join_alias_vars(expr, parse, 0); + expr = flatten_join_alias_vars(expr, parse, false); return expr; } diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c index 2f48821ece..807876e4a7 100644 --- a/src/backend/optimizer/plan/setrefs.c +++ b/src/backend/optimizer/plan/setrefs.c @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/setrefs.c,v 1.74 2002/03/12 00:51:48 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/setrefs.c,v 1.75 2002/04/28 19:54:28 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -31,7 +31,6 @@ typedef struct List *outer_tlist; List *inner_tlist; Index acceptable_rel; - Index join_rti; } join_references_context; typedef struct @@ -271,8 +270,7 @@ set_join_references(Query *root, Join *join) root, outer_tlist, inner_tlist, - (Index) 0, - join->joinrti); + (Index) 0); } /* @@ -367,8 +365,6 @@ set_uppernode_references(Plan *plan, Index subvarno) * 'inner_tlist' is the target list of the inner join relation, or NIL * 'acceptable_rel' is either zero or the rangetable index of a relation * whose Vars may appear in the clause without provoking an error. - * 'join_rti' is either zero or the join RTE index of join alias variables - * that should be expanded. * * Returns the new expression tree. The original clause structure is * not modified. @@ -378,8 +374,7 @@ join_references(List *clauses, Query *root, List *outer_tlist, List *inner_tlist, - Index acceptable_rel, - Index join_rti) + Index acceptable_rel) { join_references_context context; @@ -387,7 +382,6 @@ join_references(List *clauses, context.outer_tlist = outer_tlist; context.inner_tlist = inner_tlist; context.acceptable_rel = acceptable_rel; - context.join_rti = join_rti; return (List *) join_references_mutator((Node *) clauses, &context); } @@ -401,6 +395,7 @@ join_references_mutator(Node *node, { Var *var = (Var *) node; Resdom *resdom; + Node *newnode; /* First look for the var in the input tlists */ resdom = tlist_member((Node *) var, context->outer_tlist); @@ -423,13 +418,11 @@ join_references_mutator(Node *node, } /* Perhaps it's a join alias that can be resolved to input vars? */ - if (var->varno == context->join_rti) + newnode = flatten_join_alias_vars((Node *) var, + context->root, + true); + if (!equal(newnode, (Node *) var)) { - Node *newnode; - - newnode = flatten_join_alias_vars((Node *) var, - context->root, - context->join_rti); /* Must now resolve the input vars... */ newnode = join_references_mutator(newnode, context); return newnode; diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c index d7f100cb35..1b7cc14e2e 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.96 2002/04/05 00:31:27 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.97 2002/04/28 19:54:28 tgl Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -1902,6 +1902,8 @@ query_tree_walker(Query *query, void *context, bool visitQueryRTEs) { + List *rt; + Assert(query != NULL && IsA(query, Query)); if (walker((Node *) query->targetList, context)) @@ -1912,17 +1914,25 @@ query_tree_walker(Query *query, return true; if (walker(query->havingQual, context)) return true; - if (visitQueryRTEs) + foreach(rt, query->rtable) { - List *rt; + RangeTblEntry *rte = (RangeTblEntry *) lfirst(rt); - foreach(rt, query->rtable) + switch (rte->rtekind) { - RangeTblEntry *rte = (RangeTblEntry *) lfirst(rt); - - if (rte->subquery) - if (walker(rte->subquery, context)) + case RTE_RELATION: + case RTE_SPECIAL: + /* nothing to do */ + break; + case RTE_SUBQUERY: + if (visitQueryRTEs) + if (walker(rte->subquery, context)) + return true; + break; + case RTE_JOIN: + if (walker(rte->joinaliasvars, context)) return true; + break; } } return false; @@ -2281,32 +2291,42 @@ query_tree_mutator(Query *query, void *context, bool visitQueryRTEs) { + List *newrt = NIL; + List *rt; + Assert(query != NULL && IsA(query, Query)); MUTATE(query->targetList, query->targetList, List *); MUTATE(query->jointree, query->jointree, FromExpr *); MUTATE(query->setOperations, query->setOperations, Node *); MUTATE(query->havingQual, query->havingQual, Node *); - if (visitQueryRTEs) + foreach(rt, query->rtable) { - List *newrt = NIL; - List *rt; + RangeTblEntry *rte = (RangeTblEntry *) lfirst(rt); + RangeTblEntry *newrte; - foreach(rt, query->rtable) + switch (rte->rtekind) { - RangeTblEntry *rte = (RangeTblEntry *) lfirst(rt); - - if (rte->subquery) - { - RangeTblEntry *newrte; - + case RTE_RELATION: + case RTE_SPECIAL: + /* nothing to do, don't bother to make a copy */ + break; + case RTE_SUBQUERY: + if (visitQueryRTEs) + { + FLATCOPY(newrte, rte, RangeTblEntry); + CHECKFLATCOPY(newrte->subquery, rte->subquery, Query); + MUTATE(newrte->subquery, newrte->subquery, Query *); + rte = newrte; + } + break; + case RTE_JOIN: FLATCOPY(newrte, rte, RangeTblEntry); - CHECKFLATCOPY(newrte->subquery, rte->subquery, Query); - MUTATE(newrte->subquery, newrte->subquery, Query *); + MUTATE(newrte->joinaliasvars, rte->joinaliasvars, List *); rte = newrte; - } - newrt = lappend(newrt, rte); + break; } - query->rtable = newrt; + newrt = lappend(newrt, rte); } + query->rtable = newrt; } diff --git a/src/backend/optimizer/util/var.c b/src/backend/optimizer/util/var.c index 81aad81c0f..ee861c3557 100644 --- a/src/backend/optimizer/util/var.c +++ b/src/backend/optimizer/util/var.c @@ -8,18 +8,16 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/util/var.c,v 1.35 2002/04/11 20:00:00 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/util/var.c,v 1.36 2002/04/28 19:54:28 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 @@ -44,7 +42,7 @@ typedef struct typedef struct { Query *root; - int expandRTI; + bool force; } flatten_join_alias_vars_context; static bool pull_varnos_walker(Node *node, @@ -56,8 +54,6 @@ 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); /* @@ -314,26 +310,30 @@ pull_var_clause_walker(Node *node, pull_var_clause_context *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.) + * Replace Vars that reference JOIN outputs with references to the original + * relation variables instead. This allows quals involving such vars to be + * pushed down. * - * Upper-level vars (with varlevelsup > 0) are ignored; normally there - * should not be any by the time this routine is called. + * If force is TRUE then we will reduce all JOIN alias Vars to non-alias Vars + * or expressions thereof (there may be COALESCE and/or type conversions + * involved). If force is FALSE we will not expand a Var to a non-Var + * expression. This is a hack to avoid confusing mergejoin planning, which + * currently cannot cope with non-Var join items --- we leave the join vars + * as Vars till after planning is done, then expand them during setrefs.c. + * + * 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(Node *node, Query *root, bool force) { flatten_join_alias_vars_context context; context.root = root; - context.expandRTI = expandRTI; + context.force = force; return flatten_join_alias_vars_mutator(node, &context); } @@ -347,326 +347,24 @@ flatten_join_alias_vars_mutator(Node *node, if (IsA(node, Var)) { Var *var = (Var *) node; + RangeTblEntry *rte; + Node *newvar; 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; - + rte = rt_fetch(var->varno, context->root->rtable); 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, false); - 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, false); - 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) + return node; + Assert(var->varattno > 0); + newvar = (Node *) nth(var->varattno - 1, rte->joinaliasvars); + if (IsA(newvar, Var) || context->force) { - jtnode = find_jointree_item(lfirst(l), rtindex); - if (jtnode) - return jtnode; + /* expand it; recurse in case join input is itself a join */ + return flatten_join_alias_vars_mutator(newvar, context); } + /* we don't want to force expansion of this alias Var */ + return node; } - 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; + return expression_tree_mutator(node, flatten_join_alias_vars_mutator, + (void *) context); } |
