prepjointree.c
来自「postgresql8.3.4源码,开源数据库」· C语言 代码 · 共 1,329 行 · 第 1/3 页
C
1,329 行
/*------------------------------------------------------------------------- * * prepjointree.c * Planner preprocessing for subqueries and join tree manipulation. * * NOTE: the intended sequence for invoking these operations is * pull_up_IN_clauses * pull_up_subqueries * do expression preprocessing (including flattening JOIN alias vars) * reduce_outer_joins * * * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION * $PostgreSQL: pgsql/src/backend/optimizer/prep/prepjointree.c,v 1.49.2.1 2008/08/14 20:31:59 heikki Exp $ * *------------------------------------------------------------------------- */#include "postgres.h"#include "nodes/makefuncs.h"#include "optimizer/clauses.h"#include "optimizer/prep.h"#include "optimizer/subselect.h"#include "optimizer/tlist.h"#include "optimizer/var.h"#include "parser/parse_expr.h"#include "parser/parsetree.h"#include "rewrite/rewriteManip.h"typedef struct reduce_outer_joins_state{ Relids relids; /* base relids within this subtree */ bool contains_outer; /* does subtree contain outer join(s)? */ List *sub_states; /* List of states for subtree components */} reduce_outer_joins_state;static Node *pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte, bool below_outer_join, bool append_rel_member);static Node *pull_up_simple_union_all(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte);static void pull_up_union_leaf_queries(Node *setOp, PlannerInfo *root, int parentRTindex, Query *setOpQuery, int childRToffset);static void make_setop_translation_lists(Query *query, Index newvarno, List **col_mappings, List **translated_vars);static bool is_simple_subquery(Query *subquery);static bool is_simple_union_all(Query *subquery);static bool is_simple_union_all_recurse(Node *setOp, Query *setOpQuery, List *colTypes);static bool has_nullable_targetlist(Query *subquery);static bool is_safe_append_member(Query *subquery);static void resolvenew_in_jointree(Node *jtnode, int varno, RangeTblEntry *rte, List *subtlist);static reduce_outer_joins_state *reduce_outer_joins_pass1(Node *jtnode);static void reduce_outer_joins_pass2(Node *jtnode, reduce_outer_joins_state *state, PlannerInfo *root, Relids nonnullable_rels);static void fix_in_clause_relids(List *in_info_list, int varno, Relids subrelids);static void fix_append_rel_relids(List *append_rel_list, int varno, Relids subrelids);static Node *find_jointree_node_for_rel(Node *jtnode, int relid);/* * pull_up_IN_clauses * Attempt to pull up top-level IN clauses to be treated like joins. * * A clause "foo IN (sub-SELECT)" appearing at the top level of WHERE can * be processed by pulling the sub-SELECT up to become a rangetable entry * and handling the implied equality comparisons as join operators (with * special join rules). * This optimization *only* works at the top level of WHERE, because * it cannot distinguish whether the IN ought to return FALSE or NULL in * cases involving NULL inputs. This routine searches for such clauses * and does the necessary parsetree transformations if any are found. * * This routine has to run before preprocess_expression(), so the WHERE * clause is not yet reduced to implicit-AND format. That means we need * to recursively search through explicit AND clauses, which are * probably only binary ANDs. We stop as soon as we hit a non-AND item. * * Returns the possibly-modified version of the given qual-tree node. */Node *pull_up_IN_clauses(PlannerInfo *root, Node *node){ if (node == NULL) return NULL; if (IsA(node, SubLink)) { SubLink *sublink = (SubLink *) node; Node *subst; /* Is it a convertible IN clause? If not, return it as-is */ subst = convert_IN_to_join(root, sublink); if (subst == NULL) return node; return subst; } if (and_clause(node)) { List *newclauses = NIL; ListCell *l; foreach(l, ((BoolExpr *) node)->args) { Node *oldclause = (Node *) lfirst(l); newclauses = lappend(newclauses, pull_up_IN_clauses(root, oldclause)); } return (Node *) make_andclause(newclauses); } /* Stop if not an AND */ return node;}/* * pull_up_subqueries * Look for subqueries in the rangetable that can be pulled up into * the parent query. If the subquery has no special features like * grouping/aggregation then we can merge it into the parent's jointree. * Also, subqueries that are simple UNION ALL structures can be * converted into "append relations". * * below_outer_join is true if this jointree node is within the nullable * side of an outer join. This restricts what we can do. * * append_rel_member is true if we are looking at a member subquery of * an append relation. This puts some different restrictions on 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 * we are currently processing! We handle this by being careful not to * change the jointree structure while recursing: no nodes other than * subquery RangeTblRef entries will be replaced. Also, we can't turn * ResolveNew loose on the whole jointree, because it'll return a mutated * copy of the tree; we have to invoke it just on the quals, instead. */Node *pull_up_subqueries(PlannerInfo *root, Node *jtnode, bool below_outer_join, bool append_rel_member){ if (jtnode == NULL) return NULL; if (IsA(jtnode, RangeTblRef)) { int varno = ((RangeTblRef *) jtnode)->rtindex; RangeTblEntry *rte = rt_fetch(varno, root->parse->rtable); /* * 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. * * If we are looking at an append-relation member, we can't pull it up * unless is_safe_append_member says so. */ if (rte->rtekind == RTE_SUBQUERY && is_simple_subquery(rte->subquery) && (!below_outer_join || has_nullable_targetlist(rte->subquery)) && (!append_rel_member || is_safe_append_member(rte->subquery))) return pull_up_simple_subquery(root, jtnode, rte, below_outer_join, append_rel_member); /* * Alternatively, is it a simple UNION ALL subquery? If so, flatten * into an "append relation". We can do this regardless of * nullability considerations since this transformation does not * result in propagating non-Var expressions into upper levels of the * query. * * It's also safe to do this regardless of whether this query is * itself an appendrel member. (If you're thinking we should try to * flatten the two levels of appendrel together, you're right; but we * handle that in set_append_rel_pathlist, not here.) */ if (rte->rtekind == RTE_SUBQUERY && is_simple_union_all(rte->subquery)) return pull_up_simple_union_all(root, jtnode, rte); } else if (IsA(jtnode, FromExpr)) { FromExpr *f = (FromExpr *) jtnode; ListCell *l; Assert(!append_rel_member); foreach(l, f->fromlist) lfirst(l) = pull_up_subqueries(root, lfirst(l), below_outer_join, false); } else if (IsA(jtnode, JoinExpr)) { JoinExpr *j = (JoinExpr *) jtnode; Assert(!append_rel_member); /* Recurse, being careful to tell myself when inside outer join */ switch (j->jointype) { case JOIN_INNER: j->larg = pull_up_subqueries(root, j->larg, below_outer_join, false); j->rarg = pull_up_subqueries(root, j->rarg, below_outer_join, false); break; case JOIN_LEFT: j->larg = pull_up_subqueries(root, j->larg, below_outer_join, false); j->rarg = pull_up_subqueries(root, j->rarg, true, false); break; case JOIN_FULL: j->larg = pull_up_subqueries(root, j->larg, true, false); j->rarg = pull_up_subqueries(root, j->rarg, true, false); break; case JOIN_RIGHT: j->larg = pull_up_subqueries(root, j->larg, true, false); j->rarg = pull_up_subqueries(root, j->rarg, below_outer_join, false); break; default: elog(ERROR, "unrecognized join type: %d", (int) j->jointype); break; } } else elog(ERROR, "unrecognized node type: %d", (int) nodeTag(jtnode)); return jtnode;}/* * pull_up_simple_subquery * Attempt to pull up a single simple subquery. * * jtnode is a RangeTblRef that has been tentatively identified as a simple * subquery by pull_up_subqueries. We return the replacement jointree node, * or jtnode itself if we determine that the subquery can't be pulled up after * all. */static Node *pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte, bool below_outer_join, bool append_rel_member){ Query *parse = root->parse; int varno = ((RangeTblRef *) jtnode)->rtindex; Query *subquery; PlannerInfo *subroot; int rtoffset; List *subtlist; ListCell *rt; /* * Need a modifiable copy of the subquery to hack on. Even if we didn't * sometimes choose not to pull up below, we must do this to avoid * problems if the same subquery is referenced from multiple jointree * items (which can't happen normally, but might after rule rewriting). */ subquery = copyObject(rte->subquery); /* * Create a PlannerInfo data structure for this subquery. * * NOTE: the next few steps should match the first processing in * subquery_planner(). Can we refactor to avoid code duplication, or * would that just make things uglier? */ subroot = makeNode(PlannerInfo); subroot->parse = subquery; subroot->glob = root->glob; subroot->query_level = root->query_level; subroot->planner_cxt = CurrentMemoryContext; subroot->init_plans = NIL; subroot->in_info_list = NIL; subroot->append_rel_list = NIL; /* * Pull up any IN clauses within the subquery's WHERE, so that we don't * leave unoptimized INs behind. */ if (subquery->hasSubLinks) subquery->jointree->quals = pull_up_IN_clauses(subroot, subquery->jointree->quals); /* * Recursively pull up the subquery's subqueries, so that * pull_up_subqueries' processing is complete for its jointree and * rangetable. * * Note: below_outer_join = false is correct here even if we are within an * outer join in the upper query; the lower query starts with a clean * slate for outer-join semantics. Likewise, we say we aren't handling an * appendrel member. */ subquery->jointree = (FromExpr *) pull_up_subqueries(subroot, (Node *) subquery->jointree, false, false); /* * Now we must recheck whether the subquery is still simple enough to pull * up. If not, abandon processing it. * * We don't really need to recheck all the conditions involved, but it's * easier just to keep this "if" looking the same as the one in * pull_up_subqueries. */ if (is_simple_subquery(subquery) && (!below_outer_join || has_nullable_targetlist(subquery)) && (!append_rel_member || is_safe_append_member(subquery))) { /* good to go */ } else { /* * Give up, return unmodified RangeTblRef. * * Note: The work we just did will be redone when the subquery gets * planned on its own. Perhaps we could avoid that by storing the * modified subquery back into the rangetable, but I'm not gonna risk * it now. */ return jtnode; } /* * Adjust level-0 varnos in subquery so that we can append its rangetable * to upper query's. We have to fix the subquery's in_info_list and * append_rel_list, as well. */ rtoffset = list_length(parse->rtable); OffsetVarNodes((Node *) subquery, rtoffset, 0); OffsetVarNodes((Node *) subroot->in_info_list, rtoffset, 0); OffsetVarNodes((Node *) subroot->append_rel_list, rtoffset, 0); /* * Upper-level vars in subquery are now one level closer to their parent * than before. */ IncrementVarSublevelsUp((Node *) subquery, -1, 1); IncrementVarSublevelsUp((Node *) subroot->in_info_list, -1, 1); IncrementVarSublevelsUp((Node *) subroot->append_rel_list, -1, 1); /* * 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, rte, subtlist, CMD_SELECT, 0); parse->returningList = (List *) ResolveNew((Node *) parse->returningList, varno, 0, rte, subtlist, CMD_SELECT, 0); resolvenew_in_jointree((Node *) parse->jointree, varno, rte, subtlist); Assert(parse->setOperations == NULL); parse->havingQual = ResolveNew(parse->havingQual, varno, 0, rte, subtlist, CMD_SELECT, 0); root->in_info_list = (List *) ResolveNew((Node *) root->in_info_list, varno, 0, rte, subtlist, CMD_SELECT, 0); root->append_rel_list = (List *) ResolveNew((Node *) root->append_rel_list, varno, 0, rte, subtlist, CMD_SELECT, 0); foreach(rt, parse->rtable) { RangeTblEntry *otherrte = (RangeTblEntry *) lfirst(rt); if (otherrte->rtekind == RTE_JOIN) otherrte->joinaliasvars = (List *) ResolveNew((Node *) otherrte->joinaliasvars, varno, 0, rte, 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 = list_concat(parse->rtable, subquery->rtable); /* * Pull up any FOR UPDATE/SHARE markers, too. (OffsetVarNodes already * adjusted the marker rtindexes, so just concat the lists.) */ parse->rowMarks = list_concat(parse->rowMarks, subquery->rowMarks); /* * We also have to fix the relid sets of any parent InClauseInfo nodes. * (This could perhaps be done by ResolveNew, but it would clutter that * routine's API unreasonably.) * * Likewise, relids appearing in AppendRelInfo nodes have to be fixed (but * we took care of their translated_vars lists above). We already checked * that this won't require introducing multiple subrelids into the * single-slot AppendRelInfo structs. */ if (root->in_info_list || root->append_rel_list) { Relids subrelids; subrelids = get_relids_in_jointree((Node *) subquery->jointree); fix_in_clause_relids(root->in_info_list, varno, subrelids); fix_append_rel_relids(root->append_rel_list, varno, subrelids); } /*
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?