Listing 2
class IDbEnvConfigPolicy { protected: // Constructor is protected because this is a base-class for a singleton. // Constructor takes the most important config data as arguments. // All others parameters are changed via optional 'set' methods. IDbEnvConfigPolicy( const std::string & sHomeDirectoryName, size_t uSizeOfMemoryCache ); public: virtual ~IDbEnvConfigPolicy(); // CDatabaseEnvironment calls these query methods at appropriate time. inline std::string GetHomeDirectoryName() const; inline size_t GetSizeOfMemoryCache() const; // Derived classes can override all these to change defaults. virtual std::string GetDatabaseDirectoryName() const; virtual std::string GetTransactionLogsDirectoryName() const; virtual std::string GetBackingFilesDirectoryName() const; virtual u_int32_t GetDbEnvConstructionFlags() const; // The base-class version of this method applies all of the parameters // captured here. // Derived classes may override if necessary; also call base-class version // if desired. virtual void ConfigureDbEnvBeforeOpen( DbEnv * pDbEnv ); virtual u_int32_t GetDbEnvOpenFlags() const; virtual uint GetMaxNumLockObjects() const; virtual uint GetMaxNumLocks() const; virtual size_t GetMaxSizeOfTransactionLogFile() const; virtual u_int32_t GetAutoDeadlockResolvePolicy() const; virtual void SetLogOutput( ILogFunctor * pLogFunctor ); // ... add more config parameter accessors as neeeded // The database wrapper uses this to log errors during operation. virtual void Log( const std::string & sLog ); private: std::string m_sHomeDirectoryName; size_t m_uSizeOfMemoryCache; ILogFunctor * m_pLogFunctor; // Disable copying and assignment IDbEnvConfigPolicy( const IDbEnvConfigPolicy & ); IDbEnvConfigPolicy & operator=( const IDbEnvConfigPolicy & ); }; typedef CThreadSafeCountedPtr< IDbEnvConfigPolicy > tsrcpDbEnvConfigPolicy_t; // Base class for type-safe DbEnv wrapper singletons (declared immediately // after). This enforces calling DbEnv::close() when the database is // destroyed. class IDatabaseEnvironment { public: virtual ~IDatabaseEnvironment(); inline tsrcpDbEnvConfigPolicy_t GetConfigPolicy() const; // Get a new Db handle in this environment. inline std::auto_ptr< Db > NewDb(); protected: // Constructor is protected as this is a base class for a singleton. explicit IDatabaseEnvironment( tsrcpDbEnvConfigPolicy_t tsrcpDbEnvConfigPolicy ); // Keep a reference open to the config-policy singleton (we need it to // handle logging) tsrcpDbEnvConfigPolicy_t m_tsrcpDbEnvConfigPolicy; std::auto_ptr< CWin32Mutex > m_apRunRecoveryProcessMutex; // Hold DbEnv object by a ptr so wrapper ctor can convert any DbException // that the DbEnv ctor throws (on compilers without support for // function-try blocks) std::auto_ptr< DbEnv > m_apDbEnv; private: // Disable copying and assignment IDatabaseEnvironment( const IDatabaseEnvironment & ); IDatabaseEnvironment & operator=( const IDatabaseEnvironment & ); }; typedef CThreadSafeCountedPtr< IDatabaseEnvironment > tsrcpDbEnvBase_t; // Berkeley DbEnv wrapper (singleton). template< class TConfigPolicy > class CDatabaseEnvironment : public IDatabaseEnvironment { public: // The individual databases may use the DbEnv::ConfigPolicy::Log() method. typedef TConfigPolicy ConfigPolicy_t; // Thread-safe reference counted ptr. typedef CThreadSafeCountedPtr< CDatabaseEnvironment<TConfigPolicy> > tsrcpDbEnv_t; // Singleton accessor. // This takes a command; whether to return a new reference (as expected) // or actually relinquish the one we have. This is to allow the // application to release all references (and so run the destructor) // without closing down completely. enum EInstanceCommand{ eReturnNewRef, eRelinquishInstance }; static tsrcpDbEnv_t Instance( EInstanceCommand eInstanceCommand = eReturnNewRef ); private: // Only access is via Instance() CDatabaseEnvironment(); // Disable copying and assignment CDatabaseEnvironment( const CDatabaseEnvironment & ); CDatabaseEnvironment & operator=( const CDatabaseEnvironment & ); }; // Berkeley DB wrapper (singleton). // This enforces calling Db::close() when the database is destroyed. template< class TSerializableKey, class TSerializableRecord, class TConfigPolicy > class CDatabase { public: typedef TConfigPolicy ConfigPolicy_t; typedef TSerializableKey SerializableKey_t; typedef TSerializableRecord SerializableRecord_t; // The TConfigPolicy class specifies the type of the DbEnv we must use. typedef TConfigPolicy::DbEnv_t DbEnv_t; typedef CTransaction< DbEnv_t > Transaction_t; typedef DbEnv_t::tsrcpDbEnv_t tsrcpDbEnv_t; typedef CThreadSafeCountedPtr< CDatabase<TSerializableKey, TSerializableRecord,TConfigPolicy> > tsrcpDb_t; // Singleton accessor. enum EInstanceCommand{ eReturnNewRef, eRelinquishInstance }; static tsrcpDb_t Instance( EInstanceCommand eInstanceCommand = eReturnNewRef ); ~CDatabase(); // Access functions enum EOverwriteMode{ eAllowOverwrites = 0, eDisallowOverwrites = DB_NOOVERWRITE }; enum EGetLockMode{ eReadOnly = 0, eReadModifyWrite = DB_RMW ; void SerializeAndStoreRecord ( const TSerializableKey & Key, const TSerializableRecord & Record, EOverwriteMode eOverwriteMode, Transaction_t * pOptionalTransaction = 0 ); TSerializableRecord GetRecordBelongingToKey ( const TSerializableKey & Key, Transaction_t * pOptionalTransaction = 0, EGetLockMode eGetLockMode = eReadOnly ); void DeleteRecordBelongingToKey ( const TSerializableKey & Key, Transaction_t * pOptionalTransaction = 0 ); private: // Disallow public construction; access is via Instance() method (singleton). CDatabase(); // We store a ref-counted ptr to the DB environment to hold it open while // DB is open. tsrcpDbEnv_t m_tsrcpDbEnv; // This object owns the Berkeley API 'Db' object returned by // DbEnvWrapper->NewDb(). std::auto_ptr< Db > m_apDb; // Disable copying and assignment. CDatabase( const CDatabase & ); CDatabase & operator=( const CDatabase & ); }; // Cursor wrapper template< class TDatabase > class CCursor { public: typedef TDatabase::tsrcpDb_t tsrcpDb_t; typedef TDatabase::Transaction_t Transaction_t; typedef TDatabase::SerializableKey_t SerializableKey_t; typedef TDatabase::SerializableRecord_t SerializableRecord_t; // When constructed, the cursor has no defined position in the DB. // Its position is determined by the access functions below. inline explicit CCursor( Transaction_t * pOptionalTransaction = 0 ); inline void Close(); inline ~CCursor(); // Copy constructor uses dup() to get a duplicate cursor at the same // position, sharing the same locks. inline CCursor( const CCursor< TDatabase > & other ); // Database access methods (similar to Berkeley DBWrapper functions with // same names). get/put via cursor have a lot of modes for managing // multiple records with the same key. We have no need to support that yet. void SerializeAndOverwriteCurrentRecord( const SerializableRecord_t & Record ); enum EGetLockMode{ eReadOnly = 0, eReadModifyWrite = DB_RMW }; SerializableRecord_t MoveToKeyAndGetRecord ( const SerializableKey_t & Key, EGetLockMode eGetLockMode = eReadOnly ); enum ESetCursorPosition { eFirstRecord, eLastRecord, eCurrentRecord, eNextRecord, ePreviousRecord }; SerializableRecord_t MoveToPositionAndGetKeyAndRecord ( ESetCursorPosition eSetCursorPosition, SerializableKey_t * pOptionalReceiveCurrentKey = 0, EGetLockMode eGetLockMode = eReadOnly ); void DeleteCurrentRecord(); private: tsrcpDb_t m_tsrcpDb; Transaction_t * m_pOptionalTransaction; Dbc * m_pDbc; // Disable assignment. Copying is allowed but assignment is not needed. CCursor & operator=( const CCursor & ); // Disable array new and delete. static void * operator new[]( size_t nSize ); static void operator delete[]( void *pArray ); };