共用方式為


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 Driver 版本 長度或指標結果 描述
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 或 (divide)/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 Driver 版本 長度或指標結果 描述
SQL Server 2008 R2 (10.50.x) Native Client 或更早版本 20 SQLFetch 會報告數據右側有截斷。

Length 是傳回的數據長度,而不是儲存的數據長度(假設 *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 Driver 版本 長度或指標結果 描述
SQL Server 2008 R2 (10.50.x) Native Client 或更早版本 2468 SQLFetch 不會傳回更多可用的數據。

SQLMoreResults 不會傳回更多可用的數據。

Length 表示從伺服器傳回的數據大小,而不是儲存在緩衝區中。

原始緩衝區包含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), ...):

  • 使用 SQLBindColSQLBindParameter 系結時,數據會儲存或截斷到指定的緩衝區。

  • 如果您未系結,您可以使用 SQLGetDataSQLParamData 來擷取區塊中的數據。

另請參閱

SQL Server Native Client 功能