src/backend/access/hash/hashpage.c
void
_hash_expandtable(Relation rel, Buffer metabuf)
{
	HashMetaPage metap;
	Bucket		old_bucket;
	Bucket		new_bucket;
	uint32		spare_ndx;
	BlockNumber start_oblkno;
	BlockNumber start_nblkno;
	Buffer		buf_nblkno;
	Buffer		buf_oblkno;
	Page		opage;
	HashPageOpaque oopaque;
	uint32		maxbucket;
	uint32		highmask;
	uint32		lowmask;

restart_expand:

	/*
	 * Write-lock the meta page.  It used to be necessary to acquire a
	 * heavyweight lock to begin a split, but that is no longer required.
	 */
	LockBuffer(metabuf, BUFFER_LOCK_EXCLUSIVE);

	_hash_checkpage(rel, metabuf, LH_META_PAGE);
	metap = HashPageGetMeta(BufferGetPage(metabuf));

	/*
	 * Check to see if split is still needed; someone else might have already
	 * done one while we waited for the lock.
	 *
	 * Make sure this stays in sync with _hash_doinsert()
	 */
	if (metap->hashm_ntuples <=
		(double) metap->hashm_ffactor * (metap->hashm_maxbucket + 1))
		goto fail;

	/*
	 * Can't split anymore if maxbucket has reached its maximum possible
	 * value.
	 *
	 * Ideally we'd allow bucket numbers up to UINT_MAX-1 (no higher because
	 * the calculation maxbucket+1 mustn't overflow).  Currently we restrict
	 * to half that because of overflow looping in _hash_log2() and
	 * insufficient space in hashm_spares[].  It's moot anyway because an
	 * index with 2^32 buckets would certainly overflow BlockNumber and hence
	 * _hash_alloc_buckets() would fail, but if we supported buckets smaller
	 * 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().
	 */
	if (metap->hashm_maxbucket >= (uint32) 0x7FFFFFFE)
		goto fail;

	/*
	 * Determine which bucket is to be split, and attempt to take cleanup lock
	 * on the old bucket.  If we can't get the lock, give up.
	 *
	 * The cleanup lock protects us not only against other backends, but
	 * against our own backend as well.
	 *
	 * The cleanup lock is mainly to protect the split from concurrent
	 * inserts. See src/backend/access/hash/README, Lock Definitions for
	 * further details.  Due to this locking restriction, if there is any
	 * pending scan, the split will give up which is not good, but harmless.
	 */
	new_bucket = metap->hashm_maxbucket + 1;

	old_bucket = (new_bucket & metap->hashm_lowmask);

	start_oblkno = BUCKET_TO_BLKNO(metap, old_bucket);

	buf_oblkno = _hash_getbuf_with_condlock_cleanup(rel, start_oblkno, LH_BUCKET_PAGE);
	if (!buf_oblkno)
		goto fail;

	opage = BufferGetPage(buf_oblkno);
	oopaque = (HashPageOpaque) PageGetSpecialPointer(opage);

	/*
	 * We want to finish the split from a bucket as there is no apparent
	 * benefit by not doing so and it will make the code complicated to finish
	 * the split that involves multiple buckets considering the case where new
	 * split also fails.  We don't need to consider the new bucket for
	 * completing the split here as it is not possible that a re-split of new
	 * bucket starts when there is still a pending split from old bucket.
	 */
	if (H_BUCKET_BEING_SPLIT(oopaque))
	{
		/*
		 * Copy bucket mapping info now; refer the comment in code below where
		 * we copy this information before calling _hash_splitbucket to see
		 * why this is okay.
		 */
		maxbucket = metap->hashm_maxbucket;
		highmask = metap->hashm_highmask;
		lowmask = metap->hashm_lowmask;

		/*
		 * Release the lock on metapage and old_bucket, before completing the
		 * split.
		 */
		LockBuffer(metabuf, BUFFER_LOCK_UNLOCK);
		LockBuffer(buf_oblkno, BUFFER_LOCK_UNLOCK);

		_hash_finish_split(rel, metabuf, buf_oblkno, old_bucket, maxbucket,
						   highmask, lowmask);

		/* release the pin on old buffer and retry for expand. */
		_hash_dropbuf(rel, buf_oblkno);

		goto restart_expand;
	}

	/*
	 * Clean the tuples remained from the previous split.  This operation
	 * requires cleanup lock and we already have one on the old bucket, so
	 * let's do it. We also don't want to allow further splits from the bucket
	 * till the garbage of previous split is cleaned.  This has two
	 * advantages; first, it helps in avoiding the bloat due to garbage and
	 * second is, during cleanup of bucket, we are always sure that the
	 * garbage tuples belong to most recently split bucket.  On the contrary,
	 * if we allow cleanup of bucket after meta page is updated to indicate
	 * the new split and before the actual split, the cleanup operation won't
	 * be able to decide whether the tuple has been moved to the newly created
	 * bucket and ended up deleting such tuples.
	 */
	if (H_NEEDS_SPLIT_CLEANUP(oopaque))
	{
		/*
		 * Copy bucket mapping info now; refer to the comment in code below
		 * where we copy this information before calling _hash_splitbucket
		 * to see why this is okay.
		 */
		maxbucket = metap->hashm_maxbucket;
		highmask = metap->hashm_highmask;
		lowmask = metap->hashm_lowmask;

		/* Release the metapage lock. */
		LockBuffer(metabuf, BUFFER_LOCK_UNLOCK);

		hashbucketcleanup(rel, old_bucket, buf_oblkno, start_oblkno, NULL,
						  maxbucket, highmask, lowmask, NULL, NULL, true,
						  NULL, NULL);

		_hash_dropbuf(rel, buf_oblkno);

		goto restart_expand;
	}

	/*
	 * There shouldn't be any active scan on new bucket.
	 *
	 * Note: it is safe to compute the new bucket's blkno here, even though we
	 * may still need to update the BUCKET_TO_BLKNO mapping.  This is because
	 * the current value of hashm_spares[hashm_ovflpoint] correctly shows
	 * where we are going to put a new splitpoint's worth of buckets.
	 */
	start_nblkno = BUCKET_TO_BLKNO(metap, new_bucket);

	/*
	 * If the split point is increasing (hashm_maxbucket's log base 2
	 * increases), we need to allocate a new batch of bucket pages.
	 */
	spare_ndx = _hash_log2(new_bucket + 1);
	if (spare_ndx > metap->hashm_ovflpoint)
	{
		Assert(spare_ndx == metap->hashm_ovflpoint + 1);

		/*
		 * The number of buckets in the new splitpoint is equal to the total
		 * number already in existence, i.e. new_bucket.  Currently this maps
		 * one-to-one to blocks required, but someday we may need a more
		 * complicated calculation here.
		 */
		if (!_hash_alloc_buckets(rel, start_nblkno, new_bucket))
		{
			/* can't split due to BlockNumber overflow */
			_hash_relbuf(rel, buf_oblkno);
			goto fail;
		}
	}

	/*
	 * Physically allocate the new bucket's primary page.  We want to do this
	 * before changing the metapage's mapping info, in case we can't get the
	 * disk space.  Ideally, we don't need to check for cleanup lock on new
	 * bucket as no other backend could find this bucket unless meta page is
	 * updated.  However, it is good to be consistent with old bucket locking.
	 */
	buf_nblkno = _hash_getnewbuf(rel, start_nblkno, MAIN_FORKNUM);
	if (!IsBufferCleanupOK(buf_nblkno))
	{
		_hash_relbuf(rel, buf_oblkno);
		_hash_relbuf(rel, buf_nblkno);
		goto fail;
	}


	/*
	 * Okay to proceed with split.  Update the metapage bucket mapping info.
	 *
	 * Since we are scribbling on the metapage data right in the shared
	 * buffer, any failure in this next little bit leaves us with a big
	 * problem: the metapage is effectively corrupt but could get written back
	 * to disk.  We don't really expect any failure, but just to be sure,
	 * establish a critical section.
	 */
	START_CRIT_SECTION();

	metap->hashm_maxbucket = new_bucket;

	if (new_bucket > metap->hashm_highmask)
	{
		/* Starting a new doubling */
		metap->hashm_lowmask = metap->hashm_highmask;
		metap->hashm_highmask = new_bucket | metap->hashm_lowmask;
	}

	/*
	 * If the split point is increasing (hashm_maxbucket's log base 2
	 * increases), we need to adjust the hashm_spares[] array and
	 * hashm_ovflpoint so that future overflow pages will be created beyond
	 * this new batch of bucket pages.
	 */
	if (spare_ndx > metap->hashm_ovflpoint)
	{
		metap->hashm_spares[spare_ndx] = metap->hashm_spares[metap->hashm_ovflpoint];
		metap->hashm_ovflpoint = spare_ndx;
	}

	/* Done mucking with metapage */
	END_CRIT_SECTION();

	/*
	 * Copy bucket mapping info now; this saves re-accessing the meta page
	 * inside _hash_splitbucket's inner loop.  Note that once we drop the
	 * split lock, other splits could begin, so these values might be out of
	 * date before _hash_splitbucket finishes.  That's okay, since all it
	 * needs is to tell which of these two buckets to map hashkeys into.
	 */
	maxbucket = metap->hashm_maxbucket;
	highmask = metap->hashm_highmask;
	lowmask = metap->hashm_lowmask;

	/* Write out the metapage and drop lock, but keep pin */
	MarkBufferDirty(metabuf);
	LockBuffer(metabuf, BUFFER_LOCK_UNLOCK);

	/* Relocate records to the new bucket */
	_hash_splitbucket(rel, metabuf,
					  old_bucket, new_bucket,
					  buf_oblkno, buf_nblkno,
					  maxbucket, highmask, lowmask);

	return;

	/* Here if decide not to split or fail to acquire old bucket lock */
fail:

	/* We didn't write the metapage, so just drop lock */
	LockBuffer(metabuf, BUFFER_LOCK_UNLOCK);
}
/*
 * Attempt to expand the hash table by creating one new bucket.
 *
 * This will silently do nothing if we don't get cleanup lock on old or
 * new bucket.
 *
 * Complete the pending splits and remove the tuples from old bucket,
 * if there are any left over from the previous split.
 *
 * The caller must hold a pin, but no lock, on the metapage buffer.
 * The buffer is returned in the same state.
 */
Attempt to expand the hash table by creating one new bucket.
This will silently do nothing if we don't get cleanup lock on old or
new bucket.

Complete the pending splits and remove the tuples from old bucket,
if there are any left over from the previous split.

The caller must hold a pin, but no lock, on the metapage buffer.
The buffer is returned in the same state.
Attempt to expand the hash table by creating one new bucket . This will silently do nothing if we don ' t get cleanup lock on old or new bucket . Complete the pending splits and remove the tuples from old bucket if there are any left over from the previous split . The caller must hold a pin but no lock on the metapage buffer . The buffer is returned in the same state .
void
_hash_expandtable(Relation rel, Buffer metabuf)
{
	HashMetaPage metap;
	Bucket		old_bucket;
	Bucket		new_bucket;
	uint32		spare_ndx;
	BlockNumber start_oblkno;
	BlockNumber start_nblkno;
	Buffer		buf_nblkno;
	Buffer		buf_oblkno;
	Page		opage;
	HashPageOpaque oopaque;
	uint32		maxbucket;
	uint32		highmask;
	uint32		lowmask;
restart_expand:
	LockBuffer(metabuf, BUFFER_LOCK_EXCLUSIVE);
	_hash_checkpage(rel, metabuf, LH_META_PAGE);
	metap = HashPageGetMeta(BufferGetPage(metabuf));
	if (metap->hashm_ntuples <=
		(double) metap->hashm_ffactor * (metap->hashm_maxbucket + 1))
		goto fail;
	if (metap->hashm_maxbucket >= (uint32) 0x7FFFFFFE)
		goto fail;
	new_bucket = metap->hashm_maxbucket + 1;
	old_bucket = (new_bucket & metap->hashm_lowmask);
	start_oblkno = BUCKET_TO_BLKNO(metap, old_bucket);
	buf_oblkno = _hash_getbuf_with_condlock_cleanup(rel, start_oblkno, LH_BUCKET_PAGE);
	if (!buf_oblkno)
		goto fail;
	opage = BufferGetPage(buf_oblkno);
	oopaque = (HashPageOpaque) PageGetSpecialPointer(opage);
	if (H_BUCKET_BEING_SPLIT(oopaque))
	{
		maxbucket = metap->hashm_maxbucket;
		highmask = metap->hashm_highmask;
		lowmask = metap->hashm_lowmask;
		LockBuffer(metabuf, BUFFER_LOCK_UNLOCK);
		LockBuffer(buf_oblkno, BUFFER_LOCK_UNLOCK);
		_hash_finish_split(rel, metabuf, buf_oblkno, old_bucket, maxbucket,
						   highmask, lowmask);
		_hash_dropbuf(rel, buf_oblkno);
		goto restart_expand;
	}
	if (H_NEEDS_SPLIT_CLEANUP(oopaque))
	{
		maxbucket = metap->hashm_maxbucket;
		highmask = metap->hashm_highmask;
		lowmask = metap->hashm_lowmask;
		LockBuffer(metabuf, BUFFER_LOCK_UNLOCK);
		hashbucketcleanup(rel, old_bucket, buf_oblkno, start_oblkno, NULL,
						  maxbucket, highmask, lowmask, NULL, NULL, true,
						  NULL, NULL);
		_hash_dropbuf(rel, buf_oblkno);
		goto restart_expand;
	}
	start_nblkno = BUCKET_TO_BLKNO(metap, new_bucket);
	spare_ndx = _hash_log2(new_bucket + 1);
	if (spare_ndx > metap->hashm_ovflpoint)
	{
		Assert(spare_ndx == metap->hashm_ovflpoint + 1);
		if (!_hash_alloc_buckets(rel, start_nblkno, new_bucket))
		{
			_hash_relbuf(rel, buf_oblkno);
			goto fail;
		}
	}
	buf_nblkno = _hash_getnewbuf(rel, start_nblkno, MAIN_FORKNUM);
	if (!IsBufferCleanupOK(buf_nblkno))
	{
		_hash_relbuf(rel, buf_oblkno);
		_hash_relbuf(rel, buf_nblkno);
		goto fail;
	}
	START_CRIT_SECTION();
	metap->hashm_maxbucket = new_bucket;
	if (new_bucket > metap->hashm_highmask)
	{
		metap->hashm_lowmask = metap->hashm_highmask;
		metap->hashm_highmask = new_bucket | metap->hashm_lowmask;
	}
	if (spare_ndx > metap->hashm_ovflpoint)
	{
		metap->hashm_spares[spare_ndx] = metap->hashm_spares[metap->hashm_ovflpoint];
		metap->hashm_ovflpoint = spare_ndx;
	}
	END_CRIT_SECTION();
	maxbucket = metap->hashm_maxbucket;
	highmask = metap->hashm_highmask;
	lowmask = metap->hashm_lowmask;
	MarkBufferDirty(metabuf);
	LockBuffer(metabuf, BUFFER_LOCK_UNLOCK);
	_hash_splitbucket(rel, metabuf,
					  old_bucket, new_bucket,
					  buf_oblkno, buf_nblkno,
					  maxbucket, highmask, lowmask);
	return;
fail:
	LockBuffer(metabuf, BUFFER_LOCK_UNLOCK);
}
void _hash_expandtable ( Relation rel , Buffer metabuf ) { HashMetaPage metap ; Bucket old_bucket ; Bucket new_bucket ; uint32 spare_ndx ; BlockNumber start_oblkno ; BlockNumber start_nblkno ; Buffer buf_nblkno ; Buffer buf_oblkno ; Page opage ; HashPageOpaque oopaque ; uint32 maxbucket ; uint32 highmask ; uint32 lowmask ; restart_expand : LockBuffer ( metabuf , BUFFER_LOCK_EXCLUSIVE ) ; _hash_checkpage ( rel , metabuf , LH_META_PAGE ) ; metap = HashPageGetMeta ( BufferGetPage ( metabuf ) ) ; if ( metap -> hashm_ntuples <= ( double ) metap -> hashm_ffactor * ( metap -> hashm_maxbucket + 1 ) ) goto fail ; if ( metap -> hashm_maxbucket >= ( uint32 ) 0x7FFFFFFE ) goto fail ; new_bucket = metap -> hashm_maxbucket + 1 ; old_bucket = ( new_bucket & metap -> hashm_lowmask ) ; start_oblkno = BUCKET_TO_BLKNO ( metap , old_bucket ) ; buf_oblkno = _hash_getbuf_with_condlock_cleanup ( rel , start_oblkno , LH_BUCKET_PAGE ) ; if ( ! buf_oblkno ) goto fail ; opage = BufferGetPage ( buf_oblkno ) ; oopaque = ( HashPageOpaque ) PageGetSpecialPointer ( opage ) ; if ( H_BUCKET_BEING_SPLIT ( oopaque ) ) { maxbucket = metap -> hashm_maxbucket ; highmask = metap -> hashm_highmask ; lowmask = metap -> hashm_lowmask ; LockBuffer ( metabuf , BUFFER_LOCK_UNLOCK ) ; LockBuffer ( buf_oblkno , BUFFER_LOCK_UNLOCK ) ; _hash_finish_split ( rel , metabuf , buf_oblkno , old_bucket , maxbucket , highmask , lowmask ) ; _hash_dropbuf ( rel , buf_oblkno ) ; goto restart_expand ; } if ( H_NEEDS_SPLIT_CLEANUP ( oopaque ) ) { maxbucket = metap -> hashm_maxbucket ; highmask = metap -> hashm_highmask ; lowmask = metap -> hashm_lowmask ; LockBuffer ( metabuf , BUFFER_LOCK_UNLOCK ) ; hashbucketcleanup ( rel , old_bucket , buf_oblkno , start_oblkno , NULL , maxbucket , highmask , lowmask , NULL , NULL , true , NULL , NULL ) ; _hash_dropbuf ( rel , buf_oblkno ) ; goto restart_expand ; } start_nblkno = BUCKET_TO_BLKNO ( metap , new_bucket ) ; spare_ndx = _hash_log2 ( new_bucket + 1 ) ; if ( spare_ndx > metap -> hashm_ovflpoint ) { Assert ( spare_ndx == metap -> hashm_ovflpoint + 1 ) ; if ( ! _hash_alloc_buckets ( rel , start_nblkno , new_bucket ) ) { _hash_relbuf ( rel , buf_oblkno ) ; goto fail ; } } buf_nblkno = _hash_getnewbuf ( rel , start_nblkno , MAIN_FORKNUM ) ; if ( ! IsBufferCleanupOK ( buf_nblkno ) ) { _hash_relbuf ( rel , buf_oblkno ) ; _hash_relbuf ( rel , buf_nblkno ) ; goto fail ; } START_CRIT_SECTION ( ) ; metap -> hashm_maxbucket = new_bucket ; if ( new_bucket > metap -> hashm_highmask ) { metap -> hashm_lowmask = metap -> hashm_highmask ; metap -> hashm_highmask = new_bucket | metap -> hashm_lowmask ; } if ( spare_ndx > metap -> hashm_ovflpoint ) { metap -> hashm_spares [ spare_ndx ] = metap -> hashm_spares [ metap -> hashm_ovflpoint ] ; metap -> hashm_ovflpoint = spare_ndx ; } END_CRIT_SECTION ( ) ; maxbucket = metap -> hashm_maxbucket ; highmask = metap -> hashm_highmask ; lowmask = metap -> hashm_lowmask ; MarkBufferDirty ( metabuf ) ; LockBuffer ( metabuf , BUFFER_LOCK_UNLOCK ) ; _hash_splitbucket ( rel , metabuf , old_bucket , new_bucket , buf_oblkno , buf_nblkno , maxbucket , highmask , lowmask ) ; return ; fail : LockBuffer ( metabuf , BUFFER_LOCK_UNLOCK ) ; }
Attempt to expand the hash table by creating one new bucket.
Attempt to expand the hash table by creating one new bucket .
/*
	 * Write-lock the meta page.  It used to be necessary to acquire a
	 * heavyweight lock to begin a split, but that is no longer required.
	 */ /*
	 * Check to see if split is still needed; someone else might have already
	 * done one while we waited for the lock.
	 *
	 * Make sure this stays in sync with _hash_doinsert()
	 */ /*
	 * Can't split anymore if maxbucket has reached its maximum possible
	 * value.
	 *
	 * Ideally we'd allow bucket numbers up to UINT_MAX-1 (no higher because
	 * the calculation maxbucket+1 mustn't overflow).  Currently we restrict
	 * to half that because of overflow looping in _hash_log2() and
	 * insufficient space in hashm_spares[].  It's moot anyway because an
	 * index with 2^32 buckets would certainly overflow BlockNumber and hence
	 * _hash_alloc_buckets() would fail, but if we supported buckets smaller
	 * 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().
	 */ /*
	 * Determine which bucket is to be split, and attempt to take cleanup lock
	 * on the old bucket.  If we can't get the lock, give up.
	 *
	 * The cleanup lock protects us not only against other backends, but
	 * against our own backend as well.
	 *
	 * The cleanup lock is mainly to protect the split from concurrent
	 * inserts. See src/backend/access/hash/README, Lock Definitions for
	 * further details.  Due to this locking restriction, if there is any
	 * pending scan, the split will give up which is not good, but harmless.
	 */ /*
	 * We want to finish the split from a bucket as there is no apparent
	 * benefit by not doing so and it will make the code complicated to finish
	 * the split that involves multiple buckets considering the case where new
	 * split also fails.  We don't need to consider the new bucket for
	 * completing the split here as it is not possible that a re-split of new
	 * bucket starts when there is still a pending split from old bucket.
	 */ /*
		 * Copy bucket mapping info now; refer the comment in code below where
		 * we copy this information before calling _hash_splitbucket to see
		 * why this is okay.
		 */ /*
		 * Release the lock on metapage and old_bucket, before completing the
		 * split.
		 */ /* release the pin on old buffer and retry for expand. */ /*
	 * Clean the tuples remained from the previous split.  This operation
	 * requires cleanup lock and we already have one on the old bucket, so
	 * let's do it. We also don't want to allow further splits from the bucket
	 * till the garbage of previous split is cleaned.  This has two
	 * advantages; first, it helps in avoiding the bloat due to garbage and
	 * second is, during cleanup of bucket, we are always sure that the
	 * garbage tuples belong to most recently split bucket.  On the contrary,
	 * if we allow cleanup of bucket after meta page is updated to indicate
	 * the new split and before the actual split, the cleanup operation won't
	 * be able to decide whether the tuple has been moved to the newly created
	 * bucket and ended up deleting such tuples.
	 */ /*
		 * Copy bucket mapping info now; refer to the comment in code below
		 * where we copy this information before calling _hash_splitbucket
		 * to see why this is okay.
		 */ /* Release the metapage lock. */ /*
	 * There shouldn't be any active scan on new bucket.
	 *
	 * Note: it is safe to compute the new bucket's blkno here, even though we
	 * may still need to update the BUCKET_TO_BLKNO mapping.  This is because
	 * the current value of hashm_spares[hashm_ovflpoint] correctly shows
	 * where we are going to put a new splitpoint's worth of buckets.
	 */ /*
	 * If the split point is increasing (hashm_maxbucket's log base 2
	 * increases), we need to allocate a new batch of bucket pages.
	 */ /*
		 * The number of buckets in the new splitpoint is equal to the total
		 * number already in existence, i.e. new_bucket.  Currently this maps
		 * one-to-one to blocks required, but someday we may need a more
		 * complicated calculation here.
		 */ /* can't split due to BlockNumber overflow */ /*
	 * Physically allocate the new bucket's primary page.  We want to do this
	 * before changing the metapage's mapping info, in case we can't get the
	 * disk space.  Ideally, we don't need to check for cleanup lock on new
	 * bucket as no other backend could find this bucket unless meta page is
	 * updated.  However, it is good to be consistent with old bucket locking.
	 */ /*
	 * Okay to proceed with split.  Update the metapage bucket mapping info.
	 *
	 * Since we are scribbling on the metapage data right in the shared
	 * buffer, any failure in this next little bit leaves us with a big
	 * problem: the metapage is effectively corrupt but could get written back
	 * to disk.  We don't really expect any failure, but just to be sure,
	 * establish a critical section.
	 */ /* Starting a new doubling */ /*
	 * If the split point is increasing (hashm_maxbucket's log base 2
	 * increases), we need to adjust the hashm_spares[] array and
	 * hashm_ovflpoint so that future overflow pages will be created beyond
	 * this new batch of bucket pages.
	 */ /* Done mucking with metapage */ /*
	 * Copy bucket mapping info now; this saves re-accessing the meta page
	 * inside _hash_splitbucket's inner loop.  Note that once we drop the
	 * split lock, other splits could begin, so these values might be out of
	 * date before _hash_splitbucket finishes.  That's okay, since all it
	 * needs is to tell which of these two buckets to map hashkeys into.
	 */ /* Write out the metapage and drop lock, but keep pin */ /* Relocate records to the new bucket */ /* Here if decide not to split or fail to acquire old bucket lock */ /* We didn't write the metapage, so just drop lock */