sl@0: // Copyright (c) 2008-2010 Nokia Corporation and/or its subsidiary(-ies). sl@0: // All rights reserved. sl@0: // This component and the accompanying materials are made available sl@0: // under the terms of "Eclipse Public License v1.0" sl@0: // which accompanies this distribution, and is available sl@0: // at the URL "http://www.eclipse.org/legal/epl-v10.html". sl@0: // sl@0: // Initial Contributors: sl@0: // Nokia Corporation - initial contribution. sl@0: // sl@0: // Contributors: sl@0: // sl@0: // Description: sl@0: // sl@0: sl@0: #include sl@0: #include sl@0: #include sl@0: #include "SqlAssert.h" sl@0: #include "SqlCompactEntry.h" sl@0: #include "SqlCompactTimer.h" sl@0: #include "SqliteSymbian.h" //TSqlFreePageCallback sl@0: #include "OstTraceDefinitions.h" sl@0: #ifdef OST_TRACE_COMPILER_IN_USE sl@0: #include "SqlCompactEntryTraces.h" sl@0: #endif sl@0: #include "SqlTraceDef.h" sl@0: sl@0: //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// sl@0: //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// sl@0: sl@0: /** sl@0: Creates a new CSqlCompactEntry instance. sl@0: sl@0: @param aFullName The full database name, including the path. sl@0: @param aConnFactoryL MSqlCompactConn factory function. sl@0: @param aSettings Background compaction settings/thresholds sl@0: @param aTimer The background compaction timer object sl@0: sl@0: When the free pages threshold is reached, the background compaction sl@0: for this entry will be kicked-off. sl@0: sl@0: @return A pointer to the created CSqlCompactEntry instance sl@0: sl@0: @leave KErrNoMemory, an out of memory condition has occurred; sl@0: Note that the function may also leave with some other database specific sl@0: errors categorised as ESqlDbError, and other system-wide error codes. sl@0: sl@0: @panic SqlDb 4 In _DEBUG mode. Too short or too long database name (aFullName parameter) sl@0: @panic SqlDb 4 In _DEBUG mode. NULL aConnFactoryL. sl@0: */ sl@0: CSqlCompactEntry* CSqlCompactEntry::NewLC(const TDesC& aFullName, TSqlCompactConnFactoryL aConnFactoryL, sl@0: const TSqlCompactSettings& aSettings, CSqlCompactTimer& aTimer) sl@0: { sl@0: SQL_TRACE_COMPACT(OstTraceExt1(TRACE_INTERNALS, CSQLCOMPACTENTRY_NEWLC_ENTRY, "Entry;0;CSqlCompactEntry::NewLC;aFullName=%S", __SQLPRNSTR(aFullName))); sl@0: __ASSERT_DEBUG(aFullName.Length() > 0 && aFullName.Length() <= KMaxFileName, __SQLPANIC2(ESqlPanicBadArgument)); sl@0: __ASSERT_DEBUG(aConnFactoryL != NULL, __SQLPANIC2(ESqlPanicBadArgument)); sl@0: CSqlCompactEntry* self = new (ELeave) CSqlCompactEntry(aSettings, aTimer); sl@0: CleanupStack::PushL(self); sl@0: self->ConstructL(aFullName, aConnFactoryL); sl@0: SQL_TRACE_COMPACT(OstTrace1(TRACE_INTERNALS, CSQLCOMPACTENTRY_NEWLC_EXIT, "Exit;0x%X;CSqlCompactEntry::NewLC", (TUint)self)); sl@0: return self; sl@0: } sl@0: sl@0: /** sl@0: Destroys the CSqlCompactEntry instance. The database connection will be closed. sl@0: */ sl@0: CSqlCompactEntry::~CSqlCompactEntry() sl@0: { sl@0: SQL_TRACE_COMPACT(OstTraceExt3(TRACE_INTERNALS, CSQLCOMPACTENTRY_CSQLCOMPACTENTRY2, "0x%X;CSqlCompactEntry::~CSqlCompactEntry;iState=%d;iPageCount=%d", (TUint)this, (TInt)iState, iPageCount)); sl@0: if(iState == CSqlCompactEntry::EInProgress) sl@0: { sl@0: iTimer.DeQueue(*this); sl@0: } sl@0: if(iConnection) sl@0: { sl@0: iConnection->Release(); sl@0: } sl@0: iFullName.Close(); sl@0: } sl@0: sl@0: /** sl@0: Increments the entry reference counter. sl@0: sl@0: @return The new reference counter value. sl@0: */ sl@0: TInt CSqlCompactEntry::AddRef() sl@0: { sl@0: SQL_TRACE_COMPACT(OstTraceExt4(TRACE_INTERNALS, CSQLCOMPACTENTRY_ADDREF, "0x%X;CSqlCompactEntry::AddRef;iState=%d;iPageCount=%d;iRefCounter=%d", (TUint)this, (TInt)iState, iPageCount, iRefCounter)); sl@0: SQLCOMPACTENTRY_INVARIANT(); sl@0: return ++iRefCounter; sl@0: } sl@0: sl@0: /** sl@0: Decrements the entry reference counter. sl@0: If the counter reaches zero, the CSqlCompactEntry instance will be destroyed. sl@0: sl@0: @return The new reference counter value. sl@0: */ sl@0: TInt CSqlCompactEntry::Release() sl@0: { sl@0: SQL_TRACE_COMPACT(OstTraceExt4(TRACE_INTERNALS, CSQLCOMPACTENTRY_RELEASE, "0x%X;CSqlCompactEntry::Release;iState=%d;iPageCount=%d;iRefCounter=%d", (TUint)this, (TInt)iState, iPageCount, iRefCounter)); sl@0: SQLCOMPACTENTRY_INVARIANT(); sl@0: TInt rc = --iRefCounter; sl@0: if(rc == 0) sl@0: { sl@0: delete this; sl@0: } sl@0: return rc; sl@0: } sl@0: sl@0: /** sl@0: SQLite calls this function when the free pages count reaches the threshold. sl@0: The callback must have been registered at the moment of the database connection creation in order this to happen. sl@0: The callback implementation will schedule a background compaction (kicking-off the timer). sl@0: If a background compaction has already been scheduled, the implementation will only update the iPageCount data sl@0: meber value. sl@0: sl@0: @param aThis A pointer to the CSqlCompactEntry object for which the free page count reached or is above the threshold. sl@0: @param aFreePageCount Free pages count. sl@0: sl@0: @panic SqlDb 4 In _DEBUG mode. NULL aThis parameter. sl@0: @panic SqlDb 4 In _DEBUG mode. aFreePageCount is negative or zero. sl@0: */ sl@0: /* static */ void CSqlCompactEntry::FreePageCallback(void* aThis, TInt aFreePageCount) sl@0: { sl@0: __ASSERT_DEBUG(aThis != NULL, __SQLPANIC2(ESqlPanicBadArgument)); sl@0: __ASSERT_DEBUG(aFreePageCount > 0, __SQLPANIC2(ESqlPanicBadArgument)); sl@0: sl@0: CSqlCompactEntry& entry = *(static_cast (aThis)); sl@0: SQL_TRACE_COMPACT(OstTraceExt3(TRACE_INTERNALS, CSQLCOMPACTENTRY_FREEPAGECALLBACK, "0x%X;CSqlCompactEntry::FreePageCallback;aFreePageCount=%d;iState=%d", (TUint)aThis, aFreePageCount, (TInt)entry.iState)); sl@0: if(entry.iFreePageCallbackDisabled) sl@0: {//The callback is disabled during the background compaction step. sl@0: //The server is single-threaded, so no other client can activate the callback. sl@0: //During the background compaction step the callback can be activated only by the completion of the background sl@0: //compaction in which case if "entry.iPageCount" is bigger than the threshold, the page counter will be set from here sl@0: //and set second time from CSqlCompactEntry::Compact() - the counter value will be reduced twice. sl@0: return; sl@0: } sl@0: sl@0: entry.iPageCount = aFreePageCount; sl@0: if(entry.iState == CSqlCompactEntry::EInactive) sl@0: { sl@0: entry.iState = CSqlCompactEntry::EInProgress; sl@0: entry.iTimer.Queue(entry); sl@0: } sl@0: } sl@0: sl@0: /** sl@0: Initializes the CSqlCompactEntry data members with their default values. sl@0: sl@0: @param aSettings Background compaction settings/thresholds sl@0: */ sl@0: CSqlCompactEntry::CSqlCompactEntry(const TSqlCompactSettings& aSettings, CSqlCompactTimer& aTimer) : sl@0: iSettings(aSettings), sl@0: iTimer(aTimer), sl@0: iRefCounter(1), sl@0: iState(CSqlCompactEntry::EInactive) sl@0: { sl@0: } sl@0: sl@0: /** sl@0: Initializes the created CSqlCompactEntry instance. sl@0: Schedules a background compaction if the free pages count is above the threshold. sl@0: sl@0: @param aFullName The full database name, including the path. sl@0: @param aConnFactoryL MSqlCompactConn factory function. sl@0: sl@0: @panic SqlDb 4 In _DEBUG mode. Too short or too long database name (aFullName parameter) sl@0: @panic SqlDb 4 In _DEBUG mode. NULL aConnFactoryL. sl@0: @panic SqlDb 7 In _DEBUG mode. The CSqlCompactEntry instance has been initialized already. sl@0: */ sl@0: void CSqlCompactEntry::ConstructL(const TDesC& aFullName, TSqlCompactConnFactoryL aConnFactoryL) sl@0: { sl@0: __ASSERT_DEBUG(aFullName.Length() > 0 && aFullName.Length() <= KMaxFileName, __SQLPANIC(ESqlPanicBadArgument)); sl@0: __ASSERT_DEBUG(aConnFactoryL != NULL, __SQLPANIC(ESqlPanicBadArgument)); sl@0: __ASSERT_DEBUG(!iConnection, __SQLPANIC(ESqlPanicInternalError)); sl@0: sl@0: __SQLLEAVE_IF_ERROR(iFullName.Create(aFullName)); sl@0: sl@0: //The second parameter of TSqlFreePageCallback's constructor is expected the be threshold in pages. sl@0: //But the connection is not established yet and the page size is not known. Hence the threshold parameter sl@0: //value is initialized with the threshold in Kbs. The connection construction is expected to convert sl@0: //the threshold from Kbs to pages when the connection with the database is established. sl@0: TSqlFreePageCallback callback(this, iSettings.iFreePageThresholdKb, &CSqlCompactEntry::FreePageCallback); sl@0: iConnection = (*aConnFactoryL)(aFullName, callback); sl@0: __ASSERT_DEBUG(iConnection != NULL, __SQLPANIC(ESqlPanicInternalError)); sl@0: sl@0: //"callback.iThreshold > 0" is an indication that the background compaction should be kicked-off sl@0: if(callback.iThreshold > 0) sl@0: { sl@0: //Kick-off the compaction timer, if the number of the free pages is above the threshold. sl@0: CSqlCompactEntry::FreePageCallback(this, callback.iThreshold); sl@0: } sl@0: sl@0: SQLCOMPACTENTRY_INVARIANT(); sl@0: } sl@0: sl@0: /** sl@0: Performs a compaction step on the database. sl@0: If the number of the free pages is bigger than the number of pages removed in one compaction step, sl@0: the function will reschedule itself for another compaction step. sl@0: If the database file is corrupted, then the function will remove the database entry from the timer queue - sl@0: the database won't be compacted anymore. sl@0: sl@0: @return KErrNoMemory, an out of memory condition has occurred; sl@0: Note that the function may also return some other database specific sl@0: errors categorised as ESqlDbError, and other system-wide error codes. sl@0: sl@0: @panic SqlDb 7 In _DEBUG mode. iPageCount <= 0 - no free pages to be processed. sl@0: @panic SqlDb 7 In _DEBUG mode. iState != CSqlCompactEntry::EInProgress. sl@0: */ sl@0: TInt CSqlCompactEntry::Compact() sl@0: { sl@0: SQL_TRACE_COMPACT(OstTraceExt3(TRACE_INTERNALS, CSQLCOMPACTENTRY_COMPACT_ENTRY, "Entry;0x%X;CSqlCompactEntry::Compact;aFreePageCount=%d;iState=%d", (TUint)this, iPageCount, (TInt)iState)); sl@0: SQLCOMPACTENTRY_INVARIANT(); sl@0: __ASSERT_DEBUG(iPageCount > 0, __SQLPANIC(ESqlPanicInternalError)); sl@0: __ASSERT_DEBUG(iState == CSqlCompactEntry::EInProgress, __SQLPANIC(ESqlPanicInternalError)); sl@0: TInt processedPageCount = 0; sl@0: iFreePageCallbackDisabled = ETrue; sl@0: TInt err = Connection().Compact(iPageCount, processedPageCount, iSettings.iStepLength); sl@0: iFreePageCallbackDisabled = EFalse; sl@0: __ASSERT_DEBUG(processedPageCount >= 0, __SQLPANIC(ESqlPanicInternalError)); sl@0: if(err == KErrNone) sl@0: { sl@0: iPageCount -= processedPageCount; sl@0: if(processedPageCount == 0) sl@0: { sl@0: iPageCount = 0; sl@0: } sl@0: __ASSERT_DEBUG(iPageCount >= 0, __SQLPANIC(ESqlPanicInternalError)); sl@0: } sl@0: TBool stopCompaction = err == KSqlErrCorrupt || err == KSqlErrNotDb || err == KErrCorrupt || err == KErrDisMounted; sl@0: if(iPageCount <= 0 || stopCompaction) sl@0: {//No more pages to compact or the file is corrupted . Stop the compacting, move to EInactive state, remove from the timer queue. sl@0: ResetState(); sl@0: iTimer.DeQueue(*this); sl@0: } sl@0: SQL_TRACE_COMPACT(OstTraceExt4(TRACE_INTERNALS, CSQLCOMPACTENTRY_COMPACT_EXIT, "Exit;0x%X;CSqlCompactEntry::Compact;iPageCount=%d;iState=%d;err=%d", (TUint)this, iPageCount, (TInt)iState, err)); sl@0: SQLCOMPACTENTRY_INVARIANT(); sl@0: return err; sl@0: } sl@0: sl@0: /** sl@0: Returns the full database name, including the path. sl@0: sl@0: @return Full database name. sl@0: */ sl@0: const TDesC& CSqlCompactEntry::FullName() const sl@0: { sl@0: SQLCOMPACTENTRY_INVARIANT(); sl@0: return iFullName; sl@0: } sl@0: sl@0: /** sl@0: Resets the CSqlCompactEntry internal state. sl@0: That means - (1) no scheduled compaction step and (2) no pending free pages to be removed. sl@0: */ sl@0: void CSqlCompactEntry::ResetState() sl@0: { sl@0: SQLCOMPACTENTRY_INVARIANT(); sl@0: iState = CSqlCompactEntry::EInactive; sl@0: iPageCount = 0; sl@0: SQLCOMPACTENTRY_INVARIANT(); sl@0: } sl@0: sl@0: /** sl@0: Returns a reference to the MSqlCompactConn interface. sl@0: @return A reference to the MSqlCompactConn interface. sl@0: sl@0: @panic SqlDb 7 NULL MSqlCompactConn interface. sl@0: */ sl@0: MSqlCompactConn& CSqlCompactEntry::Connection() sl@0: { sl@0: SQLCOMPACTENTRY_INVARIANT(); sl@0: __ASSERT_ALWAYS(iConnection != NULL, __SQLPANIC(ESqlPanicInternalError)); sl@0: return *iConnection; sl@0: } sl@0: sl@0: #ifdef _DEBUG sl@0: /** sl@0: CSqlCompactEntry invariant. sl@0: */ sl@0: void CSqlCompactEntry::Invariant() const sl@0: { sl@0: __ASSERT_DEBUG(iFullName.Length() > 0 && iFullName.Length() <= KMaxFileName, __SQLPANIC(ESqlPanicInternalError)); sl@0: __ASSERT_DEBUG(iConnection != NULL, __SQLPANIC(ESqlPanicInternalError)); sl@0: __ASSERT_DEBUG(iRefCounter > 0, __SQLPANIC(ESqlPanicInternalError)); sl@0: __ASSERT_DEBUG(iState == CSqlCompactEntry::EInactive || iState == CSqlCompactEntry::EInProgress, __SQLPANIC(ESqlPanicInternalError)); sl@0: __ASSERT_DEBUG(iPageCount >= 0, __SQLPANIC(ESqlPanicInternalError)); sl@0: iSettings.Invariant(); sl@0: } sl@0: #endif//_DEBUG