/****************************************************************
 *								*
 *	Copyright 2001, 2012 Fidelity Information Services, Inc	*
 *								*
 *	This source code contains the intellectual property	*
 *	of its copyright holder(s), and is made available	*
 *	under a license.  If you do not know the terms of	*
 *	the license, please stop and do not read further.	*
 *								*
 ****************************************************************/

/******************************************************************************************
mu_clsce.c:
	Coalesce two adjacent blocks in GVT
Description:
	Join the working block (say, block1) and its right sibling (say, block2).
	In addition it needs to change the key value of the ancestor block(s).
	Say we have structure:
		block1: a(50),a(51),...a(58),a(59)
		block2: a(60),a(61),...a(68),a(69)
	After coalesce we have,
		block1: a(50),a(51),...a(58),a(59),a(60),...a(65)
		block2: a(66),a(67),a(68),a(69)
	We need to modify blocks
		1. block1
		2. block2
		3. ancestor of block1, key value "^a(59)" instead of "^a(65)"
		4. ancestor of block2, if it is different than ancestor of block1

	Some convention in naming:
		1. curr_key is the gv_currkey, which is first key of working block
		2. curr_key at level 'levelp' is the record's key value at blk1ptr->h[levelp].curr_offset
		3. a 'real key' value means a non-* key value (data blocks always have real value)
		4. working block means blk1ptr->h[level].blk_num (working block)
		5. rtsib block means blk2ptr->h[level].blk_num
		6. sz = size means uncompressed size of a key
		   len = length means compressed size of a key
*******************************************************************************************/

#include "mdef.h"

#include "gtm_string.h"

#include "cdb_sc.h"
#include "gdsroot.h"
#include "gdsblk.h"
#include "gtm_facility.h"
#include "fileinfo.h"
#include "gdsbt.h"
#include "gdsfhead.h"
#include "filestruct.h"
#include "gdsblkops.h"
#include "gdskill.h"
#include "gdscc.h"
#include "jnl.h"
#include "copy.h"
#include "muextr.h"
#include "mu_reorg.h"

/* Include prototypes */
#include "t_write.h"
#include "mupip_reorg.h"

GBLREF sgmnt_data_ptr_t	cs_data;
GBLREF char		*update_array, *update_array_ptr;
GBLREF uint4		update_array_size;	/* for the BLK_* macros */
GBLREF uint4            t_err;
GBLREF unsigned int     t_tries;
GBLREF gd_region        *gv_cur_region;
GBLREF gv_namehead	*gv_target;
GBLREF gv_key           *gv_currkey;
GBLREF gv_key           *gv_currkey_next_reorg;


/*************************************************************************************************
Input Parameters:
	gv_target: working block's history
	level : Level of working block and its right sibling
	d_blk_fill_size : Maximum fill allowed in a data block
	i_blk_fill_size : Maximum fill allowed in an index block
Output Parameters:
	kill_set_ptr : List of blocks to be freed from LBM (already killed in mu_clsce)
	remove_rtsib : if right sibling was completely merged with working
Returns:
	cdb_sc_normal on success
	Other wise error status
 *************************************************************************************************/
enum cdb_sc mu_clsce(int level, int i_max_fill, int d_max_fill, kill_set *kill_set_ptr, boolean_t *remove_rtsib)
{
	boolean_t	complete_merge = FALSE,
			old_ref_star_only = FALSE,
			new_rtsib_star_only = FALSE,
			star_only_merge = FALSE,
			blk2_ances_star_only = FALSE,
			delete_all_blk2_ances = TRUE,
			levelp_next_is_star, forward_process;
	unsigned char	oldblk1_prev_key[MAX_KEY_SZ+1],
			old_levelp_cur_prev_key[MAX_KEY_SZ+1],
			old_levelp_cur_key[MAX_KEY_SZ+1]; /* keys in private memory */
	unsigned short	temp_ushort;
	int		new_levelp_cur_cmpc, new_levelp_cur_next_cmpc, tkeycmpc,
			oldblk1_last_cmpc, newblk1_mid_cmpc, newblk1_last_cmpc;
	int		tmp_cmpc;
	int		levelp, level2;
	int		old_blk1_sz, old_blk2_sz;
	int		old_levelp_cur_prev_keysz,
			old_levelp_cur_keysz,
			old_levelp_cur_next_keysz,
			newblk1_last_keysz,
			newblk2_first_keysz,
			new_blk2_ances_first_keysz;
	int		old_levelp_cur_keylen,
			new_levelp_cur_keylen,
			old_levelp_cur_next_keylen,
			new_levelp_cur_next_keylen,
			oldblk1_last_keylen,
			newblk1_last_keylen,
			newblk2_first_keylen;
	int		rec_size, piece_len, tkeylen, old_levelp_rec_offset;
	int		blk_seg_cnt, blk_size;
	enum cdb_sc	status;
	sm_uc_ptr_t 	oldblk1_last_key, old_levelp_cur_next_key,
			newblk1_last_key, newblk2_first_key, new_blk2_ances_first_key; /* shared memory keys */
	sm_uc_ptr_t 	rec_base, old_levelp_blk_base,
			bn_ptr1, bn_ptr2, blk2_ances_remain, old_blk1_base, old_blk2_base,
			new_blk1_top, new_blk2_first_rec_base, new_blk2_remain; /* shared memory pointers */
	sm_uc_ptr_t 	rPtr1, rPtr2;
	rec_hdr_ptr_t	star_rec_hdr, old_last_rec_hdr1, new_rec_hdr1, new_rec_hdr2,
			blk2_ances_hdr, new_levelp_cur_hdr, new_levelp_cur_next_hdr;
	blk_segment	*bs_ptr1, *bs_ptr2;
	srch_hist	*blk1ptr, *blk2ptr; /* blk2ptr is for right sibling's hist from a minimum sub-tree containing both blocks */

	blk_size = cs_data->blk_size;
	CHECK_AND_RESET_UPDATE_ARRAY;	/* reset update_array_ptr to update_array */

	blk1ptr = &(gv_target->hist);
	blk2ptr = gv_target->alt_hist;
	old_blk1_base = blk1ptr->h[level].buffaddr;
	old_blk2_base = blk2ptr->h[level].buffaddr;
	old_blk1_sz = ((blk_hdr_ptr_t)old_blk1_base)->bsiz;
	old_blk2_sz = ((blk_hdr_ptr_t)old_blk2_base)->bsiz;
	if (0 != level && SIZEOF(blk_hdr) + BSTAR_REC_SIZE == old_blk1_sz)
		old_ref_star_only = TRUE;
	/* Search an ancestor block at levelp >= level+1,
	which has a real key value corresponding to the working block.
	This key value will be changed after coalesce.  */
	levelp = level;
	do
	{
		if (++levelp > blk1ptr->depth ||  levelp > blk2ptr->depth)
		{
			assert(t_tries < CDB_STAGNATE);
			return cdb_sc_blkmod;
		}
		old_levelp_blk_base = blk1ptr->h[levelp].buffaddr;
		old_levelp_rec_offset = blk1ptr->h[levelp].curr_rec.offset;
		rec_base = old_levelp_blk_base + old_levelp_rec_offset;
		GET_RSIZ(rec_size, rec_base);
	} while (BSTAR_REC_SIZE == rec_size); /* search ancestors to get a real value */

	/*
	old_levelp_cur_prev_key = real value of the key before the curr_key at levelp
	old_levelp_cur_prev_keysz = uncompressed size of the key
	Note: we may not have a previous key (old_levelp_cur_prev_keysz = 0)
	*/
	if (SIZEOF(blk_hdr) == old_levelp_rec_offset)
		old_levelp_cur_prev_keysz = 0;
	else
	{
		if (cdb_sc_normal != (status = gvcst_expand_any_key (old_levelp_blk_base, rec_base,
			&old_levelp_cur_prev_key[0], &rec_size, &tkeylen, &tkeycmpc, NULL)))
		{
			assert(t_tries < CDB_STAGNATE);
			return cdb_sc_blkmod;
		}
		old_levelp_cur_prev_keysz = tkeylen + tkeycmpc;
	}

	/*
	old_levelp_cur_key = real value of the curr_key at levelp
	old_levelp_cur_keysz = uncompressed size of the key
	old_levelp_cur_keylen = compressed size of the key
	*/
	READ_RECORD(levelp, rec_base, tkeycmpc, rec_size,  &old_levelp_cur_key[0], old_levelp_cur_keylen, status);
	if (cdb_sc_normal != status)
	{
		assert(t_tries < CDB_STAGNATE);
		return cdb_sc_blkmod;
	}
	if (old_levelp_cur_prev_keysz)
		memcpy(&old_levelp_cur_key[0], &old_levelp_cur_prev_key[0], tkeycmpc);
	rec_base += rec_size;
	old_levelp_cur_keysz = old_levelp_cur_keylen + tkeycmpc;

	/*
	old_levelp_cur_next_key = uncompressed value of the next right key of old_levelp_cur_key
	old_levelp_cur_next_keysz = uncomressed size of the key
	old_levelp_cur_next_keylen = comressed size of the key
		Note: we may not have a next key (old_levelp_cur_next_keysz = 0)
	*/
	BLK_ADDR(old_levelp_cur_next_key, gv_cur_region->max_key_size + 1, unsigned char);
	READ_RECORD(levelp, rec_base, tkeycmpc, rec_size, old_levelp_cur_next_key, old_levelp_cur_next_keylen, status);
	if (cdb_sc_starrecord == status)
		levelp_next_is_star = TRUE;
	else if (cdb_sc_normal != status)
	{
		assert(t_tries < CDB_STAGNATE);
		return cdb_sc_blkmod;
	} else
	{
		memcpy(old_levelp_cur_next_key, &old_levelp_cur_key[0], tkeycmpc);
		old_levelp_cur_next_keysz = old_levelp_cur_next_keylen + tkeycmpc;
		levelp_next_is_star = FALSE;
	}


	/*
	Now process the actual working block at current level
		oldblk1_last_key = real value of last key of the working block
			For index block decompress *-key
		oldblk1_last_keylen = compressed size of the last key
		oldblk1_last_cmpc = compression count of last key of working block
		old_last_rec_hdr1 = New working index block's last record header
	*/
	BLK_ADDR(oldblk1_last_key, gv_cur_region->max_key_size + 1, unsigned char);
	if (0 == level) /* data block */
	{
		if (cdb_sc_normal != (status = gvcst_expand_any_key (old_blk1_base, old_blk1_base + old_blk1_sz,
			oldblk1_last_key, &rec_size, &oldblk1_last_keylen, &oldblk1_last_cmpc, NULL)))
		{
			assert(t_tries < CDB_STAGNATE);
			return cdb_sc_blkmod;
		}
		rec_base = old_blk1_base + old_blk1_sz;
	} else  /* Index blocks */
	{
		/* Since we will join this working block with the right sibling,
		we need to remove the *-key at the end of working block
		and replace with actual key value (with required compression).
		We will get the real value of *-rec from its ancestor at levelp */
                memcpy (oldblk1_last_key, &old_levelp_cur_key[0], old_levelp_cur_keysz);
		if (!old_ref_star_only) /* if the index block is not a *-key only block) */
		{
			if (cdb_sc_normal != (status = gvcst_expand_any_key (old_blk1_base,
				old_blk1_base + old_blk1_sz - BSTAR_REC_SIZE, &oldblk1_prev_key[0],
				&rec_size, &tkeylen, &tkeycmpc, NULL)))
			{
				assert(t_tries < CDB_STAGNATE);
				return cdb_sc_blkmod;
			}
			GET_CMPC(oldblk1_last_cmpc, &oldblk1_prev_key[0], &old_levelp_cur_key[0]);
			oldblk1_last_keylen = old_levelp_cur_keysz - oldblk1_last_cmpc;
		}
		else /* working block has a *-key record only */
		{
			/* get key value from ancestor blocks key */
			oldblk1_last_keylen = old_levelp_cur_keysz;
			oldblk1_last_cmpc = 0;
        	}
                BLK_ADDR(old_last_rec_hdr1, SIZEOF(rec_hdr), rec_hdr);
                old_last_rec_hdr1->rsiz = BSTAR_REC_SIZE + oldblk1_last_keylen;
                SET_CMPC(old_last_rec_hdr1, oldblk1_last_cmpc);
	}

	/*
	newblk1_last_key = new working blocks final appended key
	newblk1_mid_cmpc = new working blocks firstly appended key's cmpc
	newblk1_last_keysz = new working blocks lastly appended key's size
	star_only_merge = TRUE, we can append only a *-key record into the working block
				(decompressing current *-key)
	complete_merge = TRUE, rtsib can be completely merged with working block
	piece_len = Size of data from old rtsibling to be merged into working block (includes rec_hdr size)
	*/
	BLK_ADDR(newblk1_last_key, gv_cur_region->max_key_size + 1, unsigned char);
	rec_base = old_blk2_base + SIZEOF(blk_hdr);
	READ_RECORD(level, rec_base, newblk1_last_cmpc, rec_size, newblk1_last_key, newblk1_last_keylen, status);
	if (cdb_sc_starrecord == status) /* rtsib index block has *-record only */
	{
		if (old_blk1_sz + oldblk1_last_keylen + BSTAR_REC_SIZE > i_max_fill ) /* cannot fit even one record */
			return cdb_sc_oprnotneeded;
		star_only_merge = TRUE;
		complete_merge = TRUE;
		rec_base = old_blk2_base + SIZEOF(blk_hdr) + BSTAR_REC_SIZE;
	} else if (cdb_sc_normal != status)
	{
		assert(t_tries < CDB_STAGNATE);;
		return cdb_sc_blkmod;
	} else /* for both data and non-* index block */
	{
		newblk1_last_keysz = newblk1_last_keylen; /* first key has uncompressed real value */
		GET_CMPC(newblk1_mid_cmpc, oldblk1_last_key, newblk1_last_key);
		piece_len = rec_size - newblk1_mid_cmpc;
		if (level == 0) /* data block */
		{
			if (old_blk1_sz + piece_len > d_max_fill ) /* cannot fit even one record */
				return cdb_sc_oprnotneeded;
		} else /* else an index block */
		{
			if (old_blk1_sz + oldblk1_last_keylen + BSTAR_REC_SIZE > i_max_fill ) /* cannot fit even one record */
				return cdb_sc_oprnotneeded;
			if (old_blk1_sz + oldblk1_last_keylen + piece_len + BSTAR_REC_SIZE > i_max_fill )
					star_only_merge = TRUE; /* can fit only a *-record */
		}
		rec_base += rec_size;
	}

	/* new_blk2_first_rec_base and new_blk1_top is set with final value for star_only_merge  for index block */
	new_blk2_first_rec_base = new_blk1_top = rec_base;
	if (!star_only_merge)
	{
		BLK_ADDR(new_rec_hdr1, SIZEOF(rec_hdr), rec_hdr);
		new_rec_hdr1->rsiz = piece_len;
		SET_CMPC(new_rec_hdr1, newblk1_mid_cmpc);
	}
	/* else only new_blk1_last_key will be appeneded in working block */


	/* find a piece of the right sibling to be copied into the working block.
	Note: rec_base points to 2nd record of old rtsib */
	if (0 == level) /* if data block */
	{
		complete_merge = TRUE;
		while (rec_base < old_blk2_base + old_blk2_sz)
		{
			GET_RSIZ(rec_size, rec_base);
			if (old_blk1_sz + piece_len + rec_size > d_max_fill )
			{
				complete_merge = FALSE;
				break;
			}
			READ_RECORD(level, rec_base, newblk1_last_cmpc, rec_size, newblk1_last_key, newblk1_last_keylen, status);
			if (cdb_sc_normal != status)
			{
				assert(t_tries < CDB_STAGNATE);;
				return cdb_sc_blkmod;
			}
			newblk1_last_keysz = newblk1_last_keylen + newblk1_last_cmpc;
			rec_base += rec_size;
			piece_len += rec_size;
		}/* end of "while" loop */
		new_blk1_top = new_blk2_first_rec_base = rec_base;
	} else /* index block */
	{
		if (!star_only_merge)
		{
			/* we know we can fit more record in working block and rtsibling has more records */
			complete_merge = TRUE;
			while (rec_base < old_blk2_base + old_blk2_sz)
			{
				GET_RSIZ(rec_size, rec_base);
				if (BSTAR_REC_SIZE == rec_size)
				{
					rec_base += rec_size;
					piece_len += rec_size;
					break; /* already we know we can fit this *-record in working block */
				}
				READ_RECORD(level, rec_base, newblk1_last_cmpc, rec_size,
					newblk1_last_key, newblk1_last_keylen, status);
				if (cdb_sc_normal != status)
				{
					assert(t_tries < CDB_STAGNATE);;
					return cdb_sc_blkmod;
				}
				newblk1_last_keysz = newblk1_last_keylen + newblk1_last_cmpc;
				rec_base += rec_size;
				piece_len += rec_size;
				if (old_blk1_sz + oldblk1_last_keylen + piece_len + BSTAR_REC_SIZE > i_max_fill )
				{
					complete_merge = FALSE;
					break;
				}
			}/* end of "while" loop */
			new_blk1_top = new_blk2_first_rec_base = rec_base;
		} /* end else  *-only merge */
	} /* end else index block */

	if (!complete_merge)
	{
		/*
		Adjust new right sibling's buffer
		if new_rtsib_star_only == TRUE then
			new right sibling will have a *-key record only
		else
			new_blk2_remain = base pointer of buffer including 1st record but exclude rec_header and key
			new_blk2_first_keysz = size of new rtsib block's first key
		*/
		BLK_ADDR(newblk2_first_key, gv_cur_region->max_key_size + 1, unsigned char);
		READ_RECORD(level, new_blk2_first_rec_base, tkeycmpc, rec_size,
			newblk2_first_key, newblk2_first_keylen, status);
		if (cdb_sc_starrecord == status) /* new rtsib will have a *-record only */
			new_rtsib_star_only = TRUE;
		else if (cdb_sc_normal != status)
		{
			assert(t_tries < CDB_STAGNATE);;
			return cdb_sc_blkmod;
		} else
		{
			memcpy(newblk2_first_key, newblk1_last_key, tkeycmpc); /* copy the compressed piece */
			newblk2_first_keysz = newblk2_first_keylen + tkeycmpc;
			new_blk2_remain = new_blk2_first_rec_base + SIZEOF(rec_hdr) + newblk2_first_keylen;
			BLK_ADDR(new_rec_hdr2, SIZEOF(rec_hdr), rec_hdr);
			new_rec_hdr2->rsiz = rec_size + tkeycmpc;
			SET_CMPC(new_rec_hdr2, 0);
		}
	}


	/*
	if complete_merge and level+1 <= level2 < levelp,
		if blk2ptr->h[level2].blk_num is *-record block then
			delete it
		else
			prepare to update  blk2ptr->h[level2].blk_num (for first level2>level+1)
			that is, we will delete 1st record pointing to rtsib which is merged with working.
	*/
	else /* complete merge */
	{
		for (level2 = level + 1; level2 < levelp; level2++)
		{
			if (SIZEOF(blk_hdr) + BSTAR_REC_SIZE == ((blk_hdr_ptr_t)blk2ptr->h[level2].buffaddr)->bsiz)
			{
				kill_set_ptr->blk[kill_set_ptr->used].flag = 0;
				kill_set_ptr->blk[kill_set_ptr->used].level = 0;
				kill_set_ptr->blk[kill_set_ptr->used++].block = blk2ptr->h[level2].blk_num;
			} else
			{
				/*
				new_blk2_ances_first_key =  new rtsib's ancestor's 1st key
				new_blk2_ances_first_keysz = new rtsib's ancestor's 1st key size
				blk2_ances_hdr = new rtsib's ancestor's 1st record's header
				*/
				delete_all_blk2_ances = FALSE;
				BLK_ADDR(new_blk2_ances_first_key, gv_cur_region->max_key_size + 1, unsigned char);
				rec_base =  blk2ptr->h[level2].buffaddr + SIZEOF(blk_hdr);
				READ_RECORD(level2, rec_base, tkeycmpc, rec_size, new_blk2_ances_first_key, tkeylen, status);
				if (cdb_sc_normal != status)
				{
					assert(t_tries < CDB_STAGNATE);;
					return cdb_sc_blkmod;
				}
				/* newblk1_last_key was the last key before *-key.
				   So new_blk2_ances_first_key will become real newblk1_last_key. */
				GET_CMPC(newblk1_last_cmpc, newblk1_last_key, new_blk2_ances_first_key);
				newblk1_last_keysz = tkeylen + tkeycmpc;
				newblk1_last_keylen = newblk1_last_keysz - newblk1_last_cmpc;
				memcpy(newblk1_last_key, new_blk2_ances_first_key, newblk1_last_keysz);
				/* 2nd record will become 1st record of current block at level2 */
				rec_base += rec_size;
				READ_RECORD(level2, rec_base, tkeycmpc, rec_size, new_blk2_ances_first_key, tkeylen, status);
				blk2_ances_remain = rec_base + rec_size - SIZEOF(block_id);
				if (cdb_sc_starrecord == status)
					blk2_ances_star_only = TRUE;
				else if (cdb_sc_normal != status)
				{
					assert(t_tries < CDB_STAGNATE);
					return cdb_sc_blkmod;
				} else
				{
					new_blk2_ances_first_keysz = tkeylen + tkeycmpc;
					BLK_ADDR(blk2_ances_hdr, SIZEOF(rec_hdr), rec_hdr); /* new 1st record's header */
					blk2_ances_hdr->rsiz = new_blk2_ances_first_keysz + BSTAR_REC_SIZE;
					SET_CMPC(blk2_ances_hdr, 0);
				}
				break;
			}
		} /* end for level2 */
	} /* end if/else complete_merge */

	/* for following case newblk1_last_key is the 2nd last key.  old_levelp_cur_next_key can be taken as last key */
	/*
	if (delete_all_blk2_ances && complete_merge && !levelp_next_is_star)
	{
		GET_CMPC(newblk1_last_cmpc, newblk1_last_key, &old_levelp_cur_next_key[0]);
		newblk1_last_keysz = old_levelp_cur_next_keysz;
		newblk1_last_keylen = newblk1_last_keysz - newblk1_last_cmpc;
		newblk1_last_key =  old_levelp_cur_next_key;
	}
	*/
	/* else if (delete_all_blk2_ances && complete_merge && levelp_next_is_star),
	   we do not need newblk1_last_key's real value */

	/*
	new_levelp_cur_hdr = new ancestor level curr_key header
	new_levelp_cur_keylen =  new ancestor level curr_key length
	new_levelp_cur_cmpc = new ancestor level curr_key compression count
	*/
	if (!complete_merge || !delete_all_blk2_ances) /* old_levelp_cur_key will be
							replaced by newblk1_last_key */
	{
		if (old_levelp_cur_prev_keysz == 0)	/* If a previous record doesn't exist */
		{
			new_levelp_cur_cmpc = 0;
			new_levelp_cur_keylen = newblk1_last_keysz;
		} else	/* If the previous record exists */
		{
			GET_CMPC(new_levelp_cur_cmpc, &old_levelp_cur_prev_key[0], newblk1_last_key);
			new_levelp_cur_keylen = newblk1_last_keysz - new_levelp_cur_cmpc;
		}
		/*
		forming a new record header for the current record
		in the upper index block
		*/
		BLK_ADDR(new_levelp_cur_hdr, SIZEOF(rec_hdr), rec_hdr);
		new_levelp_cur_hdr->rsiz = BSTAR_REC_SIZE + new_levelp_cur_keylen;
		SET_CMPC(new_levelp_cur_hdr, new_levelp_cur_cmpc);
	}
	/* else  old_levelp_cur_key will be deleted */

	/*
	new_levelp_cur_next_hdr = new ancestor level curr_key's next key's header
	new_levelp_cur_next_keylen =  new ancestor level curr_key's next key's length
	new_levelp_cur_next_cmpc = new ancestor level curr_key's next key's compression count
	*/
	if (!levelp_next_is_star) /* if next record is not a *-record after levelp currkey */
	{
		if (!complete_merge || !delete_all_blk2_ances) /* old_levelp_cur_key will be
			replaced by newblk1_last_key and followed by old_levelp_cur_next_key */
		{
			GET_CMPC(new_levelp_cur_next_cmpc, newblk1_last_key, old_levelp_cur_next_key);
			new_levelp_cur_next_keylen = old_levelp_cur_next_keysz - new_levelp_cur_next_cmpc;
			if (((blk_hdr_ptr_t)old_levelp_blk_base)->bsiz
				- old_levelp_cur_keylen + new_levelp_cur_keylen
				- old_levelp_cur_next_keylen + new_levelp_cur_next_keylen > i_max_fill)
				return cdb_sc_oprnotneeded;
		} else /*	old_levelp_cur_key will be deleted */
		{
			if (old_levelp_cur_prev_keysz == 0) /* If the previous record deos not exist */
			{
				new_levelp_cur_next_cmpc = 0;
				new_levelp_cur_next_keylen = old_levelp_cur_next_keysz;
			} else    /* If the previous record exists */
			{
				GET_CMPC(new_levelp_cur_next_cmpc, &old_levelp_cur_prev_key[0], old_levelp_cur_next_key);
				new_levelp_cur_next_keylen = old_levelp_cur_next_keysz - new_levelp_cur_next_cmpc;
			}
		}
		/*
		forming a new record header for the next record of current record
		in the upper index block
		*/
		BLK_ADDR(new_levelp_cur_next_hdr, SIZEOF(rec_hdr), rec_hdr);
		new_levelp_cur_next_hdr->rsiz = BSTAR_REC_SIZE + new_levelp_cur_next_keylen;
		SET_CMPC(new_levelp_cur_next_hdr, new_levelp_cur_next_cmpc);
	} else
	{
		if (!complete_merge || !delete_all_blk2_ances)
		{
			if (((blk_hdr_ptr_t)old_levelp_blk_base)->bsiz
				- old_levelp_cur_keylen + new_levelp_cur_keylen > i_max_fill)
				return cdb_sc_oprnotneeded;
		}
	}

	BLK_ADDR(star_rec_hdr, SIZEOF(rec_hdr), rec_hdr);
	star_rec_hdr->rsiz = BSTAR_REC_SIZE;
	SET_CMPC(star_rec_hdr, 0);
	/* ------------------------
	 * Working block's t_write
	 * ------------------------
	 */
	BLK_INIT(bs_ptr2, bs_ptr1);
	if (0 == level)
	{	/* if a data block */
		/* adjust old block */
		BLK_SEG(bs_ptr2, old_blk1_base + SIZEOF(blk_hdr), old_blk1_sz - SIZEOF(blk_hdr) );
		/* Join data from right sibling */
		BLK_SEG(bs_ptr2, (sm_uc_ptr_t)new_rec_hdr1, SIZEOF(rec_hdr));
		REORG_BLK_SEG(bs_ptr2, old_blk2_base + SIZEOF(blk_hdr) + SIZEOF(rec_hdr) + newblk1_mid_cmpc,
				piece_len - SIZEOF(rec_hdr));
	} else
	{	/* if an index block */
		BLK_ADDR(bn_ptr1, SIZEOF(block_id), unsigned char);
		memcpy(bn_ptr1, old_blk1_base + old_blk1_sz - SIZEOF(block_id), SIZEOF(block_id));
		/* Keep whatever was there in working block */
		BLK_SEG(bs_ptr2,  old_blk1_base + SIZEOF(blk_hdr), old_blk1_sz - SIZEOF(blk_hdr) - BSTAR_REC_SIZE);
		/* last record was a *-key for index block, so replace with its real value */
		BLK_SEG(bs_ptr2, (sm_uc_ptr_t)old_last_rec_hdr1, SIZEOF(rec_hdr) );
		BLK_SEG(bs_ptr2, oldblk1_last_key + oldblk1_last_cmpc, oldblk1_last_keylen);
		BLK_SEG(bs_ptr2, bn_ptr1, SIZEOF(block_id) );
		/* Now join data from right sibling */
		if (star_only_merge)
		{	/* May be a complete_merge too */
			/* write a new *-rec only */
			BLK_SEG(bs_ptr2, (sm_uc_ptr_t)star_rec_hdr, SIZEOF(rec_hdr) );
			REORG_BLK_SEG(bs_ptr2, new_blk1_top - SIZEOF(block_id), SIZEOF(block_id));
		} else
		{
			if (complete_merge)
			{	/* First key from rtsib had cmpc=0. After coalesce it will be nonzero.
				 * Remainings from rtsib will be appened without change.
				 */
				BLK_SEG(bs_ptr2, (sm_uc_ptr_t)new_rec_hdr1, SIZEOF(rec_hdr));
				REORG_BLK_SEG(bs_ptr2, old_blk2_base + SIZEOF(blk_hdr) + SIZEOF(rec_hdr) + newblk1_mid_cmpc,
					piece_len - SIZEOF(rec_hdr) );
			} else
			{	/* First key from rtsib had cmpc=0. After coalesce it will be nonzero.
				 * Remainings from rtsib will be appened without change.
				 * However last record will be written as a *-key record
				 * (newblk1_last_keylen + BSTAR_REC_SIZE) = old length of the last record appended
				 */
				BLK_SEG(bs_ptr2, (sm_uc_ptr_t)new_rec_hdr1, SIZEOF(rec_hdr));
				REORG_BLK_SEG(bs_ptr2, old_blk2_base + SIZEOF(blk_hdr) + SIZEOF(rec_hdr) + newblk1_mid_cmpc,
					piece_len - (newblk1_last_keylen + BSTAR_REC_SIZE)- SIZEOF(rec_hdr) );
				/* write a new *-rec only */
				BLK_SEG(bs_ptr2, (sm_uc_ptr_t)star_rec_hdr, SIZEOF(rec_hdr) );
				REORG_BLK_SEG(bs_ptr2, new_blk1_top - SIZEOF(block_id), SIZEOF(block_id));
			}
		}
	}
	if ( !BLK_FINI(bs_ptr2, bs_ptr1))
	{
		assert(t_tries < CDB_STAGNATE);
		return cdb_sc_blkmod;
	}
	t_write(&blk1ptr->h[level], (unsigned char *)bs_ptr1, 0, 0, level, FALSE, TRUE, GDS_WRITE_KILLTN);
	/* -----------------
	 * The right sibling
	 * -----------------
	 */
	if (!complete_merge)
	{
		BLK_INIT(bs_ptr2, bs_ptr1);
		if (!new_rtsib_star_only) /* more than one record in rtsib */
		{
			BLK_SEG(bs_ptr2, (sm_uc_ptr_t)new_rec_hdr2, SIZEOF(rec_hdr));
			BLK_SEG(bs_ptr2, newblk2_first_key, newblk2_first_keysz);
			BLK_SEG(bs_ptr2, new_blk2_remain, old_blk2_base + old_blk2_sz - new_blk2_remain );
		} else  /* only a *-key will remain in rtsib after coalesce is done */
		{
			/* write a new *-rec only */
			BLK_SEG(bs_ptr2, (sm_uc_ptr_t)star_rec_hdr, SIZEOF(rec_hdr) );
			BLK_SEG(bs_ptr2, old_blk2_base + old_blk2_sz - SIZEOF(block_id), SIZEOF(block_id));
		}
		if (!BLK_FINI(bs_ptr2,bs_ptr1))
		{
			assert(t_tries < CDB_STAGNATE);
			return cdb_sc_blkmod;
		}
		t_write(&blk2ptr->h[level], (unsigned char *)bs_ptr1, 0, 0, level, TRUE, TRUE, GDS_WRITE_KILLTN);
	} else
	{
		kill_set_ptr->blk[kill_set_ptr->used].flag = 0;
		kill_set_ptr->blk[kill_set_ptr->used].level = 0;
		kill_set_ptr->blk[kill_set_ptr->used++].block = blk2ptr->h[level].blk_num;
	}

	/* --------------------------
	 * ancestor of working block
	 * --------------------------
	 * bn_ptr2 = child of levelp ancestor block of currkey
	 */
	BLK_ADDR(bn_ptr2, SIZEOF(block_id), unsigned char);
	memcpy(bn_ptr2, old_levelp_blk_base + old_levelp_rec_offset + SIZEOF(rec_hdr) + old_levelp_cur_keylen, SIZEOF(block_id));

	BLK_INIT(bs_ptr2, bs_ptr1);
	/* data up to cur_rec */
	BLK_SEG(bs_ptr2, old_levelp_blk_base + SIZEOF(blk_hdr), old_levelp_rec_offset - SIZEOF(blk_hdr) );
	if (!levelp_next_is_star) /* if next record is not a *-record at levelp currkey */
	{
		if (complete_merge && delete_all_blk2_ances)
		{ /* old_levelp_curr_key will be removed and old_levelp_cur_next_key will be inserted there */
			assert (t_tries < CDB_STAGNATE || 0 != new_levelp_cur_next_keylen);
			assert (t_tries < CDB_STAGNATE || (old_levelp_rec_offset - SIZEOF(blk_hdr)) || !new_levelp_cur_next_cmpc);
			/* new header of cur_next instead of cur_rec */
			BLK_SEG(bs_ptr2, (sm_uc_ptr_t)new_levelp_cur_next_hdr, SIZEOF(rec_hdr));
			/* new key value of curr_next_key */
			BLK_SEG(bs_ptr2, old_levelp_cur_next_key + new_levelp_cur_next_cmpc, new_levelp_cur_next_keylen);
			/* new child is the working block (= descendent of levelp ancestor of currkey) */
			BLK_SEG(bs_ptr2, bn_ptr2, SIZEOF(block_id) );
			/* remaining records after levelp cur_next */
			BLK_SEG(bs_ptr2, old_levelp_blk_base + old_levelp_rec_offset +
				old_levelp_cur_keylen + BSTAR_REC_SIZE + old_levelp_cur_next_keylen + BSTAR_REC_SIZE,
				((blk_hdr_ptr_t)old_levelp_blk_base)->bsiz - old_levelp_rec_offset -
				(old_levelp_cur_keylen + BSTAR_REC_SIZE + old_levelp_cur_next_keylen + BSTAR_REC_SIZE));
			forward_process = TRUE;
		} else
		{ /* old_levelp_curr_key will be replaced by newblk1_last_key and old_levelp_cur_next_key will be inserted there */
			assert (t_tries < CDB_STAGNATE || 0 != new_levelp_cur_keylen);
			assert (t_tries < CDB_STAGNATE || 0 != old_levelp_rec_offset - SIZEOF(blk_hdr) || 0 == new_levelp_cur_cmpc);
			/* new header for new cur_rec of levelp */
			BLK_SEG(bs_ptr2, (sm_uc_ptr_t)new_levelp_cur_hdr, SIZEOF(rec_hdr) );
			/* new key value for cur_rec of levelp  */
			BLK_SEG(bs_ptr2, newblk1_last_key + new_levelp_cur_cmpc, new_levelp_cur_keylen);
			/* new child is old child */
			BLK_SEG(bs_ptr2, bn_ptr2, SIZEOF(block_id) );
			/* new header for next record after cur_rec of levelp */
			BLK_SEG(bs_ptr2, (sm_uc_ptr_t)new_levelp_cur_next_hdr, SIZEOF(rec_hdr) );
			/* new key value for cur_next of levelp  */
			BLK_SEG(bs_ptr2, old_levelp_cur_next_key + new_levelp_cur_next_cmpc, new_levelp_cur_next_keylen);
			/* copy old contents after old_levelp_cur_key */
			BLK_SEG(bs_ptr2, old_levelp_blk_base + old_levelp_rec_offset +
				BSTAR_REC_SIZE + old_levelp_cur_keylen + SIZEOF(rec_hdr) + old_levelp_cur_next_keylen,
				((blk_hdr_ptr_t)old_levelp_blk_base)->bsiz - old_levelp_rec_offset -
				(BSTAR_REC_SIZE + old_levelp_cur_keylen + SIZEOF(rec_hdr) + old_levelp_cur_next_keylen));
			forward_process = FALSE;
		}
	} else /* there is *-rec after old_levelp_cur_key */
	{
		if (complete_merge && delete_all_blk2_ances)
		{	/* delete old old_levelp_cur_key and *-key and write new *-key */
			BLK_SEG(bs_ptr2, (sm_uc_ptr_t)star_rec_hdr, SIZEOF(rec_hdr));
			BLK_SEG(bs_ptr2, bn_ptr2, SIZEOF(block_id) );
			forward_process = TRUE;
		} else
		{	/* new header for new cur_rec of levelp */
			BLK_SEG(bs_ptr2, (sm_uc_ptr_t)new_levelp_cur_hdr, SIZEOF(rec_hdr) );
			/* new key value for cur_rec of levelp  */
			BLK_SEG(bs_ptr2, newblk1_last_key + new_levelp_cur_cmpc, new_levelp_cur_keylen);
			/* new child is old child */
			BLK_SEG(bs_ptr2, bn_ptr2, SIZEOF(block_id) );
			/* old *-rec */
			BLK_SEG(bs_ptr2, old_levelp_blk_base + ((blk_hdr_ptr_t)old_levelp_blk_base)->bsiz - BSTAR_REC_SIZE,
				BSTAR_REC_SIZE);
			forward_process = FALSE;
		}
	}
	if (!BLK_FINI(bs_ptr2, bs_ptr1))
	{
		assert(t_tries < CDB_STAGNATE);
		return cdb_sc_blkmod;
	}
	t_write(&blk1ptr->h[levelp], (unsigned char *)bs_ptr1, 0, 0, levelp, FALSE, forward_process, GDS_WRITE_KILLTN);
	/* ---------------------------------------------------------------------------
	 * if delete_all_blk2_ances and level+1 <= level2 < levelp,
	 * 	if blk2ptr->h[level2].blk_num are *-record blocks
	 * 		we already deleted them
	 * 	else
	 * 		update  blk2ptr->h[level2].blk_num for first level2>level+1
	 * Note: delete_all_blk2_ances == FALSE => complete_merge = TRUE;
	 */
	if (!delete_all_blk2_ances)
	{
		BLK_INIT(bs_ptr2, bs_ptr1);
		if (blk2_ances_star_only)
		{
			BLK_SEG(bs_ptr2, (sm_uc_ptr_t)star_rec_hdr, SIZEOF(rec_hdr));
		} else
		{
			BLK_SEG(bs_ptr2,  (sm_uc_ptr_t)blk2_ances_hdr, SIZEOF(rec_hdr));
			BLK_SEG(bs_ptr2, new_blk2_ances_first_key,  new_blk2_ances_first_keysz);
			assert (t_tries < CDB_STAGNATE || 0 != new_blk2_ances_first_keysz);
		}
		BLK_SEG(bs_ptr2, blk2_ances_remain,  blk2ptr->h[level2].buffaddr +
			((blk_hdr_ptr_t)blk2ptr->h[level2].buffaddr)->bsiz - blk2_ances_remain);
		if (!BLK_FINI(bs_ptr2,bs_ptr1))
		{
			assert(t_tries < CDB_STAGNATE);
			return cdb_sc_blkmod;
		}
		t_write(&blk2ptr->h[level2], (unsigned char *)bs_ptr1, 0, 0, level2, TRUE, TRUE, GDS_WRITE_KILLTN);
	}
	/* else do not need to change blk2ptr->h[level2].blk_num.
	 * Because, for level+1 == levelp this is the same as levelp ancestor block (blk1ptr->h[levelp].blk_num).
	 * For levelp > level + 1, and a complete_merge, levelp ancestor block will take care of the case.
	 * For levelp > level + 1, and not complete_merge,  old blk2ptr->h[level2].blk_num records are still valid.
	 * 	(only leftmost records from the collation sequence are moved to the working block
	 * 	and still blk2ptr->h[level+1] correctly points to the
	 * 	right most value of collation sequence at correct block.)
	 */
	*remove_rtsib = complete_merge;
	/* prepare next gv_currkey for reorg */
	if (0 == level && !complete_merge)
	{
		memcpy(&gv_currkey_next_reorg->base[0], newblk2_first_key, newblk2_first_keysz);
		gv_currkey_next_reorg->end = newblk2_first_keysz - 1;
	}
	return cdb_sc_normal;
} /* end of the program */
