1
0
mirror of https://github.com/postgres/postgres.git synced 2025-06-13 07:41:39 +03:00

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

during COPY OUT.  Andreas Pflug, some editorialization by moi.
This commit is contained in:
Tom Lane
2006-05-26 19:51:29 +00:00
parent aadd8a23ce
commit 223ae6957f
4 changed files with 100 additions and 57 deletions

View File

@ -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 PostgreSQL documentation
--> -->
@ -744,13 +744,16 @@ 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
[ escape [ as ] '<replaceable class="parameter">character</replaceable>' ] [ header ]
[ force quote <replaceable class="parameter">column_list</replaceable> ] [ quote [ as ] '<replaceable class="parameter">character</replaceable>' ]
[ force not null <replaceable class="parameter">column_list</replaceable> ] ]</literal> [ 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> </term>
<listitem> <listitem>

View File

@ -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)
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:

View File

@ -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 @@
* *
* 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)
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)
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)
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)
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)
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)
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)
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)
* 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)
#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,59 +695,89 @@ handleCopyIn(PGconn *conn, FILE *copystream)
else else
prompt = NULL; prompt = NULL;
while (!copydone) if (isbinary)
{ /* for each input line ... */ {
int buflen;
/* interactive input probably silly, but give one prompt anyway */
if (prompt) if (prompt)
{ {
fputs(prompt, stdout); fputs(prompt, stdout);
fflush(stdout); fflush(stdout);
} }
firstload = true;
linedone = false;
while (!linedone) while ((buflen = fread(buf, 1, COPYBUFSIZ, copystream)) > 0)
{ /* for each bufferload in line ... */ {
int linelen; if (PQputCopyData(conn, buf, buflen) <= 0)
if (!fgets(buf, COPYBUFSIZ, copystream))
{ {
if (ferror(copystream)) OK = false;
OK = false;
copydone = true;
break; break;
} }
}
}
else
{
bool copydone = false;
linelen = strlen(buf); while (!copydone)
{ /* for each input line ... */
bool firstload;
bool linedone;
/* current line is done? */ if (prompt)
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 || fputs(prompt, stdout);
strcmp(buf, "\\.\r\n") == 0) fflush(stdout);
}
firstload = true;
linedone = false;
while (!linedone)
{ /* for each bufferload in line ... */
int linelen;
if (!fgets(buf, COPYBUFSIZ, copystream))
{ {
copydone = true; copydone = true;
break; 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) if (PQputCopyData(conn, buf, linelen) <= 0)
{ {
OK = false; OK = false;
copydone = true; copydone = true;
break; break;
}
} }
}
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,
OK ? NULL : _("aborted due to read failure")) <= 0) OK ? NULL : _("aborted due to read failure")) <= 0)

View File

@ -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);
/* 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