77 *
88 *
99 * IDENTIFICATION
10- * $Header: /cvsroot/pgsql/src/backend/access/nbtree/nbtsearch.c,v 1.50 1999/07/1604:58:30 momjian Exp $
10+ * $Header: /cvsroot/pgsql/src/backend/access/nbtree/nbtsearch.c,v 1.51 1999/07/1622:17:06 tgl Exp $
1111 *
1212 *-------------------------------------------------------------------------
1313 */
2626
2727static BTStack _bt_searchr (Relation rel ,int keysz ,ScanKey scankey ,
2828Buffer * bufP ,BTStack stack_in );
29- static OffsetNumber _bt_firsteq (Relation rel ,TupleDesc itupdesc ,Page page ,
30- Size keysz ,ScanKey scankey ,OffsetNumber offnum );
3129static int _bt_compare (Relation rel ,TupleDesc itupdesc ,Page page ,
3230int keysz ,ScanKey scankey ,OffsetNumber offnum );
3331static bool
@@ -368,7 +366,9 @@ _bt_skeycmp(Relation rel,
368366 *comparison for every key in the scankey. _bt_binsrch() returns
369367 *the OffsetNumber of the first matching key on the page, or the
370368 *OffsetNumber at which the matching key would appear if it were
371- *on this page.
369+ *on this page. (NOTE: in particular, this means it is possible to
370+ *return a value 1 greater than the number of keys on the page, if
371+ *the scankey is > all keys on the page.)
372372 *
373373 *By the time this procedure is called, we're sure we're looking
374374 *at the right page -- don't need to walk right. _bt_binsrch() has
@@ -385,8 +385,8 @@ _bt_binsrch(Relation rel,
385385Page page ;
386386BTPageOpaque opaque ;
387387OffsetNumber low ,
388- mid ,
389388high ;
389+ bool haveEq ;
390390int natts = rel -> rd_rel -> relnatts ;
391391int result ;
392392
@@ -395,148 +395,112 @@ _bt_binsrch(Relation rel,
395395opaque = (BTPageOpaque )PageGetSpecialPointer (page );
396396
397397/* by convention, item 1 on any non-rightmost page is the high key */
398- low = mid = P_RIGHTMOST (opaque ) ?P_HIKEY :P_FIRSTKEY ;
398+ low = P_RIGHTMOST (opaque ) ?P_HIKEY :P_FIRSTKEY ;
399399
400400high = PageGetMaxOffsetNumber (page );
401401
402402/*
403- * Since for non-rightmost pages, the first item on the page is the
404- * high key, there are two notions of emptiness. One is if nothing
405- * appears on the page. The other is if nothing but the high key
406- * does. The reason we test high <= low, rather than high == low, is
407- * that after vacuuming there may be nothing *but* the high key on a
408- * page. In that case, given the scheme above, low = 2 and high = 1.
403+ * If there are no keys on the page, return the first available slot.
404+ * Note this covers two cases: the page is really empty (no keys),
405+ * or it contains only a high key. The latter case is possible after
406+ * vacuuming.
409407 */
410-
411- if (PageIsEmpty (page ))
408+ if (high < low )
412409return low ;
413- if ((!P_RIGHTMOST (opaque )&& high <=low ))
414- {
415- if (high < low ||
416- (srchtype == BT_DESCENT && !(opaque -> btpo_flags & BTP_LEAF )))
417- return low ;
418- /* It's insertion and high == low == 2 */
419- result = _bt_compare (rel ,itupdesc ,page ,keysz ,scankey ,low );
420- if (result > 0 )
421- return OffsetNumberNext (low );
422- return low ;
423- }
424410
425- while ((high - low )> 1 )
411+ /*
412+ * Binary search to find the first key on the page >= scan key.
413+ * Loop invariant: all slots before 'low' are < scan key, all slots
414+ * at or after 'high' are >= scan key. Also, haveEq is true if the
415+ * tuple at 'high' is == scan key.
416+ * We can fall out when high == low.
417+ */
418+ high ++ ;/* establish the loop invariant for high */
419+ haveEq = false;
420+
421+ while (high > low )
426422{
427- mid = low + ((high - low ) /2 );
423+ OffsetNumber mid = low + ((high - low ) /2 );
424+ /* We have low <= mid < high, so mid points at a real slot */
425+
428426result = _bt_compare (rel ,itupdesc ,page ,keysz ,scankey ,mid );
429427
430428if (result > 0 )
431- low = mid ;
432- else if (result < 0 )
433- high = mid - 1 ;
429+ low = mid + 1 ;
434430else
435431{
436- mid = _bt_firsteq (rel ,itupdesc ,page ,keysz ,scankey ,mid );
437-
438- /*
439- * NOTE for multi-column indices: we may do scan using keys
440- * not for all attrs. But we handle duplicates using all attrs
441- * in _bt_insert/_bt_spool code. And so while searching on
442- * internal pages having number of attrs > keysize we want to
443- * point at the last item < the scankey, not at the first item
444- * = the scankey (!!!), and let _bt_moveright decide later
445- * whether to move right or not (see comments and example
446- * there). Note also that INSERTions are not affected by this
447- * code (natts == keysz). - vadim 04/15/97
448- */
449- if (natts == keysz || opaque -> btpo_flags & BTP_LEAF )
450- return mid ;
451- low = P_RIGHTMOST (opaque ) ?P_HIKEY :P_FIRSTKEY ;
452- if (mid == low )
453- return mid ;
454- return OffsetNumberPrev (mid );
432+ high = mid ;
433+ haveEq = (result == 0 );
455434}
456435}
457436
458- /*
459- * We terminated because the endpoints got too close together.There
460- * are two cases to take care of.
437+ /*--------------------
438+ * At this point we have high == low, but be careful: they could point
439+ * past the last slot on the page. We also know that haveEq is true
440+ * if and only if there is an equal key (in which case high&low point
441+ * at the first equal key).
461442 *
462- * For non-insertion searches on internal pages, we want to point at the
463- * last key <, or first key =, the scankey on the page. This
464- * guarantees that we'll descend the tree correctly. (NOTE comments
465- * above for multi-column indices).
466- *
467- * For all other cases, we want to point at the first key >= the scankey
468- * on the page. This guarantees that scans and insertions will happen
469- * correctly.
443+ * On a leaf page, we always return the first key >= scan key
444+ * (which could be the last slot + 1).
445+ *--------------------
470446 */
471447
472- if (!(opaque -> btpo_flags & BTP_LEAF )&& srchtype == BT_DESCENT )
473- {/* We want the last key <, or first key
474- * ==, the scan key. */
475- result = _bt_compare (rel ,itupdesc ,page ,keysz ,scankey ,high );
448+ if (opaque -> btpo_flags & BTP_LEAF )
449+ return low ;
476450
477- if (result == 0 )
478- {
479- mid = _bt_firsteq (rel ,itupdesc ,page ,keysz ,scankey ,high );
451+ /*--------------------
452+ * On a non-leaf page, there are special cases:
453+ *
454+ * For an insertion (srchtype != BT_DESCENT and natts == keysz)
455+ * always return first key >= scan key (which could be off the end).
456+ *
457+ * For a standard search (srchtype == BT_DESCENT and natts == keysz)
458+ * return the first equal key if one exists, else the last lesser key
459+ * if one exists, else the first slot on the page.
460+ *
461+ * For a partial-match search (srchtype == BT_DESCENT and natts < keysz)
462+ * return the last lesser key if one exists, else the first slot.
463+ *
464+ * Old comments:
465+ *For multi-column indices, we may scan using keys
466+ *not for all attrs. But we handle duplicates using all attrs
467+ *in _bt_insert/_bt_spool code. And so while searching on
468+ *internal pages having number of attrs > keysize we want to
469+ *point at the last item < the scankey, not at the first item
470+ *= the scankey (!!!), and let _bt_moveright decide later
471+ *whether to move right or not (see comments and example
472+ *there). Note also that INSERTions are not affected by this
473+ *code (since natts == keysz for inserts). - vadim 04/15/97
474+ *--------------------
475+ */
480476
481- /*
482- * If natts > keysz we want last item < the scan key. See
483- * comments above for multi-column indices.
484- */
485- if (natts == keysz )
486- return mid ;
487- low = P_RIGHTMOST (opaque ) ?P_HIKEY :P_FIRSTKEY ;
488- if (mid == low )
489- return mid ;
490- return OffsetNumberPrev (mid );
491- }
492- else if (result > 0 )
493- return high ;
494- else
495- return low ;
477+ if (haveEq )
478+ {
479+ /*
480+ * There is an equal key. We return either the first equal key
481+ * (which we just found), or the last lesser key.
482+ *
483+ * We need not check srchtype != BT_DESCENT here, since if that
484+ * is true then natts == keysz by assumption.
485+ */
486+ if (natts == keysz )
487+ return low ;/* return first equal key */
496488}
497489else
498- /* we want the first key >= the scan key */
499490{
500- result = _bt_compare (rel ,itupdesc ,page ,keysz ,scankey ,low );
501- if (result <=0 )
502- return low ;
503- else
504- {
505- if (low == high )
506- return OffsetNumberNext (low );
507-
508- result = _bt_compare (rel ,itupdesc ,page ,keysz ,scankey ,high );
509- if (result <=0 )
510- return high ;
511- else
512- return OffsetNumberNext (high );
513- }
491+ /*
492+ * There is no equal key. We return either the first greater key
493+ * (which we just found), or the last lesser key.
494+ */
495+ if (srchtype != BT_DESCENT )
496+ return low ;/* return first greater key */
514497}
515- }
516498
517- static OffsetNumber
518- _bt_firsteq (Relation rel ,
519- TupleDesc itupdesc ,
520- Page page ,
521- Size keysz ,
522- ScanKey scankey ,
523- OffsetNumber offnum )
524- {
525- BTPageOpaque opaque ;
526- OffsetNumber limit ;
527499
528- opaque = (BTPageOpaque )PageGetSpecialPointer (page );
500+ if (low == (P_RIGHTMOST (opaque ) ?P_HIKEY :P_FIRSTKEY ))
501+ return low ;/* there is no prior item */
529502
530- /* skip the high key, if any */
531- limit = P_RIGHTMOST (opaque ) ?P_HIKEY :P_FIRSTKEY ;
532-
533- /* walk backwards looking for the first key in the chain of duplicates */
534- while (offnum > limit
535- && _bt_compare (rel ,itupdesc ,page ,
536- keysz ,scankey ,OffsetNumberPrev (offnum ))== 0 )
537- offnum = OffsetNumberPrev (offnum );
538-
539- return offnum ;
503+ return OffsetNumberPrev (low );
540504}
541505
542506/*
@@ -571,7 +535,6 @@ _bt_compare(Relation rel,
571535{
572536Datum datum ;
573537BTItem btitem ;
574- ItemId itemid ;
575538IndexTuple itup ;
576539BTPageOpaque opaque ;
577540ScanKey entry ;
@@ -589,12 +552,11 @@ _bt_compare(Relation rel,
589552 */
590553
591554opaque = (BTPageOpaque )PageGetSpecialPointer (page );
555+
592556if (!(opaque -> btpo_flags & BTP_LEAF )
593557&& P_LEFTMOST (opaque )
594558&& offnum == P_HIKEY )
595559{
596- itemid = PageGetItemId (page ,offnum );
597-
598560/*
599561 * we just have to believe that this will only be called with
600562 * offnum == P_HIKEY when P_HIKEY is the OffsetNumber of the first
@@ -621,7 +583,7 @@ _bt_compare(Relation rel,
621583 * on the page is greater than anything.
622584 */
623585
624- if (_bt_skeycmp (rel ,keysz ,scankey ,page ,itemid ,
586+ if (_bt_skeycmp (rel ,keysz ,scankey ,page ,PageGetItemId ( page , offnum ) ,
625587BTEqualStrategyNumber ))
626588return 0 ;
627589return 1 ;