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"