#include	<btree.h>
#include	<ingres.h>
#include	<opsys.h>
#include	<sccs.h>

SCCSID(@(#)btree_util.c	8.3	5/30/88)

/*	Trace up to the root of the BTree, incrementing (or decrementing) all
**	key values.
**
**	Parameters :
**		tree - BTree filename (I)
**		block - starting node from which path is traced (I)
**		inc - either +1 or -1 (I)
*/

tracepath(rootpage, block, inc)

long rootpage;
register struct locator *block;
register int inc;
{
	struct locator 	p, child;

	if (block->pageno != rootpage)	/* if at root, no need for adjustment */
	{
		bmove(block, &child, sizeof(struct locator));

		/* trace up to root node */
		do
		{
			p.pageno = child.page.parent;
			parentkey(child.pageno, &p);
			p.page.node.intnode.key[p.offset] += inc;
			write_node(p.pageno, &p.page);
			bmove(&p, &child, sizeof(struct locator));
		} while (p.pageno != rootpage);
	}
}

/*	Determines which key/ptr pair is the parent of the given page
**
**	Parameters :
**		tree - BTree filename (I)
**		block - page number of child (I)
**		prt - information about the parent of the child (I, O)
**
*/

parentkey(block, prt)

long block;
register struct locator *prt;
{
	register int	i;

	get_node(prt->pageno, &prt->page);
	/* find pointer in parent which references desired block */
	for (i = 0; prt->page.node.intnode.ptr[i] != block && i < prt->page.nelmts; ++i)
		continue;

	if (i == prt->page.nelmts)
		syserr("parentkey: child = %ld", block);
	prt->offset = i;
	return(0);
}

/*	Retrieve a node from the BTree
**
**	Parameters :
**		tree - BTree filename (I)
**		pagenum - page number of page to be retrieved (I)
**		buffer - buffer into which page is retrieved (O)
*/

get_node(pagenum, buffer)

long pagenum;
register struct BTreeNode *buffer;
{
	extern int	Btree_fd;

	if ( lseek(Btree_fd, pagenum * PGSIZE, 0) == -1 )
		syserr("GET_NODE: seek to %ld failed",pagenum);
	if (read(Btree_fd, buffer, sizeof *buffer) != sizeof *buffer)
		syserr("GET_NODE: read btree, page %ld", pagenum);
}

/*	Write a node into the BTree file name 
**
**	Parameters :
**		tree - BTree filename (I)
**		pagenum - page number of page to be written (I)
**		buffer - buffer containing page to be written (I)
*/

write_node(pagenum, buffer)

long pagenum;
register struct BTreeNode *buffer;
{
	extern int	Btree_fd;

	lseek(Btree_fd, pagenum * PGSIZE, 0);
	if (write(Btree_fd, buffer, PGSIZE) != PGSIZE)
		syserr("WRITE_NODE: write btree, page %ld", pagenum);
}

/*	Retrieves a new page from the BTree file.  If there is an empty page
**	in the file, that page is used.  Otherwise, a new page is added to
**	the end of the file.
**
**	Parameters :
**		tree - BTree filename (I)
**
**	Returns :
**		page number of new page
*/

long
new_page(tree)

char *tree;
{
	int 			i;
	long			freepage, newpage;
	struct BTreeNode 	page, root, garbage;
	struct stat		fileinfo;
	extern DESC		Btreesec;

	get_node(RT, &root);
	freepage = root.parent;
	if (freepage == 0)
	/* no free pages in file, add a new page to the end */
	{
		/* determine page number of page at very end of file */
		stat(tree, &fileinfo);
		newpage = fileinfo.st_size / PGSIZE;
		write_node(newpage, &page);
	}
	else
	/* use first free page, adjusting free page chain */
	{
		newpage = freepage;
		get_node(newpage, &garbage);
		root.parent = garbage.parent;
		write_node(RT, &root);
	}
	return(newpage);
}

/*	Returns an empty file to the empty file list.  The head of the list
**	is indicated through the parent of the root and linked together
**	through the parents of the empty pages.
**
**	Parameters :
**		tree - BTree filename (I)
**		pagenum - page number of empty page (I)
*/

return_page(pagenum)

long pagenum;
{
	struct BTreeNode	root, freepage;	

	/* indicate that page is free by inserting its page number at the
	** head of the free page list */
	get_node(RT, &root);
	get_node(pagenum, &freepage);
	freepage.parent = root.parent;
	freepage.depth = 0;
	write_node(pagenum, &freepage);
	root.parent = pagenum;
	write_node(RT, &root);
}

/*
**	Determine the lid that corresponds to a given position in the B-Tree.
**
**	Parameters :
**		tree - BTree name (I)
**		tidloc - location in BTree (I)
**
**	Returns :
**		lid value
*/
get_lid(tidloc, lid)

TID *tidloc;
long lid[];
{
	static struct locator	tid;
	register int		i, j;
	long			l, child;

	do
	{
		/* determine where in BTree to search */
		pluck_page(tidloc, &tid.pageno);
	
		get_node(tid.pageno, (char *) &tid.page);
		tid.offset = tid.page.node.leafnode.back_ptr[tidloc->line_id & I1MASK];

		if (tid.offset > tid.page.nelmts - 1)
			syserr("get_lid: bad tid location %ld, tid.offset=%d, tid.page.nelmts=%d", *tidloc, tid.offset,tid.page.nelmts);
	
		/* scan through leaf */
		for (l = 1, i = tid.offset; i > 0; ++l, --i)
			continue;
		/* trace up to root, adding keys */
		while (!tid.page.depth)
		{
			child = tid.pageno;
			tid.pageno = tid.page.parent;
			parentkey(child, &tid);
			for (i = tid.offset - 1; i >= 0; l += tid.page.node.intnode.key[i--])
				continue;
		}
		lid[tid.page.depth - 1] = l;
#		ifdef xATR1
			if (tTf(24,0))
				printf("lid=%ld\n", lid[tid.page.depth - 1]);
#		endif
		bmove(&tid.page.prttree, tidloc, LIDSIZE);
	} while (tid.page.depth > 1);
}

/*
**	Uses the secondary btree relation to find the btree tid corresponding 
**	to a given main relation tid
*/

search_btree(mtid, tidloc)

TID mtid; 
register TID *tidloc;
{
	char		key[2 * LIDSIZE], tup[2 * LIDSIZE];
	TID		tid;
	extern DESC	Btreesec;

#	ifdef xATR1
		if (tTf(24,0))
		{
			printf("In Search_btree: searching for tid %ld\n", mtid);
			printdesc(&Btreesec);
		}
#	endif
	clearkeys(&Btreesec);
	setkey(&Btreesec, key, &mtid, 1);
	if (!getequal(&Btreesec, key, tup, &tid))
		bmove(tup + LIDSIZE, tidloc, LIDSIZE);
	else
		syserr("search_btree:can't find mtid %ld in btree", mtid);
#	ifdef xATR1
		if (tTf(24,0))
			printup(&Btreesec, tup);
#	endif
}

/*
**	Linearly searches the leaves of the BTree for a desired tid
**
**	Parameters :
**		tree - BTree file name (I)
**		tid - desired tid value (I)
**		tidloc - location of the desired tid (O)
*/

lin_search(level, tid, tidloc, lid, tupcnt)

int level;
long tid;
TID *tidloc;
long lid[];
long tupcnt;
{
	struct locator	block;
	register int	i;
	long		page, t, next;
	int		found;
	long		nextpage, count;
	int		forward, first;

	page = RT;
	for (i = 0; i < level - 1; ++i)
	{
		t = get_tid(page, lid[i], &block);
		page = t;
	}

	found = 0;
	forward  = 0;
	first = 1;
	do
	{
		if (!forward)
		{
			get_node(page, &block.page);
			next = block.page.nexttree;
		}
		count = 0;

		/* start at leftmost leaf */
		do
		{
			get_node(page, &block.page);
			if (forward)
				nextpage = block.page.nexttree;
			else
				nextpage = block.page.prevtree;
			get_tid(page, 1, &block);
			for ( ; ; )
			{
				/* search through leaf */
				for (i = 0; i < block.page.nelmts && block.page.node.leafnode.tid_pos[block.page.node.leafnode.tid_loc[i]] != tid; ++i)
				{
					if (!first)
						++count;
				}
				if (i < block.page.nelmts && block.page.node.leafnode.tid_pos[block.page.node.leafnode.tid_loc[i]] == tid)
				{
					found = 1;
					break;	/* tid found */
				}
				first = 0;
				if (!(block.pageno = block.page.node.leafnode.nextleaf) || count >= tupcnt)
					break;	/* all leaves exhausted */
				else
					/* try leaf to the right */
					get_node(block.pageno, &block.page);
			}
			if (found || count >= tupcnt)
				break;
		} while (page = nextpage);
		nextpage = next;
		forward = !forward;
	} while (forward != 0 && !found);
	if (!found)
		syserr("search_btree: tid not found %ld", tid);
	else
	{
		stuff_page(tidloc, &block.pageno);
		tidloc->line_id = block.page.node.leafnode.tid_loc[i];
	}
}

/*
**	Determines the value corresponding to the very last lid in the
**	BTree.
**
**	Parameters :
**		tree - BTree file name (I)
**
**	Returns :
**		value of last lid
*/
long
last_lid(rootpage)

long rootpage;

{
	register int 		i;
	long			lid;
	struct BTreeNode	root;

	lid = 0;
	get_node(rootpage, &root);
	if (root.nodetype == 'L')
		lid = root.nelmts;
	else
		for (i = 0; i < root.nelmts; ++i)
			lid += root.node.intnode.key[i];
	++lid;
	return(lid);
}

/*
**	Changes the tid value stored in the leaf of a BTree node
**
**	Parameters :
**		tree - BTree file name (I)
**		tid - new tid value (I)
**		tidloc - location of tid to be replaced (I)
*/

replace_btree(tid, tidloc)

long tid;
register TID *tidloc;

{
	long			pageno;
	struct BTreeNode	p;

	pluck_page(tidloc, &pageno);
	get_node(pageno, &p);
	p.node.leafnode.tid_pos[tidloc->line_id] = tid;
	write_node(pageno, &p);
}
