/*
* JBoss, Home of Professional Open Source.
* See the COPYRIGHT.txt file distributed with this work for information
* regarding copyright ownership. Some portions may be licensed
* to Red Hat, Inc. under one or more contributor license agreements.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*/
package org.teiid.query.processor.eval;
import static org.junit.Assert.*;
import static org.teiid.query.resolver.TestFunctionResolving.*;
import java.io.Serializable;
import java.math.BigDecimal;
import java.sql.Clob;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import org.junit.Test;
import org.teiid.api.exception.query.ExpressionEvaluationException;
import org.teiid.common.buffer.BlockedException;
import org.teiid.core.TeiidComponentException;
import org.teiid.core.TeiidException;
import org.teiid.core.TeiidProcessingException;
import org.teiid.core.types.DataTypeManager;
import org.teiid.query.eval.Evaluator;
import org.teiid.query.function.FunctionDescriptor;
import org.teiid.query.parser.QueryParser;
import org.teiid.query.processor.FakeDataManager;
import org.teiid.query.processor.ProcessorDataManager;
import org.teiid.query.resolver.TestFunctionResolving;
import org.teiid.query.sql.lang.CollectionValueIterator;
import org.teiid.query.sql.lang.CompareCriteria;
import org.teiid.query.sql.lang.IsNullCriteria;
import org.teiid.query.sql.lang.Query;
import org.teiid.query.sql.lang.QueryCommand;
import org.teiid.query.sql.lang.SubqueryContainer;
import org.teiid.query.sql.symbol.*;
import org.teiid.query.sql.util.ValueIterator;
import org.teiid.query.unittest.RealMetadataFactory;
import org.teiid.query.util.CommandContext;
@SuppressWarnings("nls")
public class TestExpressionEvaluator {
public void helpTestEval(Expression expr, Expression[] elementList, Object[] valueList, ProcessorDataManager dataMgr, CommandContext context, Object expectedValue) {
try {
Object actualValue = helpEval(expr, elementList, valueList, dataMgr, context);
assertEquals("Did not get expected result", expectedValue, actualValue); //$NON-NLS-1$
} catch(TeiidException e) {
throw new RuntimeException(e);
}
}
public Object helpEval(Expression expr, Expression[] elementList, Object[] valueList, ProcessorDataManager dataMgr, CommandContext context) throws ExpressionEvaluationException, BlockedException, TeiidComponentException {
Map<Expression, Integer> elements = new HashMap<Expression, Integer>();
if (elementList != null) {
for(int i=0; i<elementList.length; i++) {
elements.put(elementList[i], i);
}
}
List<Object> tuple = null;
if (valueList != null) {
tuple = Arrays.asList(valueList);
}
return new Evaluator(elements, dataMgr, context).evaluate(expr, tuple);
}
@Test public void testCaseExpression1() {
CaseExpression expr = TestCaseExpression.example(3);
expr.setExpression(new Constant("a")); //$NON-NLS-1$
helpTestEval(expr, null, null, null, null, new Integer(0));
expr.setExpression(new Constant("b")); //$NON-NLS-1$
helpTestEval(expr, null, null, null, null, new Integer(1));
expr.setExpression(new Constant("c")); //$NON-NLS-1$
helpTestEval(expr, null, null, null, null, new Integer(2));
expr.setExpression(new Constant("d")); //$NON-NLS-1$
helpTestEval(expr, null, null, null, null, new Integer(9999));
}
@Test public void testSearchedCaseExpression1() {
SearchedCaseExpression expr = TestSearchedCaseExpression.example(3);
helpTestEval(expr,
new Expression[] {new ElementSymbol("x")}, //$NON-NLS-1$
new Object[] {new Integer(0)},
null,
null,
new Integer(0));
helpTestEval(expr,
new Expression[] {new ElementSymbol("x")}, //$NON-NLS-1$
new Object[] {new Integer(1)},
null,
null,
new Integer(1));
helpTestEval(expr,
new Expression[] {new ElementSymbol("x")}, //$NON-NLS-1$
new Object[] {new Integer(2)},
null,
null,
new Integer(2));
helpTestEval(expr,
new Expression[] {new ElementSymbol("x")}, //$NON-NLS-1$
new Object[] {new Integer(3)},
null,
null,
new Integer(9999));
}
@Test public void testConstant() {
helpTestEval(new Constant("xyz", String.class), new Expression[0], new Object[0], null, null, "xyz"); //$NON-NLS-1$ //$NON-NLS-2$
}
@Test public void testElement1() {
ElementSymbol e1 = new ElementSymbol("e1"); //$NON-NLS-1$
ElementSymbol e2 = new ElementSymbol("e2"); //$NON-NLS-1$
Expression[] elements = new Expression[] {
e1, e2
};
Object[] values = new Object[] {
"xyz", "abc" //$NON-NLS-1$ //$NON-NLS-2$
};
helpTestEval(e1, elements, values, null, null, "xyz"); //$NON-NLS-1$
}
@Test public void testElement2() {
ElementSymbol e1 = new ElementSymbol("e1"); //$NON-NLS-1$
ElementSymbol e2 = new ElementSymbol("e2"); //$NON-NLS-1$
Expression[] elements = new Expression[] {
e1, e2
};
Object[] values = new Object[] {
"xyz", "abc" //$NON-NLS-1$ //$NON-NLS-2$
};
helpTestEval(e2, elements, values, null, null, "abc"); //$NON-NLS-1$
}
/**
* Element Symbols must have values set during evaluation
*/
@Test public void testElement3() throws Exception {
ElementSymbol e2 = new ElementSymbol("e2"); //$NON-NLS-1$
Expression[] elements = new Expression[] {};
Object[] values = new Object[] {
"xyz", "abc" //$NON-NLS-1$ //$NON-NLS-2$
};
try {
helpEval(e2, elements, values, null, null);
fail("Exception expected"); //$NON-NLS-1$
} catch (TeiidComponentException e){
//this should be a componentexception, since it is unexpected
assertEquals("TEIID30328 Unable to evaluate e2: No value was available", e.getMessage()); //$NON-NLS-1$
}
}
@Test public void testFunction1() {
ElementSymbol e1 = new ElementSymbol("e1"); //$NON-NLS-1$
e1.setType(String.class);
ElementSymbol e2 = new ElementSymbol("e2"); //$NON-NLS-1$
e2.setType(String.class);
Function func = new Function("concat", new Expression[] { e1, e2 }); //$NON-NLS-1$
FunctionDescriptor desc = RealMetadataFactory.SFM.getSystemFunctionLibrary().findFunction("concat", new Class[] { String.class, String.class } ); //$NON-NLS-1$
func.setFunctionDescriptor(desc);
Expression[] elements = new Expression[] {
e1, e2
};
Object[] values = new Object[] {
"xyz", "abc" //$NON-NLS-1$ //$NON-NLS-2$
};
helpTestEval(func, elements, values, null, null, "xyzabc"); //$NON-NLS-1$
}
@Test public void testFunction2() {
ElementSymbol e1 = new ElementSymbol("e1"); //$NON-NLS-1$
e1.setType(String.class);
ElementSymbol e2 = new ElementSymbol("e2"); //$NON-NLS-1$
e2.setType(String.class);
Function func = new Function("concat", new Expression[] { e2, e1 }); //$NON-NLS-1$
FunctionDescriptor desc = RealMetadataFactory.SFM.getSystemFunctionLibrary().findFunction("concat", new Class[] { String.class, String.class } ); //$NON-NLS-1$
func.setFunctionDescriptor(desc);
Expression[] elements = new Expression[] {
e1, e2
};
Object[] values = new Object[] {
"xyz", "abc" //$NON-NLS-1$ //$NON-NLS-2$
};
helpTestEval(func, elements, values, null, null, "abcxyz"); //$NON-NLS-1$
}
@Test public void testLookupFunction() {
ElementSymbol e1 = new ElementSymbol("e1"); //$NON-NLS-1$
e1.setType(String.class);
ElementSymbol e2 = new ElementSymbol("e2"); //$NON-NLS-1$
e1.setType(Integer.class);
Function func = new Function("lookup", new Expression[] { new Constant("pm1.g1"), new Constant("e2"), new Constant("e1"), e1 }); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
FunctionDescriptor desc = RealMetadataFactory.SFM.getSystemFunctionLibrary().findFunction("lookup", new Class[] { String.class, String.class, String.class, String.class } ); //$NON-NLS-1$
func.setFunctionDescriptor(desc);
Expression[] elements = new Expression[] {
e1, e2
};
Object[] values = new Object[] {
"xyz", new Integer(5) //$NON-NLS-1$
};
FakeDataManager dataMgr = new FakeDataManager();
Map valueMap = new HashMap();
valueMap.put("xyz", new Integer(5)); //$NON-NLS-1$
dataMgr.defineCodeTable("pm1.g1", "e1", "e2", valueMap); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
helpTestEval(func, elements, values, dataMgr, null, new Integer(5));
}
@Test public void testScalarSubquery() throws Exception{
ScalarSubquery expr = new ScalarSubquery(new Query());
ArrayList values = new ArrayList(1);
values.add("a"); //$NON-NLS-1$
Object expected = "a"; //$NON-NLS-1$
helpTestWithValueIterator(expr, values, expected);
}
private void helpTestWithValueIterator(ScalarSubquery expr,
List<?> values, Object expected)
throws BlockedException,
TeiidComponentException, ExpressionEvaluationException {
final CollectionValueIterator valueIter = new CollectionValueIterator(values);
CommandContext cc = new CommandContext();
assertEquals(expected, new Evaluator(Collections.emptyMap(), null, cc) {
@Override
protected ValueIterator evaluateSubquery(
SubqueryContainer container, List tuple)
throws TeiidProcessingException, BlockedException,
TeiidComponentException {
return valueIter;
}
}.evaluate(expr, null) );
}
@Test public void testScalarSubquery2() throws Exception{
ScalarSubquery expr = new ScalarSubquery(new Query());
ArrayList values = new ArrayList(1);
values.add(null);
helpTestWithValueIterator(expr, values, null);
}
@Test public void testScalarSubquery3() throws Exception{
ScalarSubquery expr = new ScalarSubquery(new Query());
helpTestWithValueIterator(expr, Collections.emptyList(), null);
}
@Test public void testScalarSubqueryFails() throws Exception{
ScalarSubquery expr = new ScalarSubquery((QueryCommand) QueryParser.getQueryParser().parseCommand("select x from y"));
ArrayList values = new ArrayList(2);
values.add("a"); //$NON-NLS-1$
values.add("b"); //$NON-NLS-1$
try {
helpTestWithValueIterator(expr, values, null);
fail("Expected ExpressionEvaluationException but got none"); //$NON-NLS-1$
} catch (ExpressionEvaluationException e) {
assertEquals("TEIID30328 Unable to evaluate (SELECT x FROM y): TEIID30345 The command of this scalar subquery returned more than one value: SELECT x FROM y", e.getMessage()); //$NON-NLS-1$
}
}
@Test public void testUser() throws Exception {
Function func = new Function("user", new Expression[] {}); //$NON-NLS-1$
FunctionDescriptor desc = RealMetadataFactory.SFM.getSystemFunctionLibrary().findFunction("user", new Class[] {} ); //$NON-NLS-1$
func.setFunctionDescriptor(desc);
FakeDataManager dataMgr = new FakeDataManager();
CommandContext context = new CommandContext(new Long(1), null, null, null, 0);
context.setUserName("logon"); //$NON-NLS-1$
assertEquals(context.getUserName(), new Evaluator(Collections.emptyMap(), dataMgr, context).evaluate(func, Collections.emptyList()) );
}
public void helpTestCommandPayload(Serializable payload, String property, String expectedValue) throws Exception {
Function func = new Function("commandpayload", new Expression[] {}); //$NON-NLS-1$
Class[] parameterSignature = null;
if(property == null) {
parameterSignature = new Class[] {};
} else {
parameterSignature = new Class[] { String.class };
}
FunctionDescriptor desc = RealMetadataFactory.SFM.getSystemFunctionLibrary().findFunction("commandpayload", parameterSignature ); //$NON-NLS-1$
func.setFunctionDescriptor(desc);
FakeDataManager dataMgr = new FakeDataManager();
CommandContext context = new CommandContext(null, "user", payload, "vdb", 1, false); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
if(property != null) {
func.setArgs(new Expression[] {new Constant(property)});
}
String actual = (String) new Evaluator(Collections.emptyMap(), dataMgr, context).evaluate(func, Collections.emptyList());
assertEquals(expectedValue, actual);
}
@Test public void testCommandPayloadNoArgsWithPayload() throws Exception {
helpTestCommandPayload("blah", null, "blah"); //$NON-NLS-1$ //$NON-NLS-2$
}
@Test public void testCommandPayloadNoArgsWithoutPayload() throws Exception {
helpTestCommandPayload(null, null, null);
}
@Test public void testCommandPayloadNoArgsWithNonStringPayload() throws Exception {
helpTestCommandPayload(Boolean.TRUE, null, "true"); //$NON-NLS-1$
}
@Test public void testCommandPayloadArgWithPayload() throws Exception {
Properties props = new Properties();
props.setProperty("p1", "v1"); //$NON-NLS-1$ //$NON-NLS-2$
props.setProperty("p2", "v2"); //$NON-NLS-1$ //$NON-NLS-2$
helpTestCommandPayload(props, "p1", "v1"); //$NON-NLS-1$ //$NON-NLS-2$
}
@Test public void testCommandPayloadArgWithPayloadMissingProp() throws Exception {
Properties props = new Properties();
props.setProperty("p1", "v1"); //$NON-NLS-1$ //$NON-NLS-2$
props.setProperty("p2", "v2"); //$NON-NLS-1$ //$NON-NLS-2$
helpTestCommandPayload(props, "BOGUS", null); //$NON-NLS-1$
}
@Test public void testCommandPayloadArgWithoutPayload() throws Exception {
Properties props = new Properties();
props.setProperty("p1", "v1"); //$NON-NLS-1$ //$NON-NLS-2$
props.setProperty("p2", "v2"); //$NON-NLS-1$ //$NON-NLS-2$
helpTestCommandPayload(null, "BOGUS", null); //$NON-NLS-1$
}
@Test(expected=ExpressionEvaluationException.class) public void testCommandPayloadArgWithBadPayload() throws Exception {
helpTestCommandPayload(Boolean.TRUE, "BOGUS", null); //$NON-NLS-1$
}
@Test public void testBigDecimalFromDoubleDivision() throws Exception {
Expression ex = TestFunctionResolving.getExpression("convert(1.0, bigdecimal)/3");
assertEquals(new BigDecimal("0.3333333333333333"), Evaluator.evaluate(ex));
}
@Test public void testBigDecimalDivision() throws Exception {
Expression ex = TestFunctionResolving.getExpression("1/convert('3.0', bigdecimal)");
assertEquals(new BigDecimal("0.3333333333333333"), Evaluator.evaluate(ex));
}
@Test public void testIsNull() throws Exception {
assertEquals(Boolean.TRUE, Evaluator.evaluate(new IsNullCriteria(new Constant(null, DataTypeManager.DefaultDataClasses.BOOLEAN))));
}
@Test public void testIsNull1() throws Exception {
assertEquals(Boolean.FALSE, Evaluator.evaluate(new IsNullCriteria(new Constant(Boolean.TRUE, DataTypeManager.DefaultDataClasses.BOOLEAN))));
}
@Test public void testIsNull3() throws Exception {
IsNullCriteria inc = new IsNullCriteria(new Constant(null, DataTypeManager.DefaultDataClasses.BOOLEAN));
inc.setNegated(true);
assertEquals(Boolean.FALSE, Evaluator.evaluate(inc));
}
@Test public void testIsNull4() throws Exception {
IsNullCriteria inc = new IsNullCriteria(new Constant(Boolean.TRUE, DataTypeManager.DefaultDataClasses.BOOLEAN));
inc.setNegated(true);
assertEquals(Boolean.TRUE, Evaluator.evaluate(inc));
}
@Test public void testSubstring() throws Exception {
Expression ex = TestFunctionResolving.getExpression("substring('abcd' from 2 for 2)");
assertEquals("bc", Evaluator.evaluate(ex));
}
@Test public void testExtract() throws Exception {
Expression ex = TestFunctionResolving.getExpression("extract(year from cast('2011-01-01' as date))");
assertEquals(2011, Evaluator.evaluate(ex));
}
@Test public void testExtract1() throws Exception {
Expression ex = TestFunctionResolving.getExpression("extract(day from cast('2011-01-01' as date))");
assertEquals(1, Evaluator.evaluate(ex));
}
@Test public void testSimilarTo() throws Exception {
Expression ex = TestFunctionResolving.getExpression("'aaaxy' similar to 'a+%'");
assertEquals(Boolean.TRUE, Evaluator.evaluate(ex));
}
@Test public void testSimilarTo1() throws Exception {
Expression ex = TestFunctionResolving.getExpression("'xaay' similar to 'xa{2,3}y'");
assertEquals(Boolean.TRUE, Evaluator.evaluate(ex));
}
@Test public void testSimilarTo2() throws Exception {
Expression ex = TestFunctionResolving.getExpression("'xay' similar to 'xa{2,3}y'");
assertEquals(Boolean.FALSE, Evaluator.evaluate(ex));
}
@Test(expected=ExpressionEvaluationException.class) public void testSimilarTo3() throws Exception {
Expression ex = TestFunctionResolving.getExpression("'xay' similar to '{'");
assertEquals(Boolean.FALSE, Evaluator.evaluate(ex));
}
@Test public void testSimilarTo4() throws Exception {
Expression ex = TestFunctionResolving.getExpression("'xay' similar to 'xa{2,}y'");
assertEquals(Boolean.FALSE, Evaluator.evaluate(ex));
}
@Test public void testSimilarTo5() throws Exception {
Expression ex = TestFunctionResolving.getExpression("'x1y' similar to 'x([a-z]+|[0-9])_'");
assertEquals(Boolean.TRUE, Evaluator.evaluate(ex));
}
@Test public void testSimilarTo6() throws Exception {
Expression ex = TestFunctionResolving.getExpression("'xx' similar to 'x([a-z]+|[0-9])_'");
assertEquals(Boolean.FALSE, Evaluator.evaluate(ex));
}
@Test public void testLikeRegex() throws Exception {
Expression ex = TestFunctionResolving.getExpression("'aaaxy' like_regex 'a+.*'");
assertEquals(Boolean.TRUE, Evaluator.evaluate(ex));
}
@Test public void testLikeRegex1() throws Exception {
Expression ex = TestFunctionResolving.getExpression("'xaay' similar to 'xa{2,3}y'");
assertEquals(Boolean.TRUE, Evaluator.evaluate(ex));
}
@Test public void testLikeRegex2() throws Exception {
Expression ex = TestFunctionResolving.getExpression("'xay' like_regex 'xa{2,3}y'");
assertEquals(Boolean.FALSE, Evaluator.evaluate(ex));
}
@Test(expected=ExpressionEvaluationException.class) public void testLikeRegex3() throws Exception {
Expression ex = TestFunctionResolving.getExpression("'xay' like_regex '{'");
assertEquals(Boolean.FALSE, Evaluator.evaluate(ex));
}
@Test public void testLikeRegex4() throws Exception {
Expression ex = TestFunctionResolving.getExpression("'xay' like_regex 'a'");
assertEquals(Boolean.TRUE, Evaluator.evaluate(ex));
}
@Test public void testLikePlus() throws Exception {
Expression ex = TestFunctionResolving.getExpression("'+' like '+'");
assertEquals(Boolean.TRUE, Evaluator.evaluate(ex));
}
@Test public void testArrayEquality() throws Exception {
assertEquals(Boolean.TRUE, Evaluator.evaluate(new CompareCriteria(new Array(DataTypeManager.DefaultDataClasses.INTEGER, Arrays.asList((Expression)new Constant(1))), CompareCriteria.EQ, new Array(DataTypeManager.DefaultDataClasses.INTEGER, Arrays.asList((Expression)new Constant(1))))));
assertNull(new Evaluator(null, null, null).evaluateTVL(new CompareCriteria(new Array(DataTypeManager.DefaultDataClasses.INTEGER, Arrays.asList((Expression)new Constant(1))), CompareCriteria.EQ, new Array(DataTypeManager.DefaultDataClasses.INTEGER, Arrays.asList((Expression)new Constant(null)))), null));
}
@Test public void testToCharsBytesWellformed() throws Exception {
Expression ex = TestFunctionResolving.getExpression("to_chars(to_bytes('abc', 'utf-8', false), 'utf-8', true)");
assertEquals("abc", ((Clob)Evaluator.evaluate(ex)).getSubString(1, 3));
try {
ex = TestFunctionResolving.getExpression("to_bytes('\u00ff', 'ascii', false))");
Evaluator.evaluate(ex);
fail("expected exception");
} catch (ExpressionEvaluationException e) {
}
TestFunctionResolving.getExpression("to_bytes('\u00ff', 'ascii', false))");
}
@Test public void testRegexpReplaceOkay() throws Exception {
// Test replace-first vs replace-all.
assertEval("regexp_replace('foobarbaz', 'b..', 'X')", "fooXbaz");
assertEval("regexp_replace('foobarbaz', 'b..', 'X', 'g')", "fooXX");
// Test replace-all with capture group.
assertEval("regexp_replace('foobarbaz', 'b(..)', 'X$1Y', 'g')", "fooXarYXazY");
// Test case-insensitive matching.
assertEval("regexp_replace('fooBARbaz', 'a', 'X', 'g')", "fooBARbXz");
assertEval("regexp_replace('fooBARbaz', 'a', 'X', 'gi')", "fooBXRbXz");
// Test multiline.
assertEval("regexp_replace('foo\nbar\nbaz', '(b[\\d\\w\\s]+?)$', 'X', 'g')", "foo\nX");
assertEval("regexp_replace('foo\nbar\nbaz', '(b[\\d\\w\\s]+?)$', 'X', 'gm')", "foo\nX\nX");
}
@Test public void testTimestampResolving() throws Exception {
assertEval("TIMESTAMPDIFF(SQL_TSI_YEAR, '2000-01-01', '2002-01-01')", "2");
}
@Test public void testEndsWith() throws Exception {
// Test replace-first vs replace-all.
assertEval("endsWith('c', 'abc') = 't'", "true");
}
}