15a7a8bc2b62d2f8c7a39816425f64ce7ccddcdd
[public/netxms.git] / src / server / dbdrv / mysql / mysql.cpp
1 /*
2 ** MySQL Database Driver
3 ** Copyright (C) 2003-2009 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: mysql.cpp
20 **
21 **/
22
23 #include "mysqldrv.h"
24
25
26 //
27 // API version
28 //
29
30 extern "C" int EXPORT drvAPIVersion;
31 int EXPORT drvAPIVersion = DBDRV_API_VERSION;
32
33
34 //
35 // Initialize driver
36 //
37
38 extern "C" BOOL EXPORT DrvInit(char *szCmdLine)
39 {
40 return TRUE;
41 }
42
43
44 //
45 // Unload handler
46 //
47
48 extern "C" void EXPORT DrvUnload(void)
49 {
50 }
51
52
53 //
54 // Connect to database
55 //
56
57 extern "C" DB_CONNECTION EXPORT DrvConnect(char *szHost, char *szLogin, char *szPassword,
58 char *szDatabase)
59 {
60 MYSQL *pMySQL;
61 MYSQL_CONN *pConn;
62 char *pHost = szHost;
63 char *pSocket = NULL;
64
65 pMySQL = mysql_init(NULL);
66 if (pMySQL == NULL)
67 {
68 return NULL;
69 }
70
71 pSocket = strstr(szHost, "socket:");
72 if (pSocket != NULL)
73 {
74 pHost = NULL;
75 pSocket += 7;
76 }
77
78 if (!mysql_real_connect(
79 pMySQL, // MYSQL *
80 pHost, // host
81 szLogin[0] == 0 ? NULL : szLogin, // user
82 (szPassword[0] == 0 || szLogin[0] == 0) ? NULL : szPassword, // pass
83 szDatabase, // DB Name
84 0, // use default port
85 pSocket, // char * - unix socket
86 0 // flags
87 ))
88 {
89 mysql_close(pMySQL);
90 return NULL;
91 }
92
93 pConn = (MYSQL_CONN *)malloc(sizeof(MYSQL_CONN));
94 pConn->pMySQL = pMySQL;
95 pConn->mutexQueryLock = MutexCreate();
96
97 // Switch to UTF-8 encoding
98 mysql_query(pMySQL, "SET NAMES 'utf8'");
99
100 return (DB_CONNECTION)pConn;
101 }
102
103
104 //
105 // Disconnect from database
106 //
107
108 extern "C" void EXPORT DrvDisconnect(MYSQL_CONN *pConn)
109 {
110 if (pConn != NULL)
111 {
112 mysql_close(pConn->pMySQL);
113 MutexDestroy(pConn->mutexQueryLock);
114 free(pConn);
115 }
116 }
117
118
119 //
120 // Perform actual non-SELECT query
121 //
122
123 static DWORD DrvQueryInternal(MYSQL_CONN *pConn, const char *pszQuery, TCHAR *errorText)
124 {
125 DWORD dwRet = DBERR_INVALID_HANDLE;
126
127 MutexLock(pConn->mutexQueryLock, INFINITE);
128 if (mysql_query(pConn->pMySQL, pszQuery) == 0)
129 {
130 dwRet = DBERR_SUCCESS;
131 if (errorText != NULL)
132 *errorText = 0;
133 }
134 else
135 {
136 int nErr = mysql_errno(pConn->pMySQL);
137 if (nErr == CR_SERVER_LOST || nErr == CR_CONNECTION_ERROR || nErr == CR_SERVER_GONE_ERROR) // CR_SERVER_GONE_ERROR - ???
138 {
139 dwRet = DBERR_CONNECTION_LOST;
140 }
141 else
142 {
143 dwRet = DBERR_OTHER_ERROR;
144 }
145
146 if (errorText != NULL)
147 {
148 nx_strncpy(errorText, mysql_error(pConn->pMySQL), DBDRV_MAX_ERROR_TEXT);
149 RemoveTrailingCRLF(errorText);
150 }
151 }
152
153 MutexUnlock(pConn->mutexQueryLock);
154 return dwRet;
155 }
156
157
158 //
159 // Perform non-SELECT query
160 //
161
162 extern "C" DWORD EXPORT DrvQuery(MYSQL_CONN *pConn, WCHAR *pwszQuery, TCHAR *errorText)
163 {
164 DWORD dwRet;
165 char *pszQueryUTF8;
166
167 pszQueryUTF8 = UTF8StringFromWideString(pwszQuery);
168 dwRet = DrvQueryInternal(pConn, pszQueryUTF8, errorText);
169 free(pszQueryUTF8);
170 return dwRet;
171 }
172
173
174 //
175 // Perform SELECT query
176 //
177
178 extern "C" DB_RESULT EXPORT DrvSelect(MYSQL_CONN *pConn, WCHAR *pwszQuery, DWORD *pdwError, TCHAR *errorText)
179 {
180 DB_RESULT pResult = NULL;
181 char *pszQueryUTF8;
182
183 if (pConn == NULL)
184 {
185 *pdwError = DBERR_INVALID_HANDLE;
186 return NULL;
187 }
188
189 pszQueryUTF8 = UTF8StringFromWideString(pwszQuery);
190 MutexLock(pConn->mutexQueryLock, INFINITE);
191 if (mysql_query(pConn->pMySQL, pszQueryUTF8) == 0)
192 {
193 pResult = (DB_RESULT)mysql_store_result(pConn->pMySQL);
194 *pdwError = DBERR_SUCCESS;
195 if (errorText != NULL)
196 *errorText = 0;
197 }
198 else
199 {
200 int nErr = mysql_errno(pConn->pMySQL);
201 if (nErr == CR_SERVER_LOST || nErr == CR_CONNECTION_ERROR || nErr == CR_SERVER_GONE_ERROR) // CR_SERVER_GONE_ERROR - ???
202 {
203 *pdwError = DBERR_CONNECTION_LOST;
204 }
205 else
206 {
207 *pdwError = DBERR_OTHER_ERROR;
208 }
209
210 if (errorText != NULL)
211 {
212 nx_strncpy(errorText, mysql_error(pConn->pMySQL), DBDRV_MAX_ERROR_TEXT);
213 RemoveTrailingCRLF(errorText);
214 }
215 }
216
217 MutexUnlock(pConn->mutexQueryLock);
218 free(pszQueryUTF8);
219
220 return pResult;
221 }
222
223
224 //
225 // Get field length from result
226 //
227
228 extern "C" LONG EXPORT DrvGetFieldLength(DB_RESULT hResult, int iRow, int iColumn)
229 {
230 MYSQL_ROW row;
231
232 mysql_data_seek((MYSQL_RES *)hResult, iRow);
233 row = mysql_fetch_row((MYSQL_RES *)hResult);
234 return (row == NULL) ? (LONG)-1 : (LONG)strlen(row[iColumn]);
235 }
236
237
238 //
239 // Get field value from result
240 //
241
242 extern "C" WCHAR EXPORT *DrvGetField(DB_RESULT hResult, int iRow, int iColumn,
243 WCHAR *pBuffer, int nBufSize)
244 {
245 MYSQL_ROW row;
246 WCHAR *pRet = NULL;
247
248 mysql_data_seek((MYSQL_RES *)hResult, iRow);
249 row = mysql_fetch_row((MYSQL_RES *)hResult);
250 if (row != NULL)
251 {
252 if (row[iColumn] != NULL)
253 {
254 MultiByteToWideChar(CP_UTF8, 0, row[iColumn], -1, pBuffer, nBufSize);
255 pBuffer[nBufSize - 1] = 0;
256 pRet = pBuffer;
257 }
258 }
259 return pRet;
260 }
261
262
263 //
264 // Get number of rows in result
265 //
266
267 extern "C" int EXPORT DrvGetNumRows(DB_RESULT hResult)
268 {
269 return (hResult != NULL) ? (int)mysql_num_rows((MYSQL_RES *)hResult) : 0;
270 }
271
272
273 //
274 // Get column count in query result
275 //
276
277 extern "C" int EXPORT DrvGetColumnCount(DB_RESULT hResult)
278 {
279 return (hResult != NULL) ? (int)mysql_num_fields((MYSQL_RES *)hResult) : 0;
280 }
281
282
283 //
284 // Get column name in query result
285 //
286
287 extern "C" const char EXPORT *DrvGetColumnName(DB_RESULT hResult, int column)
288 {
289 MYSQL_FIELD *field;
290
291 if (hResult == NULL)
292 return NULL;
293
294 field = mysql_fetch_field_direct((MYSQL_RES *)hResult, column);
295 return (field != NULL) ? field->name : NULL;
296 }
297
298
299 //
300 // Free SELECT results
301 //
302
303 extern "C" void EXPORT DrvFreeResult(DB_RESULT hResult)
304 {
305 mysql_free_result((MYSQL_RES *)hResult);
306 }
307
308
309 //
310 // Perform asynchronous SELECT query
311 //
312
313 extern "C" DB_ASYNC_RESULT EXPORT DrvAsyncSelect(MYSQL_CONN *pConn, WCHAR *pwszQuery,
314 DWORD *pdwError, TCHAR *errorText)
315 {
316 MYSQL_ASYNC_RESULT *pResult = NULL;
317 char *pszQueryUTF8;
318
319 if (pConn == NULL)
320 {
321 *pdwError = DBERR_INVALID_HANDLE;
322 return NULL;
323 }
324
325 pszQueryUTF8 = UTF8StringFromWideString(pwszQuery);
326 MutexLock(pConn->mutexQueryLock, INFINITE);
327 if (mysql_query(pConn->pMySQL, pszQueryUTF8) == 0)
328 {
329 pResult = (MYSQL_ASYNC_RESULT *)malloc(sizeof(MYSQL_ASYNC_RESULT));
330 pResult->pConn = pConn;
331 pResult->pHandle = mysql_use_result(pConn->pMySQL);
332 if (pResult->pHandle != NULL)
333 {
334 pResult->bNoMoreRows = FALSE;
335 pResult->iNumCols = mysql_num_fields(pResult->pHandle);
336 pResult->pCurrRow = NULL;
337 pResult->pulColLengths = (unsigned long *)malloc(sizeof(unsigned long) * pResult->iNumCols);
338 }
339 else
340 {
341 free(pResult);
342 pResult = NULL;
343 }
344
345 *pdwError = DBERR_SUCCESS;
346 if (errorText != NULL)
347 *errorText = 0;
348 }
349 else
350 {
351 int nErr = mysql_errno(pConn->pMySQL);
352 if (nErr == CR_SERVER_LOST || nErr == CR_CONNECTION_ERROR || nErr == CR_SERVER_GONE_ERROR) // CR_SERVER_GONE_ERROR - ???
353 {
354 *pdwError = DBERR_CONNECTION_LOST;
355 }
356 else
357 {
358 *pdwError = DBERR_OTHER_ERROR;
359 }
360
361 if (errorText != NULL)
362 {
363 nx_strncpy(errorText, mysql_error(pConn->pMySQL), DBDRV_MAX_ERROR_TEXT);
364 RemoveTrailingCRLF(errorText);
365 }
366 }
367
368 if (pResult == NULL)
369 {
370 MutexUnlock(pConn->mutexQueryLock);
371 }
372 free(pszQueryUTF8);
373
374 return pResult;
375 }
376
377
378 //
379 // Fetch next result line from asynchronous SELECT results
380 //
381
382 extern "C" BOOL EXPORT DrvFetch(DB_ASYNC_RESULT hResult)
383 {
384 BOOL bResult = TRUE;
385
386 if (hResult == NULL)
387 {
388 bResult = FALSE;
389 }
390 else
391 {
392 // Try to fetch next row from server
393 ((MYSQL_ASYNC_RESULT *)hResult)->pCurrRow = mysql_fetch_row(((MYSQL_ASYNC_RESULT *)hResult)->pHandle);
394 if (((MYSQL_ASYNC_RESULT *)hResult)->pCurrRow == NULL)
395 {
396 ((MYSQL_ASYNC_RESULT *)hResult)->bNoMoreRows = TRUE;
397 bResult = FALSE;
398 MutexUnlock(((MYSQL_ASYNC_RESULT *)hResult)->pConn->mutexQueryLock);
399 }
400 else
401 {
402 unsigned long *pLen;
403
404 // Get column lengths for current row
405 pLen = mysql_fetch_lengths(((MYSQL_ASYNC_RESULT *)hResult)->pHandle);
406 if (pLen != NULL)
407 {
408 memcpy(((MYSQL_ASYNC_RESULT *)hResult)->pulColLengths, pLen,
409 sizeof(unsigned long) * ((MYSQL_ASYNC_RESULT *)hResult)->iNumCols);
410 }
411 else
412 {
413 memset(((MYSQL_ASYNC_RESULT *)hResult)->pulColLengths, 0,
414 sizeof(unsigned long) * ((MYSQL_ASYNC_RESULT *)hResult)->iNumCols);
415 }
416 }
417 }
418 return bResult;
419 }
420
421
422 //
423 // Get field length from async query result result
424 //
425
426 extern "C" LONG EXPORT DrvGetFieldLengthAsync(DB_ASYNC_RESULT hResult, int iColumn)
427 {
428 // Check if we have valid result handle
429 if (hResult == NULL)
430 return 0;
431
432 // Check if there are valid fetched row
433 if ((((MYSQL_ASYNC_RESULT *)hResult)->bNoMoreRows) ||
434 (((MYSQL_ASYNC_RESULT *)hResult)->pCurrRow == NULL))
435 return 0;
436
437 // Check if column number is valid
438 if ((iColumn < 0) || (iColumn >= ((MYSQL_ASYNC_RESULT *)hResult)->iNumCols))
439 return 0;
440
441 return ((MYSQL_ASYNC_RESULT *)hResult)->pulColLengths[iColumn];
442 }
443
444
445 //
446 // Get field from current row in async query result
447 //
448
449 extern "C" WCHAR EXPORT *DrvGetFieldAsync(DB_ASYNC_RESULT hResult, int iColumn,
450 WCHAR *pBuffer, int iBufSize)
451 {
452 int iLen;
453
454 // Check if we have valid result handle
455 if (hResult == NULL)
456 return NULL;
457
458 // Check if there are valid fetched row
459 if ((((MYSQL_ASYNC_RESULT *)hResult)->bNoMoreRows) ||
460 (((MYSQL_ASYNC_RESULT *)hResult)->pCurrRow == NULL))
461 return NULL;
462
463 // Check if column number is valid
464 if ((iColumn < 0) || (iColumn >= ((MYSQL_ASYNC_RESULT *)hResult)->iNumCols))
465 return NULL;
466
467 // Now get column data
468 iLen = min((int)((MYSQL_ASYNC_RESULT *)hResult)->pulColLengths[iColumn], iBufSize - 1);
469 if (iLen > 0)
470 {
471 MultiByteToWideChar(CP_UTF8, 0, ((MYSQL_ASYNC_RESULT *)hResult)->pCurrRow[iColumn],
472 iLen, pBuffer, iBufSize);
473 }
474 pBuffer[iLen] = 0;
475
476 return pBuffer;
477 }
478
479
480 //
481 // Get column count in async query result
482 //
483
484 extern "C" int EXPORT DrvGetColumnCountAsync(DB_ASYNC_RESULT hResult)
485 {
486 return ((hResult != NULL) && (((MYSQL_ASYNC_RESULT *)hResult)->pHandle != NULL))? (int)mysql_num_fields(((MYSQL_ASYNC_RESULT *)hResult)->pHandle) : 0;
487 }
488
489
490 //
491 // Get column name in async query result
492 //
493
494 extern "C" const char EXPORT *DrvGetColumnNameAsync(DB_ASYNC_RESULT hResult, int column)
495 {
496 MYSQL_FIELD *field;
497
498 if ((hResult == NULL) || (((MYSQL_ASYNC_RESULT *)hResult)->pHandle == NULL))
499 return NULL;
500
501 field = mysql_fetch_field_direct(((MYSQL_ASYNC_RESULT *)hResult)->pHandle, column);
502 return (field != NULL) ? field->name : NULL;
503 }
504
505
506 //
507 // Destroy result of async query
508 //
509
510 extern "C" void EXPORT DrvFreeAsyncResult(DB_ASYNC_RESULT hResult)
511 {
512 if (hResult != NULL)
513 {
514 // Check if all result rows fetched
515 if (!((MYSQL_ASYNC_RESULT *)hResult)->bNoMoreRows)
516 {
517 // Fetch remaining rows
518 while(mysql_fetch_row(((MYSQL_ASYNC_RESULT *)hResult)->pHandle) != NULL);
519
520 // Now we are ready for next query, so unlock query mutex
521 MutexUnlock(((MYSQL_ASYNC_RESULT *)hResult)->pConn->mutexQueryLock);
522 }
523
524 // Free allocated memory
525 mysql_free_result(((MYSQL_ASYNC_RESULT *)hResult)->pHandle);
526 safe_free(((MYSQL_ASYNC_RESULT *)hResult)->pulColLengths);
527 free(hResult);
528 }
529 }
530
531
532 //
533 // Begin transaction
534 //
535
536 extern "C" DWORD EXPORT DrvBegin(MYSQL_CONN *pConn)
537 {
538 return DrvQueryInternal(pConn, "BEGIN", NULL);
539 }
540
541
542 //
543 // Commit transaction
544 //
545
546 extern "C" DWORD EXPORT DrvCommit(MYSQL_CONN *pConn)
547 {
548 return DrvQueryInternal(pConn, "COMMIT", NULL);
549 }
550
551
552 //
553 // Rollback transaction
554 //
555
556 extern "C" DWORD EXPORT DrvRollback(MYSQL_CONN *pConn)
557 {
558 return DrvQueryInternal(pConn, "ROLLBACK", NULL);
559 }
560
561
562 //
563 // DLL Entry point
564 //
565
566 #ifdef _WIN32
567
568 BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
569 {
570 if (dwReason == DLL_PROCESS_ATTACH)
571 DisableThreadLibraryCalls(hInstance);
572 return TRUE;
573 }
574
575 #endif