This commit is contained in:
yangke1125 2023-10-04 23:18:10 +08:00
parent 3d69ac3ea8
commit dc653b252b
1 changed files with 133 additions and 66 deletions

View File

@ -5775,7 +5775,7 @@ static double calc_joinrel_size_estimate(PlannerInfo* root, double outer_rows, d
(errmodule(MOD_OPT),
errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE),
errmsg("unrecognized join type when calculate joinrel size estimate: %d", (int)jointype)));
nrows = 0; // 果连接类型未知则将行数估算为0
nrows = 0; // <EFBFBD><EFBFBD><EFBFBD>果连接类型未知则将行数估算为0
} break;
}
@ -6045,31 +6045,41 @@ inline void set_rel_encode_info_if_vectorized(PlannerInfo *root, RelOptInfo *rel
* The per-attribute width estimates are cached for possible re-use while
* building join relations.
*/
// 设置关系的宽度估计
void set_rel_width(PlannerInfo* root, RelOptInfo* rel)
{
// 获取关系的OID
Oid reloid = planner_rt_fetch(rel->relid, root)->relid;
// 初始化元组宽度为0
int32 tuple_width = 0;
// 初始化是否有整行变量的标志为false
bool have_wholerow_var = false;
// 遍历关系的目标列表
ListCell* lc = NULL;
foreach (lc, rel->reltargetlist) {
Node* node = (Node*)lfirst(lc);
// 检查节点是否是一个变量
if (IsA(node, Var)) {
Var* var = (Var*)node;
int ndx;
int32 item_width;
// 断言变量的varno必须与关系的relid匹配
AssertEreport(var->varno == rel->relid,
MOD_OPT,
"The varno does not match to relid when setting the estimated output width of a base relation.");
// 断言变量的varattno必须大于等于关系的最小属性
AssertEreport(var->varattno >= rel->min_attr,
MOD_OPT,
"The varattno is less than min_attr when setting the estimated output width of a base relation.");
// 断言变量的varattno必须小于等于关系的最大属性
AssertEreport(var->varattno <= rel->max_attr,
MOD_OPT,
"The varattno is larger than max_attr when setting the estimated output width of a base relation.");
// 计算属性在关系中的索引
ndx = var->varattno - rel->min_attr;
/*
@ -6092,31 +6102,42 @@ void set_rel_width(PlannerInfo* root, RelOptInfo* rel)
}
/* Try to get column width from statistics */
if (reloid != InvalidOid && var->varattno > 0) {
Oid targetid = reloid;
bool ispartition = false;
RangeTblEntry* rte = planner_rt_fetch(rel->relid, root);
// 检查关系的OID是否有效以及变量的varattno是否大于0
if (reloid != InvalidOid && var->varattno > 0) {
Oid targetid = reloid;
bool ispartition = false;
RangeTblEntry* rte = planner_rt_fetch(rel->relid, root);
if (rte->isContainPartition) {
AssertEreport(OidIsValid(rte->partitionOid),
MOD_OPT,
"The partitionOid is invalid when setting the estimated output width of a base relation.");
targetid = rte->partitionOid;
ispartition = true;
}
// 如果关系包含分区则使用分区OID作为目标ID
if (rte->isContainPartition) {
// 断言分区OID必须有效
AssertEreport(OidIsValid(rte->partitionOid),
MOD_OPT,
"The partitionOid is invalid when setting the estimated output width of a base relation.");
targetid = rte->partitionOid;
ispartition = true;
}
if (rte->isContainSubPartition) {
Assert(OidIsValid(rte->partitionOid));
targetid = rte->subpartitionOid;
ispartition = true;
}
// 如果关系包含子分区则使用子分区OID作为目标ID
if (rte->isContainSubPartition) {
// 断言分区OID必须有效
Assert(OidIsValid(rte->partitionOid));
targetid = rte->subpartitionOid;
ispartition = true;
}
// 获取属性的平均宽度
item_width = get_attavgwidth(targetid, var->varattno, ispartition);
// 如果属性宽度大于0
if (item_width > 0) {
// 将属性宽度存储在关系的attr_widths数组中
rel->attr_widths[ndx] = item_width;
// 增加元组宽度
tuple_width += item_width;
// 设置关系的编码信息(如果是矢量化计划)
set_rel_encode_info_if_vectorized(root, rel, var->vartype, item_width);
continue;
item_width = get_attavgwidth(targetid, var->varattno, ispartition);
if (item_width > 0) {
rel->attr_widths[ndx] = item_width;
tuple_width += item_width;
set_rel_encode_info_if_vectorized(root, rel, var->vartype, item_width);
continue;
}
}
@ -6132,7 +6153,7 @@ void set_rel_width(PlannerInfo* root, RelOptInfo* rel)
rel->attr_widths[ndx] = item_width;
tuple_width += item_width;
set_rel_encode_info_if_vectorized(root, rel, var->vartype, item_width);
} else if (IsA(node, PlaceHolderVar)) {
} else if (IsA(node, PlaceHolderVar)) {// 如果变量不是Var类型则进入这个分支
PlaceHolderVar* phv = (PlaceHolderVar*)node;
PlaceHolderInfo* phinfo = find_placeholder_info(root, phv, false);
@ -6146,13 +6167,17 @@ void set_rel_width(PlannerInfo* root, RelOptInfo* rel)
*/
int32 item_width;
item_width = get_typavgwidth(exprType(node), exprTypmod(node));
AssertEreport(item_width > 0,
MOD_OPT,
"The estimated average width of values of the type is not larger than 0"
"when setting the estimated output width of a base relation.");
// 获取节点的类型和类型修饰符,然后计算平均宽度
item_width = get_typavgwidth(exprType(node), exprTypmod(node));
// 断言宽度必须大于0
AssertEreport(item_width > 0,
MOD_OPT,
"The estimated average width of values of the type is not larger than 0"
"when setting the estimated output width of a base relation.");
tuple_width += item_width;
// 增加元组宽度
tuple_width += item_width;
// 设置关系的编码信息(如果是矢量化计划)
set_rel_encode_info_if_vectorized(root, rel, exprType(node), item_width);
}
}
@ -6161,7 +6186,8 @@ void set_rel_width(PlannerInfo* root, RelOptInfo* rel)
* If we have a whole-row reference, estimate its width as the sum of
* per-column widths plus sizeof(HeapTupleHeaderData).
*/
if (have_wholerow_var) {
if (have_wholerow_var) {
// 如果有整行变量,计算整行的宽度
int32 wholerow_width = sizeof(HeapTupleHeaderData);
if (reloid != InvalidOid) {
@ -6180,18 +6206,31 @@ void set_rel_width(PlannerInfo* root, RelOptInfo* rel)
partid = rte->subpartitionOid;
}
/* Real relation, so estimate true tuple width */
// 获取关系数据的宽度,包括主表和分区
wholerow_width += get_relation_data_width(reloid, partid, rel->attr_widths - rel->min_attr);
} else {
/* Do what we can with info for a phony rel */
// 获取关系的宽度,包括所有属性
AttrNumber i;
for (i = 1; i <= rel->max_attr; i++)
wholerow_width += rel->attr_widths[i - rel->min_attr];
}
// 将整行的宽度设置到关系的属性宽度数组中
rel->attr_widths[0 - rel->min_attr] = wholerow_width;
// 增加元组宽度
tuple_width += wholerow_width;
}
// 断言元组宽度必须大于或等于0
AssertEreport(tuple_width >= 0,
MOD_OPT,
"The estimated width of tuple is less than 0"
"when setting the estimated output width of a base relation.");
// 将元组宽度设置到关系的宽度属性中
rel->width = tuple_width;
/*
* Include the whole-row Var as part of the output tuple. Yes, that
* really is what happens at runtime.
@ -6225,32 +6264,35 @@ void set_rel_width(PlannerInfo* root, RelOptInfo* rel)
*/
double relation_byte_size(double tuples, int width, bool vectorized, bool aligned, bool issort, bool indexsort)
{
// 断言宽度必须大于或等于0
Assert(width >= 0);
// 计算头部大小,根据是否排序和是否索引排序来选择不同的头部类型
size_t header_size = (issort && indexsort) ? sizeof(IndexTupleData) : sizeof(HeapTupleHeaderData);
if (aligned) {
if (vectorized)
// 返回关系的字节大小,考虑了对齐
return tuples * (TUPLE_OVERHEAD(true) + width);
else
// 返回关系的字节大小,考虑了对齐和分配块大小
return tuples *
(TUPLE_OVERHEAD(issort) + alloc_trunk_size(MAXALIGN((uintptr_t)width) + MAXALIGN(header_size)));
} else {
// 返回关系的字节大小,不考虑对齐
return tuples * (MAXALIGN((uintptr_t)width) + MAXALIGN(header_size));
}
}
/*
* page_size
* Returns an estimate of the number of pages covered by a given
* number of tuples of a given width (size in bytes).
*/
double page_size(double tuples, int width)
{
// 计算关系所占用的页数
return ceil(relation_byte_size(tuples, width, false) / BLCKSZ);
}
/* it used to compute page_size in createplan.cpp */
double cost_page_size(double tuples, int width)
{
// 计算成本中的页数
return page_size(tuples, width);
}
@ -6261,8 +6303,11 @@ double cost_page_size(double tuples, int width)
*/
void restore_hashjoin_cost(Path* path)
{
// 如果启用了哈希连接成本修改并且路径是哈希路径
if (u_sess->attr.attr_sql.enable_change_hjcost && IsA(path, HashPath)) {
// 获取内部路径
Path* innerpath = ((HashPath*)path)->jpath.innerjoinpath;
// 恢复哈希连接的启动成本
path->startup_cost += innerpath->total_cost - innerpath->startup_cost;
}
}
@ -6385,34 +6430,42 @@ void hybrid_samplescangetsamplesize(PlannerInfo* root, RelOptInfo* baserel, List
ListCell* lc = NULL;
uint16 i = 0;
// 断言样本参数数量必须等于2
AssertEreport(SAMPLEARGSNUM == list_length(paramexprs),
MOD_OPT,
"The number of sample percentage info does not equal 2"
"when setting the estimated output width of a base relation.");
foreach (lc, paramexprs) {
Node* paramnode = (Node*)lfirst(lc);
Node* pctnode = estimate_expression_value(root, paramnode);
float4 samplefract = 0.0;
// 检查是否成功估算了百分比值
if (likely(pctnode)) {
samplefract = get_samplefract(pctnode);
} else {
// 如果估算失败,报告错误
ereport(ERROR,
(errcode(ERRCODE_UNEXPECTED_NULL_VALUE),
errmsg("Fail to estimate expression value.")));
}
if (i == SYSTEM_SAMPLE) {
/* We'll visit a sample of the pages ... */
/* 我们将访问页面的一部分... */
// 更新关系的页面数,以考虑样本抽取
baserel->pages = clamp_row_est(baserel->pages * samplefract);
}
/* ... and hopefully get a representative number of tuples from them */
/* ... 并希望从中获得代表性的元组数量 */
// 更新关系的元组数,以考虑样本抽取
baserel->tuples = clamp_row_est(baserel->tuples * samplefract);
i++;
}
}
/*
* copy_mem_info
* copy OpMemInfo structure from source to dest
@ -6471,22 +6524,26 @@ bool has_complicate_hashkey(List* hashclauses, Relids inner_relids)
{
ListCell* lc = NULL;
// 遍历哈希关联条件列表
foreach (lc, hashclauses) {
RestrictInfo* restrictinfo = (RestrictInfo*)lfirst(lc);
Node* innerkey = NULL;
// 断言:条件节点必须是 RestrictInfo 类型
AssertEreport(IsA(restrictinfo, RestrictInfo),
MOD_OPT,
"The nodeTag of restrictinfo is not T_RestrictInfo"
"when setting the estimated output width of a base relation.");
/*
* First we have to figure out which side of the hashjoin clause
* is the inner side.
*
* restrictinfo ID inner_relids
* innerkey restrictinfo
*/
if (bms_is_subset(restrictinfo->right_relids, inner_relids)) {
innerkey = get_rightop(restrictinfo->clause);
} else {
// 断言:左侧关系 ID 必须是 inner_relids 的子集
AssertEreport(bms_is_subset(restrictinfo->left_relids, inner_relids),
MOD_OPT,
"The left relids is not subset of the relids of inner side"
@ -6494,7 +6551,7 @@ bool has_complicate_hashkey(List* hashclauses, Relids inner_relids)
innerkey = get_leftop(restrictinfo->clause);
}
/* Judge if the inner key is simple var */
/* 判断内部键是否为简单变量 */
if (innerkey != NULL && !IsA(innerkey, Var) &&
!(IsA(innerkey, RelabelType) && IsA(((RelabelType*)innerkey)->arg, Var)))
return true;
@ -6503,6 +6560,7 @@ bool has_complicate_hashkey(List* hashclauses, Relids inner_relids)
return false;
}
/*
* calc_distributekey_width
* Optimizer will add distribute key in the targetlist if not found in plan
@ -6518,30 +6576,31 @@ bool has_complicate_hashkey(List* hashclauses, Relids inner_relids)
*/
static int calc_distributekey_width(Path* path, int* width, bool vectorized, bool aligned)
{
int num = 0;
int num = 0; // 初始化计数器为0
ListCell* lc = NULL;
/* Only do this for redistribute stream, since only redistribute has distribute keys */
/* 只对分发流执行此操作,因为只有分发流有分发键 */
if (IsA(path, StreamPath) && ((StreamPath*)path)->type == STREAM_REDISTRIBUTE) {
foreach (lc, path->distribute_keys) {
foreach (lc, path->distribute_keys) { // 遍历分发键列表
Node* node = (Node*)lfirst(lc);
if (!list_member(path->parent->reltargetlist, node)) {
num++;
int32 item_width = get_typavgwidth(exprType(node), exprTypmod(node));
if (!list_member(path->parent->reltargetlist, node)) { // 检查节点是否不在目标列表中
num++; // 增加计数器
int32 item_width = get_typavgwidth(exprType(node), exprTypmod(node)); // 获取类型的平均宽度
AssertEreport(item_width > 0,
MOD_OPT,
"The item width is not larger than 0 when setting the estimated output width of a base relation.");
if (vectorized)
*width += columnar_get_col_width(exprType(node), item_width, aligned);
*width += columnar_get_col_width(exprType(node), item_width, aligned); // 计算列宽度
else
*width += item_width;
*width += item_width; // 直接累加宽度
}
}
}
return num;
return num; // 返回计数器的值
}
/*
* get_path_actual_total_width
* In PG optimizer, only width of row engine is estimated, and it has
@ -6561,33 +6620,35 @@ static int calc_distributekey_width(Path* path, int* width, bool vectorized, boo
*/
int get_path_actual_total_width(Path* path, bool vectorized, OpType type, int newcol)
{
int num_new_col = 0;
int width = 0;
bool aligned = (type >= OP_SORT);
int num_new_col = 0; // 用于记录新列的数量
int width = 0; // 用于记录宽度
bool aligned = (type >= OP_SORT); // 根据操作类型是否需要对齐
if (path->parent == NULL) {
return COL_TUPLE_WIDTH;
return COL_TUPLE_WIDTH; // 如果路径的父节点为空,则返回默认列元组宽度
}
/* For redistribute, we will add unmatched distribute key into targetlist, so count this */
// 计算分发键的宽度,并将结果累加到 width 中
num_new_col = calc_distributekey_width(path, &width, vectorized, aligned);
if (vectorized) {
switch (type) {
case OP_HASHJOIN:
// 计算哈希连接操作的宽度
width += path->parent->encodedwidth +
SIZE_COL_VALUE * (list_length(path->parent->reltargetlist) + num_new_col + newcol);
break;
case OP_HASHAGG:
// 计算哈希聚合操作的宽度
width += path->parent->encodedwidth + TUPLE_OVERHEAD(true) + sizeof(void*) * 2 +
SIZE_COL_VALUE * (list_length(path->parent->reltargetlist) + num_new_col + newcol);
break;
case OP_SORT:
if (width != 0 || path->parent->encodednum != 0)
newcol += 1;
newcol += 1; // 如果排序操作且宽度不为0或者已编码的列数不为0增加新列
/* No need break here. */
case OP_MATERIAL:
/* don't know encoded width of each column, just average them for a rough estimation */
/* 不知道每列的编码宽度,只是对它们进行粗略估算的平均值 */
if (path->parent->encodednum > 0)
width += path->parent->encodednum *
alloc_trunk_size(path->parent->encodedwidth / path->parent->encodednum);
@ -6597,48 +6658,54 @@ int get_path_actual_total_width(Path* path, bool vectorized, OpType type, int ne
break;
}
} else {
width += path->parent->width;
width += path->parent->width; // 非矢量化操作,直接使用父节点的宽度
}
return width;
return width; // 返回计算得到的总宽度
}
/*
* get_subqueryscan_stream_cost
* get stream_cost of a subquery
*/
static Cost get_subqueryscan_stream_cost(Plan* subplan)
{
Cost stream_cost = 0;
Cost stream_cost = 0; // 初始化流成本为0
if (subplan == NULL)
return stream_cost;
return stream_cost; // 如果子计划为空直接返回0
switch (nodeTag(subplan)) {
case T_HashJoin:
case T_VecHashJoin:
// 对于哈希连接计划节点,递归调用左子树的流成本
stream_cost = get_subqueryscan_stream_cost(subplan->lefttree);
break;
case T_NestLoop:
case T_VecNestLoop:
// 对于嵌套循环计划节点,递归调用右子树的流成本
stream_cost = get_subqueryscan_stream_cost(subplan->righttree);
break;
case T_MergeJoin:
case T_VecMergeJoin:
// 对于归并连接计划节点,递归调用右子树的流成本
stream_cost = get_subqueryscan_stream_cost(subplan->righttree);
break;
case T_Stream:
case T_VecStream:
// 对于流计划节点,流成本等于启动成本
stream_cost = subplan->startup_cost;
break;
default:
// 对于其他计划节点类型,递归调用左子树的流成本
stream_cost = get_subqueryscan_stream_cost(subplan->lefttree);
break;
}
return stream_cost;
return stream_cost; // 返回计算得到的流成本
}