# include	"../ingres.h"
# include	"../aux.h"
# include	"../symbol.h"
# include	"../tree.h"
# include	"../pipes.h"
# include	"qrymod.h"

/*
**  UTIL.C -- qrymod support routines
**
**	This module contains a set of support routines for use
**	by the query modification routines.
**
**	Defined Constants:
**		none
**
**	Defines:
**		trimqlend() -- trim QLEND node off of qualification.
**		appqual() -- append qualification to tree.
**		ferror() -- issue fatal error
**		lsetbit() -- set bit in large set.
**		mergevar() -- merge variables to link terms
**		makezero() -- make a zero node
**
**	Required By:
**		almost all
**
**	Trace Flags:
**		00 -> 19
**
**	History:
**		2/14/79 -- version 6.2/0 release.
*/
/*
**  TRIMQLEND -- trim QLEND node off of qualification
**
**	The QLEND node, and possible the AND node preceeding it,
**	are trimmed off.  The result of this routine should be
**	a very ordinary tree like you might see in some textbook.
**
**	A fast not on the algorithm: the pointer 't' points to the
**	current node (the one which we are checking for a QLEND).
**	's' points to 't's parent, and 'r' points to 's's parent;
**	'r' is NULL at the top of the tree.
**
**	This routine works correctly on trees with no QLEND in
**	the first place, returning the original tree.
**
**	If there is a QLEND, it must be on the far right branch
**	of the tree, that is, the tree must be INGRES-canonical.
**
**	Parameters:
**		qual -- the qualification to be trimmed.
**
**	Returns:
**		A pointer to the new qualification.
**		NULL if the qualification was null once the
**			QLEND is stripped.
**
**	Side Effects:
**		The tree pointed to by 'qual' may be modified.
**
**	Called By:
**		protect()
**		appqual()
**		maybe others.
**
**	Trace Flags:
**		none
**
**	Syserrs:
**		none
*/

QTREE *
trimqlend(qual)
QTREE	*qual;
{
	register QTREE	*t;
	register QTREE	*s;
	register QTREE	*r;

	t = qual;

	/* check for the simple null qualification case */
	if (t == NULL || t->sym.type == QLEND)
		return (NULL);
	
	/* scan tree for QLEND node */
	for (r = NULL, s = t; (t = t->right) != NULL; r = s, s = t)
	{
		if (t->sym.type == QLEND)
		{
			/* trim of QLEND and AND node */
			if (r == NULL)
			{
				/* only one AND -- return its operand */
				return (s->left);
			}

			r->right = s->left;
			break;
		}
	}

	/* return tree with final AND node and QLEND node pruned */
	return (qual);
}
/*
**  APPQUAL -- append qualification to tree
**
**	The qualification is conjoined to the qualificaion of the
**	tree which is passed.
**
**	Parameters:
**		qual -- a pointer to the qualification to be appended.
**		root -- a pointer to the tree to be appended to.
**
**	Returns:
**		none
**
**	Side Effects:
**		Both 'qual' ad 'root' may be modified.  Note that
**			'qual' is linked into 'root', and must be
**			retained.
**
**	Requires:
**		tree() -- to create a new AND node.
**
**	Called By:
**		view
**		protect
**		integrity
**
**	Trace Flags:
**		13
*/

appqual(qual, root)
QTREE	*qual;
QTREE	*root;
{
	register QTREE	*p;
	register QTREE	*r;

	r = root;
#	ifdef xQTR3
	if (r == NULL)
		syserr("appqual: NULL root");
#	endif

	/*
	**  Find node before QLEND node
	**	p points the node we are examining, r points to
	**	it's parent.
	*/

	while ((p = r->right) != NULL && p->sym.type != QLEND)
	{
#		ifdef xQTR3
		if (p->sym.type != AND)
			syserr("appqual: node %d", p->sym.type);
#		endif
		r = p;
	}
	
	/* link in qualification */
	r->right = qual;
}
/*
**  FERROR -- issue fatal error message and abort query
**
**	This call is almost exactly like 'error' (which is called),
**	but never returns: the return is done by 'reset'.  Also, the
**	R_up pipe is flushed.
**
**	Parameters:
**		errno -- the error number.
**		qmode -- the query mode to pass as $0, -1 if none.
**		vn -- the varno of the relation name to pass as
**			$1, -1 if none.
**		p1 to p5 -- the parameters $2 through $6
**
**	Returns:
**		non-local (via reset())
**
**	Side Effects:
**		The error message is generated.
**
**	Defines:
**		ferror
**		Qmdname -- the names of the various Qmodes.
**
**	Requires:
**		error
**		reset
**		Pipe
**		rdpipe
**		wrpipe
**		Nullsync -- send null query on error -- defined
**			in main.c
**		nullsync -- routine to do same -- defined in
**			issue.c.
**
**	Called By:
**		MANY
**
**	Trace Flags:
**		none
**
**	Diagnostics:
**		none of it's own.
**
**	Syserrs:
**		none
*/

extern struct pipfrmt	Pipe;
char	*Qmdname[] =
{
	"[ERROR]",	/* 0 = mdRETTERM */
	"RETRIEVE",	/* 1 = mdRETR */
	"APPEND",	/* 2 = mdAPP */
	"REPLACE",	/* 3 = mdREPL */
	"DELETE",	/* 4 = mdDEL */
	"",		/* 5 = mdCOPY */
	"",		/* 6 = mdCREATE */
	"",		/* 7 = mdDESTROY */
	"",		/* 8 = mdHELP */
	"",		/* 9 = mdINDEX */
	"",		/* 10 = mdMODIFY */
	"",		/* 11 = mdPRINT */
	"",		/* 12 = mdRANGE */
	"",		/* 13 = mdSAVE */
	"DEFINE",	/* 14 = mdDEFINE */
	"RET_UNIQUE",	/* 15 = mdRET_UNI */
	"",		/* 16 = mdVIEW */
	"",		/* 17 = mdUPDATE */
	"",		/* 18 = mdRESETREL */
	"",		/* 19 = mdERIC */
	"",		/* 20 = mdNETQRY */
	"",		/* 21 = mdMOVEREL */
	"",		/* 22 = mdPROT */
	"",		/* 23 = mdINTEG */
	"",		/* 24 = mdDCREATE */
};


ferror(errno, qmode, vn, p1, p2, p3, p4, p5, p6)
int	errno;
int	qmode;
int	vn;
char	*p1, *p2, *p3, *p4, *p5, *p6;
{
	register char	*x1;
	register char	*x2;
	char		xbuf[MAXNAME + 1];
	register int	i;
	extern char	*trim_relname();
	extern int	Nullsync;
	struct retcode	*rc, *nullsync();

	/* flush the upward read pipe */
	rdpipe(P_SYNC, &Pipe, R_up);

	/* set up qmode and varno parameters */
	x1 = x2 = "";
	i = qmode;
	if (i >= 0)
		x1 = Qmdname[i];
	i = vn;
	if (i >= 0)
		smove(trim_relname(Rangev[i].relid), x2 = xbuf);

	/* issue the error message */
	error(errno, x1, x2, p1, p2, p3, p4, p5, p6, 0);

	/* issue null query if running Equel */
	if (Nullsync)
		rc = nullsync();
	else
		rc = NULL;
	
	/* sync to above */
	wrpipe(P_PRIME, &Pipe, 0, 0, 0);
	if (rc != NULL)
		wrpipe(P_NORM, &Pipe, W_up, rc, sizeof *rc);
	wrpipe(P_END, &Pipe, W_up);

	/* return to main loop for next query */
	reset();
}
/*
**  XERROR -- interface from need() to ferror()
**
**	Parameters:
**		err -- the error number.
**
**	Returns:
**		never
**
**	Side Effects:
**		see ferror
*/

xerror(err)
int	err;
{
	ferror(err, -1, -1, 0);
}
/*
**  LSETBIT -- set a bit in a domain set
**
**	Parameters:
**		bitno -- the bit number to set (0 -> 127)
**		xset -- the set to set it in.
**
**	Returns:
**		none
**
**	Side Effects:
**		none
**
**	Called By:
**		makedset()
**
**	History:
**		8/21/78 (eric) -- written.
*/

lsetbit(bitno, xset)
int	bitno;
int	xset[8];
{
	register int	b;
	register int	n;
	register int	*x;

	x = xset;

	b = bitno;
	n = b >> LOG2WORDSIZE;
	b &= WORDSIZE - 1;

	x[n] |= 1 << b;
}
/*
**  MERGEVAR -- merge variable numbers to link terms
**
**	One specified variable gets mapped into another, effectively
**	merging those two variables.  This is used for protection
**	and integrity, since the constraint read from the tree
**	must coincide with one of the variables in the query tree.
**
**	Parameters:
**		va -- the variable which will dissappear.
**		vb -- the variable which 'va' gets mapped into.
**		root -- the root of the tree to map.
**
**	Returns:
**		none
**
**	Side Effects:
**		The tree pointed at by 'root' gets VAR and RESDOM
**			nodes mapped.
**		Range table entry for 'va' is deallocated.
**		The 'Remap' vector gets reset and left in an
**			undefined state.
**
**	Requires:
**		clrrange -- to clear the 'Remap' vector (called with
**			a FALSE parameter, so 'Rangev' is not cleared).
**		mapvars -- to do the actual mapping.
**		Remap -- to define the mapping for mapvars().
**		Rangev -- to check consistency and to clear the entry
**			for variable 'va'.
**
**	Called By:
**		protect.c
**		integrity.c
**
**	Trace Flags:
**		16
**
**	Diagnostics:
**		none
**
**	Syserrs:
**		mergevar: range %d %d
**			Either 'va' or 'vb' was out of range.
**		mergevar: incon %s %s
**			The variables for 'va' and 'vb' must range
**			over the same relation.  The strings are the
**			relation names if this does not hold.
**		mergevar: undef %d %d
**			The variables 'va' and 'vb' must both
**			be defined.
*/

mergevar(va, vb, root)
int	va;
int	vb;
QTREE	*root;
{
	register int	a;
	register int	b;

	a = va;
	b = vb;

#	ifdef xQTR1
	if (tTf(16, 0))
	{
		printf("\nmergevar(%d->%d)", a, b);
		treepr(root, NULL);
	}
#	endif

	/*
	**  Insure that 'va' and 'vb' are consistant, that is,
	**  that they both are in range, are defined, and range over
	**  the same relation.
	*/

	if (a < 0 || b < 0 || a >= MAXVAR + 1 || b >= MAXVAR + 1)
		syserr("mergevar: range %d %d", a, b);
	if (!Rangev[a].rused || !Rangev[b].rused)
		syserr("mergevar: undef %d %d", a, b);
	if (!bequal(Rangev[a].relid, Rangev[b].relid, MAXNAME) ||
	    !bequal(Rangev[a].rowner, Rangev[b].rowner, 2))
		syserr("mergevar: incon %.14s %.14s", Rangev[a].relid,
		    Rangev[b].relid);
	
	/*
	**  To do the actual mapping, we will set up 'Remap' and
	**  call 'mapvars()'.  This is because I am too lazy to
	**  do it myself.
	*/

	clrrange(FALSE);
	Remap[a] = b;
	mapvars(root);

	/* delete a from the range table */
	Rangev[a].rused = FALSE;
}
/*
**  MAKEZERO -- make a node with value 'zero'
**
**	A node is created with value representing the zero value
**	for the specified type, that is, 0 for integers, 0.0 for
**	floats, and the blank string for chars.
**
**	Parameters:
**		typ -- the node type.
**
**	Returns:
**		a pointer to the zero node.
**
**	Side Effects:
**		space is grabbed from Qbuf
**
**	Requires:
**		Qbuf -- for space.
**		need() -- to get that space.
**
**	History:
**		11/21/78 (eric) -- written.
*/

QTREE *
makezero(typ)
int	typ;
{
	register int		l;
	register struct symbol	*s;
	int			symbuf[(sizeof *s + 4) / sizeof l];
	char			*need();

	s = (struct symbol *) &symbuf;
	s->type = typ;

	switch (typ)
	{
	  case INT:
		s->len = l = 2;
		i2deref(s->value) = 0;
		break;

	  case FLOAT:
		s->len = l = 4;
		f4deref(s->value) = 0.0;
		break;

	  case CHAR:
		s->len = l = 2;
		i2deref(s->value) = 0x2020;	/* (two spaces) */
		break;

	  default:
		syserr("makezero: typ %d", typ);
	}

	/* duplicate the node into Qbuf */
	l += sizeof *s;
	s = (struct symbol *) need(Qbuf, l);
	bmove(&symbuf, s, l);
	return ((QTREE *) s);
}
