Fetches rows starting with the row specified by an offset from a bookmark.

   HWATCHREGION   hReserved1,
   HCHAPTER       hChapter,
   DBBKMARK       cbBookmark,
   const BYTE    *pBookmark,
   DBROWOFFSET    lRowsOffset,
   DBROWCOUNT     cRows,
   DBCOUNTITEM   *pcRowsObtained,
   HROW         **prghRows);


[in] Reserved for future use. Providers ignore this parameter.


[in] The chapter handle. For nonchaptered rowsets or to designate the root rowset, the caller must set hChapter to DB_NULL_HCHAPTER.


[in] The length in bytes of the bookmark. This must not be zero.


[in] A pointer to a bookmark that identifies the base row to be used. This can be a pointer to DBBMK_FIRST or DBBMK_LAST. If lRowsOffset is zero, the provider fetches this row first; otherwise, the provider skips this and subsequent rows up to the count specified in the offset and then fetches the following rows.


[in] The signed count of rows from the origin bookmark to the target row. Deleted rows that the provider has removed from the rowset are not counted in the skip. The first row fetched is determined by the bookmark and this offset. For example, if lRowsOffset is zero, the first row fetched is the bookmarked row; if lRowsOffset is 1, the first row fetched is the row after the bookmarked row; if lRowsOffset is ?1, the first row fetched is the row before the bookmarked row.

lRowsOffset can be a negative number only if the value of the DBPROP_CANSCROLLBACKWARDS property is VARIANT_TRUE.


[in] The number of rows to fetch. A negative number means to fetch backward. cRows can be a negative number only if the value of the DBPROP_CANFETCHBACKWARDS property is VARIANT_TRUE.

If cRows is zero, no rows are fetched; the fetch direction and the next fetch position are unchanged, and the provider performs no processing, returning immediately from the method invocation. Specifically, lRowsOffset is ignored in this situation.

If the provider does not discover any other errors, the method returns S_OK; whether the provider checks for any other errors is provider-specific.

See the Comments section for a full description of the semantics of lRowsOffset and cRows parameters.


[out] A pointer to memory in which to return the actual number of fetched rows. If the consumer has insufficient permission to fetch all rows, IRowsetLocate::GetRowsAt fetches all rows for which the consumer has sufficient permission and skips all other rows. If the method fails, *pcRowsObtained is set to zero.


[in/out] A pointer to memory in which to return an array of handles of the fetched rows. If *prghRows is not a null pointer on input, it must be a pointer to memory large enough to return the handles of the requested number of rows. If *prghRows is a null pointer on input, the rowset allocates memory for the row handles and returns the address to this memory. The consumer releases this memory with IMalloc::Free after it releases the row handles. If *prghRows was a null pointer on input and *pcRowsObtained is zero on output or if the method fails, the provider does not allocate any memory and ensures that *prghRows is a null pointer on output.


The method succeeded.


The following behavior is supported only on rowsets that set the DBPROP_BOOKMARKSKIPPED property to VARIANT_TRUE. If this property is VARIANT_FALSE, this return code is never returned.

lRowsOffset was zero and the row specified by *pBookmark was deleted or is no longer a member of the rowset, or the row specified by the combination of *pBookmark and lRowsOffset is a row to which the consumer does not have access rights. IRowsetLocate::GetRowsAt skipped that row. The full count of actual rows (cRows) will be met if there are enough rows available. The array of returned row handles does not have gaps for missing rows; the returned count is the number of rows actually fetched.

If a row is skipped, it is counted as one of the rows to be skipped for lRowsOffset. For example, if an offset of 1 is requested and the bookmark points to a row that is now missing, the offset is decremented by 1 and the provider begins by fetching the next row.

If this condition occurs along with another warning condition, the method returns the code for the other warning condition. Therefore, whenever a consumer receives the return code for another warning condition, it should check to see whether this condition occurred.


IRowsetLocate::GetRowsAt reached the start or the end of the rowset or chapter and could not fetch all of the requested rows because the count extended beyond the end. The number of rows actually fetched is returned in *pcRowsObtained; this will be less than cRows.

The rowset is being populated asynchronously, and no additional rows are available at this time. To determine whether additional rows may be available, the consumer should call IDBAsynchStatus::GetStatus or listen for the IDBAsynchNotify::OnStop notification.

lRowsOffset indicated a position either more than one row before the first row of the rowset or more than one row after the last row, and the provider was a version 2.0 or later provider. *pcRowsObtained is set to zero, and no rows are returned.


Fetching the number of rows specified in cRows would have exceeded the total number of active rows supported by the rowset. The number of rows that were actually fetched is returned in *pcRowsObtained. This condition can occur only when there are more rows available than can be handled by the rowset. Therefore, this condition never conflicts with those described in DB_S_ENDOFROWSET and DB_S_STOPLIMITREACHED, both of which imply that no more rows were available.


Fetching rows required further execution of the command, such as when the rowset uses a server-side cursor. Execution has been stopped because a resource limit has been reached. The number of rows that were actually fetched is returned in *pcRowsObtained.


A provider-specific error occurred.


cbBookmark was zero, or pBookmark was a null pointer.

pcRowsObtained or prghRows was a null pointer.


The provider was unable to allocate sufficient memory in which to instantiate the rows or return the row handles.


ITransaction::Commit or ITransaction::Abort was called, and the object is in a zombie state.


*pBookmark was invalid, incorrectly formed, or DBBMK_INVALID.

*pBookmark did not match any rows in the rowset. This includes the case when the row corresponding to the bookmark has been deleted and DBPROP_BOOKMARKSKIPPED was VARIANT_FALSE.

Note Note

Consumers should attempt to use only bookmarks that they have received from the provider. The provider is guaranteed to handle only bookmarks it gives out in a predictable manner. Attempting to use a random value as a bookmark is undefined; the provider may return DB_E_BADBOOKMARK, may return an unexpected row, or may terminate abnormally.


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.


lRowsOffset indicated a position either more than one row before the first row of the rowset or more than one row after the last row, and the provider was a 1.x provider.


cRows was negative, and the rowset cannot fetch backward.


lRowsOffset was negative, and the rowset cannot scroll backward.


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.


The provider requires release of existing rows before new ones can be fetched. For more information, see DBPROP_CANHOLDROWS in Rowset Properties in Appendix C.


The consumer did not have sufficient permission to fetch any of the rows; no rows were fetched.

Given values for a bookmark, offset, and number of rows to fetch:

  • The provider determines a starting position for the fetch as the bookmark plus the offset.

  • If the starting position is off either end of the rowset, the method returns DB_S_ENDOFROWSET.

  • If the starting position is in the rowset, the provider fetches rows in the specified direction until it has fetched the requested number of rows or hits the end of the rowset.

  • If the provider fetches the requested number of rows, it returns S_OK. If it hits the end of the rowset, it stops fetching, returns DB_S_ENDOFROWSET, and returns the rows it has fetched.

  • The rows are returned in rowset traversal order; that is, the direction in which they were fetched. For example, if the consumer passes the bookmark for row 10 to IRowsetLocate::GetRowsAt and specifies cRows equal to ?5 and lRowsOffset equal to 0, IRowsetLocate::GetRowsAt returns rows 10 through 6. That is, *prghRows[0] is the handle for row 10 and *prghRows[4] is the handle for row 6.

The following table defines the behavior of IRowsetLocate::GetRowsAt for all combinations of these parameters.




Total rows in the rowset.


Row in the rowset specified by the bookmark parameters cbBookmark and pBookmark. Assume 1 = B = N, DBBMK_FIRST is equivalent to B = 1, and DBBMK_LAST is equivalent to B = N.





B + F



Return code

B + F < 1 or B + F > N




1 = B + F = N




1 = B + F = N

1 = r = N ? B ? F + 1



1 = B + F = N

r > N ? B ? F + 1

N ? B ? F + 1


1 = B + F = N

r < 0 and abs(r) = B + F



1 = B + F = N

r < 0 and abs(r) > B + F

B + F


Consumers that want to fetch forward?for example, 20 rows at a time?can use the call IRowsetLocate::GetRowsAt( ..., B, 1, 20, ...), which allows for an easy check when reaching the end of the rowset. Similarly, consumers that want to fetch backward can use the call IRowsetLocate::GetRowsAt(..., B, ? 1, ? 20, ...) to facilitate the detection of the beginning of the rowset. In the latter case, DBPROP_CANFETCHBACKWARDS must be set to VARIANT_TRUE.

The following conditions apply to hChapter and pBookmark:

  • hChapter defines which rows are retrieved. Rows not in the chapter are skipped.

  • If DBPROP_BOOKMARKSKIPPED is false, pBookmark must identify a row in the chapter. If pBookmark identifies a row not in the chapter, the provider returns DB_E_BADBOOKMARK.

  • If DBPROP_BOOKMARKSKIPPED is true, it is possible for pBookmark to identify a row not in the chapter. IRowsetLocate::GetRowsAt starts from that row, returns the next rows that are in the chapter, and returns DB_S_BOOKMARKSKIPPED.

The DBPROP_BOOKMARKSKIPPED property defines the behavior for cases in which there is no exact match for the bookmark pointed to by pBookmark. If providers support DBPROP_BOOKMARKSKIPPED and chapters, they need to be able to determine where in a chapter a bookmark would positioned a row. If this property is set to VARIANT_FALSE, DB_E_BADBOOKMARK will be returned because the corresponding position is not well defined or the rows are not in the chapter. The behavior when this property is set to VARIANT_TRUE is as follows:

  • If lRowsOffset is zero, the row is skipped, the requested number of rows is fetched from subsequent rows, and a DB_S_BOOKMARKSKIPPED warning is returned.

  • If lRowsOffset and cRows are any value except zero, the fetch succeeds, based on the position of the row before it became invalid. For example, suppose row 20 is deleted. If a consumer calls IRowsetLocate::GetRowsAt with lRowsOffset set to 5 and a bookmark that points to row 20, GetRowsAt moves to row 25.

If the provider encounters a problem fetching a row?for example, data stored in a text file contains a letter in a numeric column?IRowsetLocate::GetRowsAt fetches the row normally, returns the row handle, and returns S_OK. However, when the consumer calls IRowset::GetData for the row, the provider returns DBSTATUS_E_CANTCONVERTVALUE as the status for the offending column.

For information about what IRowsetLocate::GetRowsAt does when it fetches a row that it already has in its internal buffers, see Uniqueness of Rows in the Rowset. For information about whether IRowsetLocate::GetRowsAt can detect changes made to rows in the rowset, see Visibility of Changes.

IRowsetLocate::GetRowsAt increments the reference count of each row for which it returns a handle by 1. Therefore, if a handle is returned for a row that has already been fetched, the reference count of that row will be greater than 1. IRowset::ReleaseRows must be called once for each time the handle to a row has been returned.

To use IRowsetLocate::GetRowsAt several times in sequence to obtain successive rows, the consumer should obtain the bookmark of the last row of the previous set and use that, with a skip of 1, to position for fetching the next rows.