Изменение поведения драйвера ODBC при обработке преобразования символов

Применимо к:SQL ServerAzure SQL DatabaseAzure Managed InstanceAzure Synapse Analytics AnalyticsPlatform System (PDW)

Внимание

Собственный клиент SQL Server (часто сокращенный SNAC) был удален из SQL Server 2022 (16.x) и SQL Server Management Studio 19 (SSMS). Собственный клиент SQL Server (SQLNCLI или SQLNCLI11) и устаревший поставщик Microsoft OLE DB для SQL Server (SQLOLEDB) не рекомендуется для разработки новых приложений. Перейдите на новый драйвер Microsoft OLE DB (MSOLEDBSQL) для SQL Server или последний драйвер Microsoft ODBC для SQL Server . Сведения о SQLNCLI, которые поставляется в качестве компонента ядра СУБД SQL Server (версии 2012–2019), см. в этом исключении жизненного цикла поддержки.

Драйвер ODBC собственного клиента SQL Server 2012 (11.x) (SQLNCLI11.dll) изменил способ преобразования SQL_WCHAR* (NCHAR/NVARCHAR/NVARCHAR/MAX)* и SQL_CHAR* (CHAR/VARCHAR/NARCHAR(MAX)). Функции ODBC, такие как SQLGetData, SQLBindCol, SQLBindParameter, return (-4) SQL_NO_TOTAL в качестве параметра длины или индикатора при использовании драйвера ODBC собственного клиента SQL Server 2012. Предыдущие версии драйвера 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  
Версия драйвера ODBC для собственного клиента SQL Server Итоговая длина или индикатор Description
Собственный клиент SQL Server 2008 R2 (10.50.x) или более ранний 6 Драйвер ошибочно предположил, что преобразование CHAR в WCHAR можно было выполнить как умножение длины на 2.
Собственный клиент SQL Server 2012 (11.x) (версия 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  
Версия драйвера ODBC для собственного клиента SQL Server Итоговая длина или индикатор Description
Собственный клиент SQL Server 2008 R2 (10.50.x) или более ранний 20 SQLFetch сообщает, что в правой части данных имеется усечение.

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

В буфере хранятся следующие данные: 123\0. Буфер гарантированно заканчивается на NULL.
Собственный клиент SQL Server 2012 (11.x) (версия 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  
Версия драйвера ODBC для собственного клиента SQL Server Итоговая длина или индикатор Description
Собственный клиент SQL Server 2008 R2 (10.50.x) или более ранний 2468 SQLFetch не возвращает больше доступных данных.

SQLMoreResults не возвращает больше доступных данных.

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

Первоначальный буфер содержит 63 байта и признак конца NULL. Буфер гарантированно заканчивается на NULL.
Собственный клиент SQL Server 2012 (11.x) (версия 11.0.2100.60) или более поздней версии -4 (SQL_NO_TOTAL) SQLFetch не возвращает больше доступных данных.

SQLMoreResults не возвращает больше доступных данных.

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

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

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

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

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

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

См. также

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