/* * Inserts only one entry to the index, but it can add more than 1 ItemPointer. */ static void ginEntryInsert(Relation index, GinState *ginstate, Datum value, ItemPointerData *items, uint32 nitem, bool isBuild) { GinBtreeData btree; GinBtreeStack *stack; IndexTuple itup; Page page; prepareEntryScan(&btree, index, value, ginstate); stack = ginFindLeafPage(&btree, NULL); page = BufferGetPage(stack->buffer); if (btree.findItem(&btree, stack)) { /* found entry */ itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, stack->off)); if (GinIsPostingTree(itup)) { /* lock root of posting tree */ GinPostingTreeScan *gdi; BlockNumber rootPostingTree = GinGetPostingTree(itup); /* release all stack */ LockBuffer(stack->buffer, GIN_UNLOCK); freeGinBtreeStack(stack); /* insert into posting tree */ gdi = prepareScanPostingTree(index, rootPostingTree, FALSE); gdi->btree.isBuild = isBuild; insertItemPointer(gdi, items, nitem); return; } itup = addItemPointersToTuple(index, ginstate, stack, itup, items, nitem, isBuild); btree.isDelete = TRUE; } else { /* We suppose, that tuple can store at list one itempointer */ itup = GinFormTuple(ginstate, value, items, 1); if (itup == NULL || IndexTupleSize(itup) >= GinMaxItemSize) elog(ERROR, "huge tuple"); if (nitem > 1) { IndexTuple previtup = itup; itup = addItemPointersToTuple(index, ginstate, stack, previtup, items + 1, nitem - 1, isBuild); pfree(previtup); } } btree.entry = itup; ginInsertValue(&btree, stack); pfree(itup); }
/* * Adds array of item pointers to tuple's posting list or * creates posting tree and tuple pointed to tree in a case * of not enough space. Max size of tuple is defined in * GinFormTuple(). */ static IndexTuple addItemPointersToTuple(Relation index, GinState *ginstate, GinBtreeStack *stack, IndexTuple old, ItemPointerData *items, uint32 nitem, bool isBuild) { Datum key = gin_index_getattr(ginstate, old); OffsetNumber attnum = gintuple_get_attrnum(ginstate, old); IndexTuple res = GinFormTuple(index, ginstate, attnum, key, NULL, nitem + GinGetNPosting(old), false); if (res) { /* good, small enough */ uint32 newnitem; newnitem = MergeItemPointers(GinGetPosting(res), GinGetPosting(old), GinGetNPosting(old), items, nitem); /* merge might have eliminated some duplicate items */ GinShortenTuple(res, newnitem); } else { BlockNumber postingRoot; GinPostingTreeScan *gdi; /* posting list becomes big, so we need to make posting's tree */ res = GinFormTuple(index, ginstate, attnum, key, NULL, 0, true); postingRoot = createPostingTree(index, GinGetPosting(old), GinGetNPosting(old)); GinSetPostingTree(res, postingRoot); gdi = prepareScanPostingTree(index, postingRoot, FALSE); gdi->btree.isBuild = isBuild; insertItemPointer(gdi, items, nitem); pfree(gdi); } return res; }
/* * Adds array of item pointers to tuple's posting list or * creates posting tree and tuple pointed to tree in a case * of not enough space. Max size of tuple is defined in * GinFormTuple(). */ static IndexTuple addItemPointersToTuple(Relation index, GinState *ginstate, GinBtreeStack *stack, IndexTuple old, ItemPointerData *items, uint32 nitem, bool isBuild) { bool isnull; Datum key = index_getattr(old, FirstOffsetNumber, ginstate->tupdesc, &isnull); IndexTuple res = GinFormTuple(ginstate, key, NULL, nitem + GinGetNPosting(old)); if (res) { /* good, small enough */ MergeItemPointers(GinGetPosting(res), GinGetPosting(old), GinGetNPosting(old), items, nitem ); GinSetNPosting(res, nitem + GinGetNPosting(old)); } else { BlockNumber postingRoot; GinPostingTreeScan *gdi; /* posting list becomes big, so we need to make posting's tree */ res = GinFormTuple(ginstate, key, NULL, 0); postingRoot = createPostingTree(index, GinGetPosting(old), GinGetNPosting(old)); GinSetPostingTree(res, postingRoot); gdi = prepareScanPostingTree(index, postingRoot, FALSE); gdi->btree.isBuild = isBuild; insertItemPointer(gdi, items, nitem); pfree(gdi); } return res; }
/* * Start* functions setup state of searches: find correct buffer and locks it, * Stop* functions unlock buffer (but don't release!) */ static void startScanEntry(Relation index, GinState *ginstate, GinScanEntry entry) { GinBtreeData btreeEntry; GinBtreeStack *stackEntry; Page page; bool needUnlock = TRUE; if (entry->master != NULL) { entry->isFinished = entry->master->isFinished; return; } /* * We should find entry, and begin scan of posting tree * or just store posting list in memory */ prepareEntryScan(&btreeEntry, index, entry->entry, ginstate); btreeEntry.searchMode = TRUE; stackEntry = ginFindLeafPage(&btreeEntry, NULL); page = BufferGetPage(stackEntry->buffer); entry->isFinished = TRUE; entry->buffer = InvalidBuffer; entry->offset = InvalidOffsetNumber; entry->list = NULL; entry->nlist = 0; entry->reduceResult = FALSE; entry->predictNumberResult = 0; 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; LockBuffer(stackEntry->buffer, GIN_UNLOCK); needUnlock = FALSE; gdi = prepareScanPostingTree(index, rootPostingTree, TRUE); entry->buffer = scanBeginPostingTree(gdi); /* * We keep buffer pinned because we need to prevent deletition * 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); }