/*++ Copyright (C) 1996-2001 Microsoft Corporation Module Name: SQL_1.CPP Abstract: Level 1 Syntax SQL Parser Implements the syntax described in SQL_1.BNF. This translates the input into an RPN stream of tokens. History: 21-Jun-96 Created. --*/ #include #include #include #include #include #include class CX_Exception {}; class CX_MemoryException : CX_Exception {}; //#define trace(x) printf x #define trace(x) static DWORD TranslateIntrinsic(LPWSTR pFuncName) { if (_wcsicmp(pFuncName, L"UPPER") == 0) return SQL_LEVEL_1_TOKEN::IFUNC_UPPER; if (_wcsicmp(pFuncName, L"LOWER") == 0) return SQL_LEVEL_1_TOKEN::IFUNC_LOWER; return SQL_LEVEL_1_TOKEN::IFUNC_NONE; } SQL1_Parser::SQL1_Parser(CGenLexSource *pSrc) { Init(pSrc); } SQL1_Parser::~SQL1_Parser() { Cleanup(); } void SQL1_Parser::Init(CGenLexSource *pSrc) { m_nLine = 0; m_pTokenText = 0; m_nCurrentToken = 0; m_pExpression = 0; m_pLexer = 0; // Semantic transfer variables. // ============================ m_nRelOp = 0; VariantInit(&m_vTypedConst); m_dwPropFunction = 0; m_dwConstFunction = 0; m_pIdent = 0; m_pPropComp = 0; m_bConstIsStrNumeric = FALSE; if (pSrc) { m_pLexer = new CGenLexer(Sql_1_LexTable, pSrc); m_pExpression = new SQL_LEVEL_1_RPN_EXPRESSION; } } void SQL1_Parser::Cleanup() { VariantClear(&m_vTypedConst); delete m_pIdent; delete m_pPropComp; delete m_pLexer; delete m_pExpression; } void SQL1_Parser::SetSource(CGenLexSource *pSrc) { Cleanup(); Init(pSrc); } int SQL1_Parser::GetQueryClass( LPWSTR pDestBuf, int nBufLen ) { if ((!m_pLexer) || (!pDestBuf)) { return FAILED; } // Scan until 'FROM' and then get the class name. // ============================================== for (;;) { m_nCurrentToken = m_pLexer->NextToken(); if (m_nCurrentToken == SQL_1_TOK_EOF) { m_pLexer->Reset(); return FAILED; } if (_wcsicmp(m_pLexer->GetTokenText(), L"from") == 0) { m_nCurrentToken = m_pLexer->NextToken(); if (m_nCurrentToken != SQL_1_TOK_IDENT) { m_pLexer->Reset(); return FAILED; } // If here, we have the class name. // ================================ if (wcslen(m_pLexer->GetTokenText()) >= (size_t)nBufLen) { m_pLexer->Reset(); return BUFFER_TOO_SMALL; } wcscpy(pDestBuf, m_pLexer->GetTokenText()); break; } } // Reset the scanner. // ================== m_pLexer->Reset(); return SUCCESS; } int SQL1_Parser::Parse(SQL_LEVEL_1_RPN_EXPRESSION **pOutput) { if ((!m_pLexer) || (!pOutput)) { return FAILED; } *pOutput = 0; int nRes = parse(); if (nRes) return nRes; *pOutput = m_pExpression; m_pExpression = 0; return SUCCESS; } LPSTR ToAnsi(LPWSTR Src) { static char buf[256]; WideCharToMultiByte(CP_ACP, NULL, Src, -1, buf, 256, NULL, NULL); return buf; } //*************************************************************************** // // Next() // // Advances to the next token and recognizes keywords, etc. // //*************************************************************************** BOOL SQL1_Parser::Next() { m_nCurrentToken = m_pLexer->NextToken(); if (m_nCurrentToken == SQL_1_TOK_ERROR) return FALSE; m_nLine = m_pLexer->GetLineNum(); m_pTokenText = m_pLexer->GetTokenText(); if (m_nCurrentToken == SQL_1_TOK_EOF) m_pTokenText = L""; // Keyword check. // ============== if (m_nCurrentToken == SQL_1_TOK_IDENT) { if (_wcsicmp(m_pTokenText, L"select") == 0) m_nCurrentToken = SQL_1_TOK_SELECT; else if (_wcsicmp(m_pTokenText, L"from") == 0) m_nCurrentToken = SQL_1_TOK_FROM; else if (_wcsicmp(m_pTokenText, L"where") == 0) m_nCurrentToken = SQL_1_TOK_WHERE; else if (_wcsicmp(m_pTokenText, L"like") == 0) m_nCurrentToken = SQL_1_TOK_LIKE; else if (_wcsicmp(m_pTokenText, L"or") == 0) m_nCurrentToken = SQL_1_TOK_OR; else if (_wcsicmp(m_pTokenText, L"and") == 0) m_nCurrentToken = SQL_1_TOK_AND; else if (_wcsicmp(m_pTokenText, L"not") == 0) m_nCurrentToken = SQL_1_TOK_NOT; else if (_wcsicmp(m_pTokenText, L"IS") == 0) m_nCurrentToken = SQL_1_TOK_IS; else if (_wcsicmp(m_pTokenText, L"NULL") == 0) m_nCurrentToken = SQL_1_TOK_NULL; else if (_wcsicmp(m_pTokenText, L"TRUE") == 0) { m_nCurrentToken = SQL_1_TOK_BOOL; m_pTokenText = L"65535"; } else if (_wcsicmp(m_pTokenText, L"FALSE") == 0) { m_nCurrentToken = SQL_1_TOK_BOOL; m_pTokenText = L"0"; } } return TRUE; } //*************************************************************************** // // ::= SELECT FROM WHERE ; // //*************************************************************************** // ok int SQL1_Parser::parse() { int nRes; // SELECT // ====== if (!Next()) return LEXICAL_ERROR; if (m_nCurrentToken != SQL_1_TOK_SELECT) return SYNTAX_ERROR; if (!Next()) return LEXICAL_ERROR; // // =========== if (nRes = prop_list()) return nRes; // FROM // ==== if (m_nCurrentToken != SQL_1_TOK_FROM) return SYNTAX_ERROR; if (!Next()) return LEXICAL_ERROR; // // =========== if (nRes = class_name()) return nRes; // WHERE clause. // ============= return opt_where(); } //*************************************************************************** // // ::= WHERE ; // ::= <>; // //*************************************************************************** int SQL1_Parser::opt_where() { int nRes; if (m_nCurrentToken == SQL_1_TOK_EOF) { trace(("No WHERE clause\n")); return SUCCESS; } if (m_nCurrentToken != SQL_1_TOK_WHERE) return SYNTAX_ERROR; if (!Next()) return LEXICAL_ERROR; // // ====== if (nRes = expr()) return nRes; // Verify that the current token is SQL_1_TOK_EOF. // =============================================== if (m_nCurrentToken != SQL_1_TOK_EOF) return SYNTAX_ERROR; return SUCCESS; } //*************************************************************************** // // ::= ; // //*************************************************************************** int SQL1_Parser::prop_list() { int nRes; if (m_nCurrentToken != SQL_1_TOK_ASTERISK && m_nCurrentToken != SQL_1_TOK_IDENT) return SYNTAX_ERROR; if (nRes = property_name()) return nRes; if (!Next()) return LEXICAL_ERROR; return prop_list_2(); } //*************************************************************************** // // ::= COMMA ; // ::= <>; // //*************************************************************************** int SQL1_Parser::prop_list_2() { if (m_nCurrentToken == SQL_1_TOK_COMMA) { if (!Next()) return LEXICAL_ERROR; return prop_list(); } return SUCCESS; } //*************************************************************************** // // ::= PROPERTY_NAME_STRING; // ::= ASTERISK; // //*************************************************************************** int SQL1_Parser::property_name() { try { if (m_nCurrentToken == SQL_1_TOK_ASTERISK) { trace(("Asterisk\n")); // We need to clean up the expression so far. for (int i = 0; i < m_pExpression->nNumberOfProperties; i++) SysFreeString(m_pExpression->pbsRequestedPropertyNames[i]); m_pExpression->nNumberOfProperties = 0; // This signals 'all properties' to the evaluator return SUCCESS; } // Else a property name. // ===================== trace(("Property name %S\n", m_pTokenText)); m_pExpression->AddProperty(m_pTokenText); } catch (...) { return FAILED; } return SUCCESS; } //*************************************************************************** // // ::= CLASS_NAME_STRING; // //*************************************************************************** int SQL1_Parser::class_name() { if (m_nCurrentToken != SQL_1_TOK_IDENT) return SYNTAX_ERROR; trace(("Class name is %S\n", m_pTokenText)); m_pExpression->bsClassName = SysAllocString(m_pTokenText); if (!Next()) return LEXICAL_ERROR; return SUCCESS; } //*************************************************************************** // // ::= ; // //*************************************************************************** int SQL1_Parser::expr() { int nRes; if (nRes = term()) return nRes; if (nRes = expr2()) return nRes; return SUCCESS; } //*************************************************************************** // // ::= OR ; // ::= <>; // // Entry: Assumes token OR already current. // Exit: Advances a token // //*************************************************************************** int SQL1_Parser::expr2() { int nRes; while (1) { if (m_nCurrentToken == SQL_1_TOK_OR) { trace(("Token OR\n")); if (!Next()) return LEXICAL_ERROR; if (nRes = term()) return nRes; SQL_LEVEL_1_TOKEN *pNewTok = new SQL_LEVEL_1_TOKEN; pNewTok->nTokenType = SQL_LEVEL_1_TOKEN::TOKEN_OR; m_pExpression->AddToken(pNewTok); } else break; } return SUCCESS; } //*************************************************************************** // // ::= ; // //*************************************************************************** int SQL1_Parser::term() { int nRes; if (nRes = simple_expr()) return nRes; if (nRes = term2()) return nRes; return SUCCESS; } //*************************************************************************** // // ::= AND ; // ::= <>; // //*************************************************************************** int SQL1_Parser::term2() { int nRes; while (1) { if (m_nCurrentToken == SQL_1_TOK_AND) { trace(("Token AND\n")); if (!Next()) return LEXICAL_ERROR; if (nRes = simple_expr()) return nRes; // Add the AND token. // ================== SQL_LEVEL_1_TOKEN *pNewTok = new SQL_LEVEL_1_TOKEN; pNewTok->nTokenType = SQL_LEVEL_1_TOKEN::TOKEN_AND; m_pExpression->AddToken(pNewTok); } else break; } return SUCCESS; } //*************************************************************************** // // ::= NOT ; // ::= OPEN_PAREN CLOSE_PAREN; // ::= IDENTIFIER ; // ::= VARIANT ; // //*************************************************************************** // ok int SQL1_Parser::simple_expr() { int nRes; // NOT // ========== if (m_nCurrentToken == SQL_1_TOK_NOT) { trace(("Operator NOT\n")); if (!Next()) return LEXICAL_ERROR; if (nRes = simple_expr()) return nRes; SQL_LEVEL_1_TOKEN *pNewTok = new SQL_LEVEL_1_TOKEN; pNewTok->nTokenType = SQL_LEVEL_1_TOKEN::TOKEN_NOT; m_pExpression->AddToken(pNewTok); return SUCCESS; } // OPEN_PAREN CLOSE_PAREN // ============================= else if (m_nCurrentToken == SQL_1_TOK_OPEN_PAREN) { trace(("Open Paren: Entering subexpression\n")); if (!Next()) return LEXICAL_ERROR; if (expr()) return SYNTAX_ERROR; if (m_nCurrentToken != SQL_1_TOK_CLOSE_PAREN) return SYNTAX_ERROR; trace(("Close paren: Exiting subexpression\n")); if (!Next()) return LEXICAL_ERROR; return SUCCESS; } // IDENTIFIER // ========================================== else if (m_nCurrentToken == SQL_1_TOK_IDENT) { trace((" Identifier <%S>\n", m_pTokenText)); m_pIdent = new wchar_t[wcslen(m_pTokenText) + 1]; if (!m_pIdent) { return FAILED; } wcscpy(m_pIdent, m_pTokenText); if (!Next()) return LEXICAL_ERROR; if (nRes = leading_ident_expr()) return SYNTAX_ERROR; return finalize(); } // // ====================================================== else if (m_nCurrentToken == SQL_1_TOK_INT || m_nCurrentToken == SQL_1_TOK_REAL || m_nCurrentToken == SQL_1_TOK_QSTRING ) { if (nRes = typed_constant()) return nRes; if (nRes = rel_operator()) return nRes; if (nRes = trailing_prop_expr()) return nRes; return finalize(); } return SYNTAX_ERROR; } //*************************************************************************** // // ::= IDENTIFIER ; // //*************************************************************************** // ok int SQL1_Parser::trailing_prop_expr() { if (m_nCurrentToken != SQL_1_TOK_IDENT) return SYNTAX_ERROR; if (!m_pIdent) { m_pIdent = new wchar_t[wcslen(m_pTokenText) + 1]; if (!m_pIdent) { return FAILED; } wcscpy(m_pIdent, m_pTokenText); } else { m_pPropComp = new wchar_t[wcslen(m_pTokenText) + 1]; if (!m_pPropComp) { return FAILED; } wcscpy(m_pPropComp, m_pTokenText); } if (!Next()) return LEXICAL_ERROR; return trailing_prop_expr2(); } //*************************************************************************** // // ::= OPEN_PAREN IDENTIFIER CLOSE_PAREN; // ::= <>; // //*************************************************************************** // ok int SQL1_Parser::trailing_prop_expr2() { if (m_nCurrentToken == SQL_1_TOK_OPEN_PAREN) { if (!Next()) return LEXICAL_ERROR; // If we got to this point, the string pointed to by m_pIdent // was an intrinsic function and not a property name, and we // are about to get the property name, so we have to translate // the function name to its correct code before overwriting it. // ============================================================ trace(("Translating intrinsic function %S\n", m_pIdent)); m_dwPropFunction = TranslateIntrinsic(m_pIdent); delete m_pIdent; m_pIdent = new wchar_t[wcslen(m_pTokenText) + 1]; if (!m_pIdent) { return FAILED; } wcscpy(m_pIdent, m_pTokenText); if (!Next()) return LEXICAL_ERROR; if (m_nCurrentToken != SQL_1_TOK_CLOSE_PAREN) return SYNTAX_ERROR; if (!Next()) return LEXICAL_ERROR; } trace(("Property name is %S\n", m_pIdent)); return SUCCESS; } //*************************************************************************** // // ::= OPEN_PAREN ; // ::= ; // ::= ; // ::= NULL; // //*************************************************************************** // ok int SQL1_Parser::leading_ident_expr() { int nRes; if (m_nCurrentToken == SQL_1_TOK_OPEN_PAREN) { if (!Next()) return LEXICAL_ERROR; return unknown_func_expr(); } if (SUCCESS == comp_operator() || SUCCESS == equiv_operator()) return trailing_or_null(); nRes = is_operator(); if(nRes != SUCCESS) return nRes; if (m_nCurrentToken != SQL_1_TOK_NULL) return LEXICAL_ERROR; if (Next()) { V_VT(&m_vTypedConst) = VT_NULL; return SUCCESS; } else return LEXICAL_ERROR; } //*************************************************************************** // // ::= IDENTIFIER CLOSE_PAREN // ; // // ::= CLOSE_PAREN // ; // //*************************************************************************** // ok int SQL1_Parser::unknown_func_expr() { int nRes; if (m_nCurrentToken == SQL_1_TOK_IDENT) { m_dwPropFunction = TranslateIntrinsic(m_pIdent); delete m_pIdent; m_pIdent = new wchar_t[wcslen(m_pTokenText) + 1]; if (!m_pIdent) { return FAILED; } wcscpy(m_pIdent, m_pTokenText); if (!Next()) return LEXICAL_ERROR; if (m_nCurrentToken != SQL_1_TOK_CLOSE_PAREN) return SYNTAX_ERROR; if (!Next()) return LEXICAL_ERROR; if (nRes = rel_operator()) return nRes; return trailing_const_expr(); } // Else the other production. // ========================== if (nRes = typed_constant()) return nRes; // If here, we know that the leading ident was // an intrinsic function. // =========================================== m_dwConstFunction = TranslateIntrinsic(m_pIdent); delete m_pIdent; m_pIdent = 0; if (m_nCurrentToken != SQL_1_TOK_CLOSE_PAREN) return SYNTAX_ERROR; if (!Next()) return LEXICAL_ERROR; if (nRes = rel_operator()) return nRes; return trailing_prop_expr(); } //*************************************************************************** // // ::= NULL; // ::= ; // ::= ; // //*************************************************************************** int SQL1_Parser::trailing_or_null() { int nRes; if (m_nCurrentToken == SQL_1_TOK_NULL) { if (!Next()) return LEXICAL_ERROR; else { V_VT(&m_vTypedConst) = VT_NULL; return SUCCESS; } } else if (!(nRes = trailing_const_expr())) return nRes; return trailing_prop_expr(); } //*************************************************************************** // // ::= IDENTIFIER OPEN_PAREN // CLOSE_PAREN; // ::= ; // //*************************************************************************** // ok int SQL1_Parser::trailing_const_expr() { int nRes; if (m_nCurrentToken == SQL_1_TOK_IDENT) { trace(("Function applied to typed const = %S\n", m_pTokenText)); m_dwConstFunction = TranslateIntrinsic(m_pTokenText); if (!m_dwConstFunction) return SYNTAX_ERROR; if (!Next()) return LEXICAL_ERROR; if (m_nCurrentToken != SQL_1_TOK_OPEN_PAREN) return SYNTAX_ERROR; if (!Next()) return LEXICAL_ERROR; if (nRes = typed_constant()) return nRes; if (m_nCurrentToken != SQL_1_TOK_CLOSE_PAREN) return SYNTAX_ERROR; if (!Next()) return LEXICAL_ERROR; return SUCCESS; } return typed_constant(); } //*************************************************************************** // // ::= <>; // // This composes the SQL_LEVEL_1_TOKEN for a simple relational expression, // complete with any associated intrinsic functions. All of the other // parse functions help isolate the terms of the expression, but only // this function builds the token. // // To build the token, the following member variables are used: // m_pPropName // m_vTypedConst // m_dwPropFunction // m_dwConstFunction // m_nRelOp; // // After the token is built, these are cleared/deallocated as appropriate. // No tokens are consumed and the input is not advanced. // //*************************************************************************** int SQL1_Parser::finalize() { // At this point, we have all the info needed for a token. // ======================================================= SQL_LEVEL_1_TOKEN *pNewTok = new SQL_LEVEL_1_TOKEN; if (!pNewTok) return FAILED; pNewTok->nTokenType = SQL_LEVEL_1_TOKEN::OP_EXPRESSION; pNewTok->pPropertyName = SysAllocString(m_pIdent); if (m_pPropComp) pNewTok->pPropName2 = SysAllocString(m_pPropComp); pNewTok->nOperator = m_nRelOp; VariantInit(&pNewTok->vConstValue); VariantCopy(&pNewTok->vConstValue, &m_vTypedConst); pNewTok->dwPropertyFunction = m_dwPropFunction; pNewTok->dwConstFunction = m_dwConstFunction; pNewTok->bConstIsStrNumeric = m_bConstIsStrNumeric; m_pExpression->AddToken(pNewTok); // Cleanup. // ======== VariantClear(&m_vTypedConst); delete m_pIdent; m_pIdent = 0; m_nRelOp = 0; m_dwPropFunction = 0; m_dwConstFunction = 0; m_bConstIsStrNumeric = FALSE; return SUCCESS; } //*************************************************************************** // // ::= VARIANT; // // Ouput: m_vTypedConst is set to the value of the constant. The only // supported types are VT_I4, VT_R8 and VT_BSTR. // //*************************************************************************** int SQL1_Parser::typed_constant() { trace((" Typed constant <%S> ", m_pTokenText)); VariantClear(&m_vTypedConst); m_bConstIsStrNumeric = FALSE; if (m_nCurrentToken == SQL_1_TOK_INT) { trace((" Integer\n")); DWORD x = wcslen(m_pTokenText); if (*m_pTokenText == L'-') { //negative if ((x < 11) || ((x == 11) && (wcscmp(m_pTokenText, L"-2147483648") <= 0))) { V_VT(&m_vTypedConst) = VT_I4; V_I4(&m_vTypedConst) = _wtol(m_pTokenText); } else { trace((" Actually Integer String\n")); V_VT(&m_vTypedConst) = VT_BSTR; V_BSTR(&m_vTypedConst) = SysAllocString(m_pTokenText); m_bConstIsStrNumeric = TRUE; } } else { //positive if ((x < 10) || ((x == 10) && (wcscmp(m_pTokenText, L"2147483647") <= 0))) { V_VT(&m_vTypedConst) = VT_I4; V_I4(&m_vTypedConst) = _wtol(m_pTokenText); } else { trace((" Actually Integer String\n")); V_VT(&m_vTypedConst) = VT_BSTR; V_BSTR(&m_vTypedConst) = SysAllocString(m_pTokenText); m_bConstIsStrNumeric = TRUE; } } } else if (m_nCurrentToken == SQL_1_TOK_QSTRING) { trace((" String\n")); V_VT(&m_vTypedConst) = VT_BSTR; V_BSTR(&m_vTypedConst) = SysAllocString(m_pTokenText); } else if (m_nCurrentToken == SQL_1_TOK_REAL) { trace((" Real\n")); V_VT(&m_vTypedConst) = VT_R8; V_R8(&m_vTypedConst) = 0.0; if (m_pTokenText) { VARIANT varFrom; varFrom.vt = VT_BSTR; varFrom.bstrVal = SysAllocString(m_pTokenText); if(varFrom.bstrVal != NULL) { VariantClear(&m_vTypedConst); VariantInit(&m_vTypedConst); SCODE sc = VariantChangeTypeEx(&m_vTypedConst, &varFrom, 0, 0x409, VT_R8); VariantClear(&varFrom); if(sc != S_OK) { VariantClear(&m_vTypedConst); VariantInit(&m_vTypedConst); return LEXICAL_ERROR; } } } } else if (m_nCurrentToken == SQL_1_TOK_BOOL) { trace((" Bool\n")); V_VT(&m_vTypedConst) = VT_BOOL; if (m_pTokenText && _wcsicmp(m_pTokenText, L"65535") == 0) { V_BOOL(&m_vTypedConst) = VARIANT_TRUE; } else V_BOOL(&m_vTypedConst) = VARIANT_FALSE; } // Else, not a typed constant. else return SYNTAX_ERROR; if (!Next()) return LEXICAL_ERROR; return SUCCESS; } //*************************************************************************** // // ::= ; // ::= ; // //*************************************************************************** int SQL1_Parser::rel_operator() { if(SUCCESS == equiv_operator()) return SUCCESS; else if (SUCCESS == comp_operator()) return SUCCESS; else return LEXICAL_ERROR; } //*************************************************************************** // // ::= EQUIV_OPERATOR; // =, != // // Output: m_nRelOp is set to the correct operator for a SQL_LEVEL_1_TOKEN. // //*************************************************************************** int SQL1_Parser::equiv_operator() { m_nRelOp = 0; if (m_nCurrentToken == SQL_1_TOK_EQ) { trace((" REL OP =\n")); m_nRelOp = SQL_LEVEL_1_TOKEN::OP_EQUAL; } else if (m_nCurrentToken == SQL_1_TOK_NE) { trace((" REL OP <> (!=) \n")); m_nRelOp = SQL_LEVEL_1_TOKEN::OP_NOT_EQUAL; } else return SYNTAX_ERROR; if (!Next()) return LEXICAL_ERROR; return SUCCESS; } //*************************************************************************** // // ::= IS_OPERATOR; // is, isnot // // Output: m_nRelOp is set to the correct operator for a SQL_LEVEL_1_TOKEN. // //*************************************************************************** int SQL1_Parser::is_operator() { m_nRelOp = 0; if (m_nCurrentToken != SQL_1_TOK_IS) return SYNTAX_ERROR; if (!Next()) return LEXICAL_ERROR; if (m_nCurrentToken == SQL_1_TOK_NOT) { m_nRelOp = SQL_LEVEL_1_TOKEN::OP_NOT_EQUAL; if (!Next()) return LEXICAL_ERROR; trace((" REL OP IS NOT \n")); m_nRelOp = SQL_LEVEL_1_TOKEN::OP_NOT_EQUAL; return SUCCESS; } else { trace((" REL OP IS \n")); m_nRelOp = SQL_LEVEL_1_TOKEN::OP_EQUAL; return SUCCESS; } return SUCCESS; } //*************************************************************************** // // ::= COMP_OPERATOR; // <=, >=, <, >, like // // Output: m_nRelOp is set to the correct operator for a SQL_LEVEL_1_TOKEN. // //*************************************************************************** int SQL1_Parser::comp_operator() { m_nRelOp = 0; if (m_nCurrentToken == SQL_1_TOK_LE) { trace((" REL OP <=\n")); if (m_pIdent) { m_nRelOp = SQL_LEVEL_1_TOKEN::OP_EQUALorLESSTHAN; } else { trace((" REL OP changed to >=\n")); m_nRelOp = SQL_LEVEL_1_TOKEN::OP_EQUALorGREATERTHAN; } } else if (m_nCurrentToken == SQL_1_TOK_LT) { trace((" REL OP <\n")); if (m_pIdent) { m_nRelOp = SQL_LEVEL_1_TOKEN::OP_LESSTHAN; } else { trace((" REL OP changed to >\n")); m_nRelOp = SQL_LEVEL_1_TOKEN::OP_GREATERTHAN; } } else if (m_nCurrentToken == SQL_1_TOK_GE) { trace((" REL OP >=\n")); if (m_pIdent) { m_nRelOp = SQL_LEVEL_1_TOKEN::OP_EQUALorGREATERTHAN; } else { trace((" REL OP changed to <=\n")); m_nRelOp = SQL_LEVEL_1_TOKEN::OP_EQUALorLESSTHAN; } } else if (m_nCurrentToken == SQL_1_TOK_GT) { trace((" REL OP >\n")); if (m_pIdent) { m_nRelOp = SQL_LEVEL_1_TOKEN::OP_GREATERTHAN; } else { trace((" REL OP changed to <\n")); m_nRelOp = SQL_LEVEL_1_TOKEN::OP_LESSTHAN; } } else if (m_nCurrentToken == SQL_1_TOK_LIKE) { trace((" REL OP 'like' \n")); m_nRelOp = SQL_LEVEL_1_TOKEN::OP_LIKE; } else return SYNTAX_ERROR; if (!Next()) return LEXICAL_ERROR; return SUCCESS; } //*************************************************************************** // // Expression and token structure methods. // //*************************************************************************** SQL_LEVEL_1_RPN_EXPRESSION::SQL_LEVEL_1_RPN_EXPRESSION() { nNumTokens = 0; pArrayOfTokens = 0; bsClassName = 0; nNumberOfProperties = 0; pbsRequestedPropertyNames = 0; nCurSize = 32; nCurPropSize = 32; pArrayOfTokens = new SQL_LEVEL_1_TOKEN[nCurSize]; pbsRequestedPropertyNames = new BSTR[nCurPropSize]; } SQL_LEVEL_1_RPN_EXPRESSION::~SQL_LEVEL_1_RPN_EXPRESSION() { delete [] pArrayOfTokens; if (bsClassName) SysFreeString(bsClassName); for (int i = 0; i < nNumberOfProperties; i++) SysFreeString(pbsRequestedPropertyNames[i]); delete pbsRequestedPropertyNames; } void SQL_LEVEL_1_RPN_EXPRESSION::AddToken(SQL_LEVEL_1_TOKEN *pTok) { AddToken(*pTok); delete pTok; pTok = NULL; } void SQL_LEVEL_1_RPN_EXPRESSION::AddToken(SQL_LEVEL_1_TOKEN &pTok) { if (nCurSize == nNumTokens) { nCurSize += 32; SQL_LEVEL_1_TOKEN *pTemp = new SQL_LEVEL_1_TOKEN[nCurSize]; if (pTemp) { for (int i = 0; i < nNumTokens; i++) pTemp[i] = pArrayOfTokens[i]; delete [] pArrayOfTokens; pArrayOfTokens = pTemp; } } if (pArrayOfTokens) { pArrayOfTokens[nNumTokens++] = pTok; } } void SQL_LEVEL_1_RPN_EXPRESSION::AddProperty (LPWSTR pProp) { if (nCurPropSize == nNumberOfProperties) { nCurPropSize += 32; BSTR *pTemp = new BSTR[nCurPropSize]; if (!pTemp) throw CX_MemoryException(); if (pbsRequestedPropertyNames) memcpy(pTemp, pbsRequestedPropertyNames, sizeof(BSTR) * nNumberOfProperties); else { delete [] pTemp; throw CX_MemoryException(); } delete pbsRequestedPropertyNames; pbsRequestedPropertyNames = pTemp; } pbsRequestedPropertyNames[nNumberOfProperties++] = SysAllocString(pProp); } void SQL_LEVEL_1_RPN_EXPRESSION::Dump(const char *pszTextFile) { FILE *f = fopen(pszTextFile, "wt"); if (!f) return; fprintf(f, "----RPN Expression----\n"); fprintf(f, "Class name = %S\n", bsClassName); fprintf(f, "Properties selected: "); if (!nNumberOfProperties) { fprintf(f, "* = all properties selected\n"); } else for (int i = 0; i < nNumberOfProperties; i++) { fprintf(f, "%S ", pbsRequestedPropertyNames[i]); } fprintf(f, "\n------------------\n"); fprintf(f, "Tokens:\n"); for (int i = 0; i < nNumTokens; i++) pArrayOfTokens[i].Dump(f); fprintf(f, "---end of expression---\n"); fclose(f); } SQL_LEVEL_1_TOKEN::SQL_LEVEL_1_TOKEN() { nTokenType = 0; pPropertyName = 0; pPropName2 = 0; nOperator = 0; VariantInit(&vConstValue); dwPropertyFunction = 0; dwConstFunction = 0; bConstIsStrNumeric = FALSE; } SQL_LEVEL_1_TOKEN::SQL_LEVEL_1_TOKEN(SQL_LEVEL_1_TOKEN &Src) { nTokenType = 0; pPropertyName = 0; pPropName2 = 0; nOperator = 0; VariantInit(&vConstValue); dwPropertyFunction = 0; dwConstFunction = 0; bConstIsStrNumeric = FALSE; *this = Src; } SQL_LEVEL_1_TOKEN& SQL_LEVEL_1_TOKEN::operator =(SQL_LEVEL_1_TOKEN &Src) { //first clear any old values... if (pPropertyName) SysFreeString(pPropertyName); if (pPropName2) SysFreeString(pPropName2); VariantClear(&vConstValue); nTokenType = Src.nTokenType; pPropertyName = SysAllocString(Src.pPropertyName); if (Src.pPropName2) pPropName2 = SysAllocString(Src.pPropName2); nOperator = Src.nOperator; VariantCopy(&vConstValue, &Src.vConstValue); dwPropertyFunction = Src.dwPropertyFunction; dwConstFunction = Src.dwConstFunction; bConstIsStrNumeric = Src.bConstIsStrNumeric; return *this; } SQL_LEVEL_1_TOKEN::~SQL_LEVEL_1_TOKEN() { nTokenType = 0; if (pPropertyName) SysFreeString(pPropertyName); if (pPropName2) SysFreeString(pPropName2); nOperator = 0; VariantClear(&vConstValue); } void SQL_LEVEL_1_TOKEN::Dump(FILE *f) { switch (nTokenType) { case OP_EXPRESSION: fprintf(f, "OP_EXPRESSION "); break; case TOKEN_AND: fprintf(f, "TOKEN_AND "); break; case TOKEN_OR: fprintf(f, "TOKEN_OR "); break; case TOKEN_NOT: fprintf(f, "TOKEN_NOT "); break; default: fprintf(f, "Error: no token type specified\n"); } if (nTokenType == OP_EXPRESSION) { char *pOp = ""; switch (nOperator) { case OP_EQUAL: pOp = "OP_EQUAL"; break; case OP_NOT_EQUAL: pOp = "OP_NOT_EQUAL"; break; case OP_EQUALorGREATERTHAN: pOp = "OP_EQUALorGREATERTHAN"; break; case OP_EQUALorLESSTHAN: pOp = "OP_EQUALorLESSTHAN"; break; case OP_LESSTHAN: pOp = "OP_LESSTHAN"; break; case OP_GREATERTHAN: pOp = "OP_GREATERTHAN"; break; case OP_LIKE: pOp = "OP_LIKE"; break; } fprintf(f, " Property = %S\n", pPropertyName); fprintf(f, " Operator = %s\n", pOp); fprintf(f, " Value = "); if (pPropName2) fprintf(f, " \n"); } } switch (dwPropertyFunction) { case IFUNC_NONE: break; case IFUNC_LOWER: fprintf(f, "Intrinsic function LOWER() applied to property\n"); break; case IFUNC_UPPER: fprintf(f, "Intrinsic function UPPER() applied to property\n"); break; } switch (dwConstFunction) { case IFUNC_NONE: break; case IFUNC_LOWER: fprintf(f, "Intrinsic function LOWER() applied to const value\n"); break; case IFUNC_UPPER: fprintf(f, "Intrinsic function UPPER() applied to const value\n"); break; } } fprintf(f, " \n"); } ///////////////////////////////////////////////////////////////////////////// // // Algorithm for evaluating the expression, assuming that it has been // tokenized and translated to Reverse Polish. // // Starting point: (a) An array of SQL tokens. // (b) An empty boolean token stack. // // 1. Read Next Token // // 2. If a SIMPLE EXPRESSION, evaluate it to TRUE or FALSE, and // place this boolean result on the stack. Go to 1. // // 3. If an OR operator, then pop a boolean token into A, // pop another boolean token into B. If either A or B are TRUE, // stack TRUE. Else stack FALSE. // Go to 1. // // 4. If an AND operator, then pop a boolean token into A, // and pop another into B. If both are TRUE, stack TRUE. // Else stack FALSE. // Go to 1. // // 5. If a NOT operator, reverse the value of the top-of-stack boolean. // Go to 1. // // At end-of-input, the result is at top-of-stack. // /////////////////////////////////////////////////////////////////////////////