hash: Refactor hash index creation.
authorRobert Haas <rhaas@postgresql.org>
Tue, 7 Mar 2017 22:03:51 +0000 (17:03 -0500)
committerRobert Haas <rhaas@postgresql.org>
Tue, 7 Mar 2017 22:03:51 +0000 (17:03 -0500)
The primary goal here is to move all of the related page modifications
to a single section of code, in preparation for adding write-ahead
logging.  In passing, rename _hash_metapinit to _hash_init, since it
initializes more than just the metapage.

Amit Kapila.  The larger patch series of which this is a part has been
reviewed and tested by Álvaro Herrera, Ashutosh Sharma, Mark Kirkwood,
Jeff Janes, and Jesper Pedersen.

src/backend/access/hash/hash.c
src/backend/access/hash/hashovfl.c
src/backend/access/hash/hashpage.c
src/include/access/hash.h

index 24510e78f5fd39df131ece29eb2b2b8ed723acbe..1f8a7f61c7242e4b1dc573c1c677503058856b2e 100644 (file)
@@ -120,7 +120,7 @@ hashbuild(Relation heap, Relation index, IndexInfo *indexInfo)
    estimate_rel_size(heap, NULL, &relpages, &reltuples, &allvisfrac);
 
    /* Initialize the hash index metadata page and initial buckets */
-   num_buckets = _hash_metapinit(index, reltuples, MAIN_FORKNUM);
+   num_buckets = _hash_init(index, reltuples, MAIN_FORKNUM);
 
    /*
     * If we just insert the tuples into the index in scan order, then
@@ -182,7 +182,7 @@ hashbuild(Relation heap, Relation index, IndexInfo *indexInfo)
 void
 hashbuildempty(Relation index)
 {
-   _hash_metapinit(index, 0, INIT_FORKNUM);
+   _hash_init(index, 0, INIT_FORKNUM);
 }
 
 /*
index 9d89e86aef1865f15fd6dc17bcc7f986838db1b9..1087480f7eb96d68cc88b256d675eafd99510645 100644 (file)
@@ -570,68 +570,6 @@ _hash_freeovflpage(Relation rel, Buffer bucketbuf, Buffer ovflbuf,
 }
 
 
-/*
- * _hash_initbitmap()
- *
- *  Initialize a new bitmap page.  The metapage has a write-lock upon
- *  entering the function, and must be written by caller after return.
- *
- * 'blkno' is the block number of the new bitmap page.
- *
- * All bits in the new bitmap page are set to "1", indicating "in use".
- */
-void
-_hash_initbitmap(Relation rel, HashMetaPage metap, BlockNumber blkno,
-                ForkNumber forkNum)
-{
-   Buffer      buf;
-   Page        pg;
-   HashPageOpaque op;
-   uint32     *freep;
-
-   /*
-    * It is okay to write-lock the new bitmap page while holding metapage
-    * write lock, because no one else could be contending for the new page.
-    * Also, the metapage lock makes it safe to extend the index using
-    * _hash_getnewbuf.
-    *
-    * There is some loss of concurrency in possibly doing I/O for the new
-    * page while holding the metapage lock, but this path is taken so seldom
-    * that it's not worth worrying about.
-    */
-   buf = _hash_getnewbuf(rel, blkno, forkNum);
-   pg = BufferGetPage(buf);
-
-   /* initialize the page's special space */
-   op = (HashPageOpaque) PageGetSpecialPointer(pg);
-   op->hasho_prevblkno = InvalidBlockNumber;
-   op->hasho_nextblkno = InvalidBlockNumber;
-   op->hasho_bucket = -1;
-   op->hasho_flag = LH_BITMAP_PAGE;
-   op->hasho_page_id = HASHO_PAGE_ID;
-
-   /* set all of the bits to 1 */
-   freep = HashPageGetBitmap(pg);
-   MemSet(freep, 0xFF, BMPGSZ_BYTE(metap));
-
-   /* dirty the new bitmap page, and release write lock and pin */
-   MarkBufferDirty(buf);
-   _hash_relbuf(rel, buf);
-
-   /* add the new bitmap page to the metapage's list of bitmaps */
-   /* metapage already has a write lock */
-   if (metap->hashm_nmaps >= HASH_MAX_BITMAPS)
-       ereport(ERROR,
-               (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
-                errmsg("out of overflow pages in hash index \"%s\"",
-                       RelationGetRelationName(rel))));
-
-   metap->hashm_mapp[metap->hashm_nmaps] = blkno;
-
-   metap->hashm_nmaps++;
-}
-
-
 /*
  * _hash_initbitmapbuffer()
  *
index bb1ce75634f21f34cb2fc2f943491edb10234f49..c73929cebbb6f178653c910b87406733acbdd134 100644 (file)
@@ -156,6 +156,36 @@ _hash_getinitbuf(Relation rel, BlockNumber blkno)
    return buf;
 }
 
+/*
+ * _hash_initbuf() -- Get and initialize a buffer by bucket number.
+ */
+void
+_hash_initbuf(Buffer buf, uint32 max_bucket, uint32 num_bucket, uint32 flag,
+             bool initpage)
+{
+   HashPageOpaque pageopaque;
+   Page        page;
+
+   page = BufferGetPage(buf);
+
+   /* initialize the page */
+   if (initpage)
+       _hash_pageinit(page, BufferGetPageSize(buf));
+
+   pageopaque = (HashPageOpaque) PageGetSpecialPointer(page);
+
+   /*
+    * Set hasho_prevblkno with current hashm_maxbucket. This value will
+    * be used to validate cached HashMetaPageData. See
+    * _hash_getbucketbuf_from_hashkey().
+    */
+   pageopaque->hasho_prevblkno = max_bucket;
+   pageopaque->hasho_nextblkno = InvalidBlockNumber;
+   pageopaque->hasho_bucket = num_bucket;
+   pageopaque->hasho_flag = flag;
+   pageopaque->hasho_page_id = HASHO_PAGE_ID;
+}
+
 /*
  * _hash_getnewbuf() -- Get a new page at the end of the index.
  *
@@ -288,7 +318,7 @@ _hash_dropscanbuf(Relation rel, HashScanOpaque so)
 
 
 /*
- * _hash_metapinit() -- Initialize the metadata page of a hash index,
+ * _hash_init() -- Initialize the metadata page of a hash index,
  *             the initial buckets, and the initial bitmap page.
  *
  * The initial number of buckets is dependent on num_tuples, an estimate
@@ -300,19 +330,18 @@ _hash_dropscanbuf(Relation rel, HashScanOpaque so)
  * multiple buffer locks is ignored.
  */
 uint32
-_hash_metapinit(Relation rel, double num_tuples, ForkNumber forkNum)
+_hash_init(Relation rel, double num_tuples, ForkNumber forkNum)
 {
-   HashMetaPage metap;
-   HashPageOpaque pageopaque;
    Buffer      metabuf;
    Buffer      buf;
+   Buffer      bitmapbuf;
    Page        pg;
+   HashMetaPage metap;
+   RegProcedure procid;
    int32       data_width;
    int32       item_width;
    int32       ffactor;
-   double      dnumbuckets;
    uint32      num_buckets;
-   uint32      log2_num_buckets;
    uint32      i;
 
    /* safety check */
@@ -334,6 +363,96 @@ _hash_metapinit(Relation rel, double num_tuples, ForkNumber forkNum)
    if (ffactor < 10)
        ffactor = 10;
 
+   procid = index_getprocid(rel, 1, HASHPROC);
+
+   /*
+    * We initialize the metapage, the first N bucket pages, and the first
+    * bitmap page in sequence, using _hash_getnewbuf to cause smgrextend()
+    * calls to occur.  This ensures that the smgr level has the right idea of
+    * the physical index length.
+    *
+    * Critical section not required, because on error the creation of the
+    * whole relation will be rolled back.
+    */
+   metabuf = _hash_getnewbuf(rel, HASH_METAPAGE, forkNum);
+   _hash_init_metabuffer(metabuf, num_tuples, procid, ffactor, false);
+   MarkBufferDirty(metabuf);
+
+   pg = BufferGetPage(metabuf);
+   metap = HashPageGetMeta(pg);
+
+   num_buckets = metap->hashm_maxbucket + 1;
+
+   /*
+    * Release buffer lock on the metapage while we initialize buckets.
+    * Otherwise, we'll be in interrupt holdoff and the CHECK_FOR_INTERRUPTS
+    * won't accomplish anything.  It's a bad idea to hold buffer locks for
+    * long intervals in any case, since that can block the bgwriter.
+    */
+   LockBuffer(metabuf, BUFFER_LOCK_UNLOCK);
+
+   /*
+    * Initialize and WAL Log the first N buckets
+    */
+   for (i = 0; i < num_buckets; i++)
+   {
+       BlockNumber blkno;
+
+       /* Allow interrupts, in case N is huge */
+       CHECK_FOR_INTERRUPTS();
+
+       blkno = BUCKET_TO_BLKNO(metap, i);
+       buf = _hash_getnewbuf(rel, blkno, forkNum);
+       _hash_initbuf(buf, metap->hashm_maxbucket, i, LH_BUCKET_PAGE, false);
+       MarkBufferDirty(buf);
+       _hash_relbuf(rel, buf);
+   }
+
+   /* Now reacquire buffer lock on metapage */
+   LockBuffer(metabuf, BUFFER_LOCK_EXCLUSIVE);
+
+   /*
+    * Initialize bitmap page
+    */
+   bitmapbuf = _hash_getnewbuf(rel, num_buckets + 1, forkNum);
+   _hash_initbitmapbuffer(bitmapbuf, metap->hashm_bmsize, false);
+   MarkBufferDirty(bitmapbuf);
+
+   /* add the new bitmap page to the metapage's list of bitmaps */
+   /* metapage already has a write lock */
+   if (metap->hashm_nmaps >= HASH_MAX_BITMAPS)
+       ereport(ERROR,
+               (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+                errmsg("out of overflow pages in hash index \"%s\"",
+                       RelationGetRelationName(rel))));
+
+   metap->hashm_mapp[metap->hashm_nmaps] = num_buckets + 1;
+
+   metap->hashm_nmaps++;
+   MarkBufferDirty(metabuf);
+
+   /* all done */
+   _hash_relbuf(rel, bitmapbuf);
+   _hash_relbuf(rel, metabuf);
+
+   return num_buckets;
+}
+
+/*
+ * _hash_init_metabuffer() -- Initialize the metadata page of a hash index.
+ */
+void
+_hash_init_metabuffer(Buffer buf, double num_tuples, RegProcedure procid,
+                     uint16 ffactor, bool initpage)
+{
+   HashMetaPage metap;
+   HashPageOpaque pageopaque;
+   Page        page;
+   double      dnumbuckets;
+   uint32      num_buckets;
+   uint32      log2_num_buckets;
+   uint32      i;
+
    /*
     * Choose the number of initial bucket pages to match the fill factor
     * given the estimated number of tuples.  We round up the result to the
@@ -353,30 +472,25 @@ _hash_metapinit(Relation rel, double num_tuples, ForkNumber forkNum)
    Assert(num_buckets == (((uint32) 1) << log2_num_buckets));
    Assert(log2_num_buckets < HASH_MAX_SPLITPOINTS);
 
-   /*
-    * We initialize the metapage, the first N bucket pages, and the first
-    * bitmap page in sequence, using _hash_getnewbuf to cause smgrextend()
-    * calls to occur.  This ensures that the smgr level has the right idea of
-    * the physical index length.
-    */
-   metabuf = _hash_getnewbuf(rel, HASH_METAPAGE, forkNum);
-   pg = BufferGetPage(metabuf);
+   page = BufferGetPage(buf);
+   if (initpage)
+       _hash_pageinit(page, BufferGetPageSize(buf));
 
-   pageopaque = (HashPageOpaque) PageGetSpecialPointer(pg);
+   pageopaque = (HashPageOpaque) PageGetSpecialPointer(page);
    pageopaque->hasho_prevblkno = InvalidBlockNumber;
    pageopaque->hasho_nextblkno = InvalidBlockNumber;
    pageopaque->hasho_bucket = -1;
    pageopaque->hasho_flag = LH_META_PAGE;
    pageopaque->hasho_page_id = HASHO_PAGE_ID;
 
-   metap = HashPageGetMeta(pg);
+   metap = HashPageGetMeta(page);
 
    metap->hashm_magic = HASH_MAGIC;
    metap->hashm_version = HASH_VERSION;
    metap->hashm_ntuples = 0;
    metap->hashm_nmaps = 0;
    metap->hashm_ffactor = ffactor;
-   metap->hashm_bsize = HashGetMaxBitmapSize(pg);
+   metap->hashm_bsize = HashGetMaxBitmapSize(page);
    /* find largest bitmap array size that will fit in page size */
    for (i = _hash_log2(metap->hashm_bsize); i > 0; --i)
    {
@@ -393,7 +507,7 @@ _hash_metapinit(Relation rel, double num_tuples, ForkNumber forkNum)
     * pretty useless for normal operation (in fact, hashm_procid is not used
     * anywhere), but it might be handy for forensic purposes so we keep it.
     */
-   metap->hashm_procid = index_getprocid(rel, 1, HASHPROC);
+   metap->hashm_procid = procid;
 
    /*
     * We initialize the index with N buckets, 0 .. N-1, occupying physical
@@ -411,54 +525,9 @@ _hash_metapinit(Relation rel, double num_tuples, ForkNumber forkNum)
    metap->hashm_ovflpoint = log2_num_buckets;
    metap->hashm_firstfree = 0;
 
-   /*
-    * Release buffer lock on the metapage while we initialize buckets.
-    * Otherwise, we'll be in interrupt holdoff and the CHECK_FOR_INTERRUPTS
-    * won't accomplish anything.  It's a bad idea to hold buffer locks for
-    * long intervals in any case, since that can block the bgwriter.
-    */
-   MarkBufferDirty(metabuf);
-   LockBuffer(metabuf, BUFFER_LOCK_UNLOCK);
-
-   /*
-    * Initialize the first N buckets
-    */
-   for (i = 0; i < num_buckets; i++)
-   {
-       /* Allow interrupts, in case N is huge */
-       CHECK_FOR_INTERRUPTS();
-
-       buf = _hash_getnewbuf(rel, BUCKET_TO_BLKNO(metap, i), forkNum);
-       pg = BufferGetPage(buf);
-       pageopaque = (HashPageOpaque) PageGetSpecialPointer(pg);
-
-       /*
-        * Set hasho_prevblkno with current hashm_maxbucket. This value will
-        * be used to validate cached HashMetaPageData. See
-        * _hash_getbucketbuf_from_hashkey().
-        */
-       pageopaque->hasho_prevblkno = metap->hashm_maxbucket;
-       pageopaque->hasho_nextblkno = InvalidBlockNumber;
-       pageopaque->hasho_bucket = i;
-       pageopaque->hasho_flag = LH_BUCKET_PAGE;
-       pageopaque->hasho_page_id = HASHO_PAGE_ID;
-       MarkBufferDirty(buf);
-       _hash_relbuf(rel, buf);
-   }
-
-   /* Now reacquire buffer lock on metapage */
-   LockBuffer(metabuf, BUFFER_LOCK_EXCLUSIVE);
-
-   /*
-    * Initialize first bitmap page
-    */
-   _hash_initbitmap(rel, metap, num_buckets + 1, forkNum);
-
-   /* all done */
-   MarkBufferDirty(metabuf);
-   _hash_relbuf(rel, metabuf);
-
-   return num_buckets;
+   /* Set pd_lower just past the end of the metadata. */
+   ((PageHeader) page)->pd_lower =
+       ((char *) metap + sizeof(HashMetaPageData)) - (char *) page;
 }
 
 /*
@@ -535,7 +604,7 @@ restart_expand:
     * than a disk block then this would be an independent constraint.
     *
     * If you change this, see also the maximum initial number of buckets in
-    * _hash_metapinit().
+    * _hash_init().
     */
    if (metap->hashm_maxbucket >= (uint32) 0x7FFFFFFE)
        goto fail;
index 9c0b79f8a630fdd9e405f615ba9c7e5c7716de5d..bfdfed8657f58ec3e5e18b1e39e4ac25f1880620 100644 (file)
@@ -311,8 +311,6 @@ extern Buffer _hash_addovflpage(Relation rel, Buffer metabuf, Buffer buf, bool r
 extern BlockNumber _hash_freeovflpage(Relation rel, Buffer bucketbuf, Buffer ovflbuf,
                   Buffer wbuf, IndexTuple *itups, OffsetNumber *itup_offsets,
             Size *tups_size, uint16 nitups, BufferAccessStrategy bstrategy);
-extern void _hash_initbitmap(Relation rel, HashMetaPage metap,
-                BlockNumber blkno, ForkNumber forkNum);
 extern void _hash_initbitmapbuffer(Buffer buf, uint16 bmsize, bool initpage);
 extern void _hash_squeezebucket(Relation rel,
                    Bucket bucket, BlockNumber bucket_blkno,
@@ -331,6 +329,8 @@ extern Buffer _hash_getbucketbuf_from_hashkey(Relation rel, uint32 hashkey,
                                int access,
                                HashMetaPage *cachedmetap);
 extern Buffer _hash_getinitbuf(Relation rel, BlockNumber blkno);
+extern void _hash_initbuf(Buffer buf, uint32 max_bucket, uint32 num_bucket,
+               uint32 flag, bool initpage);
 extern Buffer _hash_getnewbuf(Relation rel, BlockNumber blkno,
                ForkNumber forkNum);
 extern Buffer _hash_getbuf_with_strategy(Relation rel, BlockNumber blkno,
@@ -339,8 +339,10 @@ extern Buffer _hash_getbuf_with_strategy(Relation rel, BlockNumber blkno,
 extern void _hash_relbuf(Relation rel, Buffer buf);
 extern void _hash_dropbuf(Relation rel, Buffer buf);
 extern void _hash_dropscanbuf(Relation rel, HashScanOpaque so);
-extern uint32 _hash_metapinit(Relation rel, double num_tuples,
-               ForkNumber forkNum);
+extern uint32 _hash_init(Relation rel, double num_tuples,
+          ForkNumber forkNum);
+extern void _hash_init_metabuffer(Buffer buf, double num_tuples,
+                     RegProcedure procid, uint16 ffactor, bool initpage);
 extern void _hash_pageinit(Page page, Size size);
 extern void _hash_expandtable(Relation rel, Buffer metabuf);
 extern void _hash_finish_split(Relation rel, Buffer metabuf, Buffer obuf,