IRowsetUpdate::Undo
Undoes any changes made to a row since it was last fetched or IRowsetUpdate::Update was called for it.
Syntax
HRESULT Undo (
HCHAPTER hReserved,
DBCOUNTITEM cRows,
const HROW rghRows[],
DBCOUNTITEM *pcRows,
HROW **prgRows,
DBROWSTATUS **prgRowStatus);
Parameters
hReserved
[in] Reserved for future use. Must be DB_NULL_HCHAPTER.cRows
[in] The count of rows to undo. If cRows is nonzero, IRowsetUpdate::Undo undoes all pending changes in the rows specified in rghRows. If cRows is zero, IRowsetUpdate::Undo ignores rghRows and undoes all pending changes to all rows in the rowset.rghRows
[in] An array of handles of the rows to undo. Elements of this array can refer to rows with pending deletes.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.
pcRows
[out] A pointer to memory in which to return the number of rows IRowsetUpdate::Undo attempted to undo. If this is a null pointer, no count of rows is returned. If the method fails with an error other than DB_E_ERRORSOCCURRED, *pcRows is set to zero.prgRows
[out] A pointer to memory in which to return an array containing the handles of all the rows IRowsetUpdate::Undo attempted to undo. If rghRows is not a null pointer, the elements of this array are in one-to-one correspondence with those in rghRows. For example, if a row appears twice in rghRows, it appears twice in *prgRows. When rghRows is not a null pointer, IRowsetUpdate::Undo does not add to the reference count of the rows it returns in *prgRows, because the consumer already has these row handles.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.
prgRowStatus
[out] A pointer to memory in which to return an array of row status values. The elements of this array correspond one-to-one with the elements of rghRows (if rghRows is not a null pointer) or *prgRows (if rghRows is a null pointer). If no errors occur while undoing a row, the corresponding element of *prgRowStatus is set to DBROWSTATUS_S_OK. If an error occurs while undoing a row, the corresponding element is set as specified in DB_S_ERRORSOCCURRED. If prgRowStatus is a null pointer, no row status values are returned.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
S_OK
The method succeeded. The changes in all rows were successfully undone. The following value can be returned in *prgRowStatus:The changes in the row were successfully undone. The corresponding element of *prgRowStatus contains DBROWSTATUS_S_OK.
DB_S_ERRORSOCCURRED
An error occurred while undoing the changes in a row, but the changes in at least one row were successfully undone. Successes can occur for the reasons listed under S_OK. The following errors can occur:Undoing the changes in a row was canceled during notification. Changes made to the row were not undone, and the corresponding element of rgRowStatus contains DBROWSTATUS_E_CANCELED.
A row handle in rghRows referred to a row for which a delete had been transmitted to the data store. The corresponding element of *prgRowStatus contains DBROWSTATUS_E_DELETED.
A row handle in rghRows was invalid. The corresponding element of *prgRowStatus contains DBROWSTATUS_E_INVALID.
A row handle in rghRows referred to a row on which a storage or other object was open. The corresponding element of *prgRowStatus contains DBROWSTATUS_E_OBJECTOPEN.
The consumer encountered a recoverable, provider-specific error, such as an RPC failure when transmitting the change to a remote server. The corresponding element of *prgRowStatus contains DBROWSTATUS_E_FAIL.
E_FAIL
A provider-specific error occurred.E_INVALIDARG
cRows was not 0, and rghRows was a null pointer.pcRows was not a null pointer, and prgRows was a null pointer.
E_OUTOFMEMORY
The provider was unable to allocate sufficient memory in which to return either the handles of the rows IRowsetUpdate::Undo attempted to undo or the array of row status values.E_UNEXPECTED
ITransaction::Commit or ITransaction::Abort was called, and the object is in a zombie state.DB_E_BADCHAPTER
The rowset was chaptered, and hReserved was invalid.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.
DB_E_ERRORSOCCURRED
Errors occurred while undoing all of the rows. The provider allocates memory for *prgRows and *prgRowStatus, and the consumer checks the values in *prgRowStatus to determine why the pending changes were not undone. The consumer frees this memory when it no longer needs the information. Errors can occur for the reasons listed under DB_S_ERRORSOCCURRED.DB_E_NOTREENTRANT
The provider called a method from IRowsetNotify in the consumer that had not yet returned, and the provider does not support reentrancy in this method.
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.
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:
The consumer fetches a row.
The consumer deletes the row with IRowsetChange::DeleteRows or updates value in it with IRowsetChange::SetData.
The consumer repeats step 2 as often as it likes for the row, except that after it calls IRowsetChange::DeleteRows for the row it cannot call either IRowsetChange::SetData or IRowsetChange::DeleteRows for the row.
The consumer calls IRowsetUpdate::Undo for the row. The values in the row are changed to those it had after completing step 1.
In the second example, IRowsetUpdate::Undo backs out any changes made since values were last transmitted to the data store:
The consumer fetches a row.
The consumer updates the value in a row with IRowsetChange::SetData.
The consumer repeats step 2 as often as it likes for the row.
The consumer calls IRowsetUpdate::Update for the row.
The consumer deletes the row with IRowsetChange::DeleteRows or updates the value in it with IRowsetChange::SetData.
The consumer repeats step 5 as often as it likes for the row, except that after it calls IRowsetChange::DeleteRows for the row it cannot call either IRowsetChange::SetData or IRowsetChange::DeleteRows for the row.
The consumer calls IRowsetUpdate::Undo for the row. The values in the row are changed to those it had after completing step 4.
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.