Hi

Here is a little bit updated patch - detection of end of any child process
cannot be used on WIN32. I am not an expert on this platform, but from what
I read about it, there is no easy solution. The problem is in _popen
function. We lost the handle of the created process, and it is not possible
to find it. Writing a new implementation of _popen function looks like a
big overkill to me. We can disable this functionality there completely (on
win32) or we can accept the waiting time after pager has ended until we
detect pipe error. I hope so this is acceptable, in this moment, because a)
there are not pspg for windows (and there was only few requests for porting
there in last 4 years), b) usage of psql on mswin platform is not too wide,
c) in near future, there will be an possibility to use Unix psql on this
platform.

Regards

Pavel
diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml
index 13c1edfa4d..fb70258b0e 100644
--- a/doc/src/sgml/ref/psql-ref.sgml
+++ b/doc/src/sgml/ref/psql-ref.sgml
@@ -4652,6 +4652,21 @@ PSQL_EDITOR_LINENUMBER_ARG='--line '
     </listitem>
    </varlistentry>
 
+   <varlistentry>
+    <term><envar>PSQL_WATCH_PAGER</envar></term>
+
+    <listitem>
+     <para>
+      When an statement is evaluated repeatedly because <command>\watch</command>
+      was used, then an pager is not used. This behaviour can be changed by
+      setting <envar>PSQL_WATCH_PAGER</envar> to pager with necessary options.
+      Currently only <literal>pspg</literal> pager (version 3.0+) supports this
+      functionality (with an option <literal>--stream</literal>).
+     </para>
+
+    </listitem>
+   </varlistentry>
+
    <varlistentry>
     <term><envar>PSQLRC</envar></term>
 
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index c98e3d31d0..9173f810c1 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -12,6 +12,7 @@
 #include <pwd.h>
 #ifndef WIN32
 #include <sys/stat.h>			/* for stat() */
+#include <sys/wait.h>			/* for waitpid() */
 #include <fcntl.h>				/* open() flags */
 #include <unistd.h>				/* for geteuid(), getpid(), stat() */
 #else
@@ -4766,6 +4767,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;
 
@@ -4775,6 +4778,17 @@ do_watch(PQExpBuffer query_buf, double sleep)
 		return false;
 	}
 
+	pagerprog = getenv("PSQL_WATCH_PAGER");
+	if (pagerprog)
+	{
+		disable_sigpipe_trap();
+		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
@@ -4783,10 +4797,11 @@ do_watch(PQExpBuffer query_buf, double sleep)
 	strftime_fmt = "%c";
 
 	/*
-	 * Set up rendering options, in particular, disable the pager, because
-	 * nobody wants to be prompted while watching the output of 'watch'.
+	 * Set up rendering options, in particular, disable the pager, when
+	 * there an pipe to pager is not available.
 	 */
-	myopt.topt.pager = 0;
+	if (!pagerpipe)
+		myopt.topt.pager = 0;
 
 	/*
 	 * If there's a title in the user configuration, make sure we have room
@@ -4820,7 +4835,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
@@ -4829,6 +4844,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
@@ -4839,23 +4857,63 @@ do_watch(PQExpBuffer query_buf, double sleep)
 
 		/*
 		 * Enable 'watch' cancellations and wait a while before running the
-		 * query again.  Break the sleep into short intervals (at most 1s)
-		 * since pg_usleep isn't interruptible on some platforms.
+		 * query again.  Break the sleep into short intervals (at most 100ms)
+		 * since pg_usleep isn't interruptible on some platforms. The overhead
+		 * of 100ms interval is compromise between overhead and ergonomentry
+		 * (we don't want to wait long time after pager was ended).
 		 */
 		sigint_interrupt_enabled = true;
 		i = sleep_ms;
 		while (i > 0)
 		{
+#ifdef WIN32
 			long		s = Min(i, 1000L);
 
-			pg_usleep(s * 1000L);
+			pg_usleep(1000L * s);
+
+#else
+			long		s = Min(i, 100L);
+
+			pg_usleep(1000L * s);
+
+			/*
+			 * in this moment an pager process can be only one child of
+			 * psql process. There cannot be other processes. So we can
+			 * detect end of any child process for fast detection of
+			 * pager process.
+			 *
+			 * This simple detection doesn't work on WIN32, because we
+			 * don't know handle of process created by _popen function.
+			 * Own implementation of _popen function based on CreateProcess
+			 * looks like overkill in this moment.
+			 */
+			if (pagerpipe)
+			{
+
+				int		status;
+				pid_t	pid;
+
+				pid = waitpid(-1, &status, WNOHANG);
+				if (pid)
+					break;
+			}
+
+#endif
+
 			if (cancel_pressed)
 				break;
+
 			i -= s;
 		}
 		sigint_interrupt_enabled = false;
 	}
 
+	if (pagerpipe)
+	{
+		pclose(pagerpipe);
+		restore_sigpipe_trap();
+	}
+
 	pg_free(title);
 	return (res >= 0);
 }
diff --git a/src/bin/psql/common.c b/src/bin/psql/common.c
index 925fe34a3f..29d8fd2aeb 100644
--- a/src/bin/psql/common.c
+++ b/src/bin/psql/common.c
@@ -592,12 +592,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)
 	{
@@ -638,14 +639,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:
@@ -668,7 +671,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 041b2ac068..d8538a4e06 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 e44120bf76..673aa3304f 100644
--- a/src/bin/psql/help.c
+++ b/src/bin/psql/help.c
@@ -348,7 +348,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"));
 
@@ -504,6 +504,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