Skip to content

Commit

Permalink
TEIID-3098 allowing schema scoped pushdown functions to be seen as the
Browse files Browse the repository at this point in the history
same the corresponding sys function
  • Loading branch information
shawkins committed Sep 19, 2014
1 parent 4c2a495 commit 8621655
Show file tree
Hide file tree
Showing 8 changed files with 114 additions and 57 deletions.
3 changes: 3 additions & 0 deletions api/src/main/java/org/teiid/metadata/FunctionMethod.java
Expand Up @@ -56,6 +56,9 @@
* @see FunctionParameter
*/
public class FunctionMethod extends AbstractMetadataRecord {

public static final String SYSTEM_NAME = AbstractMetadataRecord.RELATIONAL_URI + "system-name"; //$NON-NLS-1$

private static final long serialVersionUID = -8039086494296455152L;

private static final String NOT_ALLOWED = "NOT_ALLOWED"; //$NON-NLS-1$
Expand Down
Expand Up @@ -93,11 +93,18 @@ public ConnectorManager(String translatorName, String connectionName, ExecutionF
this.id = Arrays.asList(translatorName, connectionName);
if (ef != null) {
functions = ef.getPushDownFunctions();
ValidatorReport report = new ValidatorReport("Function Validation"); //$NON-NLS-1$
FunctionMetadataValidator.validateFunctionMethods(functions, report);
if(report.hasItems()) {
throw new TeiidRuntimeException(report.getFailureMessage());
}
if (functions != null) {
//set the specific name to match against imported versions of
//the same function
for (FunctionMethod functionMethod : functions) {
functionMethod.setProperty(FunctionMethod.SYSTEM_NAME, functionMethod.getName());
}
ValidatorReport report = new ValidatorReport("Function Validation"); //$NON-NLS-1$
FunctionMetadataValidator.validateFunctionMethods(functions, report);
if(report.hasItems()) {
throw new TeiidRuntimeException(report.getFailureMessage());
}
}
}
}

Expand Down
50 changes: 29 additions & 21 deletions engine/src/main/java/org/teiid/query/function/FunctionLibrary.java
Expand Up @@ -245,6 +245,14 @@ public List<FunctionDescriptor> findAllFunctions(String name, Class<?>[] types)
return Arrays.asList(descriptor);
}
return Collections.emptyList();
}

public static class ConversionResult {
public ConversionResult(FunctionMethod method) {
this.method = method;
}
public FunctionMethod method;
public boolean needsConverion;
}

/**
Expand All @@ -257,19 +265,10 @@ public List<FunctionDescriptor> findAllFunctions(String name, Class<?>[] types)
* @param returnType
* @param args
* @param types Existing types passed to the function
* @return Null if no conversion could be found, otherwise an array of conversions
* to apply to each argument. The list should match 1-to-1 with the parameters.
* Parameters that do not need a conversion are null; parameters that do are
* FunctionDescriptors.
* @throws InvalidFunctionException
* @throws InvalidFunctionException
* @throws QueryResolverException
*/
public FunctionDescriptor[] determineNecessaryConversions(String name, Class<?> returnType, Expression[] args, Class<?>[] types, boolean hasUnknownType) throws InvalidFunctionException {
// Check for no args - no conversion necessary
if(types.length == 0) {
return null;
}

public ConversionResult determineNecessaryConversions(String name, Class<?> returnType, Expression[] args, Class<?>[] types, boolean hasUnknownType) throws InvalidFunctionException {
//First find existing functions with same name and same number of parameters
final Collection<FunctionMethod> functionMethods = new LinkedList<FunctionMethod>();
functionMethods.addAll( this.systemFunctions.findFunctionMethods(name, types.length) );
Expand Down Expand Up @@ -407,27 +406,36 @@ public FunctionDescriptor[] determineNecessaryConversions(String name, Class<?>
}
if (useCurrent) {
ambiguous = false; //prefer narrower
} else {
String sysName = result.getProperty(FunctionMethod.SYSTEM_NAME, false);
String sysNameOther = nextMethod.getProperty(FunctionMethod.SYSTEM_NAME, false);
if (sysName != null && sysName.equalsIgnoreCase(sysNameOther)) {
ambiguous = false;
}
}
}

if (currentScore < bestScore || useNext) {
ambiguous = false;
if (currentScore == 0 && isSystemNext) {
//this must be an exact match
return null;
return new ConversionResult(nextMethod);
}

bestScore = currentScore;
result = nextMethod;
isSystem = isSystemNext;
}
}

if (ambiguous || result == null) {
throw GENERIC_EXCEPTION;
}

return getConverts(result, types);
}

if (ambiguous) {
throw GENERIC_EXCEPTION;
}

ConversionResult cr = new ConversionResult(result);
if (result != null) {
cr.needsConverion = (bestScore != 0);
}
return cr;
}

private int partCount(String name) {
Expand All @@ -444,7 +452,7 @@ private int partCount(String name) {
return result;
}

private FunctionDescriptor[] getConverts(FunctionMethod method, Class<?>[] types) {
public FunctionDescriptor[] getConverts(FunctionMethod method, Class<?>[] types) {
final List<FunctionParameter> methodTypes = method.getInputParameters();
FunctionDescriptor[] result = new FunctionDescriptor[types.length];
for(int i = 0; i < types.length; i++) {
Expand Down
Expand Up @@ -88,6 +88,7 @@ public void loadMetadata(MetadataFactory factory, ExecutionFactory executionFact
functions = (List<FunctionMethod>) ois.readObject();
for (FunctionMethod functionMethod : functions) {
factory.addFunction(functionMethod);
functionMethod.setProperty(FunctionMethod.SYSTEM_NAME, functionMethod.getName());
}
} catch (IOException e) {
throw new TeiidRuntimeException(e);
Expand Down
Expand Up @@ -44,6 +44,7 @@
import org.teiid.query.QueryPlugin;
import org.teiid.query.function.FunctionDescriptor;
import org.teiid.query.function.FunctionLibrary;
import org.teiid.query.function.FunctionLibrary.ConversionResult;
import org.teiid.query.metadata.GroupInfo;
import org.teiid.query.metadata.QueryMetadataInterface;
import org.teiid.query.metadata.TempMetadataID;
Expand Down Expand Up @@ -590,24 +591,33 @@ void resolveFunction(Function function, FunctionLibrary library)
}

// Attempt to get exact match of function for this signature
List<FunctionDescriptor> fds = findWithImplicitConversions(library, function, args, types, hasArgWithoutType);

// Function did not resolve - determine reason and throw exception
if(fds.isEmpty()) {
if(!library.hasFunctionMethod(function.getName(), args.length)) {
// Unknown function form
throw new QueryResolverException(QueryPlugin.Event.TEIID30068, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID30068, function));
}
// Known function form - but without type information
List<FunctionDescriptor> fds;
try {
fds = findWithImplicitConversions(library, function, args, types, hasArgWithoutType);
if(fds.isEmpty()) {
if(!library.hasFunctionMethod(function.getName(), args.length)) {
// Unknown function form
throw new QueryResolverException(QueryPlugin.Event.TEIID30068, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID30068, function));
}
// Known function form - but without type information
if (hasArgWithoutType) {
throw new QueryResolverException(QueryPlugin.Event.TEIID30069, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID30069, function));
}

// Known function form - unable to find implicit conversions
throw new QueryResolverException(QueryPlugin.Event.TEIID30070, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID30070, function));
}
if (fds.size() > 1) {
throw new QueryResolverException(QueryPlugin.Event.TEIID31150, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID31150, function));
}
} catch (InvalidFunctionException e) {
// Known function form - but without type information
if (hasArgWithoutType) {
throw new QueryResolverException(QueryPlugin.Event.TEIID30069, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID30069, function));
}
// Known function form - unable to find implicit conversions
throw new QueryResolverException(QueryPlugin.Event.TEIID30070, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID30070, function));
}
if (fds.size() > 1) {
throw new QueryResolverException(QueryPlugin.Event.TEIID31150, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID31150, function));
}
throw new QueryResolverException(QueryPlugin.Event.TEIID31150, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID31150, function));
}

FunctionDescriptor fd = fds.get(0);
if (fd.getMethod().isVarArgs()
&& fd.getTypes().length == types.length
Expand Down Expand Up @@ -664,20 +674,18 @@ void resolveFunction(Function function, FunctionLibrary library)
* @param types
* @return
* @throws TeiidComponentException
* @throws InvalidFunctionException
* @since 4.3
*/
private List<FunctionDescriptor> findWithImplicitConversions(FunctionLibrary library, Function function, Expression[] args, Class<?>[] types, boolean hasArgWithoutType) throws QueryResolverException, TeiidComponentException {

private List<FunctionDescriptor> findWithImplicitConversions(FunctionLibrary library, Function function, Expression[] args, Class<?>[] types, boolean hasArgWithoutType) throws QueryResolverException, TeiidComponentException, InvalidFunctionException {
// Try to find implicit conversion path to still perform this function
FunctionDescriptor[] conversions;
try {
conversions = library.determineNecessaryConversions(function.getName(), function.getType(), args, types, hasArgWithoutType);
} catch (InvalidFunctionException e) {
return Collections.emptyList();
}
ConversionResult cr = library.determineNecessaryConversions(function.getName(), function.getType(), args, types, hasArgWithoutType);
if (cr.method == null) {
return Collections.emptyList();
}
Class<?>[] newSignature = types;
if(conversions != null) {
if (cr.needsConverion) {
FunctionDescriptor[] conversions = library.getConverts(cr.method, types);
newSignature = new Class[conversions.length];
// Insert new conversion functions as necessary, while building new signature
for(int i=0; i<conversions.length; i++) {
Expand All @@ -697,10 +705,11 @@ private List<FunctionDescriptor> findWithImplicitConversions(FunctionLibrary lib

newSignature[i] = newType;
}
}
}
String name = cr.method.getFullName();

// Now resolve using the new signature to get the function's descriptor
return library.findAllFunctions(function.getName(), newSignature);
return library.findAllFunctions(name, newSignature);
}

/**
Expand Down
Expand Up @@ -38,7 +38,7 @@
* descriptor.
*/
public class Function implements Expression {

private String name;
private Expression[] args;
private Class<?> type;
Expand Down
Expand Up @@ -59,6 +59,7 @@
import org.teiid.core.util.TimestampWithTimezone;
import org.teiid.language.SQLConstants.NonReserved;
import org.teiid.metadata.FunctionMethod.PushDown;
import org.teiid.query.function.FunctionLibrary.ConversionResult;
import org.teiid.query.sql.symbol.Expression;
import org.teiid.query.unittest.RealMetadataFactory;
import org.teiid.query.unittest.TimestampUtil;
Expand Down Expand Up @@ -166,9 +167,13 @@ private void helpFindConversions(String fname, Class<?>[] types, FunctionDescrip

FunctionDescriptor[] actual;
try {
actual = library.determineNecessaryConversions(fname, null, new Expression[types.length], types, false);
if (actual == null) {
ConversionResult result = library.determineNecessaryConversions(fname, null, new Expression[types.length], types, false);
if (result.needsConverion) {
actual = library.getConverts(result.method, types);
} else if (result.method != null) {
actual = new FunctionDescriptor[types.length];
} else {
actual = null;
}
} catch (InvalidFunctionException e) {
actual = null;
Expand Down
Expand Up @@ -225,7 +225,31 @@ public static String vararg(Object... vals) {

func = (Function) QueryParser.getQueryParser().parseExpression(sql);
ResolverVisitor.resolveLanguageObject(func, tm);
System.out.println(func.getType());
}

@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);
}

}

0 comments on commit 8621655

Please sign in to comment.