pageinspect: Fix hash_bitmap_info not to read the underlying page.
authorRobert Haas <rhaas@postgresql.org>
Thu, 9 Feb 2017 19:02:58 +0000 (14:02 -0500)
committerRobert Haas <rhaas@postgresql.org>
Thu, 9 Feb 2017 19:34:34 +0000 (14:34 -0500)
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.

contrib/pageinspect/expected/hash.out
contrib/pageinspect/hashfuncs.c
src/backend/access/hash/hashovfl.c

index 7eb1537b29ec2e2cccca499269d56e6a205770a5..8ed60bccc0d245d11f31a6d58b8368498c5f4ab8 100644 (file)
@@ -30,23 +30,17 @@ hash_page_type | bitmap
 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"
 SELECT * FROM hash_bitmap_info('test_hash_a_idx', 0);
-ERROR:  page is not an overflow page
-DETAIL:  Expected 00000001, got 00000008.
+ERROR:  invalid overflow block number 0
 SELECT * FROM hash_bitmap_info('test_hash_a_idx', 1);
-ERROR:  page is not an overflow page
-DETAIL:  Expected 00000001, got 00000002.
+ERROR:  invalid overflow block number 1
 SELECT * FROM hash_bitmap_info('test_hash_a_idx', 2);
-ERROR:  page is not an overflow page
-DETAIL:  Expected 00000001, got 00000002.
+ERROR:  invalid overflow block number 2
 SELECT * FROM hash_bitmap_info('test_hash_a_idx', 3);
-ERROR:  page is not an overflow page
-DETAIL:  Expected 00000001, got 00000002.
+ERROR:  invalid overflow block number 3
 SELECT * FROM hash_bitmap_info('test_hash_a_idx', 4);
-ERROR:  page is not an overflow page
-DETAIL:  Expected 00000001, got 00000002.
+ERROR:  invalid overflow block number 4
 SELECT * FROM hash_bitmap_info('test_hash_a_idx', 5);
-ERROR:  page is not an overflow page
-DETAIL:  Expected 00000001, got 00000004.
+ERROR:  invalid overflow block number 5
 SELECT magic, version, ntuples, bsize, bmsize, bmshift, maxbucket, highmask,
 lowmask, ovflpoint, firstfree, nmaps, procid, spares, mapp FROM
 hash_metapage_info(get_raw_page('test_hash_a_idx', 0));
index 08663c109d1fb6838090fbb7d1b6d461d6bdba4d..88c807af5b29d5f419ffc9ad774e6320f6b5cc85 100644 (file)
@@ -380,21 +380,22 @@ hash_bitmap_info(PG_FUNCTION_ARGS)
    Oid         indexRelid = PG_GETARG_OID(0);
    uint64      ovflblkno = PG_GETARG_INT64(1);
    HashMetaPage metap;
-   Buffer      buf,
-               metabuf;
+   Buffer      metabuf,
+               mapbuf;
    BlockNumber bitmapblkno;
-   Page        page;
+   Page        mappage;
    bool        bit = false;
-   HashPageOpaque  opaque;
    TupleDesc   tupleDesc;
    Relation    indexRel;
    uint32      ovflbitno;
    int32       bitmappage,
                bitmapbit;
    HeapTuple   tuple;
-   int         j;
+   int         i,
+               j;
    Datum       values[3];
    bool        nulls[3];
+   uint32     *freep;
 
    if (!superuser())
        ereport(ERROR,
@@ -418,30 +419,30 @@ hash_bitmap_info(PG_FUNCTION_ARGS)
                 errmsg("block number " UINT64_FORMAT " is out of range for relation \"%s\"",
                        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 */
    metabuf = _hash_getbuf(indexRel, HASH_METAPAGE, HASH_READ, LH_META_PAGE);
    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);
 
    bitmappage = ovflbitno >> BMPG_SHIFT(metap);
@@ -450,12 +451,21 @@ hash_bitmap_info(PG_FUNCTION_ARGS)
    if (bitmappage >= metap->hashm_nmaps)
        ereport(ERROR,
                (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];
 
    _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);
 
    /* Build a tuple descriptor for our result type */
index 753c8a6a134166fa5bf148f912a15e5759c27268..33340893291e0f93aa460b92a03effc3fba0a3d7 100644 (file)
@@ -69,11 +69,20 @@ _hash_ovflblkno_to_bitno(HashMetaPage metap, BlockNumber ovflblkno)
        if (ovflblkno <= (BlockNumber) (1 << i))
            break;              /* oops */
        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 */
    }
 
-   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 */
 }