#include "nodes/plannodes.h"
#include "nodes/relation.h"
Go to the source code of this file.
Functions | |
void | pull_up_sublinks (PlannerInfo *root) |
void | inline_set_returning_functions (PlannerInfo *root) |
Node * | pull_up_subqueries (PlannerInfo *root, Node *jtnode) |
void | flatten_simple_union_all (PlannerInfo *root) |
void | reduce_outer_joins (PlannerInfo *root) |
Relids | get_relids_in_jointree (Node *jtnode, bool include_joins) |
Relids | get_relids_for_join (PlannerInfo *root, int joinrelid) |
Node * | negate_clause (Node *node) |
Expr * | canonicalize_qual (Expr *qual) |
List * | preprocess_targetlist (PlannerInfo *root, List *tlist) |
PlanRowMark * | get_plan_rowmark (List *rowmarks, Index rtindex) |
Plan * | plan_set_operations (PlannerInfo *root, double tuple_fraction, List **sortClauses) |
void | expand_inherited_tables (PlannerInfo *root) |
Node * | adjust_appendrel_attrs (PlannerInfo *root, Node *node, AppendRelInfo *appinfo) |
Node* adjust_appendrel_attrs | ( | PlannerInfo * | root, | |
Node * | node, | |||
AppendRelInfo * | appinfo | |||
) |
Definition at line 1590 of file prepunion.c.
References adjust_appendrel_attrs_mutator(), adjust_inherited_tlist(), adjust_appendrel_attrs_context::appinfo, AppendRelInfo::child_relid, CMD_UPDATE, Query::commandType, IsA, AppendRelInfo::parent_relid, QTW_IGNORE_RC_SUBQUERIES, query_tree_mutator(), Query::resultRelation, adjust_appendrel_attrs_context::root, and Query::targetList.
Referenced by add_child_rel_equivalences(), generate_join_implied_equalities_broken(), inheritance_planner(), and set_append_rel_size().
{ Node *result; adjust_appendrel_attrs_context context; context.root = root; context.appinfo = appinfo; /* * Must be prepared to start with a Query or a bare expression tree. */ if (node && IsA(node, Query)) { Query *newnode; newnode = query_tree_mutator((Query *) node, adjust_appendrel_attrs_mutator, (void *) &context, QTW_IGNORE_RC_SUBQUERIES); if (newnode->resultRelation == appinfo->parent_relid) { newnode->resultRelation = appinfo->child_relid; /* Fix tlist resnos too, if it's inherited UPDATE */ if (newnode->commandType == CMD_UPDATE) newnode->targetList = adjust_inherited_tlist(newnode->targetList, appinfo); } result = (Node *) newnode; } else result = adjust_appendrel_attrs_mutator(node, &context); return result; }
Definition at line 285 of file prepqual.c.
References find_duplicate_ors(), and NULL.
Referenced by convert_EXISTS_to_ANY(), get_relation_constraints(), preprocess_expression(), and RelationGetIndexPredicate().
{ Expr *newqual; /* Quick exit for empty qual */ if (qual == NULL) return NULL; /* * Pull up redundant subclauses in OR-of-AND trees. We do this only * within the top-level AND/OR structure; there's no point in looking * deeper. */ newqual = find_duplicate_ors(qual); return newqual; }
void expand_inherited_tables | ( | PlannerInfo * | root | ) |
Definition at line 1191 of file prepunion.c.
References expand_inherited_rtentry(), lfirst, list_head(), list_length(), lnext, PlannerInfo::parse, and Query::rtable.
Referenced by subquery_planner().
{ Index nrtes; Index rti; ListCell *rl; /* * expand_inherited_rtentry may add RTEs to parse->rtable; there is no * need to scan them since they can't have inh=true. So just scan as far * as the original end of the rtable list. */ nrtes = list_length(root->parse->rtable); rl = list_head(root->parse->rtable); for (rti = 1; rti <= nrtes; rti++) { RangeTblEntry *rte = (RangeTblEntry *) lfirst(rl); expand_inherited_rtentry(root, rte, rti); rl = lnext(rl); } }
void flatten_simple_union_all | ( | PlannerInfo * | root | ) |
Definition at line 1779 of file prepjointree.c.
References Assert, copyObject(), FromExpr::fromlist, PlannerInfo::hasRecursion, RangeTblEntry::inh, is_simple_union_all_recurse(), IsA, Query::jointree, lappend(), list_length(), list_make1, makeNode, NIL, PlannerInfo::parse, parse(), pull_up_union_leaf_queries(), rt_fetch, Query::rtable, RTE_SUBQUERY, RangeTblEntry::rtekind, RangeTblRef::rtindex, and Query::setOperations.
Referenced by subquery_planner().
{ Query *parse = root->parse; SetOperationStmt *topop; Node *leftmostjtnode; int leftmostRTI; RangeTblEntry *leftmostRTE; int childRTI; RangeTblEntry *childRTE; RangeTblRef *rtr; /* Shouldn't be called unless query has setops */ topop = (SetOperationStmt *) parse->setOperations; Assert(topop && IsA(topop, SetOperationStmt)); /* Can't optimize away a recursive UNION */ if (root->hasRecursion) return; /* * Recursively check the tree of set operations. If not all UNION ALL * with identical column types, punt. */ if (!is_simple_union_all_recurse((Node *) topop, parse, topop->colTypes)) return; /* * Locate the leftmost leaf query in the setops tree. The upper query's * Vars all refer to this RTE (see transformSetOperationStmt). */ leftmostjtnode = topop->larg; while (leftmostjtnode && IsA(leftmostjtnode, SetOperationStmt)) leftmostjtnode = ((SetOperationStmt *) leftmostjtnode)->larg; Assert(leftmostjtnode && IsA(leftmostjtnode, RangeTblRef)); leftmostRTI = ((RangeTblRef *) leftmostjtnode)->rtindex; leftmostRTE = rt_fetch(leftmostRTI, parse->rtable); Assert(leftmostRTE->rtekind == RTE_SUBQUERY); /* * Make a copy of the leftmost RTE and add it to the rtable. This copy * will represent the leftmost leaf query in its capacity as a member of * the appendrel. The original will represent the appendrel as a whole. * (We must do things this way because the upper query's Vars have to be * seen as referring to the whole appendrel.) */ childRTE = copyObject(leftmostRTE); parse->rtable = lappend(parse->rtable, childRTE); childRTI = list_length(parse->rtable); /* Modify the setops tree to reference the child copy */ ((RangeTblRef *) leftmostjtnode)->rtindex = childRTI; /* Modify the formerly-leftmost RTE to mark it as an appendrel parent */ leftmostRTE->inh = true; /* * Form a RangeTblRef for the appendrel, and insert it into FROM. The top * Query of a setops tree should have had an empty FromClause initially. */ rtr = makeNode(RangeTblRef); rtr->rtindex = leftmostRTI; Assert(parse->jointree->fromlist == NIL); parse->jointree->fromlist = list_make1(rtr); /* * Now pretend the query has no setops. We must do this before trying to * do subquery pullup, because of Assert in pull_up_simple_subquery. */ parse->setOperations = NULL; /* * Build AppendRelInfo information, and apply pull_up_subqueries to the * leaf queries of the UNION ALL. (We must do that now because they * weren't previously referenced by the jointree, and so were missed by * the main invocation of pull_up_subqueries.) */ pull_up_union_leaf_queries((Node *) topop, root, leftmostRTI, parse, 0); }
PlanRowMark* get_plan_rowmark | ( | List * | rowmarks, | |
Index | rtindex | |||
) |
Definition at line 373 of file preptlist.c.
References lfirst, and PlanRowMark::rti.
Referenced by AcquireExecutorLocks(), and expand_inherited_rtentry().
{ ListCell *l; foreach(l, rowmarks) { PlanRowMark *rc = (PlanRowMark *) lfirst(l); if (rc->rti == rtindex) return rc; } return NULL; }
Relids get_relids_for_join | ( | PlannerInfo * | root, | |
int | joinrelid | |||
) |
Definition at line 2446 of file prepjointree.c.
References elog, ERROR, find_jointree_node_for_rel(), get_relids_in_jointree(), Query::jointree, and PlannerInfo::parse.
Referenced by alias_relid_set().
{ Node *jtnode; jtnode = find_jointree_node_for_rel((Node *) root->parse->jointree, joinrelid); if (!jtnode) elog(ERROR, "could not find join node %d", joinrelid); return get_relids_in_jointree(jtnode, false); }
Definition at line 2402 of file prepjointree.c.
References bms_add_member(), bms_join(), bms_make_singleton(), elog, ERROR, FromExpr::fromlist, get_relids_in_jointree(), IsA, JoinExpr::larg, lfirst, nodeTag, NULL, JoinExpr::rarg, and JoinExpr::rtindex.
Referenced by distribute_qual_to_rels(), get_relids_for_join(), get_relids_in_jointree(), is_simple_subquery(), and pull_up_simple_subquery().
{ Relids result = NULL; if (jtnode == NULL) return result; if (IsA(jtnode, RangeTblRef)) { int varno = ((RangeTblRef *) jtnode)->rtindex; result = bms_make_singleton(varno); } else if (IsA(jtnode, FromExpr)) { FromExpr *f = (FromExpr *) jtnode; ListCell *l; foreach(l, f->fromlist) { result = bms_join(result, get_relids_in_jointree(lfirst(l), include_joins)); } } else if (IsA(jtnode, JoinExpr)) { JoinExpr *j = (JoinExpr *) jtnode; result = get_relids_in_jointree(j->larg, include_joins); result = bms_join(result, get_relids_in_jointree(j->rarg, include_joins)); if (include_joins && j->rtindex) result = bms_add_member(result, j->rtindex); } else elog(ERROR, "unrecognized node type: %d", (int) nodeTag(jtnode)); return result; }
void inline_set_returning_functions | ( | PlannerInfo * | root | ) |
Definition at line 560 of file prepjointree.c.
References RangeTblEntry::funccolcollations, RangeTblEntry::funccoltypes, RangeTblEntry::funccoltypmods, RangeTblEntry::funcexpr, inline_set_returning_function(), lfirst, PlannerInfo::parse, Query::rtable, RTE_FUNCTION, RangeTblEntry::rtekind, and RangeTblEntry::subquery.
Referenced by pull_up_simple_subquery(), and subquery_planner().
{ ListCell *rt; foreach(rt, root->parse->rtable) { RangeTblEntry *rte = (RangeTblEntry *) lfirst(rt); if (rte->rtekind == RTE_FUNCTION) { Query *funcquery; /* Check safety of expansion, and expand if possible */ funcquery = inline_set_returning_function(root, rte); if (funcquery) { /* Successful expansion, replace the rtable entry */ rte->rtekind = RTE_SUBQUERY; rte->subquery = funcquery; rte->funcexpr = NULL; rte->funccoltypes = NIL; rte->funccoltypmods = NIL; rte->funccolcollations = NIL; } } } }
Definition at line 74 of file prepqual.c.
References AND_EXPR, BooleanTest::arg, NullTest::arg, NullTest::argisrow, BoolExpr::args, ScalarArrayOpExpr::args, OpExpr::args, BoolExpr::boolop, BooleanTest::booltesttype, Const::constisnull, Const::constvalue, DatumGetBool, elog, ERROR, get_negator(), ScalarArrayOpExpr::inputcollid, OpExpr::inputcollid, IS_FALSE, IS_NOT_FALSE, IS_NOT_NULL, IS_NOT_TRUE, IS_NOT_UNKNOWN, IS_NULL, IS_TRUE, IS_UNKNOWN, lappend(), lfirst, linitial, ScalarArrayOpExpr::location, OpExpr::location, make_andclause(), make_notclause(), make_orclause(), makeBoolConst(), makeNode, negate_clause(), nodeTag, NOT_EXPR, NULL, NullTest::nulltesttype, OpExpr::opcollid, ScalarArrayOpExpr::opfuncid, OpExpr::opfuncid, ScalarArrayOpExpr::opno, OpExpr::opno, OpExpr::opresulttype, OpExpr::opretset, OR_EXPR, T_BooleanTest, T_BoolExpr, T_Const, T_NullTest, T_OpExpr, T_ScalarArrayOpExpr, and ScalarArrayOpExpr::useOr.
Referenced by eval_const_expressions_mutator(), negate_clause(), and simplify_boolean_equality().
{ if (node == NULL) /* should not happen */ elog(ERROR, "can't negate an empty subexpression"); switch (nodeTag(node)) { case T_Const: { Const *c = (Const *) node; /* NOT NULL is still NULL */ if (c->constisnull) return makeBoolConst(false, true); /* otherwise pretty easy */ return makeBoolConst(!DatumGetBool(c->constvalue), false); } break; case T_OpExpr: { /* * Negate operator if possible: (NOT (< A B)) => (>= A B) */ OpExpr *opexpr = (OpExpr *) node; Oid negator = get_negator(opexpr->opno); if (negator) { OpExpr *newopexpr = makeNode(OpExpr); newopexpr->opno = negator; newopexpr->opfuncid = InvalidOid; newopexpr->opresulttype = opexpr->opresulttype; newopexpr->opretset = opexpr->opretset; newopexpr->opcollid = opexpr->opcollid; newopexpr->inputcollid = opexpr->inputcollid; newopexpr->args = opexpr->args; newopexpr->location = opexpr->location; return (Node *) newopexpr; } } break; case T_ScalarArrayOpExpr: { /* * Negate a ScalarArrayOpExpr if its operator has a negator; * for example x = ANY (list) becomes x <> ALL (list) */ ScalarArrayOpExpr *saopexpr = (ScalarArrayOpExpr *) node; Oid negator = get_negator(saopexpr->opno); if (negator) { ScalarArrayOpExpr *newopexpr = makeNode(ScalarArrayOpExpr); newopexpr->opno = negator; newopexpr->opfuncid = InvalidOid; newopexpr->useOr = !saopexpr->useOr; newopexpr->inputcollid = saopexpr->inputcollid; newopexpr->args = saopexpr->args; newopexpr->location = saopexpr->location; return (Node *) newopexpr; } } break; case T_BoolExpr: { BoolExpr *expr = (BoolExpr *) node; switch (expr->boolop) { /*-------------------- * Apply DeMorgan's Laws: * (NOT (AND A B)) => (OR (NOT A) (NOT B)) * (NOT (OR A B)) => (AND (NOT A) (NOT B)) * i.e., swap AND for OR and negate each subclause. * * If the input is already AND/OR flat and has no NOT * directly above AND or OR, this transformation preserves * those properties. For example, if no direct child of * the given AND clause is an AND or a NOT-above-OR, then * the recursive calls of negate_clause() can't return any * OR clauses. So we needn't call pull_ors() before * building a new OR clause. Similarly for the OR case. *-------------------- */ case AND_EXPR: { List *nargs = NIL; ListCell *lc; foreach(lc, expr->args) { nargs = lappend(nargs, negate_clause(lfirst(lc))); } return (Node *) make_orclause(nargs); } break; case OR_EXPR: { List *nargs = NIL; ListCell *lc; foreach(lc, expr->args) { nargs = lappend(nargs, negate_clause(lfirst(lc))); } return (Node *) make_andclause(nargs); } break; case NOT_EXPR: /* * NOT underneath NOT: they cancel. We assume the * input is already simplified, so no need to recurse. */ return (Node *) linitial(expr->args); default: elog(ERROR, "unrecognized boolop: %d", (int) expr->boolop); break; } } break; case T_NullTest: { NullTest *expr = (NullTest *) node; /* * In the rowtype case, the two flavors of NullTest are *not* * logical inverses, so we can't simplify. But it does work * for scalar datatypes. */ if (!expr->argisrow) { NullTest *newexpr = makeNode(NullTest); newexpr->arg = expr->arg; newexpr->nulltesttype = (expr->nulltesttype == IS_NULL ? IS_NOT_NULL : IS_NULL); newexpr->argisrow = expr->argisrow; return (Node *) newexpr; } } break; case T_BooleanTest: { BooleanTest *expr = (BooleanTest *) node; BooleanTest *newexpr = makeNode(BooleanTest); newexpr->arg = expr->arg; switch (expr->booltesttype) { case IS_TRUE: newexpr->booltesttype = IS_NOT_TRUE; break; case IS_NOT_TRUE: newexpr->booltesttype = IS_TRUE; break; case IS_FALSE: newexpr->booltesttype = IS_NOT_FALSE; break; case IS_NOT_FALSE: newexpr->booltesttype = IS_FALSE; break; case IS_UNKNOWN: newexpr->booltesttype = IS_NOT_UNKNOWN; break; case IS_NOT_UNKNOWN: newexpr->booltesttype = IS_UNKNOWN; break; default: elog(ERROR, "unrecognized booltesttype: %d", (int) expr->booltesttype); break; } return (Node *) newexpr; } break; default: /* else fall through */ break; } /* * Otherwise we don't know how to simplify this, so just tack on an * explicit NOT node. */ return (Node *) make_notclause((Expr *) node); }
Plan* plan_set_operations | ( | PlannerInfo * | root, | |
double | tuple_fraction, | |||
List ** | sortClauses | |||
) |
Definition at line 135 of file prepunion.c.
References Assert, Query::distinctClause, FromExpr::fromlist, generate_recursion_plan(), Query::groupClause, PlannerInfo::hasRecursion, Query::havingQual, IsA, Query::jointree, SetOperationStmt::larg, NIL, NULL, PlannerInfo::parse, parse(), FromExpr::quals, recurse_set_operations(), Query::setOperations, setup_simple_rel_arrays(), PlannerInfo::simple_rte_array, RangeTblEntry::subquery, and Query::windowClause.
Referenced by grouping_planner().
{ Query *parse = root->parse; SetOperationStmt *topop = (SetOperationStmt *) parse->setOperations; Node *node; RangeTblEntry *leftmostRTE; Query *leftmostQuery; Assert(topop && IsA(topop, SetOperationStmt)); /* check for unsupported stuff */ Assert(parse->jointree->fromlist == NIL); Assert(parse->jointree->quals == NULL); Assert(parse->groupClause == NIL); Assert(parse->havingQual == NULL); Assert(parse->windowClause == NIL); Assert(parse->distinctClause == NIL); /* * We'll need to build RelOptInfos for each of the leaf subqueries, which * are RTE_SUBQUERY rangetable entries in this Query. Prepare the index * arrays for that. */ setup_simple_rel_arrays(root); /* * Find the leftmost component Query. We need to use its column names for * all generated tlists (else SELECT INTO won't work right). */ node = topop->larg; while (node && IsA(node, SetOperationStmt)) node = ((SetOperationStmt *) node)->larg; Assert(node && IsA(node, RangeTblRef)); leftmostRTE = root->simple_rte_array[((RangeTblRef *) node)->rtindex]; leftmostQuery = leftmostRTE->subquery; Assert(leftmostQuery != NULL); /* * If the topmost node is a recursive union, it needs special processing. */ if (root->hasRecursion) return generate_recursion_plan(topop, root, tuple_fraction, leftmostQuery->targetList, sortClauses); /* * Recurse on setOperations tree to generate plans for set ops. The final * output plan should have just the column types shown as the output from * the top-level node, plus possibly resjunk working columns (we can rely * on upper-level nodes to deal with that). */ return recurse_set_operations((Node *) topop, root, tuple_fraction, topop->colTypes, topop->colCollations, true, -1, leftmostQuery->targetList, sortClauses, NULL); }
List* preprocess_targetlist | ( | PlannerInfo * | root, | |
List * | tlist | |||
) |
Definition at line 50 of file preptlist.c.
References CMD_INSERT, CMD_UPDATE, Query::commandType, elog, ERROR, expand_targetlist(), InvalidOid, IsA, PlanRowMark::isParent, lappend(), lfirst, list_free(), list_length(), makeTargetEntry(), makeVar(), makeWholeRowVar(), PlanRowMark::markType, NULL, OIDOID, PlannerInfo::parse, parse(), PlanRowMark::prti, pstrdup(), pull_var_clause(), PVC_INCLUDE_PLACEHOLDERS, PVC_RECURSE_AGGREGATES, RangeTblEntry::relid, Query::resultRelation, Query::returningList, ROW_MARK_COPY, PlanRowMark::rowmarkId, PlannerInfo::rowMarks, rt_fetch, Query::rtable, PlanRowMark::rti, SelfItemPointerAttributeNumber, snprintf(), RangeTblEntry::subquery, TableOidAttributeNumber, TIDOID, tlist_member(), and Var::varno.
Referenced by grouping_planner(), and inheritance_planner().
{ Query *parse = root->parse; int result_relation = parse->resultRelation; List *range_table = parse->rtable; CmdType command_type = parse->commandType; ListCell *lc; /* * Sanity check: if there is a result relation, it'd better be a real * relation not a subquery. Else parser or rewriter messed up. */ if (result_relation) { RangeTblEntry *rte = rt_fetch(result_relation, range_table); if (rte->subquery != NULL || rte->relid == InvalidOid) elog(ERROR, "subquery cannot be result relation"); } /* * for heap_form_tuple to work, the targetlist must match the exact order * of the attributes. We also need to fill in any missing attributes. -ay * 10/94 */ if (command_type == CMD_INSERT || command_type == CMD_UPDATE) tlist = expand_targetlist(tlist, command_type, result_relation, range_table); /* * Add necessary junk columns for rowmarked rels. These values are needed * for locking of rels selected FOR UPDATE/SHARE, and to do EvalPlanQual * rechecking. See comments for PlanRowMark in plannodes.h. */ foreach(lc, root->rowMarks) { PlanRowMark *rc = (PlanRowMark *) lfirst(lc); Var *var; char resname[32]; TargetEntry *tle; /* child rels use the same junk attrs as their parents */ if (rc->rti != rc->prti) continue; if (rc->markType != ROW_MARK_COPY) { /* It's a regular table, so fetch its TID */ var = makeVar(rc->rti, SelfItemPointerAttributeNumber, TIDOID, -1, InvalidOid, 0); snprintf(resname, sizeof(resname), "ctid%u", rc->rowmarkId); tle = makeTargetEntry((Expr *) var, list_length(tlist) + 1, pstrdup(resname), true); tlist = lappend(tlist, tle); /* if parent of inheritance tree, need the tableoid too */ if (rc->isParent) { var = makeVar(rc->rti, TableOidAttributeNumber, OIDOID, -1, InvalidOid, 0); snprintf(resname, sizeof(resname), "tableoid%u", rc->rowmarkId); tle = makeTargetEntry((Expr *) var, list_length(tlist) + 1, pstrdup(resname), true); tlist = lappend(tlist, tle); } } else { /* Not a table, so we need the whole row as a junk var */ var = makeWholeRowVar(rt_fetch(rc->rti, range_table), rc->rti, 0, false); snprintf(resname, sizeof(resname), "wholerow%u", rc->rowmarkId); tle = makeTargetEntry((Expr *) var, list_length(tlist) + 1, pstrdup(resname), true); tlist = lappend(tlist, tle); } } /* * If the query has a RETURNING list, add resjunk entries for any Vars * used in RETURNING that belong to other relations. We need to do this * to make these Vars available for the RETURNING calculation. Vars that * belong to the result rel don't need to be added, because they will be * made to refer to the actual heap tuple. */ if (parse->returningList && list_length(parse->rtable) > 1) { List *vars; ListCell *l; vars = pull_var_clause((Node *) parse->returningList, PVC_RECURSE_AGGREGATES, PVC_INCLUDE_PLACEHOLDERS); foreach(l, vars) { Var *var = (Var *) lfirst(l); TargetEntry *tle; if (IsA(var, Var) && var->varno == result_relation) continue; /* don't need it */ if (tlist_member((Node *) var, tlist)) continue; /* already got it */ tle = makeTargetEntry((Expr *) var, list_length(tlist) + 1, NULL, true); tlist = lappend(tlist, tle); } list_free(vars); } return tlist; }
void pull_up_sublinks | ( | PlannerInfo * | root | ) |
Definition at line 137 of file prepjointree.c.
References IsA, Query::jointree, list_make1, makeFromExpr(), NULL, PlannerInfo::parse, and pull_up_sublinks_jointree_recurse().
Referenced by pull_up_simple_subquery(), and subquery_planner().
{ Node *jtnode; Relids relids; /* Begin recursion through the jointree */ jtnode = pull_up_sublinks_jointree_recurse(root, (Node *) root->parse->jointree, &relids); /* * root->parse->jointree must always be a FromExpr, so insert a dummy one * if we got a bare RangeTblRef or JoinExpr out of the recursion. */ if (IsA(jtnode, FromExpr)) root->parse->jointree = (FromExpr *) jtnode; else root->parse->jointree = makeFromExpr(list_make1(jtnode), NULL); }
Node* pull_up_subqueries | ( | PlannerInfo * | root, | |
Node * | jtnode | |||
) |
Definition at line 599 of file prepjointree.c.
References NULL, and pull_up_subqueries_recurse().
Referenced by subquery_planner().
{ /* Start off with no containing join nor appendrel */ return pull_up_subqueries_recurse(root, jtnode, NULL, NULL, NULL); }
void reduce_outer_joins | ( | PlannerInfo * | root | ) |
Definition at line 1897 of file prepjointree.c.
References reduce_outer_joins_state::contains_outer, elog, ERROR, Query::jointree, NIL, NULL, PlannerInfo::parse, reduce_outer_joins_pass1(), and reduce_outer_joins_pass2().
Referenced by subquery_planner().
{ 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, NIL, NIL); }