psql-watch-v1.patch
application/octet-stream
Filename: psql-watch-v1.patch
Type: application/octet-stream
Part: 0
Message:
patch to add \watch to psql
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: context
Series: patch v1
| File | + | − |
|---|---|---|
| src/bin/psql/command.c | 94 | 0 |
| src/bin/psql/help.c | 1 | 0 |
| src/bin/psql/tab-complete.c | 1 | 0 |
*** 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 */