diff --git a/src/bin/pg_dump/pg_backup_db.c b/src/bin/pg_dump/pg_backup_db.c index 531ddb9e56..77c4086d3d 100644 --- a/src/bin/pg_dump/pg_backup_db.c +++ b/src/bin/pg_dump/pg_backup_db.c @@ -5,7 +5,7 @@ * Implements the basic DB functions used by the archiver. * * IDENTIFICATION - * $PostgreSQL: pgsql/src/bin/pg_dump/pg_backup_db.c,v 1.69 2006/02/12 06:11:50 momjian Exp $ + * $PostgreSQL: pgsql/src/bin/pg_dump/pg_backup_db.c,v 1.70 2006/03/03 23:38:29 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -391,22 +391,29 @@ _sendCopyLine(ArchiveHandle *AH, char *qry, char *eos) * enter COPY mode; this allows us to behave reasonably when trying * to continue after an error in a COPY command. */ - if (AH->pgCopyIn && PQputline(AH->connection, AH->pgCopyBuf->data) != 0) - die_horribly(AH, modulename, "error returned by PQputline: %s", + if (AH->pgCopyIn && + PQputCopyData(AH->connection, AH->pgCopyBuf->data, + AH->pgCopyBuf->len) <= 0) + die_horribly(AH, modulename, "error returned by PQputCopyData: %s", PQerrorMessage(AH->connection)); resetPQExpBuffer(AH->pgCopyBuf); - /* - * fprintf(stderr, "Buffer is '%s'\n", AH->pgCopyBuf->data); - */ - - if (isEnd) + if (isEnd && AH->pgCopyIn) { - if (AH->pgCopyIn && PQendcopy(AH->connection) != 0) - die_horribly(AH, modulename, "error returned by PQendcopy: %s", + PGresult *res; + + if (PQputCopyEnd(AH->connection, NULL) <= 0) + die_horribly(AH, modulename, "error returned by PQputCopyEnd: %s", PQerrorMessage(AH->connection)); + /* Check command status and return to normal libpq state */ + res = PQgetResult(AH->connection); + if (PQresultStatus(res) != PGRES_COMMAND_OK) + warn_or_die_horribly(AH, modulename, "COPY failed: %s", + PQerrorMessage(AH->connection)); + PQclear(res); + AH->pgCopyIn = false; } diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index 4eb2cafc19..535104d83e 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -12,7 +12,7 @@ * by PostgreSQL * * IDENTIFICATION - * $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.c,v 1.431 2006/03/02 01:18:25 tgl Exp $ + * $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.c,v 1.432 2006/03/03 23:38:29 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -831,8 +831,6 @@ selectDumpableObject(DumpableObject *dobj) * to be dumped. */ -#define COPYBUFSIZ 8192 - static int dumpTableData_copy(Archive *fout, void *dcontext) { @@ -844,8 +842,7 @@ dumpTableData_copy(Archive *fout, void *dcontext) PQExpBuffer q = createPQExpBuffer(); PGresult *res; int ret; - bool copydone; - char copybuf[COPYBUFSIZ]; + char *copybuf; const char *column_list; if (g_verbose) @@ -886,33 +883,19 @@ dumpTableData_copy(Archive *fout, void *dcontext) } res = PQexec(g_conn, q->data); check_sql_result(res, g_conn, q->data, PGRES_COPY_OUT); + PQclear(res); - copydone = false; - - while (!copydone) + for (;;) { - ret = PQgetline(g_conn, copybuf, COPYBUFSIZ); + ret = PQgetCopyData(g_conn, ©buf, 0); - if (copybuf[0] == '\\' && - copybuf[1] == '.' && - copybuf[2] == '\0') + if (ret < 0) + break; /* done or error */ + + if (copybuf) { - copydone = true; /* don't print this... */ - } - else - { - archputs(copybuf, fout); - switch (ret) - { - case EOF: - copydone = true; - /* FALLTHROUGH */ - case 0: - archputs("\n", fout); - break; - case 1: - break; - } + WriteData(fout, copybuf, ret); + PQfreemem(copybuf); } /* @@ -920,7 +903,7 @@ dumpTableData_copy(Archive *fout, void *dcontext) * * There was considerable discussion in late July, 2000 regarding * slowing down pg_dump when backing up large tables. Users with both - * slow & fast (muti-processor) machines experienced performance + * slow & fast (multi-processor) machines experienced performance * degradation when doing a backup. * * Initial attempts based on sleeping for a number of ms for each ms @@ -957,16 +940,20 @@ dumpTableData_copy(Archive *fout, void *dcontext) } archprintf(fout, "\\.\n\n\n"); - ret = PQendcopy(g_conn); - if (ret != 0) + if (ret == -2) { - write_msg(NULL, "SQL command to dump the contents of table \"%s\" failed: PQendcopy() failed.\n", classname); + /* copy data transfer failed */ + write_msg(NULL, "Dumping the contents of table \"%s\" failed: PQgetCopyData() failed.\n", classname); write_msg(NULL, "Error message from server: %s", PQerrorMessage(g_conn)); write_msg(NULL, "The command was: %s\n", q->data); exit_nicely(); } + /* Check command status and return to normal libpq state */ + res = PQgetResult(g_conn); + check_sql_result(res, g_conn, q->data, PGRES_COMMAND_OK); PQclear(res); + destroyPQExpBuffer(q); return 1; } diff --git a/src/bin/psql/common.c b/src/bin/psql/common.c index 8f6d03561a..7e436d5f39 100644 --- a/src/bin/psql/common.c +++ b/src/bin/psql/common.c @@ -3,7 +3,7 @@ * * Copyright (c) 2000-2005, PostgreSQL Global Development Group * - * $PostgreSQL: pgsql/src/bin/psql/common.c,v 1.112 2006/02/12 03:30:21 tgl Exp $ + * $PostgreSQL: pgsql/src/bin/psql/common.c,v 1.113 2006/03/03 23:38:30 tgl Exp $ */ #include "postgres_fe.h" #include "common.h" @@ -685,7 +685,10 @@ AcceptResult(const PGresult *result, const char *query) break; case PGRES_COPY_OUT: - /* keep cancel connection for copy out state */ + /* + * Keep cancel connection active during copy out state. + * The matching ResetCancelConn() is in handleCopyOut. + */ SetCancelConn(); break; @@ -702,6 +705,7 @@ AcceptResult(const PGresult *result, const char *query) psql_error("%s", error); ReportSyntaxErrorPosition(result, query); + CheckConnection(); } @@ -720,6 +724,9 @@ AcceptResult(const PGresult *result, const char *query) * is true; nothing special is done when start_xact is false. Typically, * start_xact = false is used for SELECTs and explicit BEGIN/COMMIT commands. * + * Caller is responsible for handling the ensuing processing if a COPY + * command is sent. + * * Note: we don't bother to check PQclientEncoding; it is assumed that no * caller uses this path to issue "SET CLIENT_ENCODING". */ diff --git a/src/bin/psql/copy.c b/src/bin/psql/copy.c index bd1763c5ac..6dd1b52185 100644 --- a/src/bin/psql/copy.c +++ b/src/bin/psql/copy.c @@ -3,7 +3,7 @@ * * Copyright (c) 2000-2005, PostgreSQL Global Development Group * - * $PostgreSQL: pgsql/src/bin/psql/copy.c,v 1.58 2005/10/15 02:49:40 momjian Exp $ + * $PostgreSQL: pgsql/src/bin/psql/copy.c,v 1.59 2006/03/03 23:38:30 tgl Exp $ */ #include "postgres_fe.h" #include "copy.h" @@ -568,7 +568,9 @@ do_copy(const char *args) break; default: success = false; - psql_error("\\copy: unexpected response (%d)\n", PQresultStatus(result)); + psql_error("\\copy: unexpected response (%d)\n", + PQresultStatus(result)); + break; } PQclear(result); @@ -586,85 +588,92 @@ do_copy(const char *args) } -#define COPYBUFSIZ 8192 /* size doesn't matter */ - +/* + * Functions for handling COPY IN/OUT data transfer. + * + * If you want to use COPY TO STDOUT/FROM STDIN in your application, + * this is the code to steal ;) + */ /* * handleCopyOut - * receives data as a result of a COPY ... TO stdout command + * receives data as a result of a COPY ... TO STDOUT command * - * If you want to use COPY TO in your application, this is the code to steal :) + * conn should be a database connection that you just issued COPY TO on + * and got back a PGRES_COPY_OUT result. + * copystream is the file stream for the data to go to. * - * conn should be a database connection that you just called COPY TO on - * (and which gave you PGRES_COPY_OUT back); - * copystream is the file stream you want the output to go to + * result is true if successful, false if not. */ bool handleCopyOut(PGconn *conn, FILE *copystream) { - bool copydone = false; /* haven't started yet */ - char copybuf[COPYBUFSIZ]; - int ret; + bool OK = true; + char *buf; + int ret; + PGresult *res; - while (!copydone) + for (;;) { - ret = PQgetline(conn, copybuf, COPYBUFSIZ); + ret = PQgetCopyData(conn, &buf, 0); - if (copybuf[0] == '\\' && - copybuf[1] == '.' && - copybuf[2] == '\0') + if (ret < 0) + break; /* done or error */ + + if (buf) { - copydone = true; /* we're at the end */ - } - else - { - fputs(copybuf, copystream); - switch (ret) - { - case EOF: - copydone = true; - /* FALLTHROUGH */ - case 0: - fputc('\n', copystream); - break; - case 1: - break; - } + fputs(buf, copystream); + PQfreemem(buf); } } + fflush(copystream); - ret = !PQendcopy(conn); + + if (ret == -2) + { + psql_error("COPY data transfer failed: %s", PQerrorMessage(conn)); + OK = false; + } + + /* Check command status and return to normal libpq state */ + res = PQgetResult(conn); + if (PQresultStatus(res) != PGRES_COMMAND_OK) + { + psql_error("%s", PQerrorMessage(conn)); + OK = false; + } + PQclear(res); + + /* Disable cancel connection (see AcceptResult in common.c) */ ResetCancelConn(); - return ret; + + return OK; } - - /* * handleCopyIn - * receives data as a result of a COPY ... FROM stdin command + * sends data to complete a COPY ... FROM STDIN command * - * Again, if you want to use COPY FROM in your application, copy this. + * 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. * - * conn should be a database connection that you just called COPY FROM on - * (and which gave you PGRES_COPY_IN back); - * copystream is the file stream you want the input to come from + * result is true if successful, false if not. */ +/* read chunk size for COPY IN - size is not critical */ +#define COPYBUFSIZ 8192 + bool handleCopyIn(PGconn *conn, FILE *copystream) { + bool OK = true; const char *prompt; bool copydone = false; bool firstload; bool linedone; - bool saw_cr = false; - char copybuf[COPYBUFSIZ]; - char *s; - int bufleft; - int c = 0; - int ret; - unsigned int linecount = 0; + char buf[COPYBUFSIZ]; + PGresult *res; /* Prompt if interactive input */ if (isatty(fileno(copystream))) @@ -684,64 +693,65 @@ handleCopyIn(PGconn *conn, FILE *copystream) fputs(prompt, stdout); fflush(stdout); } + firstload = true; linedone = false; while (!linedone) { /* for each bufferload in line ... */ - /* Fetch string until \n, EOF, or buffer full */ - s = copybuf; - for (bufleft = COPYBUFSIZ - 1; bufleft > 0; bufleft--) + int linelen; + + if (!fgets(buf, COPYBUFSIZ, copystream)) { - c = getc(copystream); - if (c == EOF) - { - linedone = true; - break; - } - *s++ = c; - if (c == '\n') - { - linedone = true; - break; - } - if (c == '\r') - saw_cr = true; - } - *s = '\0'; - /* EOF with empty line-so-far? */ - if (c == EOF && s == copybuf && firstload) - { - /* - * We are guessing a little bit as to the right line-ending - * here... - */ - if (saw_cr) - PQputline(conn, "\\.\r\n"); - else - PQputline(conn, "\\.\n"); + if (ferror(copystream)) + OK = false; copydone = true; - if (pset.cur_cmd_interactive) - puts("\\."); break; } - /* No, so pass the data to the backend */ - PQputline(conn, copybuf); - /* Check for line consisting only of \. */ + + 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(copybuf, "\\.\n") == 0 || - strcmp(copybuf, "\\.\r\n") == 0) + if (strcmp(buf, "\\.\n") == 0 || + strcmp(buf, "\\.\r\n") == 0) { copydone = true; break; } + firstload = false; } + + if (PQputCopyData(conn, buf, linelen) <= 0) + { + OK = false; + copydone = true; + break; + } } - linecount++; + + pset.lineno++; } - ret = !PQendcopy(conn); - pset.lineno += linecount; - return ret; + + /* Terminate data transfer */ + if (PQputCopyEnd(conn, + OK ? NULL : _("aborted due to read failure")) <= 0) + OK = false; + + /* Check command status and return to normal libpq state */ + res = PQgetResult(conn); + if (PQresultStatus(res) != PGRES_COMMAND_OK) + { + psql_error("%s", PQerrorMessage(conn)); + OK = false; + } + PQclear(res); + + return OK; }