Commit 223ae695 authored by Tom Lane's avatar Tom Lane

Support binary COPY through psql. Also improve detection of write errors

during COPY OUT.  Andreas Pflug, some editorialization by moi.
parent aadd8a23
<!-- <!--
$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 PostgreSQL documentation
--> -->
...@@ -744,10 +744,13 @@ testdb=&gt; ...@@ -744,10 +744,13 @@ testdb=&gt;
{ <literal>from</literal> | <literal>to</literal> } { <literal>from</literal> | <literal>to</literal> }
{ <replaceable class="parameter">filename</replaceable> | stdin | stdout | pstdin | pstdout } { <replaceable class="parameter">filename</replaceable> | stdin | stdout | pstdin | pstdout }
[ with ] [ with ]
[ binary ]
[ oids ] [ oids ]
[ delimiter [ as ] '<replaceable class="parameter">character</replaceable>' ] [ delimiter [ as ] '<replaceable class="parameter">character</replaceable>' ]
[ null [ as ] '<replaceable class="parameter">string</replaceable>' ] [ null [ as ] '<replaceable class="parameter">string</replaceable>' ]
[ csv [ quote [ as ] '<replaceable class="parameter">character</replaceable>' ] [ csv
[ header ]
[ quote [ as ] '<replaceable class="parameter">character</replaceable>' ]
[ escape [ as ] '<replaceable class="parameter">character</replaceable>' ] [ escape [ as ] '<replaceable class="parameter">character</replaceable>' ]
[ force quote <replaceable class="parameter">column_list</replaceable> ] [ force quote <replaceable class="parameter">column_list</replaceable> ]
[ force not null <replaceable class="parameter">column_list</replaceable> ] ]</literal> [ force not null <replaceable class="parameter">column_list</replaceable> ] ]</literal>
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
* *
* Copyright (c) 2000-2006, PostgreSQL Global Development Group * 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 "postgres_fe.h"
#include "common.h" #include "common.h"
...@@ -652,7 +652,8 @@ ProcessCopyResult(PGresult *results) ...@@ -652,7 +652,8 @@ ProcessCopyResult(PGresult *results)
break; break;
case PGRES_COPY_IN: case PGRES_COPY_IN:
success = handleCopyIn(pset.db, pset.cur_cmd_source); success = handleCopyIn(pset.db, pset.cur_cmd_source,
PQbinaryTuples(results));
break; break;
default: default:
......
...@@ -3,12 +3,11 @@ ...@@ -3,12 +3,11 @@
* *
* Copyright (c) 2000-2006, PostgreSQL Global Development Group * 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 "postgres_fe.h"
#include "copy.h" #include "copy.h"
#include <errno.h>
#include <signal.h> #include <signal.h>
#include <sys/stat.h> #include <sys/stat.h>
#ifndef WIN32 #ifndef WIN32
...@@ -37,11 +36,10 @@ ...@@ -37,11 +36,10 @@
* *
* The documented preferred syntax is: * The documented preferred syntax is:
* \copy tablename [(columnlist)] from|to filename * \copy tablename [(columnlist)] from|to filename
* [ with ] [ oids ] [ delimiter [as] char ] [ null [as] string ] * [ with ] [ binary ] [ oids ] [ delimiter [as] char ] [ null [as] string ]
* (binary is not here yet)
* *
* The pre-7.3 syntax was: * 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 ] * [ [using] delimiters char ] [ with null as string ]
* *
* The actual accepted syntax is a rather unholy combination of these, * The actual accepted syntax is a rather unholy combination of these,
...@@ -131,8 +129,6 @@ parse_slash_copy(const char *args) ...@@ -131,8 +129,6 @@ parse_slash_copy(const char *args)
if (!token) if (!token)
goto error; goto error;
#ifdef NOT_USED
/* this is not implemented yet */
if (pg_strcasecmp(token, "binary") == 0) if (pg_strcasecmp(token, "binary") == 0)
{ {
result->binary = true; result->binary = true;
...@@ -141,7 +137,6 @@ parse_slash_copy(const char *args) ...@@ -141,7 +137,6 @@ parse_slash_copy(const char *args)
if (!token) if (!token)
goto error; goto error;
} }
#endif
result->table = pg_strdup(token); result->table = pg_strdup(token);
...@@ -284,9 +279,10 @@ parse_slash_copy(const char *args) ...@@ -284,9 +279,10 @@ parse_slash_copy(const char *args)
fetch_next = true; fetch_next = true;
/* someday allow BINARY here */
if (pg_strcasecmp(token, "oids") == 0) if (pg_strcasecmp(token, "oids") == 0)
result->oids = true; result->oids = true;
else if (pg_strcasecmp(token, "binary") == 0)
result->binary = true;
else if (pg_strcasecmp(token, "csv") == 0) else if (pg_strcasecmp(token, "csv") == 0)
result->csv_mode = true; result->csv_mode = true;
else if (pg_strcasecmp(token, "header") == 0) else if (pg_strcasecmp(token, "header") == 0)
...@@ -442,6 +438,8 @@ do_copy(const char *args) ...@@ -442,6 +438,8 @@ do_copy(const char *args)
initPQExpBuffer(&query); initPQExpBuffer(&query);
printfPQExpBuffer(&query, "COPY "); printfPQExpBuffer(&query, "COPY ");
/* Uses old COPY syntax for backward compatibility 2002-06-19 */
if (options->binary) if (options->binary)
appendPQExpBuffer(&query, "BINARY "); appendPQExpBuffer(&query, "BINARY ");
...@@ -523,7 +521,8 @@ do_copy(const char *args) ...@@ -523,7 +521,8 @@ do_copy(const char *args)
else else
{ {
if (options->file) if (options->file)
copystream = fopen(options->file, "w"); copystream = fopen(options->file,
options->binary ? PG_BINARY_W : "w");
else if (!options->psql_inout) else if (!options->psql_inout)
copystream = pset.queryFout; copystream = pset.queryFout;
else else
...@@ -558,7 +557,8 @@ do_copy(const char *args) ...@@ -558,7 +557,8 @@ do_copy(const char *args)
success = handleCopyOut(pset.db, copystream); success = handleCopyOut(pset.db, copystream);
break; break;
case PGRES_COPY_IN: case PGRES_COPY_IN:
success = handleCopyIn(pset.db, copystream); success = handleCopyIn(pset.db, copystream,
PQbinaryTuples(result));
break; break;
case PGRES_NONFATAL_ERROR: case PGRES_NONFATAL_ERROR:
case PGRES_FATAL_ERROR: case PGRES_FATAL_ERROR:
...@@ -622,12 +622,23 @@ handleCopyOut(PGconn *conn, FILE *copystream) ...@@ -622,12 +622,23 @@ handleCopyOut(PGconn *conn, FILE *copystream)
if (buf) 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); PQfreemem(buf);
} }
} }
fflush(copystream); if (OK && fflush(copystream))
{
psql_error("could not write COPY data: %s\n",
strerror(errno));
OK = false;
}
if (ret == -2) if (ret == -2)
{ {
...@@ -657,6 +668,7 @@ handleCopyOut(PGconn *conn, FILE *copystream) ...@@ -657,6 +668,7 @@ handleCopyOut(PGconn *conn, FILE *copystream)
* conn should be a database connection that you just issued COPY FROM on * conn should be a database connection that you just issued COPY FROM on
* and got back a PGRES_COPY_IN result. * and got back a PGRES_COPY_IN result.
* copystream is the file stream to read the data from. * copystream is the file stream to read the data from.
* isbinary can be set from PQbinaryTuples().
* *
* result is true if successful, false if not. * result is true if successful, false if not.
*/ */
...@@ -665,13 +677,10 @@ handleCopyOut(PGconn *conn, FILE *copystream) ...@@ -665,13 +677,10 @@ handleCopyOut(PGconn *conn, FILE *copystream)
#define COPYBUFSIZ 8192 #define COPYBUFSIZ 8192
bool bool
handleCopyIn(PGconn *conn, FILE *copystream) handleCopyIn(PGconn *conn, FILE *copystream, bool isbinary)
{ {
bool OK = true; bool OK = true;
const char *prompt; const char *prompt;
bool copydone = false;
bool firstload;
bool linedone;
char buf[COPYBUFSIZ]; char buf[COPYBUFSIZ];
PGresult *res; PGresult *res;
...@@ -686,8 +695,35 @@ handleCopyIn(PGconn *conn, FILE *copystream) ...@@ -686,8 +695,35 @@ handleCopyIn(PGconn *conn, FILE *copystream)
else else
prompt = NULL; prompt = NULL;
if (isbinary)
{
int buflen;
/* interactive input probably silly, but give one prompt anyway */
if (prompt)
{
fputs(prompt, stdout);
fflush(stdout);
}
while ((buflen = fread(buf, 1, COPYBUFSIZ, copystream)) > 0)
{
if (PQputCopyData(conn, buf, buflen) <= 0)
{
OK = false;
break;
}
}
}
else
{
bool copydone = false;
while (!copydone) while (!copydone)
{ /* for each input line ... */ { /* for each input line ... */
bool firstload;
bool linedone;
if (prompt) if (prompt)
{ {
fputs(prompt, stdout); fputs(prompt, stdout);
...@@ -703,8 +739,6 @@ handleCopyIn(PGconn *conn, FILE *copystream) ...@@ -703,8 +739,6 @@ handleCopyIn(PGconn *conn, FILE *copystream)
if (!fgets(buf, COPYBUFSIZ, copystream)) if (!fgets(buf, COPYBUFSIZ, copystream))
{ {
if (ferror(copystream))
OK = false;
copydone = true; copydone = true;
break; break;
} }
...@@ -738,6 +772,11 @@ handleCopyIn(PGconn *conn, FILE *copystream) ...@@ -738,6 +772,11 @@ handleCopyIn(PGconn *conn, FILE *copystream)
pset.lineno++; pset.lineno++;
} }
}
/* Check for read error */
if (ferror(copystream))
OK = false;
/* Terminate data transfer */ /* Terminate data transfer */
if (PQputCopyEnd(conn, if (PQputCopyEnd(conn,
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
* *
* Copyright (c) 2000-2006, PostgreSQL Global Development Group * 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 #ifndef COPY_H
#define COPY_H #define COPY_H
...@@ -17,6 +17,6 @@ bool do_copy(const char *args); ...@@ -17,6 +17,6 @@ bool do_copy(const char *args);
/* lower level processors for copy in/out streams */ /* lower level processors for copy in/out streams */
bool handleCopyOut(PGconn *conn, FILE *copystream); bool handleCopyOut(PGconn *conn, FILE *copystream);
bool handleCopyIn(PGconn *conn, FILE *copystream); bool handleCopyIn(PGconn *conn, FILE *copystream, bool isbinary);
#endif #endif
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment