Re: OLEDB errors when updating rows (that contain BLO B columns) using dynamic accessor

Mark Dawson <[email protected]> Mon, 1 Jul 2002 13:20:13 +0100
Newsgroups gmane.comp.windows.devel.oledb.devel
Message-ID <8F42A0DBFC56D6118CE100508B8B5A3F099AAE@SKYLLA>
Hi Orest,

The reason the SetData is causing a crash in this instance is because of the
way OLEDB providers handle BLOBs.  In the online docs it states that a
provider is responsible for releasing a 'client' BLOB object passed to it as
part of a SetData call (or a stored proc parameter for that matter).  This
means that in your original example where you simply called SetData on the
rowset returned by the provider, you were effectively passing that BLOB back
to the provider as a 'client' storage object.  SQLOLEDB was following the
spec and releasing that BLOB as part of the SetData call.  Your
CDynamicAccessor was then attempting to release it a second time with the
inevitable results.

The golden rule here is that after a successful SetData using BLOBs you
should always NULL the BLOB pointers you had in an accessor.  It's either
that or you explicitly AddRef so you can manage the lifetime yourself.

Hope that clears things up for you.
Mark
--------------------------------------------------------------
Mark Dawson ([email protected])
Software Project Leader
Schenck Test Automation Ltd.
Lowesmoor Wharf, Worcester.
WR1 2RS. UK
+ 44 (0)1905 613361


-----Original Message-----
From: Orest Kulik [mailto:[email protected]]
Sent: 01 July 2002 11:27
To: [email protected]
Subject: [OLEDB_DEV] OLEDB errors when updating rows (that contain BLOB
columns) using dynamic accessor


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.

_____________________________________________________________________
This message has been checked for all known viruses by UUNET delivered
through the MessageLabs Virus Control Centre. For further information visit
http://www.uk.uu.net/products/security/virus/

________________________________________________________________________
This email has been scanned for all viruses by the MessageLabs SkyScan
service. For more information on a proactive anti-virus service working
around the clock, around the globe, visit http://www.messagelabs.com
________________________________________________________________________

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