mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
537 lines
13 KiB
537 lines
13 KiB
/*++
|
|
|
|
Copyright (C) 1996-2001 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
SQL1FILT.CPP
|
|
|
|
Abstract:
|
|
|
|
History:
|
|
|
|
--*/
|
|
|
|
#include "sql1filt.h"
|
|
|
|
void* CSql1Filter::GetInterface(REFIID riid)
|
|
{
|
|
if(riid == IID_IHmmFilter || riid == IID_IHmmSql1Filter)
|
|
return (IHmmSql1Filter*)&m_XFilter;
|
|
else if(riid == IID_IConfigureHmmSql1Filter)
|
|
return (IConfigureHmmSql1Filter*)&m_XConfigure;
|
|
else if(riid == IID_IHmmParse)
|
|
return (IHmmParse*)&m_XParse;
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
STDMETHODIMP CSql1Filter::XFilter::
|
|
IsSpecial()
|
|
{
|
|
if(m_pObject->m_apTokens.GetSize() == 0)
|
|
{
|
|
return HMM_S_ACCEPTS_EVERYTHING;
|
|
}
|
|
else
|
|
{
|
|
return HMM_S_FALSE;
|
|
}
|
|
}
|
|
|
|
STDMETHODIMP CSql1Filter::XFilter::
|
|
CheckObject(IN IHmmPropertySource* pSource,
|
|
OUT IHmmPropertyList** ppList, OUT IUnknown** ppHint)
|
|
{
|
|
if(ppHint) *ppHint = NULL;
|
|
|
|
return m_pObject->Evaluate(FALSE, pSource, ppList);
|
|
}
|
|
|
|
STDMETHODIMP CSql1Filter::XFilter::
|
|
GetType(IID* piid)
|
|
{
|
|
if(piid == NULL)
|
|
return HMM_E_INVALID_PARAMETER;
|
|
*piid = IID_IHmmSql1Filter;
|
|
return HMM_S_NO_ERROR;
|
|
}
|
|
|
|
STDMETHODIMP CSql1Filter::XFilter::
|
|
GetSelectedPropertyList(IN long lFlags, OUT IHmmPropertyList** ppList)
|
|
{
|
|
return m_pObject->m_TargetClass.GetSelected().QueryInterface(
|
|
IID_IHmmPropertyList,
|
|
(void**)ppList);
|
|
}
|
|
|
|
STDMETHODIMP CSql1Filter::XFilter::
|
|
GetTargetClass(OUT HMM_CLASS_INFO* pTargetClass)
|
|
{
|
|
if(pTargetClass == NULL)
|
|
return HMM_E_INVALID_PARAMETER;
|
|
|
|
m_pObject->m_XParse.EnsureTarget();
|
|
m_pObject->m_TargetClass.Save(*pTargetClass);
|
|
return HMM_S_NO_ERROR;
|
|
}
|
|
|
|
STDMETHODIMP CSql1Filter::XFilter::
|
|
GetTokens(IN long lFirstIndex, IN long lNumTokens, OUT long* plTokensReturned,
|
|
OUT HMM_SQL1_TOKEN* aTokens)
|
|
{
|
|
if(plTokensReturned == NULL || lNumTokens <= 0 || lFirstIndex < 0 ||
|
|
aTokens == NULL)
|
|
{
|
|
return HMM_E_INVALID_PARAMETER;
|
|
}
|
|
|
|
m_pObject->m_XParse.EnsureWhere();
|
|
|
|
long lHaveTokens = m_pObject->m_apTokens.GetSize();
|
|
if(lFirstIndex >= lHaveTokens)
|
|
{
|
|
*plTokensReturned = 0;
|
|
return HMM_S_NO_MORE_DATA;
|
|
}
|
|
|
|
long lGaveTokens = 0;
|
|
long lCurrentIndex = lFirstIndex;
|
|
while(lGaveTokens < lNumTokens && lCurrentIndex < lHaveTokens)
|
|
{
|
|
CSql1Token* pToken = m_pObject->m_apTokens[lCurrentIndex];
|
|
if(pToken->m_lTokenType != TOKENTYPE_SPECIAL)
|
|
{
|
|
pToken->Save(aTokens[lGaveTokens++]);
|
|
}
|
|
lCurrentIndex++;
|
|
}
|
|
|
|
*plTokensReturned = lGaveTokens;
|
|
return HMM_S_NO_ERROR;
|
|
}
|
|
|
|
//********************************* Configuration ******************************
|
|
|
|
STDMETHODIMP CSql1Filter::XConfigure::
|
|
SetTargetClass(IN HMM_CLASS_INFO* pTargetClass)
|
|
{
|
|
if(pTargetClass == NULL)
|
|
return HMM_E_INVALID_PARAMETER;
|
|
|
|
m_pObject->m_TargetClass.Load(*pTargetClass);
|
|
m_pObject->InvalidateTree();
|
|
return HMM_S_NO_ERROR;
|
|
}
|
|
|
|
STDMETHODIMP CSql1Filter::XConfigure::
|
|
AddTokens(IN long lNumTokens, IN HMM_SQL1_TOKEN* aTokens)
|
|
{
|
|
if(lNumTokens <= 0 || aTokens == NULL)
|
|
return HMM_E_INVALID_PARAMETER;
|
|
|
|
for(long l = 0; l < lNumTokens; l++)
|
|
{
|
|
m_pObject->m_apTokens.Add(new CSql1Token(aTokens[l]));
|
|
}
|
|
m_pObject->InvalidateTree();
|
|
return HMM_S_NO_ERROR;
|
|
}
|
|
|
|
STDMETHODIMP CSql1Filter::XConfigure::
|
|
RemoveAllTokens()
|
|
{
|
|
m_pObject->m_apTokens.RemoveAll();
|
|
m_pObject->InvalidateTree();
|
|
return HMM_S_NO_ERROR;
|
|
}
|
|
|
|
STDMETHODIMP CSql1Filter::XConfigure::
|
|
RemoveAllProperties()
|
|
{
|
|
return m_pObject->m_TargetClass.GetSelected().RemoveAllProperties();
|
|
}
|
|
|
|
STDMETHODIMP CSql1Filter::XConfigure::
|
|
AddProperties(IN long lNumProps, HMM_WSTR* awszProps)
|
|
{
|
|
return m_pObject->m_TargetClass.GetSelected().
|
|
AddProperties(lNumProps, awszProps);
|
|
}
|
|
|
|
//*********************************** Parsing *********************************
|
|
|
|
|
|
STDMETHODIMP CSql1Filter::XParse::
|
|
Parse(IN HMM_WSTR wszText, IN long lFlags)
|
|
{
|
|
delete m_pParser;
|
|
m_pParser = NULL;
|
|
|
|
if((lFlags & HMM_FLAG_LAZY) == 0)
|
|
{
|
|
m_wsText.Empty();
|
|
m_nStatus = not_parsing;
|
|
|
|
m_pObject->InvalidateTree();
|
|
CAbstractSql1Parser Parser(new CTextLexSource(wszText));
|
|
int nRes = Parser.Parse(this, CAbstractSql1Parser::FULL_PARSE);
|
|
m_InnerStack.Empty();
|
|
return ParserError(nRes);
|
|
}
|
|
else
|
|
{
|
|
m_wsText = wszText;
|
|
m_pParser = new CAbstractSql1Parser(new CTextLexSource((LPWSTR)m_wsText));
|
|
m_nStatus = at_0;
|
|
return HMM_S_NO_ERROR;
|
|
}
|
|
}
|
|
|
|
void CSql1Filter::XParse::SetClassName(LPCWSTR wszClass)
|
|
{
|
|
m_pObject->m_TargetClass.AccessClassName() = (LPWSTR)wszClass;
|
|
m_pObject->m_TargetClass.AccessIncludeChildren() = TRUE;
|
|
}
|
|
|
|
void CSql1Filter::XParse::AddToken(COPY const HMM_SQL1_TOKEN& Token)
|
|
{
|
|
m_pObject->m_apTokens.Add(new CSql1Token(Token));
|
|
if(Token.m_lTokenType == SQL1_OR || Token.m_lTokenType == SQL1_AND)
|
|
{
|
|
// Find the inner guy
|
|
// ==================
|
|
|
|
long lInnerIndex;
|
|
if(!m_InnerStack.Pop(lInnerIndex))
|
|
return;
|
|
|
|
V_I4(&m_pObject->m_apTokens[lInnerIndex]->m_vConstValue) =
|
|
m_pObject->m_apTokens.GetSize();
|
|
}
|
|
}
|
|
|
|
void CSql1Filter::XParse::AddProperty(COPY LPCWSTR wszProperty)
|
|
{
|
|
m_pObject->m_TargetClass.GetSelected().
|
|
AddProperties(1, (LPWSTR*)&wszProperty);
|
|
}
|
|
|
|
void CSql1Filter::XParse::AddAllProperties()
|
|
{
|
|
m_pObject->m_TargetClass.GetSelected().AddAllProperties();
|
|
}
|
|
|
|
void CSql1Filter::XParse::InOrder(long lOp)
|
|
{
|
|
/*
|
|
CSql1Token* pToken = new CSql1Token;
|
|
pToken->m_lTokenType = TOKENTYPE_SPECIAL;
|
|
pToken->m_lOperator = lOp;
|
|
V_I4(&pToken->m_vConstValue) = -1;
|
|
m_InnerStack.Push(m_pObject->m_apTokens.GetSize());
|
|
m_pObject->m_apTokens.Add(pToken);
|
|
*/
|
|
}
|
|
|
|
HRESULT CSql1Filter::XParse::EnsureTarget()
|
|
{
|
|
if(m_nStatus == at_0)
|
|
{
|
|
m_pObject->InvalidateTree();
|
|
int nRes = m_pParser->Parse(this, CAbstractSql1Parser::NO_WHERE);
|
|
m_nStatus = at_where;
|
|
return ParserError(nRes);
|
|
}
|
|
else return HMM_S_NO_ERROR;
|
|
}
|
|
|
|
HRESULT CSql1Filter::XParse::EnsureWhere()
|
|
{
|
|
if(m_nStatus == not_parsing)
|
|
{
|
|
return HMM_S_NO_ERROR;
|
|
}
|
|
else
|
|
{
|
|
m_pObject->InvalidateTree();
|
|
int nRes = m_pParser->Parse(this,
|
|
(m_nStatus == at_0)?CAbstractSql1Parser::FULL_PARSE
|
|
:CAbstractSql1Parser::JUST_WHERE);
|
|
m_InnerStack.Empty();
|
|
delete m_pParser;
|
|
m_pParser = NULL;
|
|
m_nStatus = not_parsing;
|
|
return ParserError(nRes);
|
|
}
|
|
}
|
|
|
|
HRESULT CSql1Filter::XParse::ParserError(int nRes)
|
|
{
|
|
switch(nRes)
|
|
{
|
|
case CAbstractSql1Parser::SUCCESS:
|
|
return HMM_S_NO_ERROR;
|
|
case CAbstractSql1Parser::SYNTAX_ERROR:
|
|
return HMM_E_INVALID_QUERY;
|
|
case CAbstractSql1Parser::LEXICAL_ERROR:
|
|
return HMM_E_INVALID_QUERY;
|
|
case CAbstractSql1Parser::FAILED:
|
|
return HMM_E_FAILED;
|
|
default:
|
|
return HMM_E_CRITICAL_ERROR;
|
|
}
|
|
}
|
|
|
|
|
|
CSql1Filter::XParse::~XParse()
|
|
{
|
|
delete m_pParser;
|
|
}
|
|
|
|
//*****************************************************************************
|
|
//*************************** Query Evaluator *********************************
|
|
//*****************************************************************************
|
|
//*****************************************************************************
|
|
//
|
|
// See sql1eveal.h for documentation
|
|
//
|
|
//*****************************************************************************
|
|
|
|
HRESULT CSql1Filter::Evaluate(BOOL bSkipTarget,
|
|
IN READ_ONLY IHmmPropertySource* pPropSource,
|
|
OUT IHmmPropertyList** ppList)
|
|
{
|
|
HRESULT hres;
|
|
|
|
GetTree();
|
|
if(m_pTree) m_pTree->Release();
|
|
|
|
if(ppList) *ppList = NULL;
|
|
|
|
hres = CHmmNode::EvaluateNode(m_pTree, pPropSource);
|
|
if(hres != HMM_S_NO_ERROR) return hres;
|
|
|
|
if(ppList)
|
|
{
|
|
m_TargetClass.GetSelected().QueryInterface(IID_IHmmPropertyList,
|
|
(void**)ppList);
|
|
}
|
|
|
|
return HMM_S_NO_ERROR;
|
|
|
|
|
|
if(!bSkipTarget)
|
|
{
|
|
// Check the class of the object
|
|
// =============================
|
|
|
|
hres = m_XParse.EnsureTarget();
|
|
if(FAILED(hres)) return hres;
|
|
|
|
CHmmClassInfo* pInfo = &m_TargetClass;
|
|
hres = CHmmClassInfo::CheckObjectAgainstMany(1, &pInfo,
|
|
pPropSource, ppList, NULL);
|
|
if(hres != HMM_S_NO_ERROR)
|
|
{
|
|
// Either an error or a simple class mismatch
|
|
// ==========================================
|
|
|
|
return hres;
|
|
}
|
|
}
|
|
|
|
// Now go for the expression
|
|
// =========================
|
|
|
|
hres = m_XParse.EnsureWhere();
|
|
if(FAILED(hres)) return hres;
|
|
|
|
int nNumTokens = m_apTokens.GetSize();
|
|
|
|
if(nNumTokens == 0)
|
|
{
|
|
// Empty query
|
|
// ===========
|
|
|
|
return HMM_S_NO_ERROR;
|
|
}
|
|
|
|
// Allocate boolean stack of appropriate length
|
|
// ============================================
|
|
|
|
CBooleanStack Stack(nNumTokens);
|
|
|
|
for(int nTokenIndex = 0; nTokenIndex < nNumTokens; nTokenIndex++)
|
|
{
|
|
CSql1Token* pToken = m_apTokens[nTokenIndex];
|
|
BOOL bVal1, bVal2;
|
|
|
|
switch(pToken->m_lTokenType)
|
|
{
|
|
case SQL1_AND:
|
|
// Pop the last two operands and AND them together
|
|
// ===============================================
|
|
|
|
if(!Stack.Pop(bVal1) || !Stack.Pop(bVal2))
|
|
{
|
|
return HMM_E_INVALID_QUERY;
|
|
}
|
|
|
|
Stack.Push(bVal1 && bVal2);
|
|
break;
|
|
|
|
case SQL1_OR:
|
|
// Pop the last two operands and OR them together
|
|
// ===============================================
|
|
|
|
if(!Stack.Pop(bVal1) || !Stack.Pop(bVal2))
|
|
{
|
|
return HMM_E_INVALID_QUERY;
|
|
}
|
|
|
|
Stack.Push(bVal1 || bVal2);
|
|
break;
|
|
|
|
case SQL1_NOT:
|
|
// Pop the last value and invert it
|
|
// ================================
|
|
|
|
if(!Stack.Pop(bVal1))
|
|
{
|
|
return HMM_E_INVALID_QUERY;
|
|
}
|
|
|
|
Stack.Push(!bVal1);
|
|
break;
|
|
|
|
case SQL1_OP_EXPRESSION:
|
|
// Evaluate the expression and push its value
|
|
// ==========================================
|
|
|
|
hres = pToken->Evaluate(pPropSource);
|
|
if(FAILED(hres)) return hres;
|
|
bVal1 = (hres == HMM_S_NO_ERROR);
|
|
|
|
Stack.Push(bVal1);
|
|
break;
|
|
|
|
case TOKENTYPE_SPECIAL:
|
|
if(!Stack.Pop(bVal1))
|
|
{
|
|
return HMM_E_INVALID_QUERY;
|
|
}
|
|
Stack.Push(bVal1);
|
|
if((pToken->m_lOperator == SQL1_OR && bVal1) ||
|
|
(pToken->m_lOperator == SQL1_AND && !bVal1))
|
|
{
|
|
nTokenIndex = V_I4(&pToken->m_vConstValue) - 1;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// All tokens have been processed. There better be one element on the stack
|
|
// ========================================================================
|
|
|
|
BOOL bResult;
|
|
if(!Stack.Pop(bResult))
|
|
{
|
|
return HMM_E_INVALID_QUERY;
|
|
}
|
|
|
|
if(!Stack.IsEmpty())
|
|
{
|
|
return HMM_E_INVALID_QUERY;
|
|
}
|
|
|
|
if(bResult)
|
|
return HMM_S_NO_ERROR;
|
|
else
|
|
return HMM_S_FALSE;
|
|
}
|
|
|
|
|
|
|
|
CHmmNode* CSql1Filter::GetTree()
|
|
{
|
|
if(m_pTree != NULL)
|
|
{
|
|
m_pTree->AddRef();
|
|
return m_pTree;
|
|
}
|
|
|
|
// Construct the tree from the RPN
|
|
// ===============================
|
|
|
|
CUniquePointerStack<CHmmNode> aNodes(m_apTokens.GetSize());
|
|
CHmmNode* pNode1;
|
|
CHmmNode* pNode2;
|
|
CLogicalNode* pNewNode;
|
|
CSql1Token* pNewToken;
|
|
|
|
for(int i = 0; i < m_apTokens.GetSize(); i++)
|
|
{
|
|
CSql1Token* pToken = m_apTokens[i];
|
|
switch(pToken->m_lTokenType)
|
|
{
|
|
case SQL1_OR:
|
|
case SQL1_AND:
|
|
if(!aNodes.Pop(pNode2)) return NULL;
|
|
if(!aNodes.Pop(pNode1)) return NULL;
|
|
pNewNode = new CLogicalNode;
|
|
pNewNode->m_lTokenType = pToken->m_lTokenType;
|
|
pNewNode->Add(pNode1);
|
|
pNewNode->Add(pNode2);
|
|
aNodes.Push(pNewNode);
|
|
break;
|
|
case SQL1_NOT:
|
|
if(!aNodes.Pop(pNode1)) return NULL;
|
|
pNode1->Negate();
|
|
aNodes.Push(pNode1);
|
|
break;
|
|
|
|
case SQL1_OP_EXPRESSION:
|
|
pNewToken = new CSql1Token(*pToken);
|
|
aNodes.Push(pNewToken);
|
|
break;
|
|
default:
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
CHmmNode* pWhere;
|
|
if(!aNodes.Pop(pWhere))
|
|
{
|
|
if(i == 0)
|
|
pWhere = NULL;
|
|
else
|
|
return NULL;
|
|
}
|
|
if(!aNodes.IsEmpty()) return NULL;
|
|
|
|
// Add the class check
|
|
// ===================
|
|
|
|
CHmmNode* pTarget = m_TargetClass.GetTree();
|
|
|
|
CLogicalNode* pMain = new CLogicalNode;
|
|
pMain->m_lTokenType = SQL1_AND;
|
|
pMain->Add(pTarget);
|
|
pTarget->Release(); // Matching GetTree()
|
|
if(pWhere)
|
|
{
|
|
pMain->Add(pWhere);
|
|
}
|
|
|
|
m_pTree = pMain;
|
|
m_pTree->AddRef(); // for storage
|
|
m_pTree->AddRef(); // for return
|
|
|
|
m_pTree->Print(stdout, 0);
|
|
return m_pTree;
|
|
}
|
|
|
|
|
|
|
|
|