# include	<ingres.h>
# include	<aux.h>
# include	<catalog.h>
# include	<tree.h>
# include	<symbol.h>
# include	<pv.h>
# include	<resp.h>
# include	<func.h>
# include	"qrymod.h"
# include	<sccs.h>
# include	<errors.h>

SCCSID(@(#)d_integ.c	8.4	5/30/88)


/*
**  D_INTEG -- define integrity constraint
**
**	An integrity constraint (as partially defined by the last
**	tree defined by d_tree) is defined.
**
**	Parameters:
**		none
**
**	Returns:
**		none
**
**	Side Effects:
**		Activity in 'relation' and 'integrities' catalogs.
**
**	Trace Flags:
**		49
*/

extern DESC	Intdes;
extern DESC	Reldes;

extern	void	 null_fn();
extern          d_integ();
extern short	tTqm[80];

struct fn_def	DefIntFn =
{
	"DINTEG",
	d_integ,
	null_fn,
	null_fn,
	NULL,
	0,
	tTqm,
	80,
	'Q',
	0
};



d_integ(pc, pv)
int	pc;
PARM	*pv;
{
	register int		i;
	register QTREE		*t;		/* definition tree */
	struct integrity	inttup;
	struct tup_id		tid;
	register int		rv;		/* result variable */
	struct relation		relkey;
	struct relation		reltup;
	char			relid[MAXNAME];
	char			relowner[2];
	long			relstat;
	struct	qthdr		qt;

	if (pv[0].pv_type != PV_QTREE)
		syserr("d_integ: tree");
	t = pv[0].pv_val.pv_qtree;
	rv = Qt.qt_resvar;

	/*
	**  Check for valid environment.
	**	The tree must exist, have a qualification, and have
	**	no target list.  The query mode must be mdINTEG.
	**
	**	User level stuff checks to see that this is single
	**	variable aggregate free, since that's all we know
	**	about thusfar.  Also, the relation in question must
	**	not be a view.
	*/

#	ifdef xQTR3
	if (t == NULL)
		syserr("d_integ: NULL tree");
	if ((i = t->right->sym.type) != AND)
		syserr("d_integ: qual %d", i);
	if ((i = t->left->sym.type) != TREE)
		syserr("d_integ: TL %d", i);
	if (Qt.qt_qmode != mdINTEG)
		syserr("d_integ: Qmode %d", Qt.qt_qmode);
#	endif
	
	/* check for aggregates */
	if (aggcheck(t))
		qmerror(NOAGGINT, -1, rv, 0);	/* aggregates in qual */

	/* check for multi-variable */
	for (i = 0; i < MAXRANGE; i++)
	{
		if (Qt.qt_rangev[i].rngvdesc == NULL)
			continue;
		if (i != rv)
		{
#			ifdef xQTR3
			if (tTf(49, 1))
				printf("d_integ: Rv %d(%.14s) i %d(%.14s)\n",
				    rv, Qt.qt_rangev[rv].rngvdesc->reldum.relid,
				    i, Qt.qt_rangev[i].rngvdesc->reldum.relid);
#			endif
			qmerror(NOMULTIVAR, -1, rv, 0);	/* too many vars */
		}
	}


	/* check for the resultvariable being a real relation */
	if (bitset(S_VIEW, Qt.qt_rangev[rv].rngvdesc->reldum.relstat))
		qmerror(INTVIEW, -1, rv, 0);	/* is a view */
	
	/* guarantee that you own this relation */
	if (!bequal(Usercode, Qt.qt_rangev[rv].rngvdesc->reldum.relowner, UCODE_SZ))
		qmerror(MUSTOWN, -1, rv, 0);	/* don't own reln */
	bmove(Qt.qt_rangev[rv].rngvdesc->reldum.relid, relid, MAXNAME);
	bmove(Qt.qt_rangev[rv].rngvdesc->reldum.relowner, relowner, 2);
	bmove(&Qt,&qt, sizeof (Qt));
	relstat = Qt.qt_rangev[rv].rngvdesc->reldum.relstat;

	/*
	**  Guarantee that the integrity constraint is true now.
	**	This involves issuing a retrieve statement for the
	**	inverse of the qualification.  The target list is
	**	already null, so we will get nothing printed out
	**	(only a return status).
	**
	**	We reset resp_tups if ok so that the user isn't annoyed
	**	by a tuple count.  On error, it is a count of the
	**	number of tuples that don't satisfy.
	*/

	Qt.qt_qmode = mdRETR;
	Qt.qt_resvar = -1;

	/* issue the invert of the query */
	issueinvert(t);
	if (Resp.resp_tups != 0)
		qmerror(INITCONST, -1, rv, 0);	/* constraint not satisfied */
	Resp.resp_tups = -1;
	bmove(&qt,&Qt, sizeof (Qt));

	/*
	**  Set up the rest of the environment.
	*/

	opencatalog("integrities", OR_WRITE);
	clr_tuple(&Intdes, &inttup);
	Qt.qt_resvar = -1;
	Qt.qt_qmode = -1;

	/*
	**  Set up integrity relation tuple.
	**	The qualification will be scanned, and a set of
	**	domains referenced will be created.  Other stuff
	**	is filled in from the range table and from the
	**	parser.
	**
	**	The tree is actually inserted into the tree catalog
	**	in this step.  Extra information is cleared here.
	*/

	inttup.intresvar = rv;
	bmove(relid, inttup.intrelid, MAXNAME);
	bmove(relowner, inttup.intrelowner, 2);
	makeidset(rv, t, inttup.intdomset);
	inttup.inttree = puttree(t, inttup.intrelid, inttup.intrelowner, mdINTEG);

	/*
	**  Insert tuple into integrity catalog.
	*/

	i = insert(&Intdes, &tid, &inttup, FALSE);
	if (i < 0)
		syserr("d_integ: insert");
	if (noclose(&Intdes) != 0)
		syserr("d_integ: noclose int");

	/*
	**  Update relstat S_INTEG bit.
	*/

	if (!bitset(S_INTEG, relstat))
	{
		opencatalog("relation", OR_WRITE);
		clearkeys(&Reldes);
		setkey(&Reldes, &relkey, inttup.intrelid, RELID);
		setkey(&Reldes, &relkey, inttup.intrelowner, RELOWNER);
		i = getequal(&Reldes, &relkey, &reltup, &tid);
		if (i != 0)
			syserr("d_integ: geteq returns %d",i);
		reltup.relstat |= S_INTEG;
		i = replace(&Reldes, &tid, &reltup, FALSE);
		if (i != 0)
			syserr("d_integ: replace returns %d",i);
		if (noclose(&Reldes) != 0)
			syserr("d_integ: noclose rel");
	}

	return (0);
}


makeidset(varno, tree, dset)
int	varno;
QTREE	*tree;
short	dset[8];
{
	register int	vn;
	register QTREE	*t;

	vn = varno;
	t = tree;

	while (t != NULL)
	{
		if (t->sym.type == VAR && t->sym.value.sym_var.varno == vn)
			lsetbit(t->sym.value.sym_var.attno, dset);
		
		/* handle left subtree recursively */
		makeidset(vn, t->left, dset);

		/* handle right subtree iteratively */
		t = t->right;
	}
}
