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