SLAC PEP-II
BABAR
SLAC<->RAL
Babar logo
HEPIC E,S & H Databases PDG HEP preprints
Organization Detector Computing Physics Documentation
Personnel Glossary Sitemap Search Hypernews
Unwrap page!
Computing Search
Who's who?
Meetings
FAQ Homepage
Archive
Environment
Online SW
Offline
Workbook
Simulation
Reconstruction
Data Distribution
Beta
Beta Tools
Event display
Code releases
Databases:
Hot Items!
About Us
Meetings
General DB info
Conditions DB
Event Store
Online DB
Links
Check this page for HTML 4.01 Transitional compliance with the
W3C Validator
(More checks...)

BABAR Database
Coding Guidelines and Hints

BABARDatabase Group

 

Version Information

Draft: 22nd June 1998

This document is still under development. If you have any questions or comments, please address them to David Quarrie (DRQuarrie@LBL.Gov).

Table of Contents


Introduction

This document describes coding guidelines and hints for BABAR database code developers. It is not meant to be read in isolation, but builds upon several other documents that the developer should also read. These include:

In addition, these guidelines reference the Object Database Management Group standards document ODMG V2. One of the goals of these guidelines is to reduce the direct dependency of our applications on Objectivity, and to allow us to extend their API by providing BABAR-specific customizations and performance measuring hooks.

Return to Table of Contents.


The Bdb Prefix and Bdb.hh Header file

All software packages, classes, typedefs and macros that are part of the BABAR database should use the Bdb prefix. The Bdb.hh header file should be included in all DDL (.ddl) and header (.hh) files, thus:

#include "BdbUtil/Bdb.hh"

The API that's defined by this header file is not 100% complete, but is meant to address the needs of most database programmers. In some cases, more sophisticated developers will need to directly access Objectivity capabilities.

Return to Table of Contents.


Basic Datatypes

Data members in persistent objects should only be basic datatypes as defined by the ODMG, or compound types (classes or structs whose data members themselves conform to the same restrictions). The ODMG basic datatypes are:

d_Char        (8 bit signed)
d_Octet       (8 bit unsigned)
d_Short       (16 bit signed)
d_UShort      (16 bit unsigned)
d_Long        (32 bit signed)
d_ULong       (32 bit unsigned)
d_Float       (32 bit float)
d_Double      (64 bit float)
d_Boolean     (Boolean having values d_True or d_False)
d_String      (not yet fully supported???)

Return to Table of Contents.


The BdbPersObj persistent object class

All persistent objects must inherit from the BdbPersObj class, which has identical semantics to the Objectivity ooObj class, of from a descendant class thereof.

Example:

File MyPkg/MyClass.ddl
#include "BdbUtil/Bdb.hh"

class MyClass: public BdbPersObj {
    [....]
};

Return to Table of Contents.


Other persistent object classes

The following persistent classes have identical functionality to their Objectivity counterparts:

BdbContObj          Container (ooContObj)
BdbDBObj            Database (ooDBObj)
BdbFDObj            Federated Database (ooFDObj) 
BdbMap              Map (ooMap)

Example:

[example missing]

Return to Table of Contents.


Object References - BdbHandle(T) and BdbRef(T)

These correspond to the Objectivity macros ooHandle(T) and ooRef(T) and have the identical semantics. For example, the persistent new operator (or theBdbNew(T) macro described below) should only be used in conjunction with BdbHandle(T), never in conjunction with BdbRef(T).

Example:

// Newly created persistent objects should be associated with a BdbHandle
BdbHandle(MyClass) myObject = BdbNew(MyClass);  // The BdbNew macro is described in a later section

Return to Table of Contents.


Generic Object References - BdbHandleAny and BdbRefAny

These are a shorthand for BdbHandle(BdbPersObj) and BdbRef(BdbPersObj) respectively. They are provided purely for the convenience of the developer.

Return to Table of Contents.


Object Creation - BdbNew(T)

Object creation is handled by the persistent new operator or the BdbNew(T) creation macro. The former explicitly uses a placement hint object to determine the placement of the newly created object, whereas the latter is used in confunction with the BdbHint hint specification macros.

Note that persistent objects can only be created during an update mode transaction.

Example:

// Newly created persistent objects should be associated with a BdbHandle
BdbHandle(MyClass) myObject = BdbNew(MyClass); 
BdbHandle(MyOtherClass) myOtherObj = BdbNew(MyOtherClass)(arg1, arg2);
BdbHandle(YetAnotherClass) yetAnotherObj = new( myOtherObj ) YetAnotherClass;

Return to Table of Contents.


Object Deletion - BdbDelete(T)

The BdbDelete(T) should be used to delete an instance of a persistent class.

Note that persistent objects can only be deleted during an update mode transaction.

Example:

BdbHandle(MyClass) myObject
[...]
BdbDelete(myObject);

Return to Table of Contents.


Object Placement - BdbClusteringHint classes

The BdbAbstractClusteringHint class is an abstract class that can be used as the base for implementing different clustering or placement strategies. These strategies determine where newly created objects are placed in the federated database, appropriate placement offering performance gains by minimizing the number of database files and pages that need to be transferred for subsequent accesses. This abstract base class declares the pure virtual function:

virtual BdbRefAny hint( ) = 0;

This function should be defined by any concrete descendant, returning the location where the object should be created. This is typically the location of an already existing object.

One object creation strategy is implemented by the BdbClusteringHint class, which supports both a simple and complex strategy by way of a delegate. The BdbHint and associated macros described in the next section utilize this clustering hint class.

Another strategy is implemented by the BdbEvsClusteringHint class. This is the main event store clustering class and implements clustering across multiple file systems, supporting simultaneous object creation by multiple processes. It is designed to be used as a delegate of the BdbClusteringHint class.

Return to Table of Contents.


Object Placement - BdbHint macros

A set of macros is available to specify and manage class-based clustering where objects of the same class type are clustered together. It is designed to be used in conjunction with the BdbNew(T) macro. This facility also allows this strategy to be adjusted at run-time via the use of delegate clustering hint objects.

The macros are:

BdbHintDeclare   

Clustering Hint declaration macro. This should appear in the DDL (.ddl) file for a persistent class.

BdbHintInit(T)

Clustering Hint initialization macro. This should appear in the implementation (.cc) file for a persistent class. The macro argument, T, is the name of the class.

BdbHintSet(T,V)

Set the clustering hint for the class T to the specified persistent object V. This implements a simple strategy whereby subsequent objects of classT will be created in the same container as the specified existing object. Note that V can be of type BdbHandle(BdbDatabase) or BdbRef(BdbDatabase), in which case subsequent objects of typeT will be created in the default container of the specified database.

BdbHintSetDelegate(T,D)

The macro allows a more complex placement strategy to be setup, whereby a clustering hint object inheriting from BdbAbstractClusteringHint may be specified as the delegate of the clustering hint.

Example:

File MyPkg/MyClass.ddl
#include "BdbUtil/Bdb.hh"

class MyClass: public BdbPersObj {
public:
    BdbHintDeclare;
    [...]
    void myFunc( d_Long arg );
};
File MyPkg/MyClass.cc
#include "MyPkg/MyClass.hh"

BdbHintInit(MyClass);

// Function Members etc.

void MyClass::myFunc( d_Long arg ) {
    [....]
};
File MyPkg/MyClient.cc
#include "BdbEvent/BdbEvent.hh"
#include "BdbEvent/BdbEvsClusteringHint.hh"
#include "BdbEvent/BdbEventStore.hh"

    BdbEventStore* theStore = BdbEventStore::instance( );

    if ( 0 == getenv( "BDBSIMPLE" ) ) {

    // Implement a complex clustering strategy based on a delegate
        theHint = new BdbEvsClusteringHint;
        theHint->setComponent( BdbEvsClusteringHint::Evt );
        BdbHintSetDelegate( BdbEvent, theHint );
    } else {

    // Implement a simple strategy based on a single database & container
        theStore->setDb( "Events" ); 
        BdbHintSet( BdbEvent, theStore->container( "Events", 0 ) );
    }

Return to Table of Contents.


Modifying a Persistent Object

It is not sufficient to start an update transaction and to modify data members within a persistent object to ensure that the database is updated with these modifications. The object itself must be placed in update mode before its data members are modified. It is possible that a request to enter update mode may fail if such a request conflicts with other ones from other clients. There are two mechanism for this, depending on whether the data member is an embedded object or simple data type (e.g. d_Long, d_Boolean) or an extensible ooVArray of such objects or basic datatypes.

  • If the data member to be modified is an embedded object or basic datatype, the function BdbUpdate( ) (or the Objectivity ooUpdate( ) function which is equivalent) must be called. This returns a BdbStatus value that should be tested against BdbcSuccess to determine whether the request was successful.
  • If the data member to be modified is an ooVArray of objects or basic datatypes, the function update( ) must be called on the data member itself. This returns a BdbStatus value that should be tested against BdbcSuccess to determine whether the request was successful. Note that this automatically performs an implicit BdbUpdate( ) on the persistent object itself.

These rules essentially mean that any modifier functions or any operation function on a persistent object must begin with an update of the object.

Example:

#include "BdbUtil/Bdb.hh"

class MyClass: public BdbPersObj {
public:
    void setData( d_Long newValue );
    void setElem( d_ULong index, d_Long newValue );
private:
    d_Long _data;
    ooVArray(d_Long) _array;
};

void
MyClass::setData( d_Long newValue )
{
    if ( BdbcSuccess == BdbUpdate( ) ) {
        _data = newValue;
    } else {
        // Update failed - signal an error
    }
}

void
MyClass::setElem( d_ULong index, d_Long newValue )
{
    if ( BdbcSuccess == _array.update( ) ) {
        _array[index] = newValue;
    } else {
        // Update failed - signal an error
    }
}

Notes:

Return to Table of Contents.


Status Codes

The recommended structure for function return values is to use the BdbStatus return type. This can take two values - BdbcSuccess and BdbcError. The former corresponds to a successful completion, and the latter to an unsuccessful completion. BdbcSuccess can also be treated as a boolean true, and BdbcError as a boolean false.

Example:

Return to Table of Contents.


Error Handling

Each package associated with the BABARdatabase is assigned a range of error codes which may be used to signal errors. The basic error signalling routine is:

    BdbStatus BdbSignal( BdbErrorLevel theLevel, BdbError theErrorCode, ???, char* arg1, char* arg2,...)

Where the Error Level can be one of BdbcUserError, BdbcSystemError, BdbcFatalError, the Error Code is assigned to the package as described below, [need to understand the 3rd argument] and there can be several optional char* arguments.

A single header file is used to asign a range of error codes to each package. The header file is BdbUtil/BdbErrors.hh, an extract from which is shown below:

File BdbUtil/BdbErrors.hh
const d_ULong BdbErrorBase  = 2000000;
const d_ULong BdbErrorRange = 1000;

// Error numbers for known packages
const d_ULong BdbErrors = BdbErrorBase;
const d_ULong BdbUtilErrors = BdbErrors + BdbErrorRange;
[...]
const d_ULong BdbAccessErrors = BdbCondErrors + BdbErrorRange;

Each package is represented by a single line of the form:

const d_ULong <Pkg>Errors = <PreviousPkg>Errors + BdbErrorRange;

Where <Pkg> is the name of the package and <PreviousPkg> is the name of the previous package in the list. New packages are added to the end of the list.

Within each package, the specific error codes are assigned by a pair of error files, <Pgk>Errors.hh and <Pkg>Errors.cc. The format for these can best be seen from the following example taken from the BdbEvent Package.

File BdbEvent/BdbEventErrors.hh
#ifndef BDBEVENTERRORS_HH
#define BDBEVENTERRORS_HH

#include "BdbUtil/BdbErrors.hh"

extern BdbError BdbEventErrAmbiguity1;
extern BdbError BdbEventErrCannotCreateObj;
[...]
extern BdbError BdbEventErrValueOutOfRange;

#endif /* BDBEVENTERRORS_HH */

File BdbEvent/BdbEventErrors.cc
#include "BdbUtil/Bdb.hh"
#include "BdbEvent/BdbEventErrors.hh"

BdbError BdbEventErrAmbiguity1      = { BdbEventErrors + 1, "%s(): Ambiguity: BdbEvsStructureMgr::..."};
BdbError BdbEventErrCannotCreateObj = { BdbEventErrors + 3, "%s(): Cannot create object %s"};
[...]
BdbError BdbEventErrValueOutOfRange = { BdbEventErrors +47, "%s(): Specified value: %s out of range %s"};

The following code fragment issues a error message corresponding to the BdbEventErrCannotCreateObj error. The BABAR convention is to use the first optionalchar* argument to identify the name of the class and member function where the error occurred.

return BdbSignal(BdbcUserError, BdbEventErrCannotCreateObj,0, "BdbEvsSmart
Component::createParameterObject", paramObjName() );

Return to Table of Contents.


The BdbDebug debug output classes

Two levels of diagnostic printout may be controlled through the BDBDEBUG1 and BDBDEBUG2 environment variables. Level 2 includes all printout from level 1 as well, but level 1 excludes level 2 printout. The appropriate environment variable should be set to the name of a file, or to cout or cerr (corresponding to standard output or standard error respectively), thus:

> setenv BDBDEBUG1 cerr

The mechanism by which diagnostic printout is issued is illustrated by the following code fragment:

#include "BdbEvent/BdbEventStore.hh"

BdbEventStore* theStore = BdbEventStore::instance( );
theStore->debug->out1 << "This is an example of Level 1 diagnostic printout" << endl;
theStore->debug->out2 << "This is an example of Level 2 diagnostic printout" << endl;

Return to Table of Contents.


The BdbRWVector<T> persistent vector class

The BdbRWVector<T> class is a templated, persistent-capable vector class, having an interface similar to the Rogue Wave vector class (need to look this up). The public interface is:

BdbRWVector( );
BdbRWVector( size_t len );
BdbRef(T)& operator()(uint32 index);
BdbRef(T)& operator[](uint32 index);
void insert (const BdbRef(T)& newElem); 
void append (const BdbRef(T)& newElem); 
void replace(const BdbRef(T)& newElem, size_t pos); 
size_t length() const;
size_t entries() const;
size_t size() const;

int isEmpty() const;
void clear();
void reshape(size_t newSize);
void resize(uint32 newSize);

[This would be better recast into the ODMG basic datatypes - e.g. d_Boolean instead of int etc. Need to discuss this with RD45.]

Return to Table of Contents.


Pitfalls

Embedded classes

Persistent classes cannot contain other persistent classes, only references to them. Transient classes can be directly contained in persistent classes. The following examples demonstrate this:

class AAA {
[...]
class BBB : public BdbPersObj {
 // The following is allowed
 AAA a;
};

class CCC : public BdbPersObj {
 // The following is not allowed!
 BBB b;
 // The following is allowed
 BdbRef(BBB) bb;
};

Embedded C++ Pointers

Persistent classes cannot contain embedded C++ pointers.

Transient object instances

Avoid creating transient objects of a persistent capable class

Converting a transient class to persistent

When converting a transient class to persistent, watch out for data members turning into access functions rather than object references.

Do not attempt to convert a C++ pointer into a BdbRef(T) or the converse

Template instantiation problems with DEC C++

Return to Table of Contents.


References

  1. Object Database Management Group, The Object Database Management Group Standard, 1993
  2. ATLAS Collaboration, ATLAS Computing Technical Proposal, CERN/LHCC 96-43, Dec1996.

 

DB Home | BaBar Home | Computing | Reconstruction | Simulation | Search