/* * 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.resolver; import java.sql.Clob; import static org.junit.Assert.*; import org.junit.Test; import org.teiid.api.exception.query.QueryParserException; import org.teiid.api.exception.query.QueryResolverException; import org.teiid.core.TeiidComponentException; import org.teiid.core.types.ClobType; import org.teiid.core.types.DataTypeManager; import org.teiid.core.types.XMLType; import org.teiid.query.eval.Evaluator; import org.teiid.query.metadata.QueryMetadataInterface; import org.teiid.query.metadata.TransformationMetadata; import org.teiid.query.parser.QueryParser; import org.teiid.query.resolver.util.ResolverVisitor; import org.teiid.query.sql.symbol.Constant; import org.teiid.query.sql.symbol.ElementSymbol; import org.teiid.query.sql.symbol.Expression; import org.teiid.query.sql.symbol.Function; import org.teiid.query.sql.symbol.Reference; import org.teiid.query.sql.symbol.XMLSerialize; import org.teiid.query.unittest.RealMetadataFactory; import org.teiid.query.unittest.RealMetadataFactory.DDLHolder; @SuppressWarnings("nls") public class TestFunctionResolving { @Test public void testResolveBadConvert() throws Exception { Function function = new Function("convert", new Expression[] {new Constant(new Character('a')), new Constant(DataTypeManager.DefaultDataTypes.DATE)}); //$NON-NLS-1$ try { ResolverVisitor.resolveLanguageObject(function, RealMetadataFactory.example1Cached()); fail("excpetion expected"); //$NON-NLS-1$ } catch (QueryResolverException err) { assertEquals("TEIID30071 The conversion from char to date is not allowed.", err.getMessage()); //$NON-NLS-1$ } } @Test public void testResolvesClosestType() throws Exception { ElementSymbol e1 = new ElementSymbol("pm1.g1.e1"); //$NON-NLS-1$ //dummy resolve to a byte e1.setType(DataTypeManager.DefaultDataClasses.BYTE); e1.setMetadataID(new Object()); Function function = new Function("abs", new Expression[] {e1}); //$NON-NLS-1$ ResolverVisitor.resolveLanguageObject(function, RealMetadataFactory.example1Cached()); assertEquals(DataTypeManager.DefaultDataClasses.INTEGER, function.getType()); } @Test public void testResolveConvertReference() throws Exception { Function function = new Function("convert", new Expression[] {new Reference(0), new Constant(DataTypeManager.DefaultDataTypes.BOOLEAN)}); //$NON-NLS-1$ ResolverVisitor.resolveLanguageObject(function, RealMetadataFactory.example1Cached()); assertEquals(DataTypeManager.DefaultDataClasses.BOOLEAN, function.getType()); assertEquals(DataTypeManager.DefaultDataClasses.BOOLEAN, function.getArgs()[0].getType()); } @Test public void testResolveAmbiguousFunction() throws Exception { Function function = new Function("LCASE", new Expression[] {new Reference(0)}); //$NON-NLS-1$ try { ResolverVisitor.resolveLanguageObject(function, RealMetadataFactory.example1Cached()); fail("excpetion expected"); //$NON-NLS-1$ } catch (QueryResolverException err) { assertEquals("TEIID30069 The function 'LCASE(?)' has more than one possible signature.", err.getMessage()); //$NON-NLS-1$ } } @Test public void testResolveCoalesce() throws Exception { String sql = "coalesce('', '')"; //$NON-NLS-1$ helpResolveFunction(sql); } @Test public void testResolveCoalesce1() throws Exception { String sql = "coalesce('', '', '')"; //$NON-NLS-1$ helpResolveFunction(sql); } /** * Should resolve using varags logic */ @Test public void testResolveCoalesce1a() throws Exception { String sql = "coalesce('', '', '', '')"; //$NON-NLS-1$ helpResolveFunction(sql); } /** * Should resolve as 1 is implicitly convertable to string */ @Test public void testResolveCoalesce2() throws Exception { String sql = "coalesce('', 1, '', '')"; //$NON-NLS-1$ helpResolveFunction(sql); } @Test public void testResolveCoalesce3() throws Exception { String sql = "coalesce('', 1, null, '')"; //$NON-NLS-1$ helpResolveFunction(sql); } @Test public void testResolveCoalesce4() throws Exception { String sql = "coalesce({d'2009-03-11'}, 1)"; //$NON-NLS-1$ helpResolveFunction(sql); } private Function helpResolveFunction(String sql) throws QueryParserException, QueryResolverException, TeiidComponentException { Function func = (Function)getExpression(sql); assertEquals(DataTypeManager.DefaultDataClasses.STRING, func.getType()); return func; } public static Expression getExpression(String sql) throws QueryParserException, TeiidComponentException, QueryResolverException { Expression func = QueryParser.getQueryParser().parseExpression(sql); TransformationMetadata tm = RealMetadataFactory.example1Cached(); ResolverVisitor.resolveLanguageObject(func, tm); return func; } /** * Helper to verify the result of an expression. * * @param expr SQL expression. * @param result Expected result. * @throws Exception */ public static void assertEval(String expr, String result) throws Exception { Expression ex = TestFunctionResolving.getExpression(expr); Object val = Evaluator.evaluate(ex); String valStr; if (val instanceof Clob) { valStr = ClobType.getString((Clob) val); } else if (val instanceof XMLType) { valStr = ((XMLType) val).getString(); } else { valStr = val.toString(); } assertEquals(result, valStr); } /** * e1 is of type string, so 1 should be converted to string * @throws Exception */ @Test public void testLookupTypeConversion() throws Exception { String sql = "lookup('pm1.g1', 'e2', 'e1', 1)"; //$NON-NLS-1$ Function f = (Function)getExpression(sql); assertEquals(DataTypeManager.DefaultDataClasses.STRING, f.getArg(3).getType()); } @Test public void testXMLSerialize() throws Exception { String sql = "xmlserialize(DOCUMENT '<a/>' as clob)"; //$NON-NLS-1$ XMLSerialize xs = (XMLSerialize)getExpression(sql); assertEquals(DataTypeManager.DefaultDataClasses.CLOB, xs.getType()); } @Test(expected=QueryResolverException.class) public void testXMLSerialize_1() throws Exception { String sql = "xmlserialize(DOCUMENT 1 as clob)"; //$NON-NLS-1$ XMLSerialize xs = (XMLSerialize)getExpression(sql); assertEquals(DataTypeManager.DefaultDataClasses.CLOB, xs.getType()); } @Test(expected=QueryResolverException.class) public void testStringAggWrongTypes() throws Exception { String sql = "string_agg(pm1.g1.e1, pm1.g1.e2)"; //$NON-NLS-1$ getExpression(sql); } @Test(expected=QueryResolverException.class) public void testStringAggWrongArgs() throws Exception { String sql = "string_agg(pm1.g1.e1)"; //$NON-NLS-1$ getExpression(sql); } public static String vararg(Object... vals) { return String.valueOf(vals.length); } @Test public void testVarArgsFunction() throws Exception { String ddl = "create foreign function func (VARIADIC z object) returns string options (JAVA_CLASS '"+this.getClass().getName()+"', JAVA_METHOD 'vararg');\n"; TransformationMetadata tm = RealMetadataFactory.fromDDL(ddl, "x", "y"); String sql = "func(('a', 'b'))"; Function func = (Function) QueryParser.getQueryParser().parseExpression(sql); ResolverVisitor.resolveLanguageObject(func, tm); assertEquals(1, func.getArgs().length); assertEquals("2", Evaluator.evaluate(func)); } @Test public void testAmbiguousUDF() throws Exception { TransformationMetadata tm = RealMetadataFactory.fromDDL("x", new DDLHolder("y", "create foreign function f () returns string"), new DDLHolder("z", "create foreign function f () returns string")); String sql = "f()"; Function func = (Function) QueryParser.getQueryParser().parseExpression(sql); try { ResolverVisitor.resolveLanguageObject(func, tm); fail(); } catch(QueryResolverException e) { } sql = "z.f()"; func = (Function) QueryParser.getQueryParser().parseExpression(sql); ResolverVisitor.resolveLanguageObject(func, tm); } @Test public void testUDFResolveOrder() throws Exception { QueryMetadataInterface tm = RealMetadataFactory.fromDDL("create foreign function func(x object) returns object; " + " create foreign function func(x string) returns string;" + " create foreign function func1(x object) returns double;" + " create foreign function func1(x string[]) returns bigdecimal;", "x", "y"); String sql = "func('a')"; Function func = (Function) QueryParser.getQueryParser().parseExpression(sql); ResolverVisitor.resolveLanguageObject(func, tm); assertEquals(DataTypeManager.DefaultDataClasses.STRING, func.getArgs()[0].getType()); assertEquals(DataTypeManager.DefaultDataClasses.STRING, func.getType()); sql = "func1(('1',))"; func = (Function) QueryParser.getQueryParser().parseExpression(sql); ResolverVisitor.resolveLanguageObject(func, tm); } @Test public void testImportedPushdown() throws Exception { RealMetadataFactory.example1Cached(); QueryMetadataInterface tm = RealMetadataFactory.fromDDL("x", new DDLHolder("y", "create foreign function func(x object) returns object;"), new DDLHolder("z", "create foreign function func(x object) returns object;")); String sql = "func('a')"; Function func = (Function) QueryParser.getQueryParser().parseExpression(sql); try { ResolverVisitor.resolveLanguageObject(func, tm); fail("should be ambiguous"); } catch (QueryResolverException e) { } tm = RealMetadataFactory.fromDDL("x", new DDLHolder("y", "create foreign function func(x object) returns object options (\"teiid_rel:system-name\" 'f');"), new DDLHolder("z", "create foreign function func(x object) returns object options (\"teiid_rel:system-name\" 'f');")); func = (Function) QueryParser.getQueryParser().parseExpression(sql); ResolverVisitor.resolveLanguageObject(func, tm); tm = RealMetadataFactory.fromDDL("x", new DDLHolder("y", "create foreign function func() returns object options (\"teiid_rel:system-name\" 'f');"), new DDLHolder("z", "create foreign function func() returns object options (\"teiid_rel:system-name\" 'f');")); func = (Function) QueryParser.getQueryParser().parseExpression("func()"); ResolverVisitor.resolveLanguageObject(func, tm); } /** * e1 is of type string, so 1 should be converted to string * @throws Exception */ @Test public void testNumericConversion() throws Exception { String sql = "1.0/2"; //$NON-NLS-1$ Function f = (Function)getExpression(sql); assertEquals(DataTypeManager.DefaultDataClasses.BIG_DECIMAL, f.getType()); } }