@@ -1429,6 +1429,7 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
1429
1429
/* remember which buffer we have pinned, if any */
1430
1430
Assert (!BTScanPosIsValid (so -> currPos ));
1431
1431
so -> currPos .buf = buf ;
1432
+ so -> firstPage = true;
1432
1433
1433
1434
/*
1434
1435
* Now load data from the first page of the scan.
@@ -1539,6 +1540,7 @@ _bt_readpage(IndexScanDesc scan, ScanDirection dir, OffsetNumber offnum)
1539
1540
int itemIndex ;
1540
1541
bool continuescan ;
1541
1542
int indnatts ;
1543
+ bool requiredMatchedByPrecheck ;
1542
1544
1543
1545
/*
1544
1546
* We must have the buffer pinned and locked, but the usual macro can't be
@@ -1592,6 +1594,46 @@ _bt_readpage(IndexScanDesc scan, ScanDirection dir, OffsetNumber offnum)
1592
1594
*/
1593
1595
Assert (BTScanPosIsPinned (so -> currPos ));
1594
1596
1597
+ /*
1598
+ * Prechecking the page with scan keys required for direction scan. We
1599
+ * check these keys with the last item on the page (according to our scan
1600
+ * direction). If these keys are matched, we can skip checking them with
1601
+ * every item on the page. Scan keys for our scan direction would
1602
+ * necessarily match the previous items. Scan keys required for opposite
1603
+ * direction scan are already matched by the _bt_first() call.
1604
+ *
1605
+ * With the forward scan, we do this check for the last item on the page
1606
+ * instead of the high key. It's relatively likely that the most
1607
+ * significant column in the high key will be different from the
1608
+ * corresponding value from the last item on the page. So checking with
1609
+ * the last item on the page would give a more precise answer.
1610
+ *
1611
+ * We skip this for the first page in the scan to evade the possible
1612
+ * slowdown of the point queries.
1613
+ */
1614
+ if (!so -> firstPage && minoff < maxoff )
1615
+ {
1616
+ ItemId iid ;
1617
+ IndexTuple itup ;
1618
+
1619
+ iid = PageGetItemId (page , ScanDirectionIsForward (dir ) ? maxoff : minoff );
1620
+ itup = (IndexTuple ) PageGetItem (page , iid );
1621
+
1622
+ /*
1623
+ * Do the precheck. Note that we pass the pointer to
1624
+ * 'requiredMatchedByPrecheck' to 'continuescan' argument. That will
1625
+ * set flag to true if all required keys are satisfied and false
1626
+ * otherwise.
1627
+ */
1628
+ (void ) _bt_checkkeys (scan , itup , indnatts , dir ,
1629
+ & requiredMatchedByPrecheck , false);
1630
+ }
1631
+ else
1632
+ {
1633
+ so -> firstPage = false;
1634
+ requiredMatchedByPrecheck = false;
1635
+ }
1636
+
1595
1637
if (ScanDirectionIsForward (dir ))
1596
1638
{
1597
1639
/* load items[] in ascending order */
@@ -1603,6 +1645,7 @@ _bt_readpage(IndexScanDesc scan, ScanDirection dir, OffsetNumber offnum)
1603
1645
{
1604
1646
ItemId iid = PageGetItemId (page , offnum );
1605
1647
IndexTuple itup ;
1648
+ bool passes_quals ;
1606
1649
1607
1650
/*
1608
1651
* If the scan specifies not to return killed tuples, then we
@@ -1616,7 +1659,18 @@ _bt_readpage(IndexScanDesc scan, ScanDirection dir, OffsetNumber offnum)
1616
1659
1617
1660
itup = (IndexTuple ) PageGetItem (page , iid );
1618
1661
1619
- if (_bt_checkkeys (scan , itup , indnatts , dir , & continuescan ))
1662
+ passes_quals = _bt_checkkeys (scan , itup , indnatts , dir ,
1663
+ & continuescan , requiredMatchedByPrecheck );
1664
+
1665
+ /*
1666
+ * If the result of prechecking required keys was true, then in
1667
+ * assert-enabled builds we also recheck that _bt_checkkeys()
1668
+ * result is is the same.
1669
+ */
1670
+ Assert (!requiredMatchedByPrecheck ||
1671
+ passes_quals == _bt_checkkeys (scan , itup , indnatts , dir ,
1672
+ & continuescan , false));
1673
+ if (passes_quals )
1620
1674
{
1621
1675
/* tuple passes all scan key conditions */
1622
1676
if (!BTreeTupleIsPosting (itup ))
@@ -1673,7 +1727,7 @@ _bt_readpage(IndexScanDesc scan, ScanDirection dir, OffsetNumber offnum)
1673
1727
int truncatt ;
1674
1728
1675
1729
truncatt = BTreeTupleGetNAtts (itup , scan -> indexRelation );
1676
- _bt_checkkeys (scan , itup , truncatt , dir , & continuescan );
1730
+ _bt_checkkeys (scan , itup , truncatt , dir , & continuescan , false );
1677
1731
}
1678
1732
1679
1733
if (!continuescan )
@@ -1725,7 +1779,16 @@ _bt_readpage(IndexScanDesc scan, ScanDirection dir, OffsetNumber offnum)
1725
1779
itup = (IndexTuple ) PageGetItem (page , iid );
1726
1780
1727
1781
passes_quals = _bt_checkkeys (scan , itup , indnatts , dir ,
1728
- & continuescan );
1782
+ & continuescan , requiredMatchedByPrecheck );
1783
+
1784
+ /*
1785
+ * If the result of prechecking required keys was true, then in
1786
+ * assert-enabled builds we also recheck that _bt_checkkeys()
1787
+ * result is is the same.
1788
+ */
1789
+ Assert (!requiredMatchedByPrecheck ||
1790
+ passes_quals == _bt_checkkeys (scan , itup , indnatts , dir ,
1791
+ & continuescan , false));
1729
1792
if (passes_quals && tuple_alive )
1730
1793
{
1731
1794
/* tuple passes all scan key conditions */
@@ -2443,6 +2506,7 @@ _bt_endpoint(IndexScanDesc scan, ScanDirection dir)
2443
2506
2444
2507
/* remember which buffer we have pinned */
2445
2508
so -> currPos .buf = buf ;
2509
+ so -> firstPage = true;
2446
2510
2447
2511
_bt_initialize_more_data (so , dir );
2448
2512
0 commit comments