Commit | Line | Data |
---|---|---|
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 | 25 | DECLARE_DRIVER_HEADER("SQLITE") |
5039dede | 26 | |
e8fd4c59 VK |
27 | /** |
28 | * Get error message from connection | |
29 | */ | |
ec19ded8 | 30 | static 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 | 47 | extern "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 |
83 | extern "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 | 119 | extern "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 | 132 | extern "C" void EXPORT DrvUnload() |
5039dede | 133 | { |
2a8e6a7b | 134 | sqlite3_shutdown(); |
5039dede AK |
135 | } |
136 | ||
e8fd4c59 VK |
137 | /** |
138 | * Connect to database | |
139 | */ | |
ec19ded8 | 140 | extern "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 |
170 | extern "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 | 183 | extern "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 | |
189 | retry: | |
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 | 211 | extern "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 |
259 | extern "C" DWORD EXPORT DrvExecute(SQLITE_CONN *hConn, sqlite3_stmt *stmt, WCHAR *errorText) |
260 | { | |
261 | DWORD result; | |
5039dede | 262 | |
c17f6cbc | 263 | MutexLock(hConn->mutexQueryLock); |
66c11d49 | 264 | retry: |
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 |
299 | extern "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 | 308 | static 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 |
313 | retry: |
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 | 337 | extern "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 |
351 | static 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 |
388 | extern "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 | 412 | extern "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 |
420 | retry: |
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 |
444 | extern "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 | 521 | extern "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 | 533 | extern "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 | */ | |
550 | extern "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 | 566 | extern "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 | 574 | extern "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 | 582 | extern "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 | 596 | extern "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 | 604 | retry: |
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 | */ | |
634 | extern "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 | 650 | extern "C" bool EXPORT DrvFetch(SQLITE_UNBUFFERED_RESULT *result) |
5039dede | 651 | { |
f889ad36 | 652 | if (result == NULL) |
750d59f2 | 653 | return false; |
ec19ded8 | 654 | |
66c11d49 | 655 | retry: |
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 | 675 | extern "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 | 685 | extern "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 | */ | |
706 | extern "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 | 727 | extern "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 | 735 | extern "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 | 749 | extern "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 |
765 | extern "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 |
773 | extern "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 |
781 | extern "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 | */ | |
789 | extern "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 | 812 | bool 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 |