文字変換処理での ODBC ドライバーの動作の変更

適用対象:SQL ServerAzure SQL DatabaseAzure SQL Managed InstanceAzure Synapse AnalyticsAnalytics Platform System (PDW)

重要

SQL Server Native Client (SNAC と略されることがよくあります) は、SQL Server 2022 (16.x) と SQL Server Management Studio 19 (SSMS) から削除されました。 SQL Server Native Client (SQLNCLI または SQLNCLI11) とレガシ Microsoft OLE DB Provider for SQL Server (SQLOLEDB) は、新しいアプリケーション開発には推奨されません。 今後、新しい Microsoft OLE DB Driver (MSOLEDBSQL) for SQL Server または最新の Microsoft ODBC Driver for SQL Server に切り替えてください。 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)) 変換の処理方法を変更しました。 SQLGetData、SQLBindCol、SQLBindParameter などの ODBC 関数は、SQL Server 2012 Native Client ODBC ドライバーを使用する場合、長さ/インジケーター パラメーターとして (-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 から CHAR への変換が (乗算) *2 または (除算) /2 操作であると想定されることはなくなりました。

SQLGetData を呼び出しても、予想される変換の長さが返されなくなりました。 ドライバーでは CHAR と WCHAR との間の変換が検出され、誤りの可能性のある *2 または /2 の動作の代わりに (-4) SQL_NO_TOTAL が返されます。

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 (出力パラメーターの動作)

クエリ: 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 を使用してチャンク単位でデータ 取得できます。

参照

SQL Server Native Client の機能