*** c/src/bin/psql/command.c --- w/src/bin/psql/command.c *************** *** 1393,1398 **** exec_command(const char *cmd, --- 1393,1492 ---- free(fname); } + /* \watch -- execute a query every N seconds */ + else if (strcmp(cmd, "watch") == 0) + { + char *value; + PQExpBufferData buf; + + /* Volatile to prevent clobbering by by longjmp. */ + volatile PGresult *res = NULL; + printQueryOpt myopt = pset.popt; + char quoted; + bool first = true; + long sleep = 2; + char title[50]; + time_t timer; + + const int max_watch_delay = 86400; /* seconds in a day */ + + + initPQExpBuffer(&buf); + + while ((value = psql_scan_slash_option(scan_state, + OT_NORMAL, "ed, false))) + { + /* + * If the first value scans as an integer, adjust the watch delay + * time. Otherwise, use a default. + */ + if (first && strtol(value, NULL, 10)) + { + first = false; + + sleep = strtol(value, NULL, 10); + + if (sleep < 0) + sleep = 0; + else if (sleep > max_watch_delay) + sleep = max_watch_delay; + } + else + { + appendPQExpBufferStr(&buf, " "); + appendPQExpBufferStr(&buf, value); + } + + free(value); + } + + /* + * Set up rendering options, in particular, disable the pager, because + * nobody wants to be prompted while watching the output of 'watch'. + */ + myopt.nullPrint = NULL; + myopt.topt.pager = 0; + + /* Set up cancellation of 'watch' via SIGINT. */ + if (sigsetjmp(sigint_interrupt_jmp, 1) != 0) + goto cleanup; + + while (true) + { + timer = time(NULL); + snprintf(title, sizeof(title), "Watch every %lds\t%s", sleep, + asctime(localtime(&timer))); + myopt.title = title; + res = PSQLexec(buf.data, false); + + /* + * If SIGINT is sent while the query is processing, PSQLexec will + * consume the interrupt. The user's intention, though, is to + * cancel the entire watch process, so detect a sent cancellation + * request and exit in this case. + */ + if (cancel_pressed) + goto cleanup; + + if (res) + printQuery((PGresult *) res, &myopt, pset.queryFout, + pset.logfile); + + /* + * Enable 'watch' cancellations and wait a while before running the + * query again. + */ + sigint_interrupt_enabled = true; + pg_usleep(1000000 * sleep); + sigint_interrupt_enabled = false; + } + + cleanup: + termPQExpBuffer(&buf); + if (res) + PQclear((PGresult *) res); + } + /* \x -- set or toggle expanded table representation */ else if (strcmp(cmd, "x") == 0) { *** c/src/bin/psql/help.c --- w/src/bin/psql/help.c *************** *** 191,196 **** slashUsage(unsigned short int pager) --- 191,197 ---- fprintf(output, _(" \\ir FILE as \\i, but relative to location of current script\n")); fprintf(output, _(" \\o [FILE] send all query results to file or |pipe\n")); fprintf(output, _(" \\qecho [STRING] write string to query output stream (see \\o)\n")); + fprintf(output, _(" \\watch [SEC] STRING execute query every SEC seconds\n")); fprintf(output, "\n"); fprintf(output, _("Informational\n")); *** c/src/bin/psql/tab-complete.c --- w/src/bin/psql/tab-complete.c *************** *** 860,866 **** psql_completion(char *text, int start, int end) "\\lo_import", "\\lo_export", "\\lo_list", "\\lo_unlink", "\\o", "\\p", "\\password", "\\prompt", "\\pset", "\\q", "\\qecho", "\\r", "\\set", "\\sf", "\\t", "\\T", ! "\\timing", "\\unset", "\\x", "\\w", "\\z", "\\!", NULL }; (void) end; /* not used */ --- 860,866 ---- "\\lo_import", "\\lo_export", "\\lo_list", "\\lo_unlink", "\\o", "\\p", "\\password", "\\prompt", "\\pset", "\\q", "\\qecho", "\\r", "\\set", "\\sf", "\\t", "\\T", ! "\\timing", "\\unset", "\\x", "\\w", "\\watch", "\\z", "\\!", NULL }; (void) end; /* not used */