implemented DB driver call DrvGetFieldUnbufferedUTF8 (for databases with native UTF...
[public/netxms.git] / src / db / libnxdb / drivers.cpp
1 /*
2 ** NetXMS - Network Management System
3 ** Copyright (C) 2003-2014 Victor Kirhenshtein
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
25 /**
26 * Log message codes
27 */
28 UINT32 g_logMsgCode = 0;
29 UINT32 g_sqlErrorMsgCode = 0;
30
31 /**
32 * Long-running query threshold
33 */
34 UINT32 g_sqlQueryExecTimeThreshold = 0xFFFFFFFF;
35
36 /**
37 * Loaded drivers
38 */
39 static DB_DRIVER s_drivers[MAX_DB_DRIVERS];
40 static MUTEX s_driverListLock;
41
42 /**
43 * Options
44 */
45 static bool s_writeLog = false;
46 static bool s_logSqlErrors = false;
47
48 /**
49 * Get symbol address and log errors
50 */
51 static void *DLGetSymbolAddrEx(HMODULE hModule, const char *pszSymbol, bool mandatory = true)
52 {
53 void *pFunc;
54 TCHAR szErrorText[256];
55
56 pFunc = DLGetSymbolAddr(hModule, pszSymbol, szErrorText);
57 if ((pFunc == NULL) && mandatory && s_writeLog)
58 __DBWriteLog(EVENTLOG_WARNING_TYPE, _T("Unable to resolve symbol \"%hs\": %s"), pszSymbol, szErrorText);
59 return pFunc;
60 }
61
62 /**
63 * Init DB library
64 */
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
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 */
85 DB_DRIVER LIBNXDB_EXPORTABLE DBLoadDriver(const TCHAR *module, const TCHAR *initParameters,
86 bool dumpSQL, void (* fpEventHandler)(DWORD, const WCHAR *, const WCHAR *, bool, void *),
87 void *userArg)
88 {
89 static DWORD dwVersionZero = 0;
90 bool (* fpDrvInit)(const char *);
91 DWORD *pdwAPIVersion;
92 TCHAR szErrorText[256];
93 const char *driverName;
94 DB_DRIVER driver;
95 bool alreadyLoaded = false;
96 int position = -1;
97 int i; // have to define it here because otherwise HP aCC complains at goto statements
98
99 MutexLock(s_driverListLock);
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 {
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 }
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
140 pdwAPIVersion = (DWORD *)DLGetSymbolAddr(driver->m_handle, "drvAPIVersion", NULL);
141 if (pdwAPIVersion == NULL)
142 pdwAPIVersion = &dwVersionZero;
143 if (*pdwAPIVersion != DBDRV_API_VERSION)
144 {
145 if (s_writeLog)
146 __DBWriteLog(EVENTLOG_ERROR_TYPE, _T("Database driver \"%s\" cannot be loaded because of API version mismatch (server: %d; driver: %d)"),
147 module, (int)(DBDRV_API_VERSION), (int)(*pdwAPIVersion));
148 goto failure;
149 }
150
151 // Check name
152 driverName = *((const char **)DLGetSymbolAddr(driver->m_handle, "drvName", NULL));
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
160 for(i = 0; i < MAX_DB_DRIVERS; i++)
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)
175 __DBWriteLog(EVENTLOG_INFORMATION_TYPE, _T("Reusing already loaded database driver \"%hs\""), s_drivers[position]->m_name);
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
187 fpDrvInit = (bool (*)(const char *))DLGetSymbolAddrEx(driver->m_handle, "DrvInit");
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");
190 driver->m_fpDrvSetPrefetchLimit = (bool (*)(DBDRV_CONNECTION, int))DLGetSymbolAddrEx(driver->m_handle, "DrvSetPrefetchLimit", false);
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");
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);
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");
199 driver->m_fpDrvSelectUnbuffered = (DBDRV_UNBUFFERED_RESULT (*)(DBDRV_CONNECTION, const WCHAR *, DWORD *, WCHAR *))DLGetSymbolAddrEx(driver->m_handle, "DrvSelectUnbuffered");
200 driver->m_fpDrvSelectPrepared = (DBDRV_RESULT (*)(DBDRV_CONNECTION, DBDRV_STATEMENT, DWORD *, WCHAR *))DLGetSymbolAddrEx(driver->m_handle, "DrvSelectPrepared");
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");
203 driver->m_fpDrvGetFieldLength = (LONG (*)(DBDRV_RESULT, int, int))DLGetSymbolAddrEx(driver->m_handle, "DrvGetFieldLength");
204 driver->m_fpDrvGetFieldLengthUnbuffered = (LONG (*)(DBDRV_UNBUFFERED_RESULT, int))DLGetSymbolAddrEx(driver->m_handle, "DrvGetFieldLengthUnbuffered");
205 driver->m_fpDrvGetField = (WCHAR* (*)(DBDRV_RESULT, int, int, WCHAR *, int))DLGetSymbolAddrEx(driver->m_handle, "DrvGetField");
206 driver->m_fpDrvGetFieldUTF8 = (char* (*)(DBDRV_RESULT, int, int, char *, int))DLGetSymbolAddrEx(driver->m_handle, "DrvGetFieldUTF8", false); // optional entry point
207 driver->m_fpDrvGetFieldUnbuffered = (WCHAR* (*)(DBDRV_UNBUFFERED_RESULT, int, WCHAR *, int))DLGetSymbolAddrEx(driver->m_handle, "DrvGetFieldUnbuffered");
208 driver->m_fpDrvGetFieldUnbufferedUTF8 = (char* (*)(DBDRV_UNBUFFERED_RESULT, int, char *, int))DLGetSymbolAddrEx(driver->m_handle, "DrvGetFieldUnbufferedUTF8");
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");
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");
214 driver->m_fpDrvFreeResult = (void (*)(DBDRV_RESULT))DLGetSymbolAddrEx(driver->m_handle, "DrvFreeResult");
215 driver->m_fpDrvFreeUnbufferedResult = (void (*)(DBDRV_UNBUFFERED_RESULT))DLGetSymbolAddrEx(driver->m_handle, "DrvFreeUnbufferedResult");
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");
222 driver->m_fpDrvIsTableExist = (int (*)(DBDRV_CONNECTION, const WCHAR *))DLGetSymbolAddrEx(driver->m_handle, "DrvIsTableExist");
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) ||
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) ||
230 (driver->m_fpDrvBegin == NULL) || (driver->m_fpDrvCommit == NULL) || (driver->m_fpDrvRollback == NULL) ||
231 (driver->m_fpDrvGetColumnCount == NULL) || (driver->m_fpDrvGetColumnName == NULL) ||
232 (driver->m_fpDrvGetColumnCountUnbuffered == NULL) || (driver->m_fpDrvGetColumnNameUnbuffered == NULL) ||
233 (driver->m_fpDrvGetFieldLength == NULL) || (driver->m_fpDrvGetFieldLengthUnbuffered == NULL) ||
234 (driver->m_fpDrvPrepareStringA == NULL) || (driver->m_fpDrvPrepareStringW == NULL) || (driver->m_fpDrvIsTableExist == NULL))
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
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 }
253 if (!fpDrvInit(mbInitParameters))
254 #else
255 if (!fpDrvInit(CHECK_NULL_EX(initParameters)))
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;
267 driver->m_defaultPrefetchLimit = 10;
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
290 /**
291 * Unload driver
292 */
293 void LIBNXDB_EXPORTABLE DBUnloadDriver(DB_DRIVER driver)
294 {
295 if (driver == NULL)
296 return;
297
298 MutexLock(s_driverListLock);
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 }
319
320 /**
321 * Get name of loaded driver
322 */
323 const char LIBNXDB_EXPORTABLE *DBGetDriverName(DB_DRIVER driver)
324 {
325 return driver->m_name;
326 }