@@ -456,6 +456,7 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
456
456
bool goback ;
457
457
ScanKey startKeys [INDEX_MAX_KEYS ];
458
458
ScanKeyData scankeys [INDEX_MAX_KEYS ];
459
+ ScanKeyData notnullkeys [INDEX_MAX_KEYS ];
459
460
int keysCount = 0 ;
460
461
int i ;
461
462
StrategyNumber strat_total ;
@@ -506,6 +507,14 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
506
507
* one we use --- by definition, they are either redundant or
507
508
* contradictory.
508
509
*
510
+ * Any regular (not SK_SEARCHNULL) key implies a NOT NULL qualifier.
511
+ * If the index stores nulls at the end of the index we'll be starting
512
+ * from, and we have no boundary key for the column (which means the key
513
+ * we deduced NOT NULL from is an inequality key that constrains the other
514
+ * end of the index), then we cons up an explicit SK_SEARCHNOTNULL key to
515
+ * use as a boundary key. If we didn't do this, we might find ourselves
516
+ * traversing a lot of null entries at the start of the scan.
517
+ *
509
518
* In this loop, row-comparison keys are treated the same as keys on their
510
519
* first (leftmost) columns. We'll add on lower-order columns of the row
511
520
* comparison below, if possible.
@@ -519,6 +528,7 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
519
528
{
520
529
AttrNumber curattr ;
521
530
ScanKey chosen ;
531
+ ScanKey impliesNN ;
522
532
ScanKey cur ;
523
533
524
534
/*
@@ -528,6 +538,8 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
528
538
*/
529
539
curattr = 1 ;
530
540
chosen = NULL ;
541
+ /* Also remember any scankey that implies a NOT NULL constraint */
542
+ impliesNN = NULL ;
531
543
532
544
/*
533
545
* Loop iterates from 0 to numberOfKeys inclusive; we use the last
@@ -540,8 +552,32 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
540
552
{
541
553
/*
542
554
* Done looking at keys for curattr. If we didn't find a
543
- * usable boundary key, quit; else save the boundary key
544
- * pointer in startKeys.
555
+ * usable boundary key, see if we can deduce a NOT NULL key.
556
+ */
557
+ if (chosen == NULL && impliesNN != NULL &&
558
+ ((impliesNN -> sk_flags & SK_BT_NULLS_FIRST ) ?
559
+ ScanDirectionIsForward (dir ) :
560
+ ScanDirectionIsBackward (dir )))
561
+ {
562
+ /* Yes, so build the key in notnullkeys[keysCount] */
563
+ chosen = & notnullkeys [keysCount ];
564
+ ScanKeyEntryInitialize (chosen ,
565
+ (SK_SEARCHNOTNULL | SK_ISNULL |
566
+ (impliesNN -> sk_flags &
567
+ (SK_BT_DESC | SK_BT_NULLS_FIRST ))),
568
+ curattr ,
569
+ ((impliesNN -> sk_flags & SK_BT_NULLS_FIRST ) ?
570
+ BTGreaterStrategyNumber :
571
+ BTLessStrategyNumber ),
572
+ InvalidOid ,
573
+ InvalidOid ,
574
+ InvalidOid ,
575
+ (Datum ) 0 );
576
+ }
577
+
578
+ /*
579
+ * If we still didn't find a usable boundary key, quit; else
580
+ * save the boundary key pointer in startKeys.
545
581
*/
546
582
if (chosen == NULL )
547
583
break ;
@@ -574,24 +610,41 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
574
610
*/
575
611
curattr = cur -> sk_attno ;
576
612
chosen = NULL ;
613
+ impliesNN = NULL ;
577
614
}
578
615
579
- /* Can we use this key as a starting boundary for this attr? */
616
+ /*
617
+ * Can we use this key as a starting boundary for this attr?
618
+ *
619
+ * If not, does it imply a NOT NULL constraint? (Because
620
+ * SK_SEARCHNULL keys are always assigned BTEqualStrategyNumber,
621
+ * *any* inequality key works for that; we need not test.)
622
+ */
580
623
switch (cur -> sk_strategy )
581
624
{
582
625
case BTLessStrategyNumber :
583
626
case BTLessEqualStrategyNumber :
584
- if (chosen == NULL && ScanDirectionIsBackward (dir ))
585
- chosen = cur ;
627
+ if (chosen == NULL )
628
+ {
629
+ if (ScanDirectionIsBackward (dir ))
630
+ chosen = cur ;
631
+ else
632
+ impliesNN = cur ;
633
+ }
586
634
break ;
587
635
case BTEqualStrategyNumber :
588
636
/* override any non-equality choice */
589
637
chosen = cur ;
590
638
break ;
591
639
case BTGreaterEqualStrategyNumber :
592
640
case BTGreaterStrategyNumber :
593
- if (chosen == NULL && ScanDirectionIsForward (dir ))
594
- chosen = cur ;
641
+ if (chosen == NULL )
642
+ {
643
+ if (ScanDirectionIsForward (dir ))
644
+ chosen = cur ;
645
+ else
646
+ impliesNN = cur ;
647
+ }
595
648
break ;
596
649
}
597
650
}
0 commit comments