Поделиться через


Изменение поведения драйвера 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

  • SQLFetch сообщает о наличии усечения с правой стороны данных.

  • Длина является длиной возращенных данных, а не того, что было сохранено (предполагается преобразование CHAR в WCHAR при помощи операции *2, что может быть неверным для глифов).

  • В буфере хранятся следующие данные: 123\0. Буфер гарантированно заканчивается на NULL.

Native Client SQL Server 2012 (версия 11.0.2100.60) или более поздняя версия

-4 (SQL_NO_TOTAL)

  • SQLFetch сообщает о наличии усечения с правой стороны данных.

  • Длина указывает -4 (SQL_NO_TOTAL), поскольку остальные данные не были преобразованы.

  • В буфере хранятся следующие данные: 123\0. - Буфер гарантированно заканчивается на NULL.

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

  • SQLFetch возвращает сообщение о том, что теперь нет доступных данных.

  • SQLMoreResults возвращает сообщение о том, что теперь нет доступных данных.

  • Длина указывает размер данных, возвращенных с сервера, а нет тех, которые хранятся в буфере.

  • Первоначальный буфер содержит 63 байта и признак конца NULL. Буфер гарантированно заканчивается на NULL.

Native Client SQL Server 2012 (версия 11.0.2100.60) или более поздняя версия

-4 (SQL_NO_TOTAL)

  • SQLFetch возвращает сообщение о том, что теперь нет доступных данных.

  • SQLMoreResults возвращает сообщение о том, что теперь нет доступных данных.

  • Длина указывает (-4) SQL_NO_TOTAL, поскольку остальные данные не были преобразованы.

  • Первоначальный буфер содержит 63 байта и признак конца NULL. Буфер гарантированно заканчивается на NULL.

Выполнение преобразований CHAR и WCHAR

Драйвер Native Client ODBC SQL Server 2012 предлагает несколько вариантов выполнения преобразования CHAR и WCHAR. Логика этих преобразований похожа на обработку больших двоичных объектов (varchar(max), nvarchar(max), …):

  • При привязке с помощью функции SQLBindCol или SQLBindParameter данные сохраняются в указанный буфер или усекаются в этом буфере.

  • Если привязка не выполняется, то можно получать фрагменты данных с помощью функций SQLGetData и SQLParamData.

См. также

Другие ресурсы

Компоненты собственного клиента SQL Server