Repository navigation
Refactor libsql-jdbc into focused units #194
Description
Goal
Turn the four god-classes in libsql-jdbc into focused units without changing a single observable behavior. The driver is a drop-in replacement for xerial sqlite-jdbc 3.53.4.0, so quirks, messages, SQL text, and exception types are the contract. Only structure moves.
Reader load is the success measure. About 1,300 lines leave the four largest files, and each pass must make one part of the code easier to trace. A pass that does not lower reader load gets reverted.
Why now
LibsqlResultSet is 1498 lines, LibsqlDatabaseMetaData 1057, SqlTableReference 998, LibsqlResultSetMetaData 680. Each mixes a JDBC surface with an algorithm that belongs in its own file. The baseline is green. ./mvnw -pl libsql-jdbc -am test ran 374 tests with 0 failures at 404bba6, and the selected xerial conformance suite is green at the same head (#193).
The rule
One pass is one PR. Each pass pins behavior, moves code, and proves the pin still holds. No pass adds behavior, and no pass edits an existing assertion. A pass that finds a bug splits it out and ships the structural change first.
Passes that touch the same file run in order, not in parallel. The blocked-by links below encode that.
Map
| Pass | Moves out | Target |
|---|---|---|
| #1 real text | SQLite %!.17g formatter from LibsqlResultSet |
1498 → under 1250 |
| #2 LOBs | LibsqlBlob, LibsqlClob |
under 1050 |
| #3 numeric text | SqliteNumericText, duplicate getter ladders |
under 950 |
| #4 date and time | SqliteDateTimeValues |
under 850 |
| #5 autoincrement | SqlAutoIncrement |
680 → under 580 |
| #6 column type | XerialColumnType |
under 480 |
| #7 declared type | DeclaredType |
under 430 |
| #8 table resolver | TableMetadataResolver |
under 300 |
| #9 SQL parser | SqlCursor, FromClauseParser, SelectItemParser, TableBinding |
998 → under 450 |
| #10 metadata SQL | MetadataSql |
1057 → under 950 |
| #11 columns shaper | named row records | under 900 |
| #12 foreign keys | ForeignKeyMetadata |
under 800 |
| connection cleanup | one send path, one update-count path | 763 → under 750 |
| #13 savepoints | named state transitions | 763 → under 740 |
| #14 statement results | one attachment path, closeDetached |
702 → under 650 |
| #15 parameter metadata | ParameterMetaDataView |
614 → under 560 |
| #16 URL parser | named parse steps | parse under 30 lines |
Predicate (done)
./mvnw -pl libsql-jdbc -am testgreen, and no existing assertion edited../mvnw spotless:checkgreen../mvnw -pl libsql-conformance -am verifygreen (272 selected xerial tests).- Passes that touch connection, statement, prepared statement, or driver also run
./mvnw -pl libsql-itests -am verifywithDOCKER_CONFIGset per AGENTS.md section 4. - Public API and every message unchanged.
Decisions
- Removing the unused public
LibsqlResultSetMetaData(Statement, StmtResult)constructor is a public API change. No pass touches it until the operator decides. LibsqlPreparedStatementkeepsimplements ParameterMetaData. Pass 15 moves the method bodies behind a view soinstanceofstill holds. Dropping the interface is a separate compatibility decision.
Bugs found during the survey
Two defects are tracked under this epic as bug issues and are not refactors. They ship in their own PRs with a red test first.
Rejected on purpose
- Splitting the unsupported-operation blocks. No line savings, and grouping tempts normalizing
UnsupportedOperationExceptionagainstSQLFeatureNotSupportedException. - Merging
valueAtCursorandvalueForMetadata. Different NULL semantics. - Rewriting the REAL formatter or the type if-chains. The quirks are the contract.
- Unifying
Locale.ENGLISHandLocale.ROOTupper-casing. No reader-load gain. - Consolidating the two constraint-name parsers. Both are pinned byte for byte, and the swap carries behavior risk with no structural win.
Activity