src/backend/access/hash/hashpage.c
static void
_hash_splitbucket_guts(Relation rel,
					   Buffer metabuf,
					   Bucket obucket,
					   Bucket nbucket,
					   Buffer obuf,
					   Buffer nbuf,
					   HTAB *htab,
					   uint32 maxbucket,
					   uint32 highmask,
					   uint32 lowmask)
{
	Buffer		bucket_obuf;
	Buffer		bucket_nbuf;
	Page		opage;
	Page		npage;
	HashPageOpaque oopaque;
	HashPageOpaque nopaque;

	bucket_obuf = obuf;
	opage = BufferGetPage(obuf);
	oopaque = (HashPageOpaque) PageGetSpecialPointer(opage);

	bucket_nbuf = nbuf;
	npage = BufferGetPage(nbuf);
	nopaque = (HashPageOpaque) PageGetSpecialPointer(npage);

	/*
	 * Partition the tuples in the old bucket between the old bucket and the
	 * new bucket, advancing along the old bucket's overflow bucket chain and
	 * adding overflow pages to the new bucket as needed.  Outer loop iterates
	 * once per page in old bucket.
	 */
	for (;;)
	{
		BlockNumber oblkno;
		OffsetNumber ooffnum;
		OffsetNumber omaxoffnum;

		/* Scan each tuple in old page */
		omaxoffnum = PageGetMaxOffsetNumber(opage);
		for (ooffnum = FirstOffsetNumber;
			 ooffnum <= omaxoffnum;
			 ooffnum = OffsetNumberNext(ooffnum))
		{
			IndexTuple	itup;
			Size		itemsz;
			Bucket		bucket;
			bool		found = false;

			/* skip dead tuples */
			if (ItemIdIsDead(PageGetItemId(opage, ooffnum)))
				continue;

			/*
			 * Before inserting a tuple, probe the hash table containing TIDs
			 * of tuples belonging to new bucket, if we find a match, then
			 * skip that tuple, else fetch the item's hash key (conveniently
			 * stored in the item) and determine which bucket it now belongs
			 * in.
			 */
			itup = (IndexTuple) PageGetItem(opage,
											PageGetItemId(opage, ooffnum));

			if (htab)
				(void) hash_search(htab, &itup->t_tid, HASH_FIND, &found);

			if (found)
				continue;

			bucket = _hash_hashkey2bucket(_hash_get_indextuple_hashkey(itup),
										  maxbucket, highmask, lowmask);

			if (bucket == nbucket)
			{
				IndexTuple	new_itup;

				/*
				 * make a copy of index tuple as we have to scribble on it.
				 */
				new_itup = CopyIndexTuple(itup);

				/*
				 * mark the index tuple as moved by split, such tuples are
				 * skipped by scan if there is split in progress for a bucket.
				 */
				new_itup->t_info |= INDEX_MOVED_BY_SPLIT_MASK;

				/*
				 * insert the tuple into the new bucket.  if it doesn't fit on
				 * the current page in the new bucket, we must allocate a new
				 * overflow page and place the tuple on that page instead.
				 */
				itemsz = IndexTupleDSize(*new_itup);
				itemsz = MAXALIGN(itemsz);

				if (PageGetFreeSpace(npage) < itemsz)
				{
					/* write out nbuf and drop lock, but keep pin */
					MarkBufferDirty(nbuf);
					LockBuffer(nbuf, BUFFER_LOCK_UNLOCK);
					/* chain to a new overflow page */
					nbuf = _hash_addovflpage(rel, metabuf, nbuf, (nbuf == bucket_nbuf) ? true : false);
					npage = BufferGetPage(nbuf);
					nopaque = (HashPageOpaque) PageGetSpecialPointer(npage);
				}

				/*
				 * Insert tuple on new page, using _hash_pgaddtup to ensure
				 * correct ordering by hashkey.  This is a tad inefficient
				 * since we may have to shuffle itempointers repeatedly.
				 * Possible future improvement: accumulate all the items for
				 * the new page and qsort them before insertion.
				 */
				(void) _hash_pgaddtup(rel, nbuf, itemsz, new_itup);

				/* be tidy */
				pfree(new_itup);
			}
			else
			{
				/*
				 * the tuple stays on this page, so nothing to do.
				 */
				Assert(bucket == obucket);
			}
		}

		oblkno = oopaque->hasho_nextblkno;

		/* retain the pin on the old primary bucket */
		if (obuf == bucket_obuf)
			LockBuffer(obuf, BUFFER_LOCK_UNLOCK);
		else
			_hash_relbuf(rel, obuf);

		/* Exit loop if no more overflow pages in old bucket */
		if (!BlockNumberIsValid(oblkno))
			break;

		/* Else, advance to next old page */
		obuf = _hash_getbuf(rel, oblkno, HASH_READ, LH_OVERFLOW_PAGE);
		opage = BufferGetPage(obuf);
		oopaque = (HashPageOpaque) PageGetSpecialPointer(opage);
	}

	/*
	 * We're at the end of the old bucket chain, so we're done partitioning
	 * the tuples.  Mark the old and new buckets to indicate split is
	 * finished.
	 *
	 * To avoid deadlocks due to locking order of buckets, first lock the old
	 * bucket and then the new bucket.
	 */
	if (nbuf == bucket_nbuf)
	{
		MarkBufferDirty(bucket_nbuf);
		LockBuffer(bucket_nbuf, BUFFER_LOCK_UNLOCK);
	}
	else
	{
		MarkBufferDirty(nbuf);
		_hash_relbuf(rel, nbuf);
	}

	LockBuffer(bucket_obuf, BUFFER_LOCK_EXCLUSIVE);
	opage = BufferGetPage(bucket_obuf);
	oopaque = (HashPageOpaque) PageGetSpecialPointer(opage);

	LockBuffer(bucket_nbuf, BUFFER_LOCK_EXCLUSIVE);
	npage = BufferGetPage(bucket_nbuf);
	nopaque = (HashPageOpaque) PageGetSpecialPointer(npage);

	oopaque->hasho_flag &= ~LH_BUCKET_BEING_SPLIT;
	nopaque->hasho_flag &= ~LH_BUCKET_BEING_POPULATED;

	/*
	 * After the split is finished, mark the old bucket to indicate that it
	 * contains deletable tuples.  Vacuum will clear split-cleanup flag after
	 * deleting such tuples.
	 */
	oopaque->hasho_flag |= LH_BUCKET_NEEDS_SPLIT_CLEANUP;

	/*
	 * now write the buffers, here we don't release the locks as caller is
	 * responsible to release locks.
	 */
	MarkBufferDirty(bucket_obuf);
	MarkBufferDirty(bucket_nbuf);
}
/*
 * _hash_splitbucket_guts -- Helper function to perform the split operation
 *
 * This routine is used to partition the tuples between old and new bucket and
 * to finish incomplete split operations.  To finish the previously
 * interrupted split operation, caller needs to fill htab.  If htab is set, then
 * we skip the movement of tuples that exists in htab, otherwise NULL value of
 * htab indicates movement of all the tuples that belong to new bucket.
 *
 * Caller needs to lock and unlock the old and new primary buckets.
 */
- Helper function to perform the split operation
This routine is used to partition the tuples between old and new bucket and
to finish incomplete split operations.  To finish the previously
interrupted split operation, caller needs to fill htab.  If htab is set, then
we skip the movement of tuples that exists in htab, otherwise NULL value of
htab indicates movement of all the tuples that belong to new bucket.

Caller needs to lock and unlock the old and new primary buckets.
- Helper function to perform the split operation This routine is used to partition the tuples between old and new bucket and to finish incomplete split operations . To finish the previously interrupted split operation caller needs to fill htab . If htab is set then we skip the movement of tuples that exists in htab otherwise NULL value of htab indicates movement of all the tuples that belong to new bucket . Caller needs to lock and unlock the old and new primary buckets .
static void
_hash_splitbucket_guts(Relation rel,
					   Buffer metabuf,
					   Bucket obucket,
					   Bucket nbucket,
					   Buffer obuf,
					   Buffer nbuf,
					   HTAB *htab,
					   uint32 maxbucket,
					   uint32 highmask,
					   uint32 lowmask)
{
	Buffer		bucket_obuf;
	Buffer		bucket_nbuf;
	Page		opage;
	Page		npage;
	HashPageOpaque oopaque;
	HashPageOpaque nopaque;
	bucket_obuf = obuf;
	opage = BufferGetPage(obuf);
	oopaque = (HashPageOpaque) PageGetSpecialPointer(opage);
	bucket_nbuf = nbuf;
	npage = BufferGetPage(nbuf);
	nopaque = (HashPageOpaque) PageGetSpecialPointer(npage);
	for (;;)
	{
		BlockNumber oblkno;
		OffsetNumber ooffnum;
		OffsetNumber omaxoffnum;
		omaxoffnum = PageGetMaxOffsetNumber(opage);
		for (ooffnum = FirstOffsetNumber;
			 ooffnum <= omaxoffnum;
			 ooffnum = OffsetNumberNext(ooffnum))
		{
			IndexTuple	itup;
			Size		itemsz;
			Bucket		bucket;
			bool		found = false;
			if (ItemIdIsDead(PageGetItemId(opage, ooffnum)))
				continue;
			itup = (IndexTuple) PageGetItem(opage,
											PageGetItemId(opage, ooffnum));
			if (htab)
				(void) hash_search(htab, &itup->t_tid, HASH_FIND, &found);
			if (found)
				continue;
			bucket = _hash_hashkey2bucket(_hash_get_indextuple_hashkey(itup),
										  maxbucket, highmask, lowmask);
			if (bucket == nbucket)
			{
				IndexTuple	new_itup;
				new_itup = CopyIndexTuple(itup);
				new_itup->t_info |= INDEX_MOVED_BY_SPLIT_MASK;
				itemsz = IndexTupleDSize(*new_itup);
				itemsz = MAXALIGN(itemsz);
				if (PageGetFreeSpace(npage) < itemsz)
				{
					MarkBufferDirty(nbuf);
					LockBuffer(nbuf, BUFFER_LOCK_UNLOCK);
					nbuf = _hash_addovflpage(rel, metabuf, nbuf, (nbuf == bucket_nbuf) ? true : false);
					npage = BufferGetPage(nbuf);
					nopaque = (HashPageOpaque) PageGetSpecialPointer(npage);
				}
				(void) _hash_pgaddtup(rel, nbuf, itemsz, new_itup);
				pfree(new_itup);
			}
			else
			{
				Assert(bucket == obucket);
			}
		}
		oblkno = oopaque->hasho_nextblkno;
		if (obuf == bucket_obuf)
			LockBuffer(obuf, BUFFER_LOCK_UNLOCK);
		else
			_hash_relbuf(rel, obuf);
		if (!BlockNumberIsValid(oblkno))
			break;
		obuf = _hash_getbuf(rel, oblkno, HASH_READ, LH_OVERFLOW_PAGE);
		opage = BufferGetPage(obuf);
		oopaque = (HashPageOpaque) PageGetSpecialPointer(opage);
	}
	if (nbuf == bucket_nbuf)
	{
		MarkBufferDirty(bucket_nbuf);
		LockBuffer(bucket_nbuf, BUFFER_LOCK_UNLOCK);
	}
	else
	{
		MarkBufferDirty(nbuf);
		_hash_relbuf(rel, nbuf);
	}
	LockBuffer(bucket_obuf, BUFFER_LOCK_EXCLUSIVE);
	opage = BufferGetPage(bucket_obuf);
	oopaque = (HashPageOpaque) PageGetSpecialPointer(opage);
	LockBuffer(bucket_nbuf, BUFFER_LOCK_EXCLUSIVE);
	npage = BufferGetPage(bucket_nbuf);
	nopaque = (HashPageOpaque) PageGetSpecialPointer(npage);
	oopaque->hasho_flag &= ~LH_BUCKET_BEING_SPLIT;
	nopaque->hasho_flag &= ~LH_BUCKET_BEING_POPULATED;
	oopaque->hasho_flag |= LH_BUCKET_NEEDS_SPLIT_CLEANUP;
	MarkBufferDirty(bucket_obuf);
	MarkBufferDirty(bucket_nbuf);
}
static void _hash_splitbucket_guts ( Relation rel , Buffer metabuf , Bucket obucket , Bucket nbucket , Buffer obuf , Buffer nbuf , HTAB * htab , uint32 maxbucket , uint32 highmask , uint32 lowmask ) { Buffer bucket_obuf ; Buffer bucket_nbuf ; Page opage ; Page npage ; HashPageOpaque oopaque ; HashPageOpaque nopaque ; bucket_obuf = obuf ; opage = BufferGetPage ( obuf ) ; oopaque = ( HashPageOpaque ) PageGetSpecialPointer ( opage ) ; bucket_nbuf = nbuf ; npage = BufferGetPage ( nbuf ) ; nopaque = ( HashPageOpaque ) PageGetSpecialPointer ( npage ) ; for ( ; ; ) { BlockNumber oblkno ; OffsetNumber ooffnum ; OffsetNumber omaxoffnum ; omaxoffnum = PageGetMaxOffsetNumber ( opage ) ; for ( ooffnum = FirstOffsetNumber ; ooffnum <= omaxoffnum ; ooffnum = OffsetNumberNext ( ooffnum ) ) { IndexTuple itup ; Size itemsz ; Bucket bucket ; bool found = false ; if ( ItemIdIsDead ( PageGetItemId ( opage , ooffnum ) ) ) continue ; itup = ( IndexTuple ) PageGetItem ( opage , PageGetItemId ( opage , ooffnum ) ) ; if ( htab ) ( void ) hash_search ( htab , & itup -> t_tid , HASH_FIND , & found ) ; if ( found ) continue ; bucket = _hash_hashkey2bucket ( _hash_get_indextuple_hashkey ( itup ) , maxbucket , highmask , lowmask ) ; if ( bucket == nbucket ) { IndexTuple new_itup ; new_itup = CopyIndexTuple ( itup ) ; new_itup -> t_info |= INDEX_MOVED_BY_SPLIT_MASK ; itemsz = IndexTupleDSize ( * new_itup ) ; itemsz = MAXALIGN ( itemsz ) ; if ( PageGetFreeSpace ( npage ) < itemsz ) { MarkBufferDirty ( nbuf ) ; LockBuffer ( nbuf , BUFFER_LOCK_UNLOCK ) ; nbuf = _hash_addovflpage ( rel , metabuf , nbuf , ( nbuf == bucket_nbuf ) ? true : false ) ; npage = BufferGetPage ( nbuf ) ; nopaque = ( HashPageOpaque ) PageGetSpecialPointer ( npage ) ; } ( void ) _hash_pgaddtup ( rel , nbuf , itemsz , new_itup ) ; pfree ( new_itup ) ; } else { Assert ( bucket == obucket ) ; } } oblkno = oopaque -> hasho_nextblkno ; if ( obuf == bucket_obuf ) LockBuffer ( obuf , BUFFER_LOCK_UNLOCK ) ; else _hash_relbuf ( rel , obuf ) ; if ( ! BlockNumberIsValid ( oblkno ) ) break ; obuf = _hash_getbuf ( rel , oblkno , HASH_READ , LH_OVERFLOW_PAGE ) ; opage = BufferGetPage ( obuf ) ; oopaque = ( HashPageOpaque ) PageGetSpecialPointer ( opage ) ; } if ( nbuf == bucket_nbuf ) { MarkBufferDirty ( bucket_nbuf ) ; LockBuffer ( bucket_nbuf , BUFFER_LOCK_UNLOCK ) ; } else { MarkBufferDirty ( nbuf ) ; _hash_relbuf ( rel , nbuf ) ; } LockBuffer ( bucket_obuf , BUFFER_LOCK_EXCLUSIVE ) ; opage = BufferGetPage ( bucket_obuf ) ; oopaque = ( HashPageOpaque ) PageGetSpecialPointer ( opage ) ; LockBuffer ( bucket_nbuf , BUFFER_LOCK_EXCLUSIVE ) ; npage = BufferGetPage ( bucket_nbuf ) ; nopaque = ( HashPageOpaque ) PageGetSpecialPointer ( npage ) ; oopaque -> hasho_flag &= ~ LH_BUCKET_BEING_SPLIT ; nopaque -> hasho_flag &= ~ LH_BUCKET_BEING_POPULATED ; oopaque -> hasho_flag |= LH_BUCKET_NEEDS_SPLIT_CLEANUP ; MarkBufferDirty ( bucket_obuf ) ; MarkBufferDirty ( bucket_nbuf ) ; }
_hash_splitbucket_guts -- Helper function to perform the split operation
 This routine is used to partition the tuples between old and new bucket and
 to finish incomplete split operations.
_hash_splitbucket_guts -- Helper function to perform the split operation This routine is used to partition the tuples between old and new bucket and to finish incomplete split operations .
/*
	 * Partition the tuples in the old bucket between the old bucket and the
	 * new bucket, advancing along the old bucket's overflow bucket chain and
	 * adding overflow pages to the new bucket as needed.  Outer loop iterates
	 * once per page in old bucket.
	 */ /* Scan each tuple in old page */ /* skip dead tuples */ /*
			 * Before inserting a tuple, probe the hash table containing TIDs
			 * of tuples belonging to new bucket, if we find a match, then
			 * skip that tuple, else fetch the item's hash key (conveniently
			 * stored in the item) and determine which bucket it now belongs
			 * in.
			 */ /*
				 * make a copy of index tuple as we have to scribble on it.
				 */ /*
				 * mark the index tuple as moved by split, such tuples are
				 * skipped by scan if there is split in progress for a bucket.
				 */ /*
				 * insert the tuple into the new bucket.  if it doesn't fit on
				 * the current page in the new bucket, we must allocate a new
				 * overflow page and place the tuple on that page instead.
				 */ /* write out nbuf and drop lock, but keep pin */ /* chain to a new overflow page */ /*
				 * Insert tuple on new page, using _hash_pgaddtup to ensure
				 * correct ordering by hashkey.  This is a tad inefficient
				 * since we may have to shuffle itempointers repeatedly.
				 * Possible future improvement: accumulate all the items for
				 * the new page and qsort them before insertion.
				 */ /* be tidy */ /*
				 * the tuple stays on this page, so nothing to do.
				 */ /* retain the pin on the old primary bucket */ /* Exit loop if no more overflow pages in old bucket */ /* Else, advance to next old page */ /*
	 * We're at the end of the old bucket chain, so we're done partitioning
	 * the tuples.  Mark the old and new buckets to indicate split is
	 * finished.
	 *
	 * To avoid deadlocks due to locking order of buckets, first lock the old
	 * bucket and then the new bucket.
	 */ /*
	 * After the split is finished, mark the old bucket to indicate that it
	 * contains deletable tuples.  Vacuum will clear split-cleanup flag after
	 * deleting such tuples.
	 */ /*
	 * now write the buffers, here we don't release the locks as caller is
	 * responsible to release locks.
	 */