LCOV - code coverage report
Current view: top level - src/bin/pg_dump - pg_backup_db.c (source / functions) Hit Total Coverage
Test: PostgreSQL 13devel Lines: 166 255 65.1 %
Date: 2019-11-13 23:06:49 Functions: 16 18 88.9 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * pg_backup_db.c
       4             :  *
       5             :  *  Implements the basic DB functions used by the archiver.
       6             :  *
       7             :  * IDENTIFICATION
       8             :  *    src/bin/pg_dump/pg_backup_db.c
       9             :  *
      10             :  *-------------------------------------------------------------------------
      11             :  */
      12             : #include "postgres_fe.h"
      13             : 
      14             : #include <unistd.h>
      15             : #include <ctype.h>
      16             : #ifdef HAVE_TERMIOS_H
      17             : #include <termios.h>
      18             : #endif
      19             : 
      20             : #include "dumputils.h"
      21             : #include "fe_utils/connect.h"
      22             : #include "fe_utils/string_utils.h"
      23             : #include "parallel.h"
      24             : #include "pg_backup_archiver.h"
      25             : #include "pg_backup_db.h"
      26             : #include "pg_backup_utils.h"
      27             : 
      28             : static void _check_database_version(ArchiveHandle *AH);
      29             : static PGconn *_connectDB(ArchiveHandle *AH, const char *newdbname, const char *newUser);
      30             : static void notice_processor(void *arg, const char *message);
      31             : 
      32             : static void
      33         236 : _check_database_version(ArchiveHandle *AH)
      34             : {
      35             :     const char *remoteversion_str;
      36             :     int         remoteversion;
      37             :     PGresult   *res;
      38             : 
      39         236 :     remoteversion_str = PQparameterStatus(AH->connection, "server_version");
      40         236 :     remoteversion = PQserverVersion(AH->connection);
      41         236 :     if (remoteversion == 0 || !remoteversion_str)
      42           0 :         fatal("could not get server_version from libpq");
      43             : 
      44         236 :     AH->public.remoteVersionStr = pg_strdup(remoteversion_str);
      45         236 :     AH->public.remoteVersion = remoteversion;
      46         236 :     if (!AH->archiveRemoteVersion)
      47         166 :         AH->archiveRemoteVersion = AH->public.remoteVersionStr;
      48             : 
      49         236 :     if (remoteversion != PG_VERSION_NUM
      50           0 :         && (remoteversion < AH->public.minRemoteVersion ||
      51           0 :             remoteversion > AH->public.maxRemoteVersion))
      52             :     {
      53           0 :         pg_log_error("server version: %s; %s version: %s",
      54             :                      remoteversion_str, progname, PG_VERSION);
      55           0 :         fatal("aborting because of server version mismatch");
      56             :     }
      57             : 
      58             :     /*
      59             :      * When running against 9.0 or later, check if we are in recovery mode,
      60             :      * which means we are on a hot standby.
      61             :      */
      62         236 :     if (remoteversion >= 90000)
      63             :     {
      64         236 :         res = ExecuteSqlQueryForSingleRow((Archive *) AH, "SELECT pg_catalog.pg_is_in_recovery()");
      65             : 
      66         236 :         AH->public.isStandby = (strcmp(PQgetvalue(res, 0, 0), "t") == 0);
      67         236 :         PQclear(res);
      68             :     }
      69             :     else
      70           0 :         AH->public.isStandby = false;
      71         236 : }
      72             : 
      73             : /*
      74             :  * Reconnect to the server.  If dbname is not NULL, use that database,
      75             :  * else the one associated with the archive handle.  If username is
      76             :  * not NULL, use that user name, else the one from the handle.
      77             :  */
      78             : void
      79          26 : ReconnectToServer(ArchiveHandle *AH, const char *dbname, const char *username)
      80             : {
      81             :     PGconn     *newConn;
      82             :     const char *newdbname;
      83             :     const char *newusername;
      84             : 
      85          26 :     if (!dbname)
      86           0 :         newdbname = PQdb(AH->connection);
      87             :     else
      88          26 :         newdbname = dbname;
      89             : 
      90          26 :     if (!username)
      91          26 :         newusername = PQuser(AH->connection);
      92             :     else
      93           0 :         newusername = username;
      94             : 
      95          26 :     newConn = _connectDB(AH, newdbname, newusername);
      96             : 
      97             :     /* Update ArchiveHandle's connCancel before closing old connection */
      98          26 :     set_archive_cancel_info(AH, newConn);
      99             : 
     100          26 :     PQfinish(AH->connection);
     101          26 :     AH->connection = newConn;
     102             : 
     103             :     /* Start strict; later phases may override this. */
     104          26 :     PQclear(ExecuteSqlQueryForSingleRow((Archive *) AH,
     105             :                                         ALWAYS_SECURE_SEARCH_PATH_SQL));
     106          26 : }
     107             : 
     108             : /*
     109             :  * Connect to the db again.
     110             :  *
     111             :  * Note: it's not really all that sensible to use a single-entry password
     112             :  * cache if the username keeps changing.  In current usage, however, the
     113             :  * username never does change, so one savedPassword is sufficient.  We do
     114             :  * update the cache on the off chance that the password has changed since the
     115             :  * start of the run.
     116             :  */
     117             : static PGconn *
     118          26 : _connectDB(ArchiveHandle *AH, const char *reqdb, const char *requser)
     119             : {
     120             :     PQExpBufferData connstr;
     121             :     PGconn     *newConn;
     122             :     const char *newdb;
     123             :     const char *newuser;
     124             :     char       *password;
     125             :     char        passbuf[100];
     126             :     bool        new_pass;
     127             : 
     128          26 :     if (!reqdb)
     129           0 :         newdb = PQdb(AH->connection);
     130             :     else
     131          26 :         newdb = reqdb;
     132             : 
     133          26 :     if (!requser || strlen(requser) == 0)
     134           0 :         newuser = PQuser(AH->connection);
     135             :     else
     136          26 :         newuser = requser;
     137             : 
     138          26 :     pg_log_info("connecting to database \"%s\" as user \"%s\"",
     139             :                 newdb, newuser);
     140             : 
     141          26 :     password = AH->savedPassword;
     142             : 
     143          26 :     if (AH->promptPassword == TRI_YES && password == NULL)
     144             :     {
     145           0 :         simple_prompt("Password: ", passbuf, sizeof(passbuf), false);
     146           0 :         password = passbuf;
     147             :     }
     148             : 
     149          26 :     initPQExpBuffer(&connstr);
     150          26 :     appendPQExpBufferStr(&connstr, "dbname=");
     151          26 :     appendConnStrVal(&connstr, newdb);
     152             : 
     153             :     do
     154             :     {
     155             :         const char *keywords[7];
     156             :         const char *values[7];
     157             : 
     158          26 :         keywords[0] = "host";
     159          26 :         values[0] = PQhost(AH->connection);
     160          26 :         keywords[1] = "port";
     161          26 :         values[1] = PQport(AH->connection);
     162          26 :         keywords[2] = "user";
     163          26 :         values[2] = newuser;
     164          26 :         keywords[3] = "password";
     165          26 :         values[3] = password;
     166          26 :         keywords[4] = "dbname";
     167          26 :         values[4] = connstr.data;
     168          26 :         keywords[5] = "fallback_application_name";
     169          26 :         values[5] = progname;
     170          26 :         keywords[6] = NULL;
     171          26 :         values[6] = NULL;
     172             : 
     173          26 :         new_pass = false;
     174          26 :         newConn = PQconnectdbParams(keywords, values, true);
     175             : 
     176          26 :         if (!newConn)
     177           0 :             fatal("could not reconnect to database");
     178             : 
     179          26 :         if (PQstatus(newConn) == CONNECTION_BAD)
     180             :         {
     181           0 :             if (!PQconnectionNeedsPassword(newConn))
     182           0 :                 fatal("could not reconnect to database: %s",
     183             :                       PQerrorMessage(newConn));
     184           0 :             PQfinish(newConn);
     185             : 
     186           0 :             if (password)
     187           0 :                 fprintf(stderr, "Password incorrect\n");
     188             : 
     189           0 :             fprintf(stderr, "Connecting to %s as %s\n",
     190             :                     newdb, newuser);
     191             : 
     192           0 :             if (AH->promptPassword != TRI_NO)
     193             :             {
     194           0 :                 simple_prompt("Password: ", passbuf, sizeof(passbuf), false);
     195           0 :                 password = passbuf;
     196             :             }
     197             :             else
     198           0 :                 fatal("connection needs password");
     199             : 
     200           0 :             new_pass = true;
     201             :         }
     202          26 :     } while (new_pass);
     203             : 
     204             :     /*
     205             :      * We want to remember connection's actual password, whether or not we got
     206             :      * it by prompting.  So we don't just store the password variable.
     207             :      */
     208          26 :     if (PQconnectionUsedPassword(newConn))
     209             :     {
     210           0 :         if (AH->savedPassword)
     211           0 :             free(AH->savedPassword);
     212           0 :         AH->savedPassword = pg_strdup(PQpass(newConn));
     213             :     }
     214             : 
     215          26 :     termPQExpBuffer(&connstr);
     216             : 
     217             :     /* check for version mismatch */
     218          26 :     _check_database_version(AH);
     219             : 
     220          26 :     PQsetNoticeProcessor(newConn, notice_processor, NULL);
     221             : 
     222          26 :     return newConn;
     223             : }
     224             : 
     225             : 
     226             : /*
     227             :  * Make a database connection with the given parameters.  The
     228             :  * connection handle is returned, the parameters are stored in AHX.
     229             :  * An interactive password prompt is automatically issued if required.
     230             :  *
     231             :  * Note: it's not really all that sensible to use a single-entry password
     232             :  * cache if the username keeps changing.  In current usage, however, the
     233             :  * username never does change, so one savedPassword is sufficient.
     234             :  */
     235             : void
     236         212 : ConnectDatabase(Archive *AHX,
     237             :                 const char *dbname,
     238             :                 const char *pghost,
     239             :                 const char *pgport,
     240             :                 const char *username,
     241             :                 trivalue prompt_password)
     242             : {
     243         212 :     ArchiveHandle *AH = (ArchiveHandle *) AHX;
     244             :     char       *password;
     245             :     char        passbuf[100];
     246             :     bool        new_pass;
     247             : 
     248         212 :     if (AH->connection)
     249           0 :         fatal("already connected to a database");
     250             : 
     251         212 :     password = AH->savedPassword;
     252             : 
     253         212 :     if (prompt_password == TRI_YES && password == NULL)
     254             :     {
     255           0 :         simple_prompt("Password: ", passbuf, sizeof(passbuf), false);
     256           0 :         password = passbuf;
     257             :     }
     258         212 :     AH->promptPassword = prompt_password;
     259             : 
     260             :     /*
     261             :      * Start the connection.  Loop until we have a password if requested by
     262             :      * backend.
     263             :      */
     264             :     do
     265             :     {
     266             :         const char *keywords[7];
     267             :         const char *values[7];
     268             : 
     269         212 :         keywords[0] = "host";
     270         212 :         values[0] = pghost;
     271         212 :         keywords[1] = "port";
     272         212 :         values[1] = pgport;
     273         212 :         keywords[2] = "user";
     274         212 :         values[2] = username;
     275         212 :         keywords[3] = "password";
     276         212 :         values[3] = password;
     277         212 :         keywords[4] = "dbname";
     278         212 :         values[4] = dbname;
     279         212 :         keywords[5] = "fallback_application_name";
     280         212 :         values[5] = progname;
     281         212 :         keywords[6] = NULL;
     282         212 :         values[6] = NULL;
     283             : 
     284         212 :         new_pass = false;
     285         212 :         AH->connection = PQconnectdbParams(keywords, values, true);
     286             : 
     287         212 :         if (!AH->connection)
     288           0 :             fatal("could not connect to database");
     289             : 
     290         214 :         if (PQstatus(AH->connection) == CONNECTION_BAD &&
     291           2 :             PQconnectionNeedsPassword(AH->connection) &&
     292           0 :             password == NULL &&
     293             :             prompt_password != TRI_NO)
     294             :         {
     295           0 :             PQfinish(AH->connection);
     296           0 :             simple_prompt("Password: ", passbuf, sizeof(passbuf), false);
     297           0 :             password = passbuf;
     298           0 :             new_pass = true;
     299             :         }
     300         212 :     } while (new_pass);
     301             : 
     302             :     /* check to see that the backend connection was successfully made */
     303         212 :     if (PQstatus(AH->connection) == CONNECTION_BAD)
     304           2 :         fatal("connection to database \"%s\" failed: %s",
     305             :               PQdb(AH->connection) ? PQdb(AH->connection) : "",
     306             :               PQerrorMessage(AH->connection));
     307             : 
     308             :     /* Start strict; later phases may override this. */
     309         210 :     PQclear(ExecuteSqlQueryForSingleRow((Archive *) AH,
     310             :                                         ALWAYS_SECURE_SEARCH_PATH_SQL));
     311             : 
     312             :     /*
     313             :      * We want to remember connection's actual password, whether or not we got
     314             :      * it by prompting.  So we don't just store the password variable.
     315             :      */
     316         210 :     if (PQconnectionUsedPassword(AH->connection))
     317             :     {
     318           0 :         if (AH->savedPassword)
     319           0 :             free(AH->savedPassword);
     320           0 :         AH->savedPassword = pg_strdup(PQpass(AH->connection));
     321             :     }
     322             : 
     323             :     /* check for version mismatch */
     324         210 :     _check_database_version(AH);
     325             : 
     326         210 :     PQsetNoticeProcessor(AH->connection, notice_processor, NULL);
     327             : 
     328             :     /* arrange for SIGINT to issue a query cancel on this connection */
     329         210 :     set_archive_cancel_info(AH, AH->connection);
     330         210 : }
     331             : 
     332             : /*
     333             :  * Close the connection to the database and also cancel off the query if we
     334             :  * have one running.
     335             :  */
     336             : void
     337         212 : DisconnectDatabase(Archive *AHX)
     338             : {
     339         212 :     ArchiveHandle *AH = (ArchiveHandle *) AHX;
     340             :     char        errbuf[1];
     341             : 
     342         212 :     if (!AH->connection)
     343           0 :         return;
     344             : 
     345         212 :     if (AH->connCancel)
     346             :     {
     347             :         /*
     348             :          * If we have an active query, send a cancel before closing, ignoring
     349             :          * any errors.  This is of no use for a normal exit, but might be
     350             :          * helpful during fatal().
     351             :          */
     352         210 :         if (PQtransactionStatus(AH->connection) == PQTRANS_ACTIVE)
     353           0 :             (void) PQcancel(AH->connCancel, errbuf, sizeof(errbuf));
     354             : 
     355             :         /*
     356             :          * Prevent signal handler from sending a cancel after this.
     357             :          */
     358         210 :         set_archive_cancel_info(AH, NULL);
     359             :     }
     360             : 
     361         212 :     PQfinish(AH->connection);
     362         212 :     AH->connection = NULL;
     363             : }
     364             : 
     365             : PGconn *
     366        2826 : GetConnection(Archive *AHX)
     367             : {
     368        2826 :     ArchiveHandle *AH = (ArchiveHandle *) AHX;
     369             : 
     370        2826 :     return AH->connection;
     371             : }
     372             : 
     373             : static void
     374           0 : notice_processor(void *arg, const char *message)
     375             : {
     376           0 :     pg_log_generic(PG_LOG_INFO, "%s", message);
     377           0 : }
     378             : 
     379             : /* Like fatal(), but with a complaint about a particular query. */
     380             : static void
     381           2 : die_on_query_failure(ArchiveHandle *AH, const char *query)
     382             : {
     383           2 :     pg_log_error("query failed: %s",
     384             :                  PQerrorMessage(AH->connection));
     385           2 :     fatal("query was: %s", query);
     386             : }
     387             : 
     388             : void
     389        5602 : ExecuteSqlStatement(Archive *AHX, const char *query)
     390             : {
     391        5602 :     ArchiveHandle *AH = (ArchiveHandle *) AHX;
     392             :     PGresult   *res;
     393             : 
     394        5602 :     res = PQexec(AH->connection, query);
     395        5602 :     if (PQresultStatus(res) != PGRES_COMMAND_OK)
     396           2 :         die_on_query_failure(AH, query);
     397        5600 :     PQclear(res);
     398        5600 : }
     399             : 
     400             : PGresult *
     401       40424 : ExecuteSqlQuery(Archive *AHX, const char *query, ExecStatusType status)
     402             : {
     403       40424 :     ArchiveHandle *AH = (ArchiveHandle *) AHX;
     404             :     PGresult   *res;
     405             : 
     406       40424 :     res = PQexec(AH->connection, query);
     407       40424 :     if (PQresultStatus(res) != status)
     408           0 :         die_on_query_failure(AH, query);
     409       40424 :     return res;
     410             : }
     411             : 
     412             : /*
     413             :  * Execute an SQL query and verify that we got exactly one row back.
     414             :  */
     415             : PGresult *
     416       11418 : ExecuteSqlQueryForSingleRow(Archive *fout, const char *query)
     417             : {
     418             :     PGresult   *res;
     419             :     int         ntups;
     420             : 
     421       11418 :     res = ExecuteSqlQuery(fout, query, PGRES_TUPLES_OK);
     422             : 
     423             :     /* Expecting a single result only */
     424       11418 :     ntups = PQntuples(res);
     425       11418 :     if (ntups != 1)
     426           0 :         fatal(ngettext("query returned %d row instead of one: %s",
     427             :                        "query returned %d rows instead of one: %s",
     428             :                        ntups),
     429             :               ntups, query);
     430             : 
     431       11418 :     return res;
     432             : }
     433             : 
     434             : /*
     435             :  * Convenience function to send a query.
     436             :  * Monitors result to detect COPY statements
     437             :  */
     438             : static void
     439        5566 : ExecuteSqlCommand(ArchiveHandle *AH, const char *qry, const char *desc)
     440             : {
     441        5566 :     PGconn     *conn = AH->connection;
     442             :     PGresult   *res;
     443             : 
     444             : #ifdef NOT_USED
     445             :     fprintf(stderr, "Executing: '%s'\n\n", qry);
     446             : #endif
     447        5566 :     res = PQexec(conn, qry);
     448             : 
     449        5566 :     switch (PQresultStatus(res))
     450             :     {
     451             :         case PGRES_COMMAND_OK:
     452             :         case PGRES_TUPLES_OK:
     453             :         case PGRES_EMPTY_QUERY:
     454             :             /* A-OK */
     455        5562 :             break;
     456             :         case PGRES_COPY_IN:
     457             :             /* Assume this is an expected result */
     458           4 :             AH->pgCopyIn = true;
     459           4 :             break;
     460             :         default:
     461             :             /* trouble */
     462           0 :             warn_or_exit_horribly(AH, "%s: %sCommand was: %s",
     463             :                                   desc, PQerrorMessage(conn), qry);
     464           0 :             break;
     465             :     }
     466             : 
     467        5566 :     PQclear(res);
     468        5566 : }
     469             : 
     470             : 
     471             : /*
     472             :  * Process non-COPY table data (that is, INSERT commands).
     473             :  *
     474             :  * The commands have been run together as one long string for compressibility,
     475             :  * and we are receiving them in bufferloads with arbitrary boundaries, so we
     476             :  * have to locate command boundaries and save partial commands across calls.
     477             :  * All state must be kept in AH->sqlparse, not in local variables of this
     478             :  * routine.  We assume that AH->sqlparse was filled with zeroes when created.
     479             :  *
     480             :  * We have to lex the data to the extent of identifying literals and quoted
     481             :  * identifiers, so that we can recognize statement-terminating semicolons.
     482             :  * We assume that INSERT data will not contain SQL comments, E'' literals,
     483             :  * or dollar-quoted strings, so this is much simpler than a full SQL lexer.
     484             :  *
     485             :  * Note: when restoring from a pre-9.0 dump file, this code is also used to
     486             :  * process BLOB COMMENTS data, which has the same problem of containing
     487             :  * multiple SQL commands that might be split across bufferloads.  Fortunately,
     488             :  * that data won't contain anything complicated to lex either.
     489             :  */
     490             : static void
     491           0 : ExecuteSimpleCommands(ArchiveHandle *AH, const char *buf, size_t bufLen)
     492             : {
     493           0 :     const char *qry = buf;
     494           0 :     const char *eos = buf + bufLen;
     495             : 
     496             :     /* initialize command buffer if first time through */
     497           0 :     if (AH->sqlparse.curCmd == NULL)
     498           0 :         AH->sqlparse.curCmd = createPQExpBuffer();
     499             : 
     500           0 :     for (; qry < eos; qry++)
     501             :     {
     502           0 :         char        ch = *qry;
     503             : 
     504             :         /* For neatness, we skip any newlines between commands */
     505           0 :         if (!(ch == '\n' && AH->sqlparse.curCmd->len == 0))
     506           0 :             appendPQExpBufferChar(AH->sqlparse.curCmd, ch);
     507             : 
     508           0 :         switch (AH->sqlparse.state)
     509             :         {
     510             :             case SQL_SCAN:      /* Default state == 0, set in _allocAH */
     511           0 :                 if (ch == ';')
     512             :                 {
     513             :                     /*
     514             :                      * We've found the end of a statement. Send it and reset
     515             :                      * the buffer.
     516             :                      */
     517           0 :                     ExecuteSqlCommand(AH, AH->sqlparse.curCmd->data,
     518             :                                       "could not execute query");
     519           0 :                     resetPQExpBuffer(AH->sqlparse.curCmd);
     520             :                 }
     521           0 :                 else if (ch == '\'')
     522             :                 {
     523           0 :                     AH->sqlparse.state = SQL_IN_SINGLE_QUOTE;
     524           0 :                     AH->sqlparse.backSlash = false;
     525             :                 }
     526           0 :                 else if (ch == '"')
     527             :                 {
     528           0 :                     AH->sqlparse.state = SQL_IN_DOUBLE_QUOTE;
     529             :                 }
     530           0 :                 break;
     531             : 
     532             :             case SQL_IN_SINGLE_QUOTE:
     533             :                 /* We needn't handle '' specially */
     534           0 :                 if (ch == '\'' && !AH->sqlparse.backSlash)
     535           0 :                     AH->sqlparse.state = SQL_SCAN;
     536           0 :                 else if (ch == '\\' && !AH->public.std_strings)
     537           0 :                     AH->sqlparse.backSlash = !AH->sqlparse.backSlash;
     538             :                 else
     539           0 :                     AH->sqlparse.backSlash = false;
     540           0 :                 break;
     541             : 
     542             :             case SQL_IN_DOUBLE_QUOTE:
     543             :                 /* We needn't handle "" specially */
     544           0 :                 if (ch == '"')
     545           0 :                     AH->sqlparse.state = SQL_SCAN;
     546           0 :                 break;
     547             :         }
     548             :     }
     549           0 : }
     550             : 
     551             : 
     552             : /*
     553             :  * Implement ahwrite() for direct-to-DB restore
     554             :  */
     555             : int
     556        5562 : ExecuteSqlCommandBuf(Archive *AHX, const char *buf, size_t bufLen)
     557             : {
     558        5562 :     ArchiveHandle *AH = (ArchiveHandle *) AHX;
     559             : 
     560        5562 :     if (AH->outputKind == OUTPUT_COPYDATA)
     561             :     {
     562             :         /*
     563             :          * COPY data.
     564             :          *
     565             :          * We drop the data on the floor if libpq has failed to enter COPY
     566             :          * mode; this allows us to behave reasonably when trying to continue
     567             :          * after an error in a COPY command.
     568             :          */
     569           8 :         if (AH->pgCopyIn &&
     570           4 :             PQputCopyData(AH->connection, buf, bufLen) <= 0)
     571           0 :             fatal("error returned by PQputCopyData: %s",
     572             :                   PQerrorMessage(AH->connection));
     573             :     }
     574        5558 :     else if (AH->outputKind == OUTPUT_OTHERDATA)
     575             :     {
     576             :         /*
     577             :          * Table data expressed as INSERT commands; or, in old dump files,
     578             :          * BLOB COMMENTS data (which is expressed as COMMENT ON commands).
     579             :          */
     580           0 :         ExecuteSimpleCommands(AH, buf, bufLen);
     581             :     }
     582             :     else
     583             :     {
     584             :         /*
     585             :          * General SQL commands; we assume that commands will not be split
     586             :          * across calls.
     587             :          *
     588             :          * In most cases the data passed to us will be a null-terminated
     589             :          * string, but if it's not, we have to add a trailing null.
     590             :          */
     591        5558 :         if (buf[bufLen] == '\0')
     592        5558 :             ExecuteSqlCommand(AH, buf, "could not execute query");
     593             :         else
     594             :         {
     595           0 :             char       *str = (char *) pg_malloc(bufLen + 1);
     596             : 
     597           0 :             memcpy(str, buf, bufLen);
     598           0 :             str[bufLen] = '\0';
     599           0 :             ExecuteSqlCommand(AH, str, "could not execute query");
     600           0 :             free(str);
     601             :         }
     602             :     }
     603             : 
     604        5562 :     return bufLen;
     605             : }
     606             : 
     607             : /*
     608             :  * Terminate a COPY operation during direct-to-DB restore
     609             :  */
     610             : void
     611           4 : EndDBCopyMode(Archive *AHX, const char *tocEntryTag)
     612             : {
     613           4 :     ArchiveHandle *AH = (ArchiveHandle *) AHX;
     614             : 
     615           4 :     if (AH->pgCopyIn)
     616             :     {
     617             :         PGresult   *res;
     618             : 
     619           4 :         if (PQputCopyEnd(AH->connection, NULL) <= 0)
     620           0 :             fatal("error returned by PQputCopyEnd: %s",
     621             :                   PQerrorMessage(AH->connection));
     622             : 
     623             :         /* Check command status and return to normal libpq state */
     624           4 :         res = PQgetResult(AH->connection);
     625           4 :         if (PQresultStatus(res) != PGRES_COMMAND_OK)
     626           0 :             warn_or_exit_horribly(AH, "COPY failed for table \"%s\": %s",
     627           0 :                                   tocEntryTag, PQerrorMessage(AH->connection));
     628           4 :         PQclear(res);
     629             : 
     630             :         /* Do this to ensure we've pumped libpq back to idle state */
     631           4 :         if (PQgetResult(AH->connection) != NULL)
     632           0 :             pg_log_warning("unexpected extra results during COPY of table \"%s\"",
     633             :                            tocEntryTag);
     634             : 
     635           4 :         AH->pgCopyIn = false;
     636             :     }
     637           4 : }
     638             : 
     639             : void
     640           4 : StartTransaction(Archive *AHX)
     641             : {
     642           4 :     ArchiveHandle *AH = (ArchiveHandle *) AHX;
     643             : 
     644           4 :     ExecuteSqlCommand(AH, "BEGIN", "could not start database transaction");
     645           4 : }
     646             : 
     647             : void
     648           4 : CommitTransaction(Archive *AHX)
     649             : {
     650           4 :     ArchiveHandle *AH = (ArchiveHandle *) AHX;
     651             : 
     652           4 :     ExecuteSqlCommand(AH, "COMMIT", "could not commit database transaction");
     653           4 : }
     654             : 
     655             : void
     656           2 : DropBlobIfExists(ArchiveHandle *AH, Oid oid)
     657             : {
     658             :     /*
     659             :      * If we are not restoring to a direct database connection, we have to
     660             :      * guess about how to detect whether the blob exists.  Assume new-style.
     661             :      */
     662           4 :     if (AH->connection == NULL ||
     663           2 :         PQserverVersion(AH->connection) >= 90000)
     664             :     {
     665           2 :         ahprintf(AH,
     666             :                  "SELECT pg_catalog.lo_unlink(oid) "
     667             :                  "FROM pg_catalog.pg_largeobject_metadata "
     668             :                  "WHERE oid = '%u';\n",
     669             :                  oid);
     670             :     }
     671             :     else
     672             :     {
     673             :         /* Restoring to pre-9.0 server, so do it the old way */
     674           0 :         ahprintf(AH,
     675             :                  "SELECT CASE WHEN EXISTS("
     676             :                  "SELECT 1 FROM pg_catalog.pg_largeobject WHERE loid = '%u'"
     677             :                  ") THEN pg_catalog.lo_unlink('%u') END;\n",
     678             :                  oid, oid);
     679             :     }
     680           2 : }

Generated by: LCOV version 1.13