Hi, This patch gives the end user control over psql's error stream. This allows a single psql session to use \o to send both errors and table output to multiple files. Useful when capturing test output, etc.
Control is provided via a new "estream" \pset. Here's the docs. -------------------------<snip>------------------------ estream Controls the output stream(s) used to report error messages. Value must be one of: stderr (the default), which sends errors to the standard error stream; query, which injects error messages into the query result output stream; or both, which sends errors to both output streams. "Error messages" are comprised of errors from psql and notice messages and errors from the database server. -------------------------<snip>------------------------ Against head. psql-estream.patch The patch. psql-estream_test.patch Adds a regression test to test the patch. There's a number of problems with psql-estream_test.patch, the most notable is that it probably won't work on MS Windows because it uses /dev/null to avoid touching the host filesystem. I'm not sure whether this should have a regression test and if so what the right way is to do it. Note that psql-stream.patch includes some re-writing of the docs for the psql \o option that goes slightly beyond the minimum change required to explain \pset estream's effects. Regards, Karl <k...@meme.com> Free Software: "You don't pay back, you pay forward." -- Robert A. Heinlein
diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml index c41593c..695739e 100644 --- a/doc/src/sgml/ref/psql-ref.sgml +++ b/doc/src/sgml/ref/psql-ref.sgml @@ -1800,11 +1800,16 @@ lo_import 152801 specified, the query output will be reset to the standard output. </para> - <para><quote>Query results</quote> includes all tables, command - responses, and notices obtained from the database server, as - well as output of various backslash commands that query the - database (such as <command>\d</command>), but not error - messages. + <para><quote>Query results</quote> includes all tables, sql + command responses and status information, notifications from + <command>LISTEN</command>, and output of various backslash + commands that query the database (such as + <command>\d</command>). <quote>Query results</quote> do not + include errors from <application>psql</application>or notice + messages or errors from the database server unless + <command>\pset estream</command> is used. Nor do they include + output of backslash commands which do not query the database + (such as <command>\h</command>). </para> <tip> @@ -1863,16 +1868,18 @@ lo_import 152801 <listitem> <para> - This command sets options affecting the output of query result tables. - <replaceable class="parameter">option</replaceable> - indicates which option is to be set. The semantics of - <replaceable class="parameter">value</replaceable> vary depending - on the selected option. For some options, omitting <replaceable - class="parameter">value</replaceable> causes the option to be toggled - or unset, as described under the particular option. If no such - behavior is mentioned, then omitting - <replaceable class="parameter">value</replaceable> just results in - the current setting being displayed. + This command sets options affecting the output of errors and + query results. There are extensive options regarding table + formatting. <replaceable + class="parameter">option</replaceable> indicates which option + is to be set. The semantics of <replaceable + class="parameter">value</replaceable> vary depending on the + selected option. For some options, omitting <replaceable + class="parameter">value</replaceable> causes the option to be + toggled or unset, as described under the particular option. + If no such behavior is mentioned, then omitting <replaceable + class="parameter">value</replaceable> just results in the + current setting being displayed. </para> <para> @@ -1914,6 +1921,24 @@ lo_import 152801 </varlistentry> <varlistentry> + <term><literal>estream</literal></term> + <listitem> + <para> + Controls the output stream(s) used to report error messages. + <replaceable class="parameter">Value</replaceable> must be + one of: <literal>stderr</literal> (the default), which sends + errors to the standard error stream; + <literal>query</literal>, which injects error messages into + the query result output stream; or <literal>both</literal>, + which sends errors to both output streams. <quote>Error + messages</quote> are comprised of errors from + <application>psql</application> and notice messages and + errors from the database server. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><literal>expanded</literal> (or <literal>x</literal>)</term> <listitem> <para> diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c index 8ccd00d..454a4b9 100644 --- a/src/bin/psql/command.c +++ b/src/bin/psql/command.c @@ -2448,6 +2448,32 @@ do_pset(const char *param, const char *value, printQueryOpt *popt, bool quiet) printf(_("Target width is %d.\n"), popt->topt.columns); } + /* set error output stream */ + else if (strcmp(param, "estream") == 0) + { + if (!value) + ; + else if (pg_strncasecmp("stderr", value, vallen) == 0) + pset.estream = PSQL_ESTREAM_STDERR; + else if (pg_strncasecmp("query", value, vallen) == 0) + pset.estream = PSQL_ESTREAM_QUERY; + else if (pg_strncasecmp("both", value, vallen) == 0) + pset.estream = PSQL_ESTREAM_BOTH; + else + { + psql_error("\\pset: allowed estream values are stderr, query, both\n"); + return false; + } + + if (!quiet) + if (pset.estream == PSQL_ESTREAM_STDERR) + puts(_("Errors sent to stderr.")); + else if (pset.estream == PSQL_ESTREAM_QUERY) + puts(_("Errors injected into query output.")); + else + puts(_("Errors sent to stderr and injected into query output.")); + } + else { psql_error("\\pset: unknown option: %s\n", param); diff --git a/src/bin/psql/common.c b/src/bin/psql/common.c index 179c162..6183064 100644 --- a/src/bin/psql/common.c +++ b/src/bin/psql/common.c @@ -153,10 +153,19 @@ psql_error(const char *fmt,...) if (pset.queryFout && pset.queryFout != stdout) fflush(pset.queryFout); - if (pset.inputfile) - fprintf(stderr, "%s:%s:" UINT64_FORMAT ": ", pset.progname, pset.inputfile, pset.lineno); + if (pset.inputfile) { + if (pset.estream != PSQL_ESTREAM_QUERY) + fprintf(stderr, "%s:%s:" UINT64_FORMAT ": ", + pset.progname, pset.inputfile, pset.lineno); + if (pset.estream != PSQL_ESTREAM_STDERR) + fprintf(pset.queryFout, "%s:%s:" UINT64_FORMAT ": ", + pset.progname, pset.inputfile, pset.lineno); + } va_start(ap, fmt); - vfprintf(stderr, _(fmt), ap); + if (pset.estream != PSQL_ESTREAM_QUERY) + vfprintf(stderr, _(fmt), ap); + if (pset.estream != PSQL_ESTREAM_STDERR) + vfprintf(pset.queryFout, _(fmt), ap); va_end(ap); } diff --git a/src/bin/psql/settings.h b/src/bin/psql/settings.h index c907fa0..5efb782 100644 --- a/src/bin/psql/settings.h +++ b/src/bin/psql/settings.h @@ -63,12 +63,20 @@ enum trivalue TRI_YES }; +typedef enum +{ + PSQL_ESTREAM_STDERR, + PSQL_ESTREAM_QUERY, + PSQL_ESTREAM_BOTH +} PSQL_ESTREAM; + typedef struct _psqlSettings { PGconn *db; /* connection to backend */ int encoding; /* client_encoding */ FILE *queryFout; /* where to send the query results */ bool queryFoutPipe; /* queryFout is from a popen() */ + PSQL_ESTREAM estream; /* where to send errors */ printQueryOpt popt; diff --git a/src/bin/psql/startup.c b/src/bin/psql/startup.c index 1fcc47f..bd91025 100644 --- a/src/bin/psql/startup.c +++ b/src/bin/psql/startup.c @@ -120,6 +120,7 @@ main(int argc, char *argv[]) pset.encoding = PQenv2encoding(); pset.queryFout = stdout; pset.queryFoutPipe = false; + pset.estream = PSQL_ESTREAM_STDERR; pset.cur_cmd_source = stdin; pset.cur_cmd_interactive = false;
diff --git a/src/test/regress/expected/estream.out b/src/test/regress/expected/estream.out new file mode 100644 index 0000000..1461aed --- /dev/null +++ b/src/test/regress/expected/estream.out @@ -0,0 +1,40 @@ +-- +-- Test of psql's \pset estream parameter. +-- Don't use an actual table name since we might be running in parallel. +-- +-- Bugs: +-- o Uses /dev/null, which probably breaks on non-Unix boxes. +-- o Due to the nature of the test framework, never looks at +-- what's saved to the output file. Instead tests what's +-- missing from stdout/stderr. +-- +\o /dev/null +-- This should not send an error into the output file, but +-- should send it to stderr. +select * from f oo; +ERROR: relation "f" does not exist +LINE 1: select * from f oo; + ^ +-- This should send an error into the output file, but not stderr. +\pset estream query +select * from f oo; +-- This should send an error both places. +\pset estream both +select * from f oo; +ERROR: relation "f" does not exist +LINE 1: select * from f oo; + ^ +-- And this should return to the initial (default) setting, +-- not send an error to the output file but do send it +-- to stderr. +\pset estream stderr +select * from f oo; +ERROR: relation "f" does not exist +LINE 1: select * from f oo; + ^ +-- This should produce an error from psql. +\pset estream bar +\pset: allowed estream values are stderr, query, both +-- This should display the current estream setting. +-- (But does not in pg_regress.) +\pset estream diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule index 663bf8a..594d60d 100644 --- a/src/test/regress/parallel_schedule +++ b/src/test/regress/parallel_schedule @@ -88,7 +88,7 @@ test: privileges security_label collate # ---------- # Another group of parallel tests # ---------- -test: misc alter_generic +test: misc alter_generic estream # rules cannot run concurrently with any test that creates a view test: rules diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule index be789e3..d8f2867 100644 --- a/src/test/regress/serial_schedule +++ b/src/test/regress/serial_schedule @@ -96,6 +96,7 @@ test: security_label test: collate test: misc test: alter_generic +test: estream test: rules test: event_trigger test: select_views diff --git a/src/test/regress/sql/estream.sql b/src/test/regress/sql/estream.sql new file mode 100644 index 0000000..25f3b80 --- /dev/null +++ b/src/test/regress/sql/estream.sql @@ -0,0 +1,37 @@ +-- +-- Test of psql's \pset estream parameter. +-- Don't use an actual table name since we might be running in parallel. +-- +-- Bugs: +-- o Uses /dev/null, which probably breaks on non-Unix boxes. +-- o Due to the nature of the test framework, never looks at +-- what's saved to the output file. Instead tests what's +-- missing from stdout/stderr. +-- + +\o /dev/null + +-- This should not send an error into the output file, but +-- should send it to stderr. +select * from f oo; + +-- This should send an error into the output file, but not stderr. +\pset estream query +select * from f oo; + +-- This should send an error both places. +\pset estream both +select * from f oo; + +-- And this should return to the initial (default) setting, +-- not send an error to the output file but do send it +-- to stderr. +\pset estream stderr +select * from f oo; + +-- This should produce an error from psql. +\pset estream bar + +-- This should display the current estream setting. +-- (But does not in pg_regress.) +\pset estream
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers