diff --git a/28PATCHES2013/CoolKernel/CoolKernel/NEW/IField.h b/28PATCHES2013/CoolKernel/CoolKernel/NEW/IField.h new file mode 100644 index 0000000000000000000000000000000000000000..a04c397b2cd2386f3f6f7d8f09b96f65a5486cf2 --- /dev/null +++ b/28PATCHES2013/CoolKernel/CoolKernel/NEW/IField.h @@ -0,0 +1,271 @@ +#ifndef COOLKERNEL_IFIELD_H +#define COOLKERNEL_IFIELD_H 1 + +// First of all, enable or disable the COOL290 API extensions (see bug #92204) +#include "CoolKernel/VersionInfo.h" + +// Include files +#include "CoolKernel/IFieldSpecification.h" +#include "CoolKernel/RecordException.h" + +// Forward declarations +namespace coral +{ + class Attribute; +} + +namespace cool +{ + + //-------------------------------------------------------------------------- + + /** @class IField IField.h + * + * Abstract interface to a data field of user-defined name and storage type. + * + * A field is a transient data value that can be made persistent. + * Its StorageType specification defines constraints on its allowed + * data values (e.g. strings of less than 256 characters), to ensure + * portability across different platforms and persistent backends. + * While each StorageType is associated to a platform-dependent transient + * C++ type and to a backend-dependent persistent (e.g. SQL) data type, + * the StorageType class allows users to write their code to define and + * handles records and fields in a platform- and backend-independent way. + * + * All methods of the IField interface are const except for the two + * methods (setValue and setNull) that allow changes to the data values. + * Implementations of IField are responsible for enforcing the + * StorageType constraints on the data values in these two and all other + * non-const methods, as well as at construction and assignment time. + * The use of const_cast on any const method is highly discouraged + * as it may lead to data corruption inside the IField instance. + * + * The data values at construction time are defined in the concrete + * implementations of the IField interface: default C++ values are + * recommended over nulls for all storage types (0 for numbers and + * chars, false for bool, "" for strings, empty blob for blobs). + * The value of a field is either null or a well-defined true type. + * Once the field has been set to null, its previous true type value + * is lost and the field can only be set again to non-null by setting + * it equal to a true type value using the setValue method: there is no + * such thing as a setNull(false) method to recover the previous value. + * + * Note the special semantics of null values for string variables only. + * For strings, setNull() is now considered equivalent to setValue(""): + * after such calls, isNull() returns false, and data() returns "". + * This has been introduced to avoid inconsistencies for Oracle databases + * (where empty strings are treated as SQL NULL values - see bug #22381). + * **WARNING**: CORAL Attribute's are different from COOL Field's in the + * management of null values. IField::data<T>() throws if IField::isNull() + * returns true, while coral::Attribute::data<T>() does not throw and + * returns an undefined value if coral::Attribute::isNull() returns true. + * Note also that IField::isNull() is always false for strings. + * + * It is not possible to change the name or storage types of a field + * via the IField interface: if necessary, this is the responsibility + * of the concrete classes implementing IField or IFieldSpecification. + * + * Implementations of the IField interface may or may not be based on + * the coral::Attribute class. To simplify the port of user code to the + * new API, an attribute() method is provided to retrieve the contents + * of the IField as a (read-only) constant Attribute reference. + * This is DEPRECATED and may be removed in a future COOL release. + * **WARNING**: in line with was explained above about CORAL Attribute's + * being different from COOL Field's in the management of null values, the + * Attribute reference returned by the attribute() method will behave as + * follows: for all types except strings, isNull() and attribute().isNull(), + * as well as data() and attribute().data(), always return the same value; + * for strings, isNull() always returns false, while data() returns "" + * if either of attribute().isNull() or attribute.data<std::string>()=="" + * are true, but it is not guaranteed that both these conditions are true. + * + * @author Andrea Valassi and Marco Clemencic + * @date 2006-09-28 + */ + + class IField + { + + public: + + virtual ~IField() {} + + /// Return the specification of this field. + virtual const IFieldSpecification& specification() const = 0; + + /// Return the name of this field (from the spec). + const std::string& name() const; + + /// Return the storage type of this field (from the spec). + const StorageType& storageType() const; + + /// Is the value of this field null? + /// For strings, this is always false. + virtual bool isNull() const = 0; + + /// Return the data value of this field (as true type). + /// Throw FieldWrongCppType if the field C++ type is not T. + /// Throw FieldIsNull the field is null, i.e. if isNull() is true. + /// For strings, return "" if setNull() was called. + template<typename T> const T& data() const; + + /// Return the address of the true-type data value of this field. + /// Throw FieldIsNull the field is null, i.e. if isNull() is true. + /// For strings, return a pointer to "" if setNull() was called. + virtual const void* addressOfData() const = 0; + + /// Set the value of this field to null: any previous value is lost. + /// For strings, setNull() is equivalent to setValue(""): data() and + /// addressOfData() will return "" and a pointer to "" after setNull(). + virtual void setNull() = 0; + + /// Set the value of this field to a well defined (non null) true-type. + /// Throw FieldWrongCppType if the field C++ type is not T. + /// For strings, setNull() is equivalent to setValue(""). + template<typename T> void setValue( const T& value ); + + /// Set the value of this field equal to the value of another field + /// (with the same StorageType, but not necessarily the same name). + /// Throw FieldSpecificationWrongStorageType if field has the wrong type. + void setValue( const IField& field ); + + /// Compare the names, types and values of this and another field. + virtual bool operator== ( const IField& rhs ) const; + + /// Compare the names, types and values of this and another field. + virtual bool operator!= ( const IField& rhs ) const; + + /// Print the name, storage type and data value of this field. + virtual std::ostream& print( std::ostream& os ) const; + + /// Print the data value of this field. + /// Print "NULL" if the field is null (print "" for null strings). + virtual std::ostream& printValue( std::ostream& os ) const = 0; + + /// DEPRECATED - added for easier compatibility with COOL 1.3 + /// (this is likely to be removed in a future COOL release). + /// Explicit conversion to a constant coral Attribute reference. + virtual const coral::Attribute& attribute() const = 0; + +#ifdef COOL290 + protected: + + /// Standard constructor is protected (see bug #95823) + IField() {} +#endif + + private: + +#ifdef COOL290 + /// Copy constructor is private (see bug #95823) + IField( const IField& rhs ); + + /// Assignment operator is private (see bug #95823) + IField& operator=( const IField& rhs ); +#endif + + /// Compare the values of this and another field. + /// The values of two fields are equal either if they are both non null + /// and their true type values are equal, or if they are both null. + /// Private method - this does not check that fields have the same type. + virtual bool compareValue( const IField& rhs ) const = 0; + + /// Set the value of this field to a well defined (non null) true-type. + /// For strings, setNull() is equivalent to setValue(""). + /// Throw FieldWrongCppType if the field C++ type is not cppType. + /// Private method - this will crash if the address is not of cppType type. + virtual void setValue( const std::type_info& cppType, + const void* externalAddress ) = 0; + + }; + + /// Print the name, storage type and data value of a field. + std::ostream& operator<<( std::ostream& s, const IField& field ); + + //-------------------------------------------------------------------------- + + inline const std::string& IField::name() const + { + return specification().name(); + } + + //-------------------------------------------------------------------------- + + inline const StorageType& IField::storageType() const + { + return specification().storageType(); + } + + //-------------------------------------------------------------------------- + + template<typename T> + inline const T& IField::data() const + { + if ( this->storageType().cppType() != typeid(T) ) + throw FieldWrongCppType + ( this->name(), this->storageType(), typeid(T), "IField" ); + if ( this->isNull() ) + throw FieldIsNull + ( this->name(), this->storageType(), "IField" ); + return *( static_cast< const T* >( this->addressOfData() ) ); + } + + //-------------------------------------------------------------------------- + + template<typename T> + inline void IField::setValue( const T& value ) + { + this->setValue( typeid(T), &value ); + } + + //-------------------------------------------------------------------------- + + /// Set value to null if the other field is null, else set value from C++ + /// address of the other field's value (as in coral::Attribute::fastCopy). + inline void IField::setValue( const IField& field ) + { + if ( this->storageType() != field.storageType() ) + throw FieldSpecificationWrongStorageType + ( this->name(), this->storageType(), field.storageType(), "IField" ); + if ( field.isNull() ) + this->setNull(); + else + this->setValue( this->storageType().cppType(), field.addressOfData() ); + } + + //-------------------------------------------------------------------------- + + inline bool IField::operator==( const IField& rhs ) const + { + // Compare names, storage types and values + // (NB coral::Attribute::operator== does NOT compare names) + return ( this->specification() == rhs.specification() ) + && this->compareValue( rhs ); + } + + //-------------------------------------------------------------------------- + + inline bool IField::operator!=( const IField& rhs ) const + { + return ( ! ( *this == rhs ) ); + } + + //-------------------------------------------------------------------------- + + inline std::ostream& IField::print( std::ostream& s ) const + { + s << name() << " (" << storageType().name() << ") : "; + return this->printValue( s ); + } + + //-------------------------------------------------------------------------- + + inline std::ostream& operator<<( std::ostream& s, const IField& field ) + { + return field.print( s ); + } + + //-------------------------------------------------------------------------- + +} +#endif // COOLKERNEL_IFIELD_H diff --git a/28PATCHES2013/CoolKernel/CoolKernel/NEW/IFieldSpecification.h b/28PATCHES2013/CoolKernel/CoolKernel/NEW/IFieldSpecification.h new file mode 100644 index 0000000000000000000000000000000000000000..12ea493e2f65ea0b54d08e00c9ada4d15e3b570b --- /dev/null +++ b/28PATCHES2013/CoolKernel/CoolKernel/NEW/IFieldSpecification.h @@ -0,0 +1,117 @@ +#ifndef COOLKERNEL_IFIELDSPECIFICATION_H +#define COOLKERNEL_IFIELDSPECIFICATION_H 1 + +// First of all, enable or disable the COOL290 API extensions (see bug #92204) +#include "CoolKernel/VersionInfo.h" + +// Include files +#include "CoolKernel/StorageType.h" + +// Forward declarations +namespace coral +{ + class Attribute; +} + +namespace cool +{ + + // Forward declarations + class IField; + + /** @class IFieldSpecification IFieldSpecification.h + * + * Abstract interface to the specification of a data field + * of user-defined name and storage type. + * + * A field is a transient data value that can be made persistent. + * Its StorageType specification defines constraints on its allowed + * data values (e.g. strings of less than 256 characters), to ensure + * portability across different platforms and persistent backends. + * While each StorageType is associated to a platform-dependent transient + * C++ type and to a backend-dependent persistent (e.g. SQL) data type, + * the StorageType class allows users to write their code to define and + * handles records and fields in a platform- and backend-independent way. + * + * The IFieldSpecification interface only allows read-only access to + * the specification of a record: appropriate methods to change the name + * or storage type of a field are meant to be defined and implemented in + * the concrete classes derived from it, if at all necessary. + * + * Implementations of IField and IRecord are responsible for enforcing + * the StorageType constraints on the data values in all of their + * non-const methods, as well as at construction and assignment time. + * The IFieldSpecification interface provides a validate() method to + * check if the data offered by a IField complies to the specification. + * + * Implementations of the IField interface may or may not be based + * on the coral::Attribute class. For internal COOL usage, another + * signature of the validate() method is provided to check if the + * data in an Attribue complies to the field specification. + * This is DEPRECATED and may be removed in a future COOL release. + + * @author Andrea Valassi and Marco Clemencic + * @date 2006-09-22 + */ + + class IFieldSpecification + { + + public: + + virtual ~IFieldSpecification() {} + + /// Return the name of this field. + virtual const std::string& name() const = 0; + + /// Return the storage type of this field. + virtual const StorageType& storageType() const = 0; + + /// Compare the names and storage types of this and another field. + virtual bool operator==( const IFieldSpecification& rhs ) const = 0; + + /// Compare the names and storage types of this and another field. + virtual bool operator!=( const IFieldSpecification& rhs ) const = 0; + + /// Check that a given field is compatible with this specification. + /// The field must have the same transient C++ type and a value + /// compatible with the persistent storage type of the 'reference' field. + /// If checkName is true, the field (as well as the attribute accessible + /// via the field) must also have the same name as the reference field (*). + /// Throw FieldSpecificationWrongName if field/attribute name is wrong (*). + /// Throw FieldSpecificationWrongStorageType if field has the wrong type. + /// Throw StorageTypeInvalidValue for values outside the allowed range. + virtual void validate( const IField& field, + bool checkName = true ) const = 0; + + /// DEPRECATED - added for easier compatibility with COOL 1.3 + /// (this is likely to be removed in a future COOL release). + /// Check that a given attribute is compatible with this specification. + /// The attribute must have the same transient C++ type and a value + /// compatible with the persistent storage type of the 'reference' field. + /// If checkName is true, the attribute must also have the same name (*). + /// Throw FieldSpecificationWrongName if attribute name is wrong (*). + /// Throw StorageTypeWrongCppType if the attribute has the wrong C++ type. + /// Throw StorageTypeInvalidValue for values outside the allowed range. + virtual void validate( const coral::Attribute& attribute, + bool checkName = true ) const = 0; + +#ifdef COOL290 + protected: + + /// Standard constructor is protected (see bug #95823) + IFieldSpecification() {} + + /// Copy constructor is protected (see bug #95823) + IFieldSpecification( const IFieldSpecification& ) {} + + private: + + /// Assignment operator is private (see bug #95823) + IFieldSpecification& operator=( const IFieldSpecification& rhs ); +#endif + + }; + +} +#endif // COOLKERNEL_IFIELDSPECIFICATION_H diff --git a/28PATCHES2013/CoolKernel/CoolKernel/NEW/IFolderSpecification.h b/28PATCHES2013/CoolKernel/CoolKernel/NEW/IFolderSpecification.h new file mode 100644 index 0000000000000000000000000000000000000000..90828057dcd6f9e7b376cb8fb25f12b081687d0a --- /dev/null +++ b/28PATCHES2013/CoolKernel/CoolKernel/NEW/IFolderSpecification.h @@ -0,0 +1,108 @@ +#ifndef COOLKERNEL_IFOLDERSPECIFICATION_H +#define COOLKERNEL_IFOLDERSPECIFICATION_H 1 + +// First of all, enable or disable the COOL290 API extensions (see bug #92204) +#include "CoolKernel/VersionInfo.h" + +// Include files +#include "CoolKernel/IRecordSpecification.h" +#include "CoolKernel/FolderVersioning.h" +#ifdef COOL290 +#include "CoolKernel/PayloadMode.h" +#endif + +namespace cool +{ + + //-------------------------------------------------------------------------- + + /** @class IFolderSpecification IFolderSpecification.h + * + * Abstract interface to the specification of a COOL "folder". + * + * This includes the payload specification and the versioning mode. + * The description is not included as it is a generic "HVS node" + * property (it applies to folder sets as well as to folders). + * + * This class only provides const access to the specification. + * Creating or modifying it is the task of concrete derived classes. + * + * @author Andrea Valassi and Marco Clemencic + * @date 2006-09-22 + */ + + class IFolderSpecification + { + + public: + + virtual ~IFolderSpecification() {} + + /// Get the versioning mode (const). + virtual const FolderVersioning::Mode& versioningMode() const = 0; + + /// Get the payload specification (const). + virtual const IRecordSpecification& payloadSpecification() const = 0; + + /* + /// Get the channel specification (const). + virtual const IRecordSpecification& channelSpecification() const = 0; + */ + + /// Get the payload table flag (const). + /// DEPRECATED!! This data member will be removed in COOL290!! + virtual const bool& hasPayloadTable() const = 0; + +#ifdef COOL290 + /// Get the payload mode (const). + virtual const PayloadMode::Mode& payloadMode() const = 0; +#endif + + /// Comparison operator. + bool operator==( const IFolderSpecification& rhs ) const; + + /// Comparison operator. + bool operator!=( const IFolderSpecification& rhs ) const; + +#ifdef COOL290 + protected: + + /// Standard constructor is protected (see bug #95823) + IFolderSpecification() {} + + private: + + /// Copy constructor is private (see bug #95823) + IFolderSpecification( const IFolderSpecification& rhs ); + + /// Assignment operator is private (see bug #95823) + IFolderSpecification& operator=( const IFolderSpecification& rhs ); +#endif + + }; + + //-------------------------------------------------------------------------- + + inline bool + IFolderSpecification::operator==( const IFolderSpecification& rhs ) const + { + if ( versioningMode() != rhs.versioningMode() ) return false; + if ( payloadSpecification() != rhs.payloadSpecification() ) return false; + //if ( channelSpecification() != rhs.channelSpecification() ) return false; + if ( hasPayloadTable() != rhs.hasPayloadTable() ) return false; + return true; + } + + //-------------------------------------------------------------------------- + + inline bool + IFolderSpecification::operator!=( const IFolderSpecification& rhs ) const + { + return ( ! ( *this == rhs ) ); + } + + //-------------------------------------------------------------------------- + +} + +#endif // COOLKERNEL_IFOLDERSPECIFICATION_H diff --git a/28PATCHES2013/CoolKernel/CoolKernel/NEW/IRecord.h b/28PATCHES2013/CoolKernel/CoolKernel/NEW/IRecord.h new file mode 100644 index 0000000000000000000000000000000000000000..7869e9a284d934977815d229bf4ebd62208fec94 --- /dev/null +++ b/28PATCHES2013/CoolKernel/CoolKernel/NEW/IRecord.h @@ -0,0 +1,221 @@ +#ifndef COOLKERNEL_IRECORD_H +#define COOLKERNEL_IRECORD_H 1 + +// First of all, enable or disable the COOL290 API extensions (see bug #92204) +#include "CoolKernel/VersionInfo.h" + +// Include files +#include <sstream> +#include "CoolKernel/IField.h" +#include "CoolKernel/IRecordSpecification.h" + +// Forward declarations +namespace coral +{ + class AttributeList; +} + +namespace cool +{ + + //-------------------------------------------------------------------------- + + class IRecord + { + + /** @class IRecord IRecord.h + * + * Abstract interface to a data record: an ordered collection + * of data fields of user-defined names and storage types. + * + * A field is a transient data value that can be made persistent. + * Its StorageType specification defines constraints on its allowed + * data values (e.g. strings of less than 256 characters), to ensure + * portability across different platforms and persistent backends. + * While each StorageType is associated to a platform-dependent transient + * C++ type and to a backend-dependent persistent (e.g. SQL) data type, + * the StorageType class allows users to write their code to define and + * handles records and fields in a platform- and backend-independent way. + * + * All public methods of the IRecord interface are const: this is in + * practice a read-only interface. The only non-const methods are those + * that allow non-const access to the IField interfaces of its fields: + * these are protected, to be used or implemented in derived classes. + * Implementations of IField and IRecord are responsible for enforcing + * the StorageType constraints on the data values in all of their + * non-const methods, as well as at construction and assignment time. + * The use of const_cast on any const method is highly discouraged + * as it may lead to data corruption inside the IRecord instance. + * + * It is not possible to add, remove, rename or change the types of fields + * via the IRecord interface: if necessary, this is the responsibility + * of the concrete classes implementing IRecord or IRecordSpecification. + * + * Field names are a property of fields and their specifications: record + * specifications only define which fields exist and in which order. + * The IRecord base class manages field names and indexes according + * to its record specification. The implementation of size, index and + * operator[] is inlined and delegated to IRecordSpecification, while + * the concrete derived classes must only implement the field() method. + * + * Implementations of the IRecord interface may or may not be based on + * the coral::AttributeList class. To simplify the port of user code, + * an attributeList() method is provided to retrieve the contents of + * the IRecord as a (read-only) constant AttributeList reference. + * This is DEPRECATED and may be removed in a future COOL release. + * + * @author Andrea Valassi and Marco Clemencic + * @date 2006-09-28 + */ + + public: + + virtual ~IRecord() {} + + /// Return the specification of this record. + virtual const IRecordSpecification& specification() const = 0; + + /// Return the number of fields in this record (from the spec). + UInt32 size() const; + + /// Return the index of a field in this record by its name (from the spec). + UInt32 index( const std::string& name ) const; + + /// Return a field in this record by its name (const). + const IField& operator[] ( const std::string& name ) const; + + /// Return a field in this record by its index in [0, N-1] (const). + const IField& operator[] ( UInt32 index ) const; + + /// Comparison operator. Two records are equal if they have the same + /// fields (each with the same name, type and value), in the same order. + virtual bool operator== ( const IRecord& rhs ) const; + + /// Comparison operator. Two records are equal if they have the same + /// fields (each with the same name, type and value), in the same order. + virtual bool operator!= ( const IRecord& rhs ) const; + + /// Print the names, types and data values of all fields in this record. + virtual std::ostream& print( std::ostream& os ) const; + + /// DEPRECATED - added for easier compatibility with COOL 1.3 + /// (this is likely to be removed in a future COOL release). + /// Explicit conversion to a constant coral AttributeList reference. + virtual const coral::AttributeList& attributeList() const = 0; + + protected: + + /// Return a field in this record by its name (non-const). + IField& operator[] ( const std::string& name ); + + /// Return a field in this record by its index in [0, N-1] (non-const). + IField& operator[] ( UInt32 index ); + +#ifdef COOL290 + /// Standard constructor is protected (see bug #95823) + IRecord() {} + + /// Copy constructor is protected (fix bug #95823) + IRecord( const IRecord& ) {} +#endif + + private: + +#ifdef COOL290 + /// Assignment operator is private (fix bug #95823) + IRecord& operator=( const IRecord& rhs ); +#endif + + /// Return a field in this record by its index in [0, N-1] (const). + virtual const IField& field( UInt32 index ) const = 0; + + /// Return a field in this record by its index in [0, N-1] (non-const). + virtual IField& field( UInt32 index ) = 0; + + }; + + /// Print the names, types and data values of all fields in a record. + std::ostream& operator<<( std::ostream& s, const IRecord& record ); + + //-------------------------------------------------------------------------- + + inline UInt32 IRecord::size() const + { + return specification().size(); + } + + //-------------------------------------------------------------------------- + + inline UInt32 IRecord::index( const std::string& name ) const + { + return specification().index( name ); + } + + //-------------------------------------------------------------------------- + + inline const IField& IRecord::operator[] ( const std::string& name ) const + { + return field( index( name ) ); + } + + //-------------------------------------------------------------------------- + + inline IField& IRecord::operator[] ( const std::string& name ) + { + return field( index( name ) ); + } + + //-------------------------------------------------------------------------- + + inline const IField& IRecord::operator[] ( UInt32 index ) const + { + return field( index ); + } + + //-------------------------------------------------------------------------- + + inline IField& IRecord::operator[] ( UInt32 index ) + { + return field( index ); + } + + //-------------------------------------------------------------------------- + + inline bool IRecord::operator==( const IRecord& rhs ) const + { + if ( this->specification() != rhs.specification() ) return false; + for ( UInt32 i = 0; i < this->size(); ++i ) + if ( (*this)[i] != rhs[i] ) return false; + return true; + } + + //-------------------------------------------------------------------------- + + inline bool IRecord::operator!=( const IRecord& rhs ) const + { + return ( ! ( *this == rhs ) ); + } + + //-------------------------------------------------------------------------- + + inline std::ostream& IRecord::print( std::ostream& os ) const + { + for ( UInt32 i = 0; i < this->size(); ++i ) + { + if ( i > 0 ) os << ", "; + os << "[" << (*this)[i] << "]"; + } + return os; + } + + //-------------------------------------------------------------------------- + + inline std::ostream& operator<<( std::ostream& s, const IRecord& record ) + { + return record.print( s ); + } + + //-------------------------------------------------------------------------- + +} +#endif // COOLKERNEL_IRECORD_H diff --git a/28PATCHES2013/CoolKernel/CoolKernel/NEW/IRecordSelection.h b/28PATCHES2013/CoolKernel/CoolKernel/NEW/IRecordSelection.h new file mode 100644 index 0000000000000000000000000000000000000000..689a4ea5face73d6de32e1062db7bcd5f2e1a465 --- /dev/null +++ b/28PATCHES2013/CoolKernel/CoolKernel/NEW/IRecordSelection.h @@ -0,0 +1,84 @@ +#ifndef COOLKERNEL_IRECORDSELECTION_H +#define COOLKERNEL_IRECORDSELECTION_H 1 + +// First of all, enable or disable the COOL290 API extensions (see bug #92204) +#include "CoolKernel/VersionInfo.h" + +// Include files +#include "CoolKernel/Record.h" + +namespace cool +{ + + //-------------------------------------------------------------------------- + + /** @class IRecordSelection IRecordSelection.h + * + * Abstract interface to a data record selection. + * Inspired by the HARP IEventSelection interface. + * + * This interface is designed to define a user 'payload query cut' which + * can be passed as the last argument to the IFolder::browseObjects method. + * Payload query cuts are meant to be applied after a preselection on tag, + * channel and IOV range is executed on the server using optimized SQL. + * Payload queries thus consist in a scan of individual preselected IOVs, + * which can be applied either in the C++ client after downloading all + * preselected IOVs, or directly on the database server (e.g. by appending + * a final SQL selection fragment to the WHERE clause of an RDBMS query). + * + * The 'describe' method is meant to provide an SQL-like (or ROOT-like) + * translation of payload query cuts that can be directly appended to + * the database server query. In the relational implementation of COOL, + * this approach is followed for all 'trusted' concrete implementations + * of IRecordSelection defined in the CoolKernel API. + * + * By default, the 'select' method can also be used to apply payload + * query cuts on preselected IOVs in the C++ client. In the relational + * implementation of COOL, this is the default approach for executing + * arbitrary payload queries from user-defined implementation classes. + * + * @author Andrea Valassi + * @date 2008-07-05 + */ + + class IRecordSelection + { + + public: + + virtual ~IRecordSelection() {} + + /// Can the selection be applied to a record with the given specification? + virtual bool canSelect( const IRecordSpecification& spec ) const = 0; + + /// Apply the selection to the given record. + virtual bool select( const IRecord& record ) const = 0; + + /// Describe the selection (with or without bind variables) + //virtual const std::string& describe( bool useBindVar = false ) const = 0; + + /// Get the bind variables for the selection. + //virtual const IRecord& bindVariables() const = 0; + + /// Clone the record selection (and any objects referenced therein). + virtual IRecordSelection* clone() const = 0; + +#ifdef COOL290 + protected: + + /// Standard constructor is protected (see bug #95823) + IRecordSelection() {} + + /// Copy constructor is protected (see bug #95823) + IRecordSelection( const IRecordSelection& ) {} + + private: + + /// Assignment operator is private (see bug #95823) + IRecordSelection& operator=( const IRecordSelection& rhs ); +#endif + + }; + +} +#endif // COOLKERNEL_IRECORDSELECTION_H diff --git a/28PATCHES2013/CoolKernel/CoolKernel/NEW/IRecordSpecification.h b/28PATCHES2013/CoolKernel/CoolKernel/NEW/IRecordSpecification.h new file mode 100644 index 0000000000000000000000000000000000000000..c7c7471e3f4ebb6f747fb63f4566c1f1ffa1d95f --- /dev/null +++ b/28PATCHES2013/CoolKernel/CoolKernel/NEW/IRecordSpecification.h @@ -0,0 +1,151 @@ +#ifndef COOLKERNEL_IRECORDSPECIFICATION_H +#define COOLKERNEL_IRECORDSPECIFICATION_H 1 + +// First of all, enable or disable the COOL290 API extensions (see bug #92204) +#include "CoolKernel/VersionInfo.h" + +// Include files +#include "CoolKernel/IFieldSpecification.h" +#include "CoolKernel/types.h" + +// Forward declarations +namespace coral +{ + class AttributeList; +} + +namespace cool +{ + + // Forward declarations + class IRecord; + + /** @class IRecordSpecification IRecordSpecification.h + * + * Abstract interface to the specification of a data record: an ordered + * collection of data fields of user-defined names and storage types. + * + * A field is a transient data value that can be made persistent. + * Its StorageType specification defines constraints on its allowed + * data values (e.g. strings of less than 256 characters), to ensure + * portability across different platforms and persistent backends. + * While each StorageType is associated to a platform-dependent transient + * C++ type and to a backend-dependent persistent (e.g. SQL) data type, + * the StorageType class allows users to write their code to define and + * handles records and fields in a platform- and backend-independent way. + * + * The IRecordSpecification interface only allows read-only access to + * the specification of a record: appropriate methods to add, remove + * or change the names and storage types of fields are meant to be + * defined and implemented in the concrete classes derived from it. + * + * Field names are a property of fields and their specifications: record + * specifications only define which fields exist and in which order. + * + * Implementations of IField and IRecord are responsible for enforcing + * the StorageType constraints on the data values in all of their + * non-const methods, as well as at construction and assignment time. + * The IRecordSpecification interface provides a validate() method to + * check if the data offered by a IRecord complies to the specification. + * + * Implementations of the IRecord interface may or may not be based on + * the coral::AttributeList class. For internal COOL usage, another + * signature of the validate() method is provided to check if the data + * in an AttribueList complies to the record specification. + * This is DEPRECATED and may be removed in a future COOL release. + + * @author Andrea Valassi and Marco Clemencic + * @date 2006-09-22 + */ + + class IRecordSpecification + { + + public: + + virtual ~IRecordSpecification() {} + + /// Return the number of fields in this record specification. + virtual UInt32 size() const = 0; + + /// Comparison operator. Two record specifications are equal if they have + /// the same fields (each with the same name and type), in the same order. + virtual bool operator==( const IRecordSpecification& rhs ) const = 0; + + /// Comparison operator. Two record specifications are equal if they have + /// the same fields (each with the same name and type), in the same order. + virtual bool operator!=( const IRecordSpecification& rhs ) const = 0; + + /// Does a field with this name exist? + virtual bool exists( const std::string& name ) const = 0; + + /// Return a field given its index. + /// Throws RecordSpecificationUnknownField if no such field exists. + virtual const + IFieldSpecification& operator[] ( UInt32 index ) const = 0; + + /// Return a field given its name. + /// Throws RecordSpecificationUnknownField if no such field exists. + virtual const + IFieldSpecification& operator[] ( const std::string& name ) const = 0; + + /// Return the index of a field given its name. + /// Throws RecordSpecificationUnknownField if no such field exists. + virtual UInt32 index( const std::string& name ) const = 0; + + /// Check that a given record is compatible with this specification. + /// For every 'reference' field in this specification, the record must + /// contain (in any order) one field with the name, the persistent storage + /// type and a value compatible with the storage type of that field. + /// If checkSize is true, the record must not contain any other fields (*). + /// Throw RecordSpecificationUnknownField if no field with a given name + /// exists (do not check that the attribute has the same name too). + /// Throw FieldSpecificationWrongStorageType if a field has the wrong type. + /// Throw StorageTypeInvalidValue for values outside the allowed range. + /// Throw RecordSpecificationWrongSize if record has too many fields (*). + virtual void validate( const IRecord& record, + bool checkSize = true ) const = 0; + + /// DEPRECATED - added for easier compatibility with COOL 1.3 + /// (this is likely to be removed in a future COOL release). + /// Check that an attribute list is compatible with this specification. + /// For every 'reference' field in this specification, the list must + /// contain (in any order) one field with the name, the transient C++ type + /// and a value compatible with the persistent storage type of that field. + /// If checkSize is true, the list must not contain any other fields (*). + /// Throw coral::AttributeListException if an attribute does not exist. + /// Throw StorageTypeWrongCppType if an attribute has the wrong C++ type. + /// Throw StorageTypeInvalidValue for values outside the allowed range. + /// Throw RecordSpecificationWrongSize if list has too many attributes (*). + virtual void validate( const coral::AttributeList& attributeList, + bool checkSize = true ) const = 0; + + /* + /// DEPRECATED - added for easier compatibility with COOL 1.3 + /// (this is likely to be removed in a future COOL release). + /// Explicit conversion to a coral AttributeListSpecification reference. + /// This makes it possible to construct a coral::AttributeList from + /// a cool::IRecordSpecification, "coral::AttributeList aList( spec );". + virtual const + coral::AttributeListSpecification& attributeListSpecification() const = 0; + */ + +#ifdef COOL290 + protected: + + /// Standard constructor is protected (see bug #95823) + IRecordSpecification() {} + + /// Copy constructor is protected (see bug #95823) + IRecordSpecification( const IRecordSpecification& ) {} + + private: + + /// Assignment operator is private (see bug #95823) + IRecordSpecification& operator=( const IRecordSpecification& rhs ); +#endif + + }; + +} +#endif // COOLKERNEL_IRECORDSPECIFICATION_H diff --git a/28PATCHES2013/CoolKernel/CoolKernel/NEW/ITime.h b/28PATCHES2013/CoolKernel/CoolKernel/NEW/ITime.h new file mode 100644 index 0000000000000000000000000000000000000000..a17665acae7cc950104d1112e6038fa5bb40c2f0 --- /dev/null +++ b/28PATCHES2013/CoolKernel/CoolKernel/NEW/ITime.h @@ -0,0 +1,131 @@ +#ifndef COOLKERNEL_ITIME_H +#define COOLKERNEL_ITIME_H 1 + +// First of all, enable or disable the COOL290 API extensions (see bug #92204) +#include "CoolKernel/VersionInfo.h" + +// Include files +#include <ostream> + +namespace cool +{ + + /** @class ITime ITime.h + * + * Abstract interface to a generic COOL time class. + * + * The implementation details are hidden from the public API. + * + * @author Andrea Valassi + * @date 2007-01-17 + */ + + class ITime + { + + public: + + /// Destructor. + virtual ~ITime() {} + + /// Returns the year. + virtual int year() const = 0; + + /// Returns the month [1-12]. + virtual int month() const = 0; + + /// Returns the day [1-31]. + virtual int day() const = 0; + + /// Returns the hour [0-23]. + virtual int hour() const = 0; + + /// Returns the minute [0-59]. + virtual int minute() const = 0; + + /// Returns the second [0-59]. + virtual int second() const = 0; + + /// Returns the nanosecond [0-999999999]. + virtual long nanosecond() const = 0; + + /// Print to an output stream. + virtual std::ostream& print( std::ostream& os ) const = 0; + + /// Comparison operator. + virtual bool operator==( const ITime& rhs ) const = 0; + + /// Comparison operator. + virtual bool operator>( const ITime& rhs ) const = 0; + + /// Comparison operator. + bool operator>=( const ITime& rhs ) const; + + /// Comparison operator. + bool operator<( const ITime& rhs ) const; + + /// Comparison operator. + bool operator<=( const ITime& rhs ) const; + + /// Comparison operator. + bool operator!=( const ITime& rhs ) const; + +#ifdef COOL290 + protected: + + /// Standard constructor is protected (see bug #95823) + ITime() {} + + /// Copy constructor is protected (see bug #95823) + ITime( const ITime& ) {} + + private: + + /// Assignment operator is private (see bug #95823) + ITime& operator=( const ITime& rhs ); +#endif + + }; + + /// Print to an output stream. + std::ostream& operator<<( std::ostream& s, const ITime& time ); + + //-------------------------------------------------------------------------- + + inline bool ITime::operator>=( const ITime& rhs ) const + { + return ( (*this) > rhs || (*this) == rhs ); + } + + //-------------------------------------------------------------------------- + + inline bool ITime::operator<( const ITime& rhs ) const + { + return !( (*this) >= rhs ); + } + + //-------------------------------------------------------------------------- + + inline bool ITime::operator<=( const ITime& rhs ) const + { + return !( (*this) > rhs ); + } + + //-------------------------------------------------------------------------- + + inline bool ITime::operator!=( const ITime& rhs ) const + { + return !( (*this) == rhs ); + } + + //-------------------------------------------------------------------------- + + inline std::ostream& operator<<( std::ostream& s, const ITime& time ) + { + return time.print( s ); + } + + //-------------------------------------------------------------------------- + +} +#endif // COOLKERNEL_ITIME_H diff --git a/28PATCHES2013/RelationalCool/src/ISessionMgr.h.NEW.20120208 b/28PATCHES2013/RelationalCool/src/ISessionMgr.h.NEW.20120208 new file mode 100644 index 0000000000000000000000000000000000000000..b9849f0951f774f430517041f6f63cc053fcdb04 --- /dev/null +++ b/28PATCHES2013/RelationalCool/src/ISessionMgr.h.NEW.20120208 @@ -0,0 +1,100 @@ +// $Id: ISessionMgr.h,v 1.1 2012/01/30 17:06:03 avalassi Exp $ +#ifndef RELATIONALCOOL_ISESSIONMGR_H +#define RELATIONALCOOL_ISESSIONMGR_H + +// Include files +#include "CoralBase/MessageStream.h" +#include "RelationalAccess/ISessionProxy.h" + +// Local include files + +namespace cool +{ + + /** @class ISessionMgr ISessionMgr.h + * + * Pure virtual interface to a manager of a relational database session. + * + * This interface knows nothing about tables specific to COOL. + * It is only concerned with generic relational databases functionalities + * (like those available through CORAL, which has no predefined schema). + * + * This interface was first added as part of the patch for task #6154. + * + * @author Martin Wache + * @date 2010-11-26 + */ + + class ISessionMgr + { + + friend class RalDatabase; + friend class RalQueryMgr; + friend class RalSchemaMgr; + friend class RalSequenceMgr; + friend class RalTransactionMgr; + + public: + + /// The destructor automatically disconnects from the database. + virtual ~ISessionMgr() {} + + /// Return the server technology for the current connection. + /// Supported technologies: "Oracle", "MySQL", "SQLite", "frontier". + /// This ultimately corresponds to coral::IDomain::flavorName() + /// (grep m_flavorName in the four XxxAccess/src/Domain.cpp), + /// because it is equal to ConnectionHandle::technologyName(), + /// which is equal to ConnectionParams::technologyName(), which is + /// set to IDomain::flavorName() in ReplicaCatalogue::getReplicas. + /// *** WARNING!!! THIS MAY CHANGE IN LATER VERSIONS OF THE CODE!!! *** + /// New (not for production!): for URLs using a middle tier, this method + /// returns the properties of the remote database, not of the middle tier + virtual const std::string databaseTechnology() const = 0; + + /// Return the server technology version for the current connection. + /// This ultimately corresponds to coral::IConnection::serverVersion() + /// (grep serverVersion in the four XxxAccess/src/Connection.cpp), + /// because it is equal to ConnectionHandle::serverVersion(), + /// which is equal to IConnection::serverVersion(). + /// *** WARNING!!! THIS MAY CHANGE IN LATER VERSIONS OF THE CODE!!! *** + /// New (not for production!): for URLs using a middle tier, this method + /// returns the properties of the remote database, not of the middle tier + virtual const std::string serverVersion() const = 0; + + /// Return the schema name for the current connection. + virtual const std::string schemaName() const = 0; + + /// Return the connection state of the database. + /// [Note that this is subtly different from RelationalDatabase::isOpen!] + virtual bool isConnected() const = 0; + + /// (Re)connect to the database. + virtual void connect() = 0; + + /// Close the database connection. + virtual void disconnect() = 0; + + /// Required access mode to the database. + virtual bool isReadOnly() const = 0; + + /// Required access mode to the database. + virtual bool isReadOnlyManyTx() const = 0; + + /// Sleep to work around ORA-01466 if needed (bug #87935) + /// This can also be called by RalDatabase for RO single tx sessions + virtual void sleepFor01466() const = 0; + + /// Get a CORAL MessageStream + virtual coral::MessageStream& log() const = 0; + + protected: + + /// Get a reference to the RAL database session. + /// Throw an exception if there is no connection to the database. + virtual coral::ISessionProxy& session() const = 0; + + }; + +} + +#endif diff --git a/28PATCHES2013/RelationalCool/src/NEW/RalDatabase.cpp b/28PATCHES2013/RelationalCool/src/NEW/RalDatabase.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ec7711928a7a724a3c20639970f3dea6b47bf005 --- /dev/null +++ b/28PATCHES2013/RelationalCool/src/NEW/RalDatabase.cpp @@ -0,0 +1,1882 @@ + +// Include files +#include <sstream> +#include "CoolKernel/ChannelSelection.h" +#include "HvsTagRecord.h" +#include "CoolKernel/Record.h" +#include "CoolKernel/ValidityKey.h" +#include "CoralBase/Exception.h" +#include "RelationalAccess/IBulkOperation.h" +#include "RelationalAccess/ISchema.h" +#include "RelationalAccess/ITable.h" +#include "RelationalAccess/ITableDataEditor.h" +#include "RelationalAccess/SchemaException.h" + +// Local include files +#include "DummyTransactionMgr.h" +#include "HvsPathHandler.h" +#include "HvsPathHandlerException.h" +#include "ManualTransaction.h" +#include "ObjectId.h" +#include "RalDatabase.h" +#include "RalQueryMgr.h" +#include "RalSchemaMgr.h" +#include "RalSessionMgr.h" +#include "RalTransactionMgr.h" +#include "RelationalChannelTable.h" +#include "RelationalDatabaseId.h" +#include "RelationalDatabaseTable.h" +#include "RelationalException.h" +#include "RelationalFolder.h" +#include "RelationalFolderUnsupported.h" +#include "RelationalFolderSet.h" +#include "RelationalFolderSetUnsupported.h" +#include "RelationalNodeMgr.h" +#include "RelationalNodeTable.h" +#include "RelationalGlobalTagTable.h" +#include "RelationalObject.h" +#include "RelationalObjectMgr.h" +#include "RelationalObjectTable.h" +#include "RelationalObjectTableRow.h" +#include "RelationalObject2TagTable.h" +#include "RelationalPayloadTable.h" +#include "RelationalQueryDefinition.h" +#include "RelationalSequence.h" +#include "RelationalSequenceMgr.h" +#include "RelationalTableRow.h" +#include "RelationalTagMgr.h" +#include "RelationalTagSequence.h" +#include "RelationalTagTable.h" +#include "RelationalTag2TagTable.h" +#include "SimpleObject.h" +#include "TimingReportMgr.h" +#include "TransRelFolder.h" +#include "VersionInfo.h" +#include "attributeListToString.h" +#include "sleep.h" +#include "timeToString.h" +#include "uppercaseString.h" + +// Additional VersionInfo specific to RalDatabase.cpp +namespace cool +{ + namespace VersionInfo { + const std::string cvsCheckout = "$Name: $"; + const std::string cvsCheckin = "$Id: RalDatabase.cpp,v 1.649 2012/01/27 16:23:30 avalassi Exp $"; + } +} + +// Namespace +using namespace cool; + +// Local type definitions +typedef boost::shared_ptr<RelationalSequence> RelationalSequencePtr; + +//----------------------------------------------------------------------------- + +RalDatabase::RalDatabase( CoralConnectionServiceProxyPtr ppConnSvc, + const DatabaseId& dbId, + bool readOnly ) + : RelationalDatabase( dbId ) + , m_useTimeout( true ) + , m_sessionMgr( new RalSessionMgr( ppConnSvc, dbId, readOnly ) ) +{ + std::string ro = ( readOnly ? "R/O" : "R/W" ); + log() << coral::Info << "Instantiate a " << ro << " RalDatabase for '" + << databaseId() << "'" << coral::MessageStream::endmsg; + + // Create a new relational schema manager + setSchemaMgr( std::auto_ptr<RelationalSchemaMgr> + ( new RalSchemaMgr( *this, sessionMgr() ) ) ); + + // Create a new relational node manager + setNodeMgr( std::auto_ptr<RelationalNodeMgr> + ( new RelationalNodeMgr( *this ) ) ); + + // Create a new relational tag manager + setTagMgr( std::auto_ptr<RelationalTagMgr> + ( new RelationalTagMgr( *this ) ) ); + + // Create a new object manager + setObjectMgr( std::auto_ptr<RelationalObjectMgr> + ( new RelationalObjectMgr( *this ) ) ); + + // Create a new relational transaction manager + // For read-only connections, a single R/O transaction is started by the + // RalSessionMgr for the duration of the database connection (unless in + // 'manytx mode): all other clients use a dummy transaction manager! + //if ( !readOnly ) + if ( !readOnly || sessionMgr()->isReadOnlyManyTx() ) + { + setTransactionMgr( boost::shared_ptr<IRelationalTransactionMgr> + ( new RalTransactionMgr( sessionMgr() ) ) ); + } + else + { + setTransactionMgr( boost::shared_ptr<IRelationalTransactionMgr> + ( new DummyTransactionMgr() ) ); + } + + // Initialize timing reports + if ( TimingReport::enabled() ) + { + TimingReportMgr::initialize(); + TimingReportMgr::startTimer( "TOTAL [cool::RalDatabase]" ); + } +} + +//----------------------------------------------------------------------------- + +RalDatabase::~RalDatabase() +{ + log() << coral::Info << "Delete the RalDatabase for '" + << databaseId() << "'" << coral::MessageStream::endmsg; + + // Loop over all nodes in the node map + for ( std::map<std::string,RelationalTableRow*>::const_iterator + row = m_nodes.begin(); row != m_nodes.end(); ++row ) + { + delete row->second; + } + + // Finalize timing reports + if ( TimingReportMgr::isActive() ) { + TimingReportMgr::stopTimer( "TOTAL [cool::RalDatabase]" ); + TimingReportMgr::finalize(); + } +} + +//----------------------------------------------------------------------------- + +void RalDatabase::createDatabase( const IRecord& dbAttr ) +{ + std::string dbName = databaseName(); + log() << "Create a new database with name " << dbName + << coral::MessageStream::endmsg; + + // Check if the database attribute specification is valid + if ( dbAttr.specification() != databaseAttributesSpecification() ) + throw RelationalException + ( "Invalid database attributes specification", "RalDatabase" ); + m_dbAttr = dbAttr; + + // Add release and schema related attributes + m_dbAttr[RelationalDatabaseTable::attributeNames::release].setValue + <RelationalDatabaseTable::columnTypes::attributeValue> + ( VersionInfo::release ); + m_dbAttr[RelationalDatabaseTable::attributeNames::cvsCheckout].setValue + <RelationalDatabaseTable::columnTypes::attributeValue> + ( VersionInfo::cvsCheckout ); + m_dbAttr[RelationalDatabaseTable::attributeNames::cvsCheckin].setValue + <RelationalDatabaseTable::columnTypes::attributeValue> + ( VersionInfo::cvsCheckin ); + m_dbAttr[RelationalDatabaseTable::attributeNames::schemaVersion].setValue + <RelationalDatabaseTable::columnTypes::attributeValue> + ( VersionInfo::schemaVersion ); + + // TEMPORARY? AV 04.04.2005 + // FIXME: you should check here that the default prefix is not longer + // than 8 characters and is already uppercase. + // FIXME: you should check here that the table names are all uppercase + // and not longer than the maximum allowed Oracle/MySQL limits. + + // Do NOT check that DB is open: it is not! (bug #87090) + //checkDbOpenTransaction( "RalDatabase::refreshNode", cool::ReadWrite ); + + // Check ONLY that RW transaction is active (is this needed/ok?) + if ( !transactionMgr()->isActive() ) + throw RelationalException( "Transaction is not active", + "RalDatabase::refreshNode" ); + //if ( transactionMgr()->isReadOnly() ) + // throw RelationalException( "Transaction is read only", + // "RalDatabase::refreshNode" ); + + // Get the name of the main management table, create it and fill it + schemaMgr().createMainTable( mainTableName() ); + schemaMgr().fillMainTable( mainTableName(), m_dbAttr.attributeList() ); + + /* + // *** START *** 3.0.0 schema extensions (task #4307) + // Get the name of the iovTables table and create it + std::string iovTablesTableName = + dbAttr[RelationalDatabaseTable::attributeNames::iovTablesTableName]. + data<std::string>(); + schemaMgr().createIovTablesTable( iovTablesTableName ); + + // Get the name of the channelTables table and create it + std::string channelTablesTableName = + dbAttr[RelationalDatabaseTable::attributeNames::channelTablesTableName]. + data<std::string>(); + schemaMgr().createChannelTablesTable( channelTablesTableName ); + // **** END **** 3.0.0 schema extensions (task #4307) + */ + + // Get the name of the node table and create it + std::string nodeTableName = + dbAttr[RelationalDatabaseTable::attributeNames::nodeTableName]. + data<std::string>(); + std::string defaultTablePrefix = + dbAttr[RelationalDatabaseTable::attributeNames::defaultTablePrefix]. + data<std::string>(); + schemaMgr().createNodeTable( nodeTableName, defaultTablePrefix ); + + // Get the name of the tag table and create it + std::string tagTableName = + dbAttr[RelationalDatabaseTable::attributeNames::tagTableName]. + data<std::string>(); + schemaMgr().createGlobalTagTable( tagTableName, nodeTableName ); + + /* + // *** START *** 3.0.0 schema extensions (task #4396) + // Get the name of the head tag table and create it + std::string headTagTableName = + dbAttr[RelationalDatabaseTable::attributeNames::headTagTableName]. + data<std::string>(); + schemaMgr().createGlobalHeadTagTable( headTagTableName, tagTableName ); + + // Get the name of the user tag table and create it + std::string userTagTableName = + dbAttr[RelationalDatabaseTable::attributeNames::userTagTableName]. + data<std::string>(); + schemaMgr().createGlobalUserTagTable( userTagTableName, tagTableName ); + // **** END **** 3.0.0 schema extensions (task #4396) + */ + + // Get the name of the tag table and create it + std::string tag2TagTableName = + dbAttr[RelationalDatabaseTable::attributeNames::tag2TagTableName]. + data<std::string>(); + schemaMgr().createTag2TagTable + ( tag2TagTableName, tagTableName, nodeTableName ); + + // Get the name of the tag shared sequence and create it + std::string tagSharedSequenceName = + dbAttr[RelationalDatabaseTable::attributeNames::tagSharedSequenceName]. + data<std::string>(); + schemaMgr().createSharedSequence( tagSharedSequenceName, nodeTableName ); + + // Get the name of the IOV shared sequence and create it + std::string iovSharedSequenceName = + dbAttr[RelationalDatabaseTable::attributeNames::iovSharedSequenceName]. + data<std::string>(); + schemaMgr().createSharedSequence( iovSharedSequenceName, nodeTableName ); + + // The database is now open + m_isOpen = true; + + // TEMPORARY? Sleep to work around the ORA-01466 problem (Oracle only) + if ( m_sessionMgr->databaseTechnology() == "Oracle" && m_useTimeout ) { + log() << "Sleep to work around the ORA-01466 problem" + << coral::MessageStream::endmsg; + cool::sleep(1); + } + +} + +//----------------------------------------------------------------------------- + +bool RalDatabase::dropDatabase() +{ + log() << coral::Info << "Drop database..." << coral::MessageStream::endmsg; + + // AV 2005-07-07 + // Return true if all database tables are dropped as expected. + // Return false (without throwing any exception) if the database and + // all associated tables do not exist any more on exit from this method, + // but the database or some associated tables did not exist to start with. + bool status = true; + + // Throw a RelationalException if the database or one of its tables + // cannot be dropped (i.e. continues to exist on exit from this method). + // Any exception is thrown as soon as the first problem appears: + // the implementation is based on many individual transactions, + // there is no attempt to go back to the last known state on failure + // (also because technically difficult, Oracle DDL is auto-committed). + + // Check that DB is open and RW transaction is active (is this needed/ok?) + checkDbOpenTransaction( "RalDatabase::refreshNode", cool::ReadWrite ); + + // Enclose the implementation in a try-catch block anyway, + // just to be able to print some debug messages on failures. + try + { + + // For each folder, drop IOV table and sequence and delete the folder row. + // Also delete any tags associated to the folder from the global tag table + // and from the tag2tag table (otherwise FK constraints may be violated) + // Throw an Exception if the schema of one of the nodes in this database + // is more recent than the schema version supported by the current release: + // in this case make sure you do not drop ANY node (throw immediately)! + if ( ! dropAllNodes() ) status = false; + + // Drop the IOV shared sequence + { + std::string tableName = iovSharedSequenceName(); + log() << coral::Debug << "Drop table " << tableName + << coral::MessageStream::endmsg; + if ( ! schemaMgr().dropTable( tableName ) ) status = false; + } + + // Drop the tag shared sequence + { + std::string tableName = tagSharedSequenceName(); + log() << coral::Debug << "Drop table " << tableName + << coral::MessageStream::endmsg; + if ( ! schemaMgr().dropTable( tableName ) ) status = false; + } + + // Drop the sequence associated to the tag2tag table + { + std::string seqName = + RelationalTag2TagTable::sequenceName( tag2TagTableName() ); + log() << coral::Debug << "Drop sequence " << seqName + << coral::MessageStream::endmsg; + if ( ! queryMgr().sequenceMgr().existsSequence( seqName ) ) + { + status = false; + } + else + { + queryMgr().sequenceMgr().dropSequence( seqName ); + } + } + + // Drop the tag2tag table + { + std::string tableName = tag2TagTableName(); + log() << coral::Debug << "Drop table " << tableName + << coral::MessageStream::endmsg; + if ( ! schemaMgr().dropTable( tableName ) ) status = false; + } + + /* + // *** START *** 3.0.0 schema extensions (task #4396) + // Drop the global user tag table + { + std::string tableName = globalUserTagTableName(); + log() << coral::Debug << "Drop table " << tableName + << coral::MessageStream::endmsg; + if ( ! schemaMgr().dropTable( tableName ) ) status = false; + } + + // Drop the global head tag table + { + std::string tableName = globalHeadTagTableName(); + log() << coral::Debug << "Drop table " << tableName + << coral::MessageStream::endmsg; + if ( ! schemaMgr().dropTable( tableName ) ) status = false; + } + // **** END **** 3.0.0 schema extensions (task #4396) + */ + + // Drop the global tag table + { + std::string tableName = globalTagTableName(); + log() << coral::Debug << "Drop table " << tableName + << coral::MessageStream::endmsg; + if ( ! schemaMgr().dropTable( tableName ) ) status = false; + } + + // Drop the sequence associated to the node table + { + std::string seqName = + RelationalNodeTable::sequenceName( nodeTableName() ); + log() << coral::Debug << "Drop sequence " << seqName + << coral::MessageStream::endmsg; + if ( ! queryMgr().sequenceMgr().existsSequence( seqName ) ) + { + status = false; + } + else + { + queryMgr().sequenceMgr().dropSequence( seqName ); + } + } + + // Drop the node table + { + std::string tableName = nodeTableName(); + log() << coral::Debug << "Drop table " << tableName + << coral::MessageStream::endmsg; + if ( ! schemaMgr().dropTable( tableName ) ) status = false; + } + + /* + // *** START *** 3.0.0 schema extensions (task #4307) + // Drop the channelTables table + { + std::string tableName = channelTablesTableName(); + //log() << coral::Debug << "Drop table " << tableName + // << coral::MessageStream::endmsg; + //if ( ! schemaMgr().dropTable( tableName ) ) status = false; + } + + // Drop the iovTables table + { + std::string tableName = iovTablesTableName(); + //log() << coral::Debug << "Drop table " << tableName + // << coral::MessageStream::endmsg; + //if ( ! schemaMgr().dropTable( tableName ) ) status = false; + } + // **** END **** 3.0.0 schema extensions (task #4307) + */ + + // Drop the main table + { + std::string tableName = mainTableName(); + log() << coral::Debug << "Drop table " << tableName + << coral::MessageStream::endmsg; + if ( ! schemaMgr().dropTable( tableName ) ) status = false; + } + + } + catch ( std::exception& e ) + { + log() << coral::Warning << "Exception caught in dropDatabase(): " + << e.what() << coral::MessageStream::endmsg; + throw; + } + + // Success: the database does not exist anymore + // Return status code 'false' if parts of it were missing already + log() << coral::Info << "Drop database... DONE" + << coral::MessageStream::endmsg; + return status; +} + +//----------------------------------------------------------------------------- + +void RalDatabase::refreshDatabase( bool keepNodes ) +{ + log() << coral::Info << "Refresh database" + << ( keepNodes ? " (refresh nodes)" : " (drop nodes)" ) + << "..." << coral::MessageStream::endmsg; + + // Check that DB is open and RW transaction is active (is this needed/ok?) + checkDbOpenTransaction( "RalDatabase::refreshNode", cool::ReadWrite ); + + // Enclose the implementation in a try-catch block anyway, + // just to be able to print some debug messages on failures. + try + { + + if ( !keepNodes ) + // For each node, drop IOV table and sequence and delete the node row. + // Also delete any tags associated to the node from the global tag table + // and from the tag2tag table (otherwise FK constraints may be violated) + // Throw an Exception if the schema of any nodes in this database is + // more recent than the schema version supported by the current release: + // in this case make sure you do not drop ANY node (throw immediately)! + dropAllNodes( true ); // keep the root node + else + refreshAllNodes(); + + // Delete all rows from the IOV shared sequence + { + std::string tableName = iovSharedSequenceName(); + log() << coral::Debug << "Refresh table " << tableName + << coral::MessageStream::endmsg; + queryMgr().deleteAllTableRows( tableName ); + // No need to init() or nextVal(): see RalSchemaMgr::createSharedSequence + } + + // Delete all rows from the tag shared sequence + { + std::string tableName = tagSharedSequenceName(); + log() << coral::Debug << "Refresh table " << tableName + << coral::MessageStream::endmsg; + queryMgr().deleteAllTableRows( tableName ); + // No need to init() or nextVal(): see RalSchemaMgr::createSharedSequence + } + + // Delete all rows from the sequence associated to the tag2tag table + { + std::string seqName = + RelationalTag2TagTable::sequenceName( tag2TagTableName() ); + log() << coral::Debug << "Refresh sequence " << seqName + << coral::MessageStream::endmsg; + queryMgr().deleteAllTableRows( seqName ); + queryMgr().sequenceMgr().initSequence( seqName ); + // No need to nextVal(): see RalSchemaMgr::createTag2TagTable + } + + // Delete all rows from the tag2tag table + { + std::string tableName = tag2TagTableName(); + log() << coral::Debug << "Refresh table " << tableName + << coral::MessageStream::endmsg; + queryMgr().deleteAllTableRows( tableName ); + } + + /* + // *** START *** 3.0.0 schema extensions (task #4396) + // Delete all rows from the global user tag table + { + std::string tableName = globalUserTagTableName(); + log() << coral::Debug << "Refresh table " << tableName + << coral::MessageStream::endmsg; + queryMgr().deleteAllTableRows( tableName ); + } + + // Delete all rows from the global head tag table + { + std::string tableName = globalHeadTagTableName(); + log() << coral::Debug << "Refresh table " << tableName + << coral::MessageStream::endmsg; + queryMgr().deleteAllTableRows( tableName ); + } + // **** END **** 3.0.0 schema extensions (task #4396) + */ + + // Delete all rows from the global tag table + { + std::string tableName = globalTagTableName(); + log() << coral::Debug << "Refresh table " << tableName + << coral::MessageStream::endmsg; + queryMgr().deleteAllTableRows( tableName ); + } + + // Delete all rows from the sequence associated to the node table + // (NB call nextVal: see RalSchemaMgr::createNodeTable) + if ( !keepNodes ) + { + std::string seqName = + RelationalNodeTable::sequenceName( nodeTableName() ); + log() << coral::Debug << "Refresh sequence " << seqName + << coral::MessageStream::endmsg; + queryMgr().deleteAllTableRows( seqName ); + queryMgr().sequenceMgr().initSequence( seqName ); + queryMgr().sequenceMgr().getSequence( seqName )->nextVal(); // root node! + } + + // Do nothing about the node table + // (could delete all rows except root, but we already dropped all nodes) + { + std::string tableName = nodeTableName(); + log() << coral::Debug << "Refresh table " << tableName + << coral::MessageStream::endmsg; + } + + /* + // *** START *** 3.0.0 schema extensions (task #4307) + // Delete all rows from the channelTables table + { + std::string tableName = channelTablesTableName(); + //log() << coral::Debug << "Refresh table " << tableName + // << coral::MessageStream::endmsg; + //queryMgr().deleteAllTableRows( tableName ); + } + + // Delete all rows from the iovTables table + { + std::string tableName = iovTablesTableName(); + //log() << coral::Debug << "Refresh table " << tableName + // << coral::MessageStream::endmsg; + //queryMgr().deleteAllTableRows( tableName ); + } + // **** END **** 3.0.0 schema extensions (task #4307) + */ + + // Do nothing about the main table + { + std::string tableName = mainTableName(); + log() << coral::Debug << "Refresh table " << tableName + << coral::MessageStream::endmsg; + } + + } + catch ( std::exception& e ) + { + log() << coral::Warning << "Exception caught in refreshDatabase(): " + << e.what() << coral::MessageStream::endmsg; + throw; + } + + // Success: the database has been refreshed + log() << coral::Info << "Refresh database" + << ( keepNodes ? " (refresh nodes)" : " (drop nodes)" ) + << "... DONE" << coral::MessageStream::endmsg; +} + +//----------------------------------------------------------------------------- + +void RalDatabase::refreshAllNodes() +{ + log() << coral::Info << "Refresh all nodes..." + << coral::MessageStream::endmsg; + + // Listing the nodes in reverse order may be better + std::vector<std::string> nodes( listAllNodes( false ) ); + std::vector<std::string>::const_iterator node; + for ( node = nodes.begin(); node != nodes.end(); node++ ) + refreshNode( *node ); + + // Success: the nodes have been been refreshed + log() << coral::Info << "Refresh all nodes... DONE" + << coral::MessageStream::endmsg; +} + +//----------------------------------------------------------------------------- + +void RalDatabase::refreshNode( const std::string& fullPath ) +{ + log() << coral::Debug << "Refresh node '" << fullPath << "' ..." + << coral::MessageStream::endmsg; + checkDbOpenTransaction("RalDatabase::refreshNode", cool::ReadWrite ); + + // Fetch the row for this node + RelationalTableRow nodeRow; + try { + nodeRow = fetchNodeTableRow( fullPath ); + } catch ( NodeTableRowNotFound& ) { + // A node with this name does not exist: nothing to drop + log() << coral::Error << "Node '" << fullPath + << "' cannot be refreshed (node not found)" + << coral::MessageStream::endmsg; + throw; + } catch ( coral::QueryException& e ) { + // The query on the node table failed: for instance, the query may fail + // with ORA-00904 if some columns are missing from the node table because + // the process that created the database crashed or was killed while + // altering the node table to change the SQL type of its columns + log() << coral::Error << "The node table cannot be queried: '" + << e.what() << coral::MessageStream::endmsg; + throw; + } + bool isLeaf = + nodeRow[RelationalNodeTable::columnNames::nodeIsLeaf].data<bool>(); + unsigned int nodeId = + nodeRow[RelationalNodeTable::columnNames::nodeId].data<unsigned int>(); + + // Check that the node schema version is supported by this software release + VersionNumber schemaVersion = + nodeRow[RelationalNodeTable::columnNames::nodeSchemaVersion] + .data<std::string>(); + bool isSupported = true; + if ( isLeaf ) { + if ( !RelationalFolder::isSupportedSchemaVersion( schemaVersion ) ) + isSupported = false; + } + else { + if ( !RelationalFolderSet::isSupportedSchemaVersion( schemaVersion ) ) + isSupported = false; + } + if ( VersionInfo::release < schemaVersion ) + { + std::stringstream s; + s << "Cannot refresh node:"; + if ( isLeaf ) s << " folder '"; + else s << " folder set '"; + s << fullPath << " has schema version " << schemaVersion + << " that is newer than this software release " + << VersionInfo::release; + log() << coral::Warning << s.str() << coral::MessageStream::endmsg; + throw RelationalException( s.str(), "RalDatabase" ); + } + else if ( !isSupported ) + { + std::stringstream s; + s << "PANIC! Cannot refresh node:"; + if ( isLeaf ) s << " folder '"; + else s << " folder set '"; + s << fullPath + << "' appears to have been created using UNKNOWN schema version " + << schemaVersion + << " that is older than (or as old as) the current software release " + << VersionInfo::release; + throw RelationalException( s.str(), "RalDatabase" ); + } + + // TRY block + try + { + + // Folder: refresh folder-specific tables and sequences + if ( isLeaf ) + { + std::string objectTableName = + RelationalFolder::objectTableName( nodeRow.data() ); + std::string payloadTableName = + RelationalFolder::payloadTableName( nodeRow.data() ); + std::string tagTableName = + RelationalFolder::tagTableName( nodeRow.data() ); + std::string object2TagTableName = + RelationalFolder::object2TagTableName( nodeRow.data() ); + FolderVersioning::Mode versioningMode = + RelationalFolder::versioningMode( nodeRow.data() ); + std::string tagSequenceName = RelationalTagSequence::sequenceName + ( defaultTablePrefix(), nodeId ); + std::string tableName; + std::string seqName; + // Refresh tables for MV folders + if ( versioningMode == FolderVersioning::MULTI_VERSION ) + { + // Refresh iov2tag table first (it has FK constraints on other tables) + tableName = object2TagTableName; + log() << "Refresh table " << tableName << coral::MessageStream::endmsg; + queryMgr().deleteAllTableRows( tableName ); + // Refresh local tag table + tableName = tagTableName; + log() << "Refresh table " << tableName << coral::MessageStream::endmsg; + queryMgr().deleteAllTableRows( tableName ); + // Refresh local tag sequence + // (NB call nextVal: see RalSchemaMgr::createTagSequence) + seqName = tagSequenceName; + log() << "Refresh sequence " << seqName + << coral::MessageStream::endmsg; + queryMgr().deleteAllTableRows( seqName ); + queryMgr().sequenceMgr().initSequence( seqName ); + queryMgr().sequenceMgr().getSequence( seqName )->nextVal(); + } + // vector payload table + tableName = payloadTableName; + if ( RelationalFolder::payloadMode( nodeRow.data() ) == + PayloadMode::VECTORPAYLOAD ) + { + log() << "Refresh table " << tableName << coral::MessageStream::endmsg; + queryMgr().deleteAllTableRows( tableName ); + // Refresh payload sequence + // (NB call nextVal: see RalSchemaMgr::createPayloadTable) + }; + // Refresh iov table + tableName = objectTableName; + log() << "Refresh table " << tableName << coral::MessageStream::endmsg; + queryMgr().deleteAllTableRows( tableName ); + // Refresh iov sequence + // (NB call nextVal: see RalSchemaMgr::createObjectTable) + seqName = RelationalObjectTable::sequenceName( objectTableName ); + log() << "Refresh sequence " << seqName << coral::MessageStream::endmsg; + queryMgr().deleteAllTableRows( seqName ); + queryMgr().sequenceMgr().initSequence( seqName ); + queryMgr().sequenceMgr().getSequence( seqName )->nextVal(); + // Refresh payload table if it exists + tableName = payloadTableName; + if ( RelationalFolder::payloadMode( nodeRow.data() ) == + PayloadMode::SEPARATEPAYLOAD ) + { + log() << "Refresh table " << tableName << coral::MessageStream::endmsg; + queryMgr().deleteAllTableRows( tableName ); + // Refresh payload sequence + // (NB call nextVal: see RalSchemaMgr::createPayloadTable) + seqName = RelationalPayloadTable::sequenceName( payloadTableName ); + log() << "Refresh sequence " << seqName + << coral::MessageStream::endmsg; + queryMgr().deleteAllTableRows( seqName ); + queryMgr().sequenceMgr().initSequence( seqName ); + queryMgr().sequenceMgr().getSequence( seqName )->nextVal(); + }; + // Refresh channel table last (it is referenced by FKs in the IOV table) + tableName = RelationalChannelTable::defaultTableName + ( defaultTablePrefix(), nodeId ); + log() << "Refresh table " << tableName << coral::MessageStream::endmsg; + queryMgr().deleteAllTableRows( tableName ); + } + + // Folder set + else + { + // Refresh local tag sequence + // (NB call nextVal: see RalSchemaMgr::createTagSequence) + std::string tagSequenceName = RelationalTagSequence::sequenceName + ( defaultTablePrefix(), nodeId ); + std::string seqName = tagSequenceName; + log() << "Refresh sequence " << seqName << coral::MessageStream::endmsg; + queryMgr().deleteAllTableRows( seqName ); + queryMgr().sequenceMgr().initSequence( seqName ); + queryMgr().sequenceMgr().getSequence( seqName )->nextVal(); + } + } + + // CATCH block + catch ( std::exception& e ) + { + log() << coral::Fatal << "Exception caught in refreshNode(): '" + << e.what() << "'" << std::endl; + } + + // Return + log() << coral::Debug << "Refresh node '" << fullPath << "' ... DONE" + << coral::MessageStream::endmsg; +} + +//----------------------------------------------------------------------------- + +UInt32 RalDatabase::insertNodeTableRow +( const std::string& fullPath, + const std::string& description, + bool createParents, + bool isLeaf, + const std::string& payloadSpecDesc, + FolderVersioning::Mode versioningMode, + PayloadMode::Mode payloadMode ) +{ + checkDbOpenTransaction( "RalDatabase::insertNodeTableRow", cool::ReadWrite ); + + // This is the maximum number of nodes that can be created + // Note that this limitation is coupled to the folder name + // generation via the pattern F%4.4i_IOVS --> max: F9999_IOVS + // There is no point in working around this limitation at this time + // as the database will not handle several thousands of tables well. + // (At least the MySQL backend has proven to be problematic at this scale.) + unsigned int kMaxNumberOfNodes = 9999; + + log() << "Create a new node with name " << fullPath + << coral::MessageStream::endmsg; + // Cross-check that the database is open + if ( ! isOpen() ) throw DatabaseNotOpen( "RalDatabase" ); + + /* + // AV 09.05.2005 Move this AFTER locking sequence to fix David Front's bug + // Check if a folder or folder set with this name already exists + if ( existsNode( fullPath ) ) { + throw NodeExists( fullPath, "RalDatabase" ); + } + */ + + // Split the full path + HvsPathHandler pathHandler; + std::pair<std::string, std::string> parentAndChild; + try { + parentAndChild = pathHandler.splitFullPath( fullPath ); + } catch ( HvsPathHandlerException& ) { + log() << coral::Error << "Invalid folder node path '" + << fullPath << "'" << coral::MessageStream::endmsg; + throw; + } + std::string parentFullPath = parentAndChild.first; + std::string unresolvedName = parentAndChild.second; + + // Look for the parent folder set + unsigned int nodeParentId; + try { + RelationalTableRow row = fetchNodeTableRow( parentFullPath ); + bool isParentLeaf = + row[RelationalNodeTable::columnNames::nodeIsLeaf].data<bool>(); + if ( isParentLeaf ) { + std::stringstream s; + s << "Cannot create node '" << fullPath + << "', because the parent path contains a leaf node"; + throw RelationalException( s.str(), "RalDatabase" ); + } + IFolderSetPtr parent + ( new RelationalFolderSet( relationalDbPtr(), row.data() ) ); + nodeParentId = parent->id(); + } catch ( NodeTableRowNotFound& ) { + if( ! createParents ) throw; + createFolderSet( parentFullPath, // full path + "", // description + true ); // createParents + RelationalTableRow row = fetchNodeTableRow( parentFullPath ); + IFolderSetPtr parent + ( new RelationalFolderSet( relationalDbPtr(), row.data() ) ); + nodeParentId = parent->id(); + } + + // Get a new folder ID from the sequence + std::string nodeSeqName = + RelationalNodeTable::sequenceName( nodeTableName() ); + RelationalSequencePtr nodeSeq + ( queryMgr().sequenceMgr().getSequence( nodeSeqName ) ); + unsigned int nodeId = nodeSeq->nextVal(); + if ( nodeId > kMaxNumberOfNodes ) + throw RelationalException + ( "Node ID out of boundaries", "RalDatabase" ); + std::string insertionTime = nodeSeq->currDate(); // nextVal -> recent! + + // AV 09.05.2005 Move this AFTER locking sequence to fix David Front's bug + // Check if a folder or folder set with this name already exists + if ( existsNode( fullPath ) ) { + /* + // NB Error message only makes sense if first cross-check is also kept + log() << coral::Error + << "Congratulations! You tried to create folder " << fullPath + << " from two separate connections EXACTLY at the same time!" + << coral::MessageStream::endmsg; + */ + throw NodeExists( fullPath, "RalDatabase" ); + } + + // Register the folder in the node table + // AV 14-03-2005 TEMPORARY? Till RAL handles NULL values better + // Do not insert NULL values: insert empty strings instead + const IRecordSpecification& nodeTableSpec = + RelationalNodeTable::tableSpecification(); + Record data( nodeTableSpec ); + data[RelationalNodeTable::columnNames::nodeId].setValue + ( nodeId ); + data[RelationalNodeTable::columnNames::nodeParentId].setValue + ( nodeParentId ); + data[RelationalNodeTable::columnNames::nodeName].setValue + ( unresolvedName ); + data[RelationalNodeTable::columnNames::nodeFullPath].setValue + ( fullPath ); + data[RelationalNodeTable::columnNames::nodeDescription].setValue + ( description ); + data[RelationalNodeTable::columnNames::nodeIsLeaf].setValue + ( isLeaf ); + if ( isLeaf ) { + data[RelationalNodeTable::columnNames::nodeSchemaVersion].setValue + ( std::string( RelationalFolder::folderSchemaVersion + ( payloadMode ) ) ); + } + else { + data[RelationalNodeTable::columnNames::nodeSchemaVersion].setValue + ( std::string( RelationalFolderSet::folderSetSchemaVersion() ) ); + } + data[RelationalNodeTable::columnNames::nodeInsertionTime].setValue + ( insertionTime ); + data[RelationalNodeTable::columnNames::lastModDate].setValue + ( insertionTime ); + + if ( isLeaf ) + { + // Set the folder specific information + data[RelationalNodeTable::columnNames::folderVersioningMode].setValue + ( (int)versioningMode ); + data[RelationalNodeTable::columnNames::folderPayloadSpecDesc].setValue + ( payloadSpecDesc ); + data[RelationalNodeTable::columnNames::folderPayloadInline].setValue + ( RelationalFolder::folderSchemaPayloadMode( payloadMode ) ); + if ( payloadMode != PayloadMode::INLINEPAYLOAD ) + data[RelationalNodeTable::columnNames::folderPayloadExtRef].setValue + ( RelationalPayloadTable::defaultTableName + ( defaultTablePrefix(), nodeId ) ); + else + data[RelationalNodeTable::columnNames::folderPayloadExtRef].setValue + ( std::string( "" ) ); + data[RelationalNodeTable::columnNames::folderChannelSpecDesc].setValue + ( std::string( "" ) ); + data[RelationalNodeTable::columnNames::folderChannelExtRef].setValue + ( std::string( "" ) ); + data[RelationalNodeTable::columnNames::folderObjectTableName].setValue + ( RelationalObjectTable::defaultTableName + ( defaultTablePrefix(), nodeId ) ); + data[RelationalNodeTable::columnNames::folderTagTableName].setValue + ( RelationalTagTable::defaultTableName + ( defaultTablePrefix(), nodeId ) ); + data[RelationalNodeTable::columnNames::folderObject2TagTableName].setValue + ( RelationalObject2TagTable::defaultTableName + ( defaultTablePrefix(), nodeId ) ); + data[RelationalNodeTable::columnNames::folderChannelTableName].setValue + ( RelationalChannelTable::defaultTableName + ( defaultTablePrefix(), nodeId ) ); + } + else + { + // Insert default values for folder sets + data[RelationalNodeTable::columnNames::folderVersioningMode].setValue + ( (int)FolderVersioning::NONE ); // default for folder sets + data[RelationalNodeTable::columnNames::folderPayloadSpecDesc].setValue + ( std::string( "" ) ); + data[RelationalNodeTable::columnNames::folderPayloadInline].setValue + ( RelationalFolderSet::folderSetSchemaPayloadMode() ); // default (0) + data[RelationalNodeTable::columnNames::folderPayloadExtRef].setValue + ( std::string( "" ) ); + data[RelationalNodeTable::columnNames::folderChannelSpecDesc].setValue + ( std::string( "" ) ); + data[RelationalNodeTable::columnNames::folderChannelExtRef].setValue + ( std::string( "" ) ); + data[RelationalNodeTable::columnNames::folderObjectTableName].setValue + ( std::string( "" ) ); + data[RelationalNodeTable::columnNames::folderTagTableName].setValue + ( std::string( "" ) ); + data[RelationalNodeTable::columnNames::folderObject2TagTableName].setValue + ( std::string( "" ) ); + data[RelationalNodeTable::columnNames::folderChannelTableName].setValue + ( std::string( "" ) ); + } + + /* + // Check that all column values are within their allowed range. + // REMOVE? This is already done by the IField::setValue calls above. + nodeTableSpec.validate( data.attributeList() ); + */ + + // Perform the actual db update + queryMgr().insertTableRow( nodeTableName(), data ); + return nodeId; +} + +//----------------------------------------------------------------------------- + +IFolderSetPtr RalDatabase::createFolderSet( const std::string& fullPath, + const std::string& description, + bool createParents ) +{ + log() << coral::Verbose + << "Create folder set " << fullPath << coral::MessageStream::endmsg; + + if ( ! transactionMgr()->autoTransactions() ) { + throw RelationalException("Cannot create folder set in manual " + "transaction mode", "RalDatabase"); + } + + // Cross-check that the database is open + checkDbOpenTransaction( "RalDatabase::createFolderSet", cool::ReadWrite ); + + bool isLeaf = false; + std::string payloadSpecDesc = ""; + FolderVersioning::Mode folderVersioningMode = FolderVersioning::NONE; + + unsigned int nodeId = insertNodeTableRow + ( fullPath, description, createParents, isLeaf, + // The "" payloadSpecDesc argument is set to NULL only in the Oracle db + payloadSpecDesc, folderVersioningMode ); + + std::string tagSequenceName = RelationalTagSequence::sequenceName + ( defaultTablePrefix(), nodeId ); + schemaMgr().createTagSequence( tagSequenceName ); + + log() << coral::Verbose + << "Created folder set " << fullPath + << ": now fetch it and return it" << coral::MessageStream::endmsg; + + return getFolderSet( fullPath ); +} + + +//----------------------------------------------------------------------------- + +IFolderPtr RalDatabase::createFolder +( const std::string& fullPath, + const IRecordSpecification& payloadSpec, + const std::string& description, + FolderVersioning::Mode versioningMode, + bool createParents ) +{ + // Cross-check that the database is open + if ( ! isOpen() ) throw DatabaseNotOpen( "RalDatabase" ); + // Cross-check that we're not in manual transaction more + if ( ! transactionMgr()->autoTransactions() ) { + throw RelationalException("Cannot create folder in manual " + "transaction mode", "RalDatabase"); + } + IFolderPtr folder = + createFolder( fullPath, payloadSpec, description, + versioningMode, createParents, PayloadMode::INLINEPAYLOAD ); + return folder; +} + +//----------------------------------------------------------------------------- + +IFolderPtr RalDatabase::createFolder +( const std::string& fullPath, + const IFolderSpecification& folderSpec, + const std::string& description, + bool createParents ) +{ + // Cross-check that the database is open + if ( ! isOpen() ) throw DatabaseNotOpen( "RalDatabase" ); + // Cross-check that we're not in manual transaction more + if ( ! transactionMgr()->autoTransactions() ) { + throw RelationalException("Cannot create folder in manual " + "transaction mode", "RalDatabase"); + } + FolderVersioning::Mode versioningMode = folderSpec.versioningMode(); + const IRecordSpecification& payloadSpec = folderSpec.payloadSpecification(); + IFolderPtr folder = +#ifdef COOL290 + createFolder( fullPath, payloadSpec, description, + versioningMode, createParents, folderSpec.payloadMode() ); +#else + createFolder( fullPath, payloadSpec, description, + versioningMode, createParents, + ( folderSpec.hasPayloadTable() ? + PayloadMode::SEPARATEPAYLOAD : + PayloadMode::INLINEPAYLOAD ) + ); +#endif + return folder; +} + +//----------------------------------------------------------------------------- + +IFolderPtr RalDatabase::createFolder +( const std::string& fullPath, + const IRecordSpecification& payloadSpec, + const std::string& description, + FolderVersioning::Mode versioningMode, + bool createParents, + PayloadMode::Mode payloadMode ) +{ + log() << coral::Verbose + << "Create folder " << fullPath << coral::MessageStream::endmsg; + + + // Cross-check that the database is open + if ( ! isOpen() ) throw DatabaseNotOpen( "RalDatabase" ); + + // Validate the payload specification. + // Throws InvalidPayloadSpecification if the payload spec is invalid: + // there can be at most 900 fields, including up to 10 BLOB fields; + // field names must have between 1 and 30 characters (including only + // letters, digits or '_'), must start with a letter and cannot start + // with the "COOL_" prefix (in any lowercase/uppercase combination). + validatePayloadSpecification( payloadSpec ); + + // Register the folder in the node table + std::string payloadSpecDesc = encodeRecordSpecification( payloadSpec ); + + bool isLeaf = true; + unsigned int nodeId = insertNodeTableRow + ( fullPath, description, createParents, + isLeaf, payloadSpecDesc, versioningMode, payloadMode ); + + + // Create the IOV table for the folder + std::string objectTableName = RelationalObjectTable::defaultTableName + ( defaultTablePrefix(), nodeId ); + std::string payloadTableName = ""; + if ( payloadMode != PayloadMode::INLINEPAYLOAD ) + payloadTableName = RelationalPayloadTable::defaultTableName + ( defaultTablePrefix(), nodeId ); + std::string obj2tagTableName = RelationalObject2TagTable::defaultTableName + ( defaultTablePrefix(), nodeId ); + std::string tagTableName = RelationalTagTable::defaultTableName + ( defaultTablePrefix(), nodeId ); + std::string tagSequenceName = RelationalTagSequence::sequenceName + ( defaultTablePrefix(), nodeId ); + std::string channelTableName = + RelationalChannelTable::defaultTableName( defaultTablePrefix(), nodeId ); + + if ( versioningMode == FolderVersioning::SINGLE_VERSION || + versioningMode == FolderVersioning::MULTI_VERSION ) { + + schemaMgr().createChannelTable( channelTableName ); + + if ( payloadMode == PayloadMode::SEPARATEPAYLOAD ) { + schemaMgr().createPayloadTable( payloadTableName, + objectTableName, + payloadSpec, + payloadMode ); + schemaMgr().createObjectTable + ( objectTableName, channelTableName, + RelationalPayloadTable::defaultSpecification( payloadMode ), + versioningMode, payloadTableName, payloadMode ); + } + else if ( payloadMode == PayloadMode::VECTORPAYLOAD) + { + //std::cout << "creating vector folder " << std::endl; + schemaMgr().createObjectTable + ( objectTableName, channelTableName, + RecordSpecification(), + versioningMode, payloadTableName, payloadMode ); + + schemaMgr().createPayloadTable( payloadTableName, + objectTableName, + payloadSpec, + payloadMode ); + } + else + { + // inline payload + schemaMgr().createObjectTable + ( objectTableName, channelTableName, payloadSpec, + versioningMode, "", payloadMode ); + } + + if ( versioningMode == FolderVersioning::MULTI_VERSION ) { + schemaMgr().createTagSequence( tagSequenceName ); + schemaMgr().createTagTable( tagTableName ); + schemaMgr().createObject2TagTable( obj2tagTableName, + objectTableName, + tagTableName, + payloadTableName, + payloadMode ); + } + + } else { + std::stringstream s; + s << "Invalid versioning mode specified: " << versioningMode; + throw RelationalException( s.str(), "RalDatabase" ); + } + + // TEMPORARY? Sleep to work around the ORA-01466 problem (Oracle only) + if ( m_sessionMgr->databaseTechnology() == "Oracle" && m_useTimeout ) { + log() << "Sleep to work around the ORA-01466 problem" + << coral::MessageStream::endmsg; + cool::sleep(1); + } + + // Get and return the IFolder instance for the folder that has just been + // created [NB This causes a second round trip to fetch the data that has + // just been written, but performance impact is low because folder creation + // is a relatively rare operation, and it's more modular to use getFolder] + log() << coral::Verbose + << "Created folder " << fullPath + << ": now fetch it and return it" << coral::MessageStream::endmsg; + return getFolder( fullPath ); + +} + +//----------------------------------------------------------------------------- + +void RalDatabase::updateNodeTableDescription +( const std::string& fullPath, const std::string& description ) const +{ + log() << "Updating node description at path: " + << fullPath << coral::MessageStream::endmsg; + + checkDbOpenTransaction( "updateNodeTableDescription", cool::ReadWrite ); + + // Define the SET and WHERE clauses for the update using bind variables + RecordSpecification updateDataSpec; + updateDataSpec.extend( "desc", + RelationalNodeTable::columnTypeIds::nodeDescription ); + updateDataSpec.extend( "path", + RelationalNodeTable::columnTypeIds::nodeFullPath ); + Record updateData( updateDataSpec ); + updateData["desc"].setValue( description ); + updateData["path"].setValue( fullPath ); + std::string setClause = RelationalNodeTable::columnNames::nodeDescription; + setClause += "= :desc"; + setClause += ", "; + setClause += RelationalNodeTable::columnNames::lastModDate; + setClause += " = " + queryMgr().serverTimeClause(); + std::string whereClause = RelationalNodeTable::columnNames::nodeFullPath; + whereClause += "= :path"; + + // Execute the update + if ( 1 != queryMgr().updateTableRows + ( nodeTableName(), setClause, whereClause, updateData ) ) + throw RowNotUpdated + ( "Could not update a row of the node table", "RalDatabase" ); + +} + +//----------------------------------------------------------------------------- + +IFolderPtr RalDatabase::getFolder( const std::string& fullPath ) +{ + if ( TimingReportMgr::isActive() ) + TimingReportMgr::startTimer( "cool::RalDatabase::getFolder()" ); + + checkDbOpenTransaction( "RalDatabase::getFolder", cool::ReadOnly ); + + log() << "Get folder with name " << fullPath << coral::MessageStream::endmsg; + if ( ! isOpen() ) throw DatabaseNotOpen( "RalDatabase" ); + + IFolderPtr folder; + + // For Update connections, fetch each folder every time + if ( !sessionMgr()->isReadOnly() ) + { + try { + RelationalTableRow row = fetchNodeTableRow( fullPath ); + folder = getFolder( row ); + } catch ( NodeTableRowNotFound& ) { + throw FolderNotFound( fullPath, "RalDatabase" ); + } + } + + // For ReadOnly connections, use the folder cache + else + { + if ( m_nodes.size() == 0 ) preloadAllNodes(); + if ( m_nodes.find( fullPath ) != m_nodes.end() ) + folder = getFolder( *m_nodes[fullPath] ); + else + throw FolderNotFound( fullPath, "RalDatabase" ); + } + + if ( TimingReportMgr::isActive() ) + TimingReportMgr::stopTimer( "cool::RalDatabase::getFolder()" ); + return folder; +} + +//----------------------------------------------------------------------------- + +IFolderPtr RalDatabase::getFolder( const RelationalTableRow& row ) +{ + std::string fullPath = + row[RelationalNodeTable::columnNames::nodeFullPath].data<std::string>(); + bool isLeaf = + row[RelationalNodeTable::columnNames::nodeIsLeaf].data<bool>(); + if ( ! isLeaf ) + throw FolderNotFound( fullPath, "RalDatabase", true ); + VersionNumber schemaVersion = + row[RelationalNodeTable::columnNames::nodeSchemaVersion] + .data<std::string>(); + IFolderPtr folder; + // Handle all well-defined hardcoded folder schema versions supported by + // the RelationalFolder class; return an unusable Unsupported folder + // for folders with schema versions higher than the present s/w release; + // throw a PANIC exception for all other values (should never happen!). + if ( RelationalFolder::isSupportedSchemaVersion( schemaVersion ) ) + { + folder.reset + ( new RelationalFolder( relationalDbPtr(), row.data() ) ); + } + else if ( VersionInfo::release < schemaVersion ) + { + folder.reset + ( new RelationalFolderUnsupported( relationalDbPtr(), row.data() ) ); + } + else if ( schemaVersion == VersionNumber( "2.0.0" ) ) + { + folder.reset + ( new RelationalFolderUnsupported( relationalDbPtr(), row.data() ) ); + //throw UnsupportedFolderSchema + // ( fullPath, schemaVersion, "RelationalFolderUnsupported" ); + } + else + { + std::stringstream s; + s << "PANIC! Cannot get folder '" << fullPath + << "': it appears to have been created using UNKNOWN schema version " + << schemaVersion + << " that is older than (or as old as) the current software release " + << VersionInfo::release; + throw RelationalException( s.str(), "RalDatabase" ); + } + return folder; +} + +//----------------------------------------------------------------------------- + +IFolderSetPtr RalDatabase::getFolderSet( const std::string& fullPath ) +{ + log() << "Get folderset with name " << fullPath + << coral::MessageStream::endmsg; + checkDbOpenTransaction( "RalDatabase::getFolderSet", cool::ReadOnly ); + + IFolderSetPtr folderSet; + + // For Update connections, fetch each folder every time + if ( !sessionMgr()->isReadOnly() ) + { + try { + RelationalTableRow row = fetchNodeTableRow( fullPath ); + folderSet = getFolderSet( row ); + } catch ( NodeTableRowNotFound& ) { + throw FolderSetNotFound( fullPath, "RalDatabase" ); + } + } + + // For ReadOnly connections, use the folder cache + else + { + if ( m_nodes.size() == 0 ) preloadAllNodes(); + if ( m_nodes.find( fullPath ) != m_nodes.end() ) + folderSet = getFolderSet( *m_nodes[fullPath] ); + else + throw FolderSetNotFound( fullPath, "RalDatabase" ); + } + + return folderSet; +} + +//----------------------------------------------------------------------------- + +IFolderSetPtr RalDatabase::getFolderSet( const RelationalTableRow& row ) +{ + std::string fullPath = + row[RelationalNodeTable::columnNames::nodeFullPath].data<std::string>(); + bool isLeaf = + row[RelationalNodeTable::columnNames::nodeIsLeaf].data<bool>(); + if ( isLeaf ) + throw FolderSetNotFound( fullPath, "RalDatabase", true ); + VersionNumber schemaVersion = + row[RelationalNodeTable::columnNames::nodeSchemaVersion] + .data<std::string>(); + IFolderSetPtr folderSet; + // Handle all well-defined hardcoded folder set schema versions supported by + // the RelationalFolderSet class; return an unusable Unsupported folderSet + // for folder sets with schema versions higher than the present s/w release; + // throw a PANIC exception for all other values (should never happen!). + if ( RelationalFolderSet::isSupportedSchemaVersion( schemaVersion ) ) + { + folderSet.reset + ( new RelationalFolderSet( relationalDbPtr(), row.data() ) ); + } + else if ( VersionInfo::release < schemaVersion ) + { + folderSet.reset + ( new RelationalFolderSetUnsupported( relationalDbPtr(), row.data() ) ); + } + else + { + std::stringstream s; + s << "PANIC! Cannot get folder set '" << fullPath + << "': it appears to have been created using UNKNOWN schema version " + << schemaVersion + << " that is older than (or as old as) the current software release " + << VersionInfo::release; + throw RelationalException( s.str(), "RalDatabase" ); + } + return folderSet; +} + +//----------------------------------------------------------------------------- + +void RalDatabase::preloadAllNodes() +{ + log() << "Preload all nodes" << coral::MessageStream::endmsg; + + if ( ! isOpen() ) throw DatabaseNotOpen( "RalDatabase" ); + + if ( m_nodes.size() != 0 ) + throw RelationalException + ( "PANIC! Nodes already preloaded", "RalDatabase" ); + + if ( !sessionMgr()->isReadOnly() ) + throw RelationalException + ( "PANIC! Cannot preload nodes in update mode", "RalDatabase" ); + + std::vector<RelationalTableRow> rows = nodeMgr().fetchAllNodeTableRows(); + + // Loop over all nodes in the node table + for ( std::vector<RelationalTableRow>::const_iterator + row = rows.begin(); row != rows.end(); ++row ) + { + std::string fullPath = + (*row)[RelationalNodeTable::columnNames::nodeFullPath] + .data<std::string>(); + + if ( m_nodes.find( fullPath ) != m_nodes.end() ) + { + std::stringstream s; + s << "PANIC! Node '" << fullPath + << "' was found more than once in the node table!"; + throw RelationalException( s.str(), "RalDatabase" ); + } + + m_nodes[fullPath] = new RelationalTableRow( *row ); + + } +} + +//----------------------------------------------------------------------------- + +bool RalDatabase::dropAllNodes( bool keepRoot ) +{ + log() << coral::Info << "Drop all nodes (" + << ( keepRoot ? "" : "do not" ) + << "keep root)..." << coral::MessageStream::endmsg; + + // AV 2005-07-07 + // Return true if all node, tag and tag2tag rows are deleted and all folder + // tables (for nodes that are folders) are dropped as expected. + // Return false (without throwing any exception) if the node rows and + // any associated tables do not exist any more on exit from this method, + // but some node rows or associated tables did not exist to start with. + bool status = true; + + // Reinitialise the node sequence if keepRoot is true (for tests only!) + std::string nodeSqNm = RelationalNodeTable::sequenceName( nodeTableName() ); + unsigned int nodeSqCurr = 0; // Get the real value in the RO transaction + + // Throw an Exception if the schema of one of the nodes in this database + // is more recent than the schema version supported by the current release: + // in this case make sure you do not drop ANY node (throw immediately)! + { + + // Iterate over all folders and folder sets and compare schema versions + // to a well-defined hardcoded list supported by this s/w release + RelationalQueryDefinition queryDef; + queryDef.addFromItem( nodeTableName() ); + queryDef.addSelectItems( RelationalNodeTable::tableSpecification() ); + std::vector<RelationalTableRow> rows = + queryMgr().fetchOrderedRows( queryDef ); // no WHERE or ORDER clause + for ( std::vector<RelationalTableRow>::const_iterator + row = rows.begin(); row != rows.end(); row++ ) + { + const std::string fullPath = + (*row)[RelationalNodeTable::columnNames::nodeFullPath] + .data<std::string>(); + bool isLeaf = + (*row)[RelationalNodeTable::columnNames::nodeIsLeaf] + .data<bool>(); + const VersionNumber schemaVersion = + (*row)[RelationalNodeTable::columnNames::nodeSchemaVersion] + .data<std::string>(); + bool isSupported = true; + if ( isLeaf ) + { + if ( !RelationalFolder::isSupportedSchemaVersion( schemaVersion ) ) + isSupported = false; + } + else + { + if ( !RelationalFolderSet::isSupportedSchemaVersion( schemaVersion ) ) + isSupported = false; + } + if ( VersionInfo::release < schemaVersion ) + { + std::stringstream s; + s << "Cannot drop database:"; + if ( isLeaf ) s << " folder '"; + else s << " folder set '"; + s << fullPath << " has schema version " << schemaVersion + << " that is newer than this software release " + << VersionInfo::release; + log() << coral::Warning << s.str() << coral::MessageStream::endmsg; + throw RelationalException( s.str(), "RalDatabase" ); + } + else if ( ! isSupported ) + { + std::stringstream s; + s << "PANIC! Cannot drop database:"; + if ( isLeaf ) s << " folder '"; + else s << " folder set '"; + s << fullPath + << "' appears to have been created using UNKNOWN schema version " + << schemaVersion + << " that is older than (or as old as) the current software release " + << VersionInfo::release; + throw RelationalException( s.str(), "RalDatabase" ); + } + } + + // Get the current value of the node sequence (only needed if keepRoot) + // NB: disable the for update clause (it needs a R/W transaction) + if ( keepRoot ) + { + bool forUpdate = false; + nodeSqCurr = + queryMgr().sequenceMgr().getSequence( nodeSqNm )->currVal( forUpdate ); + } + + } + + // Throw a RelationalException if a node row cannot be deleted or one table + // cannot be dropped (i.e. continues to exist on exit from this method). + + // Listing the nodes in reverse order ensures that integrity constraints + // are not violated (children are dropped before their parents) + std::vector<std::string> nodes( listAllNodes( false ) ); + std::vector<std::string>::const_iterator node; + log() << coral::Debug << "Will drop nodes in this order:" + << coral::MessageStream::endmsg; + HvsPathHandler handler; + for ( node = nodes.begin(); node != nodes.end(); node++ ) + { + if ( !keepRoot || *node != handler.rootFullPath() ) + { + log() << coral::Debug << "Will drop '" << *node << "'" + << coral::MessageStream::endmsg; + } + } + for ( node = nodes.begin(); node != nodes.end(); node++ ) + { + if ( !keepRoot || *node != handler.rootFullPath() ) + { + log() << coral::Debug << "Now drop '" << *node << "'" + << coral::MessageStream::endmsg; + if ( ! dropNode( *node ) ) status = false; + } + } + + // If you keep the root node, this is only used for tests: + // reinitialise the sequence associated to the node table. + if ( keepRoot && nodeSqCurr > 0 ) + { + log() << coral::Debug << "Refresh sequence " << nodeSqNm + << coral::MessageStream::endmsg; + queryMgr().deleteAllTableRows( nodeSqNm ); + queryMgr().sequenceMgr().initSequence( nodeSqNm ); + queryMgr().sequenceMgr().getSequence( nodeSqNm )->nextVal(); // root node! + } + + // Success: the nodes do not exist anymore + // Return status code 'false' if some nodes or tables were missing already + log() << coral::Info << "Drop all nodes (" + << ( keepRoot ? "" : "do not " ) + << "keep root)... DONE" << coral::MessageStream::endmsg; + return status; + +} + +//----------------------------------------------------------------------------- + +bool RalDatabase::dropNode( const std::string& fullPath ) +{ + log() << "Drop node with full path " << fullPath + << coral::MessageStream::endmsg; + checkDbOpenTransaction( "RalDatabase::dropNode", cool::ReadWrite ); + + // AV 2005-07-07 + // Return true if the node, tag and tag2tag rows for this node are deleted + // and all folder tables (if the node is a folder) are dropped as expected. + // Return false (without throwing any exception) if any such row and + // any associated tables do not exist any more on exit from this method, + // but the node or some associated tables did not exist to start with. + bool status = true; + + // Throw a RelationalException if the node row cannot be deleted or one table + // cannot be dropped (i.e. continues to exist on exit from this method). + // Throw a RelationalException if the node is a non-empty folder set. + // Also deletes any tags and tag2tag associated to the node + // (and throws a RelationalException if such tags cannot be deleted). + + // Fetch the row for this node + RelationalTableRow nodeRow; + try { + nodeRow = fetchNodeTableRow( fullPath ); + } catch ( NodeTableRowNotFound& ) { + // A node with this name does not exist: nothing to drop + log() << coral::Warning << "Node '" << fullPath + << "' cannot be dropped (node not found)" + << coral::MessageStream::endmsg; + return false; + } catch ( coral::QueryException& e ) { + // The query on the node table failed: for instance, the query may fail + // with ORA-00904 if some columns are missing from the node table because + // the process that created the database crashed or was killed while + // altering the node table to change the SQL type of its columns + log() << coral::Warning << "The node table cannot be queried: '" + << e.what() << coral::MessageStream::endmsg; + return false; + } + bool isLeaf = + nodeRow[RelationalNodeTable::columnNames::nodeIsLeaf].data<bool>(); + unsigned int nodeId = + nodeRow[RelationalNodeTable::columnNames::nodeId].data<unsigned int>(); + + // Throw TagIsLocked if any tags applied to this node are locked + // (either LOCKED or PARTIALLYLOCKED - both are equivalent here) + { + std::vector<RelationalTableRow> rows = + tagMgr().fetchGlobalTagTableRows( nodeId ); + for ( std::vector<RelationalTableRow>::const_iterator + row = rows.begin(); row != rows.end(); ++row ) { + HvsTagLock::Status lockStatus = + HvsTagLock::Status + ( (*row)[RelationalGlobalTagTable::columnNames::tagLockStatus] + .data<UInt16>() ); + if ( lockStatus != HvsTagLock::UNLOCKED ) { + std::string tagName = + (*row)[RelationalGlobalTagTable::columnNames::tagName] + .data<std::string>(); + throw TagIsLocked + ( "Cannot drop node '" + fullPath + + "': tag '" + tagName + "' is locked", "RalDatabase" ); + } + } + } + + // Check that the node schema version is supported by this software release + VersionNumber schemaVersion = + nodeRow[RelationalNodeTable::columnNames::nodeSchemaVersion] + .data<std::string>(); + bool isSupported = true; + if ( isLeaf ) { + if ( !RelationalFolder::isSupportedSchemaVersion( schemaVersion ) ) + isSupported = false; + } + else { + if ( !RelationalFolderSet::isSupportedSchemaVersion( schemaVersion ) ) + isSupported = false; + } + if ( VersionInfo::release < schemaVersion ) + { + std::stringstream s; + s << "Cannot drop node:"; + if ( isLeaf ) s << " folder '"; + else s << " folder set '"; + s << fullPath << " has schema version " << schemaVersion + << " that is newer than this software release " + << VersionInfo::release; + log() << coral::Warning << s.str() << coral::MessageStream::endmsg; + throw RelationalException( s.str(), "RalDatabase" ); + } + else if ( !isSupported ) + { + std::stringstream s; + s << "PANIC! Cannot drop node:"; + if ( isLeaf ) s << " folder '"; + else s << " folder set '"; + s << fullPath + << "' appears to have been created using UNKNOWN schema version " + << schemaVersion + << " that is older than (or as old as) the current software release " + << VersionInfo::release; + throw RelationalException( s.str(), "RalDatabase" ); + } + + // Folder: drop folder-specific tables and sequences + if ( isLeaf ) + { + std::string objectTableName = + RelationalFolder::objectTableName( nodeRow.data() ); + std::string payloadTableName = + RelationalFolder::payloadTableName( nodeRow.data() ); + std::string tagTableName = + RelationalFolder::tagTableName( nodeRow.data() ); + std::string object2TagTableName = + RelationalFolder::object2TagTableName( nodeRow.data() ); + FolderVersioning::Mode versioningMode = + RelationalFolder::versioningMode( nodeRow.data() ); + std::string tagSequenceName = RelationalTagSequence::sequenceName + ( defaultTablePrefix(), nodeId ); + std::string tableName; + std::string seqName; + // Drop tables for MV folders + if ( versioningMode == FolderVersioning::MULTI_VERSION ) + { + // Drop iov2tag table first as it has FK constraints on the other tables + tableName = object2TagTableName; + log() << "Drop table " << tableName << coral::MessageStream::endmsg; + if ( ! schemaMgr().dropTable( tableName ) ) status = false; + // Drop local tag table + tableName = tagTableName; + log() << "Drop table " << tableName << coral::MessageStream::endmsg; + if ( ! schemaMgr().dropTable( tableName ) ) status = false; + // Drop local tag sequence + seqName = tagSequenceName; + log() << "Drop sequence " << seqName << coral::MessageStream::endmsg; + if ( ! queryMgr().sequenceMgr().existsSequence( seqName ) ) { + status = false; + } else { + queryMgr().sequenceMgr().dropSequence( seqName ); + } + } + // for vector payload folders, drop payload table before iov table + if ( RelationalFolder::payloadMode( nodeRow.data() ) == + PayloadMode::VECTORPAYLOAD ) + { + tableName = payloadTableName; + if ( ! schemaMgr().dropTable( tableName ) ) status = false; + } + // Drop iov table + tableName = objectTableName; + log() << "Drop table " << tableName << coral::MessageStream::endmsg; + if ( ! schemaMgr().dropTable( tableName ) ) status = false; + // Drop iov sequence + seqName = RelationalObjectTable::sequenceName( objectTableName ); + log() << "Drop sequence " << seqName << coral::MessageStream::endmsg; + if ( ! queryMgr().sequenceMgr().existsSequence( seqName ) ) { + status = false; + } else { + queryMgr().sequenceMgr().dropSequence( seqName ); + } + // if exists, drop payload table + tableName = payloadTableName; + if ( RelationalFolder::payloadMode( nodeRow.data() ) == + PayloadMode::SEPARATEPAYLOAD ) + { + if ( ! schemaMgr().dropTable( tableName ) ) status = false; + // Drop payload sequence + seqName = RelationalPayloadTable::sequenceName( payloadTableName ); + log() << "Drop sequence " << seqName << coral::MessageStream::endmsg; + if ( ! queryMgr().sequenceMgr().existsSequence( seqName ) ) { + status = false; + } else { + queryMgr().sequenceMgr().dropSequence( seqName ); + } + }; + // Drop channel table last as it is referenced by FKs in the iov table + tableName = + RelationalChannelTable::defaultTableName( defaultTablePrefix(), nodeId ); + log() << "Drop table " << tableName << coral::MessageStream::endmsg; + if ( ! schemaMgr().dropTable( tableName ) ) status = false; + } + + // Folder set: make sure it is empty before deleting it and drop tag sequence + else + { + // Make sure it is empty before deleting it + bool hasFolders = ! ( listNodes( nodeId, true ).empty() ); + bool hasFolderSets = ! ( listNodes( nodeId, false ).empty() ); + if ( hasFolders || hasFolderSets ) { + std::stringstream s; + s << "Cannot drop folderset '" << fullPath + << "', because it is not empty"; + throw RelationalException( s.str(), "RalDatabase" ); + } + // Drop local tag sequence + std::string tagSequenceName = RelationalTagSequence::sequenceName + ( defaultTablePrefix(), nodeId ); + std::string seqName = tagSequenceName; + log() << "Drop sequence " << seqName << coral::MessageStream::endmsg; + if ( ! queryMgr().sequenceMgr().existsSequence( seqName ) ) { + status = false; + } else { + queryMgr().sequenceMgr().dropSequence( seqName ); + } + } + + // Delete all tag2tag relations associated to this node + if ( ! session().nominalSchema().existsTable( tag2TagTableName() ) ) + status = false; + else + tagMgr().deleteTag2TagTableRowsForNode( nodeId ); + + // Delete all global tags associated to this node + if ( ! session().nominalSchema().existsTable( globalTagTableName() ) ) + status = false; + else + tagMgr().deleteGlobalTagTableRowsForNode( nodeId ); + + // Delete the node from the node table + RecordSpecification whereDataSpec; + whereDataSpec.extend( "fullName", + RelationalNodeTable::columnTypeIds::nodeFullPath ); + Record whereData( whereDataSpec ); + whereData["fullName"].setValue( fullPath ); + std::string whereClause = RelationalNodeTable::columnNames::nodeFullPath; + whereClause += "= :fullName"; + queryMgr().deleteTableRows + ( nodeTableName(), whereClause, whereData, 1 ); + + // Success: the node does not exist anymore + // Return status code 'false' if the node or some tables were missing already + return status; +} + +//----------------------------------------------------------------------------- + +boost::shared_ptr<RelationalObjectTable> +RalDatabase::relationalObjectTable( const RelationalFolder& folder ) const +{ + boost::shared_ptr<RelationalObjectTable> + objectTable( new RelationalObjectTable( &(queryMgr()), false, folder ) ); + return objectTable; +} + +//----------------------------------------------------------------------------- + +boost::shared_ptr<RalSessionMgr> RalDatabase::sessionMgr() const +{ + return m_sessionMgr; +} + +//----------------------------------------------------------------------------- + +coral::ISessionProxy& RalDatabase::session() const +{ + return m_sessionMgr->session(); +} + +//----------------------------------------------------------------------------- + +bool RalDatabase::isConnected() const +{ + return m_sessionMgr->isConnected(); +} + +//----------------------------------------------------------------------------- + +void RalDatabase::connect() +{ + return m_sessionMgr->connect(); +} + +//----------------------------------------------------------------------------- + +void RalDatabase::disconnect() +{ + return m_sessionMgr->disconnect(); +} + +//----------------------------------------------------------------------------- + +#ifdef COOL400 +ITransactionPtr RalDatabase::startTransaction() +{ + transactionMgr()->setAutoTransactions( false ); + return ITransactionPtr( new ManualTransaction( transactionMgr() ) ); +} +#endif + +//----------------------------------------------------------------------------- diff --git a/28PATCHES2013/RelationalCool/src/NEW/RalDatabase.h b/28PATCHES2013/RelationalCool/src/NEW/RalDatabase.h new file mode 100644 index 0000000000000000000000000000000000000000..ba16ce761692c9a0fd77863b7963d8e98e5d1845 --- /dev/null +++ b/28PATCHES2013/RelationalCool/src/NEW/RalDatabase.h @@ -0,0 +1,290 @@ +#ifndef RELATIONALCOOL_RALDATABASE_H +#define RELATIONALCOOL_RALDATABASE_H 1 + +// Include files +#include "RelationalAccess/ISessionProxy.h" + +// Local include files +#include "CoralConnectionServiceProxy.h" +#include "RalSessionMgr.h" +#include "RelationalDatabase.h" + +// Disable icc warning 444: boost::enable_shared_from_this has non virtual dtor +#ifdef __ICC +#pragma warning (push) +#pragma warning (disable: 444) +#endif + +namespace cool +{ + + // Forward declarations + class RalDatabase; + class RalObjectMgr; + class RelationalObjectTableRow; + class RelationalSequence; + class RelationalTableRow; + class SimpleObject; + class TransRalDatabase; + + // Type definitions + typedef boost::shared_ptr<RalDatabase> RalDatabasePtr; + + /** @class RalDatabase RalDatabase.h + * + * RAL implementation of one COOL "condition database" instance + * (deployed on a specific physical infrastructure). + * + * @author Andrea Valassi, Sven A. Schmidt and Marco Clemencic + * @date 2004-11-09 + */ + + class RalDatabase : public RelationalDatabase + , public boost::enable_shared_from_this<RalDatabase> + { + + friend class MemoryConsumptionTest; + friend class RalDatabaseTest; + friend class RalDatabaseTest_extendedSpec; + friend class RalDatabaseTest_versioning; + friend class RalSequenceTest; + friend class RelationalObjectMgrTest; + friend class RelationalObjectTableTest; + + // Only the RalDatabaseSvc can instantiate or delete a RalDatabase + friend class RalDatabaseSvc; + // also the wrapper needs to be able to create databases + friend class TransRalDatabase; + + // Also the RalSchemaEvolution manager can access all internal methods + friend class RalSchemaEvolution; + + // Only the boost shared pointer can delete a RalDatabase: see + // http://www.boost.org/libs/smart_ptr/sp_techniques.html#preventing_delete + class deleter; + friend class deleter; + + public: + + /// Get a CORAL MessageStream + coral::MessageStream& log() const + { + return RelationalDatabase::log(); + } + + /// Returns the RAL session manager connected to the database. + boost::shared_ptr<RalSessionMgr> sessionMgr() const; + + /// Get the RelationalQueryMgr + const RelationalQueryMgr& queryMgr() const + { + return sessionMgr()->queryMgr(); + } + + /// Required access mode to the database. + bool isReadOnly() const + { + return sessionMgr()->isReadOnly(); + } + + /// Returns the database session. + /// Delegated to RalSessionMgr. + coral::ISessionProxy& session() const; + + /// Create a new folder set + /// Starts a transaction + IFolderSetPtr createFolderSet + ( const std::string& fullPath, + const std::string& description = "", + bool createParents = false ); + + /// Retrieve an existing folderset and return the corresponding manager + /// Throw an exception if the folderset does not exist + IFolderSetPtr getFolderSet( const std::string& fullPath ); + + /// Return the folderset manager for a given row + IFolderSetPtr getFolderSet( const RelationalTableRow& row ); + + /// Create a new folder and return the corresponding manager. + /// The ownership of the folder manager instance is shared. + /// Throws DatabaseNotOpen if there is no connection to the database. + /// Throws HvsPathHandlerException if the given path has an invalid format. + /// Throws NodeExists if a folder[set] with the same path already exists. + /// Throws an Exception if the max# of folder[set]s (9999) is exceeded. + /// Throws an Exception if an invalid versioning mode has been specified. + IFolderPtr createFolder + ( const std::string& fullPath, + const IFolderSpecification& folderSpec, + const std::string& description = "", + bool createParents = false ); + + /// DEPRECATED: this is likely to be removed in the next major release + /// (similar to the COOL133 API, with IRecordSpecification instead of + /// ExtendedAttributeListSpecification: use IFolderSpecification instead). + IFolderPtr createFolder + ( const std::string& fullPath, + const IRecordSpecification& payloadSpec, + const std::string& description = "", + FolderVersioning::Mode mode = FolderVersioning::SINGLE_VERSION, + bool createParents = false ); + + /// Create a new folder and return the corresponding manager + /// The ownership of the folder manager instance is shared + IFolderPtr createFolder + ( const std::string& fullPath, + const IRecordSpecification& payloadSpec, + const std::string& description, + FolderVersioning::Mode mode, + bool createParents, + PayloadMode::Mode payloadMode ); + + /// Retrieve an existing folder and return the corresponding manager + /// Throw an exception if the folder does not exist + IFolderPtr getFolder( const std::string& fullPath ); + + /// Return the folder manager for a given row + IFolderPtr getFolder( const RelationalTableRow& row ); + + /// Retrieve all existing folders and folder sets + /// (preload the cache - used for ReadOnly connections only) + void preloadAllNodes(); + + /// Drop an existing node. + bool dropNode( const std::string& fullPath ); + + /// Get a RelationalObjectTable for the given folder. + /// The concrete class can only be created by the concrete database. + /// The RelationalFolder parameter is only used to obtain + /// the associated table names and is *not* retained. + boost::shared_ptr<RelationalObjectTable> + relationalObjectTable( const RelationalFolder& folder ) const; + + /// Return the RelationalDatabasePtr + RelationalDatabasePtr relationalDbPtr() + { + return shared_from_this(); + } + + /// Return the connection state of the database. + /// This is different from isOpen(): isConnected() refers only to the + /// connection to the server, a prerequisite to the actual opening of + /// a COOL "database" (which implies reading the management tables). + /// Delegated to RalSessionMgr. + bool isConnected() const; + + /// (Re)connect to the database. + /// Delegated to RalSessionMgr. + void connect(); + + /// Close the database connection. + /// Delegated to RalSessionMgr. + void disconnect(); + +#ifdef COOL400 + /// Start a new transaction and enter manual transaction mode + virtual ITransactionPtr startTransaction(); +#endif + + /// Refresh the database + /// Keep and refresh nodes if the flag is true, else drop them + void refreshDatabase( bool keepNodes = false ); + + /// Refresh all nodes. + void refreshAllNodes(); + + /// Refresh an existing node. + void refreshNode( const std::string& fullPath ); + + /// Drops all nodes (keep the root node if required) + bool dropAllNodes( bool keepRoot=false ); + + protected: + + /// The following methods are all protected: only a RalDatabaseSvc can + /// instantiate or delete this class and create, drop or open a database + + /// Constructor + /// Throws a RelationalException if the RelationalService handle is NULL. + /// Throws a RelationalException if a connection cannot be established. + RalDatabase( CoralConnectionServiceProxyPtr ppConnSvc, + const DatabaseId& dbId, + bool readOnly ); + + /// Destructor + virtual ~RalDatabase(); + + /// Create a new database with default attributes + /// Default attributes are those specific for a RelationalDatabase + /// Expose in the public API a protected RelationalDatabase method. + void createDatabase() { + return RelationalDatabase::createDatabase(); + } + + /// Create a new database with non-default attributes. + /// Throw a RelationalException if the given attributes are invalid. + void createDatabase( const IRecord& dbAttr ); + + /// Drop the database + bool dropDatabase(); + + private: + + /// Standard constructor is private + RalDatabase(); + + /// Copy constructor is private + RalDatabase( const RalDatabase& rhs ); + + /// Assignment operator is private + RalDatabase& operator=( const RalDatabase& rhs ); + + /// Update the description for the given node + void updateNodeTableDescription( const std::string& fullPath, + const std::string& description ) const; + + /// Creates a new entry in the folder table + /// Returns the node id of the new entry + UInt32 insertNodeTableRow + ( const std::string& fullPath, + const std::string& description, + bool createParents, + bool isLeaf, + const std::string& payloadSpecDesc, + FolderVersioning::Mode versioningMode, + PayloadMode::Mode payloadMode = PayloadMode::INLINEPAYLOAD ); + + /// Set the useTimeout flag + void setUseTimeout( bool flag ) { m_useTimeout = flag; } + + /// Private deleter class + class deleter + { + public: + void operator()( RalDatabase* pDb ) { + delete pDb; + } + }; + + private: + + /// Map of all nodes (cache - only used for ReadOnly connections) + /// NB Do not use IFolderPtr/IFolderSetPtr: RalDatabase is never deleted! + std::map< std::string, RelationalTableRow* > m_nodes; + + /// Switch on the schema change timeout (ORA-01466 problem) + bool m_useTimeout; + + /// RAL session manager connected to the database + /// (created by this instance, but ownership shared, e.g. with query mgr) + boost::shared_ptr<RalSessionMgr> m_sessionMgr; + + }; + +} + +// Reenable icc warning 444 +#ifdef __ICC +#pragma warning (pop) +#endif + +#endif // RELATIONALCOOL_RALDATABASE_H diff --git a/28PATCHES2013/RelationalCool/src/NEW/RalQueryMgr.h b/28PATCHES2013/RelationalCool/src/NEW/RalQueryMgr.h new file mode 100644 index 0000000000000000000000000000000000000000..90b067bf6304206bc3e1d10b04f6176214269c96 --- /dev/null +++ b/28PATCHES2013/RelationalCool/src/NEW/RalQueryMgr.h @@ -0,0 +1,175 @@ +#ifndef RELATIONALCOOL_RALQUERYMGR_H +#define RELATIONALCOOL_RALQUERYMGR_H + +// Include files +#include "RelationalAccess/IQuery.h" + +// Local include files +#include "RelationalQueryMgr.h" + +namespace cool +{ + + // Forward declarations + class IRelationalQueryDefinition; + class RalSessionMgr; + + /** @class RalQueryMgr RalQueryMgr.h + * + * Manager of relational queries executed using RAL. + * Manager of relational DML operations executed using RAL. + * + * Transactions are NOT handled by this class. + * + * @author Andrea Valassi and Sven A. Schmidt + * @date 2005-10-11 + */ + + class RalQueryMgr : public RelationalQueryMgr + { + + public: + + /// Constructor from a RalSessionMgr shared pointer. + RalQueryMgr( const boost::shared_ptr<RalSessionMgr>& sessionMgr ); + + /// Destructor. + virtual ~RalQueryMgr(); + + /// Clone this query manager. + RelationalQueryMgr* clone() const; + + /// Checks if a table exists in the schema. + /// Implements RelationalQueryMgr pure abstract virtual method. + bool existsTable( const std::string& tableName ) const; + + /// Create a new empty CORAL query. + /// This is needed if subqueries must be defined for this query. + std::auto_ptr<coral::IQuery> newQuery() const; + + /// Prepare a CORAL query definition from one or more tables. + void prepareQueryDefinition + ( coral::IQueryDefinition& coralDef, + const IRelationalQueryDefinition& coolDef, + bool countStarSubquery = false ) const; + + /// Prepare a CORAL query from one or more tables. + /// A pointer to an AttributeList data must be passed to be overwritten + /// (a new AL with the right spec is created and used as data buffer). + std::auto_ptr<coral::IQuery> prepareQuery + ( const IRelationalQueryDefinition& coolDef, + boost::shared_ptr<coral::AttributeList>& dataBuffer ) const; + + /// Prepare and execute a CORAL query + /// (and start/stop the relevant timing report). + /// The caller becomes the owenr of the returned cursor. + IRelationalCursor* prepareAndExecuteQuery + ( const IRelationalQueryDefinition& coolDef, + boost::shared_ptr<coral::AttributeList>& dataBuffer ) const; + + /// Fetch an ordered set of rows from one or more tables as a vector. + /// If nExp>0, throws TooManyRowsFound if more than nExp rows are found. + /// If nExp>0, throws an exception if fewer than nExp rows are found. + /// Implements RelationalQueryMgr pure abstract virtual method. + const std::vector<RelationalTableRow> fetchOrderedRows + ( const IRelationalQueryDefinition& coolDef, + const std::string& description = "", + UInt32 nExp = 0, + bool forUpdate = false ) const; + + /// Fetch a "select COUNT(*)" row count from one or more tables. + /// Implements RelationalQueryMgr pure abstract virtual method. + UInt32 countRows + ( const IRelationalQueryDefinition& coolDef, + const std::string& description = "" ) const; + + /// Returns a new IRelationalBulkOperation object for performing a bulk + /// insert operation specifying the input data buffer and the number of + /// rows that should be cached on the client. + boost::shared_ptr<IRelationalBulkOperation> + bulkInsertTableRows( const std::string& tableName, + const Record& dataBuffer, + int rowCacheSize ) const; + + /// Insert one row into one table. + void insertTableRow( const std::string& tableName, + const Record& data ) const; + + /// Returns a new IRelationalBulkOperation object for performing a bulk + /// update operation specifying the input data buffer and the number of + /// rows that should be cached on the client. + boost::shared_ptr<IRelationalBulkOperation> + bulkUpdateTableRows( const std::string& tableName, + const std::string& setClause, + const std::string& whereClause, + const Record& dataBuffer, + int rowCacheSize ) const; + + /// Update rows in one table. + /// If nExp>0, throws an exception if more than nExp rows are updated. + /// If nExp>0, throws an exception if fewer than nExp rows are updated. + /// Implements RelationalQueryMgr pure abstract virtual method. + UInt32 updateTableRows + ( const std::string& tableName, + const std::string& setClause, + const std::string& whereClause, + const Record& updateData, + UInt32 nExp = 0 ) const; + + /// Delete rows from one table. + /// Throws an exception if #rows deleted is different from the expectation + /// (taken from nExp if > 0; queried internally if nExp = 0). + /// Implements RelationalQueryMgr pure abstract virtual method. + UInt32 deleteTableRows + ( const std::string& tableName, + const std::string& whereClause, + const Record& whereData, + UInt32 nExp = 0 ) const; + + /// Delete all rows from one table (truncate the table). + /// Implements RelationalQueryMgr pure abstract virtual method. + void deleteAllTableRows + ( const std::string& tableName ) const; + + /// Get a RelationalSequenceMgr. + /// Implements RelationalQueryMgr pure abstract virtual method. + RelationalSequenceMgr& sequenceMgr() const + { + return *m_sequenceMgr; + } + + /// Build the appropriate backend-specific SQL expression + /// to compute the server-side time in the format used by COOL. + /// Implements RelationalQueryMgr pure abstract virtual method. + const std::string serverTimeClause() const; + + /// Execute a CORAL query (and start/stop the relevant timing report) + static coral::ICursor& executeQuery( coral::IQuery& query ); + + /// Increment a CORAL cursor (and start/stop the relevant timing report) + static bool cursorNext( coral::ICursor& cursor ); + + /// Return the server technology for the current connection. + /// Supported technologies: "Oracle ", "MySQL", "SQLite", "frontier". + const std::string databaseTechnology() const; + + /// Return the server technology version for the current connection. + /// This ultimately corresponds to coral::IConnection::serverVersion(). + const std::string serverVersion() const; + + /// Return the schema name for the current connection. + const std::string schemaName() const; + + private: + + /// Handle to the RalSessionMgr (shared ownership) + boost::shared_ptr<RalSessionMgr> m_sessionMgr; + + /// RelationalSequenceMgr (owned by this instance) + RelationalSequenceMgr* m_sequenceMgr; + + }; + +} + +#endif // RELATIONALCOOL_RALQUERYMGR_H diff --git a/28PATCHES2013/RelationalCool/src/NEW/RalSessionMgr.cpp b/28PATCHES2013/RelationalCool/src/NEW/RalSessionMgr.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8616f1460f0f790955c9ec9b94aa726687e2b542 --- /dev/null +++ b/28PATCHES2013/RelationalCool/src/NEW/RalSessionMgr.cpp @@ -0,0 +1,414 @@ + +// Include files +#include <cstdlib> +#include <iostream> +#include "RelationalAccess/IConnectionServiceConfiguration.h" +#include "RelationalAccess/IRelationalDomain.h" +#include "RelationalAccess/IRelationalService.h" +#include "RelationalAccess/ITransaction.h" + +// Local include files +#include "RalQueryMgr.h" +#include "RalSessionMgr.h" +#include "RelationalException.h" +#include "TimingReportMgr.h" + +// Namespace +using namespace cool; + +//----------------------------------------------------------------------------- + +/// \todo FIX-ME: I do not like this, but it looks like it is the only way +/// of using SQLite if you do not have the appropriate lines in the +/// authentication.xml file. +std::string cool::RalConnectionString( const RelationalDatabaseId& dbId ) +{ + std::string connectString = dbId.middleTier(); + if ( dbId.alias().empty() ) + { + if ( dbId.technology() == "sqlite" ) + connectString += + "sqlite_file:" + dbId.schema(); + else + connectString += + dbId.technology() + "://" + dbId.server() + "/" + dbId.schema(); + } + else + connectString += dbId.alias(); + return connectString; +} + +//----------------------------------------------------------------------------- + +RalSessionMgr::RalSessionMgr( CoralConnectionServiceProxyPtr ppConnSvc, + const DatabaseId& dbId, + bool readOnly ) + : m_ppConnSvc( ppConnSvc ) + , m_relationalDbId( dbId ) + , m_log( new coral::MessageStream( "RalSessionMgr" ) ) + , m_session( 0 ) + , m_queryMgr( *this ), +{ + std::string ro; + if ( !readOnly ) + { + m_sessionMode = Update; + ro = "R/W"; + } + else if ( getenv( "COOL_READONLYSESSION_MANYTRANSACTIONS" ) ) + { + m_sessionMode = ReadOnlyManyTx; + ro = "R/O (many tx)"; + } + else + { + m_sessionMode = ReadOnly; + ro = "R/O"; + } + log() << coral::Info << "Instantiate a " << ro << " RalSessionMgr for '" + << m_relationalDbId.urlHidePswd() << "'" + << coral::MessageStream::endmsg; + // Create the appropriate RAL session and connect to the database server + connect(); +} + +//----------------------------------------------------------------------------- + +RalSessionMgr::~RalSessionMgr() +{ + log() << coral::Info << "Delete the RalSessionMgr for '" + << m_relationalDbId.urlHidePswd() << "'" + << coral::MessageStream::endmsg; + // Disconnect from the database server and delete the RAL session + disconnect(); +} + +//----------------------------------------------------------------------------- + +const std::string RalSessionMgr::databaseTechnology() const +{ +#ifdef NOCORAL210 + return m_session->properties().flavorName(); +#else + return m_session->remoteProperties().flavorName(); +#endif +} + +//----------------------------------------------------------------------------- + +const std::string RalSessionMgr::serverVersion() const +{ +#ifdef NOCORAL210 + return m_session->properties().serverVersion(); +#else + return m_session->remoteProperties().serverVersion(); +#endif +} + +//----------------------------------------------------------------------------- + +bool RalSessionMgr::isConnected() const +{ + return ( m_session != 0 ); +} + +//----------------------------------------------------------------------------- + +coral::IConnectionService& RalSessionMgr::connectionSvc() const +{ + // Disable CORAL connection pool automatic cleanup if requested + // Disable CORAL connection sharing if requested + static bool first = true; + if ( first ) + { + first = false; + if ( getenv( "COOL_DISABLE_CORALCONNECTIONPOOLCLEANUP" ) ) + { + log() << coral::Warning << "Use COOL_DISABLE_CORALCONNECTIONPOOLCLEANUP" + << coral::MessageStream::endmsg; // Use log, not cout (bug #75501) + m_ppConnSvc->configuration().disablePoolAutomaticCleanUp(); + m_ppConnSvc->configuration().setConnectionTimeOut( 0 ); + } + if ( getenv( "COOL_DISABLE_CORALCONNECTIONSHARING" ) ) + { + log() << coral::Warning << "Use COOL_DISABLE_CORALCONNECTIONSHARING" + << coral::MessageStream::endmsg; // Use log, not cout (bug #75501) + m_ppConnSvc->configuration().disableConnectionSharing(); + } + } + // Return the CORAL connection service PROXY! + return *m_ppConnSvc; +} + +//----------------------------------------------------------------------------- + +inline static int SetEnv( const std::string& name, const std::string& value ) +{ +#ifndef WIN32 + // UNIX version + return value.empty() ? + ::unsetenv(name.c_str()) , 0 : + ::setenv(name.c_str(),value.c_str(), 1); +#else + // Windows version + return ::_putenv((name+"="+value).c_str()); +#endif +} + +//----------------------------------------------------------------------------- + +void RalSessionMgr::connect() +{ + if ( ! isConnected() ) + { + if ( TimingReport::enabled() ) + { + TimingReportMgr::initialize(); + TimingReportMgr::startTimer( "EXTRA cool::RalSessionMgr::connect()" ); + } + + log() << coral::Info + << "Connect to the database server" << coral::MessageStream::endmsg; + + bool environmentAuthenticationUsed = false; + std::auto_ptr<std::string> old_env_content[2]; + + std::string ralConnectString = RalConnectionString( m_relationalDbId ); + // To pass user and password to CORAL ConnectionService we can only + // use the environment + if ( m_relationalDbId.technology() != "sqlite" && + m_relationalDbId.password() != "" && m_relationalDbId.user() != "" ) + { + log() << "explicit credentials in the connection string: I use them" + << coral::MessageStream::endmsg; + log() << coral::Warning + << "You are using explicit credentials in the connection string" + << ": this is deprecated" + << ", please use either XML or LFC based authentication" + << coral::MessageStream::endmsg; + + // If the user specified _BOTH_ user name and password, we can use the + // environment variables to pass those values to CORAL + environmentAuthenticationUsed = true; // need to revert to old values + + // Keep a copy of the old env values + char *tmp = ::getenv("CORAL_AUTH_USER"); + if (tmp) + old_env_content[0] = + std::auto_ptr<std::string>( new std::string(tmp) ); + tmp = ::getenv("CORAL_AUTH_PASSWORD"); + if (tmp) + old_env_content[1] = + std::auto_ptr<std::string>( new std::string(tmp) ); + + // Put new values in the environment variables + if ( SetEnv("CORAL_AUTH_USER", m_relationalDbId.user()) < 0 || + SetEnv("CORAL_AUTH_PASSWORD",m_relationalDbId.password()) < 0 ) + { + // something went wrong with the environment :-( + // revert to old values and forget + log() << coral::Warning << + "Problems when trying to set authentication env. variables, ignoring" + " specified credentials." << coral::MessageStream::endmsg; + if ( old_env_content[0].get() ) + { + SetEnv("CORAL_AUTH_USER", *(old_env_content[0])); + } + else + { + SetEnv("CORAL_AUTH_USER", ""); + } + if ( old_env_content[1].get() ) + { + SetEnv("CORAL_AUTH_PASSWORD", *(old_env_content[1])); + } + else + { + SetEnv("CORAL_AUTH_PASSWORD", ""); + } + environmentAuthenticationUsed = false; + } + } + + // Set the CORAL/Services/EnvironmentAuthenticationService + if ( environmentAuthenticationUsed ) + { + connectionSvc().configuration(). + setAuthenticationService + ( "CORAL/Services/EnvironmentAuthenticationService" ); + } + + // Get the session proxy + coral::AccessMode accessMode; + if ( isReadOnly() ) accessMode = coral::ReadOnly; + else accessMode = coral::Update; + if ( m_relationalDbId.role().empty() ) + { + m_session = connectionSvc().connect( ralConnectString, + accessMode ); + } + else + { + m_session = connectionSvc().connect( ralConnectString, + m_relationalDbId.role(), + accessMode ); + } + + // Clean up environment variables + if ( environmentAuthenticationUsed ) + { + // revert to old values + if ( old_env_content[0].get() ) + { + SetEnv( "CORAL_AUTH_USER", *( old_env_content[0] ) ); + } + else + { + SetEnv( "CORAL_AUTH_USER", "" ); + } + if ( old_env_content[1].get() ) + { + SetEnv( "CORAL_AUTH_PASSWORD", *(old_env_content[1]) ); + } + else + { + SetEnv( "CORAL_AUTH_PASSWORD", "" ); + } + } + + // Was a connection established successfully? + if ( !m_session ) + { + throw RelationalException( "Failed to connect to the server", + "RalSessionMgr::connect" ); + } + log() << "Connection established successfully" + << coral::MessageStream::endmsg; + + // In ReadOnly mode start a single transaction for the duration of + // the session (all other clients use a dummy transaction manager). + //if ( m_sessionMode==ReadOnly || m_sessionMode==ReadOnlyManyTx ) + if ( m_sessionMode == ReadOnly ) // NB: m_sessionMode != ReadOnlyManyTx + { + log() << coral::Info + << "Start a read-only transaction active" + << " for the duration of the database connection" + << coral::MessageStream::endmsg; + session().transaction().start( true ); // R/O transaction + } + + if ( TimingReportMgr::isActive() ) + { + TimingReportMgr::stopTimer( "EXTRA cool::RalSessionMgr::connect()" ); + TimingReportMgr::finalize(); + } + + } +} + +//----------------------------------------------------------------------------- + +void RalSessionMgr::disconnect() +{ + if ( TimingReport::enabled() ) + { + TimingReportMgr::initialize(); + TimingReportMgr::startTimer( "EXTRA cool::RalSessionMgr::disconnect()" ); + } + + if ( isConnected() ) + { + + // In Update mode there should be no active transactions unless the + // session manager is being killed as a result of an exception thrown. + // Check if there is an open transaction, and in that case rollback + // (do not rollback all the time, else CORAL will issue a warning!). + if ( m_sessionMode == Update ) + { + if ( session().transaction().isActive() ) + { + log() << coral::Warning + << "Active transactions found while disconnecting" + << " from an Update session will be rolled back" + << coral::MessageStream::endmsg; + session().transaction().rollback(); + } + } + // In ReadOnlyManyTx mode there should be no active transactions unless + // the session manager is being killed as a result of an exception thrown. + // Check if there is an open transaction, and in that case rollback + // (do not rollback all the time, else CORAL will issue a warning!). + else if ( m_sessionMode == ReadOnlyManyTx ) // fix bug #90949 + { + if ( session().transaction().isActive() ) + { + log() << coral::Warning + << "Active transactions found while disconnecting from a" + << " ReadOnly (many transactions) session will be rolled back" + << coral::MessageStream::endmsg; + session().transaction().rollback(); + } + } + // In ReadOnly mode there should be one active transaction. + else + { + if ( session().transaction().isActive() ) + { + log() << coral::Info + << "Commit the read-only transaction active" + << " for the duration of the database connection" + << coral::MessageStream::endmsg; + session().transaction().commit(); + } + else + { + log() << coral::Warning + << "PANIC! No active transactions found while disconnecting" + << " from a ReadOnly (single transaction) session" + << coral::MessageStream::endmsg; + } + } + + // Disconnect from the database server + log() << coral::Info + << "Disconnect from the database server" + << coral::MessageStream::endmsg; + delete m_session; + m_session = 0; + + } + + // TEMPORARY? Should/will be done by CORAL inside ~ISessionProxy? + // Purge the CORAL connection pool (see task #3546) to ensure + // that the connection is physically dropped if the timeout is 0 + try { m_ppConnSvc->purgeConnectionPool(); } catch( ... ) { } + + if ( TimingReportMgr::isActive() ) + { + TimingReportMgr::stopTimer( "EXTRA cool::RalSessionMgr::disconnect()" ); + TimingReportMgr::finalize(); + } + +} + +//----------------------------------------------------------------------------- + +coral::MessageStream& RalSessionMgr::log() const +{ + *m_log << coral::Verbose; + return *m_log; +} + +//----------------------------------------------------------------------------- + +coral::ISessionProxy& RalSessionMgr::session() const +{ + if ( isConnected() ) + return *m_session; + else + throw RelationalException + ( "Not connected to the database server", "RalSessionMgr" ); +} + +//----------------------------------------------------------------------------- + diff --git a/28PATCHES2013/RelationalCool/src/NEW/RalSessionMgr.h b/28PATCHES2013/RelationalCool/src/NEW/RalSessionMgr.h new file mode 100644 index 0000000000000000000000000000000000000000..e5c484367a0c56fb28ec69f2f62c8a9f12c0cf9f --- /dev/null +++ b/28PATCHES2013/RelationalCool/src/NEW/RalSessionMgr.h @@ -0,0 +1,161 @@ +#ifndef RELATIONALCOOL_RALSESSIONMGR_H +#define RELATIONALCOOL_RALSESSIONMGR_H + +// Include files +#include <memory> +#include <boost/shared_ptr.hpp> +#include "CoolKernel/DatabaseId.h" +#include "CoralBase/MessageStream.h" +#include "RelationalAccess/AccessMode.h" +#include "RelationalAccess/IConnectionService.h" +#include "RelationalAccess/ISchema.h" +#include "RelationalAccess/ISessionProxy.h" +#include "RelationalAccess/ISessionProperties.h" + +// Local include files +#include "CoralConnectionServiceProxy.h" +#include "RalQueryMgr.h" +#include "RelationalDatabaseId.h" + +// Disable icc warning 444: boost::enable_shared_from_this has non virtual dtor +#ifdef __ICC +#pragma warning (push) +#pragma warning (disable: 444) +#endif + +namespace cool +{ + + std::string RalConnectionString( const RelationalDatabaseId& dbId ); + + /** @class RalSessionMgr RalSessionMgr.h + * + * Manager of relational database connections via a RAL session. + * + * This class knows nothing about COOL tables. + * It is only concerned with relational database connections. + * + * @author Andrea Valassi, Sven A. Schmidt and Marco Clemencic + * @date 2005-10-24 + */ + + class RalSessionMgr + : public boost::enable_shared_from_this<RalSessionMgr> + { + + public: + + /// The constructor automatically connects to the database. + RalSessionMgr( CoralConnectionServiceProxyPtr ppConnSvc, + const DatabaseId& dbId, + bool readOnly ); + + /// The destructor automatically disconnects from the database. + virtual ~RalSessionMgr(); + + /// Return the server technology for the current connection. + /// Supported technologies: "Oracle", "MySQL", "SQLite", "frontier". + /// This ultimately corresponds to coral::IDomain::flavorName() + /// (grep m_flavorName in the four XxxAccess/src/Domain.cpp), + /// because it is equal to ConnectionHandle::technologyName(), + /// which is equal to ConnectionParams::technologyName(), which is + /// set to IDomain::flavorName() in ReplicaCatalogue::getReplicas. + /// *** WARNING!!! THIS MAY CHANGE IN LATER VERSIONS OF THE CODE!!! *** + /// New (not for production!): for URLs using a middle tier, this method + /// returns the properties of the remote database, not of the middle tier + const std::string databaseTechnology() const; + + /// Return the server technology version for the current connection. + /// This ultimately corresponds to coral::IConnection::serverVersion() + /// (grep serverVersion in the four XxxAccess/src/Connection.cpp), + /// because it is equal to ConnectionHandle::serverVersion(), + /// which is equal to IConnection::serverVersion(). + /// *** WARNING!!! THIS MAY CHANGE IN LATER VERSIONS OF THE CODE!!! *** + /// New (not for production!): for URLs using a middle tier, this method + /// returns the properties of the remote database, not of the middle tier + const std::string serverVersion() const; + + /// Return the schema name for the current connection. + inline std::string schemaName() const + { + return m_session->nominalSchema().schemaName(); + } + + /// Return the connection state of the database. + /// [Note that this is subtly different from RelationalDatabase::isOpen!] + bool isConnected() const; + + /// (Re)connect to the database. + void connect(); + + /// Close the database connection. + void disconnect(); + + /// Get a reference to the RAL database session. + /// Throw an exception if there is no connection to the database. + coral::ISessionProxy& session() const; + + /// Get a CORAL MessageStream + coral::MessageStream& log() const; + + /// Get the RelationalQueryMgr + const RelationalQueryMgr& queryMgr() const + { + return m_queryMgr; + } + + /// Required access mode to the database. + bool isReadOnly() const + { + return ( m_sessionMode==ReadOnly || m_sessionMode==ReadOnlyManyTx ); + } + + /// Required access mode to the database. + bool isReadOnlyManyTx() const + { + return ( m_sessionMode==ReadOnlyManyTx ); + } + + private: + + /// Standard constructor is private + RalSessionMgr(); + + /// Copy constructor is private + RalSessionMgr( const RalSessionMgr& rhs ); + + /// Assignment operator is private + RalSessionMgr& operator=( const RalSessionMgr& rhs ); + + private: + + /// Get a reference to the CORAL connection service. + coral::IConnectionService& connectionSvc() const; + + /// Shared pointer to the CORAL connection service pointer. + /// When the database service is deleted, this points to a null pointer. + CoralConnectionServiceProxyPtr m_ppConnSvc; + + /// Global identifier of the database + RelationalDatabaseId m_relationalDbId; + + /// SEAL MessageStream + std::auto_ptr<coral::MessageStream> m_log; + + /// RAL session (owned by this instance) connected to the database + coral::ISessionProxy* m_session; + + /// RelationalQueryMgr (owned by this instance) + RalQueryMgr m_queryMgr; + + /// Session access mode (see bug #90949) + typedef enum { ReadOnly, Update, ReadOnlyManyTx } SessionMode; + + /// Required access mode to the database + SessionMode m_sessionMode; + + }; + +} + +#endif // RELATIONALCOOL_RALSESSIONMGR_H diff --git a/28PATCHES2013/RelationalCool/src/NEW/RelationalDatabase.cpp b/28PATCHES2013/RelationalCool/src/NEW/RelationalDatabase.cpp new file mode 100644 index 0000000000000000000000000000000000000000..37510cc5629914a3d55278ce06cb55ad1f665eb8 --- /dev/null +++ b/28PATCHES2013/RelationalCool/src/NEW/RelationalDatabase.cpp @@ -0,0 +1,1287 @@ + +// Include files +#include <map> +#include <vector> +#include "CoolKernel/Record.h" +#include "CoolKernel/RecordException.h" +#include "CoolKernel/RecordSpecification.h" +#include "CoralBase/Attribute.h" +#include "CoralBase/AttributeList.h" + +// Local include files +#include "IRelationalTransactionMgr.h" +#include "RelationalChannelTable.h" +#include "RelationalDatabase.h" +#include "RelationalDatabaseTable.h" +#include "RelationalException.h" +#include "RelationalFolderSet.h" +#include "RelationalGlobalTagTable.h" +#include "RelationalIovSharedSequenceTable.h" +#include "RelationalNodeMgr.h" +#include "RelationalNodeTable.h" +#include "RelationalObjectTable.h" +#include "RelationalObject2TagTable.h" +#include "RelationalObjectMgr.h" +#include "RelationalQueryDefinition.h" +#include "RelationalSchemaMgr.h" +#include "RelationalTableRow.h" +#include "RelationalTag2TagTable.h" +#include "RelationalTagSequence.h" +#include "RelationalTagSharedSequenceTable.h" +#include "RelationalTagTable.h" +#include "RelationalTagMgr.h" +#include "VersionInfo.h" + +// *** START *** 3.0.0 schema extensions (task #4307, task #4396) +#include "RelationalChannelTablesTable.h" +#include "RelationalGlobalHeadTagTable.h" +#include "RelationalGlobalUserTagTable.h" +#include "RelationalIovTablesTable.h" +// **** END **** 3.0.0 schema extensions (task #4307, task #4396) + +// Namespace +using namespace cool; + +//----------------------------------------------------------------------------- + +RelationalDatabase::RelationalDatabase( const DatabaseId& dbId ) + : m_dbAttr() + , m_isOpen( false ) + , m_relationalDbId( dbId ) + , m_log( new coral::MessageStream( "RelationalDatabase" ) ) + , m_schemaMgr( 0 ) + , m_nodeMgr( 0 ) + , m_tagMgr( 0 ) +{ + log() << coral::Debug << "Instantiate a RelationalDatabase for '" + << m_relationalDbId.middleTier() + << databaseId() << "'" << coral::MessageStream::endmsg; + + // Parse the dbId URL as RelationalDatabaseId connection parameters + std::string technology = m_relationalDbId.technology(); + std::string server = m_relationalDbId.server(); + std::string user = m_relationalDbId.user(); + std::string password = m_relationalDbId.password(); + std::string schema = m_relationalDbId.schema(); + std::string dbName = m_relationalDbId.dbName(); + log() << "Technology: '" << technology << "'" << coral::MessageStream::endmsg; + log() << "Server: '" << server << "'" << coral::MessageStream::endmsg; + log() << "User: '" << user << "'" << coral::MessageStream::endmsg; + if ( getenv( "COOL_AUTH_SHOWPASSWORD" ) ) + log() << "Password: '" << password << "'" << coral::MessageStream::endmsg; + else + log() << "Password: '" << "********" << "'" << coral::MessageStream::endmsg; + log() << "Schema: '" << schema << "'" << coral::MessageStream::endmsg; + log() << "Conditions database name: '" << dbName << "'" << coral::MessageStream::endmsg; +} + +//----------------------------------------------------------------------------- + +RelationalDatabase::~RelationalDatabase() +{ + //*m_pThis = NULL; + //m_pThis.reset(); + log() << coral::Debug << "Delete the RelationalDatabase for '" + << m_relationalDbId.middleTier() + << databaseId() << "'" << coral::MessageStream::endmsg; +} + +//----------------------------------------------------------------------------- + +void RelationalDatabase::checkDbOpenTransaction( const std::string& domain, + cool::AccessMode mode ) const +{ + if ( !isOpen() ) + throw DatabaseNotOpen( domain ); + if ( mode == cool::ReadWrite && isReadOnly() ) + throw DatabaseOpenInReadOnlyMode( domain ); + if ( !transactionMgr()->isActive() ) + throw RelationalException( "Transaction is not active", domain ); +// if ( mode == cool::ReadWrite && transactionMgr()->isReadOnly() ) +// throw cool::Exception( "Transaction is read only", domain ); +} + +//----------------------------------------------------------------------------- + +const DatabaseId& RelationalDatabase::databaseId() const +{ + return m_relationalDbId.urlHidePswd(); +} + +//----------------------------------------------------------------------------- + +const std::string& RelationalDatabase::databaseName() const +{ + return m_relationalDbId.dbName(); +} + +//----------------------------------------------------------------------------- + +const IRecord& RelationalDatabase::databaseAttributes() const +{ + if ( ! isOpen() ) throw DatabaseNotOpen( "RelationalDatabase" ); + return m_dbAttr; +} + +//----------------------------------------------------------------------------- + +void RelationalDatabase::createDatabase() +{ + // Default attributes + std::string dbName = databaseName(); + Record dbAttr( databaseAttributesSpecification() ); + + std::string defaultTablePrefix = dbName + "_"; + dbAttr[RelationalDatabaseTable::attributeNames::defaultTablePrefix] + .setValue( defaultTablePrefix ); + + /* + // *** START *** 3.0.0 schema extensions (task #4307) + std::string channelTablesTableName = + RelationalChannelTablesTable::defaultTableName( defaultTablePrefix ); + dbAttr[RelationalDatabaseTable::attributeNames::channelTablesTableName] + .setValue( channelTablesTableName ); + + std::string iovTablesTableName = + RelationalIovTablesTable::defaultTableName( defaultTablePrefix ); + dbAttr[RelationalDatabaseTable::attributeNames::iovTablesTableName] + .setValue( iovTablesTableName ); + // **** END **** 3.0.0 schema extensions (task #4307) + */ + + std::string nodeTableName = + RelationalNodeTable::defaultTableName( defaultTablePrefix ); + dbAttr[RelationalDatabaseTable::attributeNames::nodeTableName] + .setValue( nodeTableName ); + + std::string tagTableName = + RelationalGlobalTagTable::defaultTableName( defaultTablePrefix ); + dbAttr[RelationalDatabaseTable::attributeNames::tagTableName] + .setValue( tagTableName ); + + /* + // *** START *** 3.0.0 schema extensions (task #4396) + std::string headTagTableName = + RelationalGlobalHeadTagTable::defaultTableName( defaultTablePrefix ); + dbAttr[RelationalDatabaseTable::attributeNames::headTagTableName] + .setValue( headTagTableName ); + + std::string userTagTableName = + RelationalGlobalUserTagTable::defaultTableName( defaultTablePrefix ); + dbAttr[RelationalDatabaseTable::attributeNames::userTagTableName] + .setValue( userTagTableName ); + // **** END **** 3.0.0 schema extensions (task #4396) + */ + + std::string tag2TagTableName = + RelationalTag2TagTable::defaultTableName( defaultTablePrefix ); + dbAttr[RelationalDatabaseTable::attributeNames::tag2TagTableName] + .setValue( tag2TagTableName ); + + std::string tagSharedSequenceName = + RelationalTagSharedSequenceTable::defaultTableName( defaultTablePrefix ); + dbAttr[RelationalDatabaseTable::attributeNames::tagSharedSequenceName] + .setValue( tagSharedSequenceName ); + + std::string iovSharedSequenceName = + RelationalIovSharedSequenceTable::defaultTableName( defaultTablePrefix ); + dbAttr[RelationalDatabaseTable::attributeNames::iovSharedSequenceName] + .setValue( iovSharedSequenceName ); + + // Create a database with the default attributes + return createDatabase( dbAttr ); +} + +//----------------------------------------------------------------------------- + +void RelationalDatabase::openDatabase() +{ + + std::string dbName = databaseName(); + log() << "Open the database with name " << dbName << coral::MessageStream::endmsg; + + // Connect to the backend server if not yet done + if ( ! isConnected() ) connect(); + + // Retrieve the database attributes in the top-level management table + log() << "Fetch database attributes" << coral::MessageStream::endmsg; + m_dbAttr = fetchDatabaseAttributes(); + log() << "Fetched database attributes: " << m_dbAttr << coral::MessageStream::endmsg; + + // Check that the release number and schema versions of the database are + // compatible with the release number and schema versions of this client + std::string releaseNumber = + m_dbAttr[RelationalDatabaseTable::attributeNames::release]. + data<std::string>(); + std::string schemaVersion = + m_dbAttr[RelationalDatabaseTable::attributeNames::schemaVersion]. + data<std::string>(); + if ( !areReleaseAndSchemaCompatible( releaseNumber, schemaVersion ) ) { + std::stringstream s; + s << "Release number mismatch - SCHEMA EVOLUTION REQUIRED: " + << "database with OLDER release number " << releaseNumber + << " cannot be opened using CURRENT client release number " + << VersionInfo::release; + throw IncompatibleReleaseNumber( s.str(), "RelationalDatabase" ); + } + + // The database is now open + m_isOpen = true; +} + +//----------------------------------------------------------------------------- + +bool +RelationalDatabase::areReleaseAndSchemaCompatible +( const std::string releaseNumber, + const std::string schemaVersion ) const +{ + bool status = true; + // Preliminary check: this release must be 1.2.0 or later + // MAKE SURE THAT 1.2.0 <= THISRELEASE + VersionNumber db_rel_version(releaseNumber); + VersionNumber db_schema_version(schemaVersion); + + if ( VersionInfo::release < "1.2.0" ) + { + std::stringstream s; + s << "PANIC! CURRENT client release number " << VersionInfo::release + << " is older than 1.2.0?"; + throw IncompatibleReleaseNumber( s.str(), "RelationalDatabase" ); + } + // Cannot open databases created with releases earlier than 1.2.0 + // No schema evolution is possible for such database schemas + // DbRelease < 1.2.0 + else if ( db_rel_version < "1.2.0" ) + { + std::stringstream s; + s << "Release number mismatch" + << " - SCHEMA EVOLUTION NOT POSSIBLE: " + << "database with OLDER release number " << db_rel_version + << " (older than 1.2.0)" + << " cannot be opened using CURRENT client release number " + << VersionInfo::release; + throw IncompatibleReleaseNumber( s.str(), "RelationalDatabase" ); + } + // Schema evolution for 1.2.0 <= DbRelease < THISRELEASE + // Open databases created with releases earlier than this release + else if ( db_rel_version < VersionInfo::release ) + { + if ( +#ifdef COOL290 + // This release (2.9.0) can read 2.9.0 + ( db_rel_version == "2.9.0" ) || +#endif + // This release (2.8.13) can read 2.8.x + // This release (2.8.13) can read 2.7.0 + // This release (2.8.13) can read 2.6.0 + // This release (2.8.13) can read 2.5.0 + // This release (2.8.13) can read 2.4.0 + // This release (2.8.13) can read 2.3.x (including the unreleased 2.3.1) + // This release (2.8.13) can read 2.2.x + // This release (2.8.13) can read 2.1.x + // This release (2.8.13) can read 2.0.0 + ( db_rel_version >= "2.8.0" && db_rel_version <= "2.8.13" ) || + ( db_rel_version == "2.7.0" ) || + ( db_rel_version == "2.6.0" ) || + ( db_rel_version == "2.5.0" ) || + ( db_rel_version == "2.4.0" ) || + ( db_rel_version >= "2.3.0" && db_rel_version <= "2.3.1" ) || + ( db_rel_version >= "2.2.0" && db_rel_version <= "2.2.2" ) || + ( db_rel_version >= "2.1.0" && db_rel_version <= "2.1.1" ) || + ( db_rel_version == "2.0.0" ) ) + { + status = true; + log() << coral::Info + << "Release number backward compatibility " + << "- NO SCHEMA EVOLUTION REQUIRED: " + << "database with OLDER release number " << releaseNumber + << " will be opened using CURRENT client release number " + << VersionInfo::release << coral::MessageStream::endmsg; + } + // This release (2.8.13) needs schema evolution before it can read 1.3.x + else if ( db_rel_version >= "1.3.0" && db_rel_version <= "1.3.4" ) + { + status = false; + log() << coral::Warning + << "Release number mismatch" + << " - SCHEMA EVOLUTION REQUIRED: " + << "database with OLDER release number " << db_rel_version + << " cannot be opened using CURRENT client release number " + << VersionInfo::release << coral::MessageStream::endmsg; + } + // This release (2.8.13) needs schema evolution before it can read 1.2.x + else if ( db_rel_version >= "1.2.0" && db_rel_version <= "1.2.9" ) + { + status = false; + log() << coral::Warning + << "Release number mismatch" + << " - SCHEMA EVOLUTION REQUIRED: " + << "database with OLDER release number " << db_rel_version + << " cannot be opened using CURRENT client release number " + << VersionInfo::release << coral::MessageStream::endmsg; + } + // This release (2.8.13) can NOT read any other previous releases + else + { + std::stringstream s; + s << "PANIC! Release number mismatch: " + << "database with (UNKNOWN!) OLDER release number " + << db_rel_version + << " cannot be opened using CURRENT client release number " + << VersionInfo::release; + throw IncompatibleReleaseNumber( s.str(), "RelationalDatabase" ); + } + } + // DbRelease == THISRELEASE + else if ( db_rel_version == VersionInfo::release ) + { + status = true; + log() << coral::Debug + << "Release number match: " + << "database with CURRENT release number " << db_rel_version + << " will be opened using CURRENT client release number " + << VersionInfo::release << coral::MessageStream::endmsg; + } + // Check schema version for dbs created with releases newer than this one! + // THIS_RELEASE < DbRelease + else if ( VersionInfo::release < db_rel_version ) + { + // Cannot open dbs with schema versions newer than that of this release! + // THIS_SCHEMAVERSION < DbSchemaVersion + if ( VersionInfo::schemaVersion < db_schema_version ) + { + std::stringstream s; + s << "Release number and schema version mismatch" + << " - SCHEMA NOT BACKWARD COMPATIBLE: " + << "database with NEWER release number " << db_rel_version + << " and NEWER schema version " << db_schema_version + << " cannot be opened using CURRENT client release number " + << VersionInfo::release + << " (CURRENT schema version " << VersionInfo::schemaVersion << ")"; + throw IncompatibleReleaseNumber( s.str(), "RelationalDatabase" ); + } + // Open databases created using a newer release but the same schema version + // THIS_SCHEMAVERSION == DbSchemaVersion + else if ( VersionInfo::schemaVersion == db_schema_version ) + { + status = true; + log() << coral::Debug + << "Release number mismatch with schema version match: " + << "database with NEWER release number " << db_rel_version + << " and CURRENT schema version " << db_schema_version + << " will be opened using CURRENT client release number " + << VersionInfo::release + << " (CURRENT schema version " + << VersionInfo::schemaVersion << ")" + << coral::MessageStream::endmsg; + } + // PANIC! How can it be that a newer release has an older schema? + else if ( VersionInfo::schemaVersion > db_schema_version ) + { + std::stringstream s; + s << "PANIC! Release number and schema version mismatch: " + << "database with NEWER release number " << db_rel_version + << " than CURRENT client release number " << VersionInfo::release + << " has OLDER schema version " << db_schema_version + << " (CURRENT schema version " << VersionInfo::schemaVersion << ")"; + throw IncompatibleReleaseNumber( s.str(), "RelationalDatabase" ); + } + // PANIC! How can it be that none of "<", "==", ">" is true? + else + { + std::stringstream s; + s << "PANIC! Release number and schema version mismatch: " + << "database with NEWER release number " << db_rel_version + << " than CURRENT client release number " << VersionInfo::release + << " has UNKNOWN schema version " << db_schema_version + << " (CURRENT schema version " << VersionInfo::schemaVersion << ")"; + throw IncompatibleReleaseNumber( s.str(), "RelationalDatabase" ); + } + } + // PANIC! How can it be that none of "<", "==", ">" is true? + else + { + std::stringstream s; + s << "PANIC! Release number mismatch: " + << "database with UNKNOWN release number " << db_rel_version + << " cannot be opened using CURRENT client release number " + << VersionInfo::release; + throw IncompatibleReleaseNumber( s.str(), "RelationalDatabase" ); + } + return status; +} + +//----------------------------------------------------------------------------- + +bool RelationalDatabase::isValidPayloadFieldName +( const std::string& name ) +{ + static std::string allowedChar = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ_1234567890"; + const std::string ucName = uppercaseString( name ); + if ( name.size() < 1 || + name.size() > 30 || + ucName.find_first_not_of( allowedChar ) != ucName.npos || + ucName.find_first_not_of( "_1234567890" ) != 0 || + ucName.find( "COOL_" ) == 0 ) + return false; + else + return true; +} + +//----------------------------------------------------------------------------- + +void RelationalDatabase::validatePayloadSpecification +( const IRecordSpecification& spec ) +{ + // Throw PayloadSpecificationTooManyFields if #fields > 900 + if ( spec.size() > 900 ) + throw PayloadSpecificationTooManyFields + ( spec.size(), "RelationalDatabase" ); + + // Throw PayloadSpecificationTooManyBlobFields if #blobFields > 10 + UInt32 nBlobFields = 0; + for ( UInt32 i = 0; i < spec.size(); i++ ) + if ( spec[i].storageType().id() == StorageType::Blob64k || + spec[i].storageType().id() == StorageType::Blob16M ) nBlobFields++; + if ( nBlobFields > 10 ) + throw PayloadSpecificationTooManyBlobFields + ( nBlobFields, "RelationalDatabase" ); + + // Throw PayloadSpecificationTooManyString255Fields if #string255Fields > 200 + UInt32 nSt255Fields = 0; + for ( UInt32 i = 0; i < spec.size(); i++ ) + if ( spec[i].storageType().id() == StorageType::String255 ) nSt255Fields++; + if ( nSt255Fields > 200 ) + throw PayloadSpecificationTooManyString255Fields + ( nSt255Fields, "RelationalDatabase" ); + + // Throw PayloadSpecificationInvalidFieldName if any field names are invalid. + // Names of payload fields must have between 1 and 30 characters (including + // only letters, digits or '_'), must start with a letter and cannot start + // with the "COOL_" prefix (in any lowercase/uppercase combination). + for ( UInt32 i = 0; i < spec.size(); i++ ) { + const std::string& name = spec[i].name(); + if ( ! isValidPayloadFieldName( name ) ) + throw PayloadSpecificationInvalidFieldName( name, "RelationalDatabase" ); + } +} + +//----------------------------------------------------------------------------- + +void RelationalDatabase::closeDatabase() +{ + disconnect(); + m_isOpen = false; +} + +//----------------------------------------------------------------------------- + +const std::string RelationalDatabase::mainTableName() const +{ + return RelationalDatabaseTable::tableName( databaseName() ); +} + +//----------------------------------------------------------------------------- + +const std::string RelationalDatabase::defaultTablePrefix() const +{ + if ( ! isOpen() ) throw DatabaseNotOpen( "RelationalDatabase" ); + const IRecord& dbAttr = databaseAttributes(); + std::string theDefaultTablePrefix = + dbAttr[RelationalDatabaseTable::attributeNames::defaultTablePrefix] + .data<RelationalDatabaseTable::columnTypes::attributeValue>(); + return theDefaultTablePrefix; +} + +//----------------------------------------------------------------------------- + +// *** START *** 3.0.0 schema extensions (task #4307) +const std::string RelationalDatabase::iovTablesTableName() const +{ + if ( ! isOpen() ) throw DatabaseNotOpen( "RelationalDatabase" ); + const IRecord& dbAttr = databaseAttributes(); + std::string tableName = + dbAttr[RelationalDatabaseTable::attributeNames::iovTablesTableName] + .data<RelationalDatabaseTable::columnTypes::attributeValue>(); + return tableName; +} + +//----------------------------------------------------------------------------- + +const std::string RelationalDatabase::channelTablesTableName() const +{ + if ( ! isOpen() ) throw DatabaseNotOpen( "RelationalDatabase" ); + const IRecord& dbAttr = databaseAttributes(); + std::string tableName = + dbAttr[RelationalDatabaseTable::attributeNames::channelTablesTableName] + .data<RelationalDatabaseTable::columnTypes::attributeValue>(); + return tableName; +} +// **** END **** 3.0.0 schema extensions (task #4307) + +//----------------------------------------------------------------------------- + +const std::string RelationalDatabase::nodeTableName() const +{ + if ( ! isOpen() ) throw DatabaseNotOpen( "RelationalDatabase" ); + const IRecord& dbAttr = databaseAttributes(); + std::string theNodeTableName = + dbAttr[RelationalDatabaseTable::attributeNames::nodeTableName] + .data<RelationalDatabaseTable::columnTypes::attributeValue>(); + return theNodeTableName; +} + +//----------------------------------------------------------------------------- + +const std::string RelationalDatabase::globalTagTableName() const +{ + if ( ! isOpen() ) throw DatabaseNotOpen( "RelationalDatabase" ); + const IRecord& dbAttr = databaseAttributes(); + std::string tableName = + dbAttr[RelationalDatabaseTable::attributeNames::tagTableName] + .data<RelationalDatabaseTable::columnTypes::attributeValue>(); + return tableName; +} + +//----------------------------------------------------------------------------- + +// *** START *** 3.0.0 schema extensions (task #4396) +const std::string RelationalDatabase::globalHeadTagTableName() const +{ + if ( ! isOpen() ) throw DatabaseNotOpen( "RelationalDatabase" ); + const IRecord& dbAttr = databaseAttributes(); + std::string tableName = + dbAttr[RelationalDatabaseTable::attributeNames::headTagTableName] + .data<RelationalDatabaseTable::columnTypes::attributeValue>(); + return tableName; +} + +//----------------------------------------------------------------------------- + +const std::string RelationalDatabase::globalUserTagTableName() const +{ + if ( ! isOpen() ) throw DatabaseNotOpen( "RelationalDatabase" ); + const IRecord& dbAttr = databaseAttributes(); + std::string tableName = + dbAttr[RelationalDatabaseTable::attributeNames::userTagTableName] + .data<RelationalDatabaseTable::columnTypes::attributeValue>(); + return tableName; +} +// **** END **** 3.0.0 schema extensions (task #4396) + +//----------------------------------------------------------------------------- + +const std::string RelationalDatabase::tag2TagTableName() const +{ + if ( ! isOpen() ) throw DatabaseNotOpen( "RelationalDatabase" ); + const IRecord& dbAttr = databaseAttributes(); + std::string tableName = + dbAttr[RelationalDatabaseTable::attributeNames::tag2TagTableName] + .data<RelationalDatabaseTable::columnTypes::attributeValue>(); + return tableName; +} + +//----------------------------------------------------------------------------- + +const std::string RelationalDatabase::tagSharedSequenceName() const +{ + if ( ! isOpen() ) throw DatabaseNotOpen( "RelationalDatabase" ); + const IRecord& dbAttr = databaseAttributes(); + std::string tableName = + dbAttr[RelationalDatabaseTable::attributeNames::tagSharedSequenceName] + .data<RelationalDatabaseTable::columnTypes::attributeValue>(); + return tableName; +} + +//----------------------------------------------------------------------------- + +const std::string RelationalDatabase::iovSharedSequenceName() const +{ + if ( ! isOpen() ) throw DatabaseNotOpen( "RelationalDatabase" ); + const IRecord& dbAttr = databaseAttributes(); + std::string tableName = + dbAttr[RelationalDatabaseTable::attributeNames::iovSharedSequenceName] + .data<RelationalDatabaseTable::columnTypes::attributeValue>(); + return tableName; +} + +//----------------------------------------------------------------------------- + +/* +bool RelationalDatabase::isValidAttributeListSpecification +( const coral::AttributeListSpecification& spec ) +{ + coral::AttributeListSpecification::const_iterator it; + for ( it=spec.begin(); it!=spec.end(); ++it ) { + std::string attrName = it->name(); + + // Check that attribute names only contain alphanumeric characters or '_' + static std::string allowedChar = + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_"; + if ( attrName.find_first_not_of(allowedChar) != std::string::npos ) { + log() << coral::Debug << "Invalid character in attribute name: '" + << attrName << "'" << coral::MessageStream::endmsg; + return false; + } + + } + return true; +} +*/ + +//----------------------------------------------------------------------------- + +bool RelationalDatabase::isOpen() const +{ + return m_isOpen; +} + +//----------------------------------------------------------------------------- + +coral::MessageStream& RelationalDatabase::log() const +{ + *m_log << coral::Verbose; + return *m_log; +} + +//----------------------------------------------------------------------------- + +RelationalSchemaMgr& RelationalDatabase::schemaMgr() const +{ + if ( m_schemaMgr.get() ) + return *m_schemaMgr; + else + throw RelationalException + ( "PANIC! RelationalSchemaMgr pointer is null", "RelationalDatabase" ); +} + +//----------------------------------------------------------------------------- + +RelationalNodeMgr& RelationalDatabase::nodeMgr() const +{ + if ( m_nodeMgr.get() ) + return *m_nodeMgr; + else + throw RelationalException + ( "PANIC! RelationalNodeMgr pointer is null", "RelationalDatabase" ); +} + +//----------------------------------------------------------------------------- + +RelationalTagMgr& RelationalDatabase::tagMgr() const +{ + if ( m_tagMgr.get() ) + return *m_tagMgr; + else + throw RelationalException + ( "PANIC! RelationalTagMgr pointer is null", "RelationalDatabase" ); +} + +//----------------------------------------------------------------------------- + +const RelationalObjectMgr& RelationalDatabase::objectMgr() const +{ + if ( m_objectMgr.get() ) + return *m_objectMgr; + else + throw RelationalException + ( "PANIC! RelationalObjectMgr pointer is null", + "RelationalDatabase" ); +} + +//----------------------------------------------------------------------------- + +void RelationalDatabase::setSchemaMgr +( std::auto_ptr<RelationalSchemaMgr> schemaMgr ) +{ + m_schemaMgr = schemaMgr; +} + +//----------------------------------------------------------------------------- + +void RelationalDatabase::setNodeMgr( std::auto_ptr<RelationalNodeMgr> nodeMgr ) +{ + m_nodeMgr = nodeMgr; +} + +//----------------------------------------------------------------------------- + +void RelationalDatabase::setTagMgr( std::auto_ptr<RelationalTagMgr> tagMgr ) +{ + m_tagMgr = tagMgr; +} + +//----------------------------------------------------------------------------- + +void RelationalDatabase::setObjectMgr +( std::auto_ptr<RelationalObjectMgr> objectMgr ) +{ + m_objectMgr = objectMgr; +} + +//----------------------------------------------------------------------------- + +boost::shared_ptr<IRelationalTransactionMgr> +RelationalDatabase::transactionMgr() const +{ + if ( m_transactionMgr.get() ) + return m_transactionMgr; + else + throw RelationalException + ( "PANIC! RelationalTransactionMgr pointer is null", + "RelationalDatabase" ); +} + +//----------------------------------------------------------------------------- + +void RelationalDatabase::setTransactionMgr +( boost::shared_ptr<IRelationalTransactionMgr> transactionMgr ) +{ + m_transactionMgr = transactionMgr; +} + +//----------------------------------------------------------------------------- + +const IRecordSpecification& +RelationalDatabase::databaseAttributesSpecification() +{ + static RecordSpecification s_dbAttrSpec; + + if ( s_dbAttrSpec.size() == 0 ) { + s_dbAttrSpec.extend + ( RelationalDatabaseTable::attributeNames::defaultTablePrefix, + RelationalDatabaseTable::columnTypeIds::attributeValue ); + /* + // *** START *** 3.0.0 schema extensions (task #4307) + s_dbAttrSpec.extend + ( RelationalDatabaseTable::attributeNames::iovTablesTableName, + RelationalDatabaseTable::columnTypeIds::attributeValue ); + s_dbAttrSpec.extend + ( RelationalDatabaseTable::attributeNames::channelTablesTableName, + RelationalDatabaseTable::columnTypeIds::attributeValue ); + // **** END **** 3.0.0 schema extensions (task #4307) + */ + s_dbAttrSpec.extend + ( RelationalDatabaseTable::attributeNames::nodeTableName, + RelationalDatabaseTable::columnTypeIds::attributeValue ); + /* + // *** START *** 3.0.0 schema extensions (task #4396) + s_dbAttrSpec.extend + ( RelationalDatabaseTable::attributeNames::headTagTableName, + RelationalDatabaseTable::columnTypeIds::attributeValue ); + s_dbAttrSpec.extend + ( RelationalDatabaseTable::attributeNames::userTagTableName, + RelationalDatabaseTable::columnTypeIds::attributeValue ); + // **** END **** 3.0.0 schema extensions (task #4396) + */ + s_dbAttrSpec.extend + ( RelationalDatabaseTable::attributeNames::tagTableName, + RelationalDatabaseTable::columnTypeIds::attributeValue ); + s_dbAttrSpec.extend + ( RelationalDatabaseTable::attributeNames::tag2TagTableName, + RelationalDatabaseTable::columnTypeIds::attributeValue ); + s_dbAttrSpec.extend + ( RelationalDatabaseTable::attributeNames::tagSharedSequenceName, + RelationalDatabaseTable::columnTypeIds::attributeValue ); + s_dbAttrSpec.extend + ( RelationalDatabaseTable::attributeNames::iovSharedSequenceName, + RelationalDatabaseTable::columnTypeIds::attributeValue ); + s_dbAttrSpec.extend + ( RelationalDatabaseTable::attributeNames::release, + RelationalDatabaseTable::columnTypeIds::attributeValue ); + s_dbAttrSpec.extend + ( RelationalDatabaseTable::attributeNames::cvsCheckout, + RelationalDatabaseTable::columnTypeIds::attributeValue ); + s_dbAttrSpec.extend + ( RelationalDatabaseTable::attributeNames::cvsCheckin, + RelationalDatabaseTable::columnTypeIds::attributeValue ); + s_dbAttrSpec.extend + ( RelationalDatabaseTable::attributeNames::schemaVersion, + RelationalDatabaseTable::columnTypeIds::attributeValue ); + } + return s_dbAttrSpec; +} + +//----------------------------------------------------------------------------- + +const StorageType& +RelationalDatabase::storageType( const std::string& name ) +{ + if ( name == "Bool" ) + return StorageType::storageType( StorageType::Bool ); + //if ( name == "Char" ) + // return StorageType::storageType( StorageType::Char ); + if ( name == "UChar" ) + return StorageType::storageType( StorageType::UChar ); + if ( name == "Int16" ) + return StorageType::storageType( StorageType::Int16 ); + if ( name == "UInt16" ) + return StorageType::storageType( StorageType::UInt16 ); + if ( name == "Int32" ) + return StorageType::storageType( StorageType::Int32 ); + if ( name == "UInt32" ) + return StorageType::storageType( StorageType::UInt32 ); + if ( name == "UInt63" ) + return StorageType::storageType( StorageType::UInt63 ); + if ( name == "Int64" ) + return StorageType::storageType( StorageType::Int64 ); + //if ( name == "UInt64" ) + // return StorageType::storageType( StorageType::UInt64 ); + if ( name == "Float" ) + return StorageType::storageType( StorageType::Float ); + if ( name == "Double" ) + return StorageType::storageType( StorageType::Double ); + if ( name == "String255" ) + return StorageType::storageType( StorageType::String255 ); + if ( name == "String4k" ) + return StorageType::storageType( StorageType::String4k ); + if ( name == "String64k" ) + return StorageType::storageType( StorageType::String64k ); + if ( name == "String16M" ) + return StorageType::storageType( StorageType::String16M ); + if ( name == "Blob64k" ) + return StorageType::storageType( StorageType::Blob64k ); + if ( name == "Blob16M" ) + return StorageType::storageType( StorageType::Blob16M ); + throw RelationalException + ( "PANIC! No StorageType exists with name " + name, "RelationalDatabase" ); +} + +//----------------------------------------------------------------------------- + +const std::string RelationalDatabase::encodeRecordSpecification +( const IRecordSpecification& recordSpec ) +{ + std::ostringstream out; + for ( unsigned int i=0; i<recordSpec.size(); i++ ) { + const IFieldSpecification& fieldSpec = recordSpec[i]; + if ( i != 0 ) out << ","; + out << fieldSpec.name() + << ":" << fieldSpec.storageType().name(); + } + return out.str(); +} + +//----------------------------------------------------------------------------- + +const RecordSpecification +RelationalDatabase::decodeRecordSpecification( const std::string& encodedSpec ) +{ + RecordSpecification recordSpec; + if ( !encodedSpec.empty() ) { + std::string::size_type pos = 0; + while ( pos != encodedSpec.npos ) { + std::string::size_type newpos = encodedSpec.find( ',', pos ); + std::string item_str; + if ( newpos != encodedSpec.npos ) + item_str = encodedSpec.substr(pos,newpos-pos); + else + item_str = encodedSpec.substr(pos); + std::string::size_type separator_pos = item_str.find(':'); + if ( separator_pos == item_str.npos ) + throw RelationalException + ( std::string + ( "Bad format, ':' not found in encoded RecordSpecification '" ) + + encodedSpec + "'", "RelationalDatabase" ); + recordSpec.extend + ( item_str.substr( 0, separator_pos ), + RelationalDatabase::storageType + ( item_str.substr( separator_pos+1 ) ) ); + pos = ( newpos != encodedSpec.npos ) ? newpos+1 : newpos; + } + } + return recordSpec; +} + +//----------------------------------------------------------------------------- + +RelationalTableRow +RelationalDatabase::fetchTagTableRow( const std::string& tagTableName, + const std::string& tagName ) +{ + log() << "Fetch tag table row for tag " << tagName + << " from table '" << tagTableName + << "'" << coral::MessageStream::endmsg; + + // Define the WHERE clause for the selection using bind variables + RecordSpecification whereDataSpec; + whereDataSpec.extend( "tagName", + RelationalTagTable::columnTypeIds::tagName ); + Record whereData( whereDataSpec ); + whereData["tagName"].setValue( tagName ); + std::string whereClause = RelationalTagTable::columnNames::tagName; + whereClause += "= :tagName"; + + // Delegate the query to the RelationalQueryMgr + RelationalQueryDefinition queryDef; + queryDef.addFromItem( tagTableName ); + queryDef.addSelectItems( RelationalTagTable::tableSpecification() ); + queryDef.setWhereClause( whereClause ); + queryDef.setBindVariables( whereData ); + try + { + return queryMgr().fetchOrderedRows( queryDef, "", 1 )[0]; // expect 1 row + } + catch( NoRowsFound& ) + { + throw TagNotFound + ( "Tag '" + tagName + "' not found in local tag table " + tagTableName , + "RelationalDatabase" ); + } +} + +//----------------------------------------------------------------------------- + +const IHvsTagMgr& RelationalDatabase::hvsTagMgr() const +{ + return tagMgr(); +} + +//----------------------------------------------------------------------------- + +const Record RelationalDatabase::fetchDatabaseAttributes() const +{ + // Fetch all rows from the top-level management table + std::vector<RelationalTableRow> rows; + try + { + RelationalQueryDefinition queryDef; + queryDef.addFromItem( mainTableName() ); + queryDef.addSelectItems( RelationalDatabaseTable::tableSpecification() ); + rows = queryMgr().fetchOrderedRows( queryDef ); // no WHERE or ORDER clause + } + catch ( TableNotFound& ) + { + log() << coral::Verbose + << "Could not open database - main database table not found" + << coral::MessageStream::endmsg; + throw DatabaseDoesNotExist( "RelationalDatabase" ); + } + + // Create a new database attributes Record from the rows retrieved + // Read ALL rows even if they are not in the default specification + // (e.g. read schema evolution information where present) + // Use a vector instead of a map to keep the order of properties. + std::vector < std::pair<std::string, std::string> > propertyList; + RecordSpecification spec; + const StorageType::TypeId attrTypeId = + RelationalDatabaseTable::columnTypeIds::attributeValue; + for ( std::vector<RelationalTableRow>::const_iterator + row = rows.begin(); row != rows.end(); ++row ) + { + std::string attrName = + (*row)[RelationalDatabaseTable::columnNames::attributeName]. + data<std::string>(); + std::string attrValue = + (*row)[RelationalDatabaseTable::columnNames::attributeValue]. + data<std::string>(); + spec.extend( attrName, attrTypeId ); + std::pair<std::string, std::string> property( attrName, attrValue ); + propertyList.push_back( property ); + } + Record dbAttr( spec ); + for ( std::vector < std::pair<std::string, std::string> >::const_iterator + prop = propertyList.begin(); prop != propertyList.end(); ++prop ) + { + dbAttr[prop->first].setValue( prop->second ); + } + + // Return the database attributes + return dbAttr; +} + +//----------------------------------------------------------------------------- + +RelationalTableRow +RelationalDatabase::fetchObject2TagTableRow +( const std::string& object2TagTableName, + unsigned int tagId, + unsigned int objectId, + PayloadMode::Mode pMode ) +{ + // Define the WHERE clause for the selection using bind variables + RecordSpecification whereDataSpec; + whereDataSpec.extend( "tagId", + RelationalObject2TagTable::columnTypeIds::tagId ); + whereDataSpec.extend( "objectId", + RelationalObject2TagTable::columnTypeIds::objectId ); + Record whereData( whereDataSpec ); + whereData["tagId"].setValue( tagId ); + whereData["objectId"].setValue( objectId ); + std::string whereClause = RelationalObject2TagTable::columnNames::tagId; + whereClause += "= :tagId"; + whereClause += " and "; + whereClause += RelationalObject2TagTable::columnNames::objectId; + whereClause += "= :objectId"; + + // Delegate the query to the RelationalQueryMgr + RelationalQueryDefinition queryDef; + queryDef.addFromItem( object2TagTableName ); + queryDef.addSelectItems + ( RelationalObject2TagTable::tableSpecification( pMode ) ); + queryDef.setWhereClause( whereClause ); + queryDef.setBindVariables( whereData ); + std::stringstream s; + s << "Query object2Tag table row with tag_id=" << tagId + << " and object_id=" << objectId; + return queryMgr().fetchOrderedRows( queryDef, s.str(), 1 )[0]; // expct 1 row +} + +//----------------------------------------------------------------------------- + +const RelationalTableRow +RelationalDatabase::fetchNodeTableRow( const std::string& fullPath ) const +{ + return nodeMgr().fetchNodeTableRow( fullPath ); +} + +//----------------------------------------------------------------------------- + +const RelationalTableRow +RelationalDatabase::fetchNodeTableRow( unsigned int nodeId ) const +{ + return nodeMgr().fetchNodeTableRow( nodeId ); +} + +//----------------------------------------------------------------------------- + +const RelationalTableRow +RelationalDatabase::fetchNodeTableRow( const std::string& whereClause, + const Record& whereData ) const +{ + return nodeMgr().fetchNodeTableRow( whereClause, whereData ); +} + +//----------------------------------------------------------------------------- + +const std::vector<std::string> +RelationalDatabase::listNodes( unsigned int nodeId, + bool isLeaf, + bool ascending ) const +{ + return nodeMgr().listNodes( nodeId, isLeaf, ascending ); +} + +//----------------------------------------------------------------------------- + +const std::vector<std::string> +RelationalDatabase::listFolders( const RelationalFolderSet* folderset, + bool ascending ) const +{ + // Cross-check that the database is open + checkDbOpenTransaction( "RelationalDatabase::listFolders", + cool::ReadOnly ); + + bool isLeaf = true; + std::vector<std::string> + nodes( listNodes( folderset->id(), isLeaf, ascending ) ); + + return nodes; +} + +//----------------------------------------------------------------------------- + +const std::vector<std::string> +RelationalDatabase::listFolderSets( const RelationalFolderSet* folderset, + bool ascending ) const +{ + // Cross-check that the database is open + checkDbOpenTransaction( "RelationalDatabase::listFolderSets", + cool::ReadOnly ); + + bool isLeaf = false; + std::vector<std::string> + nodes( listNodes( folderset->id(), isLeaf, ascending ) ); + + return nodes; +} + +//----------------------------------------------------------------------------- + +const std::vector<std::string> +RelationalDatabase::listAllNodes( bool ascending ) +{ + // Cross-check that the database is open + checkDbOpenTransaction( "RelationalDatabase::listAllNodes", + cool::ReadOnly ); + + // Delegate to the node manager + std::vector<std::string> nodeList = nodeMgr().listAllNodes( ascending ); + + // Return the list of nodes + return nodeList; +} + +//----------------------------------------------------------------------------- + +/* +const std::vector<std::string> +RelationalDatabase::listAllNodes( bool ascending ) +{ + return nodeMgr().listAllNodes( ascending ); +} +*/ + +//----------------------------------------------------------------------------- + +bool RelationalDatabase::existsNode( const std::string& fullPath ) +{ + return nodeMgr().existsNode( fullPath ); +} + +//----------------------------------------------------------------------------- + +bool RelationalDatabase::existsFolderSet( const std::string& fullPath ) +{ + return nodeMgr().existsFolderSet( fullPath ); +} + +//----------------------------------------------------------------------------- + +bool RelationalDatabase::existsFolder( const std::string& fullPath ) +{ + return nodeMgr().existsFolder( fullPath ); +} + +//----------------------------------------------------------------------------- + +const std::vector<std::string> RelationalDatabase::listAllTables() const +{ + checkDbOpenTransaction( "RelationalDatabase::listAllTables", + cool::ReadOnly ); + std::vector<std::string> tables; + + // Get the database schema version + std::string dbSchemaVersion = + m_dbAttr[RelationalDatabaseTable::attributeNames::schemaVersion]. + data<std::string>(); + + // Add the tables of each node + RelationalQueryDefinition queryDef; + queryDef.addFromItem( nodeTableName() ); + queryDef.addSelectItems( RelationalNodeTable::tableSpecification( VersionNumber( dbSchemaVersion ) ) ); + std::vector<RelationalTableRow> nodes = + queryMgr().fetchOrderedRows( queryDef ); + std::vector<RelationalTableRow>::const_iterator node; + for ( node = nodes.begin(); node != nodes.end(); node++ ) + { + std::string fullPath = + (*node)[RelationalNodeTable::columnNames::nodeFullPath] + .data<std::string>(); + bool isLeaf = + (*node)[RelationalNodeTable::columnNames::nodeIsLeaf].data<bool>(); + // If the database schema version is 2.0.0 or higher, check that + // the node schema version is supported by this software release + if ( VersionNumber( dbSchemaVersion ) >= VersionNumber( "2.0.0" ) ) + { + VersionNumber schemaVersion = + (*node)[RelationalNodeTable::columnNames::nodeSchemaVersion] + .data<std::string>(); + bool isSupported = true; + if ( isLeaf ) + { + if ( !RelationalFolder::isSupportedSchemaVersion( schemaVersion ) ) + { + // Hack: 2.0.0 folders are not supported, but tables can be listed + if ( schemaVersion != VersionNumber( "2.0.0" ) ) + isSupported = false; + } + } + else + { + if ( !RelationalFolderSet::isSupportedSchemaVersion( schemaVersion ) ) + isSupported = false; + } + if ( VersionInfo::release < schemaVersion ) + { + std::stringstream s; + s << "Cannot list tables for node:"; + if ( isLeaf ) s << " folder '"; + else s << " folder set '"; + s << fullPath << " has schema version " << schemaVersion + << " that is newer than this software release " + << VersionInfo::release; + log() << coral::Warning << s.str() << coral::MessageStream::endmsg; + if ( isLeaf ) + throw UnsupportedFolderSchema( s.str(), "RelationalDatabase" ); + else + throw UnsupportedFolderSetSchema( s.str(), "RelationalDatabase" ); + } + else if ( !isSupported ) + { + std::stringstream s; + s << "PANIC! Cannot list tables for node:"; + if ( isLeaf ) s << " folder '"; + else s << " folder set '"; + s << fullPath + << "' appears to have been created using UNKNOWN schema version " + << schemaVersion + << " that is older than (or as old as) the current software release " + << VersionInfo::release; + throw PanicException( s.str(), "RalDatabase" ); + } + } + unsigned int nodeId = + (*node)[RelationalNodeTable::columnNames::nodeId].data<unsigned int>(); + // Node is a folder + if ( isLeaf ) + { + // Add tables for MV folders + FolderVersioning::Mode versioningMode = + RelationalFolder::versioningMode( (*node).data() ); + if ( versioningMode == FolderVersioning::MULTI_VERSION ) + { + // Add the IOV2tag table + tables.push_back + ( RelationalFolder::object2TagTableName( (*node).data() ) ); + // Add the local tag sequence + tables.push_back + ( RelationalTagSequence::sequenceName + ( defaultTablePrefix(), nodeId ) ); + // Add the local tag table + tables.push_back + ( RelationalFolder::tagTableName( (*node).data() ) ); + } + // Add the channel table (2.0.0 or higher) + if ( VersionNumber( dbSchemaVersion ) >= VersionNumber( "2.0.0" ) ) + tables.push_back + ( RelationalChannelTable::defaultTableName + ( defaultTablePrefix(), nodeId ) ); + // Add the IOV table and the associated sequence + tables.push_back + ( RelationalObjectTable::sequenceName + ( RelationalFolder::objectTableName((*node).data()) ) ); + tables.push_back + ( RelationalFolder::objectTableName( (*node).data() ) ); + } + // Node is a folder set + else + { + // Add the local tag sequence + tables.push_back + ( RelationalTagSequence::sequenceName + ( defaultTablePrefix(), nodeId ) ); + } + } + + // Add the global tag table + tables.push_back( globalTagTableName() ); + + // Add the tag2tag table and its associated sequence + tables.push_back( RelationalTag2TagTable::sequenceName(tag2TagTableName()) ); + tables.push_back( tag2TagTableName() ); + + // Add the node table and its associated sequence + tables.push_back( RelationalNodeTable::sequenceName(nodeTableName()) ); + tables.push_back( nodeTableName() ); + + // Add the main table + tables.push_back( mainTableName() ); + + // Return the full list of tables + return tables; +} + +//----------------------------------------------------------------------------- diff --git a/28PATCHES2013/RelationalCool/src/NEW/RelationalDatabase.h b/28PATCHES2013/RelationalCool/src/NEW/RelationalDatabase.h new file mode 100644 index 0000000000000000000000000000000000000000..e68708e7d09b8a8527e393bae9b12e7d78c97162 --- /dev/null +++ b/28PATCHES2013/RelationalCool/src/NEW/RelationalDatabase.h @@ -0,0 +1,527 @@ +#ifndef RELATIONALCOOL_RELATIONALDATABASE_H +#define RELATIONALCOOL_RELATIONALDATABASE_H 1 + +// Include files +#include <memory> +#include <vector> +#include <boost/enable_shared_from_this.hpp> +#include "CoolKernel/ChannelSelection.h" +//#include "CoolKernel/PayloadMode.h" +#include "PayloadMode.h" // TEMPORARY +#include "CoolKernel/IDatabase.h" +#ifdef COOL400 +#include "CoolKernel/ITransaction.h" +#endif +#include "CoolKernel/Record.h" +#include "CoolKernel/ValidityKey.h" +#include "CoralBase/AttributeList.h" +#include "CoralBase/MessageStream.h" + +// Local include files +#include "IHvsTagMgr.h" +#include "RelationalDatabaseId.h" +#include "RelationalDatabasePtr.h" +#include "RelationalObjectPtr.h" + +namespace cool +{ + + // Forward declarations + class ChannelSelection; + class IRelationalTransactionMgr; + class RelationalFolder; + class RelationalFolderSet; + class RelationalNodeMgr; + class RelationalObjectMgr; + class RelationalObjectTable; + class RelationalObjectTableRow; + class RelationalQueryMgr; + class RelationalSchemaMgr; + class RelationalTableRow; + class RelationalTagMgr; + + /** + * FIXME move somewhere else? + * + * Enumeration for AccessMode + * + * To be used in parameter list, to avoid + * boolean parameters. + */ + + enum AccessMode { ReadWrite, ReadOnly }; + + /** @class RelationalDatabase RelationalDatabase.h + * + * Generic relational implementation of one COOL "condition database" + * instance (deployed on a specific physical infrastructure). + * + * Abstract base class for specific relational implementations + * sharing the same relational database schema (RAL, MySQL, ...). + * + * @author Andrea Valassi, Sven A. Schmidt and Marco Clemencic + * @date 2004-11-09 + */ + + + class RelationalDatabase : public IDatabase + { + + friend class RelationalDatabaseTest; + friend class RalDatabaseTest; + friend class RalDatabaseTest_extendedSpec; + + public: + + // --- Implementation of the IDatabase interface. --- + + /// Return the global identifier of the database + /// [WARNING: any visible passwords are masked out]. + const DatabaseId& databaseId() const; + + /// Return the 'attributes' of the database + /// (implementation-specific properties not exposed in the API). + /// Throws DatabaseNotOpen if the database is not open. + const IRecord& databaseAttributes() const; + + /// Helper methods, does the ususal checks if + /// database is open, rw/ro, a transaction active, etc + /// Throws exceptions if the + void checkDbOpenTransaction( const std::string& domain, + cool::AccessMode mode ) const; + + /* + /// Does the database support this payload specification? + bool isValidPayloadSpecification( const IRecordSpecification& spec ); + */ + + /* + /// Does the database support this channel specification? + bool isValidChannelSpecification( const IRecordSpecification& spec ); + */ + + /// Create a new folder set and return the corresponding manager. + /// The ownership of the folderset manager instance is shared. + /// Throws DatabaseNotOpen if the database is not open. + /// Throws HvsPathHandlerException if the given path has an invalid format. + /// Throws NodeExists if a folder[set] with the same path already exists. + /// Throws an Exception if the max# of folder[set]s (9999) is exceeded. + /// Throws an Exception if an invalid versioning mode has been specified. + /// Throws an Exception if the user does not have writer privileges. + /// PURE VIRTUAL method implemented in subclasses. + virtual IFolderSetPtr createFolderSet + ( const std::string& fullPath, + const std::string& description = "", + bool createParents = false ) = 0; + + /// Does this folder set exist? + /// Throws DatabaseNotOpen if the database is not open. + /// Throws HvsPathHandlerException if the given path has an invalid format. + bool existsFolderSet( const std::string& folderSetName ); + + /// Retrieve an existing folderset and return the corresponding manager. + /// The ownership of the folderset manager instance is shared. + /// Throws DatabaseNotOpen if the database is not open. + /// Throws HvsPathHandlerException if the given path has an invalid format. + /// Throws FolderSetNotFound if the folderset does not exist. + /// PURE VIRTUAL method implemented in subclasses. + virtual IFolderSetPtr getFolderSet( const std::string& fullPath ) = 0; + + /// Create a new folder and return the corresponding manager. + /// The ownership of the folder manager instance is shared. + /// Throws DatabaseNotOpen if the database is not open. + /// Throws HvsPathHandlerException if the given path has an invalid format. + /// Throws NodeExists if a folder[set] with the same path already exists. + /// Throws an Exception if the max# of folder[set]s (9999) is exceeded. + /// Throws an Exception if an invalid versioning mode has been specified. + /// Throws an Exception if the user does not have writer privileges. + /// PURE VIRTUAL method implemented in subclasses. + virtual IFolderPtr createFolder + ( const std::string& fullPath, + const IFolderSpecification& folderSpec, + const std::string& description = "", + bool createParents = false ) = 0; + + /// DEPRECATED: use IFolderSpecification instead of IRecordSpecification! + /// This is similar to the COOL1.3.3 API (with IRecordSpecification + /// instead of ExtendedAttributeListSpecification), for easier porting of + /// user code, but it is likely to be removed in a future COOL release. + /// PURE VIRTUAL method implemented in subclasses. + virtual IFolderPtr createFolder + ( const std::string& fullPath, + const IRecordSpecification& payloadSpec, + const std::string& description = "", + FolderVersioning::Mode mode = FolderVersioning::SINGLE_VERSION, + bool createParents = false ) = 0; + + /// Does this folder exist? + /// Throws DatabaseNotOpen if the database is not open. + /// Throws HvsPathHandlerException if the given path has an invalid format. + bool existsFolder( const std::string& fullPath ); + + /// Retrieve an existing folder and return the corresponding manager. + /// The ownership of the folder manager instance is shared. + /// Throws DatabaseNotOpen if the database is not open. + /// Throws HvsPathHandlerException if the given path has an invalid format. + /// Throws FolderNotFound if the folder does not exist. + /// PURE VIRTUAL method implemented in subclasses. + virtual IFolderPtr getFolder( const std::string& fullPath ) = 0; + + /// Return the list of existing nodes + /// (in ascending/descending alphabetical order). + const std::vector<std::string> listAllNodes( bool ascending = true ); + + /// Drop an existing node (folder or folder set). + /// Also delete any tags associated to the node. + /// Return true if the node and all its structures are dropped as expected. + /// Return false (without throwing any exception) if the node and + /// all its structures do not exist any more on exit from this method, + /// but the node or some of its structures did not exist to start with. + /// Throw an Exception if the node schema version is more recent than + /// the schema version supported by the current COOL software release. + /// Throw an Exception if the node or one of its structures cannot + /// be dropped (i.e. continue to exist on exit from this method). + /// Throw an Exception if the node is a non-empty folder set. + /// Throw an Exception if any associated tags cannot be deleted. + /// PURE VIRTUAL method implemented in subclasses. + virtual bool dropNode( const std::string& fullPath ) = 0; + + /// HVS: does this tag exist? + /// Tag names, except for "HEAD", are case sensitive. + /// Returns true for the reserved tags "" and "HEAD". + bool existsTag( const std::string& tagName ) const + { + return hvsTagMgr().existsTag( tagName ); + } + + /// HVS: return the node type (inner/leaf) where this tag name can be used. + /// Tag names, except for "HEAD", are case sensitive. + /// Throws TagNotFound if the tag does not exist. + IHvsNode::Type tagNameScope( const std::string& tagName ) const + { + return hvsTagMgr().tagNameScope( tagName ); + } + + /// HVS: return the names of the nodes where this tag is defined. + /// Tag names, except for "HEAD", are case sensitive. + /// Throws TagNotFound if the tag does not exist. + /// Throws ReservedHeadTag for the HEAD tag (defined in all folders). + const std::vector<std::string> + taggedNodes( const std::string& tagName ) const + { + return hvsTagMgr().taggedNodes( tagName ); + } + + /// Is the database 'open'? + /// NB Note the difference between 'open' and 'connected': the database + /// is 'connected' if the connection to the database backend has been + /// established; it is 'open' if the management table has been read. + bool isOpen() const; + + /// (Re)opens the database. + void openDatabase(); + + /// Closes the database. + void closeDatabase(); + + /// Return the "COOL database name". + const std::string& databaseName() const; + +#ifdef COOL400 + /// Start a new transaction and enter manual transaction mode + virtual ITransactionPtr startTransaction() = 0; +#endif + + // --- Other public methods. --- + + /// Return the list of folders inside the given folderset + /// (in ascending/descending alphabetical order). + const std::vector<std::string> + listFolders( const RelationalFolderSet* folderset, + bool ascending = true ) const; + + /// Return the list of foldersets inside the given folderset + /// (in ascending/descending alphabetical order). + const std::vector<std::string> + listFolderSets( const RelationalFolderSet* folderset, + bool ascending = true ) const; + + /// Get a constant reference to the HVS tag manager + const IHvsTagMgr& hvsTagMgr() const; + + /// Return the RelationalDatabasePtr + /// PURE VIRTUAL method implemented in subclasses. + virtual boost::shared_ptr<RelationalDatabase> relationalDbPtr() = 0; + + /// Return the default table prefix (from the database attributes) + const std::string defaultTablePrefix() const; + + /// Return the name of the main table (from the db name) + const std::string mainTableName() const; + + // *** START *** 3.0.0 schema extensions (task #4307) + /// Return the name of the iovTables table (from the db attributes) + const std::string iovTablesTableName() const; + + /// Return the name of the channelTables table (from the db attributes) + const std::string channelTablesTableName() const; + // **** END **** 3.0.0 schema extensions (task #4307) + + /// Return the name of the folder table (from the db attributes) + const std::string nodeTableName() const; + + /// Return the name of the global tag table (from the db attributes) + const std::string globalTagTableName() const; + + // *** START *** 3.0.0 schema extensions (task #4396) + /// Return the name of the global head tag table (from the db attributes) + const std::string globalHeadTagTableName() const; + + /// Return the name of the global user tag table (from the db attributes) + const std::string globalUserTagTableName() const; + // **** END **** 3.0.0 schema extensions (task #4396) + + /// Return the name of the tag2tag table (from the db attributes) + const std::string tag2TagTableName() const; + + /// Return the name of the tag shared sequence (from the db attributes) + const std::string tagSharedSequenceName() const; + + /// Return the name of the IOV shared sequence (from the db attributes) + const std::string iovSharedSequenceName() const; + + /// Get a RelationalObjectTable for the given folder. + /// The concrete class can only be created by the concrete database. + /// The RelationalFolder parameter is only used to obtain + /// the associated table names and is *not* retained. + /// PURE VIRTUAL method implemented in subclasses. + virtual boost::shared_ptr<RelationalObjectTable> + relationalObjectTable( const RelationalFolder& folder ) const = 0; + + /// Update the description for the given node + /// PURE VIRTUAL method implemented in subclasses. + virtual void updateNodeTableDescription + ( const std::string& fullPath, + const std::string& description ) const = 0; + + /// Get the IRelationalTransactionMgr + boost::shared_ptr<IRelationalTransactionMgr> transactionMgr() const; + + /// Return the StorageType singleton for the given type name. + /// Throw a RelationalException if no storage type exists with that name. + static const StorageType& storageType( const std::string& name ); + + /// Get a string representation of a RecordSpecification + static const std::string + encodeRecordSpecification( const IRecordSpecification& recordSpec ); + + /// Decode a RecordSpecification from its string representation + static const RecordSpecification + decodeRecordSpecification( const std::string& encodedSpec ); + + /// Get the RelationalQueryMgr + /// PURE VIRTUAL method implemented in subclasses. + virtual const RelationalQueryMgr& queryMgr() const = 0; + + /// Get the RelationalSchemaMgr + RelationalSchemaMgr& schemaMgr() const; + + /// Get the RelationalNodeMgr + RelationalNodeMgr& nodeMgr() const; + + /// Get the RelationalTagMgr + RelationalTagMgr& tagMgr() const; + + /// Get the RelationalObjectMgr + const RelationalObjectMgr& objectMgr() const; + + /// Is this a valid name for a payload field of a folder? + /// Payload field names must have between 1 and 30 characters (including + /// only letters, digits or '_'), must start with a letter and cannot start + /// with the "COOL_" prefix (in any lowercase/uppercase combination). + static bool isValidPayloadFieldName( const std::string& name ); + + /// Return the list of all existing tables (within a transaction) + const std::vector<std::string> listAllTables() const; + + /// Return the list of all existing tables (no transaction) + const std::vector<std::string> __listAllTables() const; + + /// Required access mode to the database. + /// Delegated to RalSessionMgr. + virtual bool isReadOnly() const = 0; + + protected: + + /// The following methods are all protected: only subclasses can + /// instantiate or delete this class and create, drop or open a database + + /// Destructor + virtual ~RelationalDatabase(); + + /// Constructor + RelationalDatabase( const DatabaseId& dbId ); + + /// Create a new database with default attributes. + /// Default attributes are those specific for a RelationalDatabase. + void createDatabase(); + + /// Create a new database with non-default attributes. + /// PURE VIRTUAL method implemented in subclasses. + virtual void createDatabase( const IRecord& dbAttr ) = 0; + + /// Drop the database. + /// PURE VIRTUAL method implemented in subclasses. + virtual bool dropDatabase() = 0; + + /// Fetch the database attributes (fetch all rows from the main table). + const Record fetchDatabaseAttributes() const; + + /// AV - TO BE REMOVED + /// Fetch one node row (lookup by 1 node fullPath) + const RelationalTableRow + fetchNodeTableRow( const std::string& fullPath ) const; + + /// AV - TO BE REMOVED + /// Fetch one node row (lookup by 1 nodeId) + const RelationalTableRow + fetchNodeTableRow( unsigned int nodeId ) const; + + /// AV - TO BE REMOVED + /// Fetch one node row (lookup with given WHERE clause and bind variables) + const RelationalTableRow + fetchNodeTableRow( const std::string& whereClause, + const Record& whereData ) const; + + /// WARNING: UNUSED! (AV) + /// Fetch the tag table row for the given tagname in the given tag table + RelationalTableRow + fetchTagTableRow( const std::string& tagTableName, + const std::string& tagName ); + + /// WARNING: USED ONLY IN TESTS! (AV) + /// Fetch the object2Tag table row for the given tagId, objectId + RelationalTableRow + fetchObject2TagTableRow( const std::string& tagTableName, + unsigned int tagId, + unsigned int objectId, + PayloadMode::Mode pMode ); + + /// Does this node exist? + bool existsNode( const std::string& fullPath ); + + /// Return the list of nodes inside the given nodeId with the attribute + /// isLeaf as specified (ordered by name asc/desc) + const std::vector<std::string> + listNodes( unsigned int nodeId, + bool isLeaf, + bool ascending = true ) const; + + /// Get a CORAL MessageStream + coral::MessageStream& log() const; + + /// Set the RelationalSchemaMgr (transfer ownership) + void setSchemaMgr( std::auto_ptr<RelationalSchemaMgr> schemaMgr ); + + /// Set the RelationalNodeMgr (transfer ownership) + void setNodeMgr( std::auto_ptr<RelationalNodeMgr> nodeMgr ); + + /// Set the RelationalTagMgr (transfer ownership) + void setTagMgr( std::auto_ptr<RelationalTagMgr> tagMgr ); + + /// Set the RelationalObjectMgr (transfer ownership) + void setObjectMgr( std::auto_ptr<RelationalObjectMgr> objectMgr ); + + /// Set the IRelationalTransactionMgr (shared ownership) + void setTransactionMgr + ( boost::shared_ptr<IRelationalTransactionMgr> mgr ); + + /// Database attribute specification for the RelationalDatabase class + static + const IRecordSpecification& databaseAttributesSpecification(); + + /// Check whether this software library can read the given schema. + /// Returns true if the schema can be read without schema evolution. + /// Returns false if the schema requires schema evolution. + /// Throws an IncompatibleReleaseNumber if the schema is newer than + /// this software library or no schema evolution is possible. + bool areReleaseAndSchemaCompatible + ( const std::string releaseNumber, + const std::string schemaVersion ) const; + + /// Validate the payload specification. + /// Throws InvalidPayloadSpecification if the payload specification is + /// invalid: there can be at most 900 fields, including up to 10 BLOB + /// fields and up to 200 String255 fields; field names must be between + /// 1 and 30 characters (including only letters, digits or '_'), must + /// start with a letter and cannot start with the "COOL_" prefix (in any + /// combination of lowercase and uppercase letters). + void validatePayloadSpecification( const IRecordSpecification& spec ); + + private: + + /// Is the database 'connected'? + /// Delegated to RalSessionMgr. + /// [NB Note the difference between 'open' and 'connected': the database + /// is 'connected' if the connection to the database backend has been + /// established; it is 'open' if the management table has been read]. + /// PURE VIRTUAL method implemented in subclasses. + virtual bool isConnected() const = 0; + + /// (Re)connect to the database. + /// Delegated to RalSessionMgr. + /// PURE VIRTUAL method implemented in subclasses. + virtual void connect() = 0; + + /// Disconnect from the database. + /// Delegated to RalSessionMgr. + /// PURE VIRTUAL method implemented in subclasses. + virtual void disconnect() = 0; + + private: + + /// Standard constructor is private + RelationalDatabase(); + + /// Copy constructor is private + RelationalDatabase( const RelationalDatabase& rhs ); + + /// Assignment operator is private + RelationalDatabase& operator=( const RelationalDatabase& rhs ); + + protected: + + /// Attributes of the database + Record m_dbAttr; + + /// Is the database open? + bool m_isOpen; + + /// Global identifier of the database + RelationalDatabaseId m_relationalDbId; + + private: + + /// CORAL MessageStream + std::auto_ptr<coral::MessageStream> m_log; + + /// RelationalSchemaMgr (owned by this instance) + std::auto_ptr<RelationalSchemaMgr> m_schemaMgr; + + /// RelationalNodeMgr (owned by this instance) + std::auto_ptr<RelationalNodeMgr> m_nodeMgr; + + /// RelationalTagMgr (owned by this instance) + std::auto_ptr<RelationalTagMgr> m_tagMgr; + + /// RelationalObjectMgr (owned by this instance) + std::auto_ptr<RelationalObjectMgr> m_objectMgr; + + /// IRelationalTransactionMgr (shared ownership) + boost::shared_ptr<IRelationalTransactionMgr> m_transactionMgr; + + }; + +} + +#endif // RELATIONALCOOL_RELATIONALDATABASE_H diff --git a/28PATCHES2013/RelationalCool/src/README.txt b/28PATCHES2013/RelationalCool/src/README.txt new file mode 100644 index 0000000000000000000000000000000000000000..5a180dbe2e04f82eedbe685cc1e00b6b6fd8ec8b --- /dev/null +++ b/28PATCHES2013/RelationalCool/src/README.txt @@ -0,0 +1,44 @@ +I screwed up all of the Ral/Rel stuff by committing the ISessionMgr. +The name was totally misleading. It is a RalSessionMgr, I do not understand +why MW decided to use an interface there. + +Note that if everyone gets screwed up,me included, it means that this is +totally unmaintainable as it is. The class structure of COOL is crap. + +My specific problem here is +- I want a shared ptr to a singel instance and this is the ISessionMgr +- However I cannot easily have a shared ptr to ISessionMgr and RalSessionMgr + mixed, in some cases I only need RalSessionMgr + +Short term may be to use private coral ISessionProxy inside +and declare that only some classes (eg RalQueryMgr) see it? +Or I should have used shared from this? + +How much is the extr acomplication I put in each time +to artificially maintain this separation anyway?! + +Honestly, it would all be much simpler and understandable +if yo decide COOL uses CORAL and full stop! +eg remove the RalCursor and RalBulkOperation wrappers.. + +Eventually (soon??!) get rid of class duplication? +Or otherwise find a better structure. + +Start by keeping only ONE version of each class: +- already no RelationalBulkOperation, good +- already no RelationalCursor, good +- what to do with RelationalDatabase? +- already no RelationalDatabaseSvc, good +- drop RelationalQueryMgr (easy) +- already not RelationalSchemaMgr, good +- drop RelationalSequenceMgr (easy) +- already no RelationalSessionMgr, good +- already no RelationalTransactionMgr, good +Then rename the Ral as Coral +And keep the Coral as friends in SessionMgr (cheap but effective) + +Can do it in several times +- First get a single class per name +- Eventually remove all the hassle? (maybe not, if just friendsis what remains) + +Eventually should actually review WHOLE class structure... \ No newline at end of file diff --git a/cmt/USERCONTEXT/avalassi/CMT_userenv.csh b/cmt/USERCONTEXT/avalassi/CMT_userenv.csh new file mode 100644 index 0000000000000000000000000000000000000000..41e99bda17c56dea3fbb8ba2185ec38bb20b402e --- /dev/null +++ b/cmt/USERCONTEXT/avalassi/CMT_userenv.csh @@ -0,0 +1,24 @@ +# $Id: CMT_userenv.csh,v 1.16 2013/05/15 12:03:19 avalassi Exp $ + +# Add LCGCMT patches from /home/avalassi/releases (e.g. if absent on cvmfs) +###setenv CMTPROJECTPATH /home/avalassi/releases:${CMTPROJECTPATH} + +# Special settings for the CORAL3/COOL3 preview branches +# Use a private copy of the latest ROOT6 nightlies +#if ( "$HOST" == "aicoral61.cern.ch" ) then +# setenv CMTPROJECTPATH /home/avalassi/nightlies/dev4/20140610Tue +#else +# echo "ERROR! Invalid host for this USERCONTEXT" +#endif + +# Use the nightlies to debug dev2/dev4 issues on the default gcc48 platform +# Use the nightlies to work around "wrong option -std=c++11" on gcc47 +# Use the nightlies to work around issues with icc13 buids in LCGCMT_68_root6 +#if ( "$HOST" == "aicoral61.cern.ch" ) then +# ###setenv CMTPROJECTPATH /home/avalassi/nightlies/dev2/20140610Tue +# setenv CMTPROJECTPATH /home/avalassi/nightlies/dev4/20140610Tue +# if ( "$CMTCONFIG" == "x86_64-slc6-icc13-dbg" || \ +# "$CMTCONFIG" == "x86_64-slc6-gcc47-opt" ) then +# setenv CMTPROJECTPATH /home/avalassi/nightlies/dev2/Tue +# endif +#endif diff --git a/cmt/USERCONTEXT/avalassi/CMT_userenv.sh b/cmt/USERCONTEXT/avalassi/CMT_userenv.sh new file mode 100644 index 0000000000000000000000000000000000000000..a2f9d903e948fd0ad84bcbd270888385608443c6 --- /dev/null +++ b/cmt/USERCONTEXT/avalassi/CMT_userenv.sh @@ -0,0 +1,6 @@ +# $Id: CMT_userenv.sh,v 1.15 2013/05/15 12:03:19 avalassi Exp $ + +###unset CMTPROJECTPATH; echo CMTPROJECTPATH=$CMTPROJECTPATH +cmtUserContext=`dirname $BASH_SOURCE` +export CMTPROJECTPATH=`tcsh -f -c "source $cmtUserContext/CMT_userenv.csh; echo \\$CMTPROJECTPATH"` +###echo CMTPROJECTPATH=$CMTPROJECTPATH diff --git a/cmt/USERCONTEXT/avalassi/requirements b/cmt/USERCONTEXT/avalassi/requirements new file mode 100644 index 0000000000000000000000000000000000000000..c4fc0630576df4969471dc0f176afe582639035d --- /dev/null +++ b/cmt/USERCONTEXT/avalassi/requirements @@ -0,0 +1,218 @@ +#============================================================================ +# Package CMTUSERCONTEXT - private CMT config for user avalassi +#============================================================================ + +# LCG AA nightly build area base. +###macro lcgaa_nightlies_base $(SITEROOT)/sw/lcg/app/nightlies + +# Internal COOL area with private installations of externals. +# Do not use "/afs/cern.ch/sw/lcg/app/releases/COOL/internal" because pattern +# "path_remove /COOL/" would remove the paths to any external installed there. +###macro cool_internal /afs/cern.ch/sw/lcg/app/cool/internal + +# Internal COOL area with private installations of externals for avalassi. +###macro av_internal $(cool_internal)/avalassi + +# Private local directory for user avalassi +macro av_home /home/avalassi + +# Linux system type (32/64 bit) from fs sysname +###macro fs_sysname 'i386_linux26' host-x86_64 'amd64_linux26' + +#---------------------------------------------------------------------------- + +# LCG AA nightly build area. +###macro lcgaa_nightlies $(lcgaa_nightlies_base)/dev/Wed + +# Private copy of the LCG AA nightly build area. +###macro av_nightlies_dev4 /home/avalassi/nightlies/dev4/20141003Fri + +#---------------------------------------------------------------------------- +# "COOL400" API extensions +#---------------------------------------------------------------------------- + +# Test the COOL400 API extensions only on selected platforms +###macro_append use_cppflags '' \ +### x86_64-slc6-gcc48-dbg ' -DCOOL400' +###macro_append gccxmlopts '' \ +### x86_64-slc6-gcc48-dbg ' -DCOOL400' + +#---------------------------------------------------------------------------- +# LCGCMT and LCG_releases +#---------------------------------------------------------------------------- + +# *** REMINDER ******************************************** +# Remember to set CMTPROJECTPATH in CMT_userenv.bat/csh/sh +# if you want to take LCGCMT from a copy of the nightlies! +# *** REMINDER ******************************************** + +# If you change CMTPROJECTPATH to take LCGCMT from the nightlies or from a +# copy of the nightlies, you must reset LCG_releases to use externals on AFS +# (in the nightlies LCG_releases points to externals in /build/nightlies...). +# If you use a copy of the nightlies, reset LCG_releases here (user context). +# If you use the nightlies directly, consider CMTEXTRATAGS=LCG_NIGHTLIES. + +# General macros after the changes in LCG_Settings (LCG_home no longer exists) +# [e.g. ROOT >=5.34.00 is on lcg/app/releases for both AFS and CVMFS] +# *** NB (APR 2014): THESE MACROS ARE OBSOLETE AND MAY NOT WORK ON >=LCG68! *** +###macro LCG_releases $(SITEROOT)/sw/lcg/app/releases \ +### LOCAL $(SITEROOT)/lcg/app/releases +###macro LCG_external $(SITEROOT)/sw/lcg/external \ +### LOCAL $(SITEROOT)/lcg/external +###macro LCG_external $(SITEROOT)/sw/lcg/experimental \ +### LOCAL $(SITEROOT)/lcg/external + +# Use AFS instead of CVMFS for icc13 +###macro LCG_external $(LCG_external) \ +### target-icc13 /afs/cern.ch/sw/lcg/external + +# Extra tags for root6 (extends LCG_Platforms, needed for LCG_system macro) +###tag x86_64-slc6-gcc48-dbg-root6-dev2 x86_64-slc6-gcc48-dbg-root +###tag x86_64-slc6-gcc48-dbg-root6-test x86_64-slc6-gcc48-dbg-root +###tag x86_64-slc6-gcc48-dbg-root6 x86_64-slc6-gcc48-dbg ROOT_GE_6_00 + +# Experimental setup for new and test platforms +###macro LCG_external $(LCG_external) \ +### LOCAL&x86_64-slc6-gcc48-dbg-root6 $(SITEROOT)/lcg/external \ +### x86_64-slc6-gcc48-dbg-root6 $(SITEROOT)/sw/lcg/experimental + +#---------------------------------------------------------------------------- +# CORAL (for COOL tests inside private COOL builds) +#---------------------------------------------------------------------------- +# NB: redefining CORAL_config_version and CORAL_base is harmless within +# private CORAL builds; CORAL_home however is defined by cmt/project.cmt. + +# Use another CORAL version in the default LCG release area. +###macro CORAL_config_version CORAL_2_4_0 + +# Use a private CORAL build +# NB: the "/CORAL/" path must always be present to allow CMT path cleanup. +###macro CORAL_config_version CORAL_HEAD +macro CORAL_config_version CORAL_23x +macro CORAL_base $(av_home)/CORAL/$(CORAL_config_version) +macro CORAL_home $(CORAL_home) +macro CORAL_include $(CORAL_home)/include + +# Use (a copy of) the CORAL nightlies. +# NB: the "/CORAL/" path must always be present to allow CMT path cleanup. +###macro CORAL_config_version CORAL-preview +###macro CORAL_base $(av_nightlies_dev2)/CORAL/$(CORAL_config_version) +###macro CORAL_home $(CORAL_base)/$(LCG_basesystem)-dbg + +# Use the CORAL nightlies directly. +# NB: the "/CORAL/" path must always be present to allow CMT path cleanup. +###macro CORAL_config_version CORAL-preview +###macro CORAL_base $(lcgaa_nightlies)/CORAL/$(CORAL_config_version) + +# Use opt version of CORAL for dbg builds on cvmfs (dbg is missing on cvmfs) +###macro CORAL_home $(CORAL_home) \ +### LOCAL&target-dbg $(CORAL_base)/$(LCG_basesystem)-opt + +#---------------------------------------------------------------------------- +# ROOT (for COOL tests inside private COOL builds) +#---------------------------------------------------------------------------- +# NB: redefining ROOT macros is harmless within private CORAL builds. + +# Use another ROOT version in the default LCG release area. +###macro ROOT_config_version 5.34.13 + +# Use the ROOT nightlies directly. +# NB: the "/ROOT/" path must always be present to allow CMT path cleanup. +###macro ROOT_config_version ROOT_today +###macro ROOT_base $(lcgaa_nightlies)/ROOT/$(ROOT_config_version) + +# Use ROOT6 beta2 from AFS. +# NB: the "/ROOT/" path must always be present to allow CMT path cleanup. +###macro ROOT_config_version 5.99.05 +###macro ROOT_base /afs/cern.ch/sw/lcg/app/releases/ROOT/$(ROOT_config_version) + +# Use (a copy of) the ROOT6 nightlies. +# NB: the "/ROOT/" path must always be present to allow CMT path cleanup. +###macro ROOT_config_version 6.02.00 +###macro ROOT_base $(av_nightlies_dev4)/ROOT/$(ROOT_config_version) +###macro ROOT_home $(ROOT_base)/$(LCG_basesystem)-dbg/root + +# Use opt version of ROOT for dbg builds where dbg is missing (e.g. cvmfs) +###macro ROOT_home $(ROOT_home) \ +### LOCAL&target-dbg $(ROOT_base)/$(LCG_basesystem)-opt/root + +#---------------------------------------------------------------------------- +# COOL (for CORAL_SERVER tests inside private CORAL builds) +#---------------------------------------------------------------------------- +# NB: redefining COOL_config_version and COOL_base is harmless within +# private COOL builds; COOL_home however is defined by cmt/project.cmt. + +# Use a private COOL installation. +# NB: the "/COOL/" path must always be present to allow CMT path cleanup +# (OK as this is /afs/cern.ch/sw/lcg/app/releases/COOL/internal/avalassi) +macro COOL_config_version COOL_28x +macro COOL_base $(av_home)/$(COOL_config_version) + +#---------------------------------------------------------------------------- +# Oracle 12c tests +#---------------------------------------------------------------------------- + +# Use 12.1.0.1.0 on AFS +###macro oracle_config_version $(oracle_config_version) target-linux 12.1.0.1.0 +###macro oracle_home $(oracle_home) target-linux /afs/cern.ch/sw/lcg/external/oracle/$(oracle_native_version)/$(LCG_system) + +#---------------------------------------------------------------------------- +# Frontier client for CORAL3 (SPI-561) and on mac (SPI-439) +#---------------------------------------------------------------------------- + +# Upgrade Frontier client to 2.8.10 as this is needed by CORAL3 (SPI-561) +###macro Frontier_Client_config_version 2.8.10 +###macro Frontier_Client_home /afs/cern.ch/sw/lcg/external/frontier_client/$(Frontier_Client_native_version)/$(LCG_system) + +# Downgrade Frontier client to 2.8.7 on mac +# NB: CORAL3 requires Frontier client 2.8.10! +###macro Frontier_Client_config_version $(Frontier_Client_config_version) \ +### target-mac 2.8.7 + +# Use the mac106 version on mac108 for gcc42 +###macro Frontier_Client_home $(Frontier_Client_home) \ +### target-mac&target-gcc $(LCG_external)/frontier_client/$(Frontier_Client_native_version)/x86_64-mac106-gcc42-opt + +#---------------------------------------------------------------------------- +# MySQL client on mac (SPI-440) +#---------------------------------------------------------------------------- + +# Downgrade mysql from 5.5.27 while using LCGCMT from the nightlies +###macro mysql_native_version 5.5.14 + +# Use the clang42 version for llvm42 +###macro mysql_home $(mysql_home) \ +### target-mac&target-llvm $(LCG_external)/mysql/$(mysql_native_version)/x86_64-mac108-clang42-opt + +#---------------------------------------------------------------------------- +# Boost on mac (SPI-441) +#---------------------------------------------------------------------------- + +# Use the clang42 version for llvm42 +###macro Boost_home $(Boost_home) \ +### target-mac&target-llvm $(LCG_external)/Boost/$(Boost_native_version)/x86_64-mac108-clang42-opt + +# Fix gcc2icc +###macro gcc2icc $(gcc2icc) target-icc13 gcc48 + +#---------------------------------------------------------------------------- +# QMtest on mac (SPI-442) +#---------------------------------------------------------------------------- + +# Use the clang42 version for gcc42 and llvm42 +###macro QMtest_home $(QMtest_home) \ +### target-mac&target-gcc $(LCG_external)/QMtest/$(QMtest_native_version)/x86_64-mac108-clang42-opt \ +### target-mac&target-llvm $(LCG_external)/QMtest/$(QMtest_native_version)/x86_64-mac108-clang42-opt + +# Use the mac106 version on mac108 +###macro QMtest_home $(QMtest_home) \ +### target-mac $(LCG_external)/QMtest/$(QMtest_native_version)/x86_64-mac106-gcc42-opt + +#---------------------------------------------------------------------------- +# Python +#---------------------------------------------------------------------------- + +# Downgrade python from 2.7.6 while using LCGCMT from the nightlies +###macro Python_native_version 2.7.4 + +#----------------------------------------------------------------------------