|
|
//+-------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1994 - 2000.
//
// File: hraccess.cxx
//
// Contents: OLE DB HRow accessor helper class
//
// Classes: CAccessor
// CRowDataAccessor
// CRowDataAccessorByRef
//
// History: 21 Nov 94 dlee Created from AlanW's tblwindo code
//
//--------------------------------------------------------------------------
#include <pch.cxx>
#pragma hdrstop
#include <rowset.hxx>
#include <query.hxx>
#include "tabledbg.hxx"
//+-------------------------------------------------------------------------
//
// Member: CAccessorBag::Destroy, private
//
// Synopsis: Removes an accessor from the bag and deletes it
//
// History: 12 Jan 1995 dlee Created
//
//--------------------------------------------------------------------------
void CAccessorBag::Destroy( CAccessorBase * pAccessor ) { pAccessor->SetInvalid(); Remove(pAccessor);
TRY { while (pAccessor->GetRefcount() > 0) pAccessor->Release(); // this can throw - we'll toss away error
} CATCH( CException, e ) { SCODE sc = GetOleError(e); tbDebugOut(( DEB_ERROR, "CAccessorBase::Release threw 0x%x\n", sc )); } END_CATCH;
if (0 == pAccessor->GetInheritorCount() ) delete pAccessor; } //Destroy
//+-------------------------------------------------------------------------
//
// Member: CAccessorBag::~CAccessorBag, private
//
// Synopsis: Removes accessors the user forgot to free
//
// History: 16 Jan 1997 dlee Created
//
//--------------------------------------------------------------------------
CAccessorBag::~CAccessorBag() { CAccessorBase *p; while ( p = First() ) { tbDebugOut(( DEB_ITRACE, "App bug: Deleting an accessor that wasn't freed\n" )); Destroy( p ); } } //~CAccessorBag
//+-------------------------------------------------------------------------
//
// Member: CAccessorBag::Release, public
//
// Synopsis: Dereference an accessor; delete it if refcount goes to 0
//
// History: 18 Sep 1996 Alanw Created
//
//--------------------------------------------------------------------------
void CAccessorBag::Release(HACCESSOR hAccessor, ULONG * pcRef) { CAccessorBase *pAccessor = Convert(hAccessor); // will throw if accessor invalid
// caught by ReleaseAccessor
if (0 == pAccessor->Release()) { Destroy(pAccessor); if (pcRef) *pcRef = 0; } else { if (pcRef) *pcRef = pAccessor->GetRefcount(); } } //Release
//+-------------------------------------------------------------------------
//
// Member: CAccessorBag::AddRef, public
//
// Synopsis: Adds a reference to an accessor
//
// History: 18 Sep 1996 Alanw Created
//
//--------------------------------------------------------------------------
void CAccessorBag::AddRef(HACCESSOR hAccessor, ULONG * pcRef) { CAccessorBase *pAccessor = Convert(hAccessor);
ULONG cRef = pAccessor->AddRef(); if (pcRef) *pcRef = cRef; } //AddRef
// This pool has no private data -- it just calls the OLE allocator, so
// it can be static.
CAccessorAllocator CAccessor::_Pool;
//+-------------------------------------------------------------------------
//
// Function: isVariableLength
//
// Synopsis: TRUE if the type is one of the odd oledb types that can
// have variable length inline data
//
// Arguments: [type] -- oledb data type
//
// History: 6 Feb 95 dlee created
//
//--------------------------------------------------------------------------
static BOOL isVariableLength( DWORD type ) { type &= VT_TYPEMASK;
return type == DBTYPE_STR || type == DBTYPE_BYTES || type == DBTYPE_WSTR; } //isVariableLength
//+-------------------------------------------------------------------------
//
// Function: isValidByRef
//
// Synopsis: TRUE if the type is one of the odd oledb types that can
// be combined with DBTYPE_BYREF
//
// Arguments: [type] -- oledb data type
//
// History: 9 Aug 95 dlee created
//
//--------------------------------------------------------------------------
static BOOL isValidByRef( DWORD type ) { type &= ~DBTYPE_BYREF;
return type == DBTYPE_STR || type == DBTYPE_WSTR || type == DBTYPE_BYTES || type == DBTYPE_GUID || type == VT_CF || type == DBTYPE_VARIANT; } //isValidByref
//+-------------------------------------------------------------------------
//
// Function: isEquivalentType
//
// Synopsis: TRUE if the types are interchangable between OLE-DB and
// PROPVARIANT. Unfortunately, several of the types are
// equivalent but have different representations.
//
// Arguments: [vtDst] -- OLE-DB destination type
// [vtSrc] -- PROPVARIANT source type
//
// History: 9 Aug 95 dlee created
//
//--------------------------------------------------------------------------
static BOOL isEquivalentType( VARTYPE vtDst, VARTYPE vtSrc ) { return ( ( vtDst == vtSrc ) || ( ( ( DBTYPE_WSTR | DBTYPE_BYREF ) == vtDst ) && ( VT_LPWSTR == vtSrc ) ) || ( ( ( DBTYPE_STR | DBTYPE_BYREF ) == vtDst ) && ( VT_LPSTR == vtSrc ) ) || ( ( ( DBTYPE_WSTR | DBTYPE_BYREF | DBTYPE_VECTOR ) == vtDst ) && ( ( VT_LPWSTR | VT_VECTOR ) == vtSrc ) ) || ( ( ( DBTYPE_STR | DBTYPE_BYREF | DBTYPE_VECTOR ) == vtDst ) && ( ( VT_LPSTR | VT_VECTOR ) == vtSrc ) ) || ( ( ( DBTYPE_GUID | DBTYPE_BYREF ) == vtDst ) && ( VT_CLSID == vtSrc ) ) || ( ( ( VT_CF | DBTYPE_BYREF ) == vtDst ) && ( VT_CF == vtSrc ) ) ); } //isEquivalentType
//+-------------------------------------------------------------------------
//
// Function: NullOrCantConvert, inline
//
// Synopsis: Returns DBSTATUS_S_ISNULL if [type] is one of the types which
// represent null data, DBSTATUS_E_CANTCONVERTVALUE otherwise.
//
// Arguments: [type] -- variant data type
//
// History: 24 Feb 98 AlanW created
//
//--------------------------------------------------------------------------
inline DBSTATUS NullOrCantConvert( VARTYPE type ) { if ( type == VT_EMPTY || type == VT_NULL ) return DBSTATUS_S_ISNULL; else return DBSTATUS_E_CANTCONVERTVALUE; } //NullOrCantConvert
//+---------------------------------------------------------------------------
//
// Function: ConvertBackslashToSlash, inline
//
// Synopsis: Converts '\' characters to '/' in a string inplace.
//
// Arguments: [pwszPath] -- string to be converted
//
// History: 24 Feb 98 AlanW Added header
//
//----------------------------------------------------------------------------
inline void ConvertBackslashToSlash( LPWSTR pwszPath ) { Win4Assert( 0 != pwszPath );
while ( 0 != *pwszPath ) { if ( L'\\' == *pwszPath ) { *pwszPath = L'/'; } pwszPath++; } } //ConvertBackslashToSlash
// DBGP - a debug parameter, only available on checked builds
#ifndef DBGP
#if DBG == 1
#define DBGP(a) , a
#else
#define DBGP(a)
#endif // DBG
#endif // ndef DBGP
//+-------------------------------------------------------------------------
//
// Member: CAccessor::_BindingFailed, private
//
// Synopsis: Stores a binding status for an individual binding error.
// The caller should continue processing with the next binding.
//
// Arguments: [BindStat] -- what when wrong?
// [iBinding] -- which binding was bad?
// [pBindStatus] -- where to indicate bad binding
//
// History: 6 Feb 95 dlee created
//
//--------------------------------------------------------------------------
void CAccessor::_BindingFailed( DBBINDSTATUS BindStat, DBORDINAL iBinding, DBBINDSTATUS* pBindStatus DBGP(char* pszExplanation) ) { tbDebugOut(( DEB_TRACE, "CAccessor: construction failed, bindstatus=%x, binding %d\n", BindStat, iBinding )); #if DBG == 1
if (pszExplanation) { tbDebugOut(( DEB_TRACE|DEB_NOCOMPNAME, "\t%s\n", pszExplanation )); } #endif // DBG
if (pBindStatus != 0) { pBindStatus[iBinding] = BindStat; } _scStatus = DB_E_ERRORSOCCURRED; } //_BindingFailed
//+-------------------------------------------------------------------------
//
// Member: CAccessor::_ConstructorFailed, private
//
// Synopsis: Indicate an error with parameters other than an individual
// binding. Throw an exception for the error.
//
// Arguments: [scFailure] -- what when wrong?
//
// History: 16 Sep 1996 AlanW created
//
//--------------------------------------------------------------------------
void CAccessor::_ConstructorFailed( SCODE scFailure DBGP(char* pszExplanation) ) { tbDebugOut(( DEB_TRACE, "CAccessor: construction failed, sc=%x\n", scFailure )); #if DBG == 1
if (pszExplanation) { tbDebugOut(( DEB_TRACE|DEB_NOCOMPNAME, "\t%s\n", pszExplanation )); } #endif // DBG
_scStatus = scFailure;
QUIETTHROW(CException(scFailure)); } //_ConstructorFailed
static const GUID s_guidStorage = PSGUID_STORAGE; static const GUID s_guidQuery = DBQUERYGUID; const DBORDINAL colInvalid = -1;
//+---------------------------------------------------------------------------
//
// Member: CAccessor::_Initialize
//
// Synopsis: Initializes the object without verifying the coercions.
//
// Arguments: [dwAccessorFlags] - accessor flags, read/write, etc.
// [cBindings] - count of bindings
// [rgBindings] - array of binding structures
// [pBindStat] - on return, pointer to first binding in error
//
// History: 21 Nov 94 dlee created
// 11-07-95 srikants Moved from constructor
//
// Notes:
//
//----------------------------------------------------------------------------
void CAccessor::_Initialize( DBACCESSORFLAGS dwAccessorFlags, DBORDINAL cBindings, const DBBINDING * rgBindings, DBBINDSTATUS * pBindStat) { // Invalid accessor flag?
if ( dwAccessorFlags & ~ ( DBACCESSOR_PASSBYREF | DBACCESSOR_ROWDATA | DBACCESSOR_PARAMETERDATA | DBACCESSOR_OPTIMIZED) ) _ConstructorFailed(DB_E_BADACCESSORFLAGS DBGP("bad dwAccessorFlags bits"));
if ( (dwAccessorFlags & ( DBACCESSOR_ROWDATA | DBACCESSOR_PARAMETERDATA ) ) == 0) _ConstructorFailed(DB_E_BADACCESSORFLAGS DBGP("bad dwAccessorFlags type"));
if ( dwAccessorFlags & DBACCESSOR_PARAMETERDATA ) _ConstructorFailed(DB_E_BADACCESSORFLAGS DBGP("parameter accessors are not supported"));
// byref accessors are not supported
if ( dwAccessorFlags & ( DBACCESSOR_PASSBYREF) ) _ConstructorFailed(DB_E_BYREFACCESSORNOTSUPPORTED DBGP("byref accessors are not supported"));
// null accessors are not supported
if ( 0 == _cBindings ) _ConstructorFailed(DB_E_NULLACCESSORNOTSUPPORTED DBGP("null accessors are not supported"));
// verify each binding is ok and save it
for (DBORDINAL iBinding = 0; iBinding < _cBindings; iBinding++) { CDataBinding DataBinding (rgBindings[iBinding]); DBPART cp = DataBinding.Binding().dwPart; DBTYPE wType = DataBinding.Binding().wType;
if (0 != pBindStat) pBindStat[iBinding] = DBBINDSTATUS_OK;
// check for invalid bits in the column parts
if (cp & ~(DBPART_VALUE | DBPART_LENGTH | DBPART_STATUS)) _BindingFailed(DBBINDSTATUS_BADBINDINFO,iBinding,pBindStat DBGP("bad dwPart bits"));
// at least one of value, length or status flags must be on
if (0 == (cp & (DBPART_VALUE | DBPART_LENGTH | DBPART_STATUS))) _BindingFailed(DBBINDSTATUS_BADBINDINFO,iBinding,pBindStat DBGP("zero dwPart"));
// we don't support abstract data types
if (0 != DataBinding.Binding().pTypeInfo) _BindingFailed(DBBINDSTATUS_BADBINDINFO,iBinding,pBindStat DBGP("bad pTypeInfo"));
if (0 != DataBinding.Binding().pBindExt) _BindingFailed(DBBINDSTATUS_BADBINDINFO,iBinding,pBindStat DBGP("bad pBindExt"));
if (0 != (DataBinding.Binding().dwFlags & DBBINDFLAG_HTML)) _BindingFailed(DBBINDSTATUS_BADBINDINFO,iBinding,pBindStat DBGP("no HTML binding support")); else if (0 != DataBinding.Binding().dwFlags) _BindingFailed(DBBINDSTATUS_BADBINDINFO,iBinding,pBindStat DBGP("bad dwFlags"));
//
// Verify size and alignment of output buffer. Set cbWidth in local
// copy of binding to handle length of fixed-width types correctly.
//
if ( 0 != ( wType & DBTYPE_BYREF ) ) { if ( ! isValidByRef( wType ) ) _BindingFailed( DBBINDSTATUS_BADBINDINFO, iBinding, pBindStat DBGP("byref on non-byref type") );
// byref data: client's cbMaxLen is noise
DataBinding.SetMaxLen(sizeof LPWSTR); } else if ( 0 != ( wType & DBTYPE_VECTOR ) ) { USHORT cbWidth = sizeof ( DBVECTOR ); USHORT cbAlign = sizeof ( DBVECTOR );
#if CIDBG==1
tbDebugOut(( DEB_ACCESSOR, "type %d, obValue %d, alignment needed: %d\n", (int) wType, (int) DataBinding.Binding().obValue, (int) cbAlign ));
//Win4Assert( (0 == (DBPART_VALUE & cp)) ||
// ( (0 != cbAlign) &&
// (0 == (DataBinding.Binding().obValue % cbAlign)) ) );
if ( (DBPART_VALUE & cp) && (0 != (DataBinding.Binding().obValue % cbAlign)) ) { tbDebugOut(( DEB_ERROR, "bad value alignment for DBVECTOR, obValue %d, alignment needed: %d\n", (int) DataBinding.Binding().obValue, (int) cbAlign )); } #endif // CIDBG==1
// Fixed-length data types needn't have their width set, per
// the Nile spec. So we set it to the default for the type.
DataBinding.SetMaxLen( cbWidth ); } else if ( 0 != ( wType & VT_ARRAY ) ) { DataBinding.SetMaxLen( sizeof( SAFEARRAY * ) ); } else { USHORT cbWidth,cbAlign,gfFlags;
CTableVariant::VartypeInfo( wType, cbWidth, cbAlign, gfFlags );
#if CIDBG==1
tbDebugOut(( DEB_ACCESSOR, "type %d, obValue %d, alignment needed: %d\n", (int) wType, (int) DataBinding.Binding().obValue, (int) cbAlign ));
//Win4Assert( (0 == (DBPART_VALUE & cp)) ||
// ( (0 != cbAlign) &&
// (0 == (DataBinding.Binding().obValue % cbAlign)) ) );
if ( (DBPART_VALUE & cp) && (0 != (DataBinding.Binding().obValue % cbAlign)) ) { tbDebugOut(( DEB_ERROR, "bad value alignment for type %d, obValue %d, alignment needed: %d\n", (int) wType, (int) DataBinding.Binding().obValue, (int) cbAlign )); } #endif // CIDBG==1
// Fixed-length data types needn't have their width set, per
// the Nile spec. So we set it to the default for the type.
if (! isVariableLength( wType ) ) DataBinding.SetMaxLen( cbWidth ); }
if (DataBinding.Binding().dwMemOwner & ~(DBMEMOWNER_PROVIDEROWNED)) _BindingFailed(DBBINDSTATUS_BADBINDINFO,iBinding,pBindStat DBGP("bad dwMemOwner bits"));
if (DBTYPE_EMPTY == (wType & VT_TYPEMASK) || DBTYPE_NULL == (wType & VT_TYPEMASK) || 0 != (wType & VT_RESERVED)) { _BindingFailed(DBBINDSTATUS_BADBINDINFO,iBinding,pBindStat DBGP("bad wType")); }
if ((wType & ~VT_TYPEMASK) != 0 && (wType & ~VT_TYPEMASK) != DBTYPE_ARRAY && (wType & ~VT_TYPEMASK) != DBTYPE_VECTOR && (wType & ~VT_TYPEMASK) != DBTYPE_BYREF ) { _BindingFailed(DBBINDSTATUS_BADBINDINFO,iBinding,pBindStat DBGP("bad wType modifier combination")); }
// SPECDEVIATION - this is bogus; DBTYPE_VARIANT must be supported!
if ((DataBinding.Binding().dwMemOwner & DBMEMOWNER_PROVIDEROWNED) && (wType != DBTYPE_BSTR) && !(wType & (DBTYPE_BYREF | DBTYPE_VECTOR | DBTYPE_ARRAY ))) _BindingFailed(DBBINDSTATUS_BADBINDINFO,iBinding,pBindStat DBGP("bad provider-owned mem type"));
if ( (DataBinding.Binding().dwMemOwner & DBMEMOWNER_PROVIDEROWNED) && (wType == (DBTYPE_BYREF|DBTYPE_VARIANT)) && ! _fExtendedTypes ) _BindingFailed(DBBINDSTATUS_BADBINDINFO,iBinding,pBindStat DBGP("provider-owned mem without extended types"));
_aBindings[ iBinding] = DataBinding;
if ( DataBinding.Binding().pObject ) { _aBindings[iBinding].Binding().pObject = new DBOBJECT;
RtlCopyMemory( _aBindings[(unsigned)iBinding].Binding().pObject, DataBinding.Binding().pObject, sizeof( DBOBJECT ) ); } }
// Verify that none of the output offsets and lengths overlap with
// any others. This must be run after the above loop so cbMaxLength
// fields are properly initialized.
if (_scStatus == S_OK) _ValidateOffsets( pBindStat ); } //_Initialize
//+---------------------------------------------------------------------------
//
// Member: CAccessor::Validate
//
// Synopsis: Validates the coercions with respect to the ColumnsInfo.
//
// Arguments: [rColumnsInfo] - The column info. for the rowset
// [pBindStat] - Binding status array (optional)
//
// History: 21 Nov 94 dlee created
// 11-08-95 srikants Created
// 01-15-98 VikasMan Removed call to checkcoercion here
// Checking done only in GetData now
//
//----------------------------------------------------------------------------
void CAccessor::Validate( CColumnsInfo & rColumnsInfo, DBBINDSTATUS * pBindStatus ) { _pColumnsInfo = &rColumnsInfo; Win4Assert( 0 != _pColumnsInfo );
for (DBORDINAL iBinding = 0; iBinding < _cBindings; iBinding++) { CDataBinding & DataBinding = _aBindings[iBinding]; DBTYPE wType = (DBTYPE) DataBinding.Binding().wType;
//
// Make sure the column id is valid. Remember, column numbers are
// 1-based. Map columnid 0 to the row ID column for the bookmark.
//
DBORDINAL iColumnId = DataBinding.Binding().iOrdinal;
if ( ! _pColumnsInfo->IsValidColumnId( (ULONG) iColumnId ) ) { _BindingFailed(DBBINDSTATUS_BADORDINAL, iBinding, pBindStatus DBGP("invalid iOrdinal")); continue; }
const DBCOLUMNINFO & rColInfo = _pColumnsInfo->Get1ColumnInfo( (ULONG) iColumnId );
if ( DBTYPE_HCHAPTER == wType && !(rColInfo.dwFlags & DBCOLUMNFLAGS_ISCHAPTER) ) { _BindingFailed(DBBINDSTATUS_UNSUPPORTEDCONVERSION, iBinding, pBindStatus DBGP("Chapter type binding to non-chapter column")); continue; }
//
// The only IUNKNOWN binding we currently support is for the
// DBCOL_SELFCOLUMNS guid, with propid PROPID_DBSELF_SELF.
// We don't yet support binding to a particular column, just
// to the row (file) as a whole, and only if the client is FSCI.
//
if ( DBTYPE_IUNKNOWN == wType ) { // Map self to the rowid column
if ( ( DBCOL_SELFCOLUMNS == rColInfo.columnid.uGuid.guid ) && ( PROPID_DBSELF_SELF == rColInfo.columnid.uName.ulPropid ) ) DataBinding.SetDataColumn( _pColumnsInfo->GetRowIdColumn() ); else _BindingFailed(DBBINDSTATUS_BADBINDINFO,iBinding,pBindStatus DBGP("bad IUNKNOWN binding")); }
// If it's the path column, save the column # for possible use later
// when doing deferred or self loads
if ( rColInfo.columnid.uGuid.guid == s_guidStorage && rColInfo.columnid.uName.ulPropid == PID_STG_PATH ) _iPathColumn = DataBinding.GetDataColumn();
// If it's the vpath column, save the column # for later use. We
// need to translate '\' to '/' in the vpath column.
if ( rColInfo.columnid.uGuid.guid == s_guidQuery && rColInfo.columnid.uName.ulPropid == DISPID_QUERY_VIRTUALPATH ) _iVpathBinding = iBinding;
// If it's a bookmark column, map it to the corresponding row ID
// column for retrieval.
if ( (rColInfo.dwFlags & DBCOLUMNFLAGS_ISBOOKMARK) && ! ( rColInfo.dwFlags & DBCOLUMNFLAGS_ISCHAPTER ) ) DataBinding.SetDataColumn( _pColumnsInfo->GetRowIdColumn() );
// If it's a chapter column, mark it to Addref the chapter on retrieval
if ( (rColInfo.dwFlags & DBCOLUMNFLAGS_ISCHAPTER) && (DBPART_VALUE & DataBinding.Binding().dwPart) ) DataBinding.SetChapter( TRUE ); }
// look for pathname -- it may be in the rowbuffer columns, but not
// in the accessor's bindings.
if ( colInvalid == _iPathColumn ) { for ( ULONG x = 0; x < rColumnsInfo.GetHiddenColumnCount(); x++ ) { CFullPropSpec const &spec = *rColumnsInfo.GetPropSpec( x+1 );
tbDebugOut(( DEB_ACCESSOR, "spec 0x%x IsPropid %d, propid 0x%x, pathpropid: 0x%x\n", &spec, spec.IsPropertyPropid(), spec.GetPropertyPropid(), PID_STG_PATH ));
if ( spec.IsPropertyPropid() && PID_STG_PATH == spec.GetPropertyPropid() && s_guidStorage == spec.GetPropSet() ) { _iPathColumn = x+1; break; } } }
_idColInfo = _pColumnsInfo->GetId(); } //Validate
//+-------------------------------------------------------------------------
//
// Member: CAccessor::CAccessor, public
//
// Synopsis: Constructs an accessor object
//
// Arguments: [dwAccessorFlags] -- accessor flags
// [cBindings] -- # of bindings specified
// [rgBindings] -- array of bindings
// [pBindStat] -- returns index of bad binding (if any)
// [fExtTypes] -- TRUE if extended variants are supported
// [pColumns] -- column info for early checking of column
// coercions
// [type] -- type of accessor
// [pCreator] --
//
// History: 21 Nov 94 dlee created
//
//--------------------------------------------------------------------------
CAccessor::CAccessor( DBACCESSORFLAGS dwAccessorFlags, DBCOUNTITEM cBindings, const DBBINDING * rgBindings, DBBINDSTATUS * pBindStat, BOOL fExtTypes, CColumnsInfo * pColumns, EAccessorType type, void * pCreator ) : CAccessorBase(pCreator, type), _dwAccessorFlags(dwAccessorFlags), _scStatus(S_OK), _aBindings( (unsigned) cBindings), _cBindings(cBindings), _idColInfo(0), _pColumnsInfo(0), _iPathColumn( colInvalid ), _iVpathBinding( colInvalid ), _fExtendedTypes( fExtTypes ) { _Initialize( dwAccessorFlags, cBindings, rgBindings, pBindStat );
if ( 0 != pColumns && _scStatus == S_OK ) Validate( *pColumns, pBindStat ); if (_scStatus != S_OK) QUIETTHROW(CException(_scStatus)); } //CAccessor
//+-------------------------------------------------------------------------
//
// Member: CAccessor::~CAccessor, public
//
// Synopsis: Destructs an accessor object
//
// History: 19 Apr 2000 dlee added header
//
//--------------------------------------------------------------------------
CAccessor::~CAccessor() { // Delete DBOBJECTs
for ( unsigned iBinding = 0; iBinding < _cBindings; iBinding++ ) { if ( 0 != _aBindings[iBinding].Binding().pObject ) delete _aBindings[iBinding].Binding().pObject;
// These are for future use according to OLE-DB 2.0 and should be NULL
Win4Assert( 0 == _aBindings[iBinding].Binding().pTypeInfo && 0 == _aBindings[iBinding].Binding().pBindExt ); }
CAccessorBase * pParent = GetParent();
if ( 0 != pParent ) { if ( 0 == pParent->DecInheritors() && 0 == pParent->GetRefcount() ) delete pParent; } } //~CAccessor
//+-------------------------------------------------------------------------
//
// Member: CAccessor::_ValidateOffsets, private
//
// Synopsis: Checks a binding for overlapping output fields
//
// Arguments: [pBindStat] -- where to indicate bad binding
//
// History: 18 May 1995 dlee Created
//
//--------------------------------------------------------------------------
void CAccessor::_ValidateOffsets( DBBINDSTATUS * pBindStatus ) { for ( DBORDINAL iBinding = 0; iBinding < _cBindings; iBinding++ ) { CDataBinding & DataBinding = _aBindings[ iBinding];
COffsetLengthPair aPairs[3]; ULONG cPairs = 0; DataBinding.CollectOutputPairs( aPairs, cPairs );
// Check for overlap with data/length/status in this binding
for ( DBORDINAL i = 0; i < cPairs; i++ ) { for ( DBORDINAL j = i + 1; j < cPairs; j++) { if ( aPairs[i].isInConflict( aPairs[j] ) ) { _BindingFailed( DBBINDSTATUS_BADBINDINFO, iBinding, pBindStatus DBGP("intra-binding field overlap") ); continue; } } }
// Check for overlap with other bindings
for ( i = iBinding + 1; i < _cBindings; i++ ) { CDataBinding &binding = _aBindings[i]; COffsetLengthPair aTestPairs[3]; ULONG cTestPairs = 0; binding.CollectOutputPairs( aTestPairs, cTestPairs );
for (ULONG iPair = 0; iPair < cPairs; iPair++) { for (ULONG iTestPair = 0; iTestPair < cTestPairs; iTestPair++) { if ( aPairs[iPair].isInConflict( aTestPairs[iTestPair] ) ) { _BindingFailed( DBBINDSTATUS_BADBINDINFO, i, pBindStatus DBGP("inter-binding field overlap") ); continue; } } } } } } //_ValidateOffsets
//+---------------------------------------------------------------------------
//
// Member: CAccessor::CanConvertType, static public
//
// Synopsis: Indicate whether a type conversion is valid.
//
// Arguments: [wFromType] -- source type
// [wToType] -- destination type
// [fExTypes] -- TRUE if extended types allowed
// [xDataConvert] -- OLEDB IDataConvert interface pointer
//
// Returns: TRUE if the conversion is available, FALSE otherwise.
// Throws E_FAIL or E_INVALIDARG on errors.
//
// History: 20 Nov 96 AlanW Created
// 14 Jan 98 VikasMan Add xDataConvert parameter
//
//----------------------------------------------------------------------------
BOOL CAccessor::CanConvertType( DBTYPE wFromType, DBTYPE wToType, BOOL fExTypes, XInterface<IDataConvert>& xDataConvert) { USHORT cbData, cbAlignFrom, rgfFlagsFrom; CTableVariant::VartypeInfo(wFromType, cbData, cbAlignFrom, rgfFlagsFrom);
USHORT cbAlignTo, rgfFlagsTo; CTableVariant::VartypeInfo(wToType, cbData, cbAlignTo, rgfFlagsTo);
if (0 == cbAlignFrom || 0 == cbAlignTo) QUIETTHROW(CException(E_INVALIDARG)); // bad type
if ( ((wToType & ~VT_TYPEMASK) != 0 && (wToType & ~VT_TYPEMASK) != DBTYPE_ARRAY && (wToType & ~VT_TYPEMASK) != DBTYPE_VECTOR && (wToType & ~VT_TYPEMASK) != DBTYPE_BYREF ) || 0 != (wToType & VT_RESERVED) ) { tbDebugOut(( DEB_IERROR, "CanConvertType: conversion to invalid type")); QUIETTHROW(CException(E_INVALIDARG)); }
if ( ((wFromType & ~VT_TYPEMASK) != 0 && (wFromType & ~VT_TYPEMASK) != DBTYPE_ARRAY && (wFromType & ~VT_TYPEMASK) != DBTYPE_VECTOR && (wFromType & ~VT_TYPEMASK) != DBTYPE_BYREF ) || 0 != (wFromType & VT_RESERVED) ) { tbDebugOut(( DEB_IERROR, "CanConvertType: conversion from invalid type")); QUIETTHROW(CException(E_INVALIDARG)); }
//
// check for conversions _Initialize() does not allow
//
// check if byref request for "short" type
if ( (wToType & DBTYPE_BYREF) && !isValidByRef(wToType) ) { tbDebugOut(( DEB_IERROR, "CanConvertType: conversion to byref type for short type")); return FALSE; }
if ( DBTYPE_EMPTY == (wToType & VT_TYPEMASK) || DBTYPE_NULL == (wToType & VT_TYPEMASK) )
{ tbDebugOut(( DEB_IERROR, "CanConvertType: conversion to empty or null type")); return FALSE; }
//
// check extended type conversions
//
// NEWFEATURE: - vector ==> array conversion?
if ((wToType & DBTYPE_ARRAY) && (wToType != wFromType)) { tbDebugOut(( DEB_IERROR, "CanConvertType: conversion to array")); return FALSE; } if ((wToType & DBTYPE_VECTOR) && !(isEquivalentType(wToType,wFromType) || wFromType == VT_VARIANT)) { tbDebugOut(( DEB_IERROR, "CanConvertType: conversion to vector")); return FALSE; }
//
// VARIANT conversions depend upon whether extended types are supported
//
if (wToType == DBTYPE_VARIANT) { if (fExTypes) return TRUE;
if ( (wFromType & ~(DBTYPE_BYREF)) == DBTYPE_GUID) return FALSE;
return TRUE; }
//
// Anything can coerce into DBTYPE_BYTES
//
if (wToType == DBTYPE_BYTES) return TRUE;
// now use OLEDB to check if conversion is possible
return COLEDBVariant::CanConvertType( wFromType, wToType, xDataConvert );
} //CanConvertType
//+-------------------------------------------------------------------------
//
// Member: CAccessor::GetBindings, public
//
// Synopsis: Fetches a copy of the bindings used by the accessor
//
// Arguments: [pAccessorFlags] -- accessor flags
// [pcBindings] -- # of bindings returned
// [ppBindings] -- array of bindings returned. the user
// must IMalloc::Free this memory.
//
// Returns: SCODE - S_OK. Return value required by base class
// CAccessorBase
//
// History: 21 Nov 94 dlee created
// 05 May 97 emilyb switched first 2 params from
// references to pointers so that
// this can be a virtual member of
// CAccessorBase. (CDistributedAccessor
// cannot use references).
//
//--------------------------------------------------------------------------
SCODE CAccessor::GetBindings( DBACCESSORFLAGS * pAccessorFlags, DBCOUNTITEM * pcBindings, DBBINDING ** ppBindings) { // in case of an error later, init the count to a good state
*pcBindings = 0;
// verify this pointer is good
*ppBindings = 0;
// allocate room for the bindings and copy them
*ppBindings = (DBBINDING *) _Pool.Allocate( (ULONG) _cBindings * sizeof DBBINDING );
*pAccessorFlags = _dwAccessorFlags; *pcBindings = _cBindings;
for ( DBORDINAL i = 0; i < _cBindings; i++ ) { (*ppBindings)[i] = _aBindings[ i].Binding();
if ( _aBindings[i].Binding().pObject ) { (*ppBindings)[i].pObject = (DBOBJECT*) _Pool.Allocate( sizeof( DBOBJECT ) );
RtlCopyMemory( (*ppBindings)[i].pObject, _aBindings[i].Binding().pObject, sizeof( DBOBJECT ) ); }
// These are for future use according to OLE-DB 2.0 and should be NULL
Win4Assert( 0 == _aBindings[i].Binding().pTypeInfo && 0 == _aBindings[i].Binding().pBindExt ); }
return S_OK; } //GetBindings
//+-------------------------------------------------------------------------
//
// Member: CRowDataAccessor::_LoadPath, private
//
// Synopsis: Loads the path of the object represented by the row
//
// Arguments: [rSrcSet] -- set of source columns
// [pbSrc] -- source row buffer
// [funnyPath] -- where to put the path
//
// History: 30 May 95 dlee created
//
//--------------------------------------------------------------------------
void CRowDataAccessor::_LoadPath( CTableColumnSet & rSrcSet, BYTE * pbSrc, CFunnyPath & funnyPath ) { // either path or workid must be available -- it's added by ColInfo
// when the query is created.
if ( colInvalid != _iPathColumn ) { // pathname happened to be one of the columns -- this is faster than
// doing a wid translation
CTableColumn *pPathCol = rSrcSet.Find( (ULONG) _iPathColumn );
WCHAR *pwc = 0; if (pPathCol->GetStoredType() == VT_LPWSTR) { pwc = * ( (WCHAR **) (pbSrc + pPathCol->GetValueOffset()) ); } else { Win4Assert(pPathCol->GetStoredType() == VT_VARIANT); CTableVariant & varnt = * ( (CTableVariant *) (pbSrc + pPathCol->GetValueOffset()) ); if (varnt.vt == VT_LPWSTR) pwc = varnt.pwszVal; }
if (pwc) { funnyPath.SetPath( pwc ); } } else { // translate the wid for the row into a pathname
Win4Assert( colInvalid != _pColumnsInfo->GetRowIdColumn() ); _pQuery->WorkIdToPath( _RowWid( rSrcSet, pbSrc ), funnyPath ); } } //_LoadPath
//+-------------------------------------------------------------------------
//
// Member: CRowDataAccessor::_BindToObject, private
//
// Synopsis: Binds to an object and returns an interface pointer
//
// Arguments: [pbDst] -- destination row buffer
// [rDstBinding] -- binding for the destination
// [pbSrc] -- source row buffer
// [rSrcColumn] -- source column description
// [rSrcSet] -- set of source columns
//
// Returns: DBSTATUS -- status of the column copy
//
// History: 10 Apr 95 dlee created
//
//--------------------------------------------------------------------------
DBSTATUS CRowDataAccessor::_BindToObject( BYTE * pbDst, CDataBinding & rDstBinding, BYTE * pbSrc, CTableColumn & rSrcColumn, CTableColumnSet & rSrcSet) { //
// CLEANCODE: This is all wrong. StgOpenStorage doesn't belong in
// framework code! Though fixing this would be hard -- we'd have to
// remote the object binding from the server process. And we only
// try this for the path column -- if it doesn't exist this code won't
// be executed.
//
DBSTATUS DstStatus = 0;
// get the pathname for ole to open the storage
CFunnyPath funnyPath; _LoadPath( rSrcSet, pbSrc, funnyPath );
// bind to the file and return interface pointer requested
if ( 0 != funnyPath.GetActualLength() ) { // WORKAROUND: StgOpenStorage AVs for paths > MAX_PATH currently
// So till it is fixed, make sure we do not pass a path > MAX_PATH
if ( funnyPath.GetLength() < MAX_PATH ) { XInterface<IStorage> xStorage; SCODE sc = StgOpenStorage( funnyPath.GetPath(), 0, rDstBinding.Binding().pObject->dwFlags, 0, 0, xStorage.GetPPointer() ); if ( SUCCEEDED( sc ) ) sc = xStorage->QueryInterface( rDstBinding.Binding().pObject->iid, (void **) pbDst ); if ( FAILED( sc ) ) DstStatus = DBSTATUS_E_CANTCREATE; } else { ciDebugOut(( DEB_WARN, "Not calling StgOpenStorage in CRowDataAccessor::_BindToObject for paths > MAX_PATH: \n(%ws)\n", funnyPath.GetPath() )); DstStatus = DBSTATUS_E_CANTCREATE; } } else DstStatus = DBSTATUS_S_ISNULL;
return DstStatus; } //_BindToObject
//+-------------------------------------------------------------------------
//
// Member: CRowDataAccessor::_LoadDeferred, private
//
// Synopsis: Loads a non-storage property value. Only called if the
// value is large, hence not already loaded.
//
// Arguments: [rSrcVar] -- where the value is written
// [pbSrc] -- pointer to the row data (to get the wid)
// [iColumn] -- column being copied
// [rSrcSet] -- set of source columns
//
// Returns: DBSTATUS -- status of the load
//
// History: 1 Jun 95 dlee created
//
//--------------------------------------------------------------------------
DBSTATUS CRowDataAccessor::_LoadDeferred( CTableVariant & rSrcVar, BYTE * pbSrc, DBORDINAL iColumn, CTableColumnSet & rSrcSet) { // If the property set storage has not yet been opened for this object,
// load it now.
tbDebugOut(( DEB_ACCESSOR, "loading deferred value\n" ));
rSrcVar.vt = VT_EMPTY; // just in case we can't load anything
CFullPropSpec const *ps = _pColumnsInfo->GetPropSpec( (ULONG) _aBindings[iColumn].GetDataColumn() );
//
// Try loading from the property cache/docfile if the workid column is
// available.
//
DBORDINAL rowIdCol = _pColumnsInfo->GetRowIdColumn(); if ( ( colInvalid == rowIdCol ) || ( !_pQuery->FetchDeferredValue( _RowWid( rSrcSet, pbSrc ), *ps, rSrcVar ) ) ) { return DBSTATUS_S_ISNULL; }
tbDebugOut(( DEB_ACCESSOR, "successfully loaded deferred value\n" ));
return DBSTATUS_S_OK; } //_LoadDeferred
//+-------------------------------------------------------------------------
//
// Member: CRowDataAccessor::_ComplexCopy, private
//
// Synopsis: Do a complex copy of the value to the client's buffer
//
// Arguments: [rDstBinding] -- binding for the destination
// [pbSrc] -- source row buffer
// [rSrcColumn] -- source column description
// [iColumn] -- column being copied
// [vtSrc] -- type of the source
// [cbDstLength] -- on exit, size of destination data
// [pbSrcData] -- pointer to source data
// [vtDst] -- type of the destination value
// [pbDstData] -- where to write the value
// [rSrcSet] -- set of source columns
// [rRowBuffer] -- row buffer
// [hrow] -- hrow being retrieved
// [xDataConvert] -- smart pointer to IDataConvert interface.
// this is to be passed along and used finally
// in COLEDBVariant::CopyOrCoerce
//
// Returns: DBSTATUS -- status of the column copy
//
// History: 01 Jun 1995 dlee created
// 09 Jan 1998 vikasman added xDataConvert parameter
//
//--------------------------------------------------------------------------
DBSTATUS CRowDataAccessor::_ComplexCopy( CDataBinding & rDstBinding, BYTE * pbSrc, CTableColumn & rSrcColumn, DBORDINAL iColumn, VARTYPE vtSrc, DBLENGTH & cbDstLength, BYTE * pbSrcData, VARTYPE vtDst, BYTE * pbDstData, CTableColumnSet & rSrcSet, CRowBuffer & rRowBuffer, HROW hrow, XInterface<IDataConvert>& xDataConvert) { DBSTATUS DstStatus = DBSTATUS_S_OK;
// Convert to a COLEDBVariant, then copy it using the OLEDBConvert
// method. This allows coercions and variant to nonvariant, etc.
// conversions to occur.
COLEDBVariant SrcVar; BOOL fDoCopy = TRUE; BOOL fFreeDeferredSrc = FALSE;
if ( VT_VARIANT == vtSrc ) { Win4Assert(rSrcColumn.GetValueSize() == sizeof PROPVARIANT);
if ( ( rSrcColumn.IsDeferred( pbSrc ) ) ) { DstStatus = _LoadDeferred( SrcVar, pbSrc, iColumn, rSrcSet );
if ( DBStatusOK ( DstStatus ) ) { // Attempt to store the data without using OLEDBConvert
if ( (VT_VARIANT == vtDst && _fExtendedTypes) || DBTYPE_PROPVARIANT == vtDst ) { fDoCopy = FALSE; * (PROPVARIANT *) pbDstData = SrcVar; cbDstLength = sizeof (PROPVARIANT); } else if ( (VT_VARIANT|DBTYPE_BYREF) == vtDst || (DBTYPE_PROPVARIANT|DBTYPE_BYREF) == vtDst ) { fDoCopy = FALSE;
// Slam the variant into the src data row buffer, then
// hand out a pointer to that variant.
// We need a home for the variant portion, and that
// place should do fine.
// The value portion of the variant will still be
// owned by the row buffer as a normal deferred value.
* (PROPVARIANT *) pbSrcData = SrcVar; * (PROPVARIANT **) pbDstData = (PROPVARIANT *) pbSrcData; cbDstLength = sizeof (PROPVARIANT); } else if ( isEquivalentType( vtDst, SrcVar.vt ) && SrcVar.VariantPointerInFirstWord() ) { // Grab the pointer value out of the variant; any ptr will do
fDoCopy = FALSE; * ( (LPWSTR *) pbDstData ) = SrcVar.pwszVal;
if ( SrcVar.vt & VT_ARRAY ) { cbDstLength = sizeof (SAFEARRAY *); } else { switch (SrcVar.vt ) { case VT_LPWSTR: cbDstLength = wcslen(SrcVar.pwszVal) * sizeof (WCHAR); break; case VT_LPSTR: cbDstLength = strlen(SrcVar.pszVal) * sizeof (char); break; case VT_BSTR: cbDstLength = sizeof (BSTR); break; case VT_CLSID: cbDstLength = sizeof (GUID); break; case VT_CF: cbDstLength = sizeof (CLIPDATA); break; default: tbDebugOut(( DEB_ERROR, "SrcVar.vt = 0x%x\n", SrcVar.vt )); Win4Assert( SrcVar.vt != VT_EMPTY && !"unexpected variant type!" ); } } } else if ( SrcVar.vt == vtDst ) { Win4Assert( vtDst & DBTYPE_VECTOR ); Win4Assert( vtDst != VT_VARIANT ); fDoCopy = FALSE; * (CAL *) pbDstData = SrcVar.cal; cbDstLength = 0; // vectors defined to be 0 len
} else fFreeDeferredSrc = TRUE; } else fDoCopy = FALSE; } else { RtlCopyMemory( &SrcVar, pbSrcData, sizeof SrcVar ); } } else SrcVar.Init( vtSrc, pbSrcData, rSrcColumn.GetValueSize() );
if ( fDoCopy ) { DstStatus = SrcVar.OLEDBConvert( pbDstData, rDstBinding.GetMaxLen(), vtDst, _Pool, cbDstLength, xDataConvert, _fExtendedTypes, ((DBBINDING&)rDstBinding).bPrecision, ((DBBINDING&)rDstBinding).bScale );
// Free the deferred value that was in a format that couldn't be
// used directly (and had to be converted).
if ( fFreeDeferredSrc ) { PropVariantClear( &SrcVar ); } }
// If accessor is byref and we had to allocate memory to return a
// deferred value, we have to tell the row buffer to let go of
// the memory when the HROW is released.
if ( ( DBSTATUS_S_OK == DstStatus ) && ( rSrcColumn.IsDeferred( pbSrc ) ) && ( rDstBinding.Binding().dwMemOwner == DBMEMOWNER_PROVIDEROWNED ) && ( ( vtDst & (DBTYPE_BYREF | DBTYPE_ARRAY | DBTYPE_VECTOR) ) || ( vtDst == VT_LPWSTR ) || ( vtDst == VT_LPSTR ) || ( vtDst == DBTYPE_BSTR ) ) ) { PROPVARIANT var; var.vt = vtDst;
if ( ( DBTYPE_VARIANT | DBTYPE_BYREF ) == vtDst || ( DBTYPE_PROPVARIANT | DBTYPE_BYREF ) == vtDst ) var = ** (PROPVARIANT **) pbDstData; else if ( vtDst & DBTYPE_VECTOR ) RtlCopyMemory( &var.calpwstr, pbDstData, sizeof DBVECTOR ); else var.pwszVal = * (WCHAR **) pbDstData;
tbDebugOut(( DEB_ITRACE, "save away type %x, val %x\n", (int) vtDst, ( vtDst & DBTYPE_VECTOR ) ? (WCHAR *) var.calpwstr.pElems : var.pwszVal ) );
CDeferredValue value( hrow, var ); rRowBuffer.AddDeferredValue( value ); rRowBuffer.SetByrefData( pbSrc ); }
return DstStatus; } //_ComplexCopy
//+-------------------------------------------------------------------------
//
// Member: CRowDataAccessor::_ByRefCopy, private
//
// Synopsis: Copy a ByRef value. This hands out a live pointer into the
// row buffer's storage that the client can read from but not
// write to or try to free.
//
// Arguments: [rDstBinding] -- binding for the destination
// [pbSrc] -- source row buffer
// [rSrcColumn] -- source column description
// [vtSrc] -- type of the source
// [rSrcVar] -- variant to fill as a copy of the source
// data in variant form if the source data
// is not already a variant (and thus has
// a length column in the row buffer)
// [pbSrcData] -- pointer to source data
// [vtDst] -- type of the destination value
// [pbDstData] -- where to write the value
//
// Returns: DBSTATUS -- status of the column copy
//
// Notes: Coercions are not supported. This makes sense -- if you
// want slow coercions you shouldn't be binding byref in the
// first place.
//
// History: 25 Jul 95 dlee created
//
//--------------------------------------------------------------------------
DBSTATUS CRowDataAccessor::_ByRefCopy( CDataBinding & rDstBinding, BYTE * pbSrc, CTableColumn & rSrcColumn, VARTYPE vtSrc, CTableVariant & rSrcVar, BYTE * pbSrcData, VARTYPE vtDst, BYTE * pbDstData ) { DBSTATUS DstStatus = DBSTATUS_S_OK; RtlZeroMemory( &rSrcVar, sizeof CTableVariant );
if ( VT_EMPTY == vtSrc ) return DBSTATUS_S_ISNULL;
if ( rDstBinding.Binding().wType & DBTYPE_VECTOR ) { if ( DBTYPE_VARIANT == vtSrc ) { CTableVariant & varnt = * ( (CTableVariant *) pbSrcData );
tbDebugOut(( DEB_ACCESSOR, "byref copy, vtDst 0x%x varnt.vt 0x%x\n", vtDst, varnt.vt ));
if ( isEquivalentType( vtDst, varnt.vt ) ) RtlCopyMemory( pbDstData, &(varnt.caub), sizeof DBVECTOR ); else DstStatus = NullOrCantConvert( varnt.vt ); } else if ( isEquivalentType( vtDst, vtSrc ) ) { RtlCopyMemory( pbDstData, pbSrcData, sizeof DBVECTOR );
// Make a variant in case length is bound for this column
rSrcVar.vt = vtSrc; RtlCopyMemory( &(rSrcVar.caub), pbSrcData, sizeof DBVECTOR ); } else { DstStatus = DBSTATUS_E_CANTCONVERTVALUE; } } else if ( rDstBinding.Binding().wType & DBTYPE_ARRAY ) { if ( DBTYPE_VARIANT == vtSrc ) { CTableVariant & varnt = * ( (CTableVariant *) pbSrcData );
tbDebugOut(( DEB_ACCESSOR, "byref copy, vtDst 0x%x varnt.vt 0x%x\n", vtDst, varnt.vt ));
if ( isEquivalentType( vtDst, varnt.vt ) ) RtlCopyMemory( pbDstData, &(varnt.parray), sizeof (SAFEARRAY *) ); else DstStatus = NullOrCantConvert( varnt.vt ); } else if ( isEquivalentType( vtDst, vtSrc ) ) { RtlCopyMemory( pbDstData, pbSrcData, sizeof (SAFEARRAY *) );
// Make a variant in case length is bound for this column
rSrcVar.vt = vtSrc; RtlCopyMemory( &(rSrcVar.parray), pbSrcData, sizeof (SAFEARRAY *) ); } else { DstStatus = DBSTATUS_E_CANTCONVERTVALUE; } } else { Win4Assert( (rDstBinding.Binding().wType & DBTYPE_BYREF) || (rDstBinding.Binding().wType == DBTYPE_BSTR) );
if ( DBTYPE_VARIANT == vtSrc ) { CTableVariant & varnt = * ( (CTableVariant *) pbSrcData );
tbDebugOut(( DEB_ACCESSOR, "byref non-vector copy, vtDst 0x%x, varnt.vt 0x%x\n", vtDst, varnt.vt ));
if ((rDstBinding.Binding().wType & ~DBTYPE_BYREF) == DBTYPE_VARIANT) { // Just return a pointer to the stored variant
* (VARIANT **) pbDstData = (VARIANT *) pbSrcData; } else if ( ( varnt.VariantPointerInFirstWord() ) && ( isEquivalentType( vtDst, varnt.vt ) ) ) { // Grab the pointer value out of the variant; any ptr will do
* ( (LPWSTR *) pbDstData ) = varnt.pwszVal;
// Make a variant in case length is bound for this column
rSrcVar.vt = varnt.vt; rSrcVar.pwszVal = varnt.pwszVal; } else { * ( (LPWSTR *) pbDstData ) = 0; DstStatus = NullOrCantConvert( varnt.vt ); } } else { // The constructor verified everything is fine -- copy the ptr
* (void **) pbDstData = * (void **) pbSrcData;
// Make a variant in case length is bound for this column
rSrcVar.vt = vtSrc; rSrcVar.pwszVal = * (WCHAR **) pbSrcData; } }
if ( rSrcColumn.IsNull( pbSrc ) ) { tbDebugOut(( DEB_ITRACE, "byref column status IsNull\n" )); DstStatus = DBSTATUS_S_ISNULL; } return DstStatus; } //_ByRefCopy
//+-------------------------------------------------------------------------
//
// Member: CRowDataAccessor::_CopyColumn, private
//
// Synopsis: Return a set of row data to the caller
//
// Arguments: [pbDst] -- destination row buffer
// [rDstBinding] -- binding for the destination
// [pbSrc] -- source row buffer
// [rSrcColumn] -- source column description
// [rSrcSet] -- set of source columns
// [iColumn] -- column being copied
// [rRowBuffer] -- row buffer
// [hrow] -- hrow being retrieved
// [xDataConvert] -- smart pointer to IDataConvert interface.
// this is to be passed along and used finally
// in COLEDBVariant::CopyOrCoerce
//
// Returns: DBSTATUS -- status of the column copy
//
// History: 21 Nov 1994 dlee created
// 09 Jan 1998 vikasman added xDataConvert parameter
//
//--------------------------------------------------------------------------
DBSTATUS CRowDataAccessor::_CopyColumn( BYTE * pbDst, CDataBinding & rDstBinding, BYTE * pbSrc, CTableColumn & rSrcColumn, CTableColumnSet & rSrcSet, DBORDINAL iColumn, CRowBuffer & rRowBuffer, HROW hrow, XInterface<IDataConvert>& xDataConvert) { DBSTATUS DstStatus = DBSTATUS_S_OK; DBLENGTH cbDstLength = 0; CTableVariant varLen; BOOL fVariantValid = FALSE; BOOL fLengthValid = FALSE; BOOL fStatusValid = FALSE;
// pull out the data types for clarity below
VARTYPE vtDst = (VARTYPE) rDstBinding.Binding().wType; VARTYPE vtSrc = rSrcColumn.GetStoredType();
if (DBPART_VALUE & rDstBinding.Binding().dwPart) { // the row buffer promises NOT to set the byref bit for these.
// we'll break elsewhere if this is not true.
Win4Assert(vtSrc != ( VT_LPWSTR | VT_BYREF ) ); Win4Assert(vtSrc != ( VT_LPSTR | VT_BYREF ) );
BYTE *pbSrcData = pbSrc + rSrcColumn.GetValueOffset(); BYTE *pbDstData = pbDst + rDstBinding.Binding().obValue;
tbDebugOut(( DEB_ACCESSOR, "copying column %d, vtsrc 0x%x, vtdst 0x%x\n", iColumn, vtSrc, vtDst ));
// vpath can be null if not an IIS root
if ( ( iColumn == _iVpathBinding ) && ( !rSrcColumn.IsNull( pbSrc ) ) ) { Win4Assert(VT_VARIANT == vtSrc || VT_LPWSTR == vtSrc || VT_BSTR == vtSrc); LPWSTR pwszPath = (LPWSTR) pbSrcData; if (VT_VARIANT == vtSrc) pwszPath = ((PROPVARIANT *)pbSrcData)->pwszVal; if (pwszPath) ConvertBackslashToSlash(pwszPath); }
if ( DBTYPE_IUNKNOWN == vtDst ) { DstStatus = _BindToObject( pbDstData, rDstBinding, pbSrc, rSrcColumn, rSrcSet ); } else { // Transfer a data value
USHORT cbData, cbAlignment, rgfFlags; CTableVariant::VartypeInfo(vtSrc, cbData, cbAlignment, rgfFlags);
if ( rSrcColumn.IsNull( pbSrc ) && (vtDst & DBTYPE_BYREF) == 0 ) { tbDebugOut(( DEB_ITRACE, "column %d status IsNull -> VT_EMPTY\n", iColumn ));
RtlZeroMemory( pbDstData, rDstBinding.GetMaxLen() ); DstStatus = DBSTATUS_S_ISNULL; } else if ( ( vtDst == vtSrc ) && ( vtDst != VT_VARIANT ) && ( 0 == (rgfFlags & CTableVariant::ByRef ) ) && ( 0 != (rgfFlags & CTableVariant::StoreDirect ) ) ) { // Data column equivalent and not indirect, so just copy it.
// Storage props (except filename/path) will do this.
cbDstLength = rSrcColumn.GetValueSize(); fLengthValid = TRUE; RtlCopyMemory( pbDstData, pbSrcData, cbDstLength ); } else if ( ( rDstBinding.Binding().dwMemOwner == DBMEMOWNER_PROVIDEROWNED ) && ( ! rSrcColumn.IsDeferred( pbSrc ) ) && ( ( vtDst & DBTYPE_BYREF ) || ( vtDst & DBTYPE_VECTOR ) || ( vtDst & VT_ARRAY ) || ( vtDst == DBTYPE_BSTR ) ) ) { // Efficiently bound filename/path, etc. will do this
DstStatus = _ByRefCopy( rDstBinding, pbSrc, rSrcColumn, vtSrc, varLen, pbSrcData, vtDst, pbDstData );
if ( DBSTATUS_S_OK == DstStatus ) rRowBuffer.SetByrefData( pbSrc ); fVariantValid = TRUE; } else if (vtDst == DBTYPE_BYTES && rDstBinding.GetMaxLen() > 1 && CTableVariant::TableIsStoredInline( rgfFlags, vtSrc )) { // Special case for small fixed-length fields
DBLENGTH cbCopy = rSrcColumn.GetValueSize(); if (rDstBinding.GetMaxLen() < cbCopy) { cbCopy = rDstBinding.GetMaxLen(); DstStatus = DBSTATUS_S_TRUNCATED; }
RtlCopyMemory(pbDstData, pbSrcData, cbCopy); cbDstLength = cbCopy; fLengthValid = TRUE; } else if (vtDst == DBTYPE_BYTES && rDstBinding.GetMaxLen() > 1 && vtSrc == VT_VARIANT) { // Special case for small fixed-length fields from a variant
CTableVariant * pVarnt = (CTableVariant *)pbSrcData; pVarnt->VartypeInfo(pVarnt->vt, cbData, cbAlignment, rgfFlags);
if (rgfFlags & CTableVariant::SimpleType) { DBLENGTH cbCopy = cbData; if (rDstBinding.GetMaxLen() < cbCopy) { cbCopy = rDstBinding.GetMaxLen(); DstStatus = DBSTATUS_S_TRUNCATED; } RtlCopyMemory(pbDstData, &(pVarnt->lVal), cbCopy); cbDstLength = cbCopy; fLengthValid = TRUE; } else { DstStatus = DBSTATUS_E_CANTCONVERTVALUE; } } else { // Copying to/from a variant or a coercion is required.
DstStatus = _ComplexCopy( rDstBinding, pbSrc, rSrcColumn, iColumn, vtSrc, cbDstLength, pbSrcData, vtDst, pbDstData, rSrcSet, rRowBuffer, hrow, xDataConvert);
fLengthValid = TRUE; } }
// indicate status has been validated
fStatusValid = TRUE;
// bump the chapter reference count if we successfully transferred
// a chapter value
if ( DBSTATUS_S_OK == DstStatus && rDstBinding.IsChapter() ) rRowBuffer.ReferenceChapter( pbSrc ); }
// fill-in the length if the client asked for it
if (DBPART_LENGTH & rDstBinding.Binding().dwPart) { DBLENGTH *pcbDst = (DBLENGTH *) (pbDst + rDstBinding.Binding().obLength);
if ( ( DBSTATUS_S_ISNULL == DstStatus ) || ( DBSTATUS_E_CANTCONVERTVALUE == DstStatus ) || ( 0 != ( vtDst & DBTYPE_VECTOR ) ) ) // ole-db spec says so
{ *pcbDst = 0; } else if ( fLengthValid ) { *pcbDst = cbDstLength; } else if (! isVariableLength( vtDst ) ) { if ( vtDst & VT_BYREF ) { USHORT cbWidth, cbAlign, fFlags; CTableVariant::VartypeInfo( vtDst & ~VT_BYREF, cbWidth, cbAlign, fFlags );
*pcbDst = cbWidth; } else { *pcbDst = rDstBinding.GetMaxLen(); } } else { DBLENGTH cbLen = 0;
if (fVariantValid) { Win4Assert ( varLen.vt != VT_EMPTY ); cbLen = varLen.VarDataSize(); } else { //
// DBBINDING doesn't want DBPART_VALUE
//
SCODE sc = S_OK; ULONG cbDstBufNeeded = 0; DBTYPE dbtypeSrc; COLEDBVariant SrcVar;
BYTE *pbSrcData = pbSrc + rSrcColumn.GetValueOffset(); RtlCopyMemory( &SrcVar, pbSrcData, sizeof SrcVar ); SrcVar.GetDstLength( xDataConvert, rDstBinding.Binding().wType, cbLen ); }
// WSTR and STR lengths shouldn't include the terminating NULL
if ( (vtDst & VT_TYPEMASK) == DBTYPE_WSTR || vtDst == VT_LPWSTR ) cbLen -= sizeof (WCHAR); else if ( (vtDst & VT_TYPEMASK) == DBTYPE_STR || vtDst == VT_LPSTR ) cbLen -= sizeof (char); else if (vtDst == DBTYPE_BSTR) cbLen = sizeof (BSTR);
*pcbDst = cbLen; } }
if (! fStatusValid) { if ( rSrcColumn.IsStatusStored() && rSrcColumn.GetStatus( pbSrc ) == CTableColumn::StoreStatusNull ) DstStatus = DBSTATUS_S_ISNULL; }
if (DBPART_STATUS & rDstBinding.Binding().dwPart) { * (DBSTATUS *) (pbDst + rDstBinding.Binding().obStatus) = DstStatus; }
return DstStatus; } //_CopyColumn
//+-------------------------------------------------------------------------
//
// Member: CRowDataAccessor::GetData, public
//
// Synopsis: Copies data into the buffer as specified when the accessor
// was created
//
// Arguments: [hRow] -- row whose data is copied
// [pData] -- where data is written
// [rBufferSet] -- object useful for transforming an HROW
// into a buffer and a column layout
// [rQuery] -- query to use for this getdata
// [colInfo] -- info about the columns
// [xDataConvert] -- smart pointer to IDataConvert interface.
// this is to be passed along and used finally
// in COLEDBVariant::CopyOrCoerce
//
// History: 21 Nov 1994 dlee created
// 09 Jan 1998 vikasman added xDataConvert parameter
//
//--------------------------------------------------------------------------
void CRowDataAccessor::GetData( HROW hRow, void * pData, CRowBufferSet & rBufferSet, PQuery & rQuery, CColumnsInfo & colInfo, XInterface<IDataConvert>& xDataConvert) {
if ( _idColInfo != colInfo.GetId() ) { // We have a different columnsInfo with which we must validate.
Validate( colInfo, 0 ); }
_pQuery = &rQuery; // query to be used for this data retrieval
// first find the source data buffer and its layout
CLock lock( rBufferSet.GetBufferLock() );
CTableColumnSet *pRowBufColSet; BYTE *pbSrc;
CRowBuffer &rRowBuffer = rBufferSet.Lookup( hRow, &pRowBufColSet, (void **) &pbSrc );
// now copy the data from the internal buffer to the user's buffer
ULONG cCopied = 0;
// We need to determine whether all the conversions failed or
// all of them succeeded or some failed/some passed
ULONG cSuccess = 0;
TRY { for ( cCopied = 0; cCopied < _cBindings; cCopied++ ) { CTableColumn *pSrcCol = rRowBuffer.Find( (ULONG) _aBindings[cCopied].GetDataColumn() );
if ( 0 == pSrcCol ) { // Somehow, the row buffer doesn't have a column that's in
// the accessor. This is an internal error.
Win4Assert(!"CRowDataAccessor::GetData couldn't find column binding"); }
DBSTATUS DstStatus = _CopyColumn( (BYTE *) pData, _aBindings[cCopied], pbSrc, *pSrcCol, *pRowBufColSet, cCopied, rRowBuffer, hRow, xDataConvert);
tbDebugOut(( DEB_ITRACE, "GetData column %d, status 0x%x\n", cCopied, DstStatus ));
// see if we have a coercion error. any other column status will
// not result in a special return code from GetData -- it's just
// reflected in the column status field (if the user asks for it)
if ( DBStatusOK( DstStatus ) ) { // Success
cSuccess++; } } } CATCH( CException, e ) { // fatal error -- free the data allocated so far for return to the
// client, then rethrow the error condition
for ( ULONG i = 0; i < cCopied; i++ ) if ( _aBindings[i].Binding().dwMemOwner != DBMEMOWNER_PROVIDEROWNED ) CTableVariant::Free( (BYTE *) pData + _aBindings[i].Binding().obValue, (VARTYPE) _aBindings[i].Binding().wType, _Pool );
RETHROW(); } END_CATCH;
if ( cSuccess == _cBindings ) // takes care of _cBindings == 0 case
{ // AllSuccess
; // do nothing
} else if ( cSuccess == 0 ) { // AllFail
QUIETTHROW( CException( DB_E_ERRORSOCCURRED ) ); } else { // SomeSuccess/SomeFail
QUIETTHROW( CException( DB_S_ERRORSOCCURRED ) ); }
} //GetData
//+-------------------------------------------------------------------------
//
// Member: CRowDataAccessorByRef::GetData, public
//
// Synopsis: Copies data into the buffer as specified when the accessor
// was created. For provider-owned memory with variant byref
// binding.
//
// Arguments: [hRow] -- row whose data is copied
// [pData] -- where data is written
// [rBufferSet] -- object useful for transforming an HROW
// into a buffer and a column layout
// [rQuery] -- query to use for this getdata
// [colInfo] -- info about the columns
// [xDataConvert] -- smart pointer to IDataConvert interface.
// this is to be passed along and used finally
// in COLEDBVariant::CopyOrCoerce
//
// History: 09 Apr 1996 dlee created
// 09 Jan 1998 vikasman added xDataConvert parameter
//
//--------------------------------------------------------------------------
void CRowDataAccessorByRef::GetData( HROW hRow, void * pData, CRowBufferSet & rBufferSet, PQuery & rQuery, CColumnsInfo & colInfo, XInterface<IDataConvert> & xDataConvert) { // we validated the column info on construction
Win4Assert ( _idColInfo == colInfo.GetId() );
// first find the source data buffer and its layout
CLock lock( rBufferSet.GetBufferLock() );
CTableColumnSet *pRowBufColSet; BYTE *pbSrc;
CRowBuffer &rRowBuffer = rBufferSet.Lookup( hRow, &pRowBufColSet, (void **) &pbSrc );
// now copy the data from the internal buffer to the user's buffer
for ( unsigned iCol = 0; iCol < _cBindings; iCol++ ) { CDataBinding & dstDataBinding = _aBindings[ iCol ]; CTableColumn & rSrcCol = * rRowBuffer.Find( (ULONG) dstDataBinding.GetDataColumn() );
Win4Assert( 0 != &rSrcCol && "CRowDataAccessorByRef::GetData couldn't find column binding");
BYTE *pbSrcData = pbSrc + rSrcCol.GetValueOffset(); DBBINDING & DstBinding = dstDataBinding.Binding();
Win4Assert( (DBTYPE_VARIANT|DBTYPE_BYREF) == DstBinding.wType || (DBTYPE_PROPVARIANT|DBTYPE_BYREF) == DstBinding.wType ); Win4Assert( DBPART_VALUE == DstBinding.dwPart ); Win4Assert( VT_VARIANT == rSrcCol.GetStoredType() );
PROPVARIANT **ppDstVar = (PROPVARIANT **) ( ( (BYTE*) pData ) + DstBinding.obValue );
if ( !rSrcCol.IsDeferred( pbSrc ) ) { // Convert '\' to '/' if this is the vpath.
// Vpath can be null if the file is not in an indexed IIS vroot.
if ( ( iCol == _iVpathBinding ) && ( !rSrcCol.IsNull( pbSrc ) ) ) { Win4Assert( VT_LPWSTR == ((PROPVARIANT *)pbSrcData)->vt || VT_BSTR == ((PROPVARIANT *)pbSrcData)->vt );
LPWSTR pwszPath = ((PROPVARIANT *)pbSrcData)->pwszVal; if (pwszPath) ConvertBackslashToSlash(pwszPath); }
*ppDstVar = (PROPVARIANT *) pbSrcData;
#if CIDBG==1
if ( rSrcCol.IsNull( pbSrc ) ) { Win4Assert( VT_EMPTY == (*ppDstVar)->vt ); } #endif // CIDBG==1
} else { // deferred copies can go through this slow code path
_pQuery = &rQuery; // query to be used for this data retrieval
DBLENGTH cbDstLen;
_ComplexCopy( _aBindings[iCol], pbSrc, rSrcCol, iCol, rSrcCol.GetStoredType(), cbDstLen, pbSrcData, DBTYPE_PROPVARIANT | DBTYPE_BYREF, (BYTE *) ppDstVar, *pRowBufColSet, rRowBuffer, hRow, xDataConvert); }
// bump the chapter reference count if we transferred
// a chapter value
if ( dstDataBinding.IsChapter() ) rRowBuffer.ReferenceChapter( pbSrc ); } rRowBuffer.SetByrefData( pbSrc ); } //GetData
//+-------------------------------------------------------------------------
//
// Function: CreateAnAccessor
//
// Synopsis: Creates an accessor object deriving from CAccessor that is
// best-suited to handling the binding flags.
//
// Arguments: [dwAccessorFlags] -- read/write access requested
// [cBindings] -- # of bindings in rgBindings
// [rgBindings] -- array of bindings
// [rgBindStatus] -- array of binding statuses
// [fExtTypes] -- TRUE if extended variants are supported
// [pColumns] -- column info, may be 0
//
// History: 9 Apr 96 dlee created
//
//--------------------------------------------------------------------------
CAccessor * CreateAnAccessor( DBACCESSORFLAGS dwAccessorFlags, DBCOUNTITEM cBindings, const DBBINDING * rgBindings, DBBINDSTATUS * rgBindStatus, BOOL fExtTypes, void * pCreator, CColumnsInfo * pColumns ) { BOOL fByRefCandidate = TRUE;
// CRowDataAccessorByRef candidates must:
// - have a non-0 pColumns
// - allow extended variant types (PROPVARIANT)
// - only bind to VALUE, not STATUS or LENGTH
// - only bind as DBTYPE_PROPVARIANT or DBTYPE_VARIANT byref
// - only bind with DBMEMOWNER_PROVIDEROWNED
//
// Accessors that don't meet these criteria go through the slower
// CRowDataAccessor.
if ( ( 0 == pColumns ) || ( dwAccessorFlags != DBACCESSOR_ROWDATA ) || ! fExtTypes ) fByRefCandidate = FALSE;
for ( DBORDINAL iBinding = 0; fByRefCandidate && iBinding < cBindings; iBinding++ ) { DBBINDING const & b = rgBindings[ iBinding ];
if ( b.dwPart != DBPART_VALUE || b.dwMemOwner != DBMEMOWNER_PROVIDEROWNED || (( b.wType != (DBTYPE_PROPVARIANT|DBTYPE_BYREF) ) && (! fExtTypes || b.wType != (DBTYPE_VARIANT|DBTYPE_BYREF) ) ) ) fByRefCandidate = FALSE; }
tbDebugOut(( DEB_ITRACE, "accessor can be byref: %d\n", fByRefCandidate ));
if ( fByRefCandidate ) return new CRowDataAccessorByRef( dwAccessorFlags, cBindings, rgBindings, rgBindStatus, TRUE, pCreator, pColumns );
return new CRowDataAccessor( dwAccessorFlags, cBindings, rgBindings, rgBindStatus, fExtTypes, pCreator, pColumns ); } //CreateAnAccessor
|