Subsections
Language Syntax
Terminal Atom Syntax
To express the syntax of terminal atoms, we use the standard
regular expression notation.
Integer Atom
Integers are coded on 64 bits.
The syntax for the integer type is one of the followings:
[0-9]+ decimal base
0x[0-9a-fA-F]+ hexadecimal
0[0-7]+ octal
The domain for the integer type is as follows:
Minimal Value |
Maximal Value |
-9223372036854775808 |
9223372036854775807 |
A few examples:
13940 // integer expressed in the decimal base
0x273f1 // integer expressed in the hexadecimal base
0x273F1 // integer expressed in the hexadecimal base
0100 // integer expressed in the octal base
Float Atom
The syntax for floating point atoms is one of the following regular
expressions:
[0-9]+\.[0-9]+?
[0-9]+?\.[0-9]+
[0-9]+\.[0-9]+?(e|E)[+-]?[0-9]+([fF]|[lL])?
[0-9]+?\.[0-9]+(e|E)[+-]?[0-9]+([fF]|[lL])?
The domain for the float type is as follows:
Minimal Value |
Maximal Value |
4.94065645841246544e-324 |
1.79769313486231570e+308 |
A few examples:
1.
1.23
.3
0.3039
1e+10
2.e+112
1.2e-100
.234e-200
.234e-200f
.234e-200F
String Atom
The syntax for the string type is as follows:
\"([^"]|\\\")*\"
The following escape sequences are interpreted:
Escape Sequence
|
Name
|
ASCII Name
|
\a |
alert |
BEL |
\b |
backspace |
BS |
\f |
form feed |
FF |
\n |
newline |
NL (LF) |
\r |
carriage return |
CR |
\t |
horizontal tab |
HT |
\v |
vertical tab |
VT |
\\ |
backslash |
\ |
\" |
double quote |
" |
\' |
single quote |
' |
\ooo |
octal number |
\ooo |
A few examples:
"hello"
"hello \"world\""
"this is a multi-lines\ntext\n"
"this text contains escape sequences: \007\v\f\n''
Char Atom
The syntax for the char type is one of the followings:
'ascii character'
'\[0-7+]'
'\(x|X)[0-9a-fA-F+]'
'\a'
'\b'
'\f'
'\n'
'\r'
'\t'
'\v'
A few examples:
'a'
'b'
'\n'
'\a'
'\007'
'\x50'
'\x5F'
Boolean Atom
The syntax for a boolean atom is one of the followings:
true
false
Identifier Atom
The syntax for an identifier atom is as follows:
[a-zA-Z\$_#][a-zA-Z\$_0-9#]*
This means that an identifier must start whith a letter, a ``_'',
a ``$'' or a ``#'' which may be followed by letters, digits,
``_'', ``$'' and ``#'' characters.
For instance, the following words are some valid identifiers:
a
alpha
beta1
alpha_beta
$a
oql$maxint
oql#2
$
#
_1
Note that identifiers beginning by oql$ or oql# are
reserved for special used by the interpreter.
Oid Atom
The syntax for an oid is as following:
[0-9]+:[0-9]+:[0-9]+:oid
Note that oid atoms are not typed directly by the user, but are produced
by the database via the OQL interpreter.
The following words are some syntaxically valid atom oids:
123.2.33373:oid
82727272.1.292828282:oid
Object Atom
The syntax for an atom object is as following:
[0-9a-fA-F]+:obj
Note that object atoms are not typed directly by the user, but are produced
by OQL interpreter.
The following words are some syntaxically valid atom objects:
38383:obj
ea954:obj
Null Atom
The null atom denotes an unitialized value. Its type depends on the context.
It can denote a unitialized integer, float, char, string or oid.
The syntax for a null atom is one of the followings:
null
NULL
Nil Atom
The nil atom denotes the empty atom.
The syntax for a nil atom is as follows:
nil
Non Terminal Atom Production
The other atoms - sets, bags, arrays,
lists and structs - are non terminal
atoms. This means that they cannot be generated using a simple lexical
construct.
List, Set, Bag and Array Atoms
To construct a collection atom - list, set, bag
or array -, one may use the
function collection() where collection denotes the
collection type, for instance:
set(1, 2, 3)
list(1, "hello", "world")
array(2, 3, list(3893, -2, 'a'), 22)
bag(2, 2, 3, 4, 5, 12)
This is the simple way to construct such atoms, but as any other atoms,
a collection atom may be produced by the OQL interpreter as the evaluation
of a complex expression, for instance:
select x from Person x
produces an atom bag of objects.
Struct Atom
The most direct way to construct a struct atom is as follows:
struct({identifier:expr})
For instance:
struct(a: 1)
struct(format: text, s: "this is the text")
struct(name: "john", age: 32, spouse: first(select Person))
Keywords
Any programming language has its own set of reserved words (keywords)
that cannot be used as identifiers. For instance, the keyword ``if''
cannot be used as a variable in a C program.
OQL also has its own set of keywords.
But OQL is one part among others in
the information system: for instance, there are an Object Model,
an Object Definition Language (ODL) and Language bindings.
The Object Model does not introduce
any keyword, while ODL has its own set of keywords which are different
from the OQL keywords. For instance, a class can include an
attribute whose name is ``if'' as it is not a ODL keyword.
If one wants to access this attribute in OQL, using for instance
the path expression ``x.if'', we will get a syntax error.
This is not acceptable.
We introduce in OQL (and in ODL) a way to neutralize any keyword:
the token ``@'' used as a prefix keyword neutralizes
the keyword and makes it a valid identifier. For instance, ``x.@if'',
denotes the attribute ``if'' of the instance ``x''.
More generaly, ``@identifier'' denotes the identifier
``identifier'' whether ``identifier'' is a keyword or not.
OQL introduces the following keywords:
add |
all |
append |
array |
as |
asc |
bag |
bodyof |
break |
by |
char |
classof |
contents |
define |
delete |
desc |
distinct |
do |
element |
else |
empty |
eval |
except |
exists |
false |
float |
for |
from |
function |
group |
having |
ident |
if |
import |
in |
int |
intersect |
is |
isset |
like |
list |
mod |
new |
nil |
not |
oid |
order |
pop |
print |
push |
refof |
return |
scopeof |
select |
set |
string |
struct |
structof |
suppress |
then |
throw |
to |
true |
typeof |
union |
unset |
unval |
valof |
where |
while |
Comments
In OQL, comments are identical to the C++:
- all characters after the token // until the end of the current
line are ignored by the interpreter,
- all characters between the tokens /* and */ are
ignored.
For instance:
1 + 2; // this is a comment
a := "hello"; /* this is another
comment */
Statements
A valid OQL construct is composed of a sequence of statements. Main of
the statements are expression statements.
A statement can be one of the following:
- an expression statement,
- a selection statement, if/else,
- an iteration statement, while, do/while, for,
- a function definition statement, function
- a jump statement, break, return
- a compound statement,
- an empty statement.
The OQL expression sub-grammar is very close from the C grammar. The
OQL grammar for the flow controls statement - if/else,
while, do/while, for - is identical to the C
grammar. The common operators of OQL and C have the same associativity
and precedence.
Expression Statements
An expression statement is an expression following by a semicolon.
Expressions are built from typed operands composed recursively by operators.
The syntax of an expression statement is as follows:
expr ;
where expr denotes any expression.
There are three main kinds of expressions: atomic expressions,
unary expressions and binary expressions.
Atomic expressions are composed of one terminal atom and no operators,
unary expressions are composed
of one operand and one operator, binary expressions are composed
of two operands and one operator.
We divide the OQL expression family into several semantical sub-families
according to their operators as follows:
- atomic expressions,
- arithmetic expressions,
- assignment expressions,
- auto increment & decrement expressions,
- comparison expressions,
- logical expressions,
- conditional lists,
- expression sequences,
- array deferencing,
- identifier expressions,
- path expressions,
- function call,
- method invocation,
- eval/unval operators,
- set expressions,
- object creation,
- object deletion,
- collection expressions,
- exception expressions,
- function definition expressions,
- conversion expressions,
- type information expressions
- query expressions,
- miscellenaous expressions
In the following sub-sections, we introduce all the OQL expression types
using the following template presentation:
- we present first, in an unformal way, the syntax and the semantics
of the operators,
- general formal information is presented in a first table:
- operator(s)
- type
- syntax
- operand types
- functions
- in an optionnal second table, we present all the valid operand combination
and their result type. A comment about the function performed is added
if necessary. This table is skipped in case of the operand type combination
is unique or trivial.
- in a last table, we introduce a few examples. The examples
manipulating database objects use the schema that can be found in
the directory
$EYEDBROOT/examples/common:
// person.odl
enum CivilState {
Lady = 0x10,
Sir = 0x20,
Miss = 0x40
};
class Address {
attribute string street;
attribute string<32> town;
attribute string country;
};
class Person {
attribute string name;
attribute int age;
attribute Address addr;
attribute Address other_addrs[];
attribute CivilState cstate;
attribute Person * spouse inverse Person::spouse;
attribute set<Car *> cars inverse owner;
attribute array<Person *> children;
int change_address(in string street, in string town,
out string oldstreet, out string oldtown);
static int getPersonCount();
index on name;
};
class Car {
attribute string brand;
attribute int num;
Person *owner inverse cars;
};
class Employee extends Person {
attribute long salary;
};
Expression types are gathered so to minimize the number of tables in this
document.
Atomic Literal Expressions
Atomic literal expressions are expressions composed of a single terminal
(or token or lexical unit) without any operator. They are also called
primary expressions. These expressions have already been introduced in
Section 5.1.
General Information |
Operator |
no operator |
Type |
unary |
Syntax |
terminal atom |
Operand Types |
integer, float, char, string, boolean, identifier, null, nil, oid, |
Result Type |
same type as the operand |
Expression Examples |
expression |
result |
1 |
1 |
2. |
12. |
'a' |
'a' |
"hello" |
"hello" |
alpha |
value of alpha |
true |
true |
83283.1.29292:oid |
an error is raised in case
of the oid is invalid. |
|
Otherwise the result is the input oid: 83283.1.29292:oid |
Arithmetic Expressions
Arithmetic expressions gather the expressions used for any arithmetic
computation: addition, multiplication, substraction, division, modulo, shift
left and right,
bitwise or, bitwise exclusive, bitwise and, bitwise complement.
This Section introduced these operators with a special focus on the
additive operator which is a multi-purpose operator.
Additive Expression
The additive operator (i.e. +) is used for arithmetic
addition of integers, floating point numbers and characters, and is
also used for string concatenation,
list or array concatenation and set or bag union. Its functionality
depends on the type of its operands: it is a polymorphic operator.
Note that the choice of its functionality is done at evaluation time, not
at compile time. That means that the functionality of an expression such as
x + y is unknown until the evaluation time. Depending on the
dynamic type of the operands x and y, it can be a
simple arithmetic addition, a string or list or array concatenation,
a set or bag union or it can raise an error.
When used as a arithmetic operator and when the two
operands have not the same type, one of the operands can be
automatically promote to the type of the second one.
The promotion mechanism is the same as in the C or C++ languages:
integer may be promoted to float, char may be promoted to
float or integer.
General Information |
Operator |
+ |
Type |
binary |
Syntax |
expr + expr |
Commutative |
yes |
Operand Types |
integer, float, char, string, list, bag, set, array |
Result Type |
see following table |
Function |
multi functions according to operands: arithmetic addition,
string concatenation, list or array concatenation, set or bag union. |
Possible Operand Combinations |
first operand type |
second operand type |
result type |
|
integer |
integer |
integer |
|
integer |
float |
float |
|
char |
char |
integer |
|
char |
integer |
integer |
|
char |
float |
float |
|
float |
float |
float |
|
string |
string |
string |
string concatenation |
list |
list |
list |
list concatenation |
array |
array |
array |
array concatenation |
set |
set |
set |
set union |
bag |
bag |
bag |
bag union |
Expression Examples |
expression |
result |
1 + 2 |
3 |
1 + 2. |
3. |
2 + 2.3 |
4.3 |
'a' + 'b' |
195 |
'a' + 1.2 |
98.200 |
"hello" + "world" |
"helloworld" |
list(1, 2, 3) + list(2, 3, 4) |
list(1, 2, 3, 2, 3, 4) |
set(1, 2, 3) + set(2, 3, 4) |
set(1, 2, 3, 4) |
1 + "hello" |
raises an error |
set(1, 2, 3) + list(2, 3, 4) |
raises an error |
Multiplicative, Division and Minus Expressions
Multiplicative, division and minus expression syntax, semantics, associativity
and precedence are quite identical to the corresponding C and C++ expressions.
When operands have different types, promotionnal mechanisms are the
same as for the additive operator.
General Information |
Operators |
- |
|
* |
|
/ |
Type |
binary |
Syntaxes |
expr - expr |
|
expr * expr |
|
expr / expr |
Commutative |
- : no |
|
* : yes |
|
/ : no |
Operand Types |
integer, float, char |
Result Type |
see following table |
Functions |
- : substract |
|
* : multiply |
|
/ : divide |
Possible Operand Combinations |
first operand type |
second operand type |
result type |
integer |
integer |
integer |
integer |
float |
float |
integer |
char |
integer |
char |
char |
integer |
char |
integer |
integer |
char |
float |
float |
float |
float |
float |
float |
integer |
float |
float |
char |
float |
Expression Examples |
expression |
result |
1 - 2 |
-1 |
3 * 2. |
6. |
2 * 'a' |
194 |
'a' * 'b' |
9506 |
1 / 2 |
0 |
1 / 2. |
.5000 |
1. / 2 |
.5000 |
1. / 2 |
.5000 |
"hello" * "world" |
raises an error |
1 - "hello" |
raises an error |
Shift, Mod, And, Or, XOr Expressions
Shift, modulo, and, or and xor expression syntax, semantics, associativity
and precedence are quite identical to the corresponding C and C++ expressions.
Operand types must be integer or char and the only
possible type promotion is from char to integer.
General Information |
Operators |
« |
|
» |
|
% |
|
& |
|
| |
|
^ |
Type |
binary |
Syntaxes |
expr « expr |
|
expr » expr |
|
expr % expr |
|
expr & expr |
|
expr | expr |
|
expr ^ expr |
Commutative |
« : no |
|
» : no |
|
% : no |
|
& : yes |
|
| : yes |
|
^ : yes |
Operand Types |
integer, char |
Result Type |
integer |
Functions |
« : left shift |
|
» : right shift |
|
% : modulo |
|
& : bitwise and |
|
| : bitwise or |
|
^ : bitwise exclusive or |
Expression Examples |
expression |
result |
1 « 4 |
16 |
100 » 2 |
25 |
100 % 13 |
9 |
0xf12 & 0xf |
2 |
0xf12 | 0xf |
3871 |
0xf12 ^ 0xf |
3869 |
'b' % '9' |
8 |
2 « 1.2 |
raises an error |
2 % 3.4 |
raises an error |
2.1 % 3 |
raises an error |
Sign Expressions
Sign expressions are the expressions using the unary operators +
or -.
The expression syntax, semantics, associativity
and precedence are quite identical to the corresponding C and C++ expressions.
These unary operators accept only integer,
char and float operands.
General Information |
Operators |
+ |
|
- |
Type |
unary |
Syntaxes |
+ expr |
|
- expr |
Operand Types |
integer, char, float |
Result Type |
see following table |
Functions |
sign operator |
Possible Operand Combinations |
operand type |
result type |
integer |
integer |
float |
float |
char |
integer |
Expression Examples |
expression |
result |
+12 |
12 |
-100 |
-100 |
-123.4 |
-123.4 |
+'a' |
97 |
-'a' |
-97 |
+"hello" |
raises an error |
-null" |
raises an error |
Complement Expressions
The complement operator performs a bitwise complement on its operand.
The expression syntax, semantics, associativity
and precedence are quite identical to the corresponding C and C++ expressions.
This operator accepts only integer and char operands.
General Information |
Operator |
~ |
Type |
unary |
Syntax |
~ expr |
Operand Types |
integer, char |
Result Type |
integer |
Functions |
bitwise complement |
Expression Examples |
expression |
result |
~112 |
-113 |
~0 |
-1 |
~'a' |
-98 |
~2.3 |
raises an error |
~"hello" |
raises an error |
Assignment Expressions
The expression syntax, semantics, associativity
and precedence are quite identical to the corresponding C and C++ expressions except that the simple assignment operator in OQL is :=
instead of = in C or C++. The left operand must be a left value.
A left value is an OQL entity which is assignable: for instance any
identifier or a valid path expression.
When the assignment is simple and when the left value is an identifier,
no type checking on the right operand is done. For instance, x := 10
and x := "hello" are always valid expressions. In the case
of the left value is a path expression, the OQL interpreter checks that
the type of the second operand matches the expected type of the first one.
For instance if p denotes a Person instance,
p->age := 32 is certainly valid while p->age := "hello"
raises a type check error.
When the assignment is combined with another operation (for instance,
the -= operator), the left operand must be initialized and
the interpreter checks that the left and right
operand can be combined through the other operator.
For instance, the following constructs are valid:
a := 10;
a -= 20;
a := "hello";
a += " world";
p := first(select Person);
p.name := "johnny";
first(select Person.age = 0).name := "baby";
while these ones produce errors:
a := "hello";
a -= 20; // raises the error: operation 'string + integer' is not valid
a := list(1, 2);
a *= 2; // raises the error: operation 'list * integer' is not valid
unset b;
b += 20; // raises the error: uninitialized identifier 'b'
p := first(select Person);
p.age := "baby"; // raises the error: integer expected, got string
General Information |
Operators |
:= |
|
*= |
|
/= |
|
%= |
|
+= |
|
-= |
|
«= |
|
»= |
|
&= |
|
|= |
|
^= |
Type |
binary |
Syntaxes |
lvalue := expr |
|
lvalue *= expr |
|
lvalue /= expr |
|
lvalue %= expr |
|
lvalue += expr |
|
lvalue -= expr |
|
lvalue «= expr |
|
lvalue »= expr |
|
lvalue &= expr |
|
lvalue |= expr |
|
lvalue ^= expr |
Commutative |
no |
Operand Types |
leftvalue on the left side and any type
on the right side |
Result Type |
the type of the right operand |
Functions |
perform an operation and assignment |
Expression Examples |
expression |
result |
a := 24 |
24 |
a += 12 |
36 |
a /= 2 |
18 |
a ^= 100 |
118 |
first(select Person).age := 38 |
38 |
"hello" := 4 |
raises an error (i.e. "hello" is not a leftvalue) |
8 := 5 |
raises an error (i.e. 8 is not a leftvalue) |
unset a; a += 20 |
raises an error (i.e. uninitialized identifier) |
Auto Increment & Decrement Expressions
The expression syntax, semantics, associativity
and precedence are quite identical to the corresponding C and C++ expressions. The operand must be an initialized left value of type
integer, char or float. In case of the
operand is a char atom, the result type is an integer.
Otherwise, the result type is the type of the operand.
General Information |
Operators |
++ |
|
- |
Type |
unary |
Syntaxes |
expr - |
|
expr ++ |
|
++expr |
|
-expr |
Operand Type |
leftvalue of type integer, char or float. |
Result Type |
see following table |
Functions |
expr - : post-decrementation |
|
expr ++ : post-incrementation |
|
++expr : pre-incrementation |
|
-expr : pre-incrementation |
Possible Operand Combinations |
operand type |
result type |
integer |
integer |
float |
float |
char |
integer |
Expression Examples |
expression |
result |
|
a := 1; a++ |
1 |
initially a equals 1; the result of the evaluation is 1 but after the evaluation,
a equals 2 |
-a |
0 |
|
a++ |
0 |
a equals 1 after the
evaluation |
Comparison Expressions
The expression syntax, semantics, associativity
and precedence are quite identical to the corresponding C and C++ expressions except that the equal operator could be either == or
=.
Equal and NotEqual Expressions
Operands may have any type at all. If the type of the operands differ (modulo
the type promotion mechanisms for numbers), the
result of the expression operand1 == operand2 is
always false while the result of operand1 !=
operand2 is always true.
General Information |
Operators |
== |
|
!= |
Type |
binary |
Syntaxes |
expr == expr |
|
expr != expr |
Commutative |
yes |
Operand Types |
any type |
Result Type |
boolean |
Functions |
equal |
|
not equal |
When operands are number of different types, an automatic promotion is
done to the more precise type.
Expression Examples |
expression |
result |
1 == 1 |
true |
1 == 1.0 |
true |
1 != 2 |
true |
1 == 2 |
false |
1 == "hello" |
false |
"hello" == "hello" |
true |
list(1, 2, 3) == list(1, 2, 3) |
true |
set(1, 3, 2) == set(1, 2, 3) |
true |
Less and Greater Expressions
The comparison operators <, <=, > and >= are multi-purpose
operators: they are used for
integer, floating point number and character comparison, but also
for list or array term-to-term comparison and for set or bag inclusion.
Their functionality
depends on the type of its operands: they are polymorphic operators.
Note that the choice of the functionality is done at evaluation time, not
at compile time. That means that the functionality of an expression such as
x < y is unknown until the evaluation time. Depending on the
dynamic type of the operands x and y, it can be an
arithmetic comparison (if x and y are numbers),
a alpha-numeric comparison (if x and y are strings),
a term-to-term ordered collection comparison (if x and y are lists or
arrays) or a set or bag inclusion comparison
(if x and y are sets or bags).
While arithmetic and alpha-numeric comparisons are trivial and do not
need any suplementary explanations,
the term-to-term ordered collection comparisons needs to be detailed.
The general algorithm for this functionnality is as follows:
- let l1 and l2 two OQL ordered collections, containing
respectively l1_cnt and l1_cnt atoms.
- let op one of the following polymorphic comparison operators:
< <= > >=,
- l1 op l2 is true if and only if all the
following conditions are realized:
- l1 and l2 must be of the same collection type,
- l1_cnt op l2_cnt or l1_cnt equals
l2_cnt
- for each atom l1[i] with i in [i,l1_cnt], l1[i] op l2[i]
For instance:
list(1, 2) <= list(0, 2) is true
list(1, 2) <= list(3) is false
list(1, 2) <= list(3) is false
list("aaa", 4) < list("bbbb", 8) is true
list("aaa", 4, list(1, 2)) < list("b", 8, list(2, 3)) is true
list(set(2, 4), 3) < list(set(4, 2, 3), 4) is true
list(2, 3) < list("hello", 2) raises an error
list(2, 3) < array(2, 4) raises an error
Note that the fact that l1 <= l2 is false
does not implie that l1 > l2 is true.
Indeed, list(2, 3) < list(1, 3, 2) and
list(1, 3, 2) >= list(2, 3) are false.
General Information |
Operators |
< |
|
<= |
|
> |
|
>= |
Type |
binary |
Syntaxes |
expr < expr |
|
expr <= expr |
|
expr > expr |
|
expr >= expr |
Commutative |
no |
Operand Types |
integer, float, char, string, list, array, set,
bag |
Result Type |
boolean |
Functions |
the function depends on the operands: |
|
< : less than or is included in |
|
<= : less than or equal or is included in or equal |
|
> : greater than or contains |
|
>= : greater than or equal or contains or equal |
Possible Operand Combinations |
first operand type |
second operand type |
result type |
comments |
integer, char, float |
integer,
char, float |
boolean |
performs an arithmetic
comparison |
string |
string |
boolean |
performs an alpha-numeric comparison |
set |
set |
boolean |
performs an inclusion comparison |
bag |
bag |
boolean |
performs an inclusion comparison |
set |
bag |
boolean |
performs an inclusion comparison: the set operand is converted to a
bag |
bag |
set |
boolean |
performs an inclusion comparison: the set operand is converted to a
bag |
list |
list |
boolean |
performs a term-to-term polymorphic (i.e. numeric, alpha-numeric or inclusion)
comparison |
array |
array |
boolean |
performs a term-to-term polymorphic comparison |
Note that in case of different operand types, an automatic promotion is
done to the more precise type.
Expression Examples |
expression |
result |
1 < 2 |
true |
1 >= 2 |
false |
2. <= 2 |
true |
"hello" < "world" |
true |
"hello" >= "world" |
false |
list(1, 2) < list(2, 3) |
true |
list(1, 2) < list(0, 3) |
false |
list(1, 2) < list(0, 3, 2) |
false |
list(0, 3, 2) >= list(1, 2) |
false |
list(1, 2) < list(2, 3, 3) |
true |
list(1, 2) < list(0) |
false |
set(1, 2) < set(2, 4, 44) |
false |
set(1, 2) < set(2, 1, 44) |
true |
"hello" >= 3 |
raises an error |
list(1, 2) < array(2, 4, 44) |
raises an error |
set(1, 2) < bag(2, 1, 44) |
raises an error |
Regular Expression Operators
OQL provides the ODMG OQL regular expression operator like
plus four other ones. These four extra operators are based on the regular
expression UNIX library. So, the syntax of the regular expression are
the same as that used by the well known UNIX tools grep, sed,
and so on. All the regular expression operators takes two string operands:
the first one is the string to compare, the second one is the regular
expression. So, these operators are not commutative.
These operators provide the following functionalities:
like : ODMG OQL operator. Returns true if the firstoperand matches the regular expression.
Otherwise false is returned. The regular expression is am SQL regular expression where,
for instance, % and _ are wilcard characters.
~ : This operator hasthe same functionnality as the like operator but theregular expression
has the UNIX syntax.
: Returns true if the firstoperand matches the regular expression in a case insensitive way.
Otherwise false is returned.
!~ : Returns true if the firstoperand does not match the regular expression.
Otherwise false is returned.
!~~ : Returns true if the firstoperand does not match the regular expression in a case insensitive
way. Otherwise false is returned.
Note that the operator like uses currently the
UNIX form of regular expressions instead of the SQL form. It will become
ODMG/SQL compliant in a next version.
General Information |
Operators |
~ |
|
~~ |
|
!~ |
|
!~~ |
|
like |
Type |
binary |
Syntaxes |
expr ~ expr |
|
expr ~~ expr |
|
expr !~ expr |
|
expr !~~ expr |
|
expr like expr |
Commutative |
no |
Operand Types |
string |
Result Type |
boolean |
Functions |
~ : |
matches the regular expression |
: |
matches the regular expression, case insensitive |
!~ : |
does not match the regular expression |
!~~ : |
does not match the regular expression, case insensitive |
like : |
matches the regular expression |
|
Expression Examples |
expression |
result |
"hello" ~ "LL" |
false |
"hello" ~~ "LL" |
true |
"hello" ~ "^LL" |
false |
"hello" ~ "^h" |
true |
"hello" !~ "^h" |
false |
"hello" ~ ".*ll.*" |
true |
".*ll.*" ~ "hello" |
false because regular
expression should be on the right |
Logical Expressions
OQL provide three logical expressions which can take two form each.
The logical or operator is || or or.
The logical and operator is && or and.
The logical not operator is ! or not.
The expression syntax, semantics, associativity
and precedence are quite identical to the corresponding C and C++ expressions. Note that the ODMG operator ``||'' denotes the string concatenation.
As for C and C++, the OQL interpreter
performs a lazy evaluation:
- expr1 || expr2
expr2 is not evaluated if expr1 is evaluated to true.
- expr1 && expr2
expr2 is not evaluated if expr1 is evaluated to false.
General Information |
Operators |
|| |
|
&& |
Type |
unary, binary |
Syntaxes |
expr || expr |
|
expr or expr |
|
expr && expr |
|
expr and expr |
|
! expr |
|
not expr |
Operand Type |
boolean |
Result Type |
boolean |
Functions |
logical or |
|
logical and |
|
logical not |
Expression Examples |
expression |
result |
true || false |
true |
false || false |
false |
true && false |
false |
1 == 2 || 3 == 4 |
false |
1 == 2 || 3 == 3 |
true |
1 == 2 or 3 == 3 |
true |
1 == 2 || "hello" == "hello" |
true |
(1 == 2 || 2 == 2) && (a = "hello") |
returns true
if a equals "hello". false otherwise |
1 || 3 == 3 |
raises an error: boolean expected got integer |
!3 |
raises an error: boolean expected got integer |
!(1 == 1) |
false |
not(1 == 1) |
false |
Conditional Expression
The unique conditional expression operator is ?:.
The expression syntax, semantics, associativity
and precedence are quite identical to the corresponding C and C++ expressions. The first operand must be an boolean and the two others
may be of any type. Contrary to C and C++, the two
last operands does not need to be of the same type.
General Information |
Operator |
?: |
Type |
ternary |
Syntaxe |
expr ? expr : expr |
Operand Types |
first operand is boolean, others are any type |
Result Type |
type of the evaluated operand |
Functions |
conditional evaluation: evaluates and returns the second operand if
the first operand is true; otherwise evaluates and returns the second operand |
Expression Examples |
expression |
result |
true ? "hello" : "world" |
"hello" |
true ? 2.3 : "world" |
2.3 |
1+1 == 2 ? (a := 3.1415926535) : nil |
3.1515926535 |
1 ? 3 : nil |
raises an error: boolean expected, got integer. |
Expression Sequences
The expression sequence operator - also called comma sequencing - expression syntax, semantics, associativity
and precedence are quite identical to the corresponding C and C++ expressions.
This operator , takes two operands: it evaluates both of them
and returns the second one.
General Information |
Operator |
, |
Type |
binary |
Syntaxe |
expr , expr |
Commutative |
no |
Operand Types |
any type |
Result Type |
type of the second operand |
Functions |
evaluates the first operand, then the second one.
Returns the evaluation of the second one. |
Expression Examples |
expression |
result |
true, "hello" |
"hello" |
a := 2, 4 |
4 (note that a equals
2) |
b := 10, a := b+1 |
11 |
Array Deferencing
OQL provides polymorphic single and range deferencing. The single deferencing
is used to get one element in an ordered collection or a character in a string
or one element in a non-collection array. The range deferencing is used to
get several elements.
The deferencing of ordered collections is introduced in more details
in Section 5.24.
Single Deferencing
The single deferencing operator takes as its first operand an atom of type
string, an indexed
(or ordered) collection (list or array)
or a non-collection array. The second operand must be of type integer.
Depending on the type of the first operand, the returned atom
is as follows:
- if the first operand is a string,
the returned atom is the #expr character of the string
where expr denotes the second operand.
If expr is equal to the length to the string the character
'\000' is returned. If it greater than the length the
string an out of bounds error is raised.
- for an ordered collection, the returned atom is the
#expr item of the collection. If expr is greater
than or is equal to the size of the collection, an
out of bounds error is raised.
- if the first operand is an non-collection array, the returned atom is the
#expr item of the array. If expr is greater
than or is equal to the size of the array, an
out of bounds error is raised.
The single deferencing operator may be used as a left value, that means that a single
deferencing expression is assignable. For instance the sequence of
statements:
s := "hello";
s[1] := 'E';
s[4] := 'O';
set the variable s to "hEllO".
The single deferencing operator may be used everywhere in a path expression.
For instance, first(select Person).other_addrs[2].street[3],
denotes the character #3 of the street attribute
in the #2 other_addrs non-collection array attribute of
the first Person instance.
General Information |
Operator |
[] |
Syntaxe |
expr [expr ] |
Type |
binary |
Commutative |
no |
Operand Types |
first operand: string, indexed collection
(list or array) or non-collection array,
second operand: integer |
Result Type |
char if first operand is a string,
otherwise type of the returned item in the indexed collection or non-collection
array. |
Functions |
[expr ] : returns the character (or item in the
indexed collection or in the non-collection array) number expr |
Note |
this operator may be used in the composition of a left value. |
Expression Examples |
expression |
result |
|
"hello"[0] |
'h' |
|
a := "hello"; a[1] |
'e' |
|
a[3] |
l |
|
a[6] |
raises an error |
|
a[0] := 'H' |
'H' |
a equals
"Hello" |
list(1, 2, "hello", 4)[3] |
"hello" |
|
list(1, 2, "hello", 4)[4] |
raises an error |
|
first(select Person).name[2] := 'X' |
'X' |
|
Range Deferencing
The range deferencing operators, [:] and [?],
takes as their first operand an atom of type
string, an indexed
(or ordered) collection (list or array)
or a non-collection array. The other operands must be of type integer.
The [?] may have also an unordered collection (set
or bag as its first operand.
The operator syntax and semantics are as follows:
- expr [expr1:expr2]
- if the first operand is a string,
the returned atom is a list composed of the characters between
the #expr1 and the #expr2 characters
of the string.
If expr1 is less than zero or or if expr2 is greater than the
length of the string an out of bounds error is raised.
- for an ordered collection, the returned atom is a list composed
of the items between the #expr1 and the
#expr2 items
of the collection. If expr1 is less than zero or if
i expr2 iss greater than or is equal to the size of the collection, an
out of bounds error is raised.
- if the first operand is an non-collection array, the returned atom
is a list composed of the items between the #expr1 and the
#expr2 items of the array.
If expr1 is less than zero or if
i expr2 iss greater than or is equal to the size of the collection, an
out of bounds error is raised.
- expr [?]
- if the first operand is a string,
the returned atom is a list composed of all the characters of the
string, including the last character '\000'.
- for an ordered or an unordered collection, the returned atom is a list composed
of all the items of the collection. If the collection is a list, the
list itself is returned. If the collection is an array, this operator
has the same functionnality as the listtoarray library function.
- if the first operand is an non-collection array, the returned atom
is a list composed of the all the items of the array.
Contrary to the single deferencing operator,
the range deferencing operator cannot be used as a left value.
The range deferencing operators may be used everywhere in a path expression.
For instance, first(select Person).children[?].name[?],
denotes the list of all characters of the name attribute
in all the children of the first Person instance.
General Information |
Operators |
[:] |
|
[?] |
Syntaxes |
expr [expr :expr ] |
|
expr [?] |
Type |
ternary or unary |
Operand Types |
first operand: string or indexed collections
(list or array), second operand and third operand:
integer |
Result Type |
a list of char if first operand is a string,
otherwise a list of returned items in the indexed collection or
non-collection array. |
Functions |
[expr1:expr2] :
returns a lits of characters (or items in collection) indexed
from expr1 to expr2 |
|
[?] :
returns a list of all characters (or items in collection) |
Expression Examples |
expression |
result |
"hello"[0:2] |
list('h', 'e', 'l') |
"hello"[?] |
list('h', 'e', 'l', 'l', 'o', '\000') |
list(1, 2, "hello", 4)[2:3] |
list("hello", 4) |
array(1, 2, "hello", 4)[?] |
list(1, 2, "hello", 4) |
first(select Person).name[?] |
list('j', 'o', 'h', 'n', '\000') |
list(select class.type = "user")[0:4].name |
list("Employee", "Address", "Person") |
Identifier Expressions
We call an identifier expression an unary or binary expression whose operands
must be identifiers. There are height identifier operators: ::,
isset, unset, & (identical to refof),
* (identical to valof), scopeof, push
and pop.
As all these operators take identifiers as their operands,
we skip the second table (operand combinations) while introducing these
operators.
The :: unary/binary operator (called scope operator) is used to
define a global or particular scope for a variable.
The unary version of this operator denotes a global scope.
For instance, ::alpha denotes the global variable alpha.
In the body of a function, identifiers denote local variables; outside
the body of a function identifiers denote global variables, that means
that, in this context, the global scope operator is not mandatory.
If one wants to use a global variable in the body of a function, the
global scope operator is mandatory.
Refer to the Function Definition Statement Section for more information
about local function variables.
The binary version of this operator denotes a particular scope.
For instance, Person::checkName denotes the class attribute
or method of the class Person.
note: class (or static) attributes are not currently well supported
by the OQL interpreter. Class attributes are only supported in some
specific query expressions (refer to the Query Expression Section).
General Information |
Operator |
:: |
Syntax |
:: identifier |
|
identifier::identifier |
Type |
unary and binary |
Operand Types |
identifier |
Result Type |
value of the identifier if used as a right value;
identifier reference if used as a left value |
Function |
defines a global or particular scope for the identifier. |
Expression Examples |
expression |
result |
::a |
the value of the global variable a |
::alpha := 1 |
sets the value of the global variable
alpha to 1, returns 1 |
Person::checkName("wayne") |
calls the class method
checkName in the class Person |
2::alpha |
raises an error |
The isset operator is used to check whether a variable
is already set or not. It returns true is the variable is
set, false otherwise.
General Information |
Operator |
isset |
Syntax |
isset identifier |
Type |
unary |
Operand Type |
identifier |
Result Type |
boolean |
Function |
evaluated to true if the identifier is set, false otherwise |
Expression Examples |
expression |
result |
isset oql$variables |
true |
isset a |
returns true if a is set, false otherwise |
isset 1 |
raises an error |
The unset operator is used to unset an variable. It returns the
nil atom.
General Information |
Operator |
unset |
Syntax |
unset identifier |
Type |
unary |
Operand Type |
identifier |
Result Type |
nil |
Function |
unset the identifier |
Expression Examples |
expression |
result |
unset a |
nil |
unset ::a |
nil |
unset 2 |
raises an error |
The & (identical to refof) operator is used to get the
reference of an identifier. This operator is essentially used when
one calls a function or method which updates one or more given parameters.
For instance, let the function swap(x, y) which swaps the value
of its two parameters. One needs to give the references of the
variables that one wants to swap. For instance:
i := "ii";
j := "jj";
swap(&i, &j);
After the call to swap, the variable i equals
jj while the variable j equals ii.
The reverse operator * (described following section) is used in
the swap function.
General Information |
Operator |
refof |
|
& |
Syntax |
refof identifier |
|
& identifier |
Type |
unary |
Operand Type |
identifier |
Result Type |
identifier |
Function |
evaluates the expression to the identifier reference; returned an
identifier atom |
Expression Examples |
expression |
result |
&alpha |
alpha |
refof alpha |
alpha |
The * (identical to valof) operator is used to get
the value of the identifier pointed by a reference.
For instance, after the two following expressions:
alpha := 1;
ralpha := α
*ralpha equals 1.
This operator may be used in the composition of a left value, for instance:
alpha := 1;
ralpha := α
*ralpha := 2; // now, alpha equals 2
*ralpha += 8; // now, alpha equals 10
But this operator is essentially used in the body of functions or methods which
update one or more given parameters, for instance, the function swap
described in the previous section is as follows:
function swap(x, y) {
v := *x;
*x := *y;
*y := v;
}
General Information |
Operator |
valof |
|
* |
Syntax |
valof identifier |
|
* identifier |
Type |
unary |
Operand Type |
identifier |
Result Type |
value of the identifier |
Function |
returns the value of the identifier denotes by the operand |
Expression Examples |
expression |
result |
*alpha |
if alpha value is an atom identifier x,
returns the value of x, otherwise an error is thrown |
x := 12; alpha := &x; *x |
*x returns 12 |
The scopeof operator returns the string "global" or
"local" depending whether the identifier is global or local.
General Information |
Operator |
scopeof |
Syntax |
scopeof identifier |
Type |
unary |
Operand Type |
identifier |
Result Type |
string |
Function |
returns the scope of the identifier. |
Expression Examples |
expression |
result |
scopeof ::alpha |
returns "global" for any alpha if
it set; otherwise an error is thrown |
scopeof alpha |
returns "global" or "local"
depending on the context. |
The push operator is used to push an identifier on a new local
table. This operator is rarely used.
General Information |
Operator |
push |
Syntax |
push identifier |
|
push identifier := expr |
Type |
unary and binary |
Operand Types |
first operand identifier, optionnal second operand:
any type |
Result Type |
identifier or any type in case of an assignment |
Function |
push the identifier on to the symbol table stack. An assignment
can be performed at the same time. Returns the identifier or the
value of the expression assignment. |
Expression Examples |
expression |
result |
push a |
pushes a on a new local symbol table. |
push a := 10 |
pushes a on a new local symbol table
and assigns its value to 10 |
The pop operator is used to pop an identifier from a local table.
It is used after a push. For instance:
a := "hello";
a; // a equals "hello"
push a := 10;
a; // a equals 10
pop a;
a; // a equals "hello"
General Information |
Operator |
pop |
Syntax |
pop identifier |
Type |
unary |
Operand Type |
identifier |
Result Type |
the type of the value of the identifier |
Function |
pop the identifier from the symbol table stack |
Expression Examples |
expression |
result |
pop a |
returns the value of a if it is set; otherwise
an error is returned |
Path Expressions
The path expression operator -> (identical to .) is used
to navigate from an object
and read the right data one needs. This operator enables us to go
inside complex objects, as well as to follow simple relationships.
For instance, if p denotes a Person instance,
p.spouse denotes the spouse attribute of this person.
The more complex expression p.spouse.address.street denotes
the street in the address of spouse of the
person p. This notation is very intuitive because it looks like the
well known C, C++ and Java syntaxes.
The path expression operator may composed a left value, for instance:
p.spouse.name := "mary";
set the name of the spouse of the person p
to mary.
This operator may be combined with the array deferencing operators, for
instance:
p.spouse.name[2];
p.spouse.name[2] := 'A';
p.spouse.other_addrs[2].street[3] := 'C';
p.spouse.children[?];
p.spouse.children[?].name;
The path expression operator may be also used to navigate through
struct atom, for instance:
(struct(a : 1, b : "hello")).b returns "hello". Note
that because of the precedence of operators, parenthesis are necessary
around the literal struct construct.
Finally, the path operator may be applied to a collection; in this
case a collection of the same type of this operand is returned.
For instance:
(select Person).name returns a bag of string.
(select distinct Person).age returns a set of
int.
Note that the path expression operator is used frequently in the query
expressions as shown in a next section.
General Information |
Operators |
. |
|
-> |
Syntaxes |
expr . expr |
|
expr -> expr |
Type |
binary |
Operand Types |
first operand: oid or object,
second operand: identifier |
Result Type |
type of the attribute denoted by the second operand |
Functions |
returns the attribute value denoted by second operand of the object
denoted by the first operand |
|
The first operand must denote an EYEDB instance (object or literal) of
an agregat including the attribute denoted by the second operand. |
Note |
these two operators are identical |
Expression Examples |
expression |
result |
comments |
p->name |
the value of attribute
name in the object denoted by p |
p must denote an EYEDB instance (object or literal) of
an agregat including the attribute name |
first(select x Person x from x.lastname = "wayne")->lastname |
"wayne" |
|
Function Call
OQL allows one to call an OQL function with or without parameters.
The operator for function call is ().
A function call may be the first term of a path expression, for instance:
first(select Person)->name.
Contrary to the method invocation, there are no function overloading
mechanisms: that means, that one cannot have differents functions with
the same name and a different signature.
To take benefit of the overloading mechanisms, one must use methods.
Note: contrary to the ODMG 3 specifications, one currently needs
to use parenthesis to invoke a method even if the method has no arguments.
General Information |
Operator |
() |
Syntaxe |
expr (expr_list) |
Type |
n-ary |
Operand Types |
first operand: identifier,
other operands: any type |
Returned type |
type of the returned atom by the function call |
Functions |
calls the OQL function denoted by the first operand using the other operands
as arguments. The number of operands must be equal to the number of
arguments of the OQL function plus one |
Expression Examples |
expression |
result |
fact(10) |
3628800 |
fact(fact(3)) |
720 |
toUpper("hello world") |
"HELLO WORLD" |
toUpper("hello") + "world" |
"HELLOworld" |
interval(1, 5) |
list(1, 2, 3, 4, 5) |
swap(&i, &j) |
nil |
first(select Person).spouse.name |
"mary" |
Method Invocation
Instance Method Invocation
OQL allows one to call a instance method with or without parameters.
The method can be written in C++ or in OQL.
As in C++, method calls use a combination of the path expression operator and
the function call operator.
As in C++ or Java, methods can be overloaded: that
means that one can have differents methods with the same name and a
different signature or differents methods with the same name
and the same signature in a class hierarchy.
The choice of the method to invoke
is done at evaluation time not at compile time.
For instance let two
methods Person Person::f(in int, in int) and int Person::f(in float, in string),
the method to be invoked in the expression p->f(x, y) is decided
at evaluation time according to the true types of x and y:
p := first(select Person);
x := 1; y := 2;
p->f(x, y); // X::f(in int, in int) is invoked
p->f(x, y)->name; // this is valid because p->f(x, y) returns a Person
x := 1.3; y := "hello";
p->f(x, y); // X::f(in float, in string) is invoked
p->f(x, y)->name; // this is not valid because p->f(x, y) returns an integer
A major contribution of object orientation is the possibility of manipulating
polymorphic objects and thanks to the late binding mechanism
to carry out generic actions on the elements of these objects.
For instance, let the two
methods void Person::doit(in int)
and void Employee::doit(in int),
the method to be invoked in the expression p->doit(x) is decided
at evaluation time according to the true type of p:
p := new Person();
p->doit(1); // Person::doit(in int) is invoked
p := new Employee();
p->doit(1); // Employee::doit(in int) is invoked
To invoke a method, the following conditions must be realize:
- the object or oid on which the method is applied must be
an instance of a class, for instance X.
- the name of the invoked method, the number and the type of arguments
must be compatible with an existing method in the class X,
- the result type must match the expected type in the expression.
For instance, let the methods int compute(in int, int float) and
int compute(in int, in float, in int[], out string)
in the class X.
To invoke the first method on an instance of X, one needs to
apply the method compute to an instance of X
with one integer and one float, for instance:
x := new X();
x.compute(1, 2.3);
x.compute(a := fact(10), float(fib(10)));
To invoke the second method on an instance of X, one needs to
apply the method compute to an instance of X
with an integer, a float, an ordered collection of integer and a reference
to a variable, for instance:
x.compute(1, 23.4, list(1, 2, 3, 4), &a);
The following table shows the mapping (which defines the compatibility)
between the ODL and the OQL types.
ODL/OQL Mapping |
ODL Type |
OQL Type |
in int16 |
integer |
out int16 |
identifier |
inout int16 |
identifier initialized to an integer |
in int32 |
integer |
out int32 |
identifier |
inout int32 |
identifier initialized to an integer |
in int64 |
integer |
out int64 |
identifier |
inout int64 |
identifier initialized to an integer |
in byte |
char |
out byte |
identifier |
inout byte |
identifier initialized to a char |
in char |
char |
out char |
identifier |
inout char |
identifier initialized to a char |
in string |
string |
out string |
identifier |
inout string |
identifier initialized to a string |
in float |
float |
out float |
identifier |
inout float |
identifier initialized to a float |
in oid |
oid |
out oid |
identifier |
inout oid |
identifier initialized to a oid |
in object * |
oid of any class |
out object * |
identifier |
inout object * |
identifier initialized to an oid of any class |
in X * (X denotes a class instance) |
oid of class X |
out X * (X denotes a class instance) |
identifier |
inout X * (X denotes a class instance) |
identifier initialized to a oid of class X |
in X *[] (X denotes a class instance) |
ordered collection of oid of class X |
out X *[] (X denotes a class instance) |
identifier |
inout X *[] (X denotes a class instance) |
identifier initialized to an ordered collection of oid of class X |
in X[] (X denotes any ODL type) |
ordered collection of atoms bound to X |
out X[] (X denotes any ODL type) |
identifier |
inout X[] (X denotes any ODL type) |
identifier initialized to an ordered collection of atoms bound to X |
Note: contrary to the ODMG 3 specifications, one currently needs
to use parenthesis to invoke a method even if the method has no arguments.
General Information |
Operators |
. |
|
-> |
Syntaxes |
expr . expr (expr_list) |
|
expr -> expr (expr_list) |
Type |
n-ary |
Operand Types |
first operand: oid or object,
second operand: identifier, other operands: any type |
Result Type |
type of the atom returned by the method call |
Functions |
invokes the method denoted by the second operand applied to the
object denoted by the first operand, using the other operands as arguments. |
|
The first operand must denote an EYEDB instance (object or literal) of
an agregat including the method whose name is the second operand.
The number of arguments and the type of arguments must match one of
the methods included in the class of the object denoted by the first operand. |
Note |
these two operators are identical |
Expression Examples |
expression |
result |
comments |
p->getOid() |
the value of the oid of object denoted by
p |
as getOid() is a native method of the class
object, each object can call
this method |
img->compute(1, 2.3) |
the value returned by the method call |
the first operand must denote an EYEDB instance (object or literal) of
an agregat including the method whose name is compute |
first(select Person.name = "wayne").getSpouse() |
the value returned by the method call |
|
Class Method Invocation
OQL allows one to call a class method with or without parameters.
The method can be written in C++ or in OQL.
As in C++, method calls use a combination of the scope operator and
the function call operator.
To invoke a class method, the following conditions must be realize:
- the name of the invoked method, the number and the type of arguments
must be compatible with an existing method in the class X,
- the result type must match the expected type in the expression.
The overloading and the late binding mechanisms are the same as for
the instance method invocations.
General Information |
Operator |
:: |
Syntaxe |
identifier::identifier(expr_list) |
Type |
n-ary |
Operand Types |
first operand: identifier,
second operand: identifier,
other operands: any type |
Result Type |
type of the atom returned by the method call |
Functions |
invokes the class method denoted by the second operand applied to the
class denoted by the first operand, using the other operands as arguments. |
|
The first operand must denote an EYEDB class of
an agregat including the class method whose name is the second operand.
The number of arguments and the type of arguments must match one of
the class methods included in the class denoted by the first operand. |
Expression Examples |
expression |
result |
comments |
EyeDB::getVersion() |
2.8.8 |
getVersion() is a native static method of the class EyeDB |
Person::checkName("johnny") |
the value returned by the method call |
the class method checkName must exist in the class Person
and must take one and only one input string argument. |
Eval/Unval Operators
One major feature of OQL is that one can invoke its evaluator using
the eval operator. This allows us to build OQL constructs at
runtime and perform their evaluation. This is very useful, for instance,
when we want to build a query expression where the projection or
the from reference sets are is unknown. For instance, the following
function allows us to retrieve the values of the attribute attrname
in the class classname:
function getValues(classname, attrname) {
cmd := "select x." + attrname + " from " + classname + " x";
return (eval cmd);
}
General Information |
Operator |
eval |
Syntaxe |
eval string |
Type |
unary |
Operand Types |
string |
Functions |
calls the OQL evaluator on the string operand. The string operand
can contain any OQL valid construct: an expression, a statement or a
sequence of statements. |
Expression Examples |
expression |
result |
eval "10" |
10 |
eval "a := 100" |
result is 100; the variable a
is set to 100 |
eval "a := \"hello\"; b := a + \"world\"" |
result is "hello world"; the variable a is set to "hello"; the variable b is set to "hello world" |
The unval is the inverse of the unval in the sense
that it takes any valid OQL expression and returns the string representation;
the comments and, when not necessary,
the spaces and tabulations are skipped.
For instance, the construct unval a := 10 returns
"(a:=10)".
General Information |
Operator |
unval |
Syntax |
unval expr |
Type |
unary |
Operand Types |
any type |
Functions |
returns the string expression |
Expression Examples |
expression |
result |
unval 10 |
"10" |
unval alpha += 10 - beta + 1 |
"(alpha:=(alpha+((10-beta)+1)))" |
eval unval alpha := "hello" |
returns "hello";
alpha is set to "hello" |
Set Expressions
OQL allows us to perform the following operations on sets and
bags: union, intersection,
difference and inclusion. The operands can be sets or bags.
For all these operators, when the operand's collection types are different
(bag and set), the set is first converted to a bag and
the result is a bag.
The union operator performs the union of two sets or
bags.
This operator has the same precedence as the logical or operator.
General Information |
Operator |
union |
Syntax |
expr union expr |
Type |
binary |
Operand Types |
set or bag |
Result Type |
set if both two operands are of type
set, bag otherwise |
Functions |
returns the union of the two operands. |
Expression Examples |
expression |
result |
set(1, 2) union set(2, 3) |
set(1, 2, 3) |
set(1, 2) union bag(2, 3) |
bag(1, 2, 2, 3) |
list(1, 2) union bag(2, 3) |
raises an error |
The intersect operator performs the intersection of two
sets or bags.
This operator has the same precedence as the logical and operator.
General Information |
Operator |
intersect |
Syntax |
expr intersect expr |
Type |
binary |
Operand Types |
set or bag |
Result Type |
set if both two operands are of type
set, bag otherwise |
Functions |
returns the intersection of the two operands. |
Expression Examples |
expression |
result |
set(1, 2) intersect set(2, 3) |
set(2) |
set(1, 2) intersect bag(2, 3) |
bag(2) |
bag(1, 2, 2, 3) intersect bag(2, 3, 2) |
bag(2, 2, 3) |
list(1, 2) intersect bag(2, 3) |
raises an error |
The except operator performs the difference between two
sets or bags.
This operator has the same precedence as the logical or operator.
General Information |
Operator |
except |
Syntax |
expr except expr |
Type |
binary |
Operand Types |
set or bag |
Result Type |
set if both two operands are of type
set, bag otherwise |
Functions |
returns the difference of the two operands. |
Expression Examples |
expression |
result |
set(1, 2) except set(2, 3) |
set(1) |
set(1, 2) except bag(2, 3) |
bag(1) |
set(1, 2, 10) except bag(12) |
bag(1, 2, 10) |
list(1, 2) except bag(2, 3) |
raises an error |
The inclusion operators for sets and bags are
the comparison operators less than/greater than introduced in a
previous section.
General Information |
Operator |
< |
|
<= |
|
> |
|
>= |
Syntax |
expr < expr |
|
expr <= expr |
|
expr > expr |
|
expr >= expr |
Type |
binary |
Operand Types |
set or bag |
Result Type |
boolean |
Functions |
coll1 < coll2 : returns true if and only
if coll1 is included in coll2 but not equal to coll2 |
|
coll1 > coll2 : returns true if and only
if coll2 is included in coll1 and not equal to coll1 |
|
coll2 <= coll1 : returns true if and only
if coll1 is included in coll2 or equal to coll2 |
|
coll1 >= coll2 : returns true if and only
if coll2 is included in coll1 or equal to coll1 |
Expression Examples |
expression |
result |
set(1, 2) < set(2, 3) |
false |
set(1, 2) < set(2, 3, 1) |
true |
set(1, 2) < bag(2, 3, 1) |
true |
set(1, 2) <= bag(2, 1) |
false |
set(1, 2) >= bag(2, 1) |
false |
Object Creation
OQL allows us to create persistent or transient objects using the
new operator.
The general syntax for an object creation is as follows:
[new] [<[expr]>]
class_name({path_expression : expr })
- the operator new is optionnal: when the operator is missing,
the construct is called an implicit new construct. When
the optionnal following construct ``<[expr]>'' is
not used, there is
no functionnal differences between using new or not.
- the optionnal construct after the new operator indicates
the target location of the object to create:
- if this construct is omitted, the object will be a
persistent object created in the current default database,
- if this construct is under the form <expr>,
the OQL interpreter expects for a database object handle as the result
of the expression evaluation. This database will be used as the target
location. For instance:
new < oql$db > Person();
will create a Person instance in the database pointed by
oql$db, which is in fact the current database.
- if this construct is under the form <>,
the object will be a transient object.
- the class_name indicates the name of a valid user
type in the context of the current database.
- the path_expression indicates an attribute name or
a sequence of attributes using the optional array operator, for instance
the following path expressions are valid for an object construction:
name
lastname
addr.street
addr.town[3]
spouse.name
- the expr behind path_expression is any OQL expression
as soon as its result type matches the expected type of the path_expression.
- the order of evaluation of the expressions is in the
{path_expression : expr} sequence is from left
to right.
- the expression returns the oid of the created object.
For instance:
- new Person() creates a person with all its attributes
unitialized,
- Person() creates a person with all its attributes
unitialized,
- new Person(name : "john") creates a person with its
attribute name initialized to john,
- Person(name : "john") creates a person with its
attribute name initialized to john,
- new Person(name : "john", age : 32, spouse : new Person(name :
"mary")) creates a person named mary and a person named
john, age 32 whose spouse is the person
mary.
The new operator can also be used to create basic type
object. Note that in this case, the operator is mandatory.
The syntax for basic type creation is as follows:
new [<[expr]>] basic_type
(value).
- where basic_type denotes an ODL basic type.
It may be one of the following type:
int32, int16, int32,
char, byte, float or oid. Note
that the type string is not allowed here.
- the value must be an atomic value of an OQL type mapped
from the ODL basic type
For instance:
- new int(2)
- new float(2.3)
- new float(2)
- new char('a')
- new oid(first(select Person))
Finally, the new operator can be also used to create collections.
The syntax for collection creation is as follows:
[new] [<[expr]>] coll_type<
class_name [, coll_name]> ([collection of elements])
- the new operator is optionnal,
- where coll_type denotes type of the collection:
set, bag, array or list,
- the class_name denotes the name of the class
of the elements of the collection, for instance Person*,
Car*,
- coll_name is an optionnal string which denotes the name
of the collection to create,
- the optionnal collection of elements within parenthesis
contains the elements (generally oids) to insert initially in
the created collection,
- the expression returns the oid of the created collection.
For instance:
- new set<Person *>() creates an empty set of persons,
- new set<Person *>(list(select Person)) creates a set containing
all the persons in the database,
- new set<Person *, "all babies">(list(select Person.age < 1)) creates a set named all babies containing
all the persons whose age is less than 1.
- new array<Car *>() creates an empty array of cars.
- new array<int>(list(1, 2, 3, 4) creates an array
of integers initially containing 1, 2, 3, 4.
- new set<int *> (list(new int(2), new int(10))) creates
a set of integer objects containing initially two
integer objects.
General Information |
Operator |
new |
Syntax |
new [<[expr]>]
class_name({path_expression : expr }) |
Type |
n-ary |
Operand Types |
any type |
Result Type |
oid or object |
Functions |
creates an persistent or transient object |
Expression Examples |
expression |
result |
john := new Person(name: "john",
lastname: "wayne",
age : fib(10));
|
returns the oid of the created Person instance |
new Person(name: "mary",
lastname: "poppins",
addr.town : "jungle",
addr.street[0] : 'a',
addr.street[1] : 'b',
spouse : john,
spouse.age : 72
);
|
returns the oid of the created Person instance |
Object Deletion
The delete unary operator is used to delete persistent
objects.
General Information |
Operator |
delete |
Syntax |
delete expr |
Type |
unary |
Operand Types |
oid or object |
Result Type |
the operand type |
Functions |
delete a transient or persistent object |
Expression Examples |
expression |
result |
delete first(select Person) |
the oid of the deleted Person instance |
delete new Person() |
the oid of the deleted Person instance |
for (x in (select Person) delete x |
the oids of the deleted Person instances |
Collection Expressions
OQL introduces a few operators for object collection manipulation:
one of them is the array deferencing operator ``[]''
(5.15) that
is overloaded for ordered collection manipulation.
Some them are ODMG OQL compliant, the others are EYEDB extensions.
Object collections may be persistent or transient, orderered or not.
These operators allows us to make the following kind of operations:
- gets the contents of a collection: operator contents,
- get an element at a given position in an ordered collection:
operator [] (ODMG compliant),
- get elements at some given positions in an ordered collection:
operators [:] (ODMG compliant) and [?],
- checks if an element is in a collection: operator in (ODMG
compliant),
- add an element in an unordered collection:
operator add/to
- suppress an element from an unordered collection:
operator suppress/from,
- set or suppress an element in an ordered at a given position:
operator [],
- set or suppress elements in an ordered at a given position:
operators [:] and [?],
- append an element in an ordered collection:
operator append/to,
- checks if a given condition is realized for at least one
element in a collection: operator in (ODMG compliant),
- checks if a given condition is realized for all
elements in a collection: operator for/all (ODMG compliant),
- checks if a given condition is realized for a given
number range of elements in a collection: operator extended for.
In all the following examples, the OQL variables p0 denotes
the first Person instance in the database:
p0 := first(select Person).
Important Note: although they are reference in the following
descriptions of collection operators,
the object collections list are not
implemented in the current version of EYEDB.
The contents unary operator is used to give the contents of
a given ordered or unordered object collection. It returned an OQL
collection of the same type of the object collection: set,
bag, array or list.
General Information |
Operator |
contents |
Syntax |
contents expr |
Type |
unary |
Operand Types |
oid or object collection |
Result Type |
a collection of objects |
Functions |
returns the contents of an object collection |
Expression Examples |
contents(p0.children) |
an array of Person oids |
select contents(x.children) from Person x |
returns a list of arrays of Person oids. |
contents(list(1, 2, 3)) |
raises an error:
oid or object expected, got list |
The in operator is used to check if a given element is
in ordered or unordered object collection.
General Information |
Operator |
in |
Syntax |
expr in expr |
Type |
binary |
Operand Types |
first operand: any type,
second operand: oid or object collection |
Result Type |
boolean |
Functions |
returns true if the first operand belongs to the collection
pointed by the second operand; false otherwise |
Expression Examples |
first(select Person.name = NULL) in p0.children |
returns true if the first Person instance
whose name is unitialized
is in the array of children of the first Person |
first(select Car.brand = "renault") in p0.cars |
returns true if the first Car instance whose
brand equals renault
is in the set of cars of the first Person |
The add/to operator is used to add an element in an
unordered collection (set or bag).
General Information |
Operator |
add/to |
Syntaxes |
add expr to expr |
Type |
binary |
Operand Types |
first operand: any type,
second operand: oid or object unorderered collection
(set or bag) |
Result Type |
type of the first operand |
Functions |
adds the first operand to the non-indexed collection (i.e. bag or set)
pointed by the second operand; returns the first operand. |
Expression Examples |
add new Car(num : 100) to
p0.cars |
returns the created Car oid |
add new Person(name : "john") to
p0.children |
raises an error: cannot used non indexed insertion in an array |
add new Car() to new set<Car *>() |
returns the just created car; but we have lost the
oid of the just created set of cars! |
add new Person() to (c := new bag<Person *>()) |
returns the just created person; the created bag has been kept in
the OQL variable c |
The polymorphic [] operator is used to set or get an element in an
ordered collection (array or list) at a given position:
it can be used in a right or left value.
General Information |
Operator |
[] |
Syntaxes |
expr [ expr ] |
Type |
binary |
Operand Types |
first operand: collection array or
list, second operand: integer |
Result Type |
the type of the element, |
Functions |
gets the element in the collection pointed by the first operand
at the position pointed by the second operarand. If used at a left
value, gets a reference to that element. |
Expression Examples |
p0.children[0] |
returns the child at position #0 in p0.children collection.
Returns nil if there is no child at this position |
p0.children[0] := Person(name : "john") |
returns the created Person oid |
p0.children[12039] := Person(name : "henry") |
returns the created Person oid. This expression is valid in
any cas as the collection arrays automatically increased its
size |
(array<Person *>())[0] := new Car(num : 100) |
returns the just created person; but the created array has been ``lost''
as it is not tied to any instance and as we did not bind it to any
OQL variable |
(c := array<Person *>())[0] := new Car(num : 100) |
returns the just created person; the created array has been kept in
the OQL variable c |
p0.cars[1] := Car(num : 100) |
raises an error: array expected, got set |
The polymorphic [:] operator is used to set or get some elements in an
ordered collection (array or list) at some given positions:
it can be used in a right or left value.
When used in a right value, the returned atom is a set of
struct with the two attributes index and value.
In each struct element returned, the value of index is
the position of the element, the value of value is the element
value. Note the returned struct elements are not ordered according
to the element postions; it is why a set is returned.
When used as a left value, the returned atom is a set of
references on the elements .
General Information |
Operator |
[:] |
Syntaxes |
expr [ expr : expr ] |
Type |
ternary |
Operand Types |
first operand: collection array or
list, second and third operands: integer |
Result Type |
the type of the element, |
Functions |
gets the elements in the collection pointed by the first operand
at the position range pointed by the second and third operarands.
If used at a left value, gets references to that elements. |
Expression Examples |
p0.children[0:1] |
returns a set of struct including the children and the position
of the children position #0 and #1 in the p0.children collection.
For instance: set(struct(index : 0, value : 3874.33.293847:oid),
struct(index : 1, value : 2938.33.1928394:oid)) |
Returns nil if there is no child at these positions |
|
p0.children[0:4] := Person(name : "john") |
Sets all the children at the position #0 to #4 to a new Person instance. |
returns the created Person oid |
|
p0.children[12000:12039] := Person(name : "henry") |
returns the created Person oid. This expression is valid in
any cas as the collection arrays automatically increased its
size |
(array<Person *>(list(Person())))[0] |
returns the just created person within the just created array.
But the array is ``lost''
as it is not tied to any instance and as we did not bind it to any
OQL variable |
(x := array<Person *>(list(Person())))[0] |
returns the just created person; the created array has been kept in
the OQL variable c |
The polymorphic [?] operator is used to set or get all the
elements in an ordered collection (array or list).
It can be used in a right or left value.
When used in a right value, the returned atom is a set of
struct with the two attributes index and value.
In each struct element returned, the value of index is
the position of the element, the value of value is the element
value. Note the returned struct elements are not ordered according
to the element postions; it is why a set is returned.
When used as a left value, the returned atom is a set of
references on the elements .
General Information |
Operator |
[?] |
Syntaxes |
expr [?] |
Type |
unary |
Operand Type |
collection array or
list, |
Result Type |
a set of struct or a set of references |
Functions |
gets all the elements in the collection pointed by the first operand
If used at a left value, gets references to that elements. |
Expression Examples |
p0.children[?] |
returns a set of struct including the children and the position
of all the children in the p0.children collection.
For instance: set(struct(index : 0, value : 3874.33.293847:oid),
struct(index : 1, value : 2938.33.1928394:oid)) |
Returns nil if the collection is empty |
|
p0.children[?] := Person(name : "john") |
Sets all the children to a new Person instance. |
returns the created Person oid |
|
(array<Person *>(list(Person(), Person())))[?] |
returns a set of struct including
the just created Person instances in the just created array. |
The append/to operator is used to append an element
to an ordered collection (list or array).
General Information |
Operator |
append |
Syntaxes |
append expr to expr |
Type |
binary |
Operand Types |
first operand: any type,
second operand: oid or object denoting an ordered collection |
Result Type |
any type |
Functions |
appends the element denoted by the first operand
to the indexed collection (i.e. list or array)
denoted by the second operand. |
Expression Examples |
append Person() to p0.children |
the created Person instance |
append Car()to p0.cars |
raises an error: array or list expected, got set<Person*> |
The suppress/from operator is used to suppress an element from
an ordered collection (set or bag).
General Information |
Operator |
suppress/from |
Syntaxes |
suppress expr from expr |
Type |
binary |
Operand Types |
first operand: any type, second operand:
oid or object collection |
Result Type |
type of the first operand |
Functions |
suppress the first operand from the non-indexed collection (i.e. bag or set)
pointed by the second operand; returns the first operand. |
Expression Examples |
suppress (select Car.num = 1000) from p0.cars |
the suppressed car if it was found in the collection; otherwise, raises an error |
suppress new Car() from p.cars |
raises an error: item '71238.13.3959935:oid' not found in collection |
suppress p0 from p0.children |
raises an error: cannot used non indexed suppression in an array |
The empty operator is used to empty an ordered or an
unordered collection.
General Information |
Operator |
empty |
Syntax |
empty expr |
Type |
unary |
Operand Types |
oid or object collection |
Result Type |
nil |
Functions |
empty the collection
pointed by the operand |
Expression Examples |
empty(first (select Person).children) |
nil |
empty(first (select Person).cars) |
nil |
empty new set<Car *>(list(new Car())) |
nil; this expression creates a collection of Car
containing initially a new Car, and empty it! |
The in operator is used to check if a given condition is
realized for at least one element in an ordered or unordered collection.
General Information |
Operator |
in |
Syntax |
identifier in expr : expr |
Type |
ternary |
Operand Types |
first operand: identifier, second operand:
oid or object collection,
third operand: boolean |
Result Type |
boolean |
Functions |
returns true if it exists in the collection pointed by
the second operand an element for which the third operand is
evaluated to true. |
Expression Examples |
x in p0.children: x.name = "mary" |
true or false |
x in p0.cars: x.num < 100 and
x.num >= 90 |
true or false |
The for/all operator is used to check
if a given condition is realized for all
elements in an ordered or unordered collection.
This operator is ODMG compliant.
General Information |
Operator |
for/all |
Syntaxes |
for all identifier in expr : expr |
Type |
ternary |
Operand Types |
first operand: identifier, second operand:
oid or object collection,
third operand: boolean |
Result Type |
boolean |
Functions |
returns true if for all items contained in the collection pointed by
the second operand the third operand is evaluated to true. |
Expression Examples |
for all x in p0.children: x.name == "john" |
true or false |
for all x in p0.cars: x.num % 10 |
true or false |
The for/cardinality operator is used to check if
a given condition is realized for a given
number range of elements in an orderer or unordered collection.
Note that this operator is the generalisation of the in
and for/all operators:
for <0:$> is equivalent to in
for <$> is equivalent to for all
General Information |
Operator |
forcardinality |
Syntaxes |
for < expr : expr > identifier
in expr : expr |
|
for < expr > identifier
in expr : expr |
Type |
5-ary |
Operand Types |
first and optional second operands: integer or $,
where $ denotes the collection cardinality,
third operand: oid or object,
fourth operand: identifier,
fifth operand: boolean |
Result Type |
boolean |
Functions |
returns true if the number of items in the collection pointed by
the third operand for which the third operand is evaluated to true
is in the interval [first operand, second operand]. |
Expression Examples |
for <0:4> x in p0.children: x.name == "john" |
true if at most 4 children have their name equals to john |
for <4> x in p0.children: x.name == "john" |
true if one an only one children have its name equals to john |
for <0:$> x in p0.cars: x.num = 10 |
equivalent to in |
for <$> x in p0.cars: x.num = 10 |
equivalent to for/all |
Exception Expressions
Currently, EYEDB OQL does not provide full support for exception
management as there is no try/catch operator.
Nevertheless, the throw operator allows us to raise
an error message, for instance:
if (!check(p))
throw "variable p is not correct".
The throw operator stops the current thread of statements
and returns the error message at the uppest level. In the following
code:
a := 1;
throw "this is an error";
b := 2;
the variable a will be assigned to 1, but the
variable b will not be assigned to 2 as the
throw expression deroutes the normal thread of statements.
The throw operator is often used in the body of functions,
General Information |
Operator |
throw |
Syntax |
throw expr |
Type |
unary |
Operand Type |
string |
Result Type |
nil |
Functions |
raises an error message |
Expression Examples |
throw "error message" |
nil |
throw "error #1: " + msg |
nil |
Function Definition Expressions
As introduced previously, OQL supports functions. There are two types of
functions definition syntax:
function definition expression and function definition statements.
The first ones, exposed in this section, are more restrictive than the
second ones, as their definition can contain only one expression.
The second ones contain an unbounded sequence of statements.
The function definition expressions are ODMG compatible and are called
Named Query Definition in the ODMG standard.
To define such a function, one must use the operator define/as.
The general syntax for a definition function expression is:
define identifier [(arglist)] as expr.
For instance:
define div2(x) as x/2;
div2(10); // returns 5
define pimul(x) as x * 3.1415926535;
pimul(23); // returns 72.256631
define getOnePerson(name) as first(select Person.name = name);
getOnePerson("john"); // returns an oid or nil
As the last operand of the define/as operator is any OQL
expression, it can be a sequence of expressions by using the
comma sequencing operator. Therefore, the following construct is valid:
define getit(age) as ::getit_call++,
list_of_persons := select Person.age >= age,
(count(list_of_persons) > 0 ? list_of_persons[0] : nil);
getit(10); // returns the first Person whose age is greater or equal to
10, or nil
getit_call; // equals 1
list_of_persons; // raises an error: uninitialized identifier 'list_of_persons
getit(20);
getit_call; // equals 2
Several comments about this code:
- ::getit_call denotes a global variable, while
list_of_persons denotes a variable local to the function:
the variable scoping has previously been introduced in the identifier
expressions, and will be explained again in the function definition statements
Section.
- as there are no iteration expression (for instance a for or
while expression) and as a function definition expression can
contain only one expression, one cannot use a function definition expression
with iteration statements.
To use an iteration statement, one needs to use a function definition
statement.
- when one needs to define a function with a sequence
of expressions, it may be easier and more readable to use a
function definition statement instead of a function definition expression.
The statement version of the previous function is:
function getit(age) {
::getit_call++;
list_of_persons := select Person.age >= age;
if (count(list_of_persons))
return list_of_persons[0];
}
which is - for C, C++ or Java programmers -
a more natural construct.
Finally, the function definition allows us for recursive definitions,
for instance:
define fib(n) as (n < 2 ? n : fib(n-2) + fib(n-1));
fib(10); // returns 55
General Information |
Operator |
define/as |
Syntax |
define identifier [arglist] as expr |
Type |
n-ary |
Operand Type |
first operand: identifier,
last operand: any type, other operands: identifier |
Result Type |
identifier |
Functions |
defines a function |
Expression Examples |
define Persons as select Person |
Persons |
define fact(n) as (n < 2 ? n : n * fact(n-1)) |
fact |
Conversion Expressions
The conversion unary operators string,
int, char, float, oid and
ident allows us to make the following conversions:
Operator |
From |
To |
Returned Atom |
string |
any type |
string |
the string representation
of the operand |
int |
int |
int |
the int operand |
|
char |
int |
the operand casted to an int |
|
float |
int |
the operand casted to an int |
|
string |
int |
the operand converted to an int |
char |
char |
char |
the char operand |
|
int |
char |
the operand casted to a char |
|
float |
textttchar |
the operand casted to a char |
|
string |
char |
the operand converted to a char |
float |
float |
float |
the float operand |
|
char |
float |
the operand casted to a float |
|
int |
float |
the operand casted to a float |
|
string |
float |
the operand converted to a float |
oid |
oid |
oid |
the oid operand |
|
string |
oid |
the string operand converted to an oid |
ident |
ident |
ident |
the ident operand |
|
string |
ident |
the string operand converted to an ident |
These operators are used to perform an explicit conversion such
as convert the string "123" to the integer 123,
or to perform an explicit cast for numbers such as
casting the integer 10 to the float 10.0.
These operators evaluate first their operand before
performing the conversion.
If the operand type is valid, no error is raised even if its format
is not valid, for instance: int "alpha" returns 0,
while oid "aoaoai" returns NULL.
Note that because of the precedence of these operators, parenthesis
are necessary to make a conversion of a non-primary operand. For
instance, string 1+2 is not valid: you should use
string (1+2).
The string operator evaluates its operand and returns its string representation.
General Information |
Operator |
string |
Syntax |
string expr |
Type |
unary |
Operand Type |
any type |
Result Type |
string |
Function |
returns the string representation of any atom |
Expression Examples |
string 123.3 |
"1203.300000" |
string 'a' |
"a" |
string first(select Person) |
"71211.13.1486847:oid" |
string &alpha |
"::alpha" |
string list(1, 2, 3+2) |
"list(1, 2, 5)" |
string (list("hello", 30) + list(10)) |
"list("hello", 30, 10)" |
string (1+3) |
"4" |
string 1+3 |
raises an error |
The int operator evaluates its operand and converts or
casts it to an integer.
If the operand is the string, it converts it using the atoi C
function. If the string is not a valid integer, it returns a 0.
If the operand is a char or float, it casts it to an integer.
If the operand is an integer, it returns it.
General Information |
Operator |
int |
Syntax |
int expr |
Type |
unary |
Operand Type |
int, char, float or
string |
Result Type |
int |
Function |
returns the integer conversion or cast of the operand |
Expression Examples |
int 123.3 |
123 |
int 12 |
12 |
int 'a' |
97 |
int "123" |
123 |
int ("123" + "12") |
12312 |
int alpha |
the value of alpha converted
or casted to an integer |
int list(1, 2, 3) |
raises an error |
The char operator evaluates its operand and converts or
casts it to a char.
If the operand is the string of length one, it returns the character of
this string. If the string has several characters, it returns a
'\000.
If the operand is a integer or float, it casts it to a character.
If the operand is a character integer, it returns it.
General Information |
Operator |
char |
Syntax |
char expr |
Type |
unary |
Operand Type |
int, char, float or
string |
Result Type |
char |
Function |
returns the character conversion or cast of the operand |
Expression Examples |
char 123.3 |
{ |
char 'a' |
'a' |
char alpha |
the value of alpha converted
or casted to a character |
char "a" |
'a' |
char "hello" |
'^@' |
char list(1, 2, 3) |
raises an error |
The float operator evaluates its operand and converts or
casts it to a float.
If the operand is the string, it converts it using the atof C
function. If the string is not a valid float, it returns a 0.0.
If the operand is a integer or float, it casts it to a float.
If the operand is a float, it returns it.
General Information |
Operator |
float |
Syntax |
float expr |
Type |
unary |
Operand Type |
int, char, float or
string |
Result Type |
float |
Function |
returns the float conversion or cast of the operand |
Expression Examples |
float 123.0 |
123.0 |
float 123.3 |
123.3 |
float 'a' |
97.000 |
float "123.0000000" |
123.0 |
float ("123." + "12") |
123.12 |
float "hello" |
0.0 |
float alpha |
the value of alpha converted
or casted to a float |
float list(1, 2, 3) |
raises an error |
The oid operator evaluates its string operand and returns
the corresponding oid. If the string does not denote a valid oid,
the NULL oid is returned.
If the operand is an oid, it returns it.
General Information |
Operator |
oid |
Syntax |
oid expr |
Type |
unary |
Operand Type |
oid or string |
Result Type |
oid |
Function |
returns the oid denoted by the string operand |
Expression Examples |
oid "234.34.33:oid" |
234.34.33:oid |
oid 234.34.33:oid |
234.34.33:oid |
oid first(select Person) |
returns the first person oid |
oid 'a' |
raises an error |
The ident operator evaluates its string operand and returns
the corresponding identifier.
If the operand is an identifier, it returns it.
General Information |
Operator |
ident |
Syntax |
ident expr |
Type |
unary |
Operand Type |
ident or string |
Result Type |
string |
Function |
returns the identifier denoted by the string operand |
Expression Examples |
ident "alpha" |
alpha |
ident "alpha#1x" |
alpha#1x |
ident "alpha" := 123 |
123, alpha has been
assigned to 123 |
valof &(ident "alpha") |
123 |
ident 'a' |
raises an error |
Type Information Expressions
OQL provides two type information unary operators: typeof and
classof. The first one takes any operand type, while the
second one takes an oid or an object operand.
Note that because of the precedence of these operators, parenthesis
are necessary to get type information about a non-primary operand. For
instance, typeof 1+2 is not valid: you should use
typeof (1+2).
The typeof operator is used to get the type of any OQL atom.
It evaluates its operand and returns the string type of its operand.
For instance: typeof 1 returns "int" while
typeof "hello" returns "string".
General Information |
Operator |
typeof |
Syntax |
typeof expr |
Type |
unary |
Operand Type |
any type |
Result Type |
string |
Function |
returns the type of the atom |
Expression Examples |
typeof "alpha" |
"string" |
typeof (1+20.) |
"float" |
typeof list(1, 2, 3) |
"list" |
typeof first(select Person) |
"oid" |
typeof 1+3049 |
raises an error |
typeof alpha |
type of the value of alpha |
typeof &alpha |
ident |
The classof operator is used to get the class of any oid
or object.
It evaluates its operand and returns the string class of its operand.
For instance: classof first(select Person) returns "Person" while
typeof new Car() returns "Car".
General Information |
Operator |
classof |
Syntax |
classof expr |
Type |
unary |
Operand Type |
oid or object |
Result Type |
string |
Function |
returns the class of the operand |
Expression Examples |
classof first(select class) |
"basic_class" |
classof (c := new Car(num : 10)) |
"Car" |
classof NULL |
"" |
classof first(select Person).spouse |
"Person" or NULL |
classof 1 |
raises an error |
Query Expressions
The select operator is used to perform queries in a database.
The general syntax of this operator is as follows:
select [distinct] projection [from fromList
[where predicat] [order by orderExprList
[asc|desc]]
- distinct means that duplicates must be eliminated,
- projection is an expression using the variables
defined in the fromList,
- fromList is a sequence of comma-separated items
under one of the following forms:
var in expr
expr as var
expr var
where expr is an expression of type collection or
is a class name, and var is the name of a variable.
- predicat is a boolean expression using the variables
defined in the fromList,
- orderExprList is a comma-separated list of sortable
expressions (i.e. atomic type).
- asc means that the order should be performed in an ascendant
way (the default) and desc means the inverse.
As explained in the Section OQL vs. ODMG 3 OQL,
there are a few differences between ODMG OQL and EYEDB OQL query expressions:
- the having/group clause is not
supported in the current implementation.
- in the current implementation,
one cannot use implicit from clause (i.e. from
clause without variables). ODMG OQL supports constructs such as:
from Person without any variable. This implementation does not.
- contrary to ODMG OQL, the from clause is optionnal,
A select expression which does not use the from is
called an implicit select expression.
- in a from clause such as ``x in expr'', ``expr'' can
be the name of a class. In this case, the interpreter understand
this as the extent of this class. ODMG OQL does not support that.
- the SQL specific agregate operators min(*),
max(*), count(*), sum(*) and avg(*)
are not supported
- the order by clause is more restrictive than
in the ODMG OQL specifications (see below).
- the select * is not supported in the current implementation.
Rather than introducing the select operator in a formal way by
using a lot mathematical symbols, we introduce it first, in an unformal way,
and then, through query examples.
The unformal description of the query process is as follows:
- The from clause
determine the sets of objects on which the query will be applied.
For instance, in the from clause,
``x in Person, y in x.children'', the sets on which
the query will be applied are
all the Person instances bound to the variable x
and the children of these instances, bound to the variable
y.
- These sets of objects are filtered by retaining only the objects
that satisfy the predicat in the where clause. These result objects
are gathered into a bag. If no where clause is there, all objects are
retained.
- If an order by clause is present, a sort is performed
using to the following process:
- each order expression must be of a sortable type: number (int,
char or float) or string. If not, an
error is raised.
- the bag is ordered into a list according to
the first order expression. Then identical atoms are ordered again using
the second order expression and so on.
- there is a restriction in the current implementation:
each expression in the orderExprList must be present
in the projection expression. If not present, an
error is raised.
- The projection expression is evaluated for each
object in the collection and the results of these evaluations are gathered
into a bag.
- If the keyword distinct is there, the eventual duplicates are
eliminated.
- Finally, if the order by clause is present, the
result bag is converted to a list;
if the distinct keyword is there without an order by,
the bag is converted to a set; and if neither
order by nor distinct are used, we get a bag.
The following table presents several examples:
Simple select/from Examples
|
select x from Person x
returns a bag containing
all the Person oids in the database
select x.name from Person x
returns a bag
containing the name of every Person instance in the database
select struct(name: x.name, age: x.age) from Person x
returns a bag
containing struct elements including the name and age of every Person instance in the database
select list(x, x.name) from Person x where x.name ~ "h"
returns a bag of list elements
containing the oid and the name of every Person instances
whose name matches the regular expression "h"
select list(x, x.name) from Person x where x.name ~ "h" order by x.name
same as previous example, but the result is a list ordered
by the name of the persons
select x from Person x where x.spouse.name = "john" or x.age < 10
returns
a bag of Person instances whose spouse name is equal to "john" or
the age is less than 10
select x from Person x order by x.name
current implementation restriction: raises an error: x.name not found in projection
|
Arrays and collections in query expressions
OQL provide supports for performing direct queries through non-collection array and
collection attributes without explicit joins.
To perform such queries, one must use the operators [],
[?] or [:].
All the operators may be used for non-collection arrays. For
collections (list, set, bag and array),
only the operator [?] is valid.
The operator [] denotes an element in a non-collection array.
The operator [:] denotes a range of elements in a non-collection array.
The operator [?] denotes all elements in a non-collection array
or in a collection.
The following table presents several examples:
Array and Collection based Query Examples
|
select x.name from Person x where x.name[0] = 'j'
returns all
the Person instances whose name begins with a 'j'
select x from Person x where x.other_addrs[0].street ~~ "par."
returns all the Person instances whose first other_addrs
street matches the regular expression "par."
select x from Person x where x.other_addrs[?].street ~~ "par.."
returns all the Person instances whose any other_addrs
street matches the regular expression "par.."
select x from Person x where x.other_addrs[1:3].street ~~ "par.."
returns all the Person instances whose the first, second
or third other_addrs
street matches the regular expression "par.."
select x from Person x where x.children[?].name = "johnny"
returns
all the persons whose one of its children is called "johnny"
select x from Person x where x.cars[?].num < 100 or x.children[?].name = "mary"
returns all the persons whose one of its cars has a number less
than 100 or a child called "mary"
select x from Person x where x.children[1].name = "johnny"
although the children is a collection array, an error is raised.
This is a current limitation of the implementation that will disapear
soon
|
An implicit select expression is a select expression
with neither a from nor an explicit where clause.
In fact, the where clause may be included in the projection
expression.
This particular syntax has the advantage
to be more compact and more simple than the general syntax, but
some queries cannot be performed:
- queries performing an explicit join,
- queries having or or and in their where
clause.
The following table presents several examples:
Simple Implicit select Examples
|
select 1
returns 1
select Person
returns a bag containing all Person
instances in the database
select Person.name
returns a bag
containing the name of every Person instances in the database
select Person.name = "john"
returns a bag
containing the oids of every Person instances
whose name is equal to "john"
(select distinct Person.name = "john").age
returns a set
containing the age of every Person instances
whose name is equal to "john"
select Person.name = "john" or Person.age = 10
raises an error: use select/from/where clause
select Person.name order by Person.name
returns a list containing of the sorted names of all Person
instances
|
Querying the schema
As every abstraction is an object, one can perform queries on the schema
and classes. For instance, to get the oids of all the
existing classes in a schema:
select schema which is equivalent to select class.
In the EYEDB object model, the class class has the following
native attributes (some are inherited from the object class):
- class (inherited from object) is the class
of this class,
- protection (inherited from object) is the
protection object of this class,
- type is the type of the class: "system" or
"user".
- name is the name of this class,
- parent is the parent class of this class,
- extent is the extent collection of this class: contains
all the object instances of this class,
- components is the collection of components of this class:
contains the constraints, the methods, the triggers, the index.
Queries can be performed according to one or more of the previous
attributes.
Query Schema Examples
|
select class.name = "Person"
returns a bag containing the class whose name is "Person"
select class.type = "user"
returns all the user classes
select x from class x where x.name ~ "P" and
x.type = "user"
returns the user classes whose name
matches the given regular expression
select class.parent.name = "Person"
returns a bag containing the sub-classes of the class Person
|
How queries are optimized?
EYEDB queries implementation make an heavy use of index.
Index may be used as terminal index or as non-terminal index:
terminal index are those used at the end of a path expression, and
the other are those used inside a path expression.
For instance, assuming that each attribute in our schema is indexed,
the query select Person.name = "john" uses
the index on the attribute name as
a terminal index.
The query select Person.spouse.age < 23 uses
the index on the attribute age as a terminal
index, and the index on the indirect attribute spouse
as a non-terminal index, while the query select Person.spouse
= 6252.3.48474:oid uses the index on the spouse attribute as a
terminal index.
The query select Person.children[?].spouse.name = "mary"
uses the index of the attributes name, spouse
and of the literal collection attribute children.
The queries with a where clause containing logical and
constructs are optimized so to take advantage of the index. For instance,
assuming that there is an index on the name attribute and no
index on the age attribute, the query
select x from Person x where x.age = 100 and x.name = "john"
will perform first the query on the name attribute and then
filter the result according to the given predicat on the age
attribute.
If the two expressions around the logical and operator
have an index or if none of the two expression has a index,
the interpreter performs first the left part and then the second part.
So, in this case, the order of the two expressions around the and
is important.
Finally, the query select Person.name reads directly the index
of the name attribute, instead of reading the name of each
Person instance.
Nevertheless, currently, there are some constructs that the OQL interpreter
does not interpret cleverly and where index are not used.
This is unfortanely the case of the join constructs such as:
select x from Person x, x.spouse y where y.name = "mary".
This construct will not make use of the index on the spouse attribute
(but it will use the index of the name attribute).
Fortunately, in a lot of cases, join queries may be replaced by a path expression query, for
instance:
select x from Person x where x.spouse.name = "mary" is the
alternate form the previous join query.
This alternate form (path-expression oriented) is the preferred one, because:
- it is more intuitive,
- it is more object oriented (please forget relationnal!),
- it is more compact,
- it uses index properly.
Of course, the last reason is not a good reason as a proper query
implementation should use index whatever the used syntax.
The implementation will be improved in a next version.
Note that query optimisations are fragile and do not believe that
the OQL interpreter knows your data better than you. So, the two following
simple rules should be applied:
- when path expression oriented queries are enough, do not use
join constructs,
- in a and expression within a where clause, put
the expression which denotes the most little set of instances on
the left side of the and. In some particular cases, the
OQL interpreter knows how to optimize the and, but in
most of the cases, it does not.
Some experimental hints can be added to an and expression
in the where clause to help the interpreter to do the
things better, but there are currentlty too much experimental to
be documented here.
Miscellenaous Expressions
A few OQL operators cannot be easily classified in one of the previous
categories. We choose to classify them in the miscellenaous operators.
These operators are: bodyof, structof, [!] and
import.
The bodyof operaror is used to get the body of an OQL function.
For instance, let the function fib:
define fib(n) as (n < 2 ? n : fib(n-2) + fib(n-1)).
The expression bodyof fib will return:
"fib(n) ((n<2)?n:(fib((n-2))+fib((n-1))))".
This operator could be applied to expression-functions (i.e. functions
defined with the define/as operator) or statement-functions (i.e.
functions defined with the function operator).
General Information |
Operator |
bodyof |
Syntax |
classof expr |
Type |
unary |
Operand Type |
ident denoting a function |
Result Type |
string |
Function |
returns the body of the function |
Expression Examples |
bodyof is_int |
"is_int(x) ((typeof x)=="integer")" |
bodyof first |
"first(l) { if (((!is_list(l))&&(!is_array(l)))) return l; start:=0; f:=nil; for (x in l) if ((start==0)) { start:=1; f:=x; break; }; ; return f; }" |
bodyof 1 |
raises an error |
The structof is used to get the meta-type of a struct atom.
The meta-type of a struct atom is the list of the attributes
of this struct. For instance, the meta-type of
struct(a : 1, b : "hello") is the list composed of the two
attribute a and b. The following expression
structof struct(a : 1, b : "hello") returns
list("a", "b").
General Information |
Operator |
structof |
Syntax |
structof expr |
Type |
unary |
Operand Type |
struct |
Result Type |
a list of strings |
Function |
returns the meta-type of the operand |
Expression Examples |
structof struct(alpha : 1, beta : 2) |
list("alpha", "beta") |
structof first(select struct(x: x.firstname) from Person x) |
list("x") |
structof 1 |
raises an error |
The [!] is used to get the length (or size) of an ordered
or unordered collection, a string or a struct.
For instance, "hello"[!] returns 5,
while list(1, 2, 3)[!] returns 3.
Note that this operator is far more efficient than the strlen
library function.
General Information |
Operator |
[!] |
Syntax |
expr [!] |
Type |
unary |
Operand Type |
string, collection or
struct |
Result Type |
a int |
Function |
returns length of the operand |
Expression Examples |
(select Person)[!] |
the number of person instances |
(struct(a: 1, b:2, c: "hello"))[!] |
3 |
("hello"+"world")[!] |
10 |
"hello"+"world"[!] |
raises an error |
The import operator is used to import an OQL file in the
current OQL session. Its operand is a string which denote the absolute
path (i.e. beginning with a /) or relative path
(i.e. not beginning with a /)
of the file to import. When the path is relative, the OQL interpreter
will look in every directories pointed by the EYEDB configuration variable
oqlpath. By default, oqlpath is equal to
%root%/etc/oql. If the file name has no .oql extension,
the OQL interpreter will automatically adds one.
General Information |
Operator |
import |
Syntax |
import expr |
Type |
unary |
Operand Type |
string |
Result Type |
a string |
Function |
import the file |
Expression Examples |
import "stdlib" |
"/usr/local/eyedb/etc/so/stdlib.oql" |
import "roudoudou" |
raises an error: cannot find file 'roudoudou' |
Selection Statements
The selection statement is based on the if/else constructs.
Syntax: if ( cond_expr ) statement1 [else statement2]
where cond_expr is a boolean expression, and statement1
and statement2 may be any statement: an expression
statement, an iteration statement, a compound statement, a selection statement,
a function definition statement, a jump statement or an empty statement.
Semantics: if the boolean expression cond_expr is
evaluated to true, the statement statement1 is executed.
Otherwise, if an else part is there, the statement statement2
is executed. The statements 1 and 2 may be any statement: an expression
statement, iteration statement, compound statement, selection statement,
function definition statement, jump statement or empty statement.
Note that a selection statement does not return any atom
The following table presents several examples of if/else statements:
if/else Statement Examples
|
if (true) a := 1;
the variable a is assigned to 1
if (1) b := 2;
an error is raised: boolean expected for condition
if (check(10) > 54) {a := 1; b := 2;} else {c := 2; d := 2}
here compound statements are used, because several expression statements
need to be executed
if ((check(10) > 54 || alpha < 2) && beta > 2.3 ) {callme(2);}
use of a composite conditional expression
if (a == 1) b := 2; else if (b == 3) {c := 2; return 4;} else if (check(1)) return 2; else return 3;
selection statements are combined
|
Iteration Statements
The iteration statements are based on the following constructs:
- while
- do/while
- for C, C++, Java form
- for collection form
Syntax: while ( cond_expr ) statement
where cond_expr is a boolean expression, and statement
any statement: an expression
statement, an iteration statement, a compound statement, a selection statement,
a function definition statement, a jump statement or an empty statement.
Semantics: The statement is executed while the boolean expression
cond_expr is evaluated to true.
Note that a while statement does not return any atom
The following table presents several examples of while statements:
while Statement Examples
|
while (true) a++;
a is increment definitively!
while (n-) a++;
an error is raised: boolean expected, got integer
while (n- > 0) a++;
this is better
while (n++ <= 100 || stop) {if (!perform(a++)) break; check(a);}
note the usage of a compound statement and of the break
while (name != "john") {l := (select Person.name = name); name := get_name();}
no comments
|
Syntax: do statement while ( cond_expr )
where cond_expr is a boolean expression, and statement
any statement: an expression
statement, an iteration statement, a compound statement, a selection statement,
a function definition statement, a jump statement or an empty statement.
Semantics: The statement is executed at least once.
Then while the boolean expression
cond_expr is evaluated to true, the statement is executed.
Note that a do/while statement does not return any atom
The following table presents several examples of do/while statements:
do/while Statement Examples
|
do a++; while (true);
a is increment definitively!
do a=+; while (n-);
an error is raised: boolean expected, got integer
do a++; while (n- > 0);
this is better
do {if (!perform(a++)) break; check(a);} while (n++ <= 100 || stop);
note the usage of a compound statement and of the break
do {l := (select Person.name = name); name := get_name();} while (name != "john");
no comments
|
Syntax: for ( [expr1] ; [cond_expr] ; [expr2] ) statement
where cond_expr is a boolean expression, expr1 and
expr2 are any expressionss and statement
any statement: an expression
statement, an iteration statement, a compound statement, a selection statement,
a function definition statement, a jump statement or an empty statement.
Semantics: The expression expr1 is evaluated.
While the boolean expression
cond_expr is evaluated to true, the statement is
executed and the expression expr2 is evaluated.
Note that a C-for statement does not return any atom
The following table presents several examples of C-for statements:
C-for Statement Examples
|
for (x := 0; x < 100; x++) a++;
increment a an hundred times
for (x := 0, y := 1; x < 100 && check(y); x++) {y := get(y); if (y == 9999) break;}
a more complex example
for (x := 100; x; x-) perform(x);
raises an error: boolean expected, got integer
for (x := 0;;) doit();
note that there is neither a conditionnal expression, nor a second expression
for (;;) doit();
same as while(true)
|
Syntax: for ( var in expr ) statement
where cond_expr is a boolean expression, var denotes
the name of a variable, expr is an expression of type
collection and statement
any statement: an expression
statement, an iteration statement, a compound statement, a selection statement,
a function definition statement, a jump statement or an empty statement.
Semantics:
For each element in the collection denoted by expr, the
variable var is assigned to this element and the statement
is executed.
Note that a collection-for statement does not return any atom
The following table presents several examples of collection-for statements:
collection-for Statement Examples
|
for (x in list(1, 2, 3)) a += x;
increments a with 1, 2 and 3
for (x in (select Person)) names += x.name;
concatenates all the person names
for (x in (select Person.name = "john")) if (x.age < 10 || x.spouse.age < 10) throw "cannot mary children!!";
a moralistic example
for (x in 1) doit();
raises an error: boolean expected, got integer
|
Jump Statements
There are two jump statements based on the keywords
break and return.
The syntax for break statement is:
break statement
where statement is an optional integer expression statement
indicating the break level.
If not specified, the break level is 1.
The break statement may appear only within an iteration statement:
while, do/while, for statements.
break Statement Examples |
Statement |
Comments |
break; |
raises an error: break operator «break; » : level 1 is too deep. |
break 3; |
raises an error: break operator «break; » : level 3 is too deep. |
for (x := 0; ; x++) if (x == 30) break; |
break the loop when x is equal to 30 |
while (true) {x++; if (!check(x)) break;} |
break the loop when check(x) is not true |
while (true) {x++; if (!check(x)) break 2;} |
raises an error: break operator «break; » : level 2 is too deep. |
while (true) {x++; while (x < 100) if (!check(&x)) break 2;} |
break the two loop when check(&x) is not true |
The syntax for return statement is:
return [statement]
where statement is an optional expression statement
indicating the atom to return. The atom to return may be of any type.
If not specified, no atom is returned.
The return statement may appear only within a function definition statement.
return Statement Examples |
Statement |
Comments |
return; |
raises an error: return operator «return; » : return must be performed in a function |
function fact(n) {return n * fact(n-1);} |
ok. |
function f(x) {if (g(x)) return x+1; return x+2;} |
ok. |
function f(b) {if (b) return list(1, 2, 3); return bag(1);} |
ok. |
function f(b) {if (b) return list(1, 2, 3); return bag(1);} |
ok. |
define as f(x) (if (x) return x) |
raises an error: syntax error |
Function Definition Statements
As introduced previously, OQL supports functions. There are two sorts of
functions definition syntax:
function definition expression and function definition statements.
The first ones, exposed in Section 5.26 can contain only one
expressions. That means that they cannot include neither selection,
neither statements, neither jump statements.
Furthermore, as only one expression is allowed, functions that need
several expressions, one must use the comma sequencing operaot to
seperate the expressions, thus making this code not always readable.
We introduce here the more general form of function definitions which
overrides the limitations of the previous form.
The general form of function defintion statements is:
function identifier ([arglist])
compound_statement
- identifier denotes any valid OQL identifier, except a keyword
- arglist is an optional comma-separated list
of identifiers
optionally followed, for default arguments, by a ``?''
and an expr, for instance:
(var1, var2, var3?
expr, var4? expr)
- compound_statement is a optionnal semicolon-separated
list of statements surrounded by braces.
For instance:
function f(x, y, z ? oql$maxint) {
if (x > y)
throw "error #1";
return x - y * 2 / z;
}
Argument Types/Return Type
Functions are not typed. That means that neither the
return type nor the argument types may be given.
It is why there is no function overloading mechanisms. To take
benefit of the overloading mechanisms, one must use methods.
Nevertheless, it is possible to add type checking by using
library functions such as is_int, is_string...
combined with the assert or the assert_msg
library functions.
For instance, to check that the first argument is an integer
and the second one a collection:
function doit(n, coll) {
assert_msg(is_int(n), "doit: argument #1: integer expected");
assert_msg(is_coll(coll), "doit: argument #2: collection expected");
// body of the function
}
The assert_msg check that its first argument is equal to
true, otherwiser an exception containing the second argument
string is thrown:
doit(1, list(1, 2, 3)); // ok
doit(1.2, list(1)); // raises the error:
// assertion failed: 'doit: argument #1: integer expected'
Furthermore, one cannot specify that an argument is an input
argument (in), an output argument (out) or
an input/output argument (inout).
In a function call, expressions and variables are always passed
by value not by reference, this means that the call to
``perform(x, y)'' cannot modify neither x nor y.
(In fact, yes it can! It is explained below. But forget it for now).
So, to modify variable through a function call, one needs to give the
reference (or address) of this variable, not its value.
In this case, the function must execute specific code dealing with
address variables instead of their values.
The refof operator, introduced in a previous section, gives
the reference of an identifier.
Remember that the expression refof x returns
the identifier x.
To make a function call using references one must do:
swap(refof x, refof y) or
the equivalent more compact form swap(&x, &y).
Contrary to C++, reference manipulation is not transparent in OQL:
to access the value of a reference, one must use the valof operator
(i.e. * operator).
The swap function which swaps its two inout arguments
has already been introduced:
function swap(rx, ry) {
v := *rx;
*rx := *ry;
*ry := v;
}
The arguments have been prefixed by r to
indicate that they are references.
So, the function call swap(&x, &y) will swap properly
the variables x and y.
One can add type checking in the swap function, as follows:
function swap(rx, ry) {
assert_msg(is_ident(rx), "swap: argument #1 identifier expected");
assert_msg(is_ident(ry), "swap: argument #2 identifier expected");
v := *rx;
*rx := *ry;
*ry := v;
}
Return Value
By default, a statement-oriented function returns no atom. To make a
function returning an atom, one must use the return statement
previously introduced. As a function has no specified returned
type, it may contained several return statements returning
atom of different types:
function perform(x) {
if (x == 1)
return "hello";
if (x == 2)
return list(1, 2, 3) + list(4, 20);
if (x == 3)
return 2;
if (x == 4)
return 'a';
}
alpha := perform(1); // alpha is equal to "hello"
alpha := perform(3); // alpha is equal to 2
alpha := perform(8); // alpha is equal to nil
Default Arguments
OQL provides support for default arguments in a function definition
statement.
The syntax for a default argument is: ``var ? expr''
or ``var := expr''.
As in C and C++, the arguments with a default value must not followed
by any argument with default values.
For instance, function f(x, y, z := "alpha") is valid
while function f(x, y, z := "alpha", t) is not valid.
Unval Arguments
Sometimes, it is interesting to prevent the evaluation of some input
arguments. For instance, let the function if_then_else which takes
three arguments:
- cond: a boolean expression,
- then_expr: expression of any type; is evaluated and returned
if and only if the condition is evaluated to true
- else_expr: expression of any type; is evaluated and returned
if and only if the condition is evaluated to false
It is clear that the following function definition:
function if_then_else(cond, then_expr, else_expr) {
if (cond)
return then_expr;
return else_expr;
is not correct as, although it returns the correct expression,
the then_expr and the else_expr will be evaluated.
For instance, if_then_else(x < 10, ::a := 2, ::b := 3) will
return 2 if x is less than 10, otherwise
it will return 3, but in any case, a will be assigned
to 2 and b will be assigned to 3.
So, one needs a way to tell the interpreter that we do not want to evaluate
the second and the third argument. The special character
before an argument means that this argument must not be evaluated.
In this case, this argument is substitued by the string representation of
the expression.
For instance, let the function if_then_else:
function if_then_else(cond, |then_expr, |else_expr) {
// ...
}
when performing the call ``if_then_else(x < 10, ::a := 2, ::b := 3)'':
- the value of cond in the body of the function will be
true or false,
- the value of then_expr in the body of the function will be
"::a:=2"
- the value of else_expr in the body of the function will be
"::b:=3"
The correct implementation of this function is as follows:
function if_then_else(cond, |then_expr, |else_expr) {
if (cond)
return eval then_expr;
return eval else_expr;
}
Scope of Variables
In the body of a function defintion, every variable on the left side of an
assignment has a local scope except if this variable is prefixed by
the global scope operator ::.
That means, that after the following statement sequence:
a := 2;
function doit() {
a := 1;
}
the variable a is still equal to 2. While after:
Recursivity
a := 2;
function doit() {
::a := 1;
}
the variable a is equal to 1.
Particularity
One can define a statement-oriented function inside the body of
a statement-oriented function, for instance:
function compute(amount_in_euro, usdollar_per_euro) {
function euro2usdollar(euro, usd ? usdollar_per_euro) {
return euro * usd;
}
x := euro2usdollar(euro * 1.24);
x += euro2usdollar(1000);
return x * .120;
}
Note that the function defined in the body of the function compute
has a global scope, that means that after one execution of compute
the function is available at the global level of the OQL session.
It is possible, that in a future version, the functions defined in
the body of a function definition will have a local scope.
The oql$functions value is a list whose elements are the name of all
the OQL functions
defined in the current OQL session. Each you add a user function,
this variable is updated.
At the beginning of a session,
the value of
textttoql$functions is:
list(is_int, is_char, is_double, is_string, is_oid, is_num, is_bool, is_bag,
is_set, is_array, is_list, is_coll, is_struct, is_empty, void, assert,
assert_msg, min, max, first, last, cdr, count, interval, sum, avg, is_in,
distinct, flatten, flatten1, tolower, toupper, tocap, toset, tolist,
tobag, toarray, listtoset, bagtoset, arraytoset, listtobag, settobag,
arraytobag, bagtolist, settolist, arraytolist, bagtoarray, settoarray,
listtoarray, strlen, substring, forone, forall, delete_from, get_from,
ifempty, null_ifempty, getone)
For instance, to put the complete definition of all
these functions into the variable functionString:
functionString := "";
for (x in oql$functions)
functionString += "FUNCTION " x + ": " + bodyof x + "\n";
The next section provides a few statement-oriented and
expression-oriented function definitions.
EyeDB manual