再次上溯:

/*--------------------* 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);elseroot->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);elsereturningLists = 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;elserowMarks = 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;
}

上溯

PlannedStmt *
standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
{PlannedStmt *result;PlannerGlobal *glob;double        tuple_fraction;PlannerInfo *root;Plan       *top_plan;ListCell   *lp,*lr;/* Cursor options may come from caller or from DECLARE CURSOR stmt */if (parse->utilityStmt &&IsA(parse->utilityStmt, DeclareCursorStmt))cursorOptions |= ((DeclareCursorStmt *) parse->utilityStmt)->options;/** Set up global state for this planner invocation.  This data is needed* across all levels of sub-Query that might exist in the given command,* so we keep it in a separate struct that's linked to by each per-Query* PlannerInfo.*/glob = makeNode(PlannerGlobal);glob->boundParams = boundParams;glob->subplans = NIL;glob->subroots = NIL;glob->rewindPlanIDs = NULL;glob->finalrtable = NIL;glob->finalrowmarks = NIL;glob->resultRelations = NIL;glob->relationOids = NIL;glob->invalItems = NIL;glob->nParamExec = 0;glob->lastPHId = 0;glob->lastRowMarkId = 0;glob->transientPlan = false;/* Determine what fraction of the plan is likely to be scanned */if (cursorOptions & CURSOR_OPT_FAST_PLAN){/** We have no real idea how many tuples the user will ultimately FETCH* from a cursor, but it is often the case that he doesn't want 'em* all, or would prefer a fast-start plan anyway so that he can* process some of the tuples sooner.  Use a GUC parameter to decide* what fraction to optimize for.*/tuple_fraction = cursor_tuple_fraction;/** We document cursor_tuple_fraction as simply being a fraction, which* means the edge cases 0 and 1 have to be treated specially here.    We* convert 1 to 0 ("all the tuples") and 0 to a very small fraction.*/if (tuple_fraction >= 1.0)tuple_fraction = 0.0;else if (tuple_fraction <= 0.0)tuple_fraction = 1e-10;}else{/* Default assumption is we need all the tuples */tuple_fraction = 0.0;}/* primary planning entry point (may recurse for subqueries) */top_plan = subquery_planner(glob, parse, NULL,false, tuple_fraction, &root);/** If creating a plan for a scrollable cursor, make sure it can run* backwards on demand.  Add a Material node at the top at need.*/if (cursorOptions & CURSOR_OPT_SCROLL){if (!ExecSupportsBackwardScan(top_plan))top_plan = materialize_finished_plan(top_plan);}/* final cleanup of the plan */Assert(glob->finalrtable == NIL);Assert(glob->finalrowmarks == NIL);Assert(glob->resultRelations == NIL);top_plan = set_plan_references(root, top_plan);/* ... and the subplans (both regular subplans and initplans) */Assert(list_length(glob->subplans) == list_length(glob->subroots));forboth(lp, glob->subplans, lr, glob->subroots){Plan       *subplan = (Plan *) lfirst(lp);PlannerInfo *subroot = (PlannerInfo *) lfirst(lr);lfirst(lp) = set_plan_references(subroot, subplan);}/* build the PlannedStmt result */result = makeNode(PlannedStmt);result->commandType = parse->commandType;result->queryId = parse->queryId;result->hasReturning = (parse->returningList != NIL);result->hasModifyingCTE = parse->hasModifyingCTE;result->canSetTag = parse->canSetTag;result->transientPlan = glob->transientPlan;result->planTree = top_plan;result->rtable = glob->finalrtable;result->resultRelations = glob->resultRelations;result->utilityStmt = parse->utilityStmt;result->subplans = glob->subplans;result->rewindPlanIDs = glob->rewindPlanIDs;result->rowMarks = glob->finalrowmarks;result->relationOids = glob->relationOids;result->invalItems = glob->invalItems;result->nParamExec = glob->nParamExec;return result;
}

再上溯:

/*******************************************************************************       Query optimizer entry point** To support loadable plugins that monitor or modify planner behavior,* we provide a hook variable that lets a plugin get control before and* after the standard planning process.  The plugin would normally call* standard_planner().** Note to plugin authors: standard_planner() scribbles on its Query input,* so you'd better copy that data structure if you want to plan more than once.******************************************************************************/
PlannedStmt *
planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
{PlannedStmt *result;if (planner_hook)result = (*planner_hook) (parse, cursorOptions, boundParams);elseresult = standard_planner(parse, cursorOptions, boundParams);return result;
}

对 standard_planner 分析:

/** Query -*      Parse analysis turns all statements into a Query tree*      for further processing by the rewriter and planner.**      Utility statements (i.e. non-optimizable statements) have the*      utilityStmt field set, and the Query itself is mostly dummy.*      DECLARE CURSOR is a special case: it is represented like a SELECT,*      but the original DeclareCursorStmt is stored in utilityStmt.**      Planning converts a Query tree into a Plan tree headed by a PlannedStmt*      node --- the Query structure is not used by the executor.*/
typedef struct Query
{NodeTag        type;CmdType        commandType;    /* select|insert|update|delete|utility */QuerySource querySource;    /* where did I come from? */uint32        queryId;        /* query identifier (can be set by plugins) */bool        canSetTag;        /* do I set the command result tag? */Node       *utilityStmt;    /* non-null if this is DECLARE CURSOR or a* non-optimizable statement */int            resultRelation; /* rtable index of target relation for* INSERT/UPDATE/DELETE; 0 for SELECT */bool        hasAggs;        /* has aggregates in tlist or havingQual */bool        hasWindowFuncs; /* has window functions in tlist */bool        hasSubLinks;    /* has subquery SubLink */bool        hasDistinctOn;    /* distinctClause is from DISTINCT ON */bool        hasRecursive;    /* WITH RECURSIVE was specified */bool        hasModifyingCTE;    /* has INSERT/UPDATE/DELETE in WITH */bool        hasForUpdate;    /* FOR UPDATE or FOR SHARE was specified */List       *cteList;        /* WITH list (of CommonTableExpr's) */List       *rtable;            /* list of range table entries */FromExpr   *jointree;        /* table join tree (FROM and WHERE clauses) */List       *targetList;        /* target list (of TargetEntry) */List       *returningList;    /* return-values list (of TargetEntry) */List       *groupClause;    /* a list of SortGroupClause's */Node       *havingQual;        /* qualifications applied to groups */List       *windowClause;    /* a list of WindowClause's */List       *distinctClause; /* a list of SortGroupClause's */List       *sortClause;        /* a list of SortGroupClause's */Node       *limitOffset;    /* # of result tuples to skip (int8 expr) */Node       *limitCount;        /* # of result tuples to return (int8 expr) */List       *rowMarks;        /* a list of RowMarkClause's */Node       *setOperations;    /* set-operation tree if this is top level of* a UNION/INTERSECT/EXCEPT query */List       *constraintDeps; /* a list of pg_constraint OIDs that the query* depends on to be semantically valid */
} Query;

一点一点地分析吧:

    if (parse->utilityStmt &&IsA(parse->utilityStmt, DeclareCursorStmt))cursorOptions |= ((DeclareCursorStmt *) parse->utilityStmt)->options;

parse->utilityStmt 是false,所以不成立。

再接着:

/** Set up global state for this planner invocation.  This data is needed* across all levels of sub-Query that might exist in the given command,* so we keep it in a separate struct that's linked to by each per-Query* PlannerInfo.*/glob = makeNode(PlannerGlobal);glob->boundParams = boundParams;glob->subplans = NIL;glob->subroots = NIL;glob->rewindPlanIDs = NULL;glob->finalrtable = NIL;glob->finalrowmarks = NIL;glob->resultRelations = NIL;glob->relationOids = NIL;glob->invalItems = NIL;glob->nParamExec = 0;glob->lastPHId = 0;glob->lastRowMarkId = 0;glob->transientPlan = false;

这一段只是设置了一个初始化好的 PlannerGlobal 指针。

#define newNode(size, tag) \
( \AssertMacro((size) >= sizeof(Node)),        /* need the tag, at least */ \newNodeMacroHolder = (Node *) palloc0fast(size), \newNodeMacroHolder->type = (tag), \newNodeMacroHolder \
)
#endif   /* __GNUC__ */#define makeNode(_type_)        ((_type_ *) newNode(sizeof(_type_),T_##_type_))

接下来,对于我的SQL : select id, val from tst04 where id>1 ,

cursorOPtion 是false,所以不成立,变成:

if (cursorOptions & CURSOR_OPT_FAST_PLAN){...}else{/* Default assumption is we need all the tuples */tuple_fraction = 0.0;}

接下来:

    /* primary planning entry point (may recurse for subqueries) */top_plan = subquery_planner(glob, parse, NULL,false, tuple_fraction, &root);

转载于:https://www.cnblogs.com/gaojian/archive/2013/06/04/3116518.html

PostgreSQL在何处处理 sql查询之四十七相关推荐

  1. PostgreSQL在何处处理 sql查询之四十六

    接前面,再上溯:set_base_rel_pathlists --> set_rel_pathlist /** set_base_rel_pathlists* Finds all paths a ...

  2. PostgreSQL在何处处理 sql查询之四

    看看portal生成完毕后,干了什么(PortalDefineQuery): 1 /* 2 * Create unnamed portal to run the query or queries in ...

  3. PostgreSQL在何处处理 sql查询之五十一

    继续分析 query_planner: /** query_planner* Generate a path (that is, a simplified plan) for a basic quer ...

  4. PostgreSQL在何处处理 sql查询之五十二

    开始 /** Ready to do the primary planning.*/final_rel = make_one_rel(root, joinlist); 展开: /** make_one ...

  5. PostgreSQL在何处处理 sql查询之二十二

    接前面. 回到程序调用关系上来: estimate_rel_size -> RelationGetNumberOfBlocks->RelationGetNumberOfBlocksINFo ...

  6. PostgreSQL在何处处理 sql查询

    如果我开一个psql窗口,来输入sql文,它在数据库的何处被解析?在何处被"真正"处理? postgres.c 的 int PostgresMain(int argc, char ...

  7. PostgreSQL在何处处理 sql查询之五十四

    接前面,从 cheapeast_path 的角度,关注 query_planner 函数,对其进行简化: void query_planner(PlannerInfo *root, List *tli ...

  8. PostgreSQL在何处处理 sql查询之二

    在exec_simple_query中,代码如下: 1 /* 2 * exec_simple_query 3 * 4 * Execute a "simple Query" prot ...

  9. PostgreSQL在何处处理 sql查询之三十九

    接前面,这次重点分析 ExecScan: 其for 循环内部: for (;;){ TupleTableSlot *slot;CHECK_FOR_INTERRUPTS(); slot = ExecSc ...

最新文章

  1. 寒冬 winter:代码无捷径,只怕有心人
  2. 你所忽略的Linux系统安全及应用(最全面)
  3. 系统架构设计师教程学习随笔 (计算机与网络基础知识--操作系统基础知识)
  4. 【暑假训练 7.10】 codevs 2492 上帝造题的七分钟2
  5. K-means聚类 —— matlab
  6. 中英文对照 —— 标点符号(punctuation)
  7. 如何改变WINDOWS服务的启动顺序(Win2000)
  8. 凯撒密码(移位密码)
  9. 锐浪报表 Grid++Report uniGUI Web表格打印
  10. android x86应用兼容性,x86如何解决Android应用兼容性问题
  11. 计算机主机只有一块硬盘,电脑双硬盘只显示一个怎么办
  12. ElasticSearch:相关性评分原理及应用
  13. drcom linux最新版,Drcom-client.org 上线暨新版 PUM v1.0 发布
  14. YOLOV5:不懂代码也能使用YOLOV5跑项目
  15. 巴比特CEO王雷:未来几年会迎来区块链企业上市小高潮,或将诞生万亿级企业...
  16. 基于Visio的二次开发
  17. java虚拟机JVM--java虚拟机的结构
  18. 【日常训练】Help Far Away Kingdom(Codeforces 99A)
  19. 智慧医院系统定制|慢病管理系统更全面
  20. 【GNN】台大李弘毅助教讲解——GNN系列

热门文章

  1. React Native 项目常用第三方组件汇总
  2. 课堂练习----二维数组
  3. 【BZOJ-13962865】识别子串字符串识别 后缀自动机/后缀树组 + 线段树
  4. Android app内语言环境切换
  5. iOS点滴- ViewController详解
  6. 准备篇--串口通信概述
  7. Mini音乐播放器【简单版】(附源码)
  8. C#和javascript的简单交互
  9. Delphi绘制Alpha图像的函数
  10. 程序员日常工作中如何正确的偷懒?