Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_CBC_SHA1:256) (Exim 4.89) (envelope-from ) id 1gYC7m-0003ci-1X for pgsql-hackers@arkaria.postgresql.org; Sat, 15 Dec 2018 15:45:06 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.89) (envelope-from ) id 1gYC7j-0007T7-HE for pgsql-hackers@arkaria.postgresql.org; Sat, 15 Dec 2018 15:45:03 +0000 Received: from magus.postgresql.org ([2a02:c0:301:0:ffff::29]) by malur.postgresql.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_CBC_SHA1:256) (Exim 4.89) (envelope-from ) id 1gYC7j-0007Ra-3a for pgsql-hackers@lists.postgresql.org; Sat, 15 Dec 2018 15:45:03 +0000 Received: from mail-wm1-x343.google.com ([2a00:1450:4864:20::343]) by magus.postgresql.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_CBC_SHA1:256) (Exim 4.89) (envelope-from ) id 1gYC7b-0002Z6-Iu for pgsql-hackers@lists.postgresql.org; Sat, 15 Dec 2018 15:45:02 +0000 Received: by mail-wm1-x343.google.com with SMTP id m22so8573782wml.3 for ; Sat, 15 Dec 2018 07:44:54 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=2ndquadrant-com.20150623.gappssmtp.com; s=20150623; h=subject:from:to:cc:references:message-id:date:user-agent :mime-version:in-reply-to:content-language; bh=ztUz/Ns9UFzWAwZjY/Ln86XrtEyNb7r/dfFmn5k4H4U=; b=MXK4HCTe7szpmUDfMFmIsSwT2pljlwrug/Kj82xPZVs6mvX2w3HVPt91GLl/1OpFW9 nm6neCxhoArsKsjNHvkV9zhgA0h+4BaAsxS/GDOugRLBy3enuiwqUVpye7Sk5yhdDOG+ 9WcJWv6/osvulhLYnSXPDvIrhuxcabnGm8Ykmh0Zq3PwjnyoGJ7CFGsX0D2PPSQYzYzy iFunggeIXt0BChS6uUUkOw5soEc+LW1obagx+WvmY5GPBTpLQlA+4bY5rNs73nGH6Jfd TQ/C0fmq5TbbvYYIJ12cVjumdwv0305uaLAM0FHtv9LwcANOqKZgA3wcRHTSZzwFFYHd PXAg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:subject:from:to:cc:references:message-id:date :user-agent:mime-version:in-reply-to:content-language; bh=ztUz/Ns9UFzWAwZjY/Ln86XrtEyNb7r/dfFmn5k4H4U=; b=P8YQ3Il30doWwtHgJSTEWMZqroJyXgqLNPacQha3BOQ5JmVprUbPEj2MxqIeBQxLGi 9Pc1DKoMjPUMqCDBgY9FUDYfT6Ck+Gl3hENbYViOiyGljsyoy/K0wW8fuzr8CV1YCNy8 efaMHVulCqzn5Xl7aKcdjq3svdKtCgReihDfg0vBn1QUq+uqX+5FrgqnsxyjchdNeM0e Ayx7ynAEcUwaETj97a2yZZ4wCHzwV0s/pnsNp0UWX9VGbALmZ/akOveIiyGrp/4BNjrV nK76jjflPYBJHPW2Vxvb4W4MQyklKVF/9uEX5npJ2q+F96sD8L+pP0BNU7wr0vzq6v7d VDUw== X-Gm-Message-State: AA+aEWZzYHrA+99HJYQBTp02XCrWjcUevIK8PN22tkwk3P2MWSGosQpb 1KTqyzZBeATQjzbiA7XKBm4D1LV6Htb/fWz3Usp83F7JYqQUyVHAzkXXIIwLDQ+wV+3AJ1hhh/n peb3h9vLdDYIKqaHH7hSG+6Hd8Ta/+By0AWAKC8v5rs0qDxuuF+rBqEOPIG8jNXKyJ0mxUmkj7+ i1QFaA4LSvksPqU5T9ap9Vog== X-Google-Smtp-Source: AFSGD/Uaq5423ZZrjWvfNcf58Ar3SZoypN9BEZdpNrMxRI4r3+3D57JqkJ0PwCp344HJ2Rv8Sp23Dg== X-Received: by 2002:a1c:e58c:: with SMTP id c134mr6324604wmh.124.1544888692849; Sat, 15 Dec 2018 07:44:52 -0800 (PST) Received: from [10.137.2.19] (ip-86-49-251-50.net.upcbroadband.cz. [86.49.251.50]) by smtp.gmail.com with ESMTPSA id d16sm8842210wru.52.2018.12.15.07.44.51 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Sat, 15 Dec 2018 07:44:51 -0800 (PST) Subject: Re: explain plans with information about (modified) gucs From: Tomas Vondra To: Tom Lane Cc: PostgreSQL Hackers References: <10561.1544800904@sss.pgh.pa.us> Message-ID: <631ae5c8-5581-6c5e-382c-2dcdb2e5c956@2ndquadrant.com> Date: Sat, 15 Dec 2018 16:44:48 +0100 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Thunderbird/60.3.1 MIME-Version: 1.0 In-Reply-To: Content-Type: multipart/mixed; boundary="------------AEC5569485AC5EAFBC437091" Content-Language: en-US List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Precedence: bulk This is a multi-part message in MIME format. --------------AEC5569485AC5EAFBC437091 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 7bit On 12/14/18 4:32 PM, Tomas Vondra wrote: > > > On 12/14/18 4:21 PM, Tom Lane wrote: >> Tomas Vondra writes: >>> ... I propose to extend EXPLAIN output with an additional option, which >>> would include information about modified GUCs in the execution plan >>> (disabled by default, of course): >> >> I'm a bit suspicious about whether this'll have any actual value, >> if it's disabled by default (which I agree it needs to be, if only for >> compatibility reasons). The problem you're trying to solve is basically >> "I forgot that this might have an effect", but stuff that isn't shown >> by default will not help you un-forget. It certainly won't fix the >> form of the problem that I run into, which is people sending in EXPLAIN >> plans and not mentioning their weird local settings. >> > > Not quite. > > I agree we'll still have to deal with plans from users without this > info, but it's easier to ask for explain with this extra option (just > like we regularly ask for explain analyze instead of just plain > explain). I'd expect the output to be more complete than trying to > figure out which of the GUCs might have effect / been modified here. > > But more importantly - my personal primary use case here is explains > from application connections generated using auto_explain, with some > application-level GUC magic. And there I can easily tweak auto_explain > config to do (auto_explain.log_gucs = true) of course. > >>> We certainly don't want to include all GUCs, so the question is how to >>> decide which GUCs are interesting. The simplest approach would be to >>> look for GUCs that changed in the session (source == PGC_S_SESSION), but >>> that does not help with SET SESSION AUTHORIZATION / ROLE cases. So we >>> probably want (source > PGC_S_ARGV), but probably not PGC_S_OVERRIDE >>> because that includes irrelevant options like wal_buffers etc. >> >> Don't you want to show anything that's not the built-in default? >> (I agree OVERRIDE could be excluded, but that's irrelevant for query >> tuning parameters.) Just because somebody injected a damfool setting >> of, say, random_page_cost via the postmaster command line or >> environment settings doesn't make it not damfool :-( >> > > Probably. My assumption here was that I can do > > select * from pg_settings > > and then combine it with whatever is included in the plan. But you're > right comparing it with the built-in default may be a better option. > FWIW here is a v3 of the patch, using the built-in default, and fixing a silly thinko resulting in the code not being executed from auto_explain. regards -- Tomas Vondra http://www.2ndQuadrant.com PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services --------------AEC5569485AC5EAFBC437091 Content-Type: text/x-patch; name="explain-with-gucs-v3.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="explain-with-gucs-v3.patch" 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'; + + + auto_explain.log_gucs (boolean) + + auto_explain.log_gucs configuration parameter + + + + + auto_explain.log_gucs 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. + + + + auto_explain.log_format (enum) diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c index de09ded65b..2e92690453 100644 --- a/src/backend/commands/explain.c +++ b/src/backend/commands/explain.c @@ -31,6 +31,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" @@ -164,6 +165,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; @@ -635,6 +638,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_modified_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..8223c041f7 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -8556,6 +8556,90 @@ ShowAllGUCConfig(DestReceiver *dest) end_tup_output(tstate); } +struct config_generic ** +get_modified_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 related to query tuning */ + if ((conf->group != QUERY_TUNING) && + (conf->group != QUERY_TUNING_METHOD) && + (conf->group != QUERY_TUNING_COST) && + (conf->group != QUERY_TUNING_GEQO) && + (conf->group != QUERY_TUNING_OTHER)) + 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_tables.h b/src/include/utils/guc_tables.h index 6f9fdb6a5f..4942e192d6 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_modified_guc_options(int *num); #endif /* GUC_TABLES_H */ --------------AEC5569485AC5EAFBC437091--