class ClientResult implements Result {

    protected int fetchSize;
    protected ClientSession session;
    protected TransferInputStream in;
    protected int resultId; // 如果为负数，表示后端没有缓存任何东西
    protected final ClientResultColumn[] columns;
    protected Value[] currentRow;
    protected final int rowCount;
    protected int rowId, rowOffset;
    protected ArrayList<Value[]> result;

    public ClientResult(ClientSession session, TransferInputStream in, int resultId, int columnCount,
            int rowCount, int fetchSize) throws IOException {
        this.session = session;
        this.in = in;
        this.resultId = resultId;
        this.columns = new ClientResultColumn[columnCount];
        this.rowCount = rowCount;
        for (int i = 0; i < columnCount; i++) {
            columns[i] = new ClientResultColumn(in);
        }
        rowId = -1;
        result = Utils.newSmallArrayList();
        this.fetchSize = fetchSize;
        fetchRows(false);
    }

    @Override
    public abstract boolean next();

    protected abstract void fetchRows(boolean sendFetch);

    @Override
    public String getAlias(int i) {
        return columns[i].alias;
    }

    @Override
    public String getSchemaName(int i) {
        return columns[i].schemaName;
    }

    @Override
    public String getTableName(int i) {
        return columns[i].tableName;
    }

    @Override
    public String getColumnName(int i) {
        return columns[i].columnName;
    }

    @Override
    public int getColumnType(int i) {
        return columns[i].columnType;
    }

    @Override
    public long getColumnPrecision(int i) {
        return columns[i].precision;
    }

    @Override
    public int getColumnScale(int i) {
        return columns[i].scale;
    }

    @Override
    public int getDisplaySize(int i) {
        return columns[i].displaySize;
    }

    @Override
    public boolean isAutoIncrement(int i) {
        return columns[i].autoIncrement;
    }

    @Override
    public int getNullable(int i) {
        return columns[i].nullable;
    }

    @Override
    public void reset() {
        rowId = -1;
        currentRow = null;
        if (session == null) {
            return;
        }
        if (resultId > 0) {
            session.checkClosed();
            try {
                session.send(new ResultReset(resultId));
            } catch (Exception e) {
                throw DbException.convert(e);
            }
        }
    }

    @Override
    public Value[] currentRow() {
        return currentRow;
    }

    @Override
    public int getRowId() {
        return rowId;
    }

    @Override
    public int getVisibleColumnCount() {
        return columns.length;
    }

    @Override
    public int getRowCount() {
        return rowCount;
    }

    public int getCurrentRowCount() {
        return result.size();
    }

    // 调度线程和外部线程都会调用
    protected void sendClose() {
        if (session == null) {
            return;
        }
        try {
            if (resultId > 0) {
                if (session != null) {
                    session.send(new ResultClose(resultId));
                    session = null;
                }
            } else {
                session = null;
            }
        } catch (Exception e) {
            session.getTrace().error(e, "close");
        }
    }

    protected void sendFetch(int fetchSize) throws IOException {<FILL_FUNCTION_BODY>}

    @Override
    public void close() {
        result = null;
        sendClose();
    }

    protected void remapIfOld() {
        if (session == null) {
            return;
        }
        try {
            if (resultId > 0
                    && resultId <= session.getCurrentId() - SysProperties.SERVER_CACHED_OBJECTS / 2) {
                // object is too old - we need to map it to a new id
                int newId = session.getNextId();
                session.send(new ResultChangeId(resultId, newId)); // 不需要响应
                resultId = newId;
            }
        } catch (Exception e) {
            throw DbException.convert(e);
        }
    }

    @Override
    public String toString() {
        return "columns: " + columns.length + " rows: " + rowCount + " pos: " + rowId;
    }

    @Override
    public int getFetchSize() {
        return fetchSize;
    }

    @Override
    public void setFetchSize(int fetchSize) {
        this.fetchSize = fetchSize;
    }

    @Override
    public boolean needToClose() {
        return true;
    }
}

        // 释放buffer
        in.closeInputStream();
        JdbcAsyncCallback<Boolean> ac = new JdbcAsyncCallback<>();
        session.<ResultFetchRowsAck> send(new ResultFetchRows(resultId, fetchSize)).onComplete(ar -> {
            if (ar.isSucceeded()) {
                in = (TransferInputStream) ar.getResult().in;
                ac.setAsyncResult(true);
            } else {
                ac.setAsyncResult(ar.getCause());
            }
        });
        ac.get();
    
<no_super_class>