OSDN Git Service

8.4 pgindent run, with new combined Linux/FreeBSD/MinGW typedef list
[pg-rex/syncrep.git] / src / bin / pg_dump / pg_backup_db.c
index fd0c8bd..836b02c 100644 (file)
  *     Implements the basic DB functions used by the archiver.
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/bin/pg_dump/pg_backup_db.c,v 1.33 2002/05/29 01:38:56 tgl Exp $
+ *       $PostgreSQL: pgsql/src/bin/pg_dump/pg_backup_db.c,v 1.84 2009/06/11 14:49:07 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
 
-#include "pg_backup.h"
-#include "pg_backup_archiver.h"
 #include "pg_backup_db.h"
+#include "dumputils.h"
+
+#include <unistd.h>
 
-#include <unistd.h>                            /* for getopt() */
 #include <ctype.h>
 
 #ifdef HAVE_TERMIOS_H
 #include <termios.h>
 #endif
 
-#include "libpq-fe.h"
-#include "libpq/libpq-fs.h"
-#ifndef HAVE_STRDUP
-#include "strdup.h"
-#endif
 
 static const char *modulename = gettext_noop("archiver (db)");
 
-static void _check_database_version(ArchiveHandle *AH, bool ignoreVersion);
+static void _check_database_version(ArchiveHandle *AH);
 static PGconn *_connectDB(ArchiveHandle *AH, const char *newdbname, const char *newUser);
-static int     _executeSqlCommand(ArchiveHandle *AH, PGconn *conn, PQExpBuffer qry, char *desc);
 static void notice_processor(void *arg, const char *message);
 static char *_sendSQLLine(ArchiveHandle *AH, char *qry, char *eos);
 static char *_sendCopyLine(ArchiveHandle *AH, char *qry, char *eos);
 
+static bool _isIdentChar(unsigned char c);
+static bool _isDQChar(unsigned char c, bool atStart);
 
-/*
- * simple_prompt  --- borrowed from psql
- *
- * Generalized function especially intended for reading in usernames and
- * password interactively. Reads from /dev/tty or stdin/stderr.
- *
- * prompt:             The prompt to print
- * maxlen:             How many characters to accept
- * echo:               Set to false if you want to hide what is entered (for passwords)
- *
- * Returns a malloc()'ed string with the input (w/o trailing newline).
- */
-static bool prompt_state = false;
-
-char *
-simple_prompt(const char *prompt, int maxlen, bool echo)
-{
-       int                     length;
-       char       *destination;
-       FILE       *termin,
-                          *termout;
-
-#ifdef HAVE_TERMIOS_H
-       struct termios t_orig,
-                               t;
-#endif
-
-       destination = (char *) malloc(maxlen + 2);
-       if (!destination)
-               return NULL;
-
-       prompt_state = true;            /* disable SIGINT */
-
-       /*
-        * Do not try to collapse these into one "w+" mode file. Doesn't work
-        * on some platforms (eg, HPUX 10.20).
-        */
-       termin = fopen("/dev/tty", "r");
-       termout = fopen("/dev/tty", "w");
-       if (!termin || !termout)
-       {
-               if (termin)
-                       fclose(termin);
-               if (termout)
-                       fclose(termout);
-               termin = stdin;
-               termout = stderr;
-       }
-
-#ifdef HAVE_TERMIOS_H
-       if (!echo)
-       {
-               tcgetattr(fileno(termin), &t);
-               t_orig = t;
-               t.c_lflag &= ~ECHO;
-               tcsetattr(fileno(termin), TCSAFLUSH, &t);
-       }
-#endif
-
-       if (prompt)
-       {
-               fputs(gettext(prompt), termout);
-               fflush(termout);
-       }
-
-       if (fgets(destination, maxlen, termin) == NULL)
-               destination[0] = '\0';
-
-       length = strlen(destination);
-       if (length > 0 && destination[length - 1] != '\n')
-       {
-               /* eat rest of the line */
-               char            buf[128];
-               int                     buflen;
-
-               do
-               {
-                       if (fgets(buf, sizeof(buf), termin) == NULL)
-                               break;
-                       buflen = strlen(buf);
-               } while (buflen > 0 && buf[buflen - 1] != '\n');
-       }
-
-       if (length > 0 && destination[length - 1] == '\n')
-               /* remove trailing newline */
-               destination[length - 1] = '\0';
-
-#ifdef HAVE_TERMIOS_H
-       if (!echo)
-       {
-               tcsetattr(fileno(termin), TCSAFLUSH, &t_orig);
-               fputs("\n", termout);
-               fflush(termout);
-       }
-#endif
-
-       if (termin != stdin)
-       {
-               fclose(termin);
-               fclose(termout);
-       }
-
-       prompt_state = false;           /* SIGINT okay again */
-
-       return destination;
-}
-
+#define DB_MAX_ERR_STMT 128
 
 static int
 _parse_version(ArchiveHandle *AH, const char *versionString)
 {
-       int                     cnt;
-       int                     vmaj,
-                               vmin,
-                               vrev;
-
-       cnt = sscanf(versionString, "%d.%d.%d", &vmaj, &vmin, &vrev);
-
-       if (cnt < 2)
-               die_horribly(AH, modulename, "unable to parse version string \"%s\"\n", versionString);
+       int                     v;
 
-       if (cnt == 2)
-               vrev = 0;
+       v = parse_version(versionString);
+       if (v < 0)
+               die_horribly(AH, modulename, "could not parse version string \"%s\"\n", versionString);
 
-       return (100 * vmaj + vmin) * 100 + vrev;
+       return v;
 }
 
 static void
-_check_database_version(ArchiveHandle *AH, bool ignoreVersion)
+_check_database_version(ArchiveHandle *AH)
 {
-       PGresult   *res;
        int                     myversion;
        const char *remoteversion_str;
        int                     remoteversion;
-       PGconn     *conn = AH->connection;
 
        myversion = _parse_version(AH, PG_VERSION);
 
-       res = PQexec(conn, "SELECT version();");
-       if (!res ||
-               PQresultStatus(res) != PGRES_TUPLES_OK ||
-               PQntuples(res) != 1)
-
-               die_horribly(AH, modulename, "could not get version from server: %s", PQerrorMessage(conn));
+       remoteversion_str = PQparameterStatus(AH->connection, "server_version");
+       if (!remoteversion_str)
+               die_horribly(AH, modulename, "could not get server_version from libpq\n");
 
-       remoteversion_str = PQgetvalue(res, 0, 0);
-       remoteversion = _parse_version(AH, remoteversion_str + 11);
-
-       PQclear(res);
+       remoteversion = _parse_version(AH, remoteversion_str);
 
+       AH->public.remoteVersionStr = strdup(remoteversion_str);
        AH->public.remoteVersion = remoteversion;
 
        if (myversion != remoteversion
-               && (remoteversion < AH->public.minRemoteVersion || remoteversion > AH->public.maxRemoteVersion))
+               && (remoteversion < AH->public.minRemoteVersion ||
+                       remoteversion > AH->public.maxRemoteVersion))
        {
                write_msg(NULL, "server version: %s; %s version: %s\n",
                                  remoteversion_str, progname, PG_VERSION);
-               if (ignoreVersion)
-                       write_msg(NULL, "proceeding despite version mismatch\n");
-               else
-                       die_horribly(AH, NULL, "aborting because of version mismatch  (Use the -i option to proceed anyway.)\n");
+               die_horribly(AH, NULL, "aborting because of server version mismatch\n");
        }
 }
 
@@ -205,8 +79,8 @@ _check_database_version(ArchiveHandle *AH, bool ignoreVersion)
  * Reconnect to the server.  If dbname is not NULL, use that database,
  * else the one associated with the archive handle.  If username is
  * not NULL, use that user name, else the one from the handle. If
- * both the database and the user and match the existing connection
- * already, nothing will be done.
+ * both the database and the user match the existing connection already,
+ * nothing will be done.
  *
  * Returns 1 in any case.
  */
@@ -228,8 +102,8 @@ ReconnectToServer(ArchiveHandle *AH, const char *dbname, const char *username)
                newusername = username;
 
        /* Let's see if the request is already satisfied */
-       if (strcmp(newusername, PQuser(AH->connection)) == 0
-               && strcmp(newdbname, PQdb(AH->connection)) == 0)
+       if (strcmp(newdbname, PQdb(AH->connection)) == 0 &&
+               strcmp(newusername, PQuser(AH->connection)) == 0)
                return 1;
 
        newConn = _connectDB(AH, newdbname, newusername);
@@ -237,45 +111,41 @@ ReconnectToServer(ArchiveHandle *AH, const char *dbname, const char *username)
        PQfinish(AH->connection);
        AH->connection = newConn;
 
-       free(AH->username);
-       AH->username = strdup(newusername);
-       /* XXX Why don't we update AH->dbname? */
-
-       /* don't assume we still know the output schema */
-       if (AH->currSchema)
-               free(AH->currSchema);
-       AH->currSchema = strdup("");
-
        return 1;
 }
 
 /*
  * Connect to the db again.
+ *
+ * Note: it's not really all that sensible to use a single-entry password
+ * cache if the username keeps changing.  In current usage, however, the
+ * username never does change, so one savedPassword is sufficient.     We do
+ * update the cache on the off chance that the password has changed since the
+ * start of the run.
  */
 static PGconn *
 _connectDB(ArchiveHandle *AH, const char *reqdb, const char *requser)
 {
-       int                     need_pass;
        PGconn     *newConn;
-       char       *password = NULL;
-       int                     badPwd = 0;
-       int                     noPwd = 0;
-       char       *newdb;
-       char       *newuser;
+       const char *newdb;
+       const char *newuser;
+       char       *password = AH->savedPassword;
+       bool            new_pass;
 
        if (!reqdb)
                newdb = PQdb(AH->connection);
        else
-               newdb = (char *) reqdb;
+               newdb = reqdb;
 
-       if (!requser || (strlen(requser) == 0))
+       if (!requser || strlen(requser) == 0)
                newuser = PQuser(AH->connection);
        else
-               newuser = (char *) requser;
+               newuser = requser;
 
-       ahlog(AH, 1, "connecting to database %s as user %s\n", newdb, newuser);
+       ahlog(AH, 1, "connecting to database \"%s\" as user \"%s\"\n",
+                 newdb, newuser);
 
-       if (AH->requirePassword)
+       if (AH->promptPassword == TRI_YES && password == NULL)
        {
                password = simple_prompt("Password: ", 100, false);
                if (password == NULL)
@@ -284,7 +154,7 @@ _connectDB(ArchiveHandle *AH, const char *reqdb, const char *requser)
 
        do
        {
-               need_pass = false;
+               new_pass = false;
                newConn = PQsetdbLogin(PQhost(AH->connection), PQport(AH->connection),
                                                           NULL, NULL, newdb,
                                                           newuser, password);
@@ -293,34 +163,35 @@ _connectDB(ArchiveHandle *AH, const char *reqdb, const char *requser)
 
                if (PQstatus(newConn) == CONNECTION_BAD)
                {
-                       noPwd = (strcmp(PQerrorMessage(newConn),
-                                                       "fe_sendauth: no password supplied\n") == 0);
-                       badPwd = (strncmp(PQerrorMessage(newConn),
-                                       "Password authentication failed for user", 39) == 0);
+                       if (!PQconnectionNeedsPassword(newConn))
+                               die_horribly(AH, modulename, "could not reconnect to database: %s",
+                                                        PQerrorMessage(newConn));
+                       PQfinish(newConn);
 
-                       if (noPwd || badPwd)
-                       {
+                       if (password)
+                               fprintf(stderr, "Password incorrect\n");
 
-                               if (badPwd)
-                                       fprintf(stderr, "Password incorrect\n");
+                       fprintf(stderr, "Connecting to %s as %s\n",
+                                       newdb, newuser);
 
-                               fprintf(stderr, "Connecting to %s as %s\n",
-                                               PQdb(AH->connection), newuser);
+                       if (password)
+                               free(password);
 
-                               need_pass = true;
-                               if (password)
-                                       free(password);
+                       if (AH->promptPassword != TRI_NO)
                                password = simple_prompt("Password: ", 100, false);
-                       }
                        else
-                               die_horribly(AH, modulename, "could not reconnect to database: %s",
-                                                        PQerrorMessage(newConn));
+                               die_horribly(AH, modulename, "connection needs password\n");
+
+                       if (password == NULL)
+                               die_horribly(AH, modulename, "out of memory\n");
+                       new_pass = true;
                }
+       } while (new_pass);
 
-       } while (need_pass);
+       AH->savedPassword = password;
 
-       if (password)
-               free(password);
+       /* check for version mismatch */
+       _check_database_version(AH);
 
        PQsetNoticeProcessor(newConn, notice_processor, NULL);
 
@@ -332,6 +203,10 @@ _connectDB(ArchiveHandle *AH, const char *reqdb, const char *requser)
  * Make a database connection with the given parameters.  The
  * connection handle is returned, the parameters are stored in AHX.
  * An interactive password prompt is automatically issued if required.
+ *
+ * Note: it's not really all that sensible to use a single-entry password
+ * cache if the username keeps changing.  In current usage, however, the
+ * username never does change, so one savedPassword is sufficient.
  */
 PGconn *
 ConnectDatabase(Archive *AHX,
@@ -339,81 +214,58 @@ ConnectDatabase(Archive *AHX,
                                const char *pghost,
                                const char *pgport,
                                const char *username,
-                               const int reqPwd,
-                               const int ignoreVersion)
+                               enum trivalue prompt_password)
 {
        ArchiveHandle *AH = (ArchiveHandle *) AHX;
-       char       *password = NULL;
-       bool            need_pass = false;
+       char       *password = AH->savedPassword;
+       bool            new_pass;
 
        if (AH->connection)
                die_horribly(AH, modulename, "already connected to a database\n");
 
-       if (!dbname && !(dbname = getenv("PGDATABASE")))
-               die_horribly(AH, modulename, "no database name specified\n");
-
-       AH->dbname = strdup(dbname);
-
-       if (pghost != NULL)
-               AH->pghost = strdup(pghost);
-       else
-               AH->pghost = NULL;
-
-       if (pgport != NULL)
-               AH->pgport = strdup(pgport);
-       else
-               AH->pgport = NULL;
-
-       if (username != NULL)
-               AH->username = strdup(username);
-       else
-               AH->username = NULL;
-
-       if (reqPwd)
+       if (prompt_password == TRI_YES && password == NULL)
        {
                password = simple_prompt("Password: ", 100, false);
                if (password == NULL)
                        die_horribly(AH, modulename, "out of memory\n");
-               AH->requirePassword = true;
        }
-       else
-               AH->requirePassword = false;
+       AH->promptPassword = prompt_password;
 
        /*
-        * Start the connection.  Loop until we have a password if requested
-        * by backend.
+        * Start the connection.  Loop until we have a password if requested by
+        * backend.
         */
        do
        {
-               need_pass = false;
-               AH->connection = PQsetdbLogin(AH->pghost, AH->pgport, NULL, NULL,
-                                                                         AH->dbname, AH->username, password);
+               new_pass = false;
+               AH->connection = PQsetdbLogin(pghost, pgport, NULL, NULL,
+                                                                         dbname, username, password);
 
                if (!AH->connection)
                        die_horribly(AH, modulename, "failed to connect to database\n");
 
                if (PQstatus(AH->connection) == CONNECTION_BAD &&
-                       strcmp(PQerrorMessage(AH->connection), "fe_sendauth: no password supplied\n") == 0 &&
-                       !feof(stdin))
+                       PQconnectionNeedsPassword(AH->connection) &&
+                       password == NULL &&
+                       prompt_password != TRI_NO)
                {
                        PQfinish(AH->connection);
-                       need_pass = true;
-                       free(password);
-                       password = NULL;
                        password = simple_prompt("Password: ", 100, false);
+                       if (password == NULL)
+                               die_horribly(AH, modulename, "out of memory\n");
+                       new_pass = true;
                }
-       } while (need_pass);
+       } while (new_pass);
 
-       if (password)
-               free(password);
+       AH->savedPassword = password;
 
        /* check to see that the backend connection was successfully made */
        if (PQstatus(AH->connection) == CONNECTION_BAD)
                die_horribly(AH, modulename, "connection to database \"%s\" failed: %s",
-                                        AH->dbname, PQerrorMessage(AH->connection));
+                                        PQdb(AH->connection), PQerrorMessage(AH->connection));
 
        /* check for version mismatch */
-       _check_database_version(AH, ignoreVersion);
+       _check_database_version(AH);
 
        PQsetNoticeProcessor(AH->connection, notice_processor, NULL);
 
@@ -430,47 +282,44 @@ notice_processor(void *arg, const char *message)
 
 /* Public interface */
 /* Convenience function to send a query. Monitors result to handle COPY statements */
-int
-ExecuteSqlCommand(ArchiveHandle *AH, PQExpBuffer qry, char *desc, bool use_blob)
-{
-       if (use_blob)
-               return _executeSqlCommand(AH, AH->blobConnection, qry, desc);
-       else
-               return _executeSqlCommand(AH, AH->connection, qry, desc);
-}
-
-/*
- * Handle command execution. This is used to execute a command on more than one connection,
- * but the 'pgCopyIn' setting assumes the COPY commands are ONLY executed on the primary
- * setting...an error will be raised otherwise.
- */
-static int
-_executeSqlCommand(ArchiveHandle *AH, PGconn *conn, PQExpBuffer qry, char *desc)
+static void
+ExecuteSqlCommand(ArchiveHandle *AH, const char *qry, const char *desc)
 {
+       PGconn     *conn = AH->connection;
        PGresult   *res;
+       char            errStmt[DB_MAX_ERR_STMT];
 
-       /* fprintf(stderr, "Executing: '%s'\n\n", qry->data); */
-       res = PQexec(conn, qry->data);
-       if (!res)
-               die_horribly(AH, modulename, "%s: no result from server\n", desc);
+#ifdef NOT_USED
+       fprintf(stderr, "Executing: '%s'\n\n", qry);
+#endif
+       res = PQexec(conn, qry);
 
-       if (PQresultStatus(res) != PGRES_COMMAND_OK && PQresultStatus(res) != PGRES_TUPLES_OK)
+       switch (PQresultStatus(res))
        {
-               if (PQresultStatus(res) == PGRES_COPY_IN)
-               {
-                       if (conn != AH->connection)
-                               die_horribly(AH, modulename, "COPY command executed in non-primary connection\n");
-
-                       AH->pgCopyIn = 1;
-               }
-               else
-                       die_horribly(AH, modulename, "%s: %s",
-                                                desc, PQerrorMessage(AH->connection));
+               case PGRES_COMMAND_OK:
+               case PGRES_TUPLES_OK:
+                       /* A-OK */
+                       break;
+               case PGRES_COPY_IN:
+                       /* Assume this is an expected result */
+                       AH->pgCopyIn = true;
+                       break;
+               default:
+                       /* trouble */
+                       strncpy(errStmt, qry, DB_MAX_ERR_STMT);
+                       if (errStmt[DB_MAX_ERR_STMT - 1] != '\0')
+                       {
+                               errStmt[DB_MAX_ERR_STMT - 4] = '.';
+                               errStmt[DB_MAX_ERR_STMT - 3] = '.';
+                               errStmt[DB_MAX_ERR_STMT - 2] = '.';
+                               errStmt[DB_MAX_ERR_STMT - 1] = '\0';
+                       }
+                       warn_or_die_horribly(AH, modulename, "%s: %s    Command was: %s\n",
+                                                                desc, PQerrorMessage(conn), errStmt);
+                       break;
        }
 
        PQclear(res);
-
-       return strlen(qry->data);
 }
 
 /*
@@ -479,7 +328,7 @@ _executeSqlCommand(ArchiveHandle *AH, PGconn *conn, PQExpBuffer qry, char *desc)
 static char *
 _sendCopyLine(ArchiveHandle *AH, char *qry, char *eos)
 {
-       int                     loc;                    /* Location of next newline */
+       size_t          loc;                    /* Location of next newline */
        int                     pos = 0;                /* Current position */
        int                     sPos = 0;               /* Last pos of a slash char */
        int                     isEnd = 0;
@@ -497,8 +346,8 @@ _sendCopyLine(ArchiveHandle *AH, char *qry, char *eos)
                }
 
                /*
-                * fprintf(stderr, "Found cr at %d, prev char was %c, next was
-                * %c\n", loc, qry[loc-1], qry[loc+1]);
+                * fprintf(stderr, "Found cr at %d, prev char was %c, next was %c\n",
+                * loc, qry[loc-1], qry[loc+1]);
                 */
 
                /* Count the number of preceding slashes */
@@ -509,8 +358,8 @@ _sendCopyLine(ArchiveHandle *AH, char *qry, char *eos)
                sPos = loc - sPos;
 
                /*
-                * If an odd number of preceding slashes, then \n was escaped so
-                * set the next search pos, and loop (if any left).
+                * If an odd number of preceding slashes, then \n was escaped so set
+                * the next search pos, and loop (if any left).
                 */
                if ((sPos & 1) == 1)
                {
@@ -531,170 +380,258 @@ _sendCopyLine(ArchiveHandle *AH, char *qry, char *eos)
        appendPQExpBuffer(AH->pgCopyBuf, "%s\n", qry);
        isEnd = (strcmp(AH->pgCopyBuf->data, "\\.\n") == 0);
 
-       /*---------
-        * fprintf(stderr, "Sending '%s' via
-        *              COPY (at end = %d)\n\n", AH->pgCopyBuf->data, isEnd);
-        *---------
+       /*
+        * Note that we drop the data on the floor if libpq has failed to enter
+        * COPY mode; this allows us to behave reasonably when trying to continue
+        * after an error in a COPY command.
         */
-
-       if (PQputline(AH->connection, AH->pgCopyBuf->data) != 0)
-               die_horribly(AH, modulename, "error returned by PQputline\n");
+       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 (PQendcopy(AH->connection) != 0)
-                       die_horribly(AH, modulename, "error returned by PQendcopy\n");
+               PGresult   *res;
+
+               if (PQputCopyEnd(AH->connection, NULL) <= 0)
+                       die_horribly(AH, modulename, "error returned by PQputCopyEnd: %s",
+                                                PQerrorMessage(AH->connection));
 
-               AH->pgCopyIn = 0;
+               /* 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;
        }
 
        return qry + loc + 1;
 }
 
 /*
- * Used by ExecuteSqlCommandBuf to send one buffered line of SQL (not data for the copy command).
+ * Used by ExecuteSqlCommandBuf to send one buffered line of SQL
+ * (not data for the copy command).
  */
 static char *
 _sendSQLLine(ArchiveHandle *AH, char *qry, char *eos)
 {
-       int                     pos = 0;                /* Current position */
-
        /*
         * The following is a mini state machine to assess the end of an SQL
-        * statement. It really only needs to parse good SQL, or at least
-        * that's the theory... End-of-statement is assumed to be an unquoted,
-        * un commented semi-colon.
-        */
-
-       /*
-        * fprintf(stderr, "Buffer at start is: '%s'\n\n", AH->sqlBuf->data);
+        * statement. It really only needs to parse good SQL, or at least that's
+        * the theory... End-of-statement is assumed to be an unquoted,
+        * un-commented semi-colon that's not within any parentheses.
+        *
+        * Note: the input can be split into bufferloads at arbitrary boundaries.
+        * Therefore all state must be kept in AH->sqlparse, not in local
+        * variables of this routine.  We assume that AH->sqlparse was filled with
+        * zeroes when created.
         */
-
-       for (pos = 0; pos < (eos - qry); pos++)
+       for (; qry < eos; qry++)
        {
-               appendPQExpBufferChar(AH->sqlBuf, qry[pos]);
-               /* fprintf(stderr, " %c",qry[pos]); */
-
                switch (AH->sqlparse.state)
                {
-
                        case SQL_SCAN:          /* Default state == 0, set in _allocAH */
-
-                               if (qry[pos] == ';' && AH->sqlparse.braceDepth == 0)
+                               if (*qry == ';' && AH->sqlparse.braceDepth == 0)
                                {
-                                       /* Send It & reset the buffer */
-
                                        /*
-                                        * fprintf(stderr, "    sending: '%s'\n\n",
-                                        * AH->sqlBuf->data);
+                                        * We've found the end of a statement. Send it and reset
+                                        * the buffer.
                                         */
-                                       ExecuteSqlCommand(AH, AH->sqlBuf, "could not execute query", false);
+                                       appendPQExpBufferChar(AH->sqlBuf, ';');         /* inessential */
+                                       ExecuteSqlCommand(AH, AH->sqlBuf->data,
+                                                                         "could not execute query");
                                        resetPQExpBuffer(AH->sqlBuf);
                                        AH->sqlparse.lastChar = '\0';
 
                                        /*
-                                        * Remove any following newlines - so that embedded
-                                        * COPY commands don't get a starting newline.
+                                        * Remove any following newlines - so that embedded COPY
+                                        * commands don't get a starting newline.
                                         */
-                                       pos++;
-                                       for (; pos < (eos - qry) && qry[pos] == '\n'; pos++);
+                                       qry++;
+                                       while (qry < eos && *qry == '\n')
+                                               qry++;
 
-                                       /* We've got our line, so exit */
-                                       return qry + pos;
+                                       /* We've finished one line, so exit */
+                                       return qry;
                                }
-                               else
+                               else if (*qry == '\'')
                                {
-                                       if (qry[pos] == '"' || qry[pos] == '\'')
-                                       {
-                                               /* fprintf(stderr,"[startquote]\n"); */
-                                               AH->sqlparse.state = SQL_IN_QUOTE;
-                                               AH->sqlparse.quoteChar = qry[pos];
-                                               AH->sqlparse.backSlash = 0;
-                                       }
-                                       else if (qry[pos] == '-' && AH->sqlparse.lastChar == '-')
-                                               AH->sqlparse.state = SQL_IN_SQL_COMMENT;
-                                       else if (qry[pos] == '*' && AH->sqlparse.lastChar == '/')
-                                               AH->sqlparse.state = SQL_IN_EXT_COMMENT;
-                                       else if (qry[pos] == '(')
-                                               AH->sqlparse.braceDepth++;
-                                       else if (qry[pos] == ')')
-                                               AH->sqlparse.braceDepth--;
-
-                                       AH->sqlparse.lastChar = qry[pos];
+                                       if (AH->sqlparse.lastChar == 'E')
+                                               AH->sqlparse.state = SQL_IN_E_QUOTE;
+                                       else
+                                               AH->sqlparse.state = SQL_IN_SINGLE_QUOTE;
+                                       AH->sqlparse.backSlash = false;
+                               }
+                               else if (*qry == '"')
+                               {
+                                       AH->sqlparse.state = SQL_IN_DOUBLE_QUOTE;
                                }
 
+                               /*
+                                * Look for dollar-quotes. We make the assumption that
+                                * $-quotes will not have an ident character just before them
+                                * in pg_dump output.  XXX is this good enough?
+                                */
+                               else if (*qry == '$' && !_isIdentChar(AH->sqlparse.lastChar))
+                               {
+                                       AH->sqlparse.state = SQL_IN_DOLLAR_TAG;
+                                       /* initialize separate buffer with possible tag */
+                                       if (AH->sqlparse.tagBuf == NULL)
+                                               AH->sqlparse.tagBuf = createPQExpBuffer();
+                                       else
+                                               resetPQExpBuffer(AH->sqlparse.tagBuf);
+                                       appendPQExpBufferChar(AH->sqlparse.tagBuf, *qry);
+                               }
+                               else if (*qry == '-' && AH->sqlparse.lastChar == '-')
+                                       AH->sqlparse.state = SQL_IN_SQL_COMMENT;
+                               else if (*qry == '*' && AH->sqlparse.lastChar == '/')
+                                       AH->sqlparse.state = SQL_IN_EXT_COMMENT;
+                               else if (*qry == '(')
+                                       AH->sqlparse.braceDepth++;
+                               else if (*qry == ')')
+                                       AH->sqlparse.braceDepth--;
                                break;
 
                        case SQL_IN_SQL_COMMENT:
-
-                               if (qry[pos] == '\n')
+                               if (*qry == '\n')
                                        AH->sqlparse.state = SQL_SCAN;
                                break;
 
                        case SQL_IN_EXT_COMMENT:
 
-                               if (AH->sqlparse.lastChar == '*' && qry[pos] == '/')
+                               /*
+                                * This isn't fully correct, because we don't account for
+                                * nested slash-stars, but pg_dump never emits such.
+                                */
+                               if (AH->sqlparse.lastChar == '*' && *qry == '/')
                                        AH->sqlparse.state = SQL_SCAN;
                                break;
 
-                       case SQL_IN_QUOTE:
+                       case SQL_IN_SINGLE_QUOTE:
+                               /* We needn't handle '' specially */
+                               if (*qry == '\'' && !AH->sqlparse.backSlash)
+                                       AH->sqlparse.state = SQL_SCAN;
+                               else if (*qry == '\\')
+                                       AH->sqlparse.backSlash = !AH->sqlparse.backSlash;
+                               else
+                                       AH->sqlparse.backSlash = false;
+                               break;
 
-                               if (!AH->sqlparse.backSlash && AH->sqlparse.quoteChar == qry[pos])
-                               {
-                                       /* fprintf(stderr,"[endquote]\n"); */
+                       case SQL_IN_E_QUOTE:
+
+                               /*
+                                * Eventually we will need to handle '' specially, because
+                                * after E'...''... we should still be in E_QUOTE state.
+                                *
+                                * XXX problem: how do we tell whether the dump was made by a
+                                * version that thinks backslashes aren't special in non-E
+                                * literals??
+                                */
+                               if (*qry == '\'' && !AH->sqlparse.backSlash)
+                                       AH->sqlparse.state = SQL_SCAN;
+                               else if (*qry == '\\')
+                                       AH->sqlparse.backSlash = !AH->sqlparse.backSlash;
+                               else
+                                       AH->sqlparse.backSlash = false;
+                               break;
+
+                       case SQL_IN_DOUBLE_QUOTE:
+                               /* We needn't handle "" specially */
+                               if (*qry == '"')
                                        AH->sqlparse.state = SQL_SCAN;
+                               break;
+
+                       case SQL_IN_DOLLAR_TAG:
+                               if (*qry == '$')
+                               {
+                                       /* Do not add the closing $ to tagBuf */
+                                       AH->sqlparse.state = SQL_IN_DOLLAR_QUOTE;
+                                       AH->sqlparse.minTagEndPos = AH->sqlBuf->len + AH->sqlparse.tagBuf->len + 1;
+                               }
+                               else if (_isDQChar(*qry, (AH->sqlparse.tagBuf->len == 1)))
+                               {
+                                       /* Valid, so add to tag */
+                                       appendPQExpBufferChar(AH->sqlparse.tagBuf, *qry);
                                }
                                else
                                {
-
-                                       if (qry[pos] == '\\')
-                                       {
-                                               if (AH->sqlparse.lastChar == '\\')
-                                                       AH->sqlparse.backSlash = !AH->sqlparse.backSlash;
-                                               else
-                                                       AH->sqlparse.backSlash = 1;
-                                       }
-                                       else
-                                               AH->sqlparse.backSlash = 0;
+                                       /*
+                                        * Ooops, we're not really in a dollar-tag.  Valid tag
+                                        * chars do not include the various chars we look for in
+                                        * this state machine, so it's safe to just jump from this
+                                        * state back to SCAN.  We have to back up the qry pointer
+                                        * so that the current character gets rescanned in SCAN
+                                        * state; and then "continue" so that the bottom-of-loop
+                                        * actions aren't done yet.
+                                        */
+                                       AH->sqlparse.state = SQL_SCAN;
+                                       qry--;
+                                       continue;
                                }
                                break;
 
+                       case SQL_IN_DOLLAR_QUOTE:
+
+                               /*
+                                * If we are at a $, see whether what precedes it matches
+                                * tagBuf.      (Remember that the trailing $ of the tag was not
+                                * added to tagBuf.)  However, don't compare until we have
+                                * enough data to be a possible match --- this is needed to
+                                * avoid false match on '$a$a$...'
+                                */
+                               if (*qry == '$' &&
+                                       AH->sqlBuf->len >= AH->sqlparse.minTagEndPos &&
+                                       strcmp(AH->sqlparse.tagBuf->data,
+                                                  AH->sqlBuf->data + AH->sqlBuf->len - AH->sqlparse.tagBuf->len) == 0)
+                                       AH->sqlparse.state = SQL_SCAN;
+                               break;
                }
-               AH->sqlparse.lastChar = qry[pos];
-               /* fprintf(stderr, "\n"); */
+
+               appendPQExpBufferChar(AH->sqlBuf, *qry);
+               AH->sqlparse.lastChar = *qry;
        }
 
        /*
-        * If we get here, we've processed entire string with no complete SQL
+        * If we get here, we've processed entire bufferload with no complete SQL
         * stmt
         */
        return eos;
-
 }
 
 
 /* Convenience function to send one or more queries. Monitors result to handle COPY statements */
 int
-ExecuteSqlCommandBuf(ArchiveHandle *AH, void *qryv, int bufLen)
+ExecuteSqlCommandBuf(ArchiveHandle *AH, void *qryv, size_t bufLen)
 {
        char       *qry = (char *) qryv;
        char       *eos = qry + bufLen;
 
        /*
-        * fprintf(stderr, "\n\n*****\n
-        * Buffer:\n\n%s\n*******************\n\n", qry);
+        * fprintf(stderr, "\n\n*****\n Buffer:\n\n%s\n*******************\n\n",
+        * qry);
         */
 
        /* Could switch between command and COPY IN mode at each line */
        while (qry < eos)
        {
-               if (AH->pgCopyIn)
+               /*
+                * If libpq is in CopyIn mode *or* if the archive structure shows we
+                * are sending COPY data, treat the data as COPY data.  The pgCopyIn
+                * check is only needed for backwards compatibility with ancient
+                * archive files that might just issue a COPY command without marking
+                * it properly.  Note that in an archive entry that has a copyStmt,
+                * all data up to the end of the entry will go to _sendCopyLine, and
+                * therefore will be dropped if libpq has failed to enter COPY mode.
+                * Also, if a "\." data terminator is found, anything remaining in the
+                * archive entry will be dropped.
+                */
+               if (AH->pgCopyIn || AH->writingCopyData)
                        qry = _sendCopyLine(AH, qry, eos);
                else
                        qry = _sendSQLLine(AH, qry, eos);
@@ -704,176 +641,42 @@ ExecuteSqlCommandBuf(ArchiveHandle *AH, void *qryv, int bufLen)
 }
 
 void
-FixupBlobRefs(ArchiveHandle *AH, TocEntry *te)
-{
-       PQExpBuffer tblName;
-       PQExpBuffer tblQry;
-       PGresult   *res,
-                          *uRes;
-       int                     i,
-                               n;
-       char       *attr;
-
-       if (strcmp(te->name, BLOB_XREF_TABLE) == 0)
-               return;
-
-       tblName = createPQExpBuffer();
-       tblQry = createPQExpBuffer();
-
-       if (te->namespace && strlen(te->namespace) > 0)
-               appendPQExpBuffer(tblName, "%s.",
-                                                 fmtId(te->namespace, false));
-       appendPQExpBuffer(tblName, "%s",
-                                         fmtId(te->name, false));
-
-       appendPQExpBuffer(tblQry,
-                                         "SELECT a.attname FROM "
-                                         "pg_catalog.pg_attribute a, pg_catalog.pg_type t "
-                                         "WHERE a.attnum > 0 AND a.attrelid = '%s'::pg_catalog.regclass "
-                                         "AND a.atttypid = t.oid AND t.typname in ('oid', 'lo')",
-                                         tblName->data);
-
-       res = PQexec(AH->blobConnection, tblQry->data);
-       if (!res)
-               die_horribly(AH, modulename, "could not find oid columns of table \"%s\": %s",
-                                        te->name, PQerrorMessage(AH->connection));
-
-       if ((n = PQntuples(res)) == 0)
-       {
-               /* nothing to do */
-               ahlog(AH, 1, "no OID type columns in table %s\n", te->name);
-       }
-
-       for (i = 0; i < n; i++)
-       {
-               attr = PQgetvalue(res, i, 0);
-
-               ahlog(AH, 1, "fixing large object cross-references for %s.%s\n",
-                         te->name, attr);
-
-               resetPQExpBuffer(tblQry);
-
-               /* Can't use fmtId twice in one call... */
-               appendPQExpBuffer(tblQry,
-                                                 "UPDATE %s SET %s = %s.newOid",
-                                                 tblName->data, fmtId(attr, false),
-                                                 BLOB_XREF_TABLE);
-               appendPQExpBuffer(tblQry,
-                                                 " FROM %s WHERE %s.oldOid = %s.%s",
-                                                 BLOB_XREF_TABLE,
-                                                 BLOB_XREF_TABLE,
-                                                 tblName->data, fmtId(attr, false));
-
-               ahlog(AH, 10, "SQL: %s\n", tblQry->data);
-
-               uRes = PQexec(AH->blobConnection, tblQry->data);
-               if (!uRes)
-                       die_horribly(AH, modulename,
-                                       "could not update column \"%s\" of table \"%s\": %s",
-                                       attr, te->name, PQerrorMessage(AH->blobConnection));
-
-               if (PQresultStatus(uRes) != PGRES_COMMAND_OK)
-                       die_horribly(AH, modulename,
-                               "error while updating column \"%s\" of table \"%s\": %s",
-                                       attr, te->name, PQerrorMessage(AH->blobConnection));
-
-               PQclear(uRes);
-       }
-
-       PQclear(res);
-       destroyPQExpBuffer(tblName);
-       destroyPQExpBuffer(tblQry);
-}
-
-/**********
- *     Convenient SQL calls
- **********/
-void
-CreateBlobXrefTable(ArchiveHandle *AH)
-{
-       PQExpBuffer qry = createPQExpBuffer();
-
-       /* IF we don't have a BLOB connection, then create one */
-       if (!AH->blobConnection)
-               AH->blobConnection = _connectDB(AH, NULL, NULL);
-
-       ahlog(AH, 1, "creating table for large object cross-references\n");
-
-       appendPQExpBuffer(qry, "Create Temporary Table %s(oldOid pg_catalog.oid, newOid pg_catalog.oid);", BLOB_XREF_TABLE);
-
-       ExecuteSqlCommand(AH, qry, "could not create large object cross-reference table", true);
-
-       resetPQExpBuffer(qry);
-
-       appendPQExpBuffer(qry, "Create Unique Index %s_ix on %s(oldOid)", BLOB_XREF_TABLE, BLOB_XREF_TABLE);
-       ExecuteSqlCommand(AH, qry, "could not create index on large object cross-reference table", true);
-
-       destroyPQExpBuffer(qry);
-}
-
-void
-InsertBlobXref(ArchiveHandle *AH, Oid old, Oid new)
-{
-       PQExpBuffer qry = createPQExpBuffer();
-
-       appendPQExpBuffer(qry,
-                                         "Insert Into %s(oldOid, newOid) Values ('%u', '%u');",
-                                         BLOB_XREF_TABLE, old, new);
-
-       ExecuteSqlCommand(AH, qry, "could not create large object cross-reference entry", true);
-
-       destroyPQExpBuffer(qry);
-}
-
-void
 StartTransaction(ArchiveHandle *AH)
 {
-       PQExpBuffer qry = createPQExpBuffer();
-
-       appendPQExpBuffer(qry, "Begin;");
-
-       ExecuteSqlCommand(AH, qry, "could not start database transaction", false);
-       AH->txActive = true;
-
-       destroyPQExpBuffer(qry);
+       ExecuteSqlCommand(AH, "BEGIN", "could not start database transaction");
 }
 
 void
-StartTransactionXref(ArchiveHandle *AH)
+CommitTransaction(ArchiveHandle *AH)
 {
-       PQExpBuffer qry = createPQExpBuffer();
-
-       appendPQExpBuffer(qry, "Begin;");
-
-       ExecuteSqlCommand(AH, qry,
-                                         "could not start transaction for large object cross-references", true);
-       AH->blobTxActive = true;
-
-       destroyPQExpBuffer(qry);
+       ExecuteSqlCommand(AH, "COMMIT", "could not commit database transaction");
 }
 
-void
-CommitTransaction(ArchiveHandle *AH)
+static bool
+_isIdentChar(unsigned char c)
 {
-       PQExpBuffer qry = createPQExpBuffer();
-
-       appendPQExpBuffer(qry, "Commit;");
-
-       ExecuteSqlCommand(AH, qry, "could not commit database transaction", false);
-       AH->txActive = false;
-
-       destroyPQExpBuffer(qry);
+       if ((c >= 'a' && c <= 'z')
+               || (c >= 'A' && c <= 'Z')
+               || (c >= '0' && c <= '9')
+               || (c == '_')
+               || (c == '$')
+               || (c >= (unsigned char) '\200')                /* no need to check <= \377 */
+               )
+               return true;
+       else
+               return false;
 }
 
-void
-CommitTransactionXref(ArchiveHandle *AH)
+static bool
+_isDQChar(unsigned char c, bool atStart)
 {
-       PQExpBuffer qry = createPQExpBuffer();
-
-       appendPQExpBuffer(qry, "Commit;");
-
-       ExecuteSqlCommand(AH, qry, "could not commit transaction for large object cross-references", true);
-       AH->blobTxActive = false;
-
-       destroyPQExpBuffer(qry);
+       if ((c >= 'a' && c <= 'z')
+               || (c >= 'A' && c <= 'Z')
+               || (c == '_')
+               || (!atStart && c >= '0' && c <= '9')
+               || (c >= (unsigned char) '\200')                /* no need to check <= \377 */
+               )
+               return true;
+       else
+               return false;
 }