LCOV - code coverage report
Current view: top level - src/backend/access/transam - xloginsert.c (source / functions) Coverage Total Hit
Test: PostgreSQL 20devel Lines: 84.4 % 404 341
Test Date: 2026-07-27 14:17:20 Functions: 95.0 % 20 19
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 66.1 % 242 160

             Branch data     Line data    Source code
       1                 :             : /*-------------------------------------------------------------------------
       2                 :             :  *
       3                 :             :  * xloginsert.c
       4                 :             :  *      Functions for constructing WAL records
       5                 :             :  *
       6                 :             :  * Constructing a WAL record begins with a call to XLogBeginInsert,
       7                 :             :  * followed by a number of XLogRegister* calls. The registered data is
       8                 :             :  * collected in private working memory, and finally assembled into a chain
       9                 :             :  * of XLogRecData structs by a call to XLogRecordAssemble(). See
      10                 :             :  * access/transam/README for details.
      11                 :             :  *
      12                 :             :  * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
      13                 :             :  * Portions Copyright (c) 1994, Regents of the University of California
      14                 :             :  *
      15                 :             :  * src/backend/access/transam/xloginsert.c
      16                 :             :  *
      17                 :             :  *-------------------------------------------------------------------------
      18                 :             :  */
      19                 :             : 
      20                 :             : #include "postgres.h"
      21                 :             : 
      22                 :             : #ifdef USE_LZ4
      23                 :             : #include <lz4.h>
      24                 :             : #endif
      25                 :             : 
      26                 :             : #ifdef USE_ZSTD
      27                 :             : #include <zstd.h>
      28                 :             : #endif
      29                 :             : 
      30                 :             : #include "access/xact.h"
      31                 :             : #include "access/xlog.h"
      32                 :             : #include "access/xlog_internal.h"
      33                 :             : #include "access/xloginsert.h"
      34                 :             : #include "catalog/pg_control.h"
      35                 :             : #include "common/pg_lzcompress.h"
      36                 :             : #include "executor/instrument.h"
      37                 :             : #include "miscadmin.h"
      38                 :             : #include "pg_trace.h"
      39                 :             : #include "replication/origin.h"
      40                 :             : #include "storage/bufmgr.h"
      41                 :             : #include "storage/proc.h"
      42                 :             : #include "utils/memutils.h"
      43                 :             : #include "utils/pgstat_internal.h"
      44                 :             : #include "utils/rel.h"
      45                 :             : 
      46                 :             : /*
      47                 :             :  * Guess the maximum buffer size required to store a compressed version of
      48                 :             :  * backup block image.
      49                 :             :  */
      50                 :             : #ifdef USE_LZ4
      51                 :             : #define LZ4_MAX_BLCKSZ      LZ4_COMPRESSBOUND(BLCKSZ)
      52                 :             : #else
      53                 :             : #define LZ4_MAX_BLCKSZ      0
      54                 :             : #endif
      55                 :             : 
      56                 :             : #ifdef USE_ZSTD
      57                 :             : #define ZSTD_MAX_BLCKSZ     ZSTD_COMPRESSBOUND(BLCKSZ)
      58                 :             : #else
      59                 :             : #define ZSTD_MAX_BLCKSZ     0
      60                 :             : #endif
      61                 :             : 
      62                 :             : #define PGLZ_MAX_BLCKSZ     PGLZ_MAX_OUTPUT(BLCKSZ)
      63                 :             : 
      64                 :             : /* Buffer size required to store a compressed version of backup block image */
      65                 :             : #define COMPRESS_BUFSIZE    Max(Max(PGLZ_MAX_BLCKSZ, LZ4_MAX_BLCKSZ), ZSTD_MAX_BLCKSZ)
      66                 :             : 
      67                 :             : /*
      68                 :             :  * For each block reference registered with XLogRegisterBuffer, we fill in
      69                 :             :  * a registered_buffer struct.
      70                 :             :  */
      71                 :             : typedef struct
      72                 :             : {
      73                 :             :     bool        in_use;         /* is this slot in use? */
      74                 :             :     uint8       flags;          /* REGBUF_* flags */
      75                 :             :     RelFileLocator rlocator;    /* identifies the relation and block */
      76                 :             :     ForkNumber  forkno;
      77                 :             :     BlockNumber block;
      78                 :             :     const PageData *page;       /* page content */
      79                 :             :     uint32      rdata_len;      /* total length of data in rdata chain */
      80                 :             :     XLogRecData *rdata_head;    /* head of the chain of data registered with
      81                 :             :                                  * this block */
      82                 :             :     XLogRecData *rdata_tail;    /* last entry in the chain, or &rdata_head if
      83                 :             :                                  * empty */
      84                 :             : 
      85                 :             :     XLogRecData bkp_rdatas[2];  /* temporary rdatas used to hold references to
      86                 :             :                                  * backup block data in XLogRecordAssemble() */
      87                 :             : 
      88                 :             :     /* buffer to store a compressed version of backup block image */
      89                 :             :     char        compressed_page[COMPRESS_BUFSIZE];
      90                 :             : } registered_buffer;
      91                 :             : 
      92                 :             : static registered_buffer *registered_buffers;
      93                 :             : static int  max_registered_buffers; /* allocated size */
      94                 :             : static int  max_registered_block_id = 0;    /* highest block_id + 1 currently
      95                 :             :                                              * registered */
      96                 :             : 
      97                 :             : /*
      98                 :             :  * A chain of XLogRecDatas to hold the "main data" of a WAL record, registered
      99                 :             :  * with XLogRegisterData(...).
     100                 :             :  */
     101                 :             : static XLogRecData *mainrdata_head;
     102                 :             : static XLogRecData *mainrdata_last = (XLogRecData *) &mainrdata_head;
     103                 :             : static uint64 mainrdata_len;    /* total # of bytes in chain */
     104                 :             : 
     105                 :             : /* flags for the in-progress insertion */
     106                 :             : static uint8 curinsert_flags = 0;
     107                 :             : 
     108                 :             : /*
     109                 :             :  * These are used to hold the record header while constructing a record.
     110                 :             :  * 'hdr_scratch' is not a plain variable, but is palloc'd at initialization,
     111                 :             :  * because we want it to be MAXALIGNed and padding bytes zeroed.
     112                 :             :  *
     113                 :             :  * For simplicity, it's allocated large enough to hold the headers for any
     114                 :             :  * WAL record.
     115                 :             :  */
     116                 :             : static XLogRecData hdr_rdt;
     117                 :             : static char *hdr_scratch = NULL;
     118                 :             : 
     119                 :             : #define SizeOfXlogOrigin    (sizeof(ReplOriginId) + sizeof(char))
     120                 :             : #define SizeOfXLogTransactionId (sizeof(TransactionId) + sizeof(char))
     121                 :             : 
     122                 :             : #define HEADER_SCRATCH_SIZE \
     123                 :             :     (SizeOfXLogRecord + \
     124                 :             :      MaxSizeOfXLogRecordBlockHeader * (XLR_MAX_BLOCK_ID + 1) + \
     125                 :             :      SizeOfXLogRecordDataHeaderLong + SizeOfXlogOrigin + \
     126                 :             :      SizeOfXLogTransactionId)
     127                 :             : 
     128                 :             : /*
     129                 :             :  * An array of XLogRecData structs, to hold registered data.
     130                 :             :  */
     131                 :             : static XLogRecData *rdatas;
     132                 :             : static int  num_rdatas;         /* entries currently used */
     133                 :             : static int  max_rdatas;         /* allocated size */
     134                 :             : 
     135                 :             : static bool begininsert_called = false;
     136                 :             : 
     137                 :             : /* Memory context to hold the registered buffer and data references. */
     138                 :             : static MemoryContext xloginsert_cxt;
     139                 :             : 
     140                 :             : static XLogRecData *XLogRecordAssemble(RmgrId rmid, uint8 info,
     141                 :             :                                        XLogRecPtr RedoRecPtr, bool doPageWrites,
     142                 :             :                                        XLogRecPtr *fpw_lsn, int *num_fpi,
     143                 :             :                                        uint64 *fpi_bytes,
     144                 :             :                                        bool *topxid_included);
     145                 :             : static bool XLogCompressBackupBlock(const PageData *page, uint16 hole_offset,
     146                 :             :                                     uint16 hole_length, void *dest, uint16 *dlen);
     147                 :             : 
     148                 :             : /*
     149                 :             :  * Begin constructing a WAL record. This must be called before the
     150                 :             :  * XLogRegister* functions and XLogInsert().
     151                 :             :  */
     152                 :             : void
     153                 :    26027342 : XLogBeginInsert(void)
     154                 :             : {
     155                 :             :     Assert(max_registered_block_id == 0);
     156                 :             :     Assert(mainrdata_last == (XLogRecData *) &mainrdata_head);
     157                 :             :     Assert(mainrdata_len == 0);
     158                 :             : 
     159                 :             :     /* cross-check on whether we should be here or not */
     160         [ -  + ]:    26027342 :     if (!XLogInsertAllowed())
     161         [ #  # ]:           0 :         elog(ERROR, "cannot make new WAL entries during recovery");
     162                 :             : 
     163         [ -  + ]:    26027342 :     if (begininsert_called)
     164         [ #  # ]:           0 :         elog(ERROR, "XLogBeginInsert was already called");
     165                 :             : 
     166                 :    26027342 :     begininsert_called = true;
     167                 :    26027342 : }
     168                 :             : 
     169                 :             : /*
     170                 :             :  * Ensure that there are enough buffer and data slots in the working area,
     171                 :             :  * for subsequent XLogRegisterBuffer, XLogRegisterData and XLogRegisterBufData
     172                 :             :  * calls.
     173                 :             :  *
     174                 :             :  * There is always space for a small number of buffers and data chunks, enough
     175                 :             :  * for most record types. This function is for the exceptional cases that need
     176                 :             :  * more.
     177                 :             :  */
     178                 :             : void
     179                 :       74549 : XLogEnsureRecordSpace(int max_block_id, int ndatas)
     180                 :             : {
     181                 :             :     int         nbuffers;
     182                 :             : 
     183                 :             :     /*
     184                 :             :      * This must be called before entering a critical section, because
     185                 :             :      * allocating memory inside a critical section can fail. repalloc() will
     186                 :             :      * check the same, but better to check it here too so that we fail
     187                 :             :      * consistently even if the arrays happen to be large enough already.
     188                 :             :      */
     189                 :             :     Assert(CritSectionCount == 0);
     190                 :             : 
     191                 :             :     /* the minimum values can't be decreased */
     192         [ +  + ]:       74549 :     if (max_block_id < XLR_NORMAL_MAX_BLOCK_ID)
     193                 :        2555 :         max_block_id = XLR_NORMAL_MAX_BLOCK_ID;
     194         [ +  + ]:       74549 :     if (ndatas < XLR_NORMAL_RDATAS)
     195                 :       74516 :         ndatas = XLR_NORMAL_RDATAS;
     196                 :             : 
     197         [ -  + ]:       74549 :     if (max_block_id > XLR_MAX_BLOCK_ID)
     198         [ #  # ]:           0 :         elog(ERROR, "maximum number of WAL record block references exceeded");
     199                 :       74549 :     nbuffers = max_block_id + 1;
     200                 :             : 
     201         [ +  + ]:       74549 :     if (nbuffers > max_registered_buffers)
     202                 :             :     {
     203                 :        2008 :         registered_buffers = (registered_buffer *)
     204                 :        2008 :             repalloc(registered_buffers, sizeof(registered_buffer) * nbuffers);
     205                 :             : 
     206                 :             :         /*
     207                 :             :          * At least the padding bytes in the structs must be zeroed, because
     208                 :             :          * they are included in WAL data, but initialize it all for tidiness.
     209                 :             :          */
     210   [ +  -  +  -  :        2008 :         MemSet(&registered_buffers[max_registered_buffers], 0,
          +  -  -  +  -  
                      - ]
     211                 :             :                (nbuffers - max_registered_buffers) * sizeof(registered_buffer));
     212                 :        2008 :         max_registered_buffers = nbuffers;
     213                 :             :     }
     214                 :             : 
     215         [ +  + ]:       74549 :     if (ndatas > max_rdatas)
     216                 :             :     {
     217                 :          21 :         rdatas = (XLogRecData *) repalloc(rdatas, sizeof(XLogRecData) * ndatas);
     218                 :          21 :         max_rdatas = ndatas;
     219                 :             :     }
     220                 :       74549 : }
     221                 :             : 
     222                 :             : /*
     223                 :             :  * Reset WAL record construction buffers.
     224                 :             :  */
     225                 :             : void
     226                 :    26068830 : XLogResetInsertion(void)
     227                 :             : {
     228                 :             :     int         i;
     229                 :             : 
     230         [ +  + ]:    54169725 :     for (i = 0; i < max_registered_block_id; i++)
     231                 :    28100895 :         registered_buffers[i].in_use = false;
     232                 :             : 
     233                 :    26068830 :     num_rdatas = 0;
     234                 :    26068830 :     max_registered_block_id = 0;
     235                 :    26068830 :     mainrdata_len = 0;
     236                 :    26068830 :     mainrdata_last = (XLogRecData *) &mainrdata_head;
     237                 :    26068830 :     curinsert_flags = 0;
     238                 :    26068830 :     begininsert_called = false;
     239                 :    26068830 : }
     240                 :             : 
     241                 :             : /*
     242                 :             :  * Register a reference to a buffer with the WAL record being constructed.
     243                 :             :  * This must be called for every page that the WAL-logged operation modifies.
     244                 :             :  */
     245                 :             : void
     246                 :    27730307 : XLogRegisterBuffer(uint8 block_id, Buffer buffer, uint8 flags)
     247                 :             : {
     248                 :             :     registered_buffer *regbuf;
     249                 :             : 
     250                 :             :     /* NO_IMAGE doesn't make sense with FORCE_IMAGE */
     251                 :             :     Assert(!((flags & REGBUF_FORCE_IMAGE) && (flags & (REGBUF_NO_IMAGE))));
     252                 :             :     Assert(begininsert_called);
     253                 :             : 
     254                 :             :     /*
     255                 :             :      * Ordinarily, the buffer should be exclusive-locked (or share-exclusive
     256                 :             :      * in case of hint bits) and marked dirty before we get here, otherwise we
     257                 :             :      * could end up violating one of the rules in access/transam/README.
     258                 :             :      *
     259                 :             :      * Some callers intentionally register a clean page and never update that
     260                 :             :      * page's LSN; in that case they can pass the flag REGBUF_NO_CHANGE to
     261                 :             :      * bypass these checks.
     262                 :             :      */
     263                 :             : #ifdef USE_ASSERT_CHECKING
     264                 :             :     if (!(flags & REGBUF_NO_CHANGE))
     265                 :             :     {
     266                 :             :         Assert(BufferIsDirty(buffer));
     267                 :             :         Assert(BufferIsLockedByMeInMode(buffer, BUFFER_LOCK_EXCLUSIVE) ||
     268                 :             :                BufferIsLockedByMeInMode(buffer, BUFFER_LOCK_SHARE_EXCLUSIVE));
     269                 :             :     }
     270                 :             : #endif
     271                 :             : 
     272         [ +  + ]:    27730307 :     if (block_id >= max_registered_block_id)
     273                 :             :     {
     274         [ -  + ]:    27134600 :         if (block_id >= max_registered_buffers)
     275         [ #  # ]:           0 :             elog(ERROR, "too many registered buffers");
     276                 :    27134600 :         max_registered_block_id = block_id + 1;
     277                 :             :     }
     278                 :             : 
     279                 :    27730307 :     regbuf = &registered_buffers[block_id];
     280                 :             : 
     281                 :    27730307 :     BufferGetTag(buffer, &regbuf->rlocator, &regbuf->forkno, &regbuf->block);
     282                 :    27730307 :     regbuf->page = BufferGetPage(buffer);
     283                 :    27730307 :     regbuf->flags = flags;
     284                 :    27730307 :     regbuf->rdata_tail = (XLogRecData *) &regbuf->rdata_head;
     285                 :    27730307 :     regbuf->rdata_len = 0;
     286                 :             : 
     287                 :             :     /*
     288                 :             :      * Check that this page hasn't already been registered with some other
     289                 :             :      * block_id.
     290                 :             :      */
     291                 :             : #ifdef USE_ASSERT_CHECKING
     292                 :             :     {
     293                 :             :         int         i;
     294                 :             : 
     295                 :             :         for (i = 0; i < max_registered_block_id; i++)
     296                 :             :         {
     297                 :             :             registered_buffer *regbuf_old = &registered_buffers[i];
     298                 :             : 
     299                 :             :             if (i == block_id || !regbuf_old->in_use)
     300                 :             :                 continue;
     301                 :             : 
     302                 :             :             Assert(!RelFileLocatorEquals(regbuf_old->rlocator, regbuf->rlocator) ||
     303                 :             :                    regbuf_old->forkno != regbuf->forkno ||
     304                 :             :                    regbuf_old->block != regbuf->block);
     305                 :             :         }
     306                 :             :     }
     307                 :             : #endif
     308                 :             : 
     309                 :    27730307 :     regbuf->in_use = true;
     310                 :    27730307 : }
     311                 :             : 
     312                 :             : /*
     313                 :             :  * Like XLogRegisterBuffer, but for registering a block that's not in the
     314                 :             :  * shared buffer pool (i.e. when you don't have a Buffer for it).
     315                 :             :  */
     316                 :             : void
     317                 :      353454 : XLogRegisterBlock(uint8 block_id, RelFileLocator *rlocator, ForkNumber forknum,
     318                 :             :                   BlockNumber blknum, const PageData *page, uint8 flags)
     319                 :             : {
     320                 :             :     registered_buffer *regbuf;
     321                 :             : 
     322                 :             :     Assert(begininsert_called);
     323                 :             : 
     324         [ +  - ]:      353454 :     if (block_id >= max_registered_block_id)
     325                 :      353454 :         max_registered_block_id = block_id + 1;
     326                 :             : 
     327         [ -  + ]:      353454 :     if (block_id >= max_registered_buffers)
     328         [ #  # ]:           0 :         elog(ERROR, "too many registered buffers");
     329                 :             : 
     330                 :      353454 :     regbuf = &registered_buffers[block_id];
     331                 :             : 
     332                 :      353454 :     regbuf->rlocator = *rlocator;
     333                 :      353454 :     regbuf->forkno = forknum;
     334                 :      353454 :     regbuf->block = blknum;
     335                 :      353454 :     regbuf->page = page;
     336                 :      353454 :     regbuf->flags = flags;
     337                 :      353454 :     regbuf->rdata_tail = (XLogRecData *) &regbuf->rdata_head;
     338                 :      353454 :     regbuf->rdata_len = 0;
     339                 :             : 
     340                 :             :     /*
     341                 :             :      * Check that this page hasn't already been registered with some other
     342                 :             :      * block_id.
     343                 :             :      */
     344                 :             : #ifdef USE_ASSERT_CHECKING
     345                 :             :     {
     346                 :             :         int         i;
     347                 :             : 
     348                 :             :         for (i = 0; i < max_registered_block_id; i++)
     349                 :             :         {
     350                 :             :             registered_buffer *regbuf_old = &registered_buffers[i];
     351                 :             : 
     352                 :             :             if (i == block_id || !regbuf_old->in_use)
     353                 :             :                 continue;
     354                 :             : 
     355                 :             :             Assert(!RelFileLocatorEquals(regbuf_old->rlocator, regbuf->rlocator) ||
     356                 :             :                    regbuf_old->forkno != regbuf->forkno ||
     357                 :             :                    regbuf_old->block != regbuf->block);
     358                 :             :         }
     359                 :             :     }
     360                 :             : #endif
     361                 :             : 
     362                 :      353454 :     regbuf->in_use = true;
     363                 :      353454 : }
     364                 :             : 
     365                 :             : /*
     366                 :             :  * Add data to the WAL record that's being constructed.
     367                 :             :  *
     368                 :             :  * The data is appended to the "main chunk", available at replay with
     369                 :             :  * XLogRecGetData().
     370                 :             :  */
     371                 :             : void
     372                 :    26779269 : XLogRegisterData(const void *data, uint32 len)
     373                 :             : {
     374                 :             :     XLogRecData *rdata;
     375                 :             : 
     376                 :             :     Assert(begininsert_called);
     377                 :             : 
     378         [ -  + ]:    26779269 :     if (num_rdatas >= max_rdatas)
     379         [ #  # ]:           0 :         ereport(ERROR,
     380                 :             :                 (errmsg_internal("too much WAL data"),
     381                 :             :                  errdetail_internal("%d out of %d data segments are already in use.",
     382                 :             :                                     num_rdatas, max_rdatas)));
     383                 :    26779269 :     rdata = &rdatas[num_rdatas++];
     384                 :             : 
     385                 :    26779269 :     rdata->data = data;
     386                 :    26779269 :     rdata->len = len;
     387                 :             : 
     388                 :             :     /*
     389                 :             :      * we use the mainrdata_last pointer to track the end of the chain, so no
     390                 :             :      * need to clear 'next' here.
     391                 :             :      */
     392                 :             : 
     393                 :    26779269 :     mainrdata_last->next = rdata;
     394                 :    26779269 :     mainrdata_last = rdata;
     395                 :             : 
     396                 :    26779269 :     mainrdata_len += len;
     397                 :    26779269 : }
     398                 :             : 
     399                 :             : /*
     400                 :             :  * Add buffer-specific data to the WAL record that's being constructed.
     401                 :             :  *
     402                 :             :  * Block_id must reference a block previously registered with
     403                 :             :  * XLogRegisterBuffer(). If this is called more than once for the same
     404                 :             :  * block_id, the data is appended.
     405                 :             :  *
     406                 :             :  * The maximum amount of data that can be registered per block is 65535
     407                 :             :  * bytes. That should be plenty; if you need more than BLCKSZ bytes to
     408                 :             :  * reconstruct the changes to the page, you might as well just log a full
     409                 :             :  * copy of it. (the "main data" that's not associated with a block is not
     410                 :             :  * limited)
     411                 :             :  */
     412                 :             : void
     413                 :    34571151 : XLogRegisterBufData(uint8 block_id, const void *data, uint32 len)
     414                 :             : {
     415                 :             :     registered_buffer *regbuf;
     416                 :             :     XLogRecData *rdata;
     417                 :             : 
     418                 :             :     Assert(begininsert_called);
     419                 :             : 
     420                 :             :     /* find the registered buffer struct */
     421                 :    34571151 :     regbuf = &registered_buffers[block_id];
     422         [ -  + ]:    34571151 :     if (!regbuf->in_use)
     423         [ #  # ]:           0 :         elog(ERROR, "no block with id %d registered with WAL insertion",
     424                 :             :              block_id);
     425                 :             : 
     426                 :             :     /*
     427                 :             :      * Check against max_rdatas and ensure we do not register more data per
     428                 :             :      * buffer than can be handled by the physical data format; i.e. that
     429                 :             :      * regbuf->rdata_len does not grow beyond what
     430                 :             :      * XLogRecordBlockHeader->data_length can hold.
     431                 :             :      */
     432         [ -  + ]:    34571151 :     if (num_rdatas >= max_rdatas)
     433         [ #  # ]:           0 :         ereport(ERROR,
     434                 :             :                 (errmsg_internal("too much WAL data"),
     435                 :             :                  errdetail_internal("%d out of %d data segments are already in use.",
     436                 :             :                                     num_rdatas, max_rdatas)));
     437   [ +  -  -  + ]:    34571151 :     if (regbuf->rdata_len + len > UINT16_MAX || len > UINT16_MAX)
     438         [ #  # ]:           0 :         ereport(ERROR,
     439                 :             :                 (errmsg_internal("too much WAL data"),
     440                 :             :                  errdetail_internal("Registering more than maximum %u bytes allowed to block %u: current %u bytes, adding %u bytes.",
     441                 :             :                                     UINT16_MAX, block_id, regbuf->rdata_len, len)));
     442                 :             : 
     443                 :    34571151 :     rdata = &rdatas[num_rdatas++];
     444                 :             : 
     445                 :    34571151 :     rdata->data = data;
     446                 :    34571151 :     rdata->len = len;
     447                 :             : 
     448                 :    34571151 :     regbuf->rdata_tail->next = rdata;
     449                 :    34571151 :     regbuf->rdata_tail = rdata;
     450                 :    34571151 :     regbuf->rdata_len += len;
     451                 :    34571151 : }
     452                 :             : 
     453                 :             : /*
     454                 :             :  * Set insert status flags for the upcoming WAL record.
     455                 :             :  *
     456                 :             :  * The flags that can be used here are:
     457                 :             :  * - XLOG_INCLUDE_ORIGIN, to determine if the replication origin should be
     458                 :             :  *   included in the record.
     459                 :             :  * - XLOG_MARK_UNIMPORTANT, to signal that the record is not important for
     460                 :             :  *   durability, which allows to avoid triggering WAL archiving and other
     461                 :             :  *   background activity.
     462                 :             :  */
     463                 :             : void
     464                 :    15811766 : XLogSetRecordFlags(uint8 flags)
     465                 :             : {
     466                 :             :     Assert(begininsert_called);
     467                 :    15811766 :     curinsert_flags |= flags;
     468                 :    15811766 : }
     469                 :             : 
     470                 :             : /*
     471                 :             :  * Insert an XLOG record having the specified RMID and info bytes, with the
     472                 :             :  * body of the record being the data and buffer references registered earlier
     473                 :             :  * with XLogRegister* calls.
     474                 :             :  *
     475                 :             :  * Returns XLOG pointer to end of record (beginning of next record).
     476                 :             :  * This can be used as LSN for data pages affected by the logged action.
     477                 :             :  * (LSN is the XLOG point up to which the XLOG must be flushed to disk
     478                 :             :  * before the data page can be written out.  This implements the basic
     479                 :             :  * WAL rule "write the log before the data".)
     480                 :             :  */
     481                 :             : XLogRecPtr
     482                 :    26027342 : XLogInsert(RmgrId rmid, uint8 info)
     483                 :             : {
     484                 :             :     XLogRecPtr  EndPos;
     485                 :             : 
     486                 :             :     /* XLogBeginInsert() must have been called. */
     487         [ -  + ]:    26027342 :     if (!begininsert_called)
     488         [ #  # ]:           0 :         elog(ERROR, "XLogBeginInsert was not called");
     489                 :             : 
     490                 :             :     /*
     491                 :             :      * The caller can set rmgr bits, XLR_SPECIAL_REL_UPDATE and
     492                 :             :      * XLR_CHECK_CONSISTENCY; the rest are reserved for use by me.
     493                 :             :      */
     494         [ -  + ]:    26027342 :     if ((info & ~(XLR_RMGR_INFO_MASK |
     495                 :             :                   XLR_SPECIAL_REL_UPDATE |
     496                 :             :                   XLR_CHECK_CONSISTENCY)) != 0)
     497         [ #  # ]:           0 :         elog(PANIC, "invalid xlog info mask %02X", info);
     498                 :             : 
     499                 :             :     TRACE_POSTGRESQL_WAL_INSERT(rmid, info);
     500                 :             : 
     501                 :             :     /*
     502                 :             :      * In bootstrap mode, we don't actually log anything but XLOG resources;
     503                 :             :      * return a phony record pointer.
     504                 :             :      */
     505   [ +  +  +  + ]:    26027342 :     if (IsBootstrapProcessingMode() && rmid != RM_XLOG_ID)
     506                 :             :     {
     507                 :      723045 :         XLogResetInsertion();
     508                 :      723045 :         EndPos = SizeOfXLogLongPHD; /* start of 1st chkpt record */
     509                 :      723045 :         return EndPos;
     510                 :             :     }
     511                 :             : 
     512                 :             :     do
     513                 :             :     {
     514                 :             :         XLogRecPtr  RedoRecPtr;
     515                 :             :         bool        doPageWrites;
     516                 :    25313289 :         bool        topxid_included = false;
     517                 :             :         XLogRecPtr  fpw_lsn;
     518                 :             :         XLogRecData *rdt;
     519                 :    25313289 :         int         num_fpi = 0;
     520                 :    25313289 :         uint64      fpi_bytes = 0;
     521                 :             : 
     522                 :             :         /*
     523                 :             :          * Get values needed to decide whether to do full-page writes. Since
     524                 :             :          * we don't yet have an insertion lock, these could change under us,
     525                 :             :          * but XLogInsertRecord will recheck them once it has a lock.
     526                 :             :          */
     527                 :    25313289 :         GetFullPageWriteInfo(&RedoRecPtr, &doPageWrites);
     528                 :             : 
     529                 :    25313289 :         rdt = XLogRecordAssemble(rmid, info, RedoRecPtr, doPageWrites,
     530                 :             :                                  &fpw_lsn, &num_fpi, &fpi_bytes,
     531                 :             :                                  &topxid_included);
     532                 :             : 
     533                 :    25313289 :         EndPos = XLogInsertRecord(rdt, fpw_lsn, curinsert_flags, num_fpi,
     534                 :             :                                   fpi_bytes, topxid_included);
     535         [ +  + ]:    25313289 :     } while (!XLogRecPtrIsValid(EndPos));
     536                 :             : 
     537                 :    25304297 :     XLogResetInsertion();
     538                 :             : 
     539                 :    25304297 :     return EndPos;
     540                 :             : }
     541                 :             : 
     542                 :             : /*
     543                 :             :  * Simple wrapper to XLogInsert to insert a WAL record with elementary
     544                 :             :  * contents (only an int64 is supported as value currently).
     545                 :             :  */
     546                 :             : XLogRecPtr
     547                 :      432077 : XLogSimpleInsertInt64(RmgrId rmid, uint8 info, int64 value)
     548                 :             : {
     549                 :      432077 :     XLogBeginInsert();
     550                 :      432077 :     XLogRegisterData(&value, sizeof(value));
     551                 :      432077 :     return XLogInsert(rmid, info);
     552                 :             : }
     553                 :             : 
     554                 :             : /*
     555                 :             :  * XLogGetFakeLSN - get a fake LSN for an index page that isn't WAL-logged.
     556                 :             :  *
     557                 :             :  * Some index AMs use LSNs to detect concurrent page modifications, but not
     558                 :             :  * all index pages are WAL-logged.  This function provides a sequence of fake
     559                 :             :  * LSNs for that purpose.
     560                 :             :  */
     561                 :             : XLogRecPtr
     562                 :      256369 : XLogGetFakeLSN(Relation rel)
     563                 :             : {
     564         [ +  + ]:      256369 :     if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
     565                 :             :     {
     566                 :             :         /*
     567                 :             :          * Temporary relations are only accessible in our session, so a simple
     568                 :             :          * backend-local counter will do.
     569                 :             :          */
     570                 :             :         static XLogRecPtr counter = FirstNormalUnloggedLSN;
     571                 :             : 
     572                 :       53221 :         return counter++;
     573                 :             :     }
     574         [ +  + ]:      203148 :     else if (rel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED)
     575                 :             :     {
     576                 :             :         /*
     577                 :             :          * Unlogged relations are accessible from other backends, and survive
     578                 :             :          * (clean) restarts.  GetFakeLSNForUnloggedRel() handles that for us.
     579                 :             :          */
     580                 :      202671 :         return GetFakeLSNForUnloggedRel();
     581                 :             :     }
     582                 :             :     else
     583                 :             :     {
     584                 :             :         /*
     585                 :             :          * WAL-logging on this relation will start after commit, so its LSNs
     586                 :             :          * must be distinct numbers smaller than the LSN at the next commit.
     587                 :             :          * Emit a dummy WAL record if insert-LSN hasn't advanced after the
     588                 :             :          * last call.
     589                 :             :          */
     590                 :             :         static XLogRecPtr lastlsn = InvalidXLogRecPtr;
     591                 :         477 :         XLogRecPtr  currlsn = GetXLogInsertEndRecPtr();
     592                 :             : 
     593                 :             :         Assert(!RelationNeedsWAL(rel));
     594                 :             :         Assert(RelationIsPermanent(rel));
     595                 :             : 
     596                 :             :         /* No need for an actual record if we already have a distinct LSN */
     597   [ +  +  +  + ]:         477 :         if (XLogRecPtrIsValid(lastlsn) && lastlsn == currlsn)
     598                 :         439 :             currlsn = XLogAssignLSN();
     599                 :             : 
     600                 :         477 :         lastlsn = currlsn;
     601                 :         477 :         return currlsn;
     602                 :             :     }
     603                 :             : }
     604                 :             : 
     605                 :             : /*
     606                 :             :  * Assemble a WAL record from the registered data and buffers into an
     607                 :             :  * XLogRecData chain, ready for insertion with XLogInsertRecord().
     608                 :             :  *
     609                 :             :  * The record header fields are filled in, except for the xl_prev field. The
     610                 :             :  * calculated CRC does not include the record header yet.
     611                 :             :  *
     612                 :             :  * If there are any registered buffers, and a full-page image was not taken
     613                 :             :  * of all of them, *fpw_lsn is set to the lowest LSN among such pages. This
     614                 :             :  * signals that the assembled record is only good for insertion on the
     615                 :             :  * assumption that the RedoRecPtr and doPageWrites values were up-to-date.
     616                 :             :  *
     617                 :             :  * *topxid_included is set if the topmost transaction ID is logged with the
     618                 :             :  * current subtransaction.
     619                 :             :  */
     620                 :             : static XLogRecData *
     621                 :    25313289 : XLogRecordAssemble(RmgrId rmid, uint8 info,
     622                 :             :                    XLogRecPtr RedoRecPtr, bool doPageWrites,
     623                 :             :                    XLogRecPtr *fpw_lsn, int *num_fpi, uint64 *fpi_bytes,
     624                 :             :                    bool *topxid_included)
     625                 :             : {
     626                 :             :     XLogRecData *rdt;
     627                 :    25313289 :     uint64      total_len = 0;
     628                 :             :     int         block_id;
     629                 :             :     pg_crc32c   rdata_crc;
     630                 :    25313289 :     registered_buffer *prev_regbuf = NULL;
     631                 :             :     XLogRecData *rdt_datas_last;
     632                 :             :     XLogRecord *rechdr;
     633                 :    25313289 :     char       *scratch = hdr_scratch;
     634                 :             : 
     635                 :             :     /*
     636                 :             :      * Note: this function can be called multiple times for the same record.
     637                 :             :      * All the modifications we do to the rdata chains below must handle that.
     638                 :             :      */
     639                 :             : 
     640                 :             :     /* The record begins with the fixed-size header */
     641                 :    25313289 :     rechdr = (XLogRecord *) scratch;
     642                 :    25313289 :     scratch += SizeOfXLogRecord;
     643                 :             : 
     644                 :    25313289 :     hdr_rdt.next = NULL;
     645                 :    25313289 :     rdt_datas_last = &hdr_rdt;
     646                 :    25313289 :     hdr_rdt.data = hdr_scratch;
     647                 :             : 
     648                 :             :     /*
     649                 :             :      * Enforce consistency checks for this record if user is looking for it.
     650                 :             :      * Do this before at the beginning of this routine to give the possibility
     651                 :             :      * for callers of XLogInsert() to pass XLR_CHECK_CONSISTENCY directly for
     652                 :             :      * a record.
     653                 :             :      */
     654         [ +  + ]:    25313289 :     if (wal_consistency_checking[rmid])
     655                 :     2283801 :         info |= XLR_CHECK_CONSISTENCY;
     656                 :             : 
     657                 :             :     /*
     658                 :             :      * Make an rdata chain containing all the data portions of all block
     659                 :             :      * references. This includes the data for full-page images. Also append
     660                 :             :      * the headers for the block references in the scratch buffer.
     661                 :             :      */
     662                 :    25313289 :     *fpw_lsn = InvalidXLogRecPtr;
     663         [ +  + ]:    52728331 :     for (block_id = 0; block_id < max_registered_block_id; block_id++)
     664                 :             :     {
     665                 :    27415042 :         registered_buffer *regbuf = &registered_buffers[block_id];
     666                 :             :         bool        needs_backup;
     667                 :             :         bool        needs_data;
     668                 :             :         XLogRecordBlockHeader bkpb;
     669                 :             :         XLogRecordBlockImageHeader bimg;
     670                 :    27415042 :         XLogRecordBlockCompressHeader cbimg = {0};
     671                 :             :         bool        samerel;
     672                 :    27415042 :         bool        is_compressed = false;
     673                 :             :         bool        include_image;
     674                 :             : 
     675         [ +  + ]:    27415042 :         if (!regbuf->in_use)
     676                 :       17178 :             continue;
     677                 :             : 
     678                 :             :         /* Determine if this block needs to be backed up */
     679         [ +  + ]:    27397864 :         if (regbuf->flags & REGBUF_FORCE_IMAGE)
     680                 :      388235 :             needs_backup = true;
     681         [ +  + ]:    27009629 :         else if (regbuf->flags & REGBUF_NO_IMAGE)
     682                 :      294691 :             needs_backup = false;
     683         [ +  + ]:    26714938 :         else if (!doPageWrites)
     684                 :     3332219 :             needs_backup = false;
     685                 :             :         else
     686                 :             :         {
     687                 :             :             /*
     688                 :             :              * We assume page LSN is first data on *every* page that can be
     689                 :             :              * passed to XLogInsert, whether it has the standard page layout
     690                 :             :              * or not.
     691                 :             :              */
     692                 :    23382719 :             XLogRecPtr  page_lsn = PageGetLSN(regbuf->page);
     693                 :             : 
     694                 :    23382719 :             needs_backup = (page_lsn <= RedoRecPtr);
     695         [ +  + ]:    23382719 :             if (!needs_backup)
     696                 :             :             {
     697   [ +  +  +  + ]:    23270755 :                 if (!XLogRecPtrIsValid(*fpw_lsn) || page_lsn < *fpw_lsn)
     698                 :    21566148 :                     *fpw_lsn = page_lsn;
     699                 :             :             }
     700                 :             :         }
     701                 :             : 
     702                 :             :         /* Determine if the buffer data needs to included */
     703         [ +  + ]:    27397864 :         if (regbuf->rdata_len == 0)
     704                 :     7768392 :             needs_data = false;
     705         [ +  + ]:    19629472 :         else if ((regbuf->flags & REGBUF_KEEP_DATA) != 0)
     706                 :      320542 :             needs_data = true;
     707                 :             :         else
     708                 :    19308930 :             needs_data = !needs_backup;
     709                 :             : 
     710                 :    27397864 :         bkpb.id = block_id;
     711                 :    27397864 :         bkpb.fork_flags = regbuf->forkno;
     712                 :    27397864 :         bkpb.data_length = 0;
     713                 :             : 
     714         [ +  + ]:    27397864 :         if ((regbuf->flags & REGBUF_WILL_INIT) == REGBUF_WILL_INIT)
     715                 :      291850 :             bkpb.fork_flags |= BKPBLOCK_WILL_INIT;
     716                 :             : 
     717                 :             :         /*
     718                 :             :          * If needs_backup is true or WAL checking is enabled for current
     719                 :             :          * resource manager, log a full-page write for the current block.
     720                 :             :          */
     721   [ +  +  +  + ]:    27397864 :         include_image = needs_backup || (info & XLR_CHECK_CONSISTENCY) != 0;
     722                 :             : 
     723         [ +  + ]:    27397864 :         if (include_image)
     724                 :             :         {
     725                 :     2929451 :             const PageData *page = regbuf->page;
     726                 :     2929451 :             uint16      compressed_len = 0;
     727                 :             : 
     728                 :             :             /*
     729                 :             :              * The page needs to be backed up, so calculate its hole length
     730                 :             :              * and offset.
     731                 :             :              */
     732         [ +  + ]:     2929451 :             if (regbuf->flags & REGBUF_STANDARD)
     733                 :             :             {
     734                 :             :                 /* Assume we can omit data between pd_lower and pd_upper */
     735                 :     2735279 :                 uint16      lower = ((const PageHeaderData *) page)->pd_lower;
     736                 :     2735279 :                 uint16      upper = ((const PageHeaderData *) page)->pd_upper;
     737                 :             : 
     738   [ +  +  +  + ]:     2735279 :                 if (lower >= SizeOfPageHeaderData &&
     739         [ +  - ]:     2732572 :                     upper > lower &&
     740                 :             :                     upper <= BLCKSZ)
     741                 :             :                 {
     742                 :     2732572 :                     bimg.hole_offset = lower;
     743                 :     2732572 :                     cbimg.hole_length = upper - lower;
     744                 :             :                 }
     745                 :             :                 else
     746                 :             :                 {
     747                 :             :                     /* No "hole" to remove */
     748                 :        2707 :                     bimg.hole_offset = 0;
     749                 :        2707 :                     cbimg.hole_length = 0;
     750                 :             :                 }
     751                 :             :             }
     752                 :             :             else
     753                 :             :             {
     754                 :             :                 /* Not a standard page header, don't try to eliminate "hole" */
     755                 :      194172 :                 bimg.hole_offset = 0;
     756                 :      194172 :                 cbimg.hole_length = 0;
     757                 :             :             }
     758                 :             : 
     759                 :             :             /*
     760                 :             :              * Try to compress a block image if wal_compression is enabled
     761                 :             :              */
     762         [ -  + ]:     2929451 :             if (wal_compression != WAL_COMPRESSION_NONE)
     763                 :             :             {
     764                 :             :                 is_compressed =
     765                 :           0 :                     XLogCompressBackupBlock(page, bimg.hole_offset,
     766                 :           0 :                                             cbimg.hole_length,
     767                 :           0 :                                             regbuf->compressed_page,
     768                 :             :                                             &compressed_len);
     769                 :             :             }
     770                 :             : 
     771                 :             :             /*
     772                 :             :              * Fill in the remaining fields in the XLogRecordBlockHeader
     773                 :             :              * struct
     774                 :             :              */
     775                 :     2929451 :             bkpb.fork_flags |= BKPBLOCK_HAS_IMAGE;
     776                 :             : 
     777                 :             :             /* Report a full page image constructed for the WAL record */
     778                 :     2929451 :             *num_fpi += 1;
     779                 :             : 
     780                 :             :             /*
     781                 :             :              * Construct XLogRecData entries for the page content.
     782                 :             :              */
     783                 :     2929451 :             rdt_datas_last->next = &regbuf->bkp_rdatas[0];
     784                 :     2929451 :             rdt_datas_last = rdt_datas_last->next;
     785                 :             : 
     786                 :     2929451 :             bimg.bimg_info = (cbimg.hole_length == 0) ? 0 : BKPIMAGE_HAS_HOLE;
     787                 :             : 
     788                 :             :             /*
     789                 :             :              * If WAL consistency checking is enabled for the resource manager
     790                 :             :              * of this WAL record, a full-page image is included in the record
     791                 :             :              * for the block modified. During redo, the full-page is replayed
     792                 :             :              * only if BKPIMAGE_APPLY is set.
     793                 :             :              */
     794         [ +  + ]:     2929451 :             if (needs_backup)
     795                 :      500199 :                 bimg.bimg_info |= BKPIMAGE_APPLY;
     796                 :             : 
     797         [ -  + ]:     2929451 :             if (is_compressed)
     798                 :             :             {
     799                 :             :                 /* The current compression is stored in the WAL record */
     800                 :           0 :                 bimg.length = compressed_len;
     801                 :             : 
     802                 :             :                 /* Set the compression method used for this block */
     803   [ #  #  #  #  :           0 :                 switch ((WalCompression) wal_compression)
                      # ]
     804                 :             :                 {
     805                 :           0 :                     case WAL_COMPRESSION_PGLZ:
     806                 :           0 :                         bimg.bimg_info |= BKPIMAGE_COMPRESS_PGLZ;
     807                 :           0 :                         break;
     808                 :             : 
     809                 :           0 :                     case WAL_COMPRESSION_LZ4:
     810                 :             : #ifdef USE_LZ4
     811                 :           0 :                         bimg.bimg_info |= BKPIMAGE_COMPRESS_LZ4;
     812                 :             : #else
     813                 :             :                         elog(ERROR, "LZ4 is not supported by this build");
     814                 :             : #endif
     815                 :           0 :                         break;
     816                 :             : 
     817                 :           0 :                     case WAL_COMPRESSION_ZSTD:
     818                 :             : #ifdef USE_ZSTD
     819                 :             :                         bimg.bimg_info |= BKPIMAGE_COMPRESS_ZSTD;
     820                 :             : #else
     821         [ #  # ]:           0 :                         elog(ERROR, "zstd is not supported by this build");
     822                 :             : #endif
     823                 :             :                         break;
     824                 :             : 
     825                 :           0 :                     case WAL_COMPRESSION_NONE:
     826                 :             :                         Assert(false);  /* cannot happen */
     827                 :           0 :                         break;
     828                 :             :                         /* no default case, so that compiler will warn */
     829                 :             :                 }
     830                 :             : 
     831                 :           0 :                 rdt_datas_last->data = regbuf->compressed_page;
     832                 :           0 :                 rdt_datas_last->len = compressed_len;
     833                 :             :             }
     834                 :             :             else
     835                 :             :             {
     836                 :     2929451 :                 bimg.length = BLCKSZ - cbimg.hole_length;
     837                 :             : 
     838         [ +  + ]:     2929451 :                 if (cbimg.hole_length == 0)
     839                 :             :                 {
     840                 :      196879 :                     rdt_datas_last->data = page;
     841                 :      196879 :                     rdt_datas_last->len = BLCKSZ;
     842                 :             :                 }
     843                 :             :                 else
     844                 :             :                 {
     845                 :             :                     /* must skip the hole */
     846                 :     2732572 :                     rdt_datas_last->data = page;
     847                 :     2732572 :                     rdt_datas_last->len = bimg.hole_offset;
     848                 :             : 
     849                 :     2732572 :                     rdt_datas_last->next = &regbuf->bkp_rdatas[1];
     850                 :     2732572 :                     rdt_datas_last = rdt_datas_last->next;
     851                 :             : 
     852                 :     2732572 :                     rdt_datas_last->data =
     853                 :     2732572 :                         page + (bimg.hole_offset + cbimg.hole_length);
     854                 :     2732572 :                     rdt_datas_last->len =
     855                 :     2732572 :                         BLCKSZ - (bimg.hole_offset + cbimg.hole_length);
     856                 :             :                 }
     857                 :             :             }
     858                 :             : 
     859                 :     2929451 :             total_len += bimg.length;
     860                 :             : 
     861                 :             :             /* Track the WAL full page images in bytes */
     862                 :     2929451 :             *fpi_bytes += bimg.length;
     863                 :             :         }
     864                 :             : 
     865         [ +  + ]:    27397864 :         if (needs_data)
     866                 :             :         {
     867                 :             :             /*
     868                 :             :              * When copying to XLogRecordBlockHeader, the length is narrowed
     869                 :             :              * to an uint16.  Double-check that it is still correct.
     870                 :             :              */
     871                 :             :             Assert(regbuf->rdata_len <= UINT16_MAX);
     872                 :             : 
     873                 :             :             /*
     874                 :             :              * Link the caller-supplied rdata chain for this buffer to the
     875                 :             :              * overall list.
     876                 :             :              */
     877                 :    19581350 :             bkpb.fork_flags |= BKPBLOCK_HAS_DATA;
     878                 :    19581350 :             bkpb.data_length = (uint16) regbuf->rdata_len;
     879                 :    19581350 :             total_len += regbuf->rdata_len;
     880                 :             : 
     881                 :    19581350 :             rdt_datas_last->next = regbuf->rdata_head;
     882                 :    19581350 :             rdt_datas_last = regbuf->rdata_tail;
     883                 :             :         }
     884                 :             : 
     885   [ +  +  +  -  :    27397864 :         if (prev_regbuf && RelFileLocatorEquals(regbuf->rlocator, prev_regbuf->rlocator))
             +  -  +  - ]
     886                 :             :         {
     887                 :     3055993 :             samerel = true;
     888                 :     3055993 :             bkpb.fork_flags |= BKPBLOCK_SAME_REL;
     889                 :             :         }
     890                 :             :         else
     891                 :    24341871 :             samerel = false;
     892                 :    27397864 :         prev_regbuf = regbuf;
     893                 :             : 
     894                 :             :         /* Ok, copy the header to the scratch buffer */
     895                 :    27397864 :         memcpy(scratch, &bkpb, SizeOfXLogRecordBlockHeader);
     896                 :    27397864 :         scratch += SizeOfXLogRecordBlockHeader;
     897         [ +  + ]:    27397864 :         if (include_image)
     898                 :             :         {
     899                 :     2929451 :             memcpy(scratch, &bimg, SizeOfXLogRecordBlockImageHeader);
     900                 :     2929451 :             scratch += SizeOfXLogRecordBlockImageHeader;
     901   [ +  +  -  + ]:     2929451 :             if (cbimg.hole_length != 0 && is_compressed)
     902                 :             :             {
     903                 :           0 :                 memcpy(scratch, &cbimg,
     904                 :             :                        SizeOfXLogRecordBlockCompressHeader);
     905                 :           0 :                 scratch += SizeOfXLogRecordBlockCompressHeader;
     906                 :             :             }
     907                 :             :         }
     908         [ +  + ]:    27397864 :         if (!samerel)
     909                 :             :         {
     910                 :    24341871 :             memcpy(scratch, &regbuf->rlocator, sizeof(RelFileLocator));
     911                 :    24341871 :             scratch += sizeof(RelFileLocator);
     912                 :             :         }
     913                 :    27397864 :         memcpy(scratch, &regbuf->block, sizeof(BlockNumber));
     914                 :    27397864 :         scratch += sizeof(BlockNumber);
     915                 :             :     }
     916                 :             : 
     917                 :             :     /* followed by the record's origin, if any */
     918         [ +  + ]:    25313289 :     if ((curinsert_flags & XLOG_INCLUDE_ORIGIN) &&
     919         [ +  + ]:    14964787 :         replorigin_xact_state.origin != InvalidReplOriginId)
     920                 :             :     {
     921                 :      171512 :         *(scratch++) = (char) XLR_BLOCK_ID_ORIGIN;
     922                 :      171512 :         memcpy(scratch, &replorigin_xact_state.origin, sizeof(replorigin_xact_state.origin));
     923                 :      171512 :         scratch += sizeof(replorigin_xact_state.origin);
     924                 :             :     }
     925                 :             : 
     926                 :             :     /* followed by toplevel XID, if not already included in previous record */
     927         [ +  + ]:    25313289 :     if (IsSubxactTopXidLogPending())
     928                 :             :     {
     929                 :         225 :         TransactionId xid = GetTopTransactionIdIfAny();
     930                 :             : 
     931                 :             :         /* Set the flag that the top xid is included in the WAL */
     932                 :         225 :         *topxid_included = true;
     933                 :             : 
     934                 :         225 :         *(scratch++) = (char) XLR_BLOCK_ID_TOPLEVEL_XID;
     935                 :         225 :         memcpy(scratch, &xid, sizeof(TransactionId));
     936                 :         225 :         scratch += sizeof(TransactionId);
     937                 :             :     }
     938                 :             : 
     939                 :             :     /* followed by main data, if any */
     940         [ +  + ]:    25313289 :     if (mainrdata_len > 0)
     941                 :             :     {
     942         [ +  + ]:    24886755 :         if (mainrdata_len > 255)
     943                 :             :         {
     944                 :             :             uint32      mainrdata_len_4b;
     945                 :             : 
     946         [ -  + ]:       52408 :             if (mainrdata_len > PG_UINT32_MAX)
     947         [ #  # ]:           0 :                 ereport(ERROR,
     948                 :             :                         (errmsg_internal("too much WAL data"),
     949                 :             :                          errdetail_internal("Main data length is %" PRIu64 " bytes for a maximum of %u bytes.",
     950                 :             :                                             mainrdata_len,
     951                 :             :                                             PG_UINT32_MAX)));
     952                 :             : 
     953                 :       52408 :             mainrdata_len_4b = (uint32) mainrdata_len;
     954                 :       52408 :             *(scratch++) = (char) XLR_BLOCK_ID_DATA_LONG;
     955                 :       52408 :             memcpy(scratch, &mainrdata_len_4b, sizeof(uint32));
     956                 :       52408 :             scratch += sizeof(uint32);
     957                 :             :         }
     958                 :             :         else
     959                 :             :         {
     960                 :    24834347 :             *(scratch++) = (char) XLR_BLOCK_ID_DATA_SHORT;
     961                 :    24834347 :             *(scratch++) = (uint8) mainrdata_len;
     962                 :             :         }
     963                 :    24886755 :         rdt_datas_last->next = mainrdata_head;
     964                 :    24886755 :         rdt_datas_last = mainrdata_last;
     965                 :    24886755 :         total_len += mainrdata_len;
     966                 :             :     }
     967                 :    25313289 :     rdt_datas_last->next = NULL;
     968                 :             : 
     969                 :    25313289 :     hdr_rdt.len = (scratch - hdr_scratch);
     970                 :    25313289 :     total_len += hdr_rdt.len;
     971                 :             : 
     972                 :             :     /*
     973                 :             :      * Calculate CRC of the data
     974                 :             :      *
     975                 :             :      * Note that the record header isn't added into the CRC initially since we
     976                 :             :      * don't know the prev-link yet.  Thus, the CRC will represent the CRC of
     977                 :             :      * the whole record in the order: rdata, then backup blocks, then record
     978                 :             :      * header.
     979                 :             :      */
     980                 :    25313289 :     INIT_CRC32C(rdata_crc);
     981                 :    25313289 :     COMP_CRC32C(rdata_crc, hdr_scratch + SizeOfXLogRecord, hdr_rdt.len - SizeOfXLogRecord);
     982         [ +  + ]:    90193490 :     for (rdt = hdr_rdt.next; rdt != NULL; rdt = rdt->next)
     983                 :    64880201 :         COMP_CRC32C(rdata_crc, rdt->data, rdt->len);
     984                 :             : 
     985                 :             :     /*
     986                 :             :      * Ensure that the XLogRecord is not too large.
     987                 :             :      *
     988                 :             :      * XLogReader machinery is only able to handle records up to a certain
     989                 :             :      * size (ignoring machine resource limitations), so make sure that we will
     990                 :             :      * not emit records larger than the sizes advertised to be supported.
     991                 :             :      */
     992         [ -  + ]:    25313289 :     if (total_len > XLogRecordMaxSize)
     993         [ #  # ]:           0 :         ereport(ERROR,
     994                 :             :                 (errmsg_internal("oversized WAL record"),
     995                 :             :                  errdetail_internal("WAL record would be %" PRIu64 " bytes (of maximum %u bytes); rmid %u flags %u.",
     996                 :             :                                     total_len, XLogRecordMaxSize, rmid, info)));
     997                 :             : 
     998                 :             :     /*
     999                 :             :      * Fill in the fields in the record header. Prev-link is filled in later,
    1000                 :             :      * once we know where in the WAL the record will be inserted. The CRC does
    1001                 :             :      * not include the record header yet.
    1002                 :             :      */
    1003                 :    25313289 :     rechdr->xl_xid = GetCurrentTransactionIdIfAny();
    1004                 :    25313289 :     rechdr->xl_tot_len = (uint32) total_len;
    1005                 :    25313289 :     rechdr->xl_info = info;
    1006                 :    25313289 :     rechdr->xl_rmid = rmid;
    1007                 :    25313289 :     rechdr->xl_prev = InvalidXLogRecPtr;
    1008                 :    25313289 :     rechdr->xl_crc = rdata_crc;
    1009                 :             : 
    1010                 :    25313289 :     return &hdr_rdt;
    1011                 :             : }
    1012                 :             : 
    1013                 :             : /*
    1014                 :             :  * Create a compressed version of a backup block image.
    1015                 :             :  *
    1016                 :             :  * Returns false if compression fails (i.e., compressed result is actually
    1017                 :             :  * bigger than original). Otherwise, returns true and sets 'dlen' to
    1018                 :             :  * the length of compressed block image.
    1019                 :             :  */
    1020                 :             : static bool
    1021                 :           0 : XLogCompressBackupBlock(const PageData *page, uint16 hole_offset, uint16 hole_length,
    1022                 :             :                         void *dest, uint16 *dlen)
    1023                 :             : {
    1024                 :           0 :     int32       orig_len = BLCKSZ - hole_length;
    1025                 :           0 :     int32       len = -1;
    1026                 :           0 :     int32       extra_bytes = 0;
    1027                 :             :     const void *source;
    1028                 :             :     PGAlignedBlock tmp;
    1029                 :             : 
    1030         [ #  # ]:           0 :     if (hole_length != 0)
    1031                 :             :     {
    1032                 :             :         /* must skip the hole */
    1033                 :           0 :         memcpy(tmp.data, page, hole_offset);
    1034                 :           0 :         memcpy(tmp.data + hole_offset,
    1035                 :           0 :                page + (hole_offset + hole_length),
    1036                 :           0 :                BLCKSZ - (hole_length + hole_offset));
    1037                 :           0 :         source = tmp.data;
    1038                 :             : 
    1039                 :             :         /*
    1040                 :             :          * Extra data needs to be stored in WAL record for the compressed
    1041                 :             :          * version of block image if the hole exists.
    1042                 :             :          */
    1043                 :           0 :         extra_bytes = SizeOfXLogRecordBlockCompressHeader;
    1044                 :             :     }
    1045                 :             :     else
    1046                 :           0 :         source = page;
    1047                 :             : 
    1048   [ #  #  #  #  :           0 :     switch ((WalCompression) wal_compression)
                      # ]
    1049                 :             :     {
    1050                 :           0 :         case WAL_COMPRESSION_PGLZ:
    1051                 :           0 :             len = pglz_compress(source, orig_len, dest, PGLZ_strategy_default);
    1052                 :           0 :             break;
    1053                 :             : 
    1054                 :           0 :         case WAL_COMPRESSION_LZ4:
    1055                 :             : #ifdef USE_LZ4
    1056                 :           0 :             len = LZ4_compress_default(source, dest, orig_len,
    1057                 :             :                                        COMPRESS_BUFSIZE);
    1058         [ #  # ]:           0 :             if (len <= 0)
    1059                 :           0 :                 len = -1;       /* failure */
    1060                 :             : #else
    1061                 :             :             elog(ERROR, "LZ4 is not supported by this build");
    1062                 :             : #endif
    1063                 :           0 :             break;
    1064                 :             : 
    1065                 :           0 :         case WAL_COMPRESSION_ZSTD:
    1066                 :             : #ifdef USE_ZSTD
    1067                 :             :             len = ZSTD_compress(dest, COMPRESS_BUFSIZE, source, orig_len,
    1068                 :             :                                 ZSTD_CLEVEL_DEFAULT);
    1069                 :             :             if (ZSTD_isError(len))
    1070                 :             :                 len = -1;       /* failure */
    1071                 :             : #else
    1072         [ #  # ]:           0 :             elog(ERROR, "zstd is not supported by this build");
    1073                 :             : #endif
    1074                 :             :             break;
    1075                 :             : 
    1076                 :           0 :         case WAL_COMPRESSION_NONE:
    1077                 :             :             Assert(false);      /* cannot happen */
    1078                 :           0 :             break;
    1079                 :             :             /* no default case, so that compiler will warn */
    1080                 :             :     }
    1081                 :             : 
    1082                 :             :     /*
    1083                 :             :      * We recheck the actual size even if compression reports success and see
    1084                 :             :      * if the number of bytes saved by compression is larger than the length
    1085                 :             :      * of extra data needed for the compressed version of block image.
    1086                 :             :      */
    1087         [ #  # ]:           0 :     if (len >= 0 &&
    1088         [ #  # ]:           0 :         len + extra_bytes < orig_len)
    1089                 :             :     {
    1090                 :           0 :         *dlen = (uint16) len;   /* successful compression */
    1091                 :           0 :         return true;
    1092                 :             :     }
    1093                 :           0 :     return false;
    1094                 :             : }
    1095                 :             : 
    1096                 :             : /*
    1097                 :             :  * Determine whether the buffer referenced has to be backed up.
    1098                 :             :  *
    1099                 :             :  * Since we don't yet have the insert lock, fullPageWrites and runningBackups
    1100                 :             :  * (which forces full-page writes) could change later, so the result should
    1101                 :             :  * be used for optimization purposes only.
    1102                 :             :  */
    1103                 :             : bool
    1104                 :      215317 : XLogCheckBufferNeedsBackup(Buffer buffer)
    1105                 :             : {
    1106                 :             :     XLogRecPtr  RedoRecPtr;
    1107                 :             :     bool        doPageWrites;
    1108                 :             :     Page        page;
    1109                 :             : 
    1110                 :      215317 :     GetFullPageWriteInfo(&RedoRecPtr, &doPageWrites);
    1111                 :             : 
    1112                 :      215317 :     page = BufferGetPage(buffer);
    1113                 :             : 
    1114   [ +  +  +  + ]:      215317 :     if (doPageWrites && PageGetLSN(page) <= RedoRecPtr)
    1115                 :       15934 :         return true;            /* buffer requires backup */
    1116                 :             : 
    1117                 :      199383 :     return false;               /* buffer does not need to be backed up */
    1118                 :             : }
    1119                 :             : 
    1120                 :             : /*
    1121                 :             :  * Write a backup block if needed when we are setting a hint. Note that
    1122                 :             :  * this may be called for a variety of page types, not just heaps.
    1123                 :             :  *
    1124                 :             :  * Callable while holding just a share-exclusive lock on the buffer
    1125                 :             :  * content. That suffices to prevent concurrent modifications of the
    1126                 :             :  * buffer. The buffer already needs to have been marked dirty by
    1127                 :             :  * MarkBufferDirtyHint().
    1128                 :             :  *
    1129                 :             :  * We only need to do something if page has not yet been full page written in
    1130                 :             :  * this checkpoint round. The LSN of the inserted wal record is returned if we
    1131                 :             :  * had to write, InvalidXLogRecPtr otherwise.
    1132                 :             :  */
    1133                 :             : XLogRecPtr
    1134                 :       84910 : XLogSaveBufferForHint(Buffer buffer, bool buffer_std)
    1135                 :             : {
    1136                 :       84910 :     XLogRecPtr  recptr = InvalidXLogRecPtr;
    1137                 :             :     XLogRecPtr  lsn;
    1138                 :             :     XLogRecPtr  RedoRecPtr;
    1139                 :             : 
    1140                 :             :     /* this also verifies that we hold an appropriate lock */
    1141                 :             :     Assert(BufferIsDirty(buffer));
    1142                 :             : 
    1143                 :             :     /*
    1144                 :             :      * Update RedoRecPtr so that we can make the right decision. It's possible
    1145                 :             :      * that a new checkpoint will start just after GetRedoRecPtr(), but that
    1146                 :             :      * is ok, as the buffer is already dirty, ensuring that any BufferSync()
    1147                 :             :      * started after the buffer was marked dirty cannot complete without
    1148                 :             :      * flushing this buffer.  If a checkpoint started between marking the
    1149                 :             :      * buffer dirty and this check, we will emit an unnecessary WAL record (as
    1150                 :             :      * the buffer will be written out as part of the checkpoint), but the
    1151                 :             :      * window for that is not big.
    1152                 :             :      */
    1153                 :       84910 :     RedoRecPtr = GetRedoRecPtr();
    1154                 :             : 
    1155                 :             :     /*
    1156                 :             :      * We assume page LSN is first data on *every* page that can be passed to
    1157                 :             :      * XLogInsert, whether it has the standard page layout or not.
    1158                 :             :      */
    1159                 :       84910 :     lsn = PageGetLSN(BufferGetPage(buffer));
    1160                 :             : 
    1161         [ +  + ]:       84910 :     if (lsn <= RedoRecPtr)
    1162                 :             :     {
    1163                 :       57852 :         int         flags = 0;
    1164                 :             : 
    1165                 :       57852 :         XLogBeginInsert();
    1166                 :             : 
    1167         [ +  + ]:       57852 :         if (buffer_std)
    1168                 :       44651 :             flags |= REGBUF_STANDARD;
    1169                 :             : 
    1170                 :       57852 :         XLogRegisterBuffer(0, buffer, flags);
    1171                 :             : 
    1172                 :       57852 :         recptr = XLogInsert(RM_XLOG_ID, XLOG_FPI_FOR_HINT);
    1173                 :             :     }
    1174                 :             : 
    1175                 :       84910 :     return recptr;
    1176                 :             : }
    1177                 :             : 
    1178                 :             : /*
    1179                 :             :  * Write a WAL record containing a full image of a page. Caller is responsible
    1180                 :             :  * for writing the page to disk after calling this routine.
    1181                 :             :  *
    1182                 :             :  * Note: If you're using this function, you should be building pages in private
    1183                 :             :  * memory and writing them directly to smgr.  If you're using buffers, call
    1184                 :             :  * log_newpage_buffer instead.
    1185                 :             :  *
    1186                 :             :  * If the page follows the standard page layout, with a PageHeader and unused
    1187                 :             :  * space between pd_lower and pd_upper, set 'page_std' to true. That allows
    1188                 :             :  * the unused space to be left out from the WAL record, making it smaller.
    1189                 :             :  */
    1190                 :             : XLogRecPtr
    1191                 :      196597 : log_newpage(RelFileLocator *rlocator, ForkNumber forknum, BlockNumber blkno,
    1192                 :             :             Page page, bool page_std)
    1193                 :             : {
    1194                 :             :     int         flags;
    1195                 :             :     XLogRecPtr  recptr;
    1196                 :             : 
    1197                 :      196597 :     flags = REGBUF_FORCE_IMAGE;
    1198         [ +  + ]:      196597 :     if (page_std)
    1199                 :      156147 :         flags |= REGBUF_STANDARD;
    1200                 :             : 
    1201                 :      196597 :     XLogBeginInsert();
    1202                 :      196597 :     XLogRegisterBlock(0, rlocator, forknum, blkno, page, flags);
    1203                 :      196597 :     recptr = XLogInsert(RM_XLOG_ID, XLOG_FPI);
    1204                 :             : 
    1205                 :             :     /*
    1206                 :             :      * The page may be uninitialized. If so, we can't set the LSN because that
    1207                 :             :      * would corrupt the page.
    1208                 :             :      */
    1209         [ +  + ]:      196597 :     if (!PageIsNew(page))
    1210                 :             :     {
    1211                 :      196504 :         PageSetLSN(page, recptr);
    1212                 :             :     }
    1213                 :             : 
    1214                 :      196597 :     return recptr;
    1215                 :             : }
    1216                 :             : 
    1217                 :             : /*
    1218                 :             :  * Like log_newpage(), but allows logging multiple pages in one operation.
    1219                 :             :  * It is more efficient than calling log_newpage() for each page separately,
    1220                 :             :  * because we can write multiple pages in a single WAL record.
    1221                 :             :  */
    1222                 :             : void
    1223                 :       29829 : log_newpages(RelFileLocator *rlocator, ForkNumber forknum, int num_pages,
    1224                 :             :              BlockNumber *blknos, Page *pages, bool page_std)
    1225                 :             : {
    1226                 :             :     int         flags;
    1227                 :             :     XLogRecPtr  recptr;
    1228                 :             :     int         i;
    1229                 :             :     int         j;
    1230                 :             : 
    1231                 :       29829 :     flags = REGBUF_FORCE_IMAGE;
    1232         [ +  + ]:       29829 :     if (page_std)
    1233                 :       29737 :         flags |= REGBUF_STANDARD;
    1234                 :             : 
    1235                 :             :     /*
    1236                 :             :      * Iterate over all the pages. They are collected into batches of
    1237                 :             :      * XLR_MAX_BLOCK_ID pages, and a single WAL-record is written for each
    1238                 :             :      * batch.
    1239                 :             :      */
    1240                 :       29829 :     XLogEnsureRecordSpace(XLR_MAX_BLOCK_ID - 1, 0);
    1241                 :             : 
    1242                 :       29829 :     i = 0;
    1243         [ +  + ]:       59658 :     while (i < num_pages)
    1244                 :             :     {
    1245                 :       29829 :         int         batch_start = i;
    1246                 :             :         int         nbatch;
    1247                 :             : 
    1248                 :       29829 :         XLogBeginInsert();
    1249                 :             : 
    1250                 :       29829 :         nbatch = 0;
    1251   [ +  +  +  + ]:       85918 :         while (nbatch < XLR_MAX_BLOCK_ID && i < num_pages)
    1252                 :             :         {
    1253                 :       56089 :             XLogRegisterBlock(nbatch, rlocator, forknum, blknos[i], pages[i], flags);
    1254                 :       56089 :             i++;
    1255                 :       56089 :             nbatch++;
    1256                 :             :         }
    1257                 :             : 
    1258                 :       29829 :         recptr = XLogInsert(RM_XLOG_ID, XLOG_FPI);
    1259                 :             : 
    1260         [ +  + ]:       85918 :         for (j = batch_start; j < i; j++)
    1261                 :             :         {
    1262                 :             :             /*
    1263                 :             :              * The page may be uninitialized. If so, we can't set the LSN
    1264                 :             :              * because that would corrupt the page.
    1265                 :             :              */
    1266         [ +  + ]:       56089 :             if (!PageIsNew(pages[j]))
    1267                 :             :             {
    1268                 :       56080 :                 PageSetLSN(pages[j], recptr);
    1269                 :             :             }
    1270                 :             :         }
    1271                 :             :     }
    1272                 :       29829 : }
    1273                 :             : 
    1274                 :             : /*
    1275                 :             :  * Write a WAL record containing a full image of a page.
    1276                 :             :  *
    1277                 :             :  * Caller should initialize the buffer and mark it dirty before calling this
    1278                 :             :  * function.  This function will set the page LSN.
    1279                 :             :  *
    1280                 :             :  * If the page follows the standard page layout, with a PageHeader and unused
    1281                 :             :  * space between pd_lower and pd_upper, set 'page_std' to true. That allows
    1282                 :             :  * the unused space to be left out from the WAL record, making it smaller.
    1283                 :             :  */
    1284                 :             : XLogRecPtr
    1285                 :      191408 : log_newpage_buffer(Buffer buffer, bool page_std)
    1286                 :             : {
    1287                 :      191408 :     Page        page = BufferGetPage(buffer);
    1288                 :             :     RelFileLocator rlocator;
    1289                 :             :     ForkNumber  forknum;
    1290                 :             :     BlockNumber blkno;
    1291                 :             : 
    1292                 :             :     /* Shared buffers should be modified in a critical section. */
    1293                 :             :     Assert(CritSectionCount > 0);
    1294                 :             : 
    1295                 :      191408 :     BufferGetTag(buffer, &rlocator, &forknum, &blkno);
    1296                 :             : 
    1297                 :      191408 :     return log_newpage(&rlocator, forknum, blkno, page, page_std);
    1298                 :             : }
    1299                 :             : 
    1300                 :             : /*
    1301                 :             :  * WAL-log a range of blocks in a relation.
    1302                 :             :  *
    1303                 :             :  * An image of all pages with block numbers 'startblk' <= X < 'endblk' is
    1304                 :             :  * written to the WAL. If the range is large, this is done in multiple WAL
    1305                 :             :  * records.
    1306                 :             :  *
    1307                 :             :  * If all page follows the standard page layout, with a PageHeader and unused
    1308                 :             :  * space between pd_lower and pd_upper, set 'page_std' to true. That allows
    1309                 :             :  * the unused space to be left out from the WAL records, making them smaller.
    1310                 :             :  *
    1311                 :             :  * NOTE: This function acquires exclusive-locks on the pages. Typically, this
    1312                 :             :  * is used on a newly-built relation, and the caller is holding a
    1313                 :             :  * AccessExclusiveLock on it, so no other backend can be accessing it at the
    1314                 :             :  * same time. If that's not the case, you must ensure that this does not
    1315                 :             :  * cause a deadlock through some other means.
    1316                 :             :  */
    1317                 :             : void
    1318                 :       41975 : log_newpage_range(Relation rel, ForkNumber forknum,
    1319                 :             :                   BlockNumber startblk, BlockNumber endblk,
    1320                 :             :                   bool page_std)
    1321                 :             : {
    1322                 :             :     int         flags;
    1323                 :             :     BlockNumber blkno;
    1324                 :             : 
    1325                 :       41975 :     flags = REGBUF_FORCE_IMAGE;
    1326         [ +  + ]:       41975 :     if (page_std)
    1327                 :         623 :         flags |= REGBUF_STANDARD;
    1328                 :             : 
    1329                 :             :     /*
    1330                 :             :      * Iterate over all the pages in the range. They are collected into
    1331                 :             :      * batches of XLR_MAX_BLOCK_ID pages, and a single WAL-record is written
    1332                 :             :      * for each batch.
    1333                 :             :      */
    1334                 :       41975 :     XLogEnsureRecordSpace(XLR_MAX_BLOCK_ID - 1, 0);
    1335                 :             : 
    1336                 :       41975 :     blkno = startblk;
    1337         [ +  + ]:       76449 :     while (blkno < endblk)
    1338                 :             :     {
    1339                 :             :         Buffer      bufpack[XLR_MAX_BLOCK_ID];
    1340                 :             :         XLogRecPtr  recptr;
    1341                 :             :         int         nbufs;
    1342                 :             :         int         i;
    1343                 :             : 
    1344         [ -  + ]:       34474 :         CHECK_FOR_INTERRUPTS();
    1345                 :             : 
    1346                 :             :         /* Collect a batch of blocks. */
    1347                 :       34474 :         nbufs = 0;
    1348   [ +  +  +  + ]:      166732 :         while (nbufs < XLR_MAX_BLOCK_ID && blkno < endblk)
    1349                 :             :         {
    1350                 :      132258 :             Buffer      buf = ReadBufferExtended(rel, forknum, blkno,
    1351                 :             :                                                  RBM_NORMAL, NULL);
    1352                 :             : 
    1353                 :      132258 :             LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
    1354                 :             : 
    1355                 :             :             /*
    1356                 :             :              * Completely empty pages are not WAL-logged. Writing a WAL record
    1357                 :             :              * would change the LSN, and we don't want that. We want the page
    1358                 :             :              * to stay empty.
    1359                 :             :              */
    1360         [ +  + ]:      132258 :             if (!PageIsNew(BufferGetPage(buf)))
    1361                 :      132111 :                 bufpack[nbufs++] = buf;
    1362                 :             :             else
    1363                 :         147 :                 UnlockReleaseBuffer(buf);
    1364                 :      132258 :             blkno++;
    1365                 :             :         }
    1366                 :             : 
    1367                 :             :         /* Nothing more to do if all remaining blocks were empty. */
    1368         [ -  + ]:       34474 :         if (nbufs == 0)
    1369                 :           0 :             break;
    1370                 :             : 
    1371                 :             :         /* Write WAL record for this batch. */
    1372                 :       34474 :         XLogBeginInsert();
    1373                 :             : 
    1374                 :       34474 :         START_CRIT_SECTION();
    1375         [ +  + ]:      166585 :         for (i = 0; i < nbufs; i++)
    1376                 :             :         {
    1377                 :      132111 :             MarkBufferDirty(bufpack[i]);
    1378                 :      132111 :             XLogRegisterBuffer(i, bufpack[i], flags);
    1379                 :             :         }
    1380                 :             : 
    1381                 :       34474 :         recptr = XLogInsert(RM_XLOG_ID, XLOG_FPI);
    1382                 :             : 
    1383         [ +  + ]:      166585 :         for (i = 0; i < nbufs; i++)
    1384                 :      132111 :             PageSetLSN(BufferGetPage(bufpack[i]), recptr);
    1385                 :             : 
    1386                 :       34474 :         END_CRIT_SECTION();
    1387                 :             : 
    1388         [ +  + ]:      166585 :         for (i = 0; i < nbufs; i++)
    1389                 :      132111 :             UnlockReleaseBuffer(bufpack[i]);
    1390                 :             :     }
    1391                 :       41975 : }
    1392                 :             : 
    1393                 :             : /*
    1394                 :             :  * Allocate working buffers needed for WAL record construction.
    1395                 :             :  */
    1396                 :             : void
    1397                 :       24645 : InitXLogInsert(void)
    1398                 :             : {
    1399                 :             : #ifdef USE_ASSERT_CHECKING
    1400                 :             : 
    1401                 :             :     /*
    1402                 :             :      * Check that any records assembled can be decoded.  This is capped based
    1403                 :             :      * on what XLogReader would require at its maximum bound.  The XLOG_BLCKSZ
    1404                 :             :      * addend covers the larger allocate_recordbuf() demand.  This code path
    1405                 :             :      * is called once per backend, more than enough for this check.
    1406                 :             :      */
    1407                 :             :     size_t      max_required =
    1408                 :             :         DecodeXLogRecordRequiredSpace(XLogRecordMaxSize + XLOG_BLCKSZ);
    1409                 :             : 
    1410                 :             :     Assert(AllocSizeIsValid(max_required));
    1411                 :             : #endif
    1412                 :             : 
    1413                 :             :     /* Initialize the working areas */
    1414         [ +  - ]:       24645 :     if (xloginsert_cxt == NULL)
    1415                 :             :     {
    1416                 :       24645 :         xloginsert_cxt = AllocSetContextCreate(TopMemoryContext,
    1417                 :             :                                                "WAL record construction",
    1418                 :             :                                                ALLOCSET_DEFAULT_SIZES);
    1419                 :             :     }
    1420                 :             : 
    1421         [ +  - ]:       24645 :     if (registered_buffers == NULL)
    1422                 :             :     {
    1423                 :       24645 :         registered_buffers = (registered_buffer *)
    1424                 :       24645 :             MemoryContextAllocZero(xloginsert_cxt,
    1425                 :             :                                    sizeof(registered_buffer) * (XLR_NORMAL_MAX_BLOCK_ID + 1));
    1426                 :       24645 :         max_registered_buffers = XLR_NORMAL_MAX_BLOCK_ID + 1;
    1427                 :             :     }
    1428         [ +  - ]:       24645 :     if (rdatas == NULL)
    1429                 :             :     {
    1430                 :       24645 :         rdatas = MemoryContextAlloc(xloginsert_cxt,
    1431                 :             :                                     sizeof(XLogRecData) * XLR_NORMAL_RDATAS);
    1432                 :       24645 :         max_rdatas = XLR_NORMAL_RDATAS;
    1433                 :             :     }
    1434                 :             : 
    1435                 :             :     /*
    1436                 :             :      * Allocate a buffer to hold the header information for a WAL record.
    1437                 :             :      */
    1438         [ +  - ]:       24645 :     if (hdr_scratch == NULL)
    1439                 :       24645 :         hdr_scratch = MemoryContextAllocZero(xloginsert_cxt,
    1440                 :             :                                              HEADER_SCRATCH_SIZE);
    1441                 :       24645 : }
        

Generated by: LCOV version 2.0-1