Leaked source code of windows server 2003
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.
 
 
 
 
 
 

4799 lines
152 KiB

/*++
Copyright (C) 1996-2001 Microsoft Corporation
Module Name:
QENGINE.CPP
Abstract:
WinMgmt Query Engine
History:
raymcc 20-Dec-96 Created
levn 97-98-99 Modified beyond comprehension
raymcc 14-Aug-99 Ripped out and relocated assocs to happy new home
raymcc 22-Apr-00 First mutations for new ProvSS
--*/
#include "precomp.h"
#include <stdio.h>
#include <stdlib.h>
#include <wbemcore.h>
#include <wbemstr.h>
#include <wbemmeta.h>
#include <analyser.h>
#include <genutils.h>
#include <DateTimeParser.h>
#include "stack.h"
#include <like.h>
#include "wmimerger.h"
#include <autoptr.h>
#include <wmiarbitrator.h>
//***************************************************************************
//
// Local defs
//
//***************************************************************************
#define INVALID 0x3
//***************************************************************************
//
//***************************************************************************
CQlFilteringSink::CQlFilteringSink(
CBasicObjectSink* pDest,
ADDREF QL_LEVEL_1_RPN_EXPRESSION* pExpr,
CWbemNamespace *pNamespace, BOOL bFilterNow
)
: CFilteringSink(pDest), m_pExpr(pExpr), m_bFilterNow(bFilterNow),
m_pNs(pNamespace)
{
// TBD: consider the query
m_pExpr->AddRef();
}
//***************************************************************************
//
//***************************************************************************
CQlFilteringSink::~CQlFilteringSink()
{
m_pExpr->Release();
}
//***************************************************************************
//
//***************************************************************************
STDMETHODIMP CQlFilteringSink::Indicate(
long lObjectCount,
IWbemClassObject** pObjArray
)
{
if(!m_bFilterNow)
return m_pDest->Indicate(lObjectCount, pObjArray);
return CFilteringSink::Indicate(lObjectCount, pObjArray);
}
BOOL CQlFilteringSink::Test(CWbemObject* pObj)
{
return CQlFilteringSink::Test(pObj, m_pExpr, m_pNs); // this function throws
}
//***************************************************************************
//
//***************************************************************************
BOOL CQlFilteringSink::Test(
CWbemObject* pObj,
QL_LEVEL_1_RPN_EXPRESSION* pExpr,
CWbemNamespace * pNs
)
{
CStack Stack;
// If a pure 'select' with no 'where' clause, we always
// return TRUE.
// ====================================================
if (pExpr->nNumTokens == 0)
return TRUE;
for (int i = 0; i < pExpr->nNumTokens; i++)
{
QL_LEVEL_1_TOKEN& Tok = pExpr->pArrayOfTokens[i];
if (Tok.nTokenType == QL_LEVEL_1_TOKEN::TOKEN_AND)
{
BOOL b1 = (BOOL) Stack.Pop();
BOOL b2 = (BOOL) Stack.Pop();
if (b1 == TRUE && b2 == TRUE)
Stack.Push(TRUE);
else if (b1 == INVALID || b2 == INVALID)
Stack.Push(INVALID);
else
Stack.Push(FALSE);
}
else if (Tok.nTokenType == QL_LEVEL_1_TOKEN::TOKEN_OR)
{
BOOL b1 = (BOOL) Stack.Pop();
BOOL b2 = (BOOL) Stack.Pop();
if (b1 == TRUE || b2 == TRUE)
Stack.Push(TRUE);
else if (b1 == INVALID || b2 == INVALID)
Stack.Push(INVALID);
else
Stack.Push(FALSE);
}
else if (Tok.nTokenType == QL_LEVEL_1_TOKEN::TOKEN_NOT)
{
BOOL b1 = (BOOL) Stack.Pop();
if (b1 == TRUE)
Stack.Push(FALSE);
else if (b1 == INVALID)
Stack.Push(INVALID);
else
Stack.Push(TRUE);
}
else if (Tok.nTokenType == QL_LEVEL_1_TOKEN::OP_EXPRESSION)
{
Stack.Push(EvaluateToken(pObj, Tok, pNs));
}
}
// Pop top element, which becomes the return value.
// ================================================
int nRes = Stack.Pop();
if (nRes == INVALID)
return FALSE;
return (BOOL) nRes;
}
//***************************************************************************
//
//***************************************************************************
int CQlFilteringSink::EvaluateToken(
IWbemPropertySource *pTestObj,
QL_LEVEL_1_TOKEN& Tok,
CWbemNamespace * pNs
)
{
_variant_t PropVal, CompVal;
WBEM_WSTR wszCimType, wszCimType2;
HRESULT hRes;
// Special-case 'this'
// ===================
if(Tok.PropertyName.GetNumElements() == 1 &&
!wbem_wcsicmp(Tok.PropertyName.GetStringAt(0), L"__THIS"))
{
wszCimType = WbemStringCopy(L"object");
V_VT(&PropVal) = VT_UNKNOWN;
hRes = pTestObj->QueryInterface(IID_IWbemClassObject,
(void**)&V_UNKNOWN(&PropVal));
}
else
{
hRes = pTestObj->GetPropertyValue(&Tok.PropertyName, 0,
&wszCimType, &PropVal);
}
if (FAILED(hRes)) return FALSE;
OnDelete<WBEM_WSTR,void(*)(WBEM_WSTR),WbemStringFree> wsf(wszCimType);
// Handle a property-to-property comparison,
if (Tok.m_bPropComp != FALSE)
{
hRes = pTestObj->GetPropertyValue(&Tok.PropertyName2, 0,
&wszCimType2, &CompVal);
if (FAILED(hRes))
return FALSE;
}
else
{
if ( FAILED (VariantCopy(&CompVal, &Tok.vConstValue)) )
return FALSE;
}
// Handle NULLs
// ============
if(V_VT(&PropVal) == VT_NULL)
{
if(V_VT(&CompVal) == VT_NULL)
{
if(Tok.nOperator == QL_LEVEL_1_TOKEN::OP_EQUAL)
return TRUE;
else if(Tok.nOperator == QL_LEVEL_1_TOKEN::OP_NOT_EQUAL)
return FALSE;
else
return INVALID;
}
else
{
if(Tok.nOperator == QL_LEVEL_1_TOKEN::OP_EQUAL)
return FALSE;
else if(Tok.nOperator == QL_LEVEL_1_TOKEN::OP_NOT_EQUAL)
return TRUE;
else
return INVALID;
}
}
else if(V_VT(&CompVal) == VT_NULL)
{
if(Tok.nOperator == QL_LEVEL_1_TOKEN::OP_EQUAL)
return FALSE;
else if(Tok.nOperator == QL_LEVEL_1_TOKEN::OP_NOT_EQUAL)
return TRUE;
else
return INVALID;
}
// Handle references
// =================
if(wszCimType &&
wbem_wcsnicmp(wszCimType, L"ref", 3) == 0 &&
(wszCimType[3] == 0 || wszCimType[3] == L':'))
{
// This is a reference. The only operators allowed are = and !=
// ============================================================
if(PropVal.vt != VT_BSTR || PropVal.bstrVal == NULL)
return INVALID;
if(CompVal.vt != VT_BSTR || CompVal.bstrVal == NULL)
return INVALID;
WCHAR * va = CQueryEngine::NormalizePath(V_BSTR(&PropVal), pNs);
CVectorDeleteMe<WCHAR> del_va(va);
WCHAR * vb = CQueryEngine::NormalizePath(V_BSTR(&CompVal), pNs);
CVectorDeleteMe<WCHAR> del_vb(vb);
if(va == NULL || vb == NULL)
{
ERRORTRACE((LOG_WBEMCORE, "Invalid path %S or %S specified in an association\n", V_BSTR(&PropVal), V_BSTR(&CompVal)));
return INVALID;
}
int nRet;
switch (Tok.nOperator)
{
case QL_LEVEL_1_TOKEN::OP_EQUAL:
nRet = (wbem_wcsicmp(va,vb) == 0);
break;
case QL_LEVEL_1_TOKEN::OP_NOT_EQUAL:
nRet = (wbem_wcsicmp(va, vb) != 0);
break;
default:
nRet = INVALID;
break;
}
return nRet;
}
// Check if ISA is used
// ====================
if(Tok.nOperator == QL1_OPERATOR_ISA ||
Tok.nOperator == QL1_OPERATOR_ISNOTA ||
Tok.nOperator == QL1_OPERATOR_INV_ISA ||
Tok.nOperator == QL1_OPERATOR_INV_ISNOTA)
{
// Account for inversion
// =====================
VARIANT* pv1;
VARIANT* pv2;
int bNeedDerived;
if(Tok.nOperator == QL1_OPERATOR_ISA ||
Tok.nOperator == QL1_OPERATOR_ISNOTA)
{
pv2 = &CompVal;
pv1 = &PropVal;
bNeedDerived = (Tok.nOperator == QL1_OPERATOR_ISA);
}
else
{
pv1 = &CompVal;
pv2 = &PropVal;
bNeedDerived = (Tok.nOperator == QL1_OPERATOR_INV_ISA);
}
// The second argument has to be a string
// ======================================
if(V_VT(pv2) != VT_BSTR)
{
return INVALID;
}
BSTR strParentClass = V_BSTR(pv2);
// The first argument has to be an object or a string
// ==================================================
BOOL bDerived;
if(V_VT(pv1) == VT_EMBEDDED_OBJECT)
{
IWbemClassObject* pObj = (IWbemClassObject*)V_EMBEDDED_OBJECT(pv1);
bDerived = (pObj->InheritsFrom(strParentClass) == WBEM_S_NO_ERROR);
}
else if(V_VT(pv1) == VT_BSTR)
{
// TBD
// ===
return INVALID;
}
else
{
return INVALID;
}
// Now that we have bDerived, see if it matches the requirement
// ============================================================
if(bDerived == bNeedDerived)
return TRUE;
else
return FALSE;
}
// Perform UINT32 workaround
// =========================
if(wszCimType && !wbem_wcsicmp(wszCimType, L"uint32") &&
V_VT(&PropVal) == VT_I4)
{
DWORD dwVal = (DWORD)V_I4(&PropVal);
WCHAR wszVal[20];
StringCchPrintfW(wszVal, 20, L"%lu", dwVal);
BSTR bstrTmp = SysAllocString(wszVal);
if (bstrTmp)
{
V_VT(&PropVal) = VT_BSTR;
V_BSTR(&PropVal) = bstrTmp;
}
else
{
V_VT(&PropVal) = VT_NULL;
}
}
if(wszCimType &&
(!wbem_wcsicmp(wszCimType, L"sint64") ||
!wbem_wcsicmp(wszCimType, L"uint64") ||
!wbem_wcsicmp(wszCimType, L"uint32")) &&
V_VT(&CompVal) != VT_NULL && V_VT(&PropVal) != VT_NULL)
{
BOOL bUnsigned = (wbem_wcsicmp(wszCimType, L"uint64") == 0);
// We have a 64-bit comparison where both sides are present.
// =========================================================
hRes = VariantChangeType(&CompVal, &CompVal, 0,
VT_BSTR);
if(FAILED(hRes))
{
return INVALID;
}
if(bUnsigned)
{
unsigned __int64 ui64Prop, ui64Const;
if(!ReadUI64(V_BSTR(&PropVal), ui64Prop))
return INVALID;
if(!ReadUI64(V_BSTR(&CompVal), ui64Const))
return INVALID;
switch (Tok.nOperator)
{
case QL_LEVEL_1_TOKEN::OP_EQUAL: return (ui64Prop == ui64Const);
case QL_LEVEL_1_TOKEN::OP_NOT_EQUAL:
return (ui64Prop != ui64Const);
case QL_LEVEL_1_TOKEN::OP_EQUALorGREATERTHAN:
return (ui64Prop >= ui64Const);
case QL_LEVEL_1_TOKEN::OP_EQUALorLESSTHAN:
return (ui64Prop <= ui64Const);
case QL_LEVEL_1_TOKEN::OP_LESSTHAN:
return (ui64Prop < ui64Const);
case QL_LEVEL_1_TOKEN::OP_GREATERTHAN:
return (ui64Prop > ui64Const);
case QL_LEVEL_1_TOKEN::OP_LIKE: return (ui64Prop == ui64Const);
}
return INVALID;
}
else
{
__int64 i64Prop, i64Const;
if(!ReadI64(V_BSTR(&PropVal), i64Prop))
return INVALID;
if(!ReadI64(V_BSTR(&CompVal), i64Const))
return INVALID;
switch (Tok.nOperator)
{
case QL_LEVEL_1_TOKEN::OP_EQUAL: return (i64Prop == i64Const);
case QL_LEVEL_1_TOKEN::OP_NOT_EQUAL:
return (i64Prop != i64Const);
case QL_LEVEL_1_TOKEN::OP_EQUALorGREATERTHAN:
return (i64Prop >= i64Const);
case QL_LEVEL_1_TOKEN::OP_EQUALorLESSTHAN:
return (i64Prop <= i64Const);
case QL_LEVEL_1_TOKEN::OP_LESSTHAN:
return (i64Prop < i64Const);
case QL_LEVEL_1_TOKEN::OP_GREATERTHAN:
return (i64Prop > i64Const);
case QL_LEVEL_1_TOKEN::OP_LIKE: return (i64Prop == i64Const);
}
return INVALID;
}
}
if(wszCimType && !wbem_wcsicmp(wszCimType, L"char16") &&
V_VT(&CompVal) == VT_BSTR && V_VT(&PropVal) != VT_NULL)
{
// Coerce strings correctly
// ========================
BSTR str = V_BSTR(&Tok.vConstValue);
if (wcslen(str) != 1) // SEC:REVIEWED 2002-03-22 : OK, provably recognized by lexer
return INVALID;
short va = V_I2(&PropVal);
short vb = str[0];
switch (Tok.nOperator)
{
case QL_LEVEL_1_TOKEN::OP_EQUAL: return (va == vb);
case QL_LEVEL_1_TOKEN::OP_NOT_EQUAL: return (va != vb);
case QL_LEVEL_1_TOKEN::OP_EQUALorGREATERTHAN: return (va >= vb);
case QL_LEVEL_1_TOKEN::OP_EQUALorLESSTHAN: return (va <= vb);
case QL_LEVEL_1_TOKEN::OP_LESSTHAN: return (va < vb);
case QL_LEVEL_1_TOKEN::OP_GREATERTHAN: return (va > vb);
case QL_LEVEL_1_TOKEN::OP_LIKE: return (va == vb);
}
return INVALID;
}
if(wszCimType &&
(!wbem_wcsicmp(wszCimType, L"datetime")) &&
V_VT(&CompVal) == VT_BSTR && V_VT(&PropVal) == VT_BSTR)
{
// Parse the constant specified in the query according to the
// SQL rules
// ==========================================================
TCHAR *tszBuffer;
tszBuffer = V_BSTR(&CompVal);
CDateTimeParser dtConst(tszBuffer);
if(!dtConst.IsValidDateTime())
return INVALID;
WCHAR wszConstValDMTF[26];
dtConst.FillDMTF(wszConstValDMTF, 26);
// Read both DMTF values and parse them
// ====================================
CWbemTime wtConst, wtProp;
if(!wtConst.SetDMTF(wszConstValDMTF))
return INVALID;
if(!wtProp.SetDMTF(V_BSTR(&PropVal)))
return INVALID;
__int64 i64Const = wtConst.Get100nss();
__int64 i64Prop = wtProp.Get100nss();
switch (Tok.nOperator)
{
case QL_LEVEL_1_TOKEN::OP_EQUAL: return (i64Prop == i64Const);
case QL_LEVEL_1_TOKEN::OP_NOT_EQUAL:
return (i64Prop != i64Const);
case QL_LEVEL_1_TOKEN::OP_EQUALorGREATERTHAN:
return (i64Prop >= i64Const);
case QL_LEVEL_1_TOKEN::OP_EQUALorLESSTHAN:
return (i64Prop <= i64Const);
case QL_LEVEL_1_TOKEN::OP_LESSTHAN:
return (i64Prop < i64Const);
case QL_LEVEL_1_TOKEN::OP_GREATERTHAN:
return (i64Prop > i64Const);
case QL_LEVEL_1_TOKEN::OP_LIKE: return (i64Prop == i64Const);
}
}
// Coerce types to match.
// ======================
if(V_VT(&CompVal) != VT_NULL && V_VT(&PropVal) != VT_NULL)
{
// Compensate for VT_UI1 > VT_I4
//
if (V_VT(&CompVal) == VT_UI1 && V_VT(&PropVal) !=VT_UI1)
hRes = VariantChangeType(&CompVal,&CompVal,0, VT_I4);
if (V_VT(&PropVal) == VT_UI1 && V_VT(&CompVal) !=VT_UI1)
hRes = VariantChangeType(&PropVal,&PropVal,0, VT_I4);
if (V_VT(&PropVal) > V_VT(&CompVal))
hRes = VariantChangeType(&CompVal, &CompVal, 0, V_VT(&PropVal));
else
hRes = VariantChangeType(&PropVal, &PropVal, 0, V_VT(&CompVal));
if(FAILED(hRes))
{
return INVALID;
}
}
switch (V_VT(&CompVal))
{
case VT_NULL:
return INVALID; // handled above
case VT_I4:
{
if(V_VT(&PropVal) == VT_NULL)
return INVALID;
LONG va = V_I4(&PropVal);
LONG vb = V_I4(&CompVal);
switch (Tok.nOperator)
{
case QL_LEVEL_1_TOKEN::OP_EQUAL: return (va == vb);
case QL_LEVEL_1_TOKEN::OP_NOT_EQUAL: return (va != vb);
case QL_LEVEL_1_TOKEN::OP_EQUALorGREATERTHAN: return (va >= vb);
case QL_LEVEL_1_TOKEN::OP_EQUALorLESSTHAN: return (va <= vb);
case QL_LEVEL_1_TOKEN::OP_LESSTHAN: return (va < vb);
case QL_LEVEL_1_TOKEN::OP_GREATERTHAN: return (va > vb);
case QL_LEVEL_1_TOKEN::OP_LIKE: return (va == vb);
}
}
break;
case VT_I2:
{
if(V_VT(&PropVal) == VT_NULL)
return INVALID;
short va = V_I2(&PropVal);
short vb = V_I2(&CompVal);
switch (Tok.nOperator)
{
case QL_LEVEL_1_TOKEN::OP_EQUAL: return (va == vb);
case QL_LEVEL_1_TOKEN::OP_NOT_EQUAL: return (va != vb);
case QL_LEVEL_1_TOKEN::OP_EQUALorGREATERTHAN: return (va >= vb);
case QL_LEVEL_1_TOKEN::OP_EQUALorLESSTHAN: return (va <= vb);
case QL_LEVEL_1_TOKEN::OP_LESSTHAN: return (va < vb);
case QL_LEVEL_1_TOKEN::OP_GREATERTHAN: return (va > vb);
case QL_LEVEL_1_TOKEN::OP_LIKE: return (va == vb);
}
}
break;
case VT_UI1:
{
if(V_VT(&PropVal) == VT_NULL)
return INVALID;
BYTE va = V_I1(&PropVal);
BYTE vb = V_I1(&CompVal);
switch (Tok.nOperator)
{
case QL_LEVEL_1_TOKEN::OP_EQUAL: return (va == vb);
case QL_LEVEL_1_TOKEN::OP_NOT_EQUAL: return (va != vb);
case QL_LEVEL_1_TOKEN::OP_EQUALorGREATERTHAN: return (va >= vb);
case QL_LEVEL_1_TOKEN::OP_EQUALorLESSTHAN: return (va <= vb);
case QL_LEVEL_1_TOKEN::OP_LESSTHAN: return (va < vb);
case QL_LEVEL_1_TOKEN::OP_GREATERTHAN: return (va > vb);
case QL_LEVEL_1_TOKEN::OP_LIKE: return (va == vb);
}
}
break;
case VT_BSTR:
{
if(V_VT(&PropVal) == VT_NULL)
return INVALID;
LPWSTR va = (LPWSTR) V_BSTR(&PropVal);
LPWSTR vb = (LPWSTR) V_BSTR(&CompVal);
int retCode = 0;
BOOL bDidIt = TRUE;
switch (Tok.nOperator)
{
case QL_LEVEL_1_TOKEN::OP_EQUAL:
retCode = ( wbem_wcsicmp(va,vb) == 0);
break;
case QL_LEVEL_1_TOKEN::OP_NOT_EQUAL:
retCode = (wbem_wcsicmp(va, vb) != 0);
break;
case QL_LEVEL_1_TOKEN::OP_EQUALorGREATERTHAN:
retCode = (wbem_wcsicmp(va, vb) >= 0);
break;
case QL_LEVEL_1_TOKEN::OP_EQUALorLESSTHAN:
retCode = (wbem_wcsicmp(va, vb) <= 0);
break;
case QL_LEVEL_1_TOKEN::OP_LESSTHAN:
retCode = (wbem_wcsicmp(va, vb) < 0);
break;
case QL_LEVEL_1_TOKEN::OP_GREATERTHAN:
retCode = (wbem_wcsicmp(va, vb) > 0);
break;
case QL_LEVEL_1_TOKEN::OP_LIKE:
{
CLike l (vb);
retCode = (int)l.Match(va);
}
break;
default:
bDidIt = FALSE;
break;
}
VariantClear(&CompVal);
if (bDidIt)
{
return retCode;
}
}
break;
case VT_R8:
{
if(V_VT(&PropVal) == VT_NULL)
return INVALID;
double va = V_R8(&PropVal);
double vb = V_R8(&CompVal);
switch (Tok.nOperator)
{
case QL_LEVEL_1_TOKEN::OP_EQUAL: return (va == vb);
case QL_LEVEL_1_TOKEN::OP_NOT_EQUAL: return (va != vb);
case QL_LEVEL_1_TOKEN::OP_EQUALorGREATERTHAN: return (va >= vb);
case QL_LEVEL_1_TOKEN::OP_EQUALorLESSTHAN: return (va <= vb);
case QL_LEVEL_1_TOKEN::OP_LESSTHAN: return (va < vb);
case QL_LEVEL_1_TOKEN::OP_GREATERTHAN: return (va > vb);
case QL_LEVEL_1_TOKEN::OP_LIKE: return (va == vb);
}
}
break;
case VT_R4:
{
if(V_VT(&PropVal) == VT_NULL)
return INVALID;
float va = V_R4(&PropVal);
float vb = V_R4(&CompVal);
switch (Tok.nOperator)
{
case QL_LEVEL_1_TOKEN::OP_EQUAL: return (va == vb);
case QL_LEVEL_1_TOKEN::OP_NOT_EQUAL: return (va != vb);
case QL_LEVEL_1_TOKEN::OP_EQUALorGREATERTHAN: return (va >= vb);
case QL_LEVEL_1_TOKEN::OP_EQUALorLESSTHAN: return (va <= vb);
case QL_LEVEL_1_TOKEN::OP_LESSTHAN: return (va < vb);
case QL_LEVEL_1_TOKEN::OP_GREATERTHAN: return (va > vb);
case QL_LEVEL_1_TOKEN::OP_LIKE: return (va == vb);
}
}
break;
case VT_BOOL:
{
if(V_VT(&PropVal) == VT_NULL)
return INVALID;
VARIANT_BOOL va = V_BOOL(&PropVal);
if(va != VARIANT_FALSE) va = VARIANT_TRUE;
VARIANT_BOOL vb = V_BOOL(&CompVal);
if(vb != VARIANT_FALSE) vb = VARIANT_TRUE;
switch (Tok.nOperator)
{
case QL_LEVEL_1_TOKEN::OP_EQUAL: return (va == vb);
case QL_LEVEL_1_TOKEN::OP_NOT_EQUAL: return (va != vb);
case QL_LEVEL_1_TOKEN::OP_EQUALorGREATERTHAN: return INVALID;
case QL_LEVEL_1_TOKEN::OP_EQUALorLESSTHAN: return INVALID;
case QL_LEVEL_1_TOKEN::OP_LESSTHAN: return INVALID;
case QL_LEVEL_1_TOKEN::OP_GREATERTHAN: return INVALID;
case QL_LEVEL_1_TOKEN::OP_LIKE: return (va == vb);
}
}
break;
}
return FALSE;
}
//***************************************************************************
//
//***************************************************************************
STDMETHODIMP CQlFilteringSink::SetStatus(
long lFlags,
long lParam,
BSTR strParam,
IWbemClassObject* pObjParam
)
{
if(lFlags == WBEM_STATUS_REQUIREMENTS)
{
m_bFilterNow = (lParam == S_OK);
return WBEM_S_NO_ERROR;
}
else
{
return CFilteringSink::SetStatus(lFlags, lParam, strParam, pObjParam);
}
}
//***************************************************************************
//
//***************************************************************************
CProjectingSink::CProjectingSink(
CBasicObjectSink* pDest,
CWbemClass* pClassDef,
READONLY QL_LEVEL_1_RPN_EXPRESSION* pExp,
long lQueryFlags
)
: CForwardingSink(pDest, 0), m_bValid(FALSE), m_bProjecting(FALSE)
{
// Extract the properties selected by the user.
// ============================================
CWStringArray awsPropList;
for (int i = 0; i < pExp->nNumberOfProperties; i++)
{
CPropertyName& PropName = pExp->pRequestedPropertyNames[i];
LPWSTR wszPrimaryName = CQueryEngine::GetPrimaryName(PropName);
if (wszPrimaryName && !wbem_wcsicmp(wszPrimaryName, L"count(*)"))
{
m_bValid = TRUE;
m_bProjecting = FALSE;
return;
}
// Check for complexity
// ====================
if(PropName.GetNumElements() > 1)
{
// Complex --- make sure the property is an object
// ===============================================
CIMTYPE ct;
if(FAILED(pClassDef->GetPropertyType(wszPrimaryName, &ct)) ||
ct != CIM_OBJECT)
{
m_wsError = wszPrimaryName;
return;
}
}
if (CFlexArray::no_error != awsPropList.Add(wszPrimaryName))
{
return;
}
}
if (awsPropList.Size() == 0)
{
m_bValid = TRUE;
return;
}
if(lQueryFlags & WBEM_FLAG_ENSURE_LOCATABLE)
{
if (CFlexArray::no_error != awsPropList.Add(L"__PATH"))
{
return;
};
}
// Verify that the projection will succeed.
// ========================================
m_wsError = pClassDef->FindLimitationError(0, &awsPropList);
if(m_wsError.Length() > 0)
return;
// Check for *
// ===========
if(pExp->bStar)
{
m_bValid = TRUE;
return;
}
// Map the limitaiton
// ==================
m_bValid = pClassDef->MapLimitation(0, &awsPropList, &m_Map);
m_bProjecting = TRUE;
}
//***************************************************************************
//
//***************************************************************************
STDMETHODIMP CProjectingSink::Indicate(
long lObjectCount,
IWbemClassObject** pObjArray
)
{
if(!m_bProjecting)
return m_pDest->Indicate(lObjectCount, pObjArray);
wmilib::auto_buffer<IWbemClassObject*> apNewArray(new IWbemClassObject*[lObjectCount]);
if (NULL == apNewArray.get())
return WBEM_E_OUT_OF_MEMORY;
HRESULT hres;
int i;
{
CInCritSec ics(&m_cs); // SEC:REVIEWED 2002-03-22 : Assumes entry
for(i = 0; i < lObjectCount; i++)
{
CWbemInstance* pInst = (CWbemInstance*)pObjArray[i];
CWbemInstance* pNewInst;
hres = pInst->GetLimitedVersion(&m_Map, &pNewInst);
if(FAILED(hres))
{
for(int j = 0; j < i; j++)
apNewArray[j]->Release();
return hres;
}
apNewArray[i] = pNewInst;
}
}
hres = m_pDest->Indicate(lObjectCount, (IWbemClassObject**)apNewArray.get());
for(i = 0; i < lObjectCount; i++)
apNewArray[i]->Release();
return hres;
}
//***************************************************************************
//
// class CMerger
//
// This class is a 'reverse fork'. It consumes two sinks and outputs
// one. Its purpose is to merge instances of the same key in a given
// dynasty. Each CMerger has two inputs, (a) instances of the class
// in question, (b) instances of from another Merger representing
// instances of subclasses. Given classes A,B:A,C:B, for example,
// where "<--" is a sink:
//
// | own:Instances of A
// <---| | own:Instances of B
// | child: <--------|
// | child:Instances of C
//
//
// The two input sinks for CMerger are <m_pOwnSink> which receives
// instances from the provider for "A", for example, and the <m_pChildSink>
// which receives instances from the underyling Merger.
//
// The mergers operate asynchronously to each other. Therefore,
// the instances for A may arrive in its CMerger sink before instances
// of the child classes have arrived in theirs.
//
// As objects arrive in the owning CMerger for a class, AddOwnObject()
// is called. As objects arrive from a child sink, AddChildObject()
// is called. In either case, if the object with a given key
// arrives for the first time, it is simply added to the map. If
// it is already there (via a key lookup), then a merge is performed
// via CWbemInstance::AsymmetricMerge. Immediately after this merge,
// the object is dispatched up to the next parent sink via the parent's
// AddChildObject and removed from the map.
//
// Note that in a class hierarchy {A,B:A,C:B} an enumeration/query is
// performed only against the classes in the CDynasty referenced in
// the query. This logic occurs in CQueryEngine::EvaluateSubQuery.
// For example, if "select * from B" is the query, only queries
// for B and C are performed. The CMerger logic will do individual
// 'get object' calls for any instances needed in A to complete
// the merged B/C instances while merging is taking place.
//
//***************************************************************************
typedef map<WString, CMerger::CRecord, WSiless, wbem_allocator<CMerger::CRecord> >::iterator TMapIterator;
#pragma warning(disable:4355)
CMerger::CMerger(
CBasicObjectSink* pDest,
CWbemClass* pOwnClass,
CWbemNamespace* pNamespace,
IWbemContext* pContext
)
: m_pDest(pDest), m_bOwnDone(FALSE),
m_bChildrenDone(FALSE), m_pNamespace(pNamespace), m_pContext(pContext),
m_pOwnClass(pOwnClass), m_bDerivedFromTarget(TRUE), m_lRef(0),
m_pSecurity(NULL)
{
m_pOwnSink = new COwnSink(this);
m_pChildSink = new CChildSink(this);
// IsValid will check for these allocation failures
m_pDest->AddRef();
if(m_pContext)
m_pContext->AddRef();
if(m_pNamespace)
m_pNamespace->AddRef();
if(m_pOwnClass)
{
m_pOwnClass->AddRef();
CVar v;
pOwnClass->GetClassName(&v);
m_wsClass = v.GetLPWSTR();
}
// Retrieve call security. Need to create a copy for use on another thread
// =======================================================================
m_pSecurity = CWbemCallSecurity::MakeInternalCopyOfThread();
}
//***************************************************************************
//
//***************************************************************************
CMerger::~CMerger()
{
m_pDest->Release();
if(m_pNamespace)
m_pNamespace->Release();
if(m_pContext)
m_pContext->Release();
if(m_pOwnClass)
m_pOwnClass->Release();
delete m_pOwnSink;
delete m_pChildSink;
if(m_pSecurity)
m_pSecurity->Release();
}
//***************************************************************************
//
//***************************************************************************
long CMerger::AddRef()
{
return InterlockedIncrement(&m_lRef);
}
//***************************************************************************
//
//***************************************************************************
long CMerger::Release()
{
long lRef = InterlockedDecrement(&m_lRef);
if(lRef == 0)
delete this;
return lRef;
}
//***************************************************************************
//
//***************************************************************************
void CMerger::GetKey(IWbemClassObject* pObj, WString& wsKey)
{
LPWSTR wszRelPath = ((CWbemInstance*)pObj)->GetRelPath();
if (wszRelPath == NULL)
{
ERRORTRACE((LOG_WBEMCORE, "Object with no path submitted to a "
"merge\n"));
wsKey.Empty();
return;
}
WCHAR* pwcDot = wcschr(wszRelPath, L'.');
if (pwcDot == NULL)
{
ERRORTRACE((LOG_WBEMCORE, "Object with invalid path %S submitted to a "
"merge\n", wszRelPath));
wsKey.Empty();
// Clean up the path
delete [] wszRelPath;
return;
}
wsKey = pwcDot+1;
delete [] wszRelPath;
}
//***************************************************************************
//
//***************************************************************************
void CMerger::SetIsDerivedFromTarget(BOOL bIs)
{
m_bDerivedFromTarget = bIs;
if (!bIs)
{
// We will need our OwnSink for GetObject calls
// ============================================
m_pOwnSink->AddRef();
}
}
//***************************************************************************
//
//***************************************************************************
HRESULT CMerger::AddOwnObject(IWbemClassObject* pObj)
{
WString wsKey;
GetKey(pObj, wsKey);
CInCritSec ics(&m_cs); // SEC:REVIEWED 2002-03-22 : Assumes entry
TMapIterator it = m_map.find(wsKey);
if (it == m_map.end())
{
// Not there. Check if there is any hope for children
// ==================================================
if (m_bChildrenDone)
{
if (m_bDerivedFromTarget)
{
// forward
m_pDest->Add(pObj);
}
else
{
// ignore
}
}
else
{
try
{
// Insert
CRecord& rRecord = m_map[wsKey];
rRecord.m_pData = (CWbemInstance*) pObj;
pObj->AddRef();
rRecord.m_bOwn = TRUE;
}
catch( CX_Exception &)
{
return WBEM_E_OUT_OF_MEMORY;
}
}
}
else
{
// Attempt to merge
// ================
HRESULT hres = CWbemInstance::AsymmetricMerge(
(CWbemInstance*)pObj,
(CWbemInstance*)it->second.m_pData);
if(FAILED(hres))
{
ERRORTRACE((LOG_WBEMCORE, "Merge conflict for instances with "
"key %S\n", wsKey));
}
// Dispatch the result!
// ====================
m_pDest->Add(it->second.m_pData);
it->second.m_pData->Release();
m_map.erase(it);
}
return WBEM_S_NO_ERROR;
}
//***************************************************************************
//
//***************************************************************************
HRESULT CMerger::AddChildObject(IWbemClassObject* pObj)
{
HRESULT hRes = S_OK ;
WString wsKey;
GetKey(pObj, wsKey);
CInCritSec ics(&m_cs); // SEC:REVIEWED 2002-03-22 : Assumes entry
TMapIterator it = m_map.find(wsKey);
if (it == m_map.end())
{
// Check if there is any hope for parent
// =====================================
if(m_bOwnDone)
{
BSTR str = NULL;
pObj->GetObjectText(0, &str);
// The following was commented out because it actually incorrectly logs
// an error if the child provider enumerates when the parent provider
// interprets a query and returns fewer instances. Neither provider is wrong,
// but this error message causes needless worry. In Quasar, we have to fix
// this whole merger thing to be smarter anyway.
//
// ERRORTRACE((LOG_WBEMCORE, "[Chkpt_1] [%S] Orphaned object %S returned by "
// "provider\n", LPWSTR(m_wsClass), str));
SysFreeString(str);
// m_pDest->Add(pObj);
}
else
{
// insert
try
{
CRecord& rRecord = m_map[wsKey];
rRecord.m_pData = (CWbemInstance*)pObj;
pObj->AddRef();
rRecord.m_bOwn = FALSE;
// Check if parent's retrieval is needed
// =====================================
if (!m_bDerivedFromTarget)
{
try
{
GetOwnInstance(wsKey);
}
catch( CX_MemoryException &)
{
hRes = WBEM_E_OUT_OF_MEMORY;
}
catch (...)
{
ExceptionCounter c;
hRes = WBEM_E_CRITICAL_ERROR;
}
/*
* return here because exclusion area has already been exited.
*/
return hRes ;
}
}
catch( CX_MemoryException &)
{
hRes = WBEM_E_OUT_OF_MEMORY;
}
catch(...)
{
ExceptionCounter c;
hRes = WBEM_E_CRITICAL_ERROR;
}
}
}
else if(!it->second.m_bOwn)
{
ERRORTRACE((LOG_WBEMCORE, "Two providers supplied conflicting "
"instances for key %S\n", wsKey));
}
else
{
// Attempt to merge
// ================
IWbemClassObject* pClone;
HRESULT hres = pObj->Clone(&pClone);
if (FAILED(hres))
{
ERRORTRACE((LOG_WBEMCORE, "Clone failed in AddChildObject, hresult is 0x%x",
hres));
return hres;
}
hres = CWbemInstance::AsymmetricMerge(
(CWbemInstance*)it->second.m_pData,
(CWbemInstance*)pClone
);
if (FAILED(hres))
{
ERRORTRACE((LOG_WBEMCORE, "Merge conflict for instances with "
"key %S\n", wsKey));
}
// Dispatch the result!
// ====================
m_pDest->Add(pClone);
pClone->Release();
it->second.m_pData->Release();
m_map.erase(it);
}
return WBEM_S_NO_ERROR;
}
//***************************************************************************
//
//***************************************************************************
void CMerger::DispatchChildren()
{
TMapIterator it = m_map.begin();
while (it != m_map.end())
{
if (!it->second.m_bOwn)
{
BSTR str = NULL;
it->second.m_pData->GetObjectText(0, &str);
// The following was commented out because it actually incorrectly logs
// an error if the child provider enumerates when the parent provider
// interprets a query and returns fewer instances. Neither provider is wrong,
// but this error message causes needless worry. In Quasar, we have to fix
// this whole merger thing to be smarter anyway.
//
// ERRORTRACE((LOG_WBEMCORE, "Chkpt2 [%S] Orphaned object %S returned by "
// "provider\n", LPWSTR(m_wsClass), str));
SysFreeString(str);
// m_pDest->Add(it->second.m_pData);
it->second.m_pData->Release();
it = m_map.erase(it);
}
else it++;
}
}
//***************************************************************************
//
//***************************************************************************
void CMerger::DispatchOwn()
{
if(!m_bDerivedFromTarget)
return;
try
{
TMapIterator it = m_map.begin();
while (it != m_map.end())
{
if(it->second.m_bOwn)
{
m_pDest->Add(it->second.m_pData);
it->second.m_pData->Release();
it = m_map.erase(it);
}
else it++;
}
}
catch(...)
{
ExceptionCounter c;
}
}
//***************************************************************************
//
//***************************************************************************
// SEC:REVIEWED 2002-03-22 : This whole function needs a rewrite
void CMerger::GetOwnInstance(LPCWSTR wszKey)
{
size_t tmpLength = wcslen(wszKey) + m_wsClass.Length() + 2; // SEC:REVIEWED 2002-03-22 : Needs EH
WCHAR * wszPath = new WCHAR[tmpLength];
CVectorDeleteMe<WCHAR> dm(wszPath);
if (wszPath && wcslen(wszKey)) // SEC:REVIEWED 2002-03-22 : Needs EH
{
StringCchPrintf(wszPath, tmpLength, L"%s.%s", (LPCWSTR)m_wsClass, wszKey);
{
HRESULT hr;
IServerSecurity * pSec = NULL;
hr = CoGetCallContext(IID_IServerSecurity,(void **)&pSec);
CReleaseMe rmSec(pSec);
if (RPC_E_CALL_COMPLETE == hr ) hr = S_OK; // no call context
if (FAILED(hr)) return;
BOOL bImper = (pSec)?pSec->IsImpersonating():FALSE;
if (pSec && bImper && FAILED(hr = pSec->RevertToSelf())) return;
// implant a call context
IUnknown* pOld;
// fails only if COM not initialized on the thread
if (FAILED(CoSwitchCallContext(m_pSecurity, &pOld))) return;
hr = m_pNamespace->DynAux_GetSingleInstance(m_pOwnClass, 0, wszPath,m_pContext, m_pOwnSink);
// remove the planted call context
IUnknown* pThis;
CoSwitchCallContext(pOld, &pThis);
if (bImper && pSec)
{
HRESULT hrInner = pSec->ImpersonateClient();
if (FAILED(hrInner)) throw CX_Exception();
}
}
}
}
//***************************************************************************
//
//***************************************************************************
void CMerger::OwnIsDone()
{
m_bOwnDone = TRUE;
m_pOwnSink = NULL;
}
//***************************************************************************
//
//***************************************************************************
void CMerger::ChildrenAreDone()
{
m_bChildrenDone = TRUE;
m_pChildSink = NULL;
if(!m_bDerivedFromTarget)
{
// Don't need that ref count on pOwnSink anymore
// =============================================
m_pOwnSink->Release();
}
}
//***************************************************************************
//
//***************************************************************************
STDMETHODIMP CMerger::CMemberSink::
SetStatus(long lFlags, long lParam, BSTR strParam, IWbemClassObject* pObjParam)
{
if(lFlags == 0 && lParam == WBEM_E_NOT_FOUND)
lParam = WBEM_S_NO_ERROR;
// Propagate error to error combining sink
// =======================================
return m_pMerger->m_pDest->SetStatus(lFlags, lParam, strParam,
pObjParam);
}
//***************************************************************************
//
//***************************************************************************
CMerger::COwnSink::~COwnSink()
{
m_pMerger->Enter();
m_pMerger->DispatchChildren();
m_pMerger->OwnIsDone();
if (m_pMerger->Release())
m_pMerger->Leave();
}
//***************************************************************************
//
//***************************************************************************
STDMETHODIMP CMerger::COwnSink::
Indicate(long lNumObjects, IWbemClassObject** apObjects)
{
HRESULT hr = WBEM_S_NO_ERROR;
for(long l = 0; SUCCEEDED( hr ) && l < lNumObjects; l++)
{
hr = m_pMerger->AddOwnObject(apObjects[l]);
}
return hr;
}
//***************************************************************************
//
//***************************************************************************
CMerger::CChildSink::~CChildSink()
{
m_pMerger->Enter();
m_pMerger->DispatchOwn();
m_pMerger->ChildrenAreDone();
if(m_pMerger->Release())
m_pMerger->Leave();
}
//***************************************************************************
//
//***************************************************************************
STDMETHODIMP CMerger::CChildSink::Indicate(long lNumObjects, IWbemClassObject** apObjects)
{
HRESULT hr = WBEM_S_NO_ERROR;
for (long l = 0; SUCCEEDED( hr ) && l < lNumObjects; l++)
{
hr = m_pMerger->AddChildObject(apObjects[l]);
}
return hr;
}
//***************************************************************************
//
//***************************************************************************
CProjectionRule* CProjectionRule::Find(LPCWSTR wszName)
{
for(int i = 0; i < m_apPropRules.GetSize(); i++)
{
if(!wbem_wcsicmp(m_apPropRules[i]->m_wsPropName, wszName))
return m_apPropRules[i];
}
return NULL;
}
//***************************************************************************
//
//***************************************************************************
CComplexProjectionSink::CComplexProjectionSink(CBasicObjectSink* pDest,
CWQLScanner * pParser)
: CForwardingSink(pDest)
{
m_TopRule.m_eType = CProjectionRule::e_TakePart;
bool bFirstTableEntry = true;
CWStringArray awsTables;
pParser->GetReferencedAliases(awsTables);
WString wsPrefix;
if(awsTables.Size() == 0)
{
m_TopRule.m_eType = CProjectionRule::e_TakeAll;
return;
}
else if(awsTables.Size() == 1)
{
wsPrefix = awsTables[0]; // throw
}
// Extract projection rules from the parser
// ========================================
const CFlexArray* papColumns = pParser->GetSelectedColumns();
if(papColumns == NULL)
return;
for(int i = 0; i < papColumns->Size(); i++)
{
SWQLColRef* pColRef = (SWQLColRef*)papColumns->GetAt(i);
if(pColRef->m_dwFlags & WQL_FLAG_ASTERISK)
{
m_TopRule.m_eType = CProjectionRule::e_TakeAll;
}
else
{
LPWSTR pPrefix = NULL;
if(pColRef->m_dwFlags & WQL_FLAG_TABLE || pColRef->m_dwFlags & WQL_FLAG_ALIAS)
{
if(bFirstTableEntry)
if(pColRef->m_dwFlags & WQL_FLAG_ALIAS)
{
m_FirstTable = pParser->AliasToTable(pColRef->m_pTableRef);
m_FirstTableAlias = pColRef->m_pTableRef;
}
else
m_FirstTable = pColRef->m_pTableRef;
pPrefix = pColRef->m_pTableRef;
bFirstTableEntry = false;
}
else if(wsPrefix.Length())
pPrefix = (LPWSTR)wsPrefix;
AddColumn(pColRef->m_pQName->m_aFields, pPrefix);
}
}
if(pParser->CountQuery())
{
// Add the rule for 'count'
// ========================
wmilib::auto_ptr<CProjectionRule> pCountRule( new CProjectionRule(L"count"));
if (NULL == pCountRule.get()) throw CX_MemoryException();
pCountRule->m_eType = CProjectionRule::e_TakeAll;
if (CFlexArray::no_error != m_TopRule.m_apPropRules.Add(pCountRule.get()))
throw CX_MemoryException();
pCountRule.release();
}
};
//***************************************************************************
//
//***************************************************************************
void CComplexProjectionSink::AddColumn(CFlexArray& aFields, LPCWSTR wszPrefix)
{
CProjectionRule* pCurrentRule = &m_TopRule;
for(int i = 0; i < aFields.Size(); i++)
{
SWQLQualifiedNameField* pField = (SWQLQualifiedNameField*)aFields[i];
if(!wcscmp(pField->m_pName, L"*"))
{
pCurrentRule->m_eType = CProjectionRule::e_TakeAll;
return;
}
if(i == 0 && wszPrefix && !wbem_wcsicmp(pField->m_pName, wszPrefix) && aFields.Size() ==1)
{
// Skip this part because it is nothing more that a class name
// in a single-class query
// ===========================================================
continue;
}
// Look this column up in the rule
// ===============================
CProjectionRule* pNewRule = pCurrentRule->Find(pField->m_pName);
if(pNewRule == NULL)
{
pNewRule = new CProjectionRule(pField->m_pName);
if (pNewRule)
{
pNewRule->m_eType = CProjectionRule::e_TakePart;
if (pCurrentRule->m_apPropRules.Add(pNewRule) < 0)
{
delete pNewRule;
pNewRule = NULL;
}
}
}
pCurrentRule = pNewRule; // possible assign to NULL
}
// Mark this rule as take-all
// ==========================
if (pCurrentRule)
pCurrentRule->m_eType = CProjectionRule::e_TakeAll;
}
//***************************************************************************
//
//***************************************************************************
CComplexProjectionSink::~CComplexProjectionSink()
{
}
//***************************************************************************
//
//***************************************************************************
STDMETHODIMP CComplexProjectionSink::Indicate(long lObjectCount,
IWbemClassObject** pObjArray)
{
HRESULT hres;
IWbemClassObject** apProjected = new IWbemClassObject*[lObjectCount];
if (NULL == apProjected)
return WBEM_E_OUT_OF_MEMORY;
int i;
for(i = 0; i < lObjectCount; i++)
{
hres = Project(pObjArray[i], &m_TopRule, apProjected + i);
if(FAILED(hres))
{
ERRORTRACE((LOG_WBEMCORE, "Unable to perform a projection of an "
"object returned by a complex query: 0x%X\n", hres));
pObjArray[i]->Clone(apProjected + i);
}
}
hres = CForwardingSink::Indicate(lObjectCount, apProjected);
for(i = 0; i < lObjectCount; i++)
apProjected[i]->Release();
delete [] apProjected;
return hres;
}
//***************************************************************************
//
//***************************************************************************
HRESULT CComplexProjectionSink::Project(IWbemClassObject* pObj,
CProjectionRule* pRule,
IWbemClassObject** ppProj)
{
// Make a copy
// ===========
pObj->Clone(ppProj);
CWbemInstance* pProj = (CWbemInstance*)*ppProj;
// Take care of the case where the object being returned is a product of a join, but is of single
// class. Ex; Select Site.sitenmame from Site, NotUsed.
// This a a problem since we would normally expect a generic object as a result of a join and instead
// get one of the objects that make up the join.
CVar v;
pProj->GetClassName(&v);
bool Override = !wbem_wcsicmp(m_FirstTable, v.GetLPWSTR());
if(Override && pRule->GetNumElements() == 1)
{
CProjectionRule* pNewRule = NULL;
if(m_FirstTableAlias.Length())
pNewRule = pRule->Find(m_FirstTableAlias);
else
pNewRule = pRule->Find(m_FirstTable);
if(pNewRule)
pRule = pNewRule;
}
// If take all, just return
// ========================
if(pRule->m_eType == CProjectionRule::e_TakeAll)
return WBEM_S_NO_ERROR;
// Go through all its properties
// =============================
for(int i = 0; i < pProj->GetNumProperties(); i++)
{
CVar vName;
pProj->GetPropName(i, &vName);
// Search for this name
// ====================
CProjectionRule* pPropRule = pRule->Find(vName.GetLPWSTR());
if(pPropRule == NULL)
{
// Remove the property
// ===================
pProj->DeleteProperty(i);
i--;
}
else if(pPropRule->m_eType == CProjectionRule::e_TakePart)
{
// Apply the same procedure
// ========================
CVar vValue;
pProj->GetProperty(vName.GetLPWSTR(), &vValue);
if(vValue.GetType() == VT_EMBEDDED_OBJECT)
{
// Project it
// ==========
IWbemClassObject* pEmb =
(IWbemClassObject*)vValue.GetEmbeddedObject();
IWbemClassObject* pEmbProj;
HRESULT hres = Project(pEmb, pPropRule, &pEmbProj);
pEmb->Release();
// Store it back
// =============
if(SUCCEEDED(hres))
{
vValue.Empty();
vValue.SetEmbeddedObject(pEmbProj);
pProj->SetPropValue(vName.GetLPWSTR(), &vValue, 0);
pEmbProj->Release();
}
}
}
}
pProj->CompactClass();
return WBEM_S_NO_ERROR;
}
//***************************************************************************
//
// CQueryEngine::ExecQuery
//
// Primary entry point for execution of all queries supported by
// the query engine.
//
//***************************************************************************
// ok
HRESULT CQueryEngine::ExecQuery(
IN CWbemNamespace *pNs,
IN LPWSTR pszQueryFormat,
IN LPWSTR pszQuery,
IN LONG lFlags,
IN IWbemContext* pContext,
IN CBasicObjectSink* pSink
)
{
try
{
COperationError OpInfo(pSink, L"ExecQuery", pszQuery);
if (!OpInfo.IsOk()) return pSink->Return(WBEM_E_OUT_OF_MEMORY);
if (ConfigMgr::ShutdownInProgress()) return OpInfo.ErrorOccurred(WBEM_E_SHUTTING_DOWN);
// Check query language
if(wbem_wcsicmp(pszQueryFormat, L"WQL") != 0) return OpInfo.ErrorOccurred(WBEM_E_INVALID_QUERY_TYPE);
while (*pszQuery && isspace((WCHAR)*pszQuery)) pszQuery++;
if (0 == pszQuery[0]) return OpInfo.ErrorOccurred(WBEM_E_INVALID_QUERY);
// If a prototype query is requested, get the synthesized
// class definition.
if (lFlags & WBEM_FLAG_PROTOTYPE)
{
return ExecPrototypeQuery(pNs,pszQuery,pContext,pSink);
}
WCHAR * pEndToken = pszQuery;
while (*pEndToken && !isspace((WCHAR)*pEndToken)) pEndToken++;
size_t SizeToken = (ULONG_PTR)pEndToken - (ULONG_PTR)pszQuery;
SizeToken /= sizeof(WCHAR);
// Get the first token of the query to see if it is SQL1 or TEMPQL
BOOL bSelect = FALSE;
BOOL bDelete = FALSE;
if (6 == SizeToken)
{
bSelect = (0 == wbem_wcsnicmp(pszQuery, L"select",6));
bDelete = (0 == wbem_wcsnicmp(pszQuery, L"delete",6));
}
CBasicObjectSink *pNewSink = OpInfo.GetSink();
CLocaleMergingSink *pLocaleSink = NULL;
if (lFlags & WBEM_FLAG_USE_AMENDED_QUALIFIERS)
{
pLocaleSink = new CLocaleMergingSink(pNewSink, pNs->GetLocale(), pNs->GetName());
if ( NULL == pLocaleSink ) return OpInfo.ErrorOccurred(WBEM_E_OUT_OF_MEMORY);
pLocaleSink->AddRef();
pNewSink = pLocaleSink;
}
CReleaseMe rmLocale(pLocaleSink);
CFinalizingSink* pFinalSink = new CFinalizingSink(pNs, pNewSink);
if ( NULL == pFinalSink ) return OpInfo.ErrorOccurred(WBEM_E_OUT_OF_MEMORY);
pFinalSink->AddRef();
CReleaseMe rmFinal(pFinalSink);
if (bSelect)
{
ExecQlQuery(pNs, pszQuery, lFlags, pContext, pFinalSink);
}
else if (bDelete)
{
ExecRepositoryQuery(pNs, pszQuery, lFlags, pContext, pFinalSink);
}
else // ASSOCIATORS OF or REFERENCES OF query
{
CAssocQuery *pAssocQuery = CAssocQuery::CreateInst();
if (NULL == pAssocQuery)
{
return pFinalSink->Return(WBEM_E_OUT_OF_MEMORY);
}
CReleaseMe rmAssocQ(pAssocQuery);
// Execute the query and see what happens.
// The object AddRefs and Releases itself as required.
// We only need to do a Release right after Execute.
// =====================================
pAssocQuery->Execute(pNs, pszQuery, pContext, pFinalSink);
}
}
catch (CX_Exception &)
{
pSink->SetStatus(0, WBEM_E_OUT_OF_MEMORY, 0, 0);
return WBEM_E_OUT_OF_MEMORY;
}
return WBEM_S_NO_ERROR;
}
//***************************************************************************
//
//***************************************************************************
HRESULT CQueryEngine::ExecComplexQuery(
IN CWbemNamespace *pNs,
IN LPWSTR pszQuery,
IN LONG lFlags,
IN IWbemContext* pContext,
IN CBasicObjectSink* pSink
)
{
// Try to parse it
// ===============
CTextLexSource src(pszQuery);
CWQLScanner Parser(&src);
int nRes = Parser.Parse();
if(nRes != CWQLScanner::SUCCESS)
{
return WBEM_E_INVALID_QUERY;
}
// Successfully parsed. Go to the list of tables involved
// ======================================================
CWStringArray awsTables;
Parser.GetReferencedTables(awsTables);
// Go through them and check their providers
// =========================================
WString wsProvider;
for(int i = 0; i < awsTables.Size(); i++)
{
// Get the class
IWbemClassObject* pObj = NULL;
HRESULT hres = pNs->Exec_GetObjectByPath(awsTables[i], lFlags, pContext,&pObj, NULL);
if(FAILED(hres))
{
if(hres == WBEM_E_NOT_FOUND)
{
if(!wbem_wcsicmp(awsTables[i], L"meta_class"))
hres = WBEM_E_INVALID_QUERY;
else
hres = WBEM_E_INVALID_CLASS;
}
return hres;
}
CReleaseMe rmObj(pObj);
// Check the qualifier
// ===================
CWbemClass* pClass = (CWbemClass*)pObj;
CVar vProvider;
hres = pClass->GetQualifier(L"provider", &vProvider);
if(FAILED(hres) ||
vProvider.GetType() != VT_BSTR ||
vProvider.GetLPWSTR() == 0 ||
wcslen(vProvider.GetLPWSTR()) == 0)
{
// no provider --- can't execute
return WBEM_E_INVALID_QUERY;
}
if(i == 0)
{
wsProvider = vProvider.GetLPWSTR(); // throw
}
else
{
if(!wsProvider.EqualNoCase(vProvider.GetLPWSTR()))
{
// mismatched providers!
return WBEM_E_INVALID_QUERY;
}
}
}
CComplexProjectionSink* pProjSink = new CComplexProjectionSink(pSink, &Parser);
if (NULL == pProjSink) return WBEM_E_OUT_OF_MEMORY;
pProjSink->AddRef();
CReleaseMe rm1(pProjSink);
// All the classes have the same provider: wsProvider
// ==================================================
return pNs->DynAux_ExecQueryExtendedAsync(wsProvider,pszQuery,L"WQL" ,lFlags,pContext,pProjSink);
}
//
//
// CQueryEngine::ExecQlQuery
//
// This function MUST call SetStatus if there is an error of it's own
//
///////////////////////////////////////////////////////////////////////////////
HRESULT CQueryEngine::ExecQlQuery(
IN CWbemNamespace *pNs,
IN LPWSTR pszQuery,
IN LONG lFlags,
IN IWbemContext* pContext,
IN CBasicObjectSink* pSink
)
{
HRESULT hRes;
BOOL bDirectQuery = FALSE;
BOOL bShallow = (lFlags & WBEM_FLAG_SHALLOW);
// First, try to push it off to providers,
// that can handle the query it its entirety.
// =================================
if(!bShallow)
{
hRes = ExecComplexQuery(pNs, pszQuery, lFlags, pContext, pSink); // throws
if(SUCCEEDED(hRes))
return hRes;
}
// Parse the query.
// ================
CTextLexSource src(pszQuery);
QL1_Parser parser(&src);
QL_LEVEL_1_RPN_EXPRESSION *pExp = 0;
int nRes = parser.Parse(&pExp);
if (CAbstractQl1Parser::SUCCESS != nRes) return pSink->Return(WBEM_E_INVALID_QUERY);
_DBG_ASSERT(pExp);
pExp->AddRef();
CTemplateReleaseMe<QL_LEVEL_1_RPN_EXPRESSION> trm99(pExp);
// Check if the repository for this namespace
// supports queries. If so, we can pawn off
// the entire query on it (with the exception
// of provider-backed subclasses)
// ===========================================
bDirectQuery = pNs->GetNsSession()->SupportsQueries(NULL) == WBEM_S_NO_ERROR ? TRUE : FALSE;
if (!bDirectQuery)
{
// Check for failure, or that pExp->bAggregated is TRUE, in which
// case we got a "GROUP BY" query which we do not support
if ( nRes || pExp->bAggregated || !pExp->bsClassName )
{
return pSink->Return(WBEM_E_INVALID_QUERY);
}
}
else
{
// This is strictly to allow order by clauses to squeak through,
// until we replace this parser with IWbemQuery.
if (!pExp || !pExp->bsClassName || pExp->bAggregated)
{
return pSink->Return(WBEM_E_INVALID_QUERY);
}
}
// We should make a check to see if we are doing a schema search. This is
// the case if we are doing a select against the "meta_class" class.
// =======================================================================
if (wbem_wcsicmp(pExp->bsClassName, L"meta_class") == 0)
{
if(pExp->nNumberOfProperties > 0 || !pExp->bStar)
{
return pSink->Return(WBEM_E_INVALID_QUERY);
}
return ExecSchemaQuery(pNs, pszQuery, pExp, pContext, pSink);
}
// Build the dynasty
// =================
wmilib::auto_ptr<CDynasty> pDynasty;
IWbemClassObject* pErrorObj = NULL;
HRESULT hres = pNs->DynAux_BuildClassHierarchy(pExp->bsClassName,
0, // removed the flags
pContext,
pDynasty,
&pErrorObj);
if (FAILED(hres))
{
CReleaseMe rm1(pErrorObj);
if(hres == WBEM_E_NOT_FOUND || hres == WBEM_E_INVALID_CLASS)
return pSink->Return(WBEM_E_INVALID_CLASS, pErrorObj);
else
return pSink->Return(WBEM_E_INVALID_QUERY, pErrorObj);
}
// Construct a post-filtering (if needed) and projecting sink
// ==========================================================
IWbemClassObject* pClass = NULL;
CReleaseMeRef<IWbemClassObject*> rm1(pClass);
if (!pExp->bCount)
{
hres = pNs->Exec_GetObjectByPath(pExp->bsClassName, lFlags, pContext, &pClass, NULL);
if(FAILED(hres))
{
return pSink->Return(WBEM_E_INVALID_CLASS);
}
}
else
{
if (!bDirectQuery) return pSink->Return(WBEM_E_NOT_SUPPORTED);
// here it's a Direct Query
hres = CoCreateInstance(CLSID_WbemClassObject,
NULL, CLSCTX_INPROC_SERVER,
IID_IWbemClassObject,
(void **)&pClass);
if (FAILED(hres)) return pSink->Return(WBEM_E_OUT_OF_MEMORY);
BSTR bstrName = SysAllocString(L"__Generic");
if (NULL == bstrName) return pSink->Return(WBEM_E_OUT_OF_MEMORY);
VARIANT vTemp;
V_VT(&vTemp) = VT_BSTR;
V_BSTR(&vTemp) = bstrName;
_variant_t Var;
Var.Attach(vTemp);
hres = pClass->Put(L"__Class", 0, &vTemp, CIM_STRING);
if (FAILED(hres)) return pSink->Return(hRes);
hres = pClass->Put(L"Count", 0, NULL, CIM_UINT32);
if (FAILED(hres)) return pSink->Return(hRes);
}
CBasicObjectSink* pPreFilterSink = NULL;
if(lFlags & WBEM_FLAG_KEEP_SHAPE)
{
//
// We must not project the results, otherwise we will destroy the shape
// of the instance. Remove the flag, though, lest we confuse providers
//
lFlags &= ~WBEM_FLAG_KEEP_SHAPE;
pPreFilterSink = pSink;
}
else
{
// this guy throws
CProjectingSink* pProjectingSink = new CProjectingSink(pSink, (CWbemClass*)pClass, pExp, lFlags);
if (NULL == pProjectingSink)
return pSink->Return(WBEM_E_OUT_OF_MEMORY);
if(!pProjectingSink->IsValid())
{
delete pProjectingSink;
return pSink->Return(WBEM_E_INVALID_QUERY);
}
pPreFilterSink = pProjectingSink;
}
CQlFilteringSink* pFilteringSink = new CQlFilteringSink(pPreFilterSink, pExp, pNs, TRUE);
if (NULL == pFilteringSink) return pSink->Return(WBEM_E_OUT_OF_MEMORY);
// If shallow, force post-filtering
// ================================
if(bShallow) pFilteringSink->SetStatus(WBEM_STATUS_REQUIREMENTS, S_OK, NULL, NULL);
CCombiningSink* pCombiningSink = new CCombiningSink(pFilteringSink, WBEM_E_NOT_FOUND);
if (NULL == pCombiningSink) return pSink->Return(WBEM_E_OUT_OF_MEMORY);
CDynPropsSink * pDynSink = new CDynPropsSink(pCombiningSink, pNs);
if (NULL == pDynSink) return pSink->Return(WBEM_E_OUT_OF_MEMORY);
pDynSink->AddRef();
CReleaseMe rm99(pDynSink);
// We simplify the query if our repository ain't too bright.
// Otherwise, it will reject count queries.
// Again, temporary until we get IWbemQuery plugged in.
if (!bDirectQuery)
{
// "Simplify" the query (TBD: think about doing it at each level)
// ==============================================================
QL_LEVEL_1_RPN_EXPRESSION* pSimpleExp = NULL;
CStandardMetaData* pRawMeta = new CStandardMetaData(pNs);
if (NULL == pRawMeta)
{
pDynSink->Return(WBEM_E_OUT_OF_MEMORY);
return WBEM_S_NO_ERROR;
}
CContextMetaData* pMeta = new CContextMetaData(pRawMeta, pContext);
if (NULL == pMeta)
{
delete pRawMeta;
pDynSink->Return(WBEM_E_OUT_OF_MEMORY);
return WBEM_S_NO_ERROR;
}
hres = CQueryAnalyser::SimplifyQueryForChild(pExp,
pExp->bsClassName, (CWbemClass*)pClass, pMeta, pSimpleExp);
delete pMeta;
if(FAILED(hres))
{
pDynSink->Return(WBEM_E_INVALID_QUERY);
return WBEM_S_NO_ERROR;
}
if(pSimpleExp == NULL)
{
// Query violated intergrity constraint
// ====================================
pDynSink->Return(WBEM_S_NO_ERROR); // ?? WBEM_S_IMPOSSIBLE
return WBEM_S_NO_ERROR;
}
// Substitute the simplified where clause into the query
// =====================================================
delete [] pExp->pArrayOfTokens;
pExp->pArrayOfTokens = pSimpleExp->pArrayOfTokens;
pExp->nNumTokens = pSimpleExp->nNumTokens;
pSimpleExp->pArrayOfTokens = NULL;
delete pSimpleExp;
}
// Now make a final pass to make sure this query is valid
// ======================================================
hres = ValidateQuery(pExp, (CWbemClass *)pClass);
if (FAILED(hres))
{
pDynSink->Return(hres);
return WBEM_S_NO_ERROR;
}
LPWSTR pszNewQuery = NULL;
// Preserve the original query if
// it contains count, order by
// (or other unparsable stuff)
if (pExp->bCount || nRes)
pszNewQuery = Macro_CloneLPWSTR(pszQuery);
else
pszNewQuery = pExp->GetText();
if (NULL == pszNewQuery)
{
pDynSink->Return(WBEM_E_OUT_OF_MEMORY);
return WBEM_S_NO_ERROR;
}
CVectorDeleteMe<wchar_t> cdm98(pszNewQuery);
// If direct access was requested, then don't walk
// the dynasty. Go right to the repository or the provider.
// ========================================================
if (lFlags & WBEM_FLAG_DIRECT_READ)
{
DirectRead(pNs, pDynasty.get(), pszNewQuery, pExp, pContext,
pDynSink, lFlags & ~WBEM_FLAG_ENSURE_LOCATABLE
);
}
else // Recursively execute for all classes in the dynasty
{
BOOL fUseOld = !ConfigMgr::GetMergerThrottlingEnabled();
// Check the Registry
if ( fUseOld )
{
EvaluateSubQuery_old(
pNs, pDynasty.get(), pszNewQuery, pExp, pContext, FALSE,
pDynSink, lFlags & ~WBEM_FLAG_ENSURE_LOCATABLE
);
}
else
{
// Allocate a new merger and pass it down the line
CWmiMerger* pMerger = new CWmiMerger( pNs );
if ( NULL == pMerger ) return pDynSink->Return( WBEM_E_OUT_OF_MEMORY );
pMerger->AddRef();
CReleaseMe rmMerger( (_IWmiArbitratee*) pMerger );
// Task handle will be available if we have an executing request,
// if not, don't worry about it right now. Nobody's really able
// to give a straight answer on this, so we will simply add an assert
// if a merger is created and no task handle is associated with the
// main merger.
_IWmiArbitrator* pArbitrator = CWmiArbitrator::GetRefedArbitrator();
if (NULL == pArbitrator) return pDynSink->Return(WBEM_E_CRITICAL_ERROR);
CReleaseMe rmArb( pArbitrator );
_IWmiCoreHandle* pTask = NULL;
CWbemRequest* pReq = CWbemQueue::GetCurrentRequest();
if ( pReq )
{
pTask = pReq->m_phTask;
}
//
// creates the MergerSink as the DestinationSink
//
CMergerSink* pDestSink = NULL;
HRESULT hr = pMerger->Initialize( pArbitrator, pTask, pExp->bsClassName, pDynSink, &pDestSink );
CReleaseMe rm( pDestSink );
if (FAILED(hr)) return pDynSink->Return( hr );
// If something goes wrong in this function, it will set the
// error in the sink
hr = EvaluateSubQuery(pNs, pDynasty.get(),
pszNewQuery,
pExp, pContext, FALSE,
pMerger, pDestSink, lFlags & ~WBEM_FLAG_ENSURE_LOCATABLE);
if ( SUCCEEDED( hr ) )
{
//
// Schedule a parent request if appropriate
// The Merger Request Manager knonws if it has one or more requests
// one request is handled here synchronously
// more request are kicked off by the MergerParent Request
// in a different thread of the queue
//
hr = pMerger->ScheduleMergerParentRequest( pContext );
if (FAILED(hr)) return pDestSink->Return( hr );
}
}
}
return hres;
}
//***************************************************************************
//
// CQueryEngine::DirectRead
//
// Called to directly read the instances of a class, whether
// from the repository or a provider.
//
//***************************************************************************
HRESULT CQueryEngine::DirectRead(
IN CWbemNamespace *pNs,
IN CDynasty *pCurrentDyn,
IN LPWSTR wszTextQuery,
IN QL_LEVEL_1_RPN_EXPRESSION *pParsedQuery,
IN IWbemContext* pContext,
IN CBasicObjectSink* pSink,
IN long lFlags
)
{
// SJS - Amendment is the same as Abstract
if( ( pCurrentDyn->IsAbstract() || pCurrentDyn->IsAmendment() ) && (lFlags & WBEM_FLAG_SHALLOW))
{
// No instances
// ============
return pSink->Return(WBEM_S_NO_ERROR);
}
// The class has its own instances if it has a key and is either dynamic
// or the first static class in the inheritance chain (otherwise these
// instances have been handled in the parent)
// =====================================================================
BOOL bHasOwnInstances = pCurrentDyn->IsKeyed() && !pCurrentDyn->IsAbstract()
&& !pCurrentDyn->IsAmendment();
// The class has children that we need to look at if it has children.
// ==================================================================
BOOL bHasChildren = (pCurrentDyn->m_Children.Size() > 0);
// Determine if the current query actually asks for instances of the
// current CDynasty class. This is used for WBEM_FLAG_DIRECT_READ type
// access.
// =======================================================================
BOOL bQueryMatchesCurrentNode = FALSE;
if (wbem_wcsicmp(pParsedQuery->bsClassName, pCurrentDyn->m_wszClassName) == 0)
bQueryMatchesCurrentNode = TRUE;
// If we are at the node we need, we can stop.
// ===========================================
if (bHasOwnInstances && bQueryMatchesCurrentNode)
{
// If a provider backs this class, then call it.
// ==============================================
if (pCurrentDyn->IsDynamic())
{
ExecAtomicDynQlQuery(
pNs,
pCurrentDyn,
L"WQL",
wszTextQuery,
pParsedQuery,
lFlags, // Flags
pContext,
pSink,
FALSE
);
}
// Try the repository.
// ===================
else
{
int nRes = ExecAtomicDbQuery(pNs->GetNsSession(), pNs->GetNsHandle(), pNs->GetScope(), pCurrentDyn->m_wszClassName,
pParsedQuery, pSink, pNs
);
if (nRes == invalid_query)
pSink->Return(WBEM_E_INVALID_QUERY);
else if(nRes != 0)
pSink->Return(WBEM_E_FAILED);
else
pSink->Return(WBEM_S_NO_ERROR);
}
}
// If here, we must keep looking for the target in the child classes.
// ==================================================================
else if (bHasChildren)
{
for (int i = 0; i < pCurrentDyn->m_Children.Size(); i++)
{
CDynasty *pSubDyn =
(CDynasty *) pCurrentDyn->m_Children.GetAt(i);
DirectRead(
pNs,
pSubDyn,
wszTextQuery,
pParsedQuery,
pContext,
pSink,
lFlags
);
}
}
return WBEM_S_NO_ERROR;
}
// New implementation
//***************************************************************************
//
// CQueryEngine::EvaluateSubQuery
//
// Walks through a class hierarchy and executes smaller queries against
// the individual classes in the dynasty.
//
// Note that in a class hierarchy A,B:A,C:B, an enumeration/query is
// performed only against the classes in the CDynasty referenced in
// the query. For example, if "select * from B" is the query, only queries
// for B and C are performed. The CMerger logic will do individual
// 'get object' calls for any instances needed in A to complete
// the merged B/C instances while merging is taking place.
//
// Return values:
// WBEM_NO_ERROR
// WBEM_E_FAILED
//
//***************************************************************************
// error objects dealt with
HRESULT CQueryEngine::EvaluateSubQuery(
IN CWbemNamespace *pNs,
IN CDynasty *pCurrentDyn,
IN LPWSTR wszTextQuery,
IN QL_LEVEL_1_RPN_EXPRESSION *pParsedQuery,
IN IWbemContext* pContext,
IN BOOL bSuppressStaticChild,
IN CWmiMerger* pMerger, // must have combining semantics
IN CMergerSink* pSink,
IN long lFlags,
IN bool bHasRightSibling
)
{
// SJS - Amendment is the same as Abstract
if( ( pCurrentDyn->IsAbstract() || pCurrentDyn->IsAmendment() ) && (lFlags & WBEM_FLAG_SHALLOW))
{
// No instances
// ============
pSink->SetStatus( 0L, WBEM_S_NO_ERROR, 0L, NULL);
return WBEM_S_NO_ERROR;
}
// The class has its own instances if it has a key and is either dynamic
// or the first static class in the inheritance chain (otherwise these
// instances have been handled in the parent)
// =====================================================================
BOOL bHasOwnInstances = pCurrentDyn->IsKeyed() && !pCurrentDyn->IsAbstract()
&& !pCurrentDyn->IsAmendment() && (pCurrentDyn->IsDynamic() || !bSuppressStaticChild);
// The class has children that we need to look at if it has children.
// ==================================================================
BOOL bHasChildren = (pCurrentDyn->m_Children.Size() > 0);
// The class hierarchy was built down from the class of the query, as
// well as up the inheritance chain, since parents may need to be used to
// build complete instances. However, parents are treated very different
// then classes derived from the class of the query (see below)
// ======================================================================
BOOL bDerivedFromTarget = (pCurrentDyn->m_pClassObj->InheritsFrom(pParsedQuery->bsClassName) == S_OK);
// Next, see if the query is executing out of a scope or the primary
// namespace. We exclude providers if the query is executing from
// a scope.
// ==================================================================
BOOL bInScope = pNs->IsSubscope();
// Now we have enough info to start getting the instances.
// =======================================================
CMergerSink* pOwnSink = NULL;
CMergerSink* pChildSink = NULL;
//
// Creates a CMergerRecord if there is not already one for the class
// Creates an InternalMerger if there are Own instances and Child classes
// returns Own and Child Sink from the MergerRecord
//
HRESULT hr = pMerger->RegisterSinkForClass( pCurrentDyn->m_wszClassName,
(_IWmiObject*) pCurrentDyn->m_pClassObj,
pContext,
bHasChildren, bHasOwnInstances,
bDerivedFromTarget,!pCurrentDyn->IsDynamic(),
pSink, &pOwnSink, &pChildSink );
CReleaseMe rm1( pOwnSink );
CReleaseMe rm2( pChildSink );
if ( FAILED(hr) )
{
pSink->SetStatus( 0L, hr, 0L, NULL );
return hr;
}
if(bHasOwnInstances)
{
if(bHasChildren)
{
// In order for the merge to succeed, we need to make sure that all
// keys are provided, whether or not we are asked for them
// ================================================================
if(!pParsedQuery->bStar)
{
CPropertyName Name;
Name.AddElement(L"__RELPATH"); // throws
pParsedQuery->AddProperty(Name);
}
// We need to figure out what to ask of the provider. If the
// provider is "downstream" from the original query, i.e. the query
// was asked against a class that is an ancestor of this one or is
// this one, we are fine --- this provider must understand the
// query. If not, we don't ask any query, just wait and then call
// GetObjectByPath.
// ================================================================
//pMerger->SetIsDerivedFromTarget(bDerivedFromTarget);
}
}
else if(!bHasChildren)
{
// No instances and no children
pSink->SetStatus( 0L, WBEM_S_NO_ERROR, 0L, NULL );
return WBEM_S_NO_ERROR;
}
// If this is an old security class, use the internal provider.
// ====================================================================
if((wbem_wcsicmp(pCurrentDyn->m_wszClassName, L"__ntlmgroup") == 0 ||
wbem_wcsicmp(pCurrentDyn->m_wszClassName, L"__ntlmuser") == 0) &&
(lFlags & WBEM_FLAG_ONLY_STATIC) == 0)
{
HRESULT hres = pNs->EnumerateSecurityClassInstances(pCurrentDyn->m_wszClassName,
pOwnSink, pContext, lFlags);
pOwnSink->SetStatus( 0L, hres, 0L, NULL );
}
// If the current subclass is the first keyed statically instanced subclass.
// =========================================================================
else if (bHasOwnInstances && !pCurrentDyn->IsDynamic())
{
// Execute the query against the static portion of the database.
// =============================================================
int nRes = 0;
if (pNs->GetNsSession()->SupportsQueries(NULL) == WBEM_S_NO_ERROR)
{
// The underlying repository automatically handles inheritance.
if (!bSuppressStaticChild)
nRes = ExecRepositoryQuery(pNs, wszTextQuery, lFlags, pContext, pSink);
if (nRes == invalid_query)
pOwnSink->SetStatus( 0L, WBEM_E_INVALID_QUERY, 0L, NULL );
else if(nRes != 0)
pOwnSink->SetStatus( 0L, WBEM_E_FAILED, 0L, NULL );
else
pOwnSink->SetStatus( 0L, WBEM_S_NO_ERROR, 0L, NULL );
}
else
{
hr = pNs->Static_QueryRepository( (CWbemObject *) pCurrentDyn->m_pClassObj,0L,
pContext, pOwnSink, pParsedQuery,
pCurrentDyn->m_wszClassName,pMerger );
}
}
else if (bHasOwnInstances && pCurrentDyn->IsDynamic() && !bInScope)
{
if (bDerivedFromTarget)
{
// Ask the provider.
// =================
ExecAtomicDynQlQuery(
pNs,
pCurrentDyn,
L"WQL",
wszTextQuery,
pParsedQuery,
lFlags, // Flags
pContext,
pOwnSink,
bHasChildren || bHasRightSibling
);
}
else
{
pOwnSink->SetStatus( 0L, WBEM_S_NO_ERROR, 0L, NULL );
}
}
// Manually release pOwnSink if appropriate - use the method on CReleaseMe() so
// as not to interfere with the auto-release functionality. We should do
// this here so as to relinquish any unnecessary locks we may be holding on data
// and/or results before we start spinning off child requests - it's all about
// throughput boyo!
if(pOwnSink)
rm1.release();
// If the current subclass is the first keyed statically instanced subclass.
// =========================================================================
if (bHasOwnInstances && !pCurrentDyn->IsDynamic())
{
bSuppressStaticChild = TRUE;
}
// Evaluate child classes.
// =======================
if (bHasChildren)
{
for (int i = 0; i < pCurrentDyn->m_Children.Size(); i++)
{
CDynasty *pSubDyn = (CDynasty *) pCurrentDyn->m_Children.GetAt(i);
EvaluateSubQuery (
pNs,
pSubDyn,
wszTextQuery,
pParsedQuery,
pContext,
bSuppressStaticChild,
pMerger,
pChildSink,
lFlags,
bHasRightSibling || ( ( i != ( pCurrentDyn->m_Children.Size () - 1 )) ? true : false )
) ;
}
}
return WBEM_S_NO_ERROR;
}
// Old implementation
//***************************************************************************
//
// CQueryEngine::EvaluateSubQuery
//
// Walks through a class hierarchy and executes smaller queries against
// the individual classes in the dynasty.
//
// Note that in a class hierarchy A,B:A,C:B, an enumeration/query is
// performed only against the classes in the CDynasty referenced in
// the query. For example, if "select * from B" is the query, only queries
// for B and C are performed. The CMerger logic will do individual
// 'get object' calls for any instances needed in A to complete
// the merged B/C instances while merging is taking place.
//
// Return values:
// WBEM_NO_ERROR
// WBEM_E_FAILED
//
//***************************************************************************
// error objects dealt with
HRESULT CQueryEngine::EvaluateSubQuery_old(
IN CWbemNamespace *pNs,
IN CDynasty *pCurrentDyn,
IN LPWSTR wszTextQuery,
IN QL_LEVEL_1_RPN_EXPRESSION *pParsedQuery,
IN IWbemContext* pContext,
IN BOOL bSuppressStaticChild,
IN CBasicObjectSink* pSink, // must have combining semantics
IN long lFlags,
IN bool bHasRightSibling
)
{
// SJS - Amendment is the same as Abstract
if( ( pCurrentDyn->IsAbstract() || pCurrentDyn->IsAmendment() ) && (lFlags & WBEM_FLAG_SHALLOW))
{
// No instances
// ============
return pSink->Return(WBEM_S_NO_ERROR);
}
// The class has its own instances if it has a key and is either dynamic
// or the first static class in the inheritance chain (otherwise these
// instances have been handled in the parent)
// =====================================================================
BOOL bHasOwnInstances = pCurrentDyn->IsKeyed() && !pCurrentDyn->IsAbstract()
&& !pCurrentDyn->IsAmendment() && (pCurrentDyn->IsDynamic() || !bSuppressStaticChild);
// The class has children that we need to look at if it has children.
// ==================================================================
BOOL bHasChildren = (pCurrentDyn->m_Children.Size() > 0);
// The class hierarchy was built down from the class of the query, as
// well as up the inheritance chain, since parents may need to be used to
// build complete instances. However, parents are treated very different
// then classes derived from the class of the query (see below)
// ======================================================================
BOOL bDerivedFromTarget = (pCurrentDyn->m_pClassObj->InheritsFrom(
pParsedQuery->bsClassName) == S_OK);
// Next, see if the query is executing out of a scope or the primary
// namespace. We exclude providers if the query is executing from
// a scope.
// ==================================================================
BOOL bInScope = pNs->IsSubscope();
// Now we have enough info to start getting the instances.
// =======================================================
CBasicObjectSink* pChildSink = NULL;
CBasicObjectSink* pOwnSink = NULL;
if(bHasOwnInstances)
{
if(bHasChildren)
{
// Has instances and children have instances
// =========================================
CMerger* pMerger = new CMerger(pSink,
(CWbemClass*)pCurrentDyn->m_pClassObj, pNs, pContext);
if (pMerger && pMerger->IsValid())
{
pOwnSink = pMerger->GetOwnSink();
pOwnSink->AddRef();
pChildSink = pMerger->GetChildSink();
pChildSink->AddRef();
// In order for the merge to succeed, we need to make sure that all
// keys are provided, whether or not we are asked for them
// ================================================================
if(!pParsedQuery->bStar)
{
CPropertyName Name;
Name.AddElement(L"__RELPATH");
pParsedQuery->AddProperty(Name);
}
// We need to figure out what to ask of the provider. If the
// provider is "downstream" from the original query, i.e. the query
// was asked against a class that is an ancestor of this one or is
// this one, we are fine --- this provider must understand the
// query. If not, we don't ask any query, just wait and then call
// GetObjectByPath.
// ================================================================
pMerger->SetIsDerivedFromTarget(bDerivedFromTarget);
}
else
{
return pSink->Return(WBEM_E_OUT_OF_MEMORY);
}
}
else
{
// No children --- own instances are it
// ====================================
pOwnSink = pSink;
pSink->AddRef();
}
}
else if(bHasChildren)
{
// Our children are it
// ===================
pChildSink = pSink;
pSink->AddRef();
}
else
{
// No instances
// ============
return pSink->Return(WBEM_S_NO_ERROR);
}
// If this is an old security class, use the internal provider.
// ====================================================================
if((wbem_wcsicmp(pCurrentDyn->m_wszClassName, L"__ntlmgroup") == 0 ||
wbem_wcsicmp(pCurrentDyn->m_wszClassName, L"__ntlmuser") == 0) &&
(lFlags & WBEM_FLAG_ONLY_STATIC) == 0)
{
HRESULT hres = pNs->EnumerateSecurityClassInstances(pCurrentDyn->m_wszClassName,
pOwnSink, pContext, lFlags);
pOwnSink->Return(hres);
}
// If the current subclass is the first keyed statically instanced subclass.
// =========================================================================
else if (bHasOwnInstances && !pCurrentDyn->IsDynamic())
{
// Execute the query against the static portion of the database.
// =============================================================
int nRes = 0;
if (pNs->GetNsSession()->SupportsQueries(NULL) == WBEM_S_NO_ERROR)
{
// The underlying repository automatically handles inheritance.
if (!bSuppressStaticChild)
nRes = ExecRepositoryQuery(pNs, wszTextQuery, lFlags, pContext, pSink);
}
else
{
nRes = ExecAtomicDbQuery(pNs->GetNsSession(), pNs->GetNsHandle(), pNs->GetScope(), pCurrentDyn->m_wszClassName,
pParsedQuery, pOwnSink, pNs);
}
if (nRes == invalid_query)
pOwnSink->Return(WBEM_E_INVALID_QUERY);
else if(nRes != 0)
pOwnSink->Return(WBEM_E_FAILED);
else
pOwnSink->Return(WBEM_S_NO_ERROR);
}
else if (bHasOwnInstances && pCurrentDyn->IsDynamic() && !bInScope)
{
if (bDerivedFromTarget)
{
// Ask the provider.
// =================
ExecAtomicDynQlQuery(
pNs,
pCurrentDyn,
L"WQL",
wszTextQuery,
pParsedQuery,
lFlags, // Flags
pContext,
pOwnSink,
bHasChildren || bHasRightSibling
);
}
else
{
pOwnSink->Return(WBEM_S_NO_ERROR);
}
}
if(pOwnSink)
pOwnSink->Release();
// If the current subclass is the first keyed statically instanced subclass.
// =========================================================================
if (bHasOwnInstances && !pCurrentDyn->IsDynamic())
{
bSuppressStaticChild = TRUE;
}
// Evaluate child classes.
// =======================
if (bHasChildren)
{
for (int i = 0; i < pCurrentDyn->m_Children.Size(); i++)
{
CDynasty *pSubDyn = (CDynasty *) pCurrentDyn->m_Children.GetAt(i);
EvaluateSubQuery_old(
pNs,
pSubDyn,
wszTextQuery,
pParsedQuery,
pContext,
bSuppressStaticChild,
pChildSink,
lFlags,
bHasRightSibling || ( ( i != ( pCurrentDyn->m_Children.Size () - 1 )) ? true : false )
) ;
}
}
if(pChildSink)
pChildSink->Release();
return WBEM_S_NO_ERROR;
}
//***************************************************************************
//
//***************************************************************************
HRESULT CQueryEngine::EliminateDerivedProperties(
IN QL_LEVEL_1_RPN_EXPRESSION* pOrigQuery,
IN CWbemClass* pClass,
IN BOOL bRelax,
OUT QL_LEVEL_1_RPN_EXPRESSION** ppNewQuery)
{
HRESULT hres = WBEM_S_NO_ERROR;
// Set up the new query to talk about this class
// =============================================
CVar vClassName;
hres = pClass->GetClassName(&vClassName);
if (FAILED(hres))
return hres;
(*ppNewQuery)->bsClassName = SysAllocString(vClassName.GetLPWSTR());
if (0==(*ppNewQuery)->bsClassName)
return WBEM_E_OUT_OF_MEMORY;
if(pOrigQuery->nNumTokens == 0)
{
*ppNewQuery = new QL_LEVEL_1_RPN_EXPRESSION;
if (*ppNewQuery)
return WBEM_S_NO_ERROR;
else
return WBEM_E_OUT_OF_MEMORY;
}
// Set up a stack of expressions
// =============================
std::stack<QL_LEVEL_1_RPN_EXPRESSION*, deque <QL_LEVEL_1_RPN_EXPRESSION*, wbem_allocator<QL_LEVEL_1_RPN_EXPRESSION*> > > ExprStack;
// Recursively "evaluate" the original query
// =========================================
for(int i = 0; i < pOrigQuery->nNumTokens; i++)
{
QL_LEVEL_1_TOKEN& Token = pOrigQuery->pArrayOfTokens[i];
QL_LEVEL_1_RPN_EXPRESSION* pNew = NULL;
QL_LEVEL_1_RPN_EXPRESSION* pFirst = NULL;
QL_LEVEL_1_RPN_EXPRESSION* pSecond = NULL;
switch(Token.nTokenType)
{
case QL1_OP_EXPRESSION:
if(IsTokenAboutClass(Token, pClass))
{
QL_LEVEL_1_RPN_EXPRESSION* pNew = new QL_LEVEL_1_RPN_EXPRESSION;
if (pNew)
pNew->AddToken(Token);
else
{
// force exit
i = pOrigQuery->nNumTokens;
}
}
else
{
if(bRelax)
{
QL_LEVEL_1_RPN_EXPRESSION* pNew =
new QL_LEVEL_1_RPN_EXPRESSION;
if (pNew)
ExprStack.push(pNew);
else
{
// force exit
i = pOrigQuery->nNumTokens;
}
}
else
{
ExprStack.push(NULL);
}
}
break;
case QL1_AND:
if(ExprStack.size() < 2)
{
hres = WBEM_E_CRITICAL_ERROR;
break;
}
pFirst = ExprStack.top(); ExprStack.pop();
pSecond = ExprStack.top(); ExprStack.pop();
hres = AndQueryExpressions(pFirst, pSecond, &pNew);
ExprStack.push(pNew);
delete pFirst;
delete pSecond;
break;
case QL1_OR:
if(ExprStack.size() < 2)
{
hres = WBEM_E_CRITICAL_ERROR;
break;
}
pFirst = ExprStack.top(); ExprStack.pop();
pSecond = ExprStack.top(); ExprStack.pop();
hres = OrQueryExpressions(pFirst, pSecond, &pNew);
ExprStack.push(pNew);
delete pFirst;
delete pSecond;
break;
case QL1_NOT:
if(ExprStack.size() < 1)
{
hres = WBEM_E_CRITICAL_ERROR;
break;
}
pFirst = ExprStack.top(); ExprStack.pop();
if(bRelax)
{
QL_LEVEL_1_RPN_EXPRESSION* pNew = new QL_LEVEL_1_RPN_EXPRESSION;
if (pNew)
ExprStack.push(pNew);
else
{
// force exit
i = pOrigQuery->nNumTokens;
}
}
else
{
ExprStack.push(NULL);
}
delete pFirst;
break;
default:
hres = WBEM_E_CRITICAL_ERROR;
delete pNew;
}
if(FAILED(hres))
{
// An error occurred, break out of the loop
// ========================================
break;
}
}
if(SUCCEEDED(hres) && ExprStack.size() != 1)
{
hres = WBEM_E_CRITICAL_ERROR;
}
if(FAILED(hres))
{
// An error occurred. Clear the stack
// ==================================
while(!ExprStack.empty())
{
delete ExprStack.top();
ExprStack.pop();
}
return hres;
}
// All is good
// ===========
*ppNewQuery = ExprStack.top();
return S_OK;
}
//***************************************************************************
//
//***************************************************************************
BOOL CQueryEngine::IsTokenAboutClass(IN QL_LEVEL_1_TOKEN& Token,
IN CWbemClass* pClass)
{
CPropertyName& TokenPropName = Token.PropertyName;
if(TokenPropName.GetNumElements() != 1)
return FALSE;
LPWSTR wszPropName = (LPWSTR)TokenPropName.GetStringAt(0);
return SUCCEEDED(pClass->GetPropertyType(wszPropName, NULL, NULL));
}
//***************************************************************************
//
//***************************************************************************
HRESULT CQueryEngine::AndQueryExpressions(
IN QL_LEVEL_1_RPN_EXPRESSION* pFirst,
IN QL_LEVEL_1_RPN_EXPRESSION* pSecond,
OUT QL_LEVEL_1_RPN_EXPRESSION** ppNew)
{
// If either one is false, the result is false
// ===========================================
if(pFirst == NULL || pSecond == NULL)
{
*ppNew = NULL;
return WBEM_S_NO_ERROR;
}
*ppNew = new QL_LEVEL_1_RPN_EXPRESSION;
if (NULL == *ppNew)
{
return WBEM_E_OUT_OF_MEMORY;
}
// If either one is empty, take the other
// ======================================
if(pFirst->nNumTokens == 0)
{
AppendQueryExpression(*ppNew, pSecond);
return WBEM_S_NO_ERROR;
}
if(pSecond->nNumTokens == 0)
{
AppendQueryExpression(*ppNew, pFirst);
return WBEM_S_NO_ERROR;
}
// Both are there --- and together
// ===============================
AppendQueryExpression(*ppNew, pFirst);
AppendQueryExpression(*ppNew, pSecond);
QL_LEVEL_1_TOKEN Token;
Token.nTokenType = QL1_AND;
(*ppNew)->AddToken(Token);
return WBEM_S_NO_ERROR;
}
//***************************************************************************
//
//***************************************************************************
HRESULT CQueryEngine::OrQueryExpressions(
IN QL_LEVEL_1_RPN_EXPRESSION* pFirst,
IN QL_LEVEL_1_RPN_EXPRESSION* pSecond,
OUT QL_LEVEL_1_RPN_EXPRESSION** ppNew)
{
// If both are false, so is the resulkt
// ====================================
if(pFirst == NULL && pSecond == NULL)
{
*ppNew = NULL;
return WBEM_S_NO_ERROR;
}
*ppNew = new QL_LEVEL_1_RPN_EXPRESSION;
if (NULL == *ppNew)
{
return WBEM_E_OUT_OF_MEMORY;
}
// If either one is empty, so is the result
// ========================================
if(pFirst->nNumTokens == 0 || pSecond->nNumTokens == 0)
{
return WBEM_S_NO_ERROR;
}
// If either one is false, return the other
// ========================================
if(pFirst == NULL)
{
AppendQueryExpression(*ppNew, pSecond);
return WBEM_S_NO_ERROR;
}
if(pSecond == NULL)
{
AppendQueryExpression(*ppNew, pFirst);
return WBEM_S_NO_ERROR;
}
// Both are there --- or together
// ==============================
AppendQueryExpression(*ppNew, pFirst);
AppendQueryExpression(*ppNew, pSecond);
QL_LEVEL_1_TOKEN Token;
Token.nTokenType = QL1_OR;
(*ppNew)->AddToken(Token);
return WBEM_S_NO_ERROR;
}
//***************************************************************************
//
//***************************************************************************
void CQueryEngine::AppendQueryExpression(
IN QL_LEVEL_1_RPN_EXPRESSION* pDest,
IN QL_LEVEL_1_RPN_EXPRESSION* pSource)
{
for(int i = 0; i < pSource->nNumTokens; i++)
{
pDest->AddToken(pSource->pArrayOfTokens[i]);
}
}
//***************************************************************************
//
//***************************************************************************
BSTR CQueryEngine::GetParentPath(CWbemInstance* pInst, LPCWSTR wszClassName)
{
// Get the relative path of the instance
// =====================================
LPWSTR wszRelPath = pInst->GetRelPath();
if(wszRelPath == NULL)
return NULL;
BSTR str = AdjustPathToClass(wszRelPath, wszClassName);
delete [] wszRelPath;
return str;
}
//***************************************************************************
//
//***************************************************************************
BSTR CQueryEngine::AdjustPathToClass(LPCWSTR wszRelPath, LPCWSTR wszClassName)
{
// Skip the absolute path
// ======================
if(wszRelPath[0] == '\\')
{
wszRelPath = wcschr(wszRelPath, ':');
if(wszRelPath == NULL)
return NULL;
else
wszRelPath++;
}
// Find the "post-classname" part
// ==============================
WCHAR* pwcDot = wcschr(wszRelPath, L'.');
WCHAR* pwcEquals = wcschr(wszRelPath, L'=');
LPWSTR wszPostClassPart;
if(pwcDot == NULL)
wszPostClassPart = pwcEquals;
else if(pwcEquals == NULL)
wszPostClassPart = pwcDot;
else if(pwcDot < pwcEquals)
wszPostClassPart = pwcDot;
else
wszPostClassPart = pwcEquals;
// Allocate the BSTR for the real thing
// ====================================
BSTR strNewPath;
if(wszPostClassPart)
{
size_t tmpLength = wcslen(wszClassName) + wcslen(wszPostClassPart); // SEC:REVIEWED 2002-03-22 : OK, prior logic assures NULLs
strNewPath = SysAllocStringLen(NULL, tmpLength); // SEC:REVIEWED 2002-03-22 : OK, prior logic assures proper size
if (strNewPath)
StringCchPrintfW(strNewPath, tmpLength+1, L"%s%s", wszClassName, wszPostClassPart);
}
else
{
strNewPath = SysAllocString(wszClassName);
}
return strNewPath;
}
//***************************************************************************
//
// CQueryEngine::ExecAtomicDbQuery
//
// General purpose query driver for QL LEVEL 1. This method parses
// and executes the query against the database engine. The optimizer
// is contained within this function and its auxiliaries.
//
// Preconditions:
// (1) All classes involved in the query are known to have
// only static instances in the database. No interface to dynamic
// classes is provided.
// (2) This method cannot resolve queries against abstract base classes.
//
// Parameters:
// <dwNs> The target namespace.
// <pQueryText> The QL1 query, unparsed.
// <pEnum> Receives the enumerator containing the result set.
//
// Return values:
// <no_error>
// <invalid_query>
// <failed>
// <out_of_memory>
//
//***************************************************************************
// ok / no error objects required
int CQueryEngine::ExecAtomicDbQuery(
IN IWmiDbSession *pSession,
IN IWmiDbHandle *pNsHandle,
IN IWmiDbHandle *pScopeHandle,
IN LPCWSTR wszClassName,
IN QL_LEVEL_1_RPN_EXPRESSION *pExp,
IN CBasicObjectSink* pDest, // no status
IN CWbemNamespace * pNs)
{
int nRetVal = 0;
int nRes;
// Examine the query and see if we can execute it
// in any kind of optimized fashion.
// ==============================================
CWbemObject *pClassDef = 0;
LPWSTR pPropToUse = 0;
CVar *pValToUse = 0;
int nType = 0;
nRes = QueryOptimizationTest(
pSession,
pNsHandle,
pScopeHandle,
wszClassName,
pExp,
&pClassDef,
&pPropToUse,
&pValToUse,
&nType
);
if (nRes == use_key)
{
nRes = KeyedQuery(
pSession,
pNsHandle,
pExp,
pClassDef,
0,
pDest,
pNs
);
if (nRes != 0)
nRetVal = failed;
}
else if (nRes == use_table_scan || nRes == use_index)
{
HRESULT hRes = CRepository::TableScanQuery(
pSession,
pScopeHandle,
(LPWSTR)wszClassName,
pExp,
0,
pDest
);
if (FAILED(hRes))
nRetVal = failed;
else
nRetVal = 0;
}
delete pValToUse;
delete pPropToUse;
if (pClassDef)
pClassDef->Release();
return nRetVal;
}
//***************************************************************************
//
// CQueryEngine::QueryOptimizationTest
//
// Examines a query and its associated class definition. It determines
// what optimizations, if any, can be applied to speed up the query.
// If the query is conjunctive and there is some form of primary or
// secondary indexing which can be used, this method selects the
// appropriate property to use for a retrieval by key or an indexed query.
// If <table_scan> is returned, then a table scan is required.
//
// Parameters:
// <dwNs> The relevant namespace.
// <pExp> A valid QL1 expression.
// <pClassDef> Always receives the deserialized class definition, as long
// as <invalid_class> is not returned. Use operator
// delete to deallocate.
//
// <pPropToUse> If <use_index> is returned, this is assigned to point
// to an indexed property. Use operator delete to deallocate
// This always refers to a non-key property name.
// Set to NULL if <table_scan> is returned.
//
// <pValToUse> The value to use if <use_index> is returned.
// Set to NULL if <use_index> is not returned.
//
// <pnType> Receives the VT_ type of the relevant property.
// Set to NULL if <use_index> is not returned.
//
// Return values:
// <invalid_class> The class did not appear to exist.
// <use_index> The value returned via <pPropToUse> is a property
// with a secondary index which can beused to limit
// the query.
// <use_key> The query is such that all of the key properties
// were specified with equality tests.
// <use_table_scan> A table scan is required.
//
//***************************************************************************
// ok
int CQueryEngine::QueryOptimizationTest(
IN IWmiDbSession *pSession,
IN IWmiDbHandle *pNsHandle,
IN IWmiDbHandle *pScopeHandle,
IN LPCWSTR wszClassName,
IN QL_LEVEL_1_RPN_EXPRESSION *pExp,
OUT CWbemObject **pClassDef,
OUT LPWSTR *pPropToUse,
OUT CVar **pValToUse,
OUT int *pnType
)
{
int nRes;
if (pNsHandle == 0 || pExp == 0 || pClassDef == 0 || pPropToUse == 0 ||
pValToUse == 0 || pnType == 0)
return invalid_parameter;
// Defaults.
// =========
*pClassDef = 0;
*pPropToUse = 0;
*pValToUse = 0;
*pnType = 0;
// Look up the class definition.
// =============================
IWbemClassObject *pCls = 0;
HRESULT hRes = CRepository::GetObject(pSession, pNsHandle, wszClassName, 0, &pCls);
if (FAILED(hRes))
return invalid_class;
CWbemClass *pClsDef = (CWbemClass *) pCls;
*pClassDef = pClsDef;
// Test query for conjunctiveness.
// ===============================
if (!IsConjunctiveQuery(pExp))
return use_table_scan;
// If here, the query is conjunctive. However, a table scan
// may still be required if the only relational tests are on
// non-indexed or non-keyed properties.
// First, get the key properties. If all of the keys
// are used with equality tests, then we could simply retrieve
// the object by key and test it.
// ===========================================================
CWStringArray aKeyProps;
pClsDef->GetKeyProps(aKeyProps);
if (QueryKeyTest(pExp, pClsDef, aKeyProps))
{
return use_key;
}
// If here, the keys were not adequate for limiting
// the query. We next try to see if any indexed properties
// were used.
// =======================================================
CWStringArray aIndexedProps;
pClsDef->GetIndexedProps(aIndexedProps);
if (QueryIndexTest(pExp, pClsDef, aIndexedProps, pPropToUse,
pValToUse, pnType))
{
if (*pValToUse == 0)
return use_table_scan;
// Try to coerce
// =============
if ((*pValToUse)->ChangeTypeTo(CType::GetVARTYPE(*pnType)))
{
return use_index;
}
return use_table_scan;
}
// If here, we have to use a table scan after all.
// ===============================================
return use_table_scan;
}
//***************************************************************************
//
// CQueryEngine::IsConjunctiveQuery
//
// Does an initial screen of a query to see if it clearly not optimizable.
//
// If the query contains an OR or NOT operator, it cannot currently be
// optimized.
//
//***************************************************************************
// ok
BOOL CQueryEngine::IsConjunctiveQuery(
IN QL_LEVEL_1_RPN_EXPRESSION *pExp
)
{
for (int i2 = 0; i2 < pExp->nNumTokens; i2++)
{
QL_LEVEL_1_TOKEN& Tok = pExp->pArrayOfTokens[i2];
if (Tok.nTokenType == QL_LEVEL_1_TOKEN::TOKEN_OR ||
Tok.nTokenType == QL_LEVEL_1_TOKEN::TOKEN_NOT
)
return FALSE;
}
return TRUE;
}
//***************************************************************************
//
// CQueryEngine::QueryKeyTest
//
// Examines a query to see if the result set must be a single instance
// due to use of the key in the 'where' clause. Not only must the
// key(s) be tested for equality, there must be only a single token or
// else all operators must be AND operators.
//
// This also performs type checking on the key(s).
//
//***************************************************************************
// ok
BOOL CQueryEngine::QueryKeyTest(
IN QL_LEVEL_1_RPN_EXPRESSION *pExp,
IN CWbemObject *pClassDef,
IN CWStringArray &aKeyProps
)
{
if (aKeyProps.Size() == 0)
return FALSE;
for (int i = 0; i < aKeyProps.Size(); i++)
{
// Check for unsupported key types
// ===============================
CIMTYPE ct;
pClassDef->GetPropertyType(aKeyProps[i], &ct);
if(ct == CIM_CHAR16 || ct == CIM_REFERENCE || ct== CIM_DATETIME)
return FALSE;
BOOL bFound = FALSE;
for (int i2 = 0; i2 < pExp->nNumTokens; i2++)
{
QL_LEVEL_1_TOKEN& Tok = pExp->pArrayOfTokens[i2];
if (Tok.nTokenType == QL_LEVEL_1_TOKEN::OP_EXPRESSION)
{
// If there is a matching property, check the rest
// of the expression to ensure type compatibility
// and that an equality test is used.
// ===============================================
LPWSTR wszPropName = GetSimplePropertyName(Tok.PropertyName);
if (wszPropName && wbem_wcsicmp(wszPropName, aKeyProps[i]) == 0)
{
if (Tok.nOperator == QL_LEVEL_1_TOKEN::OP_EQUAL)
{
// TBD: Do a type check test here.
if(bFound)
return FALSE; // Duplicate, probably not a good query for keys!
bFound = TRUE;
}
else
{
return FALSE; // The key is being used in a non-equality comparison!! (Bug #43969)
}
}
}
}
if (!bFound)
return FALSE;
}
return TRUE;
}
//***************************************************************************
//
// CQueryEngine::QueryIndexTest
//
// Examines a query to see if the result set can be limited by use
// of a secondary index.
//
//***************************************************************************
// ok
BOOL CQueryEngine::QueryIndexTest(
IN QL_LEVEL_1_RPN_EXPRESSION *pExp,
IN CWbemObject *pClsDef,
IN CWStringArray &aIndexedProps,
OUT LPWSTR *pPropToUse,
OUT CVar **pValToUse,
OUT int *pnType
)
{
for (int i = 0; i < pExp->nNumTokens; i++)
{
QL_LEVEL_1_TOKEN& Tok = pExp->pArrayOfTokens[i];
if (Tok.nTokenType == QL_LEVEL_1_TOKEN::OP_EXPRESSION &&
Tok.nOperator == QL_LEVEL_1_TOKEN::OP_EQUAL)
{
for (int i2 = 0; i2 < aIndexedProps.Size(); i2++)
{
LPWSTR wszPropName = GetSimplePropertyName(Tok.PropertyName);
if (wszPropName &&
wbem_wcsicmp(wszPropName, aIndexedProps[i2]) == 0)
{
CIMTYPE ctType;
HRESULT hRes = pClsDef->GetPropertyType(aIndexedProps[i2],
&ctType);
if ((ctType != CIM_SINT8) &&
(ctType != CIM_UINT8) &&
(ctType != CIM_SINT16) &&
(ctType != CIM_UINT16) &&
(ctType != CIM_SINT32) &&
(ctType != CIM_UINT32) &&
(ctType != CIM_CHAR16) &&
(ctType != CIM_STRING))
continue;
// If here, we have a match.
// =========================
*pPropToUse = Macro_CloneLPWSTR(aIndexedProps[i2]);
*pValToUse = new CVar(&Tok.vConstValue);
// a-levn: added support for NULLs
*pnType = (int)ctType;
return TRUE;
}
}
}
}
return FALSE;
}
//***************************************************************************
//
//***************************************************************************
BOOL AreWeLocal(WCHAR * pServerMachine)
{
if(pServerMachine == NULL)
return TRUE;
if(0 == wbem_wcsicmp(pServerMachine,L"."))
return TRUE;
BOOL bRet = (0 == wbem_wcsicmp(ConfigMgr::GetMachineName(),pServerMachine));
return bRet;
}
LPWSTR CQueryEngine::NormalizePath(LPCWSTR wszObjectPath, CWbemNamespace * pNs)
{
CObjectPathParser Parser;
ParsedObjectPath* pParsedPath;
LPWSTR pReturnString = NULL;
if(CObjectPathParser::NoError != Parser.Parse((LPWSTR)wszObjectPath, &pParsedPath)) return NULL;
OnDeleteObj<ParsedObjectPath*,CObjectPathParser,
void (CObjectPathParser:: *)(ParsedObjectPath *pOutput),
&CObjectPathParser::Free> FreeMe(&Parser,pParsedPath);
if (!pParsedPath->IsObject()) return NULL;
if(NULL == pParsedPath->m_pClass) return NULL;
// Start off with the server and namespace part
WString wsNormal;
try
{
wsNormal += L"\\\\";
if(AreWeLocal(pParsedPath->m_pServer))
wsNormal += L".";
else
wsNormal += pParsedPath->m_pServer;
wsNormal += L"\\";
WCHAR * pPath = pParsedPath->GetNamespacePart();
CVectorDeleteMe<WCHAR> dm1(pPath);
if(pPath)
wsNormal += pPath;
else
wsNormal += pNs->GetName();
wsNormal += L":";
// Find the parent that defined the key
// ====================================
IWbemClassObject *pCls = 0;
HRESULT hRes = CRepository::FindKeyRoot(pNs->GetNsSession(), pNs->GetScope(), pParsedPath->m_pClass, &pCls);
CReleaseMe rmRootCls(pCls);
if (hRes == WBEM_E_NOT_FOUND)
{
wsNormal += pParsedPath->m_pClass;
}
else if (SUCCEEDED(hRes))
{
CVar vName;
HRESULT getClassResult = ((CWbemClass *)pCls)->GetClassName(&vName);
if (FAILED(getClassResult))
return NULL;
wsNormal += vName.GetLPWSTR();
}
// Convert this part to upper-case
// ===============================
LPWSTR wsz = (wchar_t*)wsNormal;
SIZE_T Len = wsNormal.Length();
for(int i = 0; i < Len; i++)
{
wsz[i] = wbem_towupper(wsz[i]);
}
WCHAR * wszKey = pParsedPath->GetKeyString();
if (wszKey)
{
CVectorDeleteMe<WCHAR> dm2(wszKey);
wsNormal += L"=";
wsNormal += wszKey;
pReturnString = wsNormal.UnbindPtr();
}
}
catch (CX_MemoryException &)
{
// pReturnString is already NULL here
}
return pReturnString;
}
//***************************************************************************
//
//***************************************************************************
BOOL CQueryEngine::AreClassesRelated(CWbemNamespace* pNamespace,
IWbemContext* pContext,
CWbemObject* pClass1, LPCWSTR wszClass2)
{
HRESULT hres;
// First check if class 1 inherits from class 2
// ============================================
if(pClass1->InheritsFrom((LPWSTR)wszClass2) == S_OK)
return TRUE;
// Now, unfortunately, we have to go get the second class
// ======================================================
CSynchronousSink* pSink = CSynchronousSink::Create();
if (NULL == pSink) return FALSE;
pSink->AddRef();
CReleaseMe rm1(pSink);
hres = pNamespace->Exec_GetClass(wszClass2, 0, pContext, pSink);
if(FAILED(hres)) return FALSE;
pSink->Block();
pSink->GetStatus(&hres, NULL, NULL);
if(FAILED(hres)) return FALSE;
CWbemClass* pClass2 = (CWbemClass*)(pSink->GetObjects()[0]);
// Get the first class's name
// ==========================
CVar vFirstName;
if (FAILED(pClass1->GetClassName(&vFirstName)))
return FALSE;
// Check if the second class is derived from the first one
// =======================================================
if(pClass2->InheritsFrom(vFirstName.GetLPWSTR()) == S_OK)
return TRUE;
return FALSE;
}
//***************************************************************************
//
// Determines if property <wszPropName> in object <pObj>
// is a reference to <pTargetClass>
//
//***************************************************************************
BOOL CQueryEngine::IsAReferenceToClass(
CWbemNamespace* pNamespace,
IWbemContext* pContext,
CWbemObject* pObj,
LPCWSTR wszPropName,
CWbemObject* pTargetClass,
bool bCheckPropValue
)
{
// Get the cimtype
// ===============
CIMTYPE ct;
if(FAILED(pObj->GetPropertyType((LPWSTR)wszPropName, &ct)) ||
ct != CIM_REFERENCE)
{
return FALSE;
}
CVar vCimType;
if(FAILED(pObj->GetPropQualifier((LPWSTR)wszPropName, TYPEQUAL,
&vCimType)))
{
return FALSE;
}
// See if it is a reference
// ========================
if (!wbem_wcsicmp(vCimType.GetLPWSTR(), L"ref"))
{
// Special case of object refs which only refer to class definitions.
// ==================================================================
if (bCheckPropValue)
{
CVar vClassPath;
CVar vClassName;
int nRes = pObj->GetProperty(wszPropName, &vClassPath);
nRes = pTargetClass->GetClassName(&vClassName);
if (!vClassPath.IsNull() && !vClassPath.IsNull())
{
if (wbem_wcsicmp(vClassName.GetLPWSTR(), vClassPath.GetLPWSTR()) == 0)
return TRUE;
}
}
else
return TRUE;
}
if(wbem_wcsnicmp(vCimType.GetLPWSTR(), L"ref:", 4) == 0)
{
LPWSTR wszClass = vCimType.GetLPWSTR() + 4;
return CQueryEngine::AreClassesRelated(pNamespace, pContext,
pTargetClass, wszClass);
}
return FALSE;
}
//***************************************************************************
//
// CQueryEngine::KeyedQuery
//
// Preconditions:
// The query is known to contain all key properties with equality
// tests such that the object can be retrieved using
// CObjectDatabase::GetObjectByPath and subsequently filtered.
//
//***************************************************************************
// ok
int CQueryEngine::KeyedQuery(
IN IWmiDbSession *pSession,
IN IWmiDbHandle *pNsHandle,
IN QL_LEVEL_1_RPN_EXPRESSION *pExp,
IN CWbemObject *pClassDef,
IN DWORD dwFlags,
IN CBasicObjectSink* pDest, // no status
IN CWbemNamespace * pNs
)
{
int nRet = no_error;
// Convert the query into an object path.
// ======================================
wmilib::auto_buffer<WCHAR> pObjPath( GetObjectPathFromQuery(pClassDef, pExp, pNs));
if (NULL == pObjPath.get()) return invalid_query;
// Now get the object by path.
// ===========================
IWbemClassObject *pObj = 0;
HRESULT hRes = CRepository::GetObject(pSession, pNsHandle, pObjPath.get(), 0, &pObj);
CReleaseMe rmObj(pObj);
// If there was an object, test it against the 'rest' of the query.
// ================================================================
if (SUCCEEDED(hRes))
{
CQlFilteringSink* pFilteringSink = new CQlFilteringSink(pDest, pExp, pNs);
if (NULL == pFilteringSink) return failed;
pFilteringSink->AddRef();
// Indicate it in the Sink
pFilteringSink->Add(pObj);
pFilteringSink->Release();
}
return nRet;
}
//***************************************************************************
//
// CQueryEngine::GetObjectPathFromQuery
//
// Converts the relevant parts of a QL query to an equivalent object
// path. This assumes that the query contains equality tests on all
// key properties such that an object path would generate the same
// single instance as the query.
//
//***************************************************************************
// ok
LPWSTR CQueryEngine::GetObjectPathFromQuery(
IN CWbemObject *pClassDef,
IN QL_LEVEL_1_RPN_EXPRESSION *pExp,
IN CWbemNamespace * pNs
)
{
CWStringArray aKeys;
WString ObjPath;
CVar v;
HRESULT hr = pClassDef->GetClassName(&v);
if (FAILED(hr))
return 0;
ObjPath += v.GetLPWSTR();
ObjPath += L".";
pClassDef->GetKeyProps(aKeys);
BOOL bFirst = TRUE;
for (int i = 0; i < aKeys.Size(); i++)
{
if (!bFirst)
ObjPath += L",";
bFirst = FALSE;
ObjPath += aKeys[i];
ObjPath += L"=";
// Now find the property value.
// ============================
for (int i2 = 0; i2 < pExp->nNumTokens; i2++)
{
QL_LEVEL_1_TOKEN& Tok = pExp->pArrayOfTokens[i2];
LPWSTR wszPropName = GetSimplePropertyName(Tok.PropertyName);
if (Tok.nTokenType == QL_LEVEL_1_TOKEN::OP_EXPRESSION &&
wszPropName && wbem_wcsicmp(aKeys[i], wszPropName) == 0)
{
if (V_VT(&Tok.vConstValue) == VT_BSTR)
{
ObjPath += L"\"";
WString nonEscaped(V_BSTR(&Tok.vConstValue));
WString escaped = nonEscaped.EscapeQuotes();
ObjPath += escaped;
ObjPath += L"\"";
}
else if (V_VT(&Tok.vConstValue) == VT_BOOL)
{
short bValue = V_I2(&Tok.vConstValue);
if(bValue == VARIANT_TRUE)
ObjPath+= L"1";
else
ObjPath += L"0";
}
else
{
_variant_t varTo;
SCODE sc = VariantChangeType(&varTo, &Tok.vConstValue, 0, VT_BSTR);
if(sc == S_OK)
{
wchar_t buf[64];
StringCchPrintf(buf, 64, L"%s", varTo.bstrVal);
ObjPath += buf;
}
}
}
}
}
return ObjPath.UnbindPtr();
}
HRESULT CQueryEngine::FindOverridenProperties(CDynasty* pDyn,
CWStringArray& awsOverriden,
bool bIncludeThis)
{
//
// If this class is included (not top-level), add all the properties
// it overrides to the array
//
if(bIncludeThis)
{
CWbemObject *pTmp = (CWbemObject *) pDyn->m_pClassObj;
for(int i = 0; i < pTmp->GetNumProperties(); i++)
{
CVar vPropName;
pTmp->GetPropName(i, &vPropName);
CVar vOverride;
if(FAILED(pTmp->GetPropQualifier(vPropName.GetLPWSTR(),
L"OVERRIDEVALUE",
&vOverride)))
continue;
//
// Overriden property --- add
//
if (CFlexArray::no_error != awsOverriden.Add(vPropName.GetLPWSTR()))
{
continue;
}
}
}
//
// Recurse through all the children
//
for(int i = 0; i < pDyn->m_Children.Size(); i++)
{
CDynasty* pSubDyn = (CDynasty*)(pDyn->m_Children.GetAt(i));
HRESULT hres = FindOverridenProperties(pSubDyn, awsOverriden, true);
if(FAILED(hres))
return hres;
}
return WBEM_S_NO_ERROR;
}
//***************************************************************************
//
// CQueryEngine::ExecAtomicDynQlQuery
//
//***************************************************************************
// ok
HRESULT CQueryEngine::ExecAtomicDynQlQuery(
IN CWbemNamespace *pNs,
IN CDynasty* pDyn,
IN LPWSTR pszQueryFormat,
IN LPWSTR pszQuery,
IN QL_LEVEL_1_RPN_EXPRESSION *pParsedQuery,
IN LONG lFlags,
IN IWbemContext* pContext,
IN CBasicObjectSink* pDest, // must support selective filtering ,
IN BOOL bComplexQuery
)
{
HRESULT hres;
DEBUGTRACE((LOG_WBEMCORE,"Query Engine request: querying dyn provider with <%S>\n", pszQuery));
//
// Find all the properties that are overriden by derived classes.
// We must remove all references to those properties from the query, since
// otherwise this provider might not return the parent instances needed to
// merge with the child instances with the overriden property values.
//
CWStringArray awsOverriden;
hres = FindOverridenProperties(pDyn, awsOverriden);
if(FAILED(hres))
return pDest->Return(hres);
//
// Get the query analyzer to remove all the properties that are overriden
// or not members of this class (not possible right now anyway)
//
QL_LEVEL_1_RPN_EXPRESSION* pNewParsedQuery = NULL;
hres = CQueryAnalyser::GetNecessaryQueryForClass(pParsedQuery,
pDyn->m_pClassObj, awsOverriden, pNewParsedQuery);
if(FAILED(hres)) return pDest->Return(hres);
CDeleteMe<QL_LEVEL_1_RPN_EXPRESSION> dm1(pNewParsedQuery);
//
// Get the new text to give to provider
//
LPWSTR pszNewQuery = pNewParsedQuery->GetText();
if(pszNewQuery == NULL) return WBEM_E_OUT_OF_MEMORY;
CVectorDeleteMe<WCHAR> vdm(pszNewQuery);
DEBUGTRACE((LOG_WBEMCORE,"Query Engine actual: querying dyn provider with <%S>\n", pszNewQuery));
// Check if the query is empty
// ===========================
BOOL bEmpty = FALSE;
if(lFlags & WBEM_FLAG_SHALLOW)
{
// We know that the query is actually a shallow enumeration
// ========================================================
bEmpty = TRUE;
}
else if(pNewParsedQuery == NULL ||
(pNewParsedQuery->nNumTokens == 0 &&
pNewParsedQuery->nNumberOfProperties == 0))
{
bEmpty = TRUE;
}
if(bEmpty)
{
pNs->DynAux_GetInstances (
(CWbemObject *) pDyn->m_pClassObj, // class def
lFlags & ~WBEM_FLAG_SHALLOW, // used for WBEM_FLAG_SEND_STATUS
pContext,
pDest,
bComplexQuery
);
}
else
{
pNs->DynAux_ExecQueryAsync (
(CWbemObject *) pDyn->m_pClassObj,
pszNewQuery,
pszQueryFormat,
lFlags & ~WBEM_FLAG_SHALLOW,
pContext,
pDest,
bComplexQuery
) ;
}
return WBEM_S_NO_ERROR;
}
HRESULT CQueryEngine::EliminateDuplications(
CRefedPointerArray<CWbemClass>& apClasses,
LPCWSTR wszResultClass)
{
int i;
if(wszResultClass)
{
// Eliminate all classes not derived from wszResultClass
// =====================================================
for(i = 0; i < apClasses.GetSize(); i++)
{
if(apClasses[i]->InheritsFrom((LPWSTR)wszResultClass) !=
WBEM_S_NO_ERROR)
{
// Not derived
apClasses.RemoveAt(i);
i--;
}
}
}
for(i = 0; i < apClasses.GetSize(); i++)
{
// Check if this class is abstract. There is no reason asking abstract
// classes for their objects
// ===================================================================
CVar vAbstract;
if(SUCCEEDED(apClasses[i]->GetQualifier(L"abstract", &vAbstract))
&& vAbstract.GetType() == VT_BOOL && vAbstract.GetBool())
{
apClasses.RemoveAt(i);
i--;
}
}
// Search for pairs // TBD: can be done more efficiently!!
// =======================================================
for(i = 0; i < apClasses.GetSize(); i++)
{
CWbemClass* pClass1 = apClasses[i];
if(pClass1 == NULL)
continue;
CVar vName;
apClasses[i]->GetClassName(&vName);
for (int j = 0; j < apClasses.GetSize(); j++)
{
if(j == i) continue;
CWbemClass* pClass2 = apClasses[j];
if(pClass2 == NULL)
continue;
if (pClass2->InheritsFrom(vName.GetLPWSTR()) == WBEM_S_NO_ERROR)
{
// Eliminate class 2 --- it's parent is listed
// ===========================================
apClasses.SetAt(j, NULL);
}
}
}
return WBEM_S_NO_ERROR;
}
//***************************************************************************
//
//***************************************************************************
LPWSTR CQueryEngine::GetPrimaryName(WBEM_PROPERTY_NAME& Name)
{
if(Name.m_lNumElements < 1 ||
Name.m_aElements[0].m_nType != WBEM_NAME_ELEMENT_TYPE_PROPERTY)
{
return NULL;
}
return Name.m_aElements[0].Element.m_wszPropertyName;
}
//***************************************************************************
//
//***************************************************************************
LPWSTR CQueryEngine::GetSimplePropertyName(WBEM_PROPERTY_NAME& Name)
{
if(Name.m_lNumElements != 1 ||
Name.m_aElements[0].m_nType != WBEM_NAME_ELEMENT_TYPE_PROPERTY)
{
return NULL;
}
return Name.m_aElements[0].Element.m_wszPropertyName;
}
//***************************************************************************
//
//***************************************************************************
HRESULT CQueryEngine::ExecSchemaQuery( IN CWbemNamespace *pNs,
IN LPWSTR pszQuery,
QL_LEVEL_1_RPN_EXPRESSION *pExp,
IN IWbemContext* pContext,
IN CBasicObjectSink* pSink)
{
HRESULT hres = WBEM_S_NO_ERROR;
if (pExp->nNumTokens == 0)
{
//This means we want all classes...
pNs->Exec_CreateClassEnum(NULL, 0, pContext, pSink);
return WBEM_S_NO_ERROR;
}
else if ((pExp->nNumTokens == 1) &&
(pExp->pArrayOfTokens[0].nOperator == QL_LEVEL_1_TOKEN::OP_EQUAL))
{
//This means we have a simple expression (hopefully)
//Now we need to check which type of retrieval we are looking for...
LPCWSTR szPropName = pExp->pArrayOfTokens[0].PropertyName.GetStringAt(0);
VARIANT& vValue = pExp->pArrayOfTokens[0].vConstValue;
if (szPropName == 0)
return pSink->Return(WBEM_E_INVALID_QUERY);
if (wbem_wcsicmp(szPropName, L"__CLASS") == 0)
{
if ((V_VT(&vValue) == VT_BSTR) && (wcslen(V_BSTR(&vValue)))) // SEC:REVIEWED 2002-03-22 : Needs EH or NULL test
{
//Single class retrieval
CErrorChangingSink Err(pSink, WBEM_E_NOT_FOUND, 0);
pNs->Exec_GetObject(V_BSTR(&vValue), 0, pContext, &Err);
return WBEM_S_NO_ERROR;
}
else if((V_VT(&vValue) == VT_NULL) ||
((V_VT(&vValue) == VT_BSTR) && (wcslen(V_BSTR(&vValue))==0))) // SEC:REVIEWED 2002-03-22 : Needs EH or NULL test
{
// __CLASS = NULL
return pSink->Return(WBEM_S_NO_ERROR);
}
else
{
return pSink->Return(WBEM_E_INVALID_QUERY);
}
}
else if (wbem_wcsicmp(szPropName, L"__SUPERCLASS") == 0)
{
if(V_VT(&vValue) == VT_BSTR)
{
CErrorChangingSink Err(pSink, WBEM_E_INVALID_CLASS, 0);
//Get things which are hanging off these items
pNs->Exec_CreateClassEnum(V_BSTR(&vValue), WBEM_FLAG_SHALLOW,
pContext, &Err);
}
else if(V_VT(&vValue) == VT_NULL)
{
// get things which are hanging off root
pNs->Exec_CreateClassEnum(L"", WBEM_FLAG_SHALLOW,
pContext, pSink);
}
else
{
pSink->Return(WBEM_E_INVALID_QUERY);
}
return WBEM_S_NO_ERROR;
}
else if (wbem_wcsicmp(szPropName, L"__DYNASTY") == 0)
{
if(V_VT(&vValue) == VT_BSTR)
{
//Get things which are hanging off these items as well as the item itself
BSTR strClassName = V_BSTR(&vValue);
IWbemClassObject* pClass = NULL;
hres = pNs->Exec_GetObjectByPath(strClassName, 0, pContext,&pClass, NULL);
CReleaseMe rmCls(pClass);
if(FAILED(hres))
{
if(hres == WBEM_E_NOT_FOUND)
hres = S_OK;
return pSink->Return(hres);
}
else // restore the value
{
hres = WBEM_S_NO_ERROR;
}
// Check that this is the root of the dynasty
CVar vDyn;
if(FAILED(((CWbemObject*)pClass)->GetDynasty(&vDyn)))
return pSink->Return(WBEM_E_FAILED);
if (vDyn.IsNull())
return pSink->Return(WBEM_S_NO_ERROR);
if(wbem_wcsicmp(vDyn.GetLPWSTR(), strClassName))
return pSink->Return(WBEM_S_NO_ERROR);
pSink->Add(pClass);
pNs->Exec_CreateClassEnum(strClassName, 0, pContext, pSink);
}
else if(V_VT(&vValue) == VT_NULL)
{
pSink->Return(WBEM_S_NO_ERROR);
}
else
{
pSink->Return(WBEM_E_INVALID_QUERY);
}
return WBEM_S_NO_ERROR;
}
else
{
return pSink->Return(WBEM_E_INVALID_QUERY);
}
}
else if ((pExp->nNumTokens == 1) &&
(pExp->pArrayOfTokens[0].nOperator == QL1_OPERATOR_ISA) &&
(wbem_wcsicmp(pExp->pArrayOfTokens[0].PropertyName.GetStringAt(0), L"__THIS") == 0))
{
//With the isa, we return everything which is derived from this, as well
//as the class in question...
VARIANT & var = pExp->pArrayOfTokens[0].vConstValue;
if(var.vt != VT_BSTR || var.bstrVal == 0)
return pSink->Return(WBEM_E_INVALID_QUERY);
CCombiningSink* pCombiningSink = new CCombiningSink(pSink, WBEM_E_NOT_FOUND);
if (NULL == pCombiningSink) return pSink->Return(WBEM_E_OUT_OF_MEMORY);
pCombiningSink->AddRef();
CReleaseMe rmCombSink(pCombiningSink);
pNs->Exec_GetObject(V_BSTR(&(pExp->pArrayOfTokens[0].vConstValue)), 0, pContext, pCombiningSink);
pNs->Exec_CreateClassEnum(V_BSTR(&(pExp->pArrayOfTokens[0].vConstValue)), 0, pContext, pCombiningSink);
return WBEM_S_NO_ERROR;
}
// OK, so all the simple cases are dealt with here. We should now check everything is
// valid and process it in the best possible way. If this is a conjunctive query
// we can also do a little optimisation!
//Lets validate all of the properties to make sure they are all valid. If we
//did not do this, there are scenarios where we would get inconsistencies
//based on the different code paths.
BOOL bError = FALSE;
//While we are at it, we can do a check for the first location of each type of property
//name (this is used for optimisation!)
BOOL bConjunctive = IsConjunctiveQuery(pExp);
QL_LEVEL_1_TOKEN *pThisToken = NULL,
*pClassToken = NULL,
*pSuperclassToken = NULL,
*pDynastyToken = NULL;
for (int i = 0; i != pExp->nNumTokens; i++)
{
QL_LEVEL_1_TOKEN* pCurrentToken = pExp->pArrayOfTokens + i;
if (pCurrentToken->PropertyName.GetNumElements() > 1)
{
//This is probably an error!
bError = TRUE;
break;
}
else if (pCurrentToken->PropertyName.GetNumElements() == 1)
{
//We need to validate it...
//If it is an isa, it can only be a "__this", otherwise it has to be one
//of the "__superclass", "__dynasty" or "__class"
LPCWSTR wszCurrentPropName = pCurrentToken->PropertyName.GetStringAt(0);
if (wszCurrentPropName == 0)
{
bError = TRUE;
break;
}
if (pCurrentToken->nOperator == QL1_OPERATOR_ISA)
{
if(wbem_wcsicmp(wszCurrentPropName, L"__THIS"))
{
bError = TRUE;
break;
}
}
else
{
if(wbem_wcsicmp(wszCurrentPropName, L"__CLASS") &&
wbem_wcsicmp(wszCurrentPropName, L"__SUPERCLASS") &&
wbem_wcsicmp(wszCurrentPropName, L"__DYNASTY"))
{
bError = TRUE;
break;
}
}
if (bConjunctive)
{
VARIANT* pCurrentValue = &(pCurrentToken->vConstValue);
if (wbem_wcsicmp(wszCurrentPropName, L"__THIS") == 0)
{
if(V_VT(pCurrentValue) != VT_BSTR)
bError = TRUE;
else if (!pThisToken)
pThisToken = pCurrentToken;
}
else if (wbem_wcsicmp(wszCurrentPropName, L"__CLASS") == 0)
{
if(V_VT(pCurrentValue) != VT_BSTR && V_VT(pCurrentValue) != VT_NULL)
bError = TRUE;
else if (pCurrentToken->nOperator != QL_LEVEL_1_TOKEN::OP_EQUAL)
bConjunctive = FALSE;
else if (!pClassToken)
pClassToken = pCurrentToken;
}
else if (wbem_wcsicmp(wszCurrentPropName, L"__SUPERCLASS") == 0)
{
if(V_VT(pCurrentValue) != VT_BSTR && V_VT(pCurrentValue) != VT_NULL)
bError = TRUE;
else if (pCurrentToken->nOperator != QL_LEVEL_1_TOKEN::OP_EQUAL)
bConjunctive = FALSE;
else if (!pSuperclassToken)
pSuperclassToken = pCurrentToken;
}
else // DYNASTY
{
if(V_VT(pCurrentValue) != VT_BSTR)
bError = TRUE;
else if (pCurrentToken->nOperator != QL_LEVEL_1_TOKEN::OP_EQUAL)
bConjunctive = FALSE;
else if (!pDynastyToken)
pDynastyToken = pCurrentToken;
}
}
}
}
if (bError == TRUE) return pSink->Return(WBEM_E_INVALID_QUERY);
//We need to create a filter sink to deal with this query....
CQlFilteringSink* pFilteringSink = new CQlFilteringSink(pSink, pExp, pNs, TRUE);
if (NULL == pFilteringSink) return pSink->Return(WBEM_E_OUT_OF_MEMORY);
pFilteringSink->AddRef();
CReleaseMe rmFilter(pFilteringSink);
//If this is conjunctive we can just retrieve a single item based on a set of
//rules and pass this through the filter
if (bConjunctive)
{
//We can pick a single item to retrieve and pass this through the filter rather
//than retrieve all of them
if (pClassToken)
{
//Single class retrieval
if(V_VT(&(pClassToken->vConstValue)) == VT_NULL)
{
// null class --- no such thing
pFilteringSink->Return(WBEM_S_NO_ERROR);
}
else // VT_BSTR
{
pNs->Exec_GetObject(V_BSTR(&(pClassToken->vConstValue)), 0,
pContext, pFilteringSink);
}
}
else if (pSuperclassToken)
{
//Get things which are hanging off these items
BSTR strParent = NULL;
if(V_VT(&(pSuperclassToken->vConstValue)) == VT_NULL)
{
// null superclass
strParent = NULL;
}
else // VT_BSTR
{
strParent = V_BSTR(&(pSuperclassToken->vConstValue));
}
pNs->Exec_CreateClassEnum(strParent, 0, pContext, pFilteringSink);
}
else if (pDynastyToken)
{
//Get things which are hanging off these items and the item itself
CCombiningSink* pCombiningSink = new CCombiningSink(pFilteringSink, WBEM_E_NOT_FOUND);
if (NULL == pCombiningSink) return pSink->Return(WBEM_E_OUT_OF_MEMORY);
rmFilter.release(); // Combining Took Ownership
pCombiningSink->AddRef();
// Guaranteed to be VT_BSTR
pNs->Exec_GetObject(V_BSTR(&(pDynastyToken->vConstValue)), 0, pContext, pCombiningSink);
pNs->Exec_CreateClassEnum(V_BSTR(&(pDynastyToken->vConstValue)), 0, pContext, pCombiningSink);
pCombiningSink->Release();
}
else if (pThisToken)
{
CCombiningSink* pCombiningSink = new CCombiningSink(pFilteringSink, WBEM_E_NOT_FOUND);
if (NULL == pCombiningSink) return pSink->Return(WBEM_E_OUT_OF_MEMORY);
rmFilter.release(); // Combining Took Ownership
pCombiningSink->AddRef();
// Guaranteed to be VT_BSTR
pNs->Exec_GetObject(V_BSTR(&(pThisToken->vConstValue)), 0, pContext, pCombiningSink);
pNs->Exec_CreateClassEnum(V_BSTR(&(pThisToken->vConstValue)), 0, pContext, pCombiningSink);
pCombiningSink->Release();
}
else
{
//Something strange here!
pNs->Exec_CreateClassEnum(NULL, 0, pContext, pFilteringSink);
}
}
else
{
//We need to retrieve all of them and pass through the filter.
pNs->Exec_CreateClassEnum(NULL, 0, pContext, pFilteringSink);
}
return hres;
}
// ****************************************************************************
//
// CQueryEngine::ValidateQuery
//
// This function makes sure that the data type of the property matches
// that of the const.
//
// ****************************************************************************
HRESULT CQueryEngine::ValidateQuery(IN QL_LEVEL_1_RPN_EXPRESSION *pExpr,
IN CWbemClass *pClassDef)
{
HRESULT hr = WBEM_S_NO_ERROR;
for(int i = 0; i < pExpr->nNumTokens; i++)
{
QL_LEVEL_1_TOKEN Token = pExpr->pArrayOfTokens[i];
if (Token.nTokenType == QL1_OP_EXPRESSION)
{
WBEM_WSTR wszCimType;
VARIANT PropVal;
VariantInit(&PropVal);
// Make sure this property exists.
// ===============================
hr = pClassDef->GetPropertyValue(&Token.PropertyName, 0,
&wszCimType, &PropVal);
// If we haven't found it, that's OK... it could
// be a weakly-typed embedded object.
if (FAILED(hr))
{
hr = WBEM_S_NO_ERROR;
continue;
}
switch(Token.nOperator)
{
// These only apply to embedded objects.
case QL1_OPERATOR_ISA:
case QL1_OPERATOR_ISNOTA:
case QL1_OPERATOR_INV_ISA:
case QL1_OPERATOR_INV_ISNOTA:
if(V_VT(&PropVal)!= VT_EMBEDDED_OBJECT)
{
if (wszCimType != NULL)
{
wchar_t wszTemp[7];
wcsncpy(wszTemp, wszCimType, 6); // SEC:REVIEWED 2002-03-22 : Fix this code to be more reasonable / RAID 591466
wszTemp[6] = '\0';
if (wcscmp(wszTemp, L"object"))
hr = WBEM_E_INVALID_QUERY;
}
else
hr = WBEM_E_INVALID_QUERY;
if (Token.vConstValue.vt == VT_NULL ||
Token.vConstValue.vt == VT_EMPTY)
hr = WBEM_E_INVALID_QUERY;
}
break;
default:
break;
}
VariantClear(&PropVal);
WbemStringFree(wszCimType);
}
if (hr != WBEM_S_NO_ERROR)
break;
}
// We don't support WITHIN!
if (pExpr->Tolerance.m_bExact == FALSE)
{
hr = WBEM_E_INVALID_QUERY;
}
return hr;
}
//***************************************************************************
//
//***************************************************************************
//
HRESULT CQueryEngine::ExecRepositoryQuery(
IN CWbemNamespace *pNs,
IN LPWSTR pszQuery,
IN LONG lFlags,
IN IWbemContext* pContext,
IN CBasicObjectSink* pSink
)
{
HRESULT hRes;
// Also, add check hierarchy for dynamic instances which need deleting
// Should we simulate by a prior enum and then executing individual delete instance
// calls? Would be a big performance drain, possibly.
hRes = CRepository::ExecQuery(pNs->GetNsSession(), pNs->GetScope(), pszQuery, pSink, 0);
return hRes;
}