1
0
mirror of https://github.com/postgres/postgres.git synced 2025-06-30 21:42:05 +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;