src/backend/access/hash/hashpage.c
void
_hash_finish_split(Relation rel, Buffer metabuf, Buffer obuf, Bucket obucket,
				   uint32 maxbucket, uint32 highmask, uint32 lowmask)
{
	HASHCTL		hash_ctl;
	HTAB	   *tidhtab;
	Buffer		bucket_nbuf = InvalidBuffer;
	Buffer		nbuf;
	Page		npage;
	BlockNumber nblkno;
	BlockNumber bucket_nblkno;
	HashPageOpaque npageopaque;
	Bucket		nbucket;
	bool		found;

	/* Initialize hash tables used to track TIDs */
	memset(&hash_ctl, 0, sizeof(hash_ctl));
	hash_ctl.keysize = sizeof(ItemPointerData);
	hash_ctl.entrysize = sizeof(ItemPointerData);
	hash_ctl.hcxt = CurrentMemoryContext;

	tidhtab =
		hash_create("bucket ctids",
					256,		/* arbitrary initial size */
					&hash_ctl,
					HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);

	bucket_nblkno = nblkno = _hash_get_newblock_from_oldbucket(rel, obucket);

	/*
	 * Scan the new bucket and build hash table of TIDs
	 */
	for (;;)
	{
		OffsetNumber noffnum;
		OffsetNumber nmaxoffnum;

		nbuf = _hash_getbuf(rel, nblkno, HASH_READ,
							LH_BUCKET_PAGE | LH_OVERFLOW_PAGE);

		/* remember the primary bucket buffer to acquire cleanup lock on it. */
		if (nblkno == bucket_nblkno)
			bucket_nbuf = nbuf;

		npage = BufferGetPage(nbuf);
		npageopaque = (HashPageOpaque) PageGetSpecialPointer(npage);

		/* Scan each tuple in new page */
		nmaxoffnum = PageGetMaxOffsetNumber(npage);
		for (noffnum = FirstOffsetNumber;
			 noffnum <= nmaxoffnum;
			 noffnum = OffsetNumberNext(noffnum))
		{
			IndexTuple	itup;

			/* Fetch the item's TID and insert it in hash table. */
			itup = (IndexTuple) PageGetItem(npage,
											PageGetItemId(npage, noffnum));

			(void) hash_search(tidhtab, &itup->t_tid, HASH_ENTER, &found);

			Assert(!found);
		}

		nblkno = npageopaque->hasho_nextblkno;

		/*
		 * release our write lock without modifying buffer and ensure to
		 * retain the pin on primary bucket.
		 */
		if (nbuf == bucket_nbuf)
			LockBuffer(nbuf, BUFFER_LOCK_UNLOCK);
		else
			_hash_relbuf(rel, nbuf);

		/* Exit loop if no more overflow pages in new bucket */
		if (!BlockNumberIsValid(nblkno))
			break;
	}

	/*
	 * Conditionally get the cleanup lock on old and new buckets to perform
	 * the split operation.  If we don't get the cleanup locks, silently give
	 * up and next insertion on old bucket will try again to complete the
	 * split.
	 */
	if (!ConditionalLockBufferForCleanup(obuf))
	{
		hash_destroy(tidhtab);
		return;
	}
	if (!ConditionalLockBufferForCleanup(bucket_nbuf))
	{
		LockBuffer(obuf, BUFFER_LOCK_UNLOCK);
		hash_destroy(tidhtab);
		return;
	}

	npage = BufferGetPage(bucket_nbuf);
	npageopaque = (HashPageOpaque) PageGetSpecialPointer(npage);
	nbucket = npageopaque->hasho_bucket;

	_hash_splitbucket_guts(rel, metabuf, obucket,
						   nbucket, obuf, bucket_nbuf, tidhtab,
						   maxbucket, highmask, lowmask);

	_hash_relbuf(rel, bucket_nbuf);
	LockBuffer(obuf, BUFFER_LOCK_UNLOCK);
	hash_destroy(tidhtab);
}
/*
 *	_hash_finish_split() -- Finish the previously interrupted split operation
 *
 * To complete the split operation, we form the hash table of TIDs in new
 * bucket which is then used by split operation to skip tuples that are
 * already moved before the split operation was previously interrupted.
 *
 * The caller must hold a pin, but no lock, on the metapage and old bucket's
 * primay page buffer.  The buffers are returned in the same state.  (The
 * metapage is only touched if it becomes necessary to add or remove overflow
 * pages.)
 */
- Finish the previously interrupted split operation
To complete the split operation, we form the hash table of TIDs in new
bucket which is then used by split operation to skip tuples that are
already moved before the split operation was previously interrupted.

The caller must hold a pin, but no lock, on the metapage and old bucket's
primay page buffer.  The buffers are returned in the same state.  (The
metapage is only touched if it becomes necessary to add or remove overflow
pages.)
- Finish the previously interrupted split operation To complete the split operation we form the hash table of TIDs in new bucket which is then used by split operation to skip tuples that are already moved before the split operation was previously interrupted . The caller must hold a pin but no lock on the metapage and old bucket ' s primay page buffer . The buffers are returned in the same state . ( The metapage is only touched if it becomes necessary to add or remove overflow pages . )
void
_hash_finish_split(Relation rel, Buffer metabuf, Buffer obuf, Bucket obucket,
				   uint32 maxbucket, uint32 highmask, uint32 lowmask)
{
	HASHCTL		hash_ctl;
	HTAB	   *tidhtab;
	Buffer		bucket_nbuf = InvalidBuffer;
	Buffer		nbuf;
	Page		npage;
	BlockNumber nblkno;
	BlockNumber bucket_nblkno;
	HashPageOpaque npageopaque;
	Bucket		nbucket;
	bool		found;
	memset(&hash_ctl, 0, sizeof(hash_ctl));
	hash_ctl.keysize = sizeof(ItemPointerData);
	hash_ctl.entrysize = sizeof(ItemPointerData);
	hash_ctl.hcxt = CurrentMemoryContext;
	tidhtab =
		hash_create("bucket ctids",
					256,		
					&hash_ctl,
					HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
	bucket_nblkno = nblkno = _hash_get_newblock_from_oldbucket(rel, obucket);
	for (;;)
	{
		OffsetNumber noffnum;
		OffsetNumber nmaxoffnum;
		nbuf = _hash_getbuf(rel, nblkno, HASH_READ,
							LH_BUCKET_PAGE | LH_OVERFLOW_PAGE);
		if (nblkno == bucket_nblkno)
			bucket_nbuf = nbuf;
		npage = BufferGetPage(nbuf);
		npageopaque = (HashPageOpaque) PageGetSpecialPointer(npage);
		nmaxoffnum = PageGetMaxOffsetNumber(npage);
		for (noffnum = FirstOffsetNumber;
			 noffnum <= nmaxoffnum;
			 noffnum = OffsetNumberNext(noffnum))
		{
			IndexTuple	itup;
			itup = (IndexTuple) PageGetItem(npage,
											PageGetItemId(npage, noffnum));
			(void) hash_search(tidhtab, &itup->t_tid, HASH_ENTER, &found);
			Assert(!found);
		}
		nblkno = npageopaque->hasho_nextblkno;
		if (nbuf == bucket_nbuf)
			LockBuffer(nbuf, BUFFER_LOCK_UNLOCK);
		else
			_hash_relbuf(rel, nbuf);
		if (!BlockNumberIsValid(nblkno))
			break;
	}
	if (!ConditionalLockBufferForCleanup(obuf))
	{
		hash_destroy(tidhtab);
		return;
	}
	if (!ConditionalLockBufferForCleanup(bucket_nbuf))
	{
		LockBuffer(obuf, BUFFER_LOCK_UNLOCK);
		hash_destroy(tidhtab);
		return;
	}
	npage = BufferGetPage(bucket_nbuf);
	npageopaque = (HashPageOpaque) PageGetSpecialPointer(npage);
	nbucket = npageopaque->hasho_bucket;
	_hash_splitbucket_guts(rel, metabuf, obucket,
						   nbucket, obuf, bucket_nbuf, tidhtab,
						   maxbucket, highmask, lowmask);
	_hash_relbuf(rel, bucket_nbuf);
	LockBuffer(obuf, BUFFER_LOCK_UNLOCK);
	hash_destroy(tidhtab);
}
void _hash_finish_split ( Relation rel , Buffer metabuf , Buffer obuf , Bucket obucket , uint32 maxbucket , uint32 highmask , uint32 lowmask ) { HASHCTL hash_ctl ; HTAB * tidhtab ; Buffer bucket_nbuf = InvalidBuffer ; Buffer nbuf ; Page npage ; BlockNumber nblkno ; BlockNumber bucket_nblkno ; HashPageOpaque npageopaque ; Bucket nbucket ; bool found ; memset ( & hash_ctl , 0 , sizeof ( hash_ctl ) ) ; hash_ctl . keysize = sizeof ( ItemPointerData ) ; hash_ctl . entrysize = sizeof ( ItemPointerData ) ; hash_ctl . hcxt = CurrentMemoryContext ; tidhtab = hash_create ( " " , 256 , & hash_ctl , HASH_ELEM | HASH_BLOBS | HASH_CONTEXT ) ; bucket_nblkno = nblkno = _hash_get_newblock_from_oldbucket ( rel , obucket ) ; for ( ; ; ) { OffsetNumber noffnum ; OffsetNumber nmaxoffnum ; nbuf = _hash_getbuf ( rel , nblkno , HASH_READ , LH_BUCKET_PAGE | LH_OVERFLOW_PAGE ) ; if ( nblkno == bucket_nblkno ) bucket_nbuf = nbuf ; npage = BufferGetPage ( nbuf ) ; npageopaque = ( HashPageOpaque ) PageGetSpecialPointer ( npage ) ; nmaxoffnum = PageGetMaxOffsetNumber ( npage ) ; for ( noffnum = FirstOffsetNumber ; noffnum <= nmaxoffnum ; noffnum = OffsetNumberNext ( noffnum ) ) { IndexTuple itup ; itup = ( IndexTuple ) PageGetItem ( npage , PageGetItemId ( npage , noffnum ) ) ; ( void ) hash_search ( tidhtab , & itup -> t_tid , HASH_ENTER , & found ) ; Assert ( ! found ) ; } nblkno = npageopaque -> hasho_nextblkno ; if ( nbuf == bucket_nbuf ) LockBuffer ( nbuf , BUFFER_LOCK_UNLOCK ) ; else _hash_relbuf ( rel , nbuf ) ; if ( ! BlockNumberIsValid ( nblkno ) ) break ; } if ( ! ConditionalLockBufferForCleanup ( obuf ) ) { hash_destroy ( tidhtab ) ; return ; } if ( ! ConditionalLockBufferForCleanup ( bucket_nbuf ) ) { LockBuffer ( obuf , BUFFER_LOCK_UNLOCK ) ; hash_destroy ( tidhtab ) ; return ; } npage = BufferGetPage ( bucket_nbuf ) ; npageopaque = ( HashPageOpaque ) PageGetSpecialPointer ( npage ) ; nbucket = npageopaque -> hasho_bucket ; _hash_splitbucket_guts ( rel , metabuf , obucket , nbucket , obuf , bucket_nbuf , tidhtab , maxbucket , highmask , lowmask ) ; _hash_relbuf ( rel , bucket_nbuf ) ; LockBuffer ( obuf , BUFFER_LOCK_UNLOCK ) ; hash_destroy ( tidhtab ) ; }
_hash_finish_split() -- Finish the previously interrupted split operation
 To complete the split operation, we form the hash table of TIDs in new
 bucket which is then used by split operation to skip tuples that are
 already moved before the split operation was previously interrupted.
_hash_finish_split () -- Finish the previously interrupted split operation To complete the split operation we form the hash table of TIDs in new bucket which is then used by split operation to skip tuples that are already moved before the split operation was previously interrupted .
/* Initialize hash tables used to track TIDs */ /* arbitrary initial size */ /*
	 * Scan the new bucket and build hash table of TIDs
	 */ /* remember the primary bucket buffer to acquire cleanup lock on it. */ /* Scan each tuple in new page */ /* Fetch the item's TID and insert it in hash table. */ /*
		 * release our write lock without modifying buffer and ensure to
		 * retain the pin on primary bucket.
		 */ /* Exit loop if no more overflow pages in new bucket */ /*
	 * Conditionally get the cleanup lock on old and new buckets to perform
	 * the split operation.  If we don't get the cleanup locks, silently give
	 * up and next insertion on old bucket will try again to complete the
	 * split.
	 */