Enter
This commit is contained in:
parent
3c36671432
commit
0434074e45
|
|
@ -4828,25 +4828,40 @@ Cost cost_rescan_material(double rows, int width, OpMemInfo* mem_info, bool vect
|
|||
* the run_cost charge in cost_sort, and also see comments in
|
||||
* cost_material before you change it.)
|
||||
*/
|
||||
double local_rows = rows / dop;
|
||||
Cost run_cost = u_sess->attr.attr_sql.cpu_operator_cost * local_rows;
|
||||
double nbytes = relation_byte_size(local_rows, width, vectorized, true, false);
|
||||
long work_mem_bytes = u_sess->opt_cxt.op_work_mem * 1024L / dop;
|
||||
/* It will spill, so account for re-read cost */
|
||||
double npages = ceil(nbytes / BLCKSZ);
|
||||
double disk_cost = u_sess->attr.attr_sql.seq_page_cost * npages;
|
||||
// 计算每个执行节点的本地行数
|
||||
double local_rows = rows / dop;
|
||||
|
||||
if (nbytes > work_mem_bytes) {
|
||||
run_cost += disk_cost;
|
||||
}
|
||||
if (mem_info != NULL) {
|
||||
mem_info->opMem = u_sess->opt_cxt.op_work_mem;
|
||||
mem_info->maxMem = nbytes / 1024L * dop;
|
||||
mem_info->minMem = mem_info->maxMem / SORT_MAX_DISK_SIZE;
|
||||
mem_info->regressCost = disk_cost;
|
||||
}
|
||||
// 计算运行成本,考虑每个执行节点的本地行数和 CPU 运算成本
|
||||
Cost run_cost = u_sess->attr.attr_sql.cpu_operator_cost * local_rows;
|
||||
|
||||
// 计算每个执行节点需要的字节数,考虑本地行数、宽度、是否矢量化等因素
|
||||
double nbytes = relation_byte_size(local_rows, width, vectorized, true, false);
|
||||
|
||||
// 计算每个执行节点的工作内存字节数,根据工作内存配置和并行度进行分配
|
||||
long work_mem_bytes = u_sess->opt_cxt.op_work_mem * 1024L / dop;
|
||||
|
||||
// 计算数据需要的页数,以 BLCKSZ 为单位
|
||||
double npages = ceil(nbytes / BLCKSZ);
|
||||
|
||||
// 计算磁盘成本,考虑数据页数和磁盘顺序扫描成本
|
||||
double disk_cost = u_sess->attr.attr_sql.seq_page_cost * npages;
|
||||
|
||||
// 如果数据字节数超过工作内存限制,考虑磁盘读取成本
|
||||
if (nbytes > work_mem_bytes) {
|
||||
run_cost += disk_cost;
|
||||
}
|
||||
|
||||
// 如果传入了内存信息结构体,则更新内存信息
|
||||
if (mem_info != NULL) {
|
||||
mem_info->opMem = u_sess->opt_cxt.op_work_mem;
|
||||
mem_info->maxMem = nbytes / 1024L * dop;
|
||||
mem_info->minMem = mem_info->maxMem / SORT_MAX_DISK_SIZE;
|
||||
mem_info->regressCost = disk_cost;
|
||||
}
|
||||
|
||||
// 返回运行成本
|
||||
return run_cost;
|
||||
|
||||
return run_cost;
|
||||
}
|
||||
|
||||
#ifdef PGXC
|
||||
|
|
@ -4921,42 +4936,44 @@ static bool cost_qual_eval_walker(Node* node, cost_qual_eval_context* context)
|
|||
* cost more than once. If the clause's cost hasn't been computed yet,
|
||||
* the field's startup value will contain -1.
|
||||
*/
|
||||
if (IsA(node, RestrictInfo)) {
|
||||
RestrictInfo* rinfo = (RestrictInfo*)node;
|
||||
// 检查传入的节点是否为 RestrictInfo 类型
|
||||
if (IsA(node, RestrictInfo)) {
|
||||
RestrictInfo* rinfo = (RestrictInfo*)node;
|
||||
|
||||
if (rinfo->eval_cost.startup < 0) {
|
||||
cost_qual_eval_context locContext;
|
||||
// 如果评估成本中的 startup 值小于 0,则需要计算
|
||||
if (rinfo->eval_cost.startup < 0) {
|
||||
cost_qual_eval_context locContext;
|
||||
|
||||
locContext.root = context->root;
|
||||
locContext.total.startup = 0;
|
||||
// 初始化评估上下文,设置根节点和成本初始值
|
||||
locContext.root = context->root;
|
||||
locContext.total.startup = 0;
|
||||
locContext.total.per_tuple = 0;
|
||||
|
||||
// 如果存在 OR 子句,计算 OR 子句中的表达式成本
|
||||
if (rinfo->orclause)
|
||||
(void)cost_qual_eval_walker((Node*)rinfo->orclause, &locContext);
|
||||
else
|
||||
(void)cost_qual_eval_walker((Node*)rinfo->clause, &locContext);
|
||||
|
||||
// 如果表达式被标记为伪常量,将 startup 成本添加到总成本中
|
||||
if (rinfo->pseudoconstant) {
|
||||
locContext.total.startup += locContext.total.per_tuple;
|
||||
locContext.total.per_tuple = 0;
|
||||
|
||||
/*
|
||||
* For an OR clause, recurse into the marked-up tree so that we
|
||||
* set the eval_cost for contained RestrictInfos too.
|
||||
*/
|
||||
if (rinfo->orclause)
|
||||
(void)cost_qual_eval_walker((Node*)rinfo->orclause, &locContext);
|
||||
else
|
||||
(void)cost_qual_eval_walker((Node*)rinfo->clause, &locContext);
|
||||
|
||||
/*
|
||||
* If the RestrictInfo is marked pseudoconstant, it will be tested
|
||||
* only once, so treat its cost as all startup cost.
|
||||
*/
|
||||
if (rinfo->pseudoconstant) {
|
||||
/* count one execution during startup */
|
||||
locContext.total.startup += locContext.total.per_tuple;
|
||||
locContext.total.per_tuple = 0;
|
||||
}
|
||||
rinfo->eval_cost = locContext.total;
|
||||
}
|
||||
context->total.startup += rinfo->eval_cost.startup;
|
||||
context->total.per_tuple += rinfo->eval_cost.per_tuple;
|
||||
/* do NOT recurse into children */
|
||||
return false;
|
||||
|
||||
// 将计算得到的成本赋值给 RestrictInfo 结构体
|
||||
rinfo->eval_cost = locContext.total;
|
||||
}
|
||||
|
||||
// 将 RestrictInfo 的评估成本合并到上下文中的总成本中
|
||||
context->total.startup += rinfo->eval_cost.startup;
|
||||
context->total.per_tuple += rinfo->eval_cost.per_tuple;
|
||||
|
||||
// 返回 false 表示不需要继续递归处理子节点
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* For each operator or function node in the given tree, we charge the
|
||||
* estimated execution cost given by pg_proc.procost (remember to multiply
|
||||
|
|
@ -4979,103 +4996,80 @@ static bool cost_qual_eval_walker(Node* node, cost_qual_eval_context* context)
|
|||
* moreover, since our rowcount estimates for functions tend to be pretty
|
||||
* phony, the results would also be pretty phony.
|
||||
*/
|
||||
if (IsA(node, FuncExpr)) {
|
||||
context->total.per_tuple += get_func_cost(((FuncExpr*)node)->funcid) * u_sess->attr.attr_sql.cpu_operator_cost;
|
||||
} else if (IsA(node, OpExpr) || IsA(node, DistinctExpr) || IsA(node, NullIfExpr)) {
|
||||
/* rely on struct equivalence to treat these all alike */
|
||||
set_opfuncid((OpExpr*)node);
|
||||
context->total.per_tuple += get_func_cost(((OpExpr*)node)->opfuncid) * u_sess->attr.attr_sql.cpu_operator_cost;
|
||||
} else if (IsA(node, ScalarArrayOpExpr)) {
|
||||
/*
|
||||
* Estimate that the operator will be applied to about half of the
|
||||
* array elements before the answer is determined.
|
||||
*/
|
||||
ScalarArrayOpExpr* saop = (ScalarArrayOpExpr*)node;
|
||||
Node* arraynode = (Node*)lsecond(saop->args);
|
||||
// 检查传入的节点是否为 FuncExpr 类型
|
||||
if (IsA(node, FuncExpr)) {
|
||||
// 如果是 FuncExpr 类型,将函数的评估成本添加到总成本中
|
||||
context->total.per_tuple += get_func_cost(((FuncExpr*)node)->funcid) * u_sess->attr.attr_sql.cpu_operator_cost;
|
||||
} else if (IsA(node, OpExpr) || IsA(node, DistinctExpr) || IsA(node, NullIfExpr)) {
|
||||
// 如果是 OpExpr、DistinctExpr 或 NullIfExpr 类型,设置操作符的函数 ID 并添加其评估成本到总成本中
|
||||
set_opfuncid((OpExpr*)node);
|
||||
context->total.per_tuple += get_func_cost(((OpExpr*)node)->opfuncid) * u_sess->attr.attr_sql.cpu_operator_cost;
|
||||
} else if (IsA(node, ScalarArrayOpExpr)) {
|
||||
// 如果是 ScalarArrayOpExpr 类型,设置标量数组操作符的函数 ID 并添加其评估成本到总成本中
|
||||
ScalarArrayOpExpr* saop = (ScalarArrayOpExpr*)node;
|
||||
Node* arraynode = (Node*)lsecond(saop->args);
|
||||
|
||||
set_sa_opfuncid(saop);
|
||||
context->total.per_tuple += get_func_cost(saop->opfuncid) * u_sess->attr.attr_sql.cpu_operator_cost *
|
||||
estimate_array_length(arraynode) * 0.5;
|
||||
} else if (IsA(node, Aggref) || IsA(node, WindowFunc)) {
|
||||
/*
|
||||
* Aggref and WindowFunc nodes are (and should be) treated like Vars,
|
||||
* ie, zero execution cost in the current model, because they behave
|
||||
* essentially like Vars in execQual.c. We disregard the costs of
|
||||
* their input expressions for the same reason. The actual execution
|
||||
* costs of the aggregate/window functions and their arguments have to
|
||||
* be factored into plan-node-specific costing of the Agg or WindowAgg
|
||||
* plan node.
|
||||
*/
|
||||
return false; /* don't recurse into children */
|
||||
} else if (IsA(node, CoerceViaIO)) {
|
||||
CoerceViaIO* iocoerce = (CoerceViaIO*)node;
|
||||
Oid iofunc;
|
||||
Oid typioparam;
|
||||
bool typisvarlena = false;
|
||||
set_sa_opfuncid(saop);
|
||||
context->total.per_tuple += get_func_cost(saop->opfuncid) * u_sess->attr.attr_sql.cpu_operator_cost *
|
||||
estimate_array_length(arraynode) * 0.5;
|
||||
} else if (IsA(node, Aggref) || IsA(node, WindowFunc)) {
|
||||
// 如果是 Aggref 或 WindowFunc 类型,返回 false 表示不需要继续处理子节点
|
||||
return false;
|
||||
} else if (IsA(node, CoerceViaIO)) {
|
||||
// 如果是 CoerceViaIO 类型,计算输入和输出函数的评估成本并添加到总成本中
|
||||
CoerceViaIO* iocoerce = (CoerceViaIO*)node;
|
||||
Oid iofunc;
|
||||
Oid typioparam;
|
||||
bool typisvarlena = false;
|
||||
|
||||
/* check the result type's input function */
|
||||
getTypeInputInfo(iocoerce->resulttype, &iofunc, &typioparam);
|
||||
context->total.per_tuple += get_func_cost(iofunc) * u_sess->attr.attr_sql.cpu_operator_cost;
|
||||
/* check the input type's output function */
|
||||
getTypeOutputInfo(exprType((Node*)iocoerce->arg), &iofunc, &typisvarlena);
|
||||
context->total.per_tuple += get_func_cost(iofunc) * u_sess->attr.attr_sql.cpu_operator_cost;
|
||||
} else if (IsA(node, ArrayCoerceExpr)) {
|
||||
ArrayCoerceExpr* acoerce = (ArrayCoerceExpr*)node;
|
||||
Node* arraynode = (Node*)acoerce->arg;
|
||||
getTypeInputInfo(iocoerce->resulttype, &iofunc, &typioparam);
|
||||
context->total.per_tuple += get_func_cost(iofunc) * u_sess->attr.attr_sql.cpu_operator_cost;
|
||||
|
||||
if (OidIsValid(acoerce->elemfuncid))
|
||||
context->total.per_tuple += get_func_cost(acoerce->elemfuncid) * u_sess->attr.attr_sql.cpu_operator_cost *
|
||||
estimate_array_length(arraynode);
|
||||
} else if (IsA(node, RowCompareExpr)) {
|
||||
/* Conservatively assume we will check all the columns */
|
||||
RowCompareExpr* rcexpr = (RowCompareExpr*)node;
|
||||
ListCell* lc = NULL;
|
||||
getTypeOutputInfo(exprType((Node*)iocoerce->arg), &iofunc, &typisvarlena);
|
||||
context->total.per_tuple += get_func_cost(iofunc) * u_sess->attr.attr_sql.cpu_operator_cost;
|
||||
} else if (IsA(node, ArrayCoerceExpr)) {
|
||||
// 如果是 ArrayCoerceExpr 类型,根据元素函数 ID 计算评估成本并添加到总成本中
|
||||
ArrayCoerceExpr* acoerce = (ArrayCoerceExpr*)node;
|
||||
Node* arraynode = (Node*)acoerce->arg;
|
||||
|
||||
foreach (lc, rcexpr->opnos) {
|
||||
Oid opid = lfirst_oid(lc);
|
||||
if (OidIsValid(acoerce->elemfuncid))
|
||||
context->total.per_tuple += get_func_cost(acoerce->elemfuncid) * u_sess->attr.attr_sql.cpu_operator_cost *
|
||||
estimate_array_length(arraynode);
|
||||
} else if (IsA(node, RowCompareExpr)) {
|
||||
// 如果是 RowCompareExpr 类型,计算操作符函数的评估成本并添加到总成本中
|
||||
RowCompareExpr* rcexpr = (RowCompareExpr*)node;
|
||||
ListCell* lc = NULL;
|
||||
|
||||
context->total.per_tuple += get_func_cost(get_opcode(opid)) * u_sess->attr.attr_sql.cpu_operator_cost;
|
||||
}
|
||||
} else if (IsA(node, CurrentOfExpr)) {
|
||||
/* Report high cost to prevent selection of anything but TID scan */
|
||||
context->total.startup += g_instance.cost_cxt.disable_cost;
|
||||
} else if (IsA(node, SubLink)) {
|
||||
/* This routine should not be applied to un-planned expressions */
|
||||
ereport(ERROR,
|
||||
(errmodule(MOD_OPT),
|
||||
errcode(ERRCODE_OPTIMIZER_INCONSISTENT_STATE),
|
||||
errmsg("cannot handle unplanned sub-select when costing quals")));
|
||||
} else if (IsA(node, SubPlan)) {
|
||||
/*
|
||||
* A subplan node in an expression typically indicates that the
|
||||
* subplan will be executed on each evaluation, so charge accordingly.
|
||||
* (Sub-selects that can be executed as InitPlans have already been
|
||||
* removed from the expression.)
|
||||
*/
|
||||
SubPlan* subplan = (SubPlan*)node;
|
||||
foreach (lc, rcexpr->opnos) {
|
||||
Oid opid = lfirst_oid(lc);
|
||||
|
||||
context->total.startup += subplan->startup_cost;
|
||||
context->total.per_tuple += subplan->per_call_cost;
|
||||
|
||||
/*
|
||||
* We don't want to recurse into the testexpr, because it was already
|
||||
* counted in the SubPlan node's costs. So we're done.
|
||||
*/
|
||||
return false;
|
||||
} else if (IsA(node, AlternativeSubPlan)) {
|
||||
/*
|
||||
* Arbitrarily use the first alternative plan for costing. (We should
|
||||
* certainly only include one alternative, and we don't yet have
|
||||
* enough information to know which one the executor is most likely to
|
||||
* use.)
|
||||
*/
|
||||
AlternativeSubPlan* asplan = (AlternativeSubPlan*)node;
|
||||
|
||||
return cost_qual_eval_walker((Node*)linitial(asplan->subplans), context);
|
||||
context->total.per_tuple += get_func_cost(get_opcode(opid)) * u_sess->attr.attr_sql.cpu_operator_cost;
|
||||
}
|
||||
} else if (IsA(node, CurrentOfExpr)) {
|
||||
// 如果是 CurrentOfExpr 类型,添加禁用成本到启动成本中
|
||||
context->total.startup += g_instance.cost_cxt.disable_cost;
|
||||
} else if (IsA(node, SubLink)) {
|
||||
// 如果是 SubLink 类型,报错,因为无法处理未计划的子查询
|
||||
ereport(ERROR,
|
||||
(errmodule(MOD_OPT),
|
||||
errcode(ERRCODE_OPTIMIZER_INCONSISTENT_STATE),
|
||||
errmsg("cannot handle unplanned sub-select when costing quals")));
|
||||
} else if (IsA(node, SubPlan)) {
|
||||
// 如果是 SubPlan 类型,将子查询的启动成本和每次调用成本添加到总成本中
|
||||
SubPlan* subplan = (SubPlan*)node;
|
||||
|
||||
/* recurse into children */
|
||||
return expression_tree_walker(node, (bool (*)())cost_qual_eval_walker, (void*)context);
|
||||
context->total.startup += subplan->startup_cost;
|
||||
context->total.per_tuple += subplan->per_call_cost;
|
||||
|
||||
// 返回 false 表示不需要继续递归处理子节点
|
||||
return false;
|
||||
} else if (IsA(node, AlternativeSubPlan)) {
|
||||
// 如果是 AlternativeSubPlan 类型,继续处理第一个替代子计划节点
|
||||
return cost_qual_eval_walker((Node*)linitial(asplan->subplans), context);
|
||||
}
|
||||
|
||||
// 继续递归处理节点的子节点
|
||||
return expression_tree_walker(node, (bool (*)())cost_qual_eval_walker, (void*)context);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -5090,18 +5084,23 @@ static bool cost_qual_eval_walker(Node* node, cost_qual_eval_context* context)
|
|||
* some of the quals. We assume baserestrictcost was previously set
|
||||
* by set_baserel_size_estimates().
|
||||
*/
|
||||
// 这是一个名为 get_restriction_qual_cost 的静态函数,计算限制条件的成本
|
||||
static void get_restriction_qual_cost(
|
||||
PlannerInfo* root, RelOptInfo* baserel, ParamPathInfo* param_info, QualCost* qpqual_cost)
|
||||
{
|
||||
// 如果 param_info 不为空,则计算 param_info 中的限制条件成本
|
||||
if (param_info != NULL) {
|
||||
/* Include costs of pushed-down clauses */
|
||||
// 调用 cost_qual_eval 函数计算限制条件的成本,并存储在 qpqual_cost 中
|
||||
cost_qual_eval(qpqual_cost, param_info->ppi_clauses, root);
|
||||
|
||||
// 将基本关系的限制条件成本加到 qpqual_cost 中
|
||||
qpqual_cost->startup += baserel->baserestrictcost.startup;
|
||||
qpqual_cost->per_tuple += baserel->baserestrictcost.per_tuple;
|
||||
} else
|
||||
} else {
|
||||
// 如果 param_info 为空,则直接将基本关系的限制条件成本赋值给 qpqual_cost
|
||||
*qpqual_cost = baserel->baserestrictcost;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* compute_semi_anti_join_factors
|
||||
|
|
@ -5222,31 +5221,39 @@ void compute_semi_anti_join_factors(PlannerInfo* root, RelOptInfo* outerrel, Rel
|
|||
* unmatched outer tuple is cheap to process, whereas otherwise it's probably
|
||||
* expensive.
|
||||
*/
|
||||
// 这是一个名为 has_indexed_join_quals 的函数,检查是否有索引关联的连接条件
|
||||
bool has_indexed_join_quals(NestPath* joinpath)
|
||||
{
|
||||
// 获取连接路径的关联关系 ID 集合
|
||||
Relids joinrelids = joinpath->path.parent->relids;
|
||||
// 获取连接路径的内部路径
|
||||
Path* innerpath = joinpath->innerjoinpath;
|
||||
// 用于存储索引条件的列表
|
||||
List* indexclauses = NIL;
|
||||
// 标志,表示是否找到至少一个索引条件
|
||||
bool found_one = false;
|
||||
// 用于遍历列表的迭代器
|
||||
ListCell* lc = NULL;
|
||||
|
||||
/* If join still has quals to evaluate, it's not fast */
|
||||
// 如果连接路径有连接限制条件,则返回 false
|
||||
if (joinpath->joinrestrictinfo != NIL)
|
||||
return false;
|
||||
/* Nor if the inner path isn't parameterized at all */
|
||||
|
||||
// 如果内部路径没有 param_info,则返回 false
|
||||
if (innerpath->param_info == NULL)
|
||||
return false;
|
||||
|
||||
/* Find the indexclauses list for the inner scan */
|
||||
// 根据内部路径的类型,获取相应的索引条件列表
|
||||
switch (innerpath->pathtype) {
|
||||
case T_IndexScan:
|
||||
case T_IndexOnlyScan:
|
||||
indexclauses = ((IndexPath*)innerpath)->indexclauses;
|
||||
break;
|
||||
case T_BitmapHeapScan: {
|
||||
/* Accept only a simple bitmap scan, not AND/OR cases */
|
||||
// 如果内部路径是 BitmapHeapScan,则获取其 bitmapqual
|
||||
Path* bmqual = ((BitmapHeapPath*)innerpath)->bitmapqual;
|
||||
|
||||
// 如果 bitmapqual 是 IndexPath,则获取其索引条件列表
|
||||
if (IsA(bmqual, IndexPath))
|
||||
indexclauses = ((IndexPath*)bmqual)->indexclauses;
|
||||
else
|
||||
|
|
|
|||
Loading…
Reference in New Issue