Creating and Using Accessors
Consumers describe the memory structure for their buffers through a process called binding. An accessor is a group of bindings. The sample provider supports a very basic type of accessor and cannot support reference accessors, which allow the consumer direct access to the rowset's data cache.
Accessors are implemented through IAccessor. You can create accessors with IAccessor::CreateAccessor and release accessors with IAccessor::ReleaseAccessor. You can use IAccessor::GetBindings to determine the bindings in an existing accessor. IAccessor::AddRefAccessor enables the consumer to add a reference count to an existing accessor. The following sections describe these methods.
Determining Supported Conversions
Before the consumer creates an accessor, it can call IConvertType::CanConvert to determine if the provider supports a particular conversion.
The source code for IConvertType::CanConvert follows; you can find the complete source code for IConvertType in CvtType.cpp.
//----------------------------------------------------------------------------
// CImpIConvertType::CanConvert
//
// @mfunc Used by consumer to determine provider support for a conversion
//
// @rdesc HRESULT indicating the status of the method
// @flag S_OK | Conversion supported
// @flag S_FALSE | Conversion unsupported
// @flag DB_E_BADCONVERTFLAG | dwConvertFlags was invalid
// @flag DB_E_BADCONVERTFLAG | called on rowset for DBCONVERTFLAG_PARAMETER
// @flag OTHER | HRESULTS returned from support functions
//
STDMETHODIMP CImpIConvertType::CanConvert
(
DBTYPE wFromType, //@parm IN | src type
DBTYPE wToType, //@parm IN | dst type
DBCONVERTFLAGS dwConvertFlags //@parm IN | conversion flags
)
{
//
// We only support conversions on Rowsets, we only allow DBCONVERTFLAGS_COLUMN
//
if( (dwConvertFlags & ~(DBCONVERTFLAGS_ISLONG |
DBCONVERTFLAGS_ISFIXEDLENGTH |
DBCONVERTFLAGS_FROMVARIANT)) != DBCONVERTFLAGS_COLUMN )
return (DB_E_BADCONVERTFLAG);
//
// Make sure that we check that the type is a variant if they say so
//
if( dwConvertFlags & DBCONVERTFLAGS_FROMVARIANT )
{
DBTYPE wVtType = wFromType & VT_TYPEMASK;
//
// Take out all of the Valid VT_TYPES (36 is VT_RECORD in VC 6)
//
if( (wVtType > VT_DECIMAL && wVtType < VT_I1) ||
((wVtType > VT_LPWSTR && wVtType < VT_FILETIME) && wVtType != 36) ||
(wVtType > VT_CLSID) )
return (DB_E_BADTYPE);
}
//
// Don't allow _ISLONG on fixed-length types
//
if( dwConvertFlags & DBCONVERTFLAGS_ISLONG )
switch( wFromType & ~(DBTYPE_RESERVED|DBTYPE_VECTOR|DBTYPE_ARRAY|DBTYPE_BYREF) )
{
case DBTYPE_BYTES:
case DBTYPE_STR:
case DBTYPE_WSTR:
case DBTYPE_VARNUMERIC:
break;
default:
return (DB_E_BADCONVERTFLAG);
}
//
// If the DC is not there, try to create it again
if( !g_pIDataConvert )
{
CoCreateInstance(CLSID_OLEDB_CONVERSIONLIBRARY,
NULL,
CLSCTX_INPROC_SERVER,
IID_IDataConvert,
(void**)&g_pIDataConvert);
//
// Tell the DC that we are 2.5
//
if( g_pIDataConvert )
{
DCINFO rgInfo[] = {{DCINFOTYPE_VERSION,{VT_UI4, 0, 0, 0, 0x0}}};
IDCInfo *pIDCInfo = NULL;
if( g_pIDataConvert->QueryInterface(IID_IDCInfo, (void **)&pIDCInfo) == S_OK &&
pIDCInfo )
{
// OLE DB Version 02.50
V_UI4(&rgInfo->vData) = 0x250;
pIDCInfo->SetInfo(NUMELEM(rgInfo),rgInfo);
pIDCInfo->Release();
}
}
else
return (S_FALSE);
}
//
// Ask the conversion library for the answer
//
return (g_pIDataConvert->CanConvert(wFromType, wToType));
}
Creating an Accessor
CreateAccessor associates a set of bindings with an accessor handle that is used to send data to or fetch data from the rowset's data cache. The sample provider supports only the DBACCESSOR_ROWDATA accessor flag, which specifies that the accessor is to be used for rowset data.
The source code for IAccessor::CreateAccessor follows; you can find the complete source code for IAccessor in Accessor.cpp.
// CImpIAccessor::CreateAccessor -----------------------------------------
//
// @mfunc Creates a set of bindings that can be used to send data
// to or retrieve data from the data cache.
//
// @rdesc HRESULT
// @flag S_OK | Method Succeeded
// @flag E_FAIL | Provider specific Error
// @flag E_INVALIDARG | pHAccessor was NULL, dwAccessorFlags was
// invalid, or cBindings was not 0 and
// rgBindings was NULL
// @flag E_OUTOFMEMORY | Out of Memory
// @flag DB_E_ERRORSOCCURRED | dwBindPart in an rgBindings element was invalid, OR
// | Column number specified was out of range, OR
// | Requested coercion is not supported.
// @flag OTHER | Other HRESULTs returned by called functions
//
STDMETHODIMP CImpIAccessor::CreateAccessor
(
DBACCESSORFLAGS dwAccessorFlags,
DBCOUNTITEM cBindings, //@parm IN | Number of Bindings
const DBBINDING rgBindings[], //@parm IN | Array of DBBINDINGS
DBLENGTH cbRowSize, //@parm IN | Number of bytes in consumer's buffer
HACCESSOR* phAccessor, //@parm OUT | Accessor Handle
DBBINDSTATUS rgStatus[] //@parm OUT | Binding status
)
{
PACCESSOR pAccessor;
HACCESSOR hAccessor;
DBCOUNTITEM cBind;
DBORDINAL cCols;
HRESULT hr;
// Check Parameters
if( (cBindings && !rgBindings) || (phAccessor == NULL) )
return ResultFromScode( E_INVALIDARG );
// init out params
*phAccessor = NULL;
// Check if we have a correct accessor type
if ( dwAccessorFlags & DBACCESSOR_PASSBYREF )
return ResultFromScode( DB_E_BYREFACCESSORNOTSUPPORTED );
// Only allow DBACCESSOR_ROWDATA and DBACCESSOR_OPTIMIZED
if ( (dwAccessorFlags & ~DBACCESSOR_OPTIMIZED ) != DBACCESSOR_ROWDATA )
return ResultFromScode( DB_E_BADACCESSORFLAGS );
// Check for NULL Accessor on the Command Object
// Also check for NULL Accessor on a read only rowset
if( (m_pObj->GetBaseObjectType() == BOT_COMMAND && !cBindings) ||
(m_pObj->GetBaseObjectType() == BOT_ROWSET && !cBindings && !((CRowset *)m_pObj)->SupportIRowsetChange()) )
return ResultFromScode( DB_E_NULLACCESSORNOTSUPPORTED );
// Check for Optimized Accessor on the Rowset Object after Fetch
if( (dwAccessorFlags & DBACCESSOR_OPTIMIZED) &&
m_pObj->GetBaseObjectType() == BOT_ROWSET && ((CRowset *)m_pObj)->m_cRows )
return ResultFromScode( DB_E_BADACCESSORFLAGS );
// Initialize the status array to DBBINDSTATUS_OK.
// 64 bit TODO
if ( rgStatus )
memset(rgStatus, 0x00, (ULONG)(cBindings * sizeof(DBBINDSTATUS)));
// Check on the bindings the user gave us.
for (cBind=0, hr=NOERROR; cBind < cBindings; cBind++)
{
// other binding problems forbidden by OLE-DB
const DBTYPE currType = rgBindings[cBind].wType;
const DBTYPE currTypePtr = currType &
(DBTYPE_BYREF|DBTYPE_ARRAY|DBTYPE_VECTOR);
const DBTYPE currTypeBase = currType &
~(DBTYPE_BYREF|DBTYPE_ARRAY|DBTYPE_VECTOR);
const DWORD currFlags = rgBindings[cBind].dwFlags;
cCols = rgBindings[cBind].iOrdinal;
// Check for a Bad Ordinal
if( m_pObj->GetBaseObjectType() == BOT_ROWSET )
{
// make sure column number is in range
if ( !(0 < cCols && cCols <= ((CRowset *) m_pObj)->m_cCols) )
{
// Set Bind status to DBBINDSTATUS_BADORDINAL
hr = ResultFromScode( DB_E_ERRORSOCCURRED );
if ( rgStatus )
rgStatus[cBind] = DBBINDSTATUS_BADORDINAL;
continue;
}
}
// At least one of these valid parts has to be set. In SetData I assume it is the case.
if ( !(rgBindings[cBind].dwPart & (DBPART_VALUE|DBPART_LENGTH|DBPART_STATUS)) )
{
// Set Bind status to DBBINDSTATUS_BADBINDINFO
hr = ResultFromScode( DB_E_ERRORSOCCURRED );
if ( rgStatus )
rgStatus[cBind] = DBBINDSTATUS_BADBINDINFO;
}
// dwPart is something other than value, length, or status
else if ( (rgBindings[cBind].dwPart & ~(DBPART_VALUE|DBPART_LENGTH|DBPART_STATUS)) )
{
// Set Bind status to DBBINDSTATUS_BADBINDINFO
hr = ResultFromScode( DB_E_ERRORSOCCURRED );
if ( rgStatus )
rgStatus[cBind] = DBBINDSTATUS_BADBINDINFO;
}
// wType was DBTYPE_EMPTY or DBTYPE_NULL
else if ( (currType==DBTYPE_EMPTY || currType==DBTYPE_NULL) )
{
// Set Bind status to DBBINDSTATUS_BADBINDINFO
hr = ResultFromScode( DB_E_ERRORSOCCURRED );
if ( rgStatus )
rgStatus[cBind] = DBBINDSTATUS_BADBINDINFO;
}
// wType was DBTYPE_BYREF or'ed with DBTYPE_EMPTY, NULL, or RESERVED
else if ( ((currType & DBTYPE_BYREF) &&
(currTypeBase == DBTYPE_EMPTY || currTypeBase == DBTYPE_NULL ||
currType & DBTYPE_RESERVED)) )
{
// Set Bind status to DBBINDSTATUS_BADBINDINFO
hr = ResultFromScode( DB_E_ERRORSOCCURRED );
if ( rgStatus )
rgStatus[cBind] = DBBINDSTATUS_BADBINDINFO;
}
// dwFlags was DBBINDFLAG_HTML and the type was not a String
else if ( currFlags && (currFlags != DBBINDFLAG_HTML ||
(currTypeBase != DBTYPE_STR &&
currTypeBase != DBTYPE_WSTR &&
currTypeBase != DBTYPE_BSTR)) )
{
// Set Bind status to DBBINDSTATUS_BADBINDINFO
hr = ResultFromScode( DB_E_ERRORSOCCURRED );
if ( rgStatus )
rgStatus[cBind] = DBBINDSTATUS_BADBINDINFO;
}
// wType was used with more than one type indicators
else if ( !(currTypePtr == 0 || currTypePtr == DBTYPE_BYREF ||
currTypePtr == DBTYPE_ARRAY || currTypePtr == DBTYPE_VECTOR) )
{
// Set Bind status to DBBINDSTATUS_BADBINDINFO
hr = ResultFromScode( DB_E_ERRORSOCCURRED );
if ( rgStatus )
rgStatus[cBind] = DBBINDSTATUS_BADBINDINFO;
}
// wType was a non pointer type with provider owned memory
else if ( !currTypePtr &&
rgBindings[cBind].dwMemOwner==DBMEMOWNER_PROVIDEROWNED )
{
// Set Bind status to DBBINDSTATUS_BADBINDINFO
hr = ResultFromScode( DB_E_ERRORSOCCURRED );
if ( rgStatus )
rgStatus[cBind] = DBBINDSTATUS_BADBINDINFO;
}
// we only support client owned memory
else if ( rgBindings[cBind].dwMemOwner != DBMEMOWNER_CLIENTOWNED )
{
// Set Bind status to DBBINDSTATUS_BADBINDINFO
hr = ResultFromScode( DB_E_ERRORSOCCURRED );
if ( rgStatus )
rgStatus[cBind] = DBBINDSTATUS_BADBINDINFO;
}
}
// Any errors amongst those checks?
if (hr != NOERROR)
{
return hr;
}
// Make a copy of the client's binding array, and the type of binding.
// 64 bit TODO
pAccessor = (ACCESSOR *) new BYTE[sizeof( ACCESSOR ) + ((ULONG)cBindings - 1) *sizeof( DBBINDING )];
if ( pAccessor == NULL )
return ResultFromScode( E_OUTOFMEMORY );
// We store a ptr to the newly created variable-sized ACCESSOR.
// We have an array of ptrs (to ACCESSOR's).
// The handle is the index into the array of ptrs.
// The InsertIntoExtBuffer function appends to the end of the array.
assert( m_pextbufferAccessor );
hr = m_pextbufferAccessor->InsertIntoExtBuffer(&pAccessor, hAccessor);
if ( FAILED( hr ) )
{
SAFE_DELETE( pAccessor );
return ResultFromScode( E_OUTOFMEMORY );
}
assert( hAccessor );
// Copy the client's bindings into the ACCESSOR.
pAccessor->dwAccessorFlags = dwAccessorFlags;
pAccessor->cBindings = cBindings;
pAccessor->cRef = 1; // Establish Reference count.
// 64 bit TODO
memcpy( &(pAccessor->rgBindings[0]), &rgBindings[0], (ULONG)cBindings*sizeof( DBBINDING ));
// fill out-param and return
*phAccessor = hAccessor;
return ResultFromScode( S_OK );
}
Returning Accessor Bindings
GetBindings returns the bindings in an existing accessor. The source code for IAccessor::GetBindings follows; you can find the complete source code for IAccessor in Accessor.cpp.
// CImpIAccessor::GetBindings --------------------------------------------------
//
// @mfunc Returns the bindings in an accessor
//
// @rdesc HRESULT
// @flag S_OK | Method Succeeded
// @flag E_INVALIDARG | pdwAccessorFlags/pcBinding/prgBinding were NULL
// @flag E_OUTOFMEMORY | Out of Memory
// @flag DB_E_BADACCESSORHANDLE | Invalid Accessor given
//
STDMETHODIMP CImpIAccessor::GetBindings
(
HACCESSOR hAccessor, //@parm IN | Accessor Handle
DBACCESSORFLAGS* pdwAccessorFlags, //@parm OUT | Binding Type flag
DBCOUNTITEM* pcBindings, //@parm OUT | Number of Bindings returned
DBBINDING** prgBindings //@parm OUT | Bindings
)
{
// Retrieve our accessor structure from the client's hAccessor,
// make a copy of the bindings for the user, then done.
PACCESSOR pAccessor;
ULONG cBindingSize;
// init out-params
if( pdwAccessorFlags )
*pdwAccessorFlags = 0;
if( pcBindings )
*pcBindings = 0;
if ( prgBindings )
*prgBindings = NULL;
// check parameters
if (!pdwAccessorFlags || !pcBindings || !prgBindings)
return ResultFromScode( E_INVALIDARG );
// Validate Accessor Handle
m_pextbufferAccessor->GetItemOfExtBuffer(hAccessor, &pAccessor);
if( !pAccessor )
return DB_E_BADACCESSORHANDLE;
// Allocate and return Array of bindings
// 64 bit TODO
cBindingSize = (ULONG)pAccessor->cBindings * sizeof( DBBINDING );
if ( cBindingSize )
*prgBindings = (DBBINDING *) PROVIDER_ALLOC( cBindingSize );
// Check the Allocation
if ( ( *prgBindings == NULL ) && ( cBindingSize ) )
return ResultFromScode( E_OUTOFMEMORY );
*pdwAccessorFlags = pAccessor->dwAccessorFlags;
*pcBindings = pAccessor->cBindings;
memcpy( *prgBindings, pAccessor->rgBindings, cBindingSize );
// all went well..
return ResultFromScode( S_OK );
}
Adding a Reference Count to an Existing Accessor
AddRefAccessor adds a reference count to an existing accessor. The source code for IAccessor::AddRefAccessor follows.
// CImpIAccessor::AddRefAccessor -----------------------------------------
//
// @mfunc Adds a reference count to an existing accessor
//
// @rdesc HRESULT
// @flag S_OK | Method Succeeded
// @flag E_FAIL | Provider specific Error
//
STDMETHODIMP CImpIAccessor::AddRefAccessor
(
HACCESSOR hAccessor, //@parm IN | Accessor Handle
DBREFCOUNT* pcRefCounts //@parm OUT | Reference Count
)
{
// Retrieve our accessor structure from the client's hAccessor,
// free it, then mark accessor ptr as unused.
// We do not re-use accessor handles. This way, we hope
// to catch more client errors. (Also, ExtBuffer doesn't
// maintain a free list, so it doesn't know how to.)
PACCESSOR pAccessor;
if( pcRefCounts )
*pcRefCounts = 0;
m_pextbufferAccessor->GetItemOfExtBuffer(hAccessor, &pAccessor);
if( !pAccessor )
return DB_E_BADACCESSORHANDLE;
InterlockedIncrement((LONG*)&(pAccessor->cRef));
if( pcRefCounts )
*pcRefCounts = (DBREFCOUNT)(pAccessor->cRef);
return ResultFromScode( S_OK );
}
Releasing an Accessor
ReleaseAccessor decrements the reference count on an accessor; when the reference count reaches zero, the accessor is released. The source code for IAccessor::ReleaseAccessor follows; you can find the complete source code for IAccessor in Accessor.cpp.
// CImpIAccessor::ReleaseAccessor ---------------------------------------
//
// @mfunc Releases an Accessor
//
// @rdesc HRESULT
// @flag S_OK | Method Succeeded
// @flag DB_E_BADACCESSORHANDLE | hAccessor was invalid
//
STDMETHODIMP CImpIAccessor::ReleaseAccessor
(
HACCESSOR hAccessor, //@parm IN | Accessor handle to release
DBREFCOUNT* pcRefCounts //@parm OUT | Reference Count
)
{
// Retrieve our accessor structure from the client's hAccessor,
// free it, then mark accessor ptr as unused.
// We do not re-use accessor handles. This way, we hope
// to catch more client errors. (Also, ExtBuffer doesn't
// maintain a free list, so it doesn't know how to.)
PACCESSOR pAccessor;
if( pcRefCounts )
*pcRefCounts = 0;
m_pextbufferAccessor->GetItemOfExtBuffer(hAccessor, &pAccessor);
if( !pAccessor )
return DB_E_BADACCESSORHANDLE;
// Free the actual structure.
InterlockedDecrement((LONG*)&(pAccessor->cRef));
if( pAccessor->cRef <= 0 )
{
SAFE_DELETE( pAccessor );
if( pcRefCounts )
*pcRefCounts = 0;
// Store a null in our array-of-ptrs,
// so we know next time that it is invalid.
// (operator[] returns a ptr to the space where the ptr is stored.)
*(PACCESSOR*) ((*m_pextbufferAccessor)[hAccessor]) = NULL;
}
else
{
if( pcRefCounts )
*pcRefCounts = (DBREFCOUNT)(pAccessor->cRef);
}
return ResultFromScode( S_OK );
}