GinBtreeStack * ginPrepareFindLeafPage(GinBtree btree, BlockNumber blkno) { GinBtreeStack *stack = (GinBtreeStack *) palloc(sizeof(GinBtreeStack)); stack->blkno = blkno; stack->buffer = ReadBuffer(btree->index, stack->blkno); stack->parent = NULL; stack->predictNumber = 1; ginTraverseLock(stack->buffer, btree->searchMode); return stack; }
/* * Locates leaf page contained tuple */ GinBtreeStack * ginFindLeafPage(GinBtree btree, GinBtreeStack *stack) { bool isfirst = TRUE; BlockNumber rootBlkno; if (!stack) stack = ginPrepareFindLeafPage(btree, GIN_ROOT_BLKNO); rootBlkno = stack->blkno; for (;;) { Page page; BlockNumber child; int access = GIN_SHARE; stack->off = InvalidOffsetNumber; page = BufferGetPage(stack->buffer); if (isfirst) { if (GinPageIsLeaf(page) && !btree->searchMode) access = GIN_EXCLUSIVE; isfirst = FALSE; } else access = ginTraverseLock(stack->buffer, btree->searchMode); /* * ok, page is correctly locked, we should check to move right .., * root never has a right link, so small optimization */ while (btree->fullScan == FALSE && stack->blkno != rootBlkno && btree->isMoveRight(btree, page)) { BlockNumber rightlink = GinPageGetOpaque(page)->rightlink; if (rightlink == InvalidBlockNumber) /* rightmost page */ break; stack->blkno = rightlink; LockBuffer(stack->buffer, GIN_UNLOCK); stack->buffer = ReleaseAndReadBuffer(stack->buffer, btree->index, stack->blkno); LockBuffer(stack->buffer, access); page = BufferGetPage(stack->buffer); } if (GinPageIsLeaf(page)) /* we found, return locked page */ return stack; /* now we have correct buffer, try to find child */ child = btree->findChildPage(btree, stack); LockBuffer(stack->buffer, GIN_UNLOCK); Assert(child != InvalidBlockNumber); Assert(stack->blkno != child); if (btree->searchMode) { /* in search mode we may forget path to leaf */ stack->blkno = child; stack->buffer = ReleaseAndReadBuffer(stack->buffer, btree->index, stack->blkno); } else { GinBtreeStack *ptr = (GinBtreeStack *) palloc(sizeof(GinBtreeStack)); ptr->parent = stack; stack = ptr; stack->blkno = child; stack->buffer = ReadBuffer(btree->index, stack->blkno); stack->predictNumber = 1; } } /* keep compiler happy */ return NULL; }
/* * Descend the tree to the leaf page that contains or would contain the key * we're searching for. The key should already be filled in 'btree', in * tree-type specific manner. If btree->fullScan is true, descends to the * leftmost leaf page. * * If 'searchmode' is false, on return stack->buffer is exclusively locked, * and the stack represents the full path to the root. Otherwise stack->buffer * is share-locked, and stack->parent is NULL. */ GinBtreeStack * ginFindLeafPage(GinBtree btree, bool searchMode) { GinBtreeStack *stack; stack = (GinBtreeStack *) palloc(sizeof(GinBtreeStack)); stack->blkno = btree->rootBlkno; stack->buffer = ReadBuffer(btree->index, btree->rootBlkno); stack->parent = NULL; stack->predictNumber = 1; for (;;) { Page page; BlockNumber child; int access; stack->off = InvalidOffsetNumber; page = BufferGetPage(stack->buffer); access = ginTraverseLock(stack->buffer, searchMode); /* * If we're going to modify the tree, finish any incomplete splits we * encounter on the way. */ if (!searchMode && GinPageIsIncompleteSplit(page)) ginFinishSplit(btree, stack, false, NULL); /* * ok, page is correctly locked, we should check to move right .., * root never has a right link, so small optimization */ while (btree->fullScan == FALSE && stack->blkno != btree->rootBlkno && btree->isMoveRight(btree, page)) { BlockNumber rightlink = GinPageGetOpaque(page)->rightlink; if (rightlink == InvalidBlockNumber) /* rightmost page */ break; stack->buffer = ginStepRight(stack->buffer, btree->index, access); stack->blkno = rightlink; page = BufferGetPage(stack->buffer); if (!searchMode && GinPageIsIncompleteSplit(page)) ginFinishSplit(btree, stack, false, NULL); } if (GinPageIsLeaf(page)) /* we found, return locked page */ return stack; /* now we have correct buffer, try to find child */ child = btree->findChildPage(btree, stack); LockBuffer(stack->buffer, GIN_UNLOCK); Assert(child != InvalidBlockNumber); Assert(stack->blkno != child); if (searchMode) { /* in search mode we may forget path to leaf */ stack->blkno = child; stack->buffer = ReleaseAndReadBuffer(stack->buffer, btree->index, stack->blkno); } else { GinBtreeStack *ptr = (GinBtreeStack *) palloc(sizeof(GinBtreeStack)); ptr->parent = stack; stack = ptr; stack->blkno = child; stack->buffer = ReadBuffer(btree->index, stack->blkno); stack->predictNumber = 1; } } }
/* * Scan through posting tree, delete empty tuples from leaf pages. * Also, this function collects empty subtrees (with all empty leafs). * For parents of these subtrees CleanUp lock is taken, then we call * ScanToDelete. This is done for every inner page, which points to * empty subtree. */ static bool ginVacuumPostingTreeLeaves(GinVacuumState *gvs, BlockNumber blkno, bool isRoot) { Buffer buffer; Page page; bool hasVoidPage = FALSE; MemoryContext oldCxt; buffer = ReadBufferExtended(gvs->index, MAIN_FORKNUM, blkno, RBM_NORMAL, gvs->strategy); page = BufferGetPage(buffer); ginTraverseLock(buffer, false); Assert(GinPageIsData(page)); if (GinPageIsLeaf(page)) { oldCxt = MemoryContextSwitchTo(gvs->tmpCxt); ginVacuumPostingTreeLeaf(gvs->index, buffer, gvs); MemoryContextSwitchTo(oldCxt); MemoryContextReset(gvs->tmpCxt); /* if root is a leaf page, we don't desire further processing */ if (GinDataLeafPageIsEmpty(page)) hasVoidPage = TRUE; UnlockReleaseBuffer(buffer); return hasVoidPage; } else { OffsetNumber i; bool hasEmptyChild = FALSE; bool hasNonEmptyChild = FALSE; OffsetNumber maxoff = GinPageGetOpaque(page)->maxoff; BlockNumber *children = palloc(sizeof(BlockNumber) * (maxoff + 1)); /* * Read all children BlockNumbers. Not sure it is safe if there are * many concurrent vacuums. */ for (i = FirstOffsetNumber; i <= maxoff; i++) { PostingItem *pitem = GinDataPageGetPostingItem(page, i); children[i] = PostingItemGetBlockNumber(pitem); } UnlockReleaseBuffer(buffer); for (i = FirstOffsetNumber; i <= maxoff; i++) { if (ginVacuumPostingTreeLeaves(gvs, children[i], FALSE)) hasEmptyChild = TRUE; else hasNonEmptyChild = TRUE; } pfree(children); vacuum_delay_point(); /* * All subtree is empty - just return TRUE to indicate that parent * must do a cleanup. Unless we are ROOT an there is way to go upper. */ if (hasEmptyChild && !hasNonEmptyChild && !isRoot) return TRUE; if (hasEmptyChild) { DataPageDeleteStack root, *ptr, *tmp; buffer = ReadBufferExtended(gvs->index, MAIN_FORKNUM, blkno, RBM_NORMAL, gvs->strategy); LockBufferForCleanup(buffer); memset(&root, 0, sizeof(DataPageDeleteStack)); root.leftBlkno = InvalidBlockNumber; root.isRoot = TRUE; ginScanToDelete(gvs, blkno, TRUE, &root, InvalidOffsetNumber); ptr = root.child; while (ptr) { tmp = ptr->child; pfree(ptr); ptr = tmp; } UnlockReleaseBuffer(buffer); } /* Here we have deleted all empty subtrees */ return FALSE; } }