1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-31 22:04:40 +03:00

amcheck: Refactoring the storage of the last visible entry

This commit introduces a new data structure BtreeLastVisibleEntry comprising
information about the last visible heap entry with the current value of key.
Usage of this data structure allows us to avoid passing all this information
as individual function arguments.

Reported-by: Alexander Korotkov
Discussion: https://www.postgresql.org/message-id/CAPpHfdsVbB9ToriaB1UHuOKwjKxiZmTFQcEF%3DjuzzC_nby31uA%40mail.gmail.com
Author: Pavel Borisov, Alexander Korotkov
This commit is contained in:
Alexander Korotkov
2024-05-23 02:11:14 +03:00
parent bac44bc29a
commit 532d94fec3
2 changed files with 60 additions and 58 deletions

View File

@ -145,6 +145,19 @@ typedef struct BtreeLevel
bool istruerootlevel; bool istruerootlevel;
} BtreeLevel; } BtreeLevel;
/*
* Information about the last visible entry with current B-tree key. Used
* for validation of the unique constraint.
*/
typedef struct BtreeLastVisibleEntry
{
BlockNumber blkno; /* Index block */
OffsetNumber offset; /* Offset on index block */
int postingIndex; /* Number in the posting list (-1 for
* non-deduplicated tuples) */
ItemPointer tid; /* Heap tid */
} BtreeLastVisibleEntry;
PG_FUNCTION_INFO_V1(bt_index_check); PG_FUNCTION_INFO_V1(bt_index_check);
PG_FUNCTION_INFO_V1(bt_index_parent_check); PG_FUNCTION_INFO_V1(bt_index_parent_check);
@ -165,17 +178,14 @@ static void bt_recheck_sibling_links(BtreeCheckState *state,
BlockNumber btpo_prev_from_target, BlockNumber btpo_prev_from_target,
BlockNumber leftcurrent); BlockNumber leftcurrent);
static bool heap_entry_is_visible(BtreeCheckState *state, ItemPointer tid); static bool heap_entry_is_visible(BtreeCheckState *state, ItemPointer tid);
static void bt_report_duplicate(BtreeCheckState *state, ItemPointer tid, static void bt_report_duplicate(BtreeCheckState *state,
BlockNumber block, OffsetNumber offset, BtreeLastVisibleEntry *lVis,
int posting, ItemPointer nexttid, ItemPointer nexttid,
BlockNumber nblock, OffsetNumber noffset, BlockNumber nblock, OffsetNumber noffset,
int nposting); int nposting);
static void bt_entry_unique_check(BtreeCheckState *state, IndexTuple itup, static void bt_entry_unique_check(BtreeCheckState *state, IndexTuple itup,
BlockNumber targetblock, BlockNumber targetblock, OffsetNumber offset,
OffsetNumber offset, int *lVis_i, BtreeLastVisibleEntry *lVis);
ItemPointer *lVis_tid,
OffsetNumber *lVis_offset,
BlockNumber *lVis_block);
static void bt_target_page_check(BtreeCheckState *state); static void bt_target_page_check(BtreeCheckState *state);
static BTScanInsert bt_right_page_check_scankey(BtreeCheckState *state, static BTScanInsert bt_right_page_check_scankey(BtreeCheckState *state,
OffsetNumber *rightfirstoffset); OffsetNumber *rightfirstoffset);
@ -997,8 +1007,7 @@ heap_entry_is_visible(BtreeCheckState *state, ItemPointer tid)
*/ */
static void static void
bt_report_duplicate(BtreeCheckState *state, bt_report_duplicate(BtreeCheckState *state,
ItemPointer tid, BlockNumber block, OffsetNumber offset, BtreeLastVisibleEntry *lVis,
int posting,
ItemPointer nexttid, BlockNumber nblock, OffsetNumber noffset, ItemPointer nexttid, BlockNumber nblock, OffsetNumber noffset,
int nposting) int nposting)
{ {
@ -1010,18 +1019,18 @@ bt_report_duplicate(BtreeCheckState *state,
*pnposting = ""; *pnposting = "";
htid = psprintf("tid=(%u,%u)", htid = psprintf("tid=(%u,%u)",
ItemPointerGetBlockNumberNoCheck(tid), ItemPointerGetBlockNumberNoCheck(lVis->tid),
ItemPointerGetOffsetNumberNoCheck(tid)); ItemPointerGetOffsetNumberNoCheck(lVis->tid));
nhtid = psprintf("tid=(%u,%u)", nhtid = psprintf("tid=(%u,%u)",
ItemPointerGetBlockNumberNoCheck(nexttid), ItemPointerGetBlockNumberNoCheck(nexttid),
ItemPointerGetOffsetNumberNoCheck(nexttid)); ItemPointerGetOffsetNumberNoCheck(nexttid));
itid = psprintf("tid=(%u,%u)", block, offset); itid = psprintf("tid=(%u,%u)", lVis->blkno, lVis->offset);
if (nblock != block || noffset != offset) if (nblock != lVis->blkno || noffset != lVis->offset)
nitid = psprintf(" tid=(%u,%u)", nblock, noffset); nitid = psprintf(" tid=(%u,%u)", nblock, noffset);
if (posting >= 0) if (lVis->postingIndex >= 0)
pposting = psprintf(" posting %u", posting); pposting = psprintf(" posting %u", lVis->postingIndex);
if (nposting >= 0) if (nposting >= 0)
pnposting = psprintf(" posting %u", nposting); pnposting = psprintf(" posting %u", nposting);
@ -1038,9 +1047,8 @@ bt_report_duplicate(BtreeCheckState *state,
/* Check if current nbtree leaf entry complies with UNIQUE constraint */ /* Check if current nbtree leaf entry complies with UNIQUE constraint */
static void static void
bt_entry_unique_check(BtreeCheckState *state, IndexTuple itup, bt_entry_unique_check(BtreeCheckState *state, IndexTuple itup,
BlockNumber targetblock, OffsetNumber offset, int *lVis_i, BlockNumber targetblock, OffsetNumber offset,
ItemPointer *lVis_tid, OffsetNumber *lVis_offset, BtreeLastVisibleEntry *lVis)
BlockNumber *lVis_block)
{ {
ItemPointer tid; ItemPointer tid;
bool has_visible_entry = false; bool has_visible_entry = false;
@ -1049,7 +1057,7 @@ bt_entry_unique_check(BtreeCheckState *state, IndexTuple itup,
/* /*
* Current tuple has posting list. Report duplicate if TID of any posting * Current tuple has posting list. Report duplicate if TID of any posting
* list entry is visible and lVis_tid is valid. * list entry is visible and lVis->tid is valid.
*/ */
if (BTreeTupleIsPosting(itup)) if (BTreeTupleIsPosting(itup))
{ {
@ -1059,11 +1067,10 @@ bt_entry_unique_check(BtreeCheckState *state, IndexTuple itup,
if (heap_entry_is_visible(state, tid)) if (heap_entry_is_visible(state, tid))
{ {
has_visible_entry = true; has_visible_entry = true;
if (ItemPointerIsValid(*lVis_tid)) if (ItemPointerIsValid(lVis->tid))
{ {
bt_report_duplicate(state, bt_report_duplicate(state,
*lVis_tid, *lVis_block, lVis,
*lVis_offset, *lVis_i,
tid, targetblock, tid, targetblock,
offset, i); offset, i);
} }
@ -1073,13 +1080,13 @@ bt_entry_unique_check(BtreeCheckState *state, IndexTuple itup,
* between the posting list entries of the first tuple on the * between the posting list entries of the first tuple on the
* page after cross-page check. * page after cross-page check.
*/ */
if (*lVis_block != targetblock && ItemPointerIsValid(*lVis_tid)) if (lVis->blkno != targetblock && ItemPointerIsValid(lVis->tid))
return; return;
*lVis_i = i; lVis->blkno = targetblock;
*lVis_tid = tid; lVis->offset = offset;
*lVis_offset = offset; lVis->postingIndex = i;
*lVis_block = targetblock; lVis->tid = tid;
} }
} }
} }
@ -1087,7 +1094,7 @@ bt_entry_unique_check(BtreeCheckState *state, IndexTuple itup,
/* /*
* Current tuple has no posting list. If TID is visible save info about it * Current tuple has no posting list. If TID is visible save info about it
* for the next comparisons in the loop in bt_target_page_check(). Report * for the next comparisons in the loop in bt_target_page_check(). Report
* duplicate if lVis_tid is already valid. * duplicate if lVis->tid is already valid.
*/ */
else else
{ {
@ -1095,37 +1102,38 @@ bt_entry_unique_check(BtreeCheckState *state, IndexTuple itup,
if (heap_entry_is_visible(state, tid)) if (heap_entry_is_visible(state, tid))
{ {
has_visible_entry = true; has_visible_entry = true;
if (ItemPointerIsValid(*lVis_tid)) if (ItemPointerIsValid(lVis->tid))
{ {
bt_report_duplicate(state, bt_report_duplicate(state,
*lVis_tid, *lVis_block, lVis,
*lVis_offset, *lVis_i,
tid, targetblock, tid, targetblock,
offset, -1); offset, -1);
} }
*lVis_i = -1;
*lVis_tid = tid; lVis->blkno = targetblock;
*lVis_offset = offset; lVis->offset = offset;
*lVis_block = targetblock; lVis->tid = tid;
lVis->postingIndex = -1;
} }
} }
if (!has_visible_entry && *lVis_block != InvalidBlockNumber && if (!has_visible_entry &&
*lVis_block != targetblock) lVis->blkno != InvalidBlockNumber &&
lVis->blkno != targetblock)
{ {
char *posting = ""; char *posting = "";
if (*lVis_i >= 0) if (lVis->postingIndex >= 0)
posting = psprintf(" posting %u", *lVis_i); posting = psprintf(" posting %u", lVis->postingIndex);
ereport(DEBUG1, ereport(DEBUG1,
(errcode(ERRCODE_NO_DATA), (errcode(ERRCODE_NO_DATA),
errmsg("index uniqueness can not be checked for index tid=(%u,%u) in index \"%s\"", errmsg("index uniqueness can not be checked for index tid=(%u,%u) in index \"%s\"",
targetblock, offset, targetblock, offset,
RelationGetRelationName(state->rel)), RelationGetRelationName(state->rel)),
errdetail("It doesn't have visible heap tids and key is equal to the tid=(%u,%u)%s (points to heap tid=(%u,%u)).", errdetail("It doesn't have visible heap tids and key is equal to the tid=(%u,%u)%s (points to heap tid=(%u,%u)).",
*lVis_block, *lVis_offset, posting, lVis->blkno, lVis->offset, posting,
ItemPointerGetBlockNumberNoCheck(*lVis_tid), ItemPointerGetBlockNumberNoCheck(lVis->tid),
ItemPointerGetOffsetNumberNoCheck(*lVis_tid)), ItemPointerGetOffsetNumberNoCheck(lVis->tid)),
errhint("VACUUM the table and repeat the check."))); errhint("VACUUM the table and repeat the check.")));
} }
} }
@ -1372,12 +1380,8 @@ bt_target_page_check(BtreeCheckState *state)
OffsetNumber max; OffsetNumber max;
BTPageOpaque topaque; BTPageOpaque topaque;
/* last visible entry info for checking indexes with unique constraint */ /* Last visible entry info for checking indexes with unique constraint */
int lVis_i = -1; /* the position of last visible item for BtreeLastVisibleEntry lVis = {InvalidBlockNumber, InvalidOffsetNumber, -1, NULL};
* posting tuple. for non-posting tuple (-1) */
ItemPointer lVis_tid = NULL;
BlockNumber lVis_block = InvalidBlockNumber;
OffsetNumber lVis_offset = InvalidOffsetNumber;
topaque = BTPageGetOpaque(state->target); topaque = BTPageGetOpaque(state->target);
max = PageGetMaxOffsetNumber(state->target); max = PageGetMaxOffsetNumber(state->target);
@ -1776,8 +1780,7 @@ bt_target_page_check(BtreeCheckState *state)
if (state->checkunique && state->indexinfo->ii_Unique && if (state->checkunique && state->indexinfo->ii_Unique &&
P_ISLEAF(topaque) && !skey->anynullkeys) P_ISLEAF(topaque) && !skey->anynullkeys)
bt_entry_unique_check(state, itup, state->targetblock, offset, bt_entry_unique_check(state, itup, state->targetblock, offset,
&lVis_i, &lVis_tid, &lVis_offset, &lVis);
&lVis_block);
if (state->checkunique && state->indexinfo->ii_Unique && if (state->checkunique && state->indexinfo->ii_Unique &&
P_ISLEAF(topaque) && OffsetNumberNext(offset) <= max) P_ISLEAF(topaque) && OffsetNumberNext(offset) <= max)
@ -1800,10 +1803,10 @@ bt_target_page_check(BtreeCheckState *state)
if (_bt_compare(state->rel, skey, state->target, if (_bt_compare(state->rel, skey, state->target,
OffsetNumberNext(offset)) != 0 || skey->anynullkeys) OffsetNumberNext(offset)) != 0 || skey->anynullkeys)
{ {
lVis_i = -1; lVis.blkno = InvalidBlockNumber;
lVis_tid = NULL; lVis.offset = InvalidOffsetNumber;
lVis_block = InvalidBlockNumber; lVis.postingIndex = -1;
lVis_offset = InvalidOffsetNumber; lVis.tid = NULL;
} }
skey->scantid = scantid; /* Restore saved scan key state */ skey->scantid = scantid; /* Restore saved scan key state */
} }
@ -1902,9 +1905,7 @@ bt_target_page_check(BtreeCheckState *state)
rightfirstoffset); rightfirstoffset);
itup = (IndexTuple) PageGetItem(state->target, itemid); itup = (IndexTuple) PageGetItem(state->target, itemid);
bt_entry_unique_check(state, itup, rightblock_number, rightfirstoffset, bt_entry_unique_check(state, itup, rightblock_number, rightfirstoffset, &lVis);
&lVis_i, &lVis_tid, &lVis_offset,
&lVis_block);
} }
} }
} }

View File

@ -322,6 +322,7 @@ BrinStatsData
BrinTuple BrinTuple
BrinValues BrinValues
BtreeCheckState BtreeCheckState
BtreeLastVisibleEntry
BtreeLevel BtreeLevel
Bucket Bucket
BufFile BufFile