diff --git a/src/bin/gs_guc/cluster_guc.conf b/src/bin/gs_guc/cluster_guc.conf index a7430cce2a18d10b949c2e1bbbcf85d8de2376c8..3041827732ea57ff021964c7addbd897356bc33b 100755 --- a/src/bin/gs_guc/cluster_guc.conf +++ b/src/bin/gs_guc/cluster_guc.conf @@ -575,6 +575,7 @@ zero_damaged_pages|bool|0,0|NULL|NULL| enable_bloom_filter|bool|0,0|NULL|NULL| cstore_insert_mode|enum|auto,main,delta|NULL|NULL| plan_cache_mode|enum|auto,force_generic_plan,force_custom_plan|NULL|NULL| +plan_cache_type_validation|bool|0,0|NULL|NULL| remote_read_mode|enum|off,non_authentication,authentication|NULL|NULL| enable_debug_vacuum|bool|0,0|NULL|NULL| enable_early_free|bool|0,0|NULL|NULL| diff --git a/src/common/backend/utils/cache/plancache.cpp b/src/common/backend/utils/cache/plancache.cpp index 757c50772ee909512ccca6628193929aa450284e..aa1831462588d0d1b1e95ab72cebc3cf990bfec2 100644 --- a/src/common/backend/utils/cache/plancache.cpp +++ b/src/common/backend/utils/cache/plancache.cpp @@ -1056,8 +1056,15 @@ List* RevalidateCachedQuery(CachedPlanSource* plansource, bool has_lp) } else if (resultDesc == NULL || plansource->resultDesc == NULL || !equalTupleDescs(resultDesc, plansource->resultDesc)) { /* can we give a better error message? */ - if (plansource->fixed_result) - ereport(ERROR, (errcode(ERRCODE_INVALID_CACHE_PLAN), errmsg("cached plan must not change result type"))); + if (plansource->fixed_result) { + ereport(ERROR, (errcode(ERRCODE_INVALID_CACHE_PLAN), + errmsg("cached plan must not change result type"))); + } else if (!FORCE_VALIDATE_PLANCACHE_RESULT) { + /* If result type validation is turned off, better notice the caller */ + ereport(NOTICE, (errmsg("cached plan's result type has changed"), + errdetail("plan cache result type validation is turned off"))); + } + oldcxt = MemoryContextSwitchTo(plansource->context); if (resultDesc) resultDesc = CreateTupleDescCopy(resultDesc); diff --git a/src/common/backend/utils/misc/guc/guc_sql.cpp b/src/common/backend/utils/misc/guc/guc_sql.cpp index f7512c4949a0333347dbb3d3bb4d78253801f9a5..72f8abfe3c35524caf0b3f04331c325a4f3aa70c 100755 --- a/src/common/backend/utils/misc/guc/guc_sql.cpp +++ b/src/common/backend/utils/misc/guc/guc_sql.cpp @@ -1156,6 +1156,18 @@ static void InitSqlConfigureNamesBool() NULL, NULL}, + {{"plan_cache_type_validation", + PGC_POSTMASTER, + NODE_ALL, + XC_HOUSEKEEPING_OPTIONS, + gettext_noop("Turns off pbe result type check: allow user to change the plan cache result on the fly."), + NULL}, + &g_instance.attr.attr_sql.plan_cache_type_validation, + true, + NULL, + NULL, + NULL}, + {{"lo_compat_privileges", PGC_SUSET, NODE_ALL, diff --git a/src/gausskernel/optimizer/commands/prepare.cpp b/src/gausskernel/optimizer/commands/prepare.cpp index 0ccb5a8a4f087f2ecc0db1053edd8722b3f18b72..a462695899a6241b67501139208f2cee88d43ac1 100755 --- a/src/gausskernel/optimizer/commands/prepare.cpp +++ b/src/gausskernel/optimizer/commands/prepare.cpp @@ -143,6 +143,7 @@ void PrepareQuery(PrepareStmt* stmt, const char* queryString) int nargs; Query* query = NULL; List* query_list = NIL; + bool fixed_result = FORCE_VALIDATE_PLANCACHE_RESULT; int i; /* @@ -265,7 +266,7 @@ void PrepareQuery(PrepareStmt* stmt, const char* queryString) NULL, NULL, 0, /* default cursor options */ - true, /* fixed result */ + fixed_result, /* fixed result */ stmt->name); /* @@ -317,9 +318,9 @@ void ExecuteQuery(ExecuteStmt* stmt, IntoClause* intoClause, const char* querySt t_thrd.postgres_cxt.cur_command_tag = transform_node_tag(psrc->raw_parse_tree); /* Shouldn't find a non-fixed-result cached plan */ - if (!entry->plansource->fixed_result) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("EXECUTE does not support variable-result cached plans"))); + if (!entry->plansource->fixed_result && FORCE_VALIDATE_PLANCACHE_RESULT) + ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("EXECUTE does not support variable-result cached plans"))); /* Evaluate parameters, if any */ if (entry->plansource->num_params > 0) { @@ -956,10 +957,16 @@ bool HaveActiveCoordinatorPreparedStatement(const char* stmt_name) TupleDesc FetchPreparedStatementResultDesc(PreparedStatement *stmt) { /* - * Since we don't allow prepared statements' result tupdescs to change, - * there's no need to worry about revalidating the cached plan here. + * User are allowed to change the result type of plan cache + * on the fly, so make sure to revalidate the descriptor + * before we pass it to the portal. */ - Assert(stmt->plansource->fixed_result); + if (FORCE_VALIDATE_PLANCACHE_RESULT) { + Assert(stmt->plansource->fixed_result); + } else { + RevalidateCachedQuery(stmt->plansource); + } + if (stmt->plansource->resultDesc) return CreateTupleDescCopy(stmt->plansource->resultDesc); else @@ -1283,11 +1290,9 @@ CachedPlanSource* GetCachedPlanSourceFromExplainExecute(const char* stmt_name) Assert(psrc != NULL); /* Shouldn't find a non-fixed-result cached plan */ - if (!psrc->fixed_result) { - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("EXPLAIN EXECUTE does not support variable-result cached plans"))); - } + if (!psrc->fixed_result && FORCE_VALIDATE_PLANCACHE_RESULT) + ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("EXPLAIN EXECUTE does not support variable-result cached plans"))); return psrc; } diff --git a/src/gausskernel/process/tcop/postgres.cpp b/src/gausskernel/process/tcop/postgres.cpp index ad26cce68fdb5b98d5349ae7b1c7fd130e7b6a08..bbb5d3b635aa5d36c0ddd051ab86cfe8e11ad33e 100755 --- a/src/gausskernel/process/tcop/postgres.cpp +++ b/src/gausskernel/process/tcop/postgres.cpp @@ -3473,6 +3473,7 @@ static void exec_parse_message(const char* query_string, /* string to execute */ CachedPlanSource* psrc = NULL; bool is_named = false; bool save_log_statement_stats = u_sess->attr.attr_common.log_statement_stats; + bool fixed_result = FORCE_VALIDATE_PLANCACHE_RESULT; char msec_str[PRINTF_DST_MAX]; char* mask_string = NULL; #ifdef ENABLE_MULTIPLE_NODES @@ -3855,8 +3856,9 @@ static void exec_parse_message(const char* query_string, /* string to execute */ /* Finish filling in the CachedPlanSource */ CompleteCachedPlan(psrc, querytree_list, unnamed_stmt_context, paramTypes, paramModes, numParams, NULL, NULL, 0, /* default cursor options */ - true, stmt_name, single_exec_node, - is_read_only); /* fixed result */ + fixed_result, /* fixed result */ + stmt_name, single_exec_node, + is_read_only); /* For ctas query, rewrite is not called in PARSE, so we must set invalidation to revalidate the cached plan. */ if (is_ctas) { @@ -5734,8 +5736,8 @@ void exec_describe_statement_message(const char* stmt_name) Assert(NULL != psrc); - /* Prepared statements shouldn't have changeable result descs */ - Assert(psrc->fixed_result); + /* Prepared statements shouldn't have changeable result descs unless being forced */ + Assert(!FORCE_VALIDATE_PLANCACHE_RESULT || psrc->fixed_result); #ifdef ENABLE_MOT /* set current transaction storage engine */ @@ -11849,8 +11851,8 @@ static void exec_batch_bind_execute(StringInfo input_message) switch (describe_type) { case 'S': { StringInfoData buf; - /* Prepared statements shouldn't have changeable result descs */ - Assert(psrc->fixed_result); + /* Prepared statements shouldn't have changeable result descs unless being forced */ + Assert(!FORCE_VALIDATE_PLANCACHE_RESULT || psrc->fixed_result); /* * First describe the parameters... diff --git a/src/include/knl/knl_guc/knl_instance_attr_sql.h b/src/include/knl/knl_guc/knl_instance_attr_sql.h index ff5a7626bd910cf2ecd0660a92ff5b6150dd6874..ba7c05108c7fc5c4dd7170d9c541ad4fb6b13e16 100644 --- a/src/include/knl/knl_guc/knl_instance_attr_sql.h +++ b/src/include/knl/knl_guc/knl_instance_attr_sql.h @@ -47,6 +47,7 @@ typedef struct knl_instance_attr_sql { bool enable_orc_cache; bool enable_default_cfunc_libpath; bool enableRemoteExcute; + bool plan_cache_type_validation; int udf_memory_limit; int UDFWorkerMemHardLimit; int job_queue_processes; diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h index 405844e552f5dc5c8e340e49fbc69cd33831f1d3..19197aec2276b8fe9807dfd8536d9b32e7c5ab36 100755 --- a/src/include/utils/guc.h +++ b/src/include/utils/guc.h @@ -471,6 +471,10 @@ typedef enum { g_instance.shmem_cxt.numaNodeNum <= PARTITION_OPFUSION_MAX_NUMA_NODE && \ !g_instance.attr.attr_common.enable_global_syscache) +/* Wrapper macro for forced plan cache result type check */ +#define FORCE_VALIDATE_PLANCACHE_RESULT \ + ((bool)g_instance.attr.attr_sql.plan_cache_type_validation) + typedef enum { SUMMARY = 0, /* not collect multi column statistics info */ DETAIL = 1, /* collect multi column statistics info */ diff --git a/src/test/regress/output/recovery_2pc_tools.source b/src/test/regress/output/recovery_2pc_tools.source index 1d48f7c41ed1f10afe730ce9f8cb3f671befdd6a..a8ded9c66e6e8c3d88b45ec041a5642cee67d471 100644 --- a/src/test/regress/output/recovery_2pc_tools.source +++ b/src/test/regress/output/recovery_2pc_tools.source @@ -531,6 +531,7 @@ select name,vartype,unit,min_val,max_val from pg_settings where name <> 'qunit_c perf_directory | string | | | pgxc_node_name | string | | | plan_cache_mode | enum | | | + plan_cache_type_validation | bool | | | plan_mode_seed | integer | | -1 | 2147483647 pldebugger_timeout | integer | s | 1 | 86400 pljava_vmoptions | string | | |