Hi Steve,
Very interesting question. The main reason why we're "auto-closing" cursors is to ensure that ExecuteListener events are fired at the right moment and with all use-cases of using lazy fetching, including using the Cursor as an Iterator. The relevant GitHub issues are referenced from the call to close() within org.jooq.impl.CursorImpl.CursorIterator:
// [#1868] [#2373] [#2385] This calls through to Utils.safeClose()
// if necessary, lazy-terminating the ExecuteListener lifecycle if
// the result is not eager-fetched.
if (record == null) {
CursorImpl.this.close();
}
Changing this behaviour is an option, of course - at least in a major release.
In a minor release, we might introduce a new flag on ResultQuery, similar to the existing Query.keepStatement() flag (which keeps open PreparedStatements). A ResultQuery.keepResultSet() flag, for instance.
Right now, there is an option for you to work around this issue. You can inject a proxy ResultSet that ignores the above close() call. You can do this by implementing an ExecuteListener's executeEnd() method, and setting the proxy on the ExecuteContext.resultSet() method. The AbstractResultQuery.execute() method shows how this works:
listener.executeStart(ctx);
if (ctx.statement().execute()) {
ctx.resultSet(ctx.statement().getResultSet());
}
listener.executeEnd(ctx);
// Fetch a single result set
if (!many) {
if (ctx.resultSet() != null) {
Field<?>[] fields = getFields(ctx.resultSet().getMetaData());
cursor = new CursorImpl<R>(ctx, listener, fields, intern.internIndexes(fields), keepStatement(), keepResultSet(), getRecordType());
if (!lazy) {
result = cursor.fetch();
cursor = null;
}
}
else {
result = new ResultImpl<R>(ctx.configuration());
}
}
Let me know if that's a viable option for you, for the time being.