//--------------------------------------------------------------------
// 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 );
}