LCOV - differential code coverage report
Current view: top level - src/backend/access/transam - xloginsert.c (source / functions) Coverage Total Hit UNC UBC GNC CBC DCB
Current: a2387c32f2f8a1643c7d71b951587e6bcb2d4744 vs 371a302eecdc82274b0ae2967d18fd726a0aa6a1 Lines: 84.5 % 431 364 67 5 359 1
Current Date: 2025-10-26 12:31:50 -0700 Functions: 94.7 % 19 18 1 2 16
Baseline: lcov-20251027-010456-baseline Branches: 64.6 % 302 195 2 105 2 193
Baseline Date: 2025-10-26 11:01:32 +1300 Line coverage date bins:
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
(7,30] days: 100.0 % 1 1 1
(30,360] days: 87.5 % 8 7 1 4 3
(360..) days: 84.4 % 422 356 66 356
Function coverage date bins:
(30,360] days: 75.0 % 4 3 1 1 2
(360..) days: 100.0 % 15 15 1 14
Branch coverage date bins:
(7,30] days: 50.0 % 4 2 2 2
(360..) days: 64.8 % 298 193 105 193

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

Generated by: LCOV version 2.4-beta