# include	<pv.h>
# include	<func.h>
# include	<symbol.h>
# include	<ingres.h>
# include	<aux.h>
# include	<catalog.h>
# include	<access.h>
# include	<lock.h>
# include	<sccs.h>
# include	<errors.h>

SCCSID(@(#)index.c	8.6	5/10/86)

extern short	tTdbu[];
extern int	indexx();
extern void	null_fn();

struct fn_def	IndexFn =
{
	"INDEX",
	indexx,
	null_fn,
	null_fn,
	NULL,
	0,
	tTdbu,
	100,
	'Z',
	0
};


/*
**	This is the DBU routine INDEX
**
**  pc = # of parameters
**  pv[0] points to primary relation name
**  pv[1] points to index relation name
**  pv[2] points to domain1
**  pv[3] points to domain2
**  .
**  .
**  .
**  pv[pc] = NULL
**
*/

struct dom
{
	int	id;
	int	off;
	int	frml;
	char	frm[5];
};

int
indexx(pc, pv)
int	pc;
PARM	pv[];
{
	register int		i;
	int			j;
	register struct dom	*dom;
	register PARM		*p;
	char			*primary, *indx;
	int			ndoms, newpc;
	struct tup_id		tid, hitid;
	struct tup_id		xtid;
	PARM			newpv[MAXKEYS * 2 + 4];
	char			primtup[MAXTUP], systup[MAXTUP];
	DESC			desc, pridesc;
	extern DESC		Reldes;
	extern DESC		Attdes;
	extern DESC		Inddes;
	struct relation		relkey, reltup;
	struct attribute	attkey, atttup;
	struct index		indtup;
	struct dom		domain[MAXKEYS];

	primary = pv[0].pv_val.pv_str;
	indx = pv[1].pv_val.pv_str;
#	ifdef xZTR1
	if (tTf(33, -1))
		printf("index: (pri %s ind %s)\n", primary, indx);
#	endif
	i = openr(&pridesc, OR_READ, primary);
	if (i == AMOPNVIEW_ERR)
		return (error(NOINDVIEW, primary, 0));
	if (i > 0)
		return (error(NOPRIMREL, primary, 0));
	if (i < 0)
		syserr("INDEX : openr (%.14s) %d", primary, i);

	if (!bequal(pridesc.reldum.relowner, Usercode, UCODE_SZ))
	{
		i = NOTOWNED;
	}
	else if (pridesc.reldum.relstat & S_CATALOG)
	{
		i = NOINDXSYSREL;
	}
	else if (pridesc.reldum.relindxd == SECINDEX)
	{
		i = ALREADYINDX;
	}

	if (i)
	{
		closer(&pridesc);
		return (error(i, primary, 0));
	}
	/*
	**  GATHER INFO. ON DOMAINS
	*/
	opencatalog("attribute", OR_WRITE);
	setkey(&Attdes, &attkey, primary, ATTRELID);
	setkey(&Attdes, &attkey, pridesc.reldum.relowner, ATTOWNER);
	pc -= 2;
	p = &pv[2];
	dom = domain;
	for (i = 0; i < pc; i++)
	{
		if (i >= MAXKEYS)
		{
			closer(&pridesc);
			return (error(TOOMUCHDOMS, (p->pv_val).pv_str, primary, 0));	/* too many keys */
		}
		setkey(&Attdes, &attkey, (p->pv_val).pv_str, ATTNAME);
		j = getequal(&Attdes, &attkey, &atttup, &tid);
		if (j < 0)
			syserr("INDEX: geteq att %d", j);
		if (j)
		{
			closer(&pridesc);
			return (error(NODOM, (p->pv_val).pv_str, 0));	/* key not in relation */
		}
		if (pridesc.reldum.reldim > 0 && atttup.attid == pridesc.reldum.relatts)
		/* attempting to use lid field as part of index */
		{
			closer(&pridesc);
			return(error(NOINDXLID, primary, (p->pv_val).pv_str, 0));
		}
		dom->id = atttup.attid;
		dom->off = atttup.attoff;
		dom->frml = atttup.attfrml & I1MASK;
		dom->frm[0] = atttup.attfrmt;
		p++;
		dom++;
	}
	ndoms = i;
	noclose(&Attdes);

	/*
	** The "order" of the steps have been altered to improve
	** recovery possibilities
	*/
	/*
	**  STEP 1 & 2: CREATE INDEX RELATION.
	*/
	newpv[0].pv_val.pv_str = "0202";
	newpv[1].pv_val.pv_str = indx;
	newpc = 2;
	p = &pv[2];
	dom = domain;
	for (i = 0; i < pc; i++)
	{
		newpv[newpc++].pv_val.pv_str = (p->pv_val).pv_str;
		itoa(dom->frml, &dom->frm[1]);
		newpv[newpc++].pv_val.pv_str = dom->frm;
		dom++;
		p++;
	}
	newpv[newpc++].pv_val.pv_str = "tidp";
	newpv[newpc++].pv_val.pv_str = "i4";
	newpv[newpc].pv_type = PV_EOF;

	if (create(newpc, newpv))
	{
		closer(&pridesc);
		return (-1);
	}

	/* This is done for concurrency reasons */
	if (noclose(&Reldes))
		syserr("index: noclose");

	/*
	**  STEP 5: FILL UP THE SECONDARY INDEX FILE ITSELF
	*/
	if (Lockrel)
		/* set a shared relation lock */
		setrll(A_SLP, *(long *) &pridesc.reltid, M_SHARE);	/* pardon the kludge  */
	if (i = openr(&desc, OR_WRITE, indx))
		syserr("INDEX: openr %.14s %d", indx, i);
	find(&pridesc, NOKEY, &tid, &hitid);
	while ((i = get(&pridesc, &tid, &hitid, primtup, TRUE)) == 0)
	{
		dom = domain;
		for (i = j = 0; j < ndoms; j++)
		{
# ifndef VAX
			if (dom->frm[0] != 'c')
				i = ((i-1)|(dom->frml-1))+1;
# endif
			bmove(&primtup[dom->off], &systup[i], dom->frml);
			i += dom->frml;
			dom++;
		}
# ifndef VAX
		i = ((i-1)|3)+1;
# endif
		bmove(&tid, &systup[i], sizeof tid);		/* move in pointer */
		if ((j = insert(&desc, &xtid, systup, TRUE)) < 0)
			syserr("INDEX: insert %.14s %d", indx, j);
	}
	if (i < 0)
		syserr("INDEX: get %.14s %d", primary, i);
	closer(&pridesc);
	closer(&desc);


	/*
	**  STEP 3: ENTRIES TO INDEX-REL
	*/
	pmove(primary, indtup.irelidp, MAXNAME, ' ');		/* mv in primary name  */
	bmove(pridesc.reldum.relowner, indtup.iownerp, 2);	/* primary owner */
	pmove(indx, indtup.irelidi, MAXNAME, ' ');		/* index name */
	indtup.irelspeci = M_HEAP;
	for (i = 0; i < MAXKEYS; i++)
		indtup.idom[i] = (i < ndoms) ? domain[i].id : 0;
	opencatalog("indexes", OR_WRITE);
	if ((i = insert(&Inddes, &tid, &indtup, TRUE)) < 0)
		syserr("INDEX: insert ix %d", i);

	/*
	**  STEP 4: TURN BIT ON IN PRIMARY RELATION TO SHOW IT IS BEING INDEXED
	*/
	opencatalog("relation", OR_WRITE);
	setkey(&Reldes, &relkey, primary, RELID);
	setkey(&Reldes, &relkey, pridesc.reldum.relowner, RELOWNER);
	if (i = getequal(&Reldes, &relkey, &reltup, &tid))
		syserr("INDEX: geteq rel %d", i);
	reltup.relindxd = SECBASE;
	if ((i = replace(&Reldes, &tid, &reltup, TRUE)) < 0)
		syserr("INDEX: replace rel %d", i);

	if (Lockrel)
		unlrl(*(long *) &pridesc.reltid);	/* release relation lock */

	return (0);
}
