presetserver/serversrc/Psdatabase.cpp
changeset 0 09774dfdd46b
child 12 608f67c22514
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/presetserver/serversrc/Psdatabase.cpp	Mon Apr 19 14:01:53 2010 +0300
@@ -0,0 +1,924 @@
+/*
+* Copyright (c) 2006-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:  Implementation of the preset database
+*
+*/
+
+
+#include <bautils.h>
+#include <pscommon.h>
+
+#include "psdebug.h"
+#include "pspendingpreset.h"
+#include "psdatabase.h"
+#include "pspresetobserver.h"
+
+// Uncomment the line below to get database print outs.
+// Warning! This may cause a lot of debug messages, so use with caution.
+// #define PS_DATABASE_PRINT_DEBUG
+
+#ifdef PS_DATABASE_PRINT_DEBUG
+    #define PS_PRINT_DATABASE  RPSDatabase::PrintDatabaseL()
+#else
+    #define PS_PRINT_DATABASE
+#endif
+
+const TInt KPSDatabaseVersion   = 1; // The latest version of the database. Must be kept up to date when adding new versions.
+
+const TInt KPSDatabaseVersion1  = 1; // Database version 1 identifier.
+
+// This array contains function pointers to all database update packages. One update corresponds to one function call, therefore
+// when adding new database updates it is imperative that this array be updated as well.
+
+const RPSDatabase::KPSDatabaseUpdateFunction RPSDatabase::iDatabaseUpdateFunctions[] =
+    {
+        &RPSDatabase::CreateTablesL
+    };
+
+// Table and columns literals.
+
+_LIT( KPSPresetsTable,              "Presets" );
+_LIT( KPSPresetsIdColumn,           "Id" );
+_LIT( KPSPresetsNameColumn,         "Name" );
+_LIT( KPSPresetsIndexColumn,        "Index" );
+_LIT( KPSPresetsDataHandlerColumn,  "DataHandler" );
+_LIT( KPSPresetsDataColumn,         "Data" );
+
+_LIT( KPSVersionsTable,             "Versions" );
+_LIT( KPSVersionsVersionColumn,     "Version" );
+
+// ======== LOCAL FUNCTIONS ========
+
+// ======== MEMBER FUNCTIONS ========
+
+// ---------------------------------------------------------------------------
+// Constructor.
+// ---------------------------------------------------------------------------
+//
+RPSDatabase::RPSDatabase( MPSPresetObserver& aObserver )
+    : RDbNamedDatabase(), iObserver( aObserver )
+    {
+    }
+
+// ---------------------------------------------------------------------------
+// Opens the database.
+// ---------------------------------------------------------------------------
+//
+void RPSDatabase::OpenL( RFs& aFs, const TDesC& aFileName )
+    {
+    Close(); // Ensure that the database is closed before trying to open it.
+
+    BaflUtils::EnsurePathExistsL( aFs, aFileName );
+
+    TInt err = RDbNamedDatabase::Create( aFs, aFileName );
+    
+    if ( err == KErrAlreadyExists )
+        {
+        // The database already existed, need to open it.
+        User::LeaveIfError( RDbNamedDatabase::Open( aFs, aFileName ) );
+        }
+    else
+        {
+        User::LeaveIfError( err );
+        }
+        
+    if ( IsDamaged() || !InTransaction() )
+        {
+        err = Recover();
+        
+        if ( !err )
+            {
+            err = Compact();
+            }
+        }
+
+    if ( err )
+        {
+        Close();
+        User::Leave( err );
+        }
+
+    // The database is now open and functional, check if the structure needs to be updated.
+    TRAP( err, UpdateDatabaseL() )
+    
+    if ( err )
+        {
+        Destroy();
+        User::Leave( err );
+        }
+
+    PS_PRINT_DATABASE;
+    }
+
+// ---------------------------------------------------------------------------
+// Deletes all presets whose data handler is no longer available.
+// ---------------------------------------------------------------------------
+//
+void RPSDatabase::DeleteOrphanedPresetsL( const RArray<TInt>& aDataHandlers )
+    {
+    PS_PRINT_DATABASE;
+
+    _LIT( sql, "DELETE FROM %S" );
+    _LIT( sqlWhere, " WHERE %S <> %d" );
+    _LIT( sqlAnd, " AND %S <> %d" );
+
+    TInt count = aDataHandlers.Count();
+
+    // Constructs the query that selects all presets that have no registered data handlers.
+    
+    HBufC* sqlBuf = HBufC::NewLC( sql().Length() + KPSPresetsTable().Length() + count * KDefaultRealWidth +
+                                  Min( 1, count ) * (sqlWhere().Length() + KPSPresetsDataHandlerColumn().Length()) +
+                                  Max( 0, count - 1 ) * (sqlAnd().Length() + KPSPresetsDataHandlerColumn().Length()) );
+
+    TPtr sqlPtr( sqlBuf->Des() );
+    sqlPtr.Format( sql, &KPSPresetsTable );
+    
+    if ( count > 0 )
+        {
+        HBufC* tempBuf = HBufC::NewLC( sqlWhere().Length() + KPSPresetsDataHandlerColumn().Length() + KDefaultRealWidth );
+        tempBuf->Des().Format( sqlWhere, &KPSPresetsDataHandlerColumn, aDataHandlers[0] );
+        sqlPtr.Append( *tempBuf );
+        CleanupStack::PopAndDestroy( tempBuf );
+
+        for ( TInt i = 1; i < count; i++ )
+            {
+            tempBuf = HBufC::NewLC( sqlAnd().Length() + KPSPresetsDataHandlerColumn().Length() + KDefaultRealWidth );
+            tempBuf->Des().Format( sqlAnd, &KPSPresetsDataHandlerColumn, aDataHandlers[i] );
+            sqlPtr.Append( *tempBuf );
+            CleanupStack::PopAndDestroy( tempBuf );
+            }
+        }
+
+    TInt rowsDeleted = Execute ( sqlPtr );
+    User::LeaveIfError(rowsDeleted);
+    
+    CleanupStack::PopAndDestroy( sqlBuf );
+
+    if (rowsDeleted > 0)
+    	{
+        User::LeaveIfError( Compact() );
+    	}
+
+    PS_PRINT_DATABASE;
+    }
+
+// ---------------------------------------------------------------------------
+// Gets all presets matching the supplied data handler.
+// ---------------------------------------------------------------------------
+//
+void RPSDatabase::GetPresetListL( RArray<TInt>& aPresets, TUid aDataHandler )
+    {
+    HBufC* sqlBuf = NULL;
+
+    if ( aDataHandler == KNullUid )
+        {
+        _LIT( sql, "SELECT %S FROM %S" );
+        
+        sqlBuf = HBufC::NewLC( sql().Length() + KPSPresetsIdColumn().Length() + KPSPresetsTable().Length() );
+        
+        sqlBuf->Des().Format( sql, &KPSPresetsIdColumn, &KPSPresetsTable );
+        }
+    else
+        {
+        _LIT( sql, "SELECT %S FROM %S WHERE %S = %d" );
+        
+        sqlBuf = HBufC::NewLC( sql().Length() + KPSPresetsIdColumn().Length() + KPSPresetsTable().Length() +
+                                      KPSPresetsDataHandlerColumn().Length() + KDefaultRealWidth );
+        
+        sqlBuf->Des().Format( sql, &KPSPresetsIdColumn, &KPSPresetsTable, &KPSPresetsDataHandlerColumn, aDataHandler.iUid );
+        }
+
+    RDbView view;
+    User::LeaveIfError( view.Prepare( *this, *sqlBuf, RDbView::EReadOnly ) );
+    CleanupStack::PopAndDestroy( sqlBuf );
+    CleanupClosePushL( view );
+
+    CDbColSet* columns = view.ColSetL();
+    TInt idColumn = columns->ColNo( KPSPresetsIdColumn );
+    delete columns;
+
+    for ( view.FirstL(); view.AtRow(); view.NextL() )
+        {
+        view.GetL();
+        aPresets.AppendL( view.ColInt32( idColumn ) );
+        }
+
+    CleanupStack::PopAndDestroy( &view );                                  
+    }
+
+// ---------------------------------------------------------------------------
+// Creates a preset.
+// ---------------------------------------------------------------------------
+//
+TInt RPSDatabase::CreatePresetL( TInt aIndex, TUid aDataHandler )
+    {
+    PS_PRINT_DATABASE;
+
+    _LIT( sql, "SELECT * FROM %S WHERE %S = %d AND %S = %d" );
+    
+    HBufC* sqlBuf = HBufC::NewLC( sql().Length() + KPSPresetsTable().Length() + KPSPresetsIndexColumn().Length() +
+                                  KPSPresetsDataHandlerColumn().Length() + 2 * KDefaultRealWidth );
+    
+    sqlBuf->Des().Format( sql, &KPSPresetsTable, &KPSPresetsIndexColumn, aIndex, &KPSPresetsDataHandlerColumn, aDataHandler.iUid );
+
+    RDbView view;
+    User::LeaveIfError( view.Prepare( *this, *sqlBuf ) );
+    CleanupStack::PopAndDestroy( sqlBuf );
+    CleanupClosePushL( view );
+    User::LeaveIfError( view.EvaluateAll() );
+
+    TInt id = KErrNotFound;
+    
+    if ( view.FirstL() )
+        {
+        User::Leave( KErrAlreadyExists );
+        }
+    else
+        {
+        view.InsertL();
+        
+        CDbColSet* columns = view.ColSetL();
+        TInt indexColumn = columns->ColNo( KPSPresetsIndexColumn );
+        TInt dataHandlerColumn = columns->ColNo( KPSPresetsDataHandlerColumn );
+        TInt idColumn = columns->ColNo( KPSPresetsIdColumn );
+        delete columns;
+        
+        view.SetColL( indexColumn, aIndex );
+        view.SetColL( dataHandlerColumn, aDataHandler.iUid );
+        
+        view.PutL();
+        
+        id = view.ColInt32( idColumn );
+        
+        iObserver.HandlePresetChangedL( id, aDataHandler, MPSPresetObserver::EPSCreated );
+        }
+        
+    CleanupStack::PopAndDestroy( &view );
+
+    PS_PRINT_DATABASE;
+
+    return id;
+    }
+
+// ---------------------------------------------------------------------------
+// Deletes a preset.
+// ---------------------------------------------------------------------------
+//
+void RPSDatabase::DeletePresetL( TInt aId )
+    {
+    PS_PRINT_DATABASE;
+
+    _LIT( sql, "SELECT %S FROM %S WHERE %S = %d" );
+
+    HBufC* sqlBuf = HBufC::NewLC( sql().Length() + KPSPresetsTable().Length() + KPSPresetsDataHandlerColumn().Length() +
+                                  KPSPresetsIdColumn().Length() + KDefaultRealWidth );
+    sqlBuf->Des().Format( sql, &KPSPresetsDataHandlerColumn, &KPSPresetsTable, &KPSPresetsIdColumn, aId );
+
+    RDbView view;
+    User::LeaveIfError( view.Prepare( *this, *sqlBuf ) );
+    CleanupStack::PopAndDestroy( sqlBuf );
+    CleanupClosePushL( view );
+
+    User::LeaveIfError( view.EvaluateAll() );
+
+    if ( view.FirstL() )
+        {
+        view.GetL();
+        CDbColSet* columns = view.ColSetL();
+        TUid uid = TUid::Uid( view.ColInt32( columns->ColNo( KPSPresetsDataHandlerColumn ) ) );
+        delete columns;
+        view.DeleteL();
+        iObserver.HandlePresetChangedL( aId, uid, MPSPresetObserver::EPSDeleted );
+        }
+
+    CleanupStack::PopAndDestroy( &view );
+
+    PS_PRINT_DATABASE;
+    }
+
+// ---------------------------------------------------------------------------
+// Commits a preset to the database.
+// ---------------------------------------------------------------------------
+//
+void RPSDatabase::CommitPresetL( const CPSPendingPreset& aPreset )
+    {
+    PS_PRINT_DATABASE;
+
+    _LIT( sql, "SELECT * FROM %S WHERE %S = %d" );
+    
+    HBufC* sqlBuf = HBufC::NewLC( sql().Length() + KPSPresetsTable().Length() + KPSPresetsIdColumn().Length() + KDefaultRealWidth );
+    sqlBuf->Des().Format( sql, &KPSPresetsTable, &KPSPresetsIdColumn, aPreset.Id() );
+
+    RDbView view;
+    User::LeaveIfError( view.Prepare( *this, *sqlBuf ) );
+    CleanupStack::PopAndDestroy( sqlBuf );
+    CleanupClosePushL( view );
+
+    User::LeaveIfError( view.EvaluateAll() );
+    
+    if ( view.FirstL() )
+        {
+        CDbColSet* columns = view.ColSetL();
+        CleanupStack::PushL( columns );
+
+        view.UpdateL();
+
+        RDbColWriteStream stream;
+
+        stream.OpenLC( view, columns->ColNo( KPSPresetsNameColumn ) );
+        stream.WriteL( aPreset.Name() );
+        stream.CommitL();
+
+        CleanupStack::PopAndDestroy( &stream );
+
+        view.SetColL( columns->ColNo( KPSPresetsIndexColumn ), aPreset.Index() );
+        
+        stream.OpenLC( view, columns->ColNo( KPSPresetsDataColumn ) );
+        stream.WriteL( aPreset.Data() );
+        stream.CommitL();
+        CleanupStack::PopAndDestroy( &stream );
+
+        view.PutL();
+
+        TUid uid = TUid::Uid( view.ColInt32( columns->ColNo( KPSPresetsDataHandlerColumn ) ) );
+
+        CleanupStack::PopAndDestroy( columns );
+        
+        iObserver.HandlePresetChangedL( aPreset.Id(), uid, MPSPresetObserver::EPSModified );
+        }
+
+    CleanupStack::PopAndDestroy( &view );
+    
+    PS_PRINT_DATABASE;
+    }
+
+// ---------------------------------------------------------------------------
+// Moves a preset to the supplied index.
+// ---------------------------------------------------------------------------
+//
+void RPSDatabase::MovePresetL( TInt aId, TInt aDestinationIndex )
+    {
+    PS_PRINT_DATABASE;
+
+    if ( aDestinationIndex < 0 )
+        {
+        User::Leave( KErrArgument );
+        }
+
+    // First, fetch the index and data handler of the preset that is about to be moved.
+
+    _LIT( selectPreset, "SELECT %S, %S FROM %S WHERE %S = %d" );
+    
+    HBufC* selectPresetBuf = HBufC::NewLC( selectPreset().Length() + KPSPresetsIndexColumn().Length() + KPSPresetsDataHandlerColumn().Length() + 
+                                           KPSPresetsTable().Length() + KPSPresetsIdColumn().Length() + KDefaultRealWidth );
+    
+    selectPresetBuf->Des().Format( selectPreset, &KPSPresetsIndexColumn, &KPSPresetsDataHandlerColumn, &KPSPresetsTable, &KPSPresetsIdColumn, aId );
+    
+    RDbView view;
+    User::LeaveIfError( view.Prepare( *this, *selectPresetBuf, RDbView::EReadOnly ) );
+    CleanupStack::PopAndDestroy( selectPresetBuf );
+    CleanupClosePushL( view );
+    
+    if ( !view.FirstL() )
+        {
+        User::Leave( KErrNotFound );
+        }
+
+    view.GetL();
+    CDbColSet* columns = view.ColSetL();
+    TInt sourceIndex = view.ColInt32( columns->ColNo( KPSPresetsIndexColumn ) );
+    TUid dataHandler = TUid::Uid( view.ColInt32( columns->ColNo( KPSPresetsDataHandlerColumn ) ) );
+    delete columns;
+    CleanupStack::PopAndDestroy( &view );
+
+    TInt minIndex = Min( sourceIndex, aDestinationIndex );
+    TInt maxIndex = Max( sourceIndex, aDestinationIndex );
+    
+    TBool direction = ( aDestinationIndex > sourceIndex ); // EFalse means that the preset is to be moved up, ETrue means that it is to be moved down.
+
+    _LIT( asc, "ASC" );
+    _LIT( desc, "DESC" );
+
+    TPtrC sortPtr( direction ? desc() : asc() );
+    
+    // Selects all preset in between the source and destination indices.
+     _LIT( sql, "SELECT * FROM %S WHERE %S >= %d AND %S <= %d AND %S = %d ORDER BY %S %S" );
+    
+    HBufC* sqlBuf = HBufC::NewLC( sql().Length() + KPSPresetsIndexColumn().Length() * 3 + KPSPresetsTable().Length() +
+                                  KPSPresetsDataHandlerColumn().Length() + sortPtr.Length() + KDefaultRealWidth * 3 );
+
+    sqlBuf->Des().Format( sql, &KPSPresetsTable, &KPSPresetsIndexColumn, minIndex, &KPSPresetsIndexColumn,
+                          maxIndex, &KPSPresetsDataHandlerColumn, dataHandler.iUid, &KPSPresetsIndexColumn, &sortPtr );
+
+    User::LeaveIfError( view.Prepare( *this, *sqlBuf ) );
+    CleanupStack::PopAndDestroy( sqlBuf );
+    CleanupClosePushL( view );
+
+    User::LeaveIfError( view.EvaluateAll() );
+
+    TInt lastIndex = aDestinationIndex;
+
+    if ( view.LastL() ) // Handle the preset that is to be moved first.    
+        {
+        view.GetL();
+        view.UpdateL();
+        
+        columns = view.ColSetL();
+        CleanupStack::PushL( columns );
+        
+        view.SetColL( columns->ColNo( KPSPresetsIndexColumn ), aDestinationIndex );
+        view.PutL();
+        
+        iObserver.HandlePresetChangedL( view.ColInt32( columns->ColNo( KPSPresetsIdColumn ) ),
+                                        TUid::Uid( view.ColInt32( columns->ColNo( KPSPresetsDataHandlerColumn ) ) ),
+                                        MPSPresetObserver::EPSModified );
+
+        view.FirstL(); // Start iterating the presets from the beginning (the last element is always the preset that is to be moved).
+
+        for ( TInt i = 0; i < view.CountL() - 1; i++ ) // The preset that was moved has already been handled.
+            {
+            view.GetL();
+            view.UpdateL();
+            
+            TInt index = view.ColInt32( columns->ColNo( KPSPresetsIndexColumn ) );
+
+            if ( lastIndex != index )
+                {
+                // If there was no preset with the index that the previous preset was moved to, there's no need to continue.
+                /*lint -save -e960 (Note -- Violates MISRA Required Rule 58, non-switch break used)*/
+                break;
+                /*lint -restore*/
+                }
+            else 
+                {
+                // Preset was moved on top of another preset, continue iterating through the presets.
+                index += direction ? -1 : 1; // If moving up, all preset indices are incremented by one, otherwise they are decremented by one.
+                lastIndex = index;
+                view.SetColL( columns->ColNo( KPSPresetsIndexColumn ), index );
+                view.PutL();
+
+                iObserver.HandlePresetChangedL( view.ColInt32( columns->ColNo( KPSPresetsIdColumn ) ),
+                                                TUid::Uid( view.ColInt32( columns->ColNo( KPSPresetsDataHandlerColumn ) ) ),
+                                                MPSPresetObserver::EPSModified );
+                }
+
+            view.NextL();
+            }
+        
+        CleanupStack::PopAndDestroy( columns );
+        }
+
+    CleanupStack::PopAndDestroy( &view );
+
+    PS_PRINT_DATABASE;
+    }
+
+// ---------------------------------------------------------------------------
+// Returns the name of a preset.
+// ---------------------------------------------------------------------------
+//
+HBufC* RPSDatabase::PresetNameL( TInt aId )
+    {
+    _LIT( sql, "SELECT %S FROM %S WHERE %S = %d" );
+
+    HBufC* sqlBuf = HBufC::NewLC( sql().Length() + KPSPresetsNameColumn().Length() + KPSPresetsTable().Length() +
+                                  KPSPresetsIdColumn().Length() + KDefaultRealWidth );
+
+    sqlBuf->Des().Format( sql, &KPSPresetsNameColumn, &KPSPresetsTable, &KPSPresetsIdColumn, aId );
+
+    RDbView view;
+    User::LeaveIfError( view.Prepare( *this, *sqlBuf, RDbView::EReadOnly ) );
+    CleanupStack::PopAndDestroy( sqlBuf );
+    CleanupClosePushL( view );
+
+    TInt nameLength = PresetNameLengthL( aId );
+    HBufC* name = HBufC::NewLC( nameLength );
+
+    if ( view.FirstL() )
+        {
+    
+        view.GetL();
+        CDbColSet* columns = view.ColSetL();
+        TInt nameColumn = columns->ColNo( KPSPresetsNameColumn );
+        delete columns;
+
+        RDbColReadStream stream;
+        TPtr namePtr = name->Des();
+        stream.OpenLC( view, nameColumn );
+        stream.ReadL( namePtr, nameLength );
+        CleanupStack::PopAndDestroy( &stream);
+        }
+    else
+        {
+        User::Leave( KErrNotFound );
+        }
+    
+    CleanupStack::Pop( name );    
+    CleanupStack::PopAndDestroy( &view );
+
+    return name;
+    }
+
+// ---------------------------------------------------------------------------
+// Returns the length of the name field for a preset.
+// ---------------------------------------------------------------------------
+//
+TInt RPSDatabase::PresetNameLengthL( TInt aId )
+    {
+    return ColumnLengthL( KPSPresetsNameColumn, aId );
+    }
+
+// ---------------------------------------------------------------------------
+// Returns the index of a preset.
+// ---------------------------------------------------------------------------
+//
+TInt RPSDatabase::PresetIndexL( TInt aId )
+    {
+    _LIT( sql, "SELECT %S FROM %S WHERE %S = %d" );
+
+    HBufC* sqlBuf = HBufC::NewLC( sql().Length() + KPSPresetsIndexColumn().Length() + KPSPresetsTable().Length() +
+                                  KPSPresetsIdColumn().Length() + KDefaultRealWidth );
+
+    sqlBuf->Des().Format( sql, &KPSPresetsIndexColumn, &KPSPresetsTable, &KPSPresetsIdColumn, aId );
+
+    RDbView view;
+    User::LeaveIfError( view.Prepare( *this, *sqlBuf, RDbView::EReadOnly ) );
+    CleanupStack::PopAndDestroy( sqlBuf );
+    CleanupClosePushL( view );
+
+    TInt index = KErrNotFound;
+
+    if ( view.FirstL() )
+        {
+        view.GetL();
+        CDbColSet* columns = view.ColSetL();
+        index = view.ColInt32( columns->ColNo( KPSPresetsIndexColumn ) );
+        delete columns;
+        }
+    else
+        {
+        User::Leave( KErrNotFound );
+        }
+        
+    CleanupStack::PopAndDestroy( &view );
+
+    return index;
+    }
+
+// ---------------------------------------------------------------------------
+// Returns the data handler of a preset.
+// ---------------------------------------------------------------------------
+//
+TUid RPSDatabase::PresetDataHandlerL( TInt aId )
+    {
+    _LIT( sql, "SELECT %S FROM %S WHERE %S = %d" );
+
+    HBufC* sqlBuf = HBufC::NewLC( sql().Length() + KPSPresetsDataHandlerColumn().Length() + KPSPresetsTable().Length() +
+                                  KPSPresetsIdColumn().Length() + KDefaultRealWidth );
+
+    sqlBuf->Des().Format( sql, &KPSPresetsDataHandlerColumn, &KPSPresetsTable, &KPSPresetsIdColumn, aId );
+
+    RDbView view;
+    User::LeaveIfError( view.Prepare( *this, *sqlBuf, RDbView::EReadOnly ) );
+    CleanupStack::PopAndDestroy( sqlBuf );
+    CleanupClosePushL( view );
+
+    TUid dataHandler = KNullUid;
+
+    if ( view.FirstL() )
+        {
+        view.GetL();
+        CDbColSet* columns = view.ColSetL();
+        dataHandler.iUid = view.ColInt32( columns->ColNo( KPSPresetsDataHandlerColumn ) );
+        delete columns;
+        }
+    else
+        {
+        User::Leave( KErrNotFound );
+        }
+        
+    CleanupStack::PopAndDestroy( &view );
+
+    return dataHandler;
+    }
+
+// ---------------------------------------------------------------------------
+// Returns the binary data of a preset.
+// ---------------------------------------------------------------------------
+//
+HBufC8* RPSDatabase::PresetDataL( TInt aId )
+    {
+    _LIT( sql, "SELECT %S FROM %S WHERE %S = %d" );
+
+    HBufC* sqlBuf = HBufC::NewLC( sql().Length() + KPSPresetsDataColumn().Length() + KPSPresetsTable().Length() +
+                                  KPSPresetsIdColumn().Length() + KDefaultRealWidth );
+
+    sqlBuf->Des().Format( sql, &KPSPresetsDataColumn, &KPSPresetsTable, &KPSPresetsIdColumn, aId );
+
+    RDbView view;
+    User::LeaveIfError( view.Prepare( *this, *sqlBuf, RDbView::EReadOnly ) );
+    CleanupStack::PopAndDestroy( sqlBuf );
+    CleanupClosePushL( view );
+
+    HBufC8* data = NULL;
+
+    if ( view.FirstL() )
+        {
+        view.GetL();
+        CDbColSet* columns = view.ColSetL();
+        TInt columnNo = columns->ColNo( KPSPresetsDataColumn );
+        delete columns;
+        
+        data = HBufC8::NewLC( view.ColLength( columnNo ) );
+        TPtr8 ptr( data->Des() );
+        
+        RDbColReadStream stream;
+        stream.OpenLC( view, columnNo );
+        stream.ReadL( ptr, view.ColLength( columnNo ) );
+        CleanupStack::PopAndDestroy( &stream );
+        CleanupStack::Pop( data );
+        }
+    else
+        {
+        User::Leave( KErrNotFound );
+        }
+        
+    CleanupStack::PopAndDestroy( &view );
+
+    return data;
+    }
+
+// ---------------------------------------------------------------------------
+// Returns the length of the binary data of a preset.
+// ---------------------------------------------------------------------------
+//
+TInt RPSDatabase::PresetDataLengthL( TInt aId )
+    {
+    return ColumnLengthL( KPSPresetsDataColumn, aId );
+    }
+
+// ---------------------------------------------------------------------------
+// Returns the current database version number.
+// ---------------------------------------------------------------------------
+//
+TInt RPSDatabase::CurrentDatabaseVersionL()
+    {
+    TInt version = 0;
+
+    RDbTable table;
+    TInt err = table.Open( *this, KPSVersionsTable, RDbTable::EReadOnly );
+    
+    if ( err != KErrNotFound )
+        {
+        User::LeaveIfError( err );
+
+        CleanupClosePushL( table );
+
+        if ( table.FirstL() )
+            {
+            table.GetL();
+
+            CDbColSet* columns = table.ColSetL();
+            version = table.ColInt32( columns->ColNo( KPSVersionsVersionColumn ) );
+            delete columns;
+            }
+
+        CleanupStack::PopAndDestroy( &table );
+        }
+
+    return version;
+    }
+
+// ---------------------------------------------------------------------------
+// Updates the database version number.
+// ---------------------------------------------------------------------------
+//
+void RPSDatabase::UpdateDatabaseVersionL( TInt aVersion )
+    {
+    RDbTable table;
+    User::LeaveIfError( table.Open( *this, KPSVersionsTable ) );
+    CleanupClosePushL( table );
+    
+    CDbColSet* columns = table.ColSetL();
+    CleanupStack::PushL( columns );
+
+    table.Reset();
+    table.FirstL() ? table.UpdateL() : table.InsertL(); // If a row exists open it for update, otherwise create a new one.
+    table.SetColL( columns->ColNo( KPSVersionsVersionColumn ), aVersion );
+    table.PutL();
+
+    CleanupStack::PopAndDestroy( 2, &table );
+    }
+
+// ---------------------------------------------------------------------------
+// Updates the whole database to the latest version.
+// ---------------------------------------------------------------------------
+//
+void RPSDatabase::UpdateDatabaseL()
+    {
+    TInt currentVersion = CurrentDatabaseVersionL();
+
+    if ( currentVersion < KPSDatabaseVersion )
+        {
+        for ( TInt i = currentVersion; i < KPSDatabaseVersion; i++ )
+            {
+            ((*this).*iDatabaseUpdateFunctions[i])(); // All of these function calls can leave.
+            }
+        }
+    }
+
+// ---------------------------------------------------------------------------
+// Creates the tables.
+// ---------------------------------------------------------------------------
+//
+void RPSDatabase::CreateTablesL()
+    {
+    // Creates the preset table.
+    TDbCol presetsIdColumn( KPSPresetsIdColumn, EDbColInt32 );
+    presetsIdColumn.iAttributes = TDbCol::EAutoIncrement;
+
+    CDbColSet* columns = CDbColSet::NewLC();
+    columns->AddL( presetsIdColumn );
+    columns->AddL( TDbCol( KPSPresetsNameColumn, EDbColLongText ) );
+    columns->AddL( TDbCol( KPSPresetsIndexColumn, EDbColInt32 ) );
+    columns->AddL( TDbCol( KPSPresetsDataHandlerColumn, EDbColInt32 ) );
+    columns->AddL( TDbCol( KPSPresetsDataColumn, EDbColLongText8 ) );
+
+    User::LeaveIfError( CreateTable( KPSPresetsTable, *columns ) );
+    
+    CleanupStack::PopAndDestroy( columns );
+
+    // This is a hack to circumvent problems with Publish & Subscribe, as it initializes all data properties to zero,
+    // causing a notification to fire with the value of zero. This is the reason why most users of the API have defined
+    // zero to be the uninitialized state of the property. The problem caused by this is that the DBMS API's auto increment
+    // field starts from zero, and should there exist a preset with such an ID, it could not be properly conveyed to the
+    // interested parties. Thus, we will make a dummy preset now that the preset table is created and remove it instantly,
+    // thereby making the auto increment column to start from one instead of zero.
+    
+    _LIT( selectAll, "SELECT * FROM %S" );
+
+    HBufC* sqlBuf = HBufC::NewLC( selectAll().Length() + KPSPresetsTable().Length() );
+    sqlBuf->Des().Format( selectAll, &KPSPresetsTable );
+    
+    RDbView view;
+    User::LeaveIfError( view.Prepare( *this, *sqlBuf ) );
+    CleanupStack::PopAndDestroy( sqlBuf );
+    CleanupClosePushL( view );
+    User::LeaveIfError( view.EvaluateAll() );
+
+    view.FirstL();    
+    view.InsertL();
+    view.PutL();
+    view.DeleteL();
+
+    CleanupStack::PopAndDestroy( &view );
+
+    // Creates the version table.
+    columns = CDbColSet::NewLC();
+    columns->AddL( TDbCol( KPSVersionsVersionColumn, EDbColInt32 ) );
+    
+    User::LeaveIfError( CreateTable( KPSVersionsTable, *columns ) );
+    
+    CleanupStack::PopAndDestroy( columns );
+    
+    UpdateDatabaseVersionL( KPSDatabaseVersion1 ); // Version 1 of the database successfully created.
+    }
+
+// ---------------------------------------------------------------------------
+// Returns the length of a column.
+// ---------------------------------------------------------------------------
+//
+TInt RPSDatabase::ColumnLengthL( const TDesC& aColumn, TInt aId )
+    {
+    _LIT( sql, "SELECT %S FROM %S WHERE %S = %d" );
+
+    TInt length = KErrNotFound;
+
+    HBufC* sqlBuf = HBufC::NewLC( sql().Length() + aColumn.Length() + KPSPresetsTable().Length() + KPSPresetsIdColumn().Length() + KDefaultRealWidth );
+    sqlBuf->Des().Format( sql, &aColumn, &KPSPresetsTable, &KPSPresetsIdColumn, aId );
+
+    RDbView view;
+    User::LeaveIfError( view.Prepare( *this, *sqlBuf, RDbView::EReadOnly ) );
+    CleanupStack::PopAndDestroy( sqlBuf );
+    CleanupClosePushL( view );
+
+    if ( view.FirstL() )
+        {
+        view.GetL();
+        CDbColSet* columns = view.ColSetL();
+        length = view.ColLength( columns->ColNo( aColumn ) );
+        delete columns;
+        }
+    else
+        {
+        User::Leave( KErrNotFound );
+        }
+        
+    CleanupStack::PopAndDestroy( &view );
+
+    return length;
+    }
+    
+// ---------------------------------------------------------------------------
+// Debug method that prints the contents of the database.
+// ---------------------------------------------------------------------------
+//
+void RPSDatabase::PrintDatabaseL()
+    {
+    PSDEBUG( "RPSDatabase::PrintDatabaseL() - Versions:" );
+
+    RDbTable table;
+    TInt err = table.Open( *this, KPSVersionsTable, RDbTable::EReadOnly );
+    
+    if ( err == KErrNotFound )
+        {
+        PSDEBUG( "\tYarr, thar database be not in this world, mate!" );
+        }
+    else
+        {
+        User::LeaveIfError( err );
+
+        CleanupClosePushL( table );
+
+        if ( table.FirstL() )
+            {
+            CDbColSet* columns = table.ColSetL();
+            TInt versionColumn = columns->ColNo( KPSVersionsVersionColumn );
+            delete columns;
+            
+            do  
+                {
+                table.GetL();
+                PSDEBUG2( "\t%d", table.ColInt32( versionColumn ) );
+                } while ( table.NextL() );
+            }
+        else
+            {
+            PSDEBUG( "\tYarr, thar table be empty!" );
+            }
+
+        CleanupStack::PopAndDestroy( &table );
+        }
+    
+    PSDEBUG( "RPSDatabase::PrintDatabaseL() - Presets:" );
+
+    err = table.Open( *this, KPSPresetsTable, RDbTable::EReadOnly );
+    
+    if ( err == KErrNotFound )
+        {
+        PSDEBUG( "\tYarr, thar database be not in this world, mate!" );
+        }
+    else
+        {
+        User::LeaveIfError( err );
+        
+        CleanupClosePushL( table );
+
+		if ( table.FirstL() )
+		    {
+            CDbColSet* columns = table.ColSetL();
+            TInt idColumn = columns->ColNo( KPSPresetsIdColumn );
+            TInt dataHandlerColumn = columns->ColNo( KPSPresetsDataHandlerColumn );
+            TInt indexColumn = columns->ColNo( KPSPresetsIndexColumn );
+            TInt nameColumn = columns->ColNo( KPSPresetsNameColumn );
+            delete columns;
+            
+            do  
+                {
+                table.GetL();
+                // stream is needed for long columns
+                RDbColReadStream stream;
+                TInt nameLength = PresetNameLengthL( table.ColInt32( idColumn ) );
+                HBufC* name = HBufC::NewLC( nameLength );
+                TPtr namePtr = name->Des();
+                stream.OpenLC( table, nameColumn );
+                stream.ReadL( namePtr, nameLength );
+
+                CleanupStack::PopAndDestroy( &stream );
+                
+                PSDEBUG5( "\tId: %10u\tData Handler: 0x%x\tIndex: %10u\tName: %S", table.ColInt32( idColumn ), table.ColInt32( dataHandlerColumn ), table.ColInt32( indexColumn ), name );
+                
+                CleanupStack::PopAndDestroy( name );
+                } while ( table.NextL() );
+		    }
+        else
+            {
+            PSDEBUG( "\tYarr, thar table be empty!" );
+            }
+    
+        CleanupStack::PopAndDestroy( &table );
+        }
+    }
+
+// ======== GLOBAL FUNCTIONS ========