LCOV - code coverage report
Current view: top level - src/bin/pg_basebackup - receivelog.c (source / functions) Hit Total Coverage
Test: PostgreSQL 18devel Lines: 270 423 63.8 %
Date: 2024-11-21 10:14:43 Functions: 16 17 94.1 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * receivelog.c - receive WAL files using the streaming
       4             :  *                replication protocol.
       5             :  *
       6             :  * Author: Magnus Hagander <magnus@hagander.net>
       7             :  *
       8             :  * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
       9             :  *
      10             :  * IDENTIFICATION
      11             :  *        src/bin/pg_basebackup/receivelog.c
      12             :  *-------------------------------------------------------------------------
      13             :  */
      14             : 
      15             : #include "postgres_fe.h"
      16             : 
      17             : #include <sys/select.h>
      18             : #include <sys/stat.h>
      19             : #include <unistd.h>
      20             : 
      21             : #include "access/xlog_internal.h"
      22             : #include "common/logging.h"
      23             : #include "libpq-fe.h"
      24             : #include "receivelog.h"
      25             : #include "streamutil.h"
      26             : 
      27             : /* currently open WAL file */
      28             : static Walfile *walfile = NULL;
      29             : static bool reportFlushPosition = false;
      30             : static XLogRecPtr lastFlushPosition = InvalidXLogRecPtr;
      31             : 
      32             : static bool still_sending = true;   /* feedback still needs to be sent? */
      33             : 
      34             : static PGresult *HandleCopyStream(PGconn *conn, StreamCtl *stream,
      35             :                                   XLogRecPtr *stoppos);
      36             : static int  CopyStreamPoll(PGconn *conn, long timeout_ms, pgsocket stop_socket);
      37             : static int  CopyStreamReceive(PGconn *conn, long timeout, pgsocket stop_socket,
      38             :                               char **buffer);
      39             : static bool ProcessKeepaliveMsg(PGconn *conn, StreamCtl *stream, char *copybuf,
      40             :                                 int len, XLogRecPtr blockpos, TimestampTz *last_status);
      41             : static bool ProcessXLogDataMsg(PGconn *conn, StreamCtl *stream, char *copybuf, int len,
      42             :                                XLogRecPtr *blockpos);
      43             : static PGresult *HandleEndOfCopyStream(PGconn *conn, StreamCtl *stream, char *copybuf,
      44             :                                        XLogRecPtr blockpos, XLogRecPtr *stoppos);
      45             : static bool CheckCopyStreamStop(PGconn *conn, StreamCtl *stream, XLogRecPtr blockpos);
      46             : static long CalculateCopyStreamSleeptime(TimestampTz now, int standby_message_timeout,
      47             :                                          TimestampTz last_status);
      48             : 
      49             : static bool ReadEndOfStreamingResult(PGresult *res, XLogRecPtr *startpos,
      50             :                                      uint32 *timeline);
      51             : 
      52             : static bool
      53          28 : mark_file_as_archived(StreamCtl *stream, const char *fname)
      54             : {
      55             :     Walfile    *f;
      56             :     static char tmppath[MAXPGPATH];
      57             : 
      58          28 :     snprintf(tmppath, sizeof(tmppath), "archive_status/%s.done",
      59             :              fname);
      60             : 
      61          28 :     f = stream->walmethod->ops->open_for_write(stream->walmethod, tmppath,
      62             :                                                NULL, 0);
      63          28 :     if (f == NULL)
      64             :     {
      65           0 :         pg_log_error("could not create archive status file \"%s\": %s",
      66             :                      tmppath, GetLastWalMethodError(stream->walmethod));
      67           0 :         return false;
      68             :     }
      69             : 
      70          28 :     if (stream->walmethod->ops->close(f, CLOSE_NORMAL) != 0)
      71             :     {
      72           0 :         pg_log_error("could not close archive status file \"%s\": %s",
      73             :                      tmppath, GetLastWalMethodError(stream->walmethod));
      74           0 :         return false;
      75             :     }
      76             : 
      77          28 :     return true;
      78             : }
      79             : 
      80             : /*
      81             :  * Open a new WAL file in the specified directory.
      82             :  *
      83             :  * Returns true if OK; on failure, returns false after printing an error msg.
      84             :  * On success, 'walfile' is set to the opened WAL file.
      85             :  *
      86             :  * The file will be padded to 16Mb with zeroes.
      87             :  */
      88             : static bool
      89         282 : open_walfile(StreamCtl *stream, XLogRecPtr startpoint)
      90             : {
      91             :     Walfile    *f;
      92             :     char       *fn;
      93             :     ssize_t     size;
      94             :     XLogSegNo   segno;
      95             :     char        walfile_name[MAXPGPATH];
      96             : 
      97         282 :     XLByteToSeg(startpoint, segno, WalSegSz);
      98         282 :     XLogFileName(walfile_name, stream->timeline, segno, WalSegSz);
      99             : 
     100             :     /* Note that this considers the compression used if necessary */
     101         282 :     fn = stream->walmethod->ops->get_file_name(stream->walmethod,
     102             :                                                walfile_name,
     103         282 :                                                stream->partial_suffix);
     104             : 
     105             :     /*
     106             :      * When streaming to files, if an existing file exists we verify that it's
     107             :      * either empty (just created), or a complete WalSegSz segment (in which
     108             :      * case it has been created and padded). Anything else indicates a corrupt
     109             :      * file. Compressed files have no need for padding, so just ignore this
     110             :      * case.
     111             :      *
     112             :      * When streaming to tar, no file with this name will exist before, so we
     113             :      * never have to verify a size.
     114             :      */
     115         550 :     if (stream->walmethod->compression_algorithm == PG_COMPRESSION_NONE &&
     116         268 :         stream->walmethod->ops->existsfile(stream->walmethod, fn))
     117             :     {
     118           0 :         size = stream->walmethod->ops->get_file_size(stream->walmethod, fn);
     119           0 :         if (size < 0)
     120             :         {
     121           0 :             pg_log_error("could not get size of write-ahead log file \"%s\": %s",
     122             :                          fn, GetLastWalMethodError(stream->walmethod));
     123           0 :             pg_free(fn);
     124           0 :             return false;
     125             :         }
     126           0 :         if (size == WalSegSz)
     127             :         {
     128             :             /* Already padded file. Open it for use */
     129           0 :             f = stream->walmethod->ops->open_for_write(stream->walmethod, walfile_name, stream->partial_suffix, 0);
     130           0 :             if (f == NULL)
     131             :             {
     132           0 :                 pg_log_error("could not open existing write-ahead log file \"%s\": %s",
     133             :                              fn, GetLastWalMethodError(stream->walmethod));
     134           0 :                 pg_free(fn);
     135           0 :                 return false;
     136             :             }
     137             : 
     138             :             /* fsync file in case of a previous crash */
     139           0 :             if (stream->walmethod->ops->sync(f) != 0)
     140             :             {
     141           0 :                 pg_log_error("could not fsync existing write-ahead log file \"%s\": %s",
     142             :                              fn, GetLastWalMethodError(stream->walmethod));
     143           0 :                 stream->walmethod->ops->close(f, CLOSE_UNLINK);
     144           0 :                 exit(1);
     145             :             }
     146             : 
     147           0 :             walfile = f;
     148           0 :             pg_free(fn);
     149           0 :             return true;
     150             :         }
     151           0 :         if (size != 0)
     152             :         {
     153             :             /* if write didn't set errno, assume problem is no disk space */
     154           0 :             if (errno == 0)
     155           0 :                 errno = ENOSPC;
     156           0 :             pg_log_error(ngettext("write-ahead log file \"%s\" has %zd byte, should be 0 or %d",
     157             :                                   "write-ahead log file \"%s\" has %zd bytes, should be 0 or %d",
     158             :                                   size),
     159             :                          fn, size, WalSegSz);
     160           0 :             pg_free(fn);
     161           0 :             return false;
     162             :         }
     163             :         /* File existed and was empty, so fall through and open */
     164             :     }
     165             : 
     166             :     /* No file existed, so create one */
     167             : 
     168         282 :     f = stream->walmethod->ops->open_for_write(stream->walmethod,
     169             :                                                walfile_name,
     170         282 :                                                stream->partial_suffix,
     171             :                                                WalSegSz);
     172         282 :     if (f == NULL)
     173             :     {
     174           0 :         pg_log_error("could not open write-ahead log file \"%s\": %s",
     175             :                      fn, GetLastWalMethodError(stream->walmethod));
     176           0 :         pg_free(fn);
     177           0 :         return false;
     178             :     }
     179             : 
     180         282 :     pg_free(fn);
     181         282 :     walfile = f;
     182         282 :     return true;
     183             : }
     184             : 
     185             : /*
     186             :  * Close the current WAL file (if open), and rename it to the correct
     187             :  * filename if it's complete. On failure, prints an error message to stderr
     188             :  * and returns false, otherwise returns true.
     189             :  */
     190             : static bool
     191         304 : close_walfile(StreamCtl *stream, XLogRecPtr pos)
     192             : {
     193             :     char       *fn;
     194             :     off_t       currpos;
     195             :     int         r;
     196             :     char        walfile_name[MAXPGPATH];
     197             : 
     198         304 :     if (walfile == NULL)
     199          22 :         return true;
     200             : 
     201         282 :     strlcpy(walfile_name, walfile->pathname, MAXPGPATH);
     202         282 :     currpos = walfile->currpos;
     203             : 
     204             :     /* Note that this considers the compression used if necessary */
     205         282 :     fn = stream->walmethod->ops->get_file_name(stream->walmethod,
     206             :                                                walfile_name,
     207         282 :                                                stream->partial_suffix);
     208             : 
     209         282 :     if (stream->partial_suffix)
     210             :     {
     211          24 :         if (currpos == WalSegSz)
     212          12 :             r = stream->walmethod->ops->close(walfile, CLOSE_NORMAL);
     213             :         else
     214             :         {
     215          12 :             pg_log_info("not renaming \"%s\", segment is not complete", fn);
     216          12 :             r = stream->walmethod->ops->close(walfile, CLOSE_NO_RENAME);
     217             :         }
     218             :     }
     219             :     else
     220         258 :         r = stream->walmethod->ops->close(walfile, CLOSE_NORMAL);
     221             : 
     222         282 :     walfile = NULL;
     223             : 
     224         282 :     if (r != 0)
     225             :     {
     226           0 :         pg_log_error("could not close file \"%s\": %s",
     227             :                      fn, GetLastWalMethodError(stream->walmethod));
     228             : 
     229           0 :         pg_free(fn);
     230           0 :         return false;
     231             :     }
     232             : 
     233         282 :     pg_free(fn);
     234             : 
     235             :     /*
     236             :      * Mark file as archived if requested by the caller - pg_basebackup needs
     237             :      * to do so as files can otherwise get archived again after promotion of a
     238             :      * new node. This is in line with walreceiver.c always doing a
     239             :      * XLogArchiveForceDone() after a complete segment.
     240             :      */
     241         282 :     if (currpos == WalSegSz && stream->mark_done)
     242             :     {
     243             :         /* writes error message if failed */
     244          24 :         if (!mark_file_as_archived(stream, walfile_name))
     245           0 :             return false;
     246             :     }
     247             : 
     248         282 :     lastFlushPosition = pos;
     249         282 :     return true;
     250             : }
     251             : 
     252             : 
     253             : /*
     254             :  * Check if a timeline history file exists.
     255             :  */
     256             : static bool
     257         276 : existsTimeLineHistoryFile(StreamCtl *stream)
     258             : {
     259             :     char        histfname[MAXFNAMELEN];
     260             : 
     261             :     /*
     262             :      * Timeline 1 never has a history file. We treat that as if it existed,
     263             :      * since we never need to stream it.
     264             :      */
     265         276 :     if (stream->timeline == 1)
     266         270 :         return true;
     267             : 
     268           6 :     TLHistoryFileName(histfname, stream->timeline);
     269             : 
     270           6 :     return stream->walmethod->ops->existsfile(stream->walmethod, histfname);
     271             : }
     272             : 
     273             : static bool
     274           6 : writeTimeLineHistoryFile(StreamCtl *stream, char *filename, char *content)
     275             : {
     276           6 :     int         size = strlen(content);
     277             :     char        histfname[MAXFNAMELEN];
     278             :     Walfile    *f;
     279             : 
     280             :     /*
     281             :      * Check that the server's idea of how timeline history files should be
     282             :      * named matches ours.
     283             :      */
     284           6 :     TLHistoryFileName(histfname, stream->timeline);
     285           6 :     if (strcmp(histfname, filename) != 0)
     286             :     {
     287           0 :         pg_log_error("server reported unexpected history file name for timeline %u: %s",
     288             :                      stream->timeline, filename);
     289           0 :         return false;
     290             :     }
     291             : 
     292           6 :     f = stream->walmethod->ops->open_for_write(stream->walmethod,
     293             :                                                histfname, ".tmp", 0);
     294           6 :     if (f == NULL)
     295             :     {
     296           0 :         pg_log_error("could not create timeline history file \"%s\": %s",
     297             :                      histfname, GetLastWalMethodError(stream->walmethod));
     298           0 :         return false;
     299             :     }
     300             : 
     301           6 :     if ((int) stream->walmethod->ops->write(f, content, size) != size)
     302             :     {
     303           0 :         pg_log_error("could not write timeline history file \"%s\": %s",
     304             :                      histfname, GetLastWalMethodError(stream->walmethod));
     305             : 
     306             :         /*
     307             :          * If we fail to make the file, delete it to release disk space
     308             :          */
     309           0 :         stream->walmethod->ops->close(f, CLOSE_UNLINK);
     310             : 
     311           0 :         return false;
     312             :     }
     313             : 
     314           6 :     if (stream->walmethod->ops->close(f, CLOSE_NORMAL) != 0)
     315             :     {
     316           0 :         pg_log_error("could not close file \"%s\": %s",
     317             :                      histfname, GetLastWalMethodError(stream->walmethod));
     318           0 :         return false;
     319             :     }
     320             : 
     321             :     /* Maintain archive_status, check close_walfile() for details. */
     322           6 :     if (stream->mark_done)
     323             :     {
     324             :         /* writes error message if failed */
     325           4 :         if (!mark_file_as_archived(stream, histfname))
     326           0 :             return false;
     327             :     }
     328             : 
     329           6 :     return true;
     330             : }
     331             : 
     332             : /*
     333             :  * Send a Standby Status Update message to server.
     334             :  */
     335             : static bool
     336         272 : sendFeedback(PGconn *conn, XLogRecPtr blockpos, TimestampTz now, bool replyRequested)
     337             : {
     338             :     char        replybuf[1 + 8 + 8 + 8 + 8 + 1];
     339         272 :     int         len = 0;
     340             : 
     341         272 :     replybuf[len] = 'r';
     342         272 :     len += 1;
     343         272 :     fe_sendint64(blockpos, &replybuf[len]); /* write */
     344         272 :     len += 8;
     345         272 :     if (reportFlushPosition)
     346         264 :         fe_sendint64(lastFlushPosition, &replybuf[len]);    /* flush */
     347             :     else
     348           8 :         fe_sendint64(InvalidXLogRecPtr, &replybuf[len]);    /* flush */
     349         272 :     len += 8;
     350         272 :     fe_sendint64(InvalidXLogRecPtr, &replybuf[len]);    /* apply */
     351         272 :     len += 8;
     352         272 :     fe_sendint64(now, &replybuf[len]);  /* sendTime */
     353         272 :     len += 8;
     354         272 :     replybuf[len] = replyRequested ? 1 : 0; /* replyRequested */
     355         272 :     len += 1;
     356             : 
     357         272 :     if (PQputCopyData(conn, replybuf, len) <= 0 || PQflush(conn))
     358             :     {
     359           0 :         pg_log_error("could not send feedback packet: %s",
     360             :                      PQerrorMessage(conn));
     361           0 :         return false;
     362             :     }
     363             : 
     364         272 :     return true;
     365             : }
     366             : 
     367             : /*
     368             :  * Check that the server version we're connected to is supported by
     369             :  * ReceiveXlogStream().
     370             :  *
     371             :  * If it's not, an error message is printed to stderr, and false is returned.
     372             :  */
     373             : bool
     374         590 : CheckServerVersionForStreaming(PGconn *conn)
     375             : {
     376             :     int         minServerMajor,
     377             :                 maxServerMajor;
     378             :     int         serverMajor;
     379             : 
     380             :     /*
     381             :      * The message format used in streaming replication changed in 9.3, so we
     382             :      * cannot stream from older servers. And we don't support servers newer
     383             :      * than the client; it might work, but we don't know, so err on the safe
     384             :      * side.
     385             :      */
     386         590 :     minServerMajor = 903;
     387         590 :     maxServerMajor = PG_VERSION_NUM / 100;
     388         590 :     serverMajor = PQserverVersion(conn) / 100;
     389         590 :     if (serverMajor < minServerMajor)
     390             :     {
     391           0 :         const char *serverver = PQparameterStatus(conn, "server_version");
     392             : 
     393           0 :         pg_log_error("incompatible server version %s; client does not support streaming from server versions older than %s",
     394             :                      serverver ? serverver : "'unknown'",
     395             :                      "9.3");
     396           0 :         return false;
     397             :     }
     398         590 :     else if (serverMajor > maxServerMajor)
     399             :     {
     400           0 :         const char *serverver = PQparameterStatus(conn, "server_version");
     401             : 
     402           0 :         pg_log_error("incompatible server version %s; client does not support streaming from server versions newer than %s",
     403             :                      serverver ? serverver : "'unknown'",
     404             :                      PG_VERSION);
     405           0 :         return false;
     406             :     }
     407         590 :     return true;
     408             : }
     409             : 
     410             : /*
     411             :  * Receive a log stream starting at the specified position.
     412             :  *
     413             :  * Individual parameters are passed through the StreamCtl structure.
     414             :  *
     415             :  * If sysidentifier is specified, validate that both the system
     416             :  * identifier and the timeline matches the specified ones
     417             :  * (by sending an extra IDENTIFY_SYSTEM command)
     418             :  *
     419             :  * All received segments will be written to the directory
     420             :  * specified by basedir. This will also fetch any missing timeline history
     421             :  * files.
     422             :  *
     423             :  * The stream_stop callback will be called every time data
     424             :  * is received, and whenever a segment is completed. If it returns
     425             :  * true, the streaming will stop and the function
     426             :  * return. As long as it returns false, streaming will continue
     427             :  * indefinitely.
     428             :  *
     429             :  * If stream_stop() checks for external input, stop_socket should be set to
     430             :  * the FD it checks.  This will allow such input to be detected promptly
     431             :  * rather than after standby_message_timeout (which might be indefinite).
     432             :  * Note that signals will interrupt waits for input as well, but that is
     433             :  * race-y since a signal received while busy won't interrupt the wait.
     434             :  *
     435             :  * standby_message_timeout controls how often we send a message
     436             :  * back to the primary letting it know our progress, in milliseconds.
     437             :  * Zero means no messages are sent.
     438             :  * This message will only contain the write location, and never
     439             :  * flush or replay.
     440             :  *
     441             :  * If 'partial_suffix' is not NULL, files are initially created with the
     442             :  * given suffix, and the suffix is removed once the file is finished. That
     443             :  * allows you to tell the difference between partial and completed files,
     444             :  * so that you can continue later where you left.
     445             :  *
     446             :  * If 'synchronous' is true, the received WAL is flushed as soon as written,
     447             :  * otherwise only when the WAL file is closed.
     448             :  *
     449             :  * Note: The WAL location *must* be at a log segment start!
     450             :  */
     451             : bool
     452         274 : ReceiveXlogStream(PGconn *conn, StreamCtl *stream)
     453             : {
     454             :     char        query[128];
     455             :     char        slotcmd[128];
     456             :     PGresult   *res;
     457             :     XLogRecPtr  stoppos;
     458             : 
     459             :     /*
     460             :      * The caller should've checked the server version already, but doesn't do
     461             :      * any harm to check it here too.
     462             :      */
     463         274 :     if (!CheckServerVersionForStreaming(conn))
     464           0 :         return false;
     465             : 
     466             :     /*
     467             :      * Decide whether we want to report the flush position. If we report the
     468             :      * flush position, the primary will know what WAL we'll possibly
     469             :      * re-request, and it can then remove older WAL safely. We must always do
     470             :      * that when we are using slots.
     471             :      *
     472             :      * Reporting the flush position makes one eligible as a synchronous
     473             :      * replica. People shouldn't include generic names in
     474             :      * synchronous_standby_names, but we've protected them against it so far,
     475             :      * so let's continue to do so unless specifically requested.
     476             :      */
     477         274 :     if (stream->replication_slot != NULL)
     478             :     {
     479         264 :         reportFlushPosition = true;
     480         264 :         sprintf(slotcmd, "SLOT \"%s\" ", stream->replication_slot);
     481             :     }
     482             :     else
     483             :     {
     484          10 :         if (stream->synchronous)
     485           2 :             reportFlushPosition = true;
     486             :         else
     487           8 :             reportFlushPosition = false;
     488          10 :         slotcmd[0] = 0;
     489             :     }
     490             : 
     491         274 :     if (stream->sysidentifier != NULL)
     492             :     {
     493         274 :         char       *sysidentifier = NULL;
     494             :         TimeLineID  servertli;
     495             : 
     496             :         /*
     497             :          * Get the server system identifier and timeline, and validate them.
     498             :          */
     499         274 :         if (!RunIdentifySystem(conn, &sysidentifier, &servertli, NULL, NULL))
     500             :         {
     501           0 :             pg_free(sysidentifier);
     502           0 :             return false;
     503             :         }
     504             : 
     505         274 :         if (strcmp(stream->sysidentifier, sysidentifier) != 0)
     506             :         {
     507           0 :             pg_log_error("system identifier does not match between base backup and streaming connection");
     508           0 :             pg_free(sysidentifier);
     509           0 :             return false;
     510             :         }
     511         274 :         pg_free(sysidentifier);
     512             : 
     513         274 :         if (stream->timeline > servertli)
     514             :         {
     515           0 :             pg_log_error("starting timeline %u is not present in the server",
     516             :                          stream->timeline);
     517           0 :             return false;
     518             :         }
     519             :     }
     520             : 
     521             :     /*
     522             :      * initialize flush position to starting point, it's the caller's
     523             :      * responsibility that that's sane.
     524             :      */
     525         274 :     lastFlushPosition = stream->startpos;
     526             : 
     527             :     while (1)
     528             :     {
     529             :         /*
     530             :          * Fetch the timeline history file for this timeline, if we don't have
     531             :          * it already. When streaming log to tar, this will always return
     532             :          * false, as we are never streaming into an existing file and
     533             :          * therefore there can be no pre-existing timeline history file.
     534             :          */
     535         276 :         if (!existsTimeLineHistoryFile(stream))
     536             :         {
     537           6 :             snprintf(query, sizeof(query), "TIMELINE_HISTORY %u", stream->timeline);
     538           6 :             res = PQexec(conn, query);
     539           6 :             if (PQresultStatus(res) != PGRES_TUPLES_OK)
     540             :             {
     541             :                 /* FIXME: we might send it ok, but get an error */
     542           0 :                 pg_log_error("could not send replication command \"%s\": %s",
     543             :                              "TIMELINE_HISTORY", PQresultErrorMessage(res));
     544           0 :                 PQclear(res);
     545           0 :                 return false;
     546             :             }
     547             : 
     548             :             /*
     549             :              * The response to TIMELINE_HISTORY is a single row result set
     550             :              * with two fields: filename and content
     551             :              */
     552           6 :             if (PQnfields(res) != 2 || PQntuples(res) != 1)
     553             :             {
     554           0 :                 pg_log_warning("unexpected response to TIMELINE_HISTORY command: got %d rows and %d fields, expected %d rows and %d fields",
     555             :                                PQntuples(res), PQnfields(res), 1, 2);
     556             :             }
     557             : 
     558             :             /* Write the history file to disk */
     559           6 :             writeTimeLineHistoryFile(stream,
     560             :                                      PQgetvalue(res, 0, 0),
     561             :                                      PQgetvalue(res, 0, 1));
     562             : 
     563           6 :             PQclear(res);
     564             :         }
     565             : 
     566             :         /*
     567             :          * Before we start streaming from the requested location, check if the
     568             :          * callback tells us to stop here.
     569             :          */
     570         276 :         if (stream->stream_stop(stream->startpos, stream->timeline, false))
     571           0 :             return true;
     572             : 
     573             :         /* Initiate the replication stream at specified location */
     574         276 :         snprintf(query, sizeof(query), "START_REPLICATION %s%X/%X TIMELINE %u",
     575             :                  slotcmd,
     576         276 :                  LSN_FORMAT_ARGS(stream->startpos),
     577             :                  stream->timeline);
     578         276 :         res = PQexec(conn, query);
     579         276 :         if (PQresultStatus(res) != PGRES_COPY_BOTH)
     580             :         {
     581           4 :             pg_log_error("could not send replication command \"%s\": %s",
     582             :                          "START_REPLICATION", PQresultErrorMessage(res));
     583           4 :             PQclear(res);
     584           4 :             return false;
     585             :         }
     586         272 :         PQclear(res);
     587             : 
     588             :         /* Stream the WAL */
     589         272 :         res = HandleCopyStream(conn, stream, &stoppos);
     590         272 :         if (res == NULL)
     591           0 :             goto error;
     592             : 
     593             :         /*
     594             :          * Streaming finished.
     595             :          *
     596             :          * There are two possible reasons for that: a controlled shutdown, or
     597             :          * we reached the end of the current timeline. In case of
     598             :          * end-of-timeline, the server sends a result set after Copy has
     599             :          * finished, containing information about the next timeline. Read
     600             :          * that, and restart streaming from the next timeline. In case of
     601             :          * controlled shutdown, stop here.
     602             :          */
     603         272 :         if (PQresultStatus(res) == PGRES_TUPLES_OK)
     604             :         {
     605             :             /*
     606             :              * End-of-timeline. Read the next timeline's ID and starting
     607             :              * position. Usually, the starting position will match the end of
     608             :              * the previous timeline, but there are corner cases like if the
     609             :              * server had sent us half of a WAL record, when it was promoted.
     610             :              * The new timeline will begin at the end of the last complete
     611             :              * record in that case, overlapping the partial WAL record on the
     612             :              * old timeline.
     613             :              */
     614             :             uint32      newtimeline;
     615             :             bool        parsed;
     616             : 
     617           2 :             parsed = ReadEndOfStreamingResult(res, &stream->startpos, &newtimeline);
     618           2 :             PQclear(res);
     619           2 :             if (!parsed)
     620           0 :                 goto error;
     621             : 
     622             :             /* Sanity check the values the server gave us */
     623           2 :             if (newtimeline <= stream->timeline)
     624             :             {
     625           0 :                 pg_log_error("server reported unexpected next timeline %u, following timeline %u",
     626             :                              newtimeline, stream->timeline);
     627           0 :                 goto error;
     628             :             }
     629           2 :             if (stream->startpos > stoppos)
     630             :             {
     631           0 :                 pg_log_error("server stopped streaming timeline %u at %X/%X, but reported next timeline %u to begin at %X/%X",
     632             :                              stream->timeline, LSN_FORMAT_ARGS(stoppos),
     633             :                              newtimeline, LSN_FORMAT_ARGS(stream->startpos));
     634           0 :                 goto error;
     635             :             }
     636             : 
     637             :             /* Read the final result, which should be CommandComplete. */
     638           2 :             res = PQgetResult(conn);
     639           2 :             if (PQresultStatus(res) != PGRES_COMMAND_OK)
     640             :             {
     641           0 :                 pg_log_error("unexpected termination of replication stream: %s",
     642             :                              PQresultErrorMessage(res));
     643           0 :                 PQclear(res);
     644           0 :                 goto error;
     645             :             }
     646           2 :             PQclear(res);
     647             : 
     648             :             /*
     649             :              * Loop back to start streaming from the new timeline. Always
     650             :              * start streaming at the beginning of a segment.
     651             :              */
     652           2 :             stream->timeline = newtimeline;
     653           2 :             stream->startpos = stream->startpos -
     654           2 :                 XLogSegmentOffset(stream->startpos, WalSegSz);
     655           2 :             continue;
     656             :         }
     657         270 :         else if (PQresultStatus(res) == PGRES_COMMAND_OK)
     658             :         {
     659         268 :             PQclear(res);
     660             : 
     661             :             /*
     662             :              * End of replication (ie. controlled shut down of the server).
     663             :              *
     664             :              * Check if the callback thinks it's OK to stop here. If not,
     665             :              * complain.
     666             :              */
     667         268 :             if (stream->stream_stop(stoppos, stream->timeline, false))
     668         268 :                 return true;
     669             :             else
     670             :             {
     671           0 :                 pg_log_error("replication stream was terminated before stop point");
     672           0 :                 goto error;
     673             :             }
     674             :         }
     675             :         else
     676             :         {
     677             :             /* Server returned an error. */
     678           2 :             pg_log_error("unexpected termination of replication stream: %s",
     679             :                          PQresultErrorMessage(res));
     680           2 :             PQclear(res);
     681           2 :             goto error;
     682             :         }
     683             :     }
     684             : 
     685           2 : error:
     686           2 :     if (walfile != NULL && stream->walmethod->ops->close(walfile, CLOSE_NO_RENAME) != 0)
     687           0 :         pg_log_error("could not close file \"%s\": %s",
     688             :                      walfile->pathname, GetLastWalMethodError(stream->walmethod));
     689           2 :     walfile = NULL;
     690           2 :     return false;
     691             : }
     692             : 
     693             : /*
     694             :  * Helper function to parse the result set returned by server after streaming
     695             :  * has finished. On failure, prints an error to stderr and returns false.
     696             :  */
     697             : static bool
     698           2 : ReadEndOfStreamingResult(PGresult *res, XLogRecPtr *startpos, uint32 *timeline)
     699             : {
     700             :     uint32      startpos_xlogid,
     701             :                 startpos_xrecoff;
     702             : 
     703             :     /*----------
     704             :      * The result set consists of one row and two columns, e.g:
     705             :      *
     706             :      *  next_tli | next_tli_startpos
     707             :      * ----------+-------------------
     708             :      *         4 | 0/9949AE0
     709             :      *
     710             :      * next_tli is the timeline ID of the next timeline after the one that
     711             :      * just finished streaming. next_tli_startpos is the WAL location where
     712             :      * the server switched to it.
     713             :      *----------
     714             :      */
     715           2 :     if (PQnfields(res) < 2 || PQntuples(res) != 1)
     716             :     {
     717           0 :         pg_log_error("unexpected result set after end-of-timeline: got %d rows and %d fields, expected %d rows and %d fields",
     718             :                      PQntuples(res), PQnfields(res), 1, 2);
     719           0 :         return false;
     720             :     }
     721             : 
     722           2 :     *timeline = atoi(PQgetvalue(res, 0, 0));
     723           2 :     if (sscanf(PQgetvalue(res, 0, 1), "%X/%X", &startpos_xlogid,
     724             :                &startpos_xrecoff) != 2)
     725             :     {
     726           0 :         pg_log_error("could not parse next timeline's starting point \"%s\"",
     727             :                      PQgetvalue(res, 0, 1));
     728           0 :         return false;
     729             :     }
     730           2 :     *startpos = ((uint64) startpos_xlogid << 32) | startpos_xrecoff;
     731             : 
     732           2 :     return true;
     733             : }
     734             : 
     735             : /*
     736             :  * The main loop of ReceiveXlogStream. Handles the COPY stream after
     737             :  * initiating streaming with the START_REPLICATION command.
     738             :  *
     739             :  * If the COPY ends (not necessarily successfully) due a message from the
     740             :  * server, returns a PGresult and sets *stoppos to the last byte written.
     741             :  * On any other sort of error, returns NULL.
     742             :  */
     743             : static PGresult *
     744         272 : HandleCopyStream(PGconn *conn, StreamCtl *stream,
     745             :                  XLogRecPtr *stoppos)
     746             : {
     747         272 :     char       *copybuf = NULL;
     748         272 :     TimestampTz last_status = -1;
     749         272 :     XLogRecPtr  blockpos = stream->startpos;
     750             : 
     751         272 :     still_sending = true;
     752             : 
     753             :     while (1)
     754        2990 :     {
     755             :         int         r;
     756             :         TimestampTz now;
     757             :         long        sleeptime;
     758             : 
     759             :         /*
     760             :          * Check if we should continue streaming, or abort at this point.
     761             :          */
     762        3262 :         if (!CheckCopyStreamStop(conn, stream, blockpos))
     763           0 :             goto error;
     764             : 
     765        3262 :         now = feGetCurrentTimestamp();
     766             : 
     767             :         /*
     768             :          * If synchronous option is true, issue sync command as soon as there
     769             :          * are WAL data which has not been flushed yet.
     770             :          */
     771        3262 :         if (stream->synchronous && lastFlushPosition < blockpos && walfile != NULL)
     772             :         {
     773           0 :             if (stream->walmethod->ops->sync(walfile) != 0)
     774           0 :                 pg_fatal("could not fsync file \"%s\": %s",
     775             :                          walfile->pathname, GetLastWalMethodError(stream->walmethod));
     776           0 :             lastFlushPosition = blockpos;
     777             : 
     778             :             /*
     779             :              * Send feedback so that the server sees the latest WAL locations
     780             :              * immediately.
     781             :              */
     782           0 :             if (!sendFeedback(conn, blockpos, now, false))
     783           0 :                 goto error;
     784           0 :             last_status = now;
     785             :         }
     786             : 
     787             :         /*
     788             :          * Potentially send a status message to the primary
     789             :          */
     790        6336 :         if (still_sending && stream->standby_message_timeout > 0 &&
     791        3074 :             feTimestampDifferenceExceeds(last_status, now,
     792             :                                          stream->standby_message_timeout))
     793             :         {
     794             :             /* Time to send feedback! */
     795         272 :             if (!sendFeedback(conn, blockpos, now, false))
     796           0 :                 goto error;
     797         272 :             last_status = now;
     798             :         }
     799             : 
     800             :         /*
     801             :          * Calculate how long send/receive loops should sleep
     802             :          */
     803        3262 :         sleeptime = CalculateCopyStreamSleeptime(now, stream->standby_message_timeout,
     804             :                                                  last_status);
     805             : 
     806        3262 :         r = CopyStreamReceive(conn, sleeptime, stream->stop_socket, &copybuf);
     807       10686 :         while (r != 0)
     808             :         {
     809        7696 :             if (r == -1)
     810           0 :                 goto error;
     811        7696 :             if (r == -2)
     812             :             {
     813         272 :                 PGresult   *res = HandleEndOfCopyStream(conn, stream, copybuf, blockpos, stoppos);
     814             : 
     815         272 :                 if (res == NULL)
     816           0 :                     goto error;
     817             :                 else
     818         272 :                     return res;
     819             :             }
     820             : 
     821             :             /* Check the message type. */
     822        7424 :             if (copybuf[0] == 'k')
     823             :             {
     824           0 :                 if (!ProcessKeepaliveMsg(conn, stream, copybuf, r, blockpos,
     825             :                                          &last_status))
     826           0 :                     goto error;
     827             :             }
     828        7424 :             else if (copybuf[0] == 'w')
     829             :             {
     830        7424 :                 if (!ProcessXLogDataMsg(conn, stream, copybuf, r, &blockpos))
     831           0 :                     goto error;
     832             : 
     833             :                 /*
     834             :                  * Check if we should continue streaming, or abort at this
     835             :                  * point.
     836             :                  */
     837        7424 :                 if (!CheckCopyStreamStop(conn, stream, blockpos))
     838           0 :                     goto error;
     839             :             }
     840             :             else
     841             :             {
     842           0 :                 pg_log_error("unrecognized streaming header: \"%c\"",
     843             :                              copybuf[0]);
     844           0 :                 goto error;
     845             :             }
     846             : 
     847             :             /*
     848             :              * Process the received data, and any subsequent data we can read
     849             :              * without blocking.
     850             :              */
     851        7424 :             r = CopyStreamReceive(conn, 0, stream->stop_socket, &copybuf);
     852             :         }
     853             :     }
     854             : 
     855           0 : error:
     856           0 :     PQfreemem(copybuf);
     857           0 :     return NULL;
     858             : }
     859             : 
     860             : /*
     861             :  * Wait until we can read a CopyData message,
     862             :  * or timeout, or occurrence of a signal or input on the stop_socket.
     863             :  * (timeout_ms < 0 means wait indefinitely; 0 means don't wait.)
     864             :  *
     865             :  * Returns 1 if data has become available for reading, 0 if timed out
     866             :  * or interrupted by signal or stop_socket input, and -1 on an error.
     867             :  */
     868             : static int
     869       10310 : CopyStreamPoll(PGconn *conn, long timeout_ms, pgsocket stop_socket)
     870             : {
     871             :     int         ret;
     872             :     fd_set      input_mask;
     873             :     int         connsocket;
     874             :     int         maxfd;
     875             :     struct timeval timeout;
     876             :     struct timeval *timeoutptr;
     877             : 
     878       10310 :     connsocket = PQsocket(conn);
     879       10310 :     if (connsocket < 0)
     880             :     {
     881           0 :         pg_log_error("invalid socket: %s", PQerrorMessage(conn));
     882           0 :         return -1;
     883             :     }
     884             : 
     885       10310 :     FD_ZERO(&input_mask);
     886       10310 :     FD_SET(connsocket, &input_mask);
     887       10310 :     maxfd = connsocket;
     888       10310 :     if (stop_socket != PGINVALID_SOCKET)
     889             :     {
     890       10066 :         FD_SET(stop_socket, &input_mask);
     891       10066 :         maxfd = Max(maxfd, stop_socket);
     892             :     }
     893             : 
     894       10310 :     if (timeout_ms < 0)
     895         188 :         timeoutptr = NULL;
     896             :     else
     897             :     {
     898       10122 :         timeout.tv_sec = timeout_ms / 1000L;
     899       10122 :         timeout.tv_usec = (timeout_ms % 1000L) * 1000L;
     900       10122 :         timeoutptr = &timeout;
     901             :     }
     902             : 
     903       10310 :     ret = select(maxfd + 1, &input_mask, NULL, NULL, timeoutptr);
     904             : 
     905       10310 :     if (ret < 0)
     906             :     {
     907           0 :         if (errno == EINTR)
     908           0 :             return 0;           /* Got a signal, so not an error */
     909           0 :         pg_log_error("%s() failed: %m", "select");
     910           0 :         return -1;
     911             :     }
     912       10310 :     if (ret > 0 && FD_ISSET(connsocket, &input_mask))
     913        7826 :         return 1;               /* Got input on connection socket */
     914             : 
     915        2484 :     return 0;                   /* Got timeout or input on stop_socket */
     916             : }
     917             : 
     918             : /*
     919             :  * Receive CopyData message available from XLOG stream, blocking for
     920             :  * maximum of 'timeout' ms.
     921             :  *
     922             :  * If data was received, returns the length of the data. *buffer is set to
     923             :  * point to a buffer holding the received message. The buffer is only valid
     924             :  * until the next CopyStreamReceive call.
     925             :  *
     926             :  * Returns 0 if no data was available within timeout, or if wait was
     927             :  * interrupted by signal or stop_socket input.
     928             :  * -1 on error. -2 if the server ended the COPY.
     929             :  */
     930             : static int
     931       10686 : CopyStreamReceive(PGconn *conn, long timeout, pgsocket stop_socket,
     932             :                   char **buffer)
     933             : {
     934       10686 :     char       *copybuf = NULL;
     935             :     int         rawlen;
     936             : 
     937       10686 :     PQfreemem(*buffer);
     938       10686 :     *buffer = NULL;
     939             : 
     940             :     /* Try to receive a CopyData message */
     941       10686 :     rawlen = PQgetCopyData(conn, &copybuf, 1);
     942       10686 :     if (rawlen == 0)
     943             :     {
     944             :         int         ret;
     945             : 
     946             :         /*
     947             :          * No data available.  Wait for some to appear, but not longer than
     948             :          * the specified timeout, so that we can ping the server.  Also stop
     949             :          * waiting if input appears on stop_socket.
     950             :          */
     951       10310 :         ret = CopyStreamPoll(conn, timeout, stop_socket);
     952       10310 :         if (ret <= 0)
     953        2484 :             return ret;
     954             : 
     955             :         /* Now there is actually data on the socket */
     956        7826 :         if (PQconsumeInput(conn) == 0)
     957             :         {
     958           0 :             pg_log_error("could not receive data from WAL stream: %s",
     959             :                          PQerrorMessage(conn));
     960           0 :             return -1;
     961             :         }
     962             : 
     963             :         /* Now that we've consumed some input, try again */
     964        7826 :         rawlen = PQgetCopyData(conn, &copybuf, 1);
     965        7826 :         if (rawlen == 0)
     966         506 :             return 0;
     967             :     }
     968        7696 :     if (rawlen == -1)           /* end-of-streaming or error */
     969         272 :         return -2;
     970        7424 :     if (rawlen == -2)
     971             :     {
     972           0 :         pg_log_error("could not read COPY data: %s", PQerrorMessage(conn));
     973           0 :         return -1;
     974             :     }
     975             : 
     976             :     /* Return received messages to caller */
     977        7424 :     *buffer = copybuf;
     978        7424 :     return rawlen;
     979             : }
     980             : 
     981             : /*
     982             :  * Process the keepalive message.
     983             :  */
     984             : static bool
     985           0 : ProcessKeepaliveMsg(PGconn *conn, StreamCtl *stream, char *copybuf, int len,
     986             :                     XLogRecPtr blockpos, TimestampTz *last_status)
     987             : {
     988             :     int         pos;
     989             :     bool        replyRequested;
     990             :     TimestampTz now;
     991             : 
     992             :     /*
     993             :      * Parse the keepalive message, enclosed in the CopyData message. We just
     994             :      * check if the server requested a reply, and ignore the rest.
     995             :      */
     996           0 :     pos = 1;                    /* skip msgtype 'k' */
     997           0 :     pos += 8;                   /* skip walEnd */
     998           0 :     pos += 8;                   /* skip sendTime */
     999             : 
    1000           0 :     if (len < pos + 1)
    1001             :     {
    1002           0 :         pg_log_error("streaming header too small: %d", len);
    1003           0 :         return false;
    1004             :     }
    1005           0 :     replyRequested = copybuf[pos];
    1006             : 
    1007             :     /* If the server requested an immediate reply, send one. */
    1008           0 :     if (replyRequested && still_sending)
    1009             :     {
    1010           0 :         if (reportFlushPosition && lastFlushPosition < blockpos &&
    1011           0 :             walfile != NULL)
    1012             :         {
    1013             :             /*
    1014             :              * If a valid flush location needs to be reported, flush the
    1015             :              * current WAL file so that the latest flush location is sent back
    1016             :              * to the server. This is necessary to see whether the last WAL
    1017             :              * data has been successfully replicated or not, at the normal
    1018             :              * shutdown of the server.
    1019             :              */
    1020           0 :             if (stream->walmethod->ops->sync(walfile) != 0)
    1021           0 :                 pg_fatal("could not fsync file \"%s\": %s",
    1022             :                          walfile->pathname, GetLastWalMethodError(stream->walmethod));
    1023           0 :             lastFlushPosition = blockpos;
    1024             :         }
    1025             : 
    1026           0 :         now = feGetCurrentTimestamp();
    1027           0 :         if (!sendFeedback(conn, blockpos, now, false))
    1028           0 :             return false;
    1029           0 :         *last_status = now;
    1030             :     }
    1031             : 
    1032           0 :     return true;
    1033             : }
    1034             : 
    1035             : /*
    1036             :  * Process XLogData message.
    1037             :  */
    1038             : static bool
    1039        7424 : ProcessXLogDataMsg(PGconn *conn, StreamCtl *stream, char *copybuf, int len,
    1040             :                    XLogRecPtr *blockpos)
    1041             : {
    1042             :     int         xlogoff;
    1043             :     int         bytes_left;
    1044             :     int         bytes_written;
    1045             :     int         hdr_len;
    1046             : 
    1047             :     /*
    1048             :      * Once we've decided we don't want to receive any more, just ignore any
    1049             :      * subsequent XLogData messages.
    1050             :      */
    1051        7424 :     if (!(still_sending))
    1052         382 :         return true;
    1053             : 
    1054             :     /*
    1055             :      * Read the header of the XLogData message, enclosed in the CopyData
    1056             :      * message. We only need the WAL location field (dataStart), the rest of
    1057             :      * the header is ignored.
    1058             :      */
    1059        7042 :     hdr_len = 1;                /* msgtype 'w' */
    1060        7042 :     hdr_len += 8;               /* dataStart */
    1061        7042 :     hdr_len += 8;               /* walEnd */
    1062        7042 :     hdr_len += 8;               /* sendTime */
    1063        7042 :     if (len < hdr_len)
    1064             :     {
    1065           0 :         pg_log_error("streaming header too small: %d", len);
    1066           0 :         return false;
    1067             :     }
    1068        7042 :     *blockpos = fe_recvint64(&copybuf[1]);
    1069             : 
    1070             :     /* Extract WAL location for this block */
    1071        7042 :     xlogoff = XLogSegmentOffset(*blockpos, WalSegSz);
    1072             : 
    1073             :     /*
    1074             :      * Verify that the initial location in the stream matches where we think
    1075             :      * we are.
    1076             :      */
    1077        7042 :     if (walfile == NULL)
    1078             :     {
    1079             :         /* No file open yet */
    1080         282 :         if (xlogoff != 0)
    1081             :         {
    1082           0 :             pg_log_error("received write-ahead log record for offset %u with no file open",
    1083             :                          xlogoff);
    1084           0 :             return false;
    1085             :         }
    1086             :     }
    1087             :     else
    1088             :     {
    1089             :         /* More data in existing segment */
    1090        6760 :         if (walfile->currpos != xlogoff)
    1091             :         {
    1092           0 :             pg_log_error("got WAL data offset %08x, expected %08x",
    1093             :                          xlogoff, (int) walfile->currpos);
    1094           0 :             return false;
    1095             :         }
    1096             :     }
    1097             : 
    1098        7042 :     bytes_left = len - hdr_len;
    1099        7042 :     bytes_written = 0;
    1100             : 
    1101       14080 :     while (bytes_left)
    1102             :     {
    1103             :         int         bytes_to_write;
    1104             : 
    1105             :         /*
    1106             :          * If crossing a WAL boundary, only write up until we reach wal
    1107             :          * segment size.
    1108             :          */
    1109        7042 :         if (xlogoff + bytes_left > WalSegSz)
    1110           0 :             bytes_to_write = WalSegSz - xlogoff;
    1111             :         else
    1112        7042 :             bytes_to_write = bytes_left;
    1113             : 
    1114        7042 :         if (walfile == NULL)
    1115             :         {
    1116         282 :             if (!open_walfile(stream, *blockpos))
    1117             :             {
    1118             :                 /* Error logged by open_walfile */
    1119           0 :                 return false;
    1120             :             }
    1121             :         }
    1122             : 
    1123       14084 :         if (stream->walmethod->ops->write(walfile,
    1124        7042 :                                           copybuf + hdr_len + bytes_written,
    1125        7042 :                                           bytes_to_write) != bytes_to_write)
    1126             :         {
    1127           0 :             pg_log_error("could not write %d bytes to WAL file \"%s\": %s",
    1128             :                          bytes_to_write, walfile->pathname,
    1129             :                          GetLastWalMethodError(stream->walmethod));
    1130           0 :             return false;
    1131             :         }
    1132             : 
    1133             :         /* Write was successful, advance our position */
    1134        7042 :         bytes_written += bytes_to_write;
    1135        7042 :         bytes_left -= bytes_to_write;
    1136        7042 :         *blockpos += bytes_to_write;
    1137        7042 :         xlogoff += bytes_to_write;
    1138             : 
    1139             :         /* Did we reach the end of a WAL segment? */
    1140        7042 :         if (XLogSegmentOffset(*blockpos, WalSegSz) == 0)
    1141             :         {
    1142          36 :             if (!close_walfile(stream, *blockpos))
    1143             :                 /* Error message written in close_walfile() */
    1144           0 :                 return false;
    1145             : 
    1146          36 :             xlogoff = 0;
    1147             : 
    1148          36 :             if (still_sending && stream->stream_stop(*blockpos, stream->timeline, true))
    1149             :             {
    1150           4 :                 if (PQputCopyEnd(conn, NULL) <= 0 || PQflush(conn))
    1151             :                 {
    1152           0 :                     pg_log_error("could not send copy-end packet: %s",
    1153             :                                  PQerrorMessage(conn));
    1154           0 :                     return false;
    1155             :                 }
    1156           4 :                 still_sending = false;
    1157           4 :                 return true;    /* ignore the rest of this XLogData packet */
    1158             :             }
    1159             :         }
    1160             :     }
    1161             :     /* No more data left to write, receive next copy packet */
    1162             : 
    1163        7038 :     return true;
    1164             : }
    1165             : 
    1166             : /*
    1167             :  * Handle end of the copy stream.
    1168             :  */
    1169             : static PGresult *
    1170         272 : HandleEndOfCopyStream(PGconn *conn, StreamCtl *stream, char *copybuf,
    1171             :                       XLogRecPtr blockpos, XLogRecPtr *stoppos)
    1172             : {
    1173         272 :     PGresult   *res = PQgetResult(conn);
    1174             : 
    1175             :     /*
    1176             :      * The server closed its end of the copy stream.  If we haven't closed
    1177             :      * ours already, we need to do so now, unless the server threw an error,
    1178             :      * in which case we don't.
    1179             :      */
    1180         272 :     if (still_sending)
    1181             :     {
    1182           4 :         if (!close_walfile(stream, blockpos))
    1183             :         {
    1184             :             /* Error message written in close_walfile() */
    1185           0 :             PQclear(res);
    1186           0 :             return NULL;
    1187             :         }
    1188           4 :         if (PQresultStatus(res) == PGRES_COPY_IN)
    1189             :         {
    1190           2 :             if (PQputCopyEnd(conn, NULL) <= 0 || PQflush(conn))
    1191             :             {
    1192           0 :                 pg_log_error("could not send copy-end packet: %s",
    1193             :                              PQerrorMessage(conn));
    1194           0 :                 PQclear(res);
    1195           0 :                 return NULL;
    1196             :             }
    1197           2 :             res = PQgetResult(conn);
    1198             :         }
    1199           4 :         still_sending = false;
    1200             :     }
    1201         272 :     PQfreemem(copybuf);
    1202         272 :     *stoppos = blockpos;
    1203         272 :     return res;
    1204             : }
    1205             : 
    1206             : /*
    1207             :  * Check if we should continue streaming, or abort at this point.
    1208             :  */
    1209             : static bool
    1210       10686 : CheckCopyStreamStop(PGconn *conn, StreamCtl *stream, XLogRecPtr blockpos)
    1211             : {
    1212       10686 :     if (still_sending && stream->stream_stop(blockpos, stream->timeline, false))
    1213             :     {
    1214         264 :         if (!close_walfile(stream, blockpos))
    1215             :         {
    1216             :             /* Potential error message is written by close_walfile */
    1217           0 :             return false;
    1218             :         }
    1219         264 :         if (PQputCopyEnd(conn, NULL) <= 0 || PQflush(conn))
    1220             :         {
    1221           0 :             pg_log_error("could not send copy-end packet: %s",
    1222             :                          PQerrorMessage(conn));
    1223           0 :             return false;
    1224             :         }
    1225         264 :         still_sending = false;
    1226             :     }
    1227             : 
    1228       10686 :     return true;
    1229             : }
    1230             : 
    1231             : /*
    1232             :  * Calculate how long send/receive loops should sleep
    1233             :  */
    1234             : static long
    1235        3262 : CalculateCopyStreamSleeptime(TimestampTz now, int standby_message_timeout,
    1236             :                              TimestampTz last_status)
    1237             : {
    1238        3262 :     TimestampTz status_targettime = 0;
    1239             :     long        sleeptime;
    1240             : 
    1241        3262 :     if (standby_message_timeout && still_sending)
    1242        3074 :         status_targettime = last_status +
    1243        3074 :             (standby_message_timeout - 1) * ((int64) 1000);
    1244             : 
    1245        3262 :     if (status_targettime > 0)
    1246             :     {
    1247             :         long        secs;
    1248             :         int         usecs;
    1249             : 
    1250        3074 :         feTimestampDifference(now,
    1251             :                               status_targettime,
    1252             :                               &secs,
    1253             :                               &usecs);
    1254             :         /* Always sleep at least 1 sec */
    1255        3074 :         if (secs <= 0)
    1256             :         {
    1257           0 :             secs = 1;
    1258           0 :             usecs = 0;
    1259             :         }
    1260             : 
    1261        3074 :         sleeptime = secs * 1000 + usecs / 1000;
    1262             :     }
    1263             :     else
    1264         188 :         sleeptime = -1;
    1265             : 
    1266        3262 :     return sleeptime;
    1267             : }

Generated by: LCOV version 1.14