--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/emailservices/emailstore/message_store/server/src/ContainerStoreUtils.cpp Thu Dec 17 08:39:21 2009 +0200
@@ -0,0 +1,798 @@
+/*
+* Copyright (c) 2006 Nokia Corporation and/or its subsidiary(-ies).
+* All rights reserved.
+* This component and the accompanying materials are made available
+* under the terms of "Eclipse Public License v1.0"
+* which accompanies this distribution, and is available
+* at the URL "http://www.eclipse.org/legal/epl-v10.html".
+*
+* Initial Contributors:
+* Nokia Corporation - initial contribution.
+*
+* Contributors:
+*
+* Description: Container store utils implementation.
+*
+*/
+
+
+
+// ========
+// INCLUDES
+// ========
+
+#include "ContainerStoreUtils.h"
+#include "ContainerStoreDeleteHandler.h"
+
+#include <sysutil.h>
+#include <d32dbms.h> // database
+#include <bautils.h>
+
+//#include <s32crypt.h>
+
+// =============
+// LOCAL CLASSES
+// =============
+
+// =============================================================================
+// CLASS: CCompactionHandler
+//
+// This class performs database compaction operations incrementally using the
+// RDbIncremental class. Each active object invocation performs one step of the
+// compaction.
+//
+// The Symbian documentation explains that certain operations cannot be performed
+// on the database while an incremental operation is in progress. The Suspend
+// and Resume functions are provided to suspend incremental operations until the
+// database operation is completed.
+//
+// The wonderful Symbian documentation doesn't give a list of the functions that
+// do not work during incremental operations. In order to determine this, the
+// code was temporarily modified to always have an incremental operation in
+// progress, unless Suspend and Resume has been called. The unit tester were
+// executed, and each operation that had a problem was then wrapped with
+// Suspend/Resume, and the code was then returned to its original state.
+// =============================================================================
+class CCompactionHandler : public CActive
+ {
+ public:
+
+ // ==============
+ // PUBLIC METHODS
+ // ==============
+
+ static CCompactionHandler* NewL( RDbDatabase& aDatabase,
+ TInt aPriority );
+
+ virtual ~CCompactionHandler();
+
+ void Compact();
+
+ void Suspend();
+
+ void SuspendLC();
+
+ void Resume( TBool aPopCleanupItem = ETrue );
+
+ void FinishCompactionL();
+
+ private:
+
+ // ===============
+ // PRIVATE METHODS
+ // ===============
+
+ CCompactionHandler( RDbDatabase& aDatabase,
+ TInt aPriority );
+
+ void ConstructL();
+
+ void Reschedule();
+
+ // inherited from CActive
+ virtual void RunL();
+ virtual void DoCancel();
+
+ RTimer iDelayTimer;
+ RDbDatabase& iDatabase;
+ RDbIncremental iIncremental;
+ TInt iStep;
+
+ TBool iCompactionNeeded;
+ TInt iSuspendCount;
+
+ __LOG_DECLARATION
+
+ }; // end class CCompactionHandler
+
+// ======================
+// METHOD IMPLEMENTATIONS
+// ======================
+
+// ==========================================================================
+// FUNCTION: NewL
+// ==========================================================================
+CContainerStoreUtils* CContainerStoreUtils::NewL( TDriveNumber aDriveNumber,
+ TInt aCompactionPriority,
+ const TDesC& aDbFilename,
+ CDeleteHandler& aDeleteHandler )
+ {
+ CContainerStoreUtils* self = new(ELeave) CContainerStoreUtils( aDriveNumber,
+ aCompactionPriority,
+ aDbFilename,
+ aDeleteHandler );
+ CleanupStack::PushL( self );
+ self->ConstructL();
+ CleanupStack::Pop( self );
+ return self;
+ } // end NewL
+
+// ==========================================================================
+// FUNCTION: Constructor
+// ==========================================================================
+CContainerStoreUtils::CContainerStoreUtils( TDriveNumber aDriveNumber,
+ TInt aCompactionPriority,
+ const TDesC& aDbFilename,
+ CDeleteHandler& aDeleteHandler ) :
+ iDriveNumber( aDriveNumber ),
+ iCompactionPriority( aCompactionPriority ),
+ iDbFilename( aDbFilename ),
+ iDeleteHandler( aDeleteHandler )
+ {
+#ifdef _DEBUG
+ iLowDiskSpaceLatency = -1;
+#endif
+ } // end constructor
+
+// ==========================================================================
+// FUNCTION: ConstructL
+// ==========================================================================
+void CContainerStoreUtils::ConstructL()
+ {
+ __LOG_CONSTRUCT( "msg", "CContainerStoreUtils" )
+
+ User::LeaveIfError( iFs.Connect() );
+
+ iFs.CreatePrivatePath( iDriveNumber );
+ iFs.SetSessionToPrivate( iDriveNumber );
+
+ iFs.PrivatePath( iPrivatePath );
+ const TUint bufSize = 3;
+ TBuf<bufSize> driveLetter;
+ _LIT( KFormatString, "%C:" );
+ driveLetter.Format( KFormatString, (iDriveNumber - EDriveA + 'A' ) );
+ iPrivatePath.Insert( 0, driveLetter );
+
+ } // end ConstructL
+
+// ==========================================================================
+// FUNCTION: Destructor
+// ==========================================================================
+CContainerStoreUtils::~CContainerStoreUtils()
+ {
+ delete iCompactionHandler;
+
+ iTables.Close();
+
+ iDatabase.Close();
+
+ delete iFileStore;
+
+ iFs.Close();
+
+ __LOG_DESTRUCT
+ } // end destructor
+
+// ==========================================================================
+// FUNCTION: LeaveIfFalseL
+// ==========================================================================
+void CContainerStoreUtils::LeaveIfFalse( TBool aCondition, TInt aLeaveCode )
+{
+ if ( !aCondition )
+ {
+ User::Leave( aLeaveCode );
+ }
+}
+
+
+// ==========================================================================
+// FUNCTION: FileSystem
+// ==========================================================================
+RFs& CContainerStoreUtils::FileSystem()
+ {
+ return iFs;
+ } // end FileSystem
+
+// ==========================================================================
+// FUNCTION: CreateDatabaseL
+// ==========================================================================
+void CContainerStoreUtils::CreateDatabaseL()
+ {
+ __LOG_ENTER( "CreateDatabaseL" );
+
+ // Create the file store.
+ iFileStore = CPermanentFileStore::ReplaceL( iFs, iDbFilename, EFileRead|EFileWrite );
+
+ iFileStore->SetTypeL( iFileStore->Layout() );
+
+ // Create a database within the file store.
+ TStreamId id = iDatabase.CreateL( iFileStore );
+
+ // Set the database as the file store root object.
+ iFileStore->SetRootL( id );
+
+ // Commit the database.
+ iFileStore->CommitL();
+
+ iCompactionHandler = CCompactionHandler::NewL( iDatabase, iCompactionPriority );
+
+ __LOG_EXIT
+ } // end CreateDatabaseL
+
+// ==========================================================================
+// FUNCTION: OpenDatabaseL
+// ==========================================================================
+void CContainerStoreUtils::OpenDatabaseL()
+ {
+ __LOG_ENTER( "OpenDatabaseL" )
+
+ // construct a file store object
+ iFileStore = CPermanentFileStore::OpenL( iFs, iDbFilename, EFileRead|EFileWrite );
+
+ // open database from the root of the store
+ iDatabase.OpenL( iFileStore, iFileStore->Root() );
+
+ if( iDatabase.IsDamaged() )
+ {
+ __LOG_WRITE_ERROR( "Recovering database" );
+ User::LeaveIfError( iDatabase.Recover() );
+ } // end if
+
+ iCompactionHandler = CCompactionHandler::NewL( iDatabase, iCompactionPriority );
+ iCompactionHandler->Compact();
+
+ __LOG_EXIT
+ } // end OpenDatabaseL
+
+// ==========================================================================
+// FUNCTION: CloseDatabaseL
+// ==========================================================================
+void CContainerStoreUtils::CloseDatabaseL()
+ {
+ __LOG_ENTER( "CloseDatabaseL" )
+
+ // Finish compaction, if necessary.
+ if( iCompactionHandler )
+ {
+ iCompactionHandler->FinishCompactionL();
+ } // end if
+
+ CloseDatabase();
+
+ __LOG_EXIT
+ } // end CloseDatabaseL
+
+// ==========================================================================
+// FUNCTION: CloseDatabase
+// ==========================================================================
+void CContainerStoreUtils::CloseDatabase()
+ {
+ __LOG_ENTER( "CloseDatabase" )
+
+ delete iCompactionHandler;
+ iCompactionHandler = NULL;
+
+ iDatabase.Close();
+
+ delete iFileStore;
+ iFileStore = NULL;
+
+ __LOG_EXIT
+ } // end CloseDatabase
+
+// ==========================================================================
+// FUNCTION: PopulateViewL
+// ==========================================================================
+void CContainerStoreUtils::PopulateViewL( RDbView& aView, const TDesC& aSqlQuery, RDbRowSet::TAccess aAccess )
+ {
+ __LOG_ENTER( "PopulateViewL" )
+
+ User::LeaveIfError( aView.Prepare( iDatabase, TDbQuery(aSqlQuery), aAccess ) );
+
+ // Evaluate the view (i.e. populate it based on the SQL query).
+ User::LeaveIfError( aView.EvaluateAll() );
+
+ __LOG_EXIT
+ } // end PopulateViewL
+
+// ==========================================================================
+// FUNCTION: Execute
+// ==========================================================================
+void CContainerStoreUtils::Execute( const TDesC &aSql, TDbTextComparison aComparison )
+ {
+ __LOG_ENTER( "Execute" )
+
+ iDatabase.Execute( aSql, aComparison );
+
+ __LOG_EXIT
+ }
+
+
+// ==========================================================================
+// FUNCTION: CreateTableL
+// ==========================================================================
+void CContainerStoreUtils::CreateTableL( const TDesC& aTableName, CDbColSet& aColSet )
+ {
+ User::LeaveIfError( iDatabase.CreateTable( aTableName, aColSet ) );
+ } // end CreateTableL
+
+// ==========================================================================
+// FUNCTION: CreateIndexL
+// ==========================================================================
+void CContainerStoreUtils::CreateIndexL( const TDesC& aName, const TDesC& aTable, const CDbKey& aKey )
+ {
+ User::LeaveIfError( iDatabase.CreateIndex( aName, aTable, aKey ) );
+ } // end CreateIndexL
+
+// ==========================================================================
+// FUNCTION: OpenTableL
+// ==========================================================================
+void CContainerStoreUtils::OpenTableL( RDbTable& aTable, const TDesC& aTableName )
+ {
+ User::LeaveIfError( aTable.Open( iDatabase, aTableName ) );
+
+ iTables.AppendL( &aTable );
+
+ } // end OpenTableL
+
+// ==========================================================================
+// FUNCTION: CloseTable
+// ==========================================================================
+void CContainerStoreUtils::CloseTable( RDbTable& aTable )
+ {
+ aTable.Close();
+
+ TInt index = iTables.Find( &aTable );
+ if( index != KErrNotFound )
+ {
+ iTables.Remove( index );
+ } // end if
+
+ } // end CloseTable
+
+// ==========================================================================
+// FUNCTION: ReadLongColumnL
+// ==========================================================================
+void CContainerStoreUtils::ReadLongColumnL( RDbRowSet& aRowSet,
+ TUint aColNum,
+ RBuf8& aBuffer,
+ TUint aBufferPadSpace )
+ {
+ __LOG_ENTER_SUPPRESS( "ReadLongColumnL" )
+
+ iCompactionHandler->SuspendLC();
+
+ RDbColReadStream readStream;
+
+ TUint colLength = aRowSet.ColLength( aColNum );
+
+ TUint desiredLength = colLength + aBufferPadSpace;
+
+ if( aBuffer.MaxLength() < desiredLength )
+ {
+ __LOG_WRITE_INFO( "growing buffer" )
+ aBuffer.ReAllocL( desiredLength );
+ } // end if
+
+ aBuffer.SetLength( 0 );
+ readStream.OpenLC( aRowSet, aColNum );
+ readStream.ReadL( aBuffer, colLength );
+
+ CleanupStack::PopAndDestroy( &readStream );
+
+ iCompactionHandler->Resume();
+
+ } // end ReadLongColumnL
+
+// ==========================================================================
+// FUNCTION: WriteLongColumnL
+// ==========================================================================
+void CContainerStoreUtils::WriteLongColumnL( RDbRowSet& aRowSet,
+ TUint aColNum,
+ const TDesC8& aBuffer )
+ {
+ __LOG_ENTER( "WriteLongColumnL" )
+
+ if ( aBuffer.Length() > 0 )
+ {
+ RDbColWriteStream writeStream;
+
+ LeaveIfLowDiskSpaceL( aBuffer.Length() );
+
+ writeStream.OpenLC( aRowSet, aColNum );
+ writeStream.WriteL( aBuffer );
+ writeStream.CommitL();
+
+ CleanupStack::PopAndDestroy( &writeStream );
+ }
+ else
+ {
+ aRowSet.SetColNullL( aColNum );
+ }
+ __LOG_EXIT
+ } // end WriteLongColumnL
+
+// ==========================================================================
+// FUNCTION: LeaveIfLowDiskSpaceL
+// ==========================================================================
+void CContainerStoreUtils::LeaveIfLowDiskSpaceL( TUint aBytesToWrite )
+ {
+ __LOG_ENTER_SUPPRESS( "LeaveIfLowDiskSpaceL" )
+
+ // This safety margin is to account for some level of uncertainty in aBytesToWrite due to overhead
+ // in the database, file system, encryption, etc.
+ const TUint KSafetyMargin = 8*1024;
+
+ __LOG_WRITE8_FORMAT1_INFO( "LeaveIfLowDiskSpace(%i)", aBytesToWrite )
+
+ TBool belowCritical = SysUtil::DiskSpaceBelowCriticalLevelL( &iFs, aBytesToWrite+KSafetyMargin, iDriveNumber );
+
+#ifdef _DEBUG
+ TBool belowCriticalOverride = EFalse;
+ if( iLowDiskSpaceLatency > -1 )
+ {
+ belowCriticalOverride = (iLowDiskSpaceLatency == 0);
+ belowCritical = belowCritical || belowCriticalOverride;
+ iLowDiskSpaceLatency--;
+ } // end if
+#endif
+
+ if( belowCritical )
+ {
+ // Force the deletes and database compaction to complete in the foreground. This may take
+ // a while, but may free up enough space to fix this condition.
+
+ // : It was found that the following call will fail if a database row update is in progress (calls
+ // such as FirstL assert in those cases). The cleanup mechanism would need to be refined in order
+ // to avoid that situation.
+ // iDeleteHandler.FinishDeletes();
+
+ iCompactionHandler->FinishCompactionL();
+
+ belowCritical = SysUtil::DiskSpaceBelowCriticalLevelL( &iFs, aBytesToWrite+KSafetyMargin, iDriveNumber );
+
+#ifdef _DEBUG
+ belowCritical = belowCritical || belowCriticalOverride;
+#endif
+
+ } // end if
+
+ if( belowCritical )
+ {
+ __LOG_WRITE_ERROR( "below critical disk space!" );
+ User::Leave( KErrNoMemory );
+ } // end if
+
+ } // end LeaveIfLowDiskSpaceL
+
+// ==========================================================================
+// FUNCTION: PrivatePath
+// ==========================================================================
+const TDesC& CContainerStoreUtils::PrivatePath() const
+ {
+ return iPrivatePath;
+ } // end PrivatePath
+
+// ==========================================================================
+// FUNCTION: BeginDatabaseTransactionLC
+// ==========================================================================
+void CContainerStoreUtils::BeginDatabaseTransactionLC()
+ {
+ __LOG_ENTER( "BeginDatabaseTransactionL" )
+
+ // A database transaction cannot be started while an incremental operation is in progress, so
+ // suspend it now.
+ iCompactionHandler->Suspend();
+
+ TUint result = iDatabase.Begin();
+
+ if( result != KErrNone )
+ {
+ __LOG_WRITE8_FORMAT1_ERROR( "failed, err=%i", result );
+ User::Leave( result );
+ } // end if
+
+ // Push an item on the cleanup stack taht will rollback the database transaction
+ // if something fails during the transaction.
+ CleanupStack::PushL(TCleanupItem(&RollbackDatabaseTransactionL, this) );
+
+ __LOG_EXIT
+ } // end BeginDatabaseTransactionL
+
+// ==========================================================================
+// FUNCTION: CommitDatabaseTransactionL
+// ==========================================================================
+void CContainerStoreUtils::CommitDatabaseTransactionL()
+ {
+ __LOG_ENTER( "CommitDatabaseTransactionL" )
+
+ User::LeaveIfError( iDatabase.Commit() );
+
+ // Pop the rollback item.
+ CleanupStack::Pop( this );
+
+ iCompactionHandler->Resume( EFalse );
+
+ iCompactionHandler->Compact();
+
+ __LOG_EXIT
+ } // end CommitDatabaseTransactionL
+
+// ==========================================================================
+// FUNCTION: RollbackDatabaseTransactionL
+// ==========================================================================
+void CContainerStoreUtils::RollbackDatabaseTransactionL()
+ {
+ __LOG_ENTER( "RollbackDatabaseTransaction" )
+
+ iDatabase.Rollback();
+
+ // Rollbacks can damage the database indexes. This will fix them.
+ User::LeaveIfError( iDatabase.Recover() );
+
+ // All rowsets must be reset after a Rollback, otherwise row functions will leave with
+ // KErrNotReady.
+ for( TInt index = 0; index < iTables.Count(); index++ )
+ {
+ iTables[index]->Reset();
+ } // end for
+
+ iCompactionHandler->Resume( EFalse );
+ iCompactionHandler->Compact();
+
+ __LOG_EXIT
+ } // end RollbackDatabaseTransactionL
+
+// ==========================================================================
+// FUNCTION: RollbackDatabaseTransactionL
+// ==========================================================================
+void CContainerStoreUtils::RollbackDatabaseTransactionL( TAny* aObject )
+ {
+ __LOG_STATIC_ENTER( "msg", "RollbackDatabaseTransactionL" )
+
+ CContainerStoreUtils* utils = reinterpret_cast<CContainerStoreUtils*>(aObject);
+ utils->RollbackDatabaseTransactionL();
+
+ __LOG_STATIC_EXIT
+ } // end RollbackDatabaseTransaction
+
+// ==========================================================================
+// FUNCTION: SuspendCompactionLC
+// ==========================================================================
+void CContainerStoreUtils::SuspendCompactionLC()
+ {
+ iCompactionHandler->SuspendLC();
+ }
+
+// ==========================================================================
+// FUNCTION: ResumeCompaction
+// ==========================================================================
+void CContainerStoreUtils::ResumeCompaction()
+ {
+ iCompactionHandler->Resume();
+ }
+
+// ==========================================================================
+// FUNCTION: FindFirstEncryptedOrUnencryptedL
+// ==========================================================================
+TBool CContainerStoreUtils::FindFirstEncryptedOrUnencryptedL( RDbTable& aTable,
+ const TDesC& aColName,
+ TBool aFindEncrypted,
+ TDbBookmark& aBookmark )
+ {
+ TBool found = EFalse;
+
+ if ( !aTable.IsEmptyL() )
+ {
+ const TUint querrySize=60;
+ TBuf<querrySize> queryString;
+
+ _LIT( KEquals, "=" );
+
+ queryString.Copy( aColName );
+ queryString.Append( KEquals );
+ queryString.AppendNum( aFindEncrypted );
+
+ if ( aTable.FirstL() )
+ {
+ found = aTable.FindL( RDbRowSet::EForwards, queryString ) != KErrNotFound ;
+
+ if( found )
+ {
+ aBookmark = aTable.Bookmark( );
+ } // end if
+ }
+ }
+
+ return found;
+ }
+
+// ------------------
+// CCompactionHandler
+// ------------------
+
+// Used to make sure that compaction occurs at most once every 10 seconds. This protects lower priority clients
+// from being shut out during many sequential operations (initial sync, for example).
+const TUint KInitialCompactionDelay = 10000000;
+
+CCompactionHandler* CCompactionHandler::NewL( RDbDatabase& aDatabase,
+ TInt aPriority )
+ {
+ CCompactionHandler* self = new(ELeave) CCompactionHandler( aDatabase, aPriority );
+ CleanupStack::PushL( self );
+ self->ConstructL();
+ CleanupStack::Pop( self );
+ return self;
+ }
+
+CCompactionHandler::CCompactionHandler( RDbDatabase& aDatabase,
+ TInt aPriority ) :
+ CActive( aPriority ),
+ iDatabase( aDatabase )
+ {
+ __LOG_CONSTRUCT( "msg", "CCompactionHandler" )
+
+ CActiveScheduler::Add(this);
+ }
+
+void CCompactionHandler::ConstructL()
+ {
+ User::LeaveIfError( iDelayTimer.CreateLocal() );
+ }
+
+CCompactionHandler::~CCompactionHandler()
+ {
+ Cancel();
+
+ iIncremental.Close();
+
+ iDelayTimer.Close();
+
+ __LOG_DESTRUCT
+ }
+
+void CCompactionHandler::Compact()
+ {
+ __LOG_ENTER_SUPPRESS( "Compact" )
+
+ iCompactionNeeded = ETrue;
+
+ if( (iSuspendCount == 0) && !IsActive() )
+ {
+ __LOG_WRITE_INFO( "starting compaction" );
+ iStep = 0;
+
+ iDelayTimer.After( iStatus, KInitialCompactionDelay );
+ SetActive();
+ } // end if
+ }
+
+static void ResumeCompaction( TAny* aObject )
+ {
+ __LOG_STATIC_ENTER( "msg", "ResumeCompactionL" )
+
+ CCompactionHandler* handler = reinterpret_cast<CCompactionHandler*>(aObject);
+ handler->Resume( EFalse ); // do not pop the cleanup item
+
+ __LOG_STATIC_EXIT
+ } // end RollbackDatabaseTransaction
+
+void CCompactionHandler::Suspend()
+ {
+ __LOG_ENTER_SUPPRESS( "Suspend" )
+
+ if( iSuspendCount == 0 )
+ {
+ __LOG_WRITE_DEBUG3( "suspending compaction" )
+
+ Cancel();
+ iIncremental.Close();
+
+ } // end if
+
+ iSuspendCount++;
+ }
+
+void CCompactionHandler::SuspendLC()
+ {
+ Suspend();
+
+ // Push an item on the cleanup stack taht will resume compaction if something leaves.
+ CleanupStack::PushL(TCleanupItem(&ResumeCompaction, this) );
+ }
+
+void CCompactionHandler::Resume( TBool aPopCleanupItem )
+ {
+ __LOG_ENTER_SUPPRESS( "Resume" )
+
+ iSuspendCount--;
+
+ if( iCompactionNeeded && (iSuspendCount == 0) )
+ {
+ __LOG_WRITE_INFO( "resuming compaction" );
+ Compact();
+ } // end if
+
+ if( aPopCleanupItem )
+ {
+ // Pop the resume item.
+ CleanupStack::Pop( this );
+ } // end if
+ }
+
+void CCompactionHandler::FinishCompactionL()
+ {
+ __LOG_ENTER( "FinishCompactionL" )
+
+ SuspendLC();
+
+ // Call the synchronous compaction.
+ iDatabase.Compact();
+ iCompactionNeeded = EFalse;
+
+ Resume();
+
+ __LOG_EXIT
+ }
+
+void CCompactionHandler::Reschedule()
+ {
+ SetActive();
+ TRequestStatus* status = &iStatus;
+ User::RequestComplete( status, KErrNone );
+ }
+
+void CCompactionHandler::RunL()
+ {
+ __LOG_ENTER_SUPPRESS( "RunL" )
+
+ if( iStep == 0 )
+ {
+ iIncremental.Compact( iDatabase, iStep );
+ }
+ else
+ {
+ iIncremental.Next( iStep );
+ } // end if
+
+ if( iStep == 0 )
+ {
+ __LOG_WRITE_INFO( "Compaction completed" )
+
+ iCompactionNeeded = EFalse;
+ iIncremental.Close();
+ }
+ else
+ {
+ Reschedule();
+ } // end if
+ }
+
+void CCompactionHandler::DoCancel()
+ {
+ // The timer is only active on the first step.
+ if( iStep == 0 )
+ {
+ iDelayTimer.Cancel();
+ } // end if
+ } // end DoCancel
+
+// FUNCTIONS TO SUPPORT AUTOMATED UNIT TESTING
+
+#ifdef _DEBUG
+void CContainerStoreUtils::SimulateLowDiskSpace( TInt aLatency )
+ {
+ iLowDiskSpaceLatency = aLatency;
+ }
+#endif
+