Hi čt 4. 3. 2021 v 7:37 odesílatel Pavel Stehule <pavel.steh...@gmail.com> napsal:
> 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. > > second version - after some thinking, I think the pager for \watch command should be controlled by option "pager" too. When the pager is disabled on psql level, then the pager will not be used for \watch too. Regards Pavel > Regards > > Pavel > >
diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml index 13c1edfa4d..9a88c3f8ef 100644 --- a/doc/src/sgml/ref/psql-ref.sgml +++ b/doc/src/sgml/ref/psql-ref.sgml @@ -2993,6 +2993,11 @@ lo_import 152801 (such as <filename>more</filename>) is used. </para> + <para> + When the ennvironment variable <envar>PSQL_WATCH_PAGER</envar> is set, then + the output of repeated execution of query is piped to the specified program. + </para> + <para> When the <literal>pager</literal> option is <literal>off</literal>, the pager program is not used. When the <literal>pager</literal> option is @@ -3004,6 +3009,13 @@ lo_import 152801 without a <replaceable class="parameter">value</replaceable> toggles pager use on and off. </para> + + <para> + When an command <command>\watch</command> is executed, and environment + variable <envar>PSQL_WATCH_PAGER</envar> is defined, but the value of + the option <literal>pager</literal> is <literal>off</literal>, then the + pager is not used. + </para> </listitem> </varlistentry> @@ -4652,6 +4664,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..290efed3d3 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,25 @@ do_watch(PQExpBuffer query_buf, double sleep) return false; } + /* + * For usual queries, the pager can be used always, or + * newer, or when then content is larger than screen. In this case, + * the decision based on then content size has not sense, because the + * content can be different any time, but pager (in this case) is + * used longer time. So we use pager if the variable PSQL_WATCH_PAGER + * is defined and if pager is not disabled. + */ + pagerprog = getenv("PSQL_WATCH_PAGER"); + if (pagerprog && myopt.topt.pager) + { + 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 +4805,12 @@ 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 +4844,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 +4853,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 +4866,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"