2 ** MS SQL 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 #define EXPORT __declspec(dllexport)
28 DECLARE_DRIVER_HEADER("MSSQL")
31 * Selected ODBC driver
33 static TCHAR s_driver
[SQL_MAX_DSN_LENGTH
+ 1] = _T("SQL Native Client");
36 * Convert ODBC state to NetXMS database error code and get error text
38 static DWORD
GetSQLErrorInfo(SQLSMALLINT nHandleType
, SQLHANDLE hHandle
, WCHAR
*errorText
)
45 // Get state information and convert it to NetXMS database error code
46 nRet
= SQLGetDiagFieldA(nHandleType
, hHandle
, 1, SQL_DIAG_SQLSTATE
, szState
, 16, &nChars
);
47 if (nRet
== SQL_SUCCESS
)
49 if ((!strcmp(szState
, "08003")) || // Connection does not exist
50 (!strcmp(szState
, "08S01")) || // Communication link failure
51 (!strcmp(szState
, "HYT00")) || // Timeout expired
52 (!strcmp(szState
, "HYT01"))) // Connection timeout expired
54 dwError
= DBERR_CONNECTION_LOST
;
58 dwError
= DBERR_OTHER_ERROR
;
63 dwError
= DBERR_OTHER_ERROR
;
67 if (errorText
!= NULL
)
69 nRet
= SQLGetDiagFieldW(nHandleType
, hHandle
, 1, SQL_DIAG_MESSAGE_TEXT
, errorText
, DBDRV_MAX_ERROR_TEXT
, &nChars
);
70 if (nRet
== SQL_SUCCESS
)
72 RemoveTrailingCRLFW(errorText
);
76 wcscpy(errorText
, L
"Unable to obtain description for this error");
84 * Clear any pending result sets on given statement
86 static void ClearPendingResults(SQLHSTMT stmt
)
90 SQLRETURN rc
= SQLMoreResults(stmt
);
91 if ((rc
!= SQL_SUCCESS
) && (rc
!= SQL_SUCCESS_WITH_INFO
))
97 * Prepare string for using in SQL query - enclose in quotes and escape as needed
99 extern "C" WCHAR EXPORT
*DrvPrepareStringW(const WCHAR
*str
)
101 int len
= (int)wcslen(str
) + 3; // + two quotes and \0 at the end
102 int bufferSize
= len
+ 128;
103 WCHAR
*out
= (WCHAR
*)malloc(bufferSize
* sizeof(WCHAR
));
106 const WCHAR
*src
= str
;
108 for(outPos
= 1; *src
!= NULL
; src
++)
113 if (len
>= bufferSize
)
116 out
= (WCHAR
*)realloc(out
, bufferSize
* sizeof(WCHAR
));
118 out
[outPos
++] = L
'\'';
119 out
[outPos
++] = L
'\'';
123 out
[outPos
++] = *src
;
126 out
[outPos
++] = L
'\'';
132 extern "C" char EXPORT
*DrvPrepareStringA(const char *str
)
134 int len
= (int)strlen(str
) + 3; // + two quotes and \0 at the end
135 int bufferSize
= len
+ 128;
136 char *out
= (char *)malloc(bufferSize
);
139 const char *src
= str
;
141 for(outPos
= 1; *src
!= NULL
; src
++)
146 if (len
>= bufferSize
)
149 out
= (char *)realloc(out
, bufferSize
);
151 out
[outPos
++] = '\'';
152 out
[outPos
++] = '\'';
156 out
[outPos
++] = *src
;
159 out
[outPos
++] = '\'';
168 extern "C" bool EXPORT
DrvInit(const char *cmdLine
)
170 // Allocate environment
172 long rc
= SQLAllocHandle(SQL_HANDLE_ENV
, SQL_NULL_HANDLE
, &sqlEnv
);
173 if ((rc
!= SQL_SUCCESS
) && (rc
!= SQL_SUCCESS_WITH_INFO
))
176 rc
= SQLSetEnvAttr(sqlEnv
, SQL_ATTR_ODBC_VERSION
, (void *)SQL_OV_ODBC3
, 0);
177 if ((rc
!= SQL_SUCCESS
) && (rc
!= SQL_SUCCESS_WITH_INFO
))
180 // Find correct driver
181 // Default is "SQL Native Client", but switch to "SQL Server Native Client 10.0" if found
182 TCHAR name
[SQL_MAX_DSN_LENGTH
+ 1], attrs
[1024];
184 rc
= SQLDrivers(sqlEnv
, SQL_FETCH_FIRST
, (SQLCHAR
*)name
, SQL_MAX_DSN_LENGTH
+ 1, &l1
, (SQLCHAR
*)attrs
, 1024, &l2
);
185 while((rc
== SQL_SUCCESS
) || (rc
== SQL_SUCCESS_WITH_INFO
))
187 if (!_tcscmp(name
, _T("SQL Server Native Client 10.0")) ||
188 !_tcscmp(name
, _T("SQL Server Native Client 11.0")))
190 _tcscpy(s_driver
, name
);
193 rc
= SQLDrivers(sqlEnv
, SQL_FETCH_NEXT
, (SQLCHAR
*)name
, SQL_MAX_DSN_LENGTH
+ 1, &l1
, (SQLCHAR
*)attrs
, 1024, &l2
);
196 SQLFreeHandle(SQL_HANDLE_ENV
, sqlEnv
);
203 extern "C" void EXPORT
DrvUnload()
208 * Connect to database
210 extern "C" DBDRV_CONNECTION EXPORT
DrvConnect(const char *host
, const char *login
, const char *password
,
211 const char *database
, const char *schema
, WCHAR
*errorText
)
216 // Allocate our connection structure
217 pConn
= (MSSQL_CONN
*)malloc(sizeof(MSSQL_CONN
));
219 // Allocate environment
220 iResult
= SQLAllocHandle(SQL_HANDLE_ENV
, SQL_NULL_HANDLE
, &pConn
->sqlEnv
);
221 if ((iResult
!= SQL_SUCCESS
) && (iResult
!= SQL_SUCCESS_WITH_INFO
))
223 wcscpy(errorText
, L
"Cannot allocate environment handle");
224 goto connect_failure_0
;
227 // Set required ODBC version
228 iResult
= SQLSetEnvAttr(pConn
->sqlEnv
, SQL_ATTR_ODBC_VERSION
, (void *)SQL_OV_ODBC3
, 0);
229 if ((iResult
!= SQL_SUCCESS
) && (iResult
!= SQL_SUCCESS_WITH_INFO
))
231 wcscpy(errorText
, L
"Call to SQLSetEnvAttr failed");
232 goto connect_failure_1
;
235 // Allocate connection handle, set timeout
236 iResult
= SQLAllocHandle(SQL_HANDLE_DBC
, pConn
->sqlEnv
, &pConn
->sqlConn
);
237 if ((iResult
!= SQL_SUCCESS
) && (iResult
!= SQL_SUCCESS_WITH_INFO
))
239 wcscpy(errorText
, L
"Cannot allocate connection handle");
240 goto connect_failure_1
;
242 SQLSetConnectAttr(pConn
->sqlConn
, SQL_ATTR_LOGIN_TIMEOUT
, (SQLPOINTER
*)15, 0);
243 SQLSetConnectAttr(pConn
->sqlConn
, SQL_ATTR_CONNECTION_TIMEOUT
, (SQLPOINTER
*)30, 0);
245 // Connect to the server
247 char connectString
[1024];
249 if (!strcmp(login
, "*"))
251 snprintf(connectString
, 1024, "DRIVER={%s};Server=%s;Trusted_Connection=yes;Database=%s;APP=NetXMS",
252 s_driver
, host
, database
);
256 snprintf(connectString
, 1024, "DRIVER={%s};Server=%s;UID=%s;PWD=%s;Database=%s;APP=NetXMS",
257 s_driver
, host
, login
, password
, database
);
259 iResult
= SQLDriverConnect(pConn
->sqlConn
, NULL
, (SQLCHAR
*)connectString
, SQL_NTS
, NULL
, 0, &outLen
, SQL_DRIVER_NOPROMPT
);
261 if ((iResult
!= SQL_SUCCESS
) && (iResult
!= SQL_SUCCESS_WITH_INFO
))
263 GetSQLErrorInfo(SQL_HANDLE_DBC
, pConn
->sqlConn
, errorText
);
264 goto connect_failure_2
;
268 pConn
->mutexQuery
= MutexCreate();
271 return (DBDRV_CONNECTION
)pConn
;
275 SQLFreeHandle(SQL_HANDLE_DBC
, pConn
->sqlConn
);
278 SQLFreeHandle(SQL_HANDLE_ENV
, pConn
->sqlEnv
);
286 * Disconnect from database
288 extern "C" void EXPORT
DrvDisconnect(MSSQL_CONN
*pConn
)
290 MutexLock(pConn
->mutexQuery
);
291 MutexUnlock(pConn
->mutexQuery
);
292 SQLDisconnect(pConn
->sqlConn
);
293 SQLFreeHandle(SQL_HANDLE_DBC
, pConn
->sqlConn
);
294 SQLFreeHandle(SQL_HANDLE_ENV
, pConn
->sqlEnv
);
295 MutexDestroy(pConn
->mutexQuery
);
302 extern "C" DBDRV_STATEMENT EXPORT
DrvPrepare(MSSQL_CONN
*pConn
, WCHAR
*pwszQuery
, DWORD
*pdwError
, WCHAR
*errorText
)
306 MSSQL_STATEMENT
*result
;
308 MutexLock(pConn
->mutexQuery
);
310 // Allocate statement handle
311 iResult
= SQLAllocHandle(SQL_HANDLE_STMT
, pConn
->sqlConn
, &stmt
);
312 if ((iResult
== SQL_SUCCESS
) || (iResult
== SQL_SUCCESS_WITH_INFO
))
315 iResult
= SQLPrepareW(stmt
, (SQLWCHAR
*)pwszQuery
, SQL_NTS
);
316 if ((iResult
== SQL_SUCCESS
) ||
317 (iResult
== SQL_SUCCESS_WITH_INFO
))
319 result
= (MSSQL_STATEMENT
*)malloc(sizeof(MSSQL_STATEMENT
));
320 result
->handle
= stmt
;
321 result
->buffers
= new Array(0, 16, true);
322 result
->connection
= pConn
;
323 *pdwError
= DBERR_SUCCESS
;
327 *pdwError
= GetSQLErrorInfo(SQL_HANDLE_STMT
, stmt
, errorText
);
328 SQLFreeHandle(SQL_HANDLE_STMT
, stmt
);
334 *pdwError
= GetSQLErrorInfo(SQL_HANDLE_DBC
, pConn
->sqlConn
, errorText
);
338 MutexUnlock(pConn
->mutexQuery
);
344 // Bind parameter to statement
347 extern "C" void EXPORT
DrvBind(MSSQL_STATEMENT
*stmt
, int pos
, int sqlType
, int cType
, void *buffer
, int allocType
)
349 static SQLSMALLINT odbcSqlType
[] = { SQL_VARCHAR
, SQL_INTEGER
, SQL_BIGINT
, SQL_DOUBLE
, SQL_LONGVARCHAR
};
350 static SQLSMALLINT odbcCType
[] = { SQL_C_WCHAR
, SQL_C_SLONG
, SQL_C_ULONG
, SQL_C_SBIGINT
, SQL_C_UBIGINT
, SQL_C_DOUBLE
};
351 static DWORD bufferSize
[] = { 0, sizeof(LONG
), sizeof(DWORD
), sizeof(INT64
), sizeof(QWORD
), sizeof(double) };
353 int length
= (cType
== DB_CTYPE_STRING
) ? ((int)wcslen((WCHAR
*)buffer
) + 1) : 0;
355 SQLPOINTER sqlBuffer
;
361 case DB_BIND_DYNAMIC
:
363 stmt
->buffers
->add(sqlBuffer
);
365 case DB_BIND_TRANSIENT
:
366 sqlBuffer
= nx_memdup(buffer
, (cType
== DB_CTYPE_STRING
) ? (DWORD
)(length
* sizeof(WCHAR
)) : bufferSize
[cType
]);
367 stmt
->buffers
->add(sqlBuffer
);
370 return; // Invalid call
372 SQLBindParameter(stmt
->handle
, pos
, SQL_PARAM_INPUT
, odbcCType
[cType
], odbcSqlType
[sqlType
],
373 (cType
== DB_CTYPE_STRING
) ? length
: 0, 0, sqlBuffer
, 0, NULL
);
378 // Execute prepared statement
381 extern "C" DWORD EXPORT
DrvExecute(MSSQL_CONN
*pConn
, MSSQL_STATEMENT
*stmt
, WCHAR
*errorText
)
385 MutexLock(pConn
->mutexQuery
);
386 long rc
= SQLExecute(stmt
->handle
);
387 if ((rc
== SQL_SUCCESS
) ||
388 (rc
== SQL_SUCCESS_WITH_INFO
) ||
391 ClearPendingResults(stmt
->handle
);
392 dwResult
= DBERR_SUCCESS
;
396 dwResult
= GetSQLErrorInfo(SQL_HANDLE_STMT
, stmt
->handle
, errorText
);
398 MutexUnlock(pConn
->mutexQuery
);
404 // Destroy prepared statement
407 extern "C" void EXPORT
DrvFreeStatement(MSSQL_STATEMENT
*stmt
)
412 MutexLock(stmt
->connection
->mutexQuery
);
413 SQLFreeHandle(SQL_HANDLE_STMT
, stmt
->handle
);
414 MutexUnlock(stmt
->connection
->mutexQuery
);
415 delete stmt
->buffers
;
420 * Perform non-SELECT query
422 extern "C" DWORD EXPORT
DrvQuery(MSSQL_CONN
*pConn
, WCHAR
*pwszQuery
, WCHAR
*errorText
)
427 MutexLock(pConn
->mutexQuery
);
429 // Allocate statement handle
430 SQLHSTMT sqlStatement
;
431 iResult
= SQLAllocHandle(SQL_HANDLE_STMT
, pConn
->sqlConn
, &sqlStatement
);
432 if ((iResult
== SQL_SUCCESS
) || (iResult
== SQL_SUCCESS_WITH_INFO
))
435 iResult
= SQLExecDirectW(sqlStatement
, (SQLWCHAR
*)pwszQuery
, SQL_NTS
);
436 if ((iResult
== SQL_SUCCESS
) ||
437 (iResult
== SQL_SUCCESS_WITH_INFO
) ||
438 (iResult
== SQL_NO_DATA
))
440 dwResult
= DBERR_SUCCESS
;
444 dwResult
= GetSQLErrorInfo(SQL_HANDLE_STMT
, sqlStatement
, errorText
);
446 SQLFreeHandle(SQL_HANDLE_STMT
, sqlStatement
);
450 dwResult
= GetSQLErrorInfo(SQL_HANDLE_DBC
, pConn
->sqlConn
, errorText
);
453 MutexUnlock(pConn
->mutexQuery
);
458 * Get complete field data
460 static WCHAR
*GetFieldData(SQLHSTMT sqlStatement
, short column
)
462 WCHAR
*result
= NULL
;
465 SQLRETURN rc
= SQLGetData(sqlStatement
, column
, SQL_C_WCHAR
, buffer
, sizeof(buffer
), &dataSize
);
466 if (((rc
== SQL_SUCCESS
) || ((rc
== SQL_SUCCESS_WITH_INFO
) && (dataSize
>= 0) && (dataSize
<= (SQLLEN
)(sizeof(buffer
) - sizeof(WCHAR
))))) && (dataSize
!= SQL_NULL_DATA
))
468 result
= wcsdup(buffer
);
470 else if ((rc
== SQL_SUCCESS_WITH_INFO
) && (dataSize
!= SQL_NULL_DATA
))
472 if (dataSize
> (SQLLEN
)(sizeof(buffer
) - sizeof(WCHAR
)))
474 WCHAR
*temp
= (WCHAR
*)malloc(dataSize
+ sizeof(WCHAR
));
475 memcpy(temp
, buffer
, sizeof(buffer
));
476 rc
= SQLGetData(sqlStatement
, column
, SQL_C_WCHAR
, &temp
[255], dataSize
- 254 * sizeof(WCHAR
), &dataSize
);
477 if ((rc
== SQL_SUCCESS
) || (rc
== SQL_SUCCESS_WITH_INFO
))
486 else if (dataSize
== SQL_NO_TOTAL
)
488 size_t tempSize
= sizeof(buffer
) * 4; // temporary buffer size in bytes
489 WCHAR
*temp
= (WCHAR
*)malloc(tempSize
);
490 memcpy(temp
, buffer
, sizeof(buffer
));
491 size_t offset
= sizeof(buffer
) - sizeof(WCHAR
); // offset in buffer in bytes
494 SQLLEN readSize
= tempSize
- offset
;
495 rc
= SQLGetData(sqlStatement
, column
, SQL_C_WCHAR
, (char *)temp
+ offset
, readSize
, &dataSize
);
496 if ((rc
== SQL_SUCCESS
) || (rc
== SQL_NO_DATA
))
498 if (dataSize
== SQL_NO_TOTAL
)
500 tempSize
+= sizeof(buffer
) * 4;
504 tempSize
+= dataSize
- readSize
;
506 temp
= (WCHAR
*)realloc(temp
, tempSize
);
507 offset
+= readSize
- sizeof(WCHAR
);
512 return (result
!= NULL
) ? result
: wcsdup(L
"");
516 * Process results of SELECT query
518 static MSSQL_QUERY_RESULT
*ProcessSelectResults(SQLHSTMT stmt
)
520 // Allocate result buffer and determine column info
521 MSSQL_QUERY_RESULT
*pResult
= (MSSQL_QUERY_RESULT
*)malloc(sizeof(MSSQL_QUERY_RESULT
));
523 SQLNumResultCols(stmt
, &wNumCols
);
524 pResult
->numColumns
= wNumCols
;
525 pResult
->numRows
= 0;
526 pResult
->pValues
= NULL
;
529 pResult
->columnNames
= (char **)malloc(sizeof(char *) * pResult
->numColumns
);
530 for(int i
= 0; i
< (int)pResult
->numColumns
; i
++)
535 SQLRETURN iResult
= SQLColAttributeA(stmt
, (SQLSMALLINT
)(i
+ 1), SQL_DESC_NAME
, name
, 256, &len
, NULL
);
536 if ((iResult
== SQL_SUCCESS
) ||
537 (iResult
== SQL_SUCCESS_WITH_INFO
))
540 pResult
->columnNames
[i
] = strdup(name
);
544 pResult
->columnNames
[i
] = strdup("");
551 while(iResult
= SQLFetch(stmt
),
552 (iResult
== SQL_SUCCESS
) || (iResult
== SQL_SUCCESS_WITH_INFO
))
555 pResult
->pValues
= (WCHAR
**)realloc(pResult
->pValues
,
556 sizeof(WCHAR
*) * (pResult
->numRows
* pResult
->numColumns
));
557 for(int i
= 1; i
<= (int)pResult
->numColumns
; i
++)
559 pResult
->pValues
[iCurrValue
++] = GetFieldData(stmt
, (short)i
);
567 * Perform SELECT query
569 extern "C" DBDRV_RESULT EXPORT
DrvSelect(MSSQL_CONN
*pConn
, WCHAR
*pwszQuery
, DWORD
*pdwError
, WCHAR
*errorText
)
571 MSSQL_QUERY_RESULT
*pResult
= NULL
;
573 MutexLock(pConn
->mutexQuery
);
575 // Allocate statement handle
576 SQLHSTMT sqlStatement
;
577 SQLRETURN iResult
= SQLAllocHandle(SQL_HANDLE_STMT
, pConn
->sqlConn
, &sqlStatement
);
578 if ((iResult
== SQL_SUCCESS
) || (iResult
== SQL_SUCCESS_WITH_INFO
))
581 iResult
= SQLExecDirectW(sqlStatement
, (SQLWCHAR
*)pwszQuery
, SQL_NTS
);
582 if ((iResult
== SQL_SUCCESS
) || (iResult
== SQL_SUCCESS_WITH_INFO
))
584 pResult
= ProcessSelectResults(sqlStatement
);
585 *pdwError
= DBERR_SUCCESS
;
589 *pdwError
= GetSQLErrorInfo(SQL_HANDLE_STMT
, sqlStatement
, errorText
);
591 SQLFreeHandle(SQL_HANDLE_STMT
, sqlStatement
);
595 *pdwError
= GetSQLErrorInfo(SQL_HANDLE_DBC
, pConn
->sqlConn
, errorText
);
598 MutexUnlock(pConn
->mutexQuery
);
603 * Perform SELECT query using prepared statement
605 extern "C" DBDRV_RESULT EXPORT
DrvSelectPrepared(MSSQL_CONN
*pConn
, MSSQL_STATEMENT
*stmt
, DWORD
*pdwError
, WCHAR
*errorText
)
607 MSSQL_QUERY_RESULT
*pResult
= NULL
;
609 MutexLock(pConn
->mutexQuery
);
610 long rc
= SQLExecute(stmt
->handle
);
611 if ((rc
== SQL_SUCCESS
) ||
612 (rc
== SQL_SUCCESS_WITH_INFO
))
614 pResult
= ProcessSelectResults(stmt
->handle
);
615 *pdwError
= DBERR_SUCCESS
;
619 *pdwError
= GetSQLErrorInfo(SQL_HANDLE_STMT
, stmt
->handle
, errorText
);
621 MutexUnlock(pConn
->mutexQuery
);
626 * Get field length from result
628 extern "C" LONG EXPORT
DrvGetFieldLength(MSSQL_QUERY_RESULT
*pResult
, int iRow
, int iColumn
)
634 if ((iRow
< pResult
->numRows
) && (iRow
>= 0) &&
635 (iColumn
< pResult
->numColumns
) && (iColumn
>= 0))
636 nLen
= (LONG
)wcslen(pResult
->pValues
[iRow
* pResult
->numColumns
+ iColumn
]);
642 * Get field value from result
644 extern "C" WCHAR EXPORT
*DrvGetField(MSSQL_QUERY_RESULT
*pResult
, int iRow
, int iColumn
, WCHAR
*pBuffer
, int nBufSize
)
646 WCHAR
*pValue
= NULL
;
650 if ((iRow
< pResult
->numRows
) && (iRow
>= 0) &&
651 (iColumn
< pResult
->numColumns
) && (iColumn
>= 0))
653 wcsncpy_s(pBuffer
, nBufSize
, pResult
->pValues
[iRow
* pResult
->numColumns
+ iColumn
], _TRUNCATE
);
661 * Get number of rows in result
663 extern "C" int EXPORT
DrvGetNumRows(MSSQL_QUERY_RESULT
*pResult
)
665 return (pResult
!= NULL
) ? pResult
->numRows
: 0;
669 * Get column count in query result
671 extern "C" int EXPORT
DrvGetColumnCount(MSSQL_QUERY_RESULT
*pResult
)
673 return (pResult
!= NULL
) ? pResult
->numColumns
: 0;
677 * Get column name in query result
679 extern "C" const char EXPORT
*DrvGetColumnName(MSSQL_QUERY_RESULT
*pResult
, int column
)
681 return ((pResult
!= NULL
) && (column
>= 0) && (column
< pResult
->numColumns
)) ? pResult
->columnNames
[column
] : NULL
;
685 * Free SELECT results
687 extern "C" void EXPORT
DrvFreeResult(MSSQL_QUERY_RESULT
*pResult
)
693 iNumValues
= pResult
->numColumns
* pResult
->numRows
;
694 for(i
= 0; i
< iNumValues
; i
++)
695 safe_free(pResult
->pValues
[i
]);
696 safe_free(pResult
->pValues
);
698 for(i
= 0; i
< pResult
->numColumns
; i
++)
699 safe_free(pResult
->columnNames
[i
]);
700 safe_free(pResult
->columnNames
);
707 * Perform unbuffered SELECT query
709 extern "C" DBDRV_UNBUFFERED_RESULT EXPORT
DrvSelectUnbuffered(MSSQL_CONN
*pConn
, WCHAR
*pwszQuery
, DWORD
*pdwError
, WCHAR
*errorText
)
711 MSSQL_UNBUFFERED_QUERY_RESULT
*pResult
= NULL
;
713 MutexLock(pConn
->mutexQuery
);
715 // Allocate statement handle
716 SQLHSTMT sqlStatement
;
717 SQLRETURN iResult
= SQLAllocHandle(SQL_HANDLE_STMT
, pConn
->sqlConn
, &sqlStatement
);
718 if ((iResult
== SQL_SUCCESS
) || (iResult
== SQL_SUCCESS_WITH_INFO
))
721 iResult
= SQLExecDirectW(sqlStatement
, (SQLWCHAR
*)pwszQuery
, SQL_NTS
);
722 if ((iResult
== SQL_SUCCESS
) || (iResult
== SQL_SUCCESS_WITH_INFO
))
724 // Allocate result buffer and determine column info
725 pResult
= (MSSQL_UNBUFFERED_QUERY_RESULT
*)malloc(sizeof(MSSQL_UNBUFFERED_QUERY_RESULT
));
726 pResult
->sqlStatement
= sqlStatement
;
727 pResult
->isPrepared
= false;
730 SQLNumResultCols(sqlStatement
, &wNumCols
);
731 pResult
->numColumns
= wNumCols
;
732 pResult
->pConn
= pConn
;
733 pResult
->noMoreRows
= false;
734 pResult
->data
= (WCHAR
**)malloc(sizeof(WCHAR
*) * pResult
->numColumns
);
735 memset(pResult
->data
, 0, sizeof(WCHAR
*) * pResult
->numColumns
);
738 pResult
->columnNames
= (char **)malloc(sizeof(char *) * pResult
->numColumns
);
739 for(int i
= 0; i
< pResult
->numColumns
; i
++)
744 iResult
= SQLColAttributeA(sqlStatement
, (SQLSMALLINT
)(i
+ 1), SQL_DESC_NAME
, name
, 256, &len
, NULL
);
745 if ((iResult
== SQL_SUCCESS
) || (iResult
== SQL_SUCCESS_WITH_INFO
))
748 pResult
->columnNames
[i
] = strdup(name
);
752 pResult
->columnNames
[i
] = strdup("");
756 *pdwError
= DBERR_SUCCESS
;
760 *pdwError
= GetSQLErrorInfo(SQL_HANDLE_STMT
, sqlStatement
, errorText
);
761 // Free statement handle if query failed
762 SQLFreeHandle(SQL_HANDLE_STMT
, sqlStatement
);
767 *pdwError
= GetSQLErrorInfo(SQL_HANDLE_DBC
, pConn
->sqlConn
, errorText
);
770 if (pResult
== NULL
) // Unlock mutex if query has failed
771 MutexUnlock(pConn
->mutexQuery
);
776 * Perform unbuffered SELECT query using prepared statement
778 extern "C" DBDRV_UNBUFFERED_RESULT EXPORT
DrvSelectPreparedUnbuffered(MSSQL_CONN
*pConn
, MSSQL_STATEMENT
*stmt
, DWORD
*pdwError
, WCHAR
*errorText
)
780 MSSQL_UNBUFFERED_QUERY_RESULT
*pResult
= NULL
;
782 MutexLock(pConn
->mutexQuery
);
783 SQLRETURN rc
= SQLExecute(stmt
->handle
);
784 if ((rc
== SQL_SUCCESS
) || (rc
== SQL_SUCCESS_WITH_INFO
))
786 // Allocate result buffer and determine column info
787 pResult
= (MSSQL_UNBUFFERED_QUERY_RESULT
*)malloc(sizeof(MSSQL_UNBUFFERED_QUERY_RESULT
));
788 pResult
->sqlStatement
= stmt
->handle
;
789 pResult
->isPrepared
= true;
792 SQLNumResultCols(pResult
->sqlStatement
, &wNumCols
);
793 pResult
->numColumns
= wNumCols
;
794 pResult
->pConn
= pConn
;
795 pResult
->noMoreRows
= false;
796 pResult
->data
= (WCHAR
**)malloc(sizeof(WCHAR
*) * pResult
->numColumns
);
797 memset(pResult
->data
, 0, sizeof(WCHAR
*) * pResult
->numColumns
);
800 pResult
->columnNames
= (char **)malloc(sizeof(char *) * pResult
->numColumns
);
801 for(int i
= 0; i
< pResult
->numColumns
; i
++)
806 rc
= SQLColAttributeA(pResult
->sqlStatement
, (SQLSMALLINT
)(i
+ 1), SQL_DESC_NAME
, name
, 256, &len
, NULL
);
807 if ((rc
== SQL_SUCCESS
) || (rc
== SQL_SUCCESS_WITH_INFO
))
810 pResult
->columnNames
[i
] = strdup(name
);
814 pResult
->columnNames
[i
] = strdup("");
817 *pdwError
= DBERR_SUCCESS
;
821 *pdwError
= GetSQLErrorInfo(SQL_HANDLE_STMT
, stmt
->handle
, errorText
);
824 if (pResult
== NULL
) // Unlock mutex if query has failed
825 MutexUnlock(pConn
->mutexQuery
);
830 * Fetch next result line from unbuffered SELECT results
832 extern "C" bool EXPORT
DrvFetch(MSSQL_UNBUFFERED_QUERY_RESULT
*pResult
)
834 bool bResult
= false;
840 iResult
= SQLFetch(pResult
->sqlStatement
);
841 bResult
= ((iResult
== SQL_SUCCESS
) || (iResult
== SQL_SUCCESS_WITH_INFO
));
844 for(int i
= 0; i
< pResult
->numColumns
; i
++)
846 free(pResult
->data
[i
]);
847 pResult
->data
[i
] = GetFieldData(pResult
->sqlStatement
, (short)i
+ 1);
852 pResult
->noMoreRows
= true;
859 * Get field length from unbuffered query result
861 extern "C" LONG EXPORT
DrvGetFieldLengthUnbuffered(MSSQL_UNBUFFERED_QUERY_RESULT
*pResult
, int iColumn
)
866 if ((iColumn
< pResult
->numColumns
) && (iColumn
>= 0))
867 nLen
= (pResult
->data
[iColumn
] != NULL
) ? (LONG
)wcslen(pResult
->data
[iColumn
]) : 0;
873 * Get field from current row in unbuffered query result
875 extern "C" WCHAR EXPORT
*DrvGetFieldUnbuffered(MSSQL_UNBUFFERED_QUERY_RESULT
*pResult
, int iColumn
, WCHAR
*pBuffer
, int iBufSize
)
877 // Check if we have valid result handle
881 // Check if there are valid fetched row
882 if (pResult
->noMoreRows
)
885 if ((iColumn
>= 0) && (iColumn
< pResult
->numColumns
))
887 if (pResult
->data
[iColumn
] != NULL
)
889 wcsncpy(pBuffer
, pResult
->data
[iColumn
], iBufSize
);
890 pBuffer
[iBufSize
- 1] = 0;
905 * Get column count in unbuffered query result
907 extern "C" int EXPORT
DrvGetColumnCountUnbuffered(MSSQL_UNBUFFERED_QUERY_RESULT
*pResult
)
909 return (pResult
!= NULL
) ? pResult
->numColumns
: 0;
913 * Get column name in unbuffered query result
915 extern "C" const char EXPORT
*DrvGetColumnNameUnbuffered(MSSQL_UNBUFFERED_QUERY_RESULT
*pResult
, int column
)
917 return ((pResult
!= NULL
) && (column
>= 0) && (column
< pResult
->numColumns
)) ? pResult
->columnNames
[column
] : NULL
;
921 * Destroy result of unbuffered query
923 extern "C" void EXPORT
DrvFreeUnbufferedResult(MSSQL_UNBUFFERED_QUERY_RESULT
*pResult
)
928 if (pResult
->isPrepared
)
929 SQLCloseCursor(pResult
->sqlStatement
);
931 SQLFreeHandle(SQL_HANDLE_STMT
, pResult
->sqlStatement
);
932 MutexUnlock(pResult
->pConn
->mutexQuery
);
933 for(int i
= 0; i
< pResult
->numColumns
; i
++)
935 free(pResult
->data
[i
]);
936 free(pResult
->columnNames
[i
]);
939 free(pResult
->columnNames
);
946 extern "C" DWORD EXPORT
DrvBegin(MSSQL_CONN
*pConn
)
952 return DBERR_INVALID_HANDLE
;
954 MutexLock(pConn
->mutexQuery
);
955 nRet
= SQLSetConnectAttr(pConn
->sqlConn
, SQL_ATTR_AUTOCOMMIT
, (SQLPOINTER
)SQL_AUTOCOMMIT_OFF
, 0);
956 if ((nRet
== SQL_SUCCESS
) || (nRet
== SQL_SUCCESS_WITH_INFO
))
958 dwResult
= DBERR_SUCCESS
;
962 dwResult
= GetSQLErrorInfo(SQL_HANDLE_DBC
, pConn
->sqlConn
, NULL
);
964 MutexUnlock(pConn
->mutexQuery
);
971 extern "C" DWORD EXPORT
DrvCommit(MSSQL_CONN
*pConn
)
976 return DBERR_INVALID_HANDLE
;
978 MutexLock(pConn
->mutexQuery
);
979 nRet
= SQLEndTran(SQL_HANDLE_DBC
, pConn
->sqlConn
, SQL_COMMIT
);
980 SQLSetConnectAttr(pConn
->sqlConn
, SQL_ATTR_AUTOCOMMIT
, (SQLPOINTER
)SQL_AUTOCOMMIT_ON
, 0);
981 MutexUnlock(pConn
->mutexQuery
);
982 return ((nRet
== SQL_SUCCESS
) || (nRet
== SQL_SUCCESS_WITH_INFO
)) ? DBERR_SUCCESS
: DBERR_OTHER_ERROR
;
986 * Rollback transaction
988 extern "C" DWORD EXPORT
DrvRollback(MSSQL_CONN
*pConn
)
993 return DBERR_INVALID_HANDLE
;
995 MutexLock(pConn
->mutexQuery
);
996 nRet
= SQLEndTran(SQL_HANDLE_DBC
, pConn
->sqlConn
, SQL_ROLLBACK
);
997 SQLSetConnectAttr(pConn
->sqlConn
, SQL_ATTR_AUTOCOMMIT
, (SQLPOINTER
)SQL_AUTOCOMMIT_ON
, 0);
998 MutexUnlock(pConn
->mutexQuery
);
999 return ((nRet
== SQL_SUCCESS
) || (nRet
== SQL_SUCCESS_WITH_INFO
)) ? DBERR_SUCCESS
: DBERR_OTHER_ERROR
;
1003 * Check if table exist
1005 extern "C" int EXPORT
DrvIsTableExist(MSSQL_CONN
*pConn
, const WCHAR
*name
)
1008 swprintf(query
, 256, L
"SELECT count(*) FROM sysobjects WHERE xtype='U' AND upper(name)=upper('%ls')", name
);
1010 WCHAR errorText
[DBDRV_MAX_ERROR_TEXT
];
1011 int rc
= DBIsTableExist_Failure
;
1012 MSSQL_QUERY_RESULT
*hResult
= (MSSQL_QUERY_RESULT
*)DrvSelect(pConn
, query
, &error
, errorText
);
1013 if (hResult
!= NULL
)
1015 WCHAR buffer
[64] = L
"";
1016 DrvGetField(hResult
, 0, 0, buffer
, 64);
1017 rc
= (wcstol(buffer
, NULL
, 10) > 0) ? DBIsTableExist_Found
: DBIsTableExist_NotFound
;
1018 DrvFreeResult(hResult
);
1026 bool WINAPI
DllMain(HINSTANCE hInstance
, DWORD dwReason
, LPVOID lpReserved
)
1028 if (dwReason
== DLL_PROCESS_ATTACH
)
1029 DisableThreadLibraryCalls(hInstance
);