PostgreSQL 源码解读(41)- 查询语句#26(query_planner函数#4)
上一小节介绍了函数query_planner中子函数add_base_rels_to_query的实现逻辑,本节继续介绍其中的子函数build_base_rel_tlists/find_placeholders_in_jointree/find_lateral_references,这几个子函数的目的仍然是完善RelOptInfo结构体信息。
query_planner代码片段:
//...
build_base_rel_tlists(root, tlist);//构建"base rels"的投影列
find_placeholders_in_jointree(root);//处理jointree中的PHI
find_lateral_references(root);//处理jointree中Lateral依赖
//...
一、重要的数据结构
PlannerInfo
PlannerInfo贯穿整个构建查询计划的全过程.
build_base_rel_tlists、find_placeholders_in_jointree和find_lateral_references函数完善了PlannerInfo->placeholder_list链表.
struct AppendRelInfo;
typedef struct PlannerInfo
{
NodeTag type;//Node标识
Query *parse;
PlannerGlobal *glob;
Index query_level;
struct PlannerInfo *parent_root;
List *plan_params;
Bitmapset *outer_params;
struct RelOptInfo **simple_rel_array;
int simple_rel_array_size;
RangeTblEntry **simple_rte_array;
struct AppendRelInfo **append_rel_array;//先前已介绍,在处理集合操作如UNION ALL时使用
Relids all_baserels;//"base rels"
Relids nullable_baserels;//Nullable-side端的"base rels"
List *join_rel_list;
struct HTAB *join_rel_hash;
List **join_rel_level;
int join_cur_level;
List *init_plans;
List *cte_plan_ids;
List *multiexpr_params;
List *eq_classes;
List *canon_pathkeys;
List *left_join_clauses;
List *right_join_clauses;
List *full_join_clauses;
List *join_info_list;
List *append_rel_list;
List *rowMarks;
List *placeholder_list;
List *fkey_list;
List *query_pathkeys;
List *group_pathkeys;
List *window_pathkeys;
List *distinct_pathkeys;
List *sort_pathkeys;
List *part_schemes;
List *initial_rels;
List *upper_rels[UPPERREL_FINAL + 1];
struct PathTarget *upper_targets[UPPERREL_FINAL + 1];//
List *processed_tlist;//最后需处理的投影列
AttrNumber *grouping_map;
List *minmax_aggs;
MemoryContext planner_cxt;
double total_table_pages;
double tuple_fraction;
double limit_tuples;
Index qual_security_level;
InheritanceKind inhTargetKind;
bool hasJoinRTEs;
bool hasLateralRTEs;
bool hasDeletedRTEs;
bool hasHavingQual;
bool hasPseudoConstantQuals;
bool hasRecursion;
int wt_param_id;
struct Path *non_recursive_path;
Relids curOuterRels;
List *curOuterParams;
void *join_search_private;
bool partColsUpdated;
} PlannerInfo;
RelOptInfo
RelOptInfo结构体贯彻逻辑优化和物理优化过程的始终.
build_base_rel_tlists完善了结构体中attr_needed和reltarget变量,find_lateral_references函数完善了结构体中lateral_vars变量.
typedef struct RelOptInfo
{
NodeTag type;//节点标识
RelOptKind reloptkind;//RelOpt类型
Relids relids;
double rows;
bool consider_startup;
bool consider_param_startup;
bool consider_parallel;
struct PathTarget *reltarget;
List *pathlist;
List *ppilist;
List *partial_pathlist;
struct Path *cheapest_startup_path;//代价最低的启动路径
struct Path *cheapest_total_path;//代价最低的整体路径
struct Path *cheapest_unique_path;//代价最低的获取唯一值的路径
List *cheapest_parameterized_paths;//代价最低的参数化?路径链表
Relids direct_lateral_relids;
Relids lateral_relids;
//reloptkind=RELOPT_BASEREL时使用的数据结构
Index relid;
Oid reltablespace;
RTEKind rtekind;
AttrNumber min_attr;
AttrNumber max_attr;
Relids *attr_needed;
int32 *attr_widths;
List *lateral_vars;
Relids lateral_referencers;
List *indexlist;
List *statlist;
BlockNumber pages;
double tuples;
double allvisfrac;
PlannerInfo *subroot;
List *subplan_params;
int rel_parallel_workers;
//FWD相关信息
Oid serverid;
Oid userid;
bool useridiscurrent;
struct FdwRoutine *fdwroutine;
void *fdw_private;
//已知的,可保证唯一的Relids链表
List *unique_for_rels;
List *non_unique_for_rels;
List *baserestrictinfo;
QualCost baserestrictcost;
Index baserestrict_min_security;
List *joininfo;
bool has_eclass_joins;
bool consider_partitionwise_join;
Relids top_parent_relids;
//分区表使用
PartitionScheme part_scheme;
int nparts;
struct PartitionBoundInfoData *boundinfo;
List *partition_qual;
struct RelOptInfo **part_rels;
List **partexprs;
List **nullable_partexprs;
List *partitioned_child_rels;
} RelOptInfo;
二、源码解读
基本概念
PlaceHolder
PlaceHolder即占位符,常用于减少SQL的parse过程提高性能.
如JDBC中常用的PreparedStatement:
String sql = "select * from t_dwxx where dwbh = ? and dwmc = ?";
PreparedStatement pstmt = connection.preparestatement(sql);
pstmt.setstring(1,'1001');
pstmt.setstring(2,'测试');
resultset rs = ps.executequery();
可以认为,其中的?所代表的是占位符.
在psql中,使用set命令定义变量,在SQL语句中使用占位符:
testdb=# \set v1 '\'1001\''
testdb=# select * from t_dwxx where dwbh = :v1;
dwmc | dwbh | dwdz
-----------+------+--------------------
X有限公司 | 1001 | 广东省广州市荔湾区
(1 row)
build_base_rel_tlists
void
build_base_rel_tlists(PlannerInfo *root, List *final_tlist)
{
List *tlist_vars = pull_var_clause((Node *) final_tlist,
PVC_RECURSE_AGGREGATES |
PVC_RECURSE_WINDOWFUNCS |
PVC_INCLUDE_PLACEHOLDERS);//获取投影列
if (tlist_vars != NIL)
{
//添加到相应的Relation's targetlist(如不存在)
//标记其为连接或者最终输出所需要
add_vars_to_targetlist(root, tlist_vars, bms_make_singleton(0), true);
list_free(tlist_vars);
}
if (root->parse->havingQual)//如存在Having子句,顶层的Having是在查询语句的后期才执行,需保留需要的Vars
{
List *having_vars = pull_var_clause(root->parse->havingQual,
PVC_RECURSE_AGGREGATES |
PVC_INCLUDE_PLACEHOLDERS);
if (having_vars != NIL)
{
add_vars_to_targetlist(root, having_vars,
bms_make_singleton(0), true);
list_free(having_vars);
}
}
}
void
add_vars_to_targetlist(PlannerInfo *root, List *vars,
Relids where_needed, bool create_new_ph)
{
ListCell *temp;
Assert(!bms_is_empty(where_needed));
foreach(temp, vars)
{
Node *node = (Node *) lfirst(temp);
if (IsA(node, Var))
{
Var *var = (Var *) node;//属性Var
RelOptInfo *rel = find_base_rel(root, var->varno);//找到相应的RelOptInfo
int attno = var->varattno;//属性编号
if (bms_is_subset(where_needed, rel->relids))//where_needed是否rel的子集?
continue;//是,继续循环
Assert(attno >= rel->min_attr && attno <= rel->max_attr);
attno -= rel->min_attr;
if (rel->attr_needed[attno] == NULL)
{
rel->reltarget->exprs = lappend(rel->reltarget->exprs,
copyObject(var));//TODO,添加到rel->reltarget->exprs 中
}
rel->attr_needed[attno] = bms_add_members(rel->attr_needed[attno],
where_needed);//where_needed添加到bitmapset中
}
else if (IsA(node, PlaceHolderVar))
{
PlaceHolderVar *phv = (PlaceHolderVar *) node;
PlaceHolderInfo *phinfo = find_placeholder_info(root, phv,
create_new_ph);
phinfo->ph_needed = bms_add_members(phinfo->ph_needed,
where_needed);//添加PHV
}
else
elog(ERROR, "unrecognized node type: %d", (int) nodeTag(node));
}
}
find_placeholders_in_jointree
void
find_placeholders_in_jointree(PlannerInfo *root)
{
if (root->glob->lastPHId != 0)
{
Assert(root->parse->jointree != NULL &&
IsA(root->parse->jointree, FromExpr));
find_placeholders_recurse(root, (Node *) root->parse->jointree);//递归处理
}
}
static void
find_placeholders_recurse(PlannerInfo *root, Node *jtnode)
{
if (jtnode == NULL)
return;
if (IsA(jtnode, RangeTblRef))//RTR
{
}
else if (IsA(jtnode, FromExpr))//FromExpr
{
FromExpr *f = (FromExpr *) jtnode;
ListCell *l;
foreach(l, f->fromlist)
{
find_placeholders_recurse(root, lfirst(l));
}
find_placeholders_in_expr(root, f->quals);//在表达式中搜索
}
else if (IsA(jtnode, JoinExpr))//JoinExpr
{
JoinExpr *j = (JoinExpr *) jtnode;
find_placeholders_recurse(root, j->larg);
find_placeholders_recurse(root, j->rarg);
find_placeholders_in_expr(root, j->quals);//在表达式中搜索
}
else
elog(ERROR, "unrecognized node type: %d",
(int) nodeTag(jtnode));
}
static void
find_placeholders_in_expr(PlannerInfo *root, Node *expr)
{
List *vars;
ListCell *vl;
vars = pull_var_clause(expr,
PVC_RECURSE_AGGREGATES |
PVC_RECURSE_WINDOWFUNCS |
PVC_INCLUDE_PLACEHOLDERS);//遍历Vars,得到PH链表
foreach(vl, vars)
{
PlaceHolderVar *phv = (PlaceHolderVar *) lfirst(vl);
if (!IsA(phv, PlaceHolderVar))
continue;
(void) find_placeholder_info(root, phv, true);//创建PHInfo
}
list_free(vars);
}
PlaceHolderInfo *
find_placeholder_info(PlannerInfo *root, PlaceHolderVar *phv,
bool create_new_ph)
{
PlaceHolderInfo *phinfo;//结果
Relids rels_used;
ListCell *lc;
Assert(phv->phlevelsup == 0);
foreach(lc, root->placeholder_list)//已存在,返回
{
phinfo = (PlaceHolderInfo *) lfirst(lc);
if (phinfo->phid == phv->phid)
return phinfo;
}
if (!create_new_ph)
elog(ERROR, "too late to create a new PlaceHolderInfo");
phinfo = makeNode(PlaceHolderInfo);//构建PHInfo
phinfo->phid = phv->phid;
phinfo->ph_var = copyObject(phv);
rels_used = pull_varnos((Node *) phv->phexpr);
phinfo->ph_lateral = bms_difference(rels_used, phv->phrels);
if (bms_is_empty(phinfo->ph_lateral))
phinfo->ph_lateral = NULL;
phinfo->ph_eval_at = bms_int_members(rels_used, phv->phrels);
if (bms_is_empty(phinfo->ph_eval_at))
{
phinfo->ph_eval_at = bms_copy(phv->phrels);
Assert(!bms_is_empty(phinfo->ph_eval_at));
}
phinfo->ph_needed = NULL;
phinfo->ph_width = get_typavgwidth(exprType((Node *) phv->phexpr),
exprTypmod((Node *) phv->phexpr));
root->placeholder_list = lappend(root->placeholder_list, phinfo);//添加到优化器信息中
find_placeholders_in_expr(root, (Node *) phinfo->ph_var->phexpr);//如存在子表达式,递归进去
return phinfo;
}
find_lateral_references
void
find_lateral_references(PlannerInfo *root)
{
Index rti;
if (!root->hasLateralRTEs)
return;
for (rti = 1; rti < root->simple_rel_array_size; rti++)//遍历RelOptInfo
{
RelOptInfo *brel = root->simple_rel_array[rti];
if (brel == NULL)
continue;
Assert(brel->relid == rti);
if (brel->reloptkind != RELOPT_BASEREL)
continue;
extract_lateral_references(root, brel, rti);//获取LATERAL依赖
}
}
static void
extract_lateral_references(PlannerInfo *root, RelOptInfo *brel, Index rtindex)
{
RangeTblEntry *rte = root->simple_rte_array[rtindex];//相应的RTE
List *vars;
List *newvars;
Relids where_needed;
ListCell *lc;
if (!rte->lateral)//非LATERAL,退出
return;
//获取相应的Vars
if (rte->rtekind == RTE_RELATION)//基表
vars = pull_vars_of_level((Node *) rte->tablesample, 0);
else if (rte->rtekind == RTE_SUBQUERY)//子查询
vars = pull_vars_of_level((Node *) rte->subquery, 1);
else if (rte->rtekind == RTE_FUNCTION)//函数
vars = pull_vars_of_level((Node *) rte->functions, 0);
else if (rte->rtekind == RTE_TABLEFUNC)//TABLEFUNC
vars = pull_vars_of_level((Node *) rte->tablefunc, 0);
else if (rte->rtekind == RTE_VALUES)//VALUES
vars = pull_vars_of_level((Node *) rte->values_lists, 0);
else
{
Assert(false);
return;
}
if (vars == NIL)
return;
newvars = NIL;
foreach(lc, vars)//遍历Vars
{
Node *node = (Node *) lfirst(lc);
node = copyObject(node);
if (IsA(node, Var))//Var
{
Var *var = (Var *) node;
var->varlevelsup = 0;
}
else if (IsA(node, PlaceHolderVar))//PHVar
{
PlaceHolderVar *phv = (PlaceHolderVar *) node;
int levelsup = phv->phlevelsup;
if (levelsup != 0)
IncrementVarSublevelsUp(node, -levelsup, 0);//调整其中的表达式
if (levelsup > 0)
phv->phexpr = preprocess_phv_expression(root, phv->phexpr);//预处理PHVar表达式
}
else
Assert(false);
newvars = lappend(newvars, node);//添加到新的结果Vars中
}
list_free(vars);
where_needed = bms_make_singleton(rtindex);//获取Rel编号
add_vars_to_targetlist(root, newvars, where_needed, true);//添加到相应的Rel中
brel->lateral_vars = newvars;//RelOptInfo赋值
}
List *
pull_vars_of_level(Node *node, int levelsup)
{
pull_vars_context context;
context.vars = NIL;
context.sublevels_up = levelsup;
query_or_expression_tree_walker(node,
pull_vars_walker,
(void *) &context,
0);//调用XX_walker函数遍历
return context.vars;
}
static bool
pull_vars_walker(Node *node, pull_vars_context *context)//遍历函数
{
if (node == NULL)
return false;
if (IsA(node, Var))
{
Var *var = (Var *) node;
if (var->varlevelsup == context->sublevels_up)
context->vars = lappend(context->vars, var);
return false;
}
if (IsA(node, PlaceHolderVar))
{
PlaceHolderVar *phv = (PlaceHolderVar *) node;
if (phv->phlevelsup == context->sublevels_up)
context->vars = lappend(context->vars, phv);
return false;
}
if (IsA(node, Query))
{
bool result;
context->sublevels_up++;
result = query_tree_walker((Query *) node, pull_vars_walker,
(void *) context, 0);
context->sublevels_up--;
return result;
}
return expression_tree_walker(node, pull_vars_walker,
(void *) context);
}
公共部分
List *
pull_var_clause(Node *node, int flags)
{
pull_var_clause_context context;//上下文
//互斥选项检测
Assert((flags & (PVC_INCLUDE_AGGREGATES | PVC_RECURSE_AGGREGATES))
!= (PVC_INCLUDE_AGGREGATES | PVC_RECURSE_AGGREGATES));
Assert((flags & (PVC_INCLUDE_WINDOWFUNCS | PVC_RECURSE_WINDOWFUNCS))
!= (PVC_INCLUDE_WINDOWFUNCS | PVC_RECURSE_WINDOWFUNCS));
Assert((flags & (PVC_INCLUDE_PLACEHOLDERS | PVC_RECURSE_PLACEHOLDERS))
!= (PVC_INCLUDE_PLACEHOLDERS | PVC_RECURSE_PLACEHOLDERS));
context.varlist = NIL;
context.flags = flags;
pull_var_clause_walker(node, &context);//调用XX_walker函数遍历,结果保存在context.varlist中
return context.varlist;
}
static bool
pull_var_clause_walker(Node *node, pull_var_clause_context *context)
{
if (node == NULL)
return false;
if (IsA(node, Var))//Var类型
{
if (((Var *) node)->varlevelsup != 0)//非本级Var
elog(ERROR, "Upper-level Var found where not expected");
context->varlist = lappend(context->varlist, node);//添加到结果链表中
return false;
}
else if (IsA(node, Aggref))//聚合
{
if (((Aggref *) node)->agglevelsup != 0)
elog(ERROR, "Upper-level Aggref found where not expected");
if (context->flags & PVC_INCLUDE_AGGREGATES)//包含聚合
{
context->varlist = lappend(context->varlist, node);//添加到结果
return false;
}
else if (context->flags & PVC_RECURSE_AGGREGATES)//递归搜索
{
}
else
elog(ERROR, "Aggref found where not expected");
}
else if (IsA(node, GroupingFunc))//分组
{
if (((GroupingFunc *) node)->agglevelsup != 0)
elog(ERROR, "Upper-level GROUPING found where not expected");
if (context->flags & PVC_INCLUDE_AGGREGATES)//包含标记
{
context->varlist = lappend(context->varlist, node);
return false;
}
else if (context->flags & PVC_RECURSE_AGGREGATES)//递归标记
{
return false;//直接返回,需与GROUP BY语句一起
}
else
elog(ERROR, "GROUPING found where not expected");
}
else if (IsA(node, WindowFunc))//窗口函数
{
if (context->flags & PVC_INCLUDE_WINDOWFUNCS)//包含标记
{
context->varlist = lappend(context->varlist, node);
return false;
}
else if (context->flags & PVC_RECURSE_WINDOWFUNCS)//递归标记
{
}
else
elog(ERROR, "WindowFunc found where not expected");
}
else if (IsA(node, PlaceHolderVar))//PH
{
if (((PlaceHolderVar *) node)->phlevelsup != 0)
elog(ERROR, "Upper-level PlaceHolderVar found where not expected");
if (context->flags & PVC_INCLUDE_PLACEHOLDERS)
{
context->varlist = lappend(context->varlist, node);
return false;
}
else if (context->flags & PVC_RECURSE_PLACEHOLDERS)
{
}
else
elog(ERROR, "PlaceHolderVar found where not expected");
}
return expression_tree_walker(node, pull_var_clause_walker,
(void *) context);//表达式解析
}
三、跟踪分析
重点考察root->simple_rel_array[n]->attr_needed、root->simple_rel_array[n]->reltarget、root->placeholder_list、root->simple_rel_array[n]->lateral_vars.
启动gdb跟踪
(gdb) b build_base_rel_tlists
Breakpoint 1 at 0x76551b: file initsplan.c, line 153.
(gdb) c
Continuing.
Breakpoint 1, build_base_rel_tlists (root=0x171ae40, final_tlist=0x1734750) at initsplan.c:153
153 List *tlist_vars = pull_var_clause((Node *) final_tlist,
(gdb)
final_tlist是最终的输出列(投影列),一共有5个,分别是t_dwxx.dwmc/dwbh/dwdz,t_grxx.grbh,t_jfxx.je
(gdb) p *final_tlist
$1 = {type = T_List, length = 5, head = 0x1734730, tail = 0x1734a60}
(gdb) p *(Node *)final_tlist->head->data.ptr_value
$2 = {type = T_TargetEntry}
(gdb) p *(TargetEntry *)final_tlist->head->data.ptr_value
$3 = {xpr = {type = T_TargetEntry}, expr = 0x17346e0, resno = 1, resname = 0x171a5a0 "dwmc", ressortgroupref = 0,
resorigtbl = 16394, resorigcol = 1, resjunk = false}
跟踪函数build_base_rel_tlists
(gdb) n
158 if (tlist_vars != NIL)
(gdb)
160 add_vars_to_targetlist(root, tlist_vars, bms_make_singleton(0), true);
#5个Vars
(gdb) p *tlist_vars
$4 = {type = T_List, length = 5, head = 0x17369e0, tail = 0x1737cb8}
(gdb) p *(Var *)tlist_vars->head->data.ptr_value
$5 = {xpr = {type = T_Var}, varno = 1, varattno = 1, vartype = 1043, vartypmod = 104, varcollid = 100, varlevelsup = 0,
varnoold = 1, varoattno = 1, location = 7}
执行函数build_base_rel_tlists,检查final_rel->attr_needed和final_rel->reltarget
(gdb) finish
Run till exit from #0 build_base_rel_tlists (root=0x171ae40, final_tlist=0x1734750) at initsplan.c:160
query_planner (root=0x171ae40, tlist=0x1734750, qp_callback=0x76e97d <standard_qp_callback>, qp_extra=0x7ffe20fc33d0)
at planmain.c:152
152 find_placeholders_in_jointree(root);
检查root内存结构
(gdb) p *root
$15 = {type = T_PlannerInfo, parse = 0x1711680, glob = 0x1732118, query_level = 1, parent_root = 0x0, plan_params = 0x0,
outer_params = 0x0, simple_rel_array = 0x1736578, simple_rel_array_size = 6, simple_rte_array = 0x17365c8,
all_baserels = 0x0, nullable_baserels = 0x0, join_rel_list = 0x0, join_rel_hash = 0x0, join_rel_level = 0x0,
join_cur_level = 0, init_plans = 0x0, cte_plan_ids = 0x0, multiexpr_params = 0x0, eq_classes = 0x0, canon_pathkeys = 0x0,
left_join_clauses = 0x0, right_join_clauses = 0x0, full_join_clauses = 0x0, join_info_list = 0x0, append_rel_list = 0x0,
rowMarks = 0x0, placeholder_list = 0x0, fkey_list = 0x0, query_pathkeys = 0x0, group_pathkeys = 0x0,
window_pathkeys = 0x0, distinct_pathkeys = 0x0, sort_pathkeys = 0x0, part_schemes = 0x0, initial_rels = 0x0,
upper_rels = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, upper_targets = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
processed_tlist = 0x1734750, grouping_map = 0x0, minmax_aggs = 0x0, planner_cxt = 0x165a040, total_table_pages = 0,
tuple_fraction = 0, limit_tuples = -1, qual_security_level = 0, inhTargetKind = INHKIND_NONE, hasJoinRTEs = true,
hasLateralRTEs = true, hasDeletedRTEs = false, hasHavingQual = false, hasPseudoConstantQuals = false,
hasRecursion = false, wt_param_id = -1, non_recursive_path = 0x0, curOuterRels = 0x0, curOuterParams = 0x0,
join_search_private = 0x0, partColsUpdated = false}
RelOptInfo数组,注意数组的第0(下标)个元素为NULL(无用),有用的元素下标从1开始:
第1个元素是基础关系(相应的RTE=t_dwxx),第2个元素为NULL(相应的RTE=子查询),第3个元素为基础关系(相应的RTE=t_grxx),第4个元素为基础关系(相应的RTE=t_jfxx),第5个元素为NULL(相应的RTE=连接)
(gdb) p *root->simple_rel_array[0]
Cannot access memory at address 0x0
(gdb) p *root->simple_rel_array[1]
$31 = {type = T_RelOptInfo, reloptkind = RELOPT_BASEREL, relids = 0x1736828, rows = 0, consider_startup = false,
consider_param_startup = false, consider_parallel = false, reltarget = 0x1736840, pathlist = 0x0, ppilist = 0x0,
partial_pathlist = 0x0, cheapest_startup_path = 0x0, cheapest_total_path = 0x0, cheapest_unique_path = 0x0,
cheapest_parameterized_paths = 0x0, direct_lateral_relids = 0x0, lateral_relids = 0x0, relid = 1, reltablespace = 0,
rtekind = RTE_RELATION, min_attr = -7, max_attr = 3, attr_needed = 0x1736890, attr_widths = 0x1736920,
lateral_vars = 0x0, lateral_referencers = 0x0, indexlist = 0x1736cc8, statlist = 0x0, pages = 1, tuples = 3,
allvisfrac = 0, subroot = 0x0, subplan_params = 0x0, rel_parallel_workers = -1, serverid = 0, userid = 0,
useridiscurrent = false, fdwroutine = 0x0, fdw_private = 0x0, unique_for_rels = 0x0, non_unique_for_rels = 0x0,
baserestrictinfo = 0x0, baserestrictcost = {startup = 0, per_tuple = 0}, baserestrict_min_security = 4294967295,
joininfo = 0x0, has_eclass_joins = false, top_parent_relids = 0x0, part_scheme = 0x0, nparts = 0, boundinfo = 0x0,
partition_qual = 0x0, part_rels = 0x0, partexprs = 0x0, nullable_partexprs = 0x0, partitioned_child_rels = 0x0}
(gdb) p *root->simple_rel_array[2]
Cannot access memory at address 0x0
(gdb) p *root->simple_rel_array[3]
$32 = {type = T_RelOptInfo, reloptkind = RELOPT_BASEREL, relids = 0x17377f8, rows = 0, consider_startup = false,
consider_param_startup = false, consider_parallel = false, reltarget = 0x1737810, pathlist = 0x0, ppilist = 0x0,
partial_pathlist = 0x0, cheapest_startup_path = 0x0, cheapest_total_path = 0x0, cheapest_unique_path = 0x0,
cheapest_parameterized_paths = 0x0, direct_lateral_relids = 0x0, lateral_relids = 0x0, relid = 3, reltablespace = 0,
rtekind = RTE_RELATION, min_attr = -7, max_attr = 5, attr_needed = 0x1737860, attr_widths = 0x17378f0,
lateral_vars = 0x0, lateral_referencers = 0x0, indexlist = 0x0, statlist = 0x0, pages = 10, tuples = 400, allvisfrac = 0,
subroot = 0x0, subplan_params = 0x0, rel_parallel_workers = -1, serverid = 0, userid = 0, useridiscurrent = false,
fdwroutine = 0x0, fdw_private = 0x0, unique_for_rels = 0x0, non_unique_for_rels = 0x0, baserestrictinfo = 0x0,
baserestrictcost = {startup = 0, per_tuple = 0}, baserestrict_min_security = 4294967295, joininfo = 0x0,
has_eclass_joins = false, top_parent_relids = 0x0, part_scheme = 0x0, nparts = 0, boundinfo = 0x0, partition_qual = 0x0,
part_rels = 0x0, partexprs = 0x0, nullable_partexprs = 0x0, partitioned_child_rels = 0x0}
(gdb) p *root->simple_rel_array[4]
$33 = {type = T_RelOptInfo, reloptkind = RELOPT_BASEREL, relids = 0x1737b50, rows = 0, consider_startup = false,
consider_param_startup = false, consider_parallel = false, reltarget = 0x1737b68, pathlist = 0x0, ppilist = 0x0,
partial_pathlist = 0x0, cheapest_startup_path = 0x0, cheapest_total_path = 0x0, cheapest_unique_path = 0x0,
cheapest_parameterized_paths = 0x0, direct_lateral_relids = 0x0, lateral_relids = 0x0, relid = 4, reltablespace = 0,
rtekind = RTE_RELATION, min_attr = -7, max_attr = 3, attr_needed = 0x1737bb8, attr_widths = 0x1737c48,
lateral_vars = 0x0, lateral_referencers = 0x0, indexlist = 0x0, statlist = 0x0, pages = 10, tuples = 720, allvisfrac = 0,
subroot = 0x0, subplan_params = 0x0, rel_parallel_workers = -1, serverid = 0, userid = 0, useridiscurrent = false,
fdwroutine = 0x0, fdw_private = 0x0, unique_for_rels = 0x0, non_unique_for_rels = 0x0, baserestrictinfo = 0x0,
baserestrictcost = {startup = 0, per_tuple = 0}, baserestrict_min_security = 4294967295, joininfo = 0x0,
has_eclass_joins = false, top_parent_relids = 0x0, part_scheme = 0x0, nparts = 0, boundinfo = 0x0, partition_qual = 0x0,
part_rels = 0x0, partexprs = 0x0, nullable_partexprs = 0x0, partitioned_child_rels = 0x0}
(gdb) p *root->simple_rel_array[5]
Cannot access memory at address 0x0
查看root->simple_rel_array[n]->attr_needed、root->simple_rel_array[n]->reltarget的内存结构,以第1个元素为例:
#attr_needed(类型为Relids)为NULL
(gdb) p *root->simple_rel_array[1]->attr_needed
$47 = (Relids) 0x0
#reltarget->exprs为3个Var的链表
(gdb) p *root->simple_rel_array[1]->reltarget->exprs
$38 = {type = T_List, length = 3, head = 0x1737d40, tail = 0x1737e80}
(gdb) p *(Var *)root->simple_rel_array[1]->reltarget->exprs->head->data.ptr_value
$40 = {xpr = {type = T_Var}, varno = 1, varattno = 1, vartype = 1043, vartypmod = 104, varcollid = 100, varlevelsup = 0,
varnoold = 1, varoattno = 1, location = 7}
继续执行,调用函数find_placeholders_in_jointree
152 find_placeholders_in_jointree(root);
(gdb) step
find_placeholders_in_jointree (root=0x171ae40) at placeholder.c:148
148 if (root->glob->lastPHId != 0)
(gdb) n
155 }
(gdb) n
154 find_lateral_references(root);
#链表为NULL
#psql中的:v1似乎不是占位符,理解有偏差//TODO...
(gdb) p root->placeholder_list
$48 = (List *) 0x0
下面调用子函数find_lateral_references,由于RelOptInfo中的3个不为NULL的元素对应的lateral均为FALSE(只有子查询的lateral为true,但子查询对应的RelOptInfo为NULL),因此root->simple_rel_array[1/3/4/5]->lateral_vars均为NULL
四、参考资料
initsplan.c
免责声明:
① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。
② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341
PostgreSQL 源码解读(41)- 查询语句#26(query_planner函数#4)
下载Word文档到电脑,方便收藏和打印~