1: # include "../ingres.h" 2: # include "../aux.h" 3: # include "../symbol.h" 4: # include "../tree.h" 5: # include "qrymod.h" 6: 7: /* 8: ** VIEW.C -- view processing 9: ** 10: ** This module does the view processing. Basically, it operates 11: ** by detecting all references to views and replacing them by 12: ** references to real relations. There are a number of cases 13: ** when it cannot do this, to whit: 14: ** 15: ** Syntactic problems: the view may have a domain defined as 16: ** a non-simple value (that is, not a simple attribute), which 17: ** is then required to take on a value. For example, if the 18: ** view is defined as 19: ** range of x is baserel 20: ** define v (d = x.a / 3) 21: ** and then referenced as 22: ** append to v (d = 7) 23: ** would result after query modification as 24: ** range of x is baserel 25: ** append to baserel (a / 3 = 7) 26: ** which is not acceptable. Of course, there is a range of cases 27: ** where this can be fixed, but (for the time being) we will just 28: ** throw them all out. 29: ** 30: ** Disappearing tuple anomaly: the implicit qualification on 31: ** a view allows tuples to disappear even when not a duplicate. 32: ** For example, take a view defined as: 33: ** range of x is baserel 34: ** define v (d = x.a) where x.a = 4 35: ** and issue the query 36: ** append to v (d = 5) 37: ** The tuple will be inserted into the base relation, but will not 38: ** be included in the view. To solve that problem, we disallow 39: ** updates to domains included in the qualification of the query. 40: ** Note that this includes implicit updates, that is, an append 41: ** with the domain missing (which implicitly appends a zero or 42: ** blank domain). 43: ** 44: ** Cross product problem: a view which is defined as a cross 45: ** product of two relations has several update anomalies. For 46: ** example, take R1 and R2 as: 47: ** R1 | a | b R2 | b | c 48: ** ---|---|--- ---|---|--- 49: ** | 7 | 0 | 0 | 3 50: ** | 8 | 0 | 0 | 4 51: ** and issue the view definition 52: ** range of m is R1 53: ** range of n is R2 54: ** define v (m.a, m.b, n.c) where m.b = n.b 55: ** which will define a view which looks like 56: ** view | a | b | c 57: ** -----|---|---|--- 58: ** | 7 | 0 | 3 59: ** | 7 | 0 | 4 60: ** | 8 | 0 | 3 61: ** | 8 | 0 | 4 62: ** Now try issuing 63: ** range of v is v 64: ** delete v where v.a = 8 and v.c = 4 65: ** which will try to give a view which looks like: 66: ** view | a | b | c 67: ** -----|---|---|--- 68: ** | 7 | 0 | 3 69: ** | 7 | 0 | 4 70: ** | 8 | 0 | 3 71: ** which is of course unexpressible in R1 and R2. 72: ** 73: ** Multiple query problem: certain updates will require gener- 74: ** ating multiple queries to satisfy the update on the view. 75: ** Although this can be made to work, it won't now. Cases are 76: ** replaces where the target list contains more than one 77: ** relation, and appends to a view over more than one relation. 78: ** 79: ** To solve these problems, we dissallow the following cases: 80: ** 81: ** I. In a REPLACE or APPEND statement, if a 'v.d' appears 82: ** on the LHS in the target list of the query and 83: ** the a-fcn for 'v.d' is not a simple attribute. 84: ** II. In REPLACE or APPEND statements, if a 'v.d' appears 85: ** on the LHS in a target list of the query and in 86: ** the qualification of the view. 87: ** III. In a DELETE or APPEND statement, if the view ranges 88: ** over more than one relation. 89: ** IV. In a REPLACE statement, if the query resulting after 90: ** modification of the tree, but before appending the 91: ** view qualification Qv, has more than one variable. 92: ** V. In any update, if an aggregate or aggregate function 93: ** appears anywhere in the target list of the view. 94: ** 95: ** Note the assumption that the definition of a consistant update 96: ** is: 97: ** "An update is consistant if the result of 98: ** performing the update on the view and then 99: ** materializing that view is the same as the 100: ** result of materializing the view and then 101: ** performing the update." 102: ** 103: ** Defines: 104: ** view -- the driver for the view algorithm. 105: ** vrscan -- to scan the query tree and substitute 106: ** RESDOM nodes (plus much validity checking). 107: ** Newresvar -- the new 'Resultvar' on updates. 108: ** 109: ** Requires: 110: ** gettree -- to get a query tree from the 'tree' catalog 111: ** and prepare it for use (mainly by coming up 112: ** with unique variable numbers). 113: ** appqual -- to append a qualification onto the query 114: ** tree. 115: ** subsvars -- to scan the query tree and do the actual 116: ** substitution of VAR nodes. 117: ** trscan -- other routines to scan the tree for 118: ** various conditions. 119: ** 120: ** Required By: 121: ** qrymod.c 122: ** 123: ** Trace Flags: 124: ** 30 -> 39 125: ** 126: ** Diagnostics: 127: ** 3310: cannot update some domain -- An update occured 128: ** which would result with an expression on the 129: ** LHS of an IS, that is, a non-simple attribute. 130: ** 3320: domain occurs in qualification of view -- The 131: ** disappearing tuple anomolie makes it impossible 132: ** to update any domains which occur in the 133: ** qualification of a view. 134: ** 3330: update would result in more than one query -- 135: ** The view ranges over more than one relation, 136: ** and this update would require that more than 137: ** one query result. 138: ** 3340: views do not have TID's -- You have attempted 139: ** to reference (for retrieve or update) the 140: ** TID of a view (which is undefined). 141: ** 3350: cannot update an aggregate value -- An update 142: ** tried to change the value of an aggregate 143: ** value, similar to error 3310 above. 144: ** 3360: that update might be non-functional -- A really 145: ** bizarre one. This error occurs on REPLACE's 146: ** which are on a single relation, but which 147: ** include another relation in the qualification. 148: ** That is, the set of variables in the target 149: ** list includes only one variable (and so is not 150: ** caught as error 3330), but the result of 151: ** query modification leaves a query over two 152: ** relations. 153: ** 154: ** Syserrs: 155: ** "subsvars: bad attno %d" -- an attribute number for a 156: ** view came down in the query tree, but there 157: ** was no substitution for it in the view defn 158: ** query tree. Probably means inconsistant 159: ** catalogs (attribute v. tree). 160: ** 161: ** History: 162: ** 2/14/79 -- version 6.2/0 release. 163: */ 164: 165: 166: int Newresvar; /* new result variable, becomes Resultvar */ 167: /* 168: ** VIEW -- driver for view processing 169: ** 170: ** This routine does the view processing portion of qrymod. 171: ** Since the 'tree' catalog can contain relations which are 172: ** themselves views, it iterates over itself until no views 173: ** are found. Presumably this cannot result in an infinite 174: ** loop, although in fact it probably can; this should be 175: ** dealt with at some time. 176: ** 177: ** For each range variable declared, it is checked whether 178: ** that variable is a view. If not, it is ignored. 179: ** Then the tree which defines 180: ** this view is fetched from the "tree" catalog by 'gettree', 181: ** which also defines any variables required by this tree 182: ** and adjusts the tree so that the varno's contained in the 183: ** tree correspond to the varno's in the range table. 184: ** 185: ** 'Subsvars' and 'vrscan' really do it. Given the root of the tree 186: ** to be modified, the variable number to be eliminated, and the 187: ** target list for a replacement tree, they actually do the 188: ** tacking of 'new tree' onto 'old tree'. After it is done, 189: ** there should be no references to the old variable at all 190: ** in the tree. 'Subsvars' scans for VAR nodes (which are 191: ** retrieve-only, and hence are always alright); 'vrscan' scans 192: ** the left hand branch of the tree (the RESDOM nodes) and 193: ** substitutes them. 194: ** 195: ** 'Appqual' appends the qualification for the view (if any) 196: ** onto the tree. Finally, the variable for the view (which 197: ** had all references to it eliminated by 'subsvars') is un- 198: ** defined, so that that slot in the range table can be re- 199: ** used by later scans. 200: ** 201: ** Parameters: 202: ** root -- root of the tree to be modified. 203: ** 204: ** Returns: 205: ** Root of modified tree. 206: ** 207: ** Side Effects: 208: ** The range table is updated to delete any views and 209: ** add any base relations needed to support them. 210: ** Activity occurs in the 'tree' catalog to get the trees 211: ** needed to define the views. 212: ** The tree pointed to by 'root' is modified. 213: ** 214: ** Requires: 215: ** gettree -- to get the tree from the 'tree' catalog. 216: ** subsvars -- to do the actual substitution of VAR nodes 217: ** in the tree. 218: ** vrscan -- to substitute RESDOM nodes. 219: ** appqual -- to append the qualification to the tree. 220: ** Rangev -- the range table; used for getting relation 221: ** names. 222: ** Newresvar -- becomes the new 'Resultvar' on updates. 223: ** 224: ** Required By: 225: ** qrymod 226: ** 227: ** Trace Flags: 228: ** 30 229: ** 230: ** Diagnostics: 231: ** (All from lower level routines) 232: */ 233: 234: QTREE * 235: view(root) 236: QTREE *root; 237: { 238: register int i; 239: struct descriptor desc; 240: register int vn; 241: register QTREE *vtree; 242: int viewfound; 243: extern QTREE *gettree(), *norml(); 244: auto QTREE *r; 245: 246: # ifdef xQTR1 247: tTfp(30, -1, "\n->VIEW\n\n"); 248: # endif 249: 250: r = root; 251: 252: /* scan range table until no views */ 253: viewfound = TRUE; 254: while (viewfound) 255: { 256: # ifdef xQTR2 257: tTfp(30, 1, "scanning Rangev\n"); 258: # endif 259: 260: /* scan range table for views */ 261: viewfound = FALSE; 262: 263: /* make new resultvar old resultvar for non-update */ 264: Newresvar = Resultvar; 265: 266: /* scan all variables in range table */ 267: for (vn = 0; vn < MAXVAR + 1; vn++) 268: { 269: /* check for empty entry in range table */ 270: if (!Rangev[vn].rused) 271: continue; 272: 273: /* see if it is a view or base relation */ 274: if ((Rangev[vn].rstat & S_VIEW) == 0) 275: continue; 276: # ifdef xQTR1 277: if (tTf(30, 3)) 278: printf("view vn %d: %.12s\n", vn, Rangev[vn].relid); 279: # endif 280: 281: vtree = gettree(Rangev[vn].relid, Rangev[vn].rowner, mdVIEW, 0, FALSE); 282: # ifdef xQTR3 283: if (tTf(30, 5)) 284: treepr(vtree, "Viewdef"); 285: # endif 286: 287: /* check for updating with aggregates */ 288: if (Qmode != mdRETR && aggcheck(vtree)) 289: ferror(3350, Qmode, Resultvar, 0); /* cannot update views with aggregates */ 290: 291: /* scan view replacing RESDOM nodes */ 292: if (Qmode != mdRETR && vn == Resultvar) 293: vrscan(&r->left, vtree); 294: 295: /* scan view replacing VAR nodes */ 296: subsvars(&r, vn, vtree->left, mdVIEW); 297: 298: /* test for non-functional replace */ 299: if (Qmode == mdREPL && bitcnt(varset(r) | (1 << Newresvar)) > 1) 300: ferror(3360, Qmode, Resultvar, 0); /* non-functional update */ 301: 302: /* append new qualification */ 303: appqual(vtree->right, r); 304: 305: /* delete view range variable */ 306: Rangev[vn].rused = FALSE; 307: 308: /* mark the view as having been processed */ 309: viewfound = TRUE; 310: 311: /* change 'Resultvar' to be the base rel var */ 312: Resultvar = Newresvar; 313: } 314: } 315: 316: /* renormalize the tree (just in case) */ 317: r->right = norml(trimqlend(r->right)); 318: 319: # ifdef xQTR1 320: if (tTf(30, 15)) 321: treepr(r, "VIEW->"); 322: # endif 323: 324: return (r); 325: } 326: /* 327: ** VRSCAN -- scan query tree and replace RESDOM nodes 328: ** 329: ** The query tree issued is scanned and RESDOM nodes are 330: ** converted to conform to the underlying base relations. 331: ** There are many checks in here, and things can fail 332: ** easily. 333: ** 334: ** The first check is for more than one relation in a 335: ** DELETE or APPEND command. This would require expanding 336: ** the query into at least two queries. For DELETE commands, 337: ** this is the only check. (Note that by this time 'aggcheck' 338: ** has aborted anything which would cause problems with 339: ** aggregates.) 340: ** 341: ** For append commands, we abort immediately if there is 342: ** a qualification on the view, since the inserted tuple(s) 343: ** might not (all) appear in the view. 344: ** 345: ** For all other queries, the target list of the query submitted 346: ** is scanned down the left hand side (the RESDOM list). 347: ** For each RESDOM, that variable is looked up in the view 348: ** definition. If the definition of it is not a simple 349: ** attribute, the query is aborted. 350: ** 351: ** Then, if the variable appears anywhere in the qualification 352: ** of the view, the query is aborted. 353: ** 354: ** Finally, we keep track of the varno which should become the 355: ** new number two (that is, the Resultvar). If there are two 356: ** candidates for this position, we promptly abort. 357: ** 358: ** And as the last step, we actually change the 'resno' for 359: ** this RESDOM. 360: ** 361: ** When we exit the loop which scans RESDOM's, we change the 362: ** 'Resultvar' to be the new variable which we have selected. 363: ** 364: ** Notice that there are a number of overly restrictive 365: ** conditions on runability. Notably, there are large classes 366: ** of queries which can run consistantly but which violate 367: ** either the not-in-qualification condition or the aggregate- 368: ** free condition. 369: ** 370: ** Parameters: 371: ** root -- the root of the tree to be updated. 372: ** vtree -- the tree which defines the view. 373: ** 374: ** Returns: 375: ** none (maybe non-local on error) 376: ** 377: ** Side Effects: 378: ** The tree pointed to by 'root' is modified. 379: ** 380: ** Requires: 381: ** bitcnt 382: ** varset -- to create a bit map of variables used in 383: ** a subtree. 384: ** Qmode, Resultvar 385: ** ferror 386: ** vfind -- to find the definition for an attribute 387: ** in the view. 388: ** qscan -- to scan the qualification for the first 389: ** VAR node for a specific 'varno.attno'. 390: ** Newresvar -- marked with the new result variable for 391: ** this query. 392: ** 393: ** Called By: 394: ** view 395: ** 396: ** Trace Flags: 397: ** 33 398: ** 399: ** Diagnostics: 400: ** 3310: non-simple attribute -- an update occured which 401: ** would result in an expression on the LHS of an 402: ** equals in the resulting query. 403: ** 3320: attribute in qualification -- an attribute was 404: ** updated which occured in the qualification of 405: ** the view; there is a chance that the tuple 406: ** being updated might disappear from the view. 407: ** 3330: query would result in more than one query -- a 408: ** view was defined on the join of two base relns; 409: ** to run the query would require updates on both 410: ** of these base relations (in two queries). 411: ** 412: ** Syserrs: 413: ** vrscan: bad TL %d -- Some node other than TREE or 414: ** RESDOM appeared in the target list of the 415: ** query. 416: */ 417: 418: vrscan(root, vtree) 419: QTREE *root; 420: QTREE *vtree; 421: { 422: register QTREE *t; 423: register QTREE *v; 424: int i; 425: extern QTREE *qscan(); 426: extern QTREE *vfind(); 427: register QTREE *p; 428: 429: t = root; 430: v = vtree; 431: 432: /* check DELETE and APPEND cases of > 1 relation */ 433: if (Qmode == mdDEL || Qmode == mdAPP) 434: { 435: /* scan target list of view for > 1 relation */ 436: if (bitcnt(i = varset(v->left)) != 1) 437: ferror(3330, Qmode, Resultvar, 0); /* query would result in > 1 query */ 438: 439: /* this is the only check in this module for DELETES */ 440: if (Qmode == mdDEL) 441: { 442: /* set Resultvar to underlying (single) relation */ 443: Newresvar = bitpos(i); 444: return; 445: } 446: 447: /* check for a qualification on an append */ 448: if (v->right->sym.type != QLEND) 449: ferror(3320, Qmode, Resultvar, 0); /* attribute in qualification of view */ 450: } 451: 452: /* scan target list of query */ 453: i = -1; 454: while ((t = t->left)->sym.type != TREE) 455: { 456: if (t->sym.type != RESDOM) 457: syserr("vrscan: bad TL node %d", t->sym.type); 458: 459: /* check for 'tid' attribute (stuck in by DEL and REPL) */ 460: if (((struct qt_res *)t)->resno == 0) 461: { 462: continue; 463: } 464: 465: /* find definition for this domain in the view */ 466: p = vfind(((struct qt_res *)t)->resno, v->left); 467: 468: /* check for simple attribute */ 469: if (p->sym.type != VAR) 470: ferror(3310, Qmode, Resultvar, 0); /* non-simple attribute */ 471: 472: /* scan qualification of view for this attribute */ 473: if (qscan(v->right, ((struct qt_var *)p)->varno, ((struct qt_var *)p)->attno) != NULL) 474: ferror(3320, Qmode, Resultvar, 0); /* attribute in qualification of view */ 475: 476: /* check for trying to do update on two relations again */ 477: /* this test should only be true for REPLACE commands */ 478: if (i < 0) 479: i = ((struct qt_var *)p)->varno; 480: else if (i != ((struct qt_var *)p)->varno) 481: ferror(3330, Qmode, Resultvar, 0); /* query on two relations */ 482: 483: /* finally, do the substitution of resno's */ 484: ((struct qt_res *)t)->resno = ((struct qt_var *)p)->attno; 485: } 486: 487: /* change the result variable for the query to the underlying */ 488: Newresvar = i; 489: }