persistentstorage/sql/SRC/Server/Compact/SqlCompactEntry.cpp
changeset 0 08ec8eefde2f
child 12 31a8f755b7fe
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/persistentstorage/sql/SRC/Server/Compact/SqlCompactEntry.cpp	Fri Jan 22 11:06:30 2010 +0200
@@ -0,0 +1,281 @@
+// Copyright (c) 2008-2009 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:
+//
+
+#include <e32debug.h>
+#include <hal.h>
+#include <sqldb.h>
+#include "SqlPanic.h"
+#include "SqlCompactEntry.h"
+#include "SqlCompactTimer.h"
+#include "SqliteSymbian.h"		//TSqlFreePageCallback
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/**
+Creates a new CSqlCompactEntry instance.
+
+@param aFullName The full database name, including the path.
+@param aConnFactoryL MSqlCompactConn factory function.
+@param aSettings Background compaction settings/thresholds
+@param aTimer The background compaction timer object
+
+When the free pages threshold reach certain the threshold, the background compaction 
+for this entry will be kicked-off.
+
+@return A pointer to the created CSqlCompactEntry instance
+
+@leave KErrNoMemory, an out of memory condition has occurred;
+                     Note that the function may also leave with some other database specific 
+                     errors categorised as ESqlDbError, and other system-wide error codes.
+
+@panic SqlDb 4 In _DEBUG mode. Too short or too long database name (aFullName parameter)
+@panic SqlDb 4 In _DEBUG mode. NULL aConnFactoryL.
+*/
+CSqlCompactEntry* CSqlCompactEntry::NewLC(const TDesC& aFullName, TSqlCompactConnFactoryL aConnFactoryL, 
+										  const TSqlCompactSettings& aSettings, CSqlCompactTimer& aTimer)
+	{
+	__SQLASSERT(aFullName.Length() > 0 && aFullName.Length() <= KMaxFileName, ESqlPanicBadArgument); 
+	__SQLASSERT(aConnFactoryL != NULL, ESqlPanicBadArgument);
+	CSqlCompactEntry* self = new (ELeave) CSqlCompactEntry(aSettings, aTimer);
+	CleanupStack::PushL(self);
+	self->ConstructL(aFullName, aConnFactoryL);
+	return self;
+	}
+
+/**
+Destroys the CSqlCompactEntry instance. The database connection will be closed.
+*/
+CSqlCompactEntry::~CSqlCompactEntry()
+	{
+	if(iState == CSqlCompactEntry::EInProgress)
+		{
+		iTimer.DeQueue(*this);
+		}
+	if(iConnection)
+		{
+		iConnection->Release();	
+		}
+	iFullName.Close();
+	}
+
+/**
+Increments the entry reference counter.
+
+@return The new reference counter value.
+*/
+TInt CSqlCompactEntry::AddRef()
+	{
+	SQLCOMPACTENTRY_INVARIANT();
+	return ++iRefCounter;
+	}
+
+/**
+Decrements the entry reference counter.
+If the counter reaches zero, the CSqlCompactEntry instance will be destroyed.
+
+@return The new reference counter value.
+*/
+TInt CSqlCompactEntry::Release()
+	{
+	SQLCOMPACTENTRY_INVARIANT();
+	TInt rc = --iRefCounter;
+	if(rc == 0)
+		{
+		delete this;	
+		}
+	return rc;
+	}
+
+/**
+SQLite calls this function when the free pages count reaches the threshold.
+The callback must have been registered at the moment of the database connection creation in order this to happen.
+The callback implementation will schedule a background compaction (kicking-off the timer).
+If a background compaction has already been scheduled, the implementation will only update the iPageCount data
+meber value.
+
+@param aThis A pointer to the CSqlCompactEntry object for which the free page count reached or is above the threshold.
+@param aFreePageCount Free pages count.
+
+@panic SqlDb 4 In _DEBUG mode. NULL aThis parameter.
+@panic SqlDb 4 In _DEBUG mode. aFreePageCount is negative or zero.
+*/
+/* static */ void CSqlCompactEntry::FreePageCallback(void* aThis, TInt aFreePageCount)
+	{
+	__SQLASSERT(aThis != NULL, ESqlPanicBadArgument); 
+	__SQLASSERT(aFreePageCount > 0, ESqlPanicBadArgument); 
+	
+	CSqlCompactEntry& entry = *(static_cast <CSqlCompactEntry*> (aThis));
+	if(entry.iFreePageCallbackDisabled)
+		{//The callback is disabled during the background compaction step.
+		 //The server is single-threaded, so no other client can activate the callback.
+		 //During the background compaction step the callback can be activated only by the completion of the background 
+		 //compaction in which case if "entry.iPageCount" is bigger than the threshold, the page counter will be set from here
+		 //and set second time from CSqlCompactEntry::Compact() - the counter value will be reduced twice.
+		return;	
+		}
+	
+	entry.iPageCount = aFreePageCount;
+	if(entry.iState == CSqlCompactEntry::EInactive)
+		{
+		entry.iState = CSqlCompactEntry::EInProgress;
+		entry.iTimer.Queue(entry);
+		}
+	}
+
+/**
+Initializes the CSqlCompactEntry data members with their default values.
+
+@param aSettings Background compaction settings/thresholds
+*/
+CSqlCompactEntry::CSqlCompactEntry(const TSqlCompactSettings& aSettings, CSqlCompactTimer& aTimer) :
+	iSettings(aSettings),
+	iTimer(aTimer),
+	iRefCounter(1),
+	iState(CSqlCompactEntry::EInactive)
+	{
+	}
+
+/**
+Initializes the created CSqlCompactEntry instance.
+Schedules a background compaction if the free pages count is above the threshold.
+
+@param aFullName The full database name, including the path.
+@param aConnFactoryL MSqlCompactConn factory function.
+
+@panic SqlDb 4 In _DEBUG mode. Too short or too long database name (aFullName parameter)
+@panic SqlDb 4 In _DEBUG mode. NULL aConnFactoryL.
+@panic SqlDb 7 In _DEBUG mode. The CSqlCompactEntry instance has been initialized already.
+*/
+void CSqlCompactEntry::ConstructL(const TDesC& aFullName, TSqlCompactConnFactoryL aConnFactoryL)
+	{
+	__SQLASSERT(aFullName.Length() > 0 && aFullName.Length() <= KMaxFileName, ESqlPanicBadArgument); 
+	__SQLASSERT(aConnFactoryL != NULL, ESqlPanicBadArgument);
+	__SQLASSERT(!iConnection, ESqlPanicInternalError);
+	
+	__SQLLEAVE_IF_ERROR(iFullName.Create(aFullName));
+
+	//The second parameter of TSqlFreePageCallback's constructor is expected the be threshold in pages.
+	//But the connection is not established yet and the page size is not known. Hence the threshold parameter
+	//value is initialized with the threshold in Kbs. The connection construction is expected to convert
+	//the threshold from Kbs to pages when the connection with the database is established.
+	TSqlFreePageCallback callback(this, iSettings.iFreePageThresholdKb, &CSqlCompactEntry::FreePageCallback);
+	iConnection = (*aConnFactoryL)(aFullName, callback);
+	__SQLASSERT(iConnection != NULL, ESqlPanicInternalError);
+	
+	//"callback.iThreshold > 0" is an indication that the background compaction should be kicked-off
+	if(callback.iThreshold > 0) 
+		{
+		//Kick-off the compaction timer, if the number of the free pages is above the threshold.
+		CSqlCompactEntry::FreePageCallback(this, callback.iThreshold);
+		}
+		
+	SQLCOMPACTENTRY_INVARIANT();
+	}
+
+/**
+Performs a compaction step on the database.
+If the number of the free pages is bigger than the number of pages removed in one compaction step,
+the function will reschedule itself for another compaction step.
+
+@return KErrNoMemory, an out of memory condition has occurred;
+                      Note that the function may also return some other database specific 
+                      errors categorised as ESqlDbError, and other system-wide error codes.
+
+@panic SqlDb 7 In _DEBUG mode. iPageCount <= 0 - no free pages to be processed.
+@panic SqlDb 7 In _DEBUG mode. iState != CSqlCompactEntry::EInProgress.
+*/
+TInt CSqlCompactEntry::Compact()
+	{
+	//RDebug::Print(_L("++ CSqlCompactEntry::Compact() ++\r\n"));
+	SQLCOMPACTENTRY_INVARIANT();
+	__SQLASSERT(iPageCount > 0, ESqlPanicInternalError);
+	__SQLASSERT(iState == CSqlCompactEntry::EInProgress, ESqlPanicInternalError);
+	TInt processedPageCount = 0;
+	iFreePageCallbackDisabled = ETrue;
+	TInt err = Connection().Compact(iPageCount, processedPageCount, iSettings.iStepLength);
+	iFreePageCallbackDisabled = EFalse;
+	__SQLASSERT(processedPageCount >= 0, ESqlPanicInternalError);
+	if(err == KErrNone)
+		{
+		if(processedPageCount > 0)
+			{
+			iPageCount -= processedPageCount;
+			}
+		else
+			{
+			iPageCount = 0;
+			}
+		__SQLASSERT(iPageCount >= 0, ESqlPanicInternalError);
+		}
+	if(iPageCount <= 0)
+		{//No more pages to compact. Stop the compacting, move to EInactive state, remove from the timer queue.
+		ResetState();
+		iTimer.DeQueue(*this);
+		}
+	SQLCOMPACTENTRY_INVARIANT();
+	return err;
+	}
+
+/**
+Returns the full database name, including the path.
+
+@return Full database name.
+*/
+const TDesC& CSqlCompactEntry::FullName() const
+	{
+	SQLCOMPACTENTRY_INVARIANT();
+	return iFullName;
+	}
+
+/**
+Resets the CSqlCompactEntry internal state.
+That means - (1) no scheduled compaction step and (2) no pending free pages to be removed.
+*/
+void CSqlCompactEntry::ResetState()
+	{
+	SQLCOMPACTENTRY_INVARIANT();
+	iState = CSqlCompactEntry::EInactive;
+	iPageCount = 0;
+	SQLCOMPACTENTRY_INVARIANT();
+	}
+
+/**
+Returns a reference to the MSqlCompactConn interface.
+@return A reference to the MSqlCompactConn interface.
+
+@panic SqlDb 7 NULL MSqlCompactConn interface.
+*/
+MSqlCompactConn& CSqlCompactEntry::Connection()
+	{
+	SQLCOMPACTENTRY_INVARIANT();
+	__SQLASSERT_ALWAYS(iConnection != NULL, ESqlPanicInternalError);
+	return *iConnection;
+	}
+
+#ifdef _DEBUG
+/**
+CSqlCompactEntry invariant.
+*/
+void CSqlCompactEntry::Invariant() const
+	{
+	__SQLASSERT(iFullName.Length() > 0 && iFullName.Length() <= KMaxFileName, ESqlPanicInternalError); 
+	__SQLASSERT(iConnection != NULL, ESqlPanicInternalError);
+	__SQLASSERT(iRefCounter > 0, ESqlPanicInternalError);
+	__SQLASSERT(iState == CSqlCompactEntry::EInactive || iState == CSqlCompactEntry::EInProgress, ESqlPanicInternalError);
+	__SQLASSERT(iPageCount >= 0, ESqlPanicInternalError);
+	iSettings.Invariant();
+	}
+#endif//_DEBUG