PostgreSQL在何处处理 sql查询之四十八
接着,分析:
/*-------------------- * subquery_planner * Invokes the planner on a subquery. We recurse to here for each * sub-SELECT found in the query tree. * * glob is the global state for the current planner run. * parse is the querytree produced by the parser & rewriter. * parent_root is the immediate parent Query's info (NULL at the top level). * hasRecursion is true if this is a recursive WITH query. * tuple_fraction is the fraction of tuples we expect will be retrieved. * tuple_fraction is interpreted as explained for grouping_planner, below. * * If subroot isn't NULL, we pass back the query's final PlannerInfo struct; * among other things this tells the output sort ordering of the plan. * * Basically, this routine does the stuff that should only be done once * per Query object. It then calls grouping_planner. At one time, * grouping_planner could be invoked recursively on the same Query object; * that's not currently true, but we keep the separation between the two * routines anyway, in case we need it again someday. * * subquery_planner will be called recursively to handle sub-Query nodes * found within the query's expressions and rangetable. * * Returns a query plan. *-------------------- */ Plan * subquery_planner(PlannerGlobal *glob, Query *parse, PlannerInfo *parent_root, bool hasRecursion, double tuple_fraction, PlannerInfo **subroot) { int num_old_subplans = list_length(glob->subplans); PlannerInfo *root; Plan *plan; List *newHaving; bool hasOuterJoins; ListCell *l; /* Create a PlannerInfo data structure for this subquery */ root = makeNode(PlannerInfo); root->parse = parse; root->glob = glob; root->query_level = parent_root ? parent_root->query_level + 1 : 1; root->parent_root = parent_root; root->plan_params = NIL; root->planner_cxt = CurrentMemoryContext; root->init_plans = NIL; root->cte_plan_ids = NIL; root->eq_classes = NIL; root->append_rel_list = NIL; root->rowMarks = NIL; root->hasInheritedTarget = false; root->hasRecursion = hasRecursion; if (hasRecursion) root->wt_param_id = SS_assign_special_param(root); else root->wt_param_id = -1; root->non_recursive_plan = NULL; /* * If there is a WITH list, process each WITH query and build an initplan * SubPlan structure for it. */ if (parse->cteList) SS_process_ctes(root); /* * Look for ANY and EXISTS SubLinks in WHERE and JOIN/ON clauses, and try * to transform them into joins. Note that this step does not descend * into subqueries; if we pull up any subqueries below, their SubLinks are * processed just before pulling them up. */ if (parse->hasSubLinks) pull_up_sublinks(root); /* * Scan the rangetable for set-returning functions, and inline them if * possible (producing subqueries that might get pulled up next). * Recursion issues here are handled in the same way as for SubLinks. */ inline_set_returning_functions(root); /* * Check to see if any subqueries in the jointree can be merged into this * query. */ parse->jointree = (FromExpr *) pull_up_subqueries(root, (Node *) parse->jointree, NULL, NULL); /* * If this is a simple UNION ALL query, flatten it into an appendrel. We * do this now because it requires applying pull_up_subqueries to the leaf * queries of the UNION ALL, which weren't touched above because they * weren't referenced by the jointree (they will be after we do this). */ if (parse->setOperations) flatten_simple_union_all(root); /* * Detect whether any rangetable entries are RTE_JOIN kind; if not, we can * avoid the expense of doing flatten_join_alias_vars(). Also check for * outer joins --- if none, we can skip reduce_outer_joins(). This must be * done after we have done pull_up_subqueries, of course. */ root->hasJoinRTEs = false; hasOuterJoins = false; foreach(l, parse->rtable) { RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); if (rte->rtekind == RTE_JOIN) { root->hasJoinRTEs = true; if (IS_OUTER_JOIN(rte->jointype)) { hasOuterJoins = true; /* Can quit scanning once we find an outer join */ break; } } } /* * Preprocess RowMark information. We need to do this after subquery * pullup (so that all non-inherited RTEs are present) and before * inheritance expansion (so that the info is available for * expand_inherited_tables to examine and modify). */ preprocess_rowmarks(root); /* * Expand any rangetable entries that are inheritance sets into "append * relations". This can add entries to the rangetable, but they must be * plain base relations not joins, so it's OK (and marginally more * efficient) to do it after checking for join RTEs. We must do it after * pulling up subqueries, else we'd fail to handle inherited tables in * subqueries. */ expand_inherited_tables(root); /* * Set hasHavingQual to remember if HAVING clause is present. Needed * because preprocess_expression will reduce a constant-true condition to * an empty qual list ... but "HAVING TRUE" is not a semantic no-op. */ root->hasHavingQual = (parse->havingQual != NULL); /* Clear this flag; might get set in distribute_qual_to_rels */ root->hasPseudoConstantQuals = false; /* * Do expression preprocessing on targetlist and quals, as well as other * random expressions in the querytree. Note that we do not need to * handle sort/group expressions explicitly, because they are actually * part of the targetlist. */ parse->targetList = (List *) preprocess_expression(root, (Node *) parse->targetList, EXPRKIND_TARGET); parse->returningList = (List *) preprocess_expression(root, (Node *) parse->returningList, EXPRKIND_TARGET); preprocess_qual_conditions(root, (Node *) parse->jointree); parse->havingQual = preprocess_expression(root, parse->havingQual, EXPRKIND_QUAL); foreach(l, parse->windowClause) { WindowClause *wc = (WindowClause *) lfirst(l); /* partitionClause/orderClause are sort/group expressions */ wc->startOffset = preprocess_expression(root, wc->startOffset, EXPRKIND_LIMIT); wc->endOffset = preprocess_expression(root, wc->endOffset, EXPRKIND_LIMIT); } parse->limitOffset = preprocess_expression(root, parse->limitOffset, EXPRKIND_LIMIT); parse->limitCount = preprocess_expression(root, parse->limitCount, EXPRKIND_LIMIT); root->append_rel_list = (List *) preprocess_expression(root, (Node *) root->append_rel_list, EXPRKIND_APPINFO); /* Also need to preprocess expressions for function and values RTEs */ foreach(l, parse->rtable) { RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); if (rte->rtekind == RTE_FUNCTION) rte->funcexpr = preprocess_expression(root, rte->funcexpr, EXPRKIND_RTFUNC); else if (rte->rtekind == RTE_VALUES) rte->values_lists = (List *) preprocess_expression(root, (Node *) rte->values_lists, EXPRKIND_VALUES); } /* * In some cases we may want to transfer a HAVING clause into WHERE. We * cannot do so if the HAVING clause contains aggregates (obviously) or * volatile functions (since a HAVING clause is supposed to be executed * only once per group). Also, it may be that the clause is so expensive * to execute that we're better off doing it only once per group, despite * the loss of selectivity. This is hard to estimate short of doing the * entire planning process twice, so we use a heuristic: clauses * containing subplans are left in HAVING. Otherwise, we move or copy the * HAVING clause into WHERE, in hopes of eliminating tuples before * aggregation instead of after. * * If the query has explicit grouping then we can simply move such a * clause into WHERE; any group that fails the clause will not be in the * output because none of its tuples will reach the grouping or * aggregation stage. Otherwise we must have a degenerate (variable-free) * HAVING clause, which we put in WHERE so that query_planner() can use it * in a gating Result node, but also keep in HAVING to ensure that we * don't emit a bogus aggregated row. (This could be done better, but it * seems not worth optimizing.) * * Note that both havingQual and parse->jointree->quals are in * implicitly-ANDed-list form at this point, even though they are declared * as Node *. */ newHaving = NIL; foreach(l, (List *) parse->havingQual) { Node *havingclause = (Node *) lfirst(l); if (contain_agg_clause(havingclause) || contain_volatile_functions(havingclause) || contain_subplans(havingclause)) { /* keep it in HAVING */ newHaving = lappend(newHaving, havingclause); } else if (parse->groupClause) { /* move it to WHERE */ parse->jointree->quals = (Node *) lappend((List *) parse->jointree->quals, havingclause); } else { /* put a copy in WHERE, keep it in HAVING */ parse->jointree->quals = (Node *) lappend((List *) parse->jointree->quals, copyObject(havingclause)); newHaving = lappend(newHaving, havingclause); } } parse->havingQual = (Node *) newHaving; /* * If we have any outer joins, try to reduce them to plain inner joins. * This step is most easily done after we've done expression * preprocessing. */ if (hasOuterJoins) reduce_outer_joins(root); /* * Do the main planning. If we have an inherited target relation, that * needs special processing, else go straight to grouping_planner. */ if (parse->resultRelation && rt_fetch(parse->resultRelation, parse->rtable)->inh) plan = inheritance_planner(root); else { plan = grouping_planner(root, tuple_fraction); /* If it's not SELECT, we need a ModifyTable node */ if (parse->commandType != CMD_SELECT) { List *returningLists; List *rowMarks; /* * Set up the RETURNING list-of-lists, if needed. */ if (parse->returningList) returningLists = list_make1(parse->returningList); else returningLists = NIL; /* * If there was a FOR UPDATE/SHARE clause, the LockRows node will * have dealt with fetching non-locked marked rows, else we need * to have ModifyTable do that. */ if (parse->rowMarks) rowMarks = NIL; else rowMarks = root->rowMarks; plan = (Plan *) make_modifytable(parse->commandType, parse->canSetTag, list_make1_int(parse->resultRelation), list_make1(plan), returningLists, rowMarks, SS_assign_special_param(root)); } } /* * If any subplans were generated, or if there are any parameters to worry * about, build initPlan list and extParam/allParam sets for plan nodes, * and attach the initPlans to the top plan node. */ if (list_length(glob->subplans) != num_old_subplans || root->glob->nParamExec > 0) SS_finalize_plan(root, plan, true); /* Return internal info if caller wants it */ if (subroot) *subroot = root; return plan; }
进行分析,看这一句的状况:
int num_old_subplans = list_length(glob->subplans);
对于这个 num_old_subplans ,在我的简单查询sql 文执行的时候,都是 0:
Plan * subquery_planner(PlannerGlobal *glob, Query *parse, PlannerInfo *parent_root, bool hasRecursion, double tuple_fraction, PlannerInfo **subroot) { int num_old_subplans = list_length(glob->subplans); ... if (list_length(glob->subplans) != num_old_subplans || root->glob->nParamExec > 0) SS_finalize_plan(root, plan, true); ... }
而且,subquery_planner 只被调用了一次,root->query_level 值为1(入口参数为 NULL)。
/* primary planning entry point (may recurse for subqueries) */ top_plan = subquery_planner(glob, parse, NULL, false, tuple_fraction, &root);
下面:
因为入口参数的原因,这个也是false。
root->hasRecursion = hasRecursion; if (hasRecursion) root->wt_param_id = SS_assign_special_param(root); else root->wt_param_id = -1;
下面这段:也是 false。因为我没有使用 WITH
/* * If there is a WITH list, process each WITH query and build an initplan * SubPlan structure for it. */ if (parse->cteList) SS_process_ctes(root);
对于我的简单查询,这一段也是false。
/* * Look for ANY and EXISTS SubLinks in WHERE and JOIN/ON clauses, and try * to transform them into joins. Note that this step does not descend * into subqueries; if we pull up any subqueries below, their SubLinks are * processed just before pulling them up. */ if (parse->hasSubLinks) pull_up_sublinks(root);
再看这句话的功能:
/* * Scan the rangetable for set-returning functions, and inline them if * possible (producing subqueries that might get pulled up next). * Recursion issues here are handled in the same way as for SubLinks. */ inline_set_returning_functions(root);
展开后:
/* * inline_set_returning_functions * Attempt to "inline" set-returning functions in the FROM clause. * * If an RTE_FUNCTION rtable entry invokes a set-returning function that * contains just a simple SELECT, we can convert the rtable entry to an * RTE_SUBQUERY entry exposing the SELECT directly. This is especially * useful if the subquery can then be "pulled up" for further optimization, * but we do it even if not, to reduce executor overhead. * * This has to be done before we have started to do any optimization of * subqueries, else any such steps wouldn't get applied to subqueries * obtained via inlining. However, we do it after pull_up_sublinks * so that we can inline any functions used in SubLink subselects. * * Like most of the planner, this feels free to scribble on its input data * structure. */ void inline_set_returning_functions(PlannerInfo *root) { 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; } } } }
因为我的简单查询中没有集合运算,所以这里面 inline_set_returning_functions 相当于什么都没作。
下面这一段,对我的简单查询,也可以无视:
/* * Check to see if any subqueries in the jointree can be merged into this * query. */ parse->jointree = (FromExpr *) pull_up_subqueries(root, (Node *) parse->jointree, NULL, NULL);
下面这一段,其实也可以忽略:
/* * If this is a simple UNION ALL query, flatten it into an appendrel. We * do this now because it requires applying pull_up_subqueries to the leaf * queries of the UNION ALL, which weren't touched above because they * weren't referenced by the jointree (they will be after we do this). */ if (parse->setOperations) flatten_simple_union_all(root);
然后,可以看下面的:
对于我们的简单查询,这个 (rte->rtekind == RTE_JOIN) 也是不能成立的。
/* * Detect whether any rangetable entries are RTE_JOIN kind; if not, we can * avoid the expense of doing flatten_join_alias_vars(). Also check for * outer joins --- if none, we can skip reduce_outer_joins(). This must be * done after we have done pull_up_subqueries, of course. */ root->hasJoinRTEs = false; hasOuterJoins = false; foreach(l, parse->rtable) { RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); if (rte->rtekind == RTE_JOIN) { root->hasJoinRTEs = true; if (IS_OUTER_JOIN(rte->jointype)) { hasOuterJoins = true; /* Can quit scanning once we find an outer join */ break; } } }
接着分析:
/* * Preprocess RowMark information. We need to do this after subquery * pullup (so that all non-inherited RTEs are present) and before * inheritance expansion (so that the info is available for * expand_inherited_tables to examine and modify). */ preprocess_rowmarks(root);
接着看 preprocess_rowmarks,对于我的简单查询SQL文,其实是直接return 了。
/* * preprocess_rowmarks - set up PlanRowMarks if needed */ static void preprocess_rowmarks(PlannerInfo *root) { Query *parse = root->parse; Bitmapset *rels; List *prowmarks; ListCell *l; int i; if (parse->rowMarks) { fprintf(stderr,"parse->rowMarks is true.\n"); /* * We've got trouble if FOR UPDATE/SHARE 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. */ CheckSelectLocking(parse); } else { /**
fprintf(stderr,"parse->rowMarks is false.\n"); if (parse->commandType != CMD_UPDATE && parse->commandType != CMD_DELETE) fprintf(stderr,"+_+_+_+_+_+_+_+_+_+_+directly return!\n"); */
/* * We only need rowmarks for UPDATE, DELETE, or FOR UPDATE/SHARE. */ if (parse->commandType != CMD_UPDATE && parse->commandType != CMD_DELETE) return; } ... }
再接着分析:
/* * Expand any rangetable entries that are inheritance sets into "append * relations". This can add entries to the rangetable, but they must be * plain base relations not joins, so it's OK (and marginally more * efficient) to do it after checking for join RTEs. We must do it after * pulling up subqueries, else we'd fail to handle inherited tables in * subqueries. */ expand_inherited_tables(root);
/* * expand_inherited_tables * Expand each rangetable entry that represents an inheritance set * into an "append relation". At the conclusion of this process, * the "inh" flag is set in all and only those RTEs that are append * relation parents. */ void expand_inherited_tables(PlannerInfo *root) { 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); } }
这个 expand_inherited_rtentry 也是直接return了:
static void expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti) { Query *parse = root->parse; Oid parentOID; PlanRowMark *oldrc; Relation oldrelation; LOCKMODE lockmode; List *inhOIDs; List *appinfos; ListCell *l; /* Does RT entry allow inheritance? */ if (!rte->inh) return; /* Ignore any already-expanded UNION ALL nodes */ if (rte->rtekind != RTE_RELATION) { Assert(rte->rtekind == RTE_SUBQUERY); return; } /* Fast path for common case of childless table */ parentOID = rte->relid; if (!has_subclass(parentOID)) { /* Clear flag before returning */ rte->inh = false; return; }
...
}