|
|
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.