|
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 } |