O2 OODB System
The goal of this file is to help you get started with O2.
Please note that the syntax in some of the examples might need minor adjustment before they will work with the current version of O2. If you find any errors, or places which are unclear, or if you have any suggestions or comments, please let us know (email to Pratik - pmukhopa@cs.ucsd.edu). Your help is greatly appreciated.
You will be able to run O2 only from the uape machines uape-1 through uape-50. O2 will not run on ieng9, sdcc5, or other accounts that you might have. Only one O2 server can be running on a single machine at a time. Because of this limitation on running O2, it is really important for you to remember to terminate your usage with O2 at the end of your session, by typing "o2done". Failure to do so will result in one less copy that can be run, therefore increasing the contingency of usage. So, please do not forget to type "o2done" when you are ready to take a break. Exit or logout from your account will not release O2 license automatically. Please be considerate of other groups. When all fifty machines (uape-1 - 50) are occupied, if someone is not using O2, you can still remotely login to them and start running O2.
*******************************************************************
*** Make sure to type "o2done
"
before
logout!
*******************************************************************
Execute "prep cs132w" to access the following aliases for O2 system:
Run this script ONCE when you start O2
for the FIRST TIME. If for some reason you would like a fresh start you can run
the command again. In return, you will get some info and a question
like:
overwrite system o2_cs132w3 (y/n)
[n] ?
Answering 'y' will clean out your database.
Starts O2 server on the specific
machine. You will get this feedback:
o2server is up and running !
Whenever you login and want to work on O2, you'll
need to first start up the O2 server on your machine. O2 allows only one server
to run on a machine at a time.
Once you have the server started up, you can start up the O2 client. Client is what you will actually be running your O2 commands in (like Ingres' sql command interpreter).
o2done
When you're done using O2 for the day and want to log out, be sure to quit the O2 server by typing "o2done"!It's very important that you shutdown the O2 server on your machine before you log out, or else the next person logging in won't be able to run O2. If you ever get an error message telling you that an O2 server is already running, this means that either you have an O2 server already running, or someone failed to quit their O2 server properly before they logged out. Try using the termination command as described above. Remember, it's important that you quit the server before you log out.
To execute an O2 command(s), end each command with a carriage return, and then hit Ctrl-D (like Ingres' "\g" command).
Use semicolons (";") to separate O2 commands.
Comments are enclosed with /* */ like in C/C++.
Some help on the syntax is available from within the O2 client interpreter by typing "help" followed by an O2 keyword. For example, to find the syntax for creating a class, type "help class".
To run/load a text file containing O2 commands, use the include
syntax (#):
# "myo2file.txt"
To quit, in a new line, type:
quit
An O2 database consists of a O2 schema and a O2 base. The schema contains important information about the structure of the data such as class information, their attributes and methods, etc. The base contains the actual data that comply to that schema. A schema can have multiple bases. At a time one always works with a specific schema-base pair.
In order to work on a database, a schema and a base have to be selected. At
the beginning an empty schema and an empty base must be created:
create schema YourSchemaNameHere;
create base YourBaseNameHere;
Then, tell O2 which schema that you want to use:
set schema
YourSchemaNameHere;
Now, you can define your
classes and methods for the schema (see "definitions" parts). When done with defining
classes, confirmation is required before one can start using such
classes:
confirm classes;
Whenever you add new classes, you'll need to confirm your
classes again.
You can now tell O2 which base you want to
use:
set base YourBaseNameHere;
Finally, we're ready to use defined classes and methods.
Later on, when you start up O2, just do:
set schema
YourSchemaNameHere;
and
set base
YourBaseNameHere;
to use your previously setup schema and base
(including your classes, persistent names etc).
Common error situations
Examples
O2C language is a superset of C++ with an extended type system. This is the language we use to define classes, to write the body of their methods and to build application programs. The O2 types that can be manipulated using O2C are the following:
Atomic Types
boolean | char | string | integer | real
For each atomic types, standard operations such as
+ are defined.
Collection Types
Collection types are used to manage collections (sets) of objects of other types.
set( < type > ) | unique_set( < type > ) | list( < type > )
A set is a disordered collection of values of the base type. The same value can occur several times in the set.
Tuple Types
Tuple types are used to compose instances of other (simpler) types into records.
tuple( < attrib1 > : < type1 > , < attrib2 > : < type2 > , ... )
Components (attributes) of the tuple are named and can be accessed.
Class definitions include:
First, let's declare a Person class with information about a person such as their name and age.
class Person
public type tuple (
name: string, /* data members */
age: integer
)
method /* method
declarations */
public init (n: string, a:
integer),
public
print,
public get_age :
integer
end;
The first public designates that the data members are publicly accessible. If it is omitted, then all data members are private to the class. Methods can be declared as public, private or read. Note that the "print" method takes no arguments and doesn't return anything. The "init" method is similar to a class constructor in C++. This method gets called when you create an object of this type. For instance:
o2 Person Robert = new Person("Robert", 23); /* See local variables part below */
Suppose we want to create a TA class. A TA should be a Person, also he/she also receive a salary. Inheritance helps us understand the relationship between classes:
class TA inherit Person
public type tuple
(
salary: real
)
method public
get_salary : real
end;
Another class, namely Tutor, is different in the sense that each Tutor has several students (a set of students) whom he/she is responsible for. Associated with each student, there is a name, and mount of fee he/she needs to pay the Tutor.
class Tutor inherit Person
public type tuple
(
students: set( /* "students" is a set of ordered
pairs */
tuple(
name:
string,
fee:
real
)
),
salary:
real
)
method public
get_salary :
real
end;
Now, suppose we want to describe a person who is both a TA and a Tutor. O2 supports multiple inheritance:
class TATutor inherit TA, Tutor
public type tuple
(
total_salary: real
)
method
public
compute_salary
end;
You can display your classes with the "display" command:
display classes; /* List all classes
*/
display class MyClassName; /* Show a specific class
*/
For local variable declarations, use the keyword "o2":
o2 real total= 0.0;
o2 tuple(name:
string, fee:real) curr_student;
o2 Person per = new Person;
When you declare variables that represent class objects (like the "per" variable above), they're always pointers, like in Java, not the actual objects. You must use "new" to create objects. Since the object vars are pointers, you must use notation "->" instead of "." to access the data members and methods:
per->name =
"Joe";
per->age = 23;
The data that we created using local variables will disappear once the piece of code that contains them finishes.
When we want to keep data around, we need to make it persistent by using O2's "name" keyword. You can think of these as global variables in C/C++, except that this global data will stick around, even if you quit O2 and later on restart O2 session. This is useful for when we want to start storing objects in our database. For example, if we wanted to store a set of people, we'd declare a persistent set:
name People : set(Person);
name CombinedSalaries
: real;
You might want to define the persistent names of your database before you define the body of the class methods, because you might need to use these persistent names in the body of these methods. Also remember that names are created in the schema, not the base.
With each class, methods are associated to manipulate the value of the object. A method can have parameters that are of an arbitrary O2 type and can have a return value. The types of the parameters and the return type form the method signature. The implementation of method is the method body which is written in O2C.
Let's define the initialization method for the Person class:
method body init (n: string, a: integer) in class
Person {
self->name = n;
self->age = a;
}
The reserved word "self" is the similar to the "this" pointer in C++. Use "self" to refer to data members within your class methods. O2 also has a "printf" function similar to "printf" in C.
method body print in class Person
{
printf("Name: %s\n",
self->name);
printf("Age: %d\n",
self->age);
};
method body get_age : integer in class Person
{
return self->age;
};
Let's define the get_salary method for the Tutor class. This method should compute a Tutor's salary by adding up the fees for all of the Tutor's students.
method body get_salary : real in class Tutor
{
o2 real total = 0.0; /* Precede local var declarations with "o2"
*/
o2 tuple( name: string, fee: real ) curr_student;
for ( curr_student in self->students )
{
total += curr_student.fee;
}
self->salary = total;
return
total;
};
In the above example, every element of the "students" set has been iterated through by the 'for' loop. In this case, each element is a tuple <name, fee> (note that you have to use the same tuple type as in the class definition). The current element being iterated is stored into the local variable, "curr_student". The "curr_student" variable is a tuple, which is basically like a struct. It's not a pointer (like "self"), so we use the . operator to access the tuple's data members (eg, curr_student.fee).
In the above example we could also use object disencapsulation to get the students of a "Tutor" object by using the "*" operator, which returns the value associated with an object:
curr_student = (*self)->strudents; /* assuming that curr_student is declared as a set of tuples */
O2 supports math (* + - / += etc) and boolean (&& || == != etc) expressions like C/C++. Note that the object comparison operator "==" implements the notion of "deep equality" and compares the whole structure of the objects (atomic, collection or tuple type attributes) and not only the atomic type attributes of them.
Next, for the class TATutor, we want to define the compute_salary method to add up the person's salaries as a TA and Tutor. However, both classes (TA and Tutor) use the same name for their salary methods, "get_salary." We can refer to each method by using the @ operator. The @ operator is like the scope operator (::) in C++.
method body compute_salary in class TATutor
{
self->total_salary = self->TA@get_salary +
self->Tutor@get_salary;
};
For access data members with the same name, such as "salary" in the TA and Tutor classes, it's different. We need to rename the data members in the TATutor class:
class TATutor inherit TA, Tutor
rename
attribute salary from class TA as TA_salary,
attribute salary
from class Tutor as Tutor_salary
public type tuple
(
total_salary: real
)
method
public
compute_salary
end;
Then we can implement compute_salary this
way:
method body compute_salary in class TATutor
{
self->total_salary =
self->TA_salary +
self->Tutor_salary;
};
Now that we have some classes and methods defined, we want to write some code that'll do something with these. You can write and execute O2C code in two ways.
The first one is to write and run some piece of code using "run body":
run body {
o2 TA someTA = new
TA;
o2 real someSalary = 0.0;
someTA->name =
"Joe";
someTA->age = 23;
someTA->salary =
100.0;
someSalary =
someTA->get_salary;
someTA->print;
printf("$%.2f ==
$%.2f\n", someTA->get_salary, someSalary);
};
Remember that the data that we created in the above "run body" were local variables and that they will disappear once that piece of code finishes.
The second and more organized way is to create O2 applications that are collections of programs and variables. They make your code easier to write and run:
application
PeopleAdder
program public
add_Person,
public add_TA,
public
add_Tutor,
public add_TATutor
end;
You can then define the application's programs "add_Person" and so on.
/* This program modifies persistent data, namely
the People set */
transaction body add_TATutor in application PeopleAdder
{
o2 TATutor newTutor = new TATutor;
if
(newTutor->edit)
People +=
set(newTutor);
};
We added a "TATutor" object to the set "People" by adding (+=) a set containing the new "TATutor" object. If we want to remove an object from the "People" set we can use the "-=" operator. Also, if we want to update an object in the "People" set we can remove it from the set (-= ), update it using the local variable (Tutor) and then add it back to the "People" set (+= ).< /FONT>
Also note that the set "People" is a set of objects of type "Person". We can adda "TATutor" object to this set, because the class "TATutor" is a subclass of the "Person" class and so they are type compatible.< /FONT>
Because the above program modifies a persistent object ("People"), we must use the "transaction" keyword. If you have a program that doesn't make any modifications, (eg, some program that displays information only), then use the keyword "program" instead of "transaction" at the beginning of the program definition.
Once you have your application and its programs defined, you can run
a program of the application by using the "run" command:
run
program add_TATutor in
application PeopleAdder;
Deleting objects
You can delete unwanted schemas, bases ,classes, persistent variables and applications:
delete application PeopleAdder;
delete name
People;
delete class Person;
delete base b;
delete
schema s; /* You can't delete a schema if it has a base associated with it
*/
Change directory executing the following:
cd
/software/nonrdist/o2_5.0/manuals
You can access the O2 Beginner’s Guide by opening the following
file:
acroread o2cbegui.pdf
You can access the O2C Reference by opening the following file:
acroread o2cref.pdf