Activation diagram for SELECT statement
The following diagram describes the interaction between the Provider and the CDSC during the processing of an SQL SELECT statement.
The basic steps are:
1 On OLE DB connection, the Provider (mrOleDB) creates a CDSC Source object. The creation of the Source object is based on the CDSC name in the format CDSCName.Source. After the CDSC Source object is created, the Provider calls Initialize.
2 When an OLE DB command is created against the Provider, a new command is created against the CDSC. For example, if five concurrent commands have been created against the Provider, five concurrent commands are created against the CDSC. The CDSC Command object is the mechanism by which the Provider connects with the underlying data.
3 For each of the identifiers (columns, tokens, variables, and so on) in the expressions in the SQL statement, the Provider appends a new binding. The CDSC IDataBindings::Append method performs the validation against the new bindings and does the work required to connect the bindings to the underlying data. The DataBinding object is created by the Provider. It is a generic object and, if you are creating a CDSC in Visual C++, there is no need to modify the code generated by the Visual C++ CDSC wizard. However, the Value object can be modified. Typically, you would do this so that any custom binding properties can be stored and access to the underlying data can be optimized. The following code is a Visual C++ example of an Append method using custom Value objects. (This example is from the Quanvert CDSC.)
_bstr_t bsName(_T("<?>"));
DataTypeConstants ColType;
HRESULT hr = CheckBinding(pItem, bsName, ColType);
if (FAILED(hr)) {
CDMError de(CTX.m_lOutputLCID, hr);
de.Format(QVT_E_ADD_BINDING, (LPCTSTR) bsName);
return XERR(de, hr, IID_IDataBindings);
}
// Replace the binding's value object with one of mine
CValue* pValue = 0;
CCtxObjectPtr<CValueCategorical, IValue> cval;
CCtxObjectPtr<CValueLong, IValue> lval;
CCtxObjectPtr<CValueDouble, IValue> dval;
CCtxObjectPtr<CValueText, IValue> tval;
CCtxObjectPtr<CValueBoolean, IValue> bval;
switch (ColType) {
case mtLong:
hr = lval.CreateInstance(m_Context);
pValue = lval;
break;
case mtDouble:
case mtDate:
hr = dval.CreateInstance(m_Context);
pValue = dval;
break;
case mtCategorical:
hr = cval.CreateInstance(m_Context);
pValue = cval;
break;
case mtText:
case mtObject:
hr = tval.CreateInstance(m_Context);
pValue = tval;
break;
case mtBoolean:
hr = bval.CreateInstance(m_Context);
pValue = bval;
break;
case mtNone:
default:
CDMError de(CTX.m_lOutputLCID, E_UNEXPECTED);
de.Format(QVT_E_VAR_TYPE, (LPCTSTR) bsName);
return XERR(de, de, IID_IDataBindings);
}
pValue->m_dvtValue.m_lInputLCID = m_Context.m_lInputLCID;
pValue->m_dvtValue.m_lOutputLCID = m_Context.m_lOutputLCID;
if (FAILED(hr)) {
CDMError de(CTX.m_lOutputLCID, hr);
de.FormatWinError(hr, DMN_E_CREATING_CLASS, pValue->DMObjectDescription());
return XERR(de, hr, IID_IDataBindings);
}
CComPtr<IColumn> pColumn;
hr = pItem->get_Column(&pColumn);
CHECK_COM_RESULT;
try {
// Tell the new Value where to get its data from
QvField *pField = m_pCommand->m_Fields.MakeField(bsName);
ATLASSERT(pField != 0);
if (cval != 0) { // categorical column
const QvFieldCategoric *pCatField = dynamic_cast<const QvFieldCategoric*>(pField);
if (pCatField == 0) {
ATLASSERT(!"Expected a categoric field for a categoric column");
hr = cval->SetMaxSize(64);
}
else {
hr = cval->SetMaxSize(pCatField->GetMaxElements());
}
if (FAILED(hr)) {
CDMError de(CTX.m_lOutputLCID, hr);
de.FormatWinError(hr, DMN_E_CREATING_CLASS, pValue->DMObjectDescription());
return XERR(de, hr, IID_IDataBindings);
}
// Give the Value its mapping from QV element indexes to MDM values
cval->m_pMapper = m_pCommand->m_pSource->GetValueMapping(bsName);
}
pValue->m_pField = pField;
}
catch (const CDMError &de) {
return XERR(de, de, IID_IDataBindings);
}
hr = pItem->putref_Value(pValue);
CHECK_COM_RESULT;
return CCollection<IDataBinding>::Add(pItem);
4 After all of the bindings have been created, the Provider calls MoveFirst in response to the OLE DB data request. At this point the Command object updates the values for the requested data bindings. There are two alternative approaches for this. The approach used in the Visual C++ XML CDSC is to copy the values from storage (using the XML DOM) to the Value objects. If this approach is taken, the generic Value objects created by the Visual C++ CDSC wizard can be used. The other approach is to create custom Value objects that are instructed by the Command object to update their values. This is the approach taken by Quanvert CDSC. The advantage of this approach is that the Value objects can be optimized for each data type, and the reading of values can be deferred until the Provider actually requests them using get_Value. For example, the result of MoveFirst / MoveNext would be to set or increment the storage position, without actually reading the value.
5 After the MoveFirst / MoveNext operation is complete, the Provider obtains the values for each of the bindings. Steps 4 to 5 are then repeated for each row that is requested by the application. Steps 2 to 5 are repeated for each concurrent command.
The CDSC command object is destroyed by the Provider when the OLE DB command / rowset is released.
See also