LCOV - code coverage report
Current view: top level - src/bin/pg_basebackup - pg_recvlogical.c (source / functions) Coverage Total Hit
Test: PostgreSQL 20devel Lines: 78.7 % 488 384
Test Date: 2026-07-26 12:15:36 Functions: 90.0 % 10 9
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 72.6 % 292 212

             Branch data     Line data    Source code
       1                 :             : /*-------------------------------------------------------------------------
       2                 :             :  *
       3                 :             :  * pg_recvlogical.c - receive data from a logical decoding slot in a streaming
       4                 :             :  *                    fashion and write it to a local file.
       5                 :             :  *
       6                 :             :  * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
       7                 :             :  *
       8                 :             :  * IDENTIFICATION
       9                 :             :  *        src/bin/pg_basebackup/pg_recvlogical.c
      10                 :             :  *-------------------------------------------------------------------------
      11                 :             :  */
      12                 :             : 
      13                 :             : #include "postgres_fe.h"
      14                 :             : 
      15                 :             : #include <dirent.h>
      16                 :             : #include <limits.h>
      17                 :             : #include <sys/select.h>
      18                 :             : #include <sys/stat.h>
      19                 :             : #include <unistd.h>
      20                 :             : 
      21                 :             : #include "common/file_perm.h"
      22                 :             : #include "common/logging.h"
      23                 :             : #include "fe_utils/option_utils.h"
      24                 :             : #include "getopt_long.h"
      25                 :             : #include "libpq-fe.h"
      26                 :             : #include "libpq/pqsignal.h"
      27                 :             : #include "libpq/protocol.h"
      28                 :             : #include "pqexpbuffer.h"
      29                 :             : #include "streamutil.h"
      30                 :             : 
      31                 :             : /* Time to sleep between reconnection attempts */
      32                 :             : #define RECONNECT_SLEEP_TIME 5
      33                 :             : 
      34                 :             : typedef enum
      35                 :             : {
      36                 :             :     STREAM_STOP_NONE,
      37                 :             :     STREAM_STOP_END_OF_WAL,
      38                 :             :     STREAM_STOP_KEEPALIVE,
      39                 :             :     STREAM_STOP_SIGNAL
      40                 :             : } StreamStopReason;
      41                 :             : 
      42                 :             : /* Global Options */
      43                 :             : static char *outfile = NULL;
      44                 :             : static int  verbose = 0;
      45                 :             : static bool two_phase = false;  /* enable-two-phase option */
      46                 :             : static bool failover = false;   /* enable-failover option */
      47                 :             : static int  noloop = 0;
      48                 :             : static int  standby_message_timeout = 10 * 1000;    /* 10 sec = default */
      49                 :             : static int  fsync_interval = 10 * 1000; /* 10 sec = default */
      50                 :             : static XLogRecPtr startpos = InvalidXLogRecPtr;
      51                 :             : static XLogRecPtr endpos = InvalidXLogRecPtr;
      52                 :             : static bool do_create_slot = false;
      53                 :             : static bool slot_exists_ok = false;
      54                 :             : static bool do_start_slot = false;
      55                 :             : static bool do_drop_slot = false;
      56                 :             : static char *replication_slot = NULL;
      57                 :             : 
      58                 :             : /* filled pairwise with option, value. value may be NULL */
      59                 :             : static char **options;
      60                 :             : static size_t noptions = 0;
      61                 :             : static const char *plugin = "test_decoding";
      62                 :             : 
      63                 :             : /* Global State */
      64                 :             : static int  outfd = -1;
      65                 :             : static volatile sig_atomic_t time_to_abort = false;
      66                 :             : static volatile sig_atomic_t stop_reason = STREAM_STOP_NONE;
      67                 :             : static volatile sig_atomic_t output_reopen = false;
      68                 :             : static bool output_isfile;
      69                 :             : static TimestampTz output_last_fsync = -1;
      70                 :             : static bool output_needs_fsync = false;
      71                 :             : static XLogRecPtr output_written_lsn = InvalidXLogRecPtr;
      72                 :             : static XLogRecPtr output_fsync_lsn = InvalidXLogRecPtr;
      73                 :             : 
      74                 :             : static void usage(void);
      75                 :             : static void StreamLogicalLog(void);
      76                 :             : static bool flushAndSendFeedback(PGconn *conn, TimestampTz *now);
      77                 :             : static void prepareToTerminate(PGconn *conn, XLogRecPtr endpos,
      78                 :             :                                StreamStopReason reason,
      79                 :             :                                XLogRecPtr lsn);
      80                 :             : 
      81                 :             : static void
      82                 :           1 : usage(void)
      83                 :             : {
      84                 :           1 :     printf(_("%s controls PostgreSQL logical decoding streams.\n\n"),
      85                 :             :            progname);
      86                 :           1 :     printf(_("Usage:\n"));
      87                 :           1 :     printf(_("  %s [OPTION]...\n"), progname);
      88                 :           1 :     printf(_("\nAction to be performed:\n"));
      89                 :           1 :     printf(_("      --create-slot      create a new replication slot (for the slot's name see --slot)\n"));
      90                 :           1 :     printf(_("      --drop-slot        drop the replication slot (for the slot's name see --slot)\n"));
      91                 :           1 :     printf(_("      --start            start streaming in a replication slot (for the slot's name see --slot)\n"));
      92                 :           1 :     printf(_("\nOptions:\n"));
      93                 :           1 :     printf(_("      --enable-failover  enable replication slot synchronization to standby servers when\n"
      94                 :             :              "                         creating a replication slot\n"));
      95                 :           1 :     printf(_("  -E, --endpos=LSN       exit after receiving the specified LSN\n"));
      96                 :           1 :     printf(_("  -f, --file=FILE        receive log into this file, - for stdout\n"));
      97                 :           1 :     printf(_("  -F  --fsync-interval=SECS\n"
      98                 :             :              "                         time between fsyncs to the output file (default: %d)\n"), (fsync_interval / 1000));
      99                 :           1 :     printf(_("      --if-not-exists    do not error if slot already exists when creating a slot\n"));
     100                 :           1 :     printf(_("  -I, --startpos=LSN     where in an existing slot should the streaming start\n"));
     101                 :           1 :     printf(_("  -n, --no-loop          do not loop on connection lost\n"));
     102                 :           1 :     printf(_("  -o, --option=NAME[=VALUE]\n"
     103                 :             :              "                         pass option NAME with optional value VALUE to the\n"
     104                 :             :              "                         output plugin\n"));
     105                 :           1 :     printf(_("  -P, --plugin=PLUGIN    use output plugin PLUGIN (default: %s)\n"), plugin);
     106                 :           1 :     printf(_("  -s, --status-interval=SECS\n"
     107                 :             :              "                         time between status packets sent to server (default: %d)\n"), (standby_message_timeout / 1000));
     108                 :           1 :     printf(_("  -S, --slot=SLOTNAME    name of the logical replication slot\n"));
     109                 :           1 :     printf(_("  -t, --enable-two-phase enable decoding of prepared transactions when creating a slot\n"));
     110                 :           1 :     printf(_("      --two-phase        (same as --enable-two-phase, deprecated)\n"));
     111                 :           1 :     printf(_("  -v, --verbose          output verbose messages\n"));
     112                 :           1 :     printf(_("  -V, --version          output version information, then exit\n"));
     113                 :           1 :     printf(_("  -?, --help             show this help, then exit\n"));
     114                 :           1 :     printf(_("\nConnection options:\n"));
     115                 :           1 :     printf(_("  -d, --dbname=DBNAME    database to connect to\n"));
     116                 :           1 :     printf(_("  -h, --host=HOSTNAME    database server host or socket directory\n"));
     117                 :           1 :     printf(_("  -p, --port=PORT        database server port number\n"));
     118                 :           1 :     printf(_("  -U, --username=NAME    connect as specified database user\n"));
     119                 :           1 :     printf(_("  -w, --no-password      never prompt for password\n"));
     120                 :           1 :     printf(_("  -W, --password         force password prompt (should happen automatically)\n"));
     121                 :           1 :     printf(_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
     122                 :           1 :     printf(_("%s home page: <%s>\n"), PACKAGE_NAME, PACKAGE_URL);
     123                 :           1 : }
     124                 :             : 
     125                 :             : /*
     126                 :             :  * Send a Standby Status Update message to server.
     127                 :             :  */
     128                 :             : static bool
     129                 :          36 : sendFeedback(PGconn *conn, TimestampTz now, bool force, bool replyRequested)
     130                 :             : {
     131                 :             :     static XLogRecPtr last_written_lsn = InvalidXLogRecPtr;
     132                 :             :     static XLogRecPtr last_fsync_lsn = InvalidXLogRecPtr;
     133                 :             : 
     134                 :             :     char        replybuf[1 + 8 + 8 + 8 + 8 + 1];
     135                 :          36 :     int         len = 0;
     136                 :             : 
     137                 :             :     /*
     138                 :             :      * we normally don't want to send superfluous feedback, but if it's
     139                 :             :      * because of a timeout we need to, otherwise wal_sender_timeout will kill
     140                 :             :      * us.
     141                 :             :      */
     142         [ -  + ]:          36 :     if (!force &&
     143         [ #  # ]:           0 :         last_written_lsn == output_written_lsn &&
     144         [ #  # ]:           0 :         last_fsync_lsn == output_fsync_lsn)
     145                 :           0 :         return true;
     146                 :             : 
     147         [ +  + ]:          36 :     if (verbose)
     148                 :           3 :         pg_log_info("confirming write up to %X/%08X, flush to %X/%08X (slot %s)",
     149                 :             :                     LSN_FORMAT_ARGS(output_written_lsn),
     150                 :             :                     LSN_FORMAT_ARGS(output_fsync_lsn),
     151                 :             :                     replication_slot);
     152                 :             : 
     153                 :          36 :     replybuf[len] = PqReplMsg_StandbyStatusUpdate;
     154                 :          36 :     len += 1;
     155                 :          36 :     fe_sendint64(output_written_lsn, &replybuf[len]);   /* write */
     156                 :          36 :     len += 8;
     157                 :          36 :     fe_sendint64(output_fsync_lsn, &replybuf[len]); /* flush */
     158                 :          36 :     len += 8;
     159                 :          36 :     fe_sendint64(InvalidXLogRecPtr, &replybuf[len]);    /* apply */
     160                 :          36 :     len += 8;
     161                 :          36 :     fe_sendint64(now, &replybuf[len]);  /* sendTime */
     162                 :          36 :     len += 8;
     163                 :          36 :     replybuf[len] = replyRequested ? 1 : 0; /* replyRequested */
     164                 :          36 :     len += 1;
     165                 :             : 
     166                 :          36 :     startpos = output_written_lsn;
     167                 :          36 :     last_written_lsn = output_written_lsn;
     168                 :          36 :     last_fsync_lsn = output_fsync_lsn;
     169                 :             : 
     170   [ +  -  -  + ]:          36 :     if (PQputCopyData(conn, replybuf, len) <= 0 || PQflush(conn))
     171                 :             :     {
     172                 :           0 :         pg_log_error("could not send feedback packet: %s",
     173                 :             :                      PQerrorMessage(conn));
     174                 :           0 :         return false;
     175                 :             :     }
     176                 :             : 
     177                 :          36 :     return true;
     178                 :             : }
     179                 :             : 
     180                 :             : static void
     181                 :          64 : disconnect_atexit(void)
     182                 :             : {
     183         [ +  + ]:          64 :     if (conn != NULL)
     184                 :          36 :         PQfinish(conn);
     185                 :          64 : }
     186                 :             : 
     187                 :             : static void
     188                 :          48 : OutputFsync(TimestampTz now)
     189                 :             : {
     190                 :          48 :     output_last_fsync = now;
     191                 :             : 
     192                 :          48 :     output_fsync_lsn = output_written_lsn;
     193                 :             : 
     194                 :             :     /*
     195                 :             :      * Save the last flushed position as the replication start point. On
     196                 :             :      * reconnect, replication resumes from there to avoid re-sending flushed
     197                 :             :      * data.
     198                 :             :      */
     199                 :          48 :     startpos = output_fsync_lsn;
     200                 :             : 
     201         [ -  + ]:          48 :     if (fsync_interval <= 0)
     202                 :           0 :         return;
     203                 :             : 
     204         [ +  + ]:          48 :     if (!output_needs_fsync)
     205                 :          33 :         return;
     206                 :             : 
     207                 :          15 :     output_needs_fsync = false;
     208                 :             : 
     209                 :             :     /* can only fsync if it's a regular file */
     210         [ +  + ]:          15 :     if (!output_isfile)
     211                 :          11 :         return;
     212                 :             : 
     213         [ -  + ]:           4 :     if (fsync(outfd) != 0)
     214                 :           0 :         pg_fatal("could not fsync file \"%s\": %m", outfile);
     215                 :             : }
     216                 :             : 
     217                 :             : /*
     218                 :             :  * Start the log streaming
     219                 :             :  */
     220                 :             : static void
     221                 :          29 : StreamLogicalLog(void)
     222                 :             : {
     223                 :             :     PGresult   *res;
     224                 :          29 :     char       *copybuf = NULL;
     225                 :          29 :     TimestampTz last_status = -1;
     226                 :             :     PQExpBuffer query;
     227                 :             :     XLogRecPtr  cur_record_lsn;
     228                 :             : 
     229                 :          29 :     cur_record_lsn = InvalidXLogRecPtr;
     230                 :             : 
     231                 :             :     /*
     232                 :             :      * Connect in replication mode to the server
     233                 :             :      */
     234         [ +  + ]:          29 :     if (!conn)
     235                 :           1 :         conn = GetConnection();
     236         [ -  + ]:          29 :     if (!conn)
     237                 :             :         /* Error message already written in GetConnection() */
     238                 :           0 :         return;
     239                 :             : 
     240                 :             :     /*
     241                 :             :      * Start the replication
     242                 :             :      */
     243         [ +  + ]:          29 :     if (verbose)
     244                 :           2 :         pg_log_info("starting log streaming at %X/%08X (slot %s)",
     245                 :             :                     LSN_FORMAT_ARGS(startpos),
     246                 :             :                     replication_slot);
     247                 :             : 
     248                 :             :     /* Initiate the replication stream at specified location */
     249                 :          29 :     query = createPQExpBuffer();
     250                 :          29 :     appendPQExpBufferStr(query, "START_REPLICATION SLOT ");
     251                 :          29 :     AppendQuotedIdentifier(query, replication_slot);
     252                 :          29 :     appendPQExpBuffer(query, " LOGICAL %X/%08X", LSN_FORMAT_ARGS(startpos));
     253                 :             : 
     254                 :             :     /* print options if there are any */
     255         [ +  + ]:          29 :     if (noptions)
     256                 :          21 :         appendPQExpBufferStr(query, " (");
     257                 :             : 
     258         [ +  + ]:          71 :     for (size_t i = 0; i < noptions; i++)
     259                 :             :     {
     260                 :             :         /* separator */
     261         [ +  + ]:          42 :         if (i > 0)
     262                 :          21 :             appendPQExpBufferStr(query, ", ");
     263                 :             : 
     264                 :             :         /* write option name */
     265                 :          42 :         AppendQuotedIdentifier(query, options[i * 2]);
     266                 :             : 
     267                 :             :         /* write option value if specified */
     268         [ +  - ]:          42 :         if (options[i * 2 + 1] != NULL)
     269                 :             :         {
     270                 :          42 :             appendPQExpBufferChar(query, ' ');
     271                 :          42 :             AppendQuotedLiteral(query, options[i * 2 + 1]);
     272                 :             :         }
     273                 :             :     }
     274                 :             : 
     275         [ +  + ]:          29 :     if (noptions)
     276                 :          21 :         appendPQExpBufferChar(query, ')');
     277                 :             : 
     278                 :          29 :     res = PQexec(conn, query->data);
     279         [ +  + ]:          29 :     if (PQresultStatus(res) != PGRES_COPY_BOTH)
     280                 :             :     {
     281                 :           6 :         pg_log_error("could not send replication command \"%s\": %s",
     282                 :             :                      query->data, PQresultErrorMessage(res));
     283                 :           6 :         PQclear(res);
     284                 :           6 :         goto error;
     285                 :             :     }
     286                 :          23 :     PQclear(res);
     287                 :          23 :     resetPQExpBuffer(query);
     288                 :             : 
     289         [ +  + ]:          23 :     if (verbose)
     290                 :           2 :         pg_log_info("streaming initiated");
     291                 :             : 
     292         [ +  + ]:         413 :     while (!time_to_abort)
     293                 :             :     {
     294                 :             :         int         r;
     295                 :             :         size_t      bytes_left;
     296                 :             :         size_t      bytes_written;
     297                 :             :         TimestampTz now;
     298                 :             :         size_t      hdr_len;
     299                 :             : 
     300                 :         409 :         cur_record_lsn = InvalidXLogRecPtr;
     301                 :             : 
     302         [ +  + ]:         409 :         if (copybuf != NULL)
     303                 :             :         {
     304                 :         305 :             PQfreemem(copybuf);
     305                 :         305 :             copybuf = NULL;
     306                 :             :         }
     307                 :             : 
     308                 :             :         /*
     309                 :             :          * Potentially send a status message to the primary.
     310                 :             :          */
     311                 :         409 :         now = feGetCurrentTimestamp();
     312                 :             : 
     313   [ +  +  +  + ]:         796 :         if (outfd != -1 &&
     314                 :         387 :             feTimestampDifferenceExceeds(output_last_fsync, now,
     315                 :             :                                          fsync_interval))
     316                 :          21 :             OutputFsync(now);
     317                 :             : 
     318   [ +  -  +  + ]:         818 :         if (standby_message_timeout > 0 &&
     319                 :         409 :             feTimestampDifferenceExceeds(last_status, now,
     320                 :             :                                          standby_message_timeout))
     321                 :             :         {
     322                 :             :             /* Time to send feedback! */
     323         [ -  + ]:          23 :             if (!sendFeedback(conn, now, true, false))
     324                 :           3 :                 goto error;
     325                 :             : 
     326                 :          23 :             last_status = now;
     327                 :             :         }
     328                 :             : 
     329                 :             :         /* got SIGHUP, close output file */
     330   [ +  +  -  +  :         409 :         if (outfd != -1 && output_reopen && strcmp(outfile, "-") != 0)
                   -  - ]
     331                 :             :         {
     332                 :           0 :             now = feGetCurrentTimestamp();
     333                 :           0 :             OutputFsync(now);
     334                 :           0 :             close(outfd);
     335                 :           0 :             outfd = -1;
     336                 :             :         }
     337                 :         409 :         output_reopen = false;
     338                 :             : 
     339                 :             :         /* open the output file, if not open yet */
     340         [ +  + ]:         409 :         if (outfd == -1)
     341                 :             :         {
     342                 :             :             struct stat statbuf;
     343                 :             : 
     344         [ +  + ]:          22 :             if (strcmp(outfile, "-") == 0)
     345                 :          18 :                 outfd = fileno(stdout);
     346                 :             :             else
     347                 :           4 :                 outfd = open(outfile, O_CREAT | O_APPEND | O_WRONLY | PG_BINARY,
     348                 :             :                              pg_file_create_mode);
     349         [ -  + ]:          22 :             if (outfd == -1)
     350                 :             :             {
     351                 :           0 :                 pg_log_error("could not open log file \"%s\": %m", outfile);
     352                 :           0 :                 goto error;
     353                 :             :             }
     354                 :             : 
     355         [ -  + ]:          22 :             if (fstat(outfd, &statbuf) != 0)
     356                 :             :             {
     357                 :           0 :                 pg_log_error("could not stat file \"%s\": %m", outfile);
     358                 :           0 :                 goto error;
     359                 :             :             }
     360                 :             : 
     361   [ +  +  +  - ]:          22 :             output_isfile = S_ISREG(statbuf.st_mode) && !isatty(outfd);
     362                 :             :         }
     363                 :             : 
     364                 :         409 :         r = PQgetCopyData(conn, &copybuf, 1);
     365         [ +  + ]:         409 :         if (r == 0)
     366                 :          81 :         {
     367                 :             :             /*
     368                 :             :              * In async mode, and no data available. We block on reading but
     369                 :             :              * not more than the specified timeout, so that we can send a
     370                 :             :              * response back to the client.
     371                 :             :              */
     372                 :             :             fd_set      input_mask;
     373                 :          88 :             TimestampTz message_target = 0;
     374                 :          88 :             TimestampTz fsync_target = 0;
     375                 :             :             struct timeval timeout;
     376                 :          88 :             struct timeval *timeoutptr = NULL;
     377                 :             : 
     378         [ -  + ]:          88 :             if (PQsocket(conn) < 0)
     379                 :             :             {
     380                 :           0 :                 pg_log_error("invalid socket: %s", PQerrorMessage(conn));
     381                 :           3 :                 goto error;
     382                 :             :             }
     383                 :             : 
     384         [ +  + ]:        1496 :             FD_ZERO(&input_mask);
     385                 :          88 :             FD_SET(PQsocket(conn), &input_mask);
     386                 :             : 
     387                 :             :             /* Compute when we need to wakeup to send a keepalive message. */
     388         [ +  - ]:          88 :             if (standby_message_timeout)
     389                 :          88 :                 message_target = last_status + (standby_message_timeout - 1) *
     390                 :             :                     ((int64) 1000);
     391                 :             : 
     392                 :             :             /* Compute when we need to wakeup to fsync the output file. */
     393   [ +  -  +  + ]:          88 :             if (fsync_interval > 0 && output_needs_fsync)
     394                 :          45 :                 fsync_target = output_last_fsync + (fsync_interval - 1) *
     395                 :             :                     ((int64) 1000);
     396                 :             : 
     397                 :             :             /* Now compute when to wakeup. */
     398   [ -  +  -  - ]:          88 :             if (message_target > 0 || fsync_target > 0)
     399                 :             :             {
     400                 :             :                 TimestampTz targettime;
     401                 :             :                 long        secs;
     402                 :             :                 int         usecs;
     403                 :             : 
     404                 :          88 :                 targettime = message_target;
     405                 :             : 
     406   [ +  +  +  + ]:          88 :                 if (fsync_target > 0 && fsync_target < targettime)
     407                 :           6 :                     targettime = fsync_target;
     408                 :             : 
     409                 :          88 :                 feTimestampDifference(now,
     410                 :             :                                       targettime,
     411                 :             :                                       &secs,
     412                 :             :                                       &usecs);
     413         [ +  + ]:          88 :                 if (secs <= 0)
     414                 :           6 :                     timeout.tv_sec = 1; /* Always sleep at least 1 sec */
     415                 :             :                 else
     416                 :          82 :                     timeout.tv_sec = secs;
     417                 :          88 :                 timeout.tv_usec = usecs;
     418                 :          88 :                 timeoutptr = &timeout;
     419                 :             :             }
     420                 :             : 
     421                 :          88 :             r = select(PQsocket(conn) + 1, &input_mask, NULL, NULL, timeoutptr);
     422   [ +  -  +  +  :          88 :             if (r == 0 || (r < 0 && errno == EINTR))
                   +  - ]
     423                 :             :             {
     424                 :             :                 /*
     425                 :             :                  * Got a timeout or signal. Continue the loop and either
     426                 :             :                  * deliver a status packet to the server or just go back into
     427                 :             :                  * blocking.
     428                 :             :                  */
     429                 :          85 :                 continue;
     430                 :             :             }
     431         [ -  + ]:          84 :             else if (r < 0)
     432                 :             :             {
     433                 :           0 :                 pg_log_error("%s() failed: %m", "select");
     434                 :           0 :                 goto error;
     435                 :             :             }
     436                 :             : 
     437                 :             :             /* Else there is actually data on the socket */
     438         [ +  + ]:          84 :             if (PQconsumeInput(conn) == 0)
     439                 :             :             {
     440                 :           3 :                 pg_log_error("could not receive data from WAL stream: %s",
     441                 :             :                              PQerrorMessage(conn));
     442                 :           3 :                 goto error;
     443                 :             :             }
     444                 :          81 :             continue;
     445                 :             :         }
     446                 :             : 
     447                 :             :         /* End of copy stream */
     448         [ +  + ]:         321 :         if (r == -1)
     449                 :          16 :             break;
     450                 :             : 
     451                 :             :         /* Failure while reading the copy stream */
     452         [ -  + ]:         313 :         if (r == -2)
     453                 :             :         {
     454                 :           0 :             pg_log_error("could not read COPY data: %s",
     455                 :             :                          PQerrorMessage(conn));
     456                 :           0 :             goto error;
     457                 :             :         }
     458                 :             : 
     459                 :             :         /* Check the message type. */
     460         [ +  + ]:         313 :         if (copybuf[0] == PqReplMsg_Keepalive)
     461                 :         189 :         {
     462                 :             :             int         pos;
     463                 :             :             bool        replyRequested;
     464                 :             :             XLogRecPtr  walEnd;
     465                 :         192 :             bool        endposReached = false;
     466                 :             : 
     467                 :             :             /*
     468                 :             :              * Parse the keepalive message, enclosed in the CopyData message.
     469                 :             :              * We just check if the server requested a reply, and ignore the
     470                 :             :              * rest.
     471                 :             :              */
     472                 :         192 :             pos = 1;            /* skip msgtype PqReplMsg_Keepalive */
     473                 :         192 :             walEnd = fe_recvint64(&copybuf[pos]);
     474                 :         192 :             output_written_lsn = Max(walEnd, output_written_lsn);
     475                 :             : 
     476                 :         192 :             pos += 8;           /* read walEnd */
     477                 :             : 
     478                 :         192 :             pos += 8;           /* skip sendTime */
     479                 :             : 
     480         [ -  + ]:         192 :             if (r < pos + 1)
     481                 :             :             {
     482                 :           0 :                 pg_log_error("streaming header too small: %d", r);
     483                 :           0 :                 goto error;
     484                 :             :             }
     485                 :         192 :             replyRequested = copybuf[pos];
     486                 :             : 
     487   [ +  +  +  + ]:         192 :             if (XLogRecPtrIsValid(endpos) && walEnd >= endpos)
     488                 :             :             {
     489                 :             :                 /*
     490                 :             :                  * If there's nothing to read on the socket until a keepalive
     491                 :             :                  * we know that the server has nothing to send us; and if
     492                 :             :                  * walEnd has passed endpos, we know nothing else can have
     493                 :             :                  * committed before endpos.  So we can bail out now.
     494                 :             :                  */
     495                 :           3 :                 endposReached = true;
     496                 :             :             }
     497                 :             : 
     498                 :             :             /* Send a reply, if necessary */
     499   [ +  +  +  + ]:         192 :             if (replyRequested || endposReached)
     500                 :             :             {
     501         [ -  + ]:           4 :                 if (!flushAndSendFeedback(conn, &now))
     502                 :           0 :                     goto error;
     503                 :           4 :                 last_status = now;
     504                 :             :             }
     505                 :             : 
     506         [ +  + ]:         192 :             if (endposReached)
     507                 :             :             {
     508                 :           3 :                 stop_reason = STREAM_STOP_KEEPALIVE;
     509                 :           3 :                 time_to_abort = true;
     510                 :           3 :                 break;
     511                 :             :             }
     512                 :             : 
     513                 :         189 :             continue;
     514                 :             :         }
     515         [ -  + ]:         121 :         else if (copybuf[0] != PqReplMsg_WALData)
     516                 :             :         {
     517                 :           0 :             pg_log_error("unrecognized streaming header: \"%c\"",
     518                 :             :                          copybuf[0]);
     519                 :           0 :             goto error;
     520                 :             :         }
     521                 :             : 
     522                 :             :         /*
     523                 :             :          * Read the header of the WALData message, enclosed in the CopyData
     524                 :             :          * message. We only need the WAL location field (dataStart), the rest
     525                 :             :          * of the header is ignored.
     526                 :             :          */
     527                 :         121 :         hdr_len = 1;            /* msgtype PqReplMsg_WALData */
     528                 :         121 :         hdr_len += 8;           /* dataStart */
     529                 :         121 :         hdr_len += 8;           /* walEnd */
     530                 :         121 :         hdr_len += 8;           /* sendTime */
     531         [ -  + ]:         121 :         if (r < hdr_len + 1)
     532                 :             :         {
     533                 :           0 :             pg_log_error("streaming header too small: %d", r);
     534                 :           0 :             goto error;
     535                 :             :         }
     536                 :             : 
     537                 :             :         /* Extract WAL location for this block */
     538                 :         121 :         cur_record_lsn = fe_recvint64(&copybuf[1]);
     539                 :             : 
     540   [ +  +  -  + ]:         121 :         if (XLogRecPtrIsValid(endpos) && cur_record_lsn > endpos)
     541                 :             :         {
     542                 :             :             /*
     543                 :             :              * We've read past our endpoint, so prepare to go away being
     544                 :             :              * cautious about what happens to our output data.
     545                 :             :              */
     546         [ #  # ]:           0 :             if (!flushAndSendFeedback(conn, &now))
     547                 :           0 :                 goto error;
     548                 :           0 :             stop_reason = STREAM_STOP_END_OF_WAL;
     549                 :           0 :             time_to_abort = true;
     550                 :           0 :             break;
     551                 :             :         }
     552                 :             : 
     553                 :         121 :         output_written_lsn = Max(cur_record_lsn, output_written_lsn);
     554                 :             : 
     555                 :         121 :         bytes_left = r - hdr_len;
     556                 :         121 :         bytes_written = 0;
     557                 :             : 
     558                 :             :         /* signal that a fsync is needed */
     559                 :         121 :         output_needs_fsync = true;
     560                 :             : 
     561         [ +  + ]:         242 :         while (bytes_left)
     562                 :             :         {
     563                 :             :             ssize_t     ret;
     564                 :             : 
     565                 :         121 :             ret = write(outfd,
     566                 :         121 :                         copybuf + hdr_len + bytes_written,
     567                 :             :                         bytes_left);
     568                 :             : 
     569         [ -  + ]:         121 :             if (ret < 0)
     570                 :             :             {
     571                 :           0 :                 pg_log_error("could not write %zu bytes to log file \"%s\": %m",
     572                 :             :                              bytes_left, outfile);
     573                 :           0 :                 goto error;
     574                 :             :             }
     575                 :             : 
     576                 :             :             /* Write was successful, advance our position */
     577                 :         121 :             bytes_written += ret;
     578                 :         121 :             bytes_left -= ret;
     579                 :             :         }
     580                 :             : 
     581         [ -  + ]:         121 :         if (write(outfd, "\n", 1) != 1)
     582                 :             :         {
     583                 :           0 :             pg_log_error("could not write %zu bytes to log file \"%s\": %m",
     584                 :             :                          (size_t) 1, outfile);
     585                 :           0 :             goto error;
     586                 :             :         }
     587                 :             : 
     588   [ +  +  +  + ]:         121 :         if (XLogRecPtrIsValid(endpos) && cur_record_lsn == endpos)
     589                 :             :         {
     590                 :             :             /* endpos was exactly the record we just processed, we're done */
     591         [ -  + ]:           5 :             if (!flushAndSendFeedback(conn, &now))
     592                 :           0 :                 goto error;
     593                 :           5 :             stop_reason = STREAM_STOP_END_OF_WAL;
     594                 :           5 :             time_to_abort = true;
     595                 :           5 :             break;
     596                 :             :         }
     597                 :             :     }
     598                 :             : 
     599                 :             :     /* Clean up connection state if stream has been aborted */
     600         [ +  + ]:          20 :     if (time_to_abort)
     601                 :          12 :         prepareToTerminate(conn, endpos, stop_reason, cur_record_lsn);
     602                 :             : 
     603                 :          20 :     res = PQgetResult(conn);
     604         [ +  + ]:          20 :     if (PQresultStatus(res) == PGRES_COPY_OUT)
     605                 :             :     {
     606                 :          12 :         PQclear(res);
     607                 :             : 
     608                 :             :         /*
     609                 :             :          * We're doing a client-initiated clean exit and have sent CopyDone to
     610                 :             :          * the server. Drain any messages, so we don't miss a last-minute
     611                 :             :          * ErrorResponse. The walsender stops generating WALData records once
     612                 :             :          * it sees CopyDone, so expect this to finish quickly. After CopyDone,
     613                 :             :          * it's too late for sendFeedback(), even if this were to take a long
     614                 :             :          * time. Hence, use synchronous-mode PQgetCopyData().
     615                 :             :          */
     616                 :             :         while (1)
     617                 :         154 :         {
     618                 :             :             int         r;
     619                 :             : 
     620         [ +  + ]:         166 :             if (copybuf != NULL)
     621                 :             :             {
     622                 :         162 :                 PQfreemem(copybuf);
     623                 :         162 :                 copybuf = NULL;
     624                 :             :             }
     625                 :         166 :             r = PQgetCopyData(conn, &copybuf, 0);
     626         [ +  + ]:         166 :             if (r == -1)
     627                 :          12 :                 break;
     628         [ -  + ]:         154 :             if (r == -2)
     629                 :             :             {
     630                 :           0 :                 pg_log_error("could not read COPY data: %s",
     631                 :             :                              PQerrorMessage(conn));
     632                 :           0 :                 time_to_abort = false;  /* unclean exit */
     633                 :           0 :                 goto error;
     634                 :             :             }
     635                 :             :         }
     636                 :             : 
     637                 :          12 :         res = PQgetResult(conn);
     638                 :             :     }
     639         [ +  + ]:          20 :     if (PQresultStatus(res) != PGRES_COMMAND_OK)
     640                 :             :     {
     641                 :           7 :         pg_log_error("unexpected termination of replication stream: %s",
     642                 :             :                      PQresultErrorMessage(res));
     643                 :           7 :         PQclear(res);
     644                 :           7 :         goto error;
     645                 :             :     }
     646                 :          13 :     PQclear(res);
     647                 :             : 
     648   [ +  -  +  + ]:          13 :     if (outfd != -1 && strcmp(outfile, "-") != 0)
     649                 :             :     {
     650                 :           4 :         TimestampTz t = feGetCurrentTimestamp();
     651                 :             : 
     652                 :           4 :         OutputFsync(t);
     653         [ -  + ]:           4 :         if (close(outfd) != 0)
     654                 :           0 :             pg_log_error("could not close file \"%s\": %m", outfile);
     655                 :             :     }
     656                 :          13 :     outfd = -1;
     657                 :          29 : error:
     658         [ -  + ]:          29 :     if (copybuf != NULL)
     659                 :             :     {
     660                 :           0 :         PQfreemem(copybuf);
     661                 :           0 :         copybuf = NULL;
     662                 :             :     }
     663                 :          29 :     destroyPQExpBuffer(query);
     664                 :          29 :     PQfinish(conn);
     665                 :          29 :     conn = NULL;
     666                 :             : }
     667                 :             : 
     668                 :             : /*
     669                 :             :  * Unfortunately we can't do sensible signal handling on windows...
     670                 :             :  */
     671                 :             : #ifndef WIN32
     672                 :             : 
     673                 :             : /*
     674                 :             :  * When SIGINT/SIGTERM are caught, just tell the system to exit at the next
     675                 :             :  * possible moment.
     676                 :             :  */
     677                 :             : static void
     678                 :           4 : sigexit_handler(SIGNAL_ARGS)
     679                 :             : {
     680                 :           4 :     stop_reason = STREAM_STOP_SIGNAL;
     681                 :           4 :     time_to_abort = true;
     682                 :           4 : }
     683                 :             : 
     684                 :             : /*
     685                 :             :  * Trigger the output file to be reopened.
     686                 :             :  */
     687                 :             : static void
     688                 :           0 : sighup_handler(SIGNAL_ARGS)
     689                 :             : {
     690                 :           0 :     output_reopen = true;
     691                 :           0 : }
     692                 :             : #endif
     693                 :             : 
     694                 :             : 
     695                 :             : int
     696                 :          72 : main(int argc, char **argv)
     697                 :             : {
     698                 :             :     static struct option long_options[] = {
     699                 :             : /* general options */
     700                 :             :         {"file", required_argument, NULL, 'f'},
     701                 :             :         {"fsync-interval", required_argument, NULL, 'F'},
     702                 :             :         {"no-loop", no_argument, NULL, 'n'},
     703                 :             :         {"enable-failover", no_argument, NULL, 5},
     704                 :             :         {"enable-two-phase", no_argument, NULL, 't'},
     705                 :             :         {"two-phase", no_argument, NULL, 't'},    /* deprecated */
     706                 :             :         {"verbose", no_argument, NULL, 'v'},
     707                 :             :         {"version", no_argument, NULL, 'V'},
     708                 :             :         {"help", no_argument, NULL, '?'},
     709                 :             : /* connection options */
     710                 :             :         {"dbname", required_argument, NULL, 'd'},
     711                 :             :         {"host", required_argument, NULL, 'h'},
     712                 :             :         {"port", required_argument, NULL, 'p'},
     713                 :             :         {"username", required_argument, NULL, 'U'},
     714                 :             :         {"no-password", no_argument, NULL, 'w'},
     715                 :             :         {"password", no_argument, NULL, 'W'},
     716                 :             : /* replication options */
     717                 :             :         {"startpos", required_argument, NULL, 'I'},
     718                 :             :         {"endpos", required_argument, NULL, 'E'},
     719                 :             :         {"option", required_argument, NULL, 'o'},
     720                 :             :         {"plugin", required_argument, NULL, 'P'},
     721                 :             :         {"status-interval", required_argument, NULL, 's'},
     722                 :             :         {"slot", required_argument, NULL, 'S'},
     723                 :             : /* action */
     724                 :             :         {"create-slot", no_argument, NULL, 1},
     725                 :             :         {"start", no_argument, NULL, 2},
     726                 :             :         {"drop-slot", no_argument, NULL, 3},
     727                 :             :         {"if-not-exists", no_argument, NULL, 4},
     728                 :             :         {NULL, 0, NULL, 0}
     729                 :             :     };
     730                 :             :     int         c;
     731                 :             :     int         option_index;
     732                 :             :     uint32      hi,
     733                 :             :                 lo;
     734                 :             :     char       *db_name;
     735                 :             : 
     736                 :          72 :     pg_logging_init(argv[0]);
     737                 :          72 :     progname = get_progname(argv[0]);
     738                 :          72 :     set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_basebackup"));
     739                 :             : 
     740         [ +  + ]:          72 :     if (argc > 1)
     741                 :             :     {
     742   [ +  +  -  + ]:          71 :         if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
     743                 :             :         {
     744                 :           1 :             usage();
     745                 :           1 :             exit(0);
     746                 :             :         }
     747         [ +  - ]:          70 :         else if (strcmp(argv[1], "-V") == 0 ||
     748         [ +  + ]:          70 :                  strcmp(argv[1], "--version") == 0)
     749                 :             :         {
     750                 :           1 :             puts("pg_recvlogical (PostgreSQL) " PG_VERSION);
     751                 :           1 :             exit(0);
     752                 :             :         }
     753                 :             :     }
     754                 :             : 
     755                 :         411 :     while ((c = getopt_long(argc, argv, "E:f:F:ntvd:h:p:U:wWI:o:P:s:S:",
     756         [ +  + ]:         411 :                             long_options, &option_index)) != -1)
     757                 :             :     {
     758   [ +  +  +  +  :         342 :         switch (c)
          +  +  +  -  -  
          -  -  -  -  +  
          +  +  +  +  +  
             +  +  -  + ]
     759                 :             :         {
     760                 :             : /* general options */
     761                 :          29 :             case 'f':
     762                 :          29 :                 outfile = pg_strdup(optarg);
     763                 :          29 :                 break;
     764                 :           3 :             case 'F':
     765         [ -  + ]:           3 :                 if (!option_parse_int(optarg, "-F/--fsync-interval", 0,
     766                 :             :                                       INT_MAX / 1000,
     767                 :             :                                       &fsync_interval))
     768                 :           0 :                     exit(1);
     769                 :           3 :                 fsync_interval *= 1000;
     770                 :           3 :                 break;
     771                 :          25 :             case 'n':
     772                 :          25 :                 noloop = 1;
     773                 :          25 :                 break;
     774                 :           2 :             case 't':
     775                 :           2 :                 two_phase = true;
     776                 :           2 :                 break;
     777                 :           1 :             case 'v':
     778                 :           1 :                 verbose++;
     779                 :           1 :                 break;
     780                 :           1 :             case 5:
     781                 :           1 :                 failover = true;
     782                 :           1 :                 break;
     783                 :             : /* connection options */
     784                 :          66 :             case 'd':
     785                 :          66 :                 dbname = pg_strdup(optarg);
     786                 :          66 :                 break;
     787                 :           0 :             case 'h':
     788                 :           0 :                 dbhost = pg_strdup(optarg);
     789                 :           0 :                 break;
     790                 :           0 :             case 'p':
     791                 :           0 :                 dbport = pg_strdup(optarg);
     792                 :           0 :                 break;
     793                 :           0 :             case 'U':
     794                 :           0 :                 dbuser = pg_strdup(optarg);
     795                 :           0 :                 break;
     796                 :           0 :             case 'w':
     797                 :           0 :                 dbgetpassword = -1;
     798                 :           0 :                 break;
     799                 :           0 :             case 'W':
     800                 :           0 :                 dbgetpassword = 1;
     801                 :           0 :                 break;
     802                 :             : /* replication options */
     803                 :           0 :             case 'I':
     804         [ #  # ]:           0 :                 if (sscanf(optarg, "%X/%08X", &hi, &lo) != 2)
     805                 :           0 :                     pg_fatal("could not parse start position \"%s\"", optarg);
     806                 :           0 :                 startpos = ((uint64) hi) << 32 | lo;
     807                 :           0 :                 break;
     808                 :           9 :             case 'E':
     809         [ -  + ]:           9 :                 if (sscanf(optarg, "%X/%08X", &hi, &lo) != 2)
     810                 :           0 :                     pg_fatal("could not parse end position \"%s\"", optarg);
     811                 :           9 :                 endpos = ((uint64) hi) << 32 | lo;
     812                 :           9 :                 break;
     813                 :          42 :             case 'o':
     814                 :             :                 {
     815                 :          42 :                     char       *data = pg_strdup(optarg);
     816                 :          42 :                     char       *val = strchr(data, '=');
     817                 :             : 
     818         [ +  - ]:          42 :                     if (val != NULL)
     819                 :             :                     {
     820                 :             :                         /* remove =; separate data from val */
     821                 :          42 :                         *val = '\0';
     822                 :          42 :                         val++;
     823                 :             :                     }
     824                 :             : 
     825                 :          42 :                     noptions += 1;
     826                 :          42 :                     options = pg_realloc_array(options, char *, noptions * 2);
     827                 :             : 
     828                 :          42 :                     options[(noptions - 1) * 2] = data;
     829                 :          42 :                     options[(noptions - 1) * 2 + 1] = val;
     830                 :             :                 }
     831                 :             : 
     832                 :          42 :                 break;
     833                 :          27 :             case 'P':
     834                 :          27 :                 plugin = pg_strdup(optarg);
     835                 :          27 :                 break;
     836                 :           2 :             case 's':
     837         [ -  + ]:           2 :                 if (!option_parse_int(optarg, "-s/--status-interval", 0,
     838                 :             :                                       INT_MAX / 1000,
     839                 :             :                                       &standby_message_timeout))
     840                 :           0 :                     exit(1);
     841                 :           2 :                 standby_message_timeout *= 1000;
     842                 :           2 :                 break;
     843                 :          68 :             case 'S':
     844                 :          68 :                 replication_slot = pg_strdup(optarg);
     845                 :          68 :                 break;
     846                 :             : /* action */
     847                 :          32 :             case 1:
     848                 :          32 :                 do_create_slot = true;
     849                 :          32 :                 break;
     850                 :          30 :             case 2:
     851                 :          30 :                 do_start_slot = true;
     852                 :          30 :                 break;
     853                 :           4 :             case 3:
     854                 :           4 :                 do_drop_slot = true;
     855                 :           4 :                 break;
     856                 :           0 :             case 4:
     857                 :           0 :                 slot_exists_ok = true;
     858                 :           0 :                 break;
     859                 :             : 
     860                 :           1 :             default:
     861                 :             :                 /* getopt_long already emitted a complaint */
     862                 :           1 :                 pg_log_error_hint("Try \"%s --help\" for more information.", progname);
     863                 :           1 :                 exit(1);
     864                 :             :         }
     865                 :             :     }
     866                 :             : 
     867                 :             :     /*
     868                 :             :      * Any non-option arguments?
     869                 :             :      */
     870         [ -  + ]:          69 :     if (optind < argc)
     871                 :             :     {
     872                 :           0 :         pg_log_error("too many command-line arguments (first is \"%s\")",
     873                 :             :                      argv[optind]);
     874                 :           0 :         pg_log_error_hint("Try \"%s --help\" for more information.", progname);
     875                 :           0 :         exit(1);
     876                 :             :     }
     877                 :             : 
     878                 :             :     /*
     879                 :             :      * Required arguments
     880                 :             :      */
     881         [ +  + ]:          69 :     if (replication_slot == NULL)
     882                 :             :     {
     883                 :           1 :         pg_log_error("no slot specified");
     884                 :           1 :         pg_log_error_hint("Try \"%s --help\" for more information.", progname);
     885                 :           1 :         exit(1);
     886                 :             :     }
     887                 :             : 
     888   [ +  +  +  + ]:          68 :     if (do_start_slot && outfile == NULL)
     889                 :             :     {
     890                 :           1 :         pg_log_error("no target file specified");
     891                 :           1 :         pg_log_error_hint("Try \"%s --help\" for more information.", progname);
     892                 :           1 :         exit(1);
     893                 :             :     }
     894                 :             : 
     895   [ +  +  +  + ]:          67 :     if (!do_drop_slot && dbname == NULL)
     896                 :             :     {
     897                 :           1 :         pg_log_error("no database specified");
     898                 :           1 :         pg_log_error_hint("Try \"%s --help\" for more information.", progname);
     899                 :           1 :         exit(1);
     900                 :             :     }
     901                 :             : 
     902   [ +  +  +  +  :          66 :     if (!do_drop_slot && !do_create_slot && !do_start_slot)
                   +  + ]
     903                 :             :     {
     904                 :           1 :         pg_log_error("at least one action needs to be specified");
     905                 :           1 :         pg_log_error_hint("Try \"%s --help\" for more information.", progname);
     906                 :           1 :         exit(1);
     907                 :             :     }
     908                 :             : 
     909   [ +  +  +  -  :          65 :     if (do_drop_slot && (do_create_slot || do_start_slot))
                   -  + ]
     910                 :             :     {
     911                 :           0 :         pg_log_error("cannot use --create-slot or --start together with --drop-slot");
     912                 :           0 :         pg_log_error_hint("Try \"%s --help\" for more information.", progname);
     913                 :           0 :         exit(1);
     914                 :             :     }
     915                 :             : 
     916   [ -  +  -  -  :          65 :     if (XLogRecPtrIsValid(startpos) && (do_create_slot || do_drop_slot))
                   -  - ]
     917                 :             :     {
     918                 :           0 :         pg_log_error("cannot use --create-slot or --drop-slot together with --startpos");
     919                 :           0 :         pg_log_error_hint("Try \"%s --help\" for more information.", progname);
     920                 :           0 :         exit(1);
     921                 :             :     }
     922                 :             : 
     923   [ +  +  -  + ]:          65 :     if (XLogRecPtrIsValid(endpos) && !do_start_slot)
     924                 :             :     {
     925                 :           0 :         pg_log_error("--endpos may only be specified with --start");
     926                 :           0 :         pg_log_error_hint("Try \"%s --help\" for more information.", progname);
     927                 :           0 :         exit(1);
     928                 :             :     }
     929                 :             : 
     930         [ +  + ]:          65 :     if (!do_create_slot)
     931                 :             :     {
     932         [ +  + ]:          33 :         if (two_phase)
     933                 :             :         {
     934                 :           1 :             pg_log_error("%s may only be specified with --create-slot", "--enable-two-phase");
     935                 :           1 :             pg_log_error_hint("Try \"%s --help\" for more information.", progname);
     936                 :           1 :             exit(1);
     937                 :             :         }
     938                 :             : 
     939         [ -  + ]:          32 :         if (failover)
     940                 :             :         {
     941                 :           0 :             pg_log_error("%s may only be specified with --create-slot", "--enable-failover");
     942                 :           0 :             pg_log_error_hint("Try \"%s --help\" for more information.", progname);
     943                 :           0 :             exit(1);
     944                 :             :         }
     945                 :             :     }
     946                 :             : 
     947                 :             :     /*
     948                 :             :      * Obtain a connection to server.  Notably, if we need a password, we want
     949                 :             :      * to collect it from the user immediately.
     950                 :             :      */
     951                 :          64 :     conn = GetConnection();
     952         [ -  + ]:          64 :     if (!conn)
     953                 :             :         /* Error message already written in GetConnection() */
     954                 :           0 :         exit(1);
     955                 :          64 :     atexit(disconnect_atexit);
     956                 :             : 
     957                 :             :     /*
     958                 :             :      * Trap signals.  (Don't do this until after the initial password prompt,
     959                 :             :      * if one is needed, in GetConnection.)
     960                 :             :      */
     961                 :             : #ifndef WIN32
     962                 :          64 :     pqsignal(SIGINT, sigexit_handler);
     963                 :          64 :     pqsignal(SIGTERM, sigexit_handler);
     964                 :          64 :     pqsignal(SIGHUP, sighup_handler);
     965                 :             : #endif
     966                 :             : 
     967                 :             :     /*
     968                 :             :      * Run IDENTIFY_SYSTEM to check the connection type for each action.
     969                 :             :      * --create-slot and --start actions require a database-specific
     970                 :             :      * replication connection because they handle logical replication slots.
     971                 :             :      * --drop-slot can remove replication slots from any replication
     972                 :             :      * connection without this restriction.
     973                 :             :      */
     974         [ -  + ]:          64 :     if (!RunIdentifySystem(conn, NULL, NULL, NULL, &db_name))
     975                 :           0 :         exit(1);
     976                 :             : 
     977   [ +  +  -  + ]:          64 :     if (!do_drop_slot && db_name == NULL)
     978                 :           0 :         pg_fatal("could not establish database-specific replication connection");
     979                 :             : 
     980                 :             :     /*
     981                 :             :      * Set umask so that directories/files are created with the same
     982                 :             :      * permissions as directories/files in the source data directory.
     983                 :             :      *
     984                 :             :      * pg_mode_mask is set to owner-only by default and then updated in
     985                 :             :      * GetConnection() where we get the mode from the server-side with
     986                 :             :      * RetrieveDataDirCreatePerm() and then call SetDataDirectoryCreatePerm().
     987                 :             :      */
     988                 :          64 :     umask(pg_mode_mask);
     989                 :             : 
     990                 :             :     /* Drop a replication slot. */
     991         [ +  + ]:          64 :     if (do_drop_slot)
     992                 :             :     {
     993         [ -  + ]:           4 :         if (verbose)
     994                 :           0 :             pg_log_info("dropping replication slot \"%s\"", replication_slot);
     995                 :             : 
     996         [ -  + ]:           4 :         if (!DropReplicationSlot(conn, replication_slot))
     997                 :           0 :             exit(1);
     998                 :             :     }
     999                 :             : 
    1000                 :             :     /* Create a replication slot. */
    1001         [ +  + ]:          64 :     if (do_create_slot)
    1002                 :             :     {
    1003         [ -  + ]:          32 :         if (verbose)
    1004                 :           0 :             pg_log_info("creating replication slot \"%s\"", replication_slot);
    1005                 :             : 
    1006         [ -  + ]:          32 :         if (!CreateReplicationSlot(conn, replication_slot, plugin, false,
    1007                 :             :                                    false, false, slot_exists_ok, two_phase,
    1008                 :             :                                    failover))
    1009                 :           0 :             exit(1);
    1010                 :          32 :         startpos = InvalidXLogRecPtr;
    1011                 :             :     }
    1012                 :             : 
    1013         [ +  + ]:          64 :     if (!do_start_slot)
    1014                 :          36 :         exit(0);
    1015                 :             : 
    1016                 :             :     /* Stream loop */
    1017                 :             :     while (true)
    1018                 :             :     {
    1019                 :          29 :         StreamLogicalLog();
    1020         [ +  + ]:          29 :         if (time_to_abort)
    1021                 :             :         {
    1022                 :             :             /*
    1023                 :             :              * We've been Ctrl-C'ed or reached an exit limit condition. That's
    1024                 :             :              * not an error, so exit without an errorcode.
    1025                 :             :              */
    1026                 :          12 :             exit(0);
    1027                 :             :         }
    1028                 :             : 
    1029                 :             :         /*
    1030                 :             :          * Ensure all written data is flushed to disk before exiting or
    1031                 :             :          * starting a new replication.
    1032                 :             :          */
    1033         [ +  + ]:          17 :         if (outfd != -1)
    1034                 :          10 :             OutputFsync(feGetCurrentTimestamp());
    1035                 :             : 
    1036         [ +  + ]:          17 :         if (noloop)
    1037                 :             :         {
    1038                 :          16 :             pg_fatal("disconnected");
    1039                 :             :         }
    1040                 :             :         else
    1041                 :             :         {
    1042                 :             :             /* translator: check source for value for %d */
    1043                 :           1 :             pg_log_info("disconnected; waiting %d seconds to try again",
    1044                 :             :                         RECONNECT_SLEEP_TIME);
    1045                 :           1 :             pg_usleep(RECONNECT_SLEEP_TIME * 1000000);
    1046                 :             :         }
    1047                 :             :     }
    1048                 :             : }
    1049                 :             : 
    1050                 :             : /*
    1051                 :             :  * Fsync our output data, and send a feedback message to the server.  Returns
    1052                 :             :  * true if successful, false otherwise.
    1053                 :             :  *
    1054                 :             :  * If successful, *now is updated to the current timestamp just before sending
    1055                 :             :  * feedback.
    1056                 :             :  */
    1057                 :             : static bool
    1058                 :          13 : flushAndSendFeedback(PGconn *conn, TimestampTz *now)
    1059                 :             : {
    1060                 :             :     /* flush data to disk, so that we send a recent flush pointer */
    1061                 :          13 :     OutputFsync(*now);
    1062                 :          13 :     *now = feGetCurrentTimestamp();
    1063         [ -  + ]:          13 :     if (!sendFeedback(conn, *now, true, false))
    1064                 :           0 :         return false;
    1065                 :             : 
    1066                 :          13 :     return true;
    1067                 :             : }
    1068                 :             : 
    1069                 :             : /*
    1070                 :             :  * Try to inform the server about our upcoming demise, but don't wait around or
    1071                 :             :  * retry on failure.
    1072                 :             :  */
    1073                 :             : static void
    1074                 :          12 : prepareToTerminate(PGconn *conn, XLogRecPtr endpos, StreamStopReason reason,
    1075                 :             :                    XLogRecPtr lsn)
    1076                 :             : {
    1077                 :             :     /*
    1078                 :             :      * If pg_recvlogical is terminated by a signal, we can reach here without
    1079                 :             :      * sending final feedback. In that case, send feedback once more before
    1080                 :             :      * sending CopyDone so the replication slot can advance far enough to
    1081                 :             :      * reduce the chance of resending duplicate data when pg_recvlogical is
    1082                 :             :      * restarted.
    1083                 :             :      *
    1084                 :             :      * This is still only a best-effort attempt. Depending on when the signal
    1085                 :             :      * arrives, the receiver may have written decoded output that the server
    1086                 :             :      * cannot yet safely treat as confirmed, so a later restart can still see
    1087                 :             :      * duplicate data.
    1088                 :             :      *
    1089                 :             :      * For other termination cases, such as STREAM_STOP_KEEPALIVE and
    1090                 :             :      * STREAM_STOP_END_OF_WAL, feedback has already been sent before reaching
    1091                 :             :      * here, so there is no need to call flushAndSendFeedback() again.
    1092                 :             :      */
    1093         [ +  + ]:          12 :     if (reason == STREAM_STOP_SIGNAL)
    1094                 :             :     {
    1095                 :           4 :         TimestampTz now = feGetCurrentTimestamp();
    1096                 :             : 
    1097                 :           4 :         (void) flushAndSendFeedback(conn, &now);
    1098                 :             :     }
    1099                 :             : 
    1100                 :          12 :     (void) PQputCopyEnd(conn, NULL);
    1101                 :          12 :     (void) PQflush(conn);
    1102                 :             : 
    1103         [ +  + ]:          12 :     if (verbose)
    1104                 :             :     {
    1105   [ +  -  -  -  :           1 :         switch (reason)
                      - ]
    1106                 :             :         {
    1107                 :           1 :             case STREAM_STOP_SIGNAL:
    1108                 :           1 :                 pg_log_info("received interrupt signal, exiting");
    1109                 :           1 :                 break;
    1110                 :           0 :             case STREAM_STOP_KEEPALIVE:
    1111                 :           0 :                 pg_log_info("end position %X/%08X reached by keepalive",
    1112                 :             :                             LSN_FORMAT_ARGS(endpos));
    1113                 :           0 :                 break;
    1114                 :           0 :             case STREAM_STOP_END_OF_WAL:
    1115                 :             :                 Assert(XLogRecPtrIsValid(lsn));
    1116                 :           0 :                 pg_log_info("end position %X/%08X reached by WAL record at %X/%08X",
    1117                 :             :                             LSN_FORMAT_ARGS(endpos), LSN_FORMAT_ARGS(lsn));
    1118                 :           0 :                 break;
    1119                 :           0 :             case STREAM_STOP_NONE:
    1120                 :             :                 Assert(false);
    1121                 :           0 :                 break;
    1122                 :             :         }
    1123                 :             :     }
    1124                 :          12 : }
        

Generated by: LCOV version 2.0-1