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