1/*-------------------------------------------------------------------------
5 * PostgreSQL write-ahead log manager utility routines
7 * This file contains support routines that are used by XLOG replay functions.
8 * None of this code is used during normal system operation.
11 * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
12 * Portions Copyright (c) 1994, Regents of the University of California
14 * src/backend/access/transam/xlogutils.c
16 *-------------------------------------------------------------------------
37 * Are we doing recovery from XLOG?
39 * This is only ever true in the startup process; it should be read as meaning
40 * "this process is replaying WAL records", rather than "the system is in
41 * recovery mode". It should be examined primarily by functions that need
42 * to act differently when called from a WAL redo function (e.g., to skip WAL
43 * logging). To check whether the system is in recovery regardless of which
44 * process you're running in, use RecoveryInProgress() but only after shared
45 * memory startup and lock initialization.
47 * This is updated from xlog.c and xlogrecovery.c, but lives here because
48 * it's mostly read by WAL redo functions.
52/* Are we in Hot Standby mode? Only valid in startup process, see xlogutils.h */
56 * During XLOG replay, we may see XLOG records for incremental updates of
57 * pages that no longer exist, because their relation was later dropped or
58 * truncated. (Note: this is only possible when full_page_writes = OFF,
59 * since when it's ON, the first reference we see to a page should always
60 * be a full-page rewrite not an incremental update.) Rather than simply
61 * ignoring such records, we make a note of the referenced page, and then
62 * complain if we don't actually see a drop or truncate covering the page
75 bool present;
/* page existed but contained zeroes */
82 char *cur_page,
bool wait_for_wal);
84/* Report a reference to an invalid page */
92 elog(elevel,
"page %u of relation %s is uninitialized",
95 elog(elevel,
"page %u of relation %s does not exist",
99/* Log a reference to an invalid page */
109 * Once recovery has reached a consistent state, the invalid-page table
110 * should be empty and remain so. If a reference to an invalid page is
111 * found after consistency is reached, PANIC immediately. This might seem
112 * aggressive, but it's better than letting the invalid reference linger
113 * in the hash table until the end of recovery and PANIC there, which
114 * might come only much later if this is a standby server.
120 "WAL contains references to invalid pages");
124 * Log references to invalid pages at DEBUG1 level. This allows some
125 * tracing of the cause (note the elog context mechanism will tell us
126 * something about the XLOG record that generated the reference).
133 /* create hash table when first needed */
145 /* we currently assume xl_invalid_page_key contains no padding */
146 key.locator = locator;
154 /* hash_search already filled in the key */
159 /* repeat reference ... leave "present" as it was */
163/* Forget any invalid pages >= minblkno, because they've been dropped */
172 return;
/* nothing to do */
182 elog(
DEBUG2,
"page %u of relation %s has been dropped",
194/* Forget any invalid pages in a whole database */
202 return;
/* nothing to do */
210 elog(
DEBUG2,
"page %u of relation %s has been dropped",
222/* Are there any unresolved references to invalid pages? */
232/* Complain about any remaining invalid-page entries */
238 bool foundone =
false;
241 return;
/* nothing to do */
246 * Our strategy is to emit WARNING messages for all remaining entries and
247 * only PANIC after we've dumped all the available info.
258 "WAL contains references to invalid pages");
266 * XLogReadBufferForRedo
267 * Read a page during XLOG replay
269 * Reads a block referenced by a WAL record into shared buffer cache, and
270 * determines what needs to be done to redo the changes to it. If the WAL
271 * record includes a full-page image of the page, it is restored.
273 * 'record.EndRecPtr' is compared to the page's LSN to determine if the record
274 * has already been replayed. 'block_id' is the ID number the block was
275 * registered with, when the WAL record was created.
277 * Returns one of the following:
279 * BLK_NEEDS_REDO - changes from the WAL record need to be applied
280 * BLK_DONE - block doesn't need replaying
281 * BLK_RESTORED - block was restored from a full-page image included in
283 * BLK_NOTFOUND - block was not found (because it was truncated away by
284 * an operation later in the WAL stream)
286 * On return, the buffer is locked in exclusive-mode, and returned in *buf.
287 * Note that the buffer is locked and returned even if it doesn't need
288 * replaying. (Getting the buffer lock is not really necessary during
289 * single-process crash recovery, but some subroutines such as MarkBufferDirty
290 * will complain if we don't have the lock. In hot standby mode it's
291 * definitely necessary.)
293 * Note: when a backup block is available in XLOG with the BKPIMAGE_APPLY flag
294 * set, we restore it, even if the page in the database appears newer. This
295 * is to protect ourselves against database pages that were partially or
296 * incorrectly written during a crash. We assume that the XLOG data must be
297 * good because it has passed a CRC check, while the database page might not
298 * be. This will force us to replay all subsequent modifications of the page
299 * that appear in XLOG, rather than possibly ignoring them as already
300 * applied, but that's not a huge drawback.
311 * Pin and lock a buffer referenced by a WAL record, for the purpose of
312 * re-initializing it.
325 * XLogReadBufferForRedoExtended
326 * Like XLogReadBufferForRedo, but with extra options.
328 * In RBM_ZERO_* modes, if the page doesn't exist, the relation is extended
329 * with all-zeroes pages up to the referenced block number. In
330 * RBM_ZERO_AND_LOCK and RBM_ZERO_AND_CLEANUP_LOCK modes, the return value
331 * is always BLK_NEEDS_REDO.
333 * (The RBM_ZERO_AND_CLEANUP_LOCK mode is redundant with the get_cleanup_lock
334 * parameter. Do not use an inconsistent combination!)
336 * If 'get_cleanup_lock' is true, a "cleanup lock" is acquired on the buffer
337 * using LockBufferForCleanup(), instead of a regular exclusive lock.
357 /* Caller specified a bogus block_id */
358 elog(
PANIC,
"failed to locate backup block with ID %d in WAL record",
363 * Make sure that if the block is marked with WILL_INIT, the caller is
364 * going to initialize it. And vice versa.
368 if (willinit && !zeromode)
369 elog(
PANIC,
"block with WILL_INIT flag in WAL record must be zeroed by redo routine");
370 if (!willinit && zeromode)
371 elog(
PANIC,
"block to be initialized in redo routine must be marked with WILL_INIT flag in the WAL record");
373 /* If it has a full-page image and it should be restored, do it. */
383 (
errcode(ERRCODE_INTERNAL_ERROR),
387 * The page may be uninitialized. If so, we can't set the LSN because
388 * that would corrupt the page.
398 * At the end of crash recovery the init forks of unlogged relations
399 * are copied, without going through shared buffers. So we need to
400 * force the on-disk state of init forks to always be in sync with the
401 * state in shared buffers.
415 if (get_cleanup_lock)
431 * XLogReadBufferExtended
432 * Read a page during XLOG replay
434 * This is functionally comparable to ReadBufferExtended. There's some
435 * differences in the behavior wrt. the "mode" argument:
437 * In RBM_NORMAL mode, if the page doesn't exist, or contains all-zeroes, we
438 * return InvalidBuffer. In this case the caller should silently skip the
439 * update on this page. (In this situation, we expect that the page was later
440 * dropped or truncated. If we don't see evidence of that later in the WAL
441 * sequence, we'll complain at the end of WAL replay.)
443 * In RBM_ZERO_* modes, if the page doesn't exist, the relation is extended
444 * with all-zeroes pages up to the given block number.
446 * In RBM_NORMAL_NO_LOG mode, we return InvalidBuffer if the page doesn't
447 * exist, and we don't check for all-zeroes. Thus, no log entry is made
448 * to imply that the page should be dropped or truncated later.
450 * Optionally, recent_buffer can be used to provide a hint about the location
451 * of the page in the buffer pool; it does not have to be correct, but avoids
452 * a buffer mapping table probe if it is.
454 * NB: A redo function should normally not call this directly. To get a page
455 * to modify, use XLogReadBufferForRedoExtended instead. It is important that
456 * all pages modified by a WAL record are registered in the WAL records, or
457 * they will be invisible to tools that need to know which pages are modified.
470 /* Do we have a clue where the buffer might be already? */
475 buffer = recent_buffer;
476 goto recent_buffer_fast_path;
479 /* Open the relation at smgr level */
483 * Create the target file if it doesn't already exist. This lets us cope
484 * if the replay sequence contains writes to a relation that is later
485 * deleted. (The original coding of this routine would instead suppress
486 * the writes, but that seems like it risks losing valuable data if the
487 * filesystem loses an inode during a crash. Better to write the data
488 * until we are actually told to delete the file.)
494 if (blkno < lastblock)
496 /* page exists in file */
502 /* hm, page doesn't exist in file */
510 /* OK to extend the file */
511 /* we do this in recovery only - no rel-extension lock needed */
522recent_buffer_fast_path:
525 /* check that page has been initialized */
529 * We assume that PageIsNew is safe without a lock. During recovery,
530 * there should be no other backends that could modify the buffer at
545 * Struct actually returned by CreateFakeRelcacheEntry, though the declared
546 * return type is Relation.
557 * Create a fake relation cache entry for a physical relation
559 * It's often convenient to use the same functions in XLOG replay as in the
560 * main codepath, but those functions typically work with a relcache entry.
561 * We don't have a working relation cache during XLOG replay, but this
562 * function can be used to create a fake relcache entry instead. Only the
563 * fields related to physical storage, like rd_rel, are initialized, so the
564 * fake entry is only usable in low-level operations like ReadBuffer().
566 * This is also used for syncing WAL-skipped files.
568 * Caller must free the returned entry with FreeFakeRelcacheEntry().
576 /* Allocate the Relation struct and all related space in one block. */
584 * We will never be working with temp rels during recovery or while
585 * syncing WAL-skipped files.
589 /* It must be a permanent table here */
590 rel->
rd_rel->relpersistence = RELPERSISTENCE_PERMANENT;
592 /* We don't know the name of the relation; use relfilenumber instead */
596 * We set up the lockRelId in case anything tries to lock the dummy
597 * relation. Note that this is fairly bogus since relNumber may be
598 * different from the relation's OID. It shouldn't really matter though.
599 * In recovery, we are running by ourselves and can't have any lock
600 * conflicts. While syncing, we already hold AccessExclusiveLock.
606 * Set up a non-pinned SMgrRelation reference, so that we don't need to
607 * worry about unpinning it on error.
615 * Free a fake relation cache entry.
624 * Drop a relation during XLOG replay
626 * This is called when the relation is about to be deleted; we need to remove
627 * any open "invalid-page" records for the relation.
636 * Drop a whole database during XLOG replay
638 * As above, but for DROP DATABASE instead of dropping a single rel
644 * This is unnecessarily heavy-handed, as it will close SMgrRelation
645 * objects for other databases as well. DROP DATABASE occurs seldom enough
646 * that it's not worth introducing a variant of smgrdestroy for just this
655 * Truncate a relation during XLOG replay
657 * We need to clean up any open "invalid-page" records for the dropped pages.
667 * Determine which timeline to read an xlog page from and set the
668 * XLogReaderState's currTLI to that timeline ID.
670 * We care about timelines in xlogreader when we might be reading xlog
671 * generated prior to a promotion, either if we're currently a standby in
672 * recovery or if we're a promoted primary reading xlogs generated by the old
673 * primary before our promotion.
675 * wantPage must be set to the start address of the page to read and
676 * wantLength to the amount of the page that will be read, up to
677 * XLOG_BLCKSZ. If the amount to be read isn't known, pass XLOG_BLCKSZ.
679 * The currTLI argument should be the system-wide current timeline.
680 * Note that this may be different from state->currTLI, which is the timeline
681 * from which the caller is currently reading previous xlog records.
683 * We switch to an xlog segment from the new timeline eagerly when on a
684 * historical timeline, as soon as we reach the start of the xlog segment
685 * containing the timeline switch. The server copied the segment to the new
686 * timeline so all the data up to the switch point is the same, but there's no
687 * guarantee the old segment will still exist. It may have been deleted or
688 * renamed with a .partial suffix so we can't necessarily keep reading from
689 * the old TLI even though tliSwitchPoint says it's OK.
691 * We can't just check the timeline when we read a page on a different segment
692 * to the last page. We could've received a timeline switch from a cascading
693 * upstream, so the current segment ends abruptly (possibly getting renamed to
694 * .partial) and we have to switch to a new one. Even in the middle of reading
695 * a page we could have to dump the cached page and switch to a new TLI.
697 * Because of this, callers MAY NOT assume that currTLI is the timeline that
698 * will be in a page's xlp_tli; the page may begin on an older timeline or we
699 * might be reading from historical timeline data on a segment that's been
700 * copied to a new timeline.
702 * The caller must also make sure it doesn't read past the current replay
703 * position (using GetXLogReplayRecPtr) if executing in recovery, so it
704 * doesn't fail to notice that the current timeline became historical.
714 Assert(wantLength <= XLOG_BLCKSZ);
719 * If the desired page is currently read in and valid, we have nothing to
722 * The caller should've ensured that it didn't previously advance readOff
723 * past the valid limit of this timeline, so it doesn't matter if the
724 * current TLI has since become historical.
726 if (lastReadPage == wantPage &&
727 state->readLen != 0 &&
728 lastReadPage +
state->readLen >= wantPage +
Min(wantLength, XLOG_BLCKSZ - 1))
732 * If we're reading from the current timeline, it hasn't become historical
733 * and the page we're reading is after the last page read, we can again
734 * just carry on. (Seeking backwards requires a check to make sure the
735 * older page isn't on a prior timeline).
737 * currTLI might've become historical since the caller obtained the value,
738 * but the caller is required not to read past the flush limit it saw at
739 * the time it looked up the timeline. There's nothing we can do about it
740 * if StartupXLOG() renames it to .partial concurrently.
742 if (
state->currTLI == currTLI && wantPage >= lastReadPage)
749 * If we're just reading pages from a previously validated historical
750 * timeline and the timeline we're reading from is valid until the end of
751 * the current segment we can just keep reading.
754 state->currTLI != currTLI &&
755 state->currTLI != 0 &&
756 ((wantPage + wantLength) /
state->segcxt.ws_segsize) <
757 (
state->currTLIValidUntil /
state->segcxt.ws_segsize))
761 * If we reach this point we're either looking up a page for random
762 * access, the current timeline just became historical, or we're reading
763 * from a new segment containing a timeline switch. In all cases we need
764 * to determine the newest timeline on the segment.
766 * If it's the current timeline we can just keep reading from here unless
767 * we detect a timeline switch that makes the current timeline historical.
768 * If it's a historical timeline we can read all the segment on the newest
769 * timeline because it contains all the old timelines' data too. So only
770 * one switch check is required.
774 * We need to re-read the timeline history in case it's been changed
775 * by a promotion or replay from a cascaded replica.
780 endOfSegment = ((wantPage /
state->segcxt.ws_segsize) + 1) *
781 state->segcxt.ws_segsize - 1;
783 endOfSegment /
state->segcxt.ws_segsize);
786 * Find the timeline of the last LSN on the segment containing
794 wantPage + wantLength < state->currTLIValidUntil);
798 elog(
DEBUG3,
"switched to timeline %u valid until %X/%08X",
804/* XLogReaderRoutine->segment_open callback for local pg_wal files */
814 if (
state->seg.ws_file >= 0)
820 errmsg(
"requested WAL segment %s has already been removed",
825 errmsg(
"could not open file \"%s\": %m",
829/* stock XLogReaderRoutine->segment_close callback */
834 /* need to check errno? */
835 state->seg.ws_file = -1;
839 * XLogReaderRoutine->page_read callback for reading local xlog files
841 * Public because it would likely be very helpful for someone writing another
842 * output method outside walsender, e.g. in a bgworker.
846 int reqLen,
XLogRecPtr targetRecPtr,
char *cur_page)
849 targetRecPtr, cur_page,
true);
853 * Same as read_local_xlog_page except that it doesn't wait for future WAL
862 targetRecPtr, cur_page,
false);
866 * Implementation of read_local_xlog_page and its no wait version.
871 char *cur_page,
bool wait_for_wal)
880 loc = targetPagePtr + reqLen;
883 * Loop waiting for xlog to be available if necessary
885 * TODO: The walsender has its own version of this function, which uses a
886 * condition variable to wake up whenever WAL is flushed. We could use the
887 * same infrastructure here, instead of the check/sleep/repeat style of
893 * Determine the limit of xlog we can currently read to, and what the
894 * most recent timeline is.
903 * Check which timeline to get the record from.
905 * We have to do it each time through the loop because if we're in
906 * recovery as a cascading standby, the current timeline might've
907 * become historical. We can't rely on RecoveryInProgress() because in
908 * a standby configuration like
912 * if we're a logical decoding session on C, and B gets promoted, our
913 * timeline will change while we remain in recovery.
915 * We can't just keep reading from the old timeline as the last WAL
916 * archive in the timeline will get renamed to .partial by
919 * If that happens after our caller determined the TLI but before we
920 * actually read the xlog page, we might still try to read from the
921 * old (now renamed) segment and fail. There's not much we can do
922 * about this, but it can only happen when we're a leaf of a cascading
923 * standby whose primary gets promoted while we're decoding, so a
924 * one-off ERROR isn't too bad.
928 if (
state->currTLI == currTLI)
931 if (loc <= read_upto)
934 /* If asked, let's not wait for future WAL. */
940 * Inform the caller of read_local_xlog_page_no_wait that the
941 * end of WAL has been reached.
955 * We're on a historical timeline, so limit reading to the switch
956 * point where we moved to the next timeline.
958 * We don't need to GetFlushRecPtr or GetXLogReplayRecPtr. We know
959 * about the new timeline, so we must've received past the end of
962 read_upto =
state->currTLIValidUntil;
965 * Setting tli to our wanted record's TLI is slightly wrong; the
966 * page might begin on an older timeline if it contains a timeline
967 * switch, since its xlog segment will have been copied from the
968 * prior timeline. This is pretty harmless though, as nothing
969 * cares so long as the timeline doesn't go backwards. We should
970 * read the page header instead; FIXME someday.
972 tli =
state->currTLI;
974 /* No need to wait on a historical timeline */
979 if (targetPagePtr + XLOG_BLCKSZ <= read_upto)
982 * more than one block available; read only that block, have caller
983 * come back if they need more.
987 else if (targetPagePtr + reqLen > read_upto)
989 /* not enough data there */
994 /* enough bytes available to satisfy the request */
995 count = read_upto - targetPagePtr;
998 if (!
WALRead(
state, cur_page, targetPagePtr, count, tli,
1002 /* number of valid bytes in the buffer */
1007 * Backend-specific convenience code to handle read errors encountered by
1023 errmsg(
"could not read from WAL segment %s, offset %d: %m",
1030 errmsg(
"could not read from WAL segment %s, offset %d: read %d of %d",
List * readTimeLineHistory(TimeLineID targetTLI)
TimeLineID tliOfPointInHistory(XLogRecPtr ptr, List *history)
XLogRecPtr tliSwitchPoint(TimeLineID tli, List *history, TimeLineID *nextTLI)
Buffer ExtendBufferedRelTo(BufferManagerRelation bmr, ForkNumber fork, BufferAccessStrategy strategy, uint32 flags, BlockNumber extend_to, ReadBufferMode mode)
void ReleaseBuffer(Buffer buffer)
void MarkBufferDirty(Buffer buffer)
void LockBufferForCleanup(Buffer buffer)
void LockBuffer(Buffer buffer, int mode)
Buffer ReadBufferWithoutRelcache(RelFileLocator rlocator, ForkNumber forkNum, BlockNumber blockNum, ReadBufferMode mode, BufferAccessStrategy strategy, bool permanent)
bool ReadRecentBuffer(RelFileLocator rlocator, ForkNumber forkNum, BlockNumber blockNum, Buffer recent_buffer)
void FlushOneBuffer(Buffer buffer)
static Page BufferGetPage(Buffer buffer)
#define BMR_SMGR(p_smgr, p_relpersistence)
#define BUFFER_LOCK_EXCLUSIVE
@ RBM_ZERO_AND_CLEANUP_LOCK
static bool BufferIsValid(Buffer bufnum)
static bool PageIsNew(const PageData *page)
static void PageSetLSN(Page page, XLogRecPtr lsn)
static XLogRecPtr PageGetLSN(const PageData *page)
void * hash_search(HTAB *hashp, const void *keyPtr, HASHACTION action, bool *foundPtr)
HTAB * hash_create(const char *tabname, int64 nelem, const HASHCTL *info, int flags)
void hash_destroy(HTAB *hashp)
void * hash_seq_search(HASH_SEQ_STATUS *status)
int64 hash_get_num_entries(HTAB *hashp)
void hash_seq_init(HASH_SEQ_STATUS *status, HTAB *hashp)
int errmsg_internal(const char *fmt,...)
int errcode_for_file_access(void)
bool message_level_is_interesting(int elevel)
int errcode(int sqlerrcode)
int errmsg(const char *fmt,...)
#define ereport(elevel,...)
int BasicOpenFile(const char *fileName, int fileFlags)
struct RelationData * Relation
Assert(PointerIsAligned(start, uint64))
void list_free_deep(List *list)
void pfree(void *pointer)
void * palloc0(Size size)
#define CHECK_FOR_INTERRUPTS()
#define ERRCODE_DATA_CORRUPTED
static PgChecksumMode mode
#define INVALID_PROC_NUMBER
#define RelationGetRelationName(relation)
#define RelFileLocatorEquals(locator1, locator2)
#define relpathperm(rlocator, forknum)
void pg_usleep(long microsec)
BlockNumber smgrnblocks(SMgrRelation reln, ForkNumber forknum)
SMgrRelation smgropen(RelFileLocator rlocator, ProcNumber backend)
void smgrdestroyall(void)
void smgrcreate(SMgrRelation reln, ForkNumber forknum, bool isRedo)
char str[REL_PATH_STR_MAXLEN+1]
RelFileLocator rd_locator
bool RecoveryInProgress(void)
XLogRecPtr GetFlushRecPtr(TimeLineID *insertTLI)
static void XLogFilePath(char *path, TimeLineID tli, XLogSegNo logSegNo, int wal_segsz_bytes)
static void XLogFileName(char *fname, TimeLineID tli, XLogSegNo logSegNo, int wal_segsz_bytes)
#define LSN_FORMAT_ARGS(lsn)
#define InvalidXLogRecPtr
bool XLogRecGetBlockTagExtended(XLogReaderState *record, uint8 block_id, RelFileLocator *rlocator, ForkNumber *forknum, BlockNumber *blknum, Buffer *prefetch_buffer)
bool WALRead(XLogReaderState *state, char *buf, XLogRecPtr startptr, Size count, TimeLineID tli, WALReadError *errinfo)
bool RestoreBlockImage(XLogReaderState *record, uint8 block_id, char *page)
#define XLogRecBlockImageApply(decoder, block_id)
#define XLogRecGetBlock(decoder, i)
#define XLogRecHasBlockImage(decoder, block_id)
#define BKPBLOCK_WILL_INIT
XLogRecPtr GetXLogReplayRecPtr(TimeLineID *replayTLI)
void wal_segment_close(XLogReaderState *state)
void FreeFakeRelcacheEntry(Relation fakerel)
void wal_segment_open(XLogReaderState *state, XLogSegNo nextSegNo, TimeLineID *tli_p)
bool ignore_invalid_pages
static void report_invalid_page(int elevel, RelFileLocator locator, ForkNumber forkno, BlockNumber blkno, bool present)
void XLogReadDetermineTimeline(XLogReaderState *state, XLogRecPtr wantPage, uint32 wantLength, TimeLineID currTLI)
FakeRelCacheEntryData * FakeRelCacheEntry
bool XLogHaveInvalidPages(void)
XLogRedoAction XLogReadBufferForRedo(XLogReaderState *record, uint8 block_id, Buffer *buf)
Buffer XLogInitBufferForRedo(XLogReaderState *record, uint8 block_id)
Buffer XLogReadBufferExtended(RelFileLocator rlocator, ForkNumber forknum, BlockNumber blkno, ReadBufferMode mode, Buffer recent_buffer)
void XLogTruncateRelation(RelFileLocator rlocator, ForkNumber forkNum, BlockNumber nblocks)
struct xl_invalid_page xl_invalid_page
Relation CreateFakeRelcacheEntry(RelFileLocator rlocator)
HotStandbyState standbyState
struct xl_invalid_page_key xl_invalid_page_key
int read_local_xlog_page(XLogReaderState *state, XLogRecPtr targetPagePtr, int reqLen, XLogRecPtr targetRecPtr, char *cur_page)
void XLogCheckInvalidPages(void)
static void forget_invalid_pages(RelFileLocator locator, ForkNumber forkno, BlockNumber minblkno)
void WALReadRaiseError(WALReadError *errinfo)
static void log_invalid_page(RelFileLocator locator, ForkNumber forkno, BlockNumber blkno, bool present)
static int read_local_xlog_page_guts(XLogReaderState *state, XLogRecPtr targetPagePtr, int reqLen, XLogRecPtr targetRecPtr, char *cur_page, bool wait_for_wal)
void XLogDropRelation(RelFileLocator rlocator, ForkNumber forknum)
int read_local_xlog_page_no_wait(XLogReaderState *state, XLogRecPtr targetPagePtr, int reqLen, XLogRecPtr targetRecPtr, char *cur_page)
static HTAB * invalid_page_tab
XLogRedoAction XLogReadBufferForRedoExtended(XLogReaderState *record, uint8 block_id, ReadBufferMode mode, bool get_cleanup_lock, Buffer *buf)
static void forget_invalid_pages_db(Oid dbid)
void XLogDropDatabase(Oid dbid)