ROWSET.CPP
//-------------------------------------------------------------------- 
// Microsoft OLE DB Sample Provider 
// (C) Copyright 1994 - 1996 Microsoft Corporation.  All Rights Reserved. 
// 
// @doc 
// 
// @module ROWSET.CPP | CRowset object implementation 
// 
// 
 
// Includes ------------------------------------------------------------------ 
 
#include "headers.h" 
 
static const MAX_BITS = 1008; 
 
static const TYPE_CHAR = 1; 
static const TYPE_SLONG = 3; 
 
// Code ---------------------------------------------------------------------- 
 
// CRowset::CRowset ---------------------------------------------------------- 
// 
// @mfunc Constructor for this class 
// 
// @rdesc NONE 
// 
CRowset::CRowset 
    ( 
    LPUNKNOWN pUnkOuter         //@parm IN | Outer Unkown Pointer 
    ) 
{ 
 
    //  Initialize simple member vars 
    m_cRef          = 0L; 
    m_pUnkOuter     = pUnkOuter; 
 
    // Intialize buffered row count + pointers to allocated memory 
    m_pFileio           = NULL; 
    m_cRows             = 0; 
    m_irowFilePos       = 0; 
    m_irowLastFilePos   = 0; 
    m_cbRowSize         = 0; 
    m_cbHeapUsed        = 0; 
    m_irowMin           = 0; 
    m_ulRowRefCount     = 0; 
    m_rgdbcolinfo       = NULL; 
    m_pbHeap            = NULL; 
    m_rgdwDataOffsets   = NULL; 
    m_rgfIsSigned       = NULL; 
    m_rgcbLen           = NULL; 
    m_pextbufferAccessor= NULL; 
    m_pIBuffer          = NULL; 
    m_prowbitsIBuffer   = NULL; 
    m_pLastBindBase     = NULL; 
    m_rgbRowData        = NULL; 
    m_dwStatus          = 0; 
    m_pUtilProp         = NULL; 
m_pCreator= NULL; 
 
    //  Initially, NULL all contained interfaces 
    m_pIColumnsInfo                 = NULL; 
    m_pIRowset                      = NULL; 
    m_pIRowsetChange                = NULL; 
    m_pIAccessor                    = NULL; 
    m_pIRowsetInfo                  = NULL; 
 
    // Increment global object count. 
    OBJECT_CONSTRUCTED(); 
 
    return; 
} 
 
 
// CRowset::~CRowset --------------------------------------------------------- 
// 
// @mfunc Destructor for this class 
// 
// @rdesc NONE 
// 
CRowset:: ~CRowset 
    ( 
    void 
    ) 
{ 
    // Release buffer for column names (pointed to by m_rgdbcolinfo). 
    if (NULL != m_pbHeap) 
        VirtualFree((VOID *) m_pbHeap, 0, MEM_RELEASE ); 
 
    // Free pointers. 
    // (Note delete is safe for NULL ptr.) 
    delete [] m_rgdbcolinfo; 
    delete [] m_rgdwDataOffsets; 
    delete [] m_rgfIsSigned; 
    delete [] m_rgcbLen; 
    delete    m_prowbitsIBuffer; 
    delete    m_pUtilProp; 
 
    if (NULL != m_pIBuffer) 
        ReleaseSlotList( m_pIBuffer ); 
 
    // Free accessors. 
    // Each accessor is allocated via new/delete. 
    // We store an array of ptrs to each accessor (m_pextbufferAccessor). 
    if (NULL != m_pextbufferAccessor) 
        { 
        ULONG       hAccessor, hAccessorLast; 
        PACCESSOR   pAccessor; 
 
        m_pextbufferAccessor->GetFirstLastItemH( hAccessor, hAccessorLast ); 
        for (; hAccessor <= hAccessorLast; hAccessor++) 
            { 
            m_pextbufferAccessor->GetItemOfExtBuffer( hAccessor, &pAccessor ); 
            delete [] pAccessor; 
            } 
        delete m_pextbufferAccessor; 
        } 
 
    //  Free contained interfaces 
    delete m_pIColumnsInfo; 
    delete m_pIRowset; 
    delete m_pIRowsetChange; 
    delete m_pIAccessor; 
    delete m_pIRowsetInfo; 
 
    // free CFileio object 
    if (m_pFileio) 
        delete m_pFileio; 
 
// Decrement the DBSession Count.  GetSpecification is not possible anymore 
if( m_pCreator ) 
{ 
// Mark the session as not having an open rowset anymore 
m_pCreator->RowsetDestroyed(); 
m_pCreator->Release(); 
} 
 
    // Decrement global object count. 
    OBJECT_DESTRUCTED(); 
 
    return; 
} 
 
// CRowset::FInit ------------------------------------------------------------ 
// 
// @mfunc Initialize the rowset Object 
// 
// @rdesc Did the Initialization Succeed 
//      @flag  TRUE | Initialization succeeded 
//      @flag  FALSE | Initialization failed 
// 
BOOL CRowset::FInit 
    ( 
    CFileIO*    pCFileio        //@parm IN | pointer to Fileio object 
    ) 
{ 
    LPUNKNOWN   pIUnknown = (LPUNKNOWN) this; 
 
    if (m_pUnkOuter) 
        pIUnknown = m_pUnkOuter; 
 
    m_pFileio = pCFileio; 
 
    // Find # of columns in the result set. 
    m_cCols = m_pFileio->GetColumnCnt(); 
    if (m_cCols <= 0) 
        return FALSE; 
 
    //--------------------- 
    // Allocate info arrays 
    //--------------------- 
 
    // Heap for column names. 
    // Commit it all, then de-commit and release once we know size. 
    m_pbHeap = (BYTE *) VirtualAlloc( NULL, 
                                      MAX_HEAP_SIZE, 
                                      MEM_RESERVE | MEM_COMMIT, 
                                      PAGE_READWRITE ); 
    if (NULL == m_pbHeap) 
        return FALSE; 
 
    // Add an extra index, so we can be 1 based 
    m_rgdbcolinfo     = new DBCOLUMNINFO[m_cCols + 1]; 
    m_rgdwDataOffsets = new ULONG[m_cCols + 1]; 
    m_rgfIsSigned     = new BOOL[m_cCols + 1]; 
    m_rgcbLen         = new SDWORD[m_cCols + 1]; 
 
    if (   m_rgdbcolinfo == NULL 
        || m_rgdwDataOffsets == NULL 
        || m_rgfIsSigned == NULL 
        || m_rgcbLen == NULL) 
        return FALSE; 
 
    if (FAILED( GatherColumnInfo())) 
        return FALSE; 
 
    if (FAILED( CreateHelperFunctions())) 
        return FALSE; 
 
    //-------------------- 
    // Perform binding 
    //-------------------- 
    // Bind result set columns to the first row of the internal buffer. 
    // For each column bind it's data as well as length. Leave space for 
    // derived status info. 
    // Note that we could defer binding, but this way we can check for 
    // bad errors before we begin. 
    // We may need to bind again if going back and forth 
    // with GetNextRows. 
    assert( m_rgbRowData ); 
    if (FAILED( Rebind((BYTE *) GetRowBuff( m_irowMin )))) 
        return FALSE; 
 
    // allocate utility object that manages our properties 
    m_pUtilProp = new CUtilProp(); 
 
    // Allocate contained interface objects 
    // Note that our approach is simple - we always create *all* of the Rowset interfaces 
    // If our properties were read\write (i.e., could be set), we would need to 
    // consult properties to known which interfaces to create. 
    // Also, none of our interfaces conflict. If any did conflict, then we could 
    // not just blindly create them all. 
    m_pIColumnsInfo             = new CImpIColumnsInfo( this, pIUnknown ); 
    m_pIRowset                  = new CImpIRowset( this, pIUnknown ); 
    m_pIRowsetChange            = new CImpIRowsetChange( this, pIUnknown ); 
    m_pIAccessor                = new CImpIAccessor( this, pIUnknown ); 
    m_pIRowsetInfo              = new CImpIRowsetInfo( this, pIUnknown ); 
 
 
    // if all interfaces were created, return success 
    return (BOOL) (m_pIColumnsInfo && 
                   m_pIRowset && 
                   m_pIRowsetChange && 
                   m_pIAccessor && 
                   m_pIRowsetInfo); 
} 
 
 
// CRowset::QueryInterface --------------------------------------------------- 
// 
// @mfunc Returns a pointer to a specified interface. Callers use 
// QueryInterface to determine which interfaces the called object 
// supports. 
// 
// @rdesc HRESULT indicating the status of the method 
//      @flag S_OK | Interface is supported and ppvObject is set. 
//      @flag E_NOINTERFACE | Interface is not supported by the object 
//      @flag E_INVALIDARG | One or more arguments are invalid. 
// 
STDMETHODIMP CRowset::QueryInterface 
    ( 
    REFIID riid, 
    LPVOID * ppv 
    ) 
{ 
    if (NULL == ppv) 
        return ResultFromScode( E_INVALIDARG ); 
 
    //  Place NULL in *ppv in case of failure 
    *ppv = NULL; 
 
    //  This is the non-delegating IUnknown implementation 
    if (riid == IID_IUnknown) 
        *ppv = (LPVOID) this; 
    else if (riid == IID_IRowset) 
        *ppv = (LPVOID) m_pIRowset; 
    else if (riid == IID_IColumnsInfo) 
        *ppv = (LPVOID) m_pIColumnsInfo; 
    else if (riid == IID_IRowsetChange) 
        *ppv = (LPVOID) m_pIRowsetChange; 
    else if (riid == IID_IAccessor) 
        *ppv = (LPVOID) m_pIAccessor; 
    else if (riid == IID_IRowsetInfo) 
        *ppv = (LPVOID) m_pIRowsetInfo; 
 
 
    //  If we're going to return an interface, AddRef it first 
    if (*ppv) 
        { 
        ((LPUNKNOWN) *ppv)->AddRef(); 
        return ResultFromScode( S_OK ); 
        } 
    else 
        return ResultFromScode( E_NOINTERFACE ); 
} 
 
 
// CRowset::AddRef ----------------------------------------------------------- 
// 
// @mfunc Increments a persistence count for the object 
// 
// @rdesc Current reference count 
// 
STDMETHODIMP_( ULONG ) CRowset::AddRef 
     ( 
     void 
     ) 
{ 
    return ++m_cRef; 
} 
 
 
// CRowset::Release ---------------------------------------------------------- 
// 
// @mfunc Decrements a persistence count for the object and if 
// persistence count is 0, the object destroys itself. 
// 
// @rdesc Current reference count 
// 
STDMETHODIMP_( ULONG ) CRowset::Release 
     ( 
     void 
     ) 
{ 
    if (!--m_cRef) 
        { 
        delete this; 
        return 0; 
        } 
 
    return m_cRef; 
} 
 
// CRowset::CreateHelperFunctions -------------------------------------------- 
// 
// @mfunc Creates Helper classes that are needed to manage the Rowset Object 
// 
// @rdesc HRESULT 
//      @flag S_OK | Helper classes created 
//      @flag E_FAIL | Helper classes were not created 
// 
HRESULT CRowset::CreateHelperFunctions 
    ( 
    void 
    ) 
{ 
    //---------------------- 
    // Create helper objects 
    //---------------------- 
 
    // Bit array to track presence/absense of rows. 
    m_prowbitsIBuffer = new CBitArray; 
    if (m_prowbitsIBuffer == NULL || FAILED( m_prowbitsIBuffer->FInit( MAX_BITS, g_dwPageSize ))) 
        return ResultFromScode( E_FAIL ); 
 
    // List of free slots. 
    // This manages the allocation of sets of contiguous rows. 
    if (FAILED( InitializeSlotList( MAX_TOTAL_ROWBUFF_SIZE / m_cbRowSize, 
                         m_cbRowSize, g_dwPageSize, m_prowbitsIBuffer, 
                         &m_pIBuffer, &m_rgbRowData ))) 
        return ResultFromScode( E_FAIL ); 
 
    // Create the ExtBuffer array. 
    // This is an array of pointers to malloc'd accessors. 
    m_pextbufferAccessor = (LPEXTBUFFER) new CExtBuffer; 
    if (m_pextbufferAccessor == NULL || FAILED( m_pextbufferAccessor->FInit( 1, sizeof( PACCESSOR ), g_dwPageSize ))) 
        return ResultFromScode( E_FAIL ); 
 
    // Locate some free slots. 
    // Should be at very beginning. 
    // This tells us which row we will bind to: m_irowMin. 
    if (FAILED( GetNextSlots( m_pIBuffer, 1, &m_irowMin ))) 
        return ResultFromScode( E_FAIL ); 
 
    ReleaseSlots( m_pIBuffer, m_irowMin, 1 ); 
 
    return ResultFromScode( S_OK ); 
} 
 
 
// CRowset::GatherColumnInfo ------------------------------------------------- 
// 
// @mfunc Creates DBCOLINFO structures for each column in the result set. 
// 
// @rdesc HRESULT 
//      @flag S_OK | Column Info Obtained 
//      @flag E_FAIL | Problems getting Column Info 
// 
HRESULT CRowset::GatherColumnInfo 
    ( 
    void 
    ) 
{ 
    HRESULT         hr; 
    LPWSTR          lpwstr; 
    int             cchFree, cchWide; 
    DBCOLUMNINFO*   pcolinfo; 
    ULONG           dwOffset; 
    UWORD           icol; 
    SWORD           cbName; 
    SWORD           swCSVType; 
    UDWORD          cbColDef; 
    DWORD           dwdbtype; 
 
    //---------------------------------- 
    // Gather column info 
    //---------------------------------- 
 
    lpwstr  = (LPWSTR) m_pbHeap; 
    cchFree = MAX_HEAP_SIZE / 2; 
 
    pcolinfo = &m_rgdbcolinfo[1]; 
    dwOffset = offsetof( ROWBUFF, cdData ); 
 
    for (icol=1; icol <= m_cCols; icol++, pcolinfo++) 
        { 
        LPTSTR ptstrName; 
 
        // Get Column Names and Lengths 
        hr = m_pFileio->GetColumnName( icol, &ptstrName ); 
        if (FAILED( hr )) 
            return ResultFromScode( E_FAIL ); 
 
        // Store the Column Name in the Heap 
        if (cchFree) 
            { 
            cbName = lstrlen( ptstrName ); 
            if (cbName) 
                cchWide = MultiByteToWideChar( CP_ACP, MB_PRECOMPOSED, ptstrName, (int) cbName, lpwstr, cchFree ); 
            else 
                cchWide = 0; 
            pcolinfo->pwszName = lpwstr; 
            lpwstr += cchWide; 
 
            cchFree -= cchWide; 
            if (cchFree) 
                { 
                *lpwstr++ = 0; 
                cchFree--; 
                } 
            else 
                *(lpwstr - 1) = 0; 
            } 
        else 
            pcolinfo->pwszName = NULL; 
 
        // Get DataTypes and Precision 
        hr = m_pFileio->GetDataTypes( icol, 
                        &swCSVType, // CSV data type 
                        &cbColDef,  // Precision 
                        &m_rgfIsSigned[icol]    // Signed or Unsigned 
                        ); 
        if (FAILED( hr )) 
            return ResultFromScode( E_FAIL ); 
 
        // We use ordinal numbers for our columns 
        pcolinfo->columnid.eKind          = DBKIND_GUID_PROPID; 
        pcolinfo->columnid.uGuid.guid     = GUID_NULL; 
        pcolinfo->columnid.uName.ulPropid = icol; 
 
        // Determine the OLE DB type, for binding. 
        hr = GetInternalTypeFromCSVType( swCSVType,              // in 
                                         m_rgfIsSigned[icol],    // in 
                                         &dwdbtype );            // out, DBTYPE to show client 
        if (FAILED( hr )) 
            return ResultFromScode( E_FAIL ); 
 
        // Check for overflow of size. 
        m_rgcbLen[icol] = cbColDef; 
        m_rgcbLen[icol] = MIN( m_rgcbLen[icol], MAX_BIND_LEN ); 
 
        pcolinfo->iOrdinal= icol; 
        pcolinfo->wType= (DBTYPE) dwdbtype; 
        pcolinfo->pTypeInfo     = NULL; 
        pcolinfo->ulColumnSize  = m_rgcbLen[icol]; 
        pcolinfo->bPrecision    = (BYTE)cbColDef; 
        pcolinfo->bScale        = 0; 
        pcolinfo->dwFlags       = 0; 
 
        // Is it a fixed length datatype 
        if(pcolinfo->wType != DBTYPE_STR) 
        pcolinfo->dwFlags |= DBCOLUMNFLAGS_ISFIXEDLENGTH; 
 
        // We do support nulls 
        pcolinfo->dwFlags |= DBCOLUMNFLAGS_ISNULLABLE; 
 
        //We should always be able to write to the column 
        pcolinfo->dwFlags |= DBCOLUMNFLAGS_WRITE; 
 
        // Set the offset from the start of the row, 
        // for this column, then advance past. 
        dwOffset = ROUND_UP( dwOffset, COLUMN_ALIGNVAL ); 
        m_rgdwDataOffsets[icol] = dwOffset; 
        dwOffset += offsetof( COLUMNDATA, bData ) + m_rgcbLen[icol]; 
        } 
 
    m_cbRowSize = ROUND_UP( dwOffset, COLUMN_ALIGNVAL ); 
    m_cbHeapUsed = MAX_HEAP_SIZE - 2*cchFree; 
 
    // Decommit unused memory in our column-name buffer. 
    // We know it will never grow beyond what it is now. 
    // Decommit all pages past where we currently are. 
    BYTE  *pDiscardPage; 
    ULONG  ulSize; 
    pDiscardPage = (BYTE *) ROUND_UP( lpwstr, g_dwPageSize ); 
    ulSize       = MAX_HEAP_SIZE - (pDiscardPage - m_pbHeap); 
    if (ulSize > 0) 
        VirtualFree( pDiscardPage, ulSize, MEM_DECOMMIT ); 
    assert( '\0' == (*lpwstr = '\0'));  // We shouldn't generate a mem fault. 
 
    return ResultFromScode( S_OK ); 
 
} 
 
// CRowset::Rebind -------------------------------------------- 
// 
// @mfunc Establishes data offsets and type for the file 
// routines to place the data 
// 
// @rdesc HRESULT 
//      @flag S_OK | Bindings set fine 
//      @flag E_FAIL | Bindings could not be set 
// 
HRESULT CRowset::Rebind 
    ( 
    BYTE *pBase                 //@parm IN | Base pointer for Data Area 
    ) 
{ 
    UWORD       icol; 
    COLUMNDATA  *pColumn; 
 
    // Bind result set columns. 
    // Use established types and sizes and offsets. 
    // Bind to internal row buffer, area beginning with 'pRowBuff'. 
    // 
    // For each column, bind it's data as well as length. 
    // Offsets point to start of COLUMNDATA structure. 
 
    assert( pBase ); 
    assert( m_rgdwDataOffsets ); 
    assert( m_rgcbLen ); 
 
    // Optimize by not doing it over again. 
    if (pBase != m_pLastBindBase) 
        { 
        m_pLastBindBase = 0; 
 
        for (icol=1; icol <= m_cCols; icol++) 
            { 
            pColumn = (COLUMNDATA *) (pBase + m_rgdwDataOffsets[icol]); 
            if (FAILED( m_pFileio->SetColumnBind( icol, pColumn, m_rgcbLen[icol] ))) 
                return ResultFromScode( E_FAIL ); 
            } 
 
        // Remember in case we bind to same place again. 
        m_pLastBindBase = pBase; 
        } 
 
    return ResultFromScode( S_OK ); 
} 
 
 
// CRowset::GetRowBuff-------------------------------------------- 
// 
// @mfunc Shorthand way to get the address of a row buffer. 
// Later we may extend this so that it can span several 
// non-contiguous blocks of memory. 
// 
// @rdesc Pointer to the buffer. 
// 
ROWBUFF* CRowset::GetRowBuff 
    ( 
    ULONG iRow                  //@parm IN | Row to get address of. 
    ) 
{ 
    // This assumes iRow is valid... 
    // How can we calculate maximum row? 
    // Should we return NULL if it's out of range? 
    assert( m_rgbRowData ); 
    assert( m_cbRowSize ); 
    assert( iRow > 0 ); 
    return (ROWBUFF *) (m_rgbRowData + m_cbRowSize*iRow); 
} 
 
 
 
 
////////////////////////////////////////////////////////////////////////////// 
//      Helper functions    Helper functions    Helper functions 
////////////////////////////////////////////////////////////////////////////// 
 
// GetInternalTypeFromCSVType ------------------------------------------------ 
// 
// @func This function returns the default OLE DB representation 
// for a data type 
// 
HRESULT GetInternalTypeFromCSVType 
    ( 
    SWORD  swDataType,  //@parm IN | Data Type 
    BOOL   fIsSigned,   //@parm IN | Signed or Unsigned 
    DWORD  *pdwdbType   //@parm OUT | OLE DB type for DBColumnInfo 
    ) 
{ 
    static struct { 
        SWORD   swCSVType; 
        BOOL    fIsSigned;          // 1=signed, 0=unsigned 
        BOOL    fSignedDistinction; // 1=signed matters 
        DWORD   dwdbType; 
    } TypeTable[] = 
        { 
            {TYPE_CHAR,         0, 0, DBTYPE_STR }, 
            {TYPE_SLONG,        1, 1, DBTYPE_I4  }, 
        }; 
 
    for (int j=0; j < NUMELEM( TypeTable ); j++) 
        { 
        if (swDataType == TypeTable[j].swCSVType        // type match 
        && (!TypeTable[j].fSignedDistinction            // care about sign? 
            || fIsSigned == TypeTable[j].fIsSigned))    // sign match 
            { 
            assert( pdwdbType ); 
            *pdwdbType     = TypeTable[j].dwdbType; 
            return ResultFromScode( S_OK ); 
            } 
        } 
 
    // Should never get here, since we supposedly have 
    // a table of all possible CSV types. 
    assert( !"Unmatched CSV Type." ); 
    return ResultFromScode( E_FAIL ); 
}