1/*-------------------------------------------------------------------------
4 * WAL replay logic for SP-GiST
7 * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
8 * Portions Copyright (c) 1994, Regents of the University of California
11 * src/backend/access/spgist/spgxlog.c
13 *-------------------------------------------------------------------------
29 * Prepare a dummy SpGistState, with just the minimum info needed for replay.
31 * At present, all we need is enough info to support spgFormDeadTuple(),
32 * plus the isBuild flag.
45 * Add a leaf tuple, or replace an existing placeholder tuple. This is used
46 * to replay SpGistPageAddNewItem() operations. If the offset points at an
47 * existing tuple, it had better be a placeholder tuple.
58 elog(
ERROR,
"SPGiST tuple to be replaced is not a placeholder");
68 if (
PageAddItem(page, tuple, size, offset,
false,
false) != offset)
69 elog(
ERROR,
"failed to add item of size %u to SPGiST index page",
87 /* the leaf tuple is unaligned, so make a copy to access its header */
91 * In normal operation we would have both current and parent pages locked
92 * simultaneously; but in WAL replay it should be safe to update the leaf
93 * page before updating the parent.
109 /* insert new tuple */
112 /* normal cases, tuple was added by SpGistPageAddNewItem */
116 /* update head tuple's chain link if needed */
129 /* replacing a DEAD tuple */
132 (
Item) leafTuple, leafTupleHdr.
size,
134 elog(
ERROR,
"failed to add item of size %u to SPGiST index page",
144 /* update parent downlink if necessary */
197 /* now ptr points to the list of leaf tuples */
200 * In normal operation we would have all three pages (source, dest, and
201 * parent) locked simultaneously; but in WAL replay it should be safe to
202 * update them one at a time, as long as we do it in the right order.
205 /* Insert tuples on the dest page (do first, so redirect is valid) */
222 for (
i = 0;
i < nInsert;
i++)
228 * the tuples are not aligned, so must copy to access the size
232 memcpy(&leafTupleHdr, leafTuple,
236 leafTupleHdr.
size, toInsert[
i]);
237 ptr += leafTupleHdr.
size;
246 /* Delete tuples from the source page, inserting a redirection pointer */
255 toInsert[nInsert - 1]);
263 /* And update the parent downlink */
274 blknoDst, toInsert[nInsert - 1]);
298 /* the tuple is unaligned, so make a copy to access its header */
305 /* update in place */
314 false,
false) != xldata->
offnum)
315 elog(
ERROR,
"failed to add item of size %u to SPGiST index page",
333 * In normal operation we would have all three pages (source, dest,
334 * and parent) locked simultaneously; but in WAL replay it should be
335 * safe to update them one at a time, as long as we do it in the right
336 * order. We must insert the new tuple before replacing the old tuple
337 * with the redirect tuple.
340 /* Install new tuple first so redirect is valid */
343 /* AddNode is not used for nulls pages */
358 * If parent is in this same page, update it now.
376 /* Delete old tuple, replacing it with redirect or placeholder tuple */
395 false,
false) != xldata->
offnum)
396 elog(
ERROR,
"failed to add item of size %u to SPGiST index page",
405 * If parent is in this same page, update it now.
424 * Update parent downlink (if we didn't do it as part of the source or
425 * destination page update already).
466 /* the prefix tuple is unaligned, so make a copy to access its header */
468 ptr += prefixTupleHdr.
size;
470 /* postfix tuple is also unaligned */
474 * In normal operation we would have both pages locked simultaneously; but
475 * in WAL replay it should be safe to update them one at a time, as long
476 * as we do it in the right order.
479 /* insert postfix tuple first to avoid dangling link */
485 /* SplitTuple is not used for nulls pages */
505 /* now handle the original page */
513 elog(
ERROR,
"failed to add item of size %u to SPGiST index page",
514 prefixTupleHdr.
size);
518 postfixTupleHdr.
size,
539 uint8 *leafPageSelect;
559 leafPageSelect = (
uint8 *) ptr;
563 /* the inner tuple is unaligned, so make a copy to access its header */
565 ptr += innerTupleHdr.
size;
567 /* now ptr points to the list of leaf tuples */
571 /* when splitting root, we touch it only in the guise of new inner */
577 /* just re-init the source page */
583 /* don't update LSN etc till we're done with it */
588 * Delete the specified tuples from source page. (In case we're in
589 * Hot Standby, we need to hold lock on the page till we're done
590 * inserting leaf tuples and the new inner tuple, else the added
591 * redirect tuple will be a dangling link.)
599 * We have it a bit easier here than in doPickSplit(), because we
600 * know the inner tuple's location already, so we can inject the
601 * correct redirection tuple now.
618 /* don't update LSN etc till we're done with it */
622 /* try to access dest page if any */
630 /* just re-init the dest page */
636 /* don't update LSN etc till we're done with it */
641 * We could probably release the page lock immediately in the
642 * full-page-image case, but for safety let's hold it till later.
647 destPage = NULL;
/* don't do any page updates */
650 /* restore leaf tuples to src and/or dest page */
656 /* the tuples are not aligned, so must copy to access the size field. */
659 ptr += leafTupleHdr.
size;
661 page = leafPageSelect[
i] ? destPage : srcPage;
663 continue;
/* no need to touch this page */
669 /* Now update src and dest page LSNs if needed */
675 if (destPage != NULL)
681 /* restore new inner tuple */
698 /* if inner is also parent, update link while we're here */
716 * Now we can release the leaf-page locks. It's okay to do this before
717 * updating the parent downlink.
724 /* update parent downlink, unless we did it above */
787 toDead, xldata->
nDead,
798 /* see comments in vacuumLeafPage() */
811 moveSrc, xldata->
nMove,
849 /* The tuple numbers are in order */
868 itemToPlaceholder = xldata->
offsets;
871 * If any redirection tuples are being removed, make sure there are no
872 * live Hot Standby transactions that might need to see them.
890 /* Convert redirect pointers to plain placeholders */
906 /* Remove placeholder tuples at end of page */
921 /* The array is sorted, so can use PageIndexMultiDelete */
968 elog(
PANIC,
"spg_redo: unknown op code %u", info);
979 "SP-GiST temporary context",
991 * Mask a SpGist page before performing consistency checks on it.
1004 * Mask the unused space, but only if the page's pd_lower appears to have
1005 * been set correctly.
#define InvalidBlockNumber
void mask_page_lsn_and_checksum(Page page)
void mask_unused_space(Page page)
void mask_page_hint_bits(Page page)
void UnlockReleaseBuffer(Buffer buffer)
void MarkBufferDirty(Buffer buffer)
static Page BufferGetPage(Buffer buffer)
static bool BufferIsValid(Buffer bufnum)
void PageIndexMultiDelete(Page page, OffsetNumber *itemnos, int nitems)
void PageIndexTupleDelete(Page page, OffsetNumber offnum)
PageHeaderData * PageHeader
static Item PageGetItem(const PageData *page, const ItemIdData *itemId)
#define SizeOfPageHeaderData
static ItemId PageGetItemId(Page page, OffsetNumber offsetNumber)
static void PageSetLSN(Page page, XLogRecPtr lsn)
#define PageAddItem(page, item, size, offsetNumber, overwrite, is_heap)
static OffsetNumber PageGetMaxOffsetNumber(const PageData *page)
Assert(PointerIsAligned(start, uint64))
static void ItemPointerSetInvalid(ItemPointerData *pointer)
void MemoryContextReset(MemoryContext context)
void pfree(void *pointer)
void * palloc0(Size size)
MemoryContext CurrentMemoryContext
void MemoryContextDelete(MemoryContext context)
#define AllocSetContextCreate
#define ALLOCSET_DEFAULT_SIZES
#define InvalidOffsetNumber
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
void spgPageIndexMultiDelete(SpGistState *state, Page page, OffsetNumber *itemnos, int nitems, int firststate, int reststate, BlockNumber blkno, OffsetNumber offnum)
void spgUpdateNodeLink(SpGistInnerTuple tup, int nodeN, BlockNumber blkno, OffsetNumber offset)
SpGistDeadTupleData * SpGistDeadTuple
SpGistInnerTupleData * SpGistInnerTuple
#define SGLT_GET_NEXTOFFSET(spgLeafTuple)
#define SPGIST_PLACEHOLDER
#define SGLT_SET_NEXTOFFSET(spgLeafTuple, offsetNumber)
struct SpGistLeafTupleData * SpGistLeafTuple
#define SpGistPageGetOpaque(page)
SpGistDeadTuple spgFormDeadTuple(SpGistState *state, int tupstate, BlockNumber blkno, OffsetNumber offnum)
void SpGistInitBuffer(Buffer b, uint16 f)
static void addOrReplaceTuple(Page page, Item tuple, int size, OffsetNumber offset)
void spg_redo(XLogReaderState *record)
static void spgRedoVacuumRoot(XLogReaderState *record)
static void spgRedoSplitTuple(XLogReaderState *record)
static void spgRedoVacuumRedirect(XLogReaderState *record)
void spg_xlog_cleanup(void)
void spg_mask(char *pagedata, BlockNumber blkno)
static void spgRedoMoveLeafs(XLogReaderState *record)
static void fillFakeState(SpGistState *state, spgxlogState stateSrc)
void spg_xlog_startup(void)
static void spgRedoAddNode(XLogReaderState *record)
static MemoryContext opCtx
static void spgRedoVacuumLeaf(XLogReaderState *record)
static void spgRedoPickSplit(XLogReaderState *record)
static void spgRedoAddLeaf(XLogReaderState *record)
struct spgxlogSplitTuple spgxlogSplitTuple
#define XLOG_SPGIST_SPLIT_TUPLE
#define SizeOfSpgxlogPickSplit
#define XLOG_SPGIST_VACUUM_ROOT
struct spgxlogAddLeaf spgxlogAddLeaf
#define SizeOfSpgxlogVacuumLeaf
#define SizeOfSpgxlogMoveLeafs
#define XLOG_SPGIST_VACUUM_LEAF
#define XLOG_SPGIST_ADD_NODE
#define XLOG_SPGIST_ADD_LEAF
struct spgxlogAddNode spgxlogAddNode
#define XLOG_SPGIST_MOVE_LEAFS
#define XLOG_SPGIST_PICKSPLIT
#define XLOG_SPGIST_VACUUM_REDIRECT
void ResolveRecoveryConflictWithSnapshot(TransactionId snapshotConflictHorizon, bool isCatalogRel, RelFileLocator locator)
OffsetNumber offnumHeadLeaf
OffsetNumber offnumParent
OffsetNumber offnumParent
OffsetNumber offnumParent
OffsetNumber offnumParent
OffsetNumber offnumPostfix
OffsetNumber offnumPrefix
TransactionId redirectXid
OffsetNumber firstPlaceholder
TransactionId snapshotConflictHorizon
OffsetNumber offsets[FLEXIBLE_ARRAY_MEMBER]
OffsetNumber offsets[FLEXIBLE_ARRAY_MEMBER]
void XLogRecGetBlockTag(XLogReaderState *record, uint8 block_id, RelFileLocator *rlocator, ForkNumber *forknum, BlockNumber *blknum)
#define XLogRecGetInfo(decoder)
#define XLogRecGetData(decoder)
#define XLogRecHasBlockRef(decoder, block_id)
XLogRedoAction XLogReadBufferForRedo(XLogReaderState *record, uint8 block_id, Buffer *buf)
Buffer XLogInitBufferForRedo(XLogReaderState *record, uint8 block_id)