class FunctionTable extends Table {

    private final Function function;
    private final Expression functionExpr;
    private LocalResult cachedResult;
    private Value cachedValue;

    public FunctionTable(Schema schema, ServerSession session, Function function) {
        super(schema, 0, function.getName(), false, true);
        this.function = function;
        functionExpr = function.optimize(session);
        int type = function.getType();
        if (type != Value.RESULT_SET) {
            throw DbException.get(ErrorCode.FUNCTION_MUST_RETURN_RESULT_SET_1, function.getName());
        }
        ValueResultSet template = function.getValueForColumnList(session, function.getArgs());
        if (template == null) {
            throw DbException.get(ErrorCode.FUNCTION_MUST_RETURN_RESULT_SET_1, function.getName());
        }
        ResultSet rs = template.getResultSet();
        try {
            ResultSetMetaData meta = rs.getMetaData();
            int columnCount = meta.getColumnCount();
            Column[] cols = new Column[columnCount];
            for (int i = 0; i < columnCount; i++) {
                cols[i] = new Column(meta.getColumnName(i + 1),
                        DataType.getValueTypeFromResultSet(meta, i + 1), meta.getPrecision(i + 1),
                        meta.getScale(i + 1), meta.getColumnDisplaySize(i + 1));
            }
            setColumns(cols);
        } catch (SQLException e) {
            throw DbException.convert(e);
        }
    }

    @Override
    public TableType getTableType() {
        return TableType.FUNCTION_TABLE;
    }

    @Override
    public Index getScanIndex(ServerSession session) {
        return new FunctionIndex(this, IndexColumn.wrap(columns));
    }

    @Override
    public long getMaxDataModificationId() {
        return database.getModificationDataId();
    }

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

    @Override
    public boolean canReference() {
        return false;
    }

    @Override
    public boolean canDrop() {
        throw DbException.getInternalError();
    }

    @Override
    public boolean canGetRowCount() {
        return false;
    }

    @Override
    public long getRowCount(ServerSession session) {
        return Long.MAX_VALUE;
    }

    @Override
    public long getRowCountApproximation() {
        return Long.MAX_VALUE;
    }

    @Override
    public String getCreateSQL() {
        return null;
    }

    @Override
    public String getDropSQL() {
        return null;
    }

    @Override
    public String getSQL() {
        return function.getSQL();
    }

    /**
     * Read the result from the function. This method buffers the result in a
     * temporary file.
     *
     * @param session the session
     * @return the result
     */
    public Result getResult(ServerSession session) {<FILL_FUNCTION_BODY>}

    /**
     * Read the result set from the function. This method doesn't cache.
     *
     * @param session the session
     * @return the result set
     */
    public ResultSet getResultSet(ServerSession session) {
        ValueResultSet v = getValueResultSet(session);
        return v == null ? null : v.getResultSet();
    }

    private ValueResultSet getValueResultSet(ServerSession session) {
        Value v = functionExpr.getValue(session);
        if (v == ValueNull.INSTANCE) {
            return null;
        }
        return (ValueResultSet) v;
    }

    boolean isBufferResultSetToLocalTemp() {
        return function.isBufferResultSetToLocalTemp();
    }
}

        ValueResultSet v = getValueResultSet(session);
        if (v == null) {
            return null;
        }
        if (cachedResult != null && cachedValue == v) {
            cachedResult.reset();
            return cachedResult;
        }
        ResultSet rs = v.getResultSet();
        LocalResult result = LocalResult.read(session, Expression.getExpressionColumns(session, rs), rs,
                0);
        if (function.isDeterministic()) {
            cachedResult = result;
            cachedValue = v;
        }
        return result;
    
<methods>public void <init>(com.lealone.db.schema.Schema, int, java.lang.String, boolean, boolean) ,public void addConstraint(com.lealone.db.constraint.Constraint) ,public void addDependencies(Set<com.lealone.db.DbObject>) ,public com.lealone.db.index.Index addIndex(com.lealone.db.session.ServerSession, java.lang.String, int, com.lealone.db.index.IndexColumn[], com.lealone.db.index.IndexType, boolean, java.lang.String, com.lealone.db.lock.DbObjectLock) ,public Future<java.lang.Integer> addRow(com.lealone.db.session.ServerSession, com.lealone.db.result.Row) ,public void addSequence(com.lealone.db.schema.Sequence) ,public void addTrigger(com.lealone.db.schema.TriggerObject) ,public void addView(com.lealone.db.table.TableView) ,public void analyze(com.lealone.db.session.ServerSession, int) ,public abstract boolean canDrop() ,public abstract boolean canGetRowCount() ,public boolean canReference() ,public boolean canTruncate() ,public void checkRename() ,public void checkSupportAlter() ,public void checkWritingAllowed() ,public void close(com.lealone.db.session.ServerSession) ,public int compareTypeSafe(com.lealone.db.value.Value, com.lealone.db.value.Value) ,public boolean containsIndex() ,public boolean containsLargeObject() ,public boolean doesColumnExist(java.lang.String) ,public void dropSingleColumnConstraintsAndIndexes(com.lealone.db.session.ServerSession, com.lealone.db.table.Column, com.lealone.db.lock.DbObjectLock) ,public com.lealone.db.table.Column findColumn(java.lang.String) ,public com.lealone.db.index.Index findPrimaryKey() ,public void fire(com.lealone.db.session.ServerSession, int, boolean) ,public void fireAfterRow(com.lealone.db.session.ServerSession, com.lealone.db.result.Row, com.lealone.db.result.Row, boolean) ,public boolean fireBeforeRow(com.lealone.db.session.ServerSession, com.lealone.db.result.Row, com.lealone.db.result.Row) ,public boolean fireRow() ,public boolean getCheckForeignKeyConstraints() ,public List<com.lealone.db.DbObject> getChildren() ,public java.lang.String getCodePath() ,public com.lealone.db.table.Column getColumn(int) ,public com.lealone.db.table.Column getColumn(java.lang.String) ,public com.lealone.db.table.Column[] getColumns() ,public com.lealone.db.value.CompareMode getCompareMode() ,public ArrayList<com.lealone.db.constraint.Constraint> getConstraints() ,public com.lealone.db.DataHandler getDataHandler() ,public com.lealone.db.value.Value getDefaultValue(com.lealone.db.session.ServerSession, com.lealone.db.table.Column) ,public long getDiskSpaceUsed() ,public com.lealone.db.index.Index getIndexForColumn(com.lealone.db.table.Column) ,public ArrayList<com.lealone.db.index.Index> getIndexes() ,public abstract long getMaxDataModificationId() ,public synchronized com.lealone.db.result.Row getNullRow() ,public com.lealone.db.table.Column[] getOldColumns() ,public boolean getOnCommitDrop() ,public boolean getOnCommitTruncate() ,public java.lang.String getPackageName() ,public java.lang.String getParameter(java.lang.String) ,public Map<java.lang.String,java.lang.String> getParameters() ,public com.lealone.db.index.Index getPrimaryKey() ,public ArrayList<com.lealone.db.constraint.ConstraintReferential> getReferentialConstraints() ,public com.lealone.db.result.Row getRow(com.lealone.db.session.ServerSession, long) ,public com.lealone.db.result.Row getRow(com.lealone.db.result.Row) ,public abstract long getRowCount(com.lealone.db.session.ServerSession) ,public abstract long getRowCountApproximation() ,public com.lealone.db.table.Column getRowIdColumn() ,public abstract com.lealone.db.index.Index getScanIndex(com.lealone.db.session.ServerSession) ,public abstract com.lealone.db.table.TableType getTableType() ,public com.lealone.db.result.Row getTemplateRow() ,public com.lealone.db.result.SearchRow getTemplateSimpleRow(boolean) ,public com.lealone.db.DbObjectType getType() ,public int getVersion() ,public ArrayList<com.lealone.db.table.TableView> getViews() ,public boolean hasSelectTrigger() ,public int incrementAndGetVersion() ,public void initVersion() ,public abstract boolean isDeterministic() ,public boolean isGlobalTemporary() ,public boolean isHidden() ,public boolean isPersistData() ,public boolean isPersistIndexes() ,public boolean isRowChanged(com.lealone.db.result.Row) ,public boolean lock(com.lealone.db.session.ServerSession, boolean) ,public void removeChildrenAndResources(com.lealone.db.session.ServerSession, com.lealone.db.lock.DbObjectLock) ,public void removeConstraint(com.lealone.db.constraint.Constraint) ,public void removeIndex(com.lealone.db.index.Index) ,public void removeIndexOrTransferOwnership(com.lealone.db.session.ServerSession, com.lealone.db.index.Index, com.lealone.db.lock.DbObjectLock) ,public Future<java.lang.Integer> removeRow(com.lealone.db.session.ServerSession, com.lealone.db.result.Row) ,public Future<java.lang.Integer> removeRow(com.lealone.db.session.ServerSession, com.lealone.db.result.Row, boolean) ,public void removeSequence(com.lealone.db.schema.Sequence) ,public void removeTrigger(com.lealone.db.schema.TriggerObject) ,public void removeView(com.lealone.db.table.TableView) ,public void rename(java.lang.String) ,public void renameColumn(com.lealone.db.table.Column, java.lang.String) ,public void repair(com.lealone.db.session.ServerSession) ,public void setCheckForeignKeyConstraints(com.lealone.db.session.ServerSession, boolean, boolean) ,public void setCodePath(java.lang.String) ,public void setHidden(boolean) ,public void setNewColumns(com.lealone.db.table.Column[]) ,public void setOnCommitDrop(boolean) ,public void setOnCommitTruncate(boolean) ,public void setPackageName(java.lang.String) ,public void truncate(com.lealone.db.session.ServerSession) ,public boolean tryExclusiveLock(com.lealone.db.session.ServerSession) ,public int tryLockRow(com.lealone.db.session.ServerSession, com.lealone.db.result.Row, int[]) ,public boolean trySharedLock(com.lealone.db.session.ServerSession) ,public Future<java.lang.Integer> updateRow(com.lealone.db.session.ServerSession, com.lealone.db.result.Row, com.lealone.db.result.Row, int[]) ,public Future<java.lang.Integer> updateRow(com.lealone.db.session.ServerSession, com.lealone.db.result.Row, com.lealone.db.result.Row, int[], boolean) ,public void validateConvertUpdateSequence(com.lealone.db.session.ServerSession, com.lealone.db.result.Row) <variables>public static final int TYPE_CACHED,public static final int TYPE_MEMORY,private boolean checkForeignKeyConstraints,private java.lang.String codePath,private final non-sealed HashMap<java.lang.String,com.lealone.db.table.Column> columnMap,protected com.lealone.db.table.Column[] columns,private final non-sealed com.lealone.db.value.CompareMode compareMode,private ArrayList<com.lealone.db.constraint.Constraint> constraints,private final com.lealone.db.lock.DbObjectLock dbObjectLock,protected boolean isHidden,private com.lealone.db.result.Row nullRow,private boolean onCommitDrop,private boolean onCommitTruncate,private java.lang.String packageName,private final non-sealed boolean persistData,private final non-sealed boolean persistIndexes,private ArrayList<com.lealone.db.schema.Sequence> sequences,private ArrayList<com.lealone.db.schema.TriggerObject> triggers,private int version,private ArrayList<com.lealone.db.table.TableView> views