In what follows, the following BNF notation is used:
rdbRDB 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 ::= charStringThe "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.