Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1nzR3n-0007mf-Dg for pgsql-hackers@arkaria.postgresql.org; Thu, 09 Jun 2022 22:55:28 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.92) (envelope-from ) id 1nzR3m-0000Kb-0v for pgsql-hackers@arkaria.postgresql.org; Thu, 09 Jun 2022 22:55:26 +0000 Received: from magus.postgresql.org ([2a02:c0:301:0:ffff::29]) by malur.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1nzR3k-0000KS-L4 for pgsql-hackers@lists.postgresql.org; Thu, 09 Jun 2022 22:55:25 +0000 Received: from out4-smtp.messagingengine.com ([66.111.4.28]) by magus.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1nzR3f-0006jq-P3 for pgsql-hackers@lists.postgresql.org; Thu, 09 Jun 2022 22:55:23 +0000 Received: from compute1.internal (compute1.nyi.internal [10.202.2.41]) by mailout.nyi.internal (Postfix) with ESMTP id 868695C00E6; Thu, 9 Jun 2022 18:55:14 -0400 (EDT) Received: from mailfrontend1 ([10.202.2.162]) by compute1.internal (MEProxy); Thu, 09 Jun 2022 18:55:14 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=ilmari.org; h=cc :cc:content-type:date:date:from:from:in-reply-to:in-reply-to :message-id:mime-version:references:reply-to:sender:subject :subject:to:to; s=fm3; t=1654815314; x=1654901714; bh=ujrpb7vE5y EZ3yH6Eto/2HMDV4tbbRvWvzpY1r341yA=; b=AxGJJrdJAJW4nzkSaTuTCzFlJr C9VM4UC3g05BsGAFEkigHnduyLiWMEHlcrxg1gLNJGVCoehp9CM6+hoqV+wco00T laShUR69wH83cUjGc0JKRkZQ09sXMxtvgL8I1vgG283TPHfXd9ajKakNIqaNgI9f 9eJS1kQwvBezdbxK0+kuxjIvCZI6k+NXcRgcqxNNhVEVG8QultQp5i508kcMFgEE oPCs2WqKGxyZt4kAg7KPUCTlxMq8I5CSCn/czHtx6iUer/aSZh/1BMqPDH3JGtuJ jcx6LBrqzQMguQWKIi+5vPkAh8HVzq5lEWZDtxz7EobL4ct6HWk34I0NOgJQ== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc:cc:content-type:date:date:feedback-id :feedback-id:from:from:in-reply-to:in-reply-to:message-id :mime-version:references:reply-to:sender:subject:subject:to:to :x-me-proxy:x-me-proxy:x-me-sender:x-me-sender:x-sasl-enc; s= fm2; t=1654815314; x=1654901714; bh=ujrpb7vE5yEZ3yH6Eto/2HMDV4tb bRvWvzpY1r341yA=; b=cL0VJTaPI8zF6czkoJjMHfcpmRDrzpwTqVaHT4zwZO4o H1DMuGpcPAsiZXLRbhDlPcYtWU5KW4wLhMXwsNqsEq4rE4XS8Sf5162UeZgXyZdx FVvF+1KegeJkIGpmB/jA1sCD9b9KSlfV9nOg6rffHiAqioSuCZaKHeBeXUVN4I5p iQj1PMxNYzPjxdVBrmZ5jZUjA84HRZlGJTLcJP01zhEJCtD15yi75daf4ewB44MZ TOaYVmRcQ/XUBKD03Snvo9eQ8vqhxh13U1iU38iu8LarXXpxkyIUMMHyZD9PK05+ eZ+csdeWOEZjKlOtKPjcFDkRKRNgUKufB8Wx/+L/UQ== X-ME-Sender: X-ME-Received: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgedvfedruddutddgudehucetufdoteggodetrfdotf fvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdfqfgfvpdfurfetoffkrfgpnffqhgen uceurghilhhouhhtmecufedttdenucesvcftvggtihhpihgvnhhtshculddquddttddmne gfrhhlucfvnfffucdlfeehmdenucfjughrpefhvfevufhfffgjkfgfgggtsehmtderredt reejnecuhfhrohhmpeffrghgfhhinhhnpgfklhhmrghrihgpofgrnhhnshonkhgvrhcuoe hilhhmrghrihesihhlmhgrrhhirdhorhhgqeenucggtffrrghtthgvrhhnpeeugfefudeu tdejueehteejueekhfejtdevheejlefhudetvdeiledtvdeuvdduvdenucevlhhushhtvg hrufhiiigvpedtnecurfgrrhgrmhepmhgrihhlfhhrohhmpehilhhmrghrihesihhlmhgr rhhirdhorhhg X-ME-Proxy: Feedback-ID: i1ff147bf:Fastmail Received: by mail.messagingengine.com (Postfix) with ESMTPA; Thu, 9 Jun 2022 18:55:13 -0400 (EDT) From: =?utf-8?Q?Dagfinn_Ilmari_Manns=C3=A5ker?= To: Michael Paquier Cc: pgsql-hackers@lists.postgresql.org Subject: Re: Logging query parmeters in auto_explain References: <87ee09mohb.fsf@wibble.ilmari.org> <8735ggn2lf.fsf@wibble.ilmari.org> Date: Thu, 09 Jun 2022 23:55:11 +0100 In-Reply-To: (Michael Paquier's message of "Thu, 9 Jun 2022 10:44:13 +0900") Message-ID: <87tu8tla5s.fsf@wibble.ilmari.org> User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/27.1 (gnu/linux) MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk --=-=-= Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit Michael Paquier writes: > On Tue, Jun 07, 2022 at 12:18:52PM +0100, Dagfinn Ilmari Mannsåker wrote: >> Point, fixed in the attached v2. I've also added a test that truncation >> and disabling works. > > The tests are structured so as all the queries are run first, then the > full set of logs is slurped and scanned. With things like tests for > truncation, it becomes easier to have a given test overlap with > another one in terms of pattern matching, so we could silently lose > coverage. Wouldn't it be better to reorganize things so we save the > current size of the log file (as of -s $node->logfile), run one query, > and then slurp the log file based on the position saved previously? > The GUC updates had better be localized in each sub-section of the > tests, as well. Done (and more tests added), v3 attached. - ilmari --=-=-= Content-Type: text/x-diff Content-Disposition: inline; filename=v3-0001-Log-query-parameters-in-auto_explain.patch From de3627d972f5259c9290109a6d7b08fbfa1faea2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dagfinn=20Ilmari=20Manns=C3=A5ker?= Date: Tue, 31 May 2022 21:12:12 +0100 Subject: [PATCH v3] Log query parameters in auto_explain Add an auto_explain.log_parameter_max_length config setting, similar to the corresponding core setting, that controls the inclusion of query parameters in the logged explain output. Also adjust the tests to only look at the relevant parts of the log for each query, and tho reset the GUCs after each test. --- contrib/auto_explain/auto_explain.c | 15 +++ contrib/auto_explain/t/001_auto_explain.pl | 133 ++++++++++++++++++--- doc/src/sgml/auto-explain.sgml | 19 +++ src/backend/commands/explain.c | 22 ++++ src/include/commands/explain.h | 1 + 5 files changed, 176 insertions(+), 14 deletions(-) diff --git a/contrib/auto_explain/auto_explain.c b/contrib/auto_explain/auto_explain.c index c9a0d947c8..1ba7536879 100644 --- a/contrib/auto_explain/auto_explain.c +++ b/contrib/auto_explain/auto_explain.c @@ -19,12 +19,14 @@ #include "common/pg_prng.h" #include "executor/instrument.h" #include "jit/jit.h" +#include "nodes/params.h" #include "utils/guc.h" PG_MODULE_MAGIC; /* GUC variables */ static int auto_explain_log_min_duration = -1; /* msec or -1 */ +static int auto_explain_log_parameter_max_length = -1; /* bytes or -1 */ static bool auto_explain_log_analyze = false; static bool auto_explain_log_verbose = false; static bool auto_explain_log_buffers = false; @@ -105,6 +107,18 @@ _PG_init(void) NULL, NULL); + DefineCustomIntVariable("auto_explain.log_parameter_max_length", + "Sets the maximum length of query parameters to log.", + "Zero logs no query parameters, -1 logs them in full.", + &auto_explain_log_parameter_max_length, + -1, + -1, INT_MAX, + PGC_SUSET, + GUC_UNIT_BYTE, + NULL, + NULL, + NULL); + DefineCustomBoolVariable("auto_explain.log_analyze", "Use EXPLAIN ANALYZE for plan logging.", NULL, @@ -389,6 +403,7 @@ explain_ExecutorEnd(QueryDesc *queryDesc) ExplainBeginOutput(es); ExplainQueryText(es, queryDesc); + ExplainQueryParameters(es, queryDesc->params, auto_explain_log_parameter_max_length); ExplainPrintPlan(es, queryDesc); if (es->analyze && auto_explain_log_triggers) ExplainPrintTriggers(es, queryDesc); diff --git a/contrib/auto_explain/t/001_auto_explain.pl b/contrib/auto_explain/t/001_auto_explain.pl index 82e4d9d15c..4fa1a8457b 100644 --- a/contrib/auto_explain/t/001_auto_explain.pl +++ b/contrib/auto_explain/t/001_auto_explain.pl @@ -16,42 +16,147 @@ $node->append_conf('postgresql.conf', "auto_explain.log_analyze = on"); $node->start; +sub query_log +{ + my ($params, $sql) = @_; + + if (keys %$params) + { + for my $key (keys %$params) + { + $node->append_conf('postgresql.conf', "$key = $params->{$key}\n"); + } + $node->reload; + } + + my $log = $node->logfile(); + my $offset = -s $log; + + $node->safe_psql("postgres", $sql); + + my $log_contents = slurp_file($log, $offset); + + if (keys %$params) + { + for my $key (keys %$params) + { + $node->adjust_conf('postgresql.conf', $key, undef); + } + $node->reload; + } + + return $log_contents; +} + # run a couple of queries -$node->safe_psql("postgres", "SELECT * FROM pg_class;"); -$node->safe_psql("postgres", - "SELECT * FROM pg_proc WHERE proname = 'int4pl';"); +my $log_contents = query_log({}, "SELECT * FROM pg_class;"); -# emit some json too -$node->append_conf('postgresql.conf', "auto_explain.log_format = json"); -$node->reload; -$node->safe_psql("postgres", "SELECT * FROM pg_proc;"); -$node->safe_psql("postgres", - "SELECT * FROM pg_class WHERE relname = 'pg_class';"); +like( + $log_contents, + qr/Query Text: SELECT \* FROM pg_class;/, + "query text logged, text mode"); -$node->stop('fast'); - -my $log = $node->logfile(); - -my $log_contents = slurp_file($log); +unlike( + $log_contents, + qr/Query Parameters:/, + "no query parameters logged when none, text mode"); like( $log_contents, qr/Seq Scan on pg_class/, "sequential scan logged, text mode"); +# with parameters +$log_contents = query_log({}, + q{PREPARE get_proc(name) AS SELECT * FROM pg_proc WHERE proname = $1; EXECUTE get_proc('int4pl');} +); + +like( + $log_contents, + qr/Query Text: PREPARE get_proc\(name\) AS SELECT \* FROM pg_proc WHERE proname = \$1;/, + "prepared query text logged, text mode"); + +like( + $log_contents, + qr/Query Parameters: \$1 = 'int4pl'/, + "query parameters logged, text mode"); + like( $log_contents, qr/Index Scan using pg_proc_proname_args_nsp_index on pg_proc/, "index scan logged, text mode"); +# check that logged parameters can be truncated +$log_contents = query_log( + { "auto_explain.log_parameter_max_length" => 3 }, + q{PREPARE get_type(name) AS SELECT * FROM pg_type WHERE typname = $1; EXECUTE get_type('float8');}, +); + +like( + $log_contents, + qr/Query Text: PREPARE get_type\(name\) AS SELECT \* FROM pg_type WHERE typname = \$1;/, + "prepared query text logged, text mode"); + +like( + $log_contents, + qr/Query Parameters: \$1 = 'flo\.\.\.'/, + "query parameters truncated, text mode"); + +# check that parameter logging can be disabled +$log_contents = query_log( + { "auto_explain.log_parameter_max_length" => 0 }, + q{PREPARE get_type(name) AS SELECT * FROM pg_type WHERE typname = $1; EXECUTE get_type('float8');}, +); + +like( + $log_contents, + qr/Query Text: PREPARE get_type\(name\) AS SELECT \* FROM pg_type WHERE typname = \$1;/, + "prepared query text logged, text mode"); + +unlike( + $log_contents, + qr/Query Parameters:/, + "query parameters not logged when disabled, text mode"); + +# emit some json too +$log_contents = query_log({ "auto_explain.log_format" => "json" }, + "SELECT * FROM pg_proc;"); + +like( + $log_contents, + qr/"Query Text": "SELECT \* FROM pg_proc;"/, + "query text logged, json mode"); + +unlike( + $log_contents, + qr/"Query Parameters":/, + "query parameters not logged when none, json mode"); + like( $log_contents, qr/"Node Type": "Seq Scan"[^}]*"Relation Name": "pg_proc"/s, "sequential scan logged, json mode"); +$log_contents = query_log( + { "auto_explain.log_format" => "json" }, + q{PREPARE get_class(name) AS SELECT * FROM pg_class WHERE relname = $1; EXECUTE get_class('pg_class');}, +); + +like( + $log_contents, + qr/"Query Text": "PREPARE get_class\(name\) AS SELECT \* FROM pg_class WHERE relname = \$1;"/, + "prepared query text logged, json mode"); + +like( + $log_contents, + qr/"Query Parameters": "\$1 = 'pg_class'"/, + "query parameters logged, json mode"); + like( $log_contents, qr/"Node Type": "Index Scan"[^}]*"Index Name": "pg_class_relname_nsp_index"/s, "index scan logged, json mode"); +$node->stop('fast'); + done_testing(); diff --git a/doc/src/sgml/auto-explain.sgml b/doc/src/sgml/auto-explain.sgml index 30e35a714a..a033c9c685 100644 --- a/doc/src/sgml/auto-explain.sgml +++ b/doc/src/sgml/auto-explain.sgml @@ -63,6 +63,25 @@ + + + auto_explain.log_parameter_max_length (integer) + + auto_explain.log_parameter_max_length configuration parameter + + + + + auto_explain.log_parameter_max_length controls the + logging of query parameter values. A value of-1 (the + default) logs the parameter values in full. 0 disables + logging of parameter values. A value greater than zero truncates each + parameter value to that many bytes. Only superusers can change this + setting. + + + + auto_explain.log_analyze (boolean) diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c index 5d1f7089da..8584e3585d 100644 --- a/src/backend/commands/explain.c +++ b/src/backend/commands/explain.c @@ -972,6 +972,28 @@ ExplainQueryText(ExplainState *es, QueryDesc *queryDesc) ExplainPropertyText("Query Text", queryDesc->sourceText, es); } +/* + * ExplainQueryParameters - + * add a "Query Parameters" node that describes the parameters of the query, + * if any + * + * The caller should have set up the options fields of *es, as well as + * initializing the output buffer es->str. + * + */ +void +ExplainQueryParameters(ExplainState *es, ParamListInfo params, int maxlen) +{ + char *str; + + if (params == NULL || params->numParams == 0 || maxlen == 0) + return; + + str = BuildParamLogString(params, NULL, maxlen); + if (str && str[0] != '\0') + ExplainPropertyText("Query Parameters", str, es); +} + /* * report_triggers - * report execution stats for a single relation's triggers diff --git a/src/include/commands/explain.h b/src/include/commands/explain.h index 666977fb1f..9ebde089ae 100644 --- a/src/include/commands/explain.h +++ b/src/include/commands/explain.h @@ -99,6 +99,7 @@ extern void ExplainPrintTriggers(ExplainState *es, QueryDesc *queryDesc); extern void ExplainPrintJITSummary(ExplainState *es, QueryDesc *queryDesc); extern void ExplainQueryText(ExplainState *es, QueryDesc *queryDesc); +extern void ExplainQueryParameters(ExplainState *es, ParamListInfo params, int maxlen); extern void ExplainBeginOutput(ExplainState *es); extern void ExplainEndOutput(ExplainState *es); -- 2.30.2 --=-=-=--