In what follows, the following BNF notation is used:
rdb
RDB responds with the top-level prompt
RDB>
At the top level, you can type in
TopLevelInput ::= "help" ";"
expr ";"
binding ";"
command ";"
Tokens may be separated by arbitrary spaces, newlines and comments. Comments
are arbitrary text enclosed by "/*" and "*/" brackets. If you type in a
newline within an expression, definition or command, RDB's prompt changes to
...>
It is restored to RDB> when you return to the top level. Remember, toplevel
inputs ALWAYS end with a semicolon.
The three types of toplevel inputs will now be separately described.
expr ::= "if" expr "then" expr "else" expr
"lambda" "(" [idList] ")" "." expr
simpleExpr [comparisonOp simpleExpr]
comparisonOp ::= "<" | "<=" | "=" | "<>" | ">=" | ">"
simpleExpr ::= [prefixOp] term {addOp term}
prefixOp ::= "+" | "-" | "not"
addOp ::= "+" | "-" | "or"
term ::= factor {multOp factor}
multOp ::= "*" | "/" | "div" | "mod" | "and"
factor ::= boolConst | unsignedInt
charString | literalRelation
id [ "(" [argList] ")" ]
"(" expr ")"
"reduce" "(" expr "," expr "," expr ")"
"project" expr "over" attribList
"restrict" expr "with" expr
"join" expr "with" expr ["on" attribList]
"rename" attribList "to" idList "in" expr
"insert" expr
argList ::= expr {"," expr}
literalRelation ::= "{" idList [tupleList] "}"
tupleList ::= tuple {tuple}
tuple ::= "[" atomicValue {atomicValue} "]"
atomicValue ::= integer | charString
boolConst | id
idList ::= id {"," id}
attribList ::= attrib {"," attrib}
attrib ::= id [":" unsignedInt]
id ::= letter {restChars}
restChars ::= letter | digit | "_"
integer ::= ["-"] unsignedInt
unsignedInt ::= digit {digit}
charString ::= "'" anyNonControlChars "'"
boolConst ::= "true" | "false"
Notes:
The infix operators "+", "-" and "*" work on integers as usual, and on relations as the set union, difference and intersection operations. The "+" operation also concatenates charStrings.
The infix operator "/" is the "divide" operator on relations; it does NOT work on any other type. Division on integers is done using the infix "div" operator.
An identifier followed by 0 or more expressions in parentheses corresponds to function application.
Literal relations are written enclosed by braces. After the "{", the idList names the columns in the relation. After that comes zero or more tuples, followed by "}". Each tuple (row) has one or more data values enclosed in square brackets, which may be integers, booleans or charStrings.
"project" projects the relation (expr) over the attributes named in the attribList.
"restrict" restricts the relation (first expr) to those tuples that satisfy the predicate (second expr).
"join" joins the first relation (first expr) with the second relation (second expr) on the attributes named by the attribList. The join is an equi-join. If the ""on" attribList" clause is omitted, it performs the general cross-product of the two relations.
"rename" renames the columns named by attribList to the names in idList in the relation expr.
"insert" allows one to interactively type in tuples of relations. The expr is first evaluated to get a (possibly empty) relation. RDB then uses the attribute names of this relation to prompt you for further tuples. As soon as at least one tuple is known, RDB begins to enforce subsequent tuples to have the same datatypes.
Attribute names (attribs) are just identifiers, at their simplest. However, relations may have more than one column with the same name. Thus attribute names may be qualified by a ":n", where n is an unsigned integer. Thus, for example, "CITY:3" stands for the third column named "CITY".
"reduce" may be used to find the cardinality of a relation, totals over columns, max and min values in a column, etc. The first expr should evaluate to a relation, the second should be a unary function, and the third may be any value. "reduce" will iterate over all the tuples in the relation, repeatedly applying the function to an accumulator value to produce a new accumulator value. The initial accumulator value is the value of the third expr; the final value is returned.
binding ::= "let" id [argstruc] "=" expr
argstruc ::= "(" [idList] ")"
This causes a value to be bound to the identifier (id). If no argstruc is
given, the expr is evaluated and its value bound to the identifier. If an
argstruc is given, then a function value is bound to the identifier; the
argstruc represents its formal parameters and expr represents its body.
Identifiers may be used in expressions, where they represent the values that they are bound to. There is a special identifier "it", which always represents the most recent value resulting from toplevel expression evaluation.
command ::= "use" filename
"save" [idList] "in" filename
"bindings"
"exit"
filename ::= charString
The "use" command treats the text in the named file as if it were typed in at
the terminal. This is the most convenient way of entering large relations --
create a file with a text editor, and load it into RDB with "use".
The "save" command saves the identifier bindings named in idList in the named file, as text. The bindings can be reloaded from the file with the "use" command, or edited with a text editor. If idList is omitted, all current bindings are saved.
In both "use" and "save", the default file extension is ".rdb".
The "bindings" command shows all identifiers currently bound, along with their datatypes (integer, boolean, charString, relation or function).
The "exit" command takes you out of RDB back to the monitor.
RDB> 23;
23
RDB> 23+2*5;
33
RDB> (23+2)*5;
125
RDB> 5 + it;
130
RDB> let x = 20*4;
value x
RDB x - it;
-50
RDB> let f(y) = y div 2;
function f
RDB> f(x);
40
RDB> let s = {snum sname status city
...> [s1 Jones 20 London]
...> [s2 Blake 30 Paris ]
...> [s3 Brown 50 Paris ]};
value s
RDB> s;
--------------------------
|snum|sname|status|city |
--------------------------
|s3 |Brown| 50|Paris |
|s2 |Blake| 30|Paris |
|s1 |Jones| 20|London|
--------------------------
RDB> let sp = {snum part weight [s2 Nut 10]
...> [s2 Bolt 20][s1 Washer 25]};
value sp
RDB> join s with sp on snum;
----------------------------------------
|snum|sname|status|city |part |weight|
----------------------------------------
|s2 |Blake| 30|Paris |Nut | 10|
|s2 |Blake| 30|Paris |Bolt | 20|
|s1 |Jones| 20|London|Washer| 25|
----------------------------------------
RDB> restrict it with (weight >= 20) and (city='Paris');
----------------------------------------
|snum|sname|status|city |part |weight|
----------------------------------------
|s2 |Blake| 30|Paris |Bolt | 20|
----------------------------------------
RDB> project s over city;
--------
|city |
--------
|Paris |
|London|
--------
RDB> bindings;
x : integer
f : function
s : relation
sp : relation
RDB> save f,s in 'temp.rdb';
f saved
s saved
RDB> save in 'all';
x saved
f saved
s saved
sp saved
RDB> let f(x) = weight + x;
function f
RDB> let add1(x) = 1+x;
function add1
RDB> /* total weight is */ reduce(sp,f,0);
55
RDB> /* cardinality of sp is */ reduce(sp,add1,0);
3
RDB> /* average weight */ reduce(sp,f,0) div it;
18
RDB> exit;
References
Date,C.J., An Introduction to Database Systems, Addison Wesley.