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