Allow auto_explain to log to NOTICE

Started by Tom Dunstanover 7 years ago18 messages
#1Tom Dunstan
pgsql@tomd.cc
1 attachment(s)

Hi all

This patch allows a user to configure auto_explain to log to NOTICE instead
of the server log. This allows automated testing of application-generated
queries to ensure correct plans etc from code that can inspect returned
notices but not the server log.

I don't see any tests for auto_explain so haven't added any test cases.
Happy to do so if that's deemed necessary.

Diff is against master. Also at
https://github.com/tomdcc/postgres/tree/auto-explain-notice

Cheers

Tom

Attachments:

auto-explain-notice-v1.diffapplication/octet-stream; name=auto-explain-notice-v1.diffDownload
diff --git a/contrib/auto_explain/auto_explain.c b/contrib/auto_explain/auto_explain.c
new file mode 100644
index ea4f957..2f5a8f7
*** a/contrib/auto_explain/auto_explain.c
--- b/contrib/auto_explain/auto_explain.c
*************** static bool auto_explain_log_buffers = f
*** 28,33 ****
--- 28,34 ----
  static bool auto_explain_log_triggers = false;
  static bool auto_explain_log_timing = true;
  static int	auto_explain_log_format = EXPLAIN_FORMAT_TEXT;
+ static int	auto_explain_log_destination = LOG;
  static bool auto_explain_log_nested_statements = false;
  static double auto_explain_sample_rate = 1;
  
*************** static const struct config_enum_entry fo
*** 39,44 ****
--- 40,51 ----
  	{NULL, 0, false}
  };
  
+ static const struct config_enum_entry destination_options[] = {
+ 	{"log", LOG, false},
+ 	{"notice", NOTICE, false},
+ 	{NULL, 0, false}
+ };
+ 
  /* Current nesting depth of ExecutorRun calls */
  static int	nesting_level = 0;
  
*************** _PG_init(void)
*** 141,146 ****
--- 148,165 ----
  							 NULL,
  							 NULL);
  
+ 	DefineCustomEnumVariable("auto_explain.log_destination",
+ 							 "Log destination for the plan.",
+ 							 NULL,
+ 							 &auto_explain_log_destination,
+ 							 LOG,
+ 							 destination_options,
+ 							 PGC_SUSET,
+ 							 0,
+ 							 NULL,
+ 							 NULL,
+ 							 NULL);
+ 
  	DefineCustomBoolVariable("auto_explain.log_nested_statements",
  							 "Log nested statements.",
  							 NULL,
*************** explain_ExecutorEnd(QueryDesc *queryDesc
*** 353,359 ****
  			 * reported.  This isn't ideal but trying to do it here would
  			 * often result in duplication.
  			 */
! 			ereport(LOG,
  					(errmsg("duration: %.3f ms  plan:\n%s",
  							msec, es->str->data),
  					 errhidestmt(true)));
--- 372,378 ----
  			 * reported.  This isn't ideal but trying to do it here would
  			 * often result in duplication.
  			 */
! 			ereport(auto_explain_log_destination,
  					(errmsg("duration: %.3f ms  plan:\n%s",
  							msec, es->str->data),
  					 errhidestmt(true)));
diff --git a/doc/src/sgml/auto-explain.sgml b/doc/src/sgml/auto-explain.sgml
new file mode 100644
index 08b67f2..bc1b4d7
*** a/doc/src/sgml/auto-explain.sgml
--- b/doc/src/sgml/auto-explain.sgml
*************** LOAD 'auto_explain';
*** 189,194 ****
--- 189,213 ----
  
     <varlistentry>
      <term>
+      <varname>auto_explain.log_destination</varname> (<type>enum</type>)
+      <indexterm>
+       <primary><varname>auto_explain.log_destination</> configuration parameter</primary>
+      </indexterm>
+     </term>
+     <listitem>
+      <para>
+       <varname>auto_explain.log_destination</varname> selects where
+       auto_explain will log the query plan.
+       The allowed values are <literal>log</literal>, which logs the plan to the
+       server log, and <literal>notice</literal>, which logs it as a NOTICE, which
+       allows clients to access it. <literal>log</literal> is the default.
+       Only superusers can change this setting.
+      </para>
+     </listitem>
+    </varlistentry>
+ 
+    <varlistentry>
+     <term>
       <varname>auto_explain.log_nested_statements</varname> (<type>boolean</type>)
       <indexterm>
        <primary><varname>auto_explain.log_nested_statements</varname> configuration parameter</primary>
#2Andres Freund
andres@anarazel.de
In reply to: Tom Dunstan (#1)
Re: Allow auto_explain to log to NOTICE

Hi,

On 2018-04-27 11:43:58 +0930, Tom Dunstan wrote:

This patch allows a user to configure auto_explain to log to NOTICE instead
of the server log. This allows automated testing of application-generated
queries to ensure correct plans etc from code that can inspect returned
notices but not the server log.

I don't see any tests for auto_explain so haven't added any test cases.
Happy to do so if that's deemed necessary.

I'd be in favor of adding them.

+static int auto_explain_log_destination = LOG;

I very much dislike this name - it's too similar too the log_destination
GUC, while being about something different. How about "log_level"?

+static const struct config_enum_entry destination_options[] = {
+	{"log", LOG, false},
+	{"notice", NOTICE, false},
+	{NULL, 0, false}
+};

I'd argue this should contain the non-error cases. It's just as
reasonable to want to add this as a debug level or such.

Greetings,

Andres Freund

#3Tom Dunstan
pgsql@tomd.cc
In reply to: Tom Dunstan (#1)
Re: Allow auto_explain to log to NOTICE

(Resent with subscribed email address, thanks gmail)

Hi Andres, thanks for the extremely fast review!

On 27 April 2018 at 11:46, Andres Freund <andres@anarazel.de> wrote:

I don't see any tests for auto_explain so haven't added any test cases.

Happy to do so if that's deemed necessary.

I'd be in favor of adding them.

OK, sure.

+static int auto_explain_log_destination = LOG;

I very much dislike this name - it's too similar too the log_destination
GUC, while being about something different. How about "log_level"?

Works for me.

+static const struct config_enum_entry destination_options[] = {

+ {"log", LOG, false},

+ {"notice", NOTICE, false},

+ {NULL, 0, false}

+};

I'd argue this should contain the non-error cases. It's just as
reasonable to want to add this as a debug level or such.

So all of warning, info, debug and debug1-5?

Thanks

Tom

#4Andres Freund
andres@anarazel.de
In reply to: Tom Dunstan (#1)
Re: Allow auto_explain to log to NOTICE

On 2018-04-27 11:52:18 +0930, Tom Dunstan wrote:

I'd argue this should contain the non-error cases. It's just as
reasonable to want to add this as a debug level or such.

So all of warning, info, debug and debug1-5?

Yea. Likely nobody will ever use debug5, but I don't see a point making
that judgement call here.

Greetings,

Andres Freund

#5Andrew Gierth
andrew@tao11.riddles.org.uk
In reply to: Tom Dunstan (#1)
Re: Allow auto_explain to log to NOTICE

"Tom" == Tom Dunstan <pgsql@tomd.cc> writes:

Tom> Hi all

Tom> This patch allows a user to configure auto_explain to log to
Tom> NOTICE instead of the server log. This allows automated testing of
Tom> application-generated queries to ensure correct plans etc from
Tom> code that can inspect returned notices but not the server log.

You do know that you can do

SET client_min_messages = log;

to get all log messages generated within your session sent to the client
as well, yes?

--
Andrew (irc:RhodiumToad)

#6Daniel Gustafsson
daniel@yesql.se
In reply to: Andres Freund (#4)
Re: Allow auto_explain to log to NOTICE

On 27 Apr 2018, at 04:24, Andres Freund <andres@anarazel.de> wrote:

On 2018-04-27 11:52:18 +0930, Tom Dunstan wrote:

I'd argue this should contain the non-error cases. It's just as
reasonable to want to add this as a debug level or such.

So all of warning, info, debug and debug1-5?

Yea. Likely nobody will ever use debug5, but I don't see a point making
that judgement call here.

Did you have a chance to hack up a new version of the patch with Andres’ review
comments? I’m marking this patch as Waiting on Author for now based on the
feedback so far in this thread.

cheers ./daniel

#7Andrew Dunstan
andrew.dunstan@2ndquadrant.com
In reply to: Daniel Gustafsson (#6)
1 attachment(s)
Re: Allow auto_explain to log to NOTICE

On Wed, Jun 20, 2018 at 2:06 PM, Daniel Gustafsson <daniel@yesql.se> wrote:

On 27 Apr 2018, at 04:24, Andres Freund <andres@anarazel.de> wrote:

On 2018-04-27 11:52:18 +0930, Tom Dunstan wrote:

I'd argue this should contain the non-error cases. It's just as
reasonable to want to add this as a debug level or such.

So all of warning, info, debug and debug1-5?

Yea. Likely nobody will ever use debug5, but I don't see a point making
that judgement call here.

Did you have a chance to hack up a new version of the patch with Andres’ review
comments? I’m marking this patch as Waiting on Author for now based on the
feedback so far in this thread.

Here is an updated version of the patch. Setting this to "needs review".

I haven't added tests for auto_explain - I think that would be useful
but it's a separate project.

cheers

andrew

--
Andrew Dunstan https://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

Attachments:

auto-explain-log-level-v2.patchapplication/octet-stream; name=auto-explain-log-level-v2.patchDownload
diff --git a/contrib/auto_explain/auto_explain.c b/contrib/auto_explain/auto_explain.c
index ea4f957cfa..a6996f15a7 100644
--- a/contrib/auto_explain/auto_explain.c
+++ b/contrib/auto_explain/auto_explain.c
@@ -28,6 +28,7 @@ static bool auto_explain_log_buffers = false;
 static bool auto_explain_log_triggers = false;
 static bool auto_explain_log_timing = true;
 static int	auto_explain_log_format = EXPLAIN_FORMAT_TEXT;
+static int	auto_explain_log_level = LOG;
 static bool auto_explain_log_nested_statements = false;
 static double auto_explain_sample_rate = 1;
 
@@ -39,6 +40,20 @@ static const struct config_enum_entry format_options[] = {
 	{NULL, 0, false}
 };
 
+static const struct config_enum_entry loglevel_options[] = {
+	{"debug", DEBUG2, true},
+	{"debug5", DEBUG5, false},
+	{"debug4", DEBUG4, false},
+	{"debug3", DEBUG3, false},
+	{"debug2", DEBUG2, false},
+	{"debug1", DEBUG1, false},
+	{"info", INFO, false},
+	{"notice", NOTICE, false},
+	{"warning", WARNING, false},
+	{"log", LOG, false},
+	{NULL, 0, false}
+};
+
 /* Current nesting depth of ExecutorRun calls */
 static int	nesting_level = 0;
 
@@ -141,6 +156,18 @@ _PG_init(void)
 							 NULL,
 							 NULL);
 
+	DefineCustomEnumVariable("auto_explain.log_level",
+							 "Log level for the plan.",
+							 NULL,
+							 &auto_explain_log_level,
+							 LOG,
+							 loglevel_options,
+							 PGC_SUSET,
+							 0,
+							 NULL,
+							 NULL,
+							 NULL);
+
 	DefineCustomBoolVariable("auto_explain.log_nested_statements",
 							 "Log nested statements.",
 							 NULL,
@@ -353,7 +380,7 @@ explain_ExecutorEnd(QueryDesc *queryDesc)
 			 * reported.  This isn't ideal but trying to do it here would
 			 * often result in duplication.
 			 */
-			ereport(LOG,
+			ereport(auto_explain_log_level,
 					(errmsg("duration: %.3f ms  plan:\n%s",
 							msec, es->str->data),
 					 errhidestmt(true)));
diff --git a/doc/src/sgml/auto-explain.sgml b/doc/src/sgml/auto-explain.sgml
index 08b67f2600..2b928d0806 100644
--- a/doc/src/sgml/auto-explain.sgml
+++ b/doc/src/sgml/auto-explain.sgml
@@ -187,6 +187,27 @@ LOAD 'auto_explain';
     </listitem>
    </varlistentry>
 
+   <varlistentry>
+    <term>
+     <varname>auto_explain.log_level</varname> (<type>enum</type>)
+     <indexterm>
+      <primary><varname>auto_explain.log_level</> configuration parameter</primary>
+     </indexterm>
+    </term>
+    <listitem>
+     <para>
+      <varname>auto_explain.log_level</varname> selects the log level at which
+      auto_explain will log the query plan.
+      Valid values are <literal>DEBUG5</literal>, <literal>DEBUG4</literal>,
+      <literal>DEBUG3</literal>, <literal>DEBUG2</literal>,
+      <literal>DEBUG1</literal>, <literal>INFO</literal>,
+      <literal>NOTICE</literal>, <literal>WARNING</literal>,
+      and <literal>LOG</literal>. The default is <literal>LOG</literal>.
+      Only superusers can change this setting.
+     </para>
+    </listitem>
+   </varlistentry>
+
    <varlistentry>
     <term>
      <varname>auto_explain.log_nested_statements</varname> (<type>boolean</type>)
#8Daniel Gustafsson
daniel@yesql.se
In reply to: Andrew Dunstan (#7)
1 attachment(s)
Re: Allow auto_explain to log to NOTICE

On 4 Jul 2018, at 15:34, Andrew Dunstan <andrew.dunstan@2ndquadrant.com> wrote:

On Wed, Jun 20, 2018 at 2:06 PM, Daniel Gustafsson <daniel@yesql.se> wrote:

On 27 Apr 2018, at 04:24, Andres Freund <andres@anarazel.de> wrote:

On 2018-04-27 11:52:18 +0930, Tom Dunstan wrote:

I'd argue this should contain the non-error cases. It's just as
reasonable to want to add this as a debug level or such.

So all of warning, info, debug and debug1-5?

Yea. Likely nobody will ever use debug5, but I don't see a point making
that judgement call here.

Did you have a chance to hack up a new version of the patch with Andres’ review
comments? I’m marking this patch as Waiting on Author for now based on the
feedback so far in this thread.

Here is an updated version of the patch. Setting this to "needs review”.

Thanks! Looking at the code, and documentation this seems a worthwhile
addition. Manual testing proves that it works as expected, and the patch
follows the expected style. A few small comments:

Since DEBUG is not a defined loglevel, it seems superfluous to include it here.
It’s also omitted from the documentation so it should probably be omitted from
here.

+ {"debug", DEBUG2, true},

The <varname> tag should be closed with a matching </varname>.

+ <primary><varname>auto_explain.log_level</> configuration parameter</primary>

With those fixed it’s ready for committer.

I haven't added tests for auto_explain - I think that would be useful
but it's a separate project.

Agreeing that this would be beneficial, the attached patch (to apply on top of
the patch in question) takes a stab at adding tests for this new functionality.

In order to test plan output we need to support COSTS in the explain output, so
a new GUC auto_explain.log_costs is added. We also need to not print the
duration, so as a hack this patch omits the duration if auto_explain.log_timing
is set to off and auto_explain.log_analyze is set off. This is a hack and not
a nice overloading, but it seems overkill to add a separate GUC just to turn
off the duration, any better ideas on how support omitting the duration?

cheers ./daniel

Attachments:

auto_explain_tests.diffapplication/octet-stream; name=auto_explain_tests.diff; x-unix-mode=0644Download
diff --git a/contrib/auto_explain/Makefile b/contrib/auto_explain/Makefile
index fcf36c5ab0..db61901830 100644
--- a/contrib/auto_explain/Makefile
+++ b/contrib/auto_explain/Makefile
@@ -4,6 +4,8 @@ MODULE_big = auto_explain
 OBJS = auto_explain.o $(WIN32RES)
 PGFILEDESC = "auto_explain - logging facility for execution plans"
 
+REGRESS = auto_explain
+
 ifdef USE_PGXS
 PG_CONFIG = pg_config
 PGXS := $(shell $(PG_CONFIG) --pgxs)
diff --git a/contrib/auto_explain/auto_explain.c b/contrib/auto_explain/auto_explain.c
index ea4f957cfa..5723e4383a 100644
--- a/contrib/auto_explain/auto_explain.c
+++ b/contrib/auto_explain/auto_explain.c
@@ -27,6 +27,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_costs = true;
 static int	auto_explain_log_format = EXPLAIN_FORMAT_TEXT;
 static bool auto_explain_log_nested_statements = false;
 static double auto_explain_sample_rate = 1;
@@ -129,6 +130,17 @@ _PG_init(void)
 							 NULL,
 							 NULL);
 
+	DefineCustomBoolVariable("auto_explain.log_costs",
+							 "Include costs in plans.",
+							 NULL,
+							 &auto_explain_log_costs,
+							 true,
+							 PGC_SUSET,
+							 0,
+							 NULL,
+							 NULL,
+							 NULL);
+
 	DefineCustomEnumVariable("auto_explain.log_format",
 							 "EXPLAIN format to be used for plan logging.",
 							 NULL,
@@ -309,6 +321,7 @@ explain_ExecutorEnd(QueryDesc *queryDesc)
 	if (queryDesc->totaltime && auto_explain_enabled() && current_query_sampled)
 	{
 		double		msec;
+		StringInfoData buf;
 
 		/*
 		 * Make sure stats accumulation is done.  (Note: it's okay if several
@@ -328,6 +341,7 @@ explain_ExecutorEnd(QueryDesc *queryDesc)
 			es->timing = (es->analyze && auto_explain_log_timing);
 			es->summary = es->analyze;
 			es->format = auto_explain_log_format;
+			es->costs = auto_explain_log_costs;
 
 			ExplainBeginOutput(es);
 			ExplainQueryText(es, queryDesc);
@@ -347,18 +361,24 @@ explain_ExecutorEnd(QueryDesc *queryDesc)
 				es->str->data[es->str->len - 1] = '}';
 			}
 
+			initStringInfo(&buf);
+
+			if (es->analyze || auto_explain_log_timing)
+				appendStringInfo(&buf, "duration: %.3f ms  ", msec);
+
 			/*
 			 * Note: we rely on the existing logging of context or
 			 * debug_query_string to identify just which statement is being
 			 * reported.  This isn't ideal but trying to do it here would
 			 * often result in duplication.
 			 */
			ereport(auto_explain_log_level,
-					(errmsg("duration: %.3f ms  plan:\n%s",
-							msec, es->str->data),
+					(errmsg("%splan:\n%s",
+							buf.data, es->str->data),
 					 errhidestmt(true)));
 
 			pfree(es->str->data);
+			pfree(buf.data);
 		}
 	}
 
diff --git a/contrib/auto_explain/expected/auto_explain.out b/contrib/auto_explain/expected/auto_explain.out
new file mode 100644
index 0000000000..070d28b667
--- /dev/null
+++ b/contrib/auto_explain/expected/auto_explain.out
@@ -0,0 +1,67 @@
+LOAD 'auto_explain';
+SET auto_explain.log_min_duration = 0;
+SET auto_explain.log_level = NOTICE;
+SET auto_explain.log_timing = off;
+SET auto_explain.log_costs = off;
+-- Should log
+SELECT NULL FROM pg_catalog.pg_class WHERE relname = 'pg_class';
+NOTICE:  plan:
+Query Text: SELECT NULL FROM pg_catalog.pg_class WHERE relname = 'pg_class';
+Index Only Scan using pg_class_relname_nsp_index on pg_class
+  Index Cond: (relname = 'pg_class'::name)
+ ?column? 
+----------
+ 
+(1 row)
+
+SET auto_explain.log_verbose = on;
+SELECT NULL FROM pg_catalog.pg_class WHERE relname = 'pg_class';
+NOTICE:  plan:
+Query Text: SELECT NULL FROM pg_catalog.pg_class WHERE relname = 'pg_class';
+Index Only Scan using pg_class_relname_nsp_index on pg_catalog.pg_class
+  Output: NULL::text
+  Index Cond: (pg_class.relname = 'pg_class'::name)
+ ?column? 
+----------
+ 
+(1 row)
+
+SET auto_explain.log_format = json;
+SELECT NULL FROM pg_catalog.pg_class WHERE relname = 'pg_class';
+NOTICE:  plan:
+{
+  "Query Text": "SELECT NULL FROM pg_catalog.pg_class WHERE relname = 'pg_class';",
+  "Plan": {
+    "Node Type": "Index Only Scan",
+    "Parallel Aware": false,
+    "Scan Direction": "Forward",
+    "Index Name": "pg_class_relname_nsp_index",
+    "Relation Name": "pg_class",
+    "Schema": "pg_catalog",
+    "Alias": "pg_class",
+    "Output": ["NULL::text"],
+    "Index Cond": "(pg_class.relname = 'pg_class'::name)"
+  }
+}
+ ?column? 
+----------
+ 
+(1 row)
+
+-- Shouldn't log due to too high loglevel
+SET auto_explain.log_level = DEBUG5;
+SELECT NULL FROM pg_catalog.pg_class WHERE relname = 'pg_class';
+ ?column? 
+----------
+ 
+(1 row)
+
+-- Shouldn't log due to query being too fast
+SET auto_explain.log_level = NOTICE;
+SET auto_explain.log_min_duration = 1000;
+SELECT NULL FROM pg_catalog.pg_class WHERE relname = 'pg_class';
+ ?column? 
+----------
+ 
+(1 row)
+
diff --git a/contrib/auto_explain/sql/auto_explain.sql b/contrib/auto_explain/sql/auto_explain.sql
new file mode 100644
index 0000000000..8f7b05274e
--- /dev/null
+++ b/contrib/auto_explain/sql/auto_explain.sql
@@ -0,0 +1,25 @@
+LOAD 'auto_explain';
+
+SET auto_explain.log_min_duration = 0;
+SET auto_explain.log_level = NOTICE;
+SET auto_explain.log_timing = off;
+SET auto_explain.log_costs = off;
+
+-- Should log
+SELECT NULL FROM pg_catalog.pg_class WHERE relname = 'pg_class';
+
+SET auto_explain.log_verbose = on;
+SELECT NULL FROM pg_catalog.pg_class WHERE relname = 'pg_class';
+
+SET auto_explain.log_format = json;
+SELECT NULL FROM pg_catalog.pg_class WHERE relname = 'pg_class';
+
+-- Shouldn't log due to too high loglevel
+SET auto_explain.log_level = DEBUG5;
+SELECT NULL FROM pg_catalog.pg_class WHERE relname = 'pg_class';
+
+-- Shouldn't log due to query being too fast
+SET auto_explain.log_level = NOTICE;
+SET auto_explain.log_min_duration = 1000;
+SELECT NULL FROM pg_catalog.pg_class WHERE relname = 'pg_class';
+
diff --git a/doc/src/sgml/auto-explain.sgml b/doc/src/sgml/auto-explain.sgml
index 08b67f2600..25f445e1bb 100644
--- a/doc/src/sgml/auto-explain.sgml
+++ b/doc/src/sgml/auto-explain.sgml
@@ -108,6 +108,24 @@ LOAD 'auto_explain';
     </listitem>
    </varlistentry>
 
+   <varlistentry>
+    <term>
+     <varname>auto_explain.log_costs</varname> (<type>boolean</type>)
+     <indexterm>
+      <primary><varname>auto_explain.log_costs</varname> configuration parameter</primary>
+     </indexterm>
+    </term>
+    <listitem>
+     <para>
+      <varname>auto_explain.log_costs</varname> controls whether per node costs
+      are printed when an execution plan is logged; it's
+      equivalent to the <literal>COSTS</literal> option of <command>EXPLAIN</command>.
+      This parameter is on by default.
+      Only superusers can change this setting.
+     </para>
+    </listitem>
+   </varlistentry>
+
    <varlistentry>
     <term>
      <varname>auto_explain.log_timing</varname> (<type>boolean</type>)
@@ -124,8 +142,8 @@ LOAD 'auto_explain';
       queries significantly on some systems, so it may be useful to set this
       parameter to off when only actual row counts, and not exact times, are
       needed.
-      This parameter has no effect
-      unless <varname>auto_explain.log_analyze</varname> is enabled.
+      This parameter has turns on printing the duration of the plan if
+      <varname>auto_explain.log_analyze</varname> is disabled.
       This parameter is on by default.
       Only superusers can change this setting.
      </para>
#9Andrew Dunstan
andrew.dunstan@2ndquadrant.com
In reply to: Daniel Gustafsson (#8)
Re: Allow auto_explain to log to NOTICE

On 07/17/2018 12:04 PM, Daniel Gustafsson wrote:

On 4 Jul 2018, at 15:34, Andrew Dunstan <andrew.dunstan@2ndquadrant.com> wrote:

On Wed, Jun 20, 2018 at 2:06 PM, Daniel Gustafsson <daniel@yesql.se> wrote:

On 27 Apr 2018, at 04:24, Andres Freund <andres@anarazel.de> wrote:

On 2018-04-27 11:52:18 +0930, Tom Dunstan wrote:

I'd argue this should contain the non-error cases. It's just as
reasonable to want to add this as a debug level or such.

So all of warning, info, debug and debug1-5?

Yea. Likely nobody will ever use debug5, but I don't see a point making
that judgement call here.

Did you have a chance to hack up a new version of the patch with Andres’ review
comments? I’m marking this patch as Waiting on Author for now based on the
feedback so far in this thread.

Here is an updated version of the patch. Setting this to "needs review”.

Thanks for the review

Thanks! Looking at the code, and documentation this seems a worthwhile
addition. Manual testing proves that it works as expected, and the patch
follows the expected style. A few small comments:

Since DEBUG is not a defined loglevel, it seems superfluous to include it here.
It’s also omitted from the documentation so it should probably be omitted from
here.

+ {"debug", DEBUG2, true},

I treated this like we do for client_min_messages and log_min_messages -
the alias is there but I don;t think we document it either.

I don't mind removing it, was just trying to be consistent. It seems odd
that we would accept it in one place but not another.

The <varname> tag should be closed with a matching </varname>.

+ <primary><varname>auto_explain.log_level</> configuration parameter</primary>

With those fixed it’s ready for committer.

Thanks, will fix.

I haven't added tests for auto_explain - I think that would be useful
but it's a separate project.

Agreeing that this would be beneficial, the attached patch (to apply on top of
the patch in question) takes a stab at adding tests for this new functionality.

In order to test plan output we need to support COSTS in the explain output, so
a new GUC auto_explain.log_costs is added. We also need to not print the
duration, so as a hack this patch omits the duration if auto_explain.log_timing
is set to off and auto_explain.log_analyze is set off. This is a hack and not
a nice overloading, but it seems overkill to add a separate GUC just to turn
off the duration, any better ideas on how support omitting the duration?

Great, I'll check it out.

cheers

andrew

--
Andrew Dunstan https://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

#10Daniel Gustafsson
daniel@yesql.se
In reply to: Andrew Dunstan (#9)
Re: Allow auto_explain to log to NOTICE

On 17 Jul 2018, at 19:11, Andrew Dunstan <andrew.dunstan@2ndquadrant.com> wrote:

On 07/17/2018 12:04 PM, Daniel Gustafsson wrote:

Since DEBUG is not a defined loglevel, it seems superfluous to include it here.
It’s also omitted from the documentation so it should probably be omitted from
here.

+ {"debug", DEBUG2, true},

I treated this like we do for client_min_messages and log_min_messages - the alias is there but I don;t think we document it either.

I don't mind removing it, was just trying to be consistent. It seems odd that we would accept it in one place but not another.

Ooh.. I didn’t know that alias existed and didn’t find it when poking at the
code. In that case I agree with you, the alias should stay so I withdraw that
comment. I learned something new today =)

I haven't added tests for auto_explain - I think that would be useful
but it's a separate project.

Agreeing that this would be beneficial, the attached patch (to apply on top of
the patch in question) takes a stab at adding tests for this new functionality.

In order to test plan output we need to support COSTS in the explain output, so
a new GUC auto_explain.log_costs is added. We also need to not print the
duration, so as a hack this patch omits the duration if auto_explain.log_timing
is set to off and auto_explain.log_analyze is set off. This is a hack and not
a nice overloading, but it seems overkill to add a separate GUC just to turn
off the duration, any better ideas on how support omitting the duration?

Great, I'll check it out.

I’m not sure it’s worth adding this much to the code just to be able to test
it, but it seemed like a good excercise to write to have something to reason
about.

cheers ./daniel

#11Andrew Dunstan
andrew.dunstan@2ndquadrant.com
In reply to: Daniel Gustafsson (#10)
Re: Allow auto_explain to log to NOTICE

On 07/17/2018 02:03 PM, Daniel Gustafsson wrote:

On 17 Jul 2018, at 19:11, Andrew Dunstan <andrew.dunstan@2ndquadrant.com> wrote:

On 07/17/2018 12:04 PM, Daniel Gustafsson wrote:

Since DEBUG is not a defined loglevel, it seems superfluous to include it here.
It’s also omitted from the documentation so it should probably be omitted from
here.

+ {"debug", DEBUG2, true},

I treated this like we do for client_min_messages and log_min_messages - the alias is there but I don;t think we document it either.

I don't mind removing it, was just trying to be consistent. It seems odd that we would accept it in one place but not another.

Ooh.. I didn’t know that alias existed and didn’t find it when poking at the
code. In that case I agree with you, the alias should stay so I withdraw that
comment. I learned something new today =)

Committed with the doc fix belatedly.

I haven't added tests for auto_explain - I think that would be useful
but it's a separate project.

Agreeing that this would be beneficial, the attached patch (to apply on top of
the patch in question) takes a stab at adding tests for this new functionality.

In order to test plan output we need to support COSTS in the explain output, so
a new GUC auto_explain.log_costs is added. We also need to not print the
duration, so as a hack this patch omits the duration if auto_explain.log_timing
is set to off and auto_explain.log_analyze is set off. This is a hack and not
a nice overloading, but it seems overkill to add a separate GUC just to turn
off the duration, any better ideas on how support omitting the duration?

Great, I'll check it out.

I’m not sure it’s worth adding this much to the code just to be able to test
it, but it seemed like a good excercise to write to have something to reason
about.

I think it probably is, buit I'm not very happy about the hack, so I
didn't commit it.  Please submit this to the next commitfest, possibly
with a nicer way of managing the duration logging.

cheers

andrew

--
Andrew Dunstan https://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

#12Daniel Gustafsson
daniel@yesql.se
In reply to: Andrew Dunstan (#11)
1 attachment(s)
Re: Allow auto_explain to log to NOTICE

On 31 Jul 2018, at 14:23, Andrew Dunstan <andrew.dunstan@2ndquadrant.com> wrote:

I’m not sure it’s worth adding this much to the code just to be able to test
it, but it seemed like a good excercise to write to have something to reason
about.

I think it probably is, buit I'm not very happy about the hack, so I didn't commit it. Please submit this to the next commitfest, possibly with a nicer way of managing the duration logging.

Circling back to this, I updated the patch with providing another option as I
couldn’t think of another way to do it cleanly. I’ll add the patch to the next
CF but as it’s just about to start it should be moved to the next once started.

cheers ./daniel

Attachments:

auto_explain_tests-v2.diffapplication/octet-stream; name=auto_explain_tests-v2.diff; x-unix-mode=0644Download
From 0bb615ec6fa326cf816c54edf0ea6cea4ab91d67 Mon Sep 17 00:00:00 2001
From: Daniel Gustafsson <daniel@yesql.se>
Date: Tue, 30 Oct 2018 11:19:51 +0100
Subject: [PATCH] Add costs and duration options to auto_explain

This adds additional options to auto_explain for controlling logging
of costs and the query duration. While being able to turn costs on or
off can be useful, the main driver is to be able to properly test the
extension. This commit adds basic test coverage to auto_explain using
the options introduced to keep the test output stable.
---
 contrib/auto_explain/Makefile                  |  2 +
 contrib/auto_explain/auto_explain.c            | 39 +++++++++++++--
 contrib/auto_explain/expected/auto_explain.out | 68 ++++++++++++++++++++++++++
 contrib/auto_explain/sql/auto_explain.sql      | 26 ++++++++++
 doc/src/sgml/auto-explain.sgml                 | 34 +++++++++++++
 5 files changed, 165 insertions(+), 4 deletions(-)
 create mode 100644 contrib/auto_explain/expected/auto_explain.out
 create mode 100644 contrib/auto_explain/sql/auto_explain.sql

diff --git a/contrib/auto_explain/Makefile b/contrib/auto_explain/Makefile
index fcf36c5ab0..db61901830 100644
--- a/contrib/auto_explain/Makefile
+++ b/contrib/auto_explain/Makefile
@@ -4,6 +4,8 @@ MODULE_big = auto_explain
 OBJS = auto_explain.o $(WIN32RES)
 PGFILEDESC = "auto_explain - logging facility for execution plans"
 
+REGRESS = auto_explain
+
 ifdef USE_PGXS
 PG_CONFIG = pg_config
 PGXS := $(shell $(PG_CONFIG) --pgxs)
diff --git a/contrib/auto_explain/auto_explain.c b/contrib/auto_explain/auto_explain.c
index 646cd0d42c..89fd317cb2 100644
--- a/contrib/auto_explain/auto_explain.c
+++ b/contrib/auto_explain/auto_explain.c
@@ -28,6 +28,8 @@ 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_costs = true;
+static bool auto_explain_log_duration = true;
 static int	auto_explain_log_format = EXPLAIN_FORMAT_TEXT;
 static int	auto_explain_log_level = LOG;
 static bool auto_explain_log_nested_statements = false;
@@ -145,6 +147,29 @@ _PG_init(void)
 							 NULL,
 							 NULL);
 
+	DefineCustomBoolVariable("auto_explain.log_costs",
+							 "Include costs in plans.",
+							 NULL,
+							 &auto_explain_log_costs,
+							 true,
+							 PGC_SUSET,
+							 0,
+							 NULL,
+							 NULL,
+							 NULL);
+
+	DefineCustomBoolVariable("auto_explain.log_duration",
+							 "Log the duration of the query.",
+							 NULL,
+							 &auto_explain_log_duration,
+							 true,
+							 PGC_SUSET,
+							 0,
+							 NULL,
+							 NULL,
+							 NULL);
+
+
 	DefineCustomEnumVariable("auto_explain.log_format",
 							 "EXPLAIN format to be used for plan logging.",
 							 NULL,
@@ -356,6 +381,7 @@ explain_ExecutorEnd(QueryDesc *queryDesc)
 			es->timing = (es->analyze && auto_explain_log_timing);
 			es->summary = es->analyze;
 			es->format = auto_explain_log_format;
+			es->costs = auto_explain_log_costs;
 
 			ExplainBeginOutput(es);
 			ExplainQueryText(es, queryDesc);
@@ -383,10 +409,15 @@ explain_ExecutorEnd(QueryDesc *queryDesc)
 			 * reported.  This isn't ideal but trying to do it here would
 			 * often result in duplication.
 			 */
-			ereport(auto_explain_log_level,
-					(errmsg("duration: %.3f ms  plan:\n%s",
-							msec, es->str->data),
-					 errhidestmt(true)));
+			if (auto_explain_log_duration)
+				ereport(auto_explain_log_level,
+						(errmsg("duration: %.3f ms  plan:\n%s",
+								msec, es->str->data),
+						 errhidestmt(true)));
+			else
+				ereport(auto_explain_log_level,
+						(errmsg("plan:\n%s", es->str->data),
+						 errhidestmt(true)));
 
 			pfree(es->str->data);
 		}
diff --git a/contrib/auto_explain/expected/auto_explain.out b/contrib/auto_explain/expected/auto_explain.out
new file mode 100644
index 0000000000..461343b1e1
--- /dev/null
+++ b/contrib/auto_explain/expected/auto_explain.out
@@ -0,0 +1,68 @@
+LOAD 'auto_explain';
+SET auto_explain.log_min_duration = 0;
+SET auto_explain.log_duration = off;
+SET auto_explain.log_level = NOTICE;
+SET auto_explain.log_timing = off;
+SET auto_explain.log_costs = off;
+-- Should log
+SELECT NULL FROM pg_catalog.pg_class WHERE relname = 'pg_class';
+NOTICE:  plan:
+Query Text: SELECT NULL FROM pg_catalog.pg_class WHERE relname = 'pg_class';
+Index Only Scan using pg_class_relname_nsp_index on pg_class
+  Index Cond: (relname = 'pg_class'::name)
+ ?column? 
+----------
+ 
+(1 row)
+
+SET auto_explain.log_verbose = on;
+SELECT NULL FROM pg_catalog.pg_class WHERE relname = 'pg_class';
+NOTICE:  plan:
+Query Text: SELECT NULL FROM pg_catalog.pg_class WHERE relname = 'pg_class';
+Index Only Scan using pg_class_relname_nsp_index on pg_catalog.pg_class
+  Output: NULL::text
+  Index Cond: (pg_class.relname = 'pg_class'::name)
+ ?column? 
+----------
+ 
+(1 row)
+
+SET auto_explain.log_format = json;
+SELECT NULL FROM pg_catalog.pg_class WHERE relname = 'pg_class';
+NOTICE:  plan:
+{
+  "Query Text": "SELECT NULL FROM pg_catalog.pg_class WHERE relname = 'pg_class';",
+  "Plan": {
+    "Node Type": "Index Only Scan",
+    "Parallel Aware": false,
+    "Scan Direction": "Forward",
+    "Index Name": "pg_class_relname_nsp_index",
+    "Relation Name": "pg_class",
+    "Schema": "pg_catalog",
+    "Alias": "pg_class",
+    "Output": ["NULL::text"],
+    "Index Cond": "(pg_class.relname = 'pg_class'::name)"
+  }
+}
+ ?column? 
+----------
+ 
+(1 row)
+
+-- Shouldn't log due to too high loglevel
+SET auto_explain.log_level = DEBUG5;
+SELECT NULL FROM pg_catalog.pg_class WHERE relname = 'pg_class';
+ ?column? 
+----------
+ 
+(1 row)
+
+-- Shouldn't log due to query being too fast
+SET auto_explain.log_level = NOTICE;
+SET auto_explain.log_min_duration = 1000;
+SELECT NULL FROM pg_catalog.pg_class WHERE relname = 'pg_class';
+ ?column? 
+----------
+ 
+(1 row)
+
diff --git a/contrib/auto_explain/sql/auto_explain.sql b/contrib/auto_explain/sql/auto_explain.sql
new file mode 100644
index 0000000000..cea1be3ec1
--- /dev/null
+++ b/contrib/auto_explain/sql/auto_explain.sql
@@ -0,0 +1,26 @@
+LOAD 'auto_explain';
+
+SET auto_explain.log_min_duration = 0;
+SET auto_explain.log_duration = off;
+SET auto_explain.log_level = NOTICE;
+SET auto_explain.log_timing = off;
+SET auto_explain.log_costs = off;
+
+-- Should log
+SELECT NULL FROM pg_catalog.pg_class WHERE relname = 'pg_class';
+
+SET auto_explain.log_verbose = on;
+SELECT NULL FROM pg_catalog.pg_class WHERE relname = 'pg_class';
+
+SET auto_explain.log_format = json;
+SELECT NULL FROM pg_catalog.pg_class WHERE relname = 'pg_class';
+
+-- Shouldn't log due to too high loglevel
+SET auto_explain.log_level = DEBUG5;
+SELECT NULL FROM pg_catalog.pg_class WHERE relname = 'pg_class';
+
+-- Shouldn't log due to query being too fast
+SET auto_explain.log_level = NOTICE;
+SET auto_explain.log_min_duration = 1000;
+SELECT NULL FROM pg_catalog.pg_class WHERE relname = 'pg_class';
+
diff --git a/doc/src/sgml/auto-explain.sgml b/doc/src/sgml/auto-explain.sgml
index 120b168d45..be06a29139 100644
--- a/doc/src/sgml/auto-explain.sgml
+++ b/doc/src/sgml/auto-explain.sgml
@@ -108,6 +108,40 @@ LOAD 'auto_explain';
     </listitem>
    </varlistentry>
 
+   <varlistentry>
+    <term>
+     <varname>auto_explain.log_costs</varname> (<type>boolean</type>)
+     <indexterm>
+      <primary><varname>auto_explain.log_costs</varname> configuration parameter</primary>
+     </indexterm>
+    </term>
+    <listitem>
+     <para>
+      <varname>auto_explain.log_costs</varname> controls whether per node costs
+      are printed when an execution plan is logged; it's
+      equivalent to the <literal>COSTS</literal> option of <command>EXPLAIN</command>.
+      This parameter is on by default.
+      Only superusers can change this setting.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term>
+     <varname>auto_explain.log_duration</varname> (<type>boolean</type>)
+     <indexterm>
+      <primary><varname>auto_explain.log_duration</varname> configuration parameter</primary>
+     </indexterm>
+    </term>
+    <listitem>
+     <para>
+      <varname>auto_explain.log_duration</varname> controls whether the logged
+       plan is prefixed with the duration of the query. This parameter is on by
+       default.  Only superusers can change this setting.
+     </para>
+    </listitem>
+   </varlistentry>
+
    <varlistentry>
     <term>
      <varname>auto_explain.log_timing</varname> (<type>boolean</type>)
-- 
2.14.1.145.gb3622a4ee

#13Michael Paquier
michael@paquier.xyz
In reply to: Daniel Gustafsson (#12)
Re: Allow auto_explain to log to NOTICE

On Tue, Oct 30, 2018 at 11:44:42AM +0100, Daniel Gustafsson wrote:

Circling back to this, I updated the patch with providing another
option as I couldn’t think of another way to do it cleanly. I’ll
add the patch to the next CF but as it’s just about to start it
should be moved to the next once started.

+-- Shouldn't log due to query being too fast
+SET auto_explain.log_level = NOTICE;
+SET auto_explain.log_min_duration = 1000;
+SELECT NULL FROM pg_catalog.pg_class WHERE relname = 'pg_class';
I am ready to be that 1s is not enough as some buildfarm machines are
legendary slow.  Honestly, to not have more to worry about I think
that this bit should be dropped.

"auto_explain.log_duration" should actually be named log_summary,
except that it defaults to true to be backward-compatible, while for
EXPLAIN the default is false, no? It would be nice to be consistent
with EXPLAIN for those options for the naming at least. The default
behavior of those parameters would be inconsistent as the duration is
showed by default with auto_explain and not with EXPLAIN, but it does
not seem like a good idea to change that..
--
Michael

#14Peter Eisentraut
peter.eisentraut@2ndquadrant.com
In reply to: Michael Paquier (#13)
Re: Allow auto_explain to log to NOTICE

On 26/12/2018 06:42, Michael Paquier wrote:

On Tue, Oct 30, 2018 at 11:44:42AM +0100, Daniel Gustafsson wrote:

Circling back to this, I updated the patch with providing another
option as I couldn’t think of another way to do it cleanly. I’ll
add the patch to the next CF but as it’s just about to start it
should be moved to the next once started.

Do we really want to add user-facing options just to be able to run
tests? Should we write the tests differently instead?

--
Peter Eisentraut http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

#15Michael Paquier
michael@paquier.xyz
In reply to: Peter Eisentraut (#14)
Re: Allow auto_explain to log to NOTICE

On Fri, Jan 04, 2019 at 01:06:24PM +0100, Peter Eisentraut wrote:

Do we really want to add user-facing options just to be able to run
tests? Should we write the tests differently instead?

The take is that the output of the plans generated includes data which
is run-dependent because the duration of the plan is generated
unconditionally. One way to write generic tests considering this
would be to use a TAP test, but I feel that's overdoing it just for
this case.

Being able to control if the plan duration shows up still looks like
an interesting option to me independently of adding tests.
--
Michael

#16Daniel Gustafsson
daniel@yesql.se
In reply to: Michael Paquier (#15)
Re: Allow auto_explain to log to NOTICE

(Michael: sorry for not having responded to your comments on the patch, $life
has had little time over for hacking lately)

On 4 Jan 2019, at 13:49, Michael Paquier <michael@paquier.xyz> wrote:

On Fri, Jan 04, 2019 at 01:06:24PM +0100, Peter Eisentraut wrote:

Do we really want to add user-facing options just to be able to run
tests? Should we write the tests differently instead?

I voiced this concern upthread as well, not being sure if it’s worth the added
complexity.

The take is that the output of the plans generated includes data which
is run-dependent because the duration of the plan is generated
unconditionally. One way to write generic tests considering this
would be to use a TAP test, but I feel that's overdoing it just for
this case.

Being able to control if the plan duration shows up still looks like
an interesting option to me independently of adding tests.

There is that. We might not be excited about writing tests for this contrib
module but someone else might want to use this for testing their application in
a similar manner to how pg_regress tests?

cheers ./daniel

#17Michael Paquier
michael@paquier.xyz
In reply to: Daniel Gustafsson (#16)
Re: Allow auto_explain to log to NOTICE

On Fri, Jan 04, 2019 at 02:45:55PM +0100, Daniel Gustafsson wrote:

(Michael: sorry for not having responded to your comments on the patch, $life
has had little time over for hacking lately)

No worries, I understand.

There is that. We might not be excited about writing tests for this
contrib module but someone else might want to use this for testing
their application in a similar manner to how pg_regress tests?

If we don't introduce it, there is no way to know. Still if somebody
needs to create regression tests they could just use EXPLAIN for the
same purpose. However, the option still seems useful to me to get out
plans with the most generic output, so I support the idea. If others
don't feel so, I am fine to give up as well.
--
Michael

#18Daniel Gustafsson
daniel@yesql.se
In reply to: Michael Paquier (#17)
Re: Allow auto_explain to log to NOTICE

On 5 Jan 2019, at 01:04, Michael Paquier <michael@paquier.xyz> wrote:

On Fri, Jan 04, 2019 at 02:45:55PM +0100, Daniel Gustafsson wrote:

(Michael: sorry for not having responded to your comments on the patch, $life
has had little time over for hacking lately)

No worries, I understand.

There is that. We might not be excited about writing tests for this
contrib module but someone else might want to use this for testing
their application in a similar manner to how pg_regress tests?

If we don't introduce it, there is no way to know. Still if somebody
needs to create regression tests they could just use EXPLAIN for the
same purpose. However, the option still seems useful to me to get out
plans with the most generic output, so I support the idea. If others
don't feel so, I am fine to give up as well.

This didn’t attract any other interested parties, and I don’t feel the added
complexity is worth a continued discussion, so I’m closing this patch. Thanks
for the review.

cheers ./daniel