Now get the smallest salary (the first in the list) and take the corresponding
street name. This is the final result.
first(sortecLsalary_map).street
Example as a single query:
first(
select street, average_salary: avg(select e.salary from partition)
from (select (Employee) p from Persons p where "has a job"
in p.activities ) as e
group by street: e.address.street
order by average_salary).street
62
4.9 Language Definition
OQL is an
expression language. A query expression is built from typed operands composed
recursively by operators. We will use the term expression to designate a
valid query in this section.
The examples are
based on the schema described in Chapter 3.
4.9.1 Queries
A query consists of
a (possibly empty) set of query definition expressions followed by an
expression, which is evaluated as the query itself. The set of query definition
expressions is nonrecursive (although a query may call an operation which
issues a query recursively).
Example:
define jones as
select distinct x
from Students x
where x.name = "Jones";
select distinct student_id from jones
This defines the set jones of students named Jones and gets the set of their
student_ids.
4.9.2 Named Query Definition
If q
is a query name
and e
is a query expression, then define q
as
e
is a query definition
expression which defines the query with name q
.
Example:
define Does as
select x
from Student x
where x.name ="Doe"
This statement defines Does
as a
query returning a bag containing all the students whose name is Doe.
define Doe as element(select x from Student x where
x.name="Doe")
This statement
defines Doe
as a query which returns the student
whose name is Doe (if there is
only one, otherwise an exception is raised).
4.9.3 Elementary Expressions
4.9.3.1 Atomic Literals
If I is an atomic literal, then I is an expression
whose value is the literal itself. Literals have the usual syntax:
- Object Literal: nil
- Boolean Literal: false, true
- Integer Literal: sequence of digits, e.g. 27
- Float Literal: mantissa/exponent. The
exponent is optional, e.g., 3.14 or 314.16e-2
- Character Literal: character
between single quotes, e.g., 'z'
- String Literal: character
string between double quote, e.g.,"a string"
63
4.9.3.2 Named Objects
If e is a named object, then e is an expression. It
defines the entity attached to the name.
Example:
Students
This query defines the set of
students. We have assumed here that there exists a name Students corresponding
to the extent of objects of the class Student.
4.9.3.3 Iterator Variable
If x
is a variable declared in a from part of a
select-from-where, then x
is an expression whose
value is the current element of the iteration over the
corresponding collection.
4.9.3.4 Named Query
If define q as e
is a query definition expression, then q
is an expression.
Example:
Doe
This query returns the student with
name Doe. It refers to the query definition expression
declared in Section 4.9.2.
4.9.4 Construction Expressions
4.9.4.1 Constructing Objects
If t
is a type name,
p1, p2, ...,pn
are properties of
t
, and e1,
e2, ...,en
are expressions, then
t(p1: e1..., pn: en)
is an expression.
This defines a new object of type t
whose properties p1, p2, ...,pn
are initialized with the expression e1,
e2, ...,e2
. The type of ei
must be compatible with the type of pi
.
If t
is a type name of a collection
and e
is a collection literal, then t(e)
is a collection object. The type of e
must be compatible with t
.
Examples:
Employee(name: "Peter", boss: Chairman)
This creates a mutable Employee
object.
vectint(set(1,3,10))
This creates a mutable set object (see the definition of vectint
in Section
4.4.1).
64
4.9.4.2 Constructing Structures
If p1, p2,.... pn
are property names, and e1, e2,..., en
are expressions, then
struct(p1: e1, p2: e2,
..., pn:en)
is an expression. It defines the structure taking values e1, e2,
..., en
on the properties
p1, p2, ..., pn
.
Note that this dynamically creates
an instance of the type struct(p1: t1, p2: t2,
..., pn: tn)
if ti
is the type of ei
.
Example:
struct(name: "Peter", age: 25);
This returns a structure with two
attributes name
and age
taking respective values Peter
and 25
.
See also
abbreviated syntax for some contexts in Section 4.10.1.
4.9.4.3 Constructing Sets
If e1, e2, ..., en
are expressions, then set(e1, e2,.... en)
is an expression. It defines the set containing the elements
e1, e2, ..., en
.
It creates a set instance.
Example:
set(1,2,3)
This returns a set consisting of the three elements 1
,
2
, and 3
.
4.9.4.4 Constructing Lists
If e1, e2, ..., en
are expressions, then
list(e1, e2, ..., en)
or simply (e1, e2, ..., en)
are expressions. They define the list having elements
e1, e2, ..., en
.
They create a list instance.
If min
, max
are two expressions of
integer or character types, such that min < max, then
list(min .. max)
or simply (min .. max)
is an expression of value: list(min, min+1,... max-1, max)
Example:
list(1,2,2,3)
This returns a list of four elements.
Example:
list(3 .. 5)
This returns the list(3,4,5)
65
4.9.4.5 Constructing Bags
If e1, e2, ..., en
are expressions, then
bag(e1, e2, ...,en)
is an expression. It defines the bag having elements e1,
e2, ..., en
. It creates a bag instance.
Example:
bag(1,1,2,3,3)
This returns a bag of five elements.
4.9.4.6 Constructing Arrays
If e1, e2, ..., en
are expressions, then
array(e1, e2, ..., en)
is an expression. It defines an array having elements e1,
e2,..., en
. It creates an array instance.
Example:
array(3,4,2,1,1)
This returns an array of five elements.
4.9.5 Atomic Type Expressions
4.9.5.1 Unary Expressions
If e
is an expression and <op>
is a unary operation valid for the type of e
, then
<op> e
is an expression. It defines the result
of applying <op>
to e
.
Arithmetic unary |
+, -, abs |
Boolean unary operator: |
not |
Example:
not true
This returns false
.
4.9.5.2 Binary Expressions
If e1
and e2
are expressions and <op>
is a binary operation, then
e1<op>e2
is an expression. It defines the result of applying <op>
to e1
and e2
.
Arithmetic integer binary operators: |
+, -, *, /, mod (modulo) |
Floating point binary operators: |
+, -, *, / |
Relational binary operators: |
=, !=, <, <=, >,>= |
These operators are defined on all atomic types. |
Boolean binary operators: |
and, or |
66
Example:
count(Students) - count(TA)
This returns the difference between the number of students and the
number of TAs.
4.9.5.3 String Expressions
If s1
and s2
are expressions of type string, then
s1 || s2
, and
s1 + s2
are equivalent expressions of type string whose
value is the concatenation of the two strings.
If c
is an expression of type character,
and s
an expression of type string, then
c in s
is an expression of type
boolean whose value is true if the character belongs
to the string, else false.
If s
is an expression of type string, and
i
is an expression of type integer, then
s[i]
is an expression of type character whose
value is the i+lth
character of the string.
If s
is an expression of type string, and
low
and up
are expressions of type integer, then
s[low:up]
is an expression of type string whose
value is the substring of s
from the
low+lth
character up to the up+lth
character.
If s
is an expression of type string, and
pattern
a string literal which may include the wildcard characters:
"?" or "_", meaning any character, and "*" or "%", meaning any substring
including an empty substring, then
s like pattern
is an expression of type boolean whose value is true if
s
matches the pattern
, else false.
Example:
"a nice string" like "%nice%str_ng"
is true.
4.9.6 Object Expressions
4.9.6.1 Comparison of Mutable Objects
If e1
and
e2
are expressions which denote mutable objects
(objects with identity) of the same type, then
e1 = e2
and
e1 != e2
are expressions which return a boolean.
The second expression is equivalent to
not(e1 = e2)
.
Likewise e1 = e2
is true if they designate the same object.
Example:
Doe = element(select s from Students s where s.name = "Doe")
is true.
67
4.9.6.2 Comparison of Immutable Objects
If e1>
and e2
are expressions which denote immutable objects (literals) of the same type, then
e1 = e2
and
e1 != e2
are expressions which return a boolean.
The second expression is equivalent to
not(e1 = e2)
.
Likewise, e1 = e2
is true if the value e1
is equal to the value e2
.
4.9.6.3 Extracting an Attribute or Traversing a
Relationship from an Object
If e
is an expression,
if p
is a property name, then e->p
and
e.p
are expressions. These are
alternate syntax to extract property p
of
an object e
.
If e
happens to designate a deleted or a nonexisting object,
i.e., nil
, an attempt
to access the attribute or to traverse the relationship raises an exception.
However, a query may test explicitly if an object is different from
nil
before accessing a property.
Example:
Doe.name
This returns Doe.
Example:
Doe->spouse != nil and Doe->spouse->name = "Carol"
This returns true, if Doe
has a spouse whose name is
Carol, or else false.
4.9.6.4 Applying an Operation to an Object
If e
is an expression and f
is an operation
name, then e->f
and e.f
are
expressions. These are alternate syntax to apply an operation on an object. The
value of the expression is the one returned by the operation or else the object
nil
, if the operation returns nothing.
Example:
jones->number_of_students
This applies the operation number_of_students
to jones
.
4.9.6.5 Applying an Operation with Parameters to an Object
If e
is an expression, if e1,
e2, ..., en
are expressions
and f
is an operation name, then
e->f(e1, e2, ..., en)
and
e.f(e1, e2, ..., en)
are expressions that apply operation f
with parameters
e1, e2, ..., en
to object e
. The value of the expression is the one returned by
the operation or else the object nil
, if the operation returns nothing.
68
In both cases, if e
happens to designate a deleted or a
nonexisting object, i.e., nil
, an attempt
to apply an operation to it raises an exception. However, a query may test
explicitly if an object is different from nil
before applying
an operation.
Example:
Doe->apply_course("Math", Turing)->number
This query calls the operation
apply_course
on class Student
for the object
Doe
. It passes two parameters, a
string and an object of class Professor
.
The operation returns an object of
type Course
and the query returns the number of this course.
4.9.6.6 Dereferencing an Object
If e
is an expression which denotes an object with
identity (a mutable object), then *e
is
an expression which delivers the value of the object (a literal). Given two
variables of type Person
, p1
and p2
,
the predicate p1 = p2
is true if both
variables refer to the same object, while *p1 = *p2
is true if the objects have the same values,
even if they are not the same object.
4.9.7 Collection Expressions
4.9.7.1 Universal Quantification
If x
is a variable name, e1
and e2
are expressions,
e1
denotes a collection, and
e2
a predicate, then
for all x in e1: e2
is an expression. It returns true if all the elements
of collection e1
satisfy e2
and false otherwise.
Example:
for all x in Students: x.student_id > 0
This returns true if all the
objects in the Students
set have a positive value
for their student_id
attribute.
Otherwise it returns false.
4.9.7.2 Existential Quantification
If x
is a variable name,
if e1
and e2
are expressions,
e1
denotes a collection, and
e2
a predicate, then
exists x in e1: e2
is an expression. It returns true if there is at least one element of
collection e1
that satisfies
e2
and false otherwise.
Example:
exists x in Doe.takes: x.taught_by.name = "Turing"
This returns true if at least one course
Doe
takes is taught by someone named Turing.
69
If e
is a collection expression, then
exists(e)
and
unique(e)
are expressions which
return a boolean value. The first one returns true if there exists at least one
element in the collection, while the second one returns true if there exists
only one element in the collection.
Note that these operators accept the SQL syntax for nested queries like
select... from col where exists (select... from col1 where predicate)
The nested query
returns a bag to which the operator exists is applied. This is of course the
task of an optimizer to recognize that it is useless to compute effectively the
intermediate bag result.
4.9.7.3 Membership Testing
If e1
and e2
are expressions, e2
is a collection, and
e1
has the type of its elements, then
e1 in e2
is an
expression. It returns true if element e1
belongs to collection e2
.
Example:
Doe in Does
This returns true.
Doe in TA
This returns true if Doe is a Teaching Assistant.
4.9.7.4 Aggregate Operators
If e
is an expression which denotes a collection,
if <op>
is an operator from {min,
max, count, sum, avg}
, then <op>(e)
is an expression.
Example:
max (select salary from Professors)
This returns the maximum salary of the Professors.
4.9.8 Select From Where
If e, e',
e1, e2, ..., en
are expressions which denote collections, and
x1, x2,..., xn
are variable names if e'
is an expression of type boolean, and
if projection
is an expression or the character *, then
select projection
from e1 as x1, e2 as x2, ...,
en as xn
where e'
and
select distinct projection
from e1 as x1, e2 as x2, ...,
en as xn
where e'
are expressions.
The result of the query is a set for a select distinct
or
a bag for a select
.
70
If you assume e1, e2, ..., en
are all set and bag expressions, then the result is obtained as follows:
take the Cartesian product
1
of the sets e1, e2,..., en
;
filter that product by expression e'
(i.e., eliminate from the result all objects that do not satisfy
boolean expression e'); apply the projection
to each
one of the elements of this filtered set and get the result.
When the result is a set (distinct
case)
duplicates are automatically eliminated.
The situation where one or more of the collections
e1, e2,..., en
is an indexed
collection is a little more complex. The select operator first converts all
these collections into bags and applies the previous operation. The result is a
set (distinct
case) or else a bag. So, in this case, we simply
transform each of the ei's into a bag and apply the previous definition.
1. The Cartesian product between a set and a bag is defined by
first converting the set into a bag, and then get the resulting
bag, which is the Cartesian product of the two bags.
Back.
4.9.8.1 Projection
Before the projection, the result of the iteration
over the from variables is of type
bag< struct(x1: type_of(e1 elements), ...,
xn: type_of(en elements)) >
The projection is constructed by an
expression which can then refer implicitly to the current element of
this bag, using the variables xi
.
If for ei
neither
explicit nor implicit variable is declared, then xi
is given an internal system name (which is not accessible by the query anyway).
By convention, if the projection is
simply "*
", then the result of the select is the same
as the result of the iteration.
If the projection is "distinct *
", the result of the
select is this bag converted into a set.
In all other cases, the projection is explicitly computed by the given
expression.
Example:
select couple(student: x.name, professor: z.name)
from Students as x,
x.takes as y,
y.taught_by as z
where z.rank = "full professor"
71
This returns a bag of objects of type couple giving
student names and the names of the full
professors from which they take classes.
Example:
select *
from Students as x,
x.takes as y,
y.taught_by as z
where z.rank = "full professor"
This returns a bag of structures,
giving for each student "object", the section object followed by the
student and the full professor "object" teaching in this section:
bag< struct(x: Student, y: Section, z: Professor) >
4.9.8.2 Iterator Variables
A variable, xi
declared in the from part ranges over the collection
ei
, and thus has the
type of the elements of this collection. Such a variable can be used in any
other part of the query to evaluate any other expressions (see the Scope Rules
in Section 4.9.15). Syntactical variations are accepted for declaring these
variables, exactly as with SQL. The as keyword may be omitted. Moreover,
the variable itself can be omitted too, and in this case, the name of the
collection itself serves as a variable name to range over it.
Example:
select couple(student: Students.name, professor: z.name)
from Students,
Students.takes y,
y.taught_by z
where z.rank = "full professor"
4.9.8.3 Predicate
In a select-from-where query, the where clause can be
omitted, with the meaning of a true predicate.
4.9.9 Group-by Operator
If select_query is a select-from-where query,
partition_attributes is a structure expression
and predicate a boolean expression, then
select_query group by partition_attributes
is an expression and
select_query group by partition_attributes
having predicate
is an expression.
72
The Cartesian
product visited by the select operator is split into partitions. For each
element of the Cartesian product, the partition attributes are evaluated. All
elements which match the same values according to the given partition
attributes, belong to the same partition. Thus the partitioned set, after the
grouping operation, is a set of structures: each structure has the valued
properties for this partition (the valued partition_attributes),
completed by a property which is conventionally called partition and
which is the bag of all objects matching this particular valued partition.
If the partition attributes are
att1: e1
,
att2: e2
, ...,
attn: en
,
then the result of the grouping is of type
set< struct(att1: type_of(e1),
att2: type_of(e2), ...,
attn: type_of(en),
partition: bag< type_of(grouped elements) >)>
The type of grouped elements is defined as follows.
If the from clause declares the variables
v1
on collection col1
,
v2
on col2
, ...,
vn
on coln
, the grouped
elements is a structure with one attribute, vk
,
for each collection having the type of the
elements of the corresponding collection partition:
bag< struct(v1: type_of(col1 elements), ...,
vn: type_of(coln elements))>
If a collection colk
has
no variable declared the corresponding attribute has
an internal system name.
This partitioned
set may then be filtered by the predicate of a having clause. Finally,
the result is computed by evaluating the select clause for this
partitioned and filtered set.
The having
clause can thus apply aggregate functions on partition; likewise the
select clause can refer to partition to compute the final result. Both
clauses can refer also to the partition attributes.
Example:
select *
from Employees e
group by low: salary < 1000,
medium: salary >= 1000 and salary < 10000,
high: salary >= 10000
This gives a set
of three elements, each of which has a property called partition which
contains the bag of employees that enter in this category. So the type of the
result is
set<struct(low: boolean,
medium: boolean, high: boolean, partition: bag<struct(e: Employee)>)>
73
The second form
enhances the first one with a having clause which enables you to filter
the result using aggregative functions which operate on each partition.
Example:
select department, avg_salary: avg(select e.salary from partition)
from Employees e
group by department: e.deptno
having avg(select e.salary from partition) > 30000
This gives a set
of couples: department and average of the salaries of the employees working in
this department, when this average is more than 30000. So the type of the
result is
bag<struct(department: integer, avg_salary: float)>
Note that to compute the average
salary, we have used a shortcut notation allowed by the Scope Rules defined in
Section 4.9.15. The fully developed notation would read
avg_salary: avg(select x.e.salary from partition x)
4.9.10 Order-by Operator
If select_query
is a select-from-where or a select-from-where-group_by query, and
if e1, e2,.... en
are expressions, then
select_query order by e1, e2,..., en
is an expression. It returns a list of the selected elements
sorted by the functions e1
,
and inside each subset yielding the same e1
,
sorted by e2
, ..., and the final subsub...set,
sorted by en
.
Example:
select p from Persons p order by p.age, p.name
This sorts the set of persons on their age, then
on their name and puts the sorted objects into the result as a list.
Each sort expression criterion can be followed by the keyword
asc or desc, specifying respectively an ascending or
descending order. The default order is that of the previous
declaration. For the first expression, the default is
ascending.
Example:
select * from Persons order by age desc, name asc, department
74
4.9.11 Indexed Collection Expressions
4.9.11.1 Getting the ith Element of an Indexed Collection
If e1
and e2
are expressions, e1
is a list or an array,
e2
is an integer, then
e1[e2]
is an expression. This
extracts the e2+1
element of the indexed
collection e1
.
Notice that the first element has the rank 0.
Example:
list(a,b,c,d)[1]
This returns b
.
Example:
element (select x
from Courses x
where x.name = "Math" and x.number = "101").requires[2]
This returns the third prerequisite of Math 101.
4.9.11.2 Extracting a Subcollection of an Indexed Collection
If e1
, e2
,
and e3
are expressions, e1
is a list or an array, and e2
and e3
are integers, then
e1[e2:e3]
is an expression. This extracts the subcollection of
e1
starting at position
e2
and ending at position
e3
.
Example:
list(a,b,c,d)[1:3]
This returns list(b,c,d)
.
Example:
element (select x
from Courses x
where x.name="Math" and x.number="101").requires[0:2]
This returns the list consisting of the first
three prerequisites of Math 101.
4.9.11.3 Getting the First and Last Elements of an Indexed Collection
If e
is an expression, <op>
is an operator from {first, last}
, and e
is a
list or an array, then <op>(e)
is an expression. This extracts the first and last
element of a collection.
Example:
first(element(select x
from Courses x
where x.name="Math" and x.number="101").requires)
This returns the first prerequisite of Math 101.
75
4.9.11.4 Concatenating Two Indexed Collections
If e1
and e2
are expressions and e1
and e2
are both lists or both arrays, then
e1+e2
is an expression. This computes the concatenation of
e1
and e2
.
Example:
list(1,2)+list(2,3)
This query generates list(1,2,2,3)
.
4.9.12 Binary Set Expressions
4.9.12.1 Union, Intersection, Difference
If e1
and e2
are expressions,
if <op>
is an operator from {union, except, intersect}
,
if e1
and e2
are sets or
bags, then
e1 <op> e2
is an expression. This computes set theoretic operations, union, difference,
and intersection on e1
and e2
,
as defined in Chapter 2.
When the operand's collection types are different (bag and set),
the set is first converted into a bag and the result is a bag.
Examples:
Student except TA
This returns the set of students who are not Teaching Assistants.
bag(2,2,3,3,3) union bag(2,3,3,3)
This bag expression returns bag(2,2,3,3,3,2,3,3,3)
.
bag(2,2,3,3,3) intersect bag(2,3,3,3)
The intersection of two bags yields a bag that contains the
minimum for each of the multiple values. So the result
is bag(2,3,3,3)
.
bag(2,2,3,3,3) except bag(2,3,3,3)
This bag expression returns bag(2)
.
4.9.12.2 Inclusion
If e1
and e2
are expressions
which denote sets or bag and if <op>
is an operator
from {<, <=, >, >=}
, then
e1 <op> e2
is an expression whose value is a boolean.
When the operands are different kinds of collections (bag and set),
the set is first converted into a bag.
e1 < e2
is true
if e1
is included into e2
but not equal to e2
e1 <= e2
is true
if e1
is included into e2
Example:
set(1,2,3) < set(3,4,2,1)
is true.
76
4.9.13 Conversion Expressions
4.9.13.1 Extracting the Element of a Singleton
If e
is a collection-valued expression,
element(e)
is an expression. This takes the singleton
e
and returns its element. If e
is not a
singleton this raises an exception.
Example:
element(select x from Professors x where x.name = "Turing")
This returns the professor whose name is
Turing (if there is only one).
4.9.13.2 Turning a List into a Set
If e
is a list expression, listtoset(e)
is an expression. This converts the list into a set,
by forming the set containing all the elements of the list.
Example:
listtoset(list( 1,2,3,2))
This returns the set containing 1, 2, and 3.
4.9.13.3 Removing Duplicates
If e
is an expression whose value is a collection, then
distinct(e)
is an expression whose
value is the same collection after removing the duplicated elements.
If e
is a bag, distinct(e)
is a set.
If e
is an ordered collection, the relative ordering
of the remaining elements is preserved.
4.9.13.4 Flattening Collection of Collections
If e
is a collection-valued expression,
flatten(e)
is an expression. This converts
a collection of collections of t
into a collection of t
.
So flattening operates at the first level only.
Assuming the type of e
to be
col1<col2<t>>
,
the result of flatten(e)
is:
- If
col2
is a set (resp. a bag), the union
of all col2<t>
is done and the result is a set<t>
(resp. bag<t>
).
- If
col2
is a list or an array
and col1
is a list or an array, the concatenation of all
col2<t>
is done following the order in col1
and the result is
col2<t>
,
which is thus a list or an array. Of course duplicates, if any, are maintained
by this operation.
- If
col2
is a list or an array and
col1
is a set (resp. a bag), the lists or
arrays are converted into sets (resp. bags), the union of all these sets (resp.
bags) is done and the result is a set<t>
(resp. bag<t>
).
Examples:
flatten(list(set(1,2,3), set(3,4,5,6), set(7)))
This returns the set containing 1,2,3,4,5,6,7.
77
flatten(list(list(1,2), list(1,2,3)))
This returns list(1,2,1,2,3)
.
flatten(set(list(1,2), list(1,2,3)))
This returns the set containing 1,2,3.
4.9.13.5 Typing an Expression
If e
is an expression and c
is a type name,
then (c)e
is an expression. This asserts that
e
is an object of class type c
.
If it turns out that it is not true, an exception is raised at runtime.
This is useful to access a property of an object which is statically
known to be of a superclass of the specified class.
Example:
select ((Employee) s).salary
from Students s
where s in (select sec.assistant from Sections sec)
This returns the set of salaries of
all students who are teaching assistants, assuming that
Students
and Sections
are the extents of the classes
Student
and Section
.
4.9.14 Function Call
If f
is a function name, if
e1 , e2, ..., en
are
expressions, then f()
and
f(e1, e2, ..., e2)
are expressions whose value is the value returned by the function, or the
object nil
, when the function does not return any value.
The first form calls a function without a parameter,
while the second one calls a function with the parameters
e1, e2, ..., en
.
OQL does not define in which language the body of
such a function is written. This allows one to extend the
functionality of OQL without changing the language.
4.9.15 Scope Rules
The from part of a select-from-where query
introduces explicit or implicit variables to range
over the filtered collections. An example of an explicit variable is
select... from Persons p ... while
an implicit declaration would be
select... from Persons ...
The scope of these variables spreads over
all the parts of the select-from-where expression
including nested sub-expressions.
78
The group by part of a select-from-where-group_by query introduces
the name partition along with possible explicit attribute names which
characterize the partition. These names are visible in the corresponding
having and select parts, including nested sub-expressions
within these parts.
Inside a scope,
you use these variable names to construct path expressions and reach properties
(attributes and operations) when these variables denote complex objects. For
instance, in the scope of the first from clause above, you access the age of a
person by p.age.
When the variable is implicit, like
in the second from clause, you directly use the name of the collection by
Persons.age
.
However, when no ambiguity exists, you can use the property name
directly as a shortcut, without using the variable name to open the
scope (this is made implicitly), writing simply: age
.
There is no ambiguity when a property name is defined for one and only one
object denoted by a visible variable.
To summarize, a name appearing in a (nested) query is
looked up as follows:
- a variable in the current scope, or
- a named query introduced by the define clause, or
- a named object, i.e., an entry point in the database, or
- an attribute name or an operation name of a variable in the
current scope, when there is no ambiguity, i.e., this property
name belongs to only one variable in the scope.
Example:
Assuming that in the current schema the names
Persons
and Cities
are defined.
select
scope1
from Persons,
Cities c
where exists(select
scope2 from children as child) or
count (select
scope3, (select
scope4 from partition)
from children p,
scope5 v
group by age:
scope6
)
In scope1
, we see the names: Persons
, c
,
Cities
, all property names of class Person
and class
City
as long as they are not present in both classes,
and they are not called "Persons", "c", nor "Cities".
79
In scope2
, we see the names: child
, Persons
,
c
, Cities
, the property names of the class
City
which are not property of the class Person
. No
attribute of the class Person
can be accessed directly since
they are ambiguous between "child" and "Persons".
In scope3
, we see the names: age
,
partition
, and the same names from scope1
,
except "age" and "partition", if they exist.
In scope4
, we see the names: age
,
partition
, p
, v
, and the
same names from scope1
, except "age", "partition", "p" and "v",
if they exist.
In scope5
, we see the names: p
, and the same
names from scope1
, except "p", if it exists.
In scope6
, we see the names:
p
, v
, Persons
, c
,
Cities
, the property names of the class City
which are not property of the class Person
.
No attribute of the class Person
can be accessed
directly since they are ambiguous between "child" and "Persons".
4.10 Syntactical Abbreviations
OQL defines an orthogonal expression language, in the sense
that all operators can be composed with each other as long
as the types of the operands are correct. To achieve
this property, we have defined a functional language with simple operators such
as "+" or composite operators such as "select from where",
"group_by", and "order_by" which always deliver a result in
the same type system and which thus can be recursively operated with other
operations in the same query.
In order to accept the whole DML
query part of SQL, as a valid syntax for OQL, we have added ad-hoc
constructions each time SQL introduces a syntax which cannot be considered in
the category of true operators. This section gives the list of these
constructions that we call "abbreviations," since they are completely
equivalent to a functional OQL expression. At the same time, we give the
semantics of these constructions, since all operators used for this
description have been previously defined.
4.10.1 Structure Construction
The structure constructor has been introduced in
Section 4.9.4.2. An alternate syntax is allowed in two contexts:
select clause and group-by clause. In both contexts,
the SQL syntax is accepted, along with the one already defined.
select projection{, projection} ...
select ... group by projection {, projection}
where projection is one of the forms:
- expression as identifier
- identifier: expression
- expression
80
This is an alternate syntax for
struct(identifier: expression {, identifier: expression})
If there is only one projection and the syntax (3) is
used in a select clause, then it is
not interpreted as a structure construction but rather the expression stands as
is. Furthermore, a (3) expression is only valid if it is possible to infer the
name of the corresponding attribute (the identifier). This requires that the
expression denotes a path expression (possibly of length one) ending by a
property whose name is then chosen as the identifier.
Example:
select p.name, salary, student_id
from Professors p, p.teaches
This query returns a bag of structures:
bag<struct(name: string, salary: float, studentJd: integer)>
4.10.2 Aggregate Operators
These operators have been introduced in Section 4.9.7.4.
SQL adopts a notation which is not functional for them.
So OQL accepts this syntax, too. If we define aggregate
as one of min, max, count, sum and avg.
select count(*) from ...
|
is equivalent to |
count(select * from ...)
|
select aggregate(query) from ...
|
is equivalent to |
aggregate(select query from ...)
|
select aggregate(distinct query) from ...
|
is equivalent to |
aggregate (distinct(select query from ...)
|
4.10.3 Composite Predicates
If e1
and e2
are expressions,
e2
is a collection, e1
has
the type of its elements, if relation is a relational operator (=, !=,
<, <=, > , >=
), then
e1 relation some e2
and
e1 relation any e2
and
e1 relation all e2
are expressions whose value is a boolean.
The two first predicates are equivalent to
exists x in eg: ei relation x
81
The last predicate is equivalent to
for all x in e2: e1 relation x
Example:
10 < some (8,15, 7, 22)
is true.
4.10.4 String Literal
OQL
accepts single quotes as well to delineate a string (see Section 4.9.3.1), like
SQL does. This introduces an ambiguity for a string with one character which
then has the same syntax as a character literal. This ambiguity is solved by context.
4.11 OQL BNF
The OQL grammar is given using a rather informal BNF notation.
- { symbol } is a sequence of 0 or n symbol(s).
- [symbol] is an optional symbol. Do not confuse
this with the separators
[]
.
- keyword is a terminal of the grammar.
- xxx_name is the syntax of an identifier.
- xxx_literal is self-explanatory, e.g., "a string" is a string_literal.
- bind_argument stands for a parameter when
embedded in a programming language, e.g., $3i.
The non-terminal query stands for a
valid query expression. The grammar is presented as recursive rules producing
valid queries. This explains why most of the time this nonterminal appears on
the left side of ::=. Of course, each operator expects its "query"
operands to be of the right types. These type constraints have been introduced
in the previous sections.
These rules must
be completed by the priority of OQL operators which is given after the grammar.
Some syntactical ambiguities are solved semantically from the types of the
operands.
4.11.1 Grammar
4.11.1.1 Axiom (see 4.9.1, 4.9.2)
query_program ::= {define_query;} query
define_query ::=define identifier as query
82
4.11.1.2 Basic (see 4.9.3)
query ::= nil
query ::= true
query ::= false
query ::= integer_literal
query ::= float_literal
query ::= character_literal
query ::= string_literal
query ::= entry_name
query ::= query_name
query ::= bind_argument2
query ::= from_variable_name
query ::= (query)
2. A bind argument allows one to bind
expressions from a programming language to a query when embedded into this
language (see chapters on language bindings).
Back.
4.11.1.3 Simple Expression (see 4.9.5)
query ::= query + query3
query ::= query - query
query ::= query * query
query ::= query / query
query ::= - query
query ::= query mod query
query ::= abs(query)
query ::= query || query
3. The operator + is also used for list and array concatenation.
Back.
4.11.1.4 Comparison (see 4.9.5)
query ::= query comparison_operator query
query ::= query like string_literal
comparison_operator ::= =
comparison_operator ::= !=
comparison_operator ::= >
comparison_operator ::= <
comparison_operator ::= >=
comparison_operator ::= <=
4.11.1.5 Boolean Expression (see 4.9.5)
query ::= not query
query ::= query and query
query ::= query or query
83
4.11.1.6 Constructor (see 4.9.4)
query ::= type_name([query])
query ::= type_name (identifier: query {,identifier: query})
query ::= struct (identifier: query {,identifier: query})
query ::= set ([query {, query}])
query ::= bag ([query {, query}])
query ::= list ([query {, query}])
query ::= (query, query {, query})
query ::= [list](query .. query)
query ::= array ([query {,query}])
4.11.1.7 Accessor (see 4.9.6, 4.9.11, 4.9.14, 4.9.15)
query ::= query dot attribute_name
query ::= query dot relationship_name
query ::= query dot operation_name(query {,query})
dot ::= . | ->
query ::= * query
query ::= query [query]
query ::= query [query:query]
query ::= first(query)
query ::= last(query)
query ::= function_name([query {,query}])
4.11.1.8 Collection Expression (see 4.9.7, 4.10.3)
query ::= for all identifier in query: query
query ::= exists identifier in query: query
query ::= exists(query)
query ::= unique(query)
query ::= query in query
query ::= query comparison_operator quantifier query
quantifier ::= some
quantifier ::= any
quantifier ::= all
query ::= count(query)
query ::= count(*)
query ::= sum(query)
query ::= min(query)
query ::= max(query)
query ::= avg(query)
84
4.11.1.9 Select Expression (see 4.9.8,4.9.9,4.9.10)
query ::= |
select [ distinct] projection_attributes
from variable_declaration {, variable_declaration}
[where query]
[group by partition_atlributes]
[having query]
[order by sort_criterion {, sort_criterion}]
|
projection_attributes ::= projection {, projection}
proiection_attributes ::= *
projection ::= query
projection ::= identifier: query
projection ::= query as identifier
variable_declaration ::= query [[ as] identifier]
partition_attributes ::= projection {, projection}
sort_criterion ::= query [ordering]
ordering ::= asc
ordering ::= desc
4.11.1.10 Set Expression (see 4.9.12)
query ::= query intersect query
query ::= query union query
query ::= query except query
4.11.1.11 Conversion (see 4.9.13)
query ::= listtoset(query)
query ::= element(query)
query ::= distinct(e)
query ::= flatten(query)
query ::= (class_name) query
85
4.11.2 Operator Priorities
The following operators are sorted by decreasing priority.
Operators on the same line have the same priority and
group left-to-right.
() [] . ->
not -(unary) + (unary)
in
* / mod intersect
+ - union except ||
< > <= >= <some <any <all
= != like
and exists for all
or
.. :
,
(identifier) this is the cast operator
order
having
group by
where
from
select