Completing the Implementation of IRowsetLocateImpl

To complete your implementation of the IRowsetLocate interface, you must implement bookmarks. Bookmarks are indices into a rowset that allow high-speed access to data. The provider determines what bookmarks can uniquely identify a row. This example uses the index of the m_rgAgentInfo array.

The provider can receive a bookmark in any format. To make sure that the bookmark is valid:

  1. Call ValidateBookmark to check that you have a valid bookmark pointer.

  2. Check the size of the bookmark to make sure it is either the size of a DWORD or one byte. (The User Record, CAgentMan, specifies the dwBookmark entry to be a DWORD. The single-byte length is used for the special bookmarks DBMRK_FIRST and DBMRK_LAST.)

  3. Check that the index points to a valid location in the array (between DBMRK_FIRST and DBMRK_LAST).

After validating both bookmarks (one for each string), call the Compare method, which takes two bookmarks and determines if one is less than, greater than, or equal to the other.

To fetch rows, implement the GetRowsAt and GetRowsByBookmark functions.

GetRowsAt works the same as the IRowset::GetNextRows method except that it uses a bookmark to fetch the data. It validates the bookmark and then calls IRowsetImpl::GetNextRows to get the rows.

GetRowsByBookmark fetches one row at a number of locations. Use it to fetch specific rows. GetRowsByBookmark validates each of the bookmarks, then fetches the row by calling IRowsetImpl::CreateRow, which creates a new row, if necessary. The rows are returned in the array pointed to by rghRows.

(Because this example uses simple fixed-length bookmarks, it does not implement the IRowsetLocate::Hash method. If your provider uses variable-length bookmarks or computationally expensive bookmarks, you might want to implement the IRowsetLocate::Hash method as well.)

The complete implementation of IRowsetLocateImpl looks like this:

/////////////////////////////////////////////////////////////////////////// RowLoc.h
// class IRowsetLocateImpl

template <class T>
class ATL_NO_VTABLE IRowsetLocateImpl : public IRowsetImpl<T> 
{
public:
   STDMETHOD (Compare)(HCHAPTER hReserved, ULONG cbBookmark1, 
      const BYTE * pBookmark1, ULONG cbBookmark2, const BYTE * pBookmark2,
      DBCOMPARE * pComparison)
   {
      ATLTRACE("IRowsetLocateImpl::Compare");

      HRESULT hr = ValidateBookmark(cbBookmark1, pBookmark1);
      if (hr != S_OK)
         return hr;

      hr = ValidateBookmark(cbBookmark2, pBookmark2);
      if (hr != S_OK)
         return hr;

      // Return the value based on the bookmark values
      if (*pBookmark1 == *pBookmark2)
         *pComparison = DBCOMPARE_EQ;

      if (*pBookmark1 < *pBookmark2)
         *pComparison = DBCOMPARE_LT;

      if (*pBookmark1 > *pBookmark2)
         *pComparison = DBCOMPARE_GT;

      return S_OK;
   }

   STDMETHOD (GetRowsAt)(HWATCHREGION hReserved1, HCHAPTER hReserved2,
      ULONG cbBookmark, const BYTE * pBookmark, LONG lRowsOffset,
      LONG cRows, ULONG * pcRowsObtained, HROW ** prghRows)
   {
      ATLTRACE("IRowsetLocateImpl::GetRowsAt");
      T* pT = (T*)this;

      // Check bookmark
      HRESULT hr = ValidateBookmark(cbBookmark, pBookmark);
      if (hr != S_OK)
         return hr;

      // Check the other pointers
      if (pcRowsObtained == NULL || prghRows == NULL)
         return E_INVALIDARG;

      // Set the current row position to the bookmark.  Handle any
      // normal values
      pT->Lock();
      LONG iRowsetTemp = m_iRowset;  // Cache the current rowset 
      m_iRowset = *pBookmark;
      if (*pBookmark == DBBMK_FIRST)
         m_iRowset = 1;

      if (*pBookmark == DBBMK_LAST)
         m_iRowset = pT->m_rgRowData.GetSize();

      // Call IRowsetImpl::GetNextRows to actually get the rows.
      hr = GetNextRows(hReserved2, lRowsOffset,
         cRows, pcRowsObtained, prghRows);
      m_iRowset = iRowsetTemp;
      pT->Unlock();
      return hr;
   }

   STDMETHOD (GetRowsByBookmark)(HCHAPTER hReserved, ULONG cRows,
      const ULONG rgcbBookmarks[], const BYTE * rgpBookmarks[],
      HROW rghRows[], DBROWSTATUS rgRowStatus[])
   {
      HRESULT hr = S_OK;
      ATLTRACE("IRowsetLocateImpl::GetRowsByBookmark");

      T* pT = (T*)this;
      if (rgcbBookmarks == NULL || rgpBookmarks == NULL || rghRows == NULL)
         return E_INVALIDARG;

      if (cRows == 0)
         return S_OK;   // No rows fetched in this case.

      bool bErrors = false;
      pT->Lock();
      for (ULONG l=0; l<cRows; l++)
      {
         // Validate each bookmark before fetching the row.  Note, it is
         // an error for the bookmark to be one of the standard values
         hr = ValidateBookmark(rgcbBookmarks[l], rgpBookmarks[l]);
         if (hr != S_OK)
         {
            bErrors = TRUE;
            if (rgRowStatus != NULL)
            {
               rgRowStatus[l] = DBROWSTATUS_E_INVALID;
               continue;
            }
         }

         // Fetch the validated row
         ULONG ulRowsObtained = 0;
         if (CreateRow((long)*rgpBookmarks[l], ulRowsObtained, &rghRows[l]) != S_OK)
         {
            bErrors = TRUE;
         }
         else
         {
            if (rgRowStatus != NULL)
               rgRowStatus[l] = DBROWSTATUS_S_OK;
         }
      }

      pT->Unlock();
      if (bErrors)
         return DB_S_ERRORSOCCURRED;
      else
         return hr;
   }

   STDMETHOD (Hash)(HCHAPTER hReserved, ULONG cBookmarks,
      const ULONG rgcbBookmarks[], const BYTE * rgpBookmarks[],
      DWORD rgHashedValues[], DBROWSTATUS rgBookmarkStatus[])
   {
      ATLTRACENOTIMPL("IRowsetLocateImpl::GetRowsByBookmark");
   }

   // Implementation
   protected:
   HRESULT ValidateBookmark(ULONG cbBookmark, const BYTE* pBookmark)
   {
      T* pT = (T*)this;
      if (cbBookmark == 0 || pBookmark == NULL)
         return E_INVALIDARG;

      // All of our bookmarks are DWORDs, if they are anything other than 
      // sizeof(DWORD) then we have an invalid bookmark
      if ((cbBookmark != sizeof(DWORD)) && (cbBookmark != 1))
      {
         ATLTRACE("Bookmarks are invalid length, should be DWORDs");
         return DB_E_BADBOOKMARK;
      }

      // If the contents of our bookmarks are less than 0 or greater than
      // rowcount, then they are invalid
      UINT nRows = pT->m_rgRowData.GetSize();
      if ((*pBookmark <= 0 || *pBookmark > nRows) 
         && *pBookmark != DBBMK_FIRST && *pBookmark != DBBMK_LAST)
      {
         ATLTRACE("Bookmark has invalid range");
         return DB_E_BADBOOKMARK;
      }

      return S_OK;
   }
};

In the next topic, you will see how to dynamically determine the columns returned to the consumer.