|
|
/*++
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; }
|