Skip to content

test(jdbc): pin isFirst across cursor boundaries - #140

Merged
magnusp merged 1 commit into
mainfrom
fix/i75-isfirst-jdbc
Oct 22, 2026
Merged

magnusp merged 1 commit into
mainfrom
fix/i75-isfirst-jdbc

Conversation

@magnusp

@magnusp magnusp commented Oct 22, 2026

Copy link
Copy Markdown
Owner

Fixes #75

Contract line: Hrana 3 is the source of truth for transport, values, metadata, batching, transactions, and error reporting. java.sql is the source of truth for the JDBC surface. xerial/sqlite-jdbc is a behavioral reference and conformance foundation, never a copy target.

What changed

LibsqlResultSetNavigationTest now pins isFirst() at every cursor position:

  • before the first row: false
  • on the first row of a two-row result: true
  • on the second row: false
  • after the last row: false
  • a single-row result: false before, true on its row, false after the end
  • an empty result: false

No production change. LibsqlResultSet.isFirst() is already currentRowIndex == 0 && !rows.isEmpty(), which is exactly the java.sql rule. The plan's Build box says to change code only if the current behavior violates that rule, so this PR only adds the boundary pins.

Divergence: no red test at trunk

Lane 1 expects the new cases to fail at trunk. They cannot: trunk already returns false before the first row and after the last for both multi-row and single-row results. Running the new test file against origin/main production code gives Tests run: 13, Failures: 0 (log: /tmp/xerial-parity-i75/lane-1-regression.log). This is the intended outcome of the plan, not a skipped lane: the test pins an invariant that already held.

Reference note: xerial after-last difference

Probe against xerial 3.53.4.0 (/tmp/xerial-parity-i75/XerialIsFirstProbe.java):

single-row: before=false
single-row: onRow=true (next=true)
single-row: afterLast=true (isAfterLast=true)
two-row: before=false
two-row: onRow=true (next=true)
two-row: onSecond=false
two-row: afterLast=false (isAfterLast=true)
empty: before=false
empty: onRow=false (next=false)
empty: afterLast=false (isAfterLast=false)

Reason: xerial's isFirst() is row == 1, and its next() sets pastLastRow = true on SQLITE_DONE without incrementing row. After a single-row result is exhausted, row is still 1, so xerial reports true. That violates the java.sql rule, so it is recorded here and deliberately not copied.

Evidence at head 29d1be0 (base c2038d1)

  • Unit, new class: 13 tests, 0 failures.
  • Lane 2, full navigation class: 13 tests, 0 failures.
  • Lane 3, empty-result pins: 3 tests, 0 failures.
  • Lane 4, -pl libsql-jdbc test: 249 tests, 0 failures, BUILD SUCCESS.
  • Lane 5, conformance verify: 123 tests, 0 failures (1 skipped), BUILD SUCCESS.
  • Lane 6, -Pxerial-control verify: control run completed, 100 tests, 0 failures (1 skipped), BUILD SUCCESS.
  • Lane 7, spotless:check: BUILD SUCCESS.
  • Lane 8, full reactor test: BUILD SUCCESS.
  • Lane 9, conformance limited to ResultSetTest via -Dit.test=ResultSetTest: 21 tests, 0 failures.
  • Lane 10, -pl libsql-jdbc -am package -DskipTests: driver jar built.

Logs: /tmp/xerial-parity-i75/lane-{1..10}-*.log.

Perf (time ./mvnw -pl libsql-jdbc test, three interleaved runs): trunk median 2.04s, head median 2.05s, +0.5%, within the 10% rule. Head is test-only, so no hot-path change.

Lane notes

  • Lane 3 was run as the three empty-result cases (isFirst, getRow, getters) rather than the whole class.
  • Lane 9's "limited to ResultSetTest" was run as ./mvnw -pl libsql-conformance -am -Dit.test=ResultSetTest verify.

Assert isFirst is true only on the first row and false before the first
row and after the last, including the single-row case. Current
production behavior already satisfies java.sql, so no code change.

xerial 3.53.4.0 reports true after the last row of a single-row result
because isFirst() is row == 1 and next() leaves row unchanged on
SQLITE_DONE. That difference is recorded as a reference note and not
copied.
@magnusp
magnusp merged commit dbb9749 into main Oct 22, 2026
6 checks passed
@magnusp
magnusp deleted the fix/i75-isfirst-jdbc branch October 22, 2026 23:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

isFirst returns false after the end of a single-row result

1 participant