520 lines
15 KiB
C++
520 lines
15 KiB
C++
//--------------------------------------------------------------------
|
|
// Microsoft OLE DB Sample Provider
|
|
// (C) Copyright 1991 - 1999 Microsoft Corporation. All Rights Reserved.
|
|
//
|
|
// @doc
|
|
//
|
|
// @module ROWSET.CPP | CRowset object implementation
|
|
//
|
|
//
|
|
|
|
// Includes ------------------------------------------------------------------
|
|
|
|
#include "headers.h"
|
|
|
|
static const int MAX_BITS = 1008;
|
|
|
|
static const int TYPE_CHAR = 1;
|
|
static const int TYPE_SLONG = 3;
|
|
|
|
// Code ----------------------------------------------------------------------
|
|
|
|
// CRowset::CRowset ----------------------------------------------------------
|
|
//
|
|
// @mfunc Constructor for this class
|
|
//
|
|
// @rdesc NONE
|
|
//
|
|
CRowset::CRowset
|
|
(
|
|
LPUNKNOWN pUnkOuter //@parm IN | Outer Unkown Pointer
|
|
) // invoke ctor for base class
|
|
: CBaseObj( BOT_ROWSET )
|
|
{
|
|
|
|
// Initialize simple member vars
|
|
m_cRef = 0L;
|
|
m_pUnkOuter = pUnkOuter ? pUnkOuter : this;
|
|
|
|
// Intialize buffered row count + pointers to allocated memory
|
|
m_pFileio = NULL;
|
|
m_cRows = 0;
|
|
m_irowFilePos = 0;
|
|
m_irowLastFilePos = 0;
|
|
m_cbTotalRowSize = 0;
|
|
m_cbRowSize = 0;
|
|
m_irowMin = 0;
|
|
m_ulRowRefCount = 0;
|
|
m_pextbufferAccessor= NULL;
|
|
m_pIBuffer = NULL;
|
|
m_prowbitsIBuffer = NULL;
|
|
m_pLastBindBase = NULL;
|
|
m_rgbRowData = NULL;
|
|
m_dwStatus = 0;
|
|
m_pUtilProp = NULL;
|
|
m_pCreator = NULL;
|
|
|
|
m_wszFilePath[0] = L'\0';
|
|
m_wszDataSourcePath[0] = L'\0';
|
|
|
|
// Initially, NULL all contained interfaces
|
|
m_pIAccessor = NULL;
|
|
m_pIColumnsInfo = NULL;
|
|
m_pIConvertType = NULL;
|
|
m_pIRowset = NULL;
|
|
m_pIRowsetChange = NULL;
|
|
m_pIRowsetIdentity = NULL;
|
|
m_pIRowsetInfo = NULL;
|
|
m_pIGetRow = NULL;
|
|
|
|
// Increment global object count.
|
|
OBJECT_CONSTRUCTED();
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
// CRowset::~CRowset ---------------------------------------------------------
|
|
//
|
|
// @mfunc Destructor for this class
|
|
//
|
|
// @rdesc NONE
|
|
//
|
|
CRowset:: ~CRowset
|
|
(
|
|
void
|
|
)
|
|
{
|
|
// Free pointers.
|
|
// (Note delete is safe for NULL ptr.)
|
|
SAFE_DELETE( m_prowbitsIBuffer );
|
|
SAFE_DELETE( m_pUtilProp );
|
|
|
|
if (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 (m_pextbufferAccessor)
|
|
{
|
|
HACCESSOR hAccessor, hAccessorLast;
|
|
PACCESSOR pAccessor;
|
|
|
|
m_pextbufferAccessor->GetFirstLastItemH(hAccessor, hAccessorLast);
|
|
|
|
for (; hAccessor <= hAccessorLast; hAccessor++)
|
|
{
|
|
m_pextbufferAccessor->GetItemOfExtBuffer(hAccessor, &pAccessor);
|
|
SAFE_DELETE( pAccessor );
|
|
}
|
|
|
|
SAFE_DELETE( m_pextbufferAccessor );
|
|
}
|
|
|
|
// Free contained interfaces
|
|
SAFE_DELETE( m_pIAccessor );
|
|
SAFE_DELETE( m_pIColumnsInfo );
|
|
SAFE_DELETE( m_pIConvertType );
|
|
SAFE_DELETE( m_pIRowset );
|
|
SAFE_DELETE( m_pIRowsetChange );
|
|
SAFE_DELETE( m_pIRowsetIdentity );
|
|
SAFE_DELETE( m_pIRowsetInfo );
|
|
SAFE_DELETE( m_pIGetRow );
|
|
|
|
// free CFileio object
|
|
if (m_pFileio)
|
|
SAFE_DELETE( m_pFileio );
|
|
|
|
if( m_pCreator )
|
|
m_pCreator->GetOuterUnknown()->Release();
|
|
|
|
if( m_pParentObj )
|
|
m_pParentObj->GetOuterUnknown()->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
|
|
CBaseObj * pParentBaseObj, //@parm IN | pointer to Base Object creating the rowset
|
|
WCHAR * pwszFilePath, //@parm IN | The File Path of the csv file
|
|
WCHAR * pwszDataSource //@parm IN | The datasource path
|
|
)
|
|
{
|
|
// Asserts
|
|
assert(pCFileio);
|
|
assert(pParentBaseObj);
|
|
assert(m_pUnkOuter);
|
|
assert(pParentBaseObj);
|
|
|
|
LPUNKNOWN pIUnknown = m_pUnkOuter;
|
|
m_pFileio = pCFileio;
|
|
|
|
m_pParentObj = pParentBaseObj;
|
|
m_pParentObj->GetOuterUnknown()->AddRef();
|
|
|
|
//--------------------
|
|
// Get FileInfo
|
|
//--------------------
|
|
// Find # of columns in the result set.
|
|
m_cCols = m_pFileio->GetColumnCnt();
|
|
if( m_cCols <= 0 )
|
|
return FALSE;
|
|
|
|
m_cbRowSize = m_pFileio->GetRowSize();
|
|
|
|
if (FAILED( CreateHelperFunctions()))
|
|
return FALSE;
|
|
|
|
m_cbTotalRowSize = m_pIBuffer->cbSlot;
|
|
|
|
//--------------------
|
|
// 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, TRUE ))))
|
|
return FALSE;
|
|
|
|
// allocate utility object that manages our properties
|
|
m_pUtilProp = new CUtilProp();
|
|
if (!m_pUtilProp)
|
|
return FALSE;
|
|
|
|
// 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_pIConvertType = new CImpIConvertType(this, pIUnknown);
|
|
m_pIRowset = new CImpIRowset( this, pIUnknown );
|
|
m_pIRowsetIdentity = new CImpIRowsetIdentity( this, pIUnknown );
|
|
m_pIRowsetInfo = new CImpIRowsetInfo( this, pIUnknown );
|
|
m_pIAccessor = new CImpIAccessor( this, pIUnknown );
|
|
m_pIGetRow = new CImpIGetRow( this, pIUnknown );
|
|
|
|
if (!m_pFileio->IsReadOnly())
|
|
m_pIRowsetChange = new CImpIRowsetChange( this, pIUnknown );
|
|
|
|
if (m_pIAccessor && FAILED(m_pIAccessor->FInit(TRUE)))
|
|
return FALSE;
|
|
|
|
StringCchCopyW(m_wszFilePath, sizeof(m_wszFilePath)/sizeof(WCHAR), pwszFilePath);
|
|
StringCchCopyW(m_wszDataSourcePath, sizeof(m_wszDataSourcePath)/sizeof(WCHAR), pwszDataSource);
|
|
|
|
// if all interfaces were created, return success
|
|
return (BOOL) (m_pIAccessor && m_pIColumnsInfo && m_pIConvertType &&
|
|
m_pIRowset && m_pIRowsetIdentity && m_pIRowsetInfo &&
|
|
((m_pFileio->IsReadOnly() && !m_pIRowsetChange) ||
|
|
(!m_pFileio->IsReadOnly() && m_pIRowsetChange)) &&
|
|
m_pIGetRow);
|
|
}
|
|
|
|
|
|
// 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_IAccessor)
|
|
*ppv = (LPVOID) m_pIAccessor;
|
|
else if (riid == IID_IColumnsInfo)
|
|
*ppv = (LPVOID) m_pIColumnsInfo;
|
|
else if (riid == IID_IConvertType)
|
|
*ppv = (LPVOID) m_pIConvertType;
|
|
else if (riid == IID_IRowset)
|
|
*ppv = (LPVOID) m_pIRowset;
|
|
else if (riid == IID_IRowsetChange && SupportIRowsetChange() )
|
|
*ppv = (LPVOID) m_pIRowsetChange;
|
|
else if (riid == IID_IRowsetIdentity)
|
|
*ppv = (LPVOID) m_pIRowsetIdentity;
|
|
else if (riid == IID_IRowsetInfo)
|
|
*ppv = (LPVOID) m_pIRowsetInfo;
|
|
else if (riid == IID_IGetRow)
|
|
*ppv = (LPVOID) m_pIGetRow;
|
|
|
|
// 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_( DBREFCOUNT ) 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_( DBREFCOUNT ) CRowset::Release
|
|
(
|
|
void
|
|
)
|
|
{
|
|
if (!--m_cRef)
|
|
{
|
|
this->m_pCreator->DecrementOpenRowsets();
|
|
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/absence of rows.
|
|
m_prowbitsIBuffer = new CBitArray;
|
|
if( !m_prowbitsIBuffer || 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 / (ULONG)m_cbRowSize,
|
|
(ULONG) m_cbRowSize, g_dwPageSize, m_prowbitsIBuffer,
|
|
&m_pIBuffer, &m_rgbRowData )))
|
|
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::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
|
|
)
|
|
{
|
|
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 );
|
|
|
|
// Optimize by not doing it over again.
|
|
if( pBase != m_pLastBindBase )
|
|
{
|
|
m_pLastBindBase = 0;
|
|
|
|
for (DBORDINAL cCols=1; cCols <= m_cCols; cCols++)
|
|
{
|
|
pColumn = m_pFileio->GetColumnData(cCols, (ROWBUFF *)pBase);
|
|
|
|
if( FAILED( m_pFileio->SetColumnBind(cCols, pColumn)) )
|
|
return( E_FAIL );
|
|
}
|
|
|
|
// Remember in case we bind to same place again.
|
|
m_pLastBindBase = pBase;
|
|
}
|
|
|
|
return( 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
|
|
(
|
|
DBCOUNTITEM iRow, //@parm IN | Row to get address of.
|
|
BOOL fDataLocation //@parm IN | Get the Data offset.
|
|
)
|
|
{
|
|
// 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 );
|
|
|
|
// Get the LSTSlot address or the Data offset (We need to make sure this structure is properly aligned)
|
|
if ( fDataLocation )
|
|
return (ROWBUFF *) ROUND_UP ((m_rgbRowData + m_cbTotalRowSize*iRow), COLUMN_ALIGNVAL);
|
|
else
|
|
return (ROWBUFF *) ROUND_UP ((m_rgbRowData + m_cbRowSize + (m_cbTotalRowSize*(iRow-1))), COLUMN_ALIGNVAL);
|
|
}
|
|
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// 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 );
|
|
}
|
|
|
|
// SupportIRowsetChange ------------------------------------------------
|
|
//
|
|
// @func This function returns if IrowsetChange is supported
|
|
//
|
|
BOOL CRowset::SupportIRowsetChange()
|
|
{
|
|
BOOL fIRowsetChange = FALSE;
|
|
ULONG cPropSets = 0;
|
|
DBPROPSET * rgPropSets = NULL;
|
|
DBPROPID rgPropId[1];
|
|
DBPROPIDSET rgPropertySets[1];
|
|
|
|
// Get the value of the DBPROP_CANHOLDROWS property
|
|
rgPropertySets[0].guidPropertySet = DBPROPSET_ROWSET;
|
|
rgPropertySets[0].rgPropertyIDs = rgPropId;
|
|
rgPropertySets[0].cPropertyIDs = 1;
|
|
rgPropId[0] = DBPROP_IRowsetChange;
|
|
|
|
// Get the IRowsetChange Property from m_pUtilProp
|
|
GetCUtilProp()->GetProperties(PROPSET_ROWSET, 1, rgPropertySets,
|
|
&cPropSets, &rgPropSets);
|
|
|
|
// Get the Prompt value
|
|
if( V_BOOL(&rgPropSets->rgProperties->vValue) == VARIANT_TRUE )
|
|
fIRowsetChange = TRUE;
|
|
|
|
// release properties
|
|
FreeProperties(&cPropSets, &rgPropSets);
|
|
|
|
return fIRowsetChange;
|
|
}
|