diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/backend/nodes/params.c | 2 | ||||
-rw-r--r-- | src/backend/tcop/postgres.c | 57 | ||||
-rw-r--r-- | src/backend/utils/mb/stringinfo_mb.c | 6 | ||||
-rw-r--r-- | src/backend/utils/misc/guc.c | 34 | ||||
-rw-r--r-- | src/backend/utils/misc/postgresql.conf.sample | 7 | ||||
-rw-r--r-- | src/bin/pgbench/t/001_pgbench_with_server.pl | 93 | ||||
-rw-r--r-- | src/include/utils/guc.h | 5 | ||||
-rw-r--r-- | src/pl/plpgsql/src/pl_exec.c | 4 |
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); |