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