fixed crash in DBFreeStatement after session reconnect
[public/netxms.git] / src / db / libnxdb / session.cpp
1 /*
2 ** NetXMS - Network Management System
3 ** Database Abstraction Library
4 ** Copyright (C) 2003-2012 Victor Kirhenshtein
5 **
6 ** This program is free software; you can redistribute it and/or modify
7 ** it under the terms of the GNU Lesser General Public License as published by
8 ** the Free Software Foundation; either version 3 of the License, or
9 ** (at your option) any later version.
10 **
11 ** This program is distributed in the hope that it will be useful,
12 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
13 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 ** GNU General Public License for more details.
15 **
16 ** You should have received a copy of the GNU Lesser General Public License
17 ** along with this program; if not, write to the Free Software
18 ** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 **
20 ** File: session.cpp
21 **
22 **/
23
24 #include "libnxdb.h"
25
26 /**
27 * Check if statement handle is valid
28 */
29 #define IS_VALID_STATEMENT_HANDLE(s) ((s != NULL) && (s->m_connection != NULL))
30
31 /**
32 * Invalidate all prepared statements on connection
33 */
34 static void InvalidatePreparedStatements(DB_HANDLE hConn)
35 {
36 for(int i = 0; i < hConn->m_preparedStatements->size(); i++)
37 {
38 db_statement_t *stmt = hConn->m_preparedStatements->get(i);
39 hConn->m_driver->m_fpDrvFreeStatement(stmt->m_statement);
40 stmt->m_statement = NULL;
41 stmt->m_connection = NULL;
42 }
43 }
44
45 /**
46 * Connect to database
47 */
48 DB_HANDLE LIBNXDB_EXPORTABLE DBConnect(DB_DRIVER driver, const TCHAR *server, const TCHAR *dbName,
49 const TCHAR *login, const TCHAR *password, const TCHAR *schema, TCHAR *errorText)
50 {
51 DBDRV_CONNECTION hDrvConn;
52 DB_HANDLE hConn = NULL;
53
54 __DBDbgPrintf(8, _T("DBConnect: server=%s db=%s login=%s schema=%s"), CHECK_NULL(server), CHECK_NULL(dbName), CHECK_NULL(login), CHECK_NULL(schema));
55 #ifdef UNICODE
56 char *mbServer = (server == NULL) ? NULL : MBStringFromWideString(server);
57 char *mbDatabase = (dbName == NULL) ? NULL : MBStringFromWideString(dbName);
58 char *mbLogin = (login == NULL) ? NULL : MBStringFromWideString(login);
59 char *mbPassword = (password == NULL) ? NULL : MBStringFromWideString(password);
60 char *mbSchema = (schema == NULL) ? NULL : MBStringFromWideString(schema);
61 errorText[0] = 0;
62 hDrvConn = driver->m_fpDrvConnect(mbServer, mbLogin, mbPassword, mbDatabase, mbSchema, errorText);
63 #else
64 WCHAR wcErrorText[DBDRV_MAX_ERROR_TEXT] = L"";
65 hDrvConn = driver->m_fpDrvConnect(server, login, password, dbName, schema, wcErrorText);
66 WideCharToMultiByte(CP_ACP, WC_DEFAULTCHAR | WC_COMPOSITECHECK, wcErrorText, -1, errorText, DBDRV_MAX_ERROR_TEXT, NULL, NULL);
67 errorText[DBDRV_MAX_ERROR_TEXT - 1] = 0;
68 #endif
69 if (hDrvConn != NULL)
70 {
71 hConn = (DB_HANDLE)malloc(sizeof(struct db_handle_t));
72 if (hConn != NULL)
73 {
74 hConn->m_driver = driver;
75 hConn->m_dumpSql = driver->m_dumpSql;
76 hConn->m_reconnectEnabled = true;
77 hConn->m_connection = hDrvConn;
78 hConn->m_mutexTransLock = MutexCreateRecursive();
79 hConn->m_transactionLevel = 0;
80 hConn->m_preparedStatements = new ObjectArray<db_statement_t>(4, 4, false);
81 #ifdef UNICODE
82 hConn->m_dbName = mbDatabase;
83 hConn->m_login = mbLogin;
84 hConn->m_password = mbPassword;
85 hConn->m_server = mbServer;
86 hConn->m_schema = mbSchema;
87 #else
88 hConn->m_dbName = (dbName == NULL) ? NULL : _tcsdup(dbName);
89 hConn->m_login = (login == NULL) ? NULL : _tcsdup(login);
90 hConn->m_password = (password == NULL) ? NULL : _tcsdup(password);
91 hConn->m_server = (server == NULL) ? NULL : _tcsdup(server);
92 hConn->m_schema = (schema == NULL) ? NULL : _tcsdup(schema);
93 #endif
94 __DBDbgPrintf(4, _T("New DB connection opened: handle=%p"), hConn);
95 }
96 else
97 {
98 driver->m_fpDrvDisconnect(hDrvConn);
99 }
100 }
101 #ifdef UNICODE
102 if (hConn == NULL)
103 {
104 safe_free(mbServer);
105 safe_free(mbDatabase);
106 safe_free(mbLogin);
107 safe_free(mbPassword);
108 safe_free(mbSchema);
109 }
110 #endif
111 return hConn;
112 }
113
114 /**
115 * Disconnect from database
116 */
117 void LIBNXDB_EXPORTABLE DBDisconnect(DB_HANDLE hConn)
118 {
119 if (hConn == NULL)
120 return;
121
122 __DBDbgPrintf(4, _T("DB connection %p closed"), hConn);
123
124 InvalidatePreparedStatements(hConn);
125
126 hConn->m_driver->m_fpDrvDisconnect(hConn->m_connection);
127 MutexDestroy(hConn->m_mutexTransLock);
128 safe_free(hConn->m_dbName);
129 safe_free(hConn->m_login);
130 safe_free(hConn->m_password);
131 safe_free(hConn->m_server);
132 safe_free(hConn->m_schema);
133 delete hConn->m_preparedStatements;
134 free(hConn);
135 }
136
137 /**
138 * Enable/disable reconnect
139 */
140 void LIBNXDB_EXPORTABLE DBEnableReconnect(DB_HANDLE hConn, bool enabled)
141 {
142 if (hConn != NULL)
143 {
144 hConn->m_reconnectEnabled = enabled;
145 }
146 }
147
148 /**
149 * Reconnect to database
150 */
151 static void DBReconnect(DB_HANDLE hConn)
152 {
153 int nCount;
154 WCHAR errorText[DBDRV_MAX_ERROR_TEXT];
155
156 __DBDbgPrintf(4, _T("DB reconnect: handle=%p"), hConn);
157
158 InvalidatePreparedStatements(hConn);
159 hConn->m_driver->m_fpDrvDisconnect(hConn->m_connection);
160 for(nCount = 0; ; nCount++)
161 {
162 hConn->m_connection = hConn->m_driver->m_fpDrvConnect(hConn->m_server, hConn->m_login,
163 hConn->m_password, hConn->m_dbName, hConn->m_schema, errorText);
164 if (hConn->m_connection != NULL)
165 break;
166 if (nCount == 0)
167 {
168 MutexLock(hConn->m_driver->m_mutexReconnect);
169 if ((hConn->m_driver->m_reconnect == 0) && (hConn->m_driver->m_fpEventHandler != NULL))
170 hConn->m_driver->m_fpEventHandler(DBEVENT_CONNECTION_LOST, NULL, NULL, hConn->m_driver->m_userArg);
171 hConn->m_driver->m_reconnect++;
172 MutexUnlock(hConn->m_driver->m_mutexReconnect);
173 }
174 ThreadSleepMs(1000);
175 }
176 if (nCount > 0)
177 {
178 MutexLock(hConn->m_driver->m_mutexReconnect);
179 hConn->m_driver->m_reconnect--;
180 if ((hConn->m_driver->m_reconnect == 0) && (hConn->m_driver->m_fpEventHandler != NULL))
181 hConn->m_driver->m_fpEventHandler(DBEVENT_CONNECTION_RESTORED, NULL, NULL, hConn->m_driver->m_userArg);
182 MutexUnlock(hConn->m_driver->m_mutexReconnect);
183 }
184 }
185
186 /**
187 * Perform a non-SELECT SQL query
188 */
189 BOOL LIBNXDB_EXPORTABLE DBQueryEx(DB_HANDLE hConn, const TCHAR *szQuery, TCHAR *errorText)
190 {
191 DWORD dwResult;
192 INT64 ms;
193 #ifdef UNICODE
194 #define pwszQuery szQuery
195 #define wcErrorText errorText
196 #else
197 WCHAR *pwszQuery = WideStringFromMBString(szQuery);
198 WCHAR wcErrorText[DBDRV_MAX_ERROR_TEXT] = L"";
199 #endif
200
201 MutexLock(hConn->m_mutexTransLock);
202 if (hConn->m_driver->m_dumpSql)
203 ms = GetCurrentTimeMs();
204
205 dwResult = hConn->m_driver->m_fpDrvQuery(hConn->m_connection, pwszQuery, wcErrorText);
206 if ((dwResult == DBERR_CONNECTION_LOST) && hConn->m_reconnectEnabled)
207 {
208 DBReconnect(hConn);
209 dwResult = hConn->m_driver->m_fpDrvQuery(hConn->m_connection, pwszQuery, wcErrorText);
210 }
211
212 if (hConn->m_driver->m_dumpSql)
213 {
214 ms = GetCurrentTimeMs() - ms;
215 __DBDbgPrintf(9, _T("%s sync query: \"%s\" [%d ms]"), (dwResult == DBERR_SUCCESS) ? _T("Successful") : _T("Failed"), szQuery, ms);
216 }
217
218 MutexUnlock(hConn->m_mutexTransLock);
219
220 #ifndef UNICODE
221 WideCharToMultiByte(CP_ACP, WC_COMPOSITECHECK | WC_DEFAULTCHAR, wcErrorText, -1, errorText, DBDRV_MAX_ERROR_TEXT, NULL, NULL);
222 errorText[DBDRV_MAX_ERROR_TEXT - 1] = 0;
223 #endif
224
225 if (dwResult != DBERR_SUCCESS)
226 {
227 if (hConn->m_driver->m_logSqlErrors)
228 nxlog_write(g_sqlErrorMsgCode, EVENTLOG_ERROR_TYPE, "ss", szQuery, errorText);
229 if (hConn->m_driver->m_fpEventHandler != NULL)
230 hConn->m_driver->m_fpEventHandler(DBEVENT_QUERY_FAILED, pwszQuery, wcErrorText, hConn->m_driver->m_userArg);
231 }
232
233 #ifndef UNICODE
234 free(pwszQuery);
235 #endif
236
237 return dwResult == DBERR_SUCCESS;
238 #undef pwszQuery
239 #undef wcErrorText
240 }
241
242 BOOL LIBNXDB_EXPORTABLE DBQuery(DB_HANDLE hConn, const TCHAR *query)
243 {
244 TCHAR errorText[DBDRV_MAX_ERROR_TEXT];
245
246 return DBQueryEx(hConn, query, errorText);
247 }
248
249 /**
250 * Perform SELECT query
251 */
252 DB_RESULT LIBNXDB_EXPORTABLE DBSelectEx(DB_HANDLE hConn, const TCHAR *szQuery, TCHAR *errorText)
253 {
254 DBDRV_RESULT hResult;
255 DB_RESULT result = NULL;
256 DWORD dwError;
257 INT64 ms;
258 #ifdef UNICODE
259 #define pwszQuery szQuery
260 #define wcErrorText errorText
261 #else
262 WCHAR *pwszQuery = WideStringFromMBString(szQuery);
263 WCHAR wcErrorText[DBDRV_MAX_ERROR_TEXT] = L"";
264 #endif
265
266 MutexLock(hConn->m_mutexTransLock);
267 if (hConn->m_driver->m_dumpSql)
268 ms = GetCurrentTimeMs();
269 hResult = hConn->m_driver->m_fpDrvSelect(hConn->m_connection, pwszQuery, &dwError, wcErrorText);
270 if ((hResult == NULL) && (dwError == DBERR_CONNECTION_LOST) && hConn->m_reconnectEnabled)
271 {
272 DBReconnect(hConn);
273 hResult = hConn->m_driver->m_fpDrvSelect(hConn->m_connection, pwszQuery, &dwError, wcErrorText);
274 }
275
276 if (hConn->m_driver->m_dumpSql)
277 {
278 ms = GetCurrentTimeMs() - ms;
279 __DBDbgPrintf(9, _T("%s sync query: \"%s\" [%d ms]"), (hResult != NULL) ? _T("Successful") : _T("Failed"), szQuery, (DWORD)ms);
280 }
281 MutexUnlock(hConn->m_mutexTransLock);
282
283 #ifndef UNICODE
284 WideCharToMultiByte(CP_ACP, WC_COMPOSITECHECK | WC_DEFAULTCHAR, wcErrorText, -1, errorText, DBDRV_MAX_ERROR_TEXT, NULL, NULL);
285 errorText[DBDRV_MAX_ERROR_TEXT - 1] = 0;
286 #endif
287
288 if (hResult == NULL)
289 {
290 if (hConn->m_driver->m_logSqlErrors)
291 nxlog_write(g_sqlErrorMsgCode, EVENTLOG_ERROR_TYPE, "ss", szQuery, errorText);
292 if (hConn->m_driver->m_fpEventHandler != NULL)
293 hConn->m_driver->m_fpEventHandler(DBEVENT_QUERY_FAILED, pwszQuery, wcErrorText, hConn->m_driver->m_userArg);
294 }
295
296 #ifndef UNICODE
297 free(pwszQuery);
298 #endif
299
300 if (hResult != NULL)
301 {
302 result = (DB_RESULT)malloc(sizeof(db_result_t));
303 result->m_driver = hConn->m_driver;
304 result->m_connection = hConn;
305 result->m_data = hResult;
306 }
307
308 return result;
309 #undef pwszQuery
310 #undef wcErrorText
311 }
312
313 DB_RESULT LIBNXDB_EXPORTABLE DBSelect(DB_HANDLE hConn, const TCHAR *query)
314 {
315 TCHAR errorText[DBDRV_MAX_ERROR_TEXT];
316
317 return DBSelectEx(hConn, query, errorText);
318 }
319
320 /**
321 * Get number of columns
322 */
323 int LIBNXDB_EXPORTABLE DBGetColumnCount(DB_RESULT hResult)
324 {
325 return hResult->m_driver->m_fpDrvGetColumnCount(hResult->m_data);
326 }
327
328 /**
329 * Get column name
330 */
331 BOOL LIBNXDB_EXPORTABLE DBGetColumnName(DB_RESULT hResult, int column, TCHAR *buffer, int bufSize)
332 {
333 const char *name;
334
335 name = hResult->m_driver->m_fpDrvGetColumnName(hResult->m_data, column);
336 if (name != NULL)
337 {
338 #ifdef UNICODE
339 MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, name, -1, buffer, bufSize);
340 buffer[bufSize - 1] = 0;
341 #else
342 nx_strncpy(buffer, name, bufSize);
343 #endif
344 }
345 return name != NULL;
346 }
347
348 /**
349 * Get column count for async request
350 */
351 int LIBNXDB_EXPORTABLE DBGetColumnCountAsync(DB_ASYNC_RESULT hResult)
352 {
353 return hResult->m_driver->m_fpDrvGetColumnCountAsync(hResult->m_data);
354 }
355
356 /**
357 * Get column name for async request
358 */
359 BOOL LIBNXDB_EXPORTABLE DBGetColumnNameAsync(DB_ASYNC_RESULT hResult, int column, TCHAR *buffer, int bufSize)
360 {
361 const char *name;
362
363 name = hResult->m_driver->m_fpDrvGetColumnNameAsync(hResult->m_data, column);
364 if (name != NULL)
365 {
366 #ifdef UNICODE
367 MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, name, -1, buffer, bufSize);
368 buffer[bufSize - 1] = 0;
369 #else
370 nx_strncpy(buffer, name, bufSize);
371 #endif
372 }
373 return name != NULL;
374 }
375
376 /**
377 * Get field's value. If buffer is NULL, dynamically allocated string will be returned.
378 * Caller is responsible for destroying it by calling free().
379 */
380 TCHAR LIBNXDB_EXPORTABLE *DBGetField(DB_RESULT hResult, int iRow, int iColumn, TCHAR *pszBuffer, int nBufLen)
381 {
382 #ifdef UNICODE
383 if (pszBuffer != NULL)
384 {
385 return hResult->m_driver->m_fpDrvGetField(hResult->m_data, iRow, iColumn, pszBuffer, nBufLen);
386 }
387 else
388 {
389 LONG nLen;
390 WCHAR *pszTemp;
391
392 nLen = hResult->m_driver->m_fpDrvGetFieldLength(hResult->m_data, iRow, iColumn);
393 if (nLen == -1)
394 {
395 pszTemp = NULL;
396 }
397 else
398 {
399 nLen++;
400 pszTemp = (WCHAR *)malloc(nLen * sizeof(WCHAR));
401 hResult->m_driver->m_fpDrvGetField(hResult->m_data, iRow, iColumn, pszTemp, nLen);
402 }
403 return pszTemp;
404 }
405 #else
406 return DBGetFieldA(hResult, iRow, iColumn, pszBuffer, nBufLen);
407 #endif
408 }
409
410 char LIBNXDB_EXPORTABLE *DBGetFieldA(DB_RESULT hResult, int iRow, int iColumn, char *pszBuffer, int nBufLen)
411 {
412 WCHAR *pwszData, *pwszBuffer;
413 char *pszRet;
414 int nLen;
415
416 if (pszBuffer != NULL)
417 {
418 pwszBuffer = (WCHAR *)malloc(nBufLen * sizeof(WCHAR));
419 pwszData = hResult->m_driver->m_fpDrvGetField(hResult->m_data, iRow, iColumn, pwszBuffer, nBufLen);
420 if (pwszData != NULL)
421 {
422 WideCharToMultiByte(CP_ACP, WC_COMPOSITECHECK | WC_DEFAULTCHAR,
423 pwszData, -1, pszBuffer, nBufLen, NULL, NULL);
424 pszRet = pszBuffer;
425 }
426 else
427 {
428 pszRet = NULL;
429 }
430 free(pwszBuffer);
431 }
432 else
433 {
434 nLen = hResult->m_driver->m_fpDrvGetFieldLength(hResult->m_data, iRow, iColumn);
435 if (nLen == -1)
436 {
437 pszRet = NULL;
438 }
439 else
440 {
441 nLen++;
442 pwszBuffer = (WCHAR *)malloc(nLen * sizeof(WCHAR));
443 pwszData = hResult->m_driver->m_fpDrvGetField(hResult->m_data, iRow, iColumn, pwszBuffer, nLen);
444 if (pwszData != NULL)
445 {
446 nLen = (int)wcslen(pwszData) + 1;
447 pszRet = (char *)malloc(nLen);
448 WideCharToMultiByte(CP_ACP, WC_COMPOSITECHECK | WC_DEFAULTCHAR, pwszData, -1, pszRet, nLen, NULL, NULL);
449 }
450 else
451 {
452 pszRet = NULL;
453 }
454 free(pwszBuffer);
455 }
456 }
457 return pszRet;
458 }
459
460 /**
461 * Get field's value as unsigned long
462 */
463 UINT32 LIBNXDB_EXPORTABLE DBGetFieldULong(DB_RESULT hResult, int iRow, int iColumn)
464 {
465 INT32 iVal;
466 UINT32 dwVal;
467 TCHAR *pszVal, szBuffer[256];
468
469 pszVal = DBGetField(hResult, iRow, iColumn, szBuffer, 256);
470 if (pszVal == NULL)
471 return 0;
472 StrStrip(pszVal);
473 if (*pszVal == _T('-'))
474 {
475 iVal = _tcstol(pszVal, NULL, 10);
476 memcpy(&dwVal, &iVal, sizeof(INT32)); // To prevent possible conversion
477 }
478 else
479 {
480 dwVal = _tcstoul(pszVal, NULL, 10);
481 }
482 return dwVal;
483 }
484
485
486 //
487 // Get field's value as unsigned 64-bit int
488 //
489
490 UINT64 LIBNXDB_EXPORTABLE DBGetFieldUInt64(DB_RESULT hResult, int iRow, int iColumn)
491 {
492 INT64 iVal;
493 UINT64 qwVal;
494 TCHAR *pszVal, szBuffer[256];
495
496 pszVal = DBGetField(hResult, iRow, iColumn, szBuffer, 256);
497 if (pszVal == NULL)
498 return 0;
499 StrStrip(pszVal);
500 if (*pszVal == _T('-'))
501 {
502 iVal = _tcstoll(pszVal, NULL, 10);
503 memcpy(&qwVal, &iVal, sizeof(INT64)); // To prevent possible conversion
504 }
505 else
506 {
507 qwVal = _tcstoull(pszVal, NULL, 10);
508 }
509 return qwVal;
510 }
511
512
513 //
514 // Get field's value as signed long
515 //
516
517 INT32 LIBNXDB_EXPORTABLE DBGetFieldLong(DB_RESULT hResult, int iRow, int iColumn)
518 {
519 TCHAR *pszVal, szBuffer[256];
520
521 pszVal = DBGetField(hResult, iRow, iColumn, szBuffer, 256);
522 return pszVal == NULL ? 0 : _tcstol(pszVal, NULL, 10);
523 }
524
525
526 //
527 // Get field's value as signed 64-bit int
528 //
529
530 INT64 LIBNXDB_EXPORTABLE DBGetFieldInt64(DB_RESULT hResult, int iRow, int iColumn)
531 {
532 TCHAR *pszVal, szBuffer[256];
533
534 pszVal = DBGetField(hResult, iRow, iColumn, szBuffer, 256);
535 return pszVal == NULL ? 0 : _tcstoll(pszVal, NULL, 10);
536 }
537
538
539 //
540 // Get field's value as double
541 //
542
543 double LIBNXDB_EXPORTABLE DBGetFieldDouble(DB_RESULT hResult, int iRow, int iColumn)
544 {
545 TCHAR *pszVal, szBuffer[256];
546
547 pszVal = DBGetField(hResult, iRow, iColumn, szBuffer, 256);
548 return pszVal == NULL ? 0 : _tcstod(pszVal, NULL);
549 }
550
551
552 //
553 // Get field's value as IP address
554 //
555
556 UINT32 LIBNXDB_EXPORTABLE DBGetFieldIPAddr(DB_RESULT hResult, int iRow, int iColumn)
557 {
558 TCHAR *pszVal, szBuffer[256];
559
560 pszVal = DBGetField(hResult, iRow, iColumn, szBuffer, 256);
561 return pszVal == NULL ? INADDR_NONE : ntohl(_t_inet_addr(pszVal));
562 }
563
564
565 //
566 // Get field's value as integer array from byte array encoded in hex
567 //
568
569 BOOL LIBNXDB_EXPORTABLE DBGetFieldByteArray(DB_RESULT hResult, int iRow, int iColumn,
570 int *pnArray, int nSize, int nDefault)
571 {
572 char pbBytes[128];
573 BOOL bResult;
574 int i, nLen;
575 TCHAR *pszVal, szBuffer[256];
576
577 pszVal = DBGetField(hResult, iRow, iColumn, szBuffer, 256);
578 if (pszVal != NULL)
579 {
580 StrToBin(pszVal, (BYTE *)pbBytes, 128);
581 nLen = (int)_tcslen(pszVal) / 2;
582 for(i = 0; (i < nSize) && (i < nLen); i++)
583 pnArray[i] = pbBytes[i];
584 for(; i < nSize; i++)
585 pnArray[i] = nDefault;
586 bResult = TRUE;
587 }
588 else
589 {
590 for(i = 0; i < nSize; i++)
591 pnArray[i] = nDefault;
592 bResult = FALSE;
593 }
594 return bResult;
595 }
596
597 BOOL LIBNXDB_EXPORTABLE DBGetFieldByteArray2(DB_RESULT hResult, int iRow, int iColumn,
598 BYTE *data, int nSize, int nDefault)
599 {
600 BOOL bResult;
601 TCHAR *pszVal, szBuffer[256];
602
603 pszVal = DBGetField(hResult, iRow, iColumn, szBuffer, 256);
604 if (pszVal != NULL)
605 {
606 int bytes = (int)StrToBin(pszVal, data, nSize);
607 if (bytes < nSize)
608 memset(&data[bytes], 0, nSize - bytes);
609 bResult = TRUE;
610 }
611 else
612 {
613 memset(data, nDefault, nSize);
614 bResult = FALSE;
615 }
616 return bResult;
617 }
618
619
620 //
621 // Get field's value as GUID
622 //
623
624 BOOL LIBNXDB_EXPORTABLE DBGetFieldGUID(DB_RESULT hResult, int iRow,
625 int iColumn, uuid_t guid)
626 {
627 TCHAR *pszVal, szBuffer[256];
628 BOOL bResult;
629
630 pszVal = DBGetField(hResult, iRow, iColumn, szBuffer, 256);
631 if (pszVal != NULL)
632 {
633 if (uuid_parse(pszVal, guid) == 0)
634 {
635 bResult = TRUE;
636 }
637 else
638 {
639 uuid_clear(guid);
640 bResult = FALSE;
641 }
642 }
643 else
644 {
645 uuid_clear(guid);
646 bResult = FALSE;
647 }
648 return bResult;
649 }
650
651
652 //
653 // Get number of rows in result
654 //
655
656 int LIBNXDB_EXPORTABLE DBGetNumRows(DB_RESULT hResult)
657 {
658 if (hResult == NULL)
659 return 0;
660 return hResult->m_driver->m_fpDrvGetNumRows(hResult->m_data);
661 }
662
663
664 //
665 // Free result
666 //
667
668 void LIBNXDB_EXPORTABLE DBFreeResult(DB_RESULT hResult)
669 {
670 if (hResult != NULL)
671 {
672 hResult->m_driver->m_fpDrvFreeResult(hResult->m_data);
673 free(hResult);
674 }
675 }
676
677
678 //
679 // Asyncronous SELECT query
680 //
681
682 DB_ASYNC_RESULT LIBNXDB_EXPORTABLE DBAsyncSelectEx(DB_HANDLE hConn, const TCHAR *szQuery, TCHAR *errorText)
683 {
684 DBDRV_ASYNC_RESULT hResult;
685 DB_ASYNC_RESULT result = NULL;
686 DWORD dwError;
687 INT64 ms;
688 #ifdef UNICODE
689 #define pwszQuery szQuery
690 #define wcErrorText errorText
691 #else
692 WCHAR *pwszQuery = WideStringFromMBString(szQuery);
693 WCHAR wcErrorText[DBDRV_MAX_ERROR_TEXT] = L"";
694 #endif
695
696 MutexLock(hConn->m_mutexTransLock);
697 if (hConn->m_driver->m_dumpSql)
698 ms = GetCurrentTimeMs();
699 hResult = hConn->m_driver->m_fpDrvAsyncSelect(hConn->m_connection, pwszQuery, &dwError, wcErrorText);
700 if ((hResult == NULL) && (dwError == DBERR_CONNECTION_LOST) && hConn->m_reconnectEnabled)
701 {
702 DBReconnect(hConn);
703 hResult = hConn->m_driver->m_fpDrvAsyncSelect(hConn->m_connection, pwszQuery, &dwError, wcErrorText);
704 }
705
706 if (hConn->m_driver->m_dumpSql)
707 {
708 ms = GetCurrentTimeMs() - ms;
709 __DBDbgPrintf(9, _T("%s async query: \"%s\" [%d ms]"), (hResult != NULL) ? _T("Successful") : _T("Failed"), szQuery, (DWORD)ms);
710 }
711 if (hResult == NULL)
712 {
713 MutexUnlock(hConn->m_mutexTransLock);
714
715 #ifndef UNICODE
716 WideCharToMultiByte(CP_ACP, WC_COMPOSITECHECK | WC_DEFAULTCHAR, wcErrorText, -1, errorText, DBDRV_MAX_ERROR_TEXT, NULL, NULL);
717 errorText[DBDRV_MAX_ERROR_TEXT - 1] = 0;
718 #endif
719
720 if (hConn->m_driver->m_logSqlErrors)
721 nxlog_write(g_sqlErrorMsgCode, EVENTLOG_ERROR_TYPE, "ss", szQuery, errorText);
722 if (hConn->m_driver->m_fpEventHandler != NULL)
723 hConn->m_driver->m_fpEventHandler(DBEVENT_QUERY_FAILED, pwszQuery, wcErrorText, hConn->m_driver->m_userArg);
724 }
725
726 #ifndef UNICODE
727 free(pwszQuery);
728 #endif
729
730 if (hResult != NULL)
731 {
732 result = (DB_ASYNC_RESULT)malloc(sizeof(db_async_result_t));
733 result->m_driver = hConn->m_driver;
734 result->m_connection = hConn;
735 result->m_data = hResult;
736 }
737
738 return result;
739 #undef pwszQuery
740 #undef wcErrorText
741 }
742
743 DB_ASYNC_RESULT LIBNXDB_EXPORTABLE DBAsyncSelect(DB_HANDLE hConn, const TCHAR *query)
744 {
745 TCHAR errorText[DBDRV_MAX_ERROR_TEXT];
746
747 return DBAsyncSelectEx(hConn, query, errorText);
748 }
749
750
751 //
752 // Fetch next row from asynchronous SELECT result
753 //
754
755 BOOL LIBNXDB_EXPORTABLE DBFetch(DB_ASYNC_RESULT hResult)
756 {
757 return hResult->m_driver->m_fpDrvFetch(hResult->m_data);
758 }
759
760
761 //
762 // Get field's value from asynchronous SELECT result
763 //
764
765 TCHAR LIBNXDB_EXPORTABLE *DBGetFieldAsync(DB_ASYNC_RESULT hResult, int iColumn, TCHAR *pBuffer, int iBufSize)
766 {
767 #ifdef UNICODE
768 if (pBuffer != NULL)
769 {
770 return hResult->m_driver->m_fpDrvGetFieldAsync(hResult->m_data, iColumn, pBuffer, iBufSize);
771 }
772 else
773 {
774 INT32 nLen;
775 WCHAR *pszTemp;
776
777 nLen = hResult->m_driver->m_fpDrvGetFieldLengthAsync(hResult->m_data, iColumn);
778 if (nLen == -1)
779 {
780 pszTemp = NULL;
781 }
782 else
783 {
784 nLen++;
785 pszTemp = (WCHAR *)malloc(nLen * sizeof(WCHAR));
786 hResult->m_driver->m_fpDrvGetFieldAsync(hResult->m_data, iColumn, pszTemp, nLen);
787 }
788 return pszTemp;
789 }
790 #else
791 WCHAR *pwszData, *pwszBuffer;
792 char *pszRet;
793 int nLen;
794
795 if (pBuffer != NULL)
796 {
797 pwszBuffer = (WCHAR *)malloc(iBufSize * sizeof(WCHAR));
798 if (hResult->m_driver->m_fpDrvGetFieldAsync(hResult->m_data, iColumn, pwszBuffer, iBufSize) != NULL)
799 {
800 WideCharToMultiByte(CP_ACP, WC_COMPOSITECHECK | WC_DEFAULTCHAR,
801 pwszBuffer, -1, pBuffer, iBufSize, NULL, NULL);
802 pszRet = pBuffer;
803 }
804 else
805 {
806 pszRet = NULL;
807 }
808 free(pwszBuffer);
809 }
810 else
811 {
812 nLen = hResult->m_driver->m_fpDrvGetFieldLengthAsync(hResult->m_data, iColumn);
813 if (nLen == -1)
814 {
815 pszRet = NULL;
816 }
817 else
818 {
819 nLen++;
820 pwszBuffer = (WCHAR *)malloc(nLen * sizeof(WCHAR));
821 pwszData = hResult->m_driver->m_fpDrvGetFieldAsync(hResult->m_data, iColumn, pwszBuffer, nLen);
822 if (pwszData != NULL)
823 {
824 nLen = (int)wcslen(pwszData) + 1;
825 pszRet = (char *)malloc(nLen);
826 WideCharToMultiByte(CP_ACP, WC_COMPOSITECHECK | WC_DEFAULTCHAR,
827 pwszData, -1, pszRet, nLen, NULL, NULL);
828 }
829 else
830 {
831 pszRet = NULL;
832 }
833 free(pwszBuffer);
834 }
835 }
836 return pszRet;
837 #endif
838 }
839
840
841 //
842 // Get field's value as unsigned long from asynchronous SELECT result
843 //
844
845 UINT32 LIBNXDB_EXPORTABLE DBGetFieldAsyncULong(DB_ASYNC_RESULT hResult, int iColumn)
846 {
847 INT32 iVal;
848 UINT32 dwVal;
849 TCHAR szBuffer[64];
850
851 if (DBGetFieldAsync(hResult, iColumn, szBuffer, 64) == NULL)
852 return 0;
853 StrStrip(szBuffer);
854 if (szBuffer[0] == _T('-'))
855 {
856 iVal = _tcstol(szBuffer, NULL, 10);
857 memcpy(&dwVal, &iVal, sizeof(INT32)); // To prevent possible conversion
858 }
859 else
860 {
861 dwVal = _tcstoul(szBuffer, NULL, 10);
862 }
863 return dwVal;
864 }
865
866
867 //
868 // Get field's value as unsigned 64-bit int from asynchronous SELECT result
869 //
870
871 UINT64 LIBNXDB_EXPORTABLE DBGetFieldAsyncUInt64(DB_ASYNC_RESULT hResult, int iColumn)
872 {
873 INT64 iVal;
874 UINT64 qwVal;
875 TCHAR szBuffer[64];
876
877 if (DBGetFieldAsync(hResult, iColumn, szBuffer, 64) == NULL)
878 return 0;
879 StrStrip(szBuffer);
880 if (szBuffer[0] == _T('-'))
881 {
882 iVal = _tcstoll(szBuffer, NULL, 10);
883 memcpy(&qwVal, &iVal, sizeof(INT64)); // To prevent possible conversion
884 }
885 else
886 {
887 qwVal = _tcstoull(szBuffer, NULL, 10);
888 }
889 return qwVal;
890 }
891
892 /**
893 * Get field's value as signed long from asynchronous SELECT result
894 */
895 INT32 LIBNXDB_EXPORTABLE DBGetFieldAsyncLong(DB_ASYNC_RESULT hResult, int iColumn)
896 {
897 TCHAR szBuffer[64];
898
899 return DBGetFieldAsync(hResult, iColumn, szBuffer, 64) == NULL ? 0 : _tcstol(szBuffer, NULL, 10);
900 }
901
902 /**
903 * Get field's value as signed 64-bit int from asynchronous SELECT result
904 */
905 INT64 LIBNXDB_EXPORTABLE DBGetFieldAsyncInt64(DB_ASYNC_RESULT hResult, int iColumn)
906 {
907 TCHAR szBuffer[64];
908
909 return DBGetFieldAsync(hResult, iColumn, szBuffer, 64) == NULL ? 0 : _tcstoll(szBuffer, NULL, 10);
910 }
911
912 /**
913 * Get field's value as signed long from asynchronous SELECT result
914 */
915 double LIBNXDB_EXPORTABLE DBGetFieldAsyncDouble(DB_ASYNC_RESULT hResult, int iColumn)
916 {
917 TCHAR szBuffer[64];
918
919 return DBGetFieldAsync(hResult, iColumn, szBuffer, 64) == NULL ? 0 : _tcstod(szBuffer, NULL);
920 }
921
922 /**
923 * Get field's value as IP address from asynchronous SELECT result
924 */
925 UINT32 LIBNXDB_EXPORTABLE DBGetFieldAsyncIPAddr(DB_ASYNC_RESULT hResult, int iColumn)
926 {
927 TCHAR szBuffer[64];
928
929 return (DBGetFieldAsync(hResult, iColumn, szBuffer, 64) == NULL) ? INADDR_NONE : ntohl(_t_inet_addr(szBuffer));
930 }
931
932 /**
933 * Free asynchronous SELECT result
934 */
935 void LIBNXDB_EXPORTABLE DBFreeAsyncResult(DB_ASYNC_RESULT hResult)
936 {
937 hResult->m_driver->m_fpDrvFreeAsyncResult(hResult->m_data);
938 MutexUnlock(hResult->m_connection->m_mutexTransLock);
939 free(hResult);
940 }
941
942 /**
943 * Prepare statement
944 */
945 DB_STATEMENT LIBNXDB_EXPORTABLE DBPrepareEx(DB_HANDLE hConn, const TCHAR *query, TCHAR *errorText)
946 {
947 DB_STATEMENT result = NULL;
948 INT64 ms;
949
950 #ifdef UNICODE
951 #define pwszQuery query
952 #define wcErrorText errorText
953 #else
954 WCHAR *pwszQuery = WideStringFromMBString(query);
955 WCHAR wcErrorText[DBDRV_MAX_ERROR_TEXT] = L"";
956 #endif
957
958 MutexLock(hConn->m_mutexTransLock);
959
960 if (hConn->m_driver->m_dumpSql)
961 ms = GetCurrentTimeMs();
962
963 DWORD errorCode;
964 DBDRV_STATEMENT stmt = hConn->m_driver->m_fpDrvPrepare(hConn->m_connection, pwszQuery, &errorCode, wcErrorText);
965 if ((stmt == NULL) && (errorCode == DBERR_CONNECTION_LOST) && hConn->m_reconnectEnabled)
966 {
967 DBReconnect(hConn);
968 stmt = hConn->m_driver->m_fpDrvPrepare(hConn->m_connection, pwszQuery, &errorCode, wcErrorText);
969 }
970 MutexUnlock(hConn->m_mutexTransLock);
971
972 if (stmt != NULL)
973 {
974 result = (DB_STATEMENT)malloc(sizeof(db_statement_t));
975 result->m_driver = hConn->m_driver;
976 result->m_connection = hConn;
977 result->m_statement = stmt;
978 result->m_query = _tcsdup(query);
979 }
980 else
981 {
982 #ifndef UNICODE
983 WideCharToMultiByte(CP_ACP, WC_COMPOSITECHECK | WC_DEFAULTCHAR, wcErrorText, -1, errorText, DBDRV_MAX_ERROR_TEXT, NULL, NULL);
984 errorText[DBDRV_MAX_ERROR_TEXT - 1] = 0;
985 #endif
986
987 if (hConn->m_driver->m_logSqlErrors)
988 nxlog_write(g_sqlErrorMsgCode, EVENTLOG_ERROR_TYPE, "ss", query, errorText);
989 if (hConn->m_driver->m_fpEventHandler != NULL)
990 hConn->m_driver->m_fpEventHandler(DBEVENT_QUERY_FAILED, pwszQuery, wcErrorText, hConn->m_driver->m_userArg);
991 }
992
993 if (hConn->m_driver->m_dumpSql)
994 {
995 ms = GetCurrentTimeMs() - ms;
996 __DBDbgPrintf(9, _T("{%p} %s prepare: \"%s\" [%d ms]"), result, (result != NULL) ? _T("Successful") : _T("Failed"), query, ms);
997 }
998
999 #ifndef UNICODE
1000 free(pwszQuery);
1001 #endif
1002
1003 if (result != NULL)
1004 {
1005 hConn->m_preparedStatements->add(result);
1006 }
1007
1008 return result;
1009 #undef pwszQuery
1010 #undef wcErrorText
1011 }
1012
1013 /**
1014 * Prepare statement
1015 */
1016 DB_STATEMENT LIBNXDB_EXPORTABLE DBPrepare(DB_HANDLE hConn, const TCHAR *query)
1017 {
1018 TCHAR errorText[DBDRV_MAX_ERROR_TEXT];
1019 return DBPrepareEx(hConn, query, errorText);
1020 }
1021
1022 /**
1023 * Destroy prepared statement
1024 */
1025 void LIBNXDB_EXPORTABLE DBFreeStatement(DB_STATEMENT hStmt)
1026 {
1027 if (hStmt == NULL)
1028 return;
1029
1030 if (hStmt->m_connection != NULL)
1031 {
1032 hStmt->m_connection->m_preparedStatements->remove(hStmt);
1033 }
1034 hStmt->m_driver->m_fpDrvFreeStatement(hStmt->m_statement);
1035 safe_free(hStmt->m_query);
1036 free(hStmt);
1037 }
1038
1039 /**
1040 * Bind parameter (generic)
1041 */
1042 void LIBNXDB_EXPORTABLE DBBind(DB_STATEMENT hStmt, int pos, int sqlType, int cType, void *buffer, int allocType)
1043 {
1044 if ((pos <= 0) || !IS_VALID_STATEMENT_HANDLE(hStmt))
1045 return;
1046
1047 #ifdef UNICODE
1048 #define wBuffer buffer
1049 #define realAllocType allocType
1050 #else
1051 void *wBuffer;
1052 int realAllocType = allocType;
1053 if (cType == DB_CTYPE_STRING)
1054 {
1055 wBuffer = (void *)WideStringFromMBString((char *)buffer);
1056 if (allocType == DB_BIND_DYNAMIC)
1057 free(buffer);
1058 realAllocType = DB_BIND_DYNAMIC;
1059 }
1060 else
1061 {
1062 wBuffer = buffer;
1063 }
1064 #endif
1065 hStmt->m_driver->m_fpDrvBind(hStmt->m_statement, pos, sqlType, cType, wBuffer, realAllocType);
1066 #undef wBuffer
1067 #undef realAllocType
1068
1069 if (hStmt->m_connection->m_driver->m_dumpSql)
1070 {
1071 if (cType == DB_CTYPE_STRING)
1072 {
1073 __DBDbgPrintf(9, _T("{%p} bind at pos %d: \"%s\""), hStmt, pos, buffer);
1074 }
1075 else
1076 {
1077 TCHAR text[64];
1078 switch(cType)
1079 {
1080 case DB_CTYPE_INT32:
1081 _sntprintf(text, 64, _T("%d"), *((INT32 *)buffer));
1082 break;
1083 case DB_CTYPE_UINT32:
1084 _sntprintf(text, 64, _T("%u"), *((UINT32 *)buffer));
1085 break;
1086 case DB_CTYPE_INT64:
1087 _sntprintf(text, 64, INT64_FMT, *((INT64 *)buffer));
1088 break;
1089 case DB_CTYPE_UINT64:
1090 _sntprintf(text, 64, UINT64_FMT, *((UINT64 *)buffer));
1091 break;
1092 case DB_CTYPE_DOUBLE:
1093 _sntprintf(text, 64, _T("%f"), *((double *)buffer));
1094 break;
1095 }
1096 __DBDbgPrintf(9, _T("{%p} bind at pos %d: \"%s\""), hStmt, pos, text);
1097 }
1098 }
1099 }
1100
1101 /**
1102 * Bind string parameter
1103 */
1104 void LIBNXDB_EXPORTABLE DBBind(DB_STATEMENT hStmt, int pos, int sqlType, const TCHAR *value, int allocType)
1105 {
1106 if (value != NULL)
1107 DBBind(hStmt, pos, sqlType, DB_CTYPE_STRING, (void *)value, allocType);
1108 else
1109 DBBind(hStmt, pos, sqlType, DB_CTYPE_STRING, (void *)_T(""), DB_BIND_STATIC);
1110 }
1111
1112 /**
1113 * Bind string parameter with length valiadtion
1114 */
1115 void LIBNXDB_EXPORTABLE DBBind(DB_STATEMENT hStmt, int pos, int sqlType, const TCHAR *value, int allocType, int maxLen)
1116 {
1117 if (value != NULL)
1118 {
1119 if ((int)_tcslen(value) <= maxLen)
1120 {
1121 DBBind(hStmt, pos, sqlType, DB_CTYPE_STRING, (void *)value, allocType);
1122 }
1123 else
1124 {
1125 if (allocType == DB_BIND_DYNAMIC)
1126 {
1127 ((TCHAR *)value)[maxLen] = 0;
1128 DBBind(hStmt, pos, sqlType, DB_CTYPE_STRING, (void *)value, DB_BIND_DYNAMIC);
1129 }
1130 else
1131 {
1132 TCHAR *temp = (TCHAR *)nx_memdup(value, sizeof(TCHAR) * (maxLen + 1));
1133 temp[maxLen] = 0;
1134 DBBind(hStmt, pos, sqlType, DB_CTYPE_STRING, temp, DB_BIND_DYNAMIC);
1135 }
1136 }
1137 }
1138 else
1139 {
1140 DBBind(hStmt, pos, sqlType, DB_CTYPE_STRING, (void *)_T(""), DB_BIND_STATIC);
1141 }
1142 }
1143
1144 /**
1145 * Bind 32 bit integer parameter
1146 */
1147 void LIBNXDB_EXPORTABLE DBBind(DB_STATEMENT hStmt, int pos, int sqlType, INT32 value)
1148 {
1149 DBBind(hStmt, pos, sqlType, DB_CTYPE_INT32, &value, DB_BIND_TRANSIENT);
1150 }
1151
1152 /**
1153 * Bind 32 bit unsigned integer parameter
1154 */
1155 void LIBNXDB_EXPORTABLE DBBind(DB_STATEMENT hStmt, int pos, int sqlType, UINT32 value)
1156 {
1157 // C type intentionally set to INT32 - unsigned numbers will be written as negatives
1158 // and correctly parsed on read by DBGetFieldULong
1159 // Setting it to UINT32 may cause INSERT/UPDATE failures on some databases
1160 DBBind(hStmt, pos, sqlType, DB_CTYPE_INT32, &value, DB_BIND_TRANSIENT);
1161 }
1162
1163 /**
1164 * Bind 64 bit integer parameter
1165 */
1166 void LIBNXDB_EXPORTABLE DBBind(DB_STATEMENT hStmt, int pos, int sqlType, INT64 value)
1167 {
1168 DBBind(hStmt, pos, sqlType, DB_CTYPE_INT64, &value, DB_BIND_TRANSIENT);
1169 }
1170
1171 /**
1172 * Bind 64 bit unsigned integer parameter
1173 */
1174 void LIBNXDB_EXPORTABLE DBBind(DB_STATEMENT hStmt, int pos, int sqlType, UINT64 value)
1175 {
1176 DBBind(hStmt, pos, sqlType, DB_CTYPE_UINT64, &value, DB_BIND_TRANSIENT);
1177 }
1178
1179 /**
1180 * Bind floating point parameter
1181 */
1182 void LIBNXDB_EXPORTABLE DBBind(DB_STATEMENT hStmt, int pos, int sqlType, double value)
1183 {
1184 DBBind(hStmt, pos, sqlType, DB_CTYPE_DOUBLE, &value, DB_BIND_TRANSIENT);
1185 }
1186
1187 /**
1188 * Execute prepared statement (non-SELECT)
1189 */
1190 BOOL LIBNXDB_EXPORTABLE DBExecuteEx(DB_STATEMENT hStmt, TCHAR *errorText)
1191 {
1192 if (!IS_VALID_STATEMENT_HANDLE(hStmt))
1193 {
1194 _tcscpy(errorText, _T("Invalid statement handle"));
1195 return FALSE;
1196 }
1197
1198 #ifdef UNICODE
1199 #define wcErrorText errorText
1200 #else
1201 WCHAR wcErrorText[DBDRV_MAX_ERROR_TEXT] = L"";
1202 #endif
1203 UINT64 ms;
1204
1205 DB_HANDLE hConn = hStmt->m_connection;
1206 MutexLock(hConn->m_mutexTransLock);
1207 if (hConn->m_driver->m_dumpSql)
1208 ms = GetCurrentTimeMs();
1209
1210 DWORD dwResult = hConn->m_driver->m_fpDrvExecute(hConn->m_connection, hStmt->m_statement, wcErrorText);
1211 if (hConn->m_driver->m_dumpSql)
1212 {
1213 ms = GetCurrentTimeMs() - ms;
1214 __DBDbgPrintf(9, _T("%s prepared sync query: \"%s\" [%d ms]"), (dwResult == DBERR_SUCCESS) ? _T("Successful") : _T("Failed"), hStmt->m_query, ms);
1215 }
1216
1217 // Do reconnect if needed, but don't retry statement execution
1218 // because it will fail anyway
1219 if ((dwResult == DBERR_CONNECTION_LOST) && hConn->m_reconnectEnabled)
1220 {
1221 DBReconnect(hConn);
1222 }
1223
1224 MutexUnlock(hConn->m_mutexTransLock);
1225
1226 #ifndef UNICODE
1227 WideCharToMultiByte(CP_ACP, WC_COMPOSITECHECK | WC_DEFAULTCHAR, wcErrorText, -1, errorText, DBDRV_MAX_ERROR_TEXT, NULL, NULL);
1228 errorText[DBDRV_MAX_ERROR_TEXT - 1] = 0;
1229 #endif
1230
1231 if (dwResult != DBERR_SUCCESS)
1232 {
1233 if (hConn->m_driver->m_logSqlErrors)
1234 nxlog_write(g_sqlErrorMsgCode, EVENTLOG_ERROR_TYPE, "ss", hStmt->m_query, errorText);
1235 if (hConn->m_driver->m_fpEventHandler != NULL)
1236 {
1237 #ifdef UNICODE
1238 hConn->m_driver->m_fpEventHandler(DBEVENT_QUERY_FAILED, hStmt->m_query, wcErrorText, hConn->m_driver->m_userArg);
1239 #else
1240 WCHAR *query = WideStringFromMBString(hStmt->m_query);
1241 hConn->m_driver->m_fpEventHandler(DBEVENT_QUERY_FAILED, query, wcErrorText, hConn->m_driver->m_userArg);
1242 free(query);
1243 #endif
1244 }
1245 }
1246
1247 return dwResult == DBERR_SUCCESS;
1248 #undef wcErrorText
1249 }
1250
1251 /**
1252 * Execute prepared statement (non-SELECT)
1253 */
1254 BOOL LIBNXDB_EXPORTABLE DBExecute(DB_STATEMENT hStmt)
1255 {
1256 TCHAR errorText[DBDRV_MAX_ERROR_TEXT];
1257 return DBExecuteEx(hStmt, errorText);
1258 }
1259
1260 /**
1261 * Execute prepared SELECT statement
1262 */
1263 DB_RESULT LIBNXDB_EXPORTABLE DBSelectPreparedEx(DB_STATEMENT hStmt, TCHAR *errorText)
1264 {
1265 if (!IS_VALID_STATEMENT_HANDLE(hStmt))
1266 {
1267 _tcscpy(errorText, _T("Invalid statement handle"));
1268 return NULL;
1269 }
1270
1271 DB_RESULT result = NULL;
1272 #ifdef UNICODE
1273 #define wcErrorText errorText
1274 #else
1275 WCHAR wcErrorText[DBDRV_MAX_ERROR_TEXT] = L"";
1276 #endif
1277
1278 DB_HANDLE hConn = hStmt->m_connection;
1279 MutexLock(hConn->m_mutexTransLock);
1280
1281 INT64 ms;
1282 if (hConn->m_driver->m_dumpSql)
1283 ms = GetCurrentTimeMs();
1284 DWORD dwError;
1285 DBDRV_RESULT hResult = hConn->m_driver->m_fpDrvSelectPrepared(hConn->m_connection, hStmt->m_statement, &dwError, wcErrorText);
1286
1287 if (hConn->m_driver->m_dumpSql)
1288 {
1289 ms = GetCurrentTimeMs() - ms;
1290 __DBDbgPrintf(9, _T("%s prepared sync query: \"%s\" [%d ms]"),
1291 (hResult != NULL) ? _T("Successful") : _T("Failed"), hStmt->m_query, (DWORD)ms);
1292 }
1293
1294 // Do reconnect if needed, but don't retry statement execution
1295 // because it will fail anyway
1296 if ((dwError == DBERR_CONNECTION_LOST) && hConn->m_reconnectEnabled)
1297 {
1298 DBReconnect(hConn);
1299 }
1300
1301 MutexUnlock(hConn->m_mutexTransLock);
1302
1303 #ifndef UNICODE
1304 WideCharToMultiByte(CP_ACP, WC_COMPOSITECHECK | WC_DEFAULTCHAR, wcErrorText, -1, errorText, DBDRV_MAX_ERROR_TEXT, NULL, NULL);
1305 errorText[DBDRV_MAX_ERROR_TEXT - 1] = 0;
1306 #endif
1307
1308 if (hResult == NULL)
1309 {
1310 if (hConn->m_driver->m_logSqlErrors)
1311 nxlog_write(g_sqlErrorMsgCode, EVENTLOG_ERROR_TYPE, "ss", hStmt->m_query, errorText);
1312 if (hConn->m_driver->m_fpEventHandler != NULL)
1313 {
1314 #ifdef UNICODE
1315 hConn->m_driver->m_fpEventHandler(DBEVENT_QUERY_FAILED, hStmt->m_query, wcErrorText, hConn->m_driver->m_userArg);
1316 #else
1317 WCHAR *query = WideStringFromMBString(hStmt->m_query);
1318 hConn->m_driver->m_fpEventHandler(DBEVENT_QUERY_FAILED, query, wcErrorText, hConn->m_driver->m_userArg);
1319 free(query);
1320 #endif
1321 }
1322 }
1323
1324 if (hResult != NULL)
1325 {
1326 result = (DB_RESULT)malloc(sizeof(db_result_t));
1327 result->m_driver = hConn->m_driver;
1328 result->m_connection = hConn;
1329 result->m_data = hResult;
1330 }
1331
1332 return result;
1333 #undef wcErrorText
1334 }
1335
1336 /**
1337 * Execute prepared SELECT statement
1338 */
1339 DB_RESULT LIBNXDB_EXPORTABLE DBSelectPrepared(DB_STATEMENT hStmt)
1340 {
1341 TCHAR errorText[DBDRV_MAX_ERROR_TEXT];
1342 return DBSelectPreparedEx(hStmt, errorText);
1343 }
1344
1345 /**
1346 * Begin transaction
1347 */
1348 BOOL LIBNXDB_EXPORTABLE DBBegin(DB_HANDLE hConn)
1349 {
1350 DWORD dwResult;
1351 BOOL bRet = FALSE;
1352
1353 MutexLock(hConn->m_mutexTransLock);
1354 if (hConn->m_transactionLevel == 0)
1355 {
1356 dwResult = hConn->m_driver->m_fpDrvBegin(hConn->m_connection);
1357 if ((dwResult == DBERR_CONNECTION_LOST) && hConn->m_reconnectEnabled)
1358 {
1359 DBReconnect(hConn);
1360 dwResult = hConn->m_driver->m_fpDrvBegin(hConn->m_connection);
1361 }
1362 if (dwResult == DBERR_SUCCESS)
1363 {
1364 hConn->m_transactionLevel++;
1365 bRet = TRUE;
1366 __DBDbgPrintf(9, _T("BEGIN TRANSACTION successful (level %d)"), hConn->m_transactionLevel);
1367 }
1368 else
1369 {
1370 MutexUnlock(hConn->m_mutexTransLock);
1371 __DBDbgPrintf(9, _T("BEGIN TRANSACTION failed"), hConn->m_transactionLevel);
1372 }
1373 }
1374 else
1375 {
1376 hConn->m_transactionLevel++;
1377 bRet = TRUE;
1378 __DBDbgPrintf(9, _T("BEGIN TRANSACTION successful (level %d)"), hConn->m_transactionLevel);
1379 }
1380 return bRet;
1381 }
1382
1383 /**
1384 * Commit transaction
1385 */
1386 BOOL LIBNXDB_EXPORTABLE DBCommit(DB_HANDLE hConn)
1387 {
1388 BOOL bRet = FALSE;
1389
1390 MutexLock(hConn->m_mutexTransLock);
1391 if (hConn->m_transactionLevel > 0)
1392 {
1393 hConn->m_transactionLevel--;
1394 if (hConn->m_transactionLevel == 0)
1395 bRet = (hConn->m_driver->m_fpDrvCommit(hConn->m_connection) == DBERR_SUCCESS);
1396 else
1397 bRet = TRUE;
1398 __DBDbgPrintf(9, _T("COMMIT TRANSACTION %s (level %d)"), bRet ? _T("successful") : _T("failed"), hConn->m_transactionLevel);
1399 MutexUnlock(hConn->m_mutexTransLock);
1400 }
1401 MutexUnlock(hConn->m_mutexTransLock);
1402 return bRet;
1403 }
1404
1405 /**
1406 * Rollback transaction
1407 */
1408 BOOL LIBNXDB_EXPORTABLE DBRollback(DB_HANDLE hConn)
1409 {
1410 BOOL bRet = FALSE;
1411
1412 MutexLock(hConn->m_mutexTransLock);
1413 if (hConn->m_transactionLevel > 0)
1414 {
1415 hConn->m_transactionLevel--;
1416 if (hConn->m_transactionLevel == 0)
1417 bRet = (hConn->m_driver->m_fpDrvRollback(hConn->m_connection) == DBERR_SUCCESS);
1418 else
1419 bRet = TRUE;
1420 __DBDbgPrintf(9, _T("ROLLBACK TRANSACTION %s (level %d)"), bRet ? _T("successful") : _T("failed"), hConn->m_transactionLevel);
1421 MutexUnlock(hConn->m_mutexTransLock);
1422 }
1423 MutexUnlock(hConn->m_mutexTransLock);
1424 return bRet;
1425 }
1426
1427 /**
1428 * Prepare string for using in SQL statement
1429 */
1430 String LIBNXDB_EXPORTABLE DBPrepareString(DB_HANDLE conn, const TCHAR *str, int maxSize)
1431 {
1432 String out;
1433 if ((maxSize > 0) && (str != NULL) && (maxSize < (int)_tcslen(str)))
1434 {
1435 TCHAR *temp = (TCHAR *)malloc((maxSize + 1) * sizeof(TCHAR));
1436 nx_strncpy(temp, str, maxSize + 1);
1437 #ifdef UNICODE
1438 out.setBuffer(conn->m_driver->m_fpDrvPrepareStringW(temp));
1439 #else
1440 out.setBuffer(conn->m_driver->m_fpDrvPrepareStringA(temp));
1441 #endif
1442 free(temp);
1443 }
1444 else
1445 {
1446 #ifdef UNICODE
1447 out.setBuffer(conn->m_driver->m_fpDrvPrepareStringW(CHECK_NULL_EX(str)));
1448 #else
1449 out.setBuffer(conn->m_driver->m_fpDrvPrepareStringA(CHECK_NULL_EX(str)));
1450 #endif
1451 }
1452 return out;
1453 }
1454
1455 /**
1456 * Prepare string for using in SQL statement (multi-byte string version)
1457 */
1458 #ifdef UNICODE
1459
1460 String LIBNXDB_EXPORTABLE DBPrepareStringA(DB_HANDLE conn, const char *str, int maxSize)
1461 {
1462 WCHAR *wcs = WideStringFromMBString(str);
1463 String s = DBPrepareString(conn, wcs, maxSize);
1464 free(wcs);
1465 return s;
1466 }
1467
1468 #endif
1469
1470 /**
1471 * Characters to be escaped before writing to SQL
1472 */
1473 static TCHAR m_szSpecialChars[] = _T("\x01\x02\x03\x04\x05\x06\x07\x08")
1474 _T("\x09\x0A\x0B\x0C\x0D\x0E\x0F\x10")
1475 _T("\x11\x12\x13\x14\x15\x16\x17\x18")
1476 _T("\x19\x1A\x1B\x1C\x1D\x1E\x1F")
1477 _T("#%\\'\x7F");
1478
1479 /**
1480 * Escape some special characters in string for writing into database.
1481 * DEPRECATED!
1482 */
1483 TCHAR LIBNXDB_EXPORTABLE *EncodeSQLString(const TCHAR *pszIn)
1484 {
1485 TCHAR *pszOut;
1486 int iPosIn, iPosOut, iStrSize;
1487
1488 if ((pszIn != NULL) && (*pszIn != 0))
1489 {
1490 // Allocate destination buffer
1491 iStrSize = (int)_tcslen(pszIn) + 1;
1492 for(iPosIn = 0; pszIn[iPosIn] != 0; iPosIn++)
1493 if (_tcschr(m_szSpecialChars, pszIn[iPosIn]) != NULL)
1494 iStrSize += 2;
1495 pszOut = (TCHAR *)malloc(iStrSize * sizeof(TCHAR));
1496
1497 // Translate string
1498 for(iPosIn = 0, iPosOut = 0; pszIn[iPosIn] != 0; iPosIn++)
1499 if (_tcschr(m_szSpecialChars, pszIn[iPosIn]) != NULL)
1500 {
1501 pszOut[iPosOut++] = _T('#');
1502 pszOut[iPosOut++] = bin2hex(pszIn[iPosIn] >> 4);
1503 pszOut[iPosOut++] = bin2hex(pszIn[iPosIn] & 0x0F);
1504 }
1505 else
1506 {
1507 pszOut[iPosOut++] = pszIn[iPosIn];
1508 }
1509 pszOut[iPosOut] = 0;
1510 }
1511 else
1512 {
1513 // Encode empty strings as #00
1514 pszOut = (TCHAR *)malloc(4 * sizeof(TCHAR));
1515 _tcscpy(pszOut, _T("#00"));
1516 }
1517 return pszOut;
1518 }
1519
1520 /**
1521 * Restore characters encoded by EncodeSQLString()
1522 * Characters are decoded "in place"
1523 */
1524 void LIBNXDB_EXPORTABLE DecodeSQLString(TCHAR *pszStr)
1525 {
1526 int iPosIn, iPosOut;
1527
1528 if (pszStr == NULL)
1529 return;
1530
1531 for(iPosIn = 0, iPosOut = 0; pszStr[iPosIn] != 0; iPosIn++)
1532 {
1533 if (pszStr[iPosIn] == _T('#'))
1534 {
1535 iPosIn++;
1536 pszStr[iPosOut] = hex2bin(pszStr[iPosIn]) << 4;
1537 iPosIn++;
1538 pszStr[iPosOut] |= hex2bin(pszStr[iPosIn]);
1539 iPosOut++;
1540 }
1541 else
1542 {
1543 pszStr[iPosOut++] = pszStr[iPosIn];
1544 }
1545 }
1546 pszStr[iPosOut] = 0;
1547 }
1548
1549 /**
1550 * Get database schema version
1551 * Will return 0 for unknown and -1 in case of SQL errors
1552 */
1553 int LIBNXDB_EXPORTABLE DBGetSchemaVersion(DB_HANDLE conn)
1554 {
1555 DB_RESULT hResult;
1556 int version = 0;
1557
1558 // Read schema version from 'metadata' table, where it should
1559 // be stored starting from schema version 87
1560 // We ignore SQL error in this case, because table 'metadata'
1561 // may not exist in old schema versions
1562 hResult = DBSelect(conn, _T("SELECT var_value FROM metadata WHERE var_name='SchemaVersion'"));
1563 if (hResult != NULL)
1564 {
1565 if (DBGetNumRows(hResult) > 0)
1566 version = DBGetFieldLong(hResult, 0, 0);
1567 DBFreeResult(hResult);
1568 }
1569
1570 // If database schema version is less than 87, version number
1571 // will be stored in 'config' table
1572 if (version == 0)
1573 {
1574 hResult = DBSelect(conn, _T("SELECT var_value FROM config WHERE var_name='DBFormatVersion'"));
1575 if (hResult != NULL)
1576 {
1577 if (DBGetNumRows(hResult) > 0)
1578 version = DBGetFieldLong(hResult, 0, 0);
1579 DBFreeResult(hResult);
1580 }
1581 else
1582 {
1583 version = -1;
1584 }
1585 }
1586
1587 return version;
1588 }
1589
1590 /**
1591 * Get database syntax
1592 */
1593 int LIBNXDB_EXPORTABLE DBGetSyntax(DB_HANDLE conn)
1594 {
1595 DB_RESULT hResult;
1596 TCHAR syntaxId[256];
1597 BOOL read = FALSE;
1598 int syntax;
1599
1600 // Get database syntax
1601 hResult = DBSelect(conn, _T("SELECT var_value FROM metadata WHERE var_name='Syntax'"));
1602 if (hResult != NULL)
1603 {
1604 if (DBGetNumRows(hResult) > 0)
1605 {
1606 DBGetField(hResult, 0, 0, syntaxId, sizeof(syntaxId) / sizeof(TCHAR));
1607 read = TRUE;
1608 }
1609 else
1610 {
1611 _tcscpy(syntaxId, _T("UNKNOWN"));
1612 }
1613 DBFreeResult(hResult);
1614 }
1615
1616 // If database schema version is less than 87, syntax
1617 // will be stored in 'config' table, so try it
1618 if (!read)
1619 {
1620 hResult = DBSelect(conn, _T("SELECT var_value FROM config WHERE var_name='DBSyntax'"));
1621 if (hResult != NULL)
1622 {
1623 if (DBGetNumRows(hResult) > 0)
1624 {
1625 DBGetField(hResult, 0, 0, syntaxId, sizeof(syntaxId) / sizeof(TCHAR));
1626 read = TRUE;
1627 }
1628 else
1629 {
1630 _tcscpy(syntaxId, _T("UNKNOWN"));
1631 }
1632 DBFreeResult(hResult);
1633 }
1634 }
1635
1636 if (!_tcscmp(syntaxId, _T("MYSQL")))
1637 {
1638 syntax = DB_SYNTAX_MYSQL;
1639 }
1640 else if (!_tcscmp(syntaxId, _T("PGSQL")))
1641 {
1642 syntax = DB_SYNTAX_PGSQL;
1643 }
1644 else if (!_tcscmp(syntaxId, _T("MSSQL")))
1645 {
1646 syntax = DB_SYNTAX_MSSQL;
1647 }
1648 else if (!_tcscmp(syntaxId, _T("ORACLE")))
1649 {
1650 syntax = DB_SYNTAX_ORACLE;
1651 }
1652 else if (!_tcscmp(syntaxId, _T("SQLITE")))
1653 {
1654 syntax = DB_SYNTAX_SQLITE;
1655 }
1656 else if (!_tcscmp(syntaxId, _T("DB2")))
1657 {
1658 syntax = DB_SYNTAX_DB2;
1659 }
1660 else
1661 {
1662 syntax = DB_SYNTAX_UNKNOWN;
1663 }
1664
1665 return syntax;
1666 }