8000 Stop btree indexscans upon reaching nulls in either direction. · jaylevitt/postgres@d23165b · GitHub
[go: up one dir, main page]

Skip to content

Commit d23165b

Browse files
committed
Stop btree indexscans upon reaching nulls in either direction.
The existing scan-direction-sensitive tests were overly complex, and failed to stop the scan in cases where it's perfectly legitimate to do so. Per bug #6278 from Maksym Boguk. Back-patch to 8.3, which is as far back as the patch applies easily. Doesn't seem worth sweating over a relatively minor performance issue in 8.2 at this late date. (But note that this was a performance regression from 8.1 and before, so 8.2 is being left as an outlier.)
1 parent 04ec05e commit d23165b

File tree

1 file changed

+42
-65
lines changed

1 file changed

+42
-65
lines changed

src/backend/access/nbtree/nbtutils.c

Lines changed: 42 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -174,11 +174,11 @@ _bt_freestack(BTStack stack)
174174
* Also, for a DESC column, we commute (flip) all the sk_strategy numbers
175175
* so that the index sorts in the desired direction.
176176
*
177-
* One key purpose of this routine is to discover how many scan keys
178-
* must be satisfied to continue the scan. It also attempts to eliminate
179-
* redundant keys and detect contradictory keys. (If the index opfamily
180-
* provides incomplete sets of cross-type operators, we may fail to detect
181-
* redundant or contradictory keys, but we can survive that.)
177+
* One key purpose of this routine is to discover which scan keys must be
178+
* satisfied to continue the scan. It also attempts to eliminate redundant
179+
* keys and detect contradictory keys. (If the index opfamily provides
180+
* incomplete sets of cross-type operators, we may fail to detect redundant
181+
* or contradictory keys, but we can survive that.)
182182
*
183183
* The output keys must be sorted by index attribute. Presently we expect
184184
* (but verify) that the input keys are already so sorted --- this is done
@@ -213,6 +213,16 @@ _bt_freestack(BTStack stack)
213213
* </<= keys if we can't compare them. The logic about required keys still
214214
* works if we don't eliminate redundant keys.
215215
*
216+
* Note that the reason we need direction-sensitive required-key flags is
217+
* precisely that we may not be able to eliminate redundant keys. Suppose
218+
* we have "x > 4::int AND x > 10::bigint", and we are unable to determine
219+
* which key is more restrictive for lack of a suitable cross-type operator.
220+
* _bt_first will arbitrarily pick one of the keys to do the initial
221+
* positioning with. If it picks x > 4, then the x > 10 condition will fail
222+
* until we reach index entries > 10; but we can't stop the scan just because
223+
* x > 10 is failing. On the other hand, if we are scanning backwards, then
224+
* failure of either key is indeed enough to stop the scan.
225+
*
216226
* As a byproduct of this work, we can detect contradictory quals such
217227
* as "x = 1 AND x > 2". If we see that, we return so->qual_ok = FALSE,
218228
* indicating the scan need not be run at all since no tuples can match.
@@ -853,15 +863,16 @@ _bt_checkkeys(IndexScanDesc scan,
853863
continue; /* tuple satisfies this qual */
854864

855865
/*
856-
* Tuple fails this qual. If it's a required qual for the current
857-
* scan direction, then we can conclude no further tuples will
858-
* pass, either.
866+
* Tuple fails this qual. If it's a required qual, then we can
867+
* conclude no further tuples will pass, either. We can stop
868+
* regardless of the scan direction, because we know that NULLs
869+
* sort to one end or the other of the range of values. If this
870+
* tuple doesn't pass, then no future ones will either, until we
871+
* reach the next set of values of the higher-order index attrs
872+
* (if any) ... and those attrs must have equality quals, else
873+
* this one wouldn't be marked required.
859874
*/
860-
if ((key->sk_flags & SK_BT_REQFWD) &&
861-
ScanDirectionIsForward(dir))
862-
*continuescan = false;
863-
else if ((key->sk_flags & SK_BT_REQBKWD) &&
864-
ScanDirectionIsBackward(dir))
875+
if (key->sk_flags & (SK_BT_REQFWD | SK_BT_REQBKWD))
865876
*continuescan = false;
866877

867878
/*
@@ -872,32 +883,15 @@ _bt_checkkeys(IndexScanDesc scan,
872883

873884
if (isNull)
874885
{
875-
if (key->sk_flags & SK_BT_NULLS_FIRST)
876-
{
877-
/*
878-
* Since NULLs are sorted before non-NULLs, we know we have
879-
* reached the lower limit of the range of values for this
880-
* index attr. On a backward scan, we can stop if this qual
881-
* is one of the "must match" subset. On a forward scan,
882-
* however, we should keep going.
883-
*/
884-
if ((key->sk_flags & SK_BT_REQBKWD) &&
885-
ScanDirectionIsBackward(dir))
886-
*continuescan = false;
887-
}
888-
else
889-
{
890-
/*
891-
* Since NULLs are sorted after non-NULLs, we know we have
892-
* reached the upper limit of the range of values for this
893-
* index attr. On a forward scan, we can stop if this qual is
894-
* one of the "must match" subset. On a backward scan,
895-
* however, we should keep going.
896-
*/
897-
if ((key->sk_flags & SK_BT_REQFWD) &&
898-
ScanDirectionIsForward(dir))
899-
*continuescan = false;
900-
}
886+
/*
887+
* The index entry is NULL, so it must fail this qual (we assume
888+
* all btree operators are strict). Furthermore, we know that
889+
* all remaining entries with the same higher-order index attr
890+
* values must be NULLs too. So, just as above, we can stop the
891+
* scan regardless of direction, if the qual is required.
892+
*/
893+
if (key->sk_flags & (SK_BT_REQFWD | SK_BT_REQBKWD))
894+
*continuescan = false;
901895

902896
/*
903897
* In any case, this indextuple doesn't match the qual.
@@ -975,32 +969,15 @@ _bt_check_rowcompare(ScanKey skey, IndexTuple tuple, TupleDesc tupdesc,
975969

976970
if (isNull)
977971
{
978-
if (subkey->sk_flags & SK_BT_NULLS_FIRST)
979-
{
980-
/*
981-
* Since NULLs are sorted before non-NULLs, we know we have
982-
* reached the lower limit of the range of values for this
983-
* index attr. On a backward scan, we can stop if this qual is
984-
* one of the "must match" subset. On a forward scan,
985-
* however, we should keep going.
986-
*/
987-
if ((subkey->sk_flags & SK_BT_REQBKWD) &&
988-
ScanDirectionIsBackward(dir))
989-
*continuescan = false;
990-
}
991-
else
992-
{
993-
/*
994-
* Since NULLs are sorted after non-NULLs, we know we have
995-
* reached the upper limit of the range of values for this
996-
* index attr. On a forward scan, we can stop if this qual is
997-
* one of the "must match" subset. On a backward scan,
998-
* however, we should keep going.
999-
*/
1000-
if ((subkey->sk_flags & SK_BT_REQFWD) &&
1001-
ScanDirectionIsForward(dir))
1002-
*continuescan = false;
1003-
}
972+
/*
973+
* The index entry is NULL, so it must fail this qual (we assume
974+
* all btree operators are strict). Furthermore, we know that
975+
* all remaining entries with the same higher-order index attr
976+
* values must be NULLs too. So, just as above, we can stop the
977+
* scan regardless of direction, if the qual is required.
978+
*/
979+
if (subkey->sk_flags & (SK_BT_REQFWD | SK_BT_REQBKWD))
980+
*continuescan = false;
1004981

1005982
/*
1006983
* In any case, this indextuple doesn't match the qual.

0 commit comments

Comments
 (0)
0