@@ -456,6 +456,7 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
456456 bool goback ;
457457 ScanKey startKeys [INDEX_MAX_KEYS ];
458458 ScanKeyData scankeys [INDEX_MAX_KEYS ];
459+ ScanKeyData notnullkeys [INDEX_MAX_KEYS ];
459460 int keysCount = 0 ;
460461 int i ;
461462 StrategyNumber strat_total ;
@@ -506,6 +507,14 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
506507 * one we use --- by definition, they are either redundant or
507508 * contradictory.
508509 *
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+ *
509518 * In this loop, row-comparison keys are treated the same as keys on their
510519 * first (leftmost) columns. We'll add on lower-order columns of the row
511520 * comparison below, if possible.
@@ -519,6 +528,7 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
519528 {
520529 AttrNumber curattr ;
521530 ScanKey chosen ;
531+ ScanKey impliesNN ;
522532 ScanKey cur ;
523533
524534 /*
@@ -528,6 +538,8 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
528538 */
529539 curattr = 1 ;
530540 chosen = NULL ;
541+ /* Also remember any scankey that implies a NOT NULL constraint */
542+ impliesNN = NULL ;
531543
532544 /*
533545 * Loop iterates from 0 to numberOfKeys inclusive; we use the last
@@ -540,8 +552,32 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
540552 {
541553 /*
542554 * 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.
545581 */
546582 if (chosen == NULL )
547583 break ;
@@ -574,24 +610,41 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
574610 */
575611 curattr = cur -> sk_attno ;
576612 chosen = NULL ;
613+ impliesNN = NULL ;
577614 }
578615
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+ */
580623 switch (cur -> sk_strategy )
581624 {
582625 case BTLessStrategyNumber :
583626 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+ }
586634 break ;
587635 case BTEqualStrategyNumber :
588636 /* override any non-equality choice */
589637 chosen = cur ;
590638 break ;
591639 case BTGreaterEqualStrategyNumber :
592640 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+ }
595648 break ;
596649 }
597650 }
0 commit comments