diff --git a/contrib/auto_explain/auto_explain.c b/contrib/auto_explain/auto_explain.c index 7b22927674..edc50f9368 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_settings = 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_settings", + "Log modified configuration parameters affecting query planning.", + NULL, + &auto_explain_log_settings, + 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->settings = auto_explain_log_settings; ExplainBeginOutput(es); ExplainQueryText(es, queryDesc); diff --git a/doc/src/sgml/auto-explain.sgml b/doc/src/sgml/auto-explain.sgml index 120b168d45..296ae2de80 100644 --- a/doc/src/sgml/auto-explain.sgml +++ b/doc/src/sgml/auto-explain.sgml @@ -169,6 +169,24 @@ LOAD 'auto_explain'; + + + auto_explain.log_settings (boolean) + + auto_explain.log_settings configuration parameter + + + + + auto_explain.log_settings controls whether information + about modified configuration options affecting query planning are logged + with the execution plan. Only options affecting query planning with value + different from the built-in default value are considered. This parameter is + off by default. Only superusers can change this setting. + + + + auto_explain.log_format (enum) diff --git a/doc/src/sgml/ref/explain.sgml b/doc/src/sgml/ref/explain.sgml index 8dc0d7038a..385d10411f 100644 --- a/doc/src/sgml/ref/explain.sgml +++ b/doc/src/sgml/ref/explain.sgml @@ -39,6 +39,7 @@ EXPLAIN [ ANALYZE ] [ VERBOSE ] statementboolean ] VERBOSE [ boolean ] COSTS [ boolean ] + SETTINGS [ boolean ] BUFFERS [ boolean ] TIMING [ boolean ] SUMMARY [ boolean ] @@ -152,6 +153,17 @@ ROLLBACK; + + SETTINGS + + + Include information on configuration parameters. Specifically, include + options affecting query planning with value different from the built-in + default value. This parameter defaults to FALSE. + + + + BUFFERS diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c index 1831ea81cf..2a289b8b94 100644 --- a/src/backend/commands/explain.c +++ b/src/backend/commands/explain.c @@ -29,6 +29,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" @@ -162,6 +163,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, "settings") == 0) + es->settings = defGetBoolean(opt); else if (strcmp(opt->defname, "timing") == 0) { timing_set = true; @@ -596,6 +599,72 @@ ExplainOnePlan(PlannedStmt *plannedstmt, IntoClause *into, ExplainState *es, ExplainCloseGroup("Query", NULL, true, es); } +/* + * ExplainPrintSettings - + * Print summary of modified settings affecting query planning. + */ +static void +ExplainPrintSettings(ExplainState *es) +{ + int num; + struct config_generic **gucs; + + /* bail out if GUC information not requested */ + if (!es->settings) + return; + + gucs = get_explain_guc_options(&num); + + /* also bail out of there are no options */ + if (!num) + return; + + if (es->format != EXPLAIN_FORMAT_TEXT) + { + int i; + + ExplainOpenGroup("Settings", "Settings", false, es); + + for (i = 0; i < num; i++) + { + char *setting; + struct config_generic *conf = gucs[i]; + + setting = GetConfigOptionByName(conf->name, NULL, true); + + ExplainPropertyText(conf->name, setting, es); + } + + ExplainCloseGroup("Settings", "Settings", false, es); + } + else + { + int i; + StringInfoData str; + + initStringInfo(&str); + + for (i = 0; i < num; i++) + { + char *setting; + struct config_generic *conf = gucs[i]; + + if (i > 0) + 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("Settings", str.data, es); + } +} + /* * ExplainPrintPlan - * convert a QueryDesc's plan tree to text and append it to es->str @@ -633,6 +702,12 @@ 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 with values + * that don't match the built-in defaults. + */ + ExplainPrintSettings(es); } /* diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index aa564d153a..39f844ebc5 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -881,7 +881,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, @@ -890,7 +891,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, @@ -899,7 +901,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, @@ -908,7 +911,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, @@ -917,7 +921,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, @@ -926,7 +931,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, @@ -935,7 +941,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, @@ -944,7 +951,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, @@ -953,7 +961,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, @@ -962,7 +971,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, @@ -971,7 +981,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, @@ -980,7 +991,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, @@ -989,7 +1001,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, @@ -998,7 +1011,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, @@ -1007,7 +1021,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, @@ -1016,7 +1031,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, @@ -1027,7 +1043,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, @@ -1037,7 +1054,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, @@ -1625,7 +1643,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, @@ -1816,7 +1834,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, @@ -1826,7 +1845,8 @@ static struct config_bool ConfigureNamesBool[] = { {"jit", PGC_USERSET, QUERY_TUNING_OTHER, gettext_noop("Allow JIT compilation."), - NULL + NULL, + GUC_EXPLAIN }, &jit_enabled, true, @@ -1955,7 +1975,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, @@ -1967,7 +1988,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, @@ -1976,7 +1998,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, @@ -1985,7 +2008,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, @@ -1994,7 +2018,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, @@ -2003,7 +2028,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, @@ -2117,7 +2143,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, @@ -2184,7 +2210,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, @@ -2713,7 +2739,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 @@ -2958,7 +2985,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, @@ -2968,7 +2996,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, @@ -3058,7 +3087,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, @@ -3069,7 +3098,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, @@ -3080,7 +3109,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, @@ -3145,7 +3174,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, @@ -3155,7 +3185,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, @@ -3165,7 +3196,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, @@ -3175,7 +3207,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, @@ -3185,7 +3218,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, @@ -3195,7 +3229,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, @@ -3205,7 +3240,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, @@ -3215,7 +3251,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, @@ -3225,7 +3262,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, @@ -3235,7 +3273,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, @@ -3246,7 +3285,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, @@ -3256,7 +3296,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, @@ -3266,7 +3307,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, @@ -3714,7 +3756,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", @@ -4167,7 +4209,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, @@ -4403,7 +4446,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, @@ -4427,7 +4471,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, @@ -4487,6 +4532,7 @@ static struct config_generic **guc_variables; /* Current number of variables contained in the vector */ static int num_guc_variables; +static int num_guc_explain_variables; /* Vector capacity */ static int size_guc_variables; @@ -4751,6 +4797,7 @@ build_guc_variables(void) { int size_vars; int num_vars = 0; + int num_explain_vars = 0; struct config_generic **guc_vars; int i; @@ -4761,6 +4808,9 @@ build_guc_variables(void) /* Rather than requiring vartype to be filled in by hand, do this: */ conf->gen.vartype = PGC_BOOL; num_vars++; + + if (conf->gen.flags & GUC_EXPLAIN) + num_explain_vars++; } for (i = 0; ConfigureNamesInt[i].gen.name; i++) @@ -4769,6 +4819,9 @@ build_guc_variables(void) conf->gen.vartype = PGC_INT; num_vars++; + + if (conf->gen.flags & GUC_EXPLAIN) + num_explain_vars++; } for (i = 0; ConfigureNamesReal[i].gen.name; i++) @@ -4777,6 +4830,9 @@ build_guc_variables(void) conf->gen.vartype = PGC_REAL; num_vars++; + + if (conf->gen.flags & GUC_EXPLAIN) + num_explain_vars++; } for (i = 0; ConfigureNamesString[i].gen.name; i++) @@ -4785,6 +4841,9 @@ build_guc_variables(void) conf->gen.vartype = PGC_STRING; num_vars++; + + if (conf->gen.flags & GUC_EXPLAIN) + num_explain_vars++; } for (i = 0; ConfigureNamesEnum[i].gen.name; i++) @@ -4793,6 +4852,9 @@ build_guc_variables(void) conf->gen.vartype = PGC_ENUM; num_vars++; + + if (conf->gen.flags & GUC_EXPLAIN) + num_explain_vars++; } /* @@ -4824,6 +4886,7 @@ build_guc_variables(void) free(guc_variables); guc_variables = guc_vars; num_guc_variables = num_vars; + num_guc_explain_variables = num_explain_vars; size_guc_variables = size_vars; qsort((void *) guc_variables, num_guc_variables, sizeof(struct config_generic *), guc_var_compare); @@ -8774,6 +8837,99 @@ ShowAllGUCConfig(DestReceiver *dest) end_tup_output(tstate); } +/* + * Returns an array of modified GUC options to show in EXPLAIN. Only options + * related to query planning (marked with GUC_EXPLAIN), with values different + * from built-in defaults. + */ +struct config_generic ** +get_explain_guc_options(int *num) +{ + int i; + struct config_generic **result; + + *num = 0; + + /* + * Allocate enough space to fit all GUC_EXPLAIN options. We may not + * need all the space, but there are fairly few such options so we + * don't waste a lot of memory. + */ + result = palloc(sizeof(struct config_generic *) * num_guc_explain_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; + + Assert(*num <= num_guc_explain_variables); + } + + 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 e8854db459..db48f29501 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 settings; /* print modified settings */ 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 2712a774f7..f73edc3edc 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 a0970b2e1c..2a74b30b2f 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 */