PostgreSQL Source Code: src/backend/access/transam/xloginsert.c Source File

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

AltStyle によって変換されたページ (->オリジナル) /