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"

Reply via email to