文字変換処理での ODBC ドライバーの動作の変更
SQL Server 2012 Native Client ODBC ドライバー (SQLNCLI11.dll) での SQL_WCHAR* (NCHAR/NVARCHAR/NVARCHAR(MAX)) と SQL_CHAR* (CHAR/VARCHAR/NARCHAR(MAX)) の変換方法が変更されました。 SQL Server 2012 Native Client ODBC ドライバーを使用する場合、SQLGetData、SQLBindCol、SQLBindParameter などの 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 Native Client 以前 |
6 |
ドライバーには、CHAR から WCHAR への変換が長さ * 2 として実行できるという誤った想定がありました。 |
SQL Server 2012 Native Client (Version 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 Native Client 以前 |
20 |
|
SQL Server 2012 Native Client (Version 11.0.2100.60) 以降 |
-4 (SQL_NO_TOTAL) |
|
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 Native Client 以前 |
2468 |
|
SQL Server 2012 Native Client (Version 11.0.2100.60) 以降 |
-4 (SQL_NO_TOTAL) |
|
CHAR と WCHAR の変換の実行
SQL Server 2012 Native Client ODBC ドライバーには、CHAR と WCHAR の変換を実行する方法が複数用意されています。 論理は BLOB (varchar(max)、nvarchar(max) など) の操作に似ています。
SQLBindCol または SQLBindParameter でバインドする場合、データは指定されたバッファーに保存されるか、切り捨てられます。
バインドしない場合は、SQLGetData や SQLParamData を使用してデータ チャンクを取得できます。