From c260f7e0ad6dfea50a34e168e73ba50392da4313 Mon Sep 17 00:00:00 2001 From: Daniel Gustafsson Date: Fri, 14 Feb 2025 15:11:23 +0100 Subject: [PATCH v5] psql: Make default \watch interval configurable The default interval for \watch to wait between executing queries, when executed without a specified interval, was hardcoded to two seconds. This adds the new variable WATCH_INTERVAL which is used to set the default interval, making it configurable for the user. Author: Daniel Gustafsson Reviewed-by: Heikki Linnakangas Reviewed-by: Michael Paquier Reviewed-by: Kirill Reshke Reviewed-by: Masahiro Ikeda Discussion: https://postgr.es/m/B2FD26B4-8F64-4552-A603-5CC3DF1C7103@yesql.se --- doc/src/sgml/ref/psql-ref.sgml | 16 +++++++- src/bin/psql/command.c | 2 +- src/bin/psql/help.c | 2 + src/bin/psql/settings.h | 7 ++++ src/bin/psql/startup.c | 18 +++++++++ src/bin/psql/t/001_basic.pl | 18 +++++++++ src/bin/psql/variables.c | 69 ++++++++++++++++++++++++++++++++++ src/bin/psql/variables.h | 3 ++ 8 files changed, 132 insertions(+), 3 deletions(-) diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml index f3044fac1fa..73abbc5cf56 100644 --- a/doc/src/sgml/ref/psql-ref.sgml +++ b/doc/src/sgml/ref/psql-ref.sgml @@ -3777,8 +3777,9 @@ testdb=> \setenv LESS -imx4F Repeatedly execute the current query buffer (as \g does) until interrupted, or the query fails, or the execution count limit (if given) is reached, or the query no longer returns the minimum number - of rows. Wait the specified number of seconds (default 2) between executions. - For backwards compatibility, + of rows. Wait the specified number of seconds (defaults to 2 if omitted, which can be + changed with the variable ) + between executions. For backwards compatibility, seconds can be specified with or without an interval= prefix. Each query result is @@ -4641,6 +4642,17 @@ bar + + WATCH_INTERVAL + + + This variable sets the default interval which \watch + waits between executing the query. Specifying an interval in the + command overrides this variable. + + + + diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c index 26dfdde195a..27b2f7d47f4 100644 --- a/src/bin/psql/command.c +++ b/src/bin/psql/command.c @@ -2940,7 +2940,7 @@ exec_command_watch(PsqlScanState scan_state, bool active_branch, bool have_sleep = false; bool have_iter = false; bool have_min_rows = false; - double sleep = 2; + double sleep = pset.watch_interval; int iter = 0; int min_rows = 0; diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c index da8e1ade5df..00cbbde8c38 100644 --- a/src/bin/psql/help.c +++ b/src/bin/psql/help.c @@ -452,6 +452,8 @@ helpVariables(unsigned short int pager) " VERSION_NAME\n" " VERSION_NUM\n" " psql's version (in verbose string, short string, or numeric format)\n"); + HELP0(" WATCH_INTERVAL\n" + " number of seconds \\watch waits between executing the query buffer\n"); HELP0("\nDisplay settings:\n"); HELP0("Usage:\n"); diff --git a/src/bin/psql/settings.h b/src/bin/psql/settings.h index 2a8fe12eb55..2f0b064cf5d 100644 --- a/src/bin/psql/settings.h +++ b/src/bin/psql/settings.h @@ -27,6 +27,12 @@ #define DEFAULT_PROMPT2 "%/%R%x%# " #define DEFAULT_PROMPT3 ">> " +#define DEFAULT_WATCH_INTERVAL "2" +/* + * Limit the max default setting to a value which should be safe for the + * itimer call, yet large enough to cover all realistic usecases. + */ +#define DEFAULT_WATCH_INTERVAL_MAX (1000*1000) /* * Note: these enums should generally be chosen so that zero corresponds * to the default behavior. @@ -154,6 +160,7 @@ typedef struct _psqlSettings int fetch_count; int histsize; int ignoreeof; + double watch_interval; PSQL_ECHO echo; PSQL_ECHO_HIDDEN echo_hidden; PSQL_ERROR_ROLLBACK on_error_rollback; diff --git a/src/bin/psql/startup.c b/src/bin/psql/startup.c index 703f3f582c1..d41214908d1 100644 --- a/src/bin/psql/startup.c +++ b/src/bin/psql/startup.c @@ -939,6 +939,21 @@ histsize_hook(const char *newval) return ParseVariableNum(newval, "HISTSIZE", &pset.histsize); } +static char * +watch_interval_substitute_hook(char *newval) +{ + if (newval == NULL) + newval = pg_strdup(DEFAULT_WATCH_INTERVAL); + return newval; +} + +static bool +watch_interval_hook(const char *newval) +{ + return ParseVariableDouble(newval, "WATCH_INTERVAL", &pset.watch_interval, + 0, DEFAULT_WATCH_INTERVAL_MAX); +} + static char * ignoreeof_substitute_hook(char *newval) { @@ -1265,4 +1280,7 @@ EstablishVariableSpace(void) SetVariableHooks(pset.vars, "HIDE_TABLEAM", bool_substitute_hook, hide_tableam_hook); + SetVariableHooks(pset.vars, "WATCH_INTERVAL", + watch_interval_substitute_hook, + watch_interval_hook); } diff --git a/src/bin/psql/t/001_basic.pl b/src/bin/psql/t/001_basic.pl index 3170bc86856..2c8786dbe94 100644 --- a/src/bin/psql/t/001_basic.pl +++ b/src/bin/psql/t/001_basic.pl @@ -425,6 +425,24 @@ psql_fails_like( qr/iteration count is specified more than once/, '\watch, iteration count is specified more than once'); +# Check WATCH_INTERVAL +psql_like( + $node, + '\echo :WATCH_INTERVAL +\set WATCH_INTERVAL 0.001 +\echo :WATCH_INTERVAL +\unset WATCH_INTERVAL +\echo :WATCH_INTERVAL', + qr/^2$ +^0.001$ +^2$/m, + 'WATCH_INTERVAL variable is set and updated'); +psql_fails_like( + $node, + '\set WATCH_INTERVAL 1e500', + qr/is out of range/, + 'WATCH_INTERVAL variable is out of range'); + # Test \g output piped into a program. # The program is perl -pe '' to simply copy the input to the output. my $g_file = "$tempdir/g_file_1.out"; diff --git a/src/bin/psql/variables.c b/src/bin/psql/variables.c index 59956028918..4be7a3f4980 100644 --- a/src/bin/psql/variables.c +++ b/src/bin/psql/variables.c @@ -7,6 +7,8 @@ */ #include "postgres_fe.h" +#include + #include "common.h" #include "common/logging.h" #include "variables.h" @@ -179,6 +181,73 @@ ParseVariableNum(const char *value, const char *name, int *result) } } +/* + * Try to interpret "value" as a double value, and if successful store it in + * *result. If unsuccessful, *result isn't clobbered. "name" is the variable + * which is being assigned, the value of which is only used to produce a good + * error message. Pass NULL as the name to suppress the error message. + * + * Returns true, with *result containing the interpreted value, if "value" is + * syntactically valid, else false (with *result unchanged). + */ +bool +ParseVariableDouble(const char *value, const char *name, double *result, double min, double max) +{ + char *end; + double dblval; + + /* + * Empty-string input has historically been treated differently by strtod + * on various platforms, so handle that by specifically checking for it. + */ + if ((value == NULL) || (*value == '\0')) + { + if (name) + pg_log_error("invalid input syntax for \"%s\"", name); + return false; + } + + errno = 0; + dblval = strtod(value, &end); + if (errno == 0 && *end == '\0' && end != value) + { + if (dblval < min) + { + if (name) + pg_log_error("invalid value \"%s\" for \"%s\": must be greater than %.2f", + value, name, min); + return false; + } + else if (dblval > max) + { + if (name) + pg_log_error("invalid value \"%s\" for \"%s\": must be less than %.2f", + value, name, max); + } + *result = dblval; + return true; + } + + /* + * Cater for platforms which treat values which aren't zero, but that are + * too close to zero to have full precision, by checking for zero or real + * out-of-range values. + */ + else if ((errno = ERANGE) && + (dblval == 0.0 || dblval >= HUGE_VAL || dblval <= -HUGE_VAL)) + { + if (name) + pg_log_error("\"%s\" is out of range for \"%s\"", value, name); + return false; + } + else + { + if (name) + pg_log_error("invalid value \"%s\" for \"%s\"", value, name); + return false; + } +} + /* * Print values of all variables. */ diff --git a/src/bin/psql/variables.h b/src/bin/psql/variables.h index a95bc29f407..df23ccb987d 100644 --- a/src/bin/psql/variables.h +++ b/src/bin/psql/variables.h @@ -81,6 +81,9 @@ bool ParseVariableBool(const char *value, const char *name, bool ParseVariableNum(const char *value, const char *name, int *result); +bool ParseVariableDouble(const char *value, const char *name, + double *result, double min, double max); + void PrintVariables(VariableSpace space); bool SetVariable(VariableSpace space, const char *name, const char *value); -- 2.39.3 (Apple Git-146)