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
};
342 static SQLSMALLINT odbcCTypeA
[] = { SQL_C_CHAR
, SQL_C_SLONG
, SQL_C_ULONG
, SQL_C_SBIGINT
, SQL_C_UBIGINT
, SQL_C_DOUBLE
};
343 static DWORD bufferSize
[] = { 0, sizeof(LONG
), sizeof(DWORD
), sizeof(INT64
), sizeof(QWORD
), sizeof(double) };
345 int length
= (int)wcslen((NETXMS_WCHAR
*)buffer
) + 1;
347 SQLPOINTER sqlBuffer
;
353 #if defined(_WIN32) || defined(UNICODE_UCS2)
356 if (cType
== DB_CTYPE_STRING
)
358 sqlBuffer
= UCS2StringFromUCS4String((NETXMS_WCHAR
*)buffer
);
359 stmt
->buffers
->add(sqlBuffer
);
369 if (cType
== DB_CTYPE_STRING
)
371 sqlBuffer
= MBStringFromWideString((NETXMS_WCHAR
*)buffer
);
372 stmt
->buffers
->add(sqlBuffer
);
380 case DB_BIND_DYNAMIC
:
383 #if defined(_WIN32) || defined(UNICODE_UCS2)
386 if (cType
== DB_CTYPE_STRING
)
388 sqlBuffer
= UCS2StringFromUCS4String((NETXMS_WCHAR
*)buffer
);
399 if (cType
== DB_CTYPE_STRING
)
401 sqlBuffer
= MBStringFromWideString((NETXMS_WCHAR
*)buffer
);
409 stmt
->buffers
->add(sqlBuffer
);
411 case DB_BIND_TRANSIENT
:
414 #if defined(_WIN32) || defined(UNICODE_UCS2)
415 sqlBuffer
= nx_memdup(buffer
, (cType
== DB_CTYPE_STRING
) ? (DWORD
)(length
* sizeof(WCHAR
)) : bufferSize
[cType
]);
417 if (cType
== DB_CTYPE_STRING
)
419 sqlBuffer
= UCS2StringFromUCS4String((NETXMS_WCHAR
*)buffer
);
423 sqlBuffer
= nx_memdup(buffer
, bufferSize
[cType
]);
429 if (cType
== DB_CTYPE_STRING
)
431 sqlBuffer
= MBStringFromWideString((NETXMS_WCHAR
*)buffer
);
435 sqlBuffer
= nx_memdup(buffer
, bufferSize
[cType
]);
438 stmt
->buffers
->add(sqlBuffer
);
441 return; // Invalid call
443 SQLBindParameter(stmt
->handle
, pos
, SQL_PARAM_INPUT
, m_useUnicode
? odbcCTypeW
[cType
] : odbcCTypeA
[cType
], odbcSqlType
[sqlType
],
444 (cType
== DB_CTYPE_STRING
) ? length
: 0, 0, sqlBuffer
, 0, NULL
);
448 * Execute prepared statement
450 extern "C" DWORD EXPORT
DrvExecute(ODBCDRV_CONN
*pConn
, ODBCDRV_STATEMENT
*stmt
, NETXMS_WCHAR
*errorText
)
454 MutexLock(pConn
->mutexQuery
);
455 long rc
= SQLExecute(stmt
->handle
);
456 if ((rc
== SQL_SUCCESS
) ||
457 (rc
== SQL_SUCCESS_WITH_INFO
) ||
460 ClearPendingResults(stmt
->handle
);
461 dwResult
= DBERR_SUCCESS
;
465 dwResult
= GetSQLErrorInfo(SQL_HANDLE_STMT
, stmt
->handle
, errorText
);
467 MutexUnlock(pConn
->mutexQuery
);
472 * Destroy prepared statement
474 extern "C" void EXPORT
DrvFreeStatement(ODBCDRV_STATEMENT
*stmt
)
479 MutexLock(stmt
->connection
->mutexQuery
);
480 SQLFreeHandle(SQL_HANDLE_STMT
, stmt
->handle
);
481 MutexUnlock(stmt
->connection
->mutexQuery
);
482 delete stmt
->buffers
;
487 * Perform non-SELECT query
489 extern "C" DWORD EXPORT
DrvQuery(ODBCDRV_CONN
*pConn
, NETXMS_WCHAR
*pwszQuery
, NETXMS_WCHAR
*errorText
)
493 MutexLock(pConn
->mutexQuery
);
495 // Allocate statement handle
496 SQLHSTMT sqlStatement
;
497 SQLRETURN iResult
= SQLAllocHandle(SQL_HANDLE_STMT
, pConn
->sqlConn
, &sqlStatement
);
498 if ((iResult
== SQL_SUCCESS
) || (iResult
== SQL_SUCCESS_WITH_INFO
))
503 #if defined(_WIN32) || defined(UNICODE_UCS2)
504 iResult
= SQLExecDirectW(sqlStatement
, (SQLWCHAR
*)pwszQuery
, SQL_NTS
);
506 SQLWCHAR
*temp
= UCS2StringFromUCS4String(pwszQuery
);
507 iResult
= SQLExecDirectW(sqlStatement
, temp
, SQL_NTS
);
513 char *temp
= MBStringFromWideString(pwszQuery
);
514 iResult
= SQLExecDirectA(sqlStatement
, (SQLCHAR
*)temp
, SQL_NTS
);
517 if ((iResult
== SQL_SUCCESS
) ||
518 (iResult
== SQL_SUCCESS_WITH_INFO
) ||
519 (iResult
== SQL_NO_DATA
))
521 dwResult
= DBERR_SUCCESS
;
525 dwResult
= GetSQLErrorInfo(SQL_HANDLE_STMT
, sqlStatement
, errorText
);
527 SQLFreeHandle(SQL_HANDLE_STMT
, sqlStatement
);
531 dwResult
= GetSQLErrorInfo(SQL_HANDLE_DBC
, pConn
->sqlConn
, errorText
);
534 MutexUnlock(pConn
->mutexQuery
);
539 * Get complete field data
541 static NETXMS_WCHAR
*GetFieldData(SQLHSTMT sqlStatement
, short column
)
543 NETXMS_WCHAR
*result
= NULL
;
547 #if defined(_WIN32) || defined(UNICODE_UCS2)
549 SQLRETURN rc
= SQLGetData(sqlStatement
, column
, SQL_C_WCHAR
, buffer
, sizeof(buffer
), &dataSize
);
550 if (((rc
== SQL_SUCCESS
) || ((rc
== SQL_SUCCESS_WITH_INFO
) && (dataSize
>= 0) && (dataSize
<= (SQLLEN
)(sizeof(buffer
) - sizeof(WCHAR
))))) && (dataSize
!= SQL_NULL_DATA
))
552 result
= wcsdup(buffer
);
554 else if ((rc
== SQL_SUCCESS_WITH_INFO
) && (dataSize
!= SQL_NULL_DATA
))
556 if (dataSize
> (SQLLEN
)(sizeof(buffer
) - sizeof(WCHAR
)))
558 WCHAR
*temp
= (WCHAR
*)malloc(dataSize
+ sizeof(WCHAR
));
559 memcpy(temp
, buffer
, sizeof(buffer
));
560 rc
= SQLGetData(sqlStatement
, column
, SQL_C_WCHAR
, &temp
[255], dataSize
- 254 * sizeof(WCHAR
), &dataSize
);
561 if ((rc
== SQL_SUCCESS
) || (rc
== SQL_SUCCESS_WITH_INFO
))
570 else if (dataSize
== SQL_NO_TOTAL
)
572 size_t tempSize
= sizeof(buffer
) * 4;
573 WCHAR
*temp
= (WCHAR
*)malloc(tempSize
);
574 memcpy(temp
, buffer
, sizeof(buffer
));
575 size_t offset
= sizeof(buffer
) - sizeof(WCHAR
);
578 SQLLEN readSize
= tempSize
- offset
;
579 rc
= SQLGetData(sqlStatement
, column
, SQL_C_WCHAR
, (char *)temp
+ offset
, readSize
, &dataSize
);
580 if ((rc
== SQL_SUCCESS
) || (rc
== SQL_NO_DATA
))
582 if (dataSize
== SQL_NO_TOTAL
)
584 tempSize
+= sizeof(buffer
) * 4;
588 tempSize
+= dataSize
- readSize
;
590 temp
= (WCHAR
*)realloc(temp
, tempSize
);
591 offset
+= readSize
- sizeof(WCHAR
);
597 UCS2CHAR buffer
[256];
598 SQLRETURN rc
= SQLGetData(sqlStatement
, column
, SQL_C_WCHAR
, buffer
, sizeof(buffer
), &dataSize
);
599 if (((rc
== SQL_SUCCESS
) || ((rc
== SQL_SUCCESS_WITH_INFO
) && (dataSize
>= 0) && (dataSize
<= (SQLLEN
)(sizeof(buffer
) - sizeof(UCS2CHAR
))))) && (dataSize
!= SQL_NULL_DATA
))
601 int len
= ucs2_strlen(buffer
);
602 result
= (NETXMS_WCHAR
*)malloc((len
+ 1) * sizeof(NETXMS_WCHAR
));
603 ucs2_to_ucs4(buffer
, -1, result
, len
+ 1);
605 else if ((rc
== SQL_SUCCESS_WITH_INFO
) && (dataSize
!= SQL_NULL_DATA
))
607 if (dataSize
> (SQLLEN
)(sizeof(buffer
) - sizeof(UCS2CHAR
)))
609 UCS2CHAR
*temp
= (UCS2CHAR
*)malloc(dataSize
+ sizeof(UCS2CHAR
));
610 memcpy(temp
, buffer
, sizeof(buffer
));
611 rc
= SQLGetData(sqlStatement
, column
, SQL_C_WCHAR
, &temp
[255], dataSize
- 254 * sizeof(UCS2CHAR
), &dataSize
);
612 if ((rc
== SQL_SUCCESS
) || (rc
== SQL_SUCCESS_WITH_INFO
))
614 int len
= ucs2_strlen(temp
);
615 result
= (NETXMS_WCHAR
*)malloc((len
+ 1) * sizeof(NETXMS_WCHAR
));
616 ucs2_to_ucs4(temp
, -1, result
, len
+ 1);
620 else if (dataSize
== SQL_NO_TOTAL
)
622 size_t tempSize
= sizeof(buffer
) * 4;
623 UCS2CHAR
*temp
= (UCS2CHAR
*)malloc(tempSize
);
624 memcpy(temp
, buffer
, sizeof(buffer
));
625 size_t offset
= sizeof(buffer
) - sizeof(UCS2CHAR
);
628 SQLLEN readSize
= tempSize
- offset
;
629 rc
= SQLGetData(sqlStatement
, column
, SQL_C_WCHAR
, (char *)temp
+ offset
, readSize
, &dataSize
);
630 if ((rc
== SQL_SUCCESS
) || (rc
== SQL_NO_DATA
))
632 if (dataSize
== SQL_NO_TOTAL
)
634 tempSize
+= sizeof(buffer
) * 4;
638 tempSize
+= dataSize
- readSize
;
640 temp
= (UCS2CHAR
*)realloc(temp
, tempSize
);
641 offset
+= readSize
- sizeof(UCS2CHAR
);
643 int len
= ucs2_strlen(temp
);
644 result
= (NETXMS_WCHAR
*)malloc((len
+ 1) * sizeof(NETXMS_WCHAR
));
645 ucs2_to_ucs4(temp
, -1, result
, len
+ 1);
654 SQLRETURN rc
= SQLGetData(sqlStatement
, column
, SQL_C_CHAR
, buffer
, sizeof(buffer
), &dataSize
);
655 if (((rc
== SQL_SUCCESS
) || ((rc
== SQL_SUCCESS_WITH_INFO
) && (dataSize
>= 0) && (dataSize
<= (SQLLEN
)(sizeof(buffer
) - 1)))) && (dataSize
!= SQL_NULL_DATA
))
657 result
= WideStringFromMBString(buffer
);
659 else if ((rc
== SQL_SUCCESS_WITH_INFO
) && (dataSize
!= SQL_NULL_DATA
))
661 if (dataSize
> (SQLLEN
)(sizeof(buffer
) - 1))
663 char *temp
= (char *)malloc(dataSize
+ 1);
664 memcpy(temp
, buffer
, sizeof(buffer
));
665 rc
= SQLGetData(sqlStatement
, column
, SQL_C_CHAR
, &temp
[255], dataSize
- 254, &dataSize
);
666 if ((rc
== SQL_SUCCESS
) || (rc
== SQL_SUCCESS_WITH_INFO
))
668 result
= WideStringFromMBString(temp
);
672 else if (dataSize
== SQL_NO_TOTAL
)
674 size_t tempSize
= sizeof(buffer
) * 4;
675 char *temp
= (char *)malloc(tempSize
);
676 memcpy(temp
, buffer
, sizeof(buffer
));
677 size_t offset
= sizeof(buffer
) - 1;
680 SQLLEN readSize
= tempSize
- offset
;
681 rc
= SQLGetData(sqlStatement
, column
, SQL_C_CHAR
, &temp
[offset
], readSize
, &dataSize
);
682 if ((rc
== SQL_SUCCESS
) || (rc
== SQL_NO_DATA
))
684 if (dataSize
== SQL_NO_TOTAL
)
686 tempSize
+= sizeof(buffer
) * 4;
690 tempSize
+= dataSize
- readSize
;
692 temp
= (char *)realloc(temp
, tempSize
);
693 offset
+= readSize
- 1;
695 result
= WideStringFromMBString(temp
);
700 return (result
!= NULL
) ? result
: wcsdup(L
"");
704 * Process results of SELECT query
706 static ODBCDRV_QUERY_RESULT
*ProcessSelectResults(SQLHSTMT stmt
)
708 // Allocate result buffer and determine column info
709 ODBCDRV_QUERY_RESULT
*pResult
= (ODBCDRV_QUERY_RESULT
*)malloc(sizeof(ODBCDRV_QUERY_RESULT
));
711 SQLNumResultCols(stmt
, &wNumCols
);
712 pResult
->numColumns
= wNumCols
;
713 pResult
->numRows
= 0;
714 pResult
->pValues
= NULL
;
717 pResult
->columnNames
= (char **)malloc(sizeof(char *) * pResult
->numColumns
);
718 for(int i
= 0; i
< (int)pResult
->numColumns
; i
++)
723 SQLRETURN iResult
= SQLColAttributeA(stmt
, (SQLSMALLINT
)(i
+ 1), SQL_DESC_NAME
, name
, 256, &len
, NULL
);
724 if ((iResult
== SQL_SUCCESS
) ||
725 (iResult
== SQL_SUCCESS_WITH_INFO
))
728 pResult
->columnNames
[i
] = strdup(name
);
732 pResult
->columnNames
[i
] = strdup("");
739 while(iResult
= SQLFetch(stmt
),
740 (iResult
== SQL_SUCCESS
) || (iResult
== SQL_SUCCESS_WITH_INFO
))
743 pResult
->pValues
= (NETXMS_WCHAR
**)realloc(pResult
->pValues
,
744 sizeof(NETXMS_WCHAR
*) * (pResult
->numRows
* pResult
->numColumns
));
745 for(int i
= 1; i
<= (int)pResult
->numColumns
; i
++)
747 pResult
->pValues
[iCurrValue
++] = GetFieldData(stmt
, (short)i
);
755 * Perform SELECT query
757 extern "C" DBDRV_RESULT EXPORT
DrvSelect(ODBCDRV_CONN
*pConn
, NETXMS_WCHAR
*pwszQuery
, DWORD
*pdwError
, NETXMS_WCHAR
*errorText
)
759 ODBCDRV_QUERY_RESULT
*pResult
= NULL
;
761 MutexLock(pConn
->mutexQuery
);
763 // Allocate statement handle
764 SQLHSTMT sqlStatement
;
765 SQLRETURN iResult
= SQLAllocHandle(SQL_HANDLE_STMT
, pConn
->sqlConn
, &sqlStatement
);
766 if ((iResult
== SQL_SUCCESS
) || (iResult
== SQL_SUCCESS_WITH_INFO
))
771 #if defined(_WIN32) || defined(UNICODE_UCS2)
772 iResult
= SQLExecDirectW(sqlStatement
, (SQLWCHAR
*)pwszQuery
, SQL_NTS
);
774 SQLWCHAR
*temp
= UCS2StringFromUCS4String(pwszQuery
);
775 iResult
= SQLExecDirectW(sqlStatement
, temp
, SQL_NTS
);
781 char *temp
= MBStringFromWideString(pwszQuery
);
782 iResult
= SQLExecDirectA(sqlStatement
, (SQLCHAR
*)temp
, SQL_NTS
);
785 if ((iResult
== SQL_SUCCESS
) ||
786 (iResult
== SQL_SUCCESS_WITH_INFO
))
788 pResult
= ProcessSelectResults(sqlStatement
);
789 *pdwError
= DBERR_SUCCESS
;
793 *pdwError
= GetSQLErrorInfo(SQL_HANDLE_STMT
, sqlStatement
, errorText
);
795 SQLFreeHandle(SQL_HANDLE_STMT
, sqlStatement
);
799 *pdwError
= GetSQLErrorInfo(SQL_HANDLE_DBC
, pConn
->sqlConn
, errorText
);
802 MutexUnlock(pConn
->mutexQuery
);
807 * Perform SELECT query using prepared statement
809 extern "C" DBDRV_RESULT EXPORT
DrvSelectPrepared(ODBCDRV_CONN
*pConn
, ODBCDRV_STATEMENT
*stmt
, DWORD
*pdwError
, NETXMS_WCHAR
*errorText
)
811 ODBCDRV_QUERY_RESULT
*pResult
= NULL
;
813 MutexLock(pConn
->mutexQuery
);
814 long rc
= SQLExecute(stmt
->handle
);
815 if ((rc
== SQL_SUCCESS
) ||
816 (rc
== SQL_SUCCESS_WITH_INFO
))
818 pResult
= ProcessSelectResults(stmt
->handle
);
819 *pdwError
= DBERR_SUCCESS
;
823 *pdwError
= GetSQLErrorInfo(SQL_HANDLE_STMT
, stmt
->handle
, errorText
);
825 MutexUnlock(pConn
->mutexQuery
);
830 * Get field length from result
832 extern "C" LONG EXPORT
DrvGetFieldLength(ODBCDRV_QUERY_RESULT
*pResult
, int iRow
, int iColumn
)
838 if ((iRow
< pResult
->numRows
) && (iRow
>= 0) &&
839 (iColumn
< pResult
->numColumns
) && (iColumn
>= 0))
840 nLen
= (LONG
)wcslen(pResult
->pValues
[iRow
* pResult
->numColumns
+ iColumn
]);
846 * Get field value from result
848 extern "C" NETXMS_WCHAR EXPORT
*DrvGetField(ODBCDRV_QUERY_RESULT
*pResult
, int iRow
, int iColumn
,
849 NETXMS_WCHAR
*pBuffer
, int nBufSize
)
851 NETXMS_WCHAR
*pValue
= NULL
;
855 if ((iRow
< pResult
->numRows
) && (iRow
>= 0) &&
856 (iColumn
< pResult
->numColumns
) && (iColumn
>= 0))
859 wcsncpy_s(pBuffer
, nBufSize
, pResult
->pValues
[iRow
* pResult
->numColumns
+ iColumn
], _TRUNCATE
);
861 wcsncpy(pBuffer
, pResult
->pValues
[iRow
* pResult
->numColumns
+ iColumn
], nBufSize
);
862 pBuffer
[nBufSize
- 1] = 0;
871 * Get number of rows in result
873 extern "C" int EXPORT
DrvGetNumRows(ODBCDRV_QUERY_RESULT
*pResult
)
875 return (pResult
!= NULL
) ? pResult
->numRows
: 0;
879 * Get column count in query result
881 extern "C" int EXPORT
DrvGetColumnCount(ODBCDRV_QUERY_RESULT
*pResult
)
883 return (pResult
!= NULL
) ? pResult
->numColumns
: 0;
887 * Get column name in query result
889 extern "C" const char EXPORT
*DrvGetColumnName(ODBCDRV_QUERY_RESULT
*pResult
, int column
)
891 return ((pResult
!= NULL
) && (column
>= 0) && (column
< pResult
->numColumns
)) ? pResult
->columnNames
[column
] : NULL
;
895 * Free SELECT results
897 extern "C" void EXPORT
DrvFreeResult(ODBCDRV_QUERY_RESULT
*pResult
)
903 iNumValues
= pResult
->numColumns
* pResult
->numRows
;
904 for(i
= 0; i
< iNumValues
; i
++)
905 safe_free(pResult
->pValues
[i
]);
906 safe_free(pResult
->pValues
);
908 for(i
= 0; i
< pResult
->numColumns
; i
++)
909 safe_free(pResult
->columnNames
[i
]);
910 safe_free(pResult
->columnNames
);
917 * Perform unbuffered SELECT query
919 extern "C" DBDRV_UNBUFFERED_RESULT EXPORT
DrvSelectUnbuffered(ODBCDRV_CONN
*pConn
, NETXMS_WCHAR
*pwszQuery
, DWORD
*pdwError
, NETXMS_WCHAR
*errorText
)
921 ODBCDRV_UNBUFFERED_QUERY_RESULT
*pResult
= NULL
;
926 MutexLock(pConn
->mutexQuery
);
928 // Allocate statement handle
929 SQLHSTMT sqlStatement
;
930 iResult
= SQLAllocHandle(SQL_HANDLE_STMT
, pConn
->sqlConn
, &sqlStatement
);
931 if ((iResult
== SQL_SUCCESS
) || (iResult
== SQL_SUCCESS_WITH_INFO
))
936 #if defined(_WIN32) || defined(UNICODE_UCS2)
937 iResult
= SQLExecDirectW(sqlStatement
, (SQLWCHAR
*)pwszQuery
, SQL_NTS
);
939 SQLWCHAR
*temp
= UCS2StringFromUCS4String(pwszQuery
);
940 iResult
= SQLExecDirectW(sqlStatement
, temp
, SQL_NTS
);
946 char *temp
= MBStringFromWideString(pwszQuery
);
947 iResult
= SQLExecDirectA(sqlStatement
, (SQLCHAR
*)temp
, SQL_NTS
);
950 if ((iResult
== SQL_SUCCESS
) || (iResult
== SQL_SUCCESS_WITH_INFO
))
952 // Allocate result buffer and determine column info
953 pResult
= (ODBCDRV_UNBUFFERED_QUERY_RESULT
*)malloc(sizeof(ODBCDRV_UNBUFFERED_QUERY_RESULT
));
954 pResult
->sqlStatement
= sqlStatement
;
955 pResult
->isPrepared
= false;
957 SQLNumResultCols(sqlStatement
, &wNumCols
);
958 pResult
->numColumns
= wNumCols
;
959 pResult
->pConn
= pConn
;
960 pResult
->noMoreRows
= false;
963 pResult
->columnNames
= (char **)malloc(sizeof(char *) * pResult
->numColumns
);
964 for(i
= 0; i
< pResult
->numColumns
; i
++)
969 iResult
= SQLColAttributeA(sqlStatement
, (SQLSMALLINT
)(i
+ 1),
970 SQL_DESC_NAME
, name
, 256, &len
, NULL
);
971 if ((iResult
== SQL_SUCCESS
) ||
972 (iResult
== SQL_SUCCESS_WITH_INFO
))
975 pResult
->columnNames
[i
] = strdup(name
);
979 pResult
->columnNames
[i
] = strdup("");
983 // Column values cache
984 pResult
->values
= (NETXMS_WCHAR
**)malloc(sizeof(NETXMS_WCHAR
*) * pResult
->numColumns
);
985 memset(pResult
->values
, 0, sizeof(NETXMS_WCHAR
*) * pResult
->numColumns
);
987 *pdwError
= DBERR_SUCCESS
;
991 *pdwError
= GetSQLErrorInfo(SQL_HANDLE_STMT
, sqlStatement
, errorText
);
992 // Free statement handle if query failed
993 SQLFreeHandle(SQL_HANDLE_STMT
, sqlStatement
);
998 *pdwError
= GetSQLErrorInfo(SQL_HANDLE_DBC
, pConn
->sqlConn
, errorText
);
1001 if (pResult
== NULL
) // Unlock mutex if query has failed
1002 MutexUnlock(pConn
->mutexQuery
);
1007 * Perform unbuffered SELECT query using prepared statement
1009 extern "C" DBDRV_UNBUFFERED_RESULT EXPORT
DrvSelectPreparedUnbuffered(ODBCDRV_CONN
*pConn
, ODBCDRV_STATEMENT
*stmt
, DWORD
*pdwError
, NETXMS_WCHAR
*errorText
)
1011 ODBCDRV_UNBUFFERED_QUERY_RESULT
*pResult
= NULL
;
1013 MutexLock(pConn
->mutexQuery
);
1014 SQLRETURN rc
= SQLExecute(stmt
->handle
);
1015 if ((rc
== SQL_SUCCESS
) || (rc
== SQL_SUCCESS_WITH_INFO
))
1017 // Allocate result buffer and determine column info
1018 pResult
= (ODBCDRV_UNBUFFERED_QUERY_RESULT
*)malloc(sizeof(ODBCDRV_UNBUFFERED_QUERY_RESULT
));
1019 pResult
->sqlStatement
= stmt
->handle
;
1020 pResult
->isPrepared
= true;
1023 SQLNumResultCols(pResult
->sqlStatement
, &wNumCols
);
1024 pResult
->numColumns
= wNumCols
;
1025 pResult
->pConn
= pConn
;
1026 pResult
->noMoreRows
= false;
1027 pResult
->values
= (NETXMS_WCHAR
**)malloc(sizeof(NETXMS_WCHAR
*) * pResult
->numColumns
);
1028 memset(pResult
->values
, 0, sizeof(NETXMS_WCHAR
*) * pResult
->numColumns
);
1031 pResult
->columnNames
= (char **)malloc(sizeof(char *) * pResult
->numColumns
);
1032 for(int i
= 0; i
< pResult
->numColumns
; i
++)
1037 rc
= SQLColAttributeA(pResult
->sqlStatement
, (SQLSMALLINT
)(i
+ 1), SQL_DESC_NAME
, name
, 256, &len
, NULL
);
1038 if ((rc
== SQL_SUCCESS
) || (rc
== SQL_SUCCESS_WITH_INFO
))
1041 pResult
->columnNames
[i
] = strdup(name
);
1045 pResult
->columnNames
[i
] = strdup("");
1048 *pdwError
= DBERR_SUCCESS
;
1052 *pdwError
= GetSQLErrorInfo(SQL_HANDLE_STMT
, stmt
->handle
, errorText
);
1055 if (pResult
== NULL
) // Unlock mutex if query has failed
1056 MutexUnlock(pConn
->mutexQuery
);
1061 * Fetch next result line from asynchronous SELECT results
1063 extern "C" bool EXPORT
DrvFetch(ODBCDRV_UNBUFFERED_QUERY_RESULT
*pResult
)
1065 bool success
= false;
1067 if (pResult
!= NULL
)
1069 SQLRETURN rc
= SQLFetch(pResult
->sqlStatement
);
1070 success
= ((rc
== SQL_SUCCESS
) || (rc
== SQL_SUCCESS_WITH_INFO
));
1073 for(int i
= 0; i
< pResult
->numColumns
; i
++)
1075 free(pResult
->values
[i
]);
1076 pResult
->values
[i
] = GetFieldData(pResult
->sqlStatement
, (short)i
+ 1);
1081 pResult
->noMoreRows
= true;
1088 * Get field length from async query result
1090 extern "C" LONG EXPORT
DrvGetFieldLengthUnbuffered(ODBCDRV_UNBUFFERED_QUERY_RESULT
*result
, int col
)
1095 if ((col
>= result
->numColumns
) || (col
< 0))
1098 return (result
->values
[col
] != NULL
) ? (LONG
)wcslen(result
->values
[col
]) : -1;
1102 * Get field from current row in async query result
1104 extern "C" NETXMS_WCHAR EXPORT
*DrvGetFieldUnbuffered(ODBCDRV_UNBUFFERED_QUERY_RESULT
*result
, int col
, NETXMS_WCHAR
*buffer
, int bufferSize
)
1106 // Check if we have valid result handle
1110 // Check if there are valid fetched row
1111 if (result
->noMoreRows
)
1114 if ((col
>= 0) && (col
< result
->numColumns
) && (result
->values
[col
] != NULL
))
1116 wcsncpy(buffer
, result
->values
[col
], bufferSize
- 1);
1117 buffer
[bufferSize
- 1] = 0;
1127 * Get column count in async query result
1129 extern "C" int EXPORT
DrvGetColumnCountUnbuffered(ODBCDRV_UNBUFFERED_QUERY_RESULT
*pResult
)
1131 return (pResult
!= NULL
) ? pResult
->numColumns
: 0;
1135 * Get column name in async query result
1137 extern "C" const char EXPORT
*DrvGetColumnNameUnbuffered(ODBCDRV_UNBUFFERED_QUERY_RESULT
*pResult
, int column
)
1139 return ((pResult
!= NULL
) && (column
>= 0) && (column
< pResult
->numColumns
)) ? pResult
->columnNames
[column
] : NULL
;
1143 * Destroy result of async query
1145 extern "C" void EXPORT
DrvFreeUnbufferedResult(ODBCDRV_UNBUFFERED_QUERY_RESULT
*pResult
)
1147 if (pResult
== NULL
)
1150 if (pResult
->isPrepared
)
1151 SQLCloseCursor(pResult
->sqlStatement
);
1153 SQLFreeHandle(SQL_HANDLE_STMT
, pResult
->sqlStatement
);
1154 for(int i
= 0; i
< pResult
->numColumns
; i
++)
1156 safe_free(pResult
->columnNames
[i
]);
1157 safe_free(pResult
->values
[i
]);
1159 free(pResult
->columnNames
);
1160 free(pResult
->values
);
1161 MutexUnlock(pResult
->pConn
->mutexQuery
);
1168 extern "C" DWORD EXPORT
DrvBegin(ODBCDRV_CONN
*pConn
)
1174 return DBERR_INVALID_HANDLE
;
1176 MutexLock(pConn
->mutexQuery
);
1177 nRet
= SQLSetConnectAttr(pConn
->sqlConn
, SQL_ATTR_AUTOCOMMIT
, (SQLPOINTER
)SQL_AUTOCOMMIT_OFF
, 0);
1178 if ((nRet
== SQL_SUCCESS
) || (nRet
== SQL_SUCCESS_WITH_INFO
))
1180 dwResult
= DBERR_SUCCESS
;
1184 dwResult
= GetSQLErrorInfo(SQL_HANDLE_DBC
, pConn
->sqlConn
, NULL
);
1186 MutexUnlock(pConn
->mutexQuery
);
1191 * Commit transaction
1193 extern "C" DWORD EXPORT
DrvCommit(ODBCDRV_CONN
*pConn
)
1198 return DBERR_INVALID_HANDLE
;
1200 MutexLock(pConn
->mutexQuery
);
1201 nRet
= SQLEndTran(SQL_HANDLE_DBC
, pConn
->sqlConn
, SQL_COMMIT
);
1202 SQLSetConnectAttr(pConn
->sqlConn
, SQL_ATTR_AUTOCOMMIT
, (SQLPOINTER
)SQL_AUTOCOMMIT_ON
, 0);
1203 MutexUnlock(pConn
->mutexQuery
);
1204 return ((nRet
== SQL_SUCCESS
) || (nRet
== SQL_SUCCESS_WITH_INFO
)) ? DBERR_SUCCESS
: DBERR_OTHER_ERROR
;
1208 * Rollback transaction
1210 extern "C" DWORD EXPORT
DrvRollback(ODBCDRV_CONN
*pConn
)
1215 return DBERR_INVALID_HANDLE
;
1217 MutexLock(pConn
->mutexQuery
);
1218 nRet
= SQLEndTran(SQL_HANDLE_DBC
, pConn
->sqlConn
, SQL_ROLLBACK
);
1219 SQLSetConnectAttr(pConn
->sqlConn
, SQL_ATTR_AUTOCOMMIT
, (SQLPOINTER
)SQL_AUTOCOMMIT_ON
, 0);
1220 MutexUnlock(pConn
->mutexQuery
);
1221 return ((nRet
== SQL_SUCCESS
) || (nRet
== SQL_SUCCESS_WITH_INFO
)) ? DBERR_SUCCESS
: DBERR_OTHER_ERROR
;
1225 * Check if table exist
1227 extern "C" int EXPORT
DrvIsTableExist(ODBCDRV_CONN
*pConn
, const NETXMS_WCHAR
*name
)
1229 int rc
= DBIsTableExist_Failure
;
1231 MutexLock(pConn
->mutexQuery
);
1233 SQLHSTMT sqlStatement
;
1234 SQLRETURN iResult
= SQLAllocHandle(SQL_HANDLE_STMT
, pConn
->sqlConn
, &sqlStatement
);
1235 if ((iResult
== SQL_SUCCESS
) || (iResult
== SQL_SUCCESS_WITH_INFO
))
1239 #if defined(_WIN32) || defined(UNICODE_UCS2)
1240 iResult
= SQLTablesW(sqlStatement
, NULL
, 0, NULL
, 0, (SQLWCHAR
*)name
, SQL_NTS
, NULL
, 0);
1242 SQLWCHAR
*temp
= UCS2StringFromUCS4String(name
);
1243 iResult
= SQLTablesW(sqlStatement
, NULL
, 0, NULL
, 0, (SQLWCHAR
*)temp
, SQL_NTS
, NULL
, 0);
1249 char *temp
= MBStringFromWideString(name
);
1250 iResult
= SQLTablesA(sqlStatement
, NULL
, 0, NULL
, 0, (SQLCHAR
*)temp
, SQL_NTS
, NULL
, 0);
1253 if ((iResult
== SQL_SUCCESS
) || (iResult
== SQL_SUCCESS_WITH_INFO
))
1255 ODBCDRV_QUERY_RESULT
*pResult
= ProcessSelectResults(sqlStatement
);
1256 rc
= (DrvGetNumRows(pResult
) > 0) ? DBIsTableExist_Found
: DBIsTableExist_NotFound
;
1257 DrvFreeResult(pResult
);
1259 SQLFreeHandle(SQL_HANDLE_STMT
, sqlStatement
);
1262 MutexUnlock(pConn
->mutexQuery
);
1271 bool WINAPI
DllMain(HINSTANCE hInstance
, DWORD dwReason
, LPVOID lpReserved
)
1273 if (dwReason
== DLL_PROCESS_ATTACH
)
1274 DisableThreadLibraryCalls(hInstance
);