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