4.2BSD/usr/ingres/doc/other/equeltut.q
#
/*
** 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);
}