2 ** ODBC Database Driver
3 ** Copyright (C) 2004-2016 Victor Kirhenshtein
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.
26 DECLARE_DRIVER_HEADER("ODBC")
29 * Flag for enable/disable UNICODE
31 static bool m_useUnicode
= true;
34 * Convert ODBC state to NetXMS database error code and get error text
36 static DWORD
GetSQLErrorInfo(SQLSMALLINT nHandleType
, SQLHANDLE hHandle
, NETXMS_WCHAR
*errorText
)
43 // Get state information and convert it to NetXMS database error code
44 nRet
= SQLGetDiagFieldA(nHandleType
, hHandle
, 1, SQL_DIAG_SQLSTATE
, szState
, 16, &nChars
);
45 if (nRet
== SQL_SUCCESS
)
47 if ((!strcmp(szState
, "08003")) || // Connection does not exist
48 (!strcmp(szState
, "08S01")) || // Communication link failure
49 (!strcmp(szState
, "HYT00")) || // Timeout expired
50 (!strcmp(szState
, "HYT01"))) // Connection timeout expired
52 dwError
= DBERR_CONNECTION_LOST
;
56 dwError
= DBERR_OTHER_ERROR
;
61 dwError
= DBERR_OTHER_ERROR
;
65 if (errorText
!= NULL
)
68 nRet
= SQLGetDiagFieldW(nHandleType
, hHandle
, 1, SQL_DIAG_MESSAGE_TEXT
, errorText
, DBDRV_MAX_ERROR_TEXT
, &nChars
);
70 char buffer
[DBDRV_MAX_ERROR_TEXT
];
71 nRet
= SQLGetDiagFieldA(nHandleType
, hHandle
, 1, SQL_DIAG_MESSAGE_TEXT
, buffer
, DBDRV_MAX_ERROR_TEXT
, &nChars
);
73 if (nRet
== SQL_SUCCESS
)
76 buffer
[DBDRV_MAX_ERROR_TEXT
- 1] = 0;
77 MultiByteToWideChar(CP_ACP
, MB_PRECOMPOSED
, buffer
, -1, errorText
, DBDRV_MAX_ERROR_TEXT
);
79 RemoveTrailingCRLFW(errorText
);
83 wcscpy(errorText
, L
"Unable to obtain description for this error");
91 * Clear any pending result sets on given statement
93 static void ClearPendingResults(SQLHSTMT stmt
)
97 SQLRETURN rc
= SQLMoreResults(stmt
);
98 if ((rc
!= SQL_SUCCESS
) && (rc
!= SQL_SUCCESS_WITH_INFO
))
104 * Prepare string for using in SQL query - enclose in quotes and escape as needed
106 extern "C" NETXMS_WCHAR EXPORT
*DrvPrepareStringW(const NETXMS_WCHAR
*str
)
108 int len
= (int)wcslen(str
) + 3; // + two quotes and \0 at the end
109 int bufferSize
= len
+ 128;
110 NETXMS_WCHAR
*out
= (NETXMS_WCHAR
*)malloc(bufferSize
* sizeof(NETXMS_WCHAR
));
113 const NETXMS_WCHAR
*src
= str
;
115 for(outPos
= 1; *src
!= 0; src
++)
120 if (len
>= bufferSize
)
123 out
= (NETXMS_WCHAR
*)realloc(out
, bufferSize
* sizeof(NETXMS_WCHAR
));
125 out
[outPos
++] = L
'\'';
126 out
[outPos
++] = L
'\'';
130 out
[outPos
++] = *src
;
133 out
[outPos
++] = L
'\'';
139 extern "C" char EXPORT
*DrvPrepareStringA(const char *str
)
141 int len
= (int)strlen(str
) + 3; // + two quotes and \0 at the end
142 int bufferSize
= len
+ 128;
143 char *out
= (char *)malloc(bufferSize
);
146 const char *src
= str
;
148 for(outPos
= 1; *src
!= 0; src
++)
153 if (len
>= bufferSize
)
156 out
= (char *)realloc(out
, bufferSize
);
158 out
[outPos
++] = '\'';
159 out
[outPos
++] = '\'';
163 out
[outPos
++] = *src
;
166 out
[outPos
++] = '\'';
175 extern "C" bool EXPORT
DrvInit(const char *cmdLine
)
177 m_useUnicode
= ExtractNamedOptionValueAsBoolA(cmdLine
, "unicode", true);
184 extern "C" void EXPORT
DrvUnload()
189 * Connect to database
190 * pszHost should be set to ODBC source name, and pszDatabase is ignored
192 extern "C" DBDRV_CONNECTION EXPORT
DrvConnect(const char *pszHost
, const char *pszLogin
, const char *pszPassword
,
193 const char *pszDatabase
, const char *schema
, NETXMS_WCHAR
*errorText
)
198 // Allocate our connection structure
199 pConn
= (ODBCDRV_CONN
*)malloc(sizeof(ODBCDRV_CONN
));
201 // Allocate environment
202 iResult
= SQLAllocHandle(SQL_HANDLE_ENV
, SQL_NULL_HANDLE
, &pConn
->sqlEnv
);
203 if ((iResult
!= SQL_SUCCESS
) && (iResult
!= SQL_SUCCESS_WITH_INFO
))
205 wcscpy(errorText
, L
"Cannot allocate environment handle");
206 goto connect_failure_0
;
209 // Set required ODBC version
210 iResult
= SQLSetEnvAttr(pConn
->sqlEnv
, SQL_ATTR_ODBC_VERSION
, (void *)SQL_OV_ODBC3
, 0);
211 if ((iResult
!= SQL_SUCCESS
) && (iResult
!= SQL_SUCCESS_WITH_INFO
))
213 wcscpy(errorText
, L
"Call to SQLSetEnvAttr failed");
214 goto connect_failure_1
;
217 // Allocate connection handle, set timeout
218 iResult
= SQLAllocHandle(SQL_HANDLE_DBC
, pConn
->sqlEnv
, &pConn
->sqlConn
);
219 if ((iResult
!= SQL_SUCCESS
) && (iResult
!= SQL_SUCCESS_WITH_INFO
))
221 wcscpy(errorText
, L
"Cannot allocate connection handle");
222 goto connect_failure_1
;
224 SQLSetConnectAttr(pConn
->sqlConn
, SQL_ATTR_LOGIN_TIMEOUT
, (SQLPOINTER
*)15, 0);
225 SQLSetConnectAttr(pConn
->sqlConn
, SQL_ATTR_CONNECTION_TIMEOUT
, (SQLPOINTER
*)30, 0);
227 // Connect to the datasource
228 // If DSN name contains = character, assume that it's a connection string
229 if (strchr(pszHost
, '=') != NULL
)
232 iResult
= SQLDriverConnectA(pConn
->sqlConn
, NULL
, (SQLCHAR
*)pszHost
, SQL_NTS
, NULL
, 0, &outLen
, SQL_DRIVER_NOPROMPT
);
236 iResult
= SQLConnectA(pConn
->sqlConn
, (SQLCHAR
*)pszHost
, SQL_NTS
,
237 (SQLCHAR
*)pszLogin
, SQL_NTS
, (SQLCHAR
*)pszPassword
, SQL_NTS
);
239 if ((iResult
!= SQL_SUCCESS
) && (iResult
!= SQL_SUCCESS_WITH_INFO
))
241 GetSQLErrorInfo(SQL_HANDLE_DBC
, pConn
->sqlConn
, errorText
);
242 goto connect_failure_2
;
246 pConn
->mutexQuery
= MutexCreate();
249 return (DBDRV_CONNECTION
)pConn
;
253 SQLFreeHandle(SQL_HANDLE_DBC
, pConn
->sqlConn
);
256 SQLFreeHandle(SQL_HANDLE_ENV
, pConn
->sqlEnv
);
264 * Disconnect from database
266 extern "C" void EXPORT
DrvDisconnect(ODBCDRV_CONN
*pConn
)
268 MutexLock(pConn
->mutexQuery
);
269 MutexUnlock(pConn
->mutexQuery
);
270 SQLDisconnect(pConn
->sqlConn
);
271 SQLFreeHandle(SQL_HANDLE_DBC
, pConn
->sqlConn
);
272 SQLFreeHandle(SQL_HANDLE_ENV
, pConn
->sqlEnv
);
273 MutexDestroy(pConn
->mutexQuery
);
280 extern "C" DBDRV_STATEMENT EXPORT
DrvPrepare(ODBCDRV_CONN
*pConn
, NETXMS_WCHAR
*pwszQuery
, DWORD
*pdwError
, NETXMS_WCHAR
*errorText
)
284 ODBCDRV_STATEMENT
*result
;
286 MutexLock(pConn
->mutexQuery
);
288 // Allocate statement handle
289 iResult
= SQLAllocHandle(SQL_HANDLE_STMT
, pConn
->sqlConn
, &stmt
);
290 if ((iResult
== SQL_SUCCESS
) || (iResult
== SQL_SUCCESS_WITH_INFO
))
295 #if defined(_WIN32) || defined(UNICODE_UCS2)
296 iResult
= SQLPrepareW(stmt
, (SQLWCHAR
*)pwszQuery
, SQL_NTS
);
298 SQLWCHAR
*temp
= UCS2StringFromUCS4String(pwszQuery
);
299 iResult
= SQLPrepareW(stmt
, temp
, SQL_NTS
);
305 char *temp
= MBStringFromWideString(pwszQuery
);
306 iResult
= SQLPrepareA(stmt
, (SQLCHAR
*)temp
, SQL_NTS
);
309 if ((iResult
== SQL_SUCCESS
) ||
310 (iResult
== SQL_SUCCESS_WITH_INFO
))
312 result
= (ODBCDRV_STATEMENT
*)malloc(sizeof(ODBCDRV_STATEMENT
));
313 result
->handle
= stmt
;
314 result
->buffers
= new Array(0, 16, true);
315 result
->connection
= pConn
;
316 *pdwError
= DBERR_SUCCESS
;
320 *pdwError
= GetSQLErrorInfo(SQL_HANDLE_STMT
, stmt
, errorText
);
321 SQLFreeHandle(SQL_HANDLE_STMT
, stmt
);
327 *pdwError
= GetSQLErrorInfo(SQL_HANDLE_DBC
, pConn
->sqlConn
, errorText
);
331 MutexUnlock(pConn
->mutexQuery
);
336 * Bind parameter to statement
338 extern "C" void EXPORT
DrvBind(ODBCDRV_STATEMENT
*stmt
, int pos
, int sqlType
, int cType
, void *buffer
, int allocType
)
340 static SQLSMALLINT odbcSqlType
[] = { SQL_VARCHAR
, SQL_INTEGER
, SQL_BIGINT
, SQL_DOUBLE
, SQL_LONGVARCHAR
};
341 static SQLSMALLINT odbcCTypeW
[] = { SQL_C_WCHAR
, SQL_C_SLONG
, SQL_C_ULONG
, SQL_C_SBIGINT
, SQL_C_UBIGINT
, SQL_C_DOUBLE
, SQL_C_WCHAR
};
342 static SQLSMALLINT odbcCTypeA
[] = { SQL_C_CHAR
, SQL_C_SLONG
, SQL_C_ULONG
, SQL_C_SBIGINT
, SQL_C_UBIGINT
, SQL_C_DOUBLE
, SQL_C_CHAR
};
343 static DWORD bufferSize
[] = { 0, sizeof(LONG
), sizeof(DWORD
), sizeof(INT64
), sizeof(QWORD
), sizeof(double), 0 };
345 int length
= (cType
== DB_CTYPE_STRING
) ? (int)wcslen((NETXMS_WCHAR
*)buffer
) + 1 : 0;
347 SQLPOINTER sqlBuffer
;
353 #if defined(_WIN32) || defined(UNICODE_UCS2)
354 if (cType
== DB_CTYPE_UTF8_STRING
)
356 sqlBuffer
= WideStringFromUTF8String((char *)buffer
);
357 stmt
->buffers
->add(sqlBuffer
);
358 length
= (int)strlen((char *)sqlBuffer
) + 1;
365 if (cType
== DB_CTYPE_STRING
)
367 sqlBuffer
= UCS2StringFromUCS4String((NETXMS_WCHAR
*)buffer
);
368 stmt
->buffers
->add(sqlBuffer
);
370 else if (cType
== DB_CTYPE_UTF8_STRING
)
372 sqlBuffer
= UCS2StringFromUTF8String((char *)buffer
);
373 stmt
->buffers
->add(sqlBuffer
);
374 length
= (int)strlen((char *)sqlBuffer
) + 1;
384 if (cType
== DB_CTYPE_STRING
)
386 sqlBuffer
= MBStringFromWideString((NETXMS_WCHAR
*)buffer
);
387 stmt
->buffers
->add(sqlBuffer
);
389 else if (cType
== DB_CTYPE_UTF8_STRING
)
391 sqlBuffer
= MBStringFromUTF8String((char *)buffer
);
392 stmt
->buffers
->add(sqlBuffer
);
393 length
= (int)strlen((char *)sqlBuffer
) + 1;
401 case DB_BIND_DYNAMIC
:
404 #if defined(_WIN32) || defined(UNICODE_UCS2)
405 if (cType
== DB_CTYPE_UTF8_STRING
)
407 sqlBuffer
= WideStringFromUTF8String((char *)buffer
);
409 length
= (int)strlen((char *)sqlBuffer
) + 1;
416 if (cType
== DB_CTYPE_STRING
)
418 sqlBuffer
= UCS2StringFromUCS4String((NETXMS_WCHAR
*)buffer
);
421 else if (cType
== DB_CTYPE_UTF8_STRING
)
423 sqlBuffer
= UCS2StringFromUTF8String((char *)buffer
);
425 length
= (int)strlen((char *)sqlBuffer
) + 1;
435 if (cType
== DB_CTYPE_STRING
)
437 sqlBuffer
= MBStringFromWideString((NETXMS_WCHAR
*)buffer
);
440 else if (cType
== DB_CTYPE_UTF8_STRING
)
442 sqlBuffer
= MBStringFromUTF8String((char *)buffer
);
444 length
= (int)strlen((char *)sqlBuffer
) + 1;
451 stmt
->buffers
->add(sqlBuffer
);
453 case DB_BIND_TRANSIENT
:
456 #if defined(_WIN32) || defined(UNICODE_UCS2)
457 if (cType
== DB_CTYPE_UTF8_STRING
)
459 sqlBuffer
= WideStringFromUTF8String((char *)buffer
);
460 length
= (int)strlen((char *)sqlBuffer
) + 1;
464 sqlBuffer
= nx_memdup(buffer
, (cType
== DB_CTYPE_STRING
) ? (DWORD
)(length
* sizeof(WCHAR
)) : bufferSize
[cType
]);
467 if (cType
== DB_CTYPE_STRING
)
469 sqlBuffer
= UCS2StringFromUCS4String((NETXMS_WCHAR
*)buffer
);
471 if (cType
== DB_CTYPE_UTF8_STRING
)
473 sqlBuffer
= UCS2StringFromUTF8String((char *)buffer
);
474 length
= (int)strlen((char *)sqlBuffer
) + 1;
478 sqlBuffer
= nx_memdup(buffer
, bufferSize
[cType
]);
484 if (cType
== DB_CTYPE_STRING
)
486 sqlBuffer
= MBStringFromWideString((NETXMS_WCHAR
*)buffer
);
488 else if (cType
== DB_CTYPE_UTF8_STRING
)
490 sqlBuffer
= MBStringFromUTF8String((char *)buffer
);
491 length
= (int)strlen((char *)sqlBuffer
) + 1;
495 sqlBuffer
= nx_memdup(buffer
, bufferSize
[cType
]);
498 stmt
->buffers
->add(sqlBuffer
);
501 return; // Invalid call
503 SQLBindParameter(stmt
->handle
, pos
, SQL_PARAM_INPUT
, m_useUnicode
? odbcCTypeW
[cType
] : odbcCTypeA
[cType
], odbcSqlType
[sqlType
],
504 ((cType
== DB_CTYPE_STRING
) || (cType
== DB_CTYPE_UTF8_STRING
)) ? length
: 0, 0, sqlBuffer
, 0, NULL
);
508 * Execute prepared statement
510 extern "C" DWORD EXPORT
DrvExecute(ODBCDRV_CONN
*pConn
, ODBCDRV_STATEMENT
*stmt
, NETXMS_WCHAR
*errorText
)
514 MutexLock(pConn
->mutexQuery
);
515 long rc
= SQLExecute(stmt
->handle
);
516 if ((rc
== SQL_SUCCESS
) ||
517 (rc
== SQL_SUCCESS_WITH_INFO
) ||
520 ClearPendingResults(stmt
->handle
);
521 dwResult
= DBERR_SUCCESS
;
525 dwResult
= GetSQLErrorInfo(SQL_HANDLE_STMT
, stmt
->handle
, errorText
);
527 MutexUnlock(pConn
->mutexQuery
);
532 * Destroy prepared statement
534 extern "C" void EXPORT
DrvFreeStatement(ODBCDRV_STATEMENT
*stmt
)
539 MutexLock(stmt
->connection
->mutexQuery
);
540 SQLFreeHandle(SQL_HANDLE_STMT
, stmt
->handle
);
541 MutexUnlock(stmt
->connection
->mutexQuery
);
542 delete stmt
->buffers
;
547 * Perform non-SELECT query
549 extern "C" DWORD EXPORT
DrvQuery(ODBCDRV_CONN
*pConn
, NETXMS_WCHAR
*pwszQuery
, NETXMS_WCHAR
*errorText
)
553 MutexLock(pConn
->mutexQuery
);
555 // Allocate statement handle
556 SQLHSTMT sqlStatement
;
557 SQLRETURN iResult
= SQLAllocHandle(SQL_HANDLE_STMT
, pConn
->sqlConn
, &sqlStatement
);
558 if ((iResult
== SQL_SUCCESS
) || (iResult
== SQL_SUCCESS_WITH_INFO
))
563 #if defined(_WIN32) || defined(UNICODE_UCS2)
564 iResult
= SQLExecDirectW(sqlStatement
, (SQLWCHAR
*)pwszQuery
, SQL_NTS
);
566 SQLWCHAR
*temp
= UCS2StringFromUCS4String(pwszQuery
);
567 iResult
= SQLExecDirectW(sqlStatement
, temp
, SQL_NTS
);
573 char *temp
= MBStringFromWideString(pwszQuery
);
574 iResult
= SQLExecDirectA(sqlStatement
, (SQLCHAR
*)temp
, SQL_NTS
);
577 if ((iResult
== SQL_SUCCESS
) ||
578 (iResult
== SQL_SUCCESS_WITH_INFO
) ||
579 (iResult
== SQL_NO_DATA
))
581 dwResult
= DBERR_SUCCESS
;
585 dwResult
= GetSQLErrorInfo(SQL_HANDLE_STMT
, sqlStatement
, errorText
);
587 SQLFreeHandle(SQL_HANDLE_STMT
, sqlStatement
);
591 dwResult
= GetSQLErrorInfo(SQL_HANDLE_DBC
, pConn
->sqlConn
, errorText
);
594 MutexUnlock(pConn
->mutexQuery
);
599 * Get complete field data
601 static NETXMS_WCHAR
*GetFieldData(SQLHSTMT sqlStatement
, short column
)
603 NETXMS_WCHAR
*result
= NULL
;
607 #if defined(_WIN32) || defined(UNICODE_UCS2)
609 SQLRETURN rc
= SQLGetData(sqlStatement
, column
, SQL_C_WCHAR
, buffer
, sizeof(buffer
), &dataSize
);
610 if (((rc
== SQL_SUCCESS
) || ((rc
== SQL_SUCCESS_WITH_INFO
) && (dataSize
>= 0) && (dataSize
<= (SQLLEN
)(sizeof(buffer
) - sizeof(WCHAR
))))) && (dataSize
!= SQL_NULL_DATA
))
612 result
= wcsdup(buffer
);
614 else if ((rc
== SQL_SUCCESS_WITH_INFO
) && (dataSize
!= SQL_NULL_DATA
))
616 if (dataSize
> (SQLLEN
)(sizeof(buffer
) - sizeof(WCHAR
)))
618 WCHAR
*temp
= (WCHAR
*)malloc(dataSize
+ sizeof(WCHAR
));
619 memcpy(temp
, buffer
, sizeof(buffer
));
620 rc
= SQLGetData(sqlStatement
, column
, SQL_C_WCHAR
, &temp
[255], dataSize
- 254 * sizeof(WCHAR
), &dataSize
);
621 if ((rc
== SQL_SUCCESS
) || (rc
== SQL_SUCCESS_WITH_INFO
))
630 else if (dataSize
== SQL_NO_TOTAL
)
632 size_t tempSize
= sizeof(buffer
) * 4;
633 WCHAR
*temp
= (WCHAR
*)malloc(tempSize
);
634 memcpy(temp
, buffer
, sizeof(buffer
));
635 size_t offset
= sizeof(buffer
) - sizeof(WCHAR
);
638 SQLLEN readSize
= tempSize
- offset
;
639 rc
= SQLGetData(sqlStatement
, column
, SQL_C_WCHAR
, (char *)temp
+ offset
, readSize
, &dataSize
);
640 if ((rc
== SQL_SUCCESS
) || (rc
== SQL_NO_DATA
))
642 if (dataSize
== SQL_NO_TOTAL
)
644 tempSize
+= sizeof(buffer
) * 4;
648 tempSize
+= dataSize
- readSize
;
650 temp
= (WCHAR
*)realloc(temp
, tempSize
);
651 offset
+= readSize
- sizeof(WCHAR
);
657 UCS2CHAR buffer
[256];
658 SQLRETURN rc
= SQLGetData(sqlStatement
, column
, SQL_C_WCHAR
, buffer
, sizeof(buffer
), &dataSize
);
659 if (((rc
== SQL_SUCCESS
) || ((rc
== SQL_SUCCESS_WITH_INFO
) && (dataSize
>= 0) && (dataSize
<= (SQLLEN
)(sizeof(buffer
) - sizeof(UCS2CHAR
))))) && (dataSize
!= SQL_NULL_DATA
))
661 int len
= ucs2_strlen(buffer
);
662 result
= (NETXMS_WCHAR
*)malloc((len
+ 1) * sizeof(NETXMS_WCHAR
));
663 ucs2_to_ucs4(buffer
, -1, result
, len
+ 1);
665 else if ((rc
== SQL_SUCCESS_WITH_INFO
) && (dataSize
!= SQL_NULL_DATA
))
667 if (dataSize
> (SQLLEN
)(sizeof(buffer
) - sizeof(UCS2CHAR
)))
669 UCS2CHAR
*temp
= (UCS2CHAR
*)malloc(dataSize
+ sizeof(UCS2CHAR
));
670 memcpy(temp
, buffer
, sizeof(buffer
));
671 rc
= SQLGetData(sqlStatement
, column
, SQL_C_WCHAR
, &temp
[255], dataSize
- 254 * sizeof(UCS2CHAR
), &dataSize
);
672 if ((rc
== SQL_SUCCESS
) || (rc
== SQL_SUCCESS_WITH_INFO
))
674 int len
= ucs2_strlen(temp
);
675 result
= (NETXMS_WCHAR
*)malloc((len
+ 1) * sizeof(NETXMS_WCHAR
));
676 ucs2_to_ucs4(temp
, -1, result
, len
+ 1);
680 else if (dataSize
== SQL_NO_TOTAL
)
682 size_t tempSize
= sizeof(buffer
) * 4;
683 UCS2CHAR
*temp
= (UCS2CHAR
*)malloc(tempSize
);
684 memcpy(temp
, buffer
, sizeof(buffer
));
685 size_t offset
= sizeof(buffer
) - sizeof(UCS2CHAR
);
688 SQLLEN readSize
= tempSize
- offset
;
689 rc
= SQLGetData(sqlStatement
, column
, SQL_C_WCHAR
, (char *)temp
+ offset
, readSize
, &dataSize
);
690 if ((rc
== SQL_SUCCESS
) || (rc
== SQL_NO_DATA
))
692 if (dataSize
== SQL_NO_TOTAL
)
694 tempSize
+= sizeof(buffer
) * 4;
698 tempSize
+= dataSize
- readSize
;
700 temp
= (UCS2CHAR
*)realloc(temp
, tempSize
);
701 offset
+= readSize
- sizeof(UCS2CHAR
);
703 int len
= ucs2_strlen(temp
);
704 result
= (NETXMS_WCHAR
*)malloc((len
+ 1) * sizeof(NETXMS_WCHAR
));
705 ucs2_to_ucs4(temp
, -1, result
, len
+ 1);
714 SQLRETURN rc
= SQLGetData(sqlStatement
, column
, SQL_C_CHAR
, buffer
, sizeof(buffer
), &dataSize
);
715 if (((rc
== SQL_SUCCESS
) || ((rc
== SQL_SUCCESS_WITH_INFO
) && (dataSize
>= 0) && (dataSize
<= (SQLLEN
)(sizeof(buffer
) - 1)))) && (dataSize
!= SQL_NULL_DATA
))
717 result
= WideStringFromMBString(buffer
);
719 else if ((rc
== SQL_SUCCESS_WITH_INFO
) && (dataSize
!= SQL_NULL_DATA
))
721 if (dataSize
> (SQLLEN
)(sizeof(buffer
) - 1))
723 char *temp
= (char *)malloc(dataSize
+ 1);
724 memcpy(temp
, buffer
, sizeof(buffer
));
725 rc
= SQLGetData(sqlStatement
, column
, SQL_C_CHAR
, &temp
[255], dataSize
- 254, &dataSize
);
726 if ((rc
== SQL_SUCCESS
) || (rc
== SQL_SUCCESS_WITH_INFO
))
728 result
= WideStringFromMBString(temp
);
732 else if (dataSize
== SQL_NO_TOTAL
)
734 size_t tempSize
= sizeof(buffer
) * 4;
735 char *temp
= (char *)malloc(tempSize
);
736 memcpy(temp
, buffer
, sizeof(buffer
));
737 size_t offset
= sizeof(buffer
) - 1;
740 SQLLEN readSize
= tempSize
- offset
;
741 rc
= SQLGetData(sqlStatement
, column
, SQL_C_CHAR
, &temp
[offset
], readSize
, &dataSize
);
742 if ((rc
== SQL_SUCCESS
) || (rc
== SQL_NO_DATA
))
744 if (dataSize
== SQL_NO_TOTAL
)
746 tempSize
+= sizeof(buffer
) * 4;
750 tempSize
+= dataSize
- readSize
;
752 temp
= (char *)realloc(temp
, tempSize
);
753 offset
+= readSize
- 1;
755 result
= WideStringFromMBString(temp
);
760 return (result
!= NULL
) ? result
: wcsdup(L
"");
764 * Process results of SELECT query
766 static ODBCDRV_QUERY_RESULT
*ProcessSelectResults(SQLHSTMT stmt
)
768 // Allocate result buffer and determine column info
769 ODBCDRV_QUERY_RESULT
*pResult
= (ODBCDRV_QUERY_RESULT
*)malloc(sizeof(ODBCDRV_QUERY_RESULT
));
771 SQLNumResultCols(stmt
, &wNumCols
);
772 pResult
->numColumns
= wNumCols
;
773 pResult
->numRows
= 0;
774 pResult
->pValues
= NULL
;
777 pResult
->columnNames
= (char **)malloc(sizeof(char *) * pResult
->numColumns
);
778 for(int i
= 0; i
< (int)pResult
->numColumns
; i
++)
783 SQLRETURN iResult
= SQLColAttributeA(stmt
, (SQLSMALLINT
)(i
+ 1), SQL_DESC_NAME
, name
, 256, &len
, NULL
);
784 if ((iResult
== SQL_SUCCESS
) ||
785 (iResult
== SQL_SUCCESS_WITH_INFO
))
788 pResult
->columnNames
[i
] = strdup(name
);
792 pResult
->columnNames
[i
] = strdup("");
799 while(iResult
= SQLFetch(stmt
),
800 (iResult
== SQL_SUCCESS
) || (iResult
== SQL_SUCCESS_WITH_INFO
))
803 pResult
->pValues
= (NETXMS_WCHAR
**)realloc(pResult
->pValues
,
804 sizeof(NETXMS_WCHAR
*) * (pResult
->numRows
* pResult
->numColumns
));
805 for(int i
= 1; i
<= (int)pResult
->numColumns
; i
++)
807 pResult
->pValues
[iCurrValue
++] = GetFieldData(stmt
, (short)i
);
815 * Perform SELECT query
817 extern "C" DBDRV_RESULT EXPORT
DrvSelect(ODBCDRV_CONN
*pConn
, NETXMS_WCHAR
*pwszQuery
, DWORD
*pdwError
, NETXMS_WCHAR
*errorText
)
819 ODBCDRV_QUERY_RESULT
*pResult
= NULL
;
821 MutexLock(pConn
->mutexQuery
);
823 // Allocate statement handle
824 SQLHSTMT sqlStatement
;
825 SQLRETURN iResult
= SQLAllocHandle(SQL_HANDLE_STMT
, pConn
->sqlConn
, &sqlStatement
);
826 if ((iResult
== SQL_SUCCESS
) || (iResult
== SQL_SUCCESS_WITH_INFO
))
831 #if defined(_WIN32) || defined(UNICODE_UCS2)
832 iResult
= SQLExecDirectW(sqlStatement
, (SQLWCHAR
*)pwszQuery
, SQL_NTS
);
834 SQLWCHAR
*temp
= UCS2StringFromUCS4String(pwszQuery
);
835 iResult
= SQLExecDirectW(sqlStatement
, temp
, SQL_NTS
);
841 char *temp
= MBStringFromWideString(pwszQuery
);
842 iResult
= SQLExecDirectA(sqlStatement
, (SQLCHAR
*)temp
, SQL_NTS
);
845 if ((iResult
== SQL_SUCCESS
) ||
846 (iResult
== SQL_SUCCESS_WITH_INFO
))
848 pResult
= ProcessSelectResults(sqlStatement
);
849 *pdwError
= DBERR_SUCCESS
;
853 *pdwError
= GetSQLErrorInfo(SQL_HANDLE_STMT
, sqlStatement
, errorText
);
855 SQLFreeHandle(SQL_HANDLE_STMT
, sqlStatement
);
859 *pdwError
= GetSQLErrorInfo(SQL_HANDLE_DBC
, pConn
->sqlConn
, errorText
);
862 MutexUnlock(pConn
->mutexQuery
);
867 * Perform SELECT query using prepared statement
869 extern "C" DBDRV_RESULT EXPORT
DrvSelectPrepared(ODBCDRV_CONN
*pConn
, ODBCDRV_STATEMENT
*stmt
, DWORD
*pdwError
, NETXMS_WCHAR
*errorText
)
871 ODBCDRV_QUERY_RESULT
*pResult
= NULL
;
873 MutexLock(pConn
->mutexQuery
);
874 long rc
= SQLExecute(stmt
->handle
);
875 if ((rc
== SQL_SUCCESS
) ||
876 (rc
== SQL_SUCCESS_WITH_INFO
))
878 pResult
= ProcessSelectResults(stmt
->handle
);
879 *pdwError
= DBERR_SUCCESS
;
883 *pdwError
= GetSQLErrorInfo(SQL_HANDLE_STMT
, stmt
->handle
, errorText
);
885 MutexUnlock(pConn
->mutexQuery
);
890 * Get field length from result
892 extern "C" LONG EXPORT
DrvGetFieldLength(ODBCDRV_QUERY_RESULT
*pResult
, int iRow
, int iColumn
)
898 if ((iRow
< pResult
->numRows
) && (iRow
>= 0) &&
899 (iColumn
< pResult
->numColumns
) && (iColumn
>= 0))
900 nLen
= (LONG
)wcslen(pResult
->pValues
[iRow
* pResult
->numColumns
+ iColumn
]);
906 * Get field value from result
908 extern "C" NETXMS_WCHAR EXPORT
*DrvGetField(ODBCDRV_QUERY_RESULT
*pResult
, int iRow
, int iColumn
,
909 NETXMS_WCHAR
*pBuffer
, int nBufSize
)
911 NETXMS_WCHAR
*pValue
= NULL
;
915 if ((iRow
< pResult
->numRows
) && (iRow
>= 0) &&
916 (iColumn
< pResult
->numColumns
) && (iColumn
>= 0))
919 wcsncpy_s(pBuffer
, nBufSize
, pResult
->pValues
[iRow
* pResult
->numColumns
+ iColumn
], _TRUNCATE
);
921 wcsncpy(pBuffer
, pResult
->pValues
[iRow
* pResult
->numColumns
+ iColumn
], nBufSize
);
922 pBuffer
[nBufSize
- 1] = 0;
931 * Get number of rows in result
933 extern "C" int EXPORT
DrvGetNumRows(ODBCDRV_QUERY_RESULT
*pResult
)
935 return (pResult
!= NULL
) ? pResult
->numRows
: 0;
939 * Get column count in query result
941 extern "C" int EXPORT
DrvGetColumnCount(ODBCDRV_QUERY_RESULT
*pResult
)
943 return (pResult
!= NULL
) ? pResult
->numColumns
: 0;
947 * Get column name in query result
949 extern "C" const char EXPORT
*DrvGetColumnName(ODBCDRV_QUERY_RESULT
*pResult
, int column
)
951 return ((pResult
!= NULL
) && (column
>= 0) && (column
< pResult
->numColumns
)) ? pResult
->columnNames
[column
] : NULL
;
955 * Free SELECT results
957 extern "C" void EXPORT
DrvFreeResult(ODBCDRV_QUERY_RESULT
*pResult
)
963 iNumValues
= pResult
->numColumns
* pResult
->numRows
;
964 for(i
= 0; i
< iNumValues
; i
++)
965 safe_free(pResult
->pValues
[i
]);
966 safe_free(pResult
->pValues
);
968 for(i
= 0; i
< pResult
->numColumns
; i
++)
969 safe_free(pResult
->columnNames
[i
]);
970 safe_free(pResult
->columnNames
);
977 * Perform unbuffered SELECT query
979 extern "C" DBDRV_UNBUFFERED_RESULT EXPORT
DrvSelectUnbuffered(ODBCDRV_CONN
*pConn
, NETXMS_WCHAR
*pwszQuery
, DWORD
*pdwError
, NETXMS_WCHAR
*errorText
)
981 ODBCDRV_UNBUFFERED_QUERY_RESULT
*pResult
= NULL
;
986 MutexLock(pConn
->mutexQuery
);
988 // Allocate statement handle
989 SQLHSTMT sqlStatement
;
990 iResult
= SQLAllocHandle(SQL_HANDLE_STMT
, pConn
->sqlConn
, &sqlStatement
);
991 if ((iResult
== SQL_SUCCESS
) || (iResult
== SQL_SUCCESS_WITH_INFO
))
996 #if defined(_WIN32) || defined(UNICODE_UCS2)
997 iResult
= SQLExecDirectW(sqlStatement
, (SQLWCHAR
*)pwszQuery
, SQL_NTS
);
999 SQLWCHAR
*temp
= UCS2StringFromUCS4String(pwszQuery
);
1000 iResult
= SQLExecDirectW(sqlStatement
, temp
, SQL_NTS
);
1006 char *temp
= MBStringFromWideString(pwszQuery
);
1007 iResult
= SQLExecDirectA(sqlStatement
, (SQLCHAR
*)temp
, SQL_NTS
);
1010 if ((iResult
== SQL_SUCCESS
) || (iResult
== SQL_SUCCESS_WITH_INFO
))
1012 // Allocate result buffer and determine column info
1013 pResult
= (ODBCDRV_UNBUFFERED_QUERY_RESULT
*)malloc(sizeof(ODBCDRV_UNBUFFERED_QUERY_RESULT
));
1014 pResult
->sqlStatement
= sqlStatement
;
1015 pResult
->isPrepared
= false;
1017 SQLNumResultCols(sqlStatement
, &wNumCols
);
1018 pResult
->numColumns
= wNumCols
;
1019 pResult
->pConn
= pConn
;
1020 pResult
->noMoreRows
= false;
1023 pResult
->columnNames
= (char **)malloc(sizeof(char *) * pResult
->numColumns
);
1024 for(i
= 0; i
< pResult
->numColumns
; i
++)
1029 iResult
= SQLColAttributeA(sqlStatement
, (SQLSMALLINT
)(i
+ 1),
1030 SQL_DESC_NAME
, name
, 256, &len
, NULL
);
1031 if ((iResult
== SQL_SUCCESS
) ||
1032 (iResult
== SQL_SUCCESS_WITH_INFO
))
1035 pResult
->columnNames
[i
] = strdup(name
);
1039 pResult
->columnNames
[i
] = strdup("");
1043 // Column values cache
1044 pResult
->values
= (NETXMS_WCHAR
**)malloc(sizeof(NETXMS_WCHAR
*) * pResult
->numColumns
);
1045 memset(pResult
->values
, 0, sizeof(NETXMS_WCHAR
*) * pResult
->numColumns
);
1047 *pdwError
= DBERR_SUCCESS
;
1051 *pdwError
= GetSQLErrorInfo(SQL_HANDLE_STMT
, sqlStatement
, errorText
);
1052 // Free statement handle if query failed
1053 SQLFreeHandle(SQL_HANDLE_STMT
, sqlStatement
);
1058 *pdwError
= GetSQLErrorInfo(SQL_HANDLE_DBC
, pConn
->sqlConn
, errorText
);
1061 if (pResult
== NULL
) // Unlock mutex if query has failed
1062 MutexUnlock(pConn
->mutexQuery
);
1067 * Perform unbuffered SELECT query using prepared statement
1069 extern "C" DBDRV_UNBUFFERED_RESULT EXPORT
DrvSelectPreparedUnbuffered(ODBCDRV_CONN
*pConn
, ODBCDRV_STATEMENT
*stmt
, DWORD
*pdwError
, NETXMS_WCHAR
*errorText
)
1071 ODBCDRV_UNBUFFERED_QUERY_RESULT
*pResult
= NULL
;
1073 MutexLock(pConn
->mutexQuery
);
1074 SQLRETURN rc
= SQLExecute(stmt
->handle
);
1075 if ((rc
== SQL_SUCCESS
) || (rc
== SQL_SUCCESS_WITH_INFO
))
1077 // Allocate result buffer and determine column info
1078 pResult
= (ODBCDRV_UNBUFFERED_QUERY_RESULT
*)malloc(sizeof(ODBCDRV_UNBUFFERED_QUERY_RESULT
));
1079 pResult
->sqlStatement
= stmt
->handle
;
1080 pResult
->isPrepared
= true;
1083 SQLNumResultCols(pResult
->sqlStatement
, &wNumCols
);
1084 pResult
->numColumns
= wNumCols
;
1085 pResult
->pConn
= pConn
;
1086 pResult
->noMoreRows
= false;
1087 pResult
->values
= (NETXMS_WCHAR
**)malloc(sizeof(NETXMS_WCHAR
*) * pResult
->numColumns
);
1088 memset(pResult
->values
, 0, sizeof(NETXMS_WCHAR
*) * pResult
->numColumns
);
1091 pResult
->columnNames
= (char **)malloc(sizeof(char *) * pResult
->numColumns
);
1092 for(int i
= 0; i
< pResult
->numColumns
; i
++)
1097 rc
= SQLColAttributeA(pResult
->sqlStatement
, (SQLSMALLINT
)(i
+ 1), SQL_DESC_NAME
, name
, 256, &len
, NULL
);
1098 if ((rc
== SQL_SUCCESS
) || (rc
== SQL_SUCCESS_WITH_INFO
))
1101 pResult
->columnNames
[i
] = strdup(name
);
1105 pResult
->columnNames
[i
] = strdup("");
1108 *pdwError
= DBERR_SUCCESS
;
1112 *pdwError
= GetSQLErrorInfo(SQL_HANDLE_STMT
, stmt
->handle
, errorText
);
1115 if (pResult
== NULL
) // Unlock mutex if query has failed
1116 MutexUnlock(pConn
->mutexQuery
);
1121 * Fetch next result line from asynchronous SELECT results
1123 extern "C" bool EXPORT
DrvFetch(ODBCDRV_UNBUFFERED_QUERY_RESULT
*pResult
)
1125 bool success
= false;
1127 if (pResult
!= NULL
)
1129 SQLRETURN rc
= SQLFetch(pResult
->sqlStatement
);
1130 success
= ((rc
== SQL_SUCCESS
) || (rc
== SQL_SUCCESS_WITH_INFO
));
1133 for(int i
= 0; i
< pResult
->numColumns
; i
++)
1135 free(pResult
->values
[i
]);
1136 pResult
->values
[i
] = GetFieldData(pResult
->sqlStatement
, (short)i
+ 1);
1141 pResult
->noMoreRows
= true;
1148 * Get field length from async query result
1150 extern "C" LONG EXPORT
DrvGetFieldLengthUnbuffered(ODBCDRV_UNBUFFERED_QUERY_RESULT
*result
, int col
)
1155 if ((col
>= result
->numColumns
) || (col
< 0))
1158 return (result
->values
[col
] != NULL
) ? (LONG
)wcslen(result
->values
[col
]) : -1;
1162 * Get field from current row in async query result
1164 extern "C" NETXMS_WCHAR EXPORT
*DrvGetFieldUnbuffered(ODBCDRV_UNBUFFERED_QUERY_RESULT
*result
, int col
, NETXMS_WCHAR
*buffer
, int bufferSize
)
1166 // Check if we have valid result handle
1170 // Check if there are valid fetched row
1171 if (result
->noMoreRows
)
1174 if ((col
>= 0) && (col
< result
->numColumns
) && (result
->values
[col
] != NULL
))
1176 wcsncpy(buffer
, result
->values
[col
], bufferSize
- 1);
1177 buffer
[bufferSize
- 1] = 0;
1187 * Get column count in async query result
1189 extern "C" int EXPORT
DrvGetColumnCountUnbuffered(ODBCDRV_UNBUFFERED_QUERY_RESULT
*pResult
)
1191 return (pResult
!= NULL
) ? pResult
->numColumns
: 0;
1195 * Get column name in async query result
1197 extern "C" const char EXPORT
*DrvGetColumnNameUnbuffered(ODBCDRV_UNBUFFERED_QUERY_RESULT
*pResult
, int column
)
1199 return ((pResult
!= NULL
) && (column
>= 0) && (column
< pResult
->numColumns
)) ? pResult
->columnNames
[column
] : NULL
;
1203 * Destroy result of async query
1205 extern "C" void EXPORT
DrvFreeUnbufferedResult(ODBCDRV_UNBUFFERED_QUERY_RESULT
*pResult
)
1207 if (pResult
== NULL
)
1210 if (pResult
->isPrepared
)
1211 SQLCloseCursor(pResult
->sqlStatement
);
1213 SQLFreeHandle(SQL_HANDLE_STMT
, pResult
->sqlStatement
);
1214 for(int i
= 0; i
< pResult
->numColumns
; i
++)
1216 safe_free(pResult
->columnNames
[i
]);
1217 safe_free(pResult
->values
[i
]);
1219 free(pResult
->columnNames
);
1220 free(pResult
->values
);
1221 MutexUnlock(pResult
->pConn
->mutexQuery
);
1228 extern "C" DWORD EXPORT
DrvBegin(ODBCDRV_CONN
*pConn
)
1234 return DBERR_INVALID_HANDLE
;
1236 MutexLock(pConn
->mutexQuery
);
1237 nRet
= SQLSetConnectAttr(pConn
->sqlConn
, SQL_ATTR_AUTOCOMMIT
, (SQLPOINTER
)SQL_AUTOCOMMIT_OFF
, 0);
1238 if ((nRet
== SQL_SUCCESS
) || (nRet
== SQL_SUCCESS_WITH_INFO
))
1240 dwResult
= DBERR_SUCCESS
;
1244 dwResult
= GetSQLErrorInfo(SQL_HANDLE_DBC
, pConn
->sqlConn
, NULL
);
1246 MutexUnlock(pConn
->mutexQuery
);
1251 * Commit transaction
1253 extern "C" DWORD EXPORT
DrvCommit(ODBCDRV_CONN
*pConn
)
1258 return DBERR_INVALID_HANDLE
;
1260 MutexLock(pConn
->mutexQuery
);
1261 nRet
= SQLEndTran(SQL_HANDLE_DBC
, pConn
->sqlConn
, SQL_COMMIT
);
1262 SQLSetConnectAttr(pConn
->sqlConn
, SQL_ATTR_AUTOCOMMIT
, (SQLPOINTER
)SQL_AUTOCOMMIT_ON
, 0);
1263 MutexUnlock(pConn
->mutexQuery
);
1264 return ((nRet
== SQL_SUCCESS
) || (nRet
== SQL_SUCCESS_WITH_INFO
)) ? DBERR_SUCCESS
: DBERR_OTHER_ERROR
;
1268 * Rollback transaction
1270 extern "C" DWORD EXPORT
DrvRollback(ODBCDRV_CONN
*pConn
)
1275 return DBERR_INVALID_HANDLE
;
1277 MutexLock(pConn
->mutexQuery
);
1278 nRet
= SQLEndTran(SQL_HANDLE_DBC
, pConn
->sqlConn
, SQL_ROLLBACK
);
1279 SQLSetConnectAttr(pConn
->sqlConn
, SQL_ATTR_AUTOCOMMIT
, (SQLPOINTER
)SQL_AUTOCOMMIT_ON
, 0);
1280 MutexUnlock(pConn
->mutexQuery
);
1281 return ((nRet
== SQL_SUCCESS
) || (nRet
== SQL_SUCCESS_WITH_INFO
)) ? DBERR_SUCCESS
: DBERR_OTHER_ERROR
;
1285 * Check if table exist
1287 extern "C" int EXPORT
DrvIsTableExist(ODBCDRV_CONN
*pConn
, const NETXMS_WCHAR
*name
)
1289 int rc
= DBIsTableExist_Failure
;
1291 MutexLock(pConn
->mutexQuery
);
1293 SQLHSTMT sqlStatement
;
1294 SQLRETURN iResult
= SQLAllocHandle(SQL_HANDLE_STMT
, pConn
->sqlConn
, &sqlStatement
);
1295 if ((iResult
== SQL_SUCCESS
) || (iResult
== SQL_SUCCESS_WITH_INFO
))
1299 #if defined(_WIN32) || defined(UNICODE_UCS2)
1300 iResult
= SQLTablesW(sqlStatement
, NULL
, 0, NULL
, 0, (SQLWCHAR
*)name
, SQL_NTS
, NULL
, 0);
1302 SQLWCHAR
*temp
= UCS2StringFromUCS4String(name
);
1303 iResult
= SQLTablesW(sqlStatement
, NULL
, 0, NULL
, 0, (SQLWCHAR
*)temp
, SQL_NTS
, NULL
, 0);
1309 char *temp
= MBStringFromWideString(name
);
1310 iResult
= SQLTablesA(sqlStatement
, NULL
, 0, NULL
, 0, (SQLCHAR
*)temp
, SQL_NTS
, NULL
, 0);
1313 if ((iResult
== SQL_SUCCESS
) || (iResult
== SQL_SUCCESS_WITH_INFO
))
1315 ODBCDRV_QUERY_RESULT
*pResult
= ProcessSelectResults(sqlStatement
);
1316 rc
= (DrvGetNumRows(pResult
) > 0) ? DBIsTableExist_Found
: DBIsTableExist_NotFound
;
1317 DrvFreeResult(pResult
);
1319 SQLFreeHandle(SQL_HANDLE_STMT
, sqlStatement
);
1322 MutexUnlock(pConn
->mutexQuery
);
1331 bool WINAPI
DllMain(HINSTANCE hInstance
, DWORD dwReason
, LPVOID lpReserved
)
1333 if (dwReason
== DLL_PROCESS_ATTACH
)
1334 DisableThreadLibraryCalls(hInstance
);