mirror of
https://github.com/postgres/postgres.git
synced 2025-04-24 10:47:04 +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:
parent
2820adf775
commit
00beecfe83
@ -3551,12 +3551,16 @@ testdb=> <userinput>\setenv LESS -imx4F</userinput>
|
||||
|
||||
|
||||
<varlistentry id="app-psql-meta-command-watch">
|
||||
<term><literal>\watch [ <replaceable class="parameter">seconds</replaceable> ]</literal></term>
|
||||
<term><literal>\watch [ i[nterval]=<replaceable class="parameter">seconds</replaceable> ] [ c[ount]=<replaceable class="parameter">times</replaceable> ] [ <replaceable class="parameter">seconds</replaceable> ]</literal></term>
|
||||
<listitem>
|
||||
<para>
|
||||
Repeatedly execute the current query buffer (as <literal>\g</literal> does)
|
||||
until interrupted or the query fails. Wait the specified number of
|
||||
seconds (default 2) between executions. Each query result is
|
||||
until interrupted, or the query fails, or the execution count limit
|
||||
(if given) is reached. Wait the specified number of
|
||||
seconds (default 2) between executions. For backwards compatibility,
|
||||
<replaceable class="parameter">seconds</replaceable> can be specified
|
||||
with or without an <literal>interval=</literal> prefix.
|
||||
Each query result is
|
||||
displayed with a header that includes the <literal>\pset title</literal>
|
||||
string (if any), the time as of query start, and the delay interval.
|
||||
</para>
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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");
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user