//+------------------------------------------------------------------------- // // 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 #pragma hdrstop #include #include #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& 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 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& 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& 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& 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 & 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