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).
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.
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.
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.
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.
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.
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.
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 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.
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.
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.
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.
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.
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.
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.
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> 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.
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.
Object Database Management Group, The Object Database Management Group Standard, 1993
ATLAS Collaboration, ATLAS Computing Technical Proposal, CERN/LHCC 96-43, Dec1996.

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

|