次の方法で共有


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

SQL Server 2012 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  
ODBC ドライバーのバージョンをSQL Server Native Clientする 長さまたはインジケーターの結果 説明
SQL Server 2008 R2 Native Client 以前 6 ドライバーには、CHAR から WCHAR への変換が長さ * 2 として実行できるという誤った想定がありました。
SQL Server 2012 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  
ODBC ドライバーのバージョンをSQL Server Native Clientする 長さまたはインジケーターの結果 説明
SQL Server 2008 R2 Native Client 以前 20 - SQLFetch は、データの右側に切り捨てがあることを報告します。
- Length は、格納されたデータではなく、返されるデータの長さです (グリフに対して正しくない可能性がある *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 (出力パラメーターの動作)

クエリ: create procedure spTest @p1 varchar(max) OUTPUT

select @p1 = replicate('B', 1234)

SQLBindParameter(... SQL_W_CHAR, ...)   // Only bind up to first 64 characters  
ODBC ドライバーのバージョンをSQL Server Native Clientする 長さまたはインジケーターの結果 説明
SQL Server 2008 R2 Native Client 以前 2468 - SQLFetch は、これ以上使用できないデータを返します。
- SQLMoreResults は 、これ以上使用できないデータを返します。
- Length は、バッファーに格納されずにサーバーから返されるデータのサイズを示します。
- 元のバッファーには、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)、...) の操作に似ています。

  • SQLBindCol または SQLBindParameter を使用してバインドすると、指定したバッファーにデータが保存または切り捨てられます。

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

参照

SQL Server Native Client の機能