共计 7874 个字符,预计需要花费 20 分钟才能阅读完成。
这篇文章主要讲解了“PostgreSQL 中 create_sort_plan 函数实现逻辑是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着丸趣 TV 小编的思路慢慢深入,一起来研究和学习“PostgreSQL 中 create_sort_plan 函数实现逻辑是什么”吧!
一、数据结构
Plan
所有计划节点通过将 Plan 结构作为第一个字段从 Plan 结构“派生”。这确保了在将节点转换为计划节点时,一切都能正常工作。(在执行器中以通用方式传递时,节点指针经常被转换为 Plan *)
/* ----------------
* Plan node
*
* All plan nodes derive from the Plan structure by having the
* Plan structure as the first field. This ensures that everything works
* when nodes are cast to Plan s. (node pointers are frequently cast to Plan*
* when passed around generically in the executor)
* 所有计划节点通过将 Plan 结构作为第一个字段从 Plan 结构“派生”。 * 这确保了在将节点转换为计划节点时,一切都能正常工作。 * (在执行器中以通用方式传递时,节点指针经常被转换为 Plan *)
*
* We never actually instantiate any Plan nodes; this is just the common
* abstract superclass for all Plan-type nodes.
* 从未实例化任何 Plan 节点; 这只是所有 Plan-type 节点的通用抽象超类。 * ----------------
*/
typedef struct Plan
NodeTag type;// 节点类型
/*
* 成本估算信息;estimated execution costs for plan (see costsize.c for more info)
*/
Cost startup_cost; /* 启动成本;cost expended before fetching any tuples */
Cost total_cost; /* 总成本;total cost (assuming all tuples fetched) */
/*
* 优化器估算信息;planner s estimate of result size of this plan step
*/
double plan_rows; /* 行数;number of rows plan is expected to emit */
int plan_width; /* 平均行大小 (Byte 为单位);average row width in bytes */
/*
* 并行执行相关的信息;information needed for parallel query
*/
bool parallel_aware; /* 是否参与并行执行逻辑?engage parallel-aware logic? */
bool parallel_safe; /* 是否并行安全;OK to use as part of parallel plan? */
/*
* Plan 类型节点通用的信息.Common structural data for all Plan types.
*/
int plan_node_id; /* unique across entire final plan tree */
List *targetlist; /* target list to be computed at this node */
List *qual; /* implicitly-ANDed qual conditions */
struct Plan *lefttree; /* input plan tree(s) */
struct Plan *righttree;
List *initPlan; /* Init Plan nodes (un-correlated expr
* subselects) */
/*
* Information for management of parameter-change-driven rescanning
* parameter-change-driven 重扫描的管理信息.
*
* extParam includes the paramIDs of all external PARAM_EXEC params
* affecting this plan node or its children. setParam params from the
* node s initPlans are not included, but their extParams are.
*
* allParam includes all the extParam paramIDs, plus the IDs of local
* params that affect the node (i.e., the setParams of its initplans).
* These are _all_ the PARAM_EXEC params that affect this node.
*/
Bitmapset *extParam;
Bitmapset *allParam;
} Plan;
二、源码解读
create_plan- create_plan_recurse- create_projection_plan 函数创建计划树, 执行投影操作并通过递归的方式为子访问路径生成执行计划。create_sort_plan 函数创建 Sort 计划节点。
//---------------------------------------------------------------- create_projection_plan
* create_projection_plan
*
* Create a plan tree to do a projection step and (recursively) plans
* for its subpaths. We may need a Result node for the projection,
* but sometimes we can just let the subplan do the work.
* 创建计划树, 执行投影操作并通过递归的方式为子访问路径生成执行计划.
* 一般来说需要一个 Result 节点用于投影操作, 但有时候可以让子计划执行此项任务.
*/
static Plan *
create_projection_plan(PlannerInfo *root, ProjectionPath *best_path, int flags)
Plan *plan;
Plan *subplan;
List *tlist;
bool needs_result_node = false;
/*
* Convert our subpath to a Plan and determine whether we need a Result
* node.
* 转换 subpath 为 Plan, 并确定是否需要 Result 节点.
*
* In most cases where we don t need to project, creation_projection_path
* will have set dummypp, but not always. First, some createplan.c
* routines change the tlists of their nodes. (An example is that
* create_merge_append_plan might add resjunk sort columns to a
* MergeAppend.) Second, create_projection_path has no way of knowing
* what path node will be placed on top of the projection path and
* therefore can t predict whether it will require an exact tlist. For
* both of these reasons, we have to recheck here.
* 在大多数情况下,我们不需要投影运算,creation_projection_path 将设置 dummypp 标志,但并不总是如此。
* 首先, 一些 createplan.c 中的函数更改其节点的 tlist。
* (例如,create_merge_append_plan 可能会向 MergeAppend 添加 resjunk sort 列)。
* 其次,create_projection_path 无法知道将在投影路径顶部放置哪些路径节点,因此无法预测它是否需要一个确切的 tlist。
* 由于这两个原因,我们不得不在这里重新检查。
*/
if (use_physical_tlist(root, best_path- path, flags))
{
/*
* Our caller doesn t really care what tlist we return, so we don t
* actually need to project. However, we may still need to ensure
* proper sortgroupref labels, if the caller cares about those.
* 如果我们的调用者并不关心返回的结果 tlist,所以实际上不需要投影运算。
* 然而,如果调用者关心 sortgroupref 标签,可能仍然需要确保正确的 sortgroupref 数据。
*/
subplan = create_plan_recurse(root, best_path- subpath, 0);
tlist = subplan- targetlist;
if (flags CP_LABEL_TLIST)
apply_pathtarget_labeling_to_tlist(tlist,
best_path- path.pathtarget);
}
else if (is_projection_capable_path(best_path- subpath))
{
/*
* Our caller requires that we return the exact tlist, but no separate
* result node is needed because the subpath is projection-capable.
* Tell create_plan_recurse that we re going to ignore the tlist it
* produces.
* 调用者要求返回精确的 tlist,但是不需要单独的 Result 节点,因为子路径支持投影。
* 调用 create_plan_recurse 时忽略它生成的 tlist。
*/
subplan = create_plan_recurse(root, best_path- subpath,
CP_IGNORE_TLIST);
tlist = build_path_tlist(root, best_path- path);
}
else
{
/*
* It looks like we need a result node, unless by good fortune the
* requested tlist is exactly the one the child wants to produce.
* 看起来需要一个 Result 节点,除非幸运的是,请求的 tlist 正是子节点想要生成的。
*/
subplan = create_plan_recurse(root, best_path- subpath, 0);
tlist = build_path_tlist(root, best_path- path);
needs_result_node = !tlist_same_exprs(tlist, subplan- targetlist);
}
/*
* If we make a different decision about whether to include a Result node
* than create_projection_path did, we ll have made slightly wrong cost
* estimates; but label the plan with the cost estimates we actually used,
* not corrected ones. (XXX this could be cleaned up if we moved more
* of the sortcolumn setup logic into Path creation, but that would add
* expense to creating Paths we might end up not using.)
* 如果我们对是否包含一个 Result 节点做出与 create_projection_path 不同的决定,
* 我们就会做出略微错误的成本估算;
* 但是在这个计划上标上我们实际使用的成本估算,而不是“修正的”成本估算。
* (如果我们将更多的 sortcolumn 设置逻辑移到路径创建中,这个问题就可以解决了,
* 但是这会增加创建路径的成本,而最终可能不会使用这些路径。)
*/
if (!needs_result_node)
{
/* Don t need a separate Result, just assign tlist to subplan */
// 不需要单独的 Result 节点, 把 tlist 赋值给 subplan
plan = subplan;
plan- targetlist = tlist;
/* Label plan with the estimated costs we actually used */
// 标记估算成本
plan- startup_cost = best_path- path.startup_cost;
plan- total_cost = best_path- path.total_cost;
plan- plan_rows = best_path- path.rows;
plan- plan_width = best_path- path.pathtarget- width;
plan- parallel_safe = best_path- path.parallel_safe;
/* ... but don t change subplan s parallel_aware flag */
}
else
{
/* We need a Result node */
// 需要 Result 节点
plan = (Plan *) make_result(tlist, NULL, subplan);
copy_generic_path_info(plan, (Path *) best_path);
}
return plan;
//---------------------------------------------------------------- create_sort_plan
/*
* create_sort_plan
*
* Create a Sort plan for best_path and (recursively) plans
* for its subpaths.
* 创建 Sort 计划节点
*/
static Sort *
create_sort_plan(PlannerInfo *root, SortPath *best_path, int flags)
{
Sort *plan;
Plan *subplan;
/*
* We don t want any excess columns in the sorted tuples, so request a
* smaller tlist. Otherwise, since Sort doesn t project, tlist
* requirements pass through.
* 我们不希望在排序元组中有任何多余的列,所以希望得到一个更小的 tlist。
* 否则,由于 Sort 不执行投影运算,tlist 就会通过完整的保留传递。
*/
subplan = create_plan_recurse(root, best_path- subpath,
flags | CP_SMALL_TLIST);
/*
* make_sort_from_pathkeys() indirectly calls find_ec_member_for_tle(),
* which will ignore any child EC members that don t belong to the given
* relids. Thus, if this sort path is based on a child relation, we must
* pass its relids.
* make_sort_from_pathkeys() 间接调用 find_ec_member_for_tle(),它将忽略不属于给定 relid 的任何子 EC 成员。
* 因此,如果这个排序路径是基于子关系的,我们必须传递它的 relids。
*/
plan = make_sort_from_pathkeys(subplan, best_path- path.pathkeys,
IS_OTHER_REL(best_path- subpath- parent) ?
best_path- path.parent- relids : NULL);
copy_generic_path_info(plan- plan, (Path *) best_path);
return plan;
}
正文完