debug tags updated
[public/netxms.git] / src / db / libnxdb / session.cpp
CommitLineData
8a7cd6bb
VK
1/*
2** NetXMS - Network Management System
3** Database Abstraction Library
2df047f4 4** Copyright (C) 2003-2016 Victor Kirhenshtein
8a7cd6bb
VK
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
a13efc48
VK
26#define DEBUG_TAG_CONNECTION _T("db.conn")
27#define DEBUG_TAG_QUERY _T("db.query")
28
74d4ba34
VK
29/**
30 * Check if statement handle is valid
31 */
32#define IS_VALID_STATEMENT_HANDLE(s) ((s != NULL) && (s->m_connection != NULL))
33
a4809f58
VK
34/**
35 * Performance counters
36 */
37static UINT64 s_perfSelectQueries = 0;
38static UINT64 s_perfNonSelectQueries = 0;
39static UINT64 s_perfTotalQueries = 0;
40static UINT64 s_perfLongRunningQueries = 0;
41static UINT64 s_perfFailedQueries = 0;
42
3abf5b29
VK
43/**
44 * Session init callback
45 */
46static void (*s_sessionInitCb)(DB_HANDLE session) = NULL;
47
74d4ba34
VK
48/**
49 * Invalidate all prepared statements on connection
50 */
51static 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 }
eb60ea85 60 hConn->m_preparedStatements->clear();
74d4ba34
VK
61}
62
22aaa779
VK
63/**
64 * Connect to database
65 */
8a7cd6bb
VK
66DB_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
a13efc48 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));
8a7cd6bb
VK
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;
533ce8c0 94 hConn->m_reconnectEnabled = true;
8a7cd6bb
VK
95 hConn->m_connection = hDrvConn;
96 hConn->m_mutexTransLock = MutexCreateRecursive();
97 hConn->m_transactionLevel = 0;
74d4ba34 98 hConn->m_preparedStatements = new ObjectArray<db_statement_t>(4, 4, false);
8a7cd6bb
VK
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
85dfb586
VK
112 if (driver->m_fpDrvSetPrefetchLimit != NULL)
113 driver->m_fpDrvSetPrefetchLimit(hDrvConn, driver->m_defaultPrefetchLimit);
a13efc48 114 nxlog_debug_tag(DEBUG_TAG_CONNECTION, 4, _T("New DB connection opened: handle=%p"), hConn);
3abf5b29
VK
115 if (s_sessionInitCb != NULL)
116 s_sessionInitCb(hConn);
8a7cd6bb
VK
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);
0df58041 130 safe_free(mbSchema);
8a7cd6bb
VK
131 }
132#endif
133 return hConn;
134}
135
22aaa779
VK
136/**
137 * Disconnect from database
138 */
8a7cd6bb
VK
139void LIBNXDB_EXPORTABLE DBDisconnect(DB_HANDLE hConn)
140{
141 if (hConn == NULL)
142 return;
143
a13efc48 144 nxlog_debug_tag(DEBUG_TAG_CONNECTION, 4, _T("DB connection %p closed"), hConn);
8a7cd6bb 145
74d4ba34
VK
146 InvalidatePreparedStatements(hConn);
147
8a7cd6bb
VK
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);
74d4ba34 155 delete hConn->m_preparedStatements;
8a7cd6bb
VK
156 free(hConn);
157}
158
22aaa779
VK
159/**
160 * Enable/disable reconnect
161 */
533ce8c0
VK
162void LIBNXDB_EXPORTABLE DBEnableReconnect(DB_HANDLE hConn, bool enabled)
163{
164 if (hConn != NULL)
165 {
166 hConn->m_reconnectEnabled = enabled;
167 }
168}
169
22aaa779
VK
170/**
171 * Reconnect to database
172 */
8a7cd6bb
VK
173static void DBReconnect(DB_HANDLE hConn)
174{
175 int nCount;
176 WCHAR errorText[DBDRV_MAX_ERROR_TEXT];
177
a13efc48 178 nxlog_debug_tag(DEBUG_TAG_CONNECTION, 4, _T("DB reconnect: handle=%p"), hConn);
22aaa779 179
74d4ba34 180 InvalidatePreparedStatements(hConn);
8a7cd6bb
VK
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)
85dfb586
VK
187 {
188 if (hConn->m_driver->m_fpDrvSetPrefetchLimit != NULL)
189 hConn->m_driver->m_fpDrvSetPrefetchLimit(hConn->m_connection, hConn->m_driver->m_defaultPrefetchLimit);
3abf5b29
VK
190 if (s_sessionInitCb != NULL)
191 s_sessionInitCb(hConn);
8a7cd6bb 192 break;
85dfb586 193 }
8a7cd6bb
VK
194 if (nCount == 0)
195 {
c17f6cbc 196 MutexLock(hConn->m_driver->m_mutexReconnect);
8a7cd6bb 197 if ((hConn->m_driver->m_reconnect == 0) && (hConn->m_driver->m_fpEventHandler != NULL))
526ae8b8 198 hConn->m_driver->m_fpEventHandler(DBEVENT_CONNECTION_LOST, NULL, NULL, true, hConn->m_driver->m_userArg);
8a7cd6bb
VK
199 hConn->m_driver->m_reconnect++;
200 MutexUnlock(hConn->m_driver->m_mutexReconnect);
201 }
202 ThreadSleepMs(1000);
203 }
204 if (nCount > 0)
205 {
c17f6cbc 206 MutexLock(hConn->m_driver->m_mutexReconnect);
8a7cd6bb
VK
207 hConn->m_driver->m_reconnect--;
208 if ((hConn->m_driver->m_reconnect == 0) && (hConn->m_driver->m_fpEventHandler != NULL))
526ae8b8 209 hConn->m_driver->m_fpEventHandler(DBEVENT_CONNECTION_RESTORED, NULL, NULL, false, hConn->m_driver->m_userArg);
8a7cd6bb
VK
210 MutexUnlock(hConn->m_driver->m_mutexReconnect);
211 }
212}
213
85dfb586
VK
214/**
215 * Set default prefetch limit
216 */
217void LIBNXDB_EXPORTABLE DBSetDefaultPrefetchLimit(DB_DRIVER driver, int limit)
218{
219 driver->m_defaultPrefetchLimit = limit;
220}
221
222/**
223 * Set prefetch limit
224 */
225bool 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
3abf5b29
VK
232/**
233 * Set session initialization callback
234 */
235void LIBNXDB_EXPORTABLE DBSetSessionInitCallback(void (*cb)(DB_HANDLE))
236{
237 s_sessionInitCb = cb;
238}
239
22aaa779
VK
240/**
241 * Perform a non-SELECT SQL query
242 */
750d59f2 243bool LIBNXDB_EXPORTABLE DBQueryEx(DB_HANDLE hConn, const TCHAR *szQuery, TCHAR *errorText)
8a7cd6bb
VK
244{
245 DWORD dwResult;
8a7cd6bb
VK
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
c17f6cbc 254 MutexLock(hConn->m_mutexTransLock);
58f1f627 255 INT64 ms = GetCurrentTimeMs();
8a7cd6bb
VK
256
257 dwResult = hConn->m_driver->m_fpDrvQuery(hConn->m_connection, pwszQuery, wcErrorText);
533ce8c0 258 if ((dwResult == DBERR_CONNECTION_LOST) && hConn->m_reconnectEnabled)
8a7cd6bb
VK
259 {
260 DBReconnect(hConn);
261 dwResult = hConn->m_driver->m_fpDrvQuery(hConn->m_connection, pwszQuery, wcErrorText);
262 }
263
a4809f58
VK
264 s_perfNonSelectQueries++;
265 s_perfTotalQueries++;
266
58f1f627 267 ms = GetCurrentTimeMs() - ms;
8a7cd6bb
VK
268 if (hConn->m_driver->m_dumpSql)
269 {
a13efc48 270 nxlog_debug_tag(DEBUG_TAG_QUERY, 9, _T("%s sync query: \"%s\" [%d ms]"), (dwResult == DBERR_SUCCESS) ? _T("Successful") : _T("Failed"), szQuery, ms);
8a7cd6bb 271 }
58f1f627
VK
272 if ((dwResult == DBERR_SUCCESS) && ((UINT32)ms > g_sqlQueryExecTimeThreshold))
273 {
a13efc48 274 nxlog_debug_tag(DEBUG_TAG_QUERY, 3, _T("Long running query: \"%s\" [%d ms]"), szQuery, (int)ms);
a4809f58 275 s_perfLongRunningQueries++;
58f1f627 276 }
8a7cd6bb
VK
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 {
a4809f58 287 s_perfFailedQueries++;
8a7cd6bb
VK
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)
526ae8b8 291 hConn->m_driver->m_fpEventHandler(DBEVENT_QUERY_FAILED, pwszQuery, wcErrorText, dwResult == DBERR_CONNECTION_LOST, hConn->m_driver->m_userArg);
8a7cd6bb
VK
292 }
293
294#ifndef UNICODE
295 free(pwszQuery);
296#endif
297
298 return dwResult == DBERR_SUCCESS;
299#undef pwszQuery
300#undef wcErrorText
301}
302
750d59f2 303bool LIBNXDB_EXPORTABLE DBQuery(DB_HANDLE hConn, const TCHAR *query)
8a7cd6bb
VK
304{
305 TCHAR errorText[DBDRV_MAX_ERROR_TEXT];
8a7cd6bb
VK
306 return DBQueryEx(hConn, query, errorText);
307}
308
22aaa779
VK
309/**
310 * Perform SELECT query
311 */
8a7cd6bb
VK
312DB_RESULT LIBNXDB_EXPORTABLE DBSelectEx(DB_HANDLE hConn, const TCHAR *szQuery, TCHAR *errorText)
313{
314 DBDRV_RESULT hResult;
315 DB_RESULT result = NULL;
6e3f4556 316 DWORD dwError = DBERR_OTHER_ERROR;
8a7cd6bb
VK
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
c17f6cbc 325 MutexLock(hConn->m_mutexTransLock);
58f1f627 326 INT64 ms = GetCurrentTimeMs();
a4809f58
VK
327
328 s_perfSelectQueries++;
329 s_perfTotalQueries++;
330
8a7cd6bb 331 hResult = hConn->m_driver->m_fpDrvSelect(hConn->m_connection, pwszQuery, &dwError, wcErrorText);
533ce8c0 332 if ((hResult == NULL) && (dwError == DBERR_CONNECTION_LOST) && hConn->m_reconnectEnabled)
8a7cd6bb
VK
333 {
334 DBReconnect(hConn);
335 hResult = hConn->m_driver->m_fpDrvSelect(hConn->m_connection, pwszQuery, &dwError, wcErrorText);
336 }
337
58f1f627 338 ms = GetCurrentTimeMs() - ms;
8a7cd6bb
VK
339 if (hConn->m_driver->m_dumpSql)
340 {
a13efc48 341 nxlog_debug_tag(DEBUG_TAG_QUERY, 9, _T("%s sync query: \"%s\" [%d ms]"), (hResult != NULL) ? _T("Successful") : _T("Failed"), szQuery, (int)ms);
58f1f627
VK
342 }
343 if ((hResult != NULL) && ((UINT32)ms > g_sqlQueryExecTimeThreshold))
344 {
a13efc48 345 nxlog_debug_tag(DEBUG_TAG_QUERY, 3, _T("Long running query: \"%s\" [%d ms]"), szQuery, (int)ms);
a4809f58 346 s_perfLongRunningQueries++;
8a7cd6bb
VK
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 {
a4809f58 357 s_perfFailedQueries++;
8a7cd6bb
VK
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)
526ae8b8 361 hConn->m_driver->m_fpEventHandler(DBEVENT_QUERY_FAILED, pwszQuery, wcErrorText, dwError == DBERR_CONNECTION_LOST, hConn->m_driver->m_userArg);
8a7cd6bb
VK
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
381DB_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
74d4ba34
VK
388/**
389 * Get number of columns
390 */
8a7cd6bb
VK
391int LIBNXDB_EXPORTABLE DBGetColumnCount(DB_RESULT hResult)
392{
393 return hResult->m_driver->m_fpDrvGetColumnCount(hResult->m_data);
394}
395
74d4ba34
VK
396/**
397 * Get column name
398 */
750d59f2 399bool LIBNXDB_EXPORTABLE DBGetColumnName(DB_RESULT hResult, int column, TCHAR *buffer, int bufSize)
8a7cd6bb
VK
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
74d4ba34 416/**
f17cf019 417 * Get column count for unbuffered result set
74d4ba34 418 */
f17cf019 419int LIBNXDB_EXPORTABLE DBGetColumnCount(DB_UNBUFFERED_RESULT hResult)
8a7cd6bb 420{
f17cf019 421 return hResult->m_driver->m_fpDrvGetColumnCountUnbuffered(hResult->m_data);
8a7cd6bb
VK
422}
423
74d4ba34 424/**
f17cf019 425 * Get column name for unbuffered result set
74d4ba34 426 */
f17cf019 427bool LIBNXDB_EXPORTABLE DBGetColumnName(DB_UNBUFFERED_RESULT hResult, int column, TCHAR *buffer, int bufSize)
8a7cd6bb
VK
428{
429 const char *name;
430
f17cf019 431 name = hResult->m_driver->m_fpDrvGetColumnNameUnbuffered(hResult->m_data, column);
8a7cd6bb
VK
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
271c3551
VK
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 */
8a7cd6bb
VK
448TCHAR LIBNXDB_EXPORTABLE *DBGetField(DB_RESULT hResult, int iRow, int iColumn, TCHAR *pszBuffer, int nBufLen)
449{
450#ifdef UNICODE
451 if (pszBuffer != NULL)
452 {
c55ab1c2 453 *pszBuffer = 0;
8a7cd6bb
VK
454 return hResult->m_driver->m_fpDrvGetField(hResult->m_data, iRow, iColumn, pszBuffer, nBufLen);
455 }
456 else
457 {
8a7cd6bb 458 WCHAR *pszTemp;
43526096 459 LONG nLen = hResult->m_driver->m_fpDrvGetFieldLength(hResult->m_data, iRow, iColumn);
8a7cd6bb
VK
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
43526096
VK
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 */
481char 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 {
c55ab1c2 487 *pszBuffer = 0;
43526096
VK
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 {
343559f4 500 nLen = nLen * 2 + 1; // increase buffer size because driver may return field length in characters
43526096
VK
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;
343559f4 512 nLen = nLen * 2 + 1; // increase buffer size because driver may return field length in characters
43526096
VK
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 */
8a7cd6bb
VK
527char 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 {
c55ab1c2 535 *pszBuffer = 0;
8a7cd6bb
VK
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 {
7bdb43c3 540 WideCharToMultiByte(CP_ACP, WC_COMPOSITECHECK | WC_DEFAULTCHAR, pwszData, -1, pszBuffer, nBufLen, NULL, NULL);
8a7cd6bb
VK
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
60b9b39c
VK
577/**
578 * Get text field and escape it for XML document. Returned string
579 * always dynamically allocated and must be destroyed by caller.
580 */
581TCHAR 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
22aaa779
VK
589/**
590 * Get field's value as unsigned long
591 */
967893bb 592UINT32 LIBNXDB_EXPORTABLE DBGetFieldULong(DB_RESULT hResult, int iRow, int iColumn)
8a7cd6bb 593{
967893bb
VK
594 INT32 iVal;
595 UINT32 dwVal;
8a7cd6bb
VK
596 TCHAR *pszVal, szBuffer[256];
597
598 pszVal = DBGetField(hResult, iRow, iColumn, szBuffer, 256);
599 if (pszVal == NULL)
600 return 0;
dab4332d
VK
601 StrStrip(pszVal);
602 if (*pszVal == _T('-'))
603 {
604 iVal = _tcstol(pszVal, NULL, 10);
967893bb 605 memcpy(&dwVal, &iVal, sizeof(INT32)); // To prevent possible conversion
dab4332d
VK
606 }
607 else
608 {
609 dwVal = _tcstoul(pszVal, NULL, 10);
610 }
8a7cd6bb
VK
611 return dwVal;
612}
613
440c555e
VK
614/**
615 * Get field's value as unsigned 64-bit int
616 */
967893bb 617UINT64 LIBNXDB_EXPORTABLE DBGetFieldUInt64(DB_RESULT hResult, int iRow, int iColumn)
8a7cd6bb
VK
618{
619 INT64 iVal;
967893bb 620 UINT64 qwVal;
8a7cd6bb
VK
621 TCHAR *pszVal, szBuffer[256];
622
623 pszVal = DBGetField(hResult, iRow, iColumn, szBuffer, 256);
624 if (pszVal == NULL)
625 return 0;
dab4332d
VK
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 }
8a7cd6bb
VK
636 return qwVal;
637}
638
440c555e
VK
639/**
640 * Get field's value as signed long
641 */
967893bb 642INT32 LIBNXDB_EXPORTABLE DBGetFieldLong(DB_RESULT hResult, int iRow, int iColumn)
8a7cd6bb
VK
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
43526096
VK
650/**
651 * Get field's value as signed 64-bit int
652 */
8a7cd6bb
VK
653INT64 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
43526096
VK
661/**
662 * Get field's value as double
663 */
8a7cd6bb
VK
664double 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
43526096 672/**
43709a0c 673 * Get field's value as IPv4 address
43526096 674 */
967893bb 675UINT32 LIBNXDB_EXPORTABLE DBGetFieldIPAddr(DB_RESULT hResult, int iRow, int iColumn)
8a7cd6bb
VK
676{
677 TCHAR *pszVal, szBuffer[256];
678
679 pszVal = DBGetField(hResult, iRow, iColumn, szBuffer, 256);
87f0faf1 680 return pszVal == NULL ? 0 : ntohl(_t_inet_addr(pszVal));
43709a0c
VK
681}
682
683/**
684 * Get field's value as IP address
685 */
686InetAddress 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);
8a7cd6bb
VK
692}
693
ce9e00cc
VK
694/**
695 * Get field`s value as MAC address
696 */
697MacAddress 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
43526096
VK
705/**
706 * Get field's value as integer array from byte array encoded in hex
707 */
750d59f2 708bool LIBNXDB_EXPORTABLE DBGetFieldByteArray(DB_RESULT hResult, int iRow, int iColumn,
1e6b68a6 709 int *pnArray, int nSize, int nDefault)
8a7cd6bb
VK
710{
711 char pbBytes[128];
750d59f2 712 bool bResult;
8a7cd6bb
VK
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;
750d59f2 725 bResult = true;
8a7cd6bb
VK
726 }
727 else
728 {
729 for(i = 0; i < nSize; i++)
730 pnArray[i] = nDefault;
750d59f2 731 bResult = false;
8a7cd6bb
VK
732 }
733 return bResult;
734}
735
750d59f2 736bool LIBNXDB_EXPORTABLE DBGetFieldByteArray2(DB_RESULT hResult, int iRow, int iColumn,
1e6b68a6
VK
737 BYTE *data, int nSize, int nDefault)
738{
750d59f2 739 bool bResult;
1e6b68a6
VK
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);
750d59f2 748 bResult = true;
1e6b68a6
VK
749 }
750 else
751 {
752 memset(data, nDefault, nSize);
750d59f2 753 bResult = false;
1e6b68a6
VK
754 }
755 return bResult;
756}
757
6e3f4556
VK
758/**
759 * Get field's value as GUID
760 */
de4af576 761uuid LIBNXDB_EXPORTABLE DBGetFieldGUID(DB_RESULT hResult, int iRow, int iColumn)
8a7cd6bb 762{
de4af576
VK
763 TCHAR buffer[256];
764 TCHAR *value = DBGetField(hResult, iRow, iColumn, buffer, 256);
765 return (value == NULL) ? uuid::NULL_UUID : uuid::parse(value);
8a7cd6bb
VK
766}
767
6e3f4556
VK
768/**
769 * Get number of rows in result
770 */
8a7cd6bb
VK
771int 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
6e3f4556
VK
778/**
779 * Free result
780 */
8a7cd6bb
VK
781void 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
6e3f4556 790/**
f17cf019 791 * Unbuffered SELECT query
6e3f4556 792 */
f17cf019 793DB_UNBUFFERED_RESULT LIBNXDB_EXPORTABLE DBSelectUnbufferedEx(DB_HANDLE hConn, const TCHAR *szQuery, TCHAR *errorText)
8a7cd6bb 794{
f17cf019
VK
795 DBDRV_UNBUFFERED_RESULT hResult;
796 DB_UNBUFFERED_RESULT result = NULL;
e169a297 797 DWORD dwError = DBERR_OTHER_ERROR;
8a7cd6bb
VK
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
c17f6cbc 806 MutexLock(hConn->m_mutexTransLock);
58f1f627 807 INT64 ms = GetCurrentTimeMs();
a4809f58
VK
808
809 s_perfSelectQueries++;
810 s_perfTotalQueries++;
811
f17cf019 812 hResult = hConn->m_driver->m_fpDrvSelectUnbuffered(hConn->m_connection, pwszQuery, &dwError, wcErrorText);
533ce8c0 813 if ((hResult == NULL) && (dwError == DBERR_CONNECTION_LOST) && hConn->m_reconnectEnabled)
8a7cd6bb
VK
814 {
815 DBReconnect(hConn);
f17cf019 816 hResult = hConn->m_driver->m_fpDrvSelectUnbuffered(hConn->m_connection, pwszQuery, &dwError, wcErrorText);
8a7cd6bb
VK
817 }
818
58f1f627 819 ms = GetCurrentTimeMs() - ms;
8a7cd6bb
VK
820 if (hConn->m_driver->m_dumpSql)
821 {
a13efc48 822 nxlog_debug_tag(DEBUG_TAG_QUERY, 9, _T("%s unbuffered query: \"%s\" [%d ms]"), (hResult != NULL) ? _T("Successful") : _T("Failed"), szQuery, (int)ms);
58f1f627
VK
823 }
824 if ((hResult != NULL) && ((UINT32)ms > g_sqlQueryExecTimeThreshold))
825 {
a13efc48 826 nxlog_debug_tag(DEBUG_TAG_QUERY, 3, _T("Long running query: \"%s\" [%d ms]"), szQuery, (int)ms);
a4809f58 827 s_perfLongRunningQueries++;
8a7cd6bb
VK
828 }
829 if (hResult == NULL)
830 {
a4809f58 831 s_perfFailedQueries++;
8a7cd6bb
VK
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)
526ae8b8 842 hConn->m_driver->m_fpEventHandler(DBEVENT_QUERY_FAILED, pwszQuery, wcErrorText, dwError == DBERR_CONNECTION_LOST, hConn->m_driver->m_userArg);
8a7cd6bb
VK
843 }
844
845#ifndef UNICODE
846 free(pwszQuery);
847#endif
848
849 if (hResult != NULL)
850 {
f17cf019 851 result = (DB_UNBUFFERED_RESULT)malloc(sizeof(db_unbuffered_result_t));
8a7cd6bb
VK
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
f17cf019 862DB_UNBUFFERED_RESULT LIBNXDB_EXPORTABLE DBSelectUnbuffered(DB_HANDLE hConn, const TCHAR *query)
8a7cd6bb
VK
863{
864 TCHAR errorText[DBDRV_MAX_ERROR_TEXT];
865
f17cf019 866 return DBSelectUnbufferedEx(hConn, query, errorText);
8a7cd6bb
VK
867}
868
6e3f4556 869/**
f17cf019 870 * Fetch next row from unbuffered SELECT result
6e3f4556 871 */
f17cf019 872bool LIBNXDB_EXPORTABLE DBFetch(DB_UNBUFFERED_RESULT hResult)
8a7cd6bb
VK
873{
874 return hResult->m_driver->m_fpDrvFetch(hResult->m_data);
875}
876
84ee0f9c 877/**
f17cf019 878 * Get field's value from unbuffered SELECT result
84ee0f9c 879 */
f17cf019 880TCHAR LIBNXDB_EXPORTABLE *DBGetField(DB_UNBUFFERED_RESULT hResult, int iColumn, TCHAR *pBuffer, int iBufSize)
8a7cd6bb
VK
881{
882#ifdef UNICODE
883 if (pBuffer != NULL)
884 {
f17cf019 885 return hResult->m_driver->m_fpDrvGetFieldUnbuffered(hResult->m_data, iColumn, pBuffer, iBufSize);
8a7cd6bb
VK
886 }
887 else
888 {
967893bb 889 INT32 nLen;
8a7cd6bb
VK
890 WCHAR *pszTemp;
891
f17cf019 892 nLen = hResult->m_driver->m_fpDrvGetFieldLengthUnbuffered(hResult->m_data, iColumn);
8a7cd6bb
VK
893 if (nLen == -1)
894 {
895 pszTemp = NULL;
896 }
897 else
898 {
899 nLen++;
900 pszTemp = (WCHAR *)malloc(nLen * sizeof(WCHAR));
f17cf019 901 hResult->m_driver->m_fpDrvGetFieldUnbuffered(hResult->m_data, iColumn, pszTemp, nLen);
8a7cd6bb
VK
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));
f17cf019 913 if (hResult->m_driver->m_fpDrvGetFieldUnbuffered(hResult->m_data, iColumn, pwszBuffer, iBufSize) != NULL)
8a7cd6bb
VK
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 {
f17cf019 927 nLen = hResult->m_driver->m_fpDrvGetFieldLengthUnbuffered(hResult->m_data, iColumn);
8a7cd6bb
VK
928 if (nLen == -1)
929 {
930 pszRet = NULL;
931 }
932 else
933 {
934 nLen++;
935 pwszBuffer = (WCHAR *)malloc(nLen * sizeof(WCHAR));
f17cf019 936 pwszData = hResult->m_driver->m_fpDrvGetFieldUnbuffered(hResult->m_data, iColumn, pwszBuffer, nLen);
8a7cd6bb
VK
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
89e5f052
VK
955/**
956 * Get field's value from unbuffered SELECT result as UTF-8 string
957 */
7f8e3ccf 958char LIBNXDB_EXPORTABLE *DBGetFieldUTF8(DB_UNBUFFERED_RESULT hResult, int iColumn, char *buffer, int iBufSize)
89e5f052 959{
7f8e3ccf 960 if (hResult->m_driver->m_fpDrvGetFieldUTF8 != NULL)
89e5f052 961 {
7f8e3ccf
VK
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 }
89e5f052
VK
983 }
984 else
985 {
7f8e3ccf
VK
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;
89e5f052 997 }
89e5f052
VK
998}
999
6e3f4556 1000/**
f17cf019 1001 * Get field's value as unsigned long from unbuffered SELECT result
6e3f4556 1002 */
f17cf019 1003UINT32 LIBNXDB_EXPORTABLE DBGetFieldULong(DB_UNBUFFERED_RESULT hResult, int iColumn)
8a7cd6bb 1004{
967893bb
VK
1005 INT32 iVal;
1006 UINT32 dwVal;
8a7cd6bb
VK
1007 TCHAR szBuffer[64];
1008
f17cf019 1009 if (DBGetField(hResult, iColumn, szBuffer, 64) == NULL)
8a7cd6bb 1010 return 0;
dab4332d
VK
1011 StrStrip(szBuffer);
1012 if (szBuffer[0] == _T('-'))
1013 {
1014 iVal = _tcstol(szBuffer, NULL, 10);
967893bb 1015 memcpy(&dwVal, &iVal, sizeof(INT32)); // To prevent possible conversion
dab4332d
VK
1016 }
1017 else
1018 {
1019 dwVal = _tcstoul(szBuffer, NULL, 10);
1020 }
8a7cd6bb
VK
1021 return dwVal;
1022}
1023
6e3f4556 1024/**
f17cf019 1025 * Get field's value as unsigned 64-bit int from unbuffered SELECT result
6e3f4556 1026 */
f17cf019 1027UINT64 LIBNXDB_EXPORTABLE DBGetFieldUInt64(DB_UNBUFFERED_RESULT hResult, int iColumn)
8a7cd6bb
VK
1028{
1029 INT64 iVal;
967893bb 1030 UINT64 qwVal;
8a7cd6bb
VK
1031 TCHAR szBuffer[64];
1032
f17cf019 1033 if (DBGetField(hResult, iColumn, szBuffer, 64) == NULL)
8a7cd6bb 1034 return 0;
dab4332d
VK
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 }
8a7cd6bb
VK
1045 return qwVal;
1046}
1047
22aaa779 1048/**
f17cf019 1049 * Get field's value as signed long from unbuffered SELECT result
22aaa779 1050 */
f17cf019 1051INT32 LIBNXDB_EXPORTABLE DBGetFieldLong(DB_UNBUFFERED_RESULT hResult, int iColumn)
8a7cd6bb
VK
1052{
1053 TCHAR szBuffer[64];
f17cf019 1054 return DBGetField(hResult, iColumn, szBuffer, 64) == NULL ? 0 : _tcstol(szBuffer, NULL, 10);
8a7cd6bb
VK
1055}
1056
22aaa779 1057/**
f17cf019 1058 * Get field's value as signed 64-bit int from unbuffered SELECT result
22aaa779 1059 */
f17cf019 1060INT64 LIBNXDB_EXPORTABLE DBGetFieldInt64(DB_UNBUFFERED_RESULT hResult, int iColumn)
8a7cd6bb
VK
1061{
1062 TCHAR szBuffer[64];
f17cf019 1063 return DBGetField(hResult, iColumn, szBuffer, 64) == NULL ? 0 : _tcstoll(szBuffer, NULL, 10);
8a7cd6bb
VK
1064}
1065
22aaa779 1066/**
f17cf019 1067 * Get field's value as signed long from unbuffered SELECT result
22aaa779 1068 */
f17cf019 1069double LIBNXDB_EXPORTABLE DBGetFieldDouble(DB_UNBUFFERED_RESULT hResult, int iColumn)
8a7cd6bb
VK
1070{
1071 TCHAR szBuffer[64];
f17cf019 1072 return DBGetField(hResult, iColumn, szBuffer, 64) == NULL ? 0 : _tcstod(szBuffer, NULL);
8a7cd6bb
VK
1073}
1074
22aaa779 1075/**
f17cf019 1076 * Get field's value as IPv4 address from unbuffered SELECT result
22aaa779 1077 */
f17cf019 1078UINT32 LIBNXDB_EXPORTABLE DBGetFieldIPAddr(DB_UNBUFFERED_RESULT hResult, int iColumn)
8a7cd6bb 1079{
f17cf019
VK
1080 TCHAR buffer[64];
1081 return (DBGetField(hResult, iColumn, buffer, 64) == NULL) ? INADDR_NONE : ntohl(_t_inet_addr(buffer));
8a7cd6bb
VK
1082}
1083
43709a0c 1084/**
f17cf019 1085 * Get field's value as IP address from unbuffered SELECT result
43709a0c 1086 */
f17cf019 1087InetAddress LIBNXDB_EXPORTABLE DBGetFieldInetAddr(DB_UNBUFFERED_RESULT hResult, int iColumn)
43709a0c 1088{
f17cf019
VK
1089 TCHAR buffer[64];
1090 return (DBGetField(hResult, iColumn, buffer, 64) == NULL) ? InetAddress() : InetAddress::parse(buffer);
43709a0c
VK
1091}
1092
74d4ba34 1093/**
f17cf019 1094 * Get field's value as GUID from unbuffered SELECT result
74d4ba34 1095 */
f17cf019 1096uuid LIBNXDB_EXPORTABLE DBGetFieldGUID(DB_UNBUFFERED_RESULT hResult, int iColumn)
8a7cd6bb 1097{
f17cf019
VK
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 */
1105void LIBNXDB_EXPORTABLE DBFreeResult(DB_UNBUFFERED_RESULT hResult)
1106{
1107 hResult->m_driver->m_fpDrvFreeUnbufferedResult(hResult->m_data);
8a7cd6bb
VK
1108 MutexUnlock(hResult->m_connection->m_mutexTransLock);
1109 free(hResult);
1110}
1111
74d4ba34
VK
1112/**
1113 * Prepare statement
1114 */
8a7cd6bb
VK
1115DB_STATEMENT LIBNXDB_EXPORTABLE DBPrepareEx(DB_HANDLE hConn, const TCHAR *query, TCHAR *errorText)
1116{
1117 DB_STATEMENT result = NULL;
dab4332d 1118 INT64 ms;
8a7cd6bb
VK
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
de1d708f
VK
1128 MutexLock(hConn->m_mutexTransLock);
1129
dab4332d
VK
1130 if (hConn->m_driver->m_dumpSql)
1131 ms = GetCurrentTimeMs();
1132
de1d708f
VK
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
8a7cd6bb
VK
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)
526ae8b8 1160 hConn->m_driver->m_fpEventHandler(DBEVENT_QUERY_FAILED, pwszQuery, wcErrorText, errorCode == DBERR_CONNECTION_LOST, hConn->m_driver->m_userArg);
a4809f58
VK
1161
1162 s_perfFailedQueries++;
1163 s_perfTotalQueries++;
8a7cd6bb
VK
1164 }
1165
dab4332d
VK
1166 if (hConn->m_driver->m_dumpSql)
1167 {
1168 ms = GetCurrentTimeMs() - ms;
a13efc48 1169 nxlog_debug_tag(DEBUG_TAG_QUERY, 9, _T("{%p} %s prepare: \"%s\" [%d ms]"), result, (result != NULL) ? _T("Successful") : _T("Failed"), query, ms);
dab4332d
VK
1170 }
1171
8a7cd6bb
VK
1172#ifndef UNICODE
1173 free(pwszQuery);
1174#endif
1175
74d4ba34
VK
1176 if (result != NULL)
1177 {
1178 hConn->m_preparedStatements->add(result);
1179 }
1180
8a7cd6bb
VK
1181 return result;
1182#undef pwszQuery
1183#undef wcErrorText
1184}
1185
74d4ba34
VK
1186/**
1187 * Prepare statement
1188 */
8a7cd6bb
VK
1189DB_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
74d4ba34
VK
1195/**
1196 * Destroy prepared statement
1197 */
8a7cd6bb
VK
1198void LIBNXDB_EXPORTABLE DBFreeStatement(DB_STATEMENT hStmt)
1199{
74d4ba34
VK
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);
2223a4d7 1208 free(hStmt->m_query);
8a7cd6bb
VK
1209 free(hStmt);
1210}
1211
84ee0f9c
VK
1212/**
1213 * Get source query for prepared statement
1214 */
1215const TCHAR LIBNXDB_EXPORTABLE *DBGetStatementSource(DB_STATEMENT hStmt)
1216{
1217 return hStmt->m_query;
1218}
1219
66c11d49
VK
1220/**
1221 * Open batch
1222 */
1223bool 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 */
1233void 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
dd651102
VK
1239/**
1240 * Bind parameter (generic)
1241 */
8a7cd6bb
VK
1242void LIBNXDB_EXPORTABLE DBBind(DB_STATEMENT hStmt, int pos, int sqlType, int cType, void *buffer, int allocType)
1243{
74d4ba34 1244 if ((pos <= 0) || !IS_VALID_STATEMENT_HANDLE(hStmt))
47a89886
VK
1245 return;
1246
6449475d
VK
1247 if (hStmt->m_connection->m_driver->m_dumpSql)
1248 {
1249 if (cType == DB_CTYPE_STRING)
1250 {
a13efc48 1251 nxlog_debug_tag(DEBUG_TAG_QUERY, 9, _T("{%p} bind at pos %d: \"%s\""), hStmt, pos, buffer);
6449475d 1252 }
b8849590
VK
1253 else if (cType == DB_CTYPE_UTF8_STRING)
1254 {
a13efc48 1255 nxlog_debug_tag(DEBUG_TAG_QUERY, 9, _T("{%p} bind at pos %d (UTF-8): \"%hs\""), hStmt, pos, buffer);
b8849590 1256 }
6449475d
VK
1257 else
1258 {
1259 TCHAR text[64];
1260 switch(cType)
1261 {
1262 case DB_CTYPE_INT32:
967893bb 1263 _sntprintf(text, 64, _T("%d"), *((INT32 *)buffer));
6449475d
VK
1264 break;
1265 case DB_CTYPE_UINT32:
967893bb 1266 _sntprintf(text, 64, _T("%u"), *((UINT32 *)buffer));
6449475d
VK
1267 break;
1268 case DB_CTYPE_INT64:
1269 _sntprintf(text, 64, INT64_FMT, *((INT64 *)buffer));
1270 break;
1271 case DB_CTYPE_UINT64:
967893bb 1272 _sntprintf(text, 64, UINT64_FMT, *((UINT64 *)buffer));
6449475d
VK
1273 break;
1274 case DB_CTYPE_DOUBLE:
1275 _sntprintf(text, 64, _T("%f"), *((double *)buffer));
1276 break;
1277 }
a13efc48 1278 nxlog_debug_tag(DEBUG_TAG_QUERY, 9, _T("{%p} bind at pos %d: \"%s\""), hStmt, pos, text);
6449475d
VK
1279 }
1280 }
70d5f4b0
VK
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
8a7cd6bb
VK
1303}
1304
dd651102
VK
1305/**
1306 * Bind string parameter
1307 */
8a7cd6bb
VK
1308void LIBNXDB_EXPORTABLE DBBind(DB_STATEMENT hStmt, int pos, int sqlType, const TCHAR *value, int allocType)
1309{
511bea22
VK
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);
8a7cd6bb
VK
1314}
1315
dd651102 1316/**
526ae8b8 1317 * Bind string parameter with length validation
dd651102
VK
1318 */
1319void 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 */
967893bb 1351void LIBNXDB_EXPORTABLE DBBind(DB_STATEMENT hStmt, int pos, int sqlType, INT32 value)
8a7cd6bb
VK
1352{
1353 DBBind(hStmt, pos, sqlType, DB_CTYPE_INT32, &value, DB_BIND_TRANSIENT);
1354}
1355
dd651102
VK
1356/**
1357 * Bind 32 bit unsigned integer parameter
1358 */
967893bb 1359void LIBNXDB_EXPORTABLE DBBind(DB_STATEMENT hStmt, int pos, int sqlType, UINT32 value)
8a7cd6bb 1360{
dab4332d
VK
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);
8a7cd6bb
VK
1365}
1366
dd651102
VK
1367/**
1368 * Bind 64 bit integer parameter
1369 */
8a7cd6bb
VK
1370void 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
dd651102
VK
1375/**
1376 * Bind 64 bit unsigned integer parameter
1377 */
967893bb 1378void LIBNXDB_EXPORTABLE DBBind(DB_STATEMENT hStmt, int pos, int sqlType, UINT64 value)
8a7cd6bb
VK
1379{
1380 DBBind(hStmt, pos, sqlType, DB_CTYPE_UINT64, &value, DB_BIND_TRANSIENT);
1381}
1382
dd651102
VK
1383/**
1384 * Bind floating point parameter
1385 */
8a7cd6bb
VK
1386void 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
de4af576
VK
1391/**
1392 * Bind UUID parameter
1393 */
1394void 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
ce9e00cc
VK
1400/**
1401 * Bind Mac Address
1402 */
1403void 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
967893bb
VK
1409/**
1410 * Execute prepared statement (non-SELECT)
1411 */
750d59f2 1412bool LIBNXDB_EXPORTABLE DBExecuteEx(DB_STATEMENT hStmt, TCHAR *errorText)
8a7cd6bb 1413{
74d4ba34
VK
1414 if (!IS_VALID_STATEMENT_HANDLE(hStmt))
1415 {
1416 _tcscpy(errorText, _T("Invalid statement handle"));
750d59f2 1417 return false;
74d4ba34
VK
1418 }
1419
8a7cd6bb
VK
1420#ifdef UNICODE
1421#define wcErrorText errorText
1422#else
1423 WCHAR wcErrorText[DBDRV_MAX_ERROR_TEXT] = L"";
1424#endif
8a7cd6bb
VK
1425
1426 DB_HANDLE hConn = hStmt->m_connection;
c17f6cbc 1427 MutexLock(hConn->m_mutexTransLock);
58f1f627 1428 UINT64 ms = GetCurrentTimeMs();
8a7cd6bb 1429
a4809f58
VK
1430 s_perfNonSelectQueries++;
1431 s_perfTotalQueries++;
1432
8a7cd6bb 1433 DWORD dwResult = hConn->m_driver->m_fpDrvExecute(hConn->m_connection, hStmt->m_statement, wcErrorText);
58f1f627 1434 ms = GetCurrentTimeMs() - ms;
8a7cd6bb
VK
1435 if (hConn->m_driver->m_dumpSql)
1436 {
a13efc48 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);
58f1f627
VK
1438 }
1439 if ((dwResult == DBERR_SUCCESS) && ((UINT32)ms > g_sqlQueryExecTimeThreshold))
1440 {
a13efc48 1441 nxlog_debug_tag(DEBUG_TAG_QUERY, 3, _T("Long running query: \"%s\" [%d ms]"), hStmt->m_query, (int)ms);
a4809f58 1442 s_perfLongRunningQueries++;
8a7cd6bb 1443 }
22aaa779
VK
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 }
8a7cd6bb
VK
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
526ae8b8 1466 hConn->m_driver->m_fpEventHandler(DBEVENT_QUERY_FAILED, hStmt->m_query, wcErrorText, dwResult == DBERR_CONNECTION_LOST, hConn->m_driver->m_userArg);
8a7cd6bb
VK
1467#else
1468 WCHAR *query = WideStringFromMBString(hStmt->m_query);
526ae8b8 1469 hConn->m_driver->m_fpEventHandler(DBEVENT_QUERY_FAILED, query, wcErrorText, dwResult == DBERR_CONNECTION_LOST, hConn->m_driver->m_userArg);
8a7cd6bb
VK
1470 free(query);
1471#endif
1472 }
a4809f58 1473 s_perfFailedQueries++;
8a7cd6bb
VK
1474 }
1475
1476 return dwResult == DBERR_SUCCESS;
1477#undef wcErrorText
1478}
1479
74d4ba34
VK
1480/**
1481 * Execute prepared statement (non-SELECT)
1482 */
750d59f2 1483bool LIBNXDB_EXPORTABLE DBExecute(DB_STATEMENT hStmt)
8a7cd6bb
VK
1484{
1485 TCHAR errorText[DBDRV_MAX_ERROR_TEXT];
1486 return DBExecuteEx(hStmt, errorText);
1487}
1488
480e036b
VK
1489/**
1490 * Execute prepared SELECT statement
1491 */
8a7cd6bb
VK
1492DB_RESULT LIBNXDB_EXPORTABLE DBSelectPreparedEx(DB_STATEMENT hStmt, TCHAR *errorText)
1493{
74d4ba34
VK
1494 if (!IS_VALID_STATEMENT_HANDLE(hStmt))
1495 {
1496 _tcscpy(errorText, _T("Invalid statement handle"));
1497 return NULL;
1498 }
1499
8a7cd6bb 1500 DB_RESULT result = NULL;
8a7cd6bb
VK
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;
c17f6cbc 1508 MutexLock(hConn->m_mutexTransLock);
74d4ba34 1509
a4809f58
VK
1510 s_perfSelectQueries++;
1511 s_perfTotalQueries++;
1512
58f1f627 1513 INT64 ms = GetCurrentTimeMs();
6e3f4556 1514 DWORD dwError = DBERR_OTHER_ERROR;
74d4ba34 1515 DBDRV_RESULT hResult = hConn->m_driver->m_fpDrvSelectPrepared(hConn->m_connection, hStmt->m_statement, &dwError, wcErrorText);
8a7cd6bb 1516
58f1f627 1517 ms = GetCurrentTimeMs() - ms;
8a7cd6bb
VK
1518 if (hConn->m_driver->m_dumpSql)
1519 {
a13efc48 1520 nxlog_debug_tag(DEBUG_TAG_QUERY, 9, _T("%s prepared sync query: \"%s\" [%d ms]"),
58f1f627
VK
1521 (hResult != NULL) ? _T("Successful") : _T("Failed"), hStmt->m_query, (int)ms);
1522 }
1523 if ((hResult != NULL) && ((UINT32)ms > g_sqlQueryExecTimeThreshold))
1524 {
a13efc48 1525 nxlog_debug_tag(DEBUG_TAG_QUERY, 3, _T("Long running query: \"%s\" [%d ms]"), hStmt->m_query, (int)ms);
a4809f58 1526 s_perfLongRunningQueries++;
8a7cd6bb 1527 }
22aaa779
VK
1528
1529 // Do reconnect if needed, but don't retry statement execution
1530 // because it will fail anyway
6e3f4556 1531 if ((hResult == NULL) && (dwError == DBERR_CONNECTION_LOST) && hConn->m_reconnectEnabled)
22aaa779
VK
1532 {
1533 DBReconnect(hConn);
1534 }
1535
8a7cd6bb
VK
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
526ae8b8 1550 hConn->m_driver->m_fpEventHandler(DBEVENT_QUERY_FAILED, hStmt->m_query, wcErrorText, dwError == DBERR_CONNECTION_LOST, hConn->m_driver->m_userArg);
8a7cd6bb
VK
1551#else
1552 WCHAR *query = WideStringFromMBString(hStmt->m_query);
526ae8b8 1553 hConn->m_driver->m_fpEventHandler(DBEVENT_QUERY_FAILED, query, wcErrorText, dwError == DBERR_CONNECTION_LOST, hConn->m_driver->m_userArg);
8a7cd6bb
VK
1554 free(query);
1555#endif
1556 }
a4809f58 1557 s_perfFailedQueries++;
8a7cd6bb
VK
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
480e036b
VK
1572/**
1573 * Execute prepared SELECT statement
1574 */
8a7cd6bb
VK
1575DB_RESULT LIBNXDB_EXPORTABLE DBSelectPrepared(DB_STATEMENT hStmt)
1576{
1577 TCHAR errorText[DBDRV_MAX_ERROR_TEXT];
1578 return DBSelectPreparedEx(hStmt, errorText);
1579}
1580
f17cf019
VK
1581/**
1582 * Execute prepared SELECT statement without caching results
1583 */
1584DB_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 {
a13efc48 1612 nxlog_debug_tag(DEBUG_TAG_QUERY, 9, _T("%s prepared sync query: \"%s\" [%d ms]"),
f17cf019
VK
1613 (hResult != NULL) ? _T("Successful") : _T("Failed"), hStmt->m_query, (int)ms);
1614 }
1615 if ((hResult != NULL) && ((UINT32)ms > g_sqlQueryExecTimeThreshold))
1616 {
a13efc48 1617 nxlog_debug_tag(DEBUG_TAG_QUERY, 3, _T("Long running query: \"%s\" [%d ms]"), hStmt->m_query, (int)ms);
f17cf019
VK
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 */
1667DB_UNBUFFERED_RESULT LIBNXDB_EXPORTABLE DBSelectPreparedUnbuffered(DB_STATEMENT hStmt)
1668{
1669 TCHAR errorText[DBDRV_MAX_ERROR_TEXT];
1670 return DBSelectPreparedUnbufferedEx(hStmt, errorText);
1671}
1672
480e036b
VK
1673/**
1674 * Begin transaction
1675 */
750d59f2 1676bool LIBNXDB_EXPORTABLE DBBegin(DB_HANDLE hConn)
8a7cd6bb
VK
1677{
1678 DWORD dwResult;
750d59f2 1679 bool bRet = false;
8a7cd6bb 1680
c17f6cbc 1681 MutexLock(hConn->m_mutexTransLock);
8a7cd6bb
VK
1682 if (hConn->m_transactionLevel == 0)
1683 {
1684 dwResult = hConn->m_driver->m_fpDrvBegin(hConn->m_connection);
533ce8c0 1685 if ((dwResult == DBERR_CONNECTION_LOST) && hConn->m_reconnectEnabled)
8a7cd6bb
VK
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++;
750d59f2 1693 bRet = true;
a13efc48 1694 nxlog_debug_tag(DEBUG_TAG_QUERY, 9, _T("BEGIN TRANSACTION successful (level %d)"), hConn->m_transactionLevel);
8a7cd6bb
VK
1695 }
1696 else
1697 {
1698 MutexUnlock(hConn->m_mutexTransLock);
a13efc48 1699 nxlog_debug_tag(DEBUG_TAG_QUERY, 9, _T("BEGIN TRANSACTION failed"), hConn->m_transactionLevel);
8a7cd6bb
VK
1700 }
1701 }
1702 else
1703 {
1704 hConn->m_transactionLevel++;
750d59f2 1705 bRet = true;
a13efc48 1706 nxlog_debug_tag(DEBUG_TAG_QUERY, 9, _T("BEGIN TRANSACTION successful (level %d)"), hConn->m_transactionLevel);
8a7cd6bb
VK
1707 }
1708 return bRet;
1709}
1710
480e036b
VK
1711/**
1712 * Commit transaction
1713 */
750d59f2 1714bool LIBNXDB_EXPORTABLE DBCommit(DB_HANDLE hConn)
8a7cd6bb 1715{
750d59f2 1716 bool bRet = false;
8a7cd6bb 1717
c17f6cbc 1718 MutexLock(hConn->m_mutexTransLock);
8a7cd6bb
VK
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
750d59f2 1725 bRet = true;
a13efc48 1726 nxlog_debug_tag(DEBUG_TAG_QUERY, 9, _T("COMMIT TRANSACTION %s (level %d)"), bRet ? _T("successful") : _T("failed"), hConn->m_transactionLevel);
8a7cd6bb
VK
1727 MutexUnlock(hConn->m_mutexTransLock);
1728 }
1729 MutexUnlock(hConn->m_mutexTransLock);
1730 return bRet;
1731}
1732
480e036b
VK
1733/**
1734 * Rollback transaction
1735 */
750d59f2 1736bool LIBNXDB_EXPORTABLE DBRollback(DB_HANDLE hConn)
8a7cd6bb 1737{
750d59f2 1738 bool bRet = false;
8a7cd6bb 1739
c17f6cbc 1740 MutexLock(hConn->m_mutexTransLock);
8a7cd6bb
VK
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
750d59f2 1747 bRet = true;
a13efc48 1748 nxlog_debug_tag(DEBUG_TAG_QUERY, 9, _T("ROLLBACK TRANSACTION %s (level %d)"), bRet ? _T("successful") : _T("failed"), hConn->m_transactionLevel);
8a7cd6bb
VK
1749 MutexUnlock(hConn->m_mutexTransLock);
1750 }
1751 MutexUnlock(hConn->m_mutexTransLock);
1752 return bRet;
1753}
1754
480e036b
VK
1755/**
1756 * Prepare string for using in SQL statement
1757 */
8a7cd6bb 1758String LIBNXDB_EXPORTABLE DBPrepareString(DB_HANDLE conn, const TCHAR *str, int maxSize)
9bd1bace
VK
1759{
1760 return DBPrepareString(conn->m_driver, str, maxSize);
1761}
1762
1763/**
1764 * Prepare string for using in SQL statement
1765 */
1766String LIBNXDB_EXPORTABLE DBPrepareString(DB_DRIVER drv, const TCHAR *str, int maxSize)
8a7cd6bb
VK
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
9bd1bace 1774 out.setBuffer(drv->m_fpDrvPrepareStringW(temp));
8a7cd6bb 1775#else
9bd1bace 1776 out.setBuffer(drv->m_fpDrvPrepareStringA(temp));
8a7cd6bb
VK
1777#endif
1778 free(temp);
1779 }
1780 else
1781 {
1782#ifdef UNICODE
9bd1bace 1783 out.setBuffer(drv->m_fpDrvPrepareStringW(CHECK_NULL_EX(str)));
8a7cd6bb 1784#else
9bd1bace 1785 out.setBuffer(drv->m_fpDrvPrepareStringA(CHECK_NULL_EX(str)));
8a7cd6bb
VK
1786#endif
1787 }
1788 return out;
1789}
1790
480e036b
VK
1791/**
1792 * Prepare string for using in SQL statement (multi-byte string version)
1793 */
8a7cd6bb
VK
1794#ifdef UNICODE
1795
1796String 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
9bd1bace
VK
1804String 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
8a7cd6bb
VK
1812#endif
1813
daf3c104
VK
1814/**
1815 * Check if given table exist
1816 */
1817int 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}
a4809f58
VK
1827
1828/**
1829 * Get performance counters
1830 */
1831void 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}