# include	"../ingres.h"
# include	"../aux.h"
# include	"../access.h"

findbest(dx, tidx, tuple, need, checkdups)
struct descriptor	*dx;
struct tup_id		*tidx;
char			*tuple;
int			need;
int			checkdups;

/*
**	Findbest - find the "best" place to put a tuple.
**	Findbest does not actually put the tuple but rather
**	returns and allocates the tid for the tuple.
**
**	The initial part of the algorithm depends on whether
**	the relation is a heap or not.
**
**	If the relation is a heap, if there is a current page
**	with room for the tuple, that page is used. Otherwise
**	the last page of the heap is considered.
**
**	If the relation is hash or isam, then "find" is used
**	to determine the primary page for the tuple.
**
**	If necessary, findbest will allocate an overflow page
**	if there is not sufficient room for the tuple otherwise.
**
**	If checkdups is TRUE and the relation is not a heap,
**	findbest will check for duplicates.
**
**	Returns:
**
**		0 tuple not a duplicate, tid allocated
**		1 tuple a duplicate of the tuple at tid
*/

{
	register struct descriptor	*d;
	register struct tup_id		*tid;
	register int			i;
	struct tup_id			temptid;

	d = dx;
	tid = tidx;


	if (abs(d->relspec) == M_HEAP)
	{
		checkdups = FALSE;
		/* determine a page to place tuple in heap relation */
		find_page(d, tid, need);

	}
	else
	{
		/* find a suitable page for isam or hash */
		/* determine primary page */
		if (i = find(d, FULLKEY, tid, tid, tuple))
		{
			return (i);	/* fatal error */
		}

		/* If we are not checking for duplicates then take any
		** convenient page linked to the main page current indicated
		** in "tid"
		*/
		if (!checkdups)
			find_page(d, tid, need);
	}

	/* search the chain of pages looking for a spot */
	for (;;)
	{
		if (i = get_page(d, tid))
			break;		/* fatal error */

		/* if tuple is duplicate, drop out */
		if (checkdups && dup_check(d, tid, tuple))
		{
			i = 1;
			break;
		}

		/* is there space on this page */
		if (space_left(Acc_head) >= need)
			break;	/* found a page to use */

		/* no space yet. look on next overflow page */
		if (Acc_head->ovflopg)
		{
			stuff_page(tid, &Acc_head->ovflopg);
			continue;
		}

		/* no space. allocate new overflow page */
		if (i = add_ovflo(d, tid))
			break;		/* fatal error */
	}

	/* check for dups on remaining overflow pages */
	/* check only if there hasn't been a dup or a page error */
	if (i == 0 && checkdups && Acc_head->ovflopg)
	{
		stuff_page(&temptid, &Acc_head->ovflopg);
		if (i = scan_dups(d, &temptid, tuple))
			bmove(&temptid, tid, sizeof(temptid));	/* tid of duplicate */
	}

	/* if tuple isn't a duplicate, allocate a line number */
	if (i == 0)
		tid->line_id = newlino(need);

#	ifdef xATR1
	if (tTf(88, 2))
	{
		printf("findbest ret %d,", i);
		dumptid(tid);
	}
#	endif
	return (i);
}


find_page(dx, tid, need)
struct descriptor	*dx;
struct tup_id		*tid;
int			need;

/*
**	Find an appropriate page to put a tuple.
**	If HEAP then any page with room will do. If none
**	can be found, then use the last page.
**	If it is a user relation and a page was found but
**	was full, use it anyway. This can happen only on a
**	modify (which has checkdups turned off).
**
**	For ISAM or HASH look for a page on the same mainpage
**	chain. Duplicate checking must not be enforced.
**
**	The first page to use will be returned in tid in either
**	case.
*/

{
	register struct descriptor	*d;
	register struct accbuf		*b, *maxbf;
	int				heap;
	long				mainpg;

	d = dx;
	maxbf = NULL;
	heap = abs(d->relspec) == M_HEAP;
	pluck_page(tid, &mainpg);
	mainpg++; /* mainpage in buffer points to next higher mainpage */

	/* scan all current buffers looking for one belonging to this relation */
	for (b = Acc_head; b != NULL; b = b->modf)
	{
		if (d->reltid == b->rel_tupid && !(b->bufstatus & BUF_DIRECT)
			&& (heap || (b->mainpg == mainpg)))
		{
			if (space_left(b) >= need)
			{
				/* use this page */
				stuff_page(tid, &b->thispage);
				return;
			}

			/* save buffer of largest page */
			if (maxbf == NULL || maxbf->thispage < b->thispage)
				maxbf = b;
		}
	}

	if (heap)
		last_page(d, tid, maxbf);
	else
	{
		/* if we found a full page of a user's relation,use it */
		if (maxbf && (d->relstat & S_CATALOG) == 0)
			stuff_page(tid, &maxbf->thispage);
	}
}
