/* It might also be the newest item decoded, decode_queue_tail. */ if (state->decode_queue_tail == record)
state->decode_queue_tail = NULL;
/* Release the space. */ if (unlikely(record->oversized))
{ /* It's not in the decode buffer, so free it to release space. */
pfree(record);
} else
{ /* It must be the head (oldest) record in the decode buffer. */
Assert(state->decode_buffer_head == (char *) record);
/* *Weneedtoupdateheadtopointtothenextrecordthatisinthe *decodebuffer,ifany,beingcarefultoskipoversizedones *(they'renotinthedecodebuffer).
*/
record = record->next; while (unlikely(record && record->oversized))
record = record->next;
if (record)
{ /* Adjust head to release space up to the next record. */
state->decode_buffer_head = (char *) record;
} else
{ /* *Otherwisewemightaswelljustresetheadandtailtothe *startofthebufferspace,becausewe'reempty.Thismeans *we'llkeepoverwritingthesamepieceofmemoryifwe'renot *doinganyprefetching.
*/
state->decode_buffer_head = state->decode_buffer;
state->decode_buffer_tail = state->decode_buffer;
}
}
return next_lsn;
}
/* *AttempttoreadanXLOGrecord. * *XLogBeginRead()orXLogFindNextRecord()andthenXLogReadAhead()mustbe *calledbeforethefirstcalltoXLogNextRecord().Thisfunctionsreturns *recordsanderrorsthatwereputintoaninternalqueuebyXLogReadAhead(). * *Onsuccess,arecordisreturned. * *Thereturnedrecord(or*errormsg)pointstoaninternalbufferthat's *validuntilthenextcalltoXLogNextRecord.
*/
DecodedXLogRecord *
XLogNextRecord(XLogReaderState *state, char **errormsg)
{ /* Release the last record returned by XLogNextRecord(). */
XLogReleasePreviousRecord(state);
if (state->decode_queue_head == NULL)
{
*errormsg = NULL; if (state->errormsg_deferred)
{ if (state->errormsg_buf[0] != '\0')
*errormsg = state->errormsg_buf;
state->errormsg_deferred = false;
}
/* Allocate a circular decode buffer if we don't have one already. */ if (unlikely(state->decode_buffer == NULL))
{ if (state->decode_buffer_size == 0)
state->decode_buffer_size = DEFAULT_DECODE_BUFFER_SIZE;
state->decode_buffer = palloc(state->decode_buffer_size);
state->decode_buffer_head = state->decode_buffer;
state->decode_buffer_tail = state->decode_buffer;
state->free_decode_buffer = true;
}
/* Try to allocate space in the circular decode buffer. */ if (state->decode_buffer_tail >= state->decode_buffer_head)
{ /* Empty, or tail is to the right of head. */ if (required_space <=
state->decode_buffer_size -
(state->decode_buffer_tail - state->decode_buffer))
{ /*- *Thereisspacebetweentailandend. * *+-----+--------------------+-----+ *||////////////////////|here!| *+-----+--------------------+-----+ *^^ *|| *ht
*/
decoded = (DecodedXLogRecord *) state->decode_buffer_tail;
decoded->oversized = false; return decoded;
} elseif (required_space <
state->decode_buffer_head - state->decode_buffer)
{ /*- *Thereisspacebetweenstartandhead. * *+-----+--------------------+-----+ *|here!|////////////////////| | *+-----+--------------------+-----+ *^^ *|| *ht
*/
decoded = (DecodedXLogRecord *) state->decode_buffer;
decoded->oversized = false; return decoded;
}
} else
{ /* Tail is to the left of head. */ if (required_space <
state->decode_buffer_head - state->decode_buffer_tail)
{ /*- *Thereisspacebetweentailandhead. * *+-----+--------------------+-----+ *|/////|here! |/////| *+-----+--------------------+-----+ *^^ *|| *th
*/
decoded = (DecodedXLogRecord *) state->decode_buffer_tail;
decoded->oversized = false; return decoded;
}
}
/* Not enough space in the decode buffer. Are we allowed to allocate? */ if (allow_oversized)
{
decoded = palloc(required_space);
decoded->oversized = true; return decoded;
}
/* *Ifthewholerecordheaderisonthispage,validateitimmediately. *Otherwisedojustabasicsanitycheckonxl_tot_len,andvalidatethe *restoftheheaderafterreadingitfromthenextpage.Thexl_tot_len *checkisnecessaryheretoensurethatweenterthe"Needtoreassemble *record"codepathbelow;otherwisewemightfailtoapply *ValidXLogRecordHeaderatall.
*/ if (targetRecOff <= XLOG_BLCKSZ - SizeOfXLogRecord)
{ if (!ValidXLogRecordHeader(state, RecPtr, state->DecodeRecPtr, record,
randAccess)) goto err;
gotheader = true;
} else
{ /* There may be no next page if it's too small. */ if (total_len < SizeOfXLogRecord)
{
report_invalid_record(state, "invalid record length at %X/%X: expected at least %u, got %u",
LSN_FORMAT_ARGS(RecPtr),
(uint32) SizeOfXLogRecord, total_len); goto err;
} /* We'll validate the header once we have the next page. */
gotheader = false;
}
/* Copy the first fragment of the record from the first page. */
memcpy(state->readRecordBuf,
state->readBuf + RecPtr % XLOG_BLCKSZ, len);
buffer = state->readRecordBuf + len;
gotlen = len;
do
{ /* Calculate pointer to beginning of next page */
targetPagePtr += XLOG_BLCKSZ;
/* Check that the continuation on next page looks valid */ if (!(pageHeader->xlp_info & XLP_FIRST_IS_CONTRECORD))
{
report_invalid_record(state, "there is no contrecord flag at %X/%X",
LSN_FORMAT_ARGS(RecPtr)); goto err;
}
/* Wait for the next page to become available */
readOff = ReadPageInternal(state, targetPagePtr,
Min(total_len - gotlen + SizeOfXLogShortPHD,
XLOG_BLCKSZ)); if (readOff == XLREAD_WOULDBLOCK) return XLREAD_WOULDBLOCK; elseif (readOff < 0) goto err;
/* Append the continuation from this page to the buffer */
pageHeaderSize = XLogPageHeaderSize(pageHeader);
if (readOff < pageHeaderSize)
readOff = ReadPageInternal(state, targetPagePtr,
pageHeaderSize);
Assert(pageHeaderSize <= readOff);
contdata = (char *) state->readBuf + pageHeaderSize;
len = XLOG_BLCKSZ - pageHeaderSize; if (pageHeader->xlp_rem_len < len)
len = pageHeader->xlp_rem_len;
/* If we just reassembled the record header, validate it. */ if (!gotheader)
{
record = (XLogRecord *) state->readRecordBuf; if (!ValidXLogRecordHeader(state, RecPtr, state->DecodeRecPtr,
record, randAccess)) goto err;
gotheader = true;
}
record = (XLogRecord *) 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);
} else
{ /* Wait for the record data to become available */
readOff = ReadPageInternal(state, targetPagePtr,
Min(targetRecOff + total_len, XLOG_BLCKSZ)); if (readOff == XLREAD_WOULDBLOCK) return XLREAD_WOULDBLOCK; elseif (readOff < 0) goto err;
/* Record does not cross a page boundary */ if (!ValidXLogRecord(state, record, RecPtr)) goto err;
state->NextRecPtr = RecPtr + MAXALIGN(total_len);
state->DecodeRecPtr = RecPtr;
}
/* *Specialprocessingifit'sanXLOGSWITCHrecord
*/ if (record->xl_rmid == RM_XLOG_ID &&
(record->xl_info & ~XLR_INFO_MASK) == XLOG_SWITCH)
{ /* Pretend it extends to end of segment */
state->NextRecPtr += state->segcxt.ws_segsize - 1;
state->NextRecPtr -= XLogSegmentOffset(state->NextRecPtr, state->segcxt.ws_segsize);
}
/* *IfwegotherewithoutaDecodedXLogRecord,itmeansweneededto *validatetotal_lenbeforetrustingit,butbynowwe'vedonethat.
*/ if (decoded == NULL)
{
Assert(!nonblocking);
decoded = XLogReadRecordAlloc(state,
total_len, true/* allow_oversized */ ); /* allocation should always happen under allow_oversized */
Assert(decoded != NULL);
}
if (DecodeXLogRecord(state, decoded, record, RecPtr, &errormsg))
{ /* Record the location of the next record. */
decoded->next_lsn = state->NextRecPtr;
/* *Ifit'sinthedecodebuffer,markthedecodebufferspaceas *occupied.
*/ if (!decoded->oversized)
{ /* The new decode buffer head must be MAXALIGNed. */
Assert(decoded->size == MAXALIGN(decoded->size)); if ((char *) decoded == state->decode_buffer)
state->decode_buffer_tail = state->decode_buffer + decoded->size; else
state->decode_buffer_tail += decoded->size;
}
/* Insert it into the queue of decoded records. */
Assert(state->decode_queue_tail != decoded); if (state->decode_queue_tail)
state->decode_queue_tail->next = decoded;
state->decode_queue_tail = decoded; if (!state->decode_queue_head)
state->decode_queue_head = decoded; return XLREAD_SUCCESS;
}
/* check whether we have all the requested data already */ if (targetSegNo == state->seg.ws_segno &&
targetPageOff == state->segoff && reqLen <= state->readLen) return state->readLen;
/* Calculate the CRC */
INIT_CRC32C(crc);
COMP_CRC32C(crc, ((char *) record) + SizeOfXLogRecord, record->xl_tot_len - SizeOfXLogRecord); /* include the record header last */
COMP_CRC32C(crc, (char *) record, offsetof(XLogRecord, xl_crc));
FIN_CRC32C(crc);
if (!EQ_CRC32C(record->xl_crc, crc))
{
report_invalid_record(state, "incorrect resource manager data checksum in record at %X/%X",
LSN_FORMAT_ARGS(recptr)); returnfalse;
}
/* hmm, first page of file doesn't have a long header? */
report_invalid_record(state, "invalid info bits %04X in WAL segment %s, LSN %X/%X, offset %u",
hdr->xlp_info,
fname,
LSN_FORMAT_ARGS(recptr),
offset); returnfalse;
}
/* *weknownowthattmpRecPtrisanaddresspointingtoavalidXLogRecord *becauseeitherwe'reatthefirstrecordafterthebeginningofapage *orwejustjumpedovertheremainingdataofacontinuation.
*/
XLogBeginRead(state, tmpRecPtr); while (XLogReadRecord(state, &errormsg) != NULL)
{ /* past the record we've found, break out */ if (RecPtr <= state->ReadRecPtr)
{ /* Rewind the reader to the beginning of the last record. */
found = state->ReadRecPtr;
XLogBeginRead(state, found); return found;
}
}
/* This shouldn't happen -- indicates a bug in segment_open */
Assert(state->seg.ws_file >= 0);
/* Update the current segment info. */
state->seg.ws_tli = tli;
state->seg.ws_segno = nextSegNo;
}
/* How many bytes are within this segment? */ if (nbytes > (state->segcxt.ws_segsize - startoff))
segbytes = state->segcxt.ws_segsize - startoff; else
segbytes = nbytes;
/* andfinally, the main data */ if (decoded->main_data_len > 0)
{
out = (char *) MAXALIGN(out);
decoded->main_data = out;
memcpy(decoded->main_data, ptr, decoded->main_data_len);
ptr += decoded->main_data_len;
out += decoded->main_data_len;
}
/* Report the actual size we used. */
decoded->size = MAXALIGN(out - (char *) decoded); Assert(DecodeXLogRecordRequiredSpace(record->xl_tot_len) >=
decoded->size);
returntrue;
shortdata_err:
report_invalid_record(state, "record with invalid length at %X/%X",
LSN_FORMAT_ARGS(state->ReadRecPtr));
err:
*errormsg = state->errormsg_buf;
returnfalse;
}
/*
* Returns information about the block that a block reference refers to.
*
* This is like XLogRecGetBlockTagExtended, except that the block reference
* must exist and there's no access to prefetch_buffer.
*/
void
XLogRecGetBlockTag(XLogReaderState *record, uint8 block_id,
RelFileLocator *rlocator, ForkNumber *forknum,
BlockNumber *blknum)
{ if (!XLogRecGetBlockTagExtended(record, block_id, rlocator, forknum,
blknum, NULL))
{ #ifndef FRONTEND
elog(ERROR, "could not locate backup block with ID %d in WAL record",
block_id); #else
pg_fatal("could not locate backup block with ID %d in WAL record",
block_id); #endif
}
}
/*
* Returns information about the block that a block reference refers to,
* optionally including the buffer that the block may already be in.
*
* If the WAL record contains a block reference with the given ID, *rlocator,
* *forknum, *blknum and *prefetch_buffer are filled in (ifnot NULL), and
* returns true. Otherwise returns false.
*/
bool
XLogRecGetBlockTagExtended(XLogReaderState *record, uint8 block_id,
RelFileLocator *rlocator, ForkNumber *forknum,
BlockNumber *blknum,
Buffer *prefetch_buffer)
{
DecodedBkpBlock *bkpb;
if (!XLogRecHasBlockRef(record, block_id)) returnfalse;
bkpb = &record->record->blocks[block_id]; if (rlocator)
*rlocator = bkpb->rlocator; if (forknum)
*forknum = bkpb->forknum; if (blknum)
*blknum = bkpb->blkno; if (prefetch_buffer)
*prefetch_buffer = bkpb->prefetch_buffer; returntrue;
}
/*
* Returns the data associated with a block reference, or NULL if there is
* no data (e.g. because a full-page image was taken instead). The returned
* pointer points to a MAXALIGNed buffer.
*/
char *
XLogRecGetBlockData(XLogReaderState *record, uint8 block_id, Size *len)
{
DecodedBkpBlock *bkpb;
if (block_id > record->record->max_block_id ||
!record->record->blocks[block_id].in_use) return NULL;
bkpb = &record->record->blocks[block_id];
if (!bkpb->has_data)
{ if (len)
*len = 0; return NULL;
} else
{ if (len)
*len = bkpb->data_len; return bkpb->data;
}
}
/*
* Restore a full-page image from a backup block attached to an XLOG record.
*
* Returns trueif a full-page image is restored, andfalse on failure with
* an error to be consumed by the caller.
*/
bool
RestoreBlockImage(XLogReaderState *record, uint8 block_id, char *page)
{
DecodedBkpBlock *bkpb;
char *ptr;
PGAlignedBlock tmp;
if (block_id > record->record->max_block_id ||
!record->record->blocks[block_id].in_use)
{
report_invalid_record(record, "could not restore image at %X/%X with invalid block %d specified",
LSN_FORMAT_ARGS(record->ReadRecPtr),
block_id); returnfalse;
} if (!record->record->blocks[block_id].has_image)
{
report_invalid_record(record, "could not restore image at %X/%X with invalid state, block %d",
LSN_FORMAT_ARGS(record->ReadRecPtr),
block_id); returnfalse;
}
if (ZSTD_isError(decomp_result))
decomp_success = false; #else
report_invalid_record(record, "could not restore image at %X/%X compressed with %s not supported by build, block %d",
LSN_FORMAT_ARGS(record->ReadRecPtr), "zstd",
block_id); returnfalse; #endif
} else
{
report_invalid_record(record, "could not restore image at %X/%X compressed with unknown method, block %d",
LSN_FORMAT_ARGS(record->ReadRecPtr),
block_id); returnfalse;
}
if (!decomp_success)
{
report_invalid_record(record, "could not decompress image at %X/%X, block %d",
LSN_FORMAT_ARGS(record->ReadRecPtr),
block_id); returnfalse;
}
ptr = tmp.data;
}
/* generate page, taking into account hole if necessary */ if (bkpb->hole_length == 0)
{
memcpy(page, ptr, BLCKSZ);
} else
{
memcpy(page, ptr, bkpb->hole_offset);
/* must zero-fill the hole */
MemSet(page + bkpb->hole_offset, 0, bkpb->hole_length);
memcpy(page + (bkpb->hole_offset + bkpb->hole_length),
ptr + bkpb->hole_offset,
BLCKSZ - (bkpb->hole_offset + bkpb->hole_length));
}
returntrue;
}
#ifndef FRONTEND
/*
* Extract the FullTransactionId from a WAL record.
*/
FullTransactionId
XLogRecGetFullXid(XLogReaderState *record)
{
/*
* This function is only safe during replay, because it depends on the
* replay state. See AdvanceNextFullTransactionIdPastXid() for more.
*/ Assert(AmStartupProcess() || !IsUnderPostmaster);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.