--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/loggingservices/eventlogger/LogServ/src/LogServDatabaseMarshall.cpp Fri Jan 22 11:06:30 2010 +0200
@@ -0,0 +1,622 @@
+// Copyright (c) 2002-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 <logclientchangeobserver.h>
+#include "LogServDatabaseMarshall.h"
+#include "logservpanic.h"
+#include "LogServCacheConfig.h"
+#include "LogServCacheStrings.h"
+#include "LogServCacheTypes.h"
+#include "LogServBackupInterface.h"
+#include "LogServResourceInterpreter.h"
+#include "LogServDatabaseChangeInterface.h"
+#include <logserv.rsg>
+#include "LogServSqlStrings.h"
+#include "LOGREPDEFS.H"
+
+
+// Constants
+const TInt KExpectedNumberOfTables = 4; // Should match the code in CreateTablesL
+
+// Literal constants
+_LIT(KLogDatabaseName,"Logdbu.dat");
+
+
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// -----> CLogServDatabaseMarshall (source)
+/////////////////////////////////////////////////////////////////////////////////////////
+
+CLogServDatabaseMarshall::CLogServDatabaseMarshall(RFs& aFsSession,
+ CLogServResourceInterpreter& aResourceInterface,
+ MLogServBackupInterface& aBackupInterface)
+: iFsSession(aFsSession), iResourceInterface(aResourceInterface), iBackupInterface(aBackupInterface)
+ {
+ }
+
+CLogServDatabaseMarshall::~CLogServDatabaseMarshall()
+ {
+ iBackupInterface.BIObserverRemove(*this);
+ //
+ delete iDatabaseName;
+ delete iCacheStrings;
+ delete iCacheTypes;
+ delete iCacheConfig;
+ delete iSecurity;
+ delete iEventType;
+ //
+ iDatabase.Close();
+ iStandardTypeUids.Close();
+ }
+
+void CLogServDatabaseMarshall::ConstructL()
+ {
+ iBackupInterface.BIObserverAddL(*this, MLogServBackupInterface::EObjectDatabaseMarshall);
+ //
+ iSecurity = CLogServSecurity::NewL(iResourceInterface);
+ //
+ iEventType = CLogEventType::NewL();
+
+ DatabaseLocateL();
+ DatabaseOpenL();
+ RestoreStandardTypesL();
+
+ iCacheTypes->CopyStandardTypeUidsL(iStandardTypeUids);
+ }
+
+CLogServDatabaseMarshall* CLogServDatabaseMarshall::NewL(RFs& aFsSession,
+ CLogServResourceInterpreter& aResourceInterface,
+ MLogServBackupInterface& aBackupInterface)
+ {
+ CLogServDatabaseMarshall* self = new(ELeave) CLogServDatabaseMarshall(aFsSession, aResourceInterface, aBackupInterface);
+ CleanupStack::PushL(self);
+ self->ConstructL();
+ CleanupStack::Pop(self);
+ return self;
+ }
+
+/////////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////////
+
+TInt CLogServDatabaseMarshall::DTIBegin()
+ {
+ const TInt backupError = iBackupInterface.BIErrorValueForCurrentState();
+ if (backupError != KErrNone)
+ return backupError;
+
+ __ASSERT_DEBUG(!iDatabase.InTransaction(), Panic(ELogBeginInTransaction));
+ return iDatabase.Begin();
+ }
+
+TInt CLogServDatabaseMarshall::DTICommitAndEnd()
+ {
+ __ASSERT_DEBUG(iDatabase.InTransaction(), Panic(ELogCommitNotInTransaction));
+ TInt err = iDatabase.Commit();
+ if (err == KErrNone && iCacheStrings != NULL)
+ {
+ iCacheStrings->Commit();
+ }
+ return err;
+ }
+
+void CLogServDatabaseMarshall::DTIRollBack()
+ {
+ __ASSERT_DEBUG(iDatabase.InTransaction(), Panic(ELogRollbackNotInTransaction));
+ iDatabase.Rollback();
+ if (iCacheStrings != NULL)
+ {
+ iCacheStrings->Rollback();
+ }
+ }
+
+TInt CLogServDatabaseMarshall::DTIExecuteSql(const TDesC& aStatement, TDbTextComparison aComparison)
+ {
+ return iDatabase.Execute(aStatement, aComparison);
+ }
+
+TBool CLogServDatabaseMarshall::DTIInTransaction() const
+ {
+ return iDatabase.InTransaction();
+ }
+
+TBool CLogServDatabaseMarshall::DTIDatabaseIsDamaged() const
+ {
+ return iDatabase.IsDamaged();
+ }
+
+CLogServResourceInterpreter& CLogServDatabaseMarshall::DTIResourceInterface() const
+ {
+ return iResourceInterface;
+ }
+
+MLogServDatabaseChangeInterface& CLogServDatabaseMarshall::DTIChangeInterface() const
+ {
+ __ASSERT_ALWAYS(iChangeInterface, Panic(ELogNoChangeInterfacePointer));
+ return *iChangeInterface;
+ }
+
+CLogServCacheStrings& CLogServDatabaseMarshall::DTICacheStrings() const
+ {
+ __ASSERT_ALWAYS(iCacheStrings, Panic(ELogCacheAccessDuringBackupStrings));
+ return *iCacheStrings;
+ }
+
+CLogServCacheTypes& CLogServDatabaseMarshall::DTICacheTypes() const
+ {
+ __ASSERT_ALWAYS(iCacheTypes, Panic(ELogCacheAccessDuringBackupTypes));
+ return *iCacheTypes;
+ }
+
+CLogServCacheConfig& CLogServDatabaseMarshall::DTICacheConfig() const
+ {
+ __ASSERT_ALWAYS(iCacheConfig, Panic(ELogCacheAccessDuringBackupConfig));
+ return *iCacheConfig;
+ }
+
+RDbDatabase& CLogServDatabaseMarshall::DTIDatabase()
+ {
+ return iDatabase;
+ }
+
+TBool CLogServDatabaseMarshall::DTIIsAllowed(TEventOp aEventOp, const RMessage2& aMessage, TUid aEventType, const char* aDiagnostic) const
+ {
+ return iSecurity->IsAllowed(aMessage, aEventType, aEventOp, aDiagnostic);
+ }
+
+const RArray<TUid>& CLogServDatabaseMarshall::DTIUidsOfStandardTypes()
+ {
+ return iStandardTypeUids;
+ }
+
+/////////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CLogServDatabaseMarshall::BOHandleEventL(TLogServBackupEvent aEvent)
+ {
+ switch(aEvent)
+ {
+ case EBackupStarting:
+ {
+ //Destroy config, types and strings caches
+ delete iCacheConfig;
+ iCacheConfig = NULL;
+ delete iCacheStrings;
+ iCacheStrings = NULL;
+ delete iCacheTypes;
+ iCacheTypes = NULL;
+ //Close the database
+ iDatabase.Close();
+ }
+ break;
+
+ case EBackupEnded:
+ {
+ // Re-open the database and create config, types and strings caches
+ DatabaseOpenL();
+ // reset views as a different database is being restored
+ DTIChangeInterface().DCISubmitGlobalChangeContextL(KLogClientChangeEventRefreshView);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+/////////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CLogServDatabaseMarshall::DatabaseLocateL()
+ {
+ // Get drive for database
+ TDriveUnit driveUnit(static_cast<TInt>(RFs::GetSystemDrive()));
+ TDriveName name(driveUnit.Name());
+
+ TFileName path;
+ iFsSession.PrivatePath(path);
+
+ // Ensure database path exists
+ TParse parse;
+ User::LeaveIfError(parse.Set(path, &name, NULL));
+ path = parse.FullName();
+
+ TInt error = iFsSession.MkDirAll(path);
+ if (error != KErrAlreadyExists)
+ User::LeaveIfError(error);
+
+ path += KLogDatabaseName;
+ iDatabaseName = path.AllocL();
+ }
+
+/**
+Opens the LogEng database.
+@return KErrNone, If the "database open" operation completes successfully.
+ If the "database open" operation fails the function returns the repported error code.
+ KErrCorrupt, If the database is opened successfully but is damaged, then the function returns KErrCorrupt.
+*/
+TInt CLogServDatabaseMarshall::DoDbOpen()
+ {
+ LOGTEXT2("CLogServDatabaseMarshall::DatabaseOpenL() - attempting to open db: %S", iDatabaseName);
+#ifdef LOGGING_ENABLED
+ TEntry entry;
+ if (iFsSession.Entry(*iDatabaseName, entry) == KErrNone)
+ {
+ LOGTEXT2("CLogServDatabaseMarshall::DatabaseOpenL() - Database file: %S DOES exist", iDatabaseName);
+ }
+ else
+ {
+ LOGTEXT2("CLogServDatabaseMarshall::DatabaseOpenL() - Database file: %S NOT FOUND", iDatabaseName);
+ }
+#endif
+ // Open database
+ TInt err = iDatabase.Open(iFsSession, DatabaseName());
+ LOGTEXT2("CLogServDatabaseMarshall::DatabaseOpenL() - attempting to open DMBS database resulted in error: %d", error);
+ // Check if the database is damaged. If it is set the error to KErrCorrupt so that it
+ // will be deleted.
+ if ((err == KErrNone) && iDatabase.IsDamaged())
+ {
+ err = KErrCorrupt;
+ }
+ return err;
+ }
+
+/**
+Check if the database table count is the expected one - KExpectedNumberOfTables.
+@return True, The database tables count is KExpectedNumberOfTables,
+ False, The database tables count is not KExpectedNumberOfTables;
+@leave KErrNoMemory, an out of memory condition has occurred;
+ Note that the function may leave with database specific errors or
+ other system-wide error codes.
+*/
+TBool CLogServDatabaseMarshall::DbTableCntCheckL()
+ {
+ CDbTableNames* tables = iDatabase.TableNamesL();
+ TInt numberOfTables = tables->Count();
+ delete tables;
+ LOGTEXT2("CLogServDatabaseMarshall::DatabaseOpenL() - Number of tables: %d", numberOfTables);
+ return numberOfTables == KExpectedNumberOfTables;
+ }
+
+/**
+Alters the "Event" table if the number column length is not KLogMaxNumberLength.
+@return KErrNone, The "alter" operation has completed successfully, system wide or database specific error code otherwise.
+*/
+TInt CLogServDatabaseMarshall::AlterEventTblIfOldFmt(CDbColSet& aEventTblColSet)
+ {
+ const TDbCol* numberCol = aEventTblColSet.Col(KLogFieldEventNumberString);
+ __ASSERT_DEBUG(numberCol != NULL, User::Invariant());
+ TInt err = KErrNone;
+ // check the column width is correct
+ if(numberCol->iMaxLength != KLogMaxNumberLength)
+ {
+ //The column width is not correct, so this is an old format database.
+ //Modify the database so the number length is KLogMaxNumberLength.
+ (const_cast <TDbCol*> (numberCol))->iMaxLength = KLogMaxNumberLength;
+ err = iDatabase.AlterTable(KLogNameEventString, aEventTblColSet);
+ }
+ return err;
+ }
+
+#ifdef SYMBIAN_ENABLE_EVENTLOGGER_DUALSIM
+
+/**
+Alters the "Event" table if the the table does not have "SimId" column.
+@return KErrNone, The "alter" operation has completed successfully, system wide or database specific error code otherwise.
+@leave KErrNoMemory, an out of memory condition has occurred;
+ Some other failure codes, not related to the "alter" opertaion.
+*/
+TInt CLogServDatabaseMarshall::AlterEventTblIfNoSimIdL(CDbColSet& aEventTblColSet)
+ {//Compiled only when SYMBIAN_ENABLE_EVENTLOGGER_DUALSIM macro is defined
+ const TDbCol* simIdCol = aEventTblColSet.Col(KLogFieldEventSimId);
+ TInt err = KErrNone;
+ if(!simIdCol)
+ {
+ TDbCol col(KLogFieldEventSimId, EDbColUint32);
+ aEventTblColSet.AddL(col);
+ err = iDatabase.AlterTable(KLogNameEventString, aEventTblColSet);
+ }
+ return err;
+ }
+
+#endif
+
+/**
+Checks the database structure and alters the tables if that's an old format database.
+@return KErrNone, The "alter" operation has completed successfully, system wide or database specific error code otherwise.
+@leave KErrNoMemory, an out of memory condition has occurred;
+ Some other failure codes, not related to the "alter" opertaion.
+*/
+TInt CLogServDatabaseMarshall::AlterDbIfOldFmtL()
+ {
+ CDbColSet* tableEventCol = iDatabase.ColSetL(KLogNameEventString);
+ CleanupStack::PushL(tableEventCol);
+ //Check for old format database which had MaxNumberLength =32
+ TInt err = AlterEventTblIfOldFmt(*tableEventCol);
+#ifdef SYMBIAN_ENABLE_EVENTLOGGER_DUALSIM
+ //Check if the "SimId" column is present
+ if(err == KErrNone)
+ {
+ err = AlterEventTblIfNoSimIdL(*tableEventCol);
+ }
+#endif
+ CleanupStack::PopAndDestroy(tableEventCol);
+ return err;
+ }
+
+/**
+Closes and deletes the LogEng database. In _DEBUG builds the "delete file" error will be printed out.
+*/
+void CLogServDatabaseMarshall::DbDelete()
+ {
+ iDatabase.Close();
+#ifdef _DEBUG
+ //Do not remove the statement bellow. In _DEBUG builds it forms a single "TInt err2 = iFsSession.Delete(DatabaseName());" statement.
+ TInt err2 =
+#endif
+ iFsSession.Delete(DatabaseName());
+#ifdef _DEBUG
+ if((err2 != KErrNone) && (err2 != KErrNotFound))
+ {
+ RDebug::Print(_L("CLogServDatabaseMarshall::DatabaseOpenL() - Failed to delete file. Error = %d"), err2);
+ }
+#endif
+ }
+
+/**
+Attempts to create the LogEng database and tables.
+@return KErrNoNone, The database was created successfully, system wide or database specific error otherwise.
+*/
+TInt CLogServDatabaseMarshall::DbCreate()
+ {
+ // Try and create the database
+ LOGTEXT("CLogServDatabaseMarshall::DatabaseOpenL() - trying to create database");
+ TRAPD(err, DatabaseCreateL(DatabaseName()));
+ LOGTEXT2("CLogServDatabaseMarshall::DatabaseOpenL() - creation error was: %d", error);
+ return err;
+ }
+
+/**
+Creates config and string LogEng caches. Finishes the initialization of the event types cache.
+@leave KErrNoMemory, an out of memory condition has occurred;
+ Note that the function may leave with database specific errors or
+ other system-wide error codes.
+*/
+void CLogServDatabaseMarshall::CreateCachesL()
+ {
+ // Create other cache objects (these require the database to be already opened)
+ LOGTEXT("CLogServDatabaseMarshall::DatabaseOpenL() - creating config cache");
+ iCacheConfig = CLogServCacheConfig::NewL(*this);
+ LOGTEXT("CLogServDatabaseMarshall::DatabaseOpenL() - creating string cache");
+ iCacheStrings = CLogServCacheStrings::NewL(*this);
+ // Finish the type cache initialization
+ LOGTEXT("CLogServDatabaseMarshall::DatabaseOpenL() - initializing type cache");
+ iCacheTypes->InitializeL();
+ }
+
+void CLogServDatabaseMarshall::DatabaseOpenL()
+ {
+ // Create the cache objects - objects need to be put into the cache as
+ // soon as the database is open.
+ LOGTEXT("CLogServDatabaseMarshall::DatabaseOpenL() - creating type cache");
+ iCacheTypes = CLogServCacheTypes::NewL(*this);
+ TInt err = DoDbOpen();
+ // Check we have the expected number of tables
+ if(err == KErrNone && !DbTableCntCheckL())
+ {
+ err = KErrCorrupt;
+ }
+ // Check a compaction can be performed. If it can't it indicates a serious problem?
+ if(err == KErrNone && (err = iDatabase.Compact()) != KErrNone)
+ {
+ err = KErrCorrupt;
+ }
+ if(err == KErrNone)
+ {
+ err = CLogServDatabaseMarshall::AlterDbIfOldFmtL();
+ }
+ // If the database failed to open, delete and recreate.
+ if(err == KErrNotFound || err == KErrCorrupt || err == KErrArgument || err == KErrEof)
+ {
+ DbDelete();
+ // Try and create the database
+ err = DbCreate();
+ if(err != KErrNone)
+ {
+ DbDelete();
+ }
+ }
+ User::LeaveIfError(err);
+ CreateCachesL();
+ // At this point, its safe to tell the backup interface what file it has to watch
+ LOGTEXT("CLogServDatabaseMarshall::DatabaseOpenL() - registering database filename with backup interface");
+ iBackupInterface.BISetDatabaseNameL(DatabaseName());
+ LOGTEXT("CLogServDatabaseMarshall::DatabaseOpenL() - end");
+ }
+
+void CLogServDatabaseMarshall::DatabaseCreateL(const TDesC& aName)
+ {
+ User::LeaveIfError(iDatabase.Replace(iFsSession, aName));
+ CreateTablesL();
+ }
+
+// Note: Number of tables HAS to match KExpectedNumberOfTables
+void CLogServDatabaseMarshall::CreateTablesL()
+ {
+ DTIBeginWithRollBackProtectionLC();
+
+ // Create event table
+ TheSql.Format(KLogTableEventString, KLogMaxRemotePartyLength, KLogMaxSubjectLength, KLogMaxNumberLength);
+ User::LeaveIfError(iDatabase.Execute(TheSql));
+ MakeColumnAutoIncremetingL(KLogNameEventString, KLogFieldIdString);
+
+ // Create event type table
+ TheSql.Format(KLogTableTypeString, KLogMaxDescriptionLength);
+ User::LeaveIfError(iDatabase.Execute(TheSql));
+ MakeColumnAutoIncremetingL(KLogNameTypeString, KLogFieldIdString);
+
+ // Create string list table
+ TheSql.Format(KLogTableStringString, KLogMaxSharedStringLength);
+ User::LeaveIfError(iDatabase.Execute(TheSql));
+ MakeColumnAutoIncremetingL(KLogNameStringString, KLogFieldIdString);
+
+ // Create configuration table
+ TheSql.Copy(KLogTableConfigString);
+ User::LeaveIfError(iDatabase.Execute(TheSql));
+
+ // Create the index
+ CreateIndiciesL();
+
+ // Set the initial configuration
+ CreateConfigurationL();
+
+ // Load standard event types
+ CreateTypesL();
+
+ DTICommitAndCancelRollbackProtectionL();
+ }
+
+void CLogServDatabaseMarshall::CreateTypesL(TBool aReadOnly)
+ {
+ // Get the array size
+ TResourceReader reader;
+ iResourceInterface.CreateResourceReaderLC(reader, R_LOG_INITIAL_EVENTS, CLogServResourceInterpreter::ELogWrap);
+
+ // Create them
+ DTICacheTypes().CreateStandardTypesL(reader, aReadOnly);
+
+ CleanupStack::PopAndDestroy(); // reader
+ }
+
+void CLogServDatabaseMarshall::CreateIndiciesL()
+ {
+ // Get the array size
+ TResourceReader reader;
+ iResourceInterface.CreateResourceReaderLC(reader, R_LOG_INDEXES, CLogServResourceInterpreter::ELogWrap);
+
+ const TInt indexes = reader.ReadInt16();
+
+ // Read in the array
+ for(TInt c1 = 0; c1 < indexes; c1++)
+ {
+ const TPtrC name(reader.ReadTPtrC());
+ const TPtrC table(reader.ReadTPtrC());
+
+ // Get the number of keys
+ const TInt keys = reader.ReadInt16();
+
+ CDbKey* key = CDbKey::NewLC();
+
+ for(TInt c2 = 0; c2 < keys; c2++)
+ {
+ TPtrC col = reader.ReadTPtrC();
+ TUint order = reader.ReadUint16();
+ TInt len = reader.ReadInt16();
+
+ // Add the key
+ key->AddL(TDbKeyCol(col, len, (TDbKeyCol::TOrder)order));
+ }
+
+ // Make key unique if required
+ if (reader.ReadInt8())
+ key->MakeUnique();
+
+ // Set comparison
+ const TDbTextComparison comparison = static_cast<TDbTextComparison>(reader.ReadInt8());
+ key->SetComparison(comparison);
+
+ // Create the index
+ User::LeaveIfError(iDatabase.CreateIndex(name, table, *key));
+
+ CleanupStack::PopAndDestroy(key);
+ }
+
+ CleanupStack::PopAndDestroy(); // reader
+ }
+
+void CLogServDatabaseMarshall::CreateConfigurationL()
+ {
+ // Load the resource/repository file default configuration
+ // The precedence is given to the reading from the repository file.
+ TLogConfig config;
+
+ CRepository* repository = NULL;
+ TRAPD(res, repository = CRepository::NewL(KUidLogengRepository));
+ if (res == KErrNone)
+ {
+ CleanupStack::PushL(repository);
+ ReadRepositoryFileConfigurationL(config, *repository);
+ CleanupStack::PopAndDestroy(repository);
+ }
+ else if (res == KErrCorrupt)
+ {
+ User::Leave(res);
+ }
+ else
+ {
+ ReadResourceFileConfigurationL(config);
+ }
+ // Insert the column
+ TheSql.Format(KLogSqlInsertConfigString, config.iMaxLogSize, config.iMaxRecentLogSize, config.iMaxEventAge);
+ User::LeaveIfError(iDatabase.Execute(TheSql));
+ }
+
+void CLogServDatabaseMarshall::ReadRepositoryFileConfigurationL(TLogConfig& aConfig, CRepository& repository) const
+ {
+ TInt maxLogSize;
+ TInt maxRecentLogSize;
+ TInt maxEventAge;
+ //
+ User::LeaveIfError(repository.Get(KMaxLogSizeRepKey, maxLogSize));
+ aConfig.iMaxLogSize = static_cast<TLogSize> (maxLogSize);
+ User::LeaveIfError(repository.Get(KMaxRecentLogSizeRepKey, maxRecentLogSize));
+ aConfig.iMaxRecentLogSize = static_cast<TLogRecentSize> (maxRecentLogSize);
+ User::LeaveIfError(repository.Get(KMaxEventAgeRepKey, maxEventAge));
+ aConfig.iMaxEventAge = static_cast<TLogAge> (maxEventAge);
+ }
+
+void CLogServDatabaseMarshall::ReadResourceFileConfigurationL(TLogConfig& aConfig) const
+ {
+ TResourceReader reader;
+ iResourceInterface.CreateResourceReaderLC(reader, R_LOG_INITIAL_CONFIG, CLogServResourceInterpreter::ELogWrap);
+ //
+ aConfig.iMaxLogSize = static_cast<TLogSize>(reader.ReadUint16());
+ aConfig.iMaxRecentLogSize = static_cast<TLogRecentSize>(reader.ReadUint8());
+ aConfig.iMaxEventAge = static_cast<TLogAge>(reader.ReadUint32());
+ //
+ CleanupStack::PopAndDestroy(); // reader
+ }
+
+void CLogServDatabaseMarshall::MakeColumnAutoIncremetingL(const TDesC& aTable, const TDesC& aColumn)
+ {
+ CDbColSet* newTable = iDatabase.ColSetL(aTable);
+ CleanupStack::PushL(newTable);
+
+ const TDbCol* oldCol = newTable->Col(aColumn);
+ __ASSERT_DEBUG(oldCol != NULL, Panic(ELogNoSuchColumn));
+
+ TDbCol newCol = *oldCol;
+ newCol.iAttributes |= TDbCol::EAutoIncrement;
+
+ newTable->Remove(aColumn);
+ newTable->AddL(newCol);
+
+ User::LeaveIfError(iDatabase.DropTable(aTable));
+ User::LeaveIfError(iDatabase.CreateTable(aTable, *newTable));
+
+ CleanupStack::PopAndDestroy(newTable);
+ }
+