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