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

SCCSID(@(#)openrs.c	8.2	1/15/85)




/* Defined constants for dtmode field above */
# define	DTALLOC		0	/* descriptor allocated */
# define	DTREL		1	/* has been openr'd -1 */
# define	DTATTS		2	/* has rel+atts but not opened */
# define	DTREAD		3	/* currently open for reading */
# define	DTWRITE		4	/* currently open for writing */



/* Allocation of descriptors */

/* Globals which count #files open and maximum # of files which can be open */


/*
** OPENRS -- routines associated with maintaining the range table for decomp
**
**	openrs(root) -- fill range table info about each relation.
**
**	closers() -- close all variables in range table.
**
**	openr1(varno) -- fill range table for a particular relation.
**
**	closer1(varno) -- close a particular relation.
**
**	readopen(varno) -- open a variable for reading. returns descriptor.
**
**	writeopen(varno) -- open a variable for writing. returns descriptor.
**
**	initdesc()	-- initialize the descriptor cache.
**
**	reldescrip(varno) -- returns descriptor for var (has rel/atts
**		but might not be open).
**
**	desc_get(relnum, flag) -- finds a desc_tab & alloctes it for relnum.
**
**	desc_lru()  -- returns least recently used desc_tab.
**
**	desc_top(desc_tab) -- makes desc_tab most recently used.
**
**	desc_last(desc_tab) -- makes desc_tab the least recently used.
**
**	Trace Flags:
**		62
*/
/*
** Initdesc -- initialize descriptors for range table
*/

initdesc(mode)
int	mode;
{
	register struct desc_tab	*dt;
	register int			i;
	extern int			Equel;


	for (dt = De.de_desc, i = 0; dt <= &De.de_desc[MAXRELN - 1]; dt++, i++)
	{
		dt->dtmode = DTALLOC;
		dt->relnum = -2;	/* unused relnum value */
		dt->dtpos = i;		/* lru order */
	}

	/*
	** Determine number of available file descriptors.
	**	av_files gives number of files that are def. open
	**	for users.  if we will need to open a batch file,
	**	get rid of that also.
	*/

	De.de_dfiles = av_files();
	if (mode != mdRETR)
		De.de_dfiles--;
	De.de_dopnfiles = 0;
}
/*
**	Openrs -- open source relations for query. Fill values
**		in range table.
*/

openrs(root)
QTREE	*root;
{
	register QTREE	*r;
	register int	map, i;
	DESC		*openr1();

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

#	ifdef xDTR1
	if (tTf(62, 0))
		printf("OPENRS-root:%x,map:%o\n", r, map);
#	endif

	for (i = 0; i < MAXRANGE; i++)
		if (map & (01 << i))
			openr1(i);

}
/*
**	Close all open relations.
**	If any relations were created but never
**	opened, destroy them. The only
**	situation under which that can occur
**	is when a rub-out occurs at an
**	in opportune moment or when an error
**	occurs in ovqp.
*/

closers()
{
	register int			i;
	register struct desc_tab	*dt;
	bool				dstr_flag;


	for (dt = De.de_desc; dt <= &De.de_desc[MAXRELN - 1]; dt++)
		desc_close(dt);

	/* destroy any temps */
	initp();	/* init parameters vector for destroys */
	dstr_flag = FALSE;
	while (i = rnum_last())
	{
		dstr_flag |= dstr_mark(i); /* indicate that there are relations to be destroyed */
	}

	if (dstr_flag)
		call_dbu(mdDESTROY, TRUE);
	else
		resetp();
}
/*
**	Openr1 -- open relation to get relation relation tuple
**
**	This will not open the relation for reading -- only
**	for getting the first part of the descriptor filled
*/

DESC *
openr1(var)
int	var;
{
	register struct desc_tab	*dt;
	register struct rang_tab	*rp;
	register DESC	*d;
	int				i;
	struct desc_tab			*desc_get();
	extern char			*rnum_convert();

	rp = &De.de_rangev[var];

#	ifdef xDTR1
	if (tTf(62, 2))
		printf("openr1: var %d (%s)\t", var, rnum_convert(rp->relnum));
#	endif

	dt = desc_get(rp->relnum, TRUE);

	if (dt->dtmode == DTALLOC)
	{
		if (i = openr(&dt->desc, OR_RELTID, rnum_convert(rp->relnum)))
			syserr("openr1 open %d %s", i, rnum_convert(rp->relnum));
		dt->dtmode = DTREL;
	}

#	ifdef xDTR1
	if (tTf(62, 2))
		printf("tups=%ld\n", dt->desc.reldum.reltups);
#	endif

	d = &dt->desc;

	rp->rtspec = d->reldum.relspec;
	rp->rtstat = d->reldum.relstat;
	rp->rtwid = d->reldum.relwid;
	rp->rtcnt = d->reldum.reltups;

	return (d);
}
/*
**  CLOSER1
*/

closer1(var)
int	var;
{
	register struct desc_tab	*dt;
	register struct rang_tab	*rp;
	register int			i;
	struct desc_tab			*desc_get();
	struct desc_tab			*desc_last();

	i = var;
	rp = &De.de_rangev[i];

#	ifdef xDTR1
	if (tTf(62, 4))
		printf("closer1:var %d (%s)\n", i, rnum_convert(rp->relnum));
#	endif
	if (dt = desc_get(rp->relnum, FALSE))
	{

		/* currently a descriptor for rel */
		desc_close(dt);

		dt->relnum = -2;
		desc_last(dt);

	}
}
/*
**  READOPEN
*/

DESC *
readopen(var)
int	var;
{
	register struct desc_tab	*dt;
	struct desc_tab			*desc_get();

	/* get descv for the relation */
	dt = desc_get(De.de_rangev[var].relnum, TRUE);

	if (!(dt->dtmode == DTREAD || dt->dtmode == DTWRITE))
	{
		/* not open for reading or writing */
		openup(dt, var, OR_READ);	/* open for reading */
	}

	return (&dt->desc);
}
/*
**  WRITEOPEN
*/

DESC *
writeopen(var)
int	var;
{
	register struct desc_tab	*dt;
	struct desc_tab			*desc_get();

	/* get descv for the relation */
	dt = desc_get(De.de_rangev[var].relnum, TRUE);

	if (dt->dtmode != DTWRITE)
	{
		/* not open for writing */
		openup(dt, var, OR_WRITE);	/* open for reading */
	}

	return (&dt->desc);
}
/*
**  SPECOPEN -- open for writing not associated with any variable
*/

DESC *
specopen(relnum)
int	relnum;
{
	register struct desc_tab	*dt;
	struct desc_tab			*desc_get();

	dt = desc_get(relnum, TRUE);

	if (dt->dtmode != DTWRITE)
		openup(dt, -1, OR_WRITE);

	return (&dt->desc);
}
/*
**  SPECCLOSE
*/

specclose(relnum)
int	relnum;
{
	register struct desc_tab	*dt;
	struct desc_tab			*desc_get();
	struct desc_tab			*desc_last();

	if (dt = desc_get(relnum, FALSE))
	{
		desc_close(dt);
		desc_last(dt);
		dt->relnum = -2;
	}
}
/*
**	Openup -- make sure that the given descriptor is open
**		suitably for reading or writing.
*/

openup(dt1, varno, mode)
struct desc_tab	*dt1;
int		varno;
int		mode;
{
	register struct desc_tab	*dt;
	register int			md, openmd;
	int				i;
	extern char			*rnum_convert();
	char				rnam_tmp[MAXNAME+3];

	/* quick check to handle typical case of rel being already open */
	md = mode;
	dt = dt1;
	if ((md != OR_WRITE && dt->dtmode == DTREAD) || dt->dtmode == DTWRITE)
		return;

	/* relation not opened correctly */
	switch (dt->dtmode)
	{

	  case DTALLOC:
		/*
		** Descriptor allocated but nothing else. If this
		** is for a variable then use openr1 to get range table
		** info. Else open directly.
		*/
		if (varno < 0)
		{
			/* open unassociated with a range table variable */
			openmd = md ? OR_WRITE : OR_READ;
			bmove(rnum_convert(dt->relnum), dt->desc.reldum.relid, MAXNAME);
			break;
		}

		/* open for range table variable */
		openr1(varno);

		/* now fall through to DTREL case */

	  case DTREL:
		/* relation relation tuple present but nothing else */
		openmd = md ? OR_AWRITE : OR_AREAD;	/* open AREAD for read, AWRITE for write */
		break;

	  case DTATTS:
		/* relation & attributes filled but relation closed */
		openmd = md ? OR_REWRITE : OR_REREAD;
		break;
	  case DTREAD:
		/* relation open for reading but we need to write */
		desc_close(dt);

		openmd = OR_REWRITE;
		break;

	  default:
		syserr("openup:bad md %d", dt->dtmode);
	}

	/* close a previous file if necessary */
	if (De.de_dopnfiles == De.de_dfiles)
		desc_victum();	/* close oldest file */

	/* now open relation */
	bmove(dt->desc.reldum.relid, rnam_tmp, MAXNAME + 3);
	if (i = openr(&dt->desc, openmd, rnam_tmp))
		syserr("openup:openr %d,%d,%.12s,%s", i, openmd, rnam_tmp, rnum_convert(dt->relnum));
	De.de_dopnfiles++;

	/* update mode of descriptor */
	dt->dtmode = md ? DTWRITE : DTREAD;
}
/*
**  DESC_GET
*/

struct desc_tab *
desc_get(relnum, flag)
int	relnum;
bool	flag;
{
	register struct desc_tab	*dt, *ret;
	struct desc_tab			*desc_lru();

	ret = NULL;

	/* search for one currently allocated */
	for (dt = &De.de_desc[0]; dt <= &De.de_desc[MAXRELN-1]; dt++)
	{
		if (dt->relnum == relnum)
		{
			ret = dt;
#			ifdef xDTR1
			if (tTf(62, 3))
				printf("found desc for %d\n", relnum);
#			endif
			break;
		}
	}

	if (ret == NULL && flag)
	{
		/* get a victim and deallocate desc */
		ret = desc_lru();

		/* deallocate */
#		ifdef xDTR1
		if (tTf(62, 5))
			printf("trading %d for %d\n", ret->relnum, relnum);
#		endif
		desc_close(ret);

		/* allocate */
		ret->relnum = relnum;
		ret->dtmode = DTALLOC;
	}

	if (ret != NULL)
		desc_top(ret);

	return (ret);
}
/*
**	For text space reasons only, the close relation routine varies
**	between decomp and decomp70. In decomp, the relation is opened
**	only for reading and never for writing thus inpcloser() can be
**	called. For decomp70 closer() must be called. If there were no
**	text space shortage, then closer() could always be called.
**	The routine init_decomp() assigned the value to Des_closefunc.
*/

extern int	(*Des_closefunc)();	/* either &inpcloser or &closer */

desc_close(dt1)
struct desc_tab	*dt1;
{
	register struct desc_tab	*dt;
	register int			i;

	dt = dt1;

	if (dt->dtmode == DTREAD || dt->dtmode == DTWRITE)
	{
		if (i = (*Des_closefunc)(&dt->desc))
			syserr("desc_close:closer %d,%.12s", i, dt->desc.reldum.relid);
		De.de_dopnfiles--;
		dt->dtmode = DTATTS;
	}
}
/*
** Desc_top -- make the desc_tab entry "dtx" the most recently used.
*/

desc_top(dt1)
struct desc_tab	*dt1;
{
	register struct desc_tab	*dt, *dx;
	register int			oldpos;

	dt = dt1;

	if ((oldpos = dt->dtpos) != 0)
	{
		/* descriptor isn't currently top */
		for (dx = De.de_desc; dx <= &De.de_desc[MAXRELN-1]; dx++)
			if (dx->dtpos < oldpos)
				dx->dtpos++;

		/* make descriptor first */
		dt->dtpos = 0;
	}
}
/*
** Desc_last -- make the desc_tab entry "dt" the least recently used.
*/

struct desc_tab *
desc_last(dt)
register struct desc_tab	*dt;
{
	register int			oldpos;
	register struct desc_tab	*dx;

	oldpos = dt->dtpos;
	for (dx = De.de_desc; dx <= &De.de_desc[MAXRELN-1]; dx++)
		if (dx->dtpos > oldpos)
			dx->dtpos--;

	/* make descriptor last */
	dt->dtpos = MAXRELN - 1;
}
/*
** Desc_lru -- return least recently used descriptor
*/

struct desc_tab *
desc_lru()
{
	register struct desc_tab	*dx;

	for (dx = De.de_desc; dx <= &De.de_desc[MAXRELN-1]; dx++)
	{
		if (dx->dtpos == MAXRELN - 1)
			return (dx);
	}
	syserr("desc_lru:no lru");
	/*NOTREACHED*/
}


desc_victum()
{
	register struct desc_tab	*dt, *old;

	old = NULL;
	for (dt = &De.de_desc[0]; dt <= &De.de_desc[MAXRELN-1]; dt++)
	{
		if (dt->dtmode == DTWRITE || dt->dtmode == DTREAD)
		{
			if (old == NULL || dt->dtpos > old->dtpos)
				old = dt;
		}
	}

	if (old == NULL)
		syserr("desc_victum:no victum %d,%d", De.de_dopnfiles, De.de_dfiles);
	desc_close(old);
}
