处理字符转换时 ODBC 驱动程序行为的变化

适用于:SQL ServerAzure SQL 数据库Azure SQL 托管实例Azure Synapse AnalyticsAnalytics Platform System (PDW)

重要

已从 SQL Server 2022 (16.x) 和 SQL Server Management Studio 19 (SSMS) 中删除SQL Server Native Client(通常缩写为 SNAC)。 不建议在新应用程序开发工作中使用 SQL Server Native Client(SQLNCLI 或 SQLNCLI11)和旧版 Microsoft OLE DB Provider for SQL Server (SQLOLEDB)。 请在此后切换为使用新版 Microsoft OLE DB Driver (MSOLEDBSQL) for SQL Server 或最新版的 Microsoft OLE DB Driver for SQL Server。 对于作为 SQL Server 数据库引擎组件随附的 SQLNCLI (版本 2012 到 2019) ,请参阅此支持生命周期异常

SQL Server 2012 (11.x) Native Client ODBC Driver (SQLNCLI11.dll) 更改了SQL_WCHAR* (NCHAR/NVARCHAR/NVARCHAR (MAX) ) 和 SQL_CHAR* (CHAR/VARCHAR/NARCHAR (MAX) ) 转换的方式。 使用 SQL Server 2012 Native Client ODBC 驱动程序时,ODBC 函数(如 SQLGetData、SQLBindCol、SQLBindParameter)返回 (-4) SQL_NO_TOTAL 作为长度/指示器参数。 SQL Server Native Client ODBC 驱动程序的早期版本返回了长度值,该值可能不正确。

SQLGetData 行为

许多 Windows 函数允许指定缓冲区大小为 0,且返回的长度为返回的数据大小。 以下模式对于 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 获取数据的大小。

下面说明当您使用不正确的模式时驱动程序变化的影响。 此应用程序以 Unicode (SQL_UNICODE/SQL_WCHAR) 的形式查询 varchar 列和绑定:

查询:select convert(varchar(36), '123')

SQLGetData(hstmt, SQL_WCHAR, ....., (SQLPOINTER*) 0x1, 0 , &iSize);   // Attempting to determine storage size needed  
SQL Server Native Client ODBC 驱动程序版本 长度或指示符的结果 说明
SQL Server 2008 R2 (10.50.x) Native Client 或更早版本 6 驱动程序错误地假定将 CHAR 转换为 WCHAR 可以通过长度 * 2 来实现。
SQL Server 2012 (11.x) Native Client (版本 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  
SQL Server Native Client ODBC 驱动程序版本 长度或指示符的结果 说明
SQL Server 2008 R2 (10.50.x) Native Client 或更早版本 20 SQLFetch 报告数据右侧存在截断。

长度为返回的数据长度而非存储的数据长度(假定 *2 CHAR 到 WCHAR 的转换对于符号可能不正确)。

存储在缓冲区中的数据为 123\0。 缓冲区被保证为以 NULL 结束。
SQL Server 2012 (11.x) Native Client (版本 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  
SQL Server Native Client ODBC 驱动程序版本 长度或指示符的结果 说明
SQL Server 2008 R2 (10.50.x) Native Client 或更早版本 2468 SQLFetch 返回没有更多可用数据。

SQLMoreResults 返回没有更多可用数据。

长度指示从服务器返回的数据大小而非缓冲区中存储的数据大小。

原始缓冲区包含 63 个字节和 NULL 结束符。 缓冲区被保证为以 NULL 结束。
SQL Server 2012 (11.x) Native Client (版本 11.0.2100.60) 或更高版本 -4 (SQL_NO_TOTAL) SQLFetch 返回没有更多可用数据。

SQLMoreResults 返回没有更多可用数据。

长度指示 (-4) SQL_NO_TOTAL,因为数据的其余部分未转换。

原始缓冲区包含 63 个字节和 NULL 结束符。 缓冲区被保证为以 NULL 结束。

执行 CHAR 和 WCHAR 转换

SQL Server 2012 (11.x) Native Client ODBC 驱动程序提供多种方法来执行 CHAR 和 WCHAR 转换。 该逻辑类似于操作 blob (varchar (max) 、nvarchar (max) ...) :

  • 使用 SQLBindCol 或 SQLBindParameter 绑定时,数据将保存或截断到指定的缓冲区中。

  • 如果未绑定,可以使用 SQLGetData 和 SQLParamData 检索区块中的数据。

另请参阅

SQL Server Native Client 功能