Share via


Populating the Data Cache

IRowset::GetNextRows gets the next sequence of rows from the file and places them in the rowset's data cache. When GetNextRows is first called, it starts at the beginning of the file. After that, GetNextRows maintains information about its current position so that it can proceed forward from that position. The sample provider does not support rowsets with reversible direction. The sample provider does, however, support IRowset::RestartPosition, which repositions the next fetch position to the beginning of the file. The following is the source code for IRowset::GetNextRows.

Example

// CImpIRowset::GetNextRows -----------
//
// @mfunc Fetches rows in a sequential style, remembering the previous position
//
// @rdesc   Returns one of the following values:
//      @flag S_OK                      | Method Succeeded.
//      @flag DB_S_ENDOFROWSET          | Reached end of rowset.
//      @flag DB_E_CANTFETCHBACKWARDS   | cRows was negative and we cannot fetch backwards.
//      @flag DB_E_ROWSNOTRELEASED      | Must release all HROWs before calling GetNextRows.
//      @flag E_FAIL                    | Provider-specific error.
//      @flag E_INVALIDARG              | pcRowsObtained or prghRows was NULL.
//      @flag E_OUTOFMEMORY             | Out of Memory.
//      @flag OTHER                     | Other HRESULTs returned by called functions.
//
STDMETHODIMP CImpIRowset::GetNextRows
    (
    HCHAPTER    hChapter,        //@parm IN | The Chapter handle.
    DBROWOFFSET lRowOffset,      //@parm IN | Rows to skip before reading.
    DBROWCOUNT  cRows,           //@parm IN | Number of rows to fetch.
    DBCOUNTITEM *pcRowsObtained, //@parm OUT | Number of rows obtained.
    HROW        **prghRows       //@parm OUT | Array of Hrows obtained.
    )
{
    DBROWCOUNT      cRowsTmp;
    DBROWCOUNT      cSlotAlloc =0;
    DBROWCOUNT   irow, ih;
    DBROWCOUNT      cRowFirst, cRowLast;
    PROWBUFF   prowbuff;
    HRESULT      hr;
   BOOL      fCanHoldRows = FALSE;
   DBPROPIDSET   rgPropertyIDSets[1];
   ULONG      cPropertySets;
   DBPROPSET*   prgPropertySets;
   DBPROPID   rgPropId[1];


    // init out-params
   if ( pcRowsObtained )
      *pcRowsObtained = 0;

    // Check validity of arguments.
    if ( pcRowsObtained == NULL || prghRows == NULL )
        return ResultFromScode( E_INVALIDARG );

    // No-op case always succeeds.
    if ( cRows == 0 )
        return ResultFromScode( S_OK );

    // This implementation does not support scrolling backward.
    if ( cRows < 0 )
        return ResultFromScode( DB_E_CANTFETCHBACKWARDS );

    // This implementation does not support scrolling backward.
    if ( lRowOffset < 0 )
        return ResultFromScode( DB_E_CANTSCROLLBACKWARDS );

    // Get the value of the DBPROP_CANHOLDROWS property.
   rgPropertyIDSets[0].guidPropertySet   = DBPROPSET_ROWSET;
   rgPropertyIDSets[0].rgPropertyIDs   = rgPropId;
   rgPropertyIDSets[0].cPropertyIDs   = 1;
   rgPropId[0]                     = DBPROP_CANHOLDROWS;

    m_pObj->m_pUtilProp->GetProperties( 
                           PROPSET_ROWSET,
                           1, 
                           rgPropertyIDSets,
                           &cPropertySets,
                           &prgPropertySets );

   // Get the Prompt value.
   if( V_BOOL(&prgPropertySets->rgProperties->vValue) == VARIANT_TRUE )
      fCanHoldRows = TRUE;

   // Free the memory.
   SAFE_FREE(prgPropertySets[0].rgProperties);   
   SAFE_FREE(prgPropertySets);

    // Are there any unreleased rows?
    if( ((m_pObj->m_prowbitsIBuffer)->ArrayEmpty() != S_OK) && (!fCanHoldRows) )
        return ResultFromScode( DB_E_ROWSNOTRELEASED );

    // Is the cursor fully materialized (end-of-cursor condition)?
    if (m_pObj->m_dwStatus & STAT_ENDOFCURSOR)
        return ResultFromScode( DB_S_ENDOFROWSET );

    assert( m_pObj->m_rgbRowData );
    if (FAILED( m_pObj->Rebind((BYTE *) m_pObj->GetRowBuff( m_pObj->m_irowMin, TRUE ))))
        return ResultFromScode( E_FAIL );

    //
    // Fetch Data
    //
    if (lRowOffset)
        {
        // Calculate the new position.
        m_pObj->m_irowFilePos += lRowOffset;

        // Check if skip causes END_OF_ROWSET.
        if (m_pObj->m_irowFilePos > m_pObj->m_pFileio->GetRowCnt() ||
            m_pObj->m_irowFilePos <= 0)
            {
            m_pObj->m_dwStatus |= STAT_ENDOFCURSOR;
            return ResultFromScode( DB_S_ENDOFROWSET );
            }
        }

   if (FAILED( hr = GetNextSlots( m_pObj->m_pIBuffer, (ULONG)cRows, &cRowFirst )))
        return hr;

    cSlotAlloc = (ULONG)cRows;

    for (irow =1; irow <= cRows; irow++)
        {
      // Setup the row.
      prowbuff = m_pObj->GetRowBuff( cRowFirst + irow - 1, TRUE );
      memset(prowbuff->cdData, 0, m_pObj->m_cbRowSize);
      if (FAILED( m_pObj->Rebind((BYTE *) prowbuff)))
         return ResultFromScode( E_FAIL );

      // Get the Data from the File into the row buffer.
        if (S_FALSE == ( hr = m_pObj->m_pFileio->Fetch( m_pObj->m_irowFilePos + irow )))
            {
            m_pObj->m_dwStatus |= STAT_ENDOFCURSOR;
            break;
            }
        else
            {
            if (FAILED( hr ))
                return ResultFromScode( E_FAIL );
            }
        }

    cRowsTmp = (ULONG)(irow - 1); //Irow will be +1 because of For Loop.
    m_pObj->m_irowLastFilePos = m_pObj->m_irowFilePos;
    m_pObj->m_irowFilePos += cRowsTmp;


    //
    // Through fetching many rows of data
    //
    // Allocate row handles for client.
    // Note that we need to use IMalloc for this.
    //
    // Should only malloc cRowsTmp, instead of cRows.
    //
    // Modified to use IMalloc.
    // Should malloc cRows, since client will assume it is that big.
    //

    *pcRowsObtained = cRowsTmp;
    
   if ( *prghRows == NULL && cRowsTmp )
        *prghRows = (HROW *) PROVIDER_ALLOC( cRows*sizeof( HROW ));

    if ( *prghRows == NULL  && cRowsTmp )
        return ResultFromScode( E_OUTOFMEMORY );

    //
    // Fill in the status information: Length, IsNull.
    // May be able to wait until first call to GetData,
    // but have to do it sometime.
    //
    // Suggest keeping an array of structs of accessor info.
    // One element is whether accessor requires any status info or length info.
    // Then we could skip this whole section.
    //
    // Added check for cRowsTmp to MarkRows call.
    // Don't want to call if cRowsTmp==0.
    // (Range passed to MarkRows is inclusive, so can't specify marking 0 rows.)
    //
    // Note that SetSlots is a CBitArray member function -- not an IBuffer function.
    //
    // Bits are [0...n-1], row handles are [1...n].
    //
    // Cleanup. Row handles, bits, indices are the same [m....(m+n)], where m is some # >0,
    //
    // Added row-wise reference counts and cursor-wise reference counts.
    //

    // Set row handles, fix data length field and compute data status field.//
    m_pObj->m_cRows   = cRowsTmp;
    cRowLast = cRowFirst + cRowsTmp - 1;

    // Cleanup extra slots where no hRow actually was put..
    //  ** Because of less rows than asked for
    //  ** Because of temporary for for data transfer.
    if (cSlotAlloc > (cRowsTmp))
        if (FAILED( hr = ReleaseSlots( m_pObj->m_pIBuffer, cRowFirst + cRowsTmp, (cSlotAlloc - cRowsTmp))))
            return hr;

    for (irow = (LONG) (cRowFirst), ih =0; irow <= (LONG) cRowLast; irow++, ih++)
        {
        // Increment the rows-read count,
        // then store it as the bookmark in the very first DWORD of the row.
        prowbuff = m_pObj->GetRowBuff( irow, TRUE );

        // Insert the bookmark and its row number (from 1...n) into a hash table.
        // This allows us to quickly determine the presence of a row in mem, given the bookmark.
        // The bookmark is contained in the row buffer, at the very beginning.
        // Bookmark is the row number within the entire result set [1...num_rows_read].

        // This was a new Bookmark, not in memory,
        // so return to user (in *prghRows) the hRow we stored.
        prowbuff->ulRefCount++;
        prowbuff->pbBmk = (BYTE*) m_pObj->m_irowLastFilePos + ih + 1;
        m_pObj->m_ulRowRefCount++;

        (*prghRows)[ih] = (HROW) ( irow );
        }

    if (m_pObj->m_dwStatus & STAT_ENDOFCURSOR)
        return ResultFromScode( DB_S_ENDOFROWSET );
    else
        return ResultFromScode( S_OK );
}

See Also

Tasks

Writing an OLE DB Provider: An Introduction