/* * 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"); } }