Here is a patch to provide default gucs for EXPLAIN options. I have two goals with this patch. The first is that I personally *always* want BUFFERS turned on, so this would allow me to do it without typing it every time.
The second is it would make it easier to change the actual default for settings if we choose to do so because users would be able to switch it back if they prefer. The patch is based off of a995b371ae. -- Vik Fearing
diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index a2694e548a..ca2769b02a 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -5308,6 +5308,141 @@ ANY <replaceable class="parameter">num_sync</replaceable> ( <replaceable class=" </variablelist> </sect2> + + <sect2 id="runtime-config-query-explain"> + <title>Default EXPLAIN options</title> + + <variablelist> + + <varlistentry id="guc-default-explain-analyze" xreflabel="default_explain_analyze"> + <term><varname>default_explain_analyze</varname> (<type>boolean</type>) + <indexterm> + <primary><varname>default_explain_analyze</varname> configuration parameter</primary> + </indexterm> + </term> + <listitem> + <para> + Sets the default <command>ANALYZE</command> option for <command>EXPLAIN</command>. + The default is off. + </para> + </listitem> + </varlistentry> + + <varlistentry id="guc-default-explain-buffers" xreflabel="default_explain_buffers"> + <term><varname>default_explain_buffers</varname> (<type>boolean</type>) + <indexterm> + <primary><varname>default_explain_buffers</varname> configuration parameter</primary> + </indexterm> + </term> + <listitem> + <para> + Sets the default <command>BUFFERS</command> option for <command>EXPLAIN</command>. + The default is off. + </para> + </listitem> + </varlistentry> + + <varlistentry id="guc-default-explain-cost" xreflabel="default_explain_cost"> + <term><varname>default_explain_cost</varname> (<type>boolean</type>) + <indexterm> + <primary><varname>default_explain_cost</varname> configuration parameter</primary> + </indexterm> + </term> + <listitem> + <para> + Sets the default <command>COST</command> option for <command>EXPLAIN</command>. + The default is on. + </para> + </listitem> + </varlistentry> + + <varlistentry id="guc-default-explain-format" xreflabel="default_explain_format"> + <term><varname>default_explain_format</varname> (<type>text</type>) + <indexterm> + <primary><varname>default_explain_format</varname> configuration parameter</primary> + </indexterm> + </term> + <listitem> + <para> + Sets the default <command>FORMAT</command> option for <command>EXPLAIN</command>. + The default is text. + </para> + </listitem> + </varlistentry> + + <varlistentry id="guc-default-explain-settings" xreflabel="default_explain_settings"> + <term><varname>default_explain_settings</varname> (<type>boolean</type>) + <indexterm> + <primary><varname>default_explain_settings</varname> configuration parameter</primary> + </indexterm> + </term> + <listitem> + <para> + Sets the default <command>SETTINGS</command> option for <command>EXPLAIN</command>. + The default is off. + </para> + </listitem> + </varlistentry> + + <varlistentry id="guc-default-explain-summary" xreflabel="default_explain_summary"> + <term><varname>default_explain_summary</varname> (<type>boolean</type>) + <indexterm> + <primary><varname>default_explain_summary</varname> configuration parameter</primary> + </indexterm> + </term> + <listitem> + <para> + Sets the default <command>SUMMARY</command> option for <command>EXPLAIN</command>. + The default is off. + </para> + </listitem> + </varlistentry> + + <varlistentry id="guc-default-explain-timing" xreflabel="default_explain_timing"> + <term><varname>default_explain_timing</varname> (<type>boolean</type>) + <indexterm> + <primary><varname>default_explain_timing</varname> configuration parameter</primary> + </indexterm> + </term> + <listitem> + <para> + Sets the default <command>TIMING</command> option for <command>EXPLAIN</command>. + The default is on. + </para> + </listitem> + </varlistentry> + + <varlistentry id="guc-default-explain-verbose" xreflabel="default_explain_verbose"> + <term><varname>default_explain_verbose</varname> (<type>boolean</type>) + <indexterm> + <primary><varname>default_explain_verbose</varname> configuration parameter</primary> + </indexterm> + </term> + <listitem> + <para> + Sets the default <command>VERBOSE</command> option for <command>EXPLAIN</command>. + The default is off. + </para> + </listitem> + </varlistentry> + + <varlistentry id="guc-default-explain-wal" xreflabel="default_explain_wal"> + <term><varname>default_explain_wal</varname> (<type>boolean</type>) + <indexterm> + <primary><varname>default_explain_wal</varname> configuration parameter</primary> + </indexterm> + </term> + <listitem> + <para> + Sets the default <command>WAL</command> option for <command>EXPLAIN</command>. + The default is off. + </para> + </listitem> + </varlistentry> + + </variablelist> + </sect2> + <sect2 id="runtime-config-query-other"> <title>Other Planner Options</title> diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c index efd7201d61..974de580bb 100644 --- a/src/backend/commands/explain.c +++ b/src/backend/commands/explain.c @@ -17,6 +17,7 @@ #include "catalog/pg_type.h" #include "commands/createas.h" #include "commands/defrem.h" +#include "commands/explain.h" #include "commands/prepare.h" #include "executor/nodeHash.h" #include "foreign/fdwapi.h" @@ -39,6 +40,26 @@ #include "utils/typcache.h" #include "utils/xml.h" +/* + * User-tweakable parameters + */ +bool default_explain_analyze = false; +bool default_explain_buffers = false; +bool default_explain_costs = true; +int default_explain_format = EXPLAIN_FORMAT_TEXT; +bool default_explain_settings = false; +bool default_explain_summary = false; +bool default_explain_timing = true; +bool default_explain_verbose = false; +bool default_explain_wal = false; + +const struct config_enum_entry explain_format_options[] = { + {"text", EXPLAIN_FORMAT_TEXT, false}, + {"xml", EXPLAIN_FORMAT_XML, false}, + {"json", EXPLAIN_FORMAT_JSON, false}, + {"yaml", EXPLAIN_FORMAT_YAML, false}, + {NULL, 0, false} +}; /* Hook for plugins to get control in ExplainOneQuery() */ ExplainOneQuery_hook_type ExplainOneQuery_hook = NULL; @@ -164,8 +185,26 @@ ExplainQuery(ParseState *pstate, ExplainStmt *stmt, TupOutputState *tstate; List *rewritten; ListCell *lc; - bool timing_set = false; - bool summary_set = false; + DefElem *danalyze = NULL; + DefElem *dbuffers = NULL; + DefElem *dcosts = NULL; + DefElem *dformat = NULL; + DefElem *dsettings = NULL; + DefElem *dsummary = NULL; + DefElem *dtiming = NULL; + DefElem *dverbose = NULL; + DefElem *dwal = NULL; + + /* Set defaults. */ + es->analyze = default_explain_analyze; + es->buffers = default_explain_buffers; + es->costs = default_explain_costs; + es->format = default_explain_format; + es->settings = default_explain_settings; + es->summary = default_explain_summary; + es->timing = default_explain_timing; + es->verbose = default_explain_verbose; + es->wal = default_explain_wal; /* Parse options list. */ foreach(lc, stmt->options) @@ -173,30 +212,46 @@ ExplainQuery(ParseState *pstate, ExplainStmt *stmt, DefElem *opt = (DefElem *) lfirst(lc); if (strcmp(opt->defname, "analyze") == 0) + { + if (danalyze) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"), + parser_errposition(pstate, opt->location))); + danalyze = opt; es->analyze = defGetBoolean(opt); - else if (strcmp(opt->defname, "verbose") == 0) - es->verbose = defGetBoolean(opt); - else if (strcmp(opt->defname, "costs") == 0) - es->costs = defGetBoolean(opt); + } else if (strcmp(opt->defname, "buffers") == 0) + { + if (dbuffers) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"), + parser_errposition(pstate, opt->location))); + dbuffers = opt; es->buffers = defGetBoolean(opt); - else if (strcmp(opt->defname, "wal") == 0) - es->wal = defGetBoolean(opt); - else if (strcmp(opt->defname, "settings") == 0) - es->settings = defGetBoolean(opt); - else if (strcmp(opt->defname, "timing") == 0) - { - timing_set = true; - es->timing = defGetBoolean(opt); } - else if (strcmp(opt->defname, "summary") == 0) + else if (strcmp(opt->defname, "costs") == 0) { - summary_set = true; - es->summary = defGetBoolean(opt); + if (dcosts) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"), + parser_errposition(pstate, opt->location))); + dcosts = opt; + es->costs = defGetBoolean(opt); } else if (strcmp(opt->defname, "format") == 0) { - char *p = defGetString(opt); + char *p; + + if (dformat) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"), + parser_errposition(pstate, opt->location))); + dformat = opt; + p = defGetString(opt); if (strcmp(p, "text") == 0) es->format = EXPLAIN_FORMAT_TEXT; @@ -213,6 +268,56 @@ ExplainQuery(ParseState *pstate, ExplainStmt *stmt, opt->defname, p), parser_errposition(pstate, opt->location))); } + else if (strcmp(opt->defname, "settings") == 0) + { + if (dsettings) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"), + parser_errposition(pstate, opt->location))); + dsettings = opt; + es->settings = defGetBoolean(opt); + } + else if (strcmp(opt->defname, "summary") == 0) + { + if (dsummary) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"), + parser_errposition(pstate, opt->location))); + dsummary = opt; + es->summary = defGetBoolean(opt); + } + else if (strcmp(opt->defname, "timing") == 0) + { + if (dtiming) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"), + parser_errposition(pstate, opt->location))); + dtiming = opt; + es->timing = defGetBoolean(opt); + } + else if (strcmp(opt->defname, "verbose") == 0) + { + if (dverbose) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"), + parser_errposition(pstate, opt->location))); + dverbose = opt; + es->verbose = defGetBoolean(opt); + } + else if (strcmp(opt->defname, "wal") == 0) + { + if (dwal) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"), + parser_errposition(pstate, opt->location))); + dwal = opt; + es->wal = defGetBoolean(opt); + } else ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), @@ -221,27 +326,41 @@ ExplainQuery(ParseState *pstate, ExplainStmt *stmt, parser_errposition(pstate, opt->location))); } + /* + * If ANALYZE wasn't explicitly set and isn't on by default, turn off some + * settings that aren't also explicitly set. This is so we can raise errors + * on incompatible options. + */ + if (!danalyze && !es->analyze) + { + if (!dbuffers) + es->buffers = false; + if (!dtiming) + es->timing = false; + if (!dwal) + es->wal = false; + } + + /* check that BUFFERS is used with EXPLAIN ANALYZE */ if (es->buffers && !es->analyze) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("EXPLAIN option BUFFERS requires ANALYZE"))); - if (es->wal && !es->analyze) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("EXPLAIN option WAL requires ANALYZE"))); - - /* if the timing was not set explicitly, set default value */ - es->timing = (timing_set) ? es->timing : es->analyze; - - /* check that timing is used with EXPLAIN ANALYZE */ + /* check that TIMING is used with EXPLAIN ANALYZE */ if (es->timing && !es->analyze) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("EXPLAIN option TIMING requires ANALYZE"))); - /* if the summary was not set explicitly, set default value */ - es->summary = (summary_set) ? es->summary : es->analyze; + /* check that WAL is used with EXPLAIN ANALYZE */ + if (es->wal && !es->analyze) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("EXPLAIN option WAL requires ANALYZE"))); + + /* if SUMMARY was not set explicitly, set default value */ + es->summary = (dsummary) ? es->summary : es->analyze; /* * Parse analysis was done already, but we still have to run the rule diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index 2f3e0a70e0..349946eb89 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -38,6 +38,7 @@ #include "catalog/pg_authid.h" #include "catalog/storage.h" #include "commands/async.h" +#include "commands/explain.h" #include "commands/prepare.h" #include "commands/trigger.h" #include "commands/user.h" @@ -511,6 +512,7 @@ extern const struct config_enum_entry archive_mode_options[]; extern const struct config_enum_entry recovery_target_action_options[]; extern const struct config_enum_entry sync_method_options[]; extern const struct config_enum_entry dynamic_shared_memory_options[]; +extern const struct config_enum_entry explain_format_options[]; /* * GUC option variables that are exported from this module @@ -725,6 +727,8 @@ const char *const config_group_names[] = gettext_noop("Replication / Subscribers"), /* QUERY_TUNING */ gettext_noop("Query Tuning"), + /* QUERY_TUNING_EXPLAIN */ + gettext_noop("Query Tuning / EXPLAIN Options"), /* QUERY_TUNING_METHOD */ gettext_noop("Query Tuning / Planner Method Configuration"), /* QUERY_TUNING_COST */ @@ -931,6 +935,78 @@ static const unit_conversion time_unit_conversion_table[] = static struct config_bool ConfigureNamesBool[] = { + { + {"default_explain_analyze", PGC_USERSET, QUERY_TUNING_EXPLAIN, + gettext_noop("Sets the default ANALYZE option for EXPLAIN."), + NULL + }, + &default_explain_analyze, + false, + NULL, NULL, NULL + }, + { + {"default_explain_buffers", PGC_USERSET, QUERY_TUNING_EXPLAIN, + gettext_noop("Sets the default BUFFERS option for EXPLAIN."), + NULL + }, + &default_explain_buffers, + false, + NULL, NULL, NULL + }, + { + {"default_explain_costs", PGC_USERSET, QUERY_TUNING_EXPLAIN, + gettext_noop("Sets the default COSTS option for EXPLAIN."), + NULL + }, + &default_explain_costs, + true, + NULL, NULL, NULL + }, + { + {"default_explain_settings", PGC_USERSET, QUERY_TUNING_EXPLAIN, + gettext_noop("Sets the default SETTINGS option for EXPLAIN."), + NULL + }, + &default_explain_settings, + false, + NULL, NULL, NULL + }, + { + {"default_explain_summary", PGC_USERSET, QUERY_TUNING_EXPLAIN, + gettext_noop("Sets the default SUMMARY option for EXPLAIN."), + NULL + }, + &default_explain_summary, + false, + NULL, NULL, NULL + }, + { + {"default_explain_timing", PGC_USERSET, QUERY_TUNING_EXPLAIN, + gettext_noop("Sets the default TIMING option for EXPLAIN."), + NULL + }, + &default_explain_timing, + true, + NULL, NULL, NULL + }, + { + {"default_explain_verbose", PGC_USERSET, QUERY_TUNING_EXPLAIN, + gettext_noop("Sets the default VERBOSE option for EXPLAIN."), + NULL + }, + &default_explain_verbose, + false, + NULL, NULL, NULL + }, + { + {"default_explain_wal", PGC_USERSET, QUERY_TUNING_EXPLAIN, + gettext_noop("Sets the default WAL option for EXPLAIN."), + NULL + }, + &default_explain_wal, + false, + NULL, NULL, NULL + }, { {"enable_seqscan", PGC_USERSET, QUERY_TUNING_METHOD, gettext_noop("Enables the planner's use of sequential-scan plans."), @@ -4490,6 +4566,16 @@ static struct config_enum ConfigureNamesEnum[] = NULL, NULL, NULL }, + { + {"default_explain_format", PGC_USERSET, QUERY_TUNING_EXPLAIN, + gettext_noop("Sets the default FORMAT option for EXPLAIN."), + NULL + }, + &default_explain_format, + EXPLAIN_FORMAT_TEXT, explain_format_options, + NULL, NULL, NULL + }, + { {"default_transaction_isolation", PGC_USERSET, CLIENT_CONN_STATEMENT, gettext_noop("Sets the transaction isolation level of each new transaction."), diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample index 995b6ca155..3bcf399ba3 100644 --- a/src/backend/utils/misc/postgresql.conf.sample +++ b/src/backend/utils/misc/postgresql.conf.sample @@ -414,6 +414,18 @@ #plan_cache_mode = auto # auto, force_generic_plan or # force_custom_plan +# - EXPLAIN Options - + +#default_explain_analyze = off +#default_explain_buffers = off +#default_explain_costs = on +#default_explain_format = text +#default_explain_settings = off +#default_explain_summary = off +#default_explain_timing = on +#default_explain_verbose = off +#default_explain_wal = off + #------------------------------------------------------------------------------ # REPORTING AND LOGGING diff --git a/src/include/commands/explain.h b/src/include/commands/explain.h index ba661d32a6..9156f55920 100644 --- a/src/include/commands/explain.h +++ b/src/include/commands/explain.h @@ -124,4 +124,17 @@ extern void ExplainOpenGroup(const char *objtype, const char *labelname, extern void ExplainCloseGroup(const char *objtype, const char *labelname, bool labeled, ExplainState *es); +/* + * User-tweakable parameters + */ +extern PGDLLEXPORT bool default_explain_analyze; +extern PGDLLEXPORT bool default_explain_buffers; +extern PGDLLEXPORT bool default_explain_costs; +extern PGDLLEXPORT int default_explain_format; +extern PGDLLEXPORT bool default_explain_settings; +extern PGDLLEXPORT bool default_explain_summary; +extern PGDLLEXPORT bool default_explain_timing; +extern PGDLLEXPORT bool default_explain_verbose; +extern PGDLLEXPORT bool default_explain_wal; + #endif /* EXPLAIN_H */ diff --git a/src/include/utils/guc_tables.h b/src/include/utils/guc_tables.h index 454c2df487..f31cfa7ddf 100644 --- a/src/include/utils/guc_tables.h +++ b/src/include/utils/guc_tables.h @@ -77,6 +77,7 @@ enum config_group REPLICATION_STANDBY, REPLICATION_SUBSCRIBERS, QUERY_TUNING, + QUERY_TUNING_EXPLAIN, QUERY_TUNING_METHOD, QUERY_TUNING_COST, QUERY_TUNING_GEQO,