aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backend/nodes/params.c2
-rw-r--r--src/backend/tcop/postgres.c57
-rw-r--r--src/backend/utils/mb/stringinfo_mb.c6
-rw-r--r--src/backend/utils/misc/guc.c34
-rw-r--r--src/backend/utils/misc/postgresql.conf.sample7
-rw-r--r--src/bin/pgbench/t/001_pgbench_with_server.pl93
-rw-r--r--src/include/utils/guc.h5
-rw-r--r--src/pl/plpgsql/src/pl_exec.c4
8 files changed, 160 insertions, 48 deletions
diff --git a/src/backend/nodes/params.c b/src/backend/nodes/params.c
index a57f9eea768..ed2ee6a975a 100644
--- a/src/backend/nodes/params.c
+++ b/src/backend/nodes/params.c
@@ -269,7 +269,7 @@ RestoreParamList(char **start_address)
* can contain NULLs for any unknown individual values. NULL can be given if
* no parameters are known.
*
- * If maxlen is not zero, that's the maximum number of bytes of any one
+ * If maxlen is >= 0, that's the maximum number of bytes of any one
* parameter value to be printed; an ellipsis is added if the string is
* longer. (Added quotes are not considered in this calculation.)
*/
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 5b677863b9c..8958ec8103e 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -1763,7 +1763,7 @@ exec_bind_message(StringInfo input_message)
*/
if (numParams > 0)
{
- char **knownTextValues = NULL; /* allocate on first use */
+ char **knownTextValues = NULL; /* allocate on first use */
params = makeParamList(numParams);
@@ -1833,26 +1833,39 @@ exec_bind_message(StringInfo input_message)
pval = OidInputFunctionCall(typinput, pstring, typioparam, -1);
/*
- * Free result of encoding conversion, if any, and save a copy
- * for later when logging parameters.
+ * If we might need to log parameters later, save a copy of
+ * the converted string in MessageContext; then free the
+ * result of encoding conversion, if any was done.
*/
if (pstring)
{
- if (log_parameters_on_error)
+ if (log_parameter_max_length_on_error != 0)
{
- MemoryContext oldcxt;
+ MemoryContext oldcxt;
oldcxt = MemoryContextSwitchTo(MessageContext);
+
if (knownTextValues == NULL)
knownTextValues =
palloc0(numParams * sizeof(char *));
- /*
- * Note: must copy at least two more full characters
- * than BuildParamLogString wants to see; otherwise it
- * might fail to include the ellipsis.
- */
- knownTextValues[paramno] =
- pnstrdup(pstring, 64 + 2 * MAX_MULTIBYTE_CHAR_LEN);
+
+ if (log_parameter_max_length_on_error < 0)
+ knownTextValues[paramno] = pstrdup(pstring);
+ else
+ {
+ /*
+ * We can trim the saved string, knowing that we
+ * won't print all of it. But we must copy at
+ * least two more full characters than
+ * BuildParamLogString wants to use; otherwise it
+ * might fail to include the trailing ellipsis.
+ */
+ knownTextValues[paramno] =
+ pnstrdup(pstring,
+ log_parameter_max_length_on_error
+ + 2 * MAX_MULTIBYTE_CHAR_LEN);
+ }
+
MemoryContextSwitchTo(oldcxt);
}
if (pstring != pbuf.data)
@@ -1909,13 +1922,15 @@ exec_bind_message(StringInfo input_message)
}
/*
- * Once all parameters have been received, prepare for printing them in
- * errors, if configured to do so. (This is saved in the portal, so
- * that they'll appear when the query is executed later.)
+ * Once all parameters have been received, prepare for printing them
+ * in errors, if configured to do so. (This is saved in the portal,
+ * so that they'll appear when the query is executed later.)
*/
- if (log_parameters_on_error)
+ if (log_parameter_max_length_on_error != 0)
params->paramValuesStr =
- BuildParamLogString(params, knownTextValues, 64);
+ BuildParamLogString(params,
+ knownTextValues,
+ log_parameter_max_length_on_error);
}
else
params = NULL;
@@ -2396,15 +2411,17 @@ errdetail_execute(List *raw_parsetree_list)
* errdetail_params
*
* Add an errdetail() line showing bind-parameter data, if available.
+ * Note that this is only used for statement logging, so it is controlled
+ * by log_parameter_max_length not log_parameter_max_length_on_error.
*/
static int
errdetail_params(ParamListInfo params)
{
- if (params && params->numParams > 0)
+ if (params && params->numParams > 0 && log_parameter_max_length != 0)
{
- char *str;
+ char *str;
- str = BuildParamLogString(params, NULL, 0);
+ str = BuildParamLogString(params, NULL, log_parameter_max_length);
if (str && str[0] != '\0')
errdetail("parameters: %s", str);
}
diff --git a/src/backend/utils/mb/stringinfo_mb.c b/src/backend/utils/mb/stringinfo_mb.c
index d7cf3751aa3..5f51f538c18 100644
--- a/src/backend/utils/mb/stringinfo_mb.c
+++ b/src/backend/utils/mb/stringinfo_mb.c
@@ -27,7 +27,7 @@
* appendStringInfoStringQuoted
*
* Append up to maxlen bytes from s to str, or the whole input string if
- * maxlen <= 0, adding single quotes around it and doubling all single quotes.
+ * maxlen < 0, adding single quotes around it and doubling all single quotes.
* Add an ellipsis if the copy is incomplete.
*/
void
@@ -43,9 +43,9 @@ appendStringInfoStringQuoted(StringInfo str, const char *s, int maxlen)
Assert(str != NULL);
slen = strlen(s);
- if (maxlen > 0 && maxlen < slen)
+ if (maxlen >= 0 && maxlen < slen)
{
- int finallen = pg_mbcliplen(s, slen, maxlen);
+ int finallen = pg_mbcliplen(s, slen, maxlen);
copy = pnstrdup(s, finallen);
chunk_search_start = copy;
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 79bc7ac8cac..64dc9fbd138 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -515,7 +515,6 @@ extern const struct config_enum_entry dynamic_shared_memory_options[];
* GUC option variables that are exported from this module
*/
bool log_duration = false;
-bool log_parameters_on_error = false;
bool Debug_print_plan = false;
bool Debug_print_parse = false;
bool Debug_print_rewritten = false;
@@ -544,6 +543,8 @@ int log_min_messages = WARNING;
int client_min_messages = NOTICE;
int log_min_duration_sample = -1;
int log_min_duration_statement = -1;
+int log_parameter_max_length = -1;
+int log_parameter_max_length_on_error = 0;
int log_temp_files = -1;
double log_statement_sample_rate = 1.0;
double log_xact_sample_rate = 0;
@@ -1382,15 +1383,6 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"log_parameters_on_error", PGC_SUSET, LOGGING_WHAT,
- gettext_noop("Logs bind parameters of the logged statements where possible."),
- NULL
- },
- &log_parameters_on_error,
- false,
- NULL, NULL, NULL
- },
- {
{"debug_print_parse", PGC_USERSET, LOGGING_WHAT,
gettext_noop("Logs each query's parse tree."),
NULL
@@ -2856,6 +2848,28 @@ static struct config_int ConfigureNamesInt[] =
},
{
+ {"log_parameter_max_length", PGC_SUSET, LOGGING_WHAT,
+ gettext_noop("When logging statements, limit logged parameter values to first N bytes."),
+ gettext_noop("-1 to print values in full."),
+ GUC_UNIT_BYTE
+ },
+ &log_parameter_max_length,
+ -1, -1, INT_MAX / 2,
+ NULL, NULL, NULL
+ },
+
+ {
+ {"log_parameter_max_length_on_error", PGC_USERSET, LOGGING_WHAT,
+ gettext_noop("When reporting an error, limit logged parameter values to first N bytes."),
+ gettext_noop("-1 to print values in full."),
+ GUC_UNIT_BYTE
+ },
+ &log_parameter_max_length_on_error,
+ 0, -1, INT_MAX / 2,
+ NULL, NULL, NULL
+ },
+
+ {
{"bgwriter_delay", PGC_SIGHUP, RESOURCES_BGWRITER,
gettext_noop("Background writer sleep time between rounds."),
NULL,
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index 7de4adc2ff1..e904fa7300f 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -545,8 +545,13 @@
# %% = '%'
# e.g. '<%u%%%d> '
#log_lock_waits = off # log lock waits >= deadlock_timeout
+#log_parameter_max_length = -1 # when logging statements, limit logged
+ # bind-parameter values to N bytes;
+ # -1 means print in full, 0 disables
+#log_parameter_max_length_on_error = 0 # when logging an error, limit logged
+ # bind-parameter values to N bytes;
+ # -1 means print in full, 0 disables
#log_statement = 'none' # none, ddl, mod, all
-#log_parameters_on_error = off # on error log statements with bind parameters
#log_replication_commands = off
#log_temp_files = -1 # log temporary files equal or larger
# than the specified size in kilobytes;
diff --git a/src/bin/pgbench/t/001_pgbench_with_server.pl b/src/bin/pgbench/t/001_pgbench_with_server.pl
index 25ea17f7d11..b85a3ac32dd 100644
--- a/src/bin/pgbench/t/001_pgbench_with_server.pl
+++ b/src/bin/pgbench/t/001_pgbench_with_server.pl
@@ -270,8 +270,39 @@ COMMIT;
}
});
-# Verify server logging of parameters.
-$node->append_conf('postgresql.conf', "log_parameters_on_error = true");
+# Verify server logging of query parameters.
+# (This doesn't really belong here, but pgbench is a convenient way
+# to issue commands using extended query mode with parameters.)
+
+# 1. Logging neither with errors nor with statements
+$node->append_conf('postgresql.conf',
+ "log_min_duration_statement = 0\n" .
+ "log_parameter_max_length = 0\n" .
+ "log_parameter_max_length_on_error = 0");
+$node->reload;
+pgbench(
+ '-n -t1 -c1 -M prepared',
+ 2,
+ [],
+ [
+ qr{ERROR: invalid input syntax for type json},
+ qr{(?!extended query with parameters)}
+ ],
+ 'server parameter logging',
+ {
+ '001_param_1' => q[select '{ invalid ' as value \gset
+select $$'Valame Dios!' dijo Sancho; 'no le dije yo a vuestra merced que mirase bien lo que hacia?'$$ as long \gset
+select column1::jsonb from (values (:value), (:long)) as q;
+]
+ });
+my $log = TestLib::slurp_file($node->logfile);
+unlike($log, qr[DETAIL: parameters: \$1 = '\{ invalid ',], "no parameters logged");
+$log = undef;
+
+# 2. Logging truncated parameters on error, full with statements
+$node->append_conf('postgresql.conf',
+ "log_parameter_max_length = -1\n" .
+ "log_parameter_max_length_on_error = 64");
$node->reload;
pgbench(
'-n -t1 -c1 -M prepared',
@@ -283,13 +314,10 @@ pgbench(
],
'server parameter logging',
{
- '001_param_1' => q{select '1' as one \gset
+ '001_param_2' => q{select '1' as one \gset
SELECT 1 / (random() / 2)::int, :one::int, :two::int;
}
});
-
-$node->append_conf('postgresql.conf', "log_min_duration_statement = 0");
-$node->reload;
pgbench(
'-n -t1 -c1 -M prepared',
2,
@@ -300,17 +328,64 @@ pgbench(
],
'server parameter logging',
{
- '001_param_2' => q[select '{ invalid ' as value \gset
+ '001_param_3' => q[select '{ invalid ' as value \gset
select $$'Valame Dios!' dijo Sancho; 'no le dije yo a vuestra merced que mirase bien lo que hacia?'$$ as long \gset
select column1::jsonb from (values (:value), (:long)) as q;
]
});
-my $log = TestLib::slurp_file($node->logfile);
+$log = TestLib::slurp_file($node->logfile);
like($log, qr[DETAIL: parameters: \$1 = '\{ invalid ', \$2 = '''Valame Dios!'' dijo Sancho; ''no le dije yo a vuestra merced que mirase bien lo que hacia\?'''],
"parameter report does not truncate");
$log = undef;
-$node->append_conf('postgresql.conf', "log_min_duration_statement = -1");
+# 3. Logging full parameters on error, truncated with statements
+$node->append_conf('postgresql.conf',
+ "log_min_duration_statement = -1\n" .
+ "log_parameter_max_length = 7\n" .
+ "log_parameter_max_length_on_error = -1");
+$node->reload;
+pgbench(
+ '-n -t1 -c1 -M prepared',
+ 2,
+ [],
+ [
+ qr{ERROR: division by zero},
+ qr{CONTEXT: extended query with parameters: \$1 = '1', \$2 = NULL}
+ ],
+ 'server parameter logging',
+ {
+ '001_param_4' => q{select '1' as one \gset
+SELECT 1 / (random() / 2)::int, :one::int, :two::int;
+}
+ });
+
+$node->append_conf('postgresql.conf', "log_min_duration_statement = 0");
+$node->reload;
+pgbench(
+ '-n -t1 -c1 -M prepared',
+ 2,
+ [],
+ [
+ qr{ERROR: invalid input syntax for type json},
+ qr[CONTEXT: JSON data, line 1: \{ invalid\.\.\.[\r\n]+extended query with parameters: \$1 = '\{ invalid ', \$2 = '''Valame Dios!'' dijo Sancho; ''no le dije yo a vuestra merced que mirase bien lo que hacia\?']m
+ ],
+ 'server parameter logging',
+ {
+ '001_param_5' => q[select '{ invalid ' as value \gset
+select $$'Valame Dios!' dijo Sancho; 'no le dije yo a vuestra merced que mirase bien lo que hacia?'$$ as long \gset
+select column1::jsonb from (values (:value), (:long)) as q;
+]
+ });
+$log = TestLib::slurp_file($node->logfile);
+like($log, qr[DETAIL: parameters: \$1 = '\{ inval\.\.\.', \$2 = '''Valame\.\.\.'],
+ "parameter report truncates");
+$log = undef;
+
+# Restore default logging config
+$node->append_conf('postgresql.conf',
+ "log_min_duration_statement = -1\n" .
+ "log_parameter_max_length_on_error = 0\n" .
+ "log_parameter_max_length = -1");
$node->reload;
# test expressions
diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h
index ce93ace76c0..28192821816 100644
--- a/src/include/utils/guc.h
+++ b/src/include/utils/guc.h
@@ -233,8 +233,6 @@ typedef enum
/* GUC vars that are actually declared in guc.c, rather than elsewhere */
-extern bool log_duration;
-extern bool log_parameters_on_error;
extern bool Debug_print_plan;
extern bool Debug_print_parse;
extern bool Debug_print_rewritten;
@@ -249,6 +247,9 @@ extern bool log_btree_build_stats;
extern PGDLLIMPORT bool check_function_bodies;
extern bool session_auth_is_superuser;
+extern bool log_duration;
+extern int log_parameter_max_length;
+extern int log_parameter_max_length_on_error;
extern int log_min_error_statement;
extern PGDLLIMPORT int log_min_messages;
extern PGDLLIMPORT int client_min_messages;
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index 1c40709b3c9..aeb6c8fefc2 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -8771,7 +8771,7 @@ format_expr_params(PLpgSQL_execstate *estate,
convert_value_to_string(estate,
paramdatum,
paramtypeid),
- 0);
+ -1);
paramno++;
}
@@ -8813,7 +8813,7 @@ format_preparedparamsdata(PLpgSQL_execstate *estate,
convert_value_to_string(estate,
ppd->values[paramno],
ppd->types[paramno]),
- 0);
+ -1);
}
MemoryContextSwitchTo(oldcontext);