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