Subsections


The Language Specifications

The basic concept of the EYEDB object model is the class which, as in any traditional object model, model a set of objects of similar properties (attributes) and behaviors (methods). The attributes can be basic types, user types, references, arrays, collections. The methods can be defined in C++ or in OQL (Object Query Language).

ODL allows one to specify classes, attributes, methods, triggers, constraints, enumerate types, indexes and implementation hints.

We are going to introduced in details all the features of ODL.


Comments

The ODL comments are like in C++: For instance:
// this is a simple line comments

/* this is
   a multi line
   comments */


Basic types

The basic types are as follows:

byte 1-byte integer
char 1-byte character
short 2-byte integer
int 4-byte integer
long 8-byte integer
double 8-byte floating point
oid 8-byte internal object identifier
enum 4-byte integer


For instance:
class C {
  attribute byte b;
  attribute char c;
  attribute short s;
  attribute int i;
  attribute long l;
  attribute double d;
  attribute oid o;
};
Notes :
  1. The key word attribute is optional:
    class C {
      byte b;
      char c;
      // ...
    };
    

    is correct.
  2. The grammar does not allow one to gather several attributes on the same line declaration:
    class C {
      attribute char c1, c2, c3; // NOT correct
      // ...
    };
    


Enum types

An enumerate type is denoted by a set of integers mapped to symbols like in C++. The syntax is similar to the C++ syntax, for instance:
enum E1 {
  A, // A == 0
  B, // B == 1
  C  // C == 2
};

enum E2 {
  D = 3,   // D == 3
  E,       // E == 4
  F = 100, // F == 100
  G,       // G == 101
  H        // H == 102
};

class C {
  attribute int i;
  E1 e1;
  E2 e2;
};


Array types

The object model supports multi-dimensional fixed or variable size arrays of any type. For instance:
class C {
  attribute byte b_a[4];      // fixed length mono-dimensional array
  attribute char str[];       // variable size mono-dimensional array
  attribute int i_a[3][4][8]; // multi-dimensional fixed size array
  attribute long l_a[][4][8]; // multi-dimensional variable size array
};
One particular interesting array type is the array of characters, which can be denoted as string as follows:
class C {
  attribute string s;       // <=> char s[] (unlimited size string)
  attribute string<32> bs;  // <=> char bs[32] (bounded string)
};
Note that in a multi-dimensional array, only the extreme left dimension can be variable:
class C {
  attribute long l_a1[][4][8]; // correct
  attribute long l_a2[4][][8]; // NOT correct
  attribute long l_a2[4][8][]; // NOT correct
};


Literal and object types

Remember that each object in a set of EYEDB databases has an unique identifier called OID.
A literal attribute is an attribute fully included in the class and has no OID, while an object attribute denotes the reference to another object with an object identifier. A reference attribute is denoted by a * or a & symbol. For instance:
class C1 {
  attribute int i;
};

class C {
  attribute C1  l_c1;  // literal attribute included in C
  attribute C1 *o_c1;  // object attribute referenced by C (or &oc1_1)
};
Let c an instance of the class C.

Do not confuse the * ODL meaning and the * C/C++ meaning: in C/C++, the * type modifier denotes an address to an area of the indicated type instances: it is a pointer to an address. This pointer can be incremented and decremented to change its location in the area.
In ODL, the * denotes a reference to one and only one object, it is why the & token is also accepted, although the meaning of this token is a little bit different in C++.
 
So, in ODL the construct C1 **oc1 makes no sense, in the same manner that the construct C1 &&oc1 makes no sense in C++.


One can have arrays of literal or object as follows:
class C {
  attribute C1  l_c1_1[2];
  attribute C1  l_c1_2[];
  attribute C1  l_c1_3[][10][20];

  attribute C1 *o_c1_1[4];
  attribute C1 *o_c1_2[];
  attribute C1 *o_c1_3[][4][5];
};


Collection types

The EYEDB object model support three types of collections, set, bag and array. A fourth type, list, will be implemented in a further version: An element may be of any type, literal or object and a collection attribute may be a literal or an object, and one can have arrays of collection, for instance:
class C {
  attribute set<int>  i_lset;    // literal set of int
  attribute set<C1>   l_c1_lset; // literal set of C1 literals
  attribute set<C1 *> o_c1_lset; // literal set of C1 objects

  attribute set<int>  *i_oset;    // object set of int
  attribute set<C1>   *l_c1_oset; // object set of C1 literals
  attribute set<C1 *> *o_c1_oset; // object set of C1 objects

  attribute bag<C1 *> o_c1_lbag;  // literal bag of C1 objects

  attribute array<C1 *> o_c1_larr;   // literal array of C1 objects
  attribute bag<C1 *>   o_c1_lbag[]; // array of literal bag of C1 objects

  // multi-dimensional array of literal bag of set of array of C1 objects
  attribute bag<set<array<set<C1 *> > > > x[2][3][4]; 
};
The differences between an array collection (i.e. array<type> and an attribute array (i.e. type []) are:


Inheritance

The object model support single inheritance using the keyword extends:
class C1 {
  attribute string c1;
};

class C2 extends C1 {
  attribute string c2;
};

class C3 extends C2 {
  attribute string c3;
};
As in usual object conception, an object of class C2 includes the two attributes c1 and c2 and an object of class C3 includes the three attributes c1, c2 and c3.

In the following construct:
class C4 {
  attribute C1 *oc1;
  attribute C2 *oc2;
  attribute C3 *oc3;

  attribute C1 lc1;
  attribute C2 lc2;
  attribute C3 lc3;
};
The attribute oc1 may be of type C1, C2 or C3.
The attribute oc2 may be of type C2 or C3.
The attribute oc3 may be of type C3 only.
The attribute lc1 is of type C1.
The attribute lc2 is of type C2.
The attribute lc3 is of type C3.


Constraints

The object model supports currently two declarative constraints: notnull and unique. The cardinality constraint on collection is partially implemented and is not currently supported. Non declarative constraints are defined using triggers (see below).

Note that: For instance:
class C {
  attribute string s1;
  attribute string s2;
  attribute string s3;

  constraint<notnull> on s1;

  constraint<notnull> on s2;
  constraint<unique> on s2;

  constraint<unique> on s3;
};
The attribute s1 must not be null.
The attribute s2 must not be null and is unique in the collection of C objects.
The attribute s3 is unique in the collection of C objects.


Constraint and inheritance propagation

By default, constraints are propagated to subclasses, let C2 a subclass of C:
class C2 extends C {
  attribute string c2;
};
When one creates an C2 object, the attributes s1 and s2 must not be null and the attributes s2 and s3 must be unique.

Important note: the unique constraint applies separately on each class (C and C2) and not on the set of inheritance class tree. This means that one can have a C object with a given value for s2 and a C2 object with the same value for s2.
This is not the expected default behavior and will be parameterised in a next version.

If you do no want to propagate automatically a constraint to the subclasses, you need to use the construct propagate = off as follows:
class C {
  attribute string s1;
  attribute string s2;
  attribute string s3;

  constraint<notnull, propagate = off> on s1;

  constraint<notnull> on s2;
  constraint<unique, propagate = off> on s2;

  constraint<unique> on s3;
};

class C2 extends C {
  attribute string c2;
};
The notnull constraint on C::s1 and the unique constraint on C1::s2 will not be propagated to C2, but the notnull constraint on C::s1 and the unique constraint on C::s3 will be propagated to C2.


Constraint on attribute of literal composite type

One can define constraints on attributes of literal composite type attribute, for instance:
class C1 {
  attribute string s1;
  attribute int i1;
};

class C {
  attribute C1 c1;
 
  constraint<notnull> on c1.s1;
  constraint<unique> on c1.i1;
};


Referential integrity

The EYEDB object model support one-to-one, one-to-many and many-to-many relationships.

A relationship between a class A and a class B is materialized by attributes in the two classes of the following types according to the cardinality of the relationship: For instance for a one-to-one relationship:
class A {
  attribute string sa;
  attribute B *b;
};

class B {
  attribute string sb;
  attribute A *a;
};
In the previous case, EYEDB maintains only partially the referential integrity: for instance, one cannot create an object A with an attribute b which refers an non-existent B object. But, if the referenced B object is removed, the attribute b will still referenced the removed object.

EYEDB can maintain the referential integrity by indicating the inverse directive in the ODL as follows:
class A {
  attribute string sa;
  relationship B *b inverse B::b; // or inverse b
};

class B {
  attribute string sb;
  relationship A *a inverse A::b; // or inverse a
};
Note attribute has been replaced by relationship in this case: this is mandatory.
In this case, if the B object referenced by a A object through b is removed, b is set to the null value.
A one-to-many relationship:
class A {
  attribute string sa;
  relationship set<B *> b_set inverse a;
};

class B {
  attribute string sb;
  relationship A *a inverse b_set;;
};
and a many-to-many relationship:
class A {
  attribute string sa;
  relationship set<B *> b_set inverse a_set;
};

class B {
  attribute string sb;
  relationship set<A *> a_set inverse b_set;;
};


Methods

In ODL, one can declare the signature of C++ and OQL methods and one can defined the body of OQL methods. By default, a method is executed on the server side.
A method argument can be any basic type, reference on a composite type or mono-dimensional array of basic or composite type. An argument can be in, out or inout. Argument may be named or unnamed (only type is given), for instance:
class C1 {
  attribute string c1;
};

class C2 {
  attribute string c2;
  int perform(in int size, in string str, out double, in C1 &, inout C2 &);
};
Note that the & symbol may be replaced by the * symbol or no symbol as anyhow only a persistent object (not a litteral) may be passed to a method call.

The C::perform method must be defined in C++ but may be called from OQL or a C++ client. To define a C++ method, refer to the document C++ Binding.

Methods can be overloaded (same name but different signatures), for instance:
class C2 {
  attribute string c2;
  int perform(in int size, in string str, out double, in C1 &, inout C2 &);
  int perform(in double, out string mystr);
};
One can define OQL methods in ODL. In this case, the name of the arguments must be given:
class C2 {
  attribute string c2;
  int append(in string s)
  %oql{
    this.s2 += s;
    return strlen(this.s);
  %};
};
The OQL this variable denotes the calling instance.

A method can be an instance method (the default) or a class method (equivalent to C++ or Java static methods). To defined a class method, there are two constructs, using the keyword static or classmethod:
class C {
  static int perform1(in string); // or
  classmethod int perform2(in string);

  instmethod int perform3(in string); // <=> int perform3(in string)
};
If you want to execute a method on the client side, you must use the keyword client as follows:
class C {
  instmethod<client> int perform1(in string);
  classmethod<client> int perform2(in string);

  instmethod<server> int perform3(in string);  // <=> int perform3(...)
  classmethod<server> int perform4(in string); // <=> classmethod perform3(...)
};


Triggers

Triggers are server methods which are executed when a particular event occurs on an object: before or after creation, update, load or delete.
Like methods, a trigger can be written in C++ or in OQL. On the other hand a trigger has no argument but has a name;
class C {
  attribute string s;

  // C++ triggers
  trigger<create_before> c_b();
  trigger<create_after> c_a();

  trigger<update_before> u_b();
  trigger<update_after> u_a();

  trigger<load_before> l_a();
  trigger<load_after> l_b();

  trigger<remove_before> r_b();
  trigger<remove_after> r_a();

  trigger<create_before> c_b2(); // one can have several create_before triggers

  // OQL trigger
  trigger<create_before> l_a2()
  %oql{
    if (strlen(this.s) > 100)
      throw "invalid length";
  %};
};


Indexes

Indexes can be either defined in ODL or with the tool eyedbidxadmin. To define indexes on attributes:
class C {
  attribute string s;
  attribute int i;

  index on s;
  index on i;
};
Note that we cannot define one index on several attributes.


Index and inheritance propagation

As constraints, indexes may be or not propagated to subclasses. The behavior is the same as for constraints: indexes are propagated by default to subclasses:
class C {
  attribute string s;
  attribute int i;

  index on s;
  index on i;
};

class C2 extends C {
  attribute long l;
  
};
Indexes are created for C::s, C::i, C2::s and C2::i.

Note that the index on C::s (resp. C::i is different from the index on C2::s (resp. C2::i).
To forbid propagation:
class C {
  attribute string s;
  attribute int i;

  index<propagate=off> on s;
  index<propagate=off> on i;
};

class C2 extends C {
  attribute long l;
  
};
Indexes are created only for only C::s and C::i.


Index on attribute of literal composite type

One can create indexes on an attribute of a literal composite type, for instance:
class C1 {
  attribute int i;
  attribute double d;
};

class C {
  attribute string s;
  C1 c1; // literal composite type

  index on s;
  index on c1.i;
  index on c1.d;
};


Index specifications

By default, an index on a number attribute (char, short, int, long and double) is implemented as a BTree, while an index on either a string or a bounded string is implemented as a Hash index.
The differences between BTree and Hash are as follows: The ODL index specification allows one to change the default index type and parameters of a given target attribute. To set the type of an index on a given attribute, one uses:
class C {
  attribute string<32> s;
  attribute int i;

  index<type = btree> on s; // default is hash: change to btree
  index<type = hash> on i;  // default is btree: change to hash
};
Important note: one cannot create a BTree index on a non bounded string. One can set implementation parameters for indexes as follows:
class C {
  attribute string<32> s;
  attribute int i;

  index<type = btree, hints = "degree = 64;"> on s;
  index<type = hash, hints = "key_count = 4096; initial_size = 4096;
        extend_coef = 1; size_max = 4096;"> on i;
};

EyeDB manual