|
1 // Copyright (c) 2005-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: |
|
12 // |
|
13 // Description: |
|
14 // |
|
15 |
|
16 #include <utf.h> //CnvUtfConverter |
|
17 #include <e32math.h> |
|
18 #include "SqliteSymbian.h" //sqlite3SymbianLastOsError() |
|
19 #include "sqlite3.h" |
|
20 #include "SqlSrvStatement.h" |
|
21 #include "SqlBufIterator.h" //TSqlBufRIterator |
|
22 #include "SqlSrvResourceProfiler.h" |
|
23 #include "UTraceSql.h" |
|
24 ////////////////////////////////////////////////////////////////////////////////////////////////////// |
|
25 ///////////////////////////// local const data //////////////////////////////////////////// |
|
26 ////////////////////////////////////////////////////////////////////////////////////////////////////// |
|
27 |
|
28 //This is the name prefix which will be given to the nameless parameters. |
|
29 //For example, if the SQL string is: |
|
30 // SELECT * FROM A WHERE ColA1 = ? AND ColA2 = ? |
|
31 //then the names which will be give to the parameters will be: |
|
32 //"?0" and "?1" |
|
33 _LIT(KNamelessParameter, "?"); |
|
34 |
|
35 |
|
36 ///////////////////////////////////////////////////////////////////////////////////////////////////////// |
|
37 ////////////////// HSqlSrvStmtParamBuf ////////////////////////////////// |
|
38 ///////////////////////////////////////////////////////////////////////////////////////////////////////// |
|
39 |
|
40 /** |
|
41 Destroys the parameter buffer. |
|
42 |
|
43 Virtual method. |
|
44 */ |
|
45 HSqlSrvStmtParamBuf::~HSqlSrvStmtParamBuf() |
|
46 { |
|
47 delete iBuf; |
|
48 } |
|
49 |
|
50 /** |
|
51 Binds the parameter value. |
|
52 The buffer can be synch-ed if: |
|
53 - this is the first synch operation; |
|
54 - the bound statement object is still alive (not finalized); |
|
55 - the current object is alive; |
|
56 - the current object data is retrieved from an IPC stream; |
|
57 |
|
58 If none of the conditions above is true, the synch operation is no-op. |
|
59 |
|
60 Virtual method. |
|
61 */ |
|
62 void HSqlSrvStmtParamBuf::DoSynchL() |
|
63 { |
|
64 if(iSynchDone || !iAlive || iStatementFinalized || iBufType != HSqlSrvStmtParamBuf::EBufIpcStream) |
|
65 { |
|
66 return; |
|
67 } |
|
68 iSynchDone = ETrue; |
|
69 TBufBuf::DoSynchL(); |
|
70 iStatement.BindParamBufL(iParamIndex); |
|
71 } |
|
72 |
|
73 /** |
|
74 Destroys the HSqlSrvStmtParamBuf instance. |
|
75 This method is a no-op if the statement is not finalized yet. |
|
76 |
|
77 Virtual method. |
|
78 */ |
|
79 void HSqlSrvStmtParamBuf::DoRelease() |
|
80 { |
|
81 iAlive = EFalse; |
|
82 if(iStatementFinalized) |
|
83 {//The bound statement has been finalized - destroy the current object then. |
|
84 delete this; |
|
85 } |
|
86 } |
|
87 |
|
88 /** |
|
89 This function is called by the bound statement object to notify the current HSqlSrvStmtParamBuf object that the |
|
90 bound statement is about to be finalized. That means, when the "stream close" operation on the client side |
|
91 makes an attempt to synch the HSqlSrvStmtParamBuf object, no attempt should be made to bound the parameter data, |
|
92 because the statement object is gone. |
|
93 After this call the bound statement objects seases to exist. |
|
94 |
|
95 Actions, performed by this method: |
|
96 - if the buffer type is "simple bind", the buffer will be destroyed. No reason to keep it alive, there is no bound IPC |
|
97 stream object on the client side; |
|
98 - if the buffer type is an IPC stream buffer and the buffer is alive, that means: the bound statement object is about to be |
|
99 finalized, but there is a bound client side IPC stream object that is still alive. In this case the buffer won't be destroyed, |
|
100 but will be "told" that the bound statement is finalized, so when the client side IPC stream is closed, this object will get destroyed; |
|
101 - if the buffer type is an IPC stream buffer and the buffer is "dead", that means there is no bound IPC stream object on the client |
|
102 side and it is safe to destroy the buffer; |
|
103 |
|
104 */ |
|
105 void HSqlSrvStmtParamBuf::NotifyStatementFinalized() |
|
106 { |
|
107 iStatementFinalized = ETrue; |
|
108 if(iBufType == HSqlSrvStmtParamBuf::EBufSimpleBind || !iAlive) |
|
109 { |
|
110 DoRelease(); |
|
111 } |
|
112 } |
|
113 |
|
114 ////////////////////////////////////////////////////////////////////////////////////////////////////// |
|
115 ///////////////////////////// CSqlSrvStatement class //////////////////////////////////////////// |
|
116 ////////////////////////////////////////////////////////////////////////////////////////////////////// |
|
117 |
|
118 /** |
|
119 Creates a new CSqlSrvStatement instance. |
|
120 |
|
121 The created CSqlSrvStatement instance will be placed in the cleanup stack. |
|
122 |
|
123 @param aDbHandle The database handle |
|
124 @param aSqlStmt 16-bit SQL statement, zero-terminated string |
|
125 @param aColumnCount Output parameter. It will be initialized with the column count. |
|
126 @param aParamCount Output parameter. It will be initialized with the parameter count. |
|
127 |
|
128 @return A pointer to the created CSqlSrvStatement instance. |
|
129 |
|
130 @leave KErrNoMemory, an out of memory condition has occurred; |
|
131 KErrArgument, bad argument, for example - the SQL string contains more than one SQL statements. |
|
132 Note that the function may also leave with some other database specific |
|
133 errors categorised as ESqlDbError. |
|
134 |
|
135 @panic SqlDb 4 In _DEBUG mode if aSqlStmt is not zero-terminated string. |
|
136 */ |
|
137 CSqlSrvStatement* CSqlSrvStatement::NewLC(sqlite3* aDbHandle, const TDesC16& aSqlStmt, TInt& aColumnCount, TInt& aParamCount) |
|
138 { |
|
139 __SQLASSERT(aSqlStmt.Length() > 0 ? (TInt)aSqlStmt[aSqlStmt.Length() - 1] == 0 : ETrue, ESqlPanicBadArgument); |
|
140 |
|
141 CSqlSrvStatement* self = new (ELeave) CSqlSrvStatement; |
|
142 CleanupStack::PushL(self); |
|
143 self->ConstructL(aDbHandle, aSqlStmt); |
|
144 SYMBIAN_TRACE_SQL_EVENTS_ONLY(UTF::Printf(UTF::TTraceContext(UTF::EInternals), KSrvStmtCreated, self)); |
|
145 aColumnCount = self->iColumnCount; |
|
146 aParamCount = self->iParamCount; |
|
147 return self; |
|
148 } |
|
149 |
|
150 /** |
|
151 Creates a new CSqlSrvStatement instance. |
|
152 |
|
153 The created CSqlSrvStatement instance will be placed in the cleanup stack. |
|
154 |
|
155 @param aDbHandle The database handle |
|
156 @param aSqlStmt 8-bit SQL statement, zero-terminated string |
|
157 @param aColumnCount Output parameter. It will be initialized with the column count. |
|
158 @param aParamCount Output parameter. It will be initialized with the parameter count. |
|
159 |
|
160 @return A pointer to the created CSqlSrvStatement instance. |
|
161 |
|
162 @leave KErrNoMemory, an out of memory condition has occurred; |
|
163 KErrArgument, bad argument, for example - the SQL string contains more than one SQL statements. |
|
164 Note that the function may also leave with some other database specific |
|
165 errors categorised as ESqlDbError. |
|
166 |
|
167 @panic SqlDb 4 In _DEBUG mode if aSqlStmt is not zero-terminated string. |
|
168 */ |
|
169 CSqlSrvStatement* CSqlSrvStatement::NewLC(sqlite3* aDbHandle, const TDesC8& aSqlStmt, TInt& aColumnCount, TInt& aParamCount) |
|
170 { |
|
171 __SQLASSERT(aSqlStmt.Length() > 0 ? (TInt)aSqlStmt[aSqlStmt.Length() - 1] == 0 : ETrue, ESqlPanicBadArgument); |
|
172 |
|
173 CSqlSrvStatement* self = new (ELeave) CSqlSrvStatement; |
|
174 CleanupStack::PushL(self); |
|
175 self->ConstructL(aDbHandle, aSqlStmt); |
|
176 SYMBIAN_TRACE_SQL_EVENTS_ONLY(UTF::Printf(UTF::TTraceContext(UTF::EInternals), KSrvStmtCreated, self)); |
|
177 aColumnCount = self->iColumnCount; |
|
178 aParamCount = self->iParamCount; |
|
179 return self; |
|
180 } |
|
181 |
|
182 /** |
|
183 Destroys the allocated by CSqlSrvStatement instance memory and other resources. |
|
184 */ |
|
185 CSqlSrvStatement::~CSqlSrvStatement() |
|
186 { |
|
187 DestroyParamBufArray(); |
|
188 iBufFlat.Close(); |
|
189 if(iStmtHandle) |
|
190 { |
|
191 #ifdef SYMBIAN_TRACE_SQL_EVENTS |
|
192 TInt scanCount = sqlite3_stmt_status(iStmtHandle, SQLITE_STMTSTATUS_FULLSCAN_STEP, ETrue); |
|
193 TInt sortCount = sqlite3_stmt_status(iStmtHandle, SQLITE_STMTSTATUS_SORT, ETrue); |
|
194 SYMBIAN_TRACE_SQL_EVENTS_ONLY(UTF::Printf(UTF::TTraceContext(UTF::EInternals), KSrvStmtStatus, this, scanCount, sortCount)); |
|
195 #endif |
|
196 (void)sqlite3_finalize(iStmtHandle); |
|
197 } |
|
198 } |
|
199 |
|
200 /** |
|
201 Sets SQL statement parameter values. |
|
202 |
|
203 Only parameters, which values are set by the client, will be processed. |
|
204 |
|
205 @param aParamBuf Flat buffer with parameter values. |
|
206 |
|
207 @leave KErrArgument, unknown parameter type; |
|
208 KSqlErrStmtExpired, statement handle expired. |
|
209 Note that the function may also leave with some other database specific |
|
210 errors categorised as ESqlDbError. |
|
211 |
|
212 @panic SqlDb 2 In _DEBUG mode. Invalid (not created) CSqlSrvStatement object. |
|
213 */ |
|
214 void CSqlSrvStatement::BindL(const RSqlBufFlat& aParamBuf) |
|
215 { |
|
216 __SQLASSERT(iStmtHandle != NULL, ESqlPanicInvalidObj); |
|
217 |
|
218 (void)sqlite3SymbianLastOsError();//clear last OS error |
|
219 if(sqlite3_expired(iStmtHandle)) |
|
220 { |
|
221 __SQLLEAVE_IF_ERROR(KSqlErrStmtExpired); |
|
222 } |
|
223 |
|
224 TSqlBufRIterator it; |
|
225 it.Set(aParamBuf); |
|
226 TInt prmIdx = 0; |
|
227 |
|
228 TInt err = SQLITE_OK; |
|
229 while(it.Next() && err == SQLITE_OK) |
|
230 { |
|
231 ++prmIdx;//the first SQLITE parameter index is 1 |
|
232 if(it.IsPresent()) |
|
233 { |
|
234 switch(it.Type()) |
|
235 { |
|
236 case ESqlInt: |
|
237 err = sqlite3_bind_int(iStmtHandle, prmIdx, it.Int()); |
|
238 break; |
|
239 case ESqlInt64: |
|
240 err = sqlite3_bind_int64(iStmtHandle, prmIdx, it.Int64()); |
|
241 break; |
|
242 case ESqlReal: |
|
243 err = sqlite3_bind_double(iStmtHandle, prmIdx, it.Real()); |
|
244 break; |
|
245 case ESqlText: |
|
246 //SQLITE_STATIC is used as an argument, because the text data will be kept and can be used by the next bind call |
|
247 { |
|
248 TPtrC text = it.Text(); |
|
249 TPtrC8 prmDataCopy(reinterpret_cast <const TUint8*> (text.Ptr()), text.Length() * sizeof(TUint16)); |
|
250 prmDataCopy.Set(CopyAndStoreParamL(prmIdx - 1, HSqlSrvStmtParamBuf::EText16, prmDataCopy)); |
|
251 err = sqlite3_bind_text16(iStmtHandle, prmIdx, prmDataCopy.Ptr(), prmDataCopy.Length(), SQLITE_STATIC); |
|
252 } |
|
253 break; |
|
254 case ESqlBinary: |
|
255 //SQLITE_STATIC is used as an argument, because the blob data will be kept and can be used by the next bind call |
|
256 { |
|
257 TPtrC8 prmDataCopy = CopyAndStoreParamL(prmIdx - 1, HSqlSrvStmtParamBuf::EBinary, it.Binary()); |
|
258 err = sqlite3_bind_blob(iStmtHandle, prmIdx, prmDataCopy.Ptr(), prmDataCopy.Length(), SQLITE_STATIC); |
|
259 } |
|
260 break; |
|
261 case ESqlNull: |
|
262 err = sqlite3_bind_null(iStmtHandle, prmIdx); |
|
263 break; |
|
264 case ESqlZeroBlob: |
|
265 err = sqlite3_bind_zeroblob(iStmtHandle, prmIdx, it.Int()); |
|
266 break; |
|
267 default: |
|
268 __SQLLEAVE(KErrArgument);//unknown parameter type |
|
269 break; |
|
270 } |
|
271 }//end of - if(it.IsPresent()) |
|
272 }//end of - while(it.Next() && err == SQLITE_OK) |
|
273 err = ::Sql2OsErrCode(err, sqlite3SymbianLastOsError()); |
|
274 __SQLLEAVE_IF_ERROR(err); |
|
275 } |
|
276 |
|
277 /** |
|
278 Collects column names in a flat buffer and returns a reference to the buffer. |
|
279 |
|
280 @return A const reference to a flat buffer containing the column values. |
|
281 |
|
282 @leave KErrNoMemory, an out of memory condition has occurred. |
|
283 |
|
284 @panic SqlDb 2 In _DEBUG mode. Invalid (not created) CSqlSrvStatement object. |
|
285 */ |
|
286 const RSqlBufFlat& CSqlSrvStatement::ColumnNamesL() |
|
287 { |
|
288 __SQLASSERT(iStmtHandle != NULL, ESqlPanicInvalidObj); |
|
289 iBufFlatType = static_cast <TSqlBufFlatType> (-1); |
|
290 __SQLLEAVE_IF_ERROR(iBufFlat.SetCount(iColumnCount)); |
|
291 TSqlBufWIterator it; |
|
292 it.Set(iBufFlat); |
|
293 TInt colIdx = -1; |
|
294 while(it.Next()) |
|
295 { |
|
296 ++colIdx;//the first SQLITE column index is 0 |
|
297 const TUint16* name = reinterpret_cast <const TUint16*> (__SQLLEAVE_IF_NULL(const_cast <void*> (sqlite3_column_name16(iStmtHandle, colIdx)))); |
|
298 TPtrC ptr(name, User::StringLength(name)); |
|
299 __SQLLEAVE_IF_ERROR(it.SetText(ptr)); |
|
300 } |
|
301 iBufFlatType = ESqlColumnNamesBuf; |
|
302 SQLPROFILER_REPORT_ALLOC(iBufFlat.MaxSize()); |
|
303 return iBufFlat; |
|
304 } |
|
305 |
|
306 /** |
|
307 Collects parameter names in a flat buffer and returns a reference to the buffer. |
|
308 |
|
309 @return A const reference to a flat buffer containing the column values. |
|
310 |
|
311 @leave KErrNoMemory, an out of memory condition has occurred. |
|
312 |
|
313 @panic SqlDb 2 In _DEBUG mode. Invalid (not created) CSqlSrvStatement object. |
|
314 */ |
|
315 const RSqlBufFlat& CSqlSrvStatement::ParamNamesL() |
|
316 { |
|
317 __SQLASSERT(iStmtHandle != NULL, ESqlPanicInvalidObj); |
|
318 iBufFlatType = static_cast <TSqlBufFlatType> (-1); |
|
319 __SQLLEAVE_IF_ERROR(iBufFlat.SetCount(iParamCount)); |
|
320 TSqlBufWIterator it; |
|
321 it.Set(iBufFlat); |
|
322 TInt prmIdx = 0; |
|
323 while(it.Next()) |
|
324 { |
|
325 ++prmIdx;//the first SQLITE parameter index is 1 |
|
326 const TUint8* name8 = reinterpret_cast <const TUint8*> (sqlite3_bind_parameter_name(iStmtHandle, prmIdx)); |
|
327 if(name8) |
|
328 { |
|
329 HBufC* name = CnvUtfConverter::ConvertToUnicodeFromUtf8L(TPtrC8(name8, User::StringLength(name8))); |
|
330 TInt err = it.SetText(name->Des()); |
|
331 delete name; |
|
332 __SQLLEAVE_IF_ERROR(err); |
|
333 } |
|
334 else //nameless parameter case |
|
335 { |
|
336 //The parameter name in this case will be formatted as "?<num>", where <num> is the parameter index. |
|
337 TBuf<5> prmName; |
|
338 prmName.Append(KNamelessParameter); |
|
339 prmName.AppendNum((TInt64)(prmIdx - 1)); |
|
340 __SQLLEAVE_IF_ERROR(it.SetText(prmName)); |
|
341 } |
|
342 } |
|
343 iBufFlatType = ESqlParamNamesBuf; |
|
344 SQLPROFILER_REPORT_ALLOC(iBufFlat.MaxSize()); |
|
345 return iBufFlat; |
|
346 } |
|
347 |
|
348 /** |
|
349 Collects the column values in a flat buffer and returns a reference to the buffer. |
|
350 |
|
351 @leave KErrNoMemory, an out of memory condition has occurred. |
|
352 |
|
353 @return A const reference to a flat buffer containing the column values. |
|
354 |
|
355 @panic SqlDb 2 In _DEBUG mode. Invalid (not created) CSqlSrvStatement object |
|
356 */ |
|
357 const RSqlBufFlat& CSqlSrvStatement::ColumnValuesL() |
|
358 { |
|
359 __SQLASSERT(iStmtHandle != NULL, ESqlPanicInvalidObj); |
|
360 |
|
361 iBufFlatType = static_cast <TSqlBufFlatType> (-1); |
|
362 iBufFlat.SetCount(iColumnCount); |
|
363 TSqlBufWIterator it; |
|
364 it.Set(iBufFlat); |
|
365 TInt colIdx = -1; |
|
366 |
|
367 while(it.Next()) |
|
368 { |
|
369 ++colIdx;//the first SQLITE column index is 0 |
|
370 TInt colType = sqlite3_column_type(iStmtHandle, colIdx); |
|
371 switch(colType) |
|
372 { |
|
373 case SQLITE_INTEGER: |
|
374 { |
|
375 TInt64 val = sqlite3_column_int64(iStmtHandle, colIdx); |
|
376 __SQLLEAVE_IF_ERROR(val == TInt64(TInt32(val)) ? it.SetInt(static_cast <TInt> (val)) : it.SetInt64(val)); |
|
377 } |
|
378 break; |
|
379 case SQLITE_FLOAT: |
|
380 __SQLLEAVE_IF_ERROR(it.SetReal(sqlite3_column_double(iStmtHandle, colIdx))); |
|
381 break; |
|
382 case SQLITE_TEXT: |
|
383 { |
|
384 TInt charLength = (TUint)sqlite3_column_bytes16(iStmtHandle, colIdx) / sizeof(TUint16); |
|
385 if(charLength >= KSqlMaxDesLen) |
|
386 { |
|
387 it.SetAsNotPresent(ESqlText, charLength); |
|
388 } |
|
389 else |
|
390 { |
|
391 __SQLLEAVE_IF_ERROR(it.SetText(TPtrC16(reinterpret_cast <const TUint16*> (sqlite3_column_text16(iStmtHandle, colIdx)), charLength))); |
|
392 } |
|
393 } |
|
394 break; |
|
395 case SQLITE_BLOB: |
|
396 { |
|
397 TInt byteLength = sqlite3_column_bytes(iStmtHandle, colIdx); |
|
398 if(byteLength >= KSqlMaxDesLen) |
|
399 { |
|
400 it.SetAsNotPresent(ESqlBinary, byteLength); |
|
401 } |
|
402 else |
|
403 { |
|
404 __SQLLEAVE_IF_ERROR(it.SetBinary(TPtrC8(reinterpret_cast <const TUint8*> (sqlite3_column_blob(iStmtHandle, colIdx)), byteLength))); |
|
405 } |
|
406 } |
|
407 break; |
|
408 case SQLITE_NULL: |
|
409 it.SetNull(); |
|
410 break; |
|
411 default: |
|
412 __SQLASSERT(EFalse, ESqlPanicInternalError); |
|
413 break; |
|
414 }//end of switch(...) |
|
415 }//end of - while(it.Next()) |
|
416 iBufFlatType = ESqlColumnValuesBuf; |
|
417 SQLPROFILER_REPORT_ALLOC(iBufFlat.MaxSize()); |
|
418 return iBufFlat; |
|
419 } |
|
420 |
|
421 /** |
|
422 This method sets aColumnSource parameter to point to the column data. |
|
423 |
|
424 @param aColumnIndex Column Index, zero based. |
|
425 @param aColumnSource Output parameter. It is set to point to the column data. |
|
426 |
|
427 @return KErrNone, the operation completed successfully; |
|
428 KErrArgument, the refered by aColumnIndex index column is not a binary or text column. |
|
429 |
|
430 @panic SqlDb 2 In _DEBUG mode. Invalid (not created) CSqlSrvStatement object. |
|
431 */ |
|
432 TInt CSqlSrvStatement::ColumnSource(TInt aColumnIndex, TPtrC8& aColumnSource) const |
|
433 { |
|
434 __SQLASSERT(iStmtHandle != NULL, ESqlPanicInvalidObj); |
|
435 TInt colType = sqlite3_column_type(iStmtHandle, aColumnIndex); |
|
436 if(colType == SQLITE_TEXT) |
|
437 { |
|
438 const void* text = sqlite3_column_text16(iStmtHandle, aColumnIndex); |
|
439 TInt length = sqlite3_column_bytes16(iStmtHandle, aColumnIndex); |
|
440 aColumnSource.Set(reinterpret_cast <const TUint8*> (text), length); |
|
441 } |
|
442 else if(colType == SQLITE_BLOB) |
|
443 { |
|
444 const void* data = sqlite3_column_blob(iStmtHandle, aColumnIndex); |
|
445 TInt length = sqlite3_column_bytes(iStmtHandle, aColumnIndex); |
|
446 aColumnSource.Set(reinterpret_cast <const TUint8*> (data), length); |
|
447 } |
|
448 else |
|
449 { |
|
450 return KErrArgument; |
|
451 } |
|
452 return KErrNone; |
|
453 } |
|
454 |
|
455 /** |
|
456 Retrieves from the SQLITE library columns and parameters count. |
|
457 |
|
458 @panic SqlDb 4 In _DEBUG mode. aDbHandle is NULL. |
|
459 */ |
|
460 void CSqlSrvStatement::DoCommonConstructL() |
|
461 { |
|
462 __SQLASSERT(iStmtHandle != NULL, ESqlPanicInvalidObj); |
|
463 iColumnCount = sqlite3_column_count(iStmtHandle); |
|
464 iParamCount = sqlite3_bind_parameter_count(iStmtHandle); |
|
465 __SQLLEAVE_IF_ERROR(iBufFlat.SetCount(Max(iColumnCount, iParamCount))); |
|
466 } |
|
467 |
|
468 /** |
|
469 Destroys the parameter buffer array (used for text or binary parameters). |
|
470 Before the array destruction, each array member is notified that the statement is about to be finalized. |
|
471 */ |
|
472 void CSqlSrvStatement::DestroyParamBufArray() |
|
473 { |
|
474 TInt idx = iParamBufArray.Count(); |
|
475 while(--idx >= 0) |
|
476 { |
|
477 if(iParamBufArray[idx]) |
|
478 { |
|
479 iParamBufArray[idx]->NotifyStatementFinalized(); |
|
480 } |
|
481 } |
|
482 iParamBufArray.Close(); |
|
483 } |
|
484 |
|
485 /** |
|
486 Binds a streamed text or binary parameter value. |
|
487 |
|
488 @param aParamIndex The text/binary parameter index |
|
489 |
|
490 @leave KErrNoMemory, an out of memory condition has occurred; |
|
491 KSqlErrStmtExpired, statement handle has expired. |
|
492 Note that the function may also leave with some other database specific |
|
493 errors categorised as ESqlDbError. |
|
494 |
|
495 @panic SqlDb 2 In _DEBUG mode. Invalid (not created) CSqlSrvStatement object. |
|
496 @panic SqlDb 4 In _DEBUG mode. No parameter buffer has been created yet for this parameter. |
|
497 @panic SqlDb 4 In _DEBUG mode. Parameter index out of bounds. |
|
498 */ |
|
499 void CSqlSrvStatement::BindParamBufL(TInt aParamIndex) |
|
500 { |
|
501 __SQLASSERT(iStmtHandle != NULL, ESqlPanicInvalidObj); |
|
502 __SQLASSERT(aParamIndex >= 0 && aParamIndex < sqlite3_bind_parameter_count(iStmtHandle), ESqlPanicBadArgument); |
|
503 __SQLASSERT(aParamIndex < iParamBufArray.Count(), ESqlPanicBadArgument); |
|
504 __SQLASSERT(iParamBufArray[aParamIndex] != NULL, ESqlPanicBadArgument); |
|
505 (void)sqlite3SymbianLastOsError();//clear last OS error |
|
506 if(sqlite3_expired(iStmtHandle)) |
|
507 { |
|
508 __SQLLEAVE_IF_ERROR(KSqlErrStmtExpired); |
|
509 } |
|
510 //Bind the parameter value. |
|
511 //SQLITE_STATIC is used as an argument, because the text/blob data will be kept and can be used by the next bind call |
|
512 HSqlSrvStmtParamBuf& paramBuf = *iParamBufArray[aParamIndex]; |
|
513 const TPtrC8 paramData(paramBuf.Data()); |
|
514 SQLPROFILER_REPORT_ALLOC(paramData.Length()); |
|
515 TInt err = KErrNone; |
|
516 ++aParamIndex;//SQLite uses positive parameter indexes, the SQL server - parameter indexes begin from 0 |
|
517 switch(paramBuf.DataType()) |
|
518 { |
|
519 case HSqlSrvStmtParamBuf::EText16: |
|
520 //sqlite3_bind_text16() expects 4-th argument to be the bytes count, not the characters count. |
|
521 err = sqlite3_bind_text16(iStmtHandle, aParamIndex, paramData.Ptr(), paramData.Length(), SQLITE_STATIC); |
|
522 break; |
|
523 case HSqlSrvStmtParamBuf::EBinary: |
|
524 default: |
|
525 err = sqlite3_bind_blob(iStmtHandle, aParamIndex, paramData.Ptr(), paramData.Length(), SQLITE_STATIC); |
|
526 break; |
|
527 } |
|
528 err = ::Sql2OsErrCode(err, sqlite3SymbianLastOsError()); |
|
529 __SQLLEAVE_IF_ERROR(err); |
|
530 } |
|
531 |
|
532 /** |
|
533 @return Represents the content of the column identified by aColIdx as integer value. |
|
534 If the current column type does not refer to an integer, then |
|
535 the function will do a data conversion as described in the table which can be found |
|
536 in SqlDb.h file. |
|
537 @see RSqlStatement |
|
538 |
|
539 @panic SqlDb 2 In _DEBUG mode. Invalid (not created) CSqlSrvStatement object. |
|
540 @panic SqlDb 4 In _DEBUG mode. Invalid aColIdx value. |
|
541 */ |
|
542 TInt CSqlSrvStatement::ColumnInt(TInt aColIdx) const |
|
543 { |
|
544 __SQLASSERT(iStmtHandle != NULL, ESqlPanicInvalidObj); |
|
545 __SQLASSERT((TUint)aColIdx < iColumnCount, ESqlPanicBadArgument); |
|
546 TInt colType = sqlite3_column_type(iStmtHandle, aColIdx); |
|
547 switch(colType) |
|
548 { |
|
549 case SQLITE_FLOAT: |
|
550 { |
|
551 TReal roundVal; |
|
552 TInt err = Math::Round(roundVal, sqlite3_column_double(iStmtHandle, aColIdx), 0); |
|
553 if(err != KErrNone) |
|
554 { |
|
555 return KMinTInt; |
|
556 } |
|
557 TRealX val(roundVal); |
|
558 return static_cast <TInt> (val); |
|
559 } |
|
560 case SQLITE_NULL: |
|
561 case SQLITE_TEXT: |
|
562 case SQLITE_BLOB: |
|
563 return 0; |
|
564 default: //int, int64 |
|
565 { |
|
566 TInt64 val = sqlite3_column_int64(iStmtHandle, aColIdx); |
|
567 return val == (TInt)val ? (TInt)val : (val < KMinTInt ? KMinTInt : KMaxTInt); |
|
568 } |
|
569 } |
|
570 } |
|
571 |
|
572 /** |
|
573 @return Represents the content of the column identified by aColIdx as 64-bit integer value. |
|
574 If the current column type does not refer to a 64-bit integer, then |
|
575 the function will do a data conversion as described in the table which can be found |
|
576 in SqlDb.h file. |
|
577 @see RSqlStatement |
|
578 |
|
579 @panic SqlDb 2 In _DEBUG mode. Invalid (not created) CSqlSrvStatement object. |
|
580 @panic SqlDb 4 In _DEBUG mode. Invalid aColIdx value. |
|
581 */ |
|
582 TInt64 CSqlSrvStatement::ColumnInt64(TInt aColIdx) const |
|
583 { |
|
584 __SQLASSERT(iStmtHandle != NULL, ESqlPanicInvalidObj); |
|
585 __SQLASSERT((TUint)aColIdx < iColumnCount, ESqlPanicBadArgument); |
|
586 TInt colType = sqlite3_column_type(iStmtHandle, aColIdx); |
|
587 switch(colType) |
|
588 { |
|
589 case SQLITE_FLOAT: |
|
590 { |
|
591 TReal roundVal; |
|
592 TInt err = Math::Round(roundVal, sqlite3_column_double(iStmtHandle, aColIdx), 0); |
|
593 if(err != KErrNone) |
|
594 { |
|
595 return KMinTInt64; |
|
596 } |
|
597 TRealX val(roundVal); |
|
598 return static_cast <TInt64> (val); |
|
599 } |
|
600 case SQLITE_NULL: |
|
601 case SQLITE_TEXT: |
|
602 case SQLITE_BLOB: |
|
603 return 0; |
|
604 default: //int, int64 |
|
605 return sqlite3_column_int64(iStmtHandle, aColIdx); |
|
606 } |
|
607 } |
|
608 |
|
609 /** |
|
610 @return Represents the content of the column identified by aColIdx as real value. |
|
611 If the current column type does not refer to a real, then |
|
612 the function will do a data conversion as described in the table which can be found |
|
613 in SqlDb.h file. |
|
614 @see RSqlStatement |
|
615 |
|
616 @panic SqlDb 2 In _DEBUG mode. Invalid (not created) CSqlSrvStatement object. |
|
617 @panic SqlDb 4 In _DEBUG mode. Invalid aColIdx value. |
|
618 */ |
|
619 TReal CSqlSrvStatement::ColumnReal(TInt aColIdx) const |
|
620 { |
|
621 __SQLASSERT(iStmtHandle != NULL, ESqlPanicInvalidObj); |
|
622 __SQLASSERT((TUint)aColIdx < iColumnCount, ESqlPanicBadArgument); |
|
623 TInt colType = sqlite3_column_type(iStmtHandle, aColIdx); |
|
624 switch(colType) |
|
625 { |
|
626 case SQLITE_INTEGER: |
|
627 { |
|
628 TRealX val(sqlite3_column_int64(iStmtHandle, aColIdx)); |
|
629 return static_cast <TReal> (val); |
|
630 } |
|
631 case SQLITE_NULL: |
|
632 case SQLITE_TEXT: |
|
633 case SQLITE_BLOB: |
|
634 return 0.0; |
|
635 default: |
|
636 return sqlite3_column_double(iStmtHandle, aColIdx); |
|
637 } |
|
638 } |
|
639 |
|
640 /** |
|
641 Represents the content of the column identified by aColIdx as text (16 bit) descriptor. |
|
642 If the current column type does not refer to a text block of data, then |
|
643 the function will do a data conversion as described in the table which can be found |
|
644 in SqlDb.h file. |
|
645 |
|
646 @see RSqlStatement |
|
647 |
|
648 @panic SqlDb 2 In _DEBUG mode. Invalid (not created) CSqlSrvStatement object. |
|
649 @panic SqlDb 4 In _DEBUG mode. Invalid aColIdx value. |
|
650 */ |
|
651 TPtrC CSqlSrvStatement::ColumnText(TInt aColIdx) const |
|
652 { |
|
653 __SQLASSERT(iStmtHandle != NULL, ESqlPanicInvalidObj); |
|
654 __SQLASSERT((TUint)aColIdx < iColumnCount, ESqlPanicBadArgument); |
|
655 TPtrC res; |
|
656 TInt colType = sqlite3_column_type(iStmtHandle, aColIdx); |
|
657 if(colType == SQLITE_TEXT) |
|
658 { |
|
659 TInt charLength = (TUint)sqlite3_column_bytes16(iStmtHandle, aColIdx) / sizeof(TUint16); |
|
660 res.Set(reinterpret_cast <const TUint16*> (sqlite3_column_text16(iStmtHandle, aColIdx)), charLength); |
|
661 } |
|
662 return res; |
|
663 } |
|
664 |
|
665 /** |
|
666 Represents the content of the column identified by aColIdx as binary (8 bit) descriptor. |
|
667 If the current column type does not refer to a binary block of data, then |
|
668 the function will do a data conversion as described in the table which can be found |
|
669 in SqlDb.h file. |
|
670 |
|
671 @see RSqlStatement |
|
672 |
|
673 @panic SqlDb 2 In _DEBUG mode. Invalid (not created) CSqlSrvStatement object. |
|
674 @panic SqlDb 4 In _DEBUG mode. Invalid aColIdx value. |
|
675 */ |
|
676 TPtrC8 CSqlSrvStatement::ColumnBinary(TInt aColIdx) const |
|
677 { |
|
678 __SQLASSERT(iStmtHandle != NULL, ESqlPanicInvalidObj); |
|
679 __SQLASSERT((TUint)aColIdx < iColumnCount, ESqlPanicBadArgument); |
|
680 TPtrC8 res; |
|
681 TInt colType = sqlite3_column_type(iStmtHandle, aColIdx); |
|
682 if(colType == SQLITE_BLOB) |
|
683 { |
|
684 TInt byteLength = sqlite3_column_bytes(iStmtHandle, aColIdx); |
|
685 res.Set(reinterpret_cast <const TUint8*> (sqlite3_column_blob(iStmtHandle, aColIdx)), byteLength); |
|
686 } |
|
687 return res; |
|
688 } |
|
689 |
|
690 /** |
|
691 This function is used by the DBMS emulation library only. |
|
692 The function retrieves the declared column types from the SQLITE library, compiles them in a single string |
|
693 and then returns the string to the caller. |
|
694 |
|
695 The function also initializes iColumnText8 array, where particular array element with index "idx" will |
|
696 be set to 1, if the column with index "idx" is a 8-bit text column. |
|
697 |
|
698 @return A pointer to a heap allocated HBufC object with the delcared column types. The caller is responsible |
|
699 for the HBufC object destruction. |
|
700 */ |
|
701 HBufC* CSqlSrvStatement::GetDeclColumnTypesL() |
|
702 { |
|
703 HBufC* buf = HBufC::NewL(iColumnCount * 20);//20 as length is enough for a single column type text |
|
704 TPtr ptr = buf->Des(); |
|
705 for(TInt i=0;i<iColumnCount;++i) |
|
706 { |
|
707 const TUint16* declTypeTxt = reinterpret_cast <const TUint16*> (sqlite3_column_decltype16(iStmtHandle, i)); |
|
708 if(declTypeTxt) |
|
709 { |
|
710 TPtrC type(declTypeTxt, User::StringLength(declTypeTxt)); |
|
711 ptr.Append(type); |
|
712 } |
|
713 ptr.Append(TChar(';')); |
|
714 } |
|
715 return buf; |
|
716 } |
|
717 |
|
718 /** |
|
719 Creates a new HSqlSrvStmtParamBuf object. |
|
720 |
|
721 @param aParameterIndex Parameter index, zero based. |
|
722 @param aDataType Parameter value type - binary, text8 or text16. |
|
723 @param aIsStreamBuf True if the param data will be retrieved from an IPC stream |
|
724 |
|
725 @return A pointer to the created HSqlSrvStmtParamBuf instance. |
|
726 |
|
727 @leave KErrNoMemory, an out of memory condition has occurred; |
|
728 */ |
|
729 HSqlSrvStmtParamBuf* CSqlSrvStatement::GetParamBufL(TInt aParamIndex, HSqlSrvStmtParamBuf::TDataType aDataType, |
|
730 HSqlSrvStmtParamBuf::TBufType aBufType) |
|
731 { |
|
732 __SQLASSERT(aParamIndex >= 0 && aParamIndex < sqlite3_bind_parameter_count(iStmtHandle), ESqlPanicBadArgument); |
|
733 ExtendParamBufArrayL(aParamIndex); |
|
734 HSqlSrvStmtParamBuf*& paramBuf = iParamBufArray[aParamIndex]; |
|
735 if(paramBuf) |
|
736 {//Reset and reuse the existing buffer |
|
737 __SQLASSERT(paramBuf->ParamIndex() == aParamIndex, ESqlPanicInternalError); |
|
738 paramBuf->Reset(aDataType, aBufType); |
|
739 } |
|
740 else |
|
741 { |
|
742 paramBuf = HSqlSrvStmtParamBuf::NewL(*this, aParamIndex, aDataType, aBufType); |
|
743 } |
|
744 return paramBuf; |
|
745 } |
|
746 |
|
747 /** |
|
748 This function will extend the iParamBufArray array (filling the new array items with NULL), if it is needed - |
|
749 to ensure that there is enough place for the buffer for the parameter identified by aParamIndex. |
|
750 |
|
751 @param aParamIndex The parameter index |
|
752 |
|
753 @leave KErrNoMemory, an out of memory condition has occurred; |
|
754 |
|
755 @panic SqlDb 2 In _DEBUG mode. Invalid (not created) CSqlSrvStatement object. |
|
756 @panic SqlDb 4 In _DEBUG mode. Parameter index out of bounds. |
|
757 */ |
|
758 void CSqlSrvStatement::ExtendParamBufArrayL(TInt aParamIndex) |
|
759 { |
|
760 __SQLASSERT(iStmtHandle != NULL, ESqlPanicInvalidObj); |
|
761 __SQLASSERT(aParamIndex >= 0 && aParamIndex < sqlite3_bind_parameter_count(iStmtHandle), ESqlPanicBadArgument); |
|
762 TInt ext = aParamIndex - iParamBufArray.Count() + 1; |
|
763 while(ext-- > 0) |
|
764 { |
|
765 __SQLLEAVE_IF_ERROR(iParamBufArray.Append(NULL)); |
|
766 } |
|
767 } |
|
768 |
|
769 /** |
|
770 This function will create a copy of the aParamValue and store it in the iParamBufArray array for later use. |
|
771 The reason: once bound, the parameter value can be used multiple times by the SQLite if it is not set explicitly again. |
|
772 |
|
773 @param aParamIndex The parameter index |
|
774 @param aDataType Parameter value type - binary, text8 or text16. |
|
775 @param aParamValue The parameter value |
|
776 |
|
777 @return 8-bit descriptor to the stored parameter value |
|
778 |
|
779 @leave KErrNoMemory, an out of memory condition has occurred; |
|
780 |
|
781 @panic SqlDb 2 In _DEBUG mode. Invalid (not created) CSqlSrvStatement object. |
|
782 @panic SqlDb 4 In _DEBUG mode. Parameter index out of bounds. |
|
783 */ |
|
784 TPtrC8 CSqlSrvStatement::CopyAndStoreParamL(TInt aParamIndex, HSqlSrvStmtParamBuf::TDataType aDataType, const TDesC8& aParamValue) |
|
785 { |
|
786 __SQLASSERT(iStmtHandle != NULL, ESqlPanicInvalidObj); |
|
787 __SQLASSERT(aParamIndex >= 0 && aParamIndex < sqlite3_bind_parameter_count(iStmtHandle), ESqlPanicBadArgument); |
|
788 HSqlSrvStmtParamBuf* paramBuf = GetParamBufL(aParamIndex, aDataType, HSqlSrvStmtParamBuf::EBufSimpleBind); |
|
789 return paramBuf->SetDataL(aParamValue); |
|
790 } |