From 44609643f1f0c490bf71019ed4187adac079f4c9 Mon Sep 17 00:00:00 2001 From: Alexander Korotkov Date: Thu, 14 Sep 2023 13:18:09 +0300 Subject: [PATCH] Skip checking of scan keys required for directional scan in B-tree Reported-by: Bug: Discussion: Author: Reviewed-by: Tested-by: Backpatch-through: --- src/backend/access/nbtree/nbtsearch.c | 29 ++++++++++++++++++++++++--- src/backend/access/nbtree/nbtutils.c | 25 +++++++++++++---------- src/include/access/nbtree.h | 6 +++++- 3 files changed, 45 insertions(+), 15 deletions(-) diff --git a/src/backend/access/nbtree/nbtsearch.c b/src/backend/access/nbtree/nbtsearch.c index 17ad89749d5..1162765ab16 100644 --- a/src/backend/access/nbtree/nbtsearch.c +++ b/src/backend/access/nbtree/nbtsearch.c @@ -1429,6 +1429,7 @@ _bt_first(IndexScanDesc scan, ScanDirection dir) /* remember which buffer we have pinned, if any */ Assert(!BTScanPosIsValid(so->currPos)); so->currPos.buf = buf; + so->firstPage = true; /* * Now load data from the first page of the scan. @@ -1539,6 +1540,7 @@ _bt_readpage(IndexScanDesc scan, ScanDirection dir, OffsetNumber offnum) int itemIndex; bool continuescan; int indnatts; + bool requiredDirMatched; /* * We must have the buffer pinned and locked, but the usual macro can't be @@ -1592,6 +1594,26 @@ _bt_readpage(IndexScanDesc scan, ScanDirection dir, OffsetNumber offnum) */ Assert(BTScanPosIsPinned(so->currPos)); + /* + * Prechecking the page with scan keys required for direction scan. If + * those keys are matched with the last item on the page, we can skip + * matching them to every item. Skip this for the first page in the scan + * to evade slowdown of point queries. + */ + if (!so->firstPage) + { + ItemId iid; + IndexTuple itup; + + iid = PageGetItemId(page, ScanDirectionIsForward(dir) ? maxoff : minoff); + itup = (IndexTuple) PageGetItem(page, iid); + (void) _bt_checkkeys(scan, itup, indnatts, dir, &requiredDirMatched, false); + } + else + { + so->firstPage = false; + } + if (ScanDirectionIsForward(dir)) { /* load items[] in ascending order */ @@ -1616,7 +1638,8 @@ _bt_readpage(IndexScanDesc scan, ScanDirection dir, OffsetNumber offnum) itup = (IndexTuple) PageGetItem(page, iid); - if (_bt_checkkeys(scan, itup, indnatts, dir, &continuescan)) + if (_bt_checkkeys(scan, itup, indnatts, dir, &continuescan, + requiredDirMatched)) { /* tuple passes all scan key conditions */ if (!BTreeTupleIsPosting(itup)) @@ -1673,7 +1696,7 @@ _bt_readpage(IndexScanDesc scan, ScanDirection dir, OffsetNumber offnum) int truncatt; truncatt = BTreeTupleGetNAtts(itup, scan->indexRelation); - _bt_checkkeys(scan, itup, truncatt, dir, &continuescan); + _bt_checkkeys(scan, itup, truncatt, dir, &continuescan, false); } if (!continuescan) @@ -1725,7 +1748,7 @@ _bt_readpage(IndexScanDesc scan, ScanDirection dir, OffsetNumber offnum) itup = (IndexTuple) PageGetItem(page, iid); passes_quals = _bt_checkkeys(scan, itup, indnatts, dir, - &continuescan); + &continuescan, requiredDirMatched); if (passes_quals && tuple_alive) { /* tuple passes all scan key conditions */ diff --git a/src/backend/access/nbtree/nbtutils.c b/src/backend/access/nbtree/nbtutils.c index 7da499c4dd5..696641fe5aa 100644 --- a/src/backend/access/nbtree/nbtutils.c +++ b/src/backend/access/nbtree/nbtutils.c @@ -1357,10 +1357,13 @@ _bt_mark_scankey_required(ScanKey skey) * tupnatts: number of attributes in tupnatts (high key may be truncated) * dir: direction we are scanning in * continuescan: output parameter (will be set correctly in all cases) + * requiredDirMatched: indicates that scan keys required for direction scan + * are already matched */ bool _bt_checkkeys(IndexScanDesc scan, IndexTuple tuple, int tupnatts, - ScanDirection dir, bool *continuescan) + ScanDirection dir, bool *continuescan, + bool requiredDirMatched) { TupleDesc tupdesc; BTScanOpaque so; @@ -1381,6 +1384,14 @@ _bt_checkkeys(IndexScanDesc scan, IndexTuple tuple, int tupnatts, Datum datum; bool isNull; Datum test; + bool requiredDir = false; + + if (key->sk_flags & (SK_BT_REQFWD | SK_BT_REQBKWD) && + !(key->sk_flags & SK_ROW_MEMBER)) + requiredDir = true; + + if (requiredDir && requiredDirMatched) + continue; if (key->sk_attno > tupnatts) { @@ -1429,11 +1440,7 @@ _bt_checkkeys(IndexScanDesc scan, IndexTuple tuple, int tupnatts, * scan direction, then we can conclude no further tuples will * pass, either. */ - if ((key->sk_flags & SK_BT_REQFWD) && - ScanDirectionIsForward(dir)) - *continuescan = false; - else if ((key->sk_flags & SK_BT_REQBKWD) && - ScanDirectionIsBackward(dir)) + if (requiredDir) *continuescan = false; /* @@ -1498,11 +1505,7 @@ _bt_checkkeys(IndexScanDesc scan, IndexTuple tuple, int tupnatts, * initial positioning in _bt_first() when they are available. See * comments in _bt_first(). */ - if ((key->sk_flags & SK_BT_REQFWD) && - ScanDirectionIsForward(dir)) - *continuescan = false; - else if ((key->sk_flags & SK_BT_REQBKWD) && - ScanDirectionIsBackward(dir)) + if (requiredDir) *continuescan = false; /* diff --git a/src/include/access/nbtree.h b/src/include/access/nbtree.h index f5c66964ca0..66e6a7775bf 100644 --- a/src/include/access/nbtree.h +++ b/src/include/access/nbtree.h @@ -1054,6 +1054,9 @@ typedef struct BTScanOpaqueData int *killedItems; /* currPos.items indexes of killed items */ int numKilled; /* number of currently stored items */ + /* flag inficating the first page in the scan */ + bool firstPage; + /* * If we are doing an index-only scan, these are the tuple storage * workspaces for the currPos and markPos respectively. Each is of size @@ -1253,7 +1256,8 @@ extern void _bt_mark_array_keys(IndexScanDesc scan); extern void _bt_restore_array_keys(IndexScanDesc scan); extern void _bt_preprocess_keys(IndexScanDesc scan); extern bool _bt_checkkeys(IndexScanDesc scan, IndexTuple tuple, - int tupnatts, ScanDirection dir, bool *continuescan); + int tupnatts, ScanDirection dir, bool *continuescan, + bool requiredMatched); extern void _bt_killitems(IndexScanDesc scan); extern BTCycleId _bt_vacuum_cycleid(Relation rel); extern BTCycleId _bt_start_vacuum(Relation rel); -- 2.37.1 (Apple Git-137.1)