📄 freespace.c
字号:
if (!found) { /* New hashtable entry, initialize it (hash_search set the key) */ fsmrel->isIndex = false; /* until we learn different */ fsmrel->avgRequest = INITIAL_AVERAGE; fsmrel->interestingPages = 0; fsmrel->firstChunk = -1; /* no space allocated */ fsmrel->storedPages = 0; fsmrel->nextPage = 0; /* Discard lowest-priority existing rel, if we are over limit */ if (FreeSpaceMap->numRels >= MaxFSMRelations) delete_fsm_rel(FreeSpaceMap->usageListTail); /* Add new entry at front of LRU list */ link_fsm_rel_usage(fsmrel); fsmrel->nextPhysical = NULL; /* not in physical-storage list */ fsmrel->priorPhysical = NULL; FreeSpaceMap->numRels++; /* sumRequests is unchanged because request must be zero */ } else { /* Existing entry, move to front of LRU list */ if (fsmrel->priorUsage != NULL) { unlink_fsm_rel_usage(fsmrel); link_fsm_rel_usage(fsmrel); } } return fsmrel;}/* * Remove an existing FSMRelation entry. */static voiddelete_fsm_rel(FSMRelation *fsmrel){ FSMRelation *result; FreeSpaceMap->sumRequests -= fsm_calc_request(fsmrel); unlink_fsm_rel_usage(fsmrel); unlink_fsm_rel_storage(fsmrel); FreeSpaceMap->numRels--; result = (FSMRelation *) hash_search(FreeSpaceMapRelHash, (void *) &(fsmrel->key), HASH_REMOVE, NULL); if (!result) elog(ERROR, "FreeSpaceMap hashtable corrupted");}/* * Reallocate space for a FSMRelation. * * This is shared code for RecordRelationFreeSpace and RecordIndexFreeSpace. * The return value is the actual new allocation, in chunks. */static intrealloc_fsm_rel(FSMRelation *fsmrel, BlockNumber interestingPages, bool isIndex){ int myRequest; int myAlloc; int curAlloc; /* * Delete any existing entries, and update request status. */ fsmrel->storedPages = 0; FreeSpaceMap->sumRequests -= fsm_calc_request(fsmrel); fsmrel->interestingPages = interestingPages; fsmrel->isIndex = isIndex; myRequest = fsm_calc_request(fsmrel); FreeSpaceMap->sumRequests += myRequest; myAlloc = fsm_calc_target_allocation(myRequest); /* * Need to reallocate space if (a) my target allocation is more than my * current allocation, AND (b) my actual immediate need (myRequest+1 * chunks) is more than my current allocation. Otherwise just store the * new data in-place. */ curAlloc = fsm_current_allocation(fsmrel); if (myAlloc > curAlloc && (myRequest + 1) > curAlloc && interestingPages > 0) { /* Remove entry from storage list, and compact */ unlink_fsm_rel_storage(fsmrel); compact_fsm_storage(); /* Reattach to end of storage list */ link_fsm_rel_storage(fsmrel); /* And allocate storage */ fsmrel->firstChunk = FreeSpaceMap->usedChunks; FreeSpaceMap->usedChunks += myAlloc; curAlloc = myAlloc; /* Watch out for roundoff error */ if (FreeSpaceMap->usedChunks > FreeSpaceMap->totalChunks) { FreeSpaceMap->usedChunks = FreeSpaceMap->totalChunks; curAlloc = FreeSpaceMap->totalChunks - fsmrel->firstChunk; } } return curAlloc;}/* * Link a FSMRelation into the LRU list (always at the head). */static voidlink_fsm_rel_usage(FSMRelation *fsmrel){ fsmrel->priorUsage = NULL; fsmrel->nextUsage = FreeSpaceMap->usageList; FreeSpaceMap->usageList = fsmrel; if (fsmrel->nextUsage != NULL) fsmrel->nextUsage->priorUsage = fsmrel; else FreeSpaceMap->usageListTail = fsmrel;}/* * Delink a FSMRelation from the LRU list. */static voidunlink_fsm_rel_usage(FSMRelation *fsmrel){ if (fsmrel->priorUsage != NULL) fsmrel->priorUsage->nextUsage = fsmrel->nextUsage; else FreeSpaceMap->usageList = fsmrel->nextUsage; if (fsmrel->nextUsage != NULL) fsmrel->nextUsage->priorUsage = fsmrel->priorUsage; else FreeSpaceMap->usageListTail = fsmrel->priorUsage; /* * We don't bother resetting fsmrel's links, since it's about to be * deleted or relinked at the head. */}/* * Link a FSMRelation into the storage-order list (always at the tail). */static voidlink_fsm_rel_storage(FSMRelation *fsmrel){ fsmrel->nextPhysical = NULL; fsmrel->priorPhysical = FreeSpaceMap->lastRel; if (FreeSpaceMap->lastRel != NULL) FreeSpaceMap->lastRel->nextPhysical = fsmrel; else FreeSpaceMap->firstRel = fsmrel; FreeSpaceMap->lastRel = fsmrel;}/* * Delink a FSMRelation from the storage-order list, if it's in it. */static voidunlink_fsm_rel_storage(FSMRelation *fsmrel){ if (fsmrel->priorPhysical != NULL || FreeSpaceMap->firstRel == fsmrel) { if (fsmrel->priorPhysical != NULL) fsmrel->priorPhysical->nextPhysical = fsmrel->nextPhysical; else FreeSpaceMap->firstRel = fsmrel->nextPhysical; if (fsmrel->nextPhysical != NULL) fsmrel->nextPhysical->priorPhysical = fsmrel->priorPhysical; else FreeSpaceMap->lastRel = fsmrel->priorPhysical; } /* mark as not in list, since we may not put it back immediately */ fsmrel->nextPhysical = NULL; fsmrel->priorPhysical = NULL; /* Also mark it as having no storage */ fsmrel->firstChunk = -1; fsmrel->storedPages = 0;}/* * Look to see if a page with at least the specified amount of space is * available in the given FSMRelation. If so, return its page number, * and advance the nextPage counter so that the next inquiry will return * a different page if possible; also update the entry to show that the * requested space is not available anymore. Return InvalidBlockNumber * if no success. */static BlockNumberfind_free_space(FSMRelation *fsmrel, Size spaceNeeded){ FSMPageData *info; int pagesToCheck, /* outer loop counter */ pageIndex; /* current page index */ if (fsmrel->isIndex) elog(ERROR, "find_free_space called for an index relation"); info = (FSMPageData *) (FreeSpaceMap->arena + fsmrel->firstChunk * CHUNKBYTES); pageIndex = fsmrel->nextPage; /* Last operation may have left nextPage pointing past end */ if (pageIndex >= fsmrel->storedPages) pageIndex = 0; for (pagesToCheck = fsmrel->storedPages; pagesToCheck > 0; pagesToCheck--) { FSMPageData *page = info + pageIndex; Size spaceAvail = FSMPageGetSpace(page); /* Check this page */ if (spaceAvail >= spaceNeeded) { /* * Found what we want --- adjust the entry, and update nextPage. */ FSMPageSetSpace(page, spaceAvail - spaceNeeded); fsmrel->nextPage = pageIndex + 1; return FSMPageGetPageNum(page); } /* Advance pageIndex, wrapping around if needed */ if (++pageIndex >= fsmrel->storedPages) pageIndex = 0; } return InvalidBlockNumber; /* nothing found */}/* * As above, but for index case --- we only deal in whole pages. */static BlockNumberfind_index_free_space(FSMRelation *fsmrel){ IndexFSMPageData *info; BlockNumber result; /* * If isIndex isn't set, it could be that RecordIndexFreeSpace() has never * yet been called on this relation, and we're still looking at the * default setting from create_fsm_rel(). If so, just act as though * there's no space. */ if (!fsmrel->isIndex) { if (fsmrel->storedPages == 0) return InvalidBlockNumber; elog(ERROR, "find_index_free_space called for a non-index relation"); } /* * For indexes, there's no need for the nextPage state variable; we just * remove and return the first available page. (We could save cycles here * by returning the last page, but it seems better to encourage re-use of * lower-numbered pages.) */ if (fsmrel->storedPages <= 0) return InvalidBlockNumber; /* no pages available */ info = (IndexFSMPageData *) (FreeSpaceMap->arena + fsmrel->firstChunk * CHUNKBYTES); result = IndexFSMPageGetPageNum(info); fsmrel->storedPages--; memmove(info, info + 1, fsmrel->storedPages * sizeof(IndexFSMPageData)); return result;}/* * fsm_record_free_space - guts of RecordFreeSpace operation (now only * provided as part of RecordAndGetPageWithFreeSpace). */static voidfsm_record_free_space(FSMRelation *fsmrel, BlockNumber page, Size spaceAvail){ int pageIndex; if (fsmrel->isIndex) elog(ERROR, "fsm_record_free_space called for an index relation"); if (lookup_fsm_page_entry(fsmrel, page, &pageIndex)) { /* Found an existing entry for page; update it */ FSMPageData *info; info = (FSMPageData *) (FreeSpaceMap->arena + fsmrel->firstChunk * CHUNKBYTES); info += pageIndex; FSMPageSetSpace(info, spaceAvail); } else { /* * No existing entry; ignore the call. We used to add the page to the * FSM --- but in practice, if the page hasn't got enough space to * satisfy the caller who's kicking it back to us, then it's probably * uninteresting to everyone else as well. */ }}/* * Look for an entry for a specific page (block number) in a FSMRelation. * Returns TRUE if a matching entry exists, else FALSE. * * The output argument *outPageIndex is set to indicate where the entry exists * (if TRUE result) or could be inserted (if FALSE result). */static boollookup_fsm_page_entry(FSMRelation *fsmrel, BlockNumber page, int *outPageIndex){ /* Check for empty relation */ if (fsmrel->storedPages <= 0) { *outPageIndex = 0; return false; } /* Do binary search */ if (fsmrel->isIndex) { IndexFSMPageData *info; int low, high; info = (IndexFSMPageData *) (FreeSpaceMap->arena + fsmrel->firstChunk * CHUNKBYTES); low = 0; high = fsmrel->storedPages - 1; while (low <= high) { int middle; BlockNumber probe; middle = low + (high - low) / 2; probe = IndexFSMPageGetPageNum(info + middle); if (probe == page) { *outPageIndex = middle; return true; } else if (probe < page) low = middle + 1; else high = middle - 1; } *outPageIndex = low; return false; } else { FSMPageData *info; int low, high; info = (FSMPageData *) (FreeSpaceMap->arena + fsmrel->firstChunk * CHUNKBYTES); low = 0; high = fsmrel->storedPages - 1; while (low <= high) { int middle; BlockNumber probe; middle = low + (high - low) / 2; probe = FSMPageGetPageNum(info + middle); if (probe == page) { *outPageIndex = middle; return true; } else if (probe < page) low = middle + 1; else high = middle - 1; } *outPageIndex = low; return false; }}/* * Re-pack the FSM storage arena, dropping data if necessary to meet the * current allocation target for each relation. At conclusion, all available * space in the arena will be coalesced at the end. */static voidcompact_fsm_storage(void){ int nextChunkIndex = 0; bool did_push = false; FSMRelation *fsmrel; for (fsmrel = FreeSpaceMap->firstRel; fsmrel != NULL; fsmrel = fsmrel->nextPhysical) { int newAlloc; int newAllocPages; int newChunkIndex; int oldChunkIndex; int curChunks; char *newLocation; char *oldLocation; /* * Calculate target allocation, make sure we don't overrun due to * roundoff error */ newAlloc = fsm_calc_target_allocation(fsm_calc_request(fsmrel)); if (newAlloc > FreeSpaceMap->totalChunks - nextChunkIndex) newAlloc = FreeSpaceMap->totalChunks - nextChunkIndex; if (fsmrel->isIndex) newAllocPages = newAlloc * INDEXCHUNKPAGES; else newAllocPages = newAlloc * CHUNKPAGES; /* * Determine current size, current and new locations */ curChunks = fsm_current_chunks(fsmrel); oldChunkIndex = fsmrel->firstChunk; oldLocation = FreeSpaceMap->arena + oldChunkIndex * CHUNKBYTES; newChunkIndex = nextChunkIndex; newLocation = FreeSpaceMap->arena + newChunkIndex * CHUNKBYTES; /* * It's possible that we have to move data down, not up, if the * allocations of previous rels expanded. This normally means that * our allocation expanded too (or at least got no worse), and ditto * for later rels. So there should be room to move all our data down * without dropping any --- but we might have to push down following * rels to acquire the room. We don't want to do the push more than * once, so pack everything against the end of the arena if so. * * In corner cases where we are on the short end of a roundoff choice * that we were formerly on the long end of, it's possible that we * have to move down and compress our data too. In fact, even after * pushing down the following rels, there might not be as much space * as we computed for this rel above --- that would imply that some * following rel(s) are also on the losing end of roundoff choices. We * could handle this fairly by doing the per-rel compactions * out-of-order, but that seems like way too much complexity to deal * with a very infrequent corner case. Instead, we simply drop pages * from the end of the current rel's data until it fits. */ if (newChunkIndex > oldChunkIndex) { int limitChunkIndex; if (newAllocPages < fsmrel->storedPages) { /* move and compress --- just drop excess pages */ fsmrel->storedPages = newAllocPages; curChunks = fsm_current_chunks(fsmrel); } /* is there enough space? */ if (fsmrel->nextPhysical != NULL) limitChunkIndex = fsmrel->nextPhysical->firstChunk; else limitChunkIndex = FreeSpaceMap->totalChunks; if (newChunkIndex + curChunks > limitChunkIndex) { /* not enough space, push down following rels */ if (!did_push) { push_fsm_rels_after(fsmrel); did_push = true;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -