Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

2332 lines
81 KiB

// 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;
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