4ODS Tutorial

Mike Olson
Revision 1.0 (Initial Draft) [MCO]

1. Introduction
2. Databases and Transactions
3. Object Definition Language (ODL)
4. Objects and pstubs
5. Extensions and Inheritance
6. Binding and extents
7. Collections
8. Relationships and Object attributes
9. Type Definitions
10. Literal Data types
10.1. Enumerations
10.2. Structures
10.3. Unions
10.4. dates, Times, Intervals, and Timestamps
11. Operations and Exceptions
12. Object Query Language (OQL)
13. Object Interchange Format (OIF)
14. Advanced Usage
14.1. Overridding pstubs
14.2. Overridding literals
14.3. Multithreaded applications

Introduction

This document will attempt to walk users of 4ODS through many of the common use cases of working with 4ODS. Each section will describe using a different facet of the library. Each will have a coresponding file of example code that will be referenced and explained.

Databases and Transactions

4ODS is an object database wrapper around various forms of back end persistent stores. Someday it will have it own storage capabilities, but until then we build off of some of the excelent RDBMS available. Currently we do support connecting to Postgres, Oracles through optimized adapters, and any ODBC compliant database through the ODBC adapter.

Also, because not everyone has Oracle, or Postgres, 4ODS ships with a Dbm driver. This driver works great for simple tasks, but it is not concurrent so don't use it in a multi-threaded application.

To set which driver 4ODS uses, you define the environment variable FTODS_DB_DRIVER to the name of the database adapter. The default is Dbm. To set the driver to Postgres you would issue the following:

export FTODS_DB_DRIVER=Postgres

or on windows

set FTODS_DB_DRIVER=Postgres
    

4ODS has a database object that conforms to the ODMG Database interface. To access the database, import the module Database and create an instance of the database. The database instance is basically a transaction factory. In a ODMG compliant database, there is only on active transaction per thread. This can get a bit confusing some times and we will cover it in more detail in a later sections.

from Ft.Ods import Database

db = Database.Database()
db.open("DBNAME")


db.close()
    

You must be very careful to close your database when you are done with it in Python 1.5.2 because it contains circular references with all of the transactions that it creates. In the above code, we created a Database instance, and opened it. The parameter to the open command is the name of the database. The above code will run even if you do not have a database called "DBNAME". This is because 4ODS does not attempt to connect to the database until you begin a transaction.

to create a transaction, you use the "new" interface on a opened database. At this time you will need to make sure that you don't have the environment adapter envinroment variable set, and the Dbm database directory exists. The location of where Dbm will store you data can be found with the following script. You will need to make sure this directory exists and that you have access to it.

[molson@penny 4Suite]$ python -c "from Ft.Lib import DbmDatabase;print DbmDatabase.DATABASE_DIR"
   

You will then need to create an empty 4ods database. To do this you can use the command line command. Unless otherwise specified in this tutorial, we will use the name "test" as the name of our database. We will also be reinitializing the database between each demonstration script.

Create a new Database
4odb create test
    
Destroy an existing Database
4odb destroy test
    

With a new database, we can run the tutorial script transaction.py

transaction.py

import sys
from Ft.Ods import Database

db = Database.Database()
db.open(sys.argv[1])

tx = db.new()
tx.begin()

#This is where you do all of your transaction processing

tx.commit()
db.close()

   

We would then put our code to access and modify the database inbetween the begin and commit of the transaction. If, after doing your processing, you do not want to commit you changes to the persistent store, you can call abort on the transaction.

Object Definition Language (ODL)

Before we can use a 4ODS database, we must define what types of objects can be stored in the system. This is very similar to defining your table structure in a RDBMS, or you interfaces in a CORBA system. To perform this task, we use a langauge called ODL. This is very simialr to CORBA's IDL. With it, we can define interfaces, classes, structures, enumerations, unions, collections and more. From this, 4ODS will initialize the database's meta model, and generate a series of "pstubs" (more on these later)

One of the most powerful features of a ODMG compliant database is its ability for introspections. Stored in the database is the entire set of meta relationships defined by the ODL that was used to create the database. A standard set of interfaces is defined for accessing (and on really impressive ODMG systems modifying) the meta model of the system run time. It goes as far as allowing you to use these meta objects in the same transactions as other the pstub objects.

simple.odl
module Test {

  class Person {
    attribute string name;
    attribute short age;
  };
};
   

The above file, simple.odl, is a very simple example of an ODL file, but it will sufice for our current purposes. In it, we define a module and a class. The module is used to scope names, and the class defines an object type that can be stored in the database.

To initialize the "test" database with this ODL, we use the command line script 4odb init. Below is a sample output from a Unix terminal when this is run (Windows output is very similar):

[molson@penny tutorial]$ 4odb destroy test
[molson@penny tutorial]$ 4odb create test
[molson@penny tutorial]$ 4odb init test simple.odl 
   

4ODS comes with a command line tool that allows you to view a 4ODS databases meta model. The command is called "4odb metadig". It takes one parameter, the name of the database to view. Once run, it will prompt you for a name to resolve in the metamodel, then it will pretty print that object. Now is probably a good time to note that the ODMG specification defines that all user objects (defined through ODL) are in a modele called ODLMetaObjects. There for, when we run the metadig command and query the object "ODLMetaObjects" we see a module, with our module (and class) defined inside of it

Output from 4odb metadig test
[molson@penny tutorial]$ 4odb metadig test
4ODS MetaDig Tool
Version 0.11.1b2 Copyright Fourthought, Inc 2001
>>> ODLMetaObjects
Module:  ODLMetaObjects
  Name:  ODLMetaObjects
  Comment:  Created by Initialization of Repo
  Defined In: Repository id(2)
  Defines:
    Module:  ODLMetaObjects::Test
      Name:  Test
      Comment:  
      Defined In: ODLMetaObjects id(21)
      Defines:
        Class:  ODLMetaObjects::Test::Person
          Extent:  None
          Keys: None
          Extender: None
          Extensions:
            None
          Inherits: None
          Derives: None
          Of Collection:
            None
          Of Specifier:
            None
          Of Union:
            None
          Of Operation:
            None
          Of Property:
            None
          Of constant:
            None
          Of TypeDefs:
            None
          Defines:
            Attribute:  ODLMetaObjects::Test::Person::name
              is_read_only: 0
              type:  string
              Name:  name
              Comment:  
              Defined In: ODLMetaObjects::Test::Person id(23)
            Attribute:  ODLMetaObjects::Test::Person::age
              is_read_only: 0
              type:  short
              Name:  age
              Comment:  
              Defined In: ODLMetaObjects::Test::Person id(24)
>>> 
    

You are not limited by the command line if you want to view the meta model of a database. The Database interface defines a method called "schema". We can use it to get the root of the Meta Model. The next example script metadata.py shows how to do this. It then recursively prints out all of the classes and modeules in the system.

metadata.py
import sys
from Ft.Ods import Database
from Ft.Ods.MetaData import MetaKind

def pprint(ds,indent = ''):
    print indent + ds.absolute_name()
    for d in ds.defines:
        if d.meta_kind in [MetaKind.mk_module,
                           MetaKind.mk_class]:
            pprint(d,indent + '  ')

def run():

    db = Database.Database()
    db.open('test')

    tx = db.new()
    tx.begin()

    repo = db.schema()
    pprint(repo)

    tx.abort()
    db.close()


if __name__ == '__main__':
    run()

   
metadata.py Output
[molson@penny tutorial]$ python metadata.py 
Repository
  ODLMetaObjects
    ODLMetaObjects::Test
      ODLMetaObjects::Test::Person
  ODLTypes
   

Objects and pstubs

Extensions and Inheritance

Binding and extents

Collections

Relationships and Object attributes

Type Definitions

Literal Data types

Enumerations

Structures

Unions

dates, Times, Intervals, and Timestamps

Operations and Exceptions

Object Query Language (OQL)

Object Interchange Format (OIF)

Advanced Usage

Overridding pstubs

Overridding literals

Multithreaded applications