Allow plan cache result type to revalidate on the fly
Usually, when a relation gets changed by a DB user, an invalidation message is sent to the shared context. Plancache will revalidate this behavior multiple times during the execution so that if the base relation changes, the cached plan will be updated accordingly. However, Postgres does not allow the resulting attributes to change because changing the query's result on the fly requires extra hand- ling on the user application, which is out of the DB kernel's control. Also, it will generate unwanted behavior in a concurrent transactional application. Due to the user's heavy request, we are allowing this in this patch and introduced a new POSTMASTER GUC to control this behavior. This GUC is a POSTMASTER type because the result-checking behavior is a flag (i.e., an intrinsic attribute) of the plan cache, initially only for transactional statements. We need to ensure that when the user changes the GUC, no plan cache is working at the time.
This commit is contained in:
@ -608,6 +608,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|
|
||||
|
||||
11
src/common/backend/utils/cache/plancache.cpp
vendored
11
src/common/backend/utils/cache/plancache.cpp
vendored
@ -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);
|
||||
|
||||
@ -1205,6 +1205,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,
|
||||
|
||||
@ -199,6 +199,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;
|
||||
|
||||
/*
|
||||
@ -324,7 +325,7 @@ void PrepareQuery(PrepareStmt* stmt, const char* queryString)
|
||||
NULL,
|
||||
NULL,
|
||||
0, /* default cursor options */
|
||||
true, /* fixed result */
|
||||
fixed_result, /* fixed result */
|
||||
stmt->name);
|
||||
|
||||
/*
|
||||
@ -376,9 +377,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) {
|
||||
@ -1087,10 +1088,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
|
||||
@ -1414,11 +1421,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;
|
||||
}
|
||||
|
||||
@ -3560,6 +3560,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
|
||||
@ -3942,8 +3943,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) {
|
||||
@ -5818,8 +5820,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 */
|
||||
@ -12025,8 +12027,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...
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -474,6 +474,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 */
|
||||
|
||||
Reference in New Issue
Block a user