diff options
author | Tom Lane | 2023-04-06 17:18:14 +0000 |
---|---|---|
committer | Tom Lane | 2023-04-06 17:18:14 +0000 |
commit | 00beecfe839c878abb366b68272426ed5296bc2b (patch) | |
tree | 06e479aff010d16e0f86ada78fba8a5bfff156da /src/bin/psql/command.c | |
parent | 2820adf7755d2a377546d5b55f5b1a4a39889336 (diff) |
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
Diffstat (limited to 'src/bin/psql/command.c')
-rw-r--r-- | src/bin/psql/command.c | 118 |
1 files changed, 100 insertions, 18 deletions
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c index d7731234b63..e8f583cac22 100644 --- a/src/bin/psql/command.c +++ b/src/bin/psql/command.c @@ -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; |