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