//%2006//////////////////////////////////////////////////////////////////////// // // Copyright (c) 2000, 2001, 2002 BMC Software; Hewlett-Packard Development // Company, L.P.; IBM Corp.; The Open Group; Tivoli Systems. // Copyright (c) 2003 BMC Software; Hewlett-Packard Development Company, L.P.; // IBM Corp.; EMC Corporation, The Open Group. // Copyright (c) 2004 BMC Software; Hewlett-Packard Development Company, L.P.; // IBM Corp.; EMC Corporation; VERITAS Software Corporation; The Open Group. // Copyright (c) 2005 Hewlett-Packard Development Company, L.P.; IBM Corp.; // EMC Corporation; VERITAS Software Corporation; The Open Group. // Copyright (c) 2006 Hewlett-Packard Development Company, L.P.; IBM Corp.; // EMC Corporation; Symantec Corporation; The Open Group. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to // deal in the Software without restriction, including without limitation the // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or // sell copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // THE ABOVE COPYRIGHT NOTICE AND THIS PERMISSION NOTICE SHALL BE INCLUDED IN // ALL COPIES OR SUBSTANTIAL PORTIONS OF THE SOFTWARE. THE SOFTWARE IS PROVIDED // "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT // LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR // PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN // ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // //============================================================================== // // Author: Markus Mueller (sedgewick_de@yahoo.de) // // Modified By: Humberto Rivero (hurivero@us.ibm.com) // //%///////////////////////////////////////////////////////////////////////////// #include "Cql2Dnf.h" #include #include PEGASUS_USING_STD; PEGASUS_NAMESPACE_BEGIN #define PEGASUS_ARRAY_T term_el # include #undef PEGASUS_ARRAY_T #define PEGASUS_ARRAY_T eval_el # include #undef PEGASUS_ARRAY_T #define PEGASUS_ARRAY_T stack_el # include #undef PEGASUS_ARRAY_T // // Terminal element methods // void term_el::negate() { NOT = true; }; // // Evaluation heap element methods // stack_el eval_el::getFirst() { return stack_el(opn1, is_terminal1); } stack_el eval_el::getSecond() { return stack_el(opn2, is_terminal2); } void eval_el::setFirst(const stack_el s) { opn1 = s.opn; is_terminal1 = s.is_terminal; } void eval_el::setSecond(const stack_el s) { opn2 = s.opn; is_terminal2 = s.is_terminal; } void eval_el::assign_unary_to_first(const eval_el & assignee) { opn1 = assignee.opn1; is_terminal1 = assignee.is_terminal1; } void eval_el::assign_unary_to_second(const eval_el & assignee) { opn2 = assignee.opn1; is_terminal2 = assignee.is_terminal1; } // Ordering operators, so that op1 > op2 for all non-terminals // and terminals appear in the second operand first void eval_el::order(void) { int k; if ((!is_terminal1) && (!is_terminal2)) if ((k = opn2) > opn1) { opn2 = opn1; opn1 = k; } else if ((is_terminal1) && (!is_terminal2)) if ((k = opn2) > opn1) { opn2 = opn1; opn1 = k; is_terminal1 = false; is_terminal2 = true; } } /* static bool operator==(const term_el& x, const term_el& y) { return x._simplePredicate == y._simplePredicate; } */ // // CQL Compiler methods // /* Cql2Dnf::Cql2Dnf() { eval_heap.reserveCapacity(16); terminal_heap.reserveCapacity(16); } Cql2Dnf::Cql2Dnf(CQLSelectStatement & cqs) { eval_heap.reserveCapacity(16); terminal_heap.reserveCapacity(16); compile(&cqs); } Cql2Dnf::Cql2Dnf(CQLSelectStatement * cqs) { eval_heap.reserveCapacity(16); terminal_heap.reserveCapacity(16); compile(cqs); } */ Cql2Dnf::Cql2Dnf(CQLPredicate& topLevel){ eval_heap.reserveCapacity(16); terminal_heap.reserveCapacity(16); compile(topLevel); } Cql2Dnf::~Cql2Dnf() {} /* void Cql2Dnf::compile(CQLSelectStatement * cqs){ CQLPredicate topLevel = cqs->getPredicate(); compile(topLevel); } */ void Cql2Dnf::compile(CQLPredicate& topLevel) { PEG_METHOD_ENTER(TRC_CQL, "Cql2Dnf::compile"); _strip_ops_operands(topLevel); _buildEvalHeap(); _pushNOTDown(); _factoring(); _construct(); eval_heap.clear(); PEG_METHOD_EXIT(); } void Cql2Dnf::_buildEvalHeap() { PEG_METHOD_ENTER(TRC_CQL, "Cql2Dnf::_buildEvalHeap"); Stack stack; // Counter for Operands Uint32 j = 0; for (Uint32 i = 0, n = _operations.size(); i < n; i++) { OperationType op = _operations[i]; switch (op) { case CQL_OR: case CQL_AND: { PEGASUS_ASSERT(stack.size() >= 2); stack_el op1 = stack.top(); stack.pop(); stack_el op2 = stack.top(); // generate Eval expression eval_heap.append(eval_el(0, op , op1.opn, op1.is_terminal, op2.opn , op2.is_terminal)); stack.top() = stack_el(eval_heap.size()-1, false); break; } case CQL_NOT: { PEGASUS_ASSERT(stack.size() >= 1); stack_el op1 = stack.top(); // generate Eval expression eval_heap.append(eval_el(0, op , op1.opn, op1.is_terminal, -1, true)); stack.top() = stack_el(eval_heap.size()-1, false); break; } case CQL_EQ: case CQL_NE: case CQL_LT: case CQL_LE: case CQL_GT: case CQL_GE: case CQL_ISA: case CQL_LIKE: { PEGASUS_ASSERT(_operands.size() >= 2); CQLExpression lhs = _operands[j++]; CQLExpression rhs = _operands[j++]; CQLSimplePredicate sp(lhs,rhs,_convertOpType(op)); terminal_heap.push(term_el(false, sp)); stack.push(stack_el(terminal_heap.size()-1, true)); break; } case CQL_IS_NULL: { PEGASUS_ASSERT(_operands.size() >= 1); CQLExpression expression = _operands[j++]; CQLSimplePredicate dummy(expression,IS_NULL); terminal_heap.push(term_el(false, dummy)); stack.push(stack_el(terminal_heap.size()-1, true)); break; } case CQL_IS_NOT_NULL: { PEGASUS_ASSERT(_operands.size() >= 1); CQLExpression expression = _operands[j++]; CQLSimplePredicate dummy(expression,IS_NOT_NULL); terminal_heap.push(term_el(false, dummy)); stack.push(stack_el(terminal_heap.size()-1, true)); break; } case CQL_NOOP: default: break; } } PEGASUS_ASSERT(stack.size() == 1); PEG_METHOD_EXIT(); } void Cql2Dnf::_pushNOTDown() { PEG_METHOD_ENTER(TRC_CQL, "Cql2Dnf::_pushNOTDown"); for (int i=eval_heap.size()-1; i >= 0; i--) { Boolean _found = false; // Order all operators, so that op1 > op2 for non-terminals // and terminals appear as second operand eval_heap[i].order(); // First solve the unary NOT operator if (eval_heap[i].op == CQL_NOT) { // This serves as the equivalent of an empty operator eval_heap[i].op = CQL_NOOP; // Substitute this expression in all higher order eval statements // so that this node becomes disconnected from the tree for (int j=eval_heap.size()-1; j > i;j--) { // Test first operand if ((!eval_heap[j].is_terminal1) && (eval_heap[j].opn1 == i)) eval_heap[j].assign_unary_to_first(eval_heap[i]); // Test second operand if ((!eval_heap[j].is_terminal2) && (eval_heap[j].opn2 == i)) eval_heap[j].assign_unary_to_second(eval_heap[i]); } // Test: Double NOT created by moving down if (eval_heap[i].mark) eval_heap[i].mark = false; else _found = true; // else indicate a pending NOT to be pushed down further } // Simple NOT created by moving down if (eval_heap[i].mark) { // Remove the mark, indicate a pending NOT to be pushed down // further and switch operators (AND / OR) eval_heap[i].mark=false; if (eval_heap[i].op == CQL_OR) eval_heap[i].op = CQL_AND; else if (eval_heap[i].op == CQL_AND) eval_heap[i].op = CQL_OR; // NOT operator is already ruled out _found = true; } // Push a pending NOT further down if (_found) { // First operand int j = eval_heap[i].opn1; if (eval_heap[i].is_terminal1) // Flip NOT mark terminal_heap[j].negate(); else eval_heap[j].mark = !(eval_heap[j].mark); //Second operand (if it exists) if ((j = eval_heap[i].opn2) >= 0) { if (eval_heap[i].is_terminal2) // Flip NOT mark terminal_heap[j].negate(); else eval_heap[j].mark = !(eval_heap[j].mark); } } } PEG_METHOD_EXIT(); } void Cql2Dnf::_factoring(void) { PEG_METHOD_ENTER(TRC_CQL, "Cql2Dnf::_factoring"); int i = 0,n = eval_heap.size(); //for (int i=eval_heap.size()-1; i >= 0; i--) while (i < n) { int _found = 0; int index = 0; // look for expressions (A | B) & C ---> A & C | A & B if (eval_heap[i].op == CQL_AND) { if (!eval_heap[i].is_terminal1) { index = eval_heap[i].opn1; // remember the index if (eval_heap[index].op == CQL_OR) _found = 1; } if ((_found == 0) && (!eval_heap[i].is_terminal2)) { index = eval_heap[i].opn2; // remember the index if (eval_heap[index].op == CQL_OR) _found = 2; } if (_found != 0) { //int u1,u1_t,u2,u2_t,u3,u3_t; stack_el s; if (_found == 1) s = eval_heap[i].getSecond(); else s = eval_heap[i].getFirst(); // insert two new expression before entry i eval_el evl; evl = eval_el(false, CQL_OR, i+1, false, i, false); if ((Uint32 )i < eval_heap.size()-1) eval_heap.insert(i+1, evl); else eval_heap.append(evl); eval_heap.insert(i+1, evl); for (int j=eval_heap.size()-1; j > i + 2; j--) { //eval_heap[j] = eval_heap[j-2]; // adjust pointers if ((!eval_heap[j].is_terminal1)&& (eval_heap[j].opn1 >= i)) eval_heap[j].opn1 += 2; if ((!eval_heap[j].is_terminal2)&& (eval_heap[j].opn2 >= i)) eval_heap[j].opn2 += 2; } n+=2; // increase size of array // generate the new expressions : new OR expression // first new AND expression eval_heap[i+1].mark = false; eval_heap[i+1].op = CQL_AND; eval_heap[i+1].setFirst(s); eval_heap[i+1].setSecond( eval_heap[index].getFirst()); eval_heap[i+1].order(); // second new AND expression eval_heap[i].mark = false; eval_heap[i].op = CQL_AND; eval_heap[i].setFirst(s); eval_heap[i].setSecond( eval_heap[index].getSecond()); eval_heap[i].order(); // mark the indexed expression as inactive //eval_heap[index].op = WQL_IS_TRUE; possible disconnects i--; } /* endif _found > 0 */ } /* endif found AND operator */ i++; // increase pointer } PEG_METHOD_EXIT(); } void Cql2Dnf::_strip_ops_operands(CQLPredicate& topLevel) { PEG_METHOD_ENTER(TRC_CQL, "Cql2Dnf::_strip_ops_operands"); // // depth first search for all operations and operands // extract operations and operands and store in respective arrays for later processing // _destruct(topLevel); if(topLevel.getInverted()){ _operations.append(CQL_NOT); } PEG_METHOD_EXIT(); } OperationType Cql2Dnf::_convertOpType(ExpressionOpType op){ switch(op){ case EQ: return CQL_EQ; case NE: return CQL_NE; case GT: return CQL_GT; case LT: return CQL_LT; case GE: return CQL_GE; case LE: return CQL_LE; case IS_NULL: return CQL_IS_NULL; case IS_NOT_NULL: return CQL_IS_NOT_NULL; case ISA: return CQL_ISA; case LIKE: return CQL_LIKE; default: return CQL_NOOP; } } ExpressionOpType Cql2Dnf::_convertOpType(OperationType op){ switch(op){ case CQL_EQ: return EQ; case CQL_NE: return NE; case CQL_GT: return GT; case CQL_LT: return LT; case CQL_GE: return GE; case CQL_LE: return LE; case CQL_IS_NULL: return IS_NULL; case CQL_IS_NOT_NULL: return IS_NOT_NULL; case CQL_ISA: return ISA; case CQL_LIKE: return LIKE; default: return EQ; // should never get here } PEGASUS_UNREACHABLE(return EQ;) // should never get here } void Cql2Dnf::_destruct(CQLPredicate& _p){ if(_p.isSimple()){ CQLSimplePredicate _sp = _p.getSimplePredicate(); _operations.append(_convertOpType(_sp.getOperation())); _operands.append(_sp.getLeftExpression()); if((_operations[_operations.size()-1] != CQL_IS_NULL) && (_operations[_operations.size()-1] != CQL_IS_NOT_NULL)) _operands.append(_sp.getRightExpression()); } else{ Array _preds = _p.getPredicates(); Array _boolops = _p.getOperators(); for(Uint32 i=0;i<_preds.size();i++){ _destruct(_preds[i]); if(_preds[i].getInverted()){ _operations.append(CQL_NOT); } if(i > 0){ if(_boolops[i-1] == AND){ _operations.append(CQL_AND); } if(_boolops[i-1] == OR){ _operations.append(CQL_OR); } } } } } void Cql2Dnf::_construct(){ PEG_METHOD_ENTER(TRC_CQL, "Cql2Dnf::_construct"); // // Each eval_el on the eval heap contains all the information needed to make a CQLPredicate. // We will build a CQLPredicate for every element in the eval heap. So there is a 1 to 1 correspondence // between elements in the eval heap and elements in the CQLPredicate array used below. // The first eval_el on the eval heap will always contain at least one terminal if the operation is a NOT // or two terminals if the operation is AND or OR. We are guaranteed to build a CQLPredicate from the first // position in the eval_heap array. // // The key to the algorithm is the isterminalX flag. When set to true, we go to the // term_heap and get the CQLSimplePredicate. When set to false, we go to the _preds array below // and get the CQLPredicate. Since there is a 1 - 1 correspondence, as explained above, the index // referred to by eval.opn1 or eval.opn2 is valid into the _preds array. // // For ANDs and ORs, we need two operands, as explained above, we get those operands // from either the term_heap or the _preds array. For NOTs, we need only 1 operand, and that // comes from either the term_heap or the _preds array. // // When finished, the last element in the _preds array contains the top level CQLPredicate (the rebuilt tree) // // Example: a=b^(!c=d v e=f) // If the current eval_heap looks like: // 0,NOT,1,True,-1,True [index = 0] // 0,OR,2,True,0,False [index = 1] // 0,AND,1,False,0,True [index = 2] // // And the current term_heap looks like: // CQLSimplePredicate(a=b) [index = 0] // CQLSimplePredicate(c=d) [index = 1] // CQLSimplePredicate(e=f) [index = 0] // // The _preds array at the end would look like: // CQLPredicate(!c==d) [index = 0] // CQLPredicate(e==f v !c==d) [index = 1] // CQLPredicate((e==f v !c==d) ^ a==b) [index = 2] (the rebuilt tree) // if(eval_heap.size() > 0){ Array _preds; CQLPredicate pred; for(Uint32 i=0;i get P1s predicates (which should all be simple) // -> append its first predicate to P along with the operator passed into us // -> at this point we have P = A AND B OR C // -> then go through P1s remaining predicates and append them and P1s operators to P // -> when finished, we have P = A AND B OR C AND D INSTEAD of having P = A AND B OR P1 where P1 is a complex predicate // if(!p.isSimple()){ Array preds = p.getPredicates(); Array ops = p.getOperators(); for(Uint32 i=0;i