public Function getFunction(Function desc, Function.CompareMode mode) {
        List<Function> fns = vectorizedFunctions.get(desc.functionName());
        if (desc.hasNamedArg() && fns != null && !fns.isEmpty()) {
            fns = fns.stream().filter(Function::hasNamedArg).collect(Collectors.toList());
        }
        if (fns == null || fns.isEmpty()) {
            return null;
        }

        Function func;
        // To be back-compatible, we first choose the functions from the non-polymorphic functions, if we can't find
        // a suitable in non-polymorphic functions. We will try to search in the polymorphic functions.
        List<Function> standFns = fns.stream().filter(fn -> !fn.isPolymorphic()).collect(Collectors.toList());
        func = matchStrictFunction(desc, mode, standFns);
        if (func != null) {
            return func;
        }

        List<Function> polyFns = fns.stream().filter(Function::isPolymorphic).collect(Collectors.toList());
        func = matchPolymorphicFunction(desc, mode, polyFns);
        if (func != null) {
            return func;
        }

        return matchCastFunction(desc, mode, standFns);
    }
@Test
    public void testPolymorphicFunction() {
        // array_append(ARRAY<INT>, INT)
        Type[] argTypes = {INT_ARRAY, Type.INT};
        Function desc = new Function(new FunctionName("array_append"), argTypes, Type.INVALID, false);
        Function fn = functionSet.getFunction(desc, Function.CompareMode.IS_SUPERTYPE_OF);
        Assert.assertNotNull(fn);
        Assert.assertEquals(INT_ARRAY, fn.getReturnType());
        Assert.assertEquals(INT_ARRAY, fn.getArgs()[0]);
        Assert.assertEquals(Type.INT, fn.getArgs()[1]);

        // array_append(ARRAY<INT>, TINYINT)
        argTypes = new Type[] {INT_ARRAY, Type.TINYINT};
        desc = new Function(new FunctionName("array_append"), argTypes, Type.INVALID, false);
        fn = functionSet.getFunction(desc, Function.CompareMode.IS_SUPERTYPE_OF);
        Assert.assertNotNull(fn);
        Assert.assertEquals(INT_ARRAY, fn.getReturnType());
        Assert.assertEquals(INT_ARRAY, fn.getArgs()[0]);
        Assert.assertEquals(Type.INT, fn.getArgs()[1]);

        // array_append(ARRAY<TINYINT>, INT)
        argTypes = new Type[] {TINYINT_ARRAY, Type.INT};
        desc = new Function(new FunctionName("array_append"), argTypes, Type.INVALID, false);
        fn = functionSet.getFunction(desc, Function.CompareMode.IS_SUPERTYPE_OF);
        Assert.assertNotNull(fn);
        Assert.assertEquals(INT_ARRAY, fn.getReturnType());
        Assert.assertEquals(INT_ARRAY, fn.getArgs()[0]);
        Assert.assertEquals(Type.INT, fn.getArgs()[1]);

        // array_append(ARRAY<INT>, DOUBLE)
        argTypes = new Type[] {INT_ARRAY, Type.DOUBLE};
        desc = new Function(new FunctionName("array_append"), argTypes, Type.INVALID, false);
        fn = functionSet.getFunction(desc, Function.CompareMode.IS_SUPERTYPE_OF);
        Assert.assertNotNull(fn);
        Assert.assertEquals(DOUBLE_ARRAY, fn.getReturnType());
        Assert.assertEquals(DOUBLE_ARRAY, fn.getArgs()[0]);
        Assert.assertEquals(Type.DOUBLE, fn.getArgs()[1]);

        // array_append(NULL, INT)
        argTypes = new Type[] {Type.NULL, Type.INT};
        desc = new Function(new FunctionName("array_append"), argTypes, Type.INVALID, false);
        fn = functionSet.getFunction(desc, Function.CompareMode.IS_SUPERTYPE_OF);
        Assert.assertNotNull(fn);
        Assert.assertEquals(INT_ARRAY, fn.getReturnType());
        Assert.assertEquals(INT_ARRAY, fn.getArgs()[0]);
        Assert.assertEquals(Type.INT, fn.getArgs()[1]);

        // array_append(ARRAY<INT>, NULL)
        argTypes = new Type[] {INT_ARRAY, Type.NULL};
        desc = new Function(new FunctionName("array_append"), argTypes, Type.INVALID, false);
        fn = functionSet.getFunction(desc, Function.CompareMode.IS_SUPERTYPE_OF);
        Assert.assertNotNull(fn);
        Assert.assertEquals(INT_ARRAY, fn.getReturnType());
        Assert.assertEquals(INT_ARRAY, fn.getArgs()[0]);
        Assert.assertEquals(Type.INT, fn.getArgs()[1]);

        // array_append(ARRAY<TINYINT>, VARCHAR)
        argTypes = new Type[] {TINYINT_ARRAY, Type.VARCHAR};
        desc = new Function(new FunctionName("array_append"), argTypes, Type.INVALID, false);
        fn = functionSet.getFunction(desc, Function.CompareMode.IS_SUPERTYPE_OF);
        Assert.assertNotNull(fn);
        Assert.assertEquals(VARCHAR_ARRAY, fn.getReturnType());
        Assert.assertEquals(VARCHAR_ARRAY, fn.getArgs()[0]);
        Assert.assertEquals(Type.VARCHAR, fn.getArgs()[1]);

        // array_append(NULL, NULL)
        argTypes = new Type[] {Type.NULL, Type.NULL};
        desc = new Function(new FunctionName("array_append"), argTypes, Type.INVALID, false);
        fn = functionSet.getFunction(desc, Function.CompareMode.IS_SUPERTYPE_OF);
        Assert.assertNotNull(fn);
        Assert.assertEquals(new ArrayType(Type.BOOLEAN), fn.getReturnType());
        Assert.assertEquals(new ArrayType(Type.BOOLEAN), fn.getArgs()[0]);

        // array_append(ARRAY<ARRAY<INT>>, ARRAY<INT>)
        argTypes = new Type[] {INT_ARRAY_ARRAY, INT_ARRAY};
        desc = new Function(new FunctionName("array_append"), argTypes, Type.INVALID, false);
        fn = functionSet.getFunction(desc, Function.CompareMode.IS_SUPERTYPE_OF);
        Assert.assertNotNull(fn);
        Assert.assertEquals(INT_ARRAY_ARRAY, fn.getReturnType());
        Assert.assertEquals(INT_ARRAY_ARRAY, fn.getArgs()[0]);
        Assert.assertEquals(INT_ARRAY, fn.getArgs()[1]);

        // array_append(ARRAY<ARRAY<INT>>, ARRAY<TINYINT>)
        argTypes = new Type[] {INT_ARRAY_ARRAY, TINYINT_ARRAY};
        desc = new Function(new FunctionName("array_append"), argTypes, Type.INVALID, false);
        fn = functionSet.getFunction(desc, Function.CompareMode.IS_SUPERTYPE_OF);
        Assert.assertNotNull(fn);
        Assert.assertEquals(INT_ARRAY_ARRAY, fn.getReturnType());
        Assert.assertEquals(INT_ARRAY_ARRAY, fn.getArgs()[0]);
        Assert.assertEquals(INT_ARRAY, fn.getArgs()[1]);

        // array_append(ARRAY<ARRAY<TINYINT>>, ARRAY<INT>)
        argTypes = new Type[] {TINYINT_ARRAY_ARRAY, INT_ARRAY};
        desc = new Function(new FunctionName("array_append"), argTypes, Type.INVALID, false);
        fn = functionSet.getFunction(desc, Function.CompareMode.IS_SUPERTYPE_OF);
        Assert.assertNotNull(fn);
        Assert.assertEquals(INT_ARRAY_ARRAY, fn.getReturnType());
        Assert.assertEquals(INT_ARRAY_ARRAY, fn.getArgs()[0]);
        Assert.assertEquals(INT_ARRAY, fn.getArgs()[1]);

        // array_append(ARRAY<ARRAY<TINYINT>>, NULL)
        argTypes = new Type[] {TINYINT_ARRAY_ARRAY, Type.NULL};
        desc = new Function(new FunctionName("array_append"), argTypes, Type.INVALID, false);
        fn = functionSet.getFunction(desc, Function.CompareMode.IS_SUPERTYPE_OF);
        Assert.assertNotNull(fn);
        Assert.assertEquals(TINYINT_ARRAY_ARRAY, fn.getReturnType());
        Assert.assertEquals(TINYINT_ARRAY_ARRAY, fn.getArgs()[0]);
        Assert.assertEquals(TINYINT_ARRAY, fn.getArgs()[1]);

        // array_append(NULL, ARRAY<INT>)
        argTypes = new Type[] {Type.NULL, INT_ARRAY};
        desc = new Function(new FunctionName("array_append"), argTypes, Type.INVALID, false);
        fn = functionSet.getFunction(desc, Function.CompareMode.IS_SUPERTYPE_OF);
        Assert.assertNotNull(fn);
        Assert.assertEquals(INT_ARRAY_ARRAY, fn.getReturnType());
        Assert.assertEquals(INT_ARRAY_ARRAY, fn.getArgs()[0]);
        Assert.assertEquals(INT_ARRAY, fn.getArgs()[1]);

        // array_append(ARRAY<ARRAY<TINYINT>>, TINYINT)
        argTypes = new Type[] {TINYINT_ARRAY_ARRAY, Type.TINYINT};
        desc = new Function(new FunctionName("array_append"), argTypes, Type.INVALID, false);
        fn = functionSet.getFunction(desc, Function.CompareMode.IS_SUPERTYPE_OF);
        Assert.assertNull(fn);

        // array_append(ARRAY<ARRAY<TINYINT>>, ARRAY<VARCHAR>)
        argTypes = new Type[] {TINYINT_ARRAY_ARRAY, VARCHAR_ARRAY};
        desc = new Function(new FunctionName("array_append"), argTypes, Type.INVALID, false);
        fn = functionSet.getFunction(desc, Function.CompareMode.IS_SUPERTYPE_OF);
        Assert.assertNotNull(fn);
        Assert.assertEquals(new ArrayType(VARCHAR_ARRAY), fn.getReturnType());
        Assert.assertEquals(new ArrayType(VARCHAR_ARRAY), fn.getArgs()[0]);
        Assert.assertEquals(VARCHAR_ARRAY, fn.getArgs()[1]);

        // array_append(ARRAY<VARCHAR>, VARCHAR)
        argTypes = new Type[] {VARCHAR_ARRAY, Type.VARCHAR};
        desc = new Function(new FunctionName("array_append"), argTypes, Type.INVALID, false);
        fn = functionSet.getFunction(desc, Function.CompareMode.IS_SUPERTYPE_OF);
        Assert.assertNotNull(fn);
        Assert.assertEquals(VARCHAR_ARRAY, fn.getReturnType());
        Assert.assertEquals(VARCHAR_ARRAY, fn.getArgs()[0]);
        Assert.assertEquals(Type.VARCHAR, fn.getArgs()[1]);

        // array_append(ARRAY<VARCHAR>, CHAR)
        argTypes = new Type[] {VARCHAR_ARRAY, Type.CHAR};
        desc = new Function(new FunctionName("array_append"), argTypes, Type.INVALID, false);
        fn = functionSet.getFunction(desc, Function.CompareMode.IS_SUPERTYPE_OF);
        Assert.assertNotNull(fn);
        Assert.assertEquals(VARCHAR_ARRAY, fn.getReturnType());
        Assert.assertEquals(VARCHAR_ARRAY, fn.getArgs()[0]);
        Assert.assertEquals(Type.VARCHAR, fn.getArgs()[1]);

        // array_append(VARCHAR, VARCHAR)
        argTypes = new Type[] {Type.VARCHAR, Type.VARCHAR};
        desc = new Function(new FunctionName("array_append"), argTypes, Type.INVALID, false);
        fn = functionSet.getFunction(desc, Function.CompareMode.IS_SUPERTYPE_OF);
        Assert.assertNull(fn);

        // array_append(INT, VARCHAR)
        argTypes = new Type[] {Type.INT, Type.VARCHAR};
        desc = new Function(new FunctionName("array_append"), argTypes, Type.INVALID, false);
        fn = functionSet.getFunction(desc, Function.CompareMode.IS_SUPERTYPE_OF);
        Assert.assertNull(fn);

        // array_length(INT)
        argTypes = new Type[] {Type.INT};
        desc = new Function(new FunctionName("array_length"), argTypes, Type.INVALID, false);
        fn = functionSet.getFunction(desc, Function.CompareMode.IS_SUPERTYPE_OF);
        Assert.assertNull(fn);

        // array_length(INT)
        argTypes = new Type[] {Type.INT};
        desc = new Function(new FunctionName("array_length"), argTypes, Type.INVALID, false);
        fn = functionSet.getFunction(desc, Function.CompareMode.IS_SUPERTYPE_OF);
        Assert.assertNull(fn);

        // array_length(ARRAY<INT>)
        argTypes = new Type[] {INT_ARRAY};
        desc = new Function(new FunctionName("array_length"), argTypes, Type.INVALID, false);
        fn = functionSet.getFunction(desc, Function.CompareMode.IS_SUPERTYPE_OF);
        Assert.assertNotNull(fn);
        Assert.assertEquals(Type.INT, fn.getReturnType());
        Assert.assertEquals(INT_ARRAY, fn.getArgs()[0]);

        // array_length(ARRAY<ARRAY<INT>>)
        argTypes = new Type[] {INT_ARRAY_ARRAY};
        desc = new Function(new FunctionName("array_length"), argTypes, Type.INVALID, false);
        fn = functionSet.getFunction(desc, Function.CompareMode.IS_SUPERTYPE_OF);
        Assert.assertNotNull(fn);
        Assert.assertEquals(Type.INT, fn.getReturnType());
        Assert.assertEquals(INT_ARRAY_ARRAY, fn.getArgs()[0]);

        // array_length(NULL)
        argTypes = new Type[] {Type.NULL};
        desc = new Function(new FunctionName("array_length"), argTypes, Type.INVALID, false);
        fn = functionSet.getFunction(desc, Function.CompareMode.IS_SUPERTYPE_OF);
        Assert.assertNotNull(fn);
        Assert.assertEquals(Type.INT, fn.getReturnType());
        Assert.assertEquals(new ArrayType(Type.BOOLEAN), fn.getArgs()[0]);

        // array_generate(SmallInt,Int,BigInt)
        argTypes = new Type[] {Type.SMALLINT, Type.INT, Type.BIGINT};
        desc = new Function(new FunctionName("array_generate"), argTypes, Type.INVALID, false);
        fn = functionSet.getFunction(desc, Function.CompareMode.IS_SUPERTYPE_OF);
        Assert.assertNotNull(fn);
        Assert.assertEquals(Type.ARRAY_BIGINT, fn.getReturnType());
        Assert.assertEquals(Type.BIGINT, fn.getArgs()[0]);

    }