|
|
1.1 root 1: # include <ingres.h>
2: # include <aux.h>
3: # include <symbol.h>
4: # include <tree.h>
5: # include "qrymod.h"
6: # include <sccs.h>
7: # include <errors.h>
8:
9: SCCSID(@(#)view.c 8.2 2/8/85)
10:
11: /*
12: ** VIEW.C -- view processing
13: **
14: ** This module does the view processing. Basically, it operates
15: ** by detecting all references to views and replacing them by
16: ** references to real relations. There are a number of cases
17: ** when it cannot do this, to whit:
18: **
19: ** Syntactic problems: the view may have a domain defined as
20: ** a non-simple value (that is, not a simple attribute), which
21: ** is then required to take on a value. For example, if the
22: ** view is defined as
23: ** range of x is baserel
24: ** define v (d = x.a / 3)
25: ** and then referenced as
26: ** append to v (d = 7)
27: ** would result after query modification as
28: ** range of x is baserel
29: ** append to baserel (a / 3 = 7)
30: ** which is not acceptable. Of course, there is a range of cases
31: ** where this can be fixed, but (for the time being) we will just
32: ** throw them all out.
33: **
34: ** Disappearing tuple anomaly: the implicit qualification on
35: ** a view allows tuples to disappear even when not a duplicate.
36: ** For example, take a view defined as:
37: ** range of x is baserel
38: ** define v (d = x.a) where x.a = 4
39: ** and issue the query
40: ** append to v (d = 5)
41: ** The tuple will be inserted into the base relation, but will not
42: ** be included in the view. To solve that problem, we disallow
43: ** updates to domains included in the qualification of the query.
44: ** Note that this includes implicit updates, that is, an append
45: ** with the domain missing (which implicitly appends a zero or
46: ** blank domain).
47: **
48: ** Cross product problem: a view which is defined as a cross
49: ** product of two relations has several update anomalies. For
50: ** example, take R1 and R2 as:
51: ** R1 | a | b R2 | b | c
52: ** ---|---|--- ---|---|---
53: ** | 7 | 0 | 0 | 3
54: ** | 8 | 0 | 0 | 4
55: ** and issue the view definition
56: ** range of m is R1
57: ** range of n is R2
58: ** define v (m.a, m.b, n.c) where m.b = n.b
59: ** which will define a view which looks like
60: ** view | a | b | c
61: ** -----|---|---|---
62: ** | 7 | 0 | 3
63: ** | 7 | 0 | 4
64: ** | 8 | 0 | 3
65: ** | 8 | 0 | 4
66: ** Now try issuing
67: ** range of v is v
68: ** delete v where v.a = 8 and v.c = 4
69: ** which will try to give a view which looks like:
70: ** view | a | b | c
71: ** -----|---|---|---
72: ** | 7 | 0 | 3
73: ** | 7 | 0 | 4
74: ** | 8 | 0 | 3
75: ** which is of course unexpressible in R1 and R2.
76: **
77: ** Multiple query problem: certain updates will require gener-
78: ** ating multiple queries to satisfy the update on the view.
79: ** Although this can be made to work, it won't now. Cases are
80: ** replaces where the target list contains more than one
81: ** relation, and appends to a view over more than one relation.
82: **
83: ** To solve these problems, we dissallow the following cases:
84: **
85: ** I. In a REPLACE or APPEND statement, if a 'v.d' appears
86: ** on the LHS in the target list of the query and
87: ** the a-fcn for 'v.d' is not a simple attribute.
88: ** II. In REPLACE or APPEND statements, if a 'v.d' appears
89: ** on the LHS in a target list of the query and in
90: ** the qualification of the view.
91: ** III. In a DELETE or APPEND statement, if the view ranges
92: ** over more than one relation.
93: ** IV. In a REPLACE statement, if the query resulting after
94: ** modification of the tree, but before appending the
95: ** view qualification Qv, has more than one variable.
96: ** V. In any update, if an aggregate or aggregate function
97: ** appears anywhere in the target list of the view.
98: **
99: ** Note the assumption that the definition of a consistant update
100: ** is:
101: ** "An update is consistant if the result of
102: ** performing the update on the view and then
103: ** materializing that view is the same as the
104: ** result of materializing the view and then
105: ** performing the update."
106: **
107: ** Trace Flags:
108: ** 30 -> 39
109: */
110: /*
111: ** VIEW -- driver for view processing
112: **
113: ** This routine does the view processing portion of qrymod.
114: ** Since the 'tree' catalog can contain relations which are
115: ** themselves views, it iterates over itself until no views
116: ** are found. Presumably this cannot result in an infinite
117: ** loop, although in fact it probably can; this should be
118: ** dealt with at some time.
119: **
120: ** For each range variable declared, it is checked whether
121: ** that variable is a view. If not, it is ignored.
122: ** Then the tree which defines
123: ** this view is fetched from the "tree" catalog by 'gettree',
124: ** which also defines any variables required by this tree
125: ** and adjusts the tree so that the varno's contained in the
126: ** tree correspond to the varno's in the range table.
127: **
128: ** 'Subsvars' and 'vrscan' really do it. Given the root of the tree
129: ** to be modified, the variable number to be eliminated, and the
130: ** target list for a replacement tree, they actually do the
131: ** tacking of 'new tree' onto 'old tree'. After it is done,
132: ** there should be no references to the old variable at all
133: ** in the tree. 'Subsvars' scans for VAR nodes (which are
134: ** retrieve-only, and hence are always alright); 'vrscan' scans
135: ** the left hand branch of the tree (the RESDOM nodes) and
136: ** substitutes them.
137: **
138: ** 'Appqual' appends the qualification for the view (if any)
139: ** onto the tree. Finally, the variable for the view (which
140: ** had all references to it eliminated by 'subsvars') is un-
141: ** defined, so that that slot in the range table can be re-
142: ** used by later scans.
143: **
144: ** Parameters:
145: ** root -- root of the tree to be modified.
146: **
147: ** Returns:
148: ** Root of modified tree.
149: **
150: ** Side Effects:
151: ** The range table is updated to delete any views and
152: ** add any base relations needed to support them.
153: ** Activity occurs in the 'tree' catalog to get the trees
154: ** needed to define the views.
155: ** The tree pointed to by 'root' is modified.
156: **
157: ** Trace Flags:
158: ** 30
159: */
160:
161: QTREE *
162: view(root)
163: QTREE *root;
164: {
165: register int i;
166: DESC desc;
167: register int vn;
168: register QTREE *vtree;
169: int viewfound;
170: extern QTREE *gettree();
171: extern QTREE *norml();
172: auto QTREE *r;
173:
174: # ifdef xQTR1
175: tTfp(30, -1, "\n->VIEW\n\n");
176: # endif
177:
178: r = root;
179:
180: /* scan range table until no views */
181: viewfound = TRUE;
182: while (viewfound)
183: {
184: # ifdef xQTR2
185: tTfp(30, 1, "scanning Qt.qt_rangev\n");
186: # endif
187:
188: /* scan range table for views */
189: viewfound = FALSE;
190:
191: /* make new resultvar old resultvar for non-update */
192: Qm.qm_newresvar = Qt.qt_resvar;
193:
194: /* scan all variables in range table */
195: for (vn = 0; vn < MAXVAR + 1; vn++)
196: {
197: /* check for empty entry in range table */
198: if (Qt.qt_rangev[vn].rngvdesc == NULL)
199: continue;
200:
201: /* see if it is a view or base relation */
202: if (!bitset(S_VIEW, Qt.qt_rangev[vn].rngvdesc->reldum.relstat))
203: continue;
204: # ifdef xQTR1
205: if (tTf(30, 3))
206: printf("view vn %d: %.12s\n", vn,
207: Qt.qt_rangev[vn].rngvdesc->reldum.relid);
208: # endif
209:
210: vtree = gettree(Qt.qt_rangev[vn].rngvdesc->reldum.relid,
211: Qt.qt_rangev[vn].rngvdesc->reldum.relowner,
212: mdVIEW, 0, FALSE);
213: # ifdef xQTR3
214: if (tTf(30, 5))
215: treepr(vtree, "Viewdef");
216: # endif
217:
218: /* check for updating with aggregates */
219: if (Qt.qt_qmode != mdRETR && aggcheck(vtree))
220: qmerror(NOUPDATEAGG, Qt.qt_qmode, Qt.qt_resvar, 0); /* cannot update views with aggregates */
221:
222: /* scan view replacing RESDOM nodes */
223: if (Qt.qt_qmode != mdRETR && vn == Qt.qt_resvar)
224: vrscan(&r->left, vtree);
225:
226: /* scan view replacing VAR nodes */
227: subsvars(&r, vn, vtree->left, mdVIEW);
228:
229: /* test for non-functional replace */
230: if (Qt.qt_qmode == mdREPL && bitcnt(varset(r) | (1 << Qm.qm_newresvar)) > 1)
231: qmerror(NONFUNCUPDATE, Qt.qt_qmode, Qt.qt_resvar, 0); /* non-functional update */
232:
233: /* append new qualification */
234: appqual(vtree->right, r);
235:
236: /* delete view range variable */
237: declare(vn, NULL);
238:
239: /* mark the view as having been processed */
240: viewfound = TRUE;
241:
242: /* change 'Qt.qt_resvar' to be the base rel var */
243: Qt.qt_resvar = Qm.qm_newresvar;
244: }
245: }
246:
247: /* renormalize the tree (just in case) */
248: r->right = norml(trimqlend(r->right));
249:
250: # ifdef xQTR1
251: if (tTf(30, 15))
252: treepr(r, "VIEW->");
253: # endif
254:
255: return (r);
256: }
257: /*
258: ** VRSCAN -- scan query tree and replace RESDOM nodes
259: **
260: ** The query tree issued is scanned and RESDOM nodes are
261: ** converted to conform to the underlying base relations.
262: ** There are many checks in here, and things can fail
263: ** easily.
264: **
265: ** The first check is for more than one relation in a
266: ** DELETE or APPEND command. This would require expanding
267: ** the query into at least two queries. For DELETE commands,
268: ** this is the only check. (Note that by this time 'aggcheck'
269: ** has aborted anything which would cause problems with
270: ** aggregates.)
271: **
272: ** For append commands, we abort immediately if there is
273: ** a qualification on the view, since the inserted tuple(s)
274: ** might not (all) appear in the view.
275: **
276: ** For all other queries, the target list of the query submitted
277: ** is scanned down the left hand side (the RESDOM list).
278: ** For each RESDOM, that variable is looked up in the view
279: ** definition. If the definition of it is not a simple
280: ** attribute, the query is aborted.
281: **
282: ** Then, if the variable appears anywhere in the qualification
283: ** of the view, the query is aborted.
284: **
285: ** Finally, we keep track of the varno which should become the
286: ** new number two (that is, the Qt.qt_resvar). If there are two
287: ** candidates for this position, we promptly abort.
288: **
289: ** And as the last step, we actually change the 'resno' for
290: ** this RESDOM.
291: **
292: ** When we exit the loop which scans RESDOM's, we change the
293: ** 'Qt.qt_resvar' to be the new variable which we have selected.
294: **
295: ** Notice that there are a number of overly restrictive
296: ** conditions on runability. Notably, there are large classes
297: ** of queries which can run consistantly but which violate
298: ** either the not-in-qualification condition or the aggregate-
299: ** free condition.
300: **
301: ** Parameters:
302: ** root -- the root of the tree to be updated.
303: ** vtree -- the tree which defines the view.
304: **
305: ** Returns:
306: ** none (maybe non-local on error)
307: **
308: ** Side Effects:
309: ** The tree pointed to by 'root' is modified.
310: **
311: ** Trace Flags:
312: ** 33
313: */
314:
315: vrscan(root, vtree)
316: QTREE *root;
317: QTREE *vtree;
318: {
319: register QTREE *t;
320: register QTREE *v;
321: int i;
322: extern QTREE *qscan();
323: extern QTREE *vfind();
324: register QTREE *p;
325:
326: t = root;
327: v = vtree;
328:
329: /* check DELETE and APPEND cases of > 1 relation */
330: if (Qt.qt_qmode == mdDEL || Qt.qt_qmode == mdAPP)
331: {
332: /* scan target list of view for > 1 relation */
333: if (bitcnt(i = varset(v->left)) != 1)
334: qmerror(MOREQUERY, Qt.qt_qmode, Qt.qt_resvar, 0); /* query would result in > 1 query */
335:
336: /* this is the only check in this module for DELETES */
337: if (Qt.qt_qmode == mdDEL)
338: {
339: /* set Qt.qt_resvar to underlying (single) relation */
340: Qm.qm_newresvar = bitpos(i);
341: return;
342: }
343:
344: }
345:
346: /* scan target list of query */
347: i = -1;
348: while ((t = t->left)->sym.type != TREE)
349: {
350: if (t->sym.type != RESDOM)
351: syserr("vrscan: bad TL node %d", t->sym.type);
352:
353: /* check for 'tid' attribute (stuck in by DEL and REPL) */
354: if (t->sym.value.sym_resdom.resno == 0)
355: continue;
356:
357: /* find definition for this domain in the view */
358: p = vfind(t->sym.value.sym_resdom.resno, v->left);
359:
360: /* check for simple attribute */
361: if (p->sym.type != VAR)
362: qmerror(NOUPDATEDOM, Qt.qt_qmode, Qt.qt_resvar, 0); /* non-simple attribute */
363:
364:
365: /* check for trying to do update on two relations again */
366: /* this test should only be true for REPLACE commands */
367: if (i < 0)
368: i = p->sym.value.sym_var.varno;
369: else if (i != p->sym.value.sym_var.varno)
370: qmerror(MOREQUERY, Qt.qt_qmode, Qt.qt_resvar, 0); /* query on two relations */
371:
372: /* finally, do the substitution of resno's */
373: t->sym.value.sym_resdom.resno = p->sym.value.sym_var.attno;
374: }
375:
376: /* change the result variable for the query to the underlying */
377: Qm.qm_newresvar = i;
378: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.