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

Reply via email to