class Function extends Expression {

    protected static final int VAR_ARGS = -1;
    protected static final long PRECISION_UNKNOWN = -1;
    private static final HashMap<String, FunctionInfo> FUNCTIONS = new HashMap<>();

    private static final CopyOnWriteArrayList<FunctionFactory> FACTORIES = new CopyOnWriteArrayList<>();
    private static boolean inited;

    // 放在FACTORIES字段之后，否则FACTORIES为null
    static {
        BuiltInFunctionFactory.register();
    }

    public static void registerFunctionFactory(FunctionFactory factory) {
        FACTORIES.add(factory);
    }

    public static void deregisterFunctionFactory(FunctionFactory factory) {
        FACTORIES.remove(factory);
    }

    private synchronized static void initFunctionFactories() {
        if (inited)
            return;
        for (FunctionFactory factory : FACTORIES) {
            factory.init();
        }
        inited = true;
    }

    protected static FunctionInfo addFunction(String name, int type, int parameterCount, int dataType,
            boolean nullIfParameterIsNull, boolean deterministic) {<FILL_FUNCTION_BODY>}

    protected static FunctionInfo addFunctionNotDeterministic(String name, int type, int parameterCount,
            int dataType) {
        return addFunction(name, type, parameterCount, dataType, true, false);
    }

    protected static FunctionInfo addFunction(String name, int type, int parameterCount, int dataType) {
        return addFunction(name, type, parameterCount, dataType, true, true);
    }

    protected static FunctionInfo addFunctionWithNull(String name, int type, int parameterCount,
            int dataType) {
        return addFunction(name, type, parameterCount, dataType, false, true);
    }

    // 允许一个函数有多个别名
    protected static FunctionInfo addFunction(String name, FunctionInfo info) {
        FUNCTIONS.put(name, info);
        return info;
    }

    /**
     * Get the function info object for this function, or null if there is no
     * such function.
     *
     * @param name the function name
     * @return the function info
     */
    public static FunctionInfo getFunctionInfo(String name) {
        FunctionInfo info = FUNCTIONS.get(name);
        if (info == null && !inited) {
            initFunctionFactories();
            info = FUNCTIONS.get(name);
        }
        return info;
    }

    /**
     * Get an instance of the given function for this database.
     * If no function with this name is found, null is returned.
     *
     * @param database the database
     * @param name the function name
     * @return the function object or null
     */
    public static Function getFunction(Database database, String name) {
        if (!database.getSettings().databaseToUpper) {
            // if not yet converted to uppercase, do it now
            name = StringUtils.toUpperEnglish(name);
        }
        FunctionInfo info = getFunctionInfo(name);
        if (info == null) {
            return null;
        }
        if (info.factory != null)
            return info.factory.createFunction(database, info);
        else
            return BuiltInFunctionFactory.INSTANCE.createFunction(database, info);
    }

    protected Expression[] args;

    /**
     * Get the function arguments.
     *
     * @return argument list
     */
    public Expression[] getArgs() {
        return args;
    }

    protected boolean optimizeArgs(ServerSession session) {
        boolean allConst = isDeterministic();
        for (int i = 0; i < args.length; i++) {
            Expression e = args[i];
            if (e == null) {
                continue;
            }
            e = e.optimize(session);
            args[i] = e;
            if (!e.isConstant()) {
                allConst = false;
            }
        }
        return allConst;
    }

    protected void appendArgs(StatementBuilder buff) {
        for (Expression e : args) {
            buff.appendExceptFirst(", ");
            buff.append(e.getSQL());
        }
    }

    /**
     * Get the name of the function.
     *
     * @return the name
     */
    abstract String getName();

    /**
     * Get an empty result set with the column names set.
     *
     * @param session the session
     * @param args the argument list (some arguments may be null)
     * @return the empty result set
     */
    abstract ValueResultSet getValueForColumnList(ServerSession session, Expression[] args);

    /**
     * Get the data type.
     *
     * @return the data type
     */
    @Override
    public abstract int getType();

    /**
     * Optimize the function if possible.
     *
     * @param session the session
     * @return the optimized expression
     */
    @Override
    public abstract Expression optimize(ServerSession session);

    /**
     * Get the SQL snippet of the function (including arguments).
     *
     * @return the SQL snippet.
     */
    @Override
    public abstract String getSQL();

    public void setParameter(int index, Expression param) {
    }

    public void doneWithParameters() {
    }

    public void setDataType(Column col) {
    }

    public abstract int getFunctionType();

    /**
     * Whether the function always returns the same result for the same
     * parameters.
     *
     * @return true if it does
     */
    public abstract boolean isDeterministic();

    /**
     * Should the return value ResultSet be buffered in a local temporary file?
     *
     * @return true if it should be.
     */
    abstract boolean isBufferResultSetToLocalTemp();
}

        FunctionInfo info = new FunctionInfo();
        info.name = name;
        info.type = type;
        info.parameterCount = parameterCount;
        info.dataType = dataType;
        info.nullIfParameterIsNull = nullIfParameterIsNull;
        info.deterministic = deterministic;
        FUNCTIONS.put(name, info);
        return info;
    
<methods>public non-sealed void <init>() ,public R accept(ExpressionVisitor<R>) ,public void addFilterConditions(com.lealone.sql.optimizer.TableFilter, boolean) ,public void createIndexConditions(com.lealone.db.session.ServerSession, com.lealone.sql.optimizer.TableFilter) ,public java.lang.String getAlias() ,public boolean getBooleanValue(com.lealone.db.session.ServerSession) ,public java.lang.String getColumnName() ,public void getColumns(Set<?>) ,public abstract int getCost() ,public void getDependencies(Set<?>) ,public abstract int getDisplaySize() ,public com.lealone.sql.expression.Expression[] getExpressionColumns(com.lealone.db.session.ServerSession) ,public static com.lealone.sql.expression.Expression[] getExpressionColumns(com.lealone.db.session.ServerSession, com.lealone.db.value.ValueArray) ,public static com.lealone.sql.expression.Expression[] getExpressionColumns(com.lealone.db.session.ServerSession, java.sql.ResultSet) ,public com.lealone.sql.expression.Expression getNonAliasExpression() ,public com.lealone.sql.expression.Expression getNotIfPossible(com.lealone.db.session.ServerSession) ,public int getNullable() ,public abstract long getPrecision() ,public abstract java.lang.String getSQL() ,public abstract int getScale() ,public java.lang.String getSchemaName() ,public java.lang.String getTableName() ,public abstract int getType() ,public com.lealone.db.value.Value getValue(com.lealone.db.session.Session) ,public abstract com.lealone.db.value.Value getValue(com.lealone.db.session.ServerSession) ,public boolean isAutoIncrement() ,public boolean isConstant() ,public boolean isEvaluatable() ,public boolean isValueSet() ,public boolean isWildcard() ,public void mapColumns(com.lealone.sql.optimizer.ColumnResolver, int) ,public abstract com.lealone.sql.expression.Expression optimize(com.lealone.db.session.ServerSession) ,public com.lealone.sql.expression.Expression optimize(com.lealone.db.session.Session) ,public java.lang.String toString() ,public void updateAggregate(com.lealone.db.session.ServerSession) <variables>private boolean addedToFilter