|
|
// Row.cxx : Implementation of CRow
#include "oleds.hxx"
#if (!defined(BUILD_FOR_NT40))
#include "atl.h"
#include "row.hxx"
#include "cstream.h"
HRESULT PackLargeInteger(LARGE_INTEGER *plargeint, PVARIANT pVarDestObject); #define ARRAYSIZE(x) sizeof((x))/sizeof((x)[0])
extern const WCHAR g_cwszSpecialColumnURL[] = L"RESOURCE_ABSOLUTE_PARSENAME"; extern const WCHAR g_cwszAdsPath[] = L"ADsPath";
//----------------------------------------------------------------------------
// Note on m_fIsTearOff and m_fGetColInfoFromRowset
//
// m_fIsTearOff is set to TRUE when the row is a tear-off from a rowset from
// the client's point of view i.e, the client created a rowset and got a row
// off the rowset. Thus, a call to GetSourceRowset returns a rowset interface
// pointer only if m_fIsTearOff is TRUE.
// m_fGetColInfoFromRowset is set to TRUE if the row should get the column
// info and the column values from the rowset. If FALSE, this information is
// obtained through ADSI calls.
//
// There are 4 cases of interest.
//
// 1) The row is obtained directly from a command object using a query which
// doesn't specify "SELECT *" i.e, not all attributes are requested OR the row
// is obtained directly from a session object (defaults to no "SELECT *"). This
// is implemented by actually creating a rowset and getting its first row.
// In this case m_fIsTearOff is FALSE, m_fGetColInfoFromRowset is
// TRUE and m_pSourceRowset is non-NULL. Thus, if the client called
// GetSourceRowset on the row, no interface pointer is returned. However, there
// is a rowset backing up this row and this is used for getting column info and
// column values.
//
// 2) The row is obtained directly from a command object using SELECT *
// as the query. In this case, m_fIsTearOff is FALSE, m_fGetColInfoFromRowset
// is FALSE and m_pSourceRowset is non-NULL. Though m_pSourceRowset is
// non-NULL, it is not used.
//
// An alternative scheme would be to to call CSession::Bind directly in this
// case which would obviate the need for keeping a rowset around.
//
// 3) The row is obtained from a rowset using GetRowFromHROW. In this case,
// m_fIsTearOff is TRUE. However, if the rowset was created from a command
// object using a SELECT * query, then m_fGetColInfoFromRowset is FALSE.
// Otherwise, fGetColInfoFromRowset is TRUE. m_pSourceRowset is non-NULL.
//
// 4) The row is created using IBindResource::Bind. In this case, m_fIsTearOff
// is FALSE, m_fGetColInfoFromRowset is FALSE and m_pSourceRowset is NULL.
//
// The following table summarizes the above 4 cases.
//
// m_fIsTearOff m_fGetColInfoFromRowset Description
// ------------ ----------------------- ------------
// T T GetRowFromHROW, not SELECT *
// F F Row obtained from command object
// using SELECT * OR
// Row got using IBindResource::Bind
// T F GetRowFromHROW, SELECT *
// F T Row obtained from command object
// without a SELECT * query OR
// Row obtained from session object
//
// Every row object has a special URL column. The URL column (and any row
// columns that are not in the source rowset) should appear after all the
// source rowset columns.
//
// ADsPath needs to be treated as a special case. If m_fGetColInfoFromRowset
// is TRUE, then we will get the ADsPath just by using the rowset's copy
// of the column. However, if m_fGetColInfoFromRowset is FALSE, then
// the row should return ADsPath if it is a tear-off AND ADsPath is one of the
// columns of the source rowset. This is required by the OLEDB spec
// since the row's columns should be a superset of the rowset's columns.
// Currently, the only case where this would happen is a SELECT * query that
// requests a rowset and then a row is obtained from that rowset. In this case,
// the row adds ADsPath to the columns it returns. To be consistent, if a row
// is obtained from a command object using a SELECT * query OR the row is
// obtained using IBindResource::Bind(), then ADsPath is added to the columns
// returned by the row.
//
//----------------------------------------------------------------------------
//
//////////////////////////////////////////////////////////////////////////////
//helper functions
//
//+---------------------------------------------------------------------------
//
// Function: GetIDataConvert
//
// Synopsis: Create OLE DB conversion library and store it in static.
//
// Arguments:
// [out] pointer to pointer to IDataConvert
//
// Returns: HRESULT
//----------------------------------------------------------------------------
HRESULT GetIDataConvert(IDataConvert** ppDataConvert) { HRESULT hr = S_OK; auto_rel<IDataConvert> pIDataConvert; auto_rel<IDCInfo> pIDCInfo; DCINFO rgInfo[] = {{DCINFOTYPE_VERSION, {VT_UI4, 0, 0, 0, 0x0200}}}; ADsAssert(ppDataConvert); hr = CoCreateInstance(CLSID_OLEDB_CONVERSIONLIBRARY, NULL, CLSCTX_INPROC_SERVER, IID_IDataConvert, (void **) &pIDataConvert); if (FAILED(hr)) goto exit; // Tell data convert our OLE DB version
hr = pIDataConvert->QueryInterface(IID_IDCInfo, (void **)&pIDCInfo); if( SUCCEEDED(hr) ) hr = pIDCInfo->SetInfo(ARRAYSIZE(rgInfo), rgInfo); // auto_rel operator = does the release
if( FAILED(hr) ) pIDataConvert = NULL; if (pIDataConvert) pIDataConvert->AddRef(); *ppDataConvert = pIDataConvert;
exit: RRETURN(hr); }
/////////////////////////////////////////////////////////////////////////////
//Internal methods
//
//+---------------------------------------------------------------------------
//
// Function: CRow::Initialize
//
// Synopsis: Initializes a directly bound CRow object as opposed to a row
// created from a rowset. See the other overloaded Initialize below.
//
// Returns: HRESULT
//----------------------------------------------------------------------------
STDMETHODIMP CRow::Initialize(PWSTR pwszURL, IUnknown *pSession, IAuthenticate *pAuthenticate, DWORD dwBindFlags, BOOL fIsTearOff, BOOL fGetColInfoFromRowset, CCredentials *pSessCreds, bool fBind) { HRESULT hr = S_OK; auto_leave cs_auto_leave(m_autocs); DWORD fAuthFlags; //Make sure we have a valid session.
ADsAssert(pSession); TRYBLOCK cs_auto_leave.EnterCriticalSection(); m_dwBindFlags = dwBindFlags; m_objURL = pwszURL; m_pSession = pSession; m_pSession->AddRef(); if (!fBind) //no need to bind. Just return.
RRETURN(S_OK);
m_fIsTearOff = fIsTearOff; m_fGetColInfoFromRowset = fGetColInfoFromRowset; // Any changes made below should also be made in the overloaded
// Initialize() below for consistency.
// Fix for 351040. Use explicit credentials first, then credentials in
// session object, then default credentials. Credential in session
// object will be the default credentials if we come in through the
// binder. But, if IBindResource is obtained from the session object,
// then the credentials may not be the default credentials. From ADO,
// using Open without specifying an active connection causes the
// binder to be invoked. If an active connection is specified, then
// IBindResource is obtained from the session object.
if(pAuthenticate) { hr = GetCredentialsFromIAuthenticate(pAuthenticate, m_objCredentials); if(FAILED(hr)) BAIL_ON_FAILURE(E_INVALIDARG);
fAuthFlags = m_objCredentials.GetAuthFlags(); m_objCredentials.SetAuthFlags(fAuthFlags | ADS_SECURE_AUTHENTICATION);
hr = GetDSInterface( pwszURL, m_objCredentials, IID_IADs, (void **)&m_pADsObj ); }
if( (!pAuthenticate) || (INVALID_CREDENTIALS_ERROR(hr)) ) // try credentials in session object
{ m_objCredentials = *pSessCreds;
hr = GetDSInterface( pwszURL, m_objCredentials, IID_IADs, (void **)&m_pADsObj ); }
if(INVALID_CREDENTIALS_ERROR(hr)) // try default credentials
{ CCredentials TmpCreds; // default credentials
m_objCredentials = TmpCreds; fAuthFlags = m_objCredentials.GetAuthFlags(); m_objCredentials.SetAuthFlags(fAuthFlags | ADS_SECURE_AUTHENTICATION);
hr = GetDSInterface( pwszURL, m_objCredentials, IID_IADs, (void **)&m_pADsObj ); }
BAIL_ON_FAILURE(hr); //Get the Schema Root and store it in m_bstrSchemaRoot
hr = GetSchemaRoot(); BAIL_ON_FAILURE(hr); // Get the data. without the GetRestrictedColunInfo will no return column information.
hr = m_pADsObj->GetInfo(); BAIL_ON_FAILURE(hr); CATCHBLOCKBAIL(hr)
error: RRETURN(hr); }
//+---------------------------------------------------------------------------
//
// Function: CRow::Initialize
//
// Synopsis: Initializes a row object that represents a row in a rowset as
// as opposed to a directly bound row object.
// See the other overloaded Initialize above.
//
// Returns: HRESULT
//----------------------------------------------------------------------------
STDMETHODIMP CRow::Initialize( PWSTR pwszURL, IUnknown *pSession, IUnknown *pSourceRowset, HROW hRow, PWSTR pwszUser, PWSTR pwszPassword, DWORD dwBindFlags, BOOL fIsTearOff, BOOL fGetColInfoFromRowset, CRowProvider *pRowProvider ) { HRESULT hr = S_OK; auto_leave cs_auto_leave(m_autocs); DWORD fAuthFlags; auto_rel<IRowset> pRowset; auto_rel<IColumnsInfo> pRowsetColumnsInfo; //Make sure we have valid arguments
ADsAssert(pSession); ADsAssert(pSourceRowset); ADsAssert(hRow != DB_NULL_HROW); TRYBLOCK cs_auto_leave.EnterCriticalSection(); if (pRowProvider == NULL) BAIL_ON_FAILURE(hr = E_INVALIDARG); m_pRowProvider = pRowProvider; m_dwBindFlags = dwBindFlags; m_pSession = pSession; m_pSession->AddRef(); m_pSourceRowset = pSourceRowset; m_hRow = hRow; m_fIsTearOff = fIsTearOff; m_fGetColInfoFromRowset = fGetColInfoFromRowset; pSourceRowset->AddRef(); //Get IRowset pointer
hr = pSourceRowset->QueryInterface(__uuidof(IRowset), (void **)&pRowset); BAIL_ON_FAILURE(hr); //Get Rowset columns info and store it. Spec says a row's columns
//are always a superset of those of the source rowset. To make
//sure we return proper columns info from row object,
//we need the rowset's column info.
hr = pSourceRowset->QueryInterface( __uuidof(IColumnsInfo), (void **)&pRowsetColumnsInfo); BAIL_ON_FAILURE(hr); hr = pRowsetColumnsInfo->GetColumnInfo( &m_cSourceRowsetColumns, &m_pSourceRowsetColumnInfo, &m_pSourceRowsetStringsBuffer); BAIL_ON_FAILURE(hr); //Get username, password and authFlags from Credentials.
hr = m_objCredentials.SetUserName(pwszUser); BAIL_ON_FAILURE(hr); hr = m_objCredentials.SetPassword(pwszPassword); BAIL_ON_FAILURE(hr); //Store the URL and AddRef row handle.
m_objURL = pwszURL; hr = pRowset->AddRefRows(1, &m_hRow, NULL, NULL); BAIL_ON_FAILURE(hr);
//check if the column info is to be obtained thruogh ADSI
if( !fGetColInfoFromRowset ) { // code below should be consistent with the Initialize() call
// (overloaded function above) used for direct binding
DWORD fAuthFlags = m_objCredentials.GetAuthFlags(); m_objCredentials.SetAuthFlags(fAuthFlags | ADS_SECURE_AUTHENTICATION);
hr = GetDSInterface( pwszURL, m_objCredentials, IID_IADs, (void **)&m_pADsObj );
BAIL_ON_FAILURE(hr);
//Get the Schema Root and store it in m_bstrSchemaRoot
hr = GetSchemaRoot(); BAIL_ON_FAILURE(hr);
// Get the data. without it GetRestrictedColunInfo will not return
// column info.
hr = m_pADsObj->GetInfo(); BAIL_ON_FAILURE(hr); } CATCHBLOCKBAIL(hr)
error: RRETURN(hr); }
//+---------------------------------------------------------------------------
//
// Function: CRow::fMatchesMaskCriteria
//
// Synopsis: Tells if the given column name matches any mask criteria
// for column ids.
//
// Returns: true if matches, false otherwise
//----------------------------------------------------------------------------
bool CRow::fMatchesMaskCriteria(PWCHAR pwszColumnName, ULONG cColumnIDMasks, const DBID rgColumnIDMasks[ ]) { //if there are no masks, the name is a match.
if (cColumnIDMasks == 0) return true;
// if the name is a NULL string the name is not a match.
if (NULL == pwszColumnName) return false; ULONG ulStrLen = 0; for (int j = 0; j < cColumnIDMasks; j++) { if( (rgColumnIDMasks[j].eKind != DBKIND_NAME) || (NULL == rgColumnIDMasks[j].uName.pwszName) ) continue;
ulStrLen = wcslen(rgColumnIDMasks[j].uName.pwszName); if (_wcsnicmp( rgColumnIDMasks[j].uName.pwszName, pwszColumnName, ulStrLen ) == 0) { // Matches criterion
return true; } } //doesn't match.
return false; }
//+---------------------------------------------------------------------------
//
// Function: CRow::fMatchesMaskCriteria (OVERLOADED)
//
// Synopsis: Tells if the given column id matches any mask criteria
// for column ids.
//
// Returns: true if matches, false otherwise
//----------------------------------------------------------------------------
bool CRow::fMatchesMaskCriteria(DBID columnid, ULONG cColumnIDMasks, const DBID rgColumnIDMasks[ ]) { //if there are no masks, the name is a match.
if (cColumnIDMasks == 0) return true; ULONG ulStrLen = 0; for (int j = 0; j < cColumnIDMasks; j++) { if (TRUE == CompareDBIDs(&columnid, &rgColumnIDMasks[j])) { // Matches criterion
return true; } } //doesn't match.
return false; }
//+---------------------------------------------------------------------------
//
// Function: CRow::GetSchemaAttributes
//
// Synopsis: Gets MultiValued and MaxRange Attributes of an ADS Property.
// Any of these can be NULL indicating the caller is not interested
// in getting the attribute.
//
// Returns: HRESULT
//----------------------------------------------------------------------------
HRESULT CRow::GetSchemaAttributes( PWCHAR pwszColumnName, VARIANT_BOOL *pfMultiValued, long *plMaxRange ) { HRESULT hr = S_OK; auto_rel<IADsProperty> pProperty; CComBSTR bstrSchemaName; ADsAssert(m_bstrSchemaRoot.Length()); ADsAssert(pwszColumnName != NULL); //Does the caller want at least one attribute?
if (!pfMultiValued && !plMaxRange) return S_OK; //Append a '/' and property name to root schema name
//to make the Schema ADsPath for this property.
bstrSchemaName = m_bstrSchemaRoot; bstrSchemaName.Append(L"/"); bstrSchemaName.Append(pwszColumnName); //Bind to the schema entry and get IADsProperty interface.
hr = GetDSInterface( bstrSchemaName, m_objCredentials, __uuidof(IADsProperty), (void **)&pProperty ); BAIL_ON_FAILURE(hr); if (pfMultiValued != NULL) { //Get multivalued attribute.
hr = pProperty->get_MultiValued(pfMultiValued); BAIL_ON_FAILURE(hr); } // The following call get_MaxRange seems to create perf problems
// because it apparently requires a round-trip to the server.
// This is not critical and we cn live without asking
// for max range here.
//if (plMaxRange != NULL)
//{
// //Get MaxSize attribute. Ignore error
// //since some properties may not have
// //this attribute set.
// pProperty->get_MaxRange(plMaxRange);
//}
error: RRETURN(hr); }
//+---------------------------------------------------------------------------
//
// Function: CRow::GetTypeAndSize
//
// Synopsis: Gets the type and size of a property
//
// Returns: HRESULT
//----------------------------------------------------------------------------
HRESULT CRow::GetTypeAndSize( ADSTYPE dwADsType, CComBSTR& bstrPropName, DBTYPE *pdbType, ULONG *pulSize ) { ADsAssert(pdbType != NULL); ADsAssert(pulSize != NULL); long lAdsType = dwADsType; VARIANT_BOOL fMultiValued = VARIANT_FALSE; HRESULT hr = S_OK; //Initialize out params
*pdbType = DBTYPE_ERROR; *pulSize = ~0; hr = GetSchemaAttributes(bstrPropName, &fMultiValued, (long *)pulSize); BAIL_ON_FAILURE(hr); //Give our best shot at determining type and size.
if (fMultiValued == VARIANT_TRUE) *pdbType = DBTYPE_BYREF | DBTYPE_VARIANT; else if (lAdsType < g_cMapADsTypeToDBType2) *pdbType = g_MapADsTypeToDBType2[lAdsType].wType; else *pdbType = DBTYPE_ERROR; // If we could not determine the size from schema information,
// set the size from the mapping table.
if (*pulSize == ~0) *pulSize = g_MapADsTypeToDBType2[lAdsType].ulSize; error: RRETURN (hr); }
//+---------------------------------------------------------------------------
//
// Function: CRow::GetSchemaRoot
//
// Synopsis: Gets the root schema path and stores it in m_bstrSchemaRoot
//
// Returns: HRESULT
//----------------------------------------------------------------------------
HRESULT CRow::GetSchemaRoot() { HRESULT hr = S_OK; CComBSTR bstrSchema; auto_rel<IADs> pADsSchema; Assert(m_pADsObj.get()); hr = m_pADsObj->get_Schema(&bstrSchema); BAIL_ON_FAILURE(hr); hr = GetDSInterface( bstrSchema, m_objCredentials, __uuidof(IADs), (void **)&pADsSchema ); BAIL_ON_FAILURE(hr); hr = pADsSchema->get_Parent(&m_bstrSchemaRoot); BAIL_ON_FAILURE(hr); error: RRETURN(hr); }
//+---------------------------------------------------------------------------
//
// Function: CRow::GetSourceRowsetColumns
//
// Synopsis: Gets the requested column data from the source rowset.
//
// Returns: HRESULT
//----------------------------------------------------------------------------
HRESULT CRow::GetSourceRowsetColumns( ULONG cColumns, DBCOLUMNACCESS rgColumns[], ULONG *pcErrors ) { // Make sure there is a rowset backing this row (this doesn't imply that
// the row is a tear-off (see comment at the beginning).
ADsAssert(m_pSourceRowset.get() != NULL); ADsAssert(m_pRowProvider != NULL); ADsAssert(m_fGetColInfoFromRowset == TRUE); HRESULT hr = S_OK; auto_rel<IAccessor> pAccessor; auto_rel<IRowset> pRowset; auto_rel<IColumnsInfo> pColumnsInfo; HACCESSOR hAccessor; ULONG iCol; ULONG ulRefCount = 0; DBBINDING binding = {0}; DBBINDSTATUS bindStatus = {0}; int iRowsetIndex; DBID dbidRowUrl = DBROWCOL_ROWURL; ADsAssert(pcErrors != NULL); *pcErrors = 0; hr = m_pSourceRowset->QueryInterface(&pRowset); BAIL_ON_FAILURE(hr); hr = m_pSourceRowset->QueryInterface(&pAccessor); BAIL_ON_FAILURE(hr); hr = m_pSourceRowset->QueryInterface(&pColumnsInfo); BAIL_ON_FAILURE(hr); for (iCol = 0; iCol < cColumns; iCol++) { rgColumns[iCol].dwStatus = DBSTATUS_S_OK; if( (rgColumns[iCol].pData != NULL) && (!IgnorecbMaxLen(rgColumns[iCol].wType)) ) ZeroMemory(rgColumns[iCol].pData, rgColumns[iCol].cbMaxLen);
//Is this the special URL column.
if(TRUE == CompareDBIDs(&dbidRowUrl, &rgColumns[iCol].columnid)) { CComVariant varTemp;
varTemp.Clear(); varTemp = m_objURL; //Set max length of column. 256 is consistent with 2.0 code.
rgColumns[iCol].cbDataLen = 256; rgColumns[iCol].dwStatus = DBSTATUS_S_OK;
//Does the caller want pData?
if (rgColumns[iCol].pData != NULL) { //Get IDataConvert interface pointer.
auto_rel<IDataConvert> pDataConvert; hr = GetIDataConvert(&pDataConvert);
if(SUCCEEDED(hr)) hr = pDataConvert->DataConvert( DBTYPE_VARIANT, rgColumns[iCol].wType, sizeof(VARIANT), &rgColumns[iCol].cbDataLen, &varTemp, rgColumns[iCol].pData, rgColumns[iCol].cbMaxLen, DBSTATUS_S_OK, &rgColumns[iCol].dwStatus, rgColumns[iCol].bPrecision, rgColumns[iCol].bScale, DBDATACONVERT_DEFAULT );
if(FAILED(hr)) { hr = S_OK;
// rgColumns[iCol].dwStatus already set above by
// DataConvert().
(*pcErrors)++; continue; } } continue; // on to next column
} //Is this the bookmark column?
if (rgColumns[iCol].columnid.eKind == DBKIND_GUID_PROPID && rgColumns[iCol].columnid.uGuid.guid == DBCOL_SPECIALCOL && rgColumns[iCol].columnid.uName.ulPropid == 2 ) { iRowsetIndex = 0; // bookmark is first column
} else { // Is column id of DBKIND_NAME?
if (rgColumns[iCol].columnid.eKind != DBKIND_NAME) { rgColumns[iCol].dwStatus = DBSTATUS_E_DOESNOTEXIST; (*pcErrors)++; continue; } iRowsetIndex = 0; hr = m_pRowProvider->GetIndex( pColumnsInfo, rgColumns[iCol].columnid.uName.pwszName, iRowsetIndex ); if (FAILED(hr) || 0 == iRowsetIndex) // failure or name not found
{ hr = S_OK; if(FAILED(hr)) rgColumns[iCol].dwStatus = DBSTATUS_E_UNAVAILABLE; else rgColumns[iCol].dwStatus = DBSTATUS_E_DOESNOTEXIST; (*pcErrors)++; continue; } } // else
binding.dwPart = DBPART_VALUE; binding.obLength = 0; binding.bPrecision = rgColumns[iCol].bPrecision; binding.bScale = rgColumns[iCol].bScale; binding.pTypeInfo = 0; binding.pObject = NULL; binding.iOrdinal = iRowsetIndex; binding.cbMaxLen = rgColumns[iCol].cbMaxLen; binding.dwMemOwner = DBMEMOWNER_CLIENTOWNED; binding.wType = rgColumns[iCol].wType; hr = pAccessor->CreateAccessor( DBACCESSOR_ROWDATA, 1, &binding, 0, &hAccessor, &bindStatus ); if (FAILED(hr)) { rgColumns[iCol].dwStatus = DBSTATUS_E_CANTCONVERTVALUE; (*pcErrors)++; hr = S_OK; continue; } if(rgColumns[iCol].pData != NULL) { hr = pRowset->GetData(m_hRow, hAccessor, rgColumns[iCol].pData); if (FAILED(hr)) { rgColumns[iCol].dwStatus = StatusFromHRESULT(hr); (*pcErrors)++; hr = S_OK; pAccessor->ReleaseAccessor(hAccessor, &ulRefCount); continue; } }
hr = pAccessor->ReleaseAccessor(hAccessor, &ulRefCount);
// now get the status and length
binding.dwPart = DBPART_LENGTH | DBPART_STATUS; binding.obStatus = FIELD_OFFSET(DBCOLUMNACCESS, dwStatus); binding.obLength = FIELD_OFFSET(DBCOLUMNACCESS, cbDataLen);
hr = pAccessor->CreateAccessor( DBACCESSOR_ROWDATA, 1, &binding, 0, &hAccessor, &bindStatus ); if (FAILED(hr)) { rgColumns[iCol].dwStatus = DBSTATUS_E_CANTCONVERTVALUE; (*pcErrors)++; hr = S_OK; continue; }
hr = pRowset->GetData(m_hRow, hAccessor, &(rgColumns[iCol])); if (FAILED(hr)) { rgColumns[iCol].dwStatus = StatusFromHRESULT(hr); (*pcErrors)++; }
hr = pAccessor->ReleaseAccessor(hAccessor, &ulRefCount); // We have set the dwStatus to reflect any problems, so clear error.
hr = S_OK; } error: if (FAILED(hr)) { for (iCol = 0; iCol < cColumns; iCol++) rgColumns[iCol].dwStatus = DBSTATUS_E_UNAVAILABLE; *pcErrors = cColumns; } RRETURN (hr); }
//+---------------------------------------------------------------------------
//
// Function: CRow::StatusFromHRESULT
//
// Synopsis: Gets DBSTATUS from a given HRESULT.
//
// Returns: DBSTATUS
//----------------------------------------------------------------------------
DBSTATUS CRow::StatusFromHRESULT(HRESULT hr) { if (SUCCEEDED(hr)) RRETURN(DBSTATUS_S_OK);
switch(hr) { case (E_INVALIDARG): RRETURN(DBSTATUS_E_CANTCREATE); case (DB_E_BADACCESSORHANDLE): case (DB_E_BADACCESSORTYPE): case (DB_E_BADBINDINFO): case (DB_E_BADORDINAL): case (DB_E_BADSTORAGEFLAGS): RRETURN(DBSTATUS_E_CANTCONVERTVALUE);
case (DB_E_UNSUPPORTEDCONVERSION): RRETURN(DBSTATUS_E_CANTCONVERTVALUE);
case (DB_E_BADROWHANDLE): case (DB_E_DELETEDROW): RRETURN(DBSTATUS_E_UNAVAILABLE);
default: RRETURN(DBSTATUS_E_BADSTATUS); } }
//////////////////////////////////////////////////////////////////////////////
//ISupportErrorInfo
//
//+---------------------------------------------------------------------------
//
// Function: CRow::InterfaceSupportsErrorInfo
//
// Synopsis: Given an interface ID, tells if that interface supports
// the interface ISupportErrorInfo
//
// Arguments:
// REFIID riid
//
// Returns: HRESULT
// S_OK yes, the interface supports ISupportErrorInfo
// S_FALSE no, the interface doesn't support it.
//----------------------------------------------------------------------------
STDMETHODIMP CRow::InterfaceSupportsErrorInfo(REFIID riid) { static const IID* arr[] = { &IID_IRow, &IID_IColumnsInfo, &IID_IColumnsInfo2, &IID_IConvertType, &IID_IGetSession }; for (int i=0; i < sizeof(arr) / sizeof(arr[0]); i++) { if (InlineIsEqualGUID(*arr[i],riid)) RRETURN(S_OK); } RRETURN(S_FALSE); }
//////////////////////////////////////////////////////////////////////////////
//IRow methods
//
//+---------------------------------------------------------------------------
//
// Function: CRow::GetColumns
//
// Synopsis: Gets Columns from a Row.
//
// Returns: HRESULT
//
// For more info see OLE DB 2.5 spec.
//----------------------------------------------------------------------------
STDMETHODIMP CRow::GetColumns( /* [in] */ DBORDINAL cColumns, /* [size_is][out][in] */ DBCOLUMNACCESS rgColumns[ ]) { HRESULT hr; DWORD cErrors = 0; auto_leave cs_auto_leave(m_autocs); int i; DBID dbidRowUrl = DBROWCOL_ROWURL; if ( cColumns == 0 ) { // Nothing to do:
RRETURN(S_OK); } if (!rgColumns) RRETURN(E_INVALIDARG); TRYBLOCK cs_auto_leave.EnterCriticalSection(); // We must have a valid ADs object or a valid Source Rowset
ADsAssert(m_pADsObj.get() || m_pSourceRowset.get()); if( m_fGetColInfoFromRowset ) { ADsAssert(m_pSourceRowset.get() != NULL); hr = GetSourceRowsetColumns(cColumns, rgColumns, &cErrors); } else { //Get IDataConvert interface pointer.
auto_rel<IDataConvert> pDataConvert; hr = GetIDataConvert(&pDataConvert); if (FAILED(hr)) BAIL_ON_FAILURE(hr = DB_E_ERRORSOCCURRED); for (i = 0; i < cColumns; i++) { // DBKIND is DBKIND_NAME and null or zero length column name
if ( rgColumns[i].columnid.eKind == DBKIND_NAME && (rgColumns[i].columnid.uName.pwszName == NULL || wcslen(rgColumns[i].columnid.uName.pwszName) == 0)) { rgColumns[i].dwStatus = DBSTATUS_E_DOESNOTEXIST; cErrors++; continue; } CComVariant varTemp; DBTYPE propType = DBTYPE_VARIANT; varTemp.Clear(); hr = S_OK; //Check if this is the special URL column or the ADsPath
//column. If so, assign the URL to varTemp and proceed to data
//conversion.
if ( (rgColumns[i].columnid.eKind == DBKIND_NAME && _wcsicmp(rgColumns[i].columnid.uName.pwszName, g_cwszAdsPath) == 0) || (TRUE == CompareDBIDs(&dbidRowUrl, &rgColumns[i].columnid)) ) { varTemp = m_objURL; //Set max length of column.
//MaxLen=256 is consistent with 2.0 provider code.
rgColumns[i].cbDataLen = 256; } // check if this is the bookmark column
else if (rgColumns[i].columnid.eKind == DBKIND_GUID_PROPID && rgColumns[i].columnid.uGuid.guid == DBCOL_SPECIALCOL && rgColumns[i].columnid.uName.ulPropid == 2 ) { UINT uBmk;
if( m_hRow != DB_NULL_HROW ) // get the bookmark associated with this row handle
{ auto_rel<IRowset> pRowset; CRowset *pCRowset; LONG lRow; RBOOKMARK bmk;
hr = m_pSourceRowset->QueryInterface(__uuidof(IRowset), (void **)&pRowset); if( SUCCEEDED(hr) ) { pCRowset = (CRowset *) ((IRowset *) pRowset); lRow = pCRowset->HROWToRow(m_hRow); bmk = pCRowset->RowToBmk(lRow); uBmk = (UINT) bmk; } } else // use any value for bookmark
uBmk = 0; if( SUCCEEDED(hr) ) { VARIANT vTmpVariant;
V_VT(&vTmpVariant) = VT_UI4; V_UI4(&vTmpVariant) = uBmk;
varTemp = vTmpVariant; } // else we will fail below and continue to next iteration
} else if (rgColumns[i].columnid.eKind == DBKIND_NAME) { //Find out if property is multivalued and its size.
VARIANT_BOOL fMultiValued; long lSize = ~0; hr = GetSchemaAttributes( rgColumns[i].columnid.uName.pwszName, &fMultiValued, &lSize ); if (SUCCEEDED(hr)) { // if security descriptor is requested as a variant,
// then we return a variant with an octet string
// inside. This would always be the case with ADO as
// it requests all columns as variants. If it is
// requested as an octet string, we return an octet
// string. If it is requested as some other
// type from C++ (say DBTYPE_IDISPATCH), then we will
// call Get/GetEx and try to convert the resulting
// variant to the appropriate type. Returning security
// descriptor as an octet string is much cheaper as
// there is no network traffic for ACE conversion.
if( (!_wcsicmp(rgColumns[i].columnid.uName.pwszName, NT_SEC_DESC_ATTR)) && ((rgColumns[i].wType & DBTYPE_VARIANT) || (rgColumns[i].wType & DBTYPE_BYTES)) ) { hr = GetSecurityDescriptor(&rgColumns[i], fMultiValued); if(FAILED(hr)) { // Clear error. Status has already been set
// in rgColumns[i]
hr = S_OK; cErrors++; } //Nothing more to do, continue with next column.
continue; } if (fMultiValued) { //multi-valued column. Use GetEx.
hr = m_pADsObj->GetEx( rgColumns[i].columnid.uName.pwszName, &varTemp ); propType = DBTYPE_BYREF | DBTYPE_VARIANT; } else { //single-valued. Use Get.
hr = m_pADsObj->Get( rgColumns[i].columnid.uName.pwszName, &varTemp ); } } rgColumns[i].cbDataLen = lSize; } else { rgColumns[i].dwStatus = DBSTATUS_E_UNAVAILABLE; cErrors++; continue; } if (FAILED(hr)) { //Clear error. dwStatus below will reflect
//the error for this column.
hr = S_OK; rgColumns[i].dwStatus = DBSTATUS_E_UNAVAILABLE; cErrors++; //Increment error count.
//Nothing more to do, continue with next column.
continue; } //Does the caller want pData?
if (rgColumns[i].pData != NULL) { rgColumns[i].dwStatus = DBSTATUS_S_OK; //Convert data into requested type
if (propType == (DBTYPE_VARIANT | DBTYPE_BYREF)) //multi-valued
{ if( !(rgColumns[i].wType & DBTYPE_VARIANT) ) // can't convert a multi-valued attr. to anything else
{ rgColumns[i].dwStatus = DBSTATUS_E_CANTCONVERTVALUE; cErrors++; continue; } else { DWORD dwRequiredLen; PVARIANT pVar;
if((rgColumns[i].wType & (~DBTYPE_BYREF)) != DBTYPE_VARIANT) { // bad type
rgColumns[i].dwStatus = DBSTATUS_E_CANTCONVERTVALUE; cErrors++; continue; }
if(rgColumns[i].wType & DBTYPE_BYREF) dwRequiredLen = sizeof(VARIANT *); else dwRequiredLen = sizeof(VARIANT);
if(rgColumns[i].cbMaxLen < dwRequiredLen) { rgColumns[i].dwStatus = DBSTATUS_E_CANTCONVERTVALUE; cErrors++; continue; }
if(rgColumns[i].wType & DBTYPE_BYREF) { pVar = (PVARIANT) CoTaskMemAlloc(sizeof(VARIANT)); if (pVar == NULL) { rgColumns[i].dwStatus = DBSTATUS_E_CANTCREATE; cErrors++; continue; } } else pVar = (PVARIANT) rgColumns[i].pData;
VariantInit(pVar); hr = VariantCopy(pVar, &varTemp); if(FAILED(hr)) { hr = S_OK; rgColumns[i].dwStatus = DBSTATUS_E_CANTCREATE; cErrors++; if(rgColumns[i].wType & DBTYPE_BYREF) CoTaskMemFree(pVar); continue; }
if(rgColumns[i].wType & DBTYPE_BYREF) { *(PVARIANT *)rgColumns[i].pData = pVar; rgColumns[i].cbDataLen = sizeof(PVARIANT); } else rgColumns[i].cbDataLen = sizeof(VARIANT); } } else //single valued
{ hr = pDataConvert->DataConvert( propType, rgColumns[i].wType, sizeof(VARIANT), &rgColumns[i].cbDataLen, &varTemp, rgColumns[i].pData, rgColumns[i].cbMaxLen, DBSTATUS_S_OK, &rgColumns[i].dwStatus, rgColumns[i].bPrecision, rgColumns[i].bScale, DBDATACONVERT_DEFAULT ); } if (FAILED(hr)) { hr = S_OK; rgColumns[i].dwStatus = DBSTATUS_E_CANTCONVERTVALUE; cErrors++; } } } } CATCHBLOCKBAIL(hr) if (cErrors == 0) RRETURN(S_OK); else if (cErrors < cColumns) RRETURN(DB_S_ERRORSOCCURRED); else RRETURN(DB_E_ERRORSOCCURRED); error: RRETURN(hr); }
//+---------------------------------------------------------------------------
//
// Function: CRow::GetSourceRowset
//
// Synopsis: Returns interface pointer on the Source Rowset from which this
// Row was created.
//
// Returns: HRESULT
//
// For more info see OLE DB 2.5 spec.
//----------------------------------------------------------------------------
STDMETHODIMP CRow::GetSourceRowset( /* [in] */ REFIID riid, /* [iid_is][out] */ IUnknown **ppRowset, /* [out] */ HROW *phRow) { if (ppRowset == NULL && phRow == NULL) RRETURN(E_INVALIDARG); HRESULT hr; auto_leave cs_auto_leave(m_autocs); if (ppRowset) *ppRowset = NULL; if (phRow) *phRow = DB_NULL_HROW; TRYBLOCK cs_auto_leave.EnterCriticalSection(); if( m_fIsTearOff ) { ADsAssert(m_pSourceRowset.get()); if (ppRowset) { hr = m_pSourceRowset->QueryInterface(riid, (void**)ppRowset); if (FAILED(hr)) BAIL_ON_FAILURE(hr = E_NOINTERFACE); } if (phRow) { // increment reference count of row handle
auto_rel<IRowset> pRowset;
hr = m_pSourceRowset->QueryInterface(__uuidof(IRowset), (void **)&pRowset); BAIL_ON_FAILURE(hr);
hr = pRowset->AddRefRows(1, &m_hRow, NULL, NULL); BAIL_ON_FAILURE(hr);
*phRow = m_hRow; } } else { BAIL_ON_FAILURE(hr = DB_E_NOSOURCEOBJECT); } CATCHBLOCKBAIL(hr) RRETURN(S_OK); error: if(ppRowset && (*ppRowset != NULL)) { (*ppRowset)->Release(); *ppRowset = NULL; }
RRETURN(hr); }
//+---------------------------------------------------------------------------
//
// Function: CRow::Open
//
// Synopsis: Opens a column of a row and returns the requested
// interface on it.
//
// Returns: HRESULT
//
// For more info see OLE DB 2.5 spec.
//----------------------------------------------------------------------------
STDMETHODIMP CRow::Open( /* [unique][in] */ IUnknown *pUnkOuter, /* [in] */ DBID *pColumnID, /* [in] */ REFGUID rguidColumnType, /* [in] */ DWORD dwBindFlags, /* [in] */ REFIID riid, /* [iid_is][out] */ IUnknown **ppUnk) { HRESULT hr = S_OK; CComVariant varData; CComObject<CStreamMem>* pMemoryStream = NULL; auto_rel<IStream> pStreamDelete; DBCOLUMNACCESS rgColumns[1]; DBID dbidRowUrl = DBROWCOL_ROWURL; TRYBLOCK //General validation checks. dwBindFlags is reserved and must be 0.
if (ppUnk == NULL || pColumnID == NULL || dwBindFlags != 0) RRETURN(E_INVALIDARG);
*ppUnk = NULL; // columnID has to be URL, bookmark or DBKIND_NAME
if ((pColumnID->eKind != DBKIND_NAME) && (FALSE == CompareDBIDs(&dbidRowUrl, pColumnID)) && (!((pColumnID->eKind == DBKIND_GUID_PROPID) && (pColumnID->uGuid.guid == DBCOL_SPECIALCOL) && (pColumnID->uName.ulPropid == 2)) ) ) RRETURN(DB_E_BADCOLUMNID); if ((pColumnID->eKind == DBKIND_NAME) && (pColumnID->uName.pwszName == NULL)) RRETURN(DB_E_BADCOLUMNID); if (pUnkOuter != NULL && !InlineIsEqualGUID(riid, IID_IUnknown)) RRETURN(DB_E_NOAGGREGATION); //we don't support aggregation
if (pUnkOuter != NULL) RRETURN(DB_E_NOAGGREGATION); // riid has to be one of the interfaces that can be QIed from
// IStream, need not necessarily be IID_IStream
hr = CopyDBIDs(&rgColumns[0].columnid, pColumnID); if (FAILED(hr)) BAIL_ON_FAILURE(hr = DB_E_BADCOLUMNID); //fill relevant fields of rgColumns[0]
rgColumns[0].wType = DBTYPE_VARIANT; rgColumns[0].pData = (VARIANT *)&varData; rgColumns[0].cbMaxLen = sizeof(VARIANT); //get column data
hr = GetColumns(1, rgColumns); if (FAILED(hr)) BAIL_ON_FAILURE(hr = DB_E_BADCOLUMNID);
if (rguidColumnType != DBGUID_STREAM) //we currently support only streams
RRETURN(DB_E_OBJECTMISMATCH); //Check if the returned data is of type binary -
//Note: ADSI returns binary data as type VT_ARRAY | VT_UI1.
if (V_VT(&varData) != (VT_ARRAY | VT_UI1)) BAIL_ON_FAILURE(hr = DB_E_OBJECTMISMATCH); hr = CComObject<CStreamMem>::CreateInstance(&pMemoryStream); if (FAILED(hr)) BAIL_ON_FAILURE(hr = E_OUTOFMEMORY); //To make sure we delete the CStreamMem object in case we ecounter
//errors after this point.
pMemoryStream->AddRef(); pStreamDelete = pMemoryStream; hr = pMemoryStream->Initialize(&varData, (IRow *)this, m_hRow); if (FAILED(hr)) BAIL_ON_FAILURE(hr = DB_E_OBJECTMISMATCH); hr = pMemoryStream->QueryInterface(riid, (void **)ppUnk); if (FAILED(hr)) BAIL_ON_FAILURE(hr = E_NOINTERFACE); CATCHBLOCKBAIL(hr);
error: FreeDBID(&rgColumns[0].columnid); RRETURN(hr); }
///////////////////////////////////////////////////////////////////////////////
//IColumnsInfo2 : IColumnsInfo
//
//+---------------------------------------------------------------------------
//
// Function: CRow::GetColumnInfo
//
// Synopsis: returns column information on a row. Column 0 is the URL column.
//
// Returns: HRESULT
//
// For more info see OLE DB 2.5 spec.
//----------------------------------------------------------------------------
STDMETHODIMP CRow::GetColumnInfo( /* [out][in] */ DBORDINAL *pcColumns, /* [size_is][size_is][out] */ DBCOLUMNINFO **prgInfo, /* [out] */ OLECHAR **ppStringsBuffer) { if( pcColumns ) *pcColumns = 0; if( prgInfo ) *prgInfo = NULL; if( ppStringsBuffer ) *ppStringsBuffer = NULL; if( !pcColumns || !prgInfo || !ppStringsBuffer ) RRETURN( E_INVALIDARG ); RRETURN(GetRestrictedColumnInfo ( 0, NULL, 0, pcColumns, NULL, prgInfo, ppStringsBuffer )); }
//+---------------------------------------------------------------------------
//
// Function: CRow::MapColumnIDs
//
// Synopsis: Maps column IDs
//
// Returns: HRESULT
//
// For more info see OLE DB 2.5 spec.
//----------------------------------------------------------------------------
STDMETHODIMP CRow::MapColumnIDs( /* [in] */ DBORDINAL cColumnIDs, /* [size_is][in] */ const DBID rgColumnIDs[ ], /* [size_is][out] */ DBORDINAL rgColumns[ ]) { HRESULT hr; ULONG cValidCols = 0; //
// check in-params and NULL out-params in case of error
//
if( cColumnIDs == 0 ) RRETURN( S_OK );
if( !rgColumnIDs || !rgColumns ) RRETURN( E_INVALIDARG );
//
// Get the ColumnsInfo
//
DBORDINAL ulColumns; DBCOLUMNINFO * rgColumnInfo = NULL; OLECHAR * pStringBuffer = NULL;
hr=GetColumnInfo((DBORDINAL *)&ulColumns, &rgColumnInfo, &pStringBuffer); if( FAILED(hr) ) RRETURN( hr );
for(ULONG iCol=0; iCol < cColumnIDs; iCol++) { // Initialize the column ordinal to invalid column
rgColumns[iCol] = DB_INVALIDCOLUMN;
for (ULONG iOrdinal = 0; iOrdinal < ulColumns; iOrdinal++) { if (TRUE == CompareDBIDs( &rgColumnIDs[iCol], &rgColumnInfo[iOrdinal].columnid)) { rgColumns[iCol] = rgColumnInfo[iOrdinal].iOrdinal; cValidCols++; break; } } }
//
// Free the ColumnsInfo
//
if( rgColumnInfo ) CoTaskMemFree(rgColumnInfo);
if( pStringBuffer ) CoTaskMemFree(pStringBuffer);
//
// Return the HRESULT
//
if( cValidCols == 0 ) RRETURN( DB_E_ERRORSOCCURRED ); else if( cValidCols < cColumnIDs ) RRETURN( DB_S_ERRORSOCCURRED ); else RRETURN( S_OK ); }
//+---------------------------------------------------------------------------
//
// Function: CRow::GetRestrictedColumnInfo
//
// Synopsis: returns column information for those columns which match given
// mask criteria.
//
// Returns: HRESULT
//
// For more info see OLE DB 2.5 spec.
//----------------------------------------------------------------------------
STDMETHODIMP CRow::GetRestrictedColumnInfo( /* [in] */ DBORDINAL cColumnIDMasks, /* [in] */ const DBID rgColumnIDMasks[ ], /* [in] */ DWORD dwFlags, /* [out][in] */ DBORDINAL *pcColumns, /* [size_is][size_is][out] */ DBID **prgColumnIDs, /* [size_is][size_is][out] */ DBCOLUMNINFO **prgColumnInfo, /* [out] */ OLECHAR **ppStringsBuffer) { HRESULT hr = S_OK; ULONG ulNumColumns = 0, ulStartColumn; int iColumn = 0; int i = 0, j=0, iMask = 0; auto_leave cs_auto_leave(m_autocs); DWORD cMaxColumns ; auto_rel<IDirectoryObject> pIDirObj; DBID bookMarkColid; bool bAddBookMark = false;
//
// Zero the out params:
//
if (pcColumns) *pcColumns = 0; if (prgColumnIDs) *prgColumnIDs = NULL; if (prgColumnInfo) *prgColumnInfo = NULL; if (ppStringsBuffer) *ppStringsBuffer = NULL; // Validate arguments.
if ((dwFlags) || (pcColumns == NULL) || (ppStringsBuffer == NULL) ) RRETURN( E_INVALIDARG); // Either column info or column ids should be available.
if ((NULL == prgColumnIDs) && (NULL == prgColumnInfo)) RRETURN( E_INVALIDARG); //Validate the mask
if (cColumnIDMasks && !rgColumnIDMasks) RRETURN( E_INVALIDARG); for (iMask = 0; iMask < cColumnIDMasks; iMask++) if (S_FALSE == IsValidDBID(&rgColumnIDMasks[iMask])) RRETURN( DB_E_BADCOLUMNID ); TRYBLOCK { cs_auto_leave.EnterCriticalSection(); // We must have a valid ADs object or a valid Source Rowset
ADsAssert(m_pADsObj.get() || m_pSourceRowset.get()); // Calculate maximum number of columns that we will return,
// excluding the bookmark column. We will add bookmark later.
if (m_fGetColInfoFromRowset) { ADsAssert(m_pSourceRowset.get() != NULL);
// m_cSourceRowsetColumns includes the bookmark column.
cMaxColumns = m_cSourceRowsetColumns - 1; } else { if(m_cMaxColumns != -1) cMaxColumns = m_cMaxColumns; else { hr = m_pADsObj->QueryInterface(__uuidof(IDirectoryObject), (void**)&pIDirObj); BAIL_ON_FAILURE(hr); hr = pIDirObj->GetObjectAttributes(NULL, -1, &m_pAttrInfo, &cMaxColumns); BAIL_ON_FAILURE(hr); // Add one for ADsPath. ADsPath is returned if the query was a
// SELECT * query (irrespective of whether the row was obtained
// from a rowset or directly from a command object) OR if
// IBindResource::Bind was used to get the row.
cMaxColumns += 1; m_cMaxColumns = cMaxColumns; } }
// Add one for URL (for all rows)
cMaxColumns++; BSTR bstrName; auto_prg<CComBSTR> propStrings; auto_prg<DBTYPE> propTypes; auto_prg<ULONG> propSizes; propStrings = new CComBSTR[cMaxColumns]; if (!propStrings) { hr = E_OUTOFMEMORY; BAIL_ON_FAILURE(hr); } // If client asked for columns info. allocate DBTYPE array
if (prgColumnInfo) { propTypes = new DBTYPE[cMaxColumns]; propSizes = new ULONG[cMaxColumns]; if (!propTypes || !propSizes) BAIL_ON_FAILURE(E_OUTOFMEMORY); } int totalLen = 0; ulNumColumns = 0; BOOL fURLMatchesMask = FALSE;
if (m_fGetColInfoFromRowset) { ADsAssert(m_pSourceRowset.get() != NULL);
// start with 1 because column # 0 in source rowset
// is always a bookmark column.
for (i = 1; i < m_cSourceRowsetColumns; i++) { //We need to check if this column matches mask criteria. Use
// the column name for comparison since we should have a match
// even if a substring of the column name is specified by the
// client. Comparing column ID won't work in this case.
// We know that all columns other than the bookmark (column 0)
// are of type DBKIND_NAME. So we can access pwszName safely.
if (fMatchesMaskCriteria( m_pSourceRowsetColumnInfo[i].columnid.uName.pwszName, cColumnIDMasks, rgColumnIDMasks) == false) continue; //matches the criteria, add to list and
//update totalLen for strings buffer.
propStrings[(int)ulNumColumns] = m_pSourceRowsetColumnInfo[i].pwszName; totalLen += SysStringLen(propStrings[(int)ulNumColumns]) + 1; //add type and size to list if columninfo is requested.
if (prgColumnInfo) { propTypes[(int)ulNumColumns] = m_pSourceRowsetColumnInfo[i].wType; propSizes[(int)ulNumColumns] = m_pSourceRowsetColumnInfo[i].ulColumnSize; } ulNumColumns++; }
// Finally, check if URL column is requested
if (fMatchesMaskCriteria(DBROWCOL_ROWURL, cColumnIDMasks, rgColumnIDMasks) == true) { //matches the criteria, add to list and
//update totalLen for strings buffer.
bstrName = SysAllocString(g_cwszAdsPath); propStrings[(int)ulNumColumns].Attach(bstrName); bstrName = NULL; totalLen += SysStringLen(propStrings[(int)ulNumColumns]) + 1;
// we know the type and size of the URL column
ulNumColumns++; fURLMatchesMask = TRUE; } } else { for (i = 0; i < cMaxColumns; i++) { if ((cMaxColumns-1) == i) // special URL column
{ if (fMatchesMaskCriteria(DBROWCOL_ROWURL, cColumnIDMasks, rgColumnIDMasks) == true) { bstrName = SysAllocString(g_cwszAdsPath); fURLMatchesMask = TRUE; } else continue; } else { if (0 == i) //ADsPath
bstrName = SysAllocString(g_cwszAdsPath); else { bstrName =SysAllocString(m_pAttrInfo[i-1].pszAttrName); } // If property doesn't match
// mask criteria, continue with next property.
if ( (fMatchesMaskCriteria(bstrName, cColumnIDMasks, rgColumnIDMasks) == false) ) { SysFreeString(bstrName); bstrName = NULL; continue; } } // else
// OK Matches the criterion add to the list and
// update toalLen for strings buffer.
propStrings[(int)ulNumColumns].Attach(bstrName); bstrName = NULL; totalLen += SysStringLen(propStrings[(int)ulNumColumns]) + 1; //Get Type and size of column if ColumnInfo is requested.
//For the special URL column and the ADsPath column, we already
//know the type.
if ((i > 0) && (i < (cMaxColumns-1)) && (prgColumnInfo != NULL)) { hr = GetTypeAndSize( m_pAttrInfo[i-1].dwADsType, propStrings[(int)ulNumColumns], &propTypes[(int)ulNumColumns], &propSizes[(int)ulNumColumns] ); BAIL_ON_FAILURE(hr); } // Increment number of columns count
ulNumColumns++; } } if (ulNumColumns == 0) BAIL_ON_FAILURE(hr = DB_E_NOCOLUMN); //We will add a bookmark column to the list that we are going to return.
bookMarkColid.eKind = DBKIND_GUID_PROPID; bookMarkColid.uGuid.guid = DBCOL_SPECIALCOL; bookMarkColid.uName.ulPropid = 2; if (fMatchesMaskCriteria(bookMarkColid, cColumnIDMasks, rgColumnIDMasks)) { bAddBookMark = true; ulNumColumns += 1; } *pcColumns = ulNumColumns; // Does the caller want column IDS?
if ( prgColumnIDs ) { *prgColumnIDs = (DBID *) CoTaskMemAlloc( (ulNumColumns) * sizeof (DBID) ); if (NULL == *prgColumnIDs) BAIL_ON_FAILURE(hr = E_OUTOFMEMORY); ZeroMemory ( *prgColumnIDs, (ulNumColumns) * sizeof (DBID)); } // Does the caller want COLUMNINFO?
if ( prgColumnInfo ) { *prgColumnInfo = (DBCOLUMNINFO *) CoTaskMemAlloc( (ulNumColumns) * sizeof (DBCOLUMNINFO) ); if (NULL == *prgColumnInfo) BAIL_ON_FAILURE(hr = E_OUTOFMEMORY); ZeroMemory ( *prgColumnInfo, (ulNumColumns) * sizeof (DBCOLUMNINFO) ); } // get the string buffer allocated.
*ppStringsBuffer = (OLECHAR *)CoTaskMemAlloc(sizeof(OLECHAR)*totalLen); if (NULL ==*ppStringsBuffer) BAIL_ON_FAILURE(hr = E_OUTOFMEMORY); OLECHAR *pwChar = *ppStringsBuffer; //First fill bookmark column information.
if (prgColumnIDs && bAddBookMark) { (*prgColumnIDs)[0] = bookMarkColid; } if (prgColumnInfo && bAddBookMark) { (*prgColumnInfo)[0].pwszName = NULL; (*prgColumnInfo)[0].pTypeInfo = NULL; (*prgColumnInfo)[0].iOrdinal = 0; (*prgColumnInfo)[0].ulColumnSize = sizeof(ULONG); (*prgColumnInfo)[0].wType = DBTYPE_UI4; (*prgColumnInfo)[0].bPrecision = 10; (*prgColumnInfo)[0].bScale = (BYTE) ~ 0; (*prgColumnInfo)[0].columnid.eKind = DBKIND_GUID_PROPID; (*prgColumnInfo)[0].columnid.uGuid.guid = DBCOL_SPECIALCOL; (*prgColumnInfo)[0].columnid.uName.ulPropid = 2; (*prgColumnInfo)[0].dwFlags = DBCOLUMNFLAGS_ISBOOKMARK | DBCOLUMNFLAGS_ISFIXEDLENGTH; } // Fill the rest of the columns.
if (bAddBookMark) ulStartColumn = 1; else ulStartColumn = 0; for (iColumn= ulStartColumn, i = 0; iColumn < ulNumColumns; iColumn++, i++) { wcscpy(pwChar, propStrings[i]); //Is this the special URL column (has to be last column)
if( fURLMatchesMask && (iColumn == (ulNumColumns -1)) && (_wcsicmp(pwChar, g_cwszAdsPath)) == 0 ) { if ( prgColumnIDs ) { // Add a new DBID for this column:
//
(*prgColumnIDs)[iColumn] = DBROWCOL_ROWURL; } if ( prgColumnInfo ) { (*prgColumnInfo)[iColumn].pwszName = pwChar; (*prgColumnInfo)[iColumn].pTypeInfo = NULL; (*prgColumnInfo)[iColumn].iOrdinal = iColumn; (*prgColumnInfo)[iColumn].dwFlags = DBCOLUMNFLAGS_ISROWURL; //Rowset code sets ulColumnSize for Adspath to 256.
//We do the same thing for consistency.
(*prgColumnInfo)[iColumn].ulColumnSize = 256; (*prgColumnInfo)[iColumn].wType = DBTYPE_WSTR; (*prgColumnInfo)[iColumn].bPrecision = ~0; (*prgColumnInfo)[iColumn].bScale = ~0; (*prgColumnInfo)[iColumn].columnid = DBROWCOL_ROWURL; } } // Is this the ADsPath column (if it has the name ADsPath and it
// is not URL, it has to be the ADsPath column)
else if(_wcsicmp(pwChar, g_cwszAdsPath) == 0) { if ( prgColumnIDs ) { // Add DBID for ADsPath
(*prgColumnIDs)[iColumn].eKind = DBKIND_NAME; (*prgColumnIDs)[iColumn].uGuid.guid = GUID_NULL; (*prgColumnIDs)[iColumn].uName.pwszName = pwChar; } if ( prgColumnInfo ) { // Add a DBCOLUMNINFO for ADsPath
DBTYPE wType = DBTYPE_WSTR | DBTYPE_BYREF;
(*prgColumnInfo)[iColumn].pwszName = pwChar; (*prgColumnInfo)[iColumn].pTypeInfo = NULL; (*prgColumnInfo)[iColumn].iOrdinal = iColumn;
// OLEDB 2.0 code sets ulColumnsSize to 256 for ADsPath
(*prgColumnInfo)[iColumn].ulColumnSize = 256; (*prgColumnInfo)[iColumn].wType = wType;
// the code below has to be identical to the code in
// GetColumnsInfo2 in ccommand.cxx
wType = wType & (~DBTYPE_BYREF); if( (wType == DBTYPE_STR) || (wType == DBTYPE_WSTR) || (wType == DBTYPE_BYTES) ) (*prgColumnInfo)[iColumn].dwFlags = DBCOLUMNFLAGS_ISNULLABLE; else (*prgColumnInfo)[iColumn].dwFlags = DBCOLUMNFLAGS_ISNULLABLE | DBCOLUMNFLAGS_ISFIXEDLENGTH;
(*prgColumnInfo)[iColumn].bPrecision = SetPrecision(wType);
(*prgColumnInfo)[iColumn].bScale = ~0; (*prgColumnInfo)[iColumn].columnid.eKind = DBKIND_NAME; (*prgColumnInfo)[iColumn].columnid.uGuid.guid = GUID_NULL; (*prgColumnInfo)[iColumn].columnid.uName.pwszName= pwChar; } } else { if ( prgColumnIDs ) { // Add a new DBID for this column:
//
(*prgColumnIDs)[iColumn].eKind = DBKIND_NAME; (*prgColumnIDs)[iColumn].uGuid.guid = GUID_NULL; (*prgColumnIDs)[iColumn].uName.pwszName = pwChar; } if ( prgColumnInfo ) { // Add a new DBCOLUMNINFO for this column:
//
DBTYPE wType = propTypes[i]; (*prgColumnInfo)[iColumn].pwszName = pwChar; (*prgColumnInfo)[iColumn].pTypeInfo = NULL; (*prgColumnInfo)[iColumn].iOrdinal = iColumn; (*prgColumnInfo)[iColumn].ulColumnSize = propSizes[i]; ; (*prgColumnInfo)[iColumn].wType = wType; // the code below has to be identical to the code in
// GetColumnsInfo2 in ccommand.cxx
wType = wType & (~DBTYPE_BYREF); if( (wType == DBTYPE_STR) || (wType == DBTYPE_WSTR) || (wType == DBTYPE_BYTES) ) (*prgColumnInfo)[iColumn].dwFlags = DBCOLUMNFLAGS_ISNULLABLE; else (*prgColumnInfo)[iColumn].dwFlags = DBCOLUMNFLAGS_ISNULLABLE | DBCOLUMNFLAGS_ISFIXEDLENGTH;
(*prgColumnInfo)[iColumn].bPrecision = SetPrecision(wType); (*prgColumnInfo)[iColumn].bScale = ~0; (*prgColumnInfo)[iColumn].columnid.eKind = DBKIND_NAME; (*prgColumnInfo)[iColumn].columnid.uGuid.guid = GUID_NULL; (*prgColumnInfo)[iColumn].columnid.uName.pwszName= pwChar; } } //Position the pointer in strings buffer
//for writing next column name.
pwChar += SysStringLen(propStrings[i]) + 1; } } CATCHBLOCKBAIL(hr)
RRETURN(S_OK); error: if ((prgColumnIDs) && (*prgColumnIDs)) { CoTaskMemFree(*prgColumnIDs); *prgColumnIDs = NULL; } if ((prgColumnInfo) && (*prgColumnInfo)) { CoTaskMemFree(*prgColumnInfo); *prgColumnInfo = NULL; } if ((ppStringsBuffer) && (*ppStringsBuffer)) { CoTaskMemFree(*ppStringsBuffer); *ppStringsBuffer = NULL; }
if (pcColumns) *pcColumns = 0;
RRETURN(hr); }
/////////////////////////////////////////////////////////////////////////////
//IConvertType
//
//+---------------------------------------------------------------------------
//
// Function: CRow::ConvertType
//
// Synopsis: Converts one DBTYPE to another using data conversion library.
//
// Returns: HRESULT
//
// For more info see OLE DB 2.0 spec.
//----------------------------------------------------------------------------
STDMETHODIMP CRow::CanConvert( /* [in] */ DBTYPE wFromType, /* [in] */ DBTYPE wToType, /* [in] */ DBCONVERTFLAGS dwConvertFlags) { auto_rel<IDataConvert> pDataConvert; HRESULT hr = GetIDataConvert(&pDataConvert); BAIL_ON_FAILURE(hr); ADsAssert(pDataConvert.get());
if( dwConvertFlags & DBCONVERTFLAGS_PARAMETER ) // not allowed on row
RRETURN( DB_E_BADCONVERTFLAG );
if( (dwConvertFlags & (~(DBCONVERTFLAGS_ISLONG | DBCONVERTFLAGS_ISFIXEDLENGTH | DBCONVERTFLAGS_FROMVARIANT))) != DBCONVERTFLAGS_COLUMN ) RRETURN( DB_E_BADCONVERTFLAG );
if( dwConvertFlags & DBCONVERTFLAGS_ISLONG ) { DBTYPE wType;
wType = wFromType & (~(DBTYPE_BYREF | DBTYPE_ARRAY | DBTYPE_VECTOR));
// wType has to be variable-length DBTYPE
if( (wType != DBTYPE_STR) && (wType != DBTYPE_WSTR) && (wType != DBTYPE_BYTES) && (wType != DBTYPE_VARNUMERIC) ) RRETURN( DB_E_BADCONVERTFLAG ); }
if( dwConvertFlags & DBCONVERTFLAGS_FROMVARIANT ) { DBTYPE dbTmpType, wVtType;
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) ) RRETURN( DB_E_BADTYPE ); }
RRETURN(pDataConvert->CanConvert(wFromType, wToType)); error: RRETURN(hr); }
///////////////////////////////////////////////////////////////////////////////
//IGetSession
//
//+---------------------------------------------------------------------------
//
// Function: CRow::GetSession
//
// Synopsis: Gets the Session interface through which this row has been
// created.
//
// Returns: HRESULT
//
// For more info see OLE DB 2.5 spec.
//----------------------------------------------------------------------------
STDMETHODIMP CRow::GetSession( REFIID riid, IUnknown **ppunkSession) { if (ppunkSession == NULL) RRETURN(E_INVALIDARG); *ppunkSession = NULL; if (!m_pSession.get()) RRETURN(DB_E_NOSOURCEOBJECT); HRESULT hr = m_pSession->QueryInterface(riid, (void**)ppunkSession); if (FAILED(hr)) RRETURN(E_NOINTERFACE); RRETURN(S_OK); }
//----------------------------------------------------------------------------
// GetSecurityDescriptor
//
// Returns the security descriptor as an octet string.
//
//----------------------------------------------------------------------------
HRESULT CRow::GetSecurityDescriptor( DBCOLUMNACCESS *pColumn, BOOL fMultiValued ) { HRESULT hr; auto_rel<IDirectoryObject> pIDirObj; PVARIANT pVariant = NULL, pVarArray = NULL; SAFEARRAY *aList = NULL; SAFEARRAYBOUND aBound; VARTYPE vType; DWORD dwRequiredLen; int iAttr, j;
// multivalued attributes are always returned as variants. Single-valued
// security descriptor has to be returned as either variant or octet
// string.
if( ((pColumn->wType != (DBTYPE_VARIANT | DBTYPE_BYREF)) && (pColumn->wType != DBTYPE_VARIANT)) && (fMultiValued || ((fMultiValued == FALSE) && (pColumn->wType != DBTYPE_BYTES) && (pColumn->wType != (DBTYPE_BYTES | DBTYPE_BYREF)))) ) { pColumn->dwStatus = DBSTATUS_E_CANTCONVERTVALUE; BAIL_ON_FAILURE(hr = DB_E_CANTCONVERTVALUE); }
pColumn->dwStatus = DBSTATUS_S_OK; pColumn->cbDataLen = 0;
if(pColumn->pData == NULL) // client doesn't want any data returned
RRETURN(S_OK);
if(m_cMaxColumns == -1) // GetObjectAttributes has not been called
{ hr = m_pADsObj->QueryInterface(__uuidof(IDirectoryObject), (void**)&pIDirObj); BAIL_ON_FAILURE(hr);
hr = pIDirObj->GetObjectAttributes(NULL, -1, &m_pAttrInfo, (DWORD *) &m_cMaxColumns); BAIL_ON_FAILURE(hr);
m_cMaxColumns++; // include ADsPath
}
// get the index of security descriptor in the attribute array
for(iAttr = 0; iAttr < (m_cMaxColumns-1); iAttr++) if(!_wcsicmp(m_pAttrInfo[iAttr].pszAttrName, NT_SEC_DESC_ATTR)) break;
if(iAttr == (m_cMaxColumns-1)) { pColumn->dwStatus = DBSTATUS_E_UNAVAILABLE; BAIL_ON_FAILURE(hr = DB_E_NOTFOUND); }
ADsAssert(m_pAttrInfo[iAttr].dwADsType == ADSTYPE_NT_SECURITY_DESCRIPTOR); if(fMultiValued) { // check if the client has enough space to copy over the variant
if(pColumn->wType & DBTYPE_BYREF) dwRequiredLen = sizeof(VARIANT *); else dwRequiredLen = sizeof(VARIANT);
if(pColumn->cbMaxLen < dwRequiredLen) { pColumn->dwStatus = DBSTATUS_E_CANTCONVERTVALUE; BAIL_ON_FAILURE(hr = DB_E_CANTCONVERTVALUE); }
aBound.lLbound = 0; aBound.cElements = m_pAttrInfo[iAttr].dwNumValues;
if(pColumn->wType & DBTYPE_BYREF) { pVariant = (PVARIANT) AllocADsMem(sizeof(VARIANT)); if(NULL == pVariant) BAIL_ON_FAILURE(hr = E_OUTOFMEMORY); } else pVariant = (PVARIANT) pColumn->pData;
VariantInit(pVariant);
aList = SafeArrayCreate( VT_VARIANT, 1, &aBound ); if (aList == NULL) BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
hr = SafeArrayAccessData( aList, (void **) &pVarArray ); BAIL_ON_FAILURE(hr);
vType = g_MapADsTypeToVarType[m_pAttrInfo[iAttr].dwADsType];
for (j=0; j<aBound.cElements; j++) { VariantInit(pVarArray+j); V_VT(pVarArray+j) = vType;
hr = BinaryToVariant( m_pAttrInfo[iAttr].pADsValues[j].SecurityDescriptor.dwLength, m_pAttrInfo[iAttr].pADsValues[j].SecurityDescriptor.lpValue, pVarArray+j);
if(FAILED(hr)) { int k;
for(k = 0; k < j; k++) VariantClear(pVarArray+k); }
BAIL_ON_FAILURE(hr); }
SafeArrayUnaccessData( aList );
V_VT((PVARIANT)pVariant) = VT_ARRAY | VT_VARIANT; V_ARRAY((PVARIANT)pVariant) = aList;
if(pColumn->wType & DBTYPE_BYREF) *(PVARIANT *)pColumn->pData = pVariant;
pColumn->cbDataLen = sizeof(VARIANT); } else // single valued
{ // check if the client has enough space to copy over the octet string
if(pColumn->wType & DBTYPE_VARIANT) { if(pColumn->wType & DBTYPE_BYREF) dwRequiredLen = sizeof(VARIANT *); else dwRequiredLen = sizeof(VARIANT); } else if(pColumn->wType & DBTYPE_BYTES) { if(pColumn->wType & DBTYPE_BYREF) dwRequiredLen = sizeof(BYTE *); else dwRequiredLen = m_pAttrInfo[iAttr].pADsValues[0].SecurityDescriptor.dwLength; }
if(pColumn->cbMaxLen < dwRequiredLen) { pColumn->dwStatus = DBSTATUS_E_CANTCONVERTVALUE; BAIL_ON_FAILURE(hr = DB_E_CANTCONVERTVALUE); }
if(pColumn->wType & DBTYPE_VARIANT) { if(pColumn->wType & DBTYPE_BYREF) { pVariant = (PVARIANT) AllocADsMem(sizeof(VARIANT)); if(NULL == pVariant) BAIL_ON_FAILURE(hr = E_OUTOFMEMORY); } else pVariant = (PVARIANT) pColumn->pData;
VariantInit(pVariant);
vType = g_MapADsTypeToVarType[m_pAttrInfo[iAttr].dwADsType]; V_VT(pVariant) = vType;
hr = BinaryToVariant( m_pAttrInfo[iAttr].pADsValues[0].SecurityDescriptor.dwLength, m_pAttrInfo[iAttr].pADsValues[0].SecurityDescriptor.lpValue, pVariant); BAIL_ON_FAILURE(hr);
if(pColumn->wType & DBTYPE_BYREF) *(PVARIANT *)pColumn->pData = pVariant;
pColumn->cbDataLen = sizeof(VARIANT); } else if(pColumn->wType & DBTYPE_BYTES) { if(pColumn->wType & DBTYPE_BYREF) *(BYTE **)pColumn->pData = m_pAttrInfo[iAttr].pADsValues[0].SecurityDescriptor.lpValue; else memcpy(pColumn->pData, m_pAttrInfo[iAttr].pADsValues[0].SecurityDescriptor.lpValue, m_pAttrInfo[iAttr].pADsValues[0].SecurityDescriptor.dwLength);
pColumn->cbDataLen = m_pAttrInfo[iAttr].pADsValues[0].SecurityDescriptor.dwLength; } }
RRETURN(S_OK);
error:
if(aList) SafeArrayDestroy(aList); if((pVariant) && (pColumn->wType & DBTYPE_BYREF)) FreeADsMem(pVariant);
RRETURN(hr); }
//---------------------------------------------------------------------------
// IgnorecbMaxLen
//
// This function returns 1 if the cbMaxLen field of DBCOLUMNACCESS structure
// should be ignored and 0 otherwise. cbMaxLen should be ignored for fixed
// length data types and data types combined with DBTYPE_BYREF, DBTYPE_VECTOR
// and DBTYPE_ARRAY (page 107, OLEDB 2.0 spec)
//
//---------------------------------------------------------------------------
int CRow::IgnorecbMaxLen(DBTYPE wType) { if( (wType & DBTYPE_BYREF) || (wType & DBTYPE_VECTOR) || (wType & DBTYPE_ARRAY) ) return 1;
wType &= ( (~DBTYPE_BYREF) & (~DBTYPE_VECTOR) & (~DBTYPE_ARRAY) );
// check if it is a variable length data type
if( (DBTYPE_STR == wType) || (DBTYPE_BYTES == wType) || (DBTYPE_WSTR == wType) || (DBTYPE_VARNUMERIC == wType) ) return 0;
// must be fixed length data type
return 1; } //-----------------------------------------------------------------------------
#endif
|