LCOV - differential code coverage report
Current view: top level - src/bin/pg_basebackup - pg_recvlogical.c (source / functions) Coverage Total Hit UNC UBC GBC GNC CBC DUB DCB
Current: ba12a202ce1b5581dc0ed149cf3f637d7897ad5d vs 2866d8c7dbfc9d882a7d80fef93fbbe763709932 Lines: 79.5 % 487 387 2 98 3 6 378 4 3
Current Date: 2026-08-27 14:31:44 +0300 Functions: 90.0 % 10 9 1 3 6
Baseline: lcov-20260827-baseline Branches: 73.1 % 294 215 1 78 1 7 207
Baseline Date: 2026-08-27 14:31:58 +0300 Line coverage date bins:
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
(7,30] days: 100.0 % 2 2 2
(30,360] days: 83.3 % 30 25 2 3 4 21
(360..) days: 79.1 % 455 360 95 3 357
Function coverage date bins:
(360..) days: 90.0 % 10 9 1 3 6
Branch coverage date bins:
(7,30] days: 75.0 % 4 3 1 3
(30,360] days: 70.6 % 34 24 10 4 20
(360..) days: 73.4 % 256 188 68 1 187

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

Generated by: LCOV version 2.0-1