Rollback from r3608 to r3606
[public/netxms.git] / src / server / dbdrv / mssql / mssql.cpp
1 /*
2 ** Microsoft SQL Server Database Driver
3 ** Copyright (C) 2004, 2005, 2006 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: mssql.cpp
20 **
21 **/
22
23 #include "mssqldrv.h"
24
25
26 //
27 // Constants
28 //
29
30 #define CONNECT_TIMEOUT 30
31
32
33 //
34 // DB library error handler
35 //
36
37 static int ErrorHandler(PDBPROCESS hProcess, int severity, int dberr,
38 int oserr, const char *dberrstr, const char *oserrstr)
39 {
40 MSDB_CONN *pConn;
41
42 if (hProcess != NULL)
43 {
44 pConn = (MSDB_CONN *)dbgetuserdata(hProcess);
45 if (pConn != NULL)
46 {
47 nx_strncpy(pConn->szErrorText, dberrstr, DBDRV_MAX_ERROR_TEXT);
48 if (dbdead(hProcess))
49 pConn->bProcessDead = TRUE;
50 }
51 }
52 return INT_CANCEL;
53 }
54
55
56 //
57 // Re-establish connection to server
58 //
59
60 static BOOL Reconnect(MSDB_CONN *pConn)
61 {
62 LOGINREC *loginrec;
63 PDBPROCESS hProcess;
64 BOOL bResult = FALSE;
65
66 loginrec = dblogin();
67 if (!strcmp(pConn->szLogin, "*"))
68 {
69 DBSETLSECURE(loginrec);
70 }
71 else
72 {
73 DBSETLUSER(loginrec, pConn->szLogin);
74 DBSETLPWD(loginrec, pConn->szPassword);
75 }
76 DBSETLAPP(loginrec, "NetXMS");
77 DBSETLTIME(loginrec, CONNECT_TIMEOUT);
78 hProcess = dbopen(loginrec, pConn->szHost);
79
80 if ((hProcess != NULL) && (pConn->szDatabase[0] != 0))
81 {
82 dbsetuserdata(hProcess, NULL);
83 if (dbuse(hProcess, pConn->szDatabase) != SUCCEED)
84 {
85 dbclose(hProcess);
86 hProcess = NULL;
87 }
88 }
89
90 if (hProcess != NULL)
91 {
92 dbclose(pConn->hProcess);
93 pConn->hProcess = hProcess;
94 pConn->bProcessDead = FALSE;
95 dbsetuserdata(hProcess, pConn);
96 bResult = TRUE;
97 }
98
99 return bResult;
100 }
101
102
103 //
104 // API version
105 //
106
107 extern "C" int EXPORT drvAPIVersion = DBDRV_API_VERSION;
108
109
110 //
111 // Initialize driver
112 //
113
114 extern "C" BOOL EXPORT DrvInit(char *szCmdLine)
115 {
116 BOOL bResult = FALSE;
117
118 if (dbinit() != NULL)
119 {
120 dberrhandle(ErrorHandler);
121 bResult = TRUE;
122 }
123 return bResult;
124 }
125
126
127 //
128 // Unload handler
129 //
130
131 extern "C" void EXPORT DrvUnload(void)
132 {
133 }
134
135
136 //
137 // Connect to database
138 //
139
140 extern "C" DB_CONNECTION EXPORT DrvConnect(char *szHost, char *szLogin,
141 char *szPassword, char *szDatabase)
142 {
143 LOGINREC *loginrec;
144 MSDB_CONN *pConn = NULL;
145 PDBPROCESS hProcess;
146
147 loginrec = dblogin();
148 if (!strcmp(szLogin, "*"))
149 {
150 DBSETLSECURE(loginrec);
151 }
152 else
153 {
154 DBSETLUSER(loginrec, szLogin);
155 DBSETLPWD(loginrec, szPassword);
156 }
157 DBSETLAPP(loginrec, "NetXMS");
158 DBSETLTIME(loginrec, CONNECT_TIMEOUT);
159 hProcess = dbopen(loginrec, szHost);
160
161 if (hProcess != NULL)
162 {
163 dbsetuserdata(hProcess, NULL);
164
165 // Change to specified database
166 if (szDatabase != NULL)
167 {
168 if (dbuse(hProcess, szDatabase) != SUCCEED)
169 {
170 dbclose(hProcess);
171 hProcess = NULL;
172 }
173 }
174
175 if (hProcess != NULL)
176 {
177 pConn = (MSDB_CONN *)malloc(sizeof(MSDB_CONN));
178 pConn->hProcess = hProcess;
179 pConn->mutexQueryLock = MutexCreate();
180 pConn->bProcessDead = FALSE;
181 nx_strncpy(pConn->szHost, szHost, MAX_CONN_STRING);
182 nx_strncpy(pConn->szLogin, szLogin, MAX_CONN_STRING);
183 nx_strncpy(pConn->szPassword, szPassword, MAX_CONN_STRING);
184 nx_strncpy(pConn->szDatabase, CHECK_NULL_EX(szDatabase), MAX_CONN_STRING);
185 pConn->szErrorText[0] = 0;
186
187 dbsetuserdata(hProcess, pConn);
188 }
189 }
190
191 return (DB_CONNECTION)pConn;
192 }
193
194
195 //
196 // Disconnect from database
197 //
198
199 extern "C" void EXPORT DrvDisconnect(MSDB_CONN *pConn)
200 {
201 if (pConn != NULL)
202 {
203 dbclose(pConn->hProcess);
204 MutexDestroy(pConn->mutexQueryLock);
205 free(pConn);
206 }
207 }
208
209
210 //
211 // Execute query
212 //
213
214 static BOOL ExecuteQuery(MSDB_CONN *pConn, char *pszQuery, TCHAR *errorText)
215 {
216 BOOL bResult;
217
218 dbcmd(pConn->hProcess, pszQuery);
219 if (dbsqlexec(pConn->hProcess) == SUCCEED)
220 {
221 bResult = TRUE;
222 }
223 else
224 {
225 if (pConn->bProcessDead)
226 {
227 if (Reconnect(pConn))
228 {
229 bResult = (dbsqlexec(pConn->hProcess) == SUCCEED);
230 }
231 else
232 {
233 bResult = FALSE;
234 }
235 }
236 else
237 {
238 bResult = FALSE;
239 }
240 }
241
242 if (errorText != NULL)
243 {
244 if (bResult)
245 {
246 *errorText = 0;
247 }
248 else
249 {
250 _tcscpy(errorText, pConn->szErrorText);
251 }
252 }
253
254 return bResult;
255 }
256
257
258 //
259 // Perform non-SELECT query
260 //
261
262 extern "C" DWORD EXPORT DrvQuery(MSDB_CONN *pConn, WCHAR *pwszQuery, TCHAR *errorText)
263 {
264 DWORD dwError;
265 char *pszQueryUTF8;
266
267 pszQueryUTF8 = UTF8StringFromWideString(pwszQuery);
268 MutexLock(pConn->mutexQueryLock, INFINITE);
269
270 if (ExecuteQuery(pConn, pszQueryUTF8, errorText))
271 {
272 if (dbresults(pConn->hProcess) == SUCCEED)
273 while(dbnextrow(pConn->hProcess) != NO_MORE_ROWS);
274 dwError = DBERR_SUCCESS;
275 }
276 else
277 {
278 dwError = pConn->bProcessDead ? DBERR_CONNECTION_LOST : DBERR_OTHER_ERROR;
279 }
280 MutexUnlock(pConn->mutexQueryLock);
281 free(pszQueryUTF8);
282 return dwError;
283 }
284
285
286 //
287 // Perform SELECT query
288 //
289
290 extern "C" DB_RESULT EXPORT DrvSelect(MSDB_CONN *pConn, WCHAR *pwszQuery, DWORD *pdwError, TCHAR *errorText)
291 {
292 MSDB_QUERY_RESULT *pResult = NULL;
293 int i, iCurrPos, iLen, *piColTypes;
294 void *pData;
295 char *pszQueryUTF8;
296
297 pszQueryUTF8 = UTF8StringFromWideString(pwszQuery);
298 MutexLock(pConn->mutexQueryLock, INFINITE);
299
300 if (ExecuteQuery(pConn, pszQueryUTF8, errorText))
301 {
302 // Process query results
303 if (dbresults(pConn->hProcess) == SUCCEED)
304 {
305 pResult = (MSDB_QUERY_RESULT *)malloc(sizeof(MSDB_QUERY_RESULT));
306 pResult->iNumRows = 0;
307 pResult->iNumCols = dbnumcols(pConn->hProcess);
308 pResult->pValues = NULL;
309
310 // Determine column types
311 piColTypes = (int *)malloc(pResult->iNumCols * sizeof(int));
312 for(i = 0; i < pResult->iNumCols; i++)
313 piColTypes[i] = dbcoltype(pConn->hProcess, i + 1);
314
315 // Retrieve data
316 iCurrPos = 0;
317 while(dbnextrow(pConn->hProcess) != NO_MORE_ROWS)
318 {
319 pResult->iNumRows++;
320 pResult->pValues = (char **)realloc(pResult->pValues,
321 sizeof(char *) * pResult->iNumRows * pResult->iNumCols);
322 for(i = 1; i <= pResult->iNumCols; i++, iCurrPos++)
323 {
324 pData = (void *)dbdata(pConn->hProcess, i);
325 if (pData != NULL)
326 {
327 switch(piColTypes[i - 1])
328 {
329 case SQLCHAR:
330 case SQLTEXT:
331 case SQLBINARY:
332 iLen = dbdatlen(pConn->hProcess, i);
333 pResult->pValues[iCurrPos] = (char *)malloc(iLen + 1);
334 if (iLen > 0)
335 memcpy(pResult->pValues[iCurrPos], (char *)pData, iLen);
336 pResult->pValues[iCurrPos][iLen] = 0;
337 break;
338 case SQLINT1:
339 pResult->pValues[iCurrPos] = (char *)malloc(4);
340 if (pData)
341 sprintf(pResult->pValues[iCurrPos], "%d", *((char *)pData));
342 break;
343 case SQLINT2:
344 pResult->pValues[iCurrPos] = (char *)malloc(8);
345 sprintf(pResult->pValues[iCurrPos], "%d", *((short *)pData));
346 break;
347 case SQLINT4:
348 pResult->pValues[iCurrPos] = (char *)malloc(16);
349 sprintf(pResult->pValues[iCurrPos], "%d", *((LONG *)pData));
350 break;
351 case SQLFLT4:
352 pResult->pValues[iCurrPos] = (char *)malloc(32);
353 sprintf(pResult->pValues[iCurrPos], "%f", *((float *)pData));
354 break;
355 case SQLFLT8:
356 pResult->pValues[iCurrPos] = (char *)malloc(32);
357 sprintf(pResult->pValues[iCurrPos], "%f", *((double *)pData));
358 break;
359 default: // Unknown data type
360 pResult->pValues[iCurrPos] = (char *)malloc(2);
361 pResult->pValues[iCurrPos][0] = 0;
362 break;
363 }
364 }
365 else
366 {
367 pResult->pValues[iCurrPos] = (char *)malloc(2);
368 pResult->pValues[iCurrPos][0] = 0;
369 }
370 }
371 }
372 }
373 }
374
375 if (pResult != NULL)
376 {
377 *pdwError = DBERR_SUCCESS;
378 }
379 else
380 {
381 *pdwError = pConn->bProcessDead ? DBERR_CONNECTION_LOST : DBERR_OTHER_ERROR;
382 }
383
384 MutexUnlock(pConn->mutexQueryLock);
385 free(pszQueryUTF8);
386 return (DB_RESULT)pResult;
387 }
388
389
390 //
391 // Get field length from result
392 //
393
394 extern "C" LONG EXPORT DrvGetFieldLength(DB_RESULT hResult, int iRow, int iColumn)
395 {
396 if ((iRow < 0) || (iRow >= ((MSDB_QUERY_RESULT *)hResult)->iNumRows) ||
397 (iColumn < 0) || (iColumn >= ((MSDB_QUERY_RESULT *)hResult)->iNumCols))
398 return -1;
399 return strlen(((MSDB_QUERY_RESULT *)hResult)->pValues[iRow * ((MSDB_QUERY_RESULT *)hResult)->iNumCols + iColumn]);
400 }
401
402
403 //
404 // Get field value from result
405 //
406
407 extern "C" WCHAR EXPORT *DrvGetField(DB_RESULT hResult, int iRow, int iColumn,
408 WCHAR *pBuffer, int nBufSize)
409 {
410 if ((iRow < 0) || (iRow >= ((MSDB_QUERY_RESULT *)hResult)->iNumRows) ||
411 (iColumn < 0) || (iColumn >= ((MSDB_QUERY_RESULT *)hResult)->iNumCols))
412 return NULL;
413 MultiByteToWideChar(CP_ACP, 0, ((MSDB_QUERY_RESULT *)hResult)->pValues[iRow * ((MSDB_QUERY_RESULT *)hResult)->iNumCols + iColumn],
414 -1, pBuffer, nBufSize);
415 pBuffer[nBufSize - 1] = 0;
416 return pBuffer;
417 }
418
419
420 //
421 // Get number of rows in result
422 //
423
424 extern "C" int EXPORT DrvGetNumRows(DB_RESULT hResult)
425 {
426 return (hResult != NULL) ? ((MSDB_QUERY_RESULT *)hResult)->iNumRows : 0;
427 }
428
429
430 //
431 // Free SELECT results
432 //
433
434 extern "C" void EXPORT DrvFreeResult(DB_RESULT hResult)
435 {
436 if (hResult != NULL)
437 {
438 int i, iNumValues;
439
440 iNumValues = ((MSDB_QUERY_RESULT *)hResult)->iNumRows * ((MSDB_QUERY_RESULT *)hResult)->iNumCols;
441 for(i = 0; i < iNumValues; i++)
442 free(((MSDB_QUERY_RESULT *)hResult)->pValues[i]);
443 if (((MSDB_QUERY_RESULT *)hResult)->pValues != NULL)
444 free(((MSDB_QUERY_RESULT *)hResult)->pValues);
445 free((MSDB_QUERY_RESULT *)hResult);
446 }
447 }
448
449
450 //
451 // Perform asynchronous SELECT query
452 //
453
454 extern "C" DB_ASYNC_RESULT EXPORT DrvAsyncSelect(MSDB_CONN *pConn, WCHAR *pwszQuery,
455 DWORD *pdwError, TCHAR *errorText)
456 {
457 MSDB_ASYNC_QUERY_RESULT *pResult = NULL;
458 char *pszQueryUTF8;
459 int i;
460
461 pszQueryUTF8 = UTF8StringFromWideString(pwszQuery);
462 MutexLock(pConn->mutexQueryLock, INFINITE);
463
464 if (ExecuteQuery(pConn, pszQueryUTF8, errorText))
465 {
466 // Prepare query results for processing
467 if (dbresults(pConn->hProcess) == SUCCEED)
468 {
469 // Fill in result information structure
470 pResult = (MSDB_ASYNC_QUERY_RESULT *)malloc(sizeof(MSDB_ASYNC_QUERY_RESULT));
471 pResult->pConnection = pConn;
472 pResult->bNoMoreRows = FALSE;
473 pResult->iNumCols = dbnumcols(pConn->hProcess);
474 pResult->piColTypes = (int *)malloc(sizeof(int) * pResult->iNumCols);
475
476 // Determine column types
477 for(i = 0; i < pResult->iNumCols; i++)
478 pResult->piColTypes[i] = dbcoltype(pConn->hProcess, i + 1);
479 }
480 }
481
482 if (pResult != NULL)
483 {
484 *pdwError = DBERR_SUCCESS;
485 }
486 else
487 {
488 *pdwError = pConn->bProcessDead ? DBERR_CONNECTION_LOST : DBERR_OTHER_ERROR;
489 MutexUnlock(pConn->mutexQueryLock);
490 }
491 free(pszQueryUTF8);
492 return pResult;
493 }
494
495
496 //
497 // Fetch next result line from asynchronous SELECT results
498 //
499
500 extern "C" BOOL EXPORT DrvFetch(DB_ASYNC_RESULT hResult)
501 {
502 BOOL bResult = TRUE;
503
504 if (hResult == NULL)
505 {
506 bResult = FALSE;
507 }
508 else
509 {
510 // Try to fetch next row from server
511 if (dbnextrow(((MSDB_ASYNC_QUERY_RESULT *)hResult)->pConnection->hProcess) == NO_MORE_ROWS)
512 {
513 ((MSDB_ASYNC_QUERY_RESULT *)hResult)->bNoMoreRows = TRUE;
514 bResult = FALSE;
515 MutexUnlock(((MSDB_ASYNC_QUERY_RESULT *)hResult)->pConnection->mutexQueryLock);
516 }
517 }
518 return bResult;
519 }
520
521
522 //
523 // Get field from current row in async query result
524 //
525
526 extern "C" WCHAR EXPORT *DrvGetFieldAsync(DB_ASYNC_RESULT hResult, int iColumn,
527 WCHAR *pBuffer, int iBufSize)
528 {
529 void *pData;
530 int nLen;
531
532 // Check if we have valid result handle
533 if (hResult == NULL)
534 return NULL;
535
536 // Check if there are valid fetched row
537 if (((MSDB_ASYNC_QUERY_RESULT *)hResult)->bNoMoreRows)
538 return NULL;
539
540 // Now get column data
541 pData = (void *)dbdata(((MSDB_ASYNC_QUERY_RESULT *)hResult)->pConnection->hProcess, iColumn + 1);
542 if (pData != NULL)
543 {
544 switch(((MSDB_ASYNC_QUERY_RESULT *)hResult)->piColTypes[iColumn])
545 {
546 case SQLCHAR:
547 case SQLTEXT:
548 case SQLBINARY:
549 nLen = MultiByteToWideChar(CP_ACP, 0, (char *)pData,
550 dbdatlen(((MSDB_ASYNC_QUERY_RESULT *)hResult)->pConnection->hProcess, iColumn + 1),
551 pBuffer, iBufSize);
552 pBuffer[nLen] = 0;
553 break;
554 case SQLINT1:
555 swprintf(pBuffer, L"%d", *((char *)pData));
556 break;
557 case SQLINT2:
558 swprintf(pBuffer, L"%d", *((short *)pData));
559 break;
560 case SQLINT4:
561 swprintf(pBuffer, L"%d", *((LONG *)pData));
562 break;
563 case SQLFLT4:
564 swprintf(pBuffer, L"%f", *((float *)pData));
565 break;
566 case SQLFLT8:
567 swprintf(pBuffer, L"%f", *((double *)pData));
568 break;
569 default: // Unknown data type
570 pBuffer[0] = 0;
571 break;
572 }
573 }
574 else
575 {
576 pBuffer[0] = 0;
577 }
578
579 return pBuffer;
580 }
581
582
583 //
584 // Destroy result of async query
585 //
586
587 extern "C" void EXPORT DrvFreeAsyncResult(DB_ASYNC_RESULT hResult)
588 {
589 if (hResult != NULL)
590 {
591 // Check if all result rows fetchef
592 if (!((MSDB_ASYNC_QUERY_RESULT *)hResult)->bNoMoreRows)
593 {
594 // Fetch remaining rows
595 while(dbnextrow(((MSDB_ASYNC_QUERY_RESULT *)hResult)->pConnection->hProcess) != NO_MORE_ROWS);
596
597 // Now we are ready for next query, so unlock query mutex
598 MutexUnlock(((MSDB_ASYNC_QUERY_RESULT *)hResult)->pConnection->mutexQueryLock);
599 }
600
601 // Free allocated memory
602 if (((MSDB_ASYNC_QUERY_RESULT *)hResult)->piColTypes != NULL)
603 free(((MSDB_ASYNC_QUERY_RESULT *)hResult)->piColTypes);
604 free(hResult);
605 }
606 }
607
608
609 //
610 // Begin transaction
611 //
612
613 extern "C" DWORD EXPORT DrvBegin(MSDB_CONN *pConn)
614 {
615 return DrvQuery(pConn, L"BEGIN TRANSACTION", NULL);
616 }
617
618
619 //
620 // Commit transaction
621 //
622
623 extern "C" DWORD EXPORT DrvCommit(MSDB_CONN *pConn)
624 {
625 return DrvQuery(pConn, L"COMMIT", NULL);
626 }
627
628
629 //
630 // Rollback transaction
631 //
632
633 extern "C" DWORD EXPORT DrvRollback(MSDB_CONN *pConn)
634 {
635 return DrvQuery(pConn, L"ROLLBACK", NULL);
636 }
637
638
639 //
640 // DLL Entry point
641 //
642
643 #ifdef _WIN32
644
645 BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
646 {
647 if (dwReason == DLL_PROCESS_ATTACH)
648 DisableThreadLibraryCalls(hInstance);
649 return TRUE;
650 }
651
652 #endif