diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml index 941090ba8a..d76ed41149 100644 --- a/doc/src/sgml/ref/psql-ref.sgml +++ b/doc/src/sgml/ref/psql-ref.sgml @@ -1027,10 +1027,24 @@ testdb=> - This operation is not as efficient as the SQL - COPY command because all data must pass - through the client/server connection. For large - amounts of data the SQL command might be preferable. + Another way to obtain the same result as \copy + ... to is to use the SQL COPY + ... TO STDOUT command and terminate it + with \g filename + or \g |program. + Unlike \copy, this method allows the command to + span multiple lines; also, variable interpolation and backquote + expansion can be used. + + + + + + These operations are not as efficient as the SQL + COPY command with a file or program data source or + destination, because all data must pass through the client/server + connection. For large amounts of data the SQL + command might be preferable. diff --git a/src/bin/psql/common.c b/src/bin/psql/common.c index 62c2928e6b..26bf613c1d 100644 --- a/src/bin/psql/common.c +++ b/src/bin/psql/common.c @@ -1092,20 +1092,49 @@ ProcessResult(PGresult **results) * connection out of its COPY state, then call PQresultStatus() * once and report any error. * - * If pset.copyStream is set, use that as data source/sink, - * otherwise use queryFout or cur_cmd_source as appropriate. + * For COPY OUT, direct the output to pset.copyStream if it's set, + * otherwise to pset.gfname if it's set, otherwise to queryFout. + * For COPY IN, use pset.copyStream as data source if it's set, + * otherwise cur_cmd_source. */ - FILE *copystream = pset.copyStream; + FILE *copystream; PGresult *copy_result; SetCancelConn(); if (result_status == PGRES_COPY_OUT) { - if (!copystream) + bool need_close = false; + bool is_pipe = false; + + if (pset.copyStream) + { + /* invoked by \copy */ + copystream = pset.copyStream; + } + else if (pset.gfname) + { + /* invoked by \g */ + if (openQueryOutputFile(pset.gfname, + ©stream, &is_pipe)) + { + need_close = true; + if (is_pipe) + disable_sigpipe_trap(); + } + else + copystream = NULL; /* discard COPY data entirely */ + } + else + { + /* fall back to the generic query output stream */ copystream = pset.queryFout; + } + success = handleCopyOut(pset.db, copystream, - ©_result) && success; + ©_result) + && success + && (copystream != NULL); /* * Suppress status printing if the report would go to the same @@ -1117,11 +1146,25 @@ ProcessResult(PGresult **results) PQclear(copy_result); copy_result = NULL; } + + if (need_close) + { + /* 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 555c6331a3..20a744509b 100644 --- a/src/bin/psql/copy.c +++ b/src/bin/psql/copy.c @@ -425,7 +425,10 @@ 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 eat 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). * @@ -447,7 +450,7 @@ handleCopyOut(PGconn *conn, FILE *copystream, PGresult **res) if (buf) { - if (OK && fwrite(buf, 1, ret, copystream) != ret) + if (OK && copystream && fwrite(buf, 1, ret, copystream) != ret) { psql_error("could not write COPY data: %s\n", strerror(errno)); @@ -458,7 +461,7 @@ handleCopyOut(PGconn *conn, FILE *copystream, PGresult **res) } } - if (OK && fflush(copystream)) + if (OK && copystream && fflush(copystream)) { psql_error("could not write COPY data: %s\n", strerror(errno));