class BuiltInFunction extends Function {

    protected final Database database;
    protected final FunctionInfo info;
    private ArrayList<Expression> varArgs;
    protected int dataType, scale;
    protected long precision = PRECISION_UNKNOWN;
    protected int displaySize;

    protected BuiltInFunction(Database database, FunctionInfo info) {
        this.database = database;
        this.info = info;
        if (info.parameterCount == VAR_ARGS) {
            varArgs = new ArrayList<>(4);
        } else {
            args = new Expression[info.parameterCount];
        }
    }

    /**
     * Set the parameter expression at the given index.
     *
     * @param index the index (0, 1,...)
     * @param param the expression
     */
    @Override
    public void setParameter(int index, Expression param) {
        if (varArgs != null) {
            varArgs.add(param);
        } else {
            if (index >= args.length) {
                throw DbException.get(ErrorCode.INVALID_PARAMETER_COUNT_2, info.name, "" + args.length);
            }
            args[index] = param;
        }
    }

    @Override
    public Value getValue(ServerSession session) {
        return getValueWithArgs(session, args);
    }

    @Override
    public ValueResultSet getValueForColumnList(ServerSession session, Expression[] args) {
        return (ValueResultSet) getValueWithArgs(session, args);
    }

    private Value getValueWithArgs(ServerSession session, Expression[] args) {
        Value[] values = new Value[args.length];
        // 如果函数要求所有的参数非null，那么只要有一个参数是null，函数就直接返回null
        if (info.nullIfParameterIsNull) {
            for (int i = 0; i < args.length; i++) {
                Expression e = args[i];
                Value v = e.getValue(session);
                if (v == ValueNull.INSTANCE) {
                    return ValueNull.INSTANCE;
                }
                values[i] = v;
            }
        }
        // 大多数函数只有一个参数，所以if放在最前面
        if (info.parameterCount == 1) {
            Value v = getNullOrValue(session, args, values, 0);
            return getValue1(session, v);
        }
        if (info.parameterCount == 0) {
            return getValue0(session);
        }
        // info.parameterCount > 1 || info.parameterCount == VAR_ARGS
        return getValueN(session, args, values);
    }

    protected Value getValue0(ServerSession session) {
        return null;
    }

    protected Value getValue1(ServerSession session, Value v) {
        return null;
    }

    protected Value getValueN(ServerSession session, Expression[] args, Value[] values) {
        return null;
    }

    protected static Value getNullOrValue(ServerSession session, Expression[] args, Value[] values,
            int i) {<FILL_FUNCTION_BODY>}

    protected DbException getUnsupportedException() {
        return DbException
                .getUnsupportedException("function name: " + info.name + ", type=" + info.type);
    }

    @Override
    public int getType() {
        return dataType;
    }

    /**
     * Check if the parameter count is correct.
     *
     * @param len the number of parameters set
     * @throws DbException if the parameter count is incorrect
     */
    protected abstract void checkParameterCount(int len);

    protected void checkParameterCount(int len, int min, int max) {
        boolean ok = (len >= min) && (len <= max);
        if (!ok) {
            throw DbException.get(ErrorCode.INVALID_PARAMETER_COUNT_2, info.name, min + ".." + max);
        }
    }

    /**
     * This method is called after all the parameters have been set.
     * It checks if the parameter count is correct.
     *
     * @throws DbException if the parameter count is incorrect.
     */
    @Override
    public void doneWithParameters() {
        if (info.parameterCount == VAR_ARGS) {
            int len = varArgs.size();
            checkParameterCount(len);
            args = new Expression[len];
            varArgs.toArray(args);
            varArgs = null;
        } else {
            int len = args.length;
            if (len > 0 && args[len - 1] == null) {
                throw DbException.get(ErrorCode.INVALID_PARAMETER_COUNT_2, info.name, "" + len);
            }
        }
    }

    @Override
    public void setDataType(Column col) {
        dataType = col.getType();
        precision = col.getPrecision();
        displaySize = col.getDisplaySize();
        scale = col.getScale();
    }

    @Override
    public Expression optimize(ServerSession session) {
        boolean allConst = optimizeArgs(session);
        dataType = info.dataType;
        DataType type = DataType.getDataType(dataType);
        precision = PRECISION_UNKNOWN;
        scale = 0;
        displaySize = type.defaultScale;
        if (allConst) {
            Value v = getValue(session);
            return ValueExpression.get(v);
        }
        return this;
    }

    @Override
    public int getScale() {
        return scale;
    }

    @Override
    public long getPrecision() {
        if (precision == PRECISION_UNKNOWN) {
            calculatePrecisionAndDisplaySize();
        }
        return precision;
    }

    @Override
    public int getDisplaySize() {
        if (precision == PRECISION_UNKNOWN) {
            calculatePrecisionAndDisplaySize();
        }
        return displaySize;
    }

    protected void calculatePrecisionAndDisplaySize() {
        DataType type = DataType.getDataType(dataType);
        precision = type.defaultPrecision;
        displaySize = type.defaultDisplaySize;
    }

    @Override
    public String getSQL() {
        StatementBuilder buff = new StatementBuilder(info.name);
        buff.append('(');
        appendArgs(buff);
        return buff.append(')').toString();
    }

    @Override
    public int getFunctionType() {
        return info.type;
    }

    @Override
    public String getName() {
        return info.name;
    }

    @Override
    public int getCost() {
        int cost = 3;
        for (Expression e : args) {
            cost += e.getCost();
        }
        return cost;
    }

    @Override
    public boolean isDeterministic() {
        return info.deterministic;
    }

    @Override
    boolean isBufferResultSetToLocalTemp() {
        return false;
    }

    @Override
    public <R> R accept(ExpressionVisitor<R> visitor) {
        return visitor.visitFunction(this);
    }
}

        if (i >= args.length) {
            return null;
        }
        Value v = values[i];
        if (v == null && args[i] != null) {
            v = values[i] = args[i].getValue(session);
        }
        return v;
    
<methods>public non-sealed void <init>() ,public static void deregisterFunctionFactory(com.lealone.sql.expression.function.FunctionFactory) ,public void doneWithParameters() ,public com.lealone.sql.expression.Expression[] getArgs() ,public static com.lealone.sql.expression.function.Function getFunction(com.lealone.db.Database, java.lang.String) ,public static com.lealone.sql.expression.function.FunctionInfo getFunctionInfo(java.lang.String) ,public abstract int getFunctionType() ,public abstract java.lang.String getSQL() ,public abstract int getType() ,public abstract boolean isDeterministic() ,public abstract com.lealone.sql.expression.Expression optimize(com.lealone.db.session.ServerSession) ,public static void registerFunctionFactory(com.lealone.sql.expression.function.FunctionFactory) ,public void setDataType(com.lealone.db.table.Column) ,public void setParameter(int, com.lealone.sql.expression.Expression) <variables>private static final CopyOnWriteArrayList<com.lealone.sql.expression.function.FunctionFactory> FACTORIES,private static final HashMap<java.lang.String,com.lealone.sql.expression.function.FunctionInfo> FUNCTIONS,protected static final long PRECISION_UNKNOWN,protected static final int VAR_ARGS,protected com.lealone.sql.expression.Expression[] args,private static boolean inited