Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 2c50c9f

Browse files
committed
Fix psql's "\g target" meta-command to work with COPY TO STDOUT.
Previously, \g would successfully execute the COPY command, but the target specification if any was ignored, so that the data was always dumped to the regular query output target. This seems like a clear bug, so let's not just fix it but back-patch it. While at it, adjust the documentation for \copy to recommend "COPY ... TO STDOUT \g foo" as a plausible alternative. Back-patch to 9.5. The problem exists much further back, but the code associated with \g was refactored enough in 9.5 that we'd need a significantly different patch for 9.4, and it doesn't seem worth the trouble. Daniel Vérité, reviewed by Fabien Coelho Discussion: https://postgr.es/m/15dadc39-e050-4d46-956b-dcc4ed098753@manitou-mail.org
1 parent c0aed69 commit 2c50c9f

File tree

3 files changed

+73
-13
lines changed

3 files changed

+73
-13
lines changed

doc/src/sgml/ref/psql-ref.sgml

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1027,10 +1027,24 @@ testdb=>
10271027

10281028
<tip>
10291029
<para>
1030-
This operation is not as efficient as the <acronym>SQL</acronym>
1031-
<command>COPY</command> command because all data must pass
1032-
through the client/server connection. For large
1033-
amounts of data the <acronym>SQL</acronym> command might be preferable.
1030+
Another way to obtain the same result as <literal>\copy
1031+
... to</literal> is to use the <acronym>SQL</acronym> <literal>COPY
1032+
... TO STDOUT</literal> command and terminate it
1033+
with <literal>\g <replaceable>filename</replaceable></literal>
1034+
or <literal>\g |<replaceable>program</replaceable></literal>.
1035+
Unlike <literal>\copy</literal>, this method allows the command to
1036+
span multiple lines; also, variable interpolation and backquote
1037+
expansion can be used.
1038+
</para>
1039+
</tip>
1040+
1041+
<tip>
1042+
<para>
1043+
These operations are not as efficient as the <acronym>SQL</acronym>
1044+
<command>COPY</command> command with a file or program data source or
1045+
destination, because all data must pass through the client/server
1046+
connection. For large amounts of data the <acronym>SQL</acronym>
1047+
command might be preferable.
10341048
</para>
10351049
</tip>
10361050

src/bin/psql/common.c

Lines changed: 50 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1092,20 +1092,49 @@ ProcessResult(PGresult **results)
10921092
* connection out of its COPY state, then call PQresultStatus()
10931093
* once and report any error.
10941094
*
1095-
* If pset.copyStream is set, use that as data source/sink,
1096-
* otherwise use queryFout or cur_cmd_source as appropriate.
1095+
* For COPY OUT, direct the output to pset.copyStream if it's set,
1096+
* otherwise to pset.gfname if it's set, otherwise to queryFout.
1097+
* For COPY IN, use pset.copyStream as data source if it's set,
1098+
* otherwise cur_cmd_source.
10971099
*/
1098-
FILE *copystream = pset.copyStream;
1100+
FILE *copystream;
10991101
PGresult *copy_result;
11001102

11011103
SetCancelConn();
11021104
if (result_status == PGRES_COPY_OUT)
11031105
{
1104-
if (!copystream)
1106+
bool need_close = false;
1107+
bool is_pipe = false;
1108+
1109+
if (pset.copyStream)
1110+
{
1111+
/* invoked by \copy */
1112+
copystream = pset.copyStream;
1113+
}
1114+
else if (pset.gfname)
1115+
{
1116+
/* invoked by \g */
1117+
if (openQueryOutputFile(pset.gfname,
1118+
&copystream, &is_pipe))
1119+
{
1120+
need_close = true;
1121+
if (is_pipe)
1122+
disable_sigpipe_trap();
1123+
}
1124+
else
1125+
copystream = NULL; /* discard COPY data entirely */
1126+
}
1127+
else
1128+
{
1129+
/* fall back to the generic query output stream */
11051130
copystream = pset.queryFout;
1131+
}
1132+
11061133
success = handleCopyOut(pset.db,
11071134
copystream,
1108-
&copy_result) && success;
1135+
&copy_result)
1136+
&& success
1137+
&& (copystream != NULL);
11091138

11101139
/*
11111140
* Suppress status printing if the report would go to the same
@@ -1117,11 +1146,25 @@ ProcessResult(PGresult **results)
11171146
PQclear(copy_result);
11181147
copy_result = NULL;
11191148
}
1149+
1150+
if (need_close)
1151+
{
1152+
/* close \g argument file/pipe */
1153+
if (is_pipe)
1154+
{
1155+
pclose(copystream);
1156+
restore_sigpipe_trap();
1157+
}
1158+
else
1159+
{
1160+
fclose(copystream);
1161+
}
1162+
}
11201163
}
11211164
else
11221165
{
1123-
if (!copystream)
1124-
copystream = pset.cur_cmd_source;
1166+
/* COPY IN */
1167+
copystream = pset.copyStream ? pset.copyStream : pset.cur_cmd_source;
11251168
success = handleCopyIn(pset.db,
11261169
copystream,
11271170
PQbinaryTuples(*results),

src/bin/psql/copy.c

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -425,7 +425,10 @@ do_copy(const char *args)
425425
*
426426
* conn should be a database connection that you just issued COPY TO on
427427
* and got back a PGRES_COPY_OUT result.
428+
*
428429
* copystream is the file stream for the data to go to.
430+
* copystream can be NULL to eat the data without writing it anywhere.
431+
*
429432
* The final status for the COPY is returned into *res (but note
430433
* we already reported the error, if it's not a success result).
431434
*
@@ -447,7 +450,7 @@ handleCopyOut(PGconn *conn, FILE *copystream, PGresult **res)
447450

448451
if (buf)
449452
{
450-
if (OK && fwrite(buf, 1, ret, copystream) != ret)
453+
if (OK && copystream && fwrite(buf, 1, ret, copystream) != ret)
451454
{
452455
psql_error("could not write COPY data: %s\n",
453456
strerror(errno));
@@ -458,7 +461,7 @@ handleCopyOut(PGconn *conn, FILE *copystream, PGresult **res)
458461
}
459462
}
460463

461-
if (OK && fflush(copystream))
464+
if (OK && copystream && fflush(copystream))
462465
{
463466
psql_error("could not write COPY data: %s\n",
464467
strerror(errno));

0 commit comments

Comments
 (0)