planner.c
来自「PostgreSQL7.4.6 for Linux」· C语言 代码 · 共 1,553 行 · 第 1/4 页
C
1,553 行
/* * If it's a qual or havingQual, canonicalize it, and convert it to * implicit-AND format. * * XXX Is there any value in re-applying eval_const_expressions after * canonicalize_qual? */ if (kind == EXPRKIND_QUAL) { expr = (Node *) canonicalize_qual((Expr *) expr, true);#ifdef OPTIMIZER_DEBUG printf("After canonicalize_qual()\n"); pprint(expr);#endif } /* Expand SubLinks to SubPlans */ if (parse->hasSubLinks) expr = SS_process_sublinks(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 */ if (PlannerQueryLevel > 1) expr = SS_replace_correlation_vars(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(Query *parse, Node *jtnode){ if (jtnode == NULL) return; if (IsA(jtnode, RangeTblRef)) { /* nothing to do here */ } else if (IsA(jtnode, FromExpr)) { FromExpr *f = (FromExpr *) jtnode; List *l; foreach(l, f->fromlist) preprocess_qual_conditions(parse, lfirst(l)); f->quals = preprocess_expression(parse, f->quals, EXPRKIND_QUAL); } else if (IsA(jtnode, JoinExpr)) { JoinExpr *j = (JoinExpr *) jtnode; preprocess_qual_conditions(parse, j->larg); preprocess_qual_conditions(parse, j->rarg); j->quals = preprocess_expression(parse, 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. (This is not so critical for DELETE, but for simplicity we treat * inherited DELETE the same way.) 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. * * parse is the querytree produced by the parser & rewriter. * inheritlist is an integer list of RT indexes for the result relation set. * * Returns a query plan. *-------------------- */static Plan *inheritance_planner(Query *parse, List *inheritlist){ int parentRTindex = parse->resultRelation; Oid parentOID = getrelid(parentRTindex, parse->rtable); int mainrtlength = length(parse->rtable); List *subplans = NIL; List *tlist = NIL; List *l; foreach(l, inheritlist) { int childRTindex = lfirsti(l); Oid childOID = getrelid(childRTindex, parse->rtable); int subrtlength; Query *subquery; Plan *subplan; /* Generate modified query with this rel as target */ subquery = (Query *) adjust_inherited_attrs((Node *) parse, parentRTindex, parentOID, childRTindex, childOID); /* Generate plan */ subplan = grouping_planner(subquery, 0.0 /* retrieve all tuples */ ); subplans = lappend(subplans, subplan); /* * It's possible that additional RTEs got added to the rangetable * due to expansion of inherited source tables (see allpaths.c). * If so, we must copy 'em back to the main parse tree's rtable. * * XXX my goodness this is ugly. Really need to think about ways to * rein in planner's habit of scribbling on its input. */ subrtlength = length(subquery->rtable); if (subrtlength > mainrtlength) { List *subrt = subquery->rtable; while (mainrtlength-- > 0) /* wish we had nthcdr() */ subrt = lnext(subrt); parse->rtable = nconc(parse->rtable, subrt); mainrtlength = subrtlength; } /* Save preprocessed tlist from first rel for use in Append */ if (tlist == NIL) tlist = subplan->targetlist; } /* Save the target-relations list for the executor, too */ parse->resultRelations = inheritlist; /* Mark result as unordered (probably unnecessary) */ parse->query_pathkeys = NIL; 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. * * parse is the querytree produced by the parser & rewriter. * 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, parse->query_pathkeys is returned as the * actual output ordering of the plan (in pathkey format). *-------------------- */static Plan *grouping_planner(Query *parse, double tuple_fraction){ List *tlist = parse->targetList; Plan *result_plan; List *current_pathkeys; List *sort_pathkeys; if (parse->setOperations) { /* * 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(parse); /* * 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 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 is not allowed with UNION/INTERSECT/EXCEPT"))); /* * We set current_pathkeys NIL indicating we do not know sort * order. This is correct when the top set operation is UNION * ALL, since the appended-together results are unsorted even if * the subplans were sorted. For other set operations we could be * smarter --- room for future improvement! */ current_pathkeys = NIL; /* * Calculate pathkeys that represent ordering requirements */ sort_pathkeys = make_pathkeys_for_sortclauses(parse->sortClause, tlist); sort_pathkeys = canonicalize_pathkeys(parse, sort_pathkeys); } else { /* No set operations, do regular planning */ List *sub_tlist; List *group_pathkeys; AttrNumber *groupColIdx = NULL; bool need_tlist_eval = true; QualCost tlist_cost; double sub_tuple_fraction; Path *cheapest_path; Path *sorted_path; double dNumGroups = 0; long numGroups = 0; int numAggs = 0; int numGroupCols = length(parse->groupClause); bool use_hashed_grouping = false; /* Preprocess targetlist in case we are inside an INSERT/UPDATE. */ tlist = preprocess_targetlist(tlist, parse->commandType, parse->resultRelation, parse->rtable); /* * Add TID targets for rels selected FOR UPDATE (should this be * done in preprocess_targetlist?). The executor uses the TID to * know which rows to lock, much as for UPDATE or DELETE. */ if (parse->rowMarks) { List *l; /* * We've got trouble if the FOR UPDATE appears inside * grouping, since grouping renders a reference to individual * tuple CTIDs invalid. This is also checked at parse time, * but that's insufficient because of rule substitution, query * pullup, etc. */ CheckSelectForUpdate(parse); /* * Currently the executor only supports FOR UPDATE at top * level */ if (PlannerQueryLevel > 1) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("SELECT FOR UPDATE is not allowed in subqueries"))); foreach(l, parse->rowMarks) { Index rti = lfirsti(l); char *resname; Resdom *resdom; Var *var; TargetEntry *ctid; resname = (char *) palloc(32); snprintf(resname, 32, "ctid%u", rti); resdom = makeResdom(length(tlist) + 1, TIDOID, -1, resname, true); var = makeVar(rti, SelfItemPointerAttributeNumber, TIDOID, -1, 0); ctid = makeTargetEntry(resdom, (Expr *) var); tlist = lappend(tlist, ctid); } } /* * Generate appropriate target list for subplan; may be different * from tlist if grouping or aggregation is needed. */ sub_tlist = make_subplanTargetList(parse, tlist, &groupColIdx, &need_tlist_eval); /* * Calculate pathkeys that represent grouping/ordering * requirements */ group_pathkeys = make_pathkeys_for_sortclauses(parse->groupClause, tlist); sort_pathkeys = make_pathkeys_for_sortclauses(parse->sortClause, tlist); /* * 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) numAggs = count_agg_clause((Node *) tlist) + count_agg_clause(parse->havingQual); /* * 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) parse->query_pathkeys = group_pathkeys; else if (parse->sortClause) parse->query_pathkeys = sort_pathkeys; else parse->query_pathkeys = NIL; /* * Adjust tuple_fraction if we see that we are going to apply * limiting/grouping/aggregation/etc. This is not overridable by * the caller, since it reflects plan actions that this routine * will certainly take, not assumptions about context. */ if (parse->limitCount != NULL) { /* * A LIMIT clause limits the absolute number of tuples * returned. However, if it's not a constant LIMIT then we * have to punt; for lack of a better idea, assume 10% of the * plan's result is wanted. */ double limit_fraction = 0.0; if (IsA(parse->limitCount, Const)) { Const *limitc = (Const *) parse->limitCount; int32 count = DatumGetInt32(limitc->constvalue); /* * A NULL-constant LIMIT represents "LIMIT ALL", which we * treat the same as no limit (ie, expect to retrieve all * the tuples). */ if (!limitc->constisnull && count > 0) { limit_fraction = (double) count; /* We must also consider the OFFSET, if present */ if (parse->limitOffset != NULL) { if (IsA(parse->limitOffset, Const)) { int32 offset; limitc = (Const *) parse->limitOffset; offset = DatumGetInt32(limitc->constvalue); if (!limitc->constisnull && offset > 0) limit_fraction += (double) offset; }
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?