Skip to content

Commit c6a3fce

Browse files
committed
Add \watch [SEC] command to psql.
This allows convenient re-execution of commands. Will Leinweber, reviewed by Peter Eisentraut, Daniel Farina, and Tom Lane
1 parent e75feb2 commit c6a3fce

File tree

4 files changed

+145
-2
lines changed

4 files changed

+145
-2
lines changed

doc/src/sgml/ref/psql-ref.sgml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2478,6 +2478,18 @@ testdb=&gt; <userinput>\setenv LESS -imx4F</userinput>
24782478
</varlistentry>
24792479

24802480

2481+
<varlistentry>
2482+
<term><literal>\watch [ <replaceable class="parameter">seconds</replaceable> ]</literal></term>
2483+
<listitem>
2484+
<para>
2485+
Repeatedly execute the current query buffer (like <literal>\g</>)
2486+
until interrupted or the query fails. Wait the specified number of
2487+
seconds (default 2) between executions.
2488+
</para>
2489+
</listitem>
2490+
</varlistentry>
2491+
2492+
24812493
<varlistentry>
24822494
<term><literal>\x [ <replaceable class="parameter">on</replaceable> | <replaceable class="parameter">off</replaceable> | <replaceable class="parameter">auto</replaceable> ]</literal></term>
24832495
<listitem>

src/bin/psql/command.c

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ static bool do_edit(const char *filename_arg, PQExpBuffer query_buf,
6060
int lineno, bool *edited);
6161
static bool do_connect(char *dbname, char *user, char *host, char *port);
6262
static bool do_shell(const char *command);
63+
static bool do_watch(PQExpBuffer query_buf, long sleep);
6364
static bool lookup_function_oid(PGconn *conn, const char *desc, Oid *foid);
6465
static bool get_create_function_cmd(PGconn *conn, Oid oid, PQExpBuffer buf);
6566
static int strip_lineno_from_funcdesc(char *func);
@@ -1433,6 +1434,29 @@ exec_command(const char *cmd,
14331434
free(fname);
14341435
}
14351436

1437+
/* \watch -- execute a query every N seconds */
1438+
else if (strcmp(cmd, "watch") == 0)
1439+
{
1440+
char *opt = psql_scan_slash_option(scan_state,
1441+
OT_NORMAL, NULL, true);
1442+
long sleep = 2;
1443+
1444+
/* Convert optional sleep-length argument */
1445+
if (opt)
1446+
{
1447+
sleep = strtol(opt, NULL, 10);
1448+
if (sleep <= 0)
1449+
sleep = 1;
1450+
free(opt);
1451+
}
1452+
1453+
success = do_watch(query_buf, sleep);
1454+
1455+
/* Reset the query buffer as though for \r */
1456+
resetPQExpBuffer(query_buf);
1457+
psql_scan_reset(scan_state);
1458+
}
1459+
14361460
/* \x -- set or toggle expanded table representation */
14371461
else if (strcmp(cmd, "x") == 0)
14381462
{
@@ -2555,6 +2579,112 @@ do_shell(const char *command)
25552579
return true;
25562580
}
25572581

2582+
/*
2583+
* do_watch -- handler for \watch
2584+
*
2585+
* We break this out of exec_command to avoid having to plaster "volatile"
2586+
* onto a bunch of exec_command's variables to silence stupider compilers.
2587+
*/
2588+
static bool
2589+
do_watch(PQExpBuffer query_buf, long sleep)
2590+
{
2591+
printQueryOpt myopt = pset.popt;
2592+
char title[50];
2593+
2594+
if (!query_buf || query_buf->len <= 0)
2595+
{
2596+
psql_error(_("\\watch cannot be used with an empty query\n"));
2597+
return false;
2598+
}
2599+
2600+
/*
2601+
* Set up rendering options, in particular, disable the pager, because
2602+
* nobody wants to be prompted while watching the output of 'watch'.
2603+
*/
2604+
myopt.nullPrint = NULL;
2605+
myopt.topt.pager = 0;
2606+
2607+
for (;;)
2608+
{
2609+
PGresult *res;
2610+
time_t timer;
2611+
long i;
2612+
2613+
/*
2614+
* Prepare title for output. XXX would it be better to use the time
2615+
* of completion of the command?
2616+
*/
2617+
timer = time(NULL);
2618+
snprintf(title, sizeof(title), _("Watch every %lds\t%s"),
2619+
sleep, asctime(localtime(&timer)));
2620+
myopt.title = title;
2621+
2622+
/*
2623+
* Run the query. We use PSQLexec, which is kind of cheating, but
2624+
* SendQuery doesn't let us suppress autocommit behavior.
2625+
*/
2626+
res = PSQLexec(query_buf->data, false);
2627+
2628+
/* PSQLexec handles failure results and returns NULL */
2629+
if (res == NULL)
2630+
break;
2631+
2632+
/*
2633+
* If SIGINT is sent while the query is processing, PSQLexec will
2634+
* consume the interrupt. The user's intention, though, is to cancel
2635+
* the entire watch process, so detect a sent cancellation request and
2636+
* exit in this case.
2637+
*/
2638+
if (cancel_pressed)
2639+
{
2640+
PQclear(res);
2641+
break;
2642+
}
2643+
2644+
switch (PQresultStatus(res))
2645+
{
2646+
case PGRES_TUPLES_OK:
2647+
printQuery(res, &myopt, pset.queryFout, pset.logfile);
2648+
break;
2649+
2650+
case PGRES_EMPTY_QUERY:
2651+
psql_error(_("\\watch cannot be used with an empty query\n"));
2652+
PQclear(res);
2653+
return false;
2654+
2655+
default:
2656+
/* should we fail for non-tuple-result commands? */
2657+
break;
2658+
}
2659+
2660+
PQclear(res);
2661+
2662+
/*
2663+
* Set up cancellation of 'watch' via SIGINT. We redo this each time
2664+
* through the loop since it's conceivable something inside PSQLexec
2665+
* could change sigint_interrupt_jmp.
2666+
*/
2667+
if (sigsetjmp(sigint_interrupt_jmp, 1) != 0)
2668+
break;
2669+
2670+
/*
2671+
* Enable 'watch' cancellations and wait a while before running the
2672+
* query again. Break the sleep into short intervals since pg_usleep
2673+
* isn't interruptible on some platforms.
2674+
*/
2675+
sigint_interrupt_enabled = true;
2676+
for (i = 0; i < sleep; i++)
2677+
{
2678+
pg_usleep(1000000L);
2679+
if (cancel_pressed)
2680+
break;
2681+
}
2682+
sigint_interrupt_enabled = false;
2683+
}
2684+
2685+
return true;
2686+
}
2687+
25582688
/*
25592689
* This function takes a function description, e.g. "x" or "x(int)", and
25602690
* issues a query on the given connection to retrieve the function's OID

src/bin/psql/help.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ slashUsage(unsigned short int pager)
165165

166166
currdb = PQdb(pset.db);
167167

168-
output = PageOutput(95, pager);
168+
output = PageOutput(96, pager);
169169

170170
/* if you add/remove a line here, change the row count above */
171171

@@ -175,6 +175,7 @@ slashUsage(unsigned short int pager)
175175
fprintf(output, _(" \\gset [PREFIX] execute query and store results in psql variables\n"));
176176
fprintf(output, _(" \\h [NAME] help on syntax of SQL commands, * for all commands\n"));
177177
fprintf(output, _(" \\q quit psql\n"));
178+
fprintf(output, _(" \\watch [SEC] execute query every SEC seconds\n"));
178179
fprintf(output, "\n");
179180

180181
fprintf(output, _("Query Buffer\n"));

src/bin/psql/tab-complete.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -900,7 +900,7 @@ psql_completion(char *text, int start, int end)
900900
"\\lo_import", "\\lo_export", "\\lo_list", "\\lo_unlink",
901901
"\\o", "\\p", "\\password", "\\prompt", "\\pset", "\\q", "\\qecho", "\\r",
902902
"\\set", "\\sf", "\\t", "\\T",
903-
"\\timing", "\\unset", "\\x", "\\w", "\\z", "\\!", NULL
903+
"\\timing", "\\unset", "\\x", "\\w", "\\watch", "\\z", "\\!", NULL
904904
};
905905

906906
(void) end; /* not used */

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy