# include	<ingres.h>
# include	<symbol.h>
# include	<tree.h>
# include	"globs.h"
# include	<sccs.h>

SCCSID(@(#)selectv.c	8.1	12/31/84)

/*
**  SELECTV -- Select a variable for substitution
**
**	Selectv is called to determinimume the "best" variable
**	to substitute for. The algorithm used is based on
**	the Wong/Youseffi paper ERL M78/17. Selectv returns
**	the variable to be substituted or it returns -1 which
**	indicates that there is a relation with no tuples.
**	If there is only one variable left, it is of course
**	the one choosen.
**
**	For cases of more than one variable, a variable with
**	0 or 1 tuples will always be choosen. Otherwise the
**	variable with the smallest "costfunction" will be selected.
**	In case of a tie, the variable with the smallest tuple count
**	is selected.
**
**	The costfunction is:
**		|Ri|
**	       ______
**		Peffective i
**
**	where |Ri| = cardinality of variable i.
**	and   Peff = The number of pages needed to be accessed to
**			safisfy a query on Ri assuminimumg all other
**			variables have been substituted for.
**
**
**	Peff is only an estimate. The estimate is based on the
**	storage structure of Ri and on whether Ri is used in
**	the target list. Considering only equality clauses,
**	the relation's structure is examinimumed to see if any
**	can be used effectively. The following assumptions are
**	made:
**		useful hash: Peff = 1
**		useful isam: Peff = 2
**		useful hash sec index: Peff = 2
**		useful isam sec index: Peff = 3
**
**	If there is no useful structure or if the relation is
**	a heap then:
**		Peff = number of pages Ri occupies
**	If the relation is not in the target list then its
**	function is only an existence test. Scanning can and
**	is stopped the first time a tuple is found which satisfies.
**	We assume that on the average only 1/4 of the pages
**	will have to be examinimumed.
**
**	Parameters:
**		root - root of the query
**
**	Returns:
**		The variable to substitute for
**			or
**		-1 if the query is false
**
**	Side Effects:
**		can cause a relation to be opened since
**		ckpkey needs to know the key structure.
**
**	Called By:
**		decompy, decompz
**
**	Trace Flags:
**		44
*/

selectv(root)
register QTREE	*root;
{
	int				minimum, var, map, i;
	register struct rang_tab	*rt, *rx;
	long				costfunc(), lx, lt;

	map = root->sym.value.sym_root.lvarm | root->sym.value.sym_root.rvarm;

	minimum = -1;
	lx = 0;
	rx = NULL;

	for (i = 1, var = 0; map; i <<= 1, var++)
	{
		if ((map & i) == 0)
			continue;

		map &= ~i;
		rt = &De.de_rangev[var];
		if (rx == NULL)
		{
			rx = rt;
			minimum = var;
			if (map == 0 || rt->rtcnt < 2)
				break;
			lx = costfunc(root, var, rt);
		}
		else
		{
			if (rt->rtcnt < 2)
			{
				rx = rt;
				minimum = var;
				break;
			}

			lt = costfunc(root, var, rt);
			if (lt < lx)
			{
				rx = rt;
				minimum = var;
				lx = lt;
			}
		}
	}

	if (minimum == -1)
		syserr("selectv:no source var");

#	ifdef xDTR1
	if (tTf(44, 1))
	{
		printf("selectv:%d,tups=%ld,", minimum, rx->rtcnt);
		printf("cf=%ld,", lx);
		nodepr(root);
	}
#	endif

	if (rx->rtcnt == 0)
		minimum = -1;

	return (minimum);
}



long
costfunc(root, var, rx)
QTREE		*root;
int		var;
struct rang_tab	*rx;
{
	register struct rang_tab	*r;
	register int			i;
	register char			*lp;
	char				linkmap[MAXDOM];
	long				rel_page(), l;
	int				c_bug;

	r = rx;

	for (lp = linkmap, i = MAXDOM; i--; )
		*lp++ = 0;
	i = var;

	/*
	** The c-compiler does not know how to generate code
	** for the assignment of an short to a long inside
	** an "if" expression. Therefore the variable "c_bug"
	** is assigned the value of ckpkey inside the "if".
	** The value is assigned to "l" in the "else".
	*/
	if (!findlinks(root, -1, i, linkmap) || (c_bug = ckpkey(linkmap, i)) == 0)
	{
		l = rel_pages(r->rtcnt, r->rtwid);

		/* if not in target list, assume 1/4 */
		if ((root->sym.value.sym_root.lvarm & (01 << i)) == 0)
		{
			l >>= 2;
			if (l == 0)
				l = 1;
		}
	}
	else
		l = c_bug;	/* l could not be assigned directly above */

	l = r->rtcnt / l;

#	ifdef xDTR1
	if (tTf(44, 3))
		printf("costfunc %d is %ld\n", i, l);
#	endif
	return (l);
}
