Share via


Creating a Rowset

The session contains the interface that enables consumers to open a data file and create a rowset object containing all rows in the data file. The sample provider implements IOpenRowset on the session to create this rowset. The following section describes this interface.

Instantiating and Exposing a Rowset

The IOpenRowset interface contains a single method: OpenRowset. IOpenRowset is a required interface on the session. IOpenRowset::OpenRowset can be used by providers that do not support command objects to generate a rowset of all rows in a table or index. This provides a way for consumers to open and manipulate individual tables or indexes in a database with relatively low resource overhead. In the sample provider implementation, IOpenRowset accepts a file name as the name of the table and concatenates that with the directory name provided through IDBProperties::SetProperties. IOpenRowset uses the directory name and file name to instantiate a rowset and expose the rowset to the consumer.

The source code for the IOpenRowset::OpenRowset method follows; you can find the complete source code for the IOpenRowset interface in OpnRowst.cpp.

// CImpIOpenRowset::OpenRowset ------------------------------------------------
//
// @mfunc Opens and returns a rowset that includes all rows from a single base table
//
// @rdesc HRESULT
//      @flag S_OK                  | The method succeeded
//      @flag E_INVALIDARG          | pTableID was NULL
//      @flag E_FAIL                | Provider-specific error
//      @flag DB_E_NOTABLE          | Specified table does not exist in current Data
//                                  | Data Source object
//      @flag E_OUTOFMEMORY         | Out of memory
//      @flag E_NOINTERFACE         | The requested interface was not available

STDMETHODIMP CImpIOpenRowset::OpenRowset
    (
    IUnknown*                   pUnkOuter,          //@parm IN    | Controlling unknown, if any
    DBID*                       pTableID,           //@parm IN    | table to open
   DBID*                  pIndexID,         //@parm IN     | DBID of the index
    REFIID                      riid,               //@parm IN    | interface to return
    ULONG                       cPropertySets,      //@parm IN    | count of properties
    DBPROPSET               rgPropertySets[],   //@parm INOUT | array of property values
    IUnknown**                  ppRowset            //@parm OUT   | where to return interface
    )
{
   CCommand*            pCCommand = NULL;
   ICommandText*         pICmdText = NULL;
   ICommandProperties*      pICmdProp = NULL;
    CRowset*    pRowset = NULL;

    HRESULT     hr;
   HRESULT      hrProp   = ResultFromScode( S_OK );
   ULONG      ul,ul2;

    assert( m_pObj );
   assert( m_pObj->m_pUtilProp );

    // NULL out-params in case of error
    if( ppRowset )
       *ppRowset = NULL;

    // Check Arguments
   if ( !pTableID && !pIndexID )
        return ResultFromScode( E_INVALIDARG );

   if ( riid == IID_NULL)
        return ResultFromScode( E_NOINTERFACE );

   // Check Arguments for use by properties
   if( cPropertySets ) 
   {
      hr = m_pObj->m_pUtilProp->SetPropertiesArgChk(cPropertySets, 
                                         rgPropertySets);   
      if( FAILED(hr) )
         return hr;
   }

   // We only accept NULL for pIndexID
   if( pIndexID )
      return ResultFromScode( DB_E_NOINDEX );

   // If the eKind is not known to use, basically it
   // means we have no table identifier
   if ( (!pTableID ) || ( pTableID->eKind != DBKIND_NAME ) ||
      ( (pTableID->eKind == DBKIND_NAME) && (!(pTableID->uName.pwszName)) ) ||
      ( wcslen(pTableID->uName.pwszName) == 0 ) ||
      ( wcslen(pTableID->uName.pwszName) > _MAX_FNAME ) )
      return ResultFromScode( DB_E_NOTABLE );

   // We do not allow the riid to be anything other than IID_IUnknown for aggregation
   if ( (pUnkOuter) && (riid != IID_IUnknown) )
      return ResultFromScode( DB_E_NOAGGREGATION );
   
   // Create a Command Object
   // This is the outer unknown from the user, for the new Command,
   // not to be confused with the outer unknown of this DBSession object.
   pCCommand = new CCommand(m_pObj, NULL);
   if (pCCommand)
   {      
      hr = pCCommand->FInit();
      if( FAILED(hr) )
      {
         delete pCCommand;   // clean up.
         pCCommand = NULL;
      }
   }
   else
   {
      // Since Ctor failed, it cannot know to Release pUnkOuter, 
      // so we must do it here since we are owner.
      if (pUnkOuter)
         pUnkOuter->Release();
      hr = E_OUTOFMEMORY;
   }

   if( pCCommand )
   {
      // Since we are using the command objects interfaces to impersonate
      // doing IOpenRowset, we need to tell the command object to post errors
      // as IID_IOpenRowset
      pCCommand->SetImpersonateIID(&IID_IOpenRowset);


      // Allocate buffer for Table Name.
      if( FAILED(hr = pCCommand->QueryInterface(IID_ICommandText, (LPVOID*)&pICmdText)) )
      {
         assert(!"QI for ICmdText failed");
         goto EXIT;
      }
      assert( pICmdText ); 
      if( FAILED(hr = pICmdText->SetCommandText(DBGUID_DEFAULT, pTableID->uName.pwszName)) )
      {
         // Process Errors
         goto EXIT;            
      }            

      // Process Properties
      if( cPropertySets > 0 )
      {
         if( FAILED(hr = pCCommand->QueryInterface(IID_ICommandProperties, (LPVOID*)&pICmdProp)) )
         {
            assert(!"QI for ICmdProp failed");
            goto EXIT;
         }
         assert( pICmdProp );
         hr = (pICmdProp->SetProperties(cPropertySets, rgPropertySets));
         if( (hr == DB_E_ERRORSOCCURRED) || 
            (hr == DB_S_ERRORSOCCURRED) )
         {
            // If all the properties set were SETIFCHEAP then we can set 
            // our status to DB_S_ERRORSOCCURRED and continue.
            for(ul=0;ul<cPropertySets; ul++)
            {
               for(ul2=0;ul2<rgPropertySets[ul].cProperties; ul2++)
               {
                  // Check for a required property that failed, if found, we must return
                  // DB_E_ERRORSOCCURRED
                  if( (rgPropertySets[ul].rgProperties[ul2].dwStatus != DBPROPSTATUS_OK) &&
                     (rgPropertySets[ul].rgProperties[ul2].dwOptions != DBPROPOPTIONS_SETIFCHEAP) )
                  {
                     hr = ResultFromScode(DB_E_ERRORSOCCURRED);
                     goto EXIT;
                  }
               }
            }
            hrProp = DB_S_ERRORSOCCURRED;
         }
         else if( FAILED(hr) )
         {
            goto EXIT;
         }
      }

      // Open the rowset using ::Execute
      // ppRowset buffer.
      if( ppRowset )
      {
         hr = pICmdText->Execute(pUnkOuter, riid, NULL, NULL, ppRowset);         
                        
         // Sample Provider's property handling is simplistic.
         // All property errors should have been caught by the previous
         // call to ::SetProperties.
         // Assume that a failure was caused by something other
         // than bad properties.
         // So, no need for property post-processing
      }
   }

EXIT:
   if( pICmdText )
      pICmdText->Release();
   if( pICmdProp )
      pICmdProp->Release();

   // DB_S_ERRORSOCCURRED must take precedence over DB_S_NOTSINGLETON
   return (hr == S_OK || hr == DB_S_NOTSINGLETON) ? hrProp : hr;
}

See Also

Tasks

Writing an OLE DB Provider: An Introduction