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, return(-4) 등의 ODBC 함수는 길이/표시기 매개 변수로 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 유니코드(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로 변환하는 것이 (곱하기) *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 |
-
SQLFetch 는 데이터의 오른쪽에 잘림이 있다고 보고합니다. - 길이는 저장된 데이터가 아니라 반환된 데이터의 길이입니다(문자 모양에 대해 올바르지 않을 수 있는 *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(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 |
-
SQLFetch는 더 이상 사용할 수 있는 데이터를 반환하지 않습니다. - SQLMoreResults는 더 이상 사용할 수 있는 데이터를 반환하지 않습니다. - 길이는 버퍼에 저장되지 않고 서버에서 반환된 데이터의 크기를 나타냅니다. - 원래 버퍼에는 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를 사용하여 청크로 데이터를 검색할 수 있습니다.