c382ff3c6413da51644e21f7c6be805b05072de4
[public/netxms.git] / src / db / dbdrv / db2 / db2.cpp
1 /*
2 ** DB2 Database Driver
3 ** Copyright (C) 2010-2012 Raden Solutinos
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 **/
20
21 #include "db2drv.h"
22
23 DECLARE_DRIVER_HEADER("DB2")
24
25
26 //
27 // Constants
28 //
29
30 #define DATA_BUFFER_SIZE 65536
31
32
33 //
34 // Convert DB2 state to NetXMS database error code and get error text
35 //
36
37 static DWORD GetSQLErrorInfo(SQLSMALLINT nHandleType, SQLHANDLE hHandle, NETXMS_WCHAR *errorText)
38 {
39 SQLRETURN nRet;
40 SQLSMALLINT nChars;
41 DWORD dwError;
42 SQLWCHAR buffer[DBDRV_MAX_ERROR_TEXT];
43
44 // Get state information and convert it to NetXMS database error code
45 nRet = SQLGetDiagFieldW(nHandleType, hHandle, 1, SQL_DIAG_SQLSTATE, buffer, 16, &nChars);
46 if (nRet == SQL_SUCCESS)
47 {
48 #if UNICODE_UCS2
49 if (
50 (!wcscmp(buffer, L"08003")) || // Connection does not exist
51 (!wcscmp(buffer, L"08S01")) || // Communication link failure
52 (!wcscmp(buffer, L"HYT00")) || // Timeout expired
53 (!wcscmp(buffer, L"HYT01")) || // Connection timeout expired
54 (!wcscmp(buffer, L"08506"))) // SQL30108N: A connection failed but has been re-established.
55 #else
56 char state[16];
57 ucs2_to_mb(buffer, -1, state, 16);
58 if (
59 (!strcmp(state, "08003")) || // Connection does not exist
60 (!strcmp(state, "08S01")) || // Communication link failure
61 (!strcmp(state, "HYT00")) || // Timeout expired
62 (!strcmp(state, "HYT01")) || // Connection timeout expired
63 (!strcmp(state, "08506"))) // SQL30108N: A connection failed but has been re-established.
64 #endif
65 {
66 dwError = DBERR_CONNECTION_LOST;
67 }
68 else
69 {
70 dwError = DBERR_OTHER_ERROR;
71 }
72 }
73 else
74 {
75 dwError = DBERR_OTHER_ERROR;
76 }
77
78 // Get error message
79 if (errorText != NULL)
80 {
81 #if UNICODE_UCS2
82 nRet = SQLGetDiagFieldW(nHandleType, hHandle, 1, SQL_DIAG_MESSAGE_TEXT, errorText, DBDRV_MAX_ERROR_TEXT, &nChars);
83 #else
84 nRet = SQLGetDiagFieldW(nHandleType, hHandle, 1, SQL_DIAG_MESSAGE_TEXT, buffer, DBDRV_MAX_ERROR_TEXT, &nChars);
85 #endif
86 if (nRet == SQL_SUCCESS)
87 {
88 #if UNICODE_UCS4
89 buffer[DBDRV_MAX_ERROR_TEXT - 1] = 0;
90 ucs2_to_ucs4(buffer, -1, errorText, DBDRV_MAX_ERROR_TEXT);
91 #endif
92 RemoveTrailingCRLFW(errorText);
93 }
94 else
95 {
96 wcscpy(errorText, L"Unable to obtain description for this error");
97 }
98 }
99
100 return dwError;
101 }
102
103
104 //
105 // Clear any pending result sets on given statement
106 //
107
108 static void ClearPendingResults(SQLHSTMT statement)
109 {
110 while(1)
111 {
112 SQLRETURN rc = SQLMoreResults(statement);
113 if ((rc != SQL_SUCCESS) && (rc != SQL_SUCCESS_WITH_INFO))
114 {
115 break;
116 }
117 }
118 }
119
120
121 //
122 // Prepare string for using in SQL query - enclose in quotes and escape as needed
123 //
124
125 extern "C" WCHAR EXPORT *DrvPrepareStringW(const WCHAR *str)
126 {
127 int len = (int)wcslen(str) + 3; // + two quotes and \0 at the end
128 int bufferSize = len + 128;
129 WCHAR *out = (WCHAR *)malloc(bufferSize * sizeof(WCHAR));
130 out[0] = L'\'';
131
132 const WCHAR *src = str;
133 int outPos;
134 for(outPos = 1; *src != 0; src++)
135 {
136 if (*src == L'\'')
137 {
138 len++;
139 if (len >= bufferSize)
140 {
141 bufferSize += 128;
142 out = (WCHAR *)realloc(out, bufferSize * sizeof(WCHAR));
143 }
144 out[outPos++] = L'\'';
145 out[outPos++] = L'\'';
146 }
147 else
148 {
149 out[outPos++] = *src;
150 }
151 }
152 out[outPos++] = L'\'';
153 out[outPos++] = 0;
154
155 return out;
156 }
157
158 extern "C" char EXPORT *DrvPrepareStringA(const char *str)
159 {
160 int len = (int)strlen(str) + 3; // + two quotes and \0 at the end
161 int bufferSize = len + 128;
162 char *out = (char *)malloc(bufferSize);
163 out[0] = '\'';
164
165 const char *src = str;
166 int outPos;
167 for(outPos = 1; *src != 0; src++)
168 {
169 if (*src == '\'')
170 {
171 len++;
172 if (len >= bufferSize)
173 {
174 bufferSize += 128;
175 out = (char *)realloc(out, bufferSize);
176 }
177 out[outPos++] = '\'';
178 out[outPos++] = '\'';
179 }
180 else
181 {
182 out[outPos++] = *src;
183 }
184 }
185 out[outPos++] = '\'';
186 out[outPos++] = 0;
187
188 return out;
189 }
190
191
192 //
193 // Initialize driver
194 //
195
196 extern "C" BOOL EXPORT DrvInit(const char *cmdLine)
197 {
198 return TRUE;
199 }
200
201
202 //
203 // Unload handler
204 //
205
206 extern "C" void EXPORT DrvUnload()
207 {
208 }
209
210
211 //
212 // Connect to database
213 // pszHost should be set to DB2 source name, and pszDatabase is ignored
214 //
215
216 extern "C" DBDRV_CONNECTION EXPORT DrvConnect(char *pszHost, char *pszLogin,
217 char *pszPassword, char *pszDatabase, const char *schema, NETXMS_WCHAR *errorText)
218 {
219 long iResult;
220 DB2DRV_CONN *pConn;
221
222 // Allocate our connection structure
223 pConn = (DB2DRV_CONN *)malloc(sizeof(DB2DRV_CONN));
224
225 // Allocate environment
226 iResult = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &pConn->sqlEnv);
227 if ((iResult != SQL_SUCCESS) && (iResult != SQL_SUCCESS_WITH_INFO))
228 {
229 wcscpy(errorText, L"Cannot allocate environment handle");
230 goto connect_failure_0;
231 }
232
233 // Set required DB2 version
234 iResult = SQLSetEnvAttr(pConn->sqlEnv, SQL_ATTR_ODBC_VERSION, (void *)SQL_OV_ODBC3, 0);
235 if ((iResult != SQL_SUCCESS) && (iResult != SQL_SUCCESS_WITH_INFO))
236 {
237 wcscpy(errorText, L"Call to SQLSetEnvAttr failed");
238 goto connect_failure_1;
239 }
240
241 // Allocate connection handle, set timeout
242 iResult = SQLAllocHandle(SQL_HANDLE_DBC, pConn->sqlEnv, &pConn->sqlConn);
243 if ((iResult != SQL_SUCCESS) && (iResult != SQL_SUCCESS_WITH_INFO))
244 {
245 wcscpy(errorText, L"Cannot allocate connection handle");
246 goto connect_failure_1;
247 }
248 SQLSetConnectAttr(pConn->sqlConn, SQL_ATTR_LOGIN_TIMEOUT, (SQLPOINTER *)15, 0);
249 SQLSetConnectAttr(pConn->sqlConn, SQL_ATTR_CONNECTION_TIMEOUT, (SQLPOINTER *)30, 0);
250
251 // Connect to the datasource
252 iResult = SQLConnect(pConn->sqlConn, (SQLCHAR *)pszHost, SQL_NTS,
253 (SQLCHAR *)pszLogin, SQL_NTS, (SQLCHAR *)pszPassword, SQL_NTS);
254 if ((iResult != SQL_SUCCESS) && (iResult != SQL_SUCCESS_WITH_INFO))
255 {
256 GetSQLErrorInfo(SQL_HANDLE_DBC, pConn->sqlConn, errorText);
257 goto connect_failure_2;
258 }
259
260 // Set current schema
261 if ((schema != NULL) && (schema[0] != 0))
262 {
263 char query[256];
264 snprintf(query, 256, "SET CURRENT SCHEMA = '%s'", schema);
265
266 iResult = SQLAllocHandle(SQL_HANDLE_STMT, pConn->sqlConn, &pConn->sqlStatement);
267 if ((iResult == SQL_SUCCESS) || (iResult == SQL_SUCCESS_WITH_INFO))
268 {
269 // Execute statement
270 SQLWCHAR *temp = UCS2StringFromMBString(query);
271 iResult = SQLExecDirectW(pConn->sqlStatement, temp, SQL_NTS);
272 free(temp);
273 if ((iResult != SQL_SUCCESS) &&
274 (iResult != SQL_SUCCESS_WITH_INFO) &&
275 (iResult != SQL_NO_DATA))
276 {
277 GetSQLErrorInfo(SQL_HANDLE_STMT, pConn->sqlStatement, errorText);
278 goto connect_failure_3;
279 }
280 SQLFreeHandle(SQL_HANDLE_STMT, pConn->sqlStatement);
281 }
282 else
283 {
284 GetSQLErrorInfo(SQL_HANDLE_DBC, pConn->sqlConn, errorText);
285 goto connect_failure_2;
286 }
287 }
288
289 // Create mutex
290 pConn->mutexQuery = MutexCreate();
291
292 // Success
293 return (DBDRV_CONNECTION)pConn;
294
295 // Handle failures
296 connect_failure_3:
297 SQLFreeHandle(SQL_HANDLE_STMT, pConn->sqlStatement);
298
299 connect_failure_2:
300 SQLFreeHandle(SQL_HANDLE_DBC, pConn->sqlConn);
301
302 connect_failure_1:
303 SQLFreeHandle(SQL_HANDLE_ENV, pConn->sqlEnv);
304
305 connect_failure_0:
306 free(pConn);
307 return NULL;
308 }
309
310
311 //
312 // Disconnect from database
313 //
314
315 extern "C" void EXPORT DrvDisconnect(DB2DRV_CONN *pConn)
316 {
317 MutexLock(pConn->mutexQuery);
318 MutexUnlock(pConn->mutexQuery);
319 SQLDisconnect(pConn->sqlConn);
320 SQLFreeHandle(SQL_HANDLE_DBC, pConn->sqlConn);
321 SQLFreeHandle(SQL_HANDLE_ENV, pConn->sqlEnv);
322 MutexDestroy(pConn->mutexQuery);
323 free(pConn);
324 }
325
326 //
327 // Prepare statement
328 //
329
330 extern "C" DBDRV_STATEMENT EXPORT DrvPrepare(DB2DRV_CONN *pConn, NETXMS_WCHAR *pwszQuery, DWORD *pdwError, NETXMS_WCHAR *errorText)
331 {
332 long iResult;
333 SQLHSTMT statement;
334 DB2DRV_STATEMENT *result;
335
336 MutexLock(pConn->mutexQuery);
337
338 // Allocate statement handle
339 iResult = SQLAllocHandle(SQL_HANDLE_STMT, pConn->sqlConn, &statement);
340 if ((iResult == SQL_SUCCESS) || (iResult == SQL_SUCCESS_WITH_INFO))
341 {
342 // Prepare statement
343 #if defined(_WIN32) || defined(UNICODE_UCS2)
344 iResult = SQLPrepareW(statement, (SQLWCHAR *)pwszQuery, SQL_NTS);
345 #else
346 SQLWCHAR *temp = UCS2StringFromUCS4String(pwszQuery);
347 iResult = SQLPrepareW(statement, temp, SQL_NTS);
348 free(temp);
349 #endif
350 if ((iResult == SQL_SUCCESS) ||
351 (iResult == SQL_SUCCESS_WITH_INFO))
352 {
353 result = (DB2DRV_STATEMENT *)malloc(sizeof(DB2DRV_STATEMENT));
354 result->handle = statement;
355 result->buffers = new Array(0, 16, true);
356 result->connection = pConn;
357 *pdwError = DBERR_SUCCESS;
358 }
359 else
360 {
361 *pdwError = GetSQLErrorInfo(SQL_HANDLE_STMT, statement, errorText);
362 SQLFreeHandle(SQL_HANDLE_STMT, statement);
363 result = NULL;
364 }
365 }
366 else
367 {
368 *pdwError = GetSQLErrorInfo(SQL_HANDLE_DBC, pConn->sqlConn, errorText);
369 result = NULL;
370 }
371
372 MutexUnlock(pConn->mutexQuery);
373 return result;
374 }
375
376
377 //
378 // Bind parameter to statement
379 //
380
381 extern "C" void EXPORT DrvBind(DB2DRV_STATEMENT *statement, int pos, int sqlType, int cType, void *buffer, int allocType)
382 {
383 static SQLSMALLINT odbcSqlType[] = { SQL_VARCHAR, SQL_INTEGER, SQL_BIGINT, SQL_DOUBLE, SQL_LONGVARCHAR };
384 static SQLSMALLINT odbcCType[] = { SQL_C_WCHAR, SQL_C_SLONG, SQL_C_ULONG, SQL_C_SBIGINT, SQL_C_UBIGINT, SQL_C_DOUBLE };
385 static DWORD bufferSize[] = { 0, sizeof(LONG), sizeof(DWORD), sizeof(INT64), sizeof(QWORD), sizeof(double) };
386
387 int length = (int)wcslen((WCHAR *)buffer) + 1;
388
389 SQLPOINTER sqlBuffer;
390 switch(allocType)
391 {
392 case DB_BIND_STATIC:
393 #if defined(_WIN32) || defined(UNICODE_UCS2)
394 sqlBuffer = buffer;
395 #else
396 if (cType == DB_CTYPE_STRING)
397 {
398 sqlBuffer = UCS2StringFromUCS4String((WCHAR *)buffer);
399 statement->buffers->add(sqlBuffer);
400 }
401 else
402 {
403 sqlBuffer = buffer;
404 }
405 #endif
406 break;
407 case DB_BIND_DYNAMIC:
408 #if defined(_WIN32) || defined(UNICODE_UCS2)
409 sqlBuffer = buffer;
410 #else
411 if (cType == DB_CTYPE_STRING)
412 {
413 sqlBuffer = UCS2StringFromUCS4String((WCHAR *)buffer);
414 free(buffer);
415 }
416 else
417 {
418 sqlBuffer = buffer;
419 }
420 #endif
421 statement->buffers->add(sqlBuffer);
422 break;
423 case DB_BIND_TRANSIENT:
424 #if defined(_WIN32) || defined(UNICODE_UCS2)
425 sqlBuffer = nx_memdup(buffer, (cType == DB_CTYPE_STRING) ? (DWORD)(length * sizeof(WCHAR)) : bufferSize[cType]);
426 #else
427 if (cType == DB_CTYPE_STRING)
428 {
429 sqlBuffer = UCS2StringFromUCS4String((WCHAR *)buffer);
430 }
431 else
432 {
433 sqlBuffer = nx_memdup(buffer, bufferSize[cType]);
434 }
435 #endif
436 statement->buffers->add(sqlBuffer);
437 break;
438 default:
439 return; // Invalid call
440 }
441 SQLBindParameter(statement->handle, pos, SQL_PARAM_INPUT, odbcCType[cType], odbcSqlType[sqlType],
442 (cType == DB_CTYPE_STRING) ? length : 0, 0, sqlBuffer, 0, NULL);
443 }
444
445
446 //
447 // Execute prepared statement
448 //
449
450 extern "C" DWORD EXPORT DrvExecute(DB2DRV_CONN *pConn, DB2DRV_STATEMENT *statement, NETXMS_WCHAR *errorText)
451 {
452 DWORD dwResult;
453
454 MutexLock(pConn->mutexQuery);
455 long rc = SQLExecute(statement->handle);
456 if (
457 (rc == SQL_SUCCESS) ||
458 (rc == SQL_SUCCESS_WITH_INFO) ||
459 (rc == SQL_NO_DATA))
460 {
461 ClearPendingResults(statement->handle);
462 dwResult = DBERR_SUCCESS;
463 }
464 else
465 {
466 dwResult = GetSQLErrorInfo(SQL_HANDLE_STMT, statement->handle, errorText);
467 }
468 MutexUnlock(pConn->mutexQuery);
469 return dwResult;
470 }
471
472
473 //
474 // Destroy prepared statement
475 //
476
477 extern "C" void EXPORT DrvFreeStatement(DB2DRV_STATEMENT *statement)
478 {
479 if (statement == NULL)
480 {
481 return;
482 }
483
484 MutexLock(statement->connection->mutexQuery);
485 SQLFreeHandle(SQL_HANDLE_STMT, statement->handle);
486 MutexUnlock(statement->connection->mutexQuery);
487 delete statement->buffers;
488 free(statement);
489 }
490
491
492 //
493 // Perform non-SELECT query
494 //
495
496 extern "C" DWORD EXPORT DrvQuery(DB2DRV_CONN *pConn, NETXMS_WCHAR *pwszQuery, NETXMS_WCHAR *errorText)
497 {
498 long iResult;
499 DWORD dwResult;
500
501 MutexLock(pConn->mutexQuery);
502
503 // Allocate statement handle
504 iResult = SQLAllocHandle(SQL_HANDLE_STMT, pConn->sqlConn, &pConn->sqlStatement);
505 if ((iResult == SQL_SUCCESS) || (iResult == SQL_SUCCESS_WITH_INFO))
506 {
507 // Execute statement
508 #if defined(_WIN32) || defined(UNICODE_UCS2)
509 iResult = SQLExecDirectW(pConn->sqlStatement, (SQLWCHAR *)pwszQuery, SQL_NTS);
510 #else
511 SQLWCHAR *temp = UCS2StringFromUCS4String(pwszQuery);
512 iResult = SQLExecDirectW(pConn->sqlStatement, temp, SQL_NTS);
513 free(temp);
514 #endif
515 if (
516 (iResult == SQL_SUCCESS) ||
517 (iResult == SQL_SUCCESS_WITH_INFO) ||
518 (iResult == SQL_NO_DATA))
519 {
520 dwResult = DBERR_SUCCESS;
521 }
522 else
523 {
524 dwResult = GetSQLErrorInfo(SQL_HANDLE_STMT, pConn->sqlStatement, errorText);
525 }
526 SQLFreeHandle(SQL_HANDLE_STMT, pConn->sqlStatement);
527 }
528 else
529 {
530 dwResult = GetSQLErrorInfo(SQL_HANDLE_DBC, pConn->sqlConn, errorText);
531 }
532
533 MutexUnlock(pConn->mutexQuery);
534 return dwResult;
535 }
536
537
538 //
539 // Process results of SELECT query
540 //
541
542 static DB2DRV_QUERY_RESULT *ProcessSelectResults(SQLHSTMT statement)
543 {
544 // Allocate result buffer and determine column info
545 DB2DRV_QUERY_RESULT *pResult = (DB2DRV_QUERY_RESULT *)malloc(sizeof(DB2DRV_QUERY_RESULT));
546 short wNumCols;
547 SQLNumResultCols(statement, &wNumCols);
548 pResult->iNumCols = wNumCols;
549 pResult->iNumRows = 0;
550 pResult->pValues = NULL;
551
552 BYTE *pDataBuffer = (BYTE *)malloc(DATA_BUFFER_SIZE);
553
554 // Get column names
555 pResult->columnNames = (char **)malloc(sizeof(char *) * pResult->iNumCols);
556 for(int i = 0; i < (int)pResult->iNumCols; i++)
557 {
558 UCS2CHAR name[256];
559 SQLSMALLINT len;
560
561 SQLRETURN iResult = SQLColAttributeW(statement, (SQLSMALLINT)(i + 1), SQL_DESC_NAME, (SQLPOINTER)name, 256, &len, NULL);
562 if (
563 (iResult == SQL_SUCCESS) ||
564 (iResult == SQL_SUCCESS_WITH_INFO))
565 {
566 name[len] = 0;
567 pResult->columnNames[i] = MBStringFromUCS2String(name);
568 }
569 else
570 {
571 pResult->columnNames[i] = strdup("");
572 }
573 }
574
575 // Fetch all data
576 long iCurrValue = 0;
577 SQLRETURN iResult;
578 while(iResult = SQLFetch(statement), (iResult == SQL_SUCCESS) || (iResult == SQL_SUCCESS_WITH_INFO))
579 {
580 pResult->iNumRows++;
581 pResult->pValues = (NETXMS_WCHAR **)realloc(pResult->pValues, sizeof(NETXMS_WCHAR *) * (pResult->iNumRows * pResult->iNumCols));
582 for(int i = 1; i <= (int)pResult->iNumCols; i++)
583 {
584 SQLLEN iDataSize;
585
586 pDataBuffer[0] = 0;
587 iResult = SQLGetData(statement, (short)i, SQL_C_WCHAR, pDataBuffer, DATA_BUFFER_SIZE, &iDataSize);
588 if (iDataSize != SQL_NULL_DATA)
589 {
590 #if defined(_WIN32) || defined(UNICODE_UCS2)
591 pResult->pValues[iCurrValue++] = wcsdup((const WCHAR *)pDataBuffer);
592 #else
593 pResult->pValues[iCurrValue++] = UCS4StringFromUCS2String((const UCS2CHAR *)pDataBuffer);
594 #endif
595 }
596 else
597 {
598 pResult->pValues[iCurrValue++] = wcsdup(L"");
599 }
600 }
601 }
602
603 free(pDataBuffer);
604 return pResult;
605 }
606
607
608 //
609 // Perform SELECT query
610 //
611
612 extern "C" DBDRV_RESULT EXPORT DrvSelect(DB2DRV_CONN *pConn, NETXMS_WCHAR *pwszQuery, DWORD *pdwError, NETXMS_WCHAR *errorText)
613 {
614 DB2DRV_QUERY_RESULT *pResult = NULL;
615
616 MutexLock(pConn->mutexQuery);
617
618 // Allocate statement handle
619 long iResult = SQLAllocHandle(SQL_HANDLE_STMT, pConn->sqlConn, &pConn->sqlStatement);
620 if ((iResult == SQL_SUCCESS) || (iResult == SQL_SUCCESS_WITH_INFO))
621 {
622 // Execute statement
623 #if defined(_WIN32) || defined(UNICODE_UCS2)
624 iResult = SQLExecDirectW(pConn->sqlStatement, (SQLWCHAR *)pwszQuery, SQL_NTS);
625 #else
626 SQLWCHAR *temp = UCS2StringFromUCS4String(pwszQuery);
627 iResult = SQLExecDirectW(pConn->sqlStatement, temp, SQL_NTS);
628 free(temp);
629 #endif
630 if (
631 (iResult == SQL_SUCCESS) ||
632 (iResult == SQL_SUCCESS_WITH_INFO))
633 {
634 pResult = ProcessSelectResults(pConn->sqlStatement);
635 *pdwError = DBERR_SUCCESS;
636 }
637 else
638 {
639 *pdwError = GetSQLErrorInfo(SQL_HANDLE_STMT, pConn->sqlStatement, errorText);
640 }
641 SQLFreeHandle(SQL_HANDLE_STMT, pConn->sqlStatement);
642 }
643 else
644 {
645 *pdwError = GetSQLErrorInfo(SQL_HANDLE_DBC, pConn->sqlConn, errorText);
646 }
647
648 MutexUnlock(pConn->mutexQuery);
649 return pResult;
650 }
651
652
653 //
654 // Perform SELECT query using prepared statement
655 //
656
657 extern "C" DBDRV_RESULT EXPORT DrvSelectPrepared(DB2DRV_CONN *pConn, DB2DRV_STATEMENT *statement, DWORD *pdwError, NETXMS_WCHAR *errorText)
658 {
659 DB2DRV_QUERY_RESULT *pResult = NULL;
660
661 MutexLock(pConn->mutexQuery);
662 long rc = SQLExecute(statement->handle);
663 if (
664 (rc == SQL_SUCCESS) ||
665 (rc == SQL_SUCCESS_WITH_INFO))
666 {
667 pResult = ProcessSelectResults(statement->handle);
668 *pdwError = DBERR_SUCCESS;
669 }
670 else
671 {
672 *pdwError = GetSQLErrorInfo(SQL_HANDLE_STMT, statement->handle, errorText);
673 }
674 MutexUnlock(pConn->mutexQuery);
675 return pResult;
676 }
677
678
679 //
680 // Get field length from result
681 //
682
683 extern "C" LONG EXPORT DrvGetFieldLength(DB2DRV_QUERY_RESULT *pResult, int iRow, int iColumn)
684 {
685 LONG nLen = -1;
686
687 if (pResult != NULL)
688 {
689 if ((iRow < pResult->iNumRows) && (iRow >= 0) &&
690 (iColumn < pResult->iNumCols) && (iColumn >= 0))
691 {
692 nLen = (LONG)wcslen(pResult->pValues[iRow * pResult->iNumCols + iColumn]);
693 }
694 }
695 return nLen;
696 }
697
698
699 //
700 // Get field value from result
701 //
702
703 extern "C" NETXMS_WCHAR EXPORT *DrvGetField(DB2DRV_QUERY_RESULT *pResult, int iRow, int iColumn,
704 NETXMS_WCHAR *pBuffer, int nBufSize)
705 {
706 NETXMS_WCHAR *pValue = NULL;
707
708 if (pResult != NULL)
709 {
710 if ((iRow < pResult->iNumRows) && (iRow >= 0) &&
711 (iColumn < pResult->iNumCols) && (iColumn >= 0))
712 {
713 #ifdef _WIN32
714 wcsncpy_s(pBuffer, nBufSize, pResult->pValues[iRow * pResult->iNumCols + iColumn], _TRUNCATE);
715 #else
716 wcsncpy(pBuffer, pResult->pValues[iRow * pResult->iNumCols + iColumn], nBufSize);
717 pBuffer[nBufSize - 1] = 0;
718 #endif
719 pValue = pBuffer;
720 }
721 }
722 return pValue;
723 }
724
725
726 //
727 // Get number of rows in result
728 //
729
730 extern "C" int EXPORT DrvGetNumRows(DB2DRV_QUERY_RESULT *pResult)
731 {
732 return (pResult != NULL) ? pResult->iNumRows : 0;
733 }
734
735
736 //
737 // Get column count in query result
738 //
739
740 extern "C" int EXPORT DrvGetColumnCount(DB2DRV_QUERY_RESULT *pResult)
741 {
742 return (pResult != NULL) ? pResult->iNumCols : 0;
743 }
744
745
746 //
747 // Get column name in query result
748 //
749
750 extern "C" const char EXPORT *DrvGetColumnName(DB2DRV_QUERY_RESULT *pResult, int column)
751 {
752 return ((pResult != NULL) && (column >= 0) && (column < pResult->iNumCols)) ? pResult->columnNames[column] : NULL;
753 }
754
755
756 //
757 // Free SELECT results
758 //
759
760 extern "C" void EXPORT DrvFreeResult(DB2DRV_QUERY_RESULT *pResult)
761 {
762 if (pResult != NULL)
763 {
764 int i, iNumValues;
765
766 iNumValues = pResult->iNumCols * pResult->iNumRows;
767 for(i = 0; i < iNumValues; i++)
768 {
769 safe_free(pResult->pValues[i]);
770 }
771 safe_free(pResult->pValues);
772
773 for(i = 0; i < pResult->iNumCols; i++)
774 {
775 safe_free(pResult->columnNames[i]);
776 }
777 safe_free(pResult->columnNames);
778
779 free(pResult);
780 }
781 }
782
783
784 //
785 // Perform asynchronous SELECT query
786 //
787
788 extern "C" DBDRV_ASYNC_RESULT EXPORT DrvAsyncSelect(DB2DRV_CONN *pConn, NETXMS_WCHAR *pwszQuery,
789 DWORD *pdwError, NETXMS_WCHAR *errorText)
790 {
791 DB2DRV_ASYNC_QUERY_RESULT *pResult = NULL;
792 long iResult;
793 short wNumCols;
794 int i;
795
796 MutexLock(pConn->mutexQuery);
797
798 // Allocate statement handle
799 iResult = SQLAllocHandle(SQL_HANDLE_STMT, pConn->sqlConn, &pConn->sqlStatement);
800 if ((iResult == SQL_SUCCESS) || (iResult == SQL_SUCCESS_WITH_INFO))
801 {
802 // Execute statement
803 #if defined(_WIN32) || defined(UNICODE_UCS2)
804 iResult = SQLExecDirectW(pConn->sqlStatement, (SQLWCHAR *)pwszQuery, SQL_NTS);
805 #else
806 SQLWCHAR *temp = UCS2StringFromUCS4String(pwszQuery);
807 iResult = SQLExecDirectW(pConn->sqlStatement, temp, SQL_NTS);
808 free(temp);
809 #endif
810 if ((iResult == SQL_SUCCESS) || (iResult == SQL_SUCCESS_WITH_INFO))
811 {
812 // Allocate result buffer and determine column info
813 pResult = (DB2DRV_ASYNC_QUERY_RESULT *)malloc(sizeof(DB2DRV_ASYNC_QUERY_RESULT));
814 SQLNumResultCols(pConn->sqlStatement, &wNumCols);
815 pResult->iNumCols = wNumCols;
816 pResult->pConn = pConn;
817 pResult->bNoMoreRows = FALSE;
818
819 // Get column names
820 pResult->columnNames = (char **)malloc(sizeof(char *) * pResult->iNumCols);
821 for(i = 0; i < pResult->iNumCols; i++)
822 {
823 SQLWCHAR name[256];
824 SQLSMALLINT len;
825
826 iResult = SQLColAttributeW(pConn->sqlStatement, (SQLSMALLINT)(i + 1),
827 SQL_DESC_NAME, name, 256, &len, NULL);
828 if (
829 (iResult == SQL_SUCCESS) ||
830 (iResult == SQL_SUCCESS_WITH_INFO))
831 {
832 name[len] = 0;
833 pResult->columnNames[i] = MBStringFromUCS2String(name);
834 }
835 else
836 {
837 pResult->columnNames[i] = strdup("");
838 }
839 }
840
841 *pdwError = DBERR_SUCCESS;
842 }
843 else
844 {
845 *pdwError = GetSQLErrorInfo(SQL_HANDLE_STMT, pConn->sqlStatement, errorText);
846 // Free statement handle if query failed
847 SQLFreeHandle(SQL_HANDLE_STMT, pConn->sqlStatement);
848 }
849 }
850 else
851 {
852 *pdwError = GetSQLErrorInfo(SQL_HANDLE_DBC, pConn->sqlConn, errorText);
853 }
854
855 if (pResult == NULL) // Unlock mutex if query has failed
856 {
857 MutexUnlock(pConn->mutexQuery);
858 }
859 return pResult;
860 }
861
862
863 //
864 // Fetch next result line from asynchronous SELECT results
865 //
866
867 extern "C" BOOL EXPORT DrvFetch(DB2DRV_ASYNC_QUERY_RESULT *pResult)
868 {
869 BOOL bResult = FALSE;
870
871 if (pResult != NULL)
872 {
873 long iResult;
874
875 iResult = SQLFetch(pResult->pConn->sqlStatement);
876 bResult = ((iResult == SQL_SUCCESS) || (iResult == SQL_SUCCESS_WITH_INFO));
877 if (!bResult)
878 {
879 pResult->bNoMoreRows = TRUE;
880 }
881 }
882 return bResult;
883 }
884
885
886 //
887 // Get field length from async query result
888 //
889
890 extern "C" LONG EXPORT DrvGetFieldLengthAsync(DB2DRV_ASYNC_QUERY_RESULT *pResult, int iColumn)
891 {
892 LONG nLen = -1;
893
894 if (pResult != NULL)
895 {
896 if ((iColumn < pResult->iNumCols) && (iColumn >= 0))
897 {
898 SQLLEN dataSize;
899 char temp[1];
900 long rc = SQLGetData(pResult->pConn->sqlStatement, (short)iColumn + 1, SQL_C_CHAR, temp, 0, &dataSize);
901 if ((rc == SQL_SUCCESS) || (rc == SQL_SUCCESS_WITH_INFO))
902 {
903 nLen = (LONG)dataSize;
904 }
905 }
906 }
907 return nLen;
908 }
909
910
911 //
912 // Get field from current row in async query result
913 //
914
915 extern "C" NETXMS_WCHAR EXPORT *DrvGetFieldAsync(DB2DRV_ASYNC_QUERY_RESULT *pResult,
916 int iColumn, NETXMS_WCHAR *pBuffer, int iBufSize)
917 {
918 SQLLEN iDataSize;
919 long iResult;
920
921 // Check if we have valid result handle
922 if (pResult == NULL)
923 {
924 return NULL;
925 }
926
927 // Check if there are valid fetched row
928 if (pResult->bNoMoreRows)
929 {
930 return NULL;
931 }
932
933 if ((iColumn >= 0) && (iColumn < pResult->iNumCols))
934 {
935 #if defined(_WIN32) || defined(UNICODE_UCS2)
936 iResult = SQLGetData(pResult->pConn->sqlStatement, (short)iColumn + 1, SQL_C_WCHAR,
937 pBuffer, iBufSize * sizeof(WCHAR), &iDataSize);
938 #else
939 SQLWCHAR *tempBuff = (SQLWCHAR *)malloc(iBufSize * sizeof(SQLWCHAR));
940 iResult = SQLGetData(pResult->pConn->sqlStatement, (short)iColumn + 1, SQL_C_WCHAR,
941 tempBuff, iBufSize * sizeof(SQLWCHAR), &iDataSize);
942 ucs2_to_ucs4(tempBuff, -1, pBuffer, iBufSize);
943 pBuffer[iBufSize - 1] = 0;
944 free(tempBuff);
945 #endif
946 if (((iResult != SQL_SUCCESS) && (iResult != SQL_SUCCESS_WITH_INFO)) || (iDataSize == SQL_NULL_DATA))
947 {
948 pBuffer[0] = 0;
949 }
950 }
951 else
952 {
953 pBuffer[0] = 0;
954 }
955 return pBuffer;
956 }
957
958
959 //
960 // Get column count in async query result
961 //
962
963 extern "C" int EXPORT DrvGetColumnCountAsync(DB2DRV_ASYNC_QUERY_RESULT *pResult)
964 {
965 return (pResult != NULL) ? pResult->iNumCols : 0;
966 }
967
968
969 //
970 // Get column name in async query result
971 //
972
973 extern "C" const char EXPORT *DrvGetColumnNameAsync(DB2DRV_ASYNC_QUERY_RESULT *pResult, int column)
974 {
975 return ((pResult != NULL) && (column >= 0) && (column < pResult->iNumCols)) ? pResult->columnNames[column] : NULL;
976 }
977
978
979 //
980 // Destroy result of async query
981 //
982
983 extern "C" void EXPORT DrvFreeAsyncResult(DB2DRV_ASYNC_QUERY_RESULT *pResult)
984 {
985 if (pResult != NULL)
986 {
987 SQLFreeHandle(SQL_HANDLE_STMT, pResult->pConn->sqlStatement);
988 MutexUnlock(pResult->pConn->mutexQuery);
989 free(pResult);
990 }
991 }
992
993
994 //
995 // Begin transaction
996 //
997
998 extern "C" DWORD EXPORT DrvBegin(DB2DRV_CONN *pConn)
999 {
1000 SQLRETURN nRet;
1001 DWORD dwResult;
1002
1003 if (pConn == NULL)
1004 {
1005 return DBERR_INVALID_HANDLE;
1006 }
1007
1008 MutexLock(pConn->mutexQuery);
1009 nRet = SQLSetConnectAttr(pConn->sqlConn, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)SQL_AUTOCOMMIT_OFF, 0);
1010 if ((nRet == SQL_SUCCESS) || (nRet == SQL_SUCCESS_WITH_INFO))
1011 {
1012 dwResult = DBERR_SUCCESS;
1013 }
1014 else
1015 {
1016 dwResult = GetSQLErrorInfo(SQL_HANDLE_DBC, pConn->sqlConn, NULL);
1017 }
1018 MutexUnlock(pConn->mutexQuery);
1019 return dwResult;
1020 }
1021
1022
1023 //
1024 // Commit transaction
1025 //
1026
1027 extern "C" DWORD EXPORT DrvCommit(DB2DRV_CONN *pConn)
1028 {
1029 SQLRETURN nRet;
1030
1031 if (pConn == NULL)
1032 {
1033 return DBERR_INVALID_HANDLE;
1034 }
1035
1036 MutexLock(pConn->mutexQuery);
1037 nRet = SQLEndTran(SQL_HANDLE_DBC, pConn->sqlConn, SQL_COMMIT);
1038 SQLSetConnectAttr(pConn->sqlConn, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)SQL_AUTOCOMMIT_ON, 0);
1039 MutexUnlock(pConn->mutexQuery);
1040 return ((nRet == SQL_SUCCESS) || (nRet == SQL_SUCCESS_WITH_INFO)) ? DBERR_SUCCESS : DBERR_OTHER_ERROR;
1041 }
1042
1043
1044 //
1045 // Rollback transaction
1046 //
1047
1048 extern "C" DWORD EXPORT DrvRollback(DB2DRV_CONN *pConn)
1049 {
1050 SQLRETURN nRet;
1051
1052 if (pConn == NULL)
1053 {
1054 return DBERR_INVALID_HANDLE;
1055 }
1056
1057 MutexLock(pConn->mutexQuery);
1058 nRet = SQLEndTran(SQL_HANDLE_DBC, pConn->sqlConn, SQL_ROLLBACK);
1059 SQLSetConnectAttr(pConn->sqlConn, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)SQL_AUTOCOMMIT_ON, 0);
1060 MutexUnlock(pConn->mutexQuery);
1061 return ((nRet == SQL_SUCCESS) || (nRet == SQL_SUCCESS_WITH_INFO)) ? DBERR_SUCCESS : DBERR_OTHER_ERROR;
1062 }
1063
1064
1065 //
1066 // DLL Entry point
1067 //
1068
1069 #ifdef _WIN32
1070
1071 BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
1072 {
1073 if (dwReason == DLL_PROCESS_ATTACH)
1074 {
1075 DisableThreadLibraryCalls(hInstance);
1076 }
1077 return TRUE;
1078 }
1079
1080 #endif