pageinspect: Fix hash_bitmap_info not to read the underlying page.

It did that to verify that the page was an overflow page rather than
anything else, but that means that checking the status of all the
overflow bits requires reading the entire index.  So don't do that.
The new code validates that the page is not a primary bucket page
or bitmap page by looking at the metapage, so that using this on
large numbers of pages can be reasonably efficient.

Ashutosh Sharma, per a complaint from me, and with further
modifications by me.
This commit is contained in:
Robert Haas 2017-02-09 14:02:58 -05:00
parent 86d911ec0f
commit fc8219dc54
3 changed files with 53 additions and 40 deletions

View File

@ -30,23 +30,17 @@ hash_page_type | bitmap
SELECT hash_page_type(get_raw_page('test_hash_a_idx', 6)); SELECT hash_page_type(get_raw_page('test_hash_a_idx', 6));
ERROR: block number 6 is out of range for relation "test_hash_a_idx" ERROR: block number 6 is out of range for relation "test_hash_a_idx"
SELECT * FROM hash_bitmap_info('test_hash_a_idx', 0); SELECT * FROM hash_bitmap_info('test_hash_a_idx', 0);
ERROR: page is not an overflow page ERROR: invalid overflow block number 0
DETAIL: Expected 00000001, got 00000008.
SELECT * FROM hash_bitmap_info('test_hash_a_idx', 1); SELECT * FROM hash_bitmap_info('test_hash_a_idx', 1);
ERROR: page is not an overflow page ERROR: invalid overflow block number 1
DETAIL: Expected 00000001, got 00000002.
SELECT * FROM hash_bitmap_info('test_hash_a_idx', 2); SELECT * FROM hash_bitmap_info('test_hash_a_idx', 2);
ERROR: page is not an overflow page ERROR: invalid overflow block number 2
DETAIL: Expected 00000001, got 00000002.
SELECT * FROM hash_bitmap_info('test_hash_a_idx', 3); SELECT * FROM hash_bitmap_info('test_hash_a_idx', 3);
ERROR: page is not an overflow page ERROR: invalid overflow block number 3
DETAIL: Expected 00000001, got 00000002.
SELECT * FROM hash_bitmap_info('test_hash_a_idx', 4); SELECT * FROM hash_bitmap_info('test_hash_a_idx', 4);
ERROR: page is not an overflow page ERROR: invalid overflow block number 4
DETAIL: Expected 00000001, got 00000002.
SELECT * FROM hash_bitmap_info('test_hash_a_idx', 5); SELECT * FROM hash_bitmap_info('test_hash_a_idx', 5);
ERROR: page is not an overflow page ERROR: invalid overflow block number 5
DETAIL: Expected 00000001, got 00000004.
SELECT magic, version, ntuples, bsize, bmsize, bmshift, maxbucket, highmask, SELECT magic, version, ntuples, bsize, bmsize, bmshift, maxbucket, highmask,
lowmask, ovflpoint, firstfree, nmaps, procid, spares, mapp FROM lowmask, ovflpoint, firstfree, nmaps, procid, spares, mapp FROM
hash_metapage_info(get_raw_page('test_hash_a_idx', 0)); hash_metapage_info(get_raw_page('test_hash_a_idx', 0));

View File

@ -380,21 +380,22 @@ hash_bitmap_info(PG_FUNCTION_ARGS)
Oid indexRelid = PG_GETARG_OID(0); Oid indexRelid = PG_GETARG_OID(0);
uint64 ovflblkno = PG_GETARG_INT64(1); uint64 ovflblkno = PG_GETARG_INT64(1);
HashMetaPage metap; HashMetaPage metap;
Buffer buf, Buffer metabuf,
metabuf; mapbuf;
BlockNumber bitmapblkno; BlockNumber bitmapblkno;
Page page; Page mappage;
bool bit = false; bool bit = false;
HashPageOpaque opaque;
TupleDesc tupleDesc; TupleDesc tupleDesc;
Relation indexRel; Relation indexRel;
uint32 ovflbitno; uint32 ovflbitno;
int32 bitmappage, int32 bitmappage,
bitmapbit; bitmapbit;
HeapTuple tuple; HeapTuple tuple;
int j; int i,
j;
Datum values[3]; Datum values[3];
bool nulls[3]; bool nulls[3];
uint32 *freep;
if (!superuser()) if (!superuser())
ereport(ERROR, ereport(ERROR,
@ -418,30 +419,30 @@ hash_bitmap_info(PG_FUNCTION_ARGS)
errmsg("block number " UINT64_FORMAT " is out of range for relation \"%s\"", errmsg("block number " UINT64_FORMAT " is out of range for relation \"%s\"",
ovflblkno, RelationGetRelationName(indexRel)))); ovflblkno, RelationGetRelationName(indexRel))));
buf = ReadBufferExtended(indexRel, MAIN_FORKNUM, (BlockNumber) ovflblkno,
RBM_NORMAL, NULL);
LockBuffer(buf, BUFFER_LOCK_SHARE);
_hash_checkpage(indexRel, buf, LH_PAGE_TYPE);
page = BufferGetPage(buf);
opaque = (HashPageOpaque) PageGetSpecialPointer(page);
if (opaque->hasho_flag != LH_OVERFLOW_PAGE)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("page is not an overflow page"),
errdetail("Expected %08x, got %08x.",
LH_OVERFLOW_PAGE, opaque->hasho_flag)));
if (BlockNumberIsValid(opaque->hasho_prevblkno))
bit = true;
UnlockReleaseBuffer(buf);
/* Read the metapage so we can determine which bitmap page to use */ /* Read the metapage so we can determine which bitmap page to use */
metabuf = _hash_getbuf(indexRel, HASH_METAPAGE, HASH_READ, LH_META_PAGE); metabuf = _hash_getbuf(indexRel, HASH_METAPAGE, HASH_READ, LH_META_PAGE);
metap = HashPageGetMeta(BufferGetPage(metabuf)); metap = HashPageGetMeta(BufferGetPage(metabuf));
/* Identify overflow bit number */ /*
* Reject attempt to read the bit for a metapage or bitmap page; this is
* only meaningful for overflow pages.
*/
if (ovflblkno == 0)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("invalid overflow block number %u",
(BlockNumber) ovflblkno)));
for (i = 0; i < metap->hashm_nmaps; i++)
if (metap->hashm_mapp[i] == ovflblkno)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("invalid overflow block number %u",
(BlockNumber) ovflblkno)));
/*
* Identify overflow bit number. This will error out for primary bucket
* pages, and we've already rejected the metapage and bitmap pages above.
*/
ovflbitno = _hash_ovflblkno_to_bitno(metap, (BlockNumber) ovflblkno); ovflbitno = _hash_ovflblkno_to_bitno(metap, (BlockNumber) ovflblkno);
bitmappage = ovflbitno >> BMPG_SHIFT(metap); bitmappage = ovflbitno >> BMPG_SHIFT(metap);
@ -450,12 +451,21 @@ hash_bitmap_info(PG_FUNCTION_ARGS)
if (bitmappage >= metap->hashm_nmaps) if (bitmappage >= metap->hashm_nmaps)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE), (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("invalid overflow bit number %u", ovflbitno))); errmsg("invalid overflow block number %u",
(BlockNumber) ovflblkno)));
bitmapblkno = metap->hashm_mapp[bitmappage]; bitmapblkno = metap->hashm_mapp[bitmappage];
_hash_relbuf(indexRel, metabuf); _hash_relbuf(indexRel, metabuf);
/* Check the status of bitmap bit for overflow page */
mapbuf = _hash_getbuf(indexRel, bitmapblkno, HASH_READ, LH_BITMAP_PAGE);
mappage = BufferGetPage(mapbuf);
freep = HashPageGetBitmap(mappage);
bit = ISSET(freep, bitmapbit) != 0;
_hash_relbuf(indexRel, mapbuf);
index_close(indexRel, AccessShareLock); index_close(indexRel, AccessShareLock);
/* Build a tuple descriptor for our result type */ /* Build a tuple descriptor for our result type */

View File

@ -69,11 +69,20 @@ _hash_ovflblkno_to_bitno(HashMetaPage metap, BlockNumber ovflblkno)
if (ovflblkno <= (BlockNumber) (1 << i)) if (ovflblkno <= (BlockNumber) (1 << i))
break; /* oops */ break; /* oops */
bitnum = ovflblkno - (1 << i); bitnum = ovflblkno - (1 << i);
if (bitnum <= metap->hashm_spares[i])
/*
* bitnum has to be greater than number of overflow page added in
* previous split point. The overflow page at this splitnum (i) if any
* should start from ((2 ^ i) + metap->hashm_spares[i - 1] + 1).
*/
if (bitnum > metap->hashm_spares[i - 1] &&
bitnum <= metap->hashm_spares[i])
return bitnum - 1; /* -1 to convert 1-based to 0-based */ return bitnum - 1; /* -1 to convert 1-based to 0-based */
} }
elog(ERROR, "invalid overflow block number %u", ovflblkno); ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("invalid overflow block number %u", ovflblkno)));
return 0; /* keep compiler quiet */ return 0; /* keep compiler quiet */
} }