From 511eda737198db007a03e96ebafd57b33fae7957 Mon Sep 17 00:00:00 2001 From: yangke1125 <2987765698@qq.com> Date: Fri, 29 Sep 2023 23:44:10 +0800 Subject: [PATCH] Enter --- src/gausskernel/optimizer/plan/planner.cpp | 1802 +++++++++----------- 1 file changed, 839 insertions(+), 963 deletions(-) diff --git a/src/gausskernel/optimizer/plan/planner.cpp b/src/gausskernel/optimizer/plan/planner.cpp index fa35782e8..a00df34a6 100644 --- a/src/gausskernel/optimizer/plan/planner.cpp +++ b/src/gausskernel/optimizer/plan/planner.cpp @@ -930,10 +930,13 @@ forboth(lp, glob->subplans, lr, glob->subroots) /* Assign plan node id for each plan node */ #ifdef ENABLE_MULTIPLE_NODES + // 检查是否定义了 ENABLE_MULTIPLE_NODES 宏 if (IS_PGXC_COORDINATOR && root->query_level == 1) { #else + // 如果没有定义 ENABLE_MULTIPLE_NODES 宏,或者查询级别为1 if (root->query_level == 1) { #endif + // 调用 finalize_node_id 函数,处理一些参数 finalize_node_id(top_plan, &plan_node_id, &parent_node_id, @@ -954,20 +957,25 @@ forboth(lp, glob->subplans, lr, glob->subroots) } #endif - /* ... and the subplans (both regular subplans and initplans) */ + // 使用断言确保 glob->subplans 和 glob->subroots 的长度相等 AssertEreport(list_length(glob->subplans) == list_length(glob->subroots), MOD_OPT, "The length of subplans is not equal to that of subroots when standardize planner"); + + // 遍历 glob->subplans 和 glob->subroots 列表 forboth(lp, glob->subplans, lr, glob->subroots) { + // 获取 subplan 和 subroot Plan* subplan = (Plan*)lfirst(lp); PlannerInfo* subroot = (PlannerInfo*)lfirst(lr); #ifdef STREAMPLAN - /* We set reference of some plans in finalize_node_id. For undone plan, set plan references */ + + // 如果满足以下条件之一,则执行以下操作 if (subplan_ids[i] == 0 || IsA(subplan, RecursiveUnion) || IsA(subplan, StartWithOp)) { if (STREAM_RECURSIVECTE_SUPPORTED && IsA(subplan, RecursiveUnion)) { + // 创建一个 RecursiveRefContext 结构体,并进行初始化 RecursiveRefContext context; errno_t rc = EOK; rc = memset_s(&context, sizeof(RecursiveRefContext), 0, sizeof(RecursiveRefContext)); @@ -980,34 +988,30 @@ forboth(lp, glob->subplans, lr, glob->subroots) context.initplans = init_plan; context.subplans = glob->subplans; - /* EntryPoint for iterating the underlying plan node */ + // 调用 set_recursive_cteplan_ref 函数,设置递归通用表达式计划的引用 set_recursive_cteplan_ref(subplan, &context); } else { + // 尝试对计划进行向量化处理,并将结果存储回 subplans 列表中 subplan = try_vectorize_plan(subplan, subroot->parse, true); lfirst(lp) = set_plan_references(subroot, subplan); } - /* for start with processing */ + // 如果 subplan 是 StartWithOp 类型的计划,则执行处理操作 if (IsA(subplan, StartWithOp)) { ProcessStartWithOpMixWork(root, top_plan, subroot, (StartWithOp *)subplan); } - /* - * When enable_stream_operator = off, Subquery SQL is not processed by finalize_node_id. - * In this case we default each subquery to a SQL statement pushed down to the DN. - * Here we may misjudge the subquery executed only on the CN, - * but in order to maintain The independence of the set_plan_references function, - * there is no further judgment on such subqueries, and it is considered that the sub-query is issued to the - * DN. This operation does not affect the correctness. - */ + // 增加 max_push_sql_num 计数器 max_push_sql_num++; } #endif + // 增加 i 计数器 i++; } - /* Juse copy these fields only when the memory context total size meets the dropping condition. */ + // 如果需要释放内存上下文 if (IS_NEED_FREE_MEMORY_CONTEXT(glob->plannerContext->plannerMemContext)) { + // 复制 top_plan 及其他一些数据结构,以避免释放内存冲突 top_plan = (Plan*)copyObject(top_plan); glob->finalrtable = (List*)copyObject(glob->finalrtable); glob->resultRelations = (List*)copyObject(glob->resultRelations); @@ -1019,11 +1023,13 @@ forboth(lp, glob->subplans, lr, glob->subroots) init_plan = (List*)copyObject(init_plan); } + // 将 parse_hint_warning 和 glob->hint_warning 合并 glob->hint_warning = list_concat(parse_hint_warning, (List*)copyObject(glob->hint_warning)); - /* build the PlannedStmt result */ + // 创建 PlannedStmt 结构体并进行初始化 result = makeNode(PlannedStmt); + // 设置 PlannedStmt 结构体的各个字段 result->commandType = parse->commandType; result->queryId = parse->queryId; result->uniqueSQLId = parse->uniqueSQLId; @@ -1044,6 +1050,7 @@ forboth(lp, glob->subplans, lr, glob->subroots) result->nParamExec = glob->nParamExec; result->noanalyze_rellist = (List*)copyObject(t_thrd.postgres_cxt.g_NoAnalyzeRelNameList); + // 如果是 PGXC_COORDINATOR 节点且满足条件,则设置 nodesDefinition if (IS_PGXC_COORDINATOR && (t_thrd.proc->workingVersionNum < 92097 || total_num_streams > 0)) { result->nodesDefinition = get_all_datanodes_def(); @@ -1054,37 +1061,43 @@ forboth(lp, glob->subplans, lr, glob->subroots) result->gather_count = gather_count; result->num_plannodes = num_plannodes; + // 填充 PlanBucketmap FillPlanBucketmap(result, node_group_info_context); + // 设置 query_string 为 NULL,MaxBloomFilterNum 为 glob->bloomfilter.bloomfilter_index + 1 result->query_string = NULL; result->MaxBloomFilterNum = root->glob->bloomfilter.bloomfilter_index + 1; - /* record which suplan belongs to which thread */ + #ifdef ENABLE_MULTIPLE_NODES + // 如果启用多节点支持,则执行以下操作 if (IS_STREAM_PLAN) { #else + // 如果不启用多节点支持且 num_streams 大于0,则执行以下操作 if (result->num_streams > 0) { #endif + // 遍历子计划列表,并将子计划的编号存储到 subplan_ids 中 for (i = 1; i <= list_length(result->subplans); i++) result->subplan_ids = lappend_int(result->subplan_ids, subplan_ids[i]); result->initPlan = init_plan; } pfree_ext(subplan_ids); - /* dynamic query dop main entry */ + // 如果启用了动态 SMP(Symmetric Multi-Processing),则优化计划的 DOP(Degree of Parallelism) if (IsDynamicSmpEnabled()) { - /* the main plan */ OptimizePlanDop(result); } - /* Query mem calculation and control main entry */ + // 如果是流式计划且使用了查询内存,则设置查询内存参数 if (IS_STREAM_PLAN && use_query_mem) { result->assigned_query_mem[1] = max_mem; result->assigned_query_mem[0] = available_mem; + // 输出调试信息 ereport(DEBUG2, (errmodule(MOD_MEM), errmsg("[standard_planner]Passing in max mem %d and available mem %d", max_mem, available_mem))); CalculateQueryMemMain(result, use_tenant, false); + // 输出调试信息 ereport(DEBUG2, (errmodule(MOD_MEM), errmsg("[standard_planner]Calucated query max %d and min mem %d", @@ -1092,7 +1105,7 @@ forboth(lp, glob->subplans, lr, glob->subroots) result->query_mem[1]))); } - /* data redistribution for DFS table. */ + // 如果启用了集群调整且满足条件,则设置 dataDestRelIndex if (u_sess->attr.attr_sql.enable_cluster_resize && root->query_level == 1 && root->parse->commandType == CMD_INSERT) { result->dataDestRelIndex = root->dataDestRelIndex; @@ -1100,13 +1113,16 @@ forboth(lp, glob->subplans, lr, glob->subroots) result->dataDestRelIndex = 0; } + // 设置查询的 DOP(Degree of Parallelism) result->query_dop = u_sess->opt_cxt.query_dop; + // 如果存在触发器,则标记为 has_obsrel if (u_sess->opt_cxt.has_obsrel) { result->has_obsrel = true; } result->plan_hint_warning = glob->hint_warning; + // 恢复节点组选项设置 ng_restore_nodegroup_options(ngbk_is_multiple_nodegroup_scenario, ngbk_different_nodegroup_count, ngbk_in_redistribution_group_distribution, @@ -1114,58 +1130,74 @@ forboth(lp, glob->subplans, lr, glob->subroots) ngbk_query_union_set_group_distribution, ngbk_single_node_distribution); + // 释放优化器上下文 deinit_optimizer_context(glob); + // 如果启用了检查隐式类型转换且 g_index_vars 不为空,则检查索引列 if (enable_check_implicit_cast() && g_index_vars != NIL) check_index_column(); + // 设置 isRowTriggerShippable result->isRowTriggerShippable = parse->isRowTriggerShippable; return result; } + /* * We will not rewrite full joins if the query tree contain these members now. */ bool fulljoin_2_left_union_right_anti_support(Query* parse) { + // 检查命令类型是否是SELECT、INSERT或MERGE,如果不是则返回false if (parse->commandType != CMD_SELECT && parse->commandType != CMD_INSERT && parse->commandType != CMD_MERGE) return false; + // 如果存在utilityStmt,则返回false if (parse->utilityStmt != NULL) return false; + // 如果查询中包含递归表达式(WITH RECURSIVE),则返回false if (parse->hasRecursive) return false; + // 如果查询中包含修改公共表表达式(WITH),则返回false if (parse->hasModifyingCTE) return false; + // 如果查询中包含FOR UPDATE/FOR SHARE子句,则返回false if (parse->hasForUpdate) return false; + // 如果查询中包含RETURNING子句,则返回false if (parse->returningList != NIL) return false; + // 如果查询中包含行标记(FOR UPDATE的行标记),则返回false if (parse->rowMarks != NIL) return false; + // 如果查询中包含需要保存命令ID的标志,则返回false if (parse->has_to_save_cmd_id) return false; + // 如果查询中包含相等变量的列表,则返回false if (parse->equalVars != NIL) return false; + // 如果以上条件都不满足,则返回true return true; } -/* - * return true if the funcexpr is a implicit conversion.$ - */ static bool IsImplicitConversion(FuncExpr* expr) { + // 检查函数表达式的参数个数是否为1,且函数格式是否为COERCE_IMPLICIT_CAST if (list_length(expr->args) != 1 || expr->funcformat != COERCE_IMPLICIT_CAST) { return false; } + // 获取源数据类型和目标数据类型 Oid srctype = exprType((Node*)linitial(expr->args)); Oid targettype = expr->funcresulttype; + // 在系统缓存中查找是否存在从源数据类型到目标数据类型的隐式转换 HeapTuple tuple = SearchSysCache2(CASTSOURCETARGET, ObjectIdGetDatum(srctype), ObjectIdGetDatum(targettype)); + // 如果找到匹配的隐式转换规则 if (HeapTupleIsValid(tuple)) { Form_pg_cast castForm = (Form_pg_cast)GETSTRUCT(tuple); + // 检查转换函数和转换上下文是否符合隐式转换的要求 if (castForm->castfunc == expr->funcid && castForm->castcontext == COERCION_CODE_IMPLICIT) { ReleaseSysCache(tuple); return true; @@ -1174,76 +1206,84 @@ static bool IsImplicitConversion(FuncExpr* expr) ReleaseSysCache(tuple); } + // 如果没有找到匹配的隐式转换规则,则返回false return false; } -/* - * preprocessOperator - * Recursively scan the query and do subquery_planner's - * preprocessing work on each opexpr node, regenerate - * these nodes when the string_digit_to_numeric is on. - */ bool PreprocessOperator(Node* node, void* context) { + // 如果节点为空,则返回false if (node == NULL) { return false; } + // 如果节点是Query类型,则递归处理其子节点 if (IsA(node, Query)) { return query_tree_walker((Query*)node, (bool (*)())PreprocessOperator, (void*)context, 0); - } else if (IsA(node, OpExpr)) { + } + // 如果节点是OpExpr类型,则进行以下处理 + else if (IsA(node, OpExpr)) { OpExpr* expr = (OpExpr*)node; - /* Only regenerate the operator when opresulttype is bool. */ + // 如果OpExpr的参数个数为2,结果类型为布尔型,且输入协议为0 if (list_length(expr->args) == 2 && expr->opresulttype == BOOLOID && expr->inputcollid == 0) { Node* ltree = (Node*)list_nth(expr->args, 0); Node* rtree = (Node*)list_nth(expr->args, 1); - /* Determine if the left and right subtrees are implicit type conversion */ + // 检查左操作数是否为FuncExpr,并且是否满足隐式转换的条件 if (IsA(ltree, FuncExpr) && IsImplicitConversion((FuncExpr*)ltree) && ((FuncExpr*)ltree)->funcresulttype != NUMERICOID) { ltree = (Node*)linitial(((FuncExpr*)ltree)->args); } + // 检查右操作数是否为FuncExpr,并且是否满足隐式转换的条件 if (IsA(rtree, FuncExpr) && IsImplicitConversion((FuncExpr*)rtree) && ((FuncExpr*)rtree)->funcresulttype != NUMERICOID) { rtree = (Node*)linitial(((FuncExpr*)rtree)->args); } + // 获取左操作数和右操作数的数据类型 Oid ltypeId = exprType(ltree); Oid rtypeId = exprType(rtree); + // 如果左操作数为整数类型,右操作数为字符类型,或者左操作数为字符类型,右操作数为整数类型 if ((IsIntType(ltypeId) && IsCharType(rtypeId)) || (IsIntType(rtypeId) && IsCharType(ltypeId))) { + // 在系统缓存中查找该操作符的信息 HeapTuple tp = SearchSysCache1(OPEROID, ObjectIdGetDatum(expr->opno)); + // 如果找到匹配的操作符信息 if (HeapTupleIsValid(tp)) { Form_pg_operator optup = (Form_pg_operator)GETSTRUCT(tp); List* name = list_make1(makeString(NameStr(optup->oprname))); - /* Regenerate the opexpr node. */ + // 创建一个新的OpExpr节点 OpExpr* newNode = (OpExpr*)make_op(NULL, name, ltree, rtree, expr->location, true); + // 获取新节点的左操作数和右操作数 Node* lexpr = (Node*)list_nth(newNode->args, 0); Node* rexpr = (Node*)list_nth(newNode->args, 1); ltypeId = exprType(lexpr); rtypeId = exprType(rexpr); + // 如果新节点的结果类型为布尔型,左右操作数的数据类型为数值型 if (newNode->opresulttype == BOOLOID && ltypeId == NUMERICOID && rtypeId == NUMERICOID) { - - /* Determine if the new subtrees are implicit type conversion */ + // 如果左操作数是FuncExpr并且满足隐式转换条件,则设置输入协议 if (IsA(lexpr, FuncExpr) && IsImplicitConversion((FuncExpr*)lexpr)) { exprSetInputCollation((Node*)list_nth(newNode->args, 0), exprCollation(ltree)); } + // 如果右操作数是FuncExpr并且满足隐式转换条件,则设置输入协议 if (IsA(rexpr, FuncExpr) && IsImplicitConversion((FuncExpr*)rexpr)) { exprSetInputCollation((Node*)list_nth(newNode->args, 1), exprCollation(rtree)); } + // 将新节点的内容复制到原节点中 errno_t errorno = EOK; errorno = memcpy_s(node, sizeof(OpExpr), (Node*)newNode, sizeof(OpExpr)); securec_check_c(errorno, "\0", "\0"); } + // 释放新节点和名称列表 pfree_ext(newNode); list_free_ext(name); ReleaseSysCache(tp); @@ -1252,57 +1292,49 @@ bool PreprocessOperator(Node* node, void* context) } } + // 递归处理表达式中的其他节点 return expression_tree_walker(node, (bool (*)())PreprocessOperator, (void*)context); } -/** - * Check whether the current nodegroup state support recursive cte. - * This must be called after calling ng_init_nodegroup_optimizer and - * is_dngather_support is assigned. - */ void check_is_support_recursive_cte(PlannerInfo* root) { + // 如果不是流式计划或不在WITH RECURSIVE表达式内部,则直接返回 if (!IS_STREAM_PLAN || !root->is_under_recursive_cte) { return; } + // 根据不同的条件标记流式计划不支持 errno_t sprintf_rc = sprintf_s(u_sess->opt_cxt.not_shipping_info->not_shipping_reason, NOTPLANSHIPPING_LENGTH, "With-Recursive under multi-nodegroup scenario is not shippable"); int different_nodegroup_count = ng_get_different_nodegroup_count(); - /* 1. Installation nodegroup, compute nodegroup, single nodegroup. */ + // 如果不同节点组的数量大于2,则标记为不支持流式计划 if (different_nodegroup_count > 2) { securec_check_ss_c(sprintf_rc, "\0", "\0"); mark_stream_unsupport(); return; } - /* 2. Installation nodegroup, compute nodegroup. */ + // 如果有两个不同的节点组,并且未设置单节点分布,则标记为不支持流式计划 if (different_nodegroup_count == 2 && ng_get_single_node_distribution() == NULL) { securec_check_ss_c(sprintf_rc, "\0", "\0"); mark_stream_unsupport(); return; } - /* 3. Installation nodegroup, single nodegroup which is used */ + // 如果有两个不同的节点组,并且设置了dn_gather支持,则标记为不支持流式计划 if (different_nodegroup_count == 2 && u_sess->opt_cxt.is_dngather_support == true) { securec_check_ss_c(sprintf_rc, "\0", "\0"); mark_stream_unsupport(); return; } - /* 4. Installation nodegroup, single nodegroup but not used. */ - /* Installation nodegroup. */ return; } -/* - * Process set hint at top level. DO NOT handle subquery. - * apply_set_hint and recover_set_hint should wrap around pg_plan_query - * Returns the guc level. - */ int apply_set_hint(const Query* parse) { + // 获取查询中的提示状态 HintState* hintstate = parse->hintState; if (hintstate == NULL) { return -1; @@ -1311,16 +1343,20 @@ int apply_set_hint(const Query* parse) int ret; ListCell* lc = NULL; gucNestLevel = NewGUCNestLevel(); + + // 遍历查询中的提示列表 foreach (lc, hintstate->set_hint) { SetHint* hint = (SetHint*)lfirst(lc); + // 如果提示名称为"node_name",则设置节点名称 if (unlikely(strcmp(hint->name, "node_name") == 0)) { u_sess->attr.attr_common.node_name = hint->value; } else { + // 否则,设置相应的配置选项 ret = set_config_option(hint->name, hint->value, - PGC_USERSET, /* for now set hint only support */ - PGC_S_SESSION, /* session-level userset guc */ - GUC_ACTION_SAVE, /* need to rollback later */ + PGC_USERSET, + PGC_S_SESSION, + GUC_ACTION_SAVE, true, WARNING, false); @@ -1332,6 +1368,7 @@ int apply_set_hint(const Query* parse) void recover_set_hint(int savedNestLevel) { + // 恢复提示设置到之前的状态 if (savedNestLevel < 0) { return; } @@ -1339,61 +1376,40 @@ void recover_set_hint(int savedNestLevel) u_sess->attr.attr_common.node_name = ""; } -/* -------------------- - * subquery_planner - * Invokes the planner on a subquery. We recurse to here for each - * sub-SELECT found in the query tree. - * - * glob is the global state for the current planner run. - * parse is the querytree produced by the parser & rewriter. - * parent_root is the immediate parent Query's info (NULL at the top level). - * hasRecursion is true if this is a recursive WITH query. - * tuple_fraction is the fraction of tuples we expect will be retrieved. - * tuple_fraction is interpreted as explained for grouping_planner, below. - * - * If subroot isn't NULL, we pass back the query's final PlannerInfo struct; - * among other things this tells the output sort ordering of the plan. - * - * Basically, this routine does the stuff that should only be done once - * per Query object. It then calls grouping_planner. At one time, - * grouping_planner could be invoked recursively on the same Query object; - * that's not currently true, but we keep the separation between the two - * routines anyway, in case we need it again someday. - * - * subquery_planner will be called recursively to handle sub-Query nodes - * found within the query's expressions and rangetable. - * - * Returns a query plan. - * -------------------- - */ Plan* subquery_planner(PlannerGlobal* glob, Query* parse, PlannerInfo* parent_root, bool hasRecursion, double tuple_fraction, PlannerInfo** subroot, int options, ItstDisKey* diskeys, List* subqueryRestrictInfo) { + // 获取已有子计划的数量 int num_old_subplans = list_length(glob->subplans); - PlannerInfo* root = NULL; - Plan* plan = NULL; - List* newHaving = NIL; - bool hasOuterJoins = false; - bool hasResultRTEs = false; - ListCell* l = NULL; - StringInfoData buf; - char RewriteContextName[NAMEDATALEN] = {0}; - MemoryContext QueryRewriteContext = NULL; - MemoryContext oldcontext = NULL; - errno_t rc = EOK; + + // 初始化一些变量 + PlannerInfo* root = NULL; // 主查询的PlannerInfo + Plan* plan = NULL; // 子查询生成的执行计划 + List* newHaving = NIL; // 用于处理HAVING子句的列表 + bool hasOuterJoins = false; // 是否包含外连接 + bool hasResultRTEs = false; // 是否包含结果表达式 + ListCell* l = NULL; // 用于遍历列表的ListCell指针 + StringInfoData buf; // 用于构建字符串的StringInfoData结构 + char RewriteContextName[NAMEDATALEN] = {0}; // 重写上下文的名称 + MemoryContext QueryRewriteContext = NULL; // 查询重写的内存上下文 + MemoryContext oldcontext = NULL; // 保存当前内存上下文 + errno_t rc = EOK; // 用于处理错误码 + + /* We used DEBUG5 log to print SQL after each rewrite */ -#define DEBUG_QRW(message) \ - do { \ - if (log_min_messages <= DEBUG5) { \ - initStringInfo(&buf); \ - deparse_query(root->parse, &buf, NIL, false, false, NULL, true); \ - ereport(DEBUG5, (errmodule(MOD_OPT_REWRITE), errmsg("%s: %s", message, buf.data))); \ - pfree_ext(buf.data); \ - } \ - } while (0) + // 针对某些调试级别,输出查询重写信息的宏 + #define DEBUG_QRW(message) \ + do { \ + if (log_min_messages <= DEBUG5) { \ + initStringInfo(&buf); \ + deparse_query(root->parse, &buf, NIL, false, false, NULL, true); \ + ereport(DEBUG5, (errmodule(MOD_OPT_REWRITE), errmsg("%s: %s", message, buf.data))); \ + pfree_ext(buf.data); \ + } \ + } while (0) - /* Create a PlannerInfo data structure for this subquery */ + // 创建PlannerInfo结构并初始化 root = makeNode(PlannerInfo); root->parse = parse; root->glob = glob; @@ -1412,30 +1428,21 @@ Plan* subquery_planner(PlannerGlobal* glob, Query* parse, PlannerInfo* parent_ro root->param_upper = NULL; root->hasRownumQual = false; - /* - * Apply memory context for query rewrite in optimizer. - * OptimizerContext is NULL in PBE condition which we need to consider. - */ + // 生成查询重写上下文的名称 rc = snprintf_s(RewriteContextName, NAMEDATALEN, NAMEDATALEN - 1, "QueryRewriteContext_%d", root->query_level); securec_check_ss(rc, "\0", "\0"); + // 创建查询重写上下文 QueryRewriteContext = AllocSetContextCreate(CurrentMemoryContext, RewriteContextName, ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE); + // 切换内存上下文到查询重写上下文 oldcontext = MemoryContextSwitchTo(QueryRewriteContext); - /* - * Mark the current PlannerInfo is working for a query-block in recursive CTE planning, - * in general we want to let the sub-planning stages to know we are under a recursive-cte, - * planning, two case need - * [1]. Call subquery_planner() to planning the query block inside of with-block - * [2]. Call subquery_planner() to planning each query-block consists of the union - * operation, so consequtially inherit the "is_recursive_cte" properties from - * parent root - */ + // 检查是否在递归查询中,设置相关标志 if (hasRecursion || (parent_root && parent_root->is_under_recursive_cte)) { root->is_under_recursive_cte = true; root->is_under_recursive_tree = parent_root->is_under_recursive_tree; @@ -1443,11 +1450,15 @@ Plan* subquery_planner(PlannerGlobal* glob, Query* parse, PlannerInfo* parent_ro root->is_under_recursive_cte = false; } + // 检查是否支持递归CTE check_is_support_recursive_cte(root); + // 设置递归查询别名索引 #ifdef PGXC root->rs_alias_index = 1; #endif + + // 设置是否有递归查询 root->hasRecursion = hasRecursion; if (hasRecursion) root->wt_param_id = SS_assign_special_param(root); @@ -1457,130 +1468,97 @@ Plan* subquery_planner(PlannerGlobal* glob, Query* parse, PlannerInfo* parent_ro root->non_recursive_plan = NULL; root->subqueryRestrictInfo = subqueryRestrictInfo; - /* Mark current planner root is correlated as well */ + // 如果父查询也在递归CTE中,并且是关联的,则标记为关联的 if (parent_root != NULL && parent_root->is_under_recursive_cte && parent_root->is_correlated) { root->is_correlated = true; } + // 调试输出:查询重写前 DEBUG_QRW("Before rewrite"); + // 预处理常量参数 preprocess_const_params(root, (Node*)parse->jointree); + // 调试输出:常量参数替换后 DEBUG_QRW("After const params replace "); - /* - * If there is a WITH list, process each WITH query and build an initplan - * SubPlan structure for it. For stream plan, it's already be replaced, so - * no need to do this. - * - * For recursive cte we still process this in same way - */ + // 处理通用表达式(CTE) if (parse->cteList) { SS_process_ctes(root); } + // 调试输出:CTE替换后 DEBUG_QRW("After CTE substitution"); - /* - * If the FROM clause is empty, replace it with a dummy RTE_RESULT RTE, so - * that we don't need so many special cases to deal with that situation. - */ + // 替换空的关联表达式 replace_empty_jointree(parse); #ifdef STREAMPLAN - /* - * Since count(distinct) conversion can push down subquery, for sake of - * duplicate of sublink pullup, we put it ahead of sublink pullup - */ + + // 如果是流式计划,并且存在聚合操作,转换多个COUNT DISTINCT if (IS_STREAM_PLAN && parse->hasAggs) { convert_multi_count_distinct(root); DEBUG_QRW("After multi count distinct rewrite"); } #endif - /* - * Look for ANY and EXISTS SubLinks in WHERE and JOIN/ON clauses, and try - * to transform them into joins. Note that this step does not descend - * into subqueries; if we pull up any subqueries below, their SubLinks are - * processed just before pulling them up. - */ + // 如果存在子查询链接,提取子查询 if (parse->hasSubLinks) { pull_up_sublinks(root); DEBUG_QRW("After sublink pullup"); } - /* Reduce orderby clause in subquery for join */ + // 减少ORDER BY子句 reduce_orderby(parse, false); - DEBUG_QRW("After order by reduce"); - + // 如果启用了约束优化,移除非空约束测试 if (u_sess->attr.attr_sql.enable_constraint_optimization) { removeNotNullTest(root); DEBUG_QRW("After soft constraint removal"); } - /* - * Scan the rangetable for set-returning functions, and inline them if - * possible (producing subqueries that might get pulled up next). - * Recursion issues here are handled in the same way as for SubLinks. - */ + // 内联SET RETURNING函数 inline_set_returning_functions(root); + // 如果启用了LAZY_AGG重写规则并且允许从重写提示,则进行LAZYAGG处理 if ((LAZY_AGG & u_sess->attr.attr_sql.rewrite_rule) && permit_from_rewrite_hint(root, LAZY_AGG)) { lazyagg_main(parse); DEBUG_QRW("After lazyagg"); } - /* - * Here we only control the select permission for pan_table_data. Details see in checkPTRelkind(). - * The flag will be used in ExecCheckRTEPerms. - */ + // 如果是SELECT命令并且需要处理计划表,则设置OnlySelectFromPlanTable标志 if (parse->commandType == CMD_SELECT && checkSelectStmtForPlanTable(parse->rtable)) { OnlySelectFromPlanTable = true; } #ifndef ENABLE_MULTIPLE_NODES - /* Change ROWNUM to LIMIT if possible */ + + // 预处理Rownum preprocess_rownum(root, parse); DEBUG_QRW("After preprocess rownum"); #endif - /* - * Check to see if any subqueries in the jointree can be merged into this - * query. - */ + // 提取和展开子查询 parse->jointree = (FromExpr*)pull_up_subqueries(root, (Node*)parse->jointree); + // 调试输出:简单子查询提取后 DEBUG_QRW("After simple subquery pull up"); - /* - * If this is a simple UNION ALL query, flatten it into an appendrel. We - * do this now because it requires applying pull_up_subqueries to the leaf - * queries of the UNION ALL, which weren't touched above because they - * weren't referenced by the jointree (they will be after we do this). - */ + // 如果存在UNION操作,展平简单UNION ALL if (parse->setOperations) { flatten_simple_union_all(root); DEBUG_QRW("After simple union all flatten"); } - /* Transform hint.*/ + // 转换查询中的提示信息 transform_hints(root, parse, parse->hintState); - DEBUG_QRW("After transform hint"); - - /* - * Detect whether any rangetable entries are RTE_JOIN kind; if not, we can - * avoid the expense of doing flatten_join_alias_vars(). Likewise check - * whether any are RTE_RESULT kind; if not, we can skip - * remove_useless_result_rtes(). Also check for outer joins --- if none, - * we can skip reduce_outer_joins(). And check for LATERAL RTEs, too. - * This must be done after we have done pull_up_subqueries(), of course. - */ + // 初始化一些标志 root->hasJoinRTEs = false; root->hasLateralRTEs = false; hasOuterJoins = false; + // 遍历查询的RangeTblEntry列表,判断是否包含JOIN、OUTER JOIN、RESULT等表达式 foreach (l, parse->rtable) { RangeTblEntry* rte = (RangeTblEntry*)lfirst(l); @@ -1600,198 +1578,167 @@ Plan* subquery_planner(PlannerGlobal* glob, Query* parse, PlannerInfo* parent_ro root->hasLateralRTEs = true; } - /* - * Preprocess RowMark information. We need to do this after subquery - * pullup (so that all non-inherited RTEs are present) and before - * inheritance expansion (so that the info is available for - * expand_inherited_tables to examine and modify). - */ + // 预处理行标记 preprocess_rowmarks(root); #ifdef PGXC - /* - * In Coordinators we separate row marks in two groups - * one comprises of row marks of types ROW_MARK_EXCLUSIVE & ROW_MARK_SHARE - * and the other contains the rest of the types of row marks - * The former is handeled on Coordinator in such a way that - * FOR UPDATE/SHARE gets added in the remote query, whereas - * the later needs to be handeled the way pg does - * - * Notice : This is not a very efficient way of handling row marks - * Consider this join query - * select * from t1, t2 where t1.val = t2.val for update - * It results in this query to be fired at the Datanodes - * SELECT val, val2, ctid FROM ONLY t2 WHERE true FOR UPDATE OF t2 - * We are locking the complete table where as we should have locked - * only the rows where t1.val = t2.val is met - * - * We won't really call separate_rowmarks before we support for update with - * reomtequery. - */ if (!IS_STREAM_PLAN) separate_rowmarks(root); #endif - /* - * When the SQL dose not support stream mode in coordinator node, must send remotequery - * to datanode, and need not expand dfs table into dfs main table and delta table. - * Always support dfs table to expanding in data node. - */ + // 如果是分布式计划或者PGXC_DATANODE,展开DFS表 if (u_sess->opt_cxt.is_stream || IS_PGXC_DATANODE) { - /* - * Expand the Dfs table. - */ expand_dfs_tables(root); } - /* - * Expand any rangetable entries that are inheritance sets into "append - * relations". This can add entries to the rangetable, but they must be - * plain RTE_RELATION entries, so it's OK (and marginally more efficient) - * to do it after checking for joins and other special RTEs. We must do - * this after pulling up subqueries, else we'd fail to handle inherited - * tables in subqueries. - */ + // 展开继承表 expand_inherited_tables(root); - /* - * Set hasHavingQual to remember if HAVING clause is present. Needed - * because preprocess_expression will reduce a constant-true condition to - * an empty qual list ... but "HAVING TRUE" is not a semantic no-op. - */ + // 判断是否存在HAVING子句,设置相关标志 root->hasHavingQual = (parse->havingQual != NULL); - /* Clear this flag; might get set in distribute_qual_to_rels */ + // 初始化标志,表示没有常数表达式 root->hasPseudoConstantQuals = false; - /* * Calculate how many tables in current query level, and give a * rought estimation of work mem for each relation */ - int work_mem_orig = u_sess->opt_cxt.op_work_mem; - int esti_op_mem_orig = root->glob->estiopmem; - if (root->glob->minopmem > 0) { - int num_rel = 0; - foreach (l, parse->rtable) { - RangeTblEntry* rte = (RangeTblEntry*)lfirst(l); +// 保存原始的操作内存设置 +int work_mem_orig = u_sess->opt_cxt.op_work_mem; - if (rte->rtekind == RTE_RELATION || rte->rtekind == RTE_SUBQUERY) { - num_rel++; - } - } - if (num_rel <= 1) { - if ((parse->groupClause || parse->sortClause || parse->distinctClause)) - num_rel = 2; - else - num_rel = 1; - } - root->glob->estiopmem = Max(root->glob->minopmem, (double)root->glob->estiopmem / ceil(LOG2(num_rel + 1))); - u_sess->opt_cxt.op_work_mem = Min(root->glob->estiopmem, OPT_MAX_OP_MEM); - AssertEreport(u_sess->opt_cxt.op_work_mem > 0, - MOD_OPT, - "invalid operator work mem when roughtly estimating the work memory for each relation"); - } +// 保存原始的操作内存估算值 +int esti_op_mem_orig = root->glob->estiopmem; - /* - * Do expression preprocessing on targetlist and quals, as well as other - * random expressions in the querytree. Note that we do not need to - * handle sort/group expressions explicitly, because they are actually - * part of the targetlist. - */ - parse->targetList = (List*)preprocess_expression(root, (Node*)parse->targetList, EXPRKIND_TARGET); +// 如果指定了最小操作内存,执行以下操作 +if (root->glob->minopmem > 0) { + int num_rel = 0; - parse->returningList = (List*)preprocess_expression(root, (Node*)parse->returningList, EXPRKIND_TARGET); - - preprocess_qual_conditions(root, (Node*)parse->jointree); - - parse->havingQual = preprocess_expression(root, parse->havingQual, EXPRKIND_QUAL); - - foreach (l, parse->windowClause) { - WindowClause* wc = (WindowClause*)lfirst(l); - - /* partitionClause/orderClause are sort/group expressions */ - wc->startOffset = preprocess_expression(root, wc->startOffset, EXPRKIND_LIMIT); - wc->endOffset = preprocess_expression(root, wc->endOffset, EXPRKIND_LIMIT); - } - - parse->limitOffset = preprocess_expression(root, parse->limitOffset, EXPRKIND_LIMIT); - if (parse->limitCount != NULL && !IsA(parse->limitCount, Const)) { - parse->limitCount = preprocess_expression(root, parse->limitCount, EXPRKIND_LIMIT); - } - - foreach (l, parse->mergeActionList) { - MergeAction* action = (MergeAction*)lfirst(l); - - action->targetList = (List*)preprocess_expression(root, (Node*)action->targetList, EXPRKIND_TARGET); - - action->pulluped_targetList = - (List*)preprocess_expression(root, (Node*)(action->pulluped_targetList), EXPRKIND_TARGET); - - action->qual = preprocess_expression(root, (Node*)action->qual, EXPRKIND_QUAL); - } - - parse->mergeSourceTargetList = - (List*)preprocess_expression(root, (Node*)parse->mergeSourceTargetList, EXPRKIND_TARGET); - - if (parse->upsertClause) { - parse->upsertClause->updateTlist = (List*) - preprocess_expression(root, (Node*)parse->upsertClause->updateTlist, EXPRKIND_TARGET); - parse->upsertClause->upsertWhere = (Node*) - preprocess_expression(root, (Node*)parse->upsertClause->upsertWhere, EXPRKIND_QUAL); - } - root->append_rel_list = (List*)preprocess_expression(root, (Node*)root->append_rel_list, EXPRKIND_APPINFO); - - /* Also need to preprocess expressions for function and values RTEs */ + // 遍历查询的关系表(rtable) foreach (l, parse->rtable) { RangeTblEntry* rte = (RangeTblEntry*)lfirst(l); - int kind; - if (rte->rtekind == RTE_RELATION) { - if (rte->tablesample) { - rte->tablesample = - (TableSampleClause*)preprocess_expression(root, (Node*)rte->tablesample, EXPRKIND_TABLESAMPLE); - } - if (rte->timecapsule) { -#ifndef ENABLE_MULTIPLE_NODES - if (IS_STREAM) { - mark_stream_unsupport(); - } -#endif - rte->timecapsule = - (TimeCapsuleClause*)preprocess_expression(root, (Node*)rte->timecapsule, EXPRKIND_TIMECAPSULE); - } - } else if (rte->rtekind == RTE_SUBQUERY) { - /* - * We don't want to do all preprocessing yet on the subquery's - * expressions, since that will happen when we plan it. But if it - * contains any join aliases of our level, those have to get - * expanded now, because planning of the subquery won't do it. - * That's only possible if the subquery is LATERAL. - */ - if (rte->lateral && root->hasJoinRTEs) - rte->subquery = (Query *)flatten_join_alias_vars(root, (Node *) rte->subquery); - } else if (rte->rtekind == RTE_FUNCTION) { - /* Preprocess the function expression fully */ - kind = rte->lateral ? EXPRKIND_RTFUNC_LATERAL : EXPRKIND_RTFUNC; - rte->funcexpr = preprocess_expression(root, rte->funcexpr, kind); - } else if (rte->rtekind == RTE_VALUES) { - /* Preprocess the values lists fully */ - kind = rte->lateral ? EXPRKIND_VALUES_LATERAL : EXPRKIND_VALUES; - rte->values_lists = (List*)preprocess_expression(root, (Node*)rte->values_lists, kind); - } - - /* - * Process each element of the securityQuals list as if it were a - * separate qual expression (as indeed it is). We need to do it this - * way to get proper canonicalization of AND/OR structure. Note that - * this converts each element into an implicit-AND sublist. - */ - ListCell* cell = NULL; - foreach (cell, rte->securityQuals) { - lfirst(cell) = preprocess_expression(root, (Node*)lfirst(cell), EXPRKIND_QUAL); + // 如果关系表的类型是关系或子查询 + if (rte->rtekind == RTE_RELATION || rte->rtekind == RTE_SUBQUERY) { + num_rel++; } } + // 如果关系数小于等于1,根据条件调整关系数 + if (num_rel <= 1) { + if (parse->groupClause || parse->sortClause || parse->distinctClause) { + num_rel = 2; + } else { + num_rel = 1; + } + } + + // 更新估算的操作内存值 + root->glob->estiopmem = Max(root->glob->minopmem, (double)root->glob->estiopmem / ceil(LOG2(num_rel + 1))); + + // 限制操作内存不超过最大值 + u_sess->opt_cxt.op_work_mem = Min(root->glob->estiopmem, OPT_MAX_OP_MEM); + + // 断言操作内存大于0 + AssertEreport(u_sess->opt_cxt.op_work_mem > 0, + MOD_OPT, + "invalid operator work mem when roughly estimating the work memory for each relation"); +} + +// 对目标列表中的表达式进行预处理 +parse->targetList = (List*)preprocess_expression(root, (Node*)parse->targetList, EXPRKIND_TARGET); + +// 对返回列表中的表达式进行预处理 +parse->returningList = (List*)preprocess_expression(root, (Node*)parse->returningList, EXPRKIND_TARGET); + +// 对联接条件进行预处理 +preprocess_qual_conditions(root, (Node*)parse->jointree); + +// 对HAVING子句中的表达式进行预处理 +parse->havingQual = preprocess_expression(root, parse->havingQual, EXPRKIND_QUAL); + +// 遍历窗口子句中的表达式 +foreach (l, parse->windowClause) { + WindowClause* wc = (WindowClause*)lfirst(l); + + // 预处理窗口子句中的起始和结束表达式 + wc->startOffset = preprocess_expression(root, wc->startOffset, EXPRKIND_LIMIT); + wc->endOffset = preprocess_expression(root, wc->endOffset, EXPRKIND_LIMIT); +} + +// 预处理LIMIT子句中的表达式 +parse->limitOffset = preprocess_expression(root, parse->limitOffset, EXPRKIND_LIMIT); + +// 如果LIMIT COUNT不为空且不是常量,预处理LIMIT COUNT表达式 +if (parse->limitCount != NULL && !IsA(parse->limitCount, Const)) { + parse->limitCount = preprocess_expression(root, parse->limitCount, EXPRKIND_LIMIT); +} + +// 遍历MERGE动作列表中的动作 +foreach (l, parse->mergeActionList) { + MergeAction* action = (MergeAction*)lfirst(l); + + // 预处理动作中的目标列表、提取的目标列表和条件表达式 + action->targetList = (List*)preprocess_expression(root, (Node*)action->targetList, EXPRKIND_TARGET); + action->pulluped_targetList = + (List*)preprocess_expression(root, (Node*)(action->pulluped_targetList), EXPRKIND_TARGET); + action->qual = preprocess_expression(root, (Node*)action->qual, EXPRKIND_QUAL); +} + +// 预处理MERGE语句中的源表目标列表 +parse->mergeSourceTargetList = + (List*)preprocess_expression(root, (Node*)parse->mergeSourceTargetList, EXPRKIND_TARGET); + +// 如果存在UPSERT子句,预处理UPSERT子句中的表达式 +if (parse->upsertClause) { + parse->upsertClause->updateTlist = (List*) + preprocess_expression(root, (Node*)parse->upsertClause->updateTlist, EXPRKIND_TARGET); + parse->upsertClause->upsertWhere = (Node*) + preprocess_expression(root, (Node*)parse->upsertClause->upsertWhere, EXPRKIND_QUAL); +} + +// 预处理追加关系列表 +root->append_rel_list = (List*)preprocess_expression(root, (Node*)root->append_rel_list, EXPRKIND_APPINFO); + +// 遍历关系表,预处理其中的表达式 +foreach (l, parse->rtable) { + RangeTblEntry* rte = (RangeTblEntry*)lfirst(l); + int kind; + + if (rte->rtekind == RTE_RELATION) { + if (rte->tablesample) { + rte->tablesample = + (TableSampleClause*)preprocess_expression(root, (Node*)rte->tablesample, EXPRKIND_TABLESAMPLE); + } + if (rte->timecapsule) { +#ifndef ENABLE_MULTIPLE_NODES + if (IS_STREAM) { + mark_stream_unsupport(); + } +#endif + rte->timecapsule = + (TimeCapsuleClause*)preprocess_expression(root, (Node*)rte->timecapsule, EXPRKIND_TIMECAPSULE); + } + } else if (rte->rtekind == RTE_SUBQUERY) { + if (rte->lateral && root->hasJoinRTEs) + rte->subquery = (Query *)flatten_join_alias_vars(root, (Node *) rte->subquery); + } else if (rte->rtekind == RTE_FUNCTION) { + /* 预处理函数表达式 */ + kind = rte->lateral ? EXPRKIND_RTFUNC_LATERAL : EXPRKIND_RTFUNC; + rte->funcexpr = preprocess_expression(root, rte->funcexpr, kind); + } else if (rte->rtekind == RTE_VALUES) { + kind = rte->lateral ? EXPRKIND_VALUES_LATERAL : EXPRKIND_VALUES; + /* 预处理VALUES子句中的表达式 */ + rte->values_lists = (List*)preprocess_expression(root, (Node*)rte->values_lists, kind); + } + + // 遍历安全性限制条件列表,预处理其中的表达式 + ListCell* cell = NULL; + foreach (cell, rte->securityQuals) { + lfirst(cell) = preprocess_expression(root, (Node*)lfirst(cell), EXPRKIND_QUAL); + } +} + + DEBUG_QRW("After preprocess expressions"); u_sess->opt_cxt.op_work_mem = work_mem_orig; @@ -1822,254 +1769,222 @@ Plan* subquery_planner(PlannerGlobal* glob, Query* parse, PlannerInfo* parent_ro * implicitly-ANDed-list form at this point, even though they are declared * as Node *. */ - if (!parse->unique_check) { - newHaving = NIL; - foreach(l, (List *) parse->havingQual) { - Node *havingclause = (Node *)lfirst(l); + // 如果不需要进行唯一性检查 +if (!parse->unique_check) { + newHaving = NIL; - /* - * For groupingSets, having clause can only be calculate in havingQual, can not push-down to lefttree's qual. - * - * For example: - * select sum(a), b from group by rollup(a, b) having b > 10; - * this mean: group by a, b - * group by a - * group by () - * - * For "group by ()", we need calculate sum(a) for all lefttree's rows. - */ - if (contain_agg_clause(havingclause) || - contain_volatile_functions(havingclause) || - contain_subplans(havingclause) - || parse->groupingSets) { - /* keep it in HAVING */ - newHaving = lappend(newHaving, havingclause); - } else if (parse->groupClause) { - /* move it to WHERE */ - parse->jointree->quals = (Node *) - lappend((List *) parse->jointree->quals, havingclause); - } else { - /* put a copy in WHERE, keep it in HAVING */ - parse->jointree->quals = (Node *) - lappend((List *) parse->jointree->quals, - copyObject(havingclause)); - newHaving = lappend(newHaving, havingclause); - } - } - parse->havingQual = (Node *) newHaving; - } + // 遍历HAVING子句中的表达式 + foreach(l, (List *) parse->havingQual) { + Node *havingclause = (Node *)lfirst(l); - - DEBUG_QRW("After having qual rewrite"); - - passdown_itst_keys_to_subroot(root, diskeys); - - /* - * If we have any outer joins, try to reduce them to plain inner joins. - * This step is most easily done after we've done expression - * preprocessing. - */ - if (hasOuterJoins) { - reduce_outer_joins(root); - DEBUG_QRW("After outer-to-inner conversion"); - if (IS_STREAM_PLAN) { - bool support_rewrite = true; - if (!fulljoin_2_left_union_right_anti_support(root->parse)) - support_rewrite = false; - if (contain_volatile_functions((Node*)root->parse)) - support_rewrite = false; - contain_func_context context = - init_contain_func_context(list_make3_oid(ECEXTENSIONFUNCOID, ECHADOOPFUNCOID, RANDOMFUNCOID)); - if (contains_specified_func((Node*)root->parse, &context)) { - char* func_name = get_func_name(((FuncExpr*)linitial(context.func_exprs))->funcid); - ereport(DEBUG2, - (errmodule(MOD_OPT_REWRITE), - (errmsg("[Not rewrite full Join on true]: %s functions contained.", func_name)))); - pfree_ext(func_name); - list_free_ext(context.funcids); - context.funcids = NIL; - list_free_ext(context.func_exprs); - context.func_exprs = NIL; - support_rewrite = false; - } - if (support_rewrite) { - reduce_inequality_fulljoins(root); - DEBUG_QRW("After full join conversion"); - } + // 如果HAVING子句中包含聚合函数、不稳定函数、子查询或分组集(groupingSets) + if (contain_agg_clause(havingclause) || + contain_volatile_functions(havingclause) || + contain_subplans(havingclause) + || parse->groupingSets) { + + // 将表达式添加到新的HAVING子句中 + newHaving = lappend(newHaving, havingclause); + } else if (parse->groupClause) { + + // 如果有GROUP BY子句,将表达式添加到联接树的条件中 + parse->jointree->quals = (Node *) + lappend((List *) parse->jointree->quals, havingclause); + } else { + /* 将表达式的副本添加到WHERE子句中,并保留在HAVING子句中 */ + parse->jointree->quals = (Node *) + lappend((List *) parse->jointree->quals, + copyObject(havingclause)); + newHaving = lappend(newHaving, havingclause); } } - /* - * If we have any RTE_RESULT relations, see if they can be deleted from - * the jointree. This step is most effectively done after we've done - * expression preprocessing and outer join reduction. - */ - if (hasResultRTEs) - remove_useless_result_rtes(root); - - /* - * Check if need auto-analyze for current query level. - * No need to do auto-analyze for query on one table without Groupby. - */ - if (u_sess->attr.attr_sql.enable_autoanalyze && !u_sess->analyze_cxt.need_autoanalyze && IS_STREAM_PLAN && - (list_length(parse->rtable) > 1 || parse->groupClause)) { - /* inherit upper level and check for current query level */ - u_sess->analyze_cxt.need_autoanalyze = true; - } - (void)MemoryContextSwitchTo(oldcontext); - - /* - * Do the main planning. If we have an inherited target relation, that - * needs special processing, else go straight to grouping_planner. - */ - if (parse->resultRelation && parse->commandType != CMD_INSERT && - rt_fetch(parse->resultRelation, parse->rtable)->inh) - plan = inheritance_planner(root); - else { - plan = grouping_planner(root, tuple_fraction); - /* If it's not SELECT, we need a ModifyTable node */ - if (parse->commandType != CMD_SELECT) { - List* returningLists = NIL; - List* rowMarks = NIL; - Relation mainRel = NULL; - Oid taleOid = rt_fetch(parse->resultRelation, parse->rtable)->relid; - bool partKeyUpdated = targetListHasPartitionKey(parse->targetList, taleOid); - mainRel = RelationIdGetRelation(taleOid); - bool isDfsStore = RelationIsDfsStore(mainRel); - RelationClose(mainRel); - - /* - * Set up the RETURNING list-of-lists, if needed. - */ - if (parse->returningList) - returningLists = list_make1(parse->returningList); - else - returningLists = NIL; - - /* - * If there was a FOR [KEY] UPDATE/SHARE clause, the LockRows node will - * have dealt with fetching non-locked marked rows, else we need - * to have ModifyTable do that. - */ - if (parse->rowMarks) - rowMarks = NIL; - else - rowMarks = root->rowMarks; -#ifdef STREAMPLAN - plan = (Plan*)make_modifytable(root, - parse->commandType, - parse->canSetTag, - list_make1_int(parse->resultRelation), - list_make1(plan), - returningLists, - rowMarks, - SS_assign_special_param(root), - partKeyUpdated, - parse->mergeTarget_relation, - parse->mergeSourceTargetList, - parse->mergeActionList, - parse->upsertClause, - isDfsStore); -#else - plan = (Plan*)make_modifytable(parse->commandType, - parse->canSetTag, - list_make1_int(parse->resultRelation), - list_make1(plan), - returningLists, - rowMarks, - SS_assign_special_param(root), - partKeyUpdated, - parse->mergeTarget_relation, - parse->mergeSourceTargetList, - parse->mergeActionList, - parse->upsertClause, - isDfsStore); -#endif -#ifdef PGXC - plan = pgxc_make_modifytable(root, plan); -#endif - } - } - - /* - * If any subplans were generated, or if there are any parameters to worry - * about, build initPlan list and extParam/allParam sets for plan nodes, - * and attach the initPlans to the top plan node. - */ - if (plan == NULL) - ereport(ERROR, - (errmodule(MOD_OPT_PLANNER), errcode(ERRCODE_UNEXPECTED_NULL_VALUE), - errmsg("Fail to generate subquery plan."), - errdetail("N/A"), - errcause("System error."), - erraction("Contact Huawei Engineer."))); - - if (list_length(glob->subplans) != num_old_subplans || root->glob->nParamExec > 0) - SS_finalize_plan(root, plan, true); - - /* Return internal info if caller wants it */ - if (subroot != NULL) - *subroot = root; - - /* add not-used hints information to warning string */ - if (parse->hintState) - desc_hint_in_state(root, parse->hintState); - - /* Fix var's if we have changed var */ - if (root->var_mappings != NIL) { - fix_vars_plannode(root, plan); - root->parse->is_from_inlist2join_rewrite = true; - } - - return plan; + // 更新HAVING子句 + parse->havingQual = (Node *) newHaving; } -/* - * preprocess_expression - * Do subquery_planner's preprocessing work for an expression, - * which can be a targetlist, a WHERE clause (including JOIN/ON - * conditions), or a HAVING clause. - */ +// 输出调试信息 +DEBUG_QRW("After having qual rewrite"); + +// 传递分区键信息给子树 +passdown_itst_keys_to_subroot(root, diskeys); + +// 如果存在外连接,将其转换为内连接 +if (hasOuterJoins) { + reduce_outer_joins(root); + DEBUG_QRW("After outer-to-inner conversion"); + + // 如果是流式计划 + if (IS_STREAM_PLAN) { + bool support_rewrite = true; + + // 检查是否支持完全连接的重写 + if (!fulljoin_2_left_union_right_anti_support(root->parse)) + support_rewrite = false; + + // 检查是否包含不稳定函数 + if (contain_volatile_functions((Node*)root->parse)) + support_rewrite = false; + + // 检查是否包含指定函数 + contain_func_context context = + init_contain_func_context(list_make3_oid(ECEXTENSIONFUNCOID, ECHADOOPFUNCOID, RANDOMFUNCOID)); + if (contains_specified_func((Node*)root->parse, &context)) { + char* func_name = get_func_name(((FuncExpr*)linitial(context.func_exprs))->funcid); + ereport(DEBUG2, + (errmodule(MOD_OPT_REWRITE), + (errmsg("[Not rewrite full Join on true]: %s functions contained.", func_name)))); + pfree_ext(func_name); + list_free_ext(context.funcids); + context.funcids = NIL; + list_free_ext(context.func_exprs); + context.func_exprs = NIL; + support_rewrite = false; + } + + // 如果支持完全连接的重写,进行重写 + if (support_rewrite) { + reduce_inequality_fulljoins(root); + DEBUG_QRW("After full join conversion"); + } + } +} + +// 移除无用的结果表达式 +if (hasResultRTEs) + remove_useless_result_rtes(root); + +// 如果启用自动分析,且在流式计划中,并且有多个关系或GROUP BY子句 +if (u_sess->attr.attr_sql.enable_autoanalyze && !u_sess->analyze_cxt.need_autoanalyze && IS_STREAM_PLAN && + (list_length(parse->rtable) > 1 || parse->groupClause)) { + + // 标记需要自动分析 + u_sess->analyze_cxt.need_autoanalyze = true; +} + +// 切换回原内存上下文 +(void)MemoryContextSwitchTo(oldcontext); + +// 如果存在结果关系且不是插入操作,并且关系是继承的,使用继承规则生成计划 +if (parse->resultRelation && parse->commandType != CMD_INSERT && + rt_fetch(parse->resultRelation, parse->rtable)->inh) + plan = inheritance_planner(root); +else { + // 否则,使用分组规则生成计划,其中包括传入的tuple_fraction + plan = grouping_planner(root, tuple_fraction); + + // 如果不是SELECT操作 + if (parse->commandType != CMD_SELECT) { + List* returningLists = NIL; + List* rowMarks = NIL; + Relation mainRel = NULL; + Oid taleOid = rt_fetch(parse->resultRelation, parse->rtable)->relid; + bool partKeyUpdated = targetListHasPartitionKey(parse->targetList, taleOid); + + // 打开主关系并检查是否是DfsStore + mainRel = RelationIdGetRelation(taleOid); + bool isDfsStore = RelationIsDfsStore(mainRel); + RelationClose(mainRel); + + // 如果有返回列表,使用返回列表,否则使用根的行标记列表 + if (parse->returningList) + returningLists = list_make1(parse->returningList); + else + returningLists = NIL; + + // 如果有行标记,使用行标记,否则使用根的行标记列表 + if (parse->rowMarks) + rowMarks = NIL; + else + rowMarks = root->rowMarks; + +#ifdef STREAMPLAN + // 生成修改表计划节点 + plan = (Plan*)make_modifytable(root, + parse->commandType, + parse->canSetTag, + list_make1_int(parse->resultRelation), + list_make1(plan), + returningLists, + rowMarks, + SS_assign_special_param(root), + partKeyUpdated, + parse->mergeTarget_relation, + parse->mergeSourceTargetList, + parse->mergeActionList, + parse->upsertClause, + isDfsStore); +#else + // 生成修改表计划节点(不带流式计划支持) + plan = (Plan*)make_modifytable(parse->commandType, + parse->canSetTag, + list_make1_int(parse->resultRelation), + list_make1(plan), + returningLists, + rowMarks, + SS_assign_special_param(root), + partKeyUpdated, + parse->mergeTarget_relation, + parse->mergeSourceTargetList, + parse->mergeActionList, + parse->upsertClause, + isDfsStore); +#endif + +#ifdef PGXC + // 处理分布式计算的修改表计划 + plan = pgxc_make_modifytable(root, plan); +#endif + } +} + +// 如果计划为空,抛出错误 +if (plan == NULL) + ereport(ERROR, + (errmodule(MOD_OPT_PLANNER), errcode(ERRCODE_UNEXPECTED_NULL_VALUE), + errmsg("Fail to generate subquery plan."), + errdetail("N/A"), + errcause("System error."), + erraction("Contact Huawei Engineer."))); + +// 最终化计划 +if (list_length(glob->subplans) != num_old_subplans || root->glob->nParamExec > 0) + SS_finalize_plan(root, plan, true); + +// 如果存在子树,将子树指针设置为根 +if (subroot != NULL) + *subroot = root; + +// 处理查询提示 +if (parse->hintState) + desc_hint_in_state(root, parse->hintState); + +// 如果存在变量映射,修复计划中的变量 +if (root->var_mappings != NIL) { + fix_vars_plannode(root, plan); + root->parse->is_from_inlist2join_rewrite = true; +} + +// 返回生成的计划 +return plan; +} + +// 用于预处理表达式的辅助函数 Node* preprocess_expression(PlannerInfo* root, Node* expr, int kind) { - /* - * Fall out quickly if expression is empty. This occurs often enough to - * be worth checking. Note that null->null is the correct conversion for - * implicit-AND result format, too. - */ + if (expr == NULL) return NULL; - /* - * If the query has any join RTEs, replace join alias variables with - * base-relation variables. We must do this before sublink processing, - * else sublinks expanded out from join aliases wouldn't get processed. We - * can skip it in VALUES lists, however, since they can't contain any Vars - * at all. - */ + + // 如果查询中包含JOIN关系,并且不是RTFUNC或VALUES类型的表达式 if (root->hasJoinRTEs && !(kind == EXPRKIND_RTFUNC || kind == EXPRKIND_VALUES)) expr = flatten_join_alias_vars(root, expr); - /* - * Simplify constant expressions. - * - * Note: an essential effect of this is to convert named-argument function - * calls to positional notation and insert the current actual values of - * any default arguments for functions. To ensure that happens, we *must* - * process all expressions here. Previous PG versions sometimes skipped - * const-simplification if it didn't seem worth the trouble, but we can't - * do that anymore. - * - * Note: this also flattens nested AND and OR expressions into N-argument - * form. All processing of a qual expression after this point must be - * careful to maintain AND/OR flatness --- that is, do not generate a tree - * with AND directly under AND, nor OR directly under OR. - */ + // 对表达式进行常量折叠和简化 expr = eval_const_expressions(root, expr); - /* - * If it's a qual or havingQual, canonicalize it. - */ + // 如果是QUAL类型的表达式 if (kind == EXPRKIND_QUAL) { expr = (Node*)canonicalize_qual((Expr*)expr, false); @@ -2079,58 +1994,63 @@ Node* preprocess_expression(PlannerInfo* root, Node* expr, int kind) #endif } - /* Expand SubLinks to SubPlans */ + // 如果查询中包含子查询,处理子查询 if (root->parse->hasSubLinks) expr = SS_process_sublinks(root, expr, (kind == EXPRKIND_QUAL)); - /* - * XXX do not insert anything here unless you have grokked the comments in - * SS_replace_correlation_vars ... - * - * Replace uplevel vars with Param nodes (this IS possible in VALUES) - */ + // 如果查询的层次大于1,替换相关变量 if (root->query_level > 1) expr = SS_replace_correlation_vars(root, expr); - /* - * If it's a qual or havingQual, convert it to implicit-AND format. (We - * don't want to do this before eval_const_expressions, since the latter - * would be unable to simplify a top-level AND correctly. Also, - * SS_process_sublinks expects explicit-AND format.) - */ + // 如果是QUAL类型的表达式,将其转换为AND表达式的列表 if (kind == EXPRKIND_QUAL) expr = (Node*)make_ands_implicit((Expr*)expr); return expr; } + /* * preprocess_qual_conditions * Recursively scan the query's jointree and do subquery_planner's * preprocessing work on each qual condition found therein. */ +// 预处理联接条件,用于处理查询计划中的联接表达式 void preprocess_qual_conditions(PlannerInfo* root, Node* jtnode) { + // 如果jtnode为空,直接返回 if (jtnode == NULL) return; + + // 如果jtnode是RangeTblRef类型,表示只是一个范围表引用,不需要处理 if (IsA(jtnode, RangeTblRef)) { /* nothing to do here */ - } else if (IsA(jtnode, FromExpr)) { + } + // 如果jtnode是FromExpr类型,表示是FROM子句的一部分 + else if (IsA(jtnode, FromExpr)) { FromExpr* f = (FromExpr*)jtnode; ListCell* l = NULL; + // 遍历FROM子句中的每个元素,递归调用preprocess_qual_conditions函数 foreach (l, f->fromlist) preprocess_qual_conditions(root, (Node*)lfirst(l)); + // 预处理FROM子句的条件表达式 f->quals = preprocess_expression(root, f->quals, EXPRKIND_QUAL); - } else if (IsA(jtnode, JoinExpr)) { + } + // 如果jtnode是JoinExpr类型,表示是JOIN操作的一部分 + else if (IsA(jtnode, JoinExpr)) { JoinExpr* j = (JoinExpr*)jtnode; + // 递归处理左右子树 preprocess_qual_conditions(root, j->larg); preprocess_qual_conditions(root, j->rarg); + // 预处理JOIN操作的条件表达式 j->quals = preprocess_expression(root, j->quals, EXPRKIND_QUAL); - } else { + } + // 如果jtnode不是已知类型,抛出错误 + else { ereport(ERROR, (errmodule(MOD_OPT_PLANNER), errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE), errmsg("Unrecognized node type when processing qual condition."), @@ -2140,51 +2060,49 @@ void preprocess_qual_conditions(PlannerInfo* root, Node* jtnode) } } -/* - * preprocess_phv_expression - * Do preprocessing on a PlaceHolderVar expression that's been pulled up. - * - * If a LATERAL subquery references an output of another subquery, and that - * output must be wrapped in a PlaceHolderVar because of an intermediate outer - * join, then we'll push the PlaceHolderVar expression down into the subquery - * and later pull it back up during find_lateral_references, which runs after - * subquery_planner has preprocessed all the expressions that were in the - * current query level to start with. So we need to preprocess it then. - */ -Expr * -preprocess_phv_expression(PlannerInfo *root, Expr *expr) +// 预处理占位符表达式,用于处理占位符变量的表达式 +Expr *preprocess_phv_expression(PlannerInfo *root, Expr *expr) { + // 调用preprocess_expression函数处理表达式 return (Expr *) preprocess_expression(root, (Node *) expr, EXPRKIND_PHV); } -/* - * preprocess_const_params - * Recursively scan the query's jointree and do subquery_planner's - * preprocessing work on each qual condition found therein to replace - * params with const value if possible - */ +// 预处理常量参数,用于处理查询计划中的常量参数 void preprocess_const_params(PlannerInfo* root, Node* jtnode) { + // 如果jtnode为空,直接返回 if (jtnode == NULL) return; + + // 如果jtnode是RangeTblRef类型,表示只是一个范围表引用,不需要处理 if (IsA(jtnode, RangeTblRef)) { /* nothing to do here */ - } else if (IsA(jtnode, FromExpr)) { + } + // 如果jtnode是FromExpr类型,表示是FROM子句的一部分 + else if (IsA(jtnode, FromExpr)) { FromExpr* f = (FromExpr*)jtnode; ListCell* l = NULL; + // 遍历FROM子句中的每个元素,递归调用preprocess_const_params函数 foreach (l, f->fromlist) preprocess_const_params(root, (Node*)lfirst(l)); + // 预处理FROM子句的条件表达式,这里使用preprocess_const_params_worker函数 f->quals = preprocess_const_params_worker(root, f->quals, EXPRKIND_QUAL); - } else if (IsA(jtnode, JoinExpr)) { + } + // 如果jtnode是JoinExpr类型,表示是JOIN操作的一部分 + else if (IsA(jtnode, JoinExpr)) { JoinExpr* j = (JoinExpr*)jtnode; + // 递归处理左右子树 preprocess_const_params(root, j->larg); preprocess_const_params(root, j->rarg); + // 预处理JOIN操作的条件表达式,这里使用preprocess_const_params_worker函数 j->quals = preprocess_const_params_worker(root, j->quals, EXPRKIND_QUAL); - } else { + } + // 如果jtnode不是已知类型,抛出错误 + else { ereport(ERROR, (errmodule(MOD_OPT_PLANNER), errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE), errmsg("Unrecognized node type when processing const parameters."), @@ -2194,6 +2112,7 @@ void preprocess_const_params(PlannerInfo* root, Node* jtnode) } } + /* * preprocess_const_params_worker * worker func for params to const replacement @@ -2244,34 +2163,44 @@ static Node* preprocess_const_params_worker(PlannerInfo* root, Node* expr, int k * * Returns a query plan. */ +// 针对继承表的查询计划生成函数,用于处理继承关系的查询 static Plan* inheritance_planner(PlannerInfo* root) { - Query* parse = root->parse; - int parentRTindex = parse->resultRelation; - List* final_rtable = NIL; - int save_rel_array_size = 0; - RelOptInfo** save_rel_array = NULL; - RangeTblEntry** save_rte_array = NULL; - List* subplans = NIL; - List* resultRelations = NIL; - List* returningLists = NIL; - List* rowMarks = NIL; - ListCell* lc = NULL; - bool isDfsStore = false; - bool partKeyUpdated = false; - Oid taleOid = rt_fetch(parse->resultRelation, parse->rtable)->relid; - Relation mainRel = NULL; + Query* parse = root->parse; // 获取查询解析树 + int parentRTindex = parse->resultRelation; // 获取结果关系的索引 + List* final_rtable = NIL; // 最终的范围表条目列表 + int save_rel_array_size = 0; // 保存的关系数组大小 + RelOptInfo** save_rel_array = NULL; // 保存的关系数组 + RangeTblEntry** save_rte_array = NULL; // 保存的范围表条目数组 + List* subplans = NIL; // 子查询计划列表 + List* resultRelations = NIL; // 结果关系列表 + List* returningLists = NIL; // 返回列表 + List* rowMarks = NIL; // 行标记列表 + ListCell* lc = NULL; // 列表遍历器 + bool isDfsStore = false; // 是否是Dfs存储 + bool partKeyUpdated = false; // 分区键是否被更新 + Oid taleOid = rt_fetch(parse->resultRelation, parse->rtable)->relid; // 获取结果关系的OID + Relation mainRel = NULL; // 主关系对象 + + // 检查目标列表是否包含分区键的更新操作 partKeyUpdated = targetListHasPartitionKey(parse->targetList, taleOid); + // 获取主关系对象 mainRel = RelationIdGetRelation(taleOid); + + // 断言主关系对象有效,用于生成查询计划,结果关系是继承计划时 AssertEreport(RelationIsValid(mainRel), MOD_OPT, "The relation descriptor is invalid" "when generating a query plan and the result relation is an inherient plan."); + // 检查主关系是否是Dfs存储 isDfsStore = RelationIsDfsStore(mainRel); + + // 关闭主关系 RelationClose(mainRel); + /* * We generate a modified instance of the original Query for each target * relation, plan that, and put all the plans into a list that will be @@ -2337,31 +2266,41 @@ static Plan* inheritance_planner(PlannerInfo* root) * since subquery RTEs couldn't contain any references to the target * rel. */ - if (final_rtable != NIL) { - ListCell* lr = NULL; + // 如果final_rtable不为空,说明有最终的范围表条目需要处理 +if (final_rtable != NIL) { + ListCell* lr = NULL; + int rti = 1; // 范围表条目索引 - rti = 1; - foreach (lr, parse->rtable) { - RangeTblEntry* rte = (RangeTblEntry*)lfirst(lr); + // 遍历原始查询的范围表条目 + foreach (lr, parse->rtable) { + RangeTblEntry* rte = (RangeTblEntry*)lfirst(lr); - if (rte->rtekind == RTE_SUBQUERY) { - Index newrti; + // 如果范围表条目的类型是RTE_SUBQUERY,需要进行处理 + if (rte->rtekind == RTE_SUBQUERY) { + Index newrti; - /* - * The RTE can't contain any references to its own RT - * index, so we can save a few cycles by applying - * ChangeVarNodes before we append the RTE to the - * rangetable. - */ - newrti = list_length(subroot.parse->rtable) + 1; - ChangeVarNodes((Node*)subroot.parse, rti, newrti, 0); - ChangeVarNodes((Node*)subroot.rowMarks, rti, newrti, 0); - rte = (RangeTblEntry*)copyObject(rte); - subroot.parse->rtable = lappend(subroot.parse->rtable, rte); - } - rti++; - } + /* + * The RTE can't contain any references to its own RT + * index, so we can save a few cycles by applying + * ChangeVarNodes before we append the RTE to the + * rangetable. + */ + + // 计算新的范围表条目索引 + newrti = list_length(subroot.parse->rtable) + 1; + + // 对子查询中的变量节点进行替换 + ChangeVarNodes((Node*)subroot.parse, rti, newrti, 0); + ChangeVarNodes((Node*)subroot.rowMarks, rti, newrti, 0); + + // 复制范围表条目并添加到子查询的范围表中 + rte = (RangeTblEntry*)copyObject(rte); + subroot.parse->rtable = lappend(subroot.parse->rtable, rte); } + rti++; + } +} + /* We needn't modify the child's append_rel_list */ /* There shouldn't be any OJ info to translate, as yet */ @@ -2460,52 +2399,52 @@ static Plan* inheritance_planner(PlannerInfo* root) /* * Put back the final adjusted rtable into the master copy of the Query. */ - parse->rtable = final_rtable; - root->simple_rel_array_size = save_rel_array_size; - root->simple_rel_array = save_rel_array; - root->simple_rte_array = save_rte_array; - /* - * If there was a FOR [KEY] UPDATE/SHARE clause, the LockRows node will have - * dealt with fetching non-locked marked rows, else we need to have - * ModifyTable do that. - */ - if (parse->rowMarks) - rowMarks = NIL; - else - rowMarks = root->rowMarks; + // 将最终的范围表条目赋值给原始查询的范围表 +parse->rtable = final_rtable; - /* And last, tack on a ModifyTable node to do the UPDATE/DELETE work */ +// 恢复保存的关系和范围表数组 +root->simple_rel_array_size = save_rel_array_size; +root->simple_rel_array = save_rel_array; +root->simple_rte_array = save_rte_array; + +// 如果原始查询中存在FOR [KEY] UPDATE/SHARE子句,将rowMarks设置为空列表,否则使用root中的rowMarks +if (parse->rowMarks) + rowMarks = NIL; +else + rowMarks = root->rowMarks; + +// 创建ModifyTable节点,用于执行UPDATE/DELETE操作 #ifdef STREAMPLAN - return make_modifytables(root, - parse->commandType, - parse->canSetTag, - resultRelations, - subplans, - returningLists, - rowMarks, - SS_assign_special_param(root), - partKeyUpdated, - isDfsStore, - 0, - NULL, - NULL, - NULL); +return make_modifytables(root, + parse->commandType, + parse->canSetTag, + resultRelations, + subplans, + returningLists, + rowMarks, + SS_assign_special_param(root), + partKeyUpdated, + isDfsStore, + 0, + NULL, + NULL, + NULL); #else - return make_modifytables(parse->commandType, - parse->canSetTag, - resultRelations, - subplans, - returningLists, - rowMarks, - SS_assign_special_param(root), - partKeyUpdated, - isDfsStore, - 0, - NULL, - NULL, - NULL); +return make_modifytables(parse->commandType, + parse->canSetTag, + resultRelations, + subplans, + returningLists, + rowMarks, + SS_assign_special_param(root), + partKeyUpdated, + isDfsStore, + 0, + NULL, + NULL, + NULL); #endif -} + /* * @Description: set SortGroupClause's groupSet which will be set to true if it appears in group by clause @@ -2580,227 +2519,181 @@ static bool group_member(List* list, Expr* node) * @in collectiveGroupExpr - collective group exprs. * */ +// 调整计划的分布键 static void adjust_plan_dis_key(PlannerInfo* root, Plan* result_plan, List* collectiveGroupExpr) { - EquivalenceClass* ec = NULL; - ListCell* cell = NULL; - ListCell* lc2 = NULL; + EquivalenceClass* ec = NULL; // 等价类对象 + ListCell* cell = NULL; // 列表遍历器 + ListCell* lc2 = NULL; // 列表遍历器 - /* Do a copy since distribute key is shared by multiple operators */ + // 复制分布键列表 result_plan->distributed_keys = list_copy(result_plan->distributed_keys); + // 遍历分布键列表 foreach (cell, result_plan->distributed_keys) { Expr* dis_key = (Expr*)lfirst(cell); - /* - * If this distribute key is not in collectiveGroupExpr, we need find it's EquivalenceClass. - * If already found, replace it's members expr which be included in collectiveGroupExpr to this distribut key. - */ - if (!group_member(collectiveGroupExpr, dis_key)) { - /* Find include this dis expr equivalence class. */ - ec = get_expr_eqClass(root, dis_key); + // 获取表达式的等价类 + ec = get_expr_eqClass(root, dis_key); - AssertEreport(ec != NULL, MOD_OPT, "invalid EquivalenceClass when setting sort+group distribute keys."); + // 断言等价类不为空 + AssertEreport(ec != NULL, MOD_OPT, "invalid EquivalenceClass when setting sort+group distribute keys."); - foreach (lc2, ec->ec_members) { - EquivalenceMember* em = (EquivalenceMember*)lfirst(lc2); + // 遍历等价类的成员 + foreach (lc2, ec->ec_members) { + EquivalenceMember* em = (EquivalenceMember*)lfirst(lc2); - /* Replace this dis_key with em_expr. */ - if (group_member(collectiveGroupExpr, em->em_expr) && - judge_node_compatible(root, (Node*)dis_key, (Node*)em->em_expr)) { - lfirst(cell) = copyObject(em->em_expr); - break; - } + // 如果等价类成员在collectiveGroupExpr中,并且与分布键兼容,则替换分布键 + if (group_member(collectiveGroupExpr, em->em_expr) && + judge_node_compatible(root, (Node*)dis_key, (Node*)em->em_expr)) { + lfirst(cell) = copyObject(em->em_expr); + break; } + } - if (lc2 == NULL) { - result_plan->distributed_keys = NIL; - return; - } + // 如果没有找到兼容的等价类成员,则清空分布键列表并返回 + if (lc2 == NULL) { + result_plan->distributed_keys = NIL; + return; } } } -/* - * @Description: We need set SortGroupClause's groupSet when groupingSets is not null, - * avoid sort_pathkeys can be deleted if exist equivalence class. - * - * For exanple: - * select t1.a, t2.a from t1 inner join t2 on t1.a = t2.a - * group by grouping sets(t1.a, t2.a) order by 1, 2; - * - * In this case, sort_pathkeys only have t1.a, t2.a already be removed because t1.a = t2.a, - * but because of grouping sets(Ap Function), some value of t1.a and t2.a can be seted to NULL so that - * t1.a and t2.a is not equal, so t2.a can not be removed. Here we will again build sort path keys. - * @in root - Per-query information for planning/optimization. - * @in activeWindows - windows function list. - * @in collectiveGroupExpr - collective group exprs if have grouping set clause. - */ +// 重新构建用于分组集的路径键 template static void rebuild_pathkey_for_groupingSet( PlannerInfo* root, List* tlist, List* activeWindows, List* collectiveGroupExpr) { Query* parse = root->parse; + // 如果不存在分组集或分组子句,则直接返回 if (!parse->groupingSets || !parse->groupClause) { return; } - /* - * To window function, if only need set SortGroupClause's groupset, it's pathkey will - * be maked in grouping_planer's activeWindows part. - */ + // 根据不同的路径键类型进行处理 if (pathKey == windows_func_pathkey) { if (activeWindows != NIL) { WindowClause* wc = NULL; ListCell* l = NULL; + // 遍历活跃的窗口子句 foreach (l, activeWindows) { wc = (WindowClause*)lfirst(l); + // 设置窗口子句的分组集和排序集 set_groupset_for_sortgroup_items(root, wc->partitionClause, tlist, collectiveGroupExpr); set_groupset_for_sortgroup_items(root, wc->orderClause, tlist, collectiveGroupExpr); } } } else if (pathKey == distinct_pathkey) { - /* Make distinct pathkeys which groupSet is true. */ + // 处理distinct路径键 if (parse->distinctClause && grouping_is_sortable(parse->distinctClause)) { + // 设置distinct子句的分组集 set_groupset_for_sortgroup_items(root, parse->distinctClause, tlist, collectiveGroupExpr); + // 构建distinct路径键 root->distinct_pathkeys = make_pathkeys_for_sortclauses(root, parse->distinctClause, tlist, true); } } else if (pathKey == sort_pathkey) { - /* Make sort pathkeys which groupSet is true. */ + // 处理排序路径键 if (parse->sortClause) { + // 设置排序子句的分组集 set_groupset_for_sortgroup_items(root, parse->sortClause, tlist, collectiveGroupExpr); + // 构建排序路径键 root->sort_pathkeys = make_pathkeys_for_sortclauses(root, parse->sortClause, tlist, true); } } } +// 选择最佳路径 static inline Path* choose_best_path(bool use_cheapest_path, PlannerInfo* root, Path* cheapest_path, Path* sorted_path) { - Path* best_path; - if (use_cheapest_path) { - best_path = cheapest_path; - } - else { - best_path = sorted_path; - ereport(DEBUG2, (errmodule(MOD_OPT), (errmsg("Use presorted path instead of cheapest path.")))); - /* print more details */ - if (log_min_messages <= DEBUG2) - debug1_print_new_path(root, best_path, false); - } + Path* best_path; + if (use_cheapest_path) { + best_path = cheapest_path; + } + else { + best_path = sorted_path; + ereport(DEBUG2, (errmodule(MOD_OPT), (errmsg("Use presorted path instead of cheapest path.")))); - return best_path; + // 打印调试信息 + if (log_min_messages <= DEBUG2) + debug1_print_new_path(root, best_path, false); + } + + return best_path; } #ifdef ENABLE_MULTIPLE_NODES +// 检查目标列表是否包含时间序列函数调用 static bool has_ts_func(List* tlist) { FillWalkerContext fill_context; error_t rc = memset_s(&fill_context, sizeof(fill_context), 0, sizeof(fill_context)); securec_check(rc, "\0", "\0"); + // 使用表达式树遍历器检查目标列表中是否包含时间序列函数调用 expression_tree_walker((Node*)tlist, (walker)fill_function_call_walker, &fill_context); if (fill_context.fill_func_calls > 0 || fill_context.fill_last_func_calls > 0 || fill_context.column_calls > 0) { return true; } - return false; + return false; } #endif -/* -------------------- - * grouping_planner - * Perform planning steps related to grouping, aggregation, etc. - * This primarily means adding top-level processing to the basic - * query plan produced by query_planner. - * - * tuple_fraction is the fraction of tuples we expect will be retrieved - * - * tuple_fraction is interpreted as follows: - * 0: expect all tuples to be retrieved (normal case) - * 0 < tuple_fraction < 1: expect the given fraction of tuples available - * from the plan to be retrieved - * tuple_fraction >= 1: tuple_fraction is the absolute number of tuples - * expected to be retrieved (ie, a LIMIT specification) - * - * Returns a query plan. Also, root->query_pathkeys is returned as the - * actual output ordering of the plan (in pathkey format). - * -------------------- - */ +// 分组查询计划生成函数 static Plan* grouping_planner(PlannerInfo* root, double tuple_fraction) { - Query* parse = root->parse; - List* tlist = parse->targetList; - int64 offset_est = 0; - int64 count_est = 0; - double limit_tuples = -1.0; - Plan* result_plan = NULL; - List* current_pathkeys = NIL; - double dNumGroups[2] = {1, 1}; /* dNumGroups[0] is local distinct, dNumGroups[1] is global distinct. */ - bool use_hashed_distinct = false; - bool tested_hashed_distinct = false; - bool needs_stream = false; - bool has_second_agg_sort = false; - List* collectiveGroupExpr = NIL; - RelOptInfo* rel_info = NULL; - char PlanContextName[NAMEDATALEN] = {0}; - MemoryContext PlanGenerateContext = NULL; - MemoryContext oldcontext = NULL; + Query* parse = root->parse; // 获取查询解析树 + List* tlist = parse->targetList; // 获取目标列表 + int64 offset_est = 0; // 偏移估算 + int64 count_est = 0; // 计数估算 + double limit_tuples = -1.0; // 限制的元组数 + Plan* result_plan = NULL; // 结果计划 + List* current_pathkeys = NIL; // 当前路径键 + double dNumGroups[2] = {1, 1}; // 不同分组类型的估算元组数 + bool use_hashed_distinct = false; // 是否使用哈希去重 + bool tested_hashed_distinct = false; // 是否已测试哈希去重 + bool needs_stream = false; // 是否需要流式计划 + bool has_second_agg_sort = false; // 是否存在第二个聚合排序 + List* collectiveGroupExpr = NIL; // 集合分组表达式列表 + RelOptInfo* rel_info = NULL; // 关系信息 + char PlanContextName[NAMEDATALEN] = {0}; // 计划上下文名称 + MemoryContext PlanGenerateContext = NULL; // 计划生成上下文 + MemoryContext oldcontext = NULL; // 旧的内存上下文 errno_t rc = EOK; - /* - * Apply memory context for generate plan in optimizer. - * OptimizerContext is NULL in PBE condition which we need to consider. - */ + // 构建计划生成上下文 rc = snprintf_s(PlanContextName, NAMEDATALEN, NAMEDATALEN - 1, "PlanGenerateContext_%d", root->query_level); securec_check_ss(rc, "\0", "\0"); - PlanGenerateContext = AllocSetContextCreate(CurrentMemoryContext, PlanContextName, ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE); - /* Tweak caller-supplied tuple_fraction if have LIMIT/OFFSET */ + // 处理LIMIT子句,并更新tuple_fraction、offset_est和count_est if (parse->limitCount || parse->limitOffset) { tuple_fraction = preprocess_limit(root, tuple_fraction, &offset_est, &count_est); - /* - * If we have a known LIMIT, and don't have an unknown OFFSET, we can - * estimate the effects of using a bounded sort. - */ if (count_est > 0 && offset_est >= 0) limit_tuples = (double)count_est + (double)offset_est; } + // 如果存在集合操作子句,则计划集合操作 if (parse->setOperations) { List* set_sortclauses = NIL; - /* - * If there's a top-level ORDER BY, assume we have to fetch all the - * tuples. This might be too simplistic given all the hackery below - * to possibly avoid the sort; but the odds of accurate estimates here - * are pretty low anyway. - */ + // 如果存在排序子句,则将tuple_fraction设置为0 if (parse->sortClause) tuple_fraction = 0.0; - /* - * Construct the plan for set operations. The result will not need - * any work except perhaps a top-level sort and/or LIMIT. Note that - * any special work for recursive unions is the responsibility of - * plan_set_operations. - */ + // 计划集合操作,获取排序子句 result_plan = plan_set_operations(root, tuple_fraction, &set_sortclauses); - /* - * Calculate pathkeys representing the sort order (if any) of the set - * operation's result. We have to do this before overwriting the sort - * key information... - */ + // 构建当前路径键 current_pathkeys = make_pathkeys_for_sortclauses(root, set_sortclauses, result_plan->targetlist, true); /* @@ -2810,196 +2703,179 @@ static Plan* grouping_planner(PlannerInfo* root, double tuple_fraction) * resjunk columns!), and transfer any sort key information from the * original tlist. */ - AssertEreport( - parse->commandType == CMD_SELECT, MOD_OPT, "unexpected command type when performing grouping planner."); + // 断言查询命令类型为CMD_SELECT,否则抛出错误 +AssertEreport( + parse->commandType == CMD_SELECT, MOD_OPT, "unexpected command type when performing grouping planner."); - tlist = postprocess_setop_tlist((List*)copyObject(result_plan->targetlist), tlist); +// 对目标列表进行后处理 +tlist = postprocess_setop_tlist((List*)copyObject(result_plan->targetlist), tlist); - /* - * Can't handle FOR [KEY] UPDATE/SHARE here (parser should have checked - * already, but let's make sure). - */ - if (parse->rowMarks) - ereport(ERROR, - (errmodule(MOD_OPT), errcode(ERRCODE_FEATURE_NOT_SUPPORTED), +// 如果存在行标记,则抛出不支持的特性错误 +if (parse->rowMarks) + ereport(ERROR, + (errmodule(MOD_OPT), errcode(ERRCODE_FEATURE_NOT_SUPPORTED), #ifndef ENABLE_MULTIPLE_NODES - errmsg("SELECT FOR UPDATE/SHARE/NO KEY UPDATE/KEY SHARE is not allowed " - "with UNION/INTERSECT/EXCEPT"), + errmsg("SELECT FOR UPDATE/SHARE/NO KEY UPDATE/KEY SHARE is not allowed " + "with UNION/INTERSECT/EXCEPT"), #else - errmsg("SELECT FOR UPDATE/SHARE is not allowed with UNION/INTERSECT/EXCEPT"), + errmsg("SELECT FOR UPDATE/SHARE is not allowed with UNION/INTERSECT/EXCEPT"), #endif - errdetail("N/A"), - errcause("SQL uses unsupported feature."), - erraction("Modify SQL statement according to the manual."))); + errdetail("N/A"), + errcause("SQL uses unsupported feature."), + erraction("Modify SQL statement according to the manual."))); - /* - * Calculate pathkeys that represent result ordering requirements - */ - AssertEreport(parse->distinctClause == NIL, - MOD_OPT, - "The distinct clause is not allowed when calculating pathkeys for sortclauses."); - root->sort_pathkeys = make_pathkeys_for_sortclauses(root, parse->sortClause, tlist, true); - } else { - /* No set operations, do regular planning */ - List* sub_tlist = NIL; - double sub_limit_tuples; - AttrNumber* groupColIdx = NULL; - bool need_tlist_eval = true; - Path* cheapest_path = NULL; - Path* sorted_path = NULL; - Path* best_path = NULL; - double numGroups[2] = {1, 1}; - long localNumGroup = 1; - AggClauseCosts agg_costs; - int numGroupCols; - double path_rows; - int path_width; - bool use_hashed_grouping = false; - WindowLists* wflists = NULL; - uint32 maxref = 0; - int* tleref_to_colnum_map = NULL; - List* rollup_lists = NIL; - List* rollup_groupclauses = NIL; - bool needSecondLevelAgg = true; /* For olap function*/ - List* superset_key = root->dis_keys.superset_keys; - Size hash_entry_size = 0; - char PathContextName[NAMEDATALEN] = {0}; - MemoryContext PathGenerateContext = NULL; - RelOptInfo* final_rel = NULL; - standard_qp_extra qp_extra; +// 断言不应存在distinct子句,然后构建排序路径键 +AssertEreport(parse->distinctClause == NIL, + MOD_OPT, + "The distinct clause is not allowed when calculating pathkeys for sortclauses."); +root->sort_pathkeys = make_pathkeys_for_sortclauses(root, parse->sortClause, tlist, true); +} else { + /* No set operations, do regular planning */ + List* sub_tlist = NIL; + double sub_limit_tuples; + AttrNumber* groupColIdx = NULL; + bool need_tlist_eval = true; + Path* cheapest_path = NULL; + Path* sorted_path = NULL; + Path* best_path = NULL; + double numGroups[2] = {1, 1}; + long localNumGroup = 1; + AggClauseCosts agg_costs; + int numGroupCols; + double path_rows; + int path_width; + bool use_hashed_grouping = false; + WindowLists* wflists = NULL; + uint32 maxref = 0; + int* tleref_to_colnum_map = NULL; + List* rollup_lists = NIL; + List* rollup_groupclauses = NIL; + bool needSecondLevelAgg = true; /* For olap function*/ + List* superset_key = root->dis_keys.superset_keys; + Size hash_entry_size = 0; + char PathContextName[NAMEDATALEN] = {0}; + MemoryContext PathGenerateContext = NULL; + RelOptInfo* final_rel = NULL; + standard_qp_extra qp_extra; - /* Apply memory context for generate path in optimizer. */ - rc = snprintf_s(PathContextName, NAMEDATALEN, NAMEDATALEN - 1, "PathGenerateContext_%d", root->query_level); - securec_check_ss(rc, "\0", "\0"); + // 构建计划生成上下文 + rc = snprintf_s(PathContextName, NAMEDATALEN, NAMEDATALEN - 1, "PathGenerateContext_%d", root->query_level); + securec_check_ss(rc, "\0", "\0"); + PathGenerateContext = AllocSetContextCreate(CurrentMemoryContext, + PathContextName, + ALLOCSET_DEFAULT_MINSIZE, + ALLOCSET_DEFAULT_INITSIZE, + ALLOCSET_DEFAULT_MAXSIZE); + oldcontext = MemoryContextSwitchTo(PathGenerateContext); - PathGenerateContext = AllocSetContextCreate(CurrentMemoryContext, - PathContextName, - ALLOCSET_DEFAULT_MINSIZE, - ALLOCSET_DEFAULT_INITSIZE, - ALLOCSET_DEFAULT_MAXSIZE); - oldcontext = MemoryContextSwitchTo(PathGenerateContext); + // 初始化聚合成本信息 + errno_t errorno = memset_s(&agg_costs, sizeof(AggClauseCosts), 0, sizeof(AggClauseCosts)); + securec_check(errorno, "\0", "\0"); - errno_t errorno = memset_s(&agg_costs, sizeof(AggClauseCosts), 0, sizeof(AggClauseCosts)); - securec_check(errorno, "\0", "\0"); + // 断言不应存在递归查询 + AssertEreport(!root->hasRecursion, MOD_OPT, "A recursive query is not allowed when doing regular planning."); - /* A recursive query should always have setOperations */ - AssertEreport(!root->hasRecursion, MOD_OPT, "A recursive query is not allowed when doing regular planning."); + // 如果存在分组集,将其展开 + if (parse->groupingSets) + parse->groupingSets = expand_grouping_sets(parse->groupingSets, -1); - /* Preprocess GROUP BY clause, if any */ - /* Preprocess Grouping set, if any */ - if (parse->groupingSets) - parse->groupingSets = expand_grouping_sets(parse->groupingSets, -1); + // 初始化最大引用值 + if (parse->groupClause) { + ListCell* lc = NULL; + foreach (lc, parse->groupClause) { + SortGroupClause* gc = (SortGroupClause*)lfirst(lc); + if (gc->tleSortGroupRef > maxref) + maxref = gc->tleSortGroupRef; + } + } + tleref_to_colnum_map = (int*)palloc((maxref + 1) * sizeof(int)); - if (parse->groupClause) { - ListCell* lc = NULL; + // 如果存在分组子句,预处理分组子句 + if (parse->groupingSets) { + ListCell* lc = NULL; + ListCell* lc2 = NULL; + ListCell* lc_set = NULL; + List* sets = extract_rollup_sets(parse->groupingSets); + bool isfirst = true; - foreach (lc, parse->groupClause) { + foreach (lc_set, sets) { + List* current_sets = reorder_grouping_sets((List*)lfirst(lc_set), + (list_length(sets) == 1 ? parse->sortClause : NIL)); + + List* groupclause = preprocess_groupclause(root, (List*)linitial(current_sets)); + + // 如果是第一个集合,则初始化collectiveGroupExpr + if (isfirst) { + collectiveGroupExpr = get_group_expr((List*)llast(current_sets), tlist); + } else if (collectiveGroupExpr != NIL) { + // 对于非第一个集合,计算与collectiveGroupExpr的交集 + collectiveGroupExpr = + list_intersection(collectiveGroupExpr, get_group_expr((List*)llast(current_sets), tlist)); + } + isfirst = false; + + int ref = 0; + + // 为分组子句中的每个引用映射列号 + foreach (lc, groupclause) { SortGroupClause* gc = (SortGroupClause*)lfirst(lc); - - if (gc->tleSortGroupRef > maxref) - maxref = gc->tleSortGroupRef; + tleref_to_colnum_map[gc->tleSortGroupRef] = ref++; } - } - tleref_to_colnum_map = (int*)palloc((maxref + 1) * sizeof(int)); - if (parse->groupingSets) { - ListCell* lc = NULL; - ListCell* lc2 = NULL; - ListCell* lc_set = NULL; - List* sets = extract_rollup_sets(parse->groupingSets); - bool isfirst = true; - - /* Keep all groupby columns in sets, each cell of sets is a rollup, the cell include many list */ - foreach (lc_set, sets) { - List* current_sets = - reorder_grouping_sets((List*)lfirst(lc_set), (list_length(sets) == 1 ? parse->sortClause : NIL)); - - List* groupclause = preprocess_groupclause(root, (List*)linitial(current_sets)); - - if (isfirst) { - collectiveGroupExpr = get_group_expr((List*)llast(current_sets), tlist); - } else if (collectiveGroupExpr != NIL) { - /* Last group idxs intersection */ - collectiveGroupExpr = - list_intersection(collectiveGroupExpr, get_group_expr((List*)llast(current_sets), tlist)); + // 映射每个集合中的引用 + foreach (lc, current_sets) { + foreach (lc2, (List*)lfirst(lc)) { + lfirst_int(lc2) = tleref_to_colnum_map[lfirst_int(lc2)]; } - isfirst = false; - - int ref = 0; - - /* - * Now that we've pinned down an order for the groupClause for - * this list of grouping sets, we need to remap the entries in - * the grouping sets from sortgrouprefs to plain indices - * (0-based) into the groupClause for this collection of - * grouping sets. - */ - foreach (lc, groupclause) { - SortGroupClause* gc = (SortGroupClause*)lfirst(lc); - - tleref_to_colnum_map[gc->tleSortGroupRef] = ref++; - } - - foreach (lc, current_sets) { - foreach (lc2, (List*)lfirst(lc)) { - lfirst_int(lc2) = tleref_to_colnum_map[lfirst_int(lc2)]; - } - } - - rollup_lists = lcons(current_sets, rollup_lists); - rollup_groupclauses = lcons(groupclause, rollup_groupclauses); } - } else { - /* Preprocess GROUP BY clause, if any */ - if (parse->groupClause) - parse->groupClause = preprocess_groupclause(root, NIL); - rollup_groupclauses = list_make1(parse->groupClause); + + // 将当前集合和分组子句添加到对应的列表中 + rollup_lists = lcons(current_sets, rollup_lists); + rollup_groupclauses = lcons(groupclause, rollup_groupclauses); } + } else { + // 如果不存在分组集,但存在分组子句,则预处理分组子句 + if (parse->groupClause) + parse->groupClause = preprocess_groupclause(root, NIL); + rollup_groupclauses = list_make1(parse->groupClause); + } - numGroupCols = list_length(parse->groupClause); + // 计算分组列的数量 + numGroupCols = list_length(parse->groupClause); - /* Preprocess targetlist */ - tlist = preprocess_targetlist(root, tlist); + // 预处理目标列表 + tlist = preprocess_targetlist(root, tlist); - if (parse->upsertClause) { - UpsertExpr* upsertClause = parse->upsertClause; - upsertClause->updateTlist = - preprocess_upsert_targetlist(upsertClause->updateTlist, parse->resultRelation, parse->rtable); - } - /* - * Locate any window functions in the tlist. (We don't need to look - * anywhere else, since expressions used in ORDER BY will be in there - * too.) Note that they could all have been eliminated by constant - * folding, in which case we don't need to do any more work. - */ - if (parse->hasWindowFuncs) { - wflists = make_windows_lists(list_length(parse->windowClause)); - find_window_functions((Node*)tlist, wflists); + // 如果存在UPSERT子句,则预处理UPSERT目标列表 + if (parse->upsertClause) { + UpsertExpr* upsertClause = parse->upsertClause; + upsertClause->updateTlist = + preprocess_upsert_targetlist(upsertClause->updateTlist, parse->resultRelation, parse->rtable); + } - if (wflists->numWindowFuncs > 0) - select_active_windows(root, wflists); - else - parse->hasWindowFuncs = false; - } + // 如果查询中包含窗口函数,则构建窗口函数列表 + if (parse->hasWindowFuncs) { + wflists = make_windows_lists(list_length(parse->windowClause)); + find_window_functions((Node*)tlist, wflists); - /* - * Check this query if is correlation subquery, if is we will - * set correlated flag from correlative root to current root. - */ - check_plan_correlation(root, (Node*)parse); + if (wflists->numWindowFuncs > 0) + select_active_windows(root, wflists); + else + parse->hasWindowFuncs = false; + } - /* - * Generate appropriate target list for subplan; may be different from - * tlist if grouping or aggregation is needed. - */ - sub_tlist = make_subplanTargetList(root, tlist, &groupColIdx, &need_tlist_eval); + // 检查计划的相关性 + check_plan_correlation(root, (Node*)parse); - /* Set matching and superset key for planner info of current query level */ - if (IS_STREAM_PLAN) { - set_root_matching_key(root, tlist); + // 创建子查询的目标列表 + sub_tlist = make_subplanTargetList(root, tlist, &groupColIdx, &need_tlist_eval); - build_grouping_itst_keys(root, wflists ? wflists->activeWindows : NULL); - } + // 如果是分布式计划,则设置匹配键并构建分组的ITST键 + if (IS_STREAM_PLAN) { + set_root_matching_key(root, tlist); + + build_grouping_itst_keys(root, wflists ? wflists->activeWindows : NULL); + } /* * Do aggregate preprocessing, if the query has any aggs.