implemented DB driver call DrvGetFieldUnbufferedUTF8 (for databases with native UTF...
[public/netxms.git] / src / db / libnxdb / drivers.cpp
CommitLineData
80e96a82
VK
1/*
2** NetXMS - Network Management System
43526096 3** Copyright (C) 2003-2014 Victor Kirhenshtein
80e96a82
VK
4**
5** This program is free software; you can redistribute it and/or modify
6** it under the terms of the GNU Lesser General Public License as published by
7** the Free Software Foundation; either version 3 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 Lesser 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: drivers.cpp
20**
21**/
22
23#include "libnxdb.h"
24
ef33dc5f
VK
25/**
26 * Log message codes
27 */
58f1f627
VK
28UINT32 g_logMsgCode = 0;
29UINT32 g_sqlErrorMsgCode = 0;
30
31/**
32 * Long-running query threshold
33 */
34UINT32 g_sqlQueryExecTimeThreshold = 0xFFFFFFFF;
80e96a82 35
ef33dc5f
VK
36/**
37 * Loaded drivers
38 */
80e96a82
VK
39static DB_DRIVER s_drivers[MAX_DB_DRIVERS];
40static MUTEX s_driverListLock;
ef33dc5f
VK
41
42/**
43 * Options
44 */
80e96a82
VK
45static bool s_writeLog = false;
46static bool s_logSqlErrors = false;
47
ef33dc5f
VK
48/**
49 * Get symbol address and log errors
50 */
e2bb9f48 51static void *DLGetSymbolAddrEx(HMODULE hModule, const char *pszSymbol, bool mandatory = true)
80e96a82
VK
52{
53 void *pFunc;
54 TCHAR szErrorText[256];
55
56 pFunc = DLGetSymbolAddr(hModule, pszSymbol, szErrorText);
e2bb9f48 57 if ((pFunc == NULL) && mandatory && s_writeLog)
dda7c270 58 __DBWriteLog(EVENTLOG_WARNING_TYPE, _T("Unable to resolve symbol \"%hs\": %s"), pszSymbol, szErrorText);
80e96a82
VK
59 return pFunc;
60}
61
ef33dc5f
VK
62/**
63 * Init DB library
64 */
80e96a82
VK
65bool LIBNXDB_EXPORTABLE DBInit(DWORD logMsgCode, DWORD sqlErrorMsgCode)
66{
67 memset(s_drivers, 0, sizeof(s_drivers));
68 s_driverListLock = MutexCreate();
69
70 g_logMsgCode = logMsgCode;
71 s_writeLog = (logMsgCode > 0);
72 g_sqlErrorMsgCode = sqlErrorMsgCode;
73 s_logSqlErrors = (sqlErrorMsgCode > 0) && s_writeLog;
74
75 return true;
76}
77
ef33dc5f
VK
78/**
79 * Load and initialize database driver
80 * If logMsgCode == 0, logging will be disabled
81 * If sqlErrorMsgCode == 0, failed SQL queries will not be logged
82 *
83 * @return driver handle on success, NULL on failure
84 */
80e96a82 85DB_DRIVER LIBNXDB_EXPORTABLE DBLoadDriver(const TCHAR *module, const TCHAR *initParameters,
526ae8b8 86 bool dumpSQL, void (* fpEventHandler)(DWORD, const WCHAR *, const WCHAR *, bool, void *),
80e96a82
VK
87 void *userArg)
88{
89 static DWORD dwVersionZero = 0;
2df047f4 90 bool (* fpDrvInit)(const char *);
80e96a82
VK
91 DWORD *pdwAPIVersion;
92 TCHAR szErrorText[256];
93 const char *driverName;
94 DB_DRIVER driver;
95 bool alreadyLoaded = false;
96 int position = -1;
ef33dc5f 97 int i; // have to define it here because otherwise HP aCC complains at goto statements
80e96a82 98
c17f6cbc 99 MutexLock(s_driverListLock);
80e96a82
VK
100
101 driver = (DB_DRIVER)malloc(sizeof(db_driver_t));
102 memset(driver, 0, sizeof(db_driver_t));
103
104 driver->m_logSqlErrors = s_logSqlErrors;
105 driver->m_dumpSql = dumpSQL;
106 driver->m_fpEventHandler = fpEventHandler;
107 driver->m_userArg = userArg;
108
109 // Load driver's module
110#ifdef _WIN32
111 driver->m_handle = DLOpen(module, szErrorText);
112#else
113 TCHAR fullName[MAX_PATH];
114 if (_tcschr(module, _T('/')) == NULL)
115 {
38ffd4b0
VK
116 const TCHAR *homeDir = _tgetenv(_T("NETXMS_HOME"));
117 if ((homeDir != NULL) && (*homeDir != 0))
118 {
119 _sntprintf(fullName, MAX_PATH, _T("%s/lib/netxms/dbdrv/%s"), homeDir, module);
120 }
121 else
122 {
123 _sntprintf(fullName, MAX_PATH, _T("%s/dbdrv/%s"), PKGLIBDIR, module);
124 }
80e96a82
VK
125 }
126 else
127 {
128 nx_strncpy(fullName, module, MAX_PATH);
129 }
130 driver->m_handle = DLOpen(fullName, szErrorText);
131#endif
132 if (driver->m_handle == NULL)
133 {
134 if (s_writeLog)
135 __DBWriteLog(EVENTLOG_ERROR_TYPE, _T("Unable to load database driver module \"%s\": %s"), module, szErrorText);
136 goto failure;
137 }
138
139 // Check API version supported by driver
dda7c270 140 pdwAPIVersion = (DWORD *)DLGetSymbolAddr(driver->m_handle, "drvAPIVersion", NULL);
80e96a82
VK
141 if (pdwAPIVersion == NULL)
142 pdwAPIVersion = &dwVersionZero;
143 if (*pdwAPIVersion != DBDRV_API_VERSION)
144 {
145 if (s_writeLog)
6e3a99d7 146 __DBWriteLog(EVENTLOG_ERROR_TYPE, _T("Database driver \"%s\" cannot be loaded because of API version mismatch (server: %d; driver: %d)"),
80e96a82
VK
147 module, (int)(DBDRV_API_VERSION), (int)(*pdwAPIVersion));
148 goto failure;
149 }
150
151 // Check name
dda7c270 152 driverName = *((const char **)DLGetSymbolAddr(driver->m_handle, "drvName", NULL));
80e96a82
VK
153 if (driverName == NULL)
154 {
155 if (s_writeLog)
156 __DBWriteLog(EVENTLOG_ERROR_TYPE, _T("Unable to find all required entry points in database driver \"%s\""), module);
157 goto failure;
158 }
159
ef33dc5f 160 for(i = 0; i < MAX_DB_DRIVERS; i++)
80e96a82
VK
161 {
162 if ((s_drivers[i] != NULL) && (!stricmp(s_drivers[i]->m_name, driverName)))
163 {
164 alreadyLoaded = true;
165 position = i;
166 break;
167 }
168 if (s_drivers[i] == NULL)
169 position = i;
170 }
171
172 if (alreadyLoaded)
173 {
174 if (s_writeLog)
9a222d39 175 __DBWriteLog(EVENTLOG_INFORMATION_TYPE, _T("Reusing already loaded database driver \"%hs\""), s_drivers[position]->m_name);
80e96a82
VK
176 goto reuse_driver;
177 }
178
179 if (position == -1)
180 {
181 if (s_writeLog)
182 __DBWriteLog(EVENTLOG_ERROR_TYPE, _T("Unable to load database driver \"%s\": too many drivers already loaded"), module);
183 goto failure;
184 }
185
186 // Import symbols
2df047f4 187 fpDrvInit = (bool (*)(const char *))DLGetSymbolAddrEx(driver->m_handle, "DrvInit");
dda7c270
VK
188 driver->m_fpDrvConnect = (DBDRV_CONNECTION (*)(const char *, const char *, const char *, const char *, const char *, WCHAR *))DLGetSymbolAddrEx(driver->m_handle, "DrvConnect");
189 driver->m_fpDrvDisconnect = (void (*)(DBDRV_CONNECTION))DLGetSymbolAddrEx(driver->m_handle, "DrvDisconnect");
85dfb586 190 driver->m_fpDrvSetPrefetchLimit = (bool (*)(DBDRV_CONNECTION, int))DLGetSymbolAddrEx(driver->m_handle, "DrvSetPrefetchLimit", false);
dda7c270
VK
191 driver->m_fpDrvPrepare = (DBDRV_STATEMENT (*)(DBDRV_CONNECTION, const WCHAR *, DWORD *, WCHAR *))DLGetSymbolAddrEx(driver->m_handle, "DrvPrepare");
192 driver->m_fpDrvFreeStatement = (void (*)(DBDRV_STATEMENT))DLGetSymbolAddrEx(driver->m_handle, "DrvFreeStatement");
66c11d49
VK
193 driver->m_fpDrvOpenBatch = (bool (*)(DBDRV_STATEMENT))DLGetSymbolAddrEx(driver->m_handle, "DrvOpenBatch", false);
194 driver->m_fpDrvNextBatchRow = (void (*)(DBDRV_STATEMENT))DLGetSymbolAddrEx(driver->m_handle, "DrvNextBatchRow", false);
dda7c270
VK
195 driver->m_fpDrvBind = (void (*)(DBDRV_STATEMENT, int, int, int, void *, int))DLGetSymbolAddrEx(driver->m_handle, "DrvBind");
196 driver->m_fpDrvExecute = (DWORD (*)(DBDRV_CONNECTION, DBDRV_STATEMENT, WCHAR *))DLGetSymbolAddrEx(driver->m_handle, "DrvExecute");
197 driver->m_fpDrvQuery = (DWORD (*)(DBDRV_CONNECTION, const WCHAR *, WCHAR *))DLGetSymbolAddrEx(driver->m_handle, "DrvQuery");
198 driver->m_fpDrvSelect = (DBDRV_RESULT (*)(DBDRV_CONNECTION, const WCHAR *, DWORD *, WCHAR *))DLGetSymbolAddrEx(driver->m_handle, "DrvSelect");
f17cf019 199 driver->m_fpDrvSelectUnbuffered = (DBDRV_UNBUFFERED_RESULT (*)(DBDRV_CONNECTION, const WCHAR *, DWORD *, WCHAR *))DLGetSymbolAddrEx(driver->m_handle, "DrvSelectUnbuffered");
dda7c270 200 driver->m_fpDrvSelectPrepared = (DBDRV_RESULT (*)(DBDRV_CONNECTION, DBDRV_STATEMENT, DWORD *, WCHAR *))DLGetSymbolAddrEx(driver->m_handle, "DrvSelectPrepared");
f17cf019
VK
201 driver->m_fpDrvSelectPreparedUnbuffered = (DBDRV_UNBUFFERED_RESULT (*)(DBDRV_CONNECTION, DBDRV_STATEMENT, DWORD *, WCHAR *))DLGetSymbolAddrEx(driver->m_handle, "DrvSelectPreparedUnbuffered");
202 driver->m_fpDrvFetch = (bool (*)(DBDRV_UNBUFFERED_RESULT))DLGetSymbolAddrEx(driver->m_handle, "DrvFetch");
dda7c270 203 driver->m_fpDrvGetFieldLength = (LONG (*)(DBDRV_RESULT, int, int))DLGetSymbolAddrEx(driver->m_handle, "DrvGetFieldLength");
f17cf019 204 driver->m_fpDrvGetFieldLengthUnbuffered = (LONG (*)(DBDRV_UNBUFFERED_RESULT, int))DLGetSymbolAddrEx(driver->m_handle, "DrvGetFieldLengthUnbuffered");
dda7c270 205 driver->m_fpDrvGetField = (WCHAR* (*)(DBDRV_RESULT, int, int, WCHAR *, int))DLGetSymbolAddrEx(driver->m_handle, "DrvGetField");
e2bb9f48 206 driver->m_fpDrvGetFieldUTF8 = (char* (*)(DBDRV_RESULT, int, int, char *, int))DLGetSymbolAddrEx(driver->m_handle, "DrvGetFieldUTF8", false); // optional entry point
f17cf019 207 driver->m_fpDrvGetFieldUnbuffered = (WCHAR* (*)(DBDRV_UNBUFFERED_RESULT, int, WCHAR *, int))DLGetSymbolAddrEx(driver->m_handle, "DrvGetFieldUnbuffered");
7f8e3ccf 208 driver->m_fpDrvGetFieldUnbufferedUTF8 = (char* (*)(DBDRV_UNBUFFERED_RESULT, int, char *, int))DLGetSymbolAddrEx(driver->m_handle, "DrvGetFieldUnbufferedUTF8");
dda7c270
VK
209 driver->m_fpDrvGetNumRows = (int (*)(DBDRV_RESULT))DLGetSymbolAddrEx(driver->m_handle, "DrvGetNumRows");
210 driver->m_fpDrvGetColumnCount = (int (*)(DBDRV_RESULT))DLGetSymbolAddrEx(driver->m_handle, "DrvGetColumnCount");
211 driver->m_fpDrvGetColumnName = (const char* (*)(DBDRV_RESULT, int))DLGetSymbolAddrEx(driver->m_handle, "DrvGetColumnName");
f17cf019
VK
212 driver->m_fpDrvGetColumnCountUnbuffered = (int (*)(DBDRV_UNBUFFERED_RESULT))DLGetSymbolAddrEx(driver->m_handle, "DrvGetColumnCountUnbuffered");
213 driver->m_fpDrvGetColumnNameUnbuffered = (const char* (*)(DBDRV_UNBUFFERED_RESULT, int))DLGetSymbolAddrEx(driver->m_handle, "DrvGetColumnNameUnbuffered");
dda7c270 214 driver->m_fpDrvFreeResult = (void (*)(DBDRV_RESULT))DLGetSymbolAddrEx(driver->m_handle, "DrvFreeResult");
f17cf019 215 driver->m_fpDrvFreeUnbufferedResult = (void (*)(DBDRV_UNBUFFERED_RESULT))DLGetSymbolAddrEx(driver->m_handle, "DrvFreeUnbufferedResult");
dda7c270
VK
216 driver->m_fpDrvBegin = (DWORD (*)(DBDRV_CONNECTION))DLGetSymbolAddrEx(driver->m_handle, "DrvBegin");
217 driver->m_fpDrvCommit = (DWORD (*)(DBDRV_CONNECTION))DLGetSymbolAddrEx(driver->m_handle, "DrvCommit");
218 driver->m_fpDrvRollback = (DWORD (*)(DBDRV_CONNECTION))DLGetSymbolAddrEx(driver->m_handle, "DrvRollback");
219 driver->m_fpDrvUnload = (void (*)(void))DLGetSymbolAddrEx(driver->m_handle, "DrvUnload");
220 driver->m_fpDrvPrepareStringA = (char* (*)(const char *))DLGetSymbolAddrEx(driver->m_handle, "DrvPrepareStringA");
221 driver->m_fpDrvPrepareStringW = (WCHAR* (*)(const WCHAR *))DLGetSymbolAddrEx(driver->m_handle, "DrvPrepareStringW");
daf3c104 222 driver->m_fpDrvIsTableExist = (int (*)(DBDRV_CONNECTION, const WCHAR *))DLGetSymbolAddrEx(driver->m_handle, "DrvIsTableExist");
80e96a82
VK
223 if ((fpDrvInit == NULL) || (driver->m_fpDrvConnect == NULL) || (driver->m_fpDrvDisconnect == NULL) ||
224 (driver->m_fpDrvPrepare == NULL) || (driver->m_fpDrvBind == NULL) || (driver->m_fpDrvFreeStatement == NULL) ||
225 (driver->m_fpDrvQuery == NULL) || (driver->m_fpDrvSelect == NULL) || (driver->m_fpDrvGetField == NULL) ||
226 (driver->m_fpDrvGetNumRows == NULL) || (driver->m_fpDrvFreeResult == NULL) || (driver->m_fpDrvSelectPrepared == NULL) ||
f17cf019
VK
227 (driver->m_fpDrvSelectPreparedUnbuffered == NULL) || (driver->m_fpDrvUnload == NULL) ||
228 (driver->m_fpDrvSelectUnbuffered == NULL) || (driver->m_fpDrvFetch == NULL) ||
229 (driver->m_fpDrvFreeUnbufferedResult == NULL) || (driver->m_fpDrvGetFieldUnbuffered == NULL) ||
80e96a82
VK
230 (driver->m_fpDrvBegin == NULL) || (driver->m_fpDrvCommit == NULL) || (driver->m_fpDrvRollback == NULL) ||
231 (driver->m_fpDrvGetColumnCount == NULL) || (driver->m_fpDrvGetColumnName == NULL) ||
f17cf019
VK
232 (driver->m_fpDrvGetColumnCountUnbuffered == NULL) || (driver->m_fpDrvGetColumnNameUnbuffered == NULL) ||
233 (driver->m_fpDrvGetFieldLength == NULL) || (driver->m_fpDrvGetFieldLengthUnbuffered == NULL) ||
daf3c104 234 (driver->m_fpDrvPrepareStringA == NULL) || (driver->m_fpDrvPrepareStringW == NULL) || (driver->m_fpDrvIsTableExist == NULL))
80e96a82
VK
235 {
236 if (s_writeLog)
237 __DBWriteLog(EVENTLOG_ERROR_TYPE, _T("Unable to find all required entry points in database driver \"%s\""), module);
238 goto failure;
239 }
240
241 // Initialize driver
242#ifdef UNICODE
6d0fb256 243 char mbInitParameters[1024];
244 if (initParameters != NULL)
245 {
246 WideCharToMultiByte(CP_ACP, WC_COMPOSITECHECK | WC_DEFAULTCHAR, initParameters, -1, mbInitParameters, 1024, NULL, NULL);
247 mbInitParameters[1023] = 0;
248 }
249 else
250 {
251 mbInitParameters[0] = 0;
252 }
2df047f4 253 if (!fpDrvInit(mbInitParameters))
80e96a82 254#else
2df047f4 255 if (!fpDrvInit(CHECK_NULL_EX(initParameters)))
80e96a82
VK
256#endif
257 {
258 if (s_writeLog)
259 __DBWriteLog(EVENTLOG_ERROR_TYPE, _T("Database driver \"%s\" initialization failed"), module);
260 goto failure;
261 }
262
263 // Success
264 driver->m_mutexReconnect = MutexCreate();
265 driver->m_name = driverName;
266 driver->m_refCount = 1;
85dfb586 267 driver->m_defaultPrefetchLimit = 10;
80e96a82
VK
268 s_drivers[position] = driver;
269 if (s_writeLog)
270 __DBWriteLog(EVENTLOG_INFORMATION_TYPE, _T("Database driver \"%s\" loaded and initialized successfully"), module);
271 MutexUnlock(s_driverListLock);
272 return driver;
273
274failure:
275 if (driver->m_handle != NULL)
276 DLClose(driver->m_handle);
277 free(driver);
278 MutexUnlock(s_driverListLock);
279 return NULL;
280
281reuse_driver:
282 if (driver->m_handle != NULL)
283 DLClose(driver->m_handle);
284 free(driver);
285 s_drivers[position]->m_refCount++;
286 MutexUnlock(s_driverListLock);
287 return s_drivers[position];
288}
289
ef33dc5f
VK
290/**
291 * Unload driver
292 */
80e96a82
VK
293void LIBNXDB_EXPORTABLE DBUnloadDriver(DB_DRIVER driver)
294{
7c7a57b9
VK
295 if (driver == NULL)
296 return;
297
c17f6cbc 298 MutexLock(s_driverListLock);
80e96a82
VK
299
300 for(int i = 0; i < MAX_DB_DRIVERS; i++)
301 {
302 if (s_drivers[i] == driver)
303 {
304 driver->m_refCount--;
305 if (driver->m_refCount <= 0)
306 {
307 driver->m_fpDrvUnload();
308 DLClose(driver->m_handle);
309 MutexDestroy(driver->m_mutexReconnect);
310 free(driver);
311 s_drivers[i] = NULL;
312 }
313 break;
314 }
315 }
316
317 MutexUnlock(s_driverListLock);
318}
13d69c7d 319
ef33dc5f
VK
320/**
321 * Get name of loaded driver
322 */
13d69c7d
VK
323const char LIBNXDB_EXPORTABLE *DBGetDriverName(DB_DRIVER driver)
324{
325 return driver->m_name;
326}