Undoes any changes made to a row since it was last fetched or IRowsetUpdate::Update was called for it.
HRESULT Undo ( HCHAPTER hReserved, DBCOUNTITEM cRows, const HROW rghRows[], DBCOUNTITEM *pcRows, HROW **prgRows, DBROWSTATUS **prgRowStatus);
Parameters
If rghRows includes a row that does not have any pending changes, IRowsetUpdate::Undo does not return an error. Instead, the row remains unchanged from its original state—which is the intention of IRowsetUpdate::Undo—and its row status is set to DBROWSTATUS_S_OK.
If rghRows includes a duplicate row, IRowsetUpdate::Undo treats the occurrences as if the row were passed to the method two times sequentially. Therefore, on the first occurrence, IRowsetUpdate::Undo undoes any pending changes. On the second occurrence, IRowsetUpdate::Undo treats the row as a row with no pending changes and leaves it in its current (now original) state.
If rghRows is a null pointer, the elements of this array are the handles of all the rows that had pending changes, whether or not IRowsetUpdate::Undo was successful at undoing those changes. The consumer checks *prgRowStatus to determine which rows were undone. When rghRows is a null pointer, IRowsetUpdate::Undo adds to the reference count of the rows it returns in *prgRows, because the consumer is not guaranteed to already have these row handles. A side effect of this is that rows with a reference count of zero, but with pending changes at the time IRowsetUpdate::Undo is called, are brought back into existence; that is, their reference count is increased to 1 and they must be rereleased.
The rowset allocates memory for the array of handles and returns the address to this memory; the consumer releases this memory with IMalloc::Free when it no longer needs the handles. This argument is ignored if pcRows is a null pointer and must not be a null pointer otherwise. If *pcRows is zero on output or the method fails with an error other than DB_E_ERRORSOCCURRED, the provider does not allocate any memory and ensures that *prgRows is a null pointer on output.
The rowset allocates memory for the row status values and returns the address to this memory; the consumer releases this memory with IMalloc::Free when it no longer needs the row status values. This argument is ignored if cRows is zero and pcRows is a null pointer. If IRowsetUpdate::Undo does not attempt to undo any rows or the method fails with an error other than DB_E_ERRORSOCCURRED, the provider does not allocate any memory and ensures that *prgRowStatus is a null pointer on output.
Return Code
The changes in the row were successfully undone. The corresponding element of *prgRowStatus contains DBROWSTATUS_S_OK.
pcRows was not a null pointer, and prgRows was a null pointer.
The rowset was single-chaptered, and the specified chapter was not the currently open chapter. The consumer must use the currently open chapter or release the currently open chapter before specifying a new chapter.
Comments
IRowsetUpdate::Undo backs any pending changes out of the specified rows and clears their pending change status. That is, it undoes any changes made to the row since it was last fetched or IRowsetUpdate::Update was called for the row. If multiple changes were made to a row, IRowsetUpdate::Undo undoes all of these changes; the provider does not remember intermediate steps. If IRowsetUpdate::Update is called for a row immediately after IRowsetUpdate::Undo is called for the row, Update does not transmit any changes to the data store for the row. For more information about pending changes, see "Changing Data" in Chapter 5: Updating Data in Rowsets.
How IRowsetUpdate::Undo works is best illustrated in the following examples. In the first example, IRowsetUpdate::Undo backs out any changes made since the row was last fetched from the data store:
In the second example, IRowsetUpdate::Undo backs out any changes made since values were last transmitted to the data store:
To implement IRowsetUpdate::Undo, the provider generally caches the original values just before making a change to a row and discards the cached values when IRowsetUpdate::Undo or IRowsetUpdate::Update is called for the row. This same cache can be used by IRowsetUpdate::GetOriginalData to retrieve the original data for the row.
If IRowsetUpdate::Undo is called for a row with a pending insert, the row is deleted from the rowset. That is, calls to IRowset::GetData or IRowsetChange::SetData for the row fail with DB_E_DELETEDROW. The consumer must still call IRowset::ReleaseRows to release the row.
Whether IRowsetUpdate::Undo can undo changes made to a COM object stored in a column or to a storage object created over a BLOB depends on the value of the DBPROP_DELAYSTORAGEOBJECTS rowset property.
If IRowsetUpdate::Undo is called for a row that has a reference count of zero and exists only because the row has a pending change, IRowsetUpdate::Undo releases the row and all its resources. The only exception to this is when the handle to the row is returned in *prgRows, in which case the reference count is set to one.
The order in which IRowsetUpdate::Undo processes rows is provider-specific. If Undo encounters an error, it continues processing rows until it has attempted to undo all specified rows and then returns the appropriate warning. Because IRowsetUpdate::Undo is usually implemented by copying data from a cache of original data, such errors should be extremely rare and should usually represent a consumer programming error, such as passing an invalid row handle.
If any consumer of the rowset is using notifications, the provider sends notifications that pending changes for the specified rows are being undone.
IRowsetChange | IRowsetUpdate::GetOriginalData | IRowsetUpdate::Update