diff --git a/src/gausskernel/optimizer/path/costsize.cpp b/src/gausskernel/optimizer/path/costsize.cpp index 3638b8698..2be720501 100644 --- a/src/gausskernel/optimizer/path/costsize.cpp +++ b/src/gausskernel/optimizer/path/costsize.cpp @@ -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; // ���果连接类型未知,则将行数估算为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; // 返回计算得到的流成本 }