diff -r cad71a31b7fc -r e36f3802f733 srsf/sisrscontrollerplugin/src/sicommondb.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/srsf/sisrscontrollerplugin/src/sicommondb.cpp Wed Sep 01 12:29:17 2010 +0100 @@ -0,0 +1,604 @@ +/* +* Copyright (c) 2004-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: This class is the baseclass for all SI Controller Plugin DB +* classes. It implements the common database functionalities. +* +*/ + + + +// INCLUDE FILES +#include "sicommondb.h" +#include "rubydebug.h" + +// CONSTANTS + +// Name of the DB lock mutex +_LIT( KLockMutex, "SIGRAMMAR" ); + +// ============================ MEMBER FUNCTIONS =============================== + +// ----------------------------------------------------------------------------- +// CSICommonDB::CSICommonDB +// C++ default constructor can NOT contain any code, that +// might leave. +// ----------------------------------------------------------------------------- +// +CSICommonDB::CSICommonDB( RDbNamedDatabase& aDatabase, + RDbs& aDbSession, TInt aDrive ) +: iDb( aDatabase ), + iDbSession( aDbSession ), + iDrive( aDrive ) + { + + TInt err = iMutex.OpenGlobal( KLockMutex ); + if ( err != KErrNone ) + { + RUBY_DEBUG0( "CSICommonDB::CSICommonDB Creating new global mutex" ); + iMutex.CreateGlobal( KLockMutex ); + } + else + { + RUBY_DEBUG0( "CSICommonDB::CSICommonDB Using existing global mutex" ); + } + + } + +// ----------------------------------------------------------------------------- +// CSICommonDB::CSICommonDB +// Destructor +// ----------------------------------------------------------------------------- +// +CSICommonDB::~CSICommonDB() + { + if ( iMutex.IsHeld() ) + { + iMutex.Signal(); + } + iMutex.Close(); + } + +// ----------------------------------------------------------------------------- +// CSICommonDB::CheckIDTableL +// Each of controller's database files has an ID table. If the database has no +// table, this is the first time the database is open, so the ID table is created. +// ----------------------------------------------------------------------------- +// +void CSICommonDB::CreateIDTableL( const TDesC& aIdTable, + const TDesC& aIdColumn, + const TDesC& aIndex ) + { + RUBY_DEBUG_BLOCK( "CSICommonDB::CheckIDTableL - create ID table" ); + + TBuf<150> KSQLStatement; + // Declare a literal string to hold the SQL statement + // CREATE TABLE aIdTable (aIdColumn COUNTER, KClientUidColumn INTEGER NOT NULL, + // KCounterColumn INTEGER NOT NULL, KUsedColumn BIT NOT NULL) + _LIT(KSQLCreate1, "CREATE TABLE "); + _LIT(KSQLCreate2, " COUNTER, "); + _LIT(KSQLCreate3, " INTEGER NOT NULL, "); + _LIT(KSQLCreate4, " BIT NOT NULL)"); + + KSQLStatement = KSQLCreate1; + KSQLStatement.Append(aIdTable); + KSQLStatement.Append(KOpenParen); + KSQLStatement.Append(aIdColumn); + KSQLStatement.Append(KSQLCreate2); + KSQLStatement.Append(KClientUidColumn); + KSQLStatement.Append(KSQLCreate3); + KSQLStatement.Append(KCounterColumn); + KSQLStatement.Append(KSQLCreate3); + KSQLStatement.Append(KUsedColumn); + KSQLStatement.Append(KSQLCreate4); + + User::LeaveIfError(iDb.Execute(KSQLStatement)); + + // Create an index on the 'Id' column to ensure 'Id' values are unique + // CREATE UNIQUE INDEX aIndex ON aIdTable (aIdColumn) + _LIT(KSQLIndex1, "CREATE UNIQUE INDEX "); + _LIT(KSQLIndex2, " ON "); + + KSQLStatement = KSQLIndex1; + KSQLStatement.Append(aIndex); + KSQLStatement.Append(KSQLIndex2); + KSQLStatement.Append(aIdTable); + KSQLStatement.Append(KOpenParen); + KSQLStatement.Append(aIdColumn); + KSQLStatement.Append(KCloseParen); + + User::LeaveIfError(iDb.Execute(KSQLStatement)); + } + +// ----------------------------------------------------------------------------- +// CSICommonDB::CreateNewIDL +// This function first checks to see if there is an "unused" ID. If one is not +// found, it creates a new row in the table for a new ID. +// ----------------------------------------------------------------------------- +// +TUint32 CSICommonDB::CreateNewIDL( + const TDesC& aTableName, + const TDesC& aIdColumn, + TUid aClientUid ) + { + RUBY_DEBUG_BLOCKL( "CSICommonDB::CreateNewIDL" ); + + TUint32 newID( 0 ); + + iMutex.Wait(); + + TRAPD( error, newID = DoCreateNewIDL( aTableName, aIdColumn, aClientUid ) ); + + iMutex.Signal(); + + User::LeaveIfError( error ); + + return newID; + } + +// ----------------------------------------------------------------------------- +// CSICommonDB::CountL +// This function first searches the row with aKey and returns the counter value +// given that the row is set as "used". +// ----------------------------------------------------------------------------- +// +TInt CSICommonDB::CountL( const TDesC& aTableName, const TDesC& aIndex, + TUint aKey ) + { + RUBY_DEBUG_BLOCK( "CSICommonDB::CountL" ); + RDbTable dbTable; // Provides access to table data as a rowset + TInt count = 0; + + // Open model table + User::LeaveIfError(dbTable.Open(iDb, aTableName, RDbTable::EReadOnly)); + CleanupClosePushL(dbTable); + User::LeaveIfError(dbTable.SetIndex(aIndex)); + + TDbSeekKey key(aKey); + // Return true, if a matching row found + if ( dbTable.SeekL(key) ) + { + dbTable.GetL(); + // Get column set for column ordinals + CDbColSet* columns = dbTable.ColSetL(); + TDbColNo counter_col = columns->ColNo(KCounterColumn); + TDbColNo used_col = columns->ColNo(KUsedColumn); + delete columns; + + // Check if this row is currently in use + TBool used = dbTable.ColInt(used_col); + if ( used ) + { + count = dbTable.ColInt(counter_col); + } + else + { + RUBY_DEBUG1( "CSICommonDB::CountL - key=%d is unused", aKey ); + User::Leave(KErrNotFound); + } + } + else + { + RUBY_DEBUG1( "CSICommonDB::CountL - can't find key=%d", aKey ); + User::Leave(KErrNotFound); + } + + // Cleanup dbTable + CleanupStack::PopAndDestroy(); + return count; + } + +// ----------------------------------------------------------------------------- +// CSICommonDB::CreateDatabaseL +// Creates a database file if one does not exist yet. +// ----------------------------------------------------------------------------- +// +TBool CSICommonDB::FindUnusedIDL( + RDbTable& aDbTable ) + { + TBuf<30> KSQLQuery; + // Declare a literal string to hold the SQL search-condition for 'unused' row + // KUsedColumn = KNotUsed + KSQLQuery = KUsedColumn; + KSQLQuery.Append(KEqual); + KSQLQuery.AppendNum(KNotUsed); + + // Return true, if the table is not empty and a matching row found + return (aDbTable.FirstL() && aDbTable.FindL(RDbTable::EForwards, TDbQuery(KSQLQuery)) != KErrNotFound); + } + +// ----------------------------------------------------------------------------- +// CSICommonDB::GetAllClientIDsL +// This function returns all Ids in the specified table that belong to the specified client. +// ----------------------------------------------------------------------------- +// +void CSICommonDB::GetAllClientIDsL( + const TDesC& aIdTable, + const TDesC& aIdColumn, + TUid aClientUid, + RArray& aIDs ) + { + CleanupClosePushL( aIDs ); + + TBuf<100> KSQLStatement; + // Declare a literal string to hold the SQL statement + // SELECT aIdColumn, KUsedColumn FROM aIdTable WHERE KClientUidColumn = uid + // ORDER BY aIdColumn + _LIT(KSQLSelect1, "SELECT "); + _LIT(KSQLSelect2, " FROM "); + _LIT(KSQLSelect3, " WHERE "); + _LIT(KSQLSelect4, " ORDER BY "); + + KSQLStatement = KSQLSelect1; + KSQLStatement.Append(aIdColumn); + KSQLStatement.Append(KNext); + KSQLStatement.Append(KUsedColumn); + KSQLStatement.Append(KSQLSelect2); + KSQLStatement.Append(aIdTable); + KSQLStatement.Append(KSQLSelect3); + KSQLStatement.Append(KClientUidColumn); + KSQLStatement.Append(KEqual); + KSQLStatement.AppendNum((TInt) aClientUid.iUid); + KSQLStatement.Append(KSQLSelect4); + KSQLStatement.Append(aIdColumn); + + RDbView view; + CleanupClosePushL(view); + User::LeaveIfError(view.Prepare(iDb, TDbQuery(KSQLStatement, EDbCompareNormal))); + User::LeaveIfError(view.EvaluateAll()); + + // Get column set for column ordinals + CDbColSet* columns = view.ColSetL(); + TDbColNo id_col = columns->ColNo(aIdColumn); + TDbColNo used_col = columns->ColNo(KUsedColumn); + delete columns; + + TUint32 id; + TBool used; + // After evaluation, the first call to NextL() is equivalent to FirstL() + while ( view.NextL() ) + { + // Retrieve the current row + view.GetL(); + // Check if this row is currently in use + used = view.ColInt(used_col); + if ( used ) + { + id = view.ColUint32(id_col); + User::LeaveIfError(aIDs.Append(id)); + } + } + + // Cleanup view + CleanupStack::PopAndDestroy(); + CleanupStack::Pop(); + } + +// ----------------------------------------------------------------------------- +// CSICommonDB::GetAllIDsL +// This function returns all Ids in the specified table. +// ----------------------------------------------------------------------------- +// +void CSICommonDB::GetAllIDsL( + const TDesC& aIdTable, + const TDesC& aIdColumn, + RArray& aIDs ) + { + CleanupClosePushL( aIDs ); + + TBuf<100> KSQLStatement; + // Declare a literal string to hold the SQL statement + // SELECT aIdColumn FROM aIdTable WHERE KUsedColumn = KUsed + // ORDER BY aIdColumn + _LIT(KSQLSelect1, "SELECT "); + _LIT(KSQLSelect2, " FROM "); + _LIT(KSQLSelect3, " WHERE "); + _LIT(KSQLSelect4, " ORDER BY "); + + KSQLStatement = KSQLSelect1; + KSQLStatement.Append(aIdColumn); + KSQLStatement.Append(KSQLSelect2); + KSQLStatement.Append(aIdTable); + KSQLStatement.Append(KSQLSelect3); + KSQLStatement.Append(KUsedColumn); + KSQLStatement.Append(KEqual); + KSQLStatement.AppendNum(KUsed); + KSQLStatement.Append(KSQLSelect4); + KSQLStatement.Append(aIdColumn); + + RDbView view; + CleanupClosePushL(view); + User::LeaveIfError(view.Prepare(iDb, TDbQuery(KSQLStatement, EDbCompareNormal))); + User::LeaveIfError(view.EvaluateAll()); + + // Get column set for column ordinals + CDbColSet* columns = view.ColSetL(); + TDbColNo id_col = columns->ColNo(aIdColumn); + delete columns; + + TUint32 id; + // After evaluation, the first call to NextL() is equivalent to FirstL() + while ( view.NextL() ) + { + // Retrieve the current row + view.GetL(); + id = view.ColUint32(id_col); + User::LeaveIfError(aIDs.Append(id)); + } + + // Cleanup view + CleanupStack::PopAndDestroy(); + CleanupStack::Pop(); + } + +// ----------------------------------------------------------------------------- +// CSICommonDB::IsValidL +// Checks to see if aKey exists in the table and that it's set to "used". +// ----------------------------------------------------------------------------- +// +TBool CSICommonDB::IsValidL( const TDesC& aTableName, const TDesC& aIndex, + TUint aKey ) + { + RUBY_DEBUG_BLOCK( "CSICommonDB::IsValidL" ); + RDbTable dbTable; // Provides access to table data as a rowset + TBool valid = EFalse; + + // Open table + User::LeaveIfError(dbTable.Open(iDb, aTableName, RDbTable::EReadOnly)); + CleanupClosePushL(dbTable); + User::LeaveIfError(dbTable.SetIndex(aIndex)); + + TDbSeekKey key(aKey); + // Return true, if a matching row found + if ( dbTable.SeekL(key) ) + { + RUBY_DEBUG1( "CSICommonDB::IsValidL - %d is found", aKey ); + dbTable.GetL(); + // Get column set for column ordinals + CDbColSet* columns = dbTable.ColSetL(); + TDbColNo used_col = columns->ColNo(KUsedColumn); + delete columns; + + // Check if this row is currently in use + valid = dbTable.ColInt(used_col); + } + + // Cleanup dbTable + CleanupStack::PopAndDestroy(); + return valid; + } + +// ----------------------------------------------------------------------------- +// CSICommonDB::ReleaseIdL +// Releases the ID by marking it as "unused". +// ----------------------------------------------------------------------------- +// +void CSICommonDB::ReleaseIdL( + const TDesC& aTableName, + const TDesC& aIdColumn, + TUint32 aId ) + { + RUBY_DEBUG_BLOCKL( "CSICommonDB::ReleaseIdL" ); + + iMutex.Wait(); + + TRAPD( error, DoReleaseIdL( aTableName, aIdColumn, aId ) ); + + iMutex.Signal(); + + User::LeaveIfError( error ); + } + +// ----------------------------------------------------------------------------- +// CSICommonDB::UpdateCounterL +// Updates the counter value of the specified row. +// ----------------------------------------------------------------------------- +// +void CSICommonDB::UpdateCounterL( const TDesC& aTableName, const TDesC& aIndex, + TUint aKey, TBool aIncrement ) + { + RUBY_DEBUG_BLOCK( "CSICommonDB::UpdateCounterL" ); + RDbTable dbTable; // Provides access to table data as a rowset + + // Open table + User::LeaveIfError(dbTable.Open(iDb, aTableName, RDbTable::EUpdatable)); + CleanupClosePushL(dbTable); + User::LeaveIfError(dbTable.SetIndex(aIndex)); + + TDbSeekKey key(aKey); + // Return true, if a matching row found + if ( dbTable.SeekL(key) ) + { + dbTable.GetL(); + // Get column set for column ordinals + CDbColSet* columns = dbTable.ColSetL(); + TDbColNo counter_col = columns->ColNo(KCounterColumn); + delete columns; + + TInt counter = dbTable.ColInt(counter_col); + + if ( aIncrement ) + { + counter++; + } + else + { + counter--; + } + // Update found row + dbTable.UpdateL(); + dbTable.SetColL(counter_col, counter); + + // Write the updated row + TRAPD(err, dbTable.PutL()); + if( err != KErrNone ) + { + // Error: cancel update + dbTable.Cancel(); + dbTable.Reset(); + User::Leave(err); + } + } + else + { + RUBY_DEBUG1( "CSICommonDB::UpdateCounterL - can't find key=%d", aKey ); + User::Leave(KErrNotFound); + } + + // Cleanup dbTable + CleanupStack::PopAndDestroy(); + } + +// ----------------------------------------------------------------------------- +// CSICommonDB::VerifyOwnershipL +// Checks for data ownership. +// ----------------------------------------------------------------------------- +// +void CSICommonDB::VerifyOwnershipL( TUid aClientUid, const TDesC& aTableName, + const TDesC& aIndex, TUint aKey ) + { + RUBY_DEBUG_BLOCK( "CSICommonDB::VerifyOwnershipL" ); + RDbTable dbTable; // Provides access to table data as a rowset + + // Open table + User::LeaveIfError(dbTable.Open(iDb, aTableName, RDbTable::EReadOnly)); + CleanupClosePushL(dbTable); + User::LeaveIfError(dbTable.SetIndex(aIndex)); + + TDbSeekKey key(aKey); + // Return true, if a matching row found + if ( dbTable.SeekL(key) ) + { + RUBY_DEBUG1( "CSICommonDB::VerifyOwnershipL - %d is found", aKey ); + dbTable.GetL(); + // Get column set for column ordinals + CDbColSet* columns = dbTable.ColSetL(); + TDbColNo client_uid_col = columns->ColNo(KClientUidColumn); + TDbColNo used_col = columns->ColNo(KUsedColumn); + delete columns; + + // Check if this row is currently in use + TBool used = dbTable.ColInt(used_col); + if ( used ) + { + if ( dbTable.ColInt(client_uid_col) != aClientUid.iUid ) + { + User::Leave(KErrAsrDataRightViolation); + } + } + else + { + RUBY_DEBUG1( "CSICommonDB::VerifyOwnershipL - key=%d is unused", aKey ); + User::Leave(KErrNotFound); + } + } + else + { + RUBY_DEBUG1( "CSICommonDB::VerifyOwnershipL - can't find key=%d", aKey ); + User::Leave(KErrNotFound); + } + + // Cleanup dbTable + CleanupStack::PopAndDestroy(); + } + +// ----------------------------------------------------------------------------- +// CSICommonDB::DoReleaseIdL +// Releases the ID by marking it as "unused". +// ----------------------------------------------------------------------------- +// +void CSICommonDB::DoReleaseIdL( + const TDesC& aTableName, + const TDesC& aIdColumn, + TUint32 aId ) + { + TBuf<100> KSQLStatement; + // Declare a literal string to hold the SQL statement + // UPDATE aTableName SET KUsedColumn = KNotUsed WHERE aIdColumn = aId + _LIT(KSQLUpdate1, "UPDATE "); + _LIT(KSQLUpdate2, " SET "); + _LIT(KSQLUpdate3, " WHERE "); + + KSQLStatement = KSQLUpdate1; + KSQLStatement.Append(aTableName); + KSQLStatement.Append(KSQLUpdate2); + KSQLStatement.Append(KUsedColumn); + KSQLStatement.Append(KEqual); + KSQLStatement.AppendNum(KNotUsed); + KSQLStatement.Append(KSQLUpdate3); + KSQLStatement.Append(aIdColumn); + KSQLStatement.Append(KEqual); + KSQLStatement.AppendNumUC(aId); + + User::LeaveIfError(iDb.Execute(KSQLStatement)); + } + +// ----------------------------------------------------------------------------- +// CSICommonDB::DoCreateNewIDL +// This function first checks to see if there is an "unused" ID. If one is not +// found, it creates a new row in the table for a new ID. +// ----------------------------------------------------------------------------- +// +TUint32 CSICommonDB::DoCreateNewIDL( + const TDesC& aTableName, + const TDesC& aIdColumn, + TUid aClientUid ) + { + RDbTable dbTable; // Provides access to table data as a rowset + + // Open table + User::LeaveIfError(dbTable.Open(iDb, aTableName, RDbTable::EUpdatable)); + CleanupClosePushL(dbTable); + + // Get column set for column ordinals + CDbColSet* columns = dbTable.ColSetL(); + TDbColNo id_col = columns->ColNo(aIdColumn); + TDbColNo uid_col = columns->ColNo(KClientUidColumn); + TDbColNo counter_col = columns->ColNo(KCounterColumn); + TDbColNo used_col = columns->ColNo(KUsedColumn); + delete columns; + + // Is there an unused ID? + if( FindUnusedIDL(dbTable) ) + { + // Yes, update found row + dbTable.UpdateL(); + } + else + { + // No, insert new row + dbTable.InsertL(); + } + + dbTable.SetColL(uid_col, aClientUid.iUid); + dbTable.SetColL(counter_col, 0); + dbTable.SetColL(used_col, KUsed); + + // Write the updated row + TRAPD(err, dbTable.PutL()); + if( err != KErrNone ) + { + // Error: cancel update + dbTable.Cancel(); + dbTable.Reset(); + User::Leave(err); + } + + TUint32 newID = dbTable.ColUint32(id_col); + // Cleanup dbTable + CleanupStack::PopAndDestroy(); + return newID; + } + + +// End of File