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