OLEDB errors when updating rows (that contain BLOB columns) using dynamic accessor

Orest Kulik <[email protected]> Mon, 1 Jul 2002 03:27:22 -0700
Newsgroups gmane.comp.windows.devel.oledb.devel
Message-ID <OLEDB_DEV%[email protected]>
DESCRIPTION:
I am having problems updating records when using dynamic accessor (from ATL
Consumer Templates), SQL Server, and BLOB data (text and ntext column
types).


PLATFORM AND LIBS:
- MS VC++ 7.0 (that has fixed dynamic accessor's BindColumns, GetStatus,
  GetLength, SetStatus and SetLength methods),
- ATL Consumer Templates,
- OLEDB SQL Data Provider (SQLOLEDB),
- SQL 2000 server SP2.


MY DATA SET:
I have created a single table in database that contains two columns: one
varchar(50) and one ntext (BLOB) column. I have only a single row of data.


PROBLEM:
I get access violation reading location xxx error when exiting main() proc
if I write something like:

CoInitialize(NULL);
{
CDataSource ds;
CSession sess;
// open data source and session ...
CCommand<CDynamicAccessor> cmd;
CDBPropSet propset(DBPROPSET_ROWSET);
propset.AddProperty(DBPROP_IRowsetChange,true);
propset.AddProperty(DBPROP_UPDATABILITY,(long)(DBPROPVAL_UP_CHANGE|
    DBPROPVAL_UP_INSERT |
    DBPROPVAL_UP_DELETE));
cmd.Open(sess,"select txHTML,vszNormal from HTML",&propset); // txHTML is
                                                             // BLOB
cmd.MoveFirst(); // I have some rows in my table, hr is S_OK always
HRESULT hr = cmd.SetData();
// close everything ...
}
CoUninitialize();

Notice that no data was changed, just SetData was called (which was
non-cached update). hr of SetData was E_UNEXPECTED (0x8000ffff).

However, if I do something like writing new data between MoveFirst and
SetData, everything works! Data gets updated and return value is S_OK.

I do this by (see below for details):
1. freeing ISequentialStream ptr obtained from provider (with additional
Release),
2. inserting directly into accessor buffer a new ptr to myself provided
ISequentialStream implementation.
Details of these method are not pretty but everything seems to work fine.

***********************************************************************
So my question is why Update does not work when no data is changed?
(if I use SELECT that contains only non BLOB columns, everything works)
***********************************************************************

SOME FURTHER RESEARCH:
Interestingly enough, this access violation reading location xxx error
happens when destructor of CDynamicAccessor is freeing its column data.
When it detects that column type was DBTYPE_IUNKNOWN it casts pointer to
a stream (from accessor buffer) like (atldbcli.h, FreeType function):

case DBTYPE_IUNKNOWN:
case DBTYPE_IDISPATCH:
if( (*(IUnknown**)pValue) != NULL )
{
    (*(IUnknown**)pValue)->Release();  // This is the place where everything
                                       // breaks up
    *(IUnknown**)pValue = NULL;
}

What happens , I vaguely presume, is that when accessor binds data from
provider to it's local buffer, AddRef is never called on ISequentialStream
ptr. This is why Release() fails because by the time accessors's destructor
comes to free it's data, SQL provider is gone and ptr to it's
ISequentialStream becomes invalid.
As I was describing earlier, when I wrote some my data to text column (using
my own ISequentialStream implementation), I had to use additional AddRef so
my app wouldn't break down. I could not imagine to what purpose should this
AddRef serve, except that it kept my app running.
Here is a shortened (error handling omitted) code that writes some new data
to BLOB column:

// ord is column ordinal, pszValue is some new wchar_t* data
ISequentialStream* pISequentialStream = NULL;
ISequentialStream ** ppISequentialStream = NULL;

ppISequentialStream = static_cast<ISequentialStream**>(GetValue(ord));
(*ppISequentialStream)->Release(); // Release the pointer given by the
                                   // provider
// Write updated data in our custom buffer
ULONG written;
CSeqStream* pSeqStream = new CSeqStream();
HRESULT hr = pSeqStream->Write((void*)pszValue,wcslen(pszValue)*2,&written);
hr = pSeqStream->QueryInterface(IID_ISequentialStream,
                                (void**)&pISequentialStream);
pISequentialStream->Release();
// Assign the data as ISequentialStream in the BLOB column
*ppISequentialStream = pISequentialStream;
(*ppISequentialStream)->AddRef(); // SUSPICIOUS AddRef
// set new column length and status
SetLength(ord,pSeqStream->Length());
SetStatus(ord,DBSTATUS_S_OK);

Is this an error with DynamicAccessor binding, or something completely
different?
Is my DBPROPSET_ROWSET wrong? I've read that SQL Server automatically turns
every forward-only, read-only cursor to dynamic cursor when fetching BLOB
data. How do I declare dynamic cursor?

I any of you have gotten this far, I sincerely congratulate.

Orest Kulik
Ekobit, HR

You can read messages from the OLEDB_DEV archive, unsubscribe from OLEDB_DEV,
or subscribe to other DevelopMentor lists at http://discuss.develop.com.