v4-0007-Reformat-the-XLog-record-header.patch
application/octet-stream
Filename: v4-0007-Reformat-the-XLog-record-header.patch
Type: application/octet-stream
Part: 6
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: format-patch
Series: patch v4-0007
Subject: Reformat the XLog record header
| File | + | − |
|---|---|---|
| src/backend/access/transam/xlog.c | 92 | 41 |
| src/backend/access/transam/xloginsert.c | 98 | 37 |
| src/backend/access/transam/xlogreader.c | 60 | 45 |
| src/backend/access/transam/xlogrecovery.c | 2 | 2 |
| src/bin/pg_resetwal/pg_resetwal.c | 24 | 14 |
| src/include/access/xlog_internal.h | 97 | 3 |
| src/include/access/xlogreader.h | 2 | 2 |
| src/include/access/xlogrecord.h | 111 | 14 |
From e887248dc62f76510267472b195834452ce299e5 Mon Sep 17 00:00:00 2001
From: Matthias van de Meent <boekewurm+postgres@gmail.com>
Date: Mon, 19 Jun 2023 17:46:45 +0200
Subject: [PATCH v4 7/7] Reformat the XLog record header
Many WAL records don't utilize all bytes of the XLog header, resulting in a
lot of wasted bytes.
This new XLog header format saves bytes in several ways:
- Records that don't need the XID won't include it
This potentially saves 4 bytes /record
- Records that are small enough don't need 4 bytes to describe their length
By varint encoding the length, we save up to 3 bytes /record
(up to 4 bytes /record if dataless records are considered)
- Only include xl_rmgrinfo if it is non-0
Several RMGRs have a record that they use most often. We save 1 byte/record
if that record id is 0.
- Lose some alignment losses
We save 1 byte/record by removing one byte of alignment losses
---
src/backend/access/transam/xlog.c | 133 ++++++++++++++-------
src/backend/access/transam/xloginsert.c | 135 ++++++++++++++++------
src/backend/access/transam/xlogreader.c | 105 +++++++++--------
src/backend/access/transam/xlogrecovery.c | 4 +-
src/bin/pg_resetwal/pg_resetwal.c | 38 +++---
src/include/access/xlog_internal.h | 100 +++++++++++++++-
src/include/access/xlogreader.h | 4 +-
src/include/access/xlogrecord.h | 125 +++++++++++++++++---
8 files changed, 486 insertions(+), 158 deletions(-)
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 4b1ff0d1aa..5937b43f50 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -737,17 +737,18 @@ XLogInsertRecord(XLogRecData *rdata,
XLogCtlInsert *Insert = &XLogCtl->Insert;
pg_crc32c rdata_crc;
bool inserted;
- XLogRecord *rechdr = (XLogRecord *) rdata->data;
- uint8 rmgrinfo = rechdr->xl_rmgrinfo;
- bool isLogSwitch = (rechdr->xl_rmid == RM_XLOG_ID &&
- rmgrinfo == XLOG_SWITCH);
+ XLogRecHdr rechdr;
+ bool isLogSwitch;
XLogRecPtr StartPos;
XLogRecPtr EndPos;
+ XLogRecPtr xl_prev;
bool prevDoPageWrites = doPageWrites;
TimeLineID insertTLI;
+ int rec_payload_len,
+ rec_hdr_len;
- /* we assume that all of the record header is in the first chunk */
- Assert(rdata->len >= SizeOfXLogRecord);
+ /* we assume that all data of the record header is in the first chunk */
+ Assert(rdata->len >= XLogRecordMinHdrSize);
/* cross-check on whether we should be here or not */
if (!XLogInsertAllowed())
@@ -758,6 +759,17 @@ XLogInsertRecord(XLogRecData *rdata,
* change, so we can read it without a lock.
*/
insertTLI = XLogCtl->InsertTimeLineID;
+ rechdr = (XLogRecHdr) rdata->data;
+
+ {
+ XLogRecord record = {0};
+ XLogReadRecHdrInto(rechdr, (Size) rdata->len, &record);
+
+ isLogSwitch = (record.xl_rmid == RM_XLOG_ID &&
+ record.xl_rmgrinfo == XLOG_SWITCH);
+ rec_hdr_len = (int) XLogRecordHdrLen(rechdr->xl_info);
+ rec_payload_len = (int) record.xl_payload_len;
+ }
/*----------
*
@@ -834,34 +846,44 @@ XLogInsertRecord(XLogRecData *rdata,
}
/*
- * Reserve space for the record in the WAL. This also sets the xl_prev
- * pointer.
+ * Reserve space for the record in the WAL.
*/
if (isLogSwitch)
- inserted = ReserveXLogSwitch(&StartPos, &EndPos, &rechdr->xl_prev);
+ inserted = ReserveXLogSwitch(&StartPos, &EndPos, &xl_prev);
else
{
- ReserveXLogInsertLocation(rechdr->xl_tot_len, &StartPos, &EndPos,
- &rechdr->xl_prev);
+ ReserveXLogInsertLocation(rec_hdr_len + rec_payload_len,
+ &StartPos, &EndPos, &xl_prev);
inserted = true;
}
if (inserted)
{
+ char *rec = (char *) rechdr;
+
+ /* fill in xl_prev */
+ memcpy(rec + rec_hdr_len - sizeof(pg_crc32c) - sizeof(XLogRecPtr),
+ &xl_prev, sizeof(XLogRecPtr));
+
/*
* Now that xl_prev has been filled in, calculate CRC of the record
* header.
*/
- rdata_crc = rechdr->xl_crc;
- COMP_CRC32C(rdata_crc, rechdr, offsetof(XLogRecord, xl_crc));
+
+ memcpy(&rdata_crc, rec + rec_hdr_len - sizeof(pg_crc32c),
+ sizeof(pg_crc32c));
+
+ COMP_CRC32C(rdata_crc, rec, rec_hdr_len - sizeof(pg_crc32c));
FIN_CRC32C(rdata_crc);
- rechdr->xl_crc = rdata_crc;
+
+ memcpy(rec + rec_hdr_len - sizeof(pg_crc32c),
+ &rdata_crc, sizeof(pg_crc32c));
/*
* All the record data, including the header, is now ready to be
* inserted. Copy the record in the space reserved.
*/
- CopyXLogRecordToWAL(rechdr->xl_tot_len, isLogSwitch, rdata,
+ CopyXLogRecordToWAL(rec_hdr_len + rec_payload_len, isLogSwitch, rdata,
StartPos, EndPos, insertTLI);
/*
@@ -932,7 +954,8 @@ XLogInsertRecord(XLogRecData *rdata,
*/
if (inserted)
{
- EndPos = StartPos + SizeOfXLogRecord;
+ /* xlog switch is minimal record header, plus a byte for rmgrinfo */
+ EndPos = StartPos + XLogRecordMinHdrSize + sizeof(uint8);
if (StartPos / XLOG_BLCKSZ != EndPos / XLOG_BLCKSZ)
{
uint64 offset = XLogSegmentOffset(EndPos, wal_segment_size);
@@ -949,7 +972,7 @@ XLogInsertRecord(XLogRecData *rdata,
if (XLOG_DEBUG)
{
static XLogReaderState *debug_reader = NULL;
- XLogRecord *record;
+ XLogRecHdr record;
DecodedXLogRecord *decoded;
StringInfoData buf;
StringInfoData recordBuf;
@@ -971,9 +994,9 @@ XLogInsertRecord(XLogRecData *rdata,
appendBinaryStringInfo(&recordBuf, rdata->data, rdata->len);
/* We also need temporary space to decode the record. */
- record = (XLogRecord *) recordBuf.data;
+ record = (XLogRecHdr) recordBuf.data;
decoded = (DecodedXLogRecord *)
- palloc(DecodeXLogRecordRequiredSpace(record->xl_tot_len));
+ palloc(DecodeXLogRecordRequiredSpace(recordBuf.len));
if (!debug_reader)
debug_reader = XLogReaderAllocate(wal_segment_size, NULL,
@@ -1018,7 +1041,7 @@ XLogInsertRecord(XLogRecData *rdata,
/* Report WAL traffic to the instrumentation. */
if (inserted)
{
- pgWalUsage.wal_bytes += rechdr->xl_tot_len;
+ pgWalUsage.wal_bytes += rec_hdr_len + rec_payload_len;
pgWalUsage.wal_records++;
pgWalUsage.wal_fpi += num_fpi;
}
@@ -1049,10 +1072,10 @@ ReserveXLogInsertLocation(int size, XLogRecPtr *StartPos, XLogRecPtr *EndPos,
uint64 endbytepos;
uint64 prevbytepos;
- size = MAXALIGN(size);
+ size = XLP_ALIGN(size);
/* All (non xlog-switch) records should contain data. */
- Assert(size > SizeOfXLogRecord);
+ Assert(size > XLogRecordMinHdrSize);
/*
* The duration the spinlock needs to be held is minimized by minimizing
@@ -1103,7 +1126,7 @@ ReserveXLogSwitch(XLogRecPtr *StartPos, XLogRecPtr *EndPos, XLogRecPtr *PrevPtr)
uint64 startbytepos;
uint64 endbytepos;
uint64 prevbytepos;
- uint32 size = MAXALIGN(SizeOfXLogRecord);
+ uint32 size = XLP_ALIGN(XLogRecordMinHdrSize + sizeof(uint8));
XLogRecPtr ptr;
uint32 segleft;
@@ -1176,10 +1199,10 @@ CopyXLogRecordToWAL(int write_len, bool isLogSwitch, XLogRecData *rdata,
freespace = INSERT_FREESPACE(CurrPos);
/*
- * there should be enough space for at least the first field (xl_tot_len)
+ * there should be enough space for at least the first XL_ALIGN quantum
* on this page.
*/
- Assert(freespace >= sizeof(uint32));
+ Assert(freespace >= XLP_ALIGN(1));
/* Copy record data */
written = 0;
@@ -1247,7 +1270,7 @@ CopyXLogRecordToWAL(int write_len, bool isLogSwitch, XLogRecData *rdata,
if (isLogSwitch && XLogSegmentOffset(CurrPos, wal_segment_size) != 0)
{
/* An xlog-switch record doesn't contain any data besides the header */
- Assert(write_len == SizeOfXLogRecord);
+ Assert(write_len == XLogRecordMinHdrSize + sizeof(uint8));
/* Assert that we did reserve the right amount of space */
Assert(XLogSegmentOffset(EndPos, wal_segment_size) == 0);
@@ -1291,7 +1314,7 @@ CopyXLogRecordToWAL(int write_len, bool isLogSwitch, XLogRecData *rdata,
else
{
/* Align the end position, so that the next record starts aligned */
- CurrPos = MAXALIGN64(CurrPos);
+ CurrPos = XLP_ALIGN(CurrPos);
}
if (CurrPos != EndPos)
@@ -4649,11 +4672,15 @@ BootStrapXLOG(void)
char *buffer;
XLogPageHeader page;
XLogLongPageHeader longpage;
- XLogRecord *record;
+ XLogRecHdr rec_hdr;
+ char *baserecptr;
char *recptr;
uint64 sysidentifier;
struct timeval tv;
pg_crc32c crc;
+ const int rec_hdr_len = offsetof(XLogRecHdrData, xl_hdrdata) +
+ sizeof(uint8) + sizeof(XLogRecPtr) + sizeof(pg_crc32c);
+ const int rec_payload_len = sizeof(uint8) * 2 + sizeof(CheckPoint);
/* allow ordinary WAL segment creation, like StartupXLOG() would */
SetInstallXLogFileSegmentActive();
@@ -4724,28 +4751,52 @@ BootStrapXLOG(void)
longpage->xlp_seg_size = wal_segment_size;
longpage->xlp_xlog_blcksz = XLOG_BLCKSZ;
+ /* if this changes, we need to update the code below */
+ Assert(XLogLengthToSizeClass(SizeOfXLogRecordDataHeaderShort + sizeof(checkPoint), XLS_UINT32) == XLS_UINT8);
+ /* if this changes, we need to add XLR_HAS_RMGRINFO */
+ Assert(XLOG_CHECKPOINT_SHUTDOWN == 0);
+
/* Insert the initial checkpoint record */
- recptr = ((char *) page + SizeOfXLogLongPHD);
- record = (XLogRecord *) recptr;
- record->xl_prev = 0;
- record->xl_xid = InvalidTransactionId;
- record->xl_tot_len = SizeOfXLogRecord + SizeOfXLogRecordDataHeaderShort + sizeof(checkPoint);
- record->xl_info = 0;
- record->xl_rmgrinfo = XLOG_CHECKPOINT_SHUTDOWN;
- record->xl_rmid = RM_XLOG_ID;
- recptr += SizeOfXLogRecord;
+ baserecptr = recptr = (((char *) page) + SizeOfXLogLongPHD);
+
+ rec_hdr = (XLogRecHdr) baserecptr;
+ rec_hdr->xl_info = (char) XLS_UINT8;
+ rec_hdr->xl_rmid = (char) RM_XLOG_ID;
+ recptr += offsetof(XLogRecHdrData, xl_hdrdata);
+
+ recptr += XLogWriteLength(SizeOfXLogRecordDataHeaderShort + sizeof(checkPoint),
+ XLS_UINT8, XLS_UINT8, recptr);
+
+ /* include prevptr */
+ {
+ XLogRecPtr prevptr = 0;
+ memcpy(recptr, &prevptr, sizeof(XLogRecPtr));
+ recptr += sizeof(XLogRecPtr);
+ }
+
+ /* reserve location of crc */
+ recptr += sizeof(pg_crc32c);
+
+ Assert(recptr - baserecptr == XLogRecordHdrLen(rec_hdr->xl_info));
+ Assert(recptr - baserecptr == rec_hdr_len);
+
/* fill the XLogRecordDataHeaderShort struct */
*(recptr++) = (char) XLR_BLOCK_ID_DATA_SHORT;
*(recptr++) = sizeof(checkPoint);
memcpy(recptr, &checkPoint, sizeof(checkPoint));
recptr += sizeof(checkPoint);
- Assert(recptr - (char *) record == record->xl_tot_len);
+
+ /* Assert length of record matches expectations */
+ Assert(recptr - baserecptr == XLogRecordTotalLength(rec_hdr));
+ Assert((rec_hdr)->xl_info == XLS_UINT8);
INIT_CRC32C(crc);
- COMP_CRC32C(crc, ((char *) record) + SizeOfXLogRecord, record->xl_tot_len - SizeOfXLogRecord);
- COMP_CRC32C(crc, (char *) record, offsetof(XLogRecord, xl_crc));
+ COMP_CRC32C(crc, baserecptr + rec_hdr_len, rec_payload_len);
+ COMP_CRC32C(crc, baserecptr, rec_hdr_len - sizeof(pg_crc32c));
FIN_CRC32C(crc);
- record->xl_crc = crc;
+
+ memcpy(baserecptr + rec_hdr_len - sizeof(pg_crc32c),
+ &crc, sizeof(pg_crc32c));
/* Create first XLOG segment file */
openLogTLI = BootstrapTimeLineID;
diff --git a/src/backend/access/transam/xloginsert.c b/src/backend/access/transam/xloginsert.c
index 4816c5284b..194196f596 100644
--- a/src/backend/access/transam/xloginsert.c
+++ b/src/backend/access/transam/xloginsert.c
@@ -545,12 +545,16 @@ XLogRecordAssemble(RmgrId rmid, uint8 info, uint8 rmgr_info, XLogRecPtr RedoRecP
XLogRecPtr *fpw_lsn, int *num_fpi, bool *topxid_included)
{
XLogRecData *rdt;
- uint64 total_len = 0;
+ uint64 payload_len = 0;
+ XLogSizeClass payload_sizeclass = XLS_EMPTY;
int block_id;
pg_crc32c rdata_crc;
registered_buffer *prev_regbuf = NULL;
XLogRecData *rdt_datas_last;
- XLogRecord *rechdr;
+ TransactionId xid;
+ XLogRecHdr rechdr;
+ uint8 xlr_flags = 0;
+ uint32 rec_hdr_len;
char *scratch = hdr_scratch;
/*
@@ -558,9 +562,10 @@ XLogRecordAssemble(RmgrId rmid, uint8 info, uint8 rmgr_info, XLogRecPtr RedoRecP
* All the modifications we do to the rdata chains below must handle that.
*/
- /* The record begins with the fixed-size header */
- rechdr = (XLogRecord *) scratch;
- scratch += SizeOfXLogRecord;
+ /* The record begins with the variable-size header */
+ rechdr = (XLogRecHdr) scratch;
+
+ scratch += XLogRecordMaxHdrSize;
hdr_rdt.next = NULL;
rdt_datas_last = &hdr_rdt;
@@ -779,7 +784,7 @@ XLogRecordAssemble(RmgrId rmid, uint8 info, uint8 rmgr_info, XLogRecPtr RedoRecP
}
}
- total_len += bimg.length;
+ payload_len += bimg.length;
}
if (needs_data)
@@ -797,7 +802,7 @@ XLogRecordAssemble(RmgrId rmid, uint8 info, uint8 rmgr_info, XLogRecPtr RedoRecP
bkpb.fork_flags |= BKPBLOCK_HAS_DATA;
data_length = (uint16) regbuf->rdata_len;
data_sizeclass = XLogLengthToSizeClass(data_length, XLS_UINT16);
- total_len += regbuf->rdata_len;
+ payload_len += regbuf->rdata_len;
rdt_datas_last->next = regbuf->rdata_head;
rdt_datas_last = regbuf->rdata_tail;
@@ -851,16 +856,33 @@ XLogRecordAssemble(RmgrId rmid, uint8 info, uint8 rmgr_info, XLogRecPtr RedoRecP
}
/* followed by toplevel XID, if not already included in previous record */
- if (curinsert_flags & XLOG_INCLUDE_XID && IsSubxactTopXidLogPending())
+ if (curinsert_flags & XLOG_INCLUDE_XID)
{
- TransactionId xid = GetTopTransactionIdIfAny();
+ xid = GetCurrentTransactionIdIfAny();
+
+ if (IsSubxactTopXidLogPending())
+ {
+ TransactionId txid = GetTopTransactionIdIfAny();
+
+ xlr_flags |= XLR_HAS_XID;
+ Assert(TransactionIdIsValid(xid));
- /* Set the flag that the top xid is included in the WAL */
- *topxid_included = true;
+ /* Set the flag that the top xid is included in the WAL */
+ *topxid_included = true;
- *(scratch++) = (char) XLR_BLOCK_ID_TOPLEVEL_XID;
- memcpy(scratch, &xid, sizeof(TransactionId));
- scratch += sizeof(TransactionId);
+ *(scratch++) = (char) XLR_BLOCK_ID_TOPLEVEL_XID;
+ memcpy(scratch, &txid, sizeof(TransactionId));
+ scratch += sizeof(TransactionId);
+ }
+ else if (TransactionIdIsValid(xid))
+ {
+ xlr_flags |= XLR_HAS_XID;
+ }
+ }
+ else
+ {
+ xid = InvalidTransactionId;
+ Assert((xlr_flags & XLR_HAS_XID) == 0);
}
/* followed by main data, if any */
@@ -889,12 +911,64 @@ XLogRecordAssemble(RmgrId rmid, uint8 info, uint8 rmgr_info, XLogRecPtr RedoRecP
}
rdt_datas_last->next = mainrdata_head;
rdt_datas_last = mainrdata_last;
- total_len += mainrdata_len;
+ payload_len += mainrdata_len;
}
rdt_datas_last->next = NULL;
- hdr_rdt.len = (scratch - hdr_scratch);
- total_len += hdr_rdt.len;
+ /* Add the block headers section's length to the payload */
+ payload_len += scratch - (hdr_scratch + XLogRecordMaxHdrSize);
+
+ /*
+ * Fill in the fields in the record header. Prev-link is filled in later,
+ * once we know where in the WAL the record will be inserted. The CRC does
+ * not include the record header yet.
+ */
+ payload_sizeclass = XLogLengthToSizeClass(payload_len, XLS_UINT32);
+
+ xlr_flags |= payload_sizeclass;
+
+ if (rmgr_info != 0)
+ xlr_flags |= XLR_HAS_RMGRINFO;
+
+ /* Set up the xlog header. and xl_rmgr */
+ rechdr->xl_info = xlr_flags;
+ rechdr->xl_rmid = rmid;
+
+ rec_hdr_len = 0;
+
+ /* next, xl_payload_len */
+ rec_hdr_len += XLogWriteLength(payload_len, payload_sizeclass,
+ XLS_UINT32,
+ &rechdr->xl_hdrdata[rec_hdr_len]);
+
+ if (xlr_flags & XLR_HAS_RMGRINFO)
+ rechdr->xl_hdrdata[rec_hdr_len++] = (char) rmgr_info;
+
+ if (xlr_flags & XLR_HAS_XID)
+ {
+ Assert(curinsert_flags & XLOG_INCLUDE_XID);
+ Assert(TransactionIdIsValid(xid));
+
+ memcpy(&rechdr->xl_hdrdata[rec_hdr_len], &xid, sizeof(TransactionId));
+ rec_hdr_len += sizeof(TransactionId);
+ }
+
+ /* reserve space for XLogRecPtr and checksum */
+ rec_hdr_len += sizeof(XLogRecPtr);
+ rec_hdr_len += sizeof(pg_crc32c);
+ /* Add static header length */
+ rec_hdr_len += offsetof(XLogRecHdrData, xl_hdrdata);
+
+ Assert(rec_hdr_len == XLogRecordHdrLen(rechdr->xl_info));
+
+ /* move the record to be placed the rest of the payload */
+ memmove(hdr_scratch + XLogRecordMaxHdrSize - rec_hdr_len,
+ hdr_scratch, rec_hdr_len);
+
+ rechdr = (XLogRecHdr) (hdr_scratch + XLogRecordMaxHdrSize - rec_hdr_len);
+
+ hdr_rdt.data = (char *) rechdr;
+ hdr_rdt.len = (scratch - hdr_rdt.data);
/*
* Calculate CRC of the data
@@ -905,10 +979,14 @@ XLogRecordAssemble(RmgrId rmid, uint8 info, uint8 rmgr_info, XLogRecPtr RedoRecP
* header.
*/
INIT_CRC32C(rdata_crc);
- COMP_CRC32C(rdata_crc, hdr_scratch + SizeOfXLogRecord, hdr_rdt.len - SizeOfXLogRecord);
+ COMP_CRC32C(rdata_crc, hdr_scratch + XLogRecordMaxHdrSize,
+ hdr_rdt.len - rec_hdr_len);
for (rdt = hdr_rdt.next; rdt != NULL; rdt = rdt->next)
COMP_CRC32C(rdata_crc, rdt->data, rdt->len);
+ memcpy(hdr_rdt.data + rec_hdr_len - sizeof(pg_crc32c),
+ &rdata_crc, sizeof(pg_crc32c));
+
/*
* Ensure that the XLogRecord is not too large.
*
@@ -917,28 +995,11 @@ XLogRecordAssemble(RmgrId rmid, uint8 info, uint8 rmgr_info, XLogRecPtr RedoRecP
* not emit records larger than the sizes advertised to be supported. This
* cap is based on DecodeXLogRecordRequiredSpace().
*/
- if (total_len >= XLogRecordMaxSize)
+ if (payload_len + rec_hdr_len >= XLogRecordMaxSize)
ereport(ERROR,
(errmsg_internal("oversized WAL record"),
errdetail_internal("WAL record would be %llu bytes (of maximum %u bytes); rmid %u flags %u.",
- (unsigned long long) total_len, XLogRecordMaxSize, rmid, info)));
-
- /*
- * Fill in the fields in the record header. Prev-link is filled in later,
- * once we know where in the WAL the record will be inserted. The CRC does
- * not include the record header yet.
- */
- if (curinsert_flags & XLOG_INCLUDE_XID)
- rechdr->xl_xid = GetCurrentTransactionIdIfAny();
- else
- rechdr->xl_xid = InvalidTransactionId;
-
- rechdr->xl_tot_len = (uint32) total_len;
- rechdr->xl_info = info;
- rechdr->xl_rmid = rmid;
- rechdr->xl_rmgrinfo = rmgr_info;
- rechdr->xl_prev = InvalidXLogRecPtr;
- rechdr->xl_crc = rdata_crc;
+ (unsigned long long) payload_len, XLogRecordMaxSize, rmid, info)));
return &hdr_rdt;
}
diff --git a/src/backend/access/transam/xlogreader.c b/src/backend/access/transam/xlogreader.c
index 9f8687ae45..3f0143642d 100644
--- a/src/backend/access/transam/xlogreader.c
+++ b/src/backend/access/transam/xlogreader.c
@@ -49,8 +49,9 @@ static int ReadPageInternal(XLogReaderState *state, XLogRecPtr pageptr,
static void XLogReaderInvalReadState(XLogReaderState *state);
static XLogPageReadResult XLogDecodeNextRecord(XLogReaderState *state, bool nonblocking);
static bool ValidXLogRecordHeader(XLogReaderState *state, XLogRecPtr RecPtr,
- XLogRecPtr PrevRecPtr, XLogRecord *record, bool randAccess);
-static bool ValidXLogRecord(XLogReaderState *state, XLogRecord *record,
+ XLogRecPtr PrevRecPtr, XLogRecHdr rechdr,
+ bool randAccess);
+static bool ValidXLogRecord(XLogReaderState *state, XLogRecHdr record,
XLogRecPtr recptr);
static void ResetDecoder(XLogReaderState *state);
static void WALOpenSegmentInit(WALOpenSegment *seg, WALSegmentContext *segcxt,
@@ -535,7 +536,7 @@ static XLogPageReadResult
XLogDecodeNextRecord(XLogReaderState *state, bool nonblocking)
{
XLogRecPtr RecPtr;
- XLogRecord *record;
+ XLogRecHdr record;
XLogRecPtr targetPagePtr;
bool randAccess;
uint32 len,
@@ -602,7 +603,7 @@ restart:
* fits on the same page.
*/
readOff = ReadPageInternal(state, targetPagePtr,
- Min(targetRecOff + SizeOfXLogRecord, XLOG_BLCKSZ));
+ Min(targetRecOff + XLogRecordMaxHdrSize, XLOG_BLCKSZ));
if (readOff == XLREAD_WOULDBLOCK)
return XLREAD_WOULDBLOCK;
else if (readOff < 0)
@@ -644,13 +645,13 @@ restart:
* Read the record length.
*
* NB: Even though we use an XLogRecord pointer here, the whole record
- * header might not fit on this page. xl_tot_len is the first field of the
- * struct, so it must be on this page (the records are MAXALIGNed), but we
- * cannot access any other fields until we've verified that we got the
- * whole header.
+ * header might not fit on this page. xl_payload_len is the third field of
+ * the data and starts at the third byte, so it must be on this page (the
+ * records are aligned to 8 bytes), but we cannot access most other fields
+ * until we've verified that we got the whole header.
*/
- record = (XLogRecord *) (state->readBuf + RecPtr % XLOG_BLCKSZ);
- total_len = record->xl_tot_len;
+ record = (XLogRecHdr) (state->readBuf + (RecPtr % XLOG_BLCKSZ));
+ total_len = XLogRecordTotalLength(record);
/*
* If the whole record header is on this page, validate it immediately.
@@ -660,7 +661,7 @@ restart:
* record" code path below; otherwise we might fail to apply
* ValidXLogRecordHeader at all.
*/
- if (targetRecOff <= XLOG_BLCKSZ - SizeOfXLogRecord)
+ if (targetRecOff <= XLOG_BLCKSZ - XLogRecordHdrLen(record->xl_info))
{
if (!ValidXLogRecordHeader(state, RecPtr, state->DecodeRecPtr, record,
randAccess))
@@ -683,12 +684,12 @@ restart:
LSN_FORMAT_ARGS(RecPtr));
goto err;
}
- if (total_len < SizeOfXLogRecord)
+ if (total_len < XLogRecordMinHdrSize)
{
report_invalid_record(state,
"invalid record length at %X/%X: expected at least %u, got %u",
LSN_FORMAT_ARGS(RecPtr),
- (uint32) SizeOfXLogRecord, total_len);
+ (uint32) XLogRecordMinHdrSize, total_len);
goto err;
}
gotheader = false;
@@ -831,7 +832,7 @@ restart:
/* If we just reassembled the record header, validate it. */
if (!gotheader)
{
- record = (XLogRecord *) state->readRecordBuf;
+ record = (XLogRecHdr) state->readRecordBuf;
if (!ValidXLogRecordHeader(state, RecPtr, state->DecodeRecPtr,
record, randAccess))
goto err;
@@ -841,14 +842,14 @@ restart:
Assert(gotheader);
- record = (XLogRecord *) state->readRecordBuf;
+ record = (XLogRecHdr) state->readRecordBuf;
if (!ValidXLogRecord(state, record, RecPtr))
goto err;
pageHeaderSize = XLogPageHeaderSize((XLogPageHeader) state->readBuf);
state->DecodeRecPtr = RecPtr;
state->NextRecPtr = targetPagePtr + pageHeaderSize
- + MAXALIGN(pageHeader->xlp_rem_len);
+ + XLP_ALIGN(pageHeader->xlp_rem_len);
}
else
{
@@ -864,7 +865,7 @@ restart:
if (!ValidXLogRecord(state, record, RecPtr))
goto err;
- state->NextRecPtr = RecPtr + MAXALIGN(total_len);
+ state->NextRecPtr = RecPtr + XLP_ALIGN(total_len);
state->DecodeRecPtr = RecPtr;
}
@@ -873,7 +874,7 @@ restart:
* Special processing if it's an XLOG SWITCH record
*/
if (record->xl_rmid == RM_XLOG_ID &&
- record->xl_rmgrinfo == XLOG_SWITCH)
+ XLRHdrGetRmgrInfo(record) == XLOG_SWITCH)
{
/* Pretend it extends to end of segment */
state->NextRecPtr += state->segcxt.ws_segsize - 1;
@@ -892,7 +893,7 @@ restart:
if (!decoded->oversized)
{
/* The new decode buffer head must be MAXALIGNed. */
- Assert(decoded->size == MAXALIGN(decoded->size));
+ Assert(decoded->size == XLP_ALIGN(decoded->size));
if ((char *) decoded == state->decode_buffer)
state->decode_buffer_tail = state->decode_buffer + decoded->size;
else
@@ -1123,22 +1124,25 @@ XLogReaderInvalReadState(XLogReaderState *state)
*/
static bool
ValidXLogRecordHeader(XLogReaderState *state, XLogRecPtr RecPtr,
- XLogRecPtr PrevRecPtr, XLogRecord *record,
+ XLogRecPtr PrevRecPtr, XLogRecHdr rechdr,
bool randAccess)
{
- if (record->xl_tot_len < SizeOfXLogRecord)
+ XLogRecord record = {0};
+ XLogReadRecHdrInto(rechdr, XLogRecordHdrLen(rechdr->xl_info), &record);
+
+ if (XLogRecordTotalLength(rechdr) < XLogRecordMinHdrSize)
{
report_invalid_record(state,
"invalid record length at %X/%X: expected at least %u, got %u",
LSN_FORMAT_ARGS(RecPtr),
- (uint32) SizeOfXLogRecord, record->xl_tot_len);
+ (uint32) XLogRecordMinHdrSize, record.xl_payload_len);
return false;
}
- if (!RmgrIdIsValid(record->xl_rmid))
+ if (!RmgrIdIsValid(rechdr->xl_rmid))
{
report_invalid_record(state,
"invalid resource manager ID %u at %X/%X",
- record->xl_rmid, LSN_FORMAT_ARGS(RecPtr));
+ rechdr->xl_rmid, LSN_FORMAT_ARGS(RecPtr));
return false;
}
if (randAccess)
@@ -1147,12 +1151,14 @@ ValidXLogRecordHeader(XLogReaderState *state, XLogRecPtr RecPtr,
* We can't exactly verify the prev-link, but surely it should be less
* than the record's own address.
*/
- if (!(record->xl_prev < RecPtr))
+ if (!(record.xl_prev < RecPtr))
{
report_invalid_record(state,
- "record with incorrect prev-link %X/%X at %X/%X",
- LSN_FORMAT_ARGS(record->xl_prev),
- LSN_FORMAT_ARGS(RecPtr));
+ "record with incorrect prev-link %X/%X at %X/%X, info %u, rmgr %u",
+ LSN_FORMAT_ARGS(record.xl_prev),
+ LSN_FORMAT_ARGS(RecPtr),
+ (uint32) record.xl_info,
+ (uint32) record.xl_rmid);
return false;
}
}
@@ -1163,12 +1169,13 @@ ValidXLogRecordHeader(XLogReaderState *state, XLogRecPtr RecPtr,
* check guards against torn WAL pages where a stale but valid-looking
* WAL record starts on a sector boundary.
*/
- if (record->xl_prev != PrevRecPtr)
+ if (record.xl_prev != PrevRecPtr)
{
report_invalid_record(state,
- "record with incorrect prev-link %X/%X at %X/%X",
- LSN_FORMAT_ARGS(record->xl_prev),
- LSN_FORMAT_ARGS(RecPtr));
+ "record with incorrect prev-link %X/%X at %X/%X, expected %X/%X",
+ LSN_FORMAT_ARGS(record.xl_prev),
+ LSN_FORMAT_ARGS(RecPtr),
+ LSN_FORMAT_ARGS(PrevRecPtr));
return false;
}
}
@@ -1188,19 +1195,26 @@ ValidXLogRecordHeader(XLogReaderState *state, XLogRecPtr RecPtr,
* SizeOfXLogRecord.
*/
static bool
-ValidXLogRecord(XLogReaderState *state, XLogRecord *record, XLogRecPtr recptr)
+ValidXLogRecord(XLogReaderState *state, XLogRecHdr record, XLogRecPtr recptr)
{
- pg_crc32c crc;
+ pg_crc32c crc,
+ hdr_crc;
+ Size hdr_len = XLogRecordHdrLen(record->xl_info);
+ Size rec_length = XLogRecordTotalLength(record);
/* Calculate the CRC */
INIT_CRC32C(crc);
- COMP_CRC32C(crc, ((char *) record) + SizeOfXLogRecord, record->xl_tot_len - SizeOfXLogRecord);
+ COMP_CRC32C(crc, ((char *) record) + hdr_len, rec_length - hdr_len);
/* include the record header last */
- COMP_CRC32C(crc, (char *) record, offsetof(XLogRecord, xl_crc));
+ COMP_CRC32C(crc, (char *) record, hdr_len - sizeof(pg_crc32c));
FIN_CRC32C(crc);
- if (!EQ_CRC32C(record->xl_crc, crc))
+ memcpy(&hdr_crc, ((char *) record) + hdr_len - sizeof(pg_crc32c),
+ sizeof(pg_crc32c));
+
+ if (!EQ_CRC32C(hdr_crc, crc))
{
+ Assert(false);
report_invalid_record(state,
"incorrect resource manager data checksum in record at %X/%X",
LSN_FORMAT_ARGS(recptr));
@@ -1440,7 +1454,7 @@ XLogFindNextRecord(XLogReaderState *state, XLogRecPtr RecPtr)
*
* Note that record headers are MAXALIGN'ed
*/
- if (MAXALIGN(header->xlp_rem_len) >= (XLOG_BLCKSZ - pageHeaderSize))
+ if (XLP_ALIGN(header->xlp_rem_len) >= (XLOG_BLCKSZ - pageHeaderSize))
tmpRecPtr = targetPagePtr + XLOG_BLCKSZ;
else
{
@@ -1449,7 +1463,7 @@ XLogFindNextRecord(XLogReaderState *state, XLogRecPtr RecPtr)
* tmpRecPtr to point to the first valid record
*/
tmpRecPtr = targetPagePtr + pageHeaderSize
- + MAXALIGN(header->xlp_rem_len);
+ + XLP_ALIGN(header->xlp_rem_len);
break;
}
}
@@ -1660,7 +1674,7 @@ DecodeXLogRecordRequiredSpace(size_t xl_tot_len)
bool
DecodeXLogRecord(XLogReaderState *state,
DecodedXLogRecord *decoded,
- XLogRecord *record,
+ XLogRecHdr record,
XLogRecPtr lsn,
char **errormsg)
{
@@ -1683,7 +1697,7 @@ DecodeXLogRecord(XLogReaderState *state,
RelFileLocator *rlocator = NULL;
uint8 block_id;
- decoded->header = *record;
+ XLogReadRecHdrInto(record, XLogRecordHdrLen(record->xl_info), &decoded->header);
decoded->lsn = lsn;
decoded->next = NULL;
decoded->record_origin = InvalidRepOriginId;
@@ -1692,8 +1706,8 @@ DecodeXLogRecord(XLogReaderState *state,
decoded->main_data_len = 0;
decoded->max_block_id = -1;
ptr = (char *) record;
- ptr += SizeOfXLogRecord;
- remaining = record->xl_tot_len - SizeOfXLogRecord;
+ ptr += XLogRecordHdrLen(record->xl_info);
+ remaining = decoded->header.xl_payload_len;
/* Decode the headers */
datatotal = 0;
@@ -1968,8 +1982,8 @@ DecodeXLogRecord(XLogReaderState *state,
}
/* Report the actual size we used. */
- decoded->size = MAXALIGN(out - (char *) decoded);
- Assert(DecodeXLogRecordRequiredSpace(record->xl_tot_len) >=
+ decoded->size = XLP_ALIGN(out - (char *) decoded);
+ Assert(DecodeXLogRecordRequiredSpace(decoded->header.xl_payload_len) >=
decoded->size);
return true;
@@ -1998,6 +2012,7 @@ XLogRecGetBlockTag(XLogReaderState *record, uint8 block_id,
if (!XLogRecGetBlockTagExtended(record, block_id, rlocator, forknum,
blknum, NULL))
{
+ Assert(false);
#ifndef FRONTEND
elog(ERROR, "could not locate backup block with ID %d in WAL record",
block_id);
diff --git a/src/backend/access/transam/xlogrecovery.c b/src/backend/access/transam/xlogrecovery.c
index 56fa2f74a2..0362ba747f 100644
--- a/src/backend/access/transam/xlogrecovery.c
+++ b/src/backend/access/transam/xlogrecovery.c
@@ -4000,10 +4000,10 @@ ReadCheckpointRecord(XLogPrefetcher *xlogprefetcher, XLogRecPtr RecPtr,
(errmsg("invalid xl_rmgrinfo in checkpoint record")));
return NULL;
}
- if (record->xl_tot_len != SizeOfXLogRecord + SizeOfXLogRecordDataHeaderShort + sizeof(CheckPoint))
+ if (record->xl_payload_len != SizeOfXLogRecordDataHeaderShort + sizeof(CheckPoint))
{
ereport(LOG,
- (errmsg("invalid length of checkpoint record")));
+ (errmsg("invalid payload length of checkpoint record")));
return NULL;
}
return record;
diff --git a/src/bin/pg_resetwal/pg_resetwal.c b/src/bin/pg_resetwal/pg_resetwal.c
index 80cb9a5bc4..4e171eed1f 100644
--- a/src/bin/pg_resetwal/pg_resetwal.c
+++ b/src/bin/pg_resetwal/pg_resetwal.c
@@ -1040,12 +1040,15 @@ WriteEmptyXLOG(void)
PGAlignedXLogBlock buffer;
XLogPageHeader page;
XLogLongPageHeader longpage;
- XLogRecord *record;
pg_crc32c crc;
char path[MAXPGPATH];
int fd;
int nbytes;
char *recptr;
+ char *baserecptr;
+ const int rec_hdr_len = offsetof(XLogRecHdrData, xl_hdrdata) +
+ sizeof(uint8) + sizeof(XLogRecPtr) + sizeof(pg_crc32c);
+ const int rec_payload_len = sizeof(uint8) * 2 + sizeof(CheckPoint);
memset(buffer.data, 0, XLOG_BLCKSZ);
@@ -1061,26 +1064,33 @@ WriteEmptyXLOG(void)
longpage->xlp_xlog_blcksz = XLOG_BLCKSZ;
/* Insert the initial checkpoint record */
- recptr = (char *) page + SizeOfXLogLongPHD;
- record = (XLogRecord *) recptr;
- record->xl_prev = 0;
- record->xl_xid = InvalidTransactionId;
- record->xl_tot_len = SizeOfXLogRecord + SizeOfXLogRecordDataHeaderShort + sizeof(CheckPoint);
- record->xl_info = 0;
- record->xl_rmgrinfo = XLOG_CHECKPOINT_SHUTDOWN;
- record->xl_rmid = RM_XLOG_ID;
-
- recptr += SizeOfXLogRecord;
+ baserecptr = recptr = (char *) page + SizeOfXLogLongPHD;
+ *(recptr++) = (char) XLS_UINT8;
+ *(recptr++) = (char) RM_XLOG_ID;
+ recptr += XLogWriteLength(SizeOfXLogRecordDataHeaderShort + sizeof(CheckPoint),
+ XLS_UINT8, XLS_UINT8, recptr);
+
+ /* include prevptr */
+ {
+ XLogRecPtr prevptr = 0;
+ memcpy(recptr, &prevptr, sizeof(XLogRecPtr));
+ recptr += sizeof(XLogRecPtr);
+ }
+ /* reserve location of crc */
+ recptr += sizeof(pg_crc32c);
+
*(recptr++) = (char) XLR_BLOCK_ID_DATA_SHORT;
*(recptr++) = sizeof(CheckPoint);
memcpy(recptr, &ControlFile.checkPointCopy,
sizeof(CheckPoint));
INIT_CRC32C(crc);
- COMP_CRC32C(crc, ((char *) record) + SizeOfXLogRecord, record->xl_tot_len - SizeOfXLogRecord);
- COMP_CRC32C(crc, (char *) record, offsetof(XLogRecord, xl_crc));
+ COMP_CRC32C(crc, baserecptr + rec_hdr_len, rec_payload_len);
+ COMP_CRC32C(crc, baserecptr, rec_hdr_len - sizeof(pg_crc32c));
FIN_CRC32C(crc);
- record->xl_crc = crc;
+
+ memcpy(baserecptr + rec_hdr_len - sizeof(pg_crc32c),
+ &crc, sizeof(pg_crc32c));
/* Write the first page */
XLogFilePath(path, ControlFile.checkPointCopy.ThisTimeLineID,
diff --git a/src/include/access/xlog_internal.h b/src/include/access/xlog_internal.h
index 8b3c9b9598..ea32a9ba1b 100644
--- a/src/include/access/xlog_internal.h
+++ b/src/include/access/xlog_internal.h
@@ -26,7 +26,15 @@
#include "pgtime.h"
#include "storage/block.h"
#include "storage/relfilelocator.h"
+#include "transam.h"
+/*
+ * WAL records (and other XLogPage page-level content) are aligned to 8 bytes.
+ *
+ * Note that the contents of the records is not aligned (!)
+ */
+#define ALIGNOF_XLP_CONTENT 8
+#define XLP_ALIGN(LEN) TYPEALIGN64(ALIGNOF_XLP_CONTENT, LEN)
/*
* Each page of XLOG file has a header like this:
@@ -49,7 +57,7 @@ typedef struct XLogPageHeaderData
uint32 xlp_rem_len; /* total len of remaining data for record */
} XLogPageHeaderData;
-#define SizeOfXLogShortPHD MAXALIGN(sizeof(XLogPageHeaderData))
+#define SizeOfXLogShortPHD XLP_ALIGN(sizeof(XLogPageHeaderData))
typedef XLogPageHeaderData *XLogPageHeader;
@@ -66,7 +74,7 @@ typedef struct XLogLongPageHeaderData
uint32 xlp_xlog_blcksz; /* just as a cross-check */
} XLogLongPageHeaderData;
-#define SizeOfXLogLongPHD MAXALIGN(sizeof(XLogLongPageHeaderData))
+#define SizeOfXLogLongPHD XLP_ALIGN(sizeof(XLogLongPageHeaderData))
typedef XLogLongPageHeaderData *XLogLongPageHeader;
@@ -381,7 +389,7 @@ static inline Size XLogWriteLength(uint32 length, XLogSizeClass sizeClass,
case caseSizeClass: \
if ((caseSizeClass) <= maxSizeClass) \
{ \
- field_type typedLength = length; \
+ field_type typedLength = (field_type) length; \
memcpy(output, &typedLength, sizeof(field_type)); \
written = sizeof(field_type); \
} \
@@ -394,6 +402,9 @@ static inline Size XLogWriteLength(uint32 length, XLogSizeClass sizeClass,
WRITE_OP(XLS_UINT8, uint8);
WRITE_OP(XLS_UINT16, uint16);
WRITE_OP(XLS_UINT32, uint32);
+ default:
+ Assert(false);
+ pg_unreachable();
}
#undef WRITE_OP
@@ -436,12 +447,95 @@ static inline Size XLogReadLength(uint32 *length, XLogSizeClass sizeClass,
READ_OP(XLS_UINT8, uint8);
READ_OP(XLS_UINT16, uint16);
READ_OP(XLS_UINT32, uint32);
+ default:
+ Assert(false);
+ pg_unreachable();
}
#undef READ_OP
return readSize;
}
+
+inline static uint8 XLRHdrGetRmgrInfo(XLogRecHdr record)
+{
+ XLogSizeClass recSizeClass;
+ int offset;
+
+ if (!(record->xl_info & XLR_HAS_RMGRINFO))
+ return 0;
+
+ recSizeClass = XLR_SIZECLASS(record->xl_info);
+ /* xl_rmgrinfo is located immediately behind the xl_payload_len field */
+ offset = XLogSizeClassToByteLength(recSizeClass);
+
+ return (uint8) record->xl_hdrdata[offset];
+}
+
+
+/* Works on any partial record */
+inline static Size XLogRecordTotalLength(XLogRecHdr record)
+{
+ uint8 xl_info = record->xl_info;
+ XLogSizeClass sizeClass;
+ uint32 length = 0;
+ sizeClass = XLR_SIZECLASS(xl_info);
+
+ XLogReadLength(&length, sizeClass, XLS_UINT32,
+ &record->xl_hdrdata[0], 6);
+
+ return (Size) length + XLogRecordHdrLen(xl_info);
+}
+
+inline static void XLogReadRecHdrInto(XLogRecHdr recdata, Size length,
+ XLogRecord *record)
+{
+ Size offset = 0;
+ Size hdr_size PG_USED_FOR_ASSERTS_ONLY = 0;
+ XLogSizeClass sizeClass;
+
+ Assert(length >= XLogRecordMinHdrSize);
+
+ record->xl_info = recdata->xl_info;
+
+ hdr_size = XLogRecordHdrLen(record->xl_info);
+ Assert(length >= hdr_size);
+
+ record->xl_rmid = recdata->xl_rmid;
+
+ sizeClass = XLR_SIZECLASS(record->xl_info);
+ offset += XLogReadLength(&record->xl_payload_len, sizeClass,
+ XLS_UINT32, &recdata->xl_hdrdata[offset], length - offset);
+
+ if (record->xl_info & XLR_HAS_RMGRINFO)
+ {
+ record->xl_rmgrinfo = recdata->xl_hdrdata[offset];
+ offset += sizeof(uint8);
+ }
+ else
+ {
+ record->xl_rmgrinfo = 0;
+ }
+
+ if (record->xl_info & XLR_HAS_XID)
+ {
+ memcpy(&record->xl_xid, &recdata->xl_hdrdata[offset], sizeof(TransactionId));
+ offset += sizeof(TransactionId);
+ }
+ else
+ {
+ record->xl_xid = InvalidTransactionId;
+ }
+
+ memcpy(&record->xl_prev, &recdata->xl_hdrdata[offset], sizeof(XLogRecPtr));
+ offset += sizeof(XLogRecPtr);
+
+ memcpy(&record->xl_crc, &recdata->xl_hdrdata[offset], sizeof(pg_crc32c));
+ offset += sizeof(pg_crc32c);
+
+ Assert(hdr_size - 2 == offset);
+}
+
/*
* Method table for resource managers.
*
diff --git a/src/include/access/xlogreader.h b/src/include/access/xlogreader.h
index a1d0216404..bb64f89dba 100644
--- a/src/include/access/xlogreader.h
+++ b/src/include/access/xlogreader.h
@@ -397,7 +397,7 @@ extern bool WALRead(XLogReaderState *state,
extern size_t DecodeXLogRecordRequiredSpace(size_t xl_tot_len);
extern bool DecodeXLogRecord(XLogReaderState *state,
DecodedXLogRecord *decoded,
- XLogRecord *record,
+ XLogRecHdr record,
XLogRecPtr lsn,
char **errormsg);
@@ -405,7 +405,7 @@ extern bool DecodeXLogRecord(XLogReaderState *state,
* Macros that provide access to parts of the record most recently returned by
* XLogReadRecord() or XLogNextRecord().
*/
-#define XLogRecGetTotalLen(decoder) ((decoder)->record->header.xl_tot_len)
+#define XLogRecGetTotalLen(decoder) ((decoder)->record->header.xl_payload_len)
#define XLogRecGetPrev(decoder) ((decoder)->record->header.xl_prev)
#define XLogRecGetInfo(decoder) ((decoder)->record->header.xl_info)
#define XLogRecGetRmgrInfo(decoder) ((decoder)->record->header.xl_rmgrinfo)
diff --git a/src/include/access/xlogrecord.h b/src/include/access/xlogrecord.h
index 42b06f163e..af615e2866 100644
--- a/src/include/access/xlogrecord.h
+++ b/src/include/access/xlogrecord.h
@@ -38,9 +38,38 @@ typedef enum XLogSizeClass {
XLS_UINT32 = 3 /* length <= UINT32_MAX; stored in uint32 (4B) */
} XLogSizeClass;
+static inline int XLogSizeClassToByteLength(XLogSizeClass sz)
+{
+ switch (sz) {
+ case XLS_EMPTY:
+ return 0;
+ case XLS_UINT8:
+ return sizeof(uint8);
+ case XLS_UINT16:
+ return sizeof(uint16);
+ case XLS_UINT32:
+ return sizeof(uint32);
+ default:
+ pg_unreachable();
+ }
+}
+
+typedef struct XLogRecord
+{
+ uint8 xl_info; /* flag bits, see below */
+ RmgrId xl_rmid; /* resource manager for this record */
+ uint32 xl_payload_len; /* total len of entire record */
+ uint8 xl_rmgrinfo; /* rmgr flag bits, see below */
+ TransactionId xl_xid; /* xact id */
+ XLogRecPtr xl_prev; /* ptr to previous record in log */
+ pg_crc32c xl_crc; /* CRC for this record */
+} XLogRecord;
+
+#define SizeOfXLogRecord (offsetof(XLogRecord, xl_crc) + sizeof(pg_crc32c))
+
/*
* The overall layout of an XLOG record is:
- * Fixed-size header (XLogRecord struct)
+ * Variable-size header (containing the data of the XLogRecord struct)
* XLogRecordBlockHeader struct
* XLogRecordBlockHeader struct
* ...
@@ -50,6 +79,15 @@ typedef enum XLogSizeClass {
* ...
* main data
*
+ * Different xlog record headers need to store different header fields, so
+ * depending on flags in the xl_info field the layout may change.
+ *
+ * Note that for records with a payload larger than 0xFFFFFFF, the size field
+ * will be 4 bytes long, which can be larger than the minimum MAXALIGN quantum.
+ * To still be able to read the record correctly, we always align WAL to 8
+ * bytes, so that the length bytes are always present in the first aligned
+ * quantum of data.
+ *
* There can be zero or more XLogRecordBlockHeaders, and 0 or more bytes of
* rmgr-specific data not associated with a block. XLogRecord structs
* always start on MAXALIGN boundaries in the WAL files, but the rest of
@@ -59,22 +97,25 @@ typedef enum XLogSizeClass {
* XLogRecordDataHeaderLong structs all begin with a single 'id' byte. It's
* used to distinguish between block references, and the main data structs.
*/
-typedef struct XLogRecord
+typedef struct XLogRecHdrData
{
- uint32 xl_tot_len; /* total len of entire record */
- TransactionId xl_xid; /* xact id */
- XLogRecPtr xl_prev; /* ptr to previous record in log */
uint8 xl_info; /* flag bits, see below */
RmgrId xl_rmid; /* resource manager for this record */
- uint8 xl_rmgrinfo; /* rmgr flag bits, see below */
- /* 1 byte of padding here, initialize to zero */
- pg_crc32c xl_crc; /* CRC for this record */
-
- /* XLogRecordBlockHeaders and XLogRecordDataHeader follow, no padding */
+ char xl_hdrdata[FLEXIBLE_ARRAY_MEMBER]; /* variable length record data */
-} XLogRecord;
+ /*
+ * Payload is as follows, in order:
+ * XLogRecord data encoded
+ * xl_payload_len; 0, 1, 2 or 4 bytes
+ * xl_rmgrinfo; 0 or 1 byte
+ * xl_xid; 0 or 4 bytes
+ * xl_prev; 8 bytes
+ * xl_prev follows; 8 bytes
+ * record payload, if present.
+ */
+} XLogRecHdrData;
-#define SizeOfXLogRecord (offsetof(XLogRecord, xl_crc) + sizeof(pg_crc32c))
+typedef XLogRecHdrData *XLogRecHdr;
/*
* XLogReader needs to allocate all the data of a WAL record in a single
@@ -87,13 +128,28 @@ typedef struct XLogRecord
*/
#define XLogRecordMaxSize (1020 * 1024 * 1024)
+#define XLR_SIZECLASS_MASK 0x03
+#define XLR_SIZECLASS(xl_info) ((XLogSizeClass) ((xl_info) & XLR_SIZECLASS_MASK))
+
+/*
+ * There are several rmgrs which don't use info bits, so we omit that
+ * byte whenever possible.
+ */
+#define XLR_HAS_RMGRINFO 0x04
+
+/*
+ * If a WAL record uses the current transaction ID, that will be included
+ * in the record header. In all other cases we omit the XID to save bytes.
+ */
+#define XLR_HAS_XID 0x08
+
/*
* If a WAL record modifies any relation files, in ways not covered by the
* usual block references, this flag is set. This is not used for anything
* by PostgreSQL itself, but it allows external tools that read WAL and keep
* track of modified blocks to recognize such special record types.
*/
-#define XLR_SPECIAL_REL_UPDATE 0x01
+#define XLR_SPECIAL_REL_UPDATE 0x10
/*
* Enforces consistency checks of replayed WAL at recovery. If enabled,
@@ -102,7 +158,48 @@ typedef struct XLogRecord
* of XLogInsert can use this value if necessary, but if
* wal_consistency_checking is enabled for a rmgr this is set unconditionally.
*/
-#define XLR_CHECK_CONSISTENCY 0x02
+#define XLR_CHECK_CONSISTENCY (0x20)
+
+#define XLogRecordMaxHdrSize ( \
+ sizeof(uint8) /* xl_info */ + \
+ sizeof(uint8) /* xl_rmid */ + \
+ sizeof(uint32) /* xl_payload_len */ + \
+ sizeof(uint8) /* xl_rmgrinfo */ + \
+ sizeof(TransactionId) /* xl_xid */ + \
+ sizeof(XLogRecPtr) /* xl_prev */ + \
+ sizeof(pg_crc32c) /* xl_crc */ \
+)
+
+#define XLogRecordMinHdrSize ( \
+ sizeof(uint8) /* xl_info */ + \
+ sizeof(uint8) /* xl_rmid */ + \
+ 0 /* xl_payload_len */ + \
+ 0 /* xl_rmgrinfo */ + \
+ 0 /* xl_xid */ + \
+ sizeof(XLogRecPtr) /* xl_prev */ + \
+ sizeof(pg_crc32c) /* xl_crc */ \
+)
+
+static inline Size XLogRecordHdrLen(uint8 info)
+{
+ Size size = 0;
+ /* xl_info */
+ size += sizeof(uint8);
+ /* xl_rmid */
+ size += sizeof(uint8);
+ /* xl_payload_len */
+ size += XLogSizeClassToByteLength(XLR_SIZECLASS(info));
+ /* xl_rmgrinfo */
+ size += ((info) & XLR_HAS_RMGRINFO) ? sizeof(uint8) : 0;
+ /* xl_xid */
+ size += ((info) & XLR_HAS_XID) ? sizeof(TransactionId) : 0;
+ /* xl_prev */
+ size += sizeof(XLogRecPtr);
+ /* xl_crc */
+ size += sizeof(pg_crc32c);
+
+ return size;
+}
/*
* Header info for block data appended to an XLOG record.
--
2.40.1