planner.c
来自「postgresql8.3.4源码,开源数据库」· C语言 代码 · 共 1,820 行 · 第 1/4 页
C
1,820 行
preprocess_expression(PlannerInfo *root, Node *expr, int kind){ /* * Fall out quickly if expression is empty. This occurs often enough to * be worth checking. Note that null->null is the correct conversion for * implicit-AND result format, too. */ if (expr == NULL) return NULL; /* * If the query has any join RTEs, replace join alias variables with * base-relation variables. We must do this before sublink processing, * else sublinks expanded out from join aliases wouldn't get processed. We * can skip it in VALUES lists, however, since they can't contain any Vars * at all. */ if (root->hasJoinRTEs && kind != EXPRKIND_VALUES) expr = flatten_join_alias_vars(root, expr); /* * Simplify constant expressions. * * Note: this also flattens nested AND and OR expressions into N-argument * form. All processing of a qual expression after this point must be * careful to maintain AND/OR flatness --- that is, do not generate a tree * with AND directly under AND, nor OR directly under OR. * * Because this is a relatively expensive process, we skip it when the * query is trivial, such as "SELECT 2+2;" or "INSERT ... VALUES()". The * expression will only be evaluated once anyway, so no point in * pre-simplifying; we can't execute it any faster than the executor can, * and we will waste cycles copying the tree. Notice however that we * still must do it for quals (to get AND/OR flatness); and if we are in a * subquery we should not assume it will be done only once. * * For VALUES lists we never do this at all, again on the grounds that we * should optimize for one-time evaluation. */ if (kind != EXPRKIND_VALUES && (root->parse->jointree->fromlist != NIL || kind == EXPRKIND_QUAL || root->query_level > 1)) expr = eval_const_expressions(root, expr); /* * If it's a qual or havingQual, canonicalize it. */ if (kind == EXPRKIND_QUAL) { expr = (Node *) canonicalize_qual((Expr *) expr);#ifdef OPTIMIZER_DEBUG printf("After canonicalize_qual()\n"); pprint(expr);#endif } /* Expand SubLinks to SubPlans */ if (root->parse->hasSubLinks) expr = SS_process_sublinks(root, expr, (kind == EXPRKIND_QUAL)); /* * XXX do not insert anything here unless you have grokked the comments in * SS_replace_correlation_vars ... */ /* Replace uplevel vars with Param nodes (this IS possible in VALUES) */ if (root->query_level > 1) expr = SS_replace_correlation_vars(root, expr); /* * If it's a qual or havingQual, convert it to implicit-AND format. (We * don't want to do this before eval_const_expressions, since the latter * would be unable to simplify a top-level AND correctly. Also, * SS_process_sublinks expects explicit-AND format.) */ if (kind == EXPRKIND_QUAL) expr = (Node *) make_ands_implicit((Expr *) expr); return expr;}/* * preprocess_qual_conditions * Recursively scan the query's jointree and do subquery_planner's * preprocessing work on each qual condition found therein. */static voidpreprocess_qual_conditions(PlannerInfo *root, Node *jtnode){ 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) preprocess_qual_conditions(root, lfirst(l)); f->quals = preprocess_expression(root, f->quals, EXPRKIND_QUAL); } else if (IsA(jtnode, JoinExpr)) { JoinExpr *j = (JoinExpr *) jtnode; preprocess_qual_conditions(root, j->larg); preprocess_qual_conditions(root, j->rarg); j->quals = preprocess_expression(root, j->quals, EXPRKIND_QUAL); } else elog(ERROR, "unrecognized node type: %d", (int) nodeTag(jtnode));}/* * inheritance_planner * Generate a plan in the case where the result relation is an * inheritance set. * * We have to handle this case differently from cases where a source relation * is an inheritance set. Source inheritance is expanded at the bottom of the * plan tree (see allpaths.c), but target inheritance has to be expanded at * the top. The reason is that for UPDATE, each target relation needs a * different targetlist matching its own column set. Also, for both UPDATE * and DELETE, the executor needs the Append plan node at the top, else it * can't keep track of which table is the current target table. Fortunately, * the UPDATE/DELETE target can never be the nullable side of an outer join, * so it's OK to generate the plan this way. * * Returns a query plan. */static Plan *inheritance_planner(PlannerInfo *root){ Query *parse = root->parse; int parentRTindex = parse->resultRelation; List *subplans = NIL; List *resultRelations = NIL; List *returningLists = NIL; List *rtable = NIL; List *tlist = NIL; PlannerInfo subroot; ListCell *l; foreach(l, root->append_rel_list) { AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(l); Plan *subplan; /* append_rel_list contains all append rels; ignore others */ if (appinfo->parent_relid != parentRTindex) continue; /* * Generate modified query with this rel as target. We have to be * prepared to translate varnos in in_info_list as well as in the * Query proper. */ memcpy(&subroot, root, sizeof(PlannerInfo)); subroot.parse = (Query *) adjust_appendrel_attrs((Node *) parse, appinfo); subroot.in_info_list = (List *) adjust_appendrel_attrs((Node *) root->in_info_list, appinfo); subroot.init_plans = NIL; /* There shouldn't be any OJ info to translate, as yet */ Assert(subroot.oj_info_list == NIL); /* Generate plan */ subplan = grouping_planner(&subroot, 0.0 /* retrieve all tuples */ ); /* * If this child rel was excluded by constraint exclusion, exclude it * from the plan. */ if (is_dummy_plan(subplan)) continue; /* Save rtable and tlist from first rel for use below */ if (subplans == NIL) { rtable = subroot.parse->rtable; tlist = subplan->targetlist; } subplans = lappend(subplans, subplan); /* Make sure any initplans from this rel get into the outer list */ root->init_plans = list_concat(root->init_plans, subroot.init_plans); /* Build target-relations list for the executor */ resultRelations = lappend_int(resultRelations, appinfo->child_relid); /* Build list of per-relation RETURNING targetlists */ if (parse->returningList) { Assert(list_length(subroot.returningLists) == 1); returningLists = list_concat(returningLists, subroot.returningLists); } } root->resultRelations = resultRelations; root->returningLists = returningLists; /* Mark result as unordered (probably unnecessary) */ root->query_pathkeys = NIL; /* * If we managed to exclude every child rel, return a dummy plan */ if (subplans == NIL) { root->resultRelations = list_make1_int(parentRTindex); /* although dummy, it must have a valid tlist for executor */ tlist = preprocess_targetlist(root, parse->targetList); return (Plan *) make_result(root, tlist, (Node *) list_make1(makeBoolConst(false, false)), NULL); } /* * Planning might have modified the rangetable, due to changes of the * Query structures inside subquery RTEs. We have to ensure that this * gets propagated back to the master copy. But can't do this until we * are done planning, because all the calls to grouping_planner need * virgin sub-Queries to work from. (We are effectively assuming that * sub-Queries will get planned identically each time, or at least that * the impacts on their rangetables will be the same each time.) * * XXX should clean this up someday */ parse->rtable = rtable; /* Suppress Append if there's only one surviving child rel */ if (list_length(subplans) == 1) return (Plan *) linitial(subplans); return (Plan *) make_append(subplans, true, tlist);}/*-------------------- * grouping_planner * Perform planning steps related to grouping, aggregation, etc. * This primarily means adding top-level processing to the basic * query plan produced by query_planner. * * tuple_fraction is the fraction of tuples we expect will be retrieved * * tuple_fraction is interpreted as follows: * 0: expect all tuples to be retrieved (normal case) * 0 < tuple_fraction < 1: expect the given fraction of tuples available * from the plan to be retrieved * tuple_fraction >= 1: tuple_fraction is the absolute number of tuples * expected to be retrieved (ie, a LIMIT specification) * * Returns a query plan. Also, root->query_pathkeys is returned as the * actual output ordering of the plan (in pathkey format). *-------------------- */static Plan *grouping_planner(PlannerInfo *root, double tuple_fraction){ Query *parse = root->parse; List *tlist = parse->targetList; int64 offset_est = 0; int64 count_est = 0; double limit_tuples = -1.0; Plan *result_plan; List *current_pathkeys; List *sort_pathkeys; double dNumGroups = 0; /* Tweak caller-supplied tuple_fraction if have LIMIT/OFFSET */ if (parse->limitCount || parse->limitOffset) { tuple_fraction = preprocess_limit(root, tuple_fraction, &offset_est, &count_est); /* * If we have a known LIMIT, and don't have an unknown OFFSET, we can * estimate the effects of using a bounded sort. */ if (count_est > 0 && offset_est >= 0) limit_tuples = (double) count_est + (double) offset_est; } if (parse->setOperations) { List *set_sortclauses; /* * If there's a top-level ORDER BY, assume we have to fetch all the * tuples. This might seem too simplistic given all the hackery below * to possibly avoid the sort ... but a nonzero tuple_fraction is only * of use to plan_set_operations() when the setop is UNION ALL, and * the result of UNION ALL is always unsorted. */ if (parse->sortClause) tuple_fraction = 0.0; /* * Construct the plan for set operations. The result will not need * any work except perhaps a top-level sort and/or LIMIT. */ result_plan = plan_set_operations(root, tuple_fraction, &set_sortclauses); /* * Calculate pathkeys representing the sort order (if any) of the set * operation's result. We have to do this before overwriting the sort * key information... */ current_pathkeys = make_pathkeys_for_sortclauses(root, set_sortclauses, result_plan->targetlist, true); /* * We should not need to call preprocess_targetlist, since we must be * in a SELECT query node. Instead, use the targetlist returned by * plan_set_operations (since this tells whether it returned any * resjunk columns!), and transfer any sort key information from the * original tlist. */ Assert(parse->commandType == CMD_SELECT); tlist = postprocess_setop_tlist(result_plan->targetlist, tlist); /* * Can't handle FOR UPDATE/SHARE here (parser should have checked * already, but let's make sure). */ if (parse->rowMarks) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("SELECT FOR UPDATE/SHARE is not allowed with UNION/INTERSECT/EXCEPT"))); /* * Calculate pathkeys that represent result ordering requirements */ sort_pathkeys = make_pathkeys_for_sortclauses(root, parse->sortClause, tlist, true); } else { /* No set operations, do regular planning */ List *sub_tlist; List *group_pathkeys; AttrNumber *groupColIdx = NULL; Oid *groupOperators = NULL; bool need_tlist_eval = true; QualCost tlist_cost; Path *cheapest_path; Path *sorted_path; Path *best_path; long numGroups = 0; AggClauseCounts agg_counts; int numGroupCols = list_length(parse->groupClause); bool use_hashed_grouping = false; MemSet(&agg_counts, 0, sizeof(AggClauseCounts)); /* Preprocess targetlist */ tlist = preprocess_targetlist(root, tlist); /* * Generate appropriate target list for subplan; may be different from * tlist if grouping or aggregation is needed. */ sub_tlist = make_subplanTargetList(root, tlist, &groupColIdx, &need_tlist_eval); /* * Calculate pathkeys that represent grouping/ordering requirements. * Stash them in PlannerInfo so that query_planner can canonicalize * them after EquivalenceClasses have been formed. */ root->group_pathkeys = make_pathkeys_for_sortclauses(root, parse->groupClause, tlist, false); root->sort_pathkeys = make_pathkeys_for_sortclauses(root, parse->sortClause, tlist, false); /* * Will need actual number of aggregates for estimating costs. * * Note: we do not attempt to detect duplicate aggregates here; a * somewhat-overestimated count is okay for our present purposes. * * Note: think not that we can turn off hasAggs if we find no aggs. It * is possible for constant-expression simplification to remove all * explicit references to aggs, but we still have to follow the * aggregate semantics (eg, producing only one output row). */ if (parse->hasAggs) { count_agg_clauses((Node *) tlist, &agg_counts); count_agg_clauses(parse->havingQual, &agg_counts); } /* * Figure out whether we need a sorted result from query_planner. * * If we have a GROUP BY clause, then we want a result sorted properly * for grouping. Otherwise, if there is an ORDER BY clause, we want * to sort by the ORDER BY clause. (Note: if we have both, and ORDER * BY is a superset of GROUP BY, it would be tempting to request sort * by ORDER BY --- but that might just leave us failing to exploit an * available sort order at all. Needs more thought...) */ if (parse->groupClause) root->query_pathkeys = root->group_pathkeys; else if (parse->sortClause) root->query_pathkeys = root->sort_pathkeys; else root->query_pathkeys = NIL; /* * Generate the best unsorted and presorted paths for this Query (but * note there may not be any presorted path). query_planner will also * estimate the number of groups in the query, and canonicalize all * the pathkeys. */ query_planner(root, sub_tlist, tuple_fraction, limit_tuples, &cheapest_path, &sorted_path, &dNumGroups); group_pathkeys = root->group_pathkeys; sort_pathkeys = root->sort_pathkeys; /* * If grouping, extract the grouping operators and decide whether we * want to use hashed grouping. */ if (parse->groupClause) { groupOperators = extract_grouping_ops(parse->groupClause); use_hashed_grouping =
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?