BLOBs as In-Memory Data

When a BLOB is treated as in-memory data, it is sent or retrieved in a single piece of memory. If the consumer allocates the memory for the BLOB, it usually binds the column with a type indicator of DBTYPE_BYTES, DBTYPE_STR, or DBTYPE_WSTR, depending on the data contained in the BLOB. When getting data, if the consumer wants the provider to allocate the memory for the BLOB, it usually binds the column with one of the preceding type indicators combined with DBTYPE_BYREF.

To get the BLOB data, the consumer calls a method such as IRowset::GetData with an accessor containing this binding, and the provider returns the entire contents of the BLOB to the consumer. To set the BLOB data, the consumer calls a method such as IRowsetChange::SetData with an accessor containing this binding and sends the entire contents of the BLOB to the provider. This is no different from binding and getting or setting data in other columns, such as getting or setting data in an integer column. If you get or set character data in this manner, the data is null terminated.

For example, the following code retrieves a 5000-byte BLOB and stores it in consumer memory:

#include<oledb.h>

IMalloc* pIMalloc;
IAccessor* pIAccessor;
IRowset* pIRowset;

int main() {
 HACCESSOR  hAccessor;
 DBBINDSTATUS rgStatus[1];
 DBBINDING  rgBinding[1] = {
  1,              // Column 1
  0,              // Offset to data
  5000,             // Offset to length 
  5004,             // Offset to status
  NULL,             // No type info
  NULL,             // Not an object 
  NULL,             // No binding extensions
  DBPART_VALUE|DBPART_LENGTH|DBPART_STATUS, // Bind value, length, status
  DBMEMOWNER_CLIENTOWNED,       // Memory owned by consumer
  DBPARAMIO_NOTPARAM,         // Not a parameter
  5000,             // Max length 
  0 ,             // Reserved
  DBTYPE_BYTES,           // Type DBTYPE_BYTES
  0,              // Precision not applicable
  0               // Scale not applicable
 };

 pIRowset->QueryInterface(IID_IAccessor, (void**) &pIAccessor);
 pIAccessor->CreateAccessor(DBACCESSOR_ROWDATA, 1, rgBinding, 
        (5000 + sizeof(ULONG) + sizeof(DBSTATUS)), &hAccessor, rgStatus);
 pIAccessor->Release();

 // Allocate memory for the in-memory data. The first 5000 bytes are for the data,
 // the next sizeof(ULONG) bytes are for the length, and the final sizeof(DBSTATUS)
 // bytes are for the status.

 void * pData = pIMalloc->Alloc(5000 + sizeof(ULONG) + sizeof(DBSTATUS));

 // Get the next row, get the data in that row, and process the data. Assume the
 // length of the data is known to be <=5000 bytes, so no need to check truncation.
 HROW* rghRows = NULL;
 ULONG cRows;

 pIRowset->GetNextRows(NULL, 0, 1, &cRows, &rghRows);
 pIRowset->GetData(rghRows[0], hAccessor, pData);


 if ((DBSTATUS)(((BYTE*) pData)[rgBinding[0].obStatus]) == DBSTATUS_S_ISNULL) {
  // Process NULL data
 } else if ((DBSTATUS)((BYTE*)pData)[rgBinding[0].obStatus] == DBSTATUS_S_OK) {
  // Process data.  Length is (ULONG)pData[rgBinding[0].obLength].
 }

 pIMalloc->Free(rghRows); 

};