license for libnxdb and libnxsrv changed to LGPL
[public/netxms.git] / src / db / libnxdb / drivers.cpp
1 /*
2 ** NetXMS - Network Management System
3 ** Copyright (C) 2003-2010 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 //
27 // Global data
28 //
29
30 DWORD g_logMsgCode = 0;
31 DWORD g_sqlErrorMsgCode = 0;
32
33
34 //
35 // Static data
36 //
37
38 static DB_DRIVER s_drivers[MAX_DB_DRIVERS];
39 static MUTEX s_driverListLock;
40 static bool s_writeLog = false;
41 static bool s_logSqlErrors = false;
42
43
44 //
45 // Get symbol address and log errors
46 //
47
48 static void *DLGetSymbolAddrEx(HMODULE hModule, const TCHAR *pszSymbol)
49 {
50 void *pFunc;
51 char szErrorText[256];
52
53 pFunc = DLGetSymbolAddr(hModule, pszSymbol, szErrorText);
54 if ((pFunc == NULL) && s_writeLog)
55 __DBWriteLog(EVENTLOG_WARNING_TYPE, _T("Unable to resolve symbol \"%s\": %s"), pszSymbol, szErrorText);
56 return pFunc;
57 }
58
59
60 //
61 // Init library
62 //
63
64 bool LIBNXDB_EXPORTABLE DBInit(DWORD logMsgCode, DWORD sqlErrorMsgCode)
65 {
66 memset(s_drivers, 0, sizeof(s_drivers));
67 s_driverListLock = MutexCreate();
68
69 g_logMsgCode = logMsgCode;
70 s_writeLog = (logMsgCode > 0);
71 g_sqlErrorMsgCode = sqlErrorMsgCode;
72 s_logSqlErrors = (sqlErrorMsgCode > 0) && s_writeLog;
73
74 return true;
75 }
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 // Returns driver handle on success, NULL on failure
83 //
84
85 DB_DRIVER LIBNXDB_EXPORTABLE DBLoadDriver(const TCHAR *module, const TCHAR *initParameters,
86 bool dumpSQL, void (* fpEventHandler)(DWORD, const TCHAR *, const TCHAR *, void *),
87 void *userArg)
88 {
89 static DWORD dwVersionZero = 0;
90 BOOL (* fpDrvInit)(const char *);
91 DWORD *pdwAPIVersion;
92 char szErrorText[256];
93 const char *driverName;
94 DB_DRIVER driver;
95 bool alreadyLoaded = false;
96 int position = -1;
97
98 MutexLock(s_driverListLock, INFINITE);
99
100 driver = (DB_DRIVER)malloc(sizeof(db_driver_t));
101 memset(driver, 0, sizeof(db_driver_t));
102
103 driver->m_logSqlErrors = s_logSqlErrors;
104 driver->m_dumpSql = dumpSQL;
105 driver->m_fpEventHandler = fpEventHandler;
106 driver->m_userArg = userArg;
107
108 // Load driver's module
109 driver->m_handle = DLOpen(module, szErrorText);
110 if (driver->m_handle == NULL)
111 {
112 if (s_writeLog)
113 __DBWriteLog(EVENTLOG_ERROR_TYPE, _T("Unable to load database driver module \"%s\": %s"), module, szErrorText);
114 goto failure;
115 }
116
117 // Check API version supported by driver
118 pdwAPIVersion = (DWORD *)DLGetSymbolAddr(driver->m_handle, "drvAPIVersion", NULL);
119 if (pdwAPIVersion == NULL)
120 pdwAPIVersion = &dwVersionZero;
121 if (*pdwAPIVersion != DBDRV_API_VERSION)
122 {
123 if (s_writeLog)
124 __DBWriteLog(EVENTLOG_ERROR_TYPE, _T("Database driver \"%s\" cannot be loaded because of API version mismatch (driver: %d; server: %d)"),
125 module, (int)(DBDRV_API_VERSION), (int)(*pdwAPIVersion));
126 goto failure;
127 }
128
129 // Check name
130 driverName = (const char *)DLGetSymbolAddr(driver->m_handle, "drvName", NULL);
131 if (driverName == NULL)
132 {
133 if (s_writeLog)
134 __DBWriteLog(EVENTLOG_ERROR_TYPE, _T("Unable to find all required entry points in database driver \"%s\""), module);
135 goto failure;
136 }
137
138 for(int i = 0; i < MAX_DB_DRIVERS; i++)
139 {
140 if ((s_drivers[i] != NULL) && (!stricmp(s_drivers[i]->m_name, driverName)))
141 {
142 alreadyLoaded = true;
143 position = i;
144 break;
145 }
146 if (s_drivers[i] == NULL)
147 position = i;
148 }
149
150 if (alreadyLoaded)
151 {
152 if (s_writeLog)
153 __DBWriteLog(EVENTLOG_INFORMATION_TYPE, _T("Reusing already loaded database driver \"%s\""), s_drivers[position]->m_name);
154 goto reuse_driver;
155 }
156
157 if (position == -1)
158 {
159 if (s_writeLog)
160 __DBWriteLog(EVENTLOG_ERROR_TYPE, _T("Unable to load database driver \"%s\": too many drivers already loaded"), module);
161 goto failure;
162 }
163
164 // Import symbols
165 fpDrvInit = (BOOL (*)(const char *))DLGetSymbolAddrEx(driver->m_handle, "DrvInit");
166 driver->m_fpDrvConnect = (DBDRV_CONNECTION (*)(const char *, const char *, const char *, const char *))DLGetSymbolAddrEx(driver->m_handle, "DrvConnect");
167 driver->m_fpDrvDisconnect = (void (*)(DBDRV_CONNECTION))DLGetSymbolAddrEx(driver->m_handle, "DrvDisconnect");
168 driver->m_fpDrvQuery = (DWORD (*)(DBDRV_CONNECTION, WCHAR *, TCHAR *))DLGetSymbolAddrEx(driver->m_handle, "DrvQuery");
169 driver->m_fpDrvSelect = (DBDRV_RESULT (*)(DBDRV_CONNECTION, WCHAR *, DWORD *, TCHAR *))DLGetSymbolAddrEx(driver->m_handle, "DrvSelect");
170 driver->m_fpDrvAsyncSelect = (DBDRV_ASYNC_RESULT (*)(DBDRV_CONNECTION, WCHAR *, DWORD *, TCHAR *))DLGetSymbolAddrEx(driver->m_handle, "DrvAsyncSelect");
171 driver->m_fpDrvFetch = (BOOL (*)(DBDRV_ASYNC_RESULT))DLGetSymbolAddrEx(driver->m_handle, "DrvFetch");
172 driver->m_fpDrvGetFieldLength = (LONG (*)(DBDRV_RESULT, int, int))DLGetSymbolAddrEx(driver->m_handle, "DrvGetFieldLength");
173 driver->m_fpDrvGetFieldLengthAsync = (LONG (*)(DBDRV_RESULT, int))DLGetSymbolAddrEx(driver->m_handle, "DrvGetFieldLengthAsync");
174 driver->m_fpDrvGetField = (WCHAR* (*)(DBDRV_RESULT, int, int, WCHAR *, int))DLGetSymbolAddrEx(driver->m_handle, "DrvGetField");
175 driver->m_fpDrvGetFieldAsync = (WCHAR* (*)(DBDRV_ASYNC_RESULT, int, WCHAR *, int))DLGetSymbolAddrEx(driver->m_handle, "DrvGetFieldAsync");
176 driver->m_fpDrvGetNumRows = (int (*)(DBDRV_RESULT))DLGetSymbolAddrEx(driver->m_handle, "DrvGetNumRows");
177 driver->m_fpDrvGetColumnCount = (int (*)(DBDRV_RESULT))DLGetSymbolAddrEx(driver->m_handle, "DrvGetColumnCount");
178 driver->m_fpDrvGetColumnName = (const char* (*)(DBDRV_RESULT, int))DLGetSymbolAddrEx(driver->m_handle, "DrvGetColumnName");
179 driver->m_fpDrvGetColumnCountAsync = (int (*)(DBDRV_ASYNC_RESULT))DLGetSymbolAddrEx(driver->m_handle, "DrvGetColumnCountAsync");
180 driver->m_fpDrvGetColumnNameAsync = (const char* (*)(DBDRV_ASYNC_RESULT, int))DLGetSymbolAddrEx(driver->m_handle, "DrvGetColumnNameAsync");
181 driver->m_fpDrvFreeResult = (void (*)(DBDRV_RESULT))DLGetSymbolAddrEx(driver->m_handle, "DrvFreeResult");
182 driver->m_fpDrvFreeAsyncResult = (void (*)(DBDRV_ASYNC_RESULT))DLGetSymbolAddrEx(driver->m_handle, "DrvFreeAsyncResult");
183 driver->m_fpDrvBegin = (DWORD (*)(DBDRV_CONNECTION))DLGetSymbolAddrEx(driver->m_handle, "DrvBegin");
184 driver->m_fpDrvCommit = (DWORD (*)(DBDRV_CONNECTION))DLGetSymbolAddrEx(driver->m_handle, "DrvCommit");
185 driver->m_fpDrvRollback = (DWORD (*)(DBDRV_CONNECTION))DLGetSymbolAddrEx(driver->m_handle, "DrvRollback");
186 driver->m_fpDrvUnload = (void (*)(void))DLGetSymbolAddrEx(driver->m_handle, "DrvUnload");
187 driver->m_fpDrvPrepareString = (TCHAR* (*)(const TCHAR *))DLGetSymbolAddrEx(driver->m_handle, "DrvPrepareString");
188 if ((fpDrvInit == NULL) || (driver->m_fpDrvConnect == NULL) || (driver->m_fpDrvDisconnect == NULL) ||
189 (driver->m_fpDrvQuery == NULL) || (driver->m_fpDrvSelect == NULL) || (driver->m_fpDrvGetField == NULL) ||
190 (driver->m_fpDrvGetNumRows == NULL) || (driver->m_fpDrvFreeResult == NULL) ||
191 (driver->m_fpDrvUnload == NULL) || (driver->m_fpDrvAsyncSelect == NULL) || (driver->m_fpDrvFetch == NULL) ||
192 (driver->m_fpDrvFreeAsyncResult == NULL) || (driver->m_fpDrvGetFieldAsync == NULL) ||
193 (driver->m_fpDrvBegin == NULL) || (driver->m_fpDrvCommit == NULL) || (driver->m_fpDrvRollback == NULL) ||
194 (driver->m_fpDrvGetColumnCount == NULL) || (driver->m_fpDrvGetColumnName == NULL) ||
195 (driver->m_fpDrvGetColumnCountAsync == NULL) || (driver->m_fpDrvGetColumnNameAsync == NULL) ||
196 (driver->m_fpDrvGetFieldLength == NULL) || (driver->m_fpDrvGetFieldLengthAsync == NULL) ||
197 (driver->m_fpDrvPrepareString == NULL))
198 {
199 if (s_writeLog)
200 __DBWriteLog(EVENTLOG_ERROR_TYPE, _T("Unable to find all required entry points in database driver \"%s\""), module);
201 goto failure;
202 }
203
204 // Initialize driver
205 if (!fpDrvInit(initParameters))
206 {
207 if (s_writeLog)
208 __DBWriteLog(EVENTLOG_ERROR_TYPE, _T("Database driver \"%s\" initialization failed"), module);
209 goto failure;
210 }
211
212 // Success
213 driver->m_mutexReconnect = MutexCreate();
214 driver->m_name = driverName;
215 driver->m_refCount = 1;
216 s_drivers[position] = driver;
217 if (s_writeLog)
218 __DBWriteLog(EVENTLOG_INFORMATION_TYPE, _T("Database driver \"%s\" loaded and initialized successfully"), module);
219 MutexUnlock(s_driverListLock);
220 return driver;
221
222 failure:
223 if (driver->m_handle != NULL)
224 DLClose(driver->m_handle);
225 free(driver);
226 MutexUnlock(s_driverListLock);
227 return NULL;
228
229 reuse_driver:
230 if (driver->m_handle != NULL)
231 DLClose(driver->m_handle);
232 free(driver);
233 s_drivers[position]->m_refCount++;
234 MutexUnlock(s_driverListLock);
235 return s_drivers[position];
236 }
237
238
239 //
240 // Unload driver
241 //
242
243 void LIBNXDB_EXPORTABLE DBUnloadDriver(DB_DRIVER driver)
244 {
245 MutexLock(s_driverListLock, INFINITE);
246
247 for(int i = 0; i < MAX_DB_DRIVERS; i++)
248 {
249 if (s_drivers[i] == driver)
250 {
251 driver->m_refCount--;
252 if (driver->m_refCount <= 0)
253 {
254 driver->m_fpDrvUnload();
255 DLClose(driver->m_handle);
256 MutexDestroy(driver->m_mutexReconnect);
257 free(driver);
258 s_drivers[i] = NULL;
259 }
260 break;
261 }
262 }
263
264 MutexUnlock(s_driverListLock);
265 }