📄 prepjointree.c
字号:
break; default: elog(ERROR, "unrecognized join type: %d", (int) j->jointype); break; } } else elog(ERROR, "unrecognized node type: %d", (int) nodeTag(jtnode)); return jtnode;}/* * 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->resultRelation != 0 || subquery->into != NULL) elog(ERROR, "subquery is bogus"); /* * Can't currently pull up a query with setops. 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; /* * 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;}/* * 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;}/* * 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)) { JoinExpr *j = (JoinExpr *) jtnode; resolvenew_in_jointree(j->larg, varno, rte, subtlist); resolvenew_in_jointree(j->rarg, varno, rte, subtlist); j->quals = ResolveNew(j->quals, varno, 0, rte, subtlist, CMD_SELECT, 0); /* * We don't bother to update the colvars list, since it won't be used * again ... */ } else elog(ERROR, "unrecognized node type: %d", (int) nodeTag(jtnode));}/* * reduce_outer_joins * Attempt to reduce outer joins to plain inner joins. * * The idea here is that given a query like * SELECT ... FROM a LEFT JOIN b ON (...) WHERE b.y = 42; * we can reduce the LEFT JOIN to a plain JOIN if the "=" operator in WHERE * is strict. The strict operator will always return NULL, causing the outer * WHERE to fail, on any row where the LEFT JOIN filled in NULLs for b's * columns. Therefore, there's no need for the join to produce null-extended * rows in the first place --- which makes it a plain join not an outer join. * (This scenario may not be very likely in a query written out by hand, but * it's reasonably likely when pushing quals down into complex views.) * * More generally, an outer join can be reduced in strength if there is a * strict qual above it in the qual tree that constrains a Var from the * nullable side of the join to be non-null. (For FULL joins this applies * to each side separately.) * * To ease recognition of strict qual clauses, we require this routine to be * run after expression preprocessing (i.e., qual canonicalization and JOIN * alias-var expansion). */voidreduce_outer_joins(PlannerInfo *root){ reduce_outer_joins_state *state; /* * To avoid doing strictness checks on more quals than necessary, we want * to stop descending the jointree as soon as there are no outer joins * below our current point. This consideration forces a two-pass process. * The first pass gathers information about which base rels appear below * each side of each join clause, and about whether there are outer * join(s) below each side of each join clause. The second pass examines * qual clauses and changes join types as it descends the tree. */ state = reduce_outer_joins_pass1((Node *) root->parse->jointree); /* planner.c shouldn't have called me if no outer joins */ if (state == NULL || !state->contains_outer) elog(ERROR, "so where are the outer joins?"); reduce_outer_joins_pass2((Node *) root->parse->jointree, state, root, NULL);}/* * reduce_outer_joins_pass1 - phase 1 data collection * * Returns a state node describing the given jointree node. */static reduce_outer_joins_state *reduce_outer_joins_pass1(Node *jtnode){ reduce_outer_joins_state *result; result = (reduce_outer_joins_state *) palloc(sizeof(reduce_outer_joins_state)); result->relids = NULL; result->contains_outer = false; result->sub_states = NIL; if (jtnode == NULL) return result; if (IsA(jtnode, RangeTblRef)) { int varno = ((RangeTblRef *) jtnode)->rtindex; result->relids = bms_make_singleton(varno); } else if (IsA(jtnode, FromExpr)) { FromExpr *f = (FromExpr *) jtnode; ListCell *l; foreach(l, f->fromlist) { reduce_outer_joins_state *sub_state; sub_state = reduce_outer_joins_pass1(lfirst(l)); result->relids = bms_add_members(result->relids, sub_state->relids); result->contains_outer |= sub_state->contains_outer; result->sub_states = lappend(result->sub_states, sub_state); } } else if (IsA(jtnode, JoinExpr)) { JoinExpr *j = (JoinExpr *) jtnode; reduce_outer_joins_state *sub_state; /* join's own RT index is not wanted in result->relids */ if (IS_OUTER_JOIN(j->jointype)) result->contains_outer = true; sub_state = reduce_outer_joins_pass1(j->larg); result->relids = bms_add_members(result->relids, sub_state->relids); result->contains_outer |= sub_state->contains_outer; result->sub_states = lappend(result->sub_states, sub_state); sub_state = reduce_outer_joins_pass1(j->rarg); result->relids = bms_add_members(result->relids, sub_state->relids); result->contains_outer |= sub_state->contains_outer; result->sub_states = lappend(result->sub_states, sub_state); } else elog(ERROR, "unrecognized node type: %d", (int) nodeTag(jtnode)); return result;}/* * reduce_outer_joins_pass2 - phase 2 processing * * jtnode: current jointree node * state: state data collected by phase 1 for this node * root: toplevel planner state * nonnullable_rels: set of base relids forced non-null by upper quals */static voidreduce_outer_joins_pass2(Node *jtnode, reduce_outer_joins_state *state, PlannerInfo *root, Relids nonnullable_rels){ /* * pass 2 should never descend as far as an empty subnode or base rel, * because it's only called on subtrees marked as contains_outer. */ if (jtnode == NULL) elog(ERROR, "reached empty jointree"); if (IsA(jtnode, RangeTblRef)) elog(ERROR, "reached base rel"); else if (IsA(jtnode, FromExpr)) { FromExpr *f = (FromExpr *) jtnode; ListCell *l; ListCell *s; Relids pass_nonnullable; /* Scan quals to see if we can add any nonnullability constraints */ pass_nonnullable = find_nonnullable_rels(f->quals, true); pass_nonnullable = bms_add_members(pass_nonnullable, nonnullable_rels); /* And recurse --- but only into interesting subtrees */ Assert(list_length(f->fromlist) == list_length(state->sub_states)); forboth(l, f->fromlist, s, state->sub_states) { reduce_outer_joins_state *sub_state = lfirst(s); if (sub_state->contains_outer) reduce_outer_joins_pass2(lfirst(l), sub_state, root, pass_nonnullable); } bms_free(pass_nonnullable); } else if (IsA(jtnode, JoinExpr)) { JoinExpr *j = (JoinExpr *) jtnode; int rtindex = j->rtindex; JoinType jointype = j->jointype; reduce_outer_joins_state *left_state = linitial(state->sub_states); reduce_outer_joins_state *right_state = lsecond(state->sub_states); /* Can we simplify this join? */ switch (jointype) { case JOIN_LEFT: if (bms_overlap(nonnullable_rels, right_state->relids)) jointype = JOIN_INNER; break; case JOIN_RIGHT: if (bms_overlap(nonnullable_rels, left_state->relids)) jointype = JOIN_INNER; break; case JOIN_FULL: if (bms_overlap(nonnullable_rels, left_state->relids)) { if (bms_overlap(nonnullable_rels, right_state->relids)) jointype = JOIN_INNER; else jointype = JOIN_LEFT; } else { if (bms_overlap(nonnullable_rels, right_state->relids)) jointype = JOIN_RIGHT; } break; default: break; } if (jointype != j->jointype) { /* apply the change to both jointree node and RTE */ RangeTblEntry *rte = rt_fetch(rtindex, root->parse->rtable); Assert(rte->rtekind == RTE_JOIN); Assert(rte->jointype == j->jointype); rte->jointype = j->jointype = jointype; } /* Only recurse if there's more to do below here */ if (left_state->contains_outer || right_state->contains_outer) { Relids local_nonnullable; Relids pass_nonnullable; /* * If this join is (now) inner, we can add any nonnullability * constraints its quals provide to those we got from above. But * if it is outer, we can only pass down the local constraints * into the nullable side, because an outer join never eliminates * any rows from its non-nullable side. If it's a FULL join then * it doesn't eliminate anything from either side. */ if (jointype != JOIN_FULL) { local_nonnullable = find_nonnullable_rels(j->quals, true); local_nonnullable = bms_add_members(local_nonnullable, nonnullable_rels); } else local_nonnullable = NULL; /* no use in calculating it */ if (left_state->contains_outer) { if (jointype == JOIN_INNER || jointype == JOIN_RIGHT) pass_nonnullable = local_nonnullable; else pass_nonnullable = nonnullable_rels; reduce_outer_joins_pass2(j->larg, left_state, root, pass_nonnullable); } if (right_state->contains_outer) { if (jointype == JOIN_INNER || jointype == JOIN_LEFT) pass_nonnullable = local_nonnullable; else pass_nonnullable = nonnullable_rels;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -