/* This file is part of VoltDB.
* Copyright (C) 2008-2017 VoltDB Inc.
*
* 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 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.
*/
package org.voltdb.expressions;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Stack;
import java.util.Vector;
import junit.framework.TestCase;
import org.voltdb.VoltType;
import org.voltdb.types.ExpressionType;
import org.voltdb.types.TimestampType;
public class TestExpressionUtil extends TestCase {
/**
* Used for traversing trees.
*
*/
static abstract class TestExpressionTreeWalker {
/**
* Expression node stack
*/
private final Stack<AbstractExpression> m_stack = new Stack<AbstractExpression>();
/**
* How deep we are in the tree
*/
private int m_depth = -1;
/**
* Depth first traveral
* @param exp
*/
public final void traverse(AbstractExpression exp) {
m_stack.push(exp);
m_depth++;
if (exp.getLeft() != null) {
traverse(exp.getLeft());
}
if (exp.getRight() != null) {
traverse(exp.getRight());
}
AbstractExpression check_exp = m_stack.pop();
assert(exp.equals(check_exp));
callback(exp);
m_depth--;
}
/**
* Returns the parent of the current node in callback()
* @return
*/
protected final AbstractExpression getParent() {
AbstractExpression ret = null;
if (!m_stack.isEmpty()) ret = m_stack.peek();
return ret;
}
/**
* Returns the depth of the current callback() invocation
* @return
*/
protected final int getDepth() {
return m_depth;
}
/**
* This method will be called after the walker has explored a node's children
* @param exp the Expression object to perform an operation on
*/
public abstract void callback(AbstractExpression exp);
}
//
// AND
// / \
// EQUAL NOT
// / \ |
// P T C
//
protected static final AbstractExpression ROOT_EXP = new ConjunctionExpression(ExpressionType.CONJUNCTION_AND);
protected static final AbstractExpression CHILD_EXPS[] = { new ComparisonExpression(ExpressionType.COMPARE_EQUAL),
new ComparisonExpression(ExpressionType.OPERATOR_NOT),
new ParameterValueExpression(),
new TupleValueExpression(),
new ConstantValueExpression()
};
static {
ROOT_EXP.setLeft(CHILD_EXPS[0]);
ROOT_EXP.setRight(CHILD_EXPS[1]);
CHILD_EXPS[0].setLeft(CHILD_EXPS[2]);
CHILD_EXPS[0].setRight(CHILD_EXPS[3]);
CHILD_EXPS[1].setLeft(CHILD_EXPS[4]);
//ExpressionUtil.generateIds(ROOT_EXP);
} // STATIC
// ------------------------------------------------------------------
// COMPARISON METHODS
// ------------------------------------------------------------------
protected static void compareExpressionTrees(AbstractExpression out_exp, AbstractExpression in_exp) {
//
// Make sure that compacted tree is exactly the same as the original
// sub-tree that should have been preserved
//
final Vector<AbstractExpression> orig_list = new Vector<AbstractExpression>();
new TestExpressionTreeWalker() {
@Override
public void callback(AbstractExpression exp) {
orig_list.add(exp);
}
}.traverse(out_exp);
assertFalse(orig_list.isEmpty());
new TestExpressionTreeWalker() {
@Override
public void callback(AbstractExpression exp) {
assertFalse(orig_list.isEmpty());
AbstractExpression pop_exp = orig_list.remove(0);
TestExpressionUtil.compareExpressions(pop_exp, exp);
}
}.traverse(in_exp);
}
protected static void compareExpressions(AbstractExpression out_exp, AbstractExpression in_exp) {
//
// ID
//
/*if (out_exp.getId() != null) {
assertNotNull(in_exp.getId());
if (!out_exp.getId().equals(in_exp.getId())) {
System.err.println("OUT: " + out_exp);
System.err.println("IN: " + in_exp);
}
assertEquals(out_exp.getId(), in_exp.getId());
} else {
assertNull(in_exp.getId());
}*/
//
// LEFT & RIGHT
//
assertEquals((out_exp.getLeft() == null), (in_exp.getLeft() == null));
assertEquals((out_exp.getRight() == null), (in_exp.getRight() == null));
//
// VALUE TYPE
//
if (out_exp.getValueType() != in_exp.getValueType()) {
System.err.println("OUT: " + out_exp.getValueType());
System.err.println("IN: " + in_exp.getValueType());
}
assertEquals(out_exp.getValueType(), in_exp.getValueType());
//
// Specialized Checks
//
switch (out_exp.getExpressionType()) {
case VALUE_CONSTANT: {
ConstantValueExpression out_const_exp = (ConstantValueExpression)out_exp;
ConstantValueExpression in_const_exp = (ConstantValueExpression)in_exp;
assertEquals(out_const_exp.getValue(), in_const_exp.getValue());
break;
}
case VALUE_PARAMETER:
ParameterValueExpression out_param_exp = (ParameterValueExpression)out_exp;
ParameterValueExpression in_param_exp = (ParameterValueExpression)in_exp;
assertEquals(out_param_exp.getParameterIndex(), in_param_exp.getParameterIndex());
break;
case VALUE_TUPLE:
TupleValueExpression out_tuple_exp = (TupleValueExpression)out_exp;
TupleValueExpression in_tuple_exp = (TupleValueExpression)in_exp;
assertEquals(out_tuple_exp.getColumnIndex(), in_tuple_exp.getColumnIndex());
break;
default:
break;
} // SWITCH
return;
}
// ------------------------------------------------------------------
// TEST CASES
// ------------------------------------------------------------------
/**
* Clone Sub-Tree
*/
public void testClone() {
AbstractExpression cloned_exp = null;
try {
if (ROOT_EXP != null) {
cloned_exp = ROOT_EXP.clone();
}
} catch (Exception ex) {
ex.printStackTrace();
}
assertNotNull(cloned_exp);
//
// First build our lists of information about each node in the original tree
// It is assumed that the tree will be traversed in the same order
//
final ArrayList<AbstractExpression> orig_exps = new ArrayList<AbstractExpression>();
new TestExpressionTreeWalker() {
@Override
public void callback(AbstractExpression exp) {
orig_exps.add(exp);
}
}.traverse(ROOT_EXP);
//
// Then walk through the cloned tree and make sure the objects are different
// but the information is the same
//
new TestExpressionTreeWalker() {
@Override
public void callback(AbstractExpression exp) {
assertFalse(orig_exps.isEmpty());
AbstractExpression orig_exp = orig_exps.remove(0);
//
// We want to make sure that the cloned Expression doesn't
// share components (having the same object identity).
// This COULD still fail for wrapped primitive types
// IF Expressions ever reference any and
// IF the cloning method is not (somehow) working to avoid
// pointers into a system-provided wrapped primitive cache.
assertFalse(orig_exp == exp);
//
// Use our general comparison method to check other things
//
TestExpressionUtil.compareExpressions(orig_exp, exp);
}
}.traverse(cloned_exp);
}
/**
*
*
*/
public void testCombine() {
//
// We create a bunch of individual ComparisonExpression trees and we then
// combine them into a single tree created with AND conjunctions
//
int num_of_subtrees = 5;
final List<AbstractExpression> combine_exps = new ArrayList<AbstractExpression>();
final Map<AbstractExpression, AbstractExpression> combine_exps_left = new HashMap<AbstractExpression, AbstractExpression>();
final Map<AbstractExpression, AbstractExpression> combine_exps_right = new HashMap<AbstractExpression, AbstractExpression>();
for (int ctr = 0; ctr < num_of_subtrees; ctr++) {
AbstractExpression exps[] = { new ComparisonExpression(ExpressionType.COMPARE_EQUAL),
new ParameterValueExpression(),
new TupleValueExpression()
};
exps[0].setLeft(exps[1]);
exps[0].setRight(exps[2]);
//ExpressionUtil.generateIds(exps[0]);
combine_exps.add(exps[0]);
combine_exps_left.put(exps[0], exps[1]);
combine_exps_right.put(exps[0], exps[2]);
} // FOR
AbstractExpression combined_exp = null;
try {
combined_exp = ExpressionUtil.combinePredicates(combine_exps);
} catch (Exception ex) {
ex.printStackTrace();
}
assertNotNull(combined_exp);
assertEquals(combined_exp.getExpressionType(), ExpressionType.CONJUNCTION_AND);
//System.err.println(combined_exp.toString(true));
//
// Checking whether this worked is a bit tricky because the ordering of the may
// be different if the implementation changges. So we just need to check to make
// sure that all of our sub-trees are contained within the new tree and that their
// structure has not changed
//
TestExpressionTreeWalker treeWalker = new TestExpressionTreeWalker() {
@Override
public void callback(AbstractExpression exp) {
//
// This node was in our original tree
//
if (combine_exps.contains(exp)) {
assertTrue(combine_exps_left.containsKey(exp));
TestExpressionUtil.compareExpressions(exp.getLeft(), combine_exps_left.get(exp));
assertTrue(combine_exps_right.containsKey(exp));
TestExpressionUtil.compareExpressions(exp.getRight(), combine_exps_right.get(exp));
//
// Make sure our parent is a CONJUNCTION_AND expression node
//
assertNotNull(this.getParent());
assertEquals(this.getParent().getExpressionType(), ExpressionType.CONJUNCTION_AND);
//
// If this is a CONJUNCTION_AND that we added, make sure that both of its
// children are not null
//
} else if (exp.getExpressionType() == ExpressionType.CONJUNCTION_AND) {
assertNotNull(exp.getLeft());
assertNotNull(exp.getRight());
}
}
};
treeWalker.traverse(combined_exp);
//
// Test variadic combine
//
final List<AbstractExpression> combine_exps1 = new ArrayList<AbstractExpression>();
final List<AbstractExpression> combine_exps2 = new ArrayList<AbstractExpression>();
final List<AbstractExpression> combine_exps3 = new ArrayList<AbstractExpression>();
final List<AbstractExpression> combine_exps4 = null;
for (int ctr = 0; ctr < num_of_subtrees/2; ctr++) {
combine_exps1.add(combine_exps.get(ctr));
}
for (int ctr = num_of_subtrees/2; ctr < num_of_subtrees; ctr++) {
combine_exps2.add(combine_exps.get(ctr));
}
AbstractExpression var_combined_exp = null;
try {
var_combined_exp = ExpressionUtil.combinePredicates(combine_exps1, combine_exps2, combine_exps3, combine_exps4);
} catch (Exception ex) {
ex.printStackTrace();
}
assertNotNull(var_combined_exp);
assertEquals(var_combined_exp.getExpressionType(), ExpressionType.CONJUNCTION_AND);
treeWalker.traverse(var_combined_exp);
}
// This is basically just a check that the rules in
// VoltTypeUtil.determineImplicitCasting() push up through the aggregate
// expression properly. We'll just do a few of the corner cases and
// call it good.
public void testAssignOutputValueTypesRecursivelyForAggregateAvg()
{
AbstractExpression root =
new AggregateExpression(ExpressionType.AGGREGATE_AVG);
AbstractExpression op = new TupleValueExpression();
root.setLeft(op);
// Simple tuple value type gets pushed through
op.setValueType(VoltType.FLOAT);
ExpressionUtil.finalizeValueTypes(root);
assertEquals(VoltType.FLOAT, root.getValueType());
op.setValueType(VoltType.INTEGER);
ExpressionUtil.finalizeValueTypes(root);
assertEquals(VoltType.INTEGER, root.getValueType());
op.setValueType(VoltType.DECIMAL);
ExpressionUtil.finalizeValueTypes(root);
assertEquals(VoltType.DECIMAL, root.getValueType());
op = new OperatorExpression();
root.setLeft(op);
AbstractExpression left = new TupleValueExpression();
AbstractExpression right = new TupleValueExpression();
op.setLeft(left);
op.setRight(right);
// FLOAT + int type gets promoted to FLOAT
left.setValueType(VoltType.FLOAT);
right.setValueType(VoltType.INTEGER);
ExpressionUtil.finalizeValueTypes(root);
assertEquals(VoltType.FLOAT, root.getValueType());
// random INT types get promoted to BIGINT
left.setValueType(VoltType.TINYINT);
right.setValueType(VoltType.INTEGER);
ExpressionUtil.finalizeValueTypes(root);
assertEquals(VoltType.BIGINT, root.getValueType());
// DECIMAL works, at least
left.setValueType(VoltType.DECIMAL);
right.setValueType(VoltType.DECIMAL);
ExpressionUtil.finalizeValueTypes(root);
assertEquals(VoltType.DECIMAL, root.getValueType());
}
/** Base case test of NUMERIC literal processing */
public void testAssignLiteralConstantTypes()
{
AbstractExpression lit_dec;
AbstractExpression dec_lit;
AbstractExpression lit;
AbstractExpression dec;
AbstractExpression bint;
// convert NUMERIC to DECIMAL right/left
lit = new ConstantValueExpression();
lit.m_valueType = VoltType.NUMERIC;
lit.m_valueSize = VoltType.NUMERIC.getLengthInBytesForFixedTypes();
dec = new ConstantValueExpression();
dec.m_valueType = VoltType.DECIMAL;
dec.m_valueSize = VoltType.DECIMAL.getLengthInBytesForFixedTypes();
lit_dec = new OperatorExpression(ExpressionType.OPERATOR_PLUS, lit, dec);
lit_dec.normalizeOperandTypes_recurse();
assertEquals(lit.m_valueType, VoltType.DECIMAL);
assertEquals(lit.m_valueSize, VoltType.DECIMAL.getLengthInBytesForFixedTypes());
assertEquals(dec.m_valueType, VoltType.DECIMAL);
assertEquals(dec.m_valueSize, VoltType.DECIMAL.getLengthInBytesForFixedTypes());
// convert NUMERIC to DECIMAL (left/right)
lit = new ConstantValueExpression();
lit.m_valueType = VoltType.NUMERIC;
lit.m_valueSize = VoltType.NUMERIC.getLengthInBytesForFixedTypes();
dec = new ConstantValueExpression();
dec.m_valueType = VoltType.DECIMAL;
dec.m_valueSize = VoltType.DECIMAL.getLengthInBytesForFixedTypes();
dec_lit = new OperatorExpression(ExpressionType.OPERATOR_DIVIDE, dec, lit);
dec_lit.normalizeOperandTypes_recurse();
assertEquals(lit.m_valueType, VoltType.DECIMAL);
assertEquals(lit.m_valueSize, VoltType.DECIMAL.getLengthInBytesForFixedTypes());
assertEquals(dec.m_valueType, VoltType.DECIMAL);
assertEquals(dec.m_valueSize, VoltType.DECIMAL.getLengthInBytesForFixedTypes());
// convert numeric to float
lit = new ConstantValueExpression();
lit.m_valueType = VoltType.NUMERIC;
lit.m_valueSize = VoltType.NUMERIC.getLengthInBytesForFixedTypes();
bint = new ConstantValueExpression();
bint.m_valueType = VoltType.BIGINT;
bint.m_valueSize = VoltType.BIGINT.getLengthInBytesForFixedTypes();
AbstractExpression lit_bint =
new OperatorExpression(ExpressionType.OPERATOR_MINUS, lit, bint);
lit_bint.normalizeOperandTypes_recurse();
assertEquals(lit.m_valueType, VoltType.DECIMAL);
assertEquals(lit.m_valueSize, VoltType.DECIMAL.getLengthInBytesForFixedTypes());
assertEquals(bint.m_valueType, VoltType.BIGINT);
assertEquals(bint.m_valueSize, VoltType.BIGINT.getLengthInBytesForFixedTypes());
// test a larger tree
lit = new ConstantValueExpression();
lit.m_valueType = VoltType.NUMERIC;
lit.m_valueSize = VoltType.NUMERIC.getLengthInBytesForFixedTypes();
bint = new ConstantValueExpression();
bint.m_valueType = VoltType.DECIMAL;
bint.m_valueSize = VoltType.DECIMAL.getLengthInBytesForFixedTypes();
lit_bint = new OperatorExpression(ExpressionType.OPERATOR_MINUS, lit, bint);
AbstractExpression root = new OperatorExpression(ExpressionType.OPERATOR_MULTIPLY,
lit_bint, new TupleValueExpression());
root.normalizeOperandTypes_recurse();
assertEquals(lit.m_valueType, VoltType.DECIMAL);
assertEquals(lit.m_valueSize, VoltType.DECIMAL.getLengthInBytesForFixedTypes());
assertEquals(bint.m_valueType, VoltType.DECIMAL);
assertEquals(bint.m_valueSize, VoltType.DECIMAL.getLengthInBytesForFixedTypes());
}
public void testSetOutputTypeForInsertExpressionWithCveAndTimestamp() throws Exception
{
ConstantValueExpression cve = new ConstantValueExpression();
cve.setValue("4000000000");
cve.setValueSize(8);
cve.setValueType(VoltType.BIGINT);
cve.refineValueType(VoltType.TIMESTAMP, 8);
assertEquals(VoltType.TIMESTAMP, cve.getValueType());
assertEquals(8, cve.getValueSize());
cve = new ConstantValueExpression();
cve.setValue("400000000");
cve.setValueSize(4);
cve.setValueType(VoltType.INTEGER);
cve.refineValueType(VoltType.TIMESTAMP, 8);
assertEquals(VoltType.TIMESTAMP, cve.getValueType());
assertEquals(8, cve.getValueSize());
cve = new ConstantValueExpression();
cve.setValue("4000");
cve.setValueSize(2);
cve.setValueType(VoltType.SMALLINT);
cve.refineValueType(VoltType.TIMESTAMP, 8);
assertEquals(VoltType.TIMESTAMP, cve.getValueType());
assertEquals(8, cve.getValueSize());
cve = new ConstantValueExpression();
cve.setValue("40");
cve.setValueSize(1);
cve.setValueType(VoltType.TINYINT);
cve.refineValueType(VoltType.TIMESTAMP, 8);
assertEquals(VoltType.TIMESTAMP, cve.getValueType());
assertEquals(8, cve.getValueSize());
}
public void testSetOutputTypeForInsertExpressionWithLiteralStringDates() throws Exception
{
ConstantValueExpression cve = new ConstantValueExpression();
TimestampType ts = new TimestampType(999999999);
cve.setValue(ts.toString());
cve.setValueType(VoltType.STRING);
cve.setValueSize(ts.toString().length());
cve.refineValueType(VoltType.TIMESTAMP, 8);
assertEquals(VoltType.TIMESTAMP, cve.getValueType());
assertEquals("999999999", cve.m_value);
}
// Test interesting cases of indexable expressions and the query expressions they might match.
// Technically, this tests AbstractExpression methods, not ExpressionUtil methods,
// but do we really need to launch yet another JUnit suite? I think not.
public void testIndexedExpressionBindings() throws Exception
{
List<AbstractExpression> arguments;
TupleValueExpression extraColumn = new TupleValueExpression();
extraColumn.setTableName("T1");
extraColumn.setColumnName("extra");
ConstantValueExpression constant = new ConstantValueExpression();
constant.setValue("42");
ConstantValueExpression otherConstant = new ConstantValueExpression();
otherConstant.setValue("44");
// Interesting indexable expressions include:
// A) simple column, T1.A
TupleValueExpression exprA = new TupleValueExpression();
exprA.setTableName("T1");
exprA.setColumnName("A");
// B) math with columns, (T1.extra * T1.A)
OperatorExpression exprB = new OperatorExpression(ExpressionType.OPERATOR_MULTIPLY, extraColumn, exprA);
// C) a function of a column, ( functionName(T1.A) )
FunctionExpression exprC = new FunctionExpression();
exprC.setAttributes("functionName", "yesFunctionName", 42);
arguments = new ArrayList<AbstractExpression>();
arguments.add(exprA);
exprC.setArgs(arguments);
// D) math with a column and a constant ( 42 * T1.A )
OperatorExpression exprD = new OperatorExpression(ExpressionType.OPERATOR_MULTIPLY, constant, exprA);
// E) a function of a column and constants, ( anotherFunctionName( T1.A, 42, 44 ) )
FunctionExpression exprE = new FunctionExpression();
exprE.setAttributes("anotherFunctionName", "yesAnotherWhyNot", 44); // not 42, not that it much matters
arguments = new ArrayList<AbstractExpression>();
arguments.add(exprA);
arguments.add(constant);
arguments.add(otherConstant);
exprE.setArgs(arguments);
List<AbstractExpression> result;
//
// Interesting matches for these include:
//
// Each of A through E should match an identical "cloned" expression,
// with no "parameter binding" caveat.
AbstractExpression likeA = exprA.clone();
result = likeA.bindingToIndexedExpression(exprA);
assertNotNull(result);
assertTrue(result.isEmpty());
AbstractExpression likeB = exprB.clone();
result = likeB.bindingToIndexedExpression(exprB);
assertNotNull(result);
assertTrue(result.isEmpty());
AbstractExpression likeC = exprC.clone();
result = likeC.bindingToIndexedExpression(exprC);
assertNotNull(result);
assertTrue(result.isEmpty());
AbstractExpression likeD = exprD.clone();
result = likeD.bindingToIndexedExpression(exprD);
assertNotNull(result);
assertTrue(result.isEmpty());
AbstractExpression likeE = exprE.clone();
result = likeE.bindingToIndexedExpression(exprE);
assertNotNull(result);
assertTrue(result.isEmpty());
// Each of D and E should match one-off expressions, differing only in that one or more
// of their constants are replaced with Parameters having those identical constants as
// their "original values".
// Said parameters should (all) be listed in the resulting "parameter binding" caveats.
ParameterValueExpression paramifiedConstant = new ParameterValueExpression();
paramifiedConstant.setOriginalValue(constant);
ParameterValueExpression otherParamifiedConstant = new ParameterValueExpression();
otherParamifiedConstant.setOriginalValue(otherConstant);
// D) math with a column and a constant ( 42 * T1.A ) works for ( ? * T1.A ) w/ ? == 42
AbstractExpression paramifiedD = exprD.clone();
paramifiedD.setLeft(paramifiedConstant);
result = paramifiedD.bindingToIndexedExpression(exprD);
assertNotNull(result);
assertFalse(result.isEmpty());
assertEquals(result.get(0), paramifiedConstant);
// E) a function of a column and constants, ( anotherFunctionName( T1.A, 42, 44 ) )
// works for ( anotherFunctionName( T1.A, ?, 44 ) ) where ? = 42 ...
FunctionExpression paramifiedE = (FunctionExpression) exprE.clone();
arguments = new ArrayList<AbstractExpression>();
arguments.add(exprA);
arguments.add(paramifiedConstant);
arguments.add(otherConstant);
paramifiedE.setArgs(arguments);
result = paramifiedE.bindingToIndexedExpression(exprE);
assertNotNull(result);
assertFalse(result.isEmpty());
assertEquals(result.get(0), paramifiedConstant);
// works for ( anotherFunctionName( T1.A, 42, ? ) ) where ? = 44
FunctionExpression reparamifiedE = (FunctionExpression) exprE.clone();
arguments = new ArrayList<AbstractExpression>();
arguments.add(exprA);
arguments.add(constant);
arguments.add(otherParamifiedConstant);
reparamifiedE.setArgs(arguments);
result = reparamifiedE.bindingToIndexedExpression(exprE);
assertNotNull(result);
assertFalse(result.isEmpty());
assertEquals(result.get(0), otherParamifiedConstant);
// works for ( anotherFunctionName( T1.A, ?, ? ) ) where ?, ? = 42, 44
FunctionExpression everSoParamifiedE = (FunctionExpression) exprE.clone();
arguments = new ArrayList<AbstractExpression>();
arguments.add(exprA);
arguments.add(paramifiedConstant);
arguments.add(otherParamifiedConstant);
everSoParamifiedE.setArgs(arguments);
result = everSoParamifiedE.bindingToIndexedExpression(exprE);
assertNotNull(result);
assertFalse(result.isEmpty());
assertEquals(result.size(), 2);
assertEquals(result.get(0), paramifiedConstant);
assertEquals(result.get(1), otherParamifiedConstant);
// Done positive match testing.
ConstantValueExpression neitherConstant = new ConstantValueExpression();
neitherConstant.setValue("86");
ParameterValueExpression paramifiedNeitherConstant = new ParameterValueExpression();
paramifiedNeitherConstant.setOriginalValue(neitherConstant);
ParameterValueExpression actualUserProvidedParameter = new ParameterValueExpression();
ParameterValueExpression otherUserProvidedParameter = new ParameterValueExpression();
//
// Interesting non-matches for these indexable expressions include:
//
// Each of A through E should fail to match a "clone" of any of the others,
// all way too dissimilar.
result = likeA.bindingToIndexedExpression(exprB);
assertNull(result);
result = likeA.bindingToIndexedExpression(exprC);
assertNull(result);
result = likeA.bindingToIndexedExpression(exprD);
assertNull(result);
result = likeA.bindingToIndexedExpression(exprE);
assertNull(result);
result = likeB.bindingToIndexedExpression(exprA);
assertNull(result);
result = likeB.bindingToIndexedExpression(exprC);
assertNull(result);
result = likeB.bindingToIndexedExpression(exprD);
assertNull(result);
result = likeB.bindingToIndexedExpression(exprE);
assertNull(result);
result = likeC.bindingToIndexedExpression(exprA);
assertNull(result);
result = likeC.bindingToIndexedExpression(exprB);
assertNull(result);
result = likeC.bindingToIndexedExpression(exprD);
assertNull(result);
result = likeC.bindingToIndexedExpression(exprE);
assertNull(result);
result = likeD.bindingToIndexedExpression(exprA);
assertNull(result);
result = likeD.bindingToIndexedExpression(exprB);
assertNull(result);
result = likeD.bindingToIndexedExpression(exprC);
assertNull(result);
result = likeD.bindingToIndexedExpression(exprE);
assertNull(result);
result = likeE.bindingToIndexedExpression(exprA);
assertNull(result);
result = likeE.bindingToIndexedExpression(exprB);
assertNull(result);
result = likeE.bindingToIndexedExpression(exprC);
assertNull(result);
result = likeE.bindingToIndexedExpression(exprD);
assertNull(result);
// Each of D and E should fail to match "near misses", one-offs of their parameterized selves,
// specifically when the parameter has a different "original value" than the constant in the
// indexable expression OR has no "original value" -- like a user-provided parameter to a
// compiled statement.
// D) math with a column and a constant ( 42 * T1.A ) for ( ? * T1.A ) w/ ? == 86
AbstractExpression crossParamifiedD = exprD.clone();
crossParamifiedD.setLeft(paramifiedNeitherConstant);
result = crossParamifiedD.bindingToIndexedExpression(exprD);
assertNull(result);
// D) math with a column and a constant ( 42 * T1.A ) for ( ? * T1.A ) w/ ? == ???!
AbstractExpression userParamifiedD = exprD.clone();
userParamifiedD.setLeft(actualUserProvidedParameter);
result = userParamifiedD.bindingToIndexedExpression(exprD);
assertNull(result);
// E) a function of a column and constants, ( anotherFunctionName( T1.A, 42, 44 ) )
// for ( anotherFunctionName( T1.A, ?, ? ) ) where ?, ? = 42, 86
FunctionExpression crossParamifiedE = (FunctionExpression) exprE.clone();
arguments = new ArrayList<AbstractExpression>();
arguments.add(exprA);
arguments.add(paramifiedConstant);
arguments.add(paramifiedNeitherConstant);
crossParamifiedE.setArgs(arguments);
result = crossParamifiedE.bindingToIndexedExpression(exprE);
assertNull(result);
// for ( anotherFunctionName( T1.A, ?, ? ) ) where ?, ? = ???, ??? ...
FunctionExpression userParamifiedE = (FunctionExpression) exprE.clone();
arguments = new ArrayList<AbstractExpression>();
arguments.add(exprA);
arguments.add(actualUserProvidedParameter);
arguments.add(otherUserProvidedParameter);
userParamifiedE.setArgs(arguments);
result = userParamifiedE.bindingToIndexedExpression(exprE);
assertNull(result);
// Each of A through E should fail to match other one-offs of themselves
// (i.e. wrong column, wrong constant, wrong function, wrong math op).
// A) wrong column
TupleValueExpression notTheColumn = new TupleValueExpression();
notTheColumn.setTableName("T1");
notTheColumn.setColumnName("notA");
result = notTheColumn.bindingToIndexedExpression(exprA);
assertNull(result);
// B) wrong math with columns
OperatorExpression notTheOperator = new OperatorExpression(ExpressionType.OPERATOR_DIVIDE, extraColumn, exprA);
result = notTheOperator.bindingToIndexedExpression(exprB);
assertNull(result);
// C) wrong function of a column
FunctionExpression neitherFunction = new FunctionExpression();
neitherFunction.setAttributes("notTheFunctionName", "noNotTheFunctionName", 86); // 86 is neither 42 nor 44.
arguments = new ArrayList<AbstractExpression>();
arguments.add(exprA);
neitherFunction.setArgs(arguments);
result = neitherFunction.bindingToIndexedExpression(exprC);
assertNull(result);
// D) right math op with a wrong column and a right constant
notTheOperator = new OperatorExpression(ExpressionType.OPERATOR_MULTIPLY, constant, extraColumn);
result = notTheOperator.bindingToIndexedExpression(exprD);
assertNull(result);
// E) a right function of a column but not enough arguments.
FunctionExpression notTheArgs = (FunctionExpression) exprE.clone();
arguments = new ArrayList<AbstractExpression>();
arguments.add(exprA);
arguments.add(constant);
notTheArgs.setArgs(arguments);
result = notTheArgs.bindingToIndexedExpression(exprE);
assertNull(result);
// E) or too many arguments.
arguments = new ArrayList<AbstractExpression>();
arguments.add(exprA);
arguments.add(constant);
arguments.add(exprA);
arguments.add(constant);
notTheArgs.setArgs(arguments);
result = notTheArgs.bindingToIndexedExpression(exprE);
assertNull(result);
}
// Test various expressions for NULL-rejection.
public void testIsNullRejectingExpression() throws Exception
{
{
// Test "IS NULL (T.C)" not NULL-rejecting
TupleValueExpression tve = new TupleValueExpression();
tve.setTableName("T");
tve.setColumnName("C");
OperatorExpression expr = new OperatorExpression(ExpressionType.OPERATOR_IS_NULL, tve, null);
assertTrue(!ExpressionUtil.isNullRejectingExpression(expr, "T"));
}
{
// Test "IS NOT NULL (T.C)" is NULL-rejecting
TupleValueExpression tve = new TupleValueExpression();
tve.setTableName("T");
tve.setColumnName("C");
OperatorExpression subexpr = new OperatorExpression(ExpressionType.OPERATOR_IS_NULL, tve, null);
OperatorExpression expr = new OperatorExpression(ExpressionType.OPERATOR_NOT, subexpr, null);
assertTrue(ExpressionUtil.isNullRejectingExpression(expr, "T"));
// Wrong table
assertTrue(!ExpressionUtil.isNullRejectingExpression(expr, "TT"));
}
{
// Test "T1.C > T2.C" is NULL-rejecting
TupleValueExpression tve1 = new TupleValueExpression();
tve1.setTableName("T1");
tve1.setColumnName("C");
TupleValueExpression tve2 = new TupleValueExpression();
tve2.setTableName("T2");
tve2.setColumnName("C");
OperatorExpression expr = new OperatorExpression(ExpressionType.COMPARE_GREATERTHAN, tve1, tve2);
assertTrue(ExpressionUtil.isNullRejectingExpression(expr, "T1"));
// Wrong table
assertTrue(!ExpressionUtil.isNullRejectingExpression(expr, "T"));
}
{
// Test "T1.C IN (1,2)" is NULL-rejecting
TupleValueExpression tve1 = new TupleValueExpression();
tve1.setTableName("T1");
tve1.setColumnName("C");
VectorValueExpression vve = new VectorValueExpression();
InComparisonExpression ine = new InComparisonExpression();
ine.m_left = tve1;
ine.m_right = vve;
assertTrue(ExpressionUtil.isNullRejectingExpression(ine, "T1"));
// Wrong table
assertTrue(!ExpressionUtil.isNullRejectingExpression(ine, "T"));
}
{
// Test AND expressions
// Test "T1.C > T2.C AND T2.B IS NULL " is NULL-rejecting
TupleValueExpression tve1 = new TupleValueExpression();
tve1.setTableName("T1");
tve1.setColumnName("C");
TupleValueExpression tve2 = new TupleValueExpression();
tve2.setTableName("T2");
tve2.setColumnName("C");
OperatorExpression expr1 = new OperatorExpression(ExpressionType.COMPARE_GREATERTHAN, tve1, tve2);
TupleValueExpression tve3 = new TupleValueExpression();
tve2.setTableName("T2");
tve2.setColumnName("B");
OperatorExpression expr2 = new OperatorExpression(ExpressionType.OPERATOR_IS_NULL, tve3, null);
OperatorExpression expr = new OperatorExpression(ExpressionType.CONJUNCTION_AND, expr1, expr2);
assertTrue(ExpressionUtil.isNullRejectingExpression(expr, "T2"));
assertTrue(ExpressionUtil.isNullRejectingExpression(expr, "T1"));
// Wrong table
assertTrue(!ExpressionUtil.isNullRejectingExpression(expr, "T"));
// Test "T1.C IS NULL AND T2.B IS NULL " is NULL-rejecting
OperatorExpression expr3 = new OperatorExpression(ExpressionType.OPERATOR_IS_NULL, tve1, null);
expr = new OperatorExpression(ExpressionType.CONJUNCTION_AND, expr3, expr2);
assertTrue(!ExpressionUtil.isNullRejectingExpression(expr, "T2"));
assertTrue(!ExpressionUtil.isNullRejectingExpression(expr, "T1"));
// Wrong table
assertTrue(!ExpressionUtil.isNullRejectingExpression(expr, "T"));
}
{
// Test OR expressions
// Test "T1.C > T2.C OR T2.B IS NULL " is not NULL-rejecting
TupleValueExpression tve1 = new TupleValueExpression();
tve1.setTableName("T1");
tve1.setColumnName("C");
TupleValueExpression tve2 = new TupleValueExpression();
tve2.setTableName("T2");
tve2.setColumnName("C");
OperatorExpression expr1 = new OperatorExpression(ExpressionType.COMPARE_GREATERTHAN, tve1, tve2);
TupleValueExpression tve3 = new TupleValueExpression();
tve3.setTableName("T2");
tve3.setColumnName("B");
OperatorExpression expr2 = new OperatorExpression(ExpressionType.OPERATOR_IS_NULL, tve3, null);
OperatorExpression expr = new OperatorExpression(ExpressionType.CONJUNCTION_OR, expr1, expr2);
assertTrue(!ExpressionUtil.isNullRejectingExpression(expr, "T2"));
assertTrue(!ExpressionUtil.isNullRejectingExpression(expr, "T1"));
// Wrong table
assertTrue(!ExpressionUtil.isNullRejectingExpression(expr, "T"));
// Test "T1.C > T2.C OR T2.B > T1.C " is NULL-rejecting
OperatorExpression expr3 = new OperatorExpression(ExpressionType.COMPARE_GREATERTHAN, tve3, tve1);
expr = new OperatorExpression(ExpressionType.CONJUNCTION_OR, expr1, expr3);
assertTrue(ExpressionUtil.isNullRejectingExpression(expr, "T2"));
assertTrue(ExpressionUtil.isNullRejectingExpression(expr, "T1"));
// Wrong table
assertTrue(!ExpressionUtil.isNullRejectingExpression(expr, "T"));
}
{
// Test "ABS(T1.C) > 5" is NULL-rejecting
TupleValueExpression tve = new TupleValueExpression();
tve.setTableName("T1");
tve.setColumnName("C");
ArrayList<AbstractExpression> args = new ArrayList<AbstractExpression>();
args.add(tve);
FunctionExpression abs = new FunctionExpression();
abs.setArgs(args);
ConstantValueExpression cve = new ConstantValueExpression();
cve.setValue("5");
OperatorExpression expr = new OperatorExpression(ExpressionType.COMPARE_GREATERTHAN, abs, cve);
assertTrue(ExpressionUtil.isNullRejectingExpression(expr, "T1"));
// Wrong table
assertTrue(!ExpressionUtil.isNullRejectingExpression(expr, "T"));
}
{
// Test negation
// Test "!(T1.C > T2.C)" is NULL-rejecting
TupleValueExpression tve1 = new TupleValueExpression();
tve1.setTableName("T1");
tve1.setColumnName("C");
TupleValueExpression tve2 = new TupleValueExpression();
tve2.setTableName("T2");
tve2.setColumnName("C");
OperatorExpression expr1 = new OperatorExpression(ExpressionType.COMPARE_GREATERTHAN, tve1, tve2);
OperatorExpression expr2 = new OperatorExpression(ExpressionType.OPERATOR_NOT, expr1, null);
assertTrue(ExpressionUtil.isNullRejectingExpression(expr2, "T1"));
assertTrue(ExpressionUtil.isNullRejectingExpression(expr2, "T2"));
// Wrong table
assertTrue(!ExpressionUtil.isNullRejectingExpression(expr2, "T"));
// Test "!(P AND Q)" is equivalent to "!P OR !Q"
// !(T1.C IS NULL AND T2.C IS NULL) is not NULL-rejecting
OperatorExpression expr3 = new OperatorExpression(ExpressionType.OPERATOR_IS_NULL, tve1, null);
OperatorExpression expr4 = new OperatorExpression(ExpressionType.OPERATOR_IS_NULL, tve2, null);
OperatorExpression expr5 = new OperatorExpression(ExpressionType.CONJUNCTION_AND, expr3, expr4);
OperatorExpression expr6 = new OperatorExpression(ExpressionType.OPERATOR_NOT, expr5, null);
assertTrue(!ExpressionUtil.isNullRejectingExpression(expr6, "T1"));
assertTrue(!ExpressionUtil.isNullRejectingExpression(expr6, "T2"));
// Wrong table
assertTrue(!ExpressionUtil.isNullRejectingExpression(expr6, "T"));
// !(T1.C > T2.C AND T2.C IS NULL) is NULL-rejecting for T2 only
OperatorExpression expr7 = new OperatorExpression(ExpressionType.COMPARE_GREATERTHAN, tve1, tve2);
OperatorExpression expr8 = new OperatorExpression(ExpressionType.CONJUNCTION_AND, expr7, expr4);
OperatorExpression expr9 = new OperatorExpression(ExpressionType.OPERATOR_NOT, expr8, null);
assertTrue(!ExpressionUtil.isNullRejectingExpression(expr9, "T1"));
assertTrue(ExpressionUtil.isNullRejectingExpression(expr9, "T2"));
// Wrong table
assertTrue(!ExpressionUtil.isNullRejectingExpression(expr9, "T"));
// Test "!(P OR Q)" is equivalent to "!P AND !Q"
// !(T1.C IS NULL OR T2.C IS NULL) is NULL-rejecting for T1 and T2
OperatorExpression expr10 = new OperatorExpression(ExpressionType.CONJUNCTION_OR, expr3, expr4);
OperatorExpression expr11 = new OperatorExpression(ExpressionType.OPERATOR_NOT, expr10, null);
assertTrue(ExpressionUtil.isNullRejectingExpression(expr11, "T1"));
assertTrue(ExpressionUtil.isNullRejectingExpression(expr11, "T2"));
// Wrong table
assertTrue(!ExpressionUtil.isNullRejectingExpression(expr11, "T"));
// !(T1.C > T2.C OR T2.C IS NULL) is NULL-rejecting for T1 and T2
OperatorExpression expr12 = new OperatorExpression(ExpressionType.CONJUNCTION_OR, expr7, expr4);
OperatorExpression expr13 = new OperatorExpression(ExpressionType.OPERATOR_NOT, expr12, null);
assertTrue(ExpressionUtil.isNullRejectingExpression(expr13, "T1"));
assertTrue(ExpressionUtil.isNullRejectingExpression(expr13, "T2"));
// Wrong table
assertTrue(!ExpressionUtil.isNullRejectingExpression(expr13, "T"));
}
{
// Test double negation "NOT T.C IS NOT NULL"
TupleValueExpression tve = new TupleValueExpression();
tve.setTableName("T");
tve.setColumnName("C");
OperatorExpression expr1 = new OperatorExpression(ExpressionType.OPERATOR_IS_NULL, tve, null);
OperatorExpression expr2 = new OperatorExpression(ExpressionType.OPERATOR_NOT, expr1, null);
OperatorExpression expr3 = new OperatorExpression(ExpressionType.OPERATOR_NOT, expr2, null);
assertTrue(!ExpressionUtil.isNullRejectingExpression(expr3, "T"));
// NOT (P AND NOT Q) --> (NOT P) OR NOT NOT Q --> (NOT P) OR Q
// NOT (T.C IS NULL AND NOT T.C > T.A) --> ( NOT T.C IS NULL) OR T.C
// > T.A
TupleValueExpression tve1 = new TupleValueExpression();
tve.setTableName("T");
tve.setColumnName("A");
OperatorExpression expr4 = new OperatorExpression(ExpressionType.COMPARE_GREATERTHAN, tve, tve1);
OperatorExpression expr5 = new OperatorExpression(ExpressionType.CONJUNCTION_AND, expr1, expr4);
OperatorExpression expr6 = new OperatorExpression(ExpressionType.OPERATOR_NOT, expr5, null);
assertTrue(ExpressionUtil.isNullRejectingExpression(expr6, "T"));
// NOT (T.C IS NOT NULL AND T.C > T.A) --> T.C IS NULL OR NOT( T.C >
// T.A)
OperatorExpression expr7 = new OperatorExpression(ExpressionType.CONJUNCTION_AND, expr2, expr4);
OperatorExpression expr8 = new OperatorExpression(ExpressionType.OPERATOR_NOT, expr7, null);
assertFalse(ExpressionUtil.isNullRejectingExpression(expr8, "T"));
}
{
// COALESCE expression
TupleValueExpression tve1 = new TupleValueExpression("T1", "T1", "C", "C", 1);
TupleValueExpression tve2 = new TupleValueExpression("T2", "T2", "C", "C", 1);
OperatorExpression isnull = new OperatorExpression(ExpressionType.OPERATOR_IS_NULL, tve1, null);
OperatorExpression alternative = new OperatorExpression(ExpressionType.OPERATOR_ALTERNATIVE, tve2, tve1);
OperatorExpression colalesce = new OperatorExpression(ExpressionType.OPERATOR_CASE_WHEN, isnull, alternative);
assertFalse(ExpressionUtil.isNullRejectingExpression(colalesce, "T1"));
assertFalse(ExpressionUtil.isNullRejectingExpression(colalesce, "T2"));
assertFalse(ExpressionUtil.isNullRejectingExpression(colalesce, "T"));
TupleValueExpression tve3 = new TupleValueExpression("T3", "T3", "C", "C", 1);
OperatorExpression compExpr = new OperatorExpression(
ExpressionType.COMPARE_GREATERTHAN, tve3, colalesce);
assertFalse(ExpressionUtil.isNullRejectingExpression(compExpr, "T1"));
assertFalse(ExpressionUtil.isNullRejectingExpression(compExpr, "T2"));
assertTrue(ExpressionUtil.isNullRejectingExpression(compExpr, "T3"));
}
}
// Test compiler time expression evaluation.
public void testEvaluateExpression() throws Exception {
TupleValueExpression tve = new TupleValueExpression();
AbstractExpression comp = new OperatorExpression(ExpressionType.COMPARE_GREATERTHAN, tve, tve);
AbstractExpression notcomp = new OperatorExpression(ExpressionType.OPERATOR_NOT, comp, null);
ConstantValueExpression trueCVE = ConstantValueExpression.getTrue();
ConstantValueExpression falseCVE = ConstantValueExpression.getFalse();
AbstractExpression expr;
{
// TRUE and expr => expr
expr = new OperatorExpression(ExpressionType.CONJUNCTION_AND, trueCVE, comp);
expr = ExpressionUtil.evaluateExpression(expr);
assertEquals(comp, expr);
// expr and TRUE => expr
expr = new OperatorExpression(ExpressionType.CONJUNCTION_AND, comp, trueCVE);
expr = ExpressionUtil.evaluateExpression(expr);
assertEquals(comp, expr);
// FALSE and expr => FASLE
expr = new OperatorExpression(ExpressionType.CONJUNCTION_AND, falseCVE, comp);
expr = ExpressionUtil.evaluateExpression(expr);
assertEquals(falseCVE, expr);
// expr and FALSE => FALSE
expr = new OperatorExpression(ExpressionType.CONJUNCTION_AND, comp, falseCVE);
expr = ExpressionUtil.evaluateExpression(expr);
assertEquals(falseCVE, expr);
// TRUE and TRUE => TRUE
expr = new OperatorExpression(ExpressionType.CONJUNCTION_AND, trueCVE, trueCVE);
expr = ExpressionUtil.evaluateExpression(expr);
assertEquals(trueCVE, expr);
// TRUE and FALSE => FALSE
expr = new OperatorExpression(ExpressionType.CONJUNCTION_AND, trueCVE, falseCVE);
expr = ExpressionUtil.evaluateExpression(expr);
assertEquals(falseCVE, expr);
// FALSE and TRUE => FALSE
expr = new OperatorExpression(ExpressionType.CONJUNCTION_AND, falseCVE, trueCVE);
expr = ExpressionUtil.evaluateExpression(expr);
assertEquals(falseCVE, expr);
// FALSE and FALSE => FALSE
expr = new OperatorExpression(ExpressionType.CONJUNCTION_AND, falseCVE, falseCVE);
expr = ExpressionUtil.evaluateExpression(expr);
assertEquals(falseCVE, expr);
}
{
// TRUE or expr => TRUE
expr = new OperatorExpression(ExpressionType.CONJUNCTION_OR, trueCVE, comp);
expr = ExpressionUtil.evaluateExpression(expr);
assertEquals(trueCVE, expr);
// expr or TRUE => TRUE
expr = new OperatorExpression(ExpressionType.CONJUNCTION_OR, comp, trueCVE);
expr = ExpressionUtil.evaluateExpression(expr);
assertEquals(trueCVE, expr);
// FALSE or expr => expr
expr = new OperatorExpression(ExpressionType.CONJUNCTION_OR, falseCVE, comp);
expr = ExpressionUtil.evaluateExpression(expr);
assertEquals(comp, expr);
// expr or FALSE => expr
expr = new OperatorExpression(ExpressionType.CONJUNCTION_OR, comp, falseCVE);
expr = ExpressionUtil.evaluateExpression(expr);
assertEquals(comp, expr);
// expr or expr => no change
expr = new OperatorExpression(ExpressionType.CONJUNCTION_OR, comp, comp);
AbstractExpression origExpr = expr.clone();
expr = ExpressionUtil.evaluateExpression(expr);
assertEquals(origExpr, expr);
// TRUE or TRUE => TRUE
expr = new OperatorExpression(ExpressionType.CONJUNCTION_OR, trueCVE, trueCVE);
expr = ExpressionUtil.evaluateExpression(expr);
assertEquals(trueCVE, expr);
// TRUE or FALSE => FALSE
expr = new OperatorExpression(ExpressionType.CONJUNCTION_OR, trueCVE, falseCVE);
expr = ExpressionUtil.evaluateExpression(expr);
assertEquals(trueCVE, expr);
// FALSE or TRUE => FALSE
expr = new OperatorExpression(ExpressionType.CONJUNCTION_OR, falseCVE, trueCVE);
expr = ExpressionUtil.evaluateExpression(expr);
assertEquals(trueCVE, expr);
// FALSE or FALSE => FALSE
expr = new OperatorExpression(ExpressionType.CONJUNCTION_OR, falseCVE, falseCVE);
expr = ExpressionUtil.evaluateExpression(expr);
assertEquals(falseCVE, expr);
}
{
AbstractExpression expr1, expr2;
// expr AND expr AND TRUE => expr AND expr
expr1 = new OperatorExpression(ExpressionType.CONJUNCTION_AND, trueCVE, comp);
expr2 = new OperatorExpression(ExpressionType.CONJUNCTION_AND, expr1, comp);
expr = ExpressionUtil.evaluateExpression(expr2);
AbstractExpression finalExpr = new OperatorExpression(ExpressionType.CONJUNCTION_AND, comp, comp);
assertEquals(finalExpr, expr);
// expr AND expr AND FALSE => FALSE
expr1 = new OperatorExpression(ExpressionType.CONJUNCTION_AND, falseCVE, comp);
expr2 = new OperatorExpression(ExpressionType.CONJUNCTION_AND, expr1, comp);
expr = ExpressionUtil.evaluateExpression(expr2);
assertEquals(falseCVE, expr);
// NOT(TRUE) => FASLE
expr = new OperatorExpression(ExpressionType.OPERATOR_NOT, trueCVE, null);
expr = ExpressionUtil.evaluateExpression(expr);
assertEquals(falseCVE, expr);
// NOT(FALSE) => TRUE
expr = new OperatorExpression(ExpressionType.OPERATOR_NOT, falseCVE, null);
expr = ExpressionUtil.evaluateExpression(expr);
assertEquals(trueCVE, expr);
// NOT (FALSE OR NOT expr) => expr
expr1 = new OperatorExpression(ExpressionType.OPERATOR_NOT, comp, null);
expr2 = new OperatorExpression(ExpressionType.CONJUNCTION_OR, falseCVE, expr1);
expr = new OperatorExpression(ExpressionType.OPERATOR_NOT, expr2, null);
expr = ExpressionUtil.evaluateExpression(expr);
assertEquals(comp, expr);
// NOT( .. OR .. OR ..) => (.. AND .. AND..)
expr1 = new OperatorExpression(ExpressionType.CONJUNCTION_OR, comp, comp);
expr2 = new OperatorExpression(ExpressionType.CONJUNCTION_OR, expr1, comp);
expr = new OperatorExpression(ExpressionType.OPERATOR_NOT, expr2, null);
expr = ExpressionUtil.evaluateExpression(expr);
AbstractExpression expectedExpr1 = new OperatorExpression(ExpressionType.CONJUNCTION_AND, notcomp, notcomp);
AbstractExpression expectedExpr2 = new OperatorExpression(ExpressionType.CONJUNCTION_AND, expectedExpr1, notcomp);
assertEquals(expectedExpr2, expr);
// NOT (FALSE AND expr) => TRUE OR NOT expr => TRUE
expr1 = new OperatorExpression(ExpressionType.CONJUNCTION_AND, falseCVE, comp);
expr = new OperatorExpression(ExpressionType.OPERATOR_NOT, expr1, null);
expr = ExpressionUtil.evaluateExpression(expr);
assertEquals(trueCVE, expr);
// NOT (FALSE AND expr) => TRUE OR NOT expr => TRUE
expr1 = new OperatorExpression(ExpressionType.CONJUNCTION_AND, falseCVE, comp);
expr = new OperatorExpression(ExpressionType.OPERATOR_NOT, expr1, null);
expr = ExpressionUtil.evaluateExpression(expr);
assertEquals(trueCVE, expr);
// NOT( .. AND .. AND ..) not equal to (NOT.. OR NOT.. OR..)
// special case not handled on purpose for short circuit reason.
expr1 = new OperatorExpression(ExpressionType.CONJUNCTION_AND, comp, comp);
expr2 = new OperatorExpression(ExpressionType.CONJUNCTION_AND, expr1, comp);
expr = new OperatorExpression(ExpressionType.OPERATOR_NOT, expr2, null);
assertEquals(expr, ExpressionUtil.evaluateExpression(expr));
}
}
}