Search in sources :

Example 1 with InvalidFunctionException

use of org.teiid.api.exception.query.InvalidFunctionException in project teiid by teiid.

the class TestFunctionLibrary method helpFindConversions.

private void helpFindConversions(String fname, Class<?>[] types, FunctionDescriptor[] expected) {
    FunctionDescriptor[] actual;
    try {
        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;
    }
    if (expected == null) {
        if (actual != null) {
            // $NON-NLS-1$ //$NON-NLS-2$
            fail("Expected to find no conversion for " + fname + Arrays.asList(types) + " but found: " + Arrays.asList(actual));
        }
    } else {
        if (actual == null) {
            // $NON-NLS-1$ //$NON-NLS-2$
            fail("Expected to find conversion for " + fname + Arrays.asList(types) + " but found none");
        } else {
            // Compare returned descriptors with expected descriptor
            for (int i = 0; i < expected.length; i++) {
                if (expected[i] == null) {
                    if (actual[i] != null) {
                        // $NON-NLS-1$ //$NON-NLS-2$
                        fail("Expected no conversion at index " + i + ", but found: " + actual[i]);
                    }
                } else {
                    if (actual[i] == null) {
                        // $NON-NLS-1$ //$NON-NLS-2$
                        fail("Expected conversion at index " + i + ", but found none.");
                    } else {
                        // $NON-NLS-1$
                        assertEquals("Expected conversion function names do not match: ", expected[i].getName(), actual[i].getName());
                        // $NON-NLS-1$
                        assertEquals("Expected conversion arg lengths do not match: ", expected[i].getTypes().length, actual[i].getTypes().length);
                    }
                }
            }
        }
    }
}
Also used : ConversionResult(org.teiid.query.function.FunctionLibrary.ConversionResult) InvalidFunctionException(org.teiid.api.exception.query.InvalidFunctionException)

Example 2 with InvalidFunctionException

use of org.teiid.api.exception.query.InvalidFunctionException in project teiid by teiid.

the class FunctionLibrary method determineNecessaryConversions.

/**
 * Get the conversions that are needed to call the named function with arguments
 * of the given type.  In the case of an exact match, the list will contain all nulls.
 * In other cases the list will contain one or more non-null values where the value
 * is a conversion function that can be used to convert to the proper types for
 * executing the function.
 * @param name Name of function
 * @param returnType
 * @param args
 * @param types Existing types passed to the function
 * @throws InvalidFunctionException
 * @throws QueryResolverException
 */
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));
    if (this.userFunctions != null) {
        for (FunctionTree tree : this.userFunctions) {
            functionMethods.addAll(tree.findFunctionMethods(name, types.length));
        }
    }
    // Score each match, reject any where types can not be converted implicitly
    // Score of current method (lower score means better match with less converts
    // Current best score (lower score is best.  Higher score results in more implicit conversions
    int bestScore = Integer.MAX_VALUE;
    boolean ambiguous = false;
    FunctionMethod result = null;
    boolean isSystem = false;
    boolean narrowing = false;
    outer: for (FunctionMethod nextMethod : functionMethods) {
        int currentScore = 0;
        boolean nextNarrowing = false;
        final List<FunctionParameter> methodTypes = nextMethod.getInputParameters();
        // no implicit conversion is possible
        for (int i = 0; i < types.length; i++) {
            final String tmpTypeName = methodTypes.get(Math.min(i, methodTypes.size() - 1)).getRuntimeType();
            Class<?> targetType = DataTypeManager.getDataTypeClass(tmpTypeName);
            Class<?> sourceType = types[i];
            if (sourceType == null) {
                currentScore++;
                continue;
            }
            if (sourceType.isArray() && targetType.isArray() && sourceType.getComponentType().equals(targetType.getComponentType())) {
                currentScore++;
                continue;
            }
            if (sourceType.isArray()) {
                if (isVarArgArrayParam(nextMethod, types, i, targetType)) {
                    // vararg array parameter
                    continue;
                }
                // treat the array as object type until proper type handling is added
                sourceType = DataTypeManager.DefaultDataClasses.OBJECT;
            }
            try {
                Transform t = getConvertFunctionDescriptor(sourceType, targetType);
                if (t != null) {
                    if (t.isExplicit()) {
                        if (!(args[i] instanceof Constant) || ResolverUtil.convertConstant(DataTypeManager.getDataTypeName(sourceType), tmpTypeName, (Constant) args[i]) == null) {
                            continue outer;
                        }
                        nextNarrowing = true;
                        currentScore++;
                    } else {
                        currentScore++;
                    }
                }
            } catch (InvalidFunctionException e) {
                continue outer;
            }
        }
        // If the method is valid match and it is the current best score, capture those values as current best match
        if (currentScore > bestScore) {
            continue;
        }
        if (hasUnknownType) {
            if (returnType != null) {
                try {
                    Transform t = getConvertFunctionDescriptor(DataTypeManager.getDataTypeClass(nextMethod.getOutputParameter().getRuntimeType()), returnType);
                    if (t != null) {
                        if (t.isExplicit()) {
                            // there still may be a common type, but use any other valid conversion over this one
                            currentScore += types.length + 1;
                            nextNarrowing = true;
                        } else {
                            currentScore++;
                        }
                    }
                } catch (InvalidFunctionException e) {
                    // there still may be a common type, but use any other valid conversion over this one
                    currentScore += (types.length * types.length);
                }
            }
        }
        if (nextNarrowing && result != null && !narrowing) {
            continue;
        }
        boolean useNext = false;
        if (!nextNarrowing && narrowing) {
            useNext = true;
        }
        boolean isSystemNext = nextMethod.getParent() == null || INTERNAL_SCHEMAS.contains(nextMethod.getParent().getName());
        if ((isSystem && isSystemNext) || (!isSystem && !isSystemNext && result != null)) {
            int partCount = partCount(result.getName());
            int nextPartCount = partCount(nextMethod.getName());
            if (partCount < nextPartCount) {
                // this makes us more consistent with the table resolving logic
                continue outer;
            }
            if (nextPartCount < partCount) {
                useNext = true;
            }
        } else if (isSystemNext) {
            useNext = true;
        }
        if (currentScore == bestScore && !useNext) {
            ambiguous = true;
            boolean useCurrent = false;
            List<FunctionParameter> bestParams = result.getInputParameters();
            for (int j = 0; j < types.length; j++) {
                String t1 = bestParams.get(Math.min(j, bestParams.size() - 1)).getRuntimeType();
                String t2 = methodTypes.get((Math.min(j, methodTypes.size() - 1))).getRuntimeType();
                if (types[j] == null || t1.equals(t2)) {
                    continue;
                }
                String commonType = ResolverUtil.getCommonRuntimeType(new String[] { t1, t2 });
                if (commonType == null) {
                    // still ambiguous
                    continue outer;
                }
                if (commonType.equals(t1)) {
                    if (!useCurrent) {
                        useNext = true;
                    }
                } else if (commonType.equals(t2)) {
                    if (!useNext) {
                        useCurrent = true;
                    }
                } else {
                    continue outer;
                }
            }
            if (useCurrent) {
                // prefer narrower
                ambiguous = false;
            } 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) {
                return new ConversionResult(nextMethod);
            }
            bestScore = currentScore;
            result = nextMethod;
            isSystem = isSystemNext;
            narrowing = nextNarrowing;
        }
    }
    if (ambiguous) {
        throw GENERIC_EXCEPTION;
    }
    ConversionResult cr = new ConversionResult(result);
    if (result != null) {
        cr.needsConverion = (bestScore != 0);
    }
    return cr;
}
Also used : Constant(org.teiid.query.sql.symbol.Constant) LinkedList(java.util.LinkedList) FunctionMethod(org.teiid.metadata.FunctionMethod) ArrayList(java.util.ArrayList) LinkedList(java.util.LinkedList) List(java.util.List) Transform(org.teiid.core.types.Transform) InvalidFunctionException(org.teiid.api.exception.query.InvalidFunctionException)

Example 3 with InvalidFunctionException

use of org.teiid.api.exception.query.InvalidFunctionException in project teiid by teiid.

the class ResolverVisitor method resolveFunction.

/**
 * Resolve function such that all functions are resolved and type-safe.
 */
void resolveFunction(Function function, FunctionLibrary library) throws QueryResolverException, TeiidComponentException {
    // Check whether this function is already resolved
    if (function.getFunctionDescriptor() != null) {
        return;
    }
    // Look up types for all args
    boolean hasArgWithoutType = false;
    Expression[] args = function.getArgs();
    Class<?>[] types = new Class[args.length];
    for (int i = 0; i < args.length; i++) {
        types[i] = args[i].getType();
        if (types[i] == null) {
            if (!(args[i] instanceof Reference)) {
                throw new QueryResolverException(QueryPlugin.Event.TEIID30067, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID30067, new Object[] { args[i], function }));
            }
            hasArgWithoutType = true;
        }
    }
    // special case handling for convert of an untyped reference
    if (FunctionLibrary.isConvert(function) && hasArgWithoutType) {
        Constant constant = (Constant) function.getArg(1);
        Class<?> type = metadata.getDataTypeClass((String) constant.getValue());
        setDesiredType(function.getArg(0), type, function);
        types[0] = type;
        hasArgWithoutType = false;
    }
    // Attempt to get exact match of function for this signature
    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));
        }
        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 && library.isVarArgArrayParam(fd.getMethod(), types, types.length - 1, fd.getTypes()[types.length - 1])) {
        fd = fd.clone();
        fd.setCalledWithVarArgArrayParam(true);
    }
    if (fd.isSystemFunction(FunctionLibrary.CONVERT) || fd.isSystemFunction(FunctionLibrary.CAST)) {
        String dataType = (String) ((Constant) args[1]).getValue();
        Class<?> dataTypeClass = metadata.getDataTypeClass(dataType);
        fd = library.findTypedConversionFunction(args[0].getType(), dataTypeClass);
        // Verify that the type conversion from src to type is even valid
        Class<?> srcTypeClass = args[0].getType();
        if (srcTypeClass != null && dataTypeClass != null && !srcTypeClass.equals(dataTypeClass) && !DataTypeManager.isTransformable(srcTypeClass, dataTypeClass)) {
            throw new QueryResolverException(QueryPlugin.Event.TEIID30071, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID30071, new Object[] { DataTypeManager.getDataTypeName(srcTypeClass), dataType }));
        }
    } else if (fd.isSystemFunction(FunctionLibrary.LOOKUP)) {
        ResolverUtil.ResolvedLookup lookup = ResolverUtil.resolveLookup(function, metadata);
        fd = library.copyFunctionChangeReturnType(fd, lookup.getReturnElement().getType());
    } else if (fd.isSystemFunction(FunctionLibrary.ARRAY_GET)) {
        if (args[0].getType() != null && args[0].getType().isArray()) {
            fd = library.copyFunctionChangeReturnType(fd, args[0].getType().getComponentType());
        } else {
            if (function.getType() != null) {
                setDesiredType(args[0], function.getType(), function);
            }
            if (args[0].getType() != DataTypeManager.DefaultDataClasses.OBJECT) {
                throw new QueryResolverException(QueryPlugin.Event.TEIID31145, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID31145, DataTypeManager.getDataTypeName(args[0].getType()), function));
            }
        }
    } else if (Boolean.valueOf(fd.getMethod().getProperty(TEIID_PASS_THROUGH_TYPE, false))) {
        // hack largely to support pg
        fd = library.copyFunctionChangeReturnType(fd, args[0].getType());
    }
    function.setFunctionDescriptor(fd);
    function.setType(fd.getReturnType());
    if (CoreConstants.SYSTEM_MODEL.equals(fd.getSchema())) {
        if (StringUtil.startsWithIgnoreCase(function.getName(), SYS_PREFIX)) {
            function.setName(function.getName().substring(SYS_PREFIX.length()));
        }
    } else if (library.getSystemFunctions().hasFunctionWithName(function.getName()) && !StringUtil.startsWithIgnoreCase(function.getName(), function.getFunctionDescriptor().getSchema() + ElementSymbol.SEPARATOR)) {
        function.setName(function.getFunctionDescriptor().getSchema() + ElementSymbol.SEPARATOR + function.getName());
    }
}
Also used : FunctionDescriptor(org.teiid.query.function.FunctionDescriptor) QueryResolverException(org.teiid.api.exception.query.QueryResolverException) ExceptionExpression(org.teiid.query.sql.proc.ExceptionExpression) LanguageObject(org.teiid.query.sql.LanguageObject) InvalidFunctionException(org.teiid.api.exception.query.InvalidFunctionException)

Aggregations

InvalidFunctionException (org.teiid.api.exception.query.InvalidFunctionException)3 ArrayList (java.util.ArrayList)1 LinkedList (java.util.LinkedList)1 List (java.util.List)1 QueryResolverException (org.teiid.api.exception.query.QueryResolverException)1 Transform (org.teiid.core.types.Transform)1 FunctionMethod (org.teiid.metadata.FunctionMethod)1 FunctionDescriptor (org.teiid.query.function.FunctionDescriptor)1 ConversionResult (org.teiid.query.function.FunctionLibrary.ConversionResult)1 LanguageObject (org.teiid.query.sql.LanguageObject)1 ExceptionExpression (org.teiid.query.sql.proc.ExceptionExpression)1 Constant (org.teiid.query.sql.symbol.Constant)1