次の方法で共有


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

適用対象: SQL Server Azure SQL データベース Azure SQL Managed Instance 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)) 変換の処理方法を変更しました。 SQLGetData、SQLBindCol、SQLBindParameter などの ODBC 関数は、SQL Server 2012 Native Client ODBC ドライバーを使用する場合、長さ/インジケーター パラメーターとしてSQL_NO_TOTAL (-4) を返します。 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 または (除算)/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 Driver のバージョン 長さまたはインジケーターの結果 説明
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 Driver のバージョン 長さまたはインジケーターの結果 説明
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 を使用してバインドするときに、指定したバッファーに保存または切り捨てられます。

  • バインドしない場合は、 SQLGetDataSQLParamData を使用して、チャンク単位でデータを取得できます。

参照

SQL Server Native Client の機能