문자 변환을 처리 시 ODBC 드라이버 동작 변경
SQL Server 2012 Native Client ODBC 드라이버(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 ServerNative 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열 및 바인딩을 유니코드(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(버전 11.0.2100.60) 이상 |
-4(SQL_NO_TOTAL) |
드라이버에서는 더 이상 CHAR에서 WCHAR로 또는 WCHAR에서 CHAR로 변환이 (곱하기) *2 또는 (나누기)/2 동작이라고 가정하지 않습니다. SQLGetData를 호출하면 예상한 변환 길이가 더 이상 반환되지 않습니다. 드라이버가 CHAR에서 WCHAR 또는 WCHAR에서 CHAR로 변환을 검색하고 잘못될 수 있는 *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(버전 11.0.2100.60) 이상 |
-4(SQL_NO_TOTAL) |
|
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 드라이버 버전 |
길이 또는 표시기 결과 |
설명 |
---|---|---|
SQL Server 2008 R2 Native Client 이하 |
2468 |
|
SQL Server 2012 Native Client(버전 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를 사용하여 데이터를 청크로 검색할 수 있습니다.