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, 
&copystream, &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,
                                                                                
&copy_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;
 

Reply via email to