diff options
Diffstat (limited to 'src/backend/access/gist/gistget.c')
-rw-r--r-- | src/backend/access/gist/gistget.c | 819 |
1 files changed, 434 insertions, 385 deletions
diff --git a/src/backend/access/gist/gistget.c b/src/backend/access/gist/gistget.c index fab13eed52..f0418a08af 100644 --- a/src/backend/access/gist/gistget.c +++ b/src/backend/access/gist/gistget.c @@ -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); } |