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/6309.1544031...@sss.pgh.pa.us 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;