处理字符转换时 ODBC 驱动程序行为更改

SQL Server 2012 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 获取数据大小。

下面显示了使用不正确模式时驱动程序更改的影响。 此应用程序将列和绑定查询 varchar 为 Unicode(SQL_UNICODE/SQL_WCHAR):

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

SQLGetData(hstmt, SQL_WCHAR, ....., (SQLPOINTER*) 0x1, 0 , &iSize);   // Attempting to determine storage size needed  
SQL Server Native Client ODBC 驱动程序版本 长度或指示器结果 DESCRIPTION
SQL Server 2008 R2 Native Client 或更早版本 6 驱动程序错误地假定可以将 CHAR 转换为 WCHAR 的长度 * 2。
SQL Server 2012 Native Client (版本 11.0.2100.60) 或更高版本 -4 (SQL_NO_TOTAL) 驱动程序不再假定从 CHAR 转换为 WCHAR 或 WCHAR 是 *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 驱动程序版本 长度或指示器结果 DESCRIPTION
SQL Server 2008 R2 Native Client 或更早版本 20 - SQLFetch 报告数据右侧存在截断。
- 长度是返回的数据的长度,而不是存储的数据(假定 *2 CHAR 到 WCHAR 转换,对于字形来说可能不正确)。
- 存储在缓冲区中的数据为 123\0。 缓冲区保证为 NULL 终止。
SQL Server 2012 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 驱动程序版本 长度或指示器结果 DESCRIPTION
SQL Server 2008 R2 Native Client 或更早版本 2468 - SQLFetch 不会返回更多可用数据。
- SQLMoreResults 不返回更多可用数据。
- 长度指示从服务器返回的数据的大小,而不是存储在缓冲区中。
- 原始缓冲区包含 63 个字节和 NULL 终止符。 缓冲区保证为 NULL 终止。
SQL Server 2012 Native Client (版本 11.0.2100.60) 或更高版本 -4 (SQL_NO_TOTAL) - SQLFetch 不会返回更多可用数据。
- SQLMoreResults 不返回更多可用数据。
- 长度指示 (-4) SQL_NO_TOTAL,因为其余的数据未转换。
- 原始缓冲区包含 63 个字节和 NULL 终止符。 缓冲区保证为 NULL 终止。

执行 CHAR 和 WCHAR 转换

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

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

  • 如果不绑定,可以使用 SQLGetDataSQLParamData 检索区块中的数据。

另请参阅

SQL Server Native Client 功能