v10-0001-Iteration-count-argument-to-psql-watch-command.patch
application/octet-stream
Filename: v10-0001-Iteration-count-argument-to-psql-watch-command.patch
Type: application/octet-stream
Part: 0
Patch
Format: format-patch
Series: patch v10-0001
Subject: Iteration count argument to psql \watch command
| File | + | − |
|---|---|---|
| doc/src/sgml/ref/psql-ref.sgml | 5 | 1 |
| src/bin/psql/command.c | 90 | 15 |
| src/bin/psql/help.c | 1 | 1 |
| src/bin/psql/t/001_basic.pl | 19 | 2 |
| src/test/regress/expected/psql.out | 1 | 1 |
| src/test/regress/sql/psql.sql | 1 | 1 |
From a55836c699abcc657e0a85ca2f6dd4e03ecf9f4d Mon Sep 17 00:00:00 2001
From: "Andrey M. Borodin" <x4mmm@flight.local>
Date: Thu, 16 Feb 2023 15:07:50 -0800
Subject: [PATCH v10] Iteration count argument to psql \watch command
If the argument is not provided - continue to \watch forever.
Authour: Andrey Borodin
Reviewed-by: Kyotaro Horiguchi, Nathan Bossart, Michael Paquier, Yugo Nagata
Thread: https://postgr.es/m/CAAhFRxiZ2-n_L1ErMm9AZjgmUK%3DqS6VHb%2B0SaMn8sqqbhF7How%40mail.gmail.com
---
doc/src/sgml/ref/psql-ref.sgml | 6 +-
src/bin/psql/command.c | 105 ++++++++++++++++++++++++-----
src/bin/psql/help.c | 2 +-
src/bin/psql/t/001_basic.pl | 21 +++++-
src/test/regress/expected/psql.out | 2 +-
src/test/regress/sql/psql.sql | 2 +-
6 files changed, 117 insertions(+), 21 deletions(-)
diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml
index 7b8ae9fac3..35944af97f 100644
--- a/doc/src/sgml/ref/psql-ref.sgml
+++ b/doc/src/sgml/ref/psql-ref.sgml
@@ -3551,7 +3551,7 @@ 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 [ <replaceable class="parameter">i[nterval]</replaceable>=<replaceable class="parameter">seconds</replaceable> ] [ <replaceable class="parameter">c[ount]</replaceable>=<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)
@@ -3564,6 +3564,10 @@ testdb=> <userinput>\setenv LESS -imx4F</userinput>
If the current query buffer is empty, the most recently sent query
is re-executed instead.
</para>
+ <para>
+ If number of iterations is specified, query will be executed only
+ given number of times.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index 61ec049f05..cdfafac7a9 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 for M iteration.
*/
static backslashResult
exec_command_watch(PsqlScanState scan_state, bool active_branch,
@@ -2771,30 +2772,97 @@ exec_command_watch(PsqlScanState scan_state, bool 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)
+
+ while (opt)
{
- char *opt_end;
+ /*
+ * We can have either sleep interval or "name=value", where name is
+ * from the set ('i','interval','c','count')
+ */
+ char *valptr = strchr(opt, '=');
+ char *opt_end;
- errno = 0;
- sleep = strtod(opt, &opt_end);
- if (sleep < 0 || *opt_end || errno == ERANGE)
+ 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;
+ valptr++;
+ if (strncmp("i", opt, strlen("i")) == 0 ||
+ strncmp("interval", opt, strlen("interval")) == 0)
+ {
+ errno = 0;
+ sleep = strtod(valptr, &opt_end);
+ if (sleep < 0 || *opt_end || errno == ERANGE || have_sleep)
+ {
+ if (have_sleep)
+ pg_log_error("\\watch: interval value is specified more than once");
+ else
+ pg_log_error("\\watch: incorrect interval value '%s'", valptr);
+ free(opt);
+ resetPQExpBuffer(query_buf);
+ psql_scan_reset(scan_state);
+ return PSQL_CMD_ERROR;
+ }
+ have_sleep = true;
+ }
+ else if (strncmp("c", opt, strlen("c")) == 0 ||
+ strncmp("count", opt, strlen("count")) == 0)
+ {
+ errno = 0;
+ iter = strtol(valptr, &opt_end, 10);
+ if (iter <= 0 || *opt_end || errno == ERANGE || have_iter)
+ {
+ if (have_iter)
+ pg_log_error("\\watch: iteration count is specified more than once");
+ else
+ pg_log_error("\\watch: incorrect iteration count '%s'", valptr);
+ free(opt);
+ resetPQExpBuffer(query_buf);
+ psql_scan_reset(scan_state);
+ return PSQL_CMD_ERROR;
+ }
+ have_iter = true;
+ }
+ else
+ {
+ pg_log_error("Unknown \\watch argument '%s'", opt);
+ free(opt);
+ resetPQExpBuffer(query_buf);
+ psql_scan_reset(scan_state);
+ return PSQL_CMD_ERROR;
+ }
}
+ else
+ {
+ errno = 0;
+ sleep = strtod(opt, &opt_end);
+ if (sleep < 0 || *opt_end || errno == ERANGE || have_sleep)
+ {
+ if (have_sleep)
+ pg_log_error("\\watch: interval value is specified more than once");
+ else
+ pg_log_error("\\watch: incorrect interval value '%s'", opt);
+ free(opt);
+ resetPQExpBuffer(query_buf);
+ psql_scan_reset(scan_state);
+ return PSQL_CMD_ERROR;
+ }
+ have_sleep = true;
+ }
+
free(opt);
+ opt = psql_scan_slash_option(scan_state,
+ OT_NORMAL, NULL, true);
}
/* 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);
@@ -5056,7 +5124,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;
@@ -5158,11 +5226,18 @@ do_watch(PQExpBuffer query_buf, double sleep)
title_len = (user_title ? strlen(user_title) : 0) + 256;
title = pg_malloc(title_len);
- for (;;)
+ for (int i = 1;;)
{
time_t timer;
char timebuf[128];
+ /* If we have iteration count - check that it's not exceeded yet */
+ /* Keep in mind that first iteration was performed before do_watch() */
+ if (iter && (i++ == iter))
+ {
+ break;
+ }
+
/*
* Prepare title for output. Note that we intentionally include a
* newline at the end of the title; this is somewhat historical but it
diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c
index e45c4aaca5..1062d0ed7b 100644
--- a/src/bin/psql/help.c
+++ b/src/bin/psql/help.c
@@ -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 [c=N] [SEC] execute query every SEC seconds N times\n");
HELP0("\n");
HELP0("Help\n");
diff --git a/src/bin/psql/t/001_basic.pl b/src/bin/psql/t/001_basic.pl
index 64ce012062..b5cf7b1b43 100644
--- a/src/bin/psql/t/001_basic.pl
+++ b/src/bin/psql/t/001_basic.pl
@@ -350,6 +350,13 @@ psql_like(
'\copy from with DEFAULT'
);
+# Check \watch
+psql_like(
+ $node,
+ 'SELECT 1;\watch c=3 i=0',
+ qr/1\n1\n1/,
+ '\watch with 3 iterations');
+
# Check \watch errors
psql_fails_like(
$node,
@@ -360,11 +367,21 @@ psql_fails_like(
$node,
'SELECT 1;\watch 10ab',
qr/incorrect interval value '10ab'/,
- '\watch incorrect interval');
+ '\watch, incorrect interval');
psql_fails_like(
$node,
'SELECT 1;\watch 10e400',
qr/incorrect interval value '10e400'/,
- '\watch out-of-range interval');
+ '\watch, out-of-range interval');
+psql_fails_like(
+ $node,
+ 'SELECT 1;\watch 1 1',
+ qr/nterval 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();
diff --git a/src/test/regress/expected/psql.out b/src/test/regress/expected/psql.out
index c00e28361c..956e475447 100644
--- a/src/test/regress/expected/psql.out
+++ b/src/test/regress/expected/psql.out
@@ -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
diff --git a/src/test/regress/sql/psql.sql b/src/test/regress/sql/psql.sql
index 961783d6ea..630f638f02 100644
--- a/src/test/regress/sql/psql.sql
+++ b/src/test/regress/sql/psql.sql
@@ -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
--
2.37.1 (Apple Git-137.1)