/****************************************************************
 *								*
 * Copyright (c) 2001-2019 Fidelity National Information	*
 * Services, Inc. and/or its subsidiaries. All rights reserved.	*
 *								*
 *	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.	*
 *								*
 ****************************************************************/

#include "mdef.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 "copy.h"
#include "filestruct.h"		/* needed for jnl.h */
#include "gdscc.h"		/* needed for tp.h */
#include "jnl.h"		/* needed for tp.h */
#include "gdskill.h"		/* needed for tp.h */
#include "buddy_list.h"		/* needed for tp.h */
#include "tp.h"			/* needed for T_BEGIN_READ_NONTP_OR_TP macro */
#ifdef UNIX			/* needed for frame_pointer in GVCST_ROOT_SEARCH_AND_PREP macro */
# include "repl_msg.h"
# include "gtmsource.h"
# include "rtnhdr.h"
# include "stack_frame.h"
# include "wbox_test_init.h"
#endif

#include "t_end.h"		/* prototypes */
#include "t_retry.h"
#include "t_begin.h"
#include "gvcst_protos.h"	/* for gvcst_rtsib,gvcst_search,gvcst_search_blk,gvcst_data prototype */

/* needed for spanning nodes */
#include "op.h"
#include "op_tcommit.h"
#include "error.h"
#include "tp_frame.h"
#include "tp_restart.h"
#include "gtmimagename.h"

LITREF	mval		literal_batch;

GBLREF gv_key		*gv_currkey, *gv_altkey;
GBLREF gv_namehead	*gv_target;
GBLREF sgmnt_addrs	*cs_addrs;
GBLREF gd_region	*gv_cur_region;
GBLREF uint4		dollar_tlevel;
GBLREF unsigned int	t_tries;

error_def(ERR_GVDATAFAIL);

DEFINE_NSB_CONDITION_HANDLER(gvcst_data_ch)

mint	gvcst_data(void)
{
	bool		found, sn_tpwrapped;
	boolean_t	est_first_pass;
	int		oldend;
	mint		val;
	int		save_dollar_tlevel;

	DEBUG_ONLY(save_dollar_tlevel = dollar_tlevel);
	val = gvcst_data2();
	INCR_GVSTATS_COUNTER(cs_addrs, cs_addrs->nl, n_data, (gtm_uint64_t) 1);
	WBTEST_ONLY(WBTEST_QUERY_HANG,
		LONG_SLEEP(2); // we need this to let things see the incremented value
	);
#	ifdef UNIX
	if (-1 != val)
	{
		assert(save_dollar_tlevel == dollar_tlevel);
		return val;
	}
	oldend = gv_currkey->end;
	if (!dollar_tlevel)
	{
		sn_tpwrapped = TRUE;
		op_tstart((IMPLICIT_TSTART), TRUE, &literal_batch, 0);
		ESTABLISH_NORET(gvcst_data_ch, est_first_pass);
		GVCST_ROOT_SEARCH_AND_PREP(est_first_pass);
		WBTEST_ONLY(WBTEST_QUERY_HANG,
				LONG_SLEEP(2);
		);
		val = gvcst_data2();
	} else
		sn_tpwrapped = FALSE;
	if (-1 == val)
	{	/* -1 implies node exists. Need to see if a proper descendant exists */
		val = 1;
		/* 0 1 0 0 <-- append that to gv_currkey */
		assert(oldend == gv_currkey->end);
		GVKEY_INCREMENT_QUERY(gv_currkey);
		found = gvcst_query(); /* want to save gv_altkey? */
		if (found && (0 == memcmp(gv_currkey->base, gv_altkey->base, oldend)))
			val += 10;
	}
	if (sn_tpwrapped)
	{
		op_tcommit();
		REVERT; /* remove our condition handler */
	}
	RESTORE_CURRKEY(gv_currkey, oldend);
	assert(save_dollar_tlevel == dollar_tlevel);
#	endif
	return val;
}

mint	gvcst_data2(void)
{
	blk_hdr_ptr_t	bp;
	boolean_t	do_rtsib, is_dummy;
	enum cdb_sc	status;
	mint		val;
	rec_hdr_ptr_t	rp;
	unsigned short	match, rsiz;
	srch_blk_status *bh;
	srch_hist	*rt_history;
	sm_uc_ptr_t	b_top;
	int		tmp_cmpc;
	int		data_len, cur_val_offset, realval = 0;

	VMS_ONLY(assert((gv_target->root < cs_addrs->ti->total_blks) || dollar_tlevel));
	T_BEGIN_READ_NONTP_OR_TP(ERR_GVDATAFAIL);
	assert(t_tries < CDB_STAGNATE || cs_addrs->now_crit);	/* we better hold crit in the final retry (TP & non-TP) */
	for (;;)
	{
		/* The following code is duplicated in gvcst_dataget. Any changes here might need to be reflected there as well */
		rt_history = gv_target->alt_hist;
		rt_history->h[0].blk_num = 0;
#if defined(DEBUG) && defined(UNIX)
		if (gtm_white_box_test_case_enabled && (WBTEST_ANTIFREEZE_GVDATAFAIL == gtm_white_box_test_case_number))
		{
			t_retry(cdb_sc_blknumerr);
			continue;
		}
#endif
		if (cdb_sc_normal != (status = gvcst_search(gv_currkey, NULL)))
		{
			t_retry(status);
			continue;
		}
		bh = gv_target->hist.h;
		bp = (blk_hdr_ptr_t)bh->buffaddr;
		rp = (rec_hdr_ptr_t)(bh->buffaddr + bh->curr_rec.offset);
		b_top = bh->buffaddr + bp->bsiz;
		match = bh->curr_rec.match;
		do_rtsib = FALSE;
		realval = 0;
		if (gv_currkey->end + 1 == match)
		{
			val = 1;
			GET_USHORT(rsiz, &rp->rsiz);
#			ifdef UNIX
			/* check for spanning node dummy value: a single zero byte */
			cur_val_offset = SIZEOF(rec_hdr) + match - EVAL_CMPC((rec_hdr_ptr_t)rp);
			data_len = rsiz - cur_val_offset;
			is_dummy = IS_SN_DUMMY(data_len, (sm_uc_ptr_t)rp + cur_val_offset);
			if (is_dummy)
			{
				realval = -1;
				IF_SN_DISALLOWED_AND_NO_SPAN_IN_DB(realval = 0); /* resume since this is not a spanning node */
			}
#			endif
			rp = (rec_hdr_ptr_t)((sm_uc_ptr_t)rp + rsiz);
			if ((sm_uc_ptr_t)rp > b_top)
			{
				t_retry(cdb_sc_rmisalign);
				continue;
			} else if ((sm_uc_ptr_t)rp == b_top)
				do_rtsib = TRUE;
			else if (EVAL_CMPC(rp) >= gv_currkey->end)
				val += 10;
		} else if (match >= gv_currkey->end)
			val = 10;
		else
		{
			val = 0;
			if (rp == (rec_hdr_ptr_t)b_top)
				do_rtsib = TRUE;
		}
		if (do_rtsib && (cdb_sc_endtree != (status = gvcst_rtsib(rt_history, 0))))
		{
			if ((cdb_sc_normal != status) || (cdb_sc_normal != (status = gvcst_search_blk(gv_currkey, rt_history->h))))
			{
				t_retry(status);
				continue;
			}
			if (rt_history->h[0].curr_rec.match >= gv_currkey->end)
			{
				assert(1 >= val);
				val += 10;
			}
		}
		if (!dollar_tlevel)
		{
			if ((trans_num)0 == t_end(&gv_target->hist, 0 == rt_history->h[0].blk_num ? NULL : rt_history,
				TN_NOT_SPECIFIED))
				continue;
		} else
		{
			status = tp_hist(0 == rt_history->h[0].blk_num ? NULL : rt_history);
			if (cdb_sc_normal != status)
			{
				t_retry(status);
				continue;
			}
		}
		return (0 != realval) ? realval : val;
	}
}
