prepjointree.c
来自「postgresql8.3.4源码,开源数据库」· C语言 代码 · 共 1,329 行 · 第 1/3 页
C
1,329 行
* And now add any subquery InClauseInfos and AppendRelInfos to our lists. */ root->in_info_list = list_concat(root->in_info_list, subroot->in_info_list); root->append_rel_list = list_concat(root->append_rel_list, subroot->append_rel_list); /* * We don't have to do the equivalent bookkeeping for outer-join info, * because that hasn't been set up yet. */ Assert(root->oj_info_list == NIL); Assert(subroot->oj_info_list == NIL); /* * Miscellaneous housekeeping. */ parse->hasSubLinks |= subquery->hasSubLinks; /* subquery won't be pulled up if it hasAggs, so no work there */ /* * Return the adjusted subquery jointree to replace the RangeTblRef entry * in parent's jointree. */ return (Node *) subquery->jointree;}/* * pull_up_simple_union_all * Pull up a single simple UNION ALL subquery. * * jtnode is a RangeTblRef that has been identified as a simple UNION ALL * subquery by pull_up_subqueries. We pull up the leaf subqueries and * build an "append relation" for the union set. The result value is just * jtnode, since we don't actually need to change the query jointree. */static Node *pull_up_simple_union_all(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte){ int varno = ((RangeTblRef *) jtnode)->rtindex; Query *subquery = rte->subquery; int rtoffset; List *rtable; /* * Append the subquery rtable entries to upper query. */ rtoffset = list_length(root->parse->rtable); /* * Append child RTEs to parent rtable. * * Upper-level vars in subquery are now one level closer to their * parent than before. We don't have to worry about offsetting * varnos, though, because any such vars must refer to stuff above the * level of the query we are pulling into. */ rtable = copyObject(subquery->rtable); IncrementVarSublevelsUp_rtable(rtable, -1, 1); root->parse->rtable = list_concat(root->parse->rtable, rtable); /* * Recursively scan the subquery's setOperations tree and add * AppendRelInfo nodes for leaf subqueries to the parent's * append_rel_list. */ Assert(subquery->setOperations); pull_up_union_leaf_queries(subquery->setOperations, root, varno, subquery, rtoffset); /* * Mark the parent as an append relation. */ rte->inh = true; return jtnode;}/* * pull_up_union_leaf_queries -- recursive guts of pull_up_simple_union_all * * Note that setOpQuery is the Query containing the setOp node, whose rtable * is where to look up the RTE if setOp is a RangeTblRef. This is *not* the * same as root->parse, which is the top-level Query we are pulling up into. * * parentRTindex is the appendrel parent's index in root->parse->rtable. * * The child RTEs have already been copied to the parent. childRToffset * tells us where in the parent's range table they were copied. */static voidpull_up_union_leaf_queries(Node *setOp, PlannerInfo *root, int parentRTindex, Query *setOpQuery, int childRToffset){ if (IsA(setOp, RangeTblRef)) { RangeTblRef *rtr = (RangeTblRef *) setOp; int childRTindex; AppendRelInfo *appinfo; /* * Calculate the index in the parent's range table */ childRTindex = childRToffset + rtr->rtindex; /* * Build a suitable AppendRelInfo, and attach to parent's list. */ appinfo = makeNode(AppendRelInfo); appinfo->parent_relid = parentRTindex; appinfo->child_relid = childRTindex; appinfo->parent_reltype = InvalidOid; appinfo->child_reltype = InvalidOid; make_setop_translation_lists(setOpQuery, childRTindex, &appinfo->col_mappings, &appinfo->translated_vars); appinfo->parent_reloid = InvalidOid; root->append_rel_list = lappend(root->append_rel_list, appinfo); /* * Recursively apply pull_up_subqueries to the new child RTE. (We * must build the AppendRelInfo first, because this will modify it.) * Note that we can pass below_outer_join = false even if we're * actually under an outer join, because the child's expressions * aren't going to propagate up above the join. */ rtr = makeNode(RangeTblRef); rtr->rtindex = childRTindex; (void) pull_up_subqueries(root, (Node *) rtr, false, true); } else if (IsA(setOp, SetOperationStmt)) { SetOperationStmt *op = (SetOperationStmt *) setOp; /* Recurse to reach leaf queries */ pull_up_union_leaf_queries(op->larg, root, parentRTindex, setOpQuery, childRToffset); pull_up_union_leaf_queries(op->rarg, root, parentRTindex, setOpQuery, childRToffset); } else { elog(ERROR, "unrecognized node type: %d", (int) nodeTag(setOp)); }}/* * make_setop_translation_lists * Build the lists of translations from parent Vars to child Vars for * a UNION ALL member. We need both a column number mapping list * and a list of Vars representing the child columns. */static voidmake_setop_translation_lists(Query *query, Index newvarno, List **col_mappings, List **translated_vars){ List *numbers = NIL; List *vars = NIL; ListCell *l; foreach(l, query->targetList) { TargetEntry *tle = (TargetEntry *) lfirst(l); if (tle->resjunk) continue; numbers = lappend_int(numbers, tle->resno); vars = lappend(vars, makeVar(newvarno, tle->resno, exprType((Node *) tle->expr), exprTypmod((Node *) tle->expr), 0)); } *col_mappings = numbers; *translated_vars = vars;}/* * is_simple_subquery * Check a subquery in the range table to see if it's simple enough * to pull up into the parent query. */static boolis_simple_subquery(Query *subquery){ /* * Let's just make sure it's a valid subselect ... */ if (!IsA(subquery, Query) || subquery->commandType != CMD_SELECT || subquery->utilityStmt != NULL || subquery->intoClause != NULL) elog(ERROR, "subquery is bogus"); /* * Can't currently pull up a query with setops (unless it's simple UNION * ALL, which is handled by a different code path). Maybe after querytree * redesign... */ if (subquery->setOperations) return false; /* * Can't pull up a subquery involving grouping, aggregation, sorting, or * limiting. */ if (subquery->hasAggs || subquery->groupClause || subquery->havingQual || subquery->sortClause || subquery->distinctClause || subquery->limitOffset || subquery->limitCount) return false; /* * Don't pull up a subquery that has any set-returning functions in its * targetlist. Otherwise we might well wind up inserting set-returning * functions into places where they mustn't go, such as quals of higher * queries. */ if (expression_returns_set((Node *) subquery->targetList)) return false; /* * Don't pull up a subquery that has any volatile functions in its * targetlist. Otherwise we might introduce multiple evaluations of these * functions, if they get copied to multiple places in the upper query, * leading to surprising results. */ if (contain_volatile_functions((Node *) subquery->targetList)) return false; /* * Hack: don't try to pull up a subquery with an empty jointree. * query_planner() will correctly generate a Result plan for a jointree * that's totally empty, but I don't think the right things happen if an * empty FromExpr appears lower down in a jointree. Not worth working hard * on this, just to collapse SubqueryScan/Result into Result... */ if (subquery->jointree->fromlist == NIL) return false; return true;}/* * is_simple_union_all * Check a subquery to see if it's a simple UNION ALL. * * We require all the setops to be UNION ALL (no mixing) and there can't be * any datatype coercions involved, ie, all the leaf queries must emit the * same datatypes. */static boolis_simple_union_all(Query *subquery){ SetOperationStmt *topop; /* Let's just make sure it's a valid subselect ... */ if (!IsA(subquery, Query) || subquery->commandType != CMD_SELECT || subquery->utilityStmt != NULL || subquery->intoClause != NULL) elog(ERROR, "subquery is bogus"); /* Is it a set-operation query at all? */ topop = (SetOperationStmt *) subquery->setOperations; if (!topop) return false; Assert(IsA(topop, SetOperationStmt)); /* Can't handle ORDER BY, LIMIT/OFFSET, or locking */ if (subquery->sortClause || subquery->limitOffset || subquery->limitCount || subquery->rowMarks) return false; /* Recursively check the tree of set operations */ return is_simple_union_all_recurse((Node *) topop, subquery, topop->colTypes);}static boolis_simple_union_all_recurse(Node *setOp, Query *setOpQuery, List *colTypes){ if (IsA(setOp, RangeTblRef)) { RangeTblRef *rtr = (RangeTblRef *) setOp; RangeTblEntry *rte = rt_fetch(rtr->rtindex, setOpQuery->rtable); Query *subquery = rte->subquery; Assert(subquery != NULL); /* Leaf nodes are OK if they match the toplevel column types */ /* We don't have to compare typmods here */ return tlist_same_datatypes(subquery->targetList, colTypes, true); } else if (IsA(setOp, SetOperationStmt)) { SetOperationStmt *op = (SetOperationStmt *) setOp; /* Must be UNION ALL */ if (op->op != SETOP_UNION || !op->all) return false; /* Recurse to check inputs */ return is_simple_union_all_recurse(op->larg, setOpQuery, colTypes) && is_simple_union_all_recurse(op->rarg, setOpQuery, colTypes); } else { elog(ERROR, "unrecognized node type: %d", (int) nodeTag(setOp)); return false; /* keep compiler quiet */ }}/* * has_nullable_targetlist * Check a subquery in the range table to see if all the non-junk * targetlist items are simple variables or strict functions of simple * variables (and, hence, will correctly go to NULL when examined above * the point of an outer join). * * NOTE: it would be correct (and useful) to ignore output columns that aren't * actually referenced by the enclosing query ... but we do not have that * information available at this point. */static boolhas_nullable_targetlist(Query *subquery){ ListCell *l; foreach(l, subquery->targetList) { TargetEntry *tle = (TargetEntry *) lfirst(l); /* ignore resjunk columns */ if (tle->resjunk) continue; /* Must contain a Var of current level */ if (!contain_vars_of_level((Node *) tle->expr, 0)) return false; /* Must not contain any non-strict constructs */ if (contain_nonstrict_functions((Node *) tle->expr)) return false; /* This one's OK, keep scanning */ } return true;}/* * is_safe_append_member * Check a subquery that is a leaf of a UNION ALL appendrel to see if it's * safe to pull up. */static boolis_safe_append_member(Query *subquery){ FromExpr *jtnode; ListCell *l; /* * It's only safe to pull up the child if its jointree contains exactly * one RTE, else the AppendRelInfo data structure breaks. The one base RTE * could be buried in several levels of FromExpr, however. * * Also, the child can't have any WHERE quals because there's no place to * put them in an appendrel. (This is a bit annoying...) If we didn't * need to check this, we'd just test whether get_relids_in_jointree() * yields a singleton set, to be more consistent with the coding of * fix_append_rel_relids(). */ jtnode = subquery->jointree; while (IsA(jtnode, FromExpr)) { if (jtnode->quals != NULL) return false; if (list_length(jtnode->fromlist) != 1) return false; jtnode = linitial(jtnode->fromlist); } if (!IsA(jtnode, RangeTblRef)) return false; /* * XXX For the moment we also have to insist that the subquery's tlist * includes only simple Vars. This is pretty annoying, but fixing it * seems to require nontrivial changes --- mainly because joinrel tlists * are presently assumed to contain only Vars. Perhaps a pseudo-variable * mechanism similar to the one speculated about in pull_up_subqueries' * comments would help? FIXME someday. */ foreach(l, subquery->targetList) { TargetEntry *tle = (TargetEntry *) lfirst(l); if (tle->resjunk) continue; if (!(tle->expr && IsA(tle->expr, Var))) 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... */static voidresolvenew_in_jointree(Node *jtnode, int varno, RangeTblEntry *rte, List *subtlist){ if (jtnode == NULL) return; if (IsA(jtnode, RangeTblRef)) { /* nothing to do here */ } else if (IsA(jtnode, FromExpr)) { FromExpr *f = (FromExpr *) jtnode; ListCell *l; foreach(l, f->fromlist) resolvenew_in_jointree(lfirst(l), varno, rte, subtlist); f->quals = ResolveNew(f->quals, varno, 0, rte, subtlist, CMD_SELECT, 0); } else if (IsA(jtnode, JoinExpr)) {
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?