处理字符转换时 ODBC 驱动程序行为的变化
适用于:SQL Server Azure SQL 数据库 Azure SQL 托管实例 Azure Synapse Analytics Analytics Platform System (PDW)
重要
SQL Server Native Client (SNAC) 未随附:
- SQL Server 2022 (16.x) 及更高版本
- SQL Server Management Studio 19 及更高版本
不建议使用 SQL Server Native Client(SQLNCLI 或 SQLNCLI11)和旧的 Microsoft OLE DB Provider for SQL Server (SQLOLEDB)进行新的应用程序开发。
对于新项目,请使用以下驱动程序之一:
对于作为 SQL Server 数据库引擎组件(版本 2012 到 2019)随附的 SQLNCLI,请参阅此支持生命周期特例。
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 获取数据大小。
下面说明当您使用不正确的模式时驱动程序变化的影响。 此应用程序将 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 驱动程序版本 | 长度或指示符的结果 | 说明 |
---|---|---|
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 是 *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 检索区块中的数据。