|
|
1.1 ! root 1: # ! 2: /* ! 3: ** This is intended as a tutorial example of an Equel program. ! 4: ** You should be familiar with both C and Quel before going ! 5: ** through the examples. The program may be run to see how the ! 6: ** examples actually work. To compile and run this program you ! 7: ** should run the following shell commands: ! 8: ** ! 9: ** equel equeltut.q ! 10: ** cc equeltut.c -lq ! 11: ** a.out ! 12: ** ! 13: ** The first command invokes the Equel pre-processor which in- ! 14: ** serts code to send queries to INGRES. The output is left in ! 15: ** the file "equeltut.c" in this case. In general, the pre- ! 16: ** processor is invoked as: ! 17: ** ! 18: ** equel [-d] [-f] [-r] file1.q [file2.q ...] ! 19: ** ! 20: ** The output is left in "file1.c", etc. The -d flag tells Equel ! 21: ** to leave line number information in the file so that run time ! 22: ** errors can be associated with the proper query. ! 23: ** ! 24: ** It is possible to use the C-preprocessor to include files with ! 25: ** Equel statements and/or declarations in it if these files have ! 26: ** names ending in ".q.h". Such files will be processed by equel, ! 27: ** and a C version left in the file ending in ".c.h", which will ! 28: ** be #included by the C pre-processor. Files which are #included ! 29: ** but whose names do not end in ".q.h" will be ignored by Equel. ! 30: */ ! 31: ! 32: ! 33: /* ! 34: ** Equel uses the same syntax as Quel in almost all cases. There ! 35: ** are a few differences between Equel and Quel and also some ! 36: ** subtleties in interfaceing Quel and C constructs. Some impor- ! 37: ** tant points are: ! 38: ** ! 39: ** C-variables declared to Equel are used as variables ! 40: ** throughout Equel statements, except inside strings or when preceded ! 41: ** by the non-referencing operator '#'. In particular, be care- ! 42: ** ful with variable names which are the same as domain names. ! 43: ** ! 44: ** All strings passed to C-variables from INGRES will be ! 45: ** null terminated. This will make them one byte longer than ! 46: ** they were in the relation, so you must declare character arrays ! 47: ** to be one byte longer than the domain from which the data will ! 48: ** come. ! 49: ** ! 50: ** Retrieve statements with no result relation have a dif- ! 51: ** ferent interpretation in Equel than in Quel. There will be ! 52: ** many examples of this. ! 53: */ ! 54: ! 55: ! 56: ! 57: ! 58: ! 59: /* ! 60: ** First some of the queries found in "A Tutorial on INGRES" are ! 61: ** mapped into Equel so that the similarities and differences ! 62: ** between the two modes of accessing INGRES can be seen. ! 63: */ ! 64: ! 65: ! 66: ! 67: ! 68: /* ! 69: ** We start by declaring some variables that will be needed for ! 70: ** the interaction. Note that the variables are global to Equel, ! 71: ** that is the declarations are in effect for the entire file. ! 72: */ ! 73: ! 74: ## char pname[21]; /* ! 75: ** Pname is dimensioned to hold one more ! 76: ** character than the pname field of the ! 77: ** parts relation. We will need the ex- ! 78: ** tra character so that Equel will have ! 79: ** enough space to null terminate the ! 80: ** string. More on this later. ! 81: ** We must use the non-referencing ! 82: ** operator when using "pname" as a ! 83: ** field name, as the field has the same ! 84: ** name as tha variable and equel will ! 85: ** assume we mean the variable if we just ! 86: ** write "pname." ! 87: */ ! 88: ! 89: ## char col[9]; /* ! 90: ** This will be used to hold color attri- ! 91: ** butes from the parts relation. It is ! 92: ** named "col" insted of "color" so that ! 93: ** the term "p.color" does not contain a ! 94: ** variable reference, and may be used ! 95: ** without the non-referencing operator. ! 96: */ ! 97: ! 98: main(argc, argv) ! 99: int argc; ! 100: char *argv[]; ! 101: ! 102: { ! 103: ! 104: /* ! 105: ** We start the interaction with INGRES using data base ! 106: ** demo. ! 107: */ ! 108: ! 109: ! 110: ## ingres "-i210" demo ! 111: ! 112: /* ! 113: ** Up to 9 arguments may be specified to the INGRES call. ! 114: ** Here we have modified the integer output format. ! 115: ** Flags must be in quotes so that the plus or minus are ! 116: ** not parsed incorrectly. ! 117: */ ! 118: ! 119: ! 120: ! 121: /* ! 122: ** As in the INGRES tutorial, we may print the parts rela- ! 123: ** tion: ! 124: */ ! 125: ! 126: ## print parts ! 127: ! 128: /* ! 129: ** Note that this identical to the Quel statement except ! 130: ** that the line is tagged with the "##" telling the Equ- ! 131: ** el pre-processor to translate this line into standard ! 132: ** C ! 133: */ ! 134: ! 135: /* ! 136: ** The next section of code is intended to parallel the ! 137: ** third query in the Tutorial [page 4] ! 138: */ ! 139: ! 140: ## range of p is parts /* ! 141: ** This is identical to the Quel ! 142: ** syntax. Note also the use of ! 143: ** a comment in an Equel state- ! 144: ** ment ! 145: */ ! 146: ! 147: ! 148: /* ! 149: ** Note that the first pname is assumed to refer to the vari- ! 150: ** able "pname", while the second pname is assumed to be a ! 151: ** constant name (as opposed to the value of the vari- ! 152: ** able "pname") because of the non-referencing opera- ! 153: ** tor. ! 154: */ ! 155: ## retrieve (pname = p.#pname) ! 156: ## { ! 157: /* ! 158: ** Everything inside the braces is repeated for ! 159: ** each tuple that is retrieved. ! 160: */ ! 161: ! 162: printf("%s\n", pname); ! 163: ! 164: /* ! 165: ** pname is a properly terminated C string. Equ- ! 166: ** el null terminates ALL strings which are ! 167: ** passed from INGRES. Strings will be of length ! 168: ** one more than the width of the attribute. It ! 169: ** is assumed that the user has provided enough ! 170: ** room!! ! 171: */ ! 172: ## } ! 173: ! 174: ! 175: ! 176: /* ! 177: ** Now we will retrieve the colors and names of the parts ! 178: ** We will skip the error in the Tutorial and simply note ! 179: ** that the Equel interpreter would catch the error ! 180: ** presented on page 4: ! 181: ** ## retrieve pname = p.#pname, col = p.color ! 182: ** with the message: ! 183: ** IS = '=' : line 7, syntax error ! 184: ** which is almost as helpful as the Quel message. ! 185: */ ! 186: ! 187: ! 188: ! 189: ! 190: ## retrieve (pname = p.#pname, col = p.color) ! 191: ## /* ! 192: ** The name "col" was used for the variable name ! 193: ** insted of "color". The latter would be treat- ! 194: ** ed as a variable in the phrase "p.color" and ! 195: ** INGRES would see "p." followed by the value ! 196: ** color had at runtime. ! 197: ** ! 198: ** The comment in this situation must start on a ! 199: ** line with a "##" since Equel will look for the ! 200: ** "## {" to be contiguous with the retrieve. ! 201: ** The same holds for blank lines, they must begin ! 202: ** with a "##" if they come before the "## {". ! 203: */ ! 204: ## { ! 205: printf("The color of the %s is %s\n", pname,col); ! 206: ## } ! 207: ! 208: /* ! 209: ** The ##{ and ##} are needed, even if you wish to repeat ! 210: ** only one line of C-code inside the retrieve. ! 211: */ ! 212: ! 213: /* ! 214: ** To retrieve and print the parts which are gray we may ! 215: ** write: ! 216: */ ! 217: ! 218: printf("The following parts are gray:\n"); ! 219: ## retrieve (pname = p.#pname) ! 220: ## where p.color = "gray" ! 221: ## { ! 222: printf("\t%s\n", pname); ! 223: ## } ! 224: ! 225: ! 226: /* ! 227: ** The above query is similar to the query on page 5 of ! 228: ** the Tutorial. ! 229: */ ! 230: ! 231: ! 232: /* ! 233: ** In Equel there is no notion of a "query buffer" as in ! 234: ** the INGRES Terminal Monitor. If we want to do the ! 235: ** query on page 6 of the Tutorial we must completely ! 236: ** specify the query (except for the range statements): ! 237: */ ! 238: ! 239: ## retrieve (pname = p.#pname, col = p.color) ! 240: ## where p.color = "gray" ! 241: ## or p.color = "pink" ! 242: ## { ! 243: printf("The color of the %s is %s\n", pname, col); ! 244: ## } ! 245: ! 246: ! 247: ! 248: /* ! 249: ** We will now leave the Tutorial behind and use some of ! 250: ** features particular to Equel. ! 251: */ ! 252: ! 253: ! 254: example1(); ! 255: ! 256: /* ! 257: ** Next we have an interactive example... ! 258: */ ! 259: ! 260: raise(); ! 261: ! 262: /* ! 263: ** Next an example of "parametrized" Equel statements ! 264: */ ! 265: ! 266: param_ex(); ! 267: } ! 268: ! 269: ! 270: /* ! 271: ** Suppose we want to bring parts of a relation into core for ! 272: ** some number crunching which would be difficult in INGRES. ! 273: ** ! 274: ** This example brings elements of the supply relation into an ! 275: ** array of structures. ! 276: */ ! 277: # define MAXDATA 20 ! 278: ! 279: ! 280: ! 281: ! 282: /* ! 283: ** This defines the fields "pnum", "snum", and "quan" to Equel. ! 284: */ ! 285: ## struct supply ! 286: ## { ! 287: ## int pnum, snum; ! 288: ## int quan; ! 289: ## }; ! 290: ! 291: /* ! 292: ** The ##{ and ##} at the start and end of the example1() func- ! 293: ** tion indicate the scope of variables declared within them. ! 294: ** Therefore data is considered by Equel to be local to exam- ! 295: ** ple1. Any free block (a ##{...##} not immeadiately after a ! 296: ** ##retrieve without a result relation [an into]) makes vari- ! 297: ** ables declared within it be local (there is, however, only ! 298: ** one level of locality; i.e. either a variable is global to ! 299: ** the file, or it is local to the outermost enclosing free ! 300: ** block. ! 301: */ ! 302: ! 303: example1() ! 304: ## { ! 305: ! 306: ## struct supply data [MAXDATA + 1]; ! 307: register int i; ! 308: ! 309: i = 0; ! 310: ! 311: ## range of s is supply ! 312: ! 313: /* ! 314: ** The structure field names are known to be structure ! 315: ** fields beacuse they were declared as such, and follow ! 316: ** the structure variable "data". On the right side of ! 317: ** the equals sign (=) they are not in the position of ! 318: ** structure fields so are assumed to be domain names, ! 319: ** although the non-referencing operator could be used ! 320: ** here any way for clarity. ! 321: */ ! 322: ! 323: ## retrieve (data [i].pnum = s.pnum, ! 324: ## data [i].snum = s.snum, ! 325: ## data [i].quan = s.quan) ! 326: ## where s.shipdate <= "76-12-10" ! 327: ## { ! 328: printf("supplier #%d, supplies %d of part %d.\n", ! 329: data [i].snum, data [i].quan, data [i].pnum); ! 330: if (i++ >= MAXDATA - 1) ! 331: { ! 332: printf("Too much data!\n"); ! 333: break; ! 334: /* ! 335: ** The break is legal because the re- ! 336: ** trieve is converted into a "while" ! 337: ** statement. Break is the only accept- ! 338: ** able way to get out of a retrieve due ! 339: ** to an user detected error. There is ! 340: ** code after the "while" to flush out ! 341: ** the data sent by INGRES which was not ! 342: ** used by the Equel process. ! 343: */ ! 344: } ! 345: ! 346: ## } ! 347: ## } ! 348: ! 349: ! 350: ! 351: ! 352: ! 353: ! 354: ! 355: /* ! 356: ** The routine provides an interactive secession for updating ! 357: ** salaries. There are other ways of accomplishing this interac- ! 358: ** tion but this mode brings out some of the possible pitfalls. ! 359: */ ! 360: ! 361: raise() ! 362: ## { ! 363: int flag; ! 364: int per; ! 365: ## char percent[10]; ! 366: ## char rname[21]; ! 367: ## char ename[21]; ! 368: ## int sal; ! 369: ## char domain[20]; ! 370: ## char info[255]; ! 371: extern *IIinterrupt, reset(); ! 372: ! 373: ## range of e is employee ! 374: ! 375: /* ! 376: ** Since the range statement will be in effect as long as ! 377: ** INGRES is running we declare it at the top of the ! 378: ** loop rather than each time through the loop. ! 379: */ ! 380: ! 381: ! 382: ! 383: /* ! 384: ** Before entering the loop we arrange to continue pro- ! 385: ** cessing after an interrupt from the user. It is im- ! 386: ** perative that we do not catch the signal at this point ! 387: ** since INGRES will catch the signal and try to syn- ! 388: ** chronize with the Equel process. When the Equel pro- ! 389: ** cess has been synchronized it will call (*IIinter- ! 390: ** rupt)(). ! 391: */ ! 392: ! 393: IIinterrupt = reset; ! 394: setexit(); ! 395: loop: ! 396: printf("Please enter employee's name\n"); ! 397: ! 398: if (eread(ename)) ! 399: return (0); ! 400: ! 401: if (ename[0] == '?' && ename[1] == '\0') ! 402: ## print employee ! 403: else ! 404: { ! 405: flag = 0; ! 406: ! 407: /* ! 408: ** In this interaction we do three queries and ! 409: ** let INGRES do the arithmetic. The name is re- ! 410: ** trieved into rname since ename may contain ! 411: ** pattern matching characters and more than one ! 412: ** name may be retrieved. For example "Ross*" ! 413: ** may be entered and both Stanley and Stuart ! 414: ** will get raises. ! 415: */ ! 416: ! 417: ## retrieve (rname = e.name, sal = e.salary) ! 418: ## where e.name = ename ! 419: ## { ! 420: printf("The current salary of %s is %d\n", ! 421: rname, sal); ! 422: flag = 1; ! 423: ## } ! 424: ! 425: if (!flag) ! 426: { ! 427: printf("No such employee\n"); ! 428: goto loop; ! 429: } ! 430: printf("Enter percent increase="); ! 431: if (eread(percent)) ! 432: goto loop; ! 433: ! 434: ! 435: /* ! 436: ** There is no facility in Equel to examine, ! 437: ** modify and then put back a tuple. The replace ! 438: ** must contain the qualification since there is ! 439: ** no connection between the previous retrieve ! 440: ** and the replace. ! 441: */ ! 442: ## replace e (salary = e.salary + float8(percent)/100. * e.salary) ! 443: ## where e.name = ename ! 444: ! 445: ! 446: per = atoi(percent); ! 447: ! 448: ## retrieve (rname = e.name, sal = e.salary) ! 449: ## where e.name = ename ! 450: ## { ! 451: printf("With that "); ! 452: if (per < 5) ! 453: printf("piddly"); ! 454: else if (per < 10) ! 455: printf("modest"); ! 456: else if (per < 30) ! 457: printf("inflation fighting"); ! 458: else ! 459: printf("tremendous"); ! 460: printf(" raise, %s now makes $%d\n",rname,sal); ! 461: ## } ! 462: ! 463: ! 464: printf("Do you want any other information about %s?\n" ! 465: , ename); ! 466: ! 467: if (eread(domain) || domain[0] == 'n' ) ! 468: goto loop; ! 469: ! 470: printf("Enter domain: "); ! 471: ! 472: if (eread(domain)) ! 473: goto loop; ! 474: ! 475: /* ! 476: ** If the user responds with a '?' then show him ! 477: ** all possible domains by printing out the at- ! 478: ** tributes of that relation from the tuple in ! 479: ** the "attribute" relation. ! 480: */ ! 481: ! 482: if (domain[0] == '?' && domain[1] == '\0') ! 483: { ! 484: ! 485: ## range of a is attribute ! 486: ! 487: ## retrieve(domain = a.attname) ! 488: ## where a.attrelid = "employee" ! 489: ## { ! 490: printf("\t%s\n", domain); ! 491: ## } ! 492: printf("Enter domain: "); ! 493: ! 494: if (eread(domain)) ! 495: goto loop; ! 496: } ! 497: ! 498: ! 499: /* ! 500: ** Here we use a C-variable as a domain name. ! 501: ** The value of the variable is passed to INGRES ! 502: ** and interpreted as part of the query. ! 503: */ ! 504: ! 505: ! 506: ! 507: /* ! 508: ** The ascii funciton is used because the type of ! 509: ** the domain is not known. Ascii applied to a ! 510: ** character domain does nothing. ! 511: */ ! 512: ## retrieve (rname = e.name, info = ascii(e.domain)) ! 513: ## where e.name = ename ! 514: ## { ! 515: ! 516: printf("%s\t%s = %s\n", rname, domain, info); ! 517: ## } ! 518: ! 519: } ! 520: goto loop; ! 521: } ! 522: ! 523: /* ! 524: ** This routine shows the use of parametrized equel statements. ! 525: ** These are equel statements where the target list is undeter- ! 526: ** mined until run-time. In this way a variable number of ! 527: ** domains, or variable types may be used in the same Equel ! 528: ** statements. ! 529: */ ! 530: ! 531: param_ex() ! 532: { ! 533: char name [25]; /* ! 534: ** Variables used in the ! 535: ** target list of a ! 536: ** parametrized state- ! 537: ** ment need not be de- ! 538: ** clared to equel. ! 539: */ ! 540: register char *string; ! 541: int empno; ! 542: char *tl_vector [100]; ! 543: ! 544: /* ! 545: ** Another way to do ! 546: ** ## retrieve (name = e.#name, empno =e.number) ! 547: ** ## { ! 548: ** printf("employee #%d is called %s.\n", ! 549: ** empno, name); ! 550: ** ## } ! 551: */ ! 552: ! 553: /* ! 554: ** This statement initializes the target list variable. ! 555: ** The '%' sequences indicate the type of the correspond- ! 556: ** ing argument following. Valid types are : ! 557: ** %c -- string of any length ! 558: ** %i2, %i4 -- integer or long ! 559: ** %f4, %f8 -- float or double ! 560: */ ! 561: ! 562: string = "%c is e.name, %i2 = e.number"; ! 563: tl_vector [0] = name; ! 564: tl_vector [1] = &empno; ! 565: ! 566: ## param retrieve (string, tl_vector) ! 567: ## /* ! 568: ** This statement could also be written ! 569: ** ## param retrieve ("%c is e.name, %i2 = e.number", ! 570: ** ## tl_vector) ! 571: */ ! 572: ## { ! 573: printf("employee #%d is called %s.\n", empno, name); ! 574: ## } ! 575: ! 576: /* ! 577: ** Parametrized append, copy, create, define view, ! 578: ** retrieve with a result relation, and replace, may ! 579: ** also be used. ! 580: ** ! 581: ** One could say : ! 582: ** ## param append to employee ("name is %c, number is %i2", ! 583: ** ## tl_vector) ! 584: */ ! 585: ##} ! 586: ! 587: ! 588: ! 589: /* ! 590: ** This routine reads a string from the terminal and null ter- ! 591: ** minates it. It returns 1 when an eof is read. ! 592: */ ! 593: ! 594: eread(p) ! 595: char *p; ! 596: { ! 597: char c; ! 598: while(c = getchar()) ! 599: { ! 600: if(c == '\n') ! 601: { ! 602: *p = 0; ! 603: return(0); ! 604: } ! 605: *p++ = c; ! 606: } ! 607: return(1); ! 608: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.