implemented DB driver call DrvGetFieldUnbufferedUTF8 (for databases with native UTF...
[public/netxms.git] / src / db / dbdrv / sqlite / sqlite.cpp
CommitLineData
5039dede
AK
1/*
2** SQLite Database Driver
2df047f4 3** Copyright (C) 2005-2016 Victor Kirhenshtein
5039dede
AK
4**
5** This program is free software; you can redistribute it and/or modify
6** it under the terms of the GNU General Public License as published by
7** the Free Software Foundation; either version 2 of the License, or
8** (at your option) any later version.
9**
10** This program is distributed in the hope that it will be useful,
11** but WITHOUT ANY WARRANTY; without even the implied warranty of
12** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13** GNU General Public License for more details.
14**
15** You should have received a copy of the GNU General Public License
16** along with this program; if not, write to the Free Software
17** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18**
19** File: sqlite.cpp
20**
21**/
22
23#include "sqlitedrv.h"
24
17b35ccc 25DECLARE_DRIVER_HEADER("SQLITE")
5039dede 26
e8fd4c59
VK
27/**
28 * Get error message from connection
29 */
ec19ded8 30static void GetErrorMessage(sqlite3 *hdb, WCHAR *errorText)
5039dede 31{
ec19ded8
VK
32 if (errorText == NULL)
33 return;
5039dede 34
ec19ded8
VK
35#if UNICODE_UCS2
36 wcsncpy(errorText, (const WCHAR *)sqlite3_errmsg16(hdb), DBDRV_MAX_ERROR_TEXT);
37#else
73577be6 38 MultiByteToWideChar(CP_UTF8, 0, sqlite3_errmsg(hdb), -1, errorText, DBDRV_MAX_ERROR_TEXT);
ec19ded8
VK
39#endif
40 errorText[DBDRV_MAX_ERROR_TEXT - 1] = 0;
41 RemoveTrailingCRLFW(errorText);
5039dede
AK
42}
43
e8fd4c59
VK
44/**
45 * Prepare string for using in SQL query - enclose in quotes and escape as needed
46 */
619e5c9b 47extern "C" char EXPORT *DrvPrepareStringA(const char *str)
643c9dcb 48{
619e5c9b 49 int len = (int)strlen(str) + 3; // + two quotes and \0 at the end
643c9dcb 50 int bufferSize = len + 128;
619e5c9b
VK
51 char *out = (char *)malloc(bufferSize);
52 out[0] = '\'';
643c9dcb 53
619e5c9b 54 const char *src = str;
643c9dcb 55 int outPos;
0322eed7 56 for(outPos = 1; *src != 0; src++)
643c9dcb 57 {
619e5c9b 58 if (*src == '\'')
643c9dcb
VK
59 {
60 len++;
61 if (len >= bufferSize)
62 {
63 bufferSize += 128;
619e5c9b 64 out = (char *)realloc(out, bufferSize);
643c9dcb 65 }
619e5c9b
VK
66 out[outPos++] = '\'';
67 out[outPos++] = '\'';
643c9dcb
VK
68 }
69 else
70 {
71 out[outPos++] = *src;
72 }
73 }
619e5c9b
VK
74 out[outPos++] = '\'';
75 out[outPos++] = 0;
76
77 return out;
78}
79
e8fd4c59
VK
80/**
81 * Prepare string for using in SQL query - enclose in quotes and escape as needed
82 */
619e5c9b
VK
83extern "C" WCHAR EXPORT *DrvPrepareStringW(const WCHAR *str)
84{
85 int len = (int)wcslen(str) + 3; // + two quotes and \0 at the end
86 int bufferSize = len + 128;
87 WCHAR *out = (WCHAR *)malloc(bufferSize * sizeof(WCHAR));
88 out[0] = L'\'';
89
90 const WCHAR *src = str;
91 int outPos;
92 for(outPos = 1; *src != 0; src++)
93 {
94 if (*src == L'\'')
95 {
96 len++;
97 if (len >= bufferSize)
98 {
99 bufferSize += 128;
100 out = (WCHAR *)realloc(out, bufferSize * sizeof(WCHAR));
101 }
102 out[outPos++] = L'\'';
103 out[outPos++] = L'\'';
104 }
105 else
106 {
107 out[outPos++] = *src;
108 }
109 }
110 out[outPos++] = L'\'';
643c9dcb
VK
111 out[outPos++] = 0;
112
113 return out;
114}
115
e8fd4c59
VK
116/**
117 * Initialize driver
118 */
2df047f4 119extern "C" bool EXPORT DrvInit(const char *cmdLine)
5039dede 120{
66c11d49
VK
121 if (!sqlite3_threadsafe() || // Fail if SQLite compiled without threading support
122 (sqlite3_initialize() != SQLITE_OK))
123 return false;
124 sqlite3_enable_shared_cache(1);
2df047f4 125 nxlog_debug(1, _T("SQLite version %hs"), sqlite3_libversion());
66c11d49 126 return true;
5039dede
AK
127}
128
e8fd4c59
VK
129/**
130 * Unload handler
131 */
08b214c6 132extern "C" void EXPORT DrvUnload()
5039dede 133{
2a8e6a7b 134 sqlite3_shutdown();
5039dede
AK
135}
136
e8fd4c59
VK
137/**
138 * Connect to database
139 */
ec19ded8 140extern "C" DBDRV_CONNECTION EXPORT DrvConnect(const char *host, const char *login,
f3c30cf5 141 const char *password, const char *database, const char *schema, WCHAR *errorText)
ec19ded8
VK
142{
143 SQLITE_CONN *pConn;
144 sqlite3 *hdb;
145
146 if (sqlite3_open_v2(database, &hdb, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL) == SQLITE_OK)
147 {
148 sqlite3_busy_timeout(hdb, 30000); // 30 sec. busy timeout
149
150 // Create new handle
151 pConn = (SQLITE_CONN *)malloc(sizeof(SQLITE_CONN));
152 memset(pConn, 0, sizeof(SQLITE_CONN));
153 pConn->pdb = hdb;
154 pConn->mutexQueryLock = MutexCreate();
e8fd4c59
VK
155
156 sqlite3_exec(hdb, "PRAGMA foreign_keys = ON", NULL, NULL, NULL);
ec19ded8
VK
157 }
158 else
159 {
160 GetErrorMessage(hdb, errorText);
161 pConn = NULL;
162 sqlite3_close(hdb);
163 }
164 return (DBDRV_CONNECTION)pConn;
165}
166
e8fd4c59
VK
167/**
168 * Disconnect from database
169 */
5039dede
AK
170extern "C" void EXPORT DrvDisconnect(SQLITE_CONN *hConn)
171{
ec19ded8
VK
172 if (hConn == NULL)
173 return;
174
175 sqlite3_close(hConn->pdb);
176 MutexDestroy(hConn->mutexQueryLock);
177 free(hConn);
178}
179
e8fd4c59
VK
180/**
181 * Prepare statement
182 */
de1d708f 183extern "C" DBDRV_STATEMENT EXPORT DrvPrepare(SQLITE_CONN *hConn, WCHAR *pwszQuery, DWORD *pdwError, WCHAR *errorText)
ec19ded8
VK
184{
185 char *pszQueryUTF8 = UTF8StringFromWideString(pwszQuery);
c17f6cbc 186 MutexLock(hConn->mutexQueryLock);
ec19ded8 187 sqlite3_stmt *stmt;
66c11d49
VK
188
189retry:
190 int rc = sqlite3_prepare_v2(hConn->pdb, pszQueryUTF8, -1, &stmt, NULL);
191 if ((rc == SQLITE_LOCKED) || (rc == SQLITE_LOCKED_SHAREDCACHE))
192 {
193 // database locked by another thread, retry in 10 milliseconds
194 ThreadSleepMs(10);
195 goto retry;
196 }
197 else if (rc != SQLITE_OK)
5039dede 198 {
ec19ded8
VK
199 GetErrorMessage(hConn->pdb, errorText);
200 stmt = NULL;
de1d708f 201 *pdwError = DBERR_OTHER_ERROR;
5039dede 202 }
ec19ded8
VK
203 MutexUnlock(hConn->mutexQueryLock);
204 free(pszQueryUTF8);
205 return stmt;
5039dede
AK
206}
207
e8fd4c59
VK
208/**
209 * Bind parameter to statement
210 */
cd5bb14b 211extern "C" void EXPORT DrvBind(sqlite3_stmt *stmt, int pos, int sqlType, int cType, void *buffer, int allocType)
5039dede 212{
ec19ded8
VK
213 switch(cType)
214 {
215 case DB_CTYPE_STRING:
216#if UNICODE_UCS2
e8fd4c59 217 sqlite3_bind_text16(stmt, pos, buffer, (int)wcslen((WCHAR *)buffer) * sizeof(WCHAR),
cd5bb14b 218 (allocType == DB_BIND_STATIC) ? SQLITE_STATIC : ((allocType == DB_BIND_DYNAMIC) ? free : SQLITE_TRANSIENT));
ec19ded8 219#else
73577be6
VK
220 {
221 char *utf8String = UTF8StringFromWideString((WCHAR *)buffer);
222 sqlite3_bind_text(stmt, pos, utf8String, strlen(utf8String), free);
223 if (allocType == DB_BIND_DYNAMIC)
224 safe_free(buffer);
225 }
ec19ded8
VK
226#endif
227 break;
b8849590
VK
228 case DB_CTYPE_UTF8_STRING:
229 sqlite3_bind_text(stmt, pos, (char *)buffer, strlen((char *)buffer),
230 (allocType == DB_BIND_STATIC) ? SQLITE_STATIC : ((allocType == DB_BIND_DYNAMIC) ? free : SQLITE_TRANSIENT));
231 break;
ec19ded8
VK
232 case DB_CTYPE_INT32:
233 case DB_CTYPE_UINT32:
234 sqlite3_bind_int(stmt, pos, *((int *)buffer));
99ddf722
VK
235 if (allocType == DB_BIND_DYNAMIC)
236 safe_free(buffer);
ec19ded8
VK
237 break;
238 case DB_CTYPE_INT64:
239 case DB_CTYPE_UINT64:
240 sqlite3_bind_int64(stmt, pos, *((sqlite3_int64 *)buffer));
99ddf722
VK
241 if (allocType == DB_BIND_DYNAMIC)
242 safe_free(buffer);
ec19ded8
VK
243 break;
244 case DB_CTYPE_DOUBLE:
245 sqlite3_bind_double(stmt, pos, *((double *)buffer));
99ddf722
VK
246 if (allocType == DB_BIND_DYNAMIC)
247 safe_free(buffer);
ec19ded8
VK
248 break;
249 default:
99ddf722
VK
250 if (allocType == DB_BIND_DYNAMIC)
251 safe_free(buffer);
ec19ded8
VK
252 break;
253 }
254}
255
74d4ba34
VK
256/**
257 * Execute prepared statement
258 */
ec19ded8
VK
259extern "C" DWORD EXPORT DrvExecute(SQLITE_CONN *hConn, sqlite3_stmt *stmt, WCHAR *errorText)
260{
261 DWORD result;
5039dede 262
c17f6cbc 263 MutexLock(hConn->mutexQueryLock);
66c11d49 264retry:
114bd5c6
VK
265 int rc = sqlite3_step(stmt);
266 if ((rc == SQLITE_DONE) || (rc == SQLITE_ROW))
2a8e6a7b 267 {
114bd5c6
VK
268 if (sqlite3_reset(stmt) == SQLITE_OK)
269 {
270 result = DBERR_SUCCESS;
271 }
272 else
273 {
274 GetErrorMessage(hConn->pdb, errorText);
275 result = DBERR_OTHER_ERROR;
276 }
2a8e6a7b 277 }
66c11d49
VK
278 else if ((rc == SQLITE_LOCKED) || (rc == SQLITE_LOCKED_SHAREDCACHE))
279 {
280 // database locked by another thread, retry in 10 milliseconds
281 ThreadSleepMs(10);
282 sqlite3_reset(stmt);
283 goto retry;
284 }
ec19ded8
VK
285 else
286 {
287 GetErrorMessage(hConn->pdb, errorText);
288 result = DBERR_OTHER_ERROR;
114bd5c6
VK
289
290 sqlite3_reset(stmt);
ec19ded8
VK
291 }
292 MutexUnlock(hConn->mutexQueryLock);
293 return result;
294}
295
74d4ba34
VK
296/**
297 * Destroy prepared statement
298 */
ec19ded8
VK
299extern "C" void EXPORT DrvFreeStatement(sqlite3_stmt *stmt)
300{
74d4ba34
VK
301 if (stmt != NULL)
302 sqlite3_finalize(stmt);
5039dede
AK
303}
304
74d4ba34
VK
305/**
306 * Internal query
307 */
465b3f2d 308static DWORD DrvQueryInternal(SQLITE_CONN *pConn, const char *pszQuery, WCHAR *errorText)
5039dede 309{
ec19ded8 310 DWORD result;
5039dede 311
c17f6cbc 312 MutexLock(pConn->mutexQueryLock);
66c11d49
VK
313retry:
314 int rc = sqlite3_exec(pConn->pdb, pszQuery, NULL, NULL, NULL);
315 if (rc == SQLITE_OK)
ec19ded8
VK
316 {
317 result = DBERR_SUCCESS;
318 }
66c11d49
VK
319 else if ((rc == SQLITE_LOCKED) || (rc == SQLITE_LOCKED_SHAREDCACHE))
320 {
321 // database locked by another thread, retry in 10 milliseconds
322 ThreadSleepMs(10);
323 goto retry;
324 }
ec19ded8
VK
325 else
326 {
327 GetErrorMessage(pConn->pdb, errorText);
328 result = DBERR_OTHER_ERROR;
329 }
5039dede 330 MutexUnlock(pConn->mutexQueryLock);
ec19ded8 331 return result;
5039dede
AK
332}
333
e8fd4c59
VK
334/**
335 * Perform non-SELECT query
336 */
465b3f2d 337extern "C" DWORD EXPORT DrvQuery(SQLITE_CONN *pConn, WCHAR *pwszQuery, WCHAR *errorText)
5039dede
AK
338{
339 DWORD dwResult;
340 char *pszQueryUTF8;
341
342 pszQueryUTF8 = UTF8StringFromWideString(pwszQuery);
343 dwResult = DrvQueryInternal(pConn, pszQueryUTF8, errorText);
344 free(pszQueryUTF8);
345 return dwResult;
346}
347
e8fd4c59
VK
348/**
349 * SELECT callback
350 */
ec19ded8
VK
351static int SelectCallback(void *pArg, int nCols, char **ppszData, char **ppszNames)
352{
353 int i, nPos, nMaxCol;
354
355 if (((SQLITE_RESULT *)pArg)->nCols == 0)
356 {
357 ((SQLITE_RESULT *)pArg)->nCols = nCols;
358 nMaxCol = nCols;
359 }
360 else
361 {
362 nMaxCol = min(((SQLITE_RESULT *)pArg)->nCols, nCols);
363 }
364
365 // Store column names
366 if ((((SQLITE_RESULT *)pArg)->ppszNames == NULL) && (nCols > 0) && (ppszNames != NULL))
367 {
368 ((SQLITE_RESULT *)pArg)->ppszNames = (char **)malloc(sizeof(char *) * nCols);
369 for(i = 0; i < nCols; i++)
370 ((SQLITE_RESULT *)pArg)->ppszNames[i] = strdup(ppszNames[i]);
371 }
372
373 nPos = ((SQLITE_RESULT *)pArg)->nRows * ((SQLITE_RESULT *)pArg)->nCols;
374 ((SQLITE_RESULT *)pArg)->nRows++;
375 ((SQLITE_RESULT *)pArg)->ppszData = (char **)realloc(((SQLITE_RESULT *)pArg)->ppszData,
376 sizeof(char *) * ((SQLITE_RESULT *)pArg)->nCols * ((SQLITE_RESULT *)pArg)->nRows);
377
378 for(i = 0; i < nMaxCol; i++, nPos++)
d7970ee7 379 ((SQLITE_RESULT *)pArg)->ppszData[nPos] = strdup(CHECK_NULL_EX_A(ppszData[i]));
ec19ded8
VK
380 for(; i < ((SQLITE_RESULT *)pArg)->nCols; i++, nPos++)
381 ((SQLITE_RESULT *)pArg)->ppszData[nPos] = strdup("");
382 return 0;
383}
384
e8fd4c59
VK
385/**
386 * Free SELECT results
387 */
ec19ded8
VK
388extern "C" void EXPORT DrvFreeResult(SQLITE_RESULT *hResult)
389{
390 int i, nCount;
391
392 if (hResult != NULL)
393 {
394 if (hResult->ppszData != NULL)
395 {
396 nCount = hResult->nRows * hResult->nCols;
397 for(i = 0; i < nCount; i++)
398 safe_free(hResult->ppszData[i]);
399 free(hResult->ppszData);
400
401 for(i = 0; i < hResult->nCols; i++)
402 safe_free(hResult->ppszNames[i]);
403 free(hResult->ppszNames);
404 }
405 free(hResult);
406 }
407}
408
e8fd4c59
VK
409/**
410 * Perform SELECT query
411 */
465b3f2d 412extern "C" DBDRV_RESULT EXPORT DrvSelect(SQLITE_CONN *hConn, WCHAR *pwszQuery, DWORD *pdwError, WCHAR *errorText)
5039dede 413{
ec19ded8 414 char *pszQueryUTF8 = UTF8StringFromWideString(pwszQuery);
5039dede 415
ec19ded8
VK
416 SQLITE_RESULT *result = (SQLITE_RESULT *)malloc(sizeof(SQLITE_RESULT));
417 memset(result, 0, sizeof(SQLITE_RESULT));
418
c17f6cbc 419 MutexLock(hConn->mutexQueryLock);
66c11d49
VK
420retry:
421 int rc = sqlite3_exec(hConn->pdb, pszQueryUTF8, SelectCallback, result, NULL);
422 if ((rc == SQLITE_LOCKED) || (rc == SQLITE_LOCKED_SHAREDCACHE))
423 {
424 // database locked by another thread, retry in 10 milliseconds
425 ThreadSleepMs(10);
426 goto retry;
427 }
428 else if (rc != SQLITE_OK)
5039dede 429 {
ec19ded8
VK
430 GetErrorMessage(hConn->pdb, errorText);
431 DrvFreeResult(result);
432 result = NULL;
5039dede
AK
433 }
434 MutexUnlock(hConn->mutexQueryLock);
ec19ded8
VK
435
436 free(pszQueryUTF8);
437 *pdwError = (result != NULL) ? DBERR_SUCCESS : DBERR_OTHER_ERROR;
438 return result;
439}
440
e8fd4c59
VK
441/**
442 * Perform SELECT query using prepared statement
443 */
ec19ded8
VK
444extern "C" DBDRV_RESULT EXPORT DrvSelectPrepared(SQLITE_CONN *hConn, sqlite3_stmt *stmt, DWORD *pdwError, WCHAR *errorText)
445{
446 SQLITE_RESULT *result = (SQLITE_RESULT *)malloc(sizeof(SQLITE_RESULT));
447 memset(result, 0, sizeof(SQLITE_RESULT));
448
c17f6cbc 449 MutexLock(hConn->mutexQueryLock);
114bd5c6
VK
450
451 int nCols = sqlite3_column_count(stmt);
452 char **cnames = (char **)malloc(sizeof(char *) * nCols * 2); // column names + values
453 char **values = &cnames[nCols];
454 bool firstRow = true;
455 while(1)
ec19ded8 456 {
114bd5c6 457 int rc = sqlite3_step(stmt);
66c11d49
VK
458
459 if (firstRow && ((rc == SQLITE_LOCKED) || (rc == SQLITE_LOCKED_SHAREDCACHE)))
460 {
461 // database locked by another thread, retry in 10 milliseconds
462 ThreadSleepMs(10);
463 sqlite3_reset(stmt);
464 continue;
465 }
466
467 if (((rc == SQLITE_DONE) || (rc == SQLITE_ROW)) && firstRow)
ec19ded8 468 {
114bd5c6
VK
469 firstRow = false;
470 for(int i = 0; i < nCols; i++)
471 cnames[i] = (char *)sqlite3_column_name(stmt, i);
472 }
ec19ded8 473
114bd5c6
VK
474 if (rc == SQLITE_ROW)
475 {
476 for(int i = 0; i < nCols; i++)
477 values[i] = (char *)sqlite3_column_text(stmt, i);
478 SelectCallback(result, nCols, values, cnames);
479 }
480 else if (rc == SQLITE_DONE)
481 {
482 *pdwError = DBERR_SUCCESS;
483 break;
484 }
485 else
486 {
487 GetErrorMessage(hConn->pdb, errorText);
488 *pdwError = DBERR_OTHER_ERROR;
489 break;
ec19ded8 490 }
ec19ded8 491 }
114bd5c6
VK
492 safe_free(cnames);
493
494 if (*pdwError == DBERR_SUCCESS)
495 {
496 if (sqlite3_reset(stmt) != SQLITE_OK)
497 {
498 GetErrorMessage(hConn->pdb, errorText);
499 *pdwError = DBERR_OTHER_ERROR;
500 }
501 }
502 else
503 {
504 sqlite3_reset(stmt);
505 }
506
507 MutexUnlock(hConn->mutexQueryLock);
ec19ded8
VK
508
509 if (*pdwError != DBERR_SUCCESS)
510 {
511 DrvFreeResult(result);
512 result = NULL;
513 }
514
515 return result;
5039dede
AK
516}
517
e8fd4c59
VK
518/**
519 * Get field length from result
520 */
b8c1ec69 521extern "C" LONG EXPORT DrvGetFieldLength(DBDRV_RESULT hResult, int iRow, int iColumn)
5039dede
AK
522{
523 if ((iRow < ((SQLITE_RESULT *)hResult)->nRows) &&
524 (iColumn < ((SQLITE_RESULT *)hResult)->nCols) &&
525 (iRow >= 0) && (iColumn >= 0))
4fe6a169 526 return (LONG)strlen(((SQLITE_RESULT *)hResult)->ppszData[iRow * ((SQLITE_RESULT *)hResult)->nCols + iColumn]);
5039dede
AK
527 return -1;
528}
529
e8fd4c59
VK
530/**
531 * Get field value from result
532 */
43526096 533extern "C" WCHAR EXPORT *DrvGetField(DBDRV_RESULT hResult, int iRow, int iColumn, WCHAR *pwszBuffer, int nBufLen)
5039dede
AK
534{
535 if ((iRow < ((SQLITE_RESULT *)hResult)->nRows) &&
536 (iColumn < ((SQLITE_RESULT *)hResult)->nCols) &&
537 (iRow >= 0) && (iColumn >= 0))
538 {
539 MultiByteToWideChar(CP_UTF8, 0, ((SQLITE_RESULT *)hResult)->ppszData[iRow * ((SQLITE_RESULT *)hResult)->nCols + iColumn],
540 -1, pwszBuffer, nBufLen);
541 pwszBuffer[nBufLen - 1] = 0;
542 return pwszBuffer;
543 }
544 return NULL;
545}
546
43526096
VK
547/**
548 * Get field value from result as UTF8 string
549 */
550extern "C" char EXPORT *DrvGetFieldUTF8(DBDRV_RESULT hResult, int iRow, int iColumn, char *buffer, int nBufLen)
551{
552 if ((iRow < ((SQLITE_RESULT *)hResult)->nRows) &&
553 (iColumn < ((SQLITE_RESULT *)hResult)->nCols) &&
554 (iRow >= 0) && (iColumn >= 0))
555 {
556 strncpy(buffer, ((SQLITE_RESULT *)hResult)->ppszData[iRow * ((SQLITE_RESULT *)hResult)->nCols + iColumn], nBufLen);
557 buffer[nBufLen - 1] = 0;
558 return buffer;
559 }
560 return NULL;
561}
562
e8fd4c59
VK
563/**
564 * Get number of rows in result
565 */
ec19ded8 566extern "C" int EXPORT DrvGetNumRows(SQLITE_RESULT *hResult)
5039dede 567{
ec19ded8 568 return hResult->nRows;
5039dede
AK
569}
570
e8fd4c59
VK
571/**
572 * Get column count in query result
573 */
ec19ded8 574extern "C" int EXPORT DrvGetColumnCount(SQLITE_RESULT *hResult)
ea6f474a 575{
ec19ded8 576 return (hResult != NULL) ? hResult->nCols : 0;
ea6f474a
VK
577}
578
e8fd4c59
VK
579/**
580 * Get column name in query result
581 */
ec19ded8 582extern "C" const char EXPORT *DrvGetColumnName(SQLITE_RESULT *hResult, int column)
ea6f474a 583{
00d17c5a 584 char *pszRet = NULL;
ea6f474a 585
ec19ded8 586 if ((column >= 0) && (column < hResult->nCols))
ea6f474a 587 {
ec19ded8 588 pszRet = hResult->ppszNames[column];
ea6f474a 589 }
00d17c5a 590 return pszRet;
ea6f474a
VK
591}
592
e8fd4c59 593/**
f17cf019 594 * Perform unbuffered SELECT query
e8fd4c59 595 */
f17cf019 596extern "C" DBDRV_UNBUFFERED_RESULT EXPORT DrvSelectUnbuffered(SQLITE_CONN *hConn, WCHAR *pwszQuery, DWORD *pdwError, WCHAR *errorText)
5039dede 597{
f889ad36 598 SQLITE_UNBUFFERED_RESULT *result;
5039dede 599 char *pszQueryUTF8;
f889ad36 600 sqlite3_stmt *stmt;
5039dede
AK
601
602 pszQueryUTF8 = UTF8StringFromWideString(pwszQuery);
c17f6cbc 603 MutexLock(hConn->mutexQueryLock);
66c11d49 604retry:
f889ad36 605 int rc = sqlite3_prepare(hConn->pdb, pszQueryUTF8, -1, &stmt, NULL);
66c11d49 606 if (rc == SQLITE_OK)
5039dede 607 {
f889ad36
VK
608 result = (SQLITE_UNBUFFERED_RESULT *)malloc(sizeof(SQLITE_UNBUFFERED_RESULT));
609 result->connection = hConn;
610 result->stmt = stmt;
611 result->prepared = false;
ec19ded8 612 *pdwError = DBERR_SUCCESS;
5039dede 613 }
66c11d49
VK
614 else if ((rc == SQLITE_LOCKED) || (rc == SQLITE_LOCKED_SHAREDCACHE))
615 {
616 // database locked by another thread, retry in 10 milliseconds
617 ThreadSleepMs(10);
618 goto retry;
619 }
5039dede
AK
620 else
621 {
ec19ded8 622 GetErrorMessage(hConn->pdb, errorText);
5039dede 623 MutexUnlock(hConn->mutexQueryLock);
f889ad36 624 result = NULL;
ec19ded8 625 *pdwError = DBERR_OTHER_ERROR;
5039dede
AK
626 }
627 free(pszQueryUTF8);
f889ad36
VK
628 return result;
629}
630
631/**
632 * Perform unbuffered SELECT query using prepared statement
633 */
634extern "C" DBDRV_UNBUFFERED_RESULT EXPORT DrvSelectPreparedUnbuffered(SQLITE_CONN *hConn, sqlite3_stmt *stmt, DWORD *pdwError, WCHAR *errorText)
635{
636 if ((hConn == NULL) || (stmt == NULL))
637 return NULL;
638
639 SQLITE_UNBUFFERED_RESULT *result = (SQLITE_UNBUFFERED_RESULT *)malloc(sizeof(SQLITE_UNBUFFERED_RESULT));
640 result->connection = hConn;
641 result->stmt = stmt;
642 result->prepared = true;
643 *pdwError = DBERR_SUCCESS;
644 return result;
5039dede
AK
645}
646
e8fd4c59
VK
647/**
648 * Fetch next result line from asynchronous SELECT results
649 */
f889ad36 650extern "C" bool EXPORT DrvFetch(SQLITE_UNBUFFERED_RESULT *result)
5039dede 651{
f889ad36 652 if (result == NULL)
750d59f2 653 return false;
ec19ded8 654
66c11d49 655retry:
f889ad36 656 int rc = sqlite3_step(result->stmt);
66c11d49 657 if (rc == SQLITE_ROW)
ec19ded8 658 {
f889ad36 659 result->numColumns = sqlite3_column_count(result->stmt);
750d59f2 660 return true;
ec19ded8 661 }
66c11d49
VK
662 else if ((rc == SQLITE_LOCKED) || (rc == SQLITE_LOCKED_SHAREDCACHE))
663 {
664 // database locked by another thread, retry in 10 milliseconds
665 ThreadSleepMs(10);
f889ad36 666 sqlite3_reset(result->stmt);
66c11d49
VK
667 goto retry;
668 }
750d59f2 669 return false;
5039dede
AK
670}
671
e8fd4c59 672/**
f17cf019 673 * Get field length from unbuffered query result
e8fd4c59 674 */
f889ad36 675extern "C" LONG EXPORT DrvGetFieldLengthUnbuffered(SQLITE_UNBUFFERED_RESULT *result, int iColumn)
61f032f5 676{
f889ad36
VK
677 if ((iColumn >= 0) && (iColumn < result->numColumns))
678 return (LONG)strlen((char *)sqlite3_column_text(result->stmt, iColumn));
61f032f5
VK
679 return 0;
680}
681
e8fd4c59 682/**
f17cf019 683 * Get field from current row in unbuffered query result
e8fd4c59 684 */
f889ad36 685extern "C" WCHAR EXPORT *DrvGetFieldUnbuffered(SQLITE_UNBUFFERED_RESULT *result, int iColumn, WCHAR *pBuffer, int iBufSize)
5039dede
AK
686{
687 char *pszData;
688 WCHAR *pwszRet = NULL;
689
f889ad36 690 if ((iColumn >= 0) && (iColumn < result->numColumns))
00ab020f 691 {
f889ad36 692 pszData = (char *)sqlite3_column_text(result->stmt, iColumn);
00ab020f
VK
693 if (pszData != NULL)
694 {
695 MultiByteToWideChar(CP_UTF8, 0, pszData, -1, pBuffer, iBufSize);
696 pBuffer[iBufSize - 1] = 0;
697 pwszRet = pBuffer;
698 }
699 }
700 return pwszRet;
701}
702
7f8e3ccf
VK
703/**
704 * Get field from current row in unbuffered query result as UTF-8 string
705 */
706extern "C" char EXPORT *DrvGetFieldUnbufferedUTF8(SQLITE_UNBUFFERED_RESULT *result, int iColumn, char *pBuffer, int iBufSize)
707{
708 char *pszData;
709 char *value = NULL;
710
711 if ((iColumn >= 0) && (iColumn < result->numColumns))
712 {
713 pszData = (char *)sqlite3_column_text(result->stmt, iColumn);
714 if (pszData != NULL)
715 {
716 strncpy(pBuffer, pszData, iBufSize);
717 pBuffer[iBufSize - 1] = 0;
718 value = pBuffer;
719 }
720 }
721 return value;
722}
723
e8fd4c59
VK
724/**
725 * Get column count in async query result
726 */
f889ad36 727extern "C" int EXPORT DrvGetColumnCountUnbuffered(SQLITE_UNBUFFERED_RESULT *result)
00ab020f 728{
f889ad36 729 return (result != NULL) ? result->numColumns : 0;
00ab020f
VK
730}
731
e8fd4c59
VK
732/**
733 * Get column name in async query result
734 */
f889ad36 735extern "C" const char EXPORT *DrvGetColumnNameUnbuffered(SQLITE_UNBUFFERED_RESULT *result, int column)
00ab020f 736{
00d17c5a 737 const char *pszRet = NULL;
00ab020f 738
f889ad36 739 if ((column >= 0) && (column < result->numColumns))
5039dede 740 {
f889ad36 741 pszRet = sqlite3_column_name(result->stmt, column);
5039dede 742 }
00d17c5a 743 return pszRet;
5039dede
AK
744}
745
e8fd4c59
VK
746/**
747 * Destroy result of async query
748 */
f889ad36 749extern "C" void EXPORT DrvFreeUnbufferedResult(SQLITE_UNBUFFERED_RESULT *result)
5039dede 750{
f889ad36 751 if (result != NULL)
5039dede 752 {
f889ad36
VK
753 if (result->prepared)
754 sqlite3_reset(result->stmt);
755 else
756 sqlite3_finalize(result->stmt);
757 MutexUnlock(result->connection->mutexQueryLock);
758 free(result);
5039dede
AK
759 }
760}
761
e8fd4c59
VK
762/**
763 * Begin transaction
764 */
5039dede
AK
765extern "C" DWORD EXPORT DrvBegin(SQLITE_CONN *pConn)
766{
66c11d49 767 return DrvQueryInternal(pConn, "BEGIN IMMEDIATE", NULL);
5039dede
AK
768}
769
e8fd4c59
VK
770/**
771 * Commit transaction
772 */
5039dede
AK
773extern "C" DWORD EXPORT DrvCommit(SQLITE_CONN *pConn)
774{
775 return DrvQueryInternal(pConn, "COMMIT", NULL);
776}
777
e8fd4c59
VK
778/**
779 * Rollback transaction
780 */
5039dede
AK
781extern "C" DWORD EXPORT DrvRollback(SQLITE_CONN *pConn)
782{
783 return DrvQueryInternal(pConn, "ROLLBACK", NULL);
784}
785
daf3c104
VK
786/**
787 * Check if table exist
788 */
789extern "C" int EXPORT DrvIsTableExist(SQLITE_CONN *pConn, const WCHAR *name)
790{
791 WCHAR query[256];
fdf1ed50 792 swprintf(query, 256, L"SELECT count(*) FROM sqlite_master WHERE type='table' AND upper(name)=upper('%ls')", name);
daf3c104
VK
793 DWORD error;
794 WCHAR errorText[DBDRV_MAX_ERROR_TEXT];
795 int rc = DBIsTableExist_Failure;
796 SQLITE_RESULT *hResult = (SQLITE_RESULT *)DrvSelect(pConn, query, &error, errorText);
797 if (hResult != NULL)
798 {
799 WCHAR buffer[64] = L"";
800 DrvGetField(hResult, 0, 0, buffer, 64);
801 rc = (wcstol(buffer, NULL, 10) > 0) ? DBIsTableExist_Found : DBIsTableExist_NotFound;
802 DrvFreeResult(hResult);
803 }
804 return rc;
805}
806
5039dede
AK
807#ifdef _WIN32
808
e8fd4c59
VK
809/**
810 * DLL Entry point
811 */
750d59f2 812bool WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
5039dede
AK
813{
814 if (dwReason == DLL_PROCESS_ATTACH)
815 DisableThreadLibraryCalls(hInstance);
750d59f2 816 return true;
5039dede
AK
817}
818
819#endif