aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2006-05-26 19:51:29 +0000
committerTom Lane <tgl@sss.pgh.pa.us>2006-05-26 19:51:29 +0000
commit223ae6957f76f356bda883d427812d9214394f84 (patch)
tree1393bbe0c0ecd9a08698a23f1af06f9024b9ee0c
parentaadd8a23ce9d8cfe310f6e0fb6bf86f0eb0707bc (diff)
downloadpostgresql-223ae6957f76f356bda883d427812d9214394f84.tar.gz
postgresql-223ae6957f76f356bda883d427812d9214394f84.zip
Support binary COPY through psql. Also improve detection of write errors
during COPY OUT. Andreas Pflug, some editorialization by moi.
-rw-r--r--doc/src/sgml/ref/psql-ref.sgml13
-rw-r--r--src/bin/psql/common.c5
-rw-r--r--src/bin/psql/copy.c135
-rw-r--r--src/bin/psql/copy.h4
4 files changed, 100 insertions, 57 deletions
diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml
index 19d88c9843f..f00b4c09f6f 100644
--- a/doc/src/sgml/ref/psql-ref.sgml
+++ b/doc/src/sgml/ref/psql-ref.sgml
@@ -1,5 +1,5 @@
<!--
-$PostgreSQL: pgsql/doc/src/sgml/ref/psql-ref.sgml,v 1.161 2006/04/02 20:08:20 neilc Exp $
+$PostgreSQL: pgsql/doc/src/sgml/ref/psql-ref.sgml,v 1.162 2006/05/26 19:51:29 tgl Exp $
PostgreSQL documentation
-->
@@ -744,13 +744,16 @@ testdb=&gt;
{ <literal>from</literal> | <literal>to</literal> }
{ <replaceable class="parameter">filename</replaceable> | stdin | stdout | pstdin | pstdout }
[ with ]
+ [ binary ]
[ oids ]
[ delimiter [ as ] '<replaceable class="parameter">character</replaceable>' ]
[ null [ as ] '<replaceable class="parameter">string</replaceable>' ]
- [ csv [ quote [ as ] '<replaceable class="parameter">character</replaceable>' ]
- [ escape [ as ] '<replaceable class="parameter">character</replaceable>' ]
- [ force quote <replaceable class="parameter">column_list</replaceable> ]
- [ force not null <replaceable class="parameter">column_list</replaceable> ] ]</literal>
+ [ csv
+ [ header ]
+ [ quote [ as ] '<replaceable class="parameter">character</replaceable>' ]
+ [ escape [ as ] '<replaceable class="parameter">character</replaceable>' ]
+ [ force quote <replaceable class="parameter">column_list</replaceable> ]
+ [ force not null <replaceable class="parameter">column_list</replaceable> ] ]</literal>
</term>
<listitem>
diff --git a/src/bin/psql/common.c b/src/bin/psql/common.c
index ba8e403dd2a..6b7f683b055 100644
--- a/src/bin/psql/common.c
+++ b/src/bin/psql/common.c
@@ -3,7 +3,7 @@
*
* Copyright (c) 2000-2006, PostgreSQL Global Development Group
*
- * $PostgreSQL: pgsql/src/bin/psql/common.c,v 1.117 2006/05/11 19:15:35 tgl Exp $
+ * $PostgreSQL: pgsql/src/bin/psql/common.c,v 1.118 2006/05/26 19:51:29 tgl Exp $
*/
#include "postgres_fe.h"
#include "common.h"
@@ -652,7 +652,8 @@ ProcessCopyResult(PGresult *results)
break;
case PGRES_COPY_IN:
- success = handleCopyIn(pset.db, pset.cur_cmd_source);
+ success = handleCopyIn(pset.db, pset.cur_cmd_source,
+ PQbinaryTuples(results));
break;
default:
diff --git a/src/bin/psql/copy.c b/src/bin/psql/copy.c
index 53162b69cc9..1c3d812abdf 100644
--- a/src/bin/psql/copy.c
+++ b/src/bin/psql/copy.c
@@ -3,12 +3,11 @@
*
* Copyright (c) 2000-2006, PostgreSQL Global Development Group
*
- * $PostgreSQL: pgsql/src/bin/psql/copy.c,v 1.60 2006/03/05 15:58:51 momjian Exp $
+ * $PostgreSQL: pgsql/src/bin/psql/copy.c,v 1.61 2006/05/26 19:51:29 tgl Exp $
*/
#include "postgres_fe.h"
#include "copy.h"
-#include <errno.h>
#include <signal.h>
#include <sys/stat.h>
#ifndef WIN32
@@ -37,11 +36,10 @@
*
* The documented preferred syntax is:
* \copy tablename [(columnlist)] from|to filename
- * [ with ] [ oids ] [ delimiter [as] char ] [ null [as] string ]
- * (binary is not here yet)
+ * [ with ] [ binary ] [ oids ] [ delimiter [as] char ] [ null [as] string ]
*
* The pre-7.3 syntax was:
- * \copy tablename [(columnlist)] [with oids] from|to filename
+ * \copy [ binary ] tablename [(columnlist)] [with oids] from|to filename
* [ [using] delimiters char ] [ with null as string ]
*
* The actual accepted syntax is a rather unholy combination of these,
@@ -131,8 +129,6 @@ parse_slash_copy(const char *args)
if (!token)
goto error;
-#ifdef NOT_USED
- /* this is not implemented yet */
if (pg_strcasecmp(token, "binary") == 0)
{
result->binary = true;
@@ -141,7 +137,6 @@ parse_slash_copy(const char *args)
if (!token)
goto error;
}
-#endif
result->table = pg_strdup(token);
@@ -284,9 +279,10 @@ parse_slash_copy(const char *args)
fetch_next = true;
- /* someday allow BINARY here */
if (pg_strcasecmp(token, "oids") == 0)
result->oids = true;
+ else if (pg_strcasecmp(token, "binary") == 0)
+ result->binary = true;
else if (pg_strcasecmp(token, "csv") == 0)
result->csv_mode = true;
else if (pg_strcasecmp(token, "header") == 0)
@@ -442,6 +438,8 @@ do_copy(const char *args)
initPQExpBuffer(&query);
printfPQExpBuffer(&query, "COPY ");
+
+ /* Uses old COPY syntax for backward compatibility 2002-06-19 */
if (options->binary)
appendPQExpBuffer(&query, "BINARY ");
@@ -523,7 +521,8 @@ do_copy(const char *args)
else
{
if (options->file)
- copystream = fopen(options->file, "w");
+ copystream = fopen(options->file,
+ options->binary ? PG_BINARY_W : "w");
else if (!options->psql_inout)
copystream = pset.queryFout;
else
@@ -558,7 +557,8 @@ do_copy(const char *args)
success = handleCopyOut(pset.db, copystream);
break;
case PGRES_COPY_IN:
- success = handleCopyIn(pset.db, copystream);
+ success = handleCopyIn(pset.db, copystream,
+ PQbinaryTuples(result));
break;
case PGRES_NONFATAL_ERROR:
case PGRES_FATAL_ERROR:
@@ -622,12 +622,23 @@ handleCopyOut(PGconn *conn, FILE *copystream)
if (buf)
{
- fputs(buf, copystream);
+ if (fwrite(buf, 1, ret, copystream) != ret)
+ {
+ if (OK) /* complain only once, keep reading data */
+ psql_error("could not write COPY data: %s\n",
+ strerror(errno));
+ OK = false;
+ }
PQfreemem(buf);
}
}
- fflush(copystream);
+ if (OK && fflush(copystream))
+ {
+ psql_error("could not write COPY data: %s\n",
+ strerror(errno));
+ OK = false;
+ }
if (ret == -2)
{
@@ -657,6 +668,7 @@ handleCopyOut(PGconn *conn, FILE *copystream)
* conn should be a database connection that you just issued COPY FROM on
* and got back a PGRES_COPY_IN result.
* copystream is the file stream to read the data from.
+ * isbinary can be set from PQbinaryTuples().
*
* result is true if successful, false if not.
*/
@@ -665,13 +677,10 @@ handleCopyOut(PGconn *conn, FILE *copystream)
#define COPYBUFSIZ 8192
bool
-handleCopyIn(PGconn *conn, FILE *copystream)
+handleCopyIn(PGconn *conn, FILE *copystream, bool isbinary)
{
bool OK = true;
const char *prompt;
- bool copydone = false;
- bool firstload;
- bool linedone;
char buf[COPYBUFSIZ];
PGresult *res;
@@ -686,59 +695,89 @@ handleCopyIn(PGconn *conn, FILE *copystream)
else
prompt = NULL;
- while (!copydone)
- { /* for each input line ... */
+ if (isbinary)
+ {
+ int buflen;
+
+ /* interactive input probably silly, but give one prompt anyway */
if (prompt)
{
fputs(prompt, stdout);
fflush(stdout);
}
-
- firstload = true;
- linedone = false;
- while (!linedone)
- { /* for each bufferload in line ... */
- int linelen;
-
- if (!fgets(buf, COPYBUFSIZ, copystream))
+ while ((buflen = fread(buf, 1, COPYBUFSIZ, copystream)) > 0)
+ {
+ if (PQputCopyData(conn, buf, buflen) <= 0)
{
- if (ferror(copystream))
- OK = false;
- copydone = true;
+ OK = false;
break;
}
+ }
+ }
+ else
+ {
+ bool copydone = false;
- linelen = strlen(buf);
-
- /* current line is done? */
- if (linelen > 0 && buf[linelen-1] == '\n')
- linedone = true;
+ while (!copydone)
+ { /* for each input line ... */
+ bool firstload;
+ bool linedone;
- /* check for EOF marker, but not on a partial line */
- if (firstload)
+ if (prompt)
{
- if (strcmp(buf, "\\.\n") == 0 ||
- strcmp(buf, "\\.\r\n") == 0)
+ fputs(prompt, stdout);
+ fflush(stdout);
+ }
+
+ firstload = true;
+ linedone = false;
+
+ while (!linedone)
+ { /* for each bufferload in line ... */
+ int linelen;
+
+ if (!fgets(buf, COPYBUFSIZ, copystream))
{
copydone = true;
break;
}
+
+ linelen = strlen(buf);
+
+ /* current line is done? */
+ if (linelen > 0 && buf[linelen-1] == '\n')
+ linedone = true;
+
+ /* check for EOF marker, but not on a partial line */
+ if (firstload)
+ {
+ if (strcmp(buf, "\\.\n") == 0 ||
+ strcmp(buf, "\\.\r\n") == 0)
+ {
+ copydone = true;
+ break;
+ }
- firstload = false;
- }
+ firstload = false;
+ }
- if (PQputCopyData(conn, buf, linelen) <= 0)
- {
- OK = false;
- copydone = true;
- break;
+ if (PQputCopyData(conn, buf, linelen) <= 0)
+ {
+ OK = false;
+ copydone = true;
+ break;
+ }
}
- }
- pset.lineno++;
+ pset.lineno++;
+ }
}
+ /* Check for read error */
+ if (ferror(copystream))
+ OK = false;
+
/* Terminate data transfer */
if (PQputCopyEnd(conn,
OK ? NULL : _("aborted due to read failure")) <= 0)
diff --git a/src/bin/psql/copy.h b/src/bin/psql/copy.h
index 689cd8f49c3..7e0c5e0d838 100644
--- a/src/bin/psql/copy.h
+++ b/src/bin/psql/copy.h
@@ -3,7 +3,7 @@
*
* Copyright (c) 2000-2006, PostgreSQL Global Development Group
*
- * $PostgreSQL: pgsql/src/bin/psql/copy.h,v 1.18 2006/03/05 15:58:51 momjian Exp $
+ * $PostgreSQL: pgsql/src/bin/psql/copy.h,v 1.19 2006/05/26 19:51:29 tgl Exp $
*/
#ifndef COPY_H
#define COPY_H
@@ -17,6 +17,6 @@ bool do_copy(const char *args);
/* lower level processors for copy in/out streams */
bool handleCopyOut(PGconn *conn, FILE *copystream);
-bool handleCopyIn(PGconn *conn, FILE *copystream);
+bool handleCopyIn(PGconn *conn, FILE *copystream, bool isbinary);
#endif