Getting Large Data
Applies to: SQL Server Azure SQL Database Azure SQL Managed Instance Azure Synapse Analytics Analytics Platform System (PDW)
In general, consumers should isolate code that creates an OLE DB Driver for SQL Server storage object from other code that handles data that is not referenced through an ISequentialStream interface pointer.
This article refers to functionality available with the following functions:
IRowset:GetData
IRow::GetColumns
ICommand::Execute
The consumer should fetch only a single row of data in a call to the GetNextRows method when the DBPROP_ACCESSORDER property, in the rowset property group, is set to either DBPROPVAL_AO_SEQUENTIAL or DBPROPVAL_AO_SEQUENTIALSTORAGEOBJECTS. This is because BLOB data is not buffered. If the value of DBPROP_ACCESSORDER is set to DBPROPVAL_AO_RANDOM, the consumer can fetch multiple rows of data in GetNextRows.
The OLE DB Driver for SQL Server does not retrieve large data from SQL Server until requested to do so by the consumer. The consumer should bind all short data in one accessor, and then use one or more temporary accessors to retrieve large data values as required.
Example
This example retrieves a large data value from a single column:
HRESULT GetUnboundData
(
IRowset* pIRowset,
HROW hRow,
ULONG nCol,
BYTE* pUnboundData
)
{
UINT cbRow = sizeof(IUnknown*) + sizeof(ULONG);
BYTE* pRow = new BYTE[cbRow];
DBOBJECT dbobject;
IAccessor* pIAccessor = NULL;
HACCESSOR haccessor;
DBBINDING dbbinding;
ULONG ulbindstatus;
ULONG dwStatus;
ISequentialStream* pISequentialStream;
ULONG cbRead;
HRESULT hr;
// Set up the DBOBJECT structure.
dbobject.dwFlags = STGM_READ;
dbobject.iid = IID_ISequentialStream;
// Create the DBBINDING, requesting a storage-object pointer from
// The OLE DB Driver for SQL Server.
dbbinding.iOrdinal = nCol;
dbbinding.obValue = 0;
dbbinding.obStatus = sizeof(IUnknown*);
dbbinding.obLength = 0;
dbbinding.pTypeInfo = NULL;
dbbinding.pObject = &dbobject;
dbbinding.pBindExt = NULL;
dbbinding.dwPart = DBPART_VALUE | DBPART_STATUS;
dbbinding.dwMemOwner = DBMEMOWNER_CLIENTOWNED;
dbbinding.eParamIO = DBPARAMIO_NOTPARAM;
dbbinding.cbMaxLen = 0;
dbbinding.dwFlags = 0;
dbbinding.wType = DBTYPE_IUNKNOWN;
dbbinding.bPrecision = 0;
dbbinding.bScale = 0;
if (FAILED(hr = pIRowset->
QueryInterface(IID_IAccessor, (void**) &pIAccessor)))
{
// Process QueryInterface failure.
return (hr);
}
// Create the accessor.
if (FAILED(hr = pIAccessor->CreateAccessor(DBACCESSOR_ROWDATA, 1,
&dbbinding, 0, &haccessor, &ulbindstatus)))
{
// Process error from CreateAccessor.
pIAccessor->Release();
return (hr);
}
// Read and process BLOCK_SIZE bytes at a time.
if (SUCCEEDED(hr = pIRowset->GetData(hRow, haccessor, pRow)))
{
dwStatus = *((ULONG*) (pRow + dbbinding.obStatus));
if (dwStatus == DBSTATUS_S_ISNULL)
{
// Process NULL data
}
else if (dwStatus == DBSTATUS_S_OK)
{
pISequentialStream = *((ISequentialStream**)
(pRow + dbbinding.obValue));
do
{
if (SUCCEEDED(hr =
pISequentialStream->Read(pUnboundData,
BLOCK_SIZE, &cbRead)))
{
pUnboundData += cbRead;
}
}
while (SUCCEEDED(hr) && cbRead >= BLOCK_SIZE);
pISequentialStream->Release();
}
}
else
{
// Process error from GetData.
}
pIAccessor->ReleaseAccessor(haccessor, NULL);
pIAccessor->Release();
delete [] pRow;
return (hr);
}