2025-11-28 00:35:46 +09:00

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