@ -57,11 +57,11 @@ static bool _bt_oppodir_checkkeys(IndexScanDesc scan, ScanDirection dir,
IndexTuple finaltup ) ;
static bool _bt_check_compare ( IndexScanDesc scan , ScanDirection dir ,
IndexTuple tuple , int tupnatts , TupleDesc tupdesc ,
bool advancenonrequired , bool prechecked , bool firstmatch ,
bool advancenonrequired , bool forcenonrequired ,
bool * continuescan , int * ikey ) ;
static bool _bt_check_rowcompare ( ScanKey skey ,
IndexTuple tuple , int tupnatts , TupleDesc tupdesc ,
ScanDirection dir , bool * continuescan ) ;
ScanDirection dir , bool forcenonrequired , bool * continuescan ) ;
static void _bt_checkkeys_look_ahead ( IndexScanDesc scan , BTReadPageState * pstate ,
int tupnatts , TupleDesc tupdesc ) ;
static int _bt_keep_natts ( Relation rel , IndexTuple lastleft ,
@ -1422,9 +1422,10 @@ _bt_start_prim_scan(IndexScanDesc scan, ScanDirection dir)
* postcondition ' s < = operator with a > = . In other words , just swap the
* precondition with the postcondition . )
*
* We also deal with " advancing " non - required arrays here . Callers whose
* sktrig scan key is non - required specify sktrig_required = false . These calls
* are the only exception to the general rule about always advancing the
* We also deal with " advancing " non - required arrays here ( or arrays that are
* treated as non - required for the duration of a _bt_readpage call ) . Callers
* whose sktrig scan key is non - required specify sktrig_required = false . These
* calls are the only exception to the general rule about always advancing the
* required array keys ( the scan may not even have a required array ) . These
* callers should just pass a NULL pstate ( since there is never any question
* of stopping the scan ) . No call to _bt_tuple_before_array_skeys is required
@ -1464,6 +1465,7 @@ _bt_advance_array_keys(IndexScanDesc scan, BTReadPageState *pstate,
all_satisfied = true ;
Assert ( ! so - > needPrimScan & & ! so - > scanBehind & & ! so - > oppositeDirCheck ) ;
Assert ( _bt_verify_keys_with_arraykeys ( scan ) ) ;
if ( sktrig_required )
{
@ -1473,17 +1475,6 @@ _bt_advance_array_keys(IndexScanDesc scan, BTReadPageState *pstate,
Assert ( ! _bt_tuple_before_array_skeys ( scan , dir , tuple , tupdesc ,
tupnatts , false , 0 , NULL ) ) ;
/*
* Required scan key wasn ' t satisfied , so required arrays will have to
* advance . Invalidate page - level state that tracks whether the
* scan ' s required - in - opposite - direction - only keys are known to be
* satisfied by page ' s remaining tuples .
*/
pstate - > firstmatch = false ;
/* Shouldn't have to invalidate 'prechecked', though */
Assert ( ! pstate - > prechecked ) ;
/*
* Once we return we ' ll have a new set of required array keys , so
* reset state used by " look ahead " optimization
@ -1491,8 +1482,26 @@ _bt_advance_array_keys(IndexScanDesc scan, BTReadPageState *pstate,
pstate - > rechecks = 0 ;
pstate - > targetdistance = 0 ;
}
else if ( sktrig < so - > numberOfKeys - 1 & &
! ( so - > keyData [ so - > numberOfKeys - 1 ] . sk_flags & SK_SEARCHARRAY ) )
{
int least_sign_ikey = so - > numberOfKeys - 1 ;
bool continuescan ;
Assert ( _bt_verify_keys_with_arraykeys ( scan ) ) ;
/*
* Optimization : perform a precheck of the least significant key
* during ! sktrig_required calls when it isn ' t already our sktrig
* ( provided the precheck key is not itself an array ) .
*
* When the precheck works out we ' ll avoid an expensive binary search
* of sktrig ' s array ( plus any other arrays before least_sign_ikey ) .
*/
Assert ( so - > keyData [ sktrig ] . sk_flags & SK_SEARCHARRAY ) ;
if ( ! _bt_check_compare ( scan , dir , tuple , tupnatts , tupdesc , false ,
false , & continuescan ,
& least_sign_ikey ) )
return false ;
}
for ( int ikey = 0 ; ikey < so - > numberOfKeys ; ikey + + )
{
@ -1534,8 +1543,6 @@ _bt_advance_array_keys(IndexScanDesc scan, BTReadPageState *pstate,
if ( cur - > sk_flags & ( SK_BT_REQFWD | SK_BT_REQBKWD ) )
{
Assert ( sktrig_required ) ;
required = true ;
if ( cur - > sk_attno > tupnatts )
@ -1669,7 +1676,7 @@ _bt_advance_array_keys(IndexScanDesc scan, BTReadPageState *pstate,
}
else
{
Assert ( sktrig_required & & required ) ;
Assert ( required ) ;
/*
* This is a required non - array equality strategy scan key , which
@ -1711,7 +1718,7 @@ _bt_advance_array_keys(IndexScanDesc scan, BTReadPageState *pstate,
* be eliminated by _bt_preprocess_keys . It won ' t matter if some of
* our " true " array scan keys ( or even all of them ) are non - required .
*/
if ( required & &
if ( sktrig_required & & required & &
( ( ScanDirectionIsForward ( dir ) & & result > 0 ) | |
( ScanDirectionIsBackward ( dir ) & & result < 0 ) ) )
beyond_end_advance = true ;
@ -1726,7 +1733,7 @@ _bt_advance_array_keys(IndexScanDesc scan, BTReadPageState *pstate,
* array scan keys are considered interesting . )
*/
all_satisfied = false ;
if ( required )
if ( sktrig_required & & required )
all_required_satisfied = false ;
else
{
@ -1786,6 +1793,12 @@ _bt_advance_array_keys(IndexScanDesc scan, BTReadPageState *pstate,
* of any required scan key ) . All that matters is whether caller ' s tuple
* satisfies the new qual , so it ' s safe to just skip the _bt_check_compare
* recheck when we ' ve already determined that it can only return ' false ' .
*
* Note : In practice most scan keys are marked required by preprocessing ,
* if necessary by generating a preceding skip array . We nevertheless
* often handle array keys marked required as if they were nonrequired .
* This behavior is requested by our _bt_check_compare caller , though only
* when it is passed " forcenonrequired=true " by _bt_checkkeys .
*/
if ( ( sktrig_required & & all_required_satisfied ) | |
( ! sktrig_required & & all_satisfied ) )
@ -1796,9 +1809,9 @@ _bt_advance_array_keys(IndexScanDesc scan, BTReadPageState *pstate,
Assert ( all_required_satisfied ) ;
/* Recheck _bt_check_compare on behalf of caller */
if ( _bt_check_compare ( scan , dir , tuple , tupnatts , tupdesc ,
false , false , false ,
& continuescan , & nsktrig ) & &
if ( _bt_check_compare ( scan , dir , tuple , tupnatts , tupdesc , false ,
false , & continuescan ,
& nsktrig ) & &
! so - > scanBehind )
{
/* This tuple satisfies the new qual */
@ -2042,8 +2055,9 @@ new_prim_scan:
* read at least one leaf page before the one we ' re reading now . This
* makes primscan scheduling more efficient when scanning subsets of an
* index with many distinct attribute values matching many array elements .
* It encourages fewer , larger primitive scans where that makes sense
* ( where index descent costs need to be kept under control ) .
* It encourages fewer , larger primitive scans where that makes sense .
* This will in turn encourage _bt_readpage to apply the pstate . startikey
* optimization more often .
*
* Note : This heuristic isn ' t as aggressive as you might think . We ' re
* conservative about allowing a primitive scan to step from the first
@ -2200,17 +2214,14 @@ _bt_verify_keys_with_arraykeys(IndexScanDesc scan)
* the page to the right .
*
* Advances the scan ' s array keys when necessary for arrayKeys = true callers .
* Caller can avoid all array related side - effects when calling just to do a
* page continuescan precheck - - pass arrayKeys = false for that . Scans without
* any arrays keys must always pass arrayKeys = false .
* Scans without any array keys must always pass arrayKeys = false .
*
* Also stops and starts primitive index scans for arrayKeys = true callers .
* Scans with array keys are required to set up page state that helps us with
* this . The page ' s finaltup tuple ( the page high key for a forward scan , or
* the page ' s first non - pivot tuple for a backward scan ) must be set in
* pstate . finaltup ahead of the first call here for the page ( or possibly the
* first call after an initial continuescan - setting page precheck call ) . Set
* this to NULL for rightmost page ( or the leftmost page for backwards scans ) .
* pstate . finaltup ahead of the first call here for the page . Set this to
* NULL for rightmost page ( or the leftmost page for backwards scans ) .
*
* scan : index scan descriptor ( containing a search - type scankey )
* pstate : page level input and output parameters
@ -2225,42 +2236,46 @@ _bt_checkkeys(IndexScanDesc scan, BTReadPageState *pstate, bool arrayKeys,
TupleDesc tupdesc = RelationGetDescr ( scan - > indexRelation ) ;
BTScanOpaque so = ( BTScanOpaque ) scan - > opaque ;
ScanDirection dir = so - > currPos . dir ;
int ikey = 0 ;
int ikey = pstate - > startikey ;
bool res ;
Assert ( BTreeTupleGetNAtts ( tuple , scan - > indexRelation ) = = tupnatts ) ;
Assert ( ! so - > needPrimScan & & ! so - > scanBehind & & ! so - > oppositeDirCheck ) ;
Assert ( arrayKeys | | so - > numArrayKeys = = 0 ) ;
res = _bt_check_compare ( scan , dir , tuple , tupnatts , tupdesc ,
arrayKeys , pstate - > prechecked , pstate - > firstmatch ,
& pstate - > continuescan , & ikey ) ;
res = _bt_check_compare ( scan , dir , tuple , tupnatts , tupdesc , arrayKeys ,
pstate - > forcenonrequired , & pstate - > continuescan ,
& ikey ) ;
/*
* If _bt_check_compare relied on the pstate . startikey optimization , call
* again ( in assert - enabled builds ) to verify it didn ' t affect our answer .
*
* Note : we can ' t do this when ! pstate . forcenonrequired , since any arrays
* before pstate . startikey won ' t have advanced on this page at all .
*/
Assert ( ! pstate - > forcenonrequired | | arrayKeys ) ;
# ifdef USE_ASSERT_CHECKING
if ( ! arrayKeys & & so - > numArrayKeys )
if ( pstate - > startikey > 0 & & ! pstate - > forcenonrequired )
{
/*
* This is a continuescan precheck call for a scan with array keys .
*
* Assert that the scan isn ' t in danger of becoming confused .
*/
Assert ( ! so - > scanBehind & & ! so - > oppositeDirCheck ) ;
Assert ( ! pstate - > prechecked & & ! pstate - > firstmatch ) ;
Assert ( ! _bt_tuple_before_array_skeys ( scan , dir , tuple , tupdesc ,
tupnatts , false , 0 , NULL ) ) ;
}
if ( pstate - > prechecked | | pstate - > firstmatch )
{
bool dcontinuescan ;
bool dres ,
dcontinuescan ;
int dikey = 0 ;
/* Pass arrayKeys=false to avoid array side-effects */
dres = _bt_check_compare ( scan , dir , tuple , tupnatts , tupdesc , false ,
pstate - > forcenonrequired , & dcontinuescan ,
& dikey ) ;
Assert ( res = = dres ) ;
Assert ( pstate - > continuescan = = dcontinuescan ) ;
/*
* Call relied on continuescan / firstmatch prechecks - - assert that we
* get the same answer without those optimizations
* Should also get the same ikey result . We need a slightly weaker
* assertion during arrayKeys calls , since they might be using an
* array that couldn ' t be marked required during preprocessing .
*/
Assert ( res = = _bt_check_compare ( scan , dir , tuple , tupnatts , tupdesc ,
false , false , false ,
& dcontinuescan , & dikey ) ) ;
Assert ( pstate - > continuescan = = dcontinuescan ) ;
Assert ( arrayKeys | | ikey = = dikey ) ;
Assert ( ikey < = dikey ) ;
}
# endif
@ -2281,6 +2296,7 @@ _bt_checkkeys(IndexScanDesc scan, BTReadPageState *pstate, bool arrayKeys,
* It ' s also possible that the scan is still _before_ the _start_ of
* tuples matching the current set of array keys . Check for that first .
*/
Assert ( ! pstate - > forcenonrequired ) ;
if ( _bt_tuple_before_array_skeys ( scan , dir , tuple , tupdesc , tupnatts , true ,
ikey , NULL ) )
{
@ -2394,8 +2410,9 @@ _bt_oppodir_checkkeys(IndexScanDesc scan, ScanDirection dir,
Assert ( so - > numArrayKeys ) ;
_bt_check_compare ( scan , flipped , finaltup , nfinaltupatts , tupdesc ,
false , false , false , & continuescan , & ikey ) ;
_bt_check_compare ( scan , flipped , finaltup , nfinaltupatts , tupdesc , false ,
false , & continuescan ,
& ikey ) ;
if ( ! continuescan & & so - > keyData [ ikey ] . sk_strategy ! = BTEqualStrategyNumber )
return false ;
@ -2403,6 +2420,301 @@ _bt_oppodir_checkkeys(IndexScanDesc scan, ScanDirection dir,
return true ;
}
/*
* Determines an offset to the first scan key ( an so - > keyData [ ] - wise offset )
* that is _not_ guaranteed to be satisfied by every tuple from pstate . page ,
* which is set in pstate . startikey for _bt_checkkeys calls for the page .
* This allows caller to save cycles on comparisons of a prefix of keys while
* reading pstate . page .
*
* Also determines if later calls to _bt_checkkeys ( for pstate . page ) should be
* forced to treat all required scan keys > = pstate . startikey as nonrequired
* ( that is , if they ' re to be treated as if any SK_BT_REQFWD / SK_BT_REQBKWD
* markings that were set by preprocessing were not set at all , for the
* duration of _bt_checkkeys calls prior to the call for pstate . finaltup ) .
* This is indicated to caller by setting pstate . forcenonrequired .
*
* Call here at the start of reading a leaf page beyond the first one for the
* primitive index scan . We consider all non - pivot tuples , so it doesn ' t make
* sense to call here when only a subset of those tuples can ever be read .
* This is also a good idea on performance grounds ; not calling here when on
* the first page ( first for the current primitive scan ) avoids wasting cycles
* during selective point queries . They typically don ' t stand to gain as much
* when we can set pstate . startikey , and are likely to notice the overhead of
* calling here .
*
* Caller must reset startikey and forcenonrequired ahead of the _bt_checkkeys
* call for pstate . finaltup iff we set forcenonrequired = true . This will give
* _bt_checkkeys the opportunity to call _bt_advance_array_keys once more ,
* with sktrig_required = true , to advance the arrays that were ignored during
* checks of all of the page ' s prior tuples . Caller doesn ' t need to do this
* on the rightmost / leftmost page in the index ( where pstate . finaltup isn ' t
* set ) , since forcenonrequired won ' t be set here by us in the first place .
*/
void
_bt_set_startikey ( IndexScanDesc scan , BTReadPageState * pstate )
{
BTScanOpaque so = ( BTScanOpaque ) scan - > opaque ;
Relation rel = scan - > indexRelation ;
TupleDesc tupdesc = RelationGetDescr ( rel ) ;
ItemId iid ;
IndexTuple firsttup ,
lasttup ;
int startikey = 0 ,
arrayidx = 0 ,
firstchangingattnum ;
bool start_past_saop_eq = false ;
Assert ( ! so - > scanBehind ) ;
Assert ( pstate - > minoff < pstate - > maxoff ) ;
Assert ( ! pstate - > firstpage ) ;
Assert ( pstate - > startikey = = 0 ) ;
Assert ( ! so - > numArrayKeys | | pstate - > finaltup | |
P_RIGHTMOST ( BTPageGetOpaque ( pstate - > page ) ) | |
P_LEFTMOST ( BTPageGetOpaque ( pstate - > page ) ) ) ;
if ( so - > numberOfKeys = = 0 )
return ;
/* minoff is an offset to the lowest non-pivot tuple on the page */
iid = PageGetItemId ( pstate - > page , pstate - > minoff ) ;
firsttup = ( IndexTuple ) PageGetItem ( pstate - > page , iid ) ;
/* maxoff is an offset to the highest non-pivot tuple on the page */
iid = PageGetItemId ( pstate - > page , pstate - > maxoff ) ;
lasttup = ( IndexTuple ) PageGetItem ( pstate - > page , iid ) ;
/* Determine the first attribute whose values change on caller's page */
firstchangingattnum = _bt_keep_natts_fast ( rel , firsttup , lasttup ) ;
for ( ; startikey < so - > numberOfKeys ; startikey + + )
{
ScanKey key = so - > keyData + startikey ;
BTArrayKeyInfo * array ;
Datum firstdatum ,
lastdatum ;
bool firstnull ,
lastnull ;
int32 result ;
/*
* Determine if it ' s safe to set pstate . startikey to an offset to a
* key that comes after this key , by examining this key
*/
if ( ! ( key - > sk_flags & ( SK_BT_REQFWD | SK_BT_REQBKWD ) ) )
{
/* Scan key isn't marked required (corner case) */
Assert ( ! ( key - > sk_flags & SK_ROW_HEADER ) ) ;
break ; /* unsafe */
}
if ( key - > sk_flags & SK_ROW_HEADER )
{
/*
* Can ' t let pstate . startikey get set to an ikey beyond a
* RowCompare inequality
*/
break ; /* unsafe */
}
if ( key - > sk_strategy ! = BTEqualStrategyNumber )
{
/*
* Scalar inequality key .
*
* It ' s definitely safe for _bt_checkkeys to avoid assessing this
* inequality when the page ' s first and last non - pivot tuples both
* satisfy the inequality ( since the same must also be true of all
* the tuples in between these two ) .
*
* Unlike the " = " case , it doesn ' t matter if this attribute has
* more than one distinct value ( though it _is_ necessary for any
* and all _prior_ attributes to contain no more than one distinct
* value amongst all of the tuples from pstate . page ) .
*/
if ( key - > sk_attno > firstchangingattnum ) /* >, not >= */
break ; /* unsafe, preceding attr has multiple
* distinct values */
firstdatum = index_getattr ( firsttup , key - > sk_attno , tupdesc , & firstnull ) ;
lastdatum = index_getattr ( lasttup , key - > sk_attno , tupdesc , & lastnull ) ;
if ( key - > sk_flags & SK_ISNULL )
{
/* IS NOT NULL key */
Assert ( key - > sk_flags & SK_SEARCHNOTNULL ) ;
if ( firstnull | | lastnull )
break ; /* unsafe */
/* Safe, IS NOT NULL key satisfied by every tuple */
continue ;
}
/* Test firsttup */
if ( firstnull | |
! DatumGetBool ( FunctionCall2Coll ( & key - > sk_func ,
key - > sk_collation , firstdatum ,
key - > sk_argument ) ) )
break ; /* unsafe */
/* Test lasttup */
if ( lastnull | |
! DatumGetBool ( FunctionCall2Coll ( & key - > sk_func ,
key - > sk_collation , lastdatum ,
key - > sk_argument ) ) )
break ; /* unsafe */
/* Safe, scalar inequality satisfied by every tuple */
continue ;
}
/* Some = key (could be a a scalar = key, could be an array = key) */
Assert ( key - > sk_strategy = = BTEqualStrategyNumber ) ;
if ( ! ( key - > sk_flags & SK_SEARCHARRAY ) )
{
/*
* Scalar = key ( possibly an IS NULL key ) .
*
* It is unsafe to set pstate . startikey to an ikey beyond this
* key , unless the = key is satisfied by every possible tuple on
* the page ( possible only when attribute has just one distinct
* value among all tuples on the page ) .
*/
if ( key - > sk_attno > = firstchangingattnum )
break ; /* unsafe, multiple distinct attr values */
firstdatum = index_getattr ( firsttup , key - > sk_attno , tupdesc ,
& firstnull ) ;
if ( key - > sk_flags & SK_ISNULL )
{
/* IS NULL key */
Assert ( key - > sk_flags & SK_SEARCHNULL ) ;
if ( ! firstnull )
break ; /* unsafe */
/* Safe, IS NULL key satisfied by every tuple */
continue ;
}
if ( firstnull | |
! DatumGetBool ( FunctionCall2Coll ( & key - > sk_func ,
key - > sk_collation , firstdatum ,
key - > sk_argument ) ) )
break ; /* unsafe */
/* Safe, scalar = key satisfied by every tuple */
continue ;
}
/* = array key (could be a SAOP array, could be a skip array) */
array = & so - > arrayKeys [ arrayidx + + ] ;
Assert ( array - > scan_key = = startikey ) ;
if ( array - > num_elems ! = - 1 )
{
/*
* SAOP array = key .
*
* Handle this like we handle scalar = keys ( though binary search
* for a matching element , to avoid relying on key ' s sk_argument ) .
*/
if ( key - > sk_attno > = firstchangingattnum )
break ; /* unsafe, multiple distinct attr values */
firstdatum = index_getattr ( firsttup , key - > sk_attno , tupdesc ,
& firstnull ) ;
_bt_binsrch_array_skey ( & so - > orderProcs [ startikey ] ,
false , NoMovementScanDirection ,
firstdatum , firstnull , array , key ,
& result ) ;
if ( result ! = 0 )
break ; /* unsafe */
/* Safe, SAOP = key satisfied by every tuple */
start_past_saop_eq = true ;
continue ;
}
/*
* Skip array = key
*/
Assert ( key - > sk_flags & SK_BT_SKIP ) ;
if ( array - > null_elem )
{
/*
* Non - range skip array = key .
*
* Safe , non - range skip array " satisfied " by every tuple on page
* ( safe even when " key->sk_attno > firstchangingattnum " ) .
*/
continue ;
}
/*
* Range skip array = key .
*
* Handle this like we handle scalar inequality keys ( but avoid using
* key ' s sk_argument directly , as in the SAOP array case ) .
*/
if ( key - > sk_attno > firstchangingattnum ) /* >, not >= */
break ; /* unsafe, preceding attr has multiple
* distinct values */
firstdatum = index_getattr ( firsttup , key - > sk_attno , tupdesc , & firstnull ) ;
lastdatum = index_getattr ( lasttup , key - > sk_attno , tupdesc , & lastnull ) ;
/* Test firsttup */
_bt_binsrch_skiparray_skey ( false , ForwardScanDirection ,
firstdatum , firstnull , array , key ,
& result ) ;
if ( result ! = 0 )
break ; /* unsafe */
/* Test lasttup */
_bt_binsrch_skiparray_skey ( false , ForwardScanDirection ,
lastdatum , lastnull , array , key ,
& result ) ;
if ( result ! = 0 )
break ; /* unsafe */
/* Safe, range skip array satisfied by every tuple on page */
}
/*
* Use of forcenonrequired is typically undesirable , since it ' ll force
* _bt_readpage caller to read every tuple on the page - - even though , in
* general , it might well be possible to end the scan on an earlier tuple .
* However , caller must use forcenonrequired when start_past_saop_eq = true ,
* since the usual required array behavior might fail to roll over to the
* SAOP array .
*
* We always prefer forcenonrequired = true during scans with skip arrays
* ( except on the first page of each primitive index scan ) , though - - even
* when " startikey == 0 " . That way , _bt_advance_array_keys ' s low - order
* key precheck optimization can always be used ( unless on the first page
* of the scan ) . It seems slightly preferable to check more tuples when
* that allows us to do significantly less skip array maintenance .
*/
pstate - > forcenonrequired = ( start_past_saop_eq | | so - > skipScan ) ;
pstate - > startikey = startikey ;
/*
* _bt_readpage caller is required to call _bt_checkkeys against page ' s
* finaltup with forcenonrequired = false whenever we initially set
* forcenonrequired = true . That way the scan ' s arrays will reliably track
* its progress through the index ' s key space .
*
* We don ' t expect this when _bt_readpage caller has no finaltup due to
* its page being the rightmost ( or the leftmost , during backwards scans ) .
* When we see that _bt_readpage has no finaltup , back out of everything .
*/
Assert ( ! pstate - > forcenonrequired | | so - > numArrayKeys ) ;
if ( pstate - > forcenonrequired & & ! pstate - > finaltup )
{
pstate - > forcenonrequired = false ;
pstate - > startikey = 0 ;
}
}
/*
* Test whether an indextuple satisfies current scan condition .
*
@ -2432,23 +2744,33 @@ _bt_oppodir_checkkeys(IndexScanDesc scan, ScanDirection dir,
* by the current array key , or if they ' re truly unsatisfied ( that is , if
* they ' re unsatisfied by every possible array key ) .
*
* Though we advance non - required array keys on our own , that shouldn ' t have
* any lasting consequences for the scan . By definition , non - required arrays
* have no fixed relationship with the scan ' s progress . ( There are delicate
* considerations for non - required arrays when the arrays need to be advanced
* following our setting continuescan to false , but that doesn ' t concern us . )
*
* Pass advancenonrequired = false to avoid all array related side effects .
* This allows _bt_advance_array_keys caller to avoid infinite recursion .
*
* Pass forcenonrequired = true to instruct us to treat all keys as nonrequired .
* This is used to make it safe to temporarily stop properly maintaining the
* scan ' s required arrays . _bt_checkkeys caller ( _bt_readpage , actually )
* determines a prefix of keys that must satisfy every possible corresponding
* index attribute value from its page , which is passed to us via * ikey arg
* ( this is the first key that might be unsatisfied by tuples on the page ) .
* Obviously , we won ' t maintain any array keys from before * ikey , so it ' s
* quite possible for such arrays to " fall behind " the index ' s keyspace .
* Caller will need to " catch up " by passing forcenonrequired = true ( alongside
* an * ikey = 0 ) once the page ' s finaltup is reached .
*
* Note : it ' s safe to pass an * ikey > 0 with forcenonrequired = false , but only
* when caller determines that it won ' t affect array maintenance .
*/
static bool
_bt_check_compare ( IndexScanDesc scan , ScanDirection dir ,
IndexTuple tuple , int tupnatts , TupleDesc tupdesc ,
bool advancenonrequired , bool prechecked , bool firstmatch ,
bool advancenonrequired , bool forcenonrequired ,
bool * continuescan , int * ikey )
{
BTScanOpaque so = ( BTScanOpaque ) scan - > opaque ;
Assert ( ! forcenonrequired | | advancenonrequired ) ;
* continuescan = true ; /* default assumption */
for ( ; * ikey < so - > numberOfKeys ; ( * ikey ) + + )
@ -2461,36 +2783,20 @@ _bt_check_compare(IndexScanDesc scan, ScanDirection dir,
/*
* Check if the key is required in the current scan direction , in the
* opposite scan direction _only_ , or in neither direction
* opposite scan direction _only_ , or in neither direction ( except
* when we ' re forced to treat all scan keys as nonrequired )
*/
if ( ( ( key - > sk_flags & SK_BT_REQFWD ) & & ScanDirectionIsForward ( dir ) ) | |
( ( key - > sk_flags & SK_BT_REQBKWD ) & & ScanDirectionIsBackward ( dir ) ) )
if ( forcenonrequired )
{
/* treating scan's keys as non-required */
}
else if ( ( ( key - > sk_flags & SK_BT_REQFWD ) & & ScanDirectionIsForward ( dir ) ) | |
( ( key - > sk_flags & SK_BT_REQBKWD ) & & ScanDirectionIsBackward ( dir ) ) )
requiredSameDir = true ;
else if ( ( ( key - > sk_flags & SK_BT_REQFWD ) & & ScanDirectionIsBackward ( dir ) ) | |
( ( key - > sk_flags & SK_BT_REQBKWD ) & & ScanDirectionIsForward ( dir ) ) )
requiredOppositeDirOnly = true ;
/*
* If the caller told us the * continuescan flag is known to be true
* for the last item on the page , then we know the keys required for
* the current direction scan should be matched . Otherwise , the
* * continuescan flag would be set for the current item and
* subsequently the last item on the page accordingly .
*
* If the key is required for the opposite direction scan , we can skip
* the check if the caller tells us there was already at least one
* matching item on the page . Also , we require the * continuescan flag
* to be true for the last item on the page to know there are no
* NULLs .
*
* Both cases above work except for the row keys , where NULLs could be
* found in the middle of matching values .
*/
if ( prechecked & &
( requiredSameDir | | ( requiredOppositeDirOnly & & firstmatch ) ) & &
! ( key - > sk_flags & SK_ROW_HEADER ) )
continue ;
if ( key - > sk_attno > tupnatts )
{
/*
@ -2512,6 +2818,16 @@ _bt_check_compare(IndexScanDesc scan, ScanDirection dir,
{
Assert ( key - > sk_flags & SK_SEARCHARRAY ) ;
Assert ( key - > sk_flags & SK_BT_SKIP ) ;
Assert ( requiredSameDir | | forcenonrequired ) ;
/*
* Cannot fall back on _bt_tuple_before_array_skeys when we ' re
* treating the scan ' s keys as nonrequired , though . Just handle
* this like any other non - required equality - type array key .
*/
if ( forcenonrequired )
return _bt_advance_array_keys ( scan , NULL , tuple , tupnatts ,
tupdesc , * ikey , false ) ;
* continuescan = false ;
return false ;
@ -2521,7 +2837,7 @@ _bt_check_compare(IndexScanDesc scan, ScanDirection dir,
if ( key - > sk_flags & SK_ROW_HEADER )
{
if ( _bt_check_rowcompare ( key , tuple , tupnatts , tupdesc , dir ,
continuescan ) )
forcenonrequired , continuescan ) )
continue ;
return false ;
}
@ -2554,9 +2870,20 @@ _bt_check_compare(IndexScanDesc scan, ScanDirection dir,
*/
if ( requiredSameDir )
* continuescan = false ;
else if ( unlikely ( key - > sk_flags & SK_BT_SKIP ) )
{
/*
* If we ' re treating scan keys as nonrequired , and encounter a
* skip array scan key whose current element is NULL , then it
* must be a non - range skip array . It must be satisfied , so
* there ' s no need to call _bt_advance_array_keys to check .
*/
Assert ( forcenonrequired & & * ikey > 0 ) ;
continue ;
}
/*
* In any case , this indextuple doesn ' t match the qual .
* T his indextuple doesn ' t match the qual .
*/
return false ;
}
@ -2577,7 +2904,7 @@ _bt_check_compare(IndexScanDesc scan, ScanDirection dir,
* ( _bt_advance_array_keys also relies on this behavior during
* forward scans . )
*/
if ( ( key - > sk_flags & ( SK_BT_REQFWD | SK_BT_REQBKWD ) ) & &
if ( ( requiredSameDir | | requiredOppositeDirOnly ) & &
ScanDirectionIsBackward ( dir ) )
* continuescan = false ;
}
@ -2595,7 +2922,7 @@ _bt_check_compare(IndexScanDesc scan, ScanDirection dir,
* ( _bt_advance_array_keys also relies on this behavior during
* backward scans . )
*/
if ( ( key - > sk_flags & ( SK_BT_REQFWD | SK_BT_REQBKWD ) ) & &
if ( ( requiredSameDir | | requiredOppositeDirOnly ) & &
ScanDirectionIsForward ( dir ) )
* continuescan = false ;
}
@ -2606,15 +2933,7 @@ _bt_check_compare(IndexScanDesc scan, ScanDirection dir,
return false ;
}
/*
* Apply the key - checking function , though only if we must .
*
* When a key is required in the opposite - of - scan direction _only_ ,
* then it must already be satisfied if firstmatch = true indicates that
* an earlier tuple from this same page satisfied it earlier on .
*/
if ( ! ( requiredOppositeDirOnly & & firstmatch ) & &
! DatumGetBool ( FunctionCall2Coll ( & key - > sk_func , key - > sk_collation ,
if ( ! DatumGetBool ( FunctionCall2Coll ( & key - > sk_func , key - > sk_collation ,
datum , key - > sk_argument ) ) )
{
/*
@ -2664,7 +2983,8 @@ _bt_check_compare(IndexScanDesc scan, ScanDirection dir,
*/
static bool
_bt_check_rowcompare ( ScanKey skey , IndexTuple tuple , int tupnatts ,
TupleDesc tupdesc , ScanDirection dir , bool * continuescan )
TupleDesc tupdesc , ScanDirection dir ,
bool forcenonrequired , bool * continuescan )
{
ScanKey subkey = ( ScanKey ) DatumGetPointer ( skey - > sk_argument ) ;
int32 cmpresult = 0 ;
@ -2704,7 +3024,11 @@ _bt_check_rowcompare(ScanKey skey, IndexTuple tuple, int tupnatts,
if ( isNull )
{
if ( subkey - > sk_flags & SK_BT_NULLS_FIRST )
if ( forcenonrequired )
{
/* treating scan's keys as non-required */
}
else if ( subkey - > sk_flags & SK_BT_NULLS_FIRST )
{
/*
* Since NULLs are sorted before non - NULLs , we know we have
@ -2758,8 +3082,12 @@ _bt_check_rowcompare(ScanKey skey, IndexTuple tuple, int tupnatts,
*/
Assert ( subkey ! = ( ScanKey ) DatumGetPointer ( skey - > sk_argument ) ) ;
subkey - - ;
if ( ( subkey - > sk_flags & SK_BT_REQFWD ) & &
ScanDirectionIsForward ( dir ) )
if ( forcenonrequired )
{
/* treating scan's keys as non-required */
}
else if ( ( subkey - > sk_flags & SK_BT_REQFWD ) & &
ScanDirectionIsForward ( dir ) )
* continuescan = false ;
else if ( ( subkey - > sk_flags & SK_BT_REQBKWD ) & &
ScanDirectionIsBackward ( dir ) )
@ -2811,7 +3139,7 @@ _bt_check_rowcompare(ScanKey skey, IndexTuple tuple, int tupnatts,
break ;
}
if ( ! result )
if ( ! result & & ! forcenonrequired )
{
/*
* Tuple fails this qual . If it ' s a required qual for the current
@ -2855,6 +3183,8 @@ _bt_checkkeys_look_ahead(IndexScanDesc scan, BTReadPageState *pstate,
OffsetNumber aheadoffnum ;
IndexTuple ahead ;
Assert ( ! pstate - > forcenonrequired ) ;
/* Avoid looking ahead when comparing the page high key */
if ( pstate - > offnum < pstate - > minoff )
return ;