Repository navigation
ResultSet navigation differs on duplicate labels and cursor state #45
Description
Problem
findColumn, cursor state, getRow, isLast, and wasNull differ from sqlite-jdbc. Applications that share result sets, read metadata before the first row, or compute pagination see different results.
Actual behavior
findColumn uses a map and returns the last duplicate label (LibsqlResultSet.java:307-324). xerial 3.53.4.0 returns the first. Getters throw before the first row and after the last row (:74-86). xerial returns row 1 before next() and null or zero after the end. getRow returns 0 after the end (:432-435), isLast returns a real boolean (:452-455), and wasNull returns false before any getter (:108-112). xerial throws SQLFeatureNotSupportedException from isLast.
Expected behavior
The driver chooses the JDBC-correct contract or matches xerial, documents it, and tests navigation boundaries.
Reproduction
try (ResultSet rs = s.executeQuery("select 1 as a, 2 as a")) {
rs.findColumn("a"); // 1 in xerial, 2 in libSQL
rs.getRow(); // 1 in xerial, 0 in libSQL
rs.wasNull(); // throws in xerial, false in libSQL
}Compatibility decision
Strict JDBC says a getter outside a valid row throws, and several xerial behaviors are not strict JDBC. xerial returns a synthetic row before next(), returns null after the end, and throws from isLast. Matching xerial helps migrations but keeps non-standard cursor semantics. Following JDBC makes boundary behavior predictable but differs from the reference driver. Which contract wins for each method?
Acceptance criteria
- A documented contract covers duplicate labels and cursor boundaries.
- Tests cover duplicate labels, getter before
next(), getter after the end,getRow,isLast, andwasNull. - Tests pin the behavior before the first row and after the last row.
References
libsql-jdbc/src/main/java/com/github/magnusp/libsql/jdbc/LibsqlResultSet.java:74-86(getCurrentValue)libsql-jdbc/src/main/java/com/github/magnusp/libsql/jdbc/LibsqlResultSet.java:108-112(wasNull)libsql-jdbc/src/main/java/com/github/magnusp/libsql/jdbc/LibsqlResultSet.java:307-324(findColumn)libsql-jdbc/src/main/java/com/github/magnusp/libsql/jdbc/LibsqlResultSet.java:432-435(getRow)libsql-jdbc/src/main/java/com/github/magnusp/libsql/jdbc/LibsqlResultSet.java:452-455(isLast)- xerial 3.53.4.0
JDBC3ResultSet.java:139-153(isLast,getRow,wasNull) and:40-52(findColumn)
Steering
Lane A (LibsqlResultSet.java), position 5 of 5. Depends on #34 (lane order; P2 lands after the lane's P1s). Lands in wave 5.
Corrections from the #28 review. xerial getRow() returns 0 before next(), not 1, and returns the last row number after the end (1 for a one-row result), not 0. The body's "xerial returns row 1 before next()" claim and its reproduction comment are wrong. It is the column getters that return the first row before next() (probed: getInt returns 1 before next() and 0 after the end). Only getRow and getInt were re-verified in #28. Re-probe every other claimed xerial boundary (findColumn duplicate-label order, isLast throwing SQLFeatureNotSupportedException, wasNull before any getter) against the pinned 3.53.4.0 jar before pinning tests.
Contract. Default parity under #54. This issue holds the most contested quirks, so the PR body must list the chosen target per method: findColumn, getters before next(), getters after the end, getRow, isLast, and wasNull.
Evidence. New tests fail on the current code and pass after the fix, covering duplicate labels and every cursor boundary in the acceptance criteria. Run ./mvnw -pl libsql-jdbc -am test and ./mvnw spotless:apply before push.
PR shape. One PR, Closes #45. Part of #54.
Activity
feat(jdbc): add xerial conformance suite and harden metadata (#28)
fix(jdbc): match xerial result set navigation boundaries (#72)