BUG: Using ISequentialStream to Access Binary Data May Hang an OLE DB Consumer Application

ID: Q240394


The information in this article applies to:
  • Microsoft OLE DB Provider for SQL Server, version 7.0
  • Microsoft Visual C++, 32-bit Editions, version 6.0


SYMPTOMS

An application that binds a Binary Large Object (BLOB)/binary field to ISequentialStream objects may hang if ISequentialStream is not completely read and another query is performed on the same connection. The CPU usage shown in Windows NT taskbar will be at 100 percent.

To access BLOB/binary data, an application can use ISequentialStream to efficiently load data from a SQL Server database. However, any access to the data is blocked by the SQL Server OLE DB Provider (SQLOLEDB) connection object until ISequentialStream is completely read. This is problematic for OLE DB consumer applications that may not always want to read all data from a BLOB field, or where you may be fetching an ISequentialStream pointer and then not using it before executing the other query on the same connection.

This problem only occurs when the server-side cursors are being used.

NOTE: Releasing the ISequentialStream pointer is not sufficient to resolve this problem.


CAUSE

The application stops responding (hangs) because of a bug in SQL Server OLE DB provider. The provider waits for the application to read all incoming stream data including the binary data before it can run any further queries on the same connection/session.


RESOLUTION

You can use one of the following three workarounds to resolve this problem:

  • Flush the ISequentialStream object before running any further queries. For example:
    
    while(S_OK==pBlobData->Read (buff, sizeof(buff), &iread) && iread==sizeof(buff)); 
    Here pBlobData is the pointer to ISequentialStream object and buff is a BYTE buffer.


  • Don't set DBPROP_SERVERCURSOR to true.


  • Instead of using ISequentialStream (DBTYPE_IUNKNOWN), use a byte array (DBTYPE_BYTES) to access data in a BLOB column.



STATUS

Microsoft has confirmed this to be a bug in the Microsoft products listed at the beginning of this article.


MORE INFORMATION

To reproduce the problem, follow these steps:

  1. Create a console application in Visual C++.


  2. Create two ICommand objects, pICommand1 and pICommand2. pICommand1 selects at least one binary column.


  3. Call
    
    pICommand->Execute 
    to get an IRowset; for example, pIRowset1. Set the properties of the rowset that server cursors are used.


  4. Create an accessor (IAccessor) for this IRowset object.


  5. Call
    
    pIRowset1->GetNextRows 
    and then
    
    pIRowset1->GetData 


  6. Now, calling
    
    pICommand2->Execute 
    will cause your application to stop responding (hang).



To correct the problem, insert the following code between steps 5 and 6:

while(S_OK==pBlobData->Read (buff, sizeof(buff), &iread) && iread==sizeof(buff)); 
Here pBlobData is the pointer to ISequentialStream object and buff is a BYTE buffer.

If you are using the Visual C++ ATL Consumer templates, the code might resemble the following:

class CImageRecordAccessor
{
public:
	TCHAR m_field1[101];
	ISequentialStream* m_field2;
BEGIN_COLUMN_MAP(CImageRecordAccessor)
	COLUMN_ENTRY(1, m_field1)
	BLOB_ENTRY(2, IID_ISequentialStream, STGM_READ, m_field2)
END_COLUMN_MAP()
...
}
class CImageRecordset : public CCommand<CAccessor<CImageRecordAccessor> >
{...
   HRESULT OpenRowset()
   {
      CDBPropSet ps(DBPROPSET_ROWSET);
      ps.AddProperty(DBPROP_SERVERCURSOR, true);
      return CCommand<CAccessor<CImageRecordAccessor> >::Open(m_session, NULL, &ps);
   }
...

}	

void SomeFunction()
{...

   CDataSource ds;

   HRESULT hr;
		
   CDBPropSet	dbinit(DBPROPSET_DBINIT);

   dbinit.AddProperty(DBPROP_AUTH_USERID, OLESTR("joey"));
   dbinit.AddProperty(DBPROP_INIT_CATALOG, OLESTR("mydb"));
   dbinit.AddProperty(DBPROP_INIT_DATASOURCE, OLESTR("joeyserver"));
   dbinit.AddProperty(DBPROP_INIT_LCID, (long)1033);
   dbinit.AddProperty(DBPROP_INIT_PROMPT, (short)4);
   hr = ds.Open(_T("SQLOLEDB.1"), &dbinit);
	if (FAILED(hr))
		return;

   CSession sn;
   hr = sn.Open(ds);
	
   CImageRecrdset rs1,rs2;
   unsigned long iread;
   char buff[5000];	
   rs1.m_session = sn;

   rs1.OpenRowset();
   rs1.MoveNext();

// The following code solves the problem:
//	
// unsigned long iread;
// while(S_OK==rs1.m_field2->Read(buff, sizeof(buff), &iread) && iread==sizeof(buff)); 

   rs2.m_session = sn;

   rs2.OpenRowset();
   hr = rs2.MoveNext();
   hr = rs2.m_field2->Read(buff, sizeof(buff), &l);

   rs1.Close();
   rs2.Close();
} 


REFERENCES

For additional information, click the article number below to view the article in the Microsoft Knowledge Base:

Q190958 SAMPLE: AOTBLOB Read/Writes BLOB Using OLE DB Consumer Template

Additional query words: ATL consumer templates STGM_READ

Keywords : kbATL kbDatabase kbOLEDB kbProvider kbSQLServ kbConsumer kbGrpVCDB
Version : WINDOWS:7.0; winnt:6.0
Platform : WINDOWS winnt
Issue type : kbbug


Last Reviewed: October 22, 1999
© 2000 Microsoft Corporation. All rights reserved. Terms of Use.