ODBC 驅動程式在處理字元轉換上的行為變更
SQL Server 2012 Native Client ODBC 驅動程式 (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 Native Client 或更早版本 |
6 |
驅動程式誤認為只要長度 * 2 即可將 CHAR 轉換成 WCHAR。 |
SQL Server 2012 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 Native Client 或更早版本 |
20 |
|
SQL Server 2012 Native Client (11.0.2100.60 版) 或更新版本 |
-4 (SQL_NO_TOTAL) |
|
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 Native Client 或更早版本 |
2468 |
|
SQL Server 2012 Native Client (11.0.2100.60 版) 或更新版本 |
-4 (SQL_NO_TOTAL) |
|
執行 CHAR 和 WCHAR 轉換
SQL Server 2012 Native Client ODBC 驅動程式提供了幾種方法來執行 CHAR 和 WCHAR 轉換。 其邏輯類似於操作 BLOB (varchar(max)、nvarchar(max) 等):
透過 SQLBindCol 或 SQLBindParameter 進行繫結時,資料會儲存或截斷至指定的緩衝區。
如果沒有繫結,便可使用 SQLGetData 和 SQLParamData 以區塊形式擷取資料。