diff --git a/src/gausskernel/process/globalplancache/globalplancache_util.cpp b/src/gausskernel/process/globalplancache/globalplancache_util.cpp index b3c0927cf..f8e8c9462 100644 --- a/src/gausskernel/process/globalplancache/globalplancache_util.cpp +++ b/src/gausskernel/process/globalplancache/globalplancache_util.cpp @@ -1,26 +1,26 @@ /* -* Copyright (c) 2020 Huawei Technologies Co.,Ltd. -* -* openGauss is licensed under Mulan PSL v2. -* You can use this software according to the terms and conditions of the Mulan PSL v2. -* You may obtain a copy of Mulan PSL v2 at: -* -* http://license.coscl.org.cn/MulanPSL2 -* -* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, -* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, -* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -* See the Mulan PSL v2 for more details. -* ------------------------------------------------------------------------- -* -* globalplancache_util.cpp -* global plan cache -* -* IDENTIFICATION -* src/gausskernel/process/globalplancache/globalplancache_util.cpp -* -* ------------------------------------------------------------------------- -*/ + * Copyright (c) 2020 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * globalplancache_util.cpp + * global plan cache + * + * IDENTIFICATION + * src/gausskernel/process/globalplancache/globalplancache_util.cpp + * + * ------------------------------------------------------------------------- + */ #include "postgres.h" #include "knl/knl_variable.h" @@ -38,10 +38,17 @@ #include "utils/memutils.h" #include "utils/plancache.h" #include "utils/syscache.h" -void -GlobalPlanCache::FillClassicEnvSignatures(GPCEnv *env) +/* + * 功能:填充经典环境签名 + * + * 参数列表: + * env:指向 GPCEnv 结构的指针,表示环境配置信息 + */ +void GlobalPlanCache::FillClassicEnvSignatures(GPCEnv *env) { + // 初始化环境签名为0 env->plainenv.env_signature = 0; + // 逐个设置环境签名的各位,每一位表示一个环境配置的开关状态 env->plainenv.env_signature |= u_sess->attr.attr_sql.enable_fast_numeric; env->plainenv.env_signature |= u_sess->attr.attr_sql.enable_global_stats << 1; env->plainenv.env_signature |= u_sess->attr.attr_sql.enable_hdfs_predicate_pushdown << 2; @@ -74,17 +81,14 @@ GlobalPlanCache::FillClassicEnvSignatures(GPCEnv *env) env->plainenv.env_signature |= g_instance.attr.attr_sql.enable_orc_cache << 29; env->plainenv.env_signature |= u_sess->attr.attr_sql.acceleration_with_compute_pool << 30; env->plainenv.env_signature |= u_sess->attr.attr_sql.enable_extrapolation_stats << 31; - } - /* * @Description:Fill in the environment signatures which are bitmaps for the boolean type GUC parameters * @in num: GPCEnv * @return - void -*/ -void -GlobalPlanCache::FillEnvSignatures(GPCEnv *env) + */ +void GlobalPlanCache::FillEnvSignatures(GPCEnv *env) { /* We should only call this function if env is not NULL and it's not filled */ Assert(env && !env->filled); @@ -126,19 +130,27 @@ GlobalPlanCache::FillEnvSignatures(GPCEnv *env) env->plainenv.env_signature2 |= u_sess->attr.attr_sql.enable_opfusion << 17; env->plainenv.env_signature2 |= u_sess->attr.attr_sql.enable_partition_opfusion << 18; } - -void -GlobalPlanCache::EnvFill(GPCEnv *env, bool depends_on_role) +/* + * 功能:填充环境信息 + * + * 参数列表: + * env:指向 GPCEnv 结构的指针,表示环境配置信息 + * depends_on_role:布尔值,表示是否依赖于角色 + */ +void GlobalPlanCache::EnvFill(GPCEnv *env, bool depends_on_role) { /* We should only call this function if env is not NULL and it's not filled */ + // 断言:确保 env 不为 NULL,且未被填充过 Assert(env && !env->filled); - + // 初始化 env 为0 errno_t rc = memset_s(env, sizeof(GPCEnv), 0, sizeof(GPCEnv)); securec_check(rc, "\0", "\0"); + // 填充环境签名 FillEnvSignatures(env); + // 逐个设置环境配置参数 env->plainenv.best_agg_plan = u_sess->attr.attr_sql.best_agg_plan; env->plainenv.query_dop_tmp = u_sess->attr.attr_sql.query_dop_tmp; env->plainenv.rewrite_rule = u_sess->attr.attr_sql.rewrite_rule; @@ -174,46 +186,47 @@ GlobalPlanCache::EnvFill(GPCEnv *env, bool depends_on_role) env->plainenv.sql_beta_feature = u_sess->attr.attr_sql.sql_beta_feature; /* new GUC parameters which affect the plan */ + // 填充新的 GUC 参数 env->plainenv.qrw_inlist2join_optmode = u_sess->opt_cxt.qrw_inlist2join_optmode; env->plainenv.skew_strategy_store = u_sess->attr.attr_sql.skew_strategy_store; env->plainenv.database_id = u_sess->proc_cxt.MyDatabaseId; env->plainenv.plancachemode = u_sess->attr.attr_sql.g_planCacheMode; GlobalPlanCache::GetSchemaName(env); - + // 填充预期的计算节点组和默认的存储节点组 if (u_sess->attr.attr_sql.expected_computing_nodegroup) { - int rc = memcpy_s(env->expected_computing_nodegroup, - NAMEDATALEN, - u_sess->attr.attr_sql.expected_computing_nodegroup, - strlen(u_sess->attr.attr_sql.expected_computing_nodegroup)); + int rc = + memcpy_s(env->expected_computing_nodegroup, NAMEDATALEN, u_sess->attr.attr_sql.expected_computing_nodegroup, + strlen(u_sess->attr.attr_sql.expected_computing_nodegroup)); securec_check(rc, "", ""); } else { - env->expected_computing_nodegroup[0] = '\0'; + env->expected_computing_nodegroup[0] = '\0'; } if (u_sess->attr.attr_sql.default_storage_nodegroup) { - int rc = memcpy_s(env->default_storage_nodegroup, - NAMEDATALEN, - u_sess->attr.attr_sql.default_storage_nodegroup, - strlen(u_sess->attr.attr_sql.default_storage_nodegroup)); + int rc = memcpy_s(env->default_storage_nodegroup, NAMEDATALEN, u_sess->attr.attr_sql.default_storage_nodegroup, + strlen(u_sess->attr.attr_sql.default_storage_nodegroup)); securec_check(rc, "", ""); } else { env->default_storage_nodegroup[0] = '\0'; } + // 设置依赖角色信息和用户 ID env->depends_on_role = depends_on_role; env->user_oid = GetUserId(); } - -void -GlobalPlanCache::GetSchemaName(GPCEnv *env) +/* + * 功能:获取当前模式的模式名称 + * + * 参数列表: + * env:指向 GPCEnv 结构的指针,表示环境配置信息 + */ +void GlobalPlanCache::GetSchemaName(GPCEnv *env) { /* get schema name */ if (u_sess->attr.attr_common.namespace_current_schema) { - int rc = memcpy_s(env->schema_name, - NAMEDATALEN, - u_sess->attr.attr_common.namespace_current_schema, + int rc = memcpy_s(env->schema_name, NAMEDATALEN, u_sess->attr.attr_common.namespace_current_schema, strlen(u_sess->attr.attr_common.namespace_current_schema)); securec_check(rc, "", ""); } else { @@ -221,46 +234,77 @@ GlobalPlanCache::GetSchemaName(GPCEnv *env) } } - +/* + * 功能:全局计划缓存重置函数 + * + * 说明:此函数用于重置全局计划缓存,并清除相关内存上下文中的数据。 + */ void GPCResetAll() { pthread_mutex_lock(&g_instance.gpc_reset_lock); for (int i = 0; i < MAX_GLOBAL_CACHEMEM_NUM; ++i) { + // 解除内存上下文中的子上下文封印,以便重置 MemoryContextUnSealChildren(g_instance.cache_cxt.global_plancache_mem[i]); + // 重置内存上下文,清除其中的数据 MemoryContextReset(g_instance.cache_cxt.global_plancache_mem[i]); } + // 初始化全局计划缓存 g_instance.plan_cache->Init(); pthread_mutex_unlock(&g_instance.gpc_reset_lock); + // 记录日志,表示全局计划缓存已重置 GPC_LOG("gpc reset all", 0, 0); } - -void GPCCleanDatanodeStatement(int dn_stmt_num, const char* stmt_name) +/* + * 功能:清理数据节点上的语句 + * + * 参数列表: + * dn_stmt_num:要清理的语句数量 + * stmt_name:待清理语句的名称 + * + * 说明:此函数用于清理数据节点上的指定语句。仅在协调器节点且提供了有效的语句名称时执行。 + */ +void GPCCleanDatanodeStatement(int dn_stmt_num, const char *stmt_name) { + // 如果语句名称为空、为空字符串或不在协调器节点上,直接返回 if (stmt_name == NULL || stmt_name[0] == '\0' || !IS_PGXC_COORDINATOR) return; int n = 0; char *tmp_name = NULL; + // 循环清理指定数量的语句 for (n = 0; n < dn_stmt_num; n++) { + // 获取当前要清理的语句名称 tmp_name = get_datanode_statement_name(stmt_name, n); + // 调用 DropDatanodeStatement 函数来清理语句 DropDatanodeStatement(tmp_name); + // 释放临时语句名称的内存 pfree_ext(tmp_name); } } - -void GPCReGplan(CachedPlanSource* plansource) +/* + * 功能:将 CachedPlanSource 切换为全局计划(gplan) + * + * 参数列表: + * plansource:要切换的 CachedPlanSource 对象 + * + * 说明:此函数用于将 CachedPlanSource 切换为全局计划(gplan)。如果该计划支持 gplan 且当前不是共享状态,则进行切换。 + * 切换后,将该计划移动到首个已保存的计划中,以便进行全局共享。 + */ +void GPCReGplan(CachedPlanSource *plansource) { /* if is unshared for has cplan, and create gplan this time, * reset to shared and move to first_saved_plan */ if (!plansource->is_support_gplan || !plansource->gpc.status.IsUnShareCplan()) return; Assert(plansource->is_saved); + // 设置计划为共享状态 plansource->gpc.status.SetKind(GPC_SHARED); /* move into first_saved_plan */ if (u_sess->pcache_cxt.ungpc_saved_plan == plansource) { u_sess->pcache_cxt.ungpc_saved_plan = plansource->next_saved; } else { - CachedPlanSource* psrc = NULL; + CachedPlanSource *psrc = NULL; + // 寻找并更新未共享计划列表 for (psrc = u_sess->pcache_cxt.ungpc_saved_plan; psrc; psrc = psrc->next_saved) { if (psrc->next_saved == plansource) { psrc->next_saved = plansource->next_saved; @@ -268,72 +312,104 @@ void GPCReGplan(CachedPlanSource* plansource) } } } + // 设置计划的位置为在本地已保存计划列表中 plansource->gpc.status.SetLoc(GPC_SHARE_IN_LOCAL_SAVE_PLAN_LIST); + // 将计划移动到首个已保存计划中 plansource->next_saved = u_sess->pcache_cxt.first_saved_plan; u_sess->pcache_cxt.first_saved_plan = plansource; } - +/* + * 功能:清理与会话相关的全局计划缓存 + * + * 参数列表:无 + * + * 说明:此函数用于清理与会话相关的全局计划缓存。它首先检查是否启用了 CN-GPC(全局计划缓存), + * 如果未启用则直接返回。然后,它会删除所有的已经准备好的语句(prepared statements)。 + * 如果计划处于共享内存中,则删除对应的内存上下文。 + */ void CNGPCCleanUpSession() { + // 如果未启用 CN-GPC,则直接返回 if (!ENABLE_CN_GPC) { return; } + // 删除所有已经准备好的语句 DropAllPreparedStatements(); /* if in shared memory, delete context. */ - CachedPlanSource* psrc = u_sess->pcache_cxt.ungpc_saved_plan; - CachedPlanSource* next = NULL; + CachedPlanSource *psrc = u_sess->pcache_cxt.ungpc_saved_plan; + CachedPlanSource *next = NULL; while (psrc != NULL) { next = psrc->next_saved; - Assert (!psrc->gpc.status.InShareTable()); + // 断言计划不在共享表中 + Assert(!psrc->gpc.status.InShareTable()); + // 如果计划不是私有计划,则删除 if (!psrc->gpc.status.IsPrivatePlan()) DropCachedPlan(psrc); psrc = next; } + // 清理已保存计划列表中的计划 psrc = u_sess->pcache_cxt.first_saved_plan; while (psrc != NULL) { next = psrc->next_saved; - Assert (!psrc->gpc.status.InShareTable()); + // 断言计划不在共享表中 + Assert(!psrc->gpc.status.InShareTable()); + // 如果计划不是私有计划,则删除 if (!psrc->gpc.status.IsPrivatePlan()) DropCachedPlan(psrc); psrc = next; } } - +/* + * 功能:清理与会话相关的已保存计划 + * + * 参数列表:无 + * + * 说明:此函数用于清理与会话相关的已保存计划。它首先检查是否启用了 GPC(全局计划缓存), + * 如果未启用或者没有需要清理的计划,则直接返回。然后,它减少未命名语句(unnamed statement) + * 的引用计数,如果该语句在共享表中。最后,它清理已保存计划列表和未共享计划列表中的计划, + * 如果这些计划不是私有计划的话。 + */ void GPCCleanUpSessionSavedPlan() { + // 如果未启用 GPC 或没有需要清理的计划,则直接返回 if (!ENABLE_GPC) { return; } - if (u_sess->pcache_cxt.first_saved_plan == NULL && - u_sess->pcache_cxt.unnamed_stmt_psrc == NULL && + if (u_sess->pcache_cxt.first_saved_plan == NULL && u_sess->pcache_cxt.unnamed_stmt_psrc == NULL && u_sess->pcache_cxt.ungpc_saved_plan == NULL) { return; } /* unnamed_stmt_psrc only save shared gpc plan or private plan, * so we only need to sub refcount for shared plan. */ + // 如果未命名语句存在并且在共享表中,则减少引用计数并置为NULL if (u_sess->pcache_cxt.unnamed_stmt_psrc && u_sess->pcache_cxt.unnamed_stmt_psrc->gpc.status.InShareTable()) { u_sess->pcache_cxt.unnamed_stmt_psrc->gpc.status.SubRefCount(); u_sess->pcache_cxt.unnamed_stmt_psrc = NULL; } /* if in shared memory, delete context. */ /* For DN and CN */ - CachedPlanSource* psrc = u_sess->pcache_cxt.first_saved_plan; - CachedPlanSource* next = NULL; + CachedPlanSource *psrc = u_sess->pcache_cxt.first_saved_plan; + CachedPlanSource *next = NULL; u_sess->pcache_cxt.first_saved_plan = NULL; while (psrc != NULL) { next = psrc->next_saved; - Assert (!psrc->gpc.status.InShareTable()); + // 断言计划不在共享表中 + Assert(!psrc->gpc.status.InShareTable()); + // 如果计划不是私有计划,则删除 if (!psrc->gpc.status.IsPrivatePlan()) DropCachedPlan(psrc); psrc = next; } /* For CN */ + // 清理未共享计划列表(For CN) psrc = u_sess->pcache_cxt.ungpc_saved_plan; next = NULL; while (psrc != NULL) { next = psrc->next_saved; - Assert (!psrc->gpc.status.InShareTable()); + // 断言计划不在共享表中 + Assert(!psrc->gpc.status.InShareTable()); + // 如果计划不是私有计划,则删除 if (!psrc->gpc.status.IsPrivatePlan()) DropCachedPlan(psrc); psrc = next; @@ -341,75 +417,128 @@ void GPCCleanUpSessionSavedPlan() } /* incase change shared plan in execute stage, copy stmt into sess */ -List* CopyLocalStmt(const List* stmt_list, const MemoryContext parent_cxt, MemoryContext* plan_context) +/* + * 功能:复制本地语句列表 + * + * 参数列表: + * stmt_list:待复制的本地语句列表 + * parent_cxt:父内存上下文,新创建的内存上下文将以其为父上下文 + * plan_context:用于存储新创建的内存上下文 + * + * 返回值:复制后的本地语句列表 + * + * 说明:此函数用于复制给定的本地语句列表,并将复制后的语句列表存储在新的内存上下文中。 + * 新内存上下文以 parent_cxt 为父上下文创建。复制完成后,函数会将新的内存上下文存储在 + * plan_context 指针所指向的位置。 + */ +List *CopyLocalStmt(const List *stmt_list, const MemoryContext parent_cxt, MemoryContext *plan_context) { - *plan_context = AllocSetContextCreate(parent_cxt, - "CopyedStmt", - ALLOCSET_DEFAULT_MINSIZE, - ALLOCSET_DEFAULT_INITSIZE, + // 创建新的内存上下文,以 parent_cxt 为父上下文 + *plan_context = AllocSetContextCreate(parent_cxt, "CopyedStmt", ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE); /* * Copy plan into the new context. */ MemoryContext oldcxt = MemoryContextSwitchTo(*plan_context); - List* stmts = (List*)copyObject(stmt_list); + List *stmts = (List *)copyObject(stmt_list); (void)MemoryContextSwitchTo(oldcxt); return stmts; } /* function for modify SPICacheTable, global procedure plancache */ - +/* + * 功能:计算 SPI 缓存哈希值 + * + * 参数列表: + * key:用于计算哈希值的键值 + * keysize:键值的大小 + * + * 返回值:计算得到的哈希值 + * + * 说明:此函数用于计算 SPI 缓存中的哈希值。它接受一个键值(key)和键值的大小(keysize)作为输入, + * 并使用哈希算法计算出一个哈希值作为返回结果。如果键值为 NULL,函数将返回一个特殊值 INVALID_SPI_KEY。 + */ uint32 SPICacheHashFunc(const void *key, Size keysize) { if (unlikely(key == NULL)) { + // 如果键值为 NULL,返回特殊值 INVALID_SPI_KEY return INVALID_SPI_KEY; } + // 使用哈希算法计算哈希值 uint32 val1 = DatumGetUInt32(hash_any((const unsigned char *)key, (int)keysize)); return val1; } /* add new func */ +/* + * 功能:向 SPI 缓存表中插入条目 + * + * 参数列表: + * key:要插入的条目的键 + * func_oid:要插入的条目关联的函数 OID + * + * 说明:此函数用于向 SPI 缓存表中插入新的条目。它接受一个键(key)和一个函数 OID(func_oid)作为输入, + * 并将它们关联在一起插入到 SPI 缓存表中。如果 SPI 缓存表为空或键为特殊值 INVALID_SPI_KEY,则函数不执行任何操作。 + * 如果已经存在相同键的条目,函数会删除该条目并发出警告信息。 + */ void SPICacheTableInsert(uint32 key, Oid func_oid) { if (unlikely(u_sess->SPI_cxt.SPICacheTable == NULL || key == INVALID_SPI_KEY)) return; + // 检查是否已经存在相同的键 bool found = false; - SPIPlanCacheEnt* hentry = (SPIPlanCacheEnt*)hash_search(u_sess->SPI_cxt.SPICacheTable, - (void*)(&key), HASH_ENTER, &found); + SPIPlanCacheEnt *hentry = + (SPIPlanCacheEnt *)hash_search(u_sess->SPI_cxt.SPICacheTable, (void *)(&key), HASH_ENTER, &found); SPI_GPC_LOG("insert spiplan entry", NULL, func_oid); if (found) { + // 如果已经存在相同键的条目,释放相关的资源并发出警告信息 list_free_ext(hentry->SPIplan_list); hentry->SPIplan_list = NULL; elog(WARNING, "should not has same old function entry in SPICacheTable"); } else { hentry->SPIplan_list = NULL; } + // 关联函数 OID 到条目 hentry->func_oid = func_oid; } /* add plan for spi keep plan */ +/* + * 功能:向 SPI 缓存表中的条目插入 SPI 计划 + * + * 参数列表: + * key:要插入的条目的键 + * spi_plan:要插入的 SPI 计划 + * + * 说明:此函数用于向 SPI 缓存表中的指定条目插入新的 SPI 计划。它接受一个键(key)和一个 SPI 计划(spi_plan) + * 作为输入,将 SPI 计划与相应的条目关联起来。如果 SPI 缓存表为空,函数不执行任何操作。 + * 如果已经存在相同键的条目,函数会将 SPI 计划追加到该条目的计划列表中。 + */ void SPICacheTableInsertPlan(uint32 key, SPIPlanPtr spi_plan) { if (unlikely(u_sess->SPI_cxt.SPICacheTable == NULL)) return; + // 检查是否已经存在相同的键 bool found = false; - Assert (spi_plan->saved); - SPIPlanCacheEnt* hentry = (SPIPlanCacheEnt*)hash_search(u_sess->SPI_cxt.SPICacheTable, - (void*)(&key), HASH_ENTER, &found); + Assert(spi_plan->saved); + SPIPlanCacheEnt *hentry = + (SPIPlanCacheEnt *)hash_search(u_sess->SPI_cxt.SPICacheTable, (void *)(&key), HASH_ENTER, &found); if (found) { #ifdef USE_ASSERT_CHECKING /* cannot has same spiplan in list */ - ListCell* cell = NULL; + // 不能在列表中包含相同的 SPI 计划 + ListCell *cell = NULL; if (hentry->SPIplan_list != NULL) { - foreach(cell, hentry->SPIplan_list) { + foreach (cell, hentry->SPIplan_list) { SPIPlanPtr cur = (SPIPlanPtr)lfirst(cell); - Assert (cur->id != spi_plan->id); - Assert (cur != spi_plan); + Assert(cur->id != spi_plan->id); + Assert(cur != spi_plan); } } #endif SPI_GPC_LOG("insert spiplan into entry", spi_plan, hentry->func_oid); + // 切换内存上下文并将 SPI 计划追加到列表中 MemoryContext old_cxt = MemoryContextSwitchTo(u_sess->SPI_cxt.SPICacheTable->hcxt); hentry->SPIplan_list = lappend(hentry->SPIplan_list, spi_plan); (void)MemoryContextSwitchTo(old_cxt); @@ -419,60 +548,100 @@ void SPICacheTableInsertPlan(uint32 key, SPIPlanPtr spi_plan) } /* only use when delete function, plancache will be freed in SPI_freeplan */ +/* + * 功能:从 SPI 缓存表中删除指定键的条目 + * + * 参数列表: + * key:要删除的条目的键 + * + * 说明:此函数用于从 SPI 缓存表中删除指定键的条目。它接受一个键(key)作为输入, + * 并在 SPI 缓存表中查找匹配的条目,如果找到则删除它。如果 SPI 缓存表为空或未找到匹配的条目, + * 函数不执行任何操作。 + */ void SPIPlanCacheTableDelete(uint32 key) { if (unlikely(u_sess->SPI_cxt.SPICacheTable == NULL)) { return; } + // 查找并删除指定键的条目 bool found = false; - SPIPlanCacheEnt* hentry = (SPIPlanCacheEnt*)hash_search(u_sess->SPI_cxt.SPICacheTable, - (void*)(&key), HASH_REMOVE, &found); + SPIPlanCacheEnt *hentry = + (SPIPlanCacheEnt *)hash_search(u_sess->SPI_cxt.SPICacheTable, (void *)(&key), HASH_REMOVE, &found); if (hentry) { SPI_GPC_LOG("delete spiplan entry", NULL, hentry->func_oid); + // 释放计划列表中的内存 list_free_ext(hentry->SPIplan_list); } } - +/* + * 功能:从 SPI 缓存表中的指定键的条目中删除特定 SPI 计划 + * + * 参数列表: + * key:要查找的条目的键 + * plan:要删除的 SPI 计划 + * + * 说明:此函数用于从 SPI 缓存表中的指定键的条目中删除特定的 SPI 计划。 + * 它接受一个键(key)和一个 SPI 计划(plan)作为输入,并在指定的条目中查找匹配的 SPI 计划, + * 如果找到则删除它。如果 SPI 缓存表为空、未找到匹配的条目或未找到匹配的 SPI 计划, + * 函数不执行任何操作。 + */ void SPIPlanCacheTableDeletePlan(uint32 key, SPIPlanPtr plan) { if (unlikely(u_sess->SPI_cxt.SPICacheTable == NULL)) return; - SPIPlanCacheEnt* entry = SPIPlanCacheTableLookup(key); + // 查找指定键的条目 + SPIPlanCacheEnt *entry = SPIPlanCacheTableLookup(key); if (entry != NULL) { #ifdef USE_ASSERT_CHECKING - ListCell* cell = NULL; - foreach(cell, entry->SPIplan_list) { + ListCell *cell = NULL; + foreach (cell, entry->SPIplan_list) { SPIPlanPtr spiplan = (SPIPlanPtr)lfirst(cell); + // 断言确保要删除的计划与查找到的计划匹配 if (spiplan->id == plan->id) Assert(plan == spiplan); } #endif SPI_GPC_LOG("delete spiplan from entry", plan, entry->func_oid); - entry->SPIplan_list = list_delete_ptr(entry->SPIplan_list, (void*)plan); + // 从列表中删除指定的 SPI 计划 + entry->SPIplan_list = list_delete_ptr(entry->SPIplan_list, (void *)plan); } } - +/* + * 功能:使 SPI 缓存表中指定键的计划无效 + * + * 参数列表: + * key:要查找的条目的键 + * + * 说明:此函数用于使 SPI 缓存表中指定键的计划无效。它接受一个键(key)作为输入, + * 查找匹配的 SPI 缓存表条目,并将所有相关的计划标记为无效。 + * 如果 SPI 缓存表为空或未找到匹配的条目,函数不执行任何操作。 + */ void SPIPlanCacheTableInvalidPlan(uint32 key) { if (unlikely(u_sess->SPI_cxt.SPICacheTable == NULL)) { return; } bool found = false; - SPIPlanCacheEnt* hentry = (SPIPlanCacheEnt*)hash_search(u_sess->SPI_cxt.SPICacheTable, - (void*)(&key), HASH_REMOVE, &found); + // 查找指定键的条目 + SPIPlanCacheEnt *hentry = + (SPIPlanCacheEnt *)hash_search(u_sess->SPI_cxt.SPICacheTable, (void *)(&key), HASH_REMOVE, &found); if (hentry) { SPI_GPC_LOG("invalid each spiplan entry", NULL, hentry->func_oid); - ListCell* cell = NULL; + ListCell *cell = NULL; foreach (cell, hentry->SPIplan_list) { SPIPlanPtr spiplan = (SPIPlanPtr)lfirst(cell); + // 检查计划是否具有 plancache_list if (list_length(spiplan->plancache_list) == 0) continue; - ListCell* cl = NULL; + ListCell *cl = NULL; + // 遍历计划中的每个 CachedPlanSource foreach (cl, spiplan->plancache_list) { - CachedPlanSource* plansource = (CachedPlanSource*)lfirst(cl); + CachedPlanSource *plansource = (CachedPlanSource *)lfirst(cl); + // 如果计划在共享表中,将其状态标记为无效 if (plansource->gpc.status.InShareTable()) { plansource->gpc.status.SetStatus(GPC_INVALID); } else { + // 否则,将计划标记为无效 plansource->is_valid = false; if (plansource->gplan) plansource->gplan->is_valid = false; @@ -481,7 +650,13 @@ void SPIPlanCacheTableInvalidPlan(uint32 key) } } } - +/* + * 功能:初始化 SPI 计划缓存表 + * + * 说明:此函数用于初始化 SPI 计划缓存表。SPI 计划缓存表用于存储与 SQL 函数相关的计划, + * 其中每个计划都与一个唯一的键关联。在初始化过程中,函数会设置合适的哈希控制结构(ctl), + * 并创建哈希表来存储计划缓存条目。 + */ void SPICacheTableInit() { HASHCTL ctl; @@ -496,236 +671,355 @@ void SPICacheTableInit() ctl.entrysize = sizeof(SPIPlanCacheEnt); ctl.hash = uint32_hash; ctl.hcxt = u_sess->cache_mem_cxt; + // 创建 SPI 计划缓存表 u_sess->SPI_cxt.SPICacheTable = hash_create("SPIPlanCacheTable", func_per_user, &ctl, HASH_ELEM | HASH_FUNCTION | HASH_CONTEXT); SPI_GPC_LOG("init spiplan cache table", NULL, 0); } - -SPIPlanCacheEnt* SPIPlanCacheTableLookup(uint32 key) +/* + * 功能:查找 SPI 计划缓存表中的条目 + * + * 说明:此函数用于在 SPI 计划缓存表中查找与给定键相关的 SPIPlanCacheEnt 条目。 + * 如果找到匹配的条目,则返回该条目的指针;如果未找到匹配的条目,则返回 NULL。 + */ +SPIPlanCacheEnt *SPIPlanCacheTableLookup(uint32 key) { if (unlikely(u_sess->SPI_cxt.SPICacheTable == NULL)) return NULL; bool found = false; - SPIPlanCacheEnt* hentry = (SPIPlanCacheEnt*)hash_search( - u_sess->SPI_cxt.SPICacheTable, (void*)(&key), HASH_FIND, &found); + // 使用哈希查找在 SPI 计划缓存表中查找匹配的条目 + SPIPlanCacheEnt *hentry = + (SPIPlanCacheEnt *)hash_search(u_sess->SPI_cxt.SPICacheTable, (void *)(&key), HASH_FIND, &found); if (found) - return hentry; + return hentry; // 如果找到匹配的条目,则返回该条目的指针 else - return NULL; + return NULL; // 如果未找到匹配的条目,则返回 NULL } /* set unique id in PLpgSQL_expr for gpc */ -static void set_id_stmt(PLpgSQL_stmt* stmt, uint32* unique_id); -static void set_id_block(PLpgSQL_stmt_block* block, uint32* unique_id); -static void set_id_if(PLpgSQL_stmt_if* stmt, uint32* unique_id); -static void set_id_case(PLpgSQL_stmt_case* stmt, uint32* unique_id); -static void set_id_return_query(PLpgSQL_stmt_return_query* stmt, uint32* unique_id); -static void set_id_raise(PLpgSQL_stmt_raise* stmt, uint32* unique_id); -static void set_id_dynexecute(PLpgSQL_stmt_dynexecute* stmt, uint32* unique_id); -static void set_id_dynfors(PLpgSQL_stmt_dynfors* stmt, uint32* unique_id); -static void set_id_open(PLpgSQL_stmt_open* stmt, uint32* unique_id); -static void set_id_expr(PLpgSQL_expr* expr, uint32* unique_id); -static void set_id_stmts(List* stmts, uint32* unique_id); - -static void set_id_expr(PLpgSQL_expr* expr, uint32* unique_id) +static void set_id_stmt(PLpgSQL_stmt *stmt, uint32 *unique_id); +static void set_id_block(PLpgSQL_stmt_block *block, uint32 *unique_id); +static void set_id_if(PLpgSQL_stmt_if *stmt, uint32 *unique_id); +static void set_id_case(PLpgSQL_stmt_case *stmt, uint32 *unique_id); +static void set_id_return_query(PLpgSQL_stmt_return_query *stmt, uint32 *unique_id); +static void set_id_raise(PLpgSQL_stmt_raise *stmt, uint32 *unique_id); +static void set_id_dynexecute(PLpgSQL_stmt_dynexecute *stmt, uint32 *unique_id); +static void set_id_dynfors(PLpgSQL_stmt_dynfors *stmt, uint32 *unique_id); +static void set_id_open(PLpgSQL_stmt_open *stmt, uint32 *unique_id); +static void set_id_expr(PLpgSQL_expr *expr, uint32 *unique_id); +static void set_id_stmts(List *stmts, uint32 *unique_id); +/* + * 功能:为 PL/pgSQL 表达式设置唯一标识符 + * + * 参数列表: + * expr:PL/pgSQL 表达式 + * unique_id:用于分配唯一标识符的计数器 + */ +static void set_id_expr(PLpgSQL_expr *expr, uint32 *unique_id) { + // 检查输入的表达式是否为 NULL,如果是 NULL,则无需进行设置 if (expr == NULL) return; + + // 为表达式设置唯一标识符,通过递增 unique_id 指针的值 expr->idx = ++(*unique_id); } -static void set_id_stmts(List* stmts, uint32* unique_id) +/* + * 功能:为 PL/pgSQL 语句列表中的每个语句设置唯一标识符 + * + * 参数列表: + * stmts:PL/pgSQL 语句列表 + * unique_id:用于分配唯一标识符的计数器 + */ +static void set_id_stmts(List *stmts, uint32 *unique_id) { - ListCell* s = NULL; + ListCell *s = NULL; + // 遍历语句列表 foreach (s, stmts) { - set_id_stmt((PLpgSQL_stmt*)lfirst(s), unique_id); + // 为当前语句设置唯一标识符 + set_id_stmt((PLpgSQL_stmt *)lfirst(s), unique_id); } } -static void set_id_block(PLpgSQL_stmt_block* block, uint32* unique_id) +/* + * 功能:为 PL/pgSQL 块语句设置唯一标识符 + * + * 参数列表: + * block:PL/pgSQL 块语句 + * unique_id:用于分配唯一标识符的计数器 + */ +static void set_id_block(PLpgSQL_stmt_block *block, uint32 *unique_id) { + // 为块语句中的语句列表设置唯一标识符 set_id_stmts(block->body, unique_id); + // 如果存在异常处理块 if (block->exceptions != NULL) { - ListCell* e = NULL; + ListCell *e = NULL; + // 遍历异常处理块中的每个异常 foreach (e, block->exceptions->exc_list) { - PLpgSQL_exception* exc = (PLpgSQL_exception*)lfirst(e); + PLpgSQL_exception *exc = (PLpgSQL_exception *)lfirst(e); + // 为异常处理中的语句列表设置唯一标识符 set_id_stmts(exc->action, unique_id); } } } - -static void set_id_if(PLpgSQL_stmt_if* stmt, uint32* unique_id) +/* + * 功能:为 PL/pgSQL IF 语句设置唯一标识符 + * + * 参数列表: + * stmt:PL/pgSQL IF 语句 + * unique_id:用于分配唯一标识符的计数器 + */ +static void set_id_if(PLpgSQL_stmt_if *stmt, uint32 *unique_id) { - ListCell* l = NULL; + ListCell *l = NULL; + // 为 IF 语句的条件表达式设置唯一标识符 set_id_expr(stmt->cond, unique_id); + // 为 IF 语句的主体语句设置唯一标识符 set_id_stmts(stmt->then_body, unique_id); + // 遍历 ELSE IF 子句列表 foreach (l, stmt->elsif_list) { - PLpgSQL_if_elsif* elif = (PLpgSQL_if_elsif*)lfirst(l); + PLpgSQL_if_elsif *elif = (PLpgSQL_if_elsif *)lfirst(l); + // 为 ELSE IF 子句的条件表达式设置唯一标识符 set_id_expr(elif->cond, unique_id); + // 为 ELSE IF 子句的语句列表设置唯一标识符 set_id_stmts(elif->stmts, unique_id); } + // 为 IF 语句的 ELSE 子句设置唯一标识符 set_id_stmts(stmt->else_body, unique_id); } -static void set_id_case(PLpgSQL_stmt_case* stmt, uint32* unique_id) +/* + * 功能:为 PL/pgSQL CASE 语句设置唯一标识符 + * + * 参数列表: + * stmt:PL/pgSQL CASE 语句 + * unique_id:用于分配唯一标识符的计数器 + */ +static void set_id_case(PLpgSQL_stmt_case *stmt, uint32 *unique_id) { - ListCell* l = NULL; + ListCell *l = NULL; + // 为 IF 语句的条件表达式设置唯一标识符 set_id_expr(stmt->t_expr, unique_id); + // 遍历 CASE 语句的 WHEN 子句列表 foreach (l, stmt->case_when_list) { - PLpgSQL_case_when* cwt = (PLpgSQL_case_when*)lfirst(l); + PLpgSQL_case_when *cwt = (PLpgSQL_case_when *)lfirst(l); + // 为 WHEN 子句的条件表达式设置唯一标识符 set_id_expr(cwt->expr, unique_id); + // 为 WHEN 子句的语句列表设置唯一标识符 set_id_stmts(cwt->stmts, unique_id); } + // 为 CASE 语句的 ELSE 子句设置唯一标识符 set_id_stmts(stmt->else_stmts, unique_id); } - -static void set_id_open(PLpgSQL_stmt_open* stmt, uint32* unique_id) +/* + * 功能:为 PL/pgSQL OPEN 语句设置唯一标识符 + * + * 参数列表: + * stmt:PL/pgSQL OPEN 语句 + * unique_id:用于分配唯一标识符的计数器 + */ +static void set_id_open(PLpgSQL_stmt_open *stmt, uint32 *unique_id) { - ListCell* lc = NULL; + ListCell *lc = NULL; + // 为 OPEN 语句的参数查询表达式设置唯一标识符 set_id_expr(stmt->argquery, unique_id); + // 为 OPEN 语句的查询表达式设置唯一标识符 set_id_expr(stmt->query, unique_id); + // 为 OPEN 语句的动态查询表达式设置唯一标识符 set_id_expr(stmt->dynquery, unique_id); + // 遍历 OPEN 语句的参数列表 foreach (lc, stmt->params) { - set_id_expr((PLpgSQL_expr*)lfirst(lc), unique_id); + // 为参数表达式设置唯一标识符 + set_id_expr((PLpgSQL_expr *)lfirst(lc), unique_id); } } -static void set_id_return_query(PLpgSQL_stmt_return_query* stmt, uint32* unique_id) +/* + * 功能:为 PL/pgSQL RETURN QUERY 语句设置唯一标识符 + * + * 参数列表: + * stmt:PL/pgSQL RETURN QUERY 语句 + * unique_id:用于分配唯一标识符的计数器 + */ +static void set_id_return_query(PLpgSQL_stmt_return_query *stmt, uint32 *unique_id) { - ListCell* lc = NULL; + ListCell *lc = NULL; + // 为 RETURN QUERY 语句的查询表达式设置唯一标识符 set_id_expr(stmt->query, unique_id); + // 为 RETURN QUERY 语句的动态查询表达式设置唯一标识符 set_id_expr(stmt->dynquery, unique_id); + // 遍历 RETURN QUERY 语句的参数列表 foreach (lc, stmt->params) { - set_id_expr((PLpgSQL_expr*)lfirst(lc), unique_id); + // 为参数表达式设置唯一标识符 + set_id_expr((PLpgSQL_expr *)lfirst(lc), unique_id); } } -static void set_id_raise(PLpgSQL_stmt_raise* stmt, uint32* unique_id) +/* + * 功能:为 PL/pgSQL RAISE 语句设置唯一标识符 + * + * 参数列表: + * stmt:PL/pgSQL RAISE 语句 + * unique_id:用于分配唯一标识符的计数器 + */ +static void set_id_raise(PLpgSQL_stmt_raise *stmt, uint32 *unique_id) { - ListCell* lc = NULL; + ListCell *lc = NULL; + // 遍历 RAISE 语句的参数列表 foreach (lc, stmt->params) { - set_id_expr((PLpgSQL_expr*)lfirst(lc), unique_id); + // 为参数表达式设置唯一标识符 + set_id_expr((PLpgSQL_expr *)lfirst(lc), unique_id); } + // 遍历 RAISE 语句的选项列表 foreach (lc, stmt->options) { - PLpgSQL_raise_option* opt = (PLpgSQL_raise_option*)lfirst(lc); + PLpgSQL_raise_option *opt = (PLpgSQL_raise_option *)lfirst(lc); + // 为选项表达式设置唯一标识符 set_id_expr(opt->expr, unique_id); } } - -static void set_id_dynexecute(PLpgSQL_stmt_dynexecute* stmt, uint32* unique_id) +/* + * 功能:为 PL/pgSQL DYNEXECUTE 语句设置唯一标识符 + * + * 参数列表: + * stmt:PL/pgSQL DYNEXECUTE 语句 + * unique_id:用于分配唯一标识符的计数器 + */ +static void set_id_dynexecute(PLpgSQL_stmt_dynexecute *stmt, uint32 *unique_id) { - ListCell* lc = NULL; + ListCell *lc = NULL; + // 为 DYNEXECUTE 语句的查询表达式设置唯一标识符 set_id_expr(stmt->query, unique_id); + // 遍历 DYNEXECUTE 语句的参数列表 foreach (lc, stmt->params) { - set_id_expr((PLpgSQL_expr*)lfirst(lc), unique_id); + // 为参数表达式设置唯一标识符 + set_id_expr((PLpgSQL_expr *)lfirst(lc), unique_id); } } -static void set_id_dynfors(PLpgSQL_stmt_dynfors* stmt, uint32* unique_id) +/* + * 功能:为 PL/pgSQL DYNFORS 语句设置唯一标识符 + * + * 参数列表: + * stmt:PL/pgSQL DYNFORS 语句 + * unique_id:用于分配唯一标识符的计数器 + */ +static void set_id_dynfors(PLpgSQL_stmt_dynfors *stmt, uint32 *unique_id) { - ListCell* lc = NULL; + ListCell *lc = NULL; + // 为 DYNFORS 语句的主体语句设置唯一标识符 set_id_stmts(stmt->body, unique_id); + // 为 DYNFORS 语句的查询表达式设置唯一标识符 set_id_expr(stmt->query, unique_id); + // 遍历 DYNFORS 语句的参数列表 foreach (lc, stmt->params) { - set_id_expr((PLpgSQL_expr*)lfirst(lc), unique_id); + // 为参数表达式设置唯一标识符 + set_id_expr((PLpgSQL_expr *)lfirst(lc), unique_id); } } - -static void set_id_stmt(PLpgSQL_stmt* stmt, uint32* unique_id) +/* + * 功能:为 PL/pgSQL 语句设置唯一标识符 + * + * 参数列表: + * stmt:PL/pgSQL 语句 + * unique_id:用于分配唯一标识符的计数器 + */ +static void set_id_stmt(PLpgSQL_stmt *stmt, uint32 *unique_id) { switch ((enum PLpgSQL_stmt_types)stmt->cmd_type) { case PLPGSQL_STMT_BLOCK: - set_id_block((PLpgSQL_stmt_block*)stmt, unique_id); + set_id_block((PLpgSQL_stmt_block *)stmt, unique_id); break; case PLPGSQL_STMT_ASSIGN: - set_id_expr(((PLpgSQL_stmt_assign*)stmt)->expr, unique_id); + set_id_expr(((PLpgSQL_stmt_assign *)stmt)->expr, unique_id); break; case PLPGSQL_STMT_IF: - set_id_if((PLpgSQL_stmt_if*)stmt, unique_id); + set_id_if((PLpgSQL_stmt_if *)stmt, unique_id); break; case PLPGSQL_STMT_CASE: - set_id_case((PLpgSQL_stmt_case*)stmt, unique_id); + set_id_case((PLpgSQL_stmt_case *)stmt, unique_id); break; case PLPGSQL_STMT_LOOP: - set_id_stmts(((PLpgSQL_stmt_loop*)stmt)->body, unique_id); + set_id_stmts(((PLpgSQL_stmt_loop *)stmt)->body, unique_id); break; case PLPGSQL_STMT_WHILE: - set_id_expr(((PLpgSQL_stmt_while*)stmt)->cond, unique_id); - set_id_stmts(((PLpgSQL_stmt_while*)stmt)->body, unique_id); + set_id_expr(((PLpgSQL_stmt_while *)stmt)->cond, unique_id); + set_id_stmts(((PLpgSQL_stmt_while *)stmt)->body, unique_id); break; case PLPGSQL_STMT_FORI: - set_id_expr(((PLpgSQL_stmt_fori*)stmt)->lower, unique_id); - set_id_expr(((PLpgSQL_stmt_fori*)stmt)->upper, unique_id); - set_id_expr(((PLpgSQL_stmt_fori*)stmt)->step, unique_id); - set_id_stmts(((PLpgSQL_stmt_fori*)stmt)->body, unique_id); + set_id_expr(((PLpgSQL_stmt_fori *)stmt)->lower, unique_id); + set_id_expr(((PLpgSQL_stmt_fori *)stmt)->upper, unique_id); + set_id_expr(((PLpgSQL_stmt_fori *)stmt)->step, unique_id); + set_id_stmts(((PLpgSQL_stmt_fori *)stmt)->body, unique_id); break; case PLPGSQL_STMT_FORS: - set_id_stmts(((PLpgSQL_stmt_fors*)stmt)->body, unique_id); - set_id_expr(((PLpgSQL_stmt_fors*)stmt)->query, unique_id); + set_id_stmts(((PLpgSQL_stmt_fors *)stmt)->body, unique_id); + set_id_expr(((PLpgSQL_stmt_fors *)stmt)->query, unique_id); break; case PLPGSQL_STMT_FORC: - set_id_stmts(((PLpgSQL_stmt_forc*)stmt)->body, unique_id); - set_id_expr(((PLpgSQL_stmt_forc*)stmt)->argquery, unique_id); + set_id_stmts(((PLpgSQL_stmt_forc *)stmt)->body, unique_id); + set_id_expr(((PLpgSQL_stmt_forc *)stmt)->argquery, unique_id); break; case PLPGSQL_STMT_FOREACH_A: - set_id_expr(((PLpgSQL_stmt_foreach_a*)stmt)->expr, unique_id); - set_id_stmts(((PLpgSQL_stmt_foreach_a*)stmt)->body, unique_id); + set_id_expr(((PLpgSQL_stmt_foreach_a *)stmt)->expr, unique_id); + set_id_stmts(((PLpgSQL_stmt_foreach_a *)stmt)->body, unique_id); break; case PLPGSQL_STMT_EXIT: - set_id_expr(((PLpgSQL_stmt_exit*)stmt)->cond, unique_id); + set_id_expr(((PLpgSQL_stmt_exit *)stmt)->cond, unique_id); break; case PLPGSQL_STMT_RETURN: - set_id_expr(((PLpgSQL_stmt_return*)stmt)->expr, unique_id); + set_id_expr(((PLpgSQL_stmt_return *)stmt)->expr, unique_id); break; case PLPGSQL_STMT_RETURN_NEXT: - set_id_expr(((PLpgSQL_stmt_return_next*)stmt)->expr, unique_id); + set_id_expr(((PLpgSQL_stmt_return_next *)stmt)->expr, unique_id); break; case PLPGSQL_STMT_RETURN_QUERY: - set_id_return_query((PLpgSQL_stmt_return_query*)stmt, unique_id); + set_id_return_query((PLpgSQL_stmt_return_query *)stmt, unique_id); break; case PLPGSQL_STMT_RAISE: - set_id_raise((PLpgSQL_stmt_raise*)stmt, unique_id); + set_id_raise((PLpgSQL_stmt_raise *)stmt, unique_id); break; case PLPGSQL_STMT_EXECSQL: - set_id_expr(((PLpgSQL_stmt_execsql*)stmt)->sqlstmt, unique_id); + set_id_expr(((PLpgSQL_stmt_execsql *)stmt)->sqlstmt, unique_id); break; case PLPGSQL_STMT_DYNEXECUTE: - set_id_dynexecute((PLpgSQL_stmt_dynexecute*)stmt, unique_id); + set_id_dynexecute((PLpgSQL_stmt_dynexecute *)stmt, unique_id); break; case PLPGSQL_STMT_DYNFORS: - set_id_dynfors((PLpgSQL_stmt_dynfors*)stmt, unique_id); + set_id_dynfors((PLpgSQL_stmt_dynfors *)stmt, unique_id); break; case PLPGSQL_STMT_OPEN: - set_id_open((PLpgSQL_stmt_open*)stmt, unique_id); + set_id_open((PLpgSQL_stmt_open *)stmt, unique_id); break; case PLPGSQL_STMT_FETCH: - set_id_expr(((PLpgSQL_stmt_fetch*)stmt)->expr, unique_id); + set_id_expr(((PLpgSQL_stmt_fetch *)stmt)->expr, unique_id); break; case PLPGSQL_STMT_PERFORM: - set_id_expr(((PLpgSQL_stmt_perform*)stmt)->expr, unique_id); + set_id_expr(((PLpgSQL_stmt_perform *)stmt)->expr, unique_id); break; default: break; } } -void set_func_expr_unique_id(PLpgSQL_function* func) +void set_func_expr_unique_id(PLpgSQL_function *func) { uint32 unique_id = 0; int i; for (i = 0; i < func->ndatums; i++) { - PLpgSQL_datum* d = func->datums[i]; + PLpgSQL_datum *d = func->datums[i]; switch (d->dtype) { case PLPGSQL_DTYPE_VAR: { - PLpgSQL_var* var = (PLpgSQL_var*)d; + PLpgSQL_var *var = (PLpgSQL_var *)d; set_id_expr(var->default_val, &unique_id); set_id_expr(var->cursor_explicit_expr, &unique_id); } break; case PLPGSQL_DTYPE_ARRAYELEM: - set_id_expr(((PLpgSQL_arrayelem*)d)->subscript, &unique_id); + set_id_expr(((PLpgSQL_arrayelem *)d)->subscript, &unique_id); break; case PLPGSQL_DTYPE_ROW: case PLPGSQL_DTYPE_RECORD: { - PLpgSQL_row* row = (PLpgSQL_row*)d; + PLpgSQL_row *row = (PLpgSQL_row *)d; set_id_expr(row->default_val, &unique_id); } break; default: @@ -736,14 +1030,21 @@ void set_func_expr_unique_id(PLpgSQL_function* func) set_id_block(func->action, &unique_id); } } - +/* + * 功能:判断是否启用 GPC 解析 + * + * 参数列表: + * node:要解析的节点 + * + * 返回值:如果启用 GPC 解析,返回 true,否则返回 false + */ bool SPIParseEnableGPC(const Node *node) { if (node == NULL) return false; switch (nodeTag(node)) { case T_SelectStmt: - if (((SelectStmt*)node)->intoClause) + if (((SelectStmt *)node)->intoClause) return false; /* fall through */ case T_MergeStmt: @@ -760,4 +1061,3 @@ bool SPIParseEnableGPC(const Node *node) /* keep compiler quite */ return false; } -