# /* ** This is intended as a tutorial example of an Equel program. ** You should be familiar with both C and Quel before going ** through the examples. The program may be run to see how the ** examples actually work. To compile and run this program you ** should run the following shell commands: ** ** equel equeltut.q ** cc equeltut.c -lq ** a.out ** ** The first command invokes the Equel pre-processor which in- ** serts code to send queries to INGRES. The output is left in ** the file "equeltut.c" in this case. In general, the pre- ** processor is invoked as: ** ** equel [-d] [-f] [-r] file1.q [file2.q ...] ** ** The output is left in "file1.c", etc. The -d flag tells Equel ** to leave line number information in the file so that run time ** errors can be associated with the proper query. ** ** It is possible to use the C-preprocessor to include files with ** Equel statements and/or declarations in it if these files have ** names ending in ".q.h". Such files will be processed by equel, ** and a C version left in the file ending in ".c.h", which will ** be #included by the C pre-processor. Files which are #included ** but whose names do not end in ".q.h" will be ignored by Equel. */ /* ** Equel uses the same syntax as Quel in almost all cases. There ** are a few differences between Equel and Quel and also some ** subtleties in interfaceing Quel and C constructs. Some impor- ** tant points are: ** ** C-variables declared to Equel are used as variables ** throughout Equel statements, except inside strings or when preceded ** by the non-referencing operator '#'. In particular, be care- ** ful with variable names which are the same as domain names. ** ** All strings passed to C-variables from INGRES will be ** null terminated. This will make them one byte longer than ** they were in the relation, so you must declare character arrays ** to be one byte longer than the domain from which the data will ** come. ** ** Retrieve statements with no result relation have a dif- ** ferent interpretation in Equel than in Quel. There will be ** many examples of this. */ /* ** First some of the queries found in "A Tutorial on INGRES" are ** mapped into Equel so that the similarities and differences ** between the two modes of accessing INGRES can be seen. */ /* ** We start by declaring some variables that will be needed for ** the interaction. Note that the variables are global to Equel, ** that is the declarations are in effect for the entire file. */ ## char pname[21]; /* ** Pname is dimensioned to hold one more ** character than the pname field of the ** parts relation. We will need the ex- ** tra character so that Equel will have ** enough space to null terminate the ** string. More on this later. ** We must use the non-referencing ** operator when using "pname" as a ** field name, as the field has the same ** name as tha variable and equel will ** assume we mean the variable if we just ** write "pname." */ ## char col[9]; /* ** This will be used to hold color attri- ** butes from the parts relation. It is ** named "col" insted of "color" so that ** the term "p.color" does not contain a ** variable reference, and may be used ** without the non-referencing operator. */ main(argc, argv) int argc; char *argv[]; { /* ** We start the interaction with INGRES using data base ** demo. */ ## ingres "-i210" demo /* ** Up to 9 arguments may be specified to the INGRES call. ** Here we have modified the integer output format. ** Flags must be in quotes so that the plus or minus are ** not parsed incorrectly. */ /* ** As in the INGRES tutorial, we may print the parts rela- ** tion: */ ## print parts /* ** Note that this identical to the Quel statement except ** that the line is tagged with the "##" telling the Equ- ** el pre-processor to translate this line into standard ** C */ /* ** The next section of code is intended to parallel the ** third query in the Tutorial [page 4] */ ## range of p is parts /* ** This is identical to the Quel ** syntax. Note also the use of ** a comment in an Equel state- ** ment */ /* ** Note that the first pname is assumed to refer to the vari- ** able "pname", while the second pname is assumed to be a ** constant name (as opposed to the value of the vari- ** able "pname") because of the non-referencing opera- ** tor. */ ## retrieve (pname = p.#pname) ## { /* ** Everything inside the braces is repeated for ** each tuple that is retrieved. */ printf("%s\n", pname); /* ** pname is a properly terminated C string. Equ- ** el null terminates ALL strings which are ** passed from INGRES. Strings will be of length ** one more than the width of the attribute. It ** is assumed that the user has provided enough ** room!! */ ## } /* ** Now we will retrieve the colors and names of the parts ** We will skip the error in the Tutorial and simply note ** that the Equel interpreter would catch the error ** presented on page 4: ** ## retrieve pname = p.#pname, col = p.color ** with the message: ** IS = '=' : line 7, syntax error ** which is almost as helpful as the Quel message. */ ## retrieve (pname = p.#pname, col = p.color) ## /* ** The name "col" was used for the variable name ** insted of "color". The latter would be treat- ** ed as a variable in the phrase "p.color" and ** INGRES would see "p." followed by the value ** color had at runtime. ** ** The comment in this situation must start on a ** line with a "##" since Equel will look for the ** "## {" to be contiguous with the retrieve. ** The same holds for blank lines, they must begin ** with a "##" if they come before the "## {". */ ## { printf("The color of the %s is %s\n", pname,col); ## } /* ** The ##{ and ##} are needed, even if you wish to repeat ** only one line of C-code inside the retrieve. */ /* ** To retrieve and print the parts which are gray we may ** write: */ printf("The following parts are gray:\n"); ## retrieve (pname = p.#pname) ## where p.color = "gray" ## { printf("\t%s\n", pname); ## } /* ** The above query is similar to the query on page 5 of ** the Tutorial. */ /* ** In Equel there is no notion of a "query buffer" as in ** the INGRES Terminal Monitor. If we want to do the ** query on page 6 of the Tutorial we must completely ** specify the query (except for the range statements): */ ## retrieve (pname = p.#pname, col = p.color) ## where p.color = "gray" ## or p.color = "pink" ## { printf("The color of the %s is %s\n", pname, col); ## } /* ** We will now leave the Tutorial behind and use some of ** features particular to Equel. */ example1(); /* ** Next we have an interactive example... */ raise(); /* ** Next an example of "parametrized" Equel statements */ param_ex(); } /* ** Suppose we want to bring parts of a relation into core for ** some number crunching which would be difficult in INGRES. ** ** This example brings elements of the supply relation into an ** array of structures. */ # define MAXDATA 20 /* ** This defines the fields "pnum", "snum", and "quan" to Equel. */ ## struct supply ## { ## int pnum, snum; ## int quan; ## }; /* ** The ##{ and ##} at the start and end of the example1() func- ** tion indicate the scope of variables declared within them. ** Therefore data is considered by Equel to be local to exam- ** ple1. Any free block (a ##{...##} not immeadiately after a ** ##retrieve without a result relation [an into]) makes vari- ** ables declared within it be local (there is, however, only ** one level of locality; i.e. either a variable is global to ** the file, or it is local to the outermost enclosing free ** block. */ example1() ## { ## struct supply data [MAXDATA + 1]; register int i; i = 0; ## range of s is supply /* ** The structure field names are known to be structure ** fields beacuse they were declared as such, and follow ** the structure variable "data". On the right side of ** the equals sign (=) they are not in the position of ** structure fields so are assumed to be domain names, ** although the non-referencing operator could be used ** here any way for clarity. */ ## retrieve (data [i].pnum = s.pnum, ## data [i].snum = s.snum, ## data [i].quan = s.quan) ## where s.shipdate <= "76-12-10" ## { printf("supplier #%d, supplies %d of part %d.\n", data [i].snum, data [i].quan, data [i].pnum); if (i++ >= MAXDATA - 1) { printf("Too much data!\n"); break; /* ** The break is legal because the re- ** trieve is converted into a "while" ** statement. Break is the only accept- ** able way to get out of a retrieve due ** to an user detected error. There is ** code after the "while" to flush out ** the data sent by INGRES which was not ** used by the Equel process. */ } ## } ## } /* ** The routine provides an interactive secession for updating ** salaries. There are other ways of accomplishing this interac- ** tion but this mode brings out some of the possible pitfalls. */ raise() ## { int flag; int per; ## char percent[10]; ## char rname[21]; ## char ename[21]; ## int sal; ## char domain[20]; ## char info[255]; extern *IIinterrupt, reset(); ## range of e is employee /* ** Since the range statement will be in effect as long as ** INGRES is running we declare it at the top of the ** loop rather than each time through the loop. */ /* ** Before entering the loop we arrange to continue pro- ** cessing after an interrupt from the user. It is im- ** perative that we do not catch the signal at this point ** since INGRES will catch the signal and try to syn- ** chronize with the Equel process. When the Equel pro- ** cess has been synchronized it will call (*IIinter- ** rupt)(). */ IIinterrupt = reset; setexit(); loop: printf("Please enter employee's name\n"); if (eread(ename)) return (0); if (ename[0] == '?' && ename[1] == '\0') ## print employee else { flag = 0; /* ** In this interaction we do three queries and ** let INGRES do the arithmetic. The name is re- ** trieved into rname since ename may contain ** pattern matching characters and more than one ** name may be retrieved. For example "Ross*" ** may be entered and both Stanley and Stuart ** will get raises. */ ## retrieve (rname = e.name, sal = e.salary) ## where e.name = ename ## { printf("The current salary of %s is %d\n", rname, sal); flag = 1; ## } if (!flag) { printf("No such employee\n"); goto loop; } printf("Enter percent increase="); if (eread(percent)) goto loop; /* ** There is no facility in Equel to examine, ** modify and then put back a tuple. The replace ** must contain the qualification since there is ** no connection between the previous retrieve ** and the replace. */ ## replace e (salary = e.salary + float8(percent)/100. * e.salary) ## where e.name = ename per = atoi(percent); ## retrieve (rname = e.name, sal = e.salary) ## where e.name = ename ## { printf("With that "); if (per < 5) printf("piddly"); else if (per < 10) printf("modest"); else if (per < 30) printf("inflation fighting"); else printf("tremendous"); printf(" raise, %s now makes $%d\n",rname,sal); ## } printf("Do you want any other information about %s?\n" , ename); if (eread(domain) || domain[0] == 'n' ) goto loop; printf("Enter domain: "); if (eread(domain)) goto loop; /* ** If the user responds with a '?' then show him ** all possible domains by printing out the at- ** tributes of that relation from the tuple in ** the "attribute" relation. */ if (domain[0] == '?' && domain[1] == '\0') { ## range of a is attribute ## retrieve(domain = a.attname) ## where a.attrelid = "employee" ## { printf("\t%s\n", domain); ## } printf("Enter domain: "); if (eread(domain)) goto loop; } /* ** Here we use a C-variable as a domain name. ** The value of the variable is passed to INGRES ** and interpreted as part of the query. */ /* ** The ascii funciton is used because the type of ** the domain is not known. Ascii applied to a ** character domain does nothing. */ ## retrieve (rname = e.name, info = ascii(#e.domain)) ## where e.name = ename ## { printf("%s\t%s = %s\n", rname, domain, info); ## } } goto loop; } /* ** This routine shows the use of parametrized equel statements. ** These are equel statements where the target list is undeter- ** mined until run-time. In this way a variable number of ** domains, or variable types may be used in the same Equel ** statements. */ param_ex() { char name [25]; /* ** Variables used in the ** target list of a ** parametrized state- ** ment need not be de- ** clared to equel. */ register char *string; int empno; char *tl_vector [100]; /* ** Another way to do ** ## retrieve (name = e.#name, empno =e.number) ** ## { ** printf("employee #%d is called %s.\n", ** empno, name); ** ## } */ /* ** This statement initializes the target list variable. ** The '%' sequences indicate the type of the correspond- ** ing argument following. Valid types are : ** %c -- string of any length ** %i2, %i4 -- integer or long ** %f4, %f8 -- float or double */ string = "%c is e.name, %i2 = e.number"; tl_vector [0] = name; tl_vector [1] = &empno; ## param retrieve (string, tl_vector) ## /* ** This statement could also be written ** ## param retrieve ("%c is e.name, %i2 = e.number", ** ## tl_vector) */ ## { printf("employee #%d is called %s.\n", empno, name); ## } /* ** Parametrized append, copy, create, define view, ** retrieve with a result relation, and replace, may ** also be used. ** ** One could say : ** ## param append to employee ("name is %c, number is %i2", ** ## tl_vector) */ ##} /* ** This routine reads a string from the terminal and null ter- ** minates it. It returns 1 when an eof is read. */ eread(p) char *p; { char c; while(c = getchar()) { if(c == '\n') { *p = 0; return(0); } *p++ = c; } return(1); }