diff --git a/src/backend/access/gist/gistvacuum.c b/src/backend/access/gist/gistvacuum.c index c458bfb565..06d94d2545 100644 --- a/src/backend/access/gist/gistvacuum.c +++ b/src/backend/access/gist/gistvacuum.c @@ -23,6 +23,190 @@ #include "storage/indexfsm.h" #include "storage/lmgr.h" +/* Lowest level of radix tree is represented by bitmap */ +typedef struct +{ + char data[256/8]; +} BlockSetLevel4Data; + +typedef BlockSetLevel4Data *BlockSetLevel4; + +/* statically typed inner level chunks points to ground level */ +typedef struct +{ + /* null references denote empty subtree */ + BlockSetLevel4 next[256]; +} BlockSetLevel3Data; + +typedef BlockSetLevel3Data *BlockSetLevel3; + +/* inner level points to another inner level */ +typedef struct +{ + BlockSetLevel3 next[256]; +} BlockSetLevel2Data; + +typedef BlockSetLevel2Data *BlockSetLevel2; + +/* Radix tree root */ +typedef struct +{ + BlockSetLevel2 next[256]; +} BlockSetData; + +typedef BlockSetData *BlockSet; + +/* multiplex block number into indexes of radix tree */ +#define BLOCKSET_SPLIT_BLKNO \ + int i1, i2, i3, i4, byte_no, byte_mask; \ + i4 = blkno % 256; \ + blkno /= 256; \ + i3 = blkno % 256; \ + blkno /= 256; \ + i2 = blkno % 256; \ + blkno /= 256; \ + i1 = blkno; \ + byte_no = i4 / 8; \ + byte_mask = 1 << (i4 % 8) \ + +/* indicate presence of block number in set */ +static +BlockSet blockset_set(BlockSet bs, BlockNumber blkno) +{ + BLOCKSET_SPLIT_BLKNO; + if (bs == NULL) + { + bs = palloc0(sizeof(BlockSetData)); + } + BlockSetLevel2 bs2 = bs->next[i1]; + if (bs2 == NULL) + { + bs2 = palloc0(sizeof(BlockSetLevel2Data)); + bs->next[i1] = bs2; + } + BlockSetLevel3 bs3 = bs2->next[i2]; + if (bs3 == NULL) + { + bs3 = palloc0(sizeof(BlockSetLevel3Data)); + bs2->next[i2] = bs3; + } + BlockSetLevel4 bs4 = bs3->next[i3]; + if (bs4 == NULL) + { + bs4 = palloc0(sizeof(BlockSetLevel4Data)); + bs3->next[i3] = bs4; + } + bs4->data[byte_no] = byte_mask | bs4->data[byte_no]; + return bs; +} + +/* Test presence of block in set */ +static +bool blockset_get(BlockNumber blkno, BlockSet bs) +{ + BLOCKSET_SPLIT_BLKNO; + if (bs == NULL) + return false; + BlockSetLevel2 bs2 = bs->next[i1]; + if (bs2 == NULL) + return false; + BlockSetLevel3 bs3 = bs2->next[i2]; + if (bs3 == NULL) + return false; + BlockSetLevel4 bs4 = bs3->next[i3]; + if (bs4 == NULL) + return false; + return (bs4->data[byte_no] & byte_mask); +} + +/* + * Find nearest block number in set no less than blkno + * Return InvalidBlockNumber if nothing to return + * If given InvalidBlockNumber - returns minimal element in set + */ +static +BlockNumber blockset_next(BlockSet bs, BlockNumber blkno) +{ + if (blkno == InvalidBlockNumber) + blkno = 0; /* equvalent to ++, left for clear code */ + else + blkno++; + + BLOCKSET_SPLIT_BLKNO; + + if (bs == NULL) + return InvalidBlockNumber; + for (; i1 < 256; i1++) + { + BlockSetLevel2 bs2 = bs->next[i1]; + if (!bs2) + continue; + for (; i2 < 256; i2++) + { + BlockSetLevel3 bs3 = bs2->next[i2]; + if (!bs3) + continue; + for (; i3 < 256; i3++) + { + BlockSetLevel4 bs4 = bs3->next[i3]; + if (!bs4) + continue; + for (; byte_no < 256 / 8; byte_no++) + { + if (!bs4->data[byte_no]) + continue; + while (byte_mask <= 0x70) + { + if ((byte_mask & bs4->data[byte_no]) == byte_mask) + { + i4 = byte_no * 8; + while (byte_mask >>= 1) i4++; + return i4 + 256 * (i3 + 256 * (i2 + 256 * i1)); + } + byte_mask <<= 1; + } + byte_mask = 1; + } + byte_no = 0; + } + i3 = 0; + } + i2 = 0; + } + return InvalidBlockNumber; +} + +/* free anything palloced */ +static +void blockset_free(BlockSet bs) +{ + BlockNumber blkno = 0; + BLOCKSET_SPLIT_BLKNO; + if (bs == NULL) + return; + for (; i1 < 256; i1++) + { + BlockSetLevel2 bs2 = bs->next[i1]; + if (!bs2) + continue; + for (; i2 < 256; i2++) + { + BlockSetLevel3 bs3 = bs2->next[i2]; + if (!bs3) + continue; + for (; i3 < 256; i3++) + { + BlockSetLevel4 bs4 = bs3->next[i3]; + if (bs4) + pfree(bs4); + } + pfree(bs3); + } + pfree(bs2); + } + pfree(bs); +} + /* Working state needed by gistbulkdelete */ typedef struct { @@ -34,8 +218,8 @@ typedef struct BlockNumber totFreePages; /* true total # of free pages */ BlockNumber emptyPages; - Bitmapset *internalPagesMap; - Bitmapset *emptyLeafPagesMap; + BlockSet internalPagesMap; + BlockSet emptyLeafPagesMap; } GistVacState; static void gistvacuumscan(IndexVacuumInfo *info, IndexBulkDeleteResult *stats, @@ -53,6 +237,7 @@ gistbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats, gistvacuumscan(info, stats, callback, callback_state); + return stats; } @@ -174,11 +359,6 @@ gistvacuumscan(IndexVacuumInfo *info, IndexBulkDeleteResult *stats, if (blkno >= num_pages) break; - if (!vstate.internalPagesMap) - vstate.internalPagesMap = bms_make_empty(num_pages); - if (!vstate.emptyLeafPagesMap) - vstate.emptyLeafPagesMap = bms_make_empty(num_pages); - /* Iterate over pages, then loop back to recheck length */ for (; blkno < num_pages; blkno++) gistvacuumpage(&vstate, blkno, blkno); @@ -206,11 +386,11 @@ gistvacuumscan(IndexVacuumInfo *info, IndexBulkDeleteResult *stats, /* rescan all inner pages to find those that has empty child pages */ if (vstate.emptyPages > 0) { - int x; + BlockNumber x; - x = -1; + x = InvalidBlockNumber; while (vstate.emptyPages > 0 && - (x = bms_next_member(vstate.internalPagesMap, x)) >= 0) + (x = blockset_next(vstate.internalPagesMap, x)) != InvalidBlockNumber) { Buffer buffer; Page page; @@ -248,7 +428,7 @@ gistvacuumscan(IndexVacuumInfo *info, IndexBulkDeleteResult *stats, idxtuple = (IndexTuple) PageGetItem(page, iid); /* if this page was not empty in previous scan - we do not consider it */ leafBlockNo = ItemPointerGetBlockNumber(&(idxtuple->t_tid)); - if (!bms_is_member(leafBlockNo, vstate.emptyLeafPagesMap)) + if (!blockset_get(leafBlockNo, vstate.emptyLeafPagesMap)) continue; leafBuffer = ReadBufferExtended(rel, MAIN_FORKNUM, leafBlockNo, @@ -321,8 +501,8 @@ gistvacuumscan(IndexVacuumInfo *info, IndexBulkDeleteResult *stats, } } - bms_free(vstate.emptyLeafPagesMap); - bms_free(vstate.internalPagesMap); + blockset_free(vstate.emptyLeafPagesMap); + blockset_free(vstate.internalPagesMap); } /* @@ -447,7 +627,7 @@ restart: nremain = maxoff - FirstOffsetNumber + 1; if (nremain == 0) { - vstate->emptyLeafPagesMap = bms_add_member(vstate->emptyLeafPagesMap, blkno); + vstate->emptyLeafPagesMap = blockset_set(vstate->emptyLeafPagesMap, blkno); vstate->emptyPages++; } else @@ -455,7 +635,7 @@ restart: } else { - vstate->internalPagesMap = bms_add_member(vstate->internalPagesMap, blkno); + vstate->internalPagesMap = blockset_set(vstate->internalPagesMap, blkno); } UnlockReleaseBuffer(buffer);