Getting Large Data

In general, consumers should isolate code that creates a SQLOLEDB storage object from other code that handles data not referenced through an ISequentialStream pointer.

When the consumer indicates a storage object is one member of a row of data values, then the consumer limits its use of storage objects because SQLOLEDB can support only a single open object. In this case, the consumer can fetch only a single row of data in GetNextRows.

SQLOLEDB 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, 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
  // SQLOLEDB.
  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);
  }