/* * Try to find parent for current stack position. Returns correct parent and * child's offset in stack->parent. The root page is never released, to * to prevent conflict with vacuum process. */ static void ginFindParents(GinBtree btree, GinBtreeStack *stack) { Page page; Buffer buffer; BlockNumber blkno, leftmostBlkno; OffsetNumber offset; GinBtreeStack *root; GinBtreeStack *ptr; /* * Unwind the stack all the way up to the root, leaving only the root * item. * * Be careful not to release the pin on the root page! The pin on root * page is required to lock out concurrent vacuums on the tree. */ root = stack->parent; while (root->parent) { ReleaseBuffer(root->buffer); root = root->parent; } Assert(root->blkno == btree->rootBlkno); Assert(BufferGetBlockNumber(root->buffer) == btree->rootBlkno); root->off = InvalidOffsetNumber; blkno = root->blkno; buffer = root->buffer; offset = InvalidOffsetNumber; ptr = (GinBtreeStack *) palloc(sizeof(GinBtreeStack)); for (;;) { LockBuffer(buffer, GIN_EXCLUSIVE); page = BufferGetPage(buffer); if (GinPageIsLeaf(page)) elog(ERROR, "Lost path"); if (GinPageIsIncompleteSplit(page)) { Assert(blkno != btree->rootBlkno); ptr->blkno = blkno; ptr->buffer = buffer; /* * parent may be wrong, but if so, the ginFinishSplit call will * recurse to call ginFindParents again to fix it. */ ptr->parent = root; ptr->off = InvalidOffsetNumber; ginFinishSplit(btree, ptr, false, NULL); } leftmostBlkno = btree->getLeftMostChild(btree, page); while ((offset = btree->findChildPtr(btree, page, stack->blkno, InvalidOffsetNumber)) == InvalidOffsetNumber) { blkno = GinPageGetOpaque(page)->rightlink; if (blkno == InvalidBlockNumber) { UnlockReleaseBuffer(buffer); break; } buffer = ginStepRight(buffer, btree->index, GIN_EXCLUSIVE); page = BufferGetPage(buffer); /* finish any incomplete splits, as above */ if (GinPageIsIncompleteSplit(page)) { Assert(blkno != btree->rootBlkno); ptr->blkno = blkno; ptr->buffer = buffer; ptr->parent = root; ptr->off = InvalidOffsetNumber; ginFinishSplit(btree, ptr, false, NULL); } } if (blkno != InvalidBlockNumber) { ptr->blkno = blkno; ptr->buffer = buffer; ptr->parent = root; /* it may be wrong, but in next call we will * correct */ ptr->off = offset; stack->parent = ptr; return; } /* Descend down to next level */ blkno = leftmostBlkno; buffer = ReadBuffer(btree->index, blkno); } }
/* * Find correct tuple in non-leaf page. It supposed that * page correctly chosen and searching value SHOULD be on page */ static BlockNumber entryLocateEntry(GinBtree btree, GinBtreeStack *stack) { OffsetNumber low, high, maxoff; IndexTuple itup = NULL; int result; Page page = BufferGetPage(stack->buffer); Assert(!GinPageIsLeaf(page)); Assert(!GinPageIsData(page)); if (btree->fullScan) { stack->off = FirstOffsetNumber; stack->predictNumber *= PageGetMaxOffsetNumber(page); return btree->getLeftMostChild(btree, page); } low = FirstOffsetNumber; maxoff = high = PageGetMaxOffsetNumber(page); Assert(high >= low); high++; while (high > low) { OffsetNumber mid = low + ((high - low) / 2); if (mid == maxoff && GinPageRightMost(page)) { /* Right infinity */ result = -1; } else { OffsetNumber attnum; Datum key; GinNullCategory category; itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, mid)); attnum = gintuple_get_attrnum(btree->ginstate, itup); key = gintuple_get_key(btree->ginstate, itup, &category); result = ginCompareAttEntries(btree->ginstate, btree->entryAttnum, btree->entryKey, btree->entryCategory, attnum, key, category); } if (result == 0) { stack->off = mid; Assert(GinGetDownlink(itup) != GIN_ROOT_BLKNO); return GinGetDownlink(itup); } else if (result > 0) low = mid + 1; else high = mid; } Assert(high >= FirstOffsetNumber && high <= maxoff); stack->off = high; itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, high)); Assert(GinGetDownlink(itup) != GIN_ROOT_BLKNO); return GinGetDownlink(itup); }
/* * 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->getLeftMostChild(btree, page); } low = FirstOffsetNumber; maxoff = high = GinPageGetOpaque(page)->maxoff; Assert(high >= low); high++; while (high > low) { OffsetNumber mid = low + ((high - low) / 2); pitem = GinDataPageGetPostingItem(page, mid); if (mid == maxoff) { /* * Right infinity, page already correctly chosen with a help of * dataIsMoveRight */ result = -1; } else { pitem = GinDataPageGetPostingItem(page, mid); result = ginCompareItemPointers(&btree->itemptr, &(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 = GinDataPageGetPostingItem(page, high); return PostingItemGetBlockNumber(pitem); }