|
1 /* |
|
2 * Copyright (c) 2008-2009 Nokia Corporation and/or its subsidiary(-ies). |
|
3 * All rights reserved. |
|
4 * This component and the accompanying materials are made available |
|
5 * under the terms of the License "Eclipse Public License v1.0" |
|
6 * which accompanies this distribution, and is available |
|
7 * at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
8 * |
|
9 * Initial Contributors: |
|
10 * Nokia Corporation - initial contribution. |
|
11 * |
|
12 * Contributors: |
|
13 * |
|
14 * Description: |
|
15 * Implements the SCR Data Layer API which performs all interaction with the underlying database. |
|
16 * |
|
17 */ |
|
18 |
|
19 |
|
20 /** |
|
21 @file |
|
22 @internalComponent |
|
23 @released |
|
24 */ |
|
25 |
|
26 #include "scrdatabase.h" |
|
27 #include "usiflog.h" |
|
28 // Userinclude headers are used instead of the standard ones as SCR uses a custom version of SQLite |
|
29 #include "sqlite3.h" |
|
30 #include "SqliteSecure.h" |
|
31 #include <stdio.h> |
|
32 |
|
33 using namespace Usif; |
|
34 |
|
35 namespace Usif |
|
36 { |
|
37 class CDatabaseImplementation : public CBase |
|
38 /** |
|
39 The database implementation class encapsulates the implementation of CDatabase from its interface. |
|
40 */ |
|
41 { |
|
42 friend class CDatabase; |
|
43 public: |
|
44 static CDatabaseImplementation* NewL(RFile& aDatabaseFile, RFile& aJournalFile); |
|
45 ~CDatabaseImplementation(); |
|
46 /** |
|
47 Maps the given SQL database error into a system wide symbian specific error code. |
|
48 @leave If the given error code does not map to KErrNone, leaves with a system wide |
|
49 symbian error code. |
|
50 */ |
|
51 void CheckSqlErrCodeL(TInt aErr); |
|
52 |
|
53 /** |
|
54 @return The extended error codes that provide more detailed information about errors. |
|
55 */ |
|
56 TInt ErrorCode(); |
|
57 |
|
58 private: |
|
59 /** |
|
60 @return The English-language text that describes the most recent error. |
|
61 */ |
|
62 TPtrC8 ErrorMessage(); |
|
63 |
|
64 private: |
|
65 CDatabaseImplementation(); |
|
66 void ConstructorL(RFile& aDatabaseFile, RFile& aJournalFile); |
|
67 |
|
68 private: |
|
69 sqlite3* iDbHandle; ///< The database handle, owned by CDatabaseImplementation. |
|
70 }; |
|
71 |
|
72 |
|
73 class CStatementImplementation : public CBase |
|
74 /** |
|
75 This class encapsulates the implementation details of SQL statement object |
|
76 which contains the result sets of queries. |
|
77 */ |
|
78 { |
|
79 public: |
|
80 static CStatementImplementation* NewLC(sqlite3_stmt* aStmtHandle); |
|
81 ~CStatementImplementation(); |
|
82 inline sqlite3_stmt* Handle() const; |
|
83 |
|
84 private: |
|
85 CStatementImplementation(sqlite3_stmt* aStmtHandle); |
|
86 |
|
87 private: |
|
88 sqlite3_stmt* iStmtHandle; //SQL statement handle |
|
89 }; |
|
90 |
|
91 }// End of namespace Usif |
|
92 |
|
93 // |
|
94 // CDatabaseImplementation |
|
95 // |
|
96 |
|
97 CDatabaseImplementation::CDatabaseImplementation() |
|
98 // Constructor |
|
99 { |
|
100 // empty |
|
101 } |
|
102 |
|
103 CDatabaseImplementation::~CDatabaseImplementation() |
|
104 // Destructor |
|
105 { |
|
106 // Close the db handle |
|
107 TInt err = sqlite3_close(iDbHandle); |
|
108 if(err != SQLITE_OK) |
|
109 { |
|
110 TPtrC8 ptrErrMsg(ErrorMessage()); |
|
111 DEBUG_PRINTF3(_L8("Failed to close the database handle. Error code:%d, Error message:%S"), ErrorCode(), &ptrErrMsg); |
|
112 } |
|
113 } |
|
114 |
|
115 CDatabaseImplementation* CDatabaseImplementation::NewL(RFile& aDatabaseFile, RFile& aJournalFile) |
|
116 { |
|
117 CDatabaseImplementation* self = new(ELeave) CDatabaseImplementation(); |
|
118 CleanupStack::PushL(self); |
|
119 self->ConstructorL(aDatabaseFile, aJournalFile); |
|
120 CleanupStack::Pop(self); |
|
121 return self; |
|
122 } |
|
123 |
|
124 void CDatabaseImplementation::ConstructorL(RFile& aDatabaseFile, RFile& aJournalFile) |
|
125 { |
|
126 TSqliteSecure sqliteSecure; // Utility class to open sqlite database via file handles. Implemented by Symbian. |
|
127 TInt err = sqliteSecure.Open(aDatabaseFile, aJournalFile, &iDbHandle); |
|
128 |
|
129 // Check the returned error code |
|
130 CheckSqlErrCodeL(err); |
|
131 |
|
132 // If comes here, means the database file has been opened successfully |
|
133 // Now, enable the extended result codes feature of SQLite. In SQLite, this feature is |
|
134 // disabled by default for historical compatibility. |
|
135 err = sqlite3_extended_result_codes(iDbHandle, 0); |
|
136 if(SQLITE_OK != err) |
|
137 { |
|
138 TPtrC8 ptrErrMsg(ErrorMessage()); |
|
139 DEBUG_PRINTF3(_L8("Failed to activate the extended error mechanism. Error code:%d, Error message:%S"), ErrorCode(), &ptrErrMsg); |
|
140 } |
|
141 } |
|
142 |
|
143 TPtrC8 CDatabaseImplementation::ErrorMessage() |
|
144 { |
|
145 const char *errMsg = sqlite3_errmsg(iDbHandle); |
|
146 TPtrC8 errPtr((TUint8*)errMsg); |
|
147 return errPtr; |
|
148 } |
|
149 |
|
150 TInt CDatabaseImplementation::ErrorCode() |
|
151 { |
|
152 return sqlite3_errcode(iDbHandle); |
|
153 } |
|
154 |
|
155 |
|
156 void CDatabaseImplementation::CheckSqlErrCodeL(TInt aErr) |
|
157 { |
|
158 TInt symbianErrCode(0); |
|
159 |
|
160 switch(aErr) |
|
161 { |
|
162 case SQLITE_OK: |
|
163 case SQLITE_DONE: // sqlite3_step() has finished executing |
|
164 case SQLITE_ROW: // sqlite3_step() has another row ready |
|
165 symbianErrCode = KErrNone; |
|
166 break; |
|
167 case SQLITE_NOTADB: // File opened that is not a database file |
|
168 case SQLITE_NOTFOUND: // Table or record not found |
|
169 case SQLITE_EMPTY: // Database is empty |
|
170 case SQLITE_CANTOPEN: // Unable to open the database file |
|
171 symbianErrCode = KErrNotFound; |
|
172 break; |
|
173 case SQLITE_CORRUPT: // The database disk image is malformed |
|
174 case SQLITE_SCHEMA: // The database schema has changed |
|
175 case SQLITE_FORMAT: // Auxiliary database format error |
|
176 symbianErrCode = KErrCorrupt; |
|
177 break; |
|
178 case SQLITE_NOMEM: // A malloc() failed in sqlite engine |
|
179 case SQLITE_IOERR: // Some kind of disk I/O error occurred |
|
180 case SQLITE_FULL: // there is no space left on the disk, or the database is too big to hold any more information |
|
181 symbianErrCode = KErrNoMemory; |
|
182 break; |
|
183 case SQLITE_BUSY: // The database file is locked |
|
184 case SQLITE_LOCKED: // A table in the database is locked |
|
185 symbianErrCode = KErrInUse; |
|
186 break; |
|
187 case SQLITE_TOOBIG: // Too much data for one row of a table |
|
188 symbianErrCode = KErrOverflow; |
|
189 break; |
|
190 case SQLITE_INTERRUPT: // Operation terminated by sqlite_interrupt() |
|
191 symbianErrCode = KErrCancel; |
|
192 break; |
|
193 case SQLITE_MISUSE: // The library has been used incorrectly |
|
194 symbianErrCode = KErrNotSupported; |
|
195 break; |
|
196 case SQLITE_CONSTRAINT: // Abort due to constraint violation |
|
197 symbianErrCode = KErrAlreadyExists; |
|
198 break; |
|
199 case SQLITE_ERROR: // SQL error or missing database |
|
200 case SQLITE_MISMATCH : // Data type mismatch |
|
201 case SQLITE_RANGE: // 2nd parameter to sqlite3_bind out of range |
|
202 symbianErrCode = KErrArgument; |
|
203 break; |
|
204 case SQLITE_INTERNAL: // An internal logic error in SQLite |
|
205 symbianErrCode = KErrUnknown; |
|
206 break; |
|
207 case SQLITE_PERM: // Access permission denied |
|
208 symbianErrCode = KErrAccessDenied; |
|
209 break; |
|
210 default: |
|
211 symbianErrCode = aErr < 0 ? aErr : KErrGeneral; // The porting layer may return Symbian error codes, which are negative |
|
212 }// End of switch |
|
213 |
|
214 if(KErrNone != symbianErrCode) |
|
215 { |
|
216 if (aErr > 0) // If the error is negative, it does not come from SQLite |
|
217 { |
|
218 TPtrC8 ptrErrMsg(ErrorMessage()); |
|
219 DEBUG_PRINTF3(_L8("The SQL engine error code:%d, The SQL engine error message:%S"), ErrorCode(), &ptrErrMsg); |
|
220 } |
|
221 // Some functions returns standard error codes. |
|
222 // Here ErrorCode is called to get the extended error code, if exists. |
|
223 |
|
224 User::Leave(symbianErrCode); |
|
225 } |
|
226 } |
|
227 |
|
228 |
|
229 // |
|
230 // CStatementImplementation |
|
231 // |
|
232 |
|
233 CStatementImplementation::CStatementImplementation(sqlite3_stmt* aStmtHandle) |
|
234 // Constructor |
|
235 :iStmtHandle(aStmtHandle) |
|
236 { |
|
237 // Make sure that the statement handle is never NULL. |
|
238 ASSERT(iStmtHandle != NULL); |
|
239 } |
|
240 |
|
241 CStatementImplementation::~CStatementImplementation() |
|
242 // Destructor |
|
243 { |
|
244 TInt err = sqlite3_finalize(iStmtHandle); |
|
245 if(SQLITE_OK != err) |
|
246 { |
|
247 DEBUG_PRINTF2(_L8("Failed to finalize the statement object. Error code:%d"), err); |
|
248 } |
|
249 } |
|
250 |
|
251 CStatementImplementation* CStatementImplementation::NewLC(sqlite3_stmt* aStmtHandle) |
|
252 { |
|
253 CStatementImplementation* self = new(ELeave) CStatementImplementation(aStmtHandle); |
|
254 CleanupStack::PushL(self); |
|
255 return self; |
|
256 } |
|
257 |
|
258 inline sqlite3_stmt* CStatementImplementation::Handle() const |
|
259 { |
|
260 return iStmtHandle; |
|
261 } |
|
262 |
|
263 // |
|
264 // CDatabase |
|
265 // |
|
266 |
|
267 CDatabase::CDatabase() |
|
268 // Constructor |
|
269 { |
|
270 // Empty |
|
271 } |
|
272 |
|
273 EXPORT_C CDatabase::~CDatabase() |
|
274 // Destructor |
|
275 { |
|
276 delete iDbImpl; |
|
277 } |
|
278 |
|
279 EXPORT_C CDatabase* CDatabase::NewL(RFile& aDatabaseFile, RFile& aJournalFile) |
|
280 { |
|
281 CDatabase *self = CDatabase::NewLC(aDatabaseFile, aJournalFile); |
|
282 CleanupStack::Pop(self); |
|
283 return self; |
|
284 } |
|
285 |
|
286 EXPORT_C CDatabase* CDatabase::NewLC(RFile& aDatabaseFile, RFile& aJournalFile) |
|
287 { |
|
288 CDatabase *self = new(ELeave) CDatabase(); |
|
289 CleanupStack::PushL(self); |
|
290 self->ConstructL(aDatabaseFile, aJournalFile); |
|
291 return self; |
|
292 } |
|
293 |
|
294 void CDatabase::ConstructL(RFile& aDatabaseFile, RFile& aJournalFile) |
|
295 { |
|
296 iDbImpl = CDatabaseImplementation::NewL(aDatabaseFile, aJournalFile); |
|
297 } |
|
298 |
|
299 EXPORT_C CStatement* CDatabase::PrepareStatementLC(const TDesC& aStatementStr) |
|
300 { |
|
301 // For statements which start with the SELECT key word, this function creates an sql statement |
|
302 // object and returns it. |
|
303 sqlite3_stmt* stmtHandle(0); // Temporary statement handle |
|
304 const void* stmtTail(0); // Pointer to unused portion of Sql statement. |
|
305 TInt err = sqlite3_prepare16_v2(iDbImpl->iDbHandle, aStatementStr.Ptr(), aStatementStr.Size(), &stmtHandle, &stmtTail); |
|
306 |
|
307 // Check the returned error code |
|
308 iDbImpl->CheckSqlErrCodeL(err); |
|
309 // Since we expect single statement, stmtTail pointer should be NULL or point to zero. |
|
310 if(stmtTail && static_cast <const TUint16*> (stmtTail)[0] != 0) |
|
311 { |
|
312 err = sqlite3_finalize(stmtHandle); |
|
313 iDbImpl->CheckSqlErrCodeL(err); |
|
314 DEBUG_PRINTF(_L8("There is a problem with the provided SQL statement. It may contain more " |
|
315 "than one statement. Or It may not be terminated with semicolon. " |
|
316 "Or It may contain a space or invalid char after semicolon.")); |
|
317 User::Leave(KErrArgument); |
|
318 } |
|
319 // stmtHandle can be NULL for statements like this: ";" |
|
320 if(!stmtHandle) |
|
321 { |
|
322 DEBUG_PRINTF(_L8("The statement handle is NULL.")); |
|
323 User::Leave(KErrArgument); |
|
324 } |
|
325 // The statement object which carries handle to the result set of the sql statement |
|
326 CStatementImplementation* stmtImpl = CStatementImplementation::NewLC(stmtHandle); |
|
327 CStatement* stmtObj = CStatement::NewL(*this, stmtImpl); |
|
328 CleanupStack::Pop(stmtImpl); // Ownership has been passed to CStatement |
|
329 CleanupStack::PushL(stmtObj); |
|
330 |
|
331 return stmtObj; |
|
332 } |
|
333 |
|
334 EXPORT_C TInt CDatabase::LastInsertedIdL() |
|
335 { |
|
336 TInt retVal = (TInt)sqlite3_last_insert_rowid(iDbImpl->iDbHandle); |
|
337 // it is now expected that row ids in scr will require 64-bit storage, so cast the return value to TInt |
|
338 if(retVal <= 0) |
|
339 { |
|
340 User::Leave(KErrNotFound); |
|
341 } |
|
342 return retVal; |
|
343 } |
|
344 |
|
345 // |
|
346 // CStatement |
|
347 // |
|
348 |
|
349 CStatement::CStatement(const CDatabase& aDb, CStatementImplementation* aStmtImpl) |
|
350 // Constructor |
|
351 :iDb(aDb), iStmtImpl(aStmtImpl) |
|
352 { |
|
353 // Empty |
|
354 } |
|
355 |
|
356 EXPORT_C CStatement::~CStatement() |
|
357 // Destructor |
|
358 { |
|
359 delete iStmtImpl; |
|
360 } |
|
361 |
|
362 EXPORT_C CStatement* CStatement::NewL(const CDatabase& aDb, CStatementImplementation* aStmtImpl) |
|
363 { |
|
364 CStatement* self = new(ELeave) CStatement(aDb, aStmtImpl); |
|
365 return self; |
|
366 } |
|
367 |
|
368 EXPORT_C TBool CStatement::ProcessNextRowL() |
|
369 { |
|
370 TInt err = sqlite3_step(iStmtImpl->Handle()); |
|
371 |
|
372 switch(err) |
|
373 { |
|
374 case SQLITE_ROW: // A new row of data is ready for processing. |
|
375 return ETrue; |
|
376 |
|
377 case SQLITE_DONE: // The statement has finished executing successfully. |
|
378 return EFalse; |
|
379 default: |
|
380 iDb.iDbImpl->CheckSqlErrCodeL(err); |
|
381 }// End of switch |
|
382 return EFalse; |
|
383 } |
|
384 |
|
385 EXPORT_C void CStatement::ExecuteStatementL() |
|
386 { |
|
387 // If the statement doesn't return any result table, it should normally be executed |
|
388 // with sqlite3_exec. However, sqlite does not have a 16-bit version of sqlite3_exec. |
|
389 // Therefore, the execution is made with PrepareStatementLC and ProcessNextRowL functions. |
|
390 // Now, execute and check if the function has completed successfully by calling ProcessNextRowL. |
|
391 // If the function has failed, ProcessNextRowL will leave with one of the system wide error codes. |
|
392 while(ProcessNextRowL()) |
|
393 { |
|
394 // nop |
|
395 } |
|
396 } |
|
397 |
|
398 EXPORT_C void CStatement::BindIntL(TInt aParameterIndex, TInt aParameterValue) |
|
399 { |
|
400 TInt err = sqlite3_bind_int(iStmtImpl->Handle(), aParameterIndex, aParameterValue); |
|
401 iDb.iDbImpl->CheckSqlErrCodeL(err); |
|
402 } |
|
403 |
|
404 EXPORT_C void CStatement::BindInt64L(TInt aParameterIndex, TInt64 aParameterValue) |
|
405 { |
|
406 TInt err = sqlite3_bind_int64(iStmtImpl->Handle(), aParameterIndex, aParameterValue); |
|
407 iDb.iDbImpl->CheckSqlErrCodeL(err); |
|
408 } |
|
409 |
|
410 template <class A> void VerifyDescriptorLengthL(const A& aDesc, TUint aMaxDescriptorLength) |
|
411 { |
|
412 if (aDesc.Length() > aMaxDescriptorLength) |
|
413 User::Leave(KErrArgument); |
|
414 } |
|
415 |
|
416 EXPORT_C void CStatement::BindStrL(TInt aParameterIndex, const TDesC &aParameterStr) |
|
417 { |
|
418 const TInt KMaxInputDescriptorLength = 512; |
|
419 VerifyDescriptorLengthL(aParameterStr, KMaxInputDescriptorLength); |
|
420 TInt err = sqlite3_bind_text16(iStmtImpl->Handle(), aParameterIndex, aParameterStr.Ptr(), aParameterStr.Size(), SQLITE_TRANSIENT); |
|
421 // The fifth argument has the value SQLITE_TRANSIENT, it means that SQLite makes its own private copy of the data immediately |
|
422 iDb.iDbImpl->CheckSqlErrCodeL(err); |
|
423 } |
|
424 |
|
425 EXPORT_C void CStatement::BindBinaryL(TInt aParameterIndex, const TDesC8 &aParameterStr) |
|
426 { |
|
427 const TInt KMaxInputDescriptorLength = 512; |
|
428 BindBinaryL(aParameterIndex, aParameterStr, KMaxInputDescriptorLength); |
|
429 } |
|
430 |
|
431 EXPORT_C void CStatement::BindBinaryL(TInt aParameterIndex, const TDesC8 &aParameterStr, TUint aCustomLength) |
|
432 { |
|
433 VerifyDescriptorLengthL(aParameterStr, aCustomLength); |
|
434 TInt err = sqlite3_bind_blob(iStmtImpl->Handle(), aParameterIndex, reinterpret_cast<const char *>(aParameterStr.Ptr()), aParameterStr.Size(), SQLITE_TRANSIENT); |
|
435 iDb.iDbImpl->CheckSqlErrCodeL(err); |
|
436 } |
|
437 |
|
438 EXPORT_C TPtrC8 CStatement::BinaryColumnL(TInt aColIdx) const |
|
439 { |
|
440 TInt colType = SQLITE_BLOB; |
|
441 ValidateRequestedColumnL(aColIdx, colType); |
|
442 |
|
443 const TUint8* data = static_cast<const TUint8 *>(sqlite3_column_blob(iStmtImpl->Handle(), aColIdx)); |
|
444 if(!data) |
|
445 iDb.iDbImpl->CheckSqlErrCodeL(iDb.iDbImpl->ErrorCode()); |
|
446 |
|
447 TInt len = sqlite3_column_bytes(iStmtImpl->Handle(), aColIdx); |
|
448 if(!len) |
|
449 iDb.iDbImpl->CheckSqlErrCodeL(iDb.iDbImpl->ErrorCode()); |
|
450 |
|
451 return TPtrC8(data, len); |
|
452 } |
|
453 |
|
454 EXPORT_C TPtrC CStatement::StrColumnL(TInt aColIdx) const |
|
455 { |
|
456 TInt colType = SQLITE_TEXT; |
|
457 ValidateRequestedColumnL(aColIdx, colType); |
|
458 |
|
459 // Get the column data from the database |
|
460 TUint16* str = (TUint16*)sqlite3_column_text16(iStmtImpl->Handle(), aColIdx); |
|
461 if(!str) |
|
462 { |
|
463 iDb.iDbImpl->CheckSqlErrCodeL(iDb.iDbImpl->ErrorCode()); |
|
464 } |
|
465 // Get the length of the column data |
|
466 TInt len = sqlite3_column_bytes16(iStmtImpl->Handle(), aColIdx); |
|
467 if(!len) |
|
468 { |
|
469 iDb.iDbImpl->CheckSqlErrCodeL(iDb.iDbImpl->ErrorCode()); |
|
470 } |
|
471 // Return the value in a pointer descriptor |
|
472 // len contains the number of bytes, so divide it by 2 to get the number of chars |
|
473 ASSERT(len%2 == 0); |
|
474 return TPtrC(str, len/2); |
|
475 } |
|
476 |
|
477 EXPORT_C TInt64 CStatement::Int64ColumnL(TInt aColIdx) const |
|
478 { |
|
479 TInt colType = SQLITE_INTEGER; |
|
480 ValidateRequestedColumnL(aColIdx, colType); |
|
481 return sqlite3_column_int64(iStmtImpl->Handle(), aColIdx); |
|
482 } |
|
483 |
|
484 EXPORT_C TInt CStatement::IntColumnL(TInt aColIdx) const |
|
485 { |
|
486 TInt colType = SQLITE_INTEGER; |
|
487 ValidateRequestedColumnL(aColIdx, colType); |
|
488 return sqlite3_column_int(iStmtImpl->Handle(), aColIdx); |
|
489 } |
|
490 |
|
491 void CStatement::ValidateRequestedColumnL(TInt aColIdx, TInt& aColumnType) const |
|
492 // Validate the type and existence of the requested column. |
|
493 { |
|
494 // Get the number of columns in the result set |
|
495 TInt colNum = sqlite3_column_count(iStmtImpl->Handle()); |
|
496 // Get the type of the column |
|
497 TInt colType = sqlite3_column_type(iStmtImpl->Handle(), aColIdx); |
|
498 |
|
499 if((aColIdx < 0 || aColIdx > colNum-1) || // if the column index is invalid |
|
500 (aColumnType != colType && aColumnType != SQLITE_NULL)) // if its type doesn't match with the expected one |
|
501 {// just column range validity is checked in case of SQLITE_NULL. |
|
502 DEBUG_PRINTF5(_L("The provided column (Idx=%d, Type=%d) is not valid. Max Column Number=%d. Retrieved Column Type=%d"), aColIdx, aColumnType, colNum, colType); |
|
503 User::Leave(KErrArgument); |
|
504 } |
|
505 aColumnType = colType; |
|
506 } |
|
507 |
|
508 EXPORT_C void CStatement::ResetL() |
|
509 { |
|
510 TInt err = sqlite3_reset(iStmtImpl->Handle()); |
|
511 iDb.iDbImpl->CheckSqlErrCodeL(err); |
|
512 err = sqlite3_clear_bindings(iStmtImpl->Handle()); |
|
513 iDb.iDbImpl->CheckSqlErrCodeL(err); |
|
514 } |
|
515 |
|
516 EXPORT_C TBool CStatement::IsFieldNullL(TInt aColIdx) const |
|
517 { |
|
518 TInt colType = SQLITE_NULL; |
|
519 ValidateRequestedColumnL(aColIdx, colType); |
|
520 return (SQLITE_NULL == colType) ? ETrue : EFalse; |
|
521 } |