/* * PageIndexDeleteNoCompact * Delete the given items for an index page, and defragment the resulting * free space, but do not compact the item pointers array. * * itemnos is the array of tuples to delete; nitems is its size. maxIdxTuples * is the maximum number of tuples that can exist in a page. * * Unused items at the end of the array are removed. * * This is used for index AMs that require that existing TIDs of live tuples * remain unchanged. */ void PageIndexDeleteNoCompact(Page page, OffsetNumber *itemnos, int nitems) { PageHeader phdr = (PageHeader) page; LocationIndex pd_lower = phdr->pd_lower; LocationIndex pd_upper = phdr->pd_upper; LocationIndex pd_special = phdr->pd_special; int nline; bool empty; OffsetNumber offnum; int nextitm; /* * As with PageRepairFragmentation, paranoia seems justified. */ if (pd_lower < SizeOfPageHeaderData || pd_lower > pd_upper || pd_upper > pd_special || pd_special > BLCKSZ || pd_special != MAXALIGN(pd_special)) ereport(ERROR, (errcode(ERRCODE_DATA_CORRUPTED), errmsg("corrupted page pointers: lower = %u, upper = %u, special = %u", pd_lower, pd_upper, pd_special))); /* * Scan the existing item pointer array and mark as unused those that are * in our kill-list; make sure any non-interesting ones are marked unused * as well. */ nline = PageGetMaxOffsetNumber(page); empty = true; nextitm = 0; for (offnum = FirstOffsetNumber; offnum <= nline; offnum = OffsetNumberNext(offnum)) { ItemId lp; ItemLength itemlen; ItemOffset offset; lp = PageGetItemId(page, offnum); itemlen = ItemIdGetLength(lp); offset = ItemIdGetOffset(lp); if (ItemIdIsUsed(lp)) { if (offset < pd_upper || (offset + itemlen) > pd_special || offset != MAXALIGN(offset)) ereport(ERROR, (errcode(ERRCODE_DATA_CORRUPTED), errmsg("corrupted item pointer: offset = %u, length = %u", offset, (unsigned int) itemlen))); if (nextitm < nitems && offnum == itemnos[nextitm]) { /* this one is on our list to delete, so mark it unused */ ItemIdSetUnused(lp); nextitm++; } else if (ItemIdHasStorage(lp)) { /* This one's live -- must do the compaction dance */ empty = false; } else { /* get rid of this one too */ ItemIdSetUnused(lp); } } } /* this will catch invalid or out-of-order itemnos[] */ if (nextitm != nitems) elog(ERROR, "incorrect index offsets supplied"); if (empty) { /* Page is completely empty, so just reset it quickly */ phdr->pd_lower = SizeOfPageHeaderData; phdr->pd_upper = pd_special; } else { /* There are live items: need to compact the page the hard way */ itemIdSortData itemidbase[MaxOffsetNumber]; itemIdSort itemidptr; int i; Size totallen; /* * Scan the page taking note of each item that we need to preserve. * This includes both live items (those that contain data) and * interspersed unused ones. It's critical to preserve these unused * items, because otherwise the offset numbers for later live items * would change, which is not acceptable. Unused items might get used * again later; that is fine. */ itemidptr = itemidbase; totallen = 0; PageClearHasFreeLinePointers(page); for (i = 0; i < nline; i++) { ItemId lp; itemidptr->offsetindex = i; lp = PageGetItemId(page, i + 1); if (ItemIdHasStorage(lp)) { itemidptr->itemoff = ItemIdGetOffset(lp); itemidptr->alignedlen = MAXALIGN(ItemIdGetLength(lp)); totallen += itemidptr->alignedlen; itemidptr++; } else { PageSetHasFreeLinePointers(page); ItemIdSetUnused(lp); } } nline = itemidptr - itemidbase; /* By here, there are exactly nline elements in itemidbase array */ if (totallen > (Size) (pd_special - pd_lower)) ereport(ERROR, (errcode(ERRCODE_DATA_CORRUPTED), errmsg("corrupted item lengths: total %u, available space %u", (unsigned int) totallen, pd_special - pd_lower))); /* * Defragment the data areas of each tuple, being careful to preserve * each item's position in the linp array. */ compactify_tuples(itemidbase, nline, page); } }
/* * PageRepairFragmentation * * Frees fragmented space on a page. * It doesn't remove unused line pointers! Please don't change this. * * This routine is usable for heap pages only, but see PageIndexMultiDelete. * * As a side effect, the page's PD_HAS_FREE_LINES hint bit is updated. */ void PageRepairFragmentation(Page page) { Offset pd_lower = ((PageHeader) page)->pd_lower; Offset pd_upper = ((PageHeader) page)->pd_upper; Offset pd_special = ((PageHeader) page)->pd_special; ItemId lp; int nline, nstorage, nunused; int i; Size totallen; /* * It's worth the trouble to be more paranoid here than in most places, * because we are about to reshuffle data in (what is usually) a shared * disk buffer. If we aren't careful then corrupted pointers, lengths, * etc could cause us to clobber adjacent disk buffers, spreading the data * loss further. So, check everything. */ if (pd_lower < SizeOfPageHeaderData || pd_lower > pd_upper || pd_upper > pd_special || pd_special > BLCKSZ || pd_special != MAXALIGN(pd_special)) ereport(ERROR, (errcode(ERRCODE_DATA_CORRUPTED), errmsg("corrupted page pointers: lower = %u, upper = %u, special = %u", pd_lower, pd_upper, pd_special))); nline = PageGetMaxOffsetNumber(page); nunused = nstorage = 0; for (i = FirstOffsetNumber; i <= nline; i++) { lp = PageGetItemId(page, i); if (ItemIdIsUsed(lp)) { if (ItemIdHasStorage(lp)) nstorage++; } else { /* Unused entries should have lp_len = 0, but make sure */ ItemIdSetUnused(lp); nunused++; } } if (nstorage == 0) { /* Page is completely empty, so just reset it quickly */ ((PageHeader) page)->pd_upper = pd_special; } else { /* Need to compact the page the hard way */ itemIdSortData itemidbase[MaxHeapTuplesPerPage]; itemIdSort itemidptr = itemidbase; totallen = 0; for (i = 0; i < nline; i++) { lp = PageGetItemId(page, i + 1); if (ItemIdHasStorage(lp)) { itemidptr->offsetindex = i; itemidptr->itemoff = ItemIdGetOffset(lp); if (itemidptr->itemoff < (int) pd_upper || itemidptr->itemoff >= (int) pd_special) ereport(ERROR, (errcode(ERRCODE_DATA_CORRUPTED), errmsg("corrupted item pointer: %u", itemidptr->itemoff))); itemidptr->alignedlen = MAXALIGN(ItemIdGetLength(lp)); totallen += itemidptr->alignedlen; itemidptr++; } } if (totallen > (Size) (pd_special - pd_lower)) ereport(ERROR, (errcode(ERRCODE_DATA_CORRUPTED), errmsg("corrupted item lengths: total %u, available space %u", (unsigned int) totallen, pd_special - pd_lower))); compactify_tuples(itemidbase, nstorage, page); } /* Set hint bit for PageAddItem */ if (nunused > 0) PageSetHasFreeLinePointers(page); else PageClearHasFreeLinePointers(page); }
/* * PageRepairFragmentation * * Frees fragmented space on a page. * It doesn't remove unused line pointers! Please don't change this. * * This routine is usable for heap pages only, but see PageIndexMultiDelete. * * Returns number of unused line pointers on page. If "unused" is not NULL * then the unused[] array is filled with indexes of unused line pointers. */ int PageRepairFragmentation(Page page, OffsetNumber *unused) { Offset pd_lower = ((PageHeader) page)->pd_lower; Offset pd_upper = ((PageHeader) page)->pd_upper; Offset pd_special = ((PageHeader) page)->pd_special; itemIdSort itemidbase, itemidptr; ItemId lp; int nline, nused; int i; Size totallen; Offset upper; /* * It's worth the trouble to be more paranoid here than in most places, * because we are about to reshuffle data in (what is usually) a shared * disk buffer. If we aren't careful then corrupted pointers, lengths, * etc could cause us to clobber adjacent disk buffers, spreading the data * loss further. So, check everything. */ if (pd_lower < SizeOfPageHeaderData || pd_lower > pd_upper || pd_upper > pd_special || pd_special > BLCKSZ || pd_special != MAXALIGN(pd_special)) ereport(ERROR, (errcode(ERRCODE_DATA_CORRUPTED), errmsg("corrupted page pointers: lower = %u, upper = %u, special = %u", pd_lower, pd_upper, pd_special), errSendAlert(true))); nline = PageGetMaxOffsetNumber(page); nused = 0; for (i = 0; i < nline; i++) { lp = PageGetItemId(page, i + 1); if (ItemIdDeleted(lp)) /* marked for deletion */ lp->lp_flags &= ~(LP_USED | LP_DELETE); if (ItemIdIsUsed(lp)) nused++; else if (unused) unused[i - nused] = (OffsetNumber) i; } if (nused == 0) { /* Page is completely empty, so just reset it quickly */ for (i = 0; i < nline; i++) { lp = PageGetItemId(page, i + 1); lp->lp_len = 0; /* indicate unused & deallocated */ } ((PageHeader) page)->pd_upper = pd_special; } else { /* nused != 0 */ /* Need to compact the page the hard way */ itemidbase = (itemIdSort) palloc(sizeof(itemIdSortData) * nused); itemidptr = itemidbase; totallen = 0; for (i = 0; i < nline; i++) { lp = PageGetItemId(page, i + 1); if (ItemIdIsUsed(lp)) { itemidptr->offsetindex = i; itemidptr->itemoff = ItemIdGetOffset(lp); if (itemidptr->itemoff < (int) pd_upper || itemidptr->itemoff >= (int) pd_special) ereport(ERROR, (errcode(ERRCODE_DATA_CORRUPTED), errmsg("corrupted item pointer: %u", itemidptr->itemoff), errSendAlert(true))); itemidptr->alignedlen = MAXALIGN(ItemIdGetLength(lp)); totallen += itemidptr->alignedlen; itemidptr++; } else { lp->lp_len = 0; /* indicate unused & deallocated */ } } if (totallen > (Size) (pd_special - pd_lower)) ereport(ERROR, (errcode(ERRCODE_DATA_CORRUPTED), errmsg("corrupted item lengths: total %u, available space %u", (unsigned int) totallen, pd_special - pd_lower), errSendAlert(true))); /* sort itemIdSortData array into decreasing itemoff order */ qsort((char *) itemidbase, nused, sizeof(itemIdSortData), itemoffcompare); /* compactify page */ upper = pd_special; for (i = 0, itemidptr = itemidbase; i < nused; i++, itemidptr++) { lp = PageGetItemId(page, itemidptr->offsetindex + 1); upper -= itemidptr->alignedlen; memmove((char *) page + upper, (char *) page + itemidptr->itemoff, itemidptr->alignedlen); lp->lp_off = upper; } ((PageHeader) page)->pd_upper = upper; pfree(itemidbase); } /* Set hint bit for PageAddItem */ if (nused < nline) PageSetHasFreeLinePointers(page); else PageClearHasFreeLinePointers(page); return (nline - nused); }