Commit | Line | Data |
---|---|---|
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 |
28 | UINT32 g_logMsgCode = 0; |
29 | UINT32 g_sqlErrorMsgCode = 0; | |
30 | ||
31 | /** | |
32 | * Long-running query threshold | |
33 | */ | |
34 | UINT32 g_sqlQueryExecTimeThreshold = 0xFFFFFFFF; | |
80e96a82 | 35 | |
ef33dc5f VK |
36 | /** |
37 | * Loaded drivers | |
38 | */ | |
80e96a82 VK |
39 | static DB_DRIVER s_drivers[MAX_DB_DRIVERS]; |
40 | static MUTEX s_driverListLock; | |
ef33dc5f VK |
41 | |
42 | /** | |
43 | * Options | |
44 | */ | |
80e96a82 VK |
45 | static bool s_writeLog = false; |
46 | static bool s_logSqlErrors = false; | |
47 | ||
ef33dc5f VK |
48 | /** |
49 | * Get symbol address and log errors | |
50 | */ | |
e2bb9f48 | 51 | static 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 |
65 | bool 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 | 85 | DB_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 | ||
274 | failure: | |
275 | if (driver->m_handle != NULL) | |
276 | DLClose(driver->m_handle); | |
277 | free(driver); | |
278 | MutexUnlock(s_driverListLock); | |
279 | return NULL; | |
280 | ||
281 | reuse_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 |
293 | void 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 |
323 | const char LIBNXDB_EXPORTABLE *DBGetDriverName(DB_DRIVER driver) |
324 | { | |
325 | return driver->m_name; | |
326 | } |