石头队——openGauss完整评注代码 #40

Open
Cachuela wants to merge 163 commits from Cachuela/openGauss-server:master into master
62 changed files with 11114 additions and 7532 deletions

View File

@ -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;

View File

@ -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
}

View File

@ -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;// 返回不同之处的数量
}

View File

@ -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结构体指针rootGene geneEdge edge
// 以及Edge结构体指针edge_table作为参数不返回值。
static void remove_gene(PlannerInfo* root, Gene gene, Edge edge, Edge* edge_table);
// 声明函数gimme_gene它接受PlannerInfo结构体指针rootEdge edge
// 以及Edge结构体指针edge_table作为参数返回一个Gene。
static Gene gimme_gene(PlannerInfo* root, Edge edge, Edge* edge_table);
// 声明函数edge_failure它接受PlannerInfo结构体指针rootGene指针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 geneEdge 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结构体指针rootEdge 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结构体指针rootGene指针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 */
}

View File

@ -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.
*/
/*
PlannerInfoGene* 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列表clumpsClump new_
clump以及一个布尔标志forceClump与已有的Clumpclumps
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;

View File

@ -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;// 使用池大小作为默认的代数数量
}

View File

@ -29,7 +29,12 @@
/*
* avg_pool
*/
static double avg_pool(Pool* pool)
/*
pooledge 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,// 打印池的<E6B1A0><E79A84><EFBFBD>些统计信息包括最佳、最差、平均和平均池值
"%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);

View File

@ -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
*/

View File

@ -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)
*/

View File

@ -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)
使
*/

View File

@ -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)
*/

View File

@ -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 便使
*/

View File

@ -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
}
}
}
/*
PXtour1和tour2offspring
PX算子中tour1复制到offspring使
tour2中选择未被标记为已使用的城市offspring中tour1和tour2的信息
*/

View File

@ -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);
}
/*
*/

View File

@ -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);
}
/*
*/

View File

@ -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);

499
src/gausskernel/optimizer/path/clausesel.cpp Executable file → Normal file
View File

@ -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); // 释放变量统计数据资源
}
}
}
//这两个函数分别用于检测范围查询子句是否包含标量操作符,并为范围查询子句中的变量设置比率。第一个函数返回一个布尔值,指示是否包含标量操作符,第二个函数遍历变量列表,并根据条件设置变量比率。

3491
src/gausskernel/optimizer/path/costsize.cpp Executable file → Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -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);

View File

@ -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
}

142
src/gausskernel/optimizer/path/streampath_base.cpp Executable file → Normal file
View File

@ -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;
// 初始化 RRInfoRange 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;
// 初始化 DOPDegree 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;
}

View File

@ -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;// 如果未找到,返回无效的操作符标识
}

26
src/gausskernel/optimizer/plan/createplan.cpp Executable file → Normal file
View File

@ -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);
}

View File

@ -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;
}

File diff suppressed because it is too large Load Diff

77
src/gausskernel/optimizer/plan/pgxcplan_single.cpp Executable file → Normal file
View File

@ -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();

View File

@ -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
<EFBFBD><EFBFBD><EFBFBD>访
*
* - rootPlannerInfo结构
* - 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.
* tlistHAVING条件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聚合函数
*
*
* - rootPlannerInfo结构
* - 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.
* tlistbest_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条件<EFBFBD><EFBFBD><EFBFBD>
*/
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替换AggrefsORDER 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聚合函数truefalse
*
*
* - node:
* - context: MIN/MAX聚合函数信息的上下文列表
*
*
* MIN/MAX聚合函数truefalse
*/
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约束
*
* VarNOT NULL约束
* NOT NULL约束truefalse
*
*
* - parse: Query结构
* - ntest: NullTest节点NULL检查操作
*
*
* NOT NULL约束truefalse
*/
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值排在前面
*
*
* truefalse
*/
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.
* InitPlanParam
*/
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进入外部PlannerInfoInitPlan
* 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 truestrategy
*/
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;
}

2936
src/gausskernel/optimizer/plan/planner.cpp Executable file → Normal file

File diff suppressed because it is too large Load Diff

View File

@ -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);
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -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.
* RTErelationOids
*
* 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
*/

View File

@ -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 不是按值传递或其类型不是 INT8OIDint8 类型)或其值为 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);
}
}

View File

@ -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);

View File

@ -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普通表
// 则返回一个空的ListNIL
if (IS_PGXC_DATANODE || !IS_PGXC_COORDINATOR || !rte->relid || get_rel_relkind(rte->relid) != RELKIND_RELATION)
return NIL;
// 分布特性不受支持,抛出错误
DISTRIBUTED_FEATURE_NOT_SUPPORTED();
// 返回一个空的ListNIL
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_setopen->distribution可能不相同
* (2) mark_distribute_setop_distributionen->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操作UnionUnion 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();
}

66
src/gausskernel/optimizer/plan/streamplan_utils.cpp Executable file → Normal file
View File

@ -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.

View File

@ -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 tableupsertClause 不能包含不支持流式传输的表达式
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) {

View File

@ -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; // 参数的<E695B0><E79A84><EFBFBD>据类型
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 */
/* 在某些情况下,如 EXISTStlist 可能为空;随意使用 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;
}
/*
joinqualantiqual
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

273
src/gausskernel/optimizer/prep/prepjointree.cpp Executable file → Normal file
View File

@ -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_varnosold_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

View File

@ -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);

View File

@ -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),

View File

@ -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或裸表达式树开始;Querysublevels_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++;

View File

@ -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);//关闭触发规则的事件对象,但保持锁,直到事务提交
}

View File

@ -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 子句,从而强制执行行级安全策略
}
/*

View File

@ -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)
//用于获取给定规则OIDruleid对应的规则名
{
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;

View File

@ -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的关系中
OIDrelidIDbucketidtupleid
deldelta_relvaluesnulls
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);
}
// <20><><EFBFBD><EFBFBD><EFBFBD>闭关系
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"函数类型这里是通过检查与元组IDtuple 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));
}

View File

@ -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
TIDTuple 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<EFBFBD><EFBFBD><EFBFBD>
* 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 = &paramInfo->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;
}

View File

@ -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
}

View File

@ -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),

View File

@ -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(&current_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(&current_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 用于初始化和卸载块记录的挂钩函数。

View File

@ -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);
}
//执行全局链表的归档操作,包括权限检查、历史文件的管理、数据复制、哈希表操作等。该操作用于维护数据库的全局链表数据,并确保数据一致性和完整性。

View File

@ -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."))); // 如果获取失败<EFBC8C><E58899><EFBFBD><EFBFBD><EFBFBD>
}
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);
}
}
//这两个函数用于修复用户表和历史表之间的哈希差异,以及修复全局链表中的哈希差异。在函数开头,首先检查当前用户是否是超级用户或审计管理员,如果不是,报告权限不足的错误。然后,从函数参数中获取用户表的命名空间和名称,并将它们转换为 C 字符串,获取用户表的 OID。调用 ledger_usertable_check 函数来确保用户表存在且一致性。
//接下来,根据当前节点的角色,选择是否修复历史表。在数据节点或单节点上,调用 repaire_hist_table_internal 函数来获取并追加哈希差异。如果当前节点是协调器或单节点,会进行更多操作。在多节点环境下,会调用远程函数来获取数据节点的哈希差异,并将其累积到 delta 变量中。如果 delta 不为零,会向全局链表追加修复信息。
//最后,根据修复的结果,函数返回一个表示哈希差异或修复结果的 UInt64 数据类型。

View File

@ -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" 的列,因为它通常是用于存储哈希值的列。

View File

@ -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;
}

View File

@ -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<char>(t)),
std::istreambuf_iterator<char>());
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 库进行网络通信。

File diff suppressed because it is too large Load Diff

View File

@ -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_map<gs_stl::gs_string, gs_stl::gs_vector<PgPolicyMaskingActio
*
* load all labels of policyOid in catalog gs_masking_policy_actions into policy_labels
*/
// 这个函数用于加载已存在的数据遮蔽标签。
void load_existing_masking_labels(policy_labelname_set *policy_labels, Oid policyOid)
{
HeapTuple maskingPolicyTuple = NULL;
@ -163,6 +170,7 @@ void load_existing_masking_labels(policy_labelname_set *policy_labels, Oid polic
* load all action items of policyOid in catalog gs_masking_policy_actions actions
* load all action items of policyOid in to masking label map
*/
// 这个函数用于加载已存在的数据遮蔽动作。
void load_existing_masking_actions(masking_actions_set* actions,
masking_label_to_actions_map* labels_to_actions, long long policy_oid = 0)
{
@ -198,7 +206,7 @@ void load_existing_masking_actions(masking_actions_set* actions,
heap_close(relation, RowExclusiveLock);
}
// 这个函数用于检查数据遮蔽属性的类型是否允许遮蔽。
static bool check_masking_attrtype(Oid atttypeid)
{
if (!OidIsValid(atttypeid)) {
@ -234,7 +242,8 @@ static bool check_masking_attrtype(Oid atttypeid)
}
return result;
}
//功能:检查数据遮蔽属性的数据类型是否支持遮蔽。描述:该函数根据给定的数据类型 OID检查该数据类型是否支持进行数据遮蔽。支持的数据类型包括布尔型、日期时间类型、文本类型等。
// 这个函数用于检查列是否允许进行数据遮蔽。
static bool column_allow_to_masking(Oid relid, const char *column)
{
if (!OidIsValid(relid)) {
@ -252,20 +261,22 @@ static bool column_allow_to_masking(Oid relid, const char *column)
ReleaseSysCache(attr_tuple);
return check_masking_attrtype(column_typeid);
}
//功能:检查列是否允许进行数据遮蔽。描述:该函数根据给定的表 OID 和列名,检查该列是否存在于表中且允许进行数据遮蔽。如果列不存在或者数据类型不支持遮蔽,将返回 false。
/*
* add_labels_to_masking_action
*
* insert row of catalog gs_masking_policy_actions
*/
// 这个函数用于向数据遮蔽策略动作中添加标签信息
static inline void add_labels_to_masking_action(const gs_stl::gs_string action_type,
const gs_stl::gs_string action_params, const gs_stl::gs_string label_name,
Relation relation, Oid policyOid, Datum curtime)
{
{ // 创建一个用于标记是否为空的数组和一个数据数组
bool policy_actions_nulls[Natts_gs_masking_policy_actions] = {false};
Datum policy_actions_values[Natts_gs_masking_policy_actions] = {0};
// 检查是否有验证标签的挂钩函数
if (verify_label_hook) {
// 如果有验证标签的挂钩函数,检查标签是否有效
if (!verify_label_hook(label_name.c_str())) {
heap_close(relation, RowExclusiveLock);
ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("[%s] no such label found", label_name.c_str())));
@ -275,9 +286,12 @@ static inline void add_labels_to_masking_action(const gs_stl::gs_string action_t
/* for now mask on table/view is not allowed */
Relation label_rel = NULL;
policy_labels_map existing_labels;
// 打开策略标签的关系表
label_rel = heap_open(GsPolicyLabelRelationId, RowExclusiveLock);
// 加载现有的标签数据到existing_labels映射中
load_existing_labels(label_rel, &existing_labels);
heap_close(label_rel, RowExclusiveLock);
// 在existing_labels映射中查找指定<E68C87><E5AE9A><EFBFBD>标签
policy_labels_map::iterator lbit = existing_labels.find(label_name);
if (lbit != existing_labels.end()) {
policy_labels_set::iterator lit = (lbit->second)->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);
}
}

116
src/gausskernel/security/gs_policy/gs_policy_utils.cpp Executable file → Normal file
View File

@ -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<PolicyFilterNode *> 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;

View File

@ -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;
}
}

View File

@ -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<std::string> 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<std::string> 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<std::string> IPRange::get_ranges_set()
{
// 返回范围的字符串表示形式的无序集合
std::unordered_set<std::string> 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<std::string> 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();
}