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

Generated by: LCOV version 2.0-1