Skip to content

Commit 223ae69

Browse files
committed
Support binary COPY through psql. Also improve detection of write errors
during COPY OUT. Andreas Pflug, some editorialization by moi.
1 parent aadd8a2 commit 223ae69

File tree

4 files changed

+100
-57
lines changed

4 files changed

+100
-57
lines changed

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

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<!--
2-
$PostgreSQL: pgsql/doc/src/sgml/ref/psql-ref.sgml,v 1.161 2006/04/02 20:08:20 neilc Exp $
2+
$PostgreSQL: pgsql/doc/src/sgml/ref/psql-ref.sgml,v 1.162 2006/05/26 19:51:29 tgl Exp $
33
PostgreSQL documentation
44
-->
55

@@ -744,13 +744,16 @@ testdb=&gt;
744744
{ <literal>from</literal> | <literal>to</literal> }
745745
{ <replaceable class="parameter">filename</replaceable> | stdin | stdout | pstdin | pstdout }
746746
[ with ]
747+
[ binary ]
747748
[ oids ]
748749
[ delimiter [ as ] '<replaceable class="parameter">character</replaceable>' ]
749750
[ null [ as ] '<replaceable class="parameter">string</replaceable>' ]
750-
[ csv [ quote [ as ] '<replaceable class="parameter">character</replaceable>' ]
751-
[ escape [ as ] '<replaceable class="parameter">character</replaceable>' ]
752-
[ force quote <replaceable class="parameter">column_list</replaceable> ]
753-
[ force not null <replaceable class="parameter">column_list</replaceable> ] ]</literal>
751+
[ csv
752+
[ header ]
753+
[ quote [ as ] '<replaceable class="parameter">character</replaceable>' ]
754+
[ escape [ as ] '<replaceable class="parameter">character</replaceable>' ]
755+
[ force quote <replaceable class="parameter">column_list</replaceable> ]
756+
[ force not null <replaceable class="parameter">column_list</replaceable> ] ]</literal>
754757
</term>
755758

756759
<listitem>

src/bin/psql/common.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
*
44
* Copyright (c) 2000-2006, PostgreSQL Global Development Group
55
*
6-
* $PostgreSQL: pgsql/src/bin/psql/common.c,v 1.117 2006/05/11 19:15:35 tgl Exp $
6+
* $PostgreSQL: pgsql/src/bin/psql/common.c,v 1.118 2006/05/26 19:51:29 tgl Exp $
77
*/
88
#include "postgres_fe.h"
99
#include "common.h"
@@ -652,7 +652,8 @@ ProcessCopyResult(PGresult *results)
652652
break;
653653

654654
case PGRES_COPY_IN:
655-
success = handleCopyIn(pset.db, pset.cur_cmd_source);
655+
success = handleCopyIn(pset.db, pset.cur_cmd_source,
656+
PQbinaryTuples(results));
656657
break;
657658

658659
default:

src/bin/psql/copy.c

Lines changed: 87 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,11 @@
33
*
44
* Copyright (c) 2000-2006, PostgreSQL Global Development Group
55
*
6-
* $PostgreSQL: pgsql/src/bin/psql/copy.c,v 1.60 2006/03/05 15:58:51 momjian Exp $
6+
* $PostgreSQL: pgsql/src/bin/psql/copy.c,v 1.61 2006/05/26 19:51:29 tgl Exp $
77
*/
88
#include "postgres_fe.h"
99
#include "copy.h"
1010

11-
#include <errno.h>
1211
#include <signal.h>
1312
#include <sys/stat.h>
1413
#ifndef WIN32
@@ -37,11 +36,10 @@
3736
*
3837
* The documented preferred syntax is:
3938
* \copy tablename [(columnlist)] from|to filename
40-
* [ with ] [ oids ] [ delimiter [as] char ] [ null [as] string ]
41-
* (binary is not here yet)
39+
* [ with ] [ binary ] [ oids ] [ delimiter [as] char ] [ null [as] string ]
4240
*
4341
* The pre-7.3 syntax was:
44-
* \copy tablename [(columnlist)] [with oids] from|to filename
42+
* \copy [ binary ] tablename [(columnlist)] [with oids] from|to filename
4543
* [ [using] delimiters char ] [ with null as string ]
4644
*
4745
* The actual accepted syntax is a rather unholy combination of these,
@@ -131,8 +129,6 @@ parse_slash_copy(const char *args)
131129
if (!token)
132130
goto error;
133131

134-
#ifdef NOT_USED
135-
/* this is not implemented yet */
136132
if (pg_strcasecmp(token, "binary") == 0)
137133
{
138134
result->binary = true;
@@ -141,7 +137,6 @@ parse_slash_copy(const char *args)
141137
if (!token)
142138
goto error;
143139
}
144-
#endif
145140

146141
result->table = pg_strdup(token);
147142

@@ -284,9 +279,10 @@ parse_slash_copy(const char *args)
284279

285280
fetch_next = true;
286281

287-
/* someday allow BINARY here */
288282
if (pg_strcasecmp(token, "oids") == 0)
289283
result->oids = true;
284+
else if (pg_strcasecmp(token, "binary") == 0)
285+
result->binary = true;
290286
else if (pg_strcasecmp(token, "csv") == 0)
291287
result->csv_mode = true;
292288
else if (pg_strcasecmp(token, "header") == 0)
@@ -442,6 +438,8 @@ do_copy(const char *args)
442438
initPQExpBuffer(&query);
443439

444440
printfPQExpBuffer(&query, "COPY ");
441+
442+
/* Uses old COPY syntax for backward compatibility 2002-06-19 */
445443
if (options->binary)
446444
appendPQExpBuffer(&query, "BINARY ");
447445

@@ -523,7 +521,8 @@ do_copy(const char *args)
523521
else
524522
{
525523
if (options->file)
526-
copystream = fopen(options->file, "w");
524+
copystream = fopen(options->file,
525+
options->binary ? PG_BINARY_W : "w");
527526
else if (!options->psql_inout)
528527
copystream = pset.queryFout;
529528
else
@@ -558,7 +557,8 @@ do_copy(const char *args)
558557
success = handleCopyOut(pset.db, copystream);
559558
break;
560559
case PGRES_COPY_IN:
561-
success = handleCopyIn(pset.db, copystream);
560+
success = handleCopyIn(pset.db, copystream,
561+
PQbinaryTuples(result));
562562
break;
563563
case PGRES_NONFATAL_ERROR:
564564
case PGRES_FATAL_ERROR:
@@ -622,12 +622,23 @@ handleCopyOut(PGconn *conn, FILE *copystream)
622622

623623
if (buf)
624624
{
625-
fputs(buf, copystream);
625+
if (fwrite(buf, 1, ret, copystream) != ret)
626+
{
627+
if (OK) /* complain only once, keep reading data */
628+
psql_error("could not write COPY data: %s\n",
629+
strerror(errno));
630+
OK = false;
631+
}
626632
PQfreemem(buf);
627633
}
628634
}
629635

630-
fflush(copystream);
636+
if (OK && fflush(copystream))
637+
{
638+
psql_error("could not write COPY data: %s\n",
639+
strerror(errno));
640+
OK = false;
641+
}
631642

632643
if (ret == -2)
633644
{
@@ -657,6 +668,7 @@ handleCopyOut(PGconn *conn, FILE *copystream)
657668
* conn should be a database connection that you just issued COPY FROM on
658669
* and got back a PGRES_COPY_IN result.
659670
* copystream is the file stream to read the data from.
671+
* isbinary can be set from PQbinaryTuples().
660672
*
661673
* result is true if successful, false if not.
662674
*/
@@ -665,13 +677,10 @@ handleCopyOut(PGconn *conn, FILE *copystream)
665677
#define COPYBUFSIZ 8192
666678

667679
bool
668-
handleCopyIn(PGconn *conn, FILE *copystream)
680+
handleCopyIn(PGconn *conn, FILE *copystream, bool isbinary)
669681
{
670682
bool OK = true;
671683
const char *prompt;
672-
bool copydone = false;
673-
bool firstload;
674-
bool linedone;
675684
char buf[COPYBUFSIZ];
676685
PGresult *res;
677686

@@ -686,59 +695,89 @@ handleCopyIn(PGconn *conn, FILE *copystream)
686695
else
687696
prompt = NULL;
688697

689-
while (!copydone)
690-
{ /* for each input line ... */
698+
if (isbinary)
699+
{
700+
int buflen;
701+
702+
/* interactive input probably silly, but give one prompt anyway */
691703
if (prompt)
692704
{
693705
fputs(prompt, stdout);
694706
fflush(stdout);
695707
}
696-
697-
firstload = true;
698-
linedone = false;
699708

700-
while (!linedone)
701-
{ /* for each bufferload in line ... */
702-
int linelen;
703-
704-
if (!fgets(buf, COPYBUFSIZ, copystream))
709+
while ((buflen = fread(buf, 1, COPYBUFSIZ, copystream)) > 0)
710+
{
711+
if (PQputCopyData(conn, buf, buflen) <= 0)
705712
{
706-
if (ferror(copystream))
707-
OK = false;
708-
copydone = true;
713+
OK = false;
709714
break;
710715
}
716+
}
717+
}
718+
else
719+
{
720+
bool copydone = false;
711721

712-
linelen = strlen(buf);
713-
714-
/* current line is done? */
715-
if (linelen > 0 && buf[linelen-1] == '\n')
716-
linedone = true;
722+
while (!copydone)
723+
{ /* for each input line ... */
724+
bool firstload;
725+
bool linedone;
717726

718-
/* check for EOF marker, but not on a partial line */
719-
if (firstload)
727+
if (prompt)
720728
{
721-
if (strcmp(buf, "\\.\n") == 0 ||
722-
strcmp(buf, "\\.\r\n") == 0)
729+
fputs(prompt, stdout);
730+
fflush(stdout);
731+
}
732+
733+
firstload = true;
734+
linedone = false;
735+
736+
while (!linedone)
737+
{ /* for each bufferload in line ... */
738+
int linelen;
739+
740+
if (!fgets(buf, COPYBUFSIZ, copystream))
723741
{
724742
copydone = true;
725743
break;
726744
}
745+
746+
linelen = strlen(buf);
747+
748+
/* current line is done? */
749+
if (linelen > 0 && buf[linelen-1] == '\n')
750+
linedone = true;
751+
752+
/* check for EOF marker, but not on a partial line */
753+
if (firstload)
754+
{
755+
if (strcmp(buf, "\\.\n") == 0 ||
756+
strcmp(buf, "\\.\r\n") == 0)
757+
{
758+
copydone = true;
759+
break;
760+
}
727761

728-
firstload = false;
729-
}
762+
firstload = false;
763+
}
730764

731-
if (PQputCopyData(conn, buf, linelen) <= 0)
732-
{
733-
OK = false;
734-
copydone = true;
735-
break;
765+
if (PQputCopyData(conn, buf, linelen) <= 0)
766+
{
767+
OK = false;
768+
copydone = true;
769+
break;
770+
}
736771
}
737-
}
738772

739-
pset.lineno++;
773+
pset.lineno++;
774+
}
740775
}
741776

777+
/* Check for read error */
778+
if (ferror(copystream))
779+
OK = false;
780+
742781
/* Terminate data transfer */
743782
if (PQputCopyEnd(conn,
744783
OK ? NULL : _("aborted due to read failure")) <= 0)

src/bin/psql/copy.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
*
44
* Copyright (c) 2000-2006, PostgreSQL Global Development Group
55
*
6-
* $PostgreSQL: pgsql/src/bin/psql/copy.h,v 1.18 2006/03/05 15:58:51 momjian Exp $
6+
* $PostgreSQL: pgsql/src/bin/psql/copy.h,v 1.19 2006/05/26 19:51:29 tgl Exp $
77
*/
88
#ifndef COPY_H
99
#define COPY_H
@@ -17,6 +17,6 @@ bool do_copy(const char *args);
1717
/* lower level processors for copy in/out streams */
1818

1919
bool handleCopyOut(PGconn *conn, FILE *copystream);
20-
bool handleCopyIn(PGconn *conn, FILE *copystream);
20+
bool handleCopyIn(PGconn *conn, FILE *copystream, bool isbinary);
2121

2222
#endif

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy