--- /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<TUint32>& 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<TUint32>& 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