3 ** Copyright (C) 2010-2016 Raden Solutinos
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.
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.
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.
21 #define _CRT_SECURE_NO_WARNINGS
25 DECLARE_DRIVER_HEADER("DB2")
28 * Convert DB2 state to NetXMS database error code and get error text
30 static DWORD
GetSQLErrorInfo(SQLSMALLINT nHandleType
, SQLHANDLE hHandle
, NETXMS_WCHAR
*errorText
)
35 SQLWCHAR buffer
[DBDRV_MAX_ERROR_TEXT
];
37 // Get state information and convert it to NetXMS database error code
38 nRet
= SQLGetDiagFieldW(nHandleType
, hHandle
, 1, SQL_DIAG_SQLSTATE
, buffer
, 16, &nChars
);
39 if (nRet
== SQL_SUCCESS
)
43 (!wcscmp((WCHAR
*)buffer
, L
"08003")) || // Connection does not exist
44 (!wcscmp((WCHAR
*)buffer
, L
"08S01")) || // Communication link failure
45 (!wcscmp((WCHAR
*)buffer
, L
"HYT00")) || // Timeout expired
46 (!wcscmp((WCHAR
*)buffer
, L
"HYT01")) || // Connection timeout expired
47 (!wcscmp((WCHAR
*)buffer
, L
"08506"))) // SQL30108N: A connection failed but has been re-established.
50 ucs2_to_mb(buffer
, -1, state
, 16);
52 (!strcmp(state
, "08003")) || // Connection does not exist
53 (!strcmp(state
, "08S01")) || // Communication link failure
54 (!strcmp(state
, "HYT00")) || // Timeout expired
55 (!strcmp(state
, "HYT01")) || // Connection timeout expired
56 (!strcmp(state
, "08506"))) // SQL30108N: A connection failed but has been re-established.
59 dwError
= DBERR_CONNECTION_LOST
;
63 dwError
= DBERR_OTHER_ERROR
;
68 dwError
= DBERR_OTHER_ERROR
;
72 if (errorText
!= NULL
)
75 nRet
= SQLGetDiagFieldW(nHandleType
, hHandle
, 1, SQL_DIAG_MESSAGE_TEXT
, errorText
, DBDRV_MAX_ERROR_TEXT
, &nChars
);
77 nRet
= SQLGetDiagFieldW(nHandleType
, hHandle
, 1, SQL_DIAG_MESSAGE_TEXT
, buffer
, DBDRV_MAX_ERROR_TEXT
, &nChars
);
79 if (nRet
== SQL_SUCCESS
)
82 buffer
[DBDRV_MAX_ERROR_TEXT
- 1] = 0;
83 ucs2_to_ucs4(buffer
, -1, errorText
, DBDRV_MAX_ERROR_TEXT
);
85 RemoveTrailingCRLFW(errorText
);
89 wcscpy(errorText
, L
"Unable to obtain description for this error");
97 * Clear any pending result sets on given statement
99 static void ClearPendingResults(SQLHSTMT statement
)
103 SQLRETURN rc
= SQLMoreResults(statement
);
104 if ((rc
!= SQL_SUCCESS
) && (rc
!= SQL_SUCCESS_WITH_INFO
))
112 * Prepare string for using in SQL query - enclose in quotes and escape as needed
114 extern "C" WCHAR EXPORT
*DrvPrepareStringW(const WCHAR
*str
)
116 int len
= (int)wcslen(str
) + 3; // + two quotes and \0 at the end
117 int bufferSize
= len
+ 128;
118 WCHAR
*out
= (WCHAR
*)malloc(bufferSize
* sizeof(WCHAR
));
121 const WCHAR
*src
= str
;
123 for(outPos
= 1; *src
!= 0; src
++)
128 if (len
>= bufferSize
)
131 out
= (WCHAR
*)realloc(out
, bufferSize
* sizeof(WCHAR
));
133 out
[outPos
++] = L
'\'';
134 out
[outPos
++] = L
'\'';
138 out
[outPos
++] = *src
;
141 out
[outPos
++] = L
'\'';
147 extern "C" char EXPORT
*DrvPrepareStringA(const char *str
)
149 int len
= (int)strlen(str
) + 3; // + two quotes and \0 at the end
150 int bufferSize
= len
+ 128;
151 char *out
= (char *)malloc(bufferSize
);
154 const char *src
= str
;
156 for(outPos
= 1; *src
!= 0; src
++)
161 if (len
>= bufferSize
)
164 out
= (char *)realloc(out
, bufferSize
);
166 out
[outPos
++] = '\'';
167 out
[outPos
++] = '\'';
171 out
[outPos
++] = *src
;
174 out
[outPos
++] = '\'';
183 extern "C" bool EXPORT
DrvInit(const char *cmdLine
)
191 extern "C" void EXPORT
DrvUnload()
196 * Connect to database
197 * pszHost should be set to DB2 source name, and pszDatabase is ignored
199 extern "C" DBDRV_CONNECTION EXPORT
DrvConnect(char *pszHost
, char *pszLogin
,
200 char *pszPassword
, char *pszDatabase
, const char *schema
, NETXMS_WCHAR
*errorText
)
204 SQLHSTMT sqlStatement
= NULL
;
206 // Allocate our connection structure
207 pConn
= (DB2DRV_CONN
*)malloc(sizeof(DB2DRV_CONN
));
209 // Allocate environment
210 iResult
= SQLAllocHandle(SQL_HANDLE_ENV
, SQL_NULL_HANDLE
, &pConn
->sqlEnv
);
211 if ((iResult
!= SQL_SUCCESS
) && (iResult
!= SQL_SUCCESS_WITH_INFO
))
213 wcscpy(errorText
, L
"Cannot allocate environment handle");
214 goto connect_failure_0
;
217 // Set required DB2 version
218 iResult
= SQLSetEnvAttr(pConn
->sqlEnv
, SQL_ATTR_ODBC_VERSION
, (void *)SQL_OV_ODBC3
, 0);
219 if ((iResult
!= SQL_SUCCESS
) && (iResult
!= SQL_SUCCESS_WITH_INFO
))
221 wcscpy(errorText
, L
"Call to SQLSetEnvAttr failed");
222 goto connect_failure_1
;
225 // Allocate connection handle, set timeout
226 iResult
= SQLAllocHandle(SQL_HANDLE_DBC
, pConn
->sqlEnv
, &pConn
->sqlConn
);
227 if ((iResult
!= SQL_SUCCESS
) && (iResult
!= SQL_SUCCESS_WITH_INFO
))
229 wcscpy(errorText
, L
"Cannot allocate connection handle");
230 goto connect_failure_1
;
232 SQLSetConnectAttr(pConn
->sqlConn
, SQL_ATTR_LOGIN_TIMEOUT
, (SQLPOINTER
*)15, 0);
233 SQLSetConnectAttr(pConn
->sqlConn
, SQL_ATTR_CONNECTION_TIMEOUT
, (SQLPOINTER
*)30, 0);
235 // Connect to the datasource
236 iResult
= SQLConnect(pConn
->sqlConn
, (SQLCHAR
*)pszHost
, SQL_NTS
, (SQLCHAR
*)pszLogin
, SQL_NTS
, (SQLCHAR
*)pszPassword
, SQL_NTS
);
237 if ((iResult
!= SQL_SUCCESS
) && (iResult
!= SQL_SUCCESS_WITH_INFO
))
239 GetSQLErrorInfo(SQL_HANDLE_DBC
, pConn
->sqlConn
, errorText
);
240 goto connect_failure_2
;
243 // Set current schema
244 if ((schema
!= NULL
) && (schema
[0] != 0))
247 snprintf(query
, 256, "SET CURRENT SCHEMA = '%s'", schema
);
249 iResult
= SQLAllocHandle(SQL_HANDLE_STMT
, pConn
->sqlConn
, &sqlStatement
);
250 if ((iResult
== SQL_SUCCESS
) || (iResult
== SQL_SUCCESS_WITH_INFO
))
253 SQLWCHAR
*temp
= (SQLWCHAR
*)UCS2StringFromMBString(query
);
254 iResult
= SQLExecDirectW(sqlStatement
, temp
, SQL_NTS
);
256 if ((iResult
!= SQL_SUCCESS
) &&
257 (iResult
!= SQL_SUCCESS_WITH_INFO
) &&
258 (iResult
!= SQL_NO_DATA
))
260 GetSQLErrorInfo(SQL_HANDLE_STMT
, sqlStatement
, errorText
);
261 goto connect_failure_3
;
263 SQLFreeHandle(SQL_HANDLE_STMT
, sqlStatement
);
267 GetSQLErrorInfo(SQL_HANDLE_DBC
, pConn
->sqlConn
, errorText
);
268 goto connect_failure_2
;
273 pConn
->mutexQuery
= MutexCreate();
276 return (DBDRV_CONNECTION
)pConn
;
280 SQLFreeHandle(SQL_HANDLE_STMT
, sqlStatement
);
283 SQLFreeHandle(SQL_HANDLE_DBC
, pConn
->sqlConn
);
286 SQLFreeHandle(SQL_HANDLE_ENV
, pConn
->sqlEnv
);
294 * Disconnect from database
296 extern "C" void EXPORT
DrvDisconnect(DB2DRV_CONN
*pConn
)
298 MutexLock(pConn
->mutexQuery
);
299 MutexUnlock(pConn
->mutexQuery
);
300 SQLDisconnect(pConn
->sqlConn
);
301 SQLFreeHandle(SQL_HANDLE_DBC
, pConn
->sqlConn
);
302 SQLFreeHandle(SQL_HANDLE_ENV
, pConn
->sqlEnv
);
303 MutexDestroy(pConn
->mutexQuery
);
310 extern "C" DBDRV_STATEMENT EXPORT
DrvPrepare(DB2DRV_CONN
*pConn
, NETXMS_WCHAR
*pwszQuery
, DWORD
*pdwError
, NETXMS_WCHAR
*errorText
)
314 DB2DRV_STATEMENT
*result
;
316 MutexLock(pConn
->mutexQuery
);
318 // Allocate statement handle
319 iResult
= SQLAllocHandle(SQL_HANDLE_STMT
, pConn
->sqlConn
, &statement
);
320 if ((iResult
== SQL_SUCCESS
) || (iResult
== SQL_SUCCESS_WITH_INFO
))
323 #if defined(_WIN32) || defined(UNICODE_UCS2)
324 iResult
= SQLPrepareW(statement
, (SQLWCHAR
*)pwszQuery
, SQL_NTS
);
326 SQLWCHAR
*temp
= UCS2StringFromUCS4String(pwszQuery
);
327 iResult
= SQLPrepareW(statement
, temp
, SQL_NTS
);
330 if ((iResult
== SQL_SUCCESS
) ||
331 (iResult
== SQL_SUCCESS_WITH_INFO
))
333 result
= (DB2DRV_STATEMENT
*)malloc(sizeof(DB2DRV_STATEMENT
));
334 result
->handle
= statement
;
335 result
->buffers
= new Array(0, 16, true);
336 result
->connection
= pConn
;
337 *pdwError
= DBERR_SUCCESS
;
341 *pdwError
= GetSQLErrorInfo(SQL_HANDLE_STMT
, statement
, errorText
);
342 SQLFreeHandle(SQL_HANDLE_STMT
, statement
);
348 *pdwError
= GetSQLErrorInfo(SQL_HANDLE_DBC
, pConn
->sqlConn
, errorText
);
352 MutexUnlock(pConn
->mutexQuery
);
358 // Bind parameter to statement
361 extern "C" void EXPORT
DrvBind(DB2DRV_STATEMENT
*statement
, int pos
, int sqlType
, int cType
, void *buffer
, int allocType
)
363 static SQLSMALLINT odbcSqlType
[] = { SQL_VARCHAR
, SQL_INTEGER
, SQL_BIGINT
, SQL_DOUBLE
, SQL_LONGVARCHAR
};
364 static SQLSMALLINT odbcCType
[] = { SQL_C_WCHAR
, SQL_C_SLONG
, SQL_C_ULONG
, SQL_C_SBIGINT
, SQL_C_UBIGINT
, SQL_C_DOUBLE
, SQL_C_WCHAR
};
365 static DWORD bufferSize
[] = { 0, sizeof(LONG
), sizeof(DWORD
), sizeof(INT64
), sizeof(QWORD
), sizeof(double), 0 };
367 int length
= (cType
== DB_CTYPE_STRING
) ? (int)wcslen((WCHAR
*)buffer
) + 1 : 0;
369 SQLPOINTER sqlBuffer
;
373 #if defined(_WIN32) || defined(UNICODE_UCS2)
374 if (cType
== DB_CTYPE_UTF8_STRING
)
376 sqlBuffer
= WideStringFromUTF8String((char *)buffer
);
377 statement
->buffers
->add(sqlBuffer
);
378 length
= (int)strlen((char *)sqlBuffer
) + 1;
385 if (cType
== DB_CTYPE_STRING
)
387 sqlBuffer
= UCS2StringFromUCS4String((WCHAR
*)buffer
);
388 statement
->buffers
->add(sqlBuffer
);
390 else if (cType
== DB_CTYPE_UTF8_STRING
)
392 sqlBuffer
= UCS2StringFromUTF8String((char *)buffer
);
393 statement
->buffers
->add(sqlBuffer
);
394 length
= (int)strlen((char *)sqlBuffer
) + 1;
402 case DB_BIND_DYNAMIC
:
403 #if defined(_WIN32) || defined(UNICODE_UCS2)
404 if (cType
== DB_CTYPE_UTF8_STRING
)
406 sqlBuffer
= WideStringFromUTF8String((char *)buffer
);
408 length
= (int)strlen((char *)sqlBuffer
) + 1;
415 if (cType
== DB_CTYPE_STRING
)
417 sqlBuffer
= UCS2StringFromUCS4String((WCHAR
*)buffer
);
420 else if (cType
== DB_CTYPE_UTF8_STRING
)
422 sqlBuffer
= UCS2StringFromUTF8String((char *)buffer
);
424 length
= (int)strlen((char *)sqlBuffer
) + 1;
431 statement
->buffers
->add(sqlBuffer
);
433 case DB_BIND_TRANSIENT
:
434 #if defined(_WIN32) || defined(UNICODE_UCS2)
435 if (cType
== DB_CTYPE_UTF8_STRING
)
437 sqlBuffer
= WideStringFromUTF8String((char *)buffer
);
438 length
= (int)strlen((char *)sqlBuffer
) + 1;
442 sqlBuffer
= nx_memdup(buffer
, (cType
== DB_CTYPE_STRING
) ? (DWORD
)(length
* sizeof(WCHAR
)) : bufferSize
[cType
]);
445 if (cType
== DB_CTYPE_STRING
)
447 sqlBuffer
= UCS2StringFromUCS4String((WCHAR
*)buffer
);
449 else if (cType
== DB_CTYPE_UTF8_STRING
)
451 sqlBuffer
= UCS2StringFromUTF8String((char *)buffer
);
452 length
= (int)strlen((char *)sqlBuffer
) + 1;
456 sqlBuffer
= nx_memdup(buffer
, bufferSize
[cType
]);
459 statement
->buffers
->add(sqlBuffer
);
462 return; // Invalid call
464 SQLBindParameter(statement
->handle
, pos
, SQL_PARAM_INPUT
, odbcCType
[cType
], odbcSqlType
[sqlType
],
465 ((cType
== DB_CTYPE_STRING
) || (cType
== DB_CTYPE_UTF8_STRING
)) ? length
: 0, 0, sqlBuffer
, 0, NULL
);
469 * Execute prepared statement
471 extern "C" DWORD EXPORT
DrvExecute(DB2DRV_CONN
*pConn
, DB2DRV_STATEMENT
*statement
, NETXMS_WCHAR
*errorText
)
475 MutexLock(pConn
->mutexQuery
);
476 long rc
= SQLExecute(statement
->handle
);
478 (rc
== SQL_SUCCESS
) ||
479 (rc
== SQL_SUCCESS_WITH_INFO
) ||
482 ClearPendingResults(statement
->handle
);
483 dwResult
= DBERR_SUCCESS
;
487 dwResult
= GetSQLErrorInfo(SQL_HANDLE_STMT
, statement
->handle
, errorText
);
489 MutexUnlock(pConn
->mutexQuery
);
495 // Destroy prepared statement
498 extern "C" void EXPORT
DrvFreeStatement(DB2DRV_STATEMENT
*statement
)
500 if (statement
== NULL
)
505 MutexLock(statement
->connection
->mutexQuery
);
506 SQLFreeHandle(SQL_HANDLE_STMT
, statement
->handle
);
507 MutexUnlock(statement
->connection
->mutexQuery
);
508 delete statement
->buffers
;
513 * Perform non-SELECT query
515 extern "C" DWORD EXPORT
DrvQuery(DB2DRV_CONN
*pConn
, NETXMS_WCHAR
*pwszQuery
, NETXMS_WCHAR
*errorText
)
520 MutexLock(pConn
->mutexQuery
);
522 // Allocate statement handle
523 SQLHSTMT sqlStatement
;
524 iResult
= SQLAllocHandle(SQL_HANDLE_STMT
, pConn
->sqlConn
, &sqlStatement
);
525 if ((iResult
== SQL_SUCCESS
) || (iResult
== SQL_SUCCESS_WITH_INFO
))
528 #if defined(_WIN32) || defined(UNICODE_UCS2)
529 iResult
= SQLExecDirectW(sqlStatement
, (SQLWCHAR
*)pwszQuery
, SQL_NTS
);
531 SQLWCHAR
*temp
= UCS2StringFromUCS4String(pwszQuery
);
532 iResult
= SQLExecDirectW(sqlStatement
, temp
, SQL_NTS
);
535 if ((iResult
== SQL_SUCCESS
) || (iResult
== SQL_SUCCESS_WITH_INFO
) || (iResult
== SQL_NO_DATA
))
537 dwResult
= DBERR_SUCCESS
;
541 dwResult
= GetSQLErrorInfo(SQL_HANDLE_STMT
, sqlStatement
, errorText
);
543 SQLFreeHandle(SQL_HANDLE_STMT
, sqlStatement
);
547 dwResult
= GetSQLErrorInfo(SQL_HANDLE_DBC
, pConn
->sqlConn
, errorText
);
550 MutexUnlock(pConn
->mutexQuery
);
555 * Get complete field data
557 static NETXMS_WCHAR
*GetFieldData(SQLHSTMT sqlStatement
, short column
)
559 NETXMS_WCHAR
*result
= NULL
;
561 #if defined(_WIN32) || defined(UNICODE_UCS2)
563 SQLRETURN rc
= SQLGetData(sqlStatement
, column
, SQL_C_WCHAR
, buffer
, sizeof(buffer
), &dataSize
);
564 if (((rc
== SQL_SUCCESS
) || ((rc
== SQL_SUCCESS_WITH_INFO
) && (dataSize
>= 0) && (dataSize
<= (SQLLEN
)(sizeof(buffer
) - sizeof(WCHAR
))))) && (dataSize
!= SQL_NULL_DATA
))
566 result
= wcsdup(buffer
);
568 else if ((rc
== SQL_SUCCESS_WITH_INFO
) && (dataSize
!= SQL_NULL_DATA
))
570 if (dataSize
> (SQLLEN
)(sizeof(buffer
) - sizeof(WCHAR
)))
572 WCHAR
*temp
= (WCHAR
*)malloc(dataSize
+ sizeof(WCHAR
));
573 memcpy(temp
, buffer
, sizeof(buffer
));
574 rc
= SQLGetData(sqlStatement
, column
, SQL_C_WCHAR
, &temp
[255], dataSize
- 254 * sizeof(WCHAR
), &dataSize
);
575 if ((rc
== SQL_SUCCESS
) || (rc
== SQL_SUCCESS_WITH_INFO
))
584 else if (dataSize
== SQL_NO_TOTAL
)
586 size_t tempSize
= sizeof(buffer
) * 4; // temporary buffer size in bytes
587 WCHAR
*temp
= (WCHAR
*)malloc(tempSize
);
588 memcpy(temp
, buffer
, sizeof(buffer
));
589 size_t offset
= sizeof(buffer
) - sizeof(WCHAR
); // offset in buffer in bytes
592 SQLINTEGER readSize
= (SQLINTEGER
)(tempSize
- offset
);
593 rc
= SQLGetData(sqlStatement
, column
, SQL_C_WCHAR
, (char *)temp
+ offset
, readSize
, &dataSize
);
594 if ((rc
== SQL_SUCCESS
) || (rc
== SQL_NO_DATA
))
596 if (dataSize
== SQL_NO_TOTAL
)
598 tempSize
+= sizeof(buffer
) * 4;
602 tempSize
+= dataSize
- readSize
;
604 temp
= (WCHAR
*)realloc(temp
, tempSize
);
605 offset
+= readSize
- sizeof(WCHAR
);
611 UCS2CHAR buffer
[256];
612 SQLRETURN rc
= SQLGetData(sqlStatement
, column
, SQL_C_WCHAR
, buffer
, sizeof(buffer
), &dataSize
);
613 if (((rc
== SQL_SUCCESS
) || ((rc
== SQL_SUCCESS_WITH_INFO
) && (dataSize
>= 0) && (dataSize
<= (SQLLEN
)(sizeof(buffer
) - sizeof(UCS2CHAR
))))) && (dataSize
!= SQL_NULL_DATA
))
615 int len
= ucs2_strlen(buffer
);
616 result
= (NETXMS_WCHAR
*)malloc((len
+ 1) * sizeof(NETXMS_WCHAR
));
617 ucs2_to_ucs4(buffer
, -1, pResult
->values
[i
], len
+ 1);
619 else if ((rc
== SQL_SUCCESS_WITH_INFO
) && (dataSize
!= SQL_NULL_DATA
))
621 if (dataSize
> (SQLLEN
)(sizeof(buffer
) - sizeof(UCS2CHAR
)))
623 UCS2CHAR
*temp
= (UCS2CHAR
*)malloc(dataSize
+ sizeof(UCS2CHAR
));
624 memcpy(temp
, buffer
, sizeof(buffer
));
625 rc
= SQLGetData(sqlStatement
, column
, SQL_C_WCHAR
, &temp
[255], dataSize
- 254 * sizeof(UCS2CHAR
), &dataSize
);
626 if ((rc
== SQL_SUCCESS
) || (rc
== SQL_SUCCESS_WITH_INFO
))
628 int len
= ucs2_strlen(temp
);
629 result
= (NETXMS_WCHAR
*)malloc((len
+ 1) * sizeof(NETXMS_WCHAR
));
630 ucs2_to_ucs4(temp
, -1, result
, len
+ 1);
634 else if (dataSize
== SQL_NO_TOTAL
)
636 size_t tempSize
= sizeof(buffer
) * 4;
637 UCS2CHAR
*temp
= (UCS2CHAR
*)malloc(tempSize
);
638 memcpy(temp
, buffer
, sizeof(buffer
));
639 size_t offset
= sizeof(buffer
) - sizeof(UCS2CHAR
);
642 SQLLEN readSize
= tempSize
- offset
;
643 rc
= SQLGetData(sqlStatement
, column
, SQL_C_WCHAR
, (char *)temp
+ offset
, readSize
, &dataSize
);
644 if ((rc
== SQL_SUCCESS
) || (rc
== SQL_NO_DATA
))
646 if (dataSize
== SQL_NO_TOTAL
)
648 tempSize
+= sizeof(buffer
) * 4;
652 tempSize
+= dataSize
- readSize
;
654 temp
= (UCS2CHAR
*)realloc(temp
, tempSize
);
655 offset
+= readSize
- sizeof(UCS2CHAR
);
657 int len
= ucs2_strlen(temp
);
658 result
= (NETXMS_WCHAR
*)malloc((len
+ 1) * sizeof(NETXMS_WCHAR
));
659 ucs2_to_ucs4(temp
, -1, result
, len
+ 1);
664 return (result
!= NULL
) ? result
: wcsdup(L
"");
668 * Process results of SELECT query
670 static DB2DRV_QUERY_RESULT
*ProcessSelectResults(SQLHSTMT statement
)
672 // Allocate result buffer and determine column info
673 DB2DRV_QUERY_RESULT
*pResult
= (DB2DRV_QUERY_RESULT
*)malloc(sizeof(DB2DRV_QUERY_RESULT
));
675 SQLNumResultCols(statement
, &wNumCols
);
676 pResult
->numColumns
= wNumCols
;
677 pResult
->numRows
= 0;
678 pResult
->values
= NULL
;
681 pResult
->columnNames
= (char **)malloc(sizeof(char *) * pResult
->numColumns
);
682 for(int i
= 0; i
< (int)pResult
->numColumns
; i
++)
687 SQLRETURN iResult
= SQLColAttributeW(statement
, (SQLSMALLINT
)(i
+ 1), SQL_DESC_NAME
, (SQLPOINTER
)name
, 256, &len
, NULL
);
688 if ((iResult
== SQL_SUCCESS
) || (iResult
== SQL_SUCCESS_WITH_INFO
))
691 pResult
->columnNames
[i
] = MBStringFromUCS2String(name
);
695 pResult
->columnNames
[i
] = strdup("");
702 while(iResult
= SQLFetch(statement
), (iResult
== SQL_SUCCESS
) || (iResult
== SQL_SUCCESS_WITH_INFO
))
705 pResult
->values
= (NETXMS_WCHAR
**)realloc(pResult
->values
, sizeof(NETXMS_WCHAR
*) * (pResult
->numRows
* pResult
->numColumns
));
706 for(int i
= 1; i
<= (int)pResult
->numColumns
; i
++)
708 pResult
->values
[iCurrValue
++] = GetFieldData(statement
, (short)i
);
716 * Perform SELECT query
718 extern "C" DBDRV_RESULT EXPORT
DrvSelect(DB2DRV_CONN
*pConn
, NETXMS_WCHAR
*pwszQuery
, DWORD
*pdwError
, NETXMS_WCHAR
*errorText
)
720 DB2DRV_QUERY_RESULT
*pResult
= NULL
;
722 MutexLock(pConn
->mutexQuery
);
724 // Allocate statement handle
725 SQLHSTMT sqlStatement
;
726 long iResult
= SQLAllocHandle(SQL_HANDLE_STMT
, pConn
->sqlConn
, &sqlStatement
);
727 if ((iResult
== SQL_SUCCESS
) || (iResult
== SQL_SUCCESS_WITH_INFO
))
730 #if defined(_WIN32) || defined(UNICODE_UCS2)
731 iResult
= SQLExecDirectW(sqlStatement
, (SQLWCHAR
*)pwszQuery
, SQL_NTS
);
733 SQLWCHAR
*temp
= UCS2StringFromUCS4String(pwszQuery
);
734 iResult
= SQLExecDirectW(sqlStatement
, temp
, SQL_NTS
);
737 if ((iResult
== SQL_SUCCESS
) || (iResult
== SQL_SUCCESS_WITH_INFO
))
739 pResult
= ProcessSelectResults(sqlStatement
);
740 *pdwError
= DBERR_SUCCESS
;
744 *pdwError
= GetSQLErrorInfo(SQL_HANDLE_STMT
, sqlStatement
, errorText
);
746 SQLFreeHandle(SQL_HANDLE_STMT
, sqlStatement
);
750 *pdwError
= GetSQLErrorInfo(SQL_HANDLE_DBC
, pConn
->sqlConn
, errorText
);
753 MutexUnlock(pConn
->mutexQuery
);
758 * Perform SELECT query using prepared statement
760 extern "C" DBDRV_RESULT EXPORT
DrvSelectPrepared(DB2DRV_CONN
*pConn
, DB2DRV_STATEMENT
*statement
, DWORD
*pdwError
, NETXMS_WCHAR
*errorText
)
762 DB2DRV_QUERY_RESULT
*pResult
= NULL
;
764 MutexLock(pConn
->mutexQuery
);
765 long rc
= SQLExecute(statement
->handle
);
766 if ((rc
== SQL_SUCCESS
) || (rc
== SQL_SUCCESS_WITH_INFO
))
768 pResult
= ProcessSelectResults(statement
->handle
);
769 *pdwError
= DBERR_SUCCESS
;
773 *pdwError
= GetSQLErrorInfo(SQL_HANDLE_STMT
, statement
->handle
, errorText
);
775 MutexUnlock(pConn
->mutexQuery
);
780 * Get field length from result
782 extern "C" LONG EXPORT
DrvGetFieldLength(DB2DRV_QUERY_RESULT
*pResult
, int iRow
, int iColumn
)
788 if ((iRow
< pResult
->numRows
) && (iRow
>= 0) &&
789 (iColumn
< pResult
->numColumns
) && (iColumn
>= 0))
791 nLen
= (LONG
)wcslen(pResult
->values
[iRow
* pResult
->numColumns
+ iColumn
]);
798 * Get field value from result
800 extern "C" NETXMS_WCHAR EXPORT
*DrvGetField(DB2DRV_QUERY_RESULT
*pResult
, int iRow
, int iColumn
, NETXMS_WCHAR
*pBuffer
, int nBufSize
)
802 NETXMS_WCHAR
*pValue
= NULL
;
806 if ((iRow
< pResult
->numRows
) && (iRow
>= 0) &&
807 (iColumn
< pResult
->numColumns
) && (iColumn
>= 0))
810 wcsncpy_s(pBuffer
, nBufSize
, pResult
->values
[iRow
* pResult
->numColumns
+ iColumn
], _TRUNCATE
);
812 wcsncpy(pBuffer
, pResult
->values
[iRow
* pResult
->numColumns
+ iColumn
], nBufSize
);
813 pBuffer
[nBufSize
- 1] = 0;
822 * Get number of rows in result
824 extern "C" int EXPORT
DrvGetNumRows(DB2DRV_QUERY_RESULT
*pResult
)
826 return (pResult
!= NULL
) ? pResult
->numRows
: 0;
830 * Get column count in query result
832 extern "C" int EXPORT
DrvGetColumnCount(DB2DRV_QUERY_RESULT
*pResult
)
834 return (pResult
!= NULL
) ? pResult
->numColumns
: 0;
838 * Get column name in query result
840 extern "C" const char EXPORT
*DrvGetColumnName(DB2DRV_QUERY_RESULT
*pResult
, int column
)
842 return ((pResult
!= NULL
) && (column
>= 0) && (column
< pResult
->numColumns
)) ? pResult
->columnNames
[column
] : NULL
;
846 * Free SELECT results
848 extern "C" void EXPORT
DrvFreeResult(DB2DRV_QUERY_RESULT
*pResult
)
855 iNumValues
= pResult
->numColumns
* pResult
->numRows
;
856 for(i
= 0; i
< iNumValues
; i
++)
858 free(pResult
->values
[i
]);
860 safe_free(pResult
->values
);
862 for(i
= 0; i
< pResult
->numColumns
; i
++)
864 free(pResult
->columnNames
[i
]);
866 free(pResult
->columnNames
);
871 * Perform unbuffered SELECT query
873 extern "C" DBDRV_UNBUFFERED_RESULT EXPORT
DrvSelectUnbuffered(DB2DRV_CONN
*pConn
, NETXMS_WCHAR
*pwszQuery
, DWORD
*pdwError
, NETXMS_WCHAR
*errorText
)
875 DB2DRV_UNBUFFERED_QUERY_RESULT
*pResult
= NULL
;
877 MutexLock(pConn
->mutexQuery
);
879 // Allocate statement handle
880 SQLHSTMT sqlStatement
;
881 SQLRETURN iResult
= SQLAllocHandle(SQL_HANDLE_STMT
, pConn
->sqlConn
, &sqlStatement
);
882 if ((iResult
== SQL_SUCCESS
) || (iResult
== SQL_SUCCESS_WITH_INFO
))
885 #if defined(_WIN32) || defined(UNICODE_UCS2)
886 iResult
= SQLExecDirectW(sqlStatement
, (SQLWCHAR
*)pwszQuery
, SQL_NTS
);
888 SQLWCHAR
*temp
= UCS2StringFromUCS4String(pwszQuery
);
889 iResult
= SQLExecDirectW(sqlStatement
, temp
, SQL_NTS
);
892 if ((iResult
== SQL_SUCCESS
) || (iResult
== SQL_SUCCESS_WITH_INFO
))
894 // Allocate result buffer and determine column info
895 pResult
= (DB2DRV_UNBUFFERED_QUERY_RESULT
*)malloc(sizeof(DB2DRV_UNBUFFERED_QUERY_RESULT
));
896 pResult
->sqlStatement
= sqlStatement
;
897 pResult
->isPrepared
= false;
900 SQLNumResultCols(sqlStatement
, &wNumCols
);
901 pResult
->numColumns
= wNumCols
;
902 pResult
->pConn
= pConn
;
903 pResult
->noMoreRows
= false;
906 pResult
->columnNames
= (char **)malloc(sizeof(char *) * pResult
->numColumns
);
907 for(int i
= 0; i
< pResult
->numColumns
; i
++)
912 iResult
= SQLColAttributeW(sqlStatement
, (SQLSMALLINT
)(i
+ 1), SQL_DESC_NAME
, name
, 256, &len
, NULL
);
913 if ((iResult
== SQL_SUCCESS
) || (iResult
== SQL_SUCCESS_WITH_INFO
))
916 pResult
->columnNames
[i
] = MBStringFromUCS2String((UCS2CHAR
*)name
);
920 pResult
->columnNames
[i
] = strdup("");
924 *pdwError
= DBERR_SUCCESS
;
928 *pdwError
= GetSQLErrorInfo(SQL_HANDLE_STMT
, sqlStatement
, errorText
);
929 // Free statement handle if query failed
930 SQLFreeHandle(SQL_HANDLE_STMT
, sqlStatement
);
935 *pdwError
= GetSQLErrorInfo(SQL_HANDLE_DBC
, pConn
->sqlConn
, errorText
);
938 if (pResult
== NULL
) // Unlock mutex if query has failed
940 MutexUnlock(pConn
->mutexQuery
);
946 * Perform unbuffered SELECT query using prepared statement
948 extern "C" DBDRV_UNBUFFERED_RESULT EXPORT
DrvSelectPreparedUnbuffered(DB2DRV_CONN
*pConn
, DB2DRV_STATEMENT
*stmt
, DWORD
*pdwError
, WCHAR
*errorText
)
950 DB2DRV_UNBUFFERED_QUERY_RESULT
*pResult
= NULL
;
952 MutexLock(pConn
->mutexQuery
);
953 SQLRETURN rc
= SQLExecute(stmt
->handle
);
954 if ((rc
== SQL_SUCCESS
) || (rc
== SQL_SUCCESS_WITH_INFO
))
956 // Allocate result buffer and determine column info
957 pResult
= (DB2DRV_UNBUFFERED_QUERY_RESULT
*)malloc(sizeof(DB2DRV_UNBUFFERED_QUERY_RESULT
));
958 pResult
->sqlStatement
= stmt
->handle
;
959 pResult
->isPrepared
= true;
962 SQLNumResultCols(pResult
->sqlStatement
, &wNumCols
);
963 pResult
->numColumns
= wNumCols
;
964 pResult
->pConn
= pConn
;
965 pResult
->noMoreRows
= false;
968 pResult
->columnNames
= (char **)malloc(sizeof(char *) * pResult
->numColumns
);
969 for(int i
= 0; i
< pResult
->numColumns
; i
++)
974 rc
= SQLColAttributeW(pResult
->sqlStatement
, (SQLSMALLINT
)(i
+ 1), SQL_DESC_NAME
, name
, 256, &len
, NULL
);
975 if ((rc
== SQL_SUCCESS
) || (rc
== SQL_SUCCESS_WITH_INFO
))
978 pResult
->columnNames
[i
] = MBStringFromUCS2String((UCS2CHAR
*)name
);
982 pResult
->columnNames
[i
] = strdup("");
986 *pdwError
= DBERR_SUCCESS
;
990 *pdwError
= GetSQLErrorInfo(SQL_HANDLE_STMT
, stmt
->handle
, errorText
);
993 if (pResult
== NULL
) // Unlock mutex if query has failed
994 MutexUnlock(pConn
->mutexQuery
);
999 * Fetch next result line from asynchronous SELECT results
1001 extern "C" bool EXPORT
DrvFetch(DB2DRV_UNBUFFERED_QUERY_RESULT
*pResult
)
1003 bool bResult
= false;
1005 if (pResult
!= NULL
)
1009 iResult
= SQLFetch(pResult
->sqlStatement
);
1010 bResult
= ((iResult
== SQL_SUCCESS
) || (iResult
== SQL_SUCCESS_WITH_INFO
));
1013 pResult
->noMoreRows
= true;
1020 * Get field length from unbuffered query result
1022 extern "C" LONG EXPORT
DrvGetFieldLengthUnbuffered(DB2DRV_UNBUFFERED_QUERY_RESULT
*pResult
, int iColumn
)
1026 if (pResult
!= NULL
)
1028 if ((iColumn
< pResult
->numColumns
) && (iColumn
>= 0))
1032 long rc
= SQLGetData(pResult
->sqlStatement
, (short)iColumn
+ 1, SQL_C_CHAR
, temp
, 0, &dataSize
);
1033 if ((rc
== SQL_SUCCESS
) || (rc
== SQL_SUCCESS_WITH_INFO
))
1035 nLen
= (LONG
)dataSize
;
1043 * Get field from current row in unbuffered query result
1045 extern "C" NETXMS_WCHAR EXPORT
*DrvGetFieldUnbuffered(DB2DRV_UNBUFFERED_QUERY_RESULT
*pResult
, int iColumn
, NETXMS_WCHAR
*pBuffer
, int iBufSize
)
1050 // Check if we have valid result handle
1051 if (pResult
== NULL
)
1056 // Check if there are valid fetched row
1057 if (pResult
->noMoreRows
)
1062 if ((iColumn
>= 0) && (iColumn
< pResult
->numColumns
))
1064 #if defined(_WIN32) || defined(UNICODE_UCS2)
1065 iResult
= SQLGetData(pResult
->sqlStatement
, (short)iColumn
+ 1, SQL_C_WCHAR
,
1066 pBuffer
, iBufSize
* sizeof(WCHAR
), &iDataSize
);
1068 SQLWCHAR
*tempBuff
= (SQLWCHAR
*)malloc(iBufSize
* sizeof(SQLWCHAR
));
1069 iResult
= SQLGetData(pResult
->sqlStatement
, (short)iColumn
+ 1, SQL_C_WCHAR
,
1070 tempBuff
, iBufSize
* sizeof(SQLWCHAR
), &iDataSize
);
1071 ucs2_to_ucs4(tempBuff
, -1, pBuffer
, iBufSize
);
1072 pBuffer
[iBufSize
- 1] = 0;
1075 if (((iResult
!= SQL_SUCCESS
) && (iResult
!= SQL_SUCCESS_WITH_INFO
)) || (iDataSize
== SQL_NULL_DATA
))
1088 * Get column count in async query result
1090 extern "C" int EXPORT
DrvGetColumnCountUnbuffered(DB2DRV_UNBUFFERED_QUERY_RESULT
*pResult
)
1092 return (pResult
!= NULL
) ? pResult
->numColumns
: 0;
1096 * Get column name in async query result
1098 extern "C" const char EXPORT
*DrvGetColumnNameUnbuffered(DB2DRV_UNBUFFERED_QUERY_RESULT
*pResult
, int column
)
1100 return ((pResult
!= NULL
) && (column
>= 0) && (column
< pResult
->numColumns
)) ? pResult
->columnNames
[column
] : NULL
;
1104 * Destroy result of unbuffered query
1106 extern "C" void EXPORT
DrvFreeUnbufferedResult(DB2DRV_UNBUFFERED_QUERY_RESULT
*pResult
)
1108 if (pResult
== NULL
)
1111 if (pResult
->isPrepared
)
1112 SQLCloseCursor(pResult
->sqlStatement
);
1114 SQLFreeHandle(SQL_HANDLE_STMT
, pResult
->sqlStatement
);
1115 MutexUnlock(pResult
->pConn
->mutexQuery
);
1117 for(int i
= 0; i
< pResult
->numColumns
; i
++)
1119 free(pResult
->columnNames
[i
]);
1121 free(pResult
->columnNames
);
1129 extern "C" DWORD EXPORT
DrvBegin(DB2DRV_CONN
*pConn
)
1136 return DBERR_INVALID_HANDLE
;
1139 MutexLock(pConn
->mutexQuery
);
1140 nRet
= SQLSetConnectAttr(pConn
->sqlConn
, SQL_ATTR_AUTOCOMMIT
, (SQLPOINTER
)SQL_AUTOCOMMIT_OFF
, 0);
1141 if ((nRet
== SQL_SUCCESS
) || (nRet
== SQL_SUCCESS_WITH_INFO
))
1143 dwResult
= DBERR_SUCCESS
;
1147 dwResult
= GetSQLErrorInfo(SQL_HANDLE_DBC
, pConn
->sqlConn
, NULL
);
1149 MutexUnlock(pConn
->mutexQuery
);
1154 * Commit transaction
1156 extern "C" DWORD EXPORT
DrvCommit(DB2DRV_CONN
*pConn
)
1162 return DBERR_INVALID_HANDLE
;
1165 MutexLock(pConn
->mutexQuery
);
1166 nRet
= SQLEndTran(SQL_HANDLE_DBC
, pConn
->sqlConn
, SQL_COMMIT
);
1167 SQLSetConnectAttr(pConn
->sqlConn
, SQL_ATTR_AUTOCOMMIT
, (SQLPOINTER
)SQL_AUTOCOMMIT_ON
, 0);
1168 MutexUnlock(pConn
->mutexQuery
);
1169 return ((nRet
== SQL_SUCCESS
) || (nRet
== SQL_SUCCESS_WITH_INFO
)) ? DBERR_SUCCESS
: DBERR_OTHER_ERROR
;
1173 * Rollback transaction
1175 extern "C" DWORD EXPORT
DrvRollback(DB2DRV_CONN
*pConn
)
1181 return DBERR_INVALID_HANDLE
;
1184 MutexLock(pConn
->mutexQuery
);
1185 nRet
= SQLEndTran(SQL_HANDLE_DBC
, pConn
->sqlConn
, SQL_ROLLBACK
);
1186 SQLSetConnectAttr(pConn
->sqlConn
, SQL_ATTR_AUTOCOMMIT
, (SQLPOINTER
)SQL_AUTOCOMMIT_ON
, 0);
1187 MutexUnlock(pConn
->mutexQuery
);
1188 return ((nRet
== SQL_SUCCESS
) || (nRet
== SQL_SUCCESS_WITH_INFO
)) ? DBERR_SUCCESS
: DBERR_OTHER_ERROR
;
1193 * Check if table exist
1195 extern "C" int EXPORT
DrvIsTableExist(DB2DRV_CONN
*pConn
, const NETXMS_WCHAR
*name
)
1198 swprintf(query
, 256, L
"SELECT count(*) FROM sysibm.systables WHERE type='T' AND upper(name)=upper('%ls')", name
);
1200 WCHAR errorText
[DBDRV_MAX_ERROR_TEXT
];
1201 int rc
= DBIsTableExist_Failure
;
1202 DB2DRV_QUERY_RESULT
*hResult
= (DB2DRV_QUERY_RESULT
*)DrvSelect(pConn
, query
, &error
, errorText
);
1203 if (hResult
!= NULL
)
1205 WCHAR buffer
[64] = L
"";
1206 DrvGetField(hResult
, 0, 0, buffer
, 64);
1207 rc
= (wcstol(buffer
, NULL
, 10) > 0) ? DBIsTableExist_Found
: DBIsTableExist_NotFound
;
1208 DrvFreeResult(hResult
);
1218 BOOL WINAPI
DllMain(HINSTANCE hInstance
, DWORD dwReason
, LPVOID lpReserved
)
1220 if (dwReason
== DLL_PROCESS_ATTACH
)
1222 DisableThreadLibraryCalls(hInstance
);