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