Code review comment: how about add a speical process in `SqlToScalarOperatorTranslator.visitFunctionCall()`. We transform it to `typeof_internal` and set the new argument here. @Seaven  @kangkaisen

Code under review:
public Void visitFunctionCall(FunctionCallExpr node, Scope scope) {
            Type[] argumentTypes = node.getChildren().stream().map(Expr::getType).toArray(Type[]::new);

            if (node.isNondeterministicBuiltinFnName()) {
                ExprId exprId = analyzeState.getNextNondeterministicId();
                node.setNondeterministicId(exprId);
            }

            Function fn;
            String fnName = node.getFnName().getFunction();

            
            checkFunction(fnName, node, argumentTypes);
            if (fnName.equalsIgnoreCase("typeof") && argumentTypes.length == 1) {
                
                
                
                
                
                
                Type originType = argumentTypes[0];
                argumentTypes[0] = Type.STRING;
                fn = new Function(new FunctionName("typeof_internal"), argumentTypes, Type.STRING, false);
                node.setParams(Arrays.asList(new StringLiteral(originType.toTypeString())));
            } else if (fnName.equals(FunctionSet.COUNT) && node.getParams().isDistinct()) {
                
                
                fn = Expr.getBuiltinFunction(FunctionSet.COUNT, new Type[] {argumentTypes[0]},
                        Function.CompareMode.IS_NONSTRICT_SUPERTYPE_OF);
            } else if (fnName.equals(FunctionSet.EXCHANGE_BYTES) || fnName.equals(FunctionSet.EXCHANGE_SPEED)) {
                fn = Expr.getBuiltinFunction(fnName, argumentTypes, Function.CompareMode.IS_NONSTRICT_SUPERTYPE_OF);
                fn = fn.copy();
                fn.setArgsType(argumentTypes); 
                fn.setIsNullable(false);
            } else if (fnName.equals(FunctionSet.ARRAY_AGG) || fnName.equals(FunctionSet.GROUP_CONCAT)) {
                
                fn = Expr.getBuiltinFunction(fnName, new Type[] {argumentTypes[0]},
                        Function.CompareMode.IS_NONSTRICT_SUPERTYPE_OF);
                fn = fn.copy();
                List<OrderByElement> orderByElements = node.getParams().getOrderByElements();
                List<Boolean> isAscOrder = new ArrayList<>();
                List<Boolean> nullsFirst = new ArrayList<>();
                if (orderByElements != null) {
                    for (OrderByElement elem : orderByElements) {
                        isAscOrder.add(elem.getIsAsc());
                        nullsFirst.add(elem.getNullsFirstParam());
                    }
                }
                Type[] argsTypes = new Type[argumentTypes.length];
                for (int i = 0; i < argumentTypes.length; ++i) {
                    argsTypes[i] = argumentTypes[i] == Type.NULL ? Type.BOOLEAN : argumentTypes[i];
                    if (fnName.equals(FunctionSet.GROUP_CONCAT) && i < node.getChildren().size() - isAscOrder.size()) {
                        argsTypes[i] = Type.VARCHAR;
                    }
                }
                fn.setArgsType(argsTypes); 
                ArrayList<Type> structTypes = new ArrayList<>(argsTypes.length);
                for (Type t : argsTypes) {
                    structTypes.add(new ArrayType(t));
                }
                ((AggregateFunction) fn).setIntermediateType(new StructType(structTypes));
                ((AggregateFunction) fn).setIsAscOrder(isAscOrder);
                ((AggregateFunction) fn).setNullsFirst(nullsFirst);
                boolean outputConst = true;
                if (fnName.equals(FunctionSet.ARRAY_AGG)) {
                    fn.setRetType(new ArrayType(argsTypes[0]));     
                    outputConst = node.getChild(0).isConstant();
                } else {
                    fn.setRetType(Type.VARCHAR);
                    for (int i = 0; i < node.getChildren().size() - isAscOrder.size() - 1; i++) {
                        if (!node.getChild(i).isConstant()) {
                            outputConst = false;
                            break;
                        }
                    }
                }
                
                ((AggregateFunction) fn).setIsDistinct(node.getParams().isDistinct() &&
                        (!isAscOrder.isEmpty() || outputConst));
            } else if (FunctionSet.PERCENTILE_DISC.equals(fnName)) {
                argumentTypes[1] = Type.DOUBLE;
                fn = Expr.getBuiltinFunction(fnName, argumentTypes, Function.CompareMode.IS_IDENTICAL);
                
                if (fn.getArgs()[0].isDecimalV3()) {
                    List<Type> argTypes = Arrays.asList(argumentTypes[0], fn.getArgs()[1]);

                    AggregateFunction newFn = new AggregateFunction(fn.getFunctionName(), argTypes, argumentTypes[0],
                            ((AggregateFunction) fn).getIntermediateType(), fn.hasVarArgs());

                    newFn.setFunctionId(fn.getFunctionId());
                    newFn.setChecksum(fn.getChecksum());
                    newFn.setBinaryType(fn.getBinaryType());
                    newFn.setHasVarArgs(fn.hasVarArgs());
                    newFn.setId(fn.getId());
                    newFn.setUserVisible(fn.isUserVisible());
                    newFn.setisAnalyticFn(((AggregateFunction) fn).isAnalyticFn());

                    fn = newFn;
                }
            } else if (FunctionSet.CONCAT.equals(fnName) && node.getChildren().stream().anyMatch(child ->
                    child.getType().isArrayType())) {
                List<Type> arrayTypes = Arrays.stream(argumentTypes).map(argumentType -> {
                    if (argumentType.isArrayType()) {
                        return argumentType;
                    } else {
                        return new ArrayType(argumentType);
                    }
                }).collect(Collectors.toList());
                
                TypeManager.getCommonSuperType(arrayTypes);
                for (int i = 0; i < argumentTypes.length; ++i) {
                    if (!argumentTypes[i].isArrayType()) {
                        node.setChild(i, new ArrayExpr(new ArrayType(argumentTypes[i]),
                                Lists.newArrayList(node.getChild(i))));
                    }
                }

                argumentTypes = node.getChildren().stream().map(Expr::getType).toArray(Type[]::new);
                node.resetFnName(null, FunctionSet.ARRAY_CONCAT);
                if (DecimalV3FunctionAnalyzer.argumentTypeContainDecimalV3(FunctionSet.ARRAY_CONCAT, argumentTypes)) {
                    fn = DecimalV3FunctionAnalyzer.getDecimalV3Function(session, node, argumentTypes);
                } else {
                    fn = Expr.getBuiltinFunction(FunctionSet.ARRAY_CONCAT, argumentTypes,
                            Function.CompareMode.IS_NONSTRICT_SUPERTYPE_OF);
                }
            } else if (FunctionSet.NAMED_STRUCT.equals(fnName)) {
                
                fn = Expr.getBuiltinFunction(FunctionSet.NAMED_STRUCT, argumentTypes,
                        Function.CompareMode.IS_NONSTRICT_SUPERTYPE_OF);
                fn = fn.copy();
                ArrayList<StructField> sf = Lists.newArrayList();
                for (int i = 0; i < node.getChildren().size(); i = i + 2) {
                    StringLiteral literal = (StringLiteral) node.getChild(i);
                    sf.add(new StructField(literal.getStringValue(), node.getChild(i + 1).getType()));
                }
                fn.setRetType(new StructType(sf));
            } else if (DecimalV3FunctionAnalyzer.argumentTypeContainDecimalV3(fnName, argumentTypes)) {
                
                
                
                
                
                fn = DecimalV3FunctionAnalyzer.getDecimalV3Function(session, node, argumentTypes);
            } else if (Arrays.stream(argumentTypes).anyMatch(arg -> arg.matchesType(Type.TIME))) {
                fn = Expr.getBuiltinFunction(fnName, argumentTypes, Function.CompareMode.IS_NONSTRICT_SUPERTYPE_OF);
                if (fn instanceof AggregateFunction) {
                    throw new SemanticException("Time Type can not used in" + fnName + " function", node.getPos());
                }
            } else if (FunctionSet.STR_TO_DATE.equals(fnName)) {
                fn = getStrToDateFunction(node, argumentTypes);
            } else if (FunctionSet.ARRAY_GENERATE.equals(fnName)) {
                fn = getArrayGenerateFunction(node);
                argumentTypes = node.getChildren().stream().map(Expr::getType).toArray(Type[]::new);
            } else if (DecimalV3FunctionAnalyzer.argumentTypeContainDecimalV2(fnName, argumentTypes)) {
                fn = DecimalV3FunctionAnalyzer.getDecimalV2Function(node, argumentTypes);
            } else if (FunctionSet.BITMAP_UNION.equals(fnName)) {
                fn = Expr.getBuiltinFunction(fnName, argumentTypes, Function.CompareMode.IS_IDENTICAL);
                if (session.getSessionVariable().isEnableRewriteBitmapUnionToBitmapAgg() &&
                        node.getChild(0) instanceof FunctionCallExpr) {
                    FunctionCallExpr arg0Func = (FunctionCallExpr) node.getChild(0);
                    
                    
                    if (FunctionSet.TO_BITMAP.equals(arg0Func.getFnName().getFunction())) {
                        Expr toBitmapArg0 = arg0Func.getChild(0);
                        Type toBitmapArg0Type = toBitmapArg0.getType();
                        if (toBitmapArg0Type.isIntegerType() || toBitmapArg0Type.isBoolean()
                                || toBitmapArg0Type.isLargeIntType()) {
                            node.setChild(0, toBitmapArg0);
                            node.resetFnName("", FunctionSet.BITMAP_AGG);
                        }
                    }
                }
            } else {
                fn = Expr.getBuiltinFunction(fnName, argumentTypes, Function.CompareMode.IS_NONSTRICT_SUPERTYPE_OF);
            }

            if (fn == null) {
                fn = AnalyzerUtils.getUdfFunction(session, node.getFnName(), argumentTypes);
            }
            if (fn == null) {
                fn = ScalarOperatorEvaluator.INSTANCE.getMetaFunction(node.getFnName(), argumentTypes);
            }

            if (fn == null) {
                String msg = String.format("No matching function with signature: %s(%s)",
                        fnName,
                        node.getParams().isStar() ? "*" : Joiner.on(", ")
                                .join(Arrays.stream(argumentTypes).map(Type::toSql).collect(Collectors.toList())));
                throw new SemanticException(msg, node.getPos());
            }

            if (fn instanceof TableFunction) {
                throw new SemanticException("Table function cannot be used in expression", node.getPos());
            }

            for (int i = 0; i < fn.getNumArgs(); i++) {
                if (!argumentTypes[i].matchesType(fn.getArgs()[i]) &&
                        !Type.canCastTo(argumentTypes[i], fn.getArgs()[i])) {
                    String msg = String.format("No matching function with signature: %s(%s)", fnName,
                            node.getParams().isStar() ? "*" :
                                    Arrays.stream(argumentTypes).map(Type::toSql).collect(Collectors.joining(", ")));
                    throw new SemanticException(msg, node.getPos());
                }
            }

            if (fn.hasVarArgs()) {
                Type varType = fn.getArgs()[fn.getNumArgs() - 1];
                for (int i = fn.getNumArgs(); i < argumentTypes.length; i++) {
                    if (!argumentTypes[i].matchesType(varType) &&
                            !Type.canCastTo(argumentTypes[i], varType)) {
                        String msg = String.format("Variadic function %s(%s) can't support type: %s", fnName,
                                Arrays.stream(fn.getArgs()).map(Type::toSql).collect(Collectors.joining(", ")),
                                argumentTypes[i]);
                        throw new SemanticException(msg, node.getPos());
                    }
                }
            }

            node.setFn(fn);
            node.setType(fn.getReturnType());
            FunctionAnalyzer.analyze(node);
            return null;
        }

Line commented on: node.setParams(Arrays.asList(new StringLiteral(originType.toTypeString())));

Revised code:
public Void visitFunctionCall(FunctionCallExpr node, Scope scope) {
            Type[] argumentTypes = node.getChildren().stream().map(Expr::getType).toArray(Type[]::new);

            if (node.isNondeterministicBuiltinFnName()) {
                ExprId exprId = analyzeState.getNextNondeterministicId();
                node.setNondeterministicId(exprId);
            }

            Function fn;
            String fnName = node.getFnName().getFunction();

            
            checkFunction(fnName, node, argumentTypes);
            if (fnName.equalsIgnoreCase("typeof") && argumentTypes.length == 1) {
                
                
                
                
                
                
                Type originType = argumentTypes[0];
                argumentTypes[0] = Type.STRING;
                fn = new Function(new FunctionName("typeof_internal"), argumentTypes, Type.STRING, false);
                Expr newChildExpr = new StringLiteral(originType.toTypeString());
                node.getParams().exprs().set(0, newChildExpr);
                node.setChild(0, newChildExpr);
            } else if (fnName.equals(FunctionSet.COUNT) && node.getParams().isDistinct()) {
                
                
                fn = Expr.getBuiltinFunction(FunctionSet.COUNT, new Type[] {argumentTypes[0]},
                        Function.CompareMode.IS_NONSTRICT_SUPERTYPE_OF);
            } else if (fnName.equals(FunctionSet.EXCHANGE_BYTES) || fnName.equals(FunctionSet.EXCHANGE_SPEED)) {
                fn = Expr.getBuiltinFunction(fnName, argumentTypes, Function.CompareMode.IS_NONSTRICT_SUPERTYPE_OF);
                fn = fn.copy();
                fn.setArgsType(argumentTypes); 
                fn.setIsNullable(false);
            } else if (fnName.equals(FunctionSet.ARRAY_AGG) || fnName.equals(FunctionSet.GROUP_CONCAT)) {
                
                fn = Expr.getBuiltinFunction(fnName, new Type[] {argumentTypes[0]},
                        Function.CompareMode.IS_NONSTRICT_SUPERTYPE_OF);
                fn = fn.copy();
                List<OrderByElement> orderByElements = node.getParams().getOrderByElements();
                List<Boolean> isAscOrder = new ArrayList<>();
                List<Boolean> nullsFirst = new ArrayList<>();
                if (orderByElements != null) {
                    for (OrderByElement elem : orderByElements) {
                        isAscOrder.add(elem.getIsAsc());
                        nullsFirst.add(elem.getNullsFirstParam());
                    }
                }
                Type[] argsTypes = new Type[argumentTypes.length];
                for (int i = 0; i < argumentTypes.length; ++i) {
                    argsTypes[i] = argumentTypes[i] == Type.NULL ? Type.BOOLEAN : argumentTypes[i];
                    if (fnName.equals(FunctionSet.GROUP_CONCAT) && i < node.getChildren().size() - isAscOrder.size()) {
                        argsTypes[i] = Type.VARCHAR;
                    }
                }
                fn.setArgsType(argsTypes); 
                ArrayList<Type> structTypes = new ArrayList<>(argsTypes.length);
                for (Type t : argsTypes) {
                    structTypes.add(new ArrayType(t));
                }
                ((AggregateFunction) fn).setIntermediateType(new StructType(structTypes));
                ((AggregateFunction) fn).setIsAscOrder(isAscOrder);
                ((AggregateFunction) fn).setNullsFirst(nullsFirst);
                boolean outputConst = true;
                if (fnName.equals(FunctionSet.ARRAY_AGG)) {
                    fn.setRetType(new ArrayType(argsTypes[0]));     
                    outputConst = node.getChild(0).isConstant();
                } else {
                    fn.setRetType(Type.VARCHAR);
                    for (int i = 0; i < node.getChildren().size() - isAscOrder.size() - 1; i++) {
                        if (!node.getChild(i).isConstant()) {
                            outputConst = false;
                            break;
                        }
                    }
                }
                
                ((AggregateFunction) fn).setIsDistinct(node.getParams().isDistinct() &&
                        (!isAscOrder.isEmpty() || outputConst));
            } else if (FunctionSet.PERCENTILE_DISC.equals(fnName)) {
                argumentTypes[1] = Type.DOUBLE;
                fn = Expr.getBuiltinFunction(fnName, argumentTypes, Function.CompareMode.IS_IDENTICAL);
                
                if (fn.getArgs()[0].isDecimalV3()) {
                    List<Type> argTypes = Arrays.asList(argumentTypes[0], fn.getArgs()[1]);

                    AggregateFunction newFn = new AggregateFunction(fn.getFunctionName(), argTypes, argumentTypes[0],
                            ((AggregateFunction) fn).getIntermediateType(), fn.hasVarArgs());

                    newFn.setFunctionId(fn.getFunctionId());
                    newFn.setChecksum(fn.getChecksum());
                    newFn.setBinaryType(fn.getBinaryType());
                    newFn.setHasVarArgs(fn.hasVarArgs());
                    newFn.setId(fn.getId());
                    newFn.setUserVisible(fn.isUserVisible());
                    newFn.setisAnalyticFn(((AggregateFunction) fn).isAnalyticFn());

                    fn = newFn;
                }
            } else if (FunctionSet.CONCAT.equals(fnName) && node.getChildren().stream().anyMatch(child ->
                    child.getType().isArrayType())) {
                List<Type> arrayTypes = Arrays.stream(argumentTypes).map(argumentType -> {
                    if (argumentType.isArrayType()) {
                        return argumentType;
                    } else {
                        return new ArrayType(argumentType);
                    }
                }).collect(Collectors.toList());
                
                TypeManager.getCommonSuperType(arrayTypes);
                for (int i = 0; i < argumentTypes.length; ++i) {
                    if (!argumentTypes[i].isArrayType()) {
                        node.setChild(i, new ArrayExpr(new ArrayType(argumentTypes[i]),
                                Lists.newArrayList(node.getChild(i))));
                    }
                }

                argumentTypes = node.getChildren().stream().map(Expr::getType).toArray(Type[]::new);
                node.resetFnName(null, FunctionSet.ARRAY_CONCAT);
                if (DecimalV3FunctionAnalyzer.argumentTypeContainDecimalV3(FunctionSet.ARRAY_CONCAT, argumentTypes)) {
                    fn = DecimalV3FunctionAnalyzer.getDecimalV3Function(session, node, argumentTypes);
                } else {
                    fn = Expr.getBuiltinFunction(FunctionSet.ARRAY_CONCAT, argumentTypes,
                            Function.CompareMode.IS_NONSTRICT_SUPERTYPE_OF);
                }
            } else if (FunctionSet.NAMED_STRUCT.equals(fnName)) {
                
                fn = Expr.getBuiltinFunction(FunctionSet.NAMED_STRUCT, argumentTypes,
                        Function.CompareMode.IS_NONSTRICT_SUPERTYPE_OF);
                fn = fn.copy();
                ArrayList<StructField> sf = Lists.newArrayList();
                for (int i = 0; i < node.getChildren().size(); i = i + 2) {
                    StringLiteral literal = (StringLiteral) node.getChild(i);
                    sf.add(new StructField(literal.getStringValue(), node.getChild(i + 1).getType()));
                }
                fn.setRetType(new StructType(sf));
            } else if (DecimalV3FunctionAnalyzer.argumentTypeContainDecimalV3(fnName, argumentTypes)) {
                
                
                
                
                
                fn = DecimalV3FunctionAnalyzer.getDecimalV3Function(session, node, argumentTypes);
            } else if (Arrays.stream(argumentTypes).anyMatch(arg -> arg.matchesType(Type.TIME))) {
                fn = Expr.getBuiltinFunction(fnName, argumentTypes, Function.CompareMode.IS_NONSTRICT_SUPERTYPE_OF);
                if (fn instanceof AggregateFunction) {
                    throw new SemanticException("Time Type can not used in" + fnName + " function", node.getPos());
                }
            } else if (FunctionSet.STR_TO_DATE.equals(fnName)) {
                fn = getStrToDateFunction(node, argumentTypes);
            } else if (FunctionSet.ARRAY_GENERATE.equals(fnName)) {
                fn = getArrayGenerateFunction(node);
                argumentTypes = node.getChildren().stream().map(Expr::getType).toArray(Type[]::new);
            } else if (DecimalV3FunctionAnalyzer.argumentTypeContainDecimalV2(fnName, argumentTypes)) {
                fn = DecimalV3FunctionAnalyzer.getDecimalV2Function(node, argumentTypes);
            } else if (FunctionSet.BITMAP_UNION.equals(fnName)) {
                fn = Expr.getBuiltinFunction(fnName, argumentTypes, Function.CompareMode.IS_IDENTICAL);
                if (session.getSessionVariable().isEnableRewriteBitmapUnionToBitmapAgg() &&
                        node.getChild(0) instanceof FunctionCallExpr) {
                    FunctionCallExpr arg0Func = (FunctionCallExpr) node.getChild(0);
                    
                    
                    if (FunctionSet.TO_BITMAP.equals(arg0Func.getFnName().getFunction())) {
                        Expr toBitmapArg0 = arg0Func.getChild(0);
                        Type toBitmapArg0Type = toBitmapArg0.getType();
                        if (toBitmapArg0Type.isIntegerType() || toBitmapArg0Type.isBoolean()
                                || toBitmapArg0Type.isLargeIntType()) {
                            node.setChild(0, toBitmapArg0);
                            node.resetFnName("", FunctionSet.BITMAP_AGG);
                        }
                    }
                }
            } else {
                fn = Expr.getBuiltinFunction(fnName, argumentTypes, Function.CompareMode.IS_NONSTRICT_SUPERTYPE_OF);
            }

            if (fn == null) {
                fn = AnalyzerUtils.getUdfFunction(session, node.getFnName(), argumentTypes);
            }
            if (fn == null) {
                fn = ScalarOperatorEvaluator.INSTANCE.getMetaFunction(node.getFnName(), argumentTypes);
            }

            if (fn == null) {
                String msg = String.format("No matching function with signature: %s(%s)",
                        fnName,
                        node.getParams().isStar() ? "*" : Joiner.on(", ")
                                .join(Arrays.stream(argumentTypes).map(Type::toSql).collect(Collectors.toList())));
                throw new SemanticException(msg, node.getPos());
            }

            if (fn instanceof TableFunction) {
                throw new SemanticException("Table function cannot be used in expression", node.getPos());
            }

            for (int i = 0; i < fn.getNumArgs(); i++) {
                if (!argumentTypes[i].matchesType(fn.getArgs()[i]) &&
                        !Type.canCastTo(argumentTypes[i], fn.getArgs()[i])) {
                    String msg = String.format("No matching function with signature: %s(%s)", fnName,
                            node.getParams().isStar() ? "*" :
                                    Arrays.stream(argumentTypes).map(Type::toSql).collect(Collectors.joining(", ")));
                    throw new SemanticException(msg, node.getPos());
                }
            }

            if (fn.hasVarArgs()) {
                Type varType = fn.getArgs()[fn.getNumArgs() - 1];
                for (int i = fn.getNumArgs(); i < argumentTypes.length; i++) {
                    if (!argumentTypes[i].matchesType(varType) &&
                            !Type.canCastTo(argumentTypes[i], varType)) {
                        String msg = String.format("Variadic function %s(%s) can't support type: %s", fnName,
                                Arrays.stream(fn.getArgs()).map(Type::toSql).collect(Collectors.joining(", ")),
                                argumentTypes[i]);
                        throw new SemanticException(msg, node.getPos());
                    }
                }
            }

            node.setFn(fn);
            node.setType(fn.getReturnType());
            FunctionAnalyzer.analyze(node);
            return null;
        }