#include "postgres.h"
#include "access/gin_private.h"
#include "access/relscan.h"
#include "miscadmin.h"
#include "utils/datum.h"
#include "utils/memutils.h"
Go to the source code of this file.
Data Structures | |
struct | pendingPosition |
Defines | |
#define | gin_rand() (((double) random()) / ((double) MAX_RANDOM_VALUE)) |
#define | dropItem(e) ( gin_rand() > ((double)GinFuzzySearchLimit)/((double)((e)->predictNumberResult)) ) |
#define | GinIsNewKey(s) ( ((GinScanOpaque) scan->opaque)->keys == NULL ) |
#define | GinIsVoidRes(s) ( ((GinScanOpaque) scan->opaque)->isVoidRes ) |
Typedefs | |
typedef struct pendingPosition | pendingPosition |
Functions | |
static bool | callConsistentFn (GinState *ginstate, GinScanKey key) |
static bool | findItemInPostingPage (Page page, ItemPointer item, OffsetNumber *off) |
static bool | moveRightIfItNeeded (GinBtreeData *btree, GinBtreeStack *stack) |
static void | scanPostingTree (Relation index, GinScanEntry scanEntry, BlockNumber rootPostingTree) |
static bool | collectMatchBitmap (GinBtreeData *btree, GinBtreeStack *stack, GinScanEntry scanEntry) |
static void | startScanEntry (GinState *ginstate, GinScanEntry entry) |
static void | startScanKey (GinState *ginstate, GinScanKey key) |
static void | startScan (IndexScanDesc scan) |
static void | entryGetNextItem (GinState *ginstate, GinScanEntry entry) |
static void | entryGetItem (GinState *ginstate, GinScanEntry entry) |
static void | keyGetItem (GinState *ginstate, MemoryContext tempCtx, GinScanKey key) |
static bool | scanGetItem (IndexScanDesc scan, ItemPointer advancePast, ItemPointerData *item, bool *recheck) |
static bool | scanGetCandidate (IndexScanDesc scan, pendingPosition *pos) |
static bool | matchPartialInPendingList (GinState *ginstate, Page page, OffsetNumber off, OffsetNumber maxoff, GinScanEntry entry, Datum *datum, GinNullCategory *category, bool *datumExtracted) |
static bool | collectMatchesForHeapRow (IndexScanDesc scan, pendingPosition *pos) |
static void | scanPendingInsert (IndexScanDesc scan, TIDBitmap *tbm, int64 *ntids) |
Datum | gingetbitmap (PG_FUNCTION_ARGS) |
#define dropItem | ( | e | ) | ( gin_rand() > ((double)GinFuzzySearchLimit)/((double)((e)->predictNumberResult)) ) |
Definition at line 618 of file ginget.c.
Referenced by entryGetItem().
#define gin_rand | ( | ) | (((double) random()) / ((double) MAX_RANDOM_VALUE)) |
#define GinIsNewKey | ( | s | ) | ( ((GinScanOpaque) scan->opaque)->keys == NULL ) |
Definition at line 1501 of file ginget.c.
Referenced by gingetbitmap().
#define GinIsVoidRes | ( | s | ) | ( ((GinScanOpaque) scan->opaque)->isVoidRes ) |
Definition at line 1502 of file ginget.c.
Referenced by gingetbitmap().
typedef struct pendingPosition pendingPosition |
static bool callConsistentFn | ( | GinState * | ginstate, | |
GinScanKey | key | |||
) | [static] |
Definition at line 38 of file ginget.c.
References GinScanKeyData::attnum, GinState::consistentFn, DatumGetBool, GinScanKeyData::entryRes, GinScanKeyData::extra_data, FunctionCall8Coll(), GIN_SEARCH_MODE_EVERYTHING, GinScanKeyData::nuserentries, PointerGetDatum, GinScanKeyData::query, GinScanKeyData::queryCategories, GinScanKeyData::queryValues, GinScanKeyData::recheckCurItem, GinScanKeyData::searchMode, GinScanKeyData::strategy, GinState::supportCollation, UInt16GetDatum, and UInt32GetDatum.
Referenced by keyGetItem(), and scanPendingInsert().
{ /* * If we're dealing with a dummy EVERYTHING key, we don't want to call the * consistentFn; just claim it matches. */ if (key->searchMode == GIN_SEARCH_MODE_EVERYTHING) { key->recheckCurItem = false; return true; } /* * Initialize recheckCurItem in case the consistentFn doesn't know it * should set it. The safe assumption in that case is to force recheck. */ key->recheckCurItem = true; return DatumGetBool(FunctionCall8Coll(&ginstate->consistentFn[key->attnum - 1], ginstate->supportCollation[key->attnum - 1], PointerGetDatum(key->entryRes), UInt16GetDatum(key->strategy), key->query, UInt32GetDatum(key->nuserentries), PointerGetDatum(key->extra_data), PointerGetDatum(&key->recheckCurItem), PointerGetDatum(key->queryValues), PointerGetDatum(key->queryCategories))); }
static bool collectMatchBitmap | ( | GinBtreeData * | btree, | |
GinBtreeStack * | stack, | |||
GinScanEntry | scanEntry | |||
) | [static] |
Definition at line 188 of file ginget.c.
References GinScanEntryData::attnum, tupleDesc::attrs, GinBtreeStack::buffer, BufferGetPage, GinState::comparePartialFn, datumCopy(), DatumGetInt32, DatumGetPointer, elog, ERROR, GinScanEntryData::extra_data, FunctionCall4Coll(), GIN_CAT_NORM_KEY, GIN_CAT_NULL_ITEM, GIN_SEARCH_MODE_ALL, GIN_SHARE, GIN_UNLOCK, ginCompareEntries(), GinGetNPosting, GinGetPosting, GinGetPostingTree, GinIsPostingTree, GinPageIsLeaf, GinBtreeData::ginstate, gintuple_get_attrnum(), gintuple_get_key(), GinBtreeData::index, GinScanEntryData::isPartialMatch, LockBuffer(), GinScanEntryData::matchBitmap, moveRightIfItNeeded(), GinBtreeStack::off, GinState::origTupdesc, PageGetItem, PageGetItemId, pfree(), PointerGetDatum, GinScanEntryData::predictNumberResult, GinScanEntryData::queryCategory, GinScanEntryData::queryKey, scanPostingTree(), GinScanEntryData::searchMode, GinScanEntryData::strategy, GinState::supportCollation, tbm_add_tuples(), tbm_create(), UInt16GetDatum, and work_mem.
Referenced by startScanEntry().
{ OffsetNumber attnum; Form_pg_attribute attr; /* Initialize empty bitmap result */ scanEntry->matchBitmap = tbm_create(work_mem * 1024L); /* Null query cannot partial-match anything */ if (scanEntry->isPartialMatch && scanEntry->queryCategory != GIN_CAT_NORM_KEY) return true; /* Locate tupdesc entry for key column (for attbyval/attlen data) */ attnum = scanEntry->attnum; attr = btree->ginstate->origTupdesc->attrs[attnum - 1]; for (;;) { Page page; IndexTuple itup; Datum idatum; GinNullCategory icategory; /* * stack->off points to the interested entry, buffer is already locked */ if (moveRightIfItNeeded(btree, stack) == false) return true; page = BufferGetPage(stack->buffer); itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, stack->off)); /* * If tuple stores another attribute then stop scan */ if (gintuple_get_attrnum(btree->ginstate, itup) != attnum) return true; /* Safe to fetch attribute value */ idatum = gintuple_get_key(btree->ginstate, itup, &icategory); /* * Check for appropriate scan stop conditions */ if (scanEntry->isPartialMatch) { int32 cmp; /* * In partial match, stop scan at any null (including * placeholders); partial matches never match nulls */ if (icategory != GIN_CAT_NORM_KEY) return true; /*---------- * Check of partial match. * case cmp == 0 => match * case cmp > 0 => not match and finish scan * case cmp < 0 => not match and continue scan *---------- */ cmp = DatumGetInt32(FunctionCall4Coll(&btree->ginstate->comparePartialFn[attnum - 1], btree->ginstate->supportCollation[attnum - 1], scanEntry->queryKey, idatum, UInt16GetDatum(scanEntry->strategy), PointerGetDatum(scanEntry->extra_data))); if (cmp > 0) return true; else if (cmp < 0) { stack->off++; continue; } } else if (scanEntry->searchMode == GIN_SEARCH_MODE_ALL) { /* * In ALL mode, we are not interested in null items, so we can * stop if we get to a null-item placeholder (which will be the * last entry for a given attnum). We do want to include NULL_KEY * and EMPTY_ITEM entries, though. */ if (icategory == GIN_CAT_NULL_ITEM) return true; } /* * OK, we want to return the TIDs listed in this entry. */ if (GinIsPostingTree(itup)) { BlockNumber rootPostingTree = GinGetPostingTree(itup); /* * We should unlock current page (but not unpin) during tree scan * to prevent deadlock with vacuum processes. * * We save current entry value (idatum) to be able to re-find our * tuple after re-locking */ if (icategory == GIN_CAT_NORM_KEY) idatum = datumCopy(idatum, attr->attbyval, attr->attlen); LockBuffer(stack->buffer, GIN_UNLOCK); /* Collect all the TIDs in this entry's posting tree */ scanPostingTree(btree->index, scanEntry, rootPostingTree); /* * We lock again the entry page and while it was unlocked insert * might have occurred, so we need to re-find our position. */ LockBuffer(stack->buffer, GIN_SHARE); page = BufferGetPage(stack->buffer); if (!GinPageIsLeaf(page)) { /* * Root page becomes non-leaf while we unlock it. We will * start again, this situation doesn't occur often - root can * became a non-leaf only once per life of index. */ return false; } /* Search forward to re-find idatum */ for (;;) { Datum newDatum; GinNullCategory newCategory; if (moveRightIfItNeeded(btree, stack) == false) elog(ERROR, "lost saved point in index"); /* must not happen !!! */ page = BufferGetPage(stack->buffer); itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, stack->off)); if (gintuple_get_attrnum(btree->ginstate, itup) != attnum) elog(ERROR, "lost saved point in index"); /* must not happen !!! */ newDatum = gintuple_get_key(btree->ginstate, itup, &newCategory); if (ginCompareEntries(btree->ginstate, attnum, newDatum, newCategory, idatum, icategory) == 0) break; /* Found! */ stack->off++; } if (icategory == GIN_CAT_NORM_KEY && !attr->attbyval) pfree(DatumGetPointer(idatum)); } else { tbm_add_tuples(scanEntry->matchBitmap, GinGetPosting(itup), GinGetNPosting(itup), false); scanEntry->predictNumberResult += GinGetNPosting(itup); } /* * Done with this entry, go to the next */ stack->off++; } }
static bool collectMatchesForHeapRow | ( | IndexScanDesc | scan, | |
pendingPosition * | pos | |||
) | [static] |
Definition at line 1206 of file ginget.c.
References Assert, GinScanEntryData::attnum, GinScanKeyData::attnum, BufferGetPage, elog, GinScanKeyData::entryRes, ERROR, FALSE, pendingPosition::firstOffset, GIN_CAT_EMPTY_QUERY, GIN_CAT_NULL_ITEM, GIN_SEARCH_MODE_ALL, ginCompareEntries(), GinPageHasFullRow, GinScanOpaqueData::ginstate, gintuple_get_attrnum(), gintuple_get_key(), pendingPosition::hasMatchKey, i, GinScanEntryData::isPartialMatch, pendingPosition::item, ItemPointerEquals(), GinScanOpaqueData::keys, pendingPosition::lastOffset, matchPartialInPendingList(), GinScanKeyData::nentries, GinScanOpaqueData::nkeys, IndexScanDescData::opaque, PageGetItem, PageGetItemId, pendingPosition::pendingBuffer, GinScanEntryData::queryCategory, GinScanEntryData::queryKey, GinScanKeyData::scanEntry, scanGetCandidate(), and GinScanEntryData::searchMode.
Referenced by scanPendingInsert().
{ GinScanOpaque so = (GinScanOpaque) scan->opaque; OffsetNumber attrnum; Page page; IndexTuple itup; int i, j; /* * Reset all entryRes and hasMatchKey flags */ for (i = 0; i < so->nkeys; i++) { GinScanKey key = so->keys + i; memset(key->entryRes, FALSE, key->nentries); } memset(pos->hasMatchKey, FALSE, so->nkeys); /* * Outer loop iterates over multiple pending-list pages when a single heap * row has entries spanning those pages. */ for (;;) { Datum datum[BLCKSZ / sizeof(IndexTupleData)]; GinNullCategory category[BLCKSZ / sizeof(IndexTupleData)]; bool datumExtracted[BLCKSZ / sizeof(IndexTupleData)]; Assert(pos->lastOffset > pos->firstOffset); memset(datumExtracted + pos->firstOffset - 1, 0, sizeof(bool) * (pos->lastOffset - pos->firstOffset)); page = BufferGetPage(pos->pendingBuffer); for (i = 0; i < so->nkeys; i++) { GinScanKey key = so->keys + i; for (j = 0; j < key->nentries; j++) { GinScanEntry entry = key->scanEntry[j]; OffsetNumber StopLow = pos->firstOffset, StopHigh = pos->lastOffset, StopMiddle; /* If already matched on earlier page, do no extra work */ if (key->entryRes[j]) continue; /* * Interesting tuples are from pos->firstOffset to * pos->lastOffset and they are ordered by (attnum, Datum) as * it's done in entry tree. So we can use binary search to * avoid linear scanning. */ while (StopLow < StopHigh) { int res; StopMiddle = StopLow + ((StopHigh - StopLow) >> 1); itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, StopMiddle)); attrnum = gintuple_get_attrnum(&so->ginstate, itup); if (key->attnum < attrnum) { StopHigh = StopMiddle; continue; } if (key->attnum > attrnum) { StopLow = StopMiddle + 1; continue; } if (datumExtracted[StopMiddle - 1] == false) { datum[StopMiddle - 1] = gintuple_get_key(&so->ginstate, itup, &category[StopMiddle - 1]); datumExtracted[StopMiddle - 1] = true; } if (entry->queryCategory == GIN_CAT_EMPTY_QUERY) { /* special behavior depending on searchMode */ if (entry->searchMode == GIN_SEARCH_MODE_ALL) { /* match anything except NULL_ITEM */ if (category[StopMiddle - 1] == GIN_CAT_NULL_ITEM) res = -1; else res = 0; } else { /* match everything */ res = 0; } } else { res = ginCompareEntries(&so->ginstate, entry->attnum, entry->queryKey, entry->queryCategory, datum[StopMiddle - 1], category[StopMiddle - 1]); } if (res == 0) { /* * Found exact match (there can be only one, except in * EMPTY_QUERY mode). * * If doing partial match, scan forward from here to * end of page to check for matches. * * See comment above about tuple's ordering. */ if (entry->isPartialMatch) key->entryRes[j] = matchPartialInPendingList(&so->ginstate, page, StopMiddle, pos->lastOffset, entry, datum, category, datumExtracted); else key->entryRes[j] = true; /* done with binary search */ break; } else if (res < 0) StopHigh = StopMiddle; else StopLow = StopMiddle + 1; } if (StopLow >= StopHigh && entry->isPartialMatch) { /* * No exact match on this page. If doing partial match, * scan from the first tuple greater than target value to * end of page. Note that since we don't remember whether * the comparePartialFn told us to stop early on a * previous page, we will uselessly apply comparePartialFn * to the first tuple on each subsequent page. */ key->entryRes[j] = matchPartialInPendingList(&so->ginstate, page, StopHigh, pos->lastOffset, entry, datum, category, datumExtracted); } pos->hasMatchKey[i] |= key->entryRes[j]; } } /* Advance firstOffset over the scanned tuples */ pos->firstOffset = pos->lastOffset; if (GinPageHasFullRow(page)) { /* * We have examined all pending entries for the current heap row. * Break out of loop over pages. */ break; } else { /* * Advance to next page of pending entries for the current heap * row. Complain if there isn't one. */ ItemPointerData item = pos->item; if (scanGetCandidate(scan, pos) == false || !ItemPointerEquals(&pos->item, &item)) elog(ERROR, "could not find additional pending pages for same heap tuple"); } } /* * Now return "true" if all scan keys have at least one matching datum */ for (i = 0; i < so->nkeys; i++) { if (pos->hasMatchKey[i] == false) return false; } return true; }
static void entryGetItem | ( | GinState * | ginstate, | |
GinScanEntry | entry | |||
) | [static] |
Definition at line 634 of file ginget.c.
References Assert, TBMIterateResult::blockno, GinScanEntryData::buffer, BufferIsValid, GinScanEntryData::curItem, dropItem, entryGetNextItem(), FALSE, GinScanEntryData::isFinished, ItemPointerSet, ItemPointerSetInvalid, ItemPointerSetLossyPage, GinScanEntryData::list, GinScanEntryData::matchBitmap, GinScanEntryData::matchIterator, GinScanEntryData::matchResult, GinScanEntryData::nlist, TBMIterateResult::ntuples, NULL, GinScanEntryData::offset, TBMIterateResult::offsets, GinScanEntryData::reduceResult, tbm_end_iterate(), tbm_iterate(), and TRUE.
Referenced by scanGetItem().
{ Assert(!entry->isFinished); if (entry->matchBitmap) { do { if (entry->matchResult == NULL || entry->offset >= entry->matchResult->ntuples) { entry->matchResult = tbm_iterate(entry->matchIterator); if (entry->matchResult == NULL) { ItemPointerSetInvalid(&entry->curItem); tbm_end_iterate(entry->matchIterator); entry->matchIterator = NULL; entry->isFinished = TRUE; break; } /* * Reset counter to the beginning of entry->matchResult. Note: * entry->offset is still greater than matchResult->ntuples if * matchResult is lossy. So, on next call we will get next * result from TIDBitmap. */ entry->offset = 0; } if (entry->matchResult->ntuples < 0) { /* * lossy result, so we need to check the whole page */ ItemPointerSetLossyPage(&entry->curItem, entry->matchResult->blockno); /* * We might as well fall out of the loop; we could not * estimate number of results on this page to support correct * reducing of result even if it's enabled */ break; } ItemPointerSet(&entry->curItem, entry->matchResult->blockno, entry->matchResult->offsets[entry->offset]); entry->offset++; } while (entry->reduceResult == TRUE && dropItem(entry)); } else if (!BufferIsValid(entry->buffer)) { entry->offset++; if (entry->offset <= entry->nlist) entry->curItem = entry->list[entry->offset - 1]; else { ItemPointerSetInvalid(&entry->curItem); entry->isFinished = TRUE; } } else { do { entryGetNextItem(ginstate, entry); } while (entry->isFinished == FALSE && entry->reduceResult == TRUE && dropItem(entry)); } }
static void entryGetNextItem | ( | GinState * | ginstate, | |
GinScanEntry | entry | |||
) | [static] |
Definition at line 542 of file ginget.c.
References GinScanEntryData::buffer, BufferGetPage, GinScanEntryData::curItem, findItemInPostingPage(), FirstOffsetNumber, GIN_SHARE, GIN_UNLOCK, ginCompareItemPointers(), GinDataPageGetItem, GinPageGetOpaque, GinState::index, InvalidBlockNumber, GinScanEntryData::isFinished, ItemPointerIsValid, ItemPointerSetInvalid, GinScanEntryData::list, LockBuffer(), GinScanEntryData::nlist, GinScanEntryData::offset, ReleaseAndReadBuffer(), and ReleaseBuffer().
Referenced by entryGetItem().
{ Page page; BlockNumber blkno; for (;;) { if (entry->offset < entry->nlist) { entry->curItem = entry->list[entry->offset++]; return; } LockBuffer(entry->buffer, GIN_SHARE); page = BufferGetPage(entry->buffer); for (;;) { /* * It's needed to go by right link. During that we should refind * first ItemPointer greater that stored */ blkno = GinPageGetOpaque(page)->rightlink; LockBuffer(entry->buffer, GIN_UNLOCK); if (blkno == InvalidBlockNumber) { ReleaseBuffer(entry->buffer); ItemPointerSetInvalid(&entry->curItem); entry->buffer = InvalidBuffer; entry->isFinished = TRUE; return; } entry->buffer = ReleaseAndReadBuffer(entry->buffer, ginstate->index, blkno); LockBuffer(entry->buffer, GIN_SHARE); page = BufferGetPage(entry->buffer); entry->offset = InvalidOffsetNumber; if (!ItemPointerIsValid(&entry->curItem) || findItemInPostingPage(page, &entry->curItem, &entry->offset)) { /* * Found position equal to or greater than stored */ entry->nlist = GinPageGetOpaque(page)->maxoff; memcpy(entry->list, GinDataPageGetItem(page, FirstOffsetNumber), GinPageGetOpaque(page)->maxoff * sizeof(ItemPointerData)); LockBuffer(entry->buffer, GIN_UNLOCK); if (!ItemPointerIsValid(&entry->curItem) || ginCompareItemPointers(&entry->curItem, entry->list + entry->offset - 1) == 0) { /* * First pages are deleted or empty, or we found exact * position, so break inner loop and continue outer one. */ break; } /* * Find greater than entry->curItem position, store it. */ entry->curItem = entry->list[entry->offset - 1]; return; } } } }
static bool findItemInPostingPage | ( | Page | page, | |
ItemPointer | item, | |||
OffsetNumber * | off | |||
) | [static] |
Definition at line 72 of file ginget.c.
References FirstOffsetNumber, GIN_DELETED, ginCompareItemPointers(), GinDataPageGetItem, and GinPageGetOpaque.
Referenced by entryGetNextItem().
{ OffsetNumber maxoff = GinPageGetOpaque(page)->maxoff; int res; if (GinPageGetOpaque(page)->flags & GIN_DELETED) /* page was deleted by concurrent vacuum */ return false; /* * scan page to find equal or first greater value */ for (*off = FirstOffsetNumber; *off <= maxoff; (*off)++) { res = ginCompareItemPointers(item, (ItemPointer) GinDataPageGetItem(page, *off)); if (res <= 0) return true; } return false; }
Datum gingetbitmap | ( | PG_FUNCTION_ARGS | ) |
Definition at line 1505 of file ginget.c.
References CHECK_FOR_INTERRUPTS, GinIsNewKey, GinIsVoidRes, ginNewScanKey(), ItemPointerGetBlockNumber, ItemPointerIsLossyPage, ItemPointerSetMin, PG_GETARG_POINTER, PG_RETURN_INT64, scanGetItem(), scanPendingInsert(), startScan(), tbm_add_page(), and tbm_add_tuples().
{ IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0); TIDBitmap *tbm = (TIDBitmap *) PG_GETARG_POINTER(1); int64 ntids; ItemPointerData iptr; bool recheck; /* * Set up the scan keys, and check for unsatisfiable query. */ if (GinIsNewKey(scan)) ginNewScanKey(scan); if (GinIsVoidRes(scan)) PG_RETURN_INT64(0); ntids = 0; /* * First, scan the pending list and collect any matching entries into the * bitmap. After we scan a pending item, some other backend could post it * into the main index, and so we might visit it a second time during the * main scan. This is okay because we'll just re-set the same bit in the * bitmap. (The possibility of duplicate visits is a major reason why GIN * can't support the amgettuple API, however.) Note that it would not do * to scan the main index before the pending list, since concurrent * cleanup could then make us miss entries entirely. */ scanPendingInsert(scan, tbm, &ntids); /* * Now scan the main index. */ startScan(scan); ItemPointerSetMin(&iptr); for (;;) { CHECK_FOR_INTERRUPTS(); if (!scanGetItem(scan, &iptr, &iptr, &recheck)) break; if (ItemPointerIsLossyPage(&iptr)) tbm_add_page(tbm, ItemPointerGetBlockNumber(&iptr)); else tbm_add_tuples(tbm, &iptr, 1, recheck); ntids++; } PG_RETURN_INT64(ntids); }
static void keyGetItem | ( | GinState * | ginstate, | |
MemoryContext | tempCtx, | |||
GinScanKey | key | |||
) | [static] |
Definition at line 730 of file ginget.c.
References Assert, callConsistentFn(), GinScanKeyData::curItem, GinScanEntryData::curItem, GinScanKeyData::curItemMatches, GinScanKeyData::entryRes, FALSE, ginCompareItemPointers(), GinItemPointerGetBlockNumber, i, GinScanEntryData::isFinished, GinScanKeyData::isFinished, ItemPointerIsMax, ItemPointerSetLossyPage, ItemPointerSetMax, MemoryContextReset(), MemoryContextSwitchTo(), GinScanKeyData::nentries, GinScanKeyData::recheckCurItem, and GinScanKeyData::scanEntry.
Referenced by scanGetItem().
{ ItemPointerData minItem; ItemPointerData curPageLossy; uint32 i; uint32 lossyEntry; bool haveLossyEntry; GinScanEntry entry; bool res; MemoryContext oldCtx; Assert(!key->isFinished); /* * Find the minimum of the active entry curItems. * * Note: a lossy-page entry is encoded by a ItemPointer with max value for * offset (0xffff), so that it will sort after any exact entries for the * same page. So we'll prefer to return exact pointers not lossy * pointers, which is good. */ ItemPointerSetMax(&minItem); for (i = 0; i < key->nentries; i++) { entry = key->scanEntry[i]; if (entry->isFinished == FALSE && ginCompareItemPointers(&entry->curItem, &minItem) < 0) minItem = entry->curItem; } if (ItemPointerIsMax(&minItem)) { /* all entries are finished */ key->isFinished = TRUE; return; } /* * We might have already tested this item; if so, no need to repeat work. * (Note: the ">" case can happen, if minItem is exact but we previously * had to set curItem to a lossy-page pointer.) */ if (ginCompareItemPointers(&key->curItem, &minItem) >= 0) return; /* * OK, advance key->curItem and perform consistentFn test. */ key->curItem = minItem; /* * Lossy-page entries pose a problem, since we don't know the correct * entryRes state to pass to the consistentFn, and we also don't know what * its combining logic will be (could be AND, OR, or even NOT). If the * logic is OR then the consistentFn might succeed for all items in the * lossy page even when none of the other entries match. * * If we have a single lossy-page entry then we check to see if the * consistentFn will succeed with only that entry TRUE. If so, we return * a lossy-page pointer to indicate that the whole heap page must be * checked. (On subsequent calls, we'll do nothing until minItem is past * the page altogether, thus ensuring that we never return both regular * and lossy pointers for the same page.) * * This idea could be generalized to more than one lossy-page entry, but * ideally lossy-page entries should be infrequent so it would seldom be * the case that we have more than one at once. So it doesn't seem worth * the extra complexity to optimize that case. If we do find more than * one, we just punt and return a lossy-page pointer always. * * Note that only lossy-page entries pointing to the current item's page * should trigger this processing; we might have future lossy pages in the * entry array, but they aren't relevant yet. */ ItemPointerSetLossyPage(&curPageLossy, GinItemPointerGetBlockNumber(&key->curItem)); lossyEntry = 0; haveLossyEntry = false; for (i = 0; i < key->nentries; i++) { entry = key->scanEntry[i]; if (entry->isFinished == FALSE && ginCompareItemPointers(&entry->curItem, &curPageLossy) == 0) { if (haveLossyEntry) { /* Multiple lossy entries, punt */ key->curItem = curPageLossy; key->curItemMatches = true; key->recheckCurItem = true; return; } lossyEntry = i; haveLossyEntry = true; } } /* prepare for calling consistentFn in temp context */ oldCtx = MemoryContextSwitchTo(tempCtx); if (haveLossyEntry) { /* Single lossy-page entry, so see if whole page matches */ memset(key->entryRes, FALSE, key->nentries); key->entryRes[lossyEntry] = TRUE; if (callConsistentFn(ginstate, key)) { /* Yes, so clean up ... */ MemoryContextSwitchTo(oldCtx); MemoryContextReset(tempCtx); /* and return lossy pointer for whole page */ key->curItem = curPageLossy; key->curItemMatches = true; key->recheckCurItem = true; return; } } /* * At this point we know that we don't need to return a lossy whole-page * pointer, but we might have matches for individual exact item pointers, * possibly in combination with a lossy pointer. Our strategy if there's * a lossy pointer is to try the consistentFn both ways and return a hit * if it accepts either one (forcing the hit to be marked lossy so it will * be rechecked). An exception is that we don't need to try it both ways * if the lossy pointer is in a "hidden" entry, because the consistentFn's * result can't depend on that. * * Prepare entryRes array to be passed to consistentFn. */ for (i = 0; i < key->nentries; i++) { entry = key->scanEntry[i]; if (entry->isFinished == FALSE && ginCompareItemPointers(&entry->curItem, &key->curItem) == 0) key->entryRes[i] = TRUE; else key->entryRes[i] = FALSE; } if (haveLossyEntry) key->entryRes[lossyEntry] = TRUE; res = callConsistentFn(ginstate, key); if (!res && haveLossyEntry && lossyEntry < key->nuserentries) { /* try the other way for the lossy item */ key->entryRes[lossyEntry] = FALSE; res = callConsistentFn(ginstate, key); } key->curItemMatches = res; /* If we matched a lossy entry, force recheckCurItem = true */ if (haveLossyEntry) key->recheckCurItem = true; /* clean up after consistentFn calls */ MemoryContextSwitchTo(oldCtx); MemoryContextReset(tempCtx); }
static bool matchPartialInPendingList | ( | GinState * | ginstate, | |
Page | page, | |||
OffsetNumber | off, | |||
OffsetNumber | maxoff, | |||
GinScanEntry | entry, | |||
Datum * | datum, | |||
GinNullCategory * | category, | |||
bool * | datumExtracted | |||
) | [static] |
Definition at line 1138 of file ginget.c.
References GinScanEntryData::attnum, GinState::comparePartialFn, DatumGetInt32, GinScanEntryData::extra_data, FunctionCall4Coll(), GIN_CAT_NORM_KEY, gintuple_get_attrnum(), gintuple_get_key(), PageGetItem, PageGetItemId, PointerGetDatum, GinScanEntryData::queryCategory, GinScanEntryData::queryKey, GinScanEntryData::strategy, GinState::supportCollation, and UInt16GetDatum.
Referenced by collectMatchesForHeapRow().
{ IndexTuple itup; int32 cmp; /* Partial match to a null is not possible */ if (entry->queryCategory != GIN_CAT_NORM_KEY) return false; while (off < maxoff) { itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, off)); if (gintuple_get_attrnum(ginstate, itup) != entry->attnum) return false; if (datumExtracted[off - 1] == false) { datum[off - 1] = gintuple_get_key(ginstate, itup, &category[off - 1]); datumExtracted[off - 1] = true; } /* Once we hit nulls, no further match is possible */ if (category[off - 1] != GIN_CAT_NORM_KEY) return false; /*---------- * Check partial match. * case cmp == 0 => match * case cmp > 0 => not match and end scan (no later match possible) * case cmp < 0 => not match and continue scan *---------- */ cmp = DatumGetInt32(FunctionCall4Coll(&ginstate->comparePartialFn[entry->attnum - 1], ginstate->supportCollation[entry->attnum - 1], entry->queryKey, datum[off - 1], UInt16GetDatum(entry->strategy), PointerGetDatum(entry->extra_data))); if (cmp == 0) return true; else if (cmp > 0) return false; off++; } return false; }
static bool moveRightIfItNeeded | ( | GinBtreeData * | btree, | |
GinBtreeStack * | stack | |||
) | [static] |
Definition at line 99 of file ginget.c.
References GinBtreeStack::blkno, GinBtreeStack::buffer, BufferGetPage, GIN_SHARE, GIN_UNLOCK, GinPageGetOpaque, GinPageRightMost, GinBtreeData::index, LockBuffer(), GinBtreeStack::off, PageGetMaxOffsetNumber, and ReleaseAndReadBuffer().
Referenced by collectMatchBitmap().
{ Page page = BufferGetPage(stack->buffer); if (stack->off > PageGetMaxOffsetNumber(page)) { /* * We scanned the whole page, so we should take right page */ stack->blkno = GinPageGetOpaque(page)->rightlink; if (GinPageRightMost(page)) return false; /* no more pages */ LockBuffer(stack->buffer, GIN_UNLOCK); stack->buffer = ReleaseAndReadBuffer(stack->buffer, btree->index, stack->blkno); LockBuffer(stack->buffer, GIN_SHARE); stack->off = FirstOffsetNumber; } return true; }
static bool scanGetCandidate | ( | IndexScanDesc | scan, | |
pendingPosition * | pos | |||
) | [static] |
Definition at line 1051 of file ginget.c.
References BufferGetPage, pendingPosition::firstOffset, GIN_SHARE, GinPageGetOpaque, GinPageHasFullRow, IndexScanDescData::indexRelation, InvalidBlockNumber, pendingPosition::item, ItemPointerEquals(), ItemPointerSetInvalid, pendingPosition::lastOffset, LockBuffer(), PageGetItem, PageGetItemId, PageGetMaxOffsetNumber, pendingPosition::pendingBuffer, ReadBuffer(), IndexTupleData::t_tid, tmpbuf, and UnlockReleaseBuffer().
Referenced by collectMatchesForHeapRow(), and scanPendingInsert().
{ OffsetNumber maxoff; Page page; IndexTuple itup; ItemPointerSetInvalid(&pos->item); for (;;) { page = BufferGetPage(pos->pendingBuffer); maxoff = PageGetMaxOffsetNumber(page); if (pos->firstOffset > maxoff) { BlockNumber blkno = GinPageGetOpaque(page)->rightlink; if (blkno == InvalidBlockNumber) { UnlockReleaseBuffer(pos->pendingBuffer); pos->pendingBuffer = InvalidBuffer; return false; } else { /* * Here we must prevent deletion of next page by insertcleanup * process, which may be trying to obtain exclusive lock on * current page. So, we lock next page before releasing the * current one */ Buffer tmpbuf = ReadBuffer(scan->indexRelation, blkno); LockBuffer(tmpbuf, GIN_SHARE); UnlockReleaseBuffer(pos->pendingBuffer); pos->pendingBuffer = tmpbuf; pos->firstOffset = FirstOffsetNumber; } } else { itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, pos->firstOffset)); pos->item = itup->t_tid; if (GinPageHasFullRow(page)) { /* * find itempointer to the next row */ for (pos->lastOffset = pos->firstOffset + 1; pos->lastOffset <= maxoff; pos->lastOffset++) { itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, pos->lastOffset)); if (!ItemPointerEquals(&pos->item, &itup->t_tid)) break; } } else { /* * All itempointers are the same on this page */ pos->lastOffset = maxoff + 1; } /* * Now pos->firstOffset points to the first tuple of current heap * row, pos->lastOffset points to the first tuple of next heap row * (or to the end of page) */ break; } } return true; }
static bool scanGetItem | ( | IndexScanDesc | scan, | |
ItemPointer | advancePast, | |||
ItemPointerData * | item, | |||
bool * | recheck | |||
) | [static] |
Definition at line 906 of file ginget.c.
References Assert, GinScanKeyData::curItem, GinScanEntryData::curItem, GinScanKeyData::curItemMatches, GinScanOpaqueData::entries, entryGetItem(), FALSE, ginCompareItemPointers(), GinItemPointerGetBlockNumber, GinScanOpaqueData::ginstate, i, GinScanKeyData::isFinished, GinScanEntryData::isFinished, ItemPointerIsLossyPage, ItemPointerIsMax, ItemPointerSetMax, keyGetItem(), GinScanOpaqueData::keys, GinScanOpaqueData::nkeys, IndexScanDescData::opaque, GinScanKeyData::recheckCurItem, GinScanOpaqueData::tempCtx, and GinScanOpaqueData::totalentries.
Referenced by gingetbitmap().
{ GinScanOpaque so = (GinScanOpaque) scan->opaque; GinState *ginstate = &so->ginstate; ItemPointerData myAdvancePast = *advancePast; uint32 i; bool allFinished; bool match; for (;;) { /* * Advance any entries that are <= myAdvancePast. In particular, * since entry->curItem was initialized with ItemPointerSetMin, this * ensures we fetch the first item for each entry on the first call. */ allFinished = TRUE; for (i = 0; i < so->totalentries; i++) { GinScanEntry entry = so->entries[i]; while (entry->isFinished == FALSE && ginCompareItemPointers(&entry->curItem, &myAdvancePast) <= 0) entryGetItem(ginstate, entry); if (entry->isFinished == FALSE) allFinished = FALSE; } if (allFinished) { /* all entries exhausted, so we're done */ return false; } /* * Perform the consistentFn test for each scan key. If any key * reports isFinished, meaning its subset of the entries is exhausted, * we can stop. Otherwise, set *item to the minimum of the key * curItems. */ ItemPointerSetMax(item); for (i = 0; i < so->nkeys; i++) { GinScanKey key = so->keys + i; keyGetItem(&so->ginstate, so->tempCtx, key); if (key->isFinished) return false; /* finished one of keys */ if (ginCompareItemPointers(&key->curItem, item) < 0) *item = key->curItem; } Assert(!ItemPointerIsMax(item)); /*---------- * Now *item contains first ItemPointer after previous result. * * The item is a valid hit only if all the keys succeeded for either * that exact TID, or a lossy reference to the same page. * * This logic works only if a keyGetItem stream can never contain both * exact and lossy pointers for the same page. Else we could have a * case like * * stream 1 stream 2 * ... ... * 42/6 42/7 * 50/1 42/0xffff * ... ... * * We would conclude that 42/6 is not a match and advance stream 1, * thus never detecting the match to the lossy pointer in stream 2. * (keyGetItem has a similar problem versus entryGetItem.) *---------- */ match = true; for (i = 0; i < so->nkeys; i++) { GinScanKey key = so->keys + i; if (key->curItemMatches) { if (ginCompareItemPointers(item, &key->curItem) == 0) continue; if (ItemPointerIsLossyPage(&key->curItem) && GinItemPointerGetBlockNumber(&key->curItem) == GinItemPointerGetBlockNumber(item)) continue; } match = false; break; } if (match) break; /* * No hit. Update myAdvancePast to this TID, so that on the next pass * we'll move to the next possible entry. */ myAdvancePast = *item; } /* * We must return recheck = true if any of the keys are marked recheck. */ *recheck = false; for (i = 0; i < so->nkeys; i++) { GinScanKey key = so->keys + i; if (key->recheckCurItem) { *recheck = true; break; } } return TRUE; }
static void scanPendingInsert | ( | IndexScanDesc | scan, | |
TIDBitmap * | tbm, | |||
int64 * | ntids | |||
) | [static] |
Definition at line 1418 of file ginget.c.
References BufferGetPage, callConsistentFn(), collectMatchesForHeapRow(), GIN_METAPAGE_BLKNO, GIN_SHARE, GinPageGetMeta, GinScanOpaqueData::ginstate, i, IndexScanDescData::indexRelation, InvalidBlockNumber, GinScanOpaqueData::keys, LockBuffer(), MemoryContextReset(), MemoryContextSwitchTo(), GinScanOpaqueData::nkeys, IndexScanDescData::opaque, palloc(), pfree(), ReadBuffer(), GinScanKeyData::recheckCurItem, scanGetCandidate(), tbm_add_tuples(), GinScanOpaqueData::tempCtx, and UnlockReleaseBuffer().
Referenced by gingetbitmap().
{ GinScanOpaque so = (GinScanOpaque) scan->opaque; MemoryContext oldCtx; bool recheck, match; int i; pendingPosition pos; Buffer metabuffer = ReadBuffer(scan->indexRelation, GIN_METAPAGE_BLKNO); BlockNumber blkno; *ntids = 0; LockBuffer(metabuffer, GIN_SHARE); blkno = GinPageGetMeta(BufferGetPage(metabuffer))->head; /* * fetch head of list before unlocking metapage. head page must be pinned * to prevent deletion by vacuum process */ if (blkno == InvalidBlockNumber) { /* No pending list, so proceed with normal scan */ UnlockReleaseBuffer(metabuffer); return; } pos.pendingBuffer = ReadBuffer(scan->indexRelation, blkno); LockBuffer(pos.pendingBuffer, GIN_SHARE); pos.firstOffset = FirstOffsetNumber; UnlockReleaseBuffer(metabuffer); pos.hasMatchKey = palloc(sizeof(bool) * so->nkeys); /* * loop for each heap row. scanGetCandidate returns full row or row's * tuples from first page. */ while (scanGetCandidate(scan, &pos)) { /* * Check entries in tuple and set up entryRes array. * * If pending tuples belonging to the current heap row are spread * across several pages, collectMatchesForHeapRow will read all of * those pages. */ if (!collectMatchesForHeapRow(scan, &pos)) continue; /* * Matching of entries of one row is finished, so check row using * consistent functions. */ oldCtx = MemoryContextSwitchTo(so->tempCtx); recheck = false; match = true; for (i = 0; i < so->nkeys; i++) { GinScanKey key = so->keys + i; if (!callConsistentFn(&so->ginstate, key)) { match = false; break; } recheck |= key->recheckCurItem; } MemoryContextSwitchTo(oldCtx); MemoryContextReset(so->tempCtx); if (match) { tbm_add_tuples(tbm, &pos.item, 1, recheck); (*ntids)++; } } pfree(pos.hasMatchKey); }
static void scanPostingTree | ( | Relation | index, | |
GinScanEntry | scanEntry, | |||
BlockNumber | rootPostingTree | |||
) | [static] |
Definition at line 129 of file ginget.c.
References BufferGetPage, FirstOffsetNumber, freeGinBtreeStack(), GIN_DELETED, GIN_SHARE, GIN_UNLOCK, GinDataPageGetItem, GinPageGetOpaque, GinPageRightMost, ginPrepareScanPostingTree(), ginScanBeginPostingTree(), IncrBufferRefCount(), LockBuffer(), GinScanEntryData::matchBitmap, pfree(), GinScanEntryData::predictNumberResult, ReleaseAndReadBuffer(), GinPostingTreeScan::stack, tbm_add_tuples(), TRUE, and UnlockReleaseBuffer().
Referenced by collectMatchBitmap().
{ GinPostingTreeScan *gdi; Buffer buffer; Page page; BlockNumber blkno; /* Descend to the leftmost leaf page */ gdi = ginPrepareScanPostingTree(index, rootPostingTree, TRUE); buffer = ginScanBeginPostingTree(gdi); IncrBufferRefCount(buffer); /* prevent unpin in freeGinBtreeStack */ freeGinBtreeStack(gdi->stack); pfree(gdi); /* * Loop iterates through all leaf pages of posting tree */ for (;;) { page = BufferGetPage(buffer); if ((GinPageGetOpaque(page)->flags & GIN_DELETED) == 0 && GinPageGetOpaque(page)->maxoff >= FirstOffsetNumber) { tbm_add_tuples(scanEntry->matchBitmap, (ItemPointer) GinDataPageGetItem(page, FirstOffsetNumber), GinPageGetOpaque(page)->maxoff, false); scanEntry->predictNumberResult += GinPageGetOpaque(page)->maxoff; } if (GinPageRightMost(page)) break; /* no more pages */ blkno = GinPageGetOpaque(page)->rightlink; LockBuffer(buffer, GIN_UNLOCK); buffer = ReleaseAndReadBuffer(buffer, index, blkno); LockBuffer(buffer, GIN_SHARE); } UnlockReleaseBuffer(buffer); }
static void startScan | ( | IndexScanDesc | scan | ) | [static] |
Definition at line 502 of file ginget.c.
References GinScanOpaqueData::entries, GinFuzzySearchLimit, GinScanOpaqueData::ginstate, i, GinScanOpaqueData::keys, GinScanOpaqueData::nkeys, IndexScanDescData::opaque, GinScanEntryData::predictNumberResult, GinScanEntryData::reduceResult, startScanEntry(), startScanKey(), and GinScanOpaqueData::totalentries.
Referenced by gingetbitmap().
{ GinScanOpaque so = (GinScanOpaque) scan->opaque; GinState *ginstate = &so->ginstate; uint32 i; for (i = 0; i < so->totalentries; i++) startScanEntry(ginstate, so->entries[i]); if (GinFuzzySearchLimit > 0) { /* * If all of keys more than threshold we will try to reduce result, we * hope (and only hope, for intersection operation of array our * supposition isn't true), that total result will not more than * minimal predictNumberResult. */ for (i = 0; i < so->totalentries; i++) if (so->entries[i]->predictNumberResult <= so->totalentries * GinFuzzySearchLimit) return; for (i = 0; i < so->totalentries; i++) if (so->entries[i]->predictNumberResult > so->totalentries * GinFuzzySearchLimit) { so->entries[i]->predictNumberResult /= so->totalentries; so->entries[i]->reduceResult = TRUE; } } for (i = 0; i < so->nkeys; i++) startScanKey(ginstate, so->keys + i); }
static void startScanEntry | ( | GinState * | ginstate, | |
GinScanEntry | entry | |||
) | [static] |
Definition at line 363 of file ginget.c.
References GinScanEntryData::attnum, GinBtreeStack::buffer, GinScanEntryData::buffer, BufferGetPage, collectMatchBitmap(), GinScanEntryData::curItem, GinBtreeData::findItem, FirstOffsetNumber, freeGinBtreeStack(), GIN_CAT_EMPTY_QUERY, GIN_UNLOCK, GinDataPageGetItem, ginFindLeafPage(), GinGetNPosting, GinGetPosting, GinGetPostingTree, GinIsPostingTree, GinPageGetOpaque, ginPrepareEntryScan(), ginPrepareScanPostingTree(), ginScanBeginPostingTree(), IncrBufferRefCount(), GinState::index, GinScanEntryData::isFinished, GinScanEntryData::isPartialMatch, ItemPointerSetMin, GinScanEntryData::list, LockBuffer(), GinScanEntryData::matchBitmap, GinScanEntryData::matchIterator, GinScanEntryData::matchResult, GinScanEntryData::nlist, NULL, GinBtreeStack::off, GinScanEntryData::offset, PageGetItem, PageGetItemId, palloc(), pfree(), GinBtreeStack::predictNumber, GinScanEntryData::predictNumberResult, GinScanEntryData::queryCategory, GinScanEntryData::queryKey, GinScanEntryData::reduceResult, GinBtreeData::searchMode, GinPostingTreeScan::stack, tbm_begin_iterate(), tbm_end_iterate(), tbm_free(), tbm_is_empty(), and TRUE.
Referenced by startScan().
{ GinBtreeData btreeEntry; GinBtreeStack *stackEntry; Page page; bool needUnlock; restartScanEntry: entry->buffer = InvalidBuffer; ItemPointerSetMin(&entry->curItem); entry->offset = InvalidOffsetNumber; entry->list = NULL; entry->nlist = 0; entry->matchBitmap = NULL; entry->matchResult = NULL; entry->reduceResult = FALSE; entry->predictNumberResult = 0; /* * we should find entry, and begin scan of posting tree or just store * posting list in memory */ ginPrepareEntryScan(&btreeEntry, entry->attnum, entry->queryKey, entry->queryCategory, ginstate); btreeEntry.searchMode = TRUE; stackEntry = ginFindLeafPage(&btreeEntry, NULL); page = BufferGetPage(stackEntry->buffer); needUnlock = TRUE; entry->isFinished = TRUE; if (entry->isPartialMatch || entry->queryCategory == GIN_CAT_EMPTY_QUERY) { /* * btreeEntry.findItem locates the first item >= given search key. * (For GIN_CAT_EMPTY_QUERY, it will find the leftmost index item * because of the way the GIN_CAT_EMPTY_QUERY category code is * assigned.) We scan forward from there and collect all TIDs needed * for the entry type. */ btreeEntry.findItem(&btreeEntry, stackEntry); if (collectMatchBitmap(&btreeEntry, stackEntry, entry) == false) { /* * GIN tree was seriously restructured, so we will cleanup all * found data and rescan. See comments near 'return false' in * collectMatchBitmap() */ if (entry->matchBitmap) { if (entry->matchIterator) tbm_end_iterate(entry->matchIterator); entry->matchIterator = NULL; tbm_free(entry->matchBitmap); entry->matchBitmap = NULL; } LockBuffer(stackEntry->buffer, GIN_UNLOCK); freeGinBtreeStack(stackEntry); goto restartScanEntry; } if (entry->matchBitmap && !tbm_is_empty(entry->matchBitmap)) { entry->matchIterator = tbm_begin_iterate(entry->matchBitmap); entry->isFinished = FALSE; } } else if (btreeEntry.findItem(&btreeEntry, stackEntry)) { IndexTuple itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, stackEntry->off)); if (GinIsPostingTree(itup)) { BlockNumber rootPostingTree = GinGetPostingTree(itup); GinPostingTreeScan *gdi; Page page; /* * We should unlock entry page before touching posting tree to * prevent deadlocks with vacuum processes. Because entry is never * deleted from page and posting tree is never reduced to the * posting list, we can unlock page after getting BlockNumber of * root of posting tree. */ LockBuffer(stackEntry->buffer, GIN_UNLOCK); needUnlock = FALSE; gdi = ginPrepareScanPostingTree(ginstate->index, rootPostingTree, TRUE); entry->buffer = ginScanBeginPostingTree(gdi); /* * We keep buffer pinned because we need to prevent deletion of * page during scan. See GIN's vacuum implementation. RefCount is * increased to keep buffer pinned after freeGinBtreeStack() call. */ IncrBufferRefCount(entry->buffer); page = BufferGetPage(entry->buffer); entry->predictNumberResult = gdi->stack->predictNumber * GinPageGetOpaque(page)->maxoff; /* * Keep page content in memory to prevent durable page locking */ entry->list = (ItemPointerData *) palloc(BLCKSZ); entry->nlist = GinPageGetOpaque(page)->maxoff; memcpy(entry->list, GinDataPageGetItem(page, FirstOffsetNumber), GinPageGetOpaque(page)->maxoff * sizeof(ItemPointerData)); LockBuffer(entry->buffer, GIN_UNLOCK); freeGinBtreeStack(gdi->stack); pfree(gdi); entry->isFinished = FALSE; } else if (GinGetNPosting(itup) > 0) { entry->nlist = GinGetNPosting(itup); entry->list = (ItemPointerData *) palloc(sizeof(ItemPointerData) * entry->nlist); memcpy(entry->list, GinGetPosting(itup), sizeof(ItemPointerData) * entry->nlist); entry->isFinished = FALSE; } } if (needUnlock) LockBuffer(stackEntry->buffer, GIN_UNLOCK); freeGinBtreeStack(stackEntry); }
static void startScanKey | ( | GinState * | ginstate, | |
GinScanKey | key | |||
) | [static] |
Definition at line 493 of file ginget.c.
References GinScanKeyData::curItem, GinScanKeyData::curItemMatches, GinScanKeyData::isFinished, ItemPointerSetMin, and GinScanKeyData::recheckCurItem.
Referenced by startScan().
{ ItemPointerSetMin(&key->curItem); key->curItemMatches = false; key->recheckCurItem = false; key->isFinished = false; }