//------------------------------------------------------------------------- // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1992 - 1995 // // File: caccess.cxx // // Contents: Microsoft OleDB/OleDS Data Source Object for ADSI // // CImpIAccessor object implementing the IAccessor interface. // // History: 10-01-96 shanksh Created. //------------------------------------------------------------------------- #include "oleds.hxx" #pragma hdrstop //------------------------------------------------------------------------- // CImpIAccessor::CImpIAccessor // // CImpIAccessor constructor. // //------------------------------------------------------------------------- CImpIAccessor::CImpIAccessor ( void *pParentObj, //@parm IN | Parent object pointer LPUNKNOWN pUnkOuter //@parm IN | Outer Unknown pointer ) { // Initialize simple member vars _pUnkOuter = pUnkOuter; _pObj = pParentObj; _pextbuffer = NULL; _dwStatus = 0; _pIDC = NULL; ENLIST_TRACKING(CImpIAccessor); // This section is for serializing &CreateAccessor. InitializeCriticalSection(&_criticalsectionAccessor); return; } //------------------------------------------------------------------------- // CImpIAccessor::~CImpIAccessor // // @mfunc CImpIAccessor destructor. // // @rdesc NONE //------------------------------------------------------------------------- CImpIAccessor::~CImpIAccessor ( void ) { ULONG_PTR hAccessor, hAccessorLast; PADSACCESSOR pADsaccessor, pADsaccessorBadHandle; // Cleanup only if there's anything to clean. if( _pextbuffer ) { // Get a pointer to the special "BadHandle" accessor. _pextbuffer->GetItemOfExtBuffer(hAccessorBadHandle, &pADsaccessorBadHandle); // Get the number of available accessor handles. _pextbuffer->GetLastItemHandle(hAccessorLast); // Loop through the reminding accessor handles deleting those which // are referenced only by this object and refcount decrementing the // other ones for (hAccessor =1; hAccessor <=hAccessorLast; hAccessor++) { // Get a pointer to the next accessor. _pextbuffer->GetItemOfExtBuffer(hAccessor, &pADsaccessor); // For valid accessors just delete them. if( pADsaccessor != pADsaccessorBadHandle ) DeleteADsAccessor(pADsaccessor); } // Now delete the buffer which holds accessor handles. delete _pextbuffer; } if( _pIMalloc ) _pIMalloc->Release(); if( _pIDC ) _pIDC->Release(); // Get rid of the critical section. DeleteCriticalSection(&_criticalsectionAccessor); return; } //------------------------------------------------------------------------- // CImpIAccessor::FInit // // @mfunc Initialize the Accessor implementation object. // // @rdesc Did the Initialization Succeed // @flag S_OK initialization succeeded, // @flag E_OUTOFMEMORY initialization failed because of // memory allocation problem, //------------------------------------------------------------------------- STDMETHODIMP CImpIAccessor::FInit ( void ) { HRESULT hr; PADSACCESSOR pADsaccessorBadHandle; // Prepare a special, "Bad Handle" accessor. pADsaccessorBadHandle = (PADSACCESSOR)((BYTE *)&_dwGetDataTypeBadHandle); _dwGetDataTypeBadHandle = ACCESSORTYPE_INERROR; // Create the ExtBuffer array to hold pointers to malloc'd accessors. _pextbuffer = (LPEXTBUFF) new CExtBuff; if (_pextbuffer == NULL || !(_pextbuffer->FInit( sizeof(PADSACCESSOR), &pADsaccessorBadHandle ) )) RRETURN( E_OUTOFMEMORY ); if( !_pIDC ) { hr = CoCreateInstance( CLSID_OLEDB_CONVERSIONLIBRARY, NULL, CLSCTX_INPROC_SERVER, IID_IDataConvert, (void **)&_pIDC ); if ( FAILED(hr) ) { RRETURN (hr); } } hr = CoGetMalloc(MEMCTX_TASK, &_pIMalloc); RRETURN (hr); } //------------------------------------------------------------------------- // CImpIAccessor::AddRefAccessor // // @mfunc Adds a reference count to an existing accessor. // // @rdesc Did release of the accessor Succeed // @flag S_OK | accessor addrefed successfully, // @flag DB_E_BADACCESSORHANDLE | accessor could not be addrefed // because its handle was invalid. //------------------------------------------------------------------------- STDMETHODIMP CImpIAccessor::AddRefAccessor( HACCESSOR hAccessor, //@parm IN | handle of the accessor to release DBREFCOUNT *pcRefCounts //@parm OUT | count of references ) { PADSACCESSOR pADsaccessor, pADsaccessorBadHandle; HRESULT hr; // Clear in case of error below. if( pcRefCounts ) *pcRefCounts = 0; CAutoBlock cab( &(_criticalsectionAccessor) ); // Get a pointer to the accessor. _pextbuffer->GetItemOfExtBuffer(hAccessor, &pADsaccessor); // Get a pointer to the special "BadHandle" accessor. _pextbuffer->GetItemOfExtBuffer(hAccessorBadHandle, &pADsaccessorBadHandle); // If the handle is valid if (pADsaccessor != pADsaccessorBadHandle) { if (pADsaccessor->cRef <= 0) BAIL_ON_FAILURE( hr = E_FAIL ); pADsaccessor->cRef++; if (pcRefCounts) *pcRefCounts = pADsaccessor->cRef; RRETURN( S_OK ); } // otherwise complain about a bad handle. else RRETURN( DB_E_BADACCESSORHANDLE ); error: RRETURN( hr ); } //------------------------------------------------------------------------- // CImpIAccessor::CreateAccessor // // @mfunc Creates an accessor, which is a set of bindings that can be used to // send data to or retrieve data from the data cache. The method performs // extensive validations on the input bindings which include verification of // the access type requested, column availability, data conversions involved, // etc. // // @rdesc Returns one of the following values: // S_OK | creation of the accessor succeeded, // E_INVALIDARG | creation of the accessor failed because // phAccessor was a NULL pointer, // E_OUTOFMEMORY | creation of the accessor failed because of // memory allocation problem, // DB_E_CANTCREATEACCESSOR | DBROWSETFLAGS_MULTIPLEACCESSOR flag was not // set and a row-fetching method has already // been called. // OTHER | other result codes returned by called functions. //------------------------------------------------------------------------- STDMETHODIMP CImpIAccessor::CreateAccessor( DBACCESSORFLAGS dwAccessorFlags, DBCOUNTITEM cBindings, const DBBINDING rgBindings[], DBLENGTH cbRowSize, HACCESSOR *phAccessor, DBBINDSTATUS rgStatus[] ) { PADSACCESSOR pADsaccessor; ULONG_PTR hADsaccessor; HRESULT hr; ULONG dwGetDataType, dwSetDataType; DBCOUNTITEM ibind; BOOL fProviderOwned; DBBINDSTATUS bindstatus; ULONG wMask, wBaseType; if (phAccessor) *phAccessor = (HACCESSOR)0; // At the creation of the CAutoBlock object a critical section // is entered. Concurrent calls to CreateAccessor are entirely // legal but their interleaving cannot be allowed. The critical // section is left when this method terminate and the destructor // for CAutoBlock is called. CAutoBlock cab( &(_criticalsectionAccessor) ); // phAccessor must never be NULL, non-zero bindings count implies a // non-NULL array of bindings. if( phAccessor == NULL || (cBindings && rgBindings == NULL) ) RRETURN( E_INVALIDARG ); // cBindings is 0 and was called on the Command. if( cBindings == 0 ) RRETURN( DB_E_NULLACCESSORNOTSUPPORTED ); // dwAccessorFlags cannot take any other values than those legal. if( dwAccessorFlags & (~DBACCESSOR_VALID_FLAGS) ) RRETURN( DB_E_BADACCESSORFLAGS ); // Compute internal types for use by methods using accessors, DetermineTypes( dwAccessorFlags, &dwGetDataType, &dwSetDataType ); // and detect possible inconsistencies in AccessorFlags. if( dwGetDataType == GD_ACCESSORTYPE_INERROR || dwSetDataType == SD_ACCESSORTYPE_INERROR ) RRETURN( DB_E_BADACCESSORFLAGS ); // fix conformance test failure. If BYREF accessors are not supported, // (indicated by DBPROP_BYREFACCESSORS being FALSE), we should return // the right error else if( (DB_E_BYREFACCESSORNOTSUPPORTED == dwGetDataType) || (DB_E_BYREFACCESSORNOTSUPPORTED == dwSetDataType) ) RRETURN( DB_E_BYREFACCESSORNOTSUPPORTED ); // Initialize the status array to DBBINDSTATUS_OK. if( rgStatus ) memset(rgStatus, 0x00, (size_t)(cBindings*sizeof(DBBINDSTATUS))); hr = S_OK; fProviderOwned = FALSE; // Perform validations that apply to all types of accessors. for (ibind=0; ibind < cBindings; ibind++) { bindstatus = DBBINDSTATUS_OK; if( rgBindings[ibind].dwMemOwner != DBMEMOWNER_PROVIDEROWNED && rgBindings[ibind].dwMemOwner != DBMEMOWNER_CLIENTOWNED ) bindstatus = DBBINDSTATUS_BADBINDINFO; // The part to be bound must specify one or more out of three values, and if( (rgBindings[ibind].dwPart & (DBPART_VALUE |DBPART_LENGTH |DBPART_STATUS)) == 0 || // nothing else. (rgBindings[ibind].dwPart & ~(DBPART_VALUE |DBPART_LENGTH |DBPART_STATUS)) || // Is it a good type to bind to? !IsGoodBindingType(rgBindings[ibind].wType) ) bindstatus = DBBINDSTATUS_BADBINDINFO; // DBTYPE_BYREF, DBTYPE_VECTOR and DBTYPE_ARRAY cannot be combined. else if( (wMask = (rgBindings[ibind].wType & TYPE_MODIFIERS)) && wMask != DBTYPE_BYREF && wMask != DBTYPE_VECTOR && wMask != DBTYPE_ARRAY ) bindstatus = DBBINDSTATUS_BADBINDINFO; else if ((wBaseType = (rgBindings[ibind].wType & ~TYPE_MODIFIERS)) == DBTYPE_EMPTY || wBaseType == DBTYPE_NULL) bindstatus = DBBINDSTATUS_BADBINDINFO; // DBTYPE_ARRAY and DBTYPE_VECTOR are not supported types else if( (rgBindings[ibind].wType & DBTYPE_ARRAY) || (rgBindings[ibind].wType & DBTYPE_VECTOR) ) bindstatus = DBBINDSTATUS_UNSUPPORTEDCONVERSION; // dwFlags was DBBINDFLAG_HTML and the type was not a String else if ( rgBindings[ibind].dwFlags && (rgBindings[ibind].dwFlags != DBBINDFLAG_HTML || (wBaseType != DBTYPE_STR && wBaseType != DBTYPE_WSTR && wBaseType != DBTYPE_BSTR)) ) { // Set Bind status to DBBINDSTATUS_BADBINDINFO bindstatus = DBBINDSTATUS_BADBINDINFO; } else if( rgBindings[ibind].wType == (DBTYPE_RESERVED | DBTYPE_BYREF) ) bindstatus = DBBINDSTATUS_BADBINDINFO; else if( rgBindings[ibind].dwMemOwner == DBMEMOWNER_PROVIDEROWNED ) { if( (rgBindings[ibind].wType & TYPE_MODIFIERS) == 0 && rgBindings[ibind].wType != DBTYPE_BSTR ) bindstatus = DBBINDSTATUS_BADBINDINFO; else fProviderOwned = TRUE; } if( bindstatus != DBBINDSTATUS_OK ) hr = DB_E_ERRORSOCCURRED; if( rgStatus ) rgStatus[ibind] = bindstatus; } // Check for errors in the bindings BAIL_ON_FAILURE( hr ); if( fProviderOwned && (dwGetDataType == GD_ACCESSORTYPE_READ || dwGetDataType == GD_ACCESSORTYPE_READ_OPTIMIZED) ) dwGetDataType = GD_ACCESSORTYPE_READ_COLS_BYREF; // Allocate space for the accessor structure. pADsaccessor = (ADSACCESSOR *) new BYTE [(size_t)(sizeof(ADSACCESSOR) + (cBindings ? (cBindings-1) : 0)* sizeof(DBBINDING))]; if( pADsaccessor == NULL ) RRETURN( E_OUTOFMEMORY ); if( cBindings ) memcpy( &(pADsaccessor->rgBindings[0]), &rgBindings[0], (size_t)(cBindings*sizeof(DBBINDING)) ); pADsaccessor->cBindings = cBindings; // For accessors valid for writing fill out a list of columns // so that notifications about their changes can be properly issued. if( dwSetDataType == SD_ACCESSORTYPE_READWRITE && cBindings ) { pADsaccessor->rgcol = new DBORDINAL [ (size_t)cBindings ]; if( pADsaccessor->rgcol == NULL ) { dwSetDataType = SD_ACCESSORTYPE_INERROR; } else for (ibind =0; ibind rgcol)[ibind] = rgBindings[ibind].iOrdinal; } else pADsaccessor->rgcol = NULL; // Allocate DBOBJECT structures supporting IUnknown types. for (ibind =0; ibind rgBindings[ibind].pObject = NULL; // Set accessor flags now, so that possible accessor cleanup routine would // know about additional structures for IUnknown types support. pADsaccessor->dwFlags = dwAccessorFlags; // Insert the new accessor pointer into the extensible buffer in which // accessors are stored. if( FAILED(hr = _pextbuffer->InsertIntoExtBuffer( &pADsaccessor, hADsaccessor )) ) { DeleteADsAccessor(pADsaccessor); RRETURN (hr); } pADsaccessor->cRef = 1; // Fill out the new accessor structure with the binding info. //pADsaccessor->obRowData = obRowData; pADsaccessor->cbRowSize = cbRowSize; // Return the accessor index in the extensible buffer as the accessor // handle. *phAccessor = (HACCESSOR) hADsaccessor; // Now can safely leave. RRETURN( NOERROR ); error: RRETURN( hr ); } //------------------------------------------------------------------------- // CImpIAccessor::DetermineTypes // // @mfunc Determines internal accessor type classification for use by GetData, // SetData and parameter handling code. Each of these has a separate type indicator // variable and a separate set of defined types. This allows a very efficient // handling of different accessors by methods that utilize them. // Types are determined on the basis of AccessorFlags. Incorrect combinations of // flags result in assignment of INERROR types. // // @rdesc NONE //------------------------------------------------------------------------- STDMETHODIMP_(void) CImpIAccessor::DetermineTypes( DBACCESSORFLAGS dwAccessorFlags, ULONG *pdwGetDataType, ULONG *pdwSetDataType ) { if( dwAccessorFlags & DBACCESSOR_PASSBYREF ) { *pdwGetDataType = (ULONG) DB_E_BYREFACCESSORNOTSUPPORTED; *pdwSetDataType = (ULONG) DB_E_BYREFACCESSORNOTSUPPORTED; return; } else if( dwAccessorFlags & DBACCESSOR_PARAMETERDATA ) { *pdwGetDataType = GD_ACCESSORTYPE_INERROR; *pdwSetDataType = GD_ACCESSORTYPE_INERROR; return; } // Determine types used in row data manipulations. if( dwAccessorFlags & DBACCESSOR_ROWDATA ) { // Determine accessor type from the point of // view of GetData. if( dwAccessorFlags & DBACCESSOR_OPTIMIZED ) *pdwGetDataType = GD_ACCESSORTYPE_READ_OPTIMIZED; else *pdwGetDataType = GD_ACCESSORTYPE_READ; // Determine accessor type from the point of // view of SetData. PASSBYREF is disallowed. *pdwSetDataType = SD_ACCESSORTYPE_READWRITE; } else { *pdwGetDataType = GD_ACCESSORTYPE_INERROR; *pdwSetDataType = GD_ACCESSORTYPE_INERROR; } return; } //------------------------------------------------------------------------- // CImpIAccessor::GetBindings // // @mfunc Returns bindings of an accessor. // // @rdesc Returns one of the following values: // S_OK | getting bindings succeeded, // E_INVALIDARG | getting bindings failed because // pdwAccessorFlags or pcBindings or // prgBindings was a NULL pointer, // E_OUTOFMEMORY | getting bindings failed because memory // allocation for the bindings array failed, // OTHER | other result codes stored on the accessor // object and singifying invalid accessor handle. //------------------------------------------------------------------------- STDMETHODIMP CImpIAccessor::GetBindings ( HACCESSOR hAccessor, // IN | accessor handle DBACCESSORFLAGS *pdwAccessorFlags, // OUT | stores accessor flags DBCOUNTITEM *pcBindings, // OUT | stores # of bindings DBBINDING **prgBindings // OUT | stores array of bindings ) { PADSACCESSOR pADsaccessor; PADSACCESSOR pADsaccessorBadHandle; DBCOUNTITEM ibind; DBOBJECT *pObject; if( pcBindings ) *pcBindings = 0; if( prgBindings ) *prgBindings = NULL; if( pdwAccessorFlags ) *pdwAccessorFlags = DBACCESSOR_INVALID; // Are arguments valid? if( pdwAccessorFlags == NULL || pcBindings == NULL || prgBindings == NULL ) RRETURN( E_INVALIDARG ); // Obtain pointer to the accessor to be used. _pextbuffer->GetItemOfExtBuffer(hAccessor, &pADsaccessor); // Get a pointer to the special "BadHandle" accessor. _pextbuffer->GetItemOfExtBuffer(hAccessorBadHandle, &pADsaccessorBadHandle); // Recognize bad accessor handles and return appropriate HRESULT. if( pADsaccessor == pADsaccessorBadHandle ) return DB_E_BADACCESSORHANDLE; // If necessary allocate the array of binding structures. if( pADsaccessor->cBindings ) { *prgBindings = (DBBINDING *) _pIMalloc->Alloc((ULONG)( pADsaccessor->cBindings*sizeof(DBBINDING) )); if( *prgBindings == NULL ) RRETURN ( E_OUTOFMEMORY ); // Copy bindings. memcpy( *prgBindings, pADsaccessor->rgBindings, (size_t)(pADsaccessor->cBindings*sizeof(DBBINDING)) ); // Loop through bindings and allocate and copy DBOBJECT structs. for (ibind=0; ibind cBindings; ibind++) { // If the accessor had failed bindings for SetData, we have // overloaded an unused structure member with status info, and // this needs to be cleaned now. (*prgBindings)[ibind].pTypeInfo = NULL; if( (*prgBindings)[ibind].pObject ) { pObject = (DBOBJECT *) _pIMalloc->Alloc( sizeof(DBOBJECT) ); if( pObject ) { memcpy( pObject, (*prgBindings)[ibind].pObject, sizeof(DBOBJECT) ); (*prgBindings)[ibind].pObject = pObject; } else { while (ibind--) if( (*prgBindings)[ibind].pObject ) _pIMalloc->Free((*prgBindings)[ibind].pObject); _pIMalloc->Free( *prgBindings ); *prgBindings = NULL; RRETURN( E_OUTOFMEMORY ); } } } } // Return the count of bindings, *pcBindings = pADsaccessor->cBindings; // and accessor flags. *pdwAccessorFlags = (pADsaccessor->dwFlags & ~DBACCESSOR_REFERENCES_BLOB); return S_OK; } //------------------------------------------------------------------------- // CImpIAccessor::ReleaseAccessor // // @mfunc Releases accessor. If accessor handle is valid the corresponding accessor // is either freed (if no one else is using it) or its ref count is decremented. // // @rdesc Did release of the accessor Succeed // @flag S_OK | accessor released successfully, // @flag DB_E_BADACCESSORHANDLE | accessor could not be released because its // | handle was invalid. //------------------------------------------------------------------------- STDMETHODIMP CImpIAccessor::ReleaseAccessor ( HACCESSOR hAccessor, //@parm IN | handle of the accessor to release DBREFCOUNT *pcRefCounts //@parm OUT | ref count of the released accessor ) { PADSACCESSOR pADsaccessor, pADsaccessorBadHandle; if( pcRefCounts ) *pcRefCounts = 0; CAutoBlock cab( &(_criticalsectionAccessor) ); // Get a pointer to the accessor. _pextbuffer->GetItemOfExtBuffer(hAccessor, &pADsaccessor); // Get a pointer to the special "BadHandle" accessor. _pextbuffer->GetItemOfExtBuffer(hAccessorBadHandle, &pADsaccessorBadHandle); // If the handle is valid if( pADsaccessor != pADsaccessorBadHandle ) { ADsAssert(pADsaccessor->cRef > 0); // Delete if no one else is using it, otherwise if( pADsaccessor->cRef == 1 ) { // Delete the accessor structure itself, as well as allocations // hanging off this structure. DeleteADsAccessor(pADsaccessor); // Make sure this handle is marked as "Bad" for future. _pextbuffer->DeleteFromExtBuffer(hAccessor); } // decrement the refcount. else { pADsaccessor->cRef--; if( pcRefCounts ) *pcRefCounts = pADsaccessor->cRef; } return NOERROR; } // otherwise complain about a bad handle. else RRETURN( DB_E_BADACCESSORHANDLE ); } //------------------------------------------------------------------------- // CImpIAccessor::DeleteADsAccessor // // @mfunc Deletes structures hanging off the ADSACCESSOR and then deletes the // accessor structure itself. // // @rdesc NONE //------------------------------------------------------------------------- STDMETHODIMP_(void) CImpIAccessor::DeleteADsAccessor ( PADSACCESSOR pADsaccessor //@parm IN | Kagera accessor ptr ) { DBCOUNTITEM ibind; // Delete the list of affected columns. if( pADsaccessor->rgcol ) delete [] pADsaccessor->rgcol; // If the accessor references BLOBS then DBOBJECT structures describing // objects dealing with BLOBS need to be deallocated. for (ibind =0; ibind cBindings; ibind++) if( pADsaccessor->rgBindings[ibind].pObject ) delete pADsaccessor->rgBindings[ibind].pObject; delete [] pADsaccessor; } STDMETHODIMP CImpIAccessor::QueryInterface(REFIID iid, LPVOID FAR* ppv) { // Is the pointer bad? if( ppv == NULL ) RRETURN( E_INVALIDARG ); // Place NULL in *ppv in case of failure *ppv = NULL; if( IsEqualIID(iid, IID_IUnknown) ) { *ppv = (IAccessor FAR *) this; } else if( IsEqualIID(iid, IID_IAccessor) ) { *ppv = (IAccessor FAR *) this; } else { RRETURN( E_NOINTERFACE ); } AddRef(); return S_OK; } //--------------------------------------------------------------------------- // CreateBadAccessor // // Inserts a bad accessor into the array of accessors (indexed by accessor // handles). This is required so that inheritance of accessors from commands // works correctly. If there are any 'holes' in the command's array of // accessors, then a rowset created from the command should also inherit these // 'holes' i,e, the array of accessor handles should not be compacted to // eliminate these holes. This is done using this function. // //--------------------------------------------------------------------------- HRESULT CImpIAccessor::CreateBadAccessor(void) { PADSACCESSOR pADsaccessorBadHandle; ULONG_PTR hADsaccessor; HRESULT hr; // Get a pointer to the special "BadHandle" accessor. _pextbuffer->GetItemOfExtBuffer(hAccessorBadHandle, &pADsaccessorBadHandle); // ignore the returned accessor handle hr = _pextbuffer->InsertIntoExtBuffer( &pADsaccessorBadHandle, hADsaccessor); RRETURN( hr ); } //---------------------------------------------------------------------------