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