I wrote:
> I admit that if we could improve \g to handle COPY, it would be more
> elegant than the current proposal adding two meta-commands.
>
> But the copy-workflow and non-copy-workflow are different, and in
> order to know which one to start, \g would need to analyze the query
It turns out I was wrong on this. The workflows are different but when
psql receives the first PGresult, it's still time to handle the I/O
redirection. It just differs from \copy in the case of an error:
\copy won't even send a command to the server if the local output
stream can't be opened, whereas COPY \g would send the command, and
will have to deal with the client-side error afterwards.
Well, except that up to now, COPY ... TO STDOUT \g file or \g |command
has been ignoring "file" or "|command", which is arguably a bug as Tom
puts it in another discussion in [1].
So as a replacement for the \copyto I was proposing earlier, PFA a patch
for COPY TO STDOUT to make use of the argument to \g.
[1] bug #15535
https://www.postgresql.org/message-id/[email protected]
Best regards,
--
Daniel Vérité
PostgreSQL-powered mailer: http://www.manitou-mail.org
Twitter: @DanielVerite
diff --git a/src/bin/psql/common.c b/src/bin/psql/common.c
index 62c2928..1488bb9 100644
--- a/src/bin/psql/common.c
+++ b/src/bin/psql/common.c
@@ -1095,14 +1095,31 @@ ProcessResult(PGresult **results)
* If pset.copyStream is set, use that as data
source/sink,
* otherwise use queryFout or cur_cmd_source as
appropriate.
*/
- FILE *copystream = pset.copyStream;
+ FILE *copystream = NULL;
PGresult *copy_result;
SetCancelConn();
if (result_status == PGRES_COPY_OUT)
{
- if (!copystream)
+ bool is_pipe;
+ if (pset.gfname)
+ {
+ /*
+ * COPY TO STDOUT \g [|]file may be
used as an alternative
+ * to \copy
+ */
+ if (!openQueryOutputFile(pset.gfname,
©stream, &is_pipe))
+ {
+ copystream = NULL; /* will
discard the COPY data entirely */
+ }
+ if (is_pipe)
+ disable_sigpipe_trap();
+ }
+ else if (pset.copyStream)
+ copystream = pset.copyStream;
+ else
copystream = pset.queryFout;
+
success = handleCopyOut(pset.db,
copystream,
©_result) && success;
@@ -1117,11 +1134,25 @@ ProcessResult(PGresult **results)
PQclear(copy_result);
copy_result = NULL;
}
+
+ if (pset.gfname && copystream != NULL)
+ {
+ /* close \g argument file/pipe */
+ if (is_pipe)
+ {
+ pclose(copystream);
+ restore_sigpipe_trap();
+ }
+ else
+ {
+ fclose(copystream);
+ }
+ }
}
else
{
- if (!copystream)
- copystream = pset.cur_cmd_source;
+ /* COPY IN */
+ copystream = pset.copyStream ? pset.copyStream
: pset.cur_cmd_source;
success = handleCopyIn(pset.db,
copystream,
PQbinaryTuples(*results),
diff --git a/src/bin/psql/copy.c b/src/bin/psql/copy.c
index 555c633..645970e 100644
--- a/src/bin/psql/copy.c
+++ b/src/bin/psql/copy.c
@@ -426,6 +426,7 @@ do_copy(const char *args)
* conn should be a database connection that you just issued COPY TO on
* and got back a PGRES_COPY_OUT result.
* copystream is the file stream for the data to go to.
+ * copystream can be NULL to pump the data without writing it anywhere.
* The final status for the COPY is returned into *res (but note
* we already reported the error, if it's not a success result).
*
@@ -434,7 +435,7 @@ do_copy(const char *args)
bool
handleCopyOut(PGconn *conn, FILE *copystream, PGresult **res)
{
- bool OK = true;
+ bool OK = (copystream != NULL);
char *buf;
int ret;