Изменение поведения драйвера ODBC при обработке преобразования символов
Драйвер Native Client ODBC SQL Server 2012 (SQLNCLI11.dll) изменен в том, как он выполняет преобразования SQL_WCHAR* (NCHAR/NVARCHAR/NVARCHAR(MAX)) и SQL_CHAR* (CHAR/VARCHAR/NARCHAR(MAX)). При использовании драйвера Native Client ODBC SQL Server 2012 функции ODBC, например SQLGetData, SQLBindCol и SQLBindParameter, возвращают (-4) SQL_NO_TOTAL в качестве параметра длины или индикатора. Предыдущие версии драйвера Native Client ODBC SQL Server возвращали значение длины, которое могло быть неверным.
Поведение SQLGetData
Многие функции Windows позволяют указывать нулевой размер буфера, при этом возвращаемая длина является размером возвращаемых данных. Следующий вариант является стандартным для программистов Windows:
int iSize = 0;
BYTE * pBuffer = NULL;
GetMyFavoriteAPI(pBuffer, &iSize); // Returns needed size in iSize
pBuffer = new BYTE[iSize]; // Allocate buffer
GetMyFavoriteAPI(pBuffer, &iSize); // Retrieve actual data
Однако в этом сценарии не следует использовать функцию SQLGetData. Не следует использовать следующий вариант.
// bad
int iSize = 0;
WCHAR * pBuffer = NULL;
SQLGetData(hstmt, SQL_W_CHAR, ...., (SQLPOINTER*)0x1, 0, &iSize); // Get storage size needed
pBuffer = new WCHAR[(iSize/sizeof(WCHAR)) + 1]; // Allocate buffer
SQLGetData(hstmt, SQL_W_CHAR, ...., (SQLPOINTER*)pBuffer, iSize, &iSize); // Retrieve data
Функцию SQLGetData можно вызывать только для получения фрагментов фактических данных. Использование функции SQLGetData для получения размера данных не поддерживается.
Далее показано влияние изменения драйвера, которое проявляется при использовании неверного варианта. Это приложение запрашивает столбец и привязку varchar как Юникод (SQL_UNICODE/SQL_WCHAR):
Запрос: select convert(varchar(36), '123')
SQLGetData(hstmt, SQL_WCHAR, ….., (SQLPOINTER*) 0x1, 0 , &iSize); // Attempting to determine storage size needed
Версия драйвера Native Client ODBC SQL Server |
Итоговая длина или индикатор |
Описание |
---|---|---|
Native Client SQL Server 2008 R2 или более ранняя версия |
6 |
Драйвер ошибочно предположил, что преобразование CHAR в WCHAR можно было выполнить как умножение длины на 2. |
Native Client SQL Server 2012 (версия 11.0.2100.60) или более поздняя версия |
-4 (SQL_NO_TOTAL) |
Драйвер больше не предполагает, что преобразование из CHAR в WCHAR или из WCHAR в CHAR является действием (умножения) *2 или (деления)/2. При вызове функции SQLGetData длина ожидаемого преобразования больше не возвращается. Драйвер обнаруживает преобразование из CHAR в WCHAR или обратное преобразование и возвращает (-4) SQL_NO_TOTAL вместо *2 или /2, что могло быть неверным. |
Используйте функцию SQLGetData для получения фрагментов данных. (Показан псевдокод).
while( (SQL_SUCCESS or SQL_SUCCESS_WITH_INFO) == SQLFetch(...) ) {
SQLNumCols(...iTotalCols...)
for(int iCol = 1; iCol < iTotalCols; iCol++) {
WCHAR* pBufOrig, pBuffer = new WCHAR[100];
SQLGetData(.... iCol … pBuffer, 100, &iSize); // Get original chunk
while(NOT ALL DATA RETREIVED (SQL_NO_TOTAL, ...) ) {
pBuffer += 50; // Advance buffer for data retrieved
// May need to realloc the buffer when you reach current size
SQLGetData(.... iCol … pBuffer, 100, &iSize); // Get next chunk
}
}
}
Поведение функции SQLBindCol
Запрос: select convert(varchar(36), '1234567890')
SQLBindCol(… SQL_W_CHAR, …) // Only bound a buffer of WCHAR[4] – Expecting String Data Right Truncation behavior
Версия драйвера Native Client ODBC SQL Server |
Итоговая длина или индикатор |
Описание |
---|---|---|
Native Client SQL Server 2008 R2 или более ранняя версия |
20 |
|
Native Client SQL Server 2012 (версия 11.0.2100.60) или более поздняя версия |
-4 (SQL_NO_TOTAL) |
|
SQLBindParameter (поведение параметра OUTPUT)
Запрос: create procedure spTest @p1 varchar(max) OUTPUT
select @p1 = replicate('B', 1234)
SQLBindParameter(… SQL_W_CHAR, …) // Only bind up to first 64 characters
Версия драйвера Native Client ODBC SQL Server |
Итоговая длина или индикатор |
Описание |
---|---|---|
Native Client SQL Server 2008 R2 или более ранняя версия |
2468 |
|
Native Client SQL Server 2012 (версия 11.0.2100.60) или более поздняя версия |
-4 (SQL_NO_TOTAL) |
|
Выполнение преобразований CHAR и WCHAR
Драйвер Native Client ODBC SQL Server 2012 предлагает несколько вариантов выполнения преобразования CHAR и WCHAR. Логика этих преобразований похожа на обработку больших двоичных объектов (varchar(max), nvarchar(max), …):
При привязке с помощью функции SQLBindCol или SQLBindParameter данные сохраняются в указанный буфер или усекаются в этом буфере.
Если привязка не выполняется, то можно получать фрагменты данных с помощью функций SQLGetData и SQLParamData.