LCOV - code coverage report
Current view: top level - src/bin/pg_dump - pg_backup_db.c (source / functions) Coverage Total Hit
Test: PostgreSQL 20devel Lines: 67.6 % 225 152
Test Date: 2026-07-11 03:15:41 Functions: 89.5 % 19 17
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 48.7 % 115 56

             Branch data     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 "common/connect.h"
      21                 :             : #include "common/string.h"
      22                 :             : #include "connectdb.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 void notice_processor(void *arg, const char *message);
      30                 :             : 
      31                 :             : static void
      32                 :         339 : _check_database_version(ArchiveHandle *AH)
      33                 :             : {
      34                 :             :     const char *remoteversion_str;
      35                 :             :     int         remoteversion;
      36                 :             :     PGresult   *res;
      37                 :             : 
      38                 :         339 :     remoteversion_str = PQparameterStatus(AH->connection, "server_version");
      39                 :         339 :     remoteversion = PQserverVersion(AH->connection);
      40   [ +  -  -  + ]:         339 :     if (remoteversion == 0 || !remoteversion_str)
      41                 :           0 :         pg_fatal("could not get \"server_version\" from libpq");
      42                 :             : 
      43                 :         339 :     AH->public.remoteVersionStr = pg_strdup(remoteversion_str);
      44                 :         339 :     AH->public.remoteVersion = remoteversion;
      45         [ +  + ]:         339 :     if (!AH->archiveRemoteVersion)
      46                 :         208 :         AH->archiveRemoteVersion = AH->public.remoteVersionStr;
      47                 :             : 
      48         [ -  + ]:         339 :     if (remoteversion != PG_VERSION_NUM
      49         [ #  # ]:           0 :         && (remoteversion < AH->public.minRemoteVersion ||
      50         [ #  # ]:           0 :             remoteversion > AH->public.maxRemoteVersion))
      51                 :             :     {
      52                 :           0 :         pg_log_error("aborting because of server version mismatch");
      53                 :           0 :         pg_log_error_detail("server version: %s; %s version: %s",
      54                 :             :                             remoteversion_str, progname, PG_VERSION);
      55                 :           0 :         exit(1);
      56                 :             :     }
      57                 :             : 
      58                 :             :     /*
      59                 :             :      * Check if server is in recovery mode, which means we are on a hot
      60                 :             :      * standby.
      61                 :             :      */
      62                 :         339 :     res = ExecuteSqlQueryForSingleRow((Archive *) AH,
      63                 :             :                                       "SELECT pg_catalog.pg_is_in_recovery()");
      64                 :         339 :     AH->public.isStandby = (strcmp(PQgetvalue(res, 0, 0), "t") == 0);
      65                 :         339 :     PQclear(res);
      66                 :         339 : }
      67                 :             : 
      68                 :             : /*
      69                 :             :  * Reconnect to the server.  If dbname is not NULL, use that database,
      70                 :             :  * else the one associated with the archive handle.
      71                 :             :  */
      72                 :             : void
      73                 :          65 : ReconnectToServer(ArchiveHandle *AH, const char *dbname)
      74                 :             : {
      75                 :          65 :     PGconn     *oldConn = AH->connection;
      76                 :          65 :     RestoreOptions *ropt = AH->public.ropt;
      77                 :             : 
      78                 :             :     /*
      79                 :             :      * Save the dbname, if given, in override_dbname so that it will also
      80                 :             :      * affect any later reconnection attempt.
      81                 :             :      */
      82         [ +  - ]:          65 :     if (dbname)
      83                 :          65 :         ropt->cparams.override_dbname = pg_strdup(dbname);
      84                 :             : 
      85                 :             :     /*
      86                 :             :      * Note: we want to establish the new connection, and in particular update
      87                 :             :      * ArchiveHandle's connCancel, before closing old connection.  Otherwise
      88                 :             :      * an ill-timed SIGINT could try to access a dead connection.
      89                 :             :      */
      90                 :          65 :     AH->connection = NULL;       /* dodge error check in ConnectDatabaseAhx */
      91                 :             : 
      92                 :          65 :     ConnectDatabaseAhx((Archive *) AH, &ropt->cparams, true);
      93                 :             : 
      94                 :          65 :     PQfinish(oldConn);
      95                 :          65 : }
      96                 :             : 
      97                 :             : /*
      98                 :             :  * Make, or remake, a database connection with the given parameters.
      99                 :             :  *
     100                 :             :  * The resulting connection handle is stored in AHX->connection.
     101                 :             :  *
     102                 :             :  * An interactive password prompt is automatically issued if required.
     103                 :             :  * We store the results of that in AHX->savedPassword.
     104                 :             :  * Note: it's not really all that sensible to use a single-entry password
     105                 :             :  * cache if the username keeps changing.  In current usage, however, the
     106                 :             :  * username never does change, so one savedPassword is sufficient.
     107                 :             :  */
     108                 :             : void
     109                 :         341 : ConnectDatabaseAhx(Archive *AHX,
     110                 :             :                    const ConnParams *cparams,
     111                 :             :                    bool isReconnect)
     112                 :             : {
     113                 :         341 :     ArchiveHandle *AH = (ArchiveHandle *) AHX;
     114                 :             :     trivalue    prompt_password;
     115                 :             :     char       *password;
     116                 :             : 
     117         [ -  + ]:         341 :     if (AH->connection)
     118                 :           0 :         pg_fatal("already connected to a database");
     119                 :             : 
     120                 :             :     /* Never prompt for a password during a reconnection */
     121         [ +  + ]:         341 :     prompt_password = isReconnect ? TRI_NO : cparams->promptPassword;
     122                 :             : 
     123                 :         341 :     password = AH->savedPassword;
     124                 :             : 
     125   [ -  +  -  - ]:         341 :     if (prompt_password == TRI_YES && password == NULL)
     126                 :           0 :         password = simple_prompt("Password: ", false);
     127                 :             : 
     128                 :         680 :     AH->connection = ConnectDatabase(cparams->dbname, NULL, cparams->pghost,
     129                 :         341 :                                      cparams->pgport, cparams->username,
     130                 :             :                                      prompt_password, true,
     131                 :         341 :                                      progname, NULL, NULL, password, cparams->override_dbname);
     132                 :             : 
     133                 :             :     /* Start strict; later phases may override this. */
     134                 :         339 :     PQclear(ExecuteSqlQueryForSingleRow((Archive *) AH,
     135                 :             :                                         ALWAYS_SECURE_SEARCH_PATH_SQL));
     136                 :             : 
     137   [ -  +  -  - ]:         339 :     if (password && password != AH->savedPassword)
     138                 :           0 :         free(password);
     139                 :             : 
     140                 :             :     /*
     141                 :             :      * We want to remember connection's actual password, whether or not we got
     142                 :             :      * it by prompting.  So we don't just store the password variable.
     143                 :             :      */
     144         [ -  + ]:         339 :     if (PQconnectionUsedPassword(AH->connection))
     145                 :             :     {
     146                 :           0 :         pg_free(AH->savedPassword);
     147                 :           0 :         AH->savedPassword = pg_strdup(PQpass(AH->connection));
     148                 :             :     }
     149                 :             : 
     150                 :             :     /* check for version mismatch */
     151                 :         339 :     _check_database_version(AH);
     152                 :             : 
     153                 :         339 :     PQsetNoticeProcessor(AH->connection, notice_processor, NULL);
     154                 :             : 
     155                 :             :     /* arrange for SIGINT to issue a query cancel on this connection */
     156                 :         339 :     set_archive_cancel_info(AH, AH->connection);
     157                 :         339 : }
     158                 :             : 
     159                 :             : /*
     160                 :             :  * Close the connection to the database and also cancel off the query if we
     161                 :             :  * have one running.
     162                 :             :  */
     163                 :             : void
     164                 :         276 : DisconnectDatabase(Archive *AHX)
     165                 :             : {
     166                 :         276 :     ArchiveHandle *AH = (ArchiveHandle *) AHX;
     167                 :             :     char        errbuf[1];
     168                 :             : 
     169         [ +  + ]:         276 :     if (!AH->connection)
     170                 :           2 :         return;
     171                 :             : 
     172         [ +  - ]:         274 :     if (AH->connCancel)
     173                 :             :     {
     174                 :             :         /*
     175                 :             :          * If we have an active query, send a cancel before closing, ignoring
     176                 :             :          * any errors.  This is of no use for a normal exit, but might be
     177                 :             :          * helpful during pg_fatal().
     178                 :             :          */
     179         [ -  + ]:         274 :         if (PQtransactionStatus(AH->connection) == PQTRANS_ACTIVE)
     180                 :           0 :             (void) PQcancel(AH->connCancel, errbuf, sizeof(errbuf));
     181                 :             : 
     182                 :             :         /*
     183                 :             :          * Prevent signal handler from sending a cancel after this.
     184                 :             :          */
     185                 :         274 :         set_archive_cancel_info(AH, NULL);
     186                 :             :     }
     187                 :             : 
     188                 :         274 :     PQfinish(AH->connection);
     189                 :         274 :     AH->connection = NULL;
     190                 :             : }
     191                 :             : 
     192                 :             : PGconn *
     193                 :        4991 : GetConnection(Archive *AHX)
     194                 :             : {
     195                 :        4991 :     ArchiveHandle *AH = (ArchiveHandle *) AHX;
     196                 :             : 
     197                 :        4991 :     return AH->connection;
     198                 :             : }
     199                 :             : 
     200                 :             : static void
     201                 :          10 : notice_processor(void *arg, const char *message)
     202                 :             : {
     203                 :          10 :     pg_log_info("%s", message);
     204                 :          10 : }
     205                 :             : 
     206                 :             : /* Like pg_fatal(), but with a complaint about a particular query. */
     207                 :             : static void
     208                 :           2 : die_on_query_failure(ArchiveHandle *AH, const char *query)
     209                 :             : {
     210                 :             :     if (!is_cancel_in_progress())
     211                 :             :     {
     212                 :           2 :         pg_log_error("query failed: %s",
     213                 :             :                      PQerrorMessage(AH->connection));
     214                 :           2 :         pg_log_error_detail("Query was: %s", query);
     215                 :             :     }
     216                 :             : 
     217                 :           2 :     exit_nicely(1);
     218                 :             : }
     219                 :             : 
     220                 :             : void
     221                 :        3830 : ExecuteSqlStatement(Archive *AHX, const char *query)
     222                 :             : {
     223                 :        3830 :     ArchiveHandle *AH = (ArchiveHandle *) AHX;
     224                 :             :     PGresult   *res;
     225                 :             : 
     226                 :        3830 :     res = PQexec(AH->connection, query);
     227         [ +  + ]:        3830 :     if (PQresultStatus(res) != PGRES_COMMAND_OK)
     228                 :           1 :         die_on_query_failure(AH, query);
     229                 :        3829 :     PQclear(res);
     230                 :        3829 : }
     231                 :             : 
     232                 :             : PGresult *
     233                 :       33674 : ExecuteSqlQuery(Archive *AHX, const char *query, ExecStatusType status)
     234                 :             : {
     235                 :       33674 :     ArchiveHandle *AH = (ArchiveHandle *) AHX;
     236                 :             :     PGresult   *res;
     237                 :             : 
     238                 :       33674 :     res = PQexec(AH->connection, query);
     239         [ +  + ]:       33674 :     if (PQresultStatus(res) != status)
     240                 :           1 :         die_on_query_failure(AH, query);
     241                 :       33673 :     return res;
     242                 :             : }
     243                 :             : 
     244                 :             : /*
     245                 :             :  * Execute an SQL query and verify that we got exactly one row back.
     246                 :             :  */
     247                 :             : PGresult *
     248                 :       14065 : ExecuteSqlQueryForSingleRow(Archive *fout, const char *query)
     249                 :             : {
     250                 :             :     PGresult   *res;
     251                 :             :     int         ntups;
     252                 :             : 
     253                 :       14065 :     res = ExecuteSqlQuery(fout, query, PGRES_TUPLES_OK);
     254                 :             : 
     255                 :             :     /* Expecting a single result only */
     256                 :       14065 :     ntups = PQntuples(res);
     257         [ -  + ]:       14065 :     if (ntups != 1)
     258                 :           0 :         pg_fatal(ngettext("query returned %d row instead of one: %s",
     259                 :             :                           "query returned %d rows instead of one: %s",
     260                 :             :                           ntups),
     261                 :             :                  ntups, query);
     262                 :             : 
     263                 :       14065 :     return res;
     264                 :             : }
     265                 :             : 
     266                 :             : /*
     267                 :             :  * Convenience function to send a query.
     268                 :             :  * Monitors result to detect COPY statements
     269                 :             :  */
     270                 :             : static void
     271                 :       10225 : ExecuteSqlCommand(ArchiveHandle *AH, const char *qry, const char *desc)
     272                 :             : {
     273                 :       10225 :     PGconn     *conn = AH->connection;
     274                 :             :     PGresult   *res;
     275                 :             : 
     276                 :             : #ifdef NOT_USED
     277                 :             :     fprintf(stderr, "Executing: '%s'\n\n", qry);
     278                 :             : #endif
     279                 :       10225 :     res = PQexec(conn, qry);
     280                 :             : 
     281      [ +  +  - ]:       10225 :     switch (PQresultStatus(res))
     282                 :             :     {
     283                 :       10184 :         case PGRES_COMMAND_OK:
     284                 :             :         case PGRES_TUPLES_OK:
     285                 :             :         case PGRES_EMPTY_QUERY:
     286                 :             :             /* A-OK */
     287                 :       10184 :             break;
     288                 :          41 :         case PGRES_COPY_IN:
     289                 :             :             /* Assume this is an expected result */
     290                 :          41 :             AH->pgCopyIn = true;
     291                 :          41 :             break;
     292                 :           0 :         default:
     293                 :             :             /* trouble */
     294                 :           0 :             warn_or_exit_horribly(AH, "%s: %sCommand was: %s",
     295                 :             :                                   desc, PQerrorMessage(conn), qry);
     296                 :           0 :             break;
     297                 :             :     }
     298                 :             : 
     299                 :       10225 :     PQclear(res);
     300                 :       10225 : }
     301                 :             : 
     302                 :             : 
     303                 :             : /*
     304                 :             :  * Process non-COPY table data (that is, INSERT commands).
     305                 :             :  *
     306                 :             :  * The commands have been run together as one long string for compressibility,
     307                 :             :  * and we are receiving them in bufferloads with arbitrary boundaries, so we
     308                 :             :  * have to locate command boundaries and save partial commands across calls.
     309                 :             :  * All state must be kept in AH->sqlparse, not in local variables of this
     310                 :             :  * routine.  We assume that AH->sqlparse was filled with zeroes when created.
     311                 :             :  *
     312                 :             :  * We have to lex the data to the extent of identifying literals and quoted
     313                 :             :  * identifiers, so that we can recognize statement-terminating semicolons.
     314                 :             :  * We assume that INSERT data will not contain SQL comments, E'' literals,
     315                 :             :  * or dollar-quoted strings, so this is much simpler than a full SQL lexer.
     316                 :             :  *
     317                 :             :  * Note: when restoring from a pre-9.0 dump file, this code is also used to
     318                 :             :  * process BLOB COMMENTS data, which has the same problem of containing
     319                 :             :  * multiple SQL commands that might be split across bufferloads.  Fortunately,
     320                 :             :  * that data won't contain anything complicated to lex either.
     321                 :             :  */
     322                 :             : static void
     323                 :           7 : ExecuteSimpleCommands(ArchiveHandle *AH, const char *buf, size_t bufLen)
     324                 :             : {
     325                 :           7 :     const char *qry = buf;
     326                 :           7 :     const char *eos = buf + bufLen;
     327                 :             : 
     328                 :             :     /* initialize command buffer if first time through */
     329         [ +  + ]:           7 :     if (AH->sqlparse.curCmd == NULL)
     330                 :           3 :         AH->sqlparse.curCmd = createPQExpBuffer();
     331                 :             : 
     332         [ +  + ]:      129700 :     for (; qry < eos; qry++)
     333                 :             :     {
     334                 :      129693 :         char        ch = *qry;
     335                 :             : 
     336                 :             :         /* For neatness, we skip any newlines between commands */
     337   [ +  +  -  + ]:      129693 :         if (!(ch == '\n' && AH->sqlparse.curCmd->len == 0))
     338                 :      126679 :             appendPQExpBufferChar(AH->sqlparse.curCmd, ch);
     339                 :             : 
     340   [ +  +  -  - ]:      129693 :         switch (AH->sqlparse.state)
     341                 :             :         {
     342                 :      125693 :             case SQL_SCAN:      /* Default state == 0, set in _allocAH */
     343         [ +  + ]:      125693 :                 if (ch == ';')
     344                 :             :                 {
     345                 :             :                     /*
     346                 :             :                      * We've found the end of a statement. Send it and reset
     347                 :             :                      * the buffer.
     348                 :             :                      */
     349                 :        3000 :                     ExecuteSqlCommand(AH, AH->sqlparse.curCmd->data,
     350                 :             :                                       "could not execute query");
     351                 :        3000 :                     resetPQExpBuffer(AH->sqlparse.curCmd);
     352                 :             :                 }
     353         [ +  + ]:      122693 :                 else if (ch == '\'')
     354                 :             :                 {
     355                 :        2000 :                     AH->sqlparse.state = SQL_IN_SINGLE_QUOTE;
     356                 :        2000 :                     AH->sqlparse.backSlash = false;
     357                 :             :                 }
     358         [ -  + ]:      120693 :                 else if (ch == '"')
     359                 :             :                 {
     360                 :           0 :                     AH->sqlparse.state = SQL_IN_DOUBLE_QUOTE;
     361                 :             :                 }
     362                 :      125693 :                 break;
     363                 :             : 
     364                 :        4000 :             case SQL_IN_SINGLE_QUOTE:
     365                 :             :                 /* We needn't handle '' specially */
     366   [ +  +  +  - ]:        4000 :                 if (ch == '\'' && !AH->sqlparse.backSlash)
     367                 :        2000 :                     AH->sqlparse.state = SQL_SCAN;
     368   [ -  +  -  - ]:        2000 :                 else if (ch == '\\' && !AH->public.std_strings)
     369                 :           0 :                     AH->sqlparse.backSlash = !AH->sqlparse.backSlash;
     370                 :             :                 else
     371                 :        2000 :                     AH->sqlparse.backSlash = false;
     372                 :        4000 :                 break;
     373                 :             : 
     374                 :           0 :             case SQL_IN_DOUBLE_QUOTE:
     375                 :             :                 /* We needn't handle "" specially */
     376         [ #  # ]:           0 :                 if (ch == '"')
     377                 :           0 :                     AH->sqlparse.state = SQL_SCAN;
     378                 :           0 :                 break;
     379                 :             :         }
     380                 :             :     }
     381                 :           7 : }
     382                 :             : 
     383                 :             : 
     384                 :             : /*
     385                 :             :  * Implement ahwrite() for direct-to-DB restore
     386                 :             :  */
     387                 :             : int
     388                 :        6959 : ExecuteSqlCommandBuf(Archive *AHX, const char *buf, size_t bufLen)
     389                 :             : {
     390                 :        6959 :     ArchiveHandle *AH = (ArchiveHandle *) AHX;
     391                 :             : 
     392         [ +  + ]:        6959 :     if (AH->outputKind == OUTPUT_COPYDATA)
     393                 :             :     {
     394                 :             :         /*
     395                 :             :          * COPY data.
     396                 :             :          *
     397                 :             :          * We drop the data on the floor if libpq has failed to enter COPY
     398                 :             :          * mode; this allows us to behave reasonably when trying to continue
     399                 :             :          * after an error in a COPY command.
     400                 :             :          */
     401   [ +  -  -  + ]:          82 :         if (AH->pgCopyIn &&
     402                 :          41 :             PQputCopyData(AH->connection, buf, bufLen) <= 0)
     403                 :             :         {
     404                 :             :             /* Stay quiet if this is a result of our own cancellation. */
     405                 :             :             if (!is_cancel_in_progress())
     406                 :           0 :                 pg_log_error("error returned by PQputCopyData: %s",
     407                 :             :                              PQerrorMessage(AH->connection));
     408                 :           0 :             exit_nicely(1);
     409                 :             :         }
     410                 :             :     }
     411         [ +  + ]:        6918 :     else if (AH->outputKind == OUTPUT_OTHERDATA)
     412                 :             :     {
     413                 :             :         /*
     414                 :             :          * Table data expressed as INSERT commands; or, in old dump files,
     415                 :             :          * BLOB COMMENTS data (which is expressed as COMMENT ON commands).
     416                 :             :          */
     417                 :           7 :         ExecuteSimpleCommands(AH, buf, bufLen);
     418                 :             :     }
     419                 :             :     else
     420                 :             :     {
     421                 :             :         /*
     422                 :             :          * General SQL commands; we assume that commands will not be split
     423                 :             :          * across calls.
     424                 :             :          *
     425                 :             :          * In most cases the data passed to us will be a null-terminated
     426                 :             :          * string, but if it's not, we have to add a trailing null.
     427                 :             :          */
     428         [ +  - ]:        6911 :         if (buf[bufLen] == '\0')
     429                 :        6911 :             ExecuteSqlCommand(AH, buf, "could not execute query");
     430                 :             :         else
     431                 :             :         {
     432                 :           0 :             char       *str = (char *) pg_malloc(bufLen + 1);
     433                 :             : 
     434                 :           0 :             memcpy(str, buf, bufLen);
     435                 :           0 :             str[bufLen] = '\0';
     436                 :           0 :             ExecuteSqlCommand(AH, str, "could not execute query");
     437                 :           0 :             pg_free(str);
     438                 :             :         }
     439                 :             :     }
     440                 :             : 
     441                 :        6959 :     return bufLen;
     442                 :             : }
     443                 :             : 
     444                 :             : /*
     445                 :             :  * Terminate a COPY operation during direct-to-DB restore
     446                 :             :  */
     447                 :             : void
     448                 :          41 : EndDBCopyMode(Archive *AHX, const char *tocEntryTag)
     449                 :             : {
     450                 :          41 :     ArchiveHandle *AH = (ArchiveHandle *) AHX;
     451                 :             : 
     452         [ +  - ]:          41 :     if (AH->pgCopyIn)
     453                 :             :     {
     454                 :             :         PGresult   *res;
     455                 :             : 
     456         [ -  + ]:          41 :         if (PQputCopyEnd(AH->connection, NULL) <= 0)
     457                 :             :         {
     458                 :             :             /* Stay quiet if this is a result of our own cancellation. */
     459                 :             :             if (!is_cancel_in_progress())
     460                 :           0 :                 pg_log_error("error returned by PQputCopyEnd: %s",
     461                 :             :                              PQerrorMessage(AH->connection));
     462                 :           0 :             exit_nicely(1);
     463                 :             :         }
     464                 :             : 
     465                 :             :         /* Check command status and return to normal libpq state */
     466                 :          41 :         res = PQgetResult(AH->connection);
     467         [ -  + ]:          41 :         if (PQresultStatus(res) != PGRES_COMMAND_OK)
     468                 :           0 :             warn_or_exit_horribly(AH, "COPY failed for table \"%s\": %s",
     469                 :           0 :                                   tocEntryTag, PQerrorMessage(AH->connection));
     470                 :          41 :         PQclear(res);
     471                 :             : 
     472                 :             :         /* Do this to ensure we've pumped libpq back to idle state */
     473         [ -  + ]:          41 :         if (PQgetResult(AH->connection) != NULL)
     474                 :           0 :             pg_log_warning("unexpected extra results during COPY of table \"%s\"",
     475                 :             :                            tocEntryTag);
     476                 :             : 
     477                 :          41 :         AH->pgCopyIn = false;
     478                 :             :     }
     479                 :          41 : }
     480                 :             : 
     481                 :             : void
     482                 :         157 : StartTransaction(Archive *AHX)
     483                 :             : {
     484                 :         157 :     ArchiveHandle *AH = (ArchiveHandle *) AHX;
     485                 :             : 
     486                 :         157 :     ExecuteSqlCommand(AH, "BEGIN", "could not start database transaction");
     487                 :         157 : }
     488                 :             : 
     489                 :             : void
     490                 :         157 : CommitTransaction(Archive *AHX)
     491                 :             : {
     492                 :         157 :     ArchiveHandle *AH = (ArchiveHandle *) AHX;
     493                 :             : 
     494                 :         157 :     ExecuteSqlCommand(AH, "COMMIT", "could not commit database transaction");
     495                 :         157 : }
     496                 :             : 
     497                 :             : /*
     498                 :             :  * Issue per-blob commands for the large object(s) listed in the TocEntry
     499                 :             :  *
     500                 :             :  * The TocEntry's defn string is assumed to consist of large object OIDs,
     501                 :             :  * one per line.  Wrap these in the given SQL command fragments and issue
     502                 :             :  * the commands.  (cmdEnd need not include a semicolon.)
     503                 :             :  */
     504                 :             : void
     505                 :         150 : IssueCommandPerBlob(ArchiveHandle *AH, TocEntry *te,
     506                 :             :                     const char *cmdBegin, const char *cmdEnd)
     507                 :             : {
     508                 :             :     /* Make a writable copy of the command string */
     509                 :         150 :     char       *buf = pg_strdup(te->defn);
     510                 :         150 :     RestoreOptions *ropt = AH->public.ropt;
     511                 :             :     char       *st;
     512                 :             :     char       *en;
     513                 :             : 
     514                 :         150 :     st = buf;
     515         [ +  + ]:         316 :     while ((en = strchr(st, '\n')) != NULL)
     516                 :             :     {
     517                 :         166 :         *en++ = '\0';
     518                 :         166 :         ahprintf(AH, "%s%s%s;\n", cmdBegin, st, cmdEnd);
     519                 :             : 
     520                 :             :         /* In --transaction-size mode, count each command as an action */
     521   [ +  -  -  + ]:         166 :         if (ropt && ropt->txn_size > 0)
     522                 :             :         {
     523         [ #  # ]:           0 :             if (++AH->txnCount >= ropt->txn_size)
     524                 :             :             {
     525         [ #  # ]:           0 :                 if (AH->connection)
     526                 :             :                 {
     527                 :           0 :                     CommitTransaction(&AH->public);
     528                 :           0 :                     StartTransaction(&AH->public);
     529                 :             :                 }
     530                 :             :                 else
     531                 :           0 :                     ahprintf(AH, "COMMIT;\nBEGIN;\n\n");
     532                 :           0 :                 AH->txnCount = 0;
     533                 :             :             }
     534                 :             :         }
     535                 :             : 
     536                 :         166 :         st = en;
     537                 :             :     }
     538                 :         150 :     ahprintf(AH, "\n");
     539                 :         150 :     pg_free(buf);
     540                 :         150 : }
     541                 :             : 
     542                 :             : /*
     543                 :             :  * Process a "LARGE OBJECTS" ACL TocEntry.
     544                 :             :  *
     545                 :             :  * To save space in the dump file, the TocEntry contains only one copy
     546                 :             :  * of the required GRANT/REVOKE commands, written to apply to the first
     547                 :             :  * blob in the group (although we do not depend on that detail here).
     548                 :             :  * We must expand the text to generate commands for all the blobs listed
     549                 :             :  * in the associated BLOB METADATA entry.
     550                 :             :  */
     551                 :             : void
     552                 :           0 : IssueACLPerBlob(ArchiveHandle *AH, TocEntry *te)
     553                 :             : {
     554                 :           0 :     TocEntry   *blobte = getTocEntryByDumpId(AH, te->dependencies[0]);
     555                 :             :     char       *buf;
     556                 :             :     char       *st;
     557                 :             :     char       *st2;
     558                 :             :     char       *en;
     559                 :             :     bool        inquotes;
     560                 :             : 
     561         [ #  # ]:           0 :     if (!blobte)
     562                 :           0 :         pg_fatal("could not find entry for ID %d", te->dependencies[0]);
     563                 :             :     Assert(strcmp(blobte->desc, "BLOB METADATA") == 0);
     564                 :             : 
     565                 :             :     /* Make a writable copy of the ACL commands string */
     566                 :           0 :     buf = pg_strdup(te->defn);
     567                 :             : 
     568                 :             :     /*
     569                 :             :      * We have to parse out the commands sufficiently to locate the blob OIDs
     570                 :             :      * and find the command-ending semicolons.  The commands should not
     571                 :             :      * contain anything hard to parse except for double-quoted role names,
     572                 :             :      * which are easy to ignore.  Once we've split apart the first and second
     573                 :             :      * halves of a command, apply IssueCommandPerBlob.  (This means the
     574                 :             :      * updates on the blobs are interleaved if there's multiple commands, but
     575                 :             :      * that should cause no trouble.)
     576                 :             :      */
     577                 :           0 :     inquotes = false;
     578                 :           0 :     st = en = buf;
     579                 :           0 :     st2 = NULL;
     580         [ #  # ]:           0 :     while (*en)
     581                 :             :     {
     582                 :             :         /* Ignore double-quoted material */
     583         [ #  # ]:           0 :         if (*en == '"')
     584                 :           0 :             inquotes = !inquotes;
     585         [ #  # ]:           0 :         if (inquotes)
     586                 :             :         {
     587                 :           0 :             en++;
     588                 :           0 :             continue;
     589                 :             :         }
     590                 :             :         /* If we found "LARGE OBJECT", that's the end of the first half */
     591         [ #  # ]:           0 :         if (strncmp(en, "LARGE OBJECT ", 13) == 0)
     592                 :             :         {
     593                 :             :             /* Terminate the first-half string */
     594                 :           0 :             en += 13;
     595                 :             :             Assert(isdigit((unsigned char) *en));
     596                 :           0 :             *en++ = '\0';
     597                 :             :             /* Skip the rest of the blob OID */
     598         [ #  # ]:           0 :             while (isdigit((unsigned char) *en))
     599                 :           0 :                 en++;
     600                 :             :             /* Second half starts here */
     601                 :             :             Assert(st2 == NULL);
     602                 :           0 :             st2 = en;
     603                 :             :         }
     604                 :             :         /* If we found semicolon, that's the end of the second half */
     605         [ #  # ]:           0 :         else if (*en == ';')
     606                 :             :         {
     607                 :             :             /* Terminate the second-half string */
     608                 :           0 :             *en++ = '\0';
     609                 :             :             Assert(st2 != NULL);
     610                 :             :             /* Issue this command for each blob */
     611                 :           0 :             IssueCommandPerBlob(AH, blobte, st, st2);
     612                 :             :             /* For neatness, skip whitespace before the next command */
     613         [ #  # ]:           0 :             while (isspace((unsigned char) *en))
     614                 :           0 :                 en++;
     615                 :             :             /* Reset for new command */
     616                 :           0 :             st = en;
     617                 :           0 :             st2 = NULL;
     618                 :             :         }
     619                 :             :         else
     620                 :           0 :             en++;
     621                 :             :     }
     622                 :           0 :     pg_free(buf);
     623                 :           0 : }
     624                 :             : 
     625                 :             : void
     626                 :           0 : DropLOIfExists(ArchiveHandle *AH, Oid oid)
     627                 :             : {
     628                 :           0 :     ahprintf(AH,
     629                 :             :              "SELECT pg_catalog.lo_unlink(oid) "
     630                 :             :              "FROM pg_catalog.pg_largeobject_metadata "
     631                 :             :              "WHERE oid = '%u';\n",
     632                 :             :              oid);
     633                 :           0 : }
        

Generated by: LCOV version 2.0-1