e5e682fb062054027bf3a289aa0eabcebc5de969
[public/netxms.git] / src / server / libnxsrv / db.cpp
1 /*
2 ** NetXMS - Network Management System
3 ** Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 Victor Kirhenshtein
4 **
5 ** This program is free software; you can redistribute it and/or modify
6 ** it under the terms of the GNU General Public License as published by
7 ** the Free Software Foundation; either version 2 of the License, or
8 ** (at your option) any later version.
9 **
10 ** This program is distributed in the hope that it will be useful,
11 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
12 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 ** GNU General Public License for more details.
14 **
15 ** You should have received a copy of the GNU General Public License
16 ** along with this program; if not, write to the Free Software
17 ** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 **
19 ** File: db.cpp
20 **
21 **/
22
23 #include "libnxsrv.h"
24
25
26 //
27 // Global variables
28 //
29
30 TCHAR LIBNXSRV_EXPORTABLE g_szDbDriver[MAX_PATH] = _T("");
31 TCHAR LIBNXSRV_EXPORTABLE g_szDbDrvParams[MAX_PATH] = _T("");
32 TCHAR LIBNXSRV_EXPORTABLE g_szDbServer[MAX_PATH] = _T("127.0.0.1");
33 TCHAR LIBNXSRV_EXPORTABLE g_szDbLogin[MAX_DB_LOGIN] = _T("netxms");
34 TCHAR LIBNXSRV_EXPORTABLE g_szDbPassword[MAX_DB_PASSWORD] = _T("");
35 TCHAR LIBNXSRV_EXPORTABLE g_szDbName[MAX_DB_NAME] = _T("netxms_db");
36
37
38 //
39 // Static data
40 //
41
42 static BOOL m_bnxlog_write = FALSE;
43 static BOOL m_bLogSQLErrors = FALSE;
44 static BOOL m_bDumpSQL = FALSE;
45 static int m_nReconnect = 0;
46 static MUTEX m_mutexReconnect = INVALID_MUTEX_HANDLE;
47 static HMODULE m_hDriver = NULL;
48 static DB_CONNECTION (* m_fpDrvConnect)(const char *, const char *, const char *, const char *) = NULL;
49 static void (* m_fpDrvDisconnect)(DB_CONNECTION) = NULL;
50 static DWORD (* m_fpDrvQuery)(DB_CONNECTION, WCHAR *, TCHAR *) = NULL;
51 static DB_RESULT (* m_fpDrvSelect)(DB_CONNECTION, WCHAR *, DWORD *, TCHAR *) = NULL;
52 static DB_ASYNC_RESULT (* m_fpDrvAsyncSelect)(DB_CONNECTION, WCHAR *, DWORD *, TCHAR *) = NULL;
53 static BOOL (* m_fpDrvFetch)(DB_ASYNC_RESULT) = NULL;
54 static LONG (* m_fpDrvGetFieldLength)(DB_RESULT, int, int) = NULL;
55 static LONG (* m_fpDrvGetFieldLengthAsync)(DB_RESULT, int) = NULL;
56 static WCHAR* (* m_fpDrvGetField)(DB_RESULT, int, int, WCHAR *, int) = NULL;
57 static WCHAR* (* m_fpDrvGetFieldAsync)(DB_ASYNC_RESULT, int, WCHAR *, int) = NULL;
58 static int (* m_fpDrvGetNumRows)(DB_RESULT) = NULL;
59 static void (* m_fpDrvFreeResult)(DB_RESULT) = NULL;
60 static void (* m_fpDrvFreeAsyncResult)(DB_ASYNC_RESULT) = NULL;
61 static DWORD (* m_fpDrvBegin)(DB_CONNECTION) = NULL;
62 static DWORD (* m_fpDrvCommit)(DB_CONNECTION) = NULL;
63 static DWORD (* m_fpDrvRollback)(DB_CONNECTION) = NULL;
64 static void (* m_fpDrvUnload)(void) = NULL;
65 static void (* m_fpEventHandler)(DWORD, const TCHAR *, const TCHAR *);
66 static int (* m_fpDrvGetColumnCount)(DB_RESULT);
67 static const char* (* m_fpDrvGetColumnName)(DB_RESULT, int);
68 static int (* m_fpDrvGetColumnCountAsync)(DB_ASYNC_RESULT);
69 static const char* (* m_fpDrvGetColumnNameAsync)(DB_ASYNC_RESULT, int);
70
71
72 //
73 // Get symbol address and log errors
74 //
75
76 static void *DLGetSymbolAddrEx(HMODULE hModule, const TCHAR *pszSymbol)
77 {
78 void *pFunc;
79 char szErrorText[256];
80
81 pFunc = DLGetSymbolAddr(hModule, pszSymbol, szErrorText);
82 if ((pFunc == NULL) && m_bnxlog_write)
83 nxlog_write(MSG_DLSYM_FAILED, EVENTLOG_WARNING_TYPE, "ss", pszSymbol, szErrorText);
84 return pFunc;
85 }
86
87
88 //
89 // Load and initialize database driver
90 //
91
92 BOOL LIBNXSRV_EXPORTABLE DBInit(BOOL bnxlog_write, BOOL bLogErrors, BOOL bDumpSQL,
93 void (* fpEventHandler)(DWORD, const TCHAR *, const TCHAR *))
94 {
95 BOOL (* fpDrvInit)(char *);
96 DWORD *pdwAPIVersion;
97 char szErrorText[256];
98 static DWORD dwVersionZero = 0;
99
100 m_bnxlog_write = bnxlog_write;
101 m_bLogSQLErrors = bLogErrors && bnxlog_write;
102 m_bDumpSQL = bDumpSQL;
103 m_fpEventHandler = fpEventHandler;
104 m_nReconnect = 0;
105 m_mutexReconnect = MutexCreate();
106
107 // Load driver's module
108 m_hDriver = DLOpen(g_szDbDriver, szErrorText);
109 if (m_hDriver == NULL)
110 {
111 if (m_bnxlog_write)
112 nxlog_write(MSG_DLOPEN_FAILED, EVENTLOG_ERROR_TYPE, "ss", g_szDbDriver, szErrorText);
113 return FALSE;
114 }
115
116 // Check API version supported by driver
117 pdwAPIVersion = (DWORD *)DLGetSymbolAddr(m_hDriver, "drvAPIVersion", NULL);
118 if (pdwAPIVersion == NULL)
119 pdwAPIVersion = &dwVersionZero;
120 if (*pdwAPIVersion != DBDRV_API_VERSION)
121 {
122 if (m_bnxlog_write)
123 nxlog_write(MSG_DBDRV_API_VERSION_MISMATCH, EVENTLOG_ERROR_TYPE, "sdd",
124 g_szDbDriver, DBDRV_API_VERSION, *pdwAPIVersion);
125 DLClose(m_hDriver);
126 m_hDriver = NULL;
127 return FALSE;
128 }
129
130 // Import symbols
131 fpDrvInit = (BOOL (*)(char *))DLGetSymbolAddrEx(m_hDriver, "DrvInit");
132 m_fpDrvConnect = (DB_CONNECTION (*)(const char *, const char *, const char *, const char *))DLGetSymbolAddrEx(m_hDriver, "DrvConnect");
133 m_fpDrvDisconnect = (void (*)(DB_CONNECTION))DLGetSymbolAddrEx(m_hDriver, "DrvDisconnect");
134 m_fpDrvQuery = (DWORD (*)(DB_CONNECTION, WCHAR *, TCHAR *))DLGetSymbolAddrEx(m_hDriver, "DrvQuery");
135 m_fpDrvSelect = (DB_RESULT (*)(DB_CONNECTION, WCHAR *, DWORD *, TCHAR *))DLGetSymbolAddrEx(m_hDriver, "DrvSelect");
136 m_fpDrvAsyncSelect = (DB_ASYNC_RESULT (*)(DB_CONNECTION, WCHAR *, DWORD *, TCHAR *))DLGetSymbolAddrEx(m_hDriver, "DrvAsyncSelect");
137 m_fpDrvFetch = (BOOL (*)(DB_ASYNC_RESULT))DLGetSymbolAddrEx(m_hDriver, "DrvFetch");
138 m_fpDrvGetFieldLength = (LONG (*)(DB_RESULT, int, int))DLGetSymbolAddrEx(m_hDriver, "DrvGetFieldLength");
139 m_fpDrvGetFieldLengthAsync = (LONG (*)(DB_RESULT, int))DLGetSymbolAddrEx(m_hDriver, "DrvGetFieldLengthAsync");
140 m_fpDrvGetField = (WCHAR* (*)(DB_RESULT, int, int, WCHAR *, int))DLGetSymbolAddrEx(m_hDriver, "DrvGetField");
141 m_fpDrvGetFieldAsync = (WCHAR* (*)(DB_ASYNC_RESULT, int, WCHAR *, int))DLGetSymbolAddrEx(m_hDriver, "DrvGetFieldAsync");
142 m_fpDrvGetNumRows = (int (*)(DB_RESULT))DLGetSymbolAddrEx(m_hDriver, "DrvGetNumRows");
143 m_fpDrvGetColumnCount = (int (*)(DB_RESULT))DLGetSymbolAddrEx(m_hDriver, "DrvGetColumnCount");
144 m_fpDrvGetColumnName = (const char* (*)(DB_RESULT, int))DLGetSymbolAddrEx(m_hDriver, "DrvGetColumnName");
145 m_fpDrvGetColumnCountAsync = (int (*)(DB_ASYNC_RESULT))DLGetSymbolAddrEx(m_hDriver, "DrvGetColumnCountAsync");
146 m_fpDrvGetColumnNameAsync = (const char* (*)(DB_ASYNC_RESULT, int))DLGetSymbolAddrEx(m_hDriver, "DrvGetColumnNameAsync");
147 m_fpDrvFreeResult = (void (*)(DB_RESULT))DLGetSymbolAddrEx(m_hDriver, "DrvFreeResult");
148 m_fpDrvFreeAsyncResult = (void (*)(DB_ASYNC_RESULT))DLGetSymbolAddrEx(m_hDriver, "DrvFreeAsyncResult");
149 m_fpDrvBegin = (DWORD (*)(DB_CONNECTION))DLGetSymbolAddrEx(m_hDriver, "DrvBegin");
150 m_fpDrvCommit = (DWORD (*)(DB_CONNECTION))DLGetSymbolAddrEx(m_hDriver, "DrvCommit");
151 m_fpDrvRollback = (DWORD (*)(DB_CONNECTION))DLGetSymbolAddrEx(m_hDriver, "DrvRollback");
152 m_fpDrvUnload = (void (*)(void))DLGetSymbolAddrEx(m_hDriver, "DrvUnload");
153 if ((fpDrvInit == NULL) || (m_fpDrvConnect == NULL) || (m_fpDrvDisconnect == NULL) ||
154 (m_fpDrvQuery == NULL) || (m_fpDrvSelect == NULL) || (m_fpDrvGetField == NULL) ||
155 (m_fpDrvGetNumRows == NULL) || (m_fpDrvFreeResult == NULL) ||
156 (m_fpDrvUnload == NULL) || (m_fpDrvAsyncSelect == NULL) || (m_fpDrvFetch == NULL) ||
157 (m_fpDrvFreeAsyncResult == NULL) || (m_fpDrvGetFieldAsync == NULL) ||
158 (m_fpDrvBegin == NULL) || (m_fpDrvCommit == NULL) || (m_fpDrvRollback == NULL) ||
159 (m_fpDrvGetColumnCount == NULL) || (m_fpDrvGetColumnName == NULL) ||
160 (m_fpDrvGetColumnCountAsync == NULL) || (m_fpDrvGetColumnNameAsync == NULL) ||
161 (m_fpDrvGetFieldLength == NULL) || (m_fpDrvGetFieldLengthAsync == NULL))
162 {
163 if (m_bnxlog_write)
164 nxlog_write(MSG_DBDRV_NO_ENTRY_POINTS, EVENTLOG_ERROR_TYPE, "s", g_szDbDriver);
165 DLClose(m_hDriver);
166 m_hDriver = NULL;
167 return FALSE;
168 }
169
170 // Initialize driver
171 if (!fpDrvInit(g_szDbDrvParams))
172 {
173 if (m_bnxlog_write)
174 nxlog_write(MSG_DBDRV_INIT_FAILED, EVENTLOG_ERROR_TYPE, "s", g_szDbDriver);
175 DLClose(m_hDriver);
176 m_hDriver = NULL;
177 return FALSE;
178 }
179
180 // Success
181 if (m_bnxlog_write)
182 nxlog_write(MSG_DBDRV_LOADED, EVENTLOG_INFORMATION_TYPE, "s", g_szDbDriver);
183 return TRUE;
184 }
185
186
187 //
188 // Notify driver of unload
189 //
190
191 void LIBNXSRV_EXPORTABLE DBUnloadDriver(void)
192 {
193 m_fpDrvUnload();
194 DLClose(m_hDriver);
195 MutexDestroy(m_mutexReconnect);
196 }
197
198
199 //
200 // Connect to database
201 //
202
203 DB_HANDLE LIBNXSRV_EXPORTABLE DBConnect(void)
204 {
205 return DBConnectEx(g_szDbServer, g_szDbName, g_szDbLogin, g_szDbPassword);
206 }
207
208
209 //
210 // Connect to database using provided parameters
211 //
212
213 DB_HANDLE LIBNXSRV_EXPORTABLE DBConnectEx(const TCHAR *pszServer, const TCHAR *pszDBName,
214 const TCHAR *pszLogin, const TCHAR *pszPassword)
215 {
216 DB_CONNECTION hDrvConn;
217 DB_HANDLE hConn = NULL;
218
219 hDrvConn = m_fpDrvConnect(pszServer, pszLogin, pszPassword, pszDBName);
220 if (hDrvConn != NULL)
221 {
222 hConn = (DB_HANDLE)malloc(sizeof(struct db_handle_t));
223 if (hConn != NULL)
224 {
225 hConn->hConn = hDrvConn;
226 hConn->mutexTransLock = MutexCreateRecursive();
227 hConn->nTransactionLevel = 0;
228 hConn->pszDBName = (pszDBName == NULL) ? NULL : _tcsdup(pszDBName);
229 hConn->pszLogin = (pszLogin == NULL) ? NULL : _tcsdup(pszLogin);
230 hConn->pszPassword = (pszPassword == NULL) ? NULL : _tcsdup(pszPassword);
231 hConn->pszServer = (pszServer == NULL) ? NULL : _tcsdup(pszServer);
232 DbgPrintf(4, _T("New DB connection opened: handle=%p"), hConn);
233 }
234 else
235 {
236 m_fpDrvDisconnect(hDrvConn);
237 }
238 }
239 return hConn;
240 }
241
242
243 //
244 // Disconnect from database
245 //
246
247 void LIBNXSRV_EXPORTABLE DBDisconnect(DB_HANDLE hConn)
248 {
249 if (hConn == NULL)
250 return;
251
252 DbgPrintf(4, _T("DB connection %p closed"), hConn);
253
254 m_fpDrvDisconnect(hConn->hConn);
255 MutexDestroy(hConn->mutexTransLock);
256 safe_free(hConn->pszDBName);
257 safe_free(hConn->pszLogin);
258 safe_free(hConn->pszPassword);
259 safe_free(hConn->pszServer);
260 free(hConn);
261 }
262
263
264 //
265 // Reconnect to database
266 //
267
268 static void DBReconnect(DB_HANDLE hConn)
269 {
270 int nCount;
271
272 m_fpDrvDisconnect(hConn->hConn);
273 for(nCount = 0; ; nCount++)
274 {
275 hConn->hConn = m_fpDrvConnect(hConn->pszServer, hConn->pszLogin,
276 hConn->pszPassword, hConn->pszDBName);
277 if (hConn->hConn != NULL)
278 break;
279 if (nCount == 0)
280 {
281 MutexLock(m_mutexReconnect, INFINITE);
282 if ((m_nReconnect == 0) && (m_fpEventHandler != NULL))
283 m_fpEventHandler(DBEVENT_CONNECTION_LOST, NULL, NULL);
284 m_nReconnect++;
285 MutexUnlock(m_mutexReconnect);
286 }
287 ThreadSleepMs(1000);
288 }
289 if (nCount > 0)
290 {
291 MutexLock(m_mutexReconnect, INFINITE);
292 m_nReconnect--;
293 if ((m_nReconnect == 0) && (m_fpEventHandler != NULL))
294 m_fpEventHandler(DBEVENT_CONNECTION_RESTORED, NULL, NULL);
295 MutexUnlock(m_mutexReconnect);
296 }
297 }
298
299
300 //
301 // Perform a non-SELECT SQL query
302 //
303
304 BOOL LIBNXSRV_EXPORTABLE DBQueryEx(DB_HANDLE hConn, const TCHAR *szQuery, TCHAR *errorText)
305 {
306 DWORD dwResult;
307 INT64 ms;
308 #ifdef UNICODE
309 #define pwszQuery szQuery
310 #else
311 WCHAR *pwszQuery = WideStringFromMBString(szQuery);
312 #endif
313
314 MutexLock(hConn->mutexTransLock, INFINITE);
315 if (m_bDumpSQL)
316 ms = GetCurrentTimeMs();
317
318 dwResult = m_fpDrvQuery(hConn->hConn, pwszQuery, errorText);
319 if (dwResult == DBERR_CONNECTION_LOST)
320 {
321 DBReconnect(hConn);
322 dwResult = m_fpDrvQuery(hConn->hConn, pwszQuery, errorText);
323 }
324
325 #ifndef UNICODE
326 free(pwszQuery);
327 #endif
328
329 if (m_bDumpSQL)
330 {
331 ms = GetCurrentTimeMs() - ms;
332 DbgPrintf(9, _T("%s sync query: \"%s\" [%d ms]"), (dwResult == DBERR_SUCCESS) ? _T("Successful") : _T("Failed"), szQuery, ms);
333 }
334
335 MutexUnlock(hConn->mutexTransLock);
336 if (dwResult != DBERR_SUCCESS)
337 {
338 if (m_bLogSQLErrors)
339 nxlog_write(MSG_SQL_ERROR, EVENTLOG_ERROR_TYPE, "ss", szQuery, errorText);
340 if (m_fpEventHandler != NULL)
341 m_fpEventHandler(DBEVENT_QUERY_FAILED, szQuery, errorText);
342 }
343 return dwResult == DBERR_SUCCESS;
344 #undef pwszQuery
345 }
346
347 BOOL LIBNXSRV_EXPORTABLE DBQuery(DB_HANDLE hConn, const TCHAR *query)
348 {
349 TCHAR errorText[DBDRV_MAX_ERROR_TEXT];
350
351 return DBQueryEx(hConn, query, errorText);
352 }
353
354
355 //
356 // Perform SELECT query
357 //
358
359 DB_RESULT LIBNXSRV_EXPORTABLE DBSelectEx(DB_HANDLE hConn, const TCHAR *szQuery, TCHAR *errorText)
360 {
361 DB_RESULT hResult;
362 DWORD dwError;
363 INT64 ms;
364 #ifdef UNICODE
365 #define pwszQuery szQuery
366 #else
367 WCHAR *pwszQuery = WideStringFromMBString(szQuery);
368 #endif
369
370 MutexLock(hConn->mutexTransLock, INFINITE);
371 if (m_bDumpSQL)
372 ms = GetCurrentTimeMs();
373 hResult = m_fpDrvSelect(hConn->hConn, pwszQuery, &dwError, errorText);
374 if ((hResult == NULL) && (dwError == DBERR_CONNECTION_LOST))
375 {
376 DBReconnect(hConn);
377 hResult = m_fpDrvSelect(hConn->hConn, pwszQuery, &dwError, errorText);
378 }
379
380 #ifndef UNICODE
381 free(pwszQuery);
382 #endif
383
384 if (m_bDumpSQL)
385 {
386 ms = GetCurrentTimeMs() - ms;
387 DbgPrintf(9, _T("%s sync query: \"%s\" [%d ms]"), (hResult != NULL) ? _T("Successful") : _T("Failed"), szQuery, (DWORD)ms);
388 }
389 MutexUnlock(hConn->mutexTransLock);
390 if (hResult == NULL)
391 {
392 if (m_bLogSQLErrors)
393 nxlog_write(MSG_SQL_ERROR, EVENTLOG_ERROR_TYPE, "ss", szQuery, errorText);
394 if (m_fpEventHandler != NULL)
395 m_fpEventHandler(DBEVENT_QUERY_FAILED, szQuery, errorText);
396 }
397 return hResult;
398 }
399
400 DB_RESULT LIBNXSRV_EXPORTABLE DBSelect(DB_HANDLE hConn, const TCHAR *query)
401 {
402 TCHAR errorText[DBDRV_MAX_ERROR_TEXT];
403
404 return DBSelectEx(hConn, query, errorText);
405 }
406
407
408 //
409 // Get number of columns
410 //
411
412 int LIBNXSRV_EXPORTABLE DBGetColumnCount(DB_RESULT hResult)
413 {
414 return m_fpDrvGetColumnCount(hResult);
415 }
416
417
418 //
419 // Get column name
420 //
421
422 BOOL LIBNXSRV_EXPORTABLE DBGetColumnName(DB_RESULT hResult, int column, TCHAR *buffer, int bufSize)
423 {
424 const char *name;
425
426 name = m_fpDrvGetColumnName(hResult, column);
427 if (name != NULL)
428 {
429 #ifdef UNICODE
430 MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, name, -1, buffer, bufSize);
431 buffer[bufSize - 1] = 0;
432 #else
433 nx_strncpy(buffer, name, bufSize);
434 #endif
435 }
436 return name != NULL;
437 }
438
439
440 //
441 // Get column count for async request
442 //
443
444 int LIBNXSRV_EXPORTABLE DBGetColumnCountAsync(DB_ASYNC_RESULT hResult)
445 {
446 return m_fpDrvGetColumnCountAsync(hResult);
447 }
448
449
450 //
451 // Get column name for async request
452 //
453
454 BOOL LIBNXSRV_EXPORTABLE DBGetColumnNameAsync(DB_ASYNC_RESULT hResult, int column, TCHAR *buffer, int bufSize)
455 {
456 const char *name;
457
458 name = m_fpDrvGetColumnNameAsync(hResult, column);
459 if (name != NULL)
460 {
461 #ifdef UNICODE
462 MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, name, -1, buffer, bufSize);
463 buffer[bufSize - 1] = 0;
464 #else
465 nx_strncpy(buffer, name, bufSize);
466 #endif
467 }
468 return name != NULL;
469 }
470
471
472 //
473 // Get field's value
474 //
475
476 TCHAR LIBNXSRV_EXPORTABLE *DBGetField(DB_RESULT hResult, int iRow, int iColumn,
477 TCHAR *pszBuffer, int nBufLen)
478 {
479 #ifdef UNICODE
480 if (pszBuffer != NULL)
481 {
482 return m_fpDrvGetField(hResult, iRow, iColumn, pszBuffer, nBufLen);
483 }
484 else
485 {
486 LONG nLen;
487 WCHAR *pszTemp;
488
489 nLen = m_fpDrvGetFieldLength(hResult, iRow, iColumn);
490 if (nLen == -1)
491 {
492 pszTemp = NULL;
493 }
494 else
495 {
496 nLen++;
497 pszBuffer = (WCHAR *)malloc(nLen * sizeof(WCHAR));
498 m_fpDrvGetField(hResult, iRow, iColumn, pszTemp, nLen);
499 }
500 return pszTemp;
501 }
502 #else
503 WCHAR *pwszData, *pwszBuffer;
504 char *pszRet;
505 int nLen;
506
507 if (pszBuffer != NULL)
508 {
509 pwszBuffer = (WCHAR *)malloc(nBufLen * sizeof(WCHAR));
510 pwszData = m_fpDrvGetField(hResult, iRow, iColumn, pwszBuffer, nBufLen);
511 if (pwszData != NULL)
512 {
513 WideCharToMultiByte(CP_ACP, WC_COMPOSITECHECK | WC_DEFAULTCHAR,
514 pwszData, -1, pszBuffer, nBufLen, NULL, NULL);
515 pszRet = pszBuffer;
516 }
517 else
518 {
519 pszRet = NULL;
520 }
521 free(pwszBuffer);
522 }
523 else
524 {
525 nLen = m_fpDrvGetFieldLength(hResult, iRow, iColumn);
526 if (nLen == -1)
527 {
528 pszRet = NULL;
529 }
530 else
531 {
532 nLen++;
533 pwszBuffer = (WCHAR *)malloc(nLen * sizeof(WCHAR));
534 pwszData = m_fpDrvGetField(hResult, iRow, iColumn, pwszBuffer, nLen);
535 if (pwszData != NULL)
536 {
537 nLen = (int)wcslen(pwszData) + 1;
538 pszRet = (char *)malloc(nLen);
539 WideCharToMultiByte(CP_ACP, WC_COMPOSITECHECK | WC_DEFAULTCHAR,
540 pwszData, -1, pszRet, nLen, NULL, NULL);
541 }
542 else
543 {
544 pszRet = NULL;
545 }
546 free(pwszBuffer);
547 }
548 }
549 return pszRet;
550 #endif
551 }
552
553
554 //
555 // Get field's value as unsigned long
556 //
557
558 DWORD LIBNXSRV_EXPORTABLE DBGetFieldULong(DB_RESULT hResult, int iRow, int iColumn)
559 {
560 LONG iVal;
561 DWORD dwVal;
562 TCHAR *pszVal, szBuffer[256];
563
564 pszVal = DBGetField(hResult, iRow, iColumn, szBuffer, 256);
565 if (pszVal == NULL)
566 return 0;
567 iVal = _tcstol(pszVal, NULL, 10);
568 memcpy(&dwVal, &iVal, sizeof(LONG)); // To prevent possible conversion
569 return dwVal;
570 }
571
572
573 //
574 // Get field's value as unsigned 64-bit int
575 //
576
577 QWORD LIBNXSRV_EXPORTABLE DBGetFieldUInt64(DB_RESULT hResult, int iRow, int iColumn)
578 {
579 INT64 iVal;
580 QWORD qwVal;
581 TCHAR *pszVal, szBuffer[256];
582
583 pszVal = DBGetField(hResult, iRow, iColumn, szBuffer, 256);
584 if (pszVal == NULL)
585 return 0;
586 iVal = _tcstoll(pszVal, NULL, 10);
587 memcpy(&qwVal, &iVal, sizeof(INT64)); // To prevent possible conversion
588 return qwVal;
589 }
590
591
592 //
593 // Get field's value as signed long
594 //
595
596 LONG LIBNXSRV_EXPORTABLE DBGetFieldLong(DB_RESULT hResult, int iRow, int iColumn)
597 {
598 TCHAR *pszVal, szBuffer[256];
599
600 pszVal = DBGetField(hResult, iRow, iColumn, szBuffer, 256);
601 return pszVal == NULL ? 0 : _tcstol(pszVal, NULL, 10);
602 }
603
604
605 //
606 // Get field's value as signed 64-bit int
607 //
608
609 INT64 LIBNXSRV_EXPORTABLE DBGetFieldInt64(DB_RESULT hResult, int iRow, int iColumn)
610 {
611 TCHAR *pszVal, szBuffer[256];
612
613 pszVal = DBGetField(hResult, iRow, iColumn, szBuffer, 256);
614 return pszVal == NULL ? 0 : _tcstoll(pszVal, NULL, 10);
615 }
616
617
618 //
619 // Get field's value as double
620 //
621
622 double LIBNXSRV_EXPORTABLE DBGetFieldDouble(DB_RESULT hResult, int iRow, int iColumn)
623 {
624 TCHAR *pszVal, szBuffer[256];
625
626 pszVal = DBGetField(hResult, iRow, iColumn, szBuffer, 256);
627 return pszVal == NULL ? 0 : _tcstod(pszVal, NULL);
628 }
629
630
631 //
632 // Get field's value as IP address
633 //
634
635 DWORD LIBNXSRV_EXPORTABLE DBGetFieldIPAddr(DB_RESULT hResult, int iRow, int iColumn)
636 {
637 TCHAR *pszVal, szBuffer[256];
638
639 pszVal = DBGetField(hResult, iRow, iColumn, szBuffer, 256);
640 return pszVal == NULL ? INADDR_NONE : ntohl(_t_inet_addr(pszVal));
641 }
642
643
644 //
645 // Get field's value as integer array from byte array encoded in hex
646 //
647
648 BOOL LIBNXSRV_EXPORTABLE DBGetFieldByteArray(DB_RESULT hResult, int iRow, int iColumn,
649 int *pnArray, int nSize, int nDefault)
650 {
651 char pbBytes[128];
652 BOOL bResult;
653 int i, nLen;
654 TCHAR *pszVal, szBuffer[256];
655
656 pszVal = DBGetField(hResult, iRow, iColumn, szBuffer, 256);
657 if (pszVal != NULL)
658 {
659 StrToBin(pszVal, (BYTE *)pbBytes, 128);
660 nLen = (int)strlen(pszVal) / 2;
661 for(i = 0; (i < nSize) && (i < nLen); i++)
662 pnArray[i] = pbBytes[i];
663 for(; i < nSize; i++)
664 pnArray[i] = nDefault;
665 bResult = TRUE;
666 }
667 else
668 {
669 for(i = 0; i < nSize; i++)
670 pnArray[i] = nDefault;
671 bResult = FALSE;
672 }
673 return bResult;
674 }
675
676
677 //
678 // Get field's value as GUID
679 //
680
681 BOOL LIBNXSRV_EXPORTABLE DBGetFieldGUID(DB_RESULT hResult, int iRow,
682 int iColumn, uuid_t guid)
683 {
684 TCHAR *pszVal, szBuffer[256];
685 BOOL bResult;
686
687 pszVal = DBGetField(hResult, iRow, iColumn, szBuffer, 256);
688 if (pszVal != NULL)
689 {
690 if (uuid_parse(pszVal, guid) == 0)
691 {
692 bResult = TRUE;
693 }
694 else
695 {
696 uuid_clear(guid);
697 bResult = FALSE;
698 }
699 }
700 else
701 {
702 uuid_clear(guid);
703 bResult = FALSE;
704 }
705 return bResult;
706 }
707
708
709 //
710 // Get number of rows in result
711 //
712
713 int LIBNXSRV_EXPORTABLE DBGetNumRows(DB_RESULT hResult)
714 {
715 if (hResult == NULL)
716 return 0;
717 return m_fpDrvGetNumRows(hResult);
718 }
719
720
721 //
722 // Free result
723 //
724
725 void LIBNXSRV_EXPORTABLE DBFreeResult(DB_RESULT hResult)
726 {
727 if (hResult != NULL)
728 m_fpDrvFreeResult(hResult);
729 }
730
731
732 //
733 // Asyncronous SELECT query
734 //
735
736 DB_ASYNC_RESULT LIBNXSRV_EXPORTABLE DBAsyncSelectEx(DB_HANDLE hConn, const TCHAR *szQuery, TCHAR *errorText)
737 {
738 DB_RESULT hResult;
739 DWORD dwError;
740 INT64 ms;
741 #ifdef UNICODE
742 #define pwszQuery szQuery
743 #else
744 WCHAR *pwszQuery = WideStringFromMBString(szQuery);
745 #endif
746
747 MutexLock(hConn->mutexTransLock, INFINITE);
748 if (m_bDumpSQL)
749 ms = GetCurrentTimeMs();
750 hResult = m_fpDrvAsyncSelect(hConn->hConn, pwszQuery, &dwError, errorText);
751 if ((hResult == NULL) && (dwError == DBERR_CONNECTION_LOST))
752 {
753 DBReconnect(hConn);
754 hResult = m_fpDrvAsyncSelect(hConn->hConn, pwszQuery, &dwError, errorText);
755 }
756
757 #ifndef UNICODE
758 free(pwszQuery);
759 #endif
760
761 if (m_bDumpSQL)
762 {
763 ms = GetCurrentTimeMs() - ms;
764 DbgPrintf(9, _T("%s async query: \"%s\" [%d ms]"), (hResult != NULL) ? _T("Successful") : _T("Failed"), szQuery, (DWORD)ms);
765 }
766 if (hResult == NULL)
767 {
768 MutexUnlock(hConn->mutexTransLock);
769 if (m_bLogSQLErrors)
770 nxlog_write(MSG_SQL_ERROR, EVENTLOG_ERROR_TYPE, _T("ss"), szQuery, errorText);
771 if (m_fpEventHandler != NULL)
772 m_fpEventHandler(DBEVENT_QUERY_FAILED, szQuery, errorText);
773 }
774 return hResult;
775 }
776
777 DB_ASYNC_RESULT LIBNXSRV_EXPORTABLE DBAsyncSelect(DB_HANDLE hConn, const TCHAR *query)
778 {
779 TCHAR errorText[DBDRV_MAX_ERROR_TEXT];
780
781 return DBAsyncSelectEx(hConn, query, errorText);
782 }
783
784
785 //
786 // Fetch next row from asynchronous SELECT result
787 //
788
789 BOOL LIBNXSRV_EXPORTABLE DBFetch(DB_ASYNC_RESULT hResult)
790 {
791 return m_fpDrvFetch(hResult);
792 }
793
794
795 //
796 // Get field's value from asynchronous SELECT result
797 //
798
799 TCHAR LIBNXSRV_EXPORTABLE *DBGetFieldAsync(DB_ASYNC_RESULT hResult, int iColumn, TCHAR *pBuffer, int iBufSize)
800 {
801 #ifdef UNICODE
802 if (pBuffer != NULL)
803 {
804 return m_fpDrvGetFieldAsync(hResult, iColumn, pBuffer, iBufSize);
805 }
806 else
807 {
808 LONG nLen;
809 WCHAR *pszTemp;
810
811 nLen = m_fpDrvGetFieldLengthAsync(hResult, iColumn);
812 if (nLen == -1)
813 {
814 pszTemp = NULL;
815 }
816 else
817 {
818 nLen++;
819 pszBuffer = (WCHAR *)malloc(nLen * sizeof(WCHAR));
820 m_fpDrvGetFieldAsync(hResult, iColumn, pszTemp, nLen);
821 }
822 return pszTemp;
823 }
824 #else
825 WCHAR *pwszData, *pwszBuffer;
826 char *pszRet;
827 int nLen;
828
829 if (pBuffer != NULL)
830 {
831 pwszBuffer = (WCHAR *)malloc(iBufSize * sizeof(WCHAR));
832 if (m_fpDrvGetFieldAsync(hResult, iColumn, pwszBuffer, iBufSize) != NULL)
833 {
834 WideCharToMultiByte(CP_ACP, WC_COMPOSITECHECK | WC_DEFAULTCHAR,
835 pwszBuffer, -1, pBuffer, iBufSize, NULL, NULL);
836 pszRet = pBuffer;
837 }
838 else
839 {
840 pszRet = NULL;
841 }
842 free(pwszBuffer);
843 }
844 else
845 {
846 nLen = m_fpDrvGetFieldLengthAsync(hResult, iColumn);
847 if (nLen == -1)
848 {
849 pszRet = NULL;
850 }
851 else
852 {
853 nLen++;
854 pwszBuffer = (WCHAR *)malloc(nLen * sizeof(WCHAR));
855 pwszData = m_fpDrvGetFieldAsync(hResult, iColumn, pwszBuffer, nLen);
856 if (pwszData != NULL)
857 {
858 nLen = (int)wcslen(pwszData) + 1;
859 pszRet = (char *)malloc(nLen);
860 WideCharToMultiByte(CP_ACP, WC_COMPOSITECHECK | WC_DEFAULTCHAR,
861 pwszData, -1, pszRet, nLen, NULL, NULL);
862 }
863 else
864 {
865 pszRet = NULL;
866 }
867 free(pwszBuffer);
868 }
869 }
870 return pszRet;
871 #endif
872 }
873
874
875 //
876 // Get field's value as unsigned long from asynchronous SELECT result
877 //
878
879 DWORD LIBNXSRV_EXPORTABLE DBGetFieldAsyncULong(DB_ASYNC_RESULT hResult, int iColumn)
880 {
881 LONG iVal;
882 DWORD dwVal;
883 TCHAR szBuffer[64];
884
885 if (DBGetFieldAsync(hResult, iColumn, szBuffer, 64) == NULL)
886 return 0;
887 iVal = _tcstol(szBuffer, NULL, 10);
888 memcpy(&dwVal, &iVal, sizeof(LONG)); // To prevent possible conversion
889 return dwVal;
890 }
891
892
893 //
894 // Get field's value as unsigned 64-bit int from asynchronous SELECT result
895 //
896
897 QWORD LIBNXSRV_EXPORTABLE DBGetFieldAsyncUInt64(DB_ASYNC_RESULT hResult, int iColumn)
898 {
899 INT64 iVal;
900 QWORD qwVal;
901 TCHAR szBuffer[64];
902
903 if (DBGetFieldAsync(hResult, iColumn, szBuffer, 64) == NULL)
904 return 0;
905 iVal = _tcstoll(szBuffer, NULL, 10);
906 memcpy(&qwVal, &iVal, sizeof(INT64)); // To prevent possible conversion
907 return qwVal;
908 }
909
910
911 //
912 // Get field's value as signed long from asynchronous SELECT result
913 //
914
915 LONG LIBNXSRV_EXPORTABLE DBGetFieldAsyncLong(DB_RESULT hResult, int iColumn)
916 {
917 TCHAR szBuffer[64];
918
919 return DBGetFieldAsync(hResult, iColumn, szBuffer, 64) == NULL ? 0 : _tcstol(szBuffer, NULL, 10);
920 }
921
922
923 //
924 // Get field's value as signed 64-bit int from asynchronous SELECT result
925 //
926
927 INT64 LIBNXSRV_EXPORTABLE DBGetFieldAsyncInt64(DB_RESULT hResult, int iColumn)
928 {
929 TCHAR szBuffer[64];
930
931 return DBGetFieldAsync(hResult, iColumn, szBuffer, 64) == NULL ? 0 : _tcstoll(szBuffer, NULL, 10);
932 }
933
934
935 //
936 // Get field's value as signed long from asynchronous SELECT result
937 //
938
939 double LIBNXSRV_EXPORTABLE DBGetFieldAsyncDouble(DB_RESULT hResult, int iColumn)
940 {
941 TCHAR szBuffer[64];
942
943 return DBGetFieldAsync(hResult, iColumn, szBuffer, 64) == NULL ? 0 : _tcstod(szBuffer, NULL);
944 }
945
946
947 //
948 // Get field's value as IP address from asynchronous SELECT result
949 //
950
951 DWORD LIBNXSRV_EXPORTABLE DBGetFieldAsyncIPAddr(DB_RESULT hResult, int iColumn)
952 {
953 TCHAR szBuffer[64];
954
955 return DBGetFieldAsync(hResult, iColumn, szBuffer, 64) == NULL ? INADDR_NONE :
956 ntohl(inet_addr(szBuffer));
957 }
958
959
960 //
961 // Free asynchronous SELECT result
962 //
963
964 void LIBNXSRV_EXPORTABLE DBFreeAsyncResult(DB_HANDLE hConn, DB_ASYNC_RESULT hResult)
965 {
966 m_fpDrvFreeAsyncResult(hResult);
967 MutexUnlock(hConn->mutexTransLock);
968 }
969
970
971 //
972 // Begin transaction
973 //
974
975 BOOL LIBNXSRV_EXPORTABLE DBBegin(DB_HANDLE hConn)
976 {
977 DWORD dwResult;
978 BOOL bRet = FALSE;
979
980 MutexLock(hConn->mutexTransLock, INFINITE);
981 if (hConn->nTransactionLevel == 0)
982 {
983 dwResult = m_fpDrvBegin(hConn->hConn);
984 if (dwResult == DBERR_CONNECTION_LOST)
985 {
986 DBReconnect(hConn);
987 dwResult = m_fpDrvBegin(hConn->hConn);
988 }
989 if (dwResult == DBERR_SUCCESS)
990 {
991 hConn->nTransactionLevel++;
992 bRet = TRUE;
993 }
994 else
995 {
996 MutexUnlock(hConn->mutexTransLock);
997 }
998 }
999 else
1000 {
1001 hConn->nTransactionLevel++;
1002 bRet = TRUE;
1003 }
1004 return bRet;
1005 }
1006
1007
1008 //
1009 // Commit transaction
1010 //
1011
1012 BOOL LIBNXSRV_EXPORTABLE DBCommit(DB_HANDLE hConn)
1013 {
1014 BOOL bRet = FALSE;
1015
1016 MutexLock(hConn->mutexTransLock, INFINITE);
1017 if (hConn->nTransactionLevel > 0)
1018 {
1019 hConn->nTransactionLevel--;
1020 if (hConn->nTransactionLevel == 0)
1021 bRet = (m_fpDrvCommit(hConn->hConn) == DBERR_SUCCESS);
1022 else
1023 bRet = TRUE;
1024 MutexUnlock(hConn->mutexTransLock);
1025 }
1026 MutexUnlock(hConn->mutexTransLock);
1027 return bRet;
1028 }
1029
1030
1031 //
1032 // Begin transaction
1033 //
1034
1035 BOOL LIBNXSRV_EXPORTABLE DBRollback(DB_HANDLE hConn)
1036 {
1037 BOOL bRet = FALSE;
1038
1039 MutexLock(hConn->mutexTransLock, INFINITE);
1040 if (hConn->nTransactionLevel > 0)
1041 {
1042 hConn->nTransactionLevel--;
1043 if (hConn->nTransactionLevel == 0)
1044 bRet = (m_fpDrvRollback(hConn->hConn) == DBERR_SUCCESS);
1045 else
1046 bRet = TRUE;
1047 MutexUnlock(hConn->mutexTransLock);
1048 }
1049 MutexUnlock(hConn->mutexTransLock);
1050 return bRet;
1051 }
1052
1053
1054 //
1055 // Characters to be escaped before writing to SQL
1056 //
1057
1058 static TCHAR m_szSpecialChars[] = _T("\x01\x02\x03\x04\x05\x06\x07\x08")
1059 _T("\x09\x0A\x0B\x0C\x0D\x0E\x0F\x10")
1060 _T("\x11\x12\x13\x14\x15\x16\x17\x18")
1061 _T("\x19\x1A\x1B\x1C\x1D\x1E\x1F")
1062 _T("#%\"\\'\x7F");
1063
1064
1065 //
1066 // Escape some special characters in string for writing into database
1067 //
1068
1069 TCHAR LIBNXSRV_EXPORTABLE *EncodeSQLString(const TCHAR *pszIn)
1070 {
1071 char *pszOut;
1072 int iPosIn, iPosOut, iStrSize;
1073
1074 if ((pszIn != NULL) && (*pszIn != 0))
1075 {
1076 // Allocate destination buffer
1077 iStrSize = (int)_tcslen(pszIn) + 1;
1078 for(iPosIn = 0; pszIn[iPosIn] != 0; iPosIn++)
1079 if (_tcschr(m_szSpecialChars, pszIn[iPosIn]) != NULL)
1080 iStrSize += 2;
1081 pszOut = (char *)malloc(iStrSize);
1082
1083 // Translate string
1084 for(iPosIn = 0, iPosOut = 0; pszIn[iPosIn] != 0; iPosIn++)
1085 if (strchr(m_szSpecialChars, pszIn[iPosIn]) != NULL)
1086 {
1087 pszOut[iPosOut++] = _T('#');
1088 pszOut[iPosOut++] = bin2hex(pszIn[iPosIn] >> 4);
1089 pszOut[iPosOut++] = bin2hex(pszIn[iPosIn] & 0x0F);
1090 }
1091 else
1092 {
1093 pszOut[iPosOut++] = pszIn[iPosIn];
1094 }
1095 pszOut[iPosOut] = 0;
1096 }
1097 else
1098 {
1099 // Encode empty strings as #00
1100 pszOut = (TCHAR *)malloc(4 * sizeof(TCHAR));
1101 _tcscpy(pszOut, _T("#00"));
1102 }
1103 return pszOut;
1104 }
1105
1106
1107 //
1108 // Restore characters encoded by EncodeSQLString()
1109 // Characters are decoded "in place"
1110 //
1111
1112 void LIBNXSRV_EXPORTABLE DecodeSQLString(TCHAR *pszStr)
1113 {
1114 int iPosIn, iPosOut;
1115
1116 if (pszStr == NULL)
1117 return;
1118
1119 for(iPosIn = 0, iPosOut = 0; pszStr[iPosIn] != 0; iPosIn++)
1120 {
1121 if (pszStr[iPosIn] == _T('#'))
1122 {
1123 iPosIn++;
1124 pszStr[iPosOut] = hex2bin(pszStr[iPosIn]) << 4;
1125 iPosIn++;
1126 pszStr[iPosOut] |= hex2bin(pszStr[iPosIn]);
1127 iPosOut++;
1128 }
1129 else
1130 {
1131 pszStr[iPosOut++] = pszStr[iPosIn];
1132 }
1133 }
1134 pszStr[iPosOut] = 0;
1135 }
1136
1137
1138 //
1139 // Get database schema version
1140 // Will return 0 for unknown and -1 in case of SQL errors
1141 //
1142
1143 int LIBNXSRV_EXPORTABLE DBGetSchemaVersion(DB_HANDLE conn)
1144 {
1145 DB_RESULT hResult;
1146 int version = 0;
1147
1148 // Read schema version from 'metadata' table, where it should
1149 // be stored starting from schema version 87
1150 // We ignore SQL error in this case, because table 'metadata'
1151 // may not exist in old schema versions
1152 hResult = DBSelect(conn, _T("SELECT var_value FROM metadata WHERE var_name='SchemaVersion'"));
1153 if (hResult != NULL)
1154 {
1155 if (DBGetNumRows(hResult) > 0)
1156 version = DBGetFieldLong(hResult, 0, 0);
1157 DBFreeResult(hResult);
1158 }
1159
1160 // If database schema version is less than 87, version number
1161 // will be stored in 'config' table
1162 if (version == 0)
1163 {
1164 hResult = DBSelect(conn, _T("SELECT var_value FROM config WHERE var_name='DBFormatVersion'"));
1165 if (hResult != NULL)
1166 {
1167 if (DBGetNumRows(hResult) > 0)
1168 version = DBGetFieldLong(hResult, 0, 0);
1169 DBFreeResult(hResult);
1170 }
1171 else
1172 {
1173 version = -1;
1174 }
1175 }
1176
1177 return version;
1178 }
1179
1180
1181 //
1182 // Get database syntax
1183 //
1184
1185 int LIBNXSRV_EXPORTABLE DBGetSyntax(DB_HANDLE conn)
1186 {
1187 DB_RESULT hResult;
1188 TCHAR syntaxId[256];
1189 BOOL read = FALSE;
1190 int syntax;
1191
1192 // Get database syntax
1193 hResult = DBSelect(conn, _T("SELECT var_value FROM metadata WHERE var_name='Syntax'"));
1194 if (hResult != NULL)
1195 {
1196 if (DBGetNumRows(hResult) > 0)
1197 {
1198 DBGetField(hResult, 0, 0, syntaxId, sizeof(syntaxId));
1199 read = TRUE;
1200 }
1201 else
1202 {
1203 _tcscpy(syntaxId, _T("UNKNOWN"));
1204 }
1205 DBFreeResult(hResult);
1206 }
1207
1208 // If database schema version is less than 87, syntax
1209 // will be stored in 'config' table, so try it
1210 if (!read)
1211 {
1212 hResult = DBSelect(conn, _T("SELECT var_value FROM config WHERE var_name='DBSyntax'"));
1213 if (hResult != NULL)
1214 {
1215 if (DBGetNumRows(hResult) > 0)
1216 {
1217 DBGetField(hResult, 0, 0, syntaxId, sizeof(syntaxId));
1218 read = TRUE;
1219 }
1220 else
1221 {
1222 _tcscpy(syntaxId, _T("UNKNOWN"));
1223 }
1224 DBFreeResult(hResult);
1225 }
1226 }
1227
1228 if (!_tcscmp(syntaxId, _T("MYSQL")))
1229 {
1230 syntax = DB_SYNTAX_MYSQL;
1231 }
1232 else if (!_tcscmp(syntaxId, _T("PGSQL")))
1233 {
1234 syntax = DB_SYNTAX_PGSQL;
1235 }
1236 else if (!_tcscmp(syntaxId, _T("MSSQL")))
1237 {
1238 syntax = DB_SYNTAX_MSSQL;
1239 }
1240 else if (!_tcscmp(syntaxId, _T("ORACLE")))
1241 {
1242 syntax = DB_SYNTAX_ORACLE;
1243 }
1244 else if (!_tcscmp(syntaxId, _T("SQLITE")))
1245 {
1246 syntax = DB_SYNTAX_SQLITE;
1247 }
1248 else
1249 {
1250 syntax = DB_SYNTAX_UNKNOWN;
1251 }
1252
1253 return syntax;
1254 }