1
0
mirror of https://github.com/postgres/postgres.git synced 2025-04-20 00:42:27 +03:00

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.
This makes \watch the first command which has a user configurable
default setting.

Author: Daniel Gustafsson <daniel@yesql.se>
Reviewed-by: Heikki Linnakangas <hlinnaka@iki.fi>
Reviewed-by: Michael Paquier <michael@paquier.xyz>
Reviewed-by: Kirill Reshke <reshkekirill@gmail.com>
Reviewed-by: Masahiro Ikeda <ikedamsh@oss.nttdata.com>
Reviewed-by: Laurenz Albe <laurenz.albe@cybertec.at>
Reviewed-by: Greg Sabino Mullane <htamfids@gmail.com>
Reviewed-by: Ashutosh Bapat <ashutosh.bapat.oss@gmail.com>
Discussion: https://postgr.es/m/B2FD26B4-8F64-4552-A603-5CC3DF1C7103@yesql.se
This commit is contained in:
Daniel Gustafsson 2025-03-25 17:53:33 +01:00
parent a19db08274
commit 1a759c8327
8 changed files with 141 additions and 2 deletions

View File

@ -3852,6 +3852,8 @@ SELECT 1 \bind \sendpipeline
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.
The default wait can be changed with the variable
<xref linkend="app-psql-variables-watch-interval"/>).
For backwards compatibility,
<replaceable class="parameter">seconds</replaceable> can be specified
with or without an <literal>interval=</literal> prefix.
@ -4746,6 +4748,17 @@ bar
</listitem>
</varlistentry>
<varlistentry id="app-psql-variables-watch-interval">
<term><varname>WATCH_INTERVAL</varname></term>
<listitem>
<para>
This variable sets the default interval which <command>\watch</command>
waits between executing the query. Specifying an interval in the
command overrides this variable.
</para>
</listitem>
</varlistentry>
</variablelist>
</refsect3>

View File

@ -3278,7 +3278,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;
@ -3292,7 +3292,9 @@ exec_command_watch(PsqlScanState scan_state, bool active_branch,
/*
* Parse arguments. We allow either an unlabeled interval or
* "name=value", where name is from the set ('i', 'interval', 'c',
* 'count', 'm', 'min_rows').
* 'count', 'm', 'min_rows'). The parsing of interval value should be
* kept in sync with ParseVariableDouble which is used for setting the
* default interval value.
*/
while (success)
{

View File

@ -460,6 +460,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 by default waits between executing the query buffer\n");
HELP0("\nDisplay settings:\n");
HELP0("Usage:\n");

View File

@ -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.
@ -166,6 +172,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;

View File

@ -944,6 +944,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)
{
@ -1270,4 +1285,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);
}

View File

@ -375,6 +375,12 @@ psql_like(
$node, sprintf('SELECT 1 \watch c=3 i=%g', 0.0001),
qr/1\n1\n1/, '\watch with 3 iterations, interval of 0.0001');
# Test zero interval
psql_like(
$node, '\set WATCH_INTERVAL 0
SELECT 1 \watch c=3',
qr/1\n1\n1/, '\watch with 3 iterations, interval of 0');
# Check \watch minimum row count
psql_fails_like(
$node,
@ -426,6 +432,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";

View File

@ -7,6 +7,8 @@
*/
#include "postgres_fe.h"
#include <math.h>
#include "common.h"
#include "common/logging.h"
#include "variables.h"
@ -179,6 +181,74 @@ 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. The
* value must be within the range [min,max] in order to be considered valid.
*
* 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.
*/

View File

@ -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);