diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c index 8b76e98529..14b8bec6ff 100644 --- a/src/backend/optimizer/path/costsize.c +++ b/src/backend/optimizer/path/costsize.c @@ -154,6 +154,8 @@ bool enable_partition_pruning = true; bool enable_presorted_aggregate = true; bool enable_async_append = true; +double skewed_param_factor = DEFAULT_SKEWED_PARAM_FACTOR; + typedef struct { PlannerInfo *root; diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c index edc25d712e..8b922c0c95 100644 --- a/src/backend/optimizer/util/clauses.c +++ b/src/backend/optimizer/util/clauses.c @@ -2493,6 +2493,8 @@ eval_const_expressions_mutator(Node *node, pval, prm->isnull, typByVal); + if (paramLI->paramFetch == NULL) + con->paramid = param->paramid; con->location = param->location; return (Node *) con; } diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c index cea777e9d4..9fa6548d23 100644 --- a/src/backend/utils/adt/selfuncs.c +++ b/src/backend/utils/adt/selfuncs.c @@ -273,10 +273,32 @@ eqsel_internal(PG_FUNCTION_ARGS, bool negate) * in the query.) */ if (IsA(other, Const)) - selec = var_eq_const(&vardata, operator, collation, - ((Const *) other)->constvalue, - ((Const *) other)->constisnull, - varonleft, negate); + { + Const * con = (Const *) other; + + if (con->paramid > 0) + { + bool skewed_stat = false; + + selec = var_eq_const_ext(&vardata, operator, collation, + ((Const *) other)->constvalue, + ((Const *) other)->constisnull, + varonleft, negate, &skewed_stat); + + if (skewed_stat) + { + ParamExternData *prm; + + prm = &root->glob->boundParams->params[con->paramid - 1]; + prm->pflags |= PARAM_FLAG_SKEWEDSTAT; + } + } + else + selec = var_eq_const(&vardata, operator, collation, + ((Const *) other)->constvalue, + ((Const *) other)->constisnull, + varonleft, negate); + } else selec = var_eq_non_const(&vardata, operator, collation, other, varonleft, negate); @@ -295,6 +317,16 @@ double var_eq_const(VariableStatData *vardata, Oid oproid, Oid collation, Datum constval, bool constisnull, bool varonleft, bool negate) +{ + return var_eq_const_ext(vardata, oproid, collation, + constval, constisnull, + varonleft, negate, NULL); +} + +double +var_eq_const_ext(VariableStatData *vardata, Oid oproid, Oid collation, + Datum constval, bool constisnull, + bool varonleft, bool negate, bool *skewed_stat) { double selec; double nullfrac = 0.0; @@ -387,6 +419,39 @@ var_eq_const(VariableStatData *vardata, Oid oproid, Oid collation, break; } } + + Assert(sslot.nvalues > 0); + if (skewed_stat) + { + /* Check whether the values of the field is skewed according to the MCVs. */ + if (sslot.nvalues > 1 && + sslot.numbers[0] > sslot.numbers[sslot.nvalues - 1] * skewed_param_factor) + /* Compare the maximum and minimum quantities in the MCVs. */ + *skewed_stat = true; + else + { + /* + * Compare the maximum number in the MCVs with the average number of + * other values(not in the MCVs). + */ + int j; + double sumcommon = 0.0; + double otherdistinct, avgselec; + + for (j = 0; j < sslot.nnumbers; j++) + sumcommon += sslot.numbers[j]; + + otherdistinct = get_variable_numdistinct(vardata, &isdefault) - + sslot.nnumbers; + + if (otherdistinct > 0) + { + avgselec = (1.0 - sumcommon - nullfrac) / otherdistinct; + if (sslot.numbers[0] > (avgselec * skewed_param_factor)) + *skewed_stat = true; + } + } + } } else { diff --git a/src/backend/utils/cache/plancache.c b/src/backend/utils/cache/plancache.c index 5194cbf2cc..d1ee00139e 100644 --- a/src/backend/utils/cache/plancache.c +++ b/src/backend/utils/cache/plancache.c @@ -991,6 +991,20 @@ BuildCachedPlan(CachedPlanSource *plansource, List *qlist, else plan_context = CurrentMemoryContext; + if (boundParams) + { + int i; + + for (i =0; i < boundParams->numParams; i++) + { + if (boundParams->params[i].pflags & PARAM_FLAG_SKEWEDSTAT) + { + plansource->hasSkewedparam = true; + break; + } + } + } + /* * Create and fill the CachedPlan struct within the new context. */ @@ -1066,6 +1080,12 @@ choose_custom_plan(CachedPlanSource *plansource, ParamListInfo boundParams) if (plan_cache_mode == PLAN_CACHE_MODE_FORCE_CUSTOM_PLAN) return true; + if (plan_cache_mode == PLAN_CACHE_MODE_STAT_ADAPTIVE_PLAN) + { + if (plansource->hasSkewedparam) + return true; + } + /* See if caller wants to force the decision */ if (plansource->cursor_options & CURSOR_OPT_GENERIC_PLAN) return false; diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 70652f0a3f..93f85baedb 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -403,6 +403,7 @@ static const struct config_enum_entry debug_parallel_query_options[] = { static const struct config_enum_entry plan_cache_mode_options[] = { {"auto", PLAN_CACHE_MODE_AUTO, false}, + {"stat_adaptive_plan", PLAN_CACHE_MODE_STAT_ADAPTIVE_PLAN, false}, {"force_generic_plan", PLAN_CACHE_MODE_FORCE_GENERIC_PLAN, false}, {"force_custom_plan", PLAN_CACHE_MODE_FORCE_CUSTOM_PLAN, false}, {NULL, 0, false} @@ -3866,6 +3867,17 @@ struct config_real ConfigureNamesReal[] = NULL, NULL, NULL }, + { + {"skewed_param_factor", PGC_SUSET, LOGGING_WHEN, + gettext_noop("Sets the skewed factor for parameters of the prepare statement."), + gettext_noop("Use a thredhold to determine whether a skewed parameter exists." + "If true, use the custom plan.") + }, + &skewed_param_factor, + DEFAULT_SKEWED_PARAM_FACTOR, 2.0, 10000.0, + NULL, NULL, NULL + }, + /* End-of-list marker */ { {NULL, 0, 0, NULL, NULL}, NULL, 0.0, 0.0, 0.0, NULL, NULL, NULL diff --git a/src/include/nodes/params.h b/src/include/nodes/params.h index 10c9fc5413..051a12bb1d 100644 --- a/src/include/nodes/params.h +++ b/src/include/nodes/params.h @@ -85,7 +85,13 @@ struct ParseState; * and paramCompileArg is rather arbitrary. */ -#define PARAM_FLAG_CONST 0x0001 /* parameter is constant */ +/* parameter is constant */ +#define PARAM_FLAG_CONST 0x0001 +/* + * The var side of 'var eq param' or 'param eq var' expression + * corresponding to the parameter contains skewed data. + */ +#define PARAM_FLAG_SKEWEDSTAT 0x0002 typedef struct ParamExternData { diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h index 4a154606d2..7e6762de47 100644 --- a/src/include/nodes/primnodes.h +++ b/src/include/nodes/primnodes.h @@ -314,6 +314,12 @@ typedef struct Const */ bool constbyval pg_node_attr(query_jumble_ignore); + /* + * >0 indicates that the constant is extracted from Param. + * Its value is paramid of the Param struct. + */ + int paramid pg_node_attr(query_jumble_ignore); + /* * token location, or -1 if unknown. All constants are tracked as * locations in query jumbling, to be marked as parameters. diff --git a/src/include/optimizer/cost.h b/src/include/optimizer/cost.h index b1c51a4e70..3606e47681 100644 --- a/src/include/optimizer/cost.h +++ b/src/include/optimizer/cost.h @@ -28,6 +28,7 @@ #define DEFAULT_CPU_OPERATOR_COST 0.0025 #define DEFAULT_PARALLEL_TUPLE_COST 0.1 #define DEFAULT_PARALLEL_SETUP_COST 1000.0 +#define DEFAULT_SKEWED_PARAM_FACTOR 10.0 /* defaults for non-Cost parameters */ #define DEFAULT_RECURSIVE_WORKTABLE_FACTOR 10.0 @@ -71,6 +72,7 @@ extern PGDLLIMPORT bool enable_partition_pruning; extern PGDLLIMPORT bool enable_presorted_aggregate; extern PGDLLIMPORT bool enable_async_append; extern PGDLLIMPORT int constraint_exclusion; +extern PGDLLIMPORT double skewed_param_factor; extern double index_pages_fetched(double tuples_fetched, BlockNumber pages, double index_pages, PlannerInfo *root); diff --git a/src/include/utils/plancache.h b/src/include/utils/plancache.h index a90dfdf906..c742c6449c 100644 --- a/src/include/utils/plancache.h +++ b/src/include/utils/plancache.h @@ -30,6 +30,7 @@ struct RawStmt; typedef enum { PLAN_CACHE_MODE_AUTO, + PLAN_CACHE_MODE_STAT_ADAPTIVE_PLAN, PLAN_CACHE_MODE_FORCE_GENERIC_PLAN, PLAN_CACHE_MODE_FORCE_CUSTOM_PLAN, } PlanCacheMode; @@ -124,6 +125,7 @@ typedef struct CachedPlanSource bool is_complete; /* has CompleteCachedPlan been done? */ bool is_saved; /* has CachedPlanSource been "saved"? */ bool is_valid; /* is the query_list currently valid? */ + bool hasSkewedparam; /* see the definition of PARAM FLAG SKEWEDSTAT */ int generation; /* increments each time we create a plan */ /* If CachedPlanSource has been saved, it is a member of a global list */ dlist_node node; /* list link, if is_saved */ diff --git a/src/include/utils/selfuncs.h b/src/include/utils/selfuncs.h index 2fa4c4fc1b..a7532b8d7b 100644 --- a/src/include/utils/selfuncs.h +++ b/src/include/utils/selfuncs.h @@ -184,6 +184,10 @@ extern double var_eq_const(VariableStatData *vardata, Oid oproid, Oid collation, Datum constval, bool constisnull, bool varonleft, bool negate); +extern double var_eq_const_ext(VariableStatData *vardata, + Oid oproid, Oid collation, + Datum constval, bool constisnull, + bool varonleft, bool negate, bool *skewed_stat); extern double var_eq_non_const(VariableStatData *vardata, Oid oproid, Oid collation, Node *other,