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