Hi, last week I finished pspg 3.0 https://github.com/okbob/pspg . pspg now supports pipes, named pipes very well. Today the pspg can be used as pager for output of \watch command. Sure, psql needs attached patch.
I propose new psql environment variable PSQL_WATCH_PAGER. When this variable is not empty, then \watch command starts specified pager, and redirect output to related pipe. When pipe is closed - by pager, then \watch cycle is leaved. If you want to test proposed feature, you need a pspg with cb4114f98318344d162a84b895a3b7f8badec241 commit. Then you can set your env export PSQL_WATCH_PAGER="pspg --stream" psql SELECT * FROM pg_stat_database; \watch 1 Comments, notes? Regards Pavel
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c index a5160f91de..b78c0b3100 100644 --- a/src/bin/psql/command.c +++ b/src/bin/psql/command.c @@ -171,6 +171,7 @@ static char *pset_value_string(const char *param, printQueryOpt *popt); static void checkWin32Codepage(void); #endif +static bool sigpipe_received = false; /*---------- @@ -4637,6 +4638,17 @@ do_shell(const char *command) return true; } +/* + * We want to detect sigpipe to break from watch cycle faster, + * before waiting 1 sec in sleep time, and unsuccess write to + * pipe. + */ +static void +pagerpipe_sigpipe_handler(int signum) +{ + sigpipe_received = true; +} + /* * do_watch -- handler for \watch * @@ -4651,6 +4663,8 @@ do_watch(PQExpBuffer query_buf, double sleep) const char *strftime_fmt; const char *user_title; char *title; + const char *pagerprog = NULL; + FILE *pagerpipe = NULL; int title_len; int res = 0; @@ -4660,6 +4674,21 @@ do_watch(PQExpBuffer query_buf, double sleep) return false; } + pagerprog = getenv("PSQL_WATCH_PAGER"); + if (pagerprog) + { + sigpipe_received = false; + +#ifndef WIN32 + pqsignal(SIGPIPE, pagerpipe_sigpipe_handler); +#endif + + pagerpipe = popen(pagerprog, "w"); + if (!pagerpipe) + /*silently proceed without pager */ + restore_sigpipe_trap(); + } + /* * Choose format for timestamps. We might eventually make this a \pset * option. In the meantime, using a variable for the format suppresses @@ -4671,7 +4700,8 @@ do_watch(PQExpBuffer query_buf, double sleep) * Set up rendering options, in particular, disable the pager, because * nobody wants to be prompted while watching the output of 'watch'. */ - myopt.topt.pager = 0; + if (!pagerpipe) + myopt.topt.pager = 0; /* * If there's a title in the user configuration, make sure we have room @@ -4705,7 +4735,7 @@ do_watch(PQExpBuffer query_buf, double sleep) myopt.title = title; /* Run the query and print out the results */ - res = PSQLexecWatch(query_buf->data, &myopt); + res = PSQLexecWatch(query_buf->data, &myopt, pagerpipe); /* * PSQLexecWatch handles the case where we can no longer repeat the @@ -4714,6 +4744,9 @@ do_watch(PQExpBuffer query_buf, double sleep) if (res <= 0) break; + if (pagerpipe && ferror(pagerpipe)) + break; + /* * Set up cancellation of 'watch' via SIGINT. We redo this each time * through the loop since it's conceivable something inside @@ -4731,14 +4764,27 @@ do_watch(PQExpBuffer query_buf, double sleep) i = sleep_ms; while (i > 0) { - long s = Min(i, 1000L); + long s = Min(i, pagerpipe ? 100L : 1000L); pg_usleep(s * 1000L); if (cancel_pressed) break; + + if (sigpipe_received) + break; + i -= s; } sigint_interrupt_enabled = false; + + if (sigpipe_received) + break; + } + + if (pagerpipe) + { + fclose(pagerpipe); + restore_sigpipe_trap(); } pg_free(title); diff --git a/src/bin/psql/common.c b/src/bin/psql/common.c index 621a33f7e8..64a58eacce 100644 --- a/src/bin/psql/common.c +++ b/src/bin/psql/common.c @@ -588,12 +588,13 @@ PSQLexec(const char *query) * e.g., because of the interrupt, -1 on error. */ int -PSQLexecWatch(const char *query, const printQueryOpt *opt) +PSQLexecWatch(const char *query, const printQueryOpt *opt, FILE *printQueryFout) { PGresult *res; double elapsed_msec = 0; instr_time before; instr_time after; + FILE *fout; if (!pset.db) { @@ -634,14 +635,16 @@ PSQLexecWatch(const char *query, const printQueryOpt *opt) return 0; } + fout = printQueryFout ? printQueryFout : pset.queryFout; + switch (PQresultStatus(res)) { case PGRES_TUPLES_OK: - printQuery(res, opt, pset.queryFout, false, pset.logfile); + printQuery(res, opt, fout, false, pset.logfile); break; case PGRES_COMMAND_OK: - fprintf(pset.queryFout, "%s\n%s\n\n", opt->title, PQcmdStatus(res)); + fprintf(fout, "%s\n%s\n\n", opt->title, PQcmdStatus(res)); break; case PGRES_EMPTY_QUERY: @@ -664,7 +667,7 @@ PSQLexecWatch(const char *query, const printQueryOpt *opt) PQclear(res); - fflush(pset.queryFout); + fflush(fout); /* Possible microtiming output */ if (pset.timing) diff --git a/src/bin/psql/common.h b/src/bin/psql/common.h index ec4e83c9fd..d10cd01d16 100644 --- a/src/bin/psql/common.h +++ b/src/bin/psql/common.h @@ -29,7 +29,7 @@ extern sigjmp_buf sigint_interrupt_jmp; extern void psql_setup_cancel_handler(void); extern PGresult *PSQLexec(const char *query); -extern int PSQLexecWatch(const char *query, const printQueryOpt *opt); +extern int PSQLexecWatch(const char *query, const printQueryOpt *opt, FILE *printQueryFout); extern bool SendQuery(const char *query); diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c index e750948042..5eb16b6458 100644 --- a/src/bin/psql/help.c +++ b/src/bin/psql/help.c @@ -347,7 +347,7 @@ helpVariables(unsigned short int pager) * Windows builds currently print one more line than non-Windows builds. * Using the larger number is fine. */ - output = PageOutput(158, pager ? &(pset.popt.topt) : NULL); + output = PageOutput(160, pager ? &(pset.popt.topt) : NULL); fprintf(output, _("List of specially treated variables\n\n")); @@ -503,6 +503,8 @@ helpVariables(unsigned short int pager) " alternative location for the command history file\n")); fprintf(output, _(" PSQL_PAGER, PAGER\n" " name of external pager program\n")); + fprintf(output, _(" PSQL_WATCH_PAGER\n" + " name of external pager program used for watch mode\n")); fprintf(output, _(" PSQLRC\n" " alternative location for the user's .psqlrc file\n")); fprintf(output, _(" SHELL\n"