messagingfw/msgsrvnstore/server/src/msvmessagedbadapter.cpp
changeset 0 8e480a14352b
equal deleted inserted replaced
-1:000000000000 0:8e480a14352b
       
     1 // Copyright (c) 2001-2009 Nokia Corporation and/or its subsidiary(-ies).
       
     2 // All rights reserved.
       
     3 // This component and the accompanying materials are made available
       
     4 // under the terms of "Eclipse Public License v1.0"
       
     5 // which accompanies this distribution, and is available
       
     6 // at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     7 //
       
     8 // Initial Contributors:
       
     9 // Nokia Corporation - initial contribution.
       
    10 //
       
    11 // Contributors: Neeraj Nayan
       
    12 //
       
    13 // Description: The class provides interface to read/write header and
       
    14 // body entry from the database.
       
    15 //
       
    16 
       
    17 
       
    18 /**
       
    19  * HEADER FILES
       
    20  */
       
    21 
       
    22 #include "msvpreferreddrivelist.h"		// Preferred drive list.
       
    23 #include "msvmessagedbadapter.h"		// Header file for this class.
       
    24 #include "msvindexadapter.h"			// For GetDriveId()
       
    25 #include "msvdbadapter.h"				// For BindL() and handle to CMsvDBAdapter while constructing
       
    26 
       
    27 
       
    28 /**
       
    29  * LITERAL DEFINITION
       
    30  */
       
    31 const TInt KMaxQueryLength = 1000;
       
    32 
       
    33 // Macros to wrap DB error. KErrGeneral is thrown for
       
    34 // all DB error. Make sure you declare 'err' before 
       
    35 // using these macros.
       
    36 #define MSG_WRAP_DB_LEAVE(x) TRAP(err, x); \
       
    37 							 if(err<=KSqlErrGeneral) User::Leave(KErrGeneral); \
       
    38 							 User::LeaveIfError(err);							 
       
    39 
       
    40 #define MSG_WRAP_DB_ERROR(errTxt) err = errTxt; \
       
    41 							if(err<=KSqlErrGeneral) User::Leave(KErrGeneral); \
       
    42 							else User::LeaveIfError(err);
       
    43 
       
    44 
       
    45 
       
    46 /**
       
    47  * MEMBER FUNCTIONS OF CMsvMessageDBAdapter
       
    48  */
       
    49 
       
    50 
       
    51 
       
    52 /**
       
    53  * CMsvMessageDBAdapter()
       
    54  * The default Constructor. 
       
    55  */
       
    56 CMsvMessageDBAdapter::CMsvMessageDBAdapter(CMsvDBAdapter* aDBAdapter):
       
    57 		iDBAdapter(aDBAdapter), iDatabaseRef(aDBAdapter->iDatabase)
       
    58 	{
       
    59 	}
       
    60 
       
    61 
       
    62 
       
    63 
       
    64 /**
       
    65  * ~CMsvMessageDBAdapter()
       
    66  * The default Destructor. 
       
    67  */
       
    68 CMsvMessageDBAdapter::~CMsvMessageDBAdapter()
       
    69 	{
       
    70 	iHeaderDataList.ResetAndDestroy();
       
    71 	}
       
    72 
       
    73 
       
    74 
       
    75 
       
    76 /**
       
    77  * NewL()
       
    78  * 
       
    79  * @param CMsvDBAdapter* : A reference to database adapter class.
       
    80  * @return CMsvMessageDBAdapter* : A reference to CMsvMessageDBAdapter
       
    81  * class.
       
    82  * 
       
    83  * Allocates and constructs a new CMsvMessageDBAdapter object.
       
    84  */
       
    85 CMsvMessageDBAdapter* CMsvMessageDBAdapter::NewL(CMsvDBAdapter* aDBAdapter,TBool aDbInCurrentDrive)
       
    86 	{
       
    87 	//Create the DBAdapter object.
       
    88 	CMsvMessageDBAdapter* self = new(ELeave) CMsvMessageDBAdapter(aDBAdapter);
       
    89 	CleanupStack::PushL(self);
       
    90 	
       
    91 	self->ConstructL(aDbInCurrentDrive);
       
    92 	
       
    93 	CleanupStack::Pop();
       
    94 	return self;
       
    95 	}
       
    96 
       
    97 
       
    98 /*
       
    99  * ConstructL()
       
   100  * 
       
   101  * The function populates metadata structure. A metadata structure
       
   102  * contains information about metadata table which is later used to 
       
   103  * create and update header entries. The structure is updated whenever
       
   104  * a new header table is created in the database.
       
   105  */ 
       
   106 void CMsvMessageDBAdapter::ConstructL(TBool aDbInCurrentDrive)
       
   107 	{
       
   108 	// check if the operation are required for only single drive
       
   109 	// This is required for message store converter as it works only on 1 drive.
       
   110 	
       
   111 	if(aDbInCurrentDrive)
       
   112 		{
       
   113 		iNonAttachedDrive = ETrue;
       
   114 		UpdateMetadataStructureL(KCurrentDriveId);
       
   115 		}
       
   116 	else
       
   117 		{
       
   118 		iNonAttachedDrive = EFalse;
       
   119 		for(TInt index=1; index<KMaxNumberOfDrives; index++)
       
   120 			{
       
   121 			if((*(iDBAdapter->iDatabasePresent))[index])
       
   122 				{
       
   123 				UpdateMetadataStructureL(index);
       
   124 				}
       
   125 			}
       
   126 		}
       
   127 	}
       
   128 	
       
   129 	
       
   130 	
       
   131 	
       
   132 
       
   133 void CMsvMessageDBAdapter::UpdateMetadataStructureL(TUint aDriveId)
       
   134 	{
       
   135 	TInt err;
       
   136 	_LIT(KCheckHeaderTableQuery0, "SELECT tbl_name FROM DB");
       
   137 	_LIT(KCheckHeaderTableQuery1, ".sqlite_master WHERE type = 'table' AND tbl_name LIKE 'Header_%';");
       
   138 	
       
   139 	// 1. Get all the header table names from the SQLITE_MASTER table.
       
   140 	RBuf headerTableQuery;
       
   141 	CleanupClosePushL(headerTableQuery);
       
   142 	headerTableQuery.CreateL(KMaxQueryLength);
       
   143 
       
   144 	// Create the query string.
       
   145 	if(!iNonAttachedDrive)
       
   146 		{
       
   147 		headerTableQuery.Append(KCheckHeaderTableQuery0);
       
   148 		headerTableQuery.AppendNum(aDriveId);
       
   149 		headerTableQuery.Append(KCheckHeaderTableQuery1);
       
   150 		}
       
   151 	else
       
   152 		{
       
   153 		_LIT(KCheckHeaderTableQuery2, "SELECT tbl_name FROM sqlite_master WHERE type = 'table' AND tbl_name LIKE 'Header_%';");
       
   154 		headerTableQuery.Append(KCheckHeaderTableQuery2);
       
   155 		}
       
   156 
       
   157 	//Prepare a statement for the query.
       
   158 	RSqlStatement sqlStmt;
       
   159 	RPointerArray<HBufC> headerTableNames;
       
   160 	CleanupClosePushL(sqlStmt);
       
   161 	CleanupClosePushL(headerTableNames);
       
   162 
       
   163 	MSG_WRAP_DB_ERROR(sqlStmt.Prepare(iDatabaseRef, headerTableQuery));
       
   164 	// Prepare a list of header table names.
       
   165 	HBufC* tableName = NULL;
       
   166 	while((err = sqlStmt.Next()) == KSqlAtRow)
       
   167 		{
       
   168 		MSG_WRAP_DB_LEAVE(tableName = ColumnTextL(sqlStmt, 0));
       
   169 		CleanupStack::PushL(tableName);
       
   170 		
       
   171 		headerTableNames.AppendL(tableName);
       
   172 		CleanupStack::Pop();
       
   173 		}
       
   174 	MSG_WRAP_DB_ERROR(err);
       
   175 
       
   176 	// 2. For each tablename read the table structure from the DB.
       
   177 	_LIT(KGetHeaderSchema1, "SELECT * FROM DB");
       
   178 	_LIT(KGetHeaderSchema2, ".");
       
   179 	_LIT(KGetHeaderSchema3, ";");
       
   180 	_LIT(KGetHeaderSchema4, "SELECT * FROM ");
       
   181 	
       
   182 	while(headerTableNames.Count())
       
   183 		{
       
   184 		HBufC* tableName = headerTableNames[0];
       
   185 		
       
   186 		TLex mtmId(tableName->Mid(tableName->Locate('_')+1));
       
   187 		TUid uid;
       
   188 		mtmId.Val(uid.iUid);
       
   189 		
       
   190 		// Don't insert meta-data if we already have it for this mtmId.
       
   191 		if(GetMetadataEntryIndex(uid) != KErrNotFound)
       
   192 			{
       
   193 			delete tableName;
       
   194 			headerTableNames.Remove(0);
       
   195 			continue;
       
   196 			}
       
   197 		
       
   198 		headerTableQuery.Zero();
       
   199 		sqlStmt.Close();
       
   200 
       
   201 		if(iNonAttachedDrive) // this is for msg converter. it refers to  a single drive
       
   202 			{
       
   203 			headerTableQuery.Append(KGetHeaderSchema4);
       
   204 			}
       
   205 		else
       
   206 			{
       
   207 			// This creates following string...
       
   208 			// "SELECT * FROM DB<driveId>.HEADER_<MtmId>;" 
       
   209 	
       
   210 			headerTableQuery.Append(KGetHeaderSchema1);
       
   211 			headerTableQuery.AppendNum(aDriveId);
       
   212 			headerTableQuery.Append(KGetHeaderSchema2);
       
   213 			}
       
   214 		
       
   215 		headerTableQuery.Append(tableName->Des());
       
   216 		headerTableQuery.Append(KGetHeaderSchema3);
       
   217 
       
   218 		MSG_WRAP_DB_ERROR(sqlStmt.Prepare(iDatabaseRef, headerTableQuery));
       
   219 
       
   220 		CHeaderMetaData* headerMetaData = new(ELeave) CHeaderMetaData();
       
   221 		CleanupStack::PushL(headerMetaData);
       
   222 			
       
   223 		TInt metadataIndex = 0;
       
   224 		headerMetaData->iMtmId.iUid = uid.iUid;
       
   225 		
       
   226 		TInt colCnt = sqlStmt.ColumnCount();
       
   227 		// Record the header structure, ignore first two fields (Id, UID).
       
   228 		TRAPD(err, 
       
   229 			for(TInt index=2; index<colCnt; index++)
       
   230 				{			
       
   231 				CFieldClass* fieldObj = new(ELeave) CFieldClass();
       
   232 				CleanupStack::PushL(fieldObj);
       
   233 				
       
   234 				User::LeaveIfError(sqlStmt.DeclaredColumnType(index, fieldObj->iType));
       
   235 				TPtrC fieldName;
       
   236 				User::LeaveIfError(sqlStmt.ColumnName(index, fieldName));
       
   237 				fieldObj->iName = fieldName.AllocL();
       
   238 				headerMetaData->iFieldList.AppendL(fieldObj);
       
   239 				CheckStdColumnsAdded(fieldObj->iName, headerMetaData->iStdColumnStatus);
       
   240 				
       
   241 				CleanupStack::Pop(fieldObj);
       
   242 				}
       
   243 			);
       
   244 		if(err)
       
   245 			{			
       
   246 			MSG_WRAP_DB_ERROR(err);
       
   247 			}
       
   248 		iHeaderDataList.InsertL(headerMetaData, metadataIndex);
       
   249 		CleanupStack::Pop();			// headerMetaData
       
   250 
       
   251 		delete tableName;
       
   252 		headerTableNames.Remove(0);
       
   253 		}		// while(headerTableNames.Count())
       
   254 
       
   255 	CleanupStack::PopAndDestroy(3); // headerTableQuery, sqlStmt, headerTableNames
       
   256 	}
       
   257 
       
   258 
       
   259 
       
   260 
       
   261 /*
       
   262  * IsHeaderTableExistsL()
       
   263  *
       
   264  * @param aMtmId: MTM ID of the header table.
       
   265  * @return TBool: ETrue/EFalse
       
   266  * 
       
   267  * The function returns ETrue if the header table for 
       
   268  * passed MTM ID already exists in the database. The 
       
   269  * function works for only message server current drive.
       
   270  */
       
   271 TBool CMsvMessageDBAdapter::IsHeaderTableExistsL(const TUid& aMtmId)
       
   272 	{
       
   273 	_LIT16(KCheckHeaderTableQuery1, "SELECT COUNT(*) FROM DB");
       
   274 	_LIT16(KCheckHeaderTableQuery2, ".SQLITE_MASTER WHERE NAME LIKE 'Header_");
       
   275 	_LIT16(KCheckHeaderTableQuery3, "';");
       
   276 	
       
   277 	RBuf headerTableQuery;
       
   278 	CleanupClosePushL(headerTableQuery);
       
   279 	headerTableQuery.CreateL(KMaxQueryLength);
       
   280 
       
   281 	// Create the query string.
       
   282 	// Ex: SELECT COUNT(*) FROM DB<drive-id>.SQLITE_MASTER WHERE 
       
   283 	// NAME LIKE 'Header_<mtm-id>';
       
   284 	headerTableQuery.Append(KCheckHeaderTableQuery1);
       
   285 	headerTableQuery.AppendNum(KCurrentDriveId);
       
   286 	headerTableQuery.Append(KCheckHeaderTableQuery2);
       
   287 	headerTableQuery.AppendNum(aMtmId.iUid);
       
   288 	headerTableQuery.Append(KCheckHeaderTableQuery3);
       
   289 
       
   290 	TInt count = 0;
       
   291 	TSqlScalarFullSelectQuery query(iDatabaseRef);
       
   292 	TRAPD(err, count = query.SelectIntL(headerTableQuery));
       
   293 	CleanupStack::PopAndDestroy();			// headerTableQuery
       
   294 	
       
   295 	// If the table exists, count will have value 1.
       
   296 	if(err == KErrNone && count == 1)
       
   297 		{
       
   298 		return ETrue;
       
   299 		}
       
   300 	else
       
   301 		{
       
   302 		return EFalse;
       
   303 		}
       
   304 	}
       
   305 
       
   306 
       
   307 
       
   308 
       
   309 
       
   310 
       
   311 /*
       
   312  * CreateHeaderTableL()
       
   313  *
       
   314  * @param aMtmId: MTM ID of the table.
       
   315  * @param aFieldDetails: Table structure details.
       
   316  * @return aErrorMessage: Error message text, in case
       
   317  * header table creation fails.
       
   318  *
       
   319  * The function creates separate header table for each MTM type. 
       
   320  * If the table already exist, it simply exits without returning an error.
       
   321  */
       
   322 void CMsvMessageDBAdapter::CreateHeaderTableL(const TUid& aMtmId, const RPointerArray<CFieldPair>& aFieldDetails, TPtrC& aErrorMessage)
       
   323 	{
       
   324 	RBuf headerTableQuery;
       
   325 	CleanupClosePushL(headerTableQuery);
       
   326 	headerTableQuery.CreateL(KMaxQueryLength);	
       
   327 	
       
   328 	CHeaderMetaData* headerMetaData = new(ELeave) CHeaderMetaData();
       
   329 	CleanupStack::PushL(headerMetaData);
       
   330 		
       
   331 	TInt metadataIndex = GetMetadataEntryIndex(aMtmId);
       
   332 	// Insert a new metadata entry if it doesn't already exist.
       
   333 	if(metadataIndex == KErrNotFound)
       
   334 		{
       
   335 		iHeaderDataList.InsertL(headerMetaData, 0);
       
   336 		}
       
   337 	headerMetaData->iMtmId = aMtmId;
       
   338 	
       
   339 	if(iNonAttachedDrive)
       
   340 		{
       
   341 		_LIT16(KCreateHeaderTableQuery1, "CREATE TABLE IF NOT EXISTS ");
       
   342 		_LIT16(KCreateHeaderTableQuery2, "Header_");
       
   343 		
       
   344 		// This creates following string...
       
   345 		// "CREATE TABLE IF NOT EXISTS DB<DriveId>.Header_<MtmId> ( id INT, uid INT," 
       
   346 		headerTableQuery.Append(KCreateHeaderTableQuery1);
       
   347 		headerTableQuery.Append(KCreateHeaderTableQuery2);
       
   348 		}
       
   349 	else
       
   350 		{
       
   351 		_LIT16(KCreateHeaderTableQuery1, "CREATE TABLE IF NOT EXISTS DB");
       
   352 		_LIT16(KCreateHeaderTableQuery2, ".Header_");
       
   353 		// "CREATE TABLE IF NOT EXISTS DB<DriveId>.Header_<MtmId> ( id INT, uid INT," 
       
   354 		headerTableQuery.Append(KCreateHeaderTableQuery1);
       
   355 		headerTableQuery.AppendNum(KCurrentDriveId);
       
   356 		headerTableQuery.Append(KCreateHeaderTableQuery2);
       
   357 		}
       
   358 	_LIT16(KCreateHeaderTableQuery3, " (id INT, uid INT, ");
       
   359 	_LIT16(KCreateHeaderTableQuery4, "Details TEXT, PRIMARY KEY (id, uid));");	
       
   360 	aErrorMessage.Set(_L(""));
       
   361 	headerTableQuery.AppendNum(aMtmId.iUid);
       
   362 	headerTableQuery.Append(KCreateHeaderTableQuery3);
       
   363 	
       
   364 	// Add the column details to the query.
       
   365 	TRAPD(err, DoCreateHeaderTableCreationQueryL(aFieldDetails, headerTableQuery, headerMetaData));
       
   366 	if(err)
       
   367 		{
       
   368 		CleanupStack::Pop(headerMetaData);
       
   369 		delete headerMetaData;
       
   370 		iHeaderDataList.Remove(0);
       
   371 		User::Leave(err);
       
   372 		}
       
   373 	headerTableQuery.Append(KCreateHeaderTableQuery4);
       
   374 	
       
   375 	// Execute the query.
       
   376 	// Ignore error if table already exist.
       
   377 	err = iDatabaseRef.Exec(headerTableQuery);
       
   378 	if(err < 0)
       
   379 		{
       
   380 		aErrorMessage.Set(iDatabaseRef.LastErrorMessage());
       
   381 
       
   382 		CleanupStack::Pop(headerMetaData);
       
   383 		delete headerMetaData;
       
   384 		iHeaderDataList.Remove(0);
       
   385 		MSG_WRAP_DB_ERROR(err);
       
   386 		}
       
   387 
       
   388 	CleanupStack::Pop(headerMetaData);					// headerMetaData
       
   389 	if(metadataIndex != KErrNotFound)
       
   390 		delete headerMetaData;
       
   391 	CleanupStack::PopAndDestroy();						// headerTableQuery
       
   392 	}
       
   393 
       
   394 
       
   395 
       
   396 
       
   397 
       
   398 
       
   399 
       
   400 /*
       
   401  * DoCreateHeaderTableCreationQueryL()
       
   402  *
       
   403  * @param aFieldDetails: Table structure details.
       
   404  * @return aHeaderTableQuery: Header-table creation query string.
       
   405  * @return aHeaderMetaData: Header metadata structure.
       
   406  *
       
   407  * The function appends the column details to the header
       
   408  * creation query string. It also updates the metadata structure
       
   409  * for the header entry.
       
   410  */
       
   411 void CMsvMessageDBAdapter::DoCreateHeaderTableCreationQueryL(const RPointerArray<CFieldPair>& aFieldDetails, RBuf& aHeaderTableQuery, CHeaderMetaData*& aHeaderMetaData)
       
   412 	{
       
   413 	_LIT16(KIntField, " INT, ");
       
   414 	_LIT16(KTextField, " TEXT, ");
       
   415 	_LIT16(KDateField, " INT64, ");
       
   416 	_LIT16(KQuote, "\"");
       
   417 	
       
   418 	CFieldClass *fieldObj = NULL;
       
   419 	// Browse through each column names in the field-list.
       
   420 	TInt fieldDetailsCount = aFieldDetails.Count();
       
   421 	for(TInt fieldIndex=0; fieldIndex<fieldDetailsCount; fieldIndex++)
       
   422 		{			
       
   423 		if(!aFieldDetails[fieldIndex]->iFieldName)
       
   424 			{
       
   425 			User::Leave(KErrArgument);
       
   426 			}
       
   427 		
       
   428 		// Create a metadata entry.
       
   429 		fieldObj = new(ELeave) CFieldClass;
       
   430 		CleanupStack::PushL(fieldObj);
       
   431 		fieldObj->iName = aFieldDetails[fieldIndex]->iFieldName->Des().AllocL();
       
   432 		
       
   433 		// Append column name under quotes.
       
   434 		aHeaderTableQuery.Append(KQuote);
       
   435 		aHeaderTableQuery.Append(aFieldDetails[fieldIndex]->iFieldName->Des());
       
   436 		aHeaderTableQuery.Append(KQuote);
       
   437 		
       
   438 		// Append appropriate column type.
       
   439 		switch(aFieldDetails[fieldIndex]->iFieldType)
       
   440 			{
       
   441 			case EIntegerField:
       
   442 				aHeaderTableQuery.Append(KIntField);
       
   443 				fieldObj->iType = ESqlInt;
       
   444 				break;
       
   445 			case ETextField:
       
   446 				aHeaderTableQuery.Append(KTextField);
       
   447 				fieldObj->iType = ESqlText;
       
   448 				break;
       
   449 			case EDateField:
       
   450 				aHeaderTableQuery.Append(KDateField);
       
   451 				fieldObj->iType = ESqlInt64;
       
   452 				break;
       
   453 			default:
       
   454 				User::Leave(KErrArgument);
       
   455 				break;
       
   456 			}
       
   457 		
       
   458 		aHeaderMetaData->iFieldList.AppendL(fieldObj);
       
   459 		CleanupStack::Pop();			// fieldObj
       
   460 		
       
   461 		// Update the status of standard header columns 
       
   462 		// in the metadata structure.
       
   463 		CheckStdColumnsAdded(aFieldDetails[fieldIndex]->iFieldName, aHeaderMetaData->iStdColumnStatus);
       
   464 		}		// for(TInt fieldIndex=0; fieldIndex<aFieldDetails.Count(); fieldIndex++)
       
   465 
       
   466 
       
   467 	// Adding the 'Details' column to the query string.
       
   468 	_LIT(KDetails, "Details");
       
   469 	fieldObj = new(ELeave) CFieldClass;
       
   470 	CleanupStack::PushL(fieldObj);
       
   471 
       
   472 	fieldObj->iType = ESqlText;
       
   473 	fieldObj->iName = KDetails().AllocL();
       
   474 	aHeaderMetaData->iFieldList.AppendL(fieldObj);		// Added for Details column.
       
   475 
       
   476 	CleanupStack::Pop();								// fieldObj
       
   477 	}
       
   478 	
       
   479 	
       
   480 	
       
   481 	
       
   482 	
       
   483 
       
   484 /*
       
   485  * CreateHeaderEntryL()
       
   486  *
       
   487  * @param aMtmId: MtmId to identify the header table.
       
   488  * @param aEntryId: Metadata Entry Id.
       
   489  * @param aFieldPairList: Header details.
       
   490  *
       
   491  * The function creates a new header entry in the header
       
   492  * table. The header details passed to the function in 
       
   493  * aFieldPairList should either have values for all the
       
   494  * columns in the header table or should have value for
       
   495  * only one column (The default column "Details").
       
   496  *
       
   497  * The function can accept multiple header portion (each 
       
   498  * associated with a unique UID. These header portion will 
       
   499  * be stored in a separate row in the header table.
       
   500  */
       
   501 
       
   502 void CMsvMessageDBAdapter::CreateHeaderEntryL(const TUid& aMtmId, TMsvId aEntryId, const RPointerArray<CHeaderFields>& aFieldPairList)
       
   503 	{
       
   504 	TInt err = KErrNone;
       
   505 	
       
   506 	// Get the metadata entry index for the MTM.
       
   507 	TInt metadataEntryIndex = GetMetadataEntryIndex(aMtmId);
       
   508 	User::LeaveIfError(metadataEntryIndex);
       
   509 	TBool isLocalTransaction = EFalse;
       
   510 	
       
   511 	// Open the DB transaction, if it is not already open.
       
   512 	if(!iDBAdapter->isTransactionOpen && !iNonAttachedDrive)
       
   513 		{
       
   514 		MSG_WRAP_DB_LEAVE(iDBAdapter->BeginTransactionL());
       
   515 		isLocalTransaction = ETrue;
       
   516 		}
       
   517 	
       
   518 	// For each entry in header details, create a separate
       
   519 	// row in the header table.
       
   520 	TInt fieldPairListCount = aFieldPairList.Count();
       
   521 	TRAP(err, 
       
   522 		for(TInt index=0; index<fieldPairListCount; index++)
       
   523 			{
       
   524 			CHeaderFields* headerFields = aFieldPairList[index];
       
   525 			DoInsertHeaderEntryL(aMtmId, aEntryId, headerFields, metadataEntryIndex);
       
   526 			}		
       
   527 		);
       
   528 
       
   529 	// Handle error and close the transaction.
       
   530 	if(err && !iNonAttachedDrive)
       
   531 		{
       
   532 		if(isLocalTransaction)
       
   533 			{
       
   534 			iDBAdapter->RollbackTransactionL();
       
   535 			}
       
   536 		MSG_WRAP_DB_ERROR(err);
       
   537 		}
       
   538 	else if (!iNonAttachedDrive)
       
   539 		{
       
   540 		if(isLocalTransaction)
       
   541 			{
       
   542 			MSG_WRAP_DB_LEAVE(iDBAdapter->CommitTransactionL());
       
   543 			}
       
   544 		}
       
   545 	}
       
   546 
       
   547 
       
   548 
       
   549 
       
   550 
       
   551 
       
   552 
       
   553 /*
       
   554  * DoInsertHeaderEntryL()
       
   555  *
       
   556  * @param aMtmId: MtmId to identify the header table.
       
   557  * @param aEntryId: Metadata Entry Id.
       
   558  * @param aHeaderFields: Header entry details.
       
   559  * @param aMetadataEntryIndex: Header table metadata.
       
   560  *
       
   561  * The function creates a new header entry in the header
       
   562  * table. The header details passed to the function in 
       
   563  * aHeaderFields should either have values for all the
       
   564  * columns in the header table or should have value for
       
   565  * only one column (The default column "Details").
       
   566  */
       
   567 void CMsvMessageDBAdapter::DoInsertHeaderEntryL(const TUid& aMtmId, TMsvId aEntryId, CHeaderFields* aHeaderFields, TInt aMetadataEntryIndex)
       
   568 	{
       
   569 	TInt err = KErrNone;
       
   570 	_LIT16(KGetHeaderSchema5, "INSERT INTO HEADER_");
       
   571 	_LIT16(KGetHeaderSchema1, "INSERT INTO DB");
       
   572 	_LIT16(KGetHeaderSchema2, ".HEADER_");
       
   573 	_LIT16(KGetHeaderSchema3, " VALUES (:id, :uid");
       
   574 	_LIT16(KGetHeaderSchema4, " (id, uid, Details) VALUES (:id, :uid, :details);");
       
   575 	_LIT16(KComma, ", :");
       
   576 	_LIT16(KDelimiter, ");");
       
   577 		
       
   578 	TInt KDefaultColumnSize = 1;
       
   579 	RBuf headerTableQuery;
       
   580 	CleanupClosePushL(headerTableQuery);
       
   581 	
       
   582 	// Create header entry creation query
       
   583 	// Ex: INSERT INTO DB<driveId>.HEADER_<mtmId>
       
   584 	headerTableQuery.CreateL(KMaxQueryLength);
       
   585 	if(!iNonAttachedDrive)
       
   586 		{
       
   587 		headerTableQuery.Append(KGetHeaderSchema1);
       
   588 		headerTableQuery.AppendNum(GetDriveId(aEntryId));
       
   589 		headerTableQuery.Append(KGetHeaderSchema2);		
       
   590 		}
       
   591 	else
       
   592 		{
       
   593 		headerTableQuery.Append(KGetHeaderSchema5);
       
   594 		}
       
   595 	headerTableQuery.AppendNum(aMtmId.iUid);
       
   596 	
       
   597 	// Append column names to the query string.
       
   598 	// Column names are stored in the header metadata structure.
       
   599 	CHeaderMetaData* headerMetadata = iHeaderDataList[aMetadataEntryIndex];	
       
   600 	TInt fieldPairListCount = aHeaderFields->iFieldPairList.Count();
       
   601 
       
   602 	// If header entry has more than one field...
       
   603 	if(KDefaultColumnSize < fieldPairListCount)
       
   604 		{
       
   605 		headerTableQuery.Append(KGetHeaderSchema3);		
       
   606 		
       
   607 		// Check if number of values passed for the header entry
       
   608 		// matches with the number of columns in the header table.
       
   609 		RPointerArray<CFieldClass>& fieldList = headerMetadata->iFieldList;		
       
   610 		if(fieldList.Count() != fieldPairListCount)
       
   611 			{
       
   612 			User::Leave(KErrArgument);
       
   613 			}
       
   614 		// Append column names to the query string.
       
   615 		TInt fieldListCount = fieldList.Count();
       
   616 		for(TInt fieldCount=0; fieldCount<fieldListCount; fieldCount++)
       
   617 			{
       
   618 			headerTableQuery.Append(KComma);
       
   619 			headerTableQuery.Append(fieldList[fieldCount]->iName->Des());		
       
   620 			}
       
   621 		headerTableQuery.Append(KDelimiter);
       
   622 		}		// if(KDefaultColumnSize < aHeaderFields->iFieldPairList.Count())
       
   623 	else
       
   624 		{
       
   625 		// If the header entry has just one field
       
   626 		// It must be the value for default column (Details).
       
   627 		headerTableQuery.Append(KGetHeaderSchema4);
       
   628 		}
       
   629 	
       
   630 	// Prepare the SQL statement for execution.
       
   631 	RSqlStatement sqlStmt;
       
   632 	CleanupClosePushL(sqlStmt);
       
   633 	MSG_WRAP_DB_ERROR(sqlStmt.Prepare(iDatabaseRef, headerTableQuery));
       
   634 
       
   635 	// Bind values against column names.
       
   636 	TInt fieldIndex = 0;
       
   637 	MSG_WRAP_DB_LEAVE(BindIntL(sqlStmt, fieldIndex++, UnmaskTMsvId(aEntryId)));
       
   638 	MSG_WRAP_DB_LEAVE(BindIntL(sqlStmt, fieldIndex++, aHeaderFields->iUid.iUid));
       
   639 	
       
   640 	TInt index = 0;
       
   641 	for(; index < fieldPairListCount-1; index++, fieldIndex++)
       
   642 		{
       
   643 		switch(headerMetadata->iFieldList[index]->iType)
       
   644 			{
       
   645 			case ESqlInt:
       
   646 				BindIntL(sqlStmt, fieldIndex, aHeaderFields->iFieldPairList[index]->iFieldNumValue);
       
   647 				break;
       
   648 			case ESqlInt64:
       
   649 				BindInt64L(sqlStmt, fieldIndex, aHeaderFields->iFieldPairList[index]->iFieldNumValue);
       
   650 				break;
       
   651 			case ESqlText:
       
   652 				if(!aHeaderFields->iFieldPairList[index]->iFieldTextValue)
       
   653 					{
       
   654 					User::Leave(KErrArgument);
       
   655 					}
       
   656 				BindTextL(sqlStmt, fieldIndex, aHeaderFields->iFieldPairList[index]->iFieldTextValue->Des());
       
   657 				break;
       
   658 			}
       
   659 		}
       
   660 	if(aHeaderFields->iFieldPairList[index]->iFieldTextValue)
       
   661 		{
       
   662 		BindTextL(sqlStmt, fieldIndex, aHeaderFields->iFieldPairList[index]->iFieldTextValue->Des());
       
   663 		}
       
   664 	else
       
   665 		{
       
   666 		User::Leave(KErrArgument);
       
   667 		}
       
   668 		
       
   669 	// Execute the query. Return KErrGeneral,
       
   670 	// if entry is not inserted.	
       
   671 	err = sqlStmt.Exec();
       
   672 	if(!err)
       
   673 		{
       
   674 		User::Leave(KErrGeneral);
       
   675 		}
       
   676 	if(err == KSqlErrConstraint)
       
   677 		{
       
   678 		User::Leave(KErrAlreadyExists);
       
   679 		}
       
   680 	MSG_WRAP_DB_ERROR(err);
       
   681 	CleanupStack::PopAndDestroy(2); //sqlStmt, headerTableQuery	
       
   682 	}
       
   683 
       
   684 
       
   685 
       
   686 
       
   687 
       
   688 
       
   689 
       
   690 
       
   691 /*
       
   692  * LoadHeaderEntryL()
       
   693  * 
       
   694  * @param aMtmId: MtmId to identify the header table.
       
   695  * @param aEntryId: Metadata Entry Id.
       
   696  * @return aFieldPairList: Header entry details.
       
   697  *
       
   698  * The function loads the header entry from the header
       
   699  * table. If the header entry has multiple row, all of 
       
   700  * them will be loaded and stored as a separate element 
       
   701  * of CHeaderFields.
       
   702  */	
       
   703 void CMsvMessageDBAdapter::LoadHeaderEntryL(const TUid& aMtmId, TMsvId aEntryId, RPointerArray<CHeaderFields>& aFieldPairList)
       
   704 	{
       
   705 	TInt err = KErrNone;
       
   706 	_LIT16(KGetHeaderSchema1, "SELECT uid");
       
   707 	_LIT16(KGetHeaderSchema2, " FROM DB");
       
   708 	_LIT16(KGetHeaderSchema3, ".HEADER_");
       
   709 	_LIT16(KGetHeaderSchema4, " WHERE id = :id;");
       
   710 	_LIT16(KComma, ", \"");
       
   711 	_LIT16(KQuote, "\"");
       
   712 	
       
   713 	aFieldPairList.Reset();
       
   714 	// Get the metadata entry index for the MTM.
       
   715 	TInt metadataEntryIndex = GetMetadataEntryIndex(aMtmId);
       
   716 	User::LeaveIfError(metadataEntryIndex);
       
   717 	RPointerArray<CFieldClass>&	fieldList = iHeaderDataList[metadataEntryIndex]->iFieldList;
       
   718 	
       
   719 	// Create header query string
       
   720 	// Ex: SELECT uid, 
       
   721 	RBuf headerTableQuery;
       
   722 	CleanupClosePushL(headerTableQuery);
       
   723 	headerTableQuery.CreateL(KMaxQueryLength);
       
   724 	headerTableQuery.Append(KGetHeaderSchema1);
       
   725 	
       
   726 	// Append header column name to the query.
       
   727 	TInt fieldListCount = fieldList.Count();
       
   728 	for(TInt index=0; index<fieldListCount; index++)
       
   729 		{
       
   730 		headerTableQuery.Append(KComma);
       
   731 		headerTableQuery.Append(fieldList[index]->iName->Des());		
       
   732 		headerTableQuery.Append(KQuote);
       
   733 		}
       
   734 	
       
   735 	// Append "FROM DB<driveId>.HEADER_<mtmId> WHERE ID = :ID;
       
   736 	headerTableQuery.Append(KGetHeaderSchema2);
       
   737 	headerTableQuery.AppendNum(GetDriveId(aEntryId));
       
   738 	headerTableQuery.Append(KGetHeaderSchema3);
       
   739 	headerTableQuery.AppendNum(aMtmId.iUid);
       
   740 	headerTableQuery.Append(KGetHeaderSchema4);
       
   741 	
       
   742 	//Prepare the SQL statement.
       
   743 	RSqlStatement sqlStmt;
       
   744 	CleanupClosePushL(sqlStmt);
       
   745 
       
   746 	MSG_WRAP_DB_ERROR(sqlStmt.Prepare(iDatabaseRef, headerTableQuery));
       
   747 	MSG_WRAP_DB_LEAVE(BindIntL(sqlStmt, 0, UnmaskTMsvId(aEntryId)));
       
   748 	
       
   749 	// Load the header entry into header structure.
       
   750 	TRAP(err, DoLoadHeaderEntryL(sqlStmt, fieldList, aFieldPairList));
       
   751 	
       
   752 	// Handle errors if any.
       
   753 	if(err<0)
       
   754 		{
       
   755 		for(TInt index=0; index<aFieldPairList.Count(); index++)
       
   756 			{
       
   757 			CHeaderFields* headerRow = aFieldPairList[0];
       
   758 			aFieldPairList.Remove(0);
       
   759 			delete headerRow;
       
   760 			}
       
   761 		MSG_WRAP_DB_ERROR(err);
       
   762 		}
       
   763 	
       
   764 	if(!aFieldPairList.Count())
       
   765 		{
       
   766 		User::Leave(KErrNotFound);
       
   767 		}
       
   768 	CleanupStack::PopAndDestroy(2); //sqlStmt, headerTableQuery
       
   769 	}	
       
   770 	
       
   771 	
       
   772 	
       
   773 	
       
   774 	
       
   775 /*
       
   776  * DoLoadHeaderEntryL()
       
   777  * 
       
   778  * @param aSqlStmt: Query statement.
       
   779  * @param aMetadataList: Metadata Entry for header table.
       
   780  * @return aFieldPairList: Header entry details.
       
   781  *
       
   782  * The function loads the header entry from the header
       
   783  * table. If the header entry has multiple row, all of 
       
   784  * them will be loaded and stored as a separate element 
       
   785  * of CHeaderFields.
       
   786  */	
       
   787 void CMsvMessageDBAdapter::DoLoadHeaderEntryL(RSqlStatement& aSqlStmt, const RPointerArray<CFieldClass>& aMetadataList, RPointerArray<CHeaderFields>& aFieldPairList)
       
   788 	{
       
   789 	TInt err = KErrNone;
       
   790 	// Fetch each header-table row in the loop.
       
   791 	while(ETrue)
       
   792 		{
       
   793 		err = aSqlStmt.Next();
       
   794 		if(KSqlAtRow != err)
       
   795 			{
       
   796 			if(KSqlAtEnd == err)
       
   797 				{
       
   798 				break;
       
   799 				}
       
   800 			else
       
   801 				{
       
   802 				MSG_WRAP_DB_ERROR(err);
       
   803 				}		
       
   804 			}
       
   805 
       
   806 		// Create header structure to store a single row.
       
   807 		CHeaderFields* headerRow = new(ELeave) CHeaderFields();
       
   808 		CleanupStack::PushL(headerRow);
       
   809 		
       
   810 		TBool isPrimaryRow = EFalse;
       
   811 		TInt count = aMetadataList.Count();
       
   812 		headerRow->iUid.iUid = ColumnInt(aSqlStmt, 0);
       
   813 		// Read individual fields and populate the header structure.
       
   814 		for(TInt index=0; index<count; index++)
       
   815 			{
       
   816 			CFieldPair* fieldObj = new(ELeave) CFieldPair();
       
   817 			CleanupStack::PushL(fieldObj);
       
   818 			
       
   819 			fieldObj->iFieldName = aMetadataList[index]->iName->Des().AllocL();
       
   820 			switch(aMetadataList[index]->iType)
       
   821 				{
       
   822 				case ESqlInt:
       
   823 					fieldObj->iFieldNumValue = ColumnInt(aSqlStmt, index+1);
       
   824 					if(!isPrimaryRow && fieldObj->iFieldNumValue != NULL && index != count-1)
       
   825 						{
       
   826 						isPrimaryRow = ETrue;
       
   827 						}
       
   828 					break;
       
   829 				case ESqlInt64:
       
   830 					fieldObj->iFieldNumValue = ColumnInt64(aSqlStmt, index+1);
       
   831 					if(!isPrimaryRow && fieldObj->iFieldNumValue != NULL && index != count-1)
       
   832 						{
       
   833 						isPrimaryRow = ETrue;
       
   834 						}
       
   835 					break;
       
   836 				case ESqlText:
       
   837 					TRAP(err, fieldObj->iFieldTextValue = ColumnTextL(aSqlStmt, index+1));
       
   838 					if(err < 0)
       
   839 						{
       
   840 						User::Leave(KErrGeneral);
       
   841 						}
       
   842 					if(!isPrimaryRow && (index != count-1) && fieldObj->iFieldTextValue->Des().Compare(_L("")) )
       
   843 						{
       
   844 						isPrimaryRow = ETrue;
       
   845 						}
       
   846 					break;
       
   847 				}		// switch(fieldList[index]->iType)
       
   848 
       
   849 			headerRow->iFieldPairList.AppendL(fieldObj);
       
   850 			CleanupStack::Pop(fieldObj);
       
   851 			}		// for(TInt index=0; index<fieldList.Count(); index++)
       
   852 		
       
   853 		// If it is not a primary entry, remove extra fields
       
   854 		// from the header row. 
       
   855 		// A header table can store two types of entry, primary 
       
   856 		// entry, one which has values in all the columns in the 
       
   857 		// table, and another which has values in only default columns.
       
   858 		if(!isPrimaryRow)
       
   859 			{
       
   860 			for(TInt index=0; index<count-1; index++)
       
   861 				{
       
   862 				delete headerRow->iFieldPairList[0];
       
   863 				headerRow->iFieldPairList.Remove(0);
       
   864 				}
       
   865 			}
       
   866 		aFieldPairList.AppendL(headerRow);
       
   867 		CleanupStack::Pop(headerRow);
       
   868 		}
       
   869 	}
       
   870 	
       
   871 	
       
   872 	
       
   873 	
       
   874 
       
   875 
       
   876 
       
   877 /*
       
   878  * UpdateHeaderEntryL()
       
   879  * 
       
   880  * @param aMtmId: MtmId to identify the header table.
       
   881  * @param aEntryId: Entry Id.
       
   882  * @param aFieldPairList: Header entry details.
       
   883  *
       
   884  * The function updates the header entry in the header table with the 
       
   885  * passed header structure. A header entry is essentially a list of 
       
   886  * UID-String combination which is stored as a separate element in the 
       
   887  * passed header structure. Each such header portion is stored in separate
       
   888  * rows in the header table. 
       
   889  
       
   890  * If the number of rows in the passed header structure is more than what
       
   891  * is present in the DB, the function inserts the new rows in the header
       
   892  * table. And if the few rows are missing from the passed header structure
       
   893  * the function deletes the extra row from the header table.
       
   894  */ 
       
   895 void CMsvMessageDBAdapter::UpdateHeaderEntryL(const TUid& aMtmId, TMsvId aEntryId, const RPointerArray<CHeaderFields>& aFieldPairList)
       
   896 	{
       
   897 	TInt err = KErrNone;
       
   898 	_LIT16(KGetHeaderSchema1, "SELECT uid FROM DB");
       
   899 	_LIT16(KGetHeaderSchema2, ".HEADER_");
       
   900 	_LIT16(KGetHeaderSchema3, " WHERE id = :id;");
       
   901 
       
   902 	// First find out the number of rows present in the DB
       
   903 	// against this entry, and store their UID in a array.
       
   904 	RBuf headerTableQuery;
       
   905 	CleanupClosePushL(headerTableQuery);
       
   906 	headerTableQuery.CreateL(KMaxQueryLength);
       
   907 		
       
   908 	// This creates following string...
       
   909 	// Ex: SELECT uid FROM DB<driveId>.HEADER_<mtmId> WHERE id = :id;
       
   910 	headerTableQuery.Append(KGetHeaderSchema1);
       
   911 	headerTableQuery.AppendNum(GetDriveId(aEntryId));
       
   912 	headerTableQuery.Append(KGetHeaderSchema2);
       
   913 	headerTableQuery.AppendNum(aMtmId.iUid);
       
   914 	headerTableQuery.Append(KGetHeaderSchema3);
       
   915 
       
   916 	//Prepare a statement for the query.
       
   917 	RSqlStatement sqlStmt;
       
   918 	CleanupClosePushL(sqlStmt);
       
   919 	
       
   920 	MSG_WRAP_DB_ERROR(sqlStmt.Prepare(iDatabaseRef, headerTableQuery));
       
   921 	MSG_WRAP_DB_LEAVE(BindIntL(sqlStmt, 0, UnmaskTMsvId(aEntryId)));
       
   922 	
       
   923 	// Execute the query and form the UID list.
       
   924 	RArray<TInt> uidList;
       
   925 	CleanupClosePushL(uidList);
       
   926 	do
       
   927 		{
       
   928 		err = sqlStmt.Next();
       
   929 		if(err == KSqlAtRow)
       
   930 			{
       
   931 			uidList.AppendL(ColumnInt(sqlStmt, 0));	
       
   932 			}		
       
   933 		}
       
   934 	while(err == KSqlAtRow);
       
   935 	
       
   936 	// If no entry is found in DB, return KErrNotFound.
       
   937 	MSG_WRAP_DB_ERROR(err);
       
   938 	if(!uidList.Count())
       
   939 		{
       
   940 		User::Leave(KErrNotFound);
       
   941 		}
       
   942 
       
   943 	// Open a DB transaction if it is not already open.
       
   944 	TBool isLocalTransaction = EFalse;
       
   945 	if(!iDBAdapter->isTransactionOpen)
       
   946 		{
       
   947 		MSG_WRAP_DB_LEAVE(iDBAdapter->BeginTransactionL());
       
   948 		isLocalTransaction = ETrue;
       
   949 		}
       
   950 	
       
   951 	// Process all the header portion one by one.
       
   952 	TRAP(err, DoProcessHeaderEntryL(aMtmId, aEntryId, aFieldPairList, uidList));
       
   953 	
       
   954 	// Close the transaction and handle error.
       
   955 	if(err)
       
   956 		{
       
   957 		if(isLocalTransaction)
       
   958 			{
       
   959 			iDBAdapter->RollbackTransactionL();
       
   960 			}
       
   961 		MSG_WRAP_DB_ERROR(err);
       
   962 		}
       
   963 	else
       
   964 		{
       
   965 		if(isLocalTransaction)
       
   966 			{
       
   967 			MSG_WRAP_DB_LEAVE(iDBAdapter->CommitTransactionL());
       
   968 			}
       
   969 		}
       
   970 
       
   971 	CleanupStack::PopAndDestroy(3); //uidList, sqlStmt, headerTableQuery
       
   972 	}
       
   973 
       
   974 
       
   975 
       
   976 
       
   977 
       
   978 
       
   979 /*
       
   980  * DoProcessHeaderEntryL()
       
   981  * 
       
   982  * @param aMtmId: MtmId to identify the header table.
       
   983  * @param aEntryId: Entry Id.
       
   984  * @param aFieldPairList: Header entry details.
       
   985  * @param aUidList: UID list of the header entry present in DB.
       
   986  *
       
   987  * The function is called by UpdateHeaderEntryL() only, and it updates
       
   988  * the header entry in the header table. For details, see the comment 
       
   989  * of UpdateHeaderEntryL().
       
   990  */ 
       
   991 void CMsvMessageDBAdapter::DoProcessHeaderEntryL(const TUid& aMtmId, TMsvId aEntryId, const RPointerArray<CHeaderFields>& aFieldPairList, RArray<TInt>& aUidList)
       
   992 	{
       
   993 	TInt metadataEntryIndex = GetMetadataEntryIndex(aMtmId);
       
   994 	User::LeaveIfError(metadataEntryIndex);
       
   995 	
       
   996 	// Processing each header row.
       
   997 	TInt fieldPairListCount = aFieldPairList.Count();
       
   998 	for(TInt index=0; index < fieldPairListCount; index++)
       
   999 		{
       
  1000 		// Search the entry UID in the UID list.
       
  1001 		CHeaderFields* headerRow = aFieldPairList[index];
       
  1002 		TInt rowIndex = aUidList.Find(headerRow->iUid.iUid);
       
  1003 		// If the entry is not present in DB, create it.
       
  1004 		if(KErrNotFound == rowIndex)
       
  1005 			{
       
  1006 			// Insert the entry
       
  1007 			DoInsertHeaderEntryL(aMtmId, aEntryId, headerRow, metadataEntryIndex);
       
  1008 			}
       
  1009 		else
       
  1010 			{
       
  1011 			// else, update the entry
       
  1012 			DoUpdateHeaderEntryL(aMtmId, aEntryId, headerRow, metadataEntryIndex);
       
  1013 			aUidList.Remove(rowIndex);
       
  1014 			}
       
  1015 		}
       
  1016 	
       
  1017 	// If there is extra entries in the DB, 
       
  1018 	// delete them.
       
  1019 	if(aUidList.Count())
       
  1020 		{
       
  1021 		_LIT16(KGetHeaderSchema1, "DELETE FROM DB");
       
  1022 		_LIT16(KGetHeaderSchema2, ".HEADER_");
       
  1023 		_LIT16(KGetHeaderSchema3, " WHERE id = :id and uid in (");
       
  1024 		_LIT16(KGetHeaderSchema4, ");");
       
  1025 		_LIT16(KComma, ", ");
       
  1026 
       
  1027 		// Create the delete query string.
       
  1028 		RBuf headerTableQuery;
       
  1029 		CleanupClosePushL(headerTableQuery);
       
  1030 		headerTableQuery.CreateL(KMaxQueryLength);
       
  1031 			
       
  1032 		// This creates following string...
       
  1033 		headerTableQuery.Append(KGetHeaderSchema1);
       
  1034 		headerTableQuery.AppendNum(GetDriveId(aEntryId));
       
  1035 		headerTableQuery.Append(KGetHeaderSchema2);
       
  1036 		headerTableQuery.AppendNum(aMtmId.iUid);
       
  1037 		headerTableQuery.Append(KGetHeaderSchema3);
       
  1038 		headerTableQuery.AppendNum(aUidList[0]);
       
  1039 		TInt uidListCount = aUidList.Count();
       
  1040 		for(TInt index=1; index < uidListCount; index++)
       
  1041 			{
       
  1042 			headerTableQuery.Append(KComma);
       
  1043 			headerTableQuery.AppendNum(aUidList[index]);
       
  1044 			}
       
  1045 		headerTableQuery.Append(KGetHeaderSchema4);
       
  1046 		
       
  1047 		//Prepare a statement for the query.
       
  1048 		RSqlStatement sqlStmt;
       
  1049 		CleanupClosePushL(sqlStmt);
       
  1050 		User::LeaveIfError(sqlStmt.Prepare(iDatabaseRef, headerTableQuery));
       
  1051 		BindIntL(sqlStmt, 0, UnmaskTMsvId(aEntryId));
       
  1052 		
       
  1053 		// Execute the query and handle error.
       
  1054 		TInt err = sqlStmt.Exec();
       
  1055 		if(!err)
       
  1056 			{
       
  1057 			User::Leave(KErrNotFound);
       
  1058 			}
       
  1059 
       
  1060 		User::LeaveIfError(err);
       
  1061 		CleanupStack::PopAndDestroy(2); //sqlStmt, headerTableQuery	
       
  1062 		}
       
  1063 	}
       
  1064 
       
  1065 
       
  1066 
       
  1067 
       
  1068 
       
  1069 
       
  1070 /*
       
  1071  * DoUpdateHeaderEntryL()
       
  1072  * 
       
  1073  * @param aMtmId: MtmId to identify the header table.
       
  1074  * @param aEntryId: Entry Id.
       
  1075  * @param aHeaderFields: Header entry details.
       
  1076  * @param aMetadataEntryIndex: UID list of the header entry present in DB.
       
  1077  *
       
  1078  * The function is called by UpdateHeaderEntryL() only, and it updates
       
  1079  * the header entry in the header table. For details, see the comment 
       
  1080  * of UpdateHeaderEntryL().
       
  1081  */ 
       
  1082 void CMsvMessageDBAdapter::DoUpdateHeaderEntryL(const TUid& aMtmId, TMsvId aEntryId, CHeaderFields* aHeaderFields, TInt aMetadataEntryIndex)
       
  1083 	{
       
  1084 	_LIT16(KGetHeaderSchema1, "UPDATE DB");
       
  1085 	_LIT16(KGetHeaderSchema2, ".HEADER_");
       
  1086 	_LIT16(KGetHeaderSchema3, " SET ");
       
  1087 	_LIT16(KGetHeaderSchema4, " WHERE id = :id and uid = :uid;");
       
  1088 	_LIT16(KGetHeaderSchema5, " details = :details WHERE id = :id and uid = :uid;");
       
  1089 	_LIT16(KComma, ", ");
       
  1090 	_LIT16(KColon, "\" = :");
       
  1091 	_LIT16(KQuote, "\"");
       
  1092 		
       
  1093 	RBuf headerTableQuery;
       
  1094 	CleanupClosePushL(headerTableQuery);
       
  1095 	
       
  1096 	// Create the query string:
       
  1097 	// Ex: UPDATE DB<driveId>.HEADER_<mtmId> SET
       
  1098 	headerTableQuery.CreateL(KMaxQueryLength);
       
  1099 	headerTableQuery.Append(KGetHeaderSchema1);
       
  1100 	headerTableQuery.AppendNum(GetDriveId(aEntryId));
       
  1101 	headerTableQuery.Append(KGetHeaderSchema2);
       
  1102 	headerTableQuery.AppendNum(aMtmId.iUid);
       
  1103 	headerTableQuery.Append(KGetHeaderSchema3);
       
  1104 	
       
  1105 	TInt columnIndex = -1;
       
  1106 	// Append column information to the query
       
  1107 	CHeaderMetaData* headerMetadata = iHeaderDataList[aMetadataEntryIndex];
       
  1108 	TInt fieldPairListCount = aHeaderFields->iFieldPairList.Count();
       
  1109 	if(fieldPairListCount > 1)
       
  1110 		{
       
  1111 		// Check if all the field values are passed.
       
  1112 		if(fieldPairListCount != headerMetadata->iFieldList.Count())
       
  1113 			{
       
  1114 			User::Leave(KErrArgument);
       
  1115 			}
       
  1116 		for(TInt index=0; index < fieldPairListCount; index++)
       
  1117 			{
       
  1118 			if(index)
       
  1119 				{
       
  1120 				headerTableQuery.Append(KComma);	
       
  1121 				}
       
  1122 			headerTableQuery.Append(KQuote);
       
  1123 			headerTableQuery.Append(headerMetadata->iFieldList[index]->iName->Des());
       
  1124 			headerTableQuery.Append(KColon);
       
  1125 			headerTableQuery.Append(headerMetadata->iFieldList[index]->iName->Des());
       
  1126 			columnIndex++;
       
  1127 			}
       
  1128 		headerTableQuery.Append(KGetHeaderSchema4);		
       
  1129 		}
       
  1130 	else
       
  1131 		{
       
  1132 		headerTableQuery.Append(KGetHeaderSchema5);
       
  1133 		columnIndex++;
       
  1134 		}
       
  1135 	
       
  1136 	// Prepare the query and Bind the values.
       
  1137 	RSqlStatement sqlStmt;
       
  1138 	CleanupClosePushL(sqlStmt);
       
  1139 	User::LeaveIfError(sqlStmt.Prepare(iDatabaseRef, headerTableQuery));
       
  1140 
       
  1141 	TInt fieldIndex = 0;
       
  1142 	for(; fieldIndex<columnIndex; fieldIndex++)
       
  1143 		{
       
  1144 		switch(headerMetadata->iFieldList[fieldIndex]->iType)
       
  1145 			{
       
  1146 			case ESqlInt:
       
  1147 				BindIntL(sqlStmt, fieldIndex, aHeaderFields->iFieldPairList[fieldIndex]->iFieldNumValue);
       
  1148 				break;
       
  1149 			case ESqlInt64:
       
  1150 				BindInt64L(sqlStmt, fieldIndex, aHeaderFields->iFieldPairList[fieldIndex]->iFieldNumValue);
       
  1151 				break;
       
  1152 			case ESqlText:
       
  1153 				if(!aHeaderFields->iFieldPairList[fieldIndex]->iFieldTextValue)
       
  1154 					{
       
  1155 					User::Leave(KErrArgument);
       
  1156 					}
       
  1157 				BindTextL(sqlStmt, fieldIndex, aHeaderFields->iFieldPairList[fieldIndex]->iFieldTextValue->Des());
       
  1158 				break;
       
  1159 			}
       
  1160 		}
       
  1161 
       
  1162 	if(aHeaderFields->iFieldPairList[fieldIndex]->iFieldTextValue)
       
  1163 		{
       
  1164 		BindTextL(sqlStmt, columnIndex++, aHeaderFields->iFieldPairList[fieldIndex]->iFieldTextValue->Des());	
       
  1165 		}
       
  1166 	else
       
  1167 		{
       
  1168 		User::Leave(KErrArgument);
       
  1169 		}
       
  1170 
       
  1171 	BindIntL(sqlStmt, columnIndex++, UnmaskTMsvId(aEntryId));
       
  1172 	BindIntL(sqlStmt, columnIndex, aHeaderFields->iUid.iUid);
       
  1173 
       
  1174 	// Execute the query and process the error.
       
  1175 	TInt err = sqlStmt.Exec();
       
  1176 	if(!err)
       
  1177 		{
       
  1178 		User::Leave(KErrGeneral);
       
  1179 		}
       
  1180 	
       
  1181 	User::LeaveIfError(err);
       
  1182 	CleanupStack::PopAndDestroy(2); //sqlStmt, headerTableQuery	
       
  1183 	}
       
  1184 
       
  1185 
       
  1186 
       
  1187 
       
  1188 
       
  1189 
       
  1190 
       
  1191 
       
  1192 /*
       
  1193  * CheckStdColumnsAdded()
       
  1194  * 
       
  1195  * @param aFieldName: Column name
       
  1196  * @return aColStatus: Column status flag.
       
  1197  *
       
  1198  * The function checks the existence of standard header fields
       
  1199  * as defined in CMsvHeaderStore::TCommonHeaderField in the 
       
  1200  * header table and sets the appropriate flag n aColStatus.
       
  1201  */ 
       
  1202 void CMsvMessageDBAdapter::CheckStdColumnsAdded(HBufC* aFieldName, TInt& aColStatus)
       
  1203 	{
       
  1204 	// If it is a standard FORM field...
       
  1205 	if(!aFieldName->Des().Compare(_L("From")))
       
  1206 		{
       
  1207 		aColStatus |= CMsvHeaderStore::EFrom;
       
  1208 		}
       
  1209 	else
       
  1210 		{
       
  1211 		if(!aFieldName->Des().Compare(_L("To")))
       
  1212 			{
       
  1213 			aColStatus |= CMsvHeaderStore::ETo;			
       
  1214 			}
       
  1215 		else
       
  1216 			{
       
  1217 			if(!aFieldName->Des().Compare(_L("CC")))
       
  1218 				{
       
  1219 				aColStatus |= CMsvHeaderStore::ECC;				
       
  1220 				}
       
  1221 			else
       
  1222 				{
       
  1223 				if(!aFieldName->Des().Compare(_L("BCC")))
       
  1224 					{
       
  1225 					aColStatus |= CMsvHeaderStore::EBCC;
       
  1226 					}
       
  1227 				else
       
  1228 					{
       
  1229 					if(!aFieldName->Des().Compare(_L("Subject")))
       
  1230 						{
       
  1231 						aColStatus |= CMsvHeaderStore::ESubject;						
       
  1232 						}
       
  1233 					}	// BCC
       
  1234 				}	// CC
       
  1235 			}	// To
       
  1236 		}	// From
       
  1237 	}
       
  1238 
       
  1239 
       
  1240 
       
  1241 
       
  1242 
       
  1243 
       
  1244 
       
  1245 
       
  1246 /*
       
  1247  * DeleteHeaderEntryL()
       
  1248  * 
       
  1249  * @param aMtmId: MtmId to identify the header table.
       
  1250  * @param aEntryId: Entry Id.
       
  1251  *
       
  1252  * The function deletes the header entry from the corresponding
       
  1253  * header table. If multiple rows are associated with the header
       
  1254  * entry, all such rows are deleted from the header table.
       
  1255  */ 
       
  1256 void CMsvMessageDBAdapter::DeleteHeaderEntryL(const TUid& aMtmId, TMsvId aEntryId)
       
  1257 	{
       
  1258 	TInt err = KErrNone;
       
  1259 	_LIT16(KGetHeaderSchema1, "DELETE FROM DB");
       
  1260 	_LIT16(KGetHeaderSchema2, ".HEADER_");
       
  1261 	_LIT16(KGetHeaderSchema3, " WHERE id = :id;");
       
  1262 
       
  1263 	// Leave with KErrNotFound, if the header table does not exist.
       
  1264 	User::LeaveIfError(GetMetadataEntryIndex(aMtmId));
       
  1265 	
       
  1266 	RBuf headerTableQuery;
       
  1267 	CleanupClosePushL(headerTableQuery);
       
  1268 	headerTableQuery.CreateL(KMaxQueryLength);
       
  1269 		
       
  1270 	// This creates following string...
       
  1271 	// Ex: DELETE FROM DB<driveId>.HEADER_<mtmId> WHERE id = :id;
       
  1272 	headerTableQuery.Append(KGetHeaderSchema1);
       
  1273 	headerTableQuery.AppendNum(GetDriveId(aEntryId));
       
  1274 	headerTableQuery.Append(KGetHeaderSchema2);
       
  1275 	headerTableQuery.AppendNum(aMtmId.iUid);
       
  1276 	headerTableQuery.Append(KGetHeaderSchema3);
       
  1277 
       
  1278 	//Prepare a statement for the query.
       
  1279 	RSqlStatement sqlStmt;
       
  1280 	CleanupClosePushL(sqlStmt);
       
  1281 	
       
  1282 	MSG_WRAP_DB_ERROR(sqlStmt.Prepare(iDatabaseRef, headerTableQuery));
       
  1283 	MSG_WRAP_DB_LEAVE(BindIntL(sqlStmt, 0, UnmaskTMsvId(aEntryId)));
       
  1284 	
       
  1285 	// Execute the query and handle the error.
       
  1286 	err = sqlStmt.Exec();
       
  1287 	if(!err)
       
  1288 		{
       
  1289 		User::Leave(KErrNotFound);
       
  1290 		}
       
  1291 	
       
  1292 	MSG_WRAP_DB_ERROR(err);
       
  1293 	CleanupStack::PopAndDestroy(2); //sqlStmt, headerTableQuery	
       
  1294 	}
       
  1295 	
       
  1296 
       
  1297 
       
  1298 
       
  1299 /*
       
  1300  * CopyHeaderEntryL()
       
  1301  * 
       
  1302  * @param aMtmId The MTM UId to identify the header table.
       
  1303  * @param aSrcEntryId The source entry's TMsvId.
       
  1304  * @param aDestEntryId The destination source entry's TMsvId.
       
  1305  * @return None.
       
  1306  * @leave KErrNoMemory while creating the buffer for the query.
       
  1307  *		  
       
  1308  *
       
  1309  * The function copies header entry(s) of an entry from the corresponding
       
  1310  * header table. If multiple rows are associated with the entry's TMsvId,
       
  1311  * then copies of all such rows are made.
       
  1312  * This is 
       
  1313  */ 
       
  1314 void CMsvMessageDBAdapter::CopyHeaderEntryL(const TUid& aMtmId, const TMsvId& aSrcEntryId, const TMsvId& aDestEntryId)
       
  1315 	{
       
  1316 	_LIT(KComma, ", ");
       
  1317 	_LIT(KDelimiter, ";");
       
  1318 	_LIT(KQuote, "\"");
       
  1319 	_LIT(KCopyHeader1, "INSERT INTO DB");			// INSERT INTO DB1
       
  1320 	_LIT(KCopyHeader2, ".HEADER_");					// .HEADER_100
       
  1321 	_LIT(KCopyHeader3, " (id, uid");				//  (id, uid, field3, ...
       
  1322 	_LIT(KCopyHeader4, ") SELECT ");				// ) SELECT aDestEntryId
       
  1323 	_LIT(KCopyHeader5, ",uid");						// , uid, field3, ...
       
  1324 	_LIT(KCopyHeader6, " FROM HEADER_");			//  FROM HEADER_100
       
  1325 	_LIT(KCopyHeader7, " WHERE id = ");				//  WHERE id = aSrcEntryId;
       
  1326 	
       
  1327 	// Get metadata for the appropriate header
       
  1328 	TInt driveId = GetDriveId(aSrcEntryId);
       
  1329 	TInt metadataEntryIndex = GetMetadataEntryIndex(aMtmId);
       
  1330 	User::LeaveIfError(metadataEntryIndex);
       
  1331 	
       
  1332 	/*
       
  1333 	 We're looking to construct a query that looks like:
       
  1334 	 		INSERT INTO DB<driveId>.HEADER_<mtmId> (id, uid, field3...)
       
  1335 				SELECT aDestEntryId, uid, field3...
       
  1336 				WHERE id = aSrcEntryId;
       
  1337 	*/
       
  1338 	
       
  1339 	// Start query construction.
       
  1340 	RBuf headerTableQuery;
       
  1341 	CleanupClosePushL(headerTableQuery);
       
  1342 	headerTableQuery.CreateL(KMaxQueryLength);
       
  1343 	headerTableQuery.Append(KCopyHeader1);
       
  1344 	headerTableQuery.AppendNum(driveId);
       
  1345 	headerTableQuery.Append(KCopyHeader2);
       
  1346 	headerTableQuery.AppendNum(aMtmId.iUid);
       
  1347 	headerTableQuery.Append(KCopyHeader3);
       
  1348 	
       
  1349 	
       
  1350 	// Append column names to the query string.
       
  1351 	// Column names are stored in the header metadata structure.
       
  1352 	// We wrap the column names in quotes in case the column names are SQLite keywords. Eg: "From".
       
  1353 	CHeaderMetaData* headerMetadata = iHeaderDataList[metadataEntryIndex];
       
  1354 	RPointerArray<CFieldClass>& fieldList = headerMetadata->iFieldList;	
       
  1355 	TInt fieldListCount = fieldList.Count();
       
  1356 	TInt index = 0;
       
  1357 	for(; index < fieldListCount; ++index)
       
  1358 		{
       
  1359 		headerTableQuery.Append(KComma);
       
  1360 		headerTableQuery.Append(KQuote);
       
  1361 		headerTableQuery.Append(fieldList[index]->iName->Des());
       
  1362 		headerTableQuery.Append(KQuote);
       
  1363 		}
       
  1364 	
       
  1365 	headerTableQuery.Append(KCopyHeader4);
       
  1366 	headerTableQuery.AppendNum(UnmaskTMsvId(aDestEntryId));
       
  1367 	headerTableQuery.Append(KCopyHeader5);
       
  1368 	
       
  1369 	for(index = 0; index < fieldListCount; ++index)
       
  1370 		{
       
  1371 		headerTableQuery.Append(KComma);
       
  1372 		headerTableQuery.Append(KQuote);
       
  1373 		headerTableQuery.Append(fieldList[index]->iName->Des());
       
  1374 		headerTableQuery.Append(KQuote);
       
  1375 		}
       
  1376 	
       
  1377 	headerTableQuery.Append(KCopyHeader6);
       
  1378 	headerTableQuery.AppendNum(aMtmId.iUid);
       
  1379 	headerTableQuery.Append(KCopyHeader7);
       
  1380 	headerTableQuery.AppendNum(UnmaskTMsvId(aSrcEntryId));
       
  1381 	
       
  1382 	headerTableQuery.Append(KDelimiter);
       
  1383 	
       
  1384 	TInt err = iDatabaseRef.Exec(headerTableQuery);
       
  1385 	if(!err)
       
  1386 		{ // 0 returned by Exec is due to SELECT part of the query not returning any result to the INSERT part.
       
  1387 		User::Leave(KErrNotFound);
       
  1388 		}
       
  1389 	
       
  1390 	if(KSqlErrConstraint == err)
       
  1391 		{
       
  1392 		User::Leave(KErrAlreadyExists);
       
  1393 		}
       
  1394 	
       
  1395 	MSG_WRAP_DB_ERROR(err);
       
  1396 	CleanupStack::PopAndDestroy(); //headerTableQuery
       
  1397 	}
       
  1398 
       
  1399 
       
  1400 
       
  1401 /*
       
  1402  * CheckHeaderTableAndStdColumnFields()
       
  1403  *
       
  1404  * @param aMtmId: MtmId for related Header table
       
  1405  * @param aHeaderFilelds: Header field specified in search query
       
  1406  *
       
  1407  * Check HeaderTable And StdColumn are exist. 
       
  1408  */	
       
  1409 TBool CMsvMessageDBAdapter::CheckHeaderTableAndStdColumnFields(const TUid& aMtmId, TInt aHeaderFields)
       
  1410 	{
       
  1411 	TBool flag = EFalse;
       
  1412 	
       
  1413 	TInt index = GetMetadataEntryIndex(aMtmId);
       
  1414 	if(index != KErrNotFound)
       
  1415 		{
       
  1416 		// needs to check all standard columns given in a query are avilble in header table
       
  1417 		if( (iHeaderDataList[index]->iStdColumnStatus & aHeaderFields) == aHeaderFields)
       
  1418 			{
       
  1419 			flag = ETrue;
       
  1420 			}
       
  1421 		}
       
  1422 	return flag;
       
  1423 	}
       
  1424 
       
  1425 
       
  1426 
       
  1427 
       
  1428 TBool CMsvMessageDBAdapter::DoesAnyStoreExistsL(TMsvId aId, TUid aMtmId)
       
  1429 	{
       
  1430 	_LIT16(KCheckHeaderTableQuery1, "SELECT COUNT(*) FROM DB");
       
  1431 	_LIT16(KCheckHeaderTableQuery2, ".HEADER_");
       
  1432 	_LIT16(KCheckHeaderTableQuery3, " WHERE id = ");
       
  1433 	_LIT16(KSemiColon, ";");
       
  1434 	
       
  1435 	RBuf headerTableQuery;
       
  1436 	CleanupClosePushL(headerTableQuery);
       
  1437 	headerTableQuery.CreateL(KMaxQueryLength);
       
  1438 
       
  1439 	headerTableQuery.Append(KCheckHeaderTableQuery1);
       
  1440 	headerTableQuery.AppendNum(GetDriveId(aId));
       
  1441 	headerTableQuery.Append(KCheckHeaderTableQuery2);
       
  1442 	headerTableQuery.AppendNum(aMtmId.iUid);
       
  1443 	headerTableQuery.Append(KCheckHeaderTableQuery3);
       
  1444 	headerTableQuery.AppendNum(UnmaskTMsvId(aId));
       
  1445 	headerTableQuery.Append(KSemiColon);
       
  1446 
       
  1447 	TInt count = 0;
       
  1448 	TSqlScalarFullSelectQuery query(iDatabaseRef);
       
  1449 	TRAPD(err, count = query.SelectIntL(headerTableQuery));
       
  1450 	CleanupStack::PopAndDestroy();			// headerTableQuery
       
  1451 	
       
  1452 	if(err == KErrNone && count > 0)
       
  1453 		{
       
  1454 		return ETrue;
       
  1455 		}
       
  1456 	return EFalse;
       
  1457 	}