diff options
Diffstat (limited to 'src/backend/optimizer/plan')
| -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 |
4 files changed, 145 insertions, 116 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; |
