From df9d34af592dea2e791820a93aff1101667bbf0e Mon Sep 17 00:00:00 2001 From: Jelte Fennema-Nio Date: Wed, 10 Jan 2024 12:10:19 +0100 Subject: [PATCH v10 13/13] Add _pq_.report_parameters protocol extension Connection poolers use the ParameterStatus message to track changes in session level parameters. Before assinging a server connection to a client, the pooler will first restore the server parameters that the client expects. This is done to emulate session level SET commands in transaction pooling mode. Previously this could only be done for a hard-coded set of backend parameters, but with this change a connection pooler can choose for which backend parameters it wants to receive status changes. It can do this by setting the newly added `_pq_.report_parameters` protocol extension parameter to a list of parameter names. --- doc/src/sgml/config.sgml | 33 ++++++ doc/src/sgml/libpq.sgml | 4 +- doc/src/sgml/protocol.sgml | 8 +- src/backend/utils/misc/guc.c | 102 +++++++++++++++++- src/backend/utils/misc/guc_tables.c | 34 +++--- src/include/utils/guc.h | 1 + src/include/utils/guc_hooks.h | 2 + src/interfaces/libpq/fe-connect.c | 4 + src/interfaces/libpq/fe-protocol3.c | 3 + src/interfaces/libpq/libpq-int.h | 1 + .../modules/libpq_pipeline/libpq_pipeline.c | 73 +++++++++++++ .../libpq_pipeline/t/001_libpq_pipeline.pl | 2 +- .../traces/report_parameters.trace | 31 ++++++ 13 files changed, 277 insertions(+), 21 deletions(-) create mode 100644 src/test/modules/libpq_pipeline/traces/report_parameters.trace diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index f7a44ec7c61..eb0b7e3ab0b 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -11261,6 +11261,39 @@ dynamic_library_path = 'C:\tools\postgresql;H:\my_project\lib;$libdir' + + _pq_.report_parameters (string) + + _pq_.report_parameters configuration parameter + + + + + This parameter specifies for which backend parameters the server + uses the ParameterStatus message to report their value on connection + startup and whenever their value changes. Changing this parameter can + be useful to limit unnecessary network traffic for clients that don't + care about all of the the parameters that are reported by default. Or + it can be used to report changes for additional parameters that are not + reported by default. The parameters that are in this list by default + are + server_version, + server_encoding, + client_encoding, + application_name, + default_transaction_read_only, + in_hot_standby, + is_superuser, + session_authorization, + DateStyle, + IntervalStyle, + TimeZone, + integer_datetimes, and + standard_conforming_strings. + + + + diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml index dd58a8cabab..91cdf4645d9 100644 --- a/doc/src/sgml/libpq.sgml +++ b/doc/src/sgml/libpq.sgml @@ -2519,7 +2519,9 @@ const char *PQparameterStatus(const PGconn *conn, const char *paramName); - Parameters reported as of the current release include: + It's possible to choose for which parameter values are reported to the client + using . + The parameters that are reported by default as of the current release include: application_name client_encoding diff --git a/doc/src/sgml/protocol.sgml b/doc/src/sgml/protocol.sgml index fdab4e8e287..b83f2b806a3 100644 --- a/doc/src/sgml/protocol.sgml +++ b/doc/src/sgml/protocol.sgml @@ -1336,8 +1336,9 @@ SELCT 1/0; - At present there is a hard-wired set of parameters for which - ParameterStatus will be generated. They are: + It's possible to choose for which parameter values are reported to the + client using . The default + parameters for which ParameterStatus will be generated are: application_name client_encoding @@ -1363,7 +1364,8 @@ SELCT 1/0; server_encoding and integer_datetimes are pseudo-parameters that cannot change after startup. - This set might change in the future, or even become configurable. + Since this set is configurable, a frontend can not assume that it only + gets these specific defaults. Accordingly, a frontend should simply ignore ParameterStatus for parameters that it does not understand or care about. diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index 72a4273222b..f3cb17314d2 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -2622,8 +2622,16 @@ ReportChangedGUCOptions(void) struct config_generic *conf = slist_container(struct config_generic, report_link, iter.cur); - Assert((conf->flags & GUC_REPORT) && (conf->status & GUC_NEEDS_REPORT)); - ReportGUCOption(conf); + Assert(conf->status & GUC_NEEDS_REPORT); + + /* + * The GUC_REPORT is usually set if we reach here, but it's possible + * that it was cleared just before due to a change in the value of + * _pq_.report_parameter. + */ + if (conf->flags & GUC_REPORT) + ReportGUCOption(conf); + conf->status &= ~GUC_NEEDS_REPORT; slist_delete_current(&iter); } @@ -7070,3 +7078,93 @@ assign_protocol_managed_params(const char *newval, void *extra) pfree(protocol_params_str); list_free(namelist); } + + +/* + * GUC check_hook for report_parameters + */ +bool +check_report_parameters(char **newval, void **extra, GucSource source) +{ + List *namelist; + char *protocol_params_str = pstrdup(*newval); + + if (!SplitIdentifierString(protocol_params_str, ',', &namelist)) + { + /* syntax error in name list */ + GUC_check_errdetail("List syntax is invalid."); + pfree(protocol_params_str); + list_free(namelist); + return false; + } + + /* + * We explicitly allow unknown parameters here (but we still warn for + * them). So that it is possible to add version specific parameters to the + * report_parameters list in the StartupMessage without knowing the + * current server version yet. + */ + foreach_ptr(char, pname, namelist) + { + find_option(pname, false, false, WARNING); + } + + pfree(protocol_params_str); + list_free(namelist); + return true; +} + +/* + * GUC check_hook for report_parameters + */ +void +assign_report_parameters(const char *newval, void *extra) +{ + List *namelist; + char *old_protocol_params_str = pstrdup(report_parameters); + char *protocol_params_str = pstrdup(newval); + + if (!SplitIdentifierString(old_protocol_params_str, ',', &namelist)) + { + elog(ERROR, "List syntax is invalid and check hook should have checked."); + } + + foreach_ptr(char, pname, namelist) + { + struct config_generic *config = find_option(pname, false, true, ERROR); + + if (config) + config->flags &= ~GUC_REPORT; + } + + list_free(namelist); + + if (!SplitIdentifierString(protocol_params_str, ',', &namelist)) + { + elog(ERROR, "List syntax is invalid and check hook should have checked."); + } + + foreach_ptr(char, pname, namelist) + { + struct config_generic *config = find_option(pname, false, true, ERROR); + + if (!config) + continue; + + config->flags |= GUC_REPORT; + + /* force a report of this GUC */ + guc_free(config->last_reported); + config->last_reported = NULL; + if (!(config->status & GUC_NEEDS_REPORT) && IsNormalProcessingMode()) + { + config->status |= GUC_NEEDS_REPORT; + slist_push_head(&guc_report_list, &config->report_link); + } + + } + + pfree(old_protocol_params_str); + pfree(protocol_params_str); + list_free(namelist); +} diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 5ce3ae7153d..16dbeb9b7f8 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -495,6 +495,7 @@ extern const struct config_enum_entry dynamic_shared_memory_options[]; * GUC option variables that are exported from this module */ char *protocol_managed_params = ""; +char *report_parameters = "client_encoding,DateStyle,server_encoding,server_version,session_authorization,TimeZone,application_name,IntervalStyle,is_superuser,default_transaction_read_only,integer_datetimes,standard_conforming_strings,in_hot_standby,scram_iterations"; bool log_duration = false; bool Debug_print_plan = false; @@ -1038,7 +1039,7 @@ struct config_bool ConfigureNamesBool[] = {"is_superuser", PGC_INTERNAL, UNGROUPED, gettext_noop("Shows whether the current user is a superuser."), NULL, - GUC_REPORT | GUC_NO_SHOW_ALL | GUC_NO_RESET_ALL | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE + GUC_NO_SHOW_ALL | GUC_NO_RESET_ALL | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE }, ¤t_role_is_superuser, false, @@ -1543,7 +1544,6 @@ struct config_bool ConfigureNamesBool[] = {"default_transaction_read_only", PGC_USERSET, CLIENT_CONN_STATEMENT, gettext_noop("Sets the default read-only status of new transactions."), NULL, - GUC_REPORT }, &DefaultXactReadOnly, false, @@ -1701,7 +1701,7 @@ struct config_bool ConfigureNamesBool[] = {"integer_datetimes", PGC_INTERNAL, PRESET_OPTIONS, gettext_noop("Shows whether datetimes are integer based."), NULL, - GUC_REPORT | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE + GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE }, &integer_datetimes, true, @@ -1742,7 +1742,6 @@ struct config_bool ConfigureNamesBool[] = {"standard_conforming_strings", PGC_USERSET, COMPAT_OPTIONS_PREVIOUS, gettext_noop("Causes '...' strings to treat backslashes literally."), NULL, - GUC_REPORT }, &standard_conforming_strings, true, @@ -1803,7 +1802,7 @@ struct config_bool ConfigureNamesBool[] = {"in_hot_standby", PGC_INTERNAL, PRESET_OPTIONS, gettext_noop("Shows whether hot standby is currently active."), NULL, - GUC_REPORT | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE + GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE }, &in_hot_standby_guc, false, @@ -3623,7 +3622,6 @@ struct config_int ConfigureNamesInt[] = {"scram_iterations", PGC_USERSET, CONN_AUTH_AUTH, gettext_noop("Sets the iteration count for SCRAM secret generation."), NULL, - GUC_REPORT }, &scram_sha_256_iterations, SCRAM_SHA_256_DEFAULT_ITERATIONS, 1, INT_MAX, @@ -3930,6 +3928,16 @@ struct config_string ConfigureNamesString[] = "", check_protocol_managed_params, assign_protocol_managed_params, NULL }, + { + {"_pq_.report_parameters", PGC_PROTOCOL, PROTOCOL_EXTENSION, + gettext_noop("List of parameters for which changes should be reported using ParameterStatus."), + NULL, + GUC_LIST_INPUT | GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE + }, + &report_parameters, + "client_encoding,DateStyle,server_encoding,server_version,session_authorization,TimeZone,application_name,IntervalStyle,is_superuser,default_transaction_read_only,integer_datetimes,standard_conforming_strings,in_hot_standby,scram_iterations", + check_report_parameters, assign_report_parameters, NULL + }, { {"archive_command", PGC_SIGHUP, WAL_ARCHIVING, gettext_noop("Sets the shell command that will be called to archive a WAL file."), @@ -4061,7 +4069,7 @@ struct config_string ConfigureNamesString[] = {"client_encoding", PGC_USERSET, CLIENT_CONN_LOCALE, gettext_noop("Sets the client's character set encoding."), NULL, - GUC_IS_NAME | GUC_REPORT + GUC_IS_NAME }, &client_encoding_string, "SQL_ASCII", @@ -4093,7 +4101,7 @@ struct config_string ConfigureNamesString[] = gettext_noop("Sets the display format for date and time values."), gettext_noop("Also controls interpretation of ambiguous " "date inputs."), - GUC_LIST_INPUT | GUC_REPORT + GUC_LIST_INPUT }, &datestyle_string, "ISO, MDY", @@ -4269,7 +4277,7 @@ struct config_string ConfigureNamesString[] = {"server_encoding", PGC_INTERNAL, PRESET_OPTIONS, gettext_noop("Shows the server (database) character set encoding."), NULL, - GUC_IS_NAME | GUC_REPORT | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE + GUC_IS_NAME | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE }, &server_encoding_string, "SQL_ASCII", @@ -4281,7 +4289,7 @@ struct config_string ConfigureNamesString[] = {"server_version", PGC_INTERNAL, PRESET_OPTIONS, gettext_noop("Shows the server version."), NULL, - GUC_REPORT | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE + GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE }, &server_version_string, PG_VERSION, @@ -4305,7 +4313,7 @@ struct config_string ConfigureNamesString[] = {"session_authorization", PGC_USERSET, UNGROUPED, gettext_noop("Sets the session user name."), NULL, - GUC_IS_NAME | GUC_REPORT | GUC_NO_SHOW_ALL | GUC_NO_RESET_ALL | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE | GUC_NOT_WHILE_SEC_REST + GUC_IS_NAME | GUC_NO_SHOW_ALL | GUC_NO_RESET_ALL | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE | GUC_NOT_WHILE_SEC_REST }, &session_authorization_string, NULL, @@ -4372,7 +4380,6 @@ struct config_string ConfigureNamesString[] = {"TimeZone", PGC_USERSET, CLIENT_CONN_LOCALE, gettext_noop("Sets the time zone for displaying and interpreting time stamps."), NULL, - GUC_REPORT }, &timezone_string, "GMT", @@ -4622,7 +4629,7 @@ struct config_string ConfigureNamesString[] = {"application_name", PGC_USERSET, LOGGING_WHAT, gettext_noop("Sets the application name to be reported in statistics and logs."), NULL, - GUC_IS_NAME | GUC_REPORT | GUC_NOT_IN_SAMPLE + GUC_IS_NAME | GUC_NOT_IN_SAMPLE }, &application_name, "", @@ -4796,7 +4803,6 @@ struct config_enum ConfigureNamesEnum[] = {"IntervalStyle", PGC_USERSET, CLIENT_CONN_LOCALE, gettext_noop("Sets the display format for interval values."), NULL, - GUC_REPORT }, &IntervalStyle, INTSTYLE_POSTGRES, intervalstyle_options, diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h index 85f880dc6cd..05488aebb58 100644 --- a/src/include/utils/guc.h +++ b/src/include/utils/guc.h @@ -243,6 +243,7 @@ typedef enum /* GUC vars that are actually defined in guc_tables.c, rather than elsewhere */ extern PGDLLIMPORT char *protocol_managed_params; +extern PGDLLIMPORT char *report_parameters; extern PGDLLIMPORT bool Debug_print_plan; extern PGDLLIMPORT bool Debug_print_parse; diff --git a/src/include/utils/guc_hooks.h b/src/include/utils/guc_hooks.h index ebe048b5561..3ada4d18ed6 100644 --- a/src/include/utils/guc_hooks.h +++ b/src/include/utils/guc_hooks.h @@ -126,6 +126,8 @@ extern void assign_recovery_target_timeline(const char *newval, void *extra); extern bool check_recovery_target_xid(char **newval, void **extra, GucSource source); extern void assign_recovery_target_xid(const char *newval, void *extra); +extern bool check_report_parameters(char **newval, void **extra, GucSource source); +extern void assign_report_parameters(const char *newval, void *extra); extern bool check_role(char **newval, void **extra, GucSource source); extern void assign_role(const char *newval, void *extra); extern const char *show_role(void); diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c index 606f5c80489..ca7ebc5b79b 100644 --- a/src/interfaces/libpq/fe-connect.c +++ b/src/interfaces/libpq/fe-connect.c @@ -363,6 +363,10 @@ static const internalPQconninfoOption PQconninfoOptions[] = { "Pq-Protocol-Managed-Params", "", 40, offsetof(struct pg_conn, pq_protocol_managed_params)}, + {"_pq_.report_parameters", NULL, NULL, NULL, + "Pq-Report-Parameters", "", 40, + offsetof(struct pg_conn, pq_report_parameters)}, + /* Terminating entry --- MUST BE LAST */ {NULL, NULL, NULL, NULL, NULL, NULL, 0} diff --git a/src/interfaces/libpq/fe-protocol3.c b/src/interfaces/libpq/fe-protocol3.c index ee9205494d7..f8277e6c890 100644 --- a/src/interfaces/libpq/fe-protocol3.c +++ b/src/interfaces/libpq/fe-protocol3.c @@ -2322,6 +2322,9 @@ build_startup_packet(const PGconn *conn, char *packet, if (conn->pq_protocol_managed_params && conn->pq_protocol_managed_params[0]) ADD_STARTUP_OPTION("_pq_.protocol_managed_params", conn->pq_protocol_managed_params); + if (conn->pq_report_parameters) + ADD_STARTUP_OPTION("_pq_.report_parameters", conn->pq_report_parameters); + /* Add trailing terminator */ if (packet) packet[packet_len] = '\0'; diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h index f065d77cecc..bdf9fa5390c 100644 --- a/src/interfaces/libpq/libpq-int.h +++ b/src/interfaces/libpq/libpq-int.h @@ -410,6 +410,7 @@ struct pg_conn char *require_auth; /* name of the expected auth method */ char *load_balance_hosts; /* load balance over hosts */ char *pq_protocol_managed_params; /* _pq_.protocol_managed_params */ + char *pq_report_parameters; /* _pq_.report_parameters */ /* Optional file to write trace info to */ FILE *Pfdebug; diff --git a/src/test/modules/libpq_pipeline/libpq_pipeline.c b/src/test/modules/libpq_pipeline/libpq_pipeline.c index 5ca20eb367a..2990f795cae 100644 --- a/src/test/modules/libpq_pipeline/libpq_pipeline.c +++ b/src/test/modules/libpq_pipeline/libpq_pipeline.c @@ -1574,6 +1574,76 @@ test_pipeline_idle(PGconn *conn) fprintf(stderr, "ok - 2\n"); } +static void +test_report_parameters(PGconn *conn) +{ + PGresult *res = NULL; + const char *val = NULL; + + res = PQexec(conn, "SET application_name = 'test1'"); + if (PQresultStatus(res) != PGRES_COMMAND_OK) + pg_fatal("failed to set parameter: %s", PQerrorMessage(conn)); + + val = PQparameterStatus(conn, "application_name"); + if (strcmp(val, "test1") != 0) + pg_fatal("expected application_name to tracked as 'test1', but was '%s'", val); + + res = PQparameterSet(conn, "application_name", "test2"); + if (PQresultStatus(res) != PGRES_COMMAND_OK) + pg_fatal("failed to set parameter: %s", PQerrorMessage(conn)); + + val = PQparameterStatus(conn, "application_name"); + if (strcmp(val, "test2") != 0) + pg_fatal("expected application_name to tracked as 'test2', but was '%s'", val); + + res = PQparameterSet(conn, "_pq_.report_parameters", "lock_timeout"); + if (PQresultStatus(res) != PGRES_COMMAND_OK) + pg_fatal("failed to set parameter: %s", PQerrorMessage(conn)); + + /* Should have automatically received initial value */ + val = PQparameterStatus(conn, "lock_timeout"); + if (strcmp(val, "0") != 0) + pg_fatal("expected application_name to tracked as '123000', but was '%s'", val); + + /* + * Add some more parameters to track, including _pq_.report_parameters + * itself. + */ + res = PQparameterSet(conn, "_pq_.report_parameters", "lock_timeout,_pq_.report_parameters,work_mem"); + if (PQresultStatus(res) != PGRES_COMMAND_OK) + pg_fatal("failed to set parameter: %s", PQerrorMessage(conn)); + + val = PQparameterStatus(conn, "lock_timeout"); + if (strcmp(val, "0") != 0) + pg_fatal("expected application_name to tracked as '123000', but was '%s'", val); + + val = PQparameterStatus(conn, "_pq_.report_parameters"); + if (strcmp(val, "lock_timeout,_pq_.report_parameters,work_mem") != 0) + pg_fatal("_pq_.report_parameters was an unexpected value '%s'", val); + + val = PQparameterStatus(conn, "work_mem"); + if (strcmp(val, "4096") != 0) + pg_fatal("expected work_mem to be tracked as '4096', but was '%s'", val); + + /* changes to application_name should not be tracked anymore */ + res = PQparameterSet(conn, "application_name", "test3333"); + if (PQresultStatus(res) != PGRES_COMMAND_OK) + pg_fatal("failed to set parameter: %s", PQerrorMessage(conn)); + + val = PQparameterStatus(conn, "application_name"); + if (strcmp(val, "test2") != 0) + pg_fatal("expected application_name to tracked as 'test2', but was '%s'", val); + + /* changes to and work_mem lock_timeout should be tracked */ + res = PQparameterSet(conn, "lock_timeout", "123s"); + if (PQresultStatus(res) != PGRES_COMMAND_OK) + pg_fatal("failed to set parameter: %s", PQerrorMessage(conn)); + + val = PQparameterStatus(conn, "lock_timeout"); + if (strcmp(val, "123000") != 0) + pg_fatal("expected application_name to tracked as '123000', but was '%s'", val); +} + static void test_simple_pipeline(PGconn *conn) { @@ -2204,6 +2274,7 @@ print_test_list(void) printf("pipeline_idle\n"); printf("pipelined_insert\n"); printf("prepared\n"); + printf("report_parameters\n"); printf("simple_pipeline\n"); printf("singlerow\n"); printf("transaction\n"); @@ -2314,6 +2385,8 @@ main(int argc, char **argv) test_pipelined_insert(conn, numrows); else if (strcmp(testname, "prepared") == 0) test_prepared(conn); + else if (strcmp(testname, "report_parameters") == 0) + test_report_parameters(conn); else if (strcmp(testname, "simple_pipeline") == 0) test_simple_pipeline(conn); else if (strcmp(testname, "singlerow") == 0) diff --git a/src/test/modules/libpq_pipeline/t/001_libpq_pipeline.pl b/src/test/modules/libpq_pipeline/t/001_libpq_pipeline.pl index eec5d954bf0..c08b3c5f6bc 100644 --- a/src/test/modules/libpq_pipeline/t/001_libpq_pipeline.pl +++ b/src/test/modules/libpq_pipeline/t/001_libpq_pipeline.pl @@ -39,7 +39,7 @@ for my $testname (@tests) my $cmptrace = grep(/^$testname$/, qw(simple_pipeline nosync multi_pipelines parameter_set prepared singlerow pipeline_abort pipeline_idle transaction - disallowed_in_pipeline)) > 0; + disallowed_in_pipeline report_paramters)) > 0; # For a bunch of tests, generate a libpq trace file too. my $traceout = diff --git a/src/test/modules/libpq_pipeline/traces/report_parameters.trace b/src/test/modules/libpq_pipeline/traces/report_parameters.trace new file mode 100644 index 00000000000..a9f6b5039ea --- /dev/null +++ b/src/test/modules/libpq_pipeline/traces/report_parameters.trace @@ -0,0 +1,31 @@ +F 35 Query "SET application_name = 'test1'" +B 8 CommandComplete "SET" +B 27 ParameterStatus "application_name" "test1" +B 5 ReadyForQuery I +F 27 ParameterSet "application_name" "test2" +F 4 Sync +B 4 ParameterSetComplete +B 27 ParameterStatus "application_name" "test2" +B 5 ReadyForQuery I +F 40 ParameterSet "_pq_.report_parameters" "lock_timeout" +F 4 Sync +B 4 ParameterSetComplete +B 19 ParameterStatus "lock_timeout" "0" +B 5 ReadyForQuery I +F 72 ParameterSet "_pq_.report_parameters" "lock_timeout,_pq_.report_parameters,work_mem" +F 4 Sync +B 4 ParameterSetComplete +B 18 ParameterStatus "work_mem" "4096" +B 72 ParameterStatus "_pq_.report_parameters" "lock_timeout,_pq_.report_parameters,work_mem" +B 19 ParameterStatus "lock_timeout" "0" +B 5 ReadyForQuery I +F 30 ParameterSet "application_name" "test3333" +F 4 Sync +B 4 ParameterSetComplete +B 5 ReadyForQuery I +F 22 ParameterSet "lock_timeout" "123s" +F 4 Sync +B 4 ParameterSetComplete +B 24 ParameterStatus "lock_timeout" "123000" +B 5 ReadyForQuery I +F 4 Terminate -- 2.34.1