큰 값 형식 사용
SQL Server 2005 이전에 큰 값 데이터 형식으로 작업하려면 특별한 처리가 필요했습니다. 큰 값 데이터 형식은 최대 행 크기가 8KB를 초과하는 데이터 형식입니다. SQL Server 2005에서는 varchar, nvarchar 및 varbinary 데이터 형식에 대해 max 지정자를 제공하여 값을 2^31 -1바이트로 저장할 수 있도록 합니다. 테이블 열과 Transact-SQL 변수에서 varchar(max), nvarchar(max) 또는 varbinary(max) 데이터 형식을 지정할 수 있습니다.
[!참고]
큰 값 데이터 형식은 1-8KB의 최대 크기를 가질 수 있거나 무제한으로 지정될 수 있습니다.
이전에는 text, ntext 및 image와 같은 SQL Server 데이터 형식만 이러한 길이에 도달할 수 있었습니다. varchar, nvarchar 및 varbinary에 대한 max 지정자로 인해 이러한 데이터 형식은 중복이 되었습니다. 그러나 긴 데이터 형식은 여전히 사용할 수 있으므로, OLE DB 및 ODBC 데이터 액세스 구성 요소에 대한 대부분의 인터페이스는 동일하게 유지됩니다. 이전 릴리스와의 호환성을 위해 SQL Server Native Client OLE DB 공급자의 DBCOLUMNFLAGS_ISLONG 플래그와 SQL Server Native Client ODBC 드라이버의 SQL_LONGVARCHAR는 계속 사용됩니다. SQL Server 2005 이상 버전 대해 작성된 공급자와 드라이버는 무제한 최대 길이로 설정된 경우 새 형식에 대해 계속 이러한 용어를 사용합니다.
[!참고]
저장 프로시저, 함수 반환 형식 또는 CAST 및 CONVERT 함수의 입력 및 출력 매개 변수 형식으로 varchar(max), nvarchar(max) 및 varbinary(max) 데이터 형식을 지정할 수도 있습니다.
[!참고]
데이터를 복제할 경우 최대 텍스트 복제 크기 서버 구성 옵션을 -1로 구성해야 할 수 있습니다.
SQL Server Native Client OLE DB 공급자
SQL Server Native Client OLE DB 공급자는 varchar(max), varbinary(max) 및 nvarchar(max) 형식을 각각 DBTYPE_STR, DBTYPE_BYTES 및 DBTYPE_WSTR로 노출합니다.
max 크기가 무제한으로 설정된 열의 varchar(max), varbinary(max) 및 nvarchar(max) 데이터 형식은 열 데이터 형식을 반환하는 핵심 OLE DB 스키마 행 집합과 인터페이스를 통해 ISLONG으로 표시됩니다.
명령 개체의 IAccessor 구현이 DBTYPE_IUNKNOWN으로의 바인딩을 허용하도록 변경되었습니다. 소비자가 DBTYPE_IUNKNOWN을 지정하고 pObject를 Null로 설정하면 소비자가 출력 변수에서 varchar(max), nvarchar(max) 또는 varbinary(max) 데이터를 스트리밍할 수 있도록 공급자가 ISequentialStream 인터페이스를 소비자에게 반환합니다.
스트리밍된 출력 매개 변수 값은 결과 행 뒤에 반환됩니다. 응용 프로그램이 반환된 모든 출력 매개 변수 값을 사용하지 않고 IMultipleResults::GetResult를 호출하여 다음 결과 집합으로 이동하면 DB_E_OBJECTOPEN이 반환됩니다.
SQL Server Native Client OLE DB 공급자에서 스트리밍을 지원하려면 가변 길이 매개 변수가 순차적으로 액세스되어야 합니다. 즉, varchar(max), nvarchchar(max) 또는 varbinary(max) 열이나 출력 매개 변수가 DBTYPE_IUNKNOWN에 바인딩되어 있을 때마다 DBPROP_ACCESSORDER를 DBPROPVAL_AO_SEQUENTIALSTORAGEOBJECTS 또는 DBPROPVAL_AO_SEQUENTIAL로 설정해야 합니다. 이 액세스 순서 제한을 따르지 않으면 DBSTATUS_E_UNAVAILABLE로 인해 IRowset::GetData에 대한 호출이 실패합니다. DBTYPE_IUNKNOWN을 사용한 출력 바인딩이 없을 경우에는 이 제한이 적용되지 않습니다.
또한 SQL Server Native Client OLE DB 공급자는 저장 프로시저에서 클라이언트에 DBTYPE_IUNKNOWN으로 노출되는 큰 값 형식을 반환 값으로 반환하는 시나리오에 유용하도록 큰 값 데이터 형식에 대해 바인딩 출력 매개 변수를 DBTYPE_IUNKNOWN으로 지원합니다.
이러한 형식으로 작업하기 위해 응용 프로그램에는 다음 옵션이 있습니다.
열의 기본 형식과 지원되는 바인딩이 있는 형식으로 바인딩합니다. 예를 들어 nvarchar(max)의 경우 nvarchar에 바인딩될 수 있는 형식으로 바인딩합니다. 버퍼가 충분히 크지 않으면 이제 더 큰 값을 사용할 수 있어도 정확하게 기본 형식을 기준으로 잘림이 발생합니다.
열의 기본 형식과 지원되는 변환이 있는 형식으로 바인딩하고 DBTYPE_BYREF도 지정합니다.
DBTYPE_IUNKNOWN으로 바인딩하고 스트리밍을 사용합니다.
열의 최대 크기를 보고할 때 SQL Server Native Client OLE DB 공급자는 다음을 보고합니다.
정의된 최대 크기(예를 들어 varchar(2000) 열의 경우 2000) 또는
값 "무제한". varchar(max) 열의 경우 ~0과 같습니다. 이 값은 DBCOLUMN_COLUMNSIZE 메타데이터 속성에 대해 설정됩니다.
표준 변환 규칙이 varchar(max) 열에 적용되므로 varchar(2000) 열에 유효한 모든 변환은 varchar(max) 열에도 유효합니다. nvarchar(max) 및 varbinary(max) 열의 경우도 마찬가지입니다.
큰 값 형식을 검색할 때 가장 효과적인 방법은 DBTYPE_IUNKNOWN으로 바인딩하고 행 집합 속성 DBPROP_ACCESSORDER를 DBPROPVAL_AO_SEQUENTIALSTORAGEOBJECTS로 설정하는 것입니다. 이렇게 하면 다음 예와 같이 값이 중간 버퍼링 없이 네트워크에서 직접 스트리밍됩니다.
#define UNICODE
#define _UNICODE
#define DBINITCONSTANTS
#define INITGUID
#define OLEDBVER 0x0250 // To include the correct interfaces.
#include <stdio.h>
#include <tchar.h>
#include <stddef.h>
#include <iostream>
using std::cout;
using std::endl;
#include <windows.h>
#include <oledb.h>
#include "sqlncli.h"
#include <oledberr.h>
#define CHKHR_GOTO(hr, errMsg, Label) \
if (FAILED(hr)) \
{ \
cout << errMsg << endl; \
goto Label; \
}
#define MAX_COL_SIZE 8000
// ROUNDUP on all platforms pointers must be aligned properly.
#define ROUNDUP_AMOUNT 8
#define ROUNDUP_(size,amount) (((ULONG)(size)+((amount)-1))&~((amount)-1))
#define ROUNDUP(size) ROUNDUP_(size, ROUNDUP_AMOUNT)
HRESULT InitializeAndEstablishConnection(IDBInitialize** ppIDBInitialize);
void UnInitializeConnection(IDBInitialize* pIDBInitialize);
HRESULT CreateAndSetCommand(IDBInitialize* pIDBInitialize, ICommandText** ppICommandText);
HRESULT ProcessResultSet(IRowset* pIRowset);
void DisplayTime()
{
SYSTEMTIME st;
GetSystemTime(&st);
cout<< st.wHour << ":" << st.wMinute << ":" << st.wSecond << "." << st.wMilliseconds << endl;
}
void main()
{
HRESULT hr;
IDBInitialize* pIDBInitialize = NULL;
ICommandText* pICommandText = NULL;
IMultipleResults* pIMultipleResults = NULL;
IRowset* pIRowset = NULL;
hr = InitializeAndEstablishConnection(&pIDBInitialize);
CHKHR_GOTO(hr, L"Failed to establish connection.", _ExitMain);
hr = CreateAndSetCommand(pIDBInitialize, &pICommandText);
CHKHR_GOTO(hr, L"Failed to set up command object.", _ExitMain);
DisplayTime();
hr = pICommandText->Execute(NULL,
IID_IMultipleResults,
NULL,
NULL,
(IUnknown **) &pIMultipleResults);
CHKHR_GOTO(hr, L"Failed to execute command.", _ExitMain);
while (1)
{
hr = pIMultipleResults->GetResult(
NULL,
DBRESULTFLAG_DEFAULT,
IID_IRowset,
NULL,
(IUnknown**)&pIRowset);
CHKHR_GOTO(hr, L"Failed to obtain a results from MR object.", _ExitMain);
if (hr == DB_S_NORESULT)
break;
if (pIRowset)
{
hr = ProcessResultSet(pIRowset);
CHKHR_GOTO(hr, L"Failed to process the current Rowset.", _ExitMain);
pIRowset->Release();
pIRowset = NULL;
}
}
DisplayTime();
_ExitMain:
if (pIRowset)
{
pIRowset->Release();
pIRowset = NULL;
}
if (pIMultipleResults)
{
pIMultipleResults->Release();
pIMultipleResults = NULL;
}
if (pICommandText)
{
pICommandText->Release();
pICommandText = NULL;
}
UnInitializeConnection(pIDBInitialize);
return;
};
HRESULT InitializeAndEstablishConnection(IDBInitialize** ppIDBInitialize)
{
HRESULT hr;
IDBInitialize* pIDBInitialize = NULL;
IDBProperties* pIDBProperties = NULL;
const int NUM_DBINIT_PROPS = 3;
const wchar_t* const g_wszServer = L".";
const wchar_t* const g_wszCatalog = L"AdventureWorks";
const wchar_t* const g_wszSecurity = L"SSPI";
DBPROPSET rgdbPropSetInit[1];
DBPROP rgdbPropInit [NUM_DBINIT_PROPS];
*ppIDBInitialize = NULL;
hr = CoInitialize(NULL);
CHKHR_GOTO(hr, L"Failed to initialize COM.", _ExitInitialize);
hr = CoCreateInstance(CLSID_SQLNCLI11,
NULL,
CLSCTX_INPROC_SERVER,
IID_IDBInitialize,
(void**)&pIDBInitialize);
CHKHR_GOTO(hr, L"Failed to create SQLNCLI11 DataSource object.", _ExitInitialize);
for(int idxProp = 0; idxProp < NUM_DBINIT_PROPS; idxProp++)
{
VariantInit(&rgdbPropInit[idxProp].vValue);
}
rgdbPropInit[0].dwPropertyID = DBPROP_INIT_DATASOURCE;
rgdbPropInit[0].vValue.vt = VT_BSTR;
rgdbPropInit[0].vValue.bstrVal= SysAllocString(g_wszServer);
rgdbPropInit[0].dwOptions = DBPROPOPTIONS_REQUIRED;
rgdbPropInit[0].colid = DB_NULLID;
if (rgdbPropInit[0].vValue.bstrVal == NULL)
{
hr = E_OUTOFMEMORY;
goto _ExitInitialize;
}
rgdbPropInit[1].dwPropertyID = DBPROP_INIT_CATALOG;
rgdbPropInit[1].vValue.vt = VT_BSTR;
rgdbPropInit[1].vValue.bstrVal= SysAllocString(g_wszCatalog);
rgdbPropInit[1].dwOptions = DBPROPOPTIONS_REQUIRED;
rgdbPropInit[1].colid = DB_NULLID;
if (rgdbPropInit[1].vValue.bstrVal == NULL)
{
hr = E_OUTOFMEMORY;
goto _ExitInitialize;
}
rgdbPropInit[2].dwPropertyID = DBPROP_AUTH_INTEGRATED;
rgdbPropInit[2].vValue.vt = VT_BSTR;
rgdbPropInit[2].vValue.bstrVal= SysAllocString(g_wszSecurity);
rgdbPropInit[2].dwOptions = DBPROPOPTIONS_REQUIRED;
rgdbPropInit[2].colid = DB_NULLID;
if (rgdbPropInit[2].vValue.bstrVal == NULL)
{
hr = E_OUTOFMEMORY;
goto _ExitInitialize;
}
rgdbPropSetInit[0].guidPropertySet = DBPROPSET_DBINIT;
rgdbPropSetInit[0].cProperties = NUM_DBINIT_PROPS;
rgdbPropSetInit[0].rgProperties = rgdbPropInit;
hr = pIDBInitialize->QueryInterface(IID_IDBProperties, (void **)&pIDBProperties);
CHKHR_GOTO(hr, L"Failed to QI DataSource object for IDBProperties.", _ExitInitialize);
hr = pIDBProperties->SetProperties(1, rgdbPropSetInit);
CHKHR_GOTO(hr, L"Failed to set DataSource object Properties.", _ExitInitialize);
pIDBProperties->Release();
pIDBProperties = NULL;
hr = pIDBInitialize->Initialize();
CHKHR_GOTO(hr, L"Failed to establish connection with the server.", _ExitInitialize);
_ExitInitialize:
if (pIDBProperties)
{
pIDBProperties->Release();
pIDBProperties = NULL;
}
if (FAILED(hr))
{
if (pIDBInitialize)
{
pIDBInitialize->Release();
pIDBInitialize = NULL;
}
}
*ppIDBInitialize = pIDBInitialize;
return hr;
}
void UnInitializeConnection(IDBInitialize* pIDBInitialize)
{
if (pIDBInitialize)
{
pIDBInitialize->Uninitialize();
pIDBInitialize->Release();
pIDBInitialize = NULL;
}
CoUninitialize();
}
HRESULT CreateAndSetCommand(IDBInitialize* pIDBInitialize, ICommandText** ppICommandText)
{
HRESULT hr;
IDBCreateSession* pIDBCreateSession = NULL;
IDBCreateCommand* pIDBCreateCommand = NULL;
ICommandText* pICommandText = NULL;
ICommandProperties* pICommandProperties = NULL;
DBPROPSET rgCmdPropSet[1];
DBPROP rgCmdProperties[1];
const wchar_t* const g_wCmdString = L"declare @x xml, @y nvarchar(max); select @x = (SELECT * FROM Sales.SalesOrderHeader FOR XML AUTO); select @x;";
*ppICommandText = NULL;
if (!pIDBInitialize)
{
hr = E_FAIL;
goto _ExitCreateAndSetCommand;
}
hr = pIDBInitialize->QueryInterface(IID_IDBCreateSession, (void**) &pIDBCreateSession);
CHKHR_GOTO(hr, L"Failed to obtain IDBCreateSession interface from DSO.", _ExitCreateAndSetCommand);
hr = pIDBCreateSession->CreateSession(
NULL,
IID_IDBCreateCommand,
(IUnknown**) &pIDBCreateCommand);
CHKHR_GOTO(hr, L"Failed to Create a Session for command execution.", _ExitCreateAndSetCommand);
hr = pIDBCreateCommand->CreateCommand(
NULL,
IID_ICommandText,
(IUnknown**)&pICommandText);
CHKHR_GOTO(hr, L"Failed to Create a Command object.", _ExitCreateAndSetCommand);
hr = pICommandText->SetCommandText(DBGUID_DBSQL, g_wCmdString);
CHKHR_GOTO(hr, L"Failed to Set Command Text.", _ExitCreateAndSetCommand);
hr = pICommandText->QueryInterface(IID_ICommandProperties, (void**) &pICommandProperties);
CHKHR_GOTO(hr, L"Failed to obtain ICommandProperties interface from the command object.", _ExitCreateAndSetCommand);
rgCmdProperties[0].dwPropertyID = DBPROP_ACCESSORDER;
rgCmdProperties[0].vValue.vt = VT_I4;
rgCmdProperties[0].vValue.lVal = DBPROPVAL_AO_SEQUENTIAL;
rgCmdProperties[0].dwOptions = DBPROPOPTIONS_REQUIRED;
rgCmdProperties[0].colid = DB_NULLID;
rgCmdPropSet[0].guidPropertySet = DBPROPSET_ROWSET;
rgCmdPropSet[0].cProperties = 1;
rgCmdPropSet[0].rgProperties = rgCmdProperties;
hr = pICommandProperties->SetProperties(1, rgCmdPropSet);
CHKHR_GOTO(hr, L"Failed to Set Command object Properties.", _ExitCreateAndSetCommand);
_ExitCreateAndSetCommand:
if (pICommandProperties)
{
pICommandProperties->Release();
pICommandProperties = NULL;
}
if (pIDBCreateCommand)
{
pIDBCreateCommand->Release();
pIDBCreateCommand = NULL;
}
if (pIDBCreateSession)
{
pIDBCreateSession->Release();
pIDBCreateSession = NULL;
}
if (FAILED(hr))
{
if (pICommandText)
{
pICommandText->Release();
pICommandText = NULL;
}
}
*ppICommandText = pICommandText;
return hr;
}
HRESULT ProcessResultSet(IRowset* pIRowset)
{
HRESULT hr;
IColumnsInfo* pIColumnsInfo = NULL;
DBCOLUMNINFO* pDBColumnInfo = NULL;
ULONG lNumCols = 0;
wchar_t* pStringsBuffer = NULL;
DBBINDING* pBindings = NULL;
DBOBJECT dbobj;
ULONG idxBinding;
IAccessor* pIAccessor = NULL;
HACCESSOR hAccessor = DB_NULL_HACCESSOR;
HROW hRows[1] = {DB_NULL_HROW};
HROW* pRow = &hRows[0];
BYTE* pBuffer = NULL;
ULONG lNumRowsRetrieved;
DBLENGTH dwOffset = 0;
hr = pIRowset->QueryInterface(IID_IColumnsInfo, (void **)&pIColumnsInfo);
CHKHR_GOTO(hr, L"Failed to QI Rowset for IColumnsInfo.", _ExitProcessResultSet);
hr = pIColumnsInfo->GetColumnInfo(&lNumCols, &pDBColumnInfo, &pStringsBuffer);
CHKHR_GOTO(hr, L"Failed to obtain Column Information.", _ExitProcessResultSet);
pBindings = new DBBINDING[lNumCols];
if (!pBindings)
{
hr = E_OUTOFMEMORY;
goto _ExitProcessResultSet;
}
memset(pBindings, 0, sizeof(DBBINDING) * lNumCols);
dbobj.dwFlags = STGM_READ;
dbobj.iid = IID_ISequentialStream;
for (idxBinding = 0; idxBinding < lNumCols; idxBinding++)
{
pBindings[idxBinding].iOrdinal = idxBinding + 1;
pBindings[idxBinding].obStatus = dwOffset;
pBindings[idxBinding].obLength = dwOffset + sizeof(DBSTATUS);
pBindings[idxBinding].obValue = dwOffset + sizeof(DBSTATUS) + sizeof(DBLENGTH);
pBindings[idxBinding].pTypeInfo = NULL;
pBindings[idxBinding].pBindExt = NULL;
pBindings[idxBinding].dwPart = DBPART_VALUE | DBPART_LENGTH | DBPART_STATUS;
pBindings[idxBinding].dwMemOwner = DBMEMOWNER_CLIENTOWNED;
pBindings[idxBinding].eParamIO = DBPARAMIO_NOTPARAM;
pBindings[idxBinding].bPrecision = pDBColumnInfo[idxBinding].bPrecision;
pBindings[idxBinding].bScale = pDBColumnInfo[idxBinding].bScale;
pBindings[idxBinding].cbMaxLen = 0;
pBindings[idxBinding].wType = DBTYPE_WSTR;
// Determine the maximum number of bytes required in our buffer to
// contain the Unicode string representation of the provider's native
// data type, including room for the NULL-termination character
switch( pDBColumnInfo[idxBinding].wType )
{
case DBTYPE_NULL:
case DBTYPE_EMPTY:
case DBTYPE_I1:
case DBTYPE_I2:
case DBTYPE_I4:
case DBTYPE_UI1:
case DBTYPE_UI2:
case DBTYPE_UI4:
case DBTYPE_R4:
case DBTYPE_BOOL:
case DBTYPE_I8:
case DBTYPE_UI8:
case DBTYPE_R8:
case DBTYPE_CY:
case DBTYPE_ERROR:
// When the above types are converted to a string, they
// will all fit into 25 characters, so use that plus space
// for the NULL-terminator.
pBindings[idxBinding].cbMaxLen = (25 + 1) * sizeof(WCHAR);
break;
case DBTYPE_DECIMAL:
case DBTYPE_NUMERIC:
case DBTYPE_DATE:
case DBTYPE_DBDATE:
case DBTYPE_DBTIMESTAMP:
case DBTYPE_GUID:
// Converted to a string, the above types will all fit into
// 50 characters, so use that plus space for the terminator.
pBindings[idxBinding].cbMaxLen = (50 + 1) * sizeof(WCHAR);
break;
case DBTYPE_BYTES:
// In converting DBTYPE_BYTES to a string, each byte
// becomes two characters (e.g. 0xFF -> "FF"), so we
// will use double the maximum size of the column plus
// include space for the NULL-terminator.
pBindings[idxBinding].cbMaxLen = (pDBColumnInfo[idxBinding].ulColumnSize * 2 + 1) * sizeof(WCHAR);
break;
case DBTYPE_STR:
case DBTYPE_WSTR:
case DBTYPE_BSTR:
// Going from a string to our string representation,
// we can just take the maximum size of the column,
// a count of characters, and include space for the
// terminator, which is not included in the column size.
pBindings[idxBinding].cbMaxLen = (pDBColumnInfo[idxBinding].ulColumnSize + 1) * sizeof(WCHAR);
break;
default:
// For any other type, we will simply use our maximum
// column buffer size, since the display size of these
// columns may be variable (e.g. DBTYPE_VARIANT) or
// unknown (e.g. provider-specific types).
pBindings[idxBinding].cbMaxLen = MAX_COL_SIZE;
break;
}
// If the provider's native data type for this column is
// DBTYPE_IUNKNOWN or this is a BLOB column and the user
// has requested that we bind BLOB columns as ISequentialStream
// objects, bind this column as an ISequentialStream object if
// the provider supports our creating another ISequentialStream
// binding.
if(pDBColumnInfo[idxBinding].dwFlags & DBCOLUMNFLAGS_ISLONG)
{
pBindings[idxBinding].wType = DBTYPE_IUNKNOWN;
pBindings[idxBinding].cbMaxLen = sizeof(ISequentialStream*);
pBindings[idxBinding].pObject = (DBOBJECT *)CoTaskMemAlloc(sizeof(DBOBJECT));
if (!pBindings[idxBinding].pObject)
{
hr = E_OUTOFMEMORY;
goto _ExitProcessResultSet;
}
// Direct the provider to create an ISequentialStream
// object over the data for this column.
pBindings[idxBinding].pObject->iid = IID_ISequentialStream;
// We want read access on the ISequentialStream
// object that the provider will create for us
pBindings[idxBinding].pObject->dwFlags = STGM_READ;
}
// Ensure that the bound maximum length is no more than the
// maximum column size in bytes that we've defined.
pBindings[idxBinding].cbMaxLen = min(pBindings[idxBinding].cbMaxLen, MAX_COL_SIZE);
// Update the offset past the end of this column's data, so
// that the next column will begin in the correct place in
// the buffer.
dwOffset = pBindings[idxBinding].cbMaxLen + pBindings[idxBinding].obValue;
// Ensure that the data for the next column will be correctly
// aligned for all platforms, or, if we're done with columns,
// that if we allocate space for multiple rows that the data
// for every row is correctly aligned.
dwOffset = ROUNDUP(dwOffset);
}
hr = pIRowset->QueryInterface(IID_IAccessor, (void **) &pIAccessor);
CHKHR_GOTO(hr, L"Failed to obtain Accessor interface", _ExitProcessResultSet);
hr = pIAccessor->CreateAccessor(DBACCESSOR_ROWDATA,
lNumCols,
pBindings,
0,
&hAccessor,
NULL);
CHKHR_GOTO(hr, L"Failed to create Accessor", _ExitProcessResultSet);
for (idxBinding = 0; idxBinding < lNumCols; idxBinding++)
{
cout << pDBColumnInfo[idxBinding].pwszName << endl;
}
lNumRowsRetrieved = 0;
hr = pIRowset->GetNextRows(
NULL,
0,
1,
&lNumRowsRetrieved,
&pRow);
CHKHR_GOTO(hr, L"Failed to fetch a row from the rowset", _ExitProcessResultSet);
pBuffer = new BYTE[sizeof(DBSTATUS) + sizeof(DBLENGTH) + sizeof(IUnknown*)];
if (!pBuffer)
{
hr = E_OUTOFMEMORY;
goto _ExitProcessResultSet;
}
while(lNumRowsRetrieved && hr != DB_S_ENDOFROWSET)
{
memset(pBuffer, 0, sizeof(DBSTATUS) + sizeof(DBLENGTH) + sizeof(IUnknown*));
hr = pIRowset->GetData(hRows[0], hAccessor, pBuffer);
CHKHR_GOTO(hr, L"Failed to obtain row data", _ExitProcessResultSet);
for (idxBinding = 0; idxBinding < lNumCols; idxBinding++)
{
if (pBindings[idxBinding].wType == DBTYPE_IUNKNOWN)
{
BYTE pbBuff[3000];
ULONG cbNeeded = sizeof(pbBuff)/sizeof(BYTE);
ULONG cbRead;
ULONG cbReadTotal = 0;
ISequentialStream* pISequentialStream = NULL;
IUnknown* pIUnknown = *((IUnknown**)(pBuffer + pBindings[idxBinding].obValue));
pIUnknown->QueryInterface(IID_ISequentialStream, (void**)&pISequentialStream);
do
{
hr = pISequentialStream->Read(pbBuff, cbNeeded, &cbRead);
cbReadTotal += cbRead;
}
while (SUCCEEDED(hr) && hr != S_FALSE && cbRead == cbNeeded);
cout << "Total Bytes Read: " << cbReadTotal << endl;
pISequentialStream->Release();
pISequentialStream = NULL;
pIUnknown->Release();
pIUnknown = NULL;
}
}
pIRowset->ReleaseRows(1, pRow, NULL, NULL, NULL);
hr = pIRowset->GetNextRows(NULL,
0,
1,
&lNumRowsRetrieved,
&pRow);
CHKHR_GOTO(hr, L"Failed to fetch a row from the rowset.", _ExitProcessResultSet);
}
_ExitProcessResultSet:
pIRowset->ReleaseRows(1, pRow, NULL, NULL, NULL);
delete [] pBuffer;
if (pIAccessor)
{
if (hAccessor != DB_NULL_HACCESSOR)
{
pIAccessor->ReleaseAccessor(hAccessor, NULL);
}
pIAccessor->Release();
pIAccessor = NULL;
}
if (pBindings)
{
for (idxBinding = 0; idxBinding < lNumCols; idxBinding++)
{
if (pBindings[idxBinding].pObject)
CoTaskMemFree(pBindings[idxBinding].pObject);
}
}
delete [] pBindings;
CoTaskMemFree(pDBColumnInfo);
CoTaskMemFree(pStringsBuffer);
if (pIColumnsInfo)
{
pIColumnsInfo->Release();
pIColumnsInfo = NULL;
}
return hr;
}
SQL Server Native Client OLE DB 공급자가 큰 값 데이터 형식을 노출하는 방법은 BLOB 및 OLE 개체를 참조하십시오.
SQL Server Native Client ODBC 드라이버
SQL Server Native Client ODBC 드라이버는 ODBC SQL 데이터 형식을 받아들이거나 반환하는 ODBC API 함수에서 varchar(max), varbinary(max) and nvarchar(max) 형식을 SQL_VARCHAR, SQL_VARBINARY 및 SQL_WVARCHAR로 노출합니다.
열의 최대 크기를 보고할 때 드라이버는 다음 중 하나를 보고합니다.
정의된 최대 크기(예를 들어 varchar(2000) 열의 경우 2000) 또는
값 "무제한". varchar(max) 열의 경우 0과 같습니다.
표준 변환 규칙이 varchar(max) 열에 적용되므로 varchar(2000) 열에 유효한 모든 변환은 varchar(max) 열에도 유효합니다. nvarchar(max) 및 varbinary(max) 열의 경우도 마찬가지입니다.
다음은 큰 값 데이터 형식 작업을 위해 향상된 ODBC API 함수 목록입니다.