srsf/sisrscontrollerplugin/src/sidatabase.cpp
author Pat Downey <patd@symbian.org>
Wed, 01 Sep 2010 12:29:17 +0100
branchRCL_3
changeset 19 e36f3802f733
parent 0 bf1d17376201
permissions -rw-r--r--
Revert incorrect RCL_3 drop: Revision: 201033 Kit: 201035

/*
* 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 implements the SI Controller Plugin database.
*
*/


// INCLUDE FILES
#include "sidatabase.h"
#include "rubydebug.h"
#include "nssbackupobserver.h"


// CONSTANTS
// Estimate size in bytes for an empty database
// const TInt KSizeEstimateDb = 650;

// Plugin database format string for secure DBMS
_LIT( KPluginDatabaseFormatString, "SECURE[10201AFF]" );

// Name of the DB lock mutex
_LIT( KLockMutex, "SIDATABASE" );

// ============================ MEMBER FUNCTIONS ===============================

// -----------------------------------------------------------------------------
// CSIDatabase::CSIDatabase
// C++ default constructor can NOT contain any code, that
// might leave.
// -----------------------------------------------------------------------------
//
CSIDatabase::CSIDatabase( TFileName aDatabaseFileName )
                        : iOpenTransaction( EFalse ),
                          iDatabaseFileName( aDatabaseFileName ),
                          iDatabaseOpen( EFalse )
    {
    // Nothing
    }

// -----------------------------------------------------------------------------
// CSIDatabase::ConstructL
// Symbian 2nd phase constructor can leave.
// -----------------------------------------------------------------------------
//
void CSIDatabase::ConstructL()
    {
    RUBY_DEBUG_BLOCKL( "CSIDatabase::ConstructL" );
    // Checks to see if the Database already exists.  If not, it creates one.
    User::LeaveIfError( iDbSession.Connect() );
    CreateDatabaseL();
    OpenDatabaseL();
    
    TInt err = iMutex.OpenGlobal( KLockMutex );
    if ( err != KErrNone )
        {
        RUBY_DEBUG0( "CSIDatabase::ConstructL Creating new global mutex" );
        iMutex.CreateGlobal( KLockMutex );
        }
    else
        {
        RUBY_DEBUG0( "CSIDatabase::ConstructL Using existing global mutex" );
        }
        
    iCriticalSection.CreateLocal();
    iBackupObserver = CNssBackupObserver::NewL( *this );
    
    }

// -----------------------------------------------------------------------------
// CSIDatabase::NewL
// Two-phased constructor.
// -----------------------------------------------------------------------------
//
CSIDatabase* CSIDatabase::NewL( TFileName aSDDatabaseFileName )
    {
    RUBY_DEBUG_BLOCKL( "CSIDatabase::NewL" );
    
    CSIDatabase* self = new( ELeave ) CSIDatabase( aSDDatabaseFileName );
    CleanupStack::PushL( self );
    self->ConstructL();
    CleanupStack::Pop();
    
    return self;
    }

// -----------------------------------------------------------------------------
// CSIDatabase::Rollback
// Rollback all changes to the database. 
// -----------------------------------------------------------------------------
//
void CSIDatabase::Rollback()
    {
    RUBY_DEBUG0( "CSIDatabase::Rollback" );
    if ( iOpenTransaction )
        {
        iDb.Rollback();
        // Try to recover the database, if it is damaged
        if( iDb.IsDamaged() )
            {
            iDb.Recover();
            }
        iMutex.Signal();
        iCriticalSection.Signal();
        iOpenTransaction = EFalse;
        }
    
    }

// -----------------------------------------------------------------------------
// CSIDatabase::~CSIDatabase
// Destructor 
// -----------------------------------------------------------------------------
//
CSIDatabase::~CSIDatabase()
    {
    // Rollback if there are any changes not commited
    if ( iOpenTransaction )
        {
        iDb.Rollback();
        // Try to recover the database, if it is damaged
        if( iDb.IsDamaged() )
            {
            iDb.Recover();
            }
        // Signal mutex if somebody happens to be waiting in BeginTransactionL()
        iMutex.Signal();
        }
        
    delete iBackupObserver;
    iCriticalSection.Close();

    iDb.Close();
    iDbSession.Close();
    iDatabaseOpen = EFalse;
    }

// -----------------------------------------------------------------------------
// CSIDatabase::BeginTransactionL
// Begins a database transaction if not already. Waits until exclusive write lock
// has been gained.
// -----------------------------------------------------------------------------
//
void CSIDatabase::BeginTransactionL()
    {
   

    if ( iOpenTransaction )
        {
        // If we already have the lock, no need to take it again
        RUBY_DEBUG0( "CSIDatabase::BeginTransactionL DB is already locked by current process" );
        return;
        }
    
    // Take mutex to ensure that only one process tries to gain the lock
    iMutex.Wait();
    iCriticalSection.Wait();
    
    // Taking the global mutex makes it sure that this should succeed every time
    TInt readLockErr = iDb.Begin();

    if ( readLockErr == KErrNone )
        {
        iOpenTransaction = ETrue;
        }
    }

// -----------------------------------------------------------------------------
// CSIDatabase::CommitChangesL
// Commits all changes to the database.
// -----------------------------------------------------------------------------
//
void CSIDatabase::CommitChangesL( TBool aCompact )
    {
    if ( iOpenTransaction )
        {
        TInt err = iDb.Commit();
        
        RUBY_DEBUG1( "CSIDatabase::CommitChangesL iDb.Commit() [%d]", err );
        
        if ( err != KErrNone )
            {
            iDb.Rollback();
            // Try to recover the database, if it is damaged
            if( iDb.IsDamaged() )
                {
                iDb.Recover();
                }
                
            iMutex.Signal();
            iCriticalSection.Signal();  
            iOpenTransaction = EFalse;
            User::Leave( err );
            }
        
        if ( aCompact )
            {
            RUBY_DEBUG0( "CSIDatabase::CommitChangesL compacting DB" );
            TInt error = iDb.Compact();
            if ( error != KErrNone )
                {
                RUBY_DEBUG1( "CSIDatabase::CommitChangesL compact error: [%d]", error );
                }
            }
        
        // Signal waiting BeginTransactionL()
        iMutex.Signal();
        iCriticalSection.Signal();  
        iOpenTransaction = EFalse;
        
        User::LeaveIfError( err );
        }
        
          
    }

// -----------------------------------------------------------------------------
// CSIDatabase::DoIDTablesExistL
// Checks to see if ID tables exist.
// -----------------------------------------------------------------------------
//
TBool CSIDatabase::DoIDTablesExistL()
    {
    // Get db table names list
    CDbTableNames* table_names = iDb.TableNamesL();
    TInt count = table_names->Count();
    delete table_names;
    // 1 is for the lock table!
    return ( count > 1 );
    }

// -----------------------------------------------------------------------------
// CSIDatabase::DoesLockTableExistL
// Checks to see if lock table exists
// -----------------------------------------------------------------------------
//
TBool CSIDatabase::DoesLockTableExistL()
    {
    // Get db table names list
    CDbTableNames* table_names = iDb.TableNamesL();
    TInt count = table_names->Count();

    for ( TInt counter = 0 ; counter < count ; counter++ )
        {
        if ( (*table_names)[counter] == _L( "writelocktable" ) )
            {
            delete table_names;
            return ETrue;
            }
        }

    delete table_names;
    return EFalse;
    }

// -----------------------------------------------------------------------------
// CSIDatabase::DoesModelBankTableExistL
// Checks to see if model bank table exists
// -----------------------------------------------------------------------------
//
TBool CSIDatabase::DoesModelBankTableExistL()
    {
    // Get db table names list
    CDbTableNames* table_names = iDb.TableNamesL();
    TInt count = table_names->Count();

    for ( TInt counter = 0 ; counter < count ; counter++ )
        {
        if ( (*table_names)[counter] == _L( "siModelBankIdTable" ) )
            {
            delete table_names;
            return ETrue;
            }
        }

    delete table_names;
    return EFalse;
    }

// -----------------------------------------------------------------------------
// CSIDatabase::DoesLexiconTableExistL
// Checks if lexicon table exists
// -----------------------------------------------------------------------------
//
TBool CSIDatabase::DoesLexiconTableExistL()
    {
    // Get db table names list
    CDbTableNames* table_names = iDb.TableNamesL();
    TInt count = table_names->Count();

    for ( TInt counter = 0 ; counter < count ; counter++ )
        {
        if ( (*table_names)[counter] == _L( "siLexiconIdTable" ) )
            {
            delete table_names;
            return ETrue;
            }
        }

    delete table_names;
    return EFalse;
    }

// -----------------------------------------------------------------------------
// CSIDatabase::DoesGrammarTableExistL
// Checks if grammar table exists
// -----------------------------------------------------------------------------
//
TBool CSIDatabase::DoesGrammarTableExistL()
    {
    // Get db table names list
    CDbTableNames* table_names = iDb.TableNamesL();
    TInt count = table_names->Count();

    for ( TInt counter = 0 ; counter < count ; counter++ )
        {
        if ( (*table_names)[counter] == _L( "siGrammarIdTable" ) )
            {
            delete table_names;
            return ETrue;
            }
        }

    delete table_names;
    return EFalse;
    }


// -----------------------------------------------------------------------------
// CSIDatabase::SIDatabase
// Returns a reference to the database.
// -----------------------------------------------------------------------------
//
RDbNamedDatabase& CSIDatabase::SIDatabase()
    {
    return iDb;
    }

// -----------------------------------------------------------------------------
// CSIDatabase::CreateDatabaseL
// Creates a database file if one does not exist yet.
// -----------------------------------------------------------------------------
//
void CSIDatabase::CreateDatabaseL()
    {
    RUBY_DEBUG_BLOCKL( "CSIDatabase::CreateDatabaseL" );
    
    // Get the drive of the database file
    iDatabaseDrive = EDriveC;
    if ( iDatabaseFileName.Locate( 'c' ) == 0 || iDatabaseFileName.Locate( 'C' ) == 0 )
        {
        iDatabaseDrive = EDriveC;
        }
    else if ( iDatabaseFileName.Locate( 'd' ) == 0 || iDatabaseFileName.Locate( 'D' ) == 0 )
        {
        iDatabaseDrive = EDriveD;
        }
    else if ( iDatabaseFileName.Locate( 'z' ) == 0 || iDatabaseFileName.Locate( 'Z' ) == 0 )
        {
        iDatabaseDrive = EDriveZ;
        }
    
    else if ( iDatabaseFileName.Locate( 'e' ) == 0 || iDatabaseFileName.Locate( 'E' ) == 0 )
        {
        iDatabaseDrive = EDriveE;
        }
    else
        {
        User::Leave( KErrPathNotFound );
        }
    
    RDbNamedDatabase database;
    TInt ret = database.Create( iDbSession, iDatabaseFileName, KPluginDatabaseFormatString );
    if ( ret == KErrAlreadyExists )
        {
        // Ignore KErrAlreadyExists, we will use the existing one if it is there
        ret = KErrNone;
        }
    database.Close();
    User::LeaveIfError( ret );
    }

// -----------------------------------------------------------------------------
// CSIDatabase::OpenDatabaseL
// Opens the database.
// -----------------------------------------------------------------------------
//
void CSIDatabase::OpenDatabaseL()
    {
    if ( !iDatabaseOpen )
        {
        
        User::LeaveIfError( iDb.Open( iDbSession, iDatabaseFileName, KPluginDatabaseFormatString ) );
        
        // Try to recover the database, if it is damaged
        if( iDb.IsDamaged() )
            {
            RUBY_DEBUG0( "CSIDatabase::OpenDatabaseL - Db is damaged. Attempt to recover." );
            
            User::LeaveIfError( iDb.Recover() );
            }
        
        iDatabaseOpen = ETrue;
        }
    }
    
// -----------------------------------------------------------------------------
// CSIDatabase::CloseDatabase
// Closes the database.
// -----------------------------------------------------------------------------
//
TInt CSIDatabase::CloseDatabase()
    {
    RUBY_DEBUG0( "CSIDatabase::CloseDatabase" ); 
  
    iDb.Close();
    iDatabaseOpen = EFalse;

    return KErrNone;
    }

// -----------------------------------------------------------------------------
// CSIDatabase::DbSession
// Return reference to DB session.
// -----------------------------------------------------------------------------
//
RDbs& CSIDatabase::DbSession()
    {
    return iDbSession;
    }

// -----------------------------------------------------------------------------
// CSIDatabase::DbDrive
// Returns a drive for DB file.
// -----------------------------------------------------------------------------
//
TInt CSIDatabase::DbDrive()
    {
    return iDatabaseDrive;
    }

// -----------------------------------------------------------------------------
// CSIDatabase::CreateLockTableL
// Creates a dummy table to gain write-lock at the beginning of each transaction.
// -----------------------------------------------------------------------------
//
void CSIDatabase::CreateLockTableL()
    {
    // Create writelocktable
    // This table is accessed every time when transaction starts to gain
    // exclusive locking of the DB
    _LIT( KWriteLockTable, "writelocktable" );
    
    _LIT( KCol1, "temporary" );
    
    OpenDatabaseL();
    
    // Create the rrd tables definition (columnset).
    CDbColSet* columns = CDbColSet::NewLC();
    
    // The tag id 
    TDbCol dbCol1( KCol1, EDbColUint32 );
    columns->AddL( dbCol1 );
    
    User::LeaveIfError( iDb.CreateTable( KWriteLockTable, *columns ) );
    
    // Cleanup columns
    CleanupStack::PopAndDestroy( columns ); 
    }
    
// -----------------------------------------------------------------------------
// CSIDatabase::LockTransactionsL
// Permit transactions and release database
// -----------------------------------------------------------------------------
//
void CSIDatabase::LockTransactionsL()
    {
    // lock transactions
    RUBY_DEBUG_BLOCK( "CSIDatabase::LockTransactionsL" );
    
    iCriticalSection.Wait(); // wait until a possible transaction ends
            
    // release a database
    CloseDatabase();
    }

// -----------------------------------------------------------------------------
// CSIDatabase::UnlockTransactionsL
// Allow transactions and reserve database
// -----------------------------------------------------------------------------
//
void CSIDatabase::UnlockTransactionsL()
    {
    // unlock transactions
    RUBY_DEBUG_BLOCK( "CSIDatabase::UnlockTransactionsL" );
    OpenDatabaseL(); // open database
    
    if ( iCriticalSection.IsBlocked() )
        {        
        iCriticalSection.Signal(); // allow to do transactions
        }
    }

//  End of File