diff --git a/src/gausskernel/optimizer/commands/aggregatecmds.cpp b/src/gausskernel/optimizer/commands/aggregatecmds.cpp index 68db543f5..ab83d0216 100644 --- a/src/gausskernel/optimizer/commands/aggregatecmds.cpp +++ b/src/gausskernel/optimizer/commands/aggregatecmds.cpp @@ -49,34 +49,35 @@ * is specified by a BASETYPE element in the parameters. Otherwise, * "args" defines the input type(s). */ + // 定义聚合函数 void DefineAggregate(List* name, List* args, bool oldstyle, List* parameters) { - char* aggName = NULL; - Oid aggNamespace; + char* aggName = NULL;// 聚合函数的名称 + Oid aggNamespace;// 聚合函数所在的命名空间 AclResult aclresult; - List* transfuncName = NIL; - List* finalfuncName = NIL; - List* sortoperatorName = NIL; - TypeName* baseType = NULL; - TypeName* transType = NULL; - char* initval = NULL; + List* transfuncName = NIL;// 过渡函数的名称 + List* finalfuncName = NIL;// 最终函数的名称 + List* sortoperatorName = NIL;// 排序操作符的名称 + TypeName* baseType = NULL;// 输入类型的名称 + TypeName* transType = NULL;// 过渡类型的名称 + char* initval = NULL; // 初始化值 #ifdef PGXC - List* collectfuncName = NIL; - char* initcollect = NULL; + List* collectfuncName = NIL;// 收集函数的名称 + char* initcollect = NULL;// 初始化收集值 #endif - Oid* aggArgTypes = NULL; + Oid* aggArgTypes = NULL;// 聚合函数的参数类型列表 int numArgs; - Oid transTypeId; + Oid transTypeId; // 过渡类型的OID ListCell* pl = NULL; /* attribute for ordered set aggregate */ - char aggKind = AGGKIND_NORMAL; + char aggKind = AGGKIND_NORMAL; // 聚合函数的类型,默认为普通聚合函数 /* Convert list of names to a name and namespace */ - aggNamespace = QualifiedNameGetCreationNamespace(name, &aggName); + aggNamespace = QualifiedNameGetCreationNamespace(name, &aggName);// 从名称获取聚合函数所在的命名空间和名称 /* Check we have creation rights in target namespace */ - aclresult = pg_namespace_aclcheck(aggNamespace, GetUserId(), ACL_CREATE); + aclresult = pg_namespace_aclcheck(aggNamespace, GetUserId(), ACL_CREATE);// 检查当前用户是否有在命名空间下创建聚合函数的权限 if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(aggNamespace)); if (u_sess->attr.attr_sql.enforce_a_behavior) { @@ -99,7 +100,7 @@ void DefineAggregate(List* name, List* args, bool oldstyle, List* parameters) aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(aggNamespace)); } } - foreach (pl, parameters) { + foreach (pl, parameters) {// 遍历参数列表,解析聚合函数的各个属性 DefElem* defel = (DefElem*)lfirst(pl); /* @@ -138,15 +139,15 @@ void DefineAggregate(List* name, List* args, bool oldstyle, List* parameters) /* * make sure we have our required definitions */ - if (transType == NULL) + if (transType == NULL)// 检查过渡类型是否已经指定 ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("aggregate stype must be specified"))); - if (transfuncName == NIL) + if (transfuncName == NIL)// 检查过渡函数是否已经指定 ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("aggregate sfunc must be specified"))); /* * look up the aggregate's input datatype(s). */ - if (oldstyle) { + if (oldstyle) {// 如果使用旧式语法定义聚合函数 /* * Old style: use basetype parameter. This supports aggregates of * zero or one input, with input type ANY meaning zero inputs. @@ -205,7 +206,9 @@ void DefineAggregate(List* name, List* args, bool oldstyle, List* parameters) * worse) by connecting up incompatible internal-using functions in an * aggregate. */ + // 解析过渡类型的OID transTypeId = typenameTypeId(NULL, transType); + // 检查过渡类型是否为伪类型,且不是多态类型 if (get_typtype(transTypeId) == TYPTYPE_PSEUDO && !IsPolymorphicType(transTypeId) && (transTypeId != INTERNALOID || !superuser())) { ereport(ERROR, @@ -235,9 +238,9 @@ void DefineAggregate(List* name, List* args, bool oldstyle, List* parameters) #endif } -void RenameAggregate(List* name, List* args, const char* newname) +void RenameAggregate(List* name, List* args, const char* newname)// 重命名聚合函数 { - Oid procOid; + Oid procOid;// 聚合函数的OID Oid namespaceOid; HeapTuple tup; Form_pg_proc procForm; @@ -247,9 +250,9 @@ void RenameAggregate(List* name, List* args, const char* newname) rel = heap_open(ProcedureRelationId, RowExclusiveLock); /* Look up function and make sure it's an aggregate */ - procOid = LookupAggNameTypeNames(name, args, false); + procOid = LookupAggNameTypeNames(name, args, false);// 获取聚合函数的OID - tup = SearchSysCacheCopy1(PROCOID, ObjectIdGetDatum(procOid)); + tup = SearchSysCacheCopy1(PROCOID, ObjectIdGetDatum(procOid));// 在系统表中查找聚合函数的元组 if (!HeapTupleIsValid(tup)) /* should not happen */ ereport(ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for function %u", procOid))); procForm = (Form_pg_proc)GETSTRUCT(tup); @@ -261,7 +264,7 @@ void RenameAggregate(List* name, List* args, const char* newname) if (isNull) { packageoid = DatumGetObjectId(InvalidOid); } - +// 检查新名称是否与已存在的函数名称冲突 #ifndef ENABLE_MULTIPLE_NODES Datum allargtypes = ProcedureGetAllArgTypes(tup, &isNull); Datum argmodes = SysCacheGetAttr(PROCOID, tup, Anum_pg_proc_proargmodes, &isNull); @@ -289,16 +292,16 @@ void RenameAggregate(List* name, List* args, const char* newname) get_namespace_name(namespaceOid)))); #endif /* must be owner */ - if (!pg_proc_ownercheck(procOid, GetUserId())) + if (!pg_proc_ownercheck(procOid, GetUserId()))// 检查当前用户是否是函数的所有者 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC, NameListToString(name)); /* must have CREATE privilege on namespace */ - aclresult = pg_namespace_aclcheck(namespaceOid, GetUserId(), ACL_CREATE); + aclresult = pg_namespace_aclcheck(namespaceOid, GetUserId(), ACL_CREATE);// 检查命名空间的权限 if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(namespaceOid)); /* rename */ - (void)namestrcpy(&(((Form_pg_proc)GETSTRUCT(tup))->proname), newname); + (void)namestrcpy(&(((Form_pg_proc)GETSTRUCT(tup))->proname), newname);// 更新函数名称并更新系统缓存 simple_heap_update(rel, &tup->t_self, tup); CatalogUpdateIndexes(rel, tup); @@ -309,7 +312,7 @@ void RenameAggregate(List* name, List* args, const char* newname) /* * Change aggregate owner */ -void AlterAggregateOwner(List* name, List* args, Oid newOwnerId) +void AlterAggregateOwner(List* name, List* args, Oid newOwnerId)// 修改聚合函数的所有者 { Oid procOid; diff --git a/src/gausskernel/optimizer/geqo/geqo_copy.cpp b/src/gausskernel/optimizer/geqo/geqo_copy.cpp index 4a259823f..f436a93ef 100644 --- a/src/gausskernel/optimizer/geqo/geqo_copy.cpp +++ b/src/gausskernel/optimizer/geqo/geqo_copy.cpp @@ -44,8 +44,8 @@ void geqo_copy(PlannerInfo* root, Chromosome* chromo1, Chromosome* chromo2, int { int i; - for (i = 0; i < string_length; i++) + for (i = 0; i < string_length; i++)// 使用循环遍历染色体的字符串,将chromo2的内容复制到chromo1中 chromo1->string[i] = chromo2->string[i]; - chromo1->worth = chromo2->worth; + chromo1->worth = chromo2->worth;// 复制chromo2的worth值到chromo1 } diff --git a/src/gausskernel/optimizer/geqo/geqo_cx.cpp b/src/gausskernel/optimizer/geqo/geqo_cx.cpp index ea5c5780c..f9a5346e5 100644 --- a/src/gausskernel/optimizer/geqo/geqo_cx.cpp +++ b/src/gausskernel/optimizer/geqo/geqo_cx.cpp @@ -41,17 +41,19 @@ * * cycle crossover */ + //这段代码实现了遗传算法中的一种交叉操作(Crossover),用于生成新的候选解。 + //函数的目标是从两个不同的父代查询计划,生成一个新的子代查询计划 int cx(PlannerInfo* root, Gene* tour1, Gene* tour2, Gene* offspring, int num_gene, City* city_table) { int i, start_pos, curr_pos; int count = 0; - int num_diffs = 0; + int num_diffs = 0; /* initialize city table */ for (i = 1; i <= num_gene; i++) { - city_table[i].used = 0; - city_table[tour2[i - 1]].tour2_position = i - 1; - city_table[tour1[i - 1]].tour1_position = i - 1; + city_table[i].used = 0;// 将节点表标记为未使用 + city_table[tour2[i - 1]].tour2_position = i - 1;// 记录tour2中每个基因在数组中的位置 + city_table[tour1[i - 1]].tour1_position = i - 1;// 记录tour1中每个基因在数组中的位置 } /* choose random cycle starting position */ @@ -69,10 +71,11 @@ int cx(PlannerInfo* root, Gene* tour1, Gene* tour2, Gene* offspring, int num_gen /* cx main part */ /* STEP 1 */ while (tour2[curr_pos] != tour1[start_pos]) { - city_table[(int)tour2[curr_pos]].used = 1; - curr_pos = city_table[(int)tour2[curr_pos]].tour1_position; - offspring[curr_pos] = tour1[curr_pos]; - count++; + city_table[(int)tour2[curr_pos]].used = 1;// 标记tour2中的节点为已使用 + curr_pos = city_table[(int)tour2[curr_pos]].tour1_position;// 切换到tour1中相应节点的位置 + offspring[curr_pos] = tour1[curr_pos];// 将对应的tour1节点添加到offspring中 + + count++;// 增加已添加节点的计数 } /* STEP 2 */ @@ -81,7 +84,7 @@ int cx(PlannerInfo* root, Gene* tour1, Gene* tour2, Gene* offspring, int num_gen for (i = 1; i <= num_gene; i++) { if (!city_table[i].used) { offspring[city_table[i].tour2_position] = tour2[(int)city_table[i].tour2_position]; - count++; + count++;// 增加已添加节点的计数 } } } @@ -92,10 +95,10 @@ int cx(PlannerInfo* root, Gene* tour1, Gene* tour2, Gene* offspring, int num_gen /* count the number of differences between mom and offspring */ for (i = 0; i < num_gene; i++) { if (tour1[i] != offspring[i]) { - num_diffs++; + num_diffs++;// 增加不同之处的计数 } } } - return num_diffs; + return num_diffs;// 返回不同之处的数量 } diff --git a/src/gausskernel/optimizer/geqo/geqo_erx.cpp b/src/gausskernel/optimizer/geqo/geqo_erx.cpp index ecb728020..6e375f1ad 100644 --- a/src/gausskernel/optimizer/geqo/geqo_erx.cpp +++ b/src/gausskernel/optimizer/geqo/geqo_erx.cpp @@ -33,10 +33,17 @@ #include "optimizer/geqo_recombination.h" #include "optimizer/geqo_random.h" +// 声明函数gimme_edge,它接受PlannerInfo结构体指针root,两个Gene节点gene1和gene2, +// 以及Edge结构体指针edge_table作为参数,返回一个整数。 static int gimme_edge(PlannerInfo* root, Gene gene1, Gene gene2, Edge* edge_table); +// 声明函数remove_gene,它接受PlannerInfo结构体指针root,Gene gene,Edge edge, +// 以及Edge结构体指针edge_table作为参数,不返回值。 static void remove_gene(PlannerInfo* root, Gene gene, Edge edge, Edge* edge_table); +// 声明函数gimme_gene,它接受PlannerInfo结构体指针root,Edge edge, +// 以及Edge结构体指针edge_table作为参数,返回一个Gene。 static Gene gimme_gene(PlannerInfo* root, Edge edge, Edge* edge_table); - +// 声明函数edge_failure,它接受PlannerInfo结构体指针root,Gene指针gene,整数index, +// Edge结构体指针edge_table,以及整数num_gene作为参数,返回一个Gene。 static Gene edge_failure(PlannerInfo* root, Gene* gene, int index, Edge* edge_table, int num_gene); /* alloc_edge_table @@ -44,6 +51,8 @@ static Gene edge_failure(PlannerInfo* root, Gene* gene, int index, Edge* edge_ta * allocate memory for edge table * */ + // 分配Edge结构体数组edge_table的内存,接受PlannerInfo结构体指针root和整数num_gene作为参数, +// 返回一个Edge结构体指针。 Edge* alloc_edge_table(PlannerInfo* root, int num_gene) { Edge* edge_table = NULL; @@ -52,7 +61,7 @@ Edge* alloc_edge_table(PlannerInfo* root, int num_gene) * palloc one extra location so that nodes numbered 1..n can be indexed * directly; 0 will not be used */ - edge_table = (Edge*)palloc((num_gene + 1) * sizeof(Edge)); + edge_table = (Edge*)palloc((num_gene + 1) * sizeof(Edge));// 使用palloc函数为edge_table分配内存。 return edge_table; } @@ -62,6 +71,8 @@ Edge* alloc_edge_table(PlannerInfo* root, int num_gene) * deallocate memory of edge table * */ + // 释放Edge结构体数组edge_table的内存,接受PlannerInfo结构体指针root和Edge结构体指针edge_table作为参数, +// 不返回值。 void free_edge_table(PlannerInfo* root, Edge* edge_table) { pfree_ext(edge_table); @@ -80,20 +91,22 @@ void free_edge_table(PlannerInfo* root, Edge* edge_table) * where 2.0=homogeneous; 4.0=diverse * */ + // 计算两个遗传算法个体(tour1和tour2)之间的边数,接受PlannerInfo结构体指针root, +// 两个Gene指针tour1和tour2,整数num_gene,以及Edge结构体指针edge_table作为参数,返回一个浮点数。 float gimme_edge_table(PlannerInfo* root, Gene* tour1, Gene* tour2, int num_gene, Edge* edge_table) { int i, index1, index2; int edge_total; /* total number of unique edges in two genes */ /* at first clear the edge table's old data */ - for (i = 1; i <= num_gene; i++) { + for (i = 1; i <= num_gene; i++) {// 初始化edge_table中各个表节点的边数信息。 edge_table[i].total_edges = 0; edge_table[i].unused_edges = 0; } /* fill edge table with new data */ edge_total = 0; - +// 计算两个遗传算法个体之间的边数,调用gimme_edge函数。 for (index1 = 0; index1 < num_gene; index1++) { /* * presume the tour is circular, i.e. 1->2, 2->3, 3->1 this operaton @@ -130,6 +143,8 @@ float gimme_edge_table(PlannerInfo* root, Gene* tour1, Gene* tour2, int num_gene * returns 1 if edge was not already registered and was just added; * 0 if edge was already registered and edge_table is unchanged */ + // 实现gimme_edge函数,接受PlannerInfo结构体指针root,两个Gene gene1和gene2, +// 以及Edge结构体指针edge_table作为参数,返回一个整数。 static int gimme_edge(PlannerInfo* root, Gene gene1, Gene gene2, Edge* edge_table) { int i; @@ -138,10 +153,10 @@ static int gimme_edge(PlannerInfo* root, Gene gene1, Gene gene2, Edge* edge_tabl int city2 = (int)gene2; /* check whether edge city1->city2 already exists */ - edges = edge_table[city1].total_edges; + edges = edge_table[city1].total_edges;// 获取表city1的边数。 - for (i = 0; i < edges; i++) { - if ((Gene)Abs(edge_table[city1].edge_list[i]) == city2) { + for (i = 0; i < edges; i++) {// 遍历查询表city1的边列表,查找是否有查询表city2。 + if ((Gene)Abs(edge_table[city1].edge_list[i]) == city2) { // 如果找到,将对应边的标记置为负数表示已使用。 /* mark shared edges as negative */ edge_table[city1].edge_list[i] = 0 - city2; @@ -150,7 +165,7 @@ static int gimme_edge(PlannerInfo* root, Gene gene1, Gene gene2, Edge* edge_tabl } /* add city1->city2; */ - edge_table[city1].edge_list[edges] = city2; + edge_table[city1].edge_list[edges] = city2;// 如果没有找到表city2,将city2添加到city1的边列表中。 /* increment the number of edges from city1 */ edge_table[city1].total_edges++; @@ -167,10 +182,11 @@ static int gimme_edge(PlannerInfo* root, Gene gene1, Gene gene2, Edge* edge_tabl * in the edge table.) * */ + int gimme_tour(PlannerInfo* root, Edge* edge_table, Gene* new_gene, int num_gene) { int i; - int edge_failures = 0; + int edge_failures = 0;// 记录连接失败的数量。 /* choose int between 1 and num_gene */ new_gene[0] = (Gene)geqo_randint(root, num_gene, 1); @@ -180,23 +196,22 @@ int gimme_tour(PlannerInfo* root, Edge* edge_table, Gene* new_gene, int num_gene * as each point is entered into the tour, remove it from the edge * table */ - remove_gene(root, new_gene[i - 1], edge_table[(int)new_gene[i - 1]], edge_table); + remove_gene(root, new_gene[i - 1], edge_table[(int)new_gene[i - 1]], edge_table);// 从边表中移除前一个表和当前表之间的连接。 /* find destination for the newly entered point */ - if (edge_table[new_gene[i - 1]].unused_edges > 0) + if (edge_table[new_gene[i - 1]].unused_edges > 0)// 如果前一个表还有未使用的连接,就随机选择下一个表。 new_gene[i] = gimme_gene(root, edge_table[(int)new_gene[i - 1]], edge_table); - else { /* cope with fault */ + else { /* cope with fault */// 如果前一个表没有未使用的连接,则处理连接失败。 edge_failures++; new_gene[i] = edge_failure(root, new_gene, i - 1, edge_table, num_gene); } /* mark this node as incorporated */ - edge_table[(int)new_gene[i - 1]].unused_edges = -1; - } + edge_table[(int)new_gene[i - 1]].unused_edges = -1;// 将前一个表的未使用连接数设为-1,表示已经使用过。 - return edge_failures; + return edge_failures;// 返回连接失败的数量。 } /* remove_gene @@ -206,23 +221,25 @@ int gimme_tour(PlannerInfo* root, Edge* edge_table, Gene* new_gene, int num_gene * to identify deletion locations within edge table. * */ + // 实现remove_gene函数,用于从边列表中移除gene,接受PlannerInfo结构体指针root, +// Gene gene,Edge edge,以及Edge结构体指针edge_table作为参数,不返回值。 static void remove_gene(PlannerInfo* root, Gene gene, Edge edge, Edge* edge_table) { - int i, j; - int possess_edge; - int genes_remaining; + int i, j;//循环计数器和内部循环计数器 + int possess_edge;//用于存储另一个查询表的标识 + int genes_remaining;//用于跟踪节点表的未使用边列表中剩余的未使用边的数量。 /* * do for every gene known to have an edge to input gene (i.e. in * edge_list for input edge) */ - for (i = 0; i < edge.unused_edges; i++) { + for (i = 0; i < edge.unused_edges; i++) { // 遍历城市gene的未使用边列表。 possess_edge = (int)Abs(edge.edge_list[i]); genes_remaining = edge_table[possess_edge].unused_edges; /* find the input gene in all edge_lists and delete it */ for (j = 0; j < genes_remaining; j++) { - if ((Gene)Abs(edge_table[possess_edge].edge_list[j]) == gene) { + if ((Gene)Abs(edge_table[possess_edge].edge_list[j]) == gene) {// 找到城市gene,将其从边列表中移除。 edge_table[possess_edge].unused_edges--; edge_table[possess_edge].edge_list[j] = edge_table[possess_edge].edge_list[genes_remaining - 1]; @@ -239,23 +256,25 @@ static void remove_gene(PlannerInfo* root, Gene gene, Edge edge, Edge* edge_tabl * (i.e. edges which both genes possess) * */ + // 实现gimme_gene函数,接受PlannerInfo结构体指针root,Edge edge, +// 以及Edge结构体指针edge_table作为参数,返回一个Gene。 static Gene gimme_gene(PlannerInfo* root, Edge edge, Edge* edge_table) { - int i; - Gene frnd; - int minimum_edges; - int minimum_count = -1; - int rand_decision; - const int EDGES_SIZE = 5; + int i;// 循环计数器 + Gene frnd;// 用于存储目标节点的标识 + int minimum_edges;// 用于跟踪最小未使用边数 + int minimum_count = -1;// 用于跟踪最小未使用边数的节点表数量 + int rand_decision;// 随机决策的值 + const int EDGES_SIZE = 5;// 用于指定边的大小上限 /* * no point has edges to more than 4 other points thus, this contrived * minimum will be replaced */ - minimum_edges = EDGES_SIZE; + minimum_edges = EDGES_SIZE;// 初始化minimum_edges为EDGES_SIZE。 /* consider candidate destination points in edge list */ - for (i = 0; i < edge.unused_edges; i++) { + for (i = 0; i < edge.unused_edges; i++) { /* 考虑边列表中的候选目标点 */ frnd = (Gene)edge.edge_list[i]; /* @@ -266,6 +285,7 @@ static Gene gimme_gene(PlannerInfo* root, Edge edge, Edge* edge_table) * converting to absolute values */ if (frnd < 0) + // 如果目标节点标识小于0,返回其绝对值,表示已经访问过。 return (Gene)Abs(frnd); /* @@ -281,10 +301,11 @@ static Gene gimme_gene(PlannerInfo* root, Edge edge, Edge* edge_table) * that the test will always succeed the first time around. If it can * fail then the code is in error */ - if (edge_table[(int)frnd].unused_edges < minimum_edges) { + if (edge_table[(int)frnd].unused_edges < minimum_edges) {// 如果目标节点的未使用边数少于minimum_edges,则更新minimum_edges和minimum_count。 minimum_edges = edge_table[(int)frnd].unused_edges; minimum_count = 1; } else if (minimum_count == -1) + // 如果minimum_count未设置,报告错误。 ereport(ERROR, (errmodule(MOD_OPT), errcode(ERRCODE_OPTIMIZER_INCONSISTENT_STATE), errmsg("minimum_count not set"))); else if (edge_table[(int)frnd].unused_edges == minimum_edges) @@ -292,13 +313,13 @@ static Gene gimme_gene(PlannerInfo* root, Edge edge, Edge* edge_table) } /* random decision of the possible candidates to use */ - rand_decision = geqo_randint(root, minimum_count - 1, 0); + rand_decision = geqo_randint(root, minimum_count - 1, 0);// 随机选择一个目标节点。 for (i = 0; i < edge.unused_edges; i++) { frnd = (Gene)edge.edge_list[i]; /* return the chosen candidate point */ - if (edge_table[(int)frnd].unused_edges == minimum_edges) { + if (edge_table[(int)frnd].unused_edges == minimum_edges) {// 如果目标节点的未使用边数等于minimum_edges,则减少minimum_count,并根据rand_decision返回目标城市。 minimum_count--; if (minimum_count == rand_decision) @@ -307,7 +328,7 @@ static Gene gimme_gene(PlannerInfo* root, Edge edge, Edge* edge_table) } /* ... should never be reached */ - ereport(ERROR, + ereport(ERROR,// 如果没有找到满足条件的目标节点,报告错误 (errmodule(MOD_OPT), errcode(ERRCODE_OPTIMIZER_INCONSISTENT_STATE), errmsg("neither shared nor minimum number nor random edge found"))); @@ -319,19 +340,22 @@ static Gene gimme_gene(PlannerInfo* root, Edge edge, Edge* edge_table) * routine for handling edge failure * */ + // 实现edge_failure函数,接受PlannerInfo结构体指针root,Gene指针gene,整数index, +// Edge结构体指针edge_table,以及整数num_gene作为参数,返回一个Gene。 static Gene edge_failure(PlannerInfo* root, Gene* gene, int index, Edge* edge_table, int num_gene) { - int i; - Gene fail_gene = gene[index]; - int remaining_edges = 0; - int four_count = 0; - int rand_decision; - const int TOTAL_EDGES_COUNT = 4; + int i;// 循环计数器 + Gene fail_gene = gene[index];// 存储失败的基因 + int remaining_edges = 0;// 剩余的未使用边数 + int four_count = 0;// 拥有四条边的节点数量 + int rand_decision;// 随机决策的值 + const int TOTAL_EDGES_COUNT = 4;// 总边数的限制值 /* * how many edges remain? how many gene with four total (initial) edges * remain? */ + // 计算剩余的未使用边数和total_edges等于TOTAL_EDGES_COUNT的节点数量。 for (i = 1; i <= num_gene; i++) { if ((edge_table[i].unused_edges != -1) && (i != (int)fail_gene)) { remaining_edges++; @@ -345,7 +369,7 @@ static Gene edge_failure(PlannerInfo* root, Gene* gene, int index, Edge* edge_ta * random decision of the gene with remaining edges and whose total_edges * == 4 */ - if (four_count != 0) { + if (four_count != 0) {// 如果有total_edges等于TOTAL_EDGES_COUNT的节点,随机选择一个返回。 rand_decision = geqo_randint(root, four_count - 1, 0); for (i = 1; i <= num_gene; i++) { if ((Gene)i != fail_gene && edge_table[i].unused_edges != -1 && edge_table[i].total_edges == TOTAL_EDGES_COUNT) { @@ -355,7 +379,7 @@ static Gene edge_failure(PlannerInfo* root, Gene* gene, int index, Edge* edge_ta return (Gene)i; } } - +// 如果没有找到满足条件的节点,记录日志。 elog(LOG, "no edge found via random decision and total_edges == 4"); } else if (remaining_edges != 0) { /* random decision of the gene with remaining edges */ @@ -370,9 +394,9 @@ static Gene edge_failure(PlannerInfo* root, Gene* gene, int index, Edge* edge_ta } } } - +// 如果没有找到满足条件的节点,记录日志。 elog(LOG, "no edge found via random decision with remaining edges"); - } else { + } else { // 如果没有剩余的未使用边,返回最后一个未使用点 /* * edge table seems to be empty; this happens sometimes on the last point * due to the fact that the first point is removed from the table even @@ -389,6 +413,6 @@ static Gene edge_failure(PlannerInfo* root, Gene* gene, int index, Edge* edge_ta } /* ... should never be reached */ - ereport(ERROR, (errmodule(MOD_OPT), errcode(ERRCODE_OPTIMIZER_INCONSISTENT_STATE), errmsg("no edge found"))); + ereport(ERROR, (errmodule(MOD_OPT), errcode(ERRCODE_OPTIMIZER_INCONSISTENT_STATE), errmsg("no edge found")));// 如果没有找到满足条件的城市,报告错误。 return 0; /* to keep the compiler quiet */ } diff --git a/src/gausskernel/optimizer/geqo/geqo_eval.cpp b/src/gausskernel/optimizer/geqo/geqo_eval.cpp index 6dc6715ab..c30122ace 100644 --- a/src/gausskernel/optimizer/geqo/geqo_eval.cpp +++ b/src/gausskernel/optimizer/geqo/geqo_eval.cpp @@ -34,26 +34,33 @@ /* A "clump" of already-joined relations within gimme_tree */ typedef struct { + // 关联的关系信息 RelOptInfo* joinrel; /* joinrel for the set of relations */ + // 关联的大小 int size; /* number of input relations in clump */ } Clump; -static List* merge_clump(PlannerInfo* root, List* clumps, Clump* new_clump, bool force); -static bool desirable_join(PlannerInfo* root, RelOptInfo* outer_rel, RelOptInfo* inner_rel); +static List* merge_clump(PlannerInfo* root, List* clumps, Clump* new_clump, bool force);// 静态函数——合并关联的 Clump 结构体列表 +static bool desirable_join(PlannerInfo* root, RelOptInfo* outer_rel, RelOptInfo* inner_rel);// 静态函数——确定连接两个关系是否是合适的 /* * geqo_eval * * Returns cost of a query tree as an individual of the population. */ + + /* + 函数接收一个查询规划器(PlannerInfo)和一个遗传算法的代表性基因序列(Gene* tour), + 并尝试通过优化连接关系表的顺序来找到查询的最佳执行计划,以最小化总成本 + */ Cost geqo_eval(PlannerInfo* root, Gene* tour, int num_gene) { - MemoryContext mycontext; - MemoryContext oldcxt; - RelOptInfo* joinrel = NULL; - Cost fitness; - int savelength; - struct HTAB* savehash; + MemoryContext mycontext;// 创建一个内存上下文,用于存储 GEQO 相关数据 + MemoryContext oldcxt;// 用于保存当前内存上下文 + RelOptInfo* joinrel = NULL;// 初始化关联的关系信息 + Cost fitness;// 存储查询执行计划的适应度(成本) + int savelength;// 保存当前关联的关系表列表的长度 + struct HTAB* savehash;// 保存当前的关系表哈希表 /* * Create a private memory context that will hold all temp storage @@ -65,8 +72,9 @@ Cost geqo_eval(PlannerInfo* root, Gene* tour, int num_gene) * be freed even if we abort via ereport(ERROR). */ mycontext = AllocSetContextCreate( + // 创建一个内存上下文,用于存储 GEQO 相关数据 CurrentMemoryContext, "GEQO", ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE); - oldcxt = MemoryContextSwitchTo(mycontext); + oldcxt = MemoryContextSwitchTo(mycontext); // 切换到新的内存上下文 /* * gimme_tree will add entries to root->join_rel_list, which may or may @@ -83,14 +91,14 @@ Cost geqo_eval(PlannerInfo* root, Gene* tour, int num_gene) * * join_rel_level[] shouldn't be in use, so just Assert it isn't. */ - savelength = list_length(root->join_rel_list); - savehash = root->join_rel_hash; - AssertEreport(root->join_rel_level == NULL, MOD_OPT, ""); + savelength = list_length(root->join_rel_list);// 获取当前关联的关系表列表的长度 + savehash = root->join_rel_hash;// 保存当前的关系表哈希表 + AssertEreport(root->join_rel_level == NULL, MOD_OPT, "");// 使用断言确保当前的关联关系级别为空 - root->join_rel_hash = NULL; + root->join_rel_hash = NULL;// 清空当前关系表哈希表 /* construct the best path for the given combination of relations */ - joinrel = gimme_tree(root, tour, num_gene); + joinrel = gimme_tree(root, tour, num_gene);// 调用 gimme_tree 函数获取关联的关系树 /* * compute fitness @@ -98,20 +106,20 @@ Cost geqo_eval(PlannerInfo* root, Gene* tour, int num_gene) * XXX geqo does not currently support optimization for partial result * retrieval --- how to fix? */ - fitness = ((Path*)linitial(joinrel->cheapest_total_path))->total_cost; + fitness = ((Path*)linitial(joinrel->cheapest_total_path))->total_cost;// 获取最便宜的路径的总代价 /* * Restore join_rel_list to its former state, and put back original * hashtable if any. */ - root->join_rel_list = list_truncate(root->join_rel_list, savelength); - root->join_rel_hash = savehash; + root->join_rel_list = list_truncate(root->join_rel_list, savelength);// 恢复关系表列表的长度 + root->join_rel_hash = savehash;// 恢复关系表哈希表 /* release all the memory acquired within gimme_tree */ - (void)MemoryContextSwitchTo(oldcxt); - MemoryContextDelete(mycontext); + (void)MemoryContextSwitchTo(oldcxt);// 切换回原始的内存上下文 + MemoryContextDelete(mycontext);// 删除新创建的内存上下文 - return fitness; + return fitness;// 返回计算的适应度值 } /* @@ -138,9 +146,12 @@ Cost geqo_eval(PlannerInfo* root, Gene* tour, int num_gene) * generated plans. */ RelOptInfo* gimme_tree(PlannerInfo* root, Gene* tour, int num_gene) +/* +函数的功能是根据基因序列和初始关系表信息生成一个最优的关系表连接树,以便在查询优化中使用 +*/ { - GeqoPrivateData* priv = (GeqoPrivateData*)root->join_search_private; - List* clumps = NIL; + GeqoPrivateData* priv = (GeqoPrivateData*)root->join_search_private;// 获取 GEQO 的私有数据 + List* clumps = NIL;// 创建一个关联的关系表列表 int rel_count; /* @@ -155,7 +166,7 @@ RelOptInfo* gimme_tree(PlannerInfo* root, Gene* tour, int num_gene) * joins might still fail due to semantics, but we should always be able * to find some join order that works. */ - clumps = NIL; + clumps = NIL;// 初始化关系表列表 for (rel_count = 0; rel_count < num_gene; rel_count++) { int cur_rel_index; @@ -163,16 +174,16 @@ RelOptInfo* gimme_tree(PlannerInfo* root, Gene* tour, int num_gene) Clump* cur_clump = NULL; /* Get the next input relation */ - cur_rel_index = (int)tour[rel_count]; - cur_rel = (RelOptInfo*)list_nth(priv->initial_rels, cur_rel_index - 1); + cur_rel_index = (int)tour[rel_count];// 获取当前关联的关系索引 + cur_rel = (RelOptInfo*)list_nth(priv->initial_rels, cur_rel_index - 1);// 根据索引获取当前关系的信息 /* Make it into a single-rel clump */ - cur_clump = (Clump*)palloc(sizeof(Clump)); - cur_clump->joinrel = cur_rel; - cur_clump->size = 1; + cur_clump = (Clump*)palloc(sizeof(Clump));// 分配内存以存储当前的关联关系信息 + cur_clump->joinrel = cur_rel;// 设置当前关联的关系信息 + cur_clump->size = 1;// 设置关系大小为1 /* Merge it into the clumps list, using only desirable joins */ - clumps = merge_clump(root, clumps, cur_clump, false); + clumps = merge_clump(root, clumps, cur_clump, false);// 调用 merge_clump 函数将当前关联关系合并到列表中 } if (list_length(clumps) > 1) { @@ -184,7 +195,7 @@ RelOptInfo* gimme_tree(PlannerInfo* root, Gene* tour, int num_gene) Clump* clump = (Clump*)lfirst(lc); fclumps = merge_clump(root, fclumps, clump, true); - } + }// 强制合并关联关系列表 clumps = fclumps; } @@ -193,9 +204,9 @@ RelOptInfo* gimme_tree(PlannerInfo* root, Gene* tour, int num_gene) ereport(ERROR, (errmodule(MOD_OPT), errcode(ERRCODE_OPTIMIZER_INCONSISTENT_STATE), - errmsg("failed to join all relations together"))); + errmsg("failed to join all relations together"))); // 如果无法将所有关联关系连接在一起,则报告错误 - return ((Clump*)linitial(clumps))->joinrel; + return ((Clump*)linitial(clumps))->joinrel;// 返回最终的关联的关系表 } /* @@ -210,17 +221,25 @@ RelOptInfo* gimme_tree(PlannerInfo* root, Gene* tour, int num_gene) * a cartesian join to be performed. When force is false, do only * "desirable" joins. */ + + /* +这段代码的功能是实现一个查询计划中的Clump合并操作。Clump是一种数据结构,用于表示查询计划中的关联关系, +merge_clump函数接受一个PlannerInfo结构体(查询规划信息)、一个Clump列表clumps、一个新的Clump new_ +clump以及一个布尔标志force。它会尝试合并新的Clump与已有的Clump,以优化查询计划。函数首先迭代遍历clumps +列表,尝试找到可合并的旧Clump,如果找到可合并的关联关系,就将它们合并,并递归调用自身以处理可能的进 +一步合并。如果无法合并,它会根据一定的规则将新Clump插入到clumps列表中,并返回最终的合并结果。 + */ static List* merge_clump(PlannerInfo* root, List* clumps, Clump* new_clump, bool force) { - ListCell* prev = NULL; - ListCell* lc = NULL; + ListCell* prev = NULL;// 用于追踪前一个列表元素的指针 + ListCell* lc = NULL;// 用于迭代遍历clumps列表的指针 /* Look for a clump that new_clump can join to */ - foreach (lc, clumps) { - Clump* old_clump = (Clump*)lfirst(lc); + foreach (lc, clumps) {// 对clumps列表中的每个Clump元素进行迭代处理 + Clump* old_clump = (Clump*)lfirst(lc);// 获取当前迭代元素的指针 - if (force || desirable_join(root, old_clump->joinrel, new_clump->joinrel)) { - RelOptInfo* joinrel = NULL; + if (force || desirable_join(root, old_clump->joinrel, new_clump->joinrel)) {// 如果force标志为true,或者新旧Clump之间存在可合并的关联关系 + RelOptInfo* joinrel = NULL;// 用于存储合并后的关系信息 /* * Construct a RelOptInfo representing the join of these two input @@ -230,17 +249,17 @@ static List* merge_clump(PlannerInfo* root, List* clumps, Clump* new_clump, bool */ joinrel = make_join_rel(root, old_clump->joinrel, new_clump->joinrel); /* Keep searching if join order is not valid */ - if (joinrel != NULL) { + if (joinrel != NULL) {// 如果成功创建了新的关联关系 /* Find and save the cheapest paths for this joinrel */ - set_cheapest(joinrel); + set_cheapest(joinrel);// 标记joinrel为最便宜的查询计划 /* Absorb new clump into old */ - old_clump->joinrel = joinrel; - old_clump->size += new_clump->size; - pfree_ext(new_clump); + old_clump->joinrel = joinrel; // 更新旧Clump的关联关系信息 + old_clump->size += new_clump->size;// 更新旧Clump的大小 + pfree_ext(new_clump);// 释放新Clump的内存 /* Remove old_clump from list */ - clumps = list_delete_cell(clumps, lc, prev); + clumps = list_delete_cell(clumps, lc, prev);// 从clumps列表中删除旧Clump /* * Recursively try to merge the enlarged old_clump with @@ -250,7 +269,7 @@ static List* merge_clump(PlannerInfo* root, List* clumps, Clump* new_clump, bool return merge_clump(root, clumps, old_clump, force); } } - prev = lc; + prev = lc;// 更新prev指针以继续迭代 } /* @@ -258,13 +277,14 @@ static List* merge_clump(PlannerInfo* root, List* clumps, Clump* new_clump, bool * proper order according to size. We can be fast for the common case * where it has size 1 --- it should always go at the end. */ - if (clumps == NIL || new_clump->size == 1) + if (clumps == NIL || new_clump->size == 1) // 如果clumps为空或新Clump的大小为1,将新Clump添加到clumps列表末尾 + return lappend(clumps, new_clump); /* Check if it belongs at the front */ - lc = list_head(clumps); + lc = list_head(clumps);// 获取clumps列表的头元素指针 if (new_clump->size > ((Clump*)lfirst(lc))->size) - return lcons(new_clump, clumps); + return lcons(new_clump, clumps);// 如果新Clump的大小大于第一个Clump的大小,将新Clump插入列表头部 /* Else search for the place to insert it */ for (;;) { @@ -272,9 +292,10 @@ static List* merge_clump(PlannerInfo* root, List* clumps, Clump* new_clump, bool if (nxt == NULL || new_clump->size > ((Clump*)lfirst(nxt))->size) break; /* it belongs after 'lc', before 'nxt' */ + // 如果新Clump的大小大于下一个Clump的大小,退出循环,确定插入位置 lc = nxt; } - (void)lappend_cell(clumps, lc, new_clump); + (void)lappend_cell(clumps, lc, new_clump);// 在lc之后插入新Clump return clumps; } @@ -288,6 +309,7 @@ static bool desirable_join(PlannerInfo* root, RelOptInfo* outer_rel, RelOptInfo* * Join if there is an applicable join clause, or if there is a join order * restriction forcing these rels to be joined. */ + // 判断是否有相关的连接条件或连接顺序限制 if (have_relevant_joinclause(root, outer_rel, inner_rel) || have_join_order_restriction(root, outer_rel, inner_rel)) return true; diff --git a/src/gausskernel/optimizer/geqo/geqo_main.cpp b/src/gausskernel/optimizer/geqo/geqo_main.cpp index 774526d90..13733bb6a 100644 --- a/src/gausskernel/optimizer/geqo/geqo_main.cpp +++ b/src/gausskernel/optimizer/geqo/geqo_main.cpp @@ -32,21 +32,28 @@ #include "optimizer/geqo_random.h" #include "optimizer/geqo_selection.h" -static int gimme_pool_size(int nr_rel); -static int gimme_number_generations(int pool_size); +static int gimme_pool_size(int nr_rel);//根据关系数(nr_rel)计算池大小 +static int gimme_number_generations(int pool_size);//根据池大小计算生成的代数数量 /* define edge recombination crossover [ERX] per default */ #if !defined(ERX) && !defined(PMX) && !defined(CX) && !defined(PX) && !defined(OX1) && !defined(OX2) #define ERX #endif +/* +该部分代码在实现了遗传查询优化算法,其中查询计划的一组候选解(染色体)在多代之间演化,以找到给定关系集的最佳查询计划 +特定的交叉和变异方法可以在编译时基于宏选择,算法旨在通过评估和演化候选查询计划来找到最佳的查询计划 +*/ + + /* * geqo * solution of the query optimization problem * similar to a constrained Traveling Salesman Problem (TSP) */ -RelOptInfo* geqo(PlannerInfo* root, int number_of_rels, List* initial_rels) +RelOptInfo* geqo(PlannerInfo* root, int number_of_rels, List* initial_rels)// GEQO(遗传查询优化)算法主函数 { + // 定义GEQO的私有数据结构和变量 GeqoPrivateData priv; int generation; Chromosome* momma = NULL; @@ -55,7 +62,7 @@ RelOptInfo* geqo(PlannerInfo* root, int number_of_rels, List* initial_rels) Pool* pool = NULL; int pool_size, number_generations; -#ifdef GEQO_DEBUG +#ifdef GEQO_DEBUG// 根据宏定义,为特定的交叉方法分配额外的数据结构 int status_interval; #endif Gene* best_tour = NULL; @@ -74,19 +81,19 @@ RelOptInfo* geqo(PlannerInfo* root, int number_of_rels, List* initial_rels) #endif /* set up private information */ - root->join_search_private = (void*)&priv; + root->join_search_private = (void*)&priv;// 设置PlannerInfo中的私有数据 priv.initial_rels = initial_rels; /* initialize private number generator */ - geqo_set_seed(root, u_sess->attr.attr_sql.Geqo_seed); + geqo_set_seed(root, u_sess->attr.attr_sql.Geqo_seed);// 设置GEQO的随机种子 /* set GA parameters */ - pool_size = gimme_pool_size(number_of_rels); + pool_size = gimme_pool_size(number_of_rels);// 根据输入计算池大小和生成代数数量 number_generations = gimme_number_generations(pool_size); #ifdef GEQO_DEBUG status_interval = 10; #endif - +// 为GEQO池分配内存并初始化 /* allocate genetic pool memory */ pool = alloc_pool(root, pool_size, number_of_rels); @@ -97,7 +104,7 @@ RelOptInfo* geqo(PlannerInfo* root, int number_of_rels, List* initial_rels) sort_pool(root, pool); /* we have to do it only one time, since all * kids replace the worst individuals in * future (-> geqo_pool.c:spread_chromo ) */ -#ifdef GEQO_DEBUG +#ifdef GEQO_DEBUG// 在调试模式下记录池统计信息 elog(DEBUG1, "GEQO selected %d pool entries, best %.2f, worst %.2f", pool_size, @@ -106,9 +113,10 @@ RelOptInfo* geqo(PlannerInfo* root, int number_of_rels, List* initial_rels) #endif /* allocate chromosome momma and daddy memory */ - momma = alloc_chromo(root, pool->string_length); + momma = alloc_chromo(root, pool->string_length);// 为父代染色体(momma和daddy)分配内存 daddy = alloc_chromo(root, pool->string_length); +// 根据所选的交叉方法,为额外的数据结构分配内存 #if defined(ERX) #ifdef GEQO_DEBUG elog(DEBUG2, "using edge recombination crossover [ERX]"); @@ -153,9 +161,9 @@ RelOptInfo* geqo(PlannerInfo* root, int number_of_rels, List* initial_rels) /* my pain main part: */ /* iterative optimization */ - for (generation = 0; generation < number_generations; generation++) { + for (generation = 0; generation < number_generations; generation++) {// GEQO算法的主循环,用于代数迭代 /* SELECTION: using linear bias function */ - geqo_selection(root, momma, daddy, pool, u_sess->attr.attr_sql.Geqo_selection_bias); + geqo_selection(root, momma, daddy, pool, u_sess->attr.attr_sql.Geqo_selection_bias);// 执行父代选择和基于所选方法的交叉操作 #if defined(ERX) /* EDGE RECOMBINATION CROSSOVER */ @@ -188,10 +196,10 @@ RelOptInfo* geqo(PlannerInfo* root, int number_of_rels, List* initial_rels) #endif /* EVALUATE FITNESS */ - kid->worth = geqo_eval(root, kid->string, pool->string_length); + kid->worth = geqo_eval(root, kid->string, pool->string_length);// 评估子代染色体的价值 /* push the kid into the wilderness of life according to its worth */ - spread_chromo(root, kid, pool); + spread_chromo(root, kid, pool);// 将子代染色体传播到池中以用于下一代 #ifdef GEQO_DEBUG if (status_interval && !(generation % status_interval)) @@ -199,7 +207,7 @@ RelOptInfo* geqo(PlannerInfo* root, int number_of_rels, List* initial_rels) #endif } -#if defined(ERX) && defined(GEQO_DEBUG) +#if defined(ERX) && defined(GEQO_DEBUG)// 在调试模式下打印池的状态 if (edge_failures != 0) elog(LOG, "[GEQO] failures: %d, average: %d", edge_failures, number_generations / edge_failures); else @@ -270,14 +278,14 @@ RelOptInfo* geqo(PlannerInfo* root, int number_of_rels, List* initial_rels) * The default is based on query size (no. of relations) = 2^(QS+1), * but constrained to a range based on the effort value. */ -static int gimme_pool_size(int nr_rel) +static int gimme_pool_size(int nr_rel)// 用于根据关系数计算池大小的函数 { double size; int minsize; int maxsize; /* Legal pool size *must* be at least 2, so ignore attempt to select 1 */ - if (u_sess->attr.attr_sql.Geqo_pool_size >= 2) + if (u_sess->attr.attr_sql.Geqo_pool_size >= 2)// 检查配置中是否设置了特定的池大小 return u_sess->attr.attr_sql.Geqo_pool_size; size = pow(2.0, nr_rel + 1.0); @@ -302,10 +310,11 @@ static int gimme_pool_size(int nr_rel) * sure that less-fit individuals get pushed out of the breeding * population before the run finishes. */ -static int gimme_number_generations(int pool_size) +static int gimme_number_generations(int pool_size)// 根据池大小计算生成代数数量的函数 { + // 检查配置中是否设置了特定的代数数量 if (u_sess->attr.attr_sql.Geqo_generations > 0) return u_sess->attr.attr_sql.Geqo_generations; - return pool_size; + return pool_size;// 使用池大小作为默认的代数数量 } diff --git a/src/gausskernel/optimizer/geqo/geqo_misc.cpp b/src/gausskernel/optimizer/geqo/geqo_misc.cpp index b68237020..5875f225e 100644 --- a/src/gausskernel/optimizer/geqo/geqo_misc.cpp +++ b/src/gausskernel/optimizer/geqo/geqo_misc.cpp @@ -29,7 +29,12 @@ /* * avg_pool */ -static double avg_pool(Pool* pool) + +/* +这段代码是关于查询优化器中的一些辅助函数,用于汇总和打印池(pool)和边缘表(edge table)的信息 +*/ + +static double avg_pool(Pool* pool)//这个函数计算池中数据元素的平均值,先检查池的大小是否为零,然后遍历每个元素,将其值累加到cumulative中,最后返回平均值 { int i; double cumulative = 0.0; @@ -44,37 +49,37 @@ static double avg_pool(Pool* pool) * little in speed and accuracy, but this routine is only used for debug * printouts, so we don't care that much. */ - for (i = 0; i < pool->size; i++) + for (i = 0; i < pool->size; i++)// 遍历池中的数据,累加每个元素的值 cumulative += pool->data[i].worth / pool->size; - return cumulative; + return cumulative;// 返回平均值 } /* print_pool */ -void print_pool(FILE* fp, Pool* pool, int start, int stop) +void print_pool(FILE* fp, Pool* pool, int start, int stop)//这个函数用于打印池中的数据元素到指定文件,可以指定起始和结束位置,如果它们超出了池的边界,会重新设置它们 { int i, j; /* be extra careful that start and stop are valid inputs */ - if (start < 0) + if (start < 0)// 如果start小于0,将其设置为0 start = 0; - if (stop > pool->size) + if (stop > pool->size)// 如果stop大于池的大小,将其设置为池的大小 stop = pool->size; - if (start + stop > pool->size) { + if (start + stop > pool->size) {// 如果start和stop的和大于池的大小,重新设置它们 start = 0; stop = pool->size; } - for (i = start; i < stop; i++) { + for (i = start; i < stop; i++) {// 打印池中的数据元素到指定文件 fprintf(fp, "%d)\t", i); for (j = 0; j < pool->string_length; j++) fprintf(fp, "%d ", pool->data[i].string[j]); fprintf(fp, "%g\n", pool->data[i].worth); } - fflush(fp); + fflush(fp);// 刷新文件缓冲区 } /* print_gen @@ -89,7 +94,7 @@ void print_gen(FILE* fp, Pool* pool, int generation) /* Use 2nd to last since last is buffer. */ lowest = pool->size > 1 ? pool->size - 2 : 0; - fprintf(fp, + fprintf(fp,// 打印池的���些统计信息,包括最佳、最差、平均和平均池值 "%5d | Best: %g Worst: %g Mean: %g Avg: %g\n", generation, pool->data[0].worth, @@ -104,7 +109,7 @@ void print_edge_table(FILE* fp, Edge* edge_table, int num_gene) { int i, j; - fprintf(fp, "\nEDGE TABLE\n"); + fprintf(fp, "\nEDGE TABLE\n");// 打印边缘表的内容 for (i = 1; i <= num_gene; i++) { fprintf(fp, "%d :", i); diff --git a/src/gausskernel/optimizer/geqo/geqo_mutation.cpp b/src/gausskernel/optimizer/geqo/geqo_mutation.cpp index 3af75b3b1..5a7e2fcc8 100644 --- a/src/gausskernel/optimizer/geqo/geqo_mutation.cpp +++ b/src/gausskernel/optimizer/geqo/geqo_mutation.cpp @@ -37,22 +37,29 @@ void geqo_mutation(PlannerInfo* root, Gene* tour, int num_gene) { + // 定义两个整数变量来表示要交换的基因的索引 int swap1; int swap2; + // 计算要执行的交换次数,这里使用随机数,num_gene/3表示最大交换次数 int num_swaps = geqo_randint(root, num_gene / 3, 0); - Gene temp; + Gene temp;// 临时变量,用于存储基因交换时的中间结果 - while (num_swaps > 0) { - swap1 = geqo_randint(root, num_gene - 1, 0); + while (num_swaps > 0) {// 当还有交换次数时循环执行交换 + swap1 = geqo_randint(root, num_gene - 1, 0);// 随机选择两个不同的基因索引 swap2 = geqo_randint(root, num_gene - 1, 0); - while (swap1 == swap2) + while (swap1 == swap2)// 确保选到的两个索引不相同 swap2 = geqo_randint(root, num_gene - 1, 0); - temp = tour[swap1]; + temp = tour[swap1];// 执行基因交换,将选中的两个基因进行互换 tour[swap1] = tour[swap2]; tour[swap2] = temp; - num_swaps -= 1; + num_swaps -= 1;// 减少剩余的交换次数 } } +/* +这段代码的功能是在给定基因序列(tour)中执行基因交换操作。 +它首先确定要执行的交换次数,然后随机选择两个不同的基因位置进行交换。 +通过这种方式,它可以引入随机性,有助于在搜索空间中找到更多可能的解决方案,以提高查询优化的效果 +*/ \ No newline at end of file diff --git a/src/gausskernel/optimizer/geqo/geqo_ox1.cpp b/src/gausskernel/optimizer/geqo/geqo_ox1.cpp index d2581981c..ec3e98e25 100644 --- a/src/gausskernel/optimizer/geqo/geqo_ox1.cpp +++ b/src/gausskernel/optimizer/geqo/geqo_ox1.cpp @@ -46,34 +46,44 @@ void ox1(PlannerInfo* root, Gene* tour1, Gene* tour2, Gene* offspring, int num_g int left, right, k, p, temp; /* initialize city table */ - for (k = 1; k <= num_gene; k++) + for (k = 1; k <= num_gene; k++)// 初始化节点表中的标志位,用于记录城市是否已经在后代中出现 city_table[k].used = 0; /* select portion to copy from tour1 */ + // 随机选择要从tour1中复制到后代中的基因片段 left = geqo_randint(root, num_gene - 1, 0); right = geqo_randint(root, num_gene - 1, 0); - if (left > right) { + if (left > right) {// 确保left小于right temp = left; left = right; right = temp; } /* copy portion from tour1 to offspring */ - for (k = left; k <= right; k++) { + for (k = left; k <= right; k++) {// 复制tour1中选定的基因片段到后代 offspring[k] = tour1[k]; city_table[(int)tour1[k]].used = 1; } +// 初始化k和p,用于迭代地从tour2中选择未出现在后代中的基因 k = (right + 1) % num_gene; /* index into offspring */ p = k; /* index into tour2 */ /* copy stuff from tour2 to offspring */ - while (k != left) { + while (k != left) {// 开始迭代 + // 如果tour2中的基因没有在后代中出现过,将其添加到后代中 if (!city_table[(int)tour2[p]].used) { offspring[k] = tour2[p]; k = (k + 1) % num_gene; city_table[(int)tour2[p]].used = 1; } - p = (p + 1) % num_gene; /* increment tour2-index */ + p = (p + 1) % num_gene; /* increment tour2-index */// 增加tour2的索引 } } +/* +这段代码实现了一种遗传算法的交叉操作,称为OX1 (Order Crossover 1)。 +它从两个父代基因序列 (tour1 和 tour2) 中选取一个随机的基因片段, +然后将该片段复制到后代基因序列 (offspring) 中。 +接着,它从第二个父代基因序列 (tour2) 中选择未在后代中出现的基因,并按照原始顺序添加到后代中,以保持基因的顺序。 +这有助于保留父代的一些特性,并引入一些随机性,以生成新的个体 +*/ \ No newline at end of file diff --git a/src/gausskernel/optimizer/geqo/geqo_ox2.cpp b/src/gausskernel/optimizer/geqo/geqo_ox2.cpp index d3d8e29a1..77725bf84 100644 --- a/src/gausskernel/optimizer/geqo/geqo_ox2.cpp +++ b/src/gausskernel/optimizer/geqo/geqo_ox2.cpp @@ -45,26 +45,27 @@ void ox2(PlannerInfo* root, Gene* tour1, Gene* tour2, Gene* offspring, int num_g int k, j, count, pos, select, num_positions; /* initialize city table */ - for (k = 1; k <= num_gene; k++) { - city_table[k].used = 0; - city_table[k - 1].select_list = -1; + for (k = 1; k <= num_gene; k++) {// 随机生成要选择的位置数目 + city_table[k].used = 0;// 标记节点尚未在后代中出现过 + city_table[k - 1].select_list = -1;// 初始化选择列表为-1 } /* determine the number of positions to be inherited from tour1 */ num_positions = geqo_randint(root, 2 * num_gene / 3, num_gene / 3); + // 随机生成要选择的位置数目 /* make a list of selected cities */ - for (k = 0; k < num_positions; k++) { - pos = geqo_randint(root, num_gene - 1, 0); - city_table[pos].select_list = (int)tour1[pos]; - city_table[(int)tour1[pos]].used = 1; /* mark used */ + for (k = 0; k < num_positions; k++) {// 随机选择位置并将对应的节点标记为已使用 + pos = geqo_randint(root, num_gene - 1, 0);// 随机选择一个位置 + city_table[pos].select_list = (int)tour1[pos];// 记录tour1中选中位置的基因值 + city_table[(int)tour1[pos]].used = 1; /* mark used */ // 标记节点已在后代中使用 } count = 0; k = 0; /* consolidate the select list to adjacent positions */ - while (count < num_positions) { + while (count < num_positions) {// 创建选择列表,用于在后代中选择未出现的节点 if (city_table[k].select_list == -1) { j = k + 1; while ((city_table[j].select_list == -1) && (j < num_gene)) @@ -79,7 +80,7 @@ void ox2(PlannerInfo* root, Gene* tour1, Gene* tour2, Gene* offspring, int num_g } select = 0; - +// 根据选择列表构建后代基因序列 for (k = 0; k < num_gene; k++) { if (city_table[(int)tour2[k]].used) { offspring[k] = (Gene)city_table[select].select_list; @@ -89,3 +90,10 @@ void ox2(PlannerInfo* root, Gene* tour1, Gene* tour2, Gene* offspring, int num_g offspring[k] = tour2[k]; } } +/* +这段代码实现了另一种遗传算法的交叉操作,称为OX2 (Order Crossover 2)。 +它的目标是生成后代基因序列 (offspring),将两个父代基因序列 (tour1 和 tour2) 结合起来。 +首先,它随机选择一些位置,并将相应的节点标记为已使用。 +接着,它构建一个选择列表,用于在后代中选择未出现的节点。 +最后,它根据选择列表构建后代基因序列,保持了一部分父代的顺序特性,同时引入了一些随机性。这有助于维持多样性并生成更多的解决方案 +*/ \ No newline at end of file diff --git a/src/gausskernel/optimizer/geqo/geqo_pmx.cpp b/src/gausskernel/optimizer/geqo/geqo_pmx.cpp index 241ec9d7b..0c55ebffc 100644 --- a/src/gausskernel/optimizer/geqo/geqo_pmx.cpp +++ b/src/gausskernel/optimizer/geqo/geqo_pmx.cpp @@ -43,6 +43,7 @@ */ void pmx(PlannerInfo* root, Gene* tour1, Gene* tour2, Gene* offspring, int num_gene) { + // 为辅助数组分配内存 int* failed = (int*)palloc((num_gene + 1) * sizeof(int)); int* from = (int*)palloc((num_gene + 1) * sizeof(int)); int* indx = (int*)palloc((num_gene + 1) * sizeof(int)); @@ -53,33 +54,33 @@ void pmx(PlannerInfo* root, Gene* tour1, Gene* tour2, Gene* offspring, int num_g /* no mutation so start up the pmx replacement algorithm */ /* initialize failed[], from[], check_list[] */ - for (k = 0; k < num_gene; k++) { - failed[k] = -1; - from[k] = -1; - check_list[k + 1] = 0; + for (k = 0; k < num_gene; k++) {// 初始化辅助数组和检查列表 + failed[k] = -1;// 用于记录失败的映射关系 + from[k] = -1; // 用于记录映射的来源 (DAD or MOM) + check_list[k + 1] = 0; // 检查列表用于记录节点的出现次数 } - +// 随机选择要进行映射的基因片段 /* locate crossover points */ left = geqo_randint(root, num_gene - 1, 0); right = geqo_randint(root, num_gene - 1, 0); - if (left > right) { + if (left > right) {// 确保left小于right temp = left; left = right; right = temp; } /* copy tour2 into offspring */ - for (k = 0; k < num_gene; k++) { + for (k = 0; k < num_gene; k++) { // 复制tour2中的基因到后代,并初始化相关数据结构 offspring[k] = tour2[k]; - from[k] = DAD; - check_list[tour2[k]]++; + from[k] = DAD;// 从父代DAD中继承的基因 + check_list[tour2[k]]++;// 记录节点的出现次数 } /* copy tour1 into offspring */ - for (k = left; k <= right; k++) { + for (k = left; k <= right; k++) {// 执行部分映射操作,更新后代和相关数据结构 check_list[offspring[k]]--; offspring[k] = tour1[k]; - from[k] = MOM; + from[k] = MOM; // 从父代MOM中继承的基因 check_list[tour1[k]]++; } @@ -87,6 +88,7 @@ void pmx(PlannerInfo* root, Gene* tour1, Gene* tour2, Gene* offspring, int num_g mx_fail = 0; /* STEP 1 */ + // 处理没有成功映射的情况 for (k = left; k <= right; k++) { /* for all elements in the tour1-2 */ if (tour1[k] == tour2[k]) found = 1; /* find match in tour2 */ @@ -116,7 +118,7 @@ void pmx(PlannerInfo* root, Gene* tour1, Gene* tour2, Gene* offspring, int num_g /* STEP 2 */ /* see if any genes could not be replaced */ - if (mx_fail > 0) { + if (mx_fail > 0) {// 处理多次映射的情况 mx_hold = mx_fail; for (k = 0; k < mx_hold; k++) { @@ -140,6 +142,7 @@ void pmx(PlannerInfo* root, Gene* tour1, Gene* tour2, Gene* offspring, int num_g } /* ... if */ /* STEP 3 */ for (k = 1; k <= num_gene; k++) { + // 处理重复节点的情况,确保每个基因只出现一次 if (check_list[k] > 1) { i = 0; @@ -164,9 +167,15 @@ void pmx(PlannerInfo* root, Gene* tour1, Gene* tour2, Gene* offspring, int num_g } /* end while */ } } /* ... for */ - + // 释放内存 pfree_ext(failed); pfree_ext(from); pfree_ext(indx); pfree_ext(check_list); } +/* +这段代码实现了PMX (Partially Mapped Crossover) 运算,用于生成后代基因序列。 +它首先随机选择一个基因片段来映射,然后根据映射关系进行替换。 +在映射操作中,它处理了映射失败和多次映射的情况,并最终确保后代中每个基因只出现一次。 +这有助于保持基因的顺序性和多样性。 +*/ \ No newline at end of file diff --git a/src/gausskernel/optimizer/geqo/geqo_pool.cpp b/src/gausskernel/optimizer/geqo/geqo_pool.cpp index 30101df10..f08a61999 100644 --- a/src/gausskernel/optimizer/geqo/geqo_pool.cpp +++ b/src/gausskernel/optimizer/geqo/geqo_pool.cpp @@ -30,24 +30,24 @@ #include "optimizer/geqo_copy.h" #include "optimizer/geqo_pool.h" #include "optimizer/geqo_recombination.h" - +// 定义比较函数,用于排序 static int compare(const void* arg1, const void* arg2); /* * alloc_pool * allocates memory for GA pool */ -Pool* alloc_pool(PlannerInfo* root, int pool_size, int string_length) +Pool* alloc_pool(PlannerInfo* root, int pool_size, int string_length)// 分配内存并初始化一个新的池 { Pool* new_pool = NULL; Chromosome* chromo = NULL; int i; - + // 分配内存以存储新池 /* pool */ new_pool = (Pool*)palloc(sizeof(Pool)); new_pool->size = (int)pool_size; new_pool->string_length = (int)string_length; - +// 分配内存以存储染色体数据 /* all chromosome */ new_pool->data = (Chromosome*)palloc(pool_size * sizeof(Chromosome)); @@ -63,7 +63,7 @@ Pool* alloc_pool(PlannerInfo* root, int pool_size, int string_length) * free_pool * deallocates memory for GA pool */ -void free_pool(PlannerInfo* root, Pool* pool) +void free_pool(PlannerInfo* root, Pool* pool)// 释放池及其相关的内存 { Chromosome* chromo = NULL; int i; @@ -74,18 +74,19 @@ void free_pool(PlannerInfo* root, Pool* pool) pfree_ext(chromo[i].string); /* all chromosome */ - pfree_ext(pool->data); + pfree_ext(pool->data);// 释放染色体数据 /* pool */ pfree_ext(pool); + // 释放池本身 } -void random_init_pool(PlannerInfo* root, Pool* pool) +void random_init_pool(PlannerInfo* root, Pool* pool)// 随机初始化池中的染色体 { Chromosome* chromo = (Chromosome*)pool->data; int i; - for (i = 0; i < pool->size; i++) { + for (i = 0; i < pool->size; i++) {// 初始化池中的染色体 init_tour(root, chromo[i].string, pool->string_length); pool->data[i].worth = geqo_eval(root, chromo[i].string, pool->string_length); } @@ -97,7 +98,7 @@ void random_init_pool(PlannerInfo* root, Pool* pool) * * maybe you have to change compare() for different ordering ... */ -void sort_pool(PlannerInfo* root, Pool* pool) +void sort_pool(PlannerInfo* root, Pool* pool)// 对池中的染色体按照适应度进行排序 { qsort(pool->data, pool->size, sizeof(Chromosome), compare); } @@ -110,7 +111,7 @@ static int compare(const void* arg1, const void* arg2) { const Chromosome* chromo1 = (const Chromosome*)arg1; const Chromosome* chromo2 = (const Chromosome*)arg2; - +// 根据染色体的适应度比较它们的顺序 if (chromo1->worth - chromo2->worth == 0) return 0; else if (chromo1->worth > chromo2->worth) @@ -122,10 +123,10 @@ static int compare(const void* arg1, const void* arg2) /* alloc_chromo * allocates a chromosome and string space */ -Chromosome* alloc_chromo(PlannerInfo* root, int string_length) +Chromosome* alloc_chromo(PlannerInfo* root, int string_length)// 分配内存并初始化一个新的染色体 { Chromosome* chromo = NULL; - +// 分配内存以存储染色体 chromo = (Chromosome*)palloc(sizeof(Chromosome)); chromo->string = (Gene*)palloc((string_length + 1) * sizeof(Gene)); @@ -136,23 +137,24 @@ Chromosome* alloc_chromo(PlannerInfo* root, int string_length) * deallocates a chromosome and string space */ void free_chromo(PlannerInfo* root, Chromosome* chromo) +// 释放染色体及其相关的内存 { - pfree_ext(chromo->string); - pfree_ext(chromo); + pfree_ext(chromo->string);// 释放染色体数据 + pfree_ext(chromo);// 释放染色体本身 } /* spread_chromo * inserts a new chromosome into the pool, displacing worst gene in pool * assumes best->worst = smallest->largest */ -void spread_chromo(PlannerInfo* root, Chromosome* chromo, Pool* pool) +void spread_chromo(PlannerInfo* root, Chromosome* chromo, Pool* pool)// 将染色体插入到池中并维护池的排序顺序 { int top, mid, bot; int i, index; Chromosome swap_chromo, tmp_chromo; /* new chromo is so bad we can't use it */ - if (chromo->worth > pool->data[pool->size - 1].worth) + if (chromo->worth > pool->data[pool->size - 1].worth)// 如果染色体的适应度高于池中的最差染色体,则不插入 return; /* do a binary search to find the index of the new chromo */ @@ -160,7 +162,7 @@ void spread_chromo(PlannerInfo* root, Chromosome* chromo, Pool* pool) mid = pool->size / 2; bot = pool->size - 1; index = -1; - + // 在池中查找插入位置 while (index == -1) { /* these 4 cases find a new location */ if (chromo->worth <= pool->data[top].worth) { @@ -191,7 +193,7 @@ void spread_chromo(PlannerInfo* root, Chromosome* chromo, Pool* pool) /* * copy new gene into pool storage; always replace worst gene in pool */ - geqo_copy(root, &pool->data[pool->size - 1], chromo, pool->string_length); + geqo_copy(root, &pool->data[pool->size - 1], chromo, pool->string_length);// 复制染色体并维护池的排序顺序 swap_chromo.string = pool->data[pool->size - 1].string; swap_chromo.worth = pool->data[pool->size - 1].worth; @@ -207,3 +209,9 @@ void spread_chromo(PlannerInfo* root, Chromosome* chromo, Pool* pool) swap_chromo.worth = tmp_chromo.worth; } } +/* +这些代码涉及到遗传算法中的池管理和排序操作。 +它们用于分配和释放内存、随机初始化染色体、对池中的染色体按适应度进行排序以及插入新染色体并维护排序顺序。 +compare 函数用于比较两个染色体的适应度,以便在排序时使用。 +这些操作是遗传算法中的关键步骤,用于生成和维护染色体集合。 +*/ \ No newline at end of file diff --git a/src/gausskernel/optimizer/geqo/geqo_px.cpp b/src/gausskernel/optimizer/geqo/geqo_px.cpp index 62895e742..b51f58f5e 100644 --- a/src/gausskernel/optimizer/geqo/geqo_px.cpp +++ b/src/gausskernel/optimizer/geqo/geqo_px.cpp @@ -41,20 +41,30 @@ * * position crossover */ + // 实现部分顺序交叉算子 +/* + //PlannerInfo* root 查询优化器的信息结构 + //Gene* tour1 第一个染色体 + //Gene* tour2 第二个染色体 + //Gene* offspring 生成的子代染色体 + //int num_gene 基因的数量 + //City* city_table 城市信息表,用于标记城市是否已被使用 +*/ + void px(PlannerInfo* root, Gene* tour1, Gene* tour2, Gene* offspring, int num_gene, City* city_table) { - int num_positions; - int i, pos, tour2_index, offspring_index; + int num_positions; // 用于存储要交叉的位置数量 + int i, pos, tour2_index, offspring_index;// 循环变量和临时位置变量和用于追踪tour2和offspring中的位置 /* initialize city table */ - for (i = 1; i <= num_gene; i++) + for (i = 1; i <= num_gene; i++)// 初始化城市使用标志,标记城市是否已被使用 city_table[i].used = 0; /* choose random positions that will be inherited directly from parent */ - num_positions = geqo_randint(root, 2 * num_gene / 3, num_gene / 3); + num_positions = geqo_randint(root, 2 * num_gene / 3, num_gene / 3);// 随机确定要交叉的位置数量 /* choose random position */ - for (i = 0; i < num_positions; i++) { + for (i = 0; i < num_positions; i++) {// 随机选择并复制部分基因片段从tour1到offspring,并标记城市为已使用 pos = geqo_randint(root, num_gene - 1, 0); offspring[pos] = tour1[pos]; /* transfer cities to child */ @@ -65,7 +75,7 @@ void px(PlannerInfo* root, Gene* tour1, Gene* tour2, Gene* offspring, int num_ge offspring_index = 0; /* px main part */ - while (offspring_index < num_gene) { + while (offspring_index < num_gene) {// 从tour2中选择未被标记为已使用的城市,将其添加到offspring中 /* next position in offspring filled */ if (!city_table[(int)tour1[offspring_index]].used) { /* next city in tour1 not used */ @@ -82,3 +92,8 @@ void px(PlannerInfo* root, Gene* tour1, Gene* tour2, Gene* offspring, int num_ge } } } +/* +这段代码实现了部分顺序交叉(PX)算子,用于在遗传算法中交叉两个染色体(tour1和tour2)以生成一个新的染色体(offspring)。 +在PX算子中,首先随机选择一定数量的基因位置来交叉,然后将这些位置上的基因从tour1复制到offspring,并将相应的城市标记为已使用。 +接着,从tour2中选择未被标记为已使用的城市,并将它们添加到offspring中,以确保生成的染色体包含tour1和tour2的信息。 +*/ \ No newline at end of file diff --git a/src/gausskernel/optimizer/geqo/geqo_random.cpp b/src/gausskernel/optimizer/geqo/geqo_random.cpp index 8b9891f46..d391d22f0 100644 --- a/src/gausskernel/optimizer/geqo/geqo_random.cpp +++ b/src/gausskernel/optimizer/geqo/geqo_random.cpp @@ -17,7 +17,7 @@ #include "optimizer/geqo_random.h" -void geqo_set_seed(PlannerInfo* root, double seed) +void geqo_set_seed(PlannerInfo* root, double seed)// 设置遗传查询优化的随机种子 { GeqoPrivateData* priv = (GeqoPrivateData*)root->join_search_private; @@ -26,15 +26,19 @@ void geqo_set_seed(PlannerInfo* root, double seed) * critical to do so. */ errno_t rc = EOK; - rc = memset_s(priv->random_state, sizeof(priv->random_state), 0, sizeof(priv->random_state)); + rc = memset_s(priv->random_state, sizeof(priv->random_state), 0, sizeof(priv->random_state));// 使用memset_s将随机状态数组初始化为0 securec_check(rc, "\0", "\0"); + // 使用memcpy_s将给定的种子复制到随机状态数组中 rc = memcpy_s(priv->random_state, sizeof(priv->random_state), &seed, Min(sizeof(priv->random_state), sizeof(seed))); securec_check(rc, "\0", "\0"); } - +// 生成随机数(0到1之间)用于遗传查询优化 double geqo_rand(PlannerInfo* root) { GeqoPrivateData* priv = (GeqoPrivateData*)root->join_search_private; - +// 使用erand48函数生成随机数,基于随机状态数组 return erand48(priv->random_state); } +/* +第一个函数用于设置随机种子,第二个函数用于生成遗传查询优化中的随机数。 +*/ \ No newline at end of file diff --git a/src/gausskernel/optimizer/geqo/geqo_recombination.cpp b/src/gausskernel/optimizer/geqo/geqo_recombination.cpp index f9993eed1..c04227699 100644 --- a/src/gausskernel/optimizer/geqo/geqo_recombination.cpp +++ b/src/gausskernel/optimizer/geqo/geqo_recombination.cpp @@ -32,21 +32,21 @@ * this array. When a city is chosen, the array is shortened * and the procedure repeated. */ -void init_tour(PlannerInfo* root, Gene* tour, int num_gene) +void init_tour(PlannerInfo* root, Gene* tour, int num_gene)// 初始化一个遍历染色体(tour)的函数 { - Gene* tmp = NULL; - int remainder; - int next, i; + Gene* tmp = NULL;// 临时数组,用于初始化基因 + int remainder;// 剩余未处理的基因数量 + int next, i;// 下一个基因的位置和循环变量 /* Fill a temp array with the IDs of all not-yet-visited cities */ - tmp = (Gene*)palloc(num_gene * sizeof(Gene)); + tmp = (Gene*)palloc(num_gene * sizeof(Gene));// 分配临时数组以初始化基因 for (i = 0; i < num_gene; i++) tmp[i] = (Gene)(i + 1); remainder = num_gene - 1; - for (i = 0; i < num_gene; i++) { + for (i = 0; i < num_gene; i++) {// 随机生成遍历顺序,初始化染色体 /* choose value between 0 and remainder inclusive */ next = geqo_randint(root, remainder, 0); /* output that element of the tmp array */ @@ -56,6 +56,7 @@ void init_tour(PlannerInfo* root, Gene* tour, int num_gene) remainder--; } + // 释放临时数组内存 pfree_ext(tmp); } @@ -63,7 +64,7 @@ void init_tour(PlannerInfo* root, Gene* tour, int num_gene) * * allocate memory for city table */ -City* alloc_city_table(PlannerInfo* root, int num_gene) +City* alloc_city_table(PlannerInfo* root, int num_gene)// 分配城市信息表的内存 { City* city_table = NULL; @@ -80,7 +81,10 @@ City* alloc_city_table(PlannerInfo* root, int num_gene) * * deallocate memory of city table */ -void free_city_table(PlannerInfo* root, City* city_table) +void free_city_table(PlannerInfo* root, City* city_table)// 释放城市信息表的内存 { pfree_ext(city_table); } +/* +这些代码包括了初始化遍历染色体的函数、分配城市信息表内存的函数以及释放城市信息表内存的函数 +*/ \ No newline at end of file diff --git a/src/gausskernel/optimizer/geqo/geqo_selection.cpp b/src/gausskernel/optimizer/geqo/geqo_selection.cpp index 3255c885c..7273e0091 100644 --- a/src/gausskernel/optimizer/geqo/geqo_selection.cpp +++ b/src/gausskernel/optimizer/geqo/geqo_selection.cpp @@ -41,7 +41,7 @@ #include "optimizer/geqo_copy.h" #include "optimizer/geqo_random.h" #include "optimizer/geqo_selection.h" - +// 线性随机数生成函数,用于选择遗传算法中的父代 static int linear_rand(PlannerInfo* root, int max, double bias); /* @@ -49,11 +49,13 @@ static int linear_rand(PlannerInfo* root, int max, double bias); * according to bias described by input parameters, * first and second genes are selected from the pool */ -void geqo_selection(PlannerInfo* root, Chromosome* momma, Chromosome* daddy, Pool* pool, double bias) +void geqo_selection(PlannerInfo* root, Chromosome* momma, Chromosome* daddy, Pool* pool, double bias)// 遗传算法中的选择操作,选择两个染色体作为父代 + { int first, second; - +// 使用linear_rand函数随机选择第一个父代 first = linear_rand(root, pool->size, bias); + // 使用linear_rand函数随机选择第二个父代 second = linear_rand(root, pool->size, bias); /* @@ -64,11 +66,11 @@ void geqo_selection(PlannerInfo* root, Chromosome* momma, Chromosome* daddy, Poo * platform's implementation of erand48() was broken. We now always use * our own version. */ - if (pool->size > 1) { + if (pool->size > 1) {// 确保选择的两个父代不相同(如果池中有多于一个染色体) while (first == second) second = linear_rand(root, pool->size, bias); } - + // 复制选定的父代到momma和daddy中 geqo_copy(root, momma, &pool->data[first], pool->string_length); geqo_copy(root, daddy, &pool->data[second], pool->string_length); } @@ -83,10 +85,10 @@ void geqo_selection(PlannerInfo* root, Chromosome* momma, Chromosome* daddy, Poo * probability distribution function is: f(x) = bias - 2(bias - 1)x * bias = (prob of first rule) / (prob of middle rule) */ -static int linear_rand(PlannerInfo* root, int pool_size, double bias) +static int linear_rand(PlannerInfo* root, int pool_size, double bias)// 线性随机数生成函数,用于选择遗传算法中的父代 { - double index; /* index between 0 and pop_size */ - double max = (double)pool_size; + double index; /* index between 0 and pop_size */// 生成的随机索引值 + double max = (double)pool_size;// 池的最大大小 /* * If geqo_rand() returns exactly 1.0 then we will get exactly max from @@ -96,8 +98,8 @@ static int linear_rand(PlannerInfo* root, int pool_size, double bias) * sqrt(). If we get a bad value just try again. */ do { - double sqrtval; - + double sqrtval; // 平方根值 +// 根据bias和随机数生成index值 sqrtval = (bias * bias) - 4.0 * (bias - 1.0) * geqo_rand(root); if (sqrtval > 0.0) sqrtval = sqrt(sqrtval); diff --git a/src/gausskernel/optimizer/path/clausesel.cpp b/src/gausskernel/optimizer/path/clausesel.cpp old mode 100755 new mode 100644 index e26a57713..9948fc230 --- a/src/gausskernel/optimizer/path/clausesel.cpp +++ b/src/gausskernel/optimizer/path/clausesel.cpp @@ -129,27 +129,22 @@ static void set_varratio_for_rqclause( Selectivity clauselist_selectivity( PlannerInfo* root, List* clauses, int varRelid, JoinType jointype, SpecialJoinInfo* sjinfo, bool varratio_cached) { - Selectivity s1 = 1.0; - RangeQueryClause* rqlist = NULL; - ListCell* l = NULL; - List* varlist = NIL; - List* clauselist = clauses; - ES_SELECTIVITY* es = NULL; - MemoryContext ExtendedStat = NULL; + Selectivity s1 = 1.0;// 初始化选择性为1.0,这是最初的选择性估计值 + RangeQueryClause* rqlist = NULL;// 用于存储范围查询子句的链表 + ListCell* l = NULL; // 遍历用的链表指针 + List* varlist = NIL; // 用于存储涉及的变量列表 + List* clauselist = clauses;// 初始化待处理的子句列表 + ES_SELECTIVITY* es = NULL;// 扩展统计信息,用于某些连接类型的选择性估算 + MemoryContext ExtendedStat = NULL; // 用于存储扩展统计信息的内存上下文 MemoryContext oldcontext; - /* - * If there's exactly one clause, then no use in trying to match up pairs, - * so just go directly to clause_selectivity(). - */ - if (list_length(clauses) == 1) + if (list_length(clauses) == 1) // 如果只有一个子句,直接调用 clause_selectivity 估算选择性并返回 return clause_selectivity(root, (Node*)linitial(clauses), varRelid, jointype, sjinfo, varratio_cached); - /* initialize es_selectivity class, list_length(clauses) can be 0 when called by set_baserel_size_estimates */ if (list_length(clauses) >= 2 && (jointype == JOIN_INNER || jointype == JOIN_FULL || jointype == JOIN_LEFT || jointype == JOIN_ANTI || - jointype == JOIN_SEMI || jointype == JOIN_LEFT_ANTI_FULL)) { - ExtendedStat = AllocSetContextCreate(CurrentMemoryContext, + jointype == JOIN_SEMI || jointype == JOIN_LEFT_ANTI_FULL)) {// 对于一些连接类型,需要进行扩展统计信息的计算 + ExtendedStat = AllocSetContextCreate(CurrentMemoryContext,// 创建内存上下文用于扩展统计信息 "ExtendedStat", ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, @@ -157,33 +152,22 @@ Selectivity clauselist_selectivity( oldcontext = MemoryContextSwitchTo(ExtendedStat); es = New(ExtendedStat) ES_SELECTIVITY(); Assert(root != NULL); - s1 = es->calculate_selectivity(root, clauses, sjinfo, jointype, NULL, ES_EQJOINSEL); + s1 = es->calculate_selectivity(root, clauses, sjinfo, jointype, NULL, ES_EQJOINSEL); // 计算选择性并更新待处理的子句列表 clauselist = es->unmatched_clause_group; (void)MemoryContextSwitchTo(oldcontext); } - /* - * Initial scan over clauses. Anything that doesn't look like a potential - * rangequery clause gets multiplied into s1 and forgotten. Anything that - * does gets inserted into an rqlist entry. - */ + // 遍历待处理的子句列表 foreach (l, clauselist) { Node* clause = (Node*)lfirst(l); RestrictInfo* rinfo = NULL; Selectivity s2; - /* Always compute the selectivity using clause_selectivity */ - s2 = clause_selectivity(root, clause, varRelid, jointype, sjinfo, varratio_cached, true); + s2 = clause_selectivity(root, clause, varRelid, jointype, sjinfo, varratio_cached, true);// 计算单个子句的选择性 - /* - * Check for being passed a RestrictInfo. - * - * If it's a pseudoconstant RestrictInfo, then s2 is either 1.0 or - * 0.0; just use that rather than looking for range pairs. - */ if (IsA(clause, RestrictInfo)) { rinfo = (RestrictInfo*)clause; - if (rinfo->pseudoconstant) { + if (rinfo->pseudoconstant) {// 如果子句中包含伪常量,更新选择性并继续下一个子句 s1 = s1 * s2; rinfo->clause->selec = s2; continue; @@ -192,18 +176,9 @@ Selectivity clauselist_selectivity( } else rinfo = NULL; - /* - * if the clause is range query like 'between and', - * we should scan the pair of rangequery and compute final selectivity. - */ OpExpr* expr = (OpExpr*)clause; bool varonleft = true; - if (is_rangequery_clause(clause, rinfo, &varonleft)) { - /* - * If it's not a "<" or ">" operator, just merge the - * selectivity in generically. But if it's the right oprrest, - * add the clause to rqlist for later processing. - */ + if (is_rangequery_clause(clause, rinfo, &varonleft)) {// 检查是否为范围查询子句,根据不同的操作符类型进行处理 switch (get_oprrest(expr->opno)) { case F_SCALARLTSEL: addRangeClause(&rqlist, clause, varonleft, true, s2); @@ -211,8 +186,7 @@ Selectivity clauselist_selectivity( case F_SCALARGTSEL: addRangeClause(&rqlist, clause, varonleft, false, s2); break; - default: - /* Just merge the selectivity in generically */ + default:// 根据不同的参数选择性计算策略更新选择性 if ((uint32)u_sess->attr.attr_sql.cost_param & COST_ALTERNATIVE_CONJUNCT) { s1 = MIN(s1, s2); expr->xpr.selec = s1; @@ -224,9 +198,7 @@ Selectivity clauselist_selectivity( } continue; } - - /* Not the right form, so treat it generically. */ - if ((uint32)u_sess->attr.attr_sql.cost_param & COST_ALTERNATIVE_CONJUNCT) { + if ((uint32)u_sess->attr.attr_sql.cost_param & COST_ALTERNATIVE_CONJUNCT) {// 对于非范围查询子句,跟据不同的参数选择性计算策略更新选择性 s1 = MIN(s1, s2); expr->xpr.selec = s1; } else { @@ -234,59 +206,31 @@ Selectivity clauselist_selectivity( expr->xpr.selec = s2; } } - - /* - * Now scan the rangequery pair list. - */ - while (rqlist != NULL) { + while (rqlist != NULL) {// 处理范围查询子句列表中的每个子句 RangeQueryClause* rqnext = NULL; if (rqlist->have_lobound && rqlist->have_hibound) { /* Successfully matched a pair of range clauses */ Selectivity s2; - /* - * Exact equality to the default value probably means the - * selectivity function punted. This is not airtight but should - * be good enough. - */ - if (rqlist->hibound == DEFAULT_INEQ_SEL || rqlist->lobound == DEFAULT_INEQ_SEL) { + if (rqlist->hibound == DEFAULT_INEQ_SEL || rqlist->lobound == DEFAULT_INEQ_SEL) {//如果上下限为默认值,那么选择性为默认值 s2 = DEFAULT_RANGE_INEQ_SEL; } else { - s2 = rqlist->hibound + rqlist->lobound - 1.0; + s2 = rqlist->hibound + rqlist->lobound - 1.0;//否则进行计算 - /* Adjust for double-exclusion of NULLs */ s2 += nulltestsel(root, IS_NULL, rqlist->var, varRelid, jointype, sjinfo); - /* - * A zero or slightly negative s2 should be converted into a - * small positive value; we probably are dealing with a very - * tight range and got a bogus result due to roundoff errors. - * However, if s2 is very negative, then we probably have - * default selectivity estimates on one or both sides of the - * range that we failed to recognize above for some reason. - */ if (s2 <= 0.0) { - if (s2 < -0.01) { - /* - * No data available --- use a default estimate that - * is small, but not real small. - */ + if (s2 < -0.01) {//如果选择性非常小,选择默认值 s2 = DEFAULT_RANGE_INEQ_SEL; - } else { - /* - * It's just roundoff error; use a small positive - * value - */ + } else {//否则,取一个非常小的正数 s2 = 1.0e-10; } } } - /* Merge in the selectivity of the pair of clauses */ s1 *= s2; rqlist->clause->selec = s2; } else { - /* Only found one of a pair, merge it in generically */ if (rqlist->have_lobound) { s1 *= rqlist->lobound; rqlist->clause->selec = rqlist->lobound; @@ -301,21 +245,16 @@ Selectivity clauselist_selectivity( pfree_ext(rqlist); rqlist = rqnext; } - - /* we should cache the range query's var ratio if can do and there are range query's vars. */ - if (varratio_cached && varlist != NIL) + if (varratio_cached && varlist != NIL)// 如果启用了变量比率缓存且存在变量列表,则设置变量比率 set_varratio_for_rqclause(root, varlist, varRelid, s1, sjinfo); list_free_ext(varlist); - - /* free space used by extended statistic */ - if (es != NULL) { + if (es != NULL) {// 清理扩展统计信息相关内存 clauselist = NIL; list_free_ext(es->unmatched_clause_group); delete es; MemoryContextDelete(ExtendedStat); } - return s1; } @@ -326,27 +265,28 @@ Selectivity clauselist_selectivity( */ static void addRangeClause(RangeQueryClause** rqlist, Node* clause, bool varonleft, bool isLTsel, Selectivity s2) { - RangeQueryClause* rqelem = NULL; - Node* var = NULL; - bool is_lobound = false; + RangeQueryClause* rqelem = NULL;// 用于表示范围查询子句的数据结构 + Node* var = NULL; // 用于表示子句中的变量 + bool is_lobound = false; // 标识是否是下限子句 - if (varonleft) { + + if (varonleft) {// 如果子句的变量在左侧,获取左操作数 var = get_leftop((Expr*)clause); is_lobound = !isLTsel; /* x < something is high bound */ - } else { + } else {// 如果子句的变量在右侧,获取右操作数 var = get_rightop((Expr*)clause); is_lobound = isLTsel; /* something < x is low bound */ } - for (rqelem = *rqlist; rqelem; rqelem = rqelem->next) { + for (rqelem = *rqlist; rqelem; rqelem = rqelem->next) {// 遍历已有的范围查询子句列表 /* * We use full equal() here because the "var" might be a function of * one or more attributes of the same relation... */ - if (!equal(var, rqelem->var)) + if (!equal(var, rqelem->var)) // 如果当前子句是下限子句且范围查询子句中没有下限子句,设置下限子句信息 continue; /* Found the right group to put this clause in */ - if (is_lobound) { + if (is_lobound) {// 如果已经有下限子句,比较并保留选择性较小的下限子句 if (!rqelem->have_lobound) { rqelem->have_lobound = true; rqelem->lobound = s2; @@ -380,7 +320,7 @@ static void addRangeClause(RangeQueryClause** rqlist, Node* clause, bool varonle rqelem->clause = (Expr*)clause; return; } - +// 如果在范围查询子句列表中没有找到匹配的变量,创建一个新的范围查询子句并添加到列表 /* No matching var found, so make a new clause-pair data structure */ rqelem = (RangeQueryClause*)palloc(sizeof(RangeQueryClause)); rqelem->var = var; @@ -404,15 +344,15 @@ static void addRangeClause(RangeQueryClause** rqlist, Node* clause, bool varonle * Decide whether an operator clause is to be handled by the * restriction or join estimator. Subroutine for clause_selectivity(). */ -bool treat_as_join_clause(Node* clause, RestrictInfo* rinfo, int varRelid, SpecialJoinInfo* sjinfo) +bool treat_as_join_clause(Node* clause, RestrictInfo* rinfo, int varRelid, SpecialJoinInfo* sjinfo)//判断给定的子句是否应该被视为连接子句 { - if (varRelid != 0) { + if (varRelid != 0) {// 如果变量关联标识不为0,表示这个子句与一个特定的关系变量相关,不被视为连接子句 /* * Caller is forcing restriction mode (eg, because we are examining an * inner indexscan qual). */ return false; - } else if (sjinfo == NULL) { + } else if (sjinfo == NULL) {// 如果特殊连接信息为空,也不被视为连接子句 /* * It must be a restriction clause, since it's being evaluated at a * scan node. @@ -429,9 +369,9 @@ bool treat_as_join_clause(Node* clause, RestrictInfo* rinfo, int varRelid, Speci * anyway, it seems likely that we ought to account for the * probability of injected nulls somehow. */ - if (rinfo != NULL) + if (rinfo != NULL)// 如果有 RestrictInfo,检查 RestrictInfo 中的关系变量是否属于多重集合(可能与多个表关联) return (bms_membership(rinfo->clause_relids) == BMS_MULTIPLE); - else + else// 如果没有 RestrictInfo,检查子句中的关系变量数量是否大于1(可能与多个表关联) return (NumRelids(clause) > 1); } } @@ -482,9 +422,9 @@ Selectivity clause_selectivity(PlannerInfo* root, Node* clause, int varRelid, Jo SpecialJoinInfo* sjinfo, bool varratio_cached, bool check_scalarop) { Selectivity s1 = 0.5; /* default for any unhandled clause type */ - RestrictInfo* rinfo = NULL; - bool cacheable = false; - RatioType ratiotype = RatioType_Filter; + RestrictInfo* rinfo = NULL; // 用于存储约束信息的指针 + bool cacheable = false;// 是否可以缓存选择性值 + RatioType ratiotype = RatioType_Filter;// 选择性的类型,默认为过滤选择性 if (clause == NULL) /* can this still happen? */ return s1; @@ -502,14 +442,14 @@ Selectivity clause_selectivity(PlannerInfo* root, Node* clause, int varRelid, Jo */ if (rinfo->pseudoconstant) { if (!IsA(rinfo->clause, Const)) - return (Selectivity)1.0; + return (Selectivity)1.0;// 如果约束是伪常量但不是常量,则返回选择性1.0 } /* * If the clause is marked redundant, always return 1.0. */ if (rinfo->norm_selec > 1) - return (Selectivity)1.0; + return (Selectivity)1.0;// 如果约束的规范选择性大于1.0,则返回选择性1.0 /* * If possible, cache the result of the selectivity calculation for @@ -534,7 +474,7 @@ Selectivity clause_selectivity(PlannerInfo* root, Node* clause, int varRelid, Jo else clause = (Node*)rinfo->clause; } - +// 处理不同类型的查询子句 if (IsA(clause, Var)) { Var* var = (Var*)clause; @@ -626,7 +566,7 @@ Selectivity clause_selectivity(PlannerInfo* root, Node* clause, int varRelid, Jo * This estimation method doesn't give the right behavior for nulls, * but it's better than doing nothing. */ - if (IsA(clause, DistinctExpr)) + if (IsA(clause, DistinctExpr))// 如果是去重表达式,计算去重后的选择性 s1 = 1.0 - s1; } else if (is_funcclause(clause)) { /* @@ -682,7 +622,7 @@ Selectivity clause_selectivity(PlannerInfo* root, Node* clause, int varRelid, Jo if (jointype == JOIN_INNER) rinfo->norm_selec = s1; else - rinfo->outer_selec = s1; + rinfo->outer_selec = s1;// 如果约束可缓存,根据联接类型缓存选择性信息 } /* @@ -703,15 +643,15 @@ Selectivity clause_selectivity(PlannerInfo* root, Node* clause, int varRelid, Jo /* Produce arg list, const convert to expr type */ static List* switch_arg_items(Node* funExpr, Const* cnst, Oid* eqlOprOid, Oid* inputcollid, bool isequal) { - List* argList = NULL; - Node* arg = NULL; - Const* cnp = NULL; - Oid argType = InvalidOid; + List* argList = NULL;// 存储参数列表 + Node* arg = NULL;// 函数参数 + Const* cnp = NULL;// 常量参数 + Oid argType = InvalidOid;// 参数类型 - if (IsA(funExpr, FuncExpr) && ((FuncExpr*)funExpr)->funcformat == COERCE_IMPLICIT_CAST) { + if (IsA(funExpr, FuncExpr) && ((FuncExpr*)funExpr)->funcformat == COERCE_IMPLICIT_CAST) { // 检查函数表达式是否为隐式强制类型转换函数,以及它是否具有正确的格式 FuncExpr* fun_expr = (FuncExpr*)funExpr; arg = (Node*)linitial(fun_expr->args); - argType = exprType(arg); + argType = exprType(arg);// 获取参数的数据类型 HeapTuple typeTuple; Oid funcId = 0; Oid constType = exprType((Node*)cnst); @@ -722,125 +662,145 @@ static List* switch_arg_items(Node* funExpr, Const* cnst, Oid* eqlOprOid, Oid* i return NIL; } - CoercionPathType pathtype = find_coercion_pathway(argType, constType, COERCION_IMPLICIT, &funcId); + // 查找强制类型转换的路径,获取pathtype(强制转换路径的类型)、funcId(强制转换函数的ID) +CoercionPathType pathtype = find_coercion_pathway(argType, constType, COERCION_IMPLICIT, &funcId); - if (pathtype != COERCION_PATH_NONE) { - MemoryContext current_context = CurrentMemoryContext; - bool outer_is_stream = false; - bool outer_is_stream_support = false; - ResourceOwner currentOwner = t_thrd.utils_cxt.CurrentResourceOwner; - ResourceOwner tempOwner = ResourceOwnerCreate(t_thrd.utils_cxt.CurrentResourceOwner, "SwitchArgItems", - THREAD_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_OPTIMIZER)); - t_thrd.utils_cxt.CurrentResourceOwner = tempOwner; +// 如果存在强制转换路径 +if (pathtype != COERCION_PATH_NONE) { + // 保存当前内存上下文 + MemoryContext current_context = CurrentMemoryContext; + bool outer_is_stream = false; + bool outer_is_stream_support = false; + // 保存当前资源拥有者 + ResourceOwner currentOwner = t_thrd.utils_cxt.CurrentResourceOwner; + // 创建临时资源拥有者 + ResourceOwner tempOwner = ResourceOwnerCreate(t_thrd.utils_cxt.CurrentResourceOwner, "SwitchArgItems", + THREAD_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_OPTIMIZER)); + t_thrd.utils_cxt.CurrentResourceOwner = tempOwner; - if (IS_PGXC_COORDINATOR) { - outer_is_stream = u_sess->opt_cxt.is_stream; - outer_is_stream_support = u_sess->opt_cxt.is_stream_support; - } - - PG_TRY(); - { - constValue = OidFunctionCall1(funcId, ((Const*)cnst)->constvalue); - } - PG_CATCH(); - { - MemoryContextSwitchTo(current_context); - FlushErrorState(); - - /* in case they are not set back */ - if (IS_PGXC_COORDINATOR) { - u_sess->opt_cxt.is_stream = outer_is_stream; - u_sess->opt_cxt.is_stream_support = outer_is_stream_support; - } - - /* release resource applied in OidFunctionCall1 of the PG_TRY. */ - ResourceOwnerRelease(tempOwner, RESOURCE_RELEASE_BEFORE_LOCKS, false, false); - ResourceOwnerRelease(tempOwner, RESOURCE_RELEASE_LOCKS, false, false); - ResourceOwnerRelease(tempOwner, RESOURCE_RELEASE_AFTER_LOCKS, false, false); - t_thrd.utils_cxt.CurrentResourceOwner = currentOwner; - ResourceOwnerDelete(tempOwner); - - return NIL; - } - PG_END_TRY(); - - /* release resource applied in standard_planner of the PG_TRY. */ - ResourceOwnerRelease(tempOwner, RESOURCE_RELEASE_BEFORE_LOCKS, false, false); - ResourceOwnerRelease(tempOwner, RESOURCE_RELEASE_LOCKS, false, false); - ResourceOwnerRelease(tempOwner, RESOURCE_RELEASE_AFTER_LOCKS, false, false); - t_thrd.utils_cxt.CurrentResourceOwner = currentOwner; - ResourceOwnerDelete(tempOwner); - - if (IS_PGXC_COORDINATOR) { - u_sess->opt_cxt.is_stream = outer_is_stream; - u_sess->opt_cxt.is_stream_support = outer_is_stream_support; - } - } - - if (constValue) { - typeTuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(argType)); - if (!HeapTupleIsValid(typeTuple)) { - return NIL; - } - Form_pg_type type = (Form_pg_type)GETSTRUCT(typeTuple); - cnp = makeConst( - argType, exprTypmod(arg), type->typcollation, type->typlen, constValue, false, type->typbyval); - ReleaseSysCache(typeTuple); - } - } - if (cnp != NULL) { - argList = lappend(argList, arg); - argList = lappend(argList, cnp); - - if (argType == VARCHAROID) { - argType = TEXTOID; - } - - HeapTuple opertup; - opertup = SearchSysCache4(OPERNAMENSP, - CStringGetDatum(isequal ? "=" : "<>"), - ObjectIdGetDatum(argType), - ObjectIdGetDatum(argType), - ObjectIdGetDatum(PG_CATALOG_NAMESPACE)); - if (!HeapTupleIsValid(opertup)) { - return NIL; - } - - *eqlOprOid = HeapTupleGetOid(opertup); - - ReleaseSysCache(opertup); - - *inputcollid = exprCollation(arg); + // 如果当前运行在PGXC协调器上 + if (IS_PGXC_COORDINATOR) { + // 保存原始的流式查询标志和流式查询支持标志 + outer_is_stream = u_sess->opt_cxt.is_stream; + outer_is_stream_support = u_sess->opt_cxt.is_stream_support; } - return argList; + // 在PG_TRY块中执行强制转换函数,捕获可能的异常 + PG_TRY(); + { + // 调用强制转换函数,将常量值转换为目标类型 + constValue = OidFunctionCall1(funcId, ((Const*)cnst)->constvalue); + } + PG_CATCH(); + { + // 切换回原来的内存上下文 + MemoryContextSwitchTo(current_context); + FlushErrorState(); + + // 在PGXC协调器上,恢复流式查询标志和流式查询支持标志 + if (IS_PGXC_COORDINATOR) { + u_sess->opt_cxt.is_stream = outer_is_stream; + u_sess->opt_cxt.is_stream_support = outer_is_stream_support; + } + + // 释放在PG_TRY中申请的资源 + ResourceOwnerRelease(tempOwner, RESOURCE_RELEASE_BEFORE_LOCKS, false, false); + ResourceOwnerRelease(tempOwner, RESOURCE_RELEASE_LOCKS, false, false); + ResourceOwnerRelease(tempOwner, RESOURCE_RELEASE_AFTER_LOCKS, false, false); + t_thrd.utils_cxt.CurrentResourceOwner = currentOwner; + ResourceOwnerDelete(tempOwner); + + // 返回一个空列表(NIL) + return NIL; + } + PG_END_TRY(); + + // 释放在PG_TRY块中申请的资源 + ResourceOwnerRelease(tempOwner, RESOURCE_RELEASE_BEFORE_LOCKS, false, false); + ResourceOwnerRelease(tempOwner, RESOURCE_RELEASE_LOCKS, false, false); + ResourceOwnerRelease(tempOwner, RESOURCE_RELEASE_AFTER_LOCKS, false, false); + t_thrd.utils_cxt.CurrentResourceOwner = currentOwner; + ResourceOwnerDelete(tempOwner); + + // 如果在PGXC协调器上,恢复流式查询标志和流式查询支持标志 + if (IS_PGXC_COORDINATOR) { + u_sess->opt_cxt.is_stream = outer_is_stream; + u_sess->opt_cxt.is_stream_support = outer_is_stream_support; + } } +// 如果成功执行强制类型转换,constValue将不为NULL +if (constValue) { + // 在系统缓存中查找目标类型的元组 + typeTuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(argType)); + // 如果找不到目标类型的元组,返回一个空列表(NIL) + if (!HeapTupleIsValid(typeTuple)) { + return NIL; + } + // 获取目标类型的元组数据 + Form_pg_type type = (Form_pg_type)GETSTRUCT(typeTuple); + // 创建一个新的常量节点,表示转换后的常量 + cnp = makeConst( + argType, exprTypmod(arg), type->typcollation, type->typlen, constValue, false, type->typbyval); + // 释放系统缓存中的目标类型元组 + ReleaseSysCache(typeTuple); +} + +// 如果cnp不为空,表示成功创建了新的常量节点 +if (cnp != NULL) { + // 将原始参数和新的常量节点添加到参数列表中 + argList = lappend(argList, arg); + argList = lappend(argList, cnp); + + // 如果目标类型是VARCHAROID,则将argType更改为TEXTOID + if (argType == VARCHAROID) { + argType = TEXTOID; + } + + HeapTuple opertup; + // 在系统缓存中查找等于(=)或不等于(<>)操作符的元组 + opertup = SearchSysCache4(OPERNAMENSP, + CStringGetDatum(isequal ? "=" : "<>"), + ObjectIdGetDatum(argType), + ObjectIdGetDatum(argType), + ObjectIdGetDatum(PG_CATALOG_NAMESPACE)); + // 如果找不到操作符的元组,返回一个空列表(NIL) + if (!HeapTupleIsValid(opertup)) { + return NIL; + } + + // 获取操作符的OID并存储在eqlOprOid指针中 + *eqlOprOid = HeapTupleGetOid(opertup); + + // 释放系统缓存中的操作符元组 + ReleaseSysCache(opertup); + + // 获取表达式的排序规则(collation) + *inputcollid = exprCollation(arg); +} + +// 返回参数列表 +return argList; + static List* do_restrictinfo_conversion(List* args, Oid* eqlOprOid, Oid* inputcollid, bool isequal) { - AssertEreport(list_length(args) == 2, MOD_OPT, ""); - - bool lIsConst = false; + AssertEreport(list_length(args) == 2, MOD_OPT, "");// 使用 AssertEreport 断言确保传入的参数列表 args 包含且仅包含两个参数,否则引发错误 + bool lIsConst = false;//用于表示左参数和右参数是否为常量 bool rIsConst = false; - Node* lNode = (Node*)linitial(args); + Node* lNode = (Node*)linitial(args);// 从参数列表 args 中提取左参数 lNode 和右参数 rNode。 Node* rNode = (Node*)list_nth(args, 1); - List* argsList = NULL; - if (IsA(lNode, Const)) { lIsConst = true; } - if (IsA(rNode, Const)) { rIsConst = true; } - - if (lIsConst == true && rIsConst == false) { + if (lIsConst == true && rIsConst == false) {// 调用 switch_arg_items 函数,将右参数 rNode 视为常量,将左参数 lNode 视为函数参数,并将结果存储在 argsList 中 argsList = switch_arg_items(rNode, (Const*)lNode, eqlOprOid, inputcollid, isequal); } else if (lIsConst == false && rIsConst == true) { argsList = switch_arg_items(lNode, (Const*)rNode, eqlOprOid, inputcollid, isequal); } - return argsList; } @@ -858,19 +818,22 @@ static List* do_restrictinfo_conversion(List* args, Oid* eqlOprOid, Oid* inputco * * Returns: void */ +// 定义一个静态函数get_vardata_for_filter_or_semijoin,接受多个参数 static void get_vardata_for_filter_or_semijoin( PlannerInfo* root, Node* clause, int varRelid, Selectivity selec, SpecialJoinInfo* sjinfo, RatioType type) { + // 创建get_vardata_for_filter_or_semijoin_context结构体变量context,用于存储上下文信息 get_vardata_for_filter_or_semijoin_context context; bool vardataIsValid = false; - /* construct context members. */ - context.root = root; - context.varRelid = varRelid; - context.ratiotype = type; - context.sjinfo = sjinfo; + /* 构建上下文成员 */ + context.root = root; // 存储PlannerInfo指针 + context.varRelid = varRelid; // 存储关联变量的Relid + context.ratiotype = type; // 存储比率类型 + context.sjinfo = sjinfo; // 存储特殊连接信息 errno_t rc = EOK; + // 初始化context中的VariableStatData结构体成员为0 rc = memset_s(&context.filter_vardata, sizeof(VariableStatData), 0, sizeof(VariableStatData)); securec_check(rc, "\0", "\0"); rc = memset_s(&context.semijoin_vardata1, sizeof(VariableStatData), 0, sizeof(VariableStatData)); @@ -878,36 +841,43 @@ static void get_vardata_for_filter_or_semijoin( rc = memset_s(&context.semijoin_vardata2, sizeof(VariableStatData), 0, sizeof(VariableStatData)); securec_check(rc, "\0", "\0"); - /* get vardata walker for clause. */ + /* 获取clause中的变量数据 */ + // 调用get_vardata_for_filter_or_semijoin_walker函数,获取变量数据 vardataIsValid = get_vardata_for_filter_or_semijoin_walker(clause, &context); - /* we don't need set var ratio if vardata is invalid. */ + /* 如果变量数据无效,不需要设置变量比率,直接返回 */ if (!vardataIsValid) { return; } - /* set var ratio for filter or semi/anti join. */ + /* 设置过滤器或半连接/反半连接的变量比率 */ if (RatioType_Filter == type) { set_varratio_after_calc_selectivity(&context.filter_vardata, RatioType_Filter, selec, NULL); + // 释放VariableStatData资源 ReleaseVariableStats(context.filter_vardata); } else { set_varratio_after_calc_selectivity(&context.semijoin_vardata1, RatioType_Join, selec, sjinfo); set_varratio_after_calc_selectivity(&context.semijoin_vardata2, RatioType_Join, selec, sjinfo); + // 释放VariableStatData资源 ReleaseVariableStats(context.semijoin_vardata1); ReleaseVariableStats(context.semijoin_vardata2); } } +// 定义一个函数getVardataFromScalarArray,用于从标量数组中获取变量数据 void getVardataFromScalarArray(Node* node, get_vardata_for_filter_or_semijoin_context* context) { Node* left = NULL; + // 如果比率类型是Join if (RatioType_Join == context->ratiotype) { bool join_is_reversed = false; + // 调用get_join_variables函数,获取连接中的变量数据 get_join_variables(context->root, ((ScalarArrayOpExpr*)node)->args, context->sjinfo, &context->semijoin_vardata1, &context->semijoin_vardata2, &join_is_reversed); } else { left = (Node*)linitial(((ScalarArrayOpExpr*)node)->args); + // 调用examine_variable函数,检查变量数据 examine_variable(context->root, left, context->varRelid, &context->filter_vardata); } } @@ -921,39 +891,41 @@ void getVardataFromScalarArray(Node* node, get_vardata_for_filter_or_semijoin_co * * Returns: bool(true:vardata is valid) */ +// 定义静态函数get_vardata_for_filter_or_semijoin_walker,用于获取变量数据 static bool get_vardata_for_filter_or_semijoin_walker(Node* node, get_vardata_for_filter_or_semijoin_context* context) { - List* args = NIL; - Node* other = NULL; - Node* left = NULL; - Node* clause = NULL; - bool varonleft = false; + List* args = NIL; // 用于存储参数列表 + Node* other = NULL; // 用于存储另一个节点 + Node* left = NULL; // 用于存储左节点 + Node* clause = NULL; // 用于存储子节点 + bool varonleft = false; // 标志变量是否在左侧 if (node == NULL) return false; - /* get vardata info from different clause's args. */ - if (IsA(node, Var)) { + /* 从不同子节点中获取变量数据 */ + if (IsA(node, Var)) { // 如果节点是一个变量 Var* var = (Var*)node; /* - * We probably shouldn't ever see an uplevel Var here, but if we do, - * return the default selectivity... + * 我们可能不应该在这里看到uplevel Var,但如果出现,返回默认选择度... */ if (var->varlevelsup == 0 && (context->varRelid == 0 || context->varRelid == (int)var->varno)) { + // 检查变量数据 examine_variable(context->root, (Node*)var, context->varRelid, &context->filter_vardata); return true; } return false; - } else if (not_clause(node)) { + } else if (not_clause(node)) { // 如果节点是NOT表达式 clause = (Node*)get_notclausearg((Expr*)node); - } else if (is_opclause(node)) { + } else if (is_opclause(node)) { // 如果节点是操作符表达式 OpExpr* opclause = (OpExpr*)node; Oid opno = opclause->opno; - if (RatioType_Join == context->ratiotype) { + if (RatioType_Join == context->ratiotype) { // 如果是连接比率类型 bool join_is_reversed = false; + // 获取连接中的变量数据 get_join_variables(context->root, opclause->args, context->sjinfo, &context->semijoin_vardata1, &context->semijoin_vardata2, &join_is_reversed); return true; @@ -961,8 +933,9 @@ static bool get_vardata_for_filter_or_semijoin_walker(Node* node, get_vardata_fo Oid eqlOprOid = 0; List* argList = NULL; Oid inputcollid = 0; - /* only handle = or <> operator */ + /* 只处理=或<>操作符 */ if (get_oprrest(opno) == EQSELRETURNOID || get_oprrest(opno) == NEQSELRETURNOID) { + // 进行操作符参数的转换 argList = do_restrictinfo_conversion( opclause->args, &eqlOprOid, &inputcollid, get_oprrest(opno) == EQSELRETURNOID); } @@ -972,30 +945,36 @@ static bool get_vardata_for_filter_or_semijoin_walker(Node* node, get_vardata_fo else args = opclause->args; + // 获取限制条件中的变量数据 return get_restriction_variable( context->root, args, context->varRelid, &context->filter_vardata, &other, &varonleft); } - } else if (IsA(node, ScalarArrayOpExpr)) { + } else if (IsA(node, ScalarArrayOpExpr)) { // 如果节点是标量数组操作符表达式 + // 从标量数组中获取变量数据 getVardataFromScalarArray(node, context); return true; - } else if (IsA(node, RowCompareExpr)) { + } else if (IsA(node, RowCompareExpr)) { // 如果节点是行比较表达式 args = list_make2(linitial(((RowCompareExpr*)node)->largs), linitial(((RowCompareExpr*)node)->rargs)); + // 获取限制条件中的变量数据 return get_restriction_variable( context->root, args, context->varRelid, &context->filter_vardata, &other, &varonleft); - } else if (IsA(node, NullTest)) { + } else if (IsA(node, NullTest)) { // 如果节点是Null测试表达式 left = (Node*)((NullTest*)node)->arg; + // 检查变量数据 examine_variable(context->root, left, context->varRelid, &context->filter_vardata); return true; - } else if (IsA(node, BooleanTest)) { + } else if (IsA(node, BooleanTest)) { // 如果节点是布尔测试表达式 left = (Node*)((BooleanTest*)node)->arg; + // 检查变量数据 examine_variable(context->root, left, context->varRelid, &context->filter_vardata); return true; - } else if (IsA(node, RelabelType)) { + } else if (IsA(node, RelabelType)) { // 如果节点是重新标记类型表达式 clause = (Node*)((RelabelType*)node)->arg; - } else if (IsA(node, CoerceToDomain)) { + } else if (IsA(node, CoerceToDomain)) { // 如果节点是强制转换为域类型表达式 clause = (Node*)((CoerceToDomain*)node)->arg; } + // 递归处理子节点 return get_vardata_for_filter_or_semijoin_walker(clause, context); } @@ -1010,8 +989,10 @@ static bool get_vardata_for_filter_or_semijoin_walker(Node* node, get_vardata_fo * Returns: bool(true:the clause is range query) */ static bool is_rangequery_clause(Node* clause, RestrictInfo* rinfo, bool* varonleft) +// 定义静态函数is_rangequery_clause,用于检测是否是范围查询子句 { bool isrqclause = false; + // 初始化isrqclause为假 /* * See if it looks like a restriction clause with a pseudoconstant on @@ -1019,24 +1000,29 @@ static bool is_rangequery_clause(Node* clause, RestrictInfo* rinfo, bool* varonl * the simple way we are expecting.) Most of the tests here can be * done more efficiently with rinfo than without. */ + // 如果是操作符表达式并且参数个数为2 if (is_opclause(clause) && list_length(((OpExpr*)clause)->args) == 2) { - OpExpr* expr = (OpExpr*)clause; + OpExpr* expr = (OpExpr*)clause; // 强制转换为操作符表达式类型 + // 如果有rinfo信息 if (rinfo != NULL) { + // 检查rinfo的clause_relids成员是否只包含一个集合成员 + // 同时检查表达式的左侧或右侧是否为伪常量子句 isrqclause = (bms_membership(rinfo->clause_relids) == BMS_SINGLETON) && (is_pseudo_constant_clause_relids((Node*)lsecond(expr->args), rinfo->right_relids) || (*varonleft = false, is_pseudo_constant_clause_relids((Node*)linitial(expr->args), rinfo->left_relids))); - } else { + } else { // 如果没有rinfo信息 + // 检查clause的NumRelids是否为1 + // 同时检查表达式的左侧或右侧是否为伪常量子句 isrqclause = (NumRelids(clause) == 1) && (is_pseudo_constant_clause((Node*)lsecond(expr->args)) || (*varonleft = false, is_pseudo_constant_clause((Node*)linitial(expr->args)))); } } - return isrqclause; + return isrqclause; // 返回是否是范围查询子句的结果 } - /* * is_rangequery_contain_scalarop: if the range query contain scalar operator or not. * @@ -1046,20 +1032,24 @@ static bool is_rangequery_clause(Node* clause, RestrictInfo* rinfo, bool* varonl * * Returns: bool(true:the range query contain scalar operator) */ +// 定义静态函数is_rangequery_contain_scalarop,用于检测范围查询子句是否包含标量操作符 static bool is_rangequery_contain_scalarop(Node* clause, RestrictInfo* rinfo) { - bool varonleft = false; + bool varonleft = false; // 初始化变量varonleft为假 + // 调用is_rangequery_clause函数,检查子句是否符合范围查询条件,并获取varonleft值 if (is_rangequery_clause(clause, rinfo, &varonleft)) { - OpExpr* expr = (OpExpr*)clause; + OpExpr* expr = (OpExpr*)clause; // 强制转换为操作符表达式类型 + // 检查操作符的返回值是否为F_SCALARLTSEL或F_SCALARGTSEL if ((F_SCALARLTSEL == get_oprrest(expr->opno)) || (F_SCALARGTSEL == get_oprrest(expr->opno))) - return true; + return true; // 如果满足条件,返回真 } - return false; + return false; // 如果不满足条件,返回假 } + /* * set_varratio_for_rqclause: set var ratio for range query clause. * @@ -1073,22 +1063,27 @@ static bool is_rangequery_contain_scalarop(Node* clause, RestrictInfo* rinfo) * * Returns: void */ -static void set_varratio_for_rqclause( +static void set_varratio_for_rqclause(// 定义函数set_varratio_for_rqclause,用于为范围查询子句设置变量比率 PlannerInfo* root, List* varlist, int varRelid, double ratio, SpecialJoinInfo* sjinfo) { - ListCell* lc = NULL; + ListCell* lc = NULL; // 定义链表迭代器 + // 遍历变量列表 foreach (lc, varlist) { - VariableStatData vardata; - Node* node = (Node*)lfirst(lc); + VariableStatData vardata; // 定义变量统计数据结构体 + Node* node = (Node*)lfirst(lc); // 获取链表元素并强制转换为Node类型 + // 调用examine_variable函数,检查变量数据 examine_variable(root, node, varRelid, &vardata); + // 根据sjinfo是否为空,调用不同的函数设置变量比率 if (sjinfo == NULL) set_varratio_after_calc_selectivity(&vardata, RatioType_Filter, ratio, NULL); else set_varratio_after_calc_selectivity(&vardata, RatioType_Join, ratio, sjinfo); - ReleaseVariableStats(vardata); + ReleaseVariableStats(vardata); // 释放变量统计数据资源 } -} \ No newline at end of file +} + +//这两个函数分别用于检测范围查询子句是否包含标量操作符,并为范围查询子句中的变量设置比率。第一个函数返回一个布尔值,指示是否包含标量操作符,第二个函数遍历变量列表,并根据条件设置变量比率。 diff --git a/src/gausskernel/optimizer/path/costsize.cpp b/src/gausskernel/optimizer/path/costsize.cpp old mode 100755 new mode 100644 index 14765833e..2be720501 --- a/src/gausskernel/optimizer/path/costsize.cpp +++ b/src/gausskernel/optimizer/path/costsize.cpp @@ -104,28 +104,27 @@ #include "pgxc/pgxc.h" /* The default value of the column row width */ -#define COL_TUPLE_WIDTH 30 +#define COL_TUPLE_WIDTH 30 // 定义列元组的宽度为30 typedef struct { PlannerInfo* root; QualCost total; } cost_qual_eval_context; -/* Identify the max global rows of joinrel if have over estimate. */ -#define JOINREL_MAX_GLOBAL_ROWS (double)(1.0e11) +#define JOINREL_MAX_GLOBAL_ROWS (double)(1.0e11) // 定义JOINREL_MAX_GLOBAL_ROWS为1.0e11 -static bool cost_qual_eval_walker(Node* node, cost_qual_eval_context* context); +static bool cost_qual_eval_walker(Node* node, cost_qual_eval_context* context); // 声明一个名为cost_qual_eval_walker的静态函数 static void get_restriction_qual_cost( - PlannerInfo* root, RelOptInfo* baserel, ParamPathInfo* param_info, QualCost* qpqual_cost); + PlannerInfo* root, RelOptInfo* baserel, ParamPathInfo* param_info, QualCost* qpqual_cost); // 声明一个名为get_restriction_qual_cost的静态函数 static double calc_joinrel_size_estimate(PlannerInfo* root, double outer_rows, double inner_rows, - SpecialJoinInfo* sjinfo, List* restrictlist, bool varratio_cached); -static int calc_distributekey_width(Path* path, int* width, bool vectorized, bool aligned); -static Cost get_subqueryscan_stream_cost(Plan* subplan); -static bool is_predpush_dest(PlannerInfo* root, Relids indexes); -static bool enable_parametrized_path(PlannerInfo* root, RelOptInfo* baserel, Path* path); + SpecialJoinInfo* sjinfo, List* restrictlist, bool varratio_cached); // 声明一个名为calc_joinrel_size_estimate的静态函数 +static int calc_distributekey_width(Path* path, int* width, bool vectorized, bool aligned); // 声明一个名为calc_distributekey_width的静态函数 +static Cost get_subqueryscan_stream_cost(Plan* subplan); // 声明一个名为get_subqueryscan_stream_cost的静态函数 +static bool is_predpush_dest(PlannerInfo* root, Relids indexes); // 声明一个名为is_predpush_dest的静态函数 +static bool enable_parametrized_path(PlannerInfo* root, RelOptInfo* baserel, Path* path); // 声明一个名为enable_parametrized_path的静态函数 -extern bool isExprSonicEnable(Expr* node); -extern bool isAggrefSonicEnable(Oid aggfnoid); +extern bool isExprSonicEnable(Expr* node); // 声明一个名为isExprSonicEnable的外部函数 +extern bool isAggrefSonicEnable(Oid aggfnoid); // 声明一个名为isAggrefSonicEnable的外部函数 /* * init_plan_cost @@ -138,26 +137,26 @@ extern bool isAggrefSonicEnable(Oid aggfnoid); */ void init_plan_cost(Plan* plan) { - plan->startup_cost = 0.0; - plan->total_cost = 0.0; - plan->multiple = 1.0; - plan->plan_rows = 0.0; - plan->plan_width = 0; - plan->innerdistinct = 1.0; - plan->outerdistinct = 1.0; - plan->pred_rows = -1.0; - plan->pred_startup_time = -1.0; - plan->pred_total_time = -1.0; - plan->pred_max_memory = -1; + plan->startup_cost = 0.0; // 初始化计划的启动成本为0 + plan->total_cost = 0.0; // 初始化计划的总成本为0 + plan->multiple = 1.0; // 初始化计划的多重度为1.0 + plan->plan_rows = 0.0; // 初始化计划的行数为0 + plan->plan_width = 0; // 初始化计划的宽度为0 + plan->innerdistinct = 1.0; // 初始化计划的内部不同行数为1.0 + plan->outerdistinct = 1.0; // 初始化计划的外部不同行数为1.0 + plan->pred_rows = -1.0; // 初始化计划的预测行数为-1.0 + plan->pred_startup_time = -1.0; // 初始化计划的预测启动时间为-1.0 + plan->pred_total_time = -1.0; // 初始化计划的预测总时间为-1.0 + plan->pred_max_memory = -1; // 初始化计划的预测最大内存为-1 } static inline void get_info_from_rel( Relation relation, int* maxBatchRow, bool* isPartTable, bool* isValuePartTable, int* partialClusterRows) { - *maxBatchRow = RelationGetMaxBatchRows(relation); - *isPartTable = RELATION_IS_PARTITIONED(relation); - *isValuePartTable = RELATION_IS_VALUE_PARTITIONED(relation); - *partialClusterRows = RelationGetPartialClusterRows(relation); + *maxBatchRow = RelationGetMaxBatchRows(relation); // 获取关系的最大批次行数 + *isPartTable = RELATION_IS_PARTITIONED(relation); // 检查关系是否分区表 + *isValuePartTable = RELATION_IS_VALUE_PARTITIONED(relation); // 检查关系是否值分区表 + *partialClusterRows = RelationGetPartialClusterRows(relation); // 获取关系的部分集群行数 } /* @@ -192,90 +191,75 @@ static inline void get_info_from_rel( void cost_insert(Path* path, bool vectorized, Cost input_cost, double tuples, int width, Cost comparison_cost, int modify_mem, int dop, Oid resultRelOid, bool isDfsStore, OpMemInfo* mem_info) { - Cost startup_cost = input_cost; - Cost run_cost = 0; - double input_bytes = relation_byte_size(tuples, width, vectorized) / SET_DOP(dop); - double output_bytes = 0; - double output_bytes_insert = 0; - double output_bytes_pck = 0; - long modify_mem_bytes = modify_mem * 1024L / SET_DOP(dop); - /* isPartTable is judge whether is range partition. isValuePartTable is judge whether is value partition */ - bool isPartTable = false; - bool isValuePartTable = false; - bool hasPck = false; + Cost startup_cost = input_cost; // 初始化启动成本为输入成本 + Cost run_cost = 0; // 初始化运行成本为0 + double input_bytes = relation_byte_size(tuples, width, vectorized) / SET_DOP(dop); // 计算输入字节数 + double output_bytes = 0; // 初始化输出字节数为0 + double output_bytes_insert = 0; // 初始化插入输出字节数为0 + double output_bytes_pck = 0; // 初始化PCK输出字节数为0 + long modify_mem_bytes = modify_mem * 1024L / SET_DOP(dop); // 计算修改内存字节数 + + bool isPartTable = false; // 初始化是否为分区表标志为假 + bool isValuePartTable = false; // 初始化是否为值分区表标志为假 + bool hasPck = false; // 初始化是否具有PCK标志为假 Relation relation; - int maxBatchRow = MAX_BATCH_ROWS; - int partialClusterRows = PARTIAL_CLUSTER_ROWS; - double sortRows = 1; + int maxBatchRow = MAX_BATCH_ROWS; // 初始化最大批次行数为MAX_BATCH_ROWS + int partialClusterRows = PARTIAL_CLUSTER_ROWS; // 初始化部分集群行数为PARTIAL_CLUSTER_ROWS + double sortRows = 1; // 初始化排序行数为1 - /* We should compute the table's partition num and maxBatchRow and pck and index information. */ if (resultRelOid) { - relation = relation_open(resultRelOid, AccessShareLock); - get_info_from_rel(relation, &maxBatchRow, &isPartTable, &isValuePartTable, &partialClusterRows); + relation = relation_open(resultRelOid, AccessShareLock); // 打开指定OID的关系 + get_info_from_rel(relation, &maxBatchRow, &isPartTable, &isValuePartTable, &partialClusterRows); // 获取关系信息 if (relation->rd_rel->relhasclusterkey) { - hasPck = true; + hasPck = true; // 如果关系具有PCK,则设置标志为真 } - relation_close(relation, NoLock); + relation_close(relation, NoLock); // 关闭关系 } - /* - * We want to be sure the cost of a sort is never estimated as zero, even - * if passed-in tuple count is zero. Besides, mustn't do log(0)... - */ + if (tuples < 2.0) { - tuples = 2.0; + tuples = 2.0; // 如果行数小于2.0,则设置行数为2.0 } - /* Include the default cost-per-comparison */ - comparison_cost += 2.0 * u_sess->attr.attr_sql.cpu_operator_cost; + comparison_cost += 2.0 * u_sess->attr.attr_sql.cpu_operator_cost; // 增加比较成本 - /* if dfs table for insert, the memory is 128MB. If cstore table for insert, the memory is maxBatchRow*width. */ if (isDfsStore) - output_bytes_insert = DFS_MIN_MEM_SIZE * MEM_KB; + output_bytes_insert = DFS_MIN_MEM_SIZE * MEM_KB; // 如果是DFS存储,设置插入输出字节数为DFS_MIN_MEM_SIZE * MEM_KB else - output_bytes_insert = relation_byte_size(maxBatchRow, width, vectorized) * 3; + output_bytes_insert = relation_byte_size(maxBatchRow, width, vectorized) * 3; // 否则计算插入输出字节数 if (output_bytes_insert > modify_mem_bytes) { - /* CPU costs : Assume about N log2 N comparisons */ - startup_cost += comparison_cost * tuples * LOG2(tuples); - /* Disk costs */ - startup_cost += compute_sort_disk_cost(input_bytes, modify_mem_bytes); + startup_cost += comparison_cost * tuples * LOG2(tuples); // 如果插入输出字节数大于修改内存字节数,增加启动成本 + startup_cost += compute_sort_disk_cost(input_bytes, modify_mem_bytes); // 增加排序磁盘成本 } else { if (tuples > 2 * maxBatchRow || input_bytes > modify_mem_bytes) { - /* - * We'll use a bounded heap-sort keeping just K tuples in memory, for - * a total number of tuple comparisons of N log2 K; but the constant - * factor is a bit higher than for quicksort. Tweak it so that the - * cost curve is continuous at the crossover point. - */ - startup_cost += comparison_cost * tuples * LOG2(2.0 * maxBatchRow); + startup_cost += comparison_cost * tuples * LOG2(2.0 * maxBatchRow); // 否则根据条件增加启动成本 } else { - /* We'll use plain quicksort on all the input tuples */ - startup_cost += comparison_cost * tuples * LOG2(tuples); + startup_cost += comparison_cost * tuples * LOG2(tuples); // 或者增加启动成本 } } if (isDfsStore) { - sortRows = tuples > partialClusterRows ? partialClusterRows : tuples; + sortRows = tuples > partialClusterRows ? partialClusterRows : tuples; // 如果是DFS存储,设置排序行数 if (mem_info != NULL) { - mem_info->opMem = modify_mem; + mem_info->opMem = modify_mem; // 设置操作内存 if (hasPck && isValuePartTable) { - output_bytes_insert = PARTITION_MAX_SIZE * MEM_KB; - output_bytes_pck = relation_byte_size(sortRows, width, vectorized); - mem_info->maxMem = output_bytes_pck / MEM_KB + output_bytes_insert / MEM_KB; - mem_info->minMem = mem_info->maxMem; + output_bytes_insert = PARTITION_MAX_SIZE * MEM_KB; // 如果有PCK且是值分区表,设置插入输出字节数 + output_bytes_pck = relation_byte_size(sortRows, width, vectorized); // 计算PCK输出字节数 + mem_info->maxMem = output_bytes_pck / MEM_KB + output_bytes_insert / MEM_KB; // 设置最大内存 + mem_info->minMem = mem_info->maxMem; // 设置最小内存 } else if (!hasPck && isValuePartTable) { - output_bytes = PARTITION_MAX_SIZE * MEM_KB; - mem_info->maxMem = output_bytes / MEM_KB; - mem_info->minMem = mem_info->maxMem; + output_bytes = PARTITION_MAX_SIZE * MEM_KB; // 如果没有PCK且是值分区表,设置输出字节数 + mem_info->maxMem = output_bytes / MEM_KB; // 设置最大内存 + mem_info->minMem = mem_info->maxMem; // 设置最小内存 } else if (hasPck && !isValuePartTable) { - output_bytes_pck = relation_byte_size(sortRows, width, vectorized); - mem_info->maxMem = (output_bytes_insert + output_bytes_pck) / MEM_KB; - mem_info->minMem = output_bytes_insert / MEM_KB + output_bytes_pck / MEM_KB / SORT_MAX_DISK_SIZE; + output_bytes_pck = relation_byte_size(sortRows, width, vectorized); // 如果有PCK且不是值分区表,计算PCK输出字节数 + mem_info->maxMem = (output_bytes_insert + output_bytes_pck) / MEM_KB; // 设置最大内存 + mem_info->minMem = output_bytes_insert / MEM_KB + output_bytes_pck / MEM_KB / SORT_MAX_DISK_SIZE; // 设置最小内存 } else { - mem_info->maxMem = output_bytes_insert / MEM_KB; - mem_info->minMem = output_bytes_insert / MEM_KB; + mem_info->maxMem = output_bytes_insert / MEM_KB; // 否则设置最大内存 + mem_info->minMem = output_bytes_insert / MEM_KB; // 设置最小内存 } - mem_info->regressCost = compute_sort_disk_cost(input_bytes, mem_info->minMem); + mem_info->regressCost = compute_sort_disk_cost(input_bytes, mem_info->minMem); // 计算回归成本 MEMCTL_LOG(DEBUG2, "DFS INSERT:The opMem is: %lfKB, the maxMem is: %lfKB, the minMem is: %lfKB", mem_info->opMem, @@ -283,29 +267,26 @@ void cost_insert(Path* path, bool vectorized, Cost input_cost, double tuples, in mem_info->minMem); } } else { - /* calucate the mem_info for partition\ partition_pck,cstoretable\cstoretable_pck.*/ if (mem_info != NULL) { - mem_info->opMem = modify_mem; + mem_info->opMem = modify_mem; // 设置操作内存 if (hasPck && isPartTable) { - /* we will need 2g memory to insert ,4g memory to sort. NOTICE : PARTITION_MAX_SIZE is KB */ - output_bytes_pck = PARTITION_MAX_SIZE * MEM_KB * 2; - output_bytes_insert = PARTITION_MAX_SIZE * MEM_KB; - mem_info->maxMem = (output_bytes_pck + output_bytes_insert) / MEM_KB; + output_bytes_pck = PARTITION_MAX_SIZE * MEM_KB * 2; // 如果有PCK且是分区表,设置PCK输出字节数 + output_bytes_insert = PARTITION_MAX_SIZE * MEM_KB; // 设置插入输出字节数 + mem_info->maxMem = (output_bytes_pck + output_bytes_insert) / MEM_KB; // 设置最大内存 } else if (!hasPck && isPartTable) { - output_bytes = output_bytes_insert; - double output_k_bytes = output_bytes / MEM_KB; - mem_info->maxMem = (output_k_bytes > PARTITION_MAX_SIZE) ? output_k_bytes : PARTITION_MAX_SIZE; + output_bytes = output_bytes_insert; // 如果没有PCK且是分区表,设置输出字节数 + double output_k_bytes = output_bytes / MEM_KB; // 计算输出字节数(KB) + mem_info->maxMem = (output_k_bytes > PARTITION_MAX_SIZE) ? output_k_bytes : PARTITION_MAX_SIZE; // 设置最大内存 } else if (hasPck && !isPartTable) { - output_bytes_pck = relation_byte_size(partialClusterRows, width, vectorized); - output_bytes = output_bytes_pck + output_bytes_insert; - mem_info->maxMem = output_bytes / MEM_KB; + output_bytes_pck = relation_byte_size(partialClusterRows, width, vectorized); // 如果有PCK且不是分区表,计算PCK输出字节数 + output_bytes = output_bytes_pck + output_bytes_insert; // 计算输出字节数 + mem_info->maxMem = output_bytes / MEM_KB; // 设置最大内存 } else { - output_bytes = output_bytes_insert; - mem_info->maxMem = output_bytes / MEM_KB; + output_bytes = output_bytes_insert; // 否则设置输出字节数 + mem_info->maxMem = output_bytes / MEM_KB; // 设置最大内存 } - - mem_info->minMem = mem_info->maxMem / SORT_MAX_DISK_SIZE; - mem_info->regressCost = compute_sort_disk_cost(input_bytes, mem_info->minMem); + mem_info->minMem = mem_info->maxMem / SORT_MAX_DISK_SIZE; // 设置最小内存 + mem_info->regressCost = compute_sort_disk_cost(input_bytes, mem_info->minMem); // 计算回归成本 MEMCTL_LOG(DEBUG2, "CSTORE INSERT:The opMem is: %lfKB, the maxMem is: %lfKB, the minMem is: %lfKB", mem_info->opMem, @@ -313,6 +294,7 @@ void cost_insert(Path* path, bool vectorized, Cost input_cost, double tuples, in mem_info->minMem); } } +} /* * Also charge a small amount (arbitrarily set equal to operator cost) per @@ -353,21 +335,22 @@ void cost_insert(Path* path, bool vectorized, Cost input_cost, double tuples, in void cost_delete(Path* path, bool vectorized, Cost input_cost, double tuples, int width, Cost comparison_cost, int modify_mem, int dop, Oid resultRelOid, bool isDfsStore, OpMemInfo* mem_info) { - Cost startup_cost = input_cost; - Cost run_cost = 0; - double input_bytes = relation_byte_size(tuples, width, vectorized) / SET_DOP(dop); - double output_bytes = 0; - double output_tuples = 0; - long modify_mem_bytes = modify_mem * 1024L / SET_DOP(dop); + Cost startup_cost = input_cost; // 初始化启动成本为输入成本 + Cost run_cost = 0; // 初始化运行成本为0 + double input_bytes = relation_byte_size(tuples, width, vectorized) / SET_DOP(dop); // 计算输入字节数 + double output_bytes = 0; // 初始化输出字节数为0 + double output_tuples = 0; // 初始化输出元组数为0 + long modify_mem_bytes = modify_mem * 1024L / SET_DOP(dop); // 计算修改内存字节数 Relation relation; - int partialClusterRows = PARTIAL_CLUSTER_ROWS; + int partialClusterRows = PARTIAL_CLUSTER_ROWS; // 初始化部分集群行数为PARTIAL_CLUSTER_ROWS if (resultRelOid) { - relation = relation_open(resultRelOid, AccessShareLock); - partialClusterRows = RelationGetPartialClusterRows(relation); - relation_close(relation, NoLock); + relation = relation_open(resultRelOid, AccessShareLock); // 打开指定OID的关系 + partialClusterRows = RelationGetPartialClusterRows(relation); // 获取关系的部分集群行数 + relation_close(relation, NoLock); // 关闭关系 } + /* * We want to be sure the cost of a sort is never estimated as zero, even * if passed-in tuple count is zero. Besides, mustn't do log(0)... @@ -404,10 +387,10 @@ void cost_delete(Path* path, bool vectorized, Cost input_cost, double tuples, in * calucate the mem_info for cstore table or dfs table. */ if (mem_info != NULL) { - mem_info->opMem = modify_mem; - mem_info->maxMem = output_bytes / MEM_KB > SORT_MIM_MEM ? output_bytes / MEM_KB : SORT_MIM_MEM; - mem_info->minMem = mem_info->maxMem / SORT_MAX_DISK_SIZE; - mem_info->regressCost = compute_sort_disk_cost(input_bytes, mem_info->minMem); + mem_info->opMem = modify_mem; // 设置操作内存 + mem_info->maxMem = output_bytes / MEM_KB > SORT_MIM_MEM ? output_bytes / MEM_KB : SORT_MIM_MEM; // 设置最大内存 + mem_info->minMem = mem_info->maxMem / SORT_MAX_DISK_SIZE; // 设置最小内存 + mem_info->regressCost = compute_sort_disk_cost(input_bytes, mem_info->minMem); // 计算回归成本 MEMCTL_LOG(DEBUG2, "MEMORY DELETE:The opMem is : %lfKB, the maxMem is :%lfKB, the minMem is :%lfKB", mem_info->opMem, @@ -423,12 +406,11 @@ void cost_delete(Path* path, bool vectorized, Cost input_cost, double tuples, in * here --- the upper LIMIT will pro-rate the run cost so we'd be double * counting the LIMIT otherwise. */ - run_cost += u_sess->attr.attr_sql.cpu_operator_cost * tuples; + run_cost += u_sess->attr.attr_sql.cpu_operator_cost * tuples; // 增加运行成本 - path->startup_cost = startup_cost; - path->total_cost = startup_cost + run_cost; + path->startup_cost = startup_cost; // 设置路径的启动成本 + path->total_cost = startup_cost + run_cost; // 设置路径的总成本 } - /* * Description: estimation the memory info for cstoreupdate and dfsupdate . update = delete +insert(insert + sort). * So, we should caculate the delete mem(sort mem) and insert mem(insert and sort). Here, the deleted @@ -464,29 +446,30 @@ void cost_delete(Path* path, bool vectorized, Cost input_cost, double tuples, in void cost_update(Path* path, bool vectorized, Cost input_cost, double tuples, int width, Cost comparison_cost, int modify_mem, int dop, Oid resultRelOid, bool isDfsStore, OpMemInfo* mem_info) { - Cost startup_cost = input_cost; - Cost run_cost = 0; - double input_bytes = relation_byte_size(tuples, width, vectorized) / SET_DOP(dop); - double output_bytes = 0; - double output_bytes_insert = 0; - double output_bytes_pck = 0; - long modify_mem_bytes = modify_mem * 1024L / SET_DOP(dop); - bool isPartTable = false; - bool isValuePartTable = false; - bool hasPck = false; + Cost startup_cost = input_cost; // 初始化启动成本为输入成本 + Cost run_cost = 0; // 初始化运行成本为0 + double input_bytes = relation_byte_size(tuples, width, vectorized) / SET_DOP(dop); // 计算输入字节数 + double output_bytes = 0; // 初始化输出字节数为0 + double output_bytes_insert = 0; // 初始化插入输出字节数为0 + double output_bytes_pck = 0; // 初始化PCK输出字节数为0 + long modify_mem_bytes = modify_mem * 1024L / SET_DOP(dop); // 计算修改内存字节数 + bool isPartTable = false; // 初始化是否为分区表标志为假 + bool isValuePartTable = false; // 初始化是否为值分区表标志为假 + bool hasPck = false; // 初始化是否具有PCK标志为假 Relation relation; - int maxBatchRow = MAX_BATCH_ROWS; - int partialClusterRows = PARTIAL_CLUSTER_ROWS; + int maxBatchRow = MAX_BATCH_ROWS; // 初始化最大批次行数为MAX_BATCH_ROWS + int partialClusterRows = PARTIAL_CLUSTER_ROWS; // 初始化部分集群行数为PARTIAL_CLUSTER_ROWS /* We should compute the table's partition num and maxBatchRow and pck and index information. */ - if (resultRelOid) { - relation = relation_open(resultRelOid, AccessShareLock); - get_info_from_rel(relation, &maxBatchRow, &isPartTable, &isValuePartTable, &partialClusterRows); + if (resultRelOid) { + relation = relation_open(resultRelOid, AccessShareLock); // 打开指定OID的关系 + get_info_from_rel(relation, &maxBatchRow, &isPartTable, &isValuePartTable, &partialClusterRows); // 获取关系信息 if (relation->rd_rel->relhasclusterkey) { - hasPck = true; + hasPck = true; // 如果关系具有PCK,则设置标志为真 } - relation_close(relation, NoLock); + relation_close(relation, NoLock); // 关闭关系 } +} /* * We want to be sure the cost of a sort is never estimated as zero, even @@ -532,49 +515,50 @@ void cost_update(Path* path, bool vectorized, Cost input_cost, double tuples, in * calucate the mem_info for partition\ cstoretable. delete memory + insert memory. * delete sort will be reused to insert sort. */ - if (isDfsStore) { - if (mem_info != NULL) { - mem_info->opMem = modify_mem; - output_bytes_pck = relation_byte_size(partialClusterRows, width, vectorized); - output_bytes = output_bytes_pck + output_bytes_insert; - mem_info->maxMem = output_bytes / MEM_KB; - mem_info->minMem = output_bytes_insert / MEM_KB + output_bytes_pck / SORT_MAX_DISK_SIZE / MEM_KB; - mem_info->regressCost = compute_sort_disk_cost(input_bytes, mem_info->minMem); + if (isDfsStore) { + if (mem_info != NULL) { + mem_info->opMem = modify_mem; // 设置操作内存 + output_bytes_pck = relation_byte_size(partialClusterRows, width, vectorized); // 计算PCK输出字节数 + output_bytes = output_bytes_pck + output_bytes_insert; // 计算总输出字节数 + mem_info->maxMem = output_bytes / MEM_KB; // 设置最大内存 + mem_info->minMem = output_bytes_insert / MEM_KB + output_bytes_pck / SORT_MAX_DISK_SIZE / MEM_KB; // 设置最小内存 + mem_info->regressCost = compute_sort_disk_cost(input_bytes, mem_info->minMem); // 计算回归成本 + MEMCTL_LOG(DEBUG2, + "DFS UPDATE:The opMem is: %lfKB, the maxMem is: %lfKB, the minMem is: %lfKB", + mem_info->opMem, + mem_info->maxMem, + mem_info->minMem); + } +} else { + if (mem_info != NULL) { + mem_info->opMem = modify_mem; // 设置操作内存 + if (isPartTable) { + /* We will need 2g memory to insert ,4g memory to sort.*/ + output_bytes_pck = PARTITION_MAX_SIZE * MEM_KB * 2; // 如果是分区表,设置PCK输出字节数 + output_bytes_insert = PARTITION_MAX_SIZE * MEM_KB; // 设置插入输出字节数 + mem_info->maxMem = (output_bytes_pck + output_bytes_insert) / MEM_KB; // 设置最大内存 + mem_info->minMem = output_bytes_insert / MEM_KB + output_bytes_pck / SORT_MAX_DISK_SIZE / MEM_KB; // 设置最小内存 MEMCTL_LOG(DEBUG2, - "DFS UPDATE:The opMem is: %lfKB, the maxMem is: %lfKB, the minMem is: %lfKB", + "CSTORE PART TABLE UPDATE:The opMem is: %lfKB, the maxMem is: %lfKB, the minMem is: %lfKB", + mem_info->opMem, + mem_info->maxMem, + mem_info->minMem); + } else { + output_bytes_pck = relation_byte_size(partialClusterRows, width, vectorized); // 计算PCK输出字节数 + output_bytes = output_bytes_pck + output_bytes_insert; // 计算总输出字节数 + mem_info->maxMem = output_bytes / MEM_KB; // 设置最大内存 + mem_info->minMem = output_bytes_insert / MEM_KB + output_bytes_pck / SORT_MAX_DISK_SIZE / MEM_KB; // 设置最小内存 + MEMCTL_LOG(DEBUG2, + "CSTORE TABLE UPDATE:The opMem is: %lfKB, the maxMem is: %lfKB, the minMem is: %lfKB", mem_info->opMem, mem_info->maxMem, mem_info->minMem); } - } else { - if (mem_info != NULL) { - mem_info->opMem = modify_mem; - if (isPartTable) { - /* We will need 2g memory to insert ,4g memory to sort.*/ - output_bytes_pck = PARTITION_MAX_SIZE * MEM_KB * 2; - output_bytes_insert = PARTITION_MAX_SIZE * MEM_KB; - mem_info->maxMem = (output_bytes_pck + output_bytes_insert) / MEM_KB; - mem_info->minMem = output_bytes_insert / MEM_KB + output_bytes_pck / SORT_MAX_DISK_SIZE / MEM_KB; - MEMCTL_LOG(DEBUG2, - "CSTORE PART TABLE UPDATE:The opMem is: %lfKB, the maxMem is: %lfKB, the minMem is: %lfKB", - mem_info->opMem, - mem_info->maxMem, - mem_info->minMem); - } else { - output_bytes_pck = relation_byte_size(partialClusterRows, width, vectorized); - output_bytes = output_bytes_pck + output_bytes_insert; - mem_info->maxMem = output_bytes / MEM_KB; - mem_info->minMem = output_bytes_insert / MEM_KB + output_bytes_pck / SORT_MAX_DISK_SIZE / MEM_KB; - MEMCTL_LOG(DEBUG2, - "CSTORE TABLE UPDATE:The opMem is: %lfKB, the maxMem is: %lfKB, the minMem is: %lfKB", - mem_info->opMem, - mem_info->maxMem, - mem_info->minMem); - } - mem_info->regressCost = compute_sort_disk_cost(input_bytes, mem_info->minMem); - } + mem_info->regressCost = compute_sort_disk_cost(input_bytes, mem_info->minMem); // 计算回归成本 } +} + /* * Also charge a small amount (arbitrarily set equal to operator cost) per @@ -653,32 +637,33 @@ static void set_parallel_path_rows(Path* path) void cost_resultscan(Path *path, PlannerInfo *root, RelOptInfo *baserel, ParamPathInfo *param_info) { - Cost startup_cost = 0; - Cost run_cost = 0; - QualCost qpqual_cost; - Cost cpu_per_tuple; + Cost startup_cost = 0; // 初始化启动成本为0 + Cost run_cost = 0; // 初始化运行成本为0 + QualCost qpqual_cost; // 初始化限制条件成本结构体 + Cost cpu_per_tuple; // 初始化每个元组的CPU成本 /* Should only be applied to RTE_RESULT base relations */ - Assert(baserel->relid > 0); - Assert(baserel->rtekind == RTE_RESULT); + Assert(baserel->relid > 0); // 断言:基本关系的relid必须大于0 + Assert(baserel->rtekind == RTE_RESULT); // 断言:基本关系的rtekind必须是RTE_RESULT /* Mark the path with the correct row estimate */ if (param_info) - path->rows = param_info->ppi_rows; + path->rows = param_info->ppi_rows; // 如果存在参数路径信息,则使用参数路径信息中的行数估算 else - path->rows = baserel->rows; + path->rows = baserel->rows; // 否则使用基本关系的行数估算 /* We charge qual cost plus cpu_tuple_cost */ - get_restriction_qual_cost(root, baserel, param_info, &qpqual_cost); + get_restriction_qual_cost(root, baserel, param_info, &qpqual_cost); // 获取限制条件的成本 - startup_cost += qpqual_cost.startup; - cpu_per_tuple = DEFAULT_CPU_TUPLE_COST + qpqual_cost.per_tuple; - run_cost += cpu_per_tuple * baserel->tuples; + startup_cost += qpqual_cost.startup; // 增加限制条件的启动成本 + cpu_per_tuple = DEFAULT_CPU_TUPLE_COST + qpqual_cost.per_tuple; // 计算每个元组的CPU成本 + run_cost += cpu_per_tuple * baserel->tuples; // 基于元组数增加运行成本 - path->startup_cost = startup_cost; - path->total_cost = startup_cost + run_cost; + path->startup_cost = startup_cost; // 设置路径的启动成本 + path->total_cost = startup_cost + run_cost; // 设置路径的总成本 } + /* * cost_seqscan * Determines and returns the cost of scanning a relation sequentially. @@ -688,46 +673,45 @@ void cost_resultscan(Path *path, PlannerInfo *root, */ void cost_seqscan(Path* path, PlannerInfo* root, RelOptInfo* baserel, ParamPathInfo* param_info) { - Cost startup_cost = 0; - Cost run_cost = 0; - double spc_seq_page_cost; - QualCost qpqual_cost; - Cost cpu_per_tuple = 0.0; - int dop = SET_DOP(path->dop); - bool disable_path = enable_parametrized_path(root, baserel, (Path*)path); + Cost startup_cost = 0; // 初始化启动成本为0 + Cost run_cost = 0; // 初始化运行成本为0 + double spc_seq_page_cost; // 初始化顺序扫描页成本 + QualCost qpqual_cost; // 初始化限制条件成本 + Cost cpu_per_tuple = 0.0; // 初始化每个元组的CPU成本 + int dop = SET_DOP(path->dop); // 获取并设置并行度 + bool disable_path = enable_parametrized_path(root, baserel, (Path*)path); // 检查是否禁用路径 /* Should only be applied to base relations */ - Assert(baserel->relid > 0); - Assert(baserel->rtekind == RTE_RELATION); + Assert(baserel->relid > 0); // 断言:只能应用于基本关系 + Assert(baserel->rtekind == RTE_RELATION); // 断言:基本关系的rtekind必须是RTE_RELATION /* Mark the path with the correct row estimate */ - set_rel_path_rows(path, baserel, param_info); - set_parallel_path_rows(path); + set_rel_path_rows(path, baserel, param_info); // 设置路径的行数估算 + set_parallel_path_rows(path); // 设置并行路径的行数估算 - /* fetch estimated page cost for tablespace containing table */ + /* 获取包含表的表空间的估算页成本 */ get_tablespace_page_costs(baserel->reltablespace, NULL, &spc_seq_page_cost); - get_restriction_qual_cost(root, baserel, param_info, &qpqual_cost); + get_restriction_qual_cost(root, baserel, param_info, &qpqual_cost); // 获取限制条件的成本 - startup_cost += qpqual_cost.startup; + startup_cost += qpqual_cost.startup; // 增加限制条件的启动成本 if (!u_sess->attr.attr_sql.enable_seqscan || disable_path) - startup_cost += g_instance.cost_cxt.disable_cost; + startup_cost += g_instance.cost_cxt.disable_cost; // 如果禁用顺序扫描或禁用路径,则增加禁用成本 /* - * When we parallel the scan node, then the disk costs and cpu costs - * wiil be equal division to all parallelism thread. + * 当我们并行扫描节点时,磁盘成本和CPU成本将均匀分配给所有并行线程。 */ - run_cost += u_sess->opt_cxt.smp_thread_cost * (dop - 1); - run_cost += spc_seq_page_cost * baserel->pages / dop; - cpu_per_tuple = u_sess->attr.attr_sql.cpu_tuple_cost + qpqual_cost.per_tuple; - run_cost += cpu_per_tuple * RELOPTINFO_LOCAL_FIELD(root, baserel, tuples) / dop; + run_cost += u_sess->opt_cxt.smp_thread_cost * (dop - 1); // 基于并行线程数增加运行成本 + run_cost += spc_seq_page_cost * baserel->pages / dop; // 基于页数和并行度增加运行成本 + cpu_per_tuple = u_sess->attr.attr_sql.cpu_tuple_cost + qpqual_cost.per_tuple; // 计算每个元组的CPU成本 + run_cost += cpu_per_tuple * RELOPTINFO_LOCAL_FIELD(root, baserel, tuples) / dop; // 基于元组数和并行度增加运行成本 - path->startup_cost = startup_cost; - path->total_cost = startup_cost + run_cost; - path->stream_cost = 0; + path->startup_cost = startup_cost; // 设置路径的启动成本 + path->total_cost = startup_cost + run_cost; // 设置路径的总成本 + path->stream_cost = 0; // 初始化流成本为0 if (!u_sess->attr.attr_sql.enable_seqscan || disable_path) path->total_cost *= - (g_instance.cost_cxt.disable_cost_enlarge_factor * g_instance.cost_cxt.disable_cost_enlarge_factor); + (g_instance.cost_cxt.disable_cost_enlarge_factor * g_instance.cost_cxt.disable_cost_enlarge_factor); // 如果禁用顺序扫描或禁用路径,增加总成本 } /* @@ -814,45 +798,40 @@ void cost_samplescan(Path* path, PlannerInfo* root, RelOptInfo* baserel, ParamPa */ void cost_cstorescan(Path* path, PlannerInfo* root, RelOptInfo* baserel) { - double spc_seq_page_cost; - Cost startup_cost = 0; - Cost run_cost = 0; - Cost cpu_per_tuple = 0.0; - int dop = SET_DOP(path->dop); + double spc_seq_page_cost; // 顺序扫描页成本 + Cost startup_cost = 0; // 初始化启动成本为0 + Cost run_cost = 0; // 初始化运行成本为0 + Cost cpu_per_tuple = 0.0; // 初始化每个元组的CPU成本 + int dop = SET_DOP(path->dop); // 获取并设置并行度 - /* Should only be applied to base relations */ - Assert(baserel->relid > 0 && baserel->rtekind == RTE_RELATION); + Assert(baserel->relid > 0 && baserel->rtekind == RTE_RELATION); // 断言:只能应用于基本关系 - set_rel_path_rows(path, baserel, NULL); - set_parallel_path_rows(path); + set_rel_path_rows(path, baserel, NULL); // 设置路径的行数估算 + set_parallel_path_rows(path); // 设置并行路径的行数估算 - /* fetch estimated page cost for tablespace containing table */ - get_tablespace_page_costs(baserel->reltablespace, NULL, &spc_seq_page_cost); + get_tablespace_page_costs(baserel->reltablespace, NULL, &spc_seq_page_cost); // 获取表空间的页成本 - startup_cost += baserel->baserestrictcost.startup; + startup_cost += baserel->baserestrictcost.startup; // 增加基本限制条件的启动成本 if (!u_sess->attr.attr_sql.enable_seqscan) - startup_cost += g_instance.cost_cxt.disable_cost; + startup_cost += g_instance.cost_cxt.disable_cost; // 如果禁用顺序扫描,增加禁用成本 - /* - * When we parallel the scan node, then the disk costs and cpu costs - * wiil be equal division to all parallelism thread. - */ - run_cost += u_sess->opt_cxt.smp_thread_cost * (dop - 1); - run_cost += spc_seq_page_cost * baserel->pages / dop; + run_cost += u_sess->opt_cxt.smp_thread_cost * (dop - 1); // 基于并行线程数增加运行成本 + run_cost += spc_seq_page_cost * baserel->pages / dop; // 基于页数和并行度增加运行成本 cpu_per_tuple = - u_sess->attr.attr_sql.cpu_tuple_cost / COL_TUPLE_COST_MULTIPLIER + baserel->baserestrictcost.per_tuple; - run_cost += cpu_per_tuple * RELOPTINFO_LOCAL_FIELD(root, baserel, tuples) / dop; + u_sess->attr.attr_sql.cpu_tuple_cost / COL_TUPLE_COST_MULTIPLIER + baserel->baserestrictcost.per_tuple; // 计算每个元组的CPU成本 + run_cost += cpu_per_tuple * RELOPTINFO_LOCAL_FIELD(root, baserel, tuples) / dop; // 基于元组数和并行度增加运行成本 - path->startup_cost = startup_cost; - path->total_cost = startup_cost + run_cost; - path->stream_cost = 0; + path->startup_cost = startup_cost; // 设置路径的启动成本 + path->total_cost = startup_cost + run_cost; // 设置路径的总成本 + path->stream_cost = 0; // 初始化流成本为0 if (!u_sess->attr.attr_sql.enable_seqscan) path->total_cost *= - (g_instance.cost_cxt.disable_cost_enlarge_factor * g_instance.cost_cxt.disable_cost_enlarge_factor); + (g_instance.cost_cxt.disable_cost_enlarge_factor * g_instance.cost_cxt.disable_cost_enlarge_factor); // 如果禁用顺序扫描,增加总成本 } + /* * Determines and returns the cost of scanning a DFS relation. * path: The scan path. @@ -861,11 +840,11 @@ void cost_cstorescan(Path* path, PlannerInfo* root, RelOptInfo* baserel) */ void cost_dfsscan(Path* path, PlannerInfo* root, RelOptInfo* baserel) { - Cost startup_cost = 0; - Cost run_cost = 0; - double spc_seq_page_cost; - Cost cpu_per_tuple = 0.0; - int dop = SET_DOP(path->dop); + Cost startup_cost = 0; // 初始化启动成本为0 + Cost run_cost = 0; // 初始化运行成本为0 + double spc_seq_page_cost; // 初始化顺序扫描页成本 + Cost cpu_per_tuple = 0.0; // 初始化每个元组的CPU成本 + int dop = SET_DOP(path->dop); // 获取并设置并行度 /* * Should only be applied to base relations. @@ -876,8 +855,8 @@ void cost_dfsscan(Path* path, PlannerInfo* root, RelOptInfo* baserel) MOD_OPT, "Only base relation can be supported when determining the cost of scanning a DFS relation."); - set_rel_path_rows(path, baserel, NULL); - set_parallel_path_rows(path); + set_rel_path_rows(path, baserel, NULL); // 设置路径的行数估算 + set_parallel_path_rows(path); // 设置并行路径的行数估算 /* * Fetch estimated page cost for tablespace containing table. @@ -893,30 +872,31 @@ void cost_dfsscan(Path* path, PlannerInfo* root, RelOptInfo* baserel) * When we parallel the scan node, then the disk costs and cpu costs * wiil be equal division to all parallelism thread. */ - run_cost += u_sess->opt_cxt.smp_thread_cost * (dop - 1); - cpu_per_tuple = - u_sess->attr.attr_sql.cpu_tuple_cost / COL_TUPLE_COST_MULTIPLIER + baserel->baserestrictcost.per_tuple; + run_cost += u_sess->opt_cxt.smp_thread_cost * (dop - 1); // 基于并行线程数增加运行成本 - run_cost += (cpu_per_tuple * RELOPTINFO_LOCAL_FIELD(root, baserel, tuples)) / dop + - (spc_seq_page_cost * baserel->pages) / dop; +cpu_per_tuple = + u_sess->attr.attr_sql.cpu_tuple_cost / COL_TUPLE_COST_MULTIPLIER + baserel->baserestrictcost.per_tuple; // 计算每个元组的CPU成本 - path->startup_cost = startup_cost; - path->total_cost = startup_cost + run_cost; - path->stream_cost = 0; +run_cost += (cpu_per_tuple * RELOPTINFO_LOCAL_FIELD(root, baserel, tuples)) / dop + + (spc_seq_page_cost * baserel->pages) / dop; // 基于元组数、并行度和页数增加运行成本 - if (!u_sess->attr.attr_sql.enable_seqscan) - path->total_cost *= - (g_instance.cost_cxt.disable_cost_enlarge_factor * g_instance.cost_cxt.disable_cost_enlarge_factor); +path->startup_cost = startup_cost; // 设置路径的启动成本 +path->total_cost = startup_cost + run_cost; // 设置路径的总成本 +path->stream_cost = 0; // 初始化流成本为0 - /* - * data redistribution for DFS table. - */ - if (true == u_sess->attr.attr_sql.enable_cluster_resize && root->query_level == 1 && - root->parse->commandType == CMD_INSERT) { - root->dataDestRelIndex = baserel->relid; - } +if (!u_sess->attr.attr_sql.enable_seqscan) + path->total_cost *= + (g_instance.cost_cxt.disable_cost_enlarge_factor * g_instance.cost_cxt.disable_cost_enlarge_factor); // 如果禁用顺序扫描,增加总成本 + +/* + * data redistribution for DFS table. + */ +if (true == u_sess->attr.attr_sql.enable_cluster_resize && root->query_level == 1 && + root->parse->commandType == CMD_INSERT) { + root->dataDestRelIndex = baserel->relid; // 在 DFS 表上启用数据重分布 } + #ifdef ENABLE_MULTIPLE_NODES /* * cost_tsstorescan @@ -924,44 +904,44 @@ void cost_dfsscan(Path* path, PlannerInfo* root, RelOptInfo* baserel) */ void cost_tsstorescan(Path *path, PlannerInfo *root, RelOptInfo *baserel) { - double spc_seq_page_cost; - Cost startup_cost = 0; - Cost run_cost = 0; - Cost cpu_per_tuple = 0.0; - int dop = SET_DOP(path->dop); + double spc_seq_page_cost; // 顺序扫描页成本 + Cost startup_cost = 0; // 初始化启动成本为0 + Cost run_cost = 0; // 初始化运行成本为0 + Cost cpu_per_tuple = 0.0; // 初始化每个元组的CPU成本 + int dop = SET_DOP(path->dop); // 获取并设置并行度 /* Should only be applied to base relations */ - Assert(baserel->relid > 0 && baserel->rtekind == RTE_RELATION); + Assert(baserel->relid > 0 && baserel->rtekind == RTE_RELATION); // 断言:只能应用于基本关系 - set_rel_path_rows(path, baserel, NULL); - set_parallel_path_rows(path); + set_rel_path_rows(path, baserel, NULL); // 设置路径的行数估算 + set_parallel_path_rows(path); // 设置并行路径的行数估算 - /* fetch estimated page cost for tablespace containing table */ + /* 获取包含表的表空间的估算页成本 */ get_tablespace_page_costs(baserel->reltablespace, NULL, &spc_seq_page_cost); - startup_cost += baserel->baserestrictcost.startup; + startup_cost += baserel->baserestrictcost.startup; // 增加基本限制条件的启动成本 if (!u_sess->attr.attr_sql.enable_seqscan) { - startup_cost += g_instance.cost_cxt.disable_cost; + startup_cost += g_instance.cost_cxt.disable_cost; // 如果禁用顺序扫描,增加禁用成本 } - /* * When we parallel the scan node, then the disk costs and cpu costs * wiil be equal division to all parallelism thread. */ - run_cost += u_sess->opt_cxt.smp_thread_cost * (dop - 1); - run_cost += spc_seq_page_cost * baserel->pages / dop; - cpu_per_tuple = u_sess->attr.attr_sql.cpu_tuple_cost / COL_TUPLE_COST_MULTIPLIER - + baserel->baserestrictcost.per_tuple; - run_cost += cpu_per_tuple * RELOPTINFO_LOCAL_FIELD(root, baserel, tuples) / dop; + run_cost += u_sess->opt_cxt.smp_thread_cost * (dop - 1); // 基于并行线程数增加运行成本 + run_cost += spc_seq_page_cost * baserel->pages / dop; // 基于页数和并行度增加运行成本 - path->startup_cost = startup_cost; - path->total_cost = startup_cost + run_cost; - path->stream_cost = 0; + cpu_per_tuple = u_sess->attr.attr_sql.cpu_tuple_cost / COL_TUPLE_COST_MULTIPLIER + + baserel->baserestrictcost.per_tuple; // 计算每个元组的CPU成本 + run_cost += cpu_per_tuple * RELOPTINFO_LOCAL_FIELD(root, baserel, tuples) / dop; // 基于元组数和并行度增加运行成本 + + path->startup_cost = startup_cost; // 设置路径的启动成本 + path->total_cost = startup_cost + run_cost; // 设置路径的总成本 + path->stream_cost = 0; // 初始化流成本为0 if (!u_sess->attr.attr_sql.enable_seqscan) { path->total_cost *= - (g_instance.cost_cxt.disable_cost_enlarge_factor * g_instance.cost_cxt.disable_cost_enlarge_factor); + (g_instance.cost_cxt.disable_cost_enlarge_factor * g_instance.cost_cxt.disable_cost_enlarge_factor); // 如果禁用顺序扫描,增加总成本 } } #endif /* ENABLE_MULTIPLE_NODES */ @@ -989,19 +969,20 @@ double apply_random_page_cost_mod(double rand_page_cost, double seq_page_cost, d */ #define COST_SLOPE_FACTOR 0.005 #define COST_PAGE_THRESHOLD 1000 - double slope_factor = COST_SLOPE_FACTOR; + double slope_factor = COST_SLOPE_FACTOR; // 定义斜率因子 /* Logistic function */ - double new_page_cost = (seq_page_cost >= rand_page_cost) ? rand_page_cost : \ - LOGISTIC_FUNC(num_of_page, COST_PAGE_THRESHOLD, rand_page_cost, seq_page_cost, slope_factor); + double new_page_cost = (seq_page_cost >= rand_page_cost) ? rand_page_cost : + LOGISTIC_FUNC(num_of_page, COST_PAGE_THRESHOLD, rand_page_cost, seq_page_cost, slope_factor); // 计算新的随机页成本 ereport(DEBUG2, (errmodule(MOD_OPT), - (errmsg("Estimating random page cost = %lf with sql_beta_feature = RAND_COST_OPT.", new_page_cost)))); + (errmsg("Estimating random page cost = %lf with sql_beta_feature = RAND_COST_OPT.", new_page_cost)))); // 发送调试消息 - return new_page_cost; + return new_page_cost; // 返回新的随机页成本 } + /* * is_predpush_dest * check if the predpush dest are part of the indexes. @@ -1010,13 +991,14 @@ double apply_random_page_cost_mod(double rand_page_cost, double seq_page_cost, d */ static bool is_predpush_dest(PlannerInfo* root, Relids indexes) { - HintState *hstate = root->parse->hintState; + HintState *hstate = root->parse->hintState; // 获取提示状态 + if (hstate == NULL) { - return false; + return false; // 如果提示状态为空,返回false } if (hstate->predpush_hint == NULL) { - return false; + return false; // 如果没有预测推送提示,返回false } ListCell *lc = NULL; @@ -1024,23 +1006,24 @@ static bool is_predpush_dest(PlannerInfo* root, Relids indexes) PredpushHint *predpushHint = (PredpushHint*)lfirst(lc); if (predpushHint->dest_id != 0 && predpushHint->candidates != NULL && \ bms_is_member(predpushHint->dest_id, indexes)) { - return true; + return true; // 如果存在预测推送提示,并且目标ID在索引中,返回true } } - return false; + return false; // 如果未满足上述条件,返回false } + /* * enable_parametrized_path * If enabled, disable all unmatched paths to get the parametrized path we need. */ static bool enable_parametrized_path(PlannerInfo* root, RelOptInfo* baserel, Path* path) { - Assert(path != NULL); + Assert(path != NULL); // 断言路径不为空 if (!ENABLE_SQL_BETA_FEATURE(PREDPUSH_SAME_LEVEL)) { /* sql beta feature is necessary */ - return false; + return false; // 如果未启用SQL Beta特性,则返回false } if (ENABLE_PRED_PUSH_FORCE(root) && is_predpush_dest(root, baserel->relids)) { @@ -1051,7 +1034,7 @@ static bool enable_parametrized_path(PlannerInfo* root, RelOptInfo* baserel, Pat } } - return false; + return false; // 默认返回false } #define HEAP_PAGES_FETCHED(isUstore, pages_fetched, allvisfrac) \ @@ -1077,26 +1060,26 @@ static bool enable_parametrized_path(PlannerInfo* root, RelOptInfo* baserel, Pat */ void cost_index(IndexPath* path, PlannerInfo* root, double loop_count) { - IndexOptInfo* index = path->indexinfo; - RelOptInfo* baserel = index->rel; - bool isUstore = baserel->is_ustore; - bool indexonly = (path->path.pathtype == T_IndexOnlyScan); - List* allclauses = NIL; - Cost startup_cost = 0; - Cost run_cost = 0; - Cost indexStartupCost; - Cost indexTotalCost; - Selectivity indexSelectivity; - double indexCorrelation, csquared; - double spc_seq_page_cost, spc_random_page_cost; - Cost min_IO_cost, max_IO_cost; - QualCost qpqual_cost; - Cost cpu_per_tuple = 0.0; - double tuples_fetched; - double pages_fetched; - bool ispartitionedindex = path->indexinfo->rel->isPartitionedTable; + IndexOptInfo* index = path->indexinfo; // 获取索引信息 + RelOptInfo* baserel = index->rel; // 获取基本关系信息 + bool isUstore = baserel->is_ustore; // 检查基本关系是否是Ustore + bool indexonly = (path->path.pathtype == T_IndexOnlyScan); // 检查是否是索引扫描 + List* allclauses = NIL; // 初始化所有的约束条件列表 + Cost startup_cost = 0; // 初始化启动成本 + Cost run_cost = 0; // 初始化运行成本 + Cost indexStartupCost; // 初始化索引启动成本 + Cost indexTotalCost; // 初始化索引总成本 + Selectivity indexSelectivity; // 初始化索引选择性 + double indexCorrelation, csquared; // 初始化索引相关性 + double spc_seq_page_cost, spc_random_page_cost; // 初始化序列页成本和随机页成本 + Cost min_IO_cost, max_IO_cost; // 初始化最小I/O成本和最大I/O成本 + QualCost qpqual_cost; // 初始化Qual成本 + Cost cpu_per_tuple = 0.0; // 初始化每个元组的CPU成本 + double tuples_fetched; // 初始化获取的元组数量 + double pages_fetched; // 初始化获取的页数 + bool ispartitionedindex = path->indexinfo->rel->isPartitionedTable; // 检查索引是否分区索引 bool disable_path = enable_parametrized_path(root, baserel, (Path*)path) || \ - (!u_sess->attr.attr_sql.enable_indexscan); + (!u_sess->attr.attr_sql.enable_indexscan); // 检查是否禁用索引扫描路径 /* Should only be applied to base relations */ AssertEreport(IsA(baserel, RelOptInfo) && IsA(index, IndexOptInfo), @@ -1110,7 +1093,7 @@ void cost_index(IndexPath* path, PlannerInfo* root, double loop_count) MOD_OPT, "Only base relation can be supported when determining the cost of scanning a relation using an index."); - set_rel_path_rows(&path->path, baserel, path->path.param_info); + set_rel_path_rows(&path->path, baserel, path->path.param_info); // 设置路径的行数估算 /* Mark the path with the correct row estimate */ if (path->path.param_info) { @@ -1120,6 +1103,8 @@ void cost_index(IndexPath* path, PlannerInfo* root, double loop_count) /* allclauses should just be the rel's restriction clauses */ allclauses = baserel->baserestrictinfo; } +} + if (disable_path) startup_cost += g_instance.cost_cxt.disable_cost; @@ -1232,77 +1217,84 @@ void cost_index(IndexPath* path, PlannerInfo* root, double loop_count) * where such a plan is actually interesting, only one page would get * fetched per scan anyway, so it shouldn't matter much.) */ - pages_fetched = ceil(indexSelectivity * (double)baserel->pages); + pages_fetched = ceil(indexSelectivity * (double)baserel->pages); // 计算索引选择性乘以基本关系的页数,取上限值 - pages_fetched = index_pages_fetched( - pages_fetched * loop_count, (BlockNumber)baserel->pages, (double)index->pages, root, ispartitionedindex); +pages_fetched = index_pages_fetched( + pages_fetched * loop_count, (BlockNumber)baserel->pages, (double)index->pages, root, ispartitionedindex); +// 根据索引扫描的选择性、循环次数、基本关系的页数和索引的页数计算获取的页数 - if (indexonly) - pages_fetched = HEAP_PAGES_FETCHED(isUstore, pages_fetched, baserel->allvisfrac); +if (indexonly) + pages_fetched = HEAP_PAGES_FETCHED(isUstore, pages_fetched, baserel->allvisfrac); +// 如果只有索引,则使用堆访问的获取页数计算 - /* Apply cost mod after new pages fetched */ +/* 在获取新页数后应用成本修改 */ +spc_random_page_cost = RANDOM_PAGE_COST(use_modded_cost, old_random_page_cost, \ + spc_seq_page_cost, pages_fetched); +// 使用 COST_MOD 函数计算新的随机页成本 + +min_IO_cost = (pages_fetched * spc_random_page_cost) / loop_count; // 计算最小 I/O 成本 + +ereport(DEBUG2, + (errmodule(MOD_OPT), + errmsg("Computing IndexScanCost(loop_count > 1): min_pages_fetched: %lf, min_IO_cost: %lf", + pages_fetched, min_IO_cost))); +// 输出调试信息,显示计算结果 + +} else { + /* + * 正常情况:应用 Mackert 和 Lohman 公式,然后 + * 在该公式和基于相关性的结果之间进行插值。 + */ + pages_fetched = index_pages_fetched( + tuples_fetched, (BlockNumber)baserel->pages, (double)index->pages, root, ispartitionedindex); + // 使用索引扫描获取的元组数、基本关系的页数和索引的页数计算获取的页数 + + if (indexonly) + pages_fetched = HEAP_PAGES_FETCHED(isUstore, pages_fetched, baserel->allvisfrac); + // 如果只有索引,则使用堆访问的获取页数计算 + + /* 应用成本修改 */ + spc_random_page_cost = RANDOM_PAGE_COST(use_modded_cost, old_random_page_cost, \ + spc_seq_page_cost, pages_fetched); + + /* max_IO_cost 适用于完全不相关的情况 (csquared=0) */ + max_IO_cost = pages_fetched * spc_random_page_cost; + + ereport(DEBUG2, + (errmodule(MOD_OPT), + errmsg("Computing IndexScanCost(loop_count = 1): max_pages_fetched: %lf, max_IO_cost: %lf", + pages_fetched, max_IO_cost))); + + /* min_IO_cost 适用于完全相关的情况 (csquared=1) */ + pages_fetched = ceil(indexSelectivity * (double)baserel->pages); + + if (indexonly) + pages_fetched = HEAP_PAGES_FETCHED(isUstore, pages_fetched, baserel->allvisfrac); + + if (pages_fetched > 0) { + /* 在获取新页数后应用成本修改 */ spc_random_page_cost = RANDOM_PAGE_COST(use_modded_cost, old_random_page_cost, \ - spc_seq_page_cost, pages_fetched); + spc_seq_page_cost, pages_fetched); - min_IO_cost = (pages_fetched * spc_random_page_cost) / loop_count; - - ereport(DEBUG2, - (errmodule(MOD_OPT), - errmsg("Computing IndexScanCost(loop_count > 1): min_pages_fetched: %lf, min_IO_cost: %lf", - pages_fetched, min_IO_cost))); + min_IO_cost = spc_random_page_cost; + if (pages_fetched > 1) + min_IO_cost += (pages_fetched - 1) * spc_seq_page_cost; } else { - /* - * Normal case: apply the Mackert and Lohman formula, and then - * interpolate between that and the correlation-derived result. - */ - pages_fetched = index_pages_fetched( - tuples_fetched, (BlockNumber)baserel->pages, (double)index->pages, root, ispartitionedindex); - - if (indexonly) - pages_fetched = HEAP_PAGES_FETCHED(isUstore, pages_fetched, baserel->allvisfrac); - - /* Apply cost mod */ - spc_random_page_cost = RANDOM_PAGE_COST(use_modded_cost, old_random_page_cost, \ - spc_seq_page_cost, pages_fetched); - - /* max_IO_cost is for the perfectly uncorrelated case (csquared=0) */ - max_IO_cost = pages_fetched * spc_random_page_cost; - - ereport(DEBUG2, - (errmodule(MOD_OPT), - errmsg("Computing IndexScanCost(loop_count = 1): max_pages_fetched: %lf, max_IO_cost: %lf", - pages_fetched, max_IO_cost))); - - /* min_IO_cost is for the perfectly correlated case (csquared=1) */ - pages_fetched = ceil(indexSelectivity * (double)baserel->pages); - - if (indexonly) - pages_fetched = HEAP_PAGES_FETCHED(isUstore, pages_fetched, baserel->allvisfrac); - - if (pages_fetched > 0) { - /* Apply cost mod after new pages fetched */ - spc_random_page_cost = RANDOM_PAGE_COST(use_modded_cost, old_random_page_cost, \ - spc_seq_page_cost, pages_fetched); - - min_IO_cost = spc_random_page_cost; - if (pages_fetched > 1) - min_IO_cost += (pages_fetched - 1) * spc_seq_page_cost; - } else { - min_IO_cost = 0; - } - - /* - * When database keep running without vacuum, the number of relpages may inflate quickly - * and finally cause min_IO_cost overestimated. So, adjust min_IO_cost to ensure - * min_IO_cost < max_IO_cost. - */ - min_IO_cost = Min(min_IO_cost, max_IO_cost); - - ereport(DEBUG2, - (errmodule(MOD_OPT), - errmsg("Computing IndexScanCost(loop_count = 1): min_pages_fetched: %lf, min_IO_cost: %lf", - pages_fetched, min_IO_cost))); + min_IO_cost = 0; } + + /* + * 当数据库在没有执行 VACUUM 的情况下持续运行时,relpages 的数量可能会迅速膨胀, + * 最终导致 min_IO_cost 过高估计。因此,调整 min_IO_cost 以确保 min_IO_cost < max_IO_cost。 + */ + min_IO_cost = Min(min_IO_cost, max_IO_cost); + + ereport(DEBUG2, + (errmodule(MOD_OPT), + errmsg("Computing IndexScanCost(loop_count = 1): min_pages_fetched: %lf, min_IO_cost: %lf", + pages_fetched, min_IO_cost))); +} + min_IO_cost = Min(min_IO_cost, max_IO_cost); @@ -1333,42 +1325,51 @@ void cost_index(IndexPath* path, PlannerInfo* root, double loop_count) * to remove such unnecessary clauses from the qpquals list if this path * is selected for use. */ - cost_qual_eval(&qpqual_cost, list_difference_ptr(allclauses, path->indexquals), root); + cost_qual_eval(&qpqual_cost, list_difference_ptr(allclauses, path->indexquals), root); +// 评估过滤条件的成本,并存储在 qpqual_cost 中,过滤条件包括不在索引列中的条件 - startup_cost += qpqual_cost.startup; +startup_cost += qpqual_cost.startup; // 增加启动成本,包括过滤条件的启动成本 - if (path->path.parent->orientation == REL_COL_ORIENTED) - cpu_per_tuple = u_sess->attr.attr_sql.cpu_tuple_cost / 10 + qpqual_cost.per_tuple; - else if (path->path.parent->orientation == REL_TIMESERIES_ORIENTED) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("Unsupported Using Index FOR TIMESERIES."))); - else - cpu_per_tuple = u_sess->attr.attr_sql.cpu_tuple_cost + qpqual_cost.per_tuple; +if (path->path.parent->orientation == REL_COL_ORIENTED) + cpu_per_tuple = u_sess->attr.attr_sql.cpu_tuple_cost / 10 + qpqual_cost.per_tuple; +// 如果关系是列存储方向,则计算 CPU 每个元组的成本 +else if (path->path.parent->orientation == REL_TIMESERIES_ORIENTED) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("Unsupported Using Index FOR TIMESERIES."))); +// 如果关系是时间序列方向,则报错,不支持索引扫描 +else + cpu_per_tuple = u_sess->attr.attr_sql.cpu_tuple_cost + qpqual_cost.per_tuple; +// 否则计算 CPU 每个元组的成本 - run_cost += cpu_per_tuple * tuples_fetched; +run_cost += cpu_per_tuple * tuples_fetched; // 增加运行成本,包括 CPU 成本和获取的元组数 - ereport(DEBUG2, - (errmodule(MOD_OPT), - errmsg("Computing IndexScanCost: cpu_per_tuple: %lf, tuples_fetched: %lf, cpu_run_cost: %lf", - cpu_per_tuple, tuples_fetched, cpu_per_tuple * tuples_fetched))); +ereport(DEBUG2, + (errmodule(MOD_OPT), + errmsg("Computing IndexScanCost: cpu_per_tuple: %lf, tuples_fetched: %lf, cpu_run_cost: %lf", + cpu_per_tuple, tuples_fetched, cpu_per_tuple * tuples_fetched))); +// 输出调试信息,显示计算结果 - path->path.startup_cost = startup_cost; - path->path.total_cost = startup_cost + run_cost; - path->path.stream_cost = 0; +path->path.startup_cost = startup_cost; // 设置路径的启动成本 +path->path.total_cost = startup_cost + run_cost; // 设置路径的总成本 +path->path.stream_cost = 0; // 设置路径的流成本为 0 - if (disable_path) - path->path.total_cost *= - (g_instance.cost_cxt.disable_cost_enlarge_factor * g_instance.cost_cxt.disable_cost_enlarge_factor); - /* clamp weighted cost below 1e30 for double overflow */ - double weight = u_sess->attr.attr_sql.cost_weight_index; - path->path.startup_cost = (1e30f / weight > path->path.startup_cost) ? (path->path.startup_cost * weight) : (1e30); - path->path.total_cost = (1e30f / weight > path->path.total_cost) ? (path->path.total_cost * weight) : (1e30); - ereport(DEBUG2, - (errmodule(MOD_OPT), - errmsg("IndexScan Cost startup_cost: %lf, total_cost: %lf, pages_fetched: %lf", - path->path.startup_cost, path->path.total_cost, pages_fetched))); -} +if (disable_path) + path->path.total_cost *= + (g_instance.cost_cxt.disable_cost_enlarge_factor * g_instance.cost_cxt.disable_cost_enlarge_factor); +// 如果禁用了路径,则将总成本乘以禁用成本的放大因子 + +/* 将加权成本限制在 1e30 以下以避免 double 溢出 */ +double weight = u_sess->attr.attr_sql.cost_weight_index; +path->path.startup_cost = (1e30f / weight > path->path.startup_cost) ? (path->path.startup_cost * weight) : (1e30); +path->path.total_cost = (1e30f / weight > path->path.total_cost) ? (path->path.total_cost * weight) : (1e30); +// 将启动成本和总成本与加权值相乘,以限制它们在 1e30 以下,避免 double 溢出 + +ereport(DEBUG2, + (errmodule(MOD_OPT), + errmsg("IndexScan Cost startup_cost: %lf, total_cost: %lf, pages_fetched: %lf", + path->path.startup_cost, path->path.total_cost, pages_fetched))); +// 输出调试信息,显示最终的索引扫描成本 /* * index_pages_fetched @@ -1482,28 +1483,33 @@ double index_pages_fetched( * not completely clear, and detecting duplicates is difficult, so ignore it * for now. */ +// 递归计算 Bitmap Index Path 中的页数 static double get_indexpath_pages(Path* bitmapqual) { double result = 0; ListCell* l = NULL; if (IsA(bitmapqual, BitmapAndPath)) { + // 如果是 BitmapAndPath,对每个子路径进行递归调用 BitmapAndPath* apath = (BitmapAndPath*)bitmapqual; foreach (l, apath->bitmapquals) { result += get_indexpath_pages((Path*)lfirst(l)); } } else if (IsA(bitmapqual, BitmapOrPath)) { + // 如果是 BitmapOrPath,对每个子路径进行递归调用 BitmapOrPath* opath = (BitmapOrPath*)bitmapqual; foreach (l, opath->bitmapquals) { result += get_indexpath_pages((Path*)lfirst(l)); } } else if (IsA(bitmapqual, IndexPath)) { + // 如果是 IndexPath,直接获取其关联的索引的页数 IndexPath* ipath = (IndexPath*)bitmapqual; result = (double)ipath->indexinfo->pages; } else { + // 如果不是已知的类型,抛出错误 ereport(ERROR, (errmodule(MOD_OPT), errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE), @@ -1512,7 +1518,6 @@ static double get_indexpath_pages(Path* bitmapqual) return result; } - /* * cost_bitmap_heap_scan * Determines and returns the cost of scanning a relation using a bitmap @@ -1527,6 +1532,7 @@ static double get_indexpath_pages(Path* bitmapqual) * Note: the component IndexPaths in bitmapqual should have been costed * using the same loop_count. */ +// 计算 Bitmap Index Scan 的成本 void cost_bitmap_heap_scan( Path* path, PlannerInfo* root, RelOptInfo* baserel, ParamPathInfo* param_info, Path* bitmapqual, double loop_count) { @@ -1541,11 +1547,12 @@ void cost_bitmap_heap_scan( double pages_fetched; double spc_seq_page_cost, spc_random_page_cost; double T; + // 禁用 BitmapScan 或启用参数化路径将禁用该路径 bool disable_path = (!u_sess->attr.attr_sql.enable_bitmapscan) || enable_parametrized_path(root, baserel, (Path*)path); bool canCrossBucket = (baserel->bucketInfo == NULL); - /* Should only be applied to base relations */ + // 应该仅应用于基本关系 AssertEreport(IsA(baserel, RelOptInfo), MOD_OPT, "The nodeTag of baserel is not T_RelOptInfo" @@ -1664,31 +1671,40 @@ void cost_bitmap_heap_scan( * rechecked always. This means we charge the full freight for all the * scan clauses. */ - get_restriction_qual_cost(root, baserel, param_info, &qpqual_cost); + // 计算限制条件的成本 +get_restriction_qual_cost(root, baserel, param_info, &qpqual_cost); - startup_cost += qpqual_cost.startup; - cpu_per_tuple = u_sess->attr.attr_sql.cpu_tuple_cost + qpqual_cost.per_tuple; +// 将启动成本增加限制条件的启动成本 +startup_cost += qpqual_cost.startup; - run_cost += cpu_per_tuple * tuples_fetched; - ereport(DEBUG2, - (errmodule(MOD_OPT), - errmsg("Computing IndexScanCost: startupCost: %lf, runCost: %lf", - startup_cost, run_cost))); +// 计算每个元组的 CPU 成本并添加到运行成本中 +cpu_per_tuple = u_sess->attr.attr_sql.cpu_tuple_cost + qpqual_cost.per_tuple; - path->startup_cost = startup_cost; - path->total_cost = startup_cost + run_cost; - path->stream_cost = 0; +// 计算运行成本并添加到总成本中 +run_cost += cpu_per_tuple * tuples_fetched; - if (disable_path) { - path->total_cost *= - (g_instance.cost_cxt.disable_cost_enlarge_factor * g_instance.cost_cxt.disable_cost_enlarge_factor); - } +// 输出调试信息 +ereport(DEBUG2, + (errmodule(MOD_OPT), + errmsg("Computing IndexScanCost: startupCost: %lf, runCost: %lf", + startup_cost, run_cost))); + +// 设置路径的启动成本、总成本和流成本 +path->startup_cost = startup_cost; +path->total_cost = startup_cost + run_cost; +path->stream_cost = 0; + +// 如果禁用路径,则应用额外的成本因子 +if (disable_path) { + path->total_cost *= + (g_instance.cost_cxt.disable_cost_enlarge_factor * g_instance.cost_cxt.disable_cost_enlarge_factor); } /* * cost_bitmap_tree_node * Extract cost and selectivity from a bitmap tree node (index/and/or) */ + // 计算 Bitmap 树节点的成本和选择性 void cost_bitmap_tree_node(Path* path, Cost* cost, Selectivity* selec) { if (IsA(path, IndexPath)) { @@ -1701,6 +1717,7 @@ void cost_bitmap_tree_node(Path* path, Cost* cost, Selectivity* selec) * scan doesn't look to be the same cost as an indexscan to retrieve a * single tuple. */ + // 基于本地行数的运算符成本的调整 *cost += 0.1 * u_sess->attr.attr_sql.cpu_operator_cost * PATH_LOCAL_ROWS(path); } else if (IsA(path, BitmapAndPath)) { *cost = path->total_cost; @@ -1709,12 +1726,15 @@ void cost_bitmap_tree_node(Path* path, Cost* cost, Selectivity* selec) *cost = path->total_cost; *selec = ((BitmapOrPath*)path)->bitmapselectivity; } else { + // 如果是未知类型的节点,抛出错误 ereport(ERROR, (errmodule(MOD_OPT), errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE), errmsg("unrecognized node type when extract cost and selectivity from a bitmap tree node: %d", nodeTag(path)))); - *cost = *selec = 0; /* keep compiler quiet */ + + // 将 cost 和 selec 设置为默认值以避免编译器警告 + *cost = *selec = 0; } } @@ -1730,8 +1750,8 @@ void cost_bitmap_tree_node(Path* path, Cost* cost, Selectivity* selec) */ void cost_bitmap_and_node(BitmapAndPath* path, PlannerInfo* root) { - Cost totalCost; - Selectivity selec; + Cost totalCost;//总成本 + Selectivity selec;//选择性 ListCell* l = NULL; /* @@ -1743,23 +1763,35 @@ void cost_bitmap_and_node(BitmapAndPath* path, PlannerInfo* root) * cpu_operator_cost for each tbm_intersect needed. Probably too small, * definitely too simplistic? */ - totalCost = 0.0; - selec = 1.0; - foreach (l, path->bitmapquals) { - Path* subpath = (Path*)lfirst(l); - Cost subCost; - Selectivity subselec; + totalCost = 0.0; // 初始化总成本为0 + selec = 1.0; // 初始化选择性为1 + foreach (l, path->bitmapquals) { + Path* subpath = (Path*)lfirst(l); // 获取子路径 + Cost subCost; // 子路径的成本 + Selectivity subselec; // 子路径的选择性 + + // 计算子路径的成本和选择性 cost_bitmap_tree_node(subpath, &subCost, &subselec); + // 计算总选择性,将每个子路径的选择性相乘 selec *= subselec; + // 累加子路径的成本到总成本中 totalCost += subCost; + + // 如果不是子路径列表的第一个路径,添加额外的运算符成本 if (l != list_head(path->bitmapquals)) totalCost += 100.0 * u_sess->attr.attr_sql.cpu_operator_cost; } + + // 设置 BitmapAndPath 的选择性 path->bitmapselectivity = selec; - set_path_rows(&path->path, 0); /* per above, not used */ + + // 设置路径的行数为0(根据上述代码,这个值不会被使用) + set_path_rows(&path->path, 0); + + // 设置路径的启动成本、总成本和流成本 path->path.startup_cost = totalCost; path->path.total_cost = totalCost; path->path.stream_cost = 0; @@ -1787,23 +1819,35 @@ void cost_bitmap_or_node(BitmapOrPath* path, PlannerInfo* root) * definitely too simplistic? We are aware that the tbm_unions are * optimized out when the inputs are BitmapIndexScans. */ - totalCost = 0.0; - selec = 0.0; - foreach (l, path->bitmapquals) { - Path* subpath = (Path*)lfirst(l); - Cost subCost; - Selectivity subselec; + totalCost = 0.0; // 初始化总成本为0 + selec = 0.0; // 初始化选择性为0 + foreach (l, path->bitmapquals) { + Path* subpath = (Path*)lfirst(l); // 获取子路径 + Cost subCost; // 子路径的成本 + Selectivity subselec; // 子路径的选择性 + + // 计算子路径的成本和选择性 cost_bitmap_tree_node(subpath, &subCost, &subselec); + // 累加子路径的选择性 selec += subselec; + // 累加子路径的成本到总成本中 totalCost += subCost; + + // 如果不是子路径列表的第一个路径,并且子路径不是 IndexPath,则添加额外的运算符成本 if (l != list_head(path->bitmapquals) && !IsA(subpath, IndexPath)) totalCost += 100.0 * u_sess->attr.attr_sql.cpu_operator_cost; } + + // 设置 BitmapOrPath 的选择性为累加的选择性和1的最小值 path->bitmapselectivity = Min(selec, 1.0); - set_path_rows(&path->path, 0); /* per above, not used */ + + // 设置路径的行数为0(根据上述代码,这个值不会被使用) + set_path_rows(&path->path, 0); + + // 设置路径的启动成本、总成本和流成本 path->path.startup_cost = totalCost; path->path.total_cost = totalCost; path->path.stream_cost = 0; @@ -1815,45 +1859,47 @@ void cost_bitmap_or_node(BitmapOrPath* path, PlannerInfo* root) */ void cost_tidscan(Path* path, PlannerInfo* root, RelOptInfo* baserel, List* tidquals) { - Cost startup_cost = 0; - Cost run_cost = 0; - bool isCurrentOf = false; - Cost cpu_per_tuple = 0.0; - QualCost tid_qual_cost; - int ntuples; - ListCell* l = NULL; - double spc_random_page_cost; + Cost startup_cost = 0; // 初始化启动成本为0 + Cost run_cost = 0; // 初始化运行成本为0 + bool isCurrentOf = false; // 是否是 CurrentOf 表达式的标志 + Cost cpu_per_tuple = 0.0; // 每个元组的 CPU 成本 + QualCost tid_qual_cost; // TID 过滤条件的成本 + int ntuples; // 元组数目 + ListCell* l = NULL; // 列表迭代器 + double spc_random_page_cost; // 随机磁盘访问成本 - /* Should only be applied to base relations */ + // 断言:确保 baserel 的 relid 和 rtekind 符合条件 AssertEreport(baserel->relid > 0, MOD_OPT, "The relid is invalid when determining the cost of scanning a relation using TIDs."); AssertEreport(baserel->rtekind == RTE_RELATION, MOD_OPT, - "Only base relation can be supported" - "when determining the cost of scanning a relation using TIDs."); + "Only base relation can be supported when determining the cost of scanning a relation using TIDs."); - /* For now, tidscans are never parameterized */ + // 设置路径的行数估计 set_rel_path_rows(path, baserel, NULL); - /* Count how many tuples we expect to retrieve */ + // 初始化元组数目为0 ntuples = 0; + + // 遍历 TID 过滤条件列表 foreach (l, tidquals) { if (IsA(lfirst(l), ScalarArrayOpExpr)) { - /* Each element of the array yields 1 tuple */ + // 如果是 ScalarArrayOpExpr,表示有数组操作,估算数组长度并累加到元组数目 ScalarArrayOpExpr* saop = (ScalarArrayOpExpr*)lfirst(l); Node* arraynode = (Node*)lsecond(saop->args); ntuples += estimate_array_length(arraynode); } else if (IsA(lfirst(l), CurrentOfExpr)) { - /* CURRENT OF yields 1 tuple */ + // 如果是 CurrentOfExpr,表示是当前游标的操作,设置 isCurrentOf 为 true,并累加元组数目 isCurrentOf = true; ntuples++; } else { - /* It's just CTID = something, count 1 tuple */ + // 其他情况,累加元组数目 ntuples++; } } +} /* * We must force TID scan for WHERE CURRENT OF, because only nodeTidscan.c @@ -1929,49 +1975,55 @@ void cost_subqueryscan(Path* path, PlannerInfo* root, RelOptInfo* baserel, Param * any restriction clauses that will be attached to the SubqueryScan node, * plus cpu_tuple_cost to account for selection and projection overhead. */ - path->startup_cost = baserel->subplan->startup_cost; - path->total_cost = baserel->subplan->total_cost; + path->startup_cost = baserel->subplan->startup_cost; // 设置路径的启动成本为基本关系子计划的启动成本 +path->total_cost = baserel->subplan->total_cost; // 设置路径的总成本为基本关系子计划的总成本 - get_restriction_qual_cost(root, baserel, param_info, &qpqual_cost); +get_restriction_qual_cost(root, baserel, param_info, &qpqual_cost); // 计算约束条件的成本并存储在 qpqual_cost 中 - startup_cost = qpqual_cost.startup; - cpu_per_tuple = u_sess->attr.attr_sql.cpu_tuple_cost + qpqual_cost.per_tuple; - run_cost = cpu_per_tuple * RELOPTINFO_LOCAL_FIELD(root, baserel, tuples); +startup_cost = qpqual_cost.startup; // 获取约束条件的启动成本 +cpu_per_tuple = u_sess->attr.attr_sql.cpu_tuple_cost + qpqual_cost.per_tuple; // 计算每个元组的 CPU 成本 +run_cost = cpu_per_tuple * RELOPTINFO_LOCAL_FIELD(root, baserel, tuples); // 计算运行成本 + +path->startup_cost += startup_cost; // 将约束条件的启动成本添加到路径的启动成本上 +path->total_cost += startup_cost + run_cost; // 计算并设置路径的总成本,包括启动成本和运行成本 +path->stream_cost = get_subqueryscan_stream_cost(baserel->subplan); // 获取子查询扫描的流成本 +ereport(DEBUG2, + (errmodule(MOD_OPT_SUBPLAN), + errmsg("subqueryscan stream_cost: %lf, startup_cost: %lf, total_cost: %lf", + path->stream_cost, + path->startup_cost, + path->total_cost))); // 输出调试信息,包括流成本、启动成本和总成本 - path->startup_cost += startup_cost; - path->total_cost += startup_cost + run_cost; - path->stream_cost = get_subqueryscan_stream_cost(baserel->subplan); - ereport(DEBUG2, - (errmodule(MOD_OPT_SUBPLAN), - errmsg("subqueryscan stream_cost: %lf, startup_cost: %lf, total_cost: %lf", - path->stream_cost, - path->startup_cost, - path->total_cost))); } /* * cost_functionscan * Determines and returns the cost of scanning a function RTE. */ -void cost_functionscan(Path* path, PlannerInfo* root, RelOptInfo* baserel) -{ - Cost startup_cost = 0; - Cost run_cost = 0; - Cost cpu_per_tuple = 0.0; - RangeTblEntry* rte = NULL; - QualCost exprcost; +void cost_functionscan(Path* path, PlannerInfo* root, RelOptInfo* baserel) { + Cost startup_cost = 0; // 初始化启动成本为0 + Cost run_cost = 0; // 初始化运行成本为0 + Cost cpu_per_tuple = 0.0; // 初始化每个元组的CPU成本为0.0 + RangeTblEntry* rte = NULL; // 初始化RangeTblEntry指针为NULL + QualCost exprcost; // 声明QualCost结构体变量exprcost,用于存储表达式的成本信息 /* Should only be applied to base relations that are functions */ AssertEreport( - baserel->relid > 0, MOD_OPT, "The relid is invalid when determining the cost of scanning a function RTE."); - rte = planner_rt_fetch(baserel->relid, root); + baserel->relid > 0, MOD_OPT, "The relid is invalid when determining the cost of scanning a function RTE."); + // 断言,确保只应用于基本关系,并且这些关系是函数 + + rte = planner_rt_fetch(baserel->relid, root); + // 从PlannerInfo的rangetable中获取RangeTblEntry,用于描述函数的关系信息 AssertEreport(rte->rtekind == RTE_FUNCTION, MOD_OPT, "Only function in FROM clause can be supported" "when determining the cost of scanning a function RTE."); + // 断言,确保RangeTblEntry的类型为RTE_FUNCTION,即FROM子句中的函数 /* functionscans are never parameterized */ - set_rel_path_rows(path, baserel, NULL); + set_rel_path_rows(path, baserel, NULL); + // 设置路径的行数估计,因为函数扫描永远不会参数化,所以传入的参数为NULL + /* * Estimate costs of executing the function expression. @@ -1986,19 +2038,29 @@ void cost_functionscan(Path* path, PlannerInfo* root, RelOptInfo* baserel) * estimates for functions tend to be, there's not a lot of point in that * refinement right now. */ - cost_qual_eval_node(&exprcost, rte->funcexpr, root); + cost_qual_eval_node(&exprcost, rte->funcexpr, root); +// 计算函数扫描中函数表达式的成本,将结果存储在exprcost中 - startup_cost += exprcost.startup + exprcost.per_tuple; +startup_cost += exprcost.startup + exprcost.per_tuple; +// 将函数表达式的启动成本和每个元组的成本添加到启动成本中 - /* Add scanning CPU costs */ - startup_cost += baserel->baserestrictcost.startup; - cpu_per_tuple = u_sess->attr.attr_sql.cpu_tuple_cost + baserel->baserestrictcost.per_tuple; - run_cost += cpu_per_tuple * RELOPTINFO_LOCAL_FIELD(root, baserel, tuples); +startup_cost += baserel->baserestrictcost.startup; +// 将基本关系的限制条件的启动成本添加到启动成本中 - path->startup_cost = startup_cost; - path->total_cost = startup_cost + run_cost; - path->stream_cost = 0; -} +cpu_per_tuple = u_sess->attr.attr_sql.cpu_tuple_cost + baserel->baserestrictcost.per_tuple; +// 计算每个元组的CPU成本,将其存储在cpu_per_tuple中,包括基本关系的限制条件的每个元组的成本 + +run_cost += cpu_per_tuple * RELOPTINFO_LOCAL_FIELD(root, baserel, tuples); +// 计算运行成本,将其存储在run_cost中 + +path->startup_cost = startup_cost; +// 将启动成本赋值给路径的启动成本属性 + +path->total_cost = startup_cost + run_cost; +// 将启动成本和运行成本相加,赋值给路径的总成本属性 + +path->stream_cost = 0; +// 将流成本设置为0,因为这是函数扫描 /* * cost_valuesscan @@ -2048,32 +2110,38 @@ void cost_valuesscan(Path* path, PlannerInfo* root, RelOptInfo* baserel) */ void cost_ctescan(Path* path, PlannerInfo* root, RelOptInfo* baserel) { - Cost startup_cost = 0; - Cost run_cost = 0; - Cost cpu_per_tuple = 0.0; + Cost startup_cost = 0; // 初始化启动成本为0 + Cost run_cost = 0; // 初始化运行成本为0 + Cost cpu_per_tuple = 0.0; // 初始化每个元组的CPU成本为0.0 - /* Should only be applied to base relations that are CTEs */ - AssertEreport(baserel->relid > 0, MOD_OPT, "The relid is invalid when determining the cost of scanning a CTE RTE."); + // 确保只应用于作为CTE的基本关系 + AssertEreport(baserel->relid > 0, MOD_OPT, "在确定扫描CTE RTE的成本时,relid无效。"); AssertEreport(baserel->rtekind == RTE_CTE, MOD_OPT, - "Only common table expr can be supported when determining the cost of scanning a CTE RTE."); + "仅当确定扫描CTE RTE的成本时,才能支持通用表达式。"); - /* ctescans are never parameterized */ + // ctescans 永远不会被参数化,设置路径的行数属性 set_rel_path_rows(path, baserel, NULL); - /* Charge one CPU tuple cost per row for tuplestore manipulation */ + // 为元组操作设置初始CPU成本 cpu_per_tuple = u_sess->attr.attr_sql.cpu_tuple_cost; - /* Add scanning CPU costs */ + // 添加启动CPU成本和每个元组的CPU成本 startup_cost += baserel->baserestrictcost.startup; cpu_per_tuple += u_sess->attr.attr_sql.cpu_tuple_cost + baserel->baserestrictcost.per_tuple; + + // 计算运行成本 run_cost += cpu_per_tuple * RELOPTINFO_LOCAL_FIELD(root, baserel, tuples); + // 将计算得到的成本设置为路径的启动成本和总成本 path->startup_cost = startup_cost; path->total_cost = startup_cost + run_cost; + + // 由于CTE扫描不涉及流操作,将流成本设置为0 path->stream_cost = 0; } + /* * cost_recursive_union * Determines and returns the cost of performing a recursive union, @@ -2085,18 +2153,22 @@ void cost_ctescan(Path* path, PlannerInfo* root, RelOptInfo* baserel) * the rest of this module. That's because we don't bother setting up a * Path representation for recursive union --- we have only one way to do it. */ + // 此函数计算两个计划的成本,并将结果存储在一个计划中 void cost_recursive_union(Plan* runion, Plan* nrterm, Plan* rterm) { + // 声明启动成本、总成本、总行数、总全局行数变量 Cost startup_cost; Cost total_cost; double total_rows; double total_global_rows; /* We probably have decent estimates for the non-recursive term */ + // 获取非递归项的启动成本、总成本、本地行数 startup_cost = nrterm->startup_cost; total_cost = nrterm->total_cost; total_rows = PLAN_LOCAL_ROWS(nrterm); total_global_rows = nrterm->plan_rows; + // 获取非递归项的全局行数,然后将递归项的总成本、本地行数、全局行数加到非递归项上 /* * We arbitrarily assume that about 10 recursive iterations will be @@ -2113,10 +2185,14 @@ void cost_recursive_union(Plan* runion, Plan* nrterm, Plan* rterm) * manipulating the tuplestores. (We don't worry about possible * spill-to-disk costs.) */ + // 计算总成本,考虑 CPU tuple 成本,并将结果存储在总计划中 total_cost += u_sess->attr.attr_sql.cpu_tuple_cost * total_rows; + // 设置递归联合计划的启动成本和总成本 runion->startup_cost = startup_cost; runion->total_cost = total_cost; + + // 设置递归联合计划的总全局行数,考虑非递归项的多重度,并设置计划的宽度 set_plan_rows(runion, total_global_rows, nrterm->multiple); runion->plan_width = Max(nrterm->plan_width, rterm->plan_width); } @@ -2167,9 +2243,11 @@ void cost_recursive_union(Plan* runion, Plan* nrterm, Plan* rterm) * (Actually, the thing we'd most likely be interested in is just the number * of sort keys, which all callers *could* supply.) */ +// 此函数计算排序操作的成本 void cost_sort(Path* path, List* pathkeys, Cost input_cost, double tuples, int width, Cost comparison_cost, int sort_mem, double limit_tuples, bool col_store, int dop, OpMemInfo* mem_info, bool index_sort) { + // 初始化启动成本为输入成本 Cost startup_cost = input_cost; Cost run_cost = 0; double input_bytes = relation_byte_size(tuples, width, col_store, true, true, index_sort) / SET_DOP(dop); @@ -2177,8 +2255,10 @@ void cost_sort(Path* path, List* pathkeys, Cost input_cost, double tuples, int w double output_tuples; long sort_mem_bytes = sort_mem * 1024L / SET_DOP(dop); + // 设置并更新并行度 dop = SET_DOP(dop); + // 如果禁用了排序优化,增加禁用成本 if (!u_sess->attr.attr_sql.enable_sort) startup_cost += g_instance.cost_cxt.disable_cost; @@ -2186,14 +2266,15 @@ void cost_sort(Path* path, List* pathkeys, Cost input_cost, double tuples, int w * We want to be sure the cost of a sort is never estimated as zero, even * if passed-in tuple count is zero. Besides, mustn't do log(0)... */ + // 如果输入元组数小于2.0,将其设置为2.0 if (tuples < 2.0) { tuples = 2.0; } - /* Include the default cost-per-comparison */ + // 增加比较操作的成本 comparison_cost += 2.0 * u_sess->attr.attr_sql.cpu_operator_cost; - /* Do we have a useful LIMIT? */ + // 计算输出元组数和输出字节大小 if (limit_tuples > 0 && limit_tuples < tuples) { output_tuples = limit_tuples; output_bytes = relation_byte_size(output_tuples, width, col_store, true, true, index_sort); @@ -2202,42 +2283,32 @@ void cost_sort(Path* path, List* pathkeys, Cost input_cost, double tuples, int w output_bytes = input_bytes; } + // 根据输出字节大小和排序内存字节数计算启动成本 if (output_bytes > sort_mem_bytes) { - /* - * CPU costs - * - * Assume about N log2 N comparisons - */ startup_cost += comparison_cost * tuples * LOG2(tuples); - - /* Disk costs */ startup_cost += compute_sort_disk_cost(input_bytes, sort_mem_bytes); } else { if (tuples > 2 * output_tuples || input_bytes > sort_mem_bytes) { - /* - * We'll use a bounded heap-sort keeping just K tuples in memory, for - * a total number of tuple comparisons of N log2 K; but the constant - * factor is a bit higher than for quicksort. Tweak it so that the - * cost curve is continuous at the crossover point. - */ startup_cost += comparison_cost * tuples * LOG2(2.0 * output_tuples); } else { - /* We'll use plain quicksort on all the input tuples */ startup_cost += comparison_cost * tuples * LOG2(tuples); } } + // 如果提供了内存信息,设置操作内存信息 if (mem_info != NULL) { mem_info->opMem = u_sess->opt_cxt.op_work_mem; mem_info->maxMem = output_bytes / 1024L * dop; mem_info->minMem = mem_info->maxMem / SORT_MAX_DISK_SIZE; mem_info->regressCost = compute_sort_disk_cost(input_bytes, mem_info->minMem); - /* Special case if array larger than 1G, so we must spill to disk */ + + // 如果输出元组数大于允许的最大内存使用,则调整内存上限 if (output_tuples > (MaxAllocSize / TUPLE_OVERHEAD(true) * dop)) { mem_info->maxMem = STATEMENT_MIN_MEM * 1024L * dop; mem_info->minMem = Min(mem_info->maxMem, mem_info->minMem); } } +} /* * Also charge a small amount (arbitrarily set equal to operator cost) per @@ -2247,15 +2318,17 @@ void cost_sort(Path* path, List* pathkeys, Cost input_cost, double tuples, int w * here --- the upper LIMIT will pro-rate the run cost so we'd be double * counting the LIMIT otherwise. */ - run_cost += u_sess->attr.attr_sql.cpu_operator_cost * tuples; + // 增加运行成本,考虑CPU操作成本和元组数 +run_cost += u_sess->attr.attr_sql.cpu_operator_cost * tuples; - path->startup_cost = startup_cost; - path->total_cost = startup_cost + run_cost; - path->stream_cost = 0; +// 设置计划的启动成本和总成本,同时初始化流成本为0 +path->startup_cost = startup_cost; +path->total_cost = startup_cost + run_cost; +path->stream_cost = 0; - if (!u_sess->attr.attr_sql.enable_sort) - path->total_cost *= - (g_instance.cost_cxt.disable_cost_enlarge_factor * g_instance.cost_cxt.disable_cost_enlarge_factor); +// 如果禁用了排序优化,将总成本乘以禁用成本的扩大因子的平方 +if (!u_sess->attr.attr_sql.enable_sort) + path->total_cost *= (g_instance.cost_cxt.disable_cost_enlarge_factor * g_instance.cost_cxt.disable_cost_enlarge_factor); } /* @@ -2268,25 +2341,24 @@ void cost_sort(Path* path, List* pathkeys, Cost input_cost, double tuples, int w * * Returns: estimated disk cost */ +// 计算排序磁盘成本的函数 double compute_sort_disk_cost(double input_bytes, double sort_mem_bytes) { - /* - * We'll have to use a disk-based sort of all the tuples - */ double npages = ceil(input_bytes / BLCKSZ); double nruns = (input_bytes / sort_mem_bytes) * 0.5; double mergeorder = tuplesort_merge_order(sort_mem_bytes); double log_runs; double npageaccesses; - /* Compute logM(r) as log(r) / log(M) */ + // 根据运行次数和合并顺序计算对数值 if (nruns > mergeorder) { log_runs = ceil(log(nruns) / log(mergeorder)); } else { log_runs = 1.0; } npageaccesses = 2.0 * npages * log_runs; - /* Assume 3/4ths of accesses are sequential, 1/4th are not */ + + // 返回排序磁盘成本,考虑顺序页成本和随机页成本 return npageaccesses * (u_sess->attr.attr_sql.seq_page_cost * 0.75 + u_sess->attr.attr_sql.random_page_cost * 0.25); } @@ -2363,12 +2435,14 @@ void cost_merge_append(Path* path, PlannerInfo* root, List* pathkeys, int n_stre * relation, so the materialization is all overhead --- any savings will * occur only on rescan, which is estimated in cost_rescan. */ + // 计算材料化成本的函数 void cost_material(Path* path, Cost input_startup_cost, Cost input_total_cost, double tuples, int width) { Cost startup_cost = input_startup_cost; Cost run_cost = input_total_cost - input_startup_cost; int dop = SET_DOP(path->dop); if (dop > 1) { + // 如果并行度大于1,将元组数除以并行度 tuples = tuples / dop; } @@ -2395,12 +2469,13 @@ void cost_material(Path* path, Cost input_startup_cost, Cost input_total_cost, d * which isn't exactly accurate but our cost model doesn't allow for * nonuniform costs within the run phase. */ + // 如果字节数大于工作内存字节数,增加顺序页成本 if (nbytes > work_mem_bytes) { double npages = ceil(nbytes / BLCKSZ); run_cost += u_sess->attr.attr_sql.seq_page_cost * npages; } - + // 设置计划的启动成本和总成本 path->startup_cost = startup_cost; path->total_cost = startup_cost + run_cost; } @@ -2409,28 +2484,27 @@ void cost_material(Path* path, Cost input_startup_cost, Cost input_total_cost, d * Decide sonic hashagg routine or not. * Similar with the same function in vsonichashagg.cpp */ +// 检查是否启用 Sonic Hash Aggregation 计划的函数 bool isSonicHashAggPlanEnable(PlannerInfo* root, AggStrategy aggstrategy, int numGroupCols) { + // 如果未启用矢量化,不使用哈希聚合,或未启用 Sonic Hash Aggregation,则返回 false if (!(root->glob->vectorized) || aggstrategy != AGG_HASHED || !u_sess->attr.attr_sql.enable_sonic_hashagg) return false; - /* - * Get the target list. - * Including all the corresponding columns in group by clause, whether or not it appears in target list. - */ + // 获取目标列表和分组子句 List* tleList = root->parse->targetList; - - /* Get hash key in group by clause. */ List* sgClauses = root->parse->groupClause; ListCell* lc = NULL; - Oid hashtype = 0; + + // 遍历分组子句,检查哈希键的数据类型 foreach (lc, sgClauses) { SortGroupClause* sortcl = (SortGroupClause*)lfirst(lc); TargetEntry* tle = get_sortgroupclause_tle(sortcl, tleList, false); if (tle == NULL) return false; + // 根据表达式的类型判断哈希键的数据类型 switch (nodeTag(tle->expr)) { case T_Var: { Var* var = (Var*)(tle->expr); @@ -2444,6 +2518,7 @@ bool isSonicHashAggPlanEnable(PlannerInfo* root, AggStrategy aggstrategy, int nu break; } + // 检查哈希键的数据类型是否支持 Sonic Hash Aggregation switch (hashtype) { case CHAROID: case BPCHAROID: @@ -2463,41 +2538,58 @@ bool isSonicHashAggPlanEnable(PlannerInfo* root, AggStrategy aggstrategy, int nu } } + + + + + /* * Aggregate function only support sum(), avg() function for int4, int8 and numeric tyep. * Loop over all the targetlist and check aggref. */ - foreach (lc, tleList) { - TargetEntry* tre = (TargetEntry*)lfirst(lc); - switch (nodeTag(tre->expr)) { - case T_Aggref: { - Aggref* aggref = (Aggref*)tre->expr; + // 遍历目标列表中的每个目标条目 +foreach (lc, tleList) { + TargetEntry* tre = (TargetEntry*)lfirst(lc); - if (!isAggrefSonicEnable(aggref->aggfnoid)) - return false; + // 根据目标条目的表达式类型进行不同的处理 + switch (nodeTag(tre->expr)) { + case T_Aggref: { + Aggref* aggref = (Aggref*)tre->expr; - /* count(*) has no args */ - if (aggref->aggfnoid == COUNTOID || aggref->aggfnoid == ANYCOUNTOID) - continue; - - Expr* refexpr = (Expr*)linitial(aggref->args); - /* We only support simple expression cases */ - if (!isExprSonicEnable(refexpr)) - return false; - } break; - case T_FuncExpr: { + // 检查聚合函数是否启用 Sonic Hash Aggregation + if (!isAggrefSonicEnable(aggref->aggfnoid)) return false; - } - case T_Var: - case T_Const: - break; - default: + + // 对于没有参数的 count(*) 聚合函数,跳过检查 + if (aggref->aggfnoid == COUNTOID || aggref->aggfnoid == ANYCOUNTOID) + continue; + + // 获取聚合函数的参数表达式 + Expr* refexpr = (Expr*)linitial(aggref->args); + + // 检查参数表达式是否启用 Sonic Hash Aggregation + // 只支持简单表达式的情况 + if (!isExprSonicEnable(refexpr)) return false; + } break; + case T_FuncExpr: { + // 如果表达式是函数表达式,则返回 false + return false; } + case T_Var: + case T_Const: + // 对于变量和常量表达式,不执行任何操作 + break; + default: + // 如果表达式类型不支持 Sonic Hash Aggregation,则返回 false + return false; } - return true; } +// 如果所有表达式都支持 Sonic Hash Aggregation,则返回 true +return true; + + /* * For operator HashAgg, compute appropriate size for hashtable given the estimated size of the * columns to be hashed (number of rows). @@ -2523,6 +2615,7 @@ double estimate_hashagg_size(Path* path, PlannerInfo* root, AggStrategy aggstrat * Note: when aggstrategy == AGG_SORTED, caller must ensure that input costs * are for appropriately-sorted input. */ +// 此函数计算聚合操作的成本 void cost_agg(Path* path, PlannerInfo* root, AggStrategy aggstrategy, const AggClauseCosts* aggcosts, int numGroupCols, double numGroups, Cost input_startup_cost, Cost input_total_cost, double input_tuples, int input_width, int hash_entry_size, int dop, OpMemInfo* mem_info) @@ -2533,21 +2626,28 @@ void cost_agg(Path* path, PlannerInfo* root, AggStrategy aggstrategy, const AggC AggClauseCosts dummy_aggcosts; double hllagg_size = 0; - /* Use all-zero per-aggregate costs if NULL is passed */ + // 如果传入的 aggcosts 为 NULL,则使用零成本的占位值 if (aggcosts == NULL) { + // 仅在使用 Hashed 聚合策略时使用零成本的占位值 AssertEreport(aggstrategy == AGG_HASHED, MOD_OPT, "Only support Hashed aggstrategy" "when determining the cost of performing an Agg plan node."); + // 初始化一个全零的占位值 errno_t errorno = EOK; errorno = memset_s(&dummy_aggcosts, sizeof(AggClauseCosts), 0, sizeof(AggClauseCosts)); securec_check(errorno, "\0", "\0"); aggcosts = &dummy_aggcosts; } + // 估算 HyperLogLog 聚合的大小 hllagg_size = estimate_hllagg_size(numGroups, root->parse->targetList); + + // 设置并更新并行度 dop = SET_DOP(dop); +} + /* * The transCost.per_tuple component of aggcosts should be charged once * per input tuple, corresponding to the costs of evaluating the aggregate @@ -2570,76 +2670,80 @@ void cost_agg(Path* path, PlannerInfo* root, AggStrategy aggstrategy, const AggC * the computations below form the same intermediate values in the same * order. */ - if (aggstrategy == AGG_PLAIN) { - startup_cost = input_total_cost; - startup_cost += aggcosts->transCost.startup; - startup_cost += aggcosts->transCost.per_tuple * input_tuples; - startup_cost += aggcosts->finalCost; - /* we aren't grouping */ - total_cost = startup_cost + u_sess->attr.attr_sql.cpu_tuple_cost; - output_tuples = 1; - } else if (aggstrategy == AGG_SORTED) { - /* Here we are able to deliver output on-the-fly */ - startup_cost = input_startup_cost; - total_cost = input_total_cost; - /* calcs phrased this way to match HASHED case, see note above */ - total_cost += aggcosts->transCost.startup; - total_cost += aggcosts->transCost.per_tuple * input_tuples; - total_cost += (u_sess->attr.attr_sql.cpu_operator_cost * numGroupCols) * input_tuples; - total_cost += aggcosts->finalCost * numGroups; - total_cost += u_sess->attr.attr_sql.cpu_tuple_cost * numGroups; - output_tuples = numGroups; - } else { - bool spill_disk = false; - double hash_table_size; + // 根据聚合策略计算聚合操作的成本 +if (aggstrategy == AGG_PLAIN) { + // 普通聚合策略,无需分组 + startup_cost = input_total_cost; + startup_cost += aggcosts->transCost.startup; + startup_cost += aggcosts->transCost.per_tuple * input_tuples; + startup_cost += aggcosts->finalCost; + total_cost = startup_cost + u_sess->attr.attr_sql.cpu_tuple_cost; + output_tuples = 1; +} else if (aggstrategy == AGG_SORTED) { + // 排序聚合策略,可即时产生输出 + startup_cost = input_startup_cost; + total_cost = input_total_cost; + total_cost += aggcosts->transCost.startup; + total_cost += aggcosts->transCost.per_tuple * input_tuples; + total_cost += (u_sess->attr.attr_sql.cpu_operator_cost * numGroupCols) * input_tuples; + total_cost += aggcosts->finalCost * numGroups; + total_cost += u_sess->attr.attr_sql.cpu_tuple_cost * numGroups; + output_tuples = numGroups; +} else { + // 哈希聚合策略 + bool spill_disk = false; + double hash_table_size; - /* must be AGG_HASHED */ - startup_cost = input_total_cost; - startup_cost += aggcosts->transCost.startup; - startup_cost += aggcosts->transCost.per_tuple * input_tuples / dop; - startup_cost += (u_sess->attr.attr_sql.cpu_operator_cost * numGroupCols) * input_tuples / dop; - total_cost = startup_cost; - total_cost += aggcosts->finalCost * numGroups / dop; - total_cost += u_sess->attr.attr_sql.cpu_tuple_cost * numGroups / dop; - output_tuples = numGroups; + startup_cost = input_total_cost; + startup_cost += aggcosts->transCost.startup; + startup_cost += aggcosts->transCost.per_tuple * input_tuples / dop; + startup_cost += (u_sess->attr.attr_sql.cpu_operator_cost * numGroupCols) * input_tuples / dop; + total_cost = startup_cost; + total_cost += aggcosts->finalCost * numGroups / dop; + total_cost += u_sess->attr.attr_sql.cpu_tuple_cost * numGroups / dop; + output_tuples = numGroups; - /* Get hash table size and estimate mem_info. */ - hash_table_size = estimate_hashagg_size( - path, root, aggstrategy, numGroupCols, numGroups, input_tuples, input_width, hash_entry_size); + // 估算哈希表大小,并估算内存信息 + hash_table_size = estimate_hashagg_size( + path, root, aggstrategy, numGroupCols, numGroups, input_tuples, input_width, hash_entry_size); - /* one more step estimate for hll */ - hash_table_size += hllagg_size; - if (hash_table_size < 0) - hash_table_size = (double)LONG_MAX; - spill_disk = (hash_table_size > (double)u_sess->opt_cxt.op_work_mem); + // 估算 HyperLogLog 聚合大小并添加到哈希表大小 + hash_table_size += hllagg_size; + if (hash_table_size < 0) + hash_table_size = (double)LONG_MAX; + spill_disk = (hash_table_size > (double)u_sess->opt_cxt.op_work_mem); - if (spill_disk) { - const double disk_ratio = 1 - u_sess->opt_cxt.op_work_mem / hash_table_size; - double disk_pages = ceil(page_size(input_tuples, input_width) * disk_ratio); - double one_disk_io_cost = u_sess->attr.attr_sql.seq_page_cost * disk_pages; - double disk_hash_cost = u_sess->attr.attr_sql.cpu_operator_cost * numGroupCols * input_tuples * disk_ratio; + if (spill_disk) { + // 如果需要溢出到磁盘,计算相关磁盘成本 + const double disk_ratio = 1 - u_sess->opt_cxt.op_work_mem / hash_table_size; + double disk_pages = ceil(page_size(input_tuples, input_width) * disk_ratio); + double one_disk_io_cost = u_sess->attr.attr_sql.seq_page_cost * disk_pages; + double disk_hash_cost = u_sess->attr.attr_sql.cpu_operator_cost * numGroupCols * input_tuples * disk_ratio; - startup_cost += disk_hash_cost + one_disk_io_cost; /* hash, write cost counted */ - total_cost += disk_hash_cost + 2 * one_disk_io_cost; /* hash, write, read cost counted */ - } - if (mem_info != NULL) { - double disk_pages = ceil(page_size(input_tuples, input_width)); - double one_disk_io_cost = u_sess->attr.attr_sql.seq_page_cost * disk_pages; - double disk_hash_cost = u_sess->attr.attr_sql.cpu_operator_cost * numGroupCols * input_tuples; - - mem_info->opMem = u_sess->opt_cxt.op_work_mem; - mem_info->maxMem = hash_table_size; - mem_info->minMem = mem_info->maxMem / HASH_MAX_DISK_SIZE; - mem_info->regressCost = (disk_hash_cost + 2 * one_disk_io_cost); - } + startup_cost += disk_hash_cost + one_disk_io_cost; // 计算哈希和写入成本 + total_cost += disk_hash_cost + 2 * one_disk_io_cost; // 计算哈希、写入和读取成本 } - path->rows = get_global_rows(output_tuples, 1.0, ng_get_dest_num_data_nodes(path)); - path->multiple = 1.0; - path->startup_cost = startup_cost; - path->total_cost = total_cost; + if (mem_info != NULL) { + // 设置内存信息 + double disk_pages = ceil(page_size(input_tuples, input_width)); + double one_disk_io_cost = u_sess->attr.attr_sql.seq_page_cost * disk_pages; + double disk_hash_cost = u_sess->attr.attr_sql.cpu_operator_cost * numGroupCols * input_tuples; + + mem_info->opMem = u_sess->opt_cxt.op_work_mem; + mem_info->maxMem = hash_table_size; + mem_info->minMem = mem_info->maxMem / HASH_MAX_DISK_SIZE; + mem_info->regressCost = (disk_hash_cost + 2 * one_disk_io_cost); + } } +// 设置计划的输出行数、多重度以及启动成本和总成本 +path->rows = get_global_rows(output_tuples, 1.0, ng_get_dest_num_data_nodes(path)); +path->multiple = 1.0; +path->startup_cost = startup_cost; +path->total_cost = total_cost; + + /* * cost_windowagg * Determines and returns the cost of performing a WindowAgg plan node, @@ -2647,6 +2751,7 @@ void cost_agg(Path* path, PlannerInfo* root, AggStrategy aggstrategy, const AggC * * Input is assumed already properly sorted. */ +// 计算窗口聚合操作的成本 void cost_windowagg(Path* path, PlannerInfo* root, List* windowFuncs, int numPartCols, int numOrderCols, Cost input_startup_cost, Cost input_total_cost, double input_tuples) { @@ -2654,6 +2759,7 @@ void cost_windowagg(Path* path, PlannerInfo* root, List* windowFuncs, int numPar Cost total_cost; ListCell* lc = NULL; + // 初始化启动成本和总成本 startup_cost = input_startup_cost; total_cost = input_total_cost; @@ -2666,24 +2772,32 @@ void cost_windowagg(Path* path, PlannerInfo* root, List* windowFuncs, int numPar * any case, it's a good estimate for all the built-in window functions, * so we'll just do this for now. */ + // 遍历窗口函数列表中的每个窗口函数 foreach (lc, windowFuncs) { WindowFunc* wfunc = (WindowFunc*)lfirst(lc); Cost wfunccost; QualCost argcosts; + // 确保窗口函数是正确的类型 AssertEreport(IsA(wfunc, WindowFunc), MOD_OPT, "The nodeTag of wfunc is not T_WindowFunc" "when determining the cost of performing a WindowAgg plan node."); + + // 获取窗口函数的成本,包括函数本身和参数的成本 wfunccost = get_func_cost(wfunc->winfnoid) * u_sess->attr.attr_sql.cpu_operator_cost; - /* also add the input expressions' cost to per-input-row costs */ + // 计算参数的成本并将其添加到启动成本中 cost_qual_eval_node(&argcosts, (Node*)wfunc->args, root); startup_cost += argcosts.startup; wfunccost += argcosts.per_tuple; + // 计算总成本,考虑输入元组数 total_cost += wfunccost * input_tuples; } +} + + /* * We also charge cpu_operator_cost per grouping column per tuple for @@ -2708,32 +2822,35 @@ void cost_windowagg(Path* path, PlannerInfo* root, List* windowFuncs, int numPar * Note: caller must ensure that input costs are for appropriately-sorted * input. */ +// 计算 Group 节点的成本 void cost_group(Path* path, PlannerInfo* root, int numGroupCols, double numGroups, Cost input_startup_cost, Cost input_total_cost, double input_tuples) { Cost startup_cost; Cost total_cost; + // 初始化启动成本和总成本 startup_cost = input_startup_cost; total_cost = input_total_cost; - /* - * Charge one cpu_operator_cost per comparison per input tuple. We assume - * all columns get compared at most of the tuples. - */ + // 计算 CPU 操作成本并考虑输入元组数和分组列数 total_cost += u_sess->attr.attr_sql.cpu_operator_cost * input_tuples * numGroupCols; + // 设置计划的输出行数、多重度以及启动成本和总成本 path->rows = get_global_rows(numGroups, 1.0, ng_get_dest_num_data_nodes(path)); path->multiple = 1.0; path->startup_cost = startup_cost; path->total_cost = total_cost; } +// 调整 LIMIT 子句的行数估计值 double adjust_limit_row_count(double lefttree_rows) { - if (u_sess->attr.attr_sql.default_limit_rows < 0) { /* use percentage adjustment */ + if (u_sess->attr.attr_sql.default_limit_rows < 0) { + // 如果默认限制行数为负数,则根据百分比进行调整 return -(lefttree_rows * u_sess->attr.attr_sql.default_limit_rows / 100); - } else { /* use direct adjustment */ + } else { + // 否则,返回限制行数和左子树行数的较小值 return Min(u_sess->attr.attr_sql.default_limit_rows, lefttree_rows); } } @@ -2745,61 +2862,65 @@ double adjust_limit_row_count(double lefttree_rows) * @param[IN] lefttree: subplan * @return void */ +// 计算 LIMIT 子句的成本 void cost_limit(Plan* plan, Plan* lefttree, int64 offset_est, int64 count_est) { - /* - * Adjust the output rows count and costs according to the offset/limit. - * This is only a cosmetic issue if we are at top level, but if we are - * building a subquery then it's important to report correct info to the - * outer planner. - * - * When the offset or count couldn't be estimated, use default_limit_rows. - * Percentage adjustment is used When default_limit_rows is negative: - * e.g. 10% of the estimated number of rows emitted from the subplan is - * used if default_limit_rows is -0.1. - * Direct adjustment when positive: - * e.g. 100 is used if default_limit_rows is 100. - */ + // 处理 OFFSET 子句 if (offset_est != 0) { double offset_rows; if (offset_est > 0) { offset_rows = (double)offset_est; + // 如果 OFFSET 为正数,且计划是在数据节点上执行,则考虑数据节点的数量 if (is_replicated_plan(lefttree) && is_execute_on_datanodes(lefttree)) { offset_rows *= ng_get_dest_num_data_nodes(lefttree); } } else offset_rows = clamp_row_est(lefttree->plan_rows * 0.10); + + // 限制 OFFSET 行数不超过左子树的行数 if (offset_rows > lefttree->plan_rows) offset_rows = lefttree->plan_rows; + + // 调整计划的启动成本和行数 if (plan->plan_rows > 0) plan->startup_cost += (plan->total_cost - plan->startup_cost) * offset_rows / plan->plan_rows; + plan->plan_rows -= offset_rows; + + // 确保计划的行数不小于 1 if (plan->plan_rows < 1) plan->plan_rows = 1; } + // 处理 FETCH FIRST 子句 if (count_est != 0) { double count_rows; if (count_est > 0) { count_rows = (double)count_est; + // 如果 FETCH FIRST 为正数,且计划是在数据节点上执行,则考虑数据节点的数量 if (is_execute_on_datanodes(lefttree)) { count_rows *= ng_get_dest_num_data_nodes(lefttree); } } else count_rows = clamp_row_est(adjust_limit_row_count(lefttree->plan_rows)); + + // 限制 FETCH FIRST 行数不超过计划的行数 if (count_rows > plan->plan_rows) count_rows = plan->plan_rows; + + // 调整计划的总成本和行数 if (plan->plan_rows > 0) - plan->total_cost = - plan->startup_cost + (plan->total_cost - plan->startup_cost) * count_rows / plan->plan_rows; + plan->total_cost = plan->startup_cost + (plan->total_cost - plan->startup_cost) * count_rows / plan->plan_rows; + plan->plan_rows = count_rows; + + // 确保计划的行数不小于 1 if (plan->plan_rows < 1) plan->plan_rows = 1; } } - /* * initial_cost_nestloop * Preliminary estimate of the cost of a nestloop join path. @@ -2824,93 +2945,87 @@ void cost_limit(Plan* plan, Plan* lefttree, int64 offset_est, int64 count_est) * 'sjinfo' is extra info about the join for selectivity estimation * 'semifactors' contains valid data if jointype is SEMI or ANTI */ +// 初始化嵌套循环连接的成本计算 void initial_cost_nestloop(PlannerInfo* root, JoinCostWorkspace* workspace, JoinType jointype, Path* outer_path, Path* inner_path, SpecialJoinInfo* sjinfo, SemiAntiJoinFactors* semifactors, int dop) { + // 初始化启动成本和运行成本 Cost startup_cost = 0; Cost run_cost = 0; + + // 计算外部路径的行数(考虑并行度) double outer_path_rows = PATH_LOCAL_ROWS(outer_path) / dop; + + // 初始化内部扫描的成本信息 Cost inner_rescan_start_cost; Cost inner_rescan_total_cost; Cost inner_run_cost; Cost inner_rescan_run_cost; errno_t rc = 0; + // 初始化内部内存信息 rc = memset_s(&workspace->inner_mem_info, sizeof(OpMemInfo), 0, sizeof(OpMemInfo)); securec_check(rc, "\0", "\0"); - /* estimate costs to rescan the inner relation */ + // 计算内部路径的重新扫描成本 cost_rescan(root, inner_path, &inner_rescan_start_cost, &inner_rescan_total_cost, &workspace->inner_mem_info); - /* cost of source data */ - /* - * NOTE: clearly, we must pay both outer and inner paths' startup_cost - * before we can start returning tuples, so the join's startup cost is - * their sum. We'll also pay the inner path's rescan startup cost - * multiple times. - */ + // 计算启动成本和运行成本 startup_cost += outer_path->startup_cost + inner_path->startup_cost; run_cost += outer_path->total_cost - outer_path->startup_cost; + + // 如果外部路径行数大于1,考虑外部路径的重新扫描成本 if (outer_path_rows > 1) run_cost += (outer_path_rows - 1) * inner_rescan_start_cost; + // 计算内部路径的运行成本和重新扫描运行成本 inner_run_cost = inner_path->total_cost - inner_path->startup_cost; inner_rescan_run_cost = inner_rescan_total_cost - inner_rescan_start_cost; + // 根据连接类型计算成本 if (jointype == JOIN_SEMI || jointype == JOIN_ANTI) { double outer_matched_rows; Selectivity inner_scan_frac; - /* - * SEMI or ANTI join: executor will stop after first match. - * - * For an outer-rel row that has at least one match, we can expect the - * inner scan to stop after a fraction 1/(match_count+1) of the inner - * rows, if the matches are evenly distributed. Since they probably - * aren't quite evenly distributed, we apply a fuzz factor of 2.0 to - * that fraction. (If we used a larger fuzz factor, we'd have to - * clamp inner_scan_frac to at most 1.0; but since match_count is at - * least 1, no such clamp is needed now.) - * - * A complicating factor is that rescans may be cheaper than first - * scans. If we never scan all the way to the end of the inner rel, - * it might be (depending on the plan type) that we'd never pay the - * whole inner first-scan run cost. However it is difficult to - * estimate whether that will happen, so be conservative and always - * charge the whole first-scan cost once. - */ + // 对于 SEMI 和 ANTI 连接,计算匹配的外部行数和内部扫描比例 run_cost += inner_run_cost; outer_matched_rows = rint(outer_path_rows * semifactors->outer_match_frac); inner_scan_frac = 2.0 / (semifactors->match_count + 1.0); - /* Add inner run cost for additional outer tuples having matches */ + // 如果匹配的外部行数大于1,考虑重新扫描的运行成本 if (outer_matched_rows > 1) run_cost += (outer_matched_rows - 1) * inner_rescan_run_cost * inner_scan_frac; - /* - * The cost of processing unmatched rows varies depending on the - * details of the joinclauses, so we leave that part for later. - */ - /* Save private data for final_cost_nestloop */ + // 保存外部匹配行数和内部扫描比例 workspace->outer_matched_rows = outer_matched_rows; workspace->inner_scan_frac = inner_scan_frac; + + // 调整内存信息的成本 workspace->inner_mem_info.regressCost *= Max(outer_matched_rows, 1.0); } else { - /* Normal case; we'll scan whole input rel for each outer row */ + // 对于其他连接类型,只计算运行成本 run_cost += inner_run_cost; + + // 如果外部路径行数大于1,考虑重新扫描的运行成本 if (outer_path_rows > 1) run_cost += (outer_path_rows - 1) * inner_rescan_run_cost; + + // 调整内存信息的成本 workspace->inner_mem_info.regressCost *= Max(outer_path_rows, 1.0); } - /* CPU costs left for later */ - /* Public result fields */ + // 剩余的 CPU 成本将在后续计算中考虑 + + // 设置公共结果字段 workspace->startup_cost = startup_cost; workspace->total_cost = startup_cost + run_cost; - /* Save private data for final_cost_nestloop */ + + // 保存私有数据供最终计算使用 workspace->run_cost = run_cost; workspace->inner_rescan_run_cost = inner_rescan_run_cost; + + // 输出调试信息 ereport(DEBUG1, (errmodule(MOD_OPT_JOIN), errmsg("Initial nestloop cost: startup_cost: %lf, total_cost: %lf", @@ -3002,28 +3117,41 @@ void final_cost_nestloop(PlannerInfo* root, NestPath* path, JoinCostWorkspace* w } /* CPU costs */ - cost_qual_eval(&restrict_qual_cost, path->joinrestrictinfo, root); - startup_cost += restrict_qual_cost.startup; - cpu_per_tuple = u_sess->attr.attr_sql.cpu_tuple_cost + restrict_qual_cost.per_tuple; - run_cost += cpu_per_tuple * ntuples; + // 计算限制条件的成本 +cost_qual_eval(&restrict_qual_cost, path->joinrestrictinfo, root); - path->path.startup_cost = startup_cost; - path->path.total_cost = startup_cost + run_cost; - path->path.stream_cost = outer_path->stream_cost; +// 更新启动成本,加上限制条件的启动成本 +startup_cost += restrict_qual_cost.startup; - if (!method_enabled) - path->path.total_cost *= g_instance.cost_cxt.disable_cost_enlarge_factor; +// 计算每个元组的 CPU 成本,包括限制条件的每元组成本 +cpu_per_tuple = u_sess->attr.attr_sql.cpu_tuple_cost + restrict_qual_cost.per_tuple; - if (path->innerjoinpath->pathtype == T_Material) - copy_mem_info(&((MaterialPath*)path->innerjoinpath)->mem_info, &workspace->inner_mem_info); +// 更新运行成本,加上 CPU 成本乘以元组数 +run_cost += cpu_per_tuple * ntuples; + +// 设置路径的启动成本和总成本 +path->path.startup_cost = startup_cost; +path->path.total_cost = startup_cost + run_cost; + +// 设置路径的流成本为外部路径的流成本 +path->path.stream_cost = outer_path->stream_cost; + +// 如果方法未启用,则增加总成本 +if (!method_enabled) + path->path.total_cost *= g_instance.cost_cxt.disable_cost_enlarge_factor; + +// 如果内部连接路径是 Material 路径,则复制内存信息 +if (path->innerjoinpath->pathtype == T_Material) + copy_mem_info(&((MaterialPath*)path->innerjoinpath)->mem_info, &workspace->inner_mem_info); + +// 输出调试信息 +ereport(DEBUG2, + (errmodule(MOD_OPT_JOIN), + errmsg("final cost nest loop: stream_cost: %lf, startup_cost: %lf, total_cost: %lf", + path->path.stream_cost, + path->path.startup_cost, + path->path.total_cost))); - ereport(DEBUG2, - (errmodule(MOD_OPT_JOIN), - errmsg("final cost nest loop: stream_cost: %lf, startup_cost: %lf, total_cost: %lf", - path->path.stream_cost, - path->path.startup_cost, - path->path.total_cost))); -} /* * initial_cost_mergejoin @@ -3055,25 +3183,34 @@ void final_cost_nestloop(PlannerInfo* root, NestPath* path, JoinCostWorkspace* w * Note: outersortkeys and innersortkeys should be NIL if no explicit * sort is needed because the respective source path is already ordered. */ +// 初始化合并连接的成本计算 void initial_cost_mergejoin(PlannerInfo* root, JoinCostWorkspace* workspace, JoinType jointype, List* mergeclauses, Path* outer_path, Path* inner_path, List* outersortkeys, List* innersortkeys, SpecialJoinInfo* sjinfo) { + // 初始化启动成本和运行成本 Cost startup_cost = 0; Cost run_cost = 0; + + // 计算外部路径和内部路径的行数 double outer_path_rows = PATH_LOCAL_ROWS(outer_path); double inner_path_rows = PATH_LOCAL_ROWS(inner_path); + + // 初始化内部运行成本 Cost inner_run_cost; + + // 一些其他变量的初始化 double outer_rows, inner_rows, outer_skip_rows, inner_skip_rows; Selectivity outerstartsel, outerendsel, innerstartsel, innerendsel; - Path sort_path; /* dummy for result of cost_sort */ + Path sort_path; // 这里未使用,可能是未完成的代码 errno_t rc = 0; + // 初始化外部和内部内存信息 rc = memset_s(&workspace->outer_mem_info, sizeof(OpMemInfo), 0, sizeof(OpMemInfo)); securec_check(rc, "\0", "\0"); rc = memset_s(&workspace->inner_mem_info, sizeof(OpMemInfo), 0, sizeof(OpMemInfo)); securec_check(rc, "\0", "\0"); - /* Protect some assumptions below that rowcounts aren't zero or NaN */ + // 处理外部和内部路径行数的特殊情况 if (outer_path_rows <= 0 || isnan(outer_path_rows)) outer_path_rows = 1; if (inner_path_rows <= 0 || isnan(inner_path_rows)) @@ -3090,7 +3227,9 @@ void initial_cost_mergejoin(PlannerInfo* root, JoinCostWorkspace* workspace, Joi * mergejoinscansel() is a fairly expensive computation, we cache the * results in the merge clause RestrictInfo. */ + // 如果存在合并条件且连接类型不是 JOIN_FULL if (mergeclauses != NIL && jointype != JOIN_FULL) { + // 获取第一个合并条件的信息 RestrictInfo* firstclause = (RestrictInfo*)linitial(mergeclauses); List* opathkeys = NIL; List* ipathkeys = NIL; @@ -3098,7 +3237,7 @@ void initial_cost_mergejoin(PlannerInfo* root, JoinCostWorkspace* workspace, Joi PathKey* ipathkey = NULL; MergeScanSelCache* cache = NULL; - /* Get the input pathkeys to determine the sort-order details */ + // 获取外部和内部的排序键列表 opathkeys = outersortkeys ? outersortkeys : outer_path->pathkeys; ipathkeys = innersortkeys ? innersortkeys : inner_path->pathkeys; AssertEreport(opathkeys != NIL, @@ -3108,9 +3247,11 @@ void initial_cost_mergejoin(PlannerInfo* root, JoinCostWorkspace* workspace, Joi MOD_OPT, "The inner pathkeys is null when determining the cost of performing a mergejoin path."); + // 获取第一个排序键 opathkey = (PathKey*)linitial(opathkeys); ipathkey = (PathKey*)linitial(ipathkeys); - /* debugging check */ + + // 检查排序键的属性是否匹配 if (!OpFamilyEquals(opathkey->pk_opfamily, ipathkey->pk_opfamily) || opathkey->pk_eclass->ec_collation != ipathkey->pk_eclass->ec_collation || opathkey->pk_strategy != ipathkey->pk_strategy || opathkey->pk_nulls_first != ipathkey->pk_nulls_first) @@ -3119,22 +3260,23 @@ void initial_cost_mergejoin(PlannerInfo* root, JoinCostWorkspace* workspace, Joi errcode(ERRCODE_OPTIMIZER_INCONSISTENT_STATE), errmsg("left and right pathkeys do not match in mergejoin when initlize cost"))); - /* Get the selectivity with caching */ + // 获取缓存中的选择性信息 cache = cached_scansel(root, firstclause, opathkey); + // 根据连接左侧和右侧来设置选择性信息 if (bms_is_subset(firstclause->left_relids, outer_path->parent->relids)) { - /* left side of clause is outer */ outerstartsel = cache->leftstartsel; outerendsel = cache->leftendsel; innerstartsel = cache->rightstartsel; innerendsel = cache->rightendsel; } else { - /* left side of clause is inner */ outerstartsel = cache->rightstartsel; outerendsel = cache->rightendsel; innerstartsel = cache->leftstartsel; innerendsel = cache->leftendsel; } + + // 对于特定连接类型,调整选择性信息 if (jointype == JOIN_LEFT || jointype == JOIN_LEFT_ANTI_FULL || jointype == JOIN_ANTI) { outerstartsel = 0.0; outerendsel = 1.0; @@ -3142,15 +3284,8 @@ void initial_cost_mergejoin(PlannerInfo* root, JoinCostWorkspace* workspace, Joi innerstartsel = 0.0; innerendsel = 1.0; } - /* jointype should not be JOIN_RIGHT_ANTI_FULL, - * because JOIN_RIGHT_ANTI_FULL can not create a mergejoin plan. - */ - AssertEreport(jointype != JOIN_RIGHT_ANTI_FULL, - MOD_OPT, - "The mergejoin plan with JOIN_RIGHT_ANTI_FULL is not allowed." - "when determining the cost of performing a mergejoin path."); } else { - /* cope with clauseless or full mergejoin */ + // 如果没有合并条件或连接类型是 JOIN_FULL,则选择性信息默认为 0.0 到 1.0 outerstartsel = innerstartsel = 0.0; outerendsel = innerendsel = 1.0; } @@ -3159,85 +3294,89 @@ void initial_cost_mergejoin(PlannerInfo* root, JoinCostWorkspace* workspace, Joi * Convert selectivities to row counts. We force outer_rows and * inner_rows to be at least 1, but the skip_rows estimates can be zero. */ - outer_skip_rows = rint(outer_path_rows * outerstartsel); - inner_skip_rows = rint(inner_path_rows * innerstartsel); - outer_rows = clamp_row_est(outer_path_rows * outerendsel); - inner_rows = clamp_row_est(inner_path_rows * innerendsel); + // 计算需要跳过的外部和内部行数,以及合适的行数 +outer_skip_rows = rint(outer_path_rows * outerstartsel); +inner_skip_rows = rint(inner_path_rows * innerstartsel); +outer_rows = clamp_row_est(outer_path_rows * outerendsel); +inner_rows = clamp_row_est(inner_path_rows * innerendsel); - AssertEreport(outer_skip_rows <= outer_rows, - MOD_OPT, - "The estimated skip rows is larger than rounding outer rows which avoid possible divide-by-zero " - "when determining the cost of performing a mergejoin path."); - AssertEreport(inner_skip_rows <= inner_rows, - MOD_OPT, - "The estimated skip rows is larger than rounding inner rows which avoid possible divide-by-zero" - "when determining the cost of performing a mergejoin path."); +// 断言跳过的行数不大于相应的行数,以避免可能的除零错误 +AssertEreport(outer_skip_rows <= outer_rows, + MOD_OPT, + "The estimated skip rows is larger than rounding outer rows which avoid possible divide-by-zero " + "when determining the cost of performing a mergejoin path."); +AssertEreport(inner_skip_rows <= inner_rows, + MOD_OPT, + "The estimated skip rows is larger than rounding inner rows which avoid possible divide-by-zero" + "when determining the cost of performing a mergejoin path."); - /* - * Readjust scan selectivities to account for above rounding. This is - * normally an insignificant effect, but when there are only a few rows in - * the inputs, failing to do this makes for a large percentage error. - */ - outerstartsel = outer_skip_rows / outer_path_rows; - innerstartsel = inner_skip_rows / inner_path_rows; - outerendsel = outer_rows / outer_path_rows; - innerendsel = inner_rows / inner_path_rows; +// 更新选择性信息以考虑跳过的行数 +outerstartsel = outer_skip_rows / outer_path_rows; +innerstartsel = inner_skip_rows / inner_path_rows; +outerendsel = outer_rows / outer_path_rows; +innerendsel = inner_rows / inner_path_rows; - AssertEreport(outerstartsel <= outerendsel, - MOD_OPT, - "The selectivities corresponding to estimated skip rows is larger than that of above rounding outer rows" - "when determining the cost of performing a mergejoin path."); - AssertEreport(innerstartsel <= innerendsel, - MOD_OPT, - "The selectivities corresponding to estimated skip rows is larger than that of above rounding inner rows" - "when determining the cost of performing a mergejoin path."); +// 断言选择性信息的一致性 +AssertEreport(outerstartsel <= outerendsel, + MOD_OPT, + "The selectivities corresponding to estimated skip rows is larger than that of above rounding outer rows" + "when determining the cost of performing a mergejoin path."); +AssertEreport(innerstartsel <= innerendsel, + MOD_OPT, + "The selectivities corresponding to estimated skip rows is larger than that of above rounding inner rows" + "when determining the cost of performing a mergejoin path."); - /* cost of source data */ - if (outersortkeys || IsA(outer_path, StreamPath)) { /* do we need to sort outer? */ - int outer_width = get_path_actual_total_width(outer_path, root->glob->vectorized, OP_SORT); +// 检查是否需要对外部路径进行排序 +if (outersortkeys || IsA(outer_path, StreamPath)) { + int outer_width = get_path_actual_total_width(outer_path, root->glob->vectorized, OP_SORT); - cost_sort(&sort_path, - outersortkeys, - outer_path->total_cost, - outer_path_rows, - outer_width, - 0.0, - u_sess->opt_cxt.op_work_mem, - -1.0, - root->glob->vectorized, - 1, - &workspace->outer_mem_info); - startup_cost += sort_path.startup_cost; - startup_cost += (sort_path.total_cost - sort_path.startup_cost) * outerstartsel; - run_cost += (sort_path.total_cost - sort_path.startup_cost) * (outerendsel - outerstartsel); - } else { - startup_cost += outer_path->startup_cost; - startup_cost += (outer_path->total_cost - outer_path->startup_cost) * outerstartsel; - run_cost += (outer_path->total_cost - outer_path->startup_cost) * (outerendsel - outerstartsel); - } + // 计算外部路径的排序成本 + cost_sort(&sort_path, + outersortkeys, + outer_path->total_cost, + outer_path_rows, + outer_width, + 0.0, + u_sess->opt_cxt.op_work_mem, + -1.0, + root->glob->vectorized, + 1, + &workspace->outer_mem_info); + startup_cost += sort_path.startup_cost; + startup_cost += (sort_path.total_cost - sort_path.startup_cost) * outerstartsel; + run_cost += (sort_path.total_cost - sort_path.startup_cost) * (outerendsel - outerstartsel); +} else { + // 否则,使用外部路径的成本信息 + startup_cost += outer_path->startup_cost; + startup_cost += (outer_path->total_cost - outer_path->startup_cost) * outerstartsel; + run_cost += (outer_path->total_cost - outer_path->startup_cost) * (outerendsel - outerstartsel); +} - if (innersortkeys || IsA(inner_path, StreamPath)) { /* do we need to sort inner? */ - int inner_width = get_path_actual_total_width(inner_path, root->glob->vectorized, OP_SORT); +// 检查是否需要对内部路径进行排序 +if (innersortkeys || IsA(inner_path, StreamPath)) { + int inner_width = get_path_actual_total_width(inner_path, root->glob->vectorized, OP_SORT); - cost_sort(&sort_path, - innersortkeys, - inner_path->total_cost, - inner_path_rows, - inner_width, - 0.0, - u_sess->opt_cxt.op_work_mem, - -1.0, - root->glob->vectorized, - 1, - &workspace->inner_mem_info); - startup_cost += sort_path.startup_cost; - startup_cost += (sort_path.total_cost - sort_path.startup_cost) * innerstartsel; - inner_run_cost = (sort_path.total_cost - sort_path.startup_cost) * (innerendsel - innerstartsel); - } else { - startup_cost += inner_path->startup_cost; - startup_cost += (inner_path->total_cost - inner_path->startup_cost) * innerstartsel; - inner_run_cost = (inner_path->total_cost - inner_path->startup_cost) * (innerendsel - innerstartsel); - } + // 计算内部路径的排序成本 + cost_sort(&sort_path, + innersortkeys, + inner_path->total_cost, + inner_path_rows, + inner_width, + 0.0, + u_sess->opt_cxt.op_work_mem, + -1.0, + root->glob->vectorized, + 1, + &workspace->inner_mem_info); + startup_cost += sort_path.startup_cost; + startup_cost += (sort_path.total_cost - sort_path.startup_cost) * innerstartsel; + inner_run_cost = (sort_path.total_cost - sort_path.startup_cost) * (innerendsel - innerstartsel); +} else { + // 否则,使用内部路径的成本信息 + startup_cost += inner_path->startup_cost; + startup_cost += (inner_path->total_cost - inner_path->startup_cost) * innerstartsel; + inner_run_cost = (inner_path->total_cost - inner_path->startup_cost) * (innerendsel - innerstartsel); +} /* * We can't yet determine whether rescanning occurs, or whether @@ -3248,21 +3387,36 @@ void initial_cost_mergejoin(PlannerInfo* root, JoinCostWorkspace* workspace, Joi */ /* CPU costs left for later */ /* Public result fields */ - workspace->startup_cost = startup_cost; - workspace->total_cost = startup_cost + run_cost + inner_run_cost; - /* Save private data for final_cost_mergejoin */ - workspace->run_cost = run_cost; - workspace->inner_run_cost = inner_run_cost; - workspace->outer_rows = outer_rows; - workspace->inner_rows = inner_rows; - workspace->outer_skip_rows = outer_skip_rows; - workspace->inner_skip_rows = inner_skip_rows; - ereport(DEBUG1, - (errmodule(MOD_OPT_JOIN), - errmsg("Initial mergejoin cost: startup_cost: %lf, total_cost: %lf", - workspace->startup_cost, - workspace->total_cost))); -} + // 设置工作空间中的启动成本为计算的启动成本 +workspace->startup_cost = startup_cost; + +// 设置工作空间中的总成本为计算的总成本,包括启动成本、运行成本和内部运行成本 +workspace->total_cost = startup_cost + run_cost + inner_run_cost; + +// 设置工作空间中的运行成本为计算的运行成本 +workspace->run_cost = run_cost; + +// 设置工作空间中的内部运行成本为计算的内部运行成本 +workspace->inner_run_cost = inner_run_cost; + +// 设置工作空间中的外部行数为计算的外部行数 +workspace->outer_rows = outer_rows; + +// 设置工作空间中的内部行数为计算的内部行数 +workspace->inner_rows = inner_rows; + +// 设置工作空间中的外部跳过行数为计算的外部跳过行数 +workspace->outer_skip_rows = outer_skip_rows; + +// 设置工作空间中的内部跳过行数为计算的内部跳过行数 +workspace->inner_skip_rows = inner_skip_rows; + +// 输出调试信息,显示初始合并连接成本的启动成本和总成本 +ereport(DEBUG1, + (errmodule(MOD_OPT_JOIN), + errmsg("Initial mergejoin cost: startup_cost: %lf, total_cost: %lf", + workspace->startup_cost, + workspace->total_cost))); /* * final_cost_mergejoin @@ -3284,26 +3438,51 @@ void initial_cost_mergejoin(PlannerInfo* root, JoinCostWorkspace* workspace, Joi * 'workspace' is the result from initial_cost_mergejoin * 'sjinfo' is extra info about the join for selectivity estimation */ -void final_cost_mergejoin( - PlannerInfo* root, MergePath* path, JoinCostWorkspace* workspace, SpecialJoinInfo* sjinfo, bool hasalternative) -{ - Path* outer_path = path->jpath.outerjoinpath; - Path* inner_path = path->jpath.innerjoinpath; - double inner_path_rows = PATH_LOCAL_ROWS(inner_path); - List* mergeclauses = path->path_mergeclauses; - List* innersortkeys = path->innersortkeys; - Cost startup_cost = workspace->startup_cost; - Cost run_cost = workspace->run_cost; - Cost inner_run_cost = workspace->inner_run_cost; - double outer_rows = workspace->outer_rows; - double inner_rows = workspace->inner_rows; - double outer_skip_rows = workspace->outer_skip_rows; - double inner_skip_rows = workspace->inner_skip_rows; - Cost cpu_per_tuple, bare_inner_cost, mat_inner_cost; - QualCost merge_qual_cost; - QualCost qp_qual_cost; - double mergejointuples, rescannedtuples; - double rescanratio; +// 获取连接路径中的外部和内部路径 +Path* outer_path = path->jpath.outerjoinpath; +Path* inner_path = path->jpath.innerjoinpath; + +// 获取内部路径的行数 +double inner_path_rows = PATH_LOCAL_ROWS(inner_path); + +// 获取合并连接的连接条件 +List* mergeclauses = path->path_mergeclauses; + +// 获取内部路径的排序键 +List* innersortkeys = path->innersortkeys; + +// 获取工作空间中的启动成本 +Cost startup_cost = workspace->startup_cost; + +// 获取工作空间中的运行成本 +Cost run_cost = workspace->run_cost; + +// 获取工作空间中的内部运行成本 +Cost inner_run_cost = workspace->inner_run_cost; + +// 获取工作空间中的外部行数 +double outer_rows = workspace->outer_rows; + +// 获取工作空间中的内部行数 +double inner_rows = workspace->inner_rows; + +// 获取工作空间中的外部跳过行数 +double outer_skip_rows = workspace->outer_skip_rows; + +// 获取工作空间中的内部跳过行数 +double inner_skip_rows = workspace->inner_skip_rows; + +// 初始化用于计算成本的变量 +Cost cpu_per_tuple, bare_inner_cost, mat_inner_cost; + +// 初始化用于计算合并连接成本的质量成本和QP成本 +QualCost merge_qual_cost; +QualCost qp_qual_cost; + +// 初始化用于计算合并连接的元组数、重扫元组数和重扫比率的变量 +double mergejointuples, rescannedtuples; +double rescanratio; + /* Protect some assumptions below that rowcounts aren't zero or NaN */ if (inner_path_rows <= 0 || isnan(inner_path_rows)) @@ -3481,26 +3660,36 @@ void final_cost_mergejoin( * Note: we could adjust for SEMI/ANTI joins skipping some qual * evaluations here, but it's probably not worth the trouble. */ - startup_cost += qp_qual_cost.startup; - cpu_per_tuple = u_sess->attr.attr_sql.cpu_tuple_cost + qp_qual_cost.per_tuple; - run_cost += cpu_per_tuple * mergejointuples; + // 将合并连接质量成本的启动成本添加到启动成本中 +startup_cost += qp_qual_cost.startup; - copy_mem_info(&path->outer_mem_info, &workspace->outer_mem_info); - copy_mem_info(&path->inner_mem_info, &workspace->inner_mem_info); +// 计算每个元组的CPU成本,并将其添加到运行成本中 +cpu_per_tuple = u_sess->attr.attr_sql.cpu_tuple_cost + qp_qual_cost.per_tuple; +run_cost += cpu_per_tuple * mergejointuples; - path->jpath.path.startup_cost = startup_cost; - path->jpath.path.total_cost = startup_cost + run_cost; - path->jpath.path.stream_cost = outer_path->stream_cost; +// 复制外部和内部路径的内存信息到合并连接路径中 +copy_mem_info(&path->outer_mem_info, &workspace->outer_mem_info); +copy_mem_info(&path->inner_mem_info, &workspace->inner_mem_info); - if (!u_sess->attr.attr_sql.enable_mergejoin && hasalternative) - path->jpath.path.total_cost *= g_instance.cost_cxt.disable_cost_enlarge_factor; +// 设置合并连接路径的启动成本和总成本 +path->jpath.path.startup_cost = startup_cost; +path->jpath.path.total_cost = startup_cost + run_cost; + +// 设置合并连接路径的流成本为外部路径的流成本 +path->jpath.path.stream_cost = outer_path->stream_cost; + +// 如果禁用了合并连接并且有替代计划,则将总成本乘以禁用成本扩大因子 +if (!u_sess->attr.attr_sql.enable_mergejoin && hasalternative) + path->jpath.path.total_cost *= g_instance.cost_cxt.disable_cost_enlarge_factor; + +// 输出调试信息,包括流成本、启动成本和总成本 +ereport(DEBUG2, + (errmodule(MOD_OPT_JOIN), + errmsg("final cost merge join: stream_cost: %lf, startup_cost: %lf, total_cost: %lf", + path->jpath.path.stream_cost, + path->jpath.path.startup_cost, + path->jpath.path.total_cost))); - ereport(DEBUG2, - (errmodule(MOD_OPT_JOIN), - errmsg("final cost merge join: stream_cost: %lf, startup_cost: %lf, total_cost: %lf", - path->jpath.path.stream_cost, - path->jpath.path.startup_cost, - path->jpath.path.total_cost))); } /* @@ -3508,49 +3697,62 @@ void final_cost_mergejoin( */ MergeScanSelCache* cached_scansel(PlannerInfo* root, RestrictInfo* rinfo, PathKey* pathkey) { - MergeScanSelCache* cache = NULL; - ListCell* lc = NULL; - Selectivity leftstartsel, leftendsel, rightstartsel, rightendsel; - MemoryContext oldcontext; + // 声明一个 MergeScanSelCache 结构体指针,用于存储选择性信息的缓存 +MergeScanSelCache* cache = NULL; - /* Do we have this result already? */ - foreach (lc, rinfo->scansel_cache) { - cache = (MergeScanSelCache*)lfirst(lc); - if (OpFamilyEquals(cache->opfamily, pathkey->pk_opfamily) && - cache->collation == pathkey->pk_eclass->ec_collation && - cache->strategy == pathkey->pk_strategy && cache->nulls_first == pathkey->pk_nulls_first) - return cache; - } +// 声明一个列表迭代器,用于遍历选择性信息的缓存列表 +ListCell* lc = NULL; - /* Nope, do the computation */ - mergejoinscansel(root, - (Node*)rinfo->clause, - pathkey->pk_opfamily, - pathkey->pk_strategy, - pathkey->pk_nulls_first, - &leftstartsel, - &leftendsel, - &rightstartsel, - &rightendsel); +// 声明变量用于存储左右输入的起始和结束选择性 +Selectivity leftstartsel, leftendsel, rightstartsel, rightendsel; - /* Cache the result in suitably long-lived workspace */ - oldcontext = MemoryContextSwitchTo(root->planner_cxt); +// 获取当前内存上下文,并在后续操作中切换到 planner 上下文 +MemoryContext oldcontext; - cache = (MergeScanSelCache*)palloc(sizeof(MergeScanSelCache)); - cache->opfamily = pathkey->pk_opfamily; - cache->collation = pathkey->pk_eclass->ec_collation; - cache->strategy = pathkey->pk_strategy; - cache->nulls_first = pathkey->pk_nulls_first; - cache->leftstartsel = leftstartsel; - cache->leftendsel = leftendsel; - cache->rightstartsel = rightstartsel; - cache->rightendsel = rightendsel; +// 遍历选择性信息的缓存列表,查找是否已经存在相同条件的缓存 +foreach (lc, rinfo->scansel_cache) { + cache = (MergeScanSelCache*)lfirst(lc); + if (OpFamilyEquals(cache->opfamily, pathkey->pk_opfamily) && + cache->collation == pathkey->pk_eclass->ec_collation && + cache->strategy == pathkey->pk_strategy && cache->nulls_first == pathkey->pk_nulls_first) + return cache; +} - rinfo->scansel_cache = lappend(rinfo->scansel_cache, cache); +// 如果缓存中没有找到相同条件的选择性信息,则需要重新计算 +mergejoinscansel(root, + (Node*)rinfo->clause, + pathkey->pk_opfamily, + pathkey->pk_strategy, + pathkey->pk_nulls_first, + &leftstartsel, + &leftendsel, + &rightstartsel, + &rightendsel); + +// 切换到 planner 上下文以分配新的缓存空间 +oldcontext = MemoryContextSwitchTo(root->planner_cxt); + +// 分配新的 MergeScanSelCache 结构体,并将计算得到的选择性信息存储其中 +cache = (MergeScanSelCache*)palloc(sizeof(MergeScanSelCache)); +cache->opfamily = pathkey->pk_opfamily; +cache->collation = pathkey->pk_eclass->ec_collation; +cache->strategy = pathkey->pk_strategy; +cache->nulls_first = pathkey->pk_nulls_first; +cache->leftstartsel = leftstartsel; +cache->leftendsel = leftendsel; +cache->rightstartsel = rightstartsel; +cache->rightendsel = rightendsel; + +// 将新的缓存添加到选择性信息的缓存列表中 +rinfo->scansel_cache = lappend(rinfo->scansel_cache, cache); + +// 切换回先前的内存上下文 +MemoryContextSwitchTo(oldcontext); + +// 返回计算得到的缓存 +return cache; - MemoryContextSwitchTo(oldcontext); - return cache; } /* @@ -3578,35 +3780,33 @@ MergeScanSelCache* cached_scansel(PlannerInfo* root, RestrictInfo* rinfo, PathKe * 'sjinfo' is extra info about the join for selectivity estimation * 'semifactors' contains valid data if jointype is SEMI or ANTI */ -void initial_cost_hashjoin(PlannerInfo* root, JoinCostWorkspace* workspace, JoinType jointype, List* hashclauses, - Path* outer_path, Path* inner_path, SpecialJoinInfo* sjinfo, SemiAntiJoinFactors* semifactors, int dop) -{ - Cost startup_cost = 0; - Cost run_cost = 0; - double outer_path_rows = PATH_LOCAL_ROWS(outer_path) / dop; - double inner_path_rows = PATH_LOCAL_ROWS(inner_path) / dop; - int num_hashclauses = list_length(hashclauses); - int numbuckets; - int numbatches; - int num_skew_mcvs; - int inner_width; /* width of inner rel */ - int outer_width; /* width of outer rel */ - double outerpages; - double innerpages; +// 声明并初始化一些变量,用于存储成本估算结果 +Cost startup_cost = 0; +Cost run_cost = 0; +double outer_path_rows = PATH_LOCAL_ROWS(outer_path) / dop; // 外部路径的行数 +double inner_path_rows = PATH_LOCAL_ROWS(inner_path) / dop; // 内部路径的行数 +int num_hashclauses = list_length(hashclauses); // 哈希连接的哈希条件数量 +int numbuckets; // 哈希连接的哈希桶数量 +int numbatches; // 哈希连接的哈希批次数量 +int num_skew_mcvs; // 哈希连接的哈希表中的倾斜值数量 +int inner_width; // 内部关系的宽度 +int outer_width; // 外部关系的宽度 +double outerpages; // 外部关系的页数估计 +double innerpages; // 内部关系的页数估计 - errno_t rc = 0; +// 使用memset_s函数将 inner_mem_info 结构体清零,以确保内存初始化 +errno_t rc = 0; +rc = memset_s(&workspace->inner_mem_info, sizeof(OpMemInfo), 0, sizeof(OpMemInfo)); +securec_check(rc, "\0", "\0"); - rc = memset_s(&workspace->inner_mem_info, sizeof(OpMemInfo), 0, sizeof(OpMemInfo)); - securec_check(rc, "\0", "\0"); +// 输出一些调试信息,显示外部和内部路径的启动成本和总成本 +ereport(DEBUG1, (errmodule(MOD_OPT_JOIN), errmsg("outer: %lf, %lf", outer_path->startup_cost, outer_path->total_cost))); +ereport(DEBUG1, (errmodule(MOD_OPT_JOIN), errmsg("inner: %lf, %lf", inner_path->startup_cost, inner_path->total_cost))); - ereport( - DEBUG1, (errmodule(MOD_OPT_JOIN), errmsg("outer: %lf, %lf", outer_path->startup_cost, outer_path->total_cost))); - ereport( - DEBUG1, (errmodule(MOD_OPT_JOIN), errmsg("inner: %lf, %lf", inner_path->startup_cost, inner_path->total_cost))); +// 将外部路径的启动成本和运行成本添加到总成本中 +startup_cost += outer_path->startup_cost; +run_cost += outer_path->total_cost - outer_path->startup_cost; - /* cost of source data */ - startup_cost += outer_path->startup_cost; - run_cost += outer_path->total_cost - outer_path->startup_cost; /* * Sometimes, we suffers the case that small table with large cost join * with a large table. In such case, the cost mainly comes from large cost @@ -3639,21 +3839,29 @@ void initial_cost_hashjoin(PlannerInfo* root, JoinCostWorkspace* workspace, Join * should charge the extra eval costs of the left or right side, as * appropriate, here. This seems more work than it's worth at the moment. */ - startup_cost += (u_sess->attr.attr_sql.cpu_operator_cost * num_hashclauses + u_sess->attr.attr_sql.cpu_tuple_cost + - u_sess->attr.attr_sql.allocate_mem_cost) * - inner_path_rows; - run_cost += u_sess->attr.attr_sql.cpu_operator_cost * num_hashclauses * outer_path_rows; + // 将哈希函数的 CPU 成本、元组的 CPU 成本、分配内存的成本加到启动成本中,然后乘以内部路径的行数 +startup_cost += (u_sess->attr.attr_sql.cpu_operator_cost * num_hashclauses + u_sess->attr.attr_sql.cpu_tuple_cost + + u_sess->attr.attr_sql.allocate_mem_cost) * + inner_path_rows; - ereport(DEBUG2, - (errmodule(MOD_OPT_JOIN), - errmsg("Add hash function cost: startup_cost: %lf, run_cost: %lf", startup_cost, run_cost))); +// 将哈希函数的 CPU 成本乘以哈希条件的数量和外部路径的行数,加到运行成本中 +run_cost += u_sess->attr.attr_sql.cpu_operator_cost * num_hashclauses * outer_path_rows; + +// 输出一些调试信息,显示已添加的哈希函数成本 +ereport(DEBUG2, + (errmodule(MOD_OPT_JOIN), + errmsg("Add hash function cost: startup_cost: %lf, run_cost: %lf", startup_cost, run_cost))); + +// 检查哈希连接是否涉及复杂的哈希键 +bool isComplicateHashKey = has_complicate_hashkey(hashclauses, inner_path->parent->relids); + +// 如果哈希连接是向量化的且为右连接类型,则增加新列的数量 +int newcolnum = isComplicateHashKey ? 1 : 0; + +if (root->glob->vectorized && (jointype == JOIN_RIGHT || jointype == JOIN_RIGHT_ANTI || + jointype == JOIN_RIGHT_SEMI || jointype == JOIN_RIGHT_ANTI_FULL)) + newcolnum++; - bool isComplicateHashKey = has_complicate_hashkey(hashclauses, inner_path->parent->relids); - int newcolnum = isComplicateHashKey ? 1 : 0; - /* for vectorized right join or right anti join, we should add more column to flag match or not */ - if (root->glob->vectorized && (jointype == JOIN_RIGHT || jointype == JOIN_RIGHT_ANTI || - jointype == JOIN_RIGHT_SEMI || jointype == JOIN_RIGHT_ANTI_FULL)) - newcolnum++; /* * Get hash table size that executor would use for inner relation. @@ -3665,20 +3873,27 @@ void initial_cost_hashjoin(PlannerInfo* root, JoinCostWorkspace* workspace, Join * XXX at some point it might be interesting to try to account for skew * optimization in the cost estimate, but for now, we don't. */ - inner_width = get_path_actual_total_width(inner_path, root->glob->vectorized, OP_HASHJOIN, newcolnum); - outer_width = get_path_actual_total_width(outer_path, root->glob->vectorized, OP_HASHJOIN); - ExecChooseHashTableSize(inner_path_rows, - inner_width, - true, - &numbuckets, - &numbatches, - &num_skew_mcvs, - u_sess->opt_cxt.op_work_mem / dop, - root->glob->vectorized, - &workspace->inner_mem_info); + // 获取内部路径的实际总宽度,其中 newcolnum 用于确定是否增加新列的数量 +inner_width = get_path_actual_total_width(inner_path, root->glob->vectorized, OP_HASHJOIN, newcolnum); + +// 获取外部路径的实际总宽度 +outer_width = get_path_actual_total_width(outer_path, root->glob->vectorized, OP_HASHJOIN); + +// 选择哈希表的大小,并计算哈希桶的数量、批次数以及倾斜的 MCV 数量 +ExecChooseHashTableSize(inner_path_rows, + inner_width, + true, + &numbuckets, + &numbatches, + &num_skew_mcvs, + u_sess->opt_cxt.op_work_mem / dop, + root->glob->vectorized, + &workspace->inner_mem_info); + +// 计算内部和外部路径的页数,用于估算磁盘 I/O 成本 +innerpages = page_size(PATH_LOCAL_ROWS(inner_path), inner_width) / dop; +outerpages = page_size(PATH_LOCAL_ROWS(outer_path), outer_width) / dop; - innerpages = page_size(PATH_LOCAL_ROWS(inner_path), inner_width) / dop; - outerpages = page_size(PATH_LOCAL_ROWS(outer_path), outer_width) / dop; /* * If inner relation is too big then we will need to "batch" the join, @@ -3687,63 +3902,79 @@ void initial_cost_hashjoin(PlannerInfo* root, JoinCostWorkspace* workspace, Join * sequential. Writing the inner rel counts as startup cost, all the rest * as run cost. */ - double startuppagecost = u_sess->attr.attr_sql.seq_page_cost * innerpages; - double runpagecost = u_sess->attr.attr_sql.seq_page_cost * (innerpages + 2 * outerpages); + // 计算哈希连接的启动成本和运行成本的磁盘 I/O 成本 +double startuppagecost = u_sess->attr.attr_sql.seq_page_cost * innerpages; +double runpagecost = u_sess->attr.attr_sql.seq_page_cost * (innerpages + 2 * outerpages); - if (numbatches > 1) { - startup_cost += startuppagecost; - run_cost += runpagecost; +// 如果哈希连接有多个批次,将磁盘 I/O 成本添加到启动成本和运行成本中 +if (numbatches > 1) { + startup_cost += startuppagecost; + run_cost += runpagecost; + ereport(DEBUG2, + (errmodule(MOD_OPT_JOIN), + errmsg("Add seq page cost: startup_cost: %lf, run_cost: %lf", startup_cost, run_cost))); +} + +// 如果哈希连接只有一个批次,并且外部路径的流成本较高,则考虑将流成本的一部分削减 +if (numbatches <= 1 + && outer_path->stream_cost > STREAM_COST_THRESHOLD && outer_path->stream_cost > 0.25 * inner_path->total_cost) { + // 计算需要削减的成本 + Cost cut_cost = 0.75 * Min(outer_path->stream_cost, inner_path->total_cost); + + // 如果削减的成本小于等于原始启动成本,则将其从启动成本中减去 + if (cut_cost <= startup_cost_origin) + startup_cost -= cut_cost; + else { + // 否则,输出调试信息表示异常情况 ereport(DEBUG2, (errmodule(MOD_OPT_JOIN), - errmsg("Add seq page cost: startup_cost: %lf, run_cost: %lf", startup_cost, run_cost))); - } - - /* cut stream_cost */ - if (numbatches <= 1 /* don't cut stream cost if inner rel spill to disk */ - && outer_path->stream_cost > STREAM_COST_THRESHOLD && outer_path->stream_cost > 0.25 * inner_path->total_cost) { - Cost cut_cost = 0.75 * Min(outer_path->stream_cost, inner_path->total_cost); - - if (cut_cost <= startup_cost_origin) - startup_cost -= cut_cost; - else { - ereport(DEBUG2, - (errmodule(MOD_OPT_JOIN), - errmsg("Abnormal case: outer_stream_cost: %lf > current startup_cost: %lf", - outer_path->stream_cost, - startup_cost))); - } - - ereport(DEBUG2, - (errmodule(MOD_OPT_JOIN), - errmsg("outer_stream_cost: %lf, after cut-off: startup_cost: %lf, run_cost: %lf, cut_off_cost: %lf", + errmsg("Abnormal case: outer_stream_cost: %lf > current startup_cost: %lf", outer_path->stream_cost, - startup_cost, - run_cost, - cut_cost))); + startup_cost))); } + // 输出削减后的成本信息 + ereport(DEBUG2, + (errmodule(MOD_OPT_JOIN), + errmsg("outer_stream_cost: %lf, after cut-off: startup_cost: %lf, run_cost: %lf, cut_off_cost: %lf", + outer_path->stream_cost, + startup_cost, + run_cost, + cut_cost))); +} + + /* Set mem info for hash join path */ - workspace->inner_mem_info.maxMem *= dop; - workspace->inner_mem_info.minMem = workspace->inner_mem_info.maxMem / HASH_MAX_DISK_SIZE; - workspace->inner_mem_info.opMem = u_sess->opt_cxt.op_work_mem; - workspace->inner_mem_info.regressCost = (startuppagecost + runpagecost); + // 将内部内存信息的最大内存乘以 dop 来估算总的内存需求 +workspace->inner_mem_info.maxMem *= dop; - /* CPU costs left for later */ - /* Public result fields */ - workspace->startup_cost = startup_cost; - workspace->total_cost = startup_cost + run_cost; - /* Save private data for final_cost_hashjoin */ - workspace->run_cost = run_cost; - workspace->numbuckets = numbuckets; - workspace->numbatches = numbatches; +// 计算内存信息的最小内存,以确保不超过 HASH_MAX_DISK_SIZE +workspace->inner_mem_info.minMem = workspace->inner_mem_info.maxMem / HASH_MAX_DISK_SIZE; + +// 设置操作内存为配置的工作内存(op_work_mem) +workspace->inner_mem_info.opMem = u_sess->opt_cxt.op_work_mem; + +// 计算内存信息的回归成本,包括启动成本和运行成本 +workspace->inner_mem_info.regressCost = (startuppagecost + runpagecost); + +// 设置工作空间中的启动成本和总成本 +workspace->startup_cost = startup_cost; +workspace->total_cost = startup_cost + run_cost; + +// 设置工作空间中的运行成本、桶的数量和批次的数量 +workspace->run_cost = run_cost; +workspace->numbuckets = numbuckets; +workspace->numbatches = numbatches; + +// 输出调试信息,包括启动成本、总成本、配置的工作内存和估算的工作内存 +ereport(DEBUG1, + (errmodule(MOD_OPT_JOIN), + errmsg("Initial hashjoin cost: startup_cost: %lf, total_cost: %lf, work_mem: %d, esti_work_mem: %d", + workspace->startup_cost, + workspace->total_cost, + u_sess->opt_cxt.op_work_mem, + root->glob->estiopmem))); - ereport(DEBUG1, - (errmodule(MOD_OPT_JOIN), - errmsg("Initial hashjoin cost: startup_cost: %lf, total_cost: %lf, work_mem: %d, esti_work_mem: %d", - workspace->startup_cost, - workspace->total_cost, - u_sess->opt_cxt.op_work_mem, - root->glob->estiopmem))); } /* @@ -3759,39 +3990,47 @@ void initial_cost_hashjoin(PlannerInfo* root, JoinCostWorkspace* workspace, Join * * sjinfo: identify join info include lefthand/righthand in order to judge if can use possion to estimate distinct. */ +// 计算哈希连接的桶大小和估算的不同值数量 Selectivity compute_bucket_size(PlannerInfo* root, RestrictInfo* restrictinfo, double virtualbuckets, Path* inner_path, bool left, SpecialJoinInfo* sjinfo, double* ndistinct) { - Path* inner = NULL; - Selectivity thisbucketsize = -1; - BucketSize* bucket = left ? &restrictinfo->left_bucketsize : &restrictinfo->right_bucketsize; + Path* inner = NULL; // 内部路径,用于存储哈希连接的内部路径 + Selectivity thisbucketsize = -1; // 初始化桶大小为 -1,表示未知 + BucketSize* bucket = left ? &restrictinfo->left_bucketsize : &restrictinfo->right_bucketsize; // 获取左侧或右侧的桶信息 if (!IsA(inner_path, StreamPath)) { - /* for replicate path, we should also adjust to global distinct value */ + // 如果内部路径不是流路径 + if (IsA(inner_path, HashPath) || IsLocatorReplicated(inner_path->locator_type)) { + // 如果内部路径是哈希路径或者定位器类型是复制 inner = inner_path; } else if (bucket->normal.nbuckets == virtualbuckets) { - /* Get inner bucketsize from cache which has saved before */ + // 如果已经存储了与虚拟桶数量匹配的普通桶信息 thisbucketsize = bucket->normal.bucket_size; *ndistinct = bucket->normal.ndistinct; } } else { + // 如果内部路径是流路径 inner = inner_path; if (((StreamPath*)inner)->type == STREAM_REDISTRIBUTE) { + // 如果流路径类型是重分发 if (bucket->redistribute.nbuckets == virtualbuckets) { + // 如果已经存储了与虚拟桶数量匹配的重分发桶信息 thisbucketsize = bucket->redistribute.bucket_size; *ndistinct = bucket->redistribute.ndistinct; } } else if (((StreamPath*)inner)->type == STREAM_BROADCAST) { + // 如果流路径类型是广播 if (bucket->broadcast.nbuckets == virtualbuckets) { + // 如果已经存储了与虚拟桶数量匹配的广播桶信息 thisbucketsize = bucket->broadcast.bucket_size; *ndistinct = bucket->broadcast.ndistinct; } } } - /* Now we don't have cache for smp, should calculate every time */ if (inner_path->dop > 1) { + // 如果内部路径的并行度大于 1 thisbucketsize = estimate_hash_bucketsize(root, left ? get_leftop(restrictinfo->clause) : get_rightop(restrictinfo->clause), virtualbuckets, @@ -3799,7 +4038,7 @@ Selectivity compute_bucket_size(PlannerInfo* root, RestrictInfo* restrictinfo, d sjinfo, ndistinct); } else if (thisbucketsize < 0) { - /* not cached yet */ + // 如果桶大小仍然未知 thisbucketsize = estimate_hash_bucketsize(root, left ? get_leftop(restrictinfo->clause) : get_rightop(restrictinfo->clause), virtualbuckets, @@ -3807,17 +4046,22 @@ Selectivity compute_bucket_size(PlannerInfo* root, RestrictInfo* restrictinfo, d sjinfo, ndistinct); if (!IsA(inner_path, StreamPath)) { + // 如果内部路径不是流路径 if (!IsA(inner_path, HashPath)) { + // 如果内部路径不是哈希路径 bucket->normal.nbuckets = virtualbuckets; bucket->normal.bucket_size = thisbucketsize; bucket->normal.ndistinct = *ndistinct; } } else { + // 如果内部路径是流路径 if (((StreamPath*)inner)->type == STREAM_REDISTRIBUTE) { + // 如果流路径类型是重分发 bucket->redistribute.nbuckets = virtualbuckets; bucket->redistribute.bucket_size = thisbucketsize; bucket->redistribute.ndistinct = *ndistinct; } else if (((StreamPath*)inner)->type == STREAM_BROADCAST) { + // 如果流路径类型是广播 bucket->broadcast.nbuckets = virtualbuckets; bucket->broadcast.bucket_size = thisbucketsize; bucket->broadcast.ndistinct = *ndistinct; @@ -3825,9 +4069,10 @@ Selectivity compute_bucket_size(PlannerInfo* root, RestrictInfo* restrictinfo, d } } - return thisbucketsize; + return thisbucketsize; // 返回计算得到的桶大小 } + /* * final_cost_hashjoin * Final estimate of the cost and result size of a hashjoin path. @@ -3840,33 +4085,36 @@ Selectivity compute_bucket_size(PlannerInfo* root, RestrictInfo* restrictinfo, d * 'sjinfo' is extra info about the join for selectivity estimation * 'semifactors' contains valid data if path->jointype is SEMI or ANTI */ +// 计算哈希连接的最终成本 void final_cost_hashjoin(PlannerInfo* root, HashPath* path, JoinCostWorkspace* workspace, SpecialJoinInfo* sjinfo, SemiAntiJoinFactors* semifactors, bool hasalternative, int dop) { - Path* outer_path = path->jpath.outerjoinpath; - Path* inner_path = path->jpath.innerjoinpath; - double outer_path_rows = PATH_LOCAL_ROWS(outer_path) / dop; - double inner_path_rows = PATH_LOCAL_ROWS(inner_path) / dop; - List* hashclauses = path->path_hashclauses; - Cost startup_cost = workspace->startup_cost; - Cost run_cost = workspace->run_cost; - int numbuckets = workspace->numbuckets; - int numbatches = workspace->numbatches; - Cost cpu_per_tuple = 0.0; - QualCost hash_qual_cost; - QualCost qp_qual_cost; - double hashjointuples; - double virtualbuckets; - Selectivity innerbucketsize; - Selectivity outer_scan_ratio = 0.0; - ListCell* hcl = NULL; - ES_SELECTIVITY* es = NULL; - MemoryContext ExtendedStat = NULL; - MemoryContext oldcontext; - List* clauselist = hashclauses; - double innerdistinct = 1.0; - double tmp_distinct = 1.0; - double outerdistinct = 1.0; + Path* outer_path = path->jpath.outerjoinpath; // 外部路径 + Path* inner_path = path->jpath.innerjoinpath; // 内部路径 + double outer_path_rows = PATH_LOCAL_ROWS(outer_path) / dop; // 外部路径的行数 + double inner_path_rows = PATH_LOCAL_ROWS(inner_path) / dop; // 内部路径的行数 + List* hashclauses = path->path_hashclauses; // 哈希连接的连接条件列表 + Cost startup_cost = workspace->startup_cost; // 启动成本 + Cost run_cost = workspace->run_cost; // 运行成本 + int numbuckets = workspace->numbuckets; // 桶的数量 + int numbatches = workspace->numbatches; // 批处理的数量 + Cost cpu_per_tuple = 0.0; // 每个元组的 CPU 成本 + QualCost hash_qual_cost; // 哈希连接的条件成本 + QualCost qp_qual_cost; // 查询计划的条件成本 + double hashjointuples; // 哈希连接的元组数量 + double virtualbuckets; // 虚拟桶的数量 + Selectivity innerbucketsize; // 内部桶大小的估算 + Selectivity outer_scan_ratio = 0.0; // 外部扫描比例,初始化为0 + ListCell* hcl = NULL; // 哈希连接的连接条件列表的元素迭代器 + ES_SELECTIVITY* es = NULL; // 扩展统计信息的选择性 + MemoryContext ExtendedStat = NULL; // 扩展统计信息的内存上下文 + MemoryContext oldcontext; // 旧的内存上下文 + List* clauselist = hashclauses; // 哈希连接的连接条件列表 + double innerdistinct = 1.0; // 内部路径的不同值数量,初始化为1.0 + double tmp_distinct = 1.0; // 临时的不同值数量,初始化为1.0 + double outerdistinct = 1.0; // 外部路径的不同值数量,初始化为1.0 +} + /* Mark the path with the correct row estimate */ set_rel_path_rows(&path->jpath.path, path->jpath.path.parent, path->jpath.path.param_info); @@ -3906,103 +4154,120 @@ void final_cost_hashjoin(PlannerInfo* root, HashPath* path, JoinCostWorkspace* w innerbucketsize = 1.0; /* use extended statistics to calculate innerbucket size and outerbucketsize */ if (list_length(hashclauses) >= 2) { - ExtendedStat = AllocSetContextCreate(CurrentMemoryContext, - "ExtendedStat", - ALLOCSET_DEFAULT_MINSIZE, - ALLOCSET_DEFAULT_INITSIZE, - ALLOCSET_DEFAULT_MAXSIZE); - oldcontext = MemoryContextSwitchTo(ExtendedStat); - es = New(ExtendedStat) ES_SELECTIVITY(); - AssertEreport(root != NULL, + // 创建一个新的内存上下文ExtendedStat + ExtendedStat = AllocSetContextCreate(CurrentMemoryContext, + "ExtendedStat", + ALLOCSET_DEFAULT_MINSIZE, + ALLOCSET_DEFAULT_INITSIZE, + ALLOCSET_DEFAULT_MAXSIZE); + // 切换当前内存上下文到ExtendedStat上下文 + oldcontext = MemoryContextSwitchTo(ExtendedStat); + // 创建一个ES_SELECTIVITY对象es + es = New(ExtendedStat) ES_SELECTIVITY(); + // 断言root不为空 + AssertEreport(root != NULL, + MOD_OPT, + "The NULL PlannerInfo is not allowed." + "when estimating the cost and result size of a hashjoin path."); + // 计算选择性(selectivity),更新es中的信息 + (void)es->calculate_selectivity( + root, hashclauses, sjinfo, path->jpath.jointype, &path->jpath, ES_COMPUTEBUCKETSIZE); + // 将clauselist设置为es中未匹配的子句组 + clauselist = es->unmatched_clause_group; + // 切换回原来的内存上下文 + (void)MemoryContextSwitchTo(oldcontext); + + ListCell* bcl = NULL; + // 初始化outerbucketsize为1.0 + Selectivity outerbucketsize = 1.0; + // 遍历es中的bucketsize_list + foreach (bcl, es->bucketsize_list) { + es_bucketsize* es_bucket = (es_bucketsize*)lfirst(bcl); + if (bms_is_subset(es_bucket->left_relids, inner_path->parent->relids)) { + // 计算内部bucketsize并更新innerdistinct + innerbucketsize *= + es->estimate_hash_bucketsize(es_bucket, &tmp_distinct, true, inner_path, virtualbuckets); + innerdistinct *= tmp_distinct; + // 计算外部bucketsize并更新outerdistinct + outerbucketsize *= + es->estimate_hash_bucketsize(es_bucket, &tmp_distinct, false, inner_path, virtualbuckets); + outerdistinct *= tmp_distinct; + } else { + // 断言right_relids是inner_path的relids的子集 + AssertEreport(bms_is_subset(es_bucket->right_relids, inner_path->parent->relids), MOD_OPT, - "The NULL PlannerInfo is not allowed." + "The right relids is not subset of the relids of inner path's parent" "when estimating the cost and result size of a hashjoin path."); - (void)es->calculate_selectivity( - root, hashclauses, sjinfo, path->jpath.jointype, &path->jpath, ES_COMPUTEBUCKETSIZE); - clauselist = es->unmatched_clause_group; - (void)MemoryContextSwitchTo(oldcontext); - ListCell* bcl = NULL; - Selectivity outerbucketsize = 1.0; - foreach (bcl, es->bucketsize_list) { - es_bucketsize* es_bucket = (es_bucketsize*)lfirst(bcl); - if (bms_is_subset(es_bucket->left_relids, inner_path->parent->relids)) { - innerbucketsize *= - es->estimate_hash_bucketsize(es_bucket, &tmp_distinct, true, inner_path, virtualbuckets); - innerdistinct *= tmp_distinct; - outerbucketsize *= - es->estimate_hash_bucketsize(es_bucket, &tmp_distinct, false, inner_path, virtualbuckets); - outerdistinct *= tmp_distinct; - } else { - AssertEreport(bms_is_subset(es_bucket->right_relids, inner_path->parent->relids), - MOD_OPT, - "The right relids is not subset of the relids of inner path's parent" - "when estimating the cost and result size of a hashjoin path."); - - innerbucketsize *= - es->estimate_hash_bucketsize(es_bucket, &tmp_distinct, false, inner_path, virtualbuckets); - innerdistinct *= tmp_distinct; - outerbucketsize *= - es->estimate_hash_bucketsize(es_bucket, &tmp_distinct, true, inner_path, virtualbuckets); - outerdistinct *= tmp_distinct; - } - ereport(DEBUG2, - (errmodule(MOD_OPT_JOIN), - errmsg("[ES]innerbucketsize: %e, innerdistinct: %.0f", innerbucketsize, innerdistinct))); - ereport(DEBUG2, - (errmodule(MOD_OPT_JOIN), - errmsg("[ES]outerbucketsize: %e, outerdistinct: %.0f", outerbucketsize, outerdistinct))); - } - - if (innerdistinct < MIN_HASH_BUCKET_SIZE) { - /* cut bucket size to min size, unless there are few distinct value in outer path */ - outer_scan_ratio = Max(Min(innerdistinct / outerdistinct, 1.0), outer_scan_ratio); - - ereport(DEBUG2, - (errmodule(MOD_OPT_JOIN), - errmsg("[ES]outerdistinct: %f, outer_scan_ratio: %e", outerdistinct, outer_scan_ratio))); - } else - outer_scan_ratio = 1.0; + // 计算内部bucketsize并更新innerdistinct + innerbucketsize *= + es->estimate_hash_bucketsize(es_bucket, &tmp_distinct, false, inner_path, virtualbuckets); + innerdistinct *= tmp_distinct; + // 计算外部bucketsize并更新outerdistinct + outerbucketsize *= + es->estimate_hash_bucketsize(es_bucket, &tmp_distinct, true, inner_path, virtualbuckets); + outerdistinct *= tmp_distinct; } + // 打印内部和外部bucketsize及distinct信息 + ereport(DEBUG2, + (errmodule(MOD_OPT_JOIN), + errmsg("[ES]innerbucketsize: %e, innerdistinct: %.0f", innerbucketsize, innerdistinct))); + ereport(DEBUG2, + (errmodule(MOD_OPT_JOIN), + errmsg("[ES]outerbucketsize: %e, outerdistinct: %.0f", outerbucketsize, outerdistinct))); + } - int number_of_joinrels = 0; - Bitmapset* join_relids = NULL; - foreach (hcl, clauselist) { - RestrictInfo* restrictinfo = (RestrictInfo*)lfirst(hcl); - Selectivity thisbucketsize; - Node* outerkey = NULL; - innerdistinct = 1.0; + if (innerdistinct < MIN_HASH_BUCKET_SIZE) { + // 计算outer_scan_ratio作为innerdistinct与outerdistinct的比例 + outer_scan_ratio = Max(Min(innerdistinct / outerdistinct, 1.0), outer_scan_ratio); - AssertEreport(IsA(restrictinfo, RestrictInfo), - MOD_OPT, - "The nodeTag of restrictinfo is not T_RestrictInfo" - "when estimating the cost and result size of a hashjoin path."); + // 打印outerdistinct和outer_scan_ratio信息 + ereport(DEBUG2, + (errmodule(MOD_OPT_JOIN), + errmsg("[ES]outerdistinct: %f, outer_scan_ratio: %e", outerdistinct, outer_scan_ratio))); + } else + // 如果innerdistinct大于等于MIN_HASH_BUCKET_SIZE,则将outer_scan_ratio设置为1.0 + outer_scan_ratio = 1.0; +} - /* - * First we have to figure out which side of the hashjoin clause - * is the inner side. - * - * Since we tend to visit the same clauses over and over when - * planning a large query, we cache the bucketsize estimate in the - * RestrictInfo node to avoid repeated lookups of statistics. - */ - if (bms_is_subset(restrictinfo->right_relids, inner_path->parent->relids)) { - thisbucketsize = - compute_bucket_size(root, restrictinfo, virtualbuckets, inner_path, false, sjinfo, &innerdistinct); - outerkey = get_leftop(restrictinfo->clause); - } else { - AssertEreport(bms_is_subset(restrictinfo->left_relids, inner_path->parent->relids), - MOD_OPT, - "The left relids is not subset of the relids of inner path's parent" - "when estimating the cost and result size of a hashjoin path."); - thisbucketsize = - compute_bucket_size(root, restrictinfo, virtualbuckets, inner_path, true, sjinfo, &innerdistinct); - outerkey = get_rightop(restrictinfo->clause); - } +int number_of_joinrels = 0; +Bitmapset* join_relids = NULL; +foreach (hcl, clauselist) { + // 遍历clauselist中的每个RestrictInfo + RestrictInfo* restrictinfo = (RestrictInfo*)lfirst(hcl); + Selectivity thisbucketsize; + Node* outerkey = NULL; + innerdistinct = 1.0; + + // 断言restrictinfo的nodeTag是T_RestrictInfo + AssertEreport(IsA(restrictinfo, RestrictInfo), + MOD_OPT, + "The nodeTag of restrictinfo is not T_RestrictInfo" + "when estimating the cost and result size of a hashjoin path."); + + if (bms_is_subset(restrictinfo->right_relids, inner_path->parent->relids)) { + // 如果right_relids是inner_path的relids的子集,则计算内部bucketsize和更新innerdistinct + thisbucketsize = + compute_bucket_size(root, restrictinfo, virtualbuckets, inner_path, false, sjinfo, &innerdistinct); + outerkey = get_leftop(restrictinfo->clause); + } else { + // 断言left_relids是inner_path的relids的子集 + AssertEreport(bms_is_subset(restrictinfo->left_relids, inner_path->parent->relids), + MOD_OPT, + "The left relids is not subset of the relids of inner path's parent" + "when estimating the cost and result size of a hashjoin path."); + // 计算内部bucketsize和更新innerdistinct + thisbucketsize = + compute_bucket_size(root, restrictinfo, virtualbuckets, inner_path, true, sjinfo, &innerdistinct); + outerkey = get_rightop(restrictinfo->clause); + } + + // 打印内部bucketsize和innerdistinct信息 + ereport(DEBUG2, + (errmodule(MOD_OPT_JOIN), + errmsg("thisbucketsize: %e, innerdistinct: %.0f", thisbucketsize, innerdistinct))); +} - ereport(DEBUG2, - (errmodule(MOD_OPT_JOIN), - errmsg("thisbucketsize: %e, innerdistinct: %.0f", thisbucketsize, innerdistinct))); /* * Adjust outer scan ratio if bucket size is too big, since we have at least 32768 buckets. @@ -4049,34 +4314,47 @@ void final_cost_hashjoin(PlannerInfo* root, HashPath* path, JoinCostWorkspace* w * innerbucketsize could be too small if just multiply. For now, we have multi-column statistics and * will calculate innerbucketsize first with multi-column statistics, code above. */ - join_relids = bms_add_members(join_relids, restrictinfo->right_relids); - join_relids = bms_add_members(join_relids, restrictinfo->left_relids); - if (number_of_joinrels > 0 && number_of_joinrels == bms_num_members(join_relids)) { - /* There is no new rel added to the join_relids, which mean there could be some correlationship between - * clauses */ - Selectivity tmp_bucketsize = innerbucketsize * thisbucketsize; - innerbucketsize = Min(innerbucketsize * 0.75, thisbucketsize * 0.75); - innerbucketsize = Max(innerbucketsize, tmp_bucketsize); - ereport(DEBUG2, - (errmodule(MOD_OPT_JOIN), - errmsg("using fudge factor to fix innerbucket size: %e, tmp_bucketsize:%e", - innerbucketsize, - tmp_bucketsize))); - } else { - innerbucketsize *= thisbucketsize; - ereport(DEBUG2, (errmodule(MOD_OPT_JOIN), errmsg("multiplying innerbucket size: %e", innerbucketsize))); - } + // 将 restrictinfo 中的右侧关系的 relids 添加到 join_relids 中 +join_relids = bms_add_members(join_relids, restrictinfo->right_relids); - number_of_joinrels = bms_num_members(join_relids); - } +// 再将 restrictinfo 中的左侧关系的 relids 也添加到 join_relids 中 +join_relids = bms_add_members(join_relids, restrictinfo->left_relids); - if (join_relids != NULL) { - bms_free_ext(join_relids); - } - } +// 如果已经加入了关系且没有新的关系加入,说明可能有一些限制条件之间的关联关系 +if (number_of_joinrels > 0 && number_of_joinrels == bms_num_members(join_relids)) { + // 计算一个临时的 tmp_bucketsize,等于 innerbucketsize 乘以当前 thisbucketsize + Selectivity tmp_bucketsize = innerbucketsize * thisbucketsize; + + // 使用一个修正因子将 innerbucketsize 减小为原来的 75%,或者 thisbucketsize 的 75% 中的较大者 + innerbucketsize = Min(innerbucketsize * 0.75, thisbucketsize * 0.75); + innerbucketsize = Max(innerbucketsize, tmp_bucketsize); + + // 记录修正后的 innerbucketsize 以及 tmp_bucketsize 的值 ereport(DEBUG2, (errmodule(MOD_OPT_JOIN), - errmsg("innerbucketsize: %e, outer_scan_ratio:%e", innerbucketsize, outer_scan_ratio))); + errmsg("using fudge factor to fix innerbucket size: %e, tmp_bucketsize:%e", + innerbucketsize, + tmp_bucketsize))); +} else { + // 如果没有关联关系,直接将 innerbucketsize 与 thisbucketsize 相乘 + innerbucketsize *= thisbucketsize; + + // 记录 innerbucketsize 的值 + ereport(DEBUG2, (errmodule(MOD_OPT_JOIN), errmsg("multiplying innerbucket size: %e", innerbucketsize))); +} + +// 更新已经加入的关系数量 +number_of_joinrels = bms_num_members(join_relids); + +// 如果 join_relids 不为空,则释放它所占用的内存 +if (join_relids != NULL) { + bms_free_ext(join_relids); +} + +// 输出 innerbucketsize 和 outer_scan_ratio 的值,用于调试和日志记录 +ereport(DEBUG2, + (errmodule(MOD_OPT_JOIN), + errmsg("innerbucketsize: %e, outer_scan_ratio:%e", innerbucketsize, outer_scan_ratio))); /* * add some restrition for innerbucketsize: @@ -4122,20 +4400,31 @@ void final_cost_hashjoin(PlannerInfo* root, HashPath* path, JoinCostWorkspace* w * to clamp inner_scan_frac to at most 1.0; but since match_count is * at least 1, no such clamp is needed now.) */ - outer_matched_rows = rint(outer_path_rows * semifactors->outer_match_frac); - inner_scan_frac = 2.0 / (semifactors->match_count + 1.0); + // 计算外部匹配的行数,取外部行数的一部分,这是通过将外部行数乘以匹配分数来完成的 +outer_matched_rows = rint(outer_path_rows * semifactors->outer_match_frac); + +// 计算内部扫描的分数,这是通过 2 除以(匹配计数加 1)来完成的 +inner_scan_frac = 2.0 / (semifactors->match_count + 1.0); + +// 将匹配条件的启动成本加到启动成本中 +startup_cost += hash_qual_cost.startup; + +// 计算匹配成本,考虑到匹配条件的每个元组的成本,以及外部匹配的行数和内部扫描的分数 +double matching_cost = hash_qual_cost.per_tuple * clamp_row_est(outer_matched_rows * outer_scan_ratio) * + clamp_row_est(inner_path_rows * innerbucketsize * inner_scan_frac) * 0.5; + +// 将匹配成本添加到运行成本中 +run_cost += matching_cost; + +// 输出匹配成本以及其他相关值,用于调试和日志记录 +ereport(DEBUG2, + (errmodule(MOD_OPT_JOIN), + errmsg("matching_cost: %e, hash_qual_cost.per_tuple: %e, outer_matched_rows: %e, inner_scan_frac:%e", + matching_cost, + hash_qual_cost.per_tuple, + outer_matched_rows, + inner_scan_frac))); - startup_cost += hash_qual_cost.startup; - double matching_cost = hash_qual_cost.per_tuple * clamp_row_est(outer_matched_rows * outer_scan_ratio) * - clamp_row_est(inner_path_rows * innerbucketsize * inner_scan_frac) * 0.5; - run_cost += matching_cost; - ereport(DEBUG2, - (errmodule(MOD_OPT_JOIN), - errmsg("matching_cost: %e, hash_qual_cost.per_tuple: %e, outer_matched_rows: %e, inner_scan_frac:%e", - matching_cost, - hash_qual_cost.per_tuple, - outer_matched_rows, - inner_scan_frac))); /* * For unmatched outer-rel rows, the picture is quite a lot different. @@ -4150,30 +4439,34 @@ void final_cost_hashjoin(PlannerInfo* root, HashPath* path, JoinCostWorkspace* w * effective cost per bucket entry is one-tenth what it is for * matchable tuples. */ - run_cost += hash_qual_cost.per_tuple * - clamp_row_est((outer_path_rows - outer_matched_rows) * outer_scan_ratio) * - clamp_row_est(inner_path_rows / virtualbuckets) * 0.05; + // 增加 CPU 成本,考虑匹配的行数、外部扫描比率和内部虚拟桶数 +run_cost += hash_qual_cost.per_tuple * + clamp_row_est((outer_path_rows - outer_matched_rows) * outer_scan_ratio) * + clamp_row_est(inner_path_rows / virtualbuckets) * 0.05; - ereport(DEBUG2, - (errmodule(MOD_OPT_JOIN), - errmsg("Add cpu cost: startup_cost: %lf, run_cost: %lf", startup_cost, run_cost))); +// 输出 CPU 成本,用于调试和日志记录 +ereport(DEBUG2, + (errmodule(MOD_OPT_JOIN), + errmsg("Add cpu cost: startup_cost: %lf, run_cost: %lf", startup_cost, run_cost))); - /* Get # of tuples that will pass the basic join */ - if (path->jpath.jointype == JOIN_SEMI) - hashjointuples = outer_matched_rows; - else - hashjointuples = outer_path_rows - outer_matched_rows; +// 如果连接类型为 SEMI,hashjointuples 设置为外部匹配的行数,否则为外部行数减去外部匹配的行数 +if (path->jpath.jointype == JOIN_SEMI) + hashjointuples = outer_matched_rows; +else + hashjointuples = outer_path_rows - outer_matched_rows; + +// 输出相关值,用于调试和日志记录 +ereport(DEBUG1, + (errmodule(MOD_OPT_JOIN), + errmsg("hashjointuples=%.0f, outer_tuples=%.0f, inner_tuples=%.0f, outer_global_tuples=%.0f, " + "inner_global_tuples=%.0f, outer_matched_rows=%.10f", + hashjointuples, + outer_path_rows, + inner_path_rows, + outer_path->rows, + inner_path->rows, + outer_matched_rows))); - ereport(DEBUG1, - (errmodule(MOD_OPT_JOIN), - errmsg("hashjointuples=%.0f, outer_tuples=%.0f, inner_tuples=%.0f, outer_global_tuples=%.0f, " - "inner_global_tuples=%.0f, outer_matched_rows=%.10f", - hashjointuples, - outer_path_rows, - inner_path_rows, - outer_path->rows, - inner_path->rows, - outer_matched_rows))); } else if (path->jpath.jointype == JOIN_RIGHT_SEMI || path->jpath.jointype == JOIN_RIGHT_ANTI) { double outer_matched_rows, inner_matched_rows; @@ -4307,41 +4600,51 @@ void final_cost_hashjoin(PlannerInfo* root, HashPath* path, JoinCostWorkspace* w * clauses that are to be applied at the join. (This is pessimistic since * not all of the quals may get evaluated at each tuple.) */ - startup_cost += qp_qual_cost.startup; - cpu_per_tuple = u_sess->attr.attr_sql.cpu_tuple_cost + qp_qual_cost.per_tuple; - run_cost += cpu_per_tuple * hashjointuples; + // 增加启动成本,考虑限制条件的启动成本 +startup_cost += qp_qual_cost.startup; - ereport(DEBUG2, - (errmodule(MOD_OPT_JOIN), - errmsg("Add restriction clauses cost: startup_cost: %lf, run_cost: %lf", startup_cost, run_cost))); +// 计算每个元组的 CPU 成本,考虑 CPU 元组成本和限制条件的每个元组成本 +cpu_per_tuple = u_sess->attr.attr_sql.cpu_tuple_cost + qp_qual_cost.per_tuple; - path->jpath.path.startup_cost = startup_cost; - path->jpath.path.total_cost = startup_cost + run_cost; - path->jpath.path.stream_cost = inner_path->stream_cost; - path->joinRows = hashjointuples; +// 增加运行成本,考虑每个元组的 CPU 成本和哈希连接的元组数 +run_cost += cpu_per_tuple * hashjointuples; - if (!u_sess->attr.attr_sql.enable_hashjoin && hasalternative) - path->jpath.path.total_cost *= g_instance.cost_cxt.disable_cost_enlarge_factor; +// 输出相关成本信息,用于调试和日志记录 +ereport(DEBUG2, + (errmodule(MOD_OPT_JOIN), + errmsg("Add restriction clauses cost: startup_cost: %lf, run_cost: %lf", startup_cost, run_cost))); - ereport(DEBUG2, - (errmodule(MOD_OPT_JOIN), - errmsg("Add cpu cost: stream_cost: %lf, startup_cost: %lf, total_cost: %lf", - path->jpath.path.stream_cost, - path->jpath.path.startup_cost, - path->jpath.path.total_cost))); +// 设置路径的启动成本、总成本和流成本 +path->jpath.path.startup_cost = startup_cost; +path->jpath.path.total_cost = startup_cost + run_cost; +path->jpath.path.stream_cost = inner_path->stream_cost; +path->joinRows = hashjointuples; - copy_mem_info(&path->mem_info, &workspace->inner_mem_info); +// 如果禁用哈希连接并且有替代路径,则根据禁用因子调整总成本 +if (!u_sess->attr.attr_sql.enable_hashjoin && hasalternative) + path->jpath.path.total_cost *= g_instance.cost_cxt.disable_cost_enlarge_factor; - debug_print_hashjoin_detail( - root, path, virtualbuckets, innerbucketsize, outer_scan_ratio, startup_cost, startup_cost + run_cost); +// 输出相关成本信息,用于调试和日志记录 +ereport(DEBUG2, + (errmodule(MOD_OPT_JOIN), + errmsg("Add cpu cost: stream_cost: %lf, startup_cost: %lf, total_cost: %lf", + path->jpath.path.stream_cost, + path->jpath.path.startup_cost, + path->jpath.path.total_cost))); - /* free space used by extended statistic */ - if (es != NULL) { - clauselist = NIL; - list_free_ext(es->unmatched_clause_group); - delete es; - MemoryContextDelete(ExtendedStat); - } +// 复制内存信息 +copy_mem_info(&path->mem_info, &workspace->inner_mem_info); + +// 调试打印哈希连接的详细信息 +debug_print_hashjoin_detail( + root, path, virtualbuckets, innerbucketsize, outer_scan_ratio, startup_cost, startup_cost + run_cost); + +// 清理扩展统计信息的内存和资源 +if (es != NULL) { + clauselist = NIL; + list_free_ext(es->unmatched_clause_group); + delete es; + MemoryContextDelete(ExtendedStat); } /* @@ -4525,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 @@ -4618,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 @@ -4676,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); } /* @@ -4787,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 @@ -4919,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 @@ -5003,21 +5313,33 @@ bool has_indexed_join_quals(NestPath* joinpath) * output tuples are generated and passed through qpqual checking, it * seems OK to live with the approximation. */ -double approx_tuple_count(PlannerInfo* root, JoinPath* path, List* quals) -{ - double tuples; - double outer_global_tuples = path->outerjoinpath->rows; - double inner_global_tuples = path->innerjoinpath->rows; - double outer_tuples = PATH_LOCAL_ROWS(path->outerjoinpath); - double inner_tuples = PATH_LOCAL_ROWS(path->innerjoinpath); - SpecialJoinInfo sjinfo; - Selectivity selec = 1.0; - ListCell* l = NULL; - int dop = SET_DOP(path->path.dop); - List* qual_list = quals; - ES_SELECTIVITY* es = NULL; - MemoryContext ExtendedStat = NULL; - MemoryContext oldcontext; +// 创建一个变量来存储估算的元组数量 +double tuples; +// 获取外部连接路径的全局元组数量 +double outer_global_tuples = path->outerjoinpath->rows; +// 获取内部连接路径的全局元组数量 +double inner_global_tuples = path->innerjoinpath->rows; +// 获取外部连接路径的本地元组数量 +double outer_tuples = PATH_LOCAL_ROWS(path->outerjoinpath); +// 获取内部连接路径的本地元组数量 +double inner_tuples = PATH_LOCAL_ROWS(path->innerjoinpath); +// 创建一个用于特殊连接信息的结构体 +SpecialJoinInfo sjinfo; +// 初始化选择度为1.0 +Selectivity selec = 1.0; +// 用于遍历限制条件列表的迭代器 +ListCell* l = NULL; +// 获取连接的并行度(Degree of Parallelism) +int dop = SET_DOP(path->path.dop); +// 存储限制条件的列表 +List* qual_list = quals; +// 用于存储扩展统计信息的结构体 +ES_SELECTIVITY* es = NULL; +// 存储扩展统计信息的内存上下文 +MemoryContext ExtendedStat = NULL; +// 用于存储旧的内存上下文 +MemoryContext oldcontext; + /* * Make up a SpecialJoinInfo for JOIN_INNER semantics. @@ -5035,23 +5357,32 @@ double approx_tuple_count(PlannerInfo* root, JoinPath* path, List* quals) sjinfo.varratio_cached = true; /* initialize es_selectivity class */ - if (list_length(qual_list) >= 2) { - ExtendedStat = AllocSetContextCreate(CurrentMemoryContext, - "ExtendedStat", - ALLOCSET_DEFAULT_MINSIZE, - ALLOCSET_DEFAULT_INITSIZE, - ALLOCSET_DEFAULT_MAXSIZE); - oldcontext = MemoryContextSwitchTo(ExtendedStat); - es = New(ExtendedStat) ES_SELECTIVITY(); - AssertEreport(root != NULL, - MOD_OPT, - "The NULL PlannerInfo is not allowed " - "when estimation the number of join rows passing a set of qual conditions approximately."); + // 检查限制条件列表的长度是否大于等于2 +if (list_length(qual_list) >= 2) { + // 创建一个用于扩展统计信息的内存上下文 + ExtendedStat = AllocSetContextCreate(CurrentMemoryContext, + "ExtendedStat", + ALLOCSET_DEFAULT_MINSIZE, + ALLOCSET_DEFAULT_INITSIZE, + ALLOCSET_DEFAULT_MAXSIZE); + // 切换到新创建的内存上下文 + oldcontext = MemoryContextSwitchTo(ExtendedStat); + // 创建一个用于存储扩展统计信息的结构体 + es = New(ExtendedStat) ES_SELECTIVITY(); + // 断言确保PlannerInfo不为空 + AssertEreport(root != NULL, + MOD_OPT, + "The NULL PlannerInfo is not allowed " + "when estimating the number of join rows passing a set of qual conditions approximately."); + + // 调用calculate_selectivity方法估算选择度 + selec = es->calculate_selectivity(root, qual_list, &sjinfo, JOIN_INNER, path, ES_EQJOINSEL); + // 更新限制条件列表,仅包含未匹配的子句 + qual_list = es->unmatched_clause_group; + // 切换回旧的内存上下文 + (void)MemoryContextSwitchTo(oldcontext); +} - selec = es->calculate_selectivity(root, qual_list, &sjinfo, JOIN_INNER, path, ES_EQJOINSEL); - qual_list = es->unmatched_clause_group; - (void)MemoryContextSwitchTo(oldcontext); - } /* Get the approximate selectivity */ foreach (l, qual_list) { @@ -5069,51 +5400,63 @@ double approx_tuple_count(PlannerInfo* root, JoinPath* path, List* quals) * if outerpath or innerpath is not stream, we should use global tuples for both. * When parallel broadcast or local broadcast, the inner_tuples refer to tuples in a DN, so we divide dop. */ - if (IsA(path->outerjoinpath, StreamPath) && (STREAM_BROADCAST == ((StreamPath*)path->outerjoinpath)->type)) { - outer_global_tuples = outer_tuples / dop; - } else if (IsA(path->innerjoinpath, StreamPath) && - (((StreamPath*)path->innerjoinpath)->type == STREAM_BROADCAST)) { - inner_global_tuples = inner_tuples / dop; - } else if (IsA(path->outerjoinpath, StreamPath) && ((StreamPath*)path->outerjoinpath)->smpDesc && - ((StreamPath*)path->outerjoinpath)->smpDesc->distriType == LOCAL_BROADCAST) { - outer_global_tuples = outer_global_tuples / dop; - } else if (IsA(path->innerjoinpath, StreamPath) && ((StreamPath*)path->innerjoinpath)->smpDesc && - ((StreamPath*)path->innerjoinpath)->smpDesc->distriType == LOCAL_BROADCAST) { - inner_global_tuples = inner_global_tuples / dop; - } - - tuples = selec * outer_global_tuples * inner_global_tuples; - /* estimate local relation sizes. */ - tuples = get_local_rows(tuples, - path->path.multiple, - IsLocatorReplicated(path->path.locator_type), - ng_get_dest_num_data_nodes(&path->path)) / - dop; - } else - tuples = selec * outer_tuples * inner_tuples; - - /* free space used by extended statistic */ - if (es != NULL) { - qual_list = NIL; - list_free_ext(es->unmatched_clause_group); - delete es; - MemoryContextDelete(ExtendedStat); - } - - ereport(DEBUG1, - (errmodule(MOD_OPT_JOIN), - errmsg("hashjointuples=%.0f, outer_tuples=%.0f, inner_tuples=%.0f, outer_global_tuples=%.0f, " - "inner_global_tuples=%.0f, selec=%.10f, multiple=%.0f,", - tuples, - outer_tuples, - inner_tuples, - outer_global_tuples, - inner_global_tuples, - selec, - path->path.multiple))); - return clamp_row_est(tuples); + // 检查外部和内部连接路径是否是流式路径,并且连接类型是STREAM_BROADCAST +if (IsA(path->outerjoinpath, StreamPath) && (STREAM_BROADCAST == ((StreamPath*)path->outerjoinpath)->type)) { + // 如果是,将外部连接的全局元组数除以并行度(dop)来估算本地元组数 + outer_global_tuples = outer_tuples / dop; +} else if (IsA(path->innerjoinpath, StreamPath) && + (((StreamPath*)path->innerjoinpath)->type == STREAM_BROADCAST)) { + // 如果内部连接是流式路径,并且连接类型是STREAM_BROADCAST,也将内部连接的全局元组数除以并行度 + inner_global_tuples = inner_tuples / dop; +} else if (IsA(path->outerjoinpath, StreamPath) && ((StreamPath*)path->outerjoinpath)->smpDesc && + ((StreamPath*)path->outerjoinpath)->smpDesc->distriType == LOCAL_BROADCAST) { + // 如果外部连接是流式路径,并且具有本地广播分布,则将外部连接的全局元组数除以并行度 + outer_global_tuples = outer_global_tuples / dop; +} else if (IsA(path->innerjoinpath, StreamPath) && ((StreamPath*)path->innerjoinpath)->smpDesc && + ((StreamPath*)path->innerjoinpath)->smpDesc->distriType == LOCAL_BROADCAST) { + // 如果内部连接是流式路径,并且具有本地广播分布,则将内部连接的全局元组数除以并行度 + inner_global_tuples = inner_global_tuples / dop; } +// 计算连接路径的元组数估算 +tuples = selec * outer_global_tuples * inner_global_tuples; + +// 如果连接路径需要本地元组数估算,则调用get_local_rows来估算 +if (IsLocatorReplicated(path->path.locator_type)) { + tuples = get_local_rows(tuples, + path->path.multiple, + IsLocatorReplicated(path->path.locator_type), + ng_get_dest_num_data_nodes(&path->path)) / + dop; +} else { + // 否则,使用全局元组数估算 + tuples = selec * outer_tuples * inner_tuples; +} + +// 如果存在扩展统计信息对象es,则清理相关资源 +if (es != NULL) { + qual_list = NIL; + list_free_ext(es->unmatched_clause_group); + delete es; + MemoryContextDelete(ExtendedStat); +} + +// 打印调试信息 +ereport(DEBUG1, + (errmodule(MOD_OPT_JOIN), + errmsg("hashjointuples=%.0f, outer_tuples=%.0f, inner_tuples=%.0f, outer_global_tuples=%.0f, " + "inner_global_tuples=%.0f, selec=%.10f, multiple=%.0f,", + tuples, + outer_tuples, + inner_tuples, + outer_global_tuples, + inner_global_tuples, + selec, + path->path.multiple))); + +// 返回经过限制的元组数估算 +return clamp_row_est(tuples); + } /* * set_baserel_size_estimates * Set the size estimates for the given base relation. @@ -5132,16 +5475,22 @@ void set_baserel_size_estimates(PlannerInfo* root, RelOptInfo* rel) double nrows; /* Should only be applied to base relations */ - AssertEreport( - rel->relid > 0, MOD_OPT, "The relid is invalid when set the size estimates for the given base relation."); + // 检查关系的 relid 是否有效 +AssertEreport( + rel->relid > 0, MOD_OPT, "The relid is invalid when set the size estimates for the given base relation."); - nrows = rel->tuples * clauselist_selectivity(root, rel->baserestrictinfo, 0, JOIN_INNER, NULL); +// 通过对基本关系的元组数和限制条件进行选择度估算来估算行数 +nrows = rel->tuples * clauselist_selectivity(root, rel->baserestrictinfo, 0, JOIN_INNER, NULL); - rel->rows = clamp_row_est(nrows); +// 使用 clamp_row_est 限制行数的估算值 +rel->rows = clamp_row_est(nrows); - cost_qual_eval(&rel->baserestrictcost, rel->baserestrictinfo, root); +// 计算基本关系的限制条件代价 +cost_qual_eval(&rel->baserestrictcost, rel->baserestrictinfo, root); + +// 设置基本关系的宽度估算 +set_rel_width(root, rel); - set_rel_width(root, rel); } /* @@ -5256,23 +5605,33 @@ void set_joinrel_size_estimates(PlannerInfo* root, RelOptInfo* rel, RelOptInfo* */ void set_joinpath_multiple_for_EC(PlannerInfo* root, Path* path, Path* outer_path, Path* inner_path) { - if (path == NULL || outer_path == NULL || inner_path == NULL || root == NULL) { - return; - } + // 检查传入的路径和规划信息是否有效 +if (path == NULL || outer_path == NULL || inner_path == NULL || root == NULL) { + return; +} - RangeTblEntry* rte = NULL; +RangeTblEntry* rte = NULL; - if (outer_path->pathtype == T_FunctionScan) { - rte = planner_rt_fetch(outer_path->parent->relid, root); - if (IS_EC_FUNC(rte)) { - path->multiple = outer_path->parent->multiple; - } - } else if (inner_path->pathtype == T_FunctionScan) { - rte = planner_rt_fetch(inner_path->parent->relid, root); - if (IS_EC_FUNC(rte)) { - path->multiple = inner_path->parent->multiple; - } +// 检查外部路径的类型是否为 FunctionScan +if (outer_path->pathtype == T_FunctionScan) { + // 获取外部路径关联的 RangeTblEntry(Range Table Entry) + rte = planner_rt_fetch(outer_path->parent->relid, root); + // 检查 RangeTblEntry 是否与 Equivalence Class 相关 + if (IS_EC_FUNC(rte)) { + // 如果是与 EC 相关的函数扫描,则将路径的 multiple 设置为外部路径的 multiple + path->multiple = outer_path->parent->multiple; } +} +// 如果外部路径不是 FunctionScan,再检查内部路径 +else if (inner_path->pathtype == T_FunctionScan) { + // 获取内部路径关联的 RangeTblEntry + rte = planner_rt_fetch(inner_path->parent->relid, root); + // 检查 RangeTblEntry 是否与 Equivalence Class 相关 + if (IS_EC_FUNC(rte)) { + // 如果是与 EC 相关的函数扫描,则将路径的 multiple 设置为内部路径的 multiple + path->multiple = inner_path->parent->multiple; + } +} return; } @@ -5379,44 +5738,50 @@ static double calc_joinrel_size_estimate(PlannerInfo* root, double outer_rows, d * For JOIN_SEMI and JOIN_ANTI, the selectivity is defined as the fraction * of LHS rows that have matches, and we apply that straightforwardly. */ - switch (jointype) { - case JOIN_INNER: - nrows = outer_rows * inner_rows * jselec; - break; - case JOIN_LEFT: - nrows = outer_rows * inner_rows * jselec; - if (nrows < outer_rows) - nrows = outer_rows; - nrows *= pselec; - break; - case JOIN_FULL: - nrows = outer_rows * inner_rows * jselec; - if (nrows < outer_rows) - nrows = outer_rows; - if (nrows < inner_rows) - nrows = inner_rows; - nrows *= pselec; - break; - case JOIN_SEMI: - nrows = outer_rows * jselec; - /* pselec not used */ - break; - case JOIN_ANTI: - case JOIN_LEFT_ANTI_FULL: - nrows = outer_rows * (1.0 - jselec); - nrows *= pselec; - break; - default: { - /* other values not expected here */ - ereport(ERROR, - (errmodule(MOD_OPT), - errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE), - errmsg("unrecognized join type when calculate joinrel size estimate: %d", (int)jointype))); - nrows = 0; /* keep compiler quiet */ - } break; - } + switch (jointype) { + case JOIN_INNER: + // 内连接(INNER JOIN)的行数估算 + nrows = outer_rows * inner_rows * jselec; + break; + case JOIN_LEFT: + // 左连接(LEFT JOIN)的行数估算 + nrows = outer_rows * inner_rows * jselec; + if (nrows < outer_rows) + nrows = outer_rows; + nrows *= pselec; + break; + case JOIN_FULL: + // 全外连接(FULL OUTER JOIN)的行数估算 + nrows = outer_rows * inner_rows * jselec; + if (nrows < outer_rows) + nrows = outer_rows; + if (nrows < inner_rows) + nrows = inner_rows; + nrows *= pselec; + break; + case JOIN_SEMI: + // 半连接(SEMI JOIN)的行数估算 + nrows = outer_rows * jselec; + break; + case JOIN_ANTI: + case JOIN_LEFT_ANTI_FULL: + // 反连接(ANTI JOIN)、左反连接(LEFT ANTI JOIN)和左全外连接(LEFT FULL ANTI JOIN)的行数估算 + nrows = outer_rows * (1.0 - jselec); + nrows *= pselec; + break; + default: { + // 处理未识别的连接类型 + ereport(ERROR, + (errmodule(MOD_OPT), + errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE), + errmsg("unrecognized join type when calculate joinrel size estimate: %d", (int)jointype))); + nrows = 0; // ���果连接类型未知,则将行数估算为0 + } break; +} - return clamp_row_est(nrows); +// 使用 clamp_row_est 函数确保行数估算不小于0 +return clamp_row_est(nrows); + } /* @@ -5431,29 +5796,38 @@ static double calc_joinrel_size_estimate(PlannerInfo* root, double outer_rows, d */ void set_subquery_size_estimates(PlannerInfo* root, RelOptInfo* rel) { - PlannerInfo* subroot = rel->subroot; - RangeTblEntry PG_USED_FOR_ASSERTS_ONLY* rte = NULL; + PlannerInfo* subroot = rel->subroot; // 子查询的规划信息 + RangeTblEntry PG_USED_FOR_ASSERTS_ONLY* rte = NULL; // 范围表(RangeTblEntry)项 ListCell* lc = NULL; - /* Should only be applied to base relations that are subqueries */ + // 断言:确保关系的 relid 大于0,表示这是一个有效的关系 AssertEreport(rel->relid > 0, MOD_OPT, "The relid is invalid when set the size estimates for a base relation that is a subquery."); + + // 获取与关系相关的范围表项(RangeTblEntry) rte = planner_rt_fetch(rel->relid, root); + + // 断言:只有在 FROM 子句中的子查询才支持这种操作 AssertEreport(rte->rtekind == RTE_SUBQUERY, MOD_OPT, "Only subquery in FROM clause can be supported" "when set the size estimates for a base relation that is a subquery."); - /* Copy raw number of output rows from subplan */ + // 如果子查询具有执行节点信息,确定定位器类型 if (rel->subplan->exec_nodes != NULL) rel->locator_type = rel->subplan->exec_nodes->baselocatortype; + + // 根据定位器类型来设置关系的行数估算值 if (rel->locator_type != LOCATOR_TYPE_REPLICATED) rel->tuples = rel->subplan->plan_rows; else rel->tuples = PLAN_LOCAL_ROWS(rel->subplan); + + // 设置关系的本地大小信息 set_local_rel_size(root, rel); + /* * Compute per-output-column width estimates by examining the subquery's * targetlist. For any output that is a plain Var, get the width estimate @@ -5671,31 +6045,41 @@ inline void set_rel_encode_info_if_vectorized(PlannerInfo *root, RelOptInfo *rel * The per-attribute width estimates are cached for possible re-use while * building join relations. */ +// 设置关系的宽度估计 void set_rel_width(PlannerInfo* root, RelOptInfo* rel) { + // 获取关系的OID Oid reloid = planner_rt_fetch(rel->relid, root)->relid; + // 初始化元组宽度为0 int32 tuple_width = 0; + // 初始化是否有整行变量的标志为false bool have_wholerow_var = false; + // 遍历关系的目标列表 ListCell* lc = NULL; foreach (lc, rel->reltargetlist) { Node* node = (Node*)lfirst(lc); + // 检查节点是否是一个变量 if (IsA(node, Var)) { Var* var = (Var*)node; int ndx; int32 item_width; + // 断言:变量的varno必须与关系的relid匹配 AssertEreport(var->varno == rel->relid, MOD_OPT, "The varno does not match to relid when setting the estimated output width of a base relation."); + // 断言:变量的varattno必须大于等于关系的最小属性 AssertEreport(var->varattno >= rel->min_attr, MOD_OPT, "The varattno is less than min_attr when setting the estimated output width of a base relation."); + // 断言:变量的varattno必须小于等于关系的最大属性 AssertEreport(var->varattno <= rel->max_attr, MOD_OPT, "The varattno is larger than max_attr when setting the estimated output width of a base relation."); + // 计算属性在关系中的索引 ndx = var->varattno - rel->min_attr; /* @@ -5718,31 +6102,42 @@ void set_rel_width(PlannerInfo* root, RelOptInfo* rel) } /* Try to get column width from statistics */ - if (reloid != InvalidOid && var->varattno > 0) { - Oid targetid = reloid; - bool ispartition = false; - RangeTblEntry* rte = planner_rt_fetch(rel->relid, root); + // 检查关系的OID是否有效,以及变量的varattno是否大于0 + if (reloid != InvalidOid && var->varattno > 0) { + Oid targetid = reloid; + bool ispartition = false; + RangeTblEntry* rte = planner_rt_fetch(rel->relid, root); - if (rte->isContainPartition) { - AssertEreport(OidIsValid(rte->partitionOid), - MOD_OPT, - "The partitionOid is invalid when setting the estimated output width of a base relation."); - targetid = rte->partitionOid; - ispartition = true; - } + // 如果关系包含分区,则使用分区OID作为目标ID + if (rte->isContainPartition) { + // 断言:分区OID必须有效 + AssertEreport(OidIsValid(rte->partitionOid), + MOD_OPT, + "The partitionOid is invalid when setting the estimated output width of a base relation."); + targetid = rte->partitionOid; + ispartition = true; + } - if (rte->isContainSubPartition) { - Assert(OidIsValid(rte->partitionOid)); - targetid = rte->subpartitionOid; - ispartition = true; - } + // 如果关系包含子分区,则使用子分区OID作为目标ID + if (rte->isContainSubPartition) { + // 断言:分区OID必须有效 + Assert(OidIsValid(rte->partitionOid)); + targetid = rte->subpartitionOid; + ispartition = true; + } + + // 获取属性的平均宽度 + item_width = get_attavgwidth(targetid, var->varattno, ispartition); + // 如果属性宽度大于0 + if (item_width > 0) { + // 将属性宽度存储在关系的attr_widths数组中 + rel->attr_widths[ndx] = item_width; + // 增加元组宽度 + tuple_width += item_width; + // 设置关系的编码信息(如果是矢量化计划) + set_rel_encode_info_if_vectorized(root, rel, var->vartype, item_width); + continue; - item_width = get_attavgwidth(targetid, var->varattno, ispartition); - if (item_width > 0) { - rel->attr_widths[ndx] = item_width; - tuple_width += item_width; - set_rel_encode_info_if_vectorized(root, rel, var->vartype, item_width); - continue; } } @@ -5758,7 +6153,7 @@ void set_rel_width(PlannerInfo* root, RelOptInfo* rel) rel->attr_widths[ndx] = item_width; tuple_width += item_width; set_rel_encode_info_if_vectorized(root, rel, var->vartype, item_width); - } else if (IsA(node, PlaceHolderVar)) { + } else if (IsA(node, PlaceHolderVar)) {// 如果变量不是Var类型,则进入这个分支 PlaceHolderVar* phv = (PlaceHolderVar*)node; PlaceHolderInfo* phinfo = find_placeholder_info(root, phv, false); @@ -5772,13 +6167,17 @@ void set_rel_width(PlannerInfo* root, RelOptInfo* rel) */ int32 item_width; - item_width = get_typavgwidth(exprType(node), exprTypmod(node)); - AssertEreport(item_width > 0, - MOD_OPT, - "The estimated average width of values of the type is not larger than 0" - "when setting the estimated output width of a base relation."); + // 获取节点的类型和类型修饰符,然后计算平均宽度 + item_width = get_typavgwidth(exprType(node), exprTypmod(node)); + // 断言:宽度必须大于0 + AssertEreport(item_width > 0, + MOD_OPT, + "The estimated average width of values of the type is not larger than 0" + "when setting the estimated output width of a base relation."); - tuple_width += item_width; + // 增加元组宽度 + tuple_width += item_width; + // 设置关系的编码信息(如果是矢量化计划) set_rel_encode_info_if_vectorized(root, rel, exprType(node), item_width); } } @@ -5787,7 +6186,8 @@ void set_rel_width(PlannerInfo* root, RelOptInfo* rel) * If we have a whole-row reference, estimate its width as the sum of * per-column widths plus sizeof(HeapTupleHeaderData). */ - if (have_wholerow_var) { + if (have_wholerow_var) { + // 如果有整行变量,计算整行的宽度 int32 wholerow_width = sizeof(HeapTupleHeaderData); if (reloid != InvalidOid) { @@ -5806,18 +6206,31 @@ void set_rel_width(PlannerInfo* root, RelOptInfo* rel) partid = rte->subpartitionOid; } - /* Real relation, so estimate true tuple width */ + // 获取关系数据的宽度,包括主表和分区 wholerow_width += get_relation_data_width(reloid, partid, rel->attr_widths - rel->min_attr); } else { - /* Do what we can with info for a phony rel */ + // 获取关系的宽度,包括所有属性 AttrNumber i; - for (i = 1; i <= rel->max_attr; i++) wholerow_width += rel->attr_widths[i - rel->min_attr]; } + // 将整行的宽度设置到关系的属性宽度数组中 rel->attr_widths[0 - rel->min_attr] = wholerow_width; + // 增加元组宽度 + tuple_width += wholerow_width; + } + + // 断言:元组宽度必须大于或等于0 + AssertEreport(tuple_width >= 0, + MOD_OPT, + "The estimated width of tuple is less than 0" + "when setting the estimated output width of a base relation."); + // 将元组宽度设置到关系的宽度属性中 + rel->width = tuple_width; + + /* * Include the whole-row Var as part of the output tuple. Yes, that * really is what happens at runtime. @@ -5851,32 +6264,35 @@ void set_rel_width(PlannerInfo* root, RelOptInfo* rel) */ double relation_byte_size(double tuples, int width, bool vectorized, bool aligned, bool issort, bool indexsort) { + // 断言:宽度必须大于或等于0 Assert(width >= 0); + + // 计算头部大小,根据是否排序和是否索引排序来选择不同的头部类型 size_t header_size = (issort && indexsort) ? sizeof(IndexTupleData) : sizeof(HeapTupleHeaderData); + if (aligned) { if (vectorized) + // 返回关系的字节大小,考虑了对齐 return tuples * (TUPLE_OVERHEAD(true) + width); else + // 返回关系的字节大小,考虑了对齐和分配块大小 return tuples * (TUPLE_OVERHEAD(issort) + alloc_trunk_size(MAXALIGN((uintptr_t)width) + MAXALIGN(header_size))); } else { + // 返回关系的字节大小,不考虑对齐 return tuples * (MAXALIGN((uintptr_t)width) + MAXALIGN(header_size)); } } -/* - * page_size - * Returns an estimate of the number of pages covered by a given - * number of tuples of a given width (size in bytes). - */ double page_size(double tuples, int width) { + // 计算关系所占用的页数 return ceil(relation_byte_size(tuples, width, false) / BLCKSZ); } -/* it used to compute page_size in createplan.cpp */ double cost_page_size(double tuples, int width) { + // 计算成本中的页数 return page_size(tuples, width); } @@ -5887,8 +6303,11 @@ double cost_page_size(double tuples, int width) */ void restore_hashjoin_cost(Path* path) { + // 如果启用了哈希连接成本修改并且路径是哈希路径 if (u_sess->attr.attr_sql.enable_change_hjcost && IsA(path, HashPath)) { + // 获取内部路径 Path* innerpath = ((HashPath*)path)->jpath.innerjoinpath; + // 恢复哈希连接的启动成本 path->startup_cost += innerpath->total_cost - innerpath->startup_cost; } } @@ -6011,34 +6430,42 @@ void hybrid_samplescangetsamplesize(PlannerInfo* root, RelOptInfo* baserel, List ListCell* lc = NULL; uint16 i = 0; + // 断言:样本参数数量必须等于2 AssertEreport(SAMPLEARGSNUM == list_length(paramexprs), MOD_OPT, "The number of sample percentage info does not equal 2" "when setting the estimated output width of a base relation."); + foreach (lc, paramexprs) { Node* paramnode = (Node*)lfirst(lc); Node* pctnode = estimate_expression_value(root, paramnode); float4 samplefract = 0.0; + + // 检查是否成功估算了百分比值 if (likely(pctnode)) { samplefract = get_samplefract(pctnode); } else { + // 如果估算失败,报告错误 ereport(ERROR, (errcode(ERRCODE_UNEXPECTED_NULL_VALUE), errmsg("Fail to estimate expression value."))); } if (i == SYSTEM_SAMPLE) { - /* We'll visit a sample of the pages ... */ + /* 我们将访问页面的一部分... */ + // 更新关系的页面数,以考虑样本抽取 baserel->pages = clamp_row_est(baserel->pages * samplefract); } - /* ... and hopefully get a representative number of tuples from them */ + /* ... 并希望从中获得代表性的元组数量 */ + // 更新关系的元组数,以考虑样本抽取 baserel->tuples = clamp_row_est(baserel->tuples * samplefract); i++; } } + /* * copy_mem_info * copy OpMemInfo structure from source to dest @@ -6097,22 +6524,26 @@ bool has_complicate_hashkey(List* hashclauses, Relids inner_relids) { ListCell* lc = NULL; + // 遍历哈希关联条件列表 foreach (lc, hashclauses) { RestrictInfo* restrictinfo = (RestrictInfo*)lfirst(lc); Node* innerkey = NULL; + // 断言:条件节点必须是 RestrictInfo 类型 AssertEreport(IsA(restrictinfo, RestrictInfo), MOD_OPT, "The nodeTag of restrictinfo is not T_RestrictInfo" "when setting the estimated output width of a base relation."); /* - * First we have to figure out which side of the hashjoin clause - * is the inner side. + * 首先,我们需要确定哈希连接条件的哪一侧是内部侧。 + * 如果 restrictinfo 的右侧关系 ID 是 inner_relids 的子集, + * 那么 innerkey 就是 restrictinfo 条件的右操作数,否则就是左操作数。 */ if (bms_is_subset(restrictinfo->right_relids, inner_relids)) { innerkey = get_rightop(restrictinfo->clause); } else { + // 断言:左侧关系 ID 必须是 inner_relids 的子集 AssertEreport(bms_is_subset(restrictinfo->left_relids, inner_relids), MOD_OPT, "The left relids is not subset of the relids of inner side" @@ -6120,7 +6551,7 @@ bool has_complicate_hashkey(List* hashclauses, Relids inner_relids) innerkey = get_leftop(restrictinfo->clause); } - /* Judge if the inner key is simple var */ + /* 判断内部键是否为简单变量 */ if (innerkey != NULL && !IsA(innerkey, Var) && !(IsA(innerkey, RelabelType) && IsA(((RelabelType*)innerkey)->arg, Var))) return true; @@ -6129,6 +6560,7 @@ bool has_complicate_hashkey(List* hashclauses, Relids inner_relids) return false; } + /* * calc_distributekey_width * Optimizer will add distribute key in the targetlist if not found in plan @@ -6144,30 +6576,31 @@ bool has_complicate_hashkey(List* hashclauses, Relids inner_relids) */ static int calc_distributekey_width(Path* path, int* width, bool vectorized, bool aligned) { - int num = 0; + int num = 0; // 初始化计数器为0 ListCell* lc = NULL; - /* Only do this for redistribute stream, since only redistribute has distribute keys */ + /* 只对分发流执行此操作,因为只有分发流有分发键 */ if (IsA(path, StreamPath) && ((StreamPath*)path)->type == STREAM_REDISTRIBUTE) { - foreach (lc, path->distribute_keys) { + foreach (lc, path->distribute_keys) { // 遍历分发键列表 Node* node = (Node*)lfirst(lc); - if (!list_member(path->parent->reltargetlist, node)) { - num++; - int32 item_width = get_typavgwidth(exprType(node), exprTypmod(node)); + if (!list_member(path->parent->reltargetlist, node)) { // 检查节点是否不在目标列表中 + num++; // 增加计数器 + int32 item_width = get_typavgwidth(exprType(node), exprTypmod(node)); // 获取类型的平均宽度 AssertEreport(item_width > 0, MOD_OPT, "The item width is not larger than 0 when setting the estimated output width of a base relation."); if (vectorized) - *width += columnar_get_col_width(exprType(node), item_width, aligned); + *width += columnar_get_col_width(exprType(node), item_width, aligned); // 计算列宽度 else - *width += item_width; + *width += item_width; // 直接累加宽度 } } } - return num; + return num; // 返回计数器的值 } + /* * get_path_actual_total_width * In PG optimizer, only width of row engine is estimated, and it has @@ -6187,33 +6620,35 @@ static int calc_distributekey_width(Path* path, int* width, bool vectorized, boo */ int get_path_actual_total_width(Path* path, bool vectorized, OpType type, int newcol) { - int num_new_col = 0; - int width = 0; - bool aligned = (type >= OP_SORT); + int num_new_col = 0; // 用于记录新列的数量 + int width = 0; // 用于记录宽度 + bool aligned = (type >= OP_SORT); // 根据操作类型是否需要对齐 if (path->parent == NULL) { - return COL_TUPLE_WIDTH; + return COL_TUPLE_WIDTH; // 如果路径的父节点为空,则返回默认列元组宽度 } - /* For redistribute, we will add unmatched distribute key into targetlist, so count this */ + // 计算分发键的宽度,并将结果累加到 width 中 num_new_col = calc_distributekey_width(path, &width, vectorized, aligned); if (vectorized) { switch (type) { case OP_HASHJOIN: + // 计算哈希连接操作的宽度 width += path->parent->encodedwidth + SIZE_COL_VALUE * (list_length(path->parent->reltargetlist) + num_new_col + newcol); break; case OP_HASHAGG: + // 计算哈希聚合操作的宽度 width += path->parent->encodedwidth + TUPLE_OVERHEAD(true) + sizeof(void*) * 2 + SIZE_COL_VALUE * (list_length(path->parent->reltargetlist) + num_new_col + newcol); break; case OP_SORT: if (width != 0 || path->parent->encodednum != 0) - newcol += 1; + newcol += 1; // 如果排序操作,且宽度不为0或者已编码的列数不为0,增加新列 /* No need break here. */ case OP_MATERIAL: - /* don't know encoded width of each column, just average them for a rough estimation */ + /* 不知道每列的编码宽度,只是对它们进行粗略估算的平均值 */ if (path->parent->encodednum > 0) width += path->parent->encodednum * alloc_trunk_size(path->parent->encodedwidth / path->parent->encodednum); @@ -6223,48 +6658,54 @@ int get_path_actual_total_width(Path* path, bool vectorized, OpType type, int ne break; } } else { - width += path->parent->width; + width += path->parent->width; // 非矢量化操作,直接使用父节点的宽度 } - return width; + return width; // 返回计算得到的总宽度 } + /* * get_subqueryscan_stream_cost * get stream_cost of a subquery */ static Cost get_subqueryscan_stream_cost(Plan* subplan) { - Cost stream_cost = 0; + Cost stream_cost = 0; // 初始化流成本为0 if (subplan == NULL) - return stream_cost; + return stream_cost; // 如果子计划为空,直接返回0 switch (nodeTag(subplan)) { case T_HashJoin: case T_VecHashJoin: + // 对于哈希连接计划节点,递归调用左子树的流成本 stream_cost = get_subqueryscan_stream_cost(subplan->lefttree); break; case T_NestLoop: case T_VecNestLoop: + // 对于嵌套循环计划节点,递归调用右子树的流成本 stream_cost = get_subqueryscan_stream_cost(subplan->righttree); break; case T_MergeJoin: case T_VecMergeJoin: + // 对于归并连接计划节点,递归调用右子树的流成本 stream_cost = get_subqueryscan_stream_cost(subplan->righttree); break; case T_Stream: case T_VecStream: + // 对于流计划节点,流成本等于启动成本 stream_cost = subplan->startup_cost; break; default: + // 对于其他计划节点类型,递归调用左子树的流成本 stream_cost = get_subqueryscan_stream_cost(subplan->lefttree); break; } - return stream_cost; + return stream_cost; // 返回计算得到的流成本 } diff --git a/src/gausskernel/optimizer/path/equivclass.cpp b/src/gausskernel/optimizer/path/equivclass.cpp index ba1924521..bf47262af 100644 --- a/src/gausskernel/optimizer/path/equivclass.cpp +++ b/src/gausskernel/optimizer/path/equivclass.cpp @@ -86,36 +86,37 @@ static void generate_base_implied_quality_clause(PlannerInfo* root, RelOptInfo* * exploration, so we need not worry about whether we're in the right * memory context. */ +// 定义函数process_equivalence,用于处理等价类的限制条件 bool process_equivalence(PlannerInfo* root, RestrictInfo* restrictinfo, bool below_outer_join) { - Expr* clause = restrictinfo->clause; - Oid opno, collation, item1_type, item2_type; - Expr* item1 = NULL; - Expr* item2 = NULL; - Relids item1_relids, item2_relids, item1_nullable_relids, item2_nullable_relids; - List* opfamilies = NIL; - EquivalenceClass* ec1 = NULL; - EquivalenceClass* ec2 = NULL; - EquivalenceMember* em1 = NULL; - EquivalenceMember* em2 = NULL; - ListCell* lc1 = NULL; + Expr* clause = restrictinfo->clause; // 从restrictinfo中获取限制条件表达式 + Oid opno, collation, item1_type, item2_type; // 定义操作符、排序规则、操作数1类型和操作数2类型的OID + Expr* item1 = NULL; // 定义操作数1的表达式 + Expr* item2 = NULL; // 定义操作数2的表达式 + Relids item1_relids, item2_relids, item1_nullable_relids, item2_nullable_relids; // 定义操作数1和操作数2的关系集合 + List* opfamilies = NIL; // 定义操作符族列表 + EquivalenceClass* ec1 = NULL; // 定义等价类1 + EquivalenceClass* ec2 = NULL; // 定义等价类2 + EquivalenceMember* em1 = NULL; // 定义等价成员1 + EquivalenceMember* em2 = NULL; // 定义等价成员2 + ListCell* lc1 = NULL; // 定义链表迭代器 - /* Should not already be marked as having generated an eclass */ + // 确保限制信息尚未标记为生成等价类 AssertEreport(restrictinfo->left_ec == NULL, MOD_OPT, ""); AssertEreport(restrictinfo->right_ec == NULL, MOD_OPT, ""); - /* Reject if it is potentially postponable by security considerations */ + // 如果限制信息的安全级别大于0且不是无泄漏的,则拒绝处理 if (restrictinfo->security_level > 0 && !restrictinfo->leakproof) return false; - /* Extract info from given clause */ + // 从给定的限制条件中提取信息 AssertEreport(is_opclause(clause), MOD_OPT, ""); - opno = ((OpExpr*)clause)->opno; - collation = ((OpExpr*)clause)->inputcollid; - item1 = (Expr*)get_leftop(clause); - item2 = (Expr*)get_rightop(clause); - item1_relids = restrictinfo->left_relids; - item2_relids = restrictinfo->right_relids; + opno = ((OpExpr*)clause)->opno; // 获取操作符的OID + collation = ((OpExpr*)clause)->inputcollid; // 获取排序规则的OID + item1 = (Expr*)get_leftop(clause); // 获取操作数1的表达式 + item2 = (Expr*)get_rightop(clause); // 获取操作数2的表达式 + item1_relids = restrictinfo->left_relids; // 获取操作数1的关系集合 + item2_relids = restrictinfo->right_relids; // 获取操作数2的关系集合 /* * Ensure both input expressions expose the desired collation (their types @@ -237,17 +238,18 @@ bool process_equivalence(PlannerInfo* root, RestrictInfo* restrictinfo, bool bel } /* Sweep finished, what did we find? */ + // 继续处理等价类的逻辑,下面的代码没有提供完整,需要补充 if (ec1 != NULL && ec2 != NULL) { - /* If case 1, nothing to do, except add to sources */ + // 如果等价类1等于等价类2,则只需将限制信息添加到等价类1的来源列表 if (ec1 == ec2) { ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo); ec1->ec_below_outer_join = ec1->ec_below_outer_join || below_outer_join; ec1->ec_min_security = Min(ec1->ec_min_security, restrictinfo->security_level); ec1->ec_max_security = Max(ec1->ec_max_security, restrictinfo->security_level); - /* mark the RI as associated with this eclass */ + // 将限制信息标记为与等价类1关联 restrictinfo->left_ec = ec1; restrictinfo->right_ec = ec1; - /* mark the RI as usable with this pair of EMs */ + // 将限制信息标记为与一对等价成员关联 restrictinfo->left_em = em1; restrictinfo->right_em = em2; return true; @@ -260,90 +262,82 @@ bool process_equivalence(PlannerInfo* root, RestrictInfo* restrictinfo, bool bel * leave dangling pointers in existing PathKeys. We leave it behind * with a link so that the merged EC can be found. */ - ec1->ec_members = list_concat(ec1->ec_members, ec2->ec_members); - ec1->ec_sources = list_concat(ec1->ec_sources, ec2->ec_sources); - ec1->ec_derives = list_concat(ec1->ec_derives, ec2->ec_derives); - ec1->ec_relids = bms_join(ec1->ec_relids, ec2->ec_relids); - ec1->ec_has_const = ec1->ec_has_const || ec2->ec_has_const; - /* can't need to set has_volatile */ - ec1->ec_below_outer_join = ec1->ec_below_outer_join || ec2->ec_below_outer_join; - ec1->ec_min_security = Min(ec1->ec_min_security, ec2->ec_min_security); - ec1->ec_max_security = Max(ec1->ec_max_security, ec2->ec_max_security); - ec2->ec_merged = ec1; - root->eq_classes = list_delete_ptr(root->eq_classes, ec2); - /* just to avoid debugging confusion w/ dangling pointers: */ - ec2->ec_members = NIL; - ec2->ec_sources = NIL; - ec2->ec_derives = NIL; - ec2->ec_relids = NULL; - ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo); - ec1->ec_below_outer_join = ec1->ec_below_outer_join || below_outer_join; - ec1->ec_min_security = Min(ec1->ec_min_security, restrictinfo->security_level); - ec1->ec_max_security = Max(ec1->ec_max_security, restrictinfo->security_level); - /* mark the RI as associated with this eclass */ - restrictinfo->left_ec = ec1; - restrictinfo->right_ec = ec1; - /* mark the RI as usable with this pair of EMs */ - restrictinfo->left_em = em1; - restrictinfo->right_em = em2; - } else if (ec1 != NULL) { - /* Case 3: add item2 to ec1 */ - em2 = add_eq_member(ec1, item2, item2_relids, item2_nullable_relids, false, item2_type); - ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo); - ec1->ec_below_outer_join = ec1->ec_below_outer_join || below_outer_join; - ec1->ec_min_security = Min(ec1->ec_min_security, restrictinfo->security_level); - ec1->ec_max_security = Max(ec1->ec_max_security, restrictinfo->security_level); - /* mark the RI as associated with this eclass */ - restrictinfo->left_ec = ec1; - restrictinfo->right_ec = ec1; - /* mark the RI as usable with this pair of EMs */ - restrictinfo->left_em = em1; - restrictinfo->right_em = em2; - } else if (ec2 != NULL) { - /* Case 3: add item1 to ec2 */ - em1 = add_eq_member(ec2, item1, item1_relids, item1_nullable_relids, false, item1_type); - ec2->ec_sources = lappend(ec2->ec_sources, restrictinfo); - ec2->ec_below_outer_join = ec2->ec_below_outer_join || below_outer_join; - ec2->ec_min_security = Min(ec2->ec_min_security, restrictinfo->security_level); - ec2->ec_max_security = Max(ec2->ec_max_security, restrictinfo->security_level); - /* mark the RI as associated with this eclass */ - restrictinfo->left_ec = ec2; - restrictinfo->right_ec = ec2; - /* mark the RI as usable with this pair of EMs */ - restrictinfo->left_em = em1; - restrictinfo->right_em = em2; - } else { - /* Case 4: make a new, two-entry EC */ - EquivalenceClass* ec = makeNode(EquivalenceClass); - - ec->ec_opfamilies = opfamilies; - ec->ec_collation = collation; - ec->ec_members = NIL; - ec->ec_sources = list_make1(restrictinfo); - ec->ec_derives = NIL; - ec->ec_relids = NULL; - ec->ec_has_const = false; - ec->ec_has_volatile = false; - ec->ec_below_outer_join = below_outer_join; - ec->ec_broken = false; - ec->ec_sortref = 0; - ec->ec_min_security = restrictinfo->security_level; - ec->ec_max_security = restrictinfo->security_level; - ec->ec_merged = NULL; - em1 = add_eq_member(ec, item1, item1_relids, item1_nullable_relids, false, item1_type); - em2 = add_eq_member(ec, item2, item2_relids, item2_nullable_relids, false, item2_type); - - root->eq_classes = lappend(root->eq_classes, ec); - - /* mark the RI as associated with this eclass */ - restrictinfo->left_ec = ec; - restrictinfo->right_ec = ec; - /* mark the RI as usable with this pair of EMs */ - restrictinfo->left_em = em1; - restrictinfo->right_em = em2; - } - - return true; + // 合并等价类1和等价类2的成员、来源、衍生等信息 +ec1->ec_members = list_concat(ec1->ec_members, ec2->ec_members); +ec1->ec_sources = list_concat(ec1->ec_sources, ec2->ec_sources); +ec1->ec_derives = list_concat(ec1->ec_derives, ec2->ec_derives); +ec1->ec_relids = bms_join(ec1->ec_relids, ec2->ec_relids); +ec1->ec_has_const = ec1->ec_has_const || ec2->ec_has_const; +ec1->ec_below_outer_join = ec1->ec_below_outer_join || ec2->ec_below_outer_join; +ec1->ec_min_security = Min(ec1->ec_min_security, ec2->ec_min_security); +ec1->ec_max_security = Max(ec1->ec_max_security, ec2->ec_max_security); +ec2->ec_merged = ec1; // 标记等价类2已合并到等价类1 +root->eq_classes = list_delete_ptr(root->eq_classes, ec2); // 从根节点中删除等价类2 +ec2->ec_members = NIL; // 清空等价类2的成员列表 +ec2->ec_sources = NIL; // 清空等价类2的来源列表 +ec2->ec_derives = NIL; // 清空等价类2的衍生列表 +ec2->ec_relids = NULL; // 清空等价类2的关系集合 +// 将限制信息标记为与等价类1关联 +restrictinfo->left_ec = ec1; +restrictinfo->right_ec = ec1; +// 将限制信息标记为与一对等价成员关联 +restrictinfo->left_em = em1; +restrictinfo->right_em = em2; +} else if (ec1 != NULL) { + // Case 3: 将item2添加到等价类1 + em2 = add_eq_member(ec1, item2, item2_relids, item2_nullable_relids, false, item2_type); + ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo); + ec1->ec_below_outer_join = ec1->ec_below_outer_join || below_outer_join; + ec1->ec_min_security = Min(ec1->ec_min_security, restrictinfo->security_level); + ec1->ec_max_security = Max(ec1->ec_max_security, restrictinfo->security_level); + // 将限制信息标记为与等价类1关联 + restrictinfo->left_ec = ec1; + restrictinfo->right_ec = ec1; + // 将限制信息标记为与一对等价成员关联 + restrictinfo->left_em = em1; + restrictinfo->right_em = em2; +} else if (ec2 != NULL) { + // Case 3: 将item1添加到等价类2 + em1 = add_eq_member(ec2, item1, item1_relids, item1_nullable_relids, false, item1_type); + ec2->ec_sources = lappend(ec2->ec_sources, restrictinfo); + ec2->ec_below_outer_join = ec2->ec_below_outer_join || below_outer_join; + ec2->ec_min_security = Min(ec2->ec_min_security, restrictinfo->security_level); + ec2->ec_max_security = Max(ec2->ec_max_security, restrictinfo->security_level); + // 将限制信息标记为与等价类2关联 + restrictinfo->left_ec = ec2; + restrictinfo->right_ec = ec2; + // 将限制信息标记为与一对等价成员关联 + restrictinfo->left_em = em1; + restrictinfo->right_em = em2; +} else { + // Case 4: 创建一个新的两个成员的等价类 + EquivalenceClass* ec = makeNode(EquivalenceClass); + ec->ec_opfamilies = opfamilies; + ec->ec_collation = collation; + ec->ec_members = NIL; + ec->ec_sources = list_make1(restrictinfo); + ec->ec_derives = NIL; + ec->ec_relids = NULL; + ec->ec_has_const = false; + ec->ec_has_volatile = false; + ec->ec_below_outer_join = below_outer_join; + ec->ec_broken = false; + ec->ec_sortref = 0; + ec->ec_min_security = restrictinfo->security_level; + ec->ec_max_security = restrictinfo->security_level; + ec->ec_merged = NULL; + em1 = add_eq_member(ec, item1, item1_relids, item1_nullable_relids, false, item1_type); + em2 = add_eq_member(ec, item2, item2_relids, item2_nullable_relids, false, item2_type); + root->eq_classes = lappend(root->eq_classes, ec); // 将新创建的等价类添加到根节点中 + // 将限制信息标记为与新的等价类关联 + restrictinfo->left_ec = ec; + restrictinfo->right_ec = ec; + // 将限制信息标记为与一对等价成员关联 + restrictinfo->left_em = em1; + restrictinfo->right_em = em2; +} +// 返回true,表示处理成功 +return true; } /* @@ -661,65 +655,76 @@ void generate_base_implied_qualities(PlannerInfo* root) } } -static void generate_base_implied_quality_clause(PlannerInfo* root, RelOptInfo* rel, RestrictInfo* rinfo) -{ - ListCell* em_cell = NULL; - ListCell* ec_cell = NULL; +// 遍历等价类列表 +foreach (ec_cell, root->eq_classes) { + EquivalenceClass* ec = (EquivalenceClass*)lfirst(ec_cell); + List* src_list = NIL; - foreach (ec_cell, root->eq_classes) { - EquivalenceClass* ec = (EquivalenceClass*)lfirst(ec_cell); - List* src_list = NIL; + // 跳过成员数量少于等于1、有volatile函数的、或不包含rinfo中的relids的等价类 + if (list_length(ec->ec_members) <= 1 || ec->ec_has_volatile || + !bms_is_subset(rinfo->clause_relids, ec->ec_relids)) + continue; - if (list_length(ec->ec_members) <= 1 || ec->ec_has_volatile || - !bms_is_subset(rinfo->clause_relids, ec->ec_relids)) + // 遍历等价类成员列表 + foreach (em_cell, ec->ec_members) { + EquivalenceMember* em = (EquivalenceMember*)lfirst(em_cell); + + // 如果等价成员表达式与rinfo中的clause相等,跳过 + if (equal(em->em_expr, rinfo->clause)) continue; - foreach (em_cell, ec->ec_members) { - EquivalenceMember* em = (EquivalenceMember*)lfirst(em_cell); - - if (equal(em->em_expr, rinfo->clause)) - continue; - - if (em->em_is_const) - continue; - - if (!bms_is_subset(em->em_relids, rinfo->clause_relids)) - continue; - - if (check_node_clause((Node*)rinfo->clause, (Node*)em->em_expr)) - src_list = lappend(src_list, em->em_expr); - } - - if (NIL == src_list) + // 如果等价成员是常数,跳过 + if (em->em_is_const) continue; - foreach (em_cell, ec->ec_members) { - EquivalenceMember* em = (EquivalenceMember*)lfirst(em_cell); - Node* new_clause = NULL; - Relids relids = NULL; + // 如果等价成员的relids不是rinfo中的clause_relids的子集,跳过 + if (!bms_is_subset(em->em_relids, rinfo->clause_relids)) + continue; - if (BMS_SINGLETON != bms_membership(em->em_relids)) - continue; - - if (bms_equal(em->em_relids, rinfo->clause_relids)) - continue; - - if (list_member(src_list, em->em_expr)) - continue; - - new_clause = replace_node_clause_for_equality((Node*)rinfo->clause, src_list, (Node*)em->em_expr); - relids = pull_varnos(new_clause); - - if (!bms_equal(relids, em->em_relids)) - continue; - - process_implied_quality(root, new_clause, bms_copy(em->em_relids), ec->ec_below_outer_join); - } - - list_free_ext(src_list); + // 如果等价成员与rinfo中的clause有节点关联,将其添加到src_list + if (check_node_clause((Node*)rinfo->clause, (Node*)em->em_expr)) + src_list = lappend(src_list, em->em_expr); } + + // 如果src_list为空,跳过 + if (NIL == src_list) + continue; + + // 再次遍历等价类成员列表 + foreach (em_cell, ec->ec_members) { + EquivalenceMember* em = (EquivalenceMember*)lfirst(em_cell); + Node* new_clause = NULL; + Relids relids = NULL; + + // 如果等价成员的relids不是单一的,跳过 + if (BMS_SINGLETON != bms_membership(em->em_relids)) + continue; + + // 如果等价成员的relids与rinfo中的clause_relids相等,跳过 + if (bms_equal(em->em_relids, rinfo->clause_relids)) + continue; + + // 如果等价成员在src_list中,跳过 + if (list_member(src_list, em->em_expr)) + continue; + + // 使用等价成员替换rinfo中的clause中的表达式 + new_clause = replace_node_clause_for_equality((Node*)rinfo->clause, src_list, (Node*)em->em_expr); + relids = pull_varnos(new_clause); + + // 如果新生成的clause的relids与等价成员的relids相等,处理推导出的质量信息 + if (!bms_equal(relids, em->em_relids)) + continue; + + // 处理推导出的质量信息 + process_implied_quality(root, new_clause, bms_copy(em->em_relids), ec->ec_below_outer_join); + } + + // 释放src_list + list_free_ext(src_list); } + /* * generate_base_implied_equalities * Generate any restriction clauses that we can deduce from equivalence @@ -837,43 +842,53 @@ static void generate_base_implied_equalities_const(PlannerInfo* root, Equivalenc * machinery might be able to exclude relations on the basis of generated * "var = const" equalities, but "var = param" won't work for that. */ - foreach (lc, ec->ec_members) { - EquivalenceMember* cur_em = (EquivalenceMember*)lfirst(lc); + // 遍历等价类的成员列表 +foreach (lc, ec->ec_members) { + EquivalenceMember* cur_em = (EquivalenceMember*)lfirst(lc); - if (cur_em->em_is_const) { - const_em = cur_em; - if (IsA(cur_em->em_expr, Const)) - break; - } - } - AssertEreport(const_em != NULL, MOD_OPT, ""); - - /* Generate a derived equality against each other member */ - foreach (lc, ec->ec_members) { - EquivalenceMember* cur_em = (EquivalenceMember*)lfirst(lc); - Oid eq_op; - - AssertEreport(!cur_em->em_is_child, MOD_OPT, ""); /* no children yet */ - if (cur_em == const_em) - continue; - eq_op = select_equality_operator(ec, cur_em->em_datatype, const_em->em_datatype); - if (!OidIsValid(eq_op)) { - /* failed... */ - ec->ec_broken = true; + // 如果当前等价成员是常量,则保存在const_em中,并且如果是Const类型的表达式,就跳出循环 + if (cur_em->em_is_const) { + const_em = cur_em; + if (IsA(cur_em->em_expr, Const)) break; - } - process_implied_equality(root, - eq_op, - ec->ec_collation, - cur_em->em_expr, - const_em->em_expr, - bms_copy(ec->ec_relids), - bms_union(cur_em->em_nullable_relids, const_em->em_nullable_relids), - ec->ec_min_security, - ec->ec_below_outer_join, - cur_em->em_is_const); } } +// 断言确保已找到常量等价成员 +AssertEreport(const_em != NULL, MOD_OPT, ""); + +/* 为每个其他成员生成衍生的相等条件 */ +foreach (lc, ec->ec_members) { + EquivalenceMember* cur_em = (EquivalenceMember*)lfirst(lc); + Oid eq_op; + + // 断言确保当前等价成员不是子成员 + AssertEreport(!cur_em->em_is_child, MOD_OPT, ""); /* no children yet */ + + // 如果当前等价成员是常量,跳过 + if (cur_em == const_em) + continue; + + // 选择适当的相等运算符 + eq_op = select_equality_operator(ec, cur_em->em_datatype, const_em->em_datatype); + + // 如果未找到有效的相等运算符,将等价类标记为破损并跳出循环 + if (!OidIsValid(eq_op)) { + ec->ec_broken = true; + break; + } + + // 处理推导出的相等条件 + process_implied_equality(root, + eq_op, + ec->ec_collation, + cur_em->em_expr, + const_em->em_expr, + bms_copy(ec->ec_relids), + bms_union(cur_em->em_nullable_relids, const_em->em_nullable_relids), + ec->ec_min_security, + ec->ec_below_outer_join, + cur_em->em_is_const); +} /* * generate_base_implied_equalities when EC contains no pseudoconstants @@ -890,43 +905,60 @@ static void generate_base_implied_equalities_no_const(PlannerInfo* root, Equival * of derived clauses, but it's possible that it will fail when a * different ordering would succeed. */ - prev_ems = (EquivalenceMember**)palloc0(root->simple_rel_array_size * sizeof(EquivalenceMember*)); + // 为prev_ems数组分配内存,用于保存前一个等价成员 +prev_ems = (EquivalenceMember**)palloc0(root->simple_rel_array_size * sizeof(EquivalenceMember*)); - foreach (lc, ec->ec_members) { - EquivalenceMember* cur_em = (EquivalenceMember*)lfirst(lc); - int relid; +// 遍历等价类的成员列表 +foreach (lc, ec->ec_members) { + EquivalenceMember* cur_em = (EquivalenceMember*)lfirst(lc); + int relid; - AssertEreport(!cur_em->em_is_child, MOD_OPT, ""); /* no children yet */ - if (bms_membership(cur_em->em_relids) != BMS_SINGLETON) - continue; - relid = bms_singleton_member(cur_em->em_relids); - AssertEreport(relid < root->simple_rel_array_size, MOD_OPT, ""); + // 断言确保当前等价成员不是子成员 + AssertEreport(!cur_em->em_is_child, MOD_OPT, ""); /* no children yet */ - if (prev_ems[relid] != NULL) { - EquivalenceMember* prev_em = prev_ems[relid]; - Oid eq_op; + // 如果当前等价成员的关系标识不是单个关系,继续下一个成员 + if (bms_membership(cur_em->em_relids) != BMS_SINGLETON) + continue; - eq_op = select_equality_operator(ec, prev_em->em_datatype, cur_em->em_datatype); - if (!OidIsValid(eq_op)) { - /* failed... */ - ec->ec_broken = true; - break; - } - process_implied_equality(root, - eq_op, - ec->ec_collation, - prev_em->em_expr, - cur_em->em_expr, - bms_copy(ec->ec_relids), - bms_union(prev_em->em_nullable_relids, cur_em->em_nullable_relids), - ec->ec_min_security, - ec->ec_below_outer_join, - false); + // 获取关系标识 + relid = bms_singleton_member(cur_em->em_relids); + + // 断言确保关系标识在合法范围内 + AssertEreport(relid < root->simple_rel_array_size, MOD_OPT, ""); + + // 如果在prev_ems数组中找到前一个等价成员,则生成相等条件 + if (prev_ems[relid] != NULL) { + EquivalenceMember* prev_em = prev_ems[relid]; + Oid eq_op; + + // 选择适当的相等运算符 + eq_op = select_equality_operator(ec, prev_em->em_datatype, cur_em->em_datatype); + + // 如果未找到有效的相等运算符,将等价类标记为破损并跳出循环 + if (!OidIsValid(eq_op)) { + ec->ec_broken = true; + break; } - prev_ems[relid] = cur_em; + + // 处理推导出的相等条件 + process_implied_equality(root, + eq_op, + ec->ec_collation, + prev_em->em_expr, + cur_em->em_expr, + bms_copy(ec->ec_relids), + bms_union(prev_em->em_nullable_relids, cur_em->em_nullable_relids), + ec->ec_min_security, + ec->ec_below_outer_join, + false); } - pfree_ext(prev_ems); + // 将当前等价成员保存到prev_ems数组中 + prev_ems[relid] = cur_em; +} + +// 释放prev_ems数组的内存 +pfree_ext(prev_ems); /* * We also have to make sure that all the Vars used in the member clauses @@ -936,13 +968,18 @@ static void generate_base_implied_equalities_no_const(PlannerInfo* root, Equival * pre-analysis of which members we prefer to join, but it's no worse than * what happened in the pre-8.3 code. */ - foreach (lc, ec->ec_members) { - EquivalenceMember* cur_em = (EquivalenceMember*)lfirst(lc); - List* vars = pull_var_clause((Node*)cur_em->em_expr, PVC_RECURSE_AGGREGATES, PVC_INCLUDE_PLACEHOLDERS); + // 遍历等价类的成员列表 +foreach (lc, ec->ec_members) { + EquivalenceMember* cur_em = (EquivalenceMember*)lfirst(lc); - add_vars_to_targetlist(root, vars, ec->ec_relids, false); - list_free_ext(vars); - } + // 从当前等价成员的表达式中提取变量列表 + List* vars = pull_var_clause((Node*)cur_em->em_expr, PVC_RECURSE_AGGREGATES, PVC_INCLUDE_PLACEHOLDERS); + + // 将这些变量添加到目标列表中 + add_vars_to_targetlist(root, vars, ec->ec_relids, false); + + // 释放变量列表的内存 + list_free_ext(vars); } /* @@ -963,12 +1000,13 @@ static void generate_base_implied_equalities_broken(PlannerInfo* root, Equivalen { ListCell* lc = NULL; - foreach (lc, ec->ec_sources) { - RestrictInfo* restrictinfo = (RestrictInfo*)lfirst(lc); + // 遍历等价类的来源(可能是限制信息) +foreach (lc, ec->ec_sources) { + RestrictInfo* restrictinfo = (RestrictInfo*)lfirst(lc); - if (ec->ec_has_const || bms_membership(restrictinfo->required_relids) != BMS_MULTIPLE) - distribute_restrictinfo_to_rels(root, restrictinfo); - } + // 如果等价类包含常量,或者所需关系标识的成员不是多个关系,将限制信息分发给关系 + if (ec->ec_has_const || bms_membership(restrictinfo->required_relids) != BMS_MULTIPLE) + distribute_restrictinfo_to_rels(root, restrictinfo); } /* @@ -1021,6 +1059,7 @@ List* generate_join_implied_equalities( * generate_join_implied_equalities_for_ecs * As above, but consider only the listed ECs. */ +// 生成连接的等价条件并返回它们 List* generate_join_implied_equalities_for_ecs( PlannerInfo* root, List* eclasses, Relids join_relids, Relids outer_relids, RelOptInfo* inner_rel) { @@ -1031,13 +1070,13 @@ List* generate_join_implied_equalities_for_ecs( AppendRelInfo* inner_appinfo = NULL; ListCell* lc = NULL; - /* If inner rel is a child, extra setup work is needed */ + // 如果内部关系是子关系,需要额外的设置工作 if (inner_rel->reloptkind == RELOPT_OTHER_MEMBER_REL) { - /* Lookup parent->child translation data */ + // 查找父关系->子关系的转换数据 inner_appinfo = find_childrel_appendrelinfo(root, inner_rel); - /* Construct relids for the parent rel */ + // 构造父关系的关系标识 nominal_inner_relids = bms_make_singleton(inner_appinfo->parent_relid); - /* ECs will be marked with the parent's relid, not the child's */ + // 等价类将使用父关系的关系标识,而不是子关系的 nominal_join_relids = bms_union(outer_relids, nominal_inner_relids); } else { inner_appinfo = NULL; @@ -1045,11 +1084,12 @@ List* generate_join_implied_equalities_for_ecs( nominal_join_relids = join_relids; } + // 遍历等价类列表 foreach (lc, eclasses) { EquivalenceClass* ec = (EquivalenceClass*)lfirst(lc); List* sublist = NIL; - /* ECs containing consts do not need any further enforcement */ + // 包含常量的等价类不需要进一步的处理 #ifdef STREAMPLAN if (!IS_STREAM_PLAN && ec->ec_has_const) continue; @@ -1058,28 +1098,30 @@ List* generate_join_implied_equalities_for_ecs( continue; #endif - /* Single-member ECs won't generate any deductions */ + // 单个成员的等价类不会生成推导条件 if (list_length(ec->ec_members) <= 1) continue; - /* We can quickly ignore any that don't overlap the join, too */ + // 我们还可以快速忽略与连接不重叠的等价类 if (!bms_overlap(ec->ec_relids, nominal_join_relids)) continue; + // 如果等价类没有破损,生成正常的连接推导条件 if (!ec->ec_broken) sublist = generate_join_implied_equalities_normal(root, ec, join_relids, outer_relids, inner_relids); - /* Recover if we failed to generate required derived clauses */ + // 如果等价类破损,生成破损的连接推导条件 if (ec->ec_broken) sublist = generate_join_implied_equalities_broken( root, ec, nominal_join_relids, outer_relids, nominal_inner_relids, inner_appinfo); + // 将子列表连接到结果列表 result = list_concat(result, sublist); } + // 返回生成的等价条件列表 return result; } - /* * generate_join_implied_equalities for a still-valid EC */ @@ -1138,51 +1180,72 @@ List* generate_join_implied_equalities_normal( * hashjoinable. */ if (outer_members != NULL && inner_members != NULL) { - EquivalenceMember* best_outer_em = NULL; - EquivalenceMember* best_inner_em = NULL; - Oid best_eq_op = InvalidOid; - int best_score = -1; - RestrictInfo* rinfo = NULL; + EquivalenceMember* best_outer_em = NULL; + EquivalenceMember* best_inner_em = NULL; + Oid best_eq_op = InvalidOid; + int best_score = -1; + RestrictInfo* rinfo = NULL; - foreach (lc1, outer_members) { - EquivalenceMember* outer_em = (EquivalenceMember*)lfirst(lc1); - ListCell* lc2 = NULL; + // 遍历外部成员列表 + foreach (lc1, outer_members) { + EquivalenceMember* outer_em = (EquivalenceMember*)lfirst(lc1); + ListCell* lc2 = NULL; - foreach (lc2, inner_members) { - EquivalenceMember* inner_em = (EquivalenceMember*)lfirst(lc2); - Oid eq_op; - int score; + // 遍历内部成员列表 + foreach (lc2, inner_members) { + EquivalenceMember* inner_em = (EquivalenceMember*)lfirst(lc2); + Oid eq_op; + int score; - eq_op = select_equality_operator(ec, outer_em->em_datatype, inner_em->em_datatype); - if (!OidIsValid(eq_op)) - continue; - score = 0; - if (IsA(outer_em->em_expr, Var) || - (IsA(outer_em->em_expr, RelabelType) && IsA(((RelabelType*)outer_em->em_expr)->arg, Var))) - score++; - if (IsA(inner_em->em_expr, Var) || - (IsA(inner_em->em_expr, RelabelType) && IsA(((RelabelType*)inner_em->em_expr)->arg, Var))) - score++; - if (op_hashjoinable(eq_op, exprType((Node*)outer_em->em_expr))) - score++; - if (score > best_score) { - best_outer_em = outer_em; - best_inner_em = inner_em; - best_eq_op = eq_op; - best_score = score; - if (best_score == 3) - break; /* no need to look further */ - } + // 选择合适的等于操作符 + eq_op = select_equality_operator(ec, outer_em->em_datatype, inner_em->em_datatype); + if (!OidIsValid(eq_op)) + continue; + + // 计算分数 + score = 0; + + // 如果外部成员是Var类型或者是RelabelType包裹的Var类型,则增加分数 + if (IsA(outer_em->em_expr, Var) || + (IsA(outer_em->em_expr, RelabelType) && IsA(((RelabelType*)outer_em->em_expr)->arg, Var))) + score++; + + // 如果内部成员是Var类型或者是RelabelType包裹的Var类型,则增加分数 + if (IsA(inner_em->em_expr, Var) || + (IsA(inner_em->em_expr, RelabelType) && IsA(((RelabelType*)inner_em->em_expr)->arg, Var))) + score++; + + // 如果等于操作符支持哈希连接并且表达式的数据类型与外部成员相同,则增加分数 + if (op_hashjoinable(eq_op, exprType((Node*)outer_em->em_expr))) + score++; + + // 如果当前分数比之前的最佳分数更高,则更新最佳匹配 + if (score > best_score) { + best_outer_em = outer_em; + best_inner_em = inner_em; + best_eq_op = eq_op; + best_score = score; + + // 如果已经得到最高分数(3),则无需继续查找 + if (best_score == 3) + break; } - if (best_score == 3) - break; /* no need to look further */ - } - if (best_score < 0) { - /* failed... */ - ec->ec_broken = true; - return NIL; } + // 如果已经得到最高分数(3),则无需继续查找 + if (best_score == 3) + break; + } + + // 如果没有找到合适的等于操作符,标记等价类破损并返回空列表 + if (best_score < 0) { + /* failed... */ + ec->ec_broken = true; + return NIL; + } +} + + /* * Create clause, setting parent_ec to mark it as redundant with other * joinclauses @@ -1207,41 +1270,47 @@ List* generate_join_implied_equalities_normal( * For now, use the same left-to-right method used there. */ if (new_members != NULL) { - List* old_members = list_concat(outer_members, inner_members); - EquivalenceMember* prev_em = NULL; - RestrictInfo* rinfo = NULL; + // 将旧的成员列表合并为一个整体的成员列表 + List* old_members = list_concat(outer_members, inner_members); + EquivalenceMember* prev_em = NULL; + RestrictInfo* rinfo = NULL; - /* For now, arbitrarily take the first old_member as the one to use */ - if (old_members != NULL) - new_members = lappend(new_members, linitial(old_members)); + // 对于现在,任意选择第一个旧成员作为要使用的成员 + if (old_members != NULL) + new_members = lappend(new_members, linitial(old_members)); - foreach (lc1, new_members) { - EquivalenceMember* cur_em = (EquivalenceMember*)lfirst(lc1); + // 遍历新成员列表 + foreach (lc1, new_members) { + EquivalenceMember* cur_em = (EquivalenceMember*)lfirst(lc1); - if (prev_em != NULL) { - Oid eq_op; + if (prev_em != NULL) { + Oid eq_op; - eq_op = select_equality_operator(ec, prev_em->em_datatype, cur_em->em_datatype); - if (!OidIsValid(eq_op)) { - /* failed... */ - ec->ec_broken = true; - return NIL; - } - /* do NOT set parent_ec, this qual is not redundant! */ -#ifdef STREAMPLAN - if (!has_const) -#endif - { - rinfo = create_join_clause(root, ec, eq_op, prev_em, cur_em, NULL); - - result = lappend(result, rinfo); - } + // 选择适当的等于操作符 + eq_op = select_equality_operator(ec, prev_em->em_datatype, cur_em->em_datatype); + if (!OidIsValid(eq_op)) { + /* failed... */ + ec->ec_broken = true; + return NIL; } - prev_em = cur_em; - } - } - return result; + // 创建一个新的连接谓词信息 +#ifdef STREAMPLAN + if (!has_const) +#endif + { + rinfo = create_join_clause(root, ec, eq_op, prev_em, cur_em, NULL); + + // 将新的连接谓词信息添加到结果列表中 + result = lappend(result, rinfo); + } + } + prev_em = cur_em; + } +} + +return result; + } /* @@ -1262,11 +1331,19 @@ static List* generate_join_implied_equalities_broken(PlannerInfo* root, Equivale RestrictInfo* restrictinfo = (RestrictInfo*)lfirst(lc); Relids clause_relids = restrictinfo->required_relids; - if (bms_is_subset(clause_relids, nominal_join_relids) && !bms_is_subset(clause_relids, outer_relids) && + // 如果约束信息的关系标识符是nominal_join_relids的子集, + // 但不是outer_relids的子集,也不是nominal_inner_relids的子集, + // 则将该约束信息添加到结果列表中。 + if (bms_is_subset(clause_relids, nominal_join_relids) && + !bms_is_subset(clause_relids, outer_relids) && !bms_is_subset(clause_relids, nominal_inner_relids)) result = lappend(result, restrictinfo); } + // 返回生成的连接谓词信息列表 + +} + /* * If we have to translate, just brute-force apply adjust_appendrel_attrs * to all the RestrictInfos at once. This will result in returning @@ -1457,35 +1534,37 @@ static RestrictInfo* create_join_clause(PlannerInfo* root, EquivalenceClass* ec, */ void reconsider_outer_join_clauses(PlannerInfo* root) { - bool found = false; - ListCell* cell = NULL; - ListCell* prev = NULL; - ListCell* next = NULL; + bool found = false; // 标记是否找到推断的限制条件 + ListCell* cell = NULL; // 用于遍历链表的指针 + ListCell* prev = NULL; // 用于跟踪前一个链表元素 + ListCell* next = NULL; // 用于跟踪下一个链表元素 - /* Outer loop repeats until we find no more deductions */ + // 外部循环,重复直到没有找到更多推断的限制条件 do { - found = false; + found = false; // 每次循环开始前先重置found标志为false - /* Process the LEFT JOIN clauses */ - prev = NULL; + // 处理 LEFT JOIN 子句 + prev = NULL; // 初始化前一个链表元素为NULL for (cell = list_head(root->left_join_clauses); cell; cell = next) { - RestrictInfo* rinfo = (RestrictInfo*)lfirst(cell); + RestrictInfo* rinfo = (RestrictInfo*)lfirst(cell); // 获取当前链表元素的数据 + + next = lnext(cell); // 获取下一个链表元素的指针,防止在处理当前元素时移除它后出现问题 - next = lnext(cell); if (reconsider_outer_join_clause(root, rinfo, true)) { - found = true; - /* remove it from the list */ - root->left_join_clauses = list_delete_cell(root->left_join_clauses, cell, prev); - /* we throw it back anyway (see notes above) */ - /* but the thrown-back clause has no extra selectivity */ - rinfo->norm_selec = 2.0; - rinfo->outer_selec = 1.0; - distribute_restrictinfo_to_rels(root, rinfo); - } else - prev = cell; + found = true; // 如果限制条件需要重新考虑,设置found为true + + root->left_join_clauses = list_delete_cell(root->left_join_clauses, cell, prev); // 从列表中删除当前元素 + + rinfo->norm_selec = 2.0; // 设置推断的选择性(selectivity) + rinfo->outer_selec = 1.0; // 设置外部选择性 + + distribute_restrictinfo_to_rels(root, rinfo); // 将限制条件分发到相关的关系中 + } else { + prev = cell; // 如果不需要重新考虑,更新prev指针 + } } - /* Process the RIGHT JOIN clauses */ + // 处理 RIGHT JOIN 子句 prev = NULL; for (cell = list_head(root->right_join_clauses); cell; cell = next) { RestrictInfo* rinfo = (RestrictInfo*)lfirst(cell); @@ -1493,18 +1572,19 @@ void reconsider_outer_join_clauses(PlannerInfo* root) next = lnext(cell); if (reconsider_outer_join_clause(root, rinfo, false)) { found = true; - /* remove it from the list */ + root->right_join_clauses = list_delete_cell(root->right_join_clauses, cell, prev); - /* we throw it back anyway (see notes above) */ - /* but the thrown-back clause has no extra selectivity */ + + rinfo->norm_selec = 2.0; rinfo->outer_selec = 1.0; distribute_restrictinfo_to_rels(root, rinfo); - } else + } else { prev = cell; + } } - /* Process the FULL JOIN clauses */ + // 处理 FULL JOIN 子句 prev = NULL; for (cell = list_head(root->full_join_clauses); cell; cell = next) { RestrictInfo* rinfo = (RestrictInfo*)lfirst(cell); @@ -1512,19 +1592,20 @@ void reconsider_outer_join_clauses(PlannerInfo* root) next = lnext(cell); if (reconsider_full_join_clause(root, rinfo)) { found = true; - /* remove it from the list */ + root->full_join_clauses = list_delete_cell(root->full_join_clauses, cell, prev); - /* we throw it back anyway (see notes above) */ - /* but the thrown-back clause has no extra selectivity */ + + rinfo->norm_selec = 2.0; rinfo->outer_selec = 1.0; distribute_restrictinfo_to_rels(root, rinfo); - } else + } else { prev = cell; + } } - } while (found); + } while (found); // 如果在本轮循环中找到了新的推断条件,则继续下一轮循环 - /* Now, any remaining clauses have to be thrown back */ + // 现在,任何剩余的限制条件都需要重新添加到相关关系中 foreach (cell, root->left_join_clauses) { RestrictInfo* rinfo = (RestrictInfo*)lfirst(cell); @@ -1549,92 +1630,114 @@ void reconsider_outer_join_clauses(PlannerInfo* root) */ static bool reconsider_outer_join_clause(PlannerInfo* root, RestrictInfo* rinfo, bool outer_on_left) { - Expr* outervar = NULL; - Expr* innervar = NULL; - Oid opno, collation, left_type, right_type, inner_datatype; - Relids inner_relids, inner_nullable_relids; - ListCell* lc1 = NULL; + Expr* outervar = NULL; // 左侧变量 + Expr* innervar = NULL; // 右侧变量 + Oid opno, collation, left_type, right_type, inner_datatype; // 操作符号、排序规则、左侧数据类型、右侧数据类型、内部数据类型 + Relids inner_relids, inner_nullable_relids; // 内部关系标识、内部可空关系标识 + ListCell* lc1 = NULL; // 用于遍历链表的指针 - AssertEreport(is_opclause(rinfo->clause), MOD_OPT, ""); - opno = ((OpExpr*)rinfo->clause)->opno; - collation = ((OpExpr*)rinfo->clause)->inputcollid; + AssertEreport(is_opclause(rinfo->clause), MOD_OPT, ""); // 断言:限制条件必须是操作符表达式 + opno = ((OpExpr*)rinfo->clause)->opno; // 获取操作符号的OID + collation = ((OpExpr*)rinfo->clause)->inputcollid; // 获取排序规则的OID - /* If clause is outerjoin_delayed, operator must be strict */ + // 如果外连接延迟生效并且操作符不是严格的,则返回false,表示不需要重新考虑此限制条件 if (rinfo->outerjoin_delayed && !op_strict(opno)) return false; - /* Extract needed info from the clause */ + // 获取操作符的左侧和右侧数据类型 op_input_types(opno, &left_type, &right_type); + if (outer_on_left) { - outervar = (Expr*)get_leftop(rinfo->clause); - innervar = (Expr*)get_rightop(rinfo->clause); - inner_datatype = right_type; - inner_relids = rinfo->right_relids; + outervar = (Expr*)get_leftop(rinfo->clause); // 获取左侧变量 + innervar = (Expr*)get_rightop(rinfo->clause); // 获取右侧变量 + inner_datatype = right_type; // 内部数据类型为右侧数据类型 + inner_relids = rinfo->right_relids; // 内部关系标识为右侧关系标识 } else { - outervar = (Expr*)get_rightop(rinfo->clause); - innervar = (Expr*)get_leftop(rinfo->clause); - inner_datatype = left_type; - inner_relids = rinfo->left_relids; + outervar = (Expr*)get_rightop(rinfo->clause); // 获取右侧变量 + innervar = (Expr*)get_leftop(rinfo->clause); // 获取左侧变量 + inner_datatype = left_type; // 内部数据类型为左侧数据类型 + inner_relids = rinfo->left_relids; // 内部关系标识为左侧关系标识 } + + // 计算内部可空关系标识,它是内部关系标识与限制条件可空关系标识的交集 inner_nullable_relids = bms_intersect(inner_relids, rinfo->nullable_relids); - /* Scan EquivalenceClasses for a match to outervar */ + /* 扫描等价类(EquivalenceClasses)以查找与左侧变量匹配的等价类 */ foreach (lc1, root->eq_classes) { - EquivalenceClass* cur_ec = (EquivalenceClass*)lfirst(lc1); - bool match = false; + EquivalenceClass* cur_ec = (EquivalenceClass*)lfirst(lc1); // 获取当前等价类 + bool match = false; // 用于标记是否找到匹配的等价类 ListCell* lc2 = NULL; - /* Ignore EC unless it contains pseudoconstants */ + // 如果等价类包含常数,则继续下一轮循环 if (!cur_ec->ec_has_const) continue; - /* Never match to a volatile EC */ + + // 如果等价类包含不稳定的表达式(即无法静态计算的表达式),则继续下一轮循环 if (cur_ec->ec_has_volatile) continue; - /* It has to match the outer-join clause as to semantics, too */ + + // 如果排序规则不匹配,则继续下一轮循环 if (collation != cur_ec->ec_collation) continue; + + // 如果操作符家族不匹配,则继续下一轮循环 if (!equal(rinfo->mergeopfamilies, cur_ec->ec_opfamilies)) continue; - /* Does it contain a match to outervar? */ + + // 遍历等价类的成员以查找匹配的左侧变量 foreach (lc2, cur_ec->ec_members) { EquivalenceMember* cur_em = (EquivalenceMember*)lfirst(lc2); - AssertEreport(!cur_em->em_is_child, MOD_OPT, ""); /* no children yet */ - if (equal(outervar, cur_em->em_expr)) { + AssertEreport(!cur_em->em_is_child, MOD_OPT, ""); // 断言:等价成员不是子查询 + if (equal(outervar, cur_em->em_expr)) { // 如果找到匹配的左侧变量 match = true; break; } } + + // 如果找到匹配的等价类,则继续下一轮循环 if (!match) - continue; /* no match, so ignore this EC */ + continue;/* no match, so ignore this EC */ /* * Yes it does! Try to generate a clause INNERVAR = CONSTANT for each * CONSTANT in the EC. Note that we must succeed with at least one * constant before we can decide to throw away the outer-join clause. */ - match = false; + match = false; // 重置匹配标志为 false,因为我们现在在处理外连接子句中的限制条件 + + // 遍历等价类的成员,寻找与内部变量 innervar 匹配的等价成员 foreach (lc2, cur_ec->ec_members) { EquivalenceMember* cur_em = (EquivalenceMember*)lfirst(lc2); - Oid eq_op; - RestrictInfo* newrinfo = NULL; + Oid eq_op; // 用于存储选定的相等性操作符的 OID + RestrictInfo* newrinfo = NULL; // 用于存储生成的新限制条件 + // 忽略非常数成员,只考虑常数成员 if (!cur_em->em_is_const) - continue; /* ignore non-const members */ + continue; + + // 选择相等性操作符,该操作符将内部变量的类型与当前等价成员的类型进行比较 eq_op = select_equality_operator(cur_ec, inner_datatype, cur_em->em_datatype); + + // 如果未找到有效的相等性操作符,则继续下一轮循环 if (!OidIsValid(eq_op)) - continue; /* can't generate equality */ + continue; + + // 构建一个隐式的连接相等性限制条件 newrinfo = build_implied_join_equality(eq_op, cur_ec->ec_collation, innervar, cur_em->em_expr, - bms_copy(inner_relids), - bms_copy(inner_nullable_relids), + bms_copy(inner_relids), // 复制内部关系标识 + bms_copy(inner_nullable_relids), // 复制内部可空关系标识 cur_ec->ec_min_security); + + // 处理新生成的相等性限制条件,并将其添加到等价类中 if (process_equivalence(root, newrinfo, true)) match = true; } + /* * If we were able to equate INNERVAR to any constant, report success. * Otherwise, fall out of the search loop, since we know the OUTERVAR @@ -1654,50 +1757,48 @@ static bool reconsider_outer_join_clause(PlannerInfo* root, RestrictInfo* rinfo, * * Returns TRUE if we were able to propagate a constant through the clause. */ -static bool reconsider_full_join_clause(PlannerInfo* root, RestrictInfo* rinfo) -{ - Expr* leftvar = NULL; - Expr* rightvar = NULL; - Oid opno, collation, left_type, right_type; - Relids left_relids, right_relids, left_nullable_relids, right_nullable_relids; - ListCell* lc1 = NULL; - - /* Can't use an outerjoin_delayed clause here */ + /* 不能在这里使用 outerjoin_delayed 子句 */ if (rinfo->outerjoin_delayed) return false; - /* Extract needed info from the clause */ - AssertEreport(is_opclause(rinfo->clause), MOD_OPT, ""); - opno = ((OpExpr*)rinfo->clause)->opno; - collation = ((OpExpr*)rinfo->clause)->inputcollid; - op_input_types(opno, &left_type, &right_type); - leftvar = (Expr*)get_leftop(rinfo->clause); - rightvar = (Expr*)get_rightop(rinfo->clause); - left_relids = rinfo->left_relids; - right_relids = rinfo->right_relids; - left_nullable_relids = bms_intersect(left_relids, rinfo->nullable_relids); - right_nullable_relids = bms_intersect(right_relids, rinfo->nullable_relids); + /* 从子句中提取所需的信息 */ + AssertEreport(is_opclause(rinfo->clause), MOD_OPT, ""); // 确保子句是操作符子句 + opno = ((OpExpr*)rinfo->clause)->opno; // 获取操作符 OID + collation = ((OpExpr*)rinfo->clause)->inputcollid; // 获取协定 OID + op_input_types(opno, &left_type, &right_type); // 获取操作符的输入类型 + leftvar = (Expr*)get_leftop(rinfo->clause); // 获取左操作数 + rightvar = (Expr*)get_rightop(rinfo->clause); // 获取右操作数 + left_relids = rinfo->left_relids; // 获取左关系标识 + right_relids = rinfo->right_relids; // 获取右关系标识 + left_nullable_relids = bms_intersect(left_relids, rinfo->nullable_relids); // 获取左可为空关系标识 + right_nullable_relids = bms_intersect(right_relids, rinfo->nullable_relids); // 获取右可为空关系标识 + /* 遍历等价类以查找匹配的条件 */ foreach (lc1, root->eq_classes) { EquivalenceClass* cur_ec = (EquivalenceClass*)lfirst(lc1); - EquivalenceMember* coal_em = NULL; - bool match = false; - bool matchleft = false; - bool matchright = false; + EquivalenceMember* coal_em = NULL; // 用于存储常量成员 + bool match = false; // 用于表示是否找到匹配的条件 + bool matchleft = false; // 用于表示是否匹配左侧变量 + bool matchright = false; // 用于表示是否匹配右侧变量 ListCell* lc2 = NULL; - /* Ignore EC unless it contains pseudoconstants */ + /* 忽略不包含伪常量的等价类 */ if (!cur_ec->ec_has_const) continue; - /* Never match to a volatile EC */ + + /* 也不要匹配到易失性等价类 */ if (cur_ec->ec_has_volatile) continue; - /* It has to match the outer-join clause as to semantics, too */ + + /* 等价类必须在语义上匹配外连接子句 */ if (collation != cur_ec->ec_collation) continue; + + /* 并且必须与外连接子句的合并操作族匹配 */ if (!equal(rinfo->mergeopfamilies, cur_ec->ec_opfamilies)) continue; + /* * Does it contain a COALESCE(leftvar, rightvar) construct? * @@ -1711,28 +1812,29 @@ static bool reconsider_full_join_clause(PlannerInfo* root, RestrictInfo* rinfo) * the two column types). Is it OK to strip implicit coercions from * the COALESCE arguments? */ - match = false; + match = false; // 重置匹配标志为 false foreach (lc2, cur_ec->ec_members) { coal_em = (EquivalenceMember*)lfirst(lc2); - AssertEreport(!coal_em->em_is_child, MOD_OPT, ""); /* no children yet */ - if (IsA(coal_em->em_expr, CoalesceExpr)) { + AssertEreport(!coal_em->em_is_child, MOD_OPT, ""); /* 尚无子项 */ + if (IsA(coal_em->em_expr, CoalesceExpr)) { // 如果等价成员是 COALESCE 表达式 CoalesceExpr* cexpr = (CoalesceExpr*)coal_em->em_expr; Node* cfirst = NULL; Node* csecond = NULL; if (list_length(cexpr->args) != 2) - continue; - cfirst = (Node*)linitial(cexpr->args); - csecond = (Node*)lsecond(cexpr->args); + continue; // 忽略不是由两个参数组成的 COALESCE 表达式 + + cfirst = (Node*)linitial(cexpr->args); // 获取第一个参数 + csecond = (Node*)lsecond(cexpr->args); // 获取第二个参数 if (equal(leftvar, cfirst) && equal(rightvar, csecond)) { - match = true; + match = true; // 如果左侧变量匹配第一个参数,右侧变量匹配第二个参数,则匹配成功 break; } } } if (!match) - continue; /* no match, so ignore this EC */ + continue; // 如果没有匹配的条件,继续下一个等价类的匹配 /* no match, so ignore this EC */ /* * Yes it does! Try to generate clauses LEFTVAR = CONSTANT and @@ -1740,39 +1842,50 @@ static bool reconsider_full_join_clause(PlannerInfo* root, RestrictInfo* rinfo) * succeed with at least one constant for each var before we can * decide to throw away the outer-join clause. */ - matchleft = matchright = false; - foreach (lc2, cur_ec->ec_members) { - EquivalenceMember* cur_em = (EquivalenceMember*)lfirst(lc2); - Oid eq_op; - RestrictInfo* newrinfo = NULL; + matchleft = matchright = false; // 重置左侧和右侧的匹配标志为 false +foreach (lc2, cur_ec->ec_members) { + EquivalenceMember* cur_em = (EquivalenceMember*)lfirst(lc2); + Oid eq_op; + RestrictInfo* newrinfo = NULL; + + if (!cur_em->em_is_const) + continue; /* 忽略非常量成员 */ + + // 针对左侧变量构建等式操作符 + eq_op = select_equality_operator(cur_ec, left_type, cur_em->em_datatype); + if (OidIsValid(eq_op)) { + // 构建新的约束信息,表示左侧变量与当前常量成员之间的等式关系 + newrinfo = build_implied_join_equality(eq_op, + cur_ec->ec_collation, + leftvar, + cur_em->em_expr, + bms_copy(left_relids), + bms_copy(left_nullable_relids), + cur_ec->ec_min_security); + + // 处理新的等式关系 + if (process_equivalence(root, newrinfo, true)) + matchleft = true; + } + + // 针对右侧变量构建等式操作符 + eq_op = select_equality_operator(cur_ec, right_type, cur_em->em_datatype); + if (OidIsValid(eq_op)) { + // 构建新的约束信息,表示右侧变量与当前常量成员之间的等式关系 + newrinfo = build_implied_join_equality(eq_op, + cur_ec->ec_collation, + rightvar, + cur_em->em_expr, + bms_copy(right_relids), + bms_copy(right_nullable_relids), + cur_ec->ec_min_security); + + // 处理新的等式关系 + if (process_equivalence(root, newrinfo, true)) + matchright = true; + } +} - if (!cur_em->em_is_const) - continue; /* ignore non-const members */ - eq_op = select_equality_operator(cur_ec, left_type, cur_em->em_datatype); - if (OidIsValid(eq_op)) { - newrinfo = build_implied_join_equality(eq_op, - cur_ec->ec_collation, - leftvar, - cur_em->em_expr, - bms_copy(left_relids), - bms_copy(left_nullable_relids), - cur_ec->ec_min_security); - if (process_equivalence(root, newrinfo, true)) - matchleft = true; - } - eq_op = select_equality_operator(cur_ec, right_type, cur_em->em_datatype); - if (OidIsValid(eq_op)) { - newrinfo = build_implied_join_equality(eq_op, - cur_ec->ec_collation, - rightvar, - cur_em->em_expr, - bms_copy(right_relids), - bms_copy(right_nullable_relids), - cur_ec->ec_min_security); - if (process_equivalence(root, newrinfo, true)) - matchright = true; - } - } /* * If we were able to equate both vars to constants, we're done, and @@ -1819,27 +1932,35 @@ bool exprs_known_equal(PlannerInfo* root, Node* item1, Node* item2) bool item2member = false; ListCell* lc2 = NULL; - /* Never match to a volatile EC */ + // 忽略具有不稳定表达式的等价类 if (ec->ec_has_volatile) continue; foreach (lc2, ec->ec_members) { EquivalenceMember* em = (EquivalenceMember*)lfirst(lc2); + // 忽略子表达式 if (em->em_is_child) - continue; /* ignore children here */ + continue; + + // 检查表达式 item1 是否是等价类的成员 if (equal(item1, em->em_expr)) item1member = true; + // 检查表达式 item2 是否是等价类的成员 else if (equal(item2, em->em_expr)) item2member = true; - /* Exit as soon as equality is proven */ + + // 如果两个表达式都被识别为等价类的成员,则返回 true if (item1member && item2member) return true; } } + + // 如果没有找到两个表达式在等价类中都被认为是相等的情况,返回 false return false; } + /* * add_child_rel_equivalences * Search for EC members that reference the parent_rel, and @@ -1930,23 +2051,29 @@ void add_child_rel_equivalences( void mutate_eclass_expressions(PlannerInfo* root, Node* (*mutator)(), void* context, bool include_child_exprs) { ListCell* lc1 = NULL; + // 定义一个函数指针,用于调用传入的变换函数 Node* (*p2mutator)(Node*, void*) = (Node* (*)(Node*, void*)) mutator; + // 遍历查询中的等价类 foreach (lc1, root->eq_classes) { EquivalenceClass* cur_ec = (EquivalenceClass*)lfirst(lc1); ListCell* lc2 = NULL; + // 遍历等价类的成员 foreach (lc2, cur_ec->ec_members) { EquivalenceMember* cur_em = (EquivalenceMember*)lfirst(lc2); + // 如果当前成员是子表达式,并且不包括子表达式,则跳过 if (cur_em->em_is_child && !include_child_exprs) - continue; /* ignore children unless requested */ + continue; + // 使用传入的变换函数 mutator 对当前表达式进行变换,并更新表达式 cur_em->em_expr = (Expr*)p2mutator((Node*)cur_em->em_expr, context); } } } + /* * generate_implied_equalities_for_indexcol * Create EC-derived joinclauses usable with a specific index column. @@ -2207,20 +2334,24 @@ bool is_redundant_derived_clause(RestrictInfo* rinfo, List* clauselist) EquivalenceClass* parent_ec = rinfo->parent_ec; ListCell* lc = NULL; - /* Fail if it's not a potentially-redundant clause from some EC */ + // 如果派生限制条件的 parent_ec 为空,返回 false,表示不是多余的 if (parent_ec == NULL) return false; + // 遍历给定的限制条件列表 foreach (lc, clauselist) { RestrictInfo* otherrinfo = (RestrictInfo*)lfirst(lc); + // 如果列表中的某个限制条件具有与派生限制条件相同的 parent_ec,则认为它是多余的 if (otherrinfo->parent_ec == parent_ec) return true; } + // 如果没有找到具有相同 parent_ec 的限制条件,返回 false,表示不是多余的 return false; } + /* * @Description: Get include this expr EquivalenceClass. * @in root - Per-query information for planning/optimization. @@ -2232,17 +2363,23 @@ EquivalenceClass* get_expr_eqClass(PlannerInfo* root, Expr* expr) EquivalenceClass* ec = NULL; ListCell* lc = NULL; + // 遍历查询中的所有等价类 foreach (lc, root->eq_classes) { ec = (EquivalenceClass*)lfirst(lc); + + // 调用 find_ec_memeber_for_var 函数查找等价类中是否包含特定表达式 bool found = find_ec_memeber_for_var(ec, (Node*)expr); + // 如果找到了等价类中包含表达式的成员,就返回这个等价类 if (found) return ec; } + // 如果没有找到包含表达式的等价类,返回 NULL return NULL; } + /* * @Description: Delete this expr from EquivalenceMember which appears in group by clause * and do not appear in collectiveGroupExpr that means it's value will be altered grouping set after. @@ -2252,26 +2389,31 @@ EquivalenceClass* get_expr_eqClass(PlannerInfo* root, Expr* expr) */ void delete_eq_member(PlannerInfo* root, List* tlist, List* collectiveGroupExpr) { + // 获取查询中的分组表达式 List* groupClause = root->parse->groupClause; List* group_expr = get_sortgrouplist_exprs(groupClause, tlist); ListCell* lc = NULL; ListCell* pnext = NULL; + + // 遍历查询中的等价类 for (lc = list_head(root->eq_classes); lc != NULL; lc = pnext) { EquivalenceClass* ec = (EquivalenceClass*)lfirst(lc); pnext = lnext(lc); ListCell* lc2 = NULL; ListCell* pnext2 = NULL; + // 遍历等价类的成员 for (lc2 = list_head(ec->ec_members); lc2 != NULL; lc2 = pnext2) { EquivalenceMember* em = (EquivalenceMember*)lfirst(lc2); pnext2 = lnext(lc2); - /* Delete this em, it already be not equivalence grouping set after. */ + // 如果等价类成员在分组表达式中,并且不在 collectiveGroupExpr 中 if (list_member(group_expr, em->em_expr) && !list_member(collectiveGroupExpr, em->em_expr)) { - /* Delete this Equivalence Member. */ + // 从等价类中删除该成员 ec->ec_members = list_delete_ptr(ec->ec_members, em); + // 如果等价类不再包含成员,将其从根节点的等价类列表中删除 if (0 == list_length(ec->ec_members)) { root->eq_classes = list_delete_ptr(root->eq_classes, ec); } @@ -2279,6 +2421,7 @@ void delete_eq_member(PlannerInfo* root, List* tlist, List* collectiveGroupExpr) } } + // 释放分组表达式列表 list_free_ext(group_expr); } diff --git a/src/gausskernel/optimizer/path/es_selectivity.cpp b/src/gausskernel/optimizer/path/es_selectivity.cpp index 73e6ef109..0eeb3c81b 100644 --- a/src/gausskernel/optimizer/path/es_selectivity.cpp +++ b/src/gausskernel/optimizer/path/es_selectivity.cpp @@ -29,28 +29,38 @@ const int TOW_MEMBERS = 2; +/** + * 构造函数 ES_SELECTIVITY 的实现 + */ ES_SELECTIVITY::ES_SELECTIVITY() - : es_candidate_list(NULL), - es_candidate_saved(NULL), - unmatched_clause_group(NULL), - root(NULL), - sjinfo(NULL), - origin_clauses(NULL), - path(NULL), - bucketsize_list(NULL) -{} + : es_candidate_list(NULL), // 初始化 es_candidate_list 为 NULL + es_candidate_saved(NULL), // 初始化 es_candidate_saved 为 NULL + unmatched_clause_group(NULL), // 初始化 unmatched_clause_group 为 NULL + root(NULL), // 初始化 root 为 NULL + sjinfo(NULL), // 初始化 sjinfo 为 NULL + origin_clauses(NULL), // 初始化 origin_clauses 为 NULL + path(NULL), // 初始化 path 为 NULL + bucketsize_list(NULL) // 初始化 bucketsize_list 为 NULL +{//这个构造函数的目的是创建一个 ES_SELECTIVITY 对象,并将其各个成员变量初始化为 NULL 值。这些成员变量在对象的生命周期中将用于存储不同的数据和状态信息。 + +/** + * 析构函数 ES_SELECTIVITY 的实现 + * 此析构函数为空,因为没有需要显式释放的资源。 + */ ES_SELECTIVITY::~ES_SELECTIVITY() {} +/** + * 检查给定的索引是否包含在候选列表中 + */ bool ES_SELECTIVITY::ContainIndexCols(const es_candidate* es, const IndexOptInfo* index) const { for (int pos = 0; pos < index->ncolumns; pos++) { int indexAttNum = index->indexkeys[pos]; /* - * Notice: indexAttNum can be negative. Some indexAttNums of junk column may be negative - * since they are located before the first visible column. for example, the indexAttNum - * of 'oid' column in system table 'pg_class' is -2. + * 注意:indexAttNum 可能为负数。某些索引中的 indexAttNum 可能为负数,因为它们位于第一个可见列之前的位置。 + * 例如,系统表 'pg_class' 中 'oid' 列的 indexAttNum 为 -2。 */ if (indexAttNum >= 0 && !bms_is_member(indexAttNum, es->left_attnums)) return false; @@ -59,78 +69,104 @@ bool ES_SELECTIVITY::ContainIndexCols(const es_candidate* es, const IndexOptInfo return true; } + +/** + * 检查候选列表中的关系是否匹配唯一索引 + */ bool ES_SELECTIVITY::MatchUniqueIndex(const es_candidate* es) const { ListCell* lci = NULL; + + // 遍历候选列表项的左关系的索引列表 foreach (lci, es->left_rel->indexlist) { IndexOptInfo* indexToMatch = (IndexOptInfo*)lfirst(lci); - if (indexToMatch->relam == BTREE_AM_OID && indexToMatch->unique - && ContainIndexCols(es, indexToMatch)) { - return true; + + // 检查索引是否为B树索引(BTREE_AM_OID)并且是唯一索引,并且索引列包含在候选列表中 + if (indexToMatch->relam == BTREE_AM_OID && indexToMatch->unique && + ContainIndexCols(es, indexToMatch)) { + return true; // 如果满足条件,则返回 true } } - return false; + return false; // 如果没有匹配的唯一索引,则返回 false } /* * check whether the equality constraints match an unique index. * We know the result only has one row if finding a matched unique index. */ +/** + * 使用唯一索引计算选择性 + */ void ES_SELECTIVITY::CalSelWithUniqueIndex(Selectivity &result) { - List* es_candidate_used = NULL; + List* es_candidate_used = NULL; // 用于存储已使用的候选列表项 ListCell* l = NULL; + + // 遍历候选列表中的每个候选列表项 foreach(l, es_candidate_list) { es_candidate* temp = (es_candidate*)lfirst(l); + + // 检查候选列表项的标签是否为 ES_EQSEL,是否匹配唯一索引,以及左关系是否具有至少一个元组 if (temp->tag == ES_EQSEL && MatchUniqueIndex(temp) && temp->left_rel && temp->left_rel->tuples >= 1.0) { - result *= 1.0 / temp->left_rel->tuples; - es_candidate_used = lappend(es_candidate_used, temp); + result *= 1.0 / temp->left_rel->tuples; // 更新选择性结果 + es_candidate_used = lappend(es_candidate_used, temp); // 将候选列表项添加到已使用列表中 } } /* - * Finally, we need to delete es_candidates which have already used. The rests es_candidates - * will calculate with statistic info. + * 最后,我们需要删除已经使用的候选列表项。剩下的候选列表项将使用统计信息进行计算。 */ - es_candidate_saved = es_candidate_list; - es_candidate_list = list_difference_ptr(es_candidate_list, es_candidate_used); + es_candidate_saved = es_candidate_list; // 保存原始候选列表 + es_candidate_list = list_difference_ptr(es_candidate_list, es_candidate_used); // 删除已使用的候选列表项 - list_free(es_candidate_used); + list_free(es_candidate_used); // 释放已使用的候选列表项的内存 } + /* * @brief Main entry for using extended statistic to calculate selectivity * root_input can only be NULL when processing group by clauses */ +/** + * 计算查询的选择性 + * + * root_input 查询的PlannerInfo对象 + * clauses_input 查询的限制条件列表 + * sjinfo_input 特殊连接信息 + * jointype 连接类型 + * path_input 连接路径信息 + * action 动作类型 + * eType 统计信息估算类型 + */ Selectivity ES_SELECTIVITY::calculate_selectivity(PlannerInfo* root_input, List* clauses_input, SpecialJoinInfo* sjinfo_input, JoinType jointype, JoinPath* path_input, es_type action, STATS_EST_TYPE eType) { - Selectivity result = 1.0; - root = root_input; - sjinfo = sjinfo_input; - origin_clauses = clauses_input; - path = path_input; + Selectivity result = 1.0; // 初始化选择性结果为 1.0 + root = root_input; // 设置成员变量 root 为传入的查询的 PlannerInfo 对象 + sjinfo = sjinfo_input; // 设置成员变量 sjinfo 为传入的特殊连接信息 + origin_clauses = clauses_input; // 设置成员变量 origin_clauses 为传入的限制条件列表 + path = path_input; // 设置成员变量 path 为传入的连接路径信息 - /* group clauselist */ + /* 对限制条件列表进行分组 */ if (action == ES_GROUPBY) { - /* group clauselist for group by clauses */ + /* 对于分组操作,对限制条件列表进行分组 */ group_clauselist_groupby(origin_clauses); } else { group_clauselist(origin_clauses); } /* - * Before reading statistic, We check whether the equality constraints match an - * unique index. We know the result only has one row if finding a matched unique index. + * 在读取统计信息之前,我们检查是否存在匹配唯一索引的相等约束。 + * 如果找到匹配的唯一索引,则知道结果只包含一行。 */ CalSelWithUniqueIndex(result); - /* read statistic */ + /* 读取统计信息 */ read_statistic(); - /* calculate selectivity */ + /* 计算选择性 */ ListCell* l = NULL; foreach(l, es_candidate_list) { es_candidate* temp = (es_candidate*)lfirst(l); @@ -139,7 +175,7 @@ Selectivity ES_SELECTIVITY::calculate_selectivity(PlannerInfo* root_input, List* result *= cal_eqsel(temp); break; case ES_EQJOINSEL: - /* compute hash bucket size */ + /* 计算哈希桶的大小 */ if (action == ES_COMPUTEBUCKETSIZE) { es_bucketsize* bucket = (es_bucketsize*)palloc(sizeof(es_bucketsize)); cal_bucket_size(temp, bucket); @@ -156,47 +192,55 @@ Selectivity ES_SELECTIVITY::calculate_selectivity(PlannerInfo* root_input, List* } } - es_candidate_list = es_candidate_saved; + es_candidate_list = es_candidate_saved; // 恢复候选列表为原始状态 - /* free memory, but unmatched_clause_group need to be free manually */ + /* 释放内存,但 unmatched_clause_group 需要手动释放 */ clear(); - return result; + return result; // 返回计算得到的选择性结果 } + /* * @brief group clause by clause type and involving rels, for now, only support eqsel and eqjoinsel */ +/** + * 对限制条件列表进行分组 + */ void ES_SELECTIVITY::group_clauselist(List* clauses) { ListCell* l = NULL; foreach(l, clauses) { Node* clause = (Node*)lfirst(l); + // 检查限制条件是否为 RestrictInfo 类型 if (!IsA(clause, RestrictInfo)) { unmatched_clause_group = lappend(unmatched_clause_group, clause); continue; } RestrictInfo* rinfo = (RestrictInfo*)clause; + + // 检查条件是否为伪常量、选择性大于1或包含 OR 子句 if (rinfo->pseudoconstant || rinfo->norm_selec > 1 || rinfo->orclause) { unmatched_clause_group = lappend(unmatched_clause_group, clause); continue; } + // 如果条件是操作符子句 if (is_opclause(rinfo->clause)) { OpExpr* opclause = (OpExpr*)rinfo->clause; Oid opno = opclause->opno; - /* only handle "=" operator */ + // 仅处理"="操作符 if (get_oprrest(opno) == EQSELRETURNOID) { int relid_num = bms_num_members(rinfo->clause_relids); if (relid_num == 1) { - /* only process clause like t1.a = 1, so only one relid */ + // 处理形如 t1.a = 1 的子句,只有一个 relid load_eqsel_clause(rinfo); continue; } else if (relid_num == TOW_MEMBERS) { - /* only process clause like t1.a = t2.b, so only two relids */ + // 处理形如 t1.a = t2.b 的子句,只有两个 relid load_eqjoinsel_clause(rinfo); continue; } else { @@ -208,6 +252,7 @@ void ES_SELECTIVITY::group_clauselist(List* clauses) NullTest* nullclause = (NullTest*)rinfo->clause; int relid_num = bms_num_members(rinfo->clause_relids); if (relid_num == 1 && nullclause->nulltesttype == IS_NULL) { + // 处理形如 t1.a IS NULL 的子句,只有一个 relid load_eqsel_clause(rinfo); continue; } @@ -216,26 +261,37 @@ void ES_SELECTIVITY::group_clauselist(List* clauses) unmatched_clause_group = lappend(unmatched_clause_group, clause); } + // 重新检查候选列表 recheck_candidate_list(); + + // 打印调试信息 debug_print(); return; } + /* * @brief group groupby-clause by clause type and involving rels, for */ +/** + * 将 GroupVarInfo 列表分组到 es_candidate_list 中的候选列表中 + */ void ES_SELECTIVITY::group_clauselist_groupby(List* varinfos) { ListCell* l = NULL; foreach(l, varinfos) { GroupVarInfo* varinfo = (GroupVarInfo*)lfirst(l); + + // 检查 varinfo->var 是否为 Var 类型的节点 if (!is_var_node(varinfo->var)) { unmatched_clause_group = lappend(unmatched_clause_group, varinfo); continue; } Var* var = NULL; + + // 处理 RelabelType 包装的 Var 节点 if (IsA(varinfo->var, RelabelType)) var = (Var*)((RelabelType*)varinfo->var)->arg; else @@ -249,8 +305,9 @@ void ES_SELECTIVITY::group_clauselist_groupby(List* varinfos) if (temp->tag != ES_GROUPBY) continue; + // 检查变量的关联关系是否匹配已有的候选列表中的项 if (varinfo->rel == temp->left_rel) { - /* only use left attnums for group by clauses */ + /* 仅使用�����侧 attnums 用于 group by 子句 */ temp->left_attnums = bms_add_member(temp->left_attnums, var->varattno); temp->clause_group = lappend(temp->clause_group, varinfo); add_clause_map(temp, var->varattno, 0, (Node*)var, NULL); @@ -259,7 +316,7 @@ void ES_SELECTIVITY::group_clauselist_groupby(List* varinfos) } } - /* if not matched, build a new cell in es_candidate_list */ + /* 如果未匹配,则在 es_candidate_list 中构建新的候选项 */ if (!found_match) { es_candidate* es = (es_candidate*)palloc(sizeof(es_candidate)); RelOptInfo* temp_rel = NULL; @@ -278,113 +335,168 @@ void ES_SELECTIVITY::group_clauselist_groupby(List* varinfos) } } + // 重新检查候选列表 recheck_candidate_list(); + + // 打印调试信息 debug_print(); return; } + /* * @brief initial es_candidate, set all elements to default value or NULL */ +/** + * 初始化 es_candidate 结构 + */ void ES_SELECTIVITY::init_candidate(es_candidate* es) const { - es->tag = ES_EMPTY; - es->relids = NULL; - es->left_relids = NULL; - es->right_relids = NULL; - es->left_attnums = NULL; - es->right_attnums = NULL; - es->left_stadistinct = 0.0; - es->right_stadistinct = 0.0; - es->left_first_mcvfreq = 0.0; - es->right_first_mcvfreq = 0.0; - es->left_rel = NULL; - es->right_rel = NULL; - es->left_rte = NULL; - es->right_rte = NULL; - es->clause_group = NIL; - es->clause_map = NIL; - es->left_extended_stats = NULL; - es->right_extended_stats = NULL; - es->pseudo_clause_list = NIL; - es->has_null_clause = false; + // 初始化 es_candidate 结构的各个字段 + es->tag = ES_EMPTY; // 标记候选项的类型 + es->relids = NULL; // 关联的所有 relids + es->left_relids = NULL; // 左侧关联的 relids + es->right_relids = NULL; // 右侧关联的 relids + es->left_attnums = NULL; // 左侧的 attnums + es->right_attnums = NULL; // 右侧的 attnums + es->left_stadistinct = 0.0; // 左侧的统计信息 distinct + es->right_stadistinct = 0.0; // 右侧的统计信息 distinct + es->left_first_mcvfreq = 0.0; // 左侧的第一个 MCV 的频率 + es->right_first_mcvfreq = 0.0; // 右侧的第一个 MCV 的频率 + es->left_rel = NULL; // 左侧关联的 RelOptInfo + es->right_rel = NULL; // 右侧关联的 RelOptInfo + es->left_rte = NULL; // 左侧关联的 RangeTblEntry + es->right_rte = NULL; // 右侧关联的 RangeTblEntry + es->clause_group = NIL; // 包含的限制条件列表 + es->clause_map = NIL; // 限制条件的映射关系 + es->left_extended_stats = NULL; // 左侧关联的扩展统计信息 + es->right_extended_stats = NULL; // 右侧关联的扩展统计信息 + es->pseudo_clause_list = NIL; // 伪限制条件列表 + es->has_null_clause = false; // 是否包含 NULL 限制条件 + return; } + /* * @brief free memory used in calculate_selectivity except unmatched_clause_group */ +/** + * 清理 es_candidate_list 中的候选项以及其他相关字段 + */ void ES_SELECTIVITY::clear() { - /* delete es_candidate_list */ + /* 删除 es_candidate_list 中的每个候选项并释放相关资源 */ ListCell* l = NULL; foreach(l, es_candidate_list) { es_candidate* temp = (es_candidate*)lfirst(l); + + // 释放关联的所有 relids bms_free_ext(temp->relids); bms_free_ext(temp->left_relids); bms_free_ext(temp->right_relids); + + // 释放左侧和右侧的 attnums bms_free_ext(temp->left_attnums); bms_free_ext(temp->right_attnums); + + // 清空左侧和右侧的关联信息 temp->left_rel = NULL; temp->right_rel = NULL; temp->left_rte = NULL; temp->right_rte = NULL; + + // 释放 clause_group 列表及其内部元素 list_free_ext(temp->clause_group); + + // 释放 clause_map 列表及其内部元素 list_free_deep(temp->clause_map); + + // 清理左侧和右侧的扩展统计信息 clear_extended_stats(temp->left_extended_stats); clear_extended_stats(temp->right_extended_stats); + + // 释放伪限制条件列表 list_free_ext(temp->pseudo_clause_list); } + + // 释放 es_candidate_list 列表及其内部元素 list_free_deep(es_candidate_list); /* - * unmatched_clause_group need to be free manually after - * it is used in clause_selectivity(). + * unmatched_clause_group 需要手动释放,因为它在 clause_selectivity() 中使用后仍需释放。 */ root = NULL; sjinfo = NULL; origin_clauses = NULL; + return; } + /* * @brief free memory used by saving extended_stats after calculation */ +/** + * 清理 ExtendedStats 结构 + */ void ES_SELECTIVITY::clear_extended_stats(ExtendedStats* extended_stats) const { if (extended_stats) { + // 释放 bms_attnum 集合 bms_free_ext(extended_stats->bms_attnum); + + // 释放 mcv_numbers 数组 if (extended_stats->mcv_numbers) pfree_ext(extended_stats->mcv_numbers); + + // 释放 mcv_values 数组 if (extended_stats->mcv_values) pfree_ext(extended_stats->mcv_values); + + // 释放 mcv_nulls 数组 if (extended_stats->mcv_nulls) pfree_ext(extended_stats->mcv_nulls); + + // 释放 other_mcv_numbers 数组 if (extended_stats->other_mcv_numbers) pfree_ext(extended_stats->other_mcv_numbers); + + // 释放 ExtendedStats 结构 pfree_ext(extended_stats); + + // 将 extended_stats 指针置为 NULL,以防止悬挂指针 extended_stats = NULL; } return; } + /* * @brief free memory of extended_stats_list by calling clear_extended_stats */ +/** + * 清理 ExtendedStats 结构的列表 + * + * stats_list 指向 ExtendedStats 结构列表的指针,用于释放列表中的每个元素及相关资源 + */ void ES_SELECTIVITY::clear_extended_stats_list(List* stats_list) const { if (stats_list) { ListCell* lc = NULL; foreach(lc, stats_list) { ExtendedStats* extended_stats = (ExtendedStats*)lfirst(lc); + // 清理 ExtendedStats 结构及其相关资源 clear_extended_stats(extended_stats); } + // 释放列表及其内部元素 list_free_ext(stats_list); } return; } + /* * @brief copy the original pointer, repoint it to something else * in order to avoid failure when using list_free @@ -403,19 +515,30 @@ ExtendedStats* ES_SELECTIVITY::copy_stats_ptr(ListCell* l) const * @brief add an eqjsel clause to es_candidate_list and group by relid * we should have bms_num_members(clause->clause_relids) == 1 */ +/** + * 将相同关系的相等选择性约束合并到 es_candidate_list 中的现有候选项中, + * 如果没有匹配的候选项,则创建一个新的候选项。 + * + * clause 指向 RestrictInfo 结构的指针,表示相等选择性约束 + */ void ES_SELECTIVITY::load_eqsel_clause(RestrictInfo* clause) { - /* group clause by rels, add to es_candidate_list */ + // 遍历现有的候选项列表 ListCell* l = NULL; foreach(l, es_candidate_list) { es_candidate* temp = (es_candidate*)lfirst(l); + // 只关心相等选择性约束 if (temp->tag != ES_EQSEL) continue; + // 如果约束的关系标识符与当前候选项的关系标识符相等 if (bms_equal(clause->clause_relids, temp->relids)) { + // 将约束的属性添加到候选项中 if (add_attnum(clause, temp)) { + // 将约束添加到候选项的约束组中 temp->clause_group = lappend(temp->clause_group, clause); + // 如果约束是 NullTest,则标记候选项包含 Null 约束 if (IsA(clause->clause, NullTest)) temp->has_null_clause = true; return; @@ -423,78 +546,109 @@ void ES_SELECTIVITY::load_eqsel_clause(RestrictInfo* clause) } } - /* if not matched, build a new cell in es_candidate_list */ + // 如果没有匹配的候选项,则创建一个新的候选项 if (!build_es_candidate(clause, ES_EQSEL)) unmatched_clause_group = lappend(unmatched_clause_group, clause); return; } + /* * @brief add an eqjoinsel clause to es_candidate_list and group by relid */ +/** + * 将相同关系的等值连接选择性约束合并到 es_candidate_list 中的现有候选项中, + * 如果没有匹配的候选项,则创建一个新的候选项。 + * + * clause 指向 RestrictInfo 结构的指针,表示等值连接选择性约束 + */ void ES_SELECTIVITY::load_eqjoinsel_clause(RestrictInfo* clause) { /* - * the relids in the clause should be as same as sjinfo, so we can avoid parameterized conditon. + * 确保约束中的关系标识符与特殊连接信息(sjinfo)的左右关系标识符交集不为空, + * 以避免参数化条件。 */ if (sjinfo) { if (!bms_overlap(sjinfo->min_lefthand, clause->clause_relids) || !bms_overlap(sjinfo->min_righthand, clause->clause_relids)) { + // 如果没有交集,则将约束添加到 unmatched_clause_group 中并返回 unmatched_clause_group = lappend(unmatched_clause_group, clause); return; } } - /* group clause by rels, add to es_candidate_list */ + /* 根据关系组织约束,然后添加到 es_candidate_list 中 */ if (bms_num_members(clause->left_relids) == 1 && bms_num_members(clause->right_relids) == 1) { ListCell* l = NULL; foreach(l, es_candidate_list) { es_candidate* temp = (es_candidate*)lfirst(l); + // 只关心等值连接选择性约束 if (temp->tag != ES_EQJOINSEL) continue; + // 如果约束的关系标识符与当前候选项的关系标识符相等 if (bms_equal(clause->clause_relids, temp->relids)) { + // 将约束的属性添加到候选项中 if (add_attnum(clause, temp)) { + // 将约束添加到候选项的约束组中 temp->clause_group = lappend(temp->clause_group, clause); return; } } } - /* if not matched, build a new cell in es_candidate_list */ + // 如果没有匹配的候选项,则创建一个新的候选项 if (!build_es_candidate(clause, ES_EQJOINSEL)) unmatched_clause_group = lappend(unmatched_clause_group, clause); return; } + // 如果约束不符合条件,则将其添加到 unmatched_clause_group 中 unmatched_clause_group = lappend(unmatched_clause_group, clause); return; } + /* * @brief make a combination of es->right_attnums or es->left_attnums with input attnum by clause map * @param left: true: add to es->right_attnums; false: add to es->left_attnums * @return combination of Bitmapset * @exception None */ +/** + * 通过 es_candidate 中的 clause_map 创建一个新的属性集合, + * 这些属性与指定的属性集合中的属性具有对应关系。 + * + * es 指向 es_candidate 结构的指针,包含了关联的 clause_map 信息 + * attnums 包含属性标识符的 Bitmapset 指针 + * left 指示是否从左侧属性映射到右侧属性(true 表示是,false 表示否) + * 返回一个新的属性集合,其中包含了按照映射关系转换的属性标识符 + */ Bitmapset* ES_SELECTIVITY::make_attnums_by_clause_map(es_candidate* es, Bitmapset* attnums, bool left) const { ListCell* lc_clause_map = NULL; Bitmapset* result = NULL; + + // 遍历 es_candidate 中的 clause_map 列表 foreach(lc_clause_map, es->clause_map) { es_clause_map* clause_map = (es_clause_map*)lfirst(lc_clause_map); - if (left && bms_is_member(clause_map->left_attnum, attnums)) + if (left && bms_is_member(clause_map->left_attnum, attnums)) { + // 如果需要从左侧属性映射到右侧属性且左侧属性在给定集合中,则添加对应的右侧属性到结果集合 result = bms_add_member(result, clause_map->right_attnum); - else if (!left && bms_is_member(clause_map->right_attnum, attnums)) + } else if (!left && bms_is_member(clause_map->right_attnum, attnums)) { + // 如果需要从右侧属性映射到左侧属性且右侧属性在给定集合中,则添加对应的左侧属性到结果集合 result = bms_add_member(result, clause_map->left_attnum); + } } + return result; } + /* * @brief find the matched extended stats in stats_list * @param es :proving mathing conditions including relids , attnums @@ -503,6 +657,13 @@ Bitmapset* ES_SELECTIVITY::make_attnums_by_clause_map(es_candidate* es, Bitmapse * @return None * @exception None */ +/** + * 尝试将当前 es_candidate 与扩展统计信息列表中的统计信息进行匹配。 + * + * es 指向 es_candidate 结构的指针,包含了要匹配的候选信息 + * stats_list 包含扩展统计信息的列表 + * left 指示当前 es_candidate 是否为左侧关系(true 表示是,false 表示否) + */ void ES_SELECTIVITY::match_extended_stats(es_candidate* es, List* stats_list, bool left) { int max_matched = 0; @@ -510,35 +671,41 @@ void ES_SELECTIVITY::match_extended_stats(es_candidate* es, List* stats_list, bo char other_side_starelkind; RangeTblEntry* other_side_rte = NULL; Bitmapset* this_side_attnums = NULL; + + // 根据关系方向确定其它一侧的属性集合和关系类型 if (left) { - /* this side is left and the other side is right */ other_side_starelkind = OidIsValid(es->right_rte->partitionOid) ? STARELKIND_PARTITION : STARELKIND_CLASS; other_side_rte = es->right_rte; this_side_attnums = es->left_attnums; } else { - /* this side is right and other side is left */ other_side_starelkind = OidIsValid(es->left_rte->partitionOid) ? STARELKIND_PARTITION : STARELKIND_CLASS; other_side_rte = es->left_rte; this_side_attnums = es->right_attnums; } - /* best_matched_listcell use to save the best match from stats list */ + // 用于保存最佳匹配统计信息的列表单元和最佳匹配统计信息的列表单元 ListCell* best_matched_listcell = NULL; - /* best_matched_stats use to save the best match from es_get_multi_column_stats */ ListCell* best_matched_stats = (ListCell*)palloc(sizeof(ListCell)); lfirst(best_matched_stats) = NULL; + ListCell* lc = NULL; foreach(lc, stats_list) { ExtendedStats* extended_stats = (ExtendedStats*)lfirst(lc); ExtendedStats* other_side_extended_stats = NULL; + + // 如果当前统计信息的属性集合是当前候选的子集,尝试匹配 if (bms_is_subset(extended_stats->bms_attnum, this_side_attnums)) { int matched = bms_num_members(extended_stats->bms_attnum); + // 生成与当前属性集合匹配的其它一侧属性集合 Bitmapset* other_side_attnums = make_attnums_by_clause_map(es, extended_stats->bms_attnum, left); - other_side_extended_stats = es_get_multi_column_stats( - other_side_rte->relid, other_side_starelkind, other_side_rte->inh, other_side_attnums); + + // 获取其它一侧的多列统计信息 + other_side_extended_stats = + es_get_multi_column_stats(other_side_rte->relid, other_side_starelkind, other_side_rte->inh, other_side_attnums); + if (other_side_extended_stats != NULL && matched == num_members) { - /* all attnums have extended stats, leave */ + // 如果所有属性都有扩展统计信息,匹配完成,提前结束 if (left) { es->left_extended_stats = copy_stats_ptr(lc); es->right_extended_stats = other_side_extended_stats; @@ -549,16 +716,18 @@ void ES_SELECTIVITY::match_extended_stats(es_candidate* es, List* stats_list, bo clear_extended_stats((ExtendedStats*)lfirst(best_matched_stats)); break; } else if (other_side_extended_stats != NULL && matched > max_matched) { - /* not all attnums have extended stats, find the first maximum match */ + // 如果不是所有属性都有扩展统计信息,找到最大匹配的统计信息 best_matched_listcell = lc; clear_extended_stats((ExtendedStats*)lfirst(best_matched_stats)); lfirst(best_matched_stats) = other_side_extended_stats; max_matched = matched; - } else + } else { clear_extended_stats(other_side_extended_stats); + } } } + // 如果找到了最佳匹配的统计信息,将其保存到当前候选中 if (best_matched_listcell && lfirst(best_matched_stats)) { if (left) { es->left_extended_stats = copy_stats_ptr(best_matched_listcell); @@ -568,7 +737,8 @@ void ES_SELECTIVITY::match_extended_stats(es_candidate* es, List* stats_list, bo es->left_extended_stats = (ExtendedStats*)lfirst(best_matched_stats); } lfirst(best_matched_stats) = NULL; - /* remove members not in the multi-column stats */ + + // 移除那些不在多列统计信息中的属性 if (max_matched != num_members) { Bitmapset* tmpset = bms_difference(es->left_attnums, es->left_extended_stats->bms_attnum); int dump_attnum; @@ -580,9 +750,9 @@ void ES_SELECTIVITY::match_extended_stats(es_candidate* es, List* stats_list, bo } } pfree_ext(best_matched_stats); - return; } + /* * @brief modify distinct value using possion model * @param es : proving the distinct value to modify @@ -591,17 +761,26 @@ void ES_SELECTIVITY::match_extended_stats(es_candidate* es, List* stats_list, bo * @return None * @exception None */ +/** + * 根据泊松模型修改候选的基数估计值,以考虑统计不准确性。 + * + * @param es 指向 es_candidate 结构的指针,包含了要修改基数估计的信息 + * @param left 指示当前 es_candidate 是否为左侧关系(true 表示是,false 表示否) + * @param spjinfo 特殊连接信息,用于判断是否启用泊松模型 + */ void ES_SELECTIVITY::modify_distinct_by_possion_model(es_candidate* es, bool left, SpecialJoinInfo* spjinfo) const { - bool enablePossion = false; - double varratio = 1.0; + bool enablePossion = false; // 是否启用泊松模型 + double varratio = 1.0; // 变异系数 ListCell* lc = NULL; VariableStatData vardata; - float4 distinct = 0.0; - double tuples = 0.0; + float4 distinct = 0.0; // 原始基数估计值 + double tuples = 0.0; // 关系的元组数量 - /* build vardata */ + // 初始化 vardata 结构 vardata.enablePossion = true; + + // 根据当前关系方向设置 vardata 的关系和基数估计值 if (left && es->left_rel->tuples > 0) { vardata.rel = es->left_rel; distinct = es->left_stadistinct; @@ -609,6 +788,8 @@ void ES_SELECTIVITY::modify_distinct_by_possion_model(es_candidate* es, bool lef foreach(lc, es->clause_map) { es_clause_map* map = (es_clause_map*)lfirst(lc); vardata.var = (Node*)map->left_var; + + // 判断是否可以使用泊松模型 enablePossion = can_use_possion(&vardata, spjinfo, &varratio); if (!enablePossion) break; @@ -620,18 +801,23 @@ void ES_SELECTIVITY::modify_distinct_by_possion_model(es_candidate* es, bool lef foreach(lc, es->clause_map) { es_clause_map* map = (es_clause_map*)lfirst(lc); vardata.var = (Node*)map->right_var; + + // 判断是否可以使用泊松模型 enablePossion = can_use_possion(&vardata, spjinfo, &varratio); if (!enablePossion) break; } } + // 如果启用了泊松模型,使用模型调整基数估计值 if (enablePossion) { double tmp = distinct; distinct = NUM_DISTINCT_SELECTIVITY_FOR_POISSON(distinct, tuples, varratio); + + // 输出调整后的基数估计信息 ereport(ES_DEBUG_LEVEL, (errmodule(MOD_OPT), - (errmsg("[ES]The origin distinct value is %f. After using possion model with ntuples=%f and ration=%e \ + (errmsg("[ES]The origin distinct value is %f. After using Poisson model with ntuples=%f and ratio=%e, \ The new distinct value is %f", tmp, tuples, @@ -639,25 +825,40 @@ void ES_SELECTIVITY::modify_distinct_by_possion_model(es_candidate* es, bool lef distinct)))); } + // 根据关系方向设置修改后的基数估计值 if (left && enablePossion) { es->left_stadistinct = distinct; } else if ((!left) && enablePossion) { es->right_stadistinct = distinct; } - return; } + +/** + * 检查给定的关系表达式是否合法,根据不同类型的表达式进行检查。 + * + * @param type 表达式类型,可以是 ES_EQSEL(等于选择)、ES_EQJOINSEL(等于连接选择)等 + * @param left 左侧表达式的节点 + * @param right 右侧表达式的节点 + * @param leftAttnum 左侧属性编号 + * @param rightAttnum 右侧属性编号 + * + * @return 如果表达式合法,返回 true;否则返回 false + */ static bool ClauseIsLegal(es_type type, const Node* left, const Node* right, int leftAttnum, int rightAttnum) { + // 检查属性编号是否为负数 if (leftAttnum < 0 || rightAttnum < 0) { - return false; + return false; } - /* check clause type */ + // 根据表达式类型执行不同的检查 switch (type) { case ES_EQSEL: + // 对于等于选择表达式,检查左右表达式是否为常数或参数 if (!IsA(left, Const) && !IsA(right, Const) && !IsA(left, Param) && !IsA(right, Param)) return false; + // 如果左侧为常数且为 NULL,或者右侧为常数且为 NULL,则认为表达式非法 else if (IsA(left, Const) && ((Const*)left)->constisnull) return false; else if (IsA(right, Const) && ((Const*)right)->constisnull) @@ -667,14 +868,31 @@ static bool ClauseIsLegal(es_type type, const Node* left, const Node* right, int default: break; } + + // 表达式合法 return true; } + +/** + * 检查给定的 RangeTblEntry 是否有效,必须为 RTE_RELATION(关系表达式)。 + * + * @param rte 要检查的 RangeTblEntry + * + * @return 如果 RangeTblEntry 有效且为 RTE_RELATION,则返回 true;否则返回 false + */ static inline bool RteIsValid(const RangeTblEntry* rte) { return (rte != NULL && rte->rtekind == RTE_RELATION); } +/** + * 设置 es_candidate 结构的初始值。 + * + * @param es 要设置的 es_candidate 结构 + * @param type 表达式类型,可以是 ES_EQSEL(等于选择)、ES_EQJOINSEL(等于连接选择)等 + * @param clause 关系表达式的 RestrictInfo + */ void ES_SELECTIVITY::setup_es(es_candidate* es, es_type type, RestrictInfo* clause) { es->tag = type; @@ -685,28 +903,57 @@ void ES_SELECTIVITY::setup_es(es_candidate* es, es_type type, RestrictInfo* clau es->right_first_mcvfreq = 0.0; } +/** + * 为等于选择表达式构建 es_candidate 结构。 + * + * @param es 要构建的 es_candidate 结构 + * @param var 表达式中的变量节点 + * @param attnum 属性编号 + * @param left 是否是左侧的表达式 + * @param clause 关系表达式的 RestrictInfo + * + * @return 如果成功构建 es_candidate,则返回 true;否则返回 false + */ bool ES_SELECTIVITY::build_es_candidate_for_eqsel(es_candidate* es, Node* var, int attnum, bool left, RestrictInfo* clause) { + // 读取变量关联的 RangeTblEntry 和关系表达式的左侧和右侧关系标识 read_rel_rte(var, &es->left_rel, &es->left_rte); + + // 检查左侧 RangeTblEntry 是否有效 if (!RteIsValid(es->left_rte)) { return false; } + + // 添加属性编号到左侧属性集合中 es->left_attnums = bms_add_member(es->left_attnums, attnum); + + // 根据左侧或右侧选择合适的关系标识 if (left) { - es->left_relids = - clause->left_relids != NULL ? bms_copy(clause->left_relids) : bms_copy(clause->clause_relids); + es->left_relids = clause->left_relids != NULL ? bms_copy(clause->left_relids) : bms_copy(clause->clause_relids); } else { - es->left_relids = - clause->right_relids != NULL ? bms_copy(clause->right_relids) : bms_copy(clause->clause_relids); + es->left_relids = clause->right_relids != NULL ? bms_copy(clause->right_relids) : bms_copy(clause->clause_relids); } + + // 添加属性映射到 es_candidate 结构 add_clause_map(es, attnum, 0, var, NULL); + + // 构建成功 return true; } + /* * @brief build a new es_candidate and add to es_candidate_list */ +/** + * 根据给定的 RestrictInfo 构建 es_candidate 结构。 + * + * @param clause 要构建的 RestrictInfo,包含了关系表达式的信息 + * @param type 表达式类型,可以是 ES_EQSEL(等于选择)、ES_EQJOINSEL(等于连接选择)等 + * + * @return 如果成功构建 es_candidate,则返回 true;否则返回 false + */ bool ES_SELECTIVITY::build_es_candidate(RestrictInfo* clause, es_type type) { Node* left = NULL; @@ -715,32 +962,40 @@ bool ES_SELECTIVITY::build_es_candidate(RestrictInfo* clause, es_type type) int right_attnum = 0; bool success = false; + // 检查关系表达式的类型,并提取左右操作数和属性编号 if (IsA(clause->clause, OpExpr)) { OpExpr* opclause = (OpExpr*)clause->clause; + // 确保操作数数量正确 Assert(list_length(opclause->args) == TOW_MEMBERS); left = (Node*)linitial(opclause->args); right = (Node*)lsecond(opclause->args); left_attnum = read_attnum(left); right_attnum = read_attnum(right); + + // 检查关系表达式是否合法 if (!ClauseIsLegal(type, left, right, left_attnum, right_attnum)) return false; } else { + // 处理空值测试表达式 Assert(IsA(clause->clause, NullTest)); NullTest* nullclause = (NullTest*)clause->clause; left = (Node*)nullclause->arg; left_attnum = read_attnum(left); + + // 检查属性编号是否有效 if (left_attnum < 0) return false; } + // 分配并初始化一个新的 es_candidate 结构 es_candidate* es = (es_candidate*)palloc(sizeof(es_candidate)); init_candidate(es); switch (type) { case ES_EQSEL: - /* only use left side */ + /* 只使用左侧 */ if (left_attnum > 0 && right_attnum == 0) { success = build_es_candidate_for_eqsel(es, left, left_attnum, true, clause); } else if (right_attnum > 0 && left_attnum == 0) { @@ -753,11 +1008,14 @@ bool ES_SELECTIVITY::build_es_candidate(RestrictInfo* clause, es_type type) break; case ES_EQJOINSEL: if (left_attnum > 0 && right_attnum > 0) { + // 读取左侧和右侧的 RangeTblEntry,并检查它们是否有效 read_rel_rte(left, &es->left_rel, &es->left_rte); read_rel_rte(right, &es->right_rel, &es->right_rte); if (!RteIsValid(es->left_rte) || !RteIsValid(es->right_rte)) { break; } + + // 设置左侧和右侧的属性集合以及其他相关信息 es->left_relids = bms_copy(clause->left_relids); es->right_relids = bms_copy(clause->right_relids); es->left_attnums = bms_add_member(es->left_attnums, left_attnum); @@ -770,12 +1028,12 @@ bool ES_SELECTIVITY::build_es_candidate(RestrictInfo* clause, es_type type) } break; default: - /* for future development, should not reach here now */ + /* 用于未来开发,当前不应该到达此处 */ pfree_ext(es); return false; } - /* double check */ + // 双重检查确保构建成功 if (!success) { es->left_rel = NULL; es->right_rel = NULL; @@ -785,89 +1043,140 @@ bool ES_SELECTIVITY::build_es_candidate(RestrictInfo* clause, es_type type) return false; } + // 设置 es_candidate 结构的其他属性 setup_es(es, type, clause); + // 将构建的 es_candidate 添加到 es_candidate_list 中 es_candidate_list = lappend(es_candidate_list, es); return true; } + /* * @brief remove useless member in es_candidate_list to unmatched_clause_group */ +/** + * 对 es_candidate_list 中的 es_candidate 结构进行重新检查。 + * + * 这个函数的主要目的是尝试使用等价类(equivalence class)来重新组合子句,以提高选择性估算的准确性。 + * 如果重新组合后的 es_candidate 结构有效,则保留它们;如果无效,则将其标记为空(ES_EMPTY)并将其子句添加到 unmatched_clause_group 中。 + * + * 注意:在这个函数中,如果一个 es_candidate 无效,它的 tag 被设置为空(ES_EMPTY),并且其子句将被添加到 unmatched_clause_group 中。 + */ void ES_SELECTIVITY::recheck_candidate_list() { if (!es_candidate_list) return; + ListCell* l = NULL; bool validate = true; - /* try to use equivalence_class to re-combinate clauses first */ + /* 尝试使用等价类重新组合子句 */ foreach(l, es_candidate_list) { es_candidate* temp = (es_candidate*)lfirst(l); + + // 如果当前 es_candidate 是 ES_EQJOINSEL,且只有一个子句,并且 es_candidate_list 中有多个 es_candidate, + // 则尝试使用等价类重新组合子句。 if (temp->tag == ES_EQJOINSEL && list_length(temp->clause_group) == 1 && list_length(es_candidate_list) > 1) (void)try_equivalence_class(temp); } foreach(l, es_candidate_list) { es_candidate* temp = (es_candidate*)lfirst(l); + + // 针对每个 es_candidate 进行检查 switch (temp->tag) { case ES_EQSEL: - if (list_length(temp->clause_group) <= 1) - validate = false; - else if (temp->left_rte && bms_num_members(temp->left_attnums) <= 1) + // 如果 ES_EQSEL 的子句数量小于等于1,或者左侧关系的属性数量小于等于1,则标记为无效 + if (list_length(temp->clause_group) <= 1 || (temp->left_rte && bms_num_members(temp->left_attnums) <= 1)) validate = false; break; case ES_EQJOINSEL: - if (list_length(temp->clause_group) <= 1) - validate = false; - else if (bms_num_members(temp->left_attnums) <= 1 || bms_num_members(temp->right_attnums) <= 1) + // 如果 ES_EQJOINSEL 的子句数量小于等于1,或者左侧或右侧关系的属性数量小于等于1,则标记为无效 + if (list_length(temp->clause_group) <= 1 || + bms_num_members(temp->left_attnums) <= 1 || bms_num_members(temp->right_attnums) <= 1) validate = false; break; case ES_GROUPBY: + // 如果 ES_GROUPBY 的左侧关系的属性数量小于等于1,则标记为无效 if (bms_num_members(temp->left_attnums) <= 1) validate = false; break; default: break; } + + // 如果当前 es_candidate 无效,将其子句添加到 unmatched_clause_group 中,并标记为空(ES_EMPTY) if (!validate) { unmatched_clause_group = list_concat(unmatched_clause_group, temp->clause_group); temp->tag = ES_EMPTY; temp->clause_group = NULL; } } - return; } + +/** + * 检查等价类是否属于不支持的情况。 + * + * 不支持的情况包括: + * 1. 等价类包含常量表达式(ec_has_const 为真)。 + * 2. 等价类被标记为损坏(ec_broken 为真),表示等价类无效或不完整。 + * 3. 等价类的成员数量小于等于 2(TOW_MEMBERS),因为这种情况下不会生成任何替代。 + * + * 如果等价类满足上述条件之一,函数将返回 true,表示等价类是不支持的情况。否则,返回 false。 + * + * @param ec 等价类结构的指针,用于检查其是否属于不支持的情况。 + * @return 如果等价类是不支持的情况,则返回 true;否则返回 false。 + */ static inline bool IsUnsupportedCases(const EquivalenceClass* ec) { - /* only consider var = var situation */ + /* 只考虑 var = var 的情况 */ if (ec->ec_has_const) return true; - /* ignore broken ecs */ + /* 忽略损坏的等价类 */ if (ec->ec_broken) return true; - /* if members of ECs are less than two, won't generate any substitute */ + /* 如果等价类的成员数量小于等于 2,不会生成任何替代 */ if (list_length(ec->ec_members) <= TOW_MEMBERS) return true; return false; } + /* * @brief pre-check the es candidate item is or not in current equivalence class. * @return bool, true or false. */ +/** + * 检查 es_candidate 是否在给定的等价类中。 + * + * 该函数检查 es_candidate 是否满足等价类中的某个成员条件,以确定其是否在等价类中。 + * 满足以下条件之一即可确定 es_candidate 在等价类中: + * 1. es_candidate 为 NULL 或等价类 ec 为 NULL,返回 false。 + * 2. 如果 es_candidate 的 relids 不是等价类 ec 的子集,返回 false。 + * 3. 遍历等价类的所有成员,对于每个成员 em,获取其表达式中的变量,然后检查是否满足以下条件之一: + * a. 变量 emVar 是有效的 Var 类型。 + * b. 变量 emVar 的 varattno 大于 0。 + * c. es_candidate 的左侧关系等于 em 的关系(relids),并且 emVar 的 varattno 包含在 es_candidate 的左侧 attnums 中。 + * d. es_candidate 的右侧关系等于 em 的关系(relids),并且 emVar 的 varattno 包含在 es_candidate 的右侧 attnums 中。 + * 4. 如果上述条件之一满足,则返回 true,表示 es_candidate 在等价类中;否则,返回 false。 + * + * @param es es_candidate 结构的指针,表示待检查的候选项。 + * @param ec EquivalenceClass 结构的指针,表示要检查的等价类。 + * @return 如果 es_candidate 在等价类中,则返回 true;否则返回 false。 + */ bool ES_SELECTIVITY::IsEsCandidateInEqClass(es_candidate *es, EquivalenceClass *ec) { if (es == NULL || ec == NULL) { return false; } - /* Quickly ignore any that don't cover the join */ + /* 快速排除不涵盖连接的候选项 */ if (!bms_is_subset(es->relids, ec->ec_relids)) { return false; } @@ -884,7 +1193,8 @@ bool ES_SELECTIVITY::IsEsCandidateInEqClass(es_candidate *es, EquivalenceClass * continue; } - /* left or right branch of join es occurs in the current equivalencen member, so the ec is valid. */ + /* 如果 es_candidate 的左侧关系等于 em 的关系(relids)且 emVar 在 es_candidate 的左侧 attnums 中,或者 + * es_candidate 的右侧关系等于 em 的关系(relids)且 emVar 在 es_candidate 的右侧 attnums 中,则该等价类有效。 */ if ((bms_equal(es->left_relids, em->em_relids) && bms_is_member(emVar->varattno, es->left_attnums)) || (bms_equal(es->right_relids, em->em_relids) && bms_is_member(emVar->varattno, es->right_attnums))) { return true; @@ -894,52 +1204,79 @@ bool ES_SELECTIVITY::IsEsCandidateInEqClass(es_candidate *es, EquivalenceClass * return false; } + /* * @brief try to find a substitude clause building from equivalence classes * @return true when find a substitude clause; false when find nothing */ bool ES_SELECTIVITY::try_equivalence_class(es_candidate* es) { + // 检查当前查询路径是否为 Merge Join 类型,如果是,则不使用等价类来调整子句 if (path && path->path.pathtype == T_MergeJoin) { - /* for mergejoin, do not adjust clause using equivalence class */ - return false; + return false; // 返回 false 表示不尝试使用等价类 } - ListCell* lc = NULL; - bool result = false; + ListCell* lc = NULL; // 定义一个链表元素的指针 + bool result = false; // 初始化一个布尔变量 result,表示最终结果 + // 遍历根节点的等价类列表 foreach(lc, root->eq_classes) { - EquivalenceClass* ec = (EquivalenceClass*)lfirst(lc); + EquivalenceClass* ec = (EquivalenceClass*)lfirst(lc); // 获取当前等价类 + // 检查是否有不支持的情况,如果有,跳过该等价类的处理 if (IsUnsupportedCases(ec)) continue; - /* ignore ec which does not contain the es info. */ + // 检查等价类是否包含了 es(es_candidate)信息,如果不包含,跳过该等价类的处理 if (!IsEsCandidateInEqClass(es, ec)) continue; + // 复制等价类的关系标识集合 Bitmapset* tmpset = bms_copy(ec->ec_relids); int ec_relid = 0; + + // 遍历等价类的关系标识集合 while ((ec_relid = bms_first_member(tmpset)) >= 0) { + // 如果当前关系标识在 es 的关系标识集合中,跳过 if (bms_is_member(ec_relid, es->relids)) continue; + ListCell* lc2 = NULL; + + // 遍历 es_candidate_list foreach(lc2, es_candidate_list) { es_candidate* temp = (es_candidate*)lfirst(lc2); + + // 如果 temp 不是 ES_EQJOINSEL 类型,跳过 if (temp->tag != ES_EQJOINSEL) continue; + + // 如果 temp 的关系标识与 es 不相等,跳过 if (bms_equal(temp->relids, es->relids)) continue; + + // 如果当前关系标识在 temp 的关系标识集合中,并且与 es 的关系标识集合有交集 if (bms_is_member(ec_relid, temp->relids) && bms_overlap(temp->relids, es->relids)) { + // 计算交集、并集以及外部关系标识 Bitmapset* interset_relids = bms_intersect(temp->relids, es->relids); Bitmapset* join_relids = bms_copy(interset_relids); join_relids = bms_add_member(join_relids, ec_relid); + + // 断言 join_relids 与 temp 的关系标识集合相等 Assert(bms_equal(join_relids, temp->relids)); + Bitmapset* outer_relids = bms_make_singleton(ec_relid); + + // 生成新的等价子句 List* pseudo_clauselist = generate_join_implied_equalities_normal(root, ec, join_relids, outer_relids, interset_relids); + + // 如果成功生成了新的子句 if (pseudo_clauselist != NULL) { + // 匹配新生成的子句与 es 的子句组 result = match_pseudo_clauselist(pseudo_clauselist, temp, es->clause_group); + + // 如果日志消息级别小于等于 ES_DEBUG_LEVEL,打印调试信息 if (log_min_messages <= ES_DEBUG_LEVEL) { ereport(ES_DEBUG_LEVEL, (errmodule(MOD_OPT_JOIN), errmsg("[ES]Build new clause using equivalence class:)"))); @@ -951,104 +1288,136 @@ bool ES_SELECTIVITY::try_equivalence_class(es_candidate* es) } } + // 释放内存 bms_free_ext(interset_relids); bms_free_ext(join_relids); bms_free_ext(outer_relids); + + // 如果成功匹配,替换原始子句列表中的子句 if (result) { - /* replace the removed clause in clauselist with the new built one */ ListCell* lc3 = NULL; foreach(lc3, origin_clauses) { void* clause = (void*)lfirst(lc3); if (clause == linitial(es->clause_group)) { - /* maybe cause memory problem as the old clause is not released */ lfirst(lc3) = linitial(pseudo_clauselist); } } - /* For hashclause, we have to process joinrestrictinfo in path as well */ + + // 对于 hashclause,需要处理路径中的 joinrestrictinfo if (path) { foreach(lc3, path->joinrestrictinfo) { void* clause = (void*)lfirst(lc3); if (clause == linitial(es->clause_group)) { - /* maybe cause memory problem as the old clause is not released */ lfirst(lc3) = linitial(pseudo_clauselist); } } } - break; + + break; // 跳出循环 } } } + + // 如果成功匹配,不再继续处理当前 es_candidate if (result) { /* - * If sucess, the clause has been tranformed and saved in another es_candidate. - * So no need to keep this es_candidate anymore. + * 如果成功,子句已经转换并保存在另一个 es_candidate 中。 + * 因此不再需要保留当前的 es_candidate。 */ es->tag = ES_EMPTY; es->clause_group = NULL; break; } } + + // 释放内存 bms_free_ext(tmpset); + + // 如果成功匹配,不再继续处理其他等价类 if (result) break; } + + // 返回最终结果 return result; } + /* * @brief try to match the newborn clause building by try_equivalence_class() with the existed clause group * like what we do in group_clauselist(), but more simple. */ bool ES_SELECTIVITY::match_pseudo_clauselist(List* clauses, es_candidate* es, List* origin_clause) { - bool result = false; - ListCell* lc = NULL; - foreach(lc, clauses) { - Node* clause = (Node*)lfirst(lc); + bool result = false; // 初始化一个布尔变量 result,表示匹配结果 + ListCell* lc = NULL; // 定义一个链表元素的指针 + // 遍历生成的伪子句列表 + foreach(lc, clauses) { + Node* clause = (Node*)lfirst(lc); // 获取当前伪子句 + + // 如果当前子句不是 RestrictInfo 类型,跳过 if (!IsA(clause, RestrictInfo)) { continue; } - RestrictInfo* rinfo = (RestrictInfo*)clause; + RestrictInfo* rinfo = (RestrictInfo*)clause; // 强制转换为 RestrictInfo 类型 + + // 如果子句是伪常量、选择率大于1或者是 OR 子句,跳过 if (rinfo->pseudoconstant || rinfo->norm_selec > 1 || rinfo->orclause) { continue; } + // 如果子句是操作符子句 if (is_opclause(rinfo->clause)) { OpExpr* opclause = (OpExpr*)rinfo->clause; Oid opno = opclause->opno; - /* only handle "=" operator */ + // 只处理 "=" 运算符 if (get_oprrest(opno) == EQSELRETURNOID) { + // 断言子句关联的关系标识数量为 2(两个关系) Assert(bms_num_members(rinfo->clause_relids) == TOW_MEMBERS); + + // 调用 add_attnum 函数,将关联的属性编号添加到 es 中 if (add_attnum(rinfo, es)) { + // 将子句添加到 es 的子句组中 es->clause_group = lappend(es->clause_group, clause); + + // 将子句添加到 es 的伪子句列表中 es->pseudo_clause_list = lappend(es->pseudo_clause_list, clause); + + // 将原始子句列表与伪子句列表合并,并添加到 es 的伪子句列表中 es->pseudo_clause_list = list_concat(es->pseudo_clause_list, origin_clause); - result = true; + + result = true; // 设置匹配结果为 true } } } } - return result; + + return result; // 返回匹配结果 } + /* * @brief relpace the original clause in the input clause list with the new clause build by equivalence class, * the memory used by old clause will be release by optimizer context or something esle */ void ES_SELECTIVITY::replace_clause(Datum* old_clause, Datum* new_clause) const { - ListCell* lc = NULL; + ListCell* lc = NULL; // 定义一个链表元素的指针 + + // 遍历原始子句列表 origin_clauses foreach(lc, origin_clauses) { + // 检查当前链表元素是否等于要替换的旧子句 old_clause if (lfirst(lc) == old_clause) { - lfirst(lc) = new_clause; - break; + lfirst(lc) = new_clause; // 将当前链表元素替换为新子句 new_clause + break; // 中断循环,替换完成后不再继续遍历 } } } + /* * @brief main entry to read statistic which will be used to calculate selectivity, called by * calculate_selectivity diff --git a/src/gausskernel/optimizer/path/pathkeys.cpp b/src/gausskernel/optimizer/path/pathkeys.cpp index 02d04e8be..566d167fb 100644 --- a/src/gausskernel/optimizer/path/pathkeys.cpp +++ b/src/gausskernel/optimizer/path/pathkeys.cpp @@ -71,35 +71,40 @@ static PathKey* makePathKey(EquivalenceClass* eclass, Oid opfamily, int strategy static PathKey* make_canonical_pathkey( PlannerInfo* root, EquivalenceClass* eclass, Oid opfamily, int strategy, bool nulls_first) { - PathKey* pk = NULL; - ListCell* lc = NULL; - MemoryContext oldcontext; + PathKey* pk = NULL; // 声明一个指向PathKey的指针,并将其初始化为NULL。 + ListCell* lc = NULL; // 声明一个指向ListCell的指针,并将其初始化为NULL。 + MemoryContext oldcontext; // 声明一个MemoryContext变量,命名为oldcontext。 - /* The passed eclass might be non-canonical, so chase up to the top */ + // 遍历等价类(EquivalenceClass)的层次结构,直到达到顶层的等价类。 while (eclass->ec_merged) eclass = eclass->ec_merged; + // 遍历PlannerInfo中的规范路径键(canonical pathkeys)列表。 foreach (lc, root->canon_pathkeys) { - /* Here need ensure ec_group_set be also equal. */ + // 获取列表中的下一个PathKey。 pk = (PathKey*)lfirst(lc); + + // 检查当前PathKey是否与给定的等价类(eclass)、操作族(opfamily)、策略(strategy)和nulls_first相匹配。 if (eclass == pk->pk_eclass && eclass->ec_group_set == pk->pk_eclass->ec_group_set && OpFamilyEquals(opfamily, pk->pk_opfamily) && strategy == pk->pk_strategy && nulls_first == pk->pk_nulls_first) - return pk; + return pk; // 如果找到匹配的PathKey,则返回该PathKey。 } - /* * Be sure canonical pathkeys are allocated in the main planning context. * Not an issue in normal planning, but it is for GEQO. */ + // 切换到另一个内存上下文以进行分配。 oldcontext = MemoryContextSwitchTo(root->planner_cxt); + // 使用提供的参数创建新的PathKey,并将其添加到规范路径键列表中。 pk = makePathKey(eclass, opfamily, strategy, nulls_first); root->canon_pathkeys = lappend(root->canon_pathkeys, pk); + // 切换回原始内存上下文。 (void)MemoryContextSwitchTo(oldcontext); - return pk; + return pk; // 返回创建的或现有的PathKey。 } /* @@ -134,50 +139,52 @@ static PathKey* make_canonical_pathkey( */ static bool pathkey_is_redundant(PathKey* new_pathkey, List* pathkeys, bool predpush) { - EquivalenceClass* new_ec = new_pathkey->pk_eclass; - ListCell* lc = NULL; + EquivalenceClass* new_ec = new_pathkey->pk_eclass; // 获取新路径键的等价类。 - /* Assert we've been given canonical pathkeys */ + // 使用断言确保新等价类未合并。 Assert(!new_ec->ec_merged); - /* Check for EC containing a constant --- unconditionally redundant */ - if (predpush) { - /* skip the Param */ - bool have_const = false; - if (EC_MUST_BE_REDUNDANT(new_ec)) - { + if (predpush) { // 如果是谓词推送(predicate pushdown): + + bool have_const = false; // 初始化一个标志,表示是否存在常量表达式。 + + if (EC_MUST_BE_REDUNDANT(new_ec)) { // 检查等价类是否必须是冗余的。 + lc = NULL; - foreach (lc, new_ec->ec_members) { + foreach (lc, new_ec->ec_members) { // 遍历等价类的成员。 EquivalenceMember *mem = (EquivalenceMember *)lfirst(lc); if (mem->em_is_const && !check_param_clause((Node *)mem->em_expr)) { - have_const = true; + have_const = true; // 如果存在常量表达式且不是参数子句,将标志设置为true。 break; } } } + // 如果存在常量且不在外连接下,且没有等价类组合,则认为是冗余的。 if ((have_const && !new_ec->ec_below_outer_join) && !new_ec->ec_group_set) return true; - } else { + } else { // 如果不是谓词推送: + + // 如果等价类必须是冗余的且没有等价类组合,则认为是冗余的。 if (EC_MUST_BE_REDUNDANT(new_ec) && !new_ec->ec_group_set) - return true; - } - - /* If same EC already used in list, then redundant */ - foreach (lc, pathkeys) { - PathKey* old_pathkey = (PathKey*)lfirst(lc); - - /* Assert we've been given canonical pathkeys */ - Assert(!old_pathkey->pk_eclass->ec_merged); - - if (new_ec == old_pathkey->pk_eclass) return true; } - return false; + foreach (lc, pathkeys) { // 遍历路径键列表。 + PathKey* old_pathkey = (PathKey*)lfirst(lc); // 获取旧路径键。 + + // 使用断言确保旧路径键的等价类未合并。 + Assert(!old_pathkey->pk_eclass->ec_merged); + + if (new_ec == old_pathkey->pk_eclass) // 如果新等价类与旧路径键的等价类相同,则认为是冗余的。 + return true; + } + + return false; // 如果没有冗余,返回false。 } + /* * canonicalize_pathkeys * Convert a not-necessarily-canonical pathkeys list to canonical form. @@ -228,33 +235,31 @@ List* canonicalize_pathkeys(PlannerInfo* root, List* pathkeys) */ List* remove_param_pathkeys(PlannerInfo* root, List* pathkeys) { - List* new_pathkeys = NIL; - ListCell* l = NULL; + List* new_pathkeys = NIL; // 声明一个新的路径键列表,并初始化为空列表。 + ListCell* l = NULL; // 声明一个指向ListCell的指针,并初始化为空。 - if (pathkeys == NULL) + if (pathkeys == NULL) // 如果输入的路径键列表为空,直接返回空列表。 return NULL; - foreach (l, pathkeys) { - PathKey* pathkey = (PathKey*)lfirst(l); - EquivalenceClass* eclass = NULL; + foreach (l, pathkeys) { // 遍历输入的路径键列表。 + PathKey* pathkey = (PathKey*)lfirst(l); // 获取当前路径键。 - /* Find the canonical (merged) EquivalenceClass */ - eclass = pathkey->pk_eclass; - Assert(eclass->ec_merged == NULL); + EquivalenceClass* eclass = NULL; // 声明一个指向等价类的指针,并初始化为空。 - /* - * If we can tell it's redundant just from the EC, skip. - * pathkey_is_redundant would notice that, but we needn't even bother - * constructing the node... - */ + eclass = pathkey->pk_eclass; // 获取路径键的等价类。 + Assert(eclass->ec_merged == NULL); // 使用断言确保等价类未合并。 + + // 如果等价类必须是冗余的且没有等价类组合,则跳过当前路径键。 if (EC_MUST_BE_REDUNDANT(eclass) && !eclass->ec_group_set) continue; - new_pathkeys = lappend(new_pathkeys, pathkey); + new_pathkeys = lappend(new_pathkeys, pathkey); // 否则,将路径键添加到新的路径键列表中。 } - return new_pathkeys; + + return new_pathkeys; // 返回新的路径键列表。 } + /* * make_pathkey_from_sortinfo * Given an expression and sort-order information, create a PathKey. @@ -279,11 +284,12 @@ List* remove_param_pathkeys(PlannerInfo* root, List* pathkeys) static PathKey* make_pathkey_from_sortinfo(PlannerInfo* root, Expr* expr, Oid opfamily, Oid opcintype, Oid collation, bool reverse_sort, bool nulls_first, Index sortref, bool groupSet, Relids rel, bool create_it, bool canonicalize) { - int16 strategy; - Oid equality_op; - List* opfamilies = NIL; - EquivalenceClass* eclass = NULL; + int16 strategy; // 声明一个int16类型的变量strategy,用于表示排序策略。 + Oid equality_op; // 声明一个Oid类型的变量equality_op,用于表示相等比较运算符。 + List* opfamilies = NIL; // 声明一个List类型的变量opfamilies,用于保存操作族列表。 + EquivalenceClass* eclass = NULL; // 声明一个EquivalenceClass类型的指针eclass,初始化为NULL。 + // 根据是否逆序设置排序策略。 strategy = reverse_sort ? BTGreaterStrategyNumber : BTLessStrategyNumber; /* @@ -292,28 +298,36 @@ static PathKey* make_pathkey_from_sortinfo(PlannerInfo* root, Expr* expr, Oid op * more than one opfamily. So we have to look up the opfamily's equality * operator and get its membership. */ + // 获取操作族中的相等比较运算符。 equality_op = get_opfamily_member(opfamily, opcintype, opcintype, BTEqualStrategyNumber); - if (!OidIsValid(equality_op)) /* shouldn't happen */ + + // 如果没有找到相等比较运算符,报告错误。 + if (!OidIsValid(equality_op)) ereport(ERROR, (errmodule(MOD_OPT), errcode(ERRCODE_OPTIMIZER_INCONSISTENT_STATE), (errmsg( "could not find equality operator for opfamily %u when make pathkey from sortinfo", opfamily)))); + + // 获取相等比较运算符对应的操作族列表。 opfamilies = get_mergejoin_opfamilies(equality_op); - if (opfamilies == NIL) /* certainly should find some */ + + // 如果没有找到操作族列表,报告错误。 + if (opfamilies == NIL) ereport(ERROR, (errmodule(MOD_OPT), errcode(ERRCODE_OPTIMIZER_INCONSISTENT_STATE), (errmsg("could not find opfamilies for equality operator %u when make pathkey from sortinfo", equality_op)))); - /* Now find or (optionally) create a matching EquivalenceClass */ + + // 为排序表达式获取等价类。 eclass = get_eclass_for_sort_expr(root, expr, opfamilies, opcintype, collation, sortref, groupSet, rel, create_it); - /* Fail if no EC and !create_it */ + // 如果未找到等价类,返回NULL。 if (eclass == NULL) return NULL; - /* And finally we can find or create a PathKey node */ + // 如果要规范化路径键,调用make_canonical_pathkey函数,否则调用makePathKey函数。 if (canonicalize) return make_canonical_pathkey(root, eclass, opfamily, strategy, nulls_first); else @@ -333,16 +347,17 @@ static PathKey* make_pathkey_from_sortop(PlannerInfo* root, Expr* expr, Oid orde Oid opfamily, opcintype, collation; int16 strategy; - /* Find the operator in pg_amop --- failure shouldn't happen */ + /* 通过在pg_amop中查找操作符,获取操作族、操作数类型和排序策略 */ if (!get_ordering_op_properties(ordering_op, &opfamily, &opcintype, &strategy)) ereport(ERROR, (errmodule(MOD_OPT), errcode(ERRCODE_OPTIMIZER_INCONSISTENT_STATE), (errmsg("operator %u is not a valid ordering operator when make pathkey from sortinfo", ordering_op)))); - /* Because SortGroupClause doesn't carry collation, consult the expr */ + /* 由于SortGroupClause不携带排序规则,需查看表达式获取排序规则 */ collation = exprCollation((Node*)expr); + // 调用make_pathkey_from_sortinfo函数来创建路径键,并返回结果。 return make_pathkey_from_sortinfo(root, expr, opfamily, @@ -356,7 +371,6 @@ static PathKey* make_pathkey_from_sortop(PlannerInfo* root, Expr* expr, Oid orde create_it, canonicalize); } - /**************************************************************************** * PATHKEY COMPARISONS ****************************************************************************/ @@ -392,25 +406,32 @@ PathKeysComparison compare_pathkeys(List* keys1, List* keys2) * but PlannerInfo not accessible here... */ #ifdef NOT_USED - AssertEreport(list_member_ptr(root->canon_pathkeys, pathkey1), MOD_OPT, "pathky1 is not a member in pathkeys"); + // 使用断言确保pathkey1在root->canon_pathkeys中。 + AssertEreport(list_member_ptr(root->canon_pathkeys, pathkey1), MOD_OPT, "pathkey1 is not a member in pathkeys"); - AssertEreport(list_member_ptr(root->canon_pathkeys, pathkey2), MOD_OPT, "pathky2 is not a member in pathkeys"); + // 使用断言确保pathkey2在root->canon_pathkeys中。 + AssertEreport(list_member_ptr(root->canon_pathkeys, pathkey2), MOD_OPT, "pathkey2 is not a member in pathkeys"); #endif - if (pathkey1 == pathkey2) { - continue; - } - if (pathkey1 == NULL && pathkey2 != NULL) { - return PATHKEYS_DIFFERENT; /* no need to keep looking */ - } - if (pathkey1 != NULL && pathkey2 == NULL) { - return PATHKEYS_DIFFERENT; /* no need to keep looking */ - } - if (pathkey1->type != pathkey2->type || !OpFamilyEquals(pathkey1->pk_opfamily, pathkey2->pk_opfamily) || - pathkey1->pk_eclass != pathkey2->pk_eclass || pathkey1->pk_strategy != pathkey2->pk_strategy || - pathkey1->pk_nulls_first != pathkey2->pk_nulls_first) { - return PATHKEYS_DIFFERENT; /* no need to keep looking */ - } + + // 如果pathkey1等于pathkey2,继续循环。 + if (pathkey1 == pathkey2) { + continue; } + // 如果pathkey1为NULL而pathkey2不为NULL,则返回PATHKEYS_DIFFERENT,不再继续查找。 + if (pathkey1 == NULL && pathkey2 != NULL) { + return PATHKEYS_DIFFERENT; + } + // 如果pathkey1不为NULL而pathkey2为NULL,则返回PATHKEYS_DIFFERENT,不再继续查找。 + if (pathkey1 != NULL && pathkey2 == NULL) { + return PATHKEYS_DIFFERENT; + } + // 如果pathkey1和pathkey2的类型、操作族、等价类、策略和nulls_first都不相等,则返回PATHKEYS_DIFFERENT,不再继续查找。 + if (pathkey1->type != pathkey2->type || !OpFamilyEquals(pathkey1->pk_opfamily, pathkey2->pk_opfamily) || + pathkey1->pk_eclass != pathkey2->pk_eclass || pathkey1->pk_strategy != pathkey2->pk_strategy || + pathkey1->pk_nulls_first != pathkey2->pk_nulls_first) { + return PATHKEYS_DIFFERENT; + } +} /* * If we reached the end of only one list, the other is longer and @@ -532,32 +553,34 @@ Path* get_cheapest_fractional_path_for_pathkeys(List* paths, List* pathkeys, Rel */ List* build_index_pathkeys(PlannerInfo* root, IndexOptInfo* index, ScanDirection scandir) { - List* retval = NIL; - ListCell* lc = NULL; + List* retval = NIL; // 声明一个结果列表,并初始化为空。 + ListCell* lc = NULL; // 声明一个指向ListCell的指针,并初始化为空。 int i; + // 如果索引没有排序操作符族(sortopfamily),返回一个空列表,表示索引不支持有序扫描。 if (index->sortopfamily == NULL) - return NIL; /* non-orderable index */ + return NIL; i = 0; - foreach (lc, index->indextlist) { - TargetEntry* indextle = (TargetEntry*)lfirst(lc); - Expr* indexkey = NULL; - bool reverse_sort = false; - bool nulls_first = false; - PathKey* cpathkey = NULL; + foreach (lc, index->indextlist) { // 遍历索引列列表。 + TargetEntry* indextle = (TargetEntry*)lfirst(lc); // 获取索引目标条目。 + Expr* indexkey = NULL; // 声明一个表达式指针,并初始化为空。 + bool reverse_sort = false; // 是否逆序排序的标志,默认为false。 + bool nulls_first = false; // NULL值排在前面的标志,默认为false。 + PathKey* cpathkey = NULL; // 声明一个PathKey指针,并初始化为空。 /* - * INCLUDE columns are stored in index unordered, so they don't - * support ordered index scan. + * INCLUDE列存储在索引中无序,因此它们不支持有序索引扫描。 + * 如果i大于或等于索引的关键列数,跳出循环。 */ if (i >= index->nkeycolumns) { break; } - /* We assume we don't need to make a copy of the tlist item */ - indexkey = indextle->expr; + /* 假设我们不需要复制tlist项目 */ + indexkey = indextle->expr; // 获取索引键的表达式。 + // 根据扫描方向设置逆序排序和NULL值排在前面的标志。 if (ScanDirectionIsBackward(scandir)) { reverse_sort = !index->reverse_sort[i]; nulls_first = !index->nulls_first[i]; @@ -566,7 +589,7 @@ List* build_index_pathkeys(PlannerInfo* root, IndexOptInfo* index, ScanDirection nulls_first = index->nulls_first[i]; } - /* OK, try to make a canonical pathkey for this sort key */ + /* 尝试为这个排序键创建一个规范路径键 */ cpathkey = make_pathkey_from_sortinfo(root, indexkey, index->sortopfamily[i], @@ -581,23 +604,23 @@ List* build_index_pathkeys(PlannerInfo* root, IndexOptInfo* index, ScanDirection true); /* - * If the sort key isn't already present in any EquivalenceClass, then - * it's not an interesting sort order for this query. So we can stop - * now --- lower-order sort keys aren't useful either. + * 如果排序键尚未存在于任何等价类中,则它对于此查询不是有趣的排序顺序。 + * 因此,我们可以停止遍历 --- 较低阶的排序键也不有用。 */ if (cpathkey == NULL) break; - /* Add to list unless redundant */ + /* 将路径键添加到列表,除非它是冗余的 */ if (!pathkey_is_redundant(cpathkey, retval)) retval = lappend(retval, cpathkey); i++; } - return retval; + return retval; // 返回结果列表。 } + /* * convert_subquery_pathkeys * Build a pathkeys list that describes the ordering of a subquery's @@ -613,43 +636,55 @@ List* build_index_pathkeys(PlannerInfo* root, IndexOptInfo* index, ScanDirection */ List* convert_subquery_pathkeys(PlannerInfo* root, RelOptInfo* rel, List* subquery_pathkeys) { - List* retval = NIL; - int retvallen = 0; - int outer_query_keys = list_length(root->query_pathkeys); - List* sub_tlist = rel->subplan->targetlist; - ListCell* i = NULL; + List* retval = NIL; // 声明一个结果列表,并初始化为空。 + int retvallen = 0; // 初始化结果列表的长度为0。 + int outer_query_keys = list_length(root->query_pathkeys); // 获取外部查询的路径键数。 + List* sub_tlist = rel->subplan->targetlist; // 获取子查询的目标列表。 + ListCell* i = NULL; // 声明一个指向ListCell的指针,并初始化为空。 + // 遍历子查询的路径键列表。 foreach (i, subquery_pathkeys) { - PathKey* sub_pathkey = (PathKey*)lfirst(i); - EquivalenceClass* sub_eclass = sub_pathkey->pk_eclass; - PathKey* best_pathkey = NULL; + PathKey* sub_pathkey = (PathKey*)lfirst(i); // 获取子查询路径键。 + EquivalenceClass* sub_eclass = sub_pathkey->pk_eclass; // 获取子查询等价类。 + PathKey* best_pathkey = NULL; // 声明一个最佳路径键指针,并初始化为空。 if (sub_eclass->ec_has_volatile) { /* - * If the sub_pathkey's EquivalenceClass is volatile, then it must - * have come from an ORDER BY clause, and we have to match it to - * that same targetlist entry. + * 如果子查询路径键的等价类是易失性的,那么它必须来自于ORDER BY子句, + * 我们必须将它与相同的目标列表条目匹配。 */ TargetEntry* tle = NULL; - if (sub_eclass->ec_sortref == 0) /* can't happen */ + // 如果子查询路径键的等价类没有排序引用(sortref),则报告错误。 + if (sub_eclass->ec_sortref == 0) ereport(ERROR, (errmodule(MOD_OPT), errcode(ERRCODE_OPTIMIZER_INCONSISTENT_STATE), (errmsg("volatile EquivalenceClass has no sortref when convert subquery pathkeys")))); + + // 获取与子查询路径键等价类对应的目标列表条目。 tle = get_sortgroupref_tle(sub_eclass->ec_sortref, sub_tlist); + + // 使用断言确保目标列表条目不为空。 AssertEreport(tle != NULL, MOD_OPT, "tle is NULL"); - /* resjunk items aren't visible to outer query */ + + /* resjunk 项对外部查询不可见 */ if (!tle->resjunk) { - /* We can represent this sub_pathkey */ + /* 我们可以表示这个子查询路径键 */ EquivalenceMember* sub_member = NULL; Expr* outer_expr = NULL; EquivalenceClass* outer_ec = NULL; + // 使用断言确保等价类成员列表长度为1。 AssertEreport(list_length(sub_eclass->ec_members) == 1, MOD_OPT, "ec member number is not 1"); + + // 获取等价类的成员。 sub_member = (EquivalenceMember*)linitial(sub_eclass->ec_members); + + // 创建外部查询的表达式,以引用子查询的目标列表条目。 outer_expr = (Expr*)makeVarFromTargetEntry(rel->relid, tle); + /* * Note: it might look funny to be setting sortref = 0 for a * reference to a volatile sub_eclass. However, the @@ -696,44 +731,47 @@ List* convert_subquery_pathkeys(PlannerInfo* root, RelOptInfo* rel, List* subque * query_pathkeys). This is the most likely to be useful in the * outer query. */ - int best_score = -1; - ListCell* j = NULL; + int best_score = -1; // 初始化最佳分数为-1。 +ListCell* j = NULL; // 声明一个指向ListCell的指针,并初始化为空。 - foreach (j, sub_eclass->ec_members) { - EquivalenceMember* sub_member = (EquivalenceMember*)lfirst(j); - Expr* sub_expr = sub_member->em_expr; - Oid sub_expr_type = sub_member->em_datatype; - Oid sub_expr_coll = sub_eclass->ec_collation; - ListCell* k = NULL; - int seq = 0; +foreach (j, sub_eclass->ec_members) { + EquivalenceMember* sub_member = (EquivalenceMember*)lfirst(j); // 获取等价类成员。 + Expr* sub_expr = sub_member->em_expr; // 获取等价类成员的表达式。 + Oid sub_expr_type = sub_member->em_datatype; // 获取等价类成员的数据类型。 + Oid sub_expr_coll = sub_eclass->ec_collation; // 获取等价类成员的排序规则。 + ListCell* k = NULL; + int seq = 0; - if (sub_member->em_is_child) - continue; /* ignore children here */ + if (sub_member->em_is_child) + continue; /* 忽略子查询的子查询等价类 */ - foreach (k, sub_tlist) { - TargetEntry* tle = (TargetEntry*)lfirst(k); - Expr* tle_expr = NULL; - Expr* outer_expr = NULL; - EquivalenceClass* outer_ec = NULL; - PathKey* outer_pk = NULL; - int score; - ListCell* lc = NULL; + foreach (k, sub_tlist) { + TargetEntry* tle = (TargetEntry*)lfirst(k); // 获取子查询目标列表的目标条目。 + Expr* tle_expr = NULL; + Expr* outer_expr = NULL; + EquivalenceClass* outer_ec = NULL; + PathKey* outer_pk = NULL; + int score; + ListCell* lc = NULL; - seq++; + seq++; - /* resjunk items aren't visible to outer query */ - if (tle->resjunk) - continue; + /* resjunk 项对外部查询不可见 */ + if (tle->resjunk) + continue; - /* check if targetentry exists in final subquery targetlist */ - foreach (lc, rel->reltargetlist) { - Node* n = (Node*)lfirst(lc); - if (IsA(n, Var) && ((Var*)n)->varattno == seq) - break; - } + /* + * 检查目标条目是否存在于最终的子查询目标列表中。 + * 如果不在子查询目标列表中,则继续下一条目。 + */ + foreach (lc, rel->reltargetlist) { + Node* n = (Node*)lfirst(lc); + if (IsA(n, Var) && ((Var*)n)->varattno == seq) + break; + } - if (lc == NULL) - continue; + if (lc == NULL) + continue; /* * The targetlist entry is considered to match if it @@ -861,16 +899,22 @@ List* build_join_pathkeys(PlannerInfo* root, RelOptInfo* joinrel, JoinType joint */ List* make_pathkeys_for_sortclauses(PlannerInfo* root, List* sortclauses, List* tlist, bool canonicalize) { - List* pathkeys = NIL; - ListCell* l = NULL; + List* pathkeys = NIL; // 声明一个路径键列表,并初始化为空。 + ListCell* l = NULL; // 声明一个指向ListCell的指针,并初始化为空。 + // 遍历排序子句列表。 foreach (l, sortclauses) { - SortGroupClause* sortcl = (SortGroupClause*)lfirst(l); - Expr* sortkey = NULL; - PathKey* pathkey = NULL; + SortGroupClause* sortcl = (SortGroupClause*)lfirst(l); // 获取排序子句。 + Expr* sortkey = NULL; // 声明一个排序键表达式指针,并初始化为空。 + PathKey* pathkey = NULL; // 声明一个路径键指针,并初始化为空。 + // 获取排序子句的排序键表达式。 sortkey = (Expr*)get_sortgroupclause_expr(sortcl, tlist); + + // 使用断言确保排序子句的排序操作符是有效的。 AssertEreport(OidIsValid(sortcl->sortop), MOD_OPT, "ordering operator is invalid"); + + // 调用make_pathkey_from_sortop函数创建路径键。 pathkey = make_pathkey_from_sortop(root, sortkey, sortcl->sortop, @@ -880,16 +924,19 @@ List* make_pathkeys_for_sortclauses(PlannerInfo* root, List* sortclauses, List* true, canonicalize); - /* Canonical form eliminates redundant ordering keys */ + /* 规范形式消除冗余的排序键 */ if (canonicalize) { + // 如果路径键不是冗余的,则将其添加到路径键列表中。 if (!pathkey_is_redundant(pathkey, pathkeys, ENABLE_PRED_PUSH_ALL(root))) pathkeys = lappend(pathkeys, pathkey); } else - pathkeys = lappend(pathkeys, pathkey); + pathkeys = lappend(pathkeys, pathkey); // 将路径键添加到路径键列表中。 } - return pathkeys; + + return pathkeys; // 返回路径键列表。 } + /**************************************************************************** * PATHKEYS AND MERGECLAUSES ****************************************************************************/ @@ -914,17 +961,19 @@ List* make_pathkeys_for_sortclauses(PlannerInfo* root, List* sortclauses, List* */ void initialize_mergeclause_eclasses(PlannerInfo* root, RestrictInfo* restrictinfo) { - Expr* clause = restrictinfo->clause; + Expr* clause = restrictinfo->clause;// 获取限制信息中的表达式。 Oid lefttype, righttype; /* Should be a mergeclause ... */ AssertEreport(restrictinfo->mergeopfamilies != NIL, MOD_OPT, "clause is not mergejoinable"); + //检查限制信息是否表示一个合并条件(mergeclause) /* ... with links not yet set */ AssertEreport(restrictinfo->left_ec == NULL, MOD_OPT, "lefthand mergeclause processing is set"); AssertEreport(restrictinfo->right_ec == NULL, MOD_OPT, "righthand mergeclause processing is set"); + //检查左侧和右侧等价类是否尚未设置链接 /* Need the declared input types of the operator */ - op_input_types(((OpExpr*)clause)->opno, &lefttype, &righttype); + op_input_types(((OpExpr*)clause)->opno, &lefttype, &righttype);// 获取操作符的输入类型 /* Find or create a matching EquivalenceClass for each side */ restrictinfo->left_ec = get_eclass_for_sort_expr(root, @@ -992,21 +1041,22 @@ void update_mergeclause_eclasses(PlannerInfo* root, RestrictInfo* restrictinfo) */ List* find_mergeclauses_for_outer_pathkeys(PlannerInfo* root, List* pathkeys, List* restrictinfos) { - List* mergeclauses = NIL; - ListCell* i = NULL; + List* mergeclauses = NIL; // 初始化合并条件列表为空。 + ListCell* i = NULL; // 声明一个指向ListCell的指针,并初始化为空。 /* make sure we have eclasses cached in the clauses */ + // 遍历限制信息列表。 foreach (i, restrictinfos) { - RestrictInfo* rinfo = (RestrictInfo*)lfirst(i); + RestrictInfo* rinfo = (RestrictInfo*)lfirst(i); // 获取限制信息。 - update_mergeclause_eclasses(root, rinfo); + update_mergeclause_eclasses(root, rinfo); // 更新合并条件的等价类。 } foreach (i, pathkeys) { - PathKey* pathkey = (PathKey*)lfirst(i); - EquivalenceClass* pathkey_ec = pathkey->pk_eclass; - List* matched_restrictinfos = NIL; - ListCell* j = NULL; + PathKey* pathkey = (PathKey*)lfirst(i); // 获取路径键。 + EquivalenceClass* pathkey_ec = pathkey->pk_eclass; // 获取路径键的等价类。 + List* matched_restrictinfos = NIL; // 初始化匹配的限制信息列表为空。 + ListCell* j = NULL; // 声明一个指向ListCell的指针,并初始化为空。 /* ---------- * A mergejoin clause matches a pathkey if it has the same EC. @@ -1248,43 +1298,44 @@ List* select_outer_pathkeys_for_merge(PlannerInfo* root, List* mergeclauses, Rel */ List* make_inner_pathkeys_for_merge(PlannerInfo* root, List* mergeclauses, List* outer_pathkeys) { - List* pathkeys = NIL; - EquivalenceClass* lastoeclass = NULL; - PathKey* opathkey = NULL; - ListCell* lc = NULL; - ListCell* lop = NULL; + List* pathkeys = NIL; // 初始化内部路径键列表为空。 + EquivalenceClass* lastoeclass = NULL; // 上一个外部等价类。 + PathKey* opathkey = NULL; // 外部路径键。 + ListCell* lc = NULL; // 声明一个指向ListCell的指针。 + ListCell* lop = NULL; // 声明一个指向ListCell的指针。 - lastoeclass = NULL; - opathkey = NULL; - lop = list_head(outer_pathkeys); + lastoeclass = NULL; // 初始化上一个外部等价类为空。 + opathkey = NULL; // 初始化外部路径键为空。 + lop = list_head(outer_pathkeys); // 初始化外部路径键列表的头指针。 + // 遍历合并条件列表。 foreach (lc, mergeclauses) { - RestrictInfo* rinfo = (RestrictInfo*)lfirst(lc); - EquivalenceClass* oeclass = NULL; - EquivalenceClass* ieclass = NULL; - PathKey* pathkey = NULL; + RestrictInfo* rinfo = (RestrictInfo*)lfirst(lc); // 获取限制信息。 + EquivalenceClass* oeclass = NULL; // 外部等价类。 + EquivalenceClass* ieclass = NULL; // 内部等价类。 + PathKey* pathkey = NULL; // 路径键。 - update_mergeclause_eclasses(root, rinfo); + update_mergeclause_eclasses(root, rinfo); // 更新合并条件的等价类。 if (rinfo->outer_is_left) { - oeclass = rinfo->left_ec; - ieclass = rinfo->right_ec; + oeclass = rinfo->left_ec; // 左侧是外部等价类。 + ieclass = rinfo->right_ec; // 右侧是内部等价类。 } else { - oeclass = rinfo->right_ec; - ieclass = rinfo->left_ec; + oeclass = rinfo->right_ec; // 右侧是外部等价类。 + ieclass = rinfo->left_ec; // 左侧是内部等价类。 } - /* outer eclass should match current or next pathkeys */ /* we check this carefully for debugging reasons */ + // 如果外部等价类不同于上一个外部等价类。 if (oeclass != lastoeclass) { if (lop == NULL) ereport(ERROR, (errmodule(MOD_OPT), errcode(ERRCODE_OPTIMIZER_INCONSISTENT_STATE), (errmsg("too few pathkeys for mergeclauses when make inner pathkeys for merge")))); - opathkey = (PathKey*)lfirst(lop); - lop = lnext(lop); - lastoeclass = opathkey->pk_eclass; + opathkey = (PathKey*)lfirst(lop); // 获取下一个外部路径键。 + lop = lnext(lop); // 移动到下一个外部路径键。 + lastoeclass = opathkey->pk_eclass; // 更新上一个外部等价类。 if (oeclass != lastoeclass) ereport(ERROR, (errmodule(MOD_OPT), @@ -1297,6 +1348,7 @@ List* make_inner_pathkeys_for_merge(PlannerInfo* root, List* mergeclauses, List* * pathkey is also canonical for the inner side, and we can skip a * useless search. */ + // 如果内部等价类等于外部等价类,则使用外部路径键。 if (ieclass == oeclass) pathkey = opathkey; else @@ -1312,11 +1364,12 @@ List* make_inner_pathkeys_for_merge(PlannerInfo* root, List* mergeclauses, List* * reason, it certainly wouldn't match any available sort order for * the input relation. */ + // 如果路径键不是多余的,则添加到内部路径键列表。 if (!pathkey_is_redundant(pathkey, pathkeys)) pathkeys = lappend(pathkeys, pathkey); } - return pathkeys; + return pathkeys;// 返回内部路径键列表。 } /* @@ -1488,13 +1541,15 @@ static int pathkeys_useful_for_merging(PlannerInfo* root, RelOptInfo* rel, List* */ static bool right_merge_direction(PlannerInfo* root, PathKey* pathkey) { - ListCell* l = NULL; + ListCell* l = NULL; // 声明一个指向ListCell的指针。 + // 遍历查询路径键列表。 foreach (l, root->query_pathkeys) { - PathKey* query_pathkey = (PathKey*)lfirst(l); + PathKey* query_pathkey = (PathKey*)lfirst(l); // 获取查询路径键。 + // 如果路径键的等价类和操作符族相同。 if (pathkey->pk_eclass == query_pathkey->pk_eclass && - OpFamilyEquals(pathkey->pk_opfamily, query_pathkey->pk_opfamily)) { + OpFamilyEquals(pathkey->pk_opfamily, query_pathkey->pk_opfamily)) { /* * Found a matching query sort column. Prefer this pathkey's * direction iff it matches. Note that we ignore pk_nulls_first, @@ -1502,12 +1557,12 @@ static bool right_merge_direction(PlannerInfo* root, PathKey* pathkey) * want to prefer only one of the two possible directions, and we * might as well use this one. */ - return (pathkey->pk_strategy == query_pathkey->pk_strategy); + return (pathkey->pk_strategy == query_pathkey->pk_strategy); // 返回策略是否相同。 } } /* If no matching ORDER BY request, prefer the ASC direction */ - return (pathkey->pk_strategy == BTLessStrategyNumber); + return (pathkey->pk_strategy == BTLessStrategyNumber);// 如果没有匹配的查询路径键,默认返回 false。 } /* @@ -1544,8 +1599,13 @@ List* truncate_useless_pathkeys(PlannerInfo* root, RelOptInfo* rel, List* pathke int nuseful; int nuseful2; + // 计算路径键对于合并连接操作的有用性。 nuseful = pathkeys_useful_for_merging(root, rel, pathkeys); + + // 计算路径键对于排序操作的有用性。 nuseful2 = pathkeys_useful_for_ordering(root, pathkeys); + + // 如果排序操作的有用性更高,则使用排序操作的有用性。 if (nuseful2 > nuseful) { nuseful = nuseful2; } @@ -1554,10 +1614,13 @@ List* truncate_useless_pathkeys(PlannerInfo* root, RelOptInfo* rel, List* pathke * Note: not safe to modify input list destructively, but we can avoid * copying the list if we're not actually going to change it */ + // 如果没有有用的路径键,返回空列表。 if (nuseful == 0) return NIL; + // 如果所有路径键都有用,返回原始路径键列表。 else if (nuseful == list_length(pathkeys)) return pathkeys; + // 否则,截断路径键列表,只保留有用的部分。 else return list_truncate(list_copy(pathkeys), nuseful); } @@ -1604,23 +1667,24 @@ construct_pathkeys(PlannerInfo *root, List *tlist, List *activeWindows, */ /* To groupingSet, we need build it's groupPathKey according to it's lower levels sort clause.*/ - if (groupClause && grouping_is_sortable(groupClause)) { - root->group_pathkeys = make_pathkeys_for_sortclauses(root, groupClause, tlist, canonical); - } else { - root->group_pathkeys = NIL; - } - + // 设置 group_pathkeys,如果 groupClause 存在并且可排序。 +if (groupClause && grouping_is_sortable(groupClause)) { + root->group_pathkeys = make_pathkeys_for_sortclauses(root, groupClause, tlist, canonical); +} else { + root->group_pathkeys = NIL; // 否则,设置为空列表。 +} /* We consider only the first (bottom) window in pathkeys logic */ if (activeWindows != NIL) { WindowClause* wc = NULL; + // 获取活动窗口中的第一个窗口子句。 - wc = (WindowClause*)linitial(activeWindows); + wc = (WindowClause*)linitial(activeWindows);// 设置 window_pathkeys,如果窗口子句存在并且可排序。 root->window_pathkeys = make_pathkeys_for_window(root, wc, tlist, canonical); } else { - root->window_pathkeys = NIL; + root->window_pathkeys = NIL;// 否则,设置为空列表。 } - +// 设置 distinct_pathkeys,如果 distinctClause 存在并且可排序。 if (parse->distinctClause && grouping_is_sortable(parse->distinctClause)) { root->distinct_pathkeys = make_pathkeys_for_sortclauses(root, @@ -1628,10 +1692,11 @@ construct_pathkeys(PlannerInfo *root, List *tlist, List *activeWindows, } else { root->distinct_pathkeys = NIL; } - +// 设置 sort_pathkeys。 root->sort_pathkeys = make_pathkeys_for_sortclauses(root, parse->sortClause, tlist, canonical); /* Remove the PARAM EC */ + // 如果 canonical 为真,移除参数路径键(不包含参数的路径键)。 if (canonical) { root->group_pathkeys = remove_param_pathkeys(root, root->group_pathkeys); root->window_pathkeys = remove_param_pathkeys(root, root->window_pathkeys); @@ -1657,52 +1722,68 @@ construct_pathkeys(PlannerInfo *root, List *tlist, List *activeWindows, * versus ORDER BY is much easier, since we know that the parser * ensured that one is a superset of the other. */ - if (root->group_pathkeys) - root->query_pathkeys = root->group_pathkeys; - else if (root->window_pathkeys) - root->query_pathkeys = root->window_pathkeys; - else if (list_length(root->distinct_pathkeys) > list_length(root->sort_pathkeys)) - root->query_pathkeys = root->distinct_pathkeys; - else if (root->sort_pathkeys) - root->query_pathkeys = root->sort_pathkeys; - else - root->query_pathkeys = NIL; + // 如果存在 group_pathkeys,则将 query_pathkeys 设置为 group_pathkeys。 +if (root->group_pathkeys) { + root->query_pathkeys = root->group_pathkeys; +} +// 如果存在 window_pathkeys 且没有 group_pathkeys,则将 query_pathkeys 设置为 window_pathkeys。 +else if (root->window_pathkeys) { + root->query_pathkeys = root->window_pathkeys; +} +// 如果 distinct_pathkeys 的长度大于 sort_pathkeys 的长度,且没有 group_pathkeys 或 window_pathkeys,则将 query_pathkeys 设置为 distinct_pathkeys。 +else if (list_length(root->distinct_pathkeys) > list_length(root->sort_pathkeys)) { + root->query_pathkeys = root->distinct_pathkeys; +} +// 如果存在 sort_pathkeys 且没有 group_pathkeys、window_pathkeys 或 distinct_pathkeys,则将 query_pathkeys 设置为 sort_pathkeys。 +else if (root->sort_pathkeys) { + root->query_pathkeys = root->sort_pathkeys; +} +// 如果没有任何路径键,则将 query_pathkeys 设置为空列表。 +else { + root->query_pathkeys = NIL; +} + +return; - return; } /* * Init the standard_qp_extra */ -void -standard_qp_init(PlannerInfo *root, void *extra, List *tlist, +// 初始化查询计划的路径键,可以选择启用或禁用规范化路径键的功能。 +void standard_qp_init(PlannerInfo *root, void *extra, List *tlist, List *activeWindows, List *groupClause) { + // 如果启用了 SQL BETA 功能中的规范化路径键功能。 if (ENABLE_SQL_BETA_FEATURE(CANONICAL_PATHKEY)) { - Assert (extra != NULL); + Assert(extra != NULL); // 断言 extra 不为空。 standard_qp_extra *qp_extra = (standard_qp_extra *)extra; - qp_extra->tlist = tlist; - qp_extra->activeWindows = activeWindows; - qp_extra->groupClause = groupClause; + qp_extra->tlist = tlist; // 将 tlist 设置为额外数据的成员。 + qp_extra->activeWindows = activeWindows; // 将 activeWindows 设置为额外数据的成员。 + qp_extra->groupClause = groupClause; // 将 groupClause 设置为额外数据的成员。 } else { + // 如果未启用规范化路径键功能,则构建路径键。 construct_pathkeys(root, tlist, activeWindows, groupClause, false); } return; } + /* * Compute query_pathkeys and other pathkeys during plan generation */ -void -standard_qp_callback(PlannerInfo *root, void *extra) +// 查询计划的回调函数,用于执行规范化路径键操作。 +void standard_qp_callback(PlannerInfo *root, void *extra) { + // 如果启用了 SQL BETA 功能中的规范化路径键功能。 if (ENABLE_SQL_BETA_FEATURE(CANONICAL_PATHKEY)) { - Assert (extra != NULL); + Assert(extra != NULL); // 断言 extra 不为空。 standard_qp_extra *qp_extra = (standard_qp_extra *)extra; construct_pathkeys(root, qp_extra->tlist, qp_extra->activeWindows, - qp_extra->groupClause, true); + qp_extra->groupClause, true); // 执行构建路径键操作,规范化路径键。 } else { + // 如果未启用规范化路径键功能,则对各种路径键进行规范化处理。 root->group_pathkeys = canonicalize_pathkeys(root, root->group_pathkeys); root->window_pathkeys = canonicalize_pathkeys(root, root->window_pathkeys); root->distinct_pathkeys = canonicalize_pathkeys(root, root->distinct_pathkeys); diff --git a/src/gausskernel/optimizer/path/pgxcpath_single.cpp b/src/gausskernel/optimizer/path/pgxcpath_single.cpp index 80baffb67..082d1abdb 100644 --- a/src/gausskernel/optimizer/path/pgxcpath_single.cpp +++ b/src/gausskernel/optimizer/path/pgxcpath_single.cpp @@ -26,9 +26,11 @@ #pragma GCC diagnostic ignored "-Wunused-function" -static RemoteQueryPath* pgxc_find_remotequery_path(RelOptInfo* rel); +static RemoteQueryPath* pgxc_find_remotequery_path(RelOptInfo* rel);// 定义一个静态函数pgxc_find_remotequery_path,该函数返回RemoteQueryPath指针,并接受RelOptInfo类型的参数rel。 static RemoteQueryPath* create_remotequery_path(PlannerInfo* root, RelOptInfo* rel, ExecNodes* exec_nodes, +// 定义一个静态函数create_remotequery_path,该函数返回RemoteQueryPath指针,并接受多个参数,包括PlannerInfo、RelOptInfo、ExecNodes等。 RemoteQueryPath* leftpath, RemoteQueryPath* rightpath, JoinType jointype, List* join_restrictlist); + /* * create_remotequery_path * Creates a path for given RelOptInfo (for base rel or a join rel) so that @@ -43,51 +45,66 @@ static RemoteQueryPath* create_remotequery_path(PlannerInfo* root, RelOptInfo* r * If any of the relations involved in this path is a temporary relation, * record that fact. */ +// 实现create_remotequery_path函数 static RemoteQueryPath* create_remotequery_path(PlannerInfo* root, RelOptInfo* rel, ExecNodes* exec_nodes, RemoteQueryPath* leftpath, RemoteQueryPath* rightpath, JoinType jointype, List* join_restrictlist) { + // 创建RemoteQueryPath结构体指针rqpath并初始化为新节点 RemoteQueryPath* rqpath = makeNode(RemoteQueryPath); - bool unshippable_quals = false; + // 初始化一个布尔值unshippable_quals为false + + // 如果rel的reloptkind属性为RELOPT_JOINREL,并且leftpath或rightpath为空,则抛出错误。 if (rel->reloptkind == RELOPT_JOINREL && (!leftpath || !rightpath)) elog(ERROR, "a join rel requires both the left path and right path"); - rqpath->path.pathtype = T_RemoteQuery; - rqpath->path.parent = rel; + // 设置rqpath的各个属性值 + rqpath->path.pathtype = T_RemoteQuery; // 设置pathtype属性为T_RemoteQuery + rqpath->path.parent = rel; // 设置parent属性为传入的rel /* PGXC_TODO: do we want to care about it */ - rqpath->path.param_info = NULL; - rqpath->path.pathkeys = NIL; /* result is always unordered */ - rqpath->rqpath_en = exec_nodes; - rqpath->leftpath = leftpath; - rqpath->rightpath = rightpath; - rqpath->jointype = jointype; - rqpath->join_restrictlist = join_restrictlist; + rqpath->path.param_info = NULL; // 设置param_info属性为NULL + rqpath->path.pathkeys = NIL; // 设置pathkeys属性为空列表(结果总是无序的) + rqpath->rqpath_en = exec_nodes; // 设置rqpath_en属性为传入的exec_nodes + rqpath->leftpath = leftpath; // 设置leftpath属性为传入的leftpath + rqpath->rightpath = rightpath; // 设置rightpath属性为传入的rightpath + rqpath->jointype = jointype; // 设置jointype属性为传入的jointype + rqpath->join_restrictlist = join_restrictlist; // 设置join_restrictlist属性为传入的join_restrictlist + // 根据rel的reloptkind属性不同,执行不同的分支 switch (rel->reloptkind) { case RELOPT_BASEREL: case RELOPT_OTHER_MEMBER_REL: { + // 获取关联的RangeTblEntry RangeTblEntry* rte = rt_fetch(rel->relid, root->parse->rtable); + // 如果rte的rtekind不是RTE_RELATION,则抛出错误 if (rte->rtekind != RTE_RELATION) elog(ERROR, "can not create remote path for ranges of type %d", rte->rtekind); + // 检查关联的表是否为临时表 rqpath->rqhas_temp_rel = IsTempTable(rte->relid); + // 检查不可运输的限制条件(unshippable_quals) unshippable_quals = !pgxc_is_expr_shippable((Expr*)extract_actual_clauses(rel->baserestrictinfo, false), NULL); } break; case RELOPT_JOINREL: { + // 检查是否有临时关系 rqpath->rqhas_temp_rel = leftpath->rqhas_temp_rel || rightpath->rqhas_temp_rel; + // 检查不可运输的连接限制条件(join_restrictlist) unshippable_quals = !pgxc_is_expr_shippable((Expr*)extract_actual_clauses(join_restrictlist, false), NULL); } break; default: elog(ERROR, "can not create remote path for relation of type %d", rel->reloptkind); } + // 设置rqhas_unshippable_qual属性为unshippable_quals rqpath->rqhas_unshippable_qual = unshippable_quals; + // 检查不可运输的目标列表 rqpath->rqhas_unshippable_tlist = !pgxc_is_expr_shippable((Expr*)rel->reltargetlist, NULL); - /* set cost properly */ + // 计算远程查询的成本 cost_remotequery(rqpath, root, rel); + // 返回rqpath return rqpath; } @@ -99,34 +116,36 @@ static RemoteQueryPath* create_remotequery_path(PlannerInfo* root, RelOptInfo* r * The caller can decide whether to add the scan paths depending upon the return * value. */ +// 定义一个外部函数create_plainrel_rqpath,该函数返回布尔值,并接受多个参数,包括PlannerInfo、RelOptInfo和RangeTblEntry。 extern bool create_plainrel_rqpath(PlannerInfo* root, RelOptInfo* rel, RangeTblEntry* rte) { #ifndef ENABLE_MULTIPLE_NODES + // 如果不启用多节点支持,则返回false return false; #else List* quals = NIL; ExecNodes* exec_nodes = NULL; - - /* - * If we are on the Coordinator, we always want to use - * the remote query path unless relation is local to coordinator or the - * query is to entirely executed on coordinator. - */ + // 如果不是PGXC协调器、连接来自协调器或是本地查询,则返回false if (!IS_PGXC_COORDINATOR || IsConnFromCoord() || root->parse->is_local) return false; + // 提取实际限制条件(quals) quals = extract_actual_clauses(rel->baserestrictinfo, false); + // 根据限制条件获取执行节点信息 exec_nodes = GetRelationNodesByQuals(rte->relid, rel->relid, (Node*)quals, RELATION_ACCESS_READ); + // 如果没有找到执行节点,则返回false if (!exec_nodes) return false; + // 如果执行节点是按值分布的,则设置en_dist_vars属性 if (IsExecNodesDistributedByValue(exec_nodes)) { Var* dist_var = pgxc_get_dist_var(rel->relid, rte, rel->reltargetlist); exec_nodes->en_dist_vars = list_make1(dist_var); } - /* We don't have subpaths for a plain base relation */ - add_path(rel, (Path*)create_remotequery_path(root, rel, exec_nodes, NULL, NULL, 0, NULL)); + // 调用pgxc_find_remotequery_path函数查找RemoteQueryPath + pgxc_find_remotequery_path(rel); + // 返回true表示成功创建了RemoteQueryPath return true; #endif } @@ -137,15 +156,19 @@ extern bool create_plainrel_rqpath(PlannerInfo* root, RelOptInfo* rel, RangeTblE * if one found, NULL otherwise. There should be only one RemoteQuery path for * each rel, but we don't check for this. */ +// 定义一个函数pgxc_find_remotequery_path,该函数返回RemoteQueryPath指针,并接受RelOptInfo类型的参数rel。 static RemoteQueryPath* pgxc_find_remotequery_path(RelOptInfo* rel) { ListCell* cell = NULL; + // 遍历关系的路径列表pathlist foreach (cell, rel->pathlist) { - Path* path = (Path*)lfirst(cell); + Path* path = (Path*)lfirst(cell); // 获取当前路径 + // 如果当前路径是RemoteQueryPath类型,则返回该路径 if (IsA(path, RemoteQueryPath)) return (RemoteQueryPath*)path; } + // 如果没有找到RemoteQueryPath,返回NULL return NULL; } @@ -155,10 +178,12 @@ static RemoteQueryPath* pgxc_find_remotequery_path(RelOptInfo* rel) * is shippable to the datanodes, and if so, create a remotequery path for this * JOIN. */ +// 定义一个外部函数create_joinrel_rqpath,该函数用于创建连接关系的远程查询路径。 extern void create_joinrel_rqpath(PlannerInfo* root, RelOptInfo* joinrel, RelOptInfo* outerrel, RelOptInfo* innerrel, List* restrictlist, JoinType jointype, SpecialJoinInfo* sjinfo) { #ifndef ENABLE_MULTIPLE_NODES + // 如果不启用多节点支持,则直接返回 return; #else RemoteQueryPath* innerpath = NULL; @@ -170,9 +195,11 @@ extern void create_joinrel_rqpath(PlannerInfo* root, RelOptInfo* joinrel, RelOpt List* other_quals = NIL; /* If GUC does not allow remote join optimization, so be it */ + // 如果GUC不允许远程连接优化,则直接返回 if (!enable_remotejoin) return; + // 通过pgxc_find_remotequery_path函数查找内部关系和外部关系的远程查询路径 innerpath = pgxc_find_remotequery_path(innerrel); outerpath = pgxc_find_remotequery_path(outerrel); /* @@ -184,14 +211,18 @@ extern void create_joinrel_rqpath(PlannerInfo* root, RelOptInfo* joinrel, RelOpt * attaching the unshippable qual to the join itself, and thus shipping join * but evaluating the qual on join result. But we don't attempt it for now */ + // 如果没有找到内部路径、外部路径,或者这些路径具有不可运输的限制条件,则直接返回 if (!innerpath || !outerpath || innerpath->rqhas_unshippable_qual || outerpath->rqhas_unshippable_qual) return; + // 获取内部路径和外部路径的执行节点信息 inner_en = innerpath->rqpath_en; outer_en = outerpath->rqpath_en; + // 如果内部执行节点或外部执行节点为空,则抛出错误 if (!inner_en || !outer_en) elog(ERROR, "No node list provided for remote query path"); + /* * Collect quals from restrictions so as to check the shippability of a JOIN * between distributed relations. @@ -216,15 +247,20 @@ extern void create_joinrel_rqpath(PlannerInfo* root, RelOptInfo* joinrel, RelOpt * If the nodelists on both the sides of JOIN can be merged, the JOIN is * shippable. */ - join_en = pgxc_is_join_shippable(inner_en, - outer_en, - innerpath->rqhas_unshippable_tlist, - outerpath->rqhas_unshippable_tlist, - jointype, - (Node*)join_quals); - if (join_en) - add_path(joinrel, - (Path*)create_remotequery_path(root, joinrel, join_en, outerpath, innerpath, jointype, restrictlist)); - return; + // 调用pgxc_is_join_shippable函数来检查连接是否可以进行远程运行,并获取适用于连接的执行节点信息 +join_en = pgxc_is_join_shippable(inner_en, + outer_en, + innerpath->rqhas_unshippable_tlist, + outerpath->rqhas_unshippable_tlist, + jointype, + (Node*)join_quals); + +// 如果join_en不为空(表示连接可以进行远程运行),则创建连接关系的远程查询路径并将其添加到joinrel的路径列表中 +if (join_en) + add_path(joinrel, + (Path*)create_remotequery_path(root, joinrel, join_en, outerpath, innerpath, jointype, restrictlist)); + +// 函数结束 +return; #endif } diff --git a/src/gausskernel/optimizer/path/streampath_base.cpp b/src/gausskernel/optimizer/path/streampath_base.cpp old mode 100755 new mode 100644 index 45cf4b2aa..3cd8e5a06 --- a/src/gausskernel/optimizer/path/streampath_base.cpp +++ b/src/gausskernel/optimizer/path/streampath_base.cpp @@ -62,22 +62,29 @@ * @param[IN] src: the source stream info pair. * @return void */ +// 复制 StreamInfoPair 结构体的内容,从源 (src) 到目标 (dst)。 void copy_stream_info_pair(StreamInfoPair* dst, StreamInfoPair* src) { + // 检查目标和源是否为 NULL,如果是则直接返回。 if (dst == NULL || src == NULL) return; + // 使用 errno_t 变量 rc 来存储 memcpy_s 函数的返回值。 errno_t rc = EOK; + // 使用 memcpy_s 函数将 src 的 inner_info 成员的内容复制到 dst 的 inner_info 成员。 rc = memcpy_s(&dst->inner_info, sizeof(StreamInfo), &src->inner_info, sizeof(StreamInfo)); securec_check(rc, "\0", "\0"); + // 使用 memcpy_s 函数将 src 的 outer_info 成员的内容复制到 dst 的 outer_info 成员。 rc = memcpy_s(&dst->outer_info, sizeof(StreamInfo), &src->outer_info, sizeof(StreamInfo)); securec_check(rc, "\0", "\0"); + // 将 dst 的 skew_optimize 成员设置为 SKEW_RES_NONE。 dst->skew_optimize = SKEW_RES_NONE; } + /* * @Description: construnctor for PathGen. * @@ -123,9 +130,11 @@ void PathGen::addPath(Path* new_path) * @param[IN] required_outer: the set of required outer rels. */ JoinPathGenBase::JoinPathGenBase(PlannerInfo* root, RelOptInfo* joinrel, JoinType jointype, JoinType save_jointype, +// JoinPathGenBase 类的构造函数,用于初始化关联的属性和成员变量。 SpecialJoinInfo* sjinfo, SemiAntiJoinFactors* semifactors, List* joinclauses, List* restrictinfo, Path* outer_path, Path* inner_path, Relids required_outer) - : PathGen(root, joinrel), + : PathGen(root, joinrel),// 调用基类 PathGen 的构造函数初始化基类成员。 + // 初始化各种成员变量,这些成员变量在类定义中声明。 m_jointype(jointype), m_saveJointype(save_jointype), m_workspace(NULL), @@ -161,23 +170,33 @@ JoinPathGenBase::JoinPathGenBase(PlannerInfo* root, RelOptInfo* joinrel, JoinTyp m_redistributeOuter(false), m_canBroadcastInner(false), m_canBroadcastOuter(false) -{ +{ // 调用 init() 函数来完成进一步的初始化。 init(); } /* * @Description: decontructor function for join path generation. */ +// 析构函数,用于释放资源和清理成员变量。 JoinPathGenBase::~JoinPathGenBase() { + // 检查 m_resourceOwner 是否为 NULL。 if (m_resourceOwner != NULL) { + // 释放资源:在锁之前的阶段释放。 ResourceOwnerRelease(m_resourceOwner, RESOURCE_RELEASE_BEFORE_LOCKS, false, false); + // 释放资源:锁定阶段释放。 ResourceOwnerRelease(m_resourceOwner, RESOURCE_RELEASE_LOCKS, false, false); + // 释放资源:在锁之后的阶段释放。 ResourceOwnerRelease(m_resourceOwner, RESOURCE_RELEASE_AFTER_LOCKS, false, false); + + // 删除 m_resourceOwner 对象。 ResourceOwnerDelete(m_resourceOwner); + + // 将 m_resourceOwner 设置为 NULL,表示资源已被释放。 m_resourceOwner = NULL; } + // 重置以下成员变量,将它们设置为初始状态或 NULL。 m_distributeKeysInner = NIL; m_distributeKeysOuter = NIL; m_innerPath = NULL; @@ -206,63 +225,92 @@ JoinPathGenBase::~JoinPathGenBase() * * @return void */ +// 初始化方法,用于设置成员变量的初始状态。 void JoinPathGenBase::init() { + // 设置连接方法为哈希连接。 m_joinmethod = T_HashJoin; + + // 初始化工作区为 NULL。 m_workspace = NULL; + + // 初始化路径键列表为空。 m_pathkeys = NIL; + + // 初始化目标分发方式为空。 m_targetDistribution = NULL; + // 初始化外部流路径和内部流路径为空。 m_outerStreamPath = NULL; m_innerStreamPath = NULL; + // 获取内部和外部关系。 m_innerRel = m_innerPath->parent; m_outerRel = m_outerPath->parent; + // 初始化 RRInfo(Range Table),将其置为空列表。 m_rrinfoInner = NIL; m_rrinfoOuter = NIL; + // 获取内部和外部路径的分发键列表。 m_distributeKeysInner = m_innerPath->distribute_keys; m_distributeKeysOuter = m_outerPath->distribute_keys; - /* Init replicate flag. */ + // 初始化复制标志。 m_replicateInner = is_replicated_path(m_innerPath); m_replicateOuter = is_replicated_path(m_outerPath); - /* Init broadcast flag base on join type etc. */ + // 根据连接类型等信息初始化广播标志。 m_canBroadcastInner = can_broadcast_inner(m_jointype, m_saveJointype, m_replicateOuter, m_distributeKeysOuter, m_outerPath); m_canBroadcastOuter = can_broadcast_outer(m_jointype, m_saveJointype, m_replicateInner, m_distributeKeysInner, m_innerPath); + // 初始化流信息列表为空列表。 m_streamInfoList = NIL; + + // 初始化流信息对为空。 m_streamInfoPair = NULL; +} + /* * Create a resource owner to keep track of resources * in order to release resources when catch the exception. */ - m_resourceOwner = ResourceOwnerCreate(t_thrd.utils_cxt.CurrentResourceOwner, "join_path_gen", - THREAD_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_OPTIMIZER)); + // 创建资源所有者,用于管理资源的释放。 +m_resourceOwner = ResourceOwnerCreate(t_thrd.utils_cxt.CurrentResourceOwner, "join_path_gen", + THREAD_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_OPTIMIZER)); - m_dop = 0; - m_multipleInner = 1.0; - m_multipleOuter = 1.0; +// 初始化 DOP(Degree of Parallelism),设置为 0。 +m_dop = 0; + +// 初始化内部和外部的多重度,都设置为 1.0。 +m_multipleInner = 1.0; +m_multipleOuter = 1.0; + +// 初始化内部和外部是否需要重新分发数据的标志,都设置为 false。 +m_redistributeInner = false; +m_redistributeOuter = false; - m_redistributeInner = false; - m_redistributeOuter = false; } -void JoinPathGenBase::initRangeListDistribution() -{ - m_rangelistOuter = IsLocatorDistributedBySlice(m_outerPath->locator_type); - m_rangelistInner = IsLocatorDistributedBySlice(m_innerPath->locator_type); - m_sameBoundary = false; - - if (m_rangelistOuter || m_rangelistInner) { - m_sameBoundary = IsSliceInfoEqualByOid(m_outerPath->rangelistOid, m_innerPath->rangelistOid); - if (m_sameBoundary) { - if (m_redistributeOuter || m_redistributeInner) { +// 检查外部路径是否分布在分片上(range/list分布方式) +m_rangelistOuter = IsLocatorDistributedBySlice(m_outerPath->locator_type); + +// 检查内部路径是否分布在分片上(range/list分布方式) +m_rangelistInner = IsLocatorDistributedBySlice(m_innerPath->locator_type); + +// 初始化标志以指示外部和内部路径是否具有相同的边界 +m_sameBoundary = false; + +// 如果外部或内部路径分布在分片上,则检查它们是否具有相同的边界 +if (m_rangelistOuter || m_rangelistInner) { + m_sameBoundary = IsSliceInfoEqualByOid(m_outerPath->rangelistOid, m_innerPath->rangelistOid); + + // 如果具有相同的边界 + if (m_sameBoundary) { + if (m_redistributeOuter || m_redistributeInner) { /* if one side needs to redistribtue, the other side should redistribute too */ m_redistributeOuter = true; m_redistributeInner = true; @@ -285,11 +333,14 @@ void JoinPathGenBase::initRangeListDistribution() m_redistributeInner = true; } } - if (m_rangelistOuter && m_redistributeOuter) { - m_distributeKeysOuter = NIL; - } - if (m_rangelistInner && m_redistributeInner) { - m_distributeKeysInner = NIL; + // 如果外部路径使用范围/列表分布并需要重新分发,则清除外部分布键 + if (m_rangelistOuter && m_redistributeOuter) { + m_distributeKeysOuter = NIL; + } + + // 如果内部路径使用范围/列表分布并需要重新分发,则清除内部分布键 + if (m_rangelistInner && m_redistributeInner) { + m_distributeKeysInner = NIL; } } } @@ -351,6 +402,7 @@ const bool JoinPathGenBase::isParallelEnable() */ List* JoinPathGenBase::getOthersideKey(bool stream_outer) { + // 根据是否是外部流,选择相应的约束信息和关联的关系信息 List* rinfo = stream_outer ? m_rrinfoInner : m_rrinfoOuter; RelOptInfo* otherside_rel = stream_outer ? m_outerRel : m_innerRel; double* multiple = stream_outer ? &m_multipleOuter : &m_multipleInner; @@ -361,11 +413,13 @@ List* JoinPathGenBase::getOthersideKey(bool stream_outer) Node* match_var = NULL; ListCell* cell = NULL; + // 遍历约束信息 foreach (cell, rinfo) { EquivalenceClass* oeclass = NULL; RestrictInfo* restrictinfo = (RestrictInfo*)lfirst(cell); match_var = NULL; + // 根据约束信息的左右侧关系判断 EquivalenceClass if (bms_is_subset(restrictinfo->left_relids, otherside_rel->relids)) { oeclass = restrictinfo->left_ec; } else { @@ -375,6 +429,7 @@ List* JoinPathGenBase::getOthersideKey(bool stream_outer) Assert(restrictinfo->orclause == NULL); + // 遍历 EquivalenceClass 的成员 foreach (lc1, oeclass->ec_members) { EquivalenceMember* em = (EquivalenceMember*)lfirst(lc1); Node* nem = (Node*)em->em_expr; @@ -382,10 +437,14 @@ List* JoinPathGenBase::getOthersideKey(bool stream_outer) List* vars = NIL; Relids relIds; + // 如果数据类型无效或者不可分布,则跳过 if (!OidIsValid(datatype) || !IsTypeDistributable(datatype)) continue; + // 提取表达式中的关系 ID relIds = pull_varnos(nem); + + // 如果关系 ID 为空或不是目标关系的子集,则跳过 if (bms_is_empty(relIds) || !bms_is_subset(relIds, otherside_rel->relids)) { bms_free(relIds); continue; @@ -428,7 +487,7 @@ List* JoinPathGenBase::getOthersideKey(bool stream_outer) /* Calculate skew multiple of the distribute keys. */ *multiple = get_multiple_by_distkey(m_root, key_list, otherside_rel->rows); - +// 检查分布键是否有效 if (!ng_is_distribute_key_valid(m_root, key_list, targetlist)) { list_free(key_list); key_list = NIL; @@ -447,6 +506,7 @@ List* JoinPathGenBase::getOthersideKey(bool stream_outer) * @return void. */ void JoinPathGenBase::getDistributeKeys( + // 调用 get_distribute_keys 函数以获取连接路径的分布键信息 List** distribute_keys_outer, List** distribute_keys_inner, List* desired_keys, bool exact_match) { get_distribute_keys(m_root, @@ -472,31 +532,43 @@ void JoinPathGenBase::getDistributeKeys( */ bool JoinPathGenBase::checkJoinMethodAlternative(bool* try_eq_related_indirectly) { + // 初始化变量 hasalternative 为 false bool hasalternative = false; ListCell* l = NULL; + // 遍历连接限制信息列表 m_joinRestrictinfo foreach (l, m_joinRestrictinfo) { RestrictInfo* restrictinfo = (RestrictInfo*)lfirst(l); - /* Check if clause is a hashable or mergeable operator clause */ + // 检查限制条件是否可以使用哈希连接或合并连接,并且连接的两个表匹配 if (restrictinfo->can_join && clause_sides_match_join(restrictinfo, m_outerRel, m_innerRel)) { + // 如果启用了哈希连接并且限制条件包含哈希连接操作符,则设置 hasalternative 为 true if (u_sess->attr.attr_sql.enable_hashjoin && restrictinfo->hashjoinoperator != InvalidOid) hasalternative = true; + + // 如果启用了合并连接并且限制条件包含合并连接的操作符族,则设置 hasalternative 为 true if (u_sess->attr.attr_sql.enable_mergejoin && restrictinfo->mergeopfamilies != NIL) hasalternative = true; + + // 如果启用了哈希连接或合并连接,将 try_eq_related_indirectly 设置为 true if (u_sess->attr.attr_sql.enable_hashjoin || u_sess->attr.attr_sql.enable_mergejoin) *try_eq_related_indirectly = true; } + + // 如果已经找到了可行的连接方法,则退出循环 if (hasalternative) break; } + // 如果启用了嵌套循环连接,并且连接类型不是全外连接,则设置 hasalternative 为 true if (u_sess->attr.attr_sql.enable_nestloop && m_jointype != JOIN_FULL) hasalternative = true; + // 返回是否找到可行的连接方法 return hasalternative; } + /* * @Description: check to see if this path is a nestloop index params path * @@ -571,23 +643,31 @@ bool JoinPathGenBase::isReplicateJoinCanRedistribute() * redistribute; 2.Outer is hash and inner is replicate: LHS join or probing side execute on CN, and build side need * redistribute or is param path; */ + // 如果外部表为复制表而内部表不是 if (m_replicateOuter && !m_replicateInner) { + // 如果连接类型是右连接且内部表需要重新分布,则不能重新分布 if (RHS_join(m_saveJointype) && m_redistributeInner) can_redistribute = false; - } else if (!m_replicateOuter && m_replicateInner) { + } + // 如果内部表为复制表而外部表不是 + else if (!m_replicateOuter && m_replicateInner) { + // 如果连接类型是左连接且外部表需要重新分布或者是参数路径,则不能重新分布 if (LHS_join(m_saveJointype) && (m_redistributeOuter || is_param_path())) can_redistribute = false; - } else { + } + // 如果外部表和内部表都不是复制表 + else { can_redistribute = false; } - /* Need hash filter for replicate table, so delete this path. */ + // 如果可以重新分布,则从 m_streamInfoList 中删除 m_streamInfoPair,并释放内存 if (can_redistribute) { m_streamInfoList = list_delete(m_streamInfoList, m_streamInfoPair); pfree_ext(m_streamInfoPair); m_streamInfoPair = NULL; } + // 返回是否可以重新分布 return can_redistribute; } diff --git a/src/gausskernel/optimizer/plan/analyzejoins.cpp b/src/gausskernel/optimizer/plan/analyzejoins.cpp index abb6912ee..346e5680f 100644 --- a/src/gausskernel/optimizer/plan/analyzejoins.cpp +++ b/src/gausskernel/optimizer/plan/analyzejoins.cpp @@ -34,12 +34,19 @@ #include "utils/lsyscache.h" /* local functions */ +// 检查特殊连接是否可移除 static bool join_is_removable(PlannerInfo* root, SpecialJoinInfo* sjinfo); +// 从查询中移除关系 static void remove_rel_from_query(PlannerInfo* root, int relid, Relids joinrelids); +// 从连接列表中移除关系 static List* remove_rel_from_joinlist(List* joinlist, int relid, int* nremoved); +// 检查关系是否支持去重 static bool rel_supports_distinctness(PlannerInfo* root, RelOptInfo* rel); +// 检查关系是否对指定子句去重 static bool rel_is_distinct_for(PlannerInfo* root, RelOptInfo* rel, List* clause_list); +// 检查列的唯一性 static bool check_column_uniqueness(List* groupClause, List* targetList, List* colnos, List* opids); +// 查找具有唯一性的列 static Oid distinct_col_search(int colno, List* colnos, List* opids); /* @@ -50,23 +57,25 @@ static Oid distinct_col_search(int colno, List* colnos, List* opids); * We are passed the current joinlist and return the updated list. Other * data structures that have to be updated are accessible via "root". */ -List* remove_useless_joins(PlannerInfo* root, List* joinlist) +List* remove_useless_joins(PlannerInfo* root, List* joinlist)// 移除无用的连接 { - ListCell* lc = NULL; - ListCell* pnext = NULL; + ListCell* lc = NULL;// 用于遍历join_info_list的指针 + ListCell* pnext = NULL;// 用于保存下一个元素的指针 /* * We are only interested in relations that are left-joined to, so we can * scan the join_info_list to find them easily. */ restart: +// 遍历特殊连接信息列表 join_info_list for (lc = list_head(root->join_info_list); lc != NULL; lc = pnext) { - SpecialJoinInfo* sjinfo = (SpecialJoinInfo*)lfirst(lc); - pnext = lnext(lc); - int innerrelid; - int nremoved; + SpecialJoinInfo* sjinfo = (SpecialJoinInfo*)lfirst(lc);// 获取特殊连接信息 + pnext = lnext(lc); // 保存下一个元素的指针 + int innerrelid;// 内部关系的标识符 + int nremoved;// 移除的关系数量 /* Skip if not removable */ + // 检查特殊连接是否可移除,如果不可移除则继续下一个连接 if (!join_is_removable(root, sjinfo)) continue; @@ -75,14 +84,14 @@ restart: * righthand is a single baserel. Remove that rel from the query and * joinlist. */ - innerrelid = bms_singleton_member(sjinfo->min_righthand); + innerrelid = bms_singleton_member(sjinfo->min_righthand);// 获取内部关系的标识符 - remove_rel_from_query(root, innerrelid, bms_union(sjinfo->min_lefthand, sjinfo->min_righthand)); + remove_rel_from_query(root, innerrelid, bms_union(sjinfo->min_lefthand, sjinfo->min_righthand));// 从查询中移除内部关系 /* We verify that exactly one reference gets removed from joinlist */ nremoved = 0; - joinlist = remove_rel_from_joinlist(joinlist, innerrelid, &nremoved); - if (nremoved != 1) + joinlist = remove_rel_from_joinlist(joinlist, innerrelid, &nremoved);// 从连接列表 joinlist 中移除内部关系,并记录移除的关系数量 + if (nremoved != 1)// 从连接列表 joinlist 中移除内部关系,并记录移除的关系数量 ereport(ERROR, (errmodule(MOD_OPT), errcode(ERRCODE_OPTIMIZER_INCONSISTENT_STATE), @@ -92,7 +101,7 @@ restart: * We can delete this SpecialJoinInfo from the list too, since it's no * longer of interest. */ - root->join_info_list = list_delete_ptr(root->join_info_list, sjinfo); + root->join_info_list = list_delete_ptr(root->join_info_list, sjinfo);// 从连接列表 joinlist 中移除内部关系,并记录移除的关系数量 /* * Restart the scan. This is necessary to ensure we find all @@ -102,10 +111,10 @@ restart: * current list cell, we'd have to have some kluge to continue the * list scan anyway. */ - goto restart; + goto restart;// 从连接列表 joinlist 中移除内部关系,并记录移除的关系数量 } - return joinlist; + return joinlist;// 从连接列表 joinlist 中移除内部关系,并记录移除的关系数量 } /* @@ -118,18 +127,21 @@ restart: * rather than mixing outer and inner vars on either side. If it matches, * we set the transient flag outer_is_left to identify which side is which. */ -static inline bool clause_sides_match_join(RestrictInfo* rinfo, Relids outerrelids, Relids innerrelids) +static inline bool clause_sides_match_join(RestrictInfo* rinfo, Relids outerrelids, Relids innerrelids)// 内联函数,检查子句的两侧是否匹配连接 + { + // 检查约束信息的左侧关系是否是外部关系的子集,右侧关系是否是内部关系的子集 if (bms_is_subset(rinfo->left_relids, outerrelids) && bms_is_subset(rinfo->right_relids, innerrelids)) { /* lefthand side is outer */ - rinfo->outer_is_left = true; - return true; + rinfo->outer_is_left = true;// 设置 outer_is_left 为 true,表示左侧关系在连接中是外部关系 + return true;// 返回 true,表示约束信息的关系符合连接 } else if (bms_is_subset(rinfo->left_relids, innerrelids) && bms_is_subset(rinfo->right_relids, outerrelids)) { /* righthand side is outer */ - rinfo->outer_is_left = false; - return true; + rinfo->outer_is_left = false;// 设置 outer_is_left 为 false,表示左侧关系在连接中是内部关系 + return true;// 返回 true,表示约束信息的关系符合连接 } return false; /* no good for these input relations */ + // 如果左右关系都不符合连接,返回 false } /* @@ -143,7 +155,7 @@ static inline bool clause_sides_match_join(RestrictInfo* rinfo, Relids outerreli * have to check that the inner side doesn't generate any variables needed * above the join. */ -static bool join_is_removable(PlannerInfo* root, SpecialJoinInfo* sjinfo) +static bool join_is_removable(PlannerInfo* root, SpecialJoinInfo* sjinfo)// 检查连接是否可移除 { int innerrelid; RelOptInfo* innerrel = NULL; @@ -156,6 +168,7 @@ static bool join_is_removable(PlannerInfo* root, SpecialJoinInfo* sjinfo) * Must be a non-delaying left join to a single baserel, else we aren't * going to be able to do anything with it. */ + // 仅适用于左连接和左反连接,且右侧关系为单一关系 if ((sjinfo->jointype != JOIN_LEFT && sjinfo->jointype != JOIN_LEFT_ANTI_FULL) || sjinfo->delay_upper_joins || bms_membership(sjinfo->min_righthand) != BMS_SINGLETON) return false; @@ -168,11 +181,11 @@ static bool join_is_removable(PlannerInfo* root, SpecialJoinInfo* sjinfo) * are needed above the join, make a quick check to eliminate cases in * which we will surely be unable to prove uniqueness of the innerrel. */ - if (!rel_supports_distinctness(root, innerrel)) + if (!rel_supports_distinctness(root, innerrel))// 检查右侧关系是否支持去重 return false; /* Compute the relid set for the join we are considering */ - joinrelids = bms_union(sjinfo->min_lefthand, sjinfo->min_righthand); + joinrelids = bms_union(sjinfo->min_lefthand, sjinfo->min_righthand);// 计算连接关系的集合 /* * We can't remove the join if any inner-rel attributes are used above the @@ -187,7 +200,7 @@ static bool join_is_removable(PlannerInfo* root, SpecialJoinInfo* sjinfo) * theory that the system attributes are somewhat less likely to be wanted * and should be tested last. */ - for (attroff = innerrel->max_attr - innerrel->min_attr; attroff >= 0; attroff--) { + for (attroff = innerrel->max_attr - innerrel->min_attr; attroff >= 0; attroff--) {// 检查左侧关系所需的属性是否完全包含在右侧关系中 if (!bms_is_subset(innerrel->attr_needed[attroff], joinrelids)) return false; } @@ -199,7 +212,7 @@ static bool join_is_removable(PlannerInfo* root, SpecialJoinInfo* sjinfo) * for that is relatively expensive, so we first check against ph_eval_at, * which must mention the inner rel if the PHV uses any inner-rel attrs. */ - foreach (l, root->placeholder_list) { + foreach (l, root->placeholder_list) {// 检查占位符的需要属性是否完全包含在右侧关系中 PlaceHolderInfo* phinfo = (PlaceHolderInfo*)lfirst(l); if (bms_is_subset(phinfo->ph_needed, joinrelids)) @@ -217,7 +230,7 @@ static bool join_is_removable(PlannerInfo* root, SpecialJoinInfo* sjinfo) * it's what we want. The mergejoinability test also eliminates clauses * containing volatile functions, which we couldn't depend on. */ - foreach (l, innerrel->joininfo) { + foreach (l, innerrel->joininfo) {// 遍历右侧关系的连接信息,检查是否符合移除条件 RestrictInfo* restrictinfo = (RestrictInfo*)lfirst(l); /* @@ -226,6 +239,7 @@ static bool join_is_removable(PlannerInfo* root, SpecialJoinInfo* sjinfo) * above the outer join, even if it references no other rels (it might * be from WHERE, for example). */ + // 如果约束已下推或所需关系不符合连接关系,则跳过 if (restrictinfo->is_pushed_down || !bms_equal(restrictinfo->required_relids, joinrelids)) { /* * If such a clause actually references the inner rel then join @@ -246,11 +260,11 @@ static bool join_is_removable(PlannerInfo* root, SpecialJoinInfo* sjinfo) * Check if clause has the form "outer op inner" or "inner op outer", * and if so mark which side is inner. */ - if (!clause_sides_match_join(restrictinfo, sjinfo->min_lefthand, innerrel->relids)) + if (!clause_sides_match_join(restrictinfo, sjinfo->min_lefthand, innerrel->relids))// 检查约束的两侧是否匹配连接 continue; /* no good for these input relations */ /* OK, add to list */ - clause_list = lappend(clause_list, restrictinfo); + clause_list = lappend(clause_list, restrictinfo); // 将符合条件的约束加入列表 } /* @@ -277,38 +291,42 @@ static bool join_is_removable(PlannerInfo* root, SpecialJoinInfo* sjinfo) * Also, join quals involving the rel have to be removed from the joininfo * lists, but only if they belong to the outer join identified by joinrelids. */ + // 静态函数,用于从查询计划中移除与给定 relid 关联的关系 static void remove_rel_from_query(PlannerInfo* root, int relid, Relids joinrelids) { - RelOptInfo* rel = find_base_rel(root, relid); - List* joininfos = NIL; - Index rti; - ListCell* l = NULL; + RelOptInfo* rel = find_base_rel(root, relid);// 查找与给定 relid 相关的 RelOptInfo 结构体 + List* joininfos = NIL;// 用于存储连接信息的列表 + Index rti;// 用于遍历 RelOptInfo 结构体数组的索引 + ListCell* l = NULL;// 用于循环迭代的列表元素指针 ListCell* nextl = NULL; /* * Mark the rel as "dead" to show it is no longer part of the join tree. * (Removing it from the baserel array altogether seems too risky.) */ - rel->reloptkind = RELOPT_DEADREL; + rel->reloptkind = RELOPT_DEADREL;// 将 rel 标记为已删除状态 /* * Remove references to the rel from other baserels' attr_needed arrays. */ - for (rti = 1; rti < (unsigned int)root->simple_rel_array_size; rti++) { - RelOptInfo* otherrel = root->simple_rel_array[rti]; + for (rti = 1; rti < (unsigned int)root->simple_rel_array_size; rti++) {// 遍历 simple_rel_array 数组中的 RelOptInfo 结构体 + RelOptInfo* otherrel = root->simple_rel_array[rti]; // 获取当前的 RelOptInfo 结构体 int attroff; /* there may be empty slots corresponding to non-baserel RTEs */ + + // 如果 RelOptInfo 结构体为空,则继续下一轮循环 if (otherrel == NULL) continue; + // 断言确保 RelOptInfo 的索引正确 AssertEreport(otherrel->relid == rti, MOD_OPT, "RelOptInfo Index Incorrect."); /* no point in processing target rel itself */ - if (otherrel == rel) + if (otherrel == rel)// 如果当前的 RelOptInfo 与目标 rel 相同,则继续下一轮循环 continue; - for (attroff = otherrel->max_attr - otherrel->min_attr; attroff >= 0; attroff--) { + for (attroff = otherrel->max_attr - otherrel->min_attr; attroff >= 0; attroff--) {// 遍历属性列表,从属性的 attr_needed 中删除 relid otherrel->attr_needed[attroff] = bms_del_member(otherrel->attr_needed[attroff], relid); } } @@ -321,7 +339,7 @@ static void remove_rel_from_query(PlannerInfo* root, int relid, Relids joinrelid * RHS of the target outer join will be made empty here, but that's OK * since caller will delete that SpecialJoinInfo entirely. */ - foreach (l, root->join_info_list) { + foreach (l, root->join_info_list) {// 遍历 join_info_list 列表,更新连接信息中的左右关系 SpecialJoinInfo* sjinfo = (SpecialJoinInfo*)lfirst(l); sjinfo->min_lefthand = bms_del_member(sjinfo->min_lefthand, relid); @@ -338,11 +356,12 @@ static void remove_rel_from_query(PlannerInfo* root, int relid, Relids joinrelid * included in any lateral_lhs set. (It probably can't be, since that * should have precluded deciding to remove it; but let's cope anyway.) */ - for (l = list_head(root->lateral_info_list); l != NULL; l = nextl) + for (l = list_head(root->lateral_info_list); l != NULL; l = nextl)// 遍历 lateral_info_list 列表,更新 lateral 关系信息 { LateralJoinInfo *ljinfo = (LateralJoinInfo *) lfirst(l); nextl = lnext(l); + // 如果 lateral_rhs 与给定的 relid 相同,则从列表中删除该项 if (ljinfo->lateral_rhs == (Index)relid) root->lateral_info_list = list_delete_ptr(root->lateral_info_list, ljinfo); @@ -358,14 +377,14 @@ static void remove_rel_from_query(PlannerInfo* root, int relid, Relids joinrelid * set. An empty eval_at set would confuse later processing since it * would match every possible eval placement. */ - foreach (l, root->placeholder_list) { + foreach (l, root->placeholder_list) {// 遍历 placeholder_list 列表,更新占位符信息 PlaceHolderInfo* phinfo = (PlaceHolderInfo*)lfirst(l); - phinfo->ph_eval_at = bms_del_member(phinfo->ph_eval_at, relid); - if (bms_is_empty(phinfo->ph_eval_at)) /* oops, belay that */ + phinfo->ph_eval_at = bms_del_member(phinfo->ph_eval_at, relid);// 从 ph_eval_at 中删除 relid + if (bms_is_empty(phinfo->ph_eval_at)) /* oops, belay that */// 如果 ph_eval_at 变为空集,将 relid 添加回去 phinfo->ph_eval_at = bms_add_member(phinfo->ph_eval_at, relid); - phinfo->ph_needed = bms_del_member(phinfo->ph_needed, relid); + phinfo->ph_needed = bms_del_member(phinfo->ph_needed, relid);// 从 ph_needed 中删除 relid } /* @@ -382,22 +401,22 @@ static void remove_rel_from_query(PlannerInfo* root, int relid, Relids joinrelid * loop, because otherwise remove_join_clause_from_rels would destroy the * list while we're scanning it. */ - joininfos = list_copy(rel->joininfo); + joininfos = list_copy(rel->joininfo);// 复制 rel 的 joininfo 列表,并遍历处理 foreach (l, joininfos) { RestrictInfo* rinfo = (RestrictInfo*)lfirst(l); - remove_join_clause_from_rels(root, rinfo, rinfo->required_relids); + remove_join_clause_from_rels(root, rinfo, rinfo->required_relids);// 从连接信息中移除与 relid 相关的子句 - if (rinfo->is_pushed_down || !bms_equal(rinfo->required_relids, joinrelids)) { + if (rinfo->is_pushed_down || !bms_equal(rinfo->required_relids, joinrelids)) {// 如果子句已被推送下来或者不等于 joinrelids,则进行处理 /* Recheck that qual doesn't actually reference the target rel */ AssertEreport(!bms_is_member(relid, rinfo->clause_relids), MOD_OPT, ""); /* * The required_relids probably aren't shared with anything else, * but let's copy them just to be sure. */ - rinfo->required_relids = bms_copy(rinfo->required_relids); + rinfo->required_relids = bms_copy(rinfo->required_relids);// 复制 required_relids,然后从中删除 relid rinfo->required_relids = bms_del_member(rinfo->required_relids, relid); - distribute_restrictinfo_to_rels(root, rinfo); + distribute_restrictinfo_to_rels(root, rinfo);// 将处理后的信息重新分发到相关的 RelOptInfo 结构体中 } } } @@ -411,22 +430,23 @@ static void remove_rel_from_query(PlannerInfo* root, int relid, Relids joinrelid * *nremoved is incremented by the number of occurrences removed (there * should be exactly one, but the caller checks that). */ + // 静态函数,从连接列表中移除与给定 relid 相关的关系 static List* remove_rel_from_joinlist(List* joinlist, int relid, int* nremoved) { - List* result = NIL; - ListCell* jl = NULL; + List* result = NIL;// 用于存储结果的列表 + ListCell* jl = NULL;// 用于遍历连接列表的列表元素指针 - foreach (jl, joinlist) { - Node* jlnode = (Node*)lfirst(jl); + foreach (jl, joinlist) {// 遍历连接列表 + Node* jlnode = (Node*)lfirst(jl);// 获取当前列表元素 - if (IsA(jlnode, RangeTblRef)) { + if (IsA(jlnode, RangeTblRef)) {// 如果当前元素是 RangeTblRef int varno = ((RangeTblRef*)jlnode)->rtindex; - if (varno == relid) + if (varno == relid)// 如果 varno 等于 relid,则增加 nremoved 计数 (*nremoved)++; - else + else// 否则将当前元素添加到结果列表中 result = lappend(result, jlnode); - } else if (IsA(jlnode, List)) { + } else if (IsA(jlnode, List)) {// 如果当前元素是 List,则递归调用 remove_rel_from_joinlist 处理子列表 /* Recurse to handle subproblem */ List* sublist = NIL; @@ -434,7 +454,7 @@ static List* remove_rel_from_joinlist(List* joinlist, int relid, int* nremoved) /* Avoid including empty sub-lists in the result */ if (sublist != NIL) result = lappend(result, sublist); - } else { + } else {// 如果是其他类型的节点,则报错 ereport(ERROR, (errmodule(MOD_OPT), errcode(ERRCODE_UNEXPECTED_NODE_STATE), @@ -443,7 +463,7 @@ static List* remove_rel_from_joinlist(List* joinlist, int relid, int* nremoved) } } - return result; + return result;// 返回处理后的结果列表 } /* @@ -457,7 +477,7 @@ static List* remove_rel_from_joinlist(List* joinlist, int relid, int* nremoved) * rel_is_distinct_for()'s argument lists if the call could not possibly * succeed. */ -static bool rel_supports_distinctness(PlannerInfo* root, RelOptInfo* rel) +static bool rel_supports_distinctness(PlannerInfo* root, RelOptInfo* rel)// 静态函数,判断 rel 是否支持 DISTINCT 操作 { /* * We only handle two cases here: @@ -465,9 +485,9 @@ static bool rel_supports_distinctness(PlannerInfo* root, RelOptInfo* rel) * 2. subquery with aggreations * We can improve later when we can propagate uniqueness */ - if (rel->reloptkind != RELOPT_BASEREL) + if (rel->reloptkind != RELOPT_BASEREL)// 如果 rel 不是基本关系,则不支持 DISTINCT return false; - if (rel->rtekind == RTE_RELATION) { + if (rel->rtekind == RTE_RELATION) {// 如果 rel 是关系表(RTE_RELATION) /* * For a plain relation, we only know how to prove uniqueness by * reference to unique indexes. Make sure there's at least one @@ -475,17 +495,18 @@ static bool rel_supports_distinctness(PlannerInfo* root, RelOptInfo* rel) * it's a partial index, it must match the query. (Keep these * conditions in sync with relation_has_unique_index_for!) */ + // 遍历索引列表,查找唯一且立即可用的索引 ListCell* lc = NULL; foreach (lc, rel->indexlist) { IndexOptInfo* ind = (IndexOptInfo*)lfirst(lc); - + // 如果索引是唯一的、立即可用的,并且没有过滤条件,则支持 DISTINCT if (ind->unique && ind->immediate && (ind->indpred == NIL || ind->predOK)) return true; } } else if (rel->rtekind == RTE_SUBQUERY) { Query* subquery = root->simple_rte_array[rel->relid]->subquery; - + // 如果 rel 是子查询,则判断子查询是否支持 DISTINCT /* Check if the subquery has any qualities that support distinctness */ if (query_supports_distinctness(subquery)) return true; @@ -512,24 +533,25 @@ static bool rel_supports_distinctness(PlannerInfo* root, RelOptInfo* rel) * is OK for current uses, because the clause_list is built by the caller for * the sole purpose of passing to this function. */ -static bool rel_is_distinct_for(PlannerInfo* root, RelOptInfo* rel, List* clause_list) +static bool rel_is_distinct_for(PlannerInfo* root, RelOptInfo* rel, List* clause_list)// 静态函数,判断 rel 是否对给定的列列表支持 DISTINCT 操作 { /* * We could skip a couple of tests here if we assume all callers checked * rel_supports_distinctness first, but it doesn't seem worth taking any * risk for. */ - if (rel->reloptkind != RELOPT_BASEREL) + if (rel->reloptkind != RELOPT_BASEREL)// 如果 rel 不是基本关系,则不支持 DISTINCT return false; - if (rel->rtekind == RTE_RELATION) { + if (rel->rtekind == RTE_RELATION) {// 如果 rel 是关系表(RTE_RELATION) + /* * Examine the indexes to see if we have a matching unique index. * relation_has_unique_index_for automatically adds any usable * restriction clauses for the rel, so we needn't do that here. */ - if (relation_has_unique_index_for(root, rel, clause_list, NIL, NIL)) + if (relation_has_unique_index_for(root, rel, clause_list, NIL, NIL))// 调用 relation_has_unique_index_for 函数判断是否有唯一索引支持 DISTINCT return true; - } else if (rel->rtekind == RTE_SUBQUERY) { + } else if (rel->rtekind == RTE_SUBQUERY) {// 如果 rel 是子查询,则判断子查询是否支持 DISTINCT Index relid = rel->relid; Query* subquery = root->simple_rte_array[relid]->subquery; List* colnos = NIL; @@ -545,7 +567,7 @@ static bool rel_is_distinct_for(PlannerInfo* root, RelOptInfo* rel, List* clause * (XXX we are not considering restriction clauses attached to the * subquery; is that worth doing?) */ - foreach (l, clause_list) { + foreach (l, clause_list) { // 遍历子句列表,获取列号和操作符号 RestrictInfo* rinfo = (RestrictInfo*)lfirst(l); Oid op; Var* var = NULL; @@ -567,7 +589,7 @@ static bool rel_is_distinct_for(PlannerInfo* root, RelOptInfo* rel, List* clause else var = (Var*)get_leftop(rinfo->clause); - if (var != NULL) { + if (var != NULL) { // 尝试找到兼容的 Var /* try to find compatible var */ var = locate_distribute_var((Expr*)var); } @@ -584,7 +606,7 @@ static bool rel_is_distinct_for(PlannerInfo* root, RelOptInfo* rel, List* clause opids = lappend_oid(opids, op); } - if (query_is_distinct_for(subquery, colnos, opids)) + if (query_is_distinct_for(subquery, colnos, opids))// 调用 query_is_distinct_for 函数判断子查询是否支持 DISTINCT return true; } return false; @@ -601,7 +623,7 @@ static bool rel_is_distinct_for(PlannerInfo* root, RelOptInfo* rel, List* clause * query_is_distinct_for()'s argument lists if the call could not possibly * succeed. */ -bool query_supports_distinctness(Query* query) +bool query_supports_distinctness(Query* query)// 判断查询是否支持 DISTINCT 操作 { if (query->distinctClause != NIL || query->groupClause != NIL || query->hasAggs || query->havingQual || query->setOperations) @@ -628,11 +650,12 @@ bool query_supports_distinctness(Query* query) * should give trustworthy answers for all operators that we might need * to deal with here.) */ -bool query_is_distinct_for(Query* query, List* colnos, List* opids) +bool query_is_distinct_for(Query* query, List* colnos, List* opids)// 判断查询是否对给定列列表 colnos 和操作符列表 opids 支持 DISTINCT 操作 { ListCell* l = NULL; Oid opid; + // 断言:列列表和操作符列表长度必须相等 Assert(list_length(colnos) == list_length(opids)); /* @@ -643,7 +666,7 @@ bool query_is_distinct_for(Query* query, List* colnos, List* opids) * specified columns, since those must be evaluated before de-duplication; * but it doesn't presently seem worth the complication to check that.) */ - if (expression_returns_set((Node*)query->targetList)) + if (expression_returns_set((Node*)query->targetList))// 如果查询的目标列表返回集合,则不支持 DISTINCT return false; /* @@ -651,8 +674,9 @@ bool query_is_distinct_for(Query* query, List* colnos, List* opids) * columns in the DISTINCT clause appear in colnos and operator semantics * match. */ + // 如果查询中存在 DISTINCT 子句 if (query->distinctClause != NIL) { - if (check_column_uniqueness(query->distinctClause, query->targetList, colnos, opids)) + if (check_column_uniqueness(query->distinctClause, query->targetList, colnos, opids))// 调用 check_column_uniqueness 函数检查列是否唯一 return true; } @@ -660,15 +684,15 @@ bool query_is_distinct_for(Query* query, List* colnos, List* opids) * Similarly, GROUP BY guarantees uniqueness if all the grouped columns * appear in colnos and operator semantics match. */ - if (query->groupClause != NIL && query->groupingSets == NIL) { - if (check_column_uniqueness(query->groupClause, query->targetList, colnos, opids)) + if (query->groupClause != NIL && query->groupingSets == NIL) {// 如果查询中存在 GROUP BY 子句且没有 GROUPING SETS + if (check_column_uniqueness(query->groupClause, query->targetList, colnos, opids))// 调用 check_column_uniqueness 函数检查列是否唯一 return true; } else if (query->groupingSets != NIL) { /* * If we have grouping sets with expressions, we probably don't have * uniqueness and analysis would be hard. Punt. */ - if (query->groupClause != NIL) + if (query->groupClause != NIL) // 如果存在 GROUPING SETS return false; /* @@ -677,13 +701,13 @@ bool query_is_distinct_for(Query* query, List* colnos, List* opids) * then we're returning only one row and are certainly unique. But * otherwise, we know we're certainly not unique. */ - bool isTrue = list_length(query->groupingSets) == 1 && + bool isTrue = list_length(query->groupingSets) == 1 &&// 如果 GROUPING SETS 只包含一个空集合 ((GroupingSet*)linitial(query->groupingSets))->kind == GROUPING_SET_EMPTY; if (isTrue) return true; else return false; - } else { + } else {// 如果查询中存在聚合函数或 HAVING 子句,则支持 DISTINCT /* * If we have no GROUP BY, but do have aggregates or HAVING, then the * result is at most one row so it's surely unique, for any operators. @@ -696,18 +720,19 @@ bool query_is_distinct_for(Query* query, List* colnos, List* opids) * UNION, INTERSECT, EXCEPT guarantee uniqueness of the whole output row, * except with ALL. */ + // 如果查询中存在集合操作 if (query->setOperations != NULL) { SetOperationStmt* topop = (SetOperationStmt*)query->setOperations; Assert(IsA(topop, SetOperationStmt)); Assert(topop->op != SETOP_NONE); - if (!topop->all) { + if (!topop->all) {// 如果不是 UNION ALL 操作 ListCell* lg = NULL; /* We're good if all the nonjunk output columns are in colnos */ lg = list_head(topop->groupClauses); - foreach (l, query->targetList) { + foreach (l, query->targetList) {// 遍历查询的目标列表 TargetEntry* tle = (TargetEntry*)lfirst(l); SortGroupClause* sgc = NULL; @@ -718,12 +743,13 @@ bool query_is_distinct_for(Query* query, List* colnos, List* opids) Assert(lg != NULL); sgc = (SortGroupClause*)lfirst(lg); lg = lnext(lg); - + // 调用 distinct_col_search 函数查找列的操作符 opid = distinct_col_search(tle->resno, colnos, opids); if (!OidIsValid(opid) || !equality_ops_are_compatible(opid, sgc->eqop)) break; /* exit early if no match */ } if (l == NULL) /* had matches for all? */ + // 如果成功遍历了所有目标列,说明支持 DISTINCT return true; } } @@ -744,7 +770,7 @@ bool query_is_distinct_for(Query* query, List* colnos, List* opids) * Given group clause and targetlist, find if all the aggregated columns * in colnos, return true if so, else false. */ -static bool check_column_uniqueness(List* groupClause, List* targetList, List* colnos, List* opids) +static bool check_column_uniqueness(List* groupClause, List* targetList, List* colnos, List* opids)// 检查列是否唯一 { ListCell* l = NULL; Oid opid; @@ -754,9 +780,11 @@ static bool check_column_uniqueness(List* groupClause, List* targetList, List* c TargetEntry* tle = get_sortgroupclause_tle(sgc, targetList); opid = distinct_col_search(tle->resno, colnos, opids); + // 如果操作符无效或不兼容,则中断循环 if (!OidIsValid(opid) || !equality_ops_are_compatible(opid, sgc->eqop)) break; /* exit early if no match */ } + // 如果成功遍历了所有列,说明支持 DISTINCT if (l == NULL) /* had matches for all? */ return true; @@ -770,15 +798,15 @@ static bool check_column_uniqueness(List* groupClause, List* targetList, List* c * else return InvalidOid. (Ordinarily colnos would not contain duplicates, * but if it does, we arbitrarily select the first match.) */ -static Oid distinct_col_search(int colno, List* colnos, List* opids) +static Oid distinct_col_search(int colno, List* colnos, List* opids)// 在列列表 colnos 中查找列的操作符 { ListCell* lc1 = NULL; ListCell* lc2 = NULL; - forboth(lc1, colnos, lc2, opids) + forboth(lc1, colnos, lc2, opids) // 同时遍历列列表和操作符列表 { if (colno == lfirst_int(lc1)) return lfirst_oid(lc2); } - return InvalidOid; + return InvalidOid;// 如果未找到,返回无效的操作符标识 } diff --git a/src/gausskernel/optimizer/plan/createplan.cpp b/src/gausskernel/optimizer/plan/createplan.cpp old mode 100755 new mode 100644 index 9ada54aa5..5a9b8537a --- a/src/gausskernel/optimizer/plan/createplan.cpp +++ b/src/gausskernel/optimizer/plan/createplan.cpp @@ -252,18 +252,18 @@ FORCE_INLINE bool CanTransferInJoin(JoinType jointype) * * @return: void */ -void set_plan_rows(Plan* plan, double globalRows, double multiple) +void set_plan_rows(Plan* plan, double globalRows, double multiple)// 设置计划节点的行数估算值 { - plan->multiple = multiple; + plan->multiple = multiple;// 设置计划节点的多倍数 /* * for global stats, We should reset global rows as localRows*u_sess->pgxc_cxt.NumDataNodes for replication except * RemoteQuery, because the local rows is equal to global rows. */ - if (is_replicated_plan(plan) && is_execute_on_datanodes(plan)) { - plan->plan_rows = get_global_rows(globalRows, multiple, ng_get_dest_num_data_nodes(plan)); + if (is_replicated_plan(plan) && is_execute_on_datanodes(plan)) {// 如果计划是复制计划并且在数据节点上执行 + plan->plan_rows = get_global_rows(globalRows, multiple, ng_get_dest_num_data_nodes(plan));// 调用 get_global_rows 函数获取全局行数估算值 } else { - plan->plan_rows = globalRows; + plan->plan_rows = globalRows;// 否则,使用传入的全局行数估算值 } } @@ -280,10 +280,10 @@ void set_plan_rows(Plan* plan, double globalRows, double multiple) * * @return: void */ -void set_plan_rows_from_plan(Plan* plan, double localRows, double multiple) +void set_plan_rows_from_plan(Plan* plan, double localRows, double multiple)// 从另一个计划节点获取行数估算值,并设置多倍数 { - plan->multiple = multiple; - plan->plan_rows = get_global_rows(localRows, plan->multiple, ng_get_dest_num_data_nodes(plan)); + plan->multiple = multiple;// 设置计划节点的多倍数 + plan->plan_rows = get_global_rows(localRows, plan->multiple, ng_get_dest_num_data_nodes(plan));// 调用 get_global_rows 函数获取全局行数估算值 } /* @@ -301,23 +301,24 @@ void set_plan_rows_from_plan(Plan* plan, double localRows, double multiple) * * Returns a Plan tree. */ -Plan* create_plan(PlannerInfo* root, Path* best_path) +Plan* create_plan(PlannerInfo* root, Path* best_path)// 创建查询计划节点 { Plan* plan = NULL; /* plan_params should not be in use in current query level */ - Assert(root->plan_params == NIL); + Assert(root->plan_params == NIL);// 断言:查询计划参数列表为空 /* Initialize this module's private workspace in PlannerInfo */ + // 清空当前外部关系和参数 root->curOuterRels = NULL; root->curOuterParams = NIL; u_sess->opt_cxt.is_under_append_plan = false; /* Recursively process the path tree */ - plan = create_plan_recurse(root, best_path); + plan = create_plan_recurse(root, best_path);// 递归创建查询计划 /* Check we successfully assigned all NestLoopParams to plan nodes */ - if (root->curOuterParams != NIL) + if (root->curOuterParams != NIL)// 检查是否成功为所有 NestLoopParams 分配了计划节点 ereport(ERROR, (errmodule(MOD_OPT), errcode(ERRCODE_OPTIMIZER_INCONSISTENT_STATE), @@ -329,6 +330,7 @@ Plan* create_plan(PlannerInfo* root, Path* best_path) root->plan_params = NIL; /* Try to find the changed vars in chosed inlist2join path */ + // 如果在 QRW_INLIST2JOIN_CBO 模式下且存在变量映射,则查找 inlist2join 路径 if (u_sess->opt_cxt.qrw_inlist2join_optmode == QRW_INLIST2JOIN_CBO && root->var_mappings != NIL) { find_inlist2join_path(root, best_path); } diff --git a/src/gausskernel/optimizer/plan/dynsmp_single.cpp b/src/gausskernel/optimizer/plan/dynsmp_single.cpp index d7e5c96af..7c25055b9 100644 --- a/src/gausskernel/optimizer/plan/dynsmp_single.cpp +++ b/src/gausskernel/optimizer/plan/dynsmp_single.cpp @@ -26,21 +26,22 @@ #include "optimizer/streamplan.h" void InitDynamicSmp() { - DISTRIBUTED_FEATURE_NOT_SUPPORTED(); + DISTRIBUTED_FEATURE_NOT_SUPPORTED();// 调用 DISTRIBUTED_FEATURE_NOT_SUPPORTED 宏,表示不支持该功能 } void ChooseStartQueryDop(int hashTableCount) { - DISTRIBUTED_FEATURE_NOT_SUPPORTED(); + DISTRIBUTED_FEATURE_NOT_SUPPORTED();// 调用 DISTRIBUTED_FEATURE_NOT_SUPPORTED 宏,表示不支持该功能 } void OptimizePlanDop(PlannedStmt* plannedStmt) { - DISTRIBUTED_FEATURE_NOT_SUPPORTED(); + DISTRIBUTED_FEATURE_NOT_SUPPORTED(); // 调用 DISTRIBUTED_FEATURE_NOT_SUPPORTED 宏,表示不支持该功能 } bool IsDynamicSmpEnabled() { + // 使用一系列条件来检查是否启用了动态并行查询执行 return IS_STREAM_PLAN && u_sess->opt_cxt.max_query_dop >= 0 && !u_sess->attr.attr_common.IsInplaceUpgrade && !IsInitdb; } diff --git a/src/gausskernel/optimizer/plan/initsplan.cpp b/src/gausskernel/optimizer/plan/initsplan.cpp index d3153df76..659c209c7 100644 --- a/src/gausskernel/optimizer/plan/initsplan.cpp +++ b/src/gausskernel/optimizer/plan/initsplan.cpp @@ -38,18 +38,27 @@ /* Elements of the postponed_qual_list used during deconstruct_recurse */ typedef struct PostponedQual { + // 指向查询条件的指针 Node *qual; /* a qual clause waiting to be processed */ + // 关系标识符集合 Relids relids; /* the set of baserels it references */ } PostponedQual; static void extract_lateral_references(PlannerInfo *root, RelOptInfo *brel, Index rtindex); +// 从查询计划中提取横向引用信息 static List* deconstruct_recurse( PlannerInfo* root, Node* jtnode, bool below_outer_join, Relids* qualscope, Relids* inner_join_rels, List **postponed_qual_list); + // 递归解构查询树,处理外连接和查询条件等 static SpecialJoinInfo* make_outerjoininfo( PlannerInfo* root, Relids left_rels, Relids right_rels, Relids inner_join_rels, JoinType jointype, List* clause); + // 创建外连接信息结构 static bool check_outerjoin_delay(PlannerInfo* root, Relids* relids_p, Relids* nullable_relids_p, bool is_pushed_down); +// 检查外连接是否需要延迟处理 static bool check_equivalence_delay(PlannerInfo* root, RestrictInfo* restrictinfo); +// 检查等价性条件是否需要延迟处理 static bool check_redundant_nullability_qual(PlannerInfo* root, Node* clause); +// 检查冗余的空值条件 static void check_mergejoinable(RestrictInfo* restrictinfo); +// 检查条件是否适合合并连接 /***************************************************************************** * @@ -72,26 +81,30 @@ static void check_mergejoinable(RestrictInfo* restrictinfo); * RELOPT_BASEREL. (Note: build_simple_rel recurses internally to build * "other rel" RelOptInfos for the members of any appendrels we find here.) */ -void add_base_rels_to_query(PlannerInfo* root, Node* jtnode) +void add_base_rels_to_query(PlannerInfo* root, Node* jtnode)//函数用于将基本关系添加到查询计划中。 { - if (jtnode == NULL) + if (jtnode == NULL)// 如果 jtnode 为空,则直接返回,不进行任何操作。 return; + + // 检查 jtnode 是否是 RangeTblRef 类型的节点。 if (IsA(jtnode, RangeTblRef)) { - int varno = ((RangeTblRef*)jtnode)->rtindex; + int varno = ((RangeTblRef*)jtnode)->rtindex;// 如果是 RangeTblRef 类型,获取变量号(rtindex)。 - (void)build_simple_rel(root, varno, RELOPT_BASEREL); + (void)build_simple_rel(root, varno, RELOPT_BASEREL);// 调用 build_simple_rel 函数,将基本关系添加到查询计划中 } else if (IsA(jtnode, FromExpr)) { + // 如果 jtnode 是 FromExpr 类型的节点,表示它包含一个 FROM 子句。 FromExpr* f = (FromExpr*)jtnode; ListCell* l = NULL; - foreach (l, f->fromlist) + foreach (l, f->fromlist)// 遍历 FROM 子句中的每个元素,并递归调用 add_base_rels_to_query 函数 add_base_rels_to_query(root, (Node*)lfirst(l)); } else if (IsA(jtnode, JoinExpr)) { JoinExpr* j = (JoinExpr*)jtnode; - +// 递归调用 add_base_rels_to_query 函数来处理 JOIN 子句的左子树和右子树 add_base_rels_to_query(root, j->larg); add_base_rels_to_query(root, j->rarg); } else { + // 如果 jtnode 是不被识别的节点类型,则报告错误。 ereport(ERROR, (errmodule(MOD_OPT), errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE), @@ -112,13 +125,19 @@ void add_base_rels_to_query(PlannerInfo* root, Node* jtnode) * We mark such vars as needed by "relation 0" to ensure that they will * propagate up through all join plan steps. */ -void build_base_rel_tlists(PlannerInfo* root, List* final_tlist) +void build_base_rel_tlists(PlannerInfo* root, List* final_tlist)//函数用于构建基本关系(base relation)的目标列表(targetlist) { + // 使用 pull_var_clause 函数从 final_tlist 中提取变量引用。 + // PVC_RECURSE_AGGREGATES 表示递归处理聚合函数,PVC_INCLUDE_PLACEHOLDERS 表示包括占位符。 List* tlist_vars = pull_var_clause((Node*)final_tlist, PVC_RECURSE_AGGREGATES, PVC_INCLUDE_PLACEHOLDERS); + // 如果提取的变量列表不为空。 if (tlist_vars != NIL) { + // 将提取的变量添加到查询计划的目标列表中。 + // bms_make_singleton(0) 创建一个包含编号为 0 的单一位图集合,表示基本关系。 + // 最后一个参数 true 表示这些变量是直接引用的。 add_vars_to_targetlist(root, tlist_vars, bms_make_singleton(0), true); - list_free_ext(tlist_vars); + list_free_ext(tlist_vars);// 释放 tlist_vars 列表的内存。 } } @@ -137,34 +156,35 @@ void build_base_rel_tlists(PlannerInfo* root, List* final_tlist) * update their ph_needed. (It should be true before deconstruct_jointree * begins, and false after that.) */ + //函数用于将变量引用添加到查询计划的目标列表中。 void add_vars_to_targetlist(PlannerInfo* root, List* vars, Relids where_needed, bool create_new_ph) { ListCell* temp = NULL; - + // 断言 where_needed 不应为空�����������它表示需要这些变量的关系的集合。 AssertEreport(!bms_is_empty(where_needed), MOD_OPT, "bms should not be null"); - foreach (temp, vars) { + foreach (temp, vars) {// 遍历传入的变量列表。 Node* node = (Node*)lfirst(temp); - if (IsA(node, Var)) { + if (IsA(node, Var)) {// 如果节点是 Var 类型,表示一个变量引用。 Var* var = (Var*)node; RelOptInfo* rel = find_base_rel(root, var->varno); int attno = var->varattno; - + // 断言确保 attno 在合理的范围内。 AssertEreport(attno >= rel->min_attr && attno <= rel->max_attr, MOD_OPT, "attno is out of range"); - attno -= rel->min_attr; - if (rel->attr_needed[attno] == NULL) { + attno -= rel->min_attr;// 调整 attno 以匹配相对于 reltargetlist 的偏移。 + if (rel->attr_needed[attno] == NULL) {// 如果属性在关系的目标列表中尚未出现,则将其添加到列表中。 /* Variable not yet requested, so add to reltargetlist */ /* XXX is copyObject necessary here? */ rel->reltargetlist = lappend(rel->reltargetlist, copyObject(var)); } - rel->attr_needed[attno] = bms_add_members(rel->attr_needed[attno], where_needed); - } else if (IsA(node, PlaceHolderVar)) { + rel->attr_needed[attno] = bms_add_members(rel->attr_needed[attno], where_needed);// 将属性标记为需要的关系集合。 + } else if (IsA(node, PlaceHolderVar)) {// 如果节点是 PlaceHolderVar 类型,表示占位符变量引用。 PlaceHolderVar* phv = (PlaceHolderVar*)node; - PlaceHolderInfo* phinfo = find_placeholder_info(root, phv, create_new_ph); + PlaceHolderInfo* phinfo = find_placeholder_info(root, phv, create_new_ph);// 查找占位符变量的信息,如果不存在则创建一个新的。 - phinfo->ph_needed = bms_add_members(phinfo->ph_needed, where_needed); - } else { + phinfo->ph_needed = bms_add_members(phinfo->ph_needed, where_needed);// 将占位符标记为需要的关系集合。 + } else {// 如果节点是不被识别的类型,则报告错误。 ereport(ERROR, (errmodule(MOD_OPT), errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE), @@ -180,52 +200,53 @@ void add_vars_to_targetlist(PlannerInfo* root, List* vars, Relids where_needed, * LATERAL REFERENCES * *****************************************************************************/ -void find_lateral_references(PlannerInfo *root) +void find_lateral_references(PlannerInfo *root)//函数用于查找查询计划中的横向引用(lateral references) { int rti = 0; /* We need do nothing if the query contains no LATERAL RTEs */ - if (!root->hasLateralRTEs) { + if (!root->hasLateralRTEs) {// 如果查询计划中没有横向引用,直接返回。 return; } /* * Examine all baserels (the rel array has been set up by now). */ - for (rti = 1; rti < root->simple_rel_array_size; rti++) { + for (rti = 1; rti < root->simple_rel_array_size; rti++) {// 遍历查询计划中的每个基本关系。 RelOptInfo *brel = root->simple_rel_array[rti]; /* there may be empty slots corresponding to non-baserel RTEs */ - if (brel == NULL) { + if (brel == NULL) {// 如果基本关系为空,则继续下一次迭代。 continue; } - + // 断言确保基本关系的 relid 与 rti 相等,这是一种验证。 Assert(brel->relid == (Index)rti); /* sanity check on array */ /* ignore RTEs that are "other rels" */ - if (brel->reloptkind != RELOPT_BASEREL) { + if (brel->reloptkind != RELOPT_BASEREL) {// 如果基本关系的类型不是 RELOPT_BASEREL,则继续下一次迭代。 continue; } - extract_lateral_references(root, brel, rti); + extract_lateral_references(root, brel, rti);// 调用 extract_lateral_references 函数来提取横向引用。 } } static void extract_lateral_references(PlannerInfo *root, RelOptInfo *brel, Index rtindex) { - RangeTblEntry *rte = root->simple_rte_array[rtindex]; - List *vars = NIL; - List *newvars = NIL; - Relids where_needed = NULL; + RangeTblEntry *rte = root->simple_rte_array[rtindex];// 获取与基本关系对应的 RangeTblEntry。 + List *vars = NIL;// 存储原始变量引用的列表 + List *newvars = NIL;// 存储处理后的变量引用的列表 + Relids where_needed = NULL; // 标识哪些关系���要���些���量 ListCell *lc = NULL; /* No cross-references are possible if it's not LATERAL */ - if (!rte->lateral) { + if (!rte->lateral) {// 如果不是横向引用,直接返回。 return; } /* Fetch the appropriate variables */ + // 根据 RTE 类型提取变量引用。 if (rte->rtekind == RTE_SUBQUERY) { vars = pull_vars_of_level((Node *)rte->subquery, 1); } else if (rte->rtekind == RTE_FUNCTION) { @@ -236,12 +257,12 @@ extract_lateral_references(PlannerInfo *root, RelOptInfo *brel, Index rtindex) return; } - if (vars == NIL) { + if (vars == NIL) {// 如果没有提取到变量引用,直接返回。 return; } /* Copy each Var (or PlaceHolderVar) and adjust it to match our level */ - newvars = NIL; + newvars = NIL;// 处理变量引用并将其添加到 newvars 列表中。 foreach(lc, vars) { Node *node = (Node *)lfirst(lc); @@ -250,12 +271,14 @@ extract_lateral_references(PlannerInfo *root, RelOptInfo *brel, Index rtindex) Var *var = (Var *)node; /* Adjustment is easy since it's just one node */ + // 重置变量的 varlevelsup 以匹配基本关系的层次。 var->varlevelsup = 0; } else if (IsA(node, PlaceHolderVar)) { PlaceHolderVar *phv = (PlaceHolderVar *)node; int levelsup = phv->phlevelsup; /* Have to work harder to adjust the contained expression too */ + // 如果占位符引用的层次不匹配,进行修正。 if (levelsup != 0) { IncrementVarSublevelsUp(node, -levelsup, 0); } @@ -267,18 +290,21 @@ extract_lateral_references(PlannerInfo *root, RelOptInfo *brel, Index rtindex) * nobody is going to look at the contained expression to notice * whether its Vars have the right level. */ + // 如果占位符引用的层次大于 0,需要预处理占位符表达式 if (levelsup > 0) { phv->phexpr = preprocess_phv_expression(root, phv->phexpr); } } else { + // 如果变量引用不是 Var 或占位符引用不是 PlaceHolderVar,报告错误。 ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("expected Var or PlaceHolderVar, others unsupported. "))); } - + // 将处理后的变量引用添加到 newvars 列表中。 newvars = lappend(newvars, node); } + // 释放原始变量引用列表的内存。 list_free(vars); /* @@ -291,10 +317,10 @@ extract_lateral_references(PlannerInfo *root, RelOptInfo *brel, Index rtindex) where_needed = bms_make_singleton(rtindex); /* Push the Vars into their source relations' targetlists */ - add_vars_to_targetlist(root, newvars, where_needed, true); + add_vars_to_targetlist(root, newvars, where_needed, true);// 调用 add_vars_to_targetlist 函数将变量引用添加到目标列表中。 /* Remember the lateral references for create_lateral_join_info */ - brel->lateral_vars = newvars; + brel->lateral_vars = newvars;// 将 newvars 设置为基本关系的横向引用列表。 } /* @@ -305,55 +331,63 @@ extract_lateral_references(PlannerInfo *root, RelOptInfo *brel, Index rtindex) * This has to run after deconstruct_jointree, because we need to know the * final ph_eval_at values for referenced PlaceHolderVars. */ + +/* +该函数用于创建横向连接信息,它遍历查询计划中的基本关系,为每个基本关系标识横向引用的变量, +并将横向关系标识设置在基本关系和其子关系上,以确保查询计划正确处理横向引用 +*/ + void -create_lateral_join_info(PlannerInfo *root) +create_lateral_join_info(PlannerInfo *root)//函数用于创建横向连接信息。 { int rti; /* We need do nothing if the query contains no LATERAL RTEs */ - if (!root->hasLateralRTEs) + if (!root->hasLateralRTEs)// 如果查询计划中没有横向引用,直接返回。 return; /* * Examine all baserels (the rel array has been set up by now). */ - for (rti = 1; rti < root->simple_rel_array_size; rti++) + for (rti = 1; rti < root->simple_rel_array_size; rti++)// 遍历查询计划中的每个基本关系。 { RelOptInfo *brel = root->simple_rel_array[rti]; Relids lateral_relids; ListCell *lc = NULL; /* there may be empty slots corresponding to non-baserel RTEs */ - if (brel == NULL) + if (brel == NULL)// 如果基本关系为空,则继续下一次迭代。 continue; + // 断言确保基本关系的 relid 与 rti 相等,这是一种验证。 Assert(brel->relid == (Index)rti); /* sanity check on array */ /* ignore RTEs that are "other rels" */ - if (brel->reloptkind != RELOPT_BASEREL) + if (brel->reloptkind != RELOPT_BASEREL)// 如果基本关系的类型不是 RELOPT_BASEREL,则继续下一次迭代。 continue; lateral_relids = NULL; /* consider each laterally-referenced Var or PHV */ - foreach(lc, brel->lateral_vars) + foreach(lc, brel->lateral_vars)// 遍历基本关系的横向引用变量列表。 { Node *node = (Node *) lfirst(lc); if (IsA(node, Var)) { - Var *var = (Var *) node; + Var *var = (Var *) node;// 如果是 Var 类型的变量引用,将其添加到 lateral_relids 中。 - add_lateral_info(root, rti, bms_make_singleton(var->varno)); + add_lateral_info(root, rti, bms_make_singleton(var->varno));// 调用 add_lateral_info 函数将变量的信息添加到查询计划中。 lateral_relids = bms_add_member(lateral_relids, var->varno); } else if (IsA(node, PlaceHolderVar)) { + // 如果是 PlaceHolderVar 类型的占位符引用,处理并添加到 lateral_relids 中 PlaceHolderVar *phv = (PlaceHolderVar *) node; PlaceHolderInfo *phinfo = find_placeholder_info(root, phv, false); - + // 调用 add_lateral_info 函数将占位符信息添加到查询计划中。 add_lateral_info(root, rti, bms_copy(phinfo->ph_eval_at)); lateral_relids = bms_add_members(lateral_relids, phinfo->ph_eval_at); @@ -363,8 +397,9 @@ create_lateral_join_info(PlannerInfo *root) } /* We now know all the relids needed for lateral refs in this rel */ - if (bms_is_empty(lateral_relids)) + if (bms_is_empty(lateral_relids))// 如果 lateral_relids 为空,则继续下一次迭代。 continue; /* ensure lateral_relids is NULL if empty */ + // 将 lateral_relids 设置为基本关系的横向关系标识。 brel->lateral_relids = lateral_relids; /* @@ -376,19 +411,19 @@ create_lateral_join_info(PlannerInfo *root) * every child anyway, and there's no value in forcing extra * reparameterize_path() calls. */ - if (root->simple_rte_array[rti]->inh) + if (root->simple_rte_array[rti]->inh)// 如果基本关系是继承的,则处理其子关系。 { foreach(lc, root->append_rel_list) { AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(lc); RelOptInfo *childrel = NULL; - if (appinfo->parent_relid != (Index)rti) + if (appinfo->parent_relid != (Index)rti)// 如果 appinfo 不是基本关系的子关系,则继续下一次迭代。 continue; childrel = root->simple_rel_array[appinfo->child_relid]; - Assert(childrel->reloptkind == RELOPT_OTHER_MEMBER_REL); + Assert(childrel->reloptkind == RELOPT_OTHER_MEMBER_REL);// 断言确保子关系是 RELOPT_OTHER_MEMBER_REL 类型,并且没有横向关系标识。 Assert(childrel->lateral_relids == NULL); - childrel->lateral_relids = lateral_relids; + childrel->lateral_relids = lateral_relids;// 将 lateral_relids 设置为子关系的横向关系标识。 } } } @@ -401,12 +436,12 @@ create_lateral_join_info(PlannerInfo *root) * We suppress redundant list entries. The passed lhs set must be freshly * made; we free it if not used in a new list entry. */ -void add_lateral_info(PlannerInfo *root, Index rhs, Relids lhs) +void add_lateral_info(PlannerInfo *root, Index rhs, Relids lhs)//函数用于添加横向连接信息到查询计划的 lateral_info_list 列表中。 { LateralJoinInfo *ljinfo = NULL; ListCell *l = NULL; - Assert(!bms_is_member(rhs, lhs)); + Assert(!bms_is_member(rhs, lhs));// 断言确保 rhs 不在 lhs 中,以避免重复添加。 /* * If an existing list member has the same RHS and an LHS that is a subset @@ -414,20 +449,21 @@ void add_lateral_info(PlannerInfo *root, Index rhs, Relids lhs) * The only case that is really worth worrying about is identical entries, * and we handle that well enough with this simple logic. */ - foreach(l, root->lateral_info_list) { + foreach(l, root->lateral_info_list) {// 遍历 lateral_info_list 列表,查找是否已存在相同的横向连接信息。 ljinfo = (LateralJoinInfo *) lfirst(l); if (rhs == ljinfo->lateral_rhs && - bms_is_subset(lhs, ljinfo->lateral_lhs)) { + bms_is_subset(lhs, ljinfo->lateral_lhs)) {// 如果已存在相同的横向连接信息,释放 lhs 并直接返回。 bms_free(lhs); return; } } /* Not there, so make a new entry */ + // 如果没有找到相同的横向连接信息,则创建一个新的 LateralJoinInfo 结构。 ljinfo = makeNode(LateralJoinInfo); ljinfo->lateral_rhs = rhs; ljinfo->lateral_lhs = lhs; - root->lateral_info_list = lappend(root->lateral_info_list, ljinfo); + root->lateral_info_list = lappend(root->lateral_info_list, ljinfo); // 将新的横向连接信息添加到 lateral_info_list 列表中。 } @@ -462,26 +498,27 @@ void add_lateral_info(PlannerInfo *root, Index rhs, Relids lhs) * clauses appearing above it. This forces those clauses to be delayed until * application of the outer join (or maybe even higher in the join tree). */ -List* deconstruct_jointree(PlannerInfo* root) +List* deconstruct_jointree(PlannerInfo* root)//函数将查询的联接树(join tree)分解为一个列表 { - List *result = NIL; - Relids qualscope = NULL; - Relids inner_join_rels = NULL; - List *postponed_qual_list = NIL; + List *result = NIL; // 用于存储分解后的联接树 + Relids qualscope = NULL; // 当前的限定范围(qualifications scope) + Relids inner_join_rels = NULL; // 内部联接的关系集合 + List *postponed_qual_list = NIL; // 存储推迟处理的限定条件的列表 - /* Start recursion at top of jointree */ + /* 开始从联接树的顶部进行递归处理 */ AssertEreport( root->parse->jointree != NULL && IsA(root->parse->jointree, FromExpr), MOD_OPT, "From expression is required."); result = deconstruct_recurse(root, (Node *) root->parse->jointree, false, &qualscope, &inner_join_rels, &postponed_qual_list); - /* Shouldn't be any leftover quals */ + /* 不应该有未处理的限定条件 */ Assert(postponed_qual_list == NIL); return result; } + /* * process_security_barrier_quals * Transfer security-barrier quals into relation's baserestrictinfo list. @@ -495,18 +532,20 @@ List* deconstruct_jointree(PlannerInfo* root) * them for purposes like equivalence class creation. Quals attached to * individual child rels will be dealt with during path creation. */ + + //函数用于处理安全障碍限定条件 + //这些限定条件通常用于实现安全性相关的筛选操作,以确保只有合适的用户可以访问数据 static void process_security_barrier_quals( PlannerInfo* root, const RangeTblEntry* rte, Relids qualscope, bool below_outer_join) { ListCell* cell1 = NULL; ListCell* cell2 = NULL; - List* quals = NIL; - Node* qual = NULL; - Index security_level = 0; + List* quals = NIL; // 存储限定条件的列表 + Node* qual = NULL; // 限定条件的表达式 + Index security_level = 0; // 安全级别 /* - * Each element of the securityQuals list has been preprocessed into an - * implicitly-ANDed list of clauses. + * 安全性限定条件列表中的每个元素都已经被预处理成了一个隐式AND连接的限定条件列表。 */ foreach (cell1, rte->securityQuals) { quals = (List*)lfirst(cell1); @@ -514,21 +553,22 @@ static void process_security_barrier_quals( foreach (cell2, quals) { qual = (Node*)lfirst(cell2); + // 将限定条件分发给相关的关系 distribute_qual_to_rels( root, qual, false, below_outer_join, JOIN_INNER, security_level, qualscope, qualscope, NULL, NULL, NULL); } /* - * All the clauses in a given sublist have the same security level, - * but successive sublists get higher levels. + * 每个子列表中的所有限定条件具有相同的安全级别,但是连续的子列表具有更高的级别。 */ security_level++; } - /* Assert that qual_security_level is higher than anything we just used */ + /* 断言限定条件的安全级别要高于我们刚刚使用的任何级别 */ Assert(security_level <= root->qualSecurityLevel); } + /* * deconstruct_recurse * One recursion level of deconstruct_jointree processing. @@ -548,28 +588,34 @@ static void process_security_barrier_quals( * * In addition, entries will be added to root->join_info_list for outer joins. */ + +/* +这段代码主要负责将查询中的关联关系和条件表达式进行递归处理, +并将它们组织成一个关联关系列表。根据不同的联接类型,处理方式有所不同, +同时也考虑了条件表达式的推迟处理 +*/ static List* deconstruct_recurse(PlannerInfo* root, Node* jtnode, bool below_outer_join, Relids* qualscope, - Relids* inner_join_rels, List **postponed_qual_list) + Relids* inner_join_rels, List **postponed_qual_list) { - List* joinlist = NIL; + List* joinlist = NIL; // 用于存储关联关系的列表 if (jtnode == NULL) { *qualscope = NULL; *inner_join_rels = NULL; - return NIL; + return NIL; // 如果 jtnode 为空,则返回空列表,表示没有关联关系 } if (IsA(jtnode, RangeTblRef)) { int varno = ((RangeTblRef*)jtnode)->rtindex; - /* No quals to deal with, just return correct result */ + /* 没有需要处理的条件表达式,直接返回正确结果 */ *qualscope = bms_make_singleton(varno); - /* Deal with any securityQuals attached to the RTE */ + /* 处理与 RTE(RangeTblEntry) 相关的安全性条件 */ if (root->qualSecurityLevel > 0) process_security_barrier_quals(root, root->simple_rte_array[varno], *qualscope, below_outer_join); - /* A single baserel does not create an inner join */ + /* 单个基表不会创建内连接 */ *inner_join_rels = NULL; - joinlist = list_make1(jtnode); + joinlist = list_make1(jtnode); // 将当前关系节点添加到关联关系列表中 } else if (IsA(jtnode, FromExpr)) { FromExpr* f = (FromExpr*)jtnode; List *child_postponed_quals = NIL; @@ -577,10 +623,8 @@ static List* deconstruct_recurse(PlannerInfo* root, Node* jtnode, ListCell* l = NULL; /* - * First, recurse to handle child joins. We collapse subproblems into - * a single joinlist whenever the resulting joinlist wouldn't exceed - * from_collapse_limit members. Also, always collapse one-element - * subproblems, since that won't lengthen the joinlist anyway. + * 首先,递归处理子关联关系。只有在结果关联关系列表不会超过 from_collapse_limit 时, + * 才会将子关联关系列表合并为一个。此外,一元子问题始终合并,因为这不会增加关联关系列表的长度。 */ *qualscope = NULL; *inner_join_rels = NULL; @@ -604,18 +648,15 @@ static List* deconstruct_recurse(PlannerInfo* root, Node* jtnode, } /* - * A FROM with more than one list element is an inner join subsuming - * all below it, so we should report inner_join_rels = qualscope. If - * there was exactly one element, we should (and already did) report - * whatever its inner_join_rels were. If there were no elements (is - * that possible?) the initialization before the loop fixed it. + * 如果 FROM 子句包含多个元素,则表示是一个包含所有下级的内连接, + * 因此我们应该报告 inner_join_rels = qualscope。如果只有一个元素,则已经报告了其内连接关系。 + * 如果没有元素(这是否可能?),则循环之前的初始化已经修复了这个问题。 */ if (list_length(f->fromlist) > 1) *inner_join_rels = *qualscope; /* - * Try to process any quals postponed by children. If they need - * further postponement, add them to my output postponed_qual_list. + * 尝试处理子关联关系推迟的任何条件表达式。如果它们需要进一步推迟,就将它们添加到输出的 postponed_qual_list 中。 */ foreach(l, child_postponed_quals) { PostponedQual *pq = (PostponedQual *) lfirst(l); @@ -631,7 +672,7 @@ static List* deconstruct_recurse(PlannerInfo* root, Node* jtnode, } /* - * Now process the top-level quals. + * 现在处理顶层条件表达式。 */ foreach (l, (List*)f->quals) { Node* qual = (Node*)lfirst(l); @@ -650,16 +691,11 @@ static List* deconstruct_recurse(PlannerInfo* root, Node* jtnode, ListCell* l = NULL; /* - * Order of operations here is subtle and critical. First we recurse - * to handle sub-JOINs. Their join quals will be placed without - * regard for whether this level is an outer join, which is correct. - * Then we place our own join quals, which are restricted by lower - * outer joins in any case, and are forced to this level if this is an - * outer join and they mention the outer side. Finally, if this is an - * outer join, we create a join_info_list entry for the join. This - * will prevent quals above us in the join tree that use those rels - * from being pushed down below this level. (It's okay for upper - * quals to be pushed down to the outer side, however.) + * 操作顺序在这里非常微妙和关键。首先,我们递归处理子 JOIN。 + * 它们的连接条件将被放置,而不考虑这个级别是否是外连接,这是正确的。 + * 然后我们放置自己的连接条件,无论如何都受到较低外连接的限制,并且如果这是外连接并且它们提到了外部,则会被强制到这个级别。 + * 最后,如果这是外连接,我们为连接创建一个 SpecialJoinInfo 条目。这将防止在连接树中我们上面的使用那些关系的条件被推送到下面。 + * (对于上面的条件被推送到外面是可以的。) */ switch (j->jointype) { case JOIN_INNER: @@ -671,7 +707,7 @@ static List* deconstruct_recurse(PlannerInfo* root, Node* jtnode, &child_postponed_quals); *qualscope = bms_union(leftids, rightids); *inner_join_rels = *qualscope; - /* Inner join adds no restrictions for quals */ + /* 内连接不会增加条件表达式的限制 */ nonnullable_rels = NULL; break; case JOIN_LEFT: @@ -696,7 +732,7 @@ static List* deconstruct_recurse(PlannerInfo* root, Node* jtnode, &child_postponed_quals); *qualscope = bms_union(leftids, rightids); *inner_join_rels = bms_union(left_inners, right_inners); - /* Semi join adds no restrictions for quals */ + /* 半连接不会增加条件表达式的限制 */ nonnullable_rels = NULL; break; case JOIN_FULL: @@ -708,30 +744,26 @@ static List* deconstruct_recurse(PlannerInfo* root, Node* jtnode, &child_postponed_quals); *qualscope = bms_union(leftids, rightids); *inner_join_rels = bms_union(left_inners, right_inners); - /* each side is both outer and inner */ + /* 每一侧都是外连接和内连接 */ nonnullable_rels = *qualscope; break; default: { - /* JOIN_RIGHT was eliminated during reduce_outer_joins() */ + /* JOIN_RIGHT 在 reduce_outer_joins() 中被消除 */ ereport(ERROR, (errmodule(MOD_OPT), errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE), - errmsg("unrecognized join type in one level processing of deconstruct jointree: %d", + errmsg("在解构联接树的一个级别中,不认识的联接类型:%d", (int)j->jointype))); - nonnullable_rels = NULL; /* keep compiler quiet */ + nonnullable_rels = NULL; /* 使编译器保持安静 */ leftjoinlist = rightjoinlist = NIL; } break; } /* - * For an OJ, form the SpecialJoinInfo now, because we need the OJ's - * semantic scope (ojscope) to pass to distribute_qual_to_rels. But - * we mustn't add it to join_info_list just yet, because we don't want - * distribute_qual_to_rels to think it is an outer join below us. - * - * Semijoins are a bit of a hybrid: we build a SpecialJoinInfo, but we - * want ojscope = NULL for distribute_qual_to_rels. + * 对于外连接,现在形成 SpecialJoinInfo,因为我们需要将 OJ 的语义范围(ojscope)传递给 distribute_qual_to_rels。 + * 但我们不能立即将其添加到 join_info_list 中,因为我们不希望 distribute_qual_to_rels 认为它是在下面的外连接。 + * 半连接有点混合:我们构建了一个 SpecialJoinInfo,但我们希望 distribute_qual_to_rels 的 ojscope = NULL。 */ if (j->jointype != JOIN_INNER) { sjinfo = make_outerjoininfo(root, leftids, rightids, *inner_join_rels, @@ -746,8 +778,7 @@ static List* deconstruct_recurse(PlannerInfo* root, Node* jtnode, } /* - * Try to process any quals postponed by children. If they need - * further postponement, add them to my output postponed_qual_list. + * 尝试处理子关联关系推迟的任何条件表达式。如果它们需要进一步推迟,就将它们添加到我的输出 postponed_qual_list 中。 */ foreach(l, child_postponed_quals) { @@ -762,15 +793,14 @@ static List* deconstruct_recurse(PlannerInfo* root, Node* jtnode, else { /* - * We should not be postponing any quals past an outer join. - * If this Assert fires, pull_up_subqueries() messed up. + * 我们不应该推迟任何条件表达式到外连接之后。如果这个断言触发了,那么 pull_up_subqueries() 弄错了。 */ Assert(j->jointype == JOIN_INNER); *postponed_qual_list = lappend(*postponed_qual_list, pq); } } - /* Process the JOIN's qual clauses */ + /* 处理 JOIN 的条件表达式 */ foreach (l, (List*)j->quals) { Node* qual = (Node*)lfirst(l); @@ -787,31 +817,29 @@ static List* deconstruct_recurse(PlannerInfo* root, Node* jtnode, postponed_qual_list); } - /* Now we can add the SpecialJoinInfo to join_info_list */ + /* 现在我们可以将 SpecialJoinInfo 添加到 join_info_list 中 */ if (sjinfo != NULL) { root->join_info_list = lappend(root->join_info_list, sjinfo); - /* Each time we do that, recheck placeholder eval levels */ + /* 每次这样做时,都重新检查占位符评估级别 */ update_placeholder_eval_levels(root, sjinfo); } /* - * Finally, compute the output joinlist. We fold subproblems together - * except at a FULL JOIN or where join_collapse_limit would be - * exceeded. + * 最后,计算输出的关联关系列表。我们在 FULL JOIN 或者 join_collapse_limit 超出时合并子问题。 */ if (j->jointype == JOIN_FULL) { - /* force the join order exactly at this node */ + /* 强制在这个节点上精确地设置连接顺序 */ joinlist = list_make1(list_make2(leftjoinlist, rightjoinlist)); } else if (list_length(leftjoinlist) + list_length(rightjoinlist) <= u_sess->attr.attr_sql.join_collapse_limit) { - /* OK to combine subproblems */ + /* 可以合并子问题 */ joinlist = list_concat(leftjoinlist, rightjoinlist); } else { - /* can't combine, but needn't force join order above here */ + /* 不能合并,但不需要在这里强制连接顺序 */ Node* leftpart = NULL; Node* rightpart = NULL; - /* avoid creating useless 1-element sublists */ + /* 避免创建无用的 1 元素子列表 */ if (list_length(leftjoinlist) == 1) leftpart = (Node*)linitial(leftjoinlist); else @@ -826,28 +854,41 @@ static List* deconstruct_recurse(PlannerInfo* root, Node* jtnode, ereport(ERROR, (errmodule(MOD_OPT), errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE), - errmsg("unrecognized node type in one level of deconstruct jointree: %d", (int)nodeTag(jtnode)))); - joinlist = NIL; /* keep compiler quiet */ + errmsg("在解构联接树的一个级别中,不认识的节点类型:%d", (int)nodeTag(jtnode)))); + joinlist = NIL; /* 使编译器保持安静 */ } - return joinlist; + return joinlist; // 返回关联关系列表 } + void process_security_clause_appendrel(PlannerInfo *root) { if (root->qualSecurityLevel == 0) { + // 如果当前查询的安全级别为0,表示不需要处理安全性条款,直接返回 return; } + ListCell* lc = NULL; foreach(lc, root->append_rel_list) { + // 遍历与主查询(或父查询)关联的子查询或关联关系的列表 AppendRelInfo* appinfo = (AppendRelInfo*)lfirst(lc); Index childRTindex = appinfo->child_relid; + + // 创建一个包含子查询索引的 Relids 集合,这用于标识当前处理的子查询的范围 Relids qualscope = bms_make_singleton((int)childRTindex); + + // 获取子查询的 RangeTblEntry,其中包含了子查询的元数据信息 RangeTblEntry* childRTE = root->simple_rte_array[childRTindex]; + + // 处理与子查询关联的安全性条款 process_security_barrier_quals(root, childRTE, qualscope, false); + + // 释放创建的 Relids 集合的内存,以避免内存泄漏 pfree(qualscope); } } + /* * make_outerjoininfo * Build a SpecialJoinInfo for the current outer join @@ -869,20 +910,30 @@ void process_security_clause_appendrel(PlannerInfo *root) static SpecialJoinInfo* make_outerjoininfo( PlannerInfo* root, Relids left_rels, Relids right_rels, Relids inner_join_rels, JoinType jointype, List* clause) { + // 创建 SpecialJoinInfo 结构体并分配内存 SpecialJoinInfo* sjinfo = makeNode(SpecialJoinInfo); + + // 用于存储在 JOIN 条件中出现的所有关系的集合 Relids clause_relids; + + // 用于存储在 JOIN 条件中出现的所有关系的集合,其中不包括 INNER JOIN 中的关系 Relids strict_relids; + + // 用于存储左侧关系的最小集合 Relids min_lefthand; + + // 用于存储右侧关系的最小集合 Relids min_righthand; + ListCell* l = NULL; /* - * We should not see RIGHT JOIN here because left/right were switched - * earlier + * 在这里,我们不应该看到 RIGHT JOIN,因为在之前已经交换了左/右关系。 + * 这个断言用于确保不会出现不支持的连接类型。 */ AssertEreport(jointype != JOIN_RIGHT && jointype != JOIN_INNER && jointype != JOIN_RIGHT_ANTI_FULL, MOD_OPT, - "unexpected join type."); + "意外的连接类型。"); /* * Presently the executor cannot support FOR [KEY] UPDATE/SHARE marking of rels @@ -900,35 +951,36 @@ static SpecialJoinInfo* make_outerjoininfo( * list everything. */ foreach (l, root->parse->rowMarks) { - RowMarkClause* rc = (RowMarkClause*)lfirst(l); + RowMarkClause* rc = (RowMarkClause*)lfirst(l); - if (bms_is_member(rc->rti, right_rels) || (jointype == JOIN_FULL && bms_is_member(rc->rti, left_rels))) { - ereport(ERROR, - (errmodule(MOD_OPT), - errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + // 检查是否存在不支持的情况,即在外连接的可空一侧应用SELECT FOR UPDATE/SHARE/NO KEY UPDATE/KEY SHARE + if (bms_is_member(rc->rti, right_rels) || (jointype == JOIN_FULL && bms_is_member(rc->rti, left_rels))) { + ereport(ERROR, + (errmodule(MOD_OPT), + errcode(ERRCODE_FEATURE_NOT_SUPPORTED), #ifndef ENABLE_MULTIPLE_NODES - errmsg("SELECT FOR UPDATE/SHARE/NO KEY UPDATE/KEY SHARE cannot be applied to the nullable side " - "of an outer join"))); + errmsg("SELECT FOR UPDATE/SHARE/NO KEY UPDATE/KEY SHARE 不能应用于外连接的可空一侧"))); #else - errmsg("SELECT FOR UPDATE/SHARE cannot be applied to the nullable side of an outer join"))); + errmsg("SELECT FOR UPDATE/SHARE 不能应用于外连接的可空一侧"))); #endif - } } +} - sjinfo->syn_lefthand = left_rels; - sjinfo->syn_righthand = right_rels; - sjinfo->jointype = jointype; - /* this always starts out false */ - sjinfo->delay_upper_joins = false; - sjinfo->join_quals = clause; +// 设置 SpecialJoinInfo 结构的各个字段 + sjinfo->syn_lefthand = left_rels; // 左侧关系的集合 + sjinfo->syn_righthand = right_rels; // 右侧关系的集合 + sjinfo->jointype = jointype; // 连接类型(INNER JOIN、LEFT JOIN、RIGHT JOIN 等) + sjinfo->delay_upper_joins = false; // 延迟处理上层连接的标志,初始设置为false + sjinfo->join_quals = clause; // 连接条件表达式的列表 + +// 如果是全连接(JOIN_FULL),不需要特别处理,直接设置各个字段并返回 + if (jointype == JOIN_FULL) { + sjinfo->min_lefthand = bms_copy(left_rels); // 复制左侧关系的集合 + sjinfo->min_righthand = bms_copy(right_rels); // 复制右侧关系的集合 + sjinfo->lhs_strict = false; // 不需要考虑此字段,设置为false + return sjinfo; +} - /* If it's a full join, no need to be very smart */ - if (jointype == JOIN_FULL) { - sjinfo->min_lefthand = bms_copy(left_rels); - sjinfo->min_righthand = bms_copy(right_rels); - sjinfo->lhs_strict = false; /* don't care about this */ - return sjinfo; - } /* * Retrieve all relids mentioned within the join clause. @@ -1127,14 +1179,15 @@ void distribute_qual_to_rels(PlannerInfo* root, Node* clause, bool is_deduced, b Index security_level, Relids qualscope, Relids ojscope, Relids outerjoin_nonnullable, Relids deduced_nullable_relids, List **postponed_qual_list) { - Relids relids; - bool is_pushed_down = false; - bool outerjoin_delayed = false; - bool pseudoconstant = false; - bool maybe_equivalence = false; - bool maybe_outer_join = false; - Relids nullable_relids; - RestrictInfo* restrictinfo = NULL; + Relids relids; // 用于存储与当前表达式相关的关系的集合 + bool is_pushed_down = false; // 用于表示表达式是否已被推到下层连接 + bool outerjoin_delayed = false; // 用于表示外连接是否已被延迟处理 + bool pseudoconstant = false; // 用于表示表达式是否是伪常量 + bool maybe_equivalence = false; // 用于表示表达式是否可能是等价表达式 + bool maybe_outer_join = false; // 用于表示表达式是否可能与外连接相关 + Relids nullable_relids; // 用于存储与表达式相关的可空关系的集合 + RestrictInfo* restrictinfo = NULL; // 用于存储约束信息的结构体,表示与表达式相关的约束条件 + /* * Retrieve all relids mentioned within the clause. @@ -1507,18 +1560,18 @@ static bool check_outerjoin_delay(PlannerInfo* root, Relids* relids_p, /* in/out Relids* nullable_relids_p, /* output parameter */ bool is_pushed_down) { - Relids relids; - Relids nullable_relids; - bool outerjoin_delayed = false; - bool found_some = false; + Relids relids; // 用于存储关系集合的副本 + Relids nullable_relids; // 用于存储可空关系集合 + bool outerjoin_delayed = false; // 用于表示外连接是否已被延迟处理 + bool found_some = false; // 用于标记是否找到相关的外连接 - /* fast path if no special joins */ + /* 快速路径:如果没有特殊的连接操作(special joins) */ if (root->join_info_list == NIL) { *nullable_relids_p = NULL; return false; } - /* must copy relids because we need the original value at the end */ + /* 需要复制 relids,因为我们需要在最后保留原始值 */ relids = bms_copy(*relids_p); nullable_relids = NULL; outerjoin_delayed = false; @@ -1529,39 +1582,40 @@ static bool check_outerjoin_delay(PlannerInfo* root, Relids* relids_p, /* in/out foreach (l, root->join_info_list) { SpecialJoinInfo* sjinfo = (SpecialJoinInfo*)lfirst(l); - /* do we reference any nullable rels of this OJ? */ + /* 我们是否引用了这个特殊连接的可空关系? */ if (bms_overlap(relids, sjinfo->min_righthand) || (sjinfo->jointype == JOIN_FULL && bms_overlap(relids, sjinfo->min_lefthand))) { - /* yes; have we included all its rels in relids? */ + /* 是的;我们是否已经包含了所有关系? */ if (!bms_is_subset(sjinfo->min_lefthand, relids) || !bms_is_subset(sjinfo->min_righthand, relids)) { - /* no, so add them in */ + /* 没有,因此添加它们 */ relids = bms_add_members(relids, sjinfo->min_lefthand); relids = bms_add_members(relids, sjinfo->min_righthand); outerjoin_delayed = true; - /* we'll need another iteration */ + /* 我们需要进行另一次迭代 */ found_some = true; } - /* track all the nullable rels of relevant OJs */ + /* 跟踪所有相关特殊连接的可空关系 */ nullable_relids = bms_add_members(nullable_relids, sjinfo->min_righthand); if (sjinfo->jointype == JOIN_FULL) nullable_relids = bms_add_members(nullable_relids, sjinfo->min_lefthand); - /* set delay_upper_joins if needed */ + /* 如果需要,设置 delay_upper_joins */ if (is_pushed_down && sjinfo->jointype != JOIN_FULL && bms_overlap(relids, sjinfo->min_lefthand)) sjinfo->delay_upper_joins = true; } } } while (found_some); - /* identify just the actually-referenced nullable rels */ + /* 仅标识实际引用的可空关系 */ nullable_relids = bms_int_members(nullable_relids, *relids_p); - /* replace *relids_p, and return nullable_relids */ + /* 替换 *relids_p,并返回 nullable_relids */ bms_free_ext(*relids_p); *relids_p = relids; *nullable_relids_p = nullable_relids; return outerjoin_delayed; } + /* * check_equivalence_delay * Detect whether a potential equivalence clause is rendered unsafe @@ -1577,20 +1631,20 @@ static bool check_outerjoin_delay(PlannerInfo* root, Relids* relids_p, /* in/out */ static bool check_equivalence_delay(PlannerInfo* root, RestrictInfo* restrictinfo) { - Relids relids; - Relids nullable_relids; + Relids relids; // 用于存储关系集合 + Relids nullable_relids; // 用于存储可空关系集合 - /* fast path if no special joins */ + /* 快速路径:如果没有特殊的连接操作(special joins),则返回 true */ if (root->join_info_list == NIL) return true; - /* must copy restrictinfo's relids to avoid changing it */ + /* 必须复制 restrictinfo 的 relids 以避免更改它 */ relids = bms_copy(restrictinfo->left_relids); - /* check left side does not need delay */ + /* 检查左侧是否需要延迟 */ if (check_outerjoin_delay(root, &relids, &nullable_relids, true)) return false; - /* and similarly for the right side */ + /* 类似地,检查右侧是否需要延迟 */ relids = bms_copy(restrictinfo->right_relids); if (check_outerjoin_delay(root, &relids, &nullable_relids, true)) return false; @@ -1598,6 +1652,7 @@ static bool check_equivalence_delay(PlannerInfo* root, RestrictInfo* restrictinf return true; } + /* * check_redundant_nullability_qual * Check to see if the qual is an IS NULL qual that is redundant with @@ -1610,30 +1665,30 @@ static bool check_equivalence_delay(PlannerInfo* root, RestrictInfo* restrictinf */ static bool check_redundant_nullability_qual(PlannerInfo* root, Node* clause) { - Var* forced_null_var = NULL; - Index forced_null_rel; + Var* forced_null_var = NULL; // 存储被强制为 NULL 的变量 + Index forced_null_rel; // 存储被强制为 NULL 的关系编号 ListCell* lc = NULL; - /* Check for IS NULL, and identify the Var forced to NULL */ + /* 检查是否为 IS NULL 条件,并确定被强制设置为 NULL 的变量 */ forced_null_var = find_forced_null_var(clause); if (forced_null_var == NULL) - return false; + return false; // 不是 IS NULL 条件,返回 false forced_null_rel = forced_null_var->varno; /* - * If the Var comes from the nullable side of a lower antijoin, the IS - * NULL condition is necessarily true. + * 如果变量来自于较低级别反连接(lower antijoin)的可空侧,IS NULL 条件肯定为真。 */ foreach (lc, root->join_info_list) { SpecialJoinInfo* sjinfo = (SpecialJoinInfo*)lfirst(lc); if (sjinfo->jointype == JOIN_ANTI && bms_is_member(forced_null_rel, sjinfo->syn_righthand)) - return true; + return true; // 变量位于反连接的可空侧,返回 true } - return false; + return false; // 变量不在反连接的可空侧,返回 false } + /* * distribute_restrictinfo_to_rels * Push a completed RestrictInfo into the proper restriction or join @@ -1771,41 +1826,44 @@ void process_implied_equality(PlannerInfo* root, Oid opno, Oid collation, Expr* void process_implied_quality(PlannerInfo* root, Node* node, Relids relids, bool below_outer_join) { - Relids relids_copy = bms_copy(relids); + Relids relids_copy = bms_copy(relids); // 复制关系集合以便处理 ListCell* cell = NULL; RelOptInfo* rel = NULL; int relid = -1; bool found = false; - Assert(BMS_SINGLETON == bms_membership(relids_copy)); + Assert(BMS_SINGLETON == bms_membership(relids_copy)); // 确保只有一个关系 - relid = bms_first_member(relids_copy); - bms_free_ext(relids_copy); + relid = bms_first_member(relids_copy); // 获取唯一的关系编号 + bms_free_ext(relids_copy); // 释放复制的关系集合 if (relid < 0) { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("relid must not be less than zero."))); } - rel = root->simple_rel_array[relid]; + rel = root->simple_rel_array[relid]; // 获取关系信息 - Assert(rel != NULL && rel->reloptkind == RELOPT_BASEREL); + Assert(rel != NULL && rel->reloptkind == RELOPT_BASEREL); // 确保关系存在且为基本关系 + // 遍历基本关系的基本限制信息 foreach (cell, rel->baserestrictinfo) { RestrictInfo* rinfo = (RestrictInfo*)lfirst(cell); + // 检查是否已经存在相同的限制条件 if (equal(node, rinfo->clause)) { found = true; - break; } } if (found) - return; + return; // 如果找到相同的限制条件,则无需处理 + // 将节点添加到关系的限制条件中 distribute_qual_to_rels( root, node, true, below_outer_join, JOIN_INNER, root->qualSecurityLevel, relids, NULL, NULL, NULL, NULL); } + /* * build_implied_join_equality --- build a RestrictInfo for a derived equality * @@ -1872,29 +1930,35 @@ RestrictInfo* build_implied_join_equality( */ static void check_mergejoinable(RestrictInfo* restrictinfo) { - Expr* clause = restrictinfo->clause; + Expr* clause = restrictinfo->clause; // 获取限制条件中的表达式 Oid opno; Node* leftarg = NULL; if (restrictinfo->pseudoconstant) - return; + return; // 如果限制条件是伪常量,则不进行处理 if (!is_opclause(clause)) - return; + return; // 如果限制条件不是操作符表达式,则不进行处理 if (list_length(((OpExpr*)clause)->args) != 2) - return; + return; // 如果操作符表达式不包含两个参数,则不进行处理 - opno = ((OpExpr*)clause)->opno; - leftarg = (Node*)linitial(((OpExpr*)clause)->args); - if (op_mergejoinable(opno, exprType(leftarg)) && !contain_volatile_functions((Node*)clause)) - restrictinfo->mergeopfamilies = get_mergejoin_opfamilies(opno); + opno = ((OpExpr*)clause)->opno; // 获取操作符的OID + leftarg = (Node*)linitial(((OpExpr*)clause)->args); // 获取操作符表达式的第一个参数 - /* + // 检查操作符是否支持合并连接(merge joinable),且表达式不包含易变函数 + if (op_mergejoinable(opno, exprType(leftarg)) && !contain_volatile_functions((Node*)clause)) { + restrictinfo->mergeopfamilies = get_mergejoin_opfamilies(opno); // 获取支持合并连接的操作符族 + } + + /* * Note: op_mergejoinable is just a hint; if we fail to find the operator * in any btree opfamilies, mergeopfamilies remains NIL and so the clause * is not treated as mergejoinable. */ } + + + /* * check_hashjoinable * If the restrictinfo's clause is hashjoinable, set the hashjoin @@ -1906,23 +1970,27 @@ static void check_mergejoinable(RestrictInfo* restrictinfo) */ void check_hashjoinable(RestrictInfo* restrictinfo) { - Expr* clause = restrictinfo->clause; + Expr* clause = restrictinfo->clause; // 获取限制条件中的表达式 Oid opno; Node* leftarg = NULL; if (restrictinfo->pseudoconstant) - return; + return; // 如果限制条件是伪常量,则不进行处理 if (!is_opclause(clause)) - return; + return; // 如果限制条件不是操作符表达式,则不进行处理 if (list_length(((OpExpr*)clause)->args) != 2) - return; + return; // 如果操作符表达式不包含两个参数,则不进行处理 - opno = ((OpExpr*)clause)->opno; - leftarg = (Node*)linitial(((OpExpr*)clause)->args); - if (op_hashjoinable(opno, exprType(leftarg)) && !contain_volatile_functions((Node*)clause)) - restrictinfo->hashjoinoperator = opno; + opno = ((OpExpr*)clause)->opno; // 获取操作符的OID + leftarg = (Node*)linitial(((OpExpr*)clause)->args); // 获取操作符表达式的第一个参数 + + // 检查操作符是否支持哈希连接(hash joinable),且表达式不包含易变函数 + if (op_hashjoinable(opno, exprType(leftarg)) && !contain_volatile_functions((Node*)clause)) { + restrictinfo->hashjoinoperator = opno; // 存储支持哈希连接的操作符的OID + } } + /* check_plan_correlation * check if there's any subplan expr in expr node, and then * set correlated flag for planner infos diff --git a/src/gausskernel/optimizer/plan/pgxcplan_single.cpp b/src/gausskernel/optimizer/plan/pgxcplan_single.cpp old mode 100755 new mode 100644 index 1f454fc17..b97df6add --- a/src/gausskernel/optimizer/plan/pgxcplan_single.cpp +++ b/src/gausskernel/optimizer/plan/pgxcplan_single.cpp @@ -72,38 +72,39 @@ * If Stream is supported, a copy of the 'query' is returned as a backup in case generating a plan * with Stream fails. */ -static Query* check_shippable(bool *stream_unsupport, Query* query, shipping_context* context) +static Query* check_shippable(bool *stream_unsupport, Query* query, shipping_context* context)//函数用于检查查询是否支持流传输 { + // 根据配置选择部分或全局推送的模式,设置 stream_unsupport 标志 if (u_sess->attr.attr_sql.rewrite_rule & PARTIAL_PUSH) { *stream_unsupport = !context->query_shippable; } else { *stream_unsupport = !context->global_shippable; } + // 根据是否启用 DN-Gather,设置 is_dngather_support 标志 if (u_sess->attr.attr_sql.enable_dngather) { u_sess->opt_cxt.is_dngather_support = !context->disable_dn_gather; } else { u_sess->opt_cxt.is_dngather_support = false; } - /* single node do not support parallel query in cursor */ + // 如果查询包含声明游标操作,则强制 stream_unsupport 为 true if (query->utilityStmt && IsA(query->utilityStmt, DeclareCursorStmt)) { *stream_unsupport = true; } + // 如果流传输不支持或不是流查询(非并行查询),则输出不支持流传输的日志,并关闭流传输 if (*stream_unsupport || !IS_STREAM) { output_unshipped_log(); set_stream_off(); } else { - /* - * make a copy of query, so we can retry to create an unshippable plan - * when we fail to generate a stream plan - */ + // 在流传输可行的情况下,复制查询,以备后续重试创建不可传输的计划 return (Query*)copyObject(query); } return NULL; } + PlannedStmt* pgxc_planner(Query* query, int cursorOptions, ParamListInfo boundParams) { PlannedStmt* result = NULL; @@ -288,63 +289,84 @@ PlannedStmt* pgxc_planner(Query* query, int cursorOptions, ParamListInfo boundPa void stream_walker_context_init(shipping_context *context) { + // 使用 memset_s 初始化 shipping_context 结构体,将所有字段置为0 errno_t rc = EOK; - rc = memset_s(context, sizeof(shipping_context), 0, sizeof(shipping_context)); securec_check(rc, "\0", "\0"); + // 根据配置和流查询计划的标志,设置是否支持随机函数的标志 context->is_randomfunc_shippable = u_sess->opt_cxt.is_randomfunc_shippable && IS_STREAM_PLAN; + // 设置支持 EC 函数的标志为 true context->is_ecfunc_shippable = true; + // 初始化查询列表为一个空列表 context->query_list = NIL; + // 初始化查询计数为0 context->query_count = 0; + // 初始化当前查询是否支持流传输的标志为 true context->current_shippable = true; + // 初始化整体查询是否支持流传输的标志为 true context->query_shippable = true; + // 初始化全局查询是否支持流传输的标志为 true context->global_shippable = true; } + /* * Returns true if at least one temporary table is in use * in query (and its subqueries) */ bool contains_column_tables(List* rtable) { + // 此函数暂时不支持分布式特性,因此输出一个不支持分布式的宏 DISTRIBUTED_FEATURE_NOT_SUPPORTED(); + ListCell* item = NULL; + // 遍历关系表中的每个条目 foreach (item, rtable) { RangeTblEntry* rte = (RangeTblEntry*)lfirst(item); + // 如果条目的类型是关系表(RTE_RELATION) if (rte->rtekind == RTE_RELATION) { + // 检查关系表的存储方向是否为列存储或PAX存储 if (REL_COL_ORIENTED == rte->orientation || REL_PAX_ORIENTED == rte->orientation) - return true; - } else if (rte->rtekind == RTE_SUBQUERY && contains_column_tables(rte->subquery->rtable)) - return true; + return true; // 如果是列存储或PAX存储,返回true + } + // 如果条目的类型是子查询(RTE_SUBQUERY),则递归调用此函数检查子查询的关系表 + else if (rte->rtekind == RTE_SUBQUERY && contains_column_tables(rte->subquery->rtable)) + return true; // 如果子查询中包含列存储或PAX存储的表,返回true } + // 如果没有找到包含列存储或PAX存储的表的情况,返回false return false; } + List* AddRemoteQueryNode(List* stmts, const char* queryString, RemoteQueryExecType remoteExecType, bool is_temp) { List* result = stmts; - /* If node is appplied on EXEC_ON_NONE, simply return the list unchanged */ + /* 如果远程执行类型是EXEC_ON_NONE,直接返回未更改的列表 */ if (remoteExecType == EXEC_ON_NONE) return result; - /* Only a remote Coordinator is allowed to send a query to backend nodes */ + /* 只有远程协调节点(IS_PGXC_COORDINATOR)才能向后端节点发送查询 */ if (IS_PGXC_COORDINATOR && !IsConnFromCoord()) { + // 创建一个RemoteQuery节点用于表示远程查询 RemoteQuery* step = makeNode(RemoteQuery); - step->combine_type = COMBINE_TYPE_SAME; - step->sql_statement = (char*)queryString; - step->exec_type = remoteExecType; - step->is_temp = is_temp; + step->combine_type = COMBINE_TYPE_SAME; // 查询结果的合并方式 + step->sql_statement = (char*)queryString; // 查询字符串 + step->exec_type = remoteExecType; // 远程执行的类型 + step->is_temp = is_temp; // 是否是临时表 + + // 将RemoteQuery节点添加到查询计划列表中 result = lappend(result, step); } return result; } + bool pgxc_query_contains_temp_tables(List* queries) { DISTRIBUTED_FEATURE_NOT_SUPPORTED(); @@ -364,34 +386,44 @@ void pgxc_rqplan_adjust_tlist(PlannerInfo* root, RemoteQuery* rqplan, bool gensq bool containing_ordinary_table(Node* node) { + // 如果节点为空或者当前节点是PGXC数据节点,直接返回false if (node == NULL || IS_PGXC_DATANODE) { return false; } + // 如果当前节点是RangeTblEntry类型 if (IsA(node, RangeTblEntry)) { RangeTblEntry* rte = (RangeTblEntry*)node; + // 如果是普通表且不是系统表,返回true if (rte->relkind == RELKIND_RELATION && !is_sys_table(rte->relid)) { return true; - } else if (rte->rtekind == RTE_SUBQUERY) { + } + // 如果是子查询(RTE_SUBQUERY),递归检查子查询的节点 + else if (rte->rtekind == RTE_SUBQUERY) { Query* subquery = rte->subquery; + // 如果子查询中包含普通表,返回true if (containing_ordinary_table((Node*)subquery)) { return true; } } + // 其他情况返回false return false; } + // 如果当前节点是Query类型,使用query_tree_walker遍历查询计划树 if (IsA(node, Query)) { bool result = false; result = query_tree_walker((Query*)node, (bool (*)())containing_ordinary_table, NULL, QTW_EXAMINE_RTES); return result; } + // 对于其他类型的节点,使用expression_tree_walker进行遍历 return expression_tree_walker(node, (bool (*)())containing_ordinary_table, NULL); } + Plan* pgxc_make_modifytable(PlannerInfo* root, Plan* topplan) { ModifyTable* mt = (ModifyTable*)topplan; @@ -445,20 +477,29 @@ bool contains_temp_tables(List* rtable) ListCell* item = NULL; char rel_persistence; + // 遍历查询的表列表(RangeTblEntry) foreach (item, rtable) { RangeTblEntry* rte = (RangeTblEntry*)lfirst(item); + // 如果当前表是普通关系(RTE_RELATION) if (rte->rtekind == RTE_RELATION) { + // 获取关系的持久性信息 rel_persistence = get_rel_persistence(rte->relid); + + // 如果关系的持久性为临时表(RELPERSISTENCE_TEMP)或全局临时表(RELPERSISTENCE_GLOBAL_TEMP),返回true if (rel_persistence == RELPERSISTENCE_TEMP || rel_persistence == RELPERSISTENCE_GLOBAL_TEMP) return true; - } else if (rte->rtekind == RTE_SUBQUERY && contains_temp_tables(rte->subquery->rtable)) + } + // 如果当前表是子查询(RTE_SUBQUERY),递归检查子查询的表列表 + else if (rte->rtekind == RTE_SUBQUERY && contains_temp_tables(rte->subquery->rtable)) return true; } + // 如果遍历完所有表都没有找到临时表,返回false return false; } + Param* pgxc_make_param(int param_num, Oid param_type) { DISTRIBUTED_FEATURE_NOT_SUPPORTED(); diff --git a/src/gausskernel/optimizer/plan/planagg.cpp b/src/gausskernel/optimizer/plan/planagg.cpp index 88df15c81..e26984566 100644 --- a/src/gausskernel/optimizer/plan/planagg.cpp +++ b/src/gausskernel/optimizer/plan/planagg.cpp @@ -73,54 +73,50 @@ static Oid fetch_agg_sort_op(Oid aggfnoid); * Note: we are passed the preprocessed targetlist separately, because it's * not necessarily equal to root->parse->targetList. */ +/* + * preprocess_minmax_aggregates: 预处理查询中的MIN/MAX聚合函数。 + 此函数负责识别和准备查询中的MIN/MAX聚合函数,以便稍后进行优化。 + 它检查各种条件,如查询是否涉及联接、GROUP BY 或窗口函数,并确保查询仅引用一个表。 + 如果满足所有条件且聚合函数适合进行优化,它会���建访问路径并检查其是否可索引。如果所有聚合函数都可索引, + 它会保存信息以供稍后进行优化 + * 输入参数: + * - root:包含有关查询的信息的PlannerInfo结构。 + * - tlist:查询的目标列表。 + */ void preprocess_minmax_aggregates(PlannerInfo* root, List* tlist) { - Query* parse = root->parse; - FromExpr* jtnode = NULL; - RangeTblRef* rtr = NULL; - RangeTblEntry* rte = NULL; - List* aggs_list = NIL; - ListCell* lc = NULL; + Query* parse = root->parse; // 指向正在计划的查询的指针。 + FromExpr* jtnode = NULL; // 指向查询的FROM子句。 + RangeTblRef* rtr = NULL; // 引用范围表条目的引用。 + RangeTblEntry* rte = NULL; // 范围表条目本身。 + List* aggs_list = NIL; // 查询中的MIN/MAX聚合函数列表。 + ListCell* lc = NULL; // 列表单元迭代器。 - /* minmax_aggs list should be empty at this point */ + /* 验证minmax_aggs列表最初为空。 */ AssertEreport( - root->minmax_aggs == NIL, MOD_OPT, "The minmax_aggs is not empty when preprocessing MIN/MAX aggregates."); + root->minmax_aggs == NIL, MOD_OPT, "在预处理MIN/MAX聚合函数时,minmax_aggs不为空。"); - /* Nothing to do if query has no aggregates */ + /* 如果查询没有聚合函数,就没有什么可做的。 */ if (!parse->hasAggs) return; + /* 检查查询是否涉及集合操作或行级锁定。 */ AssertEreport(!parse->setOperations, MOD_OPT, - "setOp is not allowed when preprocessing MIN/MAX aggregates."); /* shouldn't get here if a setop */ + "在预处理MIN/MAX聚合函数时不允许setOp。"); /* 如果有set操作,不应该到达这里 */ AssertEreport(parse->rowMarks == NIL, MOD_OPT, - "RowMarkClause is not allowd when preprocessing MIN/MAX aggregates."); /* nor if FOR UPDATE */ + "在预处理MIN/MAX聚合函数时不允许RowMarkClause。"); /* 也不允许使用FOR UPDATE */ /* - * Reject unoptimizable cases. - * - * We don't handle GROUP BY or windowing, because our current - * implementations of grouping require looking at all the rows anyway, and - * so there's not much point in optimizing MIN/MAX. (Note: relaxing this - * would likely require some restructuring in grouping_planner(), since it - * performs assorted processing related to these features between calling - * preprocess_minmax_aggregates and optimize_minmax_aggregates.) - * - * For example group by grouping sets(()); parse->groupClause is null and - * the length of parse->groupingSets is 1. In this case, min/max may optimize. + * 拒绝无法优化的情况。 + * 我们不处理GROUP BY或窗口函数,因为它们需要处理所有行并使MIN/MAX优化效果不佳。 */ if (parse->groupClause || parse->hasWindowFuncs || list_length(parse->groupingSets) > 1) return; /* - * We also restrict the query to reference exactly one table, since join - * conditions can't be handled reasonably. (We could perhaps handle a - * query containing cartesian-product joins, but it hardly seems worth the - * trouble.) However, the single table could be buried in several levels - * of FromExpr due to subqueries. Note the "single" table could be an - * inheritance parent, too, including the case of a UNION ALL subquery - * that's been flattened to an appendrel. + * 确保查询引用了正好一个表。我们无法合理处理联接条件。 */ jtnode = parse->jointree; while (IsA(jtnode, FromExpr)) { @@ -132,16 +128,17 @@ void preprocess_minmax_aggregates(PlannerInfo* root, List* tlist) return; rtr = (RangeTblRef*)jtnode; rte = planner_rt_fetch(rtr->rtindex, root); + + // 检查表是普通关系或UNION ALL子查询。 if (rte->rtekind == RTE_RELATION) - /* ordinary relation, ok */; + /* 普通关系,可以优化 */; else if (rte->rtekind == RTE_SUBQUERY && rte->inh) - /* flattened UNION ALL subquery, ok */; + /* 展平的UNION ALL子查询,可以优化 */; else return; /* - * Scan the tlist and HAVING qual to find all the aggregates and verify - * all are MIN/MAX aggregates. Stop as soon as we find one that isn't. + * 扫描目标列表(tlist)和HAVING条件,查找所有聚合函数并验证它们是否都是MIN/MAX聚合函数。一旦发现不是MIN/MAX聚合函数的情况,就停止。 */ aggs_list = NIL; if (find_minmax_aggs_walker((Node*)tlist, &aggs_list)) @@ -150,12 +147,9 @@ void preprocess_minmax_aggregates(PlannerInfo* root, List* tlist) return; /* - * OK, there is at least the possibility of performing the optimization. - * Build an access path for each aggregate. (We must do this now because - * we need to call query_planner with a pristine copy of the current query - * tree; it'll be too late when optimize_minmax_aggregates gets called.) - * If any of the aggregates prove to be non-indexable, give up; there is - * no point in optimizing just some of them. + * 现在,有可能执行优化。 + * 为每个聚合函数构建一个访问路径,并检查它们是否可索引。 + * 如果有任何一个聚合函数无法索引,就放弃。 */ foreach (lc, aggs_list) { MinMaxAggInfo* mminfo = (MinMaxAggInfo*)lfirst(lc); @@ -163,37 +157,31 @@ void preprocess_minmax_aggregates(PlannerInfo* root, List* tlist) bool reverse = false; /* - * We'll need the equality operator that goes with the aggregate's - * ordering operator. + * 查找与聚合函数的排序运算符对应的相等运算符。 */ eqop = get_equality_op_for_ordering_op(mminfo->aggsortop, &reverse); - if (!OidIsValid(eqop)) /* shouldn't happen */ + if (!OidIsValid(eqop)) /* 不应该发生这种情况。 */ ereport(ERROR, (errmodule(MOD_OPT), errcode(ERRCODE_INVALID_OPERATION), - (errmsg("could not find equality operator for ordering operator %u", mminfo->aggsortop)))); + (errmsg("无法找到排序运算符 %u 的相等运算符", mminfo->aggsortop)))); /* - * We can use either an ordering that gives NULLS FIRST or one that - * gives NULLS LAST; furthermore there's unlikely to be much - * performance difference between them, so it doesn't seem worth - * costing out both ways if we get a hit on the first one. NULLS - * FIRST is more likely to be available if the operator is a - * reverse-sort operator, so try that first if reverse. + * 我们可以使用给出NULLS FIRST或NULLS LAST的排序方式;尝试两种方式,如果找到可索引的路径,则继续。 */ if (build_minmax_path(root, mminfo, eqop, mminfo->aggsortop, reverse)) continue; if (build_minmax_path(root, mminfo, eqop, mminfo->aggsortop, !reverse)) continue; - /* No indexable path for this aggregate, so fail */ + /* 无法为此聚合函数构建可索引路径,因此失败。 */ return; + } /* - * We're done until path generation is complete. Save info for later. - * (Setting root->minmax_aggs non-NIL signals we succeeded in making index - * access paths for all the aggregates.) + * 在路径生成完成之前,我们已经完成了。保存信息以供以后使用。 + * 设置root->minmax_aggs为非空表示我们成功地为所有聚合函数创建了索引访问路径。 */ root->minmax_aggs = aggs_list; } @@ -214,18 +202,32 @@ void preprocess_minmax_aggregates(PlannerInfo* root, List* tlist) * doing the aggregates the regular way, and the best path devised for * computing the input of a standard Agg node. */ +/* + * optimize_minmax_aggregates: 优化MIN/MAX聚合函数的执行计划。 + * + * 此函数负责生成优化的执行计划,以处理查询中的MIN/MAX聚合函数。它与通用的聚合函数执行计划进行成本比较,如果优化的计划成本较低,则生成并返回优化的执行计划。 + * + * 输入参数: + * - root:PlannerInfo结构,包含查询的信息。 + * - tlist:查询的目标列表。 + * - aggcosts:聚合函数的成本信息。 + * - best_path:查询的最佳执行计划。 + * + * 返回值: + * 如果无法生成优化的执行计划,返回NULL;否则返回优化的执行计划。 + */ Plan* optimize_minmax_aggregates(PlannerInfo* root, List* tlist, const AggClauseCosts* aggcosts, Path* best_path) { Query* parse = root->parse; - Cost total_cost; - Path agg_path; - Plan* plan = NULL; - Plan* sub_plan = NULL; - Node* hqual = NULL; + Cost total_cost; // 总成本 + Path agg_path; // 聚合函数的路径 + Plan* plan = NULL; // 最终执行计划 + Plan* sub_plan = NULL; // 子查询的执行计划 + Node* hqual = NULL; // HAVING条件 ListCell* lc = NULL; errno_t rc; - /* Nothing to do if preprocess_minmax_aggs rejected the query */ + /* 如果preprocess_minmax_aggs拒绝了查询,则无需执行任何操作 */ if (root->minmax_aggs == NIL) return NULL; @@ -233,12 +235,9 @@ Plan* optimize_minmax_aggregates(PlannerInfo* root, List* tlist, const AggClause securec_check(rc, "\0", "\0"); /* - * Now we have enough info to compare costs against the generic aggregate - * implementation. + * 现在,我们有足够的信息来比较成本与通用聚合函数实现。 * - * Note that we don't include evaluation cost of the tlist here; this is - * OK since it isn't included in best_path's cost either, and should be - * the same in either case. + * 请注意,我们在这里不包括目标列表(tlist)的评估成本;这是可以接受的,因为在best_path的成本中也没有包括它,而且无论哪种情况下,它应该是一样的。 */ total_cost = 0; foreach (lc, root->minmax_aggs) { @@ -260,12 +259,12 @@ Plan* optimize_minmax_aggregates(PlannerInfo* root, List* tlist, const AggClause RELOPTINFO_LOCAL_FIELD(root, best_path->parent, rows)); if (total_cost > agg_path.total_cost) - return NULL; /* too expensive */ + return NULL; /* 成本太高,无法优化 */ /* - * OK, we are going to generate an optimized plan. + * 好的,我们要生成一个优化的执行计划。 * - * First, generate a subplan and output Param node for each agg. + * 首先,为每个聚合函数生成子查询和输出Param节点。 */ foreach (lc, root->minmax_aggs) { MinMaxAggInfo* mminfo = (MinMaxAggInfo*)lfirst(lc); @@ -273,26 +272,21 @@ Plan* optimize_minmax_aggregates(PlannerInfo* root, List* tlist, const AggClause } /* - * Modify the targetlist and HAVING qual to reference subquery outputs + * 修改目标列表和HAVING条件���以引用子查询的输出。 */ tlist = (List*)replace_aggs_with_params_mutator((Node*)tlist, root); hqual = replace_aggs_with_params_mutator(parse->havingQual, root); /* - * We have to replace Aggrefs with Params in equivalence classes too, else - * ORDER BY or DISTINCT on an optimized aggregate will fail. We don't - * need to process child eclass members though, since they aren't of - * interest anymore --- and replace_aggs_with_params_mutator isn't able - * to handle Aggrefs containing translated child Vars, anyway. + * 我们必须在等价类中用Param替换Aggrefs,否则优化的聚合函数上的ORDER BY或DISTINCT将失败。 + * 我们不需要处理子等价类成员,因为它们不再感兴趣,而且replace_aggs_with_params_mutator也不能处理包含已翻译子Vars的Aggrefs。 * - * Note: at some point it might become necessary to mutate other data - * structures too, such as the query's sortClause or distinctClause. Right - * now, those won't be examined after this point. + * 注意:在某些时候,可能需要变异其他数据结构,如查询的sortClause或distinctClause。现在,这些数据在此之后不会再被检查。 */ mutate_eclass_expressions(root, (Node* (*)()) replace_aggs_with_params_mutator, (void*)root, false); /* - * Generate the output plan --- basically just a Result + * 生成输出计划 - 基本上只是一个Result节点 */ plan = (Plan*)make_result(root, tlist, hqual, NULL); @@ -302,12 +296,13 @@ Plan* optimize_minmax_aggregates(PlannerInfo* root, List* tlist, const AggClause plan->exec_nodes = ng_get_default_computing_group_exec_node(); } - /* Account for evaluation cost of the tlist (make_result did the rest) */ + /* 账户tlist的评估成本(make_result已经做了其余的工作) */ add_tlist_costs_to_plan(root, plan, tlist); return plan; } + /* * find_minmax_aggs_walker * Recursively scan the Aggref nodes in an expression tree, and check @@ -325,6 +320,23 @@ Plan* optimize_minmax_aggregates(PlannerInfo* root, List* tlist, const AggClause * reduction of sublinks to subplans. There mustn't be outer-aggregate * references either. */ +/* + * find_minmax_aggs_walker: 递归查找表达式树中的MIN/MAX聚合函数 + * + * 此函数用于递归地遍历表达式树,查找其中的MIN/MAX聚合函数。如果找到MIN/MAX聚合函数, + * 将其相关信息添加到上下文列表中以供后续处理。 + * 这个函数用于递归遍历查询表达式树,查找其中的MIN/MAX聚合函数。 + * 如果在表达式树中找到MIN/MAX聚合函数,它会提取相关信息并将其添加到上下文列表中以供后续处理。 + * 该函数通过检查每个节点的类型来判断是否是MIN/MAX聚合函数,并进行相应的处理。 + * 如果找到了MIN/MAX聚合函数,它会返回true,否则返回false + * + * 输入参数: + * - node: 当前节点 + * - context: 保存MIN/MAX聚合函数信息的上下文列表 + * + * 返回值: + * 如果在表达式树中找到MIN/MAX聚合函数,则返回true;否则返回false。 + */ static bool find_minmax_aggs_walker(Node* node, List** context) { if (node == NULL) @@ -341,22 +353,22 @@ static bool find_minmax_aggs_walker(Node* node, List** context) "The agg does not belong to current query" "when scaning the Aggref nodes in an expression tree recursively to find a MIN/MAX aggregate."); if (list_length(aggref->args) != 1 || aggref->aggorder != NIL) - return true; /* it couldn't be MIN/MAX */ - /* note: we do not care if DISTINCT is mentioned ... */ + return true; /* 它不可能是MIN/MAX聚合函数 */ + /* 注意:我们不关心是否提到了 DISTINCT ... */ curTarget = (TargetEntry*)linitial(aggref->args); aggsortop = fetch_agg_sort_op(aggref->aggfnoid); if (!OidIsValid(aggsortop)) - return true; /* not a MIN/MAX aggregate */ + return true; /* 不是MIN/MAX聚合函数 */ if (contain_mutable_functions((Node*)curTarget->expr)) - return true; /* not potentially indexable */ + return true; /* 不是潜在可索引的 */ if (type_is_rowtype(exprType((Node*)curTarget->expr))) - return true; /* IS NOT NULL would have weird semantics */ + return true; /* IS NOT NULL会有奇怪的语义 */ /* - * Check whether it's already in the list, and add it if not. + * 检查它是否已在列表中,如果不在,则添加它。 */ foreach (l, *context) { mminfo = (MinMaxAggInfo*)lfirst(l); @@ -369,7 +381,7 @@ static bool find_minmax_aggs_walker(Node* node, List** context) mminfo->aggsortop = aggsortop; mminfo->aggref = aggref; mminfo->target = curTarget->expr; - mminfo->subroot = NULL; /* don't compute path yet */ + mminfo->subroot = NULL; /* 暂不计算路径 */ mminfo->path = NULL; mminfo->pathcost = 0; mminfo->param = NULL; @@ -377,8 +389,7 @@ static bool find_minmax_aggs_walker(Node* node, List** context) *context = lappend(*context, mminfo); /* - * We need not recurse into the argument, since it can't contain any - * aggregates. + * 由于参数中不可能包含任何聚合函数,因此我们不需要递归到参数中。 */ return false; } @@ -388,33 +399,52 @@ static bool find_minmax_aggs_walker(Node* node, List** context) return expression_tree_walker(node, (bool (*)())find_minmax_aggs_walker, (void*)context); } + +/* + * HasNOTNULLConstraint: 检查是否存在NOT NULL约束 + * + * 此函数用于检查是否存在表达式中的变量(Var)对应的列具有NOT NULL约束。 + * 如果变量对应的列具有NOT NULL约束,则返回true,否则返回false。 + * + * 输入参数: + * - parse: 包含查询信息的Query结构 + * - ntest: NullTest节点,表示NULL检查操作 + * + * 返回值: + * 如果变量对应的列具有NOT NULL约束,返回true;否则返回false。 + */ static bool HasNOTNULLConstraint(Query* parse, NullTest* ntest) { if (IsA(ntest->arg, Var)) { - /* Check whether NOT NULL check can be guaranteed by table defination */ + /* 检查是否可以通过表定义保证NOT NULL检查 */ RangeTblEntry* rte = rt_fetch(((Var*)ntest->arg)->varno, parse->rtable); Oid reloid = rte->relid; AttrNumber attno = ((Var*)ntest->arg)->varoattno; + if (reloid != InvalidOid && attno != InvalidAttrNumber) { HeapTuple atttuple = SearchSysCacheCopy2(ATTNUM, ObjectIdGetDatum(reloid), Int16GetDatum(attno)); if (!HeapTupleIsValid(atttuple)) { + /* 查找缓存失败时报错 */ ereport(ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED), - errmsg("cache lookup failed for attribute %u of relation %hd", - attno, reloid))); + errmsg("为关系 %hd 的属性 %u 查找缓存失败", + reloid, attno))); } Form_pg_attribute attStruct = (Form_pg_attribute)GETSTRUCT(atttuple); if (attStruct->attnotnull) { + /* 如果属性具有NOT NULL约束,则返回true */ heap_freetuple_ext(atttuple); return true; } heap_freetuple_ext(atttuple); } } + /* 变量对应的列没有NOT NULL约束 */ return false; } + /* * build_minmax_path * Given a MIN/MAX aggregate, try to build an indexscan Path it can be @@ -423,6 +453,22 @@ static bool HasNOTNULLConstraint(Query* parse, NullTest* ntest) * If successful, stash the best path in *mminfo and return TRUE. * Otherwise, return FALSE. */ +/* + * build_minmax_path: 为MIN/MAX聚合生成路径 + * + * 此函数生成适用于MIN/MAX聚合的查询路径,以便在查询优化中使用。 + * 生成的查询路径包括对聚合目标列的排序和NULL值检查。 + * + * 输入参数: + * - root: 查询规划器的上下文信息 + * - mminfo: MIN/MAX聚合的相关信息 + * - eqop: 相等性运算符的OID + * - sortop: 排序运算符的OID + * - nulls_first: 是否将NULL值排在前面 + * + * 返回值: + * 如果成功生成路径,则返回true;否则返回false。 + */ static bool build_minmax_path(PlannerInfo* root, MinMaxAggInfo* mminfo, Oid eqop, Oid sortop, bool nulls_first) { PlannerInfo* subroot = NULL; @@ -440,34 +486,41 @@ static bool build_minmax_path(PlannerInfo* root, MinMaxAggInfo* mminfo, Oid eqop standard_qp_extra qp_extra; /* ---------- - * Generate modified query of the form - * (SELECT col FROM tab - * WHERE col IS NOT NULL AND existing-quals - * ORDER BY col ASC/DESC - * LIMIT 1) + * 生成修改后的查询,形式如下: + * (SELECT col FROM tab + * WHERE col IS NOT NULL AND existing-quals + * ORDER BY col ASC/DESC + * LIMIT 1) * ---------- */ + + /* 复制根查询规划器上下文 */ subroot = (PlannerInfo*)palloc(sizeof(PlannerInfo)); errorno = memcpy_s(subroot, sizeof(PlannerInfo), root, sizeof(PlannerInfo)); securec_check_c(errorno, "\0", "\0"); + + /* 复制根查询的查询结构 */ subroot->parse = parse = (Query*)copyObject(root->parse); - /* make sure subroot planning won't change root->init_plans contents */ + + /* 确保子查询规划不会更改根查询的 init_plans 内容 */ subroot->init_plans = list_copy(root->init_plans); - /* There shouldn't be any OJ info to translate, as yet */ + + /* 没有联接信息需要转换 */ AssertEreport(subroot->join_info_list == NIL, MOD_OPT, "join info list is not null when building an index path at a given MIN/MAX aggregate."); Assert(subroot->lateral_info_list == NIL); - /* and we haven't created PlaceHolderInfos, either */ + + /* 也没有创建占位符信息 */ AssertEreport(subroot->placeholder_list == NIL, MOD_OPT, "place holder list is not null when building an index path at a given MIN/MAX aggregate."); - /* single tlist entry that is the aggregate target */ + /* 创建一个单一的目标条目,对应于聚合目标 */ tle = makeTargetEntry((Expr*)copyObject(mminfo->target), (AttrNumber)1, pstrdup("agg_target"), false); parse->targetList = list_make1(tle); - /* No HAVING, no DISTINCT, no aggregates, no grouping sets anymore */ + /* 没有HAVING,没有DISTINCT,没有聚合函数,没有分组集合 */ parse->havingQual = NULL; subroot->hasHavingQual = false; parse->distinctClause = NIL; @@ -475,70 +528,60 @@ static bool build_minmax_path(PlannerInfo* root, MinMaxAggInfo* mminfo, Oid eqop parse->hasAggs = false; parse->groupingSets = NULL; - /* Build "target IS NOT NULL" expression */ + /* 构建 "target IS NOT NULL" 表达式 */ ntest = makeNode(NullTest); ntest->nulltesttype = IS_NOT_NULL; ntest->arg = (Expr*)copyObject(mminfo->target); - /* we checked it wasn't a rowtype in find_minmax_aggs_walker */ + /* 我们已经在 find_minmax_aggs_walker 中检查过它不是复合类型 */ ntest->argisrow = false; - /* User might have had that in WHERE already */ + /* 如果WHERE子句中没有 "target IS NOT NULL" 条件,则添加它 */ if (!list_member((List*)parse->jointree->quals, ntest) && !HasNOTNULLConstraint(parse, ntest)) parse->jointree->quals = (Node*)lcons(ntest, (List*)parse->jointree->quals); - /* Build suitable ORDER BY clause */ + /* 创建适当的ORDER BY 子句 */ sortcl = makeNode(SortGroupClause); sortcl->tleSortGroupRef = assignSortGroupRef(tle, parse->targetList); sortcl->eqop = eqop; sortcl->sortop = sortop; sortcl->nulls_first = nulls_first; - sortcl->hashable = false; /* no need to make this accurate */ + sortcl->hashable = false; /* 不需要精确计算哈希值 */ parse->sortClause = list_make1(sortcl); - /* set up expressions for LIMIT 1 */ + /* 设置 LIMIT 1 的表达式 */ parse->limitOffset = NULL; parse->limitCount = (Node*)makeConst(INT8OID, -1, InvalidOid, sizeof(int64), Int64GetDatum(1), false, FLOAT8PASSBYVAL); - /* Initialize the pathkeys */ + /* 初始化路径键列表 */ standard_qp_init(subroot, &qp_extra, parse->targetList, NULL, NULL); /* - * Generate the best paths for this query, telling query_planner that we - * have LIMIT 1. + * 为此查询生成最佳路径,告知 query_planner 我们有 LIMIT 1。 */ - /* Make tuple_fraction, limit_tuples accessible to lower-level routines */ + /* 让 tuple_fraction 和 limit_tuples 可访问到更低级别的子例程 */ subroot->tuple_fraction = 1.0; subroot->limit_tuples = 1.0; - /* - * Generate pathlist by query_planner for final_rel and canonicalize - * all the pathkeys. - */ + /* 生成 final_rel 的路径列表,并对路径键进行规范化 */ final_rel = query_planner(subroot, parse->targetList, standard_qp_callback, &qp_extra); - /* - * In the following, generate the best unsorted and presorted paths for - * this Query (but note there may not be any presorted path). - */ + /* 生成此查询的最佳未排序路径和排序路径(注意可能没有排序路径) */ bool has_groupby = true; - /* First of all, estimate the number of groups in the query. */ + /* 首先估算查询中的组数 */ has_groupby = get_number_of_groups(subroot, final_rel, dNumGroups); - /* Then update the tuple_fraction by the number of groups in the query. */ + /* 然后根据查询中的组数更新 tuple_fraction */ update_tuple_fraction(subroot, final_rel, dNumGroups); - /* - * Finally, generate the best unsorted and presorted paths for - * this Query. - */ + /* 最后生成此查询的最佳未排序和排序路径 */ generate_cheapest_and_sorted_path(subroot, final_rel, &cheapest_path, @@ -546,12 +589,10 @@ static bool build_minmax_path(PlannerInfo* root, MinMaxAggInfo* mminfo, Oid eqop dNumGroups, has_groupby); - /* - * Fail if no presorted path. However, if query_planner determines that - * the presorted path is also the cheapest, it will set sorted_path to - * NULL ... don't be fooled. (This is kind of a pain here, but it - * simplifies life for grouping_planner, so leave it be.) + * 如果没有排序路径,则返回失败。但是,如果 query_planner 确定排序路径也是最便宜的, + * 它将将 sorted_path 设置为 NULL ... 不要被愚弄。(这在这里有点麻烦, + * 但它简化了 grouping_planner 的工作,所以保留它。) */ if (sorted_path == NULL) { if (cheapest_path && pathkeys_contained_in(subroot->sort_pathkeys, cheapest_path->pathkeys)) @@ -561,10 +602,9 @@ static bool build_minmax_path(PlannerInfo* root, MinMaxAggInfo* mminfo, Oid eqop } /* - * Determine cost to get just the first row of the presorted path. + * 确定获取排序路径的第一行的成本。 * - * Note: cost calculation here should - * match compare_fractional_path_costs(). + * 注意:此处的成本计算应与 compare_fractional_path_costs() 相匹配。 */ if (RELOPTINFO_LOCAL_FIELD(subroot, sorted_path->parent, rows) > 1.0) path_fraction = 1.0 / RELOPTINFO_LOCAL_FIELD(subroot, sorted_path->parent, rows); @@ -573,7 +613,7 @@ static bool build_minmax_path(PlannerInfo* root, MinMaxAggInfo* mminfo, Oid eqop path_cost = sorted_path->startup_cost + path_fraction * (sorted_path->total_cost - sorted_path->startup_cost); - /* Save state for further processing */ + /* 保存状态以供进一步处理 */ mminfo->subroot = subroot; mminfo->path = sorted_path; mminfo->pathcost = path_cost; @@ -581,9 +621,23 @@ static bool build_minmax_path(PlannerInfo* root, MinMaxAggInfo* mminfo, Oid eqop return true; } + /* * Construct a suitable plan for a converted aggregate query */ +/* + * make_agg_subplan: 生成聚合子查询计划 + * + * 此函数生成用于MIN/MAX聚合的子查询计划,将该计划转换为InitPlan,并为其结果创建一个参数。 + * 该子查询计划执行对目标列的排序和限制以获得MIN/MAX值。 + * + * 输入参数: + * - root: 查询规划器的上下文信息 + * - mminfo: MIN/MAX聚合的相关信息 + * + * 返回值: + * 返回生成的子查询计划。 + */ static Plan* make_agg_subplan(PlannerInfo* root, MinMaxAggInfo* mminfo) { PlannerInfo* subroot = mminfo->subroot; @@ -591,14 +645,13 @@ static Plan* make_agg_subplan(PlannerInfo* root, MinMaxAggInfo* mminfo) Plan* plan = NULL; /* - * Generate the plan for the subquery. We already have a Path, but we have - * to convert it to a Plan and attach a LIMIT node above it. + * 生成子查询的计划。我们已经有一个路径,但我们需要将其转换为计划并在其上方添加一个LIMIT节点。 */ plan = create_plan(subroot, mminfo->path); plan->targetlist = subparse->targetList; - /* For partition table, we should pass targetlist down to base table scan */ + /* 对于分区表,应将目标列表传递给基表扫描 */ if (IsA(plan, PartIterator)) plan->lefttree->targetlist = plan->targetlist; @@ -606,16 +659,15 @@ static Plan* make_agg_subplan(PlannerInfo* root, MinMaxAggInfo* mminfo) #ifdef STREAMPLAN if (IS_STREAM_PLAN) { if (is_execute_on_coordinator(plan) || is_execute_on_allnodes(plan)) { - // local case or should we assert + // 本地情况或者应该断言 } else { bool stream_added = true; - // all other case, broadcast plan to all nodes, get all result back and do final agg at the coordinator. - // The final aggregate can also be done in any single node that's going to consume the results, but do that - // we need to know the interested partition of final plan which we don't have it yet. One way to do this, is - // to mark with floating target node (special node group of any_signle_node) and modify it whenever it - // becomes known. Also add a final pass at the end of optimizer to convert floating partition to known - // interested partition or coordinator which is always interested because all results should come to - // coordinator before returning to client. + // 所有其他情况,广播计划到所有节点,获取所有结果并在协调节点上进行最终聚合。 + // 最终聚合也可以在将要消耗结果的任何单个节点上执行,但是要做到这一点, + // 我们需要知道最终计划的感兴趣分区,但我们尚未知道。完成此操作的一种方法是, + // 使用浮动目标节点(任何_single_node 的特殊节点组)标记它,并在每次它变为已知时进行修改。 + // 此外,在优化器的最后添加最终传递,以将浮动分区转换为已知感兴趣分区或总协调器, + // 因为所有结果都应在返回客户端之前到达协调器。 if (root->query_level == 1) { plan = make_simple_RemoteQuery(plan, root, true); } else if (!is_replicated_plan(plan)) { @@ -630,8 +682,7 @@ static Plan* make_agg_subplan(PlannerInfo* root, MinMaxAggInfo* mminfo) errno_t errorno = memset_s(&dummy_aggcosts, sizeof(AggClauseCosts), 0, sizeof(AggClauseCosts)); securec_check(errorno, "\0", "\0"); - // Ideally we should call make_agg, but this case is a bit special in handling the the tlist that's not - // covered in make_agg shold clean up make agg. a bit. + // 理论上我们应该调用make_agg,但是这种情况在处理未在make_agg中覆盖的tlist时有点特殊。 TargetEntry* tle = makeTargetEntry((Expr*)copyObject(mminfo->aggref), (AttrNumber)1, pstrdup("agg_target"), false); @@ -683,72 +734,108 @@ static Plan* make_agg_subplan(PlannerInfo* root, MinMaxAggInfo* mminfo) #endif /* - * Convert the plan into an InitPlan, and make a Param for its result. + * 将计划转换为InitPlan,并为其结果创建一个Param。 */ mminfo->param = SS_make_initplan_from_plan( subroot, plan, exprType((Node*)mminfo->target), -1, exprCollation((Node*)mminfo->target)); /* - * Make sure the initplan gets into the outer PlannerInfo, along with any - * other initplans generated by the sub-planning run. We had to include - * the outer PlannerInfo's pre-existing initplans into the inner one's - * init_plans list earlier, so make sure we don't put back any duplicate - * entries. + * 确保InitPlan进入外部PlannerInfo,以及子规划运行生成的其他任何InitPlan。 + * 我们已经将外部PlannerInfo的现有InitPlan包括在内部PlannerInfo的init_plans列表中, + * 因此确保我们不会放回任何重复的条目。 */ root->init_plans = list_concat_unique_ptr(root->init_plans, subroot->init_plans); return plan; } + /* * Replace original aggregate calls with subplan output Params */ +/* + * replace_aggs_with_params_mutator: 用子查询输出参数替换原始聚合调用 + * + * 此函数遍历表达式树,查找并替换原始聚合调用的引用,将其替换为子查询输出的参数。 + * + * 输入参数: + * - node: 当前要处理的节点 + * - root: 查询规划器的上下文信息,包含有关MIN/MAX聚合的信息 + * + * 返回值: + * 返回替换后的节点。 + */ static Node* replace_aggs_with_params_mutator(Node* node, PlannerInfo* root) { if (node == NULL) return NULL; + + // 如果当前节点是一个聚合函数调用(Aggref) if (IsA(node, Aggref)) { Aggref* aggref = (Aggref*)node; TargetEntry* curTarget = (TargetEntry*)linitial(aggref->args); ListCell* lc = NULL; + // 遍历已保存的 MIN/MAX 聚合信息列表 foreach (lc, root->minmax_aggs) { MinMaxAggInfo* mminfo = (MinMaxAggInfo*)lfirst(lc); + // 如果找到与当前聚合函数匹配的信息 if (mminfo->aggfnoid == aggref->aggfnoid && equal(mminfo->target, curTarget->expr)) - return (Node*)mminfo->param; + return (Node*)mminfo->param; // 返回相应的参数节点 } + + // 如果未找到匹配的聚合信息,抛出错误 ereport(ERROR, (errmodule(MOD_OPT), errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("failed to re-find MinMaxAggInfo record"))); } + + // 处理其他类型的节点 AssertEreport(!IsA(node, SubLink), MOD_OPT, - "invalid node type when replaceing original aggregate calls with subplan output Params."); + "invalid node type when replacing original aggregate calls with subplan output Params."); + + // 递归处理节点的子节点 return expression_tree_mutator(node, (Node* (*)(Node*, void*)) replace_aggs_with_params_mutator, (void*)root); } + /* * Get the OID of the sort operator, if any, associated with an aggregate. * Returns InvalidOid if there is no such operator. */ +/* + * fetch_agg_sort_op: 获取聚合函数的排序操作符 + * + * 该函数根据聚合函数的OID(对象标识符)从系统目录表 pg_aggregate 中检索相关信息, + * 并返回聚合函数的排序操作符的OID。 + * + * 输入参数: + * - aggfnoid: 聚合函数的OID + * + * 返回值: + * 返回排序操作符的OID,如果未找到则返回 InvalidOid。 + */ static Oid fetch_agg_sort_op(Oid aggfnoid) { HeapTuple aggTuple; Form_pg_aggregate aggform; Oid aggsortop; - /* fetch aggregate entry from pg_aggregate */ + /* 从系统目录表 pg_aggregate 中检索聚合函数的条目 */ aggTuple = SearchSysCache1(AGGFNOID, ObjectIdGetDatum(aggfnoid)); if (!HeapTupleIsValid(aggTuple)) - return InvalidOid; + return InvalidOid; // 未找到匹配的聚合函数 + aggform = (Form_pg_aggregate)GETSTRUCT(aggTuple); - aggsortop = aggform->aggsortop; - ReleaseSysCache(aggTuple); + aggsortop = aggform->aggsortop; // 获取聚合函数的排序操作符 + ReleaseSysCache(aggTuple); // 释放系统缓存中的条目 return aggsortop; } + /* * @Description: Judge this agg if is Min/Max. * @in aggref - agg struct @@ -756,6 +843,19 @@ static Oid fetch_agg_sort_op(Oid aggfnoid) * BTGreaterStrategyNumber is mean Max, BTLessStrategyNumber is mean Min. * @return - true is mean agg is Min/Max, false not. */ +/* + * check_agg_optimizable: 检查聚合函数是否可以进行优化 + * + * 该函数用于检查聚合函数是否符合可以进行优化的条件。聚合函数必须是 MIN 或 MAX 聚合, + * 且其参数必须为一个变量(Var),不能是系统列。 + * + * 输入参数: + * - aggref: 聚合函数的 Aggref 结构 + * - strategy: 返回聚合函数的优化策略,用于表示是 MIN 还是 MAX + * + * 返回值: + * 如果聚合函数可以进行优化,返回 true,否则返回 false。如果返回 true,strategy 会被设置为适当的值。 + */ bool check_agg_optimizable(Aggref* aggref, int16* strategy) { TargetEntry* curTarget = NULL; @@ -763,30 +863,37 @@ bool check_agg_optimizable(Aggref* aggref, int16* strategy) Oid opcintype = InvalidOid; Oid aggsortop = InvalidOid; - /* not a MIN/MAX aggregate. */ + /* 如果不是 MIN/MAX 聚合,返回 false */ if (list_length(aggref->args) != 1 || aggref->aggorder != NIL) { return false; } curTarget = (TargetEntry*)linitial(aggref->args); - /* Parameter of agg only can be var type, and can not be system column. */ + + /* 参数只能是变量类型,且不能是系统列 */ if (!IsA(curTarget->expr, Var) || ((Var*)(curTarget->expr))->varattno <= 0) { return false; } Oid var_oid = ((Var*)(curTarget->expr))->vartype; - + + /* 如果变量类型是可优化的 MINMAXOPTIMIZATIONOID,则返回 false */ if (MINMAXOPTIMIZATIONOID(var_oid)) { return false; } + /* 获取聚合函数的排序操作符 */ aggsortop = fetch_agg_sort_op(aggref->aggfnoid); - /* not a MIN/MAX aggregate. */ + + /* 如果排序操作符无效,返回 false */ if (!OidIsValid(aggsortop)) { return false; } - /* see system table pg_amop, out put strategy sign this is min or max. */ + /* + * 通过排序操作符获取优化策略,用于确定是 MIN 还是 MAX 聚合函数。 + * 如果获取失败,返回 false。 + */ if (!get_ordering_op_properties(aggsortop, &opfamily, &opcintype, strategy)) { return false; } @@ -794,3 +901,4 @@ bool check_agg_optimizable(Aggref* aggref, int16* strategy) return true; } + diff --git a/src/gausskernel/optimizer/plan/planner.cpp b/src/gausskernel/optimizer/plan/planner.cpp old mode 100755 new mode 100644 index 59b701227..dd90a267a --- a/src/gausskernel/optimizer/plan/planner.cpp +++ b/src/gausskernel/optimizer/plan/planner.cpp @@ -87,35 +87,44 @@ #include "optimizer/stream_remove.h" #include "executor/node/nodeModifyTable.h" +// 定义一个取两个值中的最小值的宏 #ifndef MIN #define MIN(A, B) ((B) < (A) ? (B) : (A)) #endif +// 声明一个静态函数,用于估算 HDFS 访问成本 #ifdef ENABLE_UT bool estimate_acceleration_cost_for_HDFS(Plan* plan, const char* relname); #else static bool estimate_acceleration_cost_for_HDFS(Plan* plan, const char* relname); #endif +// 定义一个整型数组 g_agglist,包含两个元素 static int g_agglist[] = {AGG_HASHED, AGG_SORTED}; +// 定义一个常量 TWOLEVELWINFUNSELECTIVITY #define TWOLEVELWINFUNSELECTIVITY (1.0 / 3.0) +// 定义一个字符串常量 ESTIMATION_ITEM const char* ESTIMATION_ITEM = "EstimationItem"; -/* From experiment, we assume 2.5 times dn number of distinct value can give all dn work to do */ +// 定义一个浮点数常量 DN_MULTIPLIER_FOR_SATURATION #define DN_MULTIPLIER_FOR_SATURATION 2.5 -#define PLAN_HAS_DELTA(plan) \ +// 定义一个宏,用于检查计划是否包含特定类型的节点 +#define PLAN_HAS_DELTA(plan) \ ((IsA((plan), CStoreScan) && HDFS_STORE == ((CStoreScan*)(plan))->relStoreLocation) \ - ||(IsA((plan), CStoreIndexScan) && HDFS_STORE == ((CStoreIndexScan*)(plan)->relStoreLocation) \ - || IsA((plan), DfsScan) || IsA((plan), DfsIndexScan)) + || (IsA((plan), CStoreIndexScan) && HDFS_STORE == ((CStoreIndexScan*)(plan)->relStoreLocation) \ + || IsA((plan), DfsScan) || IsA((plan), DfsIndexScan))) -/* For performance reasons, memory context will be dropped only when the totalSpace larger than 1MB. */ +// 定义一个常量 MEMORY_CONTEXT_DELETE_THRESHOLD #define MEMORY_CONTEXT_DELETE_THRESHOLD (1024 * 1024) + +// 定义一个宏,用于检查是否需要释放内存上下文 #define IS_NEED_FREE_MEMORY_CONTEXT(MemContext) \ ((MemContext) != NULL && ((AllocSetContext*)(MemContext))->totalSpace > MEMORY_CONTEXT_DELETE_THRESHOLD) +// 定义一个静态数组 VectorEngineUnsupportType,包含多个 Oid 值 const static Oid VectorEngineUnsupportType[] = { POINTOID, LSEGOID, @@ -125,82 +134,150 @@ const static Oid VectorEngineUnsupportType[] = { POLYGONOID, PATHOID, HASH32OID - }; +}; +// 声明外部函数 connect_compute_pool extern PGXCNodeAllHandles* connect_compute_pool(int srvtype); + +// 声明外部函数 get_datasize extern uint64 get_datasize(Plan* plan, int srvtype, int* filenum); +// 声明外部函数 make_dummy_remote_rte extern RangeTblEntry* make_dummy_remote_rte(char* relname, Alias* alias); + +// 声明外部函数 setForeignOptions extern ForeignOptions* setForeignOptions(Oid relid); + +// 声明外部函数 reassign_nodelist extern List* reassign_nodelist(RangeTblEntry* rte, List* ori_node_list); -extern Node* preprocess_expression(PlannerInfo* root, Node* expr, int kind); +// 声明静态函数 preprocess_expression +static Node* preprocess_expression(PlannerInfo* root, Node* expr, int kind); + +// 声明静态函数 inheritance_planner static Plan* inheritance_planner(PlannerInfo* root); + +// 声明静态函数 grouping_planner static Plan* grouping_planner(PlannerInfo* root, double tuple_fraction); + +// 声明静态函数 preprocess_rowmarks static void preprocess_rowmarks(PlannerInfo* root); + +// 声明静态函数 estimate_limit_offset_count static void estimate_limit_offset_count(PlannerInfo* root, int64* offset_est, int64* count_est); + +// 声明静态函数 preprocess_limit static double preprocess_limit(PlannerInfo* root, double tuple_fraction, int64* offset_est, int64* count_est); +// 声明静态函数 grouping_is_can_hash static bool grouping_is_can_hash(Query* parse, AggClauseCosts* agg_costs); + +// 声明静态函数 compute_hash_entry_size static Size compute_hash_entry_size(bool vectorized, Path* cheapest_path, int path_width, AggClauseCosts* agg_costs); + +// 声明静态函数 choose_hashed_grouping static bool choose_hashed_grouping(PlannerInfo* root, double tuple_fraction, double limit_tuples, int path_width, Path* cheapest_path, Path* sorted_path, const double* dNumGroups, AggClauseCosts* agg_costs, Size* hash_entry_size); -static void compute_distinct_sorted_path_cost(Path* sorted_p, List* sorted_pathkeys, Query* parse, PlannerInfo* root, - int numDistinctCols, Cost sorted_startup_cost, Cost sorted_total_cost, double path_rows, + +// 声明静态函数 compute_distinct_sorted_path_cost +static void compute_distinct_sorted_path_cost(Path* sorted_p, List* sorted_pathkeys, Query* parse, PlannerInfo* root, + int numDistinctCols, Cost sorted_startup_cost, Cost sorted_total_cost, double path_rows, Distribution* sorted_distribution, int path_width, double dNumDistinctRows, double limit_tuples); + +// 声明静态函数 choose_hashed_distinct static bool choose_hashed_distinct(PlannerInfo* root, double tuple_fraction, double limit_tuples, double path_rows, int path_width, Cost cheapest_startup_cost, Cost cheapest_total_cost, Distribution* cheapest_distribution, Cost sorted_startup_cost, Cost sorted_total_cost, Distribution* sorted_distribution, List* sorted_pathkeys, double dNumDistinctRows, Size hashentrysize); + +// 声明静态函数 make_subplanTargetList static List* make_subplanTargetList(PlannerInfo* root, List* tlist, AttrNumber** groupColIdx, bool* need_tlist_eval); + +// 声明静态函数 locate_grouping_columns static void locate_grouping_columns(PlannerInfo* root, List* tlist, List* sub_tlist, AttrNumber* groupColIdx); + +// 声明静态函数 postprocess_setop_tlist static List* postprocess_setop_tlist(List* new_tlist, List* orig_tlist); + +// 声明静态函数 make_windowInputTargetList static List* make_windowInputTargetList(PlannerInfo* root, List* tlist, List* activeWindows); + +// 声明静态函数 get_column_info_for_window static void get_column_info_for_window(PlannerInfo* root, WindowClause* wc, List* tlist, int numSortCols, AttrNumber* sortColIdx, int* partNumCols, AttrNumber** partColIdx, Oid** partOperators, int* ordNumCols, AttrNumber** ordColIdx, Oid** ordOperators); + +// 声明静态函数 add_groupingIdExpr_to_tlist static List* add_groupingIdExpr_to_tlist(List* tlist); + +// 声明静态函数 get_group_expr static List* get_group_expr(List* sortrefList, List* tlist); + +// 声明静态函数 build_grouping_itst_keys static void build_grouping_itst_keys(PlannerInfo* root, List* active_windows); + +// 声明静态函数 build_grouping_chain static Plan* build_grouping_chain(PlannerInfo* root, Query* parse, List** tlist, bool need_sort_for_grouping, List* rollup_groupclauses, List* rollup_lists, AttrNumber* groupColIdx, AggClauseCosts* agg_costs, long numGroups, Plan* result_plan, WindowLists* wflists, bool need_stream); + +// 声明静态函数 group_member static bool group_member(List* list, Expr* node); + +// 声明静态函数 build_groupingsets_plan static Plan* build_groupingsets_plan(PlannerInfo* root, Query* parse, List** tlist, bool need_sort_for_grouping, List* rollup_groupclauses, List* rollup_lists, AttrNumber** groupColIdx, AggClauseCosts* agg_costs, long numGroups, Plan* result_plan, WindowLists* wflists, bool* need_hash, List* collectiveGroupExpr); + +// 声明静态函数 vector_engine_preprocess_walker static bool vector_engine_preprocess_walker(Node* node, void* rtables); + +// 声明静态函数 init_optimizer_context static void init_optimizer_context(PlannerGlobal* glob); + +// 声明静态函数 deinit_optimizer_context static void deinit_optimizer_context(PlannerGlobal* glob); + +// 声明静态函数 check_index_column static void check_index_column(); + +// 声明静态函数 check_sort_for_upsert static bool check_sort_for_upsert(PlannerInfo* root); -extern void PushDownFullPseudoTargetlist(PlannerInfo *root, Plan *topNode, Plan *botNode, - List *fullEntryList); +// 声明静态函数 PushDownFullPseudoTargetlist +extern void PushDownFullPseudoTargetlist(PlannerInfo* root, Plan* topNode, Plan* botNode, List* fullEntryList); +// 声明静态函数 separate_rowmarks #ifdef PGXC static void separate_rowmarks(PlannerInfo* root); #endif -#ifdef STREAMPLAN - +// 定义一个结构体 ExprMultipleData,包含一个表达式和一个浮点数倍数 typedef struct { Node* expr; double multiple; } ExprMultipleData; +// 枚举类型 path_key 包含三个值 typedef enum path_key { windows_func_pathkey = 0, distinct_pathkey, sort_pathkey } path_key; +// 定义一个结构体,用于存储查询和变量 typedef struct { List* queries; List* vars; } ImplicitCastVarContext; + +// 定义一个线程局部变量 g_index_vars THR_LOCAL List* g_index_vars; +// 定义一个静态数组 g_hashagg_option_list,包含多个 SAggMethod 枚举值 static SAggMethod g_hashagg_option_list[] = {DN_REDISTRIBUTE_AGG, DN_AGG_REDISTRIBUTE_AGG, DN_AGG_CN_AGG}; + +// 定义常量 ALL_HASHAGG_OPTION 和 HASHAGG_OPTION_WITH_STREAM #define ALL_HASHAGG_OPTION 3 #define HASHAGG_OPTION_WITH_STREAM 2 +// 定义一个结构体,用于查找查询上下文 typedef struct { List* rqs; bool include_all_plans; @@ -235,113 +312,233 @@ typedef struct { * All nodes involved in the query. It stores IDs(int type) of DNs, such as 1,2,3... * The same name exists in ExecNodes. */ - List *nodeList; + // 声明一个名为 nodeList 的 List 结构体指针 +List *nodeList; } FindNodesContext; +// 定义一个结构体 FindStreamNodesForLoopContext,包含两个成员变量 typedef struct { bool has_redis_stream; int broadcast_stream_cnt; } FindStreamNodesForLoopContext; +// 声明静态函数 needs_two_level_groupagg,用于判断是否需要两级分组聚合 static bool needs_two_level_groupagg(PlannerInfo* root, Plan* plan, Node* distinct_node, List* distributed_key, bool* need_redistribute, bool* need_local_redistribute); + +// 声明静态函数 mark_agg_stream,用于标记聚合节点的流信息 static Plan* mark_agg_stream(PlannerInfo* root, List* tlist, Plan* plan, List* group_or_distinct_cls, AggOrientation agg_orientation, bool* has_second_agg_sort); + +// 声明静态函数 mark_top_agg,用于标记顶层聚合节点 static Plan* mark_top_agg( PlannerInfo* root, List* tlist, Plan* agg_plan, Plan* sub_plan, AggOrientation agg_orientation); + +// 声明静态函数 mark_group_stream,用于标记分组节点的流信息 static Plan* mark_group_stream(PlannerInfo* root, List* tlist, Plan* result_plan); + +// 声明静态函数 mark_distinct_stream,用于标记去重节点的流信息 static Plan* mark_distinct_stream( PlannerInfo* root, List* tlist, Plan* plan, List* groupcls, Index query_level, List* current_pathkeys); + +// 声明静态函数 get_optimal_distribute_key,用于获取最佳的分布键 static List* get_optimal_distribute_key(PlannerInfo* root, List* groupClause, Plan* plan, double* multiple); + +// 声明静态函数 vector_engine_walker_internal,用于内部遍历计划树 static bool vector_engine_walker_internal(Plan* result_plan, bool check_rescan, VectorPlanContext* planContext); + +// 声明静态函数 vector_engine_expression_walker,用于遍历表达式 static bool vector_engine_expression_walker(Node* node, DenseRank_context* context); + +// 声明静态函数 vector_engine_walker,用于遍历计划树 static bool vector_engine_walker(Plan* result_plan, bool check_rescan); + +// 声明静态函数 fallback_plan,用于生成回退计划 static Plan* fallback_plan(Plan* result_plan); + +// 声明静态函数 vectorize_plan,用于向量化计划 static Plan* vectorize_plan(Plan* result_plan, bool ignore_remotequery, bool forceVectorEngine); + +// 声明静态函数 build_vector_plan,用于构建向量化计划 static Plan* build_vector_plan(Plan* plan); + +// 声明静态函数 mark_windowagg_stream,用于标记窗口聚合节点的流信息 static Plan* mark_windowagg_stream( PlannerInfo* root, Plan* plan, List* tlist, WindowClause* wc, List* pathkeys, WindowLists* wflists); + +// 声明静态函数 get_hashagg_skew,用于获取哈希聚合的偏斜度 static uint32 get_hashagg_skew(AggSkewInfo* skew_info, List* distribute_keys); + +// 声明静态函数 get_optimal_hashagg,用于获取最佳的哈希聚合计划 static SAggMethod get_optimal_hashagg(PlannerInfo* root, Plan* lefttree, const AggClauseCosts* aggcosts, int numGroupCols, double numGroups, List* distributed_key, List* target_list, double final_groups, double multiple, List* distribute_key_less_skew, double multiple_less_skew, AggOrientation agg_orientation, Cost* final_cost, Distribution** final_distribution, bool need_stream, AggSkewInfo* skew_info, uint32 aggmethod_filter = ALLOW_ALL_AGG); + +// 声明静态函数 generate_hashagg_plan,用于生成哈希聚合计划 static Plan* generate_hashagg_plan(PlannerInfo* root, Plan* plan, List* final_list, AggClauseCosts* agg_costs, int numGroupCols, const double* numGroups, WindowLists* wflists, AttrNumber* groupColIdx, Oid* groupColOps, bool* needs_stream, Size hash_entry_size, AggOrientation agg_orientation, RelOptInfo* rel_info); + +// 声明静态函数 get_count_distinct_partial_plan,用于获取计数去重的部分计划 static Plan* get_count_distinct_partial_plan(PlannerInfo* root, Plan* result_plan, List** final_tlist, Node* distinct_node, AggClauseCosts agg_costs, const double* numGroups, WindowLists* wflists, AttrNumber* groupColIdx, bool* needs_stream, Size hash_entry_size, RelOptInfo* rel_info); + +// 声明静态函数 get_multiple_from_expr,用于从表达式中获取多重度 static Node* get_multiple_from_expr( PlannerInfo* root, Node* expr, double rows, double* skew_multiple, double* bias_multiple); + +// 声明静态函数 set_root_matching_key,用于设置根节点的匹配键 static void set_root_matching_key(PlannerInfo* root, List* targetlist); + +// 声明静态函数 add_groupId_to_groupExpr,用于向分组表达式中添加分组ID static List* add_groupId_to_groupExpr(List* query_group, List* tlist); +// 声明静态函数 cost_agg_convert_to_path,用于将聚合计划转换为路径 static Path* cost_agg_convert_to_path(Plan* plan); + +// 声明静态函数 cost_agg_do_redistribute,用于执行分布式重分发操作 static StreamPath* cost_agg_do_redistribute(Path* subpath, List* distributed_key, double multiple, Distribution* target_distribution, int width, bool vec_output, int dop, bool needs_stream); + +// 声明静态函数 cost_agg_do_gather,用于执行收集操作 static StreamPath* cost_agg_do_gather(Path* subpath, int width, bool vec_output); + +// 声明静态函数 cost_agg_do_agg,用于执行聚合操作 static Path* cost_agg_do_agg(Path* subpath, PlannerInfo* root, AggStrategy agg_strategy, const AggClauseCosts* aggcosts, int numGroupCols, double numGroups, Size hashentrysize, QualCost total_cost, int width, bool vec_output, int dop); +// 声明静态函数 get_hashagg_gather_hashagg_path,用于获取哈希聚合-收集-哈希聚合路径 static void get_hashagg_gather_hashagg_path(PlannerInfo* root, Plan* lefttree, const AggClauseCosts* aggcosts, int numGroupCols, double numGroups, double final_groups, QualCost total_cost, Size hashentrysize, AggStrategy agg_strategy, bool needs_stream, Path* result_path); + +// 声明静态函数 get_redist_hashagg_gather_hashagg_path,用于获取重分发-哈希聚合-收集-哈希聚合路径(多节点) #ifdef ENABLE_MULTIPLE_NODES static void get_redist_hashagg_gather_hashagg_path(PlannerInfo* root, Plan* lefttree, const AggClauseCosts* aggcosts, int numGroupCols, double numGroups, double final_groups, List* distributed_key_less_skew, double multiple_less_skew, Distribution* target_distribution, QualCost total_cost, Size hashentrysize, AggStrategy agg_strategy, bool needs_stream, Path* result_path); #endif + +// 声明静态函数 get_redist_hashagg_path,用于获取重分发-哈希聚合路径 static void get_redist_hashagg_path(PlannerInfo* root, Plan* lefttree, const AggClauseCosts* aggcosts, int numGroupCols, double numGroups, double final_groups, List* distributed_key, double multiple, Distribution* target_distribution, QualCost total_cost, Size hashentrysize, bool needs_stream, Path* result_path); + +// 声明静态函数 get_hashagg_redist_hashagg_path,用于获取哈希聚合-重分发-哈希聚合路径 static void get_hashagg_redist_hashagg_path(PlannerInfo* root, Plan* lefttree, const AggClauseCosts* aggcosts, int numGroupCols, double numGroups, double final_groups, List* distributed_key, double multiple, Distribution* target_distribution, QualCost total_cost, Size hashentrysize, bool needs_stream, Path* result_path); + +// 声明静态函数 get_redist_hashagg_redist_hashagg_path,用于获取重分发-哈希聚合-重分发-哈希聚合路径 static void get_redist_hashagg_redist_hashagg_path(PlannerInfo* root, Plan* lefttree, const AggClauseCosts* aggcosts, int numGroupCols, double numGroups, double final_groups, List* distributed_key_less_skew, double multiple_less_skew, Distribution* target_distribution, List* distributed_key, double multiple, QualCost total_cost, Size hashentrysize, bool needs_stream, Path* result_path); +// 声明静态函数 get_count_distinct_param,用于获取计数去重的参数 static void get_count_distinct_param(PlannerInfo* root, Plan** result_plan, List* tlist, Node* distinct_node, int* numGrpColsNew, AttrNumber* groupColIdx, AttrNumber** groupColIdx_new, Oid** groupingOps_new, List** orig_tlist, List** duplicate_tlist, List** newtlist); + +// 声明静态函数 get_count_distinct_newtlist,用于获取计数去重的新目标列表 static List* get_count_distinct_newtlist(PlannerInfo* root, List* tlist, Node* distinct_node, List** orig_tlist, List** duplicate_tlist, Oid* distinct_eq_op); + +// 声明静态函数 make_dummy_targetlist,用于创建虚拟的目标列表 static void make_dummy_targetlist(Plan* plan); + +// 声明静态函数 passdown_itst_keys_to_subroot,用于向子根节点传递匹配键 static void passdown_itst_keys_to_subroot(PlannerInfo* root, ItstDisKey* diskeys); + +// 声明静态函数 add_itst_node_to_list,用于向列表中添加匹配键节点 static List* add_itst_node_to_list(List* result_list, List* target_list, Expr* node, bool is_matching_key); + +// 声明静态函数 copy_path_costsize,用于复制路径的成本和大小信息 static void copy_path_costsize(Path* dest, Path* src); + +// 声明静态函数 walk_plan,用于遍历计划树 static bool walk_plan(Plan* plantree, PlannerInfo* root); + +// 声明静态函数 walk_normal_plan,用于遍历普通计划树 static bool walk_normal_plan(Plan* plantree, PlannerInfo* root); + +// 声明静态函数 walk_set_plan,用于遍历集合计划树 static void walk_set_plan(Plan* plantree, PlannerInfo* root); + +// 声明静态函数 insert_gather_node,用于插入收集节点 static Plan* insert_gather_node(Plan* child, PlannerInfo* root); + +// 声明静态函数 has_dfs_node,用于检查计划树中是否包含 DFS 节点 static bool has_dfs_node(Plan* plantree, PlannerGlobal* glob); + +// 声明静态函数 try_accelerate_plan,用于尝试加速计划 static Plan* try_accelerate_plan(Plan* plantree, PlannerInfo* root, PlannerGlobal* glob); + +// 声明静态函数 try_deparse_agg,用于尝试解析聚合计划 static Plan* try_deparse_agg(Plan* plan, PlannerInfo* root, PlannerGlobal* glob); + +// 声明静态函数 dfs_node_exists,用于检查计划树中是否存在 DFS 节点 static bool dfs_node_exists(Plan* plan); + +// 声明静态函数 is_dfs_node,用于判断计划节点是否为 DFS 节点 static bool is_dfs_node(Plan* plan); + +// 声明静态函数 add_metadata,用于添加元数据到计划中 static void add_metadata(Plan* plan, PlannerInfo* root); + +// 声明静态函数 precheck_before_accelerate,用于在加速之前进行预检查 static bool precheck_before_accelerate(); + +// 声明静态函数 is_pushdown_node,用于判断计划节点是否为下推节点 static bool is_pushdown_node(Plan *plan); + +// 声明静态函数 estimate_acceleration_cost,用于估算加速计划的成本 static bool estimate_acceleration_cost(Plan *plan); -#ifdef ENABLE_MULTIPLE_NODES + +// 声明静态函数 walk_plan_for_coop_analyze,用于协同分析计划树 static bool walk_plan_for_coop_analyze(Plan *plan, PlannerInfo *root); + +// 声明静态函数 walk_set_plan_for_coop_analyze,用于协同分析集合计划树 static void walk_set_plan_for_coop_analyze(Plan *plan, PlannerInfo *root); + +// 声明静态函数 walk_normal_plan_for_coop_analyze,用于协同分析普通计划树 static bool walk_normal_plan_for_coop_analyze(Plan *plan, PlannerInfo *root); + +// 声明静态函数 find_right_agg,用于查找正确的聚合节点 static bool find_right_agg(Plan *plan); + +// 声明静态函数 has_pgfdw_rel,用于检查计划中是否包含 PGFDW 关系 static bool has_pgfdw_rel(PlannerInfo* root); + +// 声明静态函数 deparse_agg_node,用于解析聚合节点 extern Plan *deparse_agg_node(Plan *agg, PlannerInfo *root); -#endif + +// 声明静态函数 find_remotequery,用于查找远程查询节点 static void find_remotequery(Plan *plan, PlannerInfo *root); + +// 声明静态函数 gtm_process_top_node,用于处理顶层节点的 GTM 查询 static void gtm_process_top_node(Plan *plan, void *context); + +// 声明函数 GetRemoteQuery,用于获取远程查询的 SQL 语句 void GetRemoteQuery(PlannedStmt *plan, const char *queryString); + +// 声明函数 GetRemoteQueryWalker,用于遍历获取远程查询的 SQL 语句 void GetRemoteQueryWalker(Plan* plan, void* context, const char *queryString); + +// 声明函数 PlanTreeWalker,用于遍历整个计划树 void PlanTreeWalker(Plan* plan, void (*walker)(Plan*, void*, const char*), void*, const char *queryString); + +// 声明静态函数 find_implicit_cast_var,用于查找隐式类型转换的变量 static void find_implicit_cast_var(Query *query); + +// 声明静态函数 implicit_cast_var_walker,用于遍历隐式类型转换的变量 static bool implicit_cast_var_walker(Node *node, void *context); + +// 声明静态函数 save_implicit_cast_var,用于保存隐式类型转换的变量 static void save_implicit_cast_var(Node *node, void *context); #endif @@ -361,50 +558,49 @@ static Node* preprocess_const_params_worker(PlannerInfo* root, Node* expr, int k * so you'd better copy that data structure if you want to plan more than once. * *****************************************************************************/ +// 声明函数 PlannedStmt* planner,该函数用于执行查询计划的生成 PlannedStmt* planner(Query* parse, int cursorOptions, ParamListInfo boundParams) { - PlannedStmt* result = NULL; - instr_time starttime; - double totaltime = 0; + PlannedStmt* result = NULL; // 初始化计划结果为 NULL + instr_time starttime; // 初始化用于记录时间的变量 + double totaltime = 0; // 初始化总时间为 0 - INSTR_TIME_SET_CURRENT(starttime); + INSTR_TIME_SET_CURRENT(starttime); // 获取当前时间 #ifdef PGXC - /* - * streaming engine hook for agg rewrite. - */ + // 如果使用了流式查询规划钩子,则调用该钩子函数 if (t_thrd.streaming_cxt.streaming_planner_hook) (*(planner_hook_type) t_thrd.streaming_cxt.streaming_planner_hook)\ (parse, cursorOptions, boundParams); - /* - * A Coordinator receiving a query from another Coordinator - * is not allowed to go into PGXC planner. - */ + + // 如果当前节点为 PGXC 协调器或单节点,并且不是来自协调器连接,则使用 pgxc_planner 规划 if ((IS_PGXC_COORDINATOR || IS_SINGLE_NODE) && !IsConnFromCoord()) result = pgxc_planner(parse, cursorOptions, boundParams); else #endif - result = standard_planner(parse, cursorOptions, boundParams); + result = standard_planner(parse, cursorOptions, boundParams); // 否则使用标准规划 - totaltime += elapsed_time(&starttime); - result->plannertime = totaltime; + totaltime += elapsed_time(&starttime); // 计算规划所用时间 + result->plannertime = totaltime; // 将规划时间保存到结果中 + + // 如果配置了最大数据节点数限制,并且当前节点为协调器且不是来自协调器连接,则获取远程查询的 SQL 语句 if (u_sess->attr.attr_common.max_datanode_for_plan > 0 && IS_PGXC_COORDINATOR && !IsConnFromCoord()) { GetRemoteQuery(result, NULL); } - return result; + return result; // 返回查询计划结果 } +// 声明静态函数 queryIsReadOnly,用于检查查询是否为只读操作 static bool queryIsReadOnly(Query* query) { if (IsA(query, Query)) { switch (query->commandType) { case CMD_SELECT: { - /* SELECT FOR [KEY] UPDATE/SHARE */ + // 如果查询包含行标记或修改公共表达式,则不是只读操作 if (query->rowMarks != NIL) return false; - /* data-modifying CTE */ if (query->hasModifyingCTE) return false; } @@ -416,6 +612,7 @@ static bool queryIsReadOnly(Query* query) case CMD_MERGE: return false; default: { + // 报告错误,表示无法识别的命令类型 ereport(ERROR, (errmodule(MOD_OPT_PLANNER), errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE), errmsg("Unrecognized commandType when checking read-only attribute."), @@ -429,24 +626,19 @@ static bool queryIsReadOnly(Query* query) return false; } -/* - * @Description: fill bucketmap info into planstmt. - * - * @param[IN] result: plan info - * @param[IN] node_group_info_context: bucketmap info - * @return: void - */ +// 声明静态函数 FillPlanBucketmap,用于填充计划的哈希桶映射信息 static void FillPlanBucketmap(PlannedStmt *result, NodeGroupInfoContext *nodeGroupInfoContext) { #ifdef ENABLE_MULTIPLE_NODES - /* bucketmap is not needed, just return. */ + // 如果不需要哈希桶映射,则将数量设置为 0 并返回 if (!IsBucketmapNeeded(result)) { result->num_bucketmaps = 0; return; } #endif + // 将计划的哈希桶映射信息填充为上下文中的信息 result->num_bucketmaps = nodeGroupInfoContext->num_bucketmaps; for (int i = 0; i < result->num_bucketmaps; i++) { result->bucketMap[i] = nodeGroupInfoContext->bucketMap[i]; @@ -454,6 +646,7 @@ static void FillPlanBucketmap(PlannedStmt *result, } pfree_ext(nodeGroupInfoContext); + // 如果当前节点为协调器并且没有哈希桶映射信息,则获取全局哈希桶映射 if (IS_PGXC_COORDINATOR && result->num_bucketmaps == 0) { result->bucketMap[0] = GetGlobalStreamBucketMap(result); if (result->bucketMap[0] != NULL) { @@ -463,18 +656,14 @@ static void FillPlanBucketmap(PlannedStmt *result, } } -/* - * @Description: disable tsstore delete sql for pgxc plan. - * - * @param[IN] query: query info - * @return: void - */ +// 声明静态函数 checkTsstoreQuery,用于检查是否为 TSDB 删除 SQL 查询 static void checkTsstoreQuery(Query* query) { if (query->commandType == CMD_DELETE) { RangeTblEntry *rte = rt_fetch(query->resultRelation, query->rtable); Relation rel = heap_open(rte->relid, AccessShareLock); if (RelationIsTsStore(rel)) { + // 报告错误,表示不支持 TSDB 删除操作 ereport(ERROR, (errmodule(MOD_EXECUTOR), errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("PGXC Plan is not supported for tsdb deleting sql"), errdetail("modify parameters enable_stream_operator or enable_fast_query_shipping"), @@ -485,249 +674,218 @@ static void checkTsstoreQuery(Query* query) } } +// 声明函数 standard_planner,用于执行标准的查询规划 PlannedStmt* standard_planner(Query* parse, int cursorOptions, ParamListInfo boundParams) { - PlannedStmt* result = NULL; - PlannerGlobal* glob = NULL; - double tuple_fraction; - PlannerInfo* root = NULL; - Plan* top_plan = NULL; - ListCell* lp = NULL; - ListCell* lr = NULL; - int max_mem = 0; - int available_mem = 0; - int esti_op_mem = 0; - bool use_query_mem = false; - bool use_tenant = false; - List* parse_hint_warning = NIL; + PlannedStmt* result = NULL; // 初始化计划结果为 NULL + PlannerGlobal* glob = NULL; // 初始化全局规划器上下文为 NULL + double tuple_fraction; // 初始化元组分数 + PlannerInfo* root = NULL; // 初始化规划信息 + Plan* top_plan = NULL; // 初始化顶层计划 + ListCell* lp = NULL; // 初始化列表元素指针 + ListCell* lr = NULL; // 初始化列表元素指针 + int max_mem = 0; // 初始化最大内存 + int available_mem = 0; // 初始化可用内存 + int esti_op_mem = 0; // 初始化估算操作内存 + bool use_query_mem = false; // 初始化是否使用查询内存的标志 + bool use_tenant = false; // 初始化是否使用租户信息的标志 + List* parse_hint_warning = NIL; // 初始化解析提示警告列表 - //if it is pgxc plan for tsstore delete sql.errport - if((!u_sess->attr.attr_sql.enable_stream_operator || !u_sess->opt_cxt.is_stream) && IS_PGXC_COORDINATOR) { + // 如果不是流式操作或者当前节点不是协调器,则检查是否为 TSDB 删除 SQL 查询 + if ((!u_sess->attr.attr_sql.enable_stream_operator || !u_sess->opt_cxt.is_stream) && IS_PGXC_COORDINATOR) { checkTsstoreQuery(parse); } +} + + /* * Dynamic smp */ - if (IsDynamicSmpEnabled()) { - InitDynamicSmp(); - int hashTableCount = 0; + // 如果启用动态 SMP(Symmetric Multi-Processing),则执行以下操作 +if (IsDynamicSmpEnabled()) { + InitDynamicSmp(); // 初始化动态 SMP + int hashTableCount = 0; // 初始化哈希表计数为 0 - if (isIntergratedMachine) { - GetHashTableCount(parse, parse->cteList, &hashTableCount); - } - - ChooseStartQueryDop(hashTableCount); + // 如果是集成机器(Integrated Machine),则获取哈希表数量 + if (isIntergratedMachine) { + GetHashTableCount(parse, parse->cteList, &hashTableCount); } - if (enable_check_implicit_cast()) - find_implicit_cast_var(parse); + // 选择起始查询的 DOP(Degree of Parallelism) + ChooseStartQueryDop(hashTableCount); +} - /* Initilizing the work mem used by optimizer */ - if (IS_STREAM_PLAN) { - dywlm_client_get_memory_info(&max_mem, &available_mem, &use_tenant); +// 如果启用了检查隐式类型转换的选项,则执行隐式类型转换的检查 +if (enable_check_implicit_cast()) + find_implicit_cast_var(parse); - if (max_mem != 0) { - use_query_mem = true; - esti_op_mem = (double)available_mem / 2.0; - u_sess->opt_cxt.op_work_mem = Min(esti_op_mem, OPT_MAX_OP_MEM); - AssertEreport(u_sess->opt_cxt.op_work_mem > 0, - MOD_OPT, - "invalid operator work memory when initilizing the work memory used by optimizer"); - } else { - u_sess->opt_cxt.op_work_mem = u_sess->attr.attr_memory.work_mem; - esti_op_mem = u_sess->opt_cxt.op_work_mem; - } +// 如果是流式计划,则获取内存信息并设置操作内存 +if (IS_STREAM_PLAN) { + dywlm_client_get_memory_info(&max_mem, &available_mem, &use_tenant); + + if (max_mem != 0) { + use_query_mem = true; + esti_op_mem = (double)available_mem / 2.0; + u_sess->opt_cxt.op_work_mem = Min(esti_op_mem, OPT_MAX_OP_MEM); + AssertEreport(u_sess->opt_cxt.op_work_mem > 0, + MOD_OPT, + "invalid operator work memory when initializing the work memory used by optimizer"); } else { u_sess->opt_cxt.op_work_mem = u_sess->attr.attr_memory.work_mem; esti_op_mem = u_sess->opt_cxt.op_work_mem; } +} else { + u_sess->opt_cxt.op_work_mem = u_sess->attr.attr_memory.work_mem; + esti_op_mem = u_sess->opt_cxt.op_work_mem; +} - /* Cursor options may come from caller or from DECLARE CURSOR stmt */ - if (parse->utilityStmt && IsA(parse->utilityStmt, DeclareCursorStmt)) - cursorOptions = (uint32)cursorOptions | (uint32)(((DeclareCursorStmt*)parse->utilityStmt)->options); +// 如果解析中包含实用程序语句(utilityStmt)且是声明游标语句,则更新游标选项 +if (parse->utilityStmt && IsA(parse->utilityStmt, DeclareCursorStmt)) + cursorOptions = (uint32)cursorOptions | (uint32)(((DeclareCursorStmt*)parse->utilityStmt)->options); - /* - * Set up global state for this planner invocation. This data is needed - * across all levels of sub-Query that might exist in the given command, - * so we keep it in a separate struct that's linked to by each per-Query - * PlannerInfo. - */ - glob = makeNode(PlannerGlobal); +// 创建全局规划器上下文(PlannerGlobal) +glob = makeNode(PlannerGlobal); - glob->boundParams = boundParams; - glob->subplans = NIL; - glob->subroots = NIL; - glob->rewindPlanIDs = NULL; - glob->finalrtable = NIL; - glob->finalrowmarks = NIL; - glob->resultRelations = NIL; - glob->relationOids = NIL; - glob->invalItems = NIL; - glob->nParamExec = 0; - glob->lastPHId = 0; - glob->lastRowMarkId = 0; - glob->transientPlan = false; - glob->dependsOnRole = false; - glob->insideRecursion = false; - glob->bloomfilter.bloomfilter_index = -1; - glob->bloomfilter.add_index = true; - glob->estiopmem = esti_op_mem; - - if (IS_STREAM_PLAN) - glob->vectorized = !vector_engine_preprocess_walker((Node*)parse, parse->rtable); - else - glob->vectorized = false; - /* Assume work mem is at least 1/4 of query mem */ - glob->minopmem = Min(available_mem / 4, OPT_MAX_OP_MEM); - parse_hint_warning = retrieve_query_hint_warning((Node*)parse); +// 初始化全局规划器上下文的各个属性 +glob->boundParams = boundParams; +glob->subplans = NIL; +glob->subroots = NIL; +glob->rewindPlanIDs = NULL; +glob->finalrtable = NIL; +glob->finalrowmarks = NIL; +glob->resultRelations = NIL; +glob->relationOids = NIL; +glob->invalItems = NIL; +glob->nParamExec = 0; +glob->lastPHId = 0; +glob->lastRowMarkId = 0; +glob->transientPlan = false; +glob->dependsOnRole = false; +glob->insideRecursion = false; +glob->bloomfilter.bloomfilter_index = -1; +glob->bloomfilter.add_index = true; +glob->estiopmem = esti_op_mem; - /* - * Set up default exec_nodes, we fist build re-cursively iterate parse->rtable - * to check if we are refering base relations from different node group(error-out), - * then fetch 1st RTE entry to build default ExecNodes and put the reference to - * the top-most PlannerInfo->glob - */ - bool ngbk_is_multiple_nodegroup_scenario = false; - int ngbk_different_nodegroup_count = 1; - Distribution* ngbk_in_redistribution_group_distribution = NULL; - Distribution* ngbk_compute_permission_group_distribution = NULL; - Distribution* ngbk_query_union_set_group_distribution = NULL; - Distribution* ngbk_single_node_distribution = NULL; - ng_backup_nodegroup_options(&ngbk_is_multiple_nodegroup_scenario, - &ngbk_different_nodegroup_count, - &ngbk_in_redistribution_group_distribution, - &ngbk_compute_permission_group_distribution, - &ngbk_query_union_set_group_distribution, - &ngbk_single_node_distribution); - ng_init_nodegroup_optimizer(parse); +// 如果是流式计划,则检查是否支持矢量引擎预处理 +if (IS_STREAM_PLAN) + glob->vectorized = !vector_engine_preprocess_walker((Node*)parse, parse->rtable); +else + glob->vectorized = false; - /* Must assign value after call ng_init_nodegroup_optimizer(). */ - u_sess->opt_cxt.is_dngather_support = - u_sess->opt_cxt.is_dngather_support && ng_get_single_node_distribution() != NULL; +// 计算最小操作内存 +glob->minopmem = Min(available_mem / 4, OPT_MAX_OP_MEM); +parse_hint_warning = retrieve_query_hint_warning((Node*)parse); - /* Determine what fraction of the plan is likely to be scanned */ - if ((uint32)cursorOptions & CURSOR_OPT_FAST_PLAN) { - /* - * We have no real idea how many tuples the user will ultimately FETCH - * from a cursor, but it is often the case that he doesn't want 'em - * all, or would prefer a fast-start plan anyway so that he can - * process some of the tuples sooner. Use a GUC parameter to decide - * what fraction to optimize for. - */ - tuple_fraction = u_sess->attr.attr_sql.cursor_tuple_fraction; +// 初始化一些变量和上下文信息 +bool ngbk_is_multiple_nodegroup_scenario = false; +int ngbk_different_nodegroup_count = 1; +Distribution* ngbk_in_redistribution_group_distribution = NULL; +Distribution* ngbk_compute_permission_group_distribution = NULL; +Distribution* ngbk_query_union_set_group_distribution = NULL; +Distribution* ngbk_single_node_distribution = NULL; +ng_backup_nodegroup_options(&ngbk_is_multiple_nodegroup_scenario, + &ngbk_different_nodegroup_count, + &ngbk_in_redistribution_group_distribution, + &ngbk_compute_permission_group_distribution, + &ngbk_query_union_set_group_distribution, + &ngbk_single_node_distribution); +ng_init_nodegroup_optimizer(parse); +u_sess->opt_cxt.is_dngather_support = + u_sess->opt_cxt.is_dngather_support && ng_get_single_node_distribution() != NULL; - /* - * We document cursor_tuple_fraction as simply being a fraction, which - * means the edge cases 0 and 1 have to be treated specially here. We - * convert 1 to 0 ("all the tuples") and 0 to a very small fraction. - */ - if (tuple_fraction >= 1.0) { - tuple_fraction = 0.0; - } else if (tuple_fraction <= 0.0) { - tuple_fraction = 1e-10; - } - } else { - /* Default assumption is we need all the tuples */ +// 如果启用了快速计划选项,则设置元组分数 +if ((uint32)cursorOptions & CURSOR_OPT_FAST_PLAN) { + tuple_fraction = u_sess->attr.attr_sql.cursor_tuple_fraction; + if (tuple_fraction >= 1.0) { tuple_fraction = 0.0; + } else if (tuple_fraction <= 0.0) { + tuple_fraction = 1e-10; } +} else { + tuple_fraction = 0.0; +} - /* reset u_sess->analyze_cxt.need_autoanalyze */ - u_sess->analyze_cxt.need_autoanalyze = false; +u_sess->analyze_cxt.need_autoanalyze = false; // 禁用自动分析 - MemoryContext old_context = CurrentMemoryContext; - init_optimizer_context(glob); - old_context = MemoryContextSwitchTo(glob->plannerContext->plannerMemContext); +// 切换内存上下文并初始化优化器上下文 +MemoryContext old_context = CurrentMemoryContext; +init_optimizer_context(glob); +old_context = MemoryContextSwitchTo(glob->plannerContext->plannerMemContext); - /* primary planning entry point (may recurse for subqueries) */ - top_plan = subquery_planner(glob, parse, NULL, false, tuple_fraction, &root); +// 使用子查询规划器生成计划 +top_plan = subquery_planner(glob, parse, NULL, false, tuple_fraction, &root); - MemoryContextSwitchTo(old_context); +MemoryContextSwitchTo(old_context); - /* Are there OBS/HDFS ForeignScan node(s) in the plan tree? */ - u_sess->opt_cxt.srvtype = T_INVALID; - u_sess->opt_cxt.has_obsrel = has_dfs_node(top_plan, glob); +u_sess->opt_cxt.srvtype = T_INVALID; +u_sess->opt_cxt.has_obsrel = has_dfs_node(top_plan, glob); - /* - * try to accelerate the query for HDFS/OBS foreign table by pushing - * scan/agg node down to the compute pool. - */ - if (u_sess->opt_cxt.has_obsrel) { - AssertEreport(u_sess->opt_cxt.srvtype != T_INVALID, - MOD_OPT, - "invalid server type when push scan/agg node down to the compute pool to the accelerate the query."); - - top_plan = try_accelerate_plan(top_plan, root, glob); - } - - /* - * If creating a plan for a scrollable cursor, make sure it can run - * backwards on demand. Add a Material node at the top at need. - */ - if ((unsigned int)cursorOptions & CURSOR_OPT_SCROLL) { - if (!ExecSupportsBackwardScan(top_plan)) - top_plan = materialize_finished_plan(top_plan); - } - - /* final cleanup of the plan */ - AssertEreport(glob->finalrtable == NIL, +// 如果计划中包含分布式文件系统(DFS)节点,则尝试加速计划 +if (u_sess->opt_cxt.has_obsrel) { + AssertEreport(u_sess->opt_cxt.srvtype != T_INVALID, MOD_OPT, - "finalrtable is not empty when finish creating a plan for a scrollable cursor"); - AssertEreport(glob->finalrowmarks == NIL, - MOD_OPT, - "finalrowmarks is not empty when finish creating a plan for a scrollable cursor"); - AssertEreport(glob->resultRelations == NIL, - MOD_OPT, - "resultRelations is not empty when finish creating a plan for a scrollable cursor"); + "invalid server type when pushing scan/agg node down to the compute pool to accelerate the query"); - if ((IS_STREAM_PLAN || (IS_PGXC_DATANODE && (!IS_STREAM || IS_STREAM_DATANODE))) && root->query_level == 1) { - /* remote query and windowagg do not support vectorize rescan, so fallback to row plan */ - top_plan = try_vectorize_plan(top_plan, parse, cursorOptions & CURSOR_OPT_HOLD); + top_plan = try_accelerate_plan(top_plan, root, glob); +} + +// 如果启用了滚动游标选项,且计划不支持向后扫描,则将计划材料化 +if ((unsigned int)cursorOptions & CURSOR_OPT_SCROLL) { + if (!ExecSupportsBackwardScan(top_plan)) + top_plan = materialize_finished_plan(top_plan); +} + +// 断言确保在创建滚动游标计划时各项属性为空 +AssertEreport(glob->finalrtable == NIL, + MOD_OPT, + "finalrtable is not empty when finishing creating a plan for a scrollable cursor"); +AssertEreport(glob->finalrowmarks == NIL, + MOD_OPT, + "finalrowmarks is not empty when finishing creating a plan for a scrollable cursor"); +AssertEreport(glob->resultRelations == NIL, + MOD_OPT, + "resultRelations is not empty when finishing creating a plan for a scrollable cursor"); + +// 如果是流式计划或者是协调器且不是流式或流式数据节点,则执行以下操作 +if (IS_STREAM_PLAN || (IS_PGXC_DATANODE && (!IS_STREAM || IS_STREAM_DATANODE))) { + + // 尝试矢量化计划(如果符合条件) + top_plan = try_vectorize_plan(top_plan, parse, cursorOptions & CURSOR_OPT_HOLD); +} + +// 设置计划引用 +top_plan = set_plan_references(root, top_plan); +delete_redundant_streams_of_remotequery((RemoteQuery *)top_plan); + +// 尝试将聚合操作(Agg)下推 +top_plan = try_deparse_agg(top_plan, root, glob); + +// 查找远程查询节点 +find_remotequery(top_plan, root); + +// 如果是协调器且查询级别为 1,则执行以下操作 +if (IS_PGXC_COORDINATOR && root->query_level == 1) { + bool materialize = false; + bool sort_to_store = false; + + // 如果是持续游标,则启用材料化和存储排序 + if (cursorOptions & CURSOR_OPT_HOLD) { + materialize = true; + sort_to_store = true; } + materialize_remote_query(top_plan, &materialize, sort_to_store); +} - top_plan = set_plan_references(root, top_plan); - delete_redundant_streams_of_remotequery((RemoteQuery *)top_plan); +// 如果查询 DOP 大于 1,则获取子计划列表 +if (u_sess->opt_cxt.query_dop > 1) { + List* subplan_list = NIL; + (void)has_subplan(top_plan, NULL, NULL, true, &subplan_list, true); +} +confirm_parallel_info(top_plan, 1); - /* - * just for cooperation analysis on client cluster, - * try deparse agg node to remote sql in ForeignScan node. - * NOTE: call try_deparse_agg() must be after set_plan_references(). - */ - top_plan = try_deparse_agg(top_plan, root, glob); - /* - * just for cooperation analysis on source data cluster, - * reassign dn list scaned of RemoteQuery node for the request from client cluster. - */ - find_remotequery(top_plan, root); - - if (IS_PGXC_COORDINATOR && root->query_level == 1) { - bool materialize = false; - bool sort_to_store = false; - /* - * if is with hold cursor, remotequery tuplestore should be used, - * and because we do not rescan sortstore in execRemoteQueryResacn, - * tuples in sortstore should be stored into tuplestore, to avoid missing tuples. - */ - if (cursorOptions & CURSOR_OPT_HOLD) { - materialize = true; - sort_to_store = true; - } - materialize_remote_query(top_plan, &materialize, sort_to_store); - } - - /* - * Handle subplan situation. - * We have to put this under set_plan_references() function, - * otherwise we will mis-identify the subplan. - */ - if (u_sess->opt_cxt.query_dop > 1) { - List* subplan_list = NIL; - (void)has_subplan(top_plan, NULL, NULL, true, &subplan_list, true); - } - confirm_parallel_info(top_plan, 1); - -#ifdef STREAMPLAN +#ifdef STREAMPLAN// 如果是流式计划,则执行以下操作 /* * Mark plan node id and parent node id for all the plan nodes. */ @@ -755,32 +913,30 @@ PlannedStmt* standard_planner(Query* parse, int cursorOptions, ParamListInfo bou List* init_plan = NIL; int i = 1; - NodeGroupInfoContext* node_group_info_context = (NodeGroupInfoContext*)palloc0(sizeof(NodeGroupInfoContext)); + NodeGroupInfoContext* node_group_info_context = (NodeGroupInfoContext*)palloc0(sizeof(NodeGroupInfoContext)); - /* - * MPP with-recursive support - * - * Vectorize the each plan nodes under RecursiveUnion - */ - Assert(list_length(glob->subplans) == list_length(glob->subroots)); - forboth(lp, glob->subplans, lr, glob->subroots) - { - Plan* subplan = (Plan*)lfirst(lp); - PlannerInfo* subroot = (PlannerInfo*)lfirst(lr); +Assert(list_length(glob->subplans) == list_length(glob->subroots)); +forboth(lp, glob->subplans, lr, glob->subroots) +{ + Plan* subplan = (Plan*)lfirst(lp); + PlannerInfo* subroot = (PlannerInfo*)lfirst(lr); - /* Vectorize the subplan with RecursiveUnion plan node */ - if (STREAM_RECURSIVECTE_SUPPORTED && IsA(subplan, RecursiveUnion)) { - subplan = try_vectorize_plan(subplan, subroot->parse, true); - lfirst(lp) = subplan; - } + // 如果支持递归公共表表达式且计划是递归联合计划,则尝试矢量化计划 + if (STREAM_RECURSIVECTE_SUPPORTED && IsA(subplan, RecursiveUnion)) { + subplan = try_vectorize_plan(subplan, subroot->parse, true); + lfirst(lp) = subplan; } +} /* 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, @@ -801,20 +957,25 @@ PlannedStmt* standard_planner(Query* parse, int cursorOptions, ParamListInfo bou } #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)); @@ -827,34 +988,30 @@ PlannedStmt* standard_planner(Query* parse, int cursorOptions, ParamListInfo bou 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); @@ -866,11 +1023,13 @@ PlannedStmt* standard_planner(Query* parse, int cursorOptions, ParamListInfo bou 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; @@ -891,6 +1050,7 @@ PlannedStmt* standard_planner(Query* parse, int cursorOptions, ParamListInfo bou 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(); @@ -901,37 +1061,43 @@ PlannedStmt* standard_planner(Query* parse, int cursorOptions, ParamListInfo bou 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", @@ -939,7 +1105,7 @@ PlannedStmt* standard_planner(Query* parse, int cursorOptions, ParamListInfo bou 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; @@ -947,13 +1113,16 @@ PlannedStmt* standard_planner(Query* parse, int cursorOptions, ParamListInfo bou 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, @@ -961,58 +1130,74 @@ PlannedStmt* standard_planner(Query* parse, int cursorOptions, ParamListInfo bou 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; @@ -1021,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); @@ -1099,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; @@ -1158,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); @@ -1179,6 +1368,7 @@ int apply_set_hint(const Query* parse) void recover_set_hint(int savedNestLevel) { + // 恢复提示设置到之前的状态 if (savedNestLevel < 0) { return; } @@ -1186,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; @@ -1259,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; @@ -1290,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); @@ -1304,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); @@ -1447,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; @@ -1669,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); @@ -1926,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."), @@ -1987,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."), @@ -2041,6 +2112,7 @@ void preprocess_const_params(PlannerInfo* root, Node* jtnode) } } + /* * preprocess_const_params_worker * worker func for params to const replacement @@ -2091,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 @@ -2184,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 */ @@ -2307,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 @@ -2427,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); /* @@ -2657,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. @@ -3072,151 +3101,130 @@ static Plan* grouping_planner(PlannerInfo* root, double tuple_fraction) * */ if (is_dummy_plan(result_plan) && parse->groupingSets == NIL && parse->groupClause != NIL) { - if (parse->hasAggs || parse->hasWindowFuncs) { - ListCell* lc = NULL; - foreach (lc, tlist) { - TargetEntry* tle = (TargetEntry*)lfirst(lc); - List* exprList = pull_var_clause( - (Node*)tle->expr, PVC_INCLUDE_AGGREGATES_OR_WINAGGS, PVC_RECURSE_PLACEHOLDERS); - ListCell* lc2 = NULL; - Node* node = NULL; - foreach (lc2, exprList) { - node = (Node*)lfirst(lc2); - if (IsA(node, Aggref) || IsA(node, GroupingFunc) || IsA(node, WindowFunc)) - break; - } - - /* - * For aggref, grouping or windows expr, we need replace them by NULL, else error will - * happen, because AggRef not in agg node. - */ - if (lc2 != NULL) { - tle->expr = (Expr*)makeNullConst(exprType(node), exprTypmod(node), exprCollation(node)); - } - list_free_ext(exprList); - } - } - result_plan->targetlist = tlist; - return result_plan; + // 检查是否为虚拟计划,并且没有分组集和存在分组子句 + if (parse->hasAggs || parse->hasWindowFuncs) { + // 如果查询中包含聚合函数或窗口函数 + ListCell* lc = NULL; + foreach (lc, tlist) { + TargetEntry* tle = (TargetEntry*)lfirst(lc); + // 提取目标项表达式中的变量 + List* exprList = pull_var_clause( + (Node*)tle->expr, PVC_INCLUDE_AGGREGATES_OR_WINAGGS, PVC_RECURSE_PLACEHOLDERS); + ListCell* lc2 = NULL; + Node* node = NULL; + foreach (lc2, exprList) { + node = (Node*)lfirst(lc2); + // 如果表达式包含聚合函数、GroupingFunc或WindowFunc,则将其替换为NULL常量 + if (IsA(node, Aggref) || IsA(node, GroupingFunc) || IsA(node, WindowFunc)) + break; } - if (use_hashed_grouping && list_length(agg_costs.exprAggs) == 1 && - (!is_execute_on_datanodes(result_plan) || is_replicated_plan(result_plan))) { - if (!grouping_is_sortable(parse->groupClause)) { - ereport(ERROR, - (errmodule(MOD_OPT_PLANNER), errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("GROUP BY cannot be implemented."), - errdetail("Some of the datatypes only support hashing, " - "while others only support sorting."), - errcause("GROUP BY uses unsupported datatypes."), - erraction("Modify SQL statement according to the manual."))); - } - - use_hashed_grouping = false; - if (sorted_path != NULL && sorted_path != cheapest_path) { - best_path = sorted_path; - result_plan = create_plan(root, best_path); - } + if (lc2 != NULL) { + tle->expr = (Expr*)makeNullConst(exprType(node), exprTypmod(node), exprCollation(node)); } - current_pathkeys = best_path->pathkeys; + list_free_ext(exprList); + } + } + result_plan->targetlist = tlist; + return result_plan; +} - /* Detect if we'll need an explicit sort for grouping */ - if (parse->groupClause && !use_hashed_grouping && - !pathkeys_contained_in(root->group_pathkeys, current_pathkeys)) { - need_sort_for_grouping = true; +if (use_hashed_grouping && list_length(agg_costs.exprAggs) == 1 && + (!is_execute_on_datanodes(result_plan) || is_replicated_plan(result_plan))) { + // 如果使用散列分组,且仅有一个聚合表达式,并且不在数据节点上执行计划或计划是复制计划 + if (!grouping_is_sortable(parse->groupClause)) { + // 如果分组子句不可排序,抛出错误 + ereport(ERROR, + (errmodule(MOD_OPT_PLANNER), errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("GROUP BY cannot be implemented."), + errdetail("Some of the datatypes only support hashing, " + "while others only support sorting."), + errcause("GROUP BY uses unsupported datatypes."), + erraction("Modify SQL statement according to the manual."))); + } - /* - * Always override create_plan's tlist, so that we don't sort - * useless data from a "physical" tlist. - */ - need_tlist_eval = true; - } + use_hashed_grouping = false; + if (sorted_path != NULL && sorted_path != cheapest_path) { + // 如果已经存在排序路径并且不是最便宜的路径,则使用排序路径 + best_path = sorted_path; + result_plan = create_plan(root, best_path); + } +} +current_pathkeys = best_path->pathkeys; - /* - * create_plan returns a plan with just a "flat" tlist of required - * Vars. Usually we need to insert the sub_tlist as the tlist of - * the top plan node. However, we can skip that if we determined - * that whatever create_plan chose to return will be good enough. - */ - if (need_tlist_eval) { - /* - * If the top-level plan node is one that cannot do expression - * evaluation, we must insert a Result node to project the - * desired tlist. - */ - if (!is_projection_capable_plan(result_plan) || - (is_vector_scan(result_plan) && vector_engine_unsupport_expression_walker((Node*)sub_tlist))) { - result_plan = (Plan*)make_result(root, sub_tlist, NULL, result_plan); - } else { - /* - * Otherwise, just replace the subplan's flat tlist with - * the desired tlist. - */ - result_plan->targetlist = sub_tlist; +if (parse->groupClause && !use_hashed_grouping && + !pathkeys_contained_in(root->group_pathkeys, current_pathkeys)) { + // 如果存在分组子句,不使用散列分组,并且路径键不包含在根的分组路径键中 + need_sort_for_grouping = true; - if (IsA(result_plan, PartIterator)) { - /* - * If is a PartIterator + Scan, push the PartIterator's - * tlist to Scan. - */ - result_plan->lefttree->targetlist = sub_tlist; - } + need_tlist_eval = true; +} + +if (need_tlist_eval) { + // 如果需要对目标列表进行评估 + if (!is_projection_capable_plan(result_plan) || + (is_vector_scan(result_plan) && vector_engine_unsupport_expression_walker((Node*)sub_tlist))) { + // 如果不支持投影计划或者是矢量扫描并且不支持的表达式,则创建Result计划 + result_plan = (Plan*)make_result(root, sub_tlist, NULL, result_plan); + } else { + // 否则设置计划的目标列表 + result_plan->targetlist = sub_tlist; + + if (IsA(result_plan, PartIterator)) { + // 如果是分区迭代器计划,也设置子计划的目标列表 + result_plan->lefttree->targetlist = sub_tlist; + } #ifdef PGXC - /* - * If the Join tree is completely shippable, adjust the - * target list of the query according to the new targetlist - * set above. For now do this only for SELECT statements. - */ - if (IsA(result_plan, RemoteQuery) && parse->commandType == CMD_SELECT && !permit_gather(root)) { - pgxc_rqplan_adjust_tlist( - root, (RemoteQuery*)result_plan, ((RemoteQuery*)result_plan)->is_simple ? false : true); - if (((RemoteQuery*)result_plan)->is_simple) - AssertEreport(((RemoteQuery*)result_plan)->sql_statement == NULL, - MOD_OPT, - "invalid sql statement of result plan when adjusting the targetlist of remote query."); - } -#endif /* PGXC */ - } + if (IsA(result_plan, RemoteQuery) && parse->commandType == CMD_SELECT && !permit_gather(root)) { + // 如果是分布式查询计划,设置其目标列表 + pgxc_rqplan_adjust_tlist( + root, (RemoteQuery*)result_plan, ((RemoteQuery*)result_plan)->is_simple ? false : true); + if (((RemoteQuery*)result_plan)->is_simple) + AssertEreport(((RemoteQuery*)result_plan)->sql_statement == NULL, + MOD_OPT, + "invalid sql statement of result plan when adjusting the targetlist of remote query."); + } +#endif + } - /* - * Also, account for the cost of evaluation of the sub_tlist. - * See comments for add_tlist_costs_to_plan() for more info. - */ - add_tlist_costs_to_plan(root, result_plan, sub_tlist); - } else { - /* - * Since we're using create_plan's tlist and not the one - * make_subplanTargetList calculated, we have to refigure any - * grouping-column indexes make_subplanTargetList computed. - * - * We don't want any excess columns for hashagg, since we support hashagg write-out-to-disk now - */ - if (use_hashed_grouping) - disuse_physical_tlist(result_plan, best_path); + // 将目标列表的成本添加到计划中 + add_tlist_costs_to_plan(root, result_plan, sub_tlist); +} else { + // 如果不需要对目标列表进行评估 + if (use_hashed_grouping) + disuse_physical_tlist(result_plan, best_path); - locate_grouping_columns(root, tlist, result_plan->targetlist, groupColIdx); - } + // 定位分组的列 + locate_grouping_columns(root, tlist, result_plan->targetlist, groupColIdx); +} #ifdef ENABLE_MULTIPLE_NODES - /* shuffle to another node group in FORCE mode (CNG_MODE_FORCE) */ - if (IS_STREAM_PLAN && !parse->hasForUpdate && - (parse->hasAggs || parse->groupClause != NIL || parse->groupingSets != NIL || - parse->distinctClause != NIL || parse->sortClause != NIL || - (wflists != NULL && wflists->activeWindows))) { - Plan* old_result_plan = result_plan; - List* groupcls = parse->groupClause; - Path* subpath = NULL; - bool can_shuffle = true; +// 检查是否启用多节点支持,并且当前计划是流式计划 +if (IS_STREAM_PLAN && !parse->hasForUpdate && + (parse->hasAggs || parse->groupClause != NIL || parse->groupingSets != NIL || + parse->distinctClause != NIL || parse->sortClause != NIL || + (wflists != NULL && wflists->activeWindows))) { + // 如果是流式计划且满足一定条件 - /* deal with window agg if no group clause */ - if (groupcls == NIL && wflists != NULL && wflists->activeWindows) { - WindowClause* wc1 = (WindowClause*)linitial(wflists->activeWindows); - groupcls = wc1->partitionClause; - /* need to reduce targetlist here if no group clause */ - subpath = best_path; + // 保存旧的计划,以备后续使用 + Plan* old_result_plan = result_plan; + List* groupcls = parse->groupClause; // 分组子句 + Path* subpath = NULL; // 子路径 + bool can_shuffle = true; // 是否可以进行shuffle操作 - /* not shuffle if partitionClause contains aggregates */ - can_shuffle = check_windowagg_can_shuffle(wc1->partitionClause, tlist); - } + // 如果没有分组子句,但存在活动窗口 + if (groupcls == NIL && wflists != NULL && wflists->activeWindows) { + // 获取第一个活动窗口的分区子句 + WindowClause* wc1 = (WindowClause*)linitial(wflists->activeWindows); + groupcls = wc1->partitionClause; + + // 子路径等于最佳路径 + subpath = best_path; + + // 检查窗口聚合是否可以进行shuffle操作 + can_shuffle = check_windowagg_can_shuffle(wc1->partitionClause, tlist); + } +} +#endif if (can_shuffle) result_plan = ng_agg_force_shuffle(root, groupcls, result_plan, tlist, subpath); @@ -3245,27 +3253,32 @@ static Plan* grouping_planner(PlannerInfo* root, double tuple_fraction) int i = 0; /* All take part in group columns */ - foreach (lc, parse->groupClause) { - SortGroupClause* gc = (SortGroupClause*)lfirst(lc); + foreach (lc, parse->groupClause) { + // 遍历分组子句 + SortGroupClause* gc = (SortGroupClause*)lfirst(lc); + + // 将分组子句的 tleSortGroupRef 映射到 groupColIdx 中的索引位置 + grouping_map[gc->tleSortGroupRef] = groupColIdx[i++]; +} - grouping_map[gc->tleSortGroupRef] = groupColIdx[i++]; - } +// 将构建好的 grouping_map 分配给 root +root->grouping_map = grouping_map; - root->grouping_map = grouping_map; +// 调用 build_groupingsets_plan 函数构建分组集合的计划 +result_plan = build_groupingsets_plan(root, + parse, + &tlist, + need_sort_for_grouping, + rollup_groupclauses, + rollup_lists, + &groupColIdx, + &agg_costs, + localNumGroup, + result_plan, + wflists, + &needSecondLevelAgg, + collectiveGroupExpr); - result_plan = build_groupingsets_plan(root, - parse, - &tlist, - need_sort_for_grouping, - rollup_groupclauses, - rollup_lists, - &groupColIdx, - &agg_costs, - localNumGroup, - result_plan, - wflists, - &needSecondLevelAgg, - collectiveGroupExpr); /* Delete eq class expr after grouping */ delete_eq_member(root, tlist, collectiveGroupExpr); @@ -3283,47 +3296,58 @@ static Plan* grouping_planner(PlannerInfo* root, double tuple_fraction) use_hashed_grouping = true; } else if (grouping_is_sortable(parse->groupClause)) { /* or do sortagg */ - use_hashed_grouping = false; - } else { - ereport(ERROR, - (errmodule(MOD_OPT_PLANNER), errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("GROUP BY cannot be implemented."), - errdetail("Some of the datatypes only support hashing, " - "while others only support sorting."), - errcause("GROUP BY uses unsupported datatypes."), - erraction("Modify SQL statement according to the manual."))); - } + // 检查是否需要使用基于哈希的分组,初始值为false +use_hashed_grouping = false; - if (IS_STREAM_PLAN && (is_hashed_plan(result_plan) || is_rangelist_plan(result_plan))) { - if (expression_returns_set((Node*)tlist)) { - errno_t sprintf_rc = sprintf_s(u_sess->opt_cxt.not_shipping_info->not_shipping_reason, - NOTPLANSHIPPING_LENGTH, - "set-valued function + groupingsets"); - securec_check_ss_c(sprintf_rc, "\0", "\0"); - mark_stream_unsupport(); - } +// 如果分组操作不支持哈希,则报错,说明某些数据类型只支持哈希或排序其中一种 +else { + ereport(ERROR, + (errmodule(MOD_OPT_PLANNER), errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("GROUP BY cannot be implemented."), + errdetail("Some of the datatypes only support hashing, " + "while others only support sorting."), + errcause("GROUP BY uses unsupported datatypes."), + erraction("Modify SQL statement according to the manual."))); +} - if (check_subplan_in_qual(tlist, result_plan->qual)) { - errno_t sprintf_rc = sprintf_s(u_sess->opt_cxt.not_shipping_info->not_shipping_reason, - NOTPLANSHIPPING_LENGTH, - "var in quals doesn't exist in targetlist"); - securec_check_ss_c(sprintf_rc, "\0", "\0"); - mark_stream_unsupport(); - } - } - } +// 如果当前计划是流式执行计划,并且计划是哈希计划或范围计划 +if (IS_STREAM_PLAN && (is_hashed_plan(result_plan) || is_rangelist_plan(result_plan))) { - if (IS_STREAM_PLAN) { - if (is_execute_on_coordinator(result_plan) || is_execute_on_allnodes(result_plan) || - is_replicated_plan(result_plan)) { - needs_stream = false; - } else { - needs_stream = needs_agg_stream(root, tlist, result_plan->distributed_keys, &result_plan->exec_nodes->distribution); - } + // 如果目标列表包含返回集合的表达式,则标记为不支持流式执行 + if (expression_returns_set((Node*)tlist)) { + errno_t sprintf_rc = sprintf_s(u_sess->opt_cxt.not_shipping_info->not_shipping_reason, + NOTPLANSHIPPING_LENGTH, + "set-valued function + groupingsets"); + securec_check_ss_c(sprintf_rc, "\0", "\0"); + mark_stream_unsupport(); + } + + // 检查目标列表中的子查询是否在过滤条件中使用,如果是则标记为不支持流式执行 + if (check_subplan_in_qual(tlist, result_plan->qual)) { + errno_t sprintf_rc = sprintf_s(u_sess->opt_cxt.not_shipping_info->not_shipping_reason, + NOTPLANSHIPPING_LENGTH, + "var in quals doesn't exist in targetlist"); + securec_check_ss_c(sprintf_rc, "\0", "\0"); + mark_stream_unsupport(); + } +} + +// 如果是流式执行计划 +if (IS_STREAM_PLAN) { + + // 如果计划在协调节点上执行、在所有节点上执行或是复制计划,不需要流式执行 + if (is_execute_on_coordinator(result_plan) || is_execute_on_allnodes(result_plan) || + is_replicated_plan(result_plan)) { + needs_stream = false; + } else { + // 否则,检查是否需要流式执行,根据目标列表、分布键和分布策略来判断 + needs_stream = needs_agg_stream(root, tlist, result_plan->distributed_keys, &result_plan->exec_nodes->distribution); + } #ifndef ENABLE_MULTIPLE_NODES - needs_stream = needs_stream && (result_plan->dop > 1); + // 如果不是多节点部署,且并行度大于1,也需要流式执行 + needs_stream = needs_stream && (result_plan->dop > 1); #endif - } +} /* * Insert AGG or GROUP node if needed, plus an explicit sort step @@ -3340,43 +3364,53 @@ static Plan* grouping_planner(PlannerInfo* root, double tuple_fraction) /* need do nothing*/ } else if (use_hashed_grouping) { /* Hashed aggregate plan --- no sort needed */ - if (IS_STREAM_PLAN && - is_execute_on_datanodes(result_plan) && - !is_replicated_plan(result_plan)) { - if (agg_costs.hasDnAggs || list_length(agg_costs.exprAggs) == 0) { - result_plan = generate_hashagg_plan(root, - result_plan, - tlist, - &agg_costs, - numGroupCols, - numGroups, - wflists, - groupColIdx, - extract_grouping_ops(parse->groupClause), - &needs_stream, - hash_entry_size, - AGG_LEVEL_1_INTENT, - rel_info); - } else { - Node *node = (Node *) linitial(agg_costs.exprAggs); - AssertEreport(list_length(agg_costs.exprAggs) == 1, - MOD_OPT, - "invalid length of distinct expression when generating plan for hashed aggregate."); + // 如果当前计划是流式执行计划,并且在数据节点上执行,但不是复制计划 +if (IS_STREAM_PLAN && + is_execute_on_datanodes(result_plan) && + !is_replicated_plan(result_plan)) { - result_plan = get_count_distinct_partial_plan(root, - result_plan, - &tlist, - node, - agg_costs, - numGroups, - wflists, - groupColIdx, - &needs_stream, - hash_entry_size, - rel_info); - } + // 如果聚合操作包含数据节点聚合或聚合函数数量为0 + if (agg_costs.hasDnAggs || list_length(agg_costs.exprAggs) == 0) { + + // 生成哈希聚合计划 + result_plan = generate_hashagg_plan(root, + result_plan, + tlist, + &agg_costs, + numGroupCols, + numGroups, + wflists, + groupColIdx, + extract_grouping_ops(parse->groupClause), + &needs_stream, + hash_entry_size, + AGG_LEVEL_1_INTENT, + rel_info); + } else { + // 如果聚合函数数量为1,获取第一个聚合函数节点 + Node *node = (Node *) linitial(agg_costs.exprAggs); + AssertEreport(list_length(agg_costs.exprAggs) == 1, + MOD_OPT, + "invalid length of distinct expression when generating plan for hashed aggregate."); + + // 生成部分计划以处理 COUNT(DISTINCT) 聚合 + result_plan = get_count_distinct_partial_plan(root, + result_plan, + &tlist, + node, + agg_costs, + numGroups, + wflists, + groupColIdx, + &needs_stream, + hash_entry_size, + rel_info); + } + + // 下一个计划是第二级分组 + next_is_second_level_group = true; +} - next_is_second_level_group = true; } else if (!parse->groupingSets) { /* * To Ap function, need not do hashagg if it is not stream plan, diff --git a/src/gausskernel/optimizer/plan/planrecursive_single.cpp b/src/gausskernel/optimizer/plan/planrecursive_single.cpp index 7c62d675a..3ad2add33 100644 --- a/src/gausskernel/optimizer/plan/planrecursive_single.cpp +++ b/src/gausskernel/optimizer/plan/planrecursive_single.cpp @@ -87,7 +87,7 @@ * * @Return: true: walk success false: failed ***/ -static List* getSpecialPlanSubNodes(const Plan* node) + static List* getSpecialPlanSubNodes(const Plan* node) { List* ps_list = NIL; @@ -95,57 +95,68 @@ static List* getSpecialPlanSubNodes(const Plan* node) return NIL; } - /* Find plan list in special plan nodes. */ switch (nodeTag(node)) { case T_Append: case T_VecAppend: { + // 如果是Append或VecAppend计划节点 Append* append = (Append*)node; ListCell* lc = NULL; foreach (lc, append->appendplans) { + // 遍历子计划节点列表 ps_list = lappend(ps_list, lfirst(lc)); } } break; case T_ModifyTable: case T_VecModifyTable: { + // 如果是ModifyTable或VecModifyTable计划节点 ModifyTable* mt = (ModifyTable*)node; ListCell* lc = NULL; foreach (lc, mt->plans) { + // 遍历子计划节点列表 ps_list = lappend(ps_list, lfirst(lc)); } } break; case T_SubqueryScan: case T_VecSubqueryScan: { + // 如果是SubqueryScan或VecSubqueryScan计划节点 SubqueryScan* ss = (SubqueryScan*)node; if (ss->subplan) { + // 如果有子查询计划节点,将其添加到列表中 ps_list = lappend(ps_list, (void*)ss->subplan); } } break; case T_MergeAppend: case T_VecMergeAppend: { + // 如果是MergeAppend或VecMergeAppend计划节点 MergeAppend* ma = (MergeAppend*)node; ListCell* lc = NULL; foreach (lc, ma->mergeplans) { + // 遍历子计划节点列表 ps_list = lappend(ps_list, lfirst(lc)); } } break; case T_BitmapAnd: case T_CStoreIndexAnd: { + // 如果是BitmapAnd或CStoreIndexAnd计划节点 BitmapAnd* ba = (BitmapAnd*)node; ListCell* lc = NULL; foreach (lc, ba->bitmapplans) { + // 遍历子计划节点列表 ps_list = lappend(ps_list, lfirst(lc)); } } break; - case T_BitmapOr: case T_CStoreIndexOr: { + // 如果是BitmapOr或CStoreIndexOr计划节点 BitmapOr* bo = (BitmapOr*)node; ListCell* lc = NULL; foreach (lc, bo->bitmapplans) { + // 遍历子计划节点列表 ps_list = lappend(ps_list, lfirst(lc)); } } break; default: { + // 对于其他类型的计划节点,将列表置为空 ps_list = NIL; } break; } @@ -156,6 +167,7 @@ static List* getSpecialPlanSubNodes(const Plan* node) inline static void setPlanNodeId(Plan* node, RecursiveRefContext* context, const RecursiveUnion* runode) { + // 设置计划节点的控制计划节点ID node->control_plan_nodeid = ((context->nested_stream_depth == 1) ? GET_PLAN_NODEID(runode) : GET_PLAN_NODEID(context->control_plan)); @@ -165,12 +177,9 @@ static void setRecursiveRteplanRefByType(Plan* node, RecursiveRefContext* contex const RecursiveUnion* runode) { switch (nodeTag(node)) { - /* - * If we found we have operators that do not support recursive-execution we - * mark the stream-recursive unsupported - */ case T_DfsScan: case T_DfsIndexScan: { + // 如果是DfsScan或DfsIndexScan计划节点 errno_t sprintf_rc = sprintf_s(u_sess->opt_cxt.not_shipping_info->not_shipping_reason, NOTPLANSHIPPING_LENGTH, "Recursive CTE contains DFS Scan is not shippable CTE plan node id[%d]", @@ -179,95 +188,71 @@ static void setRecursiveRteplanRefByType(Plan* node, RecursiveRefContext* contex mark_stream_unsupport(); } break; case T_Stream: { + // 如果是Stream计划节点 Stream* stream = (Stream*)node; elog(DEBUG1, "set plan ref for stream node %d", GET_PLAN_NODEID(stream)); - /* Remove LOCAL GATHER for Recursive, which is just lefttree of another normal Stream */ + // 移除递归控制器的局部收集器,如果存在 if (IsA(node->lefttree, Stream) && ((Stream*)node->lefttree)->is_recursive_local) { node->lefttree = node->lefttree->lefttree; } - /* If set control plan nodeid is required, we do reset Plan::control_plan_nodeid */ + // 如果需要设置控制计划节点ID,重置Plan::control_plan_nodeid if (context->set_control_plan_nodeid) { setPlanNodeId(node, context, runode); - /* - * After set current plan node, we need reset the "set_control_plan_nodeid" - * back to false. - */ + // 设置完当前计划节点后,重置"set_control_plan_nodeid"为false context->set_control_plan_nodeid = false; } - /* - * For stream node, we need set its first underlying plannode's - * control nodeid. - */ + // 设置控制计划节点的标志为true context->set_control_plan_nodeid = true; context->nested_stream_depth++; context->control_plan = node; - /* The first node is mared as syncnode */ + // 如果没有指定同步生产者并且不是递归局部流,则设置计划节点为同步计划节点 if (!context->is_syncup_producer_specified && !stream->is_recursive_local) { - /* mark current stream node as sync-node (consumer) */ node->is_sync_plannode = true; - - /* mark current stream's input node as sync-node (producer) */ node->lefttree->is_sync_plannode = true; - - /* mark we've done with marking sync-plannode */ context->is_syncup_producer_specified = true; } - /* Assign the stream level */ + // 设置流计划节点的层级 stream->stream_level = context->nested_stream_depth; - /* - * Mark the top-plannode under current Stream is controlled by RecursiveUnion - * awk. 1st level of recursvie-controlling - */ + // 如果流计划节点的嵌套深度大于1,将计划节点标记为递归联合控制器 if (context->nested_stream_depth > 1) { node->recursive_union_controller = true; } - /* set reference producer tree */ + // 递归设置左子树的计划节点引用 set_recursive_cteplan_ref(node->lefttree, context); - /* Reset the nestted level back */ + // 减少嵌套流计划深度 context->nested_stream_depth--; } break; case T_RecursiveUnion: { - /* Mark current conttroller */ + // 如果是RecursiveUnion计划节点 node->recursive_union_controller = true; - /* - * Set ru_plan_nodeid for recursive part - * - * Note: we do not have to set the non-recursive part, the underlying - * stream thread only runs one time, no need for cluster-step control. - */ + // 递归设置内部计划节点引用 set_recursive_cteplan_ref(innerPlan(node), context); } break; default: { - /* If set control plan nodeid is required, we do reset Plan::control_plan_nodeid */ + // 对于其他类型的计划节点,设置计划节点的引用 if (context->set_control_plan_nodeid) { setPlanNodeId(node, context, runode); - - /* - * After set current plan node, we need reset the "set_control_plan_nodeid" - * back to false. - */ context->set_control_plan_nodeid = false; } - /* set recursive cteplan node id for its underlying left and right tree */ Plan* lplan = outerPlan(node); Plan* rplan = innerPlan(node); + // 根据连接类型决定遍历左右子计划节点的顺序 if (context->join_type == T_HashJoin || context->join_type == T_VecHashJoin) { if (lplan != NULL) { set_recursive_cteplan_ref(lplan, context); } - if (rplan != NULL) { set_recursive_cteplan_ref(rplan, context); } @@ -275,7 +260,6 @@ static void setRecursiveRteplanRefByType(Plan* node, RecursiveRefContext* contex if (rplan != NULL) { set_recursive_cteplan_ref(rplan, context); } - if (lplan != NULL) { set_recursive_cteplan_ref(lplan, context); } @@ -292,92 +276,97 @@ void set_recursive_cteplan_ref(Plan* node, RecursiveRefContext* context) const RecursiveUnion* runode = context->ru_plan; Assert(runode != NULL && IsA(runode, RecursiveUnion) && context != NULL); +} + + + /* * We won't build the recursive control flow as a correlated recursive UNION have * to be executed in one DN */ - if (runode->is_correlated) { - return; - } + // 检查是否为 correlated RecursiveUnion +if (runode->is_correlated) { + return; // 如果是 correlated,不需要执行后续操作 +} - /* Bind recursive union plannodeid for each underlying operators */ - node->recursive_union_plan_nodeid = GET_PLAN_NODEID(runode); +// 设置当前节点的 PlanNodeID 为 RecursiveUnion 的 PlanNodeID +node->recursive_union_plan_nodeid = GET_PLAN_NODEID(runode); - /* First, assign join type of nearest node */ - if (nodeTag(node) == T_HashJoin || nodeTag(node) == T_VecHashJoin || nodeTag(node) == T_MergeJoin || - nodeTag(node) == T_VecMergeJoin || nodeTag(node) == T_NestLoop || nodeTag(node) == T_VecNestLoop) { - context->join_type = nodeTag(node); - } +// 检查当前节点的类型,如果是一些特定的关联节点,则设置 join_type +if (nodeTag(node) == T_HashJoin || nodeTag(node) == T_VecHashJoin || nodeTag(node) == T_MergeJoin || + nodeTag(node) == T_VecMergeJoin || nodeTag(node) == T_NestLoop || nodeTag(node) == T_VecNestLoop) { + context->join_type = nodeTag(node); +} - /* Second, Process regular nodes */ - setRecursiveRteplanRefByType(node, context, runode); +// 设置递归引用的 RTE 表达式引用关系 +setRecursiveRteplanRefByType(node, context, runode); - /* Third, process the plannode is not consists of left and right tree */ - List* special_subnodes = getSpecialPlanSubNodes(node); - if (special_subnodes != NIL) { - ListCell* lc = NULL; - foreach (lc, special_subnodes) { - Plan* subnode = (Plan*)lfirst(lc); - set_recursive_cteplan_ref(subnode, context); - } - } - - /* find all subplan exprs from main plan */ - List* subplan_list = check_subplan_list(node); +// 获取当前节点的特殊子节点 +List* special_subnodes = getSpecialPlanSubNodes(node); +if (special_subnodes != NIL) { ListCell* lc = NULL; - - foreach (lc, subplan_list) { - Node* localNode = (Node*)lfirst(lc); - Plan* plan = NULL; - SubPlan* subplan = NULL; - - if (IsA(localNode, SubPlan)) { - subplan = (SubPlan*)lfirst(lc); - /* this is for the case that initplan hidden in testexpr of subplan */ - subplan_list = list_concat(subplan_list, check_subplan_expr(subplan->testexpr)); - } else { - Assert(IsA(localNode, Param)); - Param* param = (Param*)localNode; - ListCell* lc2 = NULL; - foreach (lc2, context->initplans) { - subplan = (SubPlan*)lfirst(lc2); - if (list_member_int(subplan->setParam, param->paramid)) - break; - } - if (subplan == NULL || lc2 == NULL) - continue; - } - - plan = (Plan*)list_nth(context->subplans, subplan->plan_id - 1); - set_recursive_cteplan_ref(plan, context); + foreach (lc, special_subnodes) { + Plan* subnode = (Plan*)lfirst(lc); + set_recursive_cteplan_ref(subnode, context); } } +// 获取当前节点的子查询计划列表 +List* subplan_list = check_subplan_list(node); +ListCell* lc = NULL; + +foreach (lc, subplan_list) { + Node* localNode = (Node*)lfirst(lc); + Plan* plan = NULL; + SubPlan* subplan = NULL; + + if (IsA(localNode, SubPlan)) { + subplan = (SubPlan*)lfirst(lc); + + // 将 SubPlan 的测试表达式加入到 subplan_list 中 + subplan_list = list_concat(subplan_list, check_subplan_expr(subplan->testexpr)); + } else { + Assert(IsA(localNode, Param)); + Param* param = (Param*)localNode; + ListCell* lc2 = NULL; + + // 查找 Param 对应的 SubPlan + foreach (lc2, context->initplans) { + subplan = (SubPlan*)lfirst(lc2); + if (list_member_int(subplan->setParam, param->paramid)) + break; + } + + // 如果找到 SubPlan,则继续处理 + if (subplan == NULL || lc2 == NULL) + continue; + } + + // 根据 SubPlan 的计划 ID 获取相应的计划节点 + plan = (Plan*)list_nth(context->subplans, subplan->plan_id - 1); + set_recursive_cteplan_ref(plan, context); +} +} + +// 判断当前线程是否为 SyncUp Producer 线程 bool IsSyncUpProducerThread() { DISTRIBUTED_FEATURE_NOT_SUPPORTED(); return false; } -/* - * @Function: NeedSetupSyncUpController() - * - * @Brief: Invokded at ExecInitNode()funciton, to identify if we have to set up the step - * controller to do step-syncup across the whole cluster - * - * @Input plan: the plan node need verify to create controller at plan-init stage - * - * @Return: True/False to indicate if we need set up controller - ***/ +// 判断是否需要设置 SyncUp Controller bool NeedSetupSyncUpController(Plan* plan) { bool result = false; + // 如果是协调节点或者全局对象为空,则不需要设置 SyncUp Controller if (IS_PGXC_COORDINATOR || u_sess->stream_cxt.global_obj == NULL) { return false; } + // 确保当前节点是数据节点且全局对象不为空 Assert(IS_PGXC_DATANODE && u_sess->stream_cxt.global_obj != NULL && plan != NULL); switch (nodeTag(plan)) { @@ -389,11 +378,9 @@ bool NeedSetupSyncUpController(Plan* plan) RecursiveUnion* ruplan = (RecursiveUnion*)plan; if (ruplan->is_correlated) { - /* No recursive controller should be setup in a correlated recursive CTE */ result = false; Assert(!plan->recursive_union_controller); } else if (ruplan->has_inner_stream) { - /* Setup controller if recursive-union's inner plan has stream operator */ result = true; Assert(plan->recursive_union_controller); } @@ -410,19 +397,10 @@ bool NeedSetupSyncUpController(Plan* plan) return result; } -/* - * @Function: NeedSyncUpRecursiveUnionStep() - * - * @Brief: Invokded at ExecRecursiveUnion(), to identify if we have to set up the step - * controller to do step-syncup across the whole cluster - * - * @Input plan: the plan node need verify to create controller at plan-init stage - * - * @Return: True/False to indicate if we need do sync-up(Consumer) - ***/ +// 判断是否需要同步 RecursiveUnion 步骤 bool NeedSyncUpRecursiveUnionStep(Plan* plan) { - /* we don't have to do distributed step sync-up on coordinator node */ + // 如果是协调节点,不需要同步 if (IS_PGXC_COORDINATOR) { return false; } @@ -434,7 +412,6 @@ bool NeedSyncUpRecursiveUnionStep(Plan* plan) RecursiveUnion* ruplan = (RecursiveUnion*)plan; if (ruplan->is_correlated) { - /* We don't have to do recursive step syncup in correlated RCTE */ result = false; Assert(!plan->recursive_union_controller); } else if (ruplan->has_inner_stream) { @@ -450,18 +427,10 @@ bool NeedSyncUpRecursiveUnionStep(Plan* plan) return result; } -/* - * @Function: NeedSyncUpProducerStep() - * - * @Brief: Invokded at ExecutePlan(), to identify if current plan need sync-up - * - * @Input plan: the plan node need plan sync-up check - * - * @Return: True/False to indicate whether we need do sync-up - ***/ +// 判断是否需要同步 Producer 步骤 bool NeedSyncUpProducerStep(Plan* top_plan) { - /* we don't have to do distributed step sync-up on coordinator node */ + // 如果是协调节点,不需要同步 if (IS_PGXC_COORDINATOR) { return false; } @@ -469,6 +438,7 @@ bool NeedSyncUpProducerStep(Plan* top_plan) return EXEC_IN_RECURSIVE_MODE(top_plan); } +// 标记递归联合计划中的流计划 void mark_stream_recursiveunion_plan( RecursiveUnion* runode, Plan* node, bool recursive_branch, List* subplans, List** initplans) { @@ -476,10 +446,11 @@ void mark_stream_recursiveunion_plan( return; } + // 如果节点有初始化计划,将其添加到 initplans 中 if (node->initPlan) *initplans = list_concat(*initplans, list_copy(node->initPlan)); - /* if we found both side stream is marked we are done */ + // 如果 RecursiveUnion 已经包含内外流,不需要继续标记 if (runode->has_inner_stream && runode->has_outer_stream) { return; } @@ -487,17 +458,18 @@ void mark_stream_recursiveunion_plan( Assert(IsA(runode, RecursiveUnion)); ListCell* lc = NULL; - /* First, process the plannode is not consists of left and right tree */ + + // 获取当前节点的特殊子节点,通常是连接节点 List* special_subnodes = getSpecialPlanSubNodes(node); if (special_subnodes != NIL) { foreach (lc, special_subnodes) { Plan* subnode = (Plan*)lfirst(lc); + // 递归标记特殊子节点 mark_stream_recursiveunion_plan(runode, subnode, recursive_branch, subplans, initplans); } } - /* Sedondary, Process regular nodes */ switch (nodeTag(node)) { case T_RecursiveUnion: { Plan* lplan = (Plan*)outerPlan(node); @@ -505,40 +477,41 @@ void mark_stream_recursiveunion_plan( Assert(lplan != NULL && rplan != NULL); - /* iterative left tree */ + // 标记递归联合计划的左子计划 mark_stream_recursiveunion_plan((RecursiveUnion*)node, lplan, false, subplans, initplans); - /* iterative right tree */ + // 标记递归联合计划的右子计划 mark_stream_recursiveunion_plan((RecursiveUnion*)node, rplan, true, subplans, initplans); } break; case T_Stream: case T_VecStream: if (recursive_branch && !runode->has_inner_stream) { - /* mark iner side has stream */ + // 如果是递归联合的内流,标记内流存在 runode->has_inner_stream = true; } else if (!recursive_branch && !runode->has_outer_stream) { - /* mark outer side has stream */ + // 如果是递归联合的外流,标记外流存在 runode->has_outer_stream = true; } break; default: { - /* set recursive cteplan node id for its underlying left and right tree */ Plan* lplan = outerPlan(node); Plan* rplan = innerPlan(node); - /* iterative left tree */ + // 递归标记左子计划 mark_stream_recursiveunion_plan(runode, lplan, recursive_branch, subplans, initplans); - /* iterative right tree */ + // 递归标记右子计划 mark_stream_recursiveunion_plan(runode, rplan, recursive_branch, subplans, initplans); } } - /* Finally, Process subplan and initplan */ + // 获取当前节点的子查询计划列表 List* subplan_list = getSubPlan(node, subplans, *initplans); foreach (lc, subplan_list) { Plan* subnode = (Plan*)lfirst(lc); + + // 递归标记子查询计划 mark_stream_recursiveunion_plan(runode, subnode, recursive_branch, subplans, initplans); } diff --git a/src/gausskernel/optimizer/plan/planrewrite.cpp b/src/gausskernel/optimizer/plan/planrewrite.cpp index f940a683a..897025d60 100644 --- a/src/gausskernel/optimizer/plan/planrewrite.cpp +++ b/src/gausskernel/optimizer/plan/planrewrite.cpp @@ -55,15 +55,20 @@ /* Macros used in inlist2join rewrite */ /* The varno for base rel in subquery relpacement */ const int SUBQUERY_VARNO = 1; +// 定义子查询中的变量编号常量 + /* The var attno for subquery */ const AttrNumber SUBQUERY_ATTNO = 1; +// 定义子查询中的属性编号常量 /* The varno for valuescan */ const int VALUES_SUBLINK_VARNO = 1; + // 定义子查询中值表达式的变量编号常量 /* The varattno for valuescan */ const AttrNumber VALUES_SUBLINK_VARATTNO = 1; +// 定义子查询中值表达式的属性编号常量 /* * ----------------------------------------------------------------------------- @@ -76,9 +81,9 @@ const AttrNumber VALUES_SUBLINK_VARATTNO = 1; * Brief: Data structure to support find fix-var case on parsetree and plannode */ typedef struct fix_var_context { - PlannerInfo* root; - bool oldtonew; -} fix_var_context; + PlannerInfo* root;// 查询优化器的上下文信息 + bool oldtonew;// 用于标识是否将变量从旧值更新为新值 +} fix_var_context;// 用于在表达式中修复变量的上下文结构体 /* * Name: find_inlist2join_context @@ -86,8 +91,8 @@ typedef struct fix_var_context { * Brief: Data structure to support find inlist2join converted SubQueryScan pathnode */ typedef struct find_inlist2join_context { - PlannerInfo* root; -} find_inlist2join_context; + PlannerInfo* root; // 查询优化器的上下文信息 +} find_inlist2join_context; // 用于在查询计划路径中查找Inlist到Join优化的上下文结构体 /* * Name: fix_subquery_vars_context @@ -96,30 +101,55 @@ typedef struct find_inlist2join_context { * in inlist2join QRW optimization optimization. */ typedef struct fix_subquery_vars_context { - Var* v; - Index old_varno; - Index new_varno; -} fix_subquery_vars_context; + Var* v; // 表达式中的变量 + Index old_varno; // 旧变量编号 + Index new_varno;// 新变量编号 +} fix_subquery_vars_context; // 用于修复���询中的变量的上下文结构体 static bool fix_var_expr_walker(Node* node, fix_var_context* context); +// 递归遍历表达式树,修复其中的变量,返回是否成功的布尔值 static void fix_var(const List* mappings, Var* old_var, bool oldtonew); +// 在表达式中修复变量,使用给定的映射关系 static void find_inlist2join_path_walker(Path* path, find_inlist2join_context* context); +// 遍历查询计划路径,查找Inlist到Join优化的机会 static bool belowInlist2JoinThreshold(const RelOptInfo* rel, int num, RelOrientation orientation); +// 检查是否在Inlist到Join优化的阈值以下 static Var* fix_subquery_vars_expr(Node* expr, Index old_varno, Index new_varno); +// 修复子查询中的变量,返回修复后的变量 static bool fix_subquery_vars_expr_walker(Node* expr, fix_subquery_vars_context* context); -static void fix_var_expr(PlannerInfo* root, Node* node, bool oldtonew = true); +// 递归遍历表达式树,修复子查询中的变量 +static void fix_var_expr(PlannerInfo* root, Node* node, bool oldtonew = true);、 +// 修复表达式中的变量,可选择是否将变量从旧值更新为新值 + static void rebuild_subquery(PlannerInfo* root, RelOptInfo* rel, RangeTblEntry* rte, RangeTblEntry* new_rte); +// 重建子查询的查询计划 + static RangeTblEntry* make_rte_with_subquery(PlannerInfo* root, RelOptInfo* rel, RangeTblEntry* rte); +// 创建包含子查询的RangeTblEntry static bool IsConvertableBaseRel(const RangeTblEntry* rte); +// 检查是否可以将基本关系转换为Join关系 static bool IsConvertableInlistRestrict(RelOptInfo* rel, const RestrictInfo* restrict, RelOrientation orientation); +// 检查是否可以将Inlist约束条件转换为Join关系 static bool HasConvertableInlistCond(RelOptInfo* rel); +// 检查是否存在可以转换的Inlist约束条件 + static int ConvertableInlistMaxNum(const RelOptInfo* rel); +// 获取可以转换的Inlist约束条件的最大数量 static bool IsEqualOpr(Oid opno); +// 检查操作符是否为相等操作符 + static bool inline IsConvertableType(Oid typoid); +// 检查是否可以转换为特定类型 static List* convert_constarray_to_simple(Const* combined_const, Var* listvar, Oid* paramtype); +// 将常量数组转换为简单表达式列表 + static char* get_attr_name(int attrnum); +// 获取属性名称 static void rebuild_subquery(PlannerInfo* root, RelOptInfo* rel, RangeTblEntry* rte, RangeTblEntry* new_rte); +// 重建子查询的查询计划 + RelOptInfo* build_alternative_rel(const RelOptInfo* origin, RTEKind rtekind); +// 构建替代的查询计划 /***************************************************************************** * @@ -138,21 +168,27 @@ RelOptInfo* build_alternative_rel(const RelOptInfo* origin, RTEKind rtekind); *****************************************************************************/ void inlist2join_qrw_optimization(PlannerInfo* root, int rti) { + // 获取与查询计划有关的关系和表格入口 RelOptInfo* rel = root->simple_rel_array[rti]; RangeTblEntry* rte = root->simple_rte_array[rti]; + // 确保关系类型为基本关系 Assert(rel->reloptkind == RELOPT_BASEREL); + // 获取可以转换为Join的Inlist约束条件的最大数量 int maxnum = ConvertableInlistMaxNum(rel); + // 如果Inlist到Join优化被禁用,则退出 /* No need process more when inlist2join is disabled */ if (u_sess->opt_cxt.qrw_inlist2join_optmode == QRW_INLIST2JOIN_DISABLE) { return; } + /* Add inlist2join threshold check in case of CBO to protect inaccurate cost estimation * when inlist has small num of elements */ + // 如果启用了Inlist到Join优化,并且当前关系满足阈值要求,则退出 if (u_sess->opt_cxt.qrw_inlist2join_optmode == QRW_INLIST2JOIN_CBO && belowInlist2JoinThreshold(rel, maxnum, rel->orientation)) { return; @@ -163,6 +199,7 @@ void inlist2join_qrw_optimization(PlannerInfo* root, int rti) */ if (rte->securityQuals != NULL) { return; + // 如果关系具有安全性限制,则退出 } /* @@ -173,6 +210,7 @@ void inlist2join_qrw_optimization(PlannerInfo* root, int rti) * [4]. When current rel's RTE kind is not Relation&SubQuery */ /* Do not apply inlist2join if SQL command is not SELECT */ + // 如果查询不是SELECT类型,则退出 if (root->parse->commandType != CMD_SELECT) { return; } @@ -181,31 +219,37 @@ void inlist2join_qrw_optimization(PlannerInfo* root, int rti) if (rel->rtekind != RTE_RELATION && rel->rtekind != RTE_SUBQUERY) { return; } + // 如果关系既不是基本关系也不是子查询关系,则退出 /* Do not apply inlist2join if table is HDFS table or Foreign table */ if (rel->rtekind == RTE_RELATION && !IsConvertableBaseRel(rte)) { return; } + // 如果关系是基本关系但不可转换为Join关系,则退出 /* Do not apply inlist2join if the rel has no inlist2join baserestrictinfo */ if (!HasConvertableInlistCond(rel)) { return; } + // 如果关系中没有可以转换的Inlist约束条件,则退出 /* Build new reloptinfo (SubQuery) and set it as "alternative" */ + // 构建替代的查询计划 RelOptInfo* new_rel = build_alternative_rel(rel, RTE_SUBQUERY); rel->alternatives = lappend(rel->alternatives, new_rel); /* Build new rte (SubQuery) */ + // 创建包含子查询的RangeTblEntry RangeTblEntry* new_rte = make_rte_with_subquery(root, new_rel, rte); /* Add a joinpath(inlist2join) for current rel */ set_rel_size(root, new_rel, rti, new_rte); + // 设置新关系的大小和成本估算 /* Append subquery's pathlist to current base rel */ rel->pathlist = list_concat(rel->pathlist, new_rel->pathlist); } - +// 将新关系的查询路径添加到旧关系的路径列表中 /* * Name: fix_skew_expr() * @@ -216,11 +260,13 @@ void inlist2join_qrw_optimization(PlannerInfo* root, int rti) */ static void fix_skew_expr(PlannerInfo* root, const List* skew_list) { + // 如果倾斜列表为空,直接返回 if (skew_list == NIL) return; ListCell* lc = NULL; foreach (lc, skew_list) { + // 逐个修复倾斜表达式中的变量 QualSkewInfo* qsinfo = (QualSkewInfo*)lfirst(lc); fix_var_expr(root, (Node*)qsinfo->skew_quals); } @@ -233,21 +279,25 @@ static void fix_skew_expr(PlannerInfo* root, const List* skew_list) */ static bool fix_subquery_vars_expr_walker(Node* expr, fix_subquery_vars_context* context) { + // 如果表达式为空,返回false if (expr == NULL) { return false; } /* Fix var node */ + // 如果表达式是变量 if (IsA(expr, Var)) { Var* v = (Var*)expr; + // 如果变量的编号与旧变量编号匹配,将其更新为新变量编号 if (v->varno == context->old_varno) { v->varno = context->new_varno; } + // 更新上下文中的变量 context->v = v; } - +// 递归遍历表达式树 return expression_tree_walker(expr, (bool (*)())fix_subquery_vars_expr_walker, (void*)context); } @@ -269,13 +319,16 @@ static Var* fix_subquery_vars_expr(Node* expr, Index old_varno, Index new_varno) return NULL; } + // 如果表达式为空,返回NULL + fix_subquery_vars_context ctx; + // 创建修复子查询变量的上下文 ctx.v = NULL; ctx.old_varno = old_varno; ctx.new_varno = new_varno; - + // 使用上下文修复表达式中的变量 (void)fix_subquery_vars_expr_walker(expr, &ctx); - +// 返回修复后的变量 return ctx.v; } @@ -286,19 +339,21 @@ static Var* fix_subquery_vars_expr(Node* expr, Index old_varno, Index new_varno) */ static bool fix_var_expr_walker(Node* node, fix_var_context* context) { - PlannerInfo* root = context->root; + PlannerInfo* root = context->root;// 获取上下文中的查询优化器信息 if (node == NULL) { - return false; + return false;// 如果节点为空,返回false } /* Fix var node */ if (IsA(node, Var)) { fix_var(root->var_mappings, (Var*)node, context->oldtonew); + // 如果节点是变量,调用fix_var函数修复它 } return expression_tree_walker(node, (bool (*)())fix_var_expr_walker, (void*)context); } + // 递归遍历表达式树 /* * Name: fix_var_expr() @@ -315,10 +370,11 @@ static bool fix_var_expr_walker(Node* node, fix_var_context* context) static void fix_var_expr(PlannerInfo* root, Node* node, bool oldtonew) { fix_var_context ctx; - ctx.root = root; - ctx.oldtonew = oldtonew; + ctx.root = root;// 设置上下文中的查询优化器信息 + ctx.oldtonew = oldtonew;// 设置是否将变量从旧值更新为新值 - (void)fix_var_expr_walker(node, &ctx); + + (void)fix_var_expr_walker(node, &ctx);// 调用fix_var_expr_walker函数修复表达式中的变量 } /* @@ -337,24 +393,27 @@ static void fix_var(const List* mappings, Var* old_var, bool oldtonew) { ListCell* lc = NULL; if (mappings == NULL) { - return; + return;// 如果映射列表为空,直接返回 } foreach (lc, mappings) { RewriteVarMapping* rvm = (RewriteVarMapping*)lfirst(lc); if (oldtonew && equal(rvm->old_var, old_var)) { - old_var->varno = rvm->new_var->varno; - old_var->varattno = rvm->new_var->varattno; + // 如果需要将变量从旧值更新为新值,并且找到匹配的旧变量 + old_var->varno = rvm->new_var->varno;// 更新变量的编号为新值的编号 + old_var->varattno = rvm->new_var->varattno;// 更新变量的属性编号为新值的属性编号 - break; + + break; // 停止遍历映射列表 } else if (!oldtonew && equal(rvm->new_var, old_var)) { - old_var->varno = rvm->old_var->varno; - old_var->varattno = rvm->old_var->varattno; + // 如果需要将变量从新值更新为旧值,并且找到匹配的新变量 + old_var->varno = rvm->old_var->varno; // 更新变量的编号为旧值的编号 + old_var->varattno = rvm->old_var->varattno; // 更新变量的属性编号为旧值的属性编号 break; } } - return; + return;// 返回修复后的变量 } /* @@ -372,18 +431,19 @@ void fix_vars_plannode(PlannerInfo* root, Plan* node) { Assert(u_sess->opt_cxt.qrw_inlist2join_optmode != QRW_INLIST2JOIN_DISABLE && root->var_mappings); - if (node == NULL) { + if (node == NULL) {// 如果节点为空,直接返回 return; } /* Pass 1: Fix plan target list and qual */ - fix_var_expr(root, (Node*)node->targetlist); - fix_var_expr(root, (Node*)node->qual); + fix_var_expr(root, (Node*)node->targetlist);// 修复查询计划节点的目标列表 + fix_var_expr(root, (Node*)node->qual);// 修复查询计划节点的条件 + /* For subplan, it is create from base_rel, so we fix the var attno here */ if (IsA(node, SubqueryScan)) { - fix_var_expr(root, (Node*)node->distributed_keys); - return; + fix_var_expr(root, (Node*)node->distributed_keys); // 修复子查询节点的分布键 + return; //返回 } /* Pass 2: Fix plan specific nodes */ @@ -394,12 +454,12 @@ void fix_vars_plannode(PlannerInfo* root, Plan* node) HashJoin* hj = (HashJoin*)node; /* Fix Vars in *Hash* clause */ - fix_var_expr(root, (Node*)hj->hashclauses); + fix_var_expr(root, (Node*)hj->hashclauses); // 修复Hash Join的Hash条件 /* Fix Vars in *Join* clause */ - fix_var_expr(root, (Node*)hj->join.joinqual); - fix_var_expr(root, (Node*)hj->join.nulleqqual); - fix_var_expr(root, (Node*)node->var_list); + fix_var_expr(root, (Node*)hj->join.joinqual);// 修复Hash Join的Join条件 + fix_var_expr(root, (Node*)hj->join.nulleqqual); // 修复Hash Join的Null条件 + fix_var_expr(root, (Node*)node->var_list);// 修复查询计划节点的变量列表 } break; @@ -408,49 +468,49 @@ void fix_vars_plannode(PlannerInfo* root, Plan* node) NestLoop* nl = (NestLoop*)node; foreach (lc, nl->nestParams) { NestLoopParam* nlp = (NestLoopParam*)lfirst(lc); - fix_var_expr(root, (Node*)nlp->paramval); + fix_var_expr(root, (Node*)nlp->paramval);// 修复嵌套循环节点的参数值 } - fix_var_expr(root, (Node*)nl->join.joinqual); - fix_var_expr(root, (Node*)nl->join.nulleqqual); - fix_var_expr(root, (Node*)node->var_list); + fix_var_expr(root, (Node*)nl->join.joinqual);// 修复嵌套循环的Join条件 + fix_var_expr(root, (Node*)nl->join.nulleqqual);// 修复嵌套循环的Null条件 + fix_var_expr(root, (Node*)node->var_list);// 修复查询计划节点的变量列表 } break; case T_MergeJoin: case T_VecMergeJoin: { MergeJoin* mj = (MergeJoin*)node; - fix_var_expr(root, (Node*)mj->mergeclauses); + fix_var_expr(root, (Node*)mj->mergeclauses);// 修复Merge Join的Merge条件 /* Fix Vars in *Join* clause */ - fix_var_expr(root, (Node*)mj->join.joinqual); - fix_var_expr(root, (Node*)mj->join.nulleqqual); - fix_var_expr(root, (Node*)node->var_list); + fix_var_expr(root, (Node*)mj->join.joinqual); // 修复Merge Join的Join条件 + fix_var_expr(root, (Node*)mj->join.nulleqqual); // 修复Merge Join的Null条件 + fix_var_expr(root, (Node*)node->var_list); // 修复查询计划节点的变量列表 } break; case T_Stream: case T_VecStream: { Stream* sj = (Stream*)node; - fix_var_expr(root, (Node*)sj->distribute_keys); - fix_skew_expr(root, sj->skew_list); + fix_var_expr(root, (Node*)sj->distribute_keys); // 修复流节点的分布键 + fix_skew_expr(root, sj->skew_list); // 修复流节点的分布键 } break; case T_RemoteQuery: case T_VecRemoteQuery: { RemoteQuery* rq = (RemoteQuery*)node; - fix_var_expr(root, (Node*)rq->base_tlist); + fix_var_expr(root, (Node*)rq->base_tlist); // 修复远程查询节点的基本目标列表 } break; case T_Limit: case T_VecLimit: { Limit* lm = (Limit*)node; - fix_var_expr(root, lm->limitCount); - fix_var_expr(root, lm->limitOffset); + fix_var_expr(root, lm->limitCount); // 修复Limit节点的限制计数 + fix_var_expr(root, lm->limitOffset);// 修复Limit节点的限制偏移 } break; case T_VecWindowAgg: case T_WindowAgg: { WindowAgg* wa = (WindowAgg*)node; - fix_var_expr(root, wa->startOffset); - fix_var_expr(root, wa->endOffset); + fix_var_expr(root, wa->startOffset); // 修复窗口聚合节点的起始偏移 + fix_var_expr(root, wa->endOffset);// 修复窗口聚合节点的结束偏移 } break; case T_BaseResult: case T_VecResult: { BaseResult* br = (BaseResult*)node; - fix_var_expr(root, br->resconstantqual); + fix_var_expr(root, br->resconstantqual);// 修复基本结果节点的常量条件 } break; case T_ModifyTable: case T_VecModifyTable: { @@ -458,12 +518,12 @@ void fix_vars_plannode(PlannerInfo* root, Plan* node) if (mt->mergeActionList != NIL && (IS_STREAM_PLAN || IS_SINGLE_NODE)) { foreach (lc, mt->mergeActionList) { MergeAction* action = (MergeAction*)lfirst(lc); - fix_var_expr(root, (Node*)action->targetList); - fix_var_expr(root, (Node*)action->qual); + fix_var_expr(root, (Node*)action->targetList);// 修复修改表节点中的目标列表 + fix_var_expr(root, (Node*)action->qual);// 修复修改表节点中的条件 } } foreach (lc, mt->plans) { - fix_vars_plannode(root, (Plan*)lfirst(lc)); + fix_vars_plannode(root, (Plan*)lfirst(lc));// 递归修复计划节点 } /* Adjust references of remote query nodes in ModifyTable node */ if (IS_PGXC_COORDINATOR && !IsConnFromCoord()) { @@ -478,9 +538,9 @@ void fix_vars_plannode(PlannerInfo* root, Plan* node) * If base_tlist is set, it means that we have a reduced remote * query plan. So need to set the var references accordingly. */ - fix_var_expr(root, (Node*)rq->scan.plan.targetlist); - fix_var_expr(root, (Node*)rq->scan.plan.qual); - fix_var_expr(root, (Node*)rq->base_tlist); + fix_var_expr(root, (Node*)rq->scan.plan.targetlist); // 修复远程查询节点的目标列表 + fix_var_expr(root, (Node*)rq->scan.plan.qual);// 修复远程查询节点的条件 + fix_var_expr(root, (Node*)rq->base_tlist);// 修复远程查询节点的基本目标列表 } } } @@ -489,13 +549,13 @@ void fix_vars_plannode(PlannerInfo* root, Plan* node) case T_VecAppend: { Append* ap = (Append*)node; foreach (lc, ap->appendplans) { - fix_vars_plannode(root, (Plan*)lfirst(lc)); + fix_vars_plannode(root, (Plan*)lfirst(lc));// 递归修复计划节点 } } break; case T_MergeAppend: { MergeAppend* ma = (MergeAppend*)node; foreach (lc, ma->mergeplans) { - fix_vars_plannode(root, (Plan*)lfirst(lc)); + fix_vars_plannode(root, (Plan*)lfirst(lc));// 递归修复计划节点 } } break; default: @@ -520,21 +580,22 @@ void fix_vars_plannode(PlannerInfo* root, Plan* node) */ void find_inlist2join_path(PlannerInfo* root, Path* best_path) { - find_inlist2join_context ctx; - ctx.root = root; - List* var_mapping_new = NIL; - ListCell* lc = NULL; + find_inlist2join_context ctx;// 创建查找Inlist转Join的上下文结构体 + ctx.root = root;// 设置上下文中的查询优化器信息 + List* var_mapping_new = NIL;// 创建一个新的变量映射列表 + ListCell* lc = NULL;// 创建一个列表元素迭代器 - find_inlist2join_path_walker(best_path, &ctx); + find_inlist2join_path_walker(best_path, &ctx);// 调用递归函数查找Inlist转Join的路径 foreach (lc, root->var_mappings) { - RewriteVarMapping* rvm = (RewriteVarMapping*)lfirst(lc); - if (rvm->need_fix) { - var_mapping_new = lappend(var_mapping_new, rvm); + RewriteVarMapping* rvm = (RewriteVarMapping*)lfirst(lc);// 遍历原始变量映射列表中的每个元素 + if (rvm->need_fix) {// 如果需要修复 + var_mapping_new = lappend(var_mapping_new, rvm);// 添加到新的变量映射列表中 + } } - root->var_mappings = var_mapping_new; + root->var_mappings = var_mapping_new;// 更新查询优化器的变量映射列表为新的列表 } /* @@ -545,7 +606,7 @@ void find_inlist2join_path(PlannerInfo* root, Path* best_path) static void find_inlist2join_path_walker(Path* path, find_inlist2join_context* context) { if (path == NULL) { - return; + return;// 如果路径为空,直接返回 } switch (path->pathtype) { @@ -554,8 +615,8 @@ static void find_inlist2join_path_walker(Path* path, find_inlist2join_context* c } break; /* For subqueryscan, we should find the inlist2join subquery */ case T_SubqueryScan: { - RelOptInfo* rel = path->parent; - if (rel->base_rel != NULL) { + RelOptInfo* rel = path->parent;// 获取路径所属的关系优化信息 + if (rel->base_rel != NULL) {// 如果关系信息不为空 ListCell* lc1 = NULL; ListCell* lc = NULL; @@ -563,19 +624,19 @@ static void find_inlist2join_path_walker(Path* path, find_inlist2join_context* c * The rel is new_rel and has an inlist2join path * Remove the mapping from root */ - foreach (lc1, rel->reltargetlist) { - Var* var = (Var*)lfirst(lc1); - foreach (lc, context->root->var_mappings) { - RewriteVarMapping* rvm = (RewriteVarMapping*)lfirst(lc); - if (equal(rvm->old_var, var)) { - rvm->need_fix = true; + foreach (lc1, rel->reltargetlist) {// 遍历关系的目标列表 + Var* var = (Var*)lfirst(lc1);// 获取目标列表中的变量 + foreach (lc, context->root->var_mappings) {// 遍历查询优化器的变量映射列表 + RewriteVarMapping* rvm = (RewriteVarMapping*)lfirst(lc);// 获取变量映射信息 + if (equal(rvm->old_var, var)) {// 如果变量需要修复 + rvm->need_fix = true;// 设置标志表示需要修复 break; } } } } } break; - case T_Append: { + case T_Append: {// 处理其他不同类型的路径,依情况递归调用 ListCell* cell = NULL; foreach (cell, ((AppendPath*)path)->subpaths) { @@ -637,11 +698,12 @@ static void find_inlist2join_path_walker(Path* path, find_inlist2join_context* c */ static bool IsConvertableBaseRel(const RangeTblEntry* rte) { - Oid relId = rte->relid; - bool convertable = true; + Oid relId = rte->relid;// 获取关系表的OID + bool convertable = true; // 假设关系可转换为Inlist - Assert(rte->rtekind == RTE_RELATION); - Relation rel = RelationIdGetRelation(relId); + + Assert(rte->rtekind == RTE_RELATION);// 断言关系的类型为表关系 + Relation rel = RelationIdGetRelation(relId);// 根据OID获取关系表的信息 if (rel == NULL) { ereport(ERROR, (errmodule(MOD_OPT), @@ -651,11 +713,11 @@ static bool IsConvertableBaseRel(const RangeTblEntry* rte) /* Disallow HDFS table case and Foreign table case */ if (RelationIsDfsStore(rel) || RelationIsForeignTable(rel) || RelationIsStream(rel)) { - convertable = false; + convertable = false;// 如果是HDFS表、外部表或流表,则不可转换为Inlist } - RelationClose(rel); - return convertable; + RelationClose(rel);// 关闭关系表 + return convertable; // 返回是否可转换的标志 } /* @@ -670,19 +732,21 @@ static bool IsConvertableBaseRel(const RangeTblEntry* rte) */ static bool IsConvertableInlistRestrict(RelOptInfo* rel, const RestrictInfo* restrict, RelOrientation orientation) { - Assert(restrict != NULL && IsA(restrict->clause, ScalarArrayOpExpr)); + Assert(restrict != NULL && IsA(restrict->clause, ScalarArrayOpExpr));//断言限制条件不为空且是标量数组操作表达式 - ScalarArrayOpExpr* scalarop = (ScalarArrayOpExpr*)restrict->clause; - Oid opno = scalarop->opno; + + ScalarArrayOpExpr* scalarop = (ScalarArrayOpExpr*)restrict->clause;// 强制转换为标量数组操作表达式 + Oid opno = scalarop->opno;// 获取操作符OID List* var_list = - pull_var_clause((Node*)list_nth(scalarop->args, 0), PVC_REJECT_AGGREGATES, PVC_REJECT_PLACEHOLDERS); + pull_var_clause((Node*)list_nth(scalarop->args, 0), PVC_REJECT_AGGREGATES, PVC_REJECT_PLACEHOLDERS);// 提取表达式中的变量列表 /* Apply inlist2join rewrite optimization when the var_list number is 1 */ if (list_length(var_list) != 1) { - return false; + return false; // 如果变量列表长度不等于1,不可转换为Inlist } - Var* listvar = (Var*)linitial(var_list); - Oid typoid = listvar->vartype; + Var* listvar = (Var*)linitial(var_list); // 获取变量列表中的第一个变量 + Oid typoid = listvar->vartype;// 获取变量的数据类型OID + /* * Confirm current restrictinfo to see it matchs all following cretarias: @@ -708,21 +772,23 @@ static bool IsConvertableInlistRestrict(RelOptInfo* rel, const RestrictInfo* res * Do not apply inlist2join rewrite optimization if optmode greater than 1 but * the inlist length is less than threshold */ - int inlist_number = estimate_array_length((Node*)list_nth(scalarop->args, 1)); + int inlist_number = estimate_array_length((Node*)list_nth(scalarop->args, 1));// 估算Inlist中的元素数量 if (u_sess->opt_cxt.qrw_inlist2join_optmode > QRW_INLIST2JOIN_FORCE && inlist_number < u_sess->opt_cxt.qrw_inlist2join_optmode) { - return false; + return false;// 如果Inlist元素数量不满足最小要求,不可转换为Inlist } /* We set hard thresholds only for u_sess->opt_cxt.qrw_inlist2join_optmode is cost_base */ if (u_sess->opt_cxt.qrw_inlist2join_optmode == QRW_INLIST2JOIN_CBO && belowInlist2JoinThreshold(rel, inlist_number, orientation)) { - return false; + return false; // 如果低于CBO的阈值,不可转换为Inlist } - Assert(u_sess->opt_cxt.qrw_inlist2join_optmode > QRW_INLIST2JOIN_DISABLE); + Assert(u_sess->opt_cxt.qrw_inlist2join_optmode > QRW_INLIST2JOIN_DISABLE); // 断言Inlist2Join优化模式大于DISABLE + + + return true; // 满足Inlist转换条件,可以转换为Inlist - return true; } /* @@ -738,7 +804,7 @@ static bool IsConvertableInlistRestrict(RelOptInfo* rel, const RestrictInfo* res static bool HasConvertableInlistCond(RelOptInfo* rel) { ListCell* lc = NULL; - bool convertable = false; + bool convertable = false;// 如果没有基本限制条件,不可转换为Inlist /* * If rel has no restriction info, return false as considered as no convertable @@ -754,16 +820,17 @@ static bool HasConvertableInlistCond(RelOptInfo* rel) foreach (lc, var_list) { if (!IsA(lfirst(lc), Var)) { return false; - } + }// 如果目标列表包含非变量,不可转换为Inlist } /* Search rel's restrictioninfo to confirm it is inlist2join-convertable */ foreach (lc, rel->baserestrictinfo) { - RestrictInfo* restrict = (RestrictInfo*)lfirst(lc); + RestrictInfo* restrict = (RestrictInfo*)lfirst(lc);// 获取限制条件信息 + /* check if it contains subplan */ if (check_subplan_expr((Node*)restrict->clause)) { - convertable = false; + convertable = false; // 如果包含子查询表达式,不可转换为Inlist break; } @@ -775,11 +842,11 @@ static bool HasConvertableInlistCond(RelOptInfo* rel) */ if (IsConvertableInlistRestrict(rel, restrict, rel->orientation)) { convertable = true; - } + }// 如果限制条件可以转换为Inlist,可转换为Inlist } } - return convertable; + return convertable;// 返回是否可转换的标志 } /* @@ -800,28 +867,28 @@ static bool IsEqualOpr(Oid opno) ScanKeyData skey[1]; /* Scan pg_operator */ - pg_operator = heap_open(OperatorRelationId, AccessShareLock); + pg_operator = heap_open(OperatorRelationId, AccessShareLock);// 打开操作符表 - ScanKeyInit(&skey[0], ObjectIdAttributeNumber, BTEqualStrategyNumber, F_OIDEQ, opno); + ScanKeyInit(&skey[0], ObjectIdAttributeNumber, BTEqualStrategyNumber, F_OIDEQ, opno);// 初始化扫描键 - scan = systable_beginscan(pg_operator, OperatorOidIndexId, true, NULL, 1, skey); + scan = systable_beginscan(pg_operator, OperatorOidIndexId, true, NULL, 1, skey); // 开始扫描操作符表 /* Only one record should be qualified, and get the relid */ - if (HeapTupleIsValid(tuple = systable_getnext(scan))) { + if (HeapTupleIsValid(tuple = systable_getnext(scan))) {// 如果找到匹配的操作符 Form_pg_operator pgoperform = (Form_pg_operator)GETSTRUCT(tuple); if (strcmp(NameStr(pgoperform->oprname), "=") == 0) { - systable_endscan(scan); - heap_close(pg_operator, AccessShareLock); - return true; + systable_endscan(scan);// 结束扫描 + heap_close(pg_operator, AccessShareLock); // 关闭操作符表 + return true;// 如果操作符名称为"=",则返回true } } else { - elog(LOG, "could not find tuple for operator %u", opno); + elog(LOG, "could not find tuple for operator %u", opno);// 找不到操作符元组,记录日志 } - systable_endscan(scan); - heap_close(pg_operator, AccessShareLock); + systable_endscan(scan);// 结束扫描 + heap_close(pg_operator, AccessShareLock);// 关闭操作符表 - return false; + return false;// 操作符不是"=",返回false } /* @@ -837,34 +904,34 @@ static bool IsEqualOpr(Oid opno) static bool inline IsConvertableType(Oid typoid) { switch (typoid) { - case INT1OID: /* for TINYINT */ - case INT2OID: /* for SMALLINT */ - case INT4OID: /* for INTEGER */ - case INT8OID: /* for BIGINT */ - case NUMERICOID: /* for NUMERIC */ - case FLOAT4OID: /* for FLOAT4 */ - case FLOAT8OID: /* for FLOAT8 */ - case BOOLOID: /* for BOOLEAN */ - case CHAROID: /* for CHAR */ - case BPCHAROID: /* for BPCHAR */ - case VARCHAROID: /* for VARCHAR */ - case NVARCHAR2OID: /* for NVARCHAR */ - case TEXTOID: /* for TEXT */ - case DATEOID: /* for DATE */ - case TIMEOID: /* for TIME */ - case TIMETZOID: /* for TIMEZ */ - case TIMESTAMPOID: /* for TIMESTAMP */ - case TIMESTAMPTZOID: /* for TIMESTAMPTZOID */ - case SMALLDATETIMEOID: /* for SMALLDATETIME */ - case INTERVALOID: /* for INTERVAL */ - case TINTERVALOID: /* for TINTERVAL */ - return true; + case INT1OID: // 对于 TINYINT 类型 + case INT2OID: /* for SMALLINT */// 对于 SMALLINT 类型 + case INT4OID: /* for INTEGER */// 对于 INTEGER 类型 + case INT8OID: /* for BIGINT */// 对于 BIGINT 类型 + case NUMERICOID: /* for NUMERIC */// 对于 NUMERIC 类型 + case FLOAT4OID: /* for FLOAT4 */// 对于 FLOAT4 类型 + case FLOAT8OID: /* for FLOAT8 */ // 对于 FLOAT8 类型 + case BOOLOID: /* for BOOLEAN */// 对于 BOOLEAN 类型 + case CHAROID: /* for CHAR */ // 对于 CHAR 类型 + case BPCHAROID: /* for BPCHAR */ // 对于 BPCHAR 类型 + case VARCHAROID: /* for VARCHAR */// 对于 VARCHAR 类型 + case NVARCHAR2OID: /* for NVARCHAR */// 对于 NVARCHAR 类型 + case TEXTOID: /* for TEXT */ // 对于 TEXT 类型 + case DATEOID: /* for DATE */// 对于 DATE 类型 + case TIMEOID: /* for TIME */ // 对于 TIME 类型 + case TIMETZOID: /* for TIMEZ */// 对于 TIMEZ 类型 + case TIMESTAMPOID: /* for TIMESTAMP */ // 对于 TIMESTAMP 类型 + case TIMESTAMPTZOID: /* for TIMESTAMPTZOID */// 对于 TIMESTAMPTZOID 类型 + case SMALLDATETIMEOID: /* for SMALLDATETIME */// 对于 SMALLDATETIME 类型 + case INTERVALOID: /* for INTERVAL */// 对于 INTERVAL 类型 + case TINTERVALOID: /* for TINTERVAL */// 对于 TINTERVAL 类型 + return true; // 如果类型可转换,返回 true default: elog(DEBUG1, "Inlist2join is not converted for type %u", typoid); - } - return false; + }// 如果类型不能转换,记录错误信息并返回 false + return false;// 默认情况下,返回 false } - +// 定义函数 ConvertableInlistMaxNum 用于计算可转换的 inlist 条件的最大数量 /* * Name: ConvertableInlistMaxNum() * @@ -875,9 +942,10 @@ static bool inline IsConvertableType(Oid typoid) * * Return: int. the max num of inlist condition */ + // 定义函数 ConvertableInlistMaxNum 用于计算可转换的 inlist 条件的最大数量 static int ConvertableInlistMaxNum(const RelOptInfo* rel) { - int maxnum = 0; + int maxnum = 0; // 初始化最大数量为 0 if (rel->baserestrictinfo != NULL) { ListCell* lc = NULL; @@ -888,20 +956,20 @@ static int ConvertableInlistMaxNum(const RelOptInfo* rel) Oid opno = scalarop->opno; /* - * Confirm: - * 1. array operation is "in-any" - * 2. there is no dataype coerce - * 3. equal condition + * Confirm: // 确认条件: + * 1. array operation is "in-any"// 1. 数组操作是 "in-any" + * 2. there is no dataype coerce// 2. 没有数据类型强制转换 + * 3. equal condition// 3. 相等条件 */ if (scalarop->useOr && IsA(linitial(scalarop->args), Var) && IsEqualOpr(opno)) { Node* arraynode = (Node*)lsecond(scalarop->args); maxnum = Max(maxnum, estimate_array_length(arraynode)); - } + } // 计算数组的长度,并将其与当前最大数量比较,取较大值 } } } - return maxnum; + return maxnum;// 返回计算得到的最大数量 } /* @@ -915,14 +983,15 @@ static int ConvertableInlistMaxNum(const RelOptInfo* rel) * * Return: bool. indicate Yes/Not for inlist2join conversion */ + // 定义函数 belowInlist2JoinThreshold,用于检查是否低于 inlist2join 阈值 static bool belowInlist2JoinThreshold(const RelOptInfo* rel, int num, RelOrientation orientation) { int tarnum = list_length(rel->reltargetlist); - const int row_threshod = 10; + const int row_threshod = 10; // 定义行阈值为 10 if (orientation == REL_ROW_ORIENTED || orientation == REL_ORIENT_UNKNOWN) { if (num <= row_threshod) { - return true; + return true; // 如果满足其他条件,返回 true } } else { if (!tarnum) { @@ -933,7 +1002,7 @@ static bool belowInlist2JoinThreshold(const RelOptInfo* rel, int num, RelOrienta } } - return false; + return false; // 默认情况下,返回 false } /* @@ -948,12 +1017,13 @@ static bool belowInlist2JoinThreshold(const RelOptInfo* rel, int num, RelOrienta * * Return: RangeTblEntry. the new RangeTblEntry with inlist-subquery */ + // 定义函数 make_rte_with_subquery,用于创建具有子查询的 RangeTblEntry static RangeTblEntry* make_rte_with_subquery(PlannerInfo* root, RelOptInfo* rel, RangeTblEntry* rte) { RangeTblEntry* new_rte = makeNode(RangeTblEntry); - const char* refname = "__unnamed_subquery__"; + const char* refname = "__unnamed_subquery__";// 定义子查询的名称 - List* colnames_new = NIL; + List* colnames_new = NIL;// 初始化新列名列表为空 ListCell* reltarget = NULL; if (rte->eref->colnames) { @@ -966,12 +1036,13 @@ static RangeTblEntry* make_rte_with_subquery(PlannerInfo* root, RelOptInfo* rel, colnames_new = lappend(colnames_new, get_attr_name(relvar->varattno)); } else { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("encounters invalid varno"))); - } + }// 如果遇到无效的 varno,报错 } } new_rte->alias = makeAlias(refname, NIL); new_rte->eref = makeAlias(refname, colnames_new); + // 创建新的 RangeTblEntry 并设置别名和列名 rebuild_subquery(root, rel, rte, new_rte); new_rte->rtekind = RTE_SUBQUERY; @@ -990,7 +1061,8 @@ static RangeTblEntry* make_rte_with_subquery(PlannerInfo* root, RelOptInfo* rel, * SubQueries are never checked for access rights. * ---------- */ - new_rte->inh = false; /* never true for subqueries */ + // 绑定 CTE 列表 + new_rte->inh = false; /* never true for subqueries */// 永远不是子查询 new_rte->inFromCl = true; new_rte->requiredPerms = 2; @@ -1010,66 +1082,68 @@ static RangeTblEntry* make_rte_with_subquery(PlannerInfo* root, RelOptInfo* rel, * * Return: RelOptInfo. the new RelOptInfo with specific rtekind */ + // 定义函数 build_alternative_rel,用于构建一个新的 RelOptInfo RelOptInfo* build_alternative_rel(const RelOptInfo* origin, RTEKind rtekind) { RelOptInfo* rel = NULL; - Assert(origin != NULL); + Assert(origin != NULL);// 断言 origin 不为空指针 - rel = makeNode(RelOptInfo); - rel->reloptkind = origin->reloptkind; - rel->relids = bms_make_singleton(origin->relid); - rel->isPartitionedTable = false; - rel->partflag = origin->partflag; - rel->rows = origin->rows; - rel->width = origin->width; - rel->encodedwidth = origin->encodedwidth; - rel->encodednum = origin->encodednum; + rel = makeNode(RelOptInfo);// 创建一个新的 RelOptInfo 结构体 + rel->reloptkind = origin->reloptkind;// 设置新 RelOptInfo 的类型 + rel->relids = bms_make_singleton(origin->relid);// 创建包含一个关系标识符的位图集合 + rel->isPartitionedTable = false; // 初始化分区表标志为 false + rel->partflag = origin->partflag; + rel->rows = origin->rows;// 复制原始表的估算行数 + rel->width = origin->width; // 复制原始表的宽度 + rel->encodedwidth = origin->encodedwidth; // 复制原始表的编码宽度 + rel->encodednum = origin->encodednum;// 复制原始表的编码数量 rel->reltargetlist = list_copy(origin->reltargetlist); - rel->baserestrictinfo = (List*)copyObject(origin->baserestrictinfo); - rel->pathlist = NIL; - rel->ppilist = NIL; - rel->cheapest_startup_path = NULL; - rel->cheapest_total_path = NULL; - rel->cheapest_unique_path = NULL; - rel->relid = origin->relid; + rel->baserestrictinfo = (List*)copyObject(origin->baserestrictinfo);// 复制原始表的基本限制信息 + rel->pathlist = NIL;// 初始化路径列表为空 + rel->ppilist = NIL; // 初始化分区策略信息列表为空 + rel->cheapest_startup_path = NULL; // 初始化最便宜的启动路径为空 + rel->cheapest_total_path = NULL; // 初始化最便宜的总路径为空 + rel->cheapest_unique_path = NULL; // 初始化最便宜的唯一路径为空 + rel->relid = origin->relid; // 复制原始表的关系标识符 - rel->rtekind = rtekind; + rel->rtekind = rtekind; // 设置新 RelOptInfo 的 RTE 类型 - rel->min_attr = origin->min_attr; - rel->max_attr = origin->max_attr; - rel->attr_needed = origin->attr_needed; - rel->attr_widths = origin->attr_widths; + rel->min_attr = origin->min_attr; // 复制原始表的最小属性号 + rel->max_attr = origin->max_attr; // 复制原始表的最大属性号 + rel->attr_needed = origin->attr_needed; // 复制原始表的所需属性 + rel->attr_widths = origin->attr_widths; // 复制原始表的属性宽度信息 - rel->indexlist = NIL; - rel->pages = 0; - rel->tuples = 0; - rel->multiple = 0; - rel->allvisfrac = 0; - rel->pruning_result = NULL; - rel->pruning_result_for_index_usable = NULL; - rel->pruning_result_for_index_unusable = NULL; - rel->partItrs = -1; - rel->partItrs_for_index_usable = -1; - rel->partItrs_for_index_unusable = -1; - rel->subplan = NULL; - rel->subroot = NULL; - rel->subplan_params = NIL; - rel->fdwroutine = NULL; - rel->fdw_private = NULL; - rel->baserestrictcost.startup = 0; - rel->baserestrictcost.per_tuple = 0; - rel->joininfo = (List*)copyObject(origin->joininfo); - rel->subplanrestrictinfo = (List*)copyObject(origin->subplanrestrictinfo); - rel->has_eclass_joins = false; - rel->varratio = NIL; - rel->lateral_relids = origin->lateral_relids; + rel->indexlist = NIL; // 初始化索引列表为空 + rel->pages = 0; // 初始化估算页数为 0 + rel->tuples = 0; // 初始化估算元组数为 0 + rel->multiple = 0; // 初始化倍数为 0 + rel->allvisfrac = 0; // 初始化所有可见分数为 0 + rel->pruning_result = NULL; // 初始化分区剪枝结果为空 + rel->pruning_result_for_index_usable = NULL; // 初始化用于可用索引的分区剪枝结果为空 + rel->pruning_result_for_index_unusable = NULL; // 初始化用于不可用索引的分区剪枝结果为空 + rel->partItrs = -1; // 初始化分区迭代器为 -1 + rel->partItrs_for_index_usable = -1; // 初始化用于可用索引的分区迭代器为 -1 + rel->partItrs_for_index_unusable = -1; // 初始化用于不可用索引的分区迭代器为 -1 + rel->subplan = NULL; // 初始化子查询计划为空 + rel->subroot = NULL; // 初始化子查询的 PlannerInfo 为空 + rel->subplan_params = NIL; // 初始化子查询计划参数列表为空 + rel->fdwroutine = NULL; // 初始化外部数据包装器的信息为空 + rel->fdw_private = NULL; // 初始化外部数据包装器的私有信息为空 + rel->baserestrictcost.startup = 0; // 初始化基本限制的启动代价为 0 + rel->baserestrictcost.per_tuple = 0; // 初始化基本限制的每元组代价为 0 + rel->joininfo = (List*)copyObject(origin->joininfo); // 复制原始表的连接信息列表 + rel->subplanrestrictinfo = (List*)copyObject(origin->subplanrestrictinfo); // 复制原始表的子查询限制信息列表 + rel->has_eclass_joins = false; // 初始化是否包含等价类连接为 false + rel->varratio = NIL; // 初始化变量比例列表为空 + rel->lateral_relids = origin->lateral_relids; // 复制原始表的横向引用关系标识符 - rel->alternatives = NIL; - rel->base_rel = (RelOptInfo*)origin; + rel->alternatives = NIL; // 初始化备选项列表为空 + rel->base_rel = (RelOptInfo*)origin; // 设置基本关系为原始表的 RelOptInfo - return rel; + return rel; // 返回新构建的 RelOptInfo 结构体 } + /* * Name: get_attr_name() * @@ -1080,40 +1154,50 @@ RelOptInfo* build_alternative_rel(const RelOptInfo* origin, RTEKind rtekind) * * Return: char *. the attrname */ +// 定义函数 get_attr_name,用于根据属性号获取属性名 static char* get_attr_name(int attrnum) { + // 断言属性号应该小于0,确保属性号是有效的 Assert(attrnum < 0); + + // 使用 switch 语句根据属性号返回相应的属性名 switch (attrnum) { case SelfItemPointerAttributeNumber: - return (char*)"ctid"; + return (char*)"ctid"; // 返回 "ctid" 属性名 case ObjectIdAttributeNumber: - return (char*)"oid"; + return (char*)"oid"; // 返回 "oid" 属性名 case MinTransactionIdAttributeNumber: - return (char*)"xmin"; + return (char*)"xmin"; // 返回 "xmin" 属性名 case MinCommandIdAttributeNumber: - return (char*)"cmin"; + return (char*)"cmin"; // 返回 "cmin" 属性名 case MaxTransactionIdAttributeNumber: - return (char*)"xmax"; + return (char*)"xmax"; // 返回 "xmax" 属性名 case MaxCommandIdAttributeNumber: - return (char*)"cmax"; + return (char*)"cmax"; // 返回 "cmax" 属性名 case TableOidAttributeNumber: - return (char*)"tableoid"; + return (char*)"tableoid"; // 返回 "tableoid" 属性名 + #ifdef PGXC + // 以下是一些条件编译的情况,根据条件编译的定义返回相应的属性名 case XC_NodeIdAttributeNumber: - return (char*)"xc_node_id"; + return (char*)"xc_node_id"; // 返回 "xc_node_id" 属性名 case BucketIdAttributeNumber: - return (char*)"tablebucketid"; + return (char*)"tablebucketid"; // 返回 "tablebucketid" 属性名 case UidAttributeNumber: - return (char*)"gs_tuple_uid"; + return (char*)"gs_tuple_uid"; // 返回 "gs_tuple_uid" 属性名 #endif + default: + // 如果属性号无效,报错并返回错误消息 ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("invalid column number %d for table \n", attrnum))); break; } - return NULL; /* keep compiler quiet */ + + return NULL; // 如果没有匹配的属性号,返回 NULL(保持编译器安静) } + /* * Name: convert_constarray_to_simple() * @@ -1127,6 +1211,7 @@ static char* get_attr_name(int attrnum) * * Return: List *. the converted simple const list */ +// 定义函数 convert_constarray_to_simple,用于将常量数组转换为简单常量列表 static List* convert_constarray_to_simple(Const* combined_const, Var* listvar, Oid* paramtype) { int i; @@ -1142,35 +1227,44 @@ static List* convert_constarray_to_simple(Const* combined_const, Var* listvar, O Node* simple_const = NULL; List* const_list = NIL; + // 从输入的 combined_const 中获取数组类型的数据 arr = DatumGetArrayTypeP(combined_const->constvalue); - *paramtype = ARR_ELEMTYPE(arr); - nitems = ArrayGetNItems(ARR_NDIM(arr), ARR_DIMS(arr)); + *paramtype = ARR_ELEMTYPE(arr); // 获取数组元素的数据类型 + nitems = ArrayGetNItems(ARR_NDIM(arr), ARR_DIMS(arr)); // 获取数组的元素数量 - /* Get array infomation */ + // 获取数组元素类型的长度、是否按值传递和对齐方式 get_typlenbyvalalign(*paramtype, &typlen, &typbyval, &typalign); - /* Loop over the array elements */ - s = (char*)ARR_DATA_PTR(arr); - bitmap = ARR_NULLBITMAP(arr); - bitmask = 1; + s = (char*)ARR_DATA_PTR(arr); // 获取数组数据的起始指针 + bitmap = ARR_NULLBITMAP(arr); // 获取数组的空值位图 + bitmask = 1; // 用于检查空值位图中的位 + + // 遍历数组的每个元素 for (i = 0; i < nitems; i++) { Datum elt; - /* Get array element, checking for NULL */ + // 如果空值位图存在且当前位为 0,表示当前元素为空 if (bitmap && (*bitmap & bitmask) == 0) { elemNull = true; + // 创建一个表示空值的简单常量 simple_const = (Node*)makeConst( *paramtype, combined_const->consttypmod, combined_const->constcollid, typlen, (Datum)0, true, typbyval); } else { elemNull = false; + // 获取当前元素的数据 elt = fetch_att(s, typbyval, typlen); s = att_addlength_pointer(s, typlen, s); s = (char*)att_align_nominal(s, typalign); + // 创建一个表示当前元素的简单常量 simple_const = (Node*)makeConst( *paramtype, combined_const->consttypmod, combined_const->constcollid, typlen, elt, false, typbyval); } + + // 将简单常量添加到常量列表 const_list = lappend(const_list, simple_const); + + // 更新位掩码和空值位图 if (bitmap != NULL) { bitmask <<= 1; if (bitmask == 0x100) { @@ -1180,9 +1274,10 @@ static List* convert_constarray_to_simple(Const* combined_const, Var* listvar, O } } - return const_list; + return const_list; // 返回包含简单常量的列表 } + /* * Name: rebuild_subquery() * @@ -1196,22 +1291,29 @@ static List* convert_constarray_to_simple(Const* combined_const, Var* listvar, O * * Return: void. */ +// 定义函数 rebuild_subquery,用于重建子查询 static void rebuild_subquery(PlannerInfo* root, RelOptInfo* rel, RangeTblEntry* rte, RangeTblEntry* new_rte) { + // 创建一个新的 Query 结构体,表示子查询 Query* query = makeNode(Query); - query->commandType = CMD_SELECT; - query->querySource = QSRC_ORIGINAL; - query->canSetTag = true; - query->resultRelation = 0; - query->hasSubLinks = true; - query->mergeTarget_relation = 0; - query->targetList = NIL; + // 设置子查询的属性 + query->commandType = CMD_SELECT; // 设置命令类型为 SELECT + query->querySource = QSRC_ORIGINAL; // 设置查询源为原始查询 + query->canSetTag = true; // 设置可以设置标签 + query->resultRelation = 0; // 设置结果关系的编号为 0 + query->hasSubLinks = true; // 设置子链接标志为 true + query->mergeTarget_relation = 0; // 设置合并目标关系的编号为 0 + query->targetList = NIL; // 初始化目标列表为空 + + // 将原始 RangeTblEntry 复制并添加到子查询的范围表中 query->rtable = list_make1((RangeTblEntry*)copyObject(rte)); + // 获取原始 RangeTblEntry 的列名列表 List* colnames = rte->eref->colnames; - ListCell* lc1 = NULL; + ListCell* lc1 = NULL; // 定义列表元素迭代器 lc1,用于遍历列名列表 + /* * Build target list for new SubQuery and RelOptInfo @@ -1225,61 +1327,66 @@ static void rebuild_subquery(PlannerInfo* root, RelOptInfo* rel, RangeTblEntry* * return attribues is start from 1 */ /* 1. Build SubQuery's target list */ - foreach (lc1, rel->reltargetlist) { - Assert(IsA(lfirst(lc1), Var)); - Var* relvar = (Var*)copyObject((Var*)lfirst(lc1)); + // 使用 foreach 循环遍历 rel->reltargetlist 列表 +foreach (lc1, rel->reltargetlist) { + Assert(IsA(lfirst(lc1), Var)); // 断言列表元素是 Var 类型 + Var* relvar = (Var*)copyObject((Var*)lfirst(lc1)); // 复制当前 Var 元素 - /* In Separate query, varno is always 1 */ - relvar->varno = SUBQUERY_VARNO; + // 将 relvar 的 varno 属性设置为 SUBQUERY_VARNO + relvar->varno = SUBQUERY_VARNO; - char* varname = NULL; - if (relvar->varattno < 0) - varname = get_attr_name(relvar->varattno); - else if (relvar->varattno > 0) - varname = strVal(list_nth(colnames, relvar->varattno - 1)); - else - ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("There is no exist vararrno with 0"))); + char* varname = NULL; - /* Make TargetEntry for Query */ - TargetEntry* entry = makeTargetEntry((Expr*)relvar, /* expr */ - (AttrNumber)(list_length(query->targetList) + 1), /* resno */ - varname, - false); + // 根据 varattno 的值获取变量名 + if (relvar->varattno < 0) + varname = get_attr_name(relvar->varattno); + else if (relvar->varattno > 0) + varname = strVal(list_nth(colnames, relvar->varattno - 1)); + else + ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("There is no exist vararrno with 0"))); - entry->resorigtbl = rte->relid; - entry->resorigcol = relvar->varattno; - query->targetList = lappend(query->targetList, entry); - } + // 创建 TargetEntry 条目并将其添加到 query 的 targetList 列表中 + TargetEntry* entry = makeTargetEntry((Expr*)relvar, + (AttrNumber)(list_length(query->targetList) + 1), // 新条目的编号递增 + varname, + false); - /* 2. Build RelOptInfo's targetlist as alternative rel */ - AttrNumber attno = SUBQUERY_ATTNO; - foreach (lc1, rel->reltargetlist) { - Var* old_var = (Var*)copyObject((Var*)lfirst(lc1)); - Var* new_var = (Var*)copyObject((Var*)lfirst(lc1)); + entry->resorigtbl = rte->relid; // 设置条目的 resorigtbl 属性为 rte 的 relid + entry->resorigcol = relvar->varattno; // 设置条目的 resorigcol 属性为 relvar 的 varattno + query->targetList = lappend(query->targetList, entry); // 将条目添加到 targetList 列表中 +} - /* Record the var's varattno's change */ - { - new_var->varattno = attno; +// 初始化 attno 为 SUBQUERY_ATTNO +AttrNumber attno = SUBQUERY_ATTNO; +foreach (lc1, rel->reltargetlist) { + Var* old_var = (Var*)copyObject((Var*)lfirst(lc1)); // 复制当前 Var 元素 + Var* new_var = (Var*)copyObject((Var*)lfirst(lc1)); // 复制当前 Var 元素 - RewriteVarMapping* mapping = (RewriteVarMapping*)palloc0(sizeof(RewriteVarMapping)); - mapping->old_var = old_var; - mapping->new_var = new_var; - mapping->need_fix = false; + // 更新 new_var 的 varattno 为 attno + new_var->varattno = attno; - /* Record the changed vars when varattno is changed */ - root->var_mapping_rels = bms_add_member(root->var_mapping_rels, (int)old_var->varno); - root->var_mappings = lappend(root->var_mappings, mapping); - } + // 创建 RewriteVarMapping 结构体,表示变量映射关系 + RewriteVarMapping* mapping = (RewriteVarMapping*)palloc0(sizeof(RewriteVarMapping)); + mapping->old_var = old_var; + mapping->new_var = new_var; + mapping->need_fix = false; - attno++; - } + // 将 old_var 的 varno 添加到 root->var_mapping_rels 中 + root->var_mapping_rels = bms_add_member(root->var_mapping_rels, (int)old_var->varno); - /* 3. Build SubQuery's rtf&fromlist */ - RangeTblRef* rtr = makeNode(RangeTblRef); - rtr->rtindex = SUBQUERY_VARNO; - List* fromlist = list_make1(rtr); + // 将 mapping 添加到 root->var_mappings 列表中 + root->var_mappings = lappend(root->var_mappings, mapping); - Assert(rel->baserestrictinfo); + attno++; // 增加 attno 的值 +} + +// 创建 RangeTblRef 结构体 rtr,表示范围表引用 +RangeTblRef* rtr = makeNode(RangeTblRef); +rtr->rtindex = SUBQUERY_VARNO; +List* fromlist = list_make1(rtr); // 创建包含 rtr 的 fromlist 列表 + +// 断言 rel->baserestrictinfo 不为空 +Assert(rel->baserestrictinfo); /* * 4. Build SubQuery's ValueScan part @@ -1289,30 +1396,39 @@ static void rebuild_subquery(PlannerInfo* root, RelOptInfo* rel, RangeTblEntry* * example: select * from t1 where t1.c1 in (1,2,3,4) * => select * from (select * from t1 where t1.c1 in (select column1 from (values(1),(2),(3),(4)))) */ - List* args = NIL; - ListCell* lc = NULL; - foreach (lc, rel->baserestrictinfo) { - RestrictInfo* rinfo = (RestrictInfo*)lfirst(lc); + List* args = NIL; // 初始化 args 列表为空 +ListCell* lc = NULL; // 定义列表元素迭代器 lc - if (!(IsA(rinfo->clause, ScalarArrayOpExpr))) { - /* - * Fix vars in restriction list for a case where a Var is changed in new - * SubQuery and its origin ref var - */ +// 使用 foreach 循环遍历 rel->baserestrictinfo 列表 +foreach (lc, rel->baserestrictinfo) { + RestrictInfo* rinfo = (RestrictInfo*)lfirst(lc); // 获取当前元素,并转换为 RestrictInfo 类型 + + if (!(IsA(rinfo->clause, ScalarArrayOpExpr))) { + /* + * 如果 rinfo->clause 不是 ScalarArrayOpExpr 类型, + * 则进行修复子查询中的变量引用,并将其添加到 args 列表中。 + */ + + // 修复子查询中的变量引用,将原 rel->relid 替换为 SUBQUERY_VARNO + (void)fix_subquery_vars_expr((Node*)rinfo->clause, rel->relid, (Index)SUBQUERY_VARNO); + + // 将 rinfo->clause 添加到 args 列表中 + args = lappend(args, rinfo->clause); + } else { + /* 排除无效的 inlist 条件,并继续修复变量引用 */ + + // 检查是否可以将 ScalarArrayOpExpr 转换为 in-list 连接 + if (!IsConvertableInlistRestrict(rel, rinfo, rte->orientation)) { + // 如果不能转换,则继续修复子查询中的变量引用,并将 rinfo->clause 添加到 args 列表中 (void)fix_subquery_vars_expr((Node*)rinfo->clause, rel->relid, (Index)SUBQUERY_VARNO); - - /* Put it to args if restrict is not a "INLIST" case */ args = lappend(args, rinfo->clause); - } else { - /* Exclude invalid inlist cond, just do Fix-var and continue */ - if (!IsConvertableInlistRestrict(rel, rinfo, rte->orientation)) { - (void)fix_subquery_vars_expr((Node*)rinfo->clause, rel->relid, (Index)SUBQUERY_VARNO); - args = lappend(args, rinfo->clause); - continue; - } + continue; // 跳过下面的代码,继续下一次循环 + } + + // 复制 ScalarArrayOpExpr,以便进一步处理 + ScalarArrayOpExpr* scalar = (ScalarArrayOpExpr*)copyObject(rinfo->clause); - ScalarArrayOpExpr* scalar = (ScalarArrayOpExpr*)copyObject(rinfo->clause); /* * Build the inlist-converted SubLink with content values((),(),()), for example @@ -1325,53 +1441,54 @@ static void rebuild_subquery(PlannerInfo* root, RelOptInfo* rel, RangeTblEntry* * (values(1),(2),(3),(4)) as x(column1) * ) */ - SubLink* sublink = makeNode(SubLink); - sublink->subLinkType = ANY_SUBLINK; - sublink->operName = list_make1(makeString((char *)"=")); - sublink->location = -1; + SubLink* sublink = makeNode(SubLink); // 创建 SubLink 结构体 +sublink->subLinkType = ANY_SUBLINK; // 设置子链接类型为 ANY_SUBLINK +sublink->operName = list_make1(makeString((char *)"=")); // 创建操作符名称列表,包含一个 "=" 字符串 +sublink->location = -1; // 设置位置为 -1 - Oid colid = exprCollation((Node*)linitial((List*)scalar->args)); - List* args_in_opexpr = NIL; - Oid paramtype = 0; - Var* listvar = fix_subquery_vars_expr((Node*)scalar->args, rel->relid, (Index)SUBQUERY_VARNO); - Const* combined_const = (Const*)list_nth(scalar->args, 1); - List* const_list = convert_constarray_to_simple(combined_const, listvar, ¶mtype); +// 获取表达式的字符集 OID +Oid colid = exprCollation((Node*)linitial((List*)scalar->args)); - args_in_opexpr = lappend(args_in_opexpr, linitial((List*)scalar->args)); +List* args_in_opexpr = NIL; // 初始化操作表达式的参数列表 +Oid paramtype = 0; // 初始化参数类型 +Var* listvar = fix_subquery_vars_expr((Node*)scalar->args, rel->relid, (Index)SUBQUERY_VARNO); // 修复子查询中的变量引用 +Const* combined_const = (Const*)list_nth(scalar->args, 1); // 获取 ScalarArrayOpExpr 中的第二个参数 +List* const_list = convert_constarray_to_simple(combined_const, listvar, ¶mtype); // 将 Const 数组转换为简单的常量列表 - Param* param = makeNode(Param); - param->paramkind = PARAM_SUBLINK; - param->paramid = 1; - param->paramtype = paramtype; - param->paramtypmod = -1; - param->paramcollid = listvar->varcollid; - param->location = -1; - param->tableOfIndexType = InvalidOid; - args_in_opexpr = lappend(args_in_opexpr, param); +args_in_opexpr = lappend(args_in_opexpr, linitial((List*)scalar->args)); // 将 ScalarArrayOpExpr 中的第一个参数添加到参数列表 - OpExpr* opexpr = makeNode(OpExpr); - opexpr->opno = scalar->opno; - opexpr->opfuncid = scalar->opfuncid; - opexpr->opresulttype = BOOLOID; - opexpr->opcollid = 0; - opexpr->inputcollid = listvar->varcollid; - opexpr->location = -1; - sublink->testexpr = (Node*)opexpr; +Param* param = makeNode(Param); // 创建 Param 结构体 +param->paramkind = PARAM_SUBLINK; // 设置参数类型为 PARAM_SUBLINK +param->paramid = 1; // 设置参数 ID 为 1 +param->paramtype = paramtype; // 设置参数类型为之前计算的 paramtype +param->paramtypmod = -1; // 设置参数类型修饰符为 -1 +param->paramcollid = listvar->varcollid; // 设置参数的字符集 OID +param->location = -1; // 设置位置为 -1 +param->tableOfIndexType = InvalidOid; // 设置表的索引类型为 InvalidOid +args_in_opexpr = lappend(args_in_opexpr, param); // 将 Param 参数添加到参数列表 - opexpr->args = args_in_opexpr; +OpExpr* opexpr = makeNode(OpExpr); // 创建 OpExpr 结构体 +opexpr->opno = scalar->opno; // 设置操作符的 OID +opexpr->opfuncid = scalar->opfuncid; // 设置操作符函数的 OID +opexpr->opresulttype = BOOLOID; // 设置操作结果类型为 BOOLOID +opexpr->opcollid = 0; // 设置操作的字符集 OID 为 0 +opexpr->inputcollid = listvar->varcollid; // 设置输入字符集 OID +opexpr->location = -1; // 设置位置为 -1 +sublink->testexpr = (Node*)opexpr; // 将 OpExpr 作为子查询的测试表达式 - /* Build SubLink's "subselect" part */ - Query* subselect = makeNode(Query); - subselect->commandType = CMD_SELECT; - subselect->querySource = QSRC_ORIGINAL; - subselect->canSetTag = true; - subselect->resultRelation = 0; - subselect->mergeTarget_relation = 0; +opexpr->args = args_in_opexpr; // 设置 OpExpr 的参数列表 + +Query* subselect = makeNode(Query); // 创建 Query 结构体,用于子查询 +subselect->commandType = CMD_SELECT; // 设置命令类型为 CMD_SELECT +subselect->querySource = QSRC_ORIGINAL; // 设置查询来源为 QSRC_ORIGINAL +subselect->canSetTag = true; // 允许设置标签 +subselect->resultRelation = 0; // 结果关系为 0 +subselect->mergeTarget_relation = 0; // 合并目标关系为 0 + +RangeTblRef* rtf = makeNode(RangeTblRef); // 创建 RangeTblRef 结构体,表示范围表引用 +rtf->rtindex = VALUES_SUBLINK_VARNO; // 设置范围表引用的索引号为 VALUES_SUBLINK_VARNO +subselect->jointree = makeFromExpr(list_make1(rtf), NULL); // 创建联接树,包含一个范围表引用 - /* Build SubLink's FromExpr but no Quals */ - RangeTblRef* rtf = makeNode(RangeTblRef); - rtf->rtindex = VALUES_SUBLINK_VARNO; - subselect->jointree = makeFromExpr(list_make1(rtf), NULL); /* * Build SubLink's TargetEntry @@ -1379,55 +1496,54 @@ static void rebuild_subquery(PlannerInfo* root, RelOptInfo* rel, RangeTblEntry* * This var is used in RTE *VALUE*, there is one RTE in the query and one column in the RTE * so let varno and varattno are 1, the type is same as the listvar->vartype */ - TargetEntry* target_entry = makeNode(TargetEntry); + TargetEntry* target_entry = makeNode(TargetEntry); // 创建 TargetEntry 结构体 - target_entry->expr = (Expr*)makeVar((Index)VALUES_SUBLINK_VARNO, /* varno for values((a),(b),(c)) */ - VALUES_SUBLINK_VARATTNO, /* varattno for values((a),(b),(c)) */ - paramtype, - -1, - colid, - 0); +// 创建一个表达式,表示列的访问,通过 makeVar 创建 +target_entry->expr = (Expr*)makeVar((Index)VALUES_SUBLINK_VARNO, + VALUES_SUBLINK_VARATTNO, + paramtype, + -1, + colid, + 0); - /* There is only one target column */ - target_entry->resno = VALUES_SUBLINK_VARATTNO; - target_entry->resname = (char *)"column1"; - target_entry->resorigtbl = 0; - target_entry->ressortgroupref = 0; - target_entry->resorigcol = 0; - subselect->targetList = list_make1(target_entry); +target_entry->resno = VALUES_SUBLINK_VARATTNO; // 设置结果号 +target_entry->resname = (char *)"column1"; // 设置结果名称 +target_entry->resorigtbl = 0; // 设置结果的原始表为 0 +target_entry->ressortgroupref = 0; // 设置结果的排序和分组参考为 0 +target_entry->resorigcol = 0; // 设置结果的原始列为 0 +subselect->targetList = list_make1(target_entry); // 将 TargetEntry 添加到子查询的目标列表 - /* Build VALUES part and put it as a RTE object under SubLink's subselect part */ - List* values_lists = NIL; - List* collations = NIL; +List* values_lists = NIL; // 初始化值列表 +List* collations = NIL; // 初始化字符集列表 - ListCell* lc_value = NULL; - foreach (lc_value, const_list) { - Const* const_value = (Const*)copyObject((Const*)lfirst(lc_value)); - values_lists = lappend(values_lists, list_make1(const_value)); - } - collations = lappend_oid(collations, combined_const->constcollid); - RangeTblEntry* sublink_rte = addRangeTableEntryForValues(NULL, values_lists, collations, NULL, true); +ListCell* lc_value = NULL; +foreach (lc_value, const_list) { + // 复制常量值并添加到值列表中 + Const* const_value = (Const*)copyObject((Const*)lfirst(lc_value)); + values_lists = lappend(values_lists, list_make1(const_value)); +} +collations = lappend_oid(collations, combined_const->constcollid); // 添加字符集 OID 到字符集列表 - subselect->rtable = list_make1(sublink_rte); +// 创建子查询的范围表条目,表示常量值的来源 +RangeTblEntry* sublink_rte = addRangeTableEntryForValues(NULL, values_lists, collations, NULL, true); - /* Bind SubLink's subselect part */ - sublink->subselect = (Node*)subselect; +subselect->rtable = list_make1(sublink_rte); // 将子查询的范围表条目添加到子查询的范围表中 - args = lappend(args, sublink); - } - } +sublink->subselect = (Node*)subselect; // 设置 SubLink 的子查询为刚创建的子查询 - /* After put the restrictioninfo into subquery we release it on top level */ - rel->baserestrictinfo = NIL; +args = lappend(args, sublink); // 将 SubLink 添加到参数列表中 - /* Build FromExpr */ - Expr* andexpr = makeBoolExpr(AND_EXPR, args, -1); - query->jointree = makeFromExpr(fromlist, (Node*)andexpr); +rel->baserestrictinfo = NIL; // 清空基础限制信息列表 + +// 创建一个 AND 表达式,将参数列表中的所有表达式连接起来 +Expr* andexpr = makeBoolExpr(AND_EXPR, args, -1); + +// 创建联接树,将 AND 表达式作为联接树的条件 +query->jointree = makeFromExpr(fromlist, (Node*)andexpr); #ifdef ENABLE_MULTIPLE_NODES - /* Set can_push flag of query. */ - mark_query_canpush_flag((Node *) query); +// 标记查询可以推送到多个节点上的标志 +mark_query_canpush_flag((Node *) query); #endif - new_rte->subquery = query; -} +new_rte->subquery = query; // 将新的子查询赋给新的范围表条目 diff --git a/src/gausskernel/optimizer/plan/planstartwith.cpp b/src/gausskernel/optimizer/plan/planstartwith.cpp index 9a2adfb9a..0f6b0677e 100644 --- a/src/gausskernel/optimizer/plan/planstartwith.cpp +++ b/src/gausskernel/optimizer/plan/planstartwith.cpp @@ -78,8 +78,9 @@ #include "optimizer/optimizerdebug.h" #include "parser/parse_oper.h" -extern Node* preprocess_expression(PlannerInfo* root, Node* expr, int kind); +extern Node* preprocess_expression(PlannerInfo* root, Node* expr, int kind); // 声明外部函数 preprocess_expression +// 定义一个结构体数组 g_StartWithCTEPseudoReturnColumns,包含了起始项查询中的伪返回列信息 StartWithCTEPseudoReturnColumns g_StartWithCTEPseudoReturnColumns[] = { {"level", INT4OID, -1, InvalidOid}, @@ -93,17 +94,28 @@ StartWithCTEPseudoReturnColumns g_StartWithCTEPseudoReturnColumns[] = * Data & Local routines support major planning for start with * -------------------------------------------------------------------------------------- */ -static StartWithOp *CreateStartWithOpNode(PlannerInfo *root, - CteScan *cteplan, RecursiveUnion *ruplan); +// 声明一个名为 CreateStartWithOpNode 的函数,返回一个指向 StartWithOp 结构体的指针。 +static StartWithOp *CreateStartWithOpNode(PlannerInfo *root, CteScan *cteplan, RecursiveUnion *ruplan); + +// 声明一个名为 ProcessConnectByFakeConst 的函数,无返回值。 static void ProcessConnectByFakeConst(PlannerInfo *root, StartWithOp *swplan); -static void BuildStartWithInternalTargetList(PlannerInfo *root, - CteScan *cteplan, StartWithOp *swplan); + +// 声明一个名为 BuildStartWithInternalTargetList 的函数,无返回值。 +static void BuildStartWithInternalTargetList(PlannerInfo *root, CteScan *cteplan, StartWithOp *swplan); + +// 声明一个名为 ProcessOrderSiblings 的函数,无返回值。 static void ProcessOrderSiblings(PlannerInfo *root, StartWithOp *swplan); + +// 声明一个名为 OptimizeStartWithPlan 的函数,无返回值。 static void OptimizeStartWithPlan(PlannerInfo *root, StartWithOp *swplan); +// 定义一个宏,表示材料化连接外部的阈值,值为 100。 #define SWCB_MATERIAL_OUTER_THREADHOLD 100 + +// 声明一个内联函数,用于判断是否需要对外部计划进行材料化连接。 static inline bool NeedMaterialJoinOuter(Plan *outer, Plan *inner) { + // 如果外部计划的总成本大于内部计划的总成本乘以材料化连接阈值,则返回 true。 return (outer->total_cost > inner->total_cost * SWCB_MATERIAL_OUTER_THREADHOLD); } @@ -112,67 +124,52 @@ static inline bool NeedMaterialJoinOuter(Plan *outer, Plan *inner) * Data & Local routines support internal Key/Col generation and also * -------------------------------------------------------------------------------------- */ -typedef struct PullUpConnectByFuncVarContext { - PlannerInfo *root; - List *pullupVars; - CteScan *cteplan; - StartWithOp *swplan; -} PullUpConnectByFuncVarContext; -static List *GetPRCTargetEntryList(PlannerInfo *root, RangeTblEntry *rte, StartWithOp *swplan); -static int GetVarPRCType(List *prcList, const Var* var); -static void MarkPRCNotSkip(StartWithOp *swplan, int prcType); -typedef struct ReplaceFakeConstContext { - Var *levelVar; - Var *rownumVar; - Node **nodeRef; -} ReplaceFakeConstContext; - -static bool PullUpConnectByFuncVarsWalker(Node *node, PullUpConnectByFuncVarContext *context); -static List *PullUpConnectByFuncVars(PlannerInfo *root, CteScan *cteScan, Node *targetEntry); -static void CheckInvalidConnectByfuncArgs(CteScan *cteplan, Oid funcid, List *arg_vars); - -static void GenerateStartWithInternalEntries(PlannerInfo *root, CteScan *cteplan, - List **keyEntryList, List **colEntryList); -static List* BuildStartWithPlanPseudoTargetList(Plan *plan, Index varno, - List *key_list, List *col_list, bool needSiblings); - -static inline bool StartWithNeedSiblingSort(PlannerInfo *root, CteScan *cteplan) -{ - Assert (IsA(cteplan, CteScan) && IsCteScanProcessForStartWith(cteplan)); - - return (cteplan->cteRef->swoptions->siblings_orderby_clause != NULL); -} /* * -------------------------------------------------------------------------------------- * Data & Local routines supporot ORDER SIBLING BY * -------------------------------------------------------------------------------------- */ +// 定义常量 SORTCMP_TOTAL_NUM,值为 3 #define SORTCMP_TOTAL_NUM 3 -#define SORTCMP_LT 0 -#define SORTCMP_EQ 1 -#define SORTCMP_GT 2 -#define CF_ONE 1 -#define CF_TWO 2 +// 定义常量 SORTCMP_LT,值为 0 +#define SORTCMP_LT 0 -#define TEXT_COLLCATION 100 +// 定义常量 SORTCMP_EQ,值为 1 +#define SORTCMP_EQ 1 +// 定义常量 SORTCMP_GT,值为 2 +#define SORTCMP_GT 2 + +// 定义常量 CF_ONE,值为 1 +#define CF_ONE 1 + +// 定义常量 CF_TWO,值为 2 +#define CF_TWO 2 + +// 定义常量 TEXT_COLLCATION,值为 100 +#define TEXT_COLLCATION 100 + +// 声明一个名为 OrderSiblingSortEntry 的结构体,包含了多个成员变量。 typedef struct OrderSiblingSortEntry { - TargetEntry *tle; - bool sortCmpOp[SORTCMP_TOTAL_NUM]; - bool sortByNullsFirst; + TargetEntry *tle; // 目标入口指针 + bool sortCmpOp[SORTCMP_TOTAL_NUM]; // 排序比较操作符的布尔数组 + bool sortByNullsFirst; // 是否根据 NULLS FIRST 进行排序 } OrderSiblingSortEntry; -static OrderSiblingSortEntry* CreateOrderSiblingSortEntry( - TargetEntry *entry, SortByDir dir); -static Sort *CreateSiblingsSortPlan(PlannerInfo* root, Plan* lefttree, - List *sortEntryList, double limit_tuples); -static Sort *CreateSortPlanUnderRU(PlannerInfo* root, Plan* lefttree, - List *siblings, double limit_tuples); -static Sort *CreateSortPlanAboveRU(PlannerInfo* root, Plan* lefttree, - double limit_tuples); +// 声明一个名为 CreateOrderSiblingSortEntry 的函数,返回一个指向 OrderSiblingSortEntry 结构体的指针。 +static OrderSiblingSortEntry* CreateOrderSiblingSortEntry(TargetEntry *entry, SortByDir dir); + +// 声明一个名为 CreateSiblingsSortPlan 的函数,返回一个指向 Sort 结构体的指针。 +static Sort *CreateSiblingsSortPlan(PlannerInfo* root, Plan* lefttree, List *sortEntryList, double limit_tuples); + +// 声明一个名为 CreateSortPlanUnderRU 的函数,返回一个指向 Sort 结构体的指针。 +static Sort *CreateSortPlanUnderRU(PlannerInfo* root, Plan* lefttree, List *siblings, double limit_tuples); + +// 声明一个名为 CreateSortPlanAboveRU 的函数,返回一个指向 Sort 结构体的指针。 +static Sort *CreateSortPlanAboveRU(PlannerInfo* root, Plan* lefttree, double limit_tuples); /* @@ -180,72 +177,167 @@ static Sort *CreateSortPlanAboveRU(PlannerInfo* root, Plan* lefttree, * Data & Local routines supporot pseudo targetlist push down * -------------------------------------------------------------------------------------- */ +// 定义常量 UnknownVarno,值为 0 #define UnknownVarno 0 + +// 定义常量 MAX_PLAN_DEPTH,值为 100 #define MAX_PLAN_DEPTH 100 +// 声明一个名为 StackNode 的结构体,包含了多个成员变量。 typedef struct StackNode { - Plan *value_array[MAX_PLAN_DEPTH]; - int top; + Plan *value_array[MAX_PLAN_DEPTH]; // Plan 指针数组 + int top; // 栈顶指针 } StackNode; +// 定义一个内联函数 StackNodePush,用于将 Plan 指针压入栈中 static inline void StackNodePush(StackNode *s, Plan *node) { - s->top++; - s->value_array[s->top] = node; + s->top++; // 栈顶指针加一 + s->value_array[s->top] = node; // 将节点压入栈中 } +// 定义一个内联函数 StackNodePop,用于弹出栈顶的 Plan 指针 static inline Plan* StackNodePop(StackNode *s) { if (s->top < 0) { - elog(ERROR, "error to pop() element in %s", __FUNCTION__); + elog(ERROR, "error to pop() element in %s", __FUNCTION__); // 如果栈为空,则报错 } - Plan *node = s->value_array[s->top]; - s->value_array[s->top] = NULL; - s->top--; - return node; + Plan *node = s->value_array[s->top]; // 获取栈顶的节点 + s->value_array[s->top] = NULL; // 将栈顶清空 + s->top--; // 栈顶指针减一 + return node; // 返回弹出的节点 } +// 声明一个名为 StackVarno 的结构体,包含了多个成员变量。 typedef struct StackVarno { - Index value_array[MAX_PLAN_DEPTH]; - int top; + Index value_array[MAX_PLAN_DEPTH]; // Index 数组 + int top; // 栈顶指针 } StackVarno; +// 定义一个内联函数 StackVarnoPush,用于将 Index 压入栈中 static inline void StackVarnoPush(StackVarno *s, Index varno) { - s->top++; - s->value_array[s->top] = varno; + s->top++; // 栈顶指针加一 + s->value_array[s->top] = varno; // 将 varno 压入栈中 } +// 定义一个内联函数 StackVarnoPop,用于弹出栈顶的 Index static inline Index StackVarnoPop(StackVarno *s) { if (s->top < 0) { - elog(ERROR, "error to pop() element in %s", __FUNCTION__); + elog(ERROR, "error to pop() element in %s", __FUNCTION__); // 如果栈为空,则报错 } - Index varno = s->value_array[s->top]; - s->value_array[s->top] = 0; - s->top--; - return varno; + Index varno = s->value_array[s->top]; // 获取栈顶的 varno + s->value_array[s->top] = 0; // 将栈顶清空 + s->top--; // 栈顶指针减一 + return varno; // 返回弹出的 varno } +// 声明一个名为 PlanAccessPathSearchContext 的结构体,包含了多个成员变量。 typedef struct PlanAccessPathSearchContext { - Plan *topNode; - Plan *botNode; - List *fullEntryList; - StackNode planStack; - StackVarno varnoStack; - Index relid; - bool done; - int numsPrevTlist; + Plan *topNode; // 顶部节点 + Plan *botNode; // 底部节点 + List *fullEntryList; // 完整的入口列表 + StackNode planStack; // 计划栈 + StackVarno varnoStack; // varno 栈 + Index relid; // 关系 ID + bool done; // 是否完成 + int numsPrevTlist; // 先前 Tlist 的数量 } PlanAccessPathSearchContext; -static void GetWorkTableScanPlanPath(PlannerInfo *root, Plan *node, - PlanAccessPathSearchContext *context); -static void BindPlanNodePseudoEntries(PlannerInfo *root, Plan *node, Index varno, - PlanAccessPathSearchContext *context); +// 声明一个名为 GetWorkTableScanPlanPath 的函数,用于获取工作表扫描计划的路径 +static void GetWorkTableScanPlanPath(PlannerInfo *root, Plan *node, PlanAccessPathSearchContext *context); + +// 声明一个名为 BindPlanNodePseudoEntries 的函数,用于绑定计划节点的伪条目 +static void BindPlanNodePseudoEntries(PlannerInfo *root, Plan *node, Index varno, PlanAccessPathSearchContext *context); + +// 声明一个名为 AddPseudoEntries 的函数,用于添加伪条目 static void AddPseudoEntries(Plan *plan, Index relid, PlanAccessPathSearchContext *context); +// 导出的函数注释在下文中提供 +// 定义常量 UnknownVarno,值为 0 +#define UnknownVarno 0 + +// 定义常量 MAX_PLAN_DEPTH,值为 100 +#define MAX_PLAN_DEPTH 100 + +// 声明一个名为 StackNode 的结构体,包含了多个成员变量。 +typedef struct StackNode { + Plan *value_array[MAX_PLAN_DEPTH]; // Plan 指针数组 + int top; // 栈顶指针 +} StackNode; + +// 定义一个内联函数 StackNodePush,用于将 Plan 指针压入栈中 +static inline void StackNodePush(StackNode *s, Plan *node) +{ + s->top++; // 栈顶指针加一 + s->value_array[s->top] = node; // 将节点压入栈中 +} + +// 定义一个内联函数 StackNodePop,用于弹出栈顶的 Plan 指针 +static inline Plan* StackNodePop(StackNode *s) +{ + if (s->top < 0) { + elog(ERROR, "error to pop() element in %s", __FUNCTION__); // 如果栈为空,则报错 + } + + Plan *node = s->value_array[s->top]; // 获取栈顶的节点 + s->value_array[s->top] = NULL; // 将栈顶清空 + s->top--; // 栈顶指针减一 + return node; // 返回弹出的节点 +} + +// 声明一个名为 StackVarno 的结构体,包含了多个成员变量。 +typedef struct StackVarno { + Index value_array[MAX_PLAN_DEPTH]; // Index 数组 + int top; // 栈顶指针 +} StackVarno; + +// 定义一个内联函数 StackVarnoPush,用于将 Index 压入栈中 +static inline void StackVarnoPush(StackVarno *s, Index varno) +{ + s->top++; // 栈顶指针加一 + s->value_array[s->top] = varno; // 将 varno 压入栈中 +} + +// 定义一个内联函数 StackVarnoPop,用于弹出栈顶的 Index +static inline Index StackVarnoPop(StackVarno *s) +{ + if (s->top < 0) { + elog(ERROR, "error to pop() element in %s", __FUNCTION__); // 如果栈为空,则报错 + } + + Index varno = s->value_array[s->top]; // 获取栈顶的 varno + s->value_array[s->top] = 0; // 将栈顶清空 + s->top--; // 栈顶指针减一 + return varno; // 返回弹出的 varno +} + +// 声明一个名为 PlanAccessPathSearchContext 的结构体,包含了多个成员变量。 +typedef struct PlanAccessPathSearchContext { + Plan *topNode; // 顶部节点 + Plan *botNode; // 底部节点 + List *fullEntryList; // 完整的入口列表 + StackNode planStack; // 计划栈 + StackVarno varnoStack; // varno 栈 + Index relid; // 关系 ID + bool done; // 是否完成 + int numsPrevTlist; // 先前 Tlist 的数量 +} PlanAccessPathSearchContext; + +// 声明一个名为 GetWorkTableScanPlanPath 的函数,用于获取工作表扫描计划的路径 +static void GetWorkTableScanPlanPath(PlannerInfo *root, Plan *node, PlanAccessPathSearchContext *context); + +// 声明一个名为 BindPlanNodePseudoEntries 的函数,用于绑定计划节点的伪条目 +static void BindPlanNodePseudoEntries(PlannerInfo *root, Plan *node, Index varno, PlanAccessPathSearchContext *context); + +// 声明一个名为 AddPseudoEntries 的函数,用于添加伪条目 +static void AddPseudoEntries(Plan *plan, Index relid, PlanAccessPathSearchContext *context); + +// 导出的函数注释在下文中提供 + /* * -------------------------------------------------------------------------------------- * EXPORT functions @@ -258,6 +350,7 @@ static void AddPseudoEntries(Plan *plan, Index relid, PlanAccessPathSearchContex * ISCYCLE we need do so * ------------------------------------------------------------------------------------- */ +// 声明一个名为 IsCteScanProcessForStartWith 的函数,用于确定是否需要在 CteScan 节点上添加伪 TLE 以支持 Start-With 处理。 bool IsCteScanProcessForStartWith(CteScan *ctescan) { if (!IsA((Plan *)ctescan, CteScan)) { @@ -267,6 +360,7 @@ bool IsCteScanProcessForStartWith(CteScan *ctescan) return (ctescan->cteRef != NULL && ctescan->cteRef->swoptions != NULL); } + /* * ------------------------------------------------------------------------------------- * - brief: A helper function to indicate if a target entry is for SWCB's internal @@ -275,14 +369,17 @@ bool IsCteScanProcessForStartWith(CteScan *ctescan) * - return: TRUE if tle->resname is RUITR/array_key/array_col * ------------------------------------------------------------------------------------- */ +// 声明一个名为 IsPseudoInternalTargetEntry 的函数,用于检查是否是伪内部目标条目。 bool IsPseudoInternalTargetEntry(const TargetEntry *tle) { if (tle == NULL || tle->resname == NULL) { return false; } + // 获取伪列类型 StartWithOpColumnType type = GetPseudoColumnType(tle); + // 检查伪列类型是否为 SWCOL_RUITR、SWCOL_ARRAY_KEY、SWCOL_ARRAY_COL 或 SWCOL_ARRAY_SIBLINGS return (type == SWCOL_RUITR || type == SWCOL_ARRAY_KEY || type == SWCOL_ARRAY_COL || type == SWCOL_ARRAY_SIBLINGS); } @@ -294,16 +391,19 @@ bool IsPseudoInternalTargetEntry(const TargetEntry *tle) * - return: TRUE if tle->resname is level/rownum/iscycle/isleaf * ------------------------------------------------------------------------------------- */ +// 声明一个名为 IsPseudoReturnTargetEntry 的函数,用于检查是否是伪返回目标条目。 bool IsPseudoReturnTargetEntry(const TargetEntry *tle) { if (tle == NULL || tle->resname == NULL) { return false; } + // 获取伪列类型 StartWithOpColumnType type = GetPseudoColumnType(tle); + // 检查伪列类型是否为 SWCOL_LEVEL、SWCOL_ISLEAF、SWCOL_ISCYCLE 或 SWCOL_ROWNUM return (type == SWCOL_LEVEL || type == SWCOL_ISLEAF || - type == SWCOL_ISCYCLE || type == SWCOL_ROWNUM); + type == SWCOL_ISCYCLE || type == SWCOL_ROWNUM); } /* @@ -313,26 +413,31 @@ bool IsPseudoReturnTargetEntry(const TargetEntry *tle) * - return: return a new list with resname assigned * ------------------------------------------------------------------------------------- */ +// 声明一个名为 FixSwTargetlistResname 的函数,用于修复 Start-With 查询目标列表的 resname。 List *FixSwTargetlistResname(PlannerInfo *root, RangeTblEntry *curRte, List *tlist) { + // 如果 RangeTblEntry 不是 CTE 类型或未转换为 Start-With,则返回原始目标列表 if (curRte->rtekind != RTE_CTE || !curRte->swConverted) { elog(WARNING, "unusable case just original tlist"); return tlist; } + + // 获取基础列的数量 int baseColNum = list_length(curRte->eref->colnames); int natts = list_length(tlist); + // 检查列的数量是否正确 if (natts - baseColNum != STARTWITH_PSEUDO_RETURN_ATTNUMS && baseColNum != natts) { elog(ERROR, "unrecognized case baseColNum/tlist"); } - /* attach resname for target entry */ + // 为目标条目附加 resname ListCell *lc = NULL; List *newList = NIL; AttrNumber attno = 0; - /* fix the origianl output column */ + // 修复原始输出列 foreach (lc, tlist) { TargetEntry *entry = (TargetEntry *)lfirst(lc); const char *colname = NULL; @@ -342,6 +447,7 @@ List *FixSwTargetlistResname(PlannerInfo *root, RangeTblEntry *curRte, List *tli colname = g_StartWithCTEPseudoReturnColumns[attno - baseColNum].colname; } + // 将 resname 设置为修复后的列名 entry->resname = pstrdup(colname); attno++; @@ -354,10 +460,13 @@ List *FixSwTargetlistResname(PlannerInfo *root, RangeTblEntry *curRte, List *tli /* * @Brief: identify if it is a connect by level/rownum plan node */ +// 声明一个名为 IsConnectByLevelStartWithPlan 的函数,用于检查是否为 Connect By Level 的 Start-With 计划。 bool IsConnectByLevelStartWithPlan(const StartWithOp *plan) { + // 断言 plan 必须是 StartWithOp 类型且包含 swoptions(选项) Assert (IsA(plan, StartWithOp) && plan->swoptions != NULL); + // 检查 connect_by_type 选项是否为 CONNECT_BY_LEVEL 或其他混合类型 return (plan->swoptions->connect_by_type == CONNECT_BY_LEVEL || plan->swoptions->connect_by_type == CONNECT_BY_ROWNUM || plan->swoptions->connect_by_type == CONNECT_BY_MIXED_LEVEL || @@ -367,10 +476,13 @@ bool IsConnectByLevelStartWithPlan(const StartWithOp *plan) /* * @Brief: return a INT const node value, 0 if not INT const, considered as exception */ +// 声明一个名为 GetStartWithFakeConstValue 的函数,用于获取 Start-With 假常量的值。 int32 GetStartWithFakeConstValue(A_Const *n) { + // 断言 n 必须是 A_Const 类型 Assert (IsA(n, A_Const)); + // 检查 n 的值类型是否为 T_Integer,如果是则返回整数值,否则记录错误信息并返回 0 if (n->val.type == T_Integer) { return n->val.val.ival; } else { @@ -379,13 +491,17 @@ int32 GetStartWithFakeConstValue(A_Const *n) } } + /* * @Brief: return a INT const node value, 0 if not INT const, considered as exception */ +// 声明一个名为 GetStartWithFakeConstValue 的函数,用于获取 Start-With 假常量的值。 int32 GetStartWithFakeConstValue(Const *val) { + // 断言 val 必须是 Const 类型 Assert (IsA(val, Const)); + // 检查 val 是否为有效的非空 Const 类型,且数据类型为 INT4OID,如果是则返回整数值,否则记录错误信息并返回 0 if (IsA(val, Const) && !((Const*)val)->constisnull && ((Const*)val)->consttype == INT4OID) { return DatumGetInt32(((Const*)val)->constvalue); @@ -395,19 +511,24 @@ int32 GetStartWithFakeConstValue(Const *val) } } + +// 声明一个名为 GetSiblingsColNameFromFunc 的函数,用于从函数中获取兄弟节点的列名。 static char* GetSiblingsColNameFromFunc(Node* node) { + // 检查 node 是否为 FuncCall 类型 if (!IsA(node, FuncCall)) { return NULL; } ListCell* lc = NULL; foreach(lc, ((FuncCall*) node)->args) { Node *n = (Node *) lfirst(lc); + // 检查节点是否为 ColumnRef 类型 if (!IsA(n, ColumnRef)) { continue; } ColumnRef *cr = (ColumnRef *) n; int len = list_length(cr->fields); + // 如果列引用的字段数为 1 或 2,则返回字段名 if (len == CF_ONE) { return strVal(linitial(cr->fields)); } else if (len == CF_TWO) { @@ -417,17 +538,21 @@ static char* GetSiblingsColNameFromFunc(Node* node) return NULL; } + /* * @Brief: Get Siblings column name according Siblings-SortBy Clause. */ +// 声明一个名为 GetOrderSiblingsColName 的函数,用于获取 ORDER SIBLINGS BY 子句中的列名。 char *GetOrderSiblingsColName(PlannerInfo* root, SortBy *sb) { char *colname = NULL; + // 检查 sb->node 是否为 ColumnRef 类型 if (IsA(sb->node, ColumnRef)) { ColumnRef *cr = (ColumnRef *)sb->node; int len = list_length(cr->fields); + // 如果列引用的字段数为 1 或 2,则返回字段名 if (len == CF_ONE) { colname = strVal(linitial(cr->fields)); } else if (len == CF_TWO) { @@ -437,6 +562,7 @@ char *GetOrderSiblingsColName(PlannerInfo* root, SortBy *sb) A_Const *con = (A_Const *)sb->node; Value* val = &((A_Const*)con)->val; + // 检查值是否为整数类型,如果不是则记录错误信息 if (!IsA(val, Integer)) { ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), @@ -445,32 +571,37 @@ char *GetOrderSiblingsColName(PlannerInfo* root, SortBy *sb) int siblingIdx = intVal(val); + // 检查 siblingIdx 是否超出目标列表的长度,如果是则记录错误信息 if (siblingIdx > list_length(root->parse->targetList)) { ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("Order siblings by tlistIdx %d exceed length of targetList.", siblingIdx))); } + // 获取目标列表中对应索引位置的 TargetEntry 的 resname(列名) TargetEntry *te = (TargetEntry *)list_nth(root->parse->targetList, siblingIdx - 1); colname = te->resname; } - /* * We do not support function call as sort key here yet. * Try to do our best by returning the first arg column anyway. */ - if (colname == NULL) { - colname = GetSiblingsColNameFromFunc(sb->node); - } - return colname; + // 如果列名为 NULL,则尝试从函数中获取兄弟节点的列名 +if (colname == NULL) { + colname = GetSiblingsColNameFromFunc(sb->node); +} +// 返回列名 +return colname; } +// 尝试替换假常量为目标节点 static Node* tryReplaceFakeConstWithTarget(Node* node, ReplaceFakeConstContext *context) { if (node == NULL) { return node; } + // 如果节点是 Const 类型,则尝试替换为目标节点 if (IsA(node, Const)) { int constVal = GetStartWithFakeConstValue((Const *)node); if (constVal == CONNECT_BY_LEVEL_FAKEVALUE) { @@ -482,6 +613,7 @@ static Node* tryReplaceFakeConstWithTarget(Node* node, ReplaceFakeConstContext * return node; } +// 递归地替换假常量节点 static bool ReplaceFakeConstWalker(Node *node, ReplaceFakeConstContext *context) { if (node == NULL) { @@ -493,7 +625,7 @@ static bool ReplaceFakeConstWalker(Node *node, ReplaceFakeConstContext *context) OpExpr *op = (OpExpr *)node; List *newArgs = NIL; ListCell *lc = NULL; - /* replace "fake-const" to level/rownum var attr */ + // 替换 OpExpr 中的参数中的假常量 foreach (lc, op->args) { Node *n = (Node *)lfirst(lc); n = tryReplaceFakeConstWithTarget(n, context); @@ -504,6 +636,7 @@ static bool ReplaceFakeConstWalker(Node *node, ReplaceFakeConstContext *context) } case T_TypeCast: { TypeCast* tc = (TypeCast*) node; + // 替换 TypeCast 中的参数中的假常量 tc->arg = tryReplaceFakeConstWithTarget(tc->arg, context); break; } @@ -511,6 +644,7 @@ static bool ReplaceFakeConstWalker(Node *node, ReplaceFakeConstContext *context) ScalarArrayOpExpr* opexpr = (ScalarArrayOpExpr*)node; ListCell *lc = NULL; List *newArgs = NIL; + // 替换 ScalarArrayOpExpr 中的参数中的假常量 foreach (lc, opexpr->args) { Node *n = (Node *)lfirst(lc); n = tryReplaceFakeConstWithTarget(n, context); @@ -523,7 +657,7 @@ static bool ReplaceFakeConstWalker(Node *node, ReplaceFakeConstContext *context) FuncExpr* fexpr = (FuncExpr*)node; List *newArgs = NIL; ListCell *lc = NULL; - /* replace "fake-const" to level/rownum var attr */ + // 替换 FuncExpr 中的参数中的假常量 foreach (lc, fexpr->args) { Node *n = (Node *)lfirst(lc); n = tryReplaceFakeConstWithTarget(n, context); @@ -533,6 +667,7 @@ static bool ReplaceFakeConstWalker(Node *node, ReplaceFakeConstContext *context) break; } default: { + // 如果节点类型不支持替换假常量,则记录错误信息 ereport(DEBUG1, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("StartWithOp's ConnectByLevel only suport simple connect-by expr"), @@ -541,10 +676,12 @@ static bool ReplaceFakeConstWalker(Node *node, ReplaceFakeConstContext *context) break; } } + // 递归处理子节点 return expression_tree_walker(node, (bool (*)())ReplaceFakeConstWalker, (void*)context); } + /* * ------------------------------------------------------------------------------------- * @Brief: replace the fake-const value into real level/rownum attribute, so that we can @@ -560,12 +697,15 @@ static bool ReplaceFakeConstWalker(Node *node, ReplaceFakeConstContext *context) * expression preprocess, SSQ/CSSQ can be proper planned into InitPlan/SubPlan * ------------------------------------------------------------------------------------- */ +// 替换假常量 static void ReplaceFakeConst(PlannerInfo *root, StartWithOp *swplan) { + // 获取 StartWithOp 结构体中的选项信息 StartWithOptions *swoptions = swplan->swoptions; + // 获取连接级别表达式 Node *connectByLevelExpr = swoptions->connect_by_level_quals; - /* Initialize replacement context */ + /* 初始化替换上下文 */ errno_t rc = 0; ReplaceFakeConstContext context; rc = memset_s(&context, @@ -574,7 +714,7 @@ static void ReplaceFakeConst(PlannerInfo *root, StartWithOp *swplan) sizeof(ReplaceFakeConstContext)); securec_check(rc, "\0", "\0"); - /* Step 1. Find level/rownum pseudo return columns from startwith's targetlist */ + /* 步骤 1. 从 startwith 的目标列表中查找 level/rownum 伪返回列 */ ListCell *lc = NULL; foreach (lc, swplan->plan.targetlist) { TargetEntry *entry = (TargetEntry *)lfirst(lc); @@ -589,47 +729,49 @@ static void ReplaceFakeConst(PlannerInfo *root, StartWithOp *swplan) /* && rownumVar != NULL */ Assert (context.levelVar != NULL); - /* Step 2. Apply fake const replacement walker to the expression */ + /* 步骤 2. 应用假常量替换遍历器到表达式 */ ReplaceFakeConstWalker(connectByLevelExpr, &context); - /* Step 3. Do reqular const expression process */ + /* 步骤 3. 执行常量表达式处理 */ swoptions->connect_by_level_quals = eval_const_expressions(root, connectByLevelExpr); } /* - * - brief: Do possible post-path stage optimization, e.g. add Material node on an - * NestLoop's outer part + * - 简要说明:进行可能的路径后阶段优化,例如在 NestLoop 的外部部分添加 Material 节点 * - * - Note: Ideally should be handled in PathNode generation stage, in order to do so we have - * serveral case need to come up with e.g. identfy which SubQuery's output should be - * materilzed, for short term we only to improve to avoid the worst case where - * RuPlan's inner branch ReScan()-ed many times and its BaseRel with high selectivity + * - 注意:理想情况下,应该在 PathNode 生成阶段处理,为了做到这一点,我们需要提出几种情况,例如确定哪个子查询的输出应该被材料化,短期内我们只需改进以避免最坏的情况, + * 即 RuPlan 的内部分支多次 ReScan(),其 BaseRel 具有高选择性 */ static void OptimizeStartWithPlan(PlannerInfo *root, StartWithOp *swplan) { + // 获取 StartWithOp 结构体中的 RecursiveUnion 结构体 RecursiveUnion *ruplan = swplan->ruplan; - /* shunking only supported scenarios */ + /* 仅支持缩小的情况 */ if (!IsA(innerPlan(ruplan), SubqueryScan)) { return; } + // 获取子查询扫描计划 SubqueryScan *sqscan = (SubqueryScan *)innerPlan(ruplan); + // 获取子查询的计划 Plan *joinPlan = sqscan->subplan; - /* only handle nestloop and hash join case */ + /* 仅处理 NestLoop 和 HashJoin 的情况 */ if (!IsA(joinPlan, NestLoop) && !IsA(joinPlan, HashJoin)) { return; } - /* add materialize */ + /* 添加材料化节点 */ + // 获取连接计划的外部部分 Plan *joinOuter = outerPlan(joinPlan); + // 获取连接计划的内部部分 Plan *joinInner = innerPlan(joinPlan); Plan *wtscan = NULL; /* - * Only handle WorkTableScan is in inner case, when WorkTableScan is in outer tree it - * can not be material-optimized as its result is volatile for each iteration. + * 仅在 WorkTableScan 在内部树中时才处理,当 WorkTableScan 在外部树中时, + * 无法进行材料化优化,因为其结果对于每次迭代都是不稳定的。 */ if (IsA(joinPlan, NestLoop) && IsA(joinInner, WorkTableScan)) { wtscan = joinInner; @@ -637,7 +779,7 @@ static void OptimizeStartWithPlan(PlannerInfo *root, StartWithOp *swplan) IsA(joinInner->lefttree, WorkTableScan)) { wtscan = joinInner->lefttree; } else { - /* unrecognized cases */ + /* 无法识别的情况 */ return; } @@ -647,6 +789,7 @@ static void OptimizeStartWithPlan(PlannerInfo *root, StartWithOp *swplan) return; } + // 创建材料化节点 Material *mtplan = make_material(joinOuter, true); inherit_plan_locator_info((Plan *)mtplan, joinOuter); copy_plan_costsize((Plan *)mtplan, joinOuter); @@ -654,6 +797,7 @@ static void OptimizeStartWithPlan(PlannerInfo *root, StartWithOp *swplan) joinPlan->lefttree = (Plan *)mtplan; } + /* * ------------------------------------------------------------------------------------- * @Brief: The major processing logic entry point for start-with, given a CteScan node, @@ -713,23 +857,25 @@ Plan *AddStartWithOpProcNode(PlannerInfo *root, CteScan *cteplan, RecursiveUnion /* * Local Routines to support AddStartWithOpProcNode() */ +// 创建 StartWithOp 节点 static StartWithOp* CreateStartWithOpNode(PlannerInfo *root, CteScan *cteplan, RecursiveUnion *ruplan) { + // 获取 CteScan 节点的计划 Plan *plan = (Plan *)cteplan; StartWithOp *swplan = NULL; + // 获取 CteScan 所属关系的 ID Index relid = cteplan->scan.scanrelid; int natts = 0; RangeTblEntry *rte = NULL; - /* 1. fix targetlist with explict name */ + /* 1. 使用显式名称修复目标列表 */ natts = list_length(plan->targetlist); rte = rt_fetch(relid, root->parse->rtable); plan->targetlist = FixSwTargetlistResname(root, rte, plan->targetlist); - - /* 2. Build StartWithOp's targetlist */ + /* 2. 构建 StartWithOp 的目标列表 */ swplan = makeNode(StartWithOp); swplan->plan.lefttree = (Plan *)ruplan; swplan->plan.targetlist = (List *)copyObject(plan->targetlist); @@ -737,30 +883,30 @@ static StartWithOp* CreateStartWithOpNode(PlannerInfo *root, swplan->cteplan = cteplan; swplan->swoptions = (StartWithOptions *)copyObject(cteplan->cteRef->swoptions); - /* generate PRC list for both swplan and ctescan */ + /* 生成 PRC 列表,用于 swplan 和 ctescan */ swplan->prcTargetEntryList = GetPRCTargetEntryList(root, rte, swplan); cteplan->prcTargetEntryList = (List *)copyObject(swplan->prcTargetEntryList); - /* Inherit ctescan's targetlist to StartWithOp */ + /* 继承 ctescan 的目标列表到 StartWithOp */ inherit_plan_locator_info((Plan *)swplan, plan); copy_plan_costsize((Plan *)swplan, (Plan *)ruplan); /* - * Initialize PRC runtime optimization hints. + * 初始化 PRC 运行时优化提示。 * - * Basically, we do optimistic scheme assuming all PRC on CTE is able to skip + * 基本上,我们采取乐观方案,假设 CTE 上的所有 PRC 都可以跳过 */ { SetColumnSkipOptions(swplan->swExecOptions); - /* Mark level/rownum is not skipped, as they are lit-enough */ + /* 标记 level/rownum 不跳过,因为它们足够明显 */ ClearSkipLevel(swplan->swExecOptions); SetSkipIsLeaf(swplan->swExecOptions); SetSkipIsCycle(swplan->swExecOptions); ClearSkipRownum(swplan->swExecOptions); } - /* fix original Ruplan refered place */ + /* 修复原始 Ruplan 引用的位置 */ cteplan->subplan = (RecursiveUnion *)swplan; List *newSubplans = NIL; ListCell *lc = NULL; @@ -776,6 +922,7 @@ static StartWithOp* CreateStartWithOpNode(PlannerInfo *root, return swplan; } +// 处理连接级别的假常量 static void ProcessConnectByFakeConst(PlannerInfo *root, StartWithOp *swplan) { if (!IsConnectByLevelStartWithPlan(swplan)) { @@ -784,14 +931,15 @@ static void ProcessConnectByFakeConst(PlannerInfo *root, StartWithOp *swplan) StartWithOptions *swoptions = swplan->swoptions; - /* fix fake const in qual expr */ + /* 修复限定表达式中的假常量 */ ReplaceFakeConst(root, swplan); - /* do preprocess for connect-by-level/rownum condition */ + /* 为连接级别/行号条件进行预处理 */ ((Plan *)swplan)->qual = (List *)preprocess_expression(root, swoptions->connect_by_level_quals, EXPRKIND_QUAL); } +// 构建 StartWithOp 的内部目标列表 static void BuildStartWithInternalTargetList(PlannerInfo *root, CteScan *cteplan, StartWithOp *swplan) { @@ -799,7 +947,7 @@ static void BuildStartWithInternalTargetList(PlannerInfo *root, List *colEntryList = NIL; bool needSiblings = (swplan->swoptions->siblings_orderby_clause != NULL) ? true : false; - /* Generate internal target entry keyEntryList & colEntryList */ + /* 生成内部目标项 keyEntryList 和 colEntryList */ GenerateStartWithInternalEntries(root, cteplan, &keyEntryList, &colEntryList); if (keyEntryList == NULL) { @@ -814,27 +962,27 @@ static void BuildStartWithInternalTargetList(PlannerInfo *root, t_thrd.postgres_cxt.debug_query_string))); } - /* Add internal key/col entry list and pseudo_list for StartWithOp node */ + /* 添加内部 key/col 目标项列表和 StartWithOp 节点的伪列表 */ swplan->internalEntryList = BuildStartWithPlanPseudoTargetList((Plan *)swplan, cteplan->scan.scanrelid, keyEntryList, colEntryList, needSiblings); - /* Add internal key/col entry list for CteScan node */ + /* 添加内部 key/col 目标项列表到 CteScan 节点 */ cteplan->internalEntryList = BuildStartWithPlanPseudoTargetList((Plan *)cteplan, cteplan->scan.scanrelid, keyEntryList, colEntryList, needSiblings); - /* construct fullEntryList (RUITR + array_key + array_col ) */ + /* 构建 fullEntryList(RUITR + array_key + array_col) */ List *fullEntryList = NIL; ListCell *entry = NULL; foreach (entry, cteplan->scan.plan.targetlist) { TargetEntry *te = (TargetEntry *)lfirst(entry); - /* skip regular columns */ + /* 跳过常规列 */ if (IsPseudoReturnTargetEntry(te) || IsPseudoInternalTargetEntry(te)) { fullEntryList = lappend(fullEntryList, copyObject(te)); } @@ -845,6 +993,7 @@ static void BuildStartWithInternalTargetList(PlannerInfo *root, swplan->fullEntryList = fullEntryList; } +// 处理排序的兄弟节点 static void ProcessOrderSiblings(PlannerInfo *root, StartWithOp *swplan) { CteScan *cteplan = swplan->cteplan; @@ -855,7 +1004,7 @@ static void ProcessOrderSiblings(PlannerInfo *root, StartWithOp *swplan) return; } - /* Fix RecursiveUnion TargetList */ + /* 修复 RecursiveUnion 的目标列表 */ ListCell *lc = NULL; foreach(lc, cteplan->scan.plan.targetlist) { TargetEntry *te = (TargetEntry *)lfirst(lc); @@ -871,13 +1020,13 @@ static void ProcessOrderSiblings(PlannerInfo *root, StartWithOp *swplan) swplan->colEntryList, true); - /* 1. Add under RU sort plan */ + /* 1. 添加在 RU 下的排序计划 */ ruplan->plan.lefttree = (Plan *)CreateSortPlanUnderRU(root, ruplan->plan.lefttree, swoptions->siblings_orderby_clause, -1); ruplan->plan.righttree = (Plan *)CreateSortPlanUnderRU(root, ruplan->plan.righttree, swoptions->siblings_orderby_clause, -1); - /* 2. Add up RU sort plan */ + /* 2. 添加在 RU 上的排序计划 */ swplan->plan.lefttree = (Plan *)CreateSortPlanAboveRU(root, swplan->plan.lefttree, -1); } @@ -915,127 +1064,131 @@ static void ProcessOrderSiblings(PlannerInfo *root, StartWithOp *swplan) * - array_col_4(type:text) * ] */ +// 构建 StartWithOp 节点的伪目标列表 static List* BuildStartWithPlanPseudoTargetList(Plan *plan, Index varno, List *key_list, List *col_list, bool needSiblings) { - Node *expr = NULL; - TargetEntry *te = NULL; - TargetEntry *pte = NULL; - List *internalEntryList = NIL; - int rc = 0; + Node *expr = NULL; // 表达式节点 + TargetEntry *te = NULL; // 目标项 + TargetEntry *pte = NULL; // 伪目标项 + List *internalEntryList = NIL; // 内部目标项列表 + int rc = 0; // 返回代码 - /* we only have to handle CteScan & RecursiveUnion node */ + // 我们只需要处理 CteScan、RecursiveUnion 或 StartWithOp 节点 Assert (IsA(plan, CteScan) || IsA(plan, RecursiveUnion) || IsA(plan, StartWithOp)); - /* 1. Add "RUITR" entry to support LEVEL */ + /* 1. 添加 "RUITR" 目标项以支持 LEVEL */ expr = (Node *)makeVar(varno, list_length(plan->targetlist) + 1, - INT4OID, -1, InvalidOid, 0); - pte = makeTargetEntry((Expr *)expr, list_length(plan->targetlist) + 1, "RUITR", false); - plan->targetlist = lappend(plan->targetlist, pte); - internalEntryList = lappend(internalEntryList, pte); + INT4OID, -1, InvalidOid, 0); // 创建 INT4 类型的变量 + pte = makeTargetEntry((Expr *)expr, list_length(plan->targetlist) + 1, "RUITR", false); // 创建目标项 + plan->targetlist = lappend(plan->targetlist, pte); // 将目标项添加到目标列表中 + internalEntryList = lappend(internalEntryList, pte); // 将目标项添加到内部目标项列表中 /* - * 2. Add "array_key" entry we have to add to support connect_by_isleaf, connect_by_iscycle, + * 2. 添加 "array_key" 目标项,以支持 connect_by_isleaf、connect_by_iscycle、 * order-siblings */ - ListCell *lc = NULL; - foreach (lc, key_list) { - te = (TargetEntry *)lfirst(lc); - char resname[NAMEDATALEN] = {0}; + ListCell *lc = NULL; // 遍历列表的循环变量 + foreach (lc, key_list) { // 遍历关键列表 + te = (TargetEntry *)lfirst(lc); // 获取目标项 + char resname[NAMEDATALEN] = {0}; // 用于存储目标项的名称 - rc = sprintf_s(resname, NAMEDATALEN, "array_key_%d", te->resno); + rc = sprintf_s(resname, NAMEDATALEN, "array_key_%d", te->resno); // 格式化目标项名称 securec_check_ss(rc, "\0", "\0"); expr = (Node *)makeVar(varno, list_length(plan->targetlist) + 1, - TEXTOID, -1, TEXT_COLLCATION, 0); + TEXTOID, -1, TEXT_COLLCATION, 0); // 创建 TEXT 类型的变量 pte = makeTargetEntry((Expr *)expr, list_length(plan->targetlist) + 1, - pstrdup(resname), false); - plan->targetlist = lappend(plan->targetlist, pte); - internalEntryList = lappend(internalEntryList, pte); + pstrdup(resname), false); // 创建目标项 + plan->targetlist = lappend(plan->targetlist, pte); // 将目标项添加到目标列表中 + internalEntryList = lappend(internalEntryList, pte); // 将目标项添加到内部目标项列表中 } /* - * 3. Add "array_col" to support , connect_by_root + * 3. 添加 "array_col" 目标项以支持 connect_by_root */ - foreach (lc, col_list) { - te = (TargetEntry *)lfirst(lc); - char resname[NAMEDATALEN] = {0}; + foreach (lc, col_list) { // 遍历列列表 + te = (TargetEntry *)lfirst(lc); // 获取目标项 + char resname[NAMEDATALEN] = {0}; // 用于存储目标项的名称 - rc = sprintf_s(resname, NAMEDATALEN, "array_col_%d", te->resno); + rc = sprintf_s(resname, NAMEDATALEN, "array_col_%d", te->resno); // 格式化目标项名称 securec_check_ss(rc, "\0", "\0"); expr = (Node *)makeVar(varno, list_length(plan->targetlist) + 1, - TEXTOID, -1, TEXT_COLLCATION, 0); + TEXTOID, -1, TEXT_COLLCATION, 0); // 创建 TEXT 类型的变量 pte = makeTargetEntry((Expr *)expr, list_length(plan->targetlist) + 1, - pstrdup(resname), false); - plan->targetlist = lappend(plan->targetlist, pte); - internalEntryList = lappend(internalEntryList, pte); + pstrdup(resname), false); // 创建目标项 + plan->targetlist = lappend(plan->targetlist, pte); // 将目标项添加到目标列表中 + internalEntryList = lappend(internalEntryList, pte); // 将目标项添加到内部目标项列表中 } /* - * Add "array_siblings" pseudo columns support if need + * 如果需要,添加 "array_siblings" 伪列支持 */ - if (needSiblings) { + if (needSiblings) { // 如果需要伪列支持 expr = (Node *)makeVar(varno, list_length(plan->targetlist) + 1, - BYTEAOID, -1, 0, 0); - pte = makeTargetEntry((Expr *)expr, list_length(plan->targetlist) + 1, "array_siblings", false); - plan->targetlist = lappend(plan->targetlist, pte); - internalEntryList = lappend(internalEntryList, pte); + BYTEAOID, -1, 0, 0); // 创建 BYTEA 类型的变量 + pte = makeTargetEntry((Expr *)expr, list_length(plan->targetlist) + 1, "array_siblings", false); // 创建目标项 + plan->targetlist = lappend(plan->targetlist, pte); // 将目标项添加到目标列表中 + internalEntryList = lappend(internalEntryList, pte); // 将目标项添加到内部目标项列表中 } - return internalEntryList; + return internalEntryList; // 返回内部目标项列表 } + /* * @brief: check if a given connect by function is valid for its args, currently only * connect_by_root()/sys_connect_by_path() is supported */ +// 检查无效的 connect_by_root 和 sys_connect_by_path 函数参数 static void CheckInvalidConnectByfuncArgs(CteScan *cteplan, Oid funcid, List *arg_vars) { - /* for none connect by function we do not do arg checks on its type */ + // 如果不是 connect_by_root 或 sys_connect_by_path 函数,直接返回 if (funcid != CONNECT_BY_ROOT_FUNCOID && funcid != SYS_CONNECT_BY_PATH_FUNCOID) { return; } - int maxBaseRelAttnum = - list_length(cteplan->scan.plan.targetlist) - STARTWITH_PSEUDO_RETURN_ATTNUMS; + // 计算基本关系的最大属性号 + int maxBaseRelAttnum = list_length(cteplan->scan.plan.targetlist) - STARTWITH_PSEUDO_RETURN_ATTNUMS; - /* validate common cases */ + // 如果参数变量列表长度大于 1,报错并返回 if (list_length(arg_vars) > 1) { elog(ERROR, "only single column can be put as argument in connect_by_root/sys_connect_by_path()"); } - /* - * For compatible with Oracle where sys_connect_by_path() and connect_by_root() - * allow a const value set as 1st paramameter, return anyway - */ + // 如果参数变量列表为空,直接返回 if (arg_vars == NIL) { return; } + // 获取参数变量 Var *arg = (Var *)linitial(arg_vars); + + // 如果参数变量的属性号大于最大基本关系属性号,报错并返回 if (arg->varattno > maxBaseRelAttnum) { elog(ERROR, "only base table column can be specified in connect_by_root/sys_connect_by_path()"); } /* valid specific cases */ + // 根据函数 ID 进行不同的检查 switch (funcid) { case CONNECT_BY_ROOT_FUNCOID: { - /* for connect_by_root() we only check if base column */ + // 对于 connect_by_root(),只需检查是否为基本列即可 break; } case SYS_CONNECT_BY_PATH_FUNCOID: { - /* for sys_connect_by_path() allow textual data types TEXT/VARCHAR/CHAR */ + // 对于 sys_connect_by_path(),检查参数变量的类型是否为文本类型 Oid typid = arg->vartype; if (typid != TEXTOID && typid != VARCHAROID && typid != BPCHAROID && typid != NVARCHAR2OID) { - elog(ERROR, "only text type(CHAR/VARCHAR/NVARCHAR2/TEXT) is allow for sys_connect_by_path()"); + elog(ERROR, "only text type(CHAR/VARCHAR/NVARCHAR2/TEXT) is allowed for sys_connect_by_path()"); } break; } - default: { - elog(ERROR, "unknown functions funid:%u in ConnectByFuncArg check.", funcid); + // 未知函数 ID,报错 + elog(ERROR, "unknown function funcid:%u in ConnectByFuncArg check.", funcid); } } } @@ -1048,8 +1201,10 @@ static void CheckInvalidConnectByfuncArgs(CteScan *cteplan, Oid funcid, List *ar * PullUpConnectByFuncVars() * -------------------------------------------------------------------------------------- */ +// 从目标表达式中提取 connect_by_root 和 sys_connect_by_path 函数的变量 static List *PullUpConnectByFuncVars(PlannerInfo *root, CteScan *cteScan, Node *targetEntry) { + // 初始化上下文结构体 errno_t rc = 0; PullUpConnectByFuncVarContext context; rc = memset_s(&context, @@ -1058,18 +1213,23 @@ static List *PullUpConnectByFuncVars(PlannerInfo *root, CteScan *cteScan, Node * sizeof(PullUpConnectByFuncVarContext)); securec_check(rc, "\0", "\0"); + // 设置上下文结构体的属性 context.root = root; context.pullupVars = NIL; context.cteplan = cteScan; context.swplan = (StartWithOp *)cteScan->subplan; + // 调用遍历函数,提取变量 (void)PullUpConnectByFuncVarsWalker(targetEntry, &context); + // 返回提取到的变量列表 return context.pullupVars; } +// 遍历目标表达式,提取 connect_by_root 和 sys_connect_by_path 函数的变量 static bool PullUpConnectByFuncVarsWalker(Node *node, PullUpConnectByFuncVarContext *context) { + // 如果节点为空,直接返回 if (node == NULL) { return false; } @@ -1079,29 +1239,33 @@ static bool PullUpConnectByFuncVarsWalker(Node *node, PullUpConnectByFuncVarCont * 1. FuncExpr we do connect-by func validation check * 2. Var, we check if its PRC and possible PRC-skip optimization could apply */ + // 如果节点是函数表达式 FuncExpr if (IsA(node, FuncExpr)) { FuncExpr *func = (FuncExpr *)node; - /* Only handle start with hierachocal query's function cases */ + // 只处理分层查询函数的情况 if (IsHierarchicalQueryFuncOid(func->funcid)) { - /* pull-up basic vars from FuncExpr node */ - List *vars = pull_var_clause((Node*)func->args, - PVC_RECURSE_AGGREGATES, PVC_INCLUDE_PLACEHOLDERS); + // 从 FuncExpr 节点中提取基本变量 + List *vars = pull_var_clause((Node *)func->args, PVC_RECURSE_AGGREGATES, PVC_INCLUDE_PLACEHOLDERS); + // 检查提取的变量是否合法 CheckInvalidConnectByfuncArgs(context->cteplan, func->funcid, vars); + // 将提取的变量添加到上下文中的变量列表中 context->pullupVars = list_concat_unique(context->pullupVars, vars); } } else if (IsA(node, Var)) { - /* check if there is target var refer to PRC and mark them not-skipable */ + // 如果节点是变量 Var Var *var = (Var *)node; + // 检查是否有目标变量引用 PRC,并标记它们为不可跳过 int prcType = GetVarPRCType(context->cteplan->prcTargetEntryList, var); if (prcType == SWCOL_LEVEL || prcType == SWCOL_ISLEAF || - prcType == SWCOL_ISCYCLE || prcType == SWCOL_ROWNUM) { + prcType == SWCOL_ISCYCLE || prcType == SWCOL_ROWNUM) { MarkPRCNotSkip(context->swplan, prcType); } } + // 继续遍历表达式树的子节点 return expression_tree_walker(node, - (bool (*)())PullUpConnectByFuncVarsWalker, (void*)context); + (bool (*)())PullUpConnectByFuncVarsWalker, (void *)context); } /* @@ -1112,14 +1276,15 @@ static bool PullUpConnectByFuncVarsWalker(Node *node, PullUpConnectByFuncVarCont * - MarkPRCNotSkip() * -------------------------------------------------------------------------------------- */ +// 获取 PRC(Pseudo Return Column)的目标条目列表 static List *GetPRCTargetEntryList(PlannerInfo *root, RangeTblEntry *rte, StartWithOp *swplan) { - int natts = 0; - int baseColNum = 0; - Plan *plan = (Plan *)swplan; - List *prcTargetEntryList = NIL; + int natts = 0; + int baseColNum = 0; + Plan *plan = (Plan *)swplan; + List *prcTargetEntryList = NIL; - /* check if PRC targetlist is already generated */ + /* 检查是否已生成 PRC 目标列表 */ if (swplan->prcTargetEntryList != NIL) { return swplan->prcTargetEntryList; } @@ -1127,12 +1292,11 @@ static List *GetPRCTargetEntryList(PlannerInfo *root, RangeTblEntry *rte, StartW baseColNum = list_length(rte->eref->colnames) - STARTWITH_PSEUDO_RETURN_ATTNUMS; natts = list_length(plan->targetlist); - if (natts - baseColNum != STARTWITH_PSEUDO_RETURN_ATTNUMS && - baseColNum != natts) { - elog(ERROR, "unrecognized case baseColNum/tlist"); + if (natts - baseColNum != STARTWITH_PSEUDO_RETURN_ATTNUMS && baseColNum != natts) { + elog(ERROR, "未识别的情况:baseColNum/tlist"); } - /* attach resname for target entry */ + /* 为目标条目附加 resname */ ListCell *lc = NULL; foreach (lc, plan->targetlist) { TargetEntry *entry = (TargetEntry *)lfirst(lc); @@ -1141,12 +1305,13 @@ static List *GetPRCTargetEntryList(PlannerInfo *root, RangeTblEntry *rte, StartW } } - Assert (prcTargetEntryList != NIL); + Assert(prcTargetEntryList != NIL); return prcTargetEntryList; } -static int GetVarPRCType(List *prcList, const Var* var) +// 获取变量的 PRC 类型 +static int GetVarPRCType(List *prcList, const Var *var) { ListCell *lc = NULL; int prcType = SWCOL_LEVEL; @@ -1161,15 +1326,16 @@ static int GetVarPRCType(List *prcList, const Var* var) break; } - /* none-prc column, we consider its type as unknown */ + /* 非 PRC 列,我们将其类型视为未知 */ if (prcType != SWCOL_LEVEL && prcType != SWCOL_ISLEAF && - prcType != SWCOL_ISCYCLE && prcType != SWCOL_ROWNUM) { + prcType != SWCOL_ISCYCLE && prcType != SWCOL_ROWNUM) { prcType = SWCOL_UNKNOWN; } return prcType; } +// 标记不跳过的 PRC static void MarkPRCNotSkip(StartWithOp *swplan, int prcType) { if (prcType == SWCOL_LEVEL || prcType == SWCOL_ROWNUM) { @@ -1186,7 +1352,7 @@ static void MarkPRCNotSkip(StartWithOp *swplan, int prcType) break; } default: { - /* do nothing */ + /* 什么都不做 */ } } @@ -1210,50 +1376,59 @@ static void MarkPRCNotSkip(StartWithOp *swplan, int prcType) static void GenerateStartWithInternalEntries(PlannerInfo *root, CteScan *cteplan, List **keyEntryList, List **colEntryList) { - Assert (IsA(cteplan, CteScan) && IsA(cteplan->subplan, StartWithOp)); + // 检查输入参数cteplan是否是CteScan类型并且其子计划是StartWithOp类型 + Assert(IsA(cteplan, CteScan) && IsA(cteplan->subplan, StartWithOp)); + // 将cteplan强制转换为Plan类型 Plan *plan = (Plan *)cteplan; + // 将cteplan的子计划强制转换为StartWithOp类型 StartWithOp *swplan = (StartWithOp *)cteplan->subplan; + // 声明一个ListCell指针lc,用于遍历root->origin_tlist ListCell *lc = NULL; + // 声明一个临时的List指针tmp_list List *tmp_list = NULL; - /* - * First, match cte targetEntry in funcs like connect_by_root(xxx) and - * SYS_CONNECT_BY_PATH(xxx, '/') - */ + // 遍历root->origin_tlist列表 foreach(lc, root->origin_tlist) { + // 获取当前列表元素(TargetEntry类型)并强制转换为TargetEntry类型 TargetEntry *origin = (TargetEntry *)lfirst(lc); + // 调用PullUpConnectByFuncVars函数处理origin,并将结果存入vars列表 List *vars = PullUpConnectByFuncVars(root, cteplan, (Node *)origin); + // 将vars与tmp_list合并,生成一个新的tmp_list tmp_list = list_concat(tmp_list, vars); } - /* process those specified in where clause */ + // 继续遍历plan->qual列表,执行与上面相似的操作 foreach(lc, plan->qual) { TargetEntry *origin = (TargetEntry *)lfirst(lc); List *vars = PullUpConnectByFuncVars(root, cteplan, (Node *)origin); tmp_list = list_concat(tmp_list, vars); } + // 再次遍历plan->targetlist列表 foreach (lc, plan->targetlist) { + // 获取当前列表元素(TargetEntry类型)并强制转换为TargetEntry类型 TargetEntry *te = (TargetEntry *)lfirst(lc); - Assert (IsA(te->expr, Var)); + // 断言te的表达式是Var类型 + Assert(IsA(te->expr, Var)); + // 如果te->expr在tmp_list中存在,将te添加到colEntryList中 if (list_member(tmp_list, te->expr)) { *colEntryList = lappend(*colEntryList, te); } } - /* - * Second, match cte targetEntry of connectby prior columns, like prior pid = id then pid is - * key - */ + // 获取cteplan的cteRef属性 CommonTableExpr *cteRef = cteplan->cteRef; + // 遍历cteRef的swoptions->prior_key_index列表 foreach(lc, cteRef->swoptions->prior_key_index) { + // 获取当前列表元素(int类型)并强制转换为int类型 int index = lfirst_int(lc); + // 获取plan->targetlist中的第index个元素并添加到keyEntryList中 TargetEntry *te = (TargetEntry *)list_nth(plan->targetlist, index); *keyEntryList = lappend(*keyEntryList, te); } - /* do not skip iscycle PRC if "nocycle" is specified in HQ clause */ + // 如果swplan的swoptions->nocycle属性为真,调用ClearSkipIsCycle函数 if (swplan->swoptions->nocycle) { ClearSkipIsCycle(swplan->swExecOptions); } @@ -1261,16 +1436,20 @@ static void GenerateStartWithInternalEntries(PlannerInfo *root, CteScan *cteplan return; } + /* * @Brief: Add sort operator on top of CteScan with column with sort key * {"LEVEL", "sibling_order_columns"} */ static OrderSiblingSortEntry* CreateOrderSiblingSortEntry(TargetEntry *entry, SortByDir dir) { - OrderSiblingSortEntry *sortEntry = - (OrderSiblingSortEntry *)palloc0(sizeof(OrderSiblingSortEntry)); + // 分配内存以存储OrderSiblingSortEntry结构 + OrderSiblingSortEntry *sortEntry = (OrderSiblingSortEntry *)palloc0(sizeof(OrderSiblingSortEntry)); + // 将传入的entry赋值给sortEntry的tle属性 sortEntry->tle = entry; + + // 根据排序方向dir设置sortCmpOp数组的值 switch (dir) { case SORTBY_DEFAULT: case SORTBY_ASC: { @@ -1285,9 +1464,8 @@ static OrderSiblingSortEntry* CreateOrderSiblingSortEntry(TargetEntry *entry, So sortEntry->sortCmpOp[SORTCMP_GT] = true; break; } - default: { - /* for default case we treat as ASC */ + // 默认情况下,设置为升序 sortEntry->sortCmpOp[SORTCMP_LT] = true; sortEntry->sortCmpOp[SORTCMP_EQ] = false; sortEntry->sortCmpOp[SORTCMP_GT] = false; @@ -1297,25 +1475,34 @@ static OrderSiblingSortEntry* CreateOrderSiblingSortEntry(TargetEntry *entry, So return sortEntry; } + static Sort *CreateSiblingsSortPlan(PlannerInfo* root, Plan* lefttree, List *sortEntryList, double limit_tuples) { + // 声明一个Sort指针sort Sort *sort = NULL; + // 获取sortEntryList中的排序键数目 int numsortkeys = list_length(sortEntryList); + // 为排序键分配内存 AttrNumber* sortColIdx = (AttrNumber*)palloc(numsortkeys * sizeof(AttrNumber)); Oid* sortOperators = (Oid*)palloc(numsortkeys * sizeof(Oid)); Oid* collations = (Oid*)palloc(numsortkeys * sizeof(Oid)); bool* nullsFirst = (bool*)palloc(numsortkeys * sizeof(bool)); numsortkeys = 0; + // 初始化排序操作符和是否支持哈希 Oid sortoplt = InvalidOid; Oid sortopeq = InvalidOid; Oid sortopgt = InvalidOid; bool hashable = false; + // 声明一个ListCell指针l,用于遍历sortEntryList ListCell* l = NULL; + // 遍历sortEntryList列表 foreach (l, sortEntryList) { + // 获取当前列表元素(OrderSiblingSortEntry类型)并强制转换为OrderSiblingSortEntry类型 OrderSiblingSortEntry *entry = (OrderSiblingSortEntry *)lfirst(l); + // 获取排序操作符和是否支持哈希 get_sort_group_operators(exprType((Node*)entry->tle->expr), entry->sortCmpOp[SORTCMP_LT], entry->sortCmpOp[SORTCMP_EQ], @@ -1323,15 +1510,17 @@ static Sort *CreateSiblingsSortPlan(PlannerInfo* root, Plan* lefttree, &sortoplt, &sortopeq, &sortopgt, &hashable); Oid sortop = InvalidOid; + // 根据排序方向选择相应的排序操作符 if (entry->sortCmpOp[SORTCMP_LT]) { sortop = sortoplt; } else if (entry->sortCmpOp[SORTCMP_GT]) { sortop = sortopgt; } else { - /* we shouldn't get here */ + // 默认情况下,使用相等操作符 sortop = sortopeq; } + // 将排序键的信息存入相应的数组中 sortColIdx[numsortkeys] = entry->tle->resno; sortOperators[numsortkeys] = sortop; collations[numsortkeys] = exprCollation((Node*)entry->tle->expr); @@ -1340,6 +1529,7 @@ static Sort *CreateSiblingsSortPlan(PlannerInfo* root, Plan* lefttree, numsortkeys++; } + // 创建Sort节点 sort = make_sort(root, lefttree, numsortkeys, sortColIdx, sortOperators, collations, nullsFirst, limit_tuples); @@ -1348,21 +1538,27 @@ static Sort *CreateSiblingsSortPlan(PlannerInfo* root, Plan* lefttree, static bool IsNullsFirst(SortBy *sortby) { + // 断言sortby不为空 Assert(sortby != NULL); + // 初始化返回值ret为false bool ret = false; + // 根据排序方向和空值位置设置返回值ret bool reverse = (sortby->sortby_dir == SORTBY_DESC); switch (sortby->sortby_nulls) { case SORTBY_NULLS_DEFAULT: - /* NULLS FIRST is default for DESC; other way for ASC */ + // 默认情况下,如果是降序排序,则空值在前,否则在后 ret = reverse; break; case SORTBY_NULLS_FIRST: + // 空值在前 ret = true; break; case SORTBY_NULLS_LAST: + // 空值在后 ret = false; break; default: + // 报告错误,不支持的空值位置 ereport(ERROR, (errcode(ERRCODE_UNEXPECTED_NODE_STATE), errmsg("unrecognized sortby_nulls: %d", sortby->sortby_nulls))); @@ -1385,50 +1581,61 @@ static bool IsNullsFirst(SortBy *sortby) */ static Sort *CreateSortPlanUnderRU(PlannerInfo* root, Plan* lefttree, List *siblings, double limit_tuples) { - Sort *sort = NULL; - List *sortEntryList = NIL; - ListCell *lc = NULL; - ListCell *lc1 = NULL; + Sort *sort = NULL; // 声明一个Sort计划节点指针,并初始化为NULL + List *sortEntryList = NIL; // 声明一个用于存储排序条目的列表,并初始化为空列表 + ListCell *lc = NULL; // 声明一个列表元素遍历的指针 + ListCell *lc1 = NULL; // 声明另一个列表元素遍历的指针 + // 遍历传入的siblings列表 foreach (lc, siblings) { - SortBy *sb = (SortBy *)lfirst(lc); + SortBy *sb = (SortBy *)lfirst(lc); // 获取当前元素,并将其强制转换为SortBy结构体 + // 调用GetOrderSiblingsColName函数获取排序列的名称 char *colname = GetOrderSiblingsColName(root, sb); + + // 如果获取到的列名为NULL,发出警告并继续下一次循环 if (colname == NULL) { ereport(WARNING, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("Order siblings by clause has unexpected key."))); - continue; } - bool found = false; - /* search targetlist */ - foreach (lc1, lefttree->targetlist) { - TargetEntry *tle = (TargetEntry *)lfirst(lc1); + bool found = false; // 声明一个标志,用于指示是否找到了匹配的排序列 + // 遍历lefttree的目标列表(targetlist) + foreach (lc1, lefttree->targetlist) { + TargetEntry *tle = (TargetEntry *)lfirst(lc1); // 获取当前目标列表项 + + // 如果目标列表项的名称为NULL,继续下一次循环 if (tle->resname == NULL) { continue; } - /* one more fix name */ + // 从目标列表项的名称中提取标签信息 char *label = strrchr(tle->resname, '@'); label += 1; label = pstrdup(label); + // 如果提取的标签与排序列的名称匹配,表示找到了匹配的排序列 if (pg_strcasecmp(label, colname) == 0) { found = true; + // 创建一个OrderSiblingSortEntry结构,并设置其属性 OrderSiblingSortEntry *entry = - CreateOrderSiblingSortEntry(tle,sb->sortby_dir); + CreateOrderSiblingSortEntry(tle, sb->sortby_dir); entry->sortByNullsFirst = IsNullsFirst(sb); + + // 将该排序条目添加到sortEntryList中 sortEntryList = lappend(sortEntryList, entry); + // 如果启用了调试标志,发出警告消息 if (u_sess->attr.attr_sql.enable_startwith_debug) { elog(WARNING, "Good we got siblings sort key under RU."); } } } + // 如果未找到匹配的排序列,发出错误消息 if (!found) { ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmodule(MOD_OPT), @@ -1439,6 +1646,7 @@ static Sort *CreateSortPlanUnderRU(PlannerInfo* root, Plan* lefttree, List *sibl } } + // 如果sortEntryList为空,发出错误消息 if (sortEntryList == NIL) { ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmodule(MOD_OPT), @@ -1446,16 +1654,18 @@ static Sort *CreateSortPlanUnderRU(PlannerInfo* root, Plan* lefttree, List *sibl errdetail("Siblings sort entry not found"), errcause("Incorrect query input"), erraction("Please check and revise your query"))); - } + + // 调用CreateSiblingsSortPlan函数创建Sort计划 sort = CreateSiblingsSortPlan(root, lefttree, sortEntryList, limit_tuples); - /* Free sort entries */ + // 释放sortEntryList中的内存 list_free_deep(sortEntryList); - return sort; + return sort; // 返回生成的Sort计划节点 } + /* * @Brief: Generate sort key above recursive union according array_siblings pseudo column * that was filled by recursive union. Like the query below @@ -1472,32 +1682,38 @@ static Sort *CreateSortPlanUnderRU(PlannerInfo* root, Plan* lefttree, List *sibl */ static Sort *CreateSortPlanAboveRU(PlannerInfo* root, Plan* lefttree, double limit_tuples) { - Sort *sort = NULL; - List *sortEntryList = NIL; - ListCell *lc = NULL; + Sort *sort = NULL; // 声明一个Sort计划节点指针,并初始化为NULL + List *sortEntryList = NIL; // 声明一个用于存储排序条目的列表,并初始化为空列表 + ListCell *lc = NULL; // 声明一个列表元素遍历的指针 + // 遍历lefttree的目标列表(targetlist) foreach (lc, lefttree->targetlist) { - TargetEntry *tle = (TargetEntry *)lfirst(lc); + TargetEntry *tle = (TargetEntry *)lfirst(lc); // 获取当前目标列表项 + // 如果目标列表项的名称为"array_siblings",表示找到了匹配的排序列 if (strcmp(tle->resname, "array_siblings") == 0) { + // 创建一个OrderSiblingSortEntry结构,并设置其属性 OrderSiblingSortEntry *entry = CreateOrderSiblingSortEntry(tle, SORTBY_ASC); sortEntryList = lappend(sortEntryList, entry); + // 如果启用了调试标志,发出警告消息 if (u_sess->attr.attr_sql.enable_startwith_debug) { elog(WARNING, "Good we got siblings sort key above RU."); } } } + // 调用CreateSiblingsSortPlan函数创建Sort计划 sort = CreateSiblingsSortPlan(root, lefttree, sortEntryList, limit_tuples); - /* Free sort entries */ + // 释放sortEntryList中的内存 list_free_deep(sortEntryList); - return sort; + return sort; // 返回生成的Sort计划节点 } + /* * -------------------------------------------------------------------------------------- * fix WorkTableScan's targetlist @@ -1512,9 +1728,9 @@ static Sort *CreateSortPlanAboveRU(PlannerInfo* root, Plan* lefttree, double lim void PushDownFullPseudoTargetlist(PlannerInfo *root, Plan *topNode, Plan *botNode, List *fullEntryList) { - Assert (fullEntryList != NIL); + Assert(fullEntryList != NIL); // 断言fullEntryList不为空 - PlanAccessPathSearchContext ctx; + PlanAccessPathSearchContext ctx; // 声明一个PlanAccessPathSearchContext结构 errno_t rc = EOK; rc = memset_s(&ctx, sizeof(PlanAccessPathSearchContext), 0, sizeof(PlanAccessPathSearchContext)); securec_check(rc, "\0", "\0"); @@ -1523,6 +1739,7 @@ void PushDownFullPseudoTargetlist(PlannerInfo *root, Plan *topNode, Plan *botNod ctx.botNode = botNode; ctx.fullEntryList = fullEntryList; + // 初始化计划和变量号栈 for (int i = 0; i < MAX_PLAN_DEPTH; i++) { ctx.planStack.value_array[i] = NULL; ctx.varnoStack.value_array[i] = 0; @@ -1537,9 +1754,14 @@ void PushDownFullPseudoTargetlist(PlannerInfo *root, Plan *topNode, Plan *botNod * Get the plan search path and add pseudo entry to let pseudo content return * along with executor iteration normally. */ + // 获取工作表扫描计划路径 GetWorkTableScanPlanPath(root, (Plan *)topNode, &ctx); - Assert (ctx.planStack.top == ctx.varnoStack.top); + + // 断言计划栈和变量号栈的大小一致 + Assert(ctx.planStack.top == ctx.varnoStack.top); int nodeNums = ctx.planStack.top + 1; + + // 如果节点数为0,直接返回 if (nodeNums == 0) { return; } @@ -1547,36 +1769,36 @@ void PushDownFullPseudoTargetlist(PlannerInfo *root, Plan *topNode, Plan *botNod StringInfoData si; initStringInfo(&si); - /* Iterate the plan search path and add pseudo entry properly */ Plan *plan = NULL; Index varno = 0; while (nodeNums > 0) { plan = StackNodePop(&ctx.planStack); varno = StackVarnoPop(&ctx.varnoStack); - Assert (plan != NULL && varno > 0); + Assert(plan != NULL && varno > 0); - /* bind current node with proper pseudo entry info */ + // 绑定计划节点的伪条目 (void)BindPlanNodePseudoEntries(root, plan, varno, &ctx); - /* record current numbers of tlist */ + // 更新上一个目标列表的长度 ctx.numsPrevTlist = list_length(plan->targetlist); - /* build debug path string */ + // 添加信息到字符串缓冲中 appendStringInfo(&si, " -> %s(tlist_len:%d)", nodeTagToString(nodeTag(plan)), list_length(plan->targetlist)); nodeNums--; } + // 如果启用了调试标志,发出警告消息 if (u_sess->attr.attr_sql.enable_startwith_debug) { elog(WARNING, "Pushdown pseudo_tlist >>>>> [%s]", si.data); } + // 释放字符串缓冲的内存 pfree_ext(si.data); return; } - /* * -------------------------------------------------------------------------------------- * @Brief: Fix Array Internal Entry based on current targetlist level @@ -1587,9 +1809,11 @@ static void FixArrayInternalEntry(List *targetlsit) ListCell *lc1 = NULL; ListCell *lc2 = NULL; - foreach(lc1, targetlsit) { + // 遍历目标列表(targetlist) + foreach (lc1, targetlsit) { TargetEntry *entry = (TargetEntry *)lfirst(lc1); + // 如果目标列表项的名称包含"array_key_"或"array_col_",表示需要修复 if (entry->resname != NULL && (strstr(entry->resname, "array_key_") || strstr(entry->resname, "array_col_"))) { @@ -1597,7 +1821,8 @@ static void FixArrayInternalEntry(List *targetlsit) int attno = entry->resname[10] - '0'; int resno = 0; - foreach(lc2, targetlsit) { + // 遍历目标列表(targetlist)以查找匹配的正常列 + foreach (lc2, targetlsit) { TargetEntry *entry2 = (TargetEntry *)lfirst(lc2); if (GetPseudoColumnType(entry2) == SWCOL_REGULAR) { @@ -1611,6 +1836,7 @@ static void FixArrayInternalEntry(List *targetlsit) } } + // 构造新的数组列名称 char newArrayName[NAMEDATALEN]; errno_t rc = memset_s(newArrayName, NAMEDATALEN, 0, NAMEDATALEN); securec_check(rc, "\0", "\0"); @@ -1623,7 +1849,7 @@ static void FixArrayInternalEntry(List *targetlsit) securec_check_ss(rc, "\0", "\0"); } - entry->resname = pstrdup(newArrayName); + entry->resname = pstrdup(newArrayName); // 更新目标列表项的名称 } } @@ -1638,51 +1864,36 @@ static void FixArrayInternalEntry(List *targetlsit) */ static bool IsPseudoInternalEntryExists(List *targetlist, TargetEntry *tle) { - bool result = false; - ListCell *lc = NULL; + bool result = false; // 初始化结果为false + ListCell *lc = NULL; // 声明一个列表元素遍历的指针 - Assert (tle != NULL); + Assert(tle != NULL); // 断言tle不为空 - /* - * Exist emtry targetlist during start with...connect by push down pseudo columns. - * e.x - * Select * from (select t1.c1 as num from t1 join t2 on true) as ss, t3 - * start with ss.num = t3.c2 - * connect by prior t3.c1 = t3.c3; - * - * In this case, if we have plan like - * Nestloop1 --> t1 - * --> NestLoop2 --> t2 - * --> worktable - * - * Then the NestLoop2 don't have any targetlist, but we also should - * inherit targetlist from wortable scan. - * */ if (targetlist == NULL) { - return result; + return result; // 如果目标列表为空,直接返回false } - + // 遍历目标列表(targetlist) foreach (lc, targetlist) { TargetEntry *curEntry = (TargetEntry *)lfirst(lc); + + // 如果当前目标列表项不是伪目标列表项(SWCOL_REGULAR),继续下一次循环 if (GetPseudoColumnType(curEntry) == SWCOL_REGULAR) { - /* - * skip regular entry, we only check special entries e.g. pseodu return column, - * internal entry - */ continue; } - Assert (IsA(tle->expr, Var)); + Assert(IsA(tle->expr, Var)); // 断言tle的表达式是Var类型 + // 如果当前目标列表项的名称与tle的名称相同,设置结果为true并退出循环 if (pg_strcasecmp(curEntry->resname, tle->resname) == 0) { result = true; break; } } - return result; + return result; // 返回结果 } + /* * -------------------------------------------------------------------------------------- * @brief: Add each pseudo entry to arget plan node @@ -1690,47 +1901,40 @@ static bool IsPseudoInternalEntryExists(List *targetlist, TargetEntry *tle) */ static void AddPseudoEntries(Plan *plan, Index relid, PlanAccessPathSearchContext *context) { - ListCell *lc = NULL; + ListCell *lc = NULL; // 声明一个列表元素遍历的指针 List *fullEntryList = context->fullEntryList; - Assert (plan != NULL && relid > 0 && fullEntryList != NIL); + Assert(plan != NULL && relid > 0 && fullEntryList != NIL); // 断言plan、relid和fullEntryList都有效 - int index = list_length(plan->targetlist); + int index = list_length(plan->targetlist); // 获取目标列表的长度 int attno = 0; bool adapt = false; - /* caculate PseudoColumn varattno from prev plan targetlist */ + // 如果context中有上一个目标列表的长度,计算要适应的attno if (context->numsPrevTlist != -1) { attno = context->numsPrevTlist - list_length(fullEntryList); adapt = true; } - /* Only add entry that does not added before */ + // 遍历fullEntryList foreach (lc, fullEntryList) { TargetEntry *entry = (TargetEntry *)copyObject((TargetEntry *)lfirst(lc)); - /* - * In "pseudo targetlist" push down process, we are going to avoid adding - * duplicate entry we do such kind of check, not-exit puls resno is appended - * at tail - */ + // 如果目标列表中不存在相同的伪目标列表项,执行以下操作 if (!IsPseudoInternalEntryExists(plan->targetlist, entry)) { - /* - * The target entry's resno,varno,varattno is created in CteScan planing stage, - * before we add it to curernt plan, we need do proper resno adjustment - */ - Assert (IsA(entry->expr, Var)); + Assert(IsA(entry->expr, Var)); // 断言entry的表达式是Var类型 index++; - ((Var *)entry->expr)->varno = relid; + ((Var *)entry->expr)->varno = relid; // 设置Var的varno if (adapt) { attno++; - ((Var *)entry->expr)->varattno = attno; + ((Var *)entry->expr)->varattno = attno; // 适应attno } entry->resno = index; - plan->targetlist = lappend(plan->targetlist, entry); + plan->targetlist = lappend(plan->targetlist, entry); // 添加伪目标列表项到计划的目标列表中 } } + // 如果botNode不为空,调用FixArrayInternalEntry修复数组内部条目 if (context->botNode != NULL) { FixArrayInternalEntry(plan->targetlist); } @@ -1738,6 +1942,7 @@ static void AddPseudoEntries(Plan *plan, Index relid, PlanAccessPathSearchContex return; } + /* * -------------------------------------------------------------------------------------- * @brief: Add pseudo entries to arget plan node @@ -1746,25 +1951,21 @@ static void AddPseudoEntries(Plan *plan, Index relid, PlanAccessPathSearchContex static void BindPlanNodePseudoEntries(PlannerInfo *root, Plan *plan, Index varno, PlanAccessPathSearchContext *context) { - ListCell *lc = NULL; + ListCell *lc = NULL; // 声明一个列表元素遍历的指针 switch (nodeTag(plan)) { case T_RecursiveUnion: { - /* - * for recursive-union, besides do proper pseudo entry adding, we need build - * a separate list to help tupleslot-conversion(RuScan->StartWithOp) - */ RecursiveUnion *ruplan = (RecursiveUnion *)plan; - /* - * Already add pseudo entry to RecursiveUnion once order siblings by exist, - * so we don't need add pseudo entry again. - */ + // 如果ruplan的internalEntryList为空,执行以下操作 if (ruplan->internalEntryList != NULL) { break; } + // 调用AddPseudoEntries为plan添加伪目标列表项 AddPseudoEntries(plan, varno, context); + + // 遍历context的fullEntryList foreach (lc, context->fullEntryList) { TargetEntry *entry = (TargetEntry *)copyObject((TargetEntry *)lfirst(lc)); if (IsPseudoInternalTargetEntry(entry)) { @@ -1774,7 +1975,8 @@ static void BindPlanNodePseudoEntries(PlannerInfo *root, Plan *plan, break; } - /* process regular case */ + + // 对于以下类型的节点,调用AddPseudoEntries为plan添加伪目标列表项 case T_Hash: case T_HashJoin: case T_SubqueryScan: @@ -1794,6 +1996,7 @@ static void BindPlanNodePseudoEntries(PlannerInfo *root, Plan *plan, break; } + // 对于其他类型的节点,报错不支持 default: { ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmodule(MOD_OPT), errmsg("%s is not supported in start with / connect by clauses", nodeTagToString(nodeTag(plan))), @@ -1803,7 +2006,6 @@ static void BindPlanNodePseudoEntries(PlannerInfo *root, Plan *plan, } } } - /* * -------------------------------------------------------------------------------------- * @Brief: Generate a minimal set of plan-path reaching WTScan and to add pseudo entry, @@ -1826,94 +2028,64 @@ static void BindPlanNodePseudoEntries(PlannerInfo *root, Plan *plan, static void GetWorkTableScanPlanPath(PlannerInfo *root, Plan *node, PlanAccessPathSearchContext *context) { - StackNode *planStack = &context->planStack; - StackVarno *varnoStack = &context->varnoStack; + StackNode *planStack = &context->planStack; // 获取计划节点堆栈的引用 + StackVarno *varnoStack = &context->varnoStack; // 获取变量号堆栈的引用 if (node == NULL) { - return; + return; // 如果节点为空,直接返回 } if (context->done) { - return; + return; // 如果上下文中的标志表示已完成,直接返回 } - /* enqueue() */ - StackNodePush(planStack, node); + StackNodePush(planStack, node); // 将当前节点压入计划节点堆栈 - switch (nodeTag(node)) { + switch (nodeTag(node)) { // 根据节点的类型执行不同的操作 case T_CteScan: { - CteScan *ctescan = (CteScan *)node; - if ((Node *)context->botNode == (Node *)ctescan) { - context->done = true; + CteScan *ctescan = (CteScan *)node; + if ((Node *)context->botNode == (Node *)ctescan) { + context->done = true; // 设置上下文中的标志表示已完成 - Assert (context->relid == 0); - context->relid = ctescan->scan.scanrelid; - StackVarnoPush(varnoStack, ctescan->scan.scanrelid); - } + Assert (context->relid == 0); + context->relid = ctescan->scan.scanrelid; + StackVarnoPush(varnoStack, ctescan->scan.scanrelid); // 压入变量号堆栈 } - break; + } + break; case T_WorkTableScan: { - WorkTableScan *wtscan = (WorkTableScan *)node; + WorkTableScan *wtscan = (WorkTableScan *)node; - /* mark finish and to exit the iteration call stack immediately */ - if (context->botNode == NULL) { - context->done = true; + if (context->botNode == NULL) { + context->done = true; // 设置上下文中的标志表示已完成 - /* set relid in current subquery level */ - Assert (context->relid == 0); - context->relid = wtscan->scan.scanrelid; - StackVarnoPush(varnoStack, wtscan->scan.scanrelid); - } + Assert (context->relid == 0); + context->relid = wtscan->scan.scanrelid; + StackVarnoPush(varnoStack, wtscan->scan.scanrelid); // 压入变量号堆栈 } - break; - + } + break; case T_VecSubqueryScan: case T_SubqueryScan: { - SubqueryScan *sq = (SubqueryScan *)node; - StackVarnoPush(varnoStack, sq->scan.scanrelid); - GetWorkTableScanPlanPath(root, sq->subplan, context); - if (!context->done) { - StackVarnoPop(varnoStack); - } + SubqueryScan *sq = (SubqueryScan *)node; + StackVarnoPush(varnoStack, sq->scan.scanrelid); // 压入变量号堆栈 + GetWorkTableScanPlanPath(root, sq->subplan, context); // 递归处理子查询计划 + if (!context->done) { + StackVarnoPop(varnoStack); // 如果未完成,弹出变量号堆栈 } - break; + } + break; default: { - Plan *lplan = outerPlan(node); Plan *rplan = innerPlan(node); - /* - * We need handle a case where RecursiveUnion's inner branch is BaseResult - * node, normally this happens a connectByExpr is evaluate as FALSE, instad - * of WT-join a BaseResult node is planned, e.g. - * e.g. - * [QUERY] - * SELECT * FROM test_hcb_ptb t1 - * START WITH id=141 - * CONNECT BY (prior pid)=id and prior pid>10 and 1=0; - * - * [PLAN] - * QUERY PLAN - * ------------------------------------------------------ - * CTE Scan on tmp_reuslt - * CTE tmp_reuslt - * -> StartWith Operator - * Start With pseudo atts: RUITR, array_key_9 - * -> Recursive Union - * -> Seq Scan on test_hcb_ptb - * Filter: (id = 141) - * -> Result - * One-Time Filter: false - * (9 rows) - * - * - */ + // 下面的代码处理默认情况,通常涉及左右子计划 + if (IsA(node, RecursiveUnion) && IsA(rplan, BaseResult) && - context->botNode == NULL) { + context->botNode == NULL) { context->done = true; StackVarnoPush(varnoStack, INNER_VAR); - /* print-out debug information */ ereport(DEBUG1, (errmodule(MOD_OPT_PLANNER), errmsg("SWCB query %s does not generate WTScan", t_thrd.postgres_cxt.debug_query_string))); @@ -1921,11 +2093,6 @@ static void GetWorkTableScanPlanPath(PlannerInfo *root, Plan *node, break; } - /* - * Before drill down into outer/inner plan tree, we set varno as OUTER_VAR/INNER for - * current level of plan node, as suppose the next outer/inner plan will build the final - * plan access path - */ if (!context->done) { StackVarnoPush(varnoStack, OUTER_VAR); } @@ -1934,9 +2101,7 @@ static void GetWorkTableScanPlanPath(PlannerInfo *root, Plan *node, StackVarnoPop(varnoStack); } - /* process right tree */ if (!context->done) { - /* set varno for current level of plan node, we need skip once lplan is done */ StackVarnoPush(varnoStack, INNER_VAR); } GetWorkTableScanPlanPath(root, rplan, context); @@ -1946,14 +2111,14 @@ static void GetWorkTableScanPlanPath(PlannerInfo *root, Plan *node, } } - /* dequeue() */ if (!context->done) { - StackNodePop(planStack); + StackNodePop(planStack); // 如果未完成,弹出计划节点堆栈 } return; } + /* * -------------------------------------------------------------------------------------- * @brief: @@ -1991,31 +2156,18 @@ static void GetWorkTableScanPlanPath(PlannerInfo *root, Plan *node, void ProcessStartWithOpMixWork(PlannerInfo *root, Plan *topplan, PlannerInfo *subroot, StartWithOp *swplan) { - Assert (IsA(swplan, StartWithOp)); + Assert (IsA(swplan, StartWithOp)); // 断言确保参数 swplan 是 StartWithOp 类型的节点 - /* - * 1. Build fullEntry from current StartWithOp node to "start with" - * WorkTable - */ - PushDownFullPseudoTargetlist(root, (Plan *)swplan->ruplan, - NULL, swplan->fullEntryList); + // 调用 PushDownFullPseudoTargetlist 函数,将伪目标列表项下推到 ruplan(如果存在的话) + PushDownFullPseudoTargetlist(root, (Plan *)swplan->ruplan, NULL, swplan->fullEntryList); - /* - * 2. Add internalEntryList to CteScan node to support SWCB functions, by defualt - * we add full internal entries, only do this when there is SWCB functions exits, - * where check the StartWithOp's colEntryList - */ + // 如果 colEntryList 不为空,表示需要处理 cteplan,也将伪目标列表项下推到 cteplan if (swplan->colEntryList != NIL) { CteScan *cteplan = swplan->cteplan; - PushDownFullPseudoTargetlist(root, (Plan *)cteplan, (Plan *)cteplan, - swplan->internalEntryList); + PushDownFullPseudoTargetlist(root, (Plan *)cteplan, (Plan *)cteplan, swplan->internalEntryList); } - /* - * 3. Removing the unnecessary "Internal Target Entry" created by PRC push-down, - * ruitr/array_key/array_col, normally this kind of entry only visible on - * CteScan node - */ + // 如果未启用调试模式(enable_startwith_debug),则过滤掉 topplan 中的伪目标列表项 if (!u_sess->attr.attr_sql.enable_startwith_debug) { List *newList = NIL; ListCell *lc = NULL; @@ -2024,10 +2176,8 @@ void ProcessStartWithOpMixWork(PlannerInfo *root, Plan *topplan, if (IsPseudoInternalTargetEntry(entry)) { continue; } - newList = lappend(newList, entry); } - topplan->targetlist = newList; } diff --git a/src/gausskernel/optimizer/plan/setrefs.cpp b/src/gausskernel/optimizer/plan/setrefs.cpp index 9a0b05b4b..98856a00b 100644 --- a/src/gausskernel/optimizer/plan/setrefs.cpp +++ b/src/gausskernel/optimizer/plan/setrefs.cpp @@ -58,33 +58,34 @@ typedef struct { } indexed_tlist; /* VARIABLE LENGTH STRUCT */ typedef struct { - PlannerInfo* root; - int rtoffset; + PlannerInfo* root; // 指向查询规划器的信息结构的指针 + int rtoffset; // 偏移值,通常用于调整计划中的表达式或变量引用的引用号 } fix_scan_expr_context; typedef struct { - PlannerInfo* root; - indexed_tlist* outer_itlist; - indexed_tlist* inner_itlist; - Index acceptable_rel; - int rtoffset; + PlannerInfo* root; // 指向查询规划器的信息结构的指针 + indexed_tlist* outer_itlist; // 外部输入子查询的目标列表 + indexed_tlist* inner_itlist; // 内部输入子查询的目标列表 + Index acceptable_rel; // 可接受的关系索引 + int rtoffset; // 偏移值,通常用于调整计划中的表达式或变量引用的引用号 } fix_join_expr_context; typedef struct { - PlannerInfo* root; - indexed_tlist* subplan_itlist; - Index newvarno; - int rtoffset; + PlannerInfo* root; // 指向查询规划器的信息结构的指针 + indexed_tlist* subplan_itlist; // 子查询计划的目标列表 + Index newvarno; // 新的变量编号 + int rtoffset; // 偏移值,通常用于调整计划中的表达式或变量引用的引用号 } fix_upper_expr_context; typedef struct { - PlannerGlobal* glob; - indexed_tlist* base_itlist; - int rtoffset; - Index relid; - bool return_non_base_vars; /* Should we reject or return vars not found in base_itlist */ + PlannerGlobal* glob; // 指向全局查询规划器信息的指针 + indexed_tlist* base_itlist; // 基本查询的目标列表 + int rtoffset; // 偏移值,通常用于调整计划中的表达式或变量引用的引用号 + Index relid; // 关系标识符 + bool return_non_base_vars; // 是否应拒绝或返回不在基本目标列表中找到的变量 } fix_remote_expr_context; + /* * Check if a Const node is a regclass value. We accept plain OID too, * since a regclass Const will get folded to that type if it's an argument @@ -200,30 +201,27 @@ static List* set_remote_returning_refs(PlannerInfo* root, List* rlist, Plan* top */ Plan* set_plan_references(PlannerInfo* root, Plan* plan) { - PlannerGlobal* glob = root->glob; - int rtoffset = list_length(glob->finalrtable); - ListCell* lc = NULL; + PlannerGlobal* glob = root->glob; // 获取查询规划器的全局信息 + int rtoffset = list_length(glob->finalrtable); // 获取最终范围表的长度作为偏移量 + ListCell* lc = NULL; // 定义一个链表元素迭代器 /* - * In the flat rangetable, we zero out substructure pointers that are not - * needed by the executor; this reduces the storage space and copying cost - * for cached plans. We keep only the alias and eref Alias fields, which - * are needed by EXPLAIN, and the selectedCols and modifiedCols bitmaps, - * which are needed for executor-startup permissions checking and for - * trigger event checking. + * 在扁平化的范围表中,我们将不需要的子结构指针归零,以减少缓存计划的存储空间和复制成本。 + * 我们只保留别名和eref别名字段,这对于EXPLAIN是必需的,以及selectedCols和modifiedCols位图, + * 这对于执行器启动权限检查和触发器事件检查是必需的。 */ - foreach (lc, root->parse->rtable) { - RangeTblEntry* rte = (RangeTblEntry*)lfirst(lc); - RangeTblEntry* newrte = NULL; + foreach (lc, root->parse->rtable) { // 遍历解析树的范围表 + RangeTblEntry* rte = (RangeTblEntry*)lfirst(lc); // 获取范围表条目 + RangeTblEntry* newrte = NULL; // 用于存储新的范围表条目 - /* flat copy to duplicate all the scalar fields */ + /* 对所有标量字段进行扁平复制 */ newrte = (RangeTblEntry*)palloc(sizeof(RangeTblEntry)); errno_t rc = memcpy_s(newrte, sizeof(RangeTblEntry), rte, sizeof(RangeTblEntry)); securec_check(rc, "\0", "\0"); /* - * zap unneeded sub-structure, keep nerte->securityQuals here - * because we need this information when execute explain query. + * 剔除不需要的子结构,但在这里保留nerte->securityQuals, + * 因为我们在执行解释查询时需要这些信息。 */ newrte->subquery = NULL; newrte->joinaliasvars = NIL; @@ -237,28 +235,25 @@ Plan* set_plan_references(PlannerInfo* root, Plan* plan) newrte->ctecoltypmods = NIL; newrte->ctecolcollations = NIL; - glob->finalrtable = lappend(glob->finalrtable, newrte); + glob->finalrtable = lappend(glob->finalrtable, newrte); // 添加新的范围表条目 /* - * If it's a plain relation RTE, add the table to relationOids. + * 如果它是一个普通的关系RTE,则将表添加到relationOids。 * - * We do this even though the RTE might be unreferenced in the plan - * tree; this would correspond to cases such as views that were - * expanded, child tables that were eliminated by constraint - * exclusion, etc. Schema invalidation on such a rel must still force - * rebuilding of the plan. + * 即使在计划树中RTE可能未被引用,也要这样做; + * 这对应于视图被展开,通过约束排除被消除的子表等情况。 + * 对于这样一个关系,其schema无效,必须仍然强制重新构建计划。 * - * Note we don't bother to avoid duplicate list entries. We could, - * but it would probably cost more cycles than it would save. + * 注意,我们不必费心避免重复的列表条目。我们可以这样做, + * 但它可能消耗的计算周期比它所能节省的还要多。 */ if (newrte->rtekind == RTE_RELATION) glob->relationOids = lappend_oid(glob->relationOids, newrte->relid); } /* - * Check for RT index overflow; it's very unlikely, but if it did happen, - * the executor would get confused by varnos that match the special varno - * values. + * 检查RT索引溢出;这非常不太可能发生,但如果发生了, + * 执行器会因为变量号与特殊变量号值匹配而混淆。 */ if (IS_SPECIAL_VARNO(list_length(glob->finalrtable))) ereport(ERROR, @@ -267,29 +262,30 @@ Plan* set_plan_references(PlannerInfo* root, Plan* plan) errmsg("too many range table entries when set plan reference.")))); /* - * Adjust RT indexes of PlanRowMarks and add to final rowmarks list + * 调整PlanRowMarks的RT索引并添加到最终的rowmarks列表中 */ - foreach (lc, root->rowMarks) { - PlanRowMark* rc = (PlanRowMark*)lfirst(lc); - PlanRowMark* newrc = NULL; + foreach (lc, root->rowMarks) { // 遍历行标记列表 + PlanRowMark* rc = (PlanRowMark*)lfirst(lc); // 获取行标记 + PlanRowMark* newrc = NULL; // 用于存储新的行标记 AssertEreport(IsA(rc, PlanRowMark), MOD_OPT, "type plan row mark is required."); - /* flat copy is enough since all fields are scalars */ + /* 扁平复制足够,因为所有字段都是标量 */ newrc = (PlanRowMark*)palloc(sizeof(PlanRowMark)); errno_t ret = memcpy_s(newrc, sizeof(PlanRowMark), rc, sizeof(PlanRowMark)); securec_check(ret, "\0", "\0"); - /* adjust indexes ... but *not* the rowmarkId */ + /* 调整索引...但不调整rowmarkId */ newrc->rti += rtoffset; newrc->prti += rtoffset; - glob->finalrowmarks = lappend(glob->finalrowmarks, newrc); + glob->finalrowmarks = lappend(glob->finalrowmarks, newrc); // 添加新的行标记 } - /* Now fix the Plan tree */ - return set_plan_refs(root, plan, rtoffset); + /* 现在修复Plan树 */ + return set_plan_refs(root, plan, rtoffset); // 调用set_plan_refs函数,传递修复后的计划树和偏移量 } + /* * set_plan_refs: recurse through the Plan nodes of a single subquery level */ diff --git a/src/gausskernel/optimizer/plan/stream_remove.cpp b/src/gausskernel/optimizer/plan/stream_remove.cpp index 59c7a1d66..0fe9ec568 100644 --- a/src/gausskernel/optimizer/plan/stream_remove.cpp +++ b/src/gausskernel/optimizer/plan/stream_remove.cpp @@ -32,61 +32,91 @@ #include "optimizer/streamplan.h" +// 定义枚举类型 RedundantStreamType,表示冗余流操作的类型 enum RedundantStreamType { - EXTRA_OTHER_CASE, - /* for stream, require redstribute or broadcast of select 1 from table where distribute_key = const scan */ - REDUNDANT_MANY_STREAMS, - /* for limit, require limit 1. */ - REDUNDANT_MANY_STREAM_LIMITS, - /* for subquery scan, require select 1 baseresult. */ - REDUNDANT_ONE_STREAM_AND_ONE_SUBQUERY_SCAN, - REDUNDANT_ONE_STREAM_LIMIT_AND_ONE_SUBQUERY_SCAN + EXTRA_OTHER_CASE, // 额外的其他情况 + REDUNDANT_MANY_STREAMS, // 需要对多个流进行冗余操作,例如 select 1 from table where distribute_key = const scan + REDUNDANT_MANY_STREAM_LIMITS, // 需要对多个流限制操作进行冗余操作,例如 limit 1 + REDUNDANT_ONE_STREAM_AND_ONE_SUBQUERY_SCAN, // 需要对一个流和一个子查询扫描操作进行冗余操作 + REDUNDANT_ONE_STREAM_LIMIT_AND_ONE_SUBQUERY_SCAN // 需要对一个流限制操作和一个子查询扫描操作进行冗余操作 }; + +// 定义 RedundantStreamInfo 结构体,用于存储冗余流操作的信息 typedef struct { - int count_stream; - /* no double stream limit, we remove it in set_plan_reference */ - int count_stream_limit; - int count_subquery_scan; - RedundantStreamType redundant_stream_type; + int count_stream; // 流操作的数量 + int count_stream_limit; // 流限制操作的数量 + int count_subquery_scan; // 子查询扫描操作的数量 + RedundantStreamType redundant_stream_type; // 冗余流操作的类型 } RedundantStreamInfo; +// 判断 Limit 节点是否表示限制为 1 的操作 + static bool is_limit_one(const Limit *limit) { - Assert(limit != NULL); - Assert(IsA(limit, Limit)); + Assert(limit != NULL); // 断言:确保 limit 参数不为 NULL + Assert(IsA(limit, Limit)); // 断言:确保 limit 参数是 Limit 类型的节点 + + // 如果 limit 节点的 limitOffset 不为空,返回 false if (limit->limitOffset != NULL) { return false; } + + // 如果 limit 节点的 limitCount 为空,返回 false if (limit->limitCount == NULL) { return false; } + + // 如果 limit 节点的 limitCount 不是 Const 类型的节点,返回 false if (!IsA(limit->limitCount, Const)) { return false; } + + // 将 limitCount 节点强制转换为 Const 类型 Const *limitCount = (Const *)limit->limitCount; + + // 如果 limitCount 不是按值传递或其类型不是 INT8OID(int8 类型)或其值为 NULL,返回 false if (!limitCount->constbyval || limitCount->consttype != INT8OID || limitCount->constisnull) { return false; } + + // 获取 limitCount 节点的值 Datum count = ((Const *)limit->limitCount)->constvalue; + + // 如果值不等于 1,返回 false;否则,返回 true if (count != 1) { return false; } + return true; } + + +// 判断限制条件是否是等于操作(=) static bool is_equal_operator(const List *qual, const List *distributed_keys) { + // 如果限制条件列表长度不为 1,返回 false if (list_length(qual) != 1) { return false; } + + // 如果分布键列表长度不为 1,返回 false if (list_length(distributed_keys) != 1) { return false; } + + // 获取分布键列表中的第一个元素,假定它是一个 Var 节点 Var *distribute_var = (Var *)lfirst(list_head(distributed_keys)); + + // 获取限制条件列表中的第一个元素,假定它是一个 OpExpr 节点 OpExpr *opexpr = (OpExpr *)lfirst(list_head(qual)); + + // 如果分布键不是 Var 类型或限制条件不是 OpExpr 类型,返回 false if (!IsA(opexpr, OpExpr) || !IsA(distribute_var, Var)) { return false; } + + // 定义支持的等于操作的数据类型和对应的操作符 Oid const int SUPPORT_EQ = 8; const int VAR_OP = 2; const Oid var_eq_op_array[SUPPORT_EQ][VAR_OP] = { @@ -98,161 +128,255 @@ static bool is_equal_operator(const List *qual, const List *distributed_keys) {INT8OID, INT8EQOID}, {NUMERICOID, NUMERICEQOID}, {TEXTOID, TEXTEQOID}}; + + // 遍历支持的等于操作的数据类型和对应的操作符 Oid for (int i = 0; i < SUPPORT_EQ; i++) { + // 如果分布键的数据类型与当前支持的数据类型匹配,并且限制条件的操作符 Oid 与当前支持的操作符 Oid 匹配,返回 true if (distribute_var->vartype == var_eq_op_array[i][0] && opexpr->opno == var_eq_op_array[i][1]) { return true; } } + + // 如果没有匹配的情况,返回 false return false; } + +// 判断限制条件是否为变量等于常量的情况 static bool is_equal_const(const List *qual, const List *distributed_keys) { + // 获取分布键列表中的第一个元素,假定它是一个 Var 节点 Var *distribute_var = (Var *)lfirst(list_head(distributed_keys)); + + // 获取限制条件列表中的第一个元素,假定它是一个 OpExpr 节点 OpExpr *opexpr = (OpExpr *)lfirst(list_head(qual)); + + // 获取限制条件中的表达式参数列表 List *expr_args = opexpr->args; Assert(expr_args != NULL); + + // 定义等于操作的参数长度为 2 const int EQUAL_ARGS_LEN = 2; + + // 如果表达式参数列表的长度不等于 2,返回 false if (list_length(expr_args) != EQUAL_ARGS_LEN) { return false; } + Var *expr_var = NULL; Const *expr_const = NULL; ListCell *arg = NULL; - foreach(arg, expr_args) { + + // 遍历表达式参数列表 + foreach (arg, expr_args) { Expr *expr = (Expr *)lfirst(arg); + + // 如果参数是一个 Var 节点,将其保存到 expr_var if (IsA(expr, Var)) { expr_var = (Var *)expr; - } else if (IsA(expr, Const)) { + } + // 如果参数是一个 Const 节点,将其保存到 expr_const + else if (IsA(expr, Const)) { expr_const = (Const *)expr; - } else if (IsA(expr, RelabelType) && ((RelabelType *)expr)->relabelformat == COERCE_IMPLICIT_CAST) { + } + // 如果参数是一个 RelabelType 节点,并且其 relabelformat 为 COERCE_IMPLICIT_CAST,尝试提取其中的 Var 节点 + else if (IsA(expr, RelabelType) && ((RelabelType *)expr)->relabelformat == COERCE_IMPLICIT_CAST) { Var *var = (Var *)((RelabelType *)expr)->arg; if (var != NULL && IsA(var, Var)) { expr_var = (Var *)var; } } } - /* qual is var equal const */ + + // 如果没有找到合适的 Var 和 Const,返回 false if (expr_var == NULL || expr_const == NULL) { return false; } - /* qual var equal distribute var */ - if (!(expr_var->varno == distribute_var->varno || expr_var->varnoold == distribute_var->varno) - || !(expr_var->varattno == distribute_var->varattno || expr_var->varoattno == distribute_var->varattno)) { + + // 判断限制条件中的变量与分布键是否匹配 + if (!(expr_var->varno == distribute_var->varno || expr_var->varnoold == distribute_var->varno) || + !(expr_var->varattno == distribute_var->varattno || expr_var->varoattno == distribute_var->varattno)) { return false; } + + // 如果符合上述条件,返回 true,表示限制条件为变量等于常量的情况 return true; } + +// 判断限制条件是否为分布键等于常量的情况 static bool is_distribute_key_eq_const_qual(const List *qual, const List *distributed_keys) { + // 断言限制条件和分布键列表不为空,并且分布键列表是一个 List 类型 Assert(qual != NULL); Assert(nodeTag(qual) == T_List); Assert(distributed_keys != NULL); Assert(IsA(distributed_keys, List)); + // 如果限制条件不是等于操作符,返回 false if (!is_equal_operator(qual, distributed_keys)) { return false; } + // 如果限制条件不是分布键等于常量,返回 false if (!is_equal_const(qual, distributed_keys)) { return false; } - + // 如果限制条件同时满足上述两个条件,返回 true,表示是分布键等于常量的情况 return true; } + +// 从扫描节点中提取限制条件列表 static List *fetch_qual_from_scan(const Scan *scan) { switch (nodeTag(scan)) { + // 如果是普通的扫描节点(Scan 类型),返回 NULL,表示没有限制条件 case T_Scan: { - return scan->plan.qual; - } - case T_SeqScan: { - return scan->plan.qual; - } - case T_IndexScan: { - return ((IndexScan *)scan)->indexqual; + return NULL; } + // 如果是位图索引扫描节点(BitmapIndexScan 类型),返回索引限制条件列表 case T_BitmapIndexScan: { return ((BitmapIndexScan *)scan)->indexqual; } + // 如果是索引唯一扫描节点(IndexOnlyScan 类型),返回索引限制条件列表 case T_IndexOnlyScan: { return ((IndexOnlyScan *)scan)->indexqual; } + // 对于其他类型的扫描节点,默认返回 NULL,表示没有限制条件 default: { return NULL; } } } + +// 检查是否为选择常量与分布条件计划 static bool is_select_const_with_distribute_qual_plan(const Scan *scan) { + // 断言扫描节点不为空 Assert(scan != NULL); + + // 如果左子树不为空,返回 false,不是选择常量与分布条件计划 if (scan->plan.lefttree != NULL) { return false; } + + // 获取扫描节点的限制条件列表 List *qual = fetch_qual_from_scan(scan); + + // 如果限制条件列表为空或不是 List 类型,返回 false if (qual == NULL || !IsA(qual, List)) { return false; } + + // 获取扫描节点的分布键列表 List *distributed_keys = scan->plan.distributed_keys; + + // 如果分布键列表为空或不是 List 类型,返回 false if (distributed_keys == NULL || !IsA(distributed_keys, List)) { return false; } + + // 检查限制条件是否为分布键等于常量的限制条件 if (!is_distribute_key_eq_const_qual(qual, distributed_keys)) { return false; } + + // 获取扫描节点的目标列表 List *targetlist = scan->plan.targetlist; + + // 如果目标列表为空,返回 false if (targetlist == NULL) { return false; } + + // 获取目标列表的第一个目标项 TargetEntry *targetentry = (TargetEntry *)lfirst(list_head(targetlist)); + + // 如果目标项为空,返回 false if (targetentry == NULL) { return false; } + + // 断言目标项是 TargetEntry 类型 Assert(IsA(targetentry, TargetEntry)); + + // 获取目标项的表达式,应该是常量 Const *expr = (Const *)targetentry->expr; + + // 如果表达式为空或不是 Const 类型,返回 false if (expr == NULL || !IsA(expr, Const)) { return false; } + + // 满足所有条件,返回 true,是选择常量与分布条件计划 return true; } + +// 检查是否为流限制计划 static bool is_stream_limit_plan(const Stream *stream) { + // 断言流节点不为空 Assert(stream != NULL); Assert(nodeTag(stream) == T_Stream); + + // 如果流类型不是重分布或广播,返回 false if (stream->type != STREAM_REDISTRIBUTE && stream->type != STREAM_BROADCAST) { return false; } + + // 获取流节点的左子树计划 Plan *plan = stream->scan.plan.lefttree; + + // 如果左子树为空,返回 false if (plan == NULL) { return false; } + Limit *limit = NULL; + + // 如果左子树是 Limit 节点,将其赋值给 limit if (IsA(plan, Limit)) { limit = (Limit *)plan; } + + // 如果左子树是 SubqueryScan 节点,且流类型是重分布 if (IsA(plan, SubqueryScan)) { SubqueryScan *sub = (SubqueryScan *)plan; + + // 如果流类型不是重分布,返回 false if (stream->type != STREAM_REDISTRIBUTE) { return false; } + + // 如果 SubqueryScan 子计划不为空,且子计划是 Limit 节点,将其赋值给 limit if (sub->subplan != NULL && IsA(sub->subplan, Limit)) { limit = (Limit *)sub->subplan; } } + + // 如果 limit 为空,返回 false if (limit == NULL) { return false; } + + // 检查 limit 是否为限制为 1 if (!is_limit_one(limit)) { return false; } + + // 获取 limit 的左子树计划 Plan *lefttree = limit->plan.lefttree; + + // 如果左子树为空,返回 false if (lefttree == NULL) { return false; } + + // 根据左子树的类型进行不同的检查 switch (nodeTag(lefttree)) { case T_Stream: { + // 如果左子树是流节点,递归调用 is_stream_limit_plan return is_stream_limit_plan((Stream *)lefttree); } /* fall through, we dont care which type of the scan */ @@ -261,6 +385,7 @@ static bool is_stream_limit_plan(const Stream *stream) case T_IndexScan: case T_BitmapIndexScan: case T_IndexOnlyScan: { + // 如果左子树是扫描类型的节点,检查是否为选择常量与分布条件计划 return is_select_const_with_distribute_qual_plan((Scan *)lefttree); } default: @@ -268,17 +393,29 @@ static bool is_stream_limit_plan(const Stream *stream) } } + +// 检查是否为流计划 +// 流计划涉及将查询操作分布到多个并行执行节点(例如,分布式数据库中的不同数据节点或计算节点),以加速查询的执行 static bool is_stream_plan(const Stream *stream) { + // 断言流节点不为空 Assert(stream != NULL); Assert(nodeTag(stream) == T_Stream); + + // 如果流类型不是重分布,返回 false if (stream->type != STREAM_REDISTRIBUTE) { return false; } + + // 获取流节点的左子树计划 Plan *lefttree = (Plan *)stream->scan.plan.lefttree; + + // 如果左子树为空,返回 false if (lefttree == NULL) { return false; } + + // 根据左子树的类型进行不同的检查 switch (nodeTag(lefttree)) { /* fall through, we dont care which type of the scan */ case T_Scan: @@ -286,6 +423,7 @@ static bool is_stream_plan(const Stream *stream) case T_IndexScan: case T_BitmapIndexScan: case T_IndexOnlyScan: { + // 如果左子树是扫描类型的节点,检查是否为选择常量与分布条件计划 return is_select_const_with_distribute_qual_plan((Scan *)lefttree); } default: @@ -293,166 +431,262 @@ static bool is_stream_plan(const Stream *stream) } } + +// 检测是否可以应用选择常量与哈希过滤器的查询计划 static bool is_select_const_with_hashfilter_plan(const SubqueryScan *subqueryscan) { + // 将子查询扫描计划转换为基础结果类型 BaseResult *baseresult = (BaseResult *)subqueryscan->subplan; + + // 如果基础结果为空或者不是BaseResult类型,则返回false if (baseresult == NULL || !IsA(baseresult, BaseResult)) { return false; } + + // 如果基础结果计划有左子树,则返回false if (baseresult->plan.lefttree != NULL) { return false; } + + // 获取子查询扫描计划的过滤条件列表 List *qual = subqueryscan->scan.plan.qual; + + // 如果过滤条件为空,或者条件数量不等于1,或者第一个条件不是HashFilter类型,则返回false if (qual == NULL || list_length(qual) != 1 || !IsA(linitial(qual), HashFilter)) { return false; } + // 获取基础结果计划中的目标列表 List *targetlist = baseresult->plan.targetlist; + + // 如果目标列表为空或者列表长度不等于1,则返回false if (targetlist == NULL || list_length(targetlist) != 1) { return false; } + + // 获取目标列表中的第一个目标项 TargetEntry *targetentry = (TargetEntry *)lfirst(list_head(targetlist)); + + // 如果目标项不是TargetEntry类型,则返回false if (!IsA(targetentry, TargetEntry)) { return false; } + + // 获取目标项的表达式,通常是一个常量 Const *expr = (Const *)targetentry->expr; + + // 如果表达式为空或者不是Const类型,则返回false if (expr == NULL || !IsA(expr, Const)) { return false; } + + // 如果以上所有条件都通过了,则返回true,表示可以应用选择常量与哈希过滤器的查询计划 return true; } + +// 设置冗余流类型 static void set_redundant_stream_type(RedundantStreamInfo *redundant_info) { - /* we have subqueryscan of select const */ + /* 如果存在子查询扫描的情况 */ if (redundant_info->count_subquery_scan != 0) { + // 如果有多于一个子查询扫描 if (redundant_info->count_subquery_scan > 1) { - /* who will write sql like this */ + /* 谁会编写这样的 SQL 呢? */ redundant_info->redundant_stream_type = EXTRA_OTHER_CASE; return; } + // 如果只有一个子查询扫描且没有流操作且没有流上限 if (redundant_info->count_stream == 1 && redundant_info->count_stream_limit == 0) { redundant_info->redundant_stream_type = REDUNDANT_ONE_STREAM_AND_ONE_SUBQUERY_SCAN; return; } + // 如果没有流操作但有一个流上限 if (redundant_info->count_stream == 0 && redundant_info->count_stream_limit == 1) { redundant_info->redundant_stream_type = REDUNDANT_ONE_STREAM_LIMIT_AND_ONE_SUBQUERY_SCAN; return; } + // 其他情况都归类为 EXTRA_OTHER_CASE redundant_info->redundant_stream_type = EXTRA_OTHER_CASE; return; } + // 如果没有子查询扫描但同时有流操作和流上限 if (redundant_info->count_stream != 0 && redundant_info->count_stream_limit != 0) { - /* mix type, dont support yet */ + /* 混合类型,暂不支持 */ redundant_info->redundant_stream_type = EXTRA_OTHER_CASE; return; } + // 如果有流操作但没有流上限 if (redundant_info->count_stream != 0) { redundant_info->redundant_stream_type = REDUNDANT_MANY_STREAMS; return; } + // 如果有流上限但没有流操作 if (redundant_info->count_stream_limit != 0) { redundant_info->redundant_stream_type = REDUNDANT_MANY_STREAM_LIMITS; return; } - /* the only case we go here is all three counter are 0, but it should never happen */ + /* 唯一可能到达的情况是所有三个计数器都为 0,但这应该永远不会发生 */ redundant_info->redundant_stream_type = EXTRA_OTHER_CASE; return; } -static void lookup_redundant_streams_of_append_plan(const Append *append, - RedundantStreamInfo *redundant_info) + +// 查找并确定附加计划中的冗余流 +static void lookup_redundant_streams_of_append_plan(const Append *append, RedundantStreamInfo *redundant_info) { + // 断言确保输入的指针不为空,且附加计划是一个有效的Append节点 Assert(append != NULL); Assert(redundant_info != NULL); Assert(IsA(append, Append)); + ListCell *subplan; + + // 遍历附加计划中的子计划 foreach(subplan, append->appendplans) { + // 获取当前子计划 Plan *plan = (Plan *)lfirst(subplan); + + // 根据不同的计划类型进行不同的处理 switch (nodeTag(plan)) { case T_Stream: { + // 如果是流计划并且是流上限计划 if (is_stream_limit_plan((Stream *)plan)) { redundant_info->count_stream_limit++; break; } + // 如果是普通的流计划 if (is_stream_plan((Stream *)plan)) { redundant_info->count_stream++; break; } + // 其他情况都归类为 EXTRA_OTHER_CASE,返回 redundant_info->redundant_stream_type = EXTRA_OTHER_CASE; return; } case T_SubqueryScan: { + // 如果是子查询扫描并且符合选择常量与哈希过滤器的计划条件 if (is_select_const_with_hashfilter_plan((SubqueryScan *)plan)) { redundant_info->count_subquery_scan++; break; } + // 其他情况都归类为 EXTRA_OTHER_CASE,返回 redundant_info->redundant_stream_type = EXTRA_OTHER_CASE; return; } default: + // 其他计划类型都归类为 EXTRA_OTHER_CASE,返回 redundant_info->redundant_stream_type = EXTRA_OTHER_CASE; return; } } + + // 在遍历完成后,根据不同的计数情况来设置冗余流类型 set_redundant_stream_type(redundant_info); } + +// 优化流计划 static void optimize_stream_plan(Stream *stream) { + // 如果流的类型不是STREAM_REDISTRIBUTE,直接返回,不进行优化 if (stream->type != STREAM_REDISTRIBUTE) { return; } + + // 初始化限制计划 Limit *limit = NULL; + + // 获取流计划的左子树 Plan *sub = stream->scan.plan.lefttree; + + // 如果左子树是Limit计划,将其赋值给limit if (IsA(sub, Limit)) { limit = (Limit *)sub; - } else if (IsA(sub, SubqueryScan)) { + } + // 如果左子树是SubqueryScan计划,尝试获取其子计划的Limit计划 + else if (IsA(sub, SubqueryScan)) { limit = (Limit *)((SubqueryScan *)sub)->subplan; } + + // 断言确保limit不为空 Assert(limit != NULL); + + // 如果limit不是Limit类型,直接返回,不进行优化 if (!IsA(limit, Limit)) { return; } + + // 获取下一个流计划,它应该是一个广播流计划 Stream *next_stream = (Stream *)limit->plan.lefttree; + + // 断言确保next_stream不为空 Assert(next_stream != NULL); - /* just work with double stream limit */ + + // 仅处理双重流限制情况 if (!IsA(next_stream, Stream) || next_stream->type != STREAM_BROADCAST) { return; } - /* actually we dont care what type the next_limit is */ + + // 获取下一个Limit计划,实际上不关心其类型 Limit *next_limit = (Limit *)next_stream->scan.plan.lefttree; + + // 断言确保next_limit不为空 Assert(next_limit != NULL); + + // 将流计划的左子树替换为下一个Limit计划 stream->scan.plan.lefttree = (Plan *)next_limit; + + // 更新下一个Limit计划的限制条件 next_limit->limitCount = limit->limitCount; next_limit->limitOffset = limit->limitOffset; + + // 释放内存资源 pfree_ext(next_stream); pfree_ext(limit); - pfree_ext(stream->scan.plan.exec_nodes); + + // 复制下一个Limit计划的执行节点信息,并更新到流计划中 stream->scan.plan.exec_nodes = (ExecNodes *)copyObject(next_limit->plan.exec_nodes); } + /* delete double stream limit */ +// 删除附加计划中的冗余流 void delete_redundant_streams_of_append_plan(const Append *append) { + // 如果输入不是Append类型的计划,直接返回 if (!IsA(append, Append)) { return; } + + // 分配并初始化一个RedundantStreamInfo结构 RedundantStreamInfo *redundant_info = (RedundantStreamInfo *)palloc0(sizeof(RedundantStreamInfo)); + + // 查找附加计划中的冗余流信息 lookup_redundant_streams_of_append_plan(append, redundant_info); + + // 如果冗余流类型为EXTRA_OTHER_CASE,直接返回 if (redundant_info->redundant_stream_type == EXTRA_OTHER_CASE) { + pfree_ext(redundant_info); return; } - /* we deal with double stream limit here, other case not support yet */ - if (redundant_info->redundant_stream_type != REDUNDANT_ONE_STREAM_LIMIT_AND_ONE_SUBQUERY_SCAN && + + // 仅处理双重流限制和多个流上限的情况,其他情况不支持 + if (redundant_info->redundant_stream_type != REDUNDANT_ONE_STREAM_LIMIT_AND_ONE_SUBQUERY_SCAN && redundant_info->redundant_stream_type != REDUNDANT_MANY_STREAM_LIMITS) { + pfree_ext(redundant_info); return; } + + // 遍历附加计划中的子计划 ListCell *subplan; foreach(subplan, append->appendplans) { Plan *plan = (Plan *)lfirst(subplan); + + // 根据不同的计划类型进行处理 switch (nodeTag(plan)) { case T_Stream: { + // 如果是流计划,进行流计划优化 Stream *stream = (Stream *)plan; optimize_stream_plan(stream); break; @@ -462,73 +696,109 @@ void delete_redundant_streams_of_append_plan(const Append *append) } } } + + // 释放RedundantStreamInfo结构的内存资源 pfree_ext(redundant_info); } + +// 复制节点列表 void copy_nodelist(List *dst_node_list, const List *src_node_list) { + // 断言确保目标节点列表和源节点列表都不为空 Assert(dst_node_list != NULL); Assert(src_node_list != NULL); + + // 初始化目标节点列表和源节点列表的迭代器 ListCell *dst_cell; ListCell *src_cell; + + // 将目标节点列表的长度扩展到与源节点列表相同 while (list_length(dst_node_list) < list_length(src_node_list)) { - (void)lappend_int(dst_node_list, -1); + (void)lappend_int(dst_node_list, -1); // 添加一个占位符值为-1 } + + // 如果目标节点列表的长度大于源节点列表,则删除多余的元素 while (list_length(dst_node_list) > list_length(src_node_list)) { - (void)list_delete_first(dst_node_list); + (void)list_delete_first(dst_node_list); // 删除目标节点列表的第一个元素 } + + // 遍历目标节点列表和源节点列表,逐个复制值 forboth(dst_cell, dst_node_list, src_cell, src_node_list) { - lfirst_int(dst_cell) = lfirst_int(src_cell); + lfirst_int(dst_cell) = lfirst_int(src_cell); // 复制源节点列表的值到目标节点列表 } } + +// 复制执行节点信息 void copy_exec_nodes(ExecNodes *dst_exec_nodes, const ExecNodes *src_exec_nodes) { + // 断言确保目标执行节点信息和源执行节点信息都不为空 Assert(dst_exec_nodes != NULL); Assert(src_exec_nodes != NULL); + + // 复制源执行节点信息的基本定位类型到目标执行节点信息 dst_exec_nodes->baselocatortype = src_exec_nodes->baselocatortype; + + // 复制源执行节点信息的节点列表到目标执行节点信息 copy_nodelist(dst_exec_nodes->nodeList, src_exec_nodes->nodeList); } + +// 删除一个流计划和一个子查询计划的冗余情况 void delete_redundant_case_of_one_stream_and_one_subquery(const RemoteQuery *top_plan) { + // 获取顶层计划中的基础结果 BaseResult *result = (BaseResult *)top_plan->scan.plan.lefttree; Assert(result != NULL); + + // 获取基础结果中的附加计划 Append *append = (Append *)result->plan.lefttree; Assert(append != NULL); + ListCell *subplan; ListCell *stream_subplan = NULL; ListCell *subquery_subplan = NULL; + + // 遍历附加计划中的子计划 foreach(subplan, append->appendplans) { Plan *sub = (Plan *)lfirst(subplan); + + // 根据子计划的类型进行处理 switch (nodeTag(sub)) { case T_Stream: { + // 如果子计划是流计划,记录下流计划的位置 stream_subplan = subplan; break; } case T_SubqueryScan: { + // 如果子计划是子查询扫描计划,记录下子查询扫描计划的位置 subquery_subplan = subplan; break; } default: { + // 如果子计划不是流计划或子查询扫描计划,直接返回,不进行处理 return; } } } + + // 如果没有找到流计划或子查询扫描计划,直接返回,不进行处理 if (stream_subplan == NULL || subquery_subplan == NULL) { return; } - /* delete redundant stream node */ + + // 删除冗余的流计划节点 Stream *stream = (Stream *)lfirst(stream_subplan); Plan *plan = stream->scan.plan.lefttree; - Assert(list_length(plan->exec_nodes->nodeList) == 1); + Assert(list_length(plan->exec_nodes->nodeList) == 1); // 确保只有一个执行节点 pfree_ext(stream); lfirst(stream_subplan) = (Plan *)plan; - /* modify execnode of brother node */ + // 修改兄弟节点的执行节点信息 const ExecNodes *src_exec_nodes = plan->exec_nodes; - /* let this query exec on single node of src_exec_nodes */ + // 让子查询扫描计划和其子计划以单个节点执行 SubqueryScan *subquery = (SubqueryScan *)lfirst(subquery_subplan); pfree_ext(subquery->scan.plan.qual); copy_exec_nodes(subquery->scan.plan.exec_nodes, src_exec_nodes); @@ -536,50 +806,71 @@ void delete_redundant_case_of_one_stream_and_one_subquery(const RemoteQuery *top BaseResult *sub_result = (BaseResult *)subquery->subplan; copy_exec_nodes(sub_result->plan.exec_nodes, src_exec_nodes); - /* modify exec_node of father node */ + // 修改父节点的执行节点信息 copy_exec_nodes(append->plan.exec_nodes, src_exec_nodes); copy_exec_nodes(result->plan.exec_nodes, src_exec_nodes); copy_exec_nodes(top_plan->exec_nodes, src_exec_nodes); } + +// 删除多个流计划的冗余情况 void delete_redundant_case_of_many_stream(const RemoteQuery *top_plan) { + // 获取顶层计划中的基础结果 BaseResult *result = (BaseResult *)top_plan->scan.plan.lefttree; Assert(result != NULL); + + // 获取基础结果中的附加计划 Append *append = (Append *)result->plan.lefttree; Assert(append != NULL); + ListCell *subplan; ExecNodes *src_exec_nodes = NULL; + + // 遍历附加计划中的子计划 foreach(subplan, append->appendplans) { Plan *sub = (Plan *)lfirst(subplan); + + // 根据子计划的类型进行处理 switch (nodeTag(sub)) { case T_Stream: { + // 如果子计划是流计划 Stream *stream = (Stream *)lfirst(subplan); Plan *plan = stream->scan.plan.lefttree; - Assert(list_length(plan->exec_nodes->nodeList) == 1); + Assert(list_length(plan->exec_nodes->nodeList) == 1); // 确保只有一个执行节点 + + // 删除冗余的流计划节点 pfree_ext(stream); lfirst(subplan) = (Plan *)plan; + + // 如果src_exec_nodes为空,将其初始化为流计划的执行节点信息 if (src_exec_nodes == NULL) { src_exec_nodes = (ExecNodes *)copyObject(plan->exec_nodes); - } else { + } + // 否则,将流计划的执行节点信息合并到src_exec_nodes中 + else { (void)list_concat_unique_int(src_exec_nodes->nodeList, plan->exec_nodes->nodeList); } break; } default: { - /* we should never go here */ + /* 我们不应该到达这里 */ return; } } } + + // 断言确保src_exec_nodes不为空 Assert(src_exec_nodes != NULL); - /* modify exec_node of father node */ + + // 修改父节点的执行节点信息 copy_exec_nodes(append->plan.exec_nodes, src_exec_nodes); copy_exec_nodes(result->plan.exec_nodes, src_exec_nodes); copy_exec_nodes(top_plan->exec_nodes, src_exec_nodes); } + /** * for now, we support the specific union all sql which are list here * @@ -600,36 +891,55 @@ void delete_redundant_case_of_many_stream(const RemoteQuery *top_plan) * UNION ALL * SELECT * FROM ( SELECT 1 FROM t WHERE t.distribute = 'yyy' ); */ +// 删除RemoteQuery计划中的冗余流节点 void delete_redundant_streams_of_remotequery(RemoteQuery *top_plan) { + // 如果输入的top_plan为空或不是RemoteQuery类型的计划,直接返回 if (top_plan == NULL || !IsA(top_plan, RemoteQuery)) { return; } + + // 获取顶层计划中的基础结果 BaseResult *result = (BaseResult *)top_plan->scan.plan.lefttree; + + // 如果基础结果为空或不是BaseResult类型的计划,直接返回 if (result == NULL || !IsA(result, BaseResult)) { return; } + + // 获取基础结果中的附加计划 Append *append = (Append *)result->plan.lefttree; + + // 如果附加计划为空或不是Append类型的计划,直接返回 if (append == NULL || !IsA(append, Append)) { return; } + + // 分配并初始化一个RedundantStreamInfo结构 RedundantStreamInfo *redundant_info = (RedundantStreamInfo *)palloc0(sizeof(RedundantStreamInfo)); + + // 查找附加计划中的冗余流信息 lookup_redundant_streams_of_append_plan(append, redundant_info); + + // 如果冗余流类型为EXTRA_OTHER_CASE,直接返回 if (redundant_info->redundant_stream_type == EXTRA_OTHER_CASE) { + pfree_ext(redundant_info); return; } + + // 根据不同的冗余流类型进行不同的处理 switch (redundant_info->redundant_stream_type) { case REDUNDANT_ONE_STREAM_AND_ONE_SUBQUERY_SCAN: /* fallthrough */ - /* do same thing, delete stream node, and modify execnodes of stream's brother and father to stream's son */ case REDUNDANT_ONE_STREAM_LIMIT_AND_ONE_SUBQUERY_SCAN: { + // 删除一个流计划和一个子查询计划的冗余情况 delete_redundant_case_of_one_stream_and_one_subquery(top_plan); break; } - case REDUNDANT_MANY_STREAM_LIMITS: + case REDUNDANT_MANY_STREAMS: /* fallthrough */ - /* do same thing, delete stream node, and modify execnodes of stream's father to stream's son */ - case REDUNDANT_MANY_STREAMS: { + case REDUNDANT_MANY_STREAM_LIMITS: { + // 删除多个流计划的冗余情况 delete_redundant_case_of_many_stream(top_plan); break; } @@ -637,5 +947,7 @@ void delete_redundant_streams_of_remotequery(RemoteQuery *top_plan) break; } } + + // 释放RedundantStreamInfo结构的内存资源 pfree_ext(redundant_info); -} \ No newline at end of file +} diff --git a/src/gausskernel/optimizer/plan/streamplan.cpp b/src/gausskernel/optimizer/plan/streamplan.cpp index c2d0de453..6f5831435 100644 --- a/src/gausskernel/optimizer/plan/streamplan.cpp +++ b/src/gausskernel/optimizer/plan/streamplan.cpp @@ -34,24 +34,25 @@ /* only operator with qual supporting can use hashfilter */ static int g_support_hashfilter_types[] = { - T_SeqScan, - T_CStoreScan, - T_DfsScan, + T_SeqScan, // 顺序扫描操作 + T_CStoreScan, // 列存储扫描操作 + T_DfsScan, // 分布式文件系统(DFS)扫描操作 #ifdef ENABLE_MULTIPLE_NODES - T_TsStoreScan, + T_TsStoreScan, // 时间序列存储扫描操作 (仅在启用多节点时可用) #endif /* ENABLE_MULTIPLE_NODES */ - T_ForeignScan, - T_IndexScan, - T_IndexOnlyScan, - T_CStoreIndexScan, - T_DfsIndexScan, - T_TidScan, - T_SubqueryScan, - T_BitmapHeapScan, - T_CStoreIndexHeapScan, - T_CteScan + T_ForeignScan, // 外部表扫描操作 + T_IndexScan, // 索引扫描操作 + T_IndexOnlyScan, // 仅索引扫描操作 + T_CStoreIndexScan, // 列存储索引扫描操作 + T_DfsIndexScan, // 分布式文件系统索引扫描操作 + T_TidScan, // 行标识符(TID)扫描操作 + T_SubqueryScan, // 子查询扫描操作 + T_BitmapHeapScan, // 位图堆扫描操作 + T_CStoreIndexHeapScan, // 列存储索引堆扫描操作 + T_CteScan // 通用表达式(CTE)扫描操作 }; + /* * Simple Query like: * select version(); @@ -74,27 +75,46 @@ void output_unshipped_log() return; } +// 关闭流式处理的函数 void set_stream_off() { + // 将is_stream标志设置为false,表示关闭流式处理 u_sess->opt_cxt.is_stream = false; } + +// 检查流式处理支持的函数 bool check_stream_support() { + // 返回is_stream_support标志的值,用于检查是否支持流式处理 return u_sess->opt_cxt.is_stream_support; } + /* Init the contain_func_context */ +/** + * 初始化包含函数上下文的函数 + * + * @ param funcids 包含的函数的ID列表 + * @ param find_all 是否查找所有匹配的函数 + * + * @ return 包含函数上下文的初始化结果 + */ contain_func_context init_contain_func_context(List* funcids, bool find_all) { + // 创建一个包含函数上下文的结构体变量 contain_func_context context; - context.funcids = funcids; - context.func_exprs = NIL; - context.find_all = find_all; + // 设置包含函数上下文的成员变量 + context.funcids = funcids; // 包含的函数的ID列表 + context.func_exprs = NIL; // 初始化为空列表 + context.find_all = find_all; // 是否查找所有匹配的函数 + + // 返回初始化后的包含函数上下文 return context; } + ExecNodes* get_all_data_nodes(char locatortype) { ExecNodes* exec_nodes = ng_get_installation_group_exec_node(); @@ -105,34 +125,52 @@ ExecNodes* get_all_data_nodes(char locatortype) /* * Return a random index of datanode in current plan node's execution nodegroup */ +/** + * 从执行计划中随机选择一个数据节点的标识符 + * + * @ param plan 要选择节点的执行计划 + * @ return 选择的节点的标识符 + */ int pickup_random_datanode_from_plan(Plan* plan) { - /* Randomly pickup a node from target nodegroup */ - - int nodeId = 0; + int nodeId = 0; // 用于存储选择的节点的标识符 + // 获取目标执行节点(Datanode) ExecNodes* target_execnodes = ng_get_dest_execnodes(plan); + // 使用断言(Assert)和错误报告(Ereport)检查目标执行节点和节点列表是否为空 AssertEreport(NULL != target_execnodes && NIL != target_execnodes->nodeList, - MOD_OPT, - "The target_execnodes or the node list is NULL"); + MOD_OPT, + "The target_execnodes or the node list is NULL"); + // 如果目标执行节点和节点列表都不为空,则从节点列表中随机选择一个节点 if (NULL != target_execnodes && NIL != target_execnodes->nodeList) { int random = pickup_random_datanode(list_length(target_execnodes->nodeList)); nodeId = list_nth_int(target_execnodes->nodeList, random); } else { + // 如果目标执行节点或节点列表为空,则从所有数据节点中随机选择一个节点 nodeId = pickup_random_datanode(u_sess->pgxc_cxt.NumDataNodes); } - return nodeId; + return nodeId; // 返回选择的节点的标识符 } + +/** + * 从计划中获取一个随机的数据节点,构建一个包含该节点的 ExecNodes 结构体并返回。 + */ ExecNodes* get_random_data_nodes(char locatortype, Plan* plan) { + // 从计划中随机选择一个数据节点 int nodeId = pickup_random_datanode_from_plan(plan); + + // 获取目标数据分布 Distribution* distribution = ng_get_dest_distribution(plan); + // 创建一个新的 ExecNodes 结构体 ExecNodes* execNodes = makeNode(ExecNodes); + + // 初始化 ExecNodes 结构体的各个字段 execNodes->primarynodelist = NIL; execNodes->nodeList = list_make1_int(nodeId); ng_copy_distribution(&execNodes->distribution, distribution); @@ -142,6 +180,7 @@ ExecNodes* get_random_data_nodes(char locatortype, Plan* plan) execNodes->accesstype = RELATION_ACCESS_READ; execNodes->en_dist_vars = NIL; + // 检查 plan->exec_nodes 是否为 NULL,如果是,抛出错误 if (plan->exec_nodes == NULL) { ereport(ERROR, (errmodule(MOD_OPT), @@ -149,9 +188,10 @@ ExecNodes* get_random_data_nodes(char locatortype, Plan* plan) errmsg("Invalid plan->exec_nodes object when get random data nodes."))); } - return execNodes; + return execNodes; // 返回包含随机数据节点信息的 ExecNodes 结构体 } + /* If the nodeType of subplan support hashfilter return true, otherwise return false. */ static bool is_support_hashfilter(int nodeType) { @@ -163,12 +203,15 @@ static bool is_support_hashfilter(int nodeType) return false; } +/** + * 为复制操作添加哈希过滤器 + */ bool add_hashfilter_for_replication(PlannerInfo* root, Plan* plan, List* distribute_keys) { - HashFilter* hashfilter = NULL; - List* typeOidList = NIL; - ListCell* key = NULL; - Plan* tmpplan = NULL; + HashFilter* hashfilter = NULL; // 哈希过滤器 + List* typeOidList = NIL; // 类型OID列表 + ListCell* key = NULL; // 列表元素迭代器 + Plan* tmpplan = NULL; // 临时执行计划指针 if (NULL == plan) return false; @@ -176,80 +219,91 @@ bool add_hashfilter_for_replication(PlannerInfo* root, Plan* plan, List* distrib AssertEreport(NIL != distribute_keys, MOD_OPT, "The distribute keys are NIL"); /* - * If plan node type is PartIterator, it should add hashfilter in the lefttree + * 如果计划节点类型是PartIterator,应该在左子树中添加哈希过滤器 */ tmpplan = plan; if (IsA(tmpplan, PartIterator)) tmpplan = tmpplan->lefttree; if (IsA(tmpplan, DfsIndexScan)) tmpplan = (Plan*)((DfsIndexScan*)tmpplan)->dfsScan; + if (IsA(tmpplan, Append)) { ListCell* appendPlan = NULL; - /* First check if all the children of append are satisfied */ + /* 首先检查附加节点的所有子节点是否都支持哈希过滤器 */ foreach (appendPlan, ((Append*)tmpplan)->appendplans) { Plan* insidePlan = (Plan*)lfirst(appendPlan); - /* Once there is one node which doest not support hashfilter, then return false */ + /* 一旦有一个节点不支持哈希过滤器,则返回false */ if (!is_support_hashfilter(nodeTag(insidePlan))) return false; } - /* Second add hash filter for each child node */ + /* 然后为每个子节点添加哈希过滤器 */ foreach (appendPlan, ((Append*)tmpplan)->appendplans) { Plan* insidePlan = (Plan*)lfirst(appendPlan); (void)add_hashfilter_for_replication(root, insidePlan, distribute_keys); } return true; } + if (!is_support_hashfilter(nodeTag(tmpplan))) return false; - /* Add every typeOid of distribute_keys into typeOidList */ + /* 将distribute_keys中的每个类型OID添加到typeOidList中 */ foreach (key, distribute_keys) { Node* distkey = (Node*)lfirst(key); typeOidList = lappend_oid(typeOidList, exprType(distkey)); } - /* Make Hashfilter expr node */ + /* 创建哈希过滤器表达式节点 */ AssertEreport(plan->exec_nodes && plan->exec_nodes->nodeList, MOD_OPT, "The exec nodes or node list is NULL"); List* nodeList = list_copy(plan->exec_nodes->nodeList); hashfilter = makeHashFilter(distribute_keys, typeOidList, nodeList); tmpplan->hasHashFilter = true; - /* Append Hashfilter expr to qual of tmpplan */ + /* 将哈希过滤器表达式附加到tmpplan的qual中 */ tmpplan->qual = lappend(tmpplan->qual, hashfilter); - /* Set exec node list and Distribution */ + /* 设置执行节点列表和分布信息 */ Distribution* distribution = ng_get_dest_distribution(tmpplan); tmpplan->exec_nodes = ng_convert_to_exec_nodes(distribution, LOCATOR_TYPE_HASH, RELATION_ACCESS_READ); tmpplan->exec_nodes->nodeList = list_copy(nodeList); - /* estimate plan rows for hashfilter. */ + /* 为哈希过滤器估算计划的行数 */ tmpplan->plan_rows = PLAN_LOCAL_ROWS(tmpplan); tmpplan->multiple = get_multiple_by_distkey(root, distribute_keys, tmpplan->plan_rows); return true; } + +/** + * 为流式连接计划进行调整,以适应分布式查询的要求 + */ void stream_join_plan(PlannerInfo* root, Plan* join_plan, JoinPath* join_path) { Plan* inner_plan = innerPlan(join_plan); Plan* outer_plan = outerPlan(join_plan); + // 检查内部计划和外部计划是否在数据节点上执行,如果是,则需要进一步调整计划 if (is_execute_on_datanodes(inner_plan) || is_execute_on_datanodes(outer_plan)) { Plan* child_plan = NULL; + // 如果内部计划在协调器上执行,需要检查外部计划的类型 if (is_execute_on_coordinator(inner_plan)) { List* outerpathkeys = NIL; + // 如果外部计划是流式计划,获取其子计划作为新的外部计划 if (IsA(outer_plan, Stream)) child_plan = outerPlan(outer_plan); else child_plan = outer_plan; + // 创建一个简单的RemoteQuery计划作为新的外部计划 outer_plan = make_simple_RemoteQuery(child_plan, root, false); + // 如果连接计划是MergeJoin,根据外部路径键进行排序 if (IsA(join_plan, MergeJoin)) { MergePath* merge_path = (MergePath*)join_path; @@ -259,7 +313,9 @@ void stream_join_plan(PlannerInfo* root, Plan* join_plan, JoinPath* join_path) outerpathkeys = merge_path->jpath.outerjoinpath->pathkeys; outer_plan = (Plan*)make_sort_from_pathkeys(root, outer_plan, outerpathkeys, -1.0); - } else if (IsA(join_plan, NestLoop) && join_path->path.pathkeys) { + } + // 如果连接计划是NestLoop且有路径键,根据路径键进行排序 + else if (IsA(join_plan, NestLoop) && join_path->path.pathkeys) { AssertEreport(join_path->outerjoinpath->pathkeys, MOD_OPT, "The outer join path keys is NULL"); outerpathkeys = join_path->path.pathkeys; @@ -267,28 +323,34 @@ void stream_join_plan(PlannerInfo* root, Plan* join_plan, JoinPath* join_path) } outerPlan(join_plan) = outer_plan; - } else if (is_execute_on_coordinator(outer_plan)) { + } + // 如果外部计划在协调器上执行,需要检查连接计划类型并调整内部计划 + else if (is_execute_on_coordinator(outer_plan)) { if (IsA(join_plan, HashJoin)) { AssertEreport(IsA(inner_plan, Hash), MOD_OPT, "The inner_plan is NOT a Hash"); + // 如果内部计划的外部计划是流式计划,获取其子计划作为新的内部计划 if (IsA(outerPlan(inner_plan), Stream)) child_plan = outerPlan(outerPlan(inner_plan)); else child_plan = outerPlan(inner_plan); + // 创建一个简单的RemoteQuery计划作为新的内部计划 outerPlan(inner_plan) = make_simple_RemoteQuery(child_plan, root, false); - /* Modify locator information again */ + // 再次修改定位信息 inherit_plan_locator_info(inner_plan, outerPlan(inner_plan)); } else { List* innerpathkeys = NIL; + // 如果内部计划是流式计划,获取其子计划作为新的内部计划 if (IsA(inner_plan, Stream)) child_plan = outerPlan(inner_plan); else child_plan = inner_plan; inner_plan = make_simple_RemoteQuery(child_plan, root, false); + // 如果连接计划是MergeJoin,根据内部路径键进行排序 if (IsA(join_plan, MergeJoin)) { MergePath* merge_path = (MergePath*)join_path; @@ -308,10 +370,10 @@ void stream_join_plan(PlannerInfo* root, Plan* join_plan, JoinPath* join_path) } } + // 根据计划的执行位置设置执行类型和执行节点信息 if (is_execute_on_coordinator(inner_plan) || is_execute_on_coordinator(outer_plan)) { /* - * If one side is executed on coordinator, the other side should be same - * after the logic above + * 如果一侧在协调器上执行,另一侧应该保持一致 */ join_plan->exec_type = EXEC_ON_COORDS; Distribution* distribution = ng_get_single_node_group_distribution(); @@ -324,16 +386,15 @@ void stream_join_plan(PlannerInfo* root, Plan* join_plan, JoinPath* join_path) join_plan->exec_nodes = stream_merge_exec_nodes(outer_plan, inner_plan, ENABLE_PRED_PUSH(root)); } + // 如果是HashJoin,设置streamBothSides标志用于执行HashJoin时的优化 if (IsA(join_plan, HashJoin)) { HashJoin* hashjoin = (HashJoin*)join_plan; - /* - * streamBothSides is used in ExecHashJoin for judge if we should probe the first tuple of outer - * when both outer and inner don't contain stream or not. - */ hashjoin->streamBothSides = contain_special_plan_node(outerPlan(hashjoin), T_Stream) || contain_special_plan_node(innerPlan(hashjoin), T_Stream); - } else if (IsA(join_plan, NestLoop)) { + } + // 如果是NestLoop,设置materialAll标志用于优化 + else if (IsA(join_plan, NestLoop)) { NestLoop* nestloop = (NestLoop*)join_plan; if (nestloop->nestParams && IsA(nestloop->join.plan.righttree, RemoteQuery)) { @@ -348,9 +409,11 @@ void stream_join_plan(PlannerInfo* root, Plan* join_plan, JoinPath* join_path) contain_special_plan_node(innerPlan(nestloop), T_Stream); } + // 设置连接计划的分布键信息 join_plan->distributed_keys = join_path->path.distribute_keys; } + void inherit_plan_locator_info(Plan* plan, Plan* subplan) { plan->exec_nodes = ng_get_dest_execnodes(subplan); diff --git a/src/gausskernel/optimizer/plan/streamplan_single.cpp b/src/gausskernel/optimizer/plan/streamplan_single.cpp index 4504df93e..ca604e963 100644 --- a/src/gausskernel/optimizer/plan/streamplan_single.cpp +++ b/src/gausskernel/optimizer/plan/streamplan_single.cpp @@ -72,123 +72,179 @@ #include "utils/rel.h" #include "utils/rel_gs.h" #include "utils/syscache.h" - -void set_default_stream() +/* +这段代码的作用是根据当前的数据库操作情况以及查询的并行度来决定是否启用流式查询, +并相应地设置流式查询的标志位。流式查询通常用于在多线程环境下执行查询,以提高查询性能。 +*/ +void set_default_stream()//函数用于设置默认的流式查询参数。 { /* initdb could not use smp */ + // 如果当前操作是初始化数据库(IsInitdb),则不能使用多线程并行(smp) if (IsInitdb) { + // 在初始化数据库操作中,流式查询被禁用,所以将相关标志位设置为false u_sess->opt_cxt.is_stream = false; u_sess->opt_cxt.is_stream_support = false; } else { + // 如果不是初始化数据库操作 + // 根据查询的并行度(query_dop)来确定是否支持流式查询 + // 如果并行度大于1,表示支持流式查询,将相关标志位设置为true u_sess->opt_cxt.is_stream = (u_sess->opt_cxt.query_dop > 1); u_sess->opt_cxt.is_stream_support = (u_sess->opt_cxt.query_dop > 1); } } -int2vector* get_baserel_distributekey_no(Oid relid) + +int2vector* get_baserel_distributekey_no(Oid relid)//获取指定关系的分布键,分布键是用于将数据分布到不同节点上的一组属性或属性组合。 { - /* while smp is not allowed, no need to generate distribute key */ + /* 当不支持多线程并行时,不需要生成分布键 */ if (!check_stream_support()) { + // 如果不支持多线程并行(流式查询),则返回NULL,表示不生成分布键 return NULL; } AttrNumber attnum = 1; while (true) { HeapTuple tp; Form_pg_attribute att_tup; + + // 在系统缓存中搜索表的属性信息,通过表的Oid和属性编号(attnum)来查找 tp = SearchSysCache2(ATTNUM, ObjectIdGetDatum(relid), Int16GetDatum(attnum)); + + // 如果未找到匹配的元组,说明已经搜索完所有属性,跳出循环 if (!HeapTupleIsValid(tp)) { attnum = 0; ReleaseSysCache(tp); break; } + + // 获取属性元组的数据结构 att_tup = (Form_pg_attribute)GETSTRUCT(tp); + + // 如果属性没有被标记为已删除(attisdropped),表示该属性是有效的 if (!att_tup->attisdropped) { ReleaseSysCache(tp); break; } + + // 增加属性编号,继续搜索下一个属性 ++attnum; ReleaseSysCache(tp); } + + // 如果attnum等于0,表示没有有效的属性编号,返回NULL if (attnum == 0) return NULL; + + // 创建一个包含一个属性编号的int2vector结构 int2 col[1] = { attnum }; int2vector* attnumVec = buildint2vector(col, 1); + + // 返回包含属性编号的int2vector return attnumVec; } + /* in build_simple_rel used. Put it all back. Record it */ -List* build_baserel_distributekey(RangeTblEntry* rte, int relindex) +List* build_baserel_distributekey(RangeTblEntry* rte, int relindex)//代码的作用是构建基本关系(表)的分布键列表 { + // 如果当前是PGXC数据节点(PGXC_DATANODE)或不是PGXC协调器(PGXC_COORDINATOR), + // 或者关系的relid为空,或者关系的类型不是RELKIND_RELATION(普通表), + // 则返回一个空的List(NIL) if (IS_PGXC_DATANODE || !IS_PGXC_COORDINATOR || !rte->relid || get_rel_relkind(rte->relid) != RELKIND_RELATION) return NIL; + // 分布特性不受支持,抛出错误 DISTRIBUTED_FEATURE_NOT_SUPPORTED(); + + // 返回一个空的List(NIL) return NIL; } -Plan* make_simple_RemoteQuery(Plan* lefttree, PlannerInfo* root, bool is_subplan, ExecNodes* target_exec_nodes) + +Plan* make_simple_RemoteQuery(Plan* lefttree, PlannerInfo* root, bool is_subplan, ExecNodes* target_exec_nodes)//这段代码的作用是创建简单的远程查询计划 { + // 如果全局查询信息(glob)为空,抛出错误 if (NULL == root->glob) ereport(ERROR, (errmodule(MOD_OPT), errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), - (errmsg("Could not find globle planner info when make simple remote query.")))); + (errmsg("在创建简单远程查询时找不到全局规划器信息。")))); + // 如果当前查询处于递归内部,直接返回左子树的计划 if (root->glob->insideRecursion) return lefttree; + // 如果左子树计划需要在协调节点上执行(is_execute_on_coordinator), + // 或者需要在所有节点上执行(is_execute_on_allnodes),则直接返回左子树的计划 if (is_execute_on_coordinator(lefttree) || is_execute_on_allnodes(lefttree)) return lefttree; + // 如果左子树的并行度(dop)大于1,创建一个本地聚集计划(local gather), + // 用于将多个并行计划的结果汇总到单个计划中 if (lefttree->dop > 1) { lefttree = create_local_gather(lefttree); } + // 返回左子树的计划,经过可能的修改 return lefttree; } -void add_remote_subplan(PlannerInfo* root, RemoteQuery* result_node) + +void add_remote_subplan(PlannerInfo* root, RemoteQuery* result_node)//添加远程查询子计划 { DISTRIBUTED_FEATURE_NOT_SUPPORTED(); } -ExecNodes* get_plan_max_ExecNodes(Plan* lefttree, List* subplans) +ExecNodes* get_plan_max_ExecNodes(Plan* lefttree, List* subplans)//生成最终的执行节点信息(ExecNodes)并设置分布信息 { + // 创建一个用于存储最终执行节点信息的ExecNodes结构体指针 ExecNodes* final_exec_nodes = NULL; + // 初始化final_exec_nodes结构体 final_exec_nodes = makeNode(ExecNodes); - final_exec_nodes->nodeList = NIL; - final_exec_nodes->baselocatortype = LOCATOR_TYPE_REPLICATED; - final_exec_nodes->accesstype = RELATION_ACCESS_READ; - final_exec_nodes->primarynodelist = NIL; - final_exec_nodes->en_expr = NULL; + final_exec_nodes->nodeList = NIL; // 初始化节点列表为空 + final_exec_nodes->baselocatortype = LOCATOR_TYPE_REPLICATED; // 设置基本定位器类型为REPLICATED + final_exec_nodes->accesstype = RELATION_ACCESS_READ; // 设置访问类型为读取 + final_exec_nodes->primarynodelist = NIL; // 初始化主节点列表为空 + final_exec_nodes->en_expr = NULL; // 初始化执行节点表达式为NULL + + // 复制左子树的执行节点列表到final_exec_nodes中 final_exec_nodes->nodeList = lefttree->exec_nodes->nodeList; - /* Set Distribution */ - Distribution* distribution = ng_convert_to_distribution(final_exec_nodes->nodeList); - ng_set_distribution(&final_exec_nodes->distribution, distribution); + // 设置分布信息 + Distribution* distribution = ng_convert_to_distribution(final_exec_nodes->nodeList); // 将节点列表转换为分布信息 + ng_set_distribution(&final_exec_nodes->distribution, distribution); // 设置final_exec_nodes的分布信息为计算得到的分布信息 + // 返回最终的执行节点信息 return final_exec_nodes; } + bool is_replicated_plan(Plan* plan) { return false; } -bool is_hashed_plan(Plan* plan) +bool is_hashed_plan(Plan* plan)//判断一个计划是否为散列计划 { + // 如果计划是Stream节点类型 if (IsA(plan, Stream)) { + // 如果是广播流(broadcast_stream)或聚集流(gather_stream),则不是散列计划,返回false if (is_broadcast_stream((Stream*)plan) || is_gather_stream((Stream*)plan)) return false; + // 如果是重新分布流(redistribute_stream),则是散列计划,返回true else if (is_redistribute_stream((Stream*)plan)) return true; - } else if (plan->exec_nodes != NULL) + } + // 如果计划具有执行节点信息(exec_nodes不为空) + else if (plan->exec_nodes != NULL) + // 检查执行节点的基本定位器类型是否是散列定位器类型,如果是则是散列计划,返回true return IsLocatorDistributedByHash(plan->exec_nodes->baselocatortype); + // 默认情况下,不是散列计划,返回false return false; } + bool is_rangelist_plan(Plan* plan) { return false; @@ -215,72 +271,103 @@ void SerializePlan(Plan* node, PlannedStmt* planned_stmt, StringInfoData* str, i DISTRIBUTED_FEATURE_NOT_SUPPORTED(); } -Plan* mark_distribute_dml( +Plan* mark_distribute_dml(//记分布式数据操作语言(DML)的执行计划 PlannerInfo* root, Plan** sourceplan, ModifyTable* mt_plan, List** resultRelations, List* mergeActionList) { + // 获取子计划,通常是一个修改表计划(ModifyTable) Plan* subplan = *sourceplan; - /* We should avoid the ModifyTable Exec on Datanode When it followd by BaseResult */ + + /* 当修改表计划(ModifyTable)后跟随一个基本结果计划(BaseResult)时,应避免在数据节点上执行ModifyTable */ if (is_single_baseresult_plan(subplan)) { + // 继承子计划的定位器信息,将其应用于ModifyTable计划 inherit_plan_locator_info((Plan*)mt_plan, *sourceplan); } + + // 返回已经标记了分布信息的修改表计划 return (Plan*)mt_plan; } /* * Just execute the plan as a replicateion */ -static void mark_distribute_setop_allnodes(Plan* plan) +static void mark_distribute_setop_allnodes(Plan* plan)//标记一个计划以在所有节点上执行 { + // 清空计划的分布键列表 plan->distributed_keys = NIL; + + // 设置计划的执行类型为在所有节点上执行 plan->exec_type = EXEC_ON_ALL_NODES; + + // 获取默认的计算组分布信息 Distribution* distribution = ng_get_default_computing_group_distribution(); + + // 将分布信息转换为执行节点信息,并设置计划的执行节点信息 plan->exec_nodes = ng_convert_to_exec_nodes(distribution, LOCATOR_TYPE_REPLICATED, RELATION_ACCESS_READ); } + /* * We should get distribute key for union all for two case: * 1. All subplan are hash and there have distkey; * 2. Some subplan are hash which have distkey and some are replication. */ -static List* get_distkey_for_unionall(List** subPlanKeyArray, int subPlanNum) +static List* get_distkey_for_unionall(List** subPlanKeyArray, int subPlanNum)//从一组子计划的分布键数组中获取共享的分布键列表 { - List* common_diskey = NIL; + List* common_diskey = NIL; // 用于存储共享的分布键 + // 遍历子计划的分布键数组 for (int i = 0; i < subPlanNum; i++) { + // 如果子计划的分布键列表不为空 if (subPlanKeyArray[i] != NIL) { + // 如果common_diskey为空,将当前子计划的分布键列表赋值给common_diskey if (common_diskey == NIL) common_diskey = subPlanKeyArray[i]; + // 如果common_diskey不为空,且当前子计划的分布键列表与common_diskey不相等,返回空列表 else if (!equal(common_diskey, subPlanKeyArray[i])) return NIL; } } - /* All the distkey is same, we should get first for setop. */ + /* 所有子计划的分布键相同,应该返回第一个用于集合操作 */ + // 如果所有子计划的分布键都相同,返回common_diskey作为共享的分布键 return common_diskey; } -/* For unionall case, with no replicated plan, we should judge if replicated plan can be redistributed */ -bool judge_redistribute_setop_support(PlannerInfo* root, List* subplanlist, Bitmapset* redistributePlanSet) -{ - Index subPlanIndex = 0; - ListCell* lc = NULL; +/* For unionall case, with no replicated plan, we should judge if replicated plan can be redistributed */ +bool judge_redistribute_setop_support(PlannerInfo* root, List* subplanlist, Bitmapset* redistributePlanSet)//判断是否支持重新分布的集合操作(如 UNION ALL) +{ + Index subPlanIndex = 0; // 子计划的索引 + ListCell* lc = NULL; // 遍历子计划列表的迭代器 + + // 如果要重新分布的计划集合为空,表示支持重新分布,返回 true if (bms_is_empty(redistributePlanSet)) return true; + // 遍历子计划列表 foreach (lc, subplanlist) { + // 如果当前子计划的索引在重新分布计划集合中 if (bms_is_member(subPlanIndex, redistributePlanSet)) { - Plan* subPlan = (Plan*)lfirst(lc); + Plan* subPlan = (Plan*)lfirst(lc); // 获取当前子计划 + + // 调用 make_distkey_for_append 函数尝试为当前子计划创建分布键 if (NULL != make_distkey_for_append(root, subPlan)) - return true; + return true; // 如果成功创建分布键,表示支持重新分布,返回 true else - return false; + return false; // 如果无法创建分布键,表示不支持重新分布,返回 false } - subPlanIndex++; + subPlanIndex++; // 增加子计划的索引 } + + // 默认情况下,表示支持重新分布,返回 true return true; } + +/* +这段代码的主要功能是为查询计划中的子计划选择合适的数据重分发策略,以优化分布式查询性能。 +根据不同的情况,它选择是否需要重新分发子计划,并确定重分发的键和目标分布。 +*/ static bool redistributeInfo(PlannerInfo* root, List* subPlans, Plan* plan, List** redistributeKey, Bitmapset** redistributePlanSet, Distribution** redistributeDistribution, bool isunionall, bool canDiskeyChange) { @@ -297,8 +384,8 @@ static bool redistributeInfo(PlannerInfo* root, List* subPlans, Plan* plan, List bool norediskeyplan = false, redistributedplan = false; /* - * This is the case that no columns needed by append, - * but with lower level of colstore table. + * 这是一个特殊情况,对于Append操作,不需要任何列, + * 但目标表可能具有较低级别的列存储表。 */ if (plan->targetlist == NIL && !isunionall) return false; @@ -307,29 +394,28 @@ static bool redistributeInfo(PlannerInfo* root, List* subPlans, Plan* plan, List subPlanKeyArray = (List**)palloc0(sizeof(List*) * subPlanNum); subPlanIndex = 0; - /* We get the best target node group for set op */ + /* 获取Set操作的最佳目标节点分组 */ Distribution* target_distribution = ng_get_best_setop_distribution(subPlans, isunionall, root->is_correlated); /* - * Find redistribute key for each subplan. + * 为每个子计划查找重分发键。 * - * We have three kinds of subplan here. - * (1) replicate plan. - * (2) redistributed plan. - * (3) non-replicate plan that has no distribute key possible + * 这里有三种类型的子计划。 + * (1) 复制计划。 + * (2) 重分发计划。 + * (3) 无法有分布键的非复制计划。 * - * Note, we can't have all replicate plan in subplans here, since it has been handled earlier. - * Note, there may be all replicate plan in subplans, if their exec nodes have no overlap. + * 请注意,这里的子计划不能都是复制计划,因为之前已经处理过它们。 + * 请注意,如果它们的执行节点没有重叠,子计划中可能全部都是复制计划。 * - * For union all, we need replicate plan to be redistributed, and find a common redistribute - * key as possible (not a must). For non-union all, we need to find a common redistribute - * key for all the plans. + * 对于Union All操作,我们需要将复制计划重新分发,并尽量找到一个通用的重新分发键(不是必须的)。 + * 对于非Union All操作,我们需要为所有计划找到一个通用的重新分发键。 */ foreach (cell, subPlans) { subPlan = (Plan*)lfirst(cell); Distribution* current_distribution = ng_get_dest_distribution(subPlan); - /* When the target and the current are all distributed by single node, ignore the distribution keys. */ + /* 当目标和当前都由单一节点组进行分发时,忽略分发键。 */ if (!ng_is_single_node_group_distribution(current_distribution)) { subPlanKeyIndex = distributeKeyIndex(root, subPlan->distributed_keys, subPlan->targetlist); @@ -344,19 +430,15 @@ static bool redistributeInfo(PlannerInfo* root, List* subPlans, Plan* plan, List (list_length(en->nodeList) == 1 && bms_num_members(target_distribution->bms_data_nodeids) > 1); /* - * Whether a stream has been added for the plan only executes on one datanode - * of multi-datanode group, we should redistribute it beforehand. Or it'll lead - * duplicate results when pushing down exec_nodes to other datanodes, and we - * can't prevent exec_nodes pushing down since executor needs all the consumer - * to be same in one thread, or it'll hang. + * 如果为只在多个数据节点组中的一个数据节点上执行的计划添加了流, + * 我们应该提前重新分发它。否则,当将exec_nodes下推到其他数据节点时,将导致重复结果,并且我们无法阻止exec_nodes下推,因为执行器需要在一个线程中将所有消费者都相同,否则会挂起。 */ if (partial_single_node) { ListCell* lc = NULL; List* distkeys = NIL; /* - * Since there's no stats info in append rel, we can only roughly - * choose the distribute key to do redistribute + * 由于附加关系中没有统计信息,我们只能粗略选择用于重新分发的分发键。 */ foreach (lc, subPlan->targetlist) { TargetEntry* tle = (TargetEntry*)lfirst(lc); @@ -366,15 +448,14 @@ static bool redistributeInfo(PlannerInfo* root, List* subPlans, Plan* plan, List } } if (distkeys != NIL) { - /* Found a valid distribute key, so use it */ + /* 找到一个有效的分发键,因此使用它 */ subPlan = make_stream_plan(root, subPlan, distkeys, 1.0, target_distribution); subPlanKeyArray[subPlanIndex] = distributeKeyIndex(root, subPlan->distributed_keys, subPlan->targetlist); } else { /* - * No suitable distribute key, and we don't support distribute on - * roundrobin, so make a const to distribute on it. NOTE. We know - * it's not a good idea, but no way in the moment. Can improve later + * 没有合适的分发键,而且我们不支持在RoundRobin上进行分发,所以制作一个const来进行分发。 + * 注意。我们知道这不是一个好主意,但目前没有其他方法,可以以后改进。 */ Const* con = makeConst(INT4OID, -1, InvalidOid, -2, (Datum)0, true, false); distkeys = list_make1(con); @@ -409,19 +490,18 @@ static bool redistributeInfo(PlannerInfo* root, List* subPlans, Plan* plan, List subPlanIndex++; } - /* Sepcial process for union all. */ + /* Union All操作的特殊处理。 */ if (isunionall) { - /* if all the plans are hashed plan and in same node group, common distribute key is possible */ + /* 如果所有计划都是哈希计划,并且在同一节点组中,可能存在通用分发键 */ if (!norediskeyplan) { redistributeKeyIndex = get_distkey_for_unionall(subPlanKeyArray, subPlanNum); } else if (!redistributedplan && !judge_redistribute_setop_support(root, subPlans, redistributePlanSetCopy)) { - /* if no hashed plan, no distribute key is possible */ + /* 如果没有哈希计划,也没有分发键可能 */ result = false; } } else { /* - * Find no distribute key for subPlan original, we should generate distribute key from max - * cost plan as the distribute key. + * 对于原始的子计划找不到分发键,我们应该从具有最大代价的计划生成分发键。 */ if (!redistributedplan) { redistributeKeyIndex = get_max_cost_distkey_for_nulldistkey(root, subPlans, subPlanNum, subPlanCostArray); @@ -429,8 +509,8 @@ static bool redistributeInfo(PlannerInfo* root, List* subPlans, Plan* plan, List result = false; } else { /* - * There has distribute key for subPlan original, - * use subPlanKeyArray as the distribute key. + * 子计划原始已有分发键, + * 使用subPlanKeyArray作为分发键。 */ redistributeKeyIndex = get_max_cost_distkey_for_hasdistkey( root, subPlans, subPlanNum, subPlanKeyArray, subPlanCostArray, &redistributePlanSetCopy); @@ -447,54 +527,52 @@ static bool redistributeInfo(PlannerInfo* root, List* subPlans, Plan* plan, List return result; } -static ExecNodes* append_merge_exec_nodes(List* subplans, bool is_distributed) + +static ExecNodes* append_merge_exec_nodes(List* subplans, bool is_distributed)//用于合并多个子计划的执行节点信息 { ListCell* lc = NULL; - ExecNodes* merged_en = (ExecNodes*)makeNode(ExecNodes); - Distribution* merged_distribution = NULL; + ExecNodes* merged_en = (ExecNodes*)makeNode(ExecNodes); // 创建一个合并后的执行节点信息 + Distribution* merged_distribution = NULL; // 合并后的分布信息 foreach (lc, subplans) { Plan* subplan = (Plan*)lfirst(lc); - ExecNodes* en = subplan->exec_nodes; + ExecNodes* en = subplan->exec_nodes; // 获取子计划的执行节点信息 if (IsA(subplan, Stream)) - en = ((Stream*)subplan)->consumer_nodes; + en = ((Stream*)subplan)->consumer_nodes; // 如果子计划是Stream计划,获取其消费者节点信息 - merged_en->nodeList = list_merge_int(merged_en->nodeList, en->nodeList); + merged_en->nodeList = list_merge_int(merged_en->nodeList, en->nodeList); // 合并执行节点列表 /* - * There are two callers of this function - * (1) mark_distribute_setop : the en->distribution may not the same - * (2) mark_distribute_setop_distribution : the en->distribution should be the same - * So, we could not do this assert : ng_is_same_group(&merged_en->distribution, &en->distribution) + * 有两个调用者调用了这个函数: + * (1) mark_distribute_setop:en->distribution可能不相同 + * (2) mark_distribute_setop_distribution:en->distribution应该相同 + * 因此,我们不能断言:ng_is_same_group(&merged_en->distribution, &en->distribution) */ if (is_distributed && (merged_distribution != NULL && !ng_is_same_group(merged_distribution, &en->distribution))) { - elog(ERROR, "The distribution of merged and exec node are not the same\n" - "merged distribution is %s\n" - "supblan distribution is %s", - dist_to_str(merged_distribution), - dist_to_str(&en->distribution)); - } + elog(ERROR, "合并后的执行节点和子计划的分布信息不同\n" + "合并后的分布信息:%s\n" + "子计划分布信息:%s", + dist_to_str(merged_distribution), + dist_to_str(&en->distribution)); + } - Distribution* new_merged_distribution = ng_get_union_distribution(merged_distribution, &en->distribution); - DestroyDistribution(merged_distribution); - merged_distribution = new_merged_distribution; + Distribution* new_merged_distribution = ng_get_union_distribution(merged_distribution, &en->distribution); // 合并分布信息 + DestroyDistribution(merged_distribution); // 销毁旧的合并分布信息 + merged_distribution = new_merged_distribution; // 更新合并分布信息 } - ng_set_distribution(&merged_en->distribution, merged_distribution); + ng_set_distribution(&merged_en->distribution, merged_distribution); // 设置合并后的执行节点的分布信息 foreach (lc, subplans) { Plan* subplan = (Plan*)lfirst(lc); ExecNodes* en = subplan->exec_nodes; /* - * If the subplan contains stream, we should pushdown the merged exec_nodes. - * The reason why we do this is because different exec_nodes between top plan node and - * stream consumer_nodes may cause hang up. - * - * And we must pushdown merged exec_nodes when subplan->dop > 1. because when add local - * stream, we need the plan node on both sides of the local stream node have the same exec nodes. - * pushdown merged exec_nodes can guarantee this. + * 如果子计划包含Stream,我们应该下推合并后的执行节点信息。 + * 我们之所以这样做的原因是因为顶层计划节点和流消费者节点之间的不同执行节点可能会导致挂起。 + * 当子计划的dop > 1时,我们必须下推合并后的执行节点,因为在添加本地流时,我们需要本地流节点两侧的计划节点具有相同的执行节点。 + * 下推合并后的执行节点可以保证这一点。 */ if (!contain_special_plan_node(subplan, T_Stream, CPLN_NO_IGNORE_MATERIAL) && subplan->dop == 1) { continue; @@ -503,78 +581,73 @@ static ExecNodes* append_merge_exec_nodes(List* subplans, bool is_distributed) en = ((Stream*)subplan)->consumer_nodes; if (list_length(merged_en->nodeList) > list_length(en->nodeList)) { /* - * Use a deep copy of 'merged_en', in case the subplan's - * baselocatortype was changed by the assignment of - * merged_en->baselocatortype. + * 使用'merged_en'的深拷贝,以防子计划的baselocatortype在赋值merged_en->baselocatortype时发生更改。 */ ExecNodes* temp_execnodes = (ExecNodes*)copyObject(merged_en); temp_execnodes->baselocatortype = en->baselocatortype; - pushdown_execnodes(subplan, temp_execnodes, true); + pushdown_execnodes(subplan, temp_execnodes, true); // 下推执行节点信息 } } - merged_en->baselocatortype = LOCATOR_TYPE_HASH; - return merged_en; + merged_en->baselocatortype = LOCATOR_TYPE_HASH; // 设置合并后的执行节点的基本定位类型为哈希 + return merged_en; // 返回合并后的执行节点信息 } + +/* +代码用于标记分布式查询中的Set操作(例如Union、Union All)的执行计划的分布信息。 +它主要处理分布式Set操作的子计划列表,根据一些条件和规则,对子计划的执行计划进行修改和调整,以确保分布信息的一致性和正确性。 +*/ static void mark_distribute_setop_distribution(PlannerInfo* root, Node* node, Plan* plan, List* subPlans, Bitmapset* redistributePlanSet, List* redistributeKey, Distribution* redistributeDistribution) { ListCell* cell = NULL; - List* newSubPlans = NIL; - Plan* subPlan = NULL; - Index subPlanIndex = 0; - TargetEntry* teEntry = NULL; - MergeAppend* mergeAppend = NULL; - Append* append = NULL; - ListCell* attnumCell = NULL; - AttrNumber attnum; - RecursiveUnion* recursiveUnionPlan = NULL; - Distribution *newDistribution = redistributeDistribution; + List* newSubPlans = NIL; // 存储调整后的子计划列表 + Plan* subPlan = NULL; // 子计划 + Index subPlanIndex = 0; // 子计划索引 + TargetEntry* teEntry = NULL; // 目标条目 + MergeAppend* mergeAppend = NULL; // MergeAppend计划节点 + Append* append = NULL; // Append计划节点 + ListCell* attnumCell = NULL; // 属性编号的列表元素 + AttrNumber attnum; // 属性编号 + RecursiveUnion* recursiveUnionPlan = NULL; // RecursiveUnion计划节点 + Distribution *newDistribution = redistributeDistribution; // 新的分布信息,默认为原始分布信息 + // 检查并确定计划节点的类型 if (IsA(node, MergeAppend)) { mergeAppend = (MergeAppend*)node; } else if (IsA(node, Append)) { - AssertEreport(IsA(node, Append), MOD_OPT, "The node is NOT a Append"); + AssertEreport(IsA(node, Append), MOD_OPT, "该节点不是一个Append节点"); append = (Append*)node; } else if (IsA(node, RecursiveUnion)) { recursiveUnionPlan = (RecursiveUnion*)node; } + // 如果存在需要重新分发的子计划 if (!bms_is_empty(redistributePlanSet)) { foreach (cell, subPlans) { subPlan = (Plan*)lfirst(cell); List* distribute_keys = NIL; List* subplandistkey = redistributeKey; - /* - * There are four cases enter the else branch below: - * 1. All subplan are hash and there are no distkey; - * 2. All subplan are hash which some have distkey and some have no distkey; - * 3. Some subplan are hash which involve two cases above-mentioned and some are replication. - * We will choose distkey for replication of union all. - */ + // 判断是否需要为子计划生成新的分发键 if (subplandistkey == NIL) subplandistkey = make_distkey_for_append(root, subPlan); - /* - * Add distribute node - */ + // 构建子计划的分发键 foreach (attnumCell, subplandistkey) { attnum = lfirst_int(attnumCell); if ((attnum - 1) >= list_length(subPlan->targetlist)) { - elog(ERROR, "attnum overflow the length of subplan targetlist"); + elog(ERROR, "属性编号溢出子计划目标列表的长度"); } teEntry = (TargetEntry*)list_nth(subPlan->targetlist, attnum - 1); distribute_keys = lappend(distribute_keys, teEntry->expr); } + // 如果子计划在需要重新分发的计划集合中 if (bms_is_member(subPlanIndex, redistributePlanSet)) { - /* - * If both sub plan are replicate plan and we could not get distribute keys for them, - * we need to broadcast both of them to a single datanode from redistributeDistribution - */ + // 如果两个子计划都是复制计划,并且无法为它们获取分发键,则需要将它们广播到重新分发的数据节点 bool noDistribute_keys = (distribute_keys == NIL && bms_num_members(redistributeDistribution->bms_data_nodeids) > 1); if (noDistribute_keys) { @@ -583,41 +656,36 @@ static void mark_distribute_setop_distribution(PlannerInfo* root, Node* node, Pl Plan* newplan = subPlan; + // 如果查询是相关的并且启用了子查询参数化,不允许添加流操作符 if (root->is_correlated && SUBQUERY_PREDPUSH(root)) - elog(ERROR, "Can not add stream operator on to parameterize plan."); + elog(ERROR, "不能在参数化计划上添加流操作符。"); - - /* Make stream node of redistribute. */ + // 创建重新分发的流计划节点 bool partial_single_node = bms_num_members (newDistribution->bms_data_nodeids) == 1 && bms_num_members(redistributeDistribution->bms_data_nodeids) > 1 && distribute_keys == NIL; if (partial_single_node) { - /* - * If a stream plan only executes on one datanode of multi-datanode group, - * we should redistribute it. Or it'll lead duplicate results when pushing - * down exec_nodes to other datanodes - */ + // 如果一个流计划只在多数据节点组的一个数据节点上执行,需要重新分发它,否则会导致推送执行节点到其他数据节点时出现重复结果 const int typeMod = -1; const int typeLen = -2; Const *con = makeConst(INT4OID, typeMod, InvalidOid, typeLen, (Datum)0, true, false); newplan = make_stream_plan(root, subPlan, list_make1(con), 0, redistributeDistribution); } else { newplan = make_stream_plan(root, subPlan, distribute_keys, 0, newDistribution); - /* We should use the original redistributeDistribution as the - * stream->consumer_nodes->distribution - * to make sure all the subplans of append has the same distributeion. - */ + // 确保流计划的消费者节点分布与重新分发的数据节点分布一致 if (newDistribution != redistributeDistribution) { ng_copy_distribution(&((Stream *)newplan)->consumer_nodes->distribution, redistributeDistribution); } } + // 设置流计划节点的排序信息(如果有) if (IsA(newplan, Stream)) { Stream *streamNode = (Stream *)newplan; streamNode->is_sorted = IsA(node, MergeAppend) ? true : false; } + // 将新的子计划添加到新的子计划列表 if (PointerIsValid(mergeAppend)) { newSubPlans = lappend(newSubPlans, make_sort(root, @@ -632,53 +700,59 @@ static void mark_distribute_setop_distribution(PlannerInfo* root, Node* node, Pl newSubPlans = lappend(newSubPlans, newplan); } } else { + // 如果子计划不需要重新分发,直接将其添加到新的子计划列表 newSubPlans = lappend(newSubPlans, subPlan); } subPlanIndex++; } + // 更新Set操作节点的子计划列表 if (PointerIsValid(mergeAppend)) { mergeAppend->mergeplans = newSubPlans; } else if (PointerIsValid(recursiveUnionPlan)) { const int invalidLen = 2; - AssertEreport(list_length(newSubPlans) == invalidLen, MOD_OPT, "Invalid subplan length"); + AssertEreport(list_length(newSubPlans) == invalidLen, MOD_OPT, "无效的子计划长度"); Plan* recursive_base_plan = (Plan*)recursiveUnionPlan; recursive_base_plan->lefttree = (Plan*)linitial(newSubPlans); recursive_base_plan->righttree = (Plan*)lsecond(newSubPlans); } else { - AssertEreport(PointerIsValid(append), MOD_OPT, "The append is NULL"); + AssertEreport(PointerIsValid(append), MOD_OPT, "Append节点为空"); append->appendplans = newSubPlans; } - subPlans = newSubPlans; + subPlans = newSubPlans; // 更新子计划列表 } + // 根据重新分发的键更新计划节点的分发键 foreach (attnumCell, redistributeKey) { attnum = lfirst_int(attnumCell); if (list_length(plan->targetlist) < attnum) { - elog(ERROR, "target list is too short"); + elog(ERROR, "目标列表长度过短"); } teEntry = (TargetEntry*)list_nth(plan->targetlist, attnum - 1); plan->distributed_keys = lappend(plan->distributed_keys, teEntry->expr); } + // 设置计划节点的执行类型和执行节点信息 plan->exec_type = EXEC_ON_DATANODES; plan->exec_nodes = append_merge_exec_nodes(subPlans, true); } -void mark_distribute_setop(PlannerInfo* root, Node* node, bool isunionall, bool canDiskeyChange) -{ - List* subPlans = NIL; - ListCell* planCell = NULL; - Plan* plan = NULL; - Bitmapset* execAllNodesPlanSet = NULL; - Index subPlanIndex = 0; - bool execOnCoords = false; +void mark_distribute_setop(PlannerInfo* root, Node* node, bool isunionall, bool canDiskeyChange)//代码用于标记分布式Set操作(例如Union、Union All)的执行计划 +{ + List* subPlans = NIL; // 子计划列表 + ListCell* planCell = NULL; + Plan* plan = NULL; // 主计划节点 + Bitmapset* execAllNodesPlanSet = NULL; // 所有节点都执行的计划集合 + Index subPlanIndex = 0; // 子计划索引 + bool execOnCoords = false; // 是否在协调节点执行 + + // 根据不同的Set操作类型,获取子计划列表和主计划节点 if (IsA(node, Append)) { Append* appendPlan = (Append*)node; @@ -693,7 +767,7 @@ void mark_distribute_setop(PlannerInfo* root, Node* node, bool isunionall, bool } else { MergeAppend* mergeAppendPlan = NULL; - AssertEreport(IsA(node, MergeAppend), MOD_OPT, "The node is NOT a MergeAppend"); + AssertEreport(IsA(node, MergeAppend), MOD_OPT, "该节点不是一个MergeAppend"); mergeAppendPlan = (MergeAppend*)node; @@ -701,9 +775,11 @@ void mark_distribute_setop(PlannerInfo* root, Node* node, bool isunionall, bool plan = &(mergeAppendPlan->plan); } - AssertEreport(PointerIsValid(subPlans), MOD_OPT, "The subPlan is NULL"); - AssertEreport(list_length(subPlans) >= 1, MOD_OPT, "The list length of subplan is 0"); + // 检查子计划列表的有效性 + AssertEreport(PointerIsValid(subPlans), MOD_OPT, "子计划为空"); + AssertEreport(list_length(subPlans) >= 1, MOD_OPT, "子计划列表长度为0"); + // 检查子计划是否在协调节点上执行 foreach (planCell, subPlans) { Plan* subPlan = (Plan*)lfirst(planCell); @@ -713,16 +789,17 @@ void mark_distribute_setop(PlannerInfo* root, Node* node, bool isunionall, bool } } + // 如果在协调节点上执行且查询是相关的,并且启用了子查询参数化,则不允许添加流操作符 if (execOnCoords) { if (SUBQUERY_IS_PARAM(root) && root->is_correlated) { - elog(ERROR, "Can not add stream operator on to parameterize plan."); + elog(ERROR, "不能在参数化计划上添加流操作符。"); } } else { subPlanIndex = 0; foreach (planCell, subPlans) { Plan* subPlan = (Plan*)lfirst(planCell); - /* make each branch replicated if there are subplan exprs in one branches */ + // 如果查询是相关的,但不启用子查询参数化,则将包含流计划节点的分支材料化 if (root->is_correlated && !(SUBQUERY_PREDPUSH(root))) { if (contain_special_plan_node(subPlan, T_Stream)) { subPlan = materialize_finished_plan(subPlan, true, root->glob->vectorized); @@ -730,38 +807,38 @@ void mark_distribute_setop(PlannerInfo* root, Node* node, bool isunionall, bool lfirst(planCell) = subPlan; } + // 如果子计划在所有节点上执行,则将其添加到执行所有节点的计划集合中 if (is_execute_on_allnodes(subPlan)) { execAllNodesPlanSet = bms_add_member(execAllNodesPlanSet, subPlanIndex); - subPlanIndex++; continue; } + // 如果子计划的执行节点列表为空,则记录调试信息 if (subPlan->exec_nodes->nodeList == NIL) { - elog(DEBUG1, "[mark_distribute_setop] empty node list"); + elog(DEBUG1, "[mark_distribute_setop] 执行节点列表为空"); } - AssertEreport(PointerIsValid(subPlan->exec_nodes), MOD_OPT, "The subPlan's exec_nodes is NULL"); + AssertEreport(PointerIsValid(subPlan->exec_nodes), MOD_OPT, "子计划的执行节点列表为空"); subPlanIndex++; } + // 如果所有子计划都在所有节点上执行,则标记主计划节点以执行所有节点 if (bms_num_members(execAllNodesPlanSet) == list_length(subPlans)) { mark_distribute_setop_allnodes(plan); } else { + /* - * Just redistribute subplans if there is no less than one subplan - * that is distributed + * 如果至少有一个子计划需要重新分发, + * 则获取重新分发的信息并更新执行计划,否则将添加远程查询节点到每个子计划上 */ Bitmapset* redistributePlanSet = NULL; List* redistributeKey = NIL; Distribution* redistributeDistribution = NULL; bool result = false; - /* - * get redistribute information - * return true if succeed; else return fail - */ + // 获取重新分发的信息,如果成功则返回true,否则返回false result = redistributeInfo(root, subPlans, plan, @@ -772,23 +849,25 @@ void mark_distribute_setop(PlannerInfo* root, Node* node, bool isunionall, bool canDiskeyChange); if (result) { + // 更新执行计划以反映重新分发的信息 mark_distribute_setop_distribution( root, node, plan, subPlans, redistributePlanSet, redistributeKey, redistributeDistribution); } else { /* - * Add remote query node on top of each subplan if fail - * to get redistribute information + * 如果无法获取重新分发的信息,则在每个子计划上添加远程查询节点 */ mark_distribute_setop_remotequery(root, node, plan, subPlans); } } + // 释放执行所有节点的计划集合 if (PointerIsValid(execAllNodesPlanSet)) { pfree_ext(execAllNodesPlanSet); } } } + // the name is_stream_support is used in function check_stream_support, which used as condition void mark_stream_unsupport() { @@ -806,4 +885,3 @@ void SerializePlan( { DISTRIBUTED_FEATURE_NOT_SUPPORTED(); } - diff --git a/src/gausskernel/optimizer/plan/streamplan_utils.cpp b/src/gausskernel/optimizer/plan/streamplan_utils.cpp old mode 100755 new mode 100644 index 425aaa33f..85238ace7 --- a/src/gausskernel/optimizer/plan/streamplan_utils.cpp +++ b/src/gausskernel/optimizer/plan/streamplan_utils.cpp @@ -41,10 +41,11 @@ */ List* check_op_list_template(Plan* result_plan, List* (*check_eval)(Node*)) { - List* res_list = NIL; + List* res_list = NIL; // 创建一个空的List用于存储检查结果 + + res_list = check_eval((Node*)result_plan->targetlist); // 检查目标列表(targetlist) + res_list = list_concat_unique(res_list, check_eval((Node*)result_plan->qual)); // 检查过滤条件(qual) - res_list = check_eval((Node*)result_plan->targetlist); - res_list = list_concat_unique(res_list, check_eval((Node*)result_plan->qual)); switch (nodeTag(result_plan)) { case T_SeqScan: case T_CStoreScan: @@ -60,13 +61,14 @@ List* check_op_list_template(Plan* result_plan, List* (*check_eval)(Node*)) ForeignTable* ftbl = NULL; ForeignServer* fsvr = NULL; + // 获取外部表和外部服务器的信息 ftbl = GetForeignTable(foreignScan->scan_relid); AssertEreport(NULL != ftbl, MOD_OPT, "The foreign table is NULL"); fsvr = GetForeignServer(ftbl->serverid); AssertEreport(NULL != fsvr, MOD_OPT, "The foreign server is NULL"); /* - * If the predicate is pushed down, must find subplan from item->hdfsQual struct. + * 如果谓词条件被下推,则从item->hdfsQual结构中查找子计划。 */ if (isObsOrHdfsTableFormSrvName(fsvr->servername)) { List* foreignPrivateList = (List*)foreignScan->fdw_private; @@ -78,90 +80,92 @@ List* check_op_list_template(Plan* result_plan, List* (*check_eval)(Node*)) case T_IndexScan: { IndexScan* splan = (IndexScan*)result_plan; - res_list = list_concat_unique(res_list, check_eval((Node*)splan->indexqual)); + res_list = list_concat_unique(res_list, check_eval((Node*)splan->indexqual)); // 检查索引条件 } break; case T_IndexOnlyScan: { IndexOnlyScan* splan = (IndexOnlyScan*)result_plan; - res_list = list_concat_unique(res_list, check_eval((Node*)splan->indexqual)); + res_list = list_concat_unique(res_list, check_eval((Node*)splan->indexqual)); // 检查索引条件 } break; case T_CStoreIndexScan: { CStoreIndexScan* splan = (CStoreIndexScan*)result_plan; - res_list = list_concat_unique(res_list, check_eval((Node*)splan->indexqual)); + res_list = list_concat_unique(res_list, check_eval((Node*)splan->indexqual)); // 检查索引条件 } break; case T_DfsIndexScan: { DfsIndexScan* splan = (DfsIndexScan*)result_plan; - res_list = list_concat_unique(res_list, check_eval((Node*)splan->indexqual)); + res_list = list_concat_unique(res_list, check_eval((Node*)splan->indexqual)); // 检查索引条件 } break; case T_BitmapIndexScan: { BitmapIndexScan* splan = (BitmapIndexScan*)result_plan; - res_list = list_concat_unique(res_list, check_eval((Node*)splan->indexqual)); + res_list = list_concat_unique(res_list, check_eval((Node*)splan->indexqual)); // 检查索引条件 } break; case T_CStoreIndexCtidScan: { CStoreIndexCtidScan* splan = (CStoreIndexCtidScan*)result_plan; - res_list = list_concat_unique(res_list, check_eval((Node*)splan->indexqual)); + res_list = list_concat_unique(res_list, check_eval((Node*)splan->indexqual)); // 检查索引条件 } break; case T_TidScan: { TidScan* splan = (TidScan*)result_plan; - res_list = list_concat_unique(res_list, check_eval((Node*)splan->tidquals)); + res_list = list_concat_unique(res_list, check_eval((Node*)splan->tidquals)); // 检查TID条件 } break; case T_FunctionScan: { FunctionScan* splan = (FunctionScan*)result_plan; - res_list = list_concat_unique(res_list, check_eval((Node*)splan->funcexpr)); + res_list = list_concat_unique(res_list, check_eval((Node*)splan->funcexpr)); // 检查函数表达式条件 } break; case T_ValuesScan: { ValuesScan* splan = (ValuesScan*)result_plan; - res_list = list_concat_unique(res_list, check_eval((Node*)splan->values_lists)); + res_list = list_concat_unique(res_list, check_eval((Node*)splan->values_lists)); // 检查值表达式条件 } break; case T_NestLoop: case T_VecNestLoop: { Join* splan = (Join*)result_plan; - res_list = list_concat_unique(res_list, check_eval((Node*)splan->joinqual)); + res_list = list_concat_unique(res_list, check_eval((Node*)splan->joinqual)); // 检查连接条件 } break; case T_MergeJoin: case T_VecMergeJoin: { MergeJoin* splan = (MergeJoin*)result_plan; - res_list = list_concat_unique(res_list, check_eval((Node*)splan->join.joinqual)); - res_list = list_concat_unique(res_list, check_eval((Node*)splan->mergeclauses)); + res_list = list_concat_unique(res_list, check_eval((Node*)splan->join.joinqual)); // 检查连接条件 + res_list = list_concat_unique(res_list, check_eval((Node*)splan->mergeclauses)); // 检查合并条件 } break; case T_HashJoin: case T_VecHashJoin: { HashJoin* splan = (HashJoin*)result_plan; - res_list = list_concat_unique(res_list, check_eval((Node*)splan->join.joinqual)); - res_list = list_concat_unique(res_list, check_eval((Node*)splan->hashclauses)); + res_list = list_concat_unique(res_list, check_eval((Node*)splan->join.joinqual)); // 检查连接条件 + res_list = list_concat_unique(res_list, check_eval((Node*)splan->hashclauses)); // 检查哈希条件 } break; case T_Limit: case T_VecLimit: { Limit* splan = (Limit*)result_plan; - res_list = list_concat_unique(res_list, check_eval((Node*)splan->limitOffset)); - res_list = list_concat_unique(res_list, check_eval((Node*)splan->limitCount)); + res_list = list_concat_unique(res_list, check_eval((Node*)splan->limitOffset)); // 检查限制偏移 + res_list = list_concat_unique(res_list, check_eval((Node*)splan->limitCount)); // 检查限制数量 } break; case T_VecWindowAgg: case T_WindowAgg: { WindowAgg* splan = (WindowAgg*)result_plan; - res_list = list_concat_unique(res_list, check_eval((Node*)splan->startOffset)); - res_list = list_concat_unique(res_list, check_eval((Node*)splan->endOffset)); + res_list = list_concat_unique(res_list, check_eval((Node*)splan->startOffset)); // 检查窗口聚合的起始偏移 + res_list = list_concat_unique(res_list, check_eval((Node*)splan->endOffset)); // 检查窗口聚合的结束偏移 } break; case T_BaseResult: case T_VecResult: { BaseResult* splan = (BaseResult*)result_plan; - res_list = list_concat_unique(res_list, check_eval((Node*)splan->resconstantqual)); + res_list = list_concat_unique(res_list, check_eval((Node*)splan->resconstantqual)); // 检查基本结果节点的常量条件 } break; case T_ModifyTable: { ModifyTable* splan = (ModifyTable*)result_plan; + + // 如果是更新操作(upsertAction为UPSERT_UPDATE)并且有更新目标列表(updateTlist),则检查更新目标列表 if (splan->upsertAction == UPSERT_UPDATE && splan->updateTlist != NULL) { res_list = list_concat_unique(res_list, check_eval((Node*)splan->updateTlist)); } @@ -169,9 +173,10 @@ List* check_op_list_template(Plan* result_plan, List* (*check_eval)(Node*)) default: break; } - return res_list; + return res_list; // 返回检查结果的List } + /* * @Descarption: Search node and check funcid > FirstNormalObjectId. * @in node: Current node. @@ -180,21 +185,26 @@ List* check_op_list_template(Plan* result_plan, List* (*check_eval)(Node*)) static bool check_func_walker(Node* node, bool* found) { if (node == NULL) { - return false; + return false; // 如果节点为空,返回 false,表示没有找到目标函数 } else if (IsA(node, FuncExpr)) { + // 如果节点是函数表达式(FuncExpr)类型 FuncExpr* fexpr = (FuncExpr*)node; if (fexpr->funcid > FirstNormalObjectId) { - *found = true; - return true; + // 如果函数的函数ID大于 FirstNormalObjectId,表示这是一个自定义函数 + *found = true; // 将 found 标志设置为 true,表示找到了目标函数 + return true; // 返回 true,表示已经找到了目标函数,停止遍历 } else { - return false; + return false; // 函数ID不大于 FirstNormalObjectId,不是自定义函数,返回 false } } + // 继续遍历表达式树的子节点 return expression_tree_walker(node, (bool (*)())check_func_walker, found); + // 调用 expression_tree_walker 函数,继续遍历子节点,并将 found 标志传递下去 } + /* * @Description: Check this node if vartype > FirstNormalObjectId. * @in qual: Checked node. diff --git a/src/gausskernel/optimizer/plan/streamwalker.cpp b/src/gausskernel/optimizer/plan/streamwalker.cpp index e28b35271..e68a11f21 100644 --- a/src/gausskernel/optimizer/plan/streamwalker.cpp +++ b/src/gausskernel/optimizer/plan/streamwalker.cpp @@ -82,77 +82,96 @@ static uint unsupport_func[] = { bool stream_walker(Node* node, void* context) { if (node == NULL) - return false; + return false; // 如果节点为空,返回 false - shipping_context *cxt = (shipping_context*)context; + shipping_context *cxt = (shipping_context*)context; // 将上下文数据转换为 shipping_context 结构体类型 switch (nodeTag(node)) { case T_Query: { - stream_walker_query((Query*)node, cxt); + stream_walker_query((Query*)node, cxt); // 如果节点是 Query 类型,调用 stream_walker_query 函数处理 } break; case T_TargetEntry: { - stream_walker_target_entry((TargetEntry*)node, cxt); + stream_walker_target_entry((TargetEntry*)node, cxt); // 如果节点是 TargetEntry 类型,调用 stream_walker_target_entry 函数处理 } break; case T_FuncExpr: { - stream_walker_func_expr((FuncExpr*)node, cxt); + stream_walker_func_expr((FuncExpr*)node, cxt); // 如果节点是 FuncExpr 类型,调用 stream_walker_func_expr 函数处理 } break; case T_Aggref: { - stream_walker_aggref((Aggref*)node, cxt); + stream_walker_aggref((Aggref*)node, cxt); // 如果节点是 Aggref 类型,调用 stream_walker_aggref 函数处理 } break; case T_CoerceViaIO: { - stream_walker_coerce((CoerceViaIO*) node, cxt); + stream_walker_coerce((CoerceViaIO*) node, cxt); // 如果节点是 CoerceViaIO 类型,调用 stream_walker_coerce 函数处理 } break; default: break; } - return expression_tree_walker(node, (bool (*)())stream_walker, context); + return expression_tree_walker(node, (bool (*)())stream_walker, context); // 递归遍历节点的子树,继续执行遍历操作 } + static bool containReplicatedTable(List *rtable) { ListCell *lc = NULL; foreach(lc, rtable) { - RangeTblEntry *rte = (RangeTblEntry *)lfirst(lc); + RangeTblEntry *rte = (RangeTblEntry *)lfirst(lc); // 获取范围表条目 + + // 如果范围表条目的 locator_type 表示为复制表(Replicated Table),则返回 true if (IsLocatorReplicated(rte->locator_type)) { return true; } } + + // 如果循环结束后没有找到复制表,返回 false return false; } + static void stream_walker_query_insertinto_rep(Query* query, shipping_context *cxt) { + // 如果当前查询不支持流式传输,则直接返回 if (!cxt->current_shippable) { return; } + + // 检查查询类型是否为 INSERT,以及结果关系是否为 0,或结果关系不是复制表 if (query->commandType != CMD_INSERT || query->resultRelation == 0 || !IsLocatorReplicated(rt_fetch(query->resultRelation, query->rtable)->locator_type)) { return; } + ListCell *lc = NULL; int index = 0; + + // 遍历查询的范围表中的每个条目 foreach(lc, query->rtable) { index++; + + // 跳过结果关系对应的条目 if (index == query->resultRelation) { continue; } + RangeTblEntry *rte = rt_fetch(index, query->rtable); + + // 如果当前条目不是子查询或子查询为空,则继续下一个条目的检查 if (rte->rtekind != RTE_SUBQUERY || rte->subquery == NULL) { continue; } - + + // 如果子查询中包含窗口函数并且子查询的范围表中包含复制表,则不能进行流式传输 if (rte->subquery->hasWindowFuncs && containReplicatedTable(rte->subquery->rtable)) { cxt->current_shippable = false; break; } - /* Cannot shipping if there are junk tlists in replicated subquery */ + /* 如果复制子查询中存在无效的目标列表项,则不能进行流式传输 */ if (check_replicated_junktlist(rte->subquery)) { cxt->current_shippable = false; break; } } + // 如果不能进行流式传输,设置相应的错误信息 if (!cxt->current_shippable) { errno_t sprintf_rc = sprintf_s(u_sess->opt_cxt.not_shipping_info->not_shipping_reason, NOTPLANSHIPPING_LENGTH, @@ -161,29 +180,32 @@ static void stream_walker_query_insertinto_rep(Query* query, shipping_context *c } } + static void stream_walker_query_update(Query* query, shipping_context *cxt) { /* - * Concurrent update under stream mode is not yet supported. - * When u_sess->attr.attr_sql.enable_stream_concurrent_update is off, we will return true to - * generate non-stream plan for update statements. + * 并发更新在流式传输模式下尚未支持。 + * 当 u_sess->attr.attr_sql.enable_stream_concurrent_update 为关闭状态时, + * 我们将返回 true,以生成非流式传输的更新计划。 */ if (query->commandType == CMD_UPDATE && !u_sess->attr.attr_sql.enable_stream_concurrent_update) { - cxt->current_shippable = false; + cxt->current_shippable = false; // 如果是 UPDATE 查询且不支持流式并发更新,设置不能进行流式传输 } if (query->hasForUpdate) { - /* turn off dop for FOR UPDATE/SHARE query */ + /* 关闭 FOR UPDATE/SHARE 查询的并行执行,设置查询的 DOP 为 1 */ u_sess->opt_cxt.query_dop = 1; } } + static void stream_walker_query_recursive(Query* query, shipping_context *cxt) { + // 如果查询中包含递归查询(WITH RECURSIVE),则将查询的并行度(DOP)设置为 1,即不允许并行执行。 if (query->hasRecursive) { - /* If query contains recursive union, turn off dop */ u_sess->opt_cxt.query_dop = 1; - + + // 如果配置参数 enable_stream_recursive 为关闭状态,则不能进行流式传输,并设置相应的错误信息。 if (!u_sess->attr.attr_sql.enable_stream_recursive) { errno_t sprintf_rc = sprintf_s(u_sess->opt_cxt.not_shipping_info->not_shipping_reason, NOTPLANSHIPPING_LENGTH, @@ -194,8 +216,10 @@ static void stream_walker_query_recursive(Query* query, shipping_context *cxt) } } +//函数处理包含 DISTINCT ON 子句的查询,如果查询中包含 DISTINCT ON 子句,则不能进行流式传输,并设置相应的错误信息。 static void stream_walker_query_distinct(Query* query, shipping_context *cxt) { + // 如果查询中包含 DISTINCT ON 子句,则不能进行流式传输,并设置相应的错误信息。 if (query->hasDistinctOn) { errno_t sprintf_rc = sprintf_s(u_sess->opt_cxt.not_shipping_info->not_shipping_reason, NOTPLANSHIPPING_LENGTH, @@ -205,8 +229,10 @@ static void stream_walker_query_distinct(Query* query, shipping_context *cxt) } } + static void stream_walker_query_returning(Query* query, shipping_context *cxt) { + // 如果查询中包含 RETURNING 子句,则不能进行流式传输,并设置相应的错误信息。 if (query->returningList) { errno_t sprintf_rc = sprintf_s(u_sess->opt_cxt.not_shipping_info->not_shipping_reason, NOTPLANSHIPPING_LENGTH, @@ -216,18 +242,24 @@ static void stream_walker_query_returning(Query* query, shipping_context *cxt) } } + static void stream_walker_query_merge(Query* query, shipping_context *cxt) { + // 如果查询的命令类型是 MERGE if (query->commandType == CMD_MERGE) { + // 检查合并源目标列表中是否包含不支持流式传输的特性 if (expression_tree_walker( (Node*)query->mergeSourceTargetList, (bool (*)())stream_walker, (void *)cxt)) { cxt->current_shippable = false; } + // 遍历合并操作列表 ListCell* lc2 = NULL; foreach (lc2, query->mergeActionList) { MergeAction* mc = (MergeAction*)lfirst(lc2); + // 如果合并操作是插入操作 if (mc->commandType == CMD_INSERT) { + // 检查目标表是否包含不支持流式传输的特性 if (rel_contain_unshippable_feature( (RangeTblEntry*)list_nth(query->rtable, query->mergeTarget_relation - 1), cxt, mc->commandType)) { @@ -237,6 +269,7 @@ static void stream_walker_query_merge(Query* query, shipping_context *cxt) bool saved_is_nextval_shippable = cxt->is_nextval_shippable; if (cxt->allow_func_in_targetlist) { cxt->is_nextval_shippable = true; + // 检查目标列表中是否包含不支持流式传输的特性 if (expression_tree_walker( (Node*)mc->targetList, (bool (*)())stream_walker, (void *)cxt)) { cxt->current_shippable = false; @@ -245,15 +278,18 @@ static void stream_walker_query_merge(Query* query, shipping_context *cxt) cxt->is_nextval_shippable = saved_is_nextval_shippable; cxt->allow_func_in_targetlist = false; } else { + // 检查目标列表中是否包含不支持流式传输的特性 if (expression_tree_walker((Node*)mc->targetList, (bool (*)())stream_walker, (void *)cxt)) { cxt->current_shippable = false; } } + // 检查合并操作的条件表达式是否包含不支持流式传输的特性 if (expression_tree_walker((Node*)mc->qual, (bool (*)())stream_walker, (void *)cxt)) { cxt->current_shippable = false; } } else { + // 如果合并操作不是插入操作,检查合并操作是否包含不支持流式传输的特性 if (expression_tree_walker((Node*)mc, (bool (*)())stream_walker, (void *)cxt)) { cxt->current_shippable = false; } @@ -262,36 +298,49 @@ static void stream_walker_query_merge(Query* query, shipping_context *cxt) } } + static void stream_walker_query_upsert(Query *query, shipping_context *cxt) { + // 如果查询的命令类型是 INSERT 且包含 UPSERT 子句 if (query->commandType == CMD_INSERT && query->upsertClause != NULL) { - /* For replicated table in stream, upsertClause cannot contain unshippable expression */ + // 对于流式传输中的复制表(replicated table),upsertClause 不能包含不支持流式传输的表达式 if (query->resultRelation && IsLocatorReplicated(rt_fetch(query->resultRelation, query->rtable)->locator_type)) { + // 临时禁止处理不稳定函数的流式传输,以确保表达式的稳定性 bool saved_disallow_volatile_func_shippable = cxt->disallow_volatile_func_shippable; cxt->disallow_volatile_func_shippable = true; + + // 检查 upsertClause 是否包含不支持流式传输的表达式 if (expression_tree_walker((Node *)query->upsertClause, (bool (*)())stream_walker, (void *)cxt)) { cxt->current_shippable = false; } + + // 恢复处理不稳定函数的流式传输设置 cxt->disallow_volatile_func_shippable = saved_disallow_volatile_func_shippable; } } } + static void stream_walker_query_rtable(Query* query, shipping_context *cxt) { + // 检查查询中是否包含不支持流式传输的表 if (contains_unsupport_tables(query->rtable, query, cxt)) { cxt->current_shippable = false; } + // 如果查询的命令类型不是 SELECT if (query->commandType != CMD_SELECT && - query->resultRelation <= list_length(query->rtable) && - rel_contain_unshippable_feature((RangeTblEntry*)list_nth(query->rtable, query->resultRelation - 1), + query->resultRelation <= list_length(query->rtable)) { + // 检查结果表是否包含不支持流式传输的特性 + if (rel_contain_unshippable_feature((RangeTblEntry*)list_nth(query->rtable, query->resultRelation - 1), cxt, query->commandType)) { cxt->current_shippable = false; + } } } + static void stream_walker_query_exec_direct(Query* query, shipping_context *cxt) { if (query->is_local) { /* execute direct */ @@ -302,153 +351,197 @@ static void stream_walker_query_exec_direct(Query* query, shipping_context *cxt) static void stream_walker_query_cte(Query* query, shipping_context *cxt) { /* - * Random func is not allowed in CTE and limit. - * EC func is not allowed in CTE + * 不允许在 CTE 和 LIMIT 子句中使用随机函数(random func)。 + * 不允许在 CTE 中使用等值连接函数(EC func)。 */ bool random_ori = cxt->is_randomfunc_shippable; bool ecfunc_ori = cxt->is_ecfunc_shippable; cxt->is_randomfunc_shippable = false; cxt->is_ecfunc_shippable = false; - /* walk the entire query tree to analyse the query */ + /* 遍历整个查询树以分析查询 */ ListCell* lc = NULL; foreach (lc, query->cteList) { CommonTableExpr* cte = (CommonTableExpr*)lfirst(lc); if (cte->cterecursive) { - /* Recursive cte does't support dn gather. */ + /* 递归 CTE 不支持分布式收集(dn gather) */ ((shipping_context*)cxt)->disable_dn_gather = true; } (void)stream_walker((Node*)cte->ctequery, (void *)cxt); } + // 恢复随机函数和等值连接函数的流式传输设置 cxt->is_ecfunc_shippable = ecfunc_ori; cxt->is_randomfunc_shippable = random_ori; } + static void stream_walker_query_limitoffset(Query* query, shipping_context *cxt) { /* - * Random func is not allowed in CTE and limit. + * 不允许在 LIMIT 子句中使用随机函数(random func)。 */ bool random_ori = cxt->is_randomfunc_shippable; cxt->is_randomfunc_shippable = false; + // 分析 LIMIT 子句中的表达式 (void)stream_walker((Node*)query->limitCount, (void *)cxt); + + // 分析 OFFSET 子句中的表达式 (void)stream_walker((Node*)query->limitOffset, (void *)cxt); + // 恢复随机函数的流式传输设置 cxt->is_randomfunc_shippable = random_ori; } + static void stream_walker_query_targetlist(Query* query, shipping_context *cxt) { + // 如果允许在目标列表中使用函数,则设置 is_nextval_shippable 为 true,允许流式传输 nextval 函数 if (cxt->allow_func_in_targetlist) { cxt->is_nextval_shippable = true; + // 遍历目标列表中的表达式,检查是否可以进行流式传输 if (expression_tree_walker((Node*)query->targetList, (bool (*)())stream_walker, (void *)cxt)) { cxt->current_shippable = false; } + // 恢复 is_nextval_shippable 设置,并将 allow_func_in_targetlist 设置为 false cxt->is_nextval_shippable = false; cxt->allow_func_in_targetlist = false; } else { + // 如果不允许在目标列表中使用函数,仅遍历目标列表中的表达式,检查是否可以进行流式传输 if (expression_tree_walker((Node*)query->targetList, (bool (*)())stream_walker, (void *)cxt)) { cxt->current_shippable = false; } } } + static void stream_walker_query_jointree(Query* query, shipping_context *cxt) { - if (query->jointree != NULL && - expression_tree_walker((Node*)query->jointree->fromlist, (bool (*)())stream_walker, (void *)cxt)) { - cxt->current_shippable = false; - } - if (query->jointree != NULL && stream_walker((Node*)query->jointree->quals, (void *)cxt)) { - cxt->current_shippable = false; + // 检查是否存在联接树(JOIN tree) + if (query->jointree != NULL) { + // 遍历联接树中的FROM子句(fromlist),检查是否可以进行流式传输 + if (expression_tree_walker((Node*)query->jointree->fromlist, (bool (*)())stream_walker, (void *)cxt)) { + cxt->current_shippable = false; + } + // 遍历联接树中的WHERE子句(quals),检查是否可以进行流式传输 + if (stream_walker((Node*)query->jointree->quals, (void *)cxt)) { + cxt->current_shippable = false; + } } } + static void stream_walker_query_having(Query* query, shipping_context *cxt) { + // 遍历 HAVING 子句(havingQual),检查是否可以进行流式传输 if (stream_walker((Node*)query->havingQual, (void *)cxt)) { cxt->current_shippable = false; } } + static void stream_walker_query_window(Query* query, shipping_context *cxt) { + // 遍历窗口函数(WINDOW)子句,检查是否可以进行流式传输 if (expression_tree_walker((Node*)query->windowClause, (bool (*)())stream_walker, (void *)cxt)) { cxt->current_shippable = false; } } + static void stream_walker_finalize_cxt(Query* query, shipping_context *cxt) { ListCell *lc = NULL; + // 如果当前查询可以进行流式传输 if (cxt->current_shippable) { + // 遍历查询的每个表(RangeTblEntry) foreach(lc, query->rtable) { RangeTblEntry *tmp_rte = (RangeTblEntry *) lfirst(lc); + + // 如果表是子查询(RTE_SUBQUERY)且子查询不可推送(can_push = false) if (tmp_rte->rtekind == RTE_SUBQUERY && !tmp_rte->subquery->can_push) { + // 当前查询不可进行流式传输 cxt->current_shippable = false; - break; + break; // 跳出循环 } } - } else { + } + // 如果当前查询不可进行流式传输 + else { + // 如果查询的表为空(rtable为空),或者表包含关系、函数或值表达式(RTE_RELATION、RTE_FUNCTION、RTE_VALUES) if (query->rtable == NIL) { + // 当前查询不可进行流式传输 cxt->query_shippable = false; } else { + // 遍历查询的每个表(RangeTblEntry) foreach(lc, query->rtable) { RangeTblEntry *tmp_rte = (RangeTblEntry *) lfirst(lc); + + // 如果表是关系、函数或值表达式 if (tmp_rte->rtekind == RTE_RELATION || tmp_rte->rtekind == RTE_FUNCTION || tmp_rte->rtekind == RTE_VALUES) { + // 当前查询不可进行流式传输 cxt->query_shippable = false; - break; + break; // 跳出循环 } } } } } + static void stream_walker_query(Query* query, shipping_context *cxt) { - /* Set default value of query's can_push. We will modify it according to conditions below. */ + /* 设置查询的默认 can_push 属性。后续将根据以下条件修改它。 */ bool save_shippable = cxt->current_shippable; cxt->current_shippable = true; + + /* 将查询添加到查询列表中,并更新查询计数器。 */ cxt->query_list = lappend(cxt->query_list, query); cxt->query_count = cxt->query_count + 1; - stream_walker_query_update(query, cxt); - stream_walker_query_recursive(query, cxt); - stream_walker_query_distinct(query, cxt); - stream_walker_query_returning(query, cxt); - stream_walker_query_rtable(query, cxt); - stream_walker_query_exec_direct(query, cxt); - stream_walker_query_merge(query, cxt); - stream_walker_query_upsert(query, cxt); - stream_walker_query_cte(query, cxt); - stream_walker_query_limitoffset(query, cxt); - stream_walker_query_targetlist(query, cxt); - stream_walker_query_jointree(query, cxt); - stream_walker_query_having(query, cxt); - stream_walker_query_window(query, cxt); + /* 以下一系列函数调用用于检查和修改查询的可推送性属性。 */ + stream_walker_query_update(query, cxt); // 检查更新操作的条件 + stream_walker_query_recursive(query, cxt); // 检查是否包含递归查询 + stream_walker_query_distinct(query, cxt); // 检查是否包含 DISTINCT 子句 + stream_walker_query_returning(query, cxt); // 检查是否包含 RETURNING 子句 + stream_walker_query_rtable(query, cxt); // 检查查询的关系表 + stream_walker_query_exec_direct(query, cxt); // 检查是否包含 EXECUTE DIRECT 语句 + stream_walker_query_merge(query, cxt); // 检查 MERGE 操作的条件 + stream_walker_query_upsert(query, cxt); // 检查 UPSERT 操作的条件 + stream_walker_query_cte(query, cxt); // 检查公共表达式(CTE)的条件 + stream_walker_query_limitoffset(query, cxt); // 检查 LIMIT 和 OFFSET 子句 + stream_walker_query_targetlist(query, cxt); // 检查目标列表中的表达式 + stream_walker_query_jointree(query, cxt); // 检查连接树 + stream_walker_query_having(query, cxt); // 检查 HAVING 子句 + stream_walker_query_window(query, cxt); // 检查窗口函数 - stream_walker_query_insertinto_rep(query, cxt); - /* mark shippable flag based on rte shippbility */ + stream_walker_query_insertinto_rep(query, cxt); // 检查插入到复制表的条件 + + /* 根据 RangeTblEntry(表达式)的可推送性属性来标记 shippable 标志。 */ stream_walker_finalize_cxt(query, cxt); - /* Mark query's can_push and global_shippable flag. */ + /* 设置查询的 can_push 和全局可推送性标志。 */ query->can_push = cxt->current_shippable; cxt->global_shippable = cxt->global_shippable && cxt->current_shippable; + + /* 从查询列表中删除当前查询,恢复之前的可推送性属性。 */ cxt->query_list = list_delete(cxt->query_list, query); cxt->current_shippable = save_shippable; } + static void stream_walker_target_entry(TargetEntry* te, shipping_context *cxt) { + /* 检查目标条目的表达式是否包含不支持的表达式,如果包含则将可推送性标志设为 false。 */ if (contain_unsupport_expression((Node*)te->expr, (void *)cxt)) { cxt->current_shippable = false; } - /* Handle case like 'select t from t;' */ + + /* 处理类似 'select t from t;' 这样的情况,其中目标是表的情况,不支持流式传输。 */ if (IsA(te->expr, Var) && !te->resjunk && ((Var*)te->expr)->varattno == 0) { errno_t sprintf_rc = sprintf_s(u_sess->opt_cxt.not_shipping_info->not_shipping_reason, NOTPLANSHIPPING_LENGTH, @@ -457,6 +550,7 @@ static void stream_walker_target_entry(TargetEntry* te, shipping_context *cxt) cxt->current_shippable = false; } } + #ifndef ENABLE_MULTIPLE_NODES static bool vector_search_func_shippable(Oid funcid) { @@ -467,12 +561,17 @@ static void stream_walker_func_expr(FuncExpr* func, shipping_context *cxt) { uint32 i = 0; + /* + * 如果函数是可推送的函数,并且函数的参数中包含不支持的表达式, + * 则将可推送性标志设为 false。 + */ if (pgxc_is_shippable_func_contain_any(func->funcid)) { - /* the args type of concat() and concat_ws() contains ANY, that may cause unshippable */ if (contain_unsupport_expression((Node*)func->args, (void *)cxt)) { cxt->current_shippable = false; } } + + /* 如果函数不是可推送的函数,则将可推送性标志设为 false。 */ if (!pgxc_is_func_shippable(func->funcid, cxt)) { errno_t sprintf_rc = sprintf_s(u_sess->opt_cxt.not_shipping_info->not_shipping_reason, NOTPLANSHIPPING_LENGTH, @@ -481,7 +580,11 @@ static void stream_walker_func_expr(FuncExpr* func, shipping_context *cxt) securec_check_ss_c(sprintf_rc, "\0", "\0"); cxt->current_shippable = false; } - /* EC function is of record type, but we ship it in some cases */ + + /* + * 如果函数的返回类型是 RECORDOID 且不是可推送的向量搜索函数, + * 则将可推送性标志设为 false。 + */ if (func->funcid != ECEXTENSIONFUNCOID && func->funcid != ECHADOOPFUNCOID && func->funcresulttype == RECORDOID && !vector_search_func_shippable(func->funcid)) { errno_t sprintf_rc = sprintf_s(u_sess->opt_cxt.not_shipping_info->not_shipping_reason, @@ -491,6 +594,10 @@ static void stream_walker_func_expr(FuncExpr* func, shipping_context *cxt) securec_check_ss_c(sprintf_rc, "\0", "\0"); cxt->current_shippable = false; } + + /* + * 如果函数是不支持的函数之一,则将可推送性标志设为 false。 + */ for (i = 0; i < lengthof(unsupport_func); i++) { if (func->funcid == unsupport_func[i]) { errno_t sprintf_rc = sprintf_s(u_sess->opt_cxt.not_shipping_info->not_shipping_reason, @@ -502,6 +609,10 @@ static void stream_walker_func_expr(FuncExpr* func, shipping_context *cxt) } } + /* + * 如果函数是 Nextval() 函数且 'lastval_supported' 或 'enable_beta_features' 打开, + * 则将可推送性标志设为 false。 + */ if (NEXTVALFUNCOID == func->funcid && (g_instance.attr.attr_common.lastval_supported || u_sess->attr.attr_common.enable_beta_features)) { errno_t sprintf_rc = sprintf_s(u_sess->opt_cxt.not_shipping_info->not_shipping_reason, @@ -513,8 +624,13 @@ static void stream_walker_func_expr(FuncExpr* func, shipping_context *cxt) } } + static void stream_walker_aggref(Aggref* aggref, shipping_context *cxt) { + /* + * 如果聚合引用使用了不支持的聚合函数, + * 则将可推送性标志设为 false。 + */ if (contain_unsupport_function(aggref->aggfnoid)) { errno_t sprintf_rc = sprintf_s(u_sess->opt_cxt.not_shipping_info->not_shipping_reason, NOTPLANSHIPPING_LENGTH, @@ -525,18 +641,19 @@ static void stream_walker_aggref(Aggref* aggref, shipping_context *cxt) } } + static void stream_walker_coerce(CoerceViaIO* cvio, shipping_context *cxt) { - /* Query like: + /* + * 对于类似以下的查询: * select (a.*)::text from view a; - * can't be shipped, since the defination of VIEW doesn't exists on datanode. + * 无法进行推送,因为 VIEW 的定义在数据节点上不存在。 */ if (IsA(cvio->arg, Var)) { Var* var = (Var*)(cvio->arg); if (var->varattno == InvalidAttrNumber) { /* - * Sometimes Var references outer relation, we find the corresponding Query according to the - * context->query_list and the varlevelsup. + * 有时 Var 引用外部关系,我们可以根据 context->query_list 和 varlevelsup 找到相应的查询。 */ int len = list_length(cxt->query_list); int query_level = len - var->varlevelsup; @@ -555,25 +672,37 @@ static void stream_walker_coerce(CoerceViaIO* cvio, shipping_context *cxt) } } + static void inh_shipping_context(shipping_context *dst, shipping_context *src) { + /* 如果源上下文中的 current_shippable 标志为 false,将其继承到目标上下文中 */ if (!src->current_shippable) { dst->current_shippable = src->current_shippable; } + /* 如果源上下文中的 query_shippable 标志为 false,将其继承到目标上下文中 */ if (!src->query_shippable) { dst->query_shippable = src->query_shippable; } + /* 如果源上下文中的 global_shippable 标志为 false,将其继承到目标上下文中 */ if (!src->global_shippable) { dst->global_shippable = src->global_shippable; } + /* 如果源上下文中的 disable_dn_gather 标志为 true,将其继承到目标上下文中 */ if (src->disable_dn_gather) { dst->disable_dn_gather = src->disable_dn_gather; } } +/* +函数的主要功能是遍历查询的范围表,检查其中的每个范围表项, +以确定是否存在不支持的表或具有不支持特性的表。如果找到不支持的表, +将会设置 context->current_shippable 为 false,并返回 true。 +否则,返回 false 表示没有发现不支持的表。 + +*/ static bool contains_unsupport_tables(List* rtable, Query* query, shipping_context* context) { ListCell* item = NULL; @@ -584,10 +713,7 @@ static bool contains_unsupport_tables(List* rtable, Query* query, shipping_conte errno_t rc = memcpy_s(&scontext, sizeof(scontext), context, sizeof(scontext)); securec_check(rc, "\0", "\0"); - /* random func and EC func can't be shippable when it appears in CTE, - * we set context->is_randomfunc_shippable and context->is_ecfunc_shippable - * be false in stream_walker when walker in CTE. - */ + /* 如果随机函数和 EC 函数出现在 CTE 中,则不支持,我们在 stream_walker 中将它们的标志设置为 false */ scontext.is_randomfunc_shippable = u_sess->opt_cxt.is_randomfunc_shippable && context->is_randomfunc_shippable && IS_STREAM_PLAN; scontext.is_ecfunc_shippable = context->is_ecfunc_shippable && IS_STREAM_PLAN; @@ -595,20 +721,25 @@ static bool contains_unsupport_tables(List* rtable, Query* query, shipping_conte scontext.query_shippable = true; scontext.global_shippable = true; scontext.disable_dn_gather = false; + + /* 遍历查询的范围表 */ foreach (item, rtable) { RangeTblEntry* rte = (RangeTblEntry*)lfirst(item); rIdx++; switch (rte->rtekind) { case RTE_RELATION: { + /* 检查表是否包含不支持的特性 */ if (table_contain_unsupport_feature(rte->relid, query) && !u_sess->attr.attr_sql.enable_cluster_resize) { context->current_shippable = false; return true; } + /* 获取表的分布类型 */ rte->locator_type = GetLocatorType(rte->relid); - /* SQLONHADOOP has to support RROBIN MODULO distribution mode */ + + /* 对于 SQLONHADOOP 表,必须支持 RROBIN MODULO 分布模式 */ if (((rte->locator_type == LOCATOR_TYPE_RROBIN || rte->locator_type == LOCATOR_TYPE_MODULO) && rte->relkind != RELKIND_FOREIGN_TABLE && rte->relkind != RELKIND_STREAM)) { sprintf_rc = sprintf_s(u_sess->opt_cxt.not_shipping_info->not_shipping_reason, @@ -619,6 +750,8 @@ static bool contains_unsupport_tables(List* rtable, Query* query, shipping_conte context->current_shippable = false; return true; } + + /* 检查表是否具有继承关系,继承表不能被推送 */ if (rte->inh && has_subclass(rte->relid)) { sprintf_rc = sprintf_s(u_sess->opt_cxt.not_shipping_info->not_shipping_reason, NOTPLANSHIPPING_LENGTH, @@ -629,6 +762,7 @@ static bool contains_unsupport_tables(List* rtable, Query* query, shipping_conte return true; } + /* 如果是插入操作的目标表,记录其分布类型 */ if (query->commandType == CMD_INSERT && list_length(rtable) == 2 && rIdx == 1) target_table_loctype = rte->locator_type; @@ -636,15 +770,14 @@ static bool contains_unsupport_tables(List* rtable, Query* query, shipping_conte } case RTE_SUBQUERY: { /* - * We allow to push the nextval and uuid_generate_v1 to DN for the following query: - * insert into t1 select nextval('seq1'),* from t2; - * insert into t1 select uuid_generate_v1, * from t2; - * It fullfill the following conditions: - * 1. Top level query is Insert. - * 2. There are two RTE in rtable, the first one is the target table, - * which should be hash/range/list distributed. - * The second one is a subquery - * We allow the the nextval and uuid_generate_v1 in the target list of the subquery. + * 我们允许将 nextval 和 uuid_generate_v1 推送到 DN,例如下面的查询: + * insert into t1 select nextval('seq1'),* from t2; + * insert into t1 select uuid_generate_v1, * from t2; + * 这满足以下条件: + * 1. 顶级查询是 INSERT。 + * 2. 范围表中有两个 RTE,第一个是目标表,应该是哈希/范围/列表分布。 + * 第二个是子查询。 + * 我们允许在子查询的目标列表中使用 nextval 和 uuid_generate_v1。 */ bool supportLoctype = (target_table_loctype == LOCATOR_TYPE_HASH || IsLocatorDistributedBySlice(target_table_loctype) || @@ -654,8 +787,10 @@ static bool contains_unsupport_tables(List* rtable, Query* query, shipping_conte scontext.allow_func_in_targetlist = true; } + /* 递归检查子查询 */ (void)stream_walker((Node*)rte->subquery, (void*)(&scontext)); + /* 继承子查询的上下文信息 */ inh_shipping_context(context, &scontext); scontext.allow_func_in_targetlist = false; @@ -663,15 +798,19 @@ static bool contains_unsupport_tables(List* rtable, Query* query, shipping_conte break; } case RTE_FUNCTION: { + /* 递归检查函数表达式 */ (void)stream_walker((Node*)rte->funcexpr, (void*)(&scontext)); + /* 继承上下文信息 */ inh_shipping_context(context, &scontext); break; } case RTE_VALUES: { + /* 递归检查值表达式 */ (void)stream_walker((Node*)rte->values_lists, (void*)(&scontext)); + /* 继承上下文信息 */ inh_shipping_context(context, &scontext); break; @@ -685,13 +824,17 @@ static bool contains_unsupport_tables(List* rtable, Query* query, shipping_conte return false; } + static bool rel_contain_unshippable_feature(RangeTblEntry* rte, shipping_context* context, CmdType commandType) { errno_t sprintf_rc = 0; + if (rte->rtekind == RTE_RELATION) { if (commandType == CMD_INSERT) { + // 打开与范围表项关联的关系 Relation rel = relation_open(rte->relid, AccessShareLock); - /* if the result relation has oid column, go to old way */ + + // 如果目标表具有 OID 列,无法进行推送 if (rel->rd_rel->relhasoids) { relation_close(rel, AccessShareLock); sprintf_rc = sprintf_s(u_sess->opt_cxt.not_shipping_info->not_shipping_reason, @@ -701,14 +844,17 @@ static bool rel_contain_unshippable_feature(RangeTblEntry* rte, shipping_context context->current_shippable = false; return true; } + + // 关闭关系 relation_close(rel, AccessShareLock); /* - * Check if nextval and uuid_generate_v1 can be shipped to DN or not. - * We don't allow FQS for nextval and uuid_generate_v1 - * But in order to increase the performance of bulkload, we allow streaming plan - * if 1. the target table is hash/range/list distributed table - * 2. the nextval and uuid_generate_v1 function existed in the target list of the result table + * 检查是否可以将 nextval 和 uuid_generate_v1 推送到 DN。 + * 我们不允许将 FQS 用于 nextval 和 uuid_generate_v1, + * 但是为了提高批量加载的性能,我们允许流式计划, + * 如果满足以下条件: + * 1. 目标表是哈希/范围/列表分布表 + * 2. nextval 和 uuid_generate_v1 函数存在于结果表的目标列表中 */ if (rte->locator_type == LOCATOR_TYPE_HASH || IsLocatorDistributedBySlice(rte->locator_type) || rte->locator_type == LOCATOR_TYPE_NONE) { @@ -716,14 +862,16 @@ static bool rel_contain_unshippable_feature(RangeTblEntry* rte, shipping_context } } - /* Disallow volatile function shippable when the target relation is replicated. */ + /* 如果目标表是复制表,则禁止推送易失性函数 */ if (IsLocatorReplicated(rte->locator_type)) { context->disallow_volatile_func_shippable = true; } } + return false; } + /* * Attempt to check there are all deferable triggers or not, if yes try to push it to datdanodes. * Then stream_walker could refer true under constraints DEFERABLE. @@ -737,7 +885,7 @@ static bool check_trigger_deferable(Relation rel) List *indexList = (List *)RelationGetIndexList(rel); - /* no constaint then retrun true directly. */ + /* 如果没有索引,则直接返回 true */ if (indexList == NIL) { return true; } @@ -745,6 +893,7 @@ static bool check_trigger_deferable(Relation rel) foreach (item, indexList) { Oid indexoid = lfirst_oid(item); + /* 在系统缓存中查找索引元组 */ indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid)); if (!HeapTupleIsValid(indexTuple)) { ereport(ERROR, @@ -753,18 +902,22 @@ static bool check_trigger_deferable(Relation rel) } indexStruct = (Form_pg_index)GETSTRUCT(indexTuple); + + /* 检查索引是否有效,无效索引会被跳过 */ if (!IndexIsValid(indexStruct)) { deferablesCheck = true; ReleaseSysCache(indexTuple); continue; } + /* 如果索引不是立即触发的,则将 deferablesCheck 设置为 true,并释放系统缓存 */ if (!indexStruct->indimmediate) { + deferablesCheck = true; ReleaseSysCache(indexTuple); continue; } - /* if indexTuple is invalid or normal(not deferable), then cannot pushable. */ + /* 如果索引是立即触发的,将 deferablesCheck 设置为 true,并释放系统缓存 */ deferablesCheck = true; ReleaseSysCache(indexTuple); } @@ -772,19 +925,21 @@ static bool check_trigger_deferable(Relation rel) return deferablesCheck; } -static bool table_contain_unsupport_feature(Oid relid, Query* query) + +static bool table_contain_unsupport_feature(Oid relid, Query* query)//该函数用于检查指定的表是否包含不支持流式操作的特性 { Relation rel; errno_t sprintf_rc = 0; + // 尝试打开关系(表) rel = try_relation_open(relid, NoLock); if (rel != NULL) { - /* If contains system relation, we will not output the not shipping reasion */ + // 如果关系属于系统命名空间,不记录不支持的原因 if (rel->rd_rel->relnamespace == PG_CATALOG_NAMESPACE) { u_sess->opt_cxt.not_shipping_info->need_log = false; } - /* globel temp table could not ship */ + // 全局临时表不支持流操作 if (rel->rd_rel->relpersistence == RELPERSISTENCE_GLOBAL_TEMP) { sprintf_rc = sprintf_s(u_sess->opt_cxt.not_shipping_info->not_shipping_reason, NOTPLANSHIPPING_LENGTH, @@ -794,7 +949,7 @@ static bool table_contain_unsupport_feature(Oid relid, Query* query) return true; } - /* Currently dml with trigger can not get stream plan. */ + // 当前带有触发器的 DML 操作不支持流操作 if (rel->rd_rel->relhastriggers && NULL != rel->trigdesc && ((query->commandType == CMD_INSERT && pgxc_has_trigger_for_event(TRIGGER_TYPE_INSERT, rel->trigdesc)) || (query->commandType == CMD_UPDATE && check_trigger_deferable(rel) && @@ -815,19 +970,24 @@ static bool table_contain_unsupport_feature(Oid relid, Query* query) return false; } + static bool contain_unsupport_function(Oid funcId) { + // 如果函数的 Oid 大于等于 FirstNormalObjectId,表示它是用户定义的函数,不支持流式操作 if (funcId >= FirstNormalObjectId) return true; + // 遍历不支持的函数列表,如果给定的 funcId 匹配其中一个,则表示不支持流式操作 for (uint i = 0; i < lengthof(unsupport_func); i++) { if (funcId == unsupport_func[i]) return true; } + // 如果没有匹配的不支持函数,返回 false,表示支持流式操作 return false; } + static bool contain_unsupport_expression(Node* expr, void* context) { if (expr == NULL) { diff --git a/src/gausskernel/optimizer/plan/subselect.cpp b/src/gausskernel/optimizer/plan/subselect.cpp index f96e938fe..b76f6aec2 100644 --- a/src/gausskernel/optimizer/plan/subselect.cpp +++ b/src/gausskernel/optimizer/plan/subselect.cpp @@ -53,38 +53,38 @@ extern void set_path_seed_factor_zero(); typedef struct convert_testexpr_context { - PlannerInfo* root; - List* subst_nodes; /* Nodes to substitute for Params */ + PlannerInfo* root; // 指向查询规划器的信息结构的指针 + List* subst_nodes; // 用于替换 Params 的节点列表 } convert_testexpr_context; typedef struct process_sublinks_context { - PlannerInfo* root; - bool isTopQual; + PlannerInfo* root; // 指向查询规划器的信息结构的指针 + bool isTopQual; // 指示是否为顶层限定表达式的布尔值 } process_sublinks_context; typedef struct finalize_primnode_context { - PlannerInfo* root; - Bitmapset* paramids; /* Non-local PARAM_EXEC paramids found */ + PlannerInfo* root; // 指向查询规划器的信息结构的指针 + Bitmapset* paramids; // 找到的非本地 PARAM_EXEC 参数标识符的位图集合 } finalize_primnode_context; typedef struct varWalkerContext { - int maxLevelsUp; /* Keep level up of var */ + int maxLevelsUp; // 保留变量的上一级层次 } varWalkerContext; -typedef struct pull_node_clause -{ - List *nodeList; - List *nameList; - char *name; - bool recurse; - int flag; +typedef struct pull_node_clause { + List* nodeList; // 节点列表 + List* nameList; // 名称列表 + char* name; // 名称 + bool recurse; // 是否递归 + int flag; // 标志 } pull_node_clause; typedef struct push_qual_context { - int varno; /* Var no. */ - List* qual_list; /* Find qual list. */ + int varno; // 变量编号 + List* qual_list; // 查找条件列表 } push_qual_context; + /* flags bits for pull_expr_walker and pull expr_mutator */ #define PE_OPEXPR 0x01 /* pull expr of op expr */ #define PE_NULLTEST 0x02 /* pull expr of null test */ @@ -149,39 +149,43 @@ static bool contain_outer_selfref_walker(Node *node, Index *depth); static char *denominate_sublink_name(int sublink_counter) { - int nRet = 0; - char subname[SUBLINK_COUNTER]; + int nRet = 0; // 用于存储sprintf_s函数的返回值,以检查是否成功 + char subname[SUBLINK_COUNTER]; // 用于存储子查询名称的字符数组 + // 使用sprintf_s函数将子查询名称格式化为"sublink_x",其中x是子查询计数器的值 nRet = sprintf_s(subname, sizeof(subname), "sublink_%d", sublink_counter); - securec_check_ss_c(nRet, "\0", "\0"); + securec_check_ss_c(nRet, "\0", "\0"); // 检查sprintf_s函数是否成功执行 - int newlen = strlen(subname) + 1; - char *subquery_name = (char *)palloc0(newlen); - nRet = strncpy_s(subquery_name, newlen, subname, strlen(subname)); - securec_check_ss_c(nRet, "\0", "\0"); + int newlen = strlen(subname) + 1; // 计算新的字符串长度,包括null终止符 + char *subquery_name = (char *)palloc0(newlen); // 分配新的字符串内存,并初始化为0 + nRet = strncpy_s(subquery_name, newlen, subname, strlen(subname)); // 复制子查询名称到新的字符串中 + securec_check_ss_c(nRet, "\0", "\0"); // 检查strncpy_s函数是否成功执行 - return subquery_name; + return subquery_name; // 返回新创建的子查询名称 } + static bool all_replicate_table(Query *query) { - Relids varnos = get_relids_in_jointree((Node *) query->jointree, false); - int attnum = 0; + Relids varnos = get_relids_in_jointree((Node *) query->jointree, false); // 获取查询中所有关系表的变量标识符 + int attnum = 0; // 用于迭代变量标识符的整数变量 - while ((attnum = bms_first_member(varnos)) >= 0) { - RangeTblEntry *r_table = (RangeTblEntry*)rt_fetch(attnum, query->rtable); - if (r_table->rtekind == RTE_RELATION) { - if (GetLocatorType(r_table->relid) == LOCATOR_TYPE_REPLICATED) - continue; + while ((attnum = bms_first_member(varnos)) >= 0) { // 遍历变量标识符集合 + RangeTblEntry *r_table = (RangeTblEntry*)rt_fetch(attnum, query->rtable); // 获取范围表中的条目 + + if (r_table->rtekind == RTE_RELATION) { // 如果范围表条目是关系表 + if (GetLocatorType(r_table->relid) == LOCATOR_TYPE_REPLICATED) // 检查关系表是否为复制表 + continue; // 如果是复制表,继续下一次迭代 } - return false; + return false; // 如果不是复制表,返回 false } - return true; + return true; // 如果所有表都是复制表,返回 true } + /* * Select a PARAM_EXEC number to identify the given Var as a parameter for * the current subquery, or for a nestloop's inner scan. @@ -189,56 +193,61 @@ all_replicate_table(Query *query) */ static int assign_param_for_var(PlannerInfo* root, Var* var) { - ListCell* ppl = NULL; - PlannerParamItem* pitem = NULL; - Index levelsup; + ListCell* ppl = NULL; // 用于遍历参数列表的链表迭代器 + PlannerParamItem* pitem = NULL; // 用于存储参数项 + Index levelsup; // 变量所属查询级别的索引 - /* Find the query level the Var belongs to */ + /* 找到变量所属的查询级别 */ for (levelsup = var->varlevelsup; levelsup > 0; levelsup--) root = root->parent_root; - /* If there's already a matching PlannerParamItem there, just use it */ + /* 如果已经存在匹配的 PlannerParamItem,直接使用它 */ foreach (ppl, root->plan_params) { pitem = (PlannerParamItem*)lfirst(ppl); if (IsA(pitem->item, Var)) { Var* pvar = (Var*)pitem->item; /* - * This comparison must match _equalVar(), except for ignoring - * varlevelsup. Note that _equalVar() ignores the location. + * 这个比较必须与 _equalVar() 匹配,除了忽略 varlevelsup。 + * 注意,_equalVar() 忽略了位置信息。 */ if (pvar->varno == var->varno && pvar->varattno == var->varattno && pvar->vartype == var->vartype && pvar->vartypmod == var->vartypmod && pvar->varcollid == var->varcollid && pvar->varnoold == var->varnoold && pvar->varoattno == var->varoattno) - return pitem->paramId; + return pitem->paramId; // 返回现有参数标识符 } } - /* Nope, so make a new one */ - var = (Var*)copyObject(var); + /* 没有匹配的参数标识符,创建一个新的参数标识符 */ + var = (Var*)copyObject(var); // 复制变量对象,以便修改 varlevelsup 为 0 var->varlevelsup = 0; - pitem = makeNode(PlannerParamItem); - pitem->item = (Node*)var; - pitem->paramId = root->glob->nParamExec++; + pitem = makeNode(PlannerParamItem); // 创建新的 PlannerParamItem 结构 + pitem->item = (Node*)var; // 设置参数项的节点为新的 Var + pitem->paramId = root->glob->nParamExec++; // 分配新的参数标识符 - root->plan_params = lappend(root->plan_params, pitem); + root->plan_params = lappend(root->plan_params, pitem); // 将新的参数项添加到参数列表中 - return pitem->paramId; + return pitem->paramId; // 返回新分配的参数标识符 } + static Param* initParam(Var* var, int i) { - Param* retval = makeNode(Param); - retval->paramkind = PARAM_EXEC; - retval->paramid = i; - retval->paramtype = var->vartype; - retval->paramtypmod = var->vartypmod; - retval->paramcollid = var->varcollid; - retval->location = var->location; - return retval; + Param* retval = makeNode(Param); // 创建一个 Param 结构的新实例 + + // 设置 Param 结构的字段值 + retval->paramkind = PARAM_EXEC; // 参数种类为 PARAM_EXEC + retval->paramid = i; // 参数标识符 + retval->paramtype = var->vartype; // 参数的���据类型 + retval->paramtypmod = var->vartypmod; // 参数的类型修饰符 + retval->paramcollid = var->varcollid; // 参数的碰撞标识符 + retval->location = var->location; // 参数的位置信息(源代码中的位置) + + return retval; // 返回初始化后的 Param 结构 } + /* * Generate a Param node to replace the given Var, * which is expected to have varlevelsup > 0 (ie, it is not local). @@ -251,12 +260,19 @@ static Param* replace_outer_var(PlannerInfo* root, Var* var) MOD_OPT_SUBPLAN, "Replaced var should be within valid range"); - /* Find the Var in the appropriate plan_params, or add it if not present */ + /* + * 确保变量的 varlevelsup 在有效范围内。 + * 它应该大于 0(表示外部查询层级)且小于当前查询层级。 + */ + + /* 找到变量在适当的 plan_params 中,如果不存在则分配一个新的参数标识符 */ i = assign_param_for_var(root, var); + /* 初始化 Param 结构,并返回替代的参数 */ return initParam(var, i); } + /* * Generate a Param node to replace the given Var, which will be supplied * from an upper NestLoop join node. @@ -270,11 +286,19 @@ Param* assign_nestloop_param_var(PlannerInfo* root, Var* var) AssertEreport(var->varlevelsup == 0, MOD_OPT_SUBPLAN, "Replaced var should be at current level"); + /* + * 确保变量的 varlevelsup 等于 0, + * 这意味着要替换的变量必须位于当前查询层级。 + */ + + /* 找到变量在适当的 plan_params 中,如果不存在则分配一个新的参数标识符 */ i = assign_param_for_var(root, var); + /* 初始化 Param 结构,并返回替代的参数 */ return initParam(var, i); } + /* * @@GaussDB@@ * Target : data partition @@ -286,19 +310,21 @@ Param* assign_nestloop_param_var(PlannerInfo* root, Var* var) */ int assignPartIteratorParam(PlannerInfo* root) { - PlannerParamItem* pitem = NULL; - Node* itrParam = NULL; /* just for specifying value to PlannerParamItem.item, represent nothing. */ + PlannerParamItem* pitem = NULL; // 用于存储参数项的指针 + Node* itrParam = NULL; // 用于表示参数值的节点,不代表具体值 - itrParam = (Node*)palloc(sizeof(Node)); - pitem = makeNode(PlannerParamItem); + itrParam = (Node*)palloc(sizeof(Node)); // 分配一个新的节点(通常用于表示参数值) + pitem = makeNode(PlannerParamItem); // 创建一个新的 PlannerParamItem 结构 - pitem->item = (Node*)itrParam; - pitem->paramId = SS_assign_special_param(root); - root->plan_params = lappend(root->plan_params, pitem); + pitem->item = (Node*)itrParam; // 将参数项的节点字段设置为新的节点 + pitem->paramId = SS_assign_special_param(root); // 分配一个特殊参数标识符(paramId) - return pitem->paramId; + root->plan_params = lappend(root->plan_params, pitem); // 将参数项添加到查询规划器的参数列表中 + + return pitem->paramId; // 返回分配的参数标识符 } + /* * Select a PARAM_EXEC number to identify the given PlaceHolderVar as a * parameter for the current subquery, or for a nestloop's inner scan. @@ -308,54 +334,59 @@ int assignPartIteratorParam(PlannerInfo* root) */ static int assign_param_for_placeholdervar(PlannerInfo* root, PlaceHolderVar* phv) { - ListCell* ppl = NULL; - PlannerParamItem* pitem = NULL; + ListCell* ppl = NULL; // 用于遍历参数列表的链表迭代器 + PlannerParamItem* pitem = NULL; // 用于存储参数项 Index levelsup; - /* Find the query level the PHV belongs to */ + /* 找到 PHV 属于的查询级别 */ for (levelsup = phv->phlevelsup; levelsup > 0; levelsup--) root = root->parent_root; - /* If there's already a matching PlannerParamItem there, just use it */ + /* 如果已经存在匹配的 PlannerParamItem,直接使用它 */ foreach (ppl, root->plan_params) { pitem = (PlannerParamItem*)lfirst(ppl); if (IsA(pitem->item, PlaceHolderVar)) { PlaceHolderVar* pphv = (PlaceHolderVar*)pitem->item; - /* We assume comparing the PHIDs is sufficient */ + /* 我们假设比较 PHID(占位符的唯一标识符)足够了 */ if (pphv->phid == phv->phid) - return pitem->paramId; + return pitem->paramId; // 返回现有参数标识符 } } - /* Nope, so make a new one */ - phv = (PlaceHolderVar*)copyObject(phv); + /* 没有匹配的参数标识符,创建一个新的参数标识符 */ + phv = (PlaceHolderVar*)copyObject(phv); // 复制占位符变量对象 if (phv->phlevelsup != 0) { IncrementVarSublevelsUp((Node*)phv, -((int)phv->phlevelsup), 0); AssertEreport(phv->phlevelsup == 0, MOD_OPT_SUBPLAN, "Placeholder var should be at current level"); } - pitem = makeNode(PlannerParamItem); - pitem->item = (Node*)phv; - pitem->paramId = root->glob->nParamExec++; + pitem = makeNode(PlannerParamItem); // 创建新的 PlannerParamItem 结构 + pitem->item = (Node*)phv; // 设置参数项的节点为新的占位符变量 + pitem->paramId = root->glob->nParamExec++; // 分配新的参数标识符 - root->plan_params = lappend(root->plan_params, pitem); + root->plan_params = lappend(root->plan_params, pitem); // 将新的参数项添加到参数列表中 - return pitem->paramId; + return pitem->paramId; // 返回新分配的参数标识符 } + static Param* getParamPHV(PlaceHolderVar* phv, int i) { - Param* retval = makeNode(Param); - retval->paramkind = PARAM_EXEC; - retval->paramid = i; - retval->paramtype = exprType((Node*)phv->phexpr); - retval->paramtypmod = exprTypmod((Node*)phv->phexpr); - retval->paramcollid = exprCollation((Node*)phv->phexpr); - retval->location = -1; - return retval; + Param* retval = makeNode(Param); // 创建一个 Param 结构的新实例 + + // 设置 Param 结构的字段值 + retval->paramkind = PARAM_EXEC; // 参数种类为 PARAM_EXEC + retval->paramid = i; // 参数标识符 + retval->paramtype = exprType((Node*)phv->phexpr); // 参数的数据类型 + retval->paramtypmod = exprTypmod((Node*)phv->phexpr); // 参数的类型修饰符 + retval->paramcollid = exprCollation((Node*)phv->phexpr); // 参数的碰撞标识符 + retval->location = -1; // 参数的位置信息设置为 -1(未知位置) + + return retval; // 返回初始化后的 Param 结构 } + /* * Generate a Param node to replace the given PlaceHolderVar, * which is expected to have phlevelsup > 0 (ie, it is not local). @@ -370,12 +401,19 @@ static Param* replace_outer_placeholdervar(PlannerInfo* root, PlaceHolderVar* ph MOD_OPT_SUBPLAN, "Placeholder var should be within valid range"); - /* Find the PHV in the appropriate plan_params, or add it if not present */ + /* + * 确保占位符变量的 phlevelsup 在有效范围内。 + * 它应该大于 0(表示外部查询层级)且小于当前查询层级。 + */ + + /* 找到占位符变量在适当的 plan_params 中,如果不存在则分配一个新的参数标识符 */ i = assign_param_for_placeholdervar(root, phv); + /* 获取替代的参数并返回 */ return getParamPHV(phv, i); } + /* * Generate a Param node to replace the given PlaceHolderVar, which will be * supplied from an upper NestLoop join node. @@ -388,32 +426,45 @@ Param* assign_nestloop_param_placeholdervar(PlannerInfo* root, PlaceHolderVar* p AssertEreport(phv->phlevelsup == 0, MOD_OPT_SUBPLAN, "Placeholder var should be at current level"); + /* + * 确保占位符变量的 phlevelsup 等于 0, + * 这意味着要替换的占位符变量必须位于当前查询层级。 + */ + + /* 找到占位符变量在适当的 plan_params 中,如果不存在则分配一个新的参数标识符 */ i = assign_param_for_placeholdervar(root, phv); + /* 获取替代的参数并返回 */ return getParamPHV(phv, i); } + /* * Generate a Param node to replace the given Aggref * which is expected to have agglevelsup > 0 (ie, it is not local). */ static Param* replace_outer_agg(PlannerInfo* root, Aggref* agg) { - Param* retval = NULL; - PlannerParamItem* pitem = NULL; + Param* retval = NULL; // 用于存储结果参数 + PlannerParamItem* pitem = NULL; // 用于存储参数项 Index levelsup; AssertEreport(agg->agglevelsup > 0 && agg->agglevelsup < root->query_level, MOD_OPT_SUBPLAN, "Agg expr should be within valid range"); - /* Find the query level the Aggref belongs to */ + /* + * 确保聚合表达式的 agglevelsup 在有效范围内。 + * 它应该大于 0(表示外部查询层级)且小于当前查询层级。 + */ + + /* 找到聚合表达式在适当的 plan_params 中,如果不存在则分配一个新的参数标识符 */ for (levelsup = agg->agglevelsup; levelsup > 0; levelsup--) root = root->parent_root; /* - * It does not seem worthwhile to try to match duplicate outer aggs. Just - * make a new slot every time. + * 看起来不值得尝试匹配重复的外部聚合表达式。 + * 每次都创建一个新的参数。 */ agg = (Aggref*)copyObject(agg); IncrementVarSublevelsUp((Node*)agg, -((int)agg->agglevelsup), 0); @@ -433,30 +484,36 @@ static Param* replace_outer_agg(PlannerInfo* root, Aggref* agg) retval->paramcollid = agg->aggcollid; retval->location = agg->location; - return retval; + return retval; // 返回初始化后的 Param 结构 } + /* * Generate a Param node to replace the given GroupingFunc expression which is * expected to have agglevelsup > 0 (ie, it is not local). */ static Param* replace_outer_grouping(PlannerInfo* root, GroupingFunc* grp) { - Param* retval = NULL; - PlannerParamItem* pitem = NULL; + Param* retval = NULL; // 用于存储结果参数 + PlannerParamItem* pitem = NULL; // 用于存储参数项 Index levelsup; AssertEreport(grp->agglevelsup > 0 && grp->agglevelsup < root->query_level, MOD_OPT_SUBPLAN, "Grouping expr should be within valid range"); - /* Find the query level the GroupingFunc belongs to */ + /* + * 确保 GroupingFunc 表达式的 agglevelsup 在有效范围内。 + * 它应该大于 0(表示外部查询层级)且小于当前查询层级。 + */ + + /* 找到 GroupingFunc 表达式在适当的 plan_params 中,如果不存在则分配一个新的参数标识符 */ for (levelsup = grp->agglevelsup; levelsup > 0; levelsup--) root = root->parent_root; /* - * It does not seem worthwhile to try to match duplicate outer aggs. Just - * make a new slot every time. + * 看起来不值得尝试匹配重复的外部 GroupingFunc 表达式。 + * 每次都创建一个新的参数。 */ grp = (GroupingFunc*)copyObject(grp); IncrementVarSublevelsUp((Node*)grp, -((int)grp->agglevelsup), 0); @@ -476,9 +533,10 @@ static Param* replace_outer_grouping(PlannerInfo* root, GroupingFunc* grp) retval->paramcollid = InvalidOid; retval->location = grp->location; - return retval; + return retval; // 返回初始化后的 Param 结构 } + /* * Generate a new Param node that will not conflict with any other. * @@ -488,19 +546,21 @@ static Param* replace_outer_grouping(PlannerInfo* root, GroupingFunc* grp) */ static Param* generate_new_param(PlannerInfo* root, Oid paramtype, int32 paramtypmod, Oid paramcollation) { - Param* retval = NULL; + Param* retval = NULL; // 用于存储结果参数 + // 创建一个新的 Param 结构 retval = makeNode(Param); - retval->paramkind = PARAM_EXEC; - retval->paramid = root->glob->nParamExec++; - retval->paramtype = paramtype; - retval->paramtypmod = paramtypmod; - retval->paramcollid = paramcollation; - retval->location = -1; + retval->paramkind = PARAM_EXEC; // 参数种类为 PARAM_EXEC + retval->paramid = root->glob->nParamExec++; // 分配一个新的参数标识符 + retval->paramtype = paramtype; // 参数的数据类型 + retval->paramtypmod = paramtypmod; // 参数的类型修饰符 + retval->paramcollid = paramcollation; // 参数的碰撞标识符 + retval->location = -1; // 参数的位置信息设置为 -1(未知位置) - return retval; + return retval; // 返回初始化后的 Param 结构 } + /* * Assign a (nonnegative) PARAM_EXEC ID for a special parameter (one that * is not actually used to carry a value at runtime). Such parameters are @@ -525,23 +585,28 @@ int SS_assign_special_param(PlannerInfo* root) */ static void get_first_col_type(Plan* plan, Oid* coltype, int32* coltypmod, Oid* colcollation) { - /* In cases such as EXISTS, tlist might be empty; arbitrarily use VOID */ + /* 在某些情况下,如 EXISTS,tlist 可能为空;随意使用 VOID 数据类型 */ if (plan->targetlist) { TargetEntry* tent = (TargetEntry*)linitial(plan->targetlist); AssertEreport(IsA(tent, TargetEntry), MOD_OPT_SUBPLAN, "Element of targetlist should be TargetEntry"); + if (!tent->resjunk) { + // 获取第一个非 junk 目标条目的数据类型、类型修饰符和碰撞标识符 *coltype = exprType((Node*)tent->expr); *coltypmod = exprTypmod((Node*)tent->expr); *colcollation = exprCollation((Node*)tent->expr); return; } } + + // 如果没有非 junk 目标条目,将数据类型设置为 VOIDOID,类型修饰符设置为 -1,碰撞标识符设置为 InvalidOid *coltype = VOIDOID; *coltypmod = -1; *colcollation = InvalidOid; } + /* * Description: subquery is initplan or subplan. * Parameters: @@ -551,18 +616,22 @@ static void get_first_col_type(Plan* plan, Oid* coltype, int32* coltypmod, Oid* */ static bool IsInitPlan(List* plan_params, SubLinkType subLinkType) { + // 如果计划参数列表不为空,则不是初始化计划 if (plan_params != NIL) { return false; } + // 如果子查询类型为 EXISTS_SUBLINK、EXPR_SUBLINK、ARRAY_SUBLINK 或 ROWCOMPARE_SUBLINK,则是初始化计划 if (subLinkType == EXISTS_SUBLINK || subLinkType == EXPR_SUBLINK || subLinkType == ARRAY_SUBLINK || subLinkType == ROWCOMPARE_SUBLINK) { return true; } + // 否则不是初始化计划 return false; } + /* * Convert a SubLink (as created by the parser) into a SubPlan. * @@ -980,55 +1049,85 @@ static Node* build_subplan(PlannerInfo* root, Plan* plan, PlannerInfo* subroot, * * We also return an integer list of the paramids of the Params. */ + /* +这个函数用于生成子查询中的参数列表,根据传递给函数的目标条目列表 tlist, +遍历列表中的每个目标条目,生成对应的参数,并将参数添加到 result 列表中。 +同时,将生成参数的标识符列表存储在 ids 列表中。 +最后,通过 paramIds 指针返回生成参数的标识符列表,函数返回生成的参数列表。 +这个函数通常用于为子查询生成参数,以便在执行子查询时传递参数值。 + */ static List* generate_subquery_params(PlannerInfo* root, List* tlist, List** paramIds) { - List* result = NIL; - List* ids = NIL; - ListCell* lc = NULL; + List* result = NIL; // 用于存储生成的参数列表 + List* ids = NIL; // 用于存储生成参数的标识符列表 + ListCell* lc = NULL; // 用于遍历目标条目列表 + // 初始化 result 和 ids 为空列表 result = ids = NIL; + + // 遍历目标条目列表 foreach (lc, tlist) { TargetEntry* tent = (TargetEntry*)lfirst(lc); Param* param = NULL; + // 跳过 resjunk(不需要的目标条目) if (tent->resjunk) continue; + // 生成一个新的参数(Param)并添加到 result 列表中 param = generate_new_param( root, exprType((Node*)tent->expr), exprTypmod((Node*)tent->expr), exprCollation((Node*)tent->expr)); result = lappend(result, param); + + // 将生成的参数的标识符添加到 ids 列表中 ids = lappend_int(ids, param->paramid); } + // 将生成参数的标识符列表传递给 paramIds 指针 *paramIds = ids; + + // 返回生成的参数列表 return result; } + /* * generate_subquery_vars: build a list of Vars representing the output * columns of a sublink's sub-select, given the sub-select's targetlist. * The Vars have the specified varno (RTE index). */ + /* +这个函数用于生成子查询中的变量列表,根据传递给函数的目标条目列表 tlist 和变量的标识符 varno, +遍历列表中的每个目标条目,从每个目标条目生成对应的变量,并将变量添加到 result 列表中。 +最后,函数返回生成的变量列表。这个函数通常用于为子查询生成变量,以便在执行子查询时引用这些变量。 + */ static List* generate_subquery_vars(PlannerInfo* root, List* tlist, Index varno) { - List* result = NIL; - ListCell* lc = NULL; + List* result = NIL; // 用于存储生成的变量列表 + ListCell* lc = NULL; // 用于遍历目标条目列表 + // 初始化 result 为空列表 result = NIL; + + // 遍历目标条目列表 foreach (lc, tlist) { TargetEntry* tent = (TargetEntry*)lfirst(lc); Var* var = NULL; + // 跳过 resjunk(不需要的目标条目) if (tent->resjunk) continue; + // 从目标条目生成一个新的变量(Var)并添加到 result 列表中 var = makeVarFromTargetEntry(varno, tent); result = lappend(result, var); } + // 返回生成的变量列表 return result; } + /* * convert_testexpr: convert the testexpr given by the parser into * actually executable form. This entails replacing PARAM_SUBLINK Params @@ -1045,19 +1144,28 @@ static Node* convert_testexpr(PlannerInfo* root, Node* testexpr, List* subst_nod { convert_testexpr_context context; - context.root = root; - context.subst_nodes = subst_nodes; + // 创建测试表达式转换的上下文 + context.root = root; // 传递 PlannerInfo 上下文 + context.subst_nodes = subst_nodes; // 传递替换节点的列表 + + // 调用 convert_testexpr_mutator 函数进行实际的表达式转换 return convert_testexpr_mutator(testexpr, &context); } + static Node* convert_testexpr_mutator(Node* node, convert_testexpr_context* context) { + // 如果节点为空,则返回空节点 if (node == NULL) return NULL; + + // 如果节点是 Param 类型 if (IsA(node, Param)) { Param* param = (Param*)node; + // 检查参数的类型是否是 PARAM_SUBLINK if (param->paramkind == PARAM_SUBLINK) { + // 检查参数的标识符是否有效 if (param->paramid <= 0 || param->paramid > list_length(context->subst_nodes)) ereport(ERROR, (errmodule(MOD_OPT), @@ -1065,83 +1173,82 @@ static Node* convert_testexpr_mutator(Node* node, convert_testexpr_context* cont errmsg("unexpected PARAM_SUBLINK ID: %d", param->paramid))); /* - * We copy the list item to avoid having doubly-linked - * substructure in the modified parse tree. This is probably - * unnecessary when it's a Param, but be safe. + * 返回与 PARAM_SUBLINK 对应的替换节点。我们复制列表项,以避免在修改后的解析树中具有双向链接的子结构。 + * 这在它是 Param 时可能是不必要的,但为了安全起见,我们进行复制。 */ return (Node*)copyObject(list_nth(context->subst_nodes, param->paramid - 1)); } } + // 如果节点是 SubLink 类型,直接返回,不需要进一步处理 if (IsA(node, SubLink)) { - /* - * If we come across a nested SubLink, it is neither necessary nor - * correct to recurse into it: any PARAM_SUBLINKs we might find inside - * belong to the inner SubLink not the outer. So just return it as-is. - * - * This reasoning depends on the assumption that nothing will pull - * subexpressions into or out of the testexpr field of a SubLink, at - * least not without replacing PARAM_SUBLINKs first. If we did want - * to do that we'd need to rethink the parser-output representation - * altogether, since currently PARAM_SUBLINKs are only unique per - * SubLink not globally across the query. The whole point of - * replacing them with Vars or PARAM_EXEC nodes is to make them - * globally unique before they escape from the SubLink's testexpr. - * - * Note: this can't happen when called during SS_process_sublinks, - * because that recursively processes inner SubLinks first. It can - * happen when called from convert_ANY_sublink_to_join, though. - */ return node; } + + // 对于其他类型的节点,递归调用 expression_tree_mutator 处理子节点 return expression_tree_mutator(node, (Node* (*)(Node*, void*)) convert_testexpr_mutator, (void*)context); } /* * subplan_is_hashable: can we implement an ANY subplan by hashing? */ + /* +这个函数主要用于评估子查询的结果大小是否适合于在工作内存 (work_mem) 中进行哈希操作。 +它首先计算子查询结果的预估大小,然后将其与工作内存限制进行比较。 +如果子查询结果的大小超过工作内存限制,函数返回 false,表示无法进行哈希操作。 +否则,返回 true,表示可以进行哈希操作。这个函数通常在查询规划阶段使用,用于决定是否使用哈希算法来处理子查询的结果。 + */ static bool subplan_is_hashable(Plan* plan) { double subquery_size; /* - * The estimated size of the subquery result must fit in work_mem. (Note: - * we use sizeof(HeapTupleHeaderData) here even though the tuples will - * actually be stored as MinimalTuples; this provides some fudge factor - * for hashtable overhead.) + * 子查询结果的预估大小必须适合于工作内存(work_mem)。 + * (注意:这里使用 sizeof(HeapTupleHeaderData) 来估算,即使实际上元组将以 MinimalTuples 存储; + * 这为散列表开销提供了一些余地。) */ subquery_size = plan->plan_rows * (MAXALIGN(plan->plan_width) + MAXALIGN(sizeof(HeapTupleHeaderData))); + + // 如果子查询结果的大小超过工作内存(work_mem)限制,则无法进行哈希操作 if (subquery_size > u_sess->attr.attr_memory.work_mem * 1024L) return false; + // 可以进行哈希操作 return true; } + /* * testexpr_is_hashable: is an ANY SubLink's test expression hashable? */ + /* +这个函数用于检查测试表达式是否适合在哈希操作中使用。 +要满足条件,测试表达式必须是一个单一的 OpExpr,或者是一个仅包含 OpExpr 的 AND 子句。 +此外,其中涉及的操作符必须是可哈希的并且是严格的(strict)。 +这是因为哈希操作需要可哈希的操作符以及不会对非 NULL 输入产生 NULL 结果的操作符。 +这个函数通常在查询规划阶段使用,以确定是否可以使用哈希操作来处理测试表达式。 + */ static bool testexpr_is_hashable(Node* testexpr) { /* - * The testexpr must be a single OpExpr, or an AND-clause containing only - * OpExprs. + * 测试表达式必须是单个 OpExpr,或者是只包含 OpExpr 的 AND 子句。 * - * The combining operators must be hashable and strict. The need for - * hashability is obvious, since we want to use hashing. Without - * strictness, behavior in the presence of nulls is too unpredictable. We - * actually must assume even more than plain strictness: they can't yield - * NULL for non-null inputs, either (see nodeSubplan.c). However, hash - * indexes and hash joins assume that too. + * 组合操作符必须是可哈希的并且是严格的。哈希性是显而易见的,因为我们希望使用哈希。 + * 没有严格性,在存在 NULL 的情况下,行为太不可预测了。实际上,我们需要比纯粹的严格性更多的假设: + * 它们不能为非 NULL 的输入产生 NULL(请参见 nodeSubplan.c)。但是,哈希索引和哈希连接也假设如此。 */ if (testexpr && IsA(testexpr, OpExpr)) { + // 如果测试表达式是单个 OpExpr,则检查该操作符是否可哈希 if (hash_ok_operator((OpExpr*)testexpr)) return true; } else if (and_clause(testexpr)) { + // 如果测试表达式是 AND 子句,则遍历每个 AND 子句 ListCell* l = NULL; foreach (l, ((BoolExpr*)testexpr)->args) { Node* andarg = (Node*)lfirst(l); + // 检查每个子表达式是否是 OpExpr,并且操作符是否可哈希 if (!IsA(andarg, OpExpr)) return false; if (!hash_ok_operator((OpExpr*)andarg)) @@ -1150,9 +1257,16 @@ static bool testexpr_is_hashable(Node* testexpr) return true; } + // 无法进行哈希操作 return false; } + +/* +这个函数用于将连接条件(joinqual)转换为反连接条件(antiqual),以用于反连接操作。 +函数根据连接条件的类型进行不同的处理,支持 OpExpr 和 BoolExpr 类型的连接条件, +并将它们转换为相应的反连接条件。这个函数通常在查询规划阶段用于构建反连接操作所需的条件表达式。 +*/ static Node* convert_joinqual_to_antiqual(Node* node, Query* parse) { Node* antiqual = NULL; @@ -1162,10 +1276,11 @@ static Node* convert_joinqual_to_antiqual(Node* node, Query* parse) switch (nodeTag(node)) { case T_OpExpr: + // 如果节点是 OpExpr,则将其转换为反连接条件 antiqual = convert_opexpr_to_boolexpr_for_antijoin(node, parse); break; case T_BoolExpr: { - /*Not IN, should be and clause.*/ + /* 不是 IN 连接条件,应该是 AND 子句。*/ if (and_clause(node)) { BoolExpr* boolexpr = (BoolExpr*)node; List* andarglist = NIL; @@ -1175,7 +1290,7 @@ static Node* convert_joinqual_to_antiqual(Node* node, Query* parse) Node* andarg = (Node*)lfirst(l); Node* expr = NULL; - /* The listcell type of args should be OpExpr. */ + /* args 的列表单元格类型应该是 OpExpr。 */ expr = convert_opexpr_to_boolexpr_for_antijoin(andarg, parse); if (expr == NULL) return NULL; @@ -1183,6 +1298,7 @@ static Node* convert_joinqual_to_antiqual(Node* node, Query* parse) andarglist = lappend(andarglist, expr); } + // 创建一个新的 BoolExpr,将所有 AND 子句连接起来 antiqual = (Node*)makeBoolExpr(AND_EXPR, andarglist, boolexpr->location); } else return NULL; @@ -1198,6 +1314,12 @@ static Node* convert_joinqual_to_antiqual(Node* node, Query* parse) return antiqual; } +/* +这个函数的主要任务是将 OpExpr 转换为 BoolExpr,以用于反连接操作。 +它检查 OpExpr 的左右操作数是否可为 NULL,如果是,则为每个操作数添加一个 IS NULL 测试条件, +并将它们用 OR 连接起来。最后,它返回生成的 BoolExpr 或保持 OpExpr 不变, +具体取决于是否需要添加额外的测试条件。这个函数通常在查询规划阶段用于构建反连接操作所需的条件表达式。 +*/ static Node* convert_opexpr_to_boolexpr_for_antijoin(Node* node, Query* parse) { Node* boolexpr = NULL; @@ -1209,20 +1331,27 @@ static Node* convert_opexpr_to_boolexpr_for_antijoin(Node* node, Query* parse) else opexpr = (OpExpr*)node; + // 创建一个包含 OpExpr 的列表 antiqual = (List*)list_make1(opexpr); + // 处理 OpExpr 的左操作数 Node* larg = (Node*)linitial(opexpr->args); if (IsA(larg, RelabelType)) larg = (Node*)((RelabelType*)larg)->arg; + + // 如果左操作数可为 NULL,则添加一个 IS NULL 测试 if (!check_var_nonnullable(parse, larg)) antiqual = lappend(antiqual, makeNullTest(IS_NULL, (Expr*)copyObject(larg))); + // 处理 OpExpr 的右操作数,类似地添加 IS NULL 测试 Node* rarg = (Node*)lsecond(opexpr->args); if (IsA(rarg, RelabelType)) rarg = (Node*)((RelabelType*)rarg)->arg; + if (!check_var_nonnullable(parse, rarg)) antiqual = lappend(antiqual, makeNullTest(IS_NULL, (Expr*)copyObject(rarg))); + // 如果生成了多个测试条件,则将它们用 OR 连接,否则保持 OpExpr 不变 if (list_length(antiqual) > 1) boolexpr = (Node*)makeBoolExprTreeNode(OR_EXPR, antiqual); else @@ -1231,6 +1360,7 @@ static Node* convert_opexpr_to_boolexpr_for_antijoin(Node* node, Query* parse) return boolexpr; } + /* * check_var_nonnullable * check if the node is nullable diff --git a/src/gausskernel/optimizer/prep/prepjointree.cpp b/src/gausskernel/optimizer/prep/prepjointree.cpp old mode 100755 new mode 100644 index 2187138e8..346739557 --- a/src/gausskernel/optimizer/prep/prepjointree.cpp +++ b/src/gausskernel/optimizer/prep/prepjointree.cpp @@ -127,35 +127,42 @@ static bool contains_swctes(const PlannerInfo *root); * Unlike most other functions in this file, this function doesn't recurse; * we rely on other processing to invoke it on sub-queries at suitable times. */ + /* +函数主要功能是在查询解析树中检查联接树是否为空,如果为空, +则创建一个特殊的范围表条目,然后将其添加到查询解析树的范围表中, +并在联接树中插入一个对这个新范围表条目的引用。这通常用于处理一些特殊情况, +以确保查询解析树的正确性。如果联接树已经非空或者在SET操作的顶层,就不做任何修改。 + */ void replace_empty_jointree(Query *parse) { - RangeTblEntry *rte; - Index rti; - RangeTblRef *rtr; + RangeTblEntry *rte; // 声明一个RangeTblEntry结构体指针,用于表示查询范围表条目 + Index rti; // 声明一个索引,用于表示范围表条目的索引 + RangeTblRef *rtr; // 声明一个RangeTblRef结构体指针,用于表示范围表引用 - /* Nothing to do if jointree is already nonempty */ + /* 如果联接树(jointree)已经不为空,就不需要做任何操作 */ if (parse->jointree->fromlist != NIL) return; - /* We mustn't change it in the top level of a setop tree, either */ + /* 如果在SET操作的顶层,我们也不能更改它 */ if (parse->setOperations) return; - /* Create suitable RTE */ - rte = makeNode(RangeTblEntry); - rte->rtekind = RTE_RESULT; - rte->eref = makeAlias("*RESULT*", NIL); + /* 创建适当的范围表条目(RangeTblEntry) */ + rte = makeNode(RangeTblEntry); // 创建一个新的范围表条目 + rte->rtekind = RTE_RESULT; // 设置范围表条目的类型为RTE_RESULT + rte->eref = makeAlias("*RESULT*", NIL); // 为范围表条目创建一个别名为"*RESULT*" - /* Add it to rangetable */ - parse->rtable = lappend(parse->rtable, rte); - rti = list_length(parse->rtable); + /* 将范围表条目添加到范围表中 */ + parse->rtable = lappend(parse->rtable, rte); // 将新创建的范围表条目添加到查询解析树的范围表中 + rti = list_length(parse->rtable); // 获取新添加的范围表条目的索引 - /* And jam a reference into the jointree */ - rtr = makeNode(RangeTblRef); - rtr->rtindex = rti; - parse->jointree->fromlist = list_make1(rtr); + /* 并且将一个范围表引用插入到联接树中 */ + rtr = makeNode(RangeTblRef); // 创建一个新的范围表引用 + rtr->rtindex = rti; // 设置范围表引用的索引为新添加的范围表条目的索引 + parse->jointree->fromlist = list_make1(rtr); // 将范围表引用添加到查询解析树的联接树中 } + #ifndef ENABLE_MULTIPLE_NODES /* * helper function to check if SWCB ctes contaisn in current SubQuery, normally help us to @@ -163,25 +170,29 @@ void replace_empty_jointree(Query *parse) */ static bool contains_swctes(const PlannerInfo *root) { + // 如果查询解析树为空或者其中的CTE列表为空,返回false if (root->parse == NULL || root->parse->cteList == NIL) { return false; } - List *cteList = root->parse->cteList; - ListCell *lc = NULL; - bool found = false; - foreach(lc, cteList) { - CommonTableExpr *cte = (CommonTableExpr *)lfirst(lc); + List *cteList = root->parse->cteList; // 获取查询解析树中的CTE列表 + ListCell *lc = NULL; // 声明一个用于遍历列表的列表元素指针 + bool found = false; // 声明一个布尔值,用于表示是否找到特定类型的CTE - /* check if cte from parse->ctelist is a swcb converted */ + // 遍历CTE列表 + foreach(lc, cteList) { + CommonTableExpr *cte = (CommonTableExpr *)lfirst(lc); // 获取当前CTE + + /* 检查parse->ctelist中的CTE是否是swcb转换的 */ if (cte->swoptions != NULL) { - found = true; - break; + found = true; // 如果CTE中包含swoptions,将found设置为true + break; // 跳出循环,因为已经找到了符合条件的CTE } } - return found; + return found; // 返回是否找到特定类型的CTE的结果 } + #endif /* @@ -277,35 +288,42 @@ void pull_up_sublinks(PlannerInfo* root) */ Node* assign_qual_clause(Node* new_node, Node* old_node, Node* qual, Relids old_node_relids) { + // 如果限定条件(qual)为空,则直接返回新节点(new_node),无需处理 if (qual == NULL) { return new_node; } + + // 从限定条件中获取相关的变量编号(varnos) Relids qual_varnos = pull_varnos(qual); + /* - * We need add this quals to new_node if old_node_relids can not include qual_varnos. - * than can happend when or_clause pull up. + * 如果限定条件中的变量编号(qual_varnos)不能完全包含在旧节点的关系标识集合(old_node_relids)中, + * 则需要将限定条件添加到新节点(new_node)中。 + * 这种情况可能会发生在OR条件的上升处理中。 */ if (!bms_is_subset(qual_varnos, old_node_relids)) { if (IsA(new_node, FromExpr)) { - ((FromExpr*)new_node)->quals = qual; + ((FromExpr*)new_node)->quals = qual; // 将限定条件设置为新节点的条件 } else { - new_node = (Node*)makeFromExpr(list_make1(new_node), qual); + new_node = (Node*)makeFromExpr(list_make1(new_node), qual); // 创建一个新的FromExpr节点,并将限定条件添加到其中 } } - /* Only need put qual to old node, this qual can be original(pull up before) qual.*/ + /* 否则,只需要将限定条件放在旧节点上,这个限定条件可能是原始的(在上升之前已经存在的)限定条件。 */ else { if (IsA(old_node, FromExpr)) { - ((FromExpr*)old_node)->quals = qual; + ((FromExpr*)old_node)->quals = qual; // 将限定条件设置为旧节点的条件 } else { - ((JoinExpr*)old_node)->quals = qual; + ((JoinExpr*)old_node)->quals = qual; // 将限定条件设置为联接节点的条件 } } + // 释放变量编号集合的内存 bms_free_ext(qual_varnos); - return new_node; + return new_node; // 返回处理后的新节点 } + /* * Recurse through jointree nodes for pull_up_sublinks() * @@ -506,12 +524,12 @@ static Node* pull_up_sublinks_jointree_recurse(PlannerInfo* root, Node* jtnode, static Node* pull_up_sublinks_qual_recurse(PlannerInfo* root, Node* node, Node** jtlink1, Relids *available_rels1, Node** jtlink2, Relids *available_rels2, Node* all_quals) { - if (node == NULL) + if (node == NULL)// 如果传入的节点为空,直接返回NULL return NULL; - if (IsA(node, SubLink)) { - SubLink* sublink = (SubLink*)node; - JoinExpr* j = NULL; - Relids child_rels; + if (IsA(node, SubLink)) {// 如果节点是一个子查询(SubLink) + SubLink* sublink = (SubLink*)node;// 将节点转换为子查询结构体 + JoinExpr* j = NULL;// 声明一个JoinExpr结构体指针,用于表示连接表达式 + Relids child_rels;// 用于存储子查询关联的关系标识集合 if (has_no_expand_hint((Query*)sublink->subselect)) { return node; @@ -685,12 +703,13 @@ static Node* pull_up_sublinks_qual_recurse(PlannerInfo* root, Node* node, Node** ListCell* l = NULL; foreach (l, ((BoolExpr*)node)->args) { - Node* oldclause = (Node*)lfirst(l); - Node* newclause = NULL; - + Node* oldclause = (Node*)lfirst(l);// 获取当前参数作为旧的子句 + Node* newclause = NULL;// 用于存储处理后的新子句 + // 调用递归函数处理当前子句 newclause = pull_up_sublinks_qual_recurse( root, oldclause, jtlink1, available_rels1, jtlink2, available_rels2, all_quals); - if (newclause != NULL) + // 如果处理后的新子句不为空 + if (newclause != NULL)// 将新子句添加到新子句列表中 newclauses = lappend(newclauses, newclause); } /* We might have got back fewer clauses than we started with */ @@ -834,29 +853,34 @@ static Node* pull_up_sublinks_targetlist(PlannerInfo *root, */ void inline_set_returning_functions(PlannerInfo* root) { - ListCell* rt = NULL; + ListCell* rt = NULL; // 声明一个用于遍历查询的范围表条目的列表元素指针 + // 遍历查询解析树的范围表条目 foreach (rt, root->parse->rtable) { - RangeTblEntry* rte = (RangeTblEntry*)lfirst(rt); + RangeTblEntry* rte = (RangeTblEntry*)lfirst(rt); // 获取当前范围表条目 + // 如果范围表条目的类型是函数(RTE_FUNCTION) if (rte->rtekind == RTE_FUNCTION) { - Query* funcquery = NULL; + Query* funcquery = NULL; // 用于存储函数展开后的查询 - /* Check safety of expansion, and expand if possible */ + /* 检查展开的安全性,并在可能的情况下进行展开 */ funcquery = inline_set_returning_function(root, rte); + + // 如果成功展开函数 if (funcquery != NULL) { - /* Successful expansion, replace the rtable entry */ + /* 成功展开,替换范围表条目的类型为子查询(RTE_SUBQUERY) */ rte->rtekind = RTE_SUBQUERY; - rte->subquery = funcquery; - rte->funcexpr = NULL; - rte->funccoltypes = NIL; - rte->funccoltypmods = NIL; - rte->funccolcollations = NIL; + rte->subquery = funcquery; // 将展开后的查询存储在范围表条目中 + rte->funcexpr = NULL; // 清空函数表达式 + rte->funccoltypes = NIL; // 清空函数列类型列表 + rte->funccoltypmods = NIL; // 清空函数列类型修改列表 + rte->funccolcollations = NIL; // 清空函数列的字符集列表 } } } } + /* * This recursively processes the jointree and returns a modified jointree. */ @@ -1530,18 +1554,20 @@ static Node* pull_up_simple_union_all(PlannerInfo* root, Node* jtnode, RangeTblE static void pull_up_union_leaf_queries( Node* setOp, PlannerInfo* root, int parentRTindex, Query* setOpQuery, int childRToffset) { + // 如果 setOp 是 RangeTblRef 类型 if (IsA(setOp, RangeTblRef)) { - RangeTblRef* rtr = (RangeTblRef*)setOp; + RangeTblRef* rtr = (RangeTblRef*)setOp; // 将 setOp 转换为 RangeTblRef 结构体 + int childRTindex; AppendRelInfo* appinfo = NULL; /* - * Calculate the index in the parent's range table + * 计算子查询在父查询范围表中的索引 */ childRTindex = childRToffset + rtr->rtindex; /* - * Build a suitable AppendRelInfo, and attach to parent's list. + * 创建适当的 AppendRelInfo 结构体,并附加到父查询的列表中 */ appinfo = makeNode(AppendRelInfo); appinfo->parent_relid = parentRTindex; @@ -1553,29 +1579,35 @@ static void pull_up_union_leaf_queries( root->append_rel_list = lappend(root->append_rel_list, appinfo); /* - * Recursively apply pull_up_subqueries to the new child RTE. (We - * must build the AppendRelInfo first, because this will modify it.) - * Note that we can pass NULL for containing-join info even if we're - * actually under an outer join, because the child's expressions - * aren't going to propagate up above the join. + * 递归应用 pull_up_subqueries_recurse 函数到新的子查询 RTE。 + * 需要先构建 AppendRelInfo,因为这将会修改它。 + * 注意,即使我们实际上处于外连接下,也可以传递 NULL 作为包含连接信息, + * 因为子查询的表达式不会向上传播到连接之上。 */ rtr = makeNode(RangeTblRef); rtr->rtindex = childRTindex; (void)pull_up_subqueries_recurse(root, (Node*)rtr, NULL, NULL, appinfo); - } else if (IsA(setOp, SetOperationStmt)) { + } + // 如果 setOp 是 SetOperationStmt 类型 + else if (IsA(setOp, SetOperationStmt)) { SetOperationStmt* op = (SetOperationStmt*)setOp; - /* Recurse to reach leaf queries */ + /* 递归处理左子查询 */ pull_up_union_leaf_queries(op->larg, root, parentRTindex, setOpQuery, childRToffset); + + /* 递归处理右子查询 */ pull_up_union_leaf_queries(op->rarg, root, parentRTindex, setOpQuery, childRToffset); - } else { + } + // 如果 setOp 是未知的节点类型 + else { ereport(ERROR, (errmodule(MOD_OPT), errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE), - errmsg("unrecognized node type: %d", (int)nodeTag(setOp)))); + errmsg("未知的节点类型: %d", (int)nodeTag(setOp)))); } } + /* * make_setop_translation_list * Build the list of translations from parent Vars to child Vars for @@ -1585,21 +1617,26 @@ static void pull_up_union_leaf_queries( */ static void make_setop_translation_list(Query* query, Index newvarno, List** translated_vars) { - List* vars = NIL; - ListCell* l = NULL; + List* vars = NIL; // 声明一个用于存储变量的列表 + ListCell* l = NULL; // 声明一个用于遍历目标列表的列表元素指针 + // 遍历查询的目标列表 foreach (l, query->targetList) { - TargetEntry* tle = (TargetEntry*)lfirst(l); + TargetEntry* tle = (TargetEntry*)lfirst(l); // 获取当前目标条目 + // 如果目标条目被标记为 "resjunk",则跳过它,不处理 if (tle->resjunk) continue; + // 创建一个新的变量,表示目标条目,并将其添加到变量列表中 vars = lappend(vars, makeVarFromTargetEntry(newvarno, tle)); } + // 将构建的变量列表赋值给传入的 translated_vars 指针 *translated_vars = vars; } + /* * is_simple_lateral_subquery * If the subquery is LATERAL, check for pullup restrictions from that. @@ -1660,7 +1697,7 @@ static bool is_simple_lateral_subquery(Query* subquery, JoinExpr *lowest_outer_j static bool is_grouping_subquery(Query* subquery) { - /* + /* * Can't pull up a subquery involving grouping, aggregation, sorting, * limiting, or WITH. (XXX WITH could possibly be allowed later) * @@ -1670,14 +1707,20 @@ static bool is_grouping_subquery(Query* subquery) * that case the locking was originally declared in the upper query * anyway. */ + // 如果子查询中包含聚合函数(hasAggs)、窗口函数(hasWindowFuncs)、GROUP BY 子句(groupClause)、 + // GROUPING SETS 子句(groupingSets)、HAVING 子句(havingQual)、排序子句(sortClause)、 + // DISTINCT 子句(distinctClause)、LIMIT 偏移(limitOffset)、LIMIT 计数(limitCount)、 + // FOR UPDATE 子句(hasForUpdate)或者通用表达式列表(cteList),则认为它不是分组子查询。 if (subquery->hasAggs || subquery->hasWindowFuncs || subquery->groupClause || subquery->groupingSets || subquery->havingQual || subquery->sortClause || subquery->distinctClause || subquery->limitOffset || subquery->limitCount || subquery->hasForUpdate || subquery->cteList) return false; + // 如果上述条件都不满足,则认为子查询是分组子查询。 return true; } + /* * is_simple_subquery * Check a subquery in the range table to see if it's simple enough @@ -1788,64 +1831,72 @@ static bool is_simple_union_all(Query* subquery) { SetOperationStmt* topop = NULL; - /* Let's just make sure it's a valid subselect ... */ + /* 首先确保子查询是一个有效的子查询... */ if (!IsA(subquery, Query) || subquery->commandType != CMD_SELECT || subquery->utilityStmt != NULL) ereport( - ERROR, (errmodule(MOD_OPT), errcode(ERRCODE_OPTIMIZER_INCONSISTENT_STATE), (errmsg("subquery is bogus")))); + ERROR, (errmodule(MOD_OPT), errcode(ERRCODE_OPTIMIZER_INCONSISTENT_STATE), (errmsg("子查询无效")))); - /* Is it a set-operation query at all? */ + /* 子查询是否是一个集合操作查询? */ topop = (SetOperationStmt*)subquery->setOperations; if (topop == NULL) return false; AssertEreport( - IsA(topop, SetOperationStmt), MOD_OPT_REWRITE, "subquery's setOperations mismatch in is_simple_union_all"); + IsA(topop, SetOperationStmt), MOD_OPT_REWRITE, "is_simple_union_all 中子查询的 setOperations 不匹配"); #ifndef ENABLE_MULTIPLE_NODES + // 如果子查询包含 ROWNUM 过滤条件,就不是简单的 UNION ALL 查询 if (ContainRownumQual(subquery)) { return false; } #endif - /* Can't handle ORDER BY, LIMIT/OFFSET, locking, or WITH */ + /* 不能处理包含 ORDER BY、LIMIT/OFFSET、锁定或 WITH 子句的子查询 */ if (subquery->sortClause || subquery->limitOffset || subquery->limitCount || subquery->rowMarks || subquery->cteList) return false; - /* Recursively check the tree of set operations */ + /* 递归检查集合操作树 */ return is_simple_union_all_recurse((Node*)topop, subquery, topop->colTypes); } + static bool is_simple_union_all_recurse(Node* setOp, Query* setOpQuery, List* colTypes) { + // 如果当前节点是 RangeTblRef 类型 if (IsA(setOp, RangeTblRef)) { - RangeTblRef* rtr = (RangeTblRef*)setOp; - RangeTblEntry* rte = rt_fetch(rtr->rtindex, setOpQuery->rtable); - Query* subquery = rte->subquery; + RangeTblRef* rtr = (RangeTblRef*)setOp; // 将当前节点转换为 RangeTblRef 结构体 + RangeTblEntry* rte = rt_fetch(rtr->rtindex, setOpQuery->rtable); // 获取当前节点对应的范围表条目 + Query* subquery = rte->subquery; // 获取范围表条目中的子查询 - AssertEreport(subquery != NULL, MOD_OPT_REWRITE, "subquery should not be NULL in is_simple_union_all_recurse"); + AssertEreport(subquery != NULL, MOD_OPT_REWRITE, "is_simple_union_all_recurse 中的子查询不应为 NULL"); - /* Leaf nodes are OK if they match the toplevel column types */ - /* We don't have to compare typmods or collations here */ + /* 如果是叶子节点,需要检查其列类型是否与顶层查询的列类型匹配 */ + /* 我们不必在这里比较类型修饰符(typmods)或字符集(collations) */ return tlist_same_datatypes(subquery->targetList, colTypes, true); - } else if (IsA(setOp, SetOperationStmt)) { - SetOperationStmt* op = (SetOperationStmt*)setOp; + } + // 如果当前节点是 SetOperationStmt 类型 + else if (IsA(setOp, SetOperationStmt)) { + SetOperationStmt* op = (SetOperationStmt*)setOp; // 将当前节点转换为 SetOperationStmt 结构体 - /* Must be UNION ALL */ + /* 必须是 UNION ALL 操作 */ if (op->op != SETOP_UNION || !op->all) return false; - /* Recurse to check inputs */ + /* 递归检查左子树和右子树 */ return is_simple_union_all_recurse(op->larg, setOpQuery, colTypes) && is_simple_union_all_recurse(op->rarg, setOpQuery, colTypes); - } else { + } + // 如果当前节点是未知的节点类型 + else { ereport(ERROR, (errmodule(MOD_OPT), errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE), - errmsg("unrecognized node type: %d", (int)nodeTag(setOp)))); - return false; /* keep compiler quiet */ + errmsg("未知的节点类型: %d", (int)nodeTag(setOp)))); + return false; /* 使编译器保持安静 */ } } + /* * is_safe_append_member * Check a subquery that is a leaf of a UNION ALL appendrel to see if it's @@ -1897,34 +1948,33 @@ jointree_contains_lateral_outer_refs(Node *jtnode, bool restricted, if (jtnode == NULL) return false; if (IsA(jtnode, RangeTblRef)) - return false; + return false; // 如果当前节点是范围表引用节点,不包含外部引用,返回 false else if (IsA(jtnode, FromExpr)) { FromExpr *f = (FromExpr *) jtnode; ListCell *l = NULL; - /* First, recurse to check child joins */ + /* 首先递归检查子联接 */ foreach(l, f->fromlist) { if (jointree_contains_lateral_outer_refs((Node *)lfirst(l), restricted, safe_upper_varnos)) - return true; + return true; // 如果子联接包含外部引用,返回 true } - /* Then check the top-level quals */ + /* 然后检查顶层条件表达式 */ if (restricted && !bms_is_subset(pull_varnos_of_level(f->quals, 1), safe_upper_varnos)) - return true; + return true; // 如果条件表达式中包含外部引用,且不在安全的上级变量范围内,返回 true } else if (IsA(jtnode, JoinExpr)) { JoinExpr *j = (JoinExpr *) jtnode; /* - * If this is an outer join, we mustn't allow any upper lateral - * references in or below it. + * 如果这是一个外连接,我们不能允许在其内部或下方有任何上级的外部引用。 */ if (j->jointype != JOIN_INNER) { @@ -1932,28 +1982,29 @@ jointree_contains_lateral_outer_refs(Node *jtnode, bool restricted, safe_upper_varnos = NULL; } - /* Check the child joins */ + /* 检查子联接 */ if (jointree_contains_lateral_outer_refs(j->larg, restricted, safe_upper_varnos)) - return true; + return true; // 如果左子联接包含外部引用,返回 true if (jointree_contains_lateral_outer_refs(j->rarg, restricted, safe_upper_varnos)) - return true; + return true; // 如果右子联接包含外部引用,返回 true - /* Check the JOIN's qual clauses */ + /* 检查联接的条件表达式 */ if (restricted && !bms_is_subset(pull_varnos_of_level(j->quals, 1), safe_upper_varnos)) - return true; + return true; // 如果条件表达式中包含外部引用,且不在安全的上级变量范围内,返回 true } else - elog(ERROR, "unrecognized node type: %d", + elog(ERROR, "未识别的节点类型: %d", (int) nodeTag(jtnode)); return false; } + /* * Helper routine for pull_up_subqueries: do pullup_replace_vars on every * expression in the jointree, without changing the jointree structure itself. @@ -2057,10 +2108,20 @@ static void replace_vars_in_jointree(Node* jtnode, pullup_replace_vars_context* */ static Node* pullup_replace_vars(Node* expr, pullup_replace_vars_context* context) { + // 使用 replace_rte_variables 函数替换表达式中的变量 + // 参数说明: + // expr:待替换的表达式 + // context->varno:用于替换的范围表索引 + // 0:替换的层级深度(0 表示替换所有层级) + // pullup_replace_vars_callback:用于处理替换的回调函数 + // (void*)context:回调函数的上下文数据 + // context->outer_hasSubLinks:是否包含子查询链接在外部查询中 + return replace_rte_variables( expr, context->varno, 0, pullup_replace_vars_callback, (void*)context, context->outer_hasSubLinks); } + static Node* pullup_replace_vars_callback(Var* var, replace_rte_variables_context* context) { pullup_replace_vars_context* rcon = (pullup_replace_vars_context*)context->callback_arg; @@ -2220,7 +2281,18 @@ static Query * pullup_replace_vars_subquery(Query *query, pullup_replace_vars_context *context) { + // 确保输入的 query 是一个有效的 Query 结构 Assert(IsA(query, Query)); + + // 使用 replace_rte_variables 函数替换子查询中的变量 + // 参数说明: + // (Node *) query:待替换的子查询 + // context->varno:用于替换的范围表索引 + // 1:替换的层级深度(1 表示只替换一层,即子查询中的变量) + // pullup_replace_vars_callback:用于处理替换的回调函数 + // (void *) context:回调函数的上下文数据 + // NULL:不包含子查询链接在外部查询中 + return (Query *) replace_rte_variables((Node *) query, context->varno, 1, pullup_replace_vars_callback, @@ -2228,6 +2300,7 @@ pullup_replace_vars_subquery(Query *query, NULL); } + /* * flatten_simple_union_all * Try to optimize top-level UNION ALL structure into an appendrel diff --git a/src/gausskernel/optimizer/rewrite/rewriteDefine.cpp b/src/gausskernel/optimizer/rewrite/rewriteDefine.cpp index 8ab4c682f..6bc055474 100644 --- a/src/gausskernel/optimizer/rewrite/rewriteDefine.cpp +++ b/src/gausskernel/optimizer/rewrite/rewriteDefine.cpp @@ -65,14 +65,25 @@ static void setRuleCheckAsUser_Query(Query* qry, Oid userid); * takes the arguments and inserts them as a row into the system * relation "pg_rewrite" */ -static Oid InsertRule(char* rulname, int evtype, Oid eventrel_oid, AttrNumber evslot_index, bool evinstead, - Node* event_qual, List* action, bool replace) +static Oid InsertRule(char* rulname, // 规则名称 + int evtype, // 事件类型(触发器类型) + Oid eventrel_oid, // 触发器所属关系的OID + AttrNumber evslot_index, // 触发器事件索引号 + bool evinstead, // 是否是INSTEAD 触发器 + Node* event_qual, // 触发器事件限定条件 + List* action, // 触发器的操作列表(动作) + bool replace) // 是否替换已存在的同名规则 { + // 将触发器事件限定条件和操作列表转换为字符串形式 char* evqual = nodeToString(event_qual); char* actiontree = nodeToString((Node*)action); + + // 定义数据和标志数组以保存规则的属性值和状态 Datum values[Natts_pg_rewrite]; bool nulls[Natts_pg_rewrite]; bool replaces[Natts_pg_rewrite]; + + // 定义规则名称、pg_rewrite关系的描述符、新的和旧的HeapTuple以及新的规则对象OID NameData rname; Relation pg_rewrite_desc; HeapTuple tup, oldtup; @@ -84,10 +95,14 @@ static Oid InsertRule(char* rulname, int evtype, Oid eventrel_oid, AttrNumber ev /* * Set up *nulls and *values arrays */ + + rc = memset_s(nulls, sizeof(nulls), false, sizeof(nulls)); securec_check(rc, "", ""); (void)namestrcpy(&rname, rulname); + + // 使用提供的参数初始化values数组 values[Anum_pg_rewrite_rulename - 1] = NameGetDatum(&rname); values[Anum_pg_rewrite_ev_class - 1] = ObjectIdGetDatum(eventrel_oid); values[Anum_pg_rewrite_ev_attr - 1] = Int16GetDatum(evslot_index); @@ -108,6 +123,7 @@ static Oid InsertRule(char* rulname, int evtype, Oid eventrel_oid, AttrNumber ev oldtup = SearchSysCache2(RULERELNAME, ObjectIdGetDatum(eventrel_oid), PointerGetDatum(rulname)); if (HeapTupleIsValid(oldtup)) { + // 如果不允许替换,抛出错误,因为已经存在同名规则 if (!replace) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_OBJECT), @@ -124,15 +140,19 @@ static Oid InsertRule(char* rulname, int evtype, Oid eventrel_oid, AttrNumber ev replaces[Anum_pg_rewrite_ev_qual - 1] = true; replaces[Anum_pg_rewrite_ev_action - 1] = true; + // 通过将新的属性值与旧的元组结合,创建一个新的元组 tup = (HeapTuple) tableam_tops_modify_tuple(oldtup, RelationGetDescr(pg_rewrite_desc), values, nulls, replaces); + // 更新pg_rewrite关系中的元组 simple_heap_update(pg_rewrite_desc, &tup->t_self, tup); + // 释放旧元组的系统缓存引用 ReleaseSysCache(oldtup); rewriteObjectId = HeapTupleGetOid(tup); is_update = true; } else { + // 如果没有找到同名的规则,将一个新的元组插入pg_rewrite中 tup = heap_form_tuple(pg_rewrite_desc->rd_att, values, nulls); rewriteObjectId = simple_heap_insert(pg_rewrite_desc, tup); @@ -161,6 +181,7 @@ static Oid InsertRule(char* rulname, int evtype, Oid eventrel_oid, AttrNumber ev referenced.objectId = eventrel_oid; referenced.objectSubId = 0; + // 记录依赖关系 recordDependencyOn(&myself, &referenced, (evtype == CMD_SELECT) ? DEPENDENCY_INTERNAL : DEPENDENCY_AUTO); /* @@ -173,6 +194,8 @@ static Oid InsertRule(char* rulname, int evtype, Oid eventrel_oid, AttrNumber ev Query* qry = (Query*)linitial(action); qry = getInsertSelectQuery(qry, NULL); + + // 在event_qual上创建引用对象的依赖关系 recordDependencyOnExpr(&myself, event_qual, qry->rtable, DEPENDENCY_NORMAL); } @@ -190,9 +213,9 @@ static Oid InsertRule(char* rulname, int evtype, Oid eventrel_oid, AttrNumber ev */ void DefineRule(RuleStmt* stmt, const char* queryString) { - List* actions = NIL; - Node* whereClause = NULL; - Oid relId; + List* actions = NIL;//规则的操作列表 + Node* whereClause = NULL; // 规则的WHERE子句 + Oid relId;// 关系的OID(对象标识符) /* Parse analysis. */ transformRuleStmt(stmt, queryString, &actions, &whereClause); @@ -217,15 +240,15 @@ void DefineRule(RuleStmt* stmt, const char* queryString) void DefineQueryRewrite( char* rulename, Oid event_relid, Node* event_qual, CmdType event_type, bool is_instead, bool replace, List* action) { - Relation event_relation; - int event_attno; - ListCell* l = NULL; - Query* query = NULL; - bool RelisBecomingView = false; - Datum values[Natts_pg_class]; - bool nulls[Natts_pg_class]; - bool replaces[Natts_pg_class]; - errno_t rc; + Relation event_relation;// 触发规则的关系 + int event_attno; // 触发规则的属性编号 + ListCell* l = NULL;// 遍历操作列表的指针 + Query* query = NULL;// 规则操作的查询对象 + bool RelisBecomingView = false;// 标记关系是否变成视图 + Datum values[Natts_pg_class];// 用于修改pg_class元组的字段值数组 + bool nulls[Natts_pg_class]; // 用于修改pg_class元组的空值标志数组 + bool replaces[Natts_pg_class];// 用于修改pg_class元组的字段替换标志数组 + errno_t rc;// 用于内存安全检查的错误码 /* * If we are installing an ON SELECT rule, we had better grab @@ -237,7 +260,7 @@ void DefineQueryRewrite( * * Note that this lock level should match the one used in DefineRule. */ - event_relation = heap_open(event_relid, AccessExclusiveLock); + event_relation = heap_open(event_relid, AccessExclusiveLock);//获取事件关系的 AccessExclusiveLock,确保在定义规则时对关系进行独占访问 /* * Verify relation is of a type that rules can sensibly be applied to. @@ -283,7 +306,7 @@ void DefineQueryRewrite( errmsg("rule actions on NEW are not implemented"), errhint("Use triggers instead."))); } - + //处理 CMD_UTILITY 事件(实用命令) if (event_type == CMD_UTILITY) { bool is_copy = false; if (list_length(action) == 1) { @@ -298,7 +321,7 @@ void DefineQueryRewrite( } } } - + //处理 CMD_SELEC 事件(实用命令) if (event_type == CMD_SELECT) { /* * Rules ON SELECT are restricted to view definitions @@ -356,12 +379,12 @@ void DefineQueryRewrite( */ if (!replace && event_relation->rd_rules != NULL) { int i; - + // 遍历事件关系中的每个规则 for (i = 0; i < event_relation->rd_rules->numLocks; i++) { RewriteRule* rule = NULL; - + // 获取当前规则 rule = event_relation->rd_rules->rules[i]; - if (rule->event == CMD_SELECT) + if (rule->event == CMD_SELECT)// 如果当前规则是一个 SELECT 规则,表示事件关系已经是一个视图,触发错误 ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("\"%s\" is already a view", RelationGetRelationName(event_relation)))); @@ -466,24 +489,27 @@ void DefineQueryRewrite( */ bool haveReturning = false; - foreach (l, action) { + foreach (l, action) {// 遍历规则中的每个动作 query = (Query*)lfirst(l); - + // 如果当前动作没有 RETURNING 子句,继续下一个动作 if (!query->returningList) continue; + // 如果已经有 RETURNING 子句出现,触发错误,不允许多个 RETURNING 子句 if (haveReturning) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot have multiple RETURNING lists in a rule"))); haveReturning = true; + // 如果存在条件限制(event_qual 不为空),不支持 RETURNING 子句,触发错误 if (event_qual != NULL) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("RETURNING lists are not supported in conditional rules"))); + // 如果不是 INSTEAD 规则,不支持 RETURNING 子句,触发错误 if (!is_instead) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("RETURNING lists are not supported in non-INSTEAD rules"))); - checkRuleResultList(query->returningList, RelationGetDescr(event_relation), false); + checkRuleResultList(query->returningList, RelationGetDescr(event_relation), false); // 检查 RETURNING 子句的结果列表是否与事件关系匹配 } } @@ -522,14 +548,19 @@ void DefineQueryRewrite( * --------------------------------------------------------------------- */ if (RelisBecomingView) { - Relation relationRelation; - Oid toastrelid; - HeapTuple classTup; - HeapTuple nctup; - Form_pg_class classForm; - Oid nspid; + Relation relationRelation; // 用于访问 pg_class 表的关系对象 + Oid toastrelid;// toast 表的 OID + HeapTuple classTup;// 关系在 pg_class 表中的元组 + HeapTuple nctup;// 新的关系元组,用于更新 pg_class 表 + Form_pg_class classForm; // 指向关系元组的结构体,用于访问关系属性 + Oid nspid;// 关系所属的命名空间的 OID + // 打开 pg_class 表,用于获取和修改关系元数据 + // 使用 RowExclusiveLock 锁定,以确保独占访问 relationRelation = heap_open(RelationRelationId, RowExclusiveLock); + + // 获取事件关系的 toast 表的 OID + // toast 表用于存储超过一页大小的大对象数据 toastrelid = event_relation->rd_rel->reltoastrelid; /* drop storage while table still looks like a table */ @@ -571,7 +602,7 @@ void DefineQueryRewrite( * the correct relkind and removal of reltoastrelid/reltoastidxid of * the toast table we potentially removed above. */ - classTup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(event_relid)); + classTup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(event_relid));// 在 pg_class 表中搜索指定 OID 的关系元组的副本 if (!HeapTupleIsValid(classTup)) ereport(ERROR, (errmodule(MOD_OPT_REWRITE), @@ -580,6 +611,8 @@ void DefineQueryRewrite( classForm = (Form_pg_class)GETSTRUCT(classTup); nspid = classForm->relnamespace; + + // 对关系属性进行修改,将其设置为视图的属性 classForm->reltablespace = InvalidOid; classForm->relpages = 0; classForm->reltuples = 0; @@ -604,10 +637,11 @@ void DefineQueryRewrite( replaces[Anum_pg_class_relfrozenxid64 - 1] = true; values[Anum_pg_class_relfrozenxid64 - 1] = TransactionIdGetDatum(InvalidTransactionId); + // 修改关系元组副本的内容,用于更新 pg_class 表 nctup = (HeapTuple) tableam_tops_modify_tuple(classTup, RelationGetDescr(relationRelation), values, nulls, replaces); - simple_heap_update(relationRelation, &nctup->t_self, nctup); - CatalogUpdateIndexes(relationRelation, nctup); + simple_heap_update(relationRelation, &nctup->t_self, nctup);// 在 pg_class 表中进行简单的元组更新操作 + CatalogUpdateIndexes(relationRelation, nctup);// 更新索引以反映元组更新的更改 tableam_tops_free_tuple(nctup); tableam_tops_free_tuple(classTup); @@ -617,13 +651,14 @@ void DefineQueryRewrite( RemovePgxcClass(event_relid); RemovePgxcSlice(event_relid); deleteDependencyRecordsFor(PgxcClassRelationId, event_relid, false); - if (IS_PGXC_COORDINATOR && !IsConnFromCoord()) { + if (IS_PGXC_COORDINATOR && !IsConnFromCoord()) {// 在协调器节点上执行 DROP TABLE 操作以删除关系 StringInfoData dropbuf; char* nspname = get_namespace_name(nspid); char* relname = get_rel_name(event_relid); const char* quoteNsp = quote_identifier(nspname); const char* quoteRel = quote_identifier(relname); + // 构造 DROP TABLE 命令字符串 initStringInfo(&dropbuf); appendStringInfo(&dropbuf, "DROP TABLE %s.%s;", quoteNsp, quoteRel); ExecUtilityStmtOnNodes(dropbuf.data, NULL, false, false, EXEC_ON_DATANODES, false); @@ -643,16 +678,16 @@ void DefineQueryRewrite( * isSelect tells which. (This is mostly used for choosing error messages, * but also we don't enforce column name matching for RETURNING.) */ -static void checkRuleResultList(List* targetList, TupleDesc resultDesc, bool isSelect) +static void checkRuleResultList(List* targetList, TupleDesc resultDesc, bool isSelect)//函数用于验证目标列表是否与元组描述兼容,主要检查 SELECT 或 RETURNING 列表的条目是否与元组描述中的列匹配 { ListCell* tllist = NULL; int i; i = 0; foreach (tllist, targetList) { - TargetEntry* tle = (TargetEntry*)lfirst(tllist); + TargetEntry* tle = (TargetEntry*)lfirst(tllist);//获取目标条目的指针,并转换为 TargetEntry 类型 int32 tletypmod; - Form_pg_attribute attr; + Form_pg_attribute attr;//用于存储目标条目关联的属性描述信息 char* attname = NULL; /* resjunk entries may be ignored */ @@ -681,12 +716,12 @@ static void checkRuleResultList(List* targetList, TupleDesc resultDesc, bool isS (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot convert relation containing dropped columns to view"))); - if (isSelect && strcmp(tle->resname, attname) != 0) + if (isSelect && strcmp(tle->resname, attname) != 0)//检查目标列表的条目名是否与元组描述的列名匹配(仅用于 SELECT 规则) ereport(ERROR, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("SELECT rule's target entry %d has different column name from \"%s\"", i, attname))); - if (attr->atttypid != exprType((Node*)tle->expr)) + if (attr->atttypid != exprType((Node*)tle->expr))//检查目标列表的表达式类型是否与列类型匹配 ereport(ERROR, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), isSelect ? errmsg("SELECT rule's target entry %d has different type from column \"%s\"", i, attname) @@ -706,7 +741,7 @@ static void checkRuleResultList(List* targetList, TupleDesc resultDesc, bool isS : errmsg("RETURNING list's entry %d has different size from column \"%s\"", i, attname))); } - if (i != resultDesc->natts) + if (i != resultDesc->natts)//检查目标列表的条目数量是否与元组描述的列数一致 ereport(ERROR, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), isSelect ? errmsg("SELECT rule's target list has too few entries") @@ -725,7 +760,7 @@ static void checkRuleResultList(List* targetList, TupleDesc resultDesc, bool isS * it's important to set these fields to match the rule owner. So we just set * them always. */ -void setRuleCheckAsUser(Node* node, Oid userid) +void setRuleCheckAsUser(Node* node, Oid userid)//函数递归地设置查询或表达式树中的 rtable 条目的 checkAsUser 字段为给定的用户 ID,用于在规则执行时模拟特定用户的权限 { (void)setRuleCheckAsUser_walker(node, &userid); } @@ -771,12 +806,12 @@ static void setRuleCheckAsUser_Query(Query* qry, Oid userid) /* * Change the firing semantics of an existing rule. */ -void EnableDisableRule(Relation rel, const char* rulename, char fires_when) +void EnableDisableRule(Relation rel, const char* rulename, char fires_when)//函数用于启用或禁用规则的触发语义,可以更改规则的触发条件 { - Relation pg_rewrite_desc; - Oid owningRel = RelationGetRelid(rel); - Oid eventRelationOid; - HeapTuple ruletup; + Relation pg_rewrite_desc;//用于存储 pg_rewrite 表的关系描述符 + Oid owningRel = RelationGetRelid(rel);//获得传入关系(表)的 Oid + Oid eventRelationOid;//用于存储规则关联的事件关系的 Oid + HeapTuple ruletup;//用于存储查询 pg_rewrite 表后获取的规则元组 bool changed = false; /* @@ -828,13 +863,14 @@ void EnableDisableRule(Relation rel, const char* rulename, char fires_when) * This is unused code at the moment. Note that it lacks a permissions check. */ #ifdef NOT_USED -void RenameRewriteRule(Oid owningRel, const char* oldName, const char* newName) +void RenameRewriteRule(Oid owningRel, const char* oldName, const char* newName)//函数用于重命名已存在的重写规则 { - Relation pg_rewrite_desc; - HeapTuple ruletup; + Relation pg_rewrite_desc;// 用于表示 pg_rewrite 表的关系描述符 + HeapTuple ruletup;// 用于存储查找到的重写规则元组 pg_rewrite_desc = heap_open(RewriteRelationId, RowExclusiveLock); + //在系统缓存中查找并复制指定规则的元组 ruletup = SearchSysCacheCopy2(RULERELNAME, ObjectIdGetDatum(owningRel), PointerGetDatum(oldName)); if (!HeapTupleIsValid(ruletup)) ereport(ERROR, @@ -847,6 +883,7 @@ void RenameRewriteRule(Oid owningRel, const char* oldName, const char* newName) (errcode(ERRCODE_DUPLICATE_OBJECT), errmsg("rule \"%s\" for relation \"%s\" already exists", newName, get_rel_name(owningRel)))); + //更新规则元组的 rulename 字段为新名称 (void)namestrcpy(&(((Form_pg_rewrite)GETSTRUCT(ruletup))->rulename), newName); simple_heap_update(pg_rewrite_desc, &ruletup->t_self, ruletup); diff --git a/src/gausskernel/optimizer/rewrite/rewriteHandler.cpp b/src/gausskernel/optimizer/rewrite/rewriteHandler.cpp index c5cabcf01..192565386 100644 --- a/src/gausskernel/optimizer/rewrite/rewriteHandler.cpp +++ b/src/gausskernel/optimizer/rewrite/rewriteHandler.cpp @@ -81,10 +81,10 @@ static Query* fireRIRrules(Query* parsetree, List* activeRIRs, bool forUpdatePus #ifdef PGXC typedef struct pull_qual_vars_context { - List* varlist; - int sublevels_up; - int resultRelation; - bool noRepeat; + List* varlist;// 一个指向List结构的指针,用于存储变量列表 + int sublevels_up;// 表示查询所在的嵌套层级 + int resultRelation;// 表示查询的结果关系(表) + bool noRepeat; // 一个布尔值,用于标识是否避免重复 } pull_qual_vars_context; static bool pull_qual_vars_walker(Node* node, pull_qual_vars_context* context); #endif @@ -125,7 +125,7 @@ static bool pull_qual_vars_walker(Node* node, pull_qual_vars_context* context); * That approach had horrible performance unfortunately; in particular * construction of a nested join was O(N^2) in the nesting depth.) */ -void AcquireRewriteLocks(Query* parsetree, bool forUpdatePushedDown) +void AcquireRewriteLocks(Query* parsetree, bool forUpdatePushedDown)//在查询重写过程中获取必要的锁,以确保在查询处理过程中的数据访问的正确性和一致性 { ListCell* l = NULL; int rt_index; @@ -145,7 +145,7 @@ void AcquireRewriteLocks(Query* parsetree, bool forUpdatePushedDown) ++rt_index; switch (rte->rtekind) { - case RTE_RELATION: + case RTE_RELATION://根据是否为查询结果关系和是否需要更新锁等情况,选择适当的锁类型,并更新关系的元数据 /* * Grab the appropriate lock type for the relation, and do not @@ -177,7 +177,7 @@ void AcquireRewriteLocks(Query* parsetree, bool forUpdatePushedDown) heap_close(rel, NoLock); break; - case RTE_JOIN: + case RTE_JOIN://检查别名变量列表,如果有列被删除,则将其替换为 NULL 常量 /* * Scan the join's alias var list to see if any columns have @@ -230,7 +230,7 @@ void AcquireRewriteLocks(Query* parsetree, bool forUpdatePushedDown) rte->joinaliasvars = newaliasvars; break; - case RTE_SUBQUERY: + case RTE_SUBQUERY://递归地调用 AcquireRewriteLocks 处理代表的子查询 /* * The subquery RTE itself is all right, but we have to @@ -247,7 +247,7 @@ void AcquireRewriteLocks(Query* parsetree, bool forUpdatePushedDown) } /* Recurse into subqueries in WITH */ - foreach (l, parsetree->cteList) { + foreach (l, parsetree->cteList) {//处理 WITH 子句中的子查询时,递归地对每个子查询调用 AcquireRewriteLocks CommonTableExpr* cte = (CommonTableExpr*)lfirst(l); AcquireRewriteLocks((Query*)cte->ctequery, false); @@ -257,18 +257,18 @@ void AcquireRewriteLocks(Query* parsetree, bool forUpdatePushedDown) * Recurse into sublink subqueries, too. But we already did the ones in * the rtable and cteList. */ - if (parsetree->hasSubLinks) + if (parsetree->hasSubLinks)//对于包含子链接的情况,使用 query_tree_walker 函数递归地处理子链接中的子查询 (void)query_tree_walker(parsetree, (bool (*)())acquireLocksOnSubLinks, NULL, QTW_IGNORE_RC_SUBQUERIES); } /* * Walker to find sublink subqueries for AcquireRewriteLocks */ -static bool acquireLocksOnSubLinks(Node* node, void* context) +static bool acquireLocksOnSubLinks(Node* node, void* context)//函数用于在查询重写过程中获取子查询中的锁 { if (node == NULL) return false; - if (IsA(node, SubLink)) { + if (IsA(node, SubLink)) {// 如果当前节点是 SubLink 类型,则处理子查询 SubLink* sub = (SubLink*)node; /* Do what we came for */ @@ -300,13 +300,18 @@ static bool acquireLocksOnSubLinks(Node* node, void* context) * Return value: * rewritten form of rule_action */ -static Query* rewriteRuleAction( - Query* parsetree, Query* rule_action, Node* rule_qual, int rt_index, CmdType event, bool* returning_flag) +static Query* rewriteRuleAction(//使用适当的限定词(从触发查询中获取)来重写规则操作 + Query* parsetree,//原始查询 + Query* rule_action, //规则的一个操作(查询) + Node* rule_qual, // 规则的 WHERE 条件,如果无条件则为 NULL + int rt_index, //原始查询中结果关系的 RT 索引 + CmdType event, // 规则事件的类型 + bool* returning_flag)//如果重写了规则动作中的 RETURNING 子句,则设置为 TRUE { - int current_varno, new_varno; - int rt_length; - Query* sub_action = NULL; - Query** sub_action_ptr; + int current_varno, new_varno;// 当前查询的关系变量序号和用于重写的新关系变量序号 + int rt_length;// 原始查询的关系表达式长度 + Query* sub_action = NULL; // 子查询的规则动作 + Query** sub_action_ptr;// 指向子查询规则动作的指针 /* * Make modifiable copies of rule action and qual (what we're passed are @@ -335,6 +340,7 @@ static Query* rewriteRuleAction( */ sub_action = getInsertSelectQuery(rule_action, &sub_action_ptr); + OffsetVarNodes((Node*)sub_action, rt_length, 0); OffsetVarNodes(rule_qual, rt_length, 0); /* but references to OLD should point at original rt_index */ @@ -379,18 +385,21 @@ static Query* rewriteRuleAction( ListCell* lc = NULL; foreach (lc, parsetree->rtable) { - RangeTblEntry* rte = (RangeTblEntry*)lfirst(lc); + RangeTblEntry* rte = (RangeTblEntry*)lfirst(lc);// // 获取当前迭代的 RangeTblEntry(关系表达式条目) switch (rte->rtekind) { case RTE_RELATION: sub_action->hasSubLinks = checkExprHasSubLink((Node*)rte->tablesample) || checkExprHasSubLink((Node*)rte->timecapsule); + // 检查 tablesample 和 timecapsule 子表达式是否包含子链接 break; case RTE_FUNCTION: sub_action->hasSubLinks = checkExprHasSubLink(rte->funcexpr); + // 检查 funcexpr 子表达式是否包含子链接 break; case RTE_VALUES: sub_action->hasSubLinks = checkExprHasSubLink((Node*)rte->values_lists); + // 检查 values_lists 子表达式是否包含子链接 break; default: /* other RTE types don't contain bare expressions */ @@ -534,8 +543,9 @@ static Query* rewriteRuleAction( if (*returning_flag) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot have RETURNING lists in multiple rules"))); - *returning_flag = true; - rule_action->returningList = (List*)ResolveNew((Node*)parsetree->returningList, + // // 报告错误:不支持在多个规则中使用 RETURNING 子句 + *returning_flag = true;// 设置返回标志为真,表示规则动作已经有 RETURNING 子句 + rule_action->returningList = (List*)ResolveNew((Node*)parsetree->returningList, // 解析并替换规则动作的 RETURNING 子句,保留原始查询的语义 parsetree->resultRelation, 0, rt_fetch(parsetree->resultRelation, parsetree->rtable), diff --git a/src/gausskernel/optimizer/rewrite/rewriteManip.cpp b/src/gausskernel/optimizer/rewrite/rewriteManip.cpp index ec4ffd181..f80ee51d2 100644 --- a/src/gausskernel/optimizer/rewrite/rewriteManip.cpp +++ b/src/gausskernel/optimizer/rewrite/rewriteManip.cpp @@ -50,59 +50,53 @@ static Relids adjust_relid_set(Relids relids, int oldrelid, int newrelid); /* * checkExprHasAggs - - * Check if an expression contains an aggregate function call of the - * current query level. + * 检查表达式是否包含当前Query级别的聚合函数调用。 */ bool checkExprHasAggs(Node* node) { - return contain_aggs_of_level(node, 0); + return contain_aggs_of_level(node, 0);//调用contain_aggs_of_level函数并将级别设为0 } /* * contain_aggs_of_level - - * Check if an expression contains an aggregate function call of a - * specified query level. + * 检查表达式是否包含指定Query级别的聚合函数调用。 * - * The objective of this routine is to detect whether there are aggregates - * belonging to the given query level. Aggregates belonging to subqueries - * or outer queries do NOT cause a true result. We must recurse into - * subqueries to detect outer-reference aggregates that logically belong to - * the specified query level. + * 这个例程的目的是检测是否存在属于给定Query级别的聚合。属于子查询或外部查询 + * 的聚合不会产生真结果。我们必须递归到子查询中,以检测逻辑上属于指定查询级别的外部引用聚合。 */ bool contain_aggs_of_level(Node* node, int levelsup) { - contain_aggs_of_level_context context; + contain_aggs_of_level_context context;//定义记录级别的结构体 - context.sublevels_up = levelsup; + context.sublevels_up = levelsup;//传入希望查询的聚合的级别 /* - * Must be prepared to start with a Query or a bare expression tree; if - * it's a Query, we don't want to increment sublevels_up. + * 必须准备好从Query或裸表达式树开始;如果它是一个Query,我们不想增加sublevels_up */ return query_or_expression_tree_walker(node, (bool (*)())contain_aggs_of_level_walker, (void*)&context, 0); } -static bool contain_aggs_of_level_walker(Node* node, contain_aggs_of_level_context* context) +static bool contain_aggs_of_level_walker(Node* node, contain_aggs_of_level_context* context)//传入节点及其级别 { - if (node == NULL) + if (node == NULL)//为空 return false; - if (IsA(node, Aggref)) { - if (((Aggref*)node)->agglevelsup == (Index)context->sublevels_up) - return true; /* abort the tree traversal and return true */ - /* else fall through to examine argument */ + if (IsA(node, Aggref)) {//节点为Aggref节点 + if (((Aggref*)node)->agglevelsup == (Index)context->sublevels_up)//级别相等 + return true; /* 中止树遍历并返回true */ + /* 否则就通过检验论证 */ } - if (IsA(node, GroupingFunc)) { + if (IsA(node, GroupingFunc)) {//节点为GroupingFunc if (((GroupingFunc*)node)->agglevelsup == (Index)context->sublevels_up) return true; - /* else fall through to examine argument */ + /* 否则就通过检验论证 */ } - if (IsA(node, Query)) { - /* Recurse into subselects */ + if (IsA(node, Query)) {//节点为Query + /* 递归为子选择 */ bool result = false; context->sublevels_up++; result = query_tree_walker((Query*)node, (bool (*)())contain_aggs_of_level_walker, (void*)context, 0); - context->sublevels_up--; + context->sublevels_up--;//还原sublevels_up return result; } return expression_tree_walker(node, (bool (*)())contain_aggs_of_level_walker, (void*)context); @@ -110,14 +104,13 @@ static bool contain_aggs_of_level_walker(Node* node, contain_aggs_of_level_conte /* * contain_aggs_of_level_or_above - - * Check if an expression contains an aggregate function call of a - * specified query level or level above. + * 检查表达式是否包含指定Query级别或更高级别的聚合函数调用。 * - * Return ture if any such aggregate function found. + * 如果找到任何这样的聚合函数,返回ture。 */ -bool contain_aggs_of_level_or_above(Node* node, int levelsup) +bool contain_aggs_of_level_or_above(Node* node, int levelsup)//未定义记录级别的结构体 { - int sublevels_up = levelsup; + int sublevels_up = levelsup;//传入希望查询的聚合的级别 return query_or_expression_tree_walker( node, (bool (*)())contain_aggs_of_level_or_above_walker, (void*)&sublevels_up, 0); @@ -125,27 +118,27 @@ bool contain_aggs_of_level_or_above(Node* node, int levelsup) static bool contain_aggs_of_level_or_above_walker(Node* node, int* sublevels_up) { - if (node == NULL) + if (node == NULL)//为空 return false; - if (IsA(node, Aggref)) { - if (((Aggref*)node)->agglevelsup >= (Index)*sublevels_up) { + if (IsA(node, Aggref)) {//节点为Aggref节点 + if (((Aggref*)node)->agglevelsup >= (Index)*sublevels_up) {//级别相等或大于 return true; } } - if (IsA(node, GroupingFunc)) { - if (((GroupingFunc*)node)->agglevelsup >= (Index)*sublevels_up) { + if (IsA(node, GroupingFunc)) {//节点为GroupingFunc + if (((GroupingFunc*)node)->agglevelsup >= (Index)*sublevels_up) {//级别相等或大于 return true; } - /* else fall through to examine argument */ + /* 否则就通过检验论证 */ } - if (IsA(node, Query)) { - /* Recurse into subselects */ + if (IsA(node, Query)) {//节点为Query + /* 递归为子选择 */ bool result = false; (*sublevels_up)++; result = query_tree_walker((Query*)node, (bool (*)())contain_aggs_of_level_or_above_walker, (void*)sublevels_up, 0); - (*sublevels_up)--; + (*sublevels_up)--;//还原sublevels_up return result; } return expression_tree_walker(node, (bool (*)())contain_aggs_of_level_or_above_walker, (void*)sublevels_up); @@ -153,53 +146,50 @@ static bool contain_aggs_of_level_or_above_walker(Node* node, int* sublevels_up) /* * locate_agg_of_level - - * Find the parse location of any aggregate of the specified query level. + * 查找指定Query级别的任何聚合的解析位置。 * - * Returns -1 if no such agg is in the querytree, or if they all have - * unknown parse location. (The former case is probably caller error, - * but we don't bother to distinguish it from the latter case.) + * 如果查询树中没有这样的聚合,或者它们都有未知的解析位置,则返回-1。 + * (前一种情况可能是调用者错误,但我们不必费心将其与后一种情况区分开来。) * - * Note: it might seem appropriate to merge this functionality into - * contain_aggs_of_level, but that would complicate that function's API. - * Currently, the only uses of this function are for error reporting, - * and so shaving cycles probably isn't very important. + * Note: 将此功能合并到包含级别的聚合中似乎是合适的,但这会使该函数的API复杂化。 + * 目前,该函数的唯一用途是错误报告,因此剃除周期可能不是很重要。 */ -int locate_agg_of_level(Node* node, int levelsup) +int locate_agg_of_level(Node* node, int levelsup)//传入希望查找的聚合级别 { - locate_agg_of_level_context context; + locate_agg_of_level_context context;//定义包含位置的结构体 - context.agg_location = -1; /* in case we find nothing */ + context.agg_location = -1; /* 以防我们一无所获 */ context.sublevels_up = levelsup; /* - * Must be prepared to start with a Query or a bare expression tree; if - * it's a Query, we don't want to increment sublevels_up. + * 必须准备以查询或裸表达式树开始; 如果它是一个Query,我们不希望增加子级别。 */ (void)query_or_expression_tree_walker(node, (bool (*)())locate_agg_of_level_walker, (void*)&context, 0); + //调用query_or_expression_tree_walker return context.agg_location; } static bool locate_agg_of_level_walker(Node* node, locate_agg_of_level_context* context) { - if (node == NULL) + if (node == NULL)//为空 return false; if (IsA(node, Aggref)) { - if (((Aggref*)node)->agglevelsup == (Index)(context->sublevels_up) && ((Aggref*)node)->location >= 0) { + if (((Aggref*)node)->agglevelsup == (Index)(context->sublevels_up) && ((Aggref*)node)->location >= 0) {//并且查找到的地址不为-1 context->agg_location = ((Aggref*)node)->location; - return true; /* abort the tree traversal and return true */ + return true; /* 中止树遍历并返回true */ } - /* else fall through to examine argument */ + /* 否则就通过检验论证 */ } if (IsA(node, GroupingFunc)) { if (((GroupingFunc*)node)->agglevelsup == (Index)context->sublevels_up && - ((GroupingFunc*)node)->location >= 0) { + ((GroupingFunc*)node)->location >= 0) {//并且查找到的地址不为-1 context->agg_location = ((GroupingFunc*)node)->location; - return true; /* abort the tree traversal and return true */ + return true; /* 中止树遍历并返回true */ } } if (IsA(node, Query)) { - /* Recurse into subselects */ + /* 递归为子选择 */ bool result = false; context->sublevels_up++; diff --git a/src/gausskernel/optimizer/rewrite/rewriteRemove.cpp b/src/gausskernel/optimizer/rewrite/rewriteRemove.cpp index b01f1b38b..3238fb99c 100644 --- a/src/gausskernel/optimizer/rewrite/rewriteRemove.cpp +++ b/src/gausskernel/optimizer/rewrite/rewriteRemove.cpp @@ -35,28 +35,29 @@ /* * Guts of rule deletion. */ -void RemoveRewriteRuleById(Oid ruleOid) +void RemoveRewriteRuleById(Oid ruleOid)//函数主要作用为从pg_rewrite表中删除指定的规则元组,并发出通知,确保其他后端在需要时更新相关的relcache条目,并在处理过程中使用适当的锁,以避免并发冲突 { - Relation RewriteRelation; - ScanKeyData skey[1]; - SysScanDesc rcscan; - Relation event_relation; - HeapTuple tuple; - Oid eventRelationOid; + Relation RewriteRelation;// pg_rewrite表的关系对象 + ScanKeyData skey[1];// 用于扫描pg_rewrite表的扫描键 + SysScanDesc rcscan;// 用于pg_rewrite表的扫描描述符 + Relation event_relation; // 触发规则的事件对象的关系对象 + HeapTuple tuple;// 用于保存查询到的pg_rewrite表的元组 + Oid eventRelationOid;// 触发规则的事件对象的OID /* * Open the pg_rewrite relation. */ - RewriteRelation = heap_open(RewriteRelationId, RowExclusiveLock); + RewriteRelation = heap_open(RewriteRelationId, RowExclusiveLock);//打开pg_rewrite表,使用RowExclusiveLock锁,防止其他事务同时修改表 /* * Find the tuple for the target rule. */ ScanKeyInit(&skey[0], ObjectIdAttributeNumber, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(ruleOid)); + //创建一个扫描键,用于根据ruleOid在pg_rewrite表中查找匹配的规则元组 - rcscan = systable_beginscan(RewriteRelation, RewriteOidIndexId, true, NULL, 1, skey); + rcscan = systable_beginscan(RewriteRelation, RewriteOidIndexId, true, NULL, 1, skey);//开始使用扫描键在pg_rewrite表中进行扫描 - tuple = systable_getnext(rcscan); + tuple = systable_getnext(rcscan);//获取匹配的规则元组,如果找不到,则报错 if (!HeapTupleIsValid(tuple)) ereport(ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("could not find tuple for rule %u", ruleOid))); @@ -66,24 +67,24 @@ void RemoveRewriteRuleById(Oid ruleOid) * going on that might depend on this rule. (Note: a weaker lock would * suffice if it's not an ON SELECT rule.) */ - eventRelationOid = ((Form_pg_rewrite)GETSTRUCT(tuple))->ev_class; - event_relation = heap_open(eventRelationOid, AccessExclusiveLock); + eventRelationOid = ((Form_pg_rewrite)GETSTRUCT(tuple))->ev_class;//获取触发规则的事件对象的OID + event_relation = heap_open(eventRelationOid, AccessExclusiveLock);//打开触发规则的事件对象,并使用AccessExclusiveLock锁,以防止其他查询依赖于该规则 /* * Now delete the pg_rewrite tuple for the rule */ - simple_heap_delete(RewriteRelation, &tuple->t_self); + simple_heap_delete(RewriteRelation, &tuple->t_self);//在pg_rewrite表中删除规则元组 - systable_endscan(rcscan); + systable_endscan(rcscan);//结束对pg_rewrite表的扫描 - heap_close(RewriteRelation, RowExclusiveLock); + heap_close(RewriteRelation, RowExclusiveLock);//关闭pg_rewrite表 /* * Issue shared-inval notice to force all backends (including me!) to * update relcache entries with the new rule set. */ - CacheInvalidateRelcache(event_relation); + CacheInvalidateRelcache(event_relation);//发出共享失效通知,强制所有后端(包括当前进程)更新relcache条目,以使用新的规则集 /* Close rel, but keep lock till commit... */ - heap_close(event_relation, NoLock); + heap_close(event_relation, NoLock);//关闭触发规则的事件对象,但保持锁,直到事务提交 } diff --git a/src/gausskernel/optimizer/rewrite/rewriteRlsPolicy.cpp b/src/gausskernel/optimizer/rewrite/rewriteRlsPolicy.cpp index 146d9f5da..1e9d07235 100644 --- a/src/gausskernel/optimizer/rewrite/rewriteRlsPolicy.cpp +++ b/src/gausskernel/optimizer/rewrite/rewriteRlsPolicy.cpp @@ -56,26 +56,19 @@ static void AddRlsUsingQuals( * @param (in) roleid: Role Oid * @return: This Row-Level-Security policy apply to role or not. */ -static bool CheckRoleForRlsPolicy(const RlsPolicy* policy, Oid roleid) +static bool CheckRoleForRlsPolicy(const RlsPolicy* policy, Oid roleid)//policy表示行级别安全策略,roleid表示用户角色的标识符 { - Oid* roles = (Oid*)ARR_DATA_PTR(policy->roles); - int roleNums = ARR_DIMS(policy->roles)[0]; - - /* - * ACL_ID_PUBLIC means this policy applies to all users, - * and ACL_ID_PUBLIC is the only applied user for this policy. - */ - if (roles[0] == ACL_ID_PUBLIC) { + Oid* roles = (Oid*)ARR_DATA_PTR(policy->roles);//roles用来访问传入行级别安全策略policy中的有权限的每个角色标识符 + int roleNums = ARR_DIMS(policy->roles)[0];//roleNums用于存储行级安全策略中包含的角色数量 + //ACL_ID_PUBLIC为所有用户均具备的基础权限 + if (roles[0] == ACL_ID_PUBLIC) {//优先判断,提高时间的效率 return true; } - for (int i = 0; i < roleNums; i++) { - /* Check this user has the privilege for this policy */ - if (has_privs_of_role(roleid, roles[i])) { + if (has_privs_of_role(roleid, roles[i])) {//遍历roles中所有拥有权限的角色是否为roleid return true; } } - /* This policy does not apply to current user */ return false; } @@ -95,18 +88,23 @@ static bool CheckRoleForRlsPolicy(const RlsPolicy* policy, Oid roleid) */ static void PullRlsPoliciesForRel(CmdType cmd, Oid roleid, const List* relRlsPolicies, List** permissivePolicies, List** restrictivePolicies, bool& hasSubLink) + //cmd 为SQL查询命令的类型 + //roleid 为当前用户的角色 + //relRlsPolicies 为行级安全策略表,其中存储多个 RlsPolicy 型行级安全策略 + //permissivePolicies 存储传递容许型策略,restrictivePolicies 存储传递限制型策略 + //hasSubLink 标记是否含有子链接 { - ListCell* item = NULL; - RlsPolicy* policy = NULL; - bool roleForPolicy = false; - bool cmdMatch = false; + ListCell* item = NULL;//用于遍历 relRlsPolicies 的临时变量 + RlsPolicy* policy = NULL;//用于存储 item 的值的临时变量 + bool roleForPolicy = false;//用于表示当前用户是否适用于该策略 + bool cmdMatch = false;//用于表示当前策略是否与给定的 SQL 查询命令类型匹配 foreach (item, relRlsPolicies) { policy = (RlsPolicy*)lfirst(item); /* Check this R.L.S policy affect this user, if not just skip */ - roleForPolicy = CheckRoleForRlsPolicy(policy, roleid); + roleForPolicy = CheckRoleForRlsPolicy(policy, roleid);//判断该用户是否适用于该策略 if (roleForPolicy) { cmdMatch = false; - switch (cmd) { + switch (cmd) {//依据不同的SQL查询命令cmd与当前策略policy相比较,判断是否相匹配 case CMD_SELECT: if ((policy->cmdName == ACL_SELECT_CHR) || (policy->cmdName == RLS_CMD_ALL_CHR)) cmdMatch = true; @@ -123,7 +121,7 @@ static void PullRlsPoliciesForRel(CmdType cmd, Oid roleid, const List* relRlsPol if ((policy->cmdName == ACL_UPDATE_CHR) || (policy->cmdName == RLS_CMD_ALL_CHR)) cmdMatch = true; break; - default: + default://不支持的命令类型,抛出错误 ereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR), errmsg("unsupported command type: %d.", cmd))); break; } @@ -167,10 +165,10 @@ static void AddRlsUsingQuals( foreach (item, restrictivePolicies) { policy = (RlsPolicy*)lfirst(item); - if (policy->usingExpr != NULL) { - rlsExpr = (Expr*)copyObject(policy->usingExpr); - ChangeVarNodes((Node*)rlsExpr, 1, rtIndex, 0); - *rlsUsingQuals = list_append_unique(*rlsUsingQuals, rlsExpr); + if (policy->usingExpr != NULL) {//如果策略中包含 USING 子句 + rlsExpr = (Expr*)copyObject(policy->usingExpr);//rlsExpr暂时存储 USING 子句 + ChangeVarNodes((Node*)rlsExpr, 1, rtIndex, 0);//修改 rlsExpr 中的变量节点,使其适应当前查询的表索引 + *rlsUsingQuals = list_append_unique(*rlsUsingQuals, rlsExpr);//将 rlsExpr 添加到 rlsUsingQuals 列表中 } } @@ -178,7 +176,7 @@ static void AddRlsUsingQuals( foreach (item, permissivePolicies) { policy = (RlsPolicy*)lfirst(item); - if (policy->usingExpr != NULL) { + if (policy->usingExpr != NULL) {//包含USING子句则放入‘permissiveQuals’列表 permissiveQuals = lappend(permissiveQuals, copyObject(policy->usingExpr)); } } @@ -189,10 +187,12 @@ static void AddRlsUsingQuals( * openGauss (PG will generate one-time False filter when no permissive * policies exist). */ + + //根据 permissiveQuals 列表中表达式的数量,构造一个合并所有容许型策略的 USING 子句的表达式 'rlsExpr' rlsExpr = NULL; - if (list_length(permissiveQuals) == 1) { + if (list_length(permissiveQuals) == 1) {//只含1个表达式则直接使用 rlsExpr = (Expr*)linitial(permissiveQuals); - } else if (list_length(permissiveQuals) > 1) { + } else if (list_length(permissiveQuals) > 1) {//大于1个使用 makeBoolExpr 函数生成一个使用 OR 连接的表达式 rlsExpr = makeBoolExpr(OR_EXPR, permissiveQuals, -1); } ChangeVarNodes((Node*)rlsExpr, 1, rtIndex, 0); @@ -222,10 +222,10 @@ void GetRlsPolicies(const Query* query, const RangeTblEntry* rte, const Relation /* Check whether enabled Row-Level-Security for this relation */ EnableRlsFeature rlsStatus = CheckEnableRlsPolicies(relation, roleid); /* relation did not enable row level security */ - if (rlsStatus == RLS_DISABLED) { + if (rlsStatus == RLS_DISABLED) { // 关系的 RLS 已禁用,因此无需应用安全检查 hasRowSecurity = false; return; - } else if (rlsStatus == RLS_DEPEND) { + } else if (rlsStatus == RLS_DEPEND) {// 由于存在依赖策略,关系启用了 RLS /* * relation enable row level security, but current user can bypass it. * hasRowSecurity is marked as true to force a re-plan when the environment @@ -249,6 +249,7 @@ void GetRlsPolicies(const Query* query, const RangeTblEntry* rte, const Relation * policies and t2's SELECT policies. */ CmdType cmdType = (rtIndex == query->resultRelation) ? query->commandType : CMD_SELECT; + // 确定正在执行的 SQL 命令类型(INSERT、UPDATE、DELETE 或 SELECT) List* rlsPermissivePolicies = NULL; List* rlsRestrictivePolicies = NULL; @@ -281,6 +282,8 @@ void GetRlsPolicies(const Query* query, const RangeTblEntry* rte, const Relation hasSubLink); AddRlsUsingQuals(CMD_UPDATE, rtIndex, updateMermissivePolicies, updateRestrictivePolicies, rlsQuals); + //通过调用 AddRlsUsingQuals 函数将获取的 RLS 策略转换为适当的条件,并将这些条件添加到查询的 rlsQuals 列表中 + //这些条件将在执行查询时应用于 WHERE 子句,从而强制执行行级安全策略 } /* diff --git a/src/gausskernel/optimizer/rewrite/rewriteSupport.cpp b/src/gausskernel/optimizer/rewrite/rewriteSupport.cpp index 46db8ef86..bf51b7cdd 100644 --- a/src/gausskernel/optimizer/rewrite/rewriteSupport.cpp +++ b/src/gausskernel/optimizer/rewrite/rewriteSupport.cpp @@ -36,6 +36,8 @@ bool IsDefinedRewriteRule(Oid owningRel, const char* ruleName) { return SearchSysCacheExists2(RULERELNAME, ObjectIdGetDatum(owningRel), PointerGetDatum(ruleName)); + //SearchSysCacheExists2是一个系统缓存搜索函数,用于检查是否存在满足指定条件的元组 + //RULERELNAME参数指定按照规则名进行检索 } /* @@ -52,6 +54,8 @@ bool IsDefinedRewriteRule(Oid owningRel, const char* ruleName) * row. */ void SetRelationRuleStatus(Oid relationId, bool relHasRules, bool relIsBecomingView) +//该函数用于设置给定表(relationId)的规则状态,即是否具有规则(relHasRules),以及是否将该表转换为视图(relIsBecomingView) +//并且会在修改关系的规则状态时发送失效消息以更新缓存 { Relation relationRelation; HeapTuple tuple; @@ -61,6 +65,7 @@ void SetRelationRuleStatus(Oid relationId, bool relHasRules, bool relIsBecomingV * Find the tuple to update in pg_class, using syscache for the lookup. */ relationRelation = heap_open(RelationRelationId, RowExclusiveLock); + //通过打开relationRelation(关系的系统目录表)来获取表的元组 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relationId)); if (!HeapTupleIsValid(tuple)) ereport(ERROR, @@ -70,7 +75,9 @@ void SetRelationRuleStatus(Oid relationId, bool relHasRules, bool relIsBecomingV classForm = (Form_pg_class)GETSTRUCT(tuple); if (classForm->relhasrules != relHasRules || (relIsBecomingView && classForm->relkind != RELKIND_VIEW - && classForm->relkind != RELKIND_CONTQUERY)) { + && classForm->relkind != RELKIND_CONTQUERY)) + //根据传入的参数更新表的relhasrules和relkind字段 + { /* Do the update */ classForm->relhasrules = relHasRules; if (relIsBecomingView) @@ -79,10 +86,10 @@ void SetRelationRuleStatus(Oid relationId, bool relHasRules, bool relIsBecomingV simple_heap_update(relationRelation, &tuple->t_self, tuple); /* Keep the catalog indexes up to date */ - CatalogUpdateIndexes(relationRelation, tuple); + CatalogUpdateIndexes(relationRelation, tuple);//通过CatalogUpdateIndexes更新系统目录索引 } else { /* no need to change tuple, but force relcache rebuild anyway */ - CacheInvalidateRelcacheByTuple(tuple); + CacheInvalidateRelcacheByTuple(tuple);//通过CacheInvalidateRelcacheByTuple更新缓存 } tableam_tops_free_tuple(tuple); @@ -96,12 +103,14 @@ void SetRelationRuleStatus(Oid relationId, bool relHasRules, bool relIsBecomingV * true, just return InvalidOid. */ Oid get_rewrite_oid(Oid relid, const char* rulename, bool missing_ok) +//用于获取给定表(relid)上指定规则(rulename)的OID(对象标识符) { HeapTuple tuple; Oid ruleoid; /* Find the rule's pg_rewrite tuple, get its OID */ tuple = SearchSysCache2(RULERELNAME, ObjectIdGetDatum(relid), PointerGetDatum(rulename)); + //使用SearchSysCache2来搜索符合规则名和表ID条件的规则元组,然后从元组中获取OID并返回 if (!HeapTupleIsValid(tuple)) { if (missing_ok) return InvalidOid; @@ -116,6 +125,7 @@ Oid get_rewrite_oid(Oid relid, const char* rulename, bool missing_ok) } char* get_rewrite_rulename(Oid ruleid, bool missing_ok) +//用于获取给定规则OID(ruleid)对应的规则名 { ScanKeyData entry; SysScanDesc scan; @@ -135,7 +145,7 @@ char* get_rewrite_rulename(Oid ruleid, bool missing_ok) (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("rule \"%u\" does not exist", ruleid))); } - Form_pg_rewrite pg_rewrite = (Form_pg_rewrite)GETSTRUCT(rewrite_tup); + Form_pg_rewrite pg_rewrite = (Form_pg_rewrite)GETSTRUCT(rewrite_tup);//通过在pg_rewrite表上扫描匹配规则OID的元组来获取规则名 rulename = (char*)palloc0(NAMEDATALEN); rc = strncpy_s(rulename, NAMEDATALEN, NameStr(pg_rewrite->rulename), NAMEDATALEN - 1); securec_check_c(rc, "\0", "\0"); @@ -151,6 +161,7 @@ char* get_rewrite_rulename(Oid ruleid, bool missing_ok) * ev_type is CmdType, transfer it to char beacuse it is char in system catalog pg_rewrite */ bool rel_has_rule(Oid relid, char ev_type) +//用于检查给定表(relid)上是否存在指定事件类型(ev_type)的规则 { bool has_rule = false; ScanKeyData entry; @@ -160,6 +171,7 @@ bool rel_has_rule(Oid relid, char ev_type) ScanKeyInit(&entry, Anum_pg_rewrite_ev_class, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(relid)); scan = systable_beginscan(rewrite_rel, RewriteRelRulenameIndexId, true, NULL, 1, &entry); while (HeapTupleIsValid((rewrite_tup = systable_getnext(scan)))) { + //在pg_rewrite表上进行扫描,查找满足表ID和事件类型的规则,如果找到了就返回true,否则返回false Form_pg_rewrite pg_rewrite = (Form_pg_rewrite)GETSTRUCT(rewrite_tup); if (pg_rewrite->ev_type == ev_type) { has_rule = true; @@ -180,6 +192,7 @@ bool rel_has_rule(Oid relid, char ev_type) * were unique across the entire database. */ Oid get_rewrite_oid_without_relid(const char* rulename, Oid* reloid, bool missing_ok) +//在没有关系ID的情况下,获取给定规则名(rulename)的OID { Relation RewriteRelation; TableScanDesc scanDesc; @@ -189,12 +202,12 @@ Oid get_rewrite_oid_without_relid(const char* rulename, Oid* reloid, bool missin /* Search pg_rewrite for such a rule */ ScanKeyInit(&scanKeyData, Anum_pg_rewrite_rulename, BTEqualStrategyNumber, F_NAMEEQ, CStringGetDatum(rulename)); - + RewriteRelation = heap_open(RewriteRelationId, AccessShareLock); scanDesc = tableam_scan_begin(RewriteRelation, SnapshotNow, 1, &scanKeyData); htup = (HeapTuple) tableam_scan_getnexttuple(scanDesc, ForwardScanDirection); - if (!HeapTupleIsValid(htup)) { + if (!HeapTupleIsValid(htup)) {//在pg_rewrite表上扫描,查找满足规则名的规则,如果找到了,则返回OID,如果没有找到并且missing_ok参数为false,则会报错 if (!missing_ok) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("rule \"%s\" does not exist", rulename))); ruleoid = InvalidOid; diff --git a/src/gausskernel/runtime/executor/execClusterResize.cpp b/src/gausskernel/runtime/executor/execClusterResize.cpp index 4eb0444a5..37226ee54 100644 --- a/src/gausskernel/runtime/executor/execClusterResize.cpp +++ b/src/gausskernel/runtime/executor/execClusterResize.cpp @@ -48,35 +48,45 @@ * --------------------------------------------------------------------------------- */ /* delete delta table definition */ +// 定义宏,表示pg_delete_delta关系的列数 #define Natts_pg_delete_delta 3 +// 定义列号常量,表示pg_delete_delta关系的各列 #define Anum_pg_delete_delta_xcnodeid_and_dntableoid 1 #define Anum_pg_delete_delta_tablebucketid_and_ctid 2 +// 定义用于范围扫描的Redis字符串常量 #define RANGE_SCAN_IN_REDIS "tidge+tid+pg_get_redis_rel_start_ctid+tidle+tid+pg_get_redis_rel_end_ctid+" +// 声明函数eval_dnstable_func_mutator,用于修改执行计划中的节点 static Node* eval_dnstable_func_mutator( Relation rel, Node* node, StringInfo qual_str, RangeScanInRedis *rangeScanInRedis, bool isRoot); +// 内联函数,用于检查Redis元组ID检索函数的函数签名是否匹配 static inline bool redis_tupleid_retrive_function(const char* funcname, Oid rettype, const Oid* argstype, int nargs); +// 内联函数,用于检查Redis块号检索函数的函数签名是否匹配 static inline bool redis_blocknum_retrive_function(const char* funcname, Oid rettype, const Oid* argstype, int nargs); +// 内联函数,用于检查Redis偏移量检索函数的函数签名是否匹配 static inline bool redis_offset_retrive_function(const char* funcname, Oid rettype, const Oid* argstype, int nargs); +// 定义宏,用于检查Redis元组ID检索函数的函数签名是否匹配 #define REDIS_TUPLEID_RETRIVE_FUNCSIG(rettype, argstype, nargs) \ (((nargs) == 1 && (rettype) == TIDOID && (argstype)[0] == TEXTOID) || \ ((nargs) == 4 && (rettype) == TIDOID && (argstype)[0] == TEXTOID && (argstype)[1] == NAMEOID && \ (argstype)[2] == INT4OID && (argstype)[3] == INT4OID)) +// 内联函数,用于检查Redis元组ID检索函数的函数签名是否匹配 static inline bool redis_tupleid_retrive_function(const char* funcname, Oid rettype, const Oid* argstype, int nargs) - { + // 检查函数名是否为pg_get_redis_rel_start_ctid,且函数签名匹配 if (pg_strcasecmp(funcname, "pg_get_redis_rel_start_ctid") == 0 && REDIS_TUPLEID_RETRIVE_FUNCSIG(rettype, argstype, nargs)) { return true; } + // 检查函数名是否为pg_get_redis_rel_end_ctid,且函数签名匹配 if (pg_strcasecmp(funcname, "pg_get_redis_rel_end_ctid") == 0 && REDIS_TUPLEID_RETRIVE_FUNCSIG(rettype, argstype, nargs)) { return true; @@ -85,9 +95,10 @@ static inline bool redis_tupleid_retrive_function(const char* funcname, Oid rett return false; } +// 内联函数,用于检查Redis偏移量检索函数的函数签名是否匹配 static inline bool redis_offset_retrive_function(const char* funcname, Oid rettype, const Oid* argstype, int nargs) - { + // 检查函数名是否为pg_tupleid_get_offset,且函数签名匹配 if (pg_strcasecmp(funcname, "pg_tupleid_get_offset") == 0 && (nargs == 1 && rettype == INT4OID && argstype[0] == TIDOID)) { return true; @@ -96,9 +107,10 @@ static inline bool redis_offset_retrive_function(const char* funcname, Oid retty return false; } +// 内联函数,用于检查Redis块号检索函数的函数签名是否匹配 static inline bool redis_blocknum_retrive_function(const char* funcname, Oid rettype, const Oid* argstype, int nargs) - { + // 检查函数名是否为pg_tupleid_get_blocknum,且函数签名匹配 if (pg_strcasecmp(funcname, "pg_tupleid_get_blocknum") == 0 && (nargs == 1 && rettype == INT8OID && argstype[0] == TIDOID)) { return true; @@ -107,9 +119,10 @@ static inline bool redis_blocknum_retrive_function(const char* funcname, Oid ret return false; } +// 内联函数,用于检查Redis元组ID检索函数的函数签名是否匹配 static inline bool redis_ctid_retrive_function(const char* funcname, Oid rettype, const Oid* argstype, int nargs) - { + // 检查函数名是否为pg_tupleid_get_ctid_to_bigint,且函数签名匹配 if (pg_strcasecmp(funcname, "pg_tupleid_get_ctid_to_bigint") == 0 && (nargs == 1 && rettype == INT8OID && argstype[0] == TIDOID)) { return true; @@ -119,6 +132,7 @@ static inline bool redis_ctid_retrive_function(const char* funcname, Oid rettype } + /* * - Brief: Record the given tuple's tupleid into pg_delete_delta table * - Parameter: @@ -127,35 +141,50 @@ static inline bool redis_ctid_retrive_function(const char* funcname, Oid rettype * - Return: * no return value */ + /* +这个函数的主要作用是将一个删除操作的信息记录到名为delete_delta的关系中。 +函数接受关系的OID(relid)、存储桶ID(bucketid)、被删除元组的位置(tupleid), +以及指向删除增量关系的指针(deldelta_rel)。它首先初始化一个用于存储插入元组的数组(values)和一个用于表示元组中的值是否为空的数组(nulls)。 +然后,它填充values数组中的各个列,构建一个堆元组,最后将该堆元组插入到delete_delta关系中。最后,函数释放堆元组的内存。 + */ void RecordDeletedTuple(Oid relid, int2 bucketid, const ItemPointer tupleid, const Relation deldelta_rel) { - Datum values[Natts_pg_delete_delta]; - bool nulls[Natts_pg_delete_delta]; - HeapTuple tup = NULL; + Datum values[Natts_pg_delete_delta]; // 用于存储插入元组的值 + bool nulls[Natts_pg_delete_delta]; // 用于表示元组中的值是否为空 + HeapTuple tup = NULL; // 声明堆元组变量 - Assert(deldelta_rel); - /* In redistribution, table delete_delta has 3 or 2 column. */ - Assert(RelationGetDescr(deldelta_rel)->natts <= 3); + Assert(deldelta_rel); // 断言:确保删除增量关系不为空 - /* Iterate through attributes initializing nulls and values */ + /* 在重分布中,表 delete_delta 具有 3 或 2 列。*/ + Assert(RelationGetDescr(deldelta_rel)->natts <= 3); // 断言:确保删除增量关系的列数不超过 3 + + /* 遍历属性并初始化 nulls 和 values 数组 */ for (int i = 0; i < Natts_pg_delete_delta; i++) { - nulls[i] = false; - values[i] = (Datum)0; + nulls[i] = false; // 所有属性的初始值均为非空 + values[i] = (Datum)0; // 所有属性的初始值为 0 } + + // 设置 xcnodeid_and_dntableoid 列的值 values[Anum_pg_delete_delta_xcnodeid_and_dntableoid - 1] = UInt64GetDatum(((uint64)u_sess->pgxc_cxt.PGXCNodeIdentifier << 32) | relid); + + // 设置 tablebucketid_and_ctid 列的值 values[Anum_pg_delete_delta_tablebucketid_and_ctid - 1] = UInt64GetDatum(((uint64)ItemPointerGetBlockNumber(tupleid) << 16) | ItemPointerGetOffsetNumber(tupleid)); + + // 如果 bucketid 有效,将其设置到 tablebucketid_and_ctid 列的高位 if (BUCKET_NODE_IS_VALID(bucketid)) { values[Anum_pg_delete_delta_tablebucketid_and_ctid - 1] |= ((uint64)bucketid << 48); } - /* Record delta */ - tup = heap_form_tuple(RelationGetDescr(deldelta_rel), values, nulls); - (void)simple_heap_insert(deldelta_rel, tup); - tableam_tops_free_tuple(tup); + /* 记录删除增量 */ + tup = heap_form_tuple(RelationGetDescr(deldelta_rel), values, nulls); // 创建堆元组 + (void)simple_heap_insert(deldelta_rel, tup); // 插入元组到关系中 + + tableam_tops_free_tuple(tup); // 释放堆元组的内存 } + /* * - Brief: Determine if the relation is under cluster resizing operation * - Parameter: @@ -166,15 +195,16 @@ void RecordDeletedTuple(Oid relid, int2 bucketid, const ItemPointer tupleid, con */ bool RelationInClusterResizing(const Relation rel) { - Assert(rel != NULL); + Assert(rel != NULL); // 断言:确保传递的关系参数不为空 - /* Check relation's append_mode status */ + /* 检查关系的 append_mode 状态 */ if (!IsInitdb && RelationInRedistribute(rel)) - return true; + return true; // 如果不是初始化数据库且关系正在重新分布,则返回 true - return false; + return false; // 如果不满足上述条件,则返回 false } + /* * - Brief: Determine if the relation is under cluster resizing read only operation * - Parameter: @@ -185,15 +215,16 @@ bool RelationInClusterResizing(const Relation rel) */ bool RelationInClusterResizingReadOnly(const Relation rel) { - Assert(rel != NULL); + Assert(rel != NULL); // 断言:确保传递的关系参数不为空 - /* Check relation's append_mode status */ + /* 检查关系的 append_mode 状态 */ if (!IsInitdb && RelationInRedistributeReadOnly(rel)) - return true; + return true; // 如果不是初始化数据库且关系正在以只读方式重新分布,则返回 true - return false; + return false; // 如果不满足上述条件,则返回 false } + /* * - Brief: Determine if the relation is under cluster resizing read only operation * - Parameter: @@ -204,15 +235,16 @@ bool RelationInClusterResizingReadOnly(const Relation rel) */ bool RelationInClusterResizingEndCatchup(const Relation rel) { - Assert(rel != NULL); + Assert(rel != NULL); // 断言:确保传递的关系参数不为空 - /* Check relation's append_mode status */ + /* 检查关系的 append_mode 状态 */ if (!IsInitdb && RelationInRedistributeEndCatchup(rel)) - return true; + return true; // 如果不是初始化数据库且关系处于重新分布的"EndCatchup"阶段,则返回 true - return false; + return false; // 如果不满足上述条件,则返回 false } + /* * @Description: check whether relation is in redistribution though range variable. * @in range_var: range variable which stored relation info. @@ -224,26 +256,35 @@ bool CheckRangeVarInRedistribution(const RangeVar* range_var) Oid relid; bool in_redis = false; + // 获取范围变量对应的关系OID relid = RangeVarGetRelid(range_var, AccessShareLock, true); if (OidIsValid(relid)) { + // 打开关系 relation = relation_open(relid, NoLock); - /* If the relation is index, we should check the related table is resizing or not. */ + + // 如果关系是索引,则检查相关的表是否处于重新分布状态 if (RelationIsIndex(relation)) { Oid heapOid = IndexGetRelation(relid, false); Relation heapRelation = relation_open(heapOid, AccessShareLock); in_redis = RelationInClusterResizing(heapRelation); relation_close(heapRelation, AccessShareLock); } else { + // 否则,检查关系是否处于重新分布状态 in_redis = RelationInClusterResizing(relation); } + // �����闭关系 relation_close(relation, NoLock); + + // 解锁关系OID UnlockRelationOid(relid, AccessShareLock); } - return in_redis; + + return in_redis; // 返回指示是否处于重新分布状态的布尔值 } + /* * - Brief: Determine if the table name is delete_delta table. * - Parameter: @@ -258,39 +299,44 @@ bool RelationIsDeleteDeltaTable(char* delete_delta_name) uint64 val; char* endptr = NULL; HeapTuple tuple; - + // 如果是初始化数据库,直接返回 false,因为没有删除增量表 if (IsInitdb) { return false; } - + // 检查表名是否以 "pg_delete_delta_" 开头 if (strncmp(delete_delta_name, "pg_delete_delta_", 16) != 0) { return false; } - + // 将表名中的数字部分转换为 uint64 val = strtoull(delete_delta_name + 16, &endptr, 0); - + // 检查转换是否成功 if ((errno == ERANGE) || (errno != 0 && val == 0)) { return false; } - + // 检查表名是否有效,数字后面不应有其他字符 if (endptr == delete_delta_name + 16 || *endptr != '\0') { return false; } + // 获取表的OID relid = (Oid)val; - + // 检查OID是否有效 if (!OidIsValid(relid)) { return false; } - + // 搜索系统缓存以获取表的信息 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid)); + // 如果未找到表信息,发出警告并返回 false if (!HeapTupleIsValid(tuple)) { - elog(WARNING, "Table %u related to %s does not exists.", relid, delete_delta_name); + elog(WARNING, "Table %u related to %s does not exist.", relid, delete_delta_name); return false; } + // 释放系统缓存 ReleaseSysCache(tuple); + // 返回 true 表示表是删除增量表 return true; } + /* * - Brief: Determine if the Progress is under cluster resizing status * - Return: @@ -299,35 +345,46 @@ bool RelationIsDeleteDeltaTable(char* delete_delta_name) */ bool ClusterResizingInProgress() { - Relation pgxc_group_rel = NULL; + Relation pgxc_group_rel = NULL; // 定义一个关系变量 TableScanDesc scan; HeapTuple tup = NULL; Datum datum; bool isNull = false; bool result = false; + // 打开 pgxc_group 关系以获取有关集群状态的信息 pgxc_group_rel = heap_open(PgxcGroupRelationId, AccessShareLock); + // 如果打开失败,发出 PANIC 错误 if (!pgxc_group_rel) { ereport(PANIC, (errcode(ERRCODE_RELATION_OPEN_ERROR), errmsg("can not open pgxc_group"))); } + // 开始对 pgxc_group 表进行扫描 scan = tableam_scan_begin(pgxc_group_rel, SnapshotNow, 0, NULL); + + // 遍历扫描结果 while ((tup = (HeapTuple) tableam_scan_getnexttuple(scan, ForwardScanDirection)) != NULL) { + // 获取表中 "in_redistribution" 列的值 datum = heap_getattr(tup, Anum_pgxc_group_in_redistribution, RelationGetDescr(pgxc_group_rel), &isNull); + // 如果值为 'y',表示集群正在进行调整大小,将结果设置为 true 并跳出循环 if ('y' == DatumGetChar(datum)) { result = true; break; } } + // 结束表扫描 tableam_scan_end(scan); + + // 关闭 pgxc_group 关系 heap_close(pgxc_group_rel, AccessShareLock); - return result; + return result; // 返回 true 表示集群正在进行调整大小,否则返回 false } + /* * - Brief: get the name of delete_delta table * - Parameter: @@ -341,25 +398,26 @@ static inline void RelationGetDeleteDeltaTableName(Relation rel, char* delete_de { int rc = 0; - /* Check if output parameter it not palloc()-ed from caller side */ + /* 检查输出参数是否由调用方分配内存 */ if (delete_delta_name == NULL || rel == NULL) { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("Invalid parameter in function '%s'", __FUNCTION__))); } /* - * Look up Relation's reloptions to get table's cnoid to - * form the name of delete_delta table + * 查找关系的 reloptions 以获取表的 cnoid 以形成 delete_delta 表的名称 */ if (!IsInitdb) { if (RelationInClusterResizing(rel) && !RelationInClusterResizingReadOnly(rel)) { if (isMultiCatchup) { + // 构建多重赶回(multi-catchup)删除增量表的名称 rc = snprintf_s(delete_delta_name, NAMEDATALEN, NAMEDATALEN - 1, REDIS_MULTI_CATCHUP_DELETE_DELTA_TABLE_PREFIX "%u", RelationGetRelCnOid(rel)); } else { + // 构建删除增量表的名称 rc = snprintf_s(delete_delta_name, NAMEDATALEN, NAMEDATALEN - 1, @@ -368,6 +426,7 @@ static inline void RelationGetDeleteDeltaTableName(Relation rel, char* delete_de } securec_check_ss(rc, "\0", "\0"); } else { + // 如果关系不在重新分布中,使用关系的名称构建删除增量表的名称 elog(LOG, "rel %s doesn't exist in redistributing", RelationGetRelationName(rel)); rc = snprintf_s(delete_delta_name, NAMEDATALEN, @@ -380,6 +439,7 @@ static inline void RelationGetDeleteDeltaTableName(Relation rel, char* delete_de return; } + /* * - Brief: get and open delete_delta rel * - Parameter: @@ -397,28 +457,32 @@ Relation GetAndOpenDeleteDeltaRel(const Relation rel, LOCKMODE lockmode, bool is Oid data_redis_namespace; errno_t errorno; + // 初始化变量并清零 errorno = memset_s(delete_delta_tablename, NAMEDATALEN, 0, NAMEDATALEN); securec_check_c(errorno, "\0", "\0"); + // 获取删除增量表的名称 RelationGetDeleteDeltaTableName(rel, (char*)delete_delta_tablename, isMultiCatchup); + + // 获取 "data_redis" 命名空间的OID data_redis_namespace = get_namespace_oid("data_redis", false); - /* We are going to fetch the delete delta relation under data_redis schema. */ + /* 我们将在 data_redis 模式下获取删除增量关系。 */ deldelta_relid = get_relname_relid(delete_delta_tablename, data_redis_namespace); + if (!OidIsValid(deldelta_relid)) { /* - * If multi catchup delta table is not there, just return NULL. We should not - * report error, because it is a valid case. Multi catchup delta table is - * dropped in each catchup iteration. + * 如果多重赶回(multi-catchup)删除增量表不存在,只需返回 NULL。不应报告错误,因为这是有效的情况。 + * 多重赶回(multi-catchup)删除增量表在每次赶回迭代中都会被删除。 */ if (isMultiCatchup) { return NULL; } /* - * To support Update or Delete during extension, we need to add 2 more columns. - * more columns. Limited by MaxHeapAttributeNumber, if the table already contains too many columns, - * we don't allow update or delete anymore, but insert statement can still proceed. + * 为了支持扩展期间的更新或删除,我们需要添加2个额外的列。 + * 受 MaxHeapAttributeNumber 限制,如果表已经包含太多列, + * 我们将不再允许更新或删除,但插入语句仍然可以执行。 */ if (((rel->rd_att->natts > (MaxHeapAttributeNumber - (Natts_pg_delete_delta - 1))) && !RELATION_IS_PARTITIONED(rel)) || @@ -429,22 +493,28 @@ Relation GetAndOpenDeleteDeltaRel(const Relation rel, LOCKMODE lockmode, bool is RelationGetRelationName(rel)), errdetail("Can not support online extension, if the table contains too many columns"))); } - /* ERROR case, should never come here */ + + /* 错误情况,不应该到达这里 */ ereport(ERROR, (errcode(ERRCODE_UNDEFINED_TABLE), errmsg("delete delta table %s is not found when do cluster resizing table \"%s\"", delete_delta_tablename, RelationGetRelationName(rel)))); } + + // 打开删除增量表并返回其关系对象 deldelta_rel = relation_open(deldelta_relid, lockmode); + + // 输出调试信息,表示删除增量表有效 elog(DEBUG1, "Delete_delta table %s for relation %s being under cluster resizing is valid.", delete_delta_tablename, RelationGetRelationName(rel)); - return deldelta_rel; + return deldelta_rel; // 返回删除增量表的关系对象 } + /* * - Brief: Check the stmtment during online expansion, block unsupported ddl in cluster resizing. * - Parameter: @@ -927,19 +997,21 @@ void BlockUnsupportedDDL(const Node* parsetree) */ bool redis_func_shippable(Oid funcid) { - const char* func_name = get_func_name(funcid); - Oid* argstype = NULL; - int nargs; - Oid rettype = InvalidOid; - bool result = false; + const char* func_name = get_func_name(funcid); // 获取函数的名称 + Oid* argstype = NULL; // 函数的参数类型数组 + int nargs; // 函数的参数数量 + Oid rettype = InvalidOid; // 函数的返回类型 + bool result = false; // 结果标志,初始值为 false + // 如果函数名称为 NULL,报告错误,表示函数不存在 if (func_name == NULL) { ereport(ERROR, (errcode(ERRCODE_UNDEFINED_FUNCTION), errmsg("function with OID %u does not exist", funcid))); } - /* Fetch function signatures */ + // 获取函数的签名,包括返回类型和参数类型 rettype = get_func_signature(funcid, &argstype, &nargs); + // 检查函数是否是Redis环境中可运行的函数类型 if (redis_tupleid_retrive_function(func_name, rettype, argstype, nargs)) { /* tupleid retrive functions is shippable to datanodes */ result = true; @@ -951,15 +1023,16 @@ bool redis_func_shippable(Oid funcid) result = true; } - /* pfree */ + // 释放参数类型数组的内存 if (argstype != NULL) { pfree_ext(argstype); argstype = NULL; } - return result; + return result; // 返回函数是否可在Redis环境中运行的结果 } + /* * - Brief: determine if given funcid reflects a dn-stable function * - Parameter: @@ -969,29 +1042,32 @@ bool redis_func_shippable(Oid funcid) */ bool redis_func_dnstable(Oid funcid) { - const char* func_name = get_func_name(funcid); - Oid* argstype = NULL; - int nargs; - Oid rettype = InvalidOid; - bool result = false; + const char* func_name = get_func_name(funcid); // 获取函数的名称 + Oid* argstype = NULL; // 函数的参数类型数组 + int nargs; // 函数的参数数量 + Oid rettype = InvalidOid; // 函数的返回类型 + bool result = false; // 结果标志,初始值为 false + // 如果函数名称为 NULL,报告错误,表示函数不存在 if (func_name == NULL) { ereport(ERROR, (errcode(ERRCODE_UNDEFINED_FUNCTION), errmsg("function with OID %u does not exist when checking function dnstable", funcid))); } - /* Fetch function signatures */ + // 获取函数的签名,包括返回类型和参数类型 rettype = get_func_signature(funcid, &argstype, &nargs); + // 检查函数是否属于"dnstable"函数类型,这里是通过检查与元组ID(tuple ID)相关的函数来判断 if (redis_tupleid_retrive_function(func_name, rettype, argstype, nargs)) { /* tupleid retrive functions is dnstable */ result = true; } - return result; + return result; // 返回函数是否属于"dnstable"函数类型的结果 } + /* * - Brief: evaluate ctid functions into a const value to avoid per-scanning * tuple invokation in seqscan. @@ -1004,12 +1080,11 @@ bool redis_func_dnstable(Oid funcid) */ List* eval_ctid_funcs(Relation rel, List* original_quals, RangeScanInRedis *rangeScanInRedis) { - StringInfo qual_str = makeStringInfo(); + /* - * we have to make a copy of the original quals, since the eval_dnstable_func_mutator - * will modify the it. the original qual will be needed again and again in later - * to be re-eval in partition table scans. + * 我们必须复制原始的 quals,因为 eval_dnstable_func_mutator + * 会修改它。原始 quals 会在后续的分区表扫描中再次需要。 */ List* new_quals = (List*)copyObject((const void*)(original_quals)); @@ -1018,12 +1093,14 @@ List* eval_ctid_funcs(Relation rel, List* original_quals, RangeScanInRedis *rang rangeScanInRedis->sliceIndex = 0; (void)eval_dnstable_func_mutator(rel, (Node*)new_quals, qual_str, rangeScanInRedis, true); + // 释放资源 pfree_ext(qual_str->data); pfree_ext(qual_str); return new_quals; } + static int32 get_expr_const_val(Node *val){ if (IsA(val, Const) && !((Const*)val)->constisnull && ((Const*)val)->consttype == INT4OID) { return DatumGetInt32(((Const*)val)->constvalue); @@ -1047,40 +1124,53 @@ static int32 get_expr_const_val(Node *val){ static Node* eval_dnstable_func_mutator( Relation rel, Node* node, StringInfo qual_str, RangeScanInRedis *rangeScanInRedis, bool isRoot) { + // 如果节点为空,直接返回 NULL if (node == NULL) return NULL; + // 如果运行在 PGXC 协调器节点,不做任何处理,直接返回原始节点 if (IS_PGXC_COORDINATOR) return node; + // 根据节点类型进行处理 switch (nodeTag(node)) { case T_FuncExpr: { FuncExpr* expr = (FuncExpr*)node; - /* flatten dn stable function into const value */ + /* 将 "dnstable" 函数替换为常量值 */ if (redis_func_dnstable(expr->funcid)) { Node* new_const = NULL; char* funcname = get_func_name(expr->funcid); + + // 如果函数名不存在,报错 if (funcname == NULL) { ereport(ERROR, (errcode(ERRCODE_UNDEFINED_FUNCTION), errmsg("operation expression function with OID %u does not exist.", expr->funcid))); } + // 检查函数是否为特定的两个函数 bool is_func_get_start_ctid = pg_strcasecmp(funcname, "pg_get_redis_rel_start_ctid") == 0; bool is_func_get_end_ctid = pg_strcasecmp(funcname, "pg_get_redis_rel_end_ctid") == 0; + // 根据函数类型进行不同的处理 if (is_func_get_start_ctid || is_func_get_end_ctid){ + // 获取两个额外的参数值 int32 numSlices = get_expr_const_val((Node*)list_nth(expr->args, 2)); int32 idxSlices = get_expr_const_val((Node*)list_nth(expr->args, 3)); + // 调用函数获取新的常量值 new_const = eval_redis_func_direct(rel, is_func_get_start_ctid, numSlices, idxSlices); + // 更新范围扫描信息 rangeScanInRedis->sliceIndex = idxSlices; rangeScanInRedis->sliceTotal = numSlices; } else { + // 对于其他的 dnstable 函数,简单地将其替换为常量表达式的值 new_const = eval_const_expressions(NULL, node); } + // 将函数名和 "+" 添加到查询字符串中 appendStringInfoString(qual_str, get_func_name(expr->funcid)); appendStringInfoString(qual_str, "+"); + // 返回新的常量值 return new_const; } @@ -1088,13 +1178,14 @@ static Node* eval_dnstable_func_mutator( } case T_List: { List* l = (List*)node; + // 遍历列表中的每个表达式节点 for (int i = 0; i < list_length(l); i++) { Node* expr = (Node*)list_nth(l, i); + // 递归调用自身,替换列表中的节点 Node* new_expr = eval_dnstable_func_mutator(rel, expr, qual_str, rangeScanInRedis, false); /* - * If a FuncExpr node is evalated into a T_Const value, we are hitting - * the point so replace it in qual list. + * 如果一个 FuncExpr 节点被评估为 T_Const 值,我们将其替换为 quals 列表中的相应节点。 */ if (expr && IsA(expr, FuncExpr) && new_expr && IsA(new_expr, Const)) { l = list_delete_ptr(l, expr); @@ -1103,8 +1194,7 @@ static Node* eval_dnstable_func_mutator( } /* - * If the predicate at root is something like "where ctid between pg_get_redis_rel_start_ctid('xx') - * and pg_get_redis_rel_end_ctid('xx')" on DN, we will pushdown the predicate at scan node. + * 如果是根节点,并且查询字符串中包含特定字符串 "RANGE_SCAN_IN_REDIS",则将范围扫描标志设置为 true。 */ if (isRoot && pg_strcasecmp(qual_str->data, RANGE_SCAN_IN_REDIS) == 0) { rangeScanInRedis->isRangeScanInRedis = true; @@ -1116,20 +1206,23 @@ static Node* eval_dnstable_func_mutator( OpExpr* opexpr = (OpExpr*)node; char* funcname = get_func_name(opexpr->opfuncid); + // 如果函数名不存在,报错 if (funcname == NULL) { ereport(ERROR, (errcode(ERRCODE_UNDEFINED_FUNCTION), errmsg("operation expression function with OID %u does not exist.", opexpr->opfuncid))); } + // 将函数名添加到查询字符串中 appendStringInfoString(qual_str, funcname); appendStringInfoString(qual_str, "+"); + // 递归处理操作表达式的参数节点 eval_dnstable_func_mutator(rel, (Node*)opexpr->args, qual_str, rangeScanInRedis, false); break; } case T_Var: { Var* var = (Var*)node; - /* we only expect tid column in the predicate */ + // 如果变量的类型是 TIDOID,将 "tid" 添加到查询字符串中 if (var->vartype == TIDOID) { appendStringInfoString(qual_str, "tid"); appendStringInfoString(qual_str, "+"); @@ -1137,6 +1230,7 @@ static Node* eval_dnstable_func_mutator( break; } default: { + // 对于其他节点类型,将节点的类型名称添加到查询字符串中 appendStringInfoString(qual_str, nodeTagToString(nodeTag(node))); appendStringInfoString(qual_str, "+"); break; @@ -1146,6 +1240,7 @@ static Node* eval_dnstable_func_mutator( return NULL; } + /* * - Brief: get and open new_table rel * - Parameter: @@ -1155,26 +1250,36 @@ static Node* eval_dnstable_func_mutator( */ Relation GetAndOpenNewTableRel(const Relation rel, LOCKMODE lockmode) { + // 声明变量并初始化为初始值 Relation newtable_rel = NULL; Oid newtable_relid = InvalidOid; Oid data_redis_namespace; char new_tablename[NAMEDATALEN]; errno_t errorno = EOK; + // 使用 memset_s 函数将 new_tablename 数组清零 errorno = memset_s(new_tablename, NAMEDATALEN, 0, NAMEDATALEN); securec_check_c(errorno, "\0", "\0"); + // 调用 RelationGetNewTableName 函数获取新表的名称 RelationGetNewTableName(rel, (char*)new_tablename); + + // 获取 data_redis 命名空间的 OID data_redis_namespace = get_namespace_oid("data_redis", false); + + // 根据新表名和命名空间获取新表的 OID newtable_relid = get_relname_relid(new_tablename, data_redis_namespace); + + // 如果新表的 OID 无效,报错,不应该出现在正常情况下 if (!OidIsValid(newtable_relid)) { - /* ERROR case, should never come here */ ereport(ERROR, (errcode(ERRCODE_DATA_EXCEPTION), errmsg("new table %s is not found when do cluster resizing table \"%s\"", new_tablename, RelationGetRelationName(rel)))); } + + // 打开新表并返回新表的关系对象 newtable_rel = relation_open(newtable_relid, lockmode); elog(LOG, "New temp table %s for relation %s under cluster resizing is valid.", @@ -1184,6 +1289,7 @@ Relation GetAndOpenNewTableRel(const Relation rel, LOCKMODE lockmode) return newtable_rel; } + /* * - Brief: get the name of new table * - Parameter: @@ -1196,7 +1302,7 @@ void RelationGetNewTableName(Relation rel, char* newtable_name) { int rc = 0; - /* Check if output parameter it not palloc()-ed from caller side */ + // 检查输出参数 newtable_name 是否为 NULL,以及传入的 rel 是否为 NULL if (newtable_name == NULL || rel == NULL) { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), @@ -1204,24 +1310,30 @@ void RelationGetNewTableName(Relation rel, char* newtable_name) } /* - * Look up relaion's reloptions to get table's cnoid to - * form the name of new table + * 查找关系的 reloptions 以获取表的 cnoid, + * 以形成新表的名称 */ if (!IsInitdb) { + // 获取关系的 cnoid Oid rel_cn_oid = RelationGetRelCnOid(rel); + + // 如果 cnoid 有效,使用它创建新表的名称 if (OidIsValid(rel_cn_oid)) { rc = snprintf_s(newtable_name, NAMEDATALEN, NAMEDATALEN - 1, "data_redis_tmp_%u", rel_cn_oid); } else { + // 如果 cnoid 无效,记录日志并使用关系的名称创建新表的名称 elog(LOG, "rel %s doesn't exist in redistributing", RelationGetRelationName(rel)); rc = snprintf_s( newtable_name, NAMEDATALEN, NAMEDATALEN - 1, "data_redis_tmp_%s", RelationGetRelationName(rel)); } - /* check the return value of security function */ + + // 检查 snprintf_s 函数的返回值以确保没有溢出 securec_check_ss(rc, "\0", "\0"); } return; } + /* * - Brief: Determine if the relation is under cluster resizing write error mode * - Parameter: @@ -1232,7 +1344,9 @@ void RelationGetNewTableName(Relation rel, char* newtable_name) */ bool RelationInClusterResizingWriteErrorMode(const Relation rel) { + // 检查关系是否在只读模式下进行集群调整,或者在结束追赶阶段并且没有成功获取 Redis 锁 return RelationInClusterResizingReadOnly(rel) || (RelationInClusterResizingEndCatchup(rel) && !pg_try_advisory_lock_for_redis(rel)); } + diff --git a/src/gausskernel/runtime/executor/execCurrent.cpp b/src/gausskernel/runtime/executor/execCurrent.cpp index 9e49bdc14..bd1c83924 100644 --- a/src/gausskernel/runtime/executor/execCurrent.cpp +++ b/src/gausskernel/runtime/executor/execCurrent.cpp @@ -47,6 +47,12 @@ static ScanState* search_plan_tree(PlanState *node, Oid table_oid); * legal situation in inheritance cases). Raises error if cursor is not a * valid updatable scan of the specified table. */ + /* +函数的主要目的是根据 CURRENT OF 表达式获取游标的当前位置, +并返回相关的 TID(Tuple ID)。函数根据游标是否使用 FOR UPDATE/SHARE 分为两种不同的策略来执行此操作, +并返回 true 表示成功获取 TID,或返回 false 表示未能获取 TID。函数也会检查游标的合法性, +如果游标不存在、不是 SELECT 查询、或处于无效状态,则会引发错误。 + */ bool execCurrentOf(CurrentOfExpr *cexpr, ExprContext *econtext, Relation relation, ItemPointer current_tid, RelationPtr partitionOfCursor_tid) { @@ -55,14 +61,14 @@ bool execCurrentOf(CurrentOfExpr *cexpr, ExprContext *econtext, Relation relatio QueryDesc *query_desc = NULL; Oid table_oid = RelationGetRelid(relation); - /* Get the cursor name --- may have to look up a parameter reference */ + // 获取游标名称,可能需要查找参数引用 if (cexpr->cursor_name) { cursor_name = cexpr->cursor_name; } else { cursor_name = fetch_cursor_param_value(econtext, cexpr->cursor_param); } - /* Find the cursor's portal */ + // 查找游标的 Portal portal = GetPortalByName(cursor_name); if (!PortalIsValid(portal)) { ereport(ERROR, (errcode(ERRCODE_UNDEFINED_CURSOR), @@ -70,8 +76,7 @@ bool execCurrentOf(CurrentOfExpr *cexpr, ExprContext *econtext, Relation relatio } /* - * We have to watch out for non-SELECT queries as well as held cursors, - * both of which may have null query_desc. + * 我们必须注意非SELECT查询以及持有的游标,两者都可能没有查询描述。 */ if (portal->strategy != PORTAL_ONE_SELECT) { ereport(ERROR, @@ -85,26 +90,23 @@ bool execCurrentOf(CurrentOfExpr *cexpr, ExprContext *econtext, Relation relatio } /* - * We have two different strategies depending on whether the cursor uses - * FOR UPDATE/SHARE or not. The reason for supporting both is that the - * FOR UPDATE code is able to identify a target table in many cases where - * the other code can't, while the non-FOR-UPDATE case allows use of WHERE - * CURRENT OF with an insensitive cursor. + * 根据游标是否使用 FOR UPDATE/SHARE,我们有两种不同的策���。 + * 支持这两种的原因是,FOR UPDATE 代码在很多情况下可以识别目标表,而其他代码无法识别, + * 而非 FOR UPDATE 的情况允许在不敏感游标上使用 WHERE CURRENT OF。 */ if (query_desc->estate->es_rowMarks) { ExecRowMark *erm = NULL; ListCell *lc = NULL; /* - * Here, the query must have exactly one FOR UPDATE/SHARE reference to - * the target table, and we dig the ctid info out of that. + * 在这里,查询必须对目标表具有一次 FOR UPDATE/SHARE 引用,并且我们从中找到 ctid 信息。 */ erm = NULL; foreach (lc, query_desc->estate->es_rowMarks) { ExecRowMark *thiserm = (ExecRowMark *)lfirst(lc); if (!RowMarkRequiresRowShareLock(thiserm->markType)) { - continue; /* ignore non-FOR UPDATE/SHARE items */ + continue; /* 忽略非 FOR UPDATE/SHARE 项 */ } if (RelationGetRelid(thiserm->relation) == table_oid) { @@ -124,15 +126,14 @@ bool execCurrentOf(CurrentOfExpr *cexpr, ExprContext *econtext, Relation relatio } /* - * The cursor must have a current result row: per the SQL spec, it's - * an error if not. + * 游标必须有当前结果行:根据 SQL 规范,如果没有,则出错。 */ if (portal->atStart || portal->atEnd) { ereport(ERROR, (errcode(ERRCODE_INVALID_CURSOR_STATE), errmsg("cursor \"%s\" is not positioned on a row when the cursor uses for UPDATE/SHARE", cursor_name))); } - /* Return the currently scanned TID, if there is one */ + /* 返回当前扫描的 TID,如果存在的话 */ if (ItemPointerIsValid(&(erm->curCtid))) { *current_tid = erm->curCtid; @@ -144,9 +145,7 @@ bool execCurrentOf(CurrentOfExpr *cexpr, ExprContext *econtext, Relation relatio } /* - * This table didn't produce the cursor's current row; some other - * inheritance child of the same parent must have. Signal caller to - * do nothing on this table. + * 这个表没有生成游标的当前行;同一父级的其他继承子表可能已经生成了。告诉调用者在此表上不执行任何操作。 */ return false; } else { @@ -156,9 +155,8 @@ bool execCurrentOf(CurrentOfExpr *cexpr, ExprContext *econtext, Relation relatio ItemPointer tuple_tid; /* - * Without FOR UPDATE, we dig through the cursor's plan to find the - * scan node. Fail if it's not there or buried underneath - * aggregation. + * 在没有 FOR UPDATE 的情况下,我们通过游标的计划来查找扫描节点。 + * 如果节点不存在或被深层聚合包裹,则会失败。 */ scanstate = search_plan_tree(query_desc->planstate, table_oid); if (scanstate == NULL) { @@ -168,23 +166,21 @@ bool execCurrentOf(CurrentOfExpr *cexpr, ExprContext *econtext, Relation relatio } /* - * The cursor must have a current result row: per the SQL spec, it's - * an error if not. We test this at the top level, rather than at the - * scan node level, because in inheritance cases any one table scan - * could easily not be on a row. We want to return false, not raise - * error, if the passed-in table OID is for one of the inactive scans. + * 游标必须有当前结果行:根据 SQL 规范,如果没有,则出错。我们在顶层测试这一点,而不是在扫描节点级别测试, + * 因为在继承情况下,任何一个表扫描都可能轻松不在一行上。我们想在找到非活动扫描的表 OID 时返回 false, + * 而不是引发错误。 */ if (portal->atStart || portal->atEnd) { ereport(ERROR, (errcode(ERRCODE_INVALID_CURSOR_STATE), errmsg( "cursor \"%s\" is not positioned on a row when the cursor doesn't use for UPDATE/SHARE", cursor_name))); } - /* Now OK to return false if we found an inactive scan */ + /* 如果我们找到非活动扫描,则现在可以返回 false */ if (TupIsNull(scanstate->ss_ScanTupleSlot)) { return false; } - /* Use slot_getattr to catch any possible mistakes */ + /* 使用 slot_getattr 捕获任何可能的错误 */ tuple_tableoid = DatumGetObjectId(tableam_tslot_getattr(scanstate->ss_ScanTupleSlot, TableOidAttributeNumber, &lisnull)); Assert(!lisnull); tuple_tid = (ItemPointer)DatumGetPointer( @@ -203,6 +199,7 @@ bool execCurrentOf(CurrentOfExpr *cexpr, ExprContext *econtext, Relation relatio } } + /* * fetch_cursor_param_value * @@ -212,31 +209,35 @@ static char *fetch_cursor_param_value(ExprContext *econtext, int paramId) { ParamListInfo paramInfo = econtext->ecxt_param_list_info; + // 检查参数列表信息是否有效,以及参数标识是否在有效范围内 if (paramInfo && paramId > 0 && paramId <= paramInfo->numParams) { ParamExternData *prm = ¶mInfo->params[paramId - 1]; - /* give hook a chance in case parameter is dynamic */ + // 如果参数的数据类型无效且有参数提取钩子,则尝试提取参数 if (!OidIsValid(prm->ptype) && paramInfo->paramFetch != NULL) { (*paramInfo->paramFetch)(paramInfo, paramId); } + // 如果参数的数据类型有效且不为 NULL if (OidIsValid(prm->ptype) && !prm->isnull) { - /* safety check in case hook did something unexpected */ + // 安全性检查,确保参数的数据类型与准备计划时的数据类型匹配 if (prm->ptype != REFCURSOROID) { ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("type of parameter %d (%s) does not match that when preparing the plan (%s)", paramId, format_type_be(prm->ptype), format_type_be(REFCURSOROID)))); } - /* We know that refcursor uses text's I/O routines */ + // 我们知道 refcursor 使用 text 的 I/O 例程 return TextDatumGetCString(prm->value); } } + // 如果未找到参数值,引发错误 ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("no value found for parameter %d", paramId))); return NULL; } + /* * search_plan_tree * @@ -249,6 +250,7 @@ ScanState* search_plan_tree(PlanState* node, Oid table_oid) static ScanState* search_plan_tree(PlanState* node, Oid table_oid) #endif { + // 如果节点为空,直接返回 NULL if (node == NULL) { return NULL; } @@ -261,15 +263,16 @@ static ScanState* search_plan_tree(PlanState* node, Oid table_oid) return sstate; } #endif - /* - * scan nodes can all be treated alike - */ + /* + * 所有扫描节点都可以被统一对待 + */ case T_SeqScanState: case T_IndexScanState: case T_IndexOnlyScanState: case T_BitmapHeapScanState: case T_TidScanState: { ScanState *sstate = (ScanState *)node; + // 如果当前关系的 OID 与指定的表 OID 匹配,返回当前扫描节点 if (RelationGetRelid(sstate->ss_currentRelation) == table_oid) { return sstate; } @@ -278,15 +281,15 @@ static ScanState* search_plan_tree(PlanState* node, Oid table_oid) case T_ExtensiblePlanState: { ScanState *sstate = (ScanState *)node; ScanState *result = NULL; + // 如果当前关系的 OID 与指定的表 OID 匹配,将结果设置为当前扫描节点 if (RelationGetRelid(sstate->ss_currentRelation) == table_oid) { result = sstate; } return result; } - /* - * For Append, we must look through the members; watch out for - * multiple matches (possible if it was from UNION ALL) - */ + /* + * 对于 Append,我们必须查看其成员;注意可能存在多个匹配项(如果来自 UNION ALL) + */ case T_AppendState: { AppendState *astate = (AppendState *)node; ScanState *result = NULL; @@ -297,15 +300,15 @@ static ScanState* search_plan_tree(PlanState* node, Oid table_oid) if (elem == NULL) continue; if (result != NULL) - return NULL; /* multiple matches */ + return NULL; /* 多个匹配项 */ result = elem; } return result; } - /* - * Similarly for MergeAppend - */ + /* + * 类似于 MergeAppend + */ case T_MergeAppendState: { MergeAppendState *mstate = (MergeAppendState *)node; ScanState *result = NULL; @@ -318,15 +321,14 @@ static ScanState* search_plan_tree(PlanState* node, Oid table_oid) continue; } if (result != NULL) { - return NULL; /* multiple matches */ + return NULL; /* 多个匹配项 */ } result = elem; } return result; } /* - * Result and Limit can be descended through (these are safe - * because they always return their input's current row) + * 对于 Result 和 Limit,可以继续向下查找(这些是安全的,因为它们总是返回其输入的当前行) */ #ifdef PGXC case T_MaterialState: @@ -336,15 +338,16 @@ static ScanState* search_plan_tree(PlanState* node, Oid table_oid) case T_PartIteratorState: return search_plan_tree(node->lefttree, table_oid); - /* - * SubqueryScan too, but it keeps the child in a different place - */ + /* + * SubqueryScan 也可以,但它将子节点保存在不同的位置 + */ case T_SubqueryScanState: return search_plan_tree(((SubqueryScanState *)node)->subplan, table_oid); default: - /* Otherwise, assume we can't descend through it */ + /* 否则,假设我们无法继续向下查找 */ break; } return NULL; } + diff --git a/src/gausskernel/runtime/executor/execGrouping.cpp b/src/gausskernel/runtime/executor/execGrouping.cpp index 505986cfe..cd76c0a7b 100644 --- a/src/gausskernel/runtime/executor/execGrouping.cpp +++ b/src/gausskernel/runtime/executor/execGrouping.cpp @@ -55,17 +55,16 @@ bool execTuplesMatch(TupleTableSlot* slot1, TupleTableSlot* slot2, int numCols, bool result = false; int i; - /* Reset and switch into the temp context. */ + /* 重置并切换到临时内存上下文中 */ MemoryContextReset(evalContext); oldContext = MemoryContextSwitchTo(evalContext); - - /* + /* * We cannot report a match without checking all the fields, but we can * report a non-match as soon as we find unequal fields. So, start * comparing at the last field (least significant sort key). That's the * most likely to be different if we are dealing with sorted input. */ - result = true; + result = true; /* 假设匹配 */ for (i = numCols; --i >= 0;) { AttrNumber att = matchColIdx[i]; @@ -73,31 +72,37 @@ bool execTuplesMatch(TupleTableSlot* slot1, TupleTableSlot* slot2, int numCols, bool isNull1 = false; bool isNull2 = false; + /* 从第一个槽中获取属性值和是否为 NULL */ attr1 = tableam_tslot_getattr(slot1, att, &isNull1); + /* 从第二个槽中获取属性值和是否为 NULL */ attr2 = tableam_tslot_getattr(slot2, att, &isNull2); + /* 如果一个为 NULL,一个不为 NULL,则它们不相等 */ if (isNull1 != isNull2) { - result = false; /* one null and one not; they aren't equal */ + result = false; break; } + /* 如果都为 NULL,则视为相等 */ if (isNull1) { - continue; /* both are null, treat as equal */ + continue; } - /* Apply the type-specific equality function */ + /* 应用特定类型的相等性函数 */ if (!DatumGetBool(FunctionCall2(&eqfunctions[i], attr1, attr2))) { - result = false; /* they aren't equal */ + result = false; /* 它们不相等 */ break; } } + /* 切换回原有内存上下文 */ MemoryContextSwitchTo(oldContext); return result; } + /* * execTuplesUnequal * Return true if two tuples are definitely unequal in the indicated @@ -117,17 +122,16 @@ bool execTuplesUnequal(TupleTableSlot* slot1, TupleTableSlot* slot2, int numCols Assert(slot1->tts_tupleDescriptor->tdTableAmType == slot2->tts_tupleDescriptor->tdTableAmType); - /* Reset and switch into the temp context. */ + /* 重置并切换到临时内存上下文中 */ MemoryContextReset(evalContext); oldContext = MemoryContextSwitchTo(evalContext); /* - * We cannot report a match without checking all the fields, but we can - * report a non-match as soon as we find unequal fields. So, start - * comparing at the last field (least significant sort key). That's the - * most likely to be different if we are dealing with sorted input. + * 我们不能在不检查所有字段的情况下报告匹配,但是如果我们找到不相等的字段,我们可以立即报告不匹配。 + * 因此,从最后一个字段(最不重要的排序键)开始比较。如果处理的是排序输入, + * 则最有可能不相等的字段是最后一个字段。 */ - result = false; + result = false; /* 假设相等 */ for (i = numCols; --i >= 0;) { AttrNumber att = matchColIdx[i]; @@ -135,30 +139,36 @@ bool execTuplesUnequal(TupleTableSlot* slot1, TupleTableSlot* slot2, int numCols bool isNull1 = false; bool isNull2 = false; + /* 从第一个槽中获取属性值和是否为 NULL */ attr1 = tableam_tslot_getattr(slot1, att, &isNull1); + /* 如果为 NULL,则不能判断是否不相等,继续下一个属性的比较 */ if (isNull1) { - continue; /* can't prove anything here */ + continue; } + /* 从第二个槽中获取属性值和是否为 NULL */ attr2 = tableam_tslot_getattr(slot2, att, &isNull2); + /* 如果为 NULL,则不能判断是否不相等,继续下一个属性的比较 */ if (isNull2) { - continue; /* can't prove anything here */ + continue; } - /* Apply the type-specific equality function */ + /* 应用特定类型的相等性函数,如果结果为 false,则它们不相等 */ if (!DatumGetBool(FunctionCall2(&eqfunctions[i], attr1, attr2))) { - result = true; /* they are unequal */ + result = true; /* 它们不相等 */ break; } } + /* 切换回原有内存上下文 */ MemoryContextSwitchTo(oldContext); return result; } + /* * execTuplesMatchPrepare * Look up the equality functions needed for execTuplesMatch or @@ -175,13 +185,17 @@ FmgrInfo* execTuplesMatchPrepare(int numCols, Oid* eqOperators) Oid eq_opr = eqOperators[i]; Oid eq_function; + /* 获取操作符的对应函数的 OID */ eq_function = get_opcode(eq_opr); + + /* 获取函数的详细信息并存储在 FmgrInfo 结构中 */ fmgr_info(eq_function, &eqFunctions[i]); } return eqFunctions; } + /* * execTuplesHashPrepare * Look up the equality and hashing functions needed for a TupleHashTable. @@ -192,10 +206,11 @@ FmgrInfo* execTuplesMatchPrepare(int numCols, Oid* eqOperators) * * Note: we expect that the given operators are not cross-type comparisons. */ -void execTuplesHashPrepare(int numCols, Oid* eqOperators, FmgrInfo** eqFunctions, FmgrInfo** hashFunctions) +void execTuplesHashPrepare(int numCols, Oid* eqOperators, FmgrInfo** eqFunctions, FmgrInfo** hashFunctions)//该函数的主要任务是为每个要哈希的属性找到相应的相等性比较函数和哈希函数,并将这些函数的信息存储在 FmgrInfo 结构数组中。 { int i; + /* 为相等性比较函数和哈希函数分配内存 */ *eqFunctions = (FmgrInfo*)palloc(numCols * sizeof(FmgrInfo)); *hashFunctions = (FmgrInfo*)palloc(numCols * sizeof(FmgrInfo)); @@ -205,7 +220,10 @@ void execTuplesHashPrepare(int numCols, Oid* eqOperators, FmgrInfo** eqFunctions Oid left_hash_function; Oid right_hash_function; + /* 获取操作符的对应函数的 OID */ eq_function = get_opcode(eq_opr); + + /* 获取哈希函数的 OID */ if (!get_op_hash_functions(eq_opr, &left_hash_function, &right_hash_function)) ereport(ERROR, (errmodule(MOD_EXECUTOR), @@ -216,13 +234,16 @@ void execTuplesHashPrepare(int numCols, Oid* eqOperators, FmgrInfo** eqFunctions i, numCols))); - /* We're not supporting cross-type cases here */ + /* 我们不支持跨类型的情况 */ Assert(left_hash_function == right_hash_function); + + /* 获取相等性比较函数和哈希函数的详细信息并存储在 FmgrInfo 结构中 */ fmgr_info(eq_function, &(*eqFunctions)[i]); fmgr_info(right_hash_function, &(*hashFunctions)[i]); } } + /***************************************************************************** * Utility routines for all-in-memory hash tables * @@ -257,28 +278,31 @@ TupleHashTable BuildTupleHashTable(int numCols, AttrNumber* keyColIdx, FmgrInfo* Assert(nbuckets > 0); Assert(entrysize >= sizeof(TupleHashEntryData)); - /* Limit initial table size request to not more than work_mem */ + /* 限制初始表大小请求不超过 work_mem */ nbuckets = Min(nbuckets, (long)((workMem * 1024L) / entrysize)); if (u_sess->attr.attr_sql.hashagg_table_size != 0) nbuckets = Min(nbuckets, u_sess->attr.attr_sql.hashagg_table_size); + /* 分配元组哈希表结构 */ hashtable = (TupleHashTable)MemoryContextAlloc(tablecxt, sizeof(TupleHashTableData)); - hashtable->numCols = numCols; - hashtable->keyColIdx = keyColIdx; - hashtable->tab_hash_funcs = hashfunctions; - hashtable->tab_eq_funcs = eqfunctions; - hashtable->tablecxt = tablecxt; - hashtable->tempcxt = tempcxt; - hashtable->entrysize = entrysize; - hashtable->tableslot = NULL; /* will be made on first lookup */ - hashtable->inputslot = NULL; - hashtable->in_hash_funcs = NULL; - hashtable->cur_eq_funcs = NULL; - hashtable->width = 0; - hashtable->add_width = true; - hashtable->causedBySysRes = false; + /* 初始化元组哈希表的各个字段 */ + hashtable->numCols = numCols; // 哈希键的数量 + hashtable->keyColIdx = keyColIdx; // 哈希键的属性列索引 + hashtable->tab_hash_funcs = hashfunctions; // 哈希函数数组 + hashtable->tab_eq_funcs = eqfunctions; // 相等性比较函数数组 + hashtable->tablecxt = tablecxt; // 元组哈希表内存上下文 + hashtable->tempcxt = tempcxt; // 临时内存上下文 + hashtable->entrysize = entrysize; // 表条目的大小 + hashtable->tableslot = NULL; // 表槽,首次查找时创建 + hashtable->inputslot = NULL; // 输入槽 + hashtable->in_hash_funcs = NULL; // 输入哈希函数数组 + hashtable->cur_eq_funcs = NULL; // 当前相等性比较函数数组 + hashtable->width = 0; // 哈希表宽度 + hashtable->add_width = true; // 是否添加宽度 + hashtable->causedBySysRes = false; // 是否由系统资源引起 + /* 初始化哈希表控制参数 */ errno_t rc = memset_s(&hash_ctl, sizeof(hash_ctl), 0, sizeof(hash_ctl)); securec_check(rc, "\0", "\0"); hash_ctl.keysize = sizeof(TupleHashEntryData); @@ -286,12 +310,15 @@ TupleHashTable BuildTupleHashTable(int numCols, AttrNumber* keyColIdx, FmgrInfo* hash_ctl.hash = TupleHashTableHash; hash_ctl.match = TupleHashTableMatch; hash_ctl.hcxt = tablecxt; + + /* 创建哈希表 */ hashtable->hashtab = hash_create("TupleHashTable", nbuckets, &hash_ctl, HASH_ELEM | HASH_FUNCTION | HASH_COMPARE | HASH_CONTEXT); return hashtable; } + /* * Find or create a hashtable entry for the tuple group containing the * given tuple. The tuple must be the same type as the hashtable entries. @@ -317,29 +344,28 @@ TupleHashEntry LookupTupleHashEntry(TupleHashTable hashtable, TupleTableSlot* sl TupleHashEntryData dummy; bool found = false; - /* If first time through, clone the input slot to make table slot */ + /* 如果是第一次调用,克隆输入槽以创建表槽 */ if (hashtable->tableslot == NULL) { TupleDesc tupdesc; oldContext = MemoryContextSwitchTo(hashtable->tablecxt); /* - * We copy the input tuple descriptor just for safety --- we assume - * all input tuples will have equivalent descriptors. + * 为了安全起见,我们复制输入元组描述符 --- 我们假设所有输入元组都具有等效的描述符。 */ tupdesc = CreateTupleDescCopy(slot->tts_tupleDescriptor); hashtable->tableslot = MakeSingleTupleTableSlot(tupdesc); MemoryContextSwitchTo(oldContext); } - /* Need to run the hash functions in short-lived context */ + /* 需要在临时内存上下文中运行哈希和匹配函数 */ oldContext = MemoryContextSwitchTo(hashtable->tempcxt); /* - * Set up data needed by hash and match functions + * 设置哈希和匹配函数所需的数据 * - * We save and restore u_sess->exec_cxt.cur_tuple_hash_table just in case someone manages to - * invoke this code re-entrantly. + * 为了安全起见,我们保存和恢复 u_sess->exec_cxt.cur_tuple_hash_table,以防某人设法 + * 重入此代码。 */ hashtable->inputslot = slot; hashtable->in_hash_funcs = hashtable->tab_hash_funcs; @@ -348,34 +374,33 @@ TupleHashEntry LookupTupleHashEntry(TupleHashTable hashtable, TupleTableSlot* sl saveCurHT = u_sess->exec_cxt.cur_tuple_hash_table; u_sess->exec_cxt.cur_tuple_hash_table = hashtable; - /* Search the hash table */ - dummy.firstTuple = NULL; /* flag to reference inputslot */ + /* 在哈希表中查找条目 */ + dummy.firstTuple = NULL; /* 用于引用 inputslot */ if (isinserthashtbl) { entry = (TupleHashEntry)hash_search(hashtable->hashtab, &dummy, isnew ? HASH_ENTER : HASH_FIND, &found); } else { - /* this slot will be insert into temp file instead of hash table if it is not found in hash table */ + /* 如果在哈希表中找不到,此槽将被插入到临时文件中而不是哈希表中 */ entry = (TupleHashEntry)hash_search(hashtable->hashtab, &dummy, HASH_FIND, &found); } if (isnew != NULL) { if (found) { - /* found pre-existing entry */ + /* 找到现有条目 */ *isnew = false; } else { if (entry) { Assert(isinserthashtbl); /* - * created new entry + * 创建新条目 * - * Zero any caller-requested space in the entry. (This zaps the - * "key data" dynahash.c copied into the new entry, but we don't - * care since we're about to overwrite it anyway.) + * 在条目中清零任何由调用方请求的空间。 (这会清除 dynahash.c 复制到新条目的 + * "key data" ,但我们不关心,因为我们马上就要覆盖它。) */ errno_t errorno = memset_s(entry, hashtable->entrysize, 0, hashtable->entrysize); securec_check(errorno, "\0", "\0"); - /* Copy the first tuple into the table context */ + /* 将第一个元组复制到表上下文中 */ MemoryContextSwitchTo(hashtable->tablecxt); entry->firstTuple = ExecCopySlotMinimalTuple(slot); if (hashtable->add_width) @@ -393,6 +418,7 @@ TupleHashEntry LookupTupleHashEntry(TupleHashTable hashtable, TupleTableSlot* sl return entry; } + /* * Search for a hashtable entry matching the given tuple. No entry is * created if there's not a match. This is similar to the non-creating @@ -410,14 +436,14 @@ TupleHashEntry FindTupleHashEntry( TupleHashTable saveCurHT; TupleHashEntryData dummy; - /* Need to run the hash functions in short-lived context */ + // 需要在临时内存上下文中运行哈希函数 oldContext = MemoryContextSwitchTo(hashtable->tempcxt); /* - * Set up data needed by hash and match functions + * 设置哈希和匹配函数所需的数据 * - * We save and restore u_sess->exec_cxt.cur_tuple_hash_table just in case someone manages to - * invoke this code re-entrantly. + * 为了安全起见,我们保存和恢复 u_sess->exec_cxt.cur_tuple_hash_table,以防某人设法 + * 重入此代码。 */ hashtable->inputslot = slot; hashtable->in_hash_funcs = hashfunctions; @@ -426,17 +452,23 @@ TupleHashEntry FindTupleHashEntry( saveCurHT = u_sess->exec_cxt.cur_tuple_hash_table; u_sess->exec_cxt.cur_tuple_hash_table = hashtable; - /* Search the hash table */ - dummy.firstTuple = NULL; /* flag to reference inputslot */ + // 创建一个用于查找的 dummy 条目,用于引用 inputslot + dummy.firstTuple = NULL; + + // 在哈希表中查找条目,如果找到,将返回该条目,否则返回 NULL entry = (TupleHashEntry)hash_search(hashtable->hashtab, &dummy, HASH_FIND, NULL); + // 恢复原来的哈希表上下文 u_sess->exec_cxt.cur_tuple_hash_table = saveCurHT; + // 切换回之前的内存上下文 MemoryContextSwitchTo(oldContext); + // 返回查找到的条目或 NULL return entry; } + /* * Compute the hash value for a tuple * @@ -455,38 +487,43 @@ TupleHashEntry FindTupleHashEntry( */ static uint32 TupleHashTableHash(const void* key, Size keysize) { + // 获取条目的 MinimalTuple,这是用于计算哈希值的元组数据 MinimalTuple tuple = ((const TupleHashEntryData*)key)->firstTuple; - TupleTableSlot* slot = NULL; - TupleHashTable hashtable = u_sess->exec_cxt.cur_tuple_hash_table; - int numCols = hashtable->numCols; - AttrNumber* keyColIdx = hashtable->keyColIdx; - FmgrInfo* hashfunctions = NULL; - uint32 hashkey = 0; + + // 初始化变量 + TupleTableSlot* slot = NULL; // 用于存储元组数据的槽 + TupleHashTable hashtable = u_sess->exec_cxt.cur_tuple_hash_table; // 当前的元组哈希表 + int numCols = hashtable->numCols; // 哈希键的列数 + AttrNumber* keyColIdx = hashtable->keyColIdx; // 哈希键的列索引 + FmgrInfo* hashfunctions = NULL; // 用于计算哈希值的哈希函数信息 + uint32 hashkey = 0; // 初始哈希值 int i; if (tuple == NULL) { - /* Process the current input tuple for the table */ + /* 处理哈希表中的当前输入元组 */ slot = hashtable->inputslot; hashfunctions = hashtable->in_hash_funcs; } else { - /* Process a tuple already stored in the table */ - /* (this case never actually occurs in current dynahash.c code) */ + /* 处理已存储在哈希表中的元组 */ + /* (实际上,这种情况在当前的 dynahash.c 代码中不会发生) */ slot = hashtable->tableslot; ExecStoreMinimalTuple(tuple, slot, false); hashfunctions = hashtable->tab_hash_funcs; } - /* Get the Table Accessor Method*/ + /* 循环处理哈希键的每一列 */ for (i = 0; i < numCols; i++) { AttrNumber att = keyColIdx[i]; Datum attr; bool isNull = false; - /* rotate hashkey left 1 bit at each step */ + /* 将哈希键左移 1 位,以便每一列都会影响哈希值的每一位 */ hashkey = (hashkey << 1) | ((hashkey & 0x80000000) ? 1 : 0); + // 获取元组中的属性值 attr = tableam_tslot_getattr(slot, att, &isNull); - /* treat nulls as having hash key 0 */ + + /* 如果属性值不为空,则使用相应的哈希函数计算哈希键的哈希值,并与哈希值合并 */ if (!isNull) { uint32 hkey; hkey = DatumGetUInt32(FunctionCall1(&hashfunctions[i], attr)); @@ -494,11 +531,13 @@ static uint32 TupleHashTableHash(const void* key, Size keysize) } } + // 最后,将哈希值应用 hash_uint32 函数以获得最终的哈希值 hashkey = DatumGetUInt32(hash_uint32(hashkey)); return hashkey; } + /* * See whether two tuples (presumably of the same hash value) match * @@ -512,20 +551,22 @@ static uint32 TupleHashTableHash(const void* key, Size keysize) */ static int TupleHashTableMatch(const void* key1, const void* key2, Size keysize) { + // 获取第一个条目的 MinimalTuple MinimalTuple tuple1 = ((const TupleHashEntryData*)key1)->firstTuple; #ifdef USE_ASSERT_CHECKING + // 获取第二个条目的 MinimalTuple(用于断言检查) MinimalTuple tuple2 = ((const TupleHashEntryData*)key2)->firstTuple; #endif + TupleTableSlot* slot1 = NULL; TupleTableSlot* slot2 = NULL; TupleHashTable hashtable = u_sess->exec_cxt.cur_tuple_hash_table; /* - * We assume that dynahash.c will only ever call us with the first - * argument being an actual table entry, and the second argument being - * LookupTupleHashEntry's dummy TupleHashEntryData. The other direction - * could be supported too, but is not currently used by dynahash.c. + * 我们假设 dynahash.c 只会使用实际的表条目作为第一个参数, + * 并且使用 LookupTupleHashEntry 的虚拟 TupleHashEntryData 作为第二个参数。 + * 反过来的情况也可以支持,但当前 dynahash.c 未使用。 */ Assert(tuple1 != NULL); slot1 = hashtable->tableslot; @@ -533,10 +574,10 @@ static int TupleHashTableMatch(const void* key1, const void* key2, Size keysize) Assert(tuple2 == NULL); slot2 = hashtable->inputslot; - /* For crosstype comparisons, the inputslot must be first */ + /* 对于跨类型比较,输入槽必须位于第一个位置 */ if (execTuplesMatch( slot2, slot1, hashtable->numCols, hashtable->keyColIdx, hashtable->cur_eq_funcs, hashtable->tempcxt)) - return 0; + return 0; // 条目匹配,返回0 else - return 1; + return 1; // 条目不匹配,返回1 } diff --git a/src/gausskernel/runtime/executor/execJunk.cpp b/src/gausskernel/runtime/executor/execJunk.cpp index 96adc29e0..79450a397 100644 --- a/src/gausskernel/runtime/executor/execJunk.cpp +++ b/src/gausskernel/runtime/executor/execJunk.cpp @@ -62,45 +62,44 @@ */ JunkFilter* ExecInitJunkFilter(List* targetList, bool hasoid, TupleTableSlot* slot, TableAmType tam) { - JunkFilter* junkfilter = NULL; - TupleDesc cleanTupType; - int cleanLength; - AttrNumber* cleanMap = NULL; - ListCell* t = NULL; - AttrNumber cleanResno; + JunkFilter* junkfilter = NULL; // 用于存储 JunkFilter 结构体的指针 + TupleDesc cleanTupType; // 存储清理后的元组描述符 + int cleanLength; // 清理后的元组的长度 + AttrNumber* cleanMap = NULL; // 清理后的属性映射 + ListCell* t = NULL; // 遍历 targetList 的 ListCell 指针 + AttrNumber cleanResno; // 清理后的属性编号 /* - * Compute the tuple descriptor for the cleaned tuple. + * 计算清理后元组的元组描述符。 */ cleanTupType = ExecCleanTypeFromTL(targetList, hasoid, tam); /* - * Use the given slot, or make a new slot if we weren't given one. + * 使用给定的槽位(slot),或者如果未提供槽位,则创建一个新槽位。 */ if (slot != NULL) ExecSetSlotDescriptor(slot, cleanTupType); else slot = MakeSingleTupleTableSlot(cleanTupType); - - /* - * Now calculate the mapping between the original tuple's attributes and - * the "clean" tuple's attributes. - * - * The "map" is an array of "cleanLength" attribute numbers, i.e. one - * entry for every attribute of the "clean" tuple. The value of this entry - * is the attribute number of the corresponding attribute of the - * "original" tuple. (Zero indicates a NULL output attribute, but we do - * not use that feature in this routine.) - */ - cleanLength = cleanTupType->natts; +   /* +     * Now calculate the mapping between the original tuple's attributes and +     * the "clean" tuple's attributes. +     * +     * The "map" is an array of "cleanLength" attribute numbers, i.e. one +     * entry for every attribute of the "clean" tuple. The value of this entry +     * is the attribute number of the corresponding attribute of the +     * "original" tuple.  (Zero indicates a NULL output attribute, but we do +     * not use that feature in this routine.) +     */ + cleanLength = cleanTupType->natts; // 清理后的元组的属性数目 if (cleanLength > 0) { - cleanMap = (AttrNumber*)palloc(cleanLength * sizeof(AttrNumber)); - cleanResno = 1; + cleanMap = (AttrNumber*)palloc(cleanLength * sizeof(AttrNumber)); // 分配清理后的属性映射数组的内存空间 + cleanResno = 1; // 初始化清理后的属性编号 foreach (t, targetList) { TargetEntry* tle = (TargetEntry*)lfirst(t); if (!tle->resjunk) { - cleanMap[cleanResno - 1] = tle->resno; + cleanMap[cleanResno - 1] = tle->resno; // 映射属性编号 cleanResno++; } } @@ -109,18 +108,19 @@ JunkFilter* ExecInitJunkFilter(List* targetList, bool hasoid, TupleTableSlot* sl } /* - * Finally create and initialize the JunkFilter struct. + * 最后创建并初始化 JunkFilter 结构体。 */ - junkfilter = makeNode(JunkFilter); + junkfilter = makeNode(JunkFilter); // 创建一个 JunkFilter 结构体 - junkfilter->jf_targetList = targetList; - junkfilter->jf_cleanTupType = cleanTupType; - junkfilter->jf_cleanMap = cleanMap; - junkfilter->jf_resultSlot = slot; + junkfilter->jf_targetList = targetList; // 设置目标列表 + junkfilter->jf_cleanTupType = cleanTupType; // 设置清理后的元组描述符 + junkfilter->jf_cleanMap = cleanMap; // 设置清理后的属性映射 + junkfilter->jf_resultSlot = slot; // 设置结果槽位 - return junkfilter; + return junkfilter; // 返回初始化后的 JunkFilter 结构体指针 } + /* * ExecInitJunkFilterConversion * @@ -133,14 +133,14 @@ JunkFilter* ExecInitJunkFilter(List* targetList, bool hasoid, TupleTableSlot* sl */ JunkFilter* ExecInitJunkFilterConversion(List* targetList, TupleDesc cleanTupType, TupleTableSlot* slot) { - JunkFilter* junkfilter = NULL; - int cleanLength; - AttrNumber* cleanMap = NULL; - ListCell* t = NULL; + JunkFilter* junkfilter = NULL; // 用于存储 JunkFilter 结构体的指针 + int cleanLength; // 清理后的元组的长度 + AttrNumber* cleanMap = NULL; // 清理后的属性映射 + ListCell* t = NULL; // 遍历 targetList 的 ListCell 指针 int i; /* - * Use the given slot, or make a new slot if we weren't given one. + * 使用给定的槽位(slot),或者如果未提供槽位,则创建一个新槽位。 */ if (slot != NULL) ExecSetSlotDescriptor(slot, cleanTupType); @@ -148,28 +148,25 @@ JunkFilter* ExecInitJunkFilterConversion(List* targetList, TupleDesc cleanTupTyp slot = MakeSingleTupleTableSlot(cleanTupType); /* - * Calculate the mapping between the original tuple's attributes and the - * "clean" tuple's attributes. + * 计算原始元组属性与“清理”元组属性之间的映射关系。 * - * The "map" is an array of "cleanLength" attribute numbers, i.e. one - * entry for every attribute of the "clean" tuple. The value of this entry - * is the attribute number of the corresponding attribute of the - * "original" tuple. We store zero for any deleted attributes, marking - * that a NULL is needed in the output tuple. + * “映射”是一个数组,包含“cleanLength”属性编号,即“清理”元组的每个属性对应一个条目。 + * 此条目的值是“原始”元组对应属性的属性编号。 + * 对于任何已删除的属性,我们存储零,表示在输出元组中需要一个 NULL。 */ cleanLength = cleanTupType->natts; if (cleanLength > 0) { - cleanMap = (AttrNumber*)palloc0(cleanLength * sizeof(AttrNumber)); + cleanMap = (AttrNumber*)palloc0(cleanLength * sizeof(AttrNumber)); // 分配清理后的属性映射数组的内存空间 t = list_head(targetList); for (i = 0; i < cleanLength; i++) { if (cleanTupType->attrs[i]->attisdropped) - continue; /* map entry is already zero */ + continue; /* 映射条目已经是零 */ for (;;) { TargetEntry* tle = (TargetEntry*)lfirst(t); t = lnext(t); if (!tle->resjunk) { - cleanMap[i] = tle->resno; + cleanMap[i] = tle->resno; // 映射属性编号 break; } } @@ -179,16 +176,16 @@ JunkFilter* ExecInitJunkFilterConversion(List* targetList, TupleDesc cleanTupTyp } /* - * Finally create and initialize the JunkFilter struct. + * 最后创建并初始化 JunkFilter 结构体。 */ - junkfilter = makeNode(JunkFilter); + junkfilter = makeNode(JunkFilter); // 创建一个 JunkFilter 结构体 - junkfilter->jf_targetList = targetList; - junkfilter->jf_cleanTupType = cleanTupType; - junkfilter->jf_cleanMap = cleanMap; - junkfilter->jf_resultSlot = slot; + junkfilter->jf_targetList = targetList; // 设置目标列表 + junkfilter->jf_cleanTupType = cleanTupType; // 设置清理后的元组描述符 + junkfilter->jf_cleanMap = cleanMap; // 设置清理后的属性映射 + junkfilter->jf_resultSlot = slot; // 设置结果槽位 - return junkfilter; + return junkfilter; // 返回初始化后的 JunkFilter 结构体指针 } /* @@ -210,21 +207,24 @@ AttrNumber ExecFindJunkAttribute(JunkFilter* junkfilter, const char* attrName) */ List* ExecFindJunkPrimaryKeys(List* targetlist) { - List* jk_primary_keys = NIL; - ListCell* cell = NULL; + List* jk_primary_keys = NIL; // 用于存储 "xc_primary_key" 属性的表达式列表 + ListCell* cell = NULL; // 遍历目标列表的 ListCell 指针 foreach (cell, targetlist) { - TargetEntry* tle = (TargetEntry*)lfirst(cell); + TargetEntry* tle = (TargetEntry*)lfirst(cell); // 获取目标列表中的当前元素 + // 检查是否是垃圾属性(resjunk 为 true)、是否有属性名(resname 不为空), + // 并且属性名为 "xc_primary_key" if (tle->resjunk && tle->resname && (strcmp(tle->resname, "xc_primary_key") == 0)) { - /* We found it ! */ + /* 找到了 "xc_primary_key" 属性!将其表达式添加到 jk_primary_keys 列表中 */ jk_primary_keys = lappend(jk_primary_keys, tle->expr); } } - return jk_primary_keys; + return jk_primary_keys; // 返回包含 "xc_primary_key" 属性表达式的列表 } + /* * ExecFindJunkAttributeInTlist * @@ -233,20 +233,23 @@ List* ExecFindJunkPrimaryKeys(List* targetlist) */ AttrNumber ExecFindJunkAttributeInTlist(List* targetlist, const char* attrName) { - ListCell* t = NULL; + ListCell* t = NULL; // 用于遍历目标列表的 ListCell 指针 foreach (t, targetlist) { - TargetEntry* tle = (TargetEntry*)lfirst(t); + TargetEntry* tle = (TargetEntry*)lfirst(t); // 获取目标列表中的当前元素 + // 检查是否是垃圾属性(resjunk 为 true)、是否有属性名(resname 不为空), + // 并且属性名与指定的 attrName 匹配 if (tle->resjunk && tle->resname && (strcmp(tle->resname, attrName) == 0)) { - /* We found it ! */ + /* 找到了指定名称的垃圾属性!返回该属性的属性号(resno) */ return tle->resno; } } - return InvalidAttrNumber; + return InvalidAttrNumber; // 如果未找到指定名称的垃圾属性,返回无效属性号 } + /* * ExecGetJunkAttribute * @@ -256,12 +259,14 @@ AttrNumber ExecFindJunkAttributeInTlist(List* targetlist, const char* attrName) */ Datum ExecGetJunkAttribute(TupleTableSlot* slot, AttrNumber attno, bool* isNull) { - Assert(attno > 0); - Assert(slot != NULL); + Assert(attno > 0); // 确保属性号大于 0 + Assert(slot != NULL); // 确保提供了有效的 TupleTableSlot + // 使用 tableam_tslot_getattr 函数从 TupleTableSlot 中获取指定属性的值和是否为 NULL return tableam_tslot_getattr(slot, attno, isNull); } + /* * ExecFilterJunk * @@ -269,43 +274,43 @@ Datum ExecGetJunkAttribute(TupleTableSlot* slot, AttrNumber attno, bool* isNull) */ TupleTableSlot* ExecFilterJunk(JunkFilter* junkfilter, TupleTableSlot* slot) { - TupleTableSlot* resultSlot = NULL; - AttrNumber* cleanMap = NULL; - TupleDesc cleanTupType; - int cleanLength; + TupleTableSlot* resultSlot = NULL; // 存储结果的 TupleTableSlot + AttrNumber* cleanMap = NULL; // 清洗属性映射 + TupleDesc cleanTupType; // 清洗后的元组描述 + int cleanLength; // 清洗后的属性数量 int i; - Datum* values = NULL; - bool* isnull = NULL; - Datum* old_values = NULL; - bool* old_isnull = NULL; + Datum* values = NULL; // 存储属性值的数组 + bool* isnull = NULL; // 属性是否为 NULL 的标志数组 + Datum* old_values = NULL; // 原始元组的属性值数组 + bool* old_isnull = NULL; // 原始元组的属性是否为 NULL 的标志数组 /* - * Extract all the values of the old tuple. + * 提取原始元组的所有属性值和是否为 NULL 标志。 */ - /* Get the Table Accessor Method*/ + /* 获取表访问方法的元组描述 */ Assert(slot != NULL && slot->tts_tupleDescriptor != NULL); tableam_tslot_getallattrs(slot); old_values = slot->tts_values; old_isnull = slot->tts_isnull; /* - * get info from the junk filter + * 从 JunkFilter 中获取相关信息 */ - cleanTupType = junkfilter->jf_cleanTupType; - cleanLength = cleanTupType->natts; - cleanMap = junkfilter->jf_cleanMap; - resultSlot = junkfilter->jf_resultSlot; + cleanTupType = junkfilter->jf_cleanTupType; // 清洗后的元组描述 + cleanLength = cleanTupType->natts; // 清洗后的属性数量 + cleanMap = junkfilter->jf_cleanMap; // 清洗属性映射 + resultSlot = junkfilter->jf_resultSlot; // 存储结果的 TupleTableSlot /* - * Prepare to build a virtual result tuple. + * 准备构建虚拟结果元组。 */ (void)ExecClearTuple(resultSlot); - values = resultSlot->tts_values; - isnull = resultSlot->tts_isnull; + values = resultSlot->tts_values; // 存储属性值的数组 + isnull = resultSlot->tts_isnull; // 属性是否为 NULL 的标志数组 /* - * Transpose data into proper fields of the new tuple. + * 将数据转置到新元组的适当字段中。 */ for (i = 0; i < cleanLength; i++) { int j = cleanMap[i]; @@ -320,11 +325,12 @@ TupleTableSlot* ExecFilterJunk(JunkFilter* junkfilter, TupleTableSlot* slot) } /* - * And return the virtual tuple. + * 返回虚拟元组。 */ return ExecStoreVirtualTuple(resultSlot); } + /* * BatchExecFilterJunk * @@ -332,58 +338,61 @@ TupleTableSlot* ExecFilterJunk(JunkFilter* junkfilter, TupleTableSlot* slot) */ VectorBatch* BatchExecFilterJunk(_in_ JunkFilter* junkfilter, __inout VectorBatch* batch) { - AttrNumber* cleanMap = NULL; - TupleDesc cleanTupType; - int cleanLength; + AttrNumber* cleanMap = NULL; // 清洗属性映射 + TupleDesc cleanTupType; // 清洗后的元组描述 + int cleanLength; // 清洗后的属性数量 int i; - ScalarVector* columns = NULL; + ScalarVector* columns = NULL; // 列数据 - // Get info from the junk filter + // 从 JunkFilter 中获取相关信息 // - cleanTupType = junkfilter->jf_cleanTupType; - cleanLength = cleanTupType->natts; - cleanMap = junkfilter->jf_cleanMap; - columns = batch->m_arr; + cleanTupType = junkfilter->jf_cleanTupType; // 清洗后的元组描述 + cleanLength = cleanTupType->natts; // 清洗后的属性数量 + cleanMap = junkfilter->jf_cleanMap; // 清洗属性映射 + columns = batch->m_arr; // 列数据 - // Transpose data into proper fields of the new tuple. + // 将数据转置到新元组的适当字段中。 // for (i = 0; i < cleanLength; i++) { int j = cleanMap[i]; if (j == 0) { + // 如果清洗属性对应的映射值为0,表示该属性为垃圾属性,需要设置为 NULL for (int k = 0; k < columns[i].m_rows; k++) { columns[i].SetNull(k); } } else { + // 否则,将属性的数据从对应的位置复制到新的位置 columns[i] = columns[j - 1]; } } - // Return the modified batch without changing the column count - // as the column count is early decided at compile time. + // 返回修改后的批处理数据,列数不变,因为列数在编译时已经确定。 // return batch; } -void ExecSetjunkFilteDescriptor(JunkFilter* junkfilter, TupleDesc tupdesc) + +void ExecSetJunkFilterDescriptor(JunkFilter* junkfilter, TupleDesc tupdesc) { - TupleDesc resultslotTupType; - AttrNumber* cleanMap = NULL; - int cleanLength; + TupleDesc resultslotTupType; // 用于存储结果集的TupleDesc + AttrNumber* cleanMap = NULL; // 存储属性映射关系的数组 + int cleanLength; // 属性映射的长度 int i; - cleanLength = junkfilter->jf_cleanTupType->natts; - cleanMap = junkfilter->jf_cleanMap; + cleanLength = junkfilter->jf_cleanTupType->natts; // 获取属性映射关系的长度 + cleanMap = junkfilter->jf_cleanMap; // 获取属性映射关系数组 - resultslotTupType = junkfilter->jf_resultSlot->tts_tupleDescriptor; + resultslotTupType = junkfilter->jf_resultSlot->tts_tupleDescriptor; // 获取结果集的TupleDesc /* - * Transpose tupdesc into proper fields of the new tupdesc. + * 将tupdesc的属性映射到新的tupdesc中。 */ for (i = 0; i < cleanLength; i++) { - int j = cleanMap[i]; + int j = cleanMap[i]; // 获取属性映射关系中的目标位置 if (j > 0) resultslotTupType->attrs[i]->atttypid = tupdesc->attrs[j - 1]->atttypid; + // 将目标位置上的属性类型ID更新为tupdesc中相应位置上的属性类型ID } } @@ -396,18 +405,24 @@ void ExecSetjunkFilteDescriptor(JunkFilter* junkfilter, TupleDesc tupdesc) */ void BatchCheckNodeIdentifier(JunkFilter* junkfilter, VectorBatch* batch) { - ScalarVector* xc_node_id_col = NULL; - uint32 xc_node_id = 0; - int counter = 0; + ScalarVector* xc_node_id_col = NULL; // 用于存储节点标识符列 + uint32 xc_node_id = 0; // 用于存储节点标识符的临时变量 + int counter = 0; // 用于循环计数的变量 + // 检查是否需要进行节点标识符的检查 if (InvalidAttrNumber == junkfilter->jf_xc_node_id) { - return; + return; // 如果不需要检查节点标识符,直接返回 } + // 获取节点标识符列 xc_node_id_col = &(batch->m_arr[junkfilter->jf_xc_node_id - 1]); + // 遍历节点标识符列中的每一行 for (counter = 0; counter < xc_node_id_col->m_rows; counter++) { + // 获取当前行的节点标识符值,并将其转换为无符号整数类型 xc_node_id = DatumGetUInt32(xc_node_id_col->m_vals[counter]); + + // 检查当前节点的标识符是否与批处理中的值匹配,如果不匹配则抛出错误 if (u_sess->pgxc_cxt.PGXCNodeIdentifier != xc_node_id) { ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), diff --git a/src/gausskernel/security/gs_ledger/blockchain.cpp b/src/gausskernel/security/gs_ledger/blockchain.cpp index 23c0d946f..4a281fcd5 100644 --- a/src/gausskernel/security/gs_ledger/blockchain.cpp +++ b/src/gausskernel/security/gs_ledger/blockchain.cpp @@ -41,8 +41,10 @@ * * Note: globalhash is generated by operate info and previous globalhash using md5. */ + // 生成全局链的哈希值 bool gen_global_hash(hash32_t *hash_buffer, const char *info_string, bool exist, const hash32_t *prev_hash) { + // 错误代码处理变量 errno_t rc = EOK; int comb_strlen; char *comb_string = NULL; @@ -52,7 +54,9 @@ bool gen_global_hash(hash32_t *hash_buffer, const char *info_string, bool exist, * generation. If previous block exists, we will use previous global * hash as combine string to calculate globalhash. */ + // 如果前一个块不存在,则使用全局系统表名作为生成哈希值的组合字符串 if (!exist) { + // 生成创世块的全局哈希 /* generate genesis block globalhash */ comb_strlen = strlen(GCHAIN_NAME) + strlen(info_string) + 1; comb_string = (char *)palloc0(comb_strlen); @@ -60,6 +64,7 @@ bool gen_global_hash(hash32_t *hash_buffer, const char *info_string, bool exist, securec_check_ss(rc, "", ""); } else { /* use previous globalhash and current block info to calculate globalhash. */ + // 使用前一个全局哈希和当前块信息生成全局哈希 char *pre_hash_str = DatumGetCString(DirectFunctionCall1(hash32out, HASH32GetDatum(prev_hash))); comb_strlen = strlen(pre_hash_str) + strlen(info_string) + 1; comb_string = (char *)palloc0(comb_strlen); @@ -67,7 +72,7 @@ bool gen_global_hash(hash32_t *hash_buffer, const char *info_string, bool exist, securec_check_ss(rc, "", ""); pfree_ext(pre_hash_str); } - +// 使用 md5 函数生成哈希值 if (!pg_md5_binary(comb_string, comb_strlen - 1, hash_buffer->data)) { pfree(comb_string); ereport(ERROR, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("Failed to generate globalhash, out of memory"))); @@ -87,14 +92,18 @@ bool gen_global_hash(hash32_t *hash_buffer, const char *info_string, bool exist, * cmd_text: the command query which modified user table. * rel_hash: rel_hash of current block. */ + // 组合块信息 char *set_gchain_comb_string(const char *db_name, const char *user_name, const char *nsp_name, const char *rel_name, const char *cmd_text, uint64 rel_hash) { + // 如果命令文本为空,则设置为空字符串 if (cmd_text == NULL) { cmd_text = ""; } + // 计算组合字符串长度 int comb_len = strlen(db_name) + strlen(user_name) + strlen(nsp_name) + strlen(rel_name) + strlen(cmd_text) + PREVIOUS_HASH_LEN + 1; + // 分配内存并填充组合字符串 char *comb_str = (char *)palloc0(sizeof(char) * comb_len); errno_t rc = snprintf_s(comb_str, comb_len, comb_len - 1, "%s%s%s%s%s%lu", db_name, user_name, nsp_name, rel_name, cmd_text, rel_hash); @@ -113,8 +122,10 @@ char *set_gchain_comb_string(const char *db_name, const char *user_name, * into gchain cache for next block. Thus, previous global hash is * come from cache directly. */ + // 向全局链追加块记录的函数 void ledger_gchain_append(Oid relid, const char *query_string, uint64 cn_hash) { + // 定义变量 Datum current_time; Datum values[Natts_gs_global_chain] = {0}; bool nulls[Natts_gs_global_chain] = {false}; @@ -128,6 +139,7 @@ void ledger_gchain_append(Oid relid, const char *query_string, uint64 cn_hash) GlobalPrevBlock current_block; /* get basic informations. */ + // 获取基本信息 db_name = get_database_name(u_sess->proc_cxt.MyDatabaseId); user_name = GetUserNameFromId(GetCurrentUserId()); current_time = TimestampTzGetDatum(GetCurrentTimestamp()); @@ -135,15 +147,17 @@ void ledger_gchain_append(Oid relid, const char *query_string, uint64 cn_hash) rel_name = get_rel_name(relid); /* Make combine string of current record: rel_name + nsp_name + query_string + rel_hash */ + // 创建当前记录的组合字符串 combine_string = set_gchain_comb_string(db_name, user_name, nsp_name, rel_name, query_string, cn_hash); /* * rel_hash: sum of hash in DN which generated by this query_string. * globalhash: hash for last record of gs_global_chain, it means blockchain prevhash. */ + // 计算当前块的全局哈希 current_block.blocknum = get_next_g_blocknum(); gen_global_hash(¤t_block.globalhash, combine_string, false, NULL); - +// 填充插入记录的值 values[Anum_gs_global_chain_blocknum - 1] = UInt64GetDatum(current_block.blocknum); values[Anum_gs_global_chain_dbname - 1] = DirectFunctionCall1(namein, CStringGetDatum(db_name)); values[Anum_gs_global_chain_username - 1] = DirectFunctionCall1(namein, CStringGetDatum(user_name)); @@ -154,7 +168,7 @@ void ledger_gchain_append(Oid relid, const char *query_string, uint64 cn_hash) values[Anum_gs_global_chain_relhash - 1] = UInt64GetDatum(cn_hash); values[Anum_gs_global_chain_globalhash - 1] = HASH32GetDatum(¤t_block.globalhash); values[Anum_gs_global_chain_txcommand - 1] = CStringGetTextDatum(query_string); - +// 打开全局链表并插入记录 rel_gchain = heap_open(GsGlobalChainRelationId, RowExclusiveLock); tup = heap_form_tuple(rel_gchain->rd_att, values, nulls); @@ -162,6 +176,7 @@ void ledger_gchain_append(Oid relid, const char *query_string, uint64 cn_hash) heap_freetuple(tup); /* set latest previous global chain block */ + // 设置最新的全局链块 heap_close(rel_gchain, RowExclusiveLock); pfree(combine_string); } @@ -173,12 +188,14 @@ void ledger_gchain_append(Oid relid, const char *query_string, uint64 cn_hash) * operation: command operation. * hash: the hash that prepare to append. */ + // 向响应标签追加哈希值的函数 static void ledger_output_append_hash(char *resp_tag, CmdType operation, uint64 hash) { + // 断言确保响应标签不为空 Assert(resp_tag != NULL); size_t len = strlen(resp_tag); errno_t ret = EOK; - +// 根据命令类型追加哈希值 switch (operation) { case CMD_INSERT: case CMD_UPDATE: @@ -206,11 +223,14 @@ static void ledger_ExecutorEnd(QueryDesc *query_desc) { uint64 hashsum; bool has_remote_hash = query_desc->estate->es_modifiedRowHash != NIL; + // 计算 es_modifiedRowHash 中所有哈希的组合哈希值 hashsum = hash_combiner(query_desc->estate->es_modifiedRowHash); + // 如果当前节点是协调器或单节点,并且存在远程哈希,则继续执行以下操作 if ((IS_PGXC_COORDINATOR || g_instance.role == VSINGLENODE) && has_remote_hash) { Oid relid = InvalidOid; Relation rel = NULL; int relnum = query_desc->estate->es_num_result_relations; + // 如果存在结果关系,则获取结果关系描述 if (relnum > 0) { rel = query_desc->estate->es_result_relations->ri_RelationDesc; /* gs_global_chain only records following actions */ @@ -219,6 +239,7 @@ static void ledger_ExecutorEnd(QueryDesc *query_desc) case CMD_DELETE: case CMD_UPDATE: relid = RelationGetRelid(rel); + // 如果关系是区块链表,则将块追加到 gs_global_chain if (rel->rd_isblockchain) { ledger_gchain_append(relid, query_desc->sourceText, hashsum); } @@ -228,11 +249,12 @@ static void ledger_ExecutorEnd(QueryDesc *query_desc) } } } - +// 如果存在要返回的响应标签,并且存在远程哈希,并且连接不是来自应用程序,则将哈希追加到响应标签 if (u_sess->ledger_cxt.resp_tag != NULL && has_remote_hash && !IsConnFromApp()) { ledger_output_append_hash(u_sess->ledger_cxt.resp_tag, query_desc->operation, hashsum); u_sess->ledger_cxt.resp_tag = NULL; } + // 如果存在前一个 ExecutorEnd 钩子函数,则执行它,否则执行标准的 ExecutorEnd 函数 if (t_thrd.security_ledger_cxt.prev_ExecutorEnd) { ((ExecutorEnd_hook_type)t_thrd.security_ledger_cxt.prev_ExecutorEnd)(query_desc); } else { @@ -251,16 +273,19 @@ static void ledger_ExecutorEnd(QueryDesc *query_desc) */ void light_ledger_ExecutorEnd(Query *query, uint64 relhash) { + // 检查当前节点是否为协调器或者单节点(VSINGLENODE),如果不是,则直接返回,不执行后续操作。 if (!IS_PGXC_COORDINATOR && g_instance.role != VSINGLENODE) { return; } Oid relid = InvalidOid; - + // 根据查询类型(命令类型)执行不同的操作。 switch (query->commandType) { case CMD_INSERT: case CMD_DELETE: case CMD_UPDATE: + // 获取目标查询中关系的 OID。 relid = get_target_query_relid(query->rtable, query->resultRelation); + // 检查该关系是否为区块链用户表,如果是,则将块追加到 gs_global_chain。 if (is_ledger_usertable(relid)) { ledger_gchain_append(relid, query->sql_statement, relhash); } @@ -270,7 +295,9 @@ void light_ledger_ExecutorEnd(Query *query, uint64 relhash) break; } } - +//ight_ledger_ExecutorEnd,用于在轻量级代理中记录块到 gs_global_chain。它接收一个查询对象 query 和一个关系哈希值 relhash 作为参数。 +//如果当前节点不是协调器且不是单节点(VSINGLENODE),则直接返回,不执行后续操作。否则,根据查询的类型(命令类型),执行以下操作: +//如果是插入(CMD_INSERT)、删除(CMD_DELETE)或更新(CMD_UPDATE)操作,则获取查询中目标关系的 OID,并检查是否是区块链用户表,如果是,则将块追加到 gs_global_chain。 /* * light_ledger_ExecutorEnd -- record block to gchain in opfusion. * @@ -281,14 +308,16 @@ void light_ledger_ExecutorEnd(Query *query, uint64 relhash) */ void opfusion_ledger_ExecutorEnd(FusionType fusiontype, Oid relid, const char *query, uint64 relhash) { + // 如果当前节点是数据节点(VDATANODE)或者关系不是区块链用户表,则直接返回,不执行后续操作。 if (g_instance.role == VDATANODE || !is_ledger_usertable(relid)) { return; } - + // 根据操作类型(融合类型)执行不同的操作。 switch (fusiontype) { case INSERT_FUSION: case UPDATE_FUSION: case DELETE_FUSION: + // 如果关系是区块链用户表,则将块追加到 gs_global_chain。 if (is_ledger_usertable(relid)) { ledger_gchain_append(relid, query, relhash); } @@ -298,16 +327,23 @@ void opfusion_ledger_ExecutorEnd(FusionType fusiontype, Oid relid, const char *q break; } } +//opfusion_ledger_ExecutorEnd,用于在操作融合(opfusion)中记录块到 gs_global_chain。它接收操作类型 fusiontype、用户表的 OID relid、修改用户表的原始查询 query 和源文本生成的关系哈希值 relhash 作为参数。 +//如果当前节点是数据节点(VDATANODE)或关系不是区块链用户表,则直接返回,不执行后续操作。否则,根据融合操作类型执行以下操作: +// 如果是插入融合(INSERT_FUSION)、更新融合(UPDATE_FUSION)或删除融合(DELETE_FUSION)操作,并且关系是区块链用户表,则将块追加到 gs_global_chain。 + + /* * ledger_hook_init -- install of gchain block record hook. */ void ledger_hook_init(void) { + // 保存先前的 ExecutorEnd_hook 函数到 prev_ExecutorEnd 中。 t_thrd.security_ledger_cxt.prev_ExecutorEnd = (void *)ExecutorEnd_hook; + // 将 ExecutorEnd_hook 设置为 ledger_ExecutorEnd 函数,以便在查询执行结束时记录块到 gs_global_chain。 ExecutorEnd_hook = ledger_ExecutorEnd; } - +//ledger_hook_init,用于初始化 gs_global_chain 块记录的挂钩函数。首先,它保存先前的 ExecutorEnd_hook 函数到 prev_ExecutorEnd 中。然后,将 ExecutorEnd_hook 设置为 ledger_ExecutorEnd 函数,以便在查询执行结束时记录块到 gs_global_chain。 /* * ledger_hook_fini -- uninstall of gchain block record hook. */ @@ -315,3 +351,5 @@ void ledger_hook_fini(void) { ExecutorEnd_hook = (ExecutorEnd_hook_type)t_thrd.security_ledger_cxt.prev_ExecutorEnd; } +//ledger_hook_fini,用于卸载 gs_global_chain 块记录的挂钩函数。它将 ExecutorEnd_hook 恢复为先前保存的 prev_ExecutorEnd 函数,以取消块记录的挂钩功能。 +//总结函数功能:这一系列函数用于在不同的执行上下文中记录块到 gs_global_chain。具体地,它们根据查询类型和节点角色来确定是否记录块,并将块的相关信息追加到 gs_global_chain 中。函数 ledger_hook_init 和 ledger_hook_fini 用于初始化和卸载块记录的挂钩函数。 \ No newline at end of file diff --git a/src/gausskernel/security/gs_ledger/ledger_archive.cpp b/src/gausskernel/security/gs_ledger/ledger_archive.cpp index 62647711b..051c8d0ec 100644 --- a/src/gausskernel/security/gs_ledger/ledger_archive.cpp +++ b/src/gausskernel/security/gs_ledger/ledger_archive.cpp @@ -55,18 +55,23 @@ */ static void prepare_histback_dir(void) { + // 准备历史归档目录 char ledger_histback_dir[MAXPGPATH] = {0}; + // 声明一个存储目录路径的字符数组。 int rc = snprintf_s(ledger_histback_dir, MAXPGPATH, MAXPGPATH - 1, "%s/hist_bak", g_instance.attr.attr_security.Audit_directory); securec_check_ss(rc, "\0", "\0"); - + // 使用 snprintf_s 函数构建目录路径,并检查是否出现错误。 /* * Create histback directory if not present; ignore errors */ (void)pg_mkdir_p(g_instance.attr.attr_security.Audit_directory, S_IRWXU); (void)pg_mkdir_p(ledger_histback_dir, S_IRWXU); + // 创建目录,这里使用了 PostgreSQL 提供的创建目录的函数 pg_mkdir_p。 } - +//prepare_histback_dir 函数:功能为准备历史归档目录; +//具体步骤:1构建历史归档目录的路径。2使用 pg_mkdir_p 函数创建历史归档目录。 +//作用:确保历史数据归档的目录已经存在,如果不存在则创建。 /* * ledger_copytable -- copy rows of hist table. * @@ -78,6 +83,7 @@ static void prepare_histback_dir(void) */ static uint64 ledger_copytable(CopyState cstate) { + // 复制表中的数据 Relation cur_rel; TupleDesc tuple_desc; Form_pg_attribute *attr = NULL; @@ -85,13 +91,16 @@ static uint64 ledger_copytable(CopyState cstate) int num_phys_attrs; uint64 processed = 0; bool is_gchain; + // 声明一些变量用于存储表的信息和处理数据。 cur_rel = cstate->curPartionRel; is_gchain = RelationGetRelid(cur_rel) == GsGlobalChainRelationId; + // 获取当前处理的表,并检查是否为全局链表。 tuple_desc = RelationGetDescr(cur_rel); attr = tuple_desc->attrs; num_phys_attrs = tuple_desc->natts; cstate->null_print_client = cstate->null_print; + // 获取表的描述信息,包括属性和属性数量,并设置一些用于打印 NULL 值的变量。 /* We use fe_msgbuf as a per-row buffer regardless of copy_dest */ if (cstate->fe_msgbuf == NULL) { @@ -99,7 +108,7 @@ static uint64 ledger_copytable(CopyState cstate) if (IS_PGXC_COORDINATOR || g_instance.role == VSINGLENODE) ProcessFileHeader(cstate); } - + // 如果消息缓冲区为空,则创建一个,并在特定条件下处理文件头。 /* For each column type, get its out function. */ cstate->out_functions = (FmgrInfo*)palloc(num_phys_attrs * sizeof(FmgrInfo)); foreach (cur, cstate->attnumlist) { @@ -109,6 +118,7 @@ static uint64 ledger_copytable(CopyState cstate) getTypeOutputInfo(attr[attnum - 1]->atttypid, &out_func_oid, &isvarlena); fmgr_info(out_func_oid, &cstate->out_functions[attnum - 1]); } + // 为输出函数分配内存并填充函数信息,这将用于将数据从内部格式转换为文本格式。 /* * Create a temporary memory context that we can reset once per row to @@ -118,7 +128,7 @@ static uint64 ledger_copytable(CopyState cstate) */ cstate->rowcontext = AllocSetContextCreate( CurrentMemoryContext, "COPY TO", ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE); - +// 创建一个内存上下文,用于存储行数据。 /* * For non-binary copy, we need to convert null_print to file * encoding, because it will be sent directly with CopySendString. @@ -126,6 +136,7 @@ static uint64 ledger_copytable(CopyState cstate) if (cstate->need_transcoding) { cstate->null_print_client = pg_server_to_any(cstate->null_print, cstate->null_print_len, cstate->file_encoding); } + // 如果需要字符编码转换,将 NULL 值的打印格式转换为客户端编码。 Tuple tuple; TableScanDesc scan_desc; @@ -136,14 +147,17 @@ static uint64 ledger_copytable(CopyState cstate) values = (Datum*)palloc0(num_phys_attrs * sizeof(Datum)); nulls = (bool*)palloc0(num_phys_attrs * sizeof(bool)); +// 分配内存来存储行的数据和 NULL 值的标志。 scan_desc = scan_handler_tbl_beginscan(cur_rel, GetActiveSnapshot(), 0, NULL); - +// 开始扫描表,获取一个用于扫描的描述符。 /* For each row, we will recalculate previous hash. */ while ((tuple = scan_handler_tbl_getnext(scan_desc, ForwardScanDirection, cur_rel)) != NULL) { CHECK_FOR_INTERRUPTS(); + // 检查是否有中断请求。 /* Deconstruct the tuple ... faster than repeated heap_getattr */ tableam_tops_deform_tuple2(tuple, tuple_desc, values, nulls, GetTableScanDesc(scan_desc, cur_rel)->rs_cbuf); + // 解析元组数据并填充到 values 和 nulls 数组中。 if (!is_gchain) { char comb_str[NAMEDATALEN] = {0}; uint64 t_ins = nulls[USERCHAIN_COLUMN_HASH_INS] ? 0 : DatumGetUInt64(values[USERCHAIN_COLUMN_HASH_INS]); @@ -168,19 +182,22 @@ static uint64 ledger_copytable(CopyState cstate) } /* Format and send the data */ CopyOneRowTo(cstate, HeapTupleGetOid((HeapTuple)tuple), values, nulls); + // ... 以下代码对解析后的数据进行处理,包括计算哈希值和将数据发送到输出。 rec_num++; processed++; } scan_handler_tbl_endscan(scan_desc); - + // 结束表的扫描。 pfree_ext(values); pfree_ext(nulls); MemoryContextDelete(cstate->rowcontext); - +// 释放分配的内存和上下文。 return processed; } - +//ledger_copytable 函数: 功能为复制表中的数据。 +//具体步骤:1获取要复制的表的描述信息,包括属性和属性数量。2设置输出函数,用于将内部数据转换为文本格式。3 创建用于存储行数据的内存上下文。4如果需要字符编码转换,将 NULL 值的打印格式转换为客户端编码。5扫描表中的每一行数据,解析并处理每一行的数据。6计算哈希值并将数据发送到输出。6结束表的扫描,释放分配的内存和上下文。 +//作用:将表中的数据复制到输出,同时进行一些数据处理,如哈希计算和字符编码转换。 /* * ledger_docopy -- the copy process of hist table * @@ -202,33 +219,48 @@ static uint64 ledger_docopy(CopyStmt *stmt, const char *queryString) /* Open and lock the relation, using the appropriate lock type. */ rel = heap_openrv(stmt->relation, AccessShareLock); + // 使用给定的表名打开表,并锁定以防止其他事务的写入。 rte = makeNode(RangeTblEntry); rte->rtekind = RTE_RELATION; rte->relid = RelationGetRelid(rel); rte->relkind = rel->rd_rel->relkind; rte->requiredPerms = ACL_SELECT; + // 创建一个表示关系的 RangeTblEntry 结构,并设置相关信息,包括关系ID、类型和所需的权限。 tup_desc = RelationGetDescr(rel); + // 获取关系的元组描述。 + attnum = (rte->relid == GsGlobalChainRelationId) ? Natts_gs_global_chain : USERCHAIN_COLUMN_NUM; + // 确定要选择的列数,根据关系的类型。 + /* add columns that need select permission. */ for (int i = 1; i <= attnum; ++i) { int attno = i - FirstLowInvalidHeapAttributeNumber; rte->selectedCols = bms_add_member(rte->selectedCols, attno); } + // 循环遍历列,将需要 SELECT 权限的列添加到 selectedCols 集合中。 (void)ExecCheckRTPerms(list_make1(rte), true); - +// 使用 list_make1 创建 RangeTblEntry 的列表,并检查权限。 cstate = BeginCopyTo(rel, query, queryString, stmt->filename, stmt->attlist, stmt->options); + // 初始化用于复制数据的 CopyState 结构。 cstate->range_table = list_make1(rte); cstate->curPartionRel = cstate->rel; + // 设置 range_table 和 curPartionRel。 processed = ledger_copytable(cstate); + // 调用 ledger_copytable 函数执行数据复制操作。 EndCopyTo(cstate); + // 结束数据复制操作。 if (rel != NULL) { heap_close(rel, AccessShareLock); } + // 如果关系仍然打开,关闭关系。 return processed; } +//ledger_docopy 函数:功能:执行数据复制操作。 +//具体步骤:1打开和锁定指定的数据库表。2 创建并配置 RangeTblEntry,用于表示表的相关信息和权限。3初始化 CopyState 结构,用于进行数据复制操作。4配置相关参数,如输出文件名、选定的列和其他选项。5调用 ledger_copytable 函数执行数据复制操作。6结束数据复制操作。 +//作用:执行复制数据的操作,包括表的锁定、权限检查、数据复制和结束操作。 /* * get_current_timestamp_text -- generate time text for name appending @@ -240,6 +272,7 @@ static uint64 ledger_docopy(CopyStmt *stmt, const char *queryString) static void get_current_timestamp_text(char *time_str) { const char *now = timestamptz_to_str(GetCurrentTimestamp()); + // 获取当前时间戳的文本表示。 size_t time_len = strlen(now); size_t pos = 0; for (size_t i = 0; i < time_len; ++i) { @@ -250,8 +283,11 @@ static void get_current_timestamp_text(char *time_str) } } time_str[pos] = '\0'; + // 遍历时间戳文本,仅保留数字字符,去除其他字符,生成最终的时间字符串。 } - +//get_current_timestamp_text 函数功能:获取当前时间戳的文本表示,并去除非数字字符。 +//具体步骤:获取当前时间戳的文本表示。遍历时间戳文本,仅保留数字字符,去除其他字符。 +//作用:生成当前时间的文本表示,用于文件名。 /* * copy_local_hist_table -- copy history table to hist_back dir. * @@ -267,6 +303,7 @@ static void copy_local_hist_table(Oid relid, char *histname, const char *time) initStringInfo(&buf); CopyStmt *stmt = makeNode(CopyStmt); RangeVar *relation = makeRangeVar("blockchain", histname, -1); + // 创建一个表示表的 RangeVar 结构。 if (!is_absolute_path(g_instance.attr.attr_security.Audit_directory)) { rc = snprintf_s(path, MAXPGPATH, MAXPGPATH - 1, "%s/%s/hist_bak/%s_%u_%s.hist", t_thrd.proc_cxt.DataDir, g_instance.attr.attr_security.Audit_directory, histname, relid, time); @@ -275,13 +312,18 @@ static void copy_local_hist_table(Oid relid, char *histname, const char *time) g_instance.attr.attr_security.Audit_directory, histname, relid, time); } securec_check_ss(rc, "", ""); + // 构建历史表的文件路径。 appendStringInfo(&buf, "COPY blockchain.%s to \'%s\'", histname, path); + // 构建 COPY 命令的字符串。 stmt->relation = relation; stmt->is_from = false; stmt->filename = path; + // 配置 CopyStmt 结构。 ledger_docopy((CopyStmt *)stmt, buf.data); + // 调用 ledger_docopy 函数执行数据复制操作。 } - +//copy_local_hist_table 函数功能:复制本地历史表的数据到指定文件。 +//具体步骤:构建历史表的文件路径。创建 CopyStmt 结构,表示复制操作。配置 CopyStmt 包括源表、目标文件和其他选项。调用 ledger_docopy 函数执行数据复制操作。 /* * open_histback_dir -- open hist_back dir. * @@ -301,10 +343,14 @@ static DIR *open_histback_dir(char *dir_path) g_instance.attr.attr_security.Audit_directory); } securec_check_ss(rc, "", ""); + // 构建历史归档目录的路径。 dir = AllocateDir(dir_path); + // 使用 AllocateDir 打开目录。 return dir; } - +//open_histback_dir 函数功能:打开历史归档目录并返回目录句柄。 +//具体步骤:构建历史归档目录的路径。使用 AllocateDir 函数打开目录。 +//作用:打开历史归档目录以便后续的文件操作。 /* * get_histback_dir_filesize -- count all file size of hist_back dir. */ @@ -316,6 +362,7 @@ static uint64 get_histback_dir_filesize() errno_t rc = EOK; uint64 size = 0; dir = open_histback_dir(dir_path); + // 打开历史归档目录。 if (dir == NULL) { return 0; } @@ -334,9 +381,12 @@ static uint64 get_histback_dir_filesize() } } FreeDir(dir); + // 遍历目录中的文件,计算它们的大小并累加。 return size; } - +//get_histback_dir_filesize 函数功能:计算历史归档目录中文件的总大小。 +//具体步骤:打开历史归档目录。遍历目录中的文件,计算它们的大小并累加。关闭目录句柄。 +//作用:计算历史归档目录中文件的总大小。 /* * remove_oldest_histback_file -- remove oldest file in hist_back * @@ -355,6 +405,7 @@ static uint64 remove_oldest_histback_file() struct stat stat_buf; dir = open_histback_dir(dir_path); + // 打开历史归档目录。 if (dir == NULL) { return 0; } @@ -377,12 +428,17 @@ static uint64 remove_oldest_histback_file() } } FreeDir(dir); + // 遍历目录中的文件,找到最旧的文件并记录其路径和大小。 + if (unlink(del_file) < 0) { ereport(WARNING, (errmsg("could not remove histbak file: %s", del_file))); } + // 移除最旧的文件。 return filesize; } - +//remove_oldest_histback_file 函数功能:移除历史归档目录中最旧的文件。 +//具体步骤:打开历史归档目录。遍历目录中的文件,找到最旧的文件并记录其路径和大小。移除最旧的文件。关闭目录句柄。 +//作用:移除历史归档目录中最旧的文件,以释放空间。 /* * ledger_hist_archive -- interface for history table archive * @@ -394,6 +450,7 @@ static uint64 remove_oldest_histback_file() */ Datum ledger_hist_archive(PG_FUNCTION_ARGS) { + // 检查用户权限,只有超级用户或审计管理员才能执行此操作。 if (!isRelSuperuser() && !isAuditadmin(GetUserId())) { ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("Permission denied."))); } @@ -412,15 +469,19 @@ Datum ledger_hist_archive(PG_FUNCTION_ARGS) table_name = text_to_cstring(rel_name); Oid nspoid = get_namespace_oid(table_nsp, false); relid = get_relname_relid(table_name, nspoid); + // 检查用户是否具有表的权限。 ledger_usertable_check(relid, nspoid, table_name, table_nsp); - +// 生成历史表的名称。 get_hist_name(relid, table_name, hist_name, nspoid, table_nsp); + // 获取当前时间的文本表示。 get_current_timestamp_text(current_time); + // 如果当前角色不是协调者,执行历史归档操作。 if (g_instance.role != VCOORDINATOR) { /* * Step 1. Copy user history table. */ uint64 total_histback_size = get_histback_dir_filesize(); + // 当历史归档目录大小超过限制时,删除最旧的历史文件以释放空间。 while (total_histback_size >= (uint64)(u_sess->attr.attr_security.Audit_SpaceLimit * 1024L)) { total_histback_size -= remove_oldest_histback_file(); } @@ -439,8 +500,10 @@ Datum ledger_hist_archive(PG_FUNCTION_ARGS) uint64 cur_rec_num = 0; TableScanDesc scan; HeapTuple tuple; + // 锁定历史哈希缓存。 /* sum all hash_ins and hash_del for unification. */ lock_hist_hash_cache(LW_EXCLUSIVE); + // 打开历史表以获取数据。 Relation histRel = heap_open(get_relname_relid(hist_name, PG_BLOCKCHAIN_NAMESPACE), AccessExclusiveLock); scan = heap_beginscan(histRel, SnapshotNow, 0, NULL); while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL) { @@ -475,6 +538,7 @@ Datum ledger_hist_archive(PG_FUNCTION_ARGS) values[USERCHAIN_COLUMN_HASH_DEL] = UInt64GetDatum(hash_del); nulls[USERCHAIN_COLUMN_REC_NUM] = false; nulls[USERCHAIN_COLUMN_PREVHASH] = false; + // 创建历史记录元组并插入历史表。 tuple = heap_form_tuple(RelationGetDescr(histRel), values, nulls); /* Do real truncate. */ @@ -494,7 +558,7 @@ Datum ledger_hist_archive(PG_FUNCTION_ARGS) } return BoolGetDatum(res); } - +//它们的主要功能是执行历史表和全局链表的归档操作,包括权限检查、历史文件的管理、数据复制等。这些操作用于审计和维护数据库的历史数据。 /* * ledger_gchain_archive -- archive gs_global_chain and unify each user rel * @@ -504,6 +568,7 @@ Datum ledger_hist_archive(PG_FUNCTION_ARGS) */ Datum ledger_gchain_archive(PG_FUNCTION_ARGS) { + // 检查用户权限,只有超级用户或审计管理员才能执行此操作。 if (!isRelSuperuser() && !isAuditadmin(GetUserId())) { ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("Permission denied."))); } @@ -515,8 +580,9 @@ Datum ledger_gchain_archive(PG_FUNCTION_ARGS) if (g_instance.role != VCOORDINATOR && g_instance.role != VSINGLENODE) { return BoolGetDatum(res); } - +// 获取历史备份目录的当前大小。 uint64 total_histback_size = get_histback_dir_filesize(); + // 当历史备份目录大小超过限制时,删除最旧的历史文件以释放空间。 while (total_histback_size >= (uint64)(u_sess->attr.attr_security.Audit_SpaceLimit * 1024L)) { total_histback_size -= remove_oldest_histback_file(); } @@ -546,6 +612,7 @@ Datum ledger_gchain_archive(PG_FUNCTION_ARGS) stmt->relation = relation; stmt->is_from = false; stmt->filename = path; + // 调用 ledger_docopy 函数执行数据复制。 ledger_docopy((CopyStmt *)stmt, buf.data); /* @@ -652,3 +719,4 @@ Datum ledger_gchain_archive(PG_FUNCTION_ARGS) return BoolGetDatum(res); } +//执行全局链表的归档操作,包括权限检查、历史文件的管理、数据复制、哈希表操作等。该操作用于维护数据库的全局链表数据,并确保数据一致性和完整性。 \ No newline at end of file diff --git a/src/gausskernel/security/gs_ledger/ledger_check.cpp b/src/gausskernel/security/gs_ledger/ledger_check.cpp index c64a351b4..c971b6ac5 100644 --- a/src/gausskernel/security/gs_ledger/ledger_check.cpp +++ b/src/gausskernel/security/gs_ledger/ledger_check.cpp @@ -53,34 +53,34 @@ */ static uint64 gen_usertable_hash_sum(Relation rel) { - uint64 rel_hash = 0; - bool is_null = false; - int hash_natt = user_hash_attrno(rel->rd_att); + uint64 rel_hash = 0;//初始化关系哈希和为零 + bool is_null = false;//初始化标志位为假 + int hash_natt = user_hash_attrno(rel->rd_att);//获取用户关系的哈希属性编号 Assert(hash_natt >= 0); - HeapTuple tuple; - TupleDesc desc = rel->rd_att; - Snapshot snapshot = GetActiveSnapshot(); - TableScanDesc scan; - if (RELATION_CREATE_BUCKET(rel)) { - Relation bucket_rel = NULL; - oidvector *bucket_list = searchHashBucketByOid(rel->rd_bucketoid); - for (int i = 0; i < bucket_list->dim1; i++) { - bucket_rel = bucketGetRelation(rel, NULL, bucket_list->values[i]); - scan = heap_beginscan(bucket_rel, snapshot, 0, NULL); - while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL) { + HeapTuple tuple;//用于存储关系的元组 + TupleDesc desc = rel->rd_att;//获取关系的元组描述 + Snapshot snapshot = GetActiveSnapshot();//活动快照 + TableScanDesc scan;//表扫描器 + if (RELATION_CREATE_BUCKET(rel)) {//如果关系是哈希分区表啊 + Relation bucket_rel = NULL;//初始化分区关系 + oidvector *bucket_list = searchHashBucketByOid(rel->rd_bucketoid);//获取哈希分区列表 + for (int i = 0; i < bucket_list->dim1; i++) {//遍历哈希分区列表 + bucket_rel = bucketGetRelation(rel, NULL, bucket_list->values[i]);//获取哈希分区关系 + scan = heap_beginscan(bucket_rel, snapshot, 0, NULL);//开始扫描分区表 + while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL) {//遍历分区表中的元组 rel_hash += DatumGetUInt64(heap_getattr(tuple, hash_natt + 1, desc, &is_null)); - } - heap_endscan(scan); + }//获取哈希值累加到关系哈希和中 + heap_endscan(scan);//结束扫描 bucketCloseRelation(bucket_rel); } - } else { - scan = heap_beginscan(rel, snapshot, 0, NULL); - while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL) { + } else {//如果关系不是哈希分区表 + scan = heap_beginscan(rel, snapshot, 0, NULL);//开始扫描关系 + while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL) {//遍历关系中的元组 rel_hash += DatumGetUInt64(heap_getattr(tuple, hash_natt + 1, desc, &is_null)); } - heap_endscan(scan); + heap_endscan(scan);//结束扫描 } - return rel_hash; + return rel_hash;//返回关系哈希和 } /* @@ -92,27 +92,27 @@ static uint64 gen_usertable_hash_sum(Relation rel) */ static uint64 get_usertable_hash_sum(Oid relid) { - uint64 rel_hash = 0; + uint64 rel_hash = 0;//初始化关系哈希和为零 Relation rel = NULL; - rel = heap_open(relid, AccessShareLock); - if (!RelationIsPartitioned(rel)) { - rel_hash = gen_usertable_hash_sum(rel); - } else { - List *partition_list = NIL; + rel = heap_open(relid, AccessShareLock);//打开用户表关系并获取共享锁 + if (!RelationIsPartitioned(rel)) {//如果用户表不是分区表 + rel_hash = gen_usertable_hash_sum(rel);//调用函数 + } else {//如果是分区表 + List *partition_list = NIL;//初始化 ListCell *lc = NULL; - Partition part; - Relation fake_rel; - partition_list = relationGetPartitionList(rel, AccessShareLock); - foreach (lc, partition_list) { - part = (Partition)lfirst(lc); + Partition part;//分区 + Relation fake_rel;//假分区关系 + partition_list = relationGetPartitionList(rel, AccessShareLock);//获取分区列表 + foreach (lc, partition_list) {//遍历分区列表 + part = (Partition)lfirst(lc);//获取分区 fake_rel = partitionGetRelation(rel, part); - rel_hash += gen_usertable_hash_sum(fake_rel); - releaseDummyRelation(&fake_rel); + rel_hash += gen_usertable_hash_sum(fake_rel);//调用函数计算分区关系哈希和并累加 + releaseDummyRelation(&fake_rel);//释放分区关系 } - releasePartitionList(rel, &partition_list, AccessShareLock); + releasePartitionList(rel, &partition_list, AccessShareLock);// 释放分区列表 } - heap_close(rel, AccessShareLock); - return rel_hash; + heap_close(rel, AccessShareLock);// 关闭用户表关系并释放共享锁 + return rel_hash;// 返回关系哈希和 } /* @@ -122,28 +122,28 @@ static uint64 get_usertable_hash_sum(Oid relid) */ static uint64 get_histtable_hash_sum(Oid hist_oid) { - uint64 rel_hash = 0; - bool is_null = false; - Relation hist_rel; + uint64 rel_hash = 0;// 初始化哈希差值为零 + bool is_null = false;// 初始化标志位为假 + Relation hist_rel;// 初始化历史表关系 TableScanDesc scan; - HeapTuple tuple; + HeapTuple tuple; // 用于存储历史表中的元组 Snapshot snapshot = GetActiveSnapshot(); - hist_rel = heap_open(hist_oid, AccessShareLock); - scan = heap_beginscan(hist_rel, snapshot, 0, NULL); - while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL) { + hist_rel = heap_open(hist_oid, AccessShareLock);// 打开历史表关系并获取共享锁 + scan = heap_beginscan(hist_rel, snapshot, 0, NULL); // 开始扫描历史表 + while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL) { // 遍历历史表中的元组 Datum value = heap_getattr(tuple, USERCHAIN_COLUMN_HASH_INS + 1, hist_rel->rd_att, &is_null); - if (!is_null) { - rel_hash += DatumGetUInt64(value); + if (!is_null) {// 如果不是空值 + rel_hash += DatumGetUInt64(value);// 累加到哈希差值中 } value = heap_getattr(tuple, USERCHAIN_COLUMN_HASH_DEL + 1, hist_rel->rd_att, &is_null); - if (!is_null) { - rel_hash -= DatumGetUInt64(value); + if (!is_null) {// 如果不是空值 + rel_hash -= DatumGetUInt64(value); // 从哈希差值中减去 } } - heap_endscan(scan); - heap_close(hist_rel, AccessShareLock); + heap_endscan(scan);// 结束历史表扫描 + heap_close(hist_rel, AccessShareLock); // 关闭历史表关系并释放共享锁 return rel_hash; } @@ -171,23 +171,23 @@ static bool has_ledger_consistent_privilege(Oid relid, Oid namespaceId) */ bool is_hist_hash_identity(Oid relid, uint64 *res_hash) { - uint64 user_hash_sum; - uint64 hist_hash_sum; + uint64 user_hash_sum;// 用户表的哈希和 + uint64 hist_hash_sum;// 历史表的哈希和 char hist_name[NAMEDATALEN]; - char *rel_name = get_rel_name(relid); - if (!get_hist_name(relid, rel_name, hist_name)) { - ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("get hist table name failed."))); + char *rel_name = get_rel_name(relid);// 获取用户表的名称 + if (!get_hist_name(relid, rel_name, hist_name)) {// 获取历史表的名称 + ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("get hist table name failed."))); // 如果获取失败,则�����错 } - Oid histoid = get_relname_relid(hist_name, PG_BLOCKCHAIN_NAMESPACE); - if (!OidIsValid(histoid)) { - ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("could not find hist table of \"%s\".", rel_name))); + Oid histoid = get_relname_relid(hist_name, PG_BLOCKCHAIN_NAMESPACE);// 获取历史表的 OID + if (!OidIsValid(histoid)) { // 如果 OID 无效 + ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("could not find hist table of \"%s\".", rel_name)));// 报错,找不到历史表 } - user_hash_sum = get_usertable_hash_sum(relid); - hist_hash_sum = get_histtable_hash_sum(histoid); + user_hash_sum = get_usertable_hash_sum(relid); // 获取用户表的哈希总和 + hist_hash_sum = get_histtable_hash_sum(histoid); // 获取历史表的哈希总和 - *res_hash = hist_hash_sum; - return user_hash_sum == hist_hash_sum; + *res_hash = hist_hash_sum; // 返回历史表的哈希和 + return user_hash_sum == hist_hash_sum; // 返回用户表哈希和和历史表哈希和是否相等的比较结果 } #ifdef ENABLE_MULTIPLE_NODES @@ -201,28 +201,28 @@ bool is_hist_hash_identity(Oid relid, uint64 *res_hash) */ static void StrategyFuncAnd(ParallelFunctionState* state) { - TupleTableSlot* slot = NULL; - bool result = true; + TupleTableSlot* slot = NULL;// 创建 TupleTableSlot 用于存储结果 + bool result = true;// 初始化结果为true - Assert(state); - Assert(state->tupstore); - Assert(state->tupdesc); - slot = MakeSingleTupleTableSlot(state->tupdesc); + Assert(state); // 断言 state 不为空 + Assert(state->tupstore);// 断言 tupstore 不为空 + Assert(state->tupdesc);// 断言 tupdesc 不为空 + slot = MakeSingleTupleTableSlot(state->tupdesc);// 创建单个 TupleTableSlot 用于存储数据 while (true) { bool isnull = false; - if (!tuplestore_gettupleslot(state->tupstore, true, false, slot)) + if (!tuplestore_gettupleslot(state->tupstore, true, false, slot)) // 从 tupstore 获取下一个结果,如果没有更多结果则退出循环 break; - if (!DatumGetBool(tableam_tslot_getattr(slot, 1, &isnull))) { + if (!DatumGetBool(tableam_tslot_getattr(slot, 1, &isnull))) {// 从 TupleTableSlot 中获取属性值,如果为 false 则将结果设置为 false 并退出循环 result = false; break; } - (void)ExecClearTuple(slot); + (void)ExecClearTuple(slot); // 清空 TupleTableSlot } - state->result = result; + state->result = result;// 将结果存储在并行函数状态中 } /* @@ -235,31 +235,30 @@ static void StrategyFuncAnd(ParallelFunctionState* state) */ static void StrategyFuncUInt64Sum(ParallelFunctionState* state) { - TupleTableSlot* slot = NULL; - int64 result = 0; + TupleTableSlot* slot = NULL;// 创建 TupleTableSlot 用于存储结果 + int64 result = 0;// 初始化结果为 0 - Assert(state && state->tupstore && state->tupdesc); - slot = MakeSingleTupleTableSlot(state->tupdesc); + Assert(state && state->tupstore && state->tupdesc);// 断言 state、tupstore 和 tupdesc 不为空 + slot = MakeSingleTupleTableSlot(state->tupdesc);// 创建单个 TupleTableSlot 用于存储数据 while (true) { bool isnull = false; - if (!tuplestore_gettupleslot(state->tupstore, true, false, slot)) + if (!tuplestore_gettupleslot(state->tupstore, true, false, slot))// 从 tupstore 获取下一个结果,如果没有更多结果则退出循环 break; - result += DatumGetUInt64(tableam_tslot_getattr(slot, 1, &isnull)); - ExecClearTuple(slot); + result += DatumGetUInt64(tableam_tslot_getattr(slot, 1, &isnull));// 从 TupleTableSlot 中获取属性值并累加到结果中 + ExecClearTuple(slot);// 清空 TupleTableSlot } - state->result = result; + state->result = result;// 将结果存储在并行函数状态中 } #endif - /* - * ledger_hist_check -- check whether user table hash and history table hash are equal + * ledger_hist_check -- 检查用户表哈希和历史表哈希是否相等 * - * parameter1: user table name [type: text] - * parameter2: namespace of user table [type: text] + * parameter1: 用户表名 [类型: text] + * parameter2: 用户表的命名空间 [类型: text] */ Datum ledger_hist_check(PG_FUNCTION_ARGS) { @@ -269,30 +268,32 @@ Datum ledger_hist_check(PG_FUNCTION_ARGS) bool res = false; char *table_name; char *table_nsp; - text *rel_nsp = PG_GETARG_TEXT_PP(0); - text *rel_name = PG_GETARG_TEXT_PP(1); + text *rel_nsp = PG_GETARG_TEXT_PP(0); // 获取第一个参数,用户表的命名空间 + text *rel_name = PG_GETARG_TEXT_PP(1); // 获取第二个参数,用户表名 - table_nsp = text_to_cstring(rel_nsp); - table_name = text_to_cstring(rel_name); - nsp_oid = get_namespace_oid(table_nsp, false); - relid = get_relname_relid(table_name, nsp_oid); - ledger_usertable_check(relid, nsp_oid, table_name, table_nsp); - if (!has_ledger_consistent_privilege(relid, nsp_oid)) { + + table_nsp = text_to_cstring(rel_nsp);// 将文本参数转换为 C 字符串 + table_name = text_to_cstring(rel_name);// 将文本参数转换为 C 字符串 + nsp_oid = get_namespace_oid(table_nsp, false);// 获取命名空间的 OID + relid = get_relname_relid(table_name, nsp_oid);// 获取用户表的 OID + ledger_usertable_check(relid, nsp_oid, table_name, table_nsp);// 检查用户表是否存在 + + if (!has_ledger_consistent_privilege(relid, nsp_oid)) { // 检查权限 ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("Permission denied."))); } - res = is_hist_hash_identity(relid, &res_hash); + res = is_hist_hash_identity(relid, &res_hash);// 检查用户表哈希和历史表哈希是否相等 #ifdef ENABLE_MULTIPLE_NODES - if (!IsConnFromCoord()) { - StringInfoData buf; - ParallelFunctionState* state = NULL; - initStringInfo(&buf); + if (!IsConnFromCoord()) {// 如果不是从协调器节点调用 + StringInfoData buf;// 创建一个字符串缓冲区 + ParallelFunctionState* state = NULL;// 创建并行函数状态 + initStringInfo(&buf);// 初始化字符串缓冲区 appendStringInfo(&buf, "SELECT pg_catalog.ledger_hist_check('%s', '%s')", table_nsp, table_name); /* Get all hash diffs from DNs in distribute scenairo. */ state = RemoteFunctionResultHandler(buf.data, NULL, StrategyFuncAnd); - res &= state->result; - FreeParallelFunctionState(state); + res &= state->result;// 更新结果 + FreeParallelFunctionState(state);// 释放并行函数状态 } #endif return BoolGetDatum(res); @@ -309,19 +310,19 @@ static uint64 get_gchain_relhash_sum(Oid relid) HeapTuple tuple = NULL; /* scan the gs_global_chain catalog by relid */ - Relation gchain_rel = heap_open(GsGlobalChainRelationId, AccessShareLock); + Relation gchain_rel = heap_open(GsGlobalChainRelationId, AccessShareLock);// 打开 gs_global_chain 关系 Form_gs_global_chain rdata = NULL; - TableScanDesc scan = heap_beginscan(gchain_rel, SnapshotNow, 0, NULL); - while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL) { - rdata = (Form_gs_global_chain)GETSTRUCT(tuple); - if (rdata == NULL || rdata->relid != relid) { + TableScanDesc scan = heap_beginscan(gchain_rel, SnapshotNow, 0, NULL);// 创建表扫描描述符 + while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL) {// 循环遍历结果集 + rdata = (Form_gs_global_chain)GETSTRUCT(tuple);// 获取结果的数据结构 + if (rdata == NULL || rdata->relid != relid) {// 如果数据为空或者 OID 不匹配,则继续下一轮循环 continue; } - relhash += rdata->relhash; + relhash += rdata->relhash;// 累加关系哈希值 } - heap_endscan(scan); - heap_close(gchain_rel, AccessShareLock); - return relhash; + heap_endscan(scan);// 结束扫描 + heap_close(gchain_rel, AccessShareLock);// 关闭关系 + return relhash;// 返回关系哈希和 } /* @@ -336,39 +337,39 @@ static uint64 get_gchain_relhash_sum(Oid relid) Datum get_dn_hist_relhash(PG_FUNCTION_ARGS) { #ifndef ENABLE_MULTIPLE_NODES - DISTRIBUTED_FEATURE_NOT_SUPPORTED(); + DISTRIBUTED_FEATURE_NOT_SUPPORTED(); // 不支持分布式特性 return UInt64GetDatum(0); #else - if (!IsConnFromCoord()) { + if (!IsConnFromCoord()) {// 如果不是从协调器节点调用 ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("Permission denied."))); return UInt64GetDatum(0); } Oid user_relid; Oid nsp_oid; uint64 res_hash; - text *rel_nsp = PG_GETARG_TEXT_PP(0); - text *rel_name = PG_GETARG_TEXT_PP(1); + text *rel_nsp = PG_GETARG_TEXT_PP(0);// 获取第一个参数,用户表的命名空间 + text *rel_name = PG_GETARG_TEXT_PP(1);// 获取第二个参数,用户表名 char *table_name; char *table_nsp; - table_nsp = text_to_cstring(rel_nsp); - table_name = text_to_cstring(rel_name); - nsp_oid = get_namespace_oid(table_nsp, false); - user_relid = get_relname_relid(table_name, nsp_oid); - ledger_usertable_check(user_relid, nsp_oid, table_name, table_nsp); + table_nsp = text_to_cstring(rel_nsp);// 将文本参数转换为 C 字符串 + table_name = text_to_cstring(rel_name);// 将文本参数转换为 C 字符串 + nsp_oid = get_namespace_oid(table_nsp, false);// 获取命名空间的 OID + user_relid = get_relname_relid(table_name, nsp_oid);// 获取用户表的 OID + ledger_usertable_check(user_relid, nsp_oid, table_name, table_nsp);// 检查用户表是否存在 if (!has_ledger_consistent_privilege(user_relid, nsp_oid)) { ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("Permission denied."))); } - if (IS_PGXC_DATANODE) { - if (!is_hist_hash_identity(user_relid, &res_hash)) { + if (IS_PGXC_DATANODE) {// 如果当前节点是数据节点 + if (!is_hist_hash_identity(user_relid, &res_hash)) {// 检查用户表哈希和历史表哈希是否相等 res_hash = 0; } - } else { + } else {// 如果当前节点是协调器节点 res_hash = get_gchain_relhash_sum(user_relid); } - return UInt64GetDatum(res_hash); + return UInt64GetDatum(res_hash);//返回结果 #endif } @@ -381,7 +382,7 @@ Datum get_dn_hist_relhash(PG_FUNCTION_ARGS) Datum ledger_gchain_check(PG_FUNCTION_ARGS) { #ifdef ENABLE_MULTIPLE_NODES - if (!IS_PGXC_COORDINATOR || IsConnFromCoord()) { + if (!IS_PGXC_COORDINATOR || IsConnFromCoord()) {// 如果不是协调器节点或者是从协调器节点调用 ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("Permission denied."))); } #endif @@ -399,16 +400,17 @@ Datum ledger_gchain_check(PG_FUNCTION_ARGS) table_name = text_to_cstring(rel_name); nsp_oid = get_namespace_oid(table_nsp, false); user_relid = get_relname_relid(table_name, nsp_oid); + //检查用户的一致性 ledger_usertable_check(user_relid, nsp_oid, table_name, table_nsp); - if (!has_ledger_consistent_privilege(user_relid, nsp_oid)) { + if (!has_ledger_consistent_privilege(user_relid, nsp_oid)) {// 如果用户没有足够的权限,则报告权限不足的错误 ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("Permission denied."))); } - +// 检查历史表和用户表的哈希是否一致,并获取历史表的哈希值 res = is_hist_hash_identity(user_relid, &dn_hash); if (!res) { return BoolGetDatum(res); - } + } // 获取全局链表中用户表的哈希值 cn_hash = get_gchain_relhash_sum(user_relid); #ifdef ENABLE_MULTIPLE_NODES ParallelFunctionState* state = NULL; @@ -428,7 +430,8 @@ Datum ledger_gchain_check(PG_FUNCTION_ARGS) #endif return BoolGetDatum(dn_hash == cn_hash); } - +//函数名为 ledger_gchain_check,用于检查用户表和历史表的哈希一致性,并返回一个表示一致性的布尔值。在多节点环境下(#ifdef ENABLE_MULTIPLE_NODES),首先检查当前节点是否为协调器节点,以及是否从协调器节点调用此函数。如果不是,报告权限不足的错误。然后,从函数参数中获取用户表的命名空间和名称,并将它们转换为 C 字符串,获取用户表的 OID。接着,调用 ledger_usertable_check 函数检查用户表的一致性,确保用户表存在且合法。如果用户没有足够的权限,再次报告权限不足的错误。使用 is_hist_hash_identity 函数检查历史表和用户表的哈希是否一致,并获取历史表的哈希值。 +//如果历史表和用户表的哈希不一致,函数会立即返回一个布尔值表示不一致。如果哈希一致,函数继续获取全局链表中用户表的哈希值,并在多节点环境下,从数据节点和协调器节点获取并累积哈希值。最后,函数返回一个布尔值,表示数据节点哈希与协调器哈希是否一致。 /* * repaire_hist_table_internal -- compare hash and repair hist table * @@ -441,21 +444,21 @@ Datum ledger_gchain_check(PG_FUNCTION_ARGS) */ static uint64 repaire_hist_table_internal(Oid relid, char *rel_name, Oid nspoid, bool option) { - uint64 rel_hash; - uint64 hash_diff; - char histname[NAMEDATALEN]; - get_hist_name(relid, rel_name, histname, nspoid); + uint64 rel_hash;// 声明用户表的哈希值 + uint64 hash_diff;// 声明哈希差值 + char histname[NAMEDATALEN];// 声明历史表的名称 + get_hist_name(relid, rel_name, histname, nspoid);// 获取历史表的名称 Oid histoid = get_relname_relid(histname, PG_BLOCKCHAIN_NAMESPACE); - if (!OidIsValid(histoid)) { + if (!OidIsValid(histoid)) {// 检查历史表是否有效 ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("The hist table of \"%s\" is not exist.", rel_name))); - } - rel_hash = get_usertable_hash_sum(relid); - hash_diff = rel_hash - get_histtable_hash_sum(histoid); - if (hash_diff != 0) { + } // 如果历史表无效,则报错 + rel_hash = get_usertable_hash_sum(relid); // 获取用户表的哈希值 + hash_diff = rel_hash - get_histtable_hash_sum(histoid);// 计算哈希差值(用户表哈希值减去历史表哈希值) + if (hash_diff != 0) {// 如果哈希差值不为零 /* Do hist table repair. */ - hist_table_record_internal(histoid, &hash_diff, NULL); + hist_table_record_internal(histoid, &hash_diff, NULL); // 调用函数修复历史表 } - return option ? rel_hash : hash_diff; + return option ? rel_hash : hash_diff; // 根据选项返回用户表哈希值或哈希差值 } /* @@ -464,11 +467,14 @@ static uint64 repaire_hist_table_internal(Oid relid, char *rel_name, Oid nspoid, * parameter1: user table name [type: text] * parameter2: namespace of user table [type: text] */ + // PostgreSQL 函数,用于修复用户表的历史记录和全局链表中的哈希差异。 +// 在执行任何操作之前,首先检查当前用户是否具有足够的权限。 Datum ledger_hist_repair(PG_FUNCTION_ARGS) -{ +{// 如果当前用户既不是超级用户也不是审计管理员,则拒绝执行。 if (!isRelSuperuser() && !isAuditadmin(GetUserId())) { ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("Permission denied."))); } + // 从函数参数中获取用户表的命名空间和名称。 text *rel_nsp = PG_GETARG_TEXT_PP(0); text *rel_name = PG_GETARG_TEXT_PP(1); char *table_name; @@ -476,38 +482,47 @@ Datum ledger_hist_repair(PG_FUNCTION_ARGS) Oid relid; Oid nspoid; uint64 delta = 0; - +// 将用户表的命名空间和名称转换为 C 字符串。 table_nsp = text_to_cstring(rel_nsp); table_name = text_to_cstring(rel_name); + // 获取用户表的命名空间的 OID。 nspoid = get_namespace_oid(table_nsp, false); + // 获取用户表的 OID。 relid = get_relname_relid(table_name, nspoid); + // 检查用户表的一致性。 ledger_usertable_check(relid, nspoid, table_name, table_nsp); /* * Repair hist table of current datanode. Get hash sum of hist * table and rel_hash of usertable, append the difference to hist table. */ - if (g_instance.role == VDATANODE || g_instance.role == VSINGLENODE) { + // 如果当前节点是数据节点或单节点 + if (g_instance.role == VDATANODE || g_instance.role == VSINGLENODE) { // 调用 repaire_hist_table_internal 函数以获取并追加哈希差异。 delta = repaire_hist_table_internal(relid, table_name, nspoid, false); } + // 如果当前节点是协调器或单节点 if (g_instance.role == VCOORDINATOR || g_instance.role == VSINGLENODE) { #ifdef ENABLE_MULTIPLE_NODES ParallelFunctionState* state = NULL; StringInfoData buf; initStringInfo(&buf); + // 构建一个 SQL 查询字符串,以获取哈希差异。 appendStringInfo(&buf, "SELECT pg_catalog.ledger_hist_repair('%s', '%s')", table_nsp, table_name); /* Get all hash diffs from DNs in distribute scenairo. */ + // 调用 RemoteFunctionResultHandler 函数以获取哈希差异。 state = RemoteFunctionResultHandler(buf.data, NULL, StrategyFuncUInt64Sum); + // 将哈希差异累积到 delta 变量中。 delta += state->result; - +// 释放并清理远程函数状态。 FreeParallelFunctionState(state); #endif +// 如果 delta 不为零,向全局链表追加修复信息。 if (delta != 0) { ledger_gchain_append(relid, "HIST REPAIR.", delta); } } - +// 返回 delta,表示哈希差异或修复结果。 return UInt64GetDatum(delta); } @@ -517,11 +532,14 @@ Datum ledger_hist_repair(PG_FUNCTION_ARGS) * parameter1: user table name [type: text] * parameter2: namespace of user table [type: text] */ +// 在执行任何操作之前,首先检查当前用户是否具有足够的权限。 Datum ledger_gchain_repair(PG_FUNCTION_ARGS) { + // 如果当前用户既不是超级用户也不是审计管理员,则拒绝执行。 if (!isRelSuperuser() && !isAuditadmin(GetUserId())) { ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("Permission denied."))); } + // 从函数参数中获取用户表的命名空间和名称。 text *rel_nsp = PG_GETARG_TEXT_PP(0); text *rel_name = PG_GETARG_TEXT_PP(1); char *table_name; @@ -529,54 +547,70 @@ Datum ledger_gchain_repair(PG_FUNCTION_ARGS) Oid relid; Oid nspoid; uint64 dn_hash = 0; - + // 将用户表的命名空间和名称转换为 C 字符串。 table_nsp = text_to_cstring(rel_nsp); table_name = text_to_cstring(rel_name); + // 获取用户表的命名空间的 OID。 nspoid = get_namespace_oid(table_nsp, false); + // 获取用户表的 OID。 relid = get_relname_relid(table_name, nspoid); + // 检查用户表的一致性。 ledger_usertable_check(relid, nspoid, table_name, table_nsp); /* * Repair hist table of current datanode. Get hash sum of hist * table and rel_hash of usertable, append the difference to hist table. */ - if (g_instance.role == VDATANODE || g_instance.role == VSINGLENODE) { + // 如果当前节点是数据节点或单节点 + if (g_instance.role == VDATANODE || g_instance.role == VSINGLENODE) {//调用 repaire_hist_table_internal 函数来获取并追加哈希差异 dn_hash = repaire_hist_table_internal(relid, table_name, nspoid, true); } uint64 rel_hash = dn_hash; uint64 cn_hash = 0; + // 如果当前节点是协调器或单节点 if (g_instance.role == VCOORDINATOR || g_instance.role == VSINGLENODE) { uint64 delta = 0; cn_hash = get_gchain_relhash_sum(relid); + // 如果不是从协调器节点发起的连接 if (!IsConnFromCoord()) { #ifdef ENABLE_MULTIPLE_NODES /* * CN accumulate all gchain cn_hash from all CNs, get all dn_hash from all DNs. * Then compare cn_hash and dn_hash, and fill up delta hash to gchain for repairing. */ - ParallelFunctionState* state = NULL; + arallelFunctionState* state = NULL; StringInfoData buf; initStringInfo(&buf); + // 构建一个 SQL 查询字符串,用于获取哈希差异 appendStringInfo(&buf, "SELECT pg_catalog.ledger_gchain_repair('%s', '%s')", table_nsp, table_name); /* Get and accumulate all dn_hash from all DNs. */ state = RemoteFunctionResultHandler(buf.data, NULL, StrategyFuncUInt64Sum); + // 将数据节点哈希值累积到 dn_hash 变量中。 dn_hash += state->result; + // 释放并清理远程函数状态 FreeParallelFunctionState(state); + // 如果存在其他协调器节点 if (GetAllCoordNodes() != NIL) { /* Get and accumulate all cn_hash from all CNs. */ - state = RemoteFunctionResultHandler(buf.data, NULL, StrategyFuncUInt64Sum, true, EXEC_ON_COORDS); - cn_hash += state->result; + // // 调用远程函数 RemoteFunctionResultHandler 来获取哈希差异 + state = RemoteFunctionResultHandler(buf.data, NULL, StrategyFuncUInt64Sum, true, EXEC_ON_COORDS);// 将协调器节点哈希值累积到 cn_hash 变量中。 + cn_hash += state->result; // 释放并清理远程函数状态 FreeParallelFunctionState(state); } #endif - delta = dn_hash - cn_hash; + // 计算哈希差异 + delta = dn_hash - cn_hash;// 如果 delta 不为零,向全局链表追加修复信息 if (delta != 0) { ledger_gchain_append(relid, "GCHAIN REPAIR.", delta); } } + // 将修复后的哈希值设置为协调器哈希值。 rel_hash = cn_hash; } - +// 返回 delta,表示哈希差异或修复结果 return UInt64GetDatum(rel_hash); -} \ No newline at end of file +} +//这两个函数用于修复用户表和历史表之间的哈希差异,以及修复全局链表中的哈希差异。在函数开头,首先检查当前用户是否是超级用户或审计管理员,如果不是,报告权限不足的错误。然后,从函数参数中获取用户表的命名空间和名称,并将它们转换为 C 字符串,获取用户表的 OID。调用 ledger_usertable_check 函数来确保用户表存在且一致性。 +//接下来,根据当前节点的角色,选择是否修复历史表。在数据节点或单节点上,调用 repaire_hist_table_internal 函数来获取并追加哈希差异。如果当前节点是协调器或单节点,会进行更多操作。在多节点环境下,会调用远程函数来获取数据节点的哈希差异,并将其累积到 delta 变量中。如果 delta 不为零,会向全局链表追加修复信息。 +//最后,根据修复的结果,函数返回一个表示哈希差异或修复结果的 UInt64 数据类型。 \ No newline at end of file diff --git a/src/gausskernel/security/gs_ledger/ledger_utils.cpp b/src/gausskernel/security/gs_ledger/ledger_utils.cpp index b3e1bb548..fffb34ece 100644 --- a/src/gausskernel/security/gs_ledger/ledger_utils.cpp +++ b/src/gausskernel/security/gs_ledger/ledger_utils.cpp @@ -24,7 +24,7 @@ #include "gs_ledger/ledger_utils.h" #include "catalog/gs_global_chain.h" - +// 定义全局变量 static pg_atomic_uint64 g_blocknum = 0; static HTAB *g_recnum_cache = NULL; @@ -33,6 +33,7 @@ static HTAB *g_recnum_cache = NULL; * * Note:If gchain is empty, next blocknum will start from 0. */ + // 重新加载下一个 g_blocknum,从 gchain 中加载 static uint32 reload_next_g_blocknum() { Relation gchain_rel = NULL; @@ -41,9 +42,11 @@ static uint32 reload_next_g_blocknum() uint32 blocknum; uint32 max_num = 0; bool isnull = false; - + // 打开 gs_global_chain 表 gchain_rel = heap_open(GsGlobalChainRelationId, RowExclusiveLock); + // 创建表扫描器 scan = heap_beginscan(gchain_rel, SnapshotAny, 0, NULL); + // 遍历表格,查找最大的 blocknum while ((tup = heap_getnext(scan, BackwardScanDirection)) != NULL) { blocknum = DatumGetUInt32(heap_getattr(tup, Anum_gs_global_chain_blocknum, RelationGetDescr(gchain_rel), &isnull)); @@ -57,18 +60,20 @@ static uint32 reload_next_g_blocknum() heap_close(gchain_rel, RowExclusiveLock); return max_num; } - +//函数static uint32 reload_next_g_blocknum():重新加载下一个 g_blocknum,从 gs_global_chain 表中加载。打开 gs_global_chain 表,查找最大的 blocknum 值。 /* * get_next_g_blocknum -- get next blocknum for gchain record. * * Note:provide next blocknum and auto increment itself. */ + // 获取下一个 g_blocknum uint64 get_next_g_blocknum() { uint64 res = 0; if (g_blocknum == 0) { LWLockAcquire(GlobalPrevHashLock, LW_EXCLUSIVE); if (g_blocknum == 0) { + // 原子操作:增加 g_blocknum 的值 pg_atomic_fetch_add_u64(&g_blocknum, 1); int cur_num = reload_next_g_blocknum(); pg_atomic_fetch_add_u64(&g_blocknum, cur_num); @@ -80,12 +85,13 @@ uint64 get_next_g_blocknum() LWLockRelease(GlobalPrevHashLock); return res; } - +//函数uint64 get_next_g_blocknum()功能:获取下一个 g_blocknum。 如果 g_blocknum 为零,获取锁以确保只有一个线程执行加载和更新。使用原子操作增加 g_blocknum 的值,并获取最大的 blocknum 值。释放锁并返回下一个 g_blocknum 值。 +// 重置 g_blocknum void reset_g_blocknum() { g_blocknum = 0; } - +//重置 g_blocknum 为零,用于重新开始计数。 /* * reload_g_rec_num -- load next rec_num from hist table. * @@ -93,6 +99,7 @@ void reset_g_blocknum() * * Note:return next rec_num and auto increment. */ + // 重新加载下一个 rec_num,从 hist 表中加载 uint64 reload_g_rec_num(Oid histoid) { if (!OidIsValid(histoid)) { @@ -106,9 +113,10 @@ uint64 reload_g_rec_num(Oid histoid) bool hist_empty = true; bool isnull = false; bool found; - + // 打开 hist 表 histRelation = heap_open(histoid, AccessShareLock); scan = heap_beginscan(histRelation, SnapshotNow, 0, NULL); + // 遍历表格,查找最大的 rec_num while ((tup = heap_getnext(scan, BackwardScanDirection)) != NULL) { rec_num = DatumGetUInt64(heap_getattr(tup, 1, RelationGetDescr(histRelation), &isnull)); if (rec_num >= max_rec_num) { @@ -127,12 +135,13 @@ uint64 reload_g_rec_num(Oid histoid) item->rec_num = rec_num + 1; return rec_num; } - +//重新加载下一个记录号 (rec_num),从历史表 (hist) 中加载。打开历史表,查找最大的 rec_num 值。 /* * get_next_recnum -- provide next rec_num. * * histoid: hist table oid. */ + // 获取下一个 rec_num uint64 get_next_recnum(Oid histoid) { if (g_recnum_cache == NULL) { @@ -170,7 +179,8 @@ uint64 get_next_recnum(Oid histoid) LWLockRelease(BlockchainVersionLock); return res; } - +//函数uint64 get_next_recnum(Oid histoid)功能:获取下一个记录号 (rec_num)。如果记录号缓存 (g_recnum_cache) 不存在,创建并初始化哈希表。使用原子操作获取下一个 rec_num 值,如果在缓存中找到则增加并返回。 +// 移除 hist recnum 缓存 bool remove_hist_recnum_cache(Oid histoid) { if (!OidIsValid(histoid) || g_recnum_cache == NULL) { @@ -180,22 +190,23 @@ bool remove_hist_recnum_cache(Oid histoid) return true; } - +//从历史记录号缓存中移除指定历史表的记录号缓存。 /* * lock_gchain_cache -- lock g_blocknum cache with lock mode. * * mode: lockmode */ + // 锁定 g_blocknum 缓存 void lock_gchain_cache(LWLockMode mode) { LWLockAcquire(GlobalPrevHashLock, mode); } - +//锁定 g_blocknum 缓存以提供不同的锁模式。 /* * release_gchain_cache -- release g_blocknum cache. */ void release_gchain_cache() -{ +{// 释放全局链缓存的轻量级锁 LWLockRelease(GlobalPrevHashLock); } @@ -203,7 +214,7 @@ void release_gchain_cache() * lock_hist_hash_cache -- load hist cache. */ void lock_hist_hash_cache(LWLockMode mode) -{ +{ // 获取 BlockchainVersionLock 的轻量级锁,使用指定的锁模式 LWLockAcquire(BlockchainVersionLock, mode); } @@ -211,7 +222,7 @@ void lock_hist_hash_cache(LWLockMode mode) * release_hist_hash_cache -- release hist cache. */ void release_hist_hash_cache() -{ +{ // 释放 BlockchainVersionLock 的轻量级锁 LWLockRelease(BlockchainVersionLock); } @@ -226,8 +237,10 @@ Oid get_target_query_relid(List* rte_list, int resultRelation) Oid relid = InvalidOid; if (resultRelation > 0) { + // 从 rte_list 中获取目标关系的范围表条目 RangeTblEntry *rte = (RangeTblEntry *)list_nth(rte_list, resultRelation - 1); if (rte->relkind == RELKIND_RELATION) { + // 如果范围表条目表示一个表,获取其 OID relid = rte->relid; } } @@ -250,11 +263,13 @@ bool is_ledger_usertable(Oid relid) Oid nspid = get_rel_namespace(relid); char relkind = get_rel_relkind(relid); + // 只有表才有其用户链表 /* only table has its user chain table */ if (relkind != RELKIND_RELATION) { return false; } /* check table belong to blockchain schema */ + // 检查表是否属于区块链模式 return IsLedgerNameSpace(nspid); } @@ -270,7 +285,7 @@ uint64 hash_combiner(List *relhash_list) if (relhash_list == NIL) { return relhash_sum; } - + // 遍历 relhash_list 中的每个哈希值,将它们相加 foreach (lc, relhash_list) { Datum *value = (Datum *)lfirst(lc); relhash_sum += DatumGetUInt64(value); @@ -293,6 +308,7 @@ bool is_ledger_hist_table(Oid relid) Oid relnsp = get_rel_namespace(relid); char relkind = get_rel_relkind(relid); /* check namespace oid of relation to verify hist table. */ + // 检查关系的命名空间 OID 以验证是否为历史表 return relnsp == PG_BLOCKCHAIN_NAMESPACE && relkind == RELKIND_RELATION; } @@ -319,9 +335,11 @@ bool is_ledger_related_rel(Relation rel) bool ledger_usertable_check(Oid relid, Oid nspoid, const char *tablename, const char *tablensp) { if (!OidIsValid(relid)) { + // 报告错误,指定的表不存在 ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("table %s.%s not exists.", tablensp, tablename))); } if (!IsLedgerNameSpace(nspoid)) { + // 报告错误,指定的表不是账本用户表 ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("table %s.%s is not ledger user table.", tablensp, tablename))); } @@ -335,30 +353,30 @@ bool ledger_usertable_check(Oid relid, Oid nspoid, const char *tablename, const */ List *namespace_get_depended_relid(Oid nspid) { - List *relid_list = NIL; - Relation pg_class_rel = NULL; - ScanKeyData skey[1]; - SysScanDesc sysscan; - HeapTuple tuple; - Oid tupid = InvalidOid; - + List *relid_list = NIL;// 创建一个空列表用于存储关系的OID。 + Relation pg_class_rel = NULL;// 创建一个指向系统表 pg_class 的关系 + ScanKeyData skey[1];// 创建扫描键的结构体数组,用于扫描 pg_class 表。 + SysScanDesc sysscan; // 创建系统扫描描述符。 + HeapTuple tuple;// 创建一个堆元组变量,用于存储扫描结果。 + Oid tupid = InvalidOid;// 初始化一个无效的 OID 用于后续赋值。 + // 初始化扫描键,用于检索具有特定命名空间 OID 的关系。 ScanKeyInit(&skey[0], Anum_pg_class_relnamespace, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(nspid)); pg_class_rel = heap_open(RelationRelationId, AccessShareLock); sysscan = systable_beginscan(pg_class_rel, ClassNameNspIndexId, true, SnapshotNow, 1, skey); - + // 遍历扫描结果,将每个关系的 OID 添加到 relid_list 列表中。 while (HeapTupleIsValid(tuple = systable_getnext(sysscan))) { - Form_pg_class reltup = (Form_pg_class)GETSTRUCT(tuple); - if (reltup->relkind == RELKIND_RELATION) { - tupid = HeapTupleGetOid(tuple); - relid_list = lappend_oid(relid_list, tupid); + Form_pg_class reltup = (Form_pg_class)GETSTRUCT(tuple); // 获取堆元组的数据结构。 + if (reltup->relkind == RELKIND_RELATION) {// 如果关系的类型是 RELKIND_RELATION(表)。 + tupid = HeapTupleGetOid(tuple);// 获取关系的 OID。 + relid_list = lappend_oid(relid_list, tupid);// 将关系的 OID 添加到列表中。 } } - systable_endscan(sysscan); - heap_close(pg_class_rel, AccessShareLock); - return relid_list; + systable_endscan(sysscan);// 结束系统表扫描。 + heap_close(pg_class_rel, AccessShareLock); // 关闭 pg_class 表,释放共享锁。 + return relid_list; // 返回包含关系 OID 的列表。 } - +//用于检索指定命名空间下的所有关系的 OID,然后将它们存储在一个列表中并返回。 /* * get_ledger_msg_hash -- extract relhash from response message. * @@ -379,6 +397,7 @@ bool get_ledger_msg_hash(char *message, uint64 *hash, int *msg_len) return false; } /* match space times. */ + // 匹配空格次数 for (size_t i = 0; i < lengthof(hash_offset_map); i++) { if (strncmp(message, hash_offset_map[i].name, 6) == 0) { /* 6: string length of INSERT/UPDATE/DELETE */ hash_offset = hash_offset_map[i].hash_offset; @@ -399,8 +418,11 @@ bool get_ledger_msg_hash(char *message, uint64 *hash, int *msg_len) if (hash_offset == 0) { size_t remain_len = len - pos; if (remain_len > 0) { + // 将哈希值从 message 中提取出来 *hash = strtoul(message + pos, NULL, 10); /* 10: Decimal */ + // 移除附加的哈希字符串 message[pos - 1] = '\0'; /* remove appended hash string. */ + // 更新消息的长度 *msg_len = *msg_len - remain_len - 1; return true; } @@ -426,23 +448,26 @@ bool get_ledger_msg_hash(char *message, uint64 *hash, int *msg_len) */ bool get_hist_name(Oid relid, const char *rel_name, char *hist_name, Oid nsp_oid, const char *nsp_name) { - errno_t rc; - if (!OidIsValid(relid) || rel_name == NULL) { - return false; - } - nsp_oid = OidIsValid(nsp_oid) ? nsp_oid : get_rel_namespace(relid); - nsp_name = (nsp_name == NULL) ? get_namespace_name(nsp_oid) : nsp_name; - int part_hist_name_len = strlen(rel_name) + strlen(nsp_name) + 1; - if (part_hist_name_len + strlen("_hist") >= NAMEDATALEN) { - rc = snprintf_s(hist_name, NAMEDATALEN, NAMEDATALEN - 1, "%d_%d_hist", nsp_oid, relid); - securec_check_ss(rc, "", ""); - } else { - rc = snprintf_s(hist_name, NAMEDATALEN, NAMEDATALEN - 1, "%s_%s_hist", nsp_name, rel_name); - securec_check_ss(rc, "", ""); - } - return true; -} + errno_t rc;// 用于存储 snprintf_s 函数的返回值 + if (!OidIsValid(relid) || rel_name == NULL) { + return false;// 如果表的 OID 无效或表名为空,则返回 false + } + nsp_oid = OidIsValid(nsp_oid) ? nsp_oid : get_rel_namespace(relid);// 如果模式 OID 有效,则使用给定值;否则获取表的模式 OID + nsp_name = (nsp_name == NULL) ? get_namespace_name(nsp_oid) : nsp_name;// 如果模式名为空,则获取模式名 + int part_hist_name_len = strlen(rel_name) + strlen(nsp_name) + 1;// 计算部分历史表名的长度(表名 + 模式名 + 下划线) + if (part_hist_name_len + strlen("_hist") >= NAMEDATALEN) { + // 如果完整历史表名(部分历史表名 + "_hist")的长度超过了 NAMEDATALEN,就使用 nsp_oid 和 relid 创建历史表名 + rc = snprintf_s(hist_name, NAMEDATALEN, NAMEDATALEN - 1, "%d_%d_hist", nsp_oid, relid); + securec_check_ss(rc, "", "");// 安全地检查 snprintf_s 的返回值 + } else { + // 否则,使用 nsp_name 和 rel_name 创建历史表名 + rc = snprintf_s(hist_name, NAMEDATALEN, NAMEDATALEN - 1, "%s_%s_hist", nsp_name, rel_name); + securec_check_ss(rc, "", "");// 安全地检查 snprintf_s 的返回值 + } + return true; // 返回 true 表示成功生成历史表名 +} +//此函数的目的是生成历史表的名称。如果给定了表的 OID、表名、模式 OID、模式名,它将使用这些信息来创建历史表的名称。如果未提供模式 OID 或模式名,它将根据表的 OID 获取模式信息。在生成历史表名时,它会检查表名、模式名的长度以及是否需要在历史表名中使用表的 OID 和模式 OID。最后,它将生成的历史表名存储在 hist_name 参数中,并返回 true 表示成功。 /* * querydesc_contains_ledger_usertable -- check querydesc result relation. * @@ -451,32 +476,39 @@ bool get_hist_name(Oid relid, const char *rel_name, char *hist_name, Oid nsp_oid bool querydesc_contains_ledger_usertable(QueryDesc *query_desc) { if (query_desc == NULL || query_desc->estate == NULL) { + // 如果传入的 QueryDesc 为空或者其 estate 为空,返回 false return false; } EState *estate = query_desc->estate; int relnum = estate->es_num_result_relations; if (relnum == 0 || estate->es_result_relations == NULL) { + // 如果结果关系数为 0 或者结果关系数组为空,返回 false return false; } for (int i = 0; i < relnum; ++i) { + // 遍历结果关系数组 if (estate->es_result_relations[i].ri_RelationDesc->rd_isblockchain) { + // 如果结果关系的描述符中包含 rd_isblockchain 为真,表示是账本用户表,返回 true return true; } - } + } // 没有找到账本用户表,返回 false return false; } - +//功能:检查查询描述中是否包含账本用户表。 +//描述:该函数接受一个查询描述结构体指针,检查其内部的执行状态是否包含账本用户表。如果包含,则返回 true,否则返回 false /* * ledger_check_switch_schema -- check two schema has same blockchain option. */ void ledger_check_switch_schema(Oid old_nsp, Oid new_nsp) { if (IsLedgerNameSpace(old_nsp) != IsLedgerNameSpace(new_nsp)) { + // 如果旧模式和新模式的区块链选项不一致,报错 ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("Unsupport to switch schema of a table between ledger schema and normal schema."))); } } - +// 功能:检查模式切换是否允许。 +//描述:该函数接受两个模式的 OID(旧模式和新模式),然后检查它们是否都属于账本模式或都不属于账本模式。如果不一致,抛出错误。 /* * is_ledger_rowstore -- check withOpt of CreateStmt. * @@ -492,12 +524,15 @@ bool is_ledger_rowstore(List *defList) DefElem* def = (DefElem*)lfirst(lc); if (pg_strcasecmp(def->defname, "orientation") == 0 && pg_strcasecmp(defGetString(def), ORIENTATION_ROW) != 0) { + // 如果 ORIENTATION 选项存在且值不为 ROW,返回 false return false; } } + // 未找到 ORIENTATION 选项或其值为 ROW,返回 true return true; } - +//功能:检查表的存储选项是否为 ROW。 +//描述:该函数接受一个表的定义选项列表,检查其中是否包含存储选项 "orientation",并且其值是否为 "ROW"。如果是,返回 true,否则返回 false。 bool is_ledger_hashbucketstore(List *defList) { ListCell *lc = NULL; @@ -507,11 +542,15 @@ bool is_ledger_hashbucketstore(List *defList) if (pg_strcasecmp(def->defname, "bucketcnt") == 0 || (pg_strcasecmp(def->defname, "hashbucket") == 0 && defGetBoolean(def))) { + // 如果选项中包含 bucketcnt 或者 hashbucket 为真,表示使用哈希分桶存储,返回 true return true; } } + // 未找到哈希分桶存储的选项,返回 false return false; } +//功能:检查表的存储选项是否为哈希分桶存储。 +//描述:该函数接受一个表的定义选项列表,检查其中是否包含 "bucketcnt" 或 "hashbucket" 存储选项,并且 "hashbucket" 的值为真。如果是哈希分桶存储,返回 true,否则返回 false。 /* * check_ledger_attrs_support -- check attrs is ledger supported. * @@ -523,6 +562,7 @@ bool is_ledger_hashbucketstore(List *defList) void check_ledger_attrs_support(List *attrs) { if (attrs == NIL) { + // 如果属性列表为空,直接返回 return; } ListCell *lc = NULL; @@ -534,6 +574,7 @@ void check_ledger_attrs_support(List *attrs) } Oid typid = colDef->typname->typeOid; if (!OidIsValid(typid)) { + // 如果列的类型无效,尝试获取类型信息 Type ctype = typenameType(NULL, colDef->typname, NULL); if (ctype != NULL) { typid = typeTypeId(ctype); @@ -541,6 +582,7 @@ void check_ledger_attrs_support(List *attrs) } } switch (typid) { + // 支持的列类型 case INT8OID: case INT1OID: case INT2OID: @@ -575,6 +617,7 @@ void check_ledger_attrs_support(List *attrs) case NUMERICOID: case UUIDOID: break; + // 不支持的列类型,报错 default: ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("Unsupport column type \"%s\" of ledger user table.", @@ -582,3 +625,5 @@ void check_ledger_attrs_support(List *attrs) } } } +//功能:检查表的列是否支持。 +// 描述:该函数接受一个表的列定义列表,遍历每个列,检查其数据类型是否属于支持的类型。如果遇到不支持的类型,将抛出错误。此函数还会跳过名为 "hash" 的列,因为它通常是用于存储哈希值的列。 \ No newline at end of file diff --git a/src/gausskernel/security/gs_ledger/userchain.cpp b/src/gausskernel/security/gs_ledger/userchain.cpp index 68dc66dcd..82f368777 100644 --- a/src/gausskernel/security/gs_ledger/userchain.cpp +++ b/src/gausskernel/security/gs_ledger/userchain.cpp @@ -47,6 +47,16 @@ * reloptions: relation options used to define new relation * mainTblStmt: Some statement of the query when create the new relation. */ +/* + * create_hist_relation: 为给定的用户表创建历史链关系表。 + * + * 参数: + * - rel:用户表的 Relation 对象。 + * - reloptions:历史表的附加选项。 + * - mainTblStmt:指向主用户表的 CreateStmt 的指针。 + * + * 此函数为用户表创建历史链表。历史链表存储了用户表的所有列,以及每个块的命令类型、块编号和哈希值。 + */ void create_hist_relation(Relation rel, Datum reloptions, CreateStmt *mainTblStmt) { errno_t rc; @@ -59,60 +69,59 @@ void create_hist_relation(Relation rel, Datum reloptions, CreateStmt *mainTblStm int16 coloptions[1]; bool shared_relation = rel->rd_rel->relisshared; + // 生成历史链表的名称 get_hist_name(relid, get_rel_name(relid), hist_name); - /* - * history chain table contains all the columns from the origin user table, and then need to - * record the command type, blocknum, and hash value of last block record. - */ + // 创建历史链表的描述 TupleDesc chain_desc = CreateTemplateTupleDesc(USERCHAIN_COLUMN_NUM, false); - /* Now consider the additional columns and initilize the description */ + // 初始化描述中的额外列 TupleDescInitEntry(chain_desc, USERCHAIN_COLUMN_REC_NUM + 1, "rec_num", INT8OID, -1, 0); TupleDescInitEntry(chain_desc, USERCHAIN_COLUMN_HASH_INS + 1, "hash_ins", HASH16OID, -1, 0); TupleDescInitEntry(chain_desc, USERCHAIN_COLUMN_HASH_DEL + 1, "hash_del", HASH16OID, -1, 0); - TupleDescInitEntry(chain_desc, USERCHAIN_COLUMN_PREVHASH + 1, "pre_hash", HASH32OID, -1, 0); + // 添加内部选项以进行历史记录 reloptions = AddInternalOption(reloptions, INTERNAL_MASK_DALTER | INTERNAL_MASK_DDELETE | INTERNAL_MASK_DINSERT | INTERNAL_MASK_DUPDATE); + // 创建历史链表 hist_oid = heap_create_with_catalog(hist_name, nsp_oid, rel->rd_rel->reltablespace, InvalidOid, InvalidOid, InvalidOid, rel->rd_rel->relowner, chain_desc, NIL, 'r', (rel->rd_rel->relpersistence == 't') ? 'u' : rel->rd_rel->relpersistence, shared_relation, false, true, 0, ONCOMMIT_NOOP, reloptions, false, true, NULL, REL_CMPRS_NOT_SUPPORT, NULL, false); - /* make the history chain relation visible, else heap_open will fail */ + // 使历史链表可见,否则 heap_open 将失败 CommandCounterIncrement(); #ifdef ENABLE_MULTIPLE_NODES bool is_initdb_on_dn = false; - /* Add to pgxc_class */ - /* When the sum of shmemNumDataNodes and shmemNumCoords equals to one, - * the create table command is executed on datanode during initialization . - * In this case, we do not write created table info in pgxc_class. + /* 添加到 pgxc_class */ + /* 当 shmemNumDataNodes 和 shmemNumCoords 的总和等于一时, + * 在初始化期间在数据节点上执行创建表命令。 + * 在这种情况下,我们不会将创建的表信息写入 pgxc_class。 */ if ((*t_thrd.pgxc_cxt.shmemNumDataNodes + *t_thrd.pgxc_cxt.shmemNumCoords) == 1) { is_initdb_on_dn = true; } - /* only support normal table, do not support foreign table (can be supported in the future) */ + // 仅支持普通表,不支持外部表(未来可能支持) if ((!u_sess->attr.attr_common.IsInplaceUpgrade || !IsSystemNamespace(nsp_oid)) && (IS_PGXC_COORDINATOR || (isRestoreMode && mainTblStmt->distributeby != NULL && !is_initdb_on_dn))) { AddRelationDistribution(hist_name, hist_oid, NULL, mainTblStmt->subcluster, InvalidOid, chain_desc, true); CommandCounterIncrement(); - /* Make sure locator info gets rebuilt */ + /* 确保定位器信息得到重建 */ RelationCacheInvalidateEntry(hist_oid); } #endif - /* now create index for this new history table */ + // 为新历史表创建索引 char hist_index_name[NAMEDATALEN]; rc = snprintf_s(hist_index_name, NAMEDATALEN, NAMEDATALEN - 1, "gs_hist_%u_index", relid); securec_check_ss(rc, "", ""); - /* open the previous created history chain table */ + // 打开先前创建的历史链表 Relation hist_rel = heap_open(hist_oid, ShareLock); IndexInfo *hist_index = makeNode(IndexInfo); hist_index->ii_NumIndexAttrs = 1; @@ -140,6 +149,7 @@ void create_hist_relation(Relation rel, Datum reloptions, CreateStmt *mainTblStm extra.isPartitionedIndex = false; extra.isGlobalPartitionedIndex = false; + // 创建索引 index_create(hist_rel, hist_index_name, InvalidOid, InvalidOid, hist_index, list_make1((void *)"rec_num"), BTREE_AM_OID, rel->rd_rel->reltablespace, collationObjectId, classObjectId, @@ -148,7 +158,7 @@ void create_hist_relation(Relation rel, Datum reloptions, CreateStmt *mainTblStm heap_close(hist_rel, NoLock); - /* Specify dependent between history table and origin table with depend option audo. */ + // 指定历史表与原始表之间的依赖关系,自动处理依赖关系 ObjectAddress myself; ObjectAddress referenced; myself.classId = RelationRelationId; @@ -159,10 +169,10 @@ void create_hist_relation(Relation rel, Datum reloptions, CreateStmt *mainTblStm referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO); + // 释放资源 pfree_ext(chain_desc); - /* - * Make changes visible - */ + + // 使更改可见 CommandCounterIncrement(); } @@ -175,21 +185,33 @@ void create_hist_relation(Relation rel, Datum reloptions, CreateStmt *mainTblStm * Note: This function is used after origin user table renamed, and then caller * can use this function to rename the corresponding hist table name. */ +/* + * rename_hist_by_usertable: 根据用户表的 OID 重命名相关的历史表。 + * + * 参数: + * - relid: 用户表的 OID。 + * - new_usertable_name: 新的用户表名称。 + * + * 此函数用于重命名与给定用户表相关的历史表。 + */ void rename_hist_by_usertable(Oid relid, const char *new_usertable_name) { Oid hist_oid = get_hist_oid(relid); char new_hist_name[NAMEDATALEN]; get_hist_name(relid, new_usertable_name, new_hist_name); - /* Do rename hist table. */ + /* 执行历史表重命名。 */ RenameRelationInternal(hist_oid, new_hist_name); } /* - * rename_hist_by_newnsp -- rename one hist table while altering schema name + * rename_hist_by_newnsp: 在更改模式名称时重命名一个历史表。 * - * user_relid: relation oid of user table - * new_nsp_name: the new schema name of user table + * 参数: + * - user_relid: 用户表的关系 OID。 + * - new_nsp_name: 用户表的新模式名称。 + * + * 此函数在更改用户表的模式名称时重命名一个历史表。 */ void rename_hist_by_newnsp(Oid user_relid, const char *new_nsp_name) { @@ -198,20 +220,25 @@ void rename_hist_by_newnsp(Oid user_relid, const char *new_nsp_name) char new_hist_name[NAMEDATALEN] = {0}; get_hist_name(user_relid, get_rel_name(user_relid), old_hist_name); hist_oid = get_relname_relid(old_hist_name, PG_BLOCKCHAIN_NAMESPACE); - /* Some especial tables such as foreign tables have no hist table. So make sure hist exists. */ + + /* 某些特殊的表,如外部表,没有历史表。因此请确保历史表存在。 */ if (!OidIsValid(hist_oid)) { return; } + get_hist_name(user_relid, get_rel_name(user_relid), new_hist_name, get_rel_namespace(user_relid), new_nsp_name); RenameRelationInternal(hist_oid, new_hist_name); } /* - * rename_histlist_by_newnsp -- rename a list of hist table while altering schema name + * rename_histlist_by_newnsp: 在更改模式名称时重命名一组历史表。 * - * usertable_oid_list: relation oid list of user tables - * new_nsp_name: the new schema name of user table + * 参数: + * - usertable_oid_list: 用户表关系 OID 列表。 + * - new_nsp_name: 用户表的新模式名称。 + * + * 此函数在更改用户表的模式名称时重命名一组历史表。 */ void rename_histlist_by_newnsp(List *usertable_oid_list, const char *new_nsp_name) { @@ -224,14 +251,20 @@ void rename_histlist_by_newnsp(List *usertable_oid_list, const char *new_nsp_nam } /* - * user_hash_attrno -- get the attribute number of user table's hash column. + * user_hash_attrno: 获取用户表哈希列的属性编号。 * - * rd_att: tuple description of user table + * 参数: + * - rd_att: 用户表的元组描述。 + * + * 返回值:哈希列的属性编号。 + * + * 此函数用于获取用户表的哈希列的属性编号。 */ int user_hash_attrno(const TupleDesc rd_att) { int hash_natt = -1; Form_pg_attribute rel_attr = NULL; + for (int i = rd_att->natts - 1; i >= 0; i--) { rel_attr = rd_att->attrs[i]; if (strcmp(rel_attr->attname.data, "hash") == 0) { @@ -239,6 +272,7 @@ int user_hash_attrno(const TupleDesc rd_att) break; } } + return hash_natt; } @@ -251,29 +285,39 @@ int user_hash_attrno(const TupleDesc rd_att) */ static void hash_combine_tuple_data(char *buf, int buf_size, TupleDesc tabledesc, HeapTuple tuple) { - int natts = tabledesc->natts; - int buflen = 0; - errno_t rc = EOK; - char hash_str[UINT64STRSIZE + 1] = {0}; - Datum *values = (Datum *) palloc0(natts * sizeof(Datum)); - bool *nulls = (bool *) palloc0(natts * sizeof(bool)); - heap_deform_tuple(tuple, tabledesc, values, nulls); - for (int i = 0; i < natts - 1; ++i) { /* except 'hash' column. */ + int natts = tabledesc->natts; // 获取表的列数 + + int buflen = 0; // 初始化缓冲区长度 + errno_t rc = EOK; // 用于错误处理的变量 + + char hash_str[UINT64STRSIZE + 1] = {0}; // 用于存储列的哈希值的字符串数组 + + Datum *values = (Datum *) palloc0(natts * sizeof(Datum)); // 分配空间用于存储列的数据值 + bool *nulls = (bool *) palloc0(natts * sizeof(bool)); // 分配空间用于存储列的NULL值信息 + + heap_deform_tuple(tuple, tabledesc, values, nulls); // 将堆元组解析成列数据和NULL信息 + + for (int i = 0; i < natts - 1; ++i) { // 遍历每一列,排除 'hash' 列 if (nulls[i]) { - continue; + continue; // 如果该列为NULL,跳过 } - uint64 col_hash = compute_hash(tabledesc->attrs[i]->atttypid, values[i], LOCATOR_TYPE_HASH); - rc = snprintf_s(hash_str, UINT64STRSIZE + 1, UINT64STRSIZE, "%lu", col_hash); - securec_check_ss(rc, "", ""); - rc = snprintf_s(buf + buflen, buf_size - buflen, buf_size - buflen - 1, "%s", hash_str); - securec_check_ss(rc, "", ""); - buflen += strlen(hash_str); + uint64 col_hash = compute_hash(tabledesc->attrs[i]->atttypid, values[i], LOCATOR_TYPE_HASH); // 计算列的哈希值 + + rc = snprintf_s(hash_str, UINT64STRSIZE + 1, UINT64STRSIZE, "%lu", col_hash); // 将哈希值转换成字符串 + securec_check_ss(rc, "", ""); // 检查 snprintf_s 是否成功 + + rc = snprintf_s(buf + buflen, buf_size - buflen, buf_size - buflen - 1, "%s", hash_str); // 将哈希值字符串添加到缓冲区 + securec_check_ss(rc, "", ""); // 检查 snprintf_s 是否成功 + + buflen += strlen(hash_str); // 更新缓冲区长度 } - pfree_ext(values); - pfree_ext(nulls); + + pfree_ext(values); // 释放列数据值内存 + pfree_ext(nulls); // 释放NULL信息内存 } + /* * get_user_tuple_hash -- get the hash value of usertable's tuple. * @@ -282,14 +326,20 @@ static void hash_combine_tuple_data(char *buf, int buf_size, TupleDesc tabledesc */ uint64 get_user_tuple_hash(HeapTuple tuple, TupleDesc desc) { - Datum value; - bool isnull = false; - int hash_attno = user_hash_attrno(desc); + Datum value; // 用于存储列数据值的变量 + bool isnull = false; // 用于存储是否为NULL的标志 + int hash_attno = user_hash_attrno(desc); // 获取哈希列的列号 + + // 获取最后一列的数据值(根据列号),并将是否为NULL的信息存储在 isnull 中 value = heap_getattr(tuple, hash_attno + 1, desc, &isnull); /* get last column. */ - Assert(!isnull); + + Assert(!isnull); // 使用断言确保数据值不为NULL,否则抛出错误 + + // 将数据值转换为 uint64 并返回 return DatumGetUInt64(value); } + /* * gen_user_tuple_hash -- generate hash of each user table's tuple. * @@ -298,27 +348,36 @@ uint64 get_user_tuple_hash(HeapTuple tuple, TupleDesc desc) */ static uint64 gen_user_tuple_hash(Relation rel, HeapTuple tuple) { - TupleDesc tabledesc = RelationGetDescr(rel); - int data_size = UINT64STRSIZE * tabledesc->natts + 1; - char *data_string = (char *)palloc0(data_size * sizeof(char)); + TupleDesc tabledesc = RelationGetDescr(rel); // 获取表的元组描述符 + + int data_size = UINT64STRSIZE * tabledesc->natts + 1; // 计算存储数据的字符串所需的缓冲区大小 + char *data_string = (char *)palloc0(data_size * sizeof(char)); // 分配存储数据的字符串的内存空间,并初始化为0 + + // 调用 hash_combine_tuple_data 函数将元组数据计算为字符串形式并存储在 data_string 中 hash_combine_tuple_data(data_string, data_size, tabledesc, tuple); - uint8 sum[16]; - if (pg_md5_binary(data_string, strlen(data_string), sum) == false) { - pfree_ext(data_string); - ereport(ERROR, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("out of memory"))); - } - uint64 result = 0; - for (int i = 0; i < 7; i++) { - result |= sum[4 + i]; - result = (result << 8); - } - result |= sum[11]; + uint8 sum[16]; // 用于存储 MD5 哈希的结果 - pfree_ext(data_string); - return result; + // 使用 pg_md5_binary 函数计算 data_string 的 MD5 哈希值,结果存储在 sum 中 + if (pg_md5_binary(data_string, strlen(data_string), sum) == false) { + pfree_ext(data_string); // 释放 data_string 内存 + ereport(ERROR, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("out of memory"))); // 如果计算失败,则抛出内存不足的错误 + } + + uint64 result = 0; // 用于存储最终的哈希结果 + + for (int i = 0; i < 7; i++) { + result |= sum[4 + i]; // 将 sum 中的字节按位合并到 result 中 + result = (result << 8); // 左移 8 位,为下一个字节腾出位置 + } + + result |= sum[11]; // 将最后一个字节合并到 result 中 + + pfree_ext(data_string); // 释放 data_string 内存 + return result; // 返回最终的哈希结果 } + /* * set_user_tuple_hash -- calculate and fill the hash attribute of user table's tuple. * @@ -331,34 +390,52 @@ static uint64 gen_user_tuple_hash(Relation rel, HeapTuple tuple) */ HeapTuple set_user_tuple_hash(HeapTuple tup, Relation rel, bool hash_exists) { + // 计算元组的哈希值 uint64 row_hash = gen_user_tuple_hash(rel, tup); + + // 获取哈希列的列号 int hash_attrno = user_hash_attrno(rel->rd_att); + if (hash_exists) { bool is_null; Datum hash = heap_getattr(tup, hash_attrno + 1, rel->rd_att, &is_null); + + // 如果哈希值列为NULL或者与计算得到的哈希值不匹配,则抛出错误 if (is_null || row_hash != DatumGetUInt64(hash)) { ereport(ERROR, (errcode(ERRCODE_OPERATE_INVALID_PARAM), errmsg("Invalid tuple hash."))); } - return tup; + + return tup; // 如果哈希值已存在且匹配,直接返回原始元组 } + Datum *values = NULL; bool *nulls = NULL; bool *replaces = NULL; - /* Build modified tuple */ + + // 获取表的属性数量 int2 nattrs = RelationGetNumberOfAttributes(rel); + + // 分配内存用于存储修改后的元组的数据值、NULL信息和替换标志 values = (Datum*)palloc0(nattrs * sizeof(Datum)); nulls = (bool*)palloc0(nattrs * sizeof(bool)); replaces = (bool*)palloc0(nattrs * sizeof(bool)); + + // 设置哈希值列的数据值和替换标志 values[hash_attrno] = UInt64GetDatum(row_hash); replaces[hash_attrno] = true; + + // 使用 heap_modify_tuple 函数创建修改后的元组 HeapTuple newtup = heap_modify_tuple(tup, RelationGetDescr(rel), values, nulls, replaces); + // 释放分配的内存 pfree_ext(values); pfree_ext(nulls); pfree_ext(replaces); - return newtup; + + return newtup; // 返回修改后的元组 } + /* * get_hist_oid -- get the oid of history table by oid, name and namespace name of user table * @@ -369,12 +446,18 @@ HeapTuple set_user_tuple_hash(HeapTuple tup, Relation rel, bool hash_exists) Oid get_hist_oid(Oid relid, const char *rel_name, Oid rel_nsp) { if (rel_name == NULL) { + // 如果传入的表名为空,根据 relid 获取表名 rel_name = get_rel_name(relid); } - char hist_name[NAMEDATALEN]; + char hist_name[NAMEDATALEN]; // 用于存储历史表的名称 + + // 获取历史表的名称,这个函数的实现没有提供,但可以推测它根据传入的参数生成历史表的名称 get_hist_name(relid, rel_name, hist_name, rel_nsp); + + // 根据历史表的名称和命名空间获取历史表的 OID Oid hist_oid = get_relname_relid(hist_name, PG_BLOCKCHAIN_NAMESPACE); - return hist_oid; + + return hist_oid; // 返回历史表的 OID } /* @@ -394,31 +477,36 @@ uint64 get_user_tupleid_hash(Relation relation, ItemPointer tupleid) TupleDesc tabledescr; uint64 result; + // 获取表的元组描述符 tabledescr = RelationGetDescr(relation); - /* get tuple use tupleid */ - block = ItemPointerGetBlockNumber(tupleid); - buffer = ReadBuffer(relation, block); + + /* 根据 tupleid 获取元组数据 */ + block = ItemPointerGetBlockNumber(tupleid); // 获取元组所在的数据块号 + buffer = ReadBuffer(relation, block); // 读取数据块的缓冲区 page = BufferGetPage(buffer); + + // 如果数据块中的所有元组都可见,则锁定可见性映射 if (PageIsAllVisible(page)) { visibilitymap_pin(relation, block, &vmbuffer); } - LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE); + LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE); // 锁定数据块的缓冲区,以便访问其中的元组 - lp = PageGetItemId(page, ItemPointerGetOffsetNumber(tupleid)); - tp.t_tableOid = RelationGetRelid(relation); - tp.t_data = (HeapTupleHeader) PageGetItem(page, lp); - tp.t_len = ItemIdGetLength(lp); - tp.t_self = *tupleid; + lp = PageGetItemId(page, ItemPointerGetOffsetNumber(tupleid)); // 获取元组在数据块中的位置 + tp.t_tableOid = RelationGetRelid(relation); // 获取表的 OID + tp.t_data = (HeapTupleHeader) PageGetItem(page, lp); // 获取元组的数据 + tp.t_len = ItemIdGetLength(lp); // 获取元组的长度 + tp.t_self = *tupleid; // 设置元组的位置信息 + // 计算元组的哈希值 result = get_user_tuple_hash(&tp, tabledescr); - UnlockReleaseBuffer(buffer); + UnlockReleaseBuffer(buffer); // 解锁并释放数据块的缓冲区 if (vmbuffer != InvalidBuffer) { - ReleaseBuffer(vmbuffer); + ReleaseBuffer(vmbuffer); // 释放可见性映射的缓冲区 } - return result; + return result; // 返回计算得到的哈希值 } /* @@ -434,23 +522,38 @@ void gen_hist_tuple_hash(Oid relid, char *current_block_data, bool pre_row_exist hash32_t *pre_row_hash, hash32_t *hash) { errno_t rc; + + // 计算缓冲区的大小,用于存储当前块数据和哈希值 int buf_size = strlen(current_block_data) + NAMEDATALEN + 1; + + // 分配内存用于存储数据字符串 char *data_string = (char *)palloc0(buf_size * sizeof(char)); + if (pre_row_exist) { + // 如果前一行存在,则将前一行的哈希值转换为字符串 char *pre_hash_str = DatumGetCString(DirectFunctionCall1(hash32out, HASH32GetDatum(pre_row_hash))); + + // 将当前块数据和前一行哈希值字符串拼接到一起 rc = snprintf_s(data_string, buf_size, buf_size - 1, "%s%s", current_block_data, pre_hash_str); + + // 释放前一行哈希值字符串的内存 pfree_ext(pre_hash_str); } else { + // 如果前一行不存在,则根据 relid 获取表名 char *rel_name = get_rel_name(relid); + + // 将当前块数据和表名字符串拼接到一起 rc = snprintf_s(data_string, buf_size, buf_size - 1, "%s%s", current_block_data, rel_name); } - securec_check_ss(rc, "", ""); + securec_check_ss(rc, "", ""); // 检查 snprintf_s 是否成功 + // 使用 pg_md5_binary 计算数据字符串的 MD5 哈希值,结果存储在 hash->data 中 if (!pg_md5_binary(data_string, strlen(data_string), hash->data)) { - pfree_ext(data_string); - ereport(ERROR, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("out of memory"))); + pfree_ext(data_string); // 释放数据字符串内存 + ereport(ERROR, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("out of memory"))); // 如果计算失败,则抛出内存不足的错误 } - pfree_ext(data_string); + + pfree_ext(data_string); // 释放数据字符串内存 } /* @@ -463,16 +566,20 @@ void gen_hist_tuple_hash(Oid relid, char *current_block_data, bool pre_row_exist */ static void fill_hist_block(Oid histoid, uint64 hash_ins, uint64 hash_del, HistBlock *block) { - char data[NAMEDATALEN] = {0}; + char data[NAMEDATALEN] = {0}; // 初始化一个字符数组用于存储数据 - block->rec_num = get_next_recnum(histoid); - /* Before generate previous hash, we should get current block information */ + block->rec_num = get_next_recnum(histoid); // 获取下一个记录号 + + /* 在生成前一块哈希之前,我们应该获取当前块的信息 */ + // 将记录号、插入哈希值和删除哈希值格式化为字符串并存储在 data 中 error_t rc = sprintf_s(data, NAMEDATALEN, "%lu%lu%lu", block->rec_num, hash_ins, hash_del); - securec_check_ss(rc, "", ""); + securec_check_ss(rc, "", ""); // 检查 sprintf_s 是否成功 + // 调用 gen_hist_tuple_hash 函数计算当前块的哈希值,并存储在 block->prev_hash 中 gen_hist_tuple_hash(histoid, data, false, NULL, &block->prev_hash); } + /* * hist_table_record_internal -- append record to history table when user table is modified * @@ -482,22 +589,25 @@ static void fill_hist_block(Oid histoid, uint64 hash_ins, uint64 hash_del, HistB */ bool hist_table_record_internal(Oid hist_oid, const uint64 *hash_ins, const uint64 *hash_del) { - Datum values[USERCHAIN_COLUMN_NUM] = {0}; - bool nulls[USERCHAIN_COLUMN_NUM] = {false}; - bool ins_null = hash_ins == NULL; - bool del_null = hash_del == NULL; - uint64 t_ins = ins_null ? 0 : *hash_ins; - uint64 t_del = del_null ? 0 : *hash_del; - HistBlock block; + Datum values[USERCHAIN_COLUMN_NUM] = {0}; // 用于存储要插入的列的数据值 + bool nulls[USERCHAIN_COLUMN_NUM] = {false}; // 用于存储要插入的列的 NULL 信息 + bool ins_null = hash_ins == NULL; // 判断插入哈希值是否为 NULL + bool del_null = hash_del == NULL; // 判断删除哈希值是否为 NULL + uint64 t_ins = ins_null ? 0 : *hash_ins; // 获取插入哈希值,如果为 NULL 则为 0 + uint64 t_del = del_null ? 0 : *hash_del; // 获取删除哈希值,如果为 NULL 则为 0 + HistBlock block; // 用于存储历史块的信息 if (!OidIsValid(hist_oid)) { + // 如果历史表的 OID 无效,抛出错误并返回 false ereport(ERROR, (errcode(ERRCODE_UNDEFINED_TABLE), errmsg("could not find history table"))); return false; } - /* Before generate previous hash, we should get current block information */ + /* 在生成前一块哈希之前,我们应该获取当前块的信息 */ + // 调用 fill_hist_block 函数填充历史块的信息 fill_hist_block(hist_oid, t_ins, t_del, &block); + // 填充要插入的列的数据值和 NULL 信息 values[USERCHAIN_COLUMN_REC_NUM] = UInt64GetDatum(block.rec_num); values[USERCHAIN_COLUMN_HASH_INS] = UInt64GetDatum(t_ins); values[USERCHAIN_COLUMN_HASH_DEL] = UInt64GetDatum(t_del); @@ -507,17 +617,26 @@ bool hist_table_record_internal(Oid hist_oid, const uint64 *hash_ins, const uint nulls[USERCHAIN_COLUMN_HASH_DEL] = del_null; nulls[USERCHAIN_COLUMN_PREVHASH] = false; + // 打开历史表 Relation hist_rel = heap_open(hist_oid, RowExclusiveLock); TupleDesc hist_desc = RelationGetDescr(hist_rel); + // 使用 heap_form_tuple 函数创建要插入的元组 HeapTuple tuple = heap_form_tuple(hist_desc, values, nulls); + + // 将元组插入历史表 simple_heap_insert(hist_rel, tuple); + + // 释放元组内存 heap_freetuple(tuple); + + // 关闭历史表 heap_close(hist_rel, RowExclusiveLock); - return true; + return true; // 返回 true 表示插入成功 } + /* * hist_table_record_insert -- append a record while inserting into user table * @@ -527,17 +646,25 @@ bool hist_table_record_internal(Oid hist_oid, const uint64 *hash_ins, const uint */ bool hist_table_record_insert(Relation rel, HeapTuple tup, uint64 *res_hash) { - /* check all inputs are avaliable */ + /* 检查所有输入是否有效 */ if (tup == NULL || rel == NULL) { - return false; /* Do some thing */ + return false; /* 做一些处理 */ } + // 计算插入哈希值 uint64 hash_ins = get_user_tuple_hash(tup, rel->rd_att); + + // 获取历史表的 OID Oid hist_oid = get_hist_oid(RelationGetRelid(rel), RelationGetRelationName(rel), RelationGetNamespace(rel)); + + // 将插入哈希值存储在 res_hash 中 *res_hash = hash_ins; + + // 调用 hist_table_record_internal 函数插入历史记录 return hist_table_record_internal(hist_oid, &hash_ins, NULL); } + /* * hist_table_record_delete -- append a record while deleting from user table * @@ -547,12 +674,18 @@ bool hist_table_record_insert(Relation rel, HeapTuple tup, uint64 *res_hash) */ bool hist_table_record_delete(Relation rel, uint64 hash_del, uint64 *res_hash) { + // 获取历史表的 OID Oid hist_oid = get_hist_oid(RelationGetRelid(rel), RelationGetRelationName(rel), RelationGetNamespace(rel)); + + // 计算删除哈希值,并将其存储在 res_hash 中(取负值) *res_hash = -hash_del; - /* insert history record into userchain table */ + + /* 将历史记录插入用户链表表 */ + // 调用 hist_table_record_internal 函数删除历史记录 return hist_table_record_internal(hist_oid, NULL, &hash_del); } + /* * hist_table_record_update -- append a record while updating user table * @@ -563,13 +696,21 @@ bool hist_table_record_delete(Relation rel, uint64 hash_del, uint64 *res_hash) */ bool hist_table_record_update(Relation rel, HeapTuple newtup, uint64 hash_del, uint64 *res_hash) { + // 计算插入哈希值 uint64 hash_ins = get_user_tuple_hash(newtup, rel->rd_att); + + // 获取历史表的 OID Oid hist_oid = get_hist_oid(RelationGetRelid(rel), RelationGetRelationName(rel), RelationGetNamespace(rel)); + + // 计算更新哈希值(插入哈希值减去删除哈希值)并存储在 res_hash 中 *res_hash = hash_ins - hash_del; - /* insert history record into userchain table */ + + /* 将历史记录插入用户链表表 */ + // 调用 hist_table_record_internal 函数更新历史记录 return hist_table_record_internal(hist_oid, &hash_ins, &hash_del); } + /* * get_copyfrom_line_relhash -- extract hash from each copyfrom line * @@ -582,22 +723,30 @@ bool hist_table_record_update(Relation rel, HeapTuple newtup, uint64 hash_del, u bool get_copyfrom_line_relhash(const char *row_data, int len, int hash_colno, char split, uint64 *hash) { int pos; - /* Not found hash column. */ + + /* 如果未找到哈希列,返回 false */ if (hash_colno == -1) { return false; } + + // 遍历行数据,寻找哈希列的位置 for (pos = 0; pos < len && hash_colno > 0; ++pos) { if (row_data[pos] == split) { --hash_colno; } } + + // 如果找到了哈希列 if (hash_colno == 0) { - int remain_len = len - pos; - const char *hash_str = row_data + pos; + int remain_len = len - pos; // 哈希值字符串的长度 + const char *hash_str = row_data + pos; // 哈希值字符串的起始位置 + + // 如果剩余长度大于 0,将哈希值字符串转换为 uint64 类型的哈希值 if (remain_len > 0) { *hash = DatumGetUInt64(DirectFunctionCall1(hash16in, CStringGetDatum(hash_str))); return true; } } + return false; } diff --git a/src/gausskernel/security/gs_policy/curl_utils.cpp b/src/gausskernel/security/gs_policy/curl_utils.cpp index 5720f85e8..7ccda1c22 100644 --- a/src/gausskernel/security/gs_policy/curl_utils.cpp +++ b/src/gausskernel/security/gs_policy/curl_utils.cpp @@ -36,9 +36,9 @@ #include "gs_policy/curl_utils.h" #include "utils/elog.h" - +// 用于多线程访问的互斥锁 static std::mutex g_i_mutex; - +// CurlUtils 类的构造函数 CurlUtils::CurlUtils() : m_withSSL(false), m_certificate(""), m_user(""), @@ -46,12 +46,12 @@ CurlUtils::CurlUtils() : m_withSSL(false), m_curlForPost(NULL) { } - +// CurlUtils 类的析构函数 CurlUtils::~CurlUtils() -{ +{ // 清理 Curl 对象 curl_easy_cleanup(m_curlForPost); } - +// 初始化 CurlUtils 类的成员变量 void CurlUtils::initialize(bool withSSL, const std::string certificate, const std::string user, const std::string password) { @@ -65,16 +65,20 @@ void CurlUtils::initialize(bool withSSL, const std::string certificate, const st /* * Send file to remote web server as rest interface */ + // 发送 HTTP POST 请求,上传文件 bool CurlUtils::http_post_file_request(const std::string url, const std::string fileName, bool connection_testing) -{ +{// 记录日志 ereport(INFO, (errmsg("Url = %s, fileName = %s", url.c_str(), fileName.c_str()))); + // 从文件中读取文件内容 std::ifstream t(fileName); std::string str((std::istreambuf_iterator(t)), std::istreambuf_iterator()); if (m_curlForPost != NULL) { + // 设置请求的 Content-Type struct curl_slist *slist1 = NULL; slist1 = curl_slist_append(slist1, "Content-Type: application/json"); + // 设置 Curl 选项 (void)curl_easy_setopt(m_curlForPost, CURLOPT_URL, url.c_str()); (void)curl_easy_setopt(m_curlForPost, CURLOPT_NOPROGRESS, 1L); (void)curl_easy_setopt(m_curlForPost, CURLOPT_POSTFIELDS, str.c_str()); @@ -92,11 +96,13 @@ bool CurlUtils::http_post_file_request(const std::string url, const std::string (void)curl_easy_setopt(m_curlForPost, CURLOPT_TCP_KEEPALIVE, 1L); /* a simply connection test to server, just verify the connection without any data transfer */ + // 用于连接测试,只验证连接而不传输数据 if (connection_testing) { (void)curl_easy_setopt(m_curlForPost, CURLOPT_CONNECT_ONLY, 1L); } /* perform a file transfer */ + // 执行文件传输 CURLcode res = curl_easy_perform(m_curlForPost); if (res != CURLE_OK) { /* @@ -108,15 +114,17 @@ bool CurlUtils::http_post_file_request(const std::string url, const std::string (errmsg("make sure connection to elastic_search_ip_addr, error info: %s\n", curl_easy_strerror(res)))); } - + // 清理资源并重置 Curl 对象 curl_slist_free_all(slist1); curl_easy_reset(m_curlForPost); ereport(WARNING, (errmsg("Connection issue happended, post file error: %s\n", curl_easy_strerror(res)))); return false; } + // 清理资源并重置 Curl 对象 curl_slist_free_all(slist1); curl_easy_reset(m_curlForPost); } return true; } +//是一个使用 libcurl 库发送 HTTP POST 请求的示例。它包含了初始化 Curl 对象、发送文件请求、处理连接问题等功能。此代码适用于在 C++ 程序中使用 libcurl 库进行网络通信。 \ No newline at end of file diff --git a/src/gausskernel/security/gs_policy/gs_policy_audit.cpp b/src/gausskernel/security/gs_policy/gs_policy_audit.cpp index 6209b6bd4..117ae4c16 100644 --- a/src/gausskernel/security/gs_policy/gs_policy_audit.cpp +++ b/src/gausskernel/security/gs_policy/gs_policy_audit.cpp @@ -48,17 +48,20 @@ #include "utils/syscache.h" #include "pgaudit.h" -LoadPoliciesPtr load_audit_policies_hook = NULL; -LoadPolicyAccessPtr load_policy_access_hook = NULL; -LoadPolicyPrivilegesPtr load_policy_privileges_hook = NULL; -LoadPolicyFilterPtr load_policy_filter_hook = NULL; -THR_LOCAL LightUnifiedAuditExecutorPtr light_unified_audit_executor_hook = NULL; -OpFusionUnifiedAuditExecutorPtr opfusion_unified_audit_executor_hook = NULL; -OpFusionUnifiedAuditFlushLogsPtr opfusion_unified_audit_flush_logs_hook = NULL; +// 定义钩子函数指针,用于加载安全策略相关的钩子函数 +LoadPoliciesPtr load_audit_policies_hook = NULL; // 用于加载审计策略的钩子函数 +LoadPolicyAccessPtr load_policy_access_hook = NULL; // 用于加载策略访问权限的钩子函数 +LoadPolicyPrivilegesPtr load_policy_privileges_hook = NULL; // 用于加载策略特权的钩子函数 +LoadPolicyFilterPtr load_policy_filter_hook = NULL; // 用于加载策略过滤器的钩子函数 +THR_LOCAL LightUnifiedAuditExecutorPtr light_unified_audit_executor_hook = NULL; // 用于加载轻量级统一审计执行器的钩子函数 +OpFusionUnifiedAuditExecutorPtr opfusion_unified_audit_executor_hook = NULL; // 用于加载操作融合统一审计执行器的钩子函数 +OpFusionUnifiedAuditFlushLogsPtr opfusion_unified_audit_flush_logs_hook = NULL; // 用于加载操作融合统一审计刷新日志的钩子函数 +// 字符串数组,包含了特权类型的字符串 static const char* privileges_type[] = { "alter", "analyze", "comment", "create", "drop", "grant", "revoke", "set", "show", "login_any", "login_failure", "login_success", "logout"}; +// 字符串数组,包含了访问类型的字符串 static const char* access_type[] = {"copy", "deallocate", "delete", "execute", "insert", "prepare", "reindex", "select", "truncate", "update"}; @@ -73,51 +76,79 @@ static const char* access_type[] = {"copy", "deallocate", "delete", "execute", " * @relation : relation to add configuration * @policyOid : policy id */ +// 这个函数用于添加安全策略的操作类型到数据库中,可以是访问操作类型或特权操作类型。 + static void add_action_type(bool is_access, const char *action_type, const gs_stl::gs_string target_name_s, Relation relation, Oid policyOid) { - HeapTuple policy_htup = NULL; + HeapTuple policy_htup = NULL; // 用于存储策略的元组 + + // 根据是否是访问操作来选择合适的字段和值 if (is_access) { - bool pol_nulls[Natts_gs_auditing_policy_acc] = {false}; - Datum pol_values[Natts_gs_auditing_policy_acc] = {0}; + // 如果是访问操作 + bool pol_nulls[Natts_gs_auditing_policy_acc] = {false}; // 策略表的访问操作字段是否为NULL + Datum pol_values[Natts_gs_auditing_policy_acc] = {0}; // 策略表的访问操作字段的值 + + // 设置策略表的字段值 pol_values[Anum_gs_auditing_policy_acc_type - 1] = DirectFunctionCall1(namein, CStringGetDatum(action_type)); pol_values[Anum_gs_auditing_policy_acc_label_name - 1] = DirectFunctionCall1(namein, CStringGetDatum(target_name_s.c_str())); pol_values[Anum_gs_auditing_policy_acc_policy_oid - 1] = ObjectIdGetDatum(policyOid); pol_values[Anum_gs_auditing_policy_acc_modify_date - 1] = DirectFunctionCall1(timestamptz_timestamp, GetCurrentTimestamp()); + + // 创建策略元组 policy_htup = heap_form_tuple(relation->rd_att, pol_values, pol_nulls); } else { - bool pol_nulls[Natts_gs_auditing_policy_priv] = {false}; - Datum pol_values[Natts_gs_auditing_policy_priv] = {0}; - pol_values[Anum_gs_auditing_policy_priv_type- 1] = DirectFunctionCall1(namein, CStringGetDatum(action_type)); + // 如果是特权操作 + bool pol_nulls[Natts_gs_auditing_policy_priv] = {false}; // 策略表的特权操作字段是否为NULL + Datum pol_values[Natts_gs_auditing_policy_priv] = {0}; // 策略表的特权操作字段的值 + + // 设置策略表的字段值 + pol_values[Anum_gs_auditing_policy_priv_type - 1] = DirectFunctionCall1(namein, CStringGetDatum(action_type)); pol_values[Anum_gs_auditing_policy_priv_label_name - 1] = DirectFunctionCall1(namein, CStringGetDatum(target_name_s.c_str())); pol_values[Anum_gs_auditing_policy_priv_policy_oid - 1] = ObjectIdGetDatum(policyOid); pol_values[Anum_gs_auditing_policy_priv_modify_date - 1] = DirectFunctionCall1(timestamptz_timestamp, GetCurrentTimestamp()); + + // 创建策略元组 policy_htup = heap_form_tuple(relation->rd_att, pol_values, pol_nulls); } - /* Do the insertion */ + + // 执行插入操作 (void)simple_heap_insert(relation, policy_htup); + + // 更新索引 CatalogUpdateIndexes(relation, policy_htup); + + // 释放策略元组内存 heap_freetuple(policy_htup); } /* * Handle 'all' syntax in auding access expression operations; */ +// 这个函数处理添加或移除指定对象的所有支持的访问或特权操作类型。 + static void handle_add_remove_all_types(int opt_type, privileges_access_set *add_actions, privileges_access_set *rem_actions, const privileges_access_set *exist_actions, bool is_add, long long polID, const char *object) { - /* add all supported type */ + // 创建一个包含策略操作详细信息的结构体 PgPolicyPrivilegesAccessStruct item; + + // 初始化结构体字段,设置通用的标签名和策略OID item.m_label_name = "all"; item.m_policy_oid = polID; + + // 如果指定的对象不是 "all",则处理单个操作类型 if (strcasecmp(object, "all")) { item.m_type = object; + if (is_add) { + // 如果是添加操作,检查操作是否已经存在,如果不存在则添加到添加操作集合中 if (exist_actions->find(item) == exist_actions->end()) { (void)add_actions->insert(item); } } else { + // 如果是移除操作,查找操作并将其添加到移除操作集合中 privileges_access_set::const_iterator it = exist_actions->find(item); if (it != exist_actions->end()) { (void)rem_actions->insert(*it); @@ -126,16 +157,20 @@ static void handle_add_remove_all_types(int opt_type, privileges_access_set *add return; } + // 如果指定的对象是 "all",则处理所有支持的操作类型 int array_size = (opt_type == POLICY_OPT_ACCESS) ? (sizeof(access_type) / sizeof(access_type[0])) : (sizeof(privileges_type) / sizeof(privileges_type[0])); for (int i = 0; i < array_size; ++i) { + // 根据操作类型设置详细信息,并根据操作是否存在执行添加或移除操作 item.m_type = (opt_type == POLICY_OPT_ACCESS) ? access_type[i] : privileges_type[i]; if (is_add) { + // 如果是添加操作,检查操作是否已经存在,如果不存在则添加到添加操作集合中 if (exist_actions->find(item) == exist_actions->end()) { (void)add_actions->insert(item); } } else { + // 如果是移除操作,直接将操作添加到移除操作集合中 (void)rem_actions->insert(item); } } @@ -148,18 +183,25 @@ static void handle_add_remove_all_types(int opt_type, privileges_access_set *add * @relation - relation to add configuration * @policyOid - policy id */ +// 这个内联函数用于将所有支持的访问或特权操作类型添加到策略表中。 + static inline void add_all_supported_types(bool is_access, const gs_stl::gs_string target_name_s, Relation relation, Oid policyOid) { - /* add all supported types */ + // 确定操作类型数组的大小,根据是否是访问操作或特权操作 int array_size = is_access ? (sizeof(access_type) / sizeof(access_type[0])) : (sizeof(privileges_type) / sizeof(privileges_type[0])); + + // 遍历所有操作类型 for (int i = 0; i < array_size; ++i) { const char *action_type = is_access ? access_type[i] : privileges_type[i]; + + // 调用函数将操作类型添加到策略表中 add_action_type(is_access, action_type, target_name_s, relation, policyOid); } } + /** * Parse resource labels associated with auditing policy according to the request * @is_access - true means ACCESS; false means PRIVILEGES @@ -168,13 +210,15 @@ static inline void add_all_supported_types(bool is_access, const gs_stl::gs_stri * @relation - relation to add configuration * @policyOid - policy id */ -static void add_labels_to_policy(bool is_access, const char *action_type, DefElem *policy_item, Relation relation, +// 这个函数用于向策略表中添加标签(label)或资源对象,指定操作类型的权限。 + +static void add_labels_to_policy(bool is_access, const char *action_type, DefElem *policy_item, Relation relation,//函数用于向策略表中添加标签(label)或资源对象,指定操作类型的权限 Oid policyOid) { - List *targets = policy_item ? (List *) policy_item->arg : NULL; /* labels list */ + List *targets = policy_item ? (List *) policy_item->arg : NULL; /* 标签列表 */ ListCell *target_name = NULL; - /* no targets - means for all db objects */ + /* 如果没有指定标签,表示适用于所有数据库对象 */ if (targets == NULL) { if (strcasecmp(action_type, "all") == 0) { add_all_supported_types(is_access, "all", relation, policyOid); @@ -184,25 +228,30 @@ static void add_labels_to_policy(bool is_access, const char *action_type, DefEle return; } - /* one or more targets */ + /* 处理一个或多个标签 */ foreach (target_name, targets) { RangeVar *rel = (RangeVar*)lfirst(target_name); gs_stl::gs_string target_name_s; construct_resource_name(rel, &target_name_s); - if (target_name_s == "all") { /* no validation whether "ALL" label exists */ + + // 如果标签是 "all",则适用于所有对象,不验证是否存在 + if (target_name_s == "all") { if (strcasecmp(action_type, "all") == 0) { add_all_supported_types(is_access, "all", relation, policyOid); } else { add_action_type(is_access, action_type, "all", relation, policyOid); } } else { - if (verify_label_hook) { /* validate whether this label exists */ + // 如果存在验证标签钩子函数,并且标签不存在,报错 + if (verify_label_hook) { if (!verify_label_hook(target_name_s.c_str())) { heap_close(relation, RowExclusiveLock); ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("[%s] no such label found", target_name_s.c_str()))); } } + + // 添加指定标签的权限 if (strcasecmp(action_type, "all") == 0) { add_all_supported_types(is_access, target_name_s, relation, policyOid); } else { @@ -212,6 +261,7 @@ static void add_labels_to_policy(bool is_access, const char *action_type, DefEle } } + /** * Update labels for access or privileges infomation and insert into catalog, since each policy * is only for access or privileges, flag is needed to distinct. @@ -220,137 +270,184 @@ static void add_labels_to_policy(bool is_access, const char *action_type, DefEle * @policy : auditing oplicy information associated with the gs_auditing_policy catalog * @relation : access catalog or privilege catalog. */ +// 这个函数用于向安全策略表中添加标签或资源对象,并指定操作类型的权限。 static void add_labels_to_privileges_access(bool is_access, const privileges_access_set *actions, const GsPolicyStruct *policy, Relation relation) { + // 遍历权限访问操作集合中的每个操作 for (privileges_access_set::const_iterator it = actions->begin(); it != actions->end(); ++it) { HeapTuple policy_htup = NULL; const char *action_type = it->m_type.c_str(); gs_stl::gs_string target_name_s = it->m_label_name; - Oid policyOid = policy->m_id; + Oid policyOid = policy->m_id; + + // 如果存在验证标签的钩子函数并且标签不存在,报错 if (verify_label_hook) { if (!verify_label_hook(target_name_s.c_str())) { heap_close(relation, RowExclusiveLock); - ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), + ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("[%s] no such label found", target_name_s.c_str()))); } } + // 根据是否是访问操作选择合适的字段和值 if (is_access) { + // 访问权限表的字段和值 bool policy_nulls[Natts_gs_auditing_policy_acc] = {false}; Datum policy_values[Natts_gs_auditing_policy_acc] = {0}; + policy_values[Anum_gs_auditing_policy_acc_type - 1] = DirectFunctionCall1(namein, CStringGetDatum(action_type)); policy_values[Anum_gs_auditing_policy_acc_label_name - 1] = DirectFunctionCall1(namein, CStringGetDatum(target_name_s.c_str())); policy_values[Anum_gs_auditing_policy_acc_policy_oid - 1] = ObjectIdGetDatum(policyOid); policy_values[Anum_gs_auditing_policy_acc_modify_date - 1] = DirectFunctionCall1(timestamptz_timestamp, GetCurrentTimestamp()); + + // 创建权限访问策略元组 policy_htup = heap_form_tuple(relation->rd_att, policy_values, policy_nulls); } else { + // 特权权限表的字段和值 bool policy_nulls[Natts_gs_auditing_policy_priv] = {false}; Datum policy_values[Natts_gs_auditing_policy_priv] = {0}; - policy_values[Anum_gs_auditing_policy_priv_type- 1] = DirectFunctionCall1(namein, CStringGetDatum(action_type)); + + policy_values[Anum_gs_auditing_policy_priv_type - 1] = DirectFunctionCall1(namein, CStringGetDatum(action_type)); policy_values[Anum_gs_auditing_policy_priv_label_name - 1] = DirectFunctionCall1(namein, CStringGetDatum(target_name_s.c_str())); policy_values[Anum_gs_auditing_policy_priv_policy_oid - 1] = ObjectIdGetDatum(policyOid); policy_values[Anum_gs_auditing_policy_priv_modify_date - 1] = DirectFunctionCall1(timestamptz_timestamp, GetCurrentTimestamp()); + + // 创建特权访问策略元组 policy_htup = heap_form_tuple(relation->rd_att, policy_values, policy_nulls); } - /* Do the insertion */ + + // 执行插入操作 (void)simple_heap_insert(relation, policy_htup); + // 更新索引 CatalogUpdateIndexes(relation, policy_htup); + + // 释放策略元组内存 heap_freetuple(policy_htup); } } + +// 这个函数用于从权限访问策略表中移除指定的标签(label)或资源对象,并指定操作类型的权限。 + static bool remove_labels_from_privileges_access(bool is_access, const privileges_access_set *actions, privileges_access_set *existing_actions, Relation relation, gs_stl::gs_string *err_msg) { - bool is_deleted = false; + bool is_deleted = false; // 用于跟踪是否已删除权限 for (privileges_access_set::const_iterator it = actions->begin(); it != actions->end(); ++it) { - /* Removing access or privilege from policy having only one item is not allowed */ + + /* 如果权限访问策略表中仅有一个项目,不允许删除 */ if (existing_actions->size() == 1) { *err_msg = (is_access) ? "Removing auditing access from policy with a single item not allowed" : "Removing auditing privilege from policy with a single item not allowed"; break; } + + // 在现有权限访问操作集合中查找指定的操作 privileges_access_set::iterator i_it = existing_actions->find(*it); if (i_it != existing_actions->end()) { + // 从关系中删除匹配的权限策略 if (!scan_to_delete_from_relation(i_it->m_id, relation, is_access ? GsAuditingPolicyAccessOidIndexId : GsAuditingPolicyPrivilegesOidIndexId)) break; + + // 从现有权限访问操作集合中移除匹配的权限 (void)existing_actions->erase(i_it); is_deleted = true; } } - return is_deleted; + return is_deleted; // 返回是否已删除权限的标志 } + /** * Add filter expr information into auditing policy. */ +// 这个函数用于向策略过滤器表中添加策略过滤器。 +//函数遍历要添加的策略过滤器集合,为每个过滤器创建策略过滤器元组, +//设置字段的值,并将元组插入到指定的关系(表)中。函数还更新了相关的索引,并释放了策略过滤器元组的内存。 static void add_filters(const filters_set *filters_to_add, Relation relation) { - Datum curtime; - HeapTuple policy_filters_htup; - bool policy_filters_nulls[Natts_gs_auditing_policy_filters]; - Datum policy_filters_values[Natts_gs_auditing_policy_filters]; - errno_t rc; - /* Get current timestamp */ + Datum curtime; // 用于存储当前时间戳 + HeapTuple policy_filters_htup; // 策略过滤器元组 + bool policy_filters_nulls[Natts_gs_auditing_policy_filters]; // 策略过滤器字段的NULL标志 + Datum policy_filters_values[Natts_gs_auditing_policy_filters]; // 策略过滤器字段的值 + errno_t rc; // 用于存储安全检查结果 + + /* 获取当前时间戳 */ curtime = DirectFunctionCall1(timestamptz_timestamp, GetCurrentTimestamp()); + + // 遍历要添加的策略过滤器集合 for (filters_set::const_iterator it = filters_to_add->begin(); it != filters_to_add->end(); ++it) { - /* restore values and nulls for insert new node group record */ + /* 恢复插入新节点组记录的值和NULL标志 */ rc = memset_s(policy_filters_values, sizeof(policy_filters_values), 0, sizeof(policy_filters_values)); securec_check(rc, "\0", "\0"); - rc = memset_s(policy_filters_nulls, sizeof(policy_filters_nulls), false, sizeof(policy_filters_nulls)); + rc = memset_s(policy_filters_nulls, sizeof(policy_filters_nulls), false, sizeof(policy_filters_nulls)); securec_check(rc, "\0", "\0"); + // 设置策略过滤器表字段的值 policy_filters_values[Anum_gs_auditing_policy_fltr_filter_type - 1] = DirectFunctionCall1(namein, CStringGetDatum(it->m_type.c_str())); policy_filters_values[Anum_gs_auditing_policy_fltr_label_name - 1] = DirectFunctionCall1(namein, CStringGetDatum(it->m_label_name.c_str())); policy_filters_values[Anum_gs_auditing_policy_fltr_logical_operator - 1] = CStringGetTextDatum(it->m_tree_string.c_str()); policy_filters_values[Anum_gs_auditing_policy_fltr_policy_oid - 1] = ObjectIdGetDatum(it->m_policy_oid); policy_filters_values[Anum_gs_auditing_policy_fltr_modify_date - 1] = curtime; + + // 创建策略过滤器元组 policy_filters_htup = heap_form_tuple(relation->rd_att, policy_filters_values, policy_filters_nulls); - /* Do the insertion */ + + /* 执行插入操作 */ (void)simple_heap_insert(relation, policy_filters_htup); + // 更新索引 CatalogUpdateIndexes(relation, policy_filters_htup); + + // 释放策略过滤器元组内存 heap_freetuple(policy_filters_htup); } } + /** * Update filter expr information into auditing policy. */ +// 这个函数用于更新策略过滤器表中的策略过滤器。如果策略过滤器不存在,则将其添加。 + static void update_filters(const filters_set *filters_to_update, Relation policy_filters_relation) { + // 遍历要更新的策略过滤器集合 for (filters_set::const_iterator it = filters_to_update->begin(); it != filters_to_update->end(); ++it) { ScanKeyData scanKey[1]; ScanKeyInit(&scanKey[0], Anum_gs_auditing_policy_fltr_policy_oid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(it->m_policy_oid)); - /* Search tuple by index */ + /* 通过索引搜索元组 */ SysScanDesc scanDesc = systable_beginscan(policy_filters_relation, GsAuditingPolicyFiltersPolicyOidIndexId, true, NULL, 1, scanKey); HeapTuple auditingPolicyTuple = systable_getnext(scanDesc); if (!HeapTupleIsValid(auditingPolicyTuple)) { - add_filters(filters_to_update, policy_filters_relation); /* curtime */ + add_filters(filters_to_update, policy_filters_relation); /* 如果不存在,则添加策略过滤器 */ } else { Datum values[Natts_gs_auditing_policy_filters] = { 0 }; bool nulls[Natts_gs_auditing_policy_filters] = { false }; bool replaces[Natts_gs_auditing_policy_filters] = { false }; errno_t rc; + rc = memset_s(values, sizeof(values), 0, sizeof(values)); securec_check(rc, "", ""); - rc = memset_s(nulls, sizeof(nulls), false, sizeof(nulls)); + rc = memset_s(nulls, sizeof(nulls), false, sizeof(nulls)); securec_check(rc, "", ""); - rc = memset_s(replaces, sizeof(replaces), false, sizeof(replaces)); + rc = memset_s(replaces, sizeof(replaces), false, sizeof(replaces)); securec_check(rc, "", ""); + // 更新逻辑操作符字段的值 values[Anum_gs_auditing_policy_fltr_logical_operator - 1] = CStringGetTextDatum(it->m_tree_string.c_str()); nulls[Anum_gs_auditing_policy_fltr_logical_operator - 1] = false; replaces[Anum_gs_auditing_policy_fltr_logical_operator - 1] = true; + // 修改策略过滤器元组并更新 HeapTuple newtuple = heap_modify_tuple(auditingPolicyTuple, RelationGetDescr(policy_filters_relation), values, nulls, replaces); simple_heap_update(policy_filters_relation, &newtuple->t_self, newtuple); @@ -360,170 +457,210 @@ static void update_filters(const filters_set *filters_to_update, Relation policy } } + +// 这个函数用于处理修改策略的过滤器操作,可以添加或更新过滤器。 + static void handle_alter_add_update_filter(List *filter, Oid policyOid, bool to_add) { if (filter == NULL) { - return; + return; // 如果过滤器为空,直接返回,无需执行任何操作 } gs_stl::gs_string flat_tree; + // 处理传入的策略过滤器,并将其转化为平面字符串形式 if (!process_new_filters(filter, &flat_tree)) { ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("Unsupported policy filter values"))); } filters_set filters_to_alter; if (flat_tree.size() > 0) { + // 创建策略过滤器结构体,并设置相关字段值 PgPolicyFiltersStruct item; item.m_type = "logical_expr"; item.m_label_name = "logical_expr"; item.m_tree_string = flat_tree; item.m_policy_oid = policyOid; + // 将策略过滤器添加到要处理的策略过滤器集合中 (void)filters_to_alter.insert(item); } if (filters_to_alter.size() > 0) { + // 打开策略过滤器表关系 Relation policy_filters_relation = heap_open(GsAuditingPolicyFiltersRelationId, RowExclusiveLock); if (policy_filters_relation) { if (to_add) { + // 如果标志 to_add 为 true,则执行添加策略过滤器操作 add_filters(&filters_to_alter, policy_filters_relation); } else { + // 否则执行更新策略过滤器操作 update_filters(&filters_to_alter, policy_filters_relation); } + // 关闭策略过滤器表关系 heap_close(policy_filters_relation, RowExclusiveLock); } } } + /* * Load existing auditing policy about DML synatax. */ +// 这个函数用于加载现有的权限(privileges)并将它们添加到指定的权限访问操作集合中。 + static void load_existing_privileges(privileges_access_set *privs, long long policy_oid) { + // 打开权限访问策略表关系 Relation relation = heap_open(GsAuditingPolicyPrivilegesRelationId, RowExclusiveLock); if (relation == NULL) { - return; + return; // 如果无法打开关系,则直接返回 } HeapTuple rtup; Form_gs_auditing_policy_privileges rel_data; PgPolicyPrivilegesAccessStruct item; + // 开始对权限访问策略表进行扫描 TableScanDesc scan = tableam_scan_begin(relation, SnapshotNow, 0, NULL); while (scan && (rtup = (HeapTuple) tableam_scan_getnexttuple(scan, ForwardScanDirection))) { rel_data = (Form_gs_auditing_policy_privileges)GETSTRUCT(rtup); if (rel_data == NULL) { - continue; + continue; // 跳过无效的元组 } - item.m_id = HeapTupleGetOid(rtup); - item.m_type = rel_data->privilegetype.data; - item.m_label_name = rel_data->labelname.data; - item.m_policy_oid = (long long)(rel_data->policyoid); - /* load only matching privileges to policy id */ + item.m_id = HeapTupleGetOid(rtup); // 获取权限访问策略元组的OID + item.m_type = rel_data->privilegetype.data; // 获取权限类型 + item.m_label_name = rel_data->labelname.data; // 获取标签名称 + item.m_policy_oid = (long long)(rel_data->policyoid); // 获取策略OID + + /* 仅加载与指定策略ID匹配的权限 */ if (item.m_policy_oid == policy_oid) { - (void)privs->insert(item); + (void)privs->insert(item); // 将匹配的权限添加到权限访问操作集合中 } } + // 结束扫描 tableam_scan_end(scan); + // 关闭权限访问策略表关系 heap_close(relation, RowExclusiveLock); } + /* * Load existing auditing policy about DDL synatax. */ +// 这个函数用于加载现有的访问权限(access)并将它们添加到指定的权限访问操作集合中。 + static void load_existing_access(privileges_access_set *acc, long long policy_oid) { + // 打开权限访问策略表关系 Relation relation = heap_open(GsAuditingPolicyAccessRelationId, RowExclusiveLock); if (!relation) { - return; + return; // 如果无法打开关系,则直接返回 } HeapTuple rtup; Form_gs_auditing_policy_access rel_data; PgPolicyPrivilegesAccessStruct item; + // 开始对权限访问策略表进行扫描 TableScanDesc scan = tableam_scan_begin(relation, SnapshotNow, 0, NULL); while (scan && (rtup = (HeapTuple) tableam_scan_getnexttuple(scan, ForwardScanDirection))) { rel_data = (Form_gs_auditing_policy_access)GETSTRUCT(rtup); if (rel_data == NULL) { - continue; + continue; // 跳过无效的元组 } - item.m_id = HeapTupleGetOid(rtup); - item.m_type = rel_data->accesstype.data; - item.m_label_name = rel_data->labelname.data; - item.m_policy_oid = (long long)(rel_data->policyoid); - /* load only matching access to policy id */ + item.m_id = HeapTupleGetOid(rtup); // 获取权限访问策略元组的OID + item.m_type = rel_data->accesstype.data; // 获取访问权限类型 + item.m_label_name = rel_data->labelname.data; // 获取标签名称 + item.m_policy_oid = (long long)(rel_data->policyoid); // 获取策略OID + + /* 仅加载与指定策略ID匹配的访问权限 */ if (item.m_policy_oid == policy_oid) { - (void)acc->insert(item); + (void)acc->insert(item); // 将匹配的访问权限添加到权限访问操作集合中 } } + // 结束扫描 tableam_scan_end(scan); + // 关闭权限访问策略表关系 heap_close(relation, RowExclusiveLock); } + static bool update_policy(const GsPolicyStruct *policy, Relation relation, bool policy_status_changed, gs_stl::gs_string *err_msg) { - bool policy_nulls[Natts_gs_auditing_policy]; - bool policy_replaces[Natts_gs_auditing_policy]; - Datum policy_values[Natts_gs_auditing_policy]; - Datum curtime; - errno_t rc; + // 声明一些辅助变量 + bool policy_nulls[Natts_gs_auditing_policy]; // 用于表示每个字段是否为空 + bool policy_replaces[Natts_gs_auditing_policy]; // 用于表示哪些字段需要替换 + Datum policy_values[Natts_gs_auditing_policy]; // 存储新值的数组 + Datum curtime; // 用于存储当前时间戳 + errno_t rc; // 用于存储错误码 - /* restore values and nulls for insert new node group record */ - rc = memset_s(policy_values, sizeof(policy_values), 0, sizeof(policy_values)); - securec_check(rc, "", ""); - rc = memset_s(policy_nulls, sizeof(policy_nulls), false, sizeof(policy_nulls)); - securec_check(rc, "", ""); - rc = memset_s(policy_replaces, sizeof(policy_replaces), false, sizeof(policy_replaces)); - securec_check(rc, "", ""); + /* 为插入新的节点组记录恢复值和空值 */ + rc = memset_s(policy_values, sizeof(policy_values), 0, sizeof(policy_values)); // 初始化policy_values为0 + securec_check(rc, "", ""); // 检查memset_s的返回值 + rc = memset_s(policy_nulls, sizeof(policy_nulls), false, sizeof(policy_nulls)); // 初始化policy_nulls为false + securec_check(rc, "", ""); // 检查memset_s的返回值 + rc = memset_s(policy_replaces, sizeof(policy_replaces), false, sizeof(policy_replaces)); // 初始化policy_replaces为false + securec_check(rc, "", ""); // 检查memset_s的返回值 - ScanKeyData skey; - /* Find the policy row to update */ + ScanKeyData skey; // 用于定义扫描条件的结构体 + /* 查找要更新的策略行 */ ScanKeyInit(&skey, ObjectIdAttributeNumber, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(policy->m_id)); /* - * set up for heap-or-index scan, not need to check tgscan as systable_getnext will deal with sysscan->irel = NULL + * 设置为堆或索引扫描,无需检查tgscan,因为systable_getnext将处理sysscan->irel = NULL */ - SysScanDesc tgscan = systable_beginscan(relation, GsAuditingPolicyOidIndexId, true, NULL, 1, &skey); - HeapTuple tup; - tup = systable_getnext(tgscan); - if (!tup || !HeapTupleIsValid(tup)) { - systable_endscan(tgscan); - (void)err_msg->append("could not find tuple for policy "); + SysScanDesc tgscan = systable_beginscan(relation, GsAuditingPolicyOidIndexId, true, NULL, 1, &skey); // 开始扫描 + HeapTuple tup; // 用于存储数据库中的元组(行) + tup = systable_getnext(tgscan); // 获取下一个符合条件的元组 + if (!tup || !HeapTupleIsValid(tup)) { // 如果找不到有效的元组 + systable_endscan(tgscan); // 结束扫描 + (void)err_msg->append("could not find tuple for policy "); // 记录错误消息 (void)err_msg->append(policy->m_name); - return false; + return false; // 返回false,表示更新失败 } - /* Get current timestamp */ + /* 获取当前时间戳 */ curtime = DirectFunctionCall1(timestamptz_timestamp, GetCurrentTimestamp()); if (policy_status_changed) { - policy_replaces[Anum_gs_auditing_policy_pol_enabled - 1] = true; - policy_values[Anum_gs_auditing_policy_pol_enabled - 1] = BoolGetDatum(policy->m_enabled); - } else { /* in case that comments changed */ - policy_replaces[Anum_gs_auditing_policy_pol_comments - 1] = true; - policy_values[Anum_gs_auditing_policy_pol_comments - 1] = - DirectFunctionCall1(namein, CStringGetDatum(policy->m_comments.c_str())); + // 如果策略的状态发生了变化 + policy_replaces[Anum_gs_auditing_policy_pol_enabled - 1] = true; // 标记需要替换启用状态字段 + policy_values[Anum_gs_auditing_policy_pol_enabled - 1] = BoolGetDatum(policy->m_enabled); // 设置新的启用状态值 + } else { /* 在注释更改的情况下 */ + // 如果策略的状态没有变化,但注释发生了变化 + policy_replaces[Anum_gs_auditing_policy_pol_comments - 1] = true; // 标记需要替换注释字段 + policy_values[Anum_gs_auditing_policy_pol_comments - 1] = + DirectFunctionCall1(namein, CStringGetDatum(policy->m_comments.c_str())); // 设置新的注释值 } - policy_replaces[Anum_gs_auditing_policy_pol_modify_date - 1] = true; - policy_values[Anum_gs_auditing_policy_pol_modify_date - 1] = curtime; + policy_replaces[Anum_gs_auditing_policy_pol_modify_date - 1] = true; // 标记需要替换修改日期字段 + policy_values[Anum_gs_auditing_policy_pol_modify_date - 1] = curtime; // 设置新的修改日期值 + + // 使用新值、空值标志和替换标志创建一个新的元组 HeapTuple newtuple = heap_modify_tuple(tup, RelationGetDescr(relation), policy_values, policy_nulls, policy_replaces); + + // 更新数据库中的元组 simple_heap_update(relation, &newtuple->t_self, newtuple); + + // 更新索引 CatalogUpdateIndexes(relation, newtuple); + + // 结束扫描 systable_endscan(tgscan); - return true; + + return true; // 返回true,表示更新成功 } + /* * function name: create_audit_policy * description : create auditing policy */ void create_audit_policy(CreateAuditPolicyStmt *stmt) { - /* check that if has access to config audit policy */ + /* 检查是否有权限配置审计策略 */ if (!is_policy_enabled()) { ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), @@ -537,6 +674,7 @@ void create_audit_policy(CreateAuditPolicyStmt *stmt) char session_ip[MAX_IP_LEN] = {0}; get_session_ip(session_ip, MAX_IP_LEN); + // 创建审计策略的描述信息 int ret = snprintf_s(buff, sizeof(buff), sizeof(buff) - 1, "user name: [%s], app_name: [%s], ip: [%s], CREATE AUDIT POLICY [%s], TYPE: [%s]", user_name, u_sess->attr.attr_common.application_name, session_ip, stmt->policy_name, stmt->policy_type); @@ -554,41 +692,41 @@ void create_audit_policy(CreateAuditPolicyStmt *stmt) bool policy_nulls[Natts_gs_auditing_policy] = {false}; Datum policy_values[Natts_gs_auditing_policy] = {0}; - /* restore values and nulls for insert new node group record */ + /* 恢复插入新策略记录的值和空值标志 */ rc = memset_s(policy_values, sizeof(policy_values), 0, sizeof(policy_values)); securec_check(rc, "", ""); rc = memset_s(policy_nulls, sizeof(policy_nulls), false, sizeof(policy_nulls)); securec_check(rc, "", ""); - /* start to process policy */ + /* 开始处理策略 */ Relation policy_relation = heap_open(GsAuditingPolicyRelationId, RowExclusiveLock); if (!policy_relation) { - /* generate an error */ + /* 生成错误消息 */ send_manage_message(AUDIT_FAILED); ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("%s", "failed to open policies relation"))); return; } - /* no more than MAX_POLICIES_NUM is allowed */ + /* 不允许超过最大策略数量 MAX_POLICIES_NUM */ if (get_num_of_existing_policies(policy_relation) >= MAX_POLICIES_NUM) { heap_close(policy_relation, RowExclusiveLock); - /* generate an error */ + /* 生成错误消息 */ send_manage_message(AUDIT_FAILED); ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("%s", "too many policies, adding new policiy is restricted"))); + (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("%s", "too many policies, adding new policy is restricted"))); return; } - /* check whether such policy exists */ + /* 检查是否已存在该策略 */ policies_set existing_policies; load_existing_policies(policy_relation, &existing_policies); GsPolicyStruct cur_policy; cur_policy.m_name = policy_name; policies_set::iterator it = existing_policies.find(cur_policy); - if (it != existing_policies.end()) { /* policy already exists */ + if (it != existing_policies.end()) { /* 策略已存在 */ heap_close(policy_relation, RowExclusiveLock); - /* while the 'if not exists' is specified generate a notice, else an error */ + /* 如果指定了 'if not exists' 则生成通知,否则生成错误 */ if (stmt->if_not_exists == true) { send_manage_message(AUDIT_OK); ereport(NOTICE, (errmsg("%s policy already exists, create skipping", policy_name))); @@ -601,7 +739,7 @@ void create_audit_policy(CreateAuditPolicyStmt *stmt) return; } - /* Get current timestamp */ + /* 获取当前时间戳 */ curtime = DirectFunctionCall1(timestamptz_timestamp, GetCurrentTimestamp()); policy_values[Anum_gs_auditing_policy_pol_name - 1] = DirectFunctionCall1(namein, CStringGetDatum(policy_name)); @@ -609,20 +747,20 @@ void create_audit_policy(CreateAuditPolicyStmt *stmt) policy_values[Anum_gs_auditing_policy_pol_modify_date - 1] = curtime; policy_values[Anum_gs_auditing_policy_pol_enabled - 1] = BoolGetDatum(policy_enabled); policy_htup = heap_form_tuple(policy_relation->rd_att, policy_values, policy_nulls); - /* Do the insertion */ + /* 执行插入操作 */ (void)simple_heap_insert(policy_relation, policy_htup); CatalogUpdateIndexes(policy_relation, policy_htup); Oid policyOid = HeapTupleGetOid(policy_htup); heap_freetuple(policy_htup); heap_close(policy_relation, RowExclusiveLock); - /* Start to Process PRIVILEGES(DML) && ACCESS(DDL) exprs */ + /* 开始处理 PRIVILEGES(DML) && ACCESS(DDL) 表达式 */ int opt_type = get_option_type(policy_type); - /* Extract policy targets from the statement node tree */ + /* 从语句节点树中提取策略目标 */ foreach (policy_target_item, stmt->policy_targets) { DefElem *defel = (DefElem *) lfirst(policy_target_item); - const char *action_type = defel->defname; /* action: DELETE, INSERT, UPDATE, etc. */ + const char *action_type = defel->defname; /* 动作类型: DELETE, INSERT, UPDATE, 等 */ DefElem *policy_items = (DefElem *) defel->arg; switch (opt_type) { case POLICY_OPT_PRIVILEGES: { @@ -641,18 +779,18 @@ void create_audit_policy(CreateAuditPolicyStmt *stmt) } } break; - /* handled later */ + /* 后面处理 */ case POLICY_OPT_FILTER: break; default: { - /* report about unknown policy type */ + /* 报告未知的策略类型 */ ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("Unsupported policy type"))); } break; } } - handle_alter_add_update_filter(stmt->policy_filters, policyOid, true /* add new filter row */); + handle_alter_add_update_filter(stmt->policy_filters, policyOid, true /* 添加新的过滤器行 */); CommandCounterIncrement(); send_manage_message(AUDIT_OK); @@ -665,47 +803,61 @@ void create_audit_policy(CreateAuditPolicyStmt *stmt) if (load_audit_policies_hook) { load_audit_policies_hook(false); } - /* load filters must be last */ + /* 加载策略必须放在最后 */ if (load_policy_filter_hook) { load_policy_filter_hook(false); } } + static void handle_alter_audit_node(AlterAuditPolicyStmt *stmt, gs_stl::gs_string& err_msg, GsPolicyStruct& cur_policy, privileges_access_set& access_to_add, privileges_access_set& access_to_remove, privileges_access_set& privs_to_add, privileges_access_set& privs_to_remove, policy_labels_map& existing_labels, privileges_access_set& existing_privileges, privileges_access_set& existing_access) { - /* Extract policy items from the statement node tree */ + /* 从语句节点树中提取策略项 */ if (stmt->policy_type != NULL) { ListCell *policy_item = NULL; - bool is_add = strcasecmp(stmt->policy_action, "add") == 0; /* action: add or remove */ + + // 判断操作类型是"add"还是"remove" + bool is_add = strcasecmp(stmt->policy_action, "add") == 0; /* 动作: add 或 remove */ + + // 获取策略类型的选项值 int opt_type = get_option_type(stmt->policy_type); + foreach (policy_item, stmt->policy_items) { - DefElem *pol_option_item = (DefElem *) lfirst(policy_item); /* policy option & list of targets */ - if (pol_option_item == NULL) { /* for optional parts of statement */ + DefElem *pol_option_item = (DefElem *) lfirst(policy_item); /* 策略选项 & 目标列表 */ + + // 如果策略选项为空(用于可选的语句部分),则跳过 + if (pol_option_item == NULL) { continue; } + /* pol_option_item->defname; copy, ... */ bool ret = true; DefElem* arguments = (DefElem*)pol_option_item->arg; - List *targets = arguments ? (List *) arguments->arg : nullptr; /* policy targets */ + List *targets = arguments ? (List *) arguments->arg : nullptr; /* 策略目标 */ ListCell *target = NULL; + if (targets && list_length(targets) > 0) { /* arguments->defname; LABEL */ + + // 遍历处理策略目标 foreach (target, targets) { if (!(ret = handle_target(target, opt_type, is_add, &err_msg, &access_to_add, &access_to_remove, &privs_to_add, &privs_to_remove, &existing_labels, &cur_policy, pol_option_item->defname))) { break; } } - } else { /* all objects */ + } else { /* 适用于所有对象 */ switch (opt_type) { case POLICY_OPT_ACCESS: + // 处理访问控制的全部对象 handle_add_remove_all_types(opt_type, &access_to_add, &access_to_remove, &existing_access, is_add, cur_policy.m_id, pol_option_item->defname); break; case POLICY_OPT_PRIVILEGES: + // 处理权限的全部对象 handle_add_remove_all_types(opt_type, &privs_to_add, &privs_to_remove, &existing_privileges, is_add, cur_policy.m_id, pol_option_item->defname); break; @@ -714,9 +866,9 @@ static void handle_alter_audit_node(AlterAuditPolicyStmt *stmt, gs_stl::gs_strin } } - /* validations, If anything is added/removed to label, label must exist unless it's for ALL */ + /* 验证,如果向标签添加/移除了任何内容,则标签必须存在,除非它是用于 ALL */ if (!ret) { - /* generate an error */ + /* 生成错误消息 */ ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("%s", err_msg.c_str()))); return; } @@ -724,22 +876,25 @@ static void handle_alter_audit_node(AlterAuditPolicyStmt *stmt, gs_stl::gs_strin } } -static void update_audit_policy_actions(privileges_access_set& privs_to_add, privileges_access_set& privs_to_remove, + +static void update_audit_policy_actions(privileges_access_set& privs_to_add, privileges_access_set& privs_to_remove,//用于更新审计策略操作权限的函数 privileges_access_set& access_to_add, privileges_access_set& access_to_remove, GsPolicyStruct& cur_policy, privileges_access_set& existing_privileges, privileges_access_set& existing_access, gs_stl::gs_string& err_msg) { + // 检查是否有要添加或移除的权限 if ((privs_to_add.size() > 0) || (privs_to_remove.size() > 0)) { + // 打开审计策略权限表 Relation priv_relation = heap_open(GsAuditingPolicyPrivilegesRelationId, RowExclusiveLock); if (priv_relation != NULL) { - /* add privileges */ + /* 添加权限 */ if (privs_to_add.size() > 0) { add_labels_to_privileges_access(false, &privs_to_add, &cur_policy, priv_relation); } else if (!remove_labels_from_privileges_access(false, &privs_to_remove, &existing_privileges, priv_relation, - &err_msg)) { /* remove privileges */ + &err_msg)) { /* 移除权限 */ heap_close(priv_relation, RowExclusiveLock); - /* generate an error */ + /* 生成错误消息 */ ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("%s", (err_msg.size() > 0) ? err_msg.c_str() : "No matching privilege to delete found"))); return; @@ -747,16 +902,19 @@ static void update_audit_policy_actions(privileges_access_set& privs_to_add, pri heap_close(priv_relation, RowExclusiveLock); } } + + // 检查是否有要添加或移除的访问权限 if ((access_to_add.size() > 0) || (access_to_remove.size() > 0)) { + // 打开审计策略访问权限表 Relation acc_relation = heap_open(GsAuditingPolicyAccessRelationId, RowExclusiveLock); if (acc_relation != NULL) { - /* add access */ + /* 添加访问权限 */ if (access_to_add.size() > 0) { add_labels_to_privileges_access(true, &access_to_add, &cur_policy, acc_relation); } else if (!remove_labels_from_privileges_access(true, &access_to_remove, &existing_access, acc_relation, - &err_msg)) { /* remove access */ + &err_msg)) { /* 移除访问权限 */ heap_close(acc_relation, RowExclusiveLock); - /* generate an error */ + /* 生成错误消息 */ ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("%s", (err_msg.size() > 0) ? err_msg.c_str() : "No matching access to delete found"))); return; @@ -766,13 +924,14 @@ static void update_audit_policy_actions(privileges_access_set& privs_to_add, pri } } + /* * function name: alter_audit_policy * description : alter auditing policy */ void alter_audit_policy(AlterAuditPolicyStmt *stmt) { - /* check that if has access to config audit policy */ + // 检查是否具有配置审计策略的权限 if (!is_policy_enabled()) { ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("Permission denied."))); return; @@ -782,15 +941,19 @@ void alter_audit_policy(AlterAuditPolicyStmt *stmt) char user_name[USERNAME_LEN] = {0}; char session_ip[MAX_IP_LEN] = {0}; + // 获取当前用户的用户名和会话IP地址 get_session_ip(session_ip, MAX_IP_LEN); (void)GetRoleName(GetCurrentUserId(), user_name, sizeof(user_name)); + // 构建管理消息 int rc = snprintf_s(buff, sizeof(buff), sizeof(buff) - 1, "user name: [%s], app_name: [%s], ip: [%s], ALTER AUDIT POLICY [%s] FOR %s", user_name, u_sess->attr.attr_common.application_name, session_ip, stmt->policy_name, stmt->policy_action); securec_check_ss(rc, "", ""); save_manage_message(buff); + const char *policy_name = stmt->policy_name; + policies_set existing_policies; policy_labels_map existing_labels; @@ -802,24 +965,24 @@ void alter_audit_policy(AlterAuditPolicyStmt *stmt) privileges_access_set access_to_remove; privileges_access_set access_to_add; - Relation policy_relation = NULL; - Relation labels_relation = NULL; + Relation policy_relation = NULL; + Relation labels_relation = NULL; - /* Open the relation for read and insertion */ + // 打开审计策略关系以进行读取和插入 policy_relation = heap_open(GsAuditingPolicyRelationId, RowExclusiveLock); load_existing_policies(policy_relation, &existing_policies); - /* first check whether such policy exists */ + // 首先检查是否存在此策略 GsPolicyStruct cur_policy; cur_policy.m_name = policy_name; policies_set::iterator it = existing_policies.find(cur_policy); if (it == existing_policies.end()) { heap_close(policy_relation, RowExclusiveLock); - if (stmt->missing_ok) { /* IF EXISTS is specified, generate a notice */ + if (stmt->missing_ok) { // 如果指定了 IF EXISTS,则生成通知 send_manage_message(AUDIT_OK); ereport(NOTICE, (errmsg("%s policy not found, alter skipping", policy_name))); } else { - /* generate an error */ + // 生成错误消息 send_manage_message(AUDIT_FAILED); ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), @@ -830,7 +993,7 @@ void alter_audit_policy(AlterAuditPolicyStmt *stmt) cur_policy.m_id = it->m_id; cur_policy.m_enabled = it->m_enabled; - /* Update policy if needed */ + // 如果需要,更新策略 bool policy_status_changed = false; if (stmt->policy_enabled != NULL) { DefElem *defel = (DefElem *) stmt->policy_enabled; @@ -847,7 +1010,7 @@ void alter_audit_policy(AlterAuditPolicyStmt *stmt) gs_stl::gs_string err_msg; if (!update_policy(&cur_policy, policy_relation, policy_status_changed, &err_msg)) { heap_close(policy_relation, RowExclusiveLock); - /* generate an error */ + // 生成错误消息 send_manage_message(AUDIT_FAILED); ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), @@ -857,24 +1020,25 @@ void alter_audit_policy(AlterAuditPolicyStmt *stmt) } heap_close(policy_relation, RowExclusiveLock); + // 打开策略标签关系以进行读取 labels_relation = heap_open(GsPolicyLabelRelationId, RowExclusiveLock); load_existing_labels(labels_relation, &existing_labels); heap_close(labels_relation, RowExclusiveLock); - /* Get current timestamp */ + // 获取当前时间戳 (void)DirectFunctionCall1(timestamptz_timestamp, GetCurrentTimestamp()); load_existing_privileges(&existing_privileges, cur_policy.m_id); load_existing_access(&existing_access, cur_policy.m_id); - /* Extract policy items from the statement node tree */ + // 提取语句节点树中的策略项 gs_stl::gs_string err_msg; - handle_alter_audit_node(stmt, err_msg, cur_policy, access_to_add, access_to_remove, privs_to_add, privs_to_remove, existing_labels, existing_privileges, existing_access); update_audit_policy_actions(privs_to_add, privs_to_remove, access_to_add, access_to_remove, cur_policy, existing_privileges, existing_access, err_msg); handle_alter_add_update_filter(stmt->policy_filters, cur_policy.m_id, false /* update filter */); + // 如果策略操作为 "drop_filter",则删除策略引用 if (stmt->policy_action && !strcmp(stmt->policy_action, "drop_filter")) { drop_policy_reference(GsAuditingPolicyFiltersRelationId, cur_policy.m_id); } @@ -891,19 +1055,20 @@ void alter_audit_policy(AlterAuditPolicyStmt *stmt) if (load_audit_policies_hook) { load_audit_policies_hook(false); } - /* load filters must be last */ + // 加载策略过滤器必须放在最后 if (load_policy_filter_hook) { load_policy_filter_hook(false); } } + /** * Main enterance for droping audit policy, which will drop all the catalog information associated with this Policy. * @stmt : Data structure for Drop Policy syntax */ void drop_audit_policy(DropAuditPolicyStmt *stmt) { - /* check that if has access to config audit policy */ + // 检查是否具有配置审计策略的权限 if (!is_policy_enabled()) { ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), @@ -911,7 +1076,7 @@ void drop_audit_policy(DropAuditPolicyStmt *stmt) return; } - /* save Mng logs */ + // 保存管理日志 ListCell* policy_obj = NULL; foreach (policy_obj, stmt->policy_names) { const char* polname = (const char *)(((Value*)lfirst(policy_obj))->val.str); @@ -919,9 +1084,11 @@ void drop_audit_policy(DropAuditPolicyStmt *stmt) char user_name[USERNAME_LEN] = {0}; char session_ip[MAX_IP_LEN] = {0}; + // 获取当前会话的IP地址和用户名 get_session_ip(session_ip, MAX_IP_LEN); (void)GetRoleName(GetCurrentUserId(), user_name, sizeof(user_name)); + // 构建管理消息 int rc = snprintf_s(buff, sizeof(buff), sizeof(buff) - 1, "user name: [%s], app_name: [%s], ip: [%s], DROP AUDIT POLICY [%s]", user_name, u_sess->attr.attr_common.application_name, session_ip, polname); @@ -935,9 +1102,11 @@ void drop_audit_policy(DropAuditPolicyStmt *stmt) drop_policy_by_name(GsAuditingPolicyRelationId, polname, &ids); if (ids.empty()) { if (stmt->missing_ok) { + // 如果指定了 `IF EXISTS` 选项,则生成通知 ereport(NOTICE, (errmsg("%s policy does not exist, drop skipping", polname))); continue; } else { + // 否则,生成错误消息 send_manage_message(AUDIT_FAILED); ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), @@ -946,11 +1115,11 @@ void drop_audit_policy(DropAuditPolicyStmt *stmt) } } for (long long _id : ids) { - /* drop gs_auditing_policy_access catalog information */ + // 删除 gs_auditing_policy_access 目录信息 drop_policy_reference(GsAuditingPolicyAccessRelationId, _id); - /* drop gs_auditing_policy_privilege catalog information */ + // 删除 gs_auditing_policy_privilege 目录信息 drop_policy_reference(GsAuditingPolicyPrivilegesRelationId, _id); - /* drop gs_auditing_policy_filter catalog information */ + // 删除 gs_auditing_policy_filter 目录信息 drop_policy_reference(GsAuditingPolicyFiltersRelationId, _id); } } @@ -967,9 +1136,18 @@ void drop_audit_policy(DropAuditPolicyStmt *stmt) if (load_audit_policies_hook) { load_audit_policies_hook(false); } - /* load filters must be last */ + // 加载策略过滤器必须放在最后 if (load_policy_filter_hook) { load_policy_filter_hook(false); } } +/* +检查是否具有配置审计策略的权限。 +保存管理日志,记录用户操作。 +对每个要删除的策略执行以下操作: +检查策略是否存在,如果不存在且未指定 IF EXISTS 选项,则生成错误消息,否则生成通知。 +删除与策略相关的目录信息,包括权限、访问权限和策略过滤器。 +增加命令计数器以反映操作已成功完成。 +调用相应的挂钩函数以重新加载策略和策略过滤器。策略过滤器必须最后加载。 +*/ diff --git a/src/gausskernel/security/gs_policy/gs_policy_masking.cpp b/src/gausskernel/security/gs_policy/gs_policy_masking.cpp index 0af803079..a3554e0ac 100644 --- a/src/gausskernel/security/gs_policy/gs_policy_masking.cpp +++ b/src/gausskernel/security/gs_policy/gs_policy_masking.cpp @@ -48,13 +48,13 @@ #include "pgaudit.h" #include "utils/snapmgr.h" - +// 这些变量用于存储不同的钩子函数,用于加载和验证数据遮蔽策略。 LoadPoliciesPtr load_masking_policies_hook = NULL; LoadPolicyAccessPtr load_masking_policy_actions_hook = NULL; LoadPolicyFilterPtr load_masking_policy_filter_hook = NULL; ValidateBehaviourPtr validate_masking_behaviour_hook = NULL; VerifyLabelsByPolicy gs_verify_labels_by_policy_hook = NULL; - +// 这是一个字符串数组,包含了不同的数据遮蔽函数名称。 static const char* g_maskFunctions[] = { "creditcardmasking", "basicemailmasking", @@ -65,13 +65,14 @@ static const char* g_maskFunctions[] = { "regexpmasking", NULL }; - +// 这个函数用于重新加载数据遮蔽策略。 static void reload_masking_policies() { + // 如果当前数据库无效,直接返回。 if (!OidIsValid(u_sess->proc_cxt.MyDatabaseId)) { return; } - +// 如果有加载数据遮蔽策略的钩子函数,就调用它们。 if (load_masking_policies_hook != NULL) { load_masking_policies_hook(false); } @@ -79,11 +80,14 @@ static void reload_masking_policies() load_masking_policy_actions_hook(false); } /* load filters must be last */ + // 加载过滤器应该在最后加载。 if (load_masking_policy_filter_hook != NULL) { load_masking_policy_filter_hook(false); } } - + //功能:重新加载数据遮蔽策略。描述:该函数检查当前数据库是否有效,然后调用不同的钩子函数来加载数据遮蔽策略、策略动作以及策略过滤器。策略过滤器的加载应该在最后进行。 +// 下面是一系列关于数据遮蔽策略的结构体操作函数,用于比较和排序。 +// 这个函数用于比较两个 PgPolicyMaskingActionStruct 是否相等。 #define BUFFSIZE 512 bool PgPolicyMaskingActionStruct::operator == (const PgPolicyMaskingActionStruct &arg) const { @@ -95,7 +99,8 @@ bool PgPolicyMaskingActionStruct::operator == (const PgPolicyMaskingActionStruct return true; } } - +//功能:比较两个数据遮蔽策略动作是否相等。描述:该函数用于比较两个数据遮蔽策略动作对象是否相等。 +// 这个函数用于比较两个 PgPolicyMaskingActionStruct 的大小关系。 bool PgPolicyMaskingActionStruct::operator < (const PgPolicyMaskingActionStruct &arg) const { int res = strcasecmp(m_type.c_str(), arg.m_type.c_str()); @@ -107,6 +112,7 @@ bool PgPolicyMaskingActionStruct::operator < (const PgPolicyMaskingActionStruct return m_policy_oid < arg.m_policy_oid; } +// 这个函数用于比较两个 PgPolicyMaskingActionStruct 的大小关系,通常用于排序。 int PgPolicyMaskingActionStruct::operator - (const PgPolicyMaskingActionStruct &arg) const { if (*this < arg) { @@ -125,6 +131,7 @@ typedef gs_stl::gs_mapsecond)->begin(); @@ -285,6 +299,7 @@ static inline void add_labels_to_masking_action(const gs_stl::gs_string action_t for (; lit != eit; ++lit) { Oid fqdnOid = lit->m_data_value_fqdn.m_value_object; /* masking only allowed operator on column */ + // 检查标签的数据类型是否为"column" if (strcmp(lit->m_data_type.c_str(), "column") != 0) { ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), @@ -292,13 +307,16 @@ static inline void add_labels_to_masking_action(const gs_stl::gs_string action_t } /* masked column should belong to an ordinary table */ if (OidIsValid(fqdnOid)) { + // 获取关系对象的类型 char relkind = get_rel_relkind(fqdnOid); + // 检查关系对象是否是普通表 if (relkind != RELKIND_RELATION || get_rel_persistence(fqdnOid) != 'p') { ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("Masking policy can only operate on column of ordinary table."))); } const char *column_name = lit->m_data_value_fqdn.m_value_object_attrib.c_str(); + // 检查列是否允许进行数据遮蔽 if (!column_allow_to_masking(fqdnOid, column_name)) { ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), @@ -307,17 +325,19 @@ static inline void add_labels_to_masking_action(const gs_stl::gs_string action_t } } } - +// 将数据添加到policy_actions_values数组中 policy_actions_values[Anum_gs_masking_policy_act_action_type - 1] = DirectFunctionCall1(namein, CStringGetDatum(action_type.c_str())); policy_actions_values[Anum_gs_masking_policy_act_action_params - 1] = DirectFunctionCall1(namein, CStringGetDatum(action_params.c_str())); policy_actions_values[Anum_gs_masking_policy_act_label_name - 1] = DirectFunctionCall1(namein, CStringGetDatum(label_name.c_str())); policy_actions_values[Anum_gs_masking_policy_act_policy_oid - 1] = ObjectIdGetDatum(policyOid); policy_actions_values[Anum_gs_masking_policy_act_modify_date - 1] = curtime; - +// 创建一个用于插入的堆元组 HeapTuple policy_htup = heap_form_tuple(relation->rd_att, policy_actions_values, policy_actions_nulls); - +// 将堆元组插入关系表 simple_heap_insert(relation, policy_htup); + // 更新关系表的索引 CatalogUpdateIndexes(relation, policy_htup); + // 释放堆元组的内存 heap_freetuple(policy_htup); } @@ -325,6 +345,7 @@ static inline void add_labels_to_masking_action(const gs_stl::gs_string action_t * add_masking_filters * insert a set of filters into gs_masking_policy_filters */ + // 这个函数用于向数据遮蔽策略过滤器中添加策略过滤条件 static inline void add_masking_filters(const filters_set* filters_to_add, Relation relation) { Datum curtime; @@ -334,10 +355,12 @@ static inline void add_masking_filters(const filters_set* filters_to_add, Relati errno_t rc = EOK; /* Get current timestamp */ + // 获取当前时间 curtime = DirectFunctionCall1(timestamptz_timestamp, GetCurrentTimestamp()); - +// 遍历要添加的策略过滤器集合 for (filters_set::const_iterator it = filters_to_add->begin(); it != filters_to_add->end(); ++it) { /* restore values and nulls for insert new node group record */ + // 清空policy_filters_values和policy_filters_nulls数组 rc = memset_s(policy_filters_values, sizeof(policy_filters_values), 0, sizeof(policy_filters_values)); securec_check(rc, "\0", "\0"); rc = memset_s(policy_filters_nulls, sizeof(policy_filters_nulls), false, sizeof(policy_filters_nulls)); @@ -352,9 +375,11 @@ static inline void add_masking_filters(const filters_set* filters_to_add, Relati policy_filters_htup = heap_form_tuple(relation->rd_att, policy_filters_values, policy_filters_nulls); /* Do the insertion */ + // 将HeapTuple插入到关系中 simple_heap_insert(relation, policy_filters_htup); - + // 更新关系的索引信息 CatalogUpdateIndexes(relation, policy_filters_htup); + // 释放HeapTuple占用的内存 heap_freetuple(policy_filters_htup); } } diff --git a/src/gausskernel/security/gs_policy/gs_policy_utils.cpp b/src/gausskernel/security/gs_policy/gs_policy_utils.cpp old mode 100755 new mode 100644 index fdc289cf9..467b206c8 --- a/src/gausskernel/security/gs_policy/gs_policy_utils.cpp +++ b/src/gausskernel/security/gs_policy/gs_policy_utils.cpp @@ -54,24 +54,26 @@ #ifdef ENABLE_UT #define static #endif - +// 定义用于管理事件的钩子函数 GsSaveManagementEvent gs_save_mng_event_hook = NULL; GsSendManagementEvent gs_send_mng_event_hook = NULL; - +//GsSaveManagementEvent 和 GsSendManagementEvent 就是用于注册管理事件钩子的函数。 +//这些钩子函数允许用户在特定事件发生时执行自定义的管理操作。钩子函数通常用于插件系统、事件驱动编程和扩展应用程序的功能。 +// 保存管理消息 void save_manage_message(const char* message) { if (gs_save_mng_event_hook != NULL) { gs_save_mng_event_hook(message); } } - +// 发送管理消息 void send_manage_message(AuditResult result_type) { if (gs_send_mng_event_hook != NULL) { gs_send_mng_event_hook(result_type); } } - +// 以下是一系列重载操作符的定义,用于比较 GsPolicyStruct、PgPolicyFiltersStruct 和 PgPolicyPrivilegesAccessStruct 对象。 bool GsPolicyStruct::operator == (const GsPolicyStruct &arg) const { return strcasecmp(m_name.c_str(), arg.m_name.c_str()) == 0; @@ -165,46 +167,72 @@ int PgPolicyPrivilegesAccessStruct::operator - (const PgPolicyPrivilegesAccessSt } /* Process new filters from parser tree and tranform them into string */ +// 处理来自解析器树的新筛选器,并将它们转化为字符串 bool process_new_filters(const List *policy_filters, gs_stl::gs_string *flat_tree) { + // 如果策略过滤器为空,直接返回true if (!policy_filters) return true; + + // 清空输出的扁平化树字符串 flat_tree->clear(); - ListCell *policy_filter_item = NULL; + + // 用于存储策略树节点的堆栈 gs_stl::gs_vector nodes; + // 遍历策略过滤器列表 + ListCell *policy_filter_item = NULL; foreach(policy_filter_item, policy_filters) { PolicyFilterNode *root = (PolicyFilterNode *) lfirst(policy_filter_item); nodes.push_back(root); + + // 迭代处理策略树节点 while (nodes.size() > 0) { PolicyFilterNode* n = nodes.back(); nodes.pop_back(); - /* operator type node */ + + // 如果节点类型为操作符节点 if (!strcmp(n->node_type, "op")) { if (!strcmp(n->op_value, "and")) { + // 将逻辑与操作符 "*" 添加到扁平化树中 (void)flat_tree->append("*"); } else if (!strcmp(n->op_value, "or")) { + // 将逻辑或操作符 "+" 添加到扁平化树中 (void)flat_tree->append("+"); - } else { /* unsupported operator */ + } else { + // 不支持的操作符类型,返回false表示处理失败 return false; } + + // 将右子节点和左子节点压入堆栈,以便继续处理 nodes.push_back((PolicyFilterNode *)n->right); nodes.push_back((PolicyFilterNode *)n->left); - } else if (!strcmp(n->node_type, "filter")) { /* value type node */ + } else if (!strcmp(n->node_type, "filter")) { // 如果节点类型为值节点 if (n->has_not_operator == true) { + // 如果节点带有 "!" 表示否定操作符,则将 "!" 添加到扁平化树中 (void)flat_tree->append("!"); } + + // 添加策略过滤器的类型到扁平化树中 (void)flat_tree->append(n->filter_type); (void)flat_tree->append("["); List *filter_item_objects = (List *) n->values; ListCell *filter_obj = NULL; + + // 遍历策略过滤器的值列表 foreach(filter_obj, filter_item_objects) { const char *filter_value = (const char *)(((Value*)lfirst(filter_obj))->val.str); + + // 验证策略过滤器的值是否有效,如果无效则返回false表示处理失败 if (!verify_ip_role_app(n->filter_type, filter_value, flat_tree)) { return false; } + + // 添加策略过滤器的值到扁平化树中,并用逗号分隔 (void)flat_tree->append(","); } + + // 如果扁平化树的最后一个字符是逗号,则删除它 if (flat_tree->back() == ',') { flat_tree->pop_back(); } @@ -212,46 +240,54 @@ bool process_new_filters(const List *policy_filters, gs_stl::gs_string *flat_tre } } } + + // 处理完毕,返回true表示处理成功 return true; } + bool scan_to_delete_from_relation(long long row_id, Relation relation, unsigned int index_id) -{ +{// 如果关系为空,返回false if (relation == NULL) { return false; } ScanKeyData skey; /* Find the row to delete. */ + // 创建用于扫描的键值对结构体skey,这里的目的是找到要删除的行。 ScanKeyInit(&skey, ObjectIdAttributeNumber, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(row_id)); - + // 使用systable_beginscan函数开始对关系的扫描,使用指定的索引(index_id)。 SysScanDesc tgscan = systable_beginscan(relation, index_id, true, NULL, 1, &skey); - + // 获取扫描到的下一个堆元组。 HeapTuple tup = systable_getnext(tgscan); + // 如果没有有效的堆元组,结束扫描并返回false。 if (!HeapTupleIsValid(tup)) { systable_endscan(tgscan); return false; } /* Delete the label tuple */ + // 删除找到的堆元组。 simple_heap_delete(relation, &tup->t_self); - + // 结束扫描。 systable_endscan(tgscan); return true; } - +// 这个函数用于构建资源名称。 void construct_resource_name(const RangeVar *rel, gs_stl::gs_string *target_name_s) -{ +{// 如果有目录名,将其添加到目标名称中,以"."分隔。 if (rel->catalogname) { (void)target_name_s->append((const char *)rel->catalogname); target_name_s->push_back('.'); } + // 如果有模式名,将其添加到目标名称中,以"."分隔。 if (rel->schemaname) { (void)target_name_s->append((const char *)rel->schemaname); target_name_s->push_back('.'); } + // 添加表名到目标名称中。 if (rel->relname) { (void)target_name_s->append(rel->relname); } @@ -260,20 +296,22 @@ void construct_resource_name(const RangeVar *rel, gs_stl::gs_string *target_name /** * Check if current app is valid or not. */ + // 这个函数用于验证应用程序过滤器是否有效。 bool verify_app_filter(const char* obj_value) -{ +{// 如果应用程序值为空,生成错误报告并返回false。 if (strlen(obj_value) == 0) { ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("app: [%s] is invalid", obj_value))); return false; } /* The first character id numbers or dollar */ + // 检查应用程序值的首字符是否是数字或"$",如果是,生成错误报告并返回false。 char c = obj_value[0]; if ((c >= '0' && c <= '9') || c == '$') { ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("app: [%s] is invalid", obj_value))); return false; } - +// 遍历应用程序值的每个字符,检查是否包含无效字符。 int len = strlen(obj_value); for (int i = 0; i < len; i++) { c = obj_value[i]; @@ -294,10 +332,12 @@ bool verify_app_filter(const char* obj_value) * @ obj_value : the actual filter information. * @ return_value : record about the filter information. */ + // 这个函数用于验证过滤器信息的有效性,包括IP、角色和应用程序。 bool verify_ip_role_app(const char* obj_type, const char* obj_value, gs_stl::gs_string *return_value) { if (!strcasecmp(obj_type, "ip")) { const char* check_value = obj_value; + // 验证IP范围是否有效。 if (!IPRange::is_range_valid(check_value)) { ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("ip range: [%s] is invalid, please identify", obj_value))); return false; @@ -305,17 +345,20 @@ bool verify_ip_role_app(const char* obj_type, const char* obj_value, gs_stl::gs_ (void)return_value->append(check_value); return true; } else if (!strcasecmp(obj_type, "roles")) { + // 获取角色的OID并验证其有效性。 Oid uid = get_role_oid(obj_value, true); if (!OidIsValid(uid)) { ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("role: [%s] is invalid", obj_value))); return false; } char buffer[64]; /* buffer to store the oid int as string. 64 is the max length of oid. */ + // 用于将OID转换为字符串的缓冲区。 int nRet = snprintf_s(buffer, sizeof(buffer), sizeof(buffer) - 1, "%d", uid); securec_check_ss(nRet, "\0", "\0"); (void)return_value->append(buffer); return true; } else if (!strcasecmp(obj_type, "app")) { + // 验证应用程序过滤器是否有效。 bool is_valid_app = verify_app_filter(obj_value); if (!is_valid_app) { return false; @@ -324,7 +367,7 @@ bool verify_ip_role_app(const char* obj_type, const char* obj_value, gs_stl::gs_ (void)return_value->append(obj_value); return true; } - +// 这个函数用于添加权限和访问信息。 static bool add_privileges_access(const char *action_type, const char *label_name, privileges_access_set *actions, const policy_labels_map *existing_labels, const GsPolicyStruct *policy, gs_stl::gs_string *err_msg) @@ -334,6 +377,7 @@ static bool add_privileges_access(const char *action_type, const char *label_nam item.m_label_name = label_name; item.m_policy_oid = policy->m_id; /* validate that such label exists */ + // 验证是否存在该标签,如果不存在,生成错误报告并返回false。 if (existing_labels->find(label_name) == existing_labels->end()) { err_msg->clear(); (void)err_msg->append("Trying to add/remove privilege/access ["); @@ -346,7 +390,7 @@ static bool add_privileges_access(const char *action_type, const char *label_nam (void)actions->insert(item); return true; } - +// 这个函数用于处理目标,包括权限和访问。 bool handle_target(ListCell *target, int opt_type, bool is_add, @@ -356,12 +400,20 @@ bool handle_target(ListCell *target, const policy_labels_map *existing_labels, const GsPolicyStruct *policy, const char *acc_action_type) { + // 用于返回操作结果的变量 bool ret = false; + + // 根据策略选项类型进行不同的处理 switch (opt_type) { case POLICY_OPT_PRIVILEGES: { + // 获取目标对象的RangeVar RangeVar *rel = (RangeVar*)lfirst(target); + + // 构建目标对象的名称字符串 gs_stl::gs_string target_name_s; construct_resource_name((const RangeVar*)rel, &target_name_s); + + // 根据操作类型(添加或删除)调用add_privileges_access函数,处理特权访问控制 if (is_add) { ret = add_privileges_access(acc_action_type, target_name_s.c_str(), privs_to_add, existing_labels, policy, err_msg); @@ -372,9 +424,14 @@ bool handle_target(ListCell *target, } break; case POLICY_OPT_ACCESS: { + // 获取目标对象的RangeVar RangeVar *rel = (RangeVar*)lfirst(target); + + // 构建目标对象的名称字符串 gs_stl::gs_string target_name_s; construct_resource_name((const RangeVar*)rel, &target_name_s); + + // 根据操作类型(添加或删除)调用add_privileges_access函数,处理访问控制 if (is_add) { ret = add_privileges_access(acc_action_type, target_name_s.c_str(), access_to_add, existing_labels, policy, err_msg); @@ -384,13 +441,16 @@ bool handle_target(ListCell *target, } } break; - // : this is not handled here... + // : 这里未处理其他策略选项类型 default: break; } + + // 返回操作结果 return ret; } +// 这个函数用于解析逗号分隔的值,并验证它们的有效性。 static bool parse_values(const gs_stl::gs_string logical_expr_str, int *offset, const char* obj_type) { std::size_t found = gs_stl::gs_string::npos; @@ -401,12 +461,14 @@ static bool parse_values(const gs_stl::gs_string logical_expr_str, int *offset, bool filter_valid = false; int nRet; /* not finding last ']' means error */ + // 如果在偏移位置找不到最后的 ']',则表示出错。 if (limit_pos == gs_stl::gs_string::npos) { ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("filter: [%s] is invalid", logical_expr_str.c_str()))); return false; } + // 在逗号之间循环解析值,并验证它们的有效性。 while ((found = logical_expr_str.find(',', *offset)) != gs_stl::gs_string::npos && found < limit_pos) { nRet = snprintf_s(buff, sizeof(buff), sizeof(buff) - 1, "%.*s", (int)(found - *offset), logical_expr_str.c_str() + *offset); @@ -415,7 +477,7 @@ static bool parse_values(const gs_stl::gs_string logical_expr_str, int *offset, parsed = parsed && filter_valid; *offset = found + 1; } - + // 处理最后一个值。 if (*offset < (int)limit_pos) { nRet = snprintf_s(buff, sizeof(buff), sizeof(buff) - 1, "%.*s", (int)(limit_pos - *offset), logical_expr_str.c_str() + *offset); @@ -431,14 +493,16 @@ static bool parse_values(const gs_stl::gs_string logical_expr_str, int *offset, } /* Parses & validates (recursively) polish-notation format string into logical tree */ +// 这个函数用于验证逻辑表达式字符串是否有效。 bool validate_logical_expression(const gs_stl::gs_string logical_expr_str, int *offset) { int logical_expr_len = logical_expr_str.size(); while (*offset < logical_expr_len) { - /* AND/OR node */ + /* AND/OR node */// AND/OR 节点 if ((logical_expr_str[*offset] == '*') || (logical_expr_str[*offset] == '+')) { (*offset)++; + // 递归验证左右子树 return (validate_logical_expression(logical_expr_str, offset) /* go left */ && validate_logical_expression(logical_expr_str, offset)); /* go right */ } else if (logical_expr_str[*offset] == '!') { /* NOT operator */ @@ -457,7 +521,7 @@ bool validate_logical_expression(const gs_stl::gs_string logical_expr_str, int * return false; } - +// 这个函数用于获取当前会话的IP地址。 void get_session_ip(char *session_ip, int len) { if (len < MAX_IP_LEN) { @@ -475,7 +539,7 @@ void get_session_ip(char *session_ip, int len) get_client_ip(remote_addr, session_ip); } } - +// 这个函数用于解析客户端的IP地址。 void get_client_ip(const struct sockaddr* remote_addr, char *ip_str) { /* parse the remote ip address */ @@ -485,7 +549,7 @@ void get_client_ip(const struct sockaddr* remote_addr, char *ip_str) (void)inet_ntop(AF_INET, &((struct sockaddr_in*)remote_addr)->sin_addr, ip_str, MAX_IP_LEN - 1); } } - +// 这个函数用于检查数据库是否有效。 bool is_database_valid(const char* dbname) { if (dbname == NULL) { @@ -498,7 +562,7 @@ bool is_database_valid(const char* dbname) return false; } - +// 这个函数用于创建临时资源所有者。 ResourceOwnerData* create_temp_resourceowner() { ResourceOwner tmpOwner = ResourceOwnerCreate(t_thrd.utils_cxt.CurrentResourceOwner, @@ -507,7 +571,7 @@ ResourceOwnerData* create_temp_resourceowner() t_thrd.utils_cxt.CurrentResourceOwner = tmpOwner; return currentOwner; } - +// 这个函数用于释放临时资源所有者。 void release_temp_resourceowner(ResourceOwnerData* resource_owner) { ResourceOwner tmpOwner = t_thrd.utils_cxt.CurrentResourceOwner; diff --git a/src/gausskernel/security/gs_policy/gs_string.cpp b/src/gausskernel/security/gs_policy/gs_string.cpp index a2d21f6ed..7c1fc2197 100644 --- a/src/gausskernel/security/gs_policy/gs_string.cpp +++ b/src/gausskernel/security/gs_policy/gs_string.cpp @@ -32,6 +32,7 @@ namespace gs_stl { MemoryContext GetStringMemory() { if (t_thrd.security_policy_cxt.StringMemoryContext == NULL) { + // 如果字符串内存上下文尚未创建,则创建一个新的字符串内存上下文 t_thrd.security_policy_cxt.StringMemoryContext = AllocSetContextCreate(THREAD_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_SECURITY), "StringMemory", ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE); @@ -42,6 +43,7 @@ MemoryContext GetStringMemory() void DeleteStringMemory() { if (t_thrd.security_policy_cxt.StringMemoryContext != NULL) { + // 如果字符串内存上下文存在,则删除它 MemoryContextDelete(t_thrd.security_policy_cxt.StringMemoryContext); t_thrd.security_policy_cxt.StringMemoryContext = NULL; } @@ -50,6 +52,7 @@ void DeleteStringMemory() MemoryContext GetVectorMemory() { if (t_thrd.security_policy_cxt.VectorMemoryContext == NULL) { + // 如果向量内存上下文尚未创建,则创建一个新的向量内存上下文 t_thrd.security_policy_cxt.VectorMemoryContext = AllocSetContextCreate(THREAD_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_SECURITY), "VectorMemory", ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE); @@ -60,6 +63,7 @@ MemoryContext GetVectorMemory() void DeleteVectorMemory() { if (t_thrd.security_policy_cxt.VectorMemoryContext != NULL) { + // 如果向量内存上下文存在,则删除它 MemoryContextDelete(t_thrd.security_policy_cxt.VectorMemoryContext); t_thrd.security_policy_cxt.VectorMemoryContext = NULL; } @@ -68,6 +72,7 @@ void DeleteVectorMemory() MemoryContext GetMapMemory() { if (!t_thrd.security_policy_cxt.MapMemoryContext) { + // 如果映射内存上下文尚未创建,则创建一个新的映射内存上下文 t_thrd.security_policy_cxt.MapMemoryContext = AllocSetContextCreate(TopMemoryContext, "MapMemory", ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE); } @@ -77,6 +82,7 @@ MemoryContext GetMapMemory() void DeleteMapMemory() { if (t_thrd.security_policy_cxt.MapMemoryContext) { + // 如果映射内存上下文存在,则删除它 MemoryContextDelete(t_thrd.security_policy_cxt.MapMemoryContext); t_thrd.security_policy_cxt.MapMemoryContext = nullptr; } @@ -84,12 +90,14 @@ void DeleteMapMemory() void *_HashMapAllocFunc(Size request) { + // 使用映射内存上下文分配内存 return MemoryContextAlloc(GetMapMemory(), request); } MemoryContext GetSetMemory() { if (!t_thrd.security_policy_cxt.SetMemoryContext) { + // 如果集合内存上下文尚未创建,则创建一个新的集合内存上下文 t_thrd.security_policy_cxt.SetMemoryContext = AllocSetContextCreate(TopMemoryContext, "SetMemory", ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE); } @@ -99,6 +107,7 @@ MemoryContext GetSetMemory() void DeleteSetMemory() { if (t_thrd.security_policy_cxt.SetMemoryContext) { + // 如果集合内存上下文存在,则删除它 MemoryContextDelete(t_thrd.security_policy_cxt.SetMemoryContext); t_thrd.security_policy_cxt.SetMemoryContext = nullptr; } @@ -106,16 +115,19 @@ void DeleteSetMemory() void *_HashSetAllocFunc(Size request) { + // 使用集合内存上下文分配内存 return MemoryContextAlloc(GetSetMemory(), request); } int matchStr(const void *key1, const void *key2, Size keysize) { + // 比较两个字符串是否相等(不区分大小写) return strncasecmp((const char *)key1, (const char *)key2, keysize - 1); } int gs_stringCompareKeyFunc(const void *keyA, const void *keyB) { + // 比较两个gs_string对象作为键的大小 if (*(const gs_string *)keyA < *(const gs_string *)keyB) { return -1; } else if (*(const gs_string *)keyB < *(const gs_string *)keyA) { @@ -125,23 +137,28 @@ int gs_stringCompareKeyFunc(const void *keyA, const void *keyB) } } + // string implementation +// 初始化字符串缓冲区 inline bool gs_string::InitBuff(const char *str, size_t len) { if (m_buff == NULL) { - size_t init_len = (len > 0) ? (len + 1) : (strlen(str) + 1); - m_capacity = Max(MIN_STR_CAPACITY, init_len); - m_buff = AllocFunc(m_capacity); - errno_t ret = snprintf_s(m_buff, m_capacity, init_len - 1, "%.*s", (int)(init_len - 1), str); - securec_check_ss(ret, "\0", "\0"); - m_len = (size_t)ret; - return true; + // 如果字符串缓冲区为空,执行初始化 + size_t init_len = (len > 0) ? (len + 1) : (strlen(str) + 1); // 计算初始化长度 + m_capacity = Max(MIN_STR_CAPACITY, init_len); // 设置容量,至少为MIN_STR_CAPACITY + m_buff = AllocFunc(m_capacity); // 分配内存 + errno_t ret = snprintf_s(m_buff, m_capacity, init_len - 1, "%.*s", (int)(init_len - 1), str); // 格式化字符串 + securec_check_ss(ret, "\0", "\0"); // 检查 snprintf_s 的返回值 + m_len = (size_t)ret; // 设置字符串长度 + return true; // 返回初始化成功 } - return false; + return false; // 如果缓冲区不为空,返回初始化失败 } + +// 构造函数,用于创建 gs_string 对象 gs_string::gs_string(const char *str, size_t len) : m_buff(NULL), m_len(0), m_capacity(0) { - (void)InitBuff(str, len); + (void)InitBuff(str, len); // 调用 InitBuff 进行初始化 } gs_string::~gs_string() @@ -161,18 +178,20 @@ gs_string::~gs_string() } } +// 构造函数,从另一个 gs_string 对象拷贝构造 gs_string::gs_string(const gs_string &arg) : m_buff(NULL), m_len(0), m_capacity(0) { - operator = (arg); + operator = (arg); // 调用赋值运算符函数来复制内容 } +// 赋值运算符函数,将一个 gs_string 对象的内容赋值给另一个对象 gs_string &gs_string::operator = (const gs_string &arg) { if (&arg == this) { - return *this; + return *this; // 避免自赋值 } - /* m_buff should always be free if not NULL as will be taken place with arg */ + // 释放当前对象的缓冲区内存 if (m_buff != NULL) { pfree(m_buff); m_buff = NULL; @@ -180,13 +199,14 @@ gs_string &gs_string::operator = (const gs_string &arg) size_t len = arg.size(); if (len > 0) { - (void)InitBuff(arg.c_str(), arg.size()); + (void)InitBuff(arg.c_str(), arg.size()); // 初始化当前对象的缓冲区 } else { - (void)InitBuff("", 0); + (void)InitBuff("", 0); // 如果源字符串为空,则初始化为一个空字符串 } return *this; } +// 重载运算符 -,用于字符串比较 int gs_string::operator - (const gs_string &arg) const { if (this == &arg) { @@ -201,45 +221,50 @@ int gs_string::operator - (const gs_string &arg) const return 0; } +// 追加一个 gs_string 对象到当前对象 gs_string &gs_string::append(const gs_string &str) { return append(str.c_str(), str.size()); } +// 追加一个字符串到当前对象 gs_string &gs_string::append(const char *str, size_t len) { if (!InitBuff(str)) { size_t init_len = (len > 0) ? (len + 1) : (strlen(str) + 1); if (init_len > (m_capacity - m_len)) { - m_buff = ReallocFunc(m_capacity + init_len); + m_buff = ReallocFunc(m_capacity + init_len); // 如果空间不足,重新分配更大的空间 } errno_t ret = snprintf_s(m_buff + m_len, m_capacity - m_len, init_len - 1, "%.*s", (int)(init_len - 1), str); securec_check_ss(ret, "\0", "\0"); - m_len += (size_t)ret; + m_len += (size_t)ret; // 更新字符串长度 } return *this; } +// 在字符串末尾添加一个字符 void gs_string::push_back(char ch) { char t_chr[2] = {0}; t_chr[1] = ch; if (!InitBuff(t_chr)) { if ((m_len + 1) >= m_capacity) { - m_buff = ReallocFunc(m_capacity * 2); + m_buff = ReallocFunc(m_capacity * 2); // 如果空间不足,扩展为当前的两倍 } - m_buff[m_len++] = ch; - m_buff[m_len] = '\0'; + m_buff[m_len++] = ch; // 添加字符到缓冲区末尾 + m_buff[m_len] = '\0'; // 添加字符串结束符 } } +// 删除字符串末尾的字符 void gs_string::pop_back() { if (m_len > 0) { - m_buff[--m_len] = '\0'; + m_buff[--m_len] = '\0'; // 删除最后一个字符并添加结束符 } } +// 访问字符串的字符,类似于数组下标访问 char gs_string::operator[](int idx) const { if (idx > (int)m_len) { @@ -248,14 +273,16 @@ char gs_string::operator[](int idx) const return m_buff[idx]; } +// 清空字符串 void gs_string::clear() { if (m_buff != NULL) { - m_buff[0] = '\0'; - m_len = 0; + m_buff[0] = '\0'; // 将字符串清空 + m_len = 0; // 长度设为0 } } +// 在字符串中查找字符的位置 size_t gs_string::find(char arg, size_t start) const { for (; start < m_len; ++start) { @@ -263,28 +290,31 @@ size_t gs_string::find(char arg, size_t start) const return start; } } - return npos; + return npos; // 如果未找到,返回npos(无效位置) } +// 返回字符串的最后一个字符 char gs_string::back() const { if (m_len > 0) { - return m_buff[m_len - 1]; + return m_buff[m_len - 1]; // 返回最后一个字符 } - return m_buff[0]; + return m_buff[0]; // 如果字符串为空,返回第一个字符 } +// 截取字符串的子串 gs_string gs_string::substr(size_t pos, size_t len) const { if ((pos + len) < m_len) { - return gs_string((const char *)(m_buff + pos), len); + return gs_string((const char *)(m_buff + pos), len); // 返回指定位置和长度的子串 } if (pos < m_len) { - return gs_string((const char *)(m_buff + pos), m_len - pos); + return gs_string((const char *)(m_buff + pos), m_len - pos); // 返回从指定位置到末尾的子串 } - return gs_string((const char *)m_buff, m_len); + return gs_string((const char *)m_buff, m_len); // 返回整个字符串 } +// 替换字符串的一部分 gs_string &gs_string::replace(size_t pos, size_t len, const char *s) { if (pos < m_len) { @@ -293,7 +323,7 @@ gs_string &gs_string::replace(size_t pos, size_t len, const char *s) size_t jump = (rep_len - len); errno_t ret = EOK; if ((m_len + jump) >= m_capacity) { - (void)ReallocFunc(m_capacity + jump); + (void)ReallocFunc(m_capacity + jump); // 如果空间不足,重新分配更大的空间 } if (replace_len < m_len) { @@ -315,6 +345,7 @@ gs_string &gs_string::replace(size_t pos, size_t len, const char *s) return *this; } +// 删除字符串的一部分 void gs_string::erase(size_t pos, size_t len) { if (m_len == 0 || (pos >= m_len)) { @@ -325,48 +356,51 @@ void gs_string::erase(size_t pos, size_t len) while (idx < m_len) { m_buff[pos++] = m_buff[idx++]; } - m_len = pos; + m_len = pos; // 更新字符串长度 } else { - m_len = pos; + m_len = pos; // 删除指定位置后的所有字符 } - m_buff[m_len] = '\0'; + m_buff[m_len] = '\0'; // 添加字符串结束符 } +// 比较两个字符串是否相等 bool gs_string::operator == (const gs_string &arg) const { if (m_len != arg.m_len) { - return false; + return false; // 长度不同,字符串不等 } if (m_len > 0) { - return (strcasecmp(m_buff, arg.m_buff) == 0); + return (strcasecmp(m_buff, arg.m_buff) == 0); // 大小写不敏感比较字符串 } - return m_len == 0; + return m_len == 0; // 空字符串相等 } +// 比较两个字符串的大小 bool gs_string::operator < (const gs_string &arg) const { - return strcasecmp(m_buff, arg.m_buff) < 0; + return strcasecmp(m_buff, arg.m_buff) < 0; // 大小写不敏感比较字符串大小 } +// 分配内存并返回指向内存的指针 inline char *gs_string::AllocFunc(size_t _size) const { - return (char *)MemoryContextAlloc(GetStringMemory(), _size); + return (char *)MemoryContextAlloc(GetStringMemory(), _size); // 分配内存,使用GetStringMemory()分配 } +// 重新分配内存 inline char *gs_string::ReallocFunc(size_t _size) { - m_capacity = _size; + m_capacity = _size; // 更新容量 - char *buff = AllocFunc(m_capacity); - /* copy old data */ + char *buff = AllocFunc(m_capacity); // 分配新内存 + /* 复制旧数据 */ if (m_buff != NULL) { - errno_t ret = snprintf_s(buff, m_capacity, strlen(m_buff), "%s", m_buff); + errno_t ret = snprintf_s(buff, m_capacity, strlen(m_buff), "%s", m_buff); // 复制旧数据 securec_check_ss(ret, "\0", "\0"); - m_len = (size_t)ret; - pfree(m_buff); + m_len = (size_t)ret; // 更新字符串长度 + pfree(m_buff); // 释放旧内存 } - m_buff = buff; + m_buff = buff; // 更新缓冲区指针 return m_buff; } -} diff --git a/src/gausskernel/security/iprange/iprange.cpp b/src/gausskernel/security/iprange/iprange.cpp index ca7b66ef9..c1e39a830 100644 --- a/src/gausskernel/security/iprange/iprange.cpp +++ b/src/gausskernel/security/iprange/iprange.cpp @@ -126,8 +126,12 @@ IPRange::~IPRange() void IPRange::net_ipv6_to_host_order(IPV6 *ip, const struct sockaddr_in6 *sa) const { IPV6 tmp_ip; + + // 将 IPv6 地址从网络字节顺序转换为主机字节顺序 int rc = memcpy_s(&(tmp_ip.ip_64), sizeof(tmp_ip.ip_64), &(sa->sin6_addr), sizeof(tmp_ip.ip_64)); securec_check(rc, "\0", "\0"); + + // 使用 ntohl 函数将 32 位整数从网络字节顺序转换为主机字节顺序 ip->ip_32.a = ntohl(tmp_ip.ip_32.d); ip->ip_32.b = ntohl(tmp_ip.ip_32.c); ip->ip_32.c = ntohl(tmp_ip.ip_32.b); @@ -136,7 +140,10 @@ void IPRange::net_ipv6_to_host_order(IPV6 *ip, const struct sockaddr_in6 *sa) co void IPRange::net_ipv4_to_host_order(IPV6 *ip, const struct in_addr *addr) const { + // 使用 ntohl 函数将 IPv4 地址从网络字节顺序转换为主机字节顺序 ip->ip_32.a = ntohl(addr->s_addr); + + // 设置其他三个 32 位整数的值 ip->ip_32.b = 0x0000FFFF; ip->ip_32.c = ip->ip_32.d = 0; } @@ -146,15 +153,15 @@ bool IPRange::str_to_ip(const char* ip_str, IPV6 *ip) struct in_addr addr; struct sockaddr_in6 sa; + // 尝试将 IP 地址字符串解析为 IPv6 地址 if (inet_pton(AF_INET6, ip_str, &sa.sin6_addr) > 0) { net_ipv6_to_host_order(ip, &sa); - } else if (inet_pton(AF_INET, ip_str, &addr) > 0) { + } + // 如果解析为 IPv6 失败,则尝试解析为 IPv4 地址 + else if (inet_pton(AF_INET, ip_str, &addr) > 0) { net_ipv4_to_host_order(ip, &addr); } else { - /* - * Note that even the format keep the same as ipv6 or ipv4 - * still recognize it as invalid ip if ip exceed the valid range - */ + // 如果解析失败,设置错误消息并返回 false m_err_str = "invalid ip: " + std::string(ip_str); return false; } @@ -169,6 +176,7 @@ bool IPRange::mask_range(Range *range, unsigned short cidr) return false; } + // 使用预定义的掩码 LUT 更新 IPv4 范围 range->from.ip_32.a &= MASK_FROM_LUT[cidr]; range->to.ip_32.a |= MASK_TO_LUT[cidr]; } else { /* ipv6 */ @@ -176,7 +184,7 @@ bool IPRange::mask_range(Range *range, unsigned short cidr) m_err_str = "invalid cidr for ipv6: " + cidr; return false; } - unsigned short complement = cidr % 32; /* the result is less or equal to 31 */ + unsigned short complement = cidr % 32; /* 结果小于或等于 31 */ if (cidr > 96) { range->from.ip_32.a &= MASK_FROM_LUT[complement]; @@ -207,6 +215,7 @@ bool IPRange::mask_range(Range *range, unsigned short cidr) return true; } + /* * parse the ip with mask into range sturst , format is as below: * x.x.x.x|x, ptr is the postion of "|" @@ -222,12 +231,17 @@ bool IPRange::parse_mask(const char* range, size_t range_len, const char *ptr, R char mask_ip_str[IP_MAX_LEN] = {0}; size_t first_ip_str_len = ptr - range; size_t mask_ip_str_len = range_len - 1 - first_ip_str_len; - /* copy the first ip */ + + // 复制第一个 IP 地址部分 copy_without_spaces(first_ip_str, sizeof(first_ip_str), range, first_ip_str_len); - /* get the other ip */ + + // 获取掩码 IP 地址部分 copy_without_spaces(mask_ip_str, sizeof(mask_ip_str), ptr + 1, mask_ip_str_len); + IPV6 ip; IPV6 mask_ip; + + // 将第一个 IP 地址和掩码 IP 地址解析为 IPV6 结构体 if (!str_to_ip(first_ip_str, &ip)) { m_err_str = "failed to convert ip: " + std::string(first_ip_str); return false; @@ -236,7 +250,9 @@ bool IPRange::parse_mask(const char* range, size_t range_len, const char *ptr, R m_err_str = "failed to convert mask ip: " + std::string(mask_ip_str); return false; } + if (mask_ip.ip_32.b == 0x0000FFFF) { /* ipv4 */ + // 更新 IPv4 范围 new_range->from.ip_32.a = ip.ip_32.a & mask_ip.ip_32.a; new_range->from.ip_32.b = 0x0000FFFF; new_range->from.ip_64.upper = 0; @@ -244,9 +260,11 @@ bool IPRange::parse_mask(const char* range, size_t range_len, const char *ptr, R new_range->to.ip_32.b = 0x0000FFFF; new_range->to.ip_64.upper = 0; } else { + // 更新 IPv6 范围 new_range->from = ip & mask_ip; new_range->to = ip | ~mask_ip; } + return true; } @@ -256,14 +274,20 @@ bool IPRange::parse_single(const char* range, size_t range_len, Range *new_range m_err_str = "the range string length is not valid: " + range_len; return false; } + char buf[IP_MAX_LEN] = {0}; - /* copy the ip part to buf */ + + // 复制 IP 地址部分 copy_without_spaces(buf, sizeof(buf), range, range_len); + if (!str_to_ip(buf, &(new_range->from))) { m_err_str = "failed to convert ip: " + std::string(buf); return false; } + + // 设置范围的结束 IP 为起始 IP new_range->to = new_range->from; + return true; } @@ -273,18 +297,25 @@ bool IPRange::parse_slash(const char* range, size_t range_len, const char *ptr, m_err_str = "the range string length is not valid: " + range_len; return false; } + char buf[IP_MAX_LEN] = {0}; size_t real_range_len = ptr - range; - /* copy the ip part to buf */ + + // 复制 IP 地址部分 copy_without_spaces(buf, sizeof(buf), range, real_range_len); - /* get the CIDR */ + + // 获取 CIDR unsigned short cidr = (unsigned short)atoi(ptr + 1); + if (!str_to_ip(buf, &(new_range->from))) { m_err_str = "failed to convert ip: " + std::string(buf); return false; } + + // 设置结束 IP 为起始 IP,并应用掩码 new_range->to = new_range->from; (void)mask_range(new_range, cidr); + return true; } @@ -294,29 +325,36 @@ bool IPRange::parse_hyphen(const char* range, size_t range_len, const char *ptr, m_err_str = "the range string length is not valid: " + range_len; return false; } - /* clean white spaces */ + + /* 清除空格 */ char first_ip[IP_MAX_LEN] = {0}; char second_ip[IP_MAX_LEN] = {0}; size_t first_ip_len = ptr - range; size_t second_ip_len = range_len - 1 - first_ip_len; - /* copy the first ip */ + + /* 复制第一个 IP */ copy_without_spaces(first_ip, sizeof(first_ip), range, first_ip_len); - /* get the other ip */ + + /* 获取另一个 IP */ copy_without_spaces(second_ip, sizeof(second_ip), ptr + 1, second_ip_len); + if (!str_to_ip(first_ip, &(new_range->from))) { m_err_str = "failed to parse ip: " + std::string(first_ip); return false; } + if (!str_to_ip(second_ip, &(new_range->to))) { m_err_str = "failed to parse ip: " + std::string(second_ip); return false; } + if (new_range->from > new_range->to) { m_err_str = "the first ip (" + std::string(first_ip) + ") is bigger than the other (" + std::string(second_ip) + ")"; return false; } + return true; } @@ -324,31 +362,34 @@ void IPRange::handle_remove_intersection(Ranges_t *new_ranges, const Range *remo { IPV6 range_min = std::max(remove_range->from, exist_range->from); IPV6 range_max = std::min(remove_range->to, exist_range->to); + if (range_min > range_max) { - /* no intersaction */ + /* 没有交集,将存在的范围添加到新范围列表中 */ new_ranges->push_back(*exist_range); return; } - /* there is an intersaction the remove_range includes the exist_range */ + + /* 存在交集 */ if ((remove_range->from <= exist_range->from) && (remove_range->to >= exist_range->to)) { - /* remove the exist range */ + /* 移除的范围包含了存在的范围,不添加任何范围 */ return; } - // example: - // exist_range: 2 - 5 - // remove_range: 1 - 3 - // expected result: 4 - 5 + if (remove_range->from <= exist_range->from) { + /* 移除的范围起始于存在的范围的起始位置之前,更新存在的范围的起始位置 */ exist_range->from = remove_range->to + 1; new_ranges->push_back(*exist_range); return; } + if (remove_range->to >= exist_range->to) { + /* 移除的范围结束于存在的范围的结束位置之后,更新存在的范围的结束位置 */ exist_range->to = remove_range->from - 1; new_ranges->push_back(*exist_range); return; } - /* the remove range is inside the exist one */ + + /* 移除的范围在存在的范围内部,将更新后的存在的范围分为两个部分 */ new_ranges->push_back({exist_range->from, remove_range->from - 1}); new_ranges->push_back({remove_range->to + 1, exist_range->to}); } @@ -357,15 +398,19 @@ bool IPRange::handle_add_intersection(Range *new_range, const Range *exist_range { IPV6 range_min = std::max(new_range->from, exist_range->from); IPV6 range_max = std::min(new_range->to, exist_range->to); + if (range_min > range_max) { + /* 没有交集 */ return false; } - + + /* 存在交集,更新新范围以包含两个范围的合并 */ new_range->from = std::min(new_range->from, exist_range->from); new_range->to = std::max(new_range->to, exist_range->to); return true; } + /* * parse the ip range support below format * single ip: 127.0.0.1 @@ -375,7 +420,7 @@ bool IPRange::handle_add_intersection(Range *new_range, const Range *exist_range */ bool IPRange::parse_range(const char *range, size_t range_len, Range *new_range) { - /* handle format of "ip/cidr" */ + // 处理格式为 "ip/cidr" const char *ptr = (const char *)memchr(range, '/', range_len); if (ptr != NULL) { if (!parse_slash(range, range_len, ptr, new_range)) { @@ -384,7 +429,7 @@ bool IPRange::parse_range(const char *range, size_t range_len, Range *new_range) } return true; } - /* handle format of "ip from-ip to" */ + // 处理格式为 "ip from-ip to" ptr = (const char *)memchr(range, '-', range_len); if (ptr != NULL) { if (!parse_hyphen(range, range_len, ptr, new_range)) { @@ -393,7 +438,7 @@ bool IPRange::parse_range(const char *range, size_t range_len, Range *new_range) } return true; } - /* handle format of "ip | ip mask" */ + // 处理格式为 "ip | ip mask" ptr = (const char *)memchr(range, '|', range_len); if (ptr != NULL) { if (!parse_mask(range, range_len, ptr, new_range)) { @@ -401,7 +446,7 @@ bool IPRange::parse_range(const char *range, size_t range_len, Range *new_range) return false; } return true; - } else { /* handle single ip address as range */ + } else { // 处理单个 IP 地址作为范围 if (!parse_single(range, range_len, new_range)) { m_err_str = "failed with parsing the range: " + std::string(range); return false; @@ -412,102 +457,131 @@ bool IPRange::parse_range(const char *range, size_t range_len, Range *new_range) return false; } + bool IPRange::add_ranges(const std::unordered_set ranges) { + // 遍历传入的IP地址范围集合 for (const std::string range : ranges) { + // 调用add_range函数尝试添加每个IP地址范围 if (!add_range(range.c_str(), range.length())) { + // 如果添加失败,返回false return false; } } + // 所有IP地址范围添加成功,返回true return true; } bool IPRange::remove_ranges(const std::unordered_set ranges) { + // 遍历传入的IP地址范围集合 for (const std::string range : ranges) { + // 调用remove_range函数尝试移除每个IP地址范围 if (!remove_range(range.c_str(), range.length())) { + // 如果移除失败,返回false return false; } } + // 所有IP地址范围移除成功,返回true return true; } bool IPRange::add_range(Range *new_range) { - /* adding the new range */ + /* 添加新范围 */ if (m_ranges.size() == 0 || new_range->to < m_ranges[0].from) { + // 如果范围为空或新范围的结束地址小于第一个范围的开始地址 + // 将新范围插入到范围列表的开头 (void)m_ranges.insert(m_ranges.begin(), *new_range); return true; } else if (new_range->from > m_ranges.back().to) { + // 如果新范围的开始地址大于范围列表中最后一个范围的结束地址 + // 将新范围追加到范围列表的末尾 m_ranges.push_back(*new_range); return true; } Ranges_t new_ranges; bool we_had_intersection = false; uint32_t i = 0; - /* interate over the ranges and check for intersection or the place to add the new range */ + /* 遍历范围并检查交叉或添加新范围的位置 */ while (i < m_ranges.size()) { - /* in case of intersaction update the new range */ + /* 在交叉的情况下更新新范围 */ if (handle_add_intersection(new_range, &m_ranges[i])) { we_had_intersection = true; ++i; continue; } - /* just insert the intersaction */ + /* 只插入交叉部分 */ if (we_had_intersection) { we_had_intersection = false; new_ranges.push_back(*new_range); break; } else if (new_range->to < m_ranges[i].from) { - /* we got the plcae to put the new range */ + /* 找到插入新范围的位置 */ new_ranges.push_back(*new_range); break; } - /* just add the old range */ + /* 只添加旧范围 */ new_ranges.push_back(m_ranges[i]); ++i; } - /* if the intersection was until the end of the list - add it now */ + /* 如果交叉一直到列表的末尾 - 现在添加它 */ if (we_had_intersection) { new_ranges.push_back(*new_range); } - /* copy the rest of the list if exist */ + /* 复制剩余的列表(如果存在) */ while (i < m_ranges.size()) { new_ranges.push_back(m_ranges[i]); ++i; } + // 用新的范围列表替换旧的范围列表 m_ranges.swap(new_ranges); return true; } bool IPRange::is_range_valid(const std::string range) { + // 创建临时IPRange对象 IPRange tmp; + // 创建新的范围对象 Range new_range; + // 调用parse_range函数来解析传入的IP地址范围,并将结果存储在new_range中 return tmp.parse_range(range.c_str(), range.size(), &new_range); } bool IPRange::add_range(const char* range, size_t range_len) { + // 创建新的范围对象 Range new_range; + // 清除错误字符串 m_err_str.clear(); + // 调用parse_range函数来解析传入的IP地址范围,并将结果存储在new_range中 if (!parse_range(range, range_len, &new_range)) { + // 如果解析失败,返回false return false; } + // 调用add_range(&new_range)来添加新的范围 return add_range(&new_range); } bool IPRange::remove_range(const char *range, size_t range_len) { + // 创建新的范围列表 Ranges_t new_ranges; + // 创建要移除的范围对象 Range remove_range; + // 清除错误字符串 m_err_str.clear(); + // 调用parse_range函数来解析传入的IP地址范围,并将结果存储在remove_range中 if (!parse_range(range, range_len, &remove_range)) { + // 如果解析失败,返回false return false; } + // 遍历当前范围列表,处理交叉情况,将不交叉的范围添加到新列表中 for (Range exist_range : m_ranges) { handle_remove_intersection(&new_ranges, &remove_range, &exist_range); } + // 用新的范围列表替换旧的范围列表 m_ranges.swap(new_ranges); return true; } @@ -515,11 +589,13 @@ bool IPRange::remove_range(const char *range, size_t range_len) std::string IPRange::ip_to_str(const IPV6 *ip) const { char ip_str[INET6_ADDRSTRLEN]; - /* now get it back and print it */ + /* 获取IP地址字符串表示 */ if (IPRANGE_IS_IPV4(*ip)) { + // 如果是IPv4地址,将其转换为字符串表示 uint32_t tmp = htonl(ip->ip_32.a); (void)inet_ntop(AF_INET, &tmp, ip_str, INET_ADDRSTRLEN); } else { + // 如果是IPv6地址,将其转换为字符串表示 IPV6 tmp_ip = *ip; tmp_ip.ip_32.a = htonl(ip->ip_32.d); tmp_ip.ip_32.b = htonl(ip->ip_32.c); @@ -527,18 +603,20 @@ std::string IPRange::ip_to_str(const IPV6 *ip) const tmp_ip.ip_32.d = htonl(ip->ip_32.a); (void)inet_ntop(AF_INET6, &tmp_ip, ip_str, INET6_ADDRSTRLEN); } + // 返回IP地址的字符串表示 return std::string(ip_str); } bool IPRange::binary_search(const IPV6 ip) const { - /* do a binary search */ + /* 进行二进制搜索 */ size_t i = 0; size_t j = m_ranges.size() - 1; size_t mid = 0; while (i != j) { mid = (i + j) / 2; if (ip >= m_ranges[mid].from && ip <= m_ranges[mid].to) { + // 如果IP地址在范围中,返回true return true; } if (ip < m_ranges[mid].from) { @@ -547,24 +625,29 @@ bool IPRange::binary_search(const IPV6 ip) const i = mid + 1; } } + // 检查最后一个范围 return (ip >= m_ranges[i].from && ip <= m_ranges[i].to); } bool IPRange::is_intersect(const IPRange *arg) { + // 检查当前IP范围与另一个IP范围是否相交 for (size_t i = 0; i < m_ranges.size(); ++i) { Range tmp(m_ranges[i].from, m_ranges[i].to); for (size_t j = 0 ; j < arg->m_ranges.size(); ++j) { if (handle_add_intersection(&tmp, &arg->m_ranges[j])) { + // 如果有交集,返回true return true; } } } + // 没有交集,返回false return false; } bool IPRange::is_in_range(const char *ip_str) { + // 将IP地址字符串转换为IPv6对象并检查是否在范围内 IPV6 ip; if (!str_to_ip(ip_str, &ip)) { return false; @@ -574,19 +657,23 @@ bool IPRange::is_in_range(const char *ip_str) bool IPRange::is_in_range(const IPV6 *ip) { + // 检查IPv6地址是否在范围内 if (m_ranges.size() == 0) { m_err_str = "there are no ranges in this object"; return false; } m_err_str.clear(); if (*ip == localhost_ipv4 || *ip == localhost_ipv6) { + // 如果是本地主机地址,检查是否包含在范围内 return binary_search(localhost_ipv4) || binary_search(localhost_ipv6); } + // 检查IPv6地址是否在范围内 return binary_search(*ip); } bool IPRange::is_in_range(const uint32_t ipv4) { + // 将IPv4地址转换为IPv6对象并检查是否在范围内 IPV6 ip; net_ipv4_to_host_order(&ip, (struct in_addr*)&ipv4); return is_in_range(&ip); @@ -594,6 +681,7 @@ bool IPRange::is_in_range(const uint32_t ipv4) std::unordered_set IPRange::get_ranges_set() { + // 返回范围的字符串表示形式的无序集合 std::unordered_set rslt; for (Range range : m_ranges) { if (ip_to_str(&range.from).compare(ip_to_str(&range.to)) == 0) { @@ -607,6 +695,7 @@ std::unordered_set IPRange::get_ranges_set() void IPRange::copy_without_spaces(char buf[], size_t buf_len, const char *original, size_t original_len) const { + // 从原始字符串中复制字符,忽略空格 if (original_len == 0 || original_len > buf_len) { return; } @@ -621,6 +710,8 @@ void IPRange::copy_without_spaces(char buf[], size_t buf_len, const char *origin bool IPRange::empty() const { + // 检查IP范围是否为空 return m_ranges.empty(); } +