Why this matters

The bug. The JDBC spec leaves it implementation-defined whether closing a Statement closes its ResultSet. Postgres' driver does, Oracle's used to leak in older versions, embedded drivers vary. The only safe assumption is: every JDBC resource you open, you close.

The fix. Close rs before ps, or — better — switch to try-with-resources so the compiler enforces it:

try (PreparedStatement ps = ...; ResultSet rs = ps.executeQuery()) { ... }

The puzzle accepts the minimal inline fix; in real code, prefer try-with-resources.

Defense in depth. Static analyzers (Error Prone's MustBeClosedChecker, SpotBugs' OS_OPEN_STREAM) catch this.

Review heuristic

Read every function with an open, connect, acquire, or lock and ask: on every path out of this function — successful return, error return, exception, panic — does the matching close fire? If you can't quickly answer yes for all paths, the cleanup needs to move into a structured construct.

CWE-772; JDBC spec ambiguity.