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: }