IRowsetUpdate::Undo

Undoes any changes made to a row since it was last fetched or Update was called for it.

HRESULT Undo (
   HCHAPTER            hChapter,
   ULONG               cRows,
   const HROW            rghRows[],
   ULONG *               pcRows,
   HROW **               prgRows,
   DBROWSTATUS **   prgRowStatus);

Parameters

hChapter

[in]
The chapter handle. For nonchaptered rowsets, hChapter is ignored.

cRows

[in]
The count of rows to undo. If cRows is nonzero, Undo undoes all pending changes in the rows specified in rghRows. If cRows is zero, 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, Undo does not return an error. Instead, the row remains unchanged from its original state—which is the intention of Undo—and its row status is set to DBROWSTATUS_S_OK.

If rghRows includes a duplicate row, Undo treats the occurrences as if the row were passed to the method two times sequentially. Thus, on the first occurrence, Undo undoes any pending changes. On the second occurrence, 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 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 Undo attempted to undo. If rghRows is not a null pointer, then 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, Undo does not add to the reference count of the rows it returns in *prgRows; the reason is that 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, regardless of whether Undo was successful at undoing those changes. The consumer checks *prgRowStatus to determine which rows were undone. When rghRows is a null pointer, Undo adds to the reference count of the rows it returns in *prgRows; the reason is that 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 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. For information about the DBROWSTATUS enumerated type, see "Arrays of Errors" in Chapter 13.

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 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:

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:

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 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 hChapter 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 consumer called this method while it was processing a notification, and it is an error to call this method while processing the specified DBREASON value.

Comments

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 Update was called for the row. If multiple changes were made to a row, Undo undoes all of these changes; the provider does not remember intermediate steps. If Update is called for a row immediately after Undo is called for the row, Update does not transmit any changes to the data source for the row. For more information about pending changes, see "Changing Data" in Chapter 5.

How Undo works is best illustrated in the following examples. In the first example, Undo backs out any changes made since the row was last fetched from the data source:

  1. The consumer fetches a row.

  2. The consumer deletes the row with IRowsetChange::DeleteRows or updates value in it with IRowsetChange::SetData.

  3. The consumer repeats step 2 as often as it likes for the row, except that after it calls DeleteRows for the row it cannot call either SetData or DeleteRows for the row.

  4. The consumer calls Undo for the row. The values in the row are changed to those it had after completing step 1.

In the second example, Undo backs out any changes made since values were last transmitted to the data source:

  1. The consumer fetches a row.

  2. The consumer updates the value in a row with SetData.

  3. The consumer repeats step 2 as often as it likes for the row.

  4. The consumer calls Update for the row.

  5. The consumer deletes the row with DeleteRows or updates the value in it with SetData.

  6. The consumer repeats step 5 as often as it likes for the row, except that after it calls DeleteRows for the row it cannot call either SetData or DeleteRows for the row.

  7. The consumer calls Undo for the row. The values in the row are changed to those it had after completing step 4.

To implement Undo, the provider generally caches the original values just before making a change to a row and discards the cached values when Undo or Update is called for the row. This same cache can be used by GetOriginalData to retrieve the original data for the row.

If Undo is called for a row with a pending insert, the row is deleted from the rowset. That is, calls to IRowset::GetData or SetData for the row fail with DB_E_DELETEDROW. The consumer must still call IRowset::ReleaseRows to release the row.

Whether Undo can undo changes made to an OLE 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 Undo is called for a row that has a reference count of zero and exists only because the row has a pending change, 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 Undo processes rows is provider-specific. If Undo encounters an error, it continues processing rows until it has attempted to undo all specified rows, then returns the appropriate warning. Because Undo is generally implemented by copying data from a cache of original data, such errors should be extremely rare and generally 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.

See Also

IRowsetChange, IRowsetUpdate::GetOriginalData, IRowsetUpdate::Update