/* Combiner function for rbtree.c */ static void ginCombineData(RBNode *existing, const RBNode *newdata, void *arg) { EntryAccumulator *eo = (EntryAccumulator *) existing; const EntryAccumulator *en = (const EntryAccumulator *) newdata; BuildAccumulator *accum = (BuildAccumulator *) arg; /* * Note this code assumes that newdata contains only one itempointer. */ if (eo->number >= eo->length) { accum->allocatedMemory -= GetMemoryChunkSpace(eo->list); eo->length *= 2; eo->list = (ItemPointerData *) repalloc(eo->list, sizeof(ItemPointerData) * eo->length); accum->allocatedMemory += GetMemoryChunkSpace(eo->list); } /* If item pointers are not ordered, they will need to be sorted. */ if (eo->shouldSort == FALSE) { int res; res = compareItemPointers(eo->list + eo->number - 1, en->list); Assert(res != 0); if (res > 0) eo->shouldSort = TRUE; } eo->list[eo->number] = en->list[0]; eo->number++; }
static int qsortCompareItemPointers(const void *a, const void *b) { int res = compareItemPointers((ItemPointer) a, (ItemPointer) b); Assert(res != 0); return res; }
/* * Get heap item pointer from scan * returns true if found */ static bool scanGetItem(IndexScanDesc scan, ItemPointerData *item) { uint32 i; GinScanOpaque so = (GinScanOpaque) scan->opaque; ItemPointerSetMin(item); for (i = 0; i < so->nkeys; i++) { GinScanKey key = so->keys + i; if (keyGetItem(scan->indexRelation, &so->ginstate, so->tempCtx, key) == FALSE) { if (compareItemPointers(item, &key->curItem) < 0) *item = key->curItem; } else return FALSE; /* finished one of keys */ } for (i = 1; i <= so->nkeys; i++) { GinScanKey key = so->keys + i - 1; for (;;) { int cmp = compareItemPointers(item, &key->curItem); if (cmp == 0) break; else if (cmp > 0) { if (keyGetItem(scan->indexRelation, &so->ginstate, so->tempCtx, key) == TRUE) return FALSE; /* finished one of keys */ } else { /* returns to begin */ *item = key->curItem; i = 0; break; } } } return TRUE; }
/* * Checks, should we move to right link... * Compares inserting itemp pointer with right bound of current page */ static bool dataIsMoveRight(GinBtree btree, Page page) { ItemPointer iptr = GinDataPageGetRightBound(page); if (GinPageRightMost(page)) return FALSE; return (compareItemPointers(btree->items + btree->curitem, iptr) > 0) ? TRUE : FALSE; }
/* * Searches correct position for value on leaf page. * Page should be correctly chosen. * Returns true if value found on page. */ static bool dataLocateLeafItem(GinBtree btree, GinBtreeStack *stack) { Page page = BufferGetPage(stack->buffer); OffsetNumber low, high; int result; Assert(GinPageIsLeaf(page)); Assert(GinPageIsData(page)); if (btree->fullScan) { stack->off = FirstOffsetNumber; return TRUE; } low = FirstOffsetNumber; high = GinPageGetOpaque(page)->maxoff; if (high < low) { stack->off = FirstOffsetNumber; return false; } high++; while (high > low) { OffsetNumber mid = low + ((high - low) / 2); result = compareItemPointers(btree->items + btree->curitem, (ItemPointer) GinDataPageGetItem(page, mid)); if (result == 0) { stack->off = mid; return true; } else if (result > 0) low = mid + 1; else high = mid; } stack->off = high; return false; }
/* * Merge two ordered array of itempointer */ void MergeItemPointers(ItemPointerData *dst, ItemPointerData *a, uint32 na, ItemPointerData *b, uint32 nb) { ItemPointerData *dptr = dst; ItemPointerData *aptr = a, *bptr = b; while (aptr - a < na && bptr - b < nb) { if (compareItemPointers(aptr, bptr) > 0) *dptr++ = *bptr++; else *dptr++ = *aptr++; } while (aptr - a < na) *dptr++ = *aptr++; while (bptr - b < nb) *dptr++ = *bptr++; }
static bool findItemInPage(Page page, ItemPointer item, OffsetNumber *off) { 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 = compareItemPointers(item, (ItemPointer) GinDataPageGetItem(page, *off)); if (res <= 0) return true; } return false; }
/* * Stores heap item pointer. For robust, it checks that * item pointer are ordered */ static void ginInsertData(BuildAccumulator *accum, EntryAccumulator *entry, ItemPointer heapptr) { if (entry->number >= entry->length) { accum->allocatedMemory += sizeof(ItemPointerData) * entry->length; entry->length *= 2; entry->list = (ItemPointerData *) repalloc(entry->list, sizeof(ItemPointerData) * entry->length); } if (entry->shouldSort == FALSE) { int res = compareItemPointers(entry->list + entry->number - 1, heapptr); Assert(res != 0); if (res > 0) entry->shouldSort = TRUE; } entry->list[entry->number] = *heapptr; entry->number++; }
/* * Find correct PostingItem in non-leaf page. It supposed that page * correctly chosen and searching value SHOULD be on page */ static BlockNumber dataLocateItem(GinBtree btree, GinBtreeStack *stack) { OffsetNumber low, high, maxoff; PostingItem *pitem = NULL; int result; Page page = BufferGetPage(stack->buffer); Assert(!GinPageIsLeaf(page)); Assert(GinPageIsData(page)); if (btree->fullScan) { stack->off = FirstOffsetNumber; stack->predictNumber *= GinPageGetOpaque(page)->maxoff; return btree->getLeftMostPage(btree, page); } low = FirstOffsetNumber; maxoff = high = GinPageGetOpaque(page)->maxoff; Assert(high >= low); high++; while (high > low) { OffsetNumber mid = low + ((high - low) / 2); pitem = (PostingItem *) GinDataPageGetItem(page, mid); if (mid == maxoff) /* * Right infinity, page already correctly chosen with a help of * dataIsMoveRight */ result = -1; else { pitem = (PostingItem *) GinDataPageGetItem(page, mid); result = compareItemPointers(btree->items + btree->curitem, &(pitem->key)); } if (result == 0) { stack->off = mid; return PostingItemGetBlockNumber(pitem); } else if (result > 0) low = mid + 1; else high = mid; } Assert(high >= FirstOffsetNumber && high <= maxoff); stack->off = high; pitem = (PostingItem *) GinDataPageGetItem(page, high); return PostingItemGetBlockNumber(pitem); }
/* * Sets key->curItem to new found heap item pointer for one scan key * returns isFinished! */ static bool keyGetItem(Relation index, GinState *ginstate, MemoryContext tempCtx, GinScanKey key) { uint32 i; GinScanEntry entry; bool res; MemoryContext oldCtx; if (key->isFinished) return TRUE; do { /* * move forward from previously value and set new curItem, which is * minimal from entries->curItems */ ItemPointerSetMax(&key->curItem); for (i = 0; i < key->nentries; i++) { entry = key->scanEntry + i; if (key->entryRes[i]) { if (entry->isFinished == FALSE && entryGetItem(index, entry) == FALSE) { if (compareItemPointers(&entry->curItem, &key->curItem) < 0) key->curItem = entry->curItem; } else key->entryRes[i] = FALSE; } else if (entry->isFinished == FALSE) { if (compareItemPointers(&entry->curItem, &key->curItem) < 0) key->curItem = entry->curItem; } } if (ItemPointerIsMax(&key->curItem)) { /* all entries are finished */ key->isFinished = TRUE; return TRUE; } if (key->nentries == 1) { /* we can do not call consistentFn !! */ key->entryRes[0] = TRUE; return FALSE; } /* setting up array for consistentFn */ for (i = 0; i < key->nentries; i++) { entry = key->scanEntry + i; if (entry->isFinished == FALSE && compareItemPointers(&entry->curItem, &key->curItem) == 0) key->entryRes[i] = TRUE; else key->entryRes[i] = FALSE; } oldCtx = MemoryContextSwitchTo(tempCtx); res = DatumGetBool(FunctionCall3( &ginstate->consistentFn, PointerGetDatum(key->entryRes), UInt16GetDatum(key->strategy), key->query )); MemoryContextSwitchTo(oldCtx); MemoryContextReset(tempCtx); } while (!res); return FALSE; }
/* * Gets next ItemPointer from PostingTree. Note, that we copy * page into GinScanEntry->list array and unlock page, but keep it pinned * to prevent interference with vacuum */ static void entryGetNextItem(Relation index, GinScanEntry entry) { Page page; BlockNumber blkno; for(;;) { entry->offset++; if (entry->offset <= entry->nlist) { entry->curItem = entry->list[entry->offset - 1]; 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); ItemPointerSet(&entry->curItem, InvalidBlockNumber, InvalidOffsetNumber); entry->buffer = InvalidBuffer; entry->isFinished = TRUE; return; } entry->buffer = ReleaseAndReadBuffer(entry->buffer, index, blkno); LockBuffer(entry->buffer, GIN_SHARE); page = BufferGetPage(entry->buffer); entry->offset = InvalidOffsetNumber; if (!ItemPointerIsValid(&entry->curItem) || findItemInPage(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) || compareItemPointers( &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; } } } }