Click here to Skip to main content
Click here to Skip to main content

Making ATL OLE DB Provider templates support updating of data - Part 2

, 20 Feb 2000
Rate this:
Please Sign up or sign in to vote.
The ATL OLE DB Provider templates only seem to support read-only rowsets and making them support updating of data isn't as easy as you'd expect!
  • Download the Simple Data Object with CCE update support - 58 Kb

    Client Cursor Engine Updates

    It turns out that supporting updates through the client cursor engine is relatively easy. Discovering that it's relatively easy was extremely difficult. An article that was recently added to the MSDN gives complete and full information on what a rowset needs to support for updates via the CCE to be possible. Before that article was published you had to rely on guess work to find out how to do it... By the way, if anyone from Microsoft reads this, the client cursor engine article really rocks, we need more articles with that level of technical information.

    A9645971-91EE-11D1-9251-00C04FBBBFB3

    I must have spent around 6 months trying to get client cursor updates to work. I tried adding  IRowsetUpdate, an obvious choice, but completely unnecessary. I tried adding all manner of other interfaces, guessing at random and failing completely. The main problem with client cursor updates is that at the point when the dreaded "Insufficient base table information for updating or refreshing" message is issued your OLE DB provider's session object is queried for an undocumented interface. The IID is {A9645971-91EE-11D1-9251-00C04FBBBFB3} and according to a Microsoft support person this interface is the subject of a documentation bug which relates to updating providers that do not support SQL. 

    So, what's the secret

    It turns out that all you need to do to get rid of the "Insufficient..." error message is to implement IColumnsRowset which isn't too hard as most of the required information can be obtained from a call to IColumnsInfo::GetColumnInfo which is supported by the ATL templates. The main issues with the implementation of IColumnsRowset are that GetAvailableColumns must return at least the following three optional meta data columns:

      <LI><CODE>DBCOLUMN_BASETABLENAME
    • DBCOLUMN_BASECOLUMNNAME
    • DBCOLUMN_KEYCOLUMN

    and that GetColumnsRowset must return a rowset where these optional meta data columns are present and contain valid information.

    In our current situation the table name is not really relevant, so we can fill in any old name and the base column names can be the same as the column names that IColumnsInfo reports.

    Once this is done we can add support to our updateable proxy and add the appropriate rowset property and the "Insufficient..." error is no more. Unfortunately we have yet to actually fix the problem.

    Once a rowset supports IColumnsRowset the client cursor engine has enough information to issue insert, update and delete requests to the rowset in SQL that is written in terms of the 'base table' information. That is it knows the real names of the columns and tables involved in an update and can write an update statement that can deal with rowsets that are the result of joins or data shape commands on an underlying data source. Unfortunately for us this means we are required to support SQL for updates, inserts and deletes.

    The resulting SQL arrives at our provider as a command. If you create a table, obtain an ADO recordset from it with client side cursors and optimistic locking then a a breakpoint set in CConversionProviderCommand::Execute will be hit as soon as you change data in the Data Grid and tab away from the cell. The value of m_strCommandText will be the SQL, something along the lines of:

       update table set Col1 = ? where Col1 = ? and
       Col2 = ? and Col3 = ? and Col4 = ?

    The question marks are place holders for values that have been passed to us in the DBPARAMs accessor. This is because we support ICommandWithParameters (which we need to for our conversion to an ADO recordset). If we didn't support ICommandWithParameters then the SQL would hold the values in the text string itself.

      update table set Col1 = 'a' where Col1 = '1'
       and Col2 = '2' and Col3 = '3' and Col4 = '4'

    The reason for the painful where clause is that our data object doesn't have any key columns. The CCE wont use a bookmark column as a key column so unless your data source has a valid unique key column and the column flags are set to include DBCOLUMNFLAGS_ISROWID inside of your data object's GetColumnInformation method then the where clause will be written so that all columns are used to identify the row that needs to be changed.

    An exercise for the reader...

    I'm afraid that's where I'm going to leave this particular problem. Parsing the SQL is relatively straight forward as the statements will only ever be generated by the CCE and should remain in a consistent form. Identifying the rowset that your command is to manipulate can be done by retaining a mapping table within the provider and using the key into the mapping table as the base table name returned from a call to GetColumnsRowset for a particular rowset object. The row to be modified is easily located if your data source has a unique key and more laboriously located if not (it's a pity that the CCE can't be made to use the bookmark column if no other unique key is present...). Once you have your row handles you need to create a copy of the accessor that you're passed into your command in the context of the rowset and then call the methods in IRowsetChange to do the actual work.

    I may be tempted to write another article which covers this final piece of work...

    The source was built using Visual Studio 6.0 SP3. Using the July edition of the Platform SDK. If you don't have the Platform SDK installed then you may find that the compile will fail looking for "msado15.h". You can fix this problem by creating a file of that name that includes "adoint.h". 

    Please send any comments or bug reports to me via email. For any updates to this article, check my site here.

  • License

    This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

    About the Author

    Len Holgate
    Software Developer (Senior) JetByte Limited
    United Kingdom United Kingdom
    Len has been programming for over 30 years, having first started with a Sinclair ZX-80. Now he runs his own consulting company, JetByte Limited and has a technical blog here.
     
    JetByte provides contract programming and consultancy services. We can provide experience in COM, Corba, C++, Windows NT and UNIX. Our speciality is the design and implementation of systems but we are happy to work with you throughout the entire project life-cycle. We are happy to quote for fixed price work, or, if required, can work for an hourly rate.
     
    We are based in London, England, but, thanks to the Internet, we can work 'virtually' anywhere...
     
    Please note that many of the articles here may have updated code available on Len's blog and that the IOCP socket server framework is also available in a licensed, much improved and fully supported version, see here for details.

    Comments and Discussions

     
    QuestionMSAccess row insertion problem using OLEDB Pinmemberna.nu13-Jul-06 19:58 
    QuestionUpdated MSDN URL? PinmemberKees Lairstra24-Jun-01 9:30 
    Generali'm sorry. This is test. PinmemberAlexey Semenov16-Jan-01 4:20 

    General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

    Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

    | Advertise | Privacy | Mobile
    Web04 | 2.8.140709.1 | Last Updated 21 Feb 2000
    Article Copyright 2000 by Len Holgate
    Everything else Copyright © CodeProject, 1999-2014
    Terms of Service
    Layout: fixed | fluid