--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/messagingfw/msgsrvnstore/server/src/msvmessagedbadapter.cpp Mon Jan 18 20:36:02 2010 +0200
@@ -0,0 +1,1457 @@
+// Copyright (c) 2001-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: Neeraj Nayan
+//
+// Description: The class provides interface to read/write header and
+// body entry from the database.
+//
+
+
+/**
+ * HEADER FILES
+ */
+
+#include "msvpreferreddrivelist.h" // Preferred drive list.
+#include "msvmessagedbadapter.h" // Header file for this class.
+#include "msvindexadapter.h" // For GetDriveId()
+#include "msvdbadapter.h" // For BindL() and handle to CMsvDBAdapter while constructing
+
+
+/**
+ * LITERAL DEFINITION
+ */
+const TInt KMaxQueryLength = 1000;
+
+// Macros to wrap DB error. KErrGeneral is thrown for
+// all DB error. Make sure you declare 'err' before
+// using these macros.
+#define MSG_WRAP_DB_LEAVE(x) TRAP(err, x); \
+ if(err<=KSqlErrGeneral) User::Leave(KErrGeneral); \
+ User::LeaveIfError(err);
+
+#define MSG_WRAP_DB_ERROR(errTxt) err = errTxt; \
+ if(err<=KSqlErrGeneral) User::Leave(KErrGeneral); \
+ else User::LeaveIfError(err);
+
+
+
+/**
+ * MEMBER FUNCTIONS OF CMsvMessageDBAdapter
+ */
+
+
+
+/**
+ * CMsvMessageDBAdapter()
+ * The default Constructor.
+ */
+CMsvMessageDBAdapter::CMsvMessageDBAdapter(CMsvDBAdapter* aDBAdapter):
+ iDBAdapter(aDBAdapter), iDatabaseRef(aDBAdapter->iDatabase)
+ {
+ }
+
+
+
+
+/**
+ * ~CMsvMessageDBAdapter()
+ * The default Destructor.
+ */
+CMsvMessageDBAdapter::~CMsvMessageDBAdapter()
+ {
+ iHeaderDataList.ResetAndDestroy();
+ }
+
+
+
+
+/**
+ * NewL()
+ *
+ * @param CMsvDBAdapter* : A reference to database adapter class.
+ * @return CMsvMessageDBAdapter* : A reference to CMsvMessageDBAdapter
+ * class.
+ *
+ * Allocates and constructs a new CMsvMessageDBAdapter object.
+ */
+CMsvMessageDBAdapter* CMsvMessageDBAdapter::NewL(CMsvDBAdapter* aDBAdapter,TBool aDbInCurrentDrive)
+ {
+ //Create the DBAdapter object.
+ CMsvMessageDBAdapter* self = new(ELeave) CMsvMessageDBAdapter(aDBAdapter);
+ CleanupStack::PushL(self);
+
+ self->ConstructL(aDbInCurrentDrive);
+
+ CleanupStack::Pop();
+ return self;
+ }
+
+
+/*
+ * ConstructL()
+ *
+ * The function populates metadata structure. A metadata structure
+ * contains information about metadata table which is later used to
+ * create and update header entries. The structure is updated whenever
+ * a new header table is created in the database.
+ */
+void CMsvMessageDBAdapter::ConstructL(TBool aDbInCurrentDrive)
+ {
+ // check if the operation are required for only single drive
+ // This is required for message store converter as it works only on 1 drive.
+
+ if(aDbInCurrentDrive)
+ {
+ iNonAttachedDrive = ETrue;
+ UpdateMetadataStructureL(KCurrentDriveId);
+ }
+ else
+ {
+ iNonAttachedDrive = EFalse;
+ for(TInt index=1; index<KMaxNumberOfDrives; index++)
+ {
+ if((*(iDBAdapter->iDatabasePresent))[index])
+ {
+ UpdateMetadataStructureL(index);
+ }
+ }
+ }
+ }
+
+
+
+
+
+void CMsvMessageDBAdapter::UpdateMetadataStructureL(TUint aDriveId)
+ {
+ TInt err;
+ _LIT(KCheckHeaderTableQuery0, "SELECT tbl_name FROM DB");
+ _LIT(KCheckHeaderTableQuery1, ".sqlite_master WHERE type = 'table' AND tbl_name LIKE 'Header_%';");
+
+ // 1. Get all the header table names from the SQLITE_MASTER table.
+ RBuf headerTableQuery;
+ CleanupClosePushL(headerTableQuery);
+ headerTableQuery.CreateL(KMaxQueryLength);
+
+ // Create the query string.
+ if(!iNonAttachedDrive)
+ {
+ headerTableQuery.Append(KCheckHeaderTableQuery0);
+ headerTableQuery.AppendNum(aDriveId);
+ headerTableQuery.Append(KCheckHeaderTableQuery1);
+ }
+ else
+ {
+ _LIT(KCheckHeaderTableQuery2, "SELECT tbl_name FROM sqlite_master WHERE type = 'table' AND tbl_name LIKE 'Header_%';");
+ headerTableQuery.Append(KCheckHeaderTableQuery2);
+ }
+
+ //Prepare a statement for the query.
+ RSqlStatement sqlStmt;
+ RPointerArray<HBufC> headerTableNames;
+ CleanupClosePushL(sqlStmt);
+ CleanupClosePushL(headerTableNames);
+
+ MSG_WRAP_DB_ERROR(sqlStmt.Prepare(iDatabaseRef, headerTableQuery));
+ // Prepare a list of header table names.
+ HBufC* tableName = NULL;
+ while((err = sqlStmt.Next()) == KSqlAtRow)
+ {
+ MSG_WRAP_DB_LEAVE(tableName = ColumnTextL(sqlStmt, 0));
+ CleanupStack::PushL(tableName);
+
+ headerTableNames.AppendL(tableName);
+ CleanupStack::Pop();
+ }
+ MSG_WRAP_DB_ERROR(err);
+
+ // 2. For each tablename read the table structure from the DB.
+ _LIT(KGetHeaderSchema1, "SELECT * FROM DB");
+ _LIT(KGetHeaderSchema2, ".");
+ _LIT(KGetHeaderSchema3, ";");
+ _LIT(KGetHeaderSchema4, "SELECT * FROM ");
+
+ while(headerTableNames.Count())
+ {
+ HBufC* tableName = headerTableNames[0];
+
+ TLex mtmId(tableName->Mid(tableName->Locate('_')+1));
+ TUid uid;
+ mtmId.Val(uid.iUid);
+
+ // Don't insert meta-data if we already have it for this mtmId.
+ if(GetMetadataEntryIndex(uid) != KErrNotFound)
+ {
+ delete tableName;
+ headerTableNames.Remove(0);
+ continue;
+ }
+
+ headerTableQuery.Zero();
+ sqlStmt.Close();
+
+ if(iNonAttachedDrive) // this is for msg converter. it refers to a single drive
+ {
+ headerTableQuery.Append(KGetHeaderSchema4);
+ }
+ else
+ {
+ // This creates following string...
+ // "SELECT * FROM DB<driveId>.HEADER_<MtmId>;"
+
+ headerTableQuery.Append(KGetHeaderSchema1);
+ headerTableQuery.AppendNum(aDriveId);
+ headerTableQuery.Append(KGetHeaderSchema2);
+ }
+
+ headerTableQuery.Append(tableName->Des());
+ headerTableQuery.Append(KGetHeaderSchema3);
+
+ MSG_WRAP_DB_ERROR(sqlStmt.Prepare(iDatabaseRef, headerTableQuery));
+
+ CHeaderMetaData* headerMetaData = new(ELeave) CHeaderMetaData();
+ CleanupStack::PushL(headerMetaData);
+
+ TInt metadataIndex = 0;
+ headerMetaData->iMtmId.iUid = uid.iUid;
+
+ TInt colCnt = sqlStmt.ColumnCount();
+ // Record the header structure, ignore first two fields (Id, UID).
+ TRAPD(err,
+ for(TInt index=2; index<colCnt; index++)
+ {
+ CFieldClass* fieldObj = new(ELeave) CFieldClass();
+ CleanupStack::PushL(fieldObj);
+
+ User::LeaveIfError(sqlStmt.DeclaredColumnType(index, fieldObj->iType));
+ TPtrC fieldName;
+ User::LeaveIfError(sqlStmt.ColumnName(index, fieldName));
+ fieldObj->iName = fieldName.AllocL();
+ headerMetaData->iFieldList.AppendL(fieldObj);
+ CheckStdColumnsAdded(fieldObj->iName, headerMetaData->iStdColumnStatus);
+
+ CleanupStack::Pop(fieldObj);
+ }
+ );
+ if(err)
+ {
+ MSG_WRAP_DB_ERROR(err);
+ }
+ iHeaderDataList.InsertL(headerMetaData, metadataIndex);
+ CleanupStack::Pop(); // headerMetaData
+
+ delete tableName;
+ headerTableNames.Remove(0);
+ } // while(headerTableNames.Count())
+
+ CleanupStack::PopAndDestroy(3); // headerTableQuery, sqlStmt, headerTableNames
+ }
+
+
+
+
+/*
+ * IsHeaderTableExistsL()
+ *
+ * @param aMtmId: MTM ID of the header table.
+ * @return TBool: ETrue/EFalse
+ *
+ * The function returns ETrue if the header table for
+ * passed MTM ID already exists in the database. The
+ * function works for only message server current drive.
+ */
+TBool CMsvMessageDBAdapter::IsHeaderTableExistsL(const TUid& aMtmId)
+ {
+ _LIT16(KCheckHeaderTableQuery1, "SELECT COUNT(*) FROM DB");
+ _LIT16(KCheckHeaderTableQuery2, ".SQLITE_MASTER WHERE NAME LIKE 'Header_");
+ _LIT16(KCheckHeaderTableQuery3, "';");
+
+ RBuf headerTableQuery;
+ CleanupClosePushL(headerTableQuery);
+ headerTableQuery.CreateL(KMaxQueryLength);
+
+ // Create the query string.
+ // Ex: SELECT COUNT(*) FROM DB<drive-id>.SQLITE_MASTER WHERE
+ // NAME LIKE 'Header_<mtm-id>';
+ headerTableQuery.Append(KCheckHeaderTableQuery1);
+ headerTableQuery.AppendNum(KCurrentDriveId);
+ headerTableQuery.Append(KCheckHeaderTableQuery2);
+ headerTableQuery.AppendNum(aMtmId.iUid);
+ headerTableQuery.Append(KCheckHeaderTableQuery3);
+
+ TInt count = 0;
+ TSqlScalarFullSelectQuery query(iDatabaseRef);
+ TRAPD(err, count = query.SelectIntL(headerTableQuery));
+ CleanupStack::PopAndDestroy(); // headerTableQuery
+
+ // If the table exists, count will have value 1.
+ if(err == KErrNone && count == 1)
+ {
+ return ETrue;
+ }
+ else
+ {
+ return EFalse;
+ }
+ }
+
+
+
+
+
+
+/*
+ * CreateHeaderTableL()
+ *
+ * @param aMtmId: MTM ID of the table.
+ * @param aFieldDetails: Table structure details.
+ * @return aErrorMessage: Error message text, in case
+ * header table creation fails.
+ *
+ * The function creates separate header table for each MTM type.
+ * If the table already exist, it simply exits without returning an error.
+ */
+void CMsvMessageDBAdapter::CreateHeaderTableL(const TUid& aMtmId, const RPointerArray<CFieldPair>& aFieldDetails, TPtrC& aErrorMessage)
+ {
+ RBuf headerTableQuery;
+ CleanupClosePushL(headerTableQuery);
+ headerTableQuery.CreateL(KMaxQueryLength);
+
+ CHeaderMetaData* headerMetaData = new(ELeave) CHeaderMetaData();
+ CleanupStack::PushL(headerMetaData);
+
+ TInt metadataIndex = GetMetadataEntryIndex(aMtmId);
+ // Insert a new metadata entry if it doesn't already exist.
+ if(metadataIndex == KErrNotFound)
+ {
+ iHeaderDataList.InsertL(headerMetaData, 0);
+ }
+ headerMetaData->iMtmId = aMtmId;
+
+ if(iNonAttachedDrive)
+ {
+ _LIT16(KCreateHeaderTableQuery1, "CREATE TABLE IF NOT EXISTS ");
+ _LIT16(KCreateHeaderTableQuery2, "Header_");
+
+ // This creates following string...
+ // "CREATE TABLE IF NOT EXISTS DB<DriveId>.Header_<MtmId> ( id INT, uid INT,"
+ headerTableQuery.Append(KCreateHeaderTableQuery1);
+ headerTableQuery.Append(KCreateHeaderTableQuery2);
+ }
+ else
+ {
+ _LIT16(KCreateHeaderTableQuery1, "CREATE TABLE IF NOT EXISTS DB");
+ _LIT16(KCreateHeaderTableQuery2, ".Header_");
+ // "CREATE TABLE IF NOT EXISTS DB<DriveId>.Header_<MtmId> ( id INT, uid INT,"
+ headerTableQuery.Append(KCreateHeaderTableQuery1);
+ headerTableQuery.AppendNum(KCurrentDriveId);
+ headerTableQuery.Append(KCreateHeaderTableQuery2);
+ }
+ _LIT16(KCreateHeaderTableQuery3, " (id INT, uid INT, ");
+ _LIT16(KCreateHeaderTableQuery4, "Details TEXT, PRIMARY KEY (id, uid));");
+ aErrorMessage.Set(_L(""));
+ headerTableQuery.AppendNum(aMtmId.iUid);
+ headerTableQuery.Append(KCreateHeaderTableQuery3);
+
+ // Add the column details to the query.
+ TRAPD(err, DoCreateHeaderTableCreationQueryL(aFieldDetails, headerTableQuery, headerMetaData));
+ if(err)
+ {
+ CleanupStack::Pop(headerMetaData);
+ delete headerMetaData;
+ iHeaderDataList.Remove(0);
+ User::Leave(err);
+ }
+ headerTableQuery.Append(KCreateHeaderTableQuery4);
+
+ // Execute the query.
+ // Ignore error if table already exist.
+ err = iDatabaseRef.Exec(headerTableQuery);
+ if(err < 0)
+ {
+ aErrorMessage.Set(iDatabaseRef.LastErrorMessage());
+
+ CleanupStack::Pop(headerMetaData);
+ delete headerMetaData;
+ iHeaderDataList.Remove(0);
+ MSG_WRAP_DB_ERROR(err);
+ }
+
+ CleanupStack::Pop(headerMetaData); // headerMetaData
+ if(metadataIndex != KErrNotFound)
+ delete headerMetaData;
+ CleanupStack::PopAndDestroy(); // headerTableQuery
+ }
+
+
+
+
+
+
+
+/*
+ * DoCreateHeaderTableCreationQueryL()
+ *
+ * @param aFieldDetails: Table structure details.
+ * @return aHeaderTableQuery: Header-table creation query string.
+ * @return aHeaderMetaData: Header metadata structure.
+ *
+ * The function appends the column details to the header
+ * creation query string. It also updates the metadata structure
+ * for the header entry.
+ */
+void CMsvMessageDBAdapter::DoCreateHeaderTableCreationQueryL(const RPointerArray<CFieldPair>& aFieldDetails, RBuf& aHeaderTableQuery, CHeaderMetaData*& aHeaderMetaData)
+ {
+ _LIT16(KIntField, " INT, ");
+ _LIT16(KTextField, " TEXT, ");
+ _LIT16(KDateField, " INT64, ");
+ _LIT16(KQuote, "\"");
+
+ CFieldClass *fieldObj = NULL;
+ // Browse through each column names in the field-list.
+ TInt fieldDetailsCount = aFieldDetails.Count();
+ for(TInt fieldIndex=0; fieldIndex<fieldDetailsCount; fieldIndex++)
+ {
+ if(!aFieldDetails[fieldIndex]->iFieldName)
+ {
+ User::Leave(KErrArgument);
+ }
+
+ // Create a metadata entry.
+ fieldObj = new(ELeave) CFieldClass;
+ CleanupStack::PushL(fieldObj);
+ fieldObj->iName = aFieldDetails[fieldIndex]->iFieldName->Des().AllocL();
+
+ // Append column name under quotes.
+ aHeaderTableQuery.Append(KQuote);
+ aHeaderTableQuery.Append(aFieldDetails[fieldIndex]->iFieldName->Des());
+ aHeaderTableQuery.Append(KQuote);
+
+ // Append appropriate column type.
+ switch(aFieldDetails[fieldIndex]->iFieldType)
+ {
+ case EIntegerField:
+ aHeaderTableQuery.Append(KIntField);
+ fieldObj->iType = ESqlInt;
+ break;
+ case ETextField:
+ aHeaderTableQuery.Append(KTextField);
+ fieldObj->iType = ESqlText;
+ break;
+ case EDateField:
+ aHeaderTableQuery.Append(KDateField);
+ fieldObj->iType = ESqlInt64;
+ break;
+ default:
+ User::Leave(KErrArgument);
+ break;
+ }
+
+ aHeaderMetaData->iFieldList.AppendL(fieldObj);
+ CleanupStack::Pop(); // fieldObj
+
+ // Update the status of standard header columns
+ // in the metadata structure.
+ CheckStdColumnsAdded(aFieldDetails[fieldIndex]->iFieldName, aHeaderMetaData->iStdColumnStatus);
+ } // for(TInt fieldIndex=0; fieldIndex<aFieldDetails.Count(); fieldIndex++)
+
+
+ // Adding the 'Details' column to the query string.
+ _LIT(KDetails, "Details");
+ fieldObj = new(ELeave) CFieldClass;
+ CleanupStack::PushL(fieldObj);
+
+ fieldObj->iType = ESqlText;
+ fieldObj->iName = KDetails().AllocL();
+ aHeaderMetaData->iFieldList.AppendL(fieldObj); // Added for Details column.
+
+ CleanupStack::Pop(); // fieldObj
+ }
+
+
+
+
+
+
+/*
+ * CreateHeaderEntryL()
+ *
+ * @param aMtmId: MtmId to identify the header table.
+ * @param aEntryId: Metadata Entry Id.
+ * @param aFieldPairList: Header details.
+ *
+ * The function creates a new header entry in the header
+ * table. The header details passed to the function in
+ * aFieldPairList should either have values for all the
+ * columns in the header table or should have value for
+ * only one column (The default column "Details").
+ *
+ * The function can accept multiple header portion (each
+ * associated with a unique UID. These header portion will
+ * be stored in a separate row in the header table.
+ */
+
+void CMsvMessageDBAdapter::CreateHeaderEntryL(const TUid& aMtmId, TMsvId aEntryId, const RPointerArray<CHeaderFields>& aFieldPairList)
+ {
+ TInt err = KErrNone;
+
+ // Get the metadata entry index for the MTM.
+ TInt metadataEntryIndex = GetMetadataEntryIndex(aMtmId);
+ User::LeaveIfError(metadataEntryIndex);
+ TBool isLocalTransaction = EFalse;
+
+ // Open the DB transaction, if it is not already open.
+ if(!iDBAdapter->isTransactionOpen && !iNonAttachedDrive)
+ {
+ MSG_WRAP_DB_LEAVE(iDBAdapter->BeginTransactionL());
+ isLocalTransaction = ETrue;
+ }
+
+ // For each entry in header details, create a separate
+ // row in the header table.
+ TInt fieldPairListCount = aFieldPairList.Count();
+ TRAP(err,
+ for(TInt index=0; index<fieldPairListCount; index++)
+ {
+ CHeaderFields* headerFields = aFieldPairList[index];
+ DoInsertHeaderEntryL(aMtmId, aEntryId, headerFields, metadataEntryIndex);
+ }
+ );
+
+ // Handle error and close the transaction.
+ if(err && !iNonAttachedDrive)
+ {
+ if(isLocalTransaction)
+ {
+ iDBAdapter->RollbackTransactionL();
+ }
+ MSG_WRAP_DB_ERROR(err);
+ }
+ else if (!iNonAttachedDrive)
+ {
+ if(isLocalTransaction)
+ {
+ MSG_WRAP_DB_LEAVE(iDBAdapter->CommitTransactionL());
+ }
+ }
+ }
+
+
+
+
+
+
+
+/*
+ * DoInsertHeaderEntryL()
+ *
+ * @param aMtmId: MtmId to identify the header table.
+ * @param aEntryId: Metadata Entry Id.
+ * @param aHeaderFields: Header entry details.
+ * @param aMetadataEntryIndex: Header table metadata.
+ *
+ * The function creates a new header entry in the header
+ * table. The header details passed to the function in
+ * aHeaderFields should either have values for all the
+ * columns in the header table or should have value for
+ * only one column (The default column "Details").
+ */
+void CMsvMessageDBAdapter::DoInsertHeaderEntryL(const TUid& aMtmId, TMsvId aEntryId, CHeaderFields* aHeaderFields, TInt aMetadataEntryIndex)
+ {
+ TInt err = KErrNone;
+ _LIT16(KGetHeaderSchema5, "INSERT INTO HEADER_");
+ _LIT16(KGetHeaderSchema1, "INSERT INTO DB");
+ _LIT16(KGetHeaderSchema2, ".HEADER_");
+ _LIT16(KGetHeaderSchema3, " VALUES (:id, :uid");
+ _LIT16(KGetHeaderSchema4, " (id, uid, Details) VALUES (:id, :uid, :details);");
+ _LIT16(KComma, ", :");
+ _LIT16(KDelimiter, ");");
+
+ TInt KDefaultColumnSize = 1;
+ RBuf headerTableQuery;
+ CleanupClosePushL(headerTableQuery);
+
+ // Create header entry creation query
+ // Ex: INSERT INTO DB<driveId>.HEADER_<mtmId>
+ headerTableQuery.CreateL(KMaxQueryLength);
+ if(!iNonAttachedDrive)
+ {
+ headerTableQuery.Append(KGetHeaderSchema1);
+ headerTableQuery.AppendNum(GetDriveId(aEntryId));
+ headerTableQuery.Append(KGetHeaderSchema2);
+ }
+ else
+ {
+ headerTableQuery.Append(KGetHeaderSchema5);
+ }
+ headerTableQuery.AppendNum(aMtmId.iUid);
+
+ // Append column names to the query string.
+ // Column names are stored in the header metadata structure.
+ CHeaderMetaData* headerMetadata = iHeaderDataList[aMetadataEntryIndex];
+ TInt fieldPairListCount = aHeaderFields->iFieldPairList.Count();
+
+ // If header entry has more than one field...
+ if(KDefaultColumnSize < fieldPairListCount)
+ {
+ headerTableQuery.Append(KGetHeaderSchema3);
+
+ // Check if number of values passed for the header entry
+ // matches with the number of columns in the header table.
+ RPointerArray<CFieldClass>& fieldList = headerMetadata->iFieldList;
+ if(fieldList.Count() != fieldPairListCount)
+ {
+ User::Leave(KErrArgument);
+ }
+ // Append column names to the query string.
+ TInt fieldListCount = fieldList.Count();
+ for(TInt fieldCount=0; fieldCount<fieldListCount; fieldCount++)
+ {
+ headerTableQuery.Append(KComma);
+ headerTableQuery.Append(fieldList[fieldCount]->iName->Des());
+ }
+ headerTableQuery.Append(KDelimiter);
+ } // if(KDefaultColumnSize < aHeaderFields->iFieldPairList.Count())
+ else
+ {
+ // If the header entry has just one field
+ // It must be the value for default column (Details).
+ headerTableQuery.Append(KGetHeaderSchema4);
+ }
+
+ // Prepare the SQL statement for execution.
+ RSqlStatement sqlStmt;
+ CleanupClosePushL(sqlStmt);
+ MSG_WRAP_DB_ERROR(sqlStmt.Prepare(iDatabaseRef, headerTableQuery));
+
+ // Bind values against column names.
+ TInt fieldIndex = 0;
+ MSG_WRAP_DB_LEAVE(BindIntL(sqlStmt, fieldIndex++, UnmaskTMsvId(aEntryId)));
+ MSG_WRAP_DB_LEAVE(BindIntL(sqlStmt, fieldIndex++, aHeaderFields->iUid.iUid));
+
+ TInt index = 0;
+ for(; index < fieldPairListCount-1; index++, fieldIndex++)
+ {
+ switch(headerMetadata->iFieldList[index]->iType)
+ {
+ case ESqlInt:
+ BindIntL(sqlStmt, fieldIndex, aHeaderFields->iFieldPairList[index]->iFieldNumValue);
+ break;
+ case ESqlInt64:
+ BindInt64L(sqlStmt, fieldIndex, aHeaderFields->iFieldPairList[index]->iFieldNumValue);
+ break;
+ case ESqlText:
+ if(!aHeaderFields->iFieldPairList[index]->iFieldTextValue)
+ {
+ User::Leave(KErrArgument);
+ }
+ BindTextL(sqlStmt, fieldIndex, aHeaderFields->iFieldPairList[index]->iFieldTextValue->Des());
+ break;
+ }
+ }
+ if(aHeaderFields->iFieldPairList[index]->iFieldTextValue)
+ {
+ BindTextL(sqlStmt, fieldIndex, aHeaderFields->iFieldPairList[index]->iFieldTextValue->Des());
+ }
+ else
+ {
+ User::Leave(KErrArgument);
+ }
+
+ // Execute the query. Return KErrGeneral,
+ // if entry is not inserted.
+ err = sqlStmt.Exec();
+ if(!err)
+ {
+ User::Leave(KErrGeneral);
+ }
+ if(err == KSqlErrConstraint)
+ {
+ User::Leave(KErrAlreadyExists);
+ }
+ MSG_WRAP_DB_ERROR(err);
+ CleanupStack::PopAndDestroy(2); //sqlStmt, headerTableQuery
+ }
+
+
+
+
+
+
+
+
+/*
+ * LoadHeaderEntryL()
+ *
+ * @param aMtmId: MtmId to identify the header table.
+ * @param aEntryId: Metadata Entry Id.
+ * @return aFieldPairList: Header entry details.
+ *
+ * The function loads the header entry from the header
+ * table. If the header entry has multiple row, all of
+ * them will be loaded and stored as a separate element
+ * of CHeaderFields.
+ */
+void CMsvMessageDBAdapter::LoadHeaderEntryL(const TUid& aMtmId, TMsvId aEntryId, RPointerArray<CHeaderFields>& aFieldPairList)
+ {
+ TInt err = KErrNone;
+ _LIT16(KGetHeaderSchema1, "SELECT uid");
+ _LIT16(KGetHeaderSchema2, " FROM DB");
+ _LIT16(KGetHeaderSchema3, ".HEADER_");
+ _LIT16(KGetHeaderSchema4, " WHERE id = :id;");
+ _LIT16(KComma, ", \"");
+ _LIT16(KQuote, "\"");
+
+ aFieldPairList.Reset();
+ // Get the metadata entry index for the MTM.
+ TInt metadataEntryIndex = GetMetadataEntryIndex(aMtmId);
+ User::LeaveIfError(metadataEntryIndex);
+ RPointerArray<CFieldClass>& fieldList = iHeaderDataList[metadataEntryIndex]->iFieldList;
+
+ // Create header query string
+ // Ex: SELECT uid,
+ RBuf headerTableQuery;
+ CleanupClosePushL(headerTableQuery);
+ headerTableQuery.CreateL(KMaxQueryLength);
+ headerTableQuery.Append(KGetHeaderSchema1);
+
+ // Append header column name to the query.
+ TInt fieldListCount = fieldList.Count();
+ for(TInt index=0; index<fieldListCount; index++)
+ {
+ headerTableQuery.Append(KComma);
+ headerTableQuery.Append(fieldList[index]->iName->Des());
+ headerTableQuery.Append(KQuote);
+ }
+
+ // Append "FROM DB<driveId>.HEADER_<mtmId> WHERE ID = :ID;
+ headerTableQuery.Append(KGetHeaderSchema2);
+ headerTableQuery.AppendNum(GetDriveId(aEntryId));
+ headerTableQuery.Append(KGetHeaderSchema3);
+ headerTableQuery.AppendNum(aMtmId.iUid);
+ headerTableQuery.Append(KGetHeaderSchema4);
+
+ //Prepare the SQL statement.
+ RSqlStatement sqlStmt;
+ CleanupClosePushL(sqlStmt);
+
+ MSG_WRAP_DB_ERROR(sqlStmt.Prepare(iDatabaseRef, headerTableQuery));
+ MSG_WRAP_DB_LEAVE(BindIntL(sqlStmt, 0, UnmaskTMsvId(aEntryId)));
+
+ // Load the header entry into header structure.
+ TRAP(err, DoLoadHeaderEntryL(sqlStmt, fieldList, aFieldPairList));
+
+ // Handle errors if any.
+ if(err<0)
+ {
+ for(TInt index=0; index<aFieldPairList.Count(); index++)
+ {
+ CHeaderFields* headerRow = aFieldPairList[0];
+ aFieldPairList.Remove(0);
+ delete headerRow;
+ }
+ MSG_WRAP_DB_ERROR(err);
+ }
+
+ if(!aFieldPairList.Count())
+ {
+ User::Leave(KErrNotFound);
+ }
+ CleanupStack::PopAndDestroy(2); //sqlStmt, headerTableQuery
+ }
+
+
+
+
+
+/*
+ * DoLoadHeaderEntryL()
+ *
+ * @param aSqlStmt: Query statement.
+ * @param aMetadataList: Metadata Entry for header table.
+ * @return aFieldPairList: Header entry details.
+ *
+ * The function loads the header entry from the header
+ * table. If the header entry has multiple row, all of
+ * them will be loaded and stored as a separate element
+ * of CHeaderFields.
+ */
+void CMsvMessageDBAdapter::DoLoadHeaderEntryL(RSqlStatement& aSqlStmt, const RPointerArray<CFieldClass>& aMetadataList, RPointerArray<CHeaderFields>& aFieldPairList)
+ {
+ TInt err = KErrNone;
+ // Fetch each header-table row in the loop.
+ while(ETrue)
+ {
+ err = aSqlStmt.Next();
+ if(KSqlAtRow != err)
+ {
+ if(KSqlAtEnd == err)
+ {
+ break;
+ }
+ else
+ {
+ MSG_WRAP_DB_ERROR(err);
+ }
+ }
+
+ // Create header structure to store a single row.
+ CHeaderFields* headerRow = new(ELeave) CHeaderFields();
+ CleanupStack::PushL(headerRow);
+
+ TBool isPrimaryRow = EFalse;
+ TInt count = aMetadataList.Count();
+ headerRow->iUid.iUid = ColumnInt(aSqlStmt, 0);
+ // Read individual fields and populate the header structure.
+ for(TInt index=0; index<count; index++)
+ {
+ CFieldPair* fieldObj = new(ELeave) CFieldPair();
+ CleanupStack::PushL(fieldObj);
+
+ fieldObj->iFieldName = aMetadataList[index]->iName->Des().AllocL();
+ switch(aMetadataList[index]->iType)
+ {
+ case ESqlInt:
+ fieldObj->iFieldNumValue = ColumnInt(aSqlStmt, index+1);
+ if(!isPrimaryRow && fieldObj->iFieldNumValue != NULL && index != count-1)
+ {
+ isPrimaryRow = ETrue;
+ }
+ break;
+ case ESqlInt64:
+ fieldObj->iFieldNumValue = ColumnInt64(aSqlStmt, index+1);
+ if(!isPrimaryRow && fieldObj->iFieldNumValue != NULL && index != count-1)
+ {
+ isPrimaryRow = ETrue;
+ }
+ break;
+ case ESqlText:
+ TRAP(err, fieldObj->iFieldTextValue = ColumnTextL(aSqlStmt, index+1));
+ if(err < 0)
+ {
+ User::Leave(KErrGeneral);
+ }
+ if(!isPrimaryRow && (index != count-1) && fieldObj->iFieldTextValue->Des().Compare(_L("")) )
+ {
+ isPrimaryRow = ETrue;
+ }
+ break;
+ } // switch(fieldList[index]->iType)
+
+ headerRow->iFieldPairList.AppendL(fieldObj);
+ CleanupStack::Pop(fieldObj);
+ } // for(TInt index=0; index<fieldList.Count(); index++)
+
+ // If it is not a primary entry, remove extra fields
+ // from the header row.
+ // A header table can store two types of entry, primary
+ // entry, one which has values in all the columns in the
+ // table, and another which has values in only default columns.
+ if(!isPrimaryRow)
+ {
+ for(TInt index=0; index<count-1; index++)
+ {
+ delete headerRow->iFieldPairList[0];
+ headerRow->iFieldPairList.Remove(0);
+ }
+ }
+ aFieldPairList.AppendL(headerRow);
+ CleanupStack::Pop(headerRow);
+ }
+ }
+
+
+
+
+
+
+
+/*
+ * UpdateHeaderEntryL()
+ *
+ * @param aMtmId: MtmId to identify the header table.
+ * @param aEntryId: Entry Id.
+ * @param aFieldPairList: Header entry details.
+ *
+ * The function updates the header entry in the header table with the
+ * passed header structure. A header entry is essentially a list of
+ * UID-String combination which is stored as a separate element in the
+ * passed header structure. Each such header portion is stored in separate
+ * rows in the header table.
+
+ * If the number of rows in the passed header structure is more than what
+ * is present in the DB, the function inserts the new rows in the header
+ * table. And if the few rows are missing from the passed header structure
+ * the function deletes the extra row from the header table.
+ */
+void CMsvMessageDBAdapter::UpdateHeaderEntryL(const TUid& aMtmId, TMsvId aEntryId, const RPointerArray<CHeaderFields>& aFieldPairList)
+ {
+ TInt err = KErrNone;
+ _LIT16(KGetHeaderSchema1, "SELECT uid FROM DB");
+ _LIT16(KGetHeaderSchema2, ".HEADER_");
+ _LIT16(KGetHeaderSchema3, " WHERE id = :id;");
+
+ // First find out the number of rows present in the DB
+ // against this entry, and store their UID in a array.
+ RBuf headerTableQuery;
+ CleanupClosePushL(headerTableQuery);
+ headerTableQuery.CreateL(KMaxQueryLength);
+
+ // This creates following string...
+ // Ex: SELECT uid FROM DB<driveId>.HEADER_<mtmId> WHERE id = :id;
+ headerTableQuery.Append(KGetHeaderSchema1);
+ headerTableQuery.AppendNum(GetDriveId(aEntryId));
+ headerTableQuery.Append(KGetHeaderSchema2);
+ headerTableQuery.AppendNum(aMtmId.iUid);
+ headerTableQuery.Append(KGetHeaderSchema3);
+
+ //Prepare a statement for the query.
+ RSqlStatement sqlStmt;
+ CleanupClosePushL(sqlStmt);
+
+ MSG_WRAP_DB_ERROR(sqlStmt.Prepare(iDatabaseRef, headerTableQuery));
+ MSG_WRAP_DB_LEAVE(BindIntL(sqlStmt, 0, UnmaskTMsvId(aEntryId)));
+
+ // Execute the query and form the UID list.
+ RArray<TInt> uidList;
+ CleanupClosePushL(uidList);
+ do
+ {
+ err = sqlStmt.Next();
+ if(err == KSqlAtRow)
+ {
+ uidList.AppendL(ColumnInt(sqlStmt, 0));
+ }
+ }
+ while(err == KSqlAtRow);
+
+ // If no entry is found in DB, return KErrNotFound.
+ MSG_WRAP_DB_ERROR(err);
+ if(!uidList.Count())
+ {
+ User::Leave(KErrNotFound);
+ }
+
+ // Open a DB transaction if it is not already open.
+ TBool isLocalTransaction = EFalse;
+ if(!iDBAdapter->isTransactionOpen)
+ {
+ MSG_WRAP_DB_LEAVE(iDBAdapter->BeginTransactionL());
+ isLocalTransaction = ETrue;
+ }
+
+ // Process all the header portion one by one.
+ TRAP(err, DoProcessHeaderEntryL(aMtmId, aEntryId, aFieldPairList, uidList));
+
+ // Close the transaction and handle error.
+ if(err)
+ {
+ if(isLocalTransaction)
+ {
+ iDBAdapter->RollbackTransactionL();
+ }
+ MSG_WRAP_DB_ERROR(err);
+ }
+ else
+ {
+ if(isLocalTransaction)
+ {
+ MSG_WRAP_DB_LEAVE(iDBAdapter->CommitTransactionL());
+ }
+ }
+
+ CleanupStack::PopAndDestroy(3); //uidList, sqlStmt, headerTableQuery
+ }
+
+
+
+
+
+
+/*
+ * DoProcessHeaderEntryL()
+ *
+ * @param aMtmId: MtmId to identify the header table.
+ * @param aEntryId: Entry Id.
+ * @param aFieldPairList: Header entry details.
+ * @param aUidList: UID list of the header entry present in DB.
+ *
+ * The function is called by UpdateHeaderEntryL() only, and it updates
+ * the header entry in the header table. For details, see the comment
+ * of UpdateHeaderEntryL().
+ */
+void CMsvMessageDBAdapter::DoProcessHeaderEntryL(const TUid& aMtmId, TMsvId aEntryId, const RPointerArray<CHeaderFields>& aFieldPairList, RArray<TInt>& aUidList)
+ {
+ TInt metadataEntryIndex = GetMetadataEntryIndex(aMtmId);
+ User::LeaveIfError(metadataEntryIndex);
+
+ // Processing each header row.
+ TInt fieldPairListCount = aFieldPairList.Count();
+ for(TInt index=0; index < fieldPairListCount; index++)
+ {
+ // Search the entry UID in the UID list.
+ CHeaderFields* headerRow = aFieldPairList[index];
+ TInt rowIndex = aUidList.Find(headerRow->iUid.iUid);
+ // If the entry is not present in DB, create it.
+ if(KErrNotFound == rowIndex)
+ {
+ // Insert the entry
+ DoInsertHeaderEntryL(aMtmId, aEntryId, headerRow, metadataEntryIndex);
+ }
+ else
+ {
+ // else, update the entry
+ DoUpdateHeaderEntryL(aMtmId, aEntryId, headerRow, metadataEntryIndex);
+ aUidList.Remove(rowIndex);
+ }
+ }
+
+ // If there is extra entries in the DB,
+ // delete them.
+ if(aUidList.Count())
+ {
+ _LIT16(KGetHeaderSchema1, "DELETE FROM DB");
+ _LIT16(KGetHeaderSchema2, ".HEADER_");
+ _LIT16(KGetHeaderSchema3, " WHERE id = :id and uid in (");
+ _LIT16(KGetHeaderSchema4, ");");
+ _LIT16(KComma, ", ");
+
+ // Create the delete query string.
+ RBuf headerTableQuery;
+ CleanupClosePushL(headerTableQuery);
+ headerTableQuery.CreateL(KMaxQueryLength);
+
+ // This creates following string...
+ headerTableQuery.Append(KGetHeaderSchema1);
+ headerTableQuery.AppendNum(GetDriveId(aEntryId));
+ headerTableQuery.Append(KGetHeaderSchema2);
+ headerTableQuery.AppendNum(aMtmId.iUid);
+ headerTableQuery.Append(KGetHeaderSchema3);
+ headerTableQuery.AppendNum(aUidList[0]);
+ TInt uidListCount = aUidList.Count();
+ for(TInt index=1; index < uidListCount; index++)
+ {
+ headerTableQuery.Append(KComma);
+ headerTableQuery.AppendNum(aUidList[index]);
+ }
+ headerTableQuery.Append(KGetHeaderSchema4);
+
+ //Prepare a statement for the query.
+ RSqlStatement sqlStmt;
+ CleanupClosePushL(sqlStmt);
+ User::LeaveIfError(sqlStmt.Prepare(iDatabaseRef, headerTableQuery));
+ BindIntL(sqlStmt, 0, UnmaskTMsvId(aEntryId));
+
+ // Execute the query and handle error.
+ TInt err = sqlStmt.Exec();
+ if(!err)
+ {
+ User::Leave(KErrNotFound);
+ }
+
+ User::LeaveIfError(err);
+ CleanupStack::PopAndDestroy(2); //sqlStmt, headerTableQuery
+ }
+ }
+
+
+
+
+
+
+/*
+ * DoUpdateHeaderEntryL()
+ *
+ * @param aMtmId: MtmId to identify the header table.
+ * @param aEntryId: Entry Id.
+ * @param aHeaderFields: Header entry details.
+ * @param aMetadataEntryIndex: UID list of the header entry present in DB.
+ *
+ * The function is called by UpdateHeaderEntryL() only, and it updates
+ * the header entry in the header table. For details, see the comment
+ * of UpdateHeaderEntryL().
+ */
+void CMsvMessageDBAdapter::DoUpdateHeaderEntryL(const TUid& aMtmId, TMsvId aEntryId, CHeaderFields* aHeaderFields, TInt aMetadataEntryIndex)
+ {
+ _LIT16(KGetHeaderSchema1, "UPDATE DB");
+ _LIT16(KGetHeaderSchema2, ".HEADER_");
+ _LIT16(KGetHeaderSchema3, " SET ");
+ _LIT16(KGetHeaderSchema4, " WHERE id = :id and uid = :uid;");
+ _LIT16(KGetHeaderSchema5, " details = :details WHERE id = :id and uid = :uid;");
+ _LIT16(KComma, ", ");
+ _LIT16(KColon, "\" = :");
+ _LIT16(KQuote, "\"");
+
+ RBuf headerTableQuery;
+ CleanupClosePushL(headerTableQuery);
+
+ // Create the query string:
+ // Ex: UPDATE DB<driveId>.HEADER_<mtmId> SET
+ headerTableQuery.CreateL(KMaxQueryLength);
+ headerTableQuery.Append(KGetHeaderSchema1);
+ headerTableQuery.AppendNum(GetDriveId(aEntryId));
+ headerTableQuery.Append(KGetHeaderSchema2);
+ headerTableQuery.AppendNum(aMtmId.iUid);
+ headerTableQuery.Append(KGetHeaderSchema3);
+
+ TInt columnIndex = -1;
+ // Append column information to the query
+ CHeaderMetaData* headerMetadata = iHeaderDataList[aMetadataEntryIndex];
+ TInt fieldPairListCount = aHeaderFields->iFieldPairList.Count();
+ if(fieldPairListCount > 1)
+ {
+ // Check if all the field values are passed.
+ if(fieldPairListCount != headerMetadata->iFieldList.Count())
+ {
+ User::Leave(KErrArgument);
+ }
+ for(TInt index=0; index < fieldPairListCount; index++)
+ {
+ if(index)
+ {
+ headerTableQuery.Append(KComma);
+ }
+ headerTableQuery.Append(KQuote);
+ headerTableQuery.Append(headerMetadata->iFieldList[index]->iName->Des());
+ headerTableQuery.Append(KColon);
+ headerTableQuery.Append(headerMetadata->iFieldList[index]->iName->Des());
+ columnIndex++;
+ }
+ headerTableQuery.Append(KGetHeaderSchema4);
+ }
+ else
+ {
+ headerTableQuery.Append(KGetHeaderSchema5);
+ columnIndex++;
+ }
+
+ // Prepare the query and Bind the values.
+ RSqlStatement sqlStmt;
+ CleanupClosePushL(sqlStmt);
+ User::LeaveIfError(sqlStmt.Prepare(iDatabaseRef, headerTableQuery));
+
+ TInt fieldIndex = 0;
+ for(; fieldIndex<columnIndex; fieldIndex++)
+ {
+ switch(headerMetadata->iFieldList[fieldIndex]->iType)
+ {
+ case ESqlInt:
+ BindIntL(sqlStmt, fieldIndex, aHeaderFields->iFieldPairList[fieldIndex]->iFieldNumValue);
+ break;
+ case ESqlInt64:
+ BindInt64L(sqlStmt, fieldIndex, aHeaderFields->iFieldPairList[fieldIndex]->iFieldNumValue);
+ break;
+ case ESqlText:
+ if(!aHeaderFields->iFieldPairList[fieldIndex]->iFieldTextValue)
+ {
+ User::Leave(KErrArgument);
+ }
+ BindTextL(sqlStmt, fieldIndex, aHeaderFields->iFieldPairList[fieldIndex]->iFieldTextValue->Des());
+ break;
+ }
+ }
+
+ if(aHeaderFields->iFieldPairList[fieldIndex]->iFieldTextValue)
+ {
+ BindTextL(sqlStmt, columnIndex++, aHeaderFields->iFieldPairList[fieldIndex]->iFieldTextValue->Des());
+ }
+ else
+ {
+ User::Leave(KErrArgument);
+ }
+
+ BindIntL(sqlStmt, columnIndex++, UnmaskTMsvId(aEntryId));
+ BindIntL(sqlStmt, columnIndex, aHeaderFields->iUid.iUid);
+
+ // Execute the query and process the error.
+ TInt err = sqlStmt.Exec();
+ if(!err)
+ {
+ User::Leave(KErrGeneral);
+ }
+
+ User::LeaveIfError(err);
+ CleanupStack::PopAndDestroy(2); //sqlStmt, headerTableQuery
+ }
+
+
+
+
+
+
+
+
+/*
+ * CheckStdColumnsAdded()
+ *
+ * @param aFieldName: Column name
+ * @return aColStatus: Column status flag.
+ *
+ * The function checks the existence of standard header fields
+ * as defined in CMsvHeaderStore::TCommonHeaderField in the
+ * header table and sets the appropriate flag n aColStatus.
+ */
+void CMsvMessageDBAdapter::CheckStdColumnsAdded(HBufC* aFieldName, TInt& aColStatus)
+ {
+ // If it is a standard FORM field...
+ if(!aFieldName->Des().Compare(_L("From")))
+ {
+ aColStatus |= CMsvHeaderStore::EFrom;
+ }
+ else
+ {
+ if(!aFieldName->Des().Compare(_L("To")))
+ {
+ aColStatus |= CMsvHeaderStore::ETo;
+ }
+ else
+ {
+ if(!aFieldName->Des().Compare(_L("CC")))
+ {
+ aColStatus |= CMsvHeaderStore::ECC;
+ }
+ else
+ {
+ if(!aFieldName->Des().Compare(_L("BCC")))
+ {
+ aColStatus |= CMsvHeaderStore::EBCC;
+ }
+ else
+ {
+ if(!aFieldName->Des().Compare(_L("Subject")))
+ {
+ aColStatus |= CMsvHeaderStore::ESubject;
+ }
+ } // BCC
+ } // CC
+ } // To
+ } // From
+ }
+
+
+
+
+
+
+
+
+/*
+ * DeleteHeaderEntryL()
+ *
+ * @param aMtmId: MtmId to identify the header table.
+ * @param aEntryId: Entry Id.
+ *
+ * The function deletes the header entry from the corresponding
+ * header table. If multiple rows are associated with the header
+ * entry, all such rows are deleted from the header table.
+ */
+void CMsvMessageDBAdapter::DeleteHeaderEntryL(const TUid& aMtmId, TMsvId aEntryId)
+ {
+ TInt err = KErrNone;
+ _LIT16(KGetHeaderSchema1, "DELETE FROM DB");
+ _LIT16(KGetHeaderSchema2, ".HEADER_");
+ _LIT16(KGetHeaderSchema3, " WHERE id = :id;");
+
+ // Leave with KErrNotFound, if the header table does not exist.
+ User::LeaveIfError(GetMetadataEntryIndex(aMtmId));
+
+ RBuf headerTableQuery;
+ CleanupClosePushL(headerTableQuery);
+ headerTableQuery.CreateL(KMaxQueryLength);
+
+ // This creates following string...
+ // Ex: DELETE FROM DB<driveId>.HEADER_<mtmId> WHERE id = :id;
+ headerTableQuery.Append(KGetHeaderSchema1);
+ headerTableQuery.AppendNum(GetDriveId(aEntryId));
+ headerTableQuery.Append(KGetHeaderSchema2);
+ headerTableQuery.AppendNum(aMtmId.iUid);
+ headerTableQuery.Append(KGetHeaderSchema3);
+
+ //Prepare a statement for the query.
+ RSqlStatement sqlStmt;
+ CleanupClosePushL(sqlStmt);
+
+ MSG_WRAP_DB_ERROR(sqlStmt.Prepare(iDatabaseRef, headerTableQuery));
+ MSG_WRAP_DB_LEAVE(BindIntL(sqlStmt, 0, UnmaskTMsvId(aEntryId)));
+
+ // Execute the query and handle the error.
+ err = sqlStmt.Exec();
+ if(!err)
+ {
+ User::Leave(KErrNotFound);
+ }
+
+ MSG_WRAP_DB_ERROR(err);
+ CleanupStack::PopAndDestroy(2); //sqlStmt, headerTableQuery
+ }
+
+
+
+
+/*
+ * CopyHeaderEntryL()
+ *
+ * @param aMtmId The MTM UId to identify the header table.
+ * @param aSrcEntryId The source entry's TMsvId.
+ * @param aDestEntryId The destination source entry's TMsvId.
+ * @return None.
+ * @leave KErrNoMemory while creating the buffer for the query.
+ *
+ *
+ * The function copies header entry(s) of an entry from the corresponding
+ * header table. If multiple rows are associated with the entry's TMsvId,
+ * then copies of all such rows are made.
+ * This is
+ */
+void CMsvMessageDBAdapter::CopyHeaderEntryL(const TUid& aMtmId, const TMsvId& aSrcEntryId, const TMsvId& aDestEntryId)
+ {
+ _LIT(KComma, ", ");
+ _LIT(KDelimiter, ";");
+ _LIT(KQuote, "\"");
+ _LIT(KCopyHeader1, "INSERT INTO DB"); // INSERT INTO DB1
+ _LIT(KCopyHeader2, ".HEADER_"); // .HEADER_100
+ _LIT(KCopyHeader3, " (id, uid"); // (id, uid, field3, ...
+ _LIT(KCopyHeader4, ") SELECT "); // ) SELECT aDestEntryId
+ _LIT(KCopyHeader5, ",uid"); // , uid, field3, ...
+ _LIT(KCopyHeader6, " FROM HEADER_"); // FROM HEADER_100
+ _LIT(KCopyHeader7, " WHERE id = "); // WHERE id = aSrcEntryId;
+
+ // Get metadata for the appropriate header
+ TInt driveId = GetDriveId(aSrcEntryId);
+ TInt metadataEntryIndex = GetMetadataEntryIndex(aMtmId);
+ User::LeaveIfError(metadataEntryIndex);
+
+ /*
+ We're looking to construct a query that looks like:
+ INSERT INTO DB<driveId>.HEADER_<mtmId> (id, uid, field3...)
+ SELECT aDestEntryId, uid, field3...
+ WHERE id = aSrcEntryId;
+ */
+
+ // Start query construction.
+ RBuf headerTableQuery;
+ CleanupClosePushL(headerTableQuery);
+ headerTableQuery.CreateL(KMaxQueryLength);
+ headerTableQuery.Append(KCopyHeader1);
+ headerTableQuery.AppendNum(driveId);
+ headerTableQuery.Append(KCopyHeader2);
+ headerTableQuery.AppendNum(aMtmId.iUid);
+ headerTableQuery.Append(KCopyHeader3);
+
+
+ // Append column names to the query string.
+ // Column names are stored in the header metadata structure.
+ // We wrap the column names in quotes in case the column names are SQLite keywords. Eg: "From".
+ CHeaderMetaData* headerMetadata = iHeaderDataList[metadataEntryIndex];
+ RPointerArray<CFieldClass>& fieldList = headerMetadata->iFieldList;
+ TInt fieldListCount = fieldList.Count();
+ TInt index = 0;
+ for(; index < fieldListCount; ++index)
+ {
+ headerTableQuery.Append(KComma);
+ headerTableQuery.Append(KQuote);
+ headerTableQuery.Append(fieldList[index]->iName->Des());
+ headerTableQuery.Append(KQuote);
+ }
+
+ headerTableQuery.Append(KCopyHeader4);
+ headerTableQuery.AppendNum(UnmaskTMsvId(aDestEntryId));
+ headerTableQuery.Append(KCopyHeader5);
+
+ for(index = 0; index < fieldListCount; ++index)
+ {
+ headerTableQuery.Append(KComma);
+ headerTableQuery.Append(KQuote);
+ headerTableQuery.Append(fieldList[index]->iName->Des());
+ headerTableQuery.Append(KQuote);
+ }
+
+ headerTableQuery.Append(KCopyHeader6);
+ headerTableQuery.AppendNum(aMtmId.iUid);
+ headerTableQuery.Append(KCopyHeader7);
+ headerTableQuery.AppendNum(UnmaskTMsvId(aSrcEntryId));
+
+ headerTableQuery.Append(KDelimiter);
+
+ TInt err = iDatabaseRef.Exec(headerTableQuery);
+ if(!err)
+ { // 0 returned by Exec is due to SELECT part of the query not returning any result to the INSERT part.
+ User::Leave(KErrNotFound);
+ }
+
+ if(KSqlErrConstraint == err)
+ {
+ User::Leave(KErrAlreadyExists);
+ }
+
+ MSG_WRAP_DB_ERROR(err);
+ CleanupStack::PopAndDestroy(); //headerTableQuery
+ }
+
+
+
+/*
+ * CheckHeaderTableAndStdColumnFields()
+ *
+ * @param aMtmId: MtmId for related Header table
+ * @param aHeaderFilelds: Header field specified in search query
+ *
+ * Check HeaderTable And StdColumn are exist.
+ */
+TBool CMsvMessageDBAdapter::CheckHeaderTableAndStdColumnFields(const TUid& aMtmId, TInt aHeaderFields)
+ {
+ TBool flag = EFalse;
+
+ TInt index = GetMetadataEntryIndex(aMtmId);
+ if(index != KErrNotFound)
+ {
+ // needs to check all standard columns given in a query are avilble in header table
+ if( (iHeaderDataList[index]->iStdColumnStatus & aHeaderFields) == aHeaderFields)
+ {
+ flag = ETrue;
+ }
+ }
+ return flag;
+ }
+
+
+
+
+TBool CMsvMessageDBAdapter::DoesAnyStoreExistsL(TMsvId aId, TUid aMtmId)
+ {
+ _LIT16(KCheckHeaderTableQuery1, "SELECT COUNT(*) FROM DB");
+ _LIT16(KCheckHeaderTableQuery2, ".HEADER_");
+ _LIT16(KCheckHeaderTableQuery3, " WHERE id = ");
+ _LIT16(KSemiColon, ";");
+
+ RBuf headerTableQuery;
+ CleanupClosePushL(headerTableQuery);
+ headerTableQuery.CreateL(KMaxQueryLength);
+
+ headerTableQuery.Append(KCheckHeaderTableQuery1);
+ headerTableQuery.AppendNum(GetDriveId(aId));
+ headerTableQuery.Append(KCheckHeaderTableQuery2);
+ headerTableQuery.AppendNum(aMtmId.iUid);
+ headerTableQuery.Append(KCheckHeaderTableQuery3);
+ headerTableQuery.AppendNum(UnmaskTMsvId(aId));
+ headerTableQuery.Append(KSemiColon);
+
+ TInt count = 0;
+ TSqlScalarFullSelectQuery query(iDatabaseRef);
+ TRAPD(err, count = query.SelectIntL(headerTableQuery));
+ CleanupStack::PopAndDestroy(); // headerTableQuery
+
+ if(err == KErrNone && count > 0)
+ {
+ return ETrue;
+ }
+ return EFalse;
+ }