@ -20,504 +20,553 @@
# include "miscadmin.h"
# include "pgstat.h"
# include "storage/bufmgr.h"
# include "utils/builtins.h"
# include "utils/memutils.h"
static OffsetNumber gistfindnext ( IndexScanDesc scan , OffsetNumber n ) ;
static int64 gistnext ( IndexScanDesc scan , TIDBitmap * tbm ) ;
static bool gistindex_keytest ( IndexTuple tuple , IndexScanDesc scan ,
OffsetNumber offset ) ;
static void
killtuple ( Relation r , GISTScanOpaque so , ItemPointer iptr )
/*
* gistindex_keytest ( ) - - does this index tuple satisfy the scan key ( s ) ?
*
* The index tuple might represent either a heap tuple or a lower index page ,
* depending on whether the containing page is a leaf page or not .
*
* On success return for a heap tuple , * recheck_p is set to indicate
* whether recheck is needed . We recheck if any of the consistent ( ) functions
* request it . recheck is not interesting when examining a non - leaf entry ,
* since we must visit the lower index page if there ' s any doubt .
*
* If we are doing an ordered scan , so - > distances [ ] is filled with distance
* data from the distance ( ) functions before returning success .
*
* We must decompress the key in the IndexTuple before passing it to the
* sk_funcs ( which actually are the opclass Consistent or Distance methods ) .
*
* Note that this function is always invoked in a short - lived memory context ,
* so we don ' t need to worry about cleaning up allocated memory , either here
* or in the implementation of any Consistent or Distance methods .
*/
static bool
gistindex_keytest ( IndexScanDesc scan ,
IndexTuple tuple ,
Page page ,
OffsetNumber offset ,
bool * recheck_p )
{
Page p ;
OffsetNumber offset ;
GISTScanOpaque so = ( GISTScanOpaque ) scan - > opaque ;
GISTSTATE * giststate = so - > giststate ;
ScanKey key = scan - > keyData ;
int keySize = scan - > numberOfKeys ;
double * distance_p ;
Relation r = scan - > indexRelation ;
LockBuffer ( so - > curbuf , GIST_SHARE ) ;
gistcheckpage ( r , so - > curbuf ) ;
p = ( Page ) BufferGetPage ( so - > curbuf ) ;
* recheck_p = false ;
if ( XLByteEQ ( so - > stack - > lsn , PageGetLSN ( p ) ) )
/*
* If it ' s a leftover invalid tuple from pre - 9.1 , treat it as a match
* with minimum possible distances . This means we ' ll always follow it
* to the referenced page .
*/
if ( GistTupleIsInvalid ( tuple ) )
{
/* page unchanged, so all is simple */
offset = ItemPointerGetOffsetNumber ( iptr ) ;
ItemIdMarkDead ( PageGetItemId ( p , offset ) ) ;
SetBufferCommitInfoNeedsSave ( so - > curbuf ) ;
int i ;
for ( i = 0 ; i < scan - > numberOfOrderBys ; i + + )
so - > distances [ i ] = - get_float8_infinity ( ) ;
* recheck_p = true ; /* probably unnecessary */
return true ;
}
else
/* Check whether it matches according to the Consistent functions */
while ( keySize > 0 )
{
OffsetNumber maxoff = PageGetMaxOffsetNumber ( p ) ;
Datum datum ;
bool isNull ;
for ( offset = FirstOffsetNumber ; offset < = maxoff ; offset = OffsetNumberNext ( offset ) )
{
IndexTuple ituple = ( IndexTuple ) PageGetItem ( p , PageGetItemId ( p , offset ) ) ;
datum = index_getattr ( tuple ,
key - > sk_attno ,
giststate - > tupdesc ,
& isNull ) ;
if ( ItemPointerEquals ( & ( ituple - > t_tid ) , iptr ) )
if ( key - > sk_flags & SK_ISNULL )
{
/*
* On non - leaf page we can ' t conclude that child hasn ' t NULL
* values because of assumption in GiST : union ( VAL , NULL ) is VAL .
* But if on non - leaf page key IS NULL , then all children are
* NULL .
*/
if ( key - > sk_flags & SK_SEARCHNULL )
{
/* found */
ItemIdMarkDead ( PageGetItemId ( p , offset ) ) ;
SetBufferCommitInfoNeedsSave ( so - > curbuf ) ;
break ;
if ( GistPageIsLeaf ( page ) & & ! isNull )
return false ;
}
else
{
Assert ( key - > sk_flags & SK_SEARCHNOTNULL ) ;
if ( isNull )
return false ;
}
}
}
else if ( isNull )
{
return false ;
}
else
{
Datum test ;
bool recheck ;
GISTENTRY de ;
LockBuffer ( so - > curbuf , GIST_UNLOCK ) ;
}
gistdentryinit ( giststate , key - > sk_attno - 1 , & de ,
datum , r , page , offset ,
FALSE , isNull ) ;
/*
* gistgettuple ( ) - - Get the next tuple in the scan
*/
Datum
gistgettuple ( PG_FUNCTION_ARGS )
{
IndexScanDesc scan = ( IndexScanDesc ) PG_GETARG_POINTER ( 0 ) ;
ScanDirection dir = ( ScanDirection ) PG_GETARG_INT32 ( 1 ) ;
GISTScanOpaque so ;
bool res ;
/*
* Call the Consistent function to evaluate the test . The
* arguments are the index datum ( as a GISTENTRY * ) , the comparison
* datum , the comparison operator ' s strategy number and subtype
* from pg_amop , and the recheck flag .
*
* ( Presently there ' s no need to pass the subtype since it ' ll
* always be zero , but might as well pass it for possible future
* use . )
*
* We initialize the recheck flag to true ( the safest assumption )
* in case the Consistent function forgets to set it .
*/
recheck = true ;
so = ( GISTScanOpaque ) scan - > opaque ;
test = FunctionCall5 ( & key - > sk_func ,
PointerGetDatum ( & de ) ,
key - > sk_argument ,
Int32GetDatum ( key - > sk_strategy ) ,
ObjectIdGetDatum ( key - > sk_subtype ) ,
PointerGetDatum ( & recheck ) ) ;
if ( dir ! = ForwardScanDirection )
elog ( ERROR , " GiST doesn't support other scan directions than forward " ) ;
if ( ! DatumGetBool ( test ) )
return false ;
* recheck_p | = recheck ;
}
/*
* If we have produced an index tuple in the past and the executor has
* informed us we need to mark it as " killed " , do so now .
*/
if ( scan - > kill_prior_tuple & & ItemPointerIsValid ( & ( so - > curpos ) ) )
killtuple ( scan - > indexRelation , so , & ( so - > curpos ) ) ;
key + + ;
keySize - - ;
}
/*
* Get the next tuple that matches the search key .
*/
res = ( gistnext ( scan , NULL ) > 0 ) ;
/* OK, it passes --- now let's compute the distances */
key = scan - > orderByData ;
distance_p = so - > distances ;
keySize = scan - > numberOfOrderBys ;
while ( keySize > 0 )
{
Datum datum ;
bool isNull ;
PG_RETURN_BOOL ( res ) ;
}
datum = index_getattr ( tuple ,
key - > sk_attno ,
giststate - > tupdesc ,
& isNull ) ;
Datum
gistgetbitmap ( PG_FUNCTION_ARGS )
{
IndexScanDesc scan = ( IndexScanDesc ) PG_GETARG_POINTER ( 0 ) ;
TIDBitmap * tbm = ( TIDBitmap * ) PG_GETARG_POINTER ( 1 ) ;
int64 ntids ;
if ( ( key - > sk_flags & SK_ISNULL ) | | isNull )
{
/* Assume distance computes as null and sorts to the end */
* distance_p = get_float8_infinity ( ) ;
}
else
{
Datum dist ;
GISTENTRY de ;
ntids = gistnext ( scan , tbm ) ;
gistdentryinit ( giststate , key - > sk_attno - 1 , & de ,
datum , r , page , offset ,
FALSE , isNull ) ;
PG_RETURN_INT64 ( ntids ) ;
/*
* Call the Distance function to evaluate the distance . The
* arguments are the index datum ( as a GISTENTRY * ) , the comparison
* datum , and the ordering operator ' s strategy number and subtype
* from pg_amop .
*
* ( Presently there ' s no need to pass the subtype since it ' ll
* always be zero , but might as well pass it for possible future
* use . )
*
* Note that Distance functions don ' t get a recheck argument .
* We can ' t tolerate lossy distance calculations on leaf tuples ;
* there is no opportunity to re - sort the tuples afterwards .
*/
dist = FunctionCall4 ( & key - > sk_func ,
PointerGetDatum ( & de ) ,
key - > sk_argument ,
Int32GetDatum ( key - > sk_strategy ) ,
ObjectIdGetDatum ( key - > sk_subtype ) ) ;
* distance_p = DatumGetFloat8 ( dist ) ;
}
key + + ;
distance_p + + ;
keySize - - ;
}
return true ;
}
/*
* Fetch tuple ( s ) that match the search key ; this can be invoked
* either to fetch the first such tuple or subsequent matching tuples .
* Scan all items on the GiST index page identified by * pageItem , and insert
* them into the queue ( or directly to output areas )
*
* This function is used by both gistgettuple and gistgetbitmap . When
* invoked from gistgettuple , tbm is null and the next matching tuple
* is returned in scan - > xs_ctup . t_self . When invoked from getbitmap ,
* tbm is non - null and all matching tuples are added to tbm before
* returning . In both cases , the function result is the number of
* returned tuples .
* scan : index scan we are executing
* pageItem : search queue item identifying an index page to scan
* myDistances : distances array associated with pageItem , or NULL at the root
* tbm : if not NULL , gistgetbitmap ' s output bitmap
* ntids : if not NULL , gistgetbitmap ' s output tuple counter
*
* If scan specifies to skip killed tuples , continue looping until we find a
* non - killed tuple that matches the search key .
* If tbm / ntids aren ' t NULL , we are doing an amgetbitmap scan , and heap
* tuples should be reported directly into the bitmap . If they are NULL ,
* we ' re doing a plain or ordered indexscan . For a plain indexscan , heap
* tuple TIDs are returned into so - > pageData [ ] . For an ordered indexscan ,
* heap tuple TIDs are pushed into individual search queue items .
*
* If we detect that the index page has split since we saw its downlink
* in the parent , we push its new right sibling onto the queue so the
* sibling will be processed next .
*/
static int64
gistnext ( IndexScanDesc scan , TIDBitmap * tbm )
static void
gistScanPage ( IndexScanDesc scan , GISTSearchItem * pageItem , double * myDistances ,
TIDBitmap * tbm , int64 * ntids )
{
Page p ;
OffsetNumber n ;
GISTScanOpaque so ;
GISTSearchStack * stk ;
IndexTuple it ;
GISTScanOpaque so = ( GISTScanOpaque ) scan - > opaque ;
Buffer buffer ;
Page page ;
GISTPageOpaque opaque ;
int64 ntids = 0 ;
OffsetNumber maxoff ;
OffsetNumber i ;
GISTSearchTreeItem * tmpItem = so - > tmpTreeItem ;
bool isNew ;
MemoryContext oldcxt ;
so = ( GISTScanOpaque ) scan - > opaque ;
Assert ( ! GISTSearchItemIsHeap ( * pageItem ) ) ;
if ( so - > qual_ok = = false )
return 0 ;
buffer = ReadBuffer ( scan - > indexRelation , pageItem - > blkno ) ;
LockBuffer ( buffer , GIST_SHARE ) ;
gistcheckpage ( scan - > indexRelation , buffer ) ;
page = BufferGetPage ( buffer ) ;
opaque = GistPageGetOpaque ( page ) ;
if ( so - > curbuf = = InvalidBuffer )
/* check if page split occurred since visit to parent */
if ( ! XLogRecPtrIsInvalid ( pageItem - > data . parentlsn ) & &
XLByteLT ( pageItem - > data . parentlsn , opaque - > nsn ) & &
opaque - > rightlink ! = InvalidBlockNumber /* sanity check */ )
{
if ( ItemPointerIsValid ( & so - > curpos ) = = false )
{
/* Being asked to fetch the first entry, so start at the root */
Assert ( so - > curbuf = = InvalidBuffer ) ;
Assert ( so - > stack = = NULL ) ;
/* There was a page split, follow right link to add pages */
GISTSearchItem * item ;
so - > curbuf = ReadBuffer ( scan - > indexRelation , GIST_ROOT_BLKNO ) ;
/* This can't happen when starting at the root */
Assert ( myDistances ! = NULL ) ;
stk = so - > stack = ( GISTSearchStack * ) palloc0 ( sizeof ( GISTSearchStack ) ) ;
oldcxt = MemoryContextSwitchTo ( so - > queueCxt ) ;
stk - > next = NULL ;
stk - > block = GIST_ROOT_BLKNO ;
/* Create new GISTSearchItem for the right sibling index page */
item = palloc ( sizeof ( GISTSearchItem ) ) ;
item - > next = NULL ;
item - > blkno = opaque - > rightlink ;
item - > data . parentlsn = pageItem - > data . parentlsn ;
pgstat_count_index_scan ( scan - > indexRelation ) ;
}
else
{
/* scan is finished */
return 0 ;
}
/* Insert it into the queue using same distances as for this page */
tmpItem - > head = item ;
tmpItem - > lastHeap = NULL ;
memcpy ( tmpItem - > distances , myDistances ,
sizeof ( double ) * scan - > numberOfOrderBys ) ;
( void ) rb_insert ( so - > queue , ( RBNode * ) tmpItem , & isNew ) ;
MemoryContextSwitchTo ( oldcxt ) ;
}
so - > nPageData = so - > curPageData = 0 ;
/*
* check stored pointers from last visit
* check all tuples on page
*/
if ( so - > nPageData > 0 )
maxoff = PageGetMaxOffsetNumber ( page ) ;
for ( i = FirstOffsetNumber ; i < = maxoff ; i = OffsetNumberNext ( i ) )
{
IndexTuple it = ( IndexTuple ) PageGetItem ( page , PageGetItemId ( page , i ) ) ;
bool match ;
bool recheck ;
/*
* gistgetmulti never should go here
* Must call gistindex_keytest in tempCxt , and clean up any leftover
* junk afterward .
*/
Assert ( tbm = = NULL ) ;
oldcxt = MemoryContextSwitchTo ( so - > tempCxt ) ;
if ( so - > curPageData < so - > nPageData )
{
scan - > xs_ctup . t_self = so - > pageData [ so - > curPageData ] . heapPtr ;
scan - > xs_recheck = so - > pageData [ so - > curPageData ] . recheck ;
match = gistindex_keytest ( scan , it , page , i , & recheck ) ;
ItemPointerSet ( & so - > curpos ,
BufferGetBlockNumber ( so - > curbuf ) ,
so - > pageData [ so - > curPageData ] . pageOffset ) ;
MemoryContextSwitchTo ( oldcxt ) ;
MemoryContextReset ( so - > tempCxt ) ;
so - > curPageData + + ;
/* Ignore tuple if it doesn't match */
if ( ! match )
continue ;
return 1 ;
if ( tbm & & GistPageIsLeaf ( page ) )
{
/*
* getbitmap scan , so just push heap tuple TIDs into the bitmap
* without worrying about ordering
*/
tbm_add_tuples ( tbm , & it - > t_tid , 1 , recheck ) ;
( * ntids ) + + ;
}
else if ( scan - > numberOfOrderBys = = 0 & & GistPageIsLeaf ( page ) )
{
/*
* Non - ordered scan , so report heap tuples in so - > pageData [ ]
*/
so - > pageData [ so - > nPageData ] . heapPtr = it - > t_tid ;
so - > pageData [ so - > nPageData ] . recheck = recheck ;
so - > nPageData + + ;
}
else
{
/*
* Go to the next page
* Must push item into search queue . We get here for any lower
* index page , and also for heap tuples if doing an ordered
* search .
*/
stk = so - > stack - > next ;
pfree ( so - > stack ) ;
so - > stack = stk ;
GISTSearchItem * item ;
oldcxt = MemoryContextSwitchTo ( so - > queueCxt ) ;
/* If we're out of stack entries, we're done */
if ( so - > stack = = NULL )
/* Create new GISTSearchItem for this item */
item = palloc ( sizeof ( GISTSearchItem ) ) ;
item - > next = NULL ;
if ( GistPageIsLeaf ( page ) )
{
ReleaseBuffer ( so - > curbuf ) ;
so - > curbuf = InvalidBuffer ;
return 0 ;
/* Creating heap-tuple GISTSearchItem */
item - > blkno = InvalidBlockNumber ;
item - > data . heap . heapPtr = it - > t_tid ;
item - > data . heap . recheck = recheck ;
}
else
{
/* Creating index-page GISTSearchItem */
item - > blkno = ItemPointerGetBlockNumber ( & it - > t_tid ) ;
/* lsn of current page is lsn of parent page for child */
item - > data . parentlsn = PageGetLSN ( page ) ;
}
/* Insert it into the queue using new distance data */
tmpItem - > head = item ;
tmpItem - > lastHeap = GISTSearchItemIsHeap ( * item ) ? item : NULL ;
memcpy ( tmpItem - > distances , so - > distances ,
sizeof ( double ) * scan - > numberOfOrderBys ) ;
so - > curbuf = ReleaseAndReadBuffer ( so - > curbuf ,
scan - > indexRelation ,
stk - > block ) ;
( void ) rb_insert ( so - > queue , ( RBNode * ) tmpItem , & isNew ) ;
MemoryContextSwitchTo ( oldcxt ) ;
}
}
UnlockReleaseBuffer ( buffer ) ;
}
/*
* Extract next item ( in order ) from search queue
*
* Returns a GISTSearchItem or NULL . Caller must pfree item when done with it .
*
* NOTE : on successful return , so - > curTreeItem is the GISTSearchTreeItem that
* contained the result item . Callers can use so - > curTreeItem - > distances as
* the distances value for the item .
*/
static GISTSearchItem *
getNextGISTSearchItem ( GISTScanOpaque so )
{
for ( ; ; )
{
CHECK_FOR_INTERRUPTS ( ) ;
GISTSearchItem * item ;
/* First of all, we need lock buffer */
Assert ( so - > curbuf ! = InvalidBuffer ) ;
LockBuffer ( so - > curbuf , GIST_SHARE ) ;
gistcheckpage ( scan - > indexRelation , so - > curbuf ) ;
p = BufferGetPage ( so - > curbuf ) ;
opaque = GistPageGetOpaque ( p ) ;
/* remember lsn to identify page changed for tuple's killing */
so - > stack - > lsn = PageGetLSN ( p ) ;
/* check page split, occured since visit to parent */
if ( ! XLogRecPtrIsInvalid ( so - > stack - > parentlsn ) & &
XLByteLT ( so - > stack - > parentlsn , opaque - > nsn ) & &
opaque - > rightlink ! = InvalidBlockNumber /* sanity check */ & &
( so - > stack - > next = = NULL | | so - > stack - > next - > block ! = opaque - > rightlink ) /* check if already
added */ )
/* Update curTreeItem if we don't have one */
if ( so - > curTreeItem = = NULL )
{
/* detect page split, follow right link to add pages */
stk = ( GISTSearchStack * ) palloc ( sizeof ( GISTSearchStack ) ) ;
stk - > next = so - > stack - > next ;
stk - > block = opaque - > rightlink ;
stk - > parentlsn = so - > stack - > parentlsn ;
memset ( & ( stk - > lsn ) , 0 , sizeof ( GistNSN ) ) ;
so - > stack - > next = stk ;
so - > curTreeItem = ( GISTSearchTreeItem * ) rb_leftmost ( so - > queue ) ;
/* Done when tree is empty */
if ( so - > curTreeItem = = NULL )
break ;
}
/* if page is empty, then just skip it */
if ( PageIsEmpty ( p ) )
item = so - > curTreeItem - > head ;
if ( item ! = NULL )
{
LockBuffer ( so - > curbuf , GIST_UNLOCK ) ;
stk = so - > stack - > next ;
pfree ( so - > stack ) ;
so - > stack = stk ;
if ( so - > stack = = NULL )
{
ReleaseBuffer ( so - > curbuf ) ;
so - > curbuf = InvalidBuffer ;
return ntids ;
}
so - > curbuf = ReleaseAndReadBuffer ( so - > curbuf , scan - > indexRelation ,
stk - > block ) ;
continue ;
/* Delink item from chain */
so - > curTreeItem - > head = item - > next ;
/* Return item; caller is responsible to pfree it */
return item ;
}
n = FirstOffsetNumber ;
/* wonderful, we can look at page */
so - > nPageData = so - > curPageData = 0 ;
for ( ; ; )
{
n = gistfindnext ( scan , n ) ;
if ( ! OffsetNumberIsValid ( n ) )
{
/*
* If we was called from gistgettuple and current buffer
* contains something matched then make a recursive call - it
* will return ItemPointer from so - > pageData . But we save
* buffer pinned to support tuple ' s killing
*/
if ( ! tbm & & so - > nPageData > 0 )
{
LockBuffer ( so - > curbuf , GIST_UNLOCK ) ;
return gistnext ( scan , NULL ) ;
}
/* curTreeItem is exhausted, so remove it from rbtree */
rb_delete ( so - > queue , ( RBNode * ) so - > curTreeItem ) ;
so - > curTreeItem = NULL ;
}
/*
* We ran out of matching index entries on the current page ,
* so pop the top stack entry and use it to continue the
* search .
*/
LockBuffer ( so - > curbuf , GIST_UNLOCK ) ;
stk = so - > stack - > next ;
pfree ( so - > stack ) ;
so - > stack = stk ;
/* If we're out of stack entries, we're done */
if ( so - > stack = = NULL )
{
ReleaseBuffer ( so - > curbuf ) ;
so - > curbuf = InvalidBuffer ;
return ntids ;
}
so - > curbuf = ReleaseAndReadBuffer ( so - > curbuf ,
scan - > indexRelation ,
stk - > block ) ;
/* XXX go up */
break ;
}
return NULL ;
}
if ( GistPageIsLeaf ( p ) )
{
/*
* We ' ve found a matching index entry in a leaf page , so
* return success . Note that we keep " curbuf " pinned so that
* we can efficiently resume the index scan later .
*/
/*
* Fetch next heap tuple in an ordered search
*/
static bool
getNextNearest ( IndexScanDesc scan )
{
GISTScanOpaque so = ( GISTScanOpaque ) scan - > opaque ;
bool res = false ;
if ( ! ( scan - > ignore_killed_tuples & &
ItemIdIsDead ( PageGetItemId ( p , n ) ) ) )
{
it = ( IndexTuple ) PageGetItem ( p , PageGetItemId ( p , n ) ) ;
ntids + + ;
if ( tbm ! = NULL )
tbm_add_tuples ( tbm , & it - > t_tid , 1 , scan - > xs_recheck ) ;
else
{
so - > pageData [ so - > nPageData ] . heapPtr = it - > t_tid ;
so - > pageData [ so - > nPageData ] . pageOffset = n ;
so - > pageData [ so - > nPageData ] . recheck = scan - > xs_recheck ;
so - > nPageData + + ;
}
}
}
else
{
/*
* We ' ve found an entry in an internal node whose key is
* consistent with the search key , so push it to stack
*/
stk = ( GISTSearchStack * ) palloc ( sizeof ( GISTSearchStack ) ) ;
do
{
GISTSearchItem * item = getNextGISTSearchItem ( so ) ;
it = ( IndexTuple ) PageGetItem ( p , PageGetItemId ( p , n ) ) ;
stk - > block = ItemPointerGetBlockNumber ( & ( it - > t_tid ) ) ;
memset ( & ( stk - > lsn ) , 0 , sizeof ( GistNSN ) ) ;
stk - > parentlsn = so - > stack - > lsn ;
if ( ! item )
break ;
stk - > next = so - > stack - > next ;
so - > stack - > next = stk ;
}
if ( GISTSearchItemIsHeap ( * item ) )
{
/* found a heap item at currently minimal distance */
scan - > xs_ctup . t_self = item - > data . heap . heapPtr ;
scan - > xs_recheck = item - > data . heap . recheck ;
res = true ;
}
else
{
/* visit an index page, extract its items into queue */
CHECK_FOR_INTERRUPTS ( ) ;
n = OffsetNumberNext ( n ) ;
gistScanPage ( scan , item , so - > curTreeItem - > distances , NULL , NULL ) ;
}
}
return ntids ;
pfree ( item ) ;
} while ( ! res ) ;
return res ;
}
/*
* gistindex_keytest ( ) - - does this index tuple satisfy the scan key ( s ) ?
*
* On success return for a leaf tuple , scan - > xs_recheck is set to indicate
* whether recheck is needed . We recheck if any of the consistent ( ) functions
* request it .
*
* We must decompress the key in the IndexTuple before passing it to the
* sk_func ( and we have previously overwritten the sk_func to use the
* user - defined Consistent method , so we actually are invoking that ) .
*
* Note that this function is always invoked in a short - lived memory context ,
* so we don ' t need to worry about cleaning up allocated memory , either here
* or in the implementation of any Consistent methods .
* gistgettuple ( ) - - Get the next tuple in the scan
*/
static bool
gistindex_keytest ( IndexTuple tuple ,
IndexScanDesc scan ,
OffsetNumber offset )
Datum
gistgettuple ( PG_FUNCTION_ARGS )
{
int keySize = scan - > numberOfKeys ;
ScanKey key = scan - > keyData ;
Relation r = scan - > indexRelation ;
GISTScanOpaque so ;
Page p ;
GISTSTATE * giststate ;
IndexScanDesc scan = ( IndexScanDesc ) PG_GETARG_POINTER ( 0 ) ;
GISTScanOpaque so = ( GISTScanOpaque ) scan - > opaque ;
so = ( GISTScanOpaque ) scan - > opaque ;
giststate = so - > giststate ;
p = BufferGetPage ( so - > curbuf ) ;
if ( ! so - > qual_ok )
PG_RETURN_BOOL ( false ) ;
scan - > xs_recheck = false ;
if ( so - > firstCall )
{
/* Begin the scan by processing the root page */
GISTSearchItem fakeItem ;
/*
* Tuple doesn ' t restore after crash recovery because of incomplete insert
*/
if ( ! GistPageIsLeaf ( p ) & & GistTupleIsInvalid ( tuple ) )
return true ;
pgstat_count_index_scan ( scan - > indexRelation ) ;
while ( keySize > 0 )
{
Datum datum ;
bool isNull ;
Datum test ;
bool recheck ;
GISTENTRY de ;
so - > firstCall = false ;
so - > curTreeItem = NULL ;
so - > curPageData = so - > nPageData = 0 ;
datum = index_getattr ( tuple ,
key - > sk_attno ,
giststate - > tupdesc ,
& isNull ) ;
fakeItem . blkno = GIST_ROOT_BLKNO ;
memset ( & fakeItem . data . parentlsn , 0 , sizeof ( GistNSN ) ) ;
gistScanPage ( scan , & fakeItem , NULL , NULL , NULL ) ;
}
if ( key - > sk_flags & SK_ISNULL )
if ( scan - > numberOfOrderBys > 0 )
{
/* Must fetch tuples in strict distance order */
PG_RETURN_BOOL ( getNextNearest ( scan ) ) ;
}
else
{
/* Fetch tuples index-page-at-a-time */
for ( ; ; )
{
/*
* On non - leaf page we can ' t conclude that child hasn ' t NULL
* values because of assumption in GiST : union ( VAL , NULL ) is VAL .
* But if on non - leaf page key IS NULL , then all children are
* NULL .
*/
if ( key - > sk_flags & SK_SEARCHNULL )
if ( so - > curPageData < so - > nPageData )
{
if ( GistPageIsLeaf ( p ) & & ! isNull )
return false ;
/* continuing to return tuples from a leaf page */
scan - > xs_ctup . t_self = so - > pageData [ so - > curPageData ] . heapPtr ;
scan - > xs_recheck = so - > pageData [ so - > curPageData ] . recheck ;
so - > curPageData + + ;
PG_RETURN_BOOL ( true ) ;
}
else
/* find and process the next index page */
do
{
Assert ( key - > sk_flags & SK_SEARCHNOTNULL ) ;
if ( isNull )
return false ;
}
}
else if ( isNull )
{
return false ;
}
else
{
gistdentryinit ( giststate , key - > sk_attno - 1 , & de ,
datum , r , p , offset ,
FALSE , isNull ) ;
GISTSearchItem * item = getNextGISTSearchItem ( so ) ;
/*
* Call the Consistent function to evaluate the test . The
* arguments are the index datum ( as a GISTENTRY * ) , the comparison
* datum , the comparison operator ' s strategy number and subtype
* from pg_amop , and the recheck flag .
*
* ( Presently there ' s no need to pass the subtype since it ' ll
* always be zero , but might as well pass it for possible future
* use . )
*
* We initialize the recheck flag to true ( the safest assumption )
* in case the Consistent function forgets to set it .
*/
recheck = true ;
if ( ! item )
PG_RETURN_BOOL ( false ) ;
test = FunctionCall5 ( & key - > sk_func ,
PointerGetDatum ( & de ) ,
key - > sk_argument ,
Int32GetDatum ( key - > sk_strategy ) ,
ObjectIdGetDatum ( key - > sk_subtype ) ,
PointerGetDatum ( & recheck ) ) ;
CHECK_FOR_INTERRUPTS ( ) ;
if ( ! DatumGetBool ( test ) )
return false ;
scan - > xs_recheck | = recheck ;
}
/*
* While scanning a leaf page , ItemPointers of matching heap
* tuples are stored in so - > pageData . If there are any on
* this page , we fall out of the inner " do " and loop around
* to return them .
*/
gistScanPage ( scan , item , so - > curTreeItem - > distances , NULL , NULL ) ;
keySize - - ;
key + + ;
pfree ( item ) ;
} while ( so - > nPageData = = 0 ) ;
}
}
return true ;
PG_RETURN_BOOL ( false ) ; /* keep compiler quiet */
}
/*
* Return the offset of the first index entry that is consistent with
* the search key after offset ' n ' in the current page . If there are
* no more consistent entries , return InvalidOffsetNumber .
* On success , scan - > xs_recheck is set correctly , too .
* Page should be locked . . . .
* gistgetbitmap ( ) - - Get a bitmap of all heap tuple locations
*/
static OffsetNumber
gistfindnext ( IndexScanDesc scan , OffsetNumber n )
Datum
gistgetbitmap ( PG_FUNCTION_ARGS )
{
OffsetNumber maxoff ;
IndexTuple it ;
GISTScanOpaque so ;
MemoryContext oldcxt ;
Page p ;
IndexScanDesc scan = ( IndexScanDesc ) PG_GETARG_POINTER ( 0 ) ;
TIDBitmap * tbm = ( TIDBitmap * ) PG_GETARG_POINTER ( 1 ) ;
GISTScanOpaque so = ( GISTScanOpaque ) scan - > opaque ;
int64 ntids = 0 ;
GISTSearchItem fakeItem ;
if ( ! so - > qual_ok )
PG_RETURN_INT64 ( 0 ) ;
pgstat_count_index_scan ( scan - > indexRelation ) ;
so = ( GISTScanOpaque ) scan - > opaque ;
p = BufferGetPage ( so - > curbuf ) ;
maxoff = PageGetMaxOffsetNumber ( p ) ;
/* Begin the scan by processing the root page */
so - > curTreeItem = NULL ;
so - > curPageData = so - > nPageData = 0 ;
fakeItem . blkno = GIST_ROOT_BLKNO ;
memset ( & fakeItem . data . parentlsn , 0 , sizeof ( GistNSN ) ) ;
gistScanPage ( scan , & fakeItem , NULL , tbm , & ntids ) ;
/*
* Make sure we ' re in a short - lived memory context when we invoke a
* user - supplied GiST method in gistindex_keytest ( ) , so we don ' t leak
* memory
* While scanning a leaf page , ItemPointers of matching heap tuples will
* be stored directly into tbm , so we don ' t need to deal with them here .
*/
oldcxt = MemoryContextSwitchTo ( so - > tempCxt ) ;
while ( n > = FirstOffsetNumber & & n < = maxoff )
for ( ; ; )
{
it = ( IndexTuple ) PageGetItem ( p , PageGetItemId ( p , n ) ) ;
if ( gistindex_keytest ( it , scan , n ) )
GISTSearchItem * item = getNextGISTSearchItem ( so ) ;
if ( ! item )
break ;
n = OffsetNumberNext ( n ) ;
}
CHECK_FOR_INTERRUPTS ( ) ;
MemoryContextSwitchTo ( oldcxt ) ;
MemoryContextReset ( so - > tempCxt ) ;
gistScanPage ( scan , item , so - > curTreeItem - > distances , tbm , & ntids ) ;
/*
* If we found a matching entry , return its offset ; otherwise return
* InvalidOffsetNumber to inform the caller to go to the next page .
*/
if ( n > = FirstOffsetNumber & & n < = maxoff )
return n ;
else
return InvalidOffsetNumber ;
pfree ( item ) ;
}
PG_RETURN_INT64 ( ntids ) ;
}