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.