Skip to content

Commit cdd6ab9

Browse files
committed
amcheck: Optimize speed of checking for unique constraint violation
Currently, when amcheck validates a unique constraint, it visits the heap for each index tuple. This commit implements skipping keys, which have only one non-dedeuplicated index tuple (quite common case for unique indexes). That gives substantial economy on index checking time. Reported-by: Noah Misch Discussion: https://postgr.es/m/20240325020323.fd.nmisch%40google.com Author: Alexander Korotkov, Pavel Borisov
1 parent b181062 commit cdd6ab9

File tree

1 file changed

+33
-3
lines changed

1 file changed

+33
-3
lines changed

contrib/amcheck/verify_nbtree.c

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1433,6 +1433,13 @@ bt_target_page_check(BtreeCheckState *state)
14331433
bool lowersizelimit;
14341434
ItemPointer scantid;
14351435

1436+
/*
1437+
* True if we already called bt_entry_unique_check() for the current
1438+
* item. This helps to avoid visiting the heap for keys, which are
1439+
* anyway presented only once and can't comprise a unique violation.
1440+
*/
1441+
bool unique_checked = false;
1442+
14361443
CHECK_FOR_INTERRUPTS();
14371444

14381445
itemid = PageGetItemIdCareful(state, state->targetblock,
@@ -1775,12 +1782,18 @@ bt_target_page_check(BtreeCheckState *state)
17751782

17761783
/*
17771784
* If the index is unique verify entries uniqueness by checking the
1778-
* heap tuples visibility.
1785+
* heap tuples visibility. Immediately check posting tuples and
1786+
* tuples with repeated keys. Postpone check for keys, which have the
1787+
* first appearance.
17791788
*/
17801789
if (state->checkunique && state->indexinfo->ii_Unique &&
1781-
P_ISLEAF(topaque) && !skey->anynullkeys)
1790+
P_ISLEAF(topaque) && !skey->anynullkeys &&
1791+
(BTreeTupleIsPosting(itup) || ItemPointerIsValid(lVis.tid)))
1792+
{
17821793
bt_entry_unique_check(state, itup, state->targetblock, offset,
17831794
&lVis);
1795+
unique_checked = true;
1796+
}
17841797

17851798
if (state->checkunique && state->indexinfo->ii_Unique &&
17861799
P_ISLEAF(topaque) && OffsetNumberNext(offset) <= max)
@@ -1799,6 +1812,9 @@ bt_target_page_check(BtreeCheckState *state)
17991812
* data (whole index tuple or last posting in index tuple). Key
18001813
* containing null value does not violate unique constraint and
18011814
* treated as different to any other key.
1815+
*
1816+
* If the next key is the same as the previous one, do the
1817+
* bt_entry_unique_check() call if it was postponed.
18021818
*/
18031819
if (_bt_compare(state->rel, skey, state->target,
18041820
OffsetNumberNext(offset)) != 0 || skey->anynullkeys)
@@ -1808,6 +1824,11 @@ bt_target_page_check(BtreeCheckState *state)
18081824
lVis.postingIndex = -1;
18091825
lVis.tid = NULL;
18101826
}
1827+
else if (!unique_checked)
1828+
{
1829+
bt_entry_unique_check(state, itup, state->targetblock, offset,
1830+
&lVis);
1831+
}
18111832
skey->scantid = scantid; /* Restore saved scan key state */
18121833
}
18131834

@@ -1890,10 +1911,19 @@ bt_target_page_check(BtreeCheckState *state)
18901911
rightkey->scantid = NULL;
18911912

18921913
/* The first key on the next page is the same */
1893-
if (_bt_compare(state->rel, rightkey, state->target, max) == 0 && !rightkey->anynullkeys)
1914+
if (_bt_compare(state->rel, rightkey, state->target, max) == 0 &&
1915+
!rightkey->anynullkeys)
18941916
{
18951917
Page rightpage;
18961918

1919+
/*
1920+
* Do the bt_entry_unique_check() call if it was
1921+
* postponed.
1922+
*/
1923+
if (!unique_checked)
1924+
bt_entry_unique_check(state, itup, state->targetblock,
1925+
offset, &lVis);
1926+
18971927
elog(DEBUG2, "cross page equal keys");
18981928
rightpage = palloc_btree_page(state,
18991929
rightblock_number);

0 commit comments

Comments
 (0)