/* * Fetch next heap tuple in an ordered search */ static bool getNextNearest(IndexScanDesc scan) { GISTScanOpaque so = (GISTScanOpaque) scan->opaque; bool res = false; do { GISTSearchItem *item = getNextGISTSearchItem(so); if (!item) break; 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(); gistScanPage(scan, item, so->curTreeItem->distances, NULL, NULL); } pfree(item); } while (!res); return res; }
/* * gistgetbitmap() -- Get a bitmap of all heap tuple locations */ Datum gistgetbitmap(PG_FUNCTION_ARGS) { IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0); Node *n = (Node *) PG_GETARG_POINTER(1); TIDBitmap *tbm; GISTScanOpaque so = (GISTScanOpaque) scan->opaque; int64 ntids = 0; GISTSearchItem fakeItem; if (n == NULL) tbm = tbm_create(work_mem * 1024L); else if (!IsA(n, TIDBitmap)) elog(ERROR, "non hash bitmap"); else tbm = (TIDBitmap *) n; if (!so->qual_ok) PG_RETURN_POINTER(tbm); pgstat_count_index_scan(scan->indexRelation); /* 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); /* * 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. */ for (;;) { GISTSearchItem *item = getNextGISTSearchItem(so); if (!item) break; CHECK_FOR_INTERRUPTS(); gistScanPage(scan, item, so->curTreeItem->distances, tbm, &ntids); pfree(item); } PG_RETURN_POINTER(tbm); }
/* * Fetch next heap tuple in an ordered search */ static bool getNextNearest(IndexScanDesc scan) { GISTScanOpaque so = (GISTScanOpaque) scan->opaque; bool res = false; if (scan->xs_hitup) { /* free previously returned tuple */ pfree(scan->xs_hitup); scan->xs_hitup = NULL; } do { GISTSearchItem *item = getNextGISTSearchItem(so); if (!item) break; 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; index_store_float8_orderby_distances(scan, so->orderByTypes, item->distances, item->data.heap.recheckDistances); /* in an index-only scan, also return the reconstructed tuple. */ if (scan->xs_want_itup) scan->xs_hitup = item->data.heap.recontup; res = true; } else { /* visit an index page, extract its items into queue */ CHECK_FOR_INTERRUPTS(); gistScanPage(scan, item, item->distances, NULL, NULL); } pfree(item); } while (!res); return res; }
/* * gistgetbitmap() -- Get a bitmap of all heap tuple locations */ int64 gistgetbitmap(IndexScanDesc scan, TIDBitmap *tbm) { GISTScanOpaque so = (GISTScanOpaque) scan->opaque; int64 ntids = 0; GISTSearchItem fakeItem; if (!so->qual_ok) return 0; pgstat_count_index_scan(scan->indexRelation); /* Begin the scan by processing the root page */ so->curPageData = so->nPageData = 0; scan->xs_hitup = NULL; if (so->pageDataCxt) MemoryContextReset(so->pageDataCxt); fakeItem.blkno = GIST_ROOT_BLKNO; memset(&fakeItem.data.parentlsn, 0, sizeof(GistNSN)); gistScanPage(scan, &fakeItem, NULL, tbm, &ntids); /* * 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. */ for (;;) { GISTSearchItem *item = getNextGISTSearchItem(so); if (!item) break; CHECK_FOR_INTERRUPTS(); gistScanPage(scan, item, item->distances, tbm, &ntids); pfree(item); } return ntids; }
/* * 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 = (GISTScanOpaque) scan->opaque; if (dir != ForwardScanDirection) elog(ERROR, "GiST only supports forward scan direction"); if (!so->qual_ok) PG_RETURN_BOOL(false); if (so->firstCall) { /* Begin the scan by processing the root page */ GISTSearchItem fakeItem; pgstat_count_index_scan(scan->indexRelation); so->firstCall = false; so->curTreeItem = NULL; so->curPageData = so->nPageData = 0; fakeItem.blkno = GIST_ROOT_BLKNO; memset(&fakeItem.data.parentlsn, 0, sizeof(GistNSN)); gistScanPage(scan, &fakeItem, NULL, NULL, NULL); } 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 (;;) { if (so->curPageData < so->nPageData) { /* 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); } /* find and process the next index page */ do { GISTSearchItem *item = getNextGISTSearchItem(so); if (!item) PG_RETURN_BOOL(false); CHECK_FOR_INTERRUPTS(); /* * 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); pfree(item); } while (so->nPageData == 0); } } }
/* * gistgettuple() -- Get the next tuple in the scan */ bool gistgettuple(IndexScanDesc scan, ScanDirection dir) { GISTScanOpaque so = (GISTScanOpaque) scan->opaque; if (dir != ForwardScanDirection) elog(ERROR, "GiST only supports forward scan direction"); if (!so->qual_ok) return false; if (so->firstCall) { /* Begin the scan by processing the root page */ GISTSearchItem fakeItem; pgstat_count_index_scan(scan->indexRelation); so->firstCall = false; so->curPageData = so->nPageData = 0; scan->xs_hitup = NULL; if (so->pageDataCxt) MemoryContextReset(so->pageDataCxt); fakeItem.blkno = GIST_ROOT_BLKNO; memset(&fakeItem.data.parentlsn, 0, sizeof(GistNSN)); gistScanPage(scan, &fakeItem, NULL, NULL, NULL); } if (scan->numberOfOrderBys > 0) { /* Must fetch tuples in strict distance order */ return getNextNearest(scan); } else { /* Fetch tuples index-page-at-a-time */ for (;;) { if (so->curPageData < so->nPageData) { if (scan->kill_prior_tuple && so->curPageData > 0) { if (so->killedItems == NULL) { MemoryContext oldCxt = MemoryContextSwitchTo(so->giststate->scanCxt); so->killedItems = (OffsetNumber *) palloc(MaxIndexTuplesPerPage * sizeof(OffsetNumber)); MemoryContextSwitchTo(oldCxt); } if (so->numKilled < MaxIndexTuplesPerPage) so->killedItems[so->numKilled++] = so->pageData[so->curPageData - 1].offnum; } /* 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; /* in an index-only scan, also return the reconstructed tuple */ if (scan->xs_want_itup) scan->xs_hitup = so->pageData[so->curPageData].recontup; so->curPageData++; return true; } /* * Check the last returned tuple and add it to killitems if * necessary */ if (scan->kill_prior_tuple && so->curPageData > 0 && so->curPageData == so->nPageData) { if (so->killedItems == NULL) { MemoryContext oldCxt = MemoryContextSwitchTo(so->giststate->scanCxt); so->killedItems = (OffsetNumber *) palloc(MaxIndexTuplesPerPage * sizeof(OffsetNumber)); MemoryContextSwitchTo(oldCxt); } if (so->numKilled < MaxIndexTuplesPerPage) so->killedItems[so->numKilled++] = so->pageData[so->curPageData - 1].offnum; } /* find and process the next index page */ do { GISTSearchItem *item; if ((so->curBlkno != InvalidBlockNumber) && (so->numKilled > 0)) gistkillitems(scan); item = getNextGISTSearchItem(so); if (!item) return false; CHECK_FOR_INTERRUPTS(); /* save current item BlockNumber for next gistkillitems() call */ so->curBlkno = item->blkno; /* * 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, item->distances, NULL, NULL); pfree(item); } while (so->nPageData == 0); } } }
/* * Fetch next heap tuple in an ordered search */ static bool getNextNearest(IndexScanDesc scan) { GISTScanOpaque so = (GISTScanOpaque) scan->opaque; bool res = false; int i; if (scan->xs_itup) { /* free previously returned tuple */ pfree(scan->xs_itup); scan->xs_itup = NULL; } do { GISTSearchItem *item = getNextGISTSearchItem(so); if (!item) break; 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; scan->xs_recheckorderby = item->data.heap.recheckDistances; for (i = 0; i < scan->numberOfOrderBys; i++) { if (so->orderByTypes[i] == FLOAT8OID) { #ifndef USE_FLOAT8_BYVAL /* must free any old value to avoid memory leakage */ if (!scan->xs_orderbynulls[i]) pfree(DatumGetPointer(scan->xs_orderbyvals[i])); #endif scan->xs_orderbyvals[i] = Float8GetDatum(item->distances[i]); scan->xs_orderbynulls[i] = false; } else if (so->orderByTypes[i] == FLOAT4OID) { /* convert distance function's result to ORDER BY type */ #ifndef USE_FLOAT4_BYVAL /* must free any old value to avoid memory leakage */ if (!scan->xs_orderbynulls[i]) pfree(DatumGetPointer(scan->xs_orderbyvals[i])); #endif scan->xs_orderbyvals[i] = Float4GetDatum((float4) item->distances[i]); scan->xs_orderbynulls[i] = false; } else { /* * If the ordering operator's return value is anything * else, we don't know how to convert the float8 bound * calculated by the distance function to that. The * executor won't actually need the order by values we * return here, if there are no lossy results, so only * insist on converting if the *recheck flag is set. */ if (scan->xs_recheckorderby) elog(ERROR, "GiST operator family's FOR ORDER BY operator must return float8 or float4 if the distance function is lossy"); scan->xs_orderbynulls[i] = true; } } /* in an index-only scan, also return the reconstructed tuple. */ if (scan->xs_want_itup) scan->xs_itup = item->data.heap.ftup; res = true; } else { /* visit an index page, extract its items into queue */ CHECK_FOR_INTERRUPTS(); gistScanPage(scan, item, item->distances, NULL, NULL); } pfree(item); } while (!res); return res; }