Attached is v4, changing how GUCs are picked for inclusion on the query plans. Instead of picking the GUCs based on group and/or explicitly, a new GUC_EXPLAIN flag is used for that.
I went through GUCs defined in guc.c and marked those in QUERY_TUNING* groups accordingly, with the exception of default_statistics_target because that seems somewhat useless without showing the value used to actually analyze the table (and/or columns). I've also included a couple of other GUCs, that I find to be relevant: - parallel_leader_participation - max_parallel_workers_per_gather - max_parallel_workers - search_path - effective_io_concurrency - work_mem - temp_buffers - plan_cache_mode I think this covers the interesting GUCs pretty well, although perhaps I missed something. The one bit that needs fixing is escaping the GUC values when showing them in the plan. Looking at the other places that currently escape stuff, I see they only care about YAML/JSON/XML and leave the regular output unescaped. I was wondering if it's OK with the current format with all GUCs on a single line QUERY PLAN --------------------------------------------------- Seq Scan on t (cost=0.00..54.63 rows=13 width=4) Filter: ('x''y'::text = (a)::text) GUCs: enable_nestloop = 'off', work_mem = '32MB' (3 rows) but I suppose it is, because without the escaping a user can break whatever format we use. So I'll do the same thing, escaping just the structured formats (YAML et al). The question however is whether someone has a better formatting idea? regards -- Tomas Vondra http://www.2ndQuadrant.com PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
diff --git a/contrib/auto_explain/auto_explain.c b/contrib/auto_explain/auto_explain.c index 646cd0d42c..ec44c59b7f 100644 --- a/contrib/auto_explain/auto_explain.c +++ b/contrib/auto_explain/auto_explain.c @@ -28,6 +28,7 @@ static bool auto_explain_log_verbose = false; static bool auto_explain_log_buffers = false; static bool auto_explain_log_triggers = false; static bool auto_explain_log_timing = true; +static bool auto_explain_log_gucs = false; static int auto_explain_log_format = EXPLAIN_FORMAT_TEXT; static int auto_explain_log_level = LOG; static bool auto_explain_log_nested_statements = false; @@ -112,6 +113,17 @@ _PG_init(void) NULL, NULL); + DefineCustomBoolVariable("auto_explain.log_gucs", + "Print modified GUC values.", + NULL, + &auto_explain_log_gucs, + false, + PGC_SUSET, + 0, + NULL, + NULL, + NULL); + DefineCustomBoolVariable("auto_explain.log_verbose", "Use EXPLAIN VERBOSE for plan logging.", NULL, @@ -356,6 +368,7 @@ explain_ExecutorEnd(QueryDesc *queryDesc) es->timing = (es->analyze && auto_explain_log_timing); es->summary = es->analyze; es->format = auto_explain_log_format; + es->gucs = auto_explain_log_gucs; ExplainBeginOutput(es); ExplainQueryText(es, queryDesc); diff --git a/doc/src/sgml/auto-explain.sgml b/doc/src/sgml/auto-explain.sgml index 120b168d45..852c69b7bb 100644 --- a/doc/src/sgml/auto-explain.sgml +++ b/doc/src/sgml/auto-explain.sgml @@ -169,6 +169,23 @@ LOAD 'auto_explain'; </listitem> </varlistentry> + <varlistentry> + <term> + <varname>auto_explain.log_gucs</varname> (<type>boolean</type>) + <indexterm> + <primary><varname>auto_explain.log_gucs</varname> configuration parameter</primary> + </indexterm> + </term> + <listitem> + <para> + <varname>auto_explain.log_gucs</varname> controls whether information + about modified configuration options are logged with the execution + plan. Only options modified at the database, user, client or session + level are considered modified. This parameter is off by default. + </para> + </listitem> + </varlistentry> + <varlistentry> <term> <varname>auto_explain.log_format</varname> (<type>enum</type>) diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c index 094e977fb5..e32497d30d 100644 --- a/src/backend/commands/explain.c +++ b/src/backend/commands/explain.c @@ -30,6 +30,7 @@ #include "storage/bufmgr.h" #include "tcop/tcopprot.h" #include "utils/builtins.h" +#include "utils/guc_tables.h" #include "utils/json.h" #include "utils/lsyscache.h" #include "utils/rel.h" @@ -163,6 +164,8 @@ ExplainQuery(ParseState *pstate, ExplainStmt *stmt, const char *queryString, es->costs = defGetBoolean(opt); else if (strcmp(opt->defname, "buffers") == 0) es->buffers = defGetBoolean(opt); + else if (strcmp(opt->defname, "gucs") == 0) + es->gucs = defGetBoolean(opt); else if (strcmp(opt->defname, "timing") == 0) { timing_set = true; @@ -634,6 +637,41 @@ ExplainPrintPlan(ExplainState *es, QueryDesc *queryDesc) if (IsA(ps, GatherState) &&((Gather *) ps->plan)->invisible) ps = outerPlanState(ps); ExplainNode(ps, NIL, NULL, NULL, es); + + /* + * If requested, include information about GUC parameters that don't + * match the built-in defaults. + */ + if (es->gucs) + { + int i; + int num; + StringInfoData str; + struct config_generic **gucs; + + gucs = get_explain_guc_options(&num); + + for (i = 0; i < num; i++) + { + char *setting; + struct config_generic *conf = gucs[i]; + + if (i == 0) + initStringInfo(&str); + else + appendStringInfoString(&str, ", "); + + setting = GetConfigOptionByName(conf->name, NULL, true); + + if (setting) + appendStringInfo(&str, "%s = '%s'", conf->name, setting); + else + appendStringInfo(&str, "%s = NULL", conf->name); + } + + if (num > 0) + ExplainPropertyText("GUCs", str.data, es); + } } /* diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index 6fe1939881..c2f1bb956d 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -862,7 +862,8 @@ static struct config_bool ConfigureNamesBool[] = { {"enable_seqscan", PGC_USERSET, QUERY_TUNING_METHOD, gettext_noop("Enables the planner's use of sequential-scan plans."), - NULL + NULL, + GUC_EXPLAIN }, &enable_seqscan, true, @@ -871,7 +872,8 @@ static struct config_bool ConfigureNamesBool[] = { {"enable_indexscan", PGC_USERSET, QUERY_TUNING_METHOD, gettext_noop("Enables the planner's use of index-scan plans."), - NULL + NULL, + GUC_EXPLAIN }, &enable_indexscan, true, @@ -880,7 +882,8 @@ static struct config_bool ConfigureNamesBool[] = { {"enable_indexonlyscan", PGC_USERSET, QUERY_TUNING_METHOD, gettext_noop("Enables the planner's use of index-only-scan plans."), - NULL + NULL, + GUC_EXPLAIN }, &enable_indexonlyscan, true, @@ -889,7 +892,8 @@ static struct config_bool ConfigureNamesBool[] = { {"enable_bitmapscan", PGC_USERSET, QUERY_TUNING_METHOD, gettext_noop("Enables the planner's use of bitmap-scan plans."), - NULL + NULL, + GUC_EXPLAIN }, &enable_bitmapscan, true, @@ -898,7 +902,8 @@ static struct config_bool ConfigureNamesBool[] = { {"enable_tidscan", PGC_USERSET, QUERY_TUNING_METHOD, gettext_noop("Enables the planner's use of TID scan plans."), - NULL + NULL, + GUC_EXPLAIN }, &enable_tidscan, true, @@ -907,7 +912,8 @@ static struct config_bool ConfigureNamesBool[] = { {"enable_sort", PGC_USERSET, QUERY_TUNING_METHOD, gettext_noop("Enables the planner's use of explicit sort steps."), - NULL + NULL, + GUC_EXPLAIN }, &enable_sort, true, @@ -916,7 +922,8 @@ static struct config_bool ConfigureNamesBool[] = { {"enable_hashagg", PGC_USERSET, QUERY_TUNING_METHOD, gettext_noop("Enables the planner's use of hashed aggregation plans."), - NULL + NULL, + GUC_EXPLAIN }, &enable_hashagg, true, @@ -925,7 +932,8 @@ static struct config_bool ConfigureNamesBool[] = { {"enable_material", PGC_USERSET, QUERY_TUNING_METHOD, gettext_noop("Enables the planner's use of materialization."), - NULL + NULL, + GUC_EXPLAIN }, &enable_material, true, @@ -934,7 +942,8 @@ static struct config_bool ConfigureNamesBool[] = { {"enable_nestloop", PGC_USERSET, QUERY_TUNING_METHOD, gettext_noop("Enables the planner's use of nested-loop join plans."), - NULL + NULL, + GUC_EXPLAIN }, &enable_nestloop, true, @@ -943,7 +952,8 @@ static struct config_bool ConfigureNamesBool[] = { {"enable_mergejoin", PGC_USERSET, QUERY_TUNING_METHOD, gettext_noop("Enables the planner's use of merge join plans."), - NULL + NULL, + GUC_EXPLAIN }, &enable_mergejoin, true, @@ -952,7 +962,8 @@ static struct config_bool ConfigureNamesBool[] = { {"enable_hashjoin", PGC_USERSET, QUERY_TUNING_METHOD, gettext_noop("Enables the planner's use of hash join plans."), - NULL + NULL, + GUC_EXPLAIN }, &enable_hashjoin, true, @@ -961,7 +972,8 @@ static struct config_bool ConfigureNamesBool[] = { {"enable_gathermerge", PGC_USERSET, QUERY_TUNING_METHOD, gettext_noop("Enables the planner's use of gather merge plans."), - NULL + NULL, + GUC_EXPLAIN }, &enable_gathermerge, true, @@ -970,7 +982,8 @@ static struct config_bool ConfigureNamesBool[] = { {"enable_partitionwise_join", PGC_USERSET, QUERY_TUNING_METHOD, gettext_noop("Enables partitionwise join."), - NULL + NULL, + GUC_EXPLAIN }, &enable_partitionwise_join, false, @@ -979,7 +992,8 @@ static struct config_bool ConfigureNamesBool[] = { {"enable_partitionwise_aggregate", PGC_USERSET, QUERY_TUNING_METHOD, gettext_noop("Enables partitionwise aggregation and grouping."), - NULL + NULL, + GUC_EXPLAIN }, &enable_partitionwise_aggregate, false, @@ -988,7 +1002,8 @@ static struct config_bool ConfigureNamesBool[] = { {"enable_parallel_append", PGC_USERSET, QUERY_TUNING_METHOD, gettext_noop("Enables the planner's use of parallel append plans."), - NULL + NULL, + GUC_EXPLAIN }, &enable_parallel_append, true, @@ -997,7 +1012,8 @@ static struct config_bool ConfigureNamesBool[] = { {"enable_parallel_hash", PGC_USERSET, QUERY_TUNING_METHOD, gettext_noop("Enables the planner's use of parallel hash plans."), - NULL + NULL, + GUC_EXPLAIN }, &enable_parallel_hash, true, @@ -1008,7 +1024,8 @@ static struct config_bool ConfigureNamesBool[] = gettext_noop("Enable plan-time and run-time partition pruning."), gettext_noop("Allows the query planner and executor to compare partition " "bounds to conditions in the query to determine which " - "partitions must be scanned.") + "partitions must be scanned."), + GUC_EXPLAIN }, &enable_partition_pruning, true, @@ -1018,7 +1035,8 @@ static struct config_bool ConfigureNamesBool[] = {"geqo", PGC_USERSET, QUERY_TUNING_GEQO, gettext_noop("Enables genetic query optimization."), gettext_noop("This algorithm attempts to do planning without " - "exhaustive searching.") + "exhaustive searching."), + GUC_EXPLAIN }, &enable_geqo, true, @@ -1591,7 +1609,7 @@ static struct config_bool ConfigureNamesBool[] = "optimize_bounded_sort", PGC_USERSET, QUERY_TUNING_METHOD, gettext_noop("Enable bounded sorting using heap sort."), NULL, - GUC_NOT_IN_SAMPLE + GUC_NOT_IN_SAMPLE | GUC_EXPLAIN }, &optimize_bounded_sort, true, @@ -1782,7 +1800,8 @@ static struct config_bool ConfigureNamesBool[] = { {"parallel_leader_participation", PGC_USERSET, RESOURCES_ASYNCHRONOUS, gettext_noop("Controls whether Gather and Gather Merge also run subplans."), - gettext_noop("Should gather nodes also run subplans, or just gather tuples?") + gettext_noop("Should gather nodes also run subplans, or just gather tuples?"), + GUC_EXPLAIN }, ¶llel_leader_participation, true, @@ -1792,7 +1811,8 @@ static struct config_bool ConfigureNamesBool[] = { {"jit", PGC_USERSET, QUERY_TUNING_OTHER, gettext_noop("Allow JIT compilation."), - NULL + NULL, + GUC_EXPLAIN }, &jit_enabled, true, @@ -1921,7 +1941,8 @@ static struct config_int ConfigureNamesInt[] = "are not collapsed."), gettext_noop("The planner will merge subqueries into upper " "queries if the resulting FROM list would have no more than " - "this many items.") + "this many items."), + GUC_EXPLAIN }, &from_collapse_limit, 8, 1, INT_MAX, @@ -1933,7 +1954,8 @@ static struct config_int ConfigureNamesInt[] = "constructs are not flattened."), gettext_noop("The planner will flatten explicit JOIN " "constructs into lists of FROM items whenever a " - "list of no more than this many items would result.") + "list of no more than this many items would result."), + GUC_EXPLAIN }, &join_collapse_limit, 8, 1, INT_MAX, @@ -1942,7 +1964,8 @@ static struct config_int ConfigureNamesInt[] = { {"geqo_threshold", PGC_USERSET, QUERY_TUNING_GEQO, gettext_noop("Sets the threshold of FROM items beyond which GEQO is used."), - NULL + NULL, + GUC_EXPLAIN }, &geqo_threshold, 12, 2, INT_MAX, @@ -1951,7 +1974,8 @@ static struct config_int ConfigureNamesInt[] = { {"geqo_effort", PGC_USERSET, QUERY_TUNING_GEQO, gettext_noop("GEQO: effort is used to set the default for other GEQO parameters."), - NULL + NULL, + GUC_EXPLAIN }, &Geqo_effort, DEFAULT_GEQO_EFFORT, MIN_GEQO_EFFORT, MAX_GEQO_EFFORT, @@ -1960,7 +1984,8 @@ static struct config_int ConfigureNamesInt[] = { {"geqo_pool_size", PGC_USERSET, QUERY_TUNING_GEQO, gettext_noop("GEQO: number of individuals in the population."), - gettext_noop("Zero selects a suitable default value.") + gettext_noop("Zero selects a suitable default value."), + GUC_EXPLAIN }, &Geqo_pool_size, 0, 0, INT_MAX, @@ -1969,7 +1994,8 @@ static struct config_int ConfigureNamesInt[] = { {"geqo_generations", PGC_USERSET, QUERY_TUNING_GEQO, gettext_noop("GEQO: number of iterations of the algorithm."), - gettext_noop("Zero selects a suitable default value.") + gettext_noop("Zero selects a suitable default value."), + GUC_EXPLAIN }, &Geqo_generations, 0, 0, INT_MAX, @@ -2083,7 +2109,7 @@ static struct config_int ConfigureNamesInt[] = {"temp_buffers", PGC_USERSET, RESOURCES_MEM, gettext_noop("Sets the maximum number of temporary buffers used by each session."), NULL, - GUC_UNIT_BLOCKS + GUC_UNIT_BLOCKS | GUC_EXPLAIN }, &num_temp_buffers, 1024, 100, INT_MAX / 2, @@ -2150,7 +2176,7 @@ static struct config_int ConfigureNamesInt[] = gettext_noop("This much memory can be used by each internal " "sort operation and hash table before switching to " "temporary disk files."), - GUC_UNIT_KB + GUC_UNIT_KB | GUC_EXPLAIN }, &work_mem, 4096, 64, MAX_KILOBYTES, @@ -2701,7 +2727,8 @@ static struct config_int ConfigureNamesInt[] = PGC_USERSET, RESOURCES_ASYNCHRONOUS, gettext_noop("Number of simultaneous requests that can be handled efficiently by the disk subsystem."), - gettext_noop("For RAID arrays, this should be approximately the number of drive spindles in the array.") + gettext_noop("For RAID arrays, this should be approximately the number of drive spindles in the array."), + GUC_EXPLAIN }, &effective_io_concurrency, #ifdef USE_PREFETCH @@ -2946,7 +2973,8 @@ static struct config_int ConfigureNamesInt[] = { {"max_parallel_workers_per_gather", PGC_USERSET, RESOURCES_ASYNCHRONOUS, gettext_noop("Sets the maximum number of parallel processes per executor node."), - NULL + NULL, + GUC_EXPLAIN }, &max_parallel_workers_per_gather, 2, 0, MAX_PARALLEL_WORKER_LIMIT, @@ -2956,7 +2984,8 @@ static struct config_int ConfigureNamesInt[] = { {"max_parallel_workers", PGC_USERSET, RESOURCES_ASYNCHRONOUS, gettext_noop("Sets the maximum number of parallel workers that can be active at one time."), - NULL + NULL, + GUC_EXPLAIN }, &max_parallel_workers, 8, 0, MAX_PARALLEL_WORKER_LIMIT, @@ -3046,7 +3075,7 @@ static struct config_int ConfigureNamesInt[] = gettext_noop("Sets the planner's assumption about the total size of the data caches."), gettext_noop("That is, the total size of the caches (kernel cache and shared buffers) used for PostgreSQL data files. " "This is measured in disk pages, which are normally 8 kB each."), - GUC_UNIT_BLOCKS, + GUC_UNIT_BLOCKS | GUC_EXPLAIN, }, &effective_cache_size, DEFAULT_EFFECTIVE_CACHE_SIZE, 1, INT_MAX, @@ -3057,7 +3086,7 @@ static struct config_int ConfigureNamesInt[] = {"min_parallel_table_scan_size", PGC_USERSET, QUERY_TUNING_COST, gettext_noop("Sets the minimum amount of table data for a parallel scan."), gettext_noop("If the planner estimates that it will read a number of table pages too small to reach this limit, a parallel scan will not be considered."), - GUC_UNIT_BLOCKS, + GUC_UNIT_BLOCKS | GUC_EXPLAIN, }, &min_parallel_table_scan_size, (8 * 1024 * 1024) / BLCKSZ, 0, INT_MAX / 3, @@ -3068,7 +3097,7 @@ static struct config_int ConfigureNamesInt[] = {"min_parallel_index_scan_size", PGC_USERSET, QUERY_TUNING_COST, gettext_noop("Sets the minimum amount of index data for a parallel scan."), gettext_noop("If the planner estimates that it will read a number of index pages too small to reach this limit, a parallel scan will not be considered."), - GUC_UNIT_BLOCKS, + GUC_UNIT_BLOCKS | GUC_EXPLAIN, }, &min_parallel_index_scan_size, (512 * 1024) / BLCKSZ, 0, INT_MAX / 3, @@ -3133,7 +3162,8 @@ static struct config_real ConfigureNamesReal[] = {"seq_page_cost", PGC_USERSET, QUERY_TUNING_COST, gettext_noop("Sets the planner's estimate of the cost of a " "sequentially fetched disk page."), - NULL + NULL, + GUC_EXPLAIN }, &seq_page_cost, DEFAULT_SEQ_PAGE_COST, 0, DBL_MAX, @@ -3143,7 +3173,8 @@ static struct config_real ConfigureNamesReal[] = {"random_page_cost", PGC_USERSET, QUERY_TUNING_COST, gettext_noop("Sets the planner's estimate of the cost of a " "nonsequentially fetched disk page."), - NULL + NULL, + GUC_EXPLAIN }, &random_page_cost, DEFAULT_RANDOM_PAGE_COST, 0, DBL_MAX, @@ -3153,7 +3184,8 @@ static struct config_real ConfigureNamesReal[] = {"cpu_tuple_cost", PGC_USERSET, QUERY_TUNING_COST, gettext_noop("Sets the planner's estimate of the cost of " "processing each tuple (row)."), - NULL + NULL, + GUC_EXPLAIN }, &cpu_tuple_cost, DEFAULT_CPU_TUPLE_COST, 0, DBL_MAX, @@ -3163,7 +3195,8 @@ static struct config_real ConfigureNamesReal[] = {"cpu_index_tuple_cost", PGC_USERSET, QUERY_TUNING_COST, gettext_noop("Sets the planner's estimate of the cost of " "processing each index entry during an index scan."), - NULL + NULL, + GUC_EXPLAIN }, &cpu_index_tuple_cost, DEFAULT_CPU_INDEX_TUPLE_COST, 0, DBL_MAX, @@ -3173,7 +3206,8 @@ static struct config_real ConfigureNamesReal[] = {"cpu_operator_cost", PGC_USERSET, QUERY_TUNING_COST, gettext_noop("Sets the planner's estimate of the cost of " "processing each operator or function call."), - NULL + NULL, + GUC_EXPLAIN }, &cpu_operator_cost, DEFAULT_CPU_OPERATOR_COST, 0, DBL_MAX, @@ -3183,7 +3217,8 @@ static struct config_real ConfigureNamesReal[] = {"parallel_tuple_cost", PGC_USERSET, QUERY_TUNING_COST, gettext_noop("Sets the planner's estimate of the cost of " "passing each tuple (row) from worker to master backend."), - NULL + NULL, + GUC_EXPLAIN }, ¶llel_tuple_cost, DEFAULT_PARALLEL_TUPLE_COST, 0, DBL_MAX, @@ -3193,7 +3228,8 @@ static struct config_real ConfigureNamesReal[] = {"parallel_setup_cost", PGC_USERSET, QUERY_TUNING_COST, gettext_noop("Sets the planner's estimate of the cost of " "starting up worker processes for parallel query."), - NULL + NULL, + GUC_EXPLAIN }, ¶llel_setup_cost, DEFAULT_PARALLEL_SETUP_COST, 0, DBL_MAX, @@ -3203,7 +3239,8 @@ static struct config_real ConfigureNamesReal[] = { {"jit_above_cost", PGC_USERSET, QUERY_TUNING_COST, gettext_noop("Perform JIT compilation if query is more expensive."), - gettext_noop("-1 disables JIT compilation.") + gettext_noop("-1 disables JIT compilation."), + GUC_EXPLAIN }, &jit_above_cost, 100000, -1, DBL_MAX, @@ -3213,7 +3250,8 @@ static struct config_real ConfigureNamesReal[] = { {"jit_optimize_above_cost", PGC_USERSET, QUERY_TUNING_COST, gettext_noop("Optimize JITed functions if query is more expensive."), - gettext_noop("-1 disables optimization.") + gettext_noop("-1 disables optimization."), + GUC_EXPLAIN }, &jit_optimize_above_cost, 500000, -1, DBL_MAX, @@ -3223,7 +3261,8 @@ static struct config_real ConfigureNamesReal[] = { {"jit_inline_above_cost", PGC_USERSET, QUERY_TUNING_COST, gettext_noop("Perform JIT inlining if query is more expensive."), - gettext_noop("-1 disables inlining.") + gettext_noop("-1 disables inlining."), + GUC_EXPLAIN }, &jit_inline_above_cost, 500000, -1, DBL_MAX, @@ -3234,7 +3273,8 @@ static struct config_real ConfigureNamesReal[] = {"cursor_tuple_fraction", PGC_USERSET, QUERY_TUNING_OTHER, gettext_noop("Sets the planner's estimate of the fraction of " "a cursor's rows that will be retrieved."), - NULL + NULL, + GUC_EXPLAIN }, &cursor_tuple_fraction, DEFAULT_CURSOR_TUPLE_FRACTION, 0.0, 1.0, @@ -3244,7 +3284,8 @@ static struct config_real ConfigureNamesReal[] = { {"geqo_selection_bias", PGC_USERSET, QUERY_TUNING_GEQO, gettext_noop("GEQO: selective pressure within the population."), - NULL + NULL, + GUC_EXPLAIN }, &Geqo_selection_bias, DEFAULT_GEQO_SELECTION_BIAS, @@ -3254,7 +3295,8 @@ static struct config_real ConfigureNamesReal[] = { {"geqo_seed", PGC_USERSET, QUERY_TUNING_GEQO, gettext_noop("GEQO: seed for random path selection."), - NULL + NULL, + GUC_EXPLAIN }, &Geqo_seed, 0.0, 0.0, 1.0, @@ -3669,7 +3711,7 @@ static struct config_string ConfigureNamesString[] = {"search_path", PGC_USERSET, CLIENT_CONN_STATEMENT, gettext_noop("Sets the schema search order for names that are not schema-qualified."), NULL, - GUC_LIST_INPUT | GUC_LIST_QUOTE + GUC_LIST_INPUT | GUC_LIST_QUOTE | GUC_EXPLAIN }, &namespace_search_path, "\"$user\", public", @@ -4122,7 +4164,8 @@ static struct config_enum ConfigureNamesEnum[] = {"constraint_exclusion", PGC_USERSET, QUERY_TUNING_OTHER, gettext_noop("Enables the planner to use constraints to optimize queries."), gettext_noop("Table scans will be skipped if their constraints" - " guarantee that no rows match the query.") + " guarantee that no rows match the query."), + GUC_EXPLAIN }, &constraint_exclusion, CONSTRAINT_EXCLUSION_PARTITION, constraint_exclusion_options, @@ -4348,7 +4391,8 @@ static struct config_enum ConfigureNamesEnum[] = { {"force_parallel_mode", PGC_USERSET, QUERY_TUNING_OTHER, gettext_noop("Forces use of parallel query facilities."), - gettext_noop("If possible, run query using a parallel worker and with parallel restrictions.") + gettext_noop("If possible, run query using a parallel worker and with parallel restrictions."), + GUC_EXPLAIN }, &force_parallel_mode, FORCE_PARALLEL_OFF, force_parallel_mode_options, @@ -4372,7 +4416,8 @@ static struct config_enum ConfigureNamesEnum[] = gettext_noop("Controls the planner's selection of custom or generic plan."), gettext_noop("Prepared statements can have custom and generic plans, and the planner " "will attempt to choose which is better. This can be set to override " - "the default behavior.") + "the default behavior."), + GUC_EXPLAIN }, &plan_cache_mode, PLAN_CACHE_MODE_AUTO, plan_cache_mode_options, @@ -8556,6 +8601,86 @@ ShowAllGUCConfig(DestReceiver *dest) end_tup_output(tstate); } +struct config_generic ** +get_explain_guc_options(int *num) +{ + int i; + struct config_generic **result; + + *num = 0; + result = palloc(sizeof(struct config_generic *) * num_guc_variables); + + for (i = 0; i < num_guc_variables; i++) + { + bool modified; + struct config_generic *conf = guc_variables[i]; + + /* return only options visible to the user */ + if ((conf->flags & GUC_NO_SHOW_ALL) || + ((conf->flags & GUC_SUPERUSER_ONLY) && + !is_member_of_role(GetUserId(), DEFAULT_ROLE_READ_ALL_SETTINGS))) + continue; + + /* only parameters explicitly marked for inclusion in explain */ + if (!(conf->flags & GUC_EXPLAIN)) + continue; + + /* return only options that were modified (w.r.t. config file) */ + modified = false; + + switch (conf->vartype) + { + case PGC_BOOL: + { + struct config_bool *lconf = (struct config_bool *) conf; + modified = (lconf->boot_val != *(lconf->variable)); + } + break; + + case PGC_INT: + { + struct config_int *lconf = (struct config_int *) conf; + modified = (lconf->boot_val != *(lconf->variable)); + } + break; + + case PGC_REAL: + { + struct config_real *lconf = (struct config_real *) conf; + modified = (lconf->boot_val != *(lconf->variable)); + } + break; + + case PGC_STRING: + { + struct config_string *lconf = (struct config_string *) conf; + modified = (strcmp(lconf->boot_val, *(lconf->variable)) != 0); + } + break; + + case PGC_ENUM: + { + struct config_enum *lconf = (struct config_enum *) conf; + modified = (lconf->boot_val != *(lconf->variable)); + } + break; + + default: + elog(ERROR, "unexcpected GUC type: %d", conf->vartype); + } + + /* skip GUC variables that match the built-in default */ + if (!modified) + continue; + + /* assign to the values array */ + result[*num] = conf; + *num = *num + 1; + } + + return result; +} + /* * Return GUC variable value by name; optionally return canonical form of * name. If the GUC is unset, then throw an error unless missing_ok is true, diff --git a/src/include/commands/explain.h b/src/include/commands/explain.h index d3f70fda08..05afca22aa 100644 --- a/src/include/commands/explain.h +++ b/src/include/commands/explain.h @@ -35,6 +35,7 @@ typedef struct ExplainState bool buffers; /* print buffer usage */ bool timing; /* print detailed node timing */ bool summary; /* print total planning and execution timing */ + bool gucs; /* print modified GUCs */ ExplainFormat format; /* output format */ /* state for output formatting --- not reset for each new plan tree */ int indent; /* current indentation level */ diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h index 64457c792a..f316320efc 100644 --- a/src/include/utils/guc.h +++ b/src/include/utils/guc.h @@ -227,6 +227,8 @@ typedef enum #define GUC_UNIT_MIN 0x30000 /* value is in minutes */ #define GUC_UNIT_TIME 0xF0000 /* mask for time-related units */ +#define GUC_EXPLAIN 0x100000 /* include in explain */ + #define GUC_UNIT (GUC_UNIT_MEMORY | GUC_UNIT_TIME) diff --git a/src/include/utils/guc_tables.h b/src/include/utils/guc_tables.h index 6f9fdb6a5f..641cb29fbe 100644 --- a/src/include/utils/guc_tables.h +++ b/src/include/utils/guc_tables.h @@ -267,5 +267,6 @@ extern void build_guc_variables(void); extern const char *config_enum_lookup_by_value(struct config_enum *record, int val); extern bool config_enum_lookup_by_name(struct config_enum *record, const char *value, int *retval); +extern struct config_generic **get_explain_guc_options(int *num); #endif /* GUC_TABLES_H */