2.11BSD/ingres/source/qrymod/view.c

Compare this file to the similar file:
Show the results in this format:

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

/*
**  VIEW.C -- view processing
**
**	This module does the view processing.  Basically, it operates
**	by detecting all references to views and replacing them by
**	references to real relations.  There are a number of cases
**	when it cannot do this, to whit:
**
**	Syntactic problems:  the view may have a domain defined as
**	a non-simple value (that is, not a simple attribute), which
**	is then required to take on a value.  For example, if the
**	view is defined as
**		range of x is baserel
**		define v (d = x.a / 3)
**	and then referenced as
**		append to v (d = 7)
**	would result after query modification as
**		range of x is baserel
**		append to baserel (a / 3 = 7)
**	which is not acceptable.  Of course, there is a range of cases
**	where this can be fixed, but (for the time being) we will just
**	throw them all out.
**
**	Disappearing tuple anomaly:  the implicit qualification on
**	a view allows tuples to disappear even when not a duplicate.
**	For example, take a view defined as:
**		range of x is baserel
**		define v (d = x.a) where x.a = 4
**	and issue the query
**		append to v (d = 5)
**	The tuple will be inserted into the base relation, but will not
**	be included in the view.  To solve that problem, we disallow
**	updates to domains included in the qualification of the query.
**	Note that this includes implicit updates, that is, an append
**	with the domain missing (which implicitly appends a zero or
**	blank domain).
**
**	Cross product problem:  a view which is defined as a cross
**	product of two relations has several update anomalies.  For
**	example, take R1 and R2 as:
**              R1 | a | b      R2 | b | c
**              ---|---|---     ---|---|---
**                 | 7 | 0         | 0 | 3
**                 | 8 | 0         | 0 | 4
**	and issue the view definition
**		range of m is R1
**		range of n is R2
**		define v (m.a, m.b, n.c) where m.b = n.b
**	which will define a view which looks like
**              view | a | b | c
**              -----|---|---|---
**                   | 7 | 0 | 3
**                   | 7 | 0 | 4
**                   | 8 | 0 | 3
**                   | 8 | 0 | 4
**	Now try issuing
**		range of v is v
**		delete v where v.a = 8 and v.c = 4
**	which will try to give a view which looks like:
**              view | a | b | c
**              -----|---|---|---
**                   | 7 | 0 | 3
**                   | 7 | 0 | 4
**                   | 8 | 0 | 3
**	which is of course unexpressible in R1 and R2.
**
**	Multiple query problem:  certain updates will require gener-
**	ating multiple queries to satisfy the update on the view.
**	Although this can be made to work, it won't now.  Cases are
**	replaces where the target list contains more than one
**	relation, and appends to a view over more than one relation.
**
**	To solve these problems, we dissallow the following cases:
**
**	I.  In a REPLACE or APPEND statement, if a 'v.d' appears
**		on the LHS in the target list of the query and
**		the a-fcn for 'v.d' is not a simple attribute.
**	II.  In REPLACE or APPEND statements, if a 'v.d' appears
**		on the LHS in a target list of the query and in
**		the qualification of the view.
**	III.  In a DELETE or APPEND statement, if the view ranges
**		over more than one relation.
**	IV.  In a REPLACE statement, if the query resulting after
**		modification of the tree, but before appending the
**		view qualification Qv, has more than one variable.
**	V.  In any update, if an aggregate or aggregate function
**		appears anywhere in the target list of the view.
**
**	Note the assumption that the definition of a consistant update
**	is:
**		"An update is consistant if the result of
**		 performing the update on the view and then
**		 materializing that view is the same as the
**		 result of materializing the view and then
**		 performing the update."
**
**	Defines:
**		view -- the driver for the view algorithm.
**		vrscan -- to scan the query tree and substitute
**			RESDOM nodes (plus much validity checking).
**		Newresvar -- the new 'Resultvar' on updates.
**
**	Requires:
**		gettree -- to get a query tree from the 'tree' catalog
**			and prepare it for use (mainly by coming up
**			with unique variable numbers).
**		appqual -- to append a qualification onto the query
**			tree.
**		subsvars -- to scan the query tree and do the actual
**			substitution of VAR nodes.
**		trscan -- other routines to scan the tree for
**			various conditions.
**
**	Required By:
**		qrymod.c
**
**	Trace Flags:
**		30 -> 39
**
**	Diagnostics:
**		3310: cannot update some domain -- An update occured
**			which would result with an expression on the
**			LHS of an IS, that is, a non-simple attribute.
**		3320: domain occurs in qualification of view -- The
**			disappearing tuple anomolie makes it impossible
**			to update any domains which occur in the
**			qualification of a view.
**		3330: update would result in more than one query --
**			The view ranges over more than one relation,
**			and this update would require that more than
**			one query result.
**		3340: views do not have TID's -- You have attempted
**			to reference (for retrieve or update) the
**			TID of a view (which is undefined).
**		3350: cannot update an aggregate value -- An update
**			tried to change the value of an aggregate
**			value, similar to error 3310 above.
**		3360: that update might be non-functional -- A really
**			bizarre one.  This error occurs on REPLACE's
**			which are on a single relation, but which
**			include another relation in the qualification.
**			That is, the set of variables in the target
**			list includes only one variable (and so is not
**			caught as error 3330), but the result of
**			query modification leaves a query over two
**			relations.
**
**	Syserrs:
**		"subsvars: bad attno %d" -- an attribute number for a
**			view came down in the query tree, but there
**			was no substitution for it in the view defn
**			query tree.  Probably means inconsistant
**			catalogs (attribute v. tree).
**
**	History:
**		2/14/79 -- version 6.2/0 release.
*/


int	Newresvar;	/* new result variable, becomes Resultvar */
/*
**  VIEW -- driver for view processing
**
**	This routine does the view processing portion of qrymod.
**	Since the 'tree' catalog can contain relations which are
**	themselves views, it iterates over itself until no views
**	are found.  Presumably this cannot result in an infinite
**	loop, although in fact it probably can; this should be
**	dealt with at some time.
**
**	For each range variable declared, it is checked whether
**	that variable is a view.  If not, it is ignored.
**	Then the tree which defines
**	this view is fetched from the "tree" catalog by 'gettree',
**	which also defines any variables required by this tree
**	and adjusts the tree so that the varno's contained in the
**	tree correspond to the varno's in the range table.
**
**	'Subsvars' and 'vrscan' really do it.  Given the root of the tree
**	to be modified, the variable number to be eliminated, and the
**	target list for a replacement tree, they actually do the
**	tacking of 'new tree' onto 'old tree'.  After it is done,
**	there should be no references to the old variable at all
**	in the tree.  'Subsvars' scans for VAR nodes (which are
**	retrieve-only, and hence are always alright); 'vrscan' scans
**	the left hand branch of the tree (the RESDOM nodes) and
**	substitutes them.
**
**	'Appqual' appends the qualification for the view (if any)
**	onto the tree.  Finally, the variable for the view (which
**	had all references to it eliminated by 'subsvars') is un-
**	defined, so that that slot in the range table can be re-
**	used by later scans.
**
**	Parameters:
**		root -- root of the tree to be modified.
**
**	Returns:
**		Root of modified tree.
**
**	Side Effects:
**		The range table is updated to delete any views and
**			add any base relations needed to support them.
**		Activity occurs in the 'tree' catalog to get the trees
**			needed to define the views.
**		The tree pointed to by 'root' is modified.
**
**	Requires:
**		gettree -- to get the tree from the 'tree' catalog.
**		subsvars -- to do the actual substitution of VAR nodes
**			in the tree.
**		vrscan -- to substitute RESDOM nodes.
**		appqual -- to append the qualification to the tree.
**		Rangev -- the range table; used for getting relation
**			names.
**		Newresvar -- becomes the new 'Resultvar' on updates.
**
**	Required By:
**		qrymod
**
**	Trace Flags:
**		30
**
**	Diagnostics:
**		(All from lower level routines)
*/

QTREE *
view(root)
QTREE	*root;
{
	register int		i;
	struct descriptor	desc;
	register int		vn;
	register QTREE		*vtree;
	int			viewfound;
	extern QTREE		*gettree(), *norml();
	auto QTREE		*r;

#	ifdef xQTR1
	tTfp(30, -1, "\n->VIEW\n\n");
#	endif

	r = root;

	/* scan range table until no views */
	viewfound = TRUE;
	while (viewfound)
	{
#		ifdef xQTR2
		tTfp(30, 1, "scanning Rangev\n");
#		endif

		/* scan range table for views */
		viewfound = FALSE;

		/* make new resultvar old resultvar for non-update */
		Newresvar = Resultvar;

		/* scan all variables in range table */
		for (vn = 0; vn < MAXVAR + 1; vn++)
		{
			/* check for empty entry in range table */
			if (!Rangev[vn].rused)
				continue;

			/* see if it is a view or base relation */
			if ((Rangev[vn].rstat & S_VIEW) == 0)
				continue;
#			ifdef xQTR1
			if (tTf(30, 3))
				printf("view vn %d: %.12s\n", vn, Rangev[vn].relid);
#			endif

			vtree = gettree(Rangev[vn].relid, Rangev[vn].rowner, mdVIEW, 0, FALSE);
#			ifdef xQTR3
			if (tTf(30, 5))
				treepr(vtree, "Viewdef");
#			endif

			/* check for updating with aggregates */
			if (Qmode != mdRETR && aggcheck(vtree))
				ferror(3350, Qmode, Resultvar, 0);	/* cannot update views with aggregates */

			/* scan view replacing RESDOM nodes */
			if (Qmode != mdRETR && vn == Resultvar)
				vrscan(&r->left, vtree);

			/* scan view replacing VAR nodes */
			subsvars(&r, vn, vtree->left, mdVIEW);

			/* test for non-functional replace */
			if (Qmode == mdREPL && bitcnt(varset(r) | (1 << Newresvar)) > 1)
				ferror(3360, Qmode, Resultvar, 0);	/* non-functional update */

			/* append new qualification */
			appqual(vtree->right, r);

			/* delete view range variable */
			Rangev[vn].rused = FALSE;

			/* mark the view as having been processed */
			viewfound = TRUE;

			/* change 'Resultvar' to be the base rel var */
			Resultvar = Newresvar;
		}
	}

	/* renormalize the tree (just in case) */
	r->right = norml(trimqlend(r->right));

#	ifdef xQTR1
	if (tTf(30, 15))
		treepr(r, "VIEW->");
#	endif

	return (r);
}
/*
**  VRSCAN -- scan query tree and replace RESDOM nodes
**
**	The query tree issued is scanned and RESDOM nodes are
**	converted to conform to the underlying base relations.
**	There are many checks in here, and things can fail
**	easily.
**
**	The first check is for more than one relation in a
**	DELETE or APPEND command.  This would require expanding
**	the query into at least two queries.  For DELETE commands,
**	this is the only check.  (Note that by this time 'aggcheck'
**	has aborted anything which would cause problems with
**	aggregates.)
**
**	For append commands, we abort immediately if there is
**	a qualification on the view, since the inserted tuple(s)
**	might not (all) appear in the view.
**
**	For all other queries, the target list of the query submitted
**	is scanned down the left hand side (the RESDOM list).
**	For each RESDOM, that variable is looked up in the view
**	definition.  If the definition of it is not a simple
**	attribute, the query is aborted.
**
**	Then, if the variable appears anywhere in the qualification
**	of the view, the query is aborted.
**
**	Finally, we keep track of the varno which should become the
**	new number two (that is, the Resultvar).  If there are two
**	candidates for this position, we promptly abort.
**
**	And as the last step, we actually change the 'resno' for
**	this RESDOM.
**
**	When we exit the loop which scans RESDOM's, we change the
**	'Resultvar' to be the new variable which we have selected.
**
**	Notice that there are a number of overly restrictive
**	conditions on runability.  Notably, there are large classes
**	of queries which can run consistantly but which violate
**	either the not-in-qualification condition or the aggregate-
**	free condition.
**
**	Parameters:
**		root -- the root of the tree to be updated.
**		vtree -- the tree which defines the view.
**
**	Returns:
**		none (maybe non-local on error)
**
**	Side Effects:
**		The tree pointed to by 'root' is modified.
**
**	Requires:
**		bitcnt
**		varset -- to create a bit map of variables used in
**			a subtree.
**		Qmode, Resultvar
**		ferror
**		vfind -- to find the definition for an attribute
**			in the view.
**		qscan -- to scan the qualification for the first
**			VAR node for a specific 'varno.attno'.
**		Newresvar -- marked with the new result variable for
**			this query.
**
**	Called By:
**		view
**
**	Trace Flags:
**		33
**
**	Diagnostics:
**		3310: non-simple attribute -- an update occured which
**			would result in an expression on the LHS of an
**			equals in the resulting query.
**		3320: attribute in qualification -- an attribute was
**			updated which occured in the qualification of
**			the view; there is a chance that the tuple
**			being updated might disappear from the view.
**		3330: query would result in more than one query -- a
**			view was defined on the join of two base relns;
**			to run the query would require updates on both
**			of these base relations (in two queries).
**
**	Syserrs:
**		vrscan: bad TL %d -- Some node other than TREE or
**			RESDOM appeared in the target list of the
**			query.
*/

vrscan(root, vtree)
QTREE	*root;
QTREE	*vtree;
{
	register QTREE	*t;
	register QTREE	*v;
	int		i;
	extern QTREE	*qscan();
	extern QTREE	*vfind();
	register QTREE	*p;

	t = root;
	v = vtree;

	/* check DELETE and APPEND cases of > 1 relation */
	if (Qmode == mdDEL || Qmode == mdAPP)
	{
		/* scan target list of view for > 1 relation */
		if (bitcnt(i = varset(v->left)) != 1)
			ferror(3330, Qmode, Resultvar, 0);	/* query would result in > 1 query */

		/* this is the only check in this module for DELETES */
		if (Qmode == mdDEL)
		{
			/* set Resultvar to underlying (single) relation */
			Newresvar = bitpos(i);
			return;
		}

		/* check for a qualification on an append */
		if (v->right->sym.type != QLEND)
			ferror(3320, Qmode, Resultvar, 0);	/* attribute in qualification of view */
	}

	/* scan target list of query */
	i = -1;
	while ((t = t->left)->sym.type != TREE)
	{
		if (t->sym.type != RESDOM)
			syserr("vrscan: bad TL node %d", t->sym.type);

		/* check for 'tid' attribute (stuck in by DEL and REPL) */
		if (((struct qt_res *)t)->resno == 0)
		{
			continue;
		}

		/* find definition for this domain in the view */
		p = vfind(((struct qt_res *)t)->resno, v->left);

		/* check for simple attribute */
		if (p->sym.type != VAR)
			ferror(3310, Qmode, Resultvar, 0);	/* non-simple attribute */

		/* scan qualification of view for this attribute */
		if (qscan(v->right, ((struct qt_var *)p)->varno, ((struct qt_var *)p)->attno) != NULL)
			ferror(3320, Qmode, Resultvar, 0);	/* attribute in qualification of view */

		/* check for trying to do update on two relations again */
		/* this test should only be true for REPLACE commands */
		if (i < 0)
			i = ((struct qt_var *)p)->varno;
		else if (i != ((struct qt_var *)p)->varno)
			ferror(3330, Qmode, Resultvar, 0);	/* query on two relations */

		/* finally, do the substitution of resno's */
		((struct qt_res *)t)->resno = ((struct qt_var *)p)->attno;
	}

	/* change the result variable for the query to the underlying */
	Newresvar = i;
}