1
0
mirror of https://github.com/postgres/postgres.git synced 2025-06-17 17:02:08 +03:00

psql: add an optional execution-count limit to \watch.

\watch can now be told to stop after N executions of the query.

With the idea that we might want to add more options to \watch
in future, this patch generalizes the command's syntax to a list
of name=value options, with the interval allowed to omit the name
for backwards compatibility.

Andrey Borodin, reviewed by Kyotaro Horiguchi, Nathan Bossart,
Michael Paquier, Yugo Nagata, and myself

Discussion: https://postgr.es/m/CAAhFRxiZ2-n_L1ErMm9AZjgmUK=qS6VHb+0SaMn8sqqbhF7How@mail.gmail.com
This commit is contained in:
Tom Lane
2023-04-06 13:18:14 -04:00
parent 2820adf775
commit 00beecfe83
6 changed files with 135 additions and 32 deletions

View File

@ -162,7 +162,7 @@ static bool do_connect(enum trivalue reuse_previous_specification,
static bool do_edit(const char *filename_arg, PQExpBuffer query_buf,
int lineno, bool discard_on_quit, bool *edited);
static bool do_shell(const char *command);
static bool do_watch(PQExpBuffer query_buf, double sleep);
static bool do_watch(PQExpBuffer query_buf, double sleep, int iter);
static bool lookup_object_oid(EditableObjectType obj_type, const char *desc,
Oid *obj_oid);
static bool get_create_object_cmd(EditableObjectType obj_type, Oid oid,
@ -2759,7 +2759,8 @@ exec_command_write(PsqlScanState scan_state, bool active_branch,
}
/*
* \watch -- execute a query every N seconds
* \watch -- execute a query every N seconds.
* Optionally, stop after M iterations.
*/
static backslashResult
exec_command_watch(PsqlScanState scan_state, bool active_branch,
@ -2769,32 +2770,109 @@ exec_command_watch(PsqlScanState scan_state, bool active_branch,
if (active_branch)
{
char *opt = psql_scan_slash_option(scan_state,
OT_NORMAL, NULL, true);
bool have_sleep = false;
bool have_iter = false;
double sleep = 2;
int iter = 0;
/* Convert optional sleep-length argument */
if (opt)
/*
* Parse arguments. We allow either an unlabeled interval or
* "name=value", where name is from the set ('i', 'interval', 'c',
* 'count').
*/
while (success)
{
char *opt = psql_scan_slash_option(scan_state,
OT_NORMAL, NULL, true);
char *valptr;
char *opt_end;
errno = 0;
sleep = strtod(opt, &opt_end);
if (sleep < 0 || *opt_end || errno == ERANGE)
if (!opt)
break; /* no more arguments */
valptr = strchr(opt, '=');
if (valptr)
{
pg_log_error("\\watch: incorrect interval value '%s'", opt);
free(opt);
resetPQExpBuffer(query_buf);
psql_scan_reset(scan_state);
return PSQL_CMD_ERROR;
/* Labeled argument */
valptr++;
if (strncmp("i=", opt, strlen("i=")) == 0 ||
strncmp("interval=", opt, strlen("interval=")) == 0)
{
if (have_sleep)
{
pg_log_error("\\watch: interval value is specified more than once");
success = false;
}
else
{
have_sleep = true;
errno = 0;
sleep = strtod(valptr, &opt_end);
if (sleep < 0 || *opt_end || errno == ERANGE)
{
pg_log_error("\\watch: incorrect interval value \"%s\"", valptr);
success = false;
}
}
}
else if (strncmp("c=", opt, strlen("c=")) == 0 ||
strncmp("count=", opt, strlen("count=")) == 0)
{
if (have_iter)
{
pg_log_error("\\watch: iteration count is specified more than once");
success = false;
}
else
{
have_iter = true;
errno = 0;
iter = strtoint(valptr, &opt_end, 10);
if (iter <= 0 || *opt_end || errno == ERANGE)
{
pg_log_error("\\watch: incorrect iteration count \"%s\"", valptr);
success = false;
}
}
}
else
{
pg_log_error("\\watch: unrecognized parameter \"%s\"", opt);
success = false;
}
}
else
{
/* Unlabeled argument: take it as interval */
if (have_sleep)
{
pg_log_error("\\watch: interval value is specified more than once");
success = false;
}
else
{
have_sleep = true;
errno = 0;
sleep = strtod(opt, &opt_end);
if (sleep < 0 || *opt_end || errno == ERANGE)
{
pg_log_error("\\watch: incorrect interval value \"%s\"", opt);
success = false;
}
}
}
free(opt);
}
/* If query_buf is empty, recall and execute previous query */
(void) copy_previous_query(query_buf, previous_buf);
/* If we parsed arguments successfully, do the command */
if (success)
{
/* If query_buf is empty, recall and execute previous query */
(void) copy_previous_query(query_buf, previous_buf);
success = do_watch(query_buf, sleep);
success = do_watch(query_buf, sleep, iter);
}
/* Reset the query buffer as though for \r */
resetPQExpBuffer(query_buf);
@ -5071,7 +5149,7 @@ do_shell(const char *command)
* onto a bunch of exec_command's variables to silence stupider compilers.
*/
static bool
do_watch(PQExpBuffer query_buf, double sleep)
do_watch(PQExpBuffer query_buf, double sleep, int iter)
{
long sleep_ms = (long) (sleep * 1000);
printQueryOpt myopt = pset.popt;
@ -5204,6 +5282,10 @@ do_watch(PQExpBuffer query_buf, double sleep)
if (res <= 0)
break;
/* If we have iteration count, check that it's not exceeded yet */
if (iter && (--iter <= 0))
break;
if (pagerpipe && ferror(pagerpipe))
break;

View File

@ -200,7 +200,7 @@ slashUsage(unsigned short int pager)
HELP0(" \\gset [PREFIX] execute query and store result in psql variables\n");
HELP0(" \\gx [(OPTIONS)] [FILE] as \\g, but forces expanded output mode\n");
HELP0(" \\q quit psql\n");
HELP0(" \\watch [SEC] execute query every SEC seconds\n");
HELP0(" \\watch [[i=]SEC] [c=N] execute query every SEC seconds, up to N times\n");
HELP0("\n");
HELP0("Help\n");

View File

@ -350,21 +350,38 @@ psql_like(
'\copy from with DEFAULT'
);
# Check \watch
psql_like(
$node,
'SELECT 1 \watch c=3 i=0.01',
qr/1\n1\n1/,
'\watch with 3 iterations');
# Check \watch errors
psql_fails_like(
$node,
'SELECT 1;\watch -10',
qr/incorrect interval value '-10'/,
'SELECT 1 \watch -10',
qr/incorrect interval value "-10"/,
'\watch, negative interval');
psql_fails_like(
$node,
'SELECT 1;\watch 10ab',
qr/incorrect interval value '10ab'/,
'\watch incorrect interval');
'SELECT 1 \watch 10ab',
qr/incorrect interval value "10ab"/,
'\watch, incorrect interval');
psql_fails_like(
$node,
'SELECT 1;\watch 10e400',
qr/incorrect interval value '10e400'/,
'\watch out-of-range interval');
'SELECT 1 \watch 10e400',
qr/incorrect interval value "10e400"/,
'\watch, out-of-range interval');
psql_fails_like(
$node,
'SELECT 1 \watch 1 1',
qr/interval value is specified more than once/,
'\watch, interval value is specified more than once');
psql_fails_like(
$node,
'SELECT 1 \watch c=1 c=1',
qr/iteration count is specified more than once/,
'\watch, iteration count is specified more than once');
done_testing();

View File

@ -4536,7 +4536,7 @@ invalid command \lo
\timing arg1
\unset arg1
\w arg1
\watch arg1
\watch arg1 arg2
\x arg1
-- \else here is eaten as part of OT_FILEPIPE argument
\w |/no/such/file \else

View File

@ -1022,7 +1022,7 @@ select \if false \\ (bogus \else \\ 42 \endif \\ forty_two;
\timing arg1
\unset arg1
\w arg1
\watch arg1
\watch arg1 arg2
\x arg1
-- \else here is eaten as part of OT_FILEPIPE argument
\w |/no/such/file \else