ODBC 驅動程式在處理字元轉換上的行為變更

SQL Server 2012 Native Client ODBC Driver (SQLNCLI11.dll) 變更了SQL_WCHAR* (NCHAR/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 - 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 驅動程式版本 長度或指標結果 描述
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 功能