Skip to content

refactor(jdbc): extract LibsqlBlob and LibsqlClob - #217

Merged
magnusp merged 3 commits into
mainfrom
refactor/i199-lob-classes
Oct 24, 2026
Merged

magnusp merged 3 commits into
mainfrom
refactor/i199-lob-classes

Conversation

@magnusp

@magnusp magnusp commented Oct 24, 2026

Copy link
Copy Markdown
Owner

Fixes #199

Pure move, zero behavior change. Extracts the two read-only LOB implementations from LibsqlResultSet into package-private top-level LibsqlBlob and LibsqlClob, each still final, with a package-private constructor so getBlob and getClob construct them as before.

Base note: this PR targets refactor/i198-sqlite-real-text, the parent stack link (real-text formatter already extracted). It is not based on main.

Diff stat

 .../com/github/magnusp/libsql/jdbc/LibsqlBlob.java | 112 +++++++++++
 .../com/github/magnusp/libsql/jdbc/LibsqlClob.java | 114 +++++++++++
 .../magnusp/libsql/jdbc/LibsqlResultSet.java       | 209 ---------------------
 3 files changed, 226 insertions(+), 209 deletions(-)

git diff --cached -- LibsqlResultSet.java has zero added lines: every change there is a deletion.

Proof the moved lines are unchanged

Old LibsqlResultSet.java (parent head) lines 1034–1135 and 1137–1237 compared with the new files. Only the class declaration, the constructor visibility, and the closing brace differ; all method bodies, comments, messages, and checks are byte-identical.

$ git show HEAD^:.../LibsqlResultSet.java | sed -n '1034,1135p' > /tmp/old-blob.txt
$ sed -n '11,112p' .../LibsqlBlob.java > /tmp/new-blob.txt
$ diff /tmp/old-blob.txt /tmp/new-blob.txt
2c2
<     private static final class LibsqlBlob implements Blob {
---
> final class LibsqlBlob implements Blob {
7c7
<         private LibsqlBlob(byte[] bytes) {
---
>         LibsqlBlob(byte[] bytes) {
102c102
<     }
---
> }

$ git show HEAD^:.../LibsqlResultSet.java | sed -n '1137,1237p' > /tmp/old-clob.txt
$ sed -n '14,114p' .../LibsqlClob.java > /tmp/new-clob.txt
$ diff /tmp/old-clob.txt /tmp/new-clob.txt
2c2
<     private static final class LibsqlClob implements Clob {
---
> final class LibsqlClob implements Clob {
7c7
<         private LibsqlClob(String text) {
---
>         LibsqlClob(String text) {
101c101
<     }
---
> }

The moved bodies keep their original indentation so the relocation is byte-for-byte a move.

Preserved verbatim:

  • "Blob has been freed", "Clob has been freed", "Invalid position: ...", "Invalid length: ...", "Blob is read-only", "Clob is read-only".
  • Every SQLException / SQLFeatureNotSupportedException type and every position and length check.
  • LibsqlBlob.free() sets bytes = null; LibsqlClob.free() sets text = null.
  • LibsqlClob.getAsciiStream() still encodes with StandardCharsets.US_ASCII.

The only other edit in LibsqlResultSet is removing the four imports that only the moved code used (OutputStream, Writer, StandardCharsets, Arrays). ByteArrayInputStream, InputStream, Reader, StringReader, Blob, and Clob are still used by the result set.

Line counts

File Before After
LibsqlResultSet.java 1238 (1498 at the issue baseline) 1029 (under the 1050 gate)
LibsqlBlob.java — 112
LibsqlClob.java — 114

No nested LibsqlBlob or LibsqlClob remains in LibsqlResultSet. Nothing else in LibsqlResultSet moved.

Lanes

$ ./mvnw -pl libsql-jdbc -am test
[INFO] Tests run: 383, Failures: 0, Errors: 0, Skipped: 0
[INFO] BUILD SUCCESS
$ ./mvnw spotless:apply
[INFO] BUILD SUCCESS
$ ./mvnw spotless:check
[INFO] BUILD SUCCESS
$ ./mvnw -pl libsql-conformance -am verify
[INFO] Tests run: 272, Failures: 0, Errors: 0, Skipped: 7
[INFO] BUILD SUCCESS

No test file, dependency, or pom.xml change. No public surface change.

Characterization tests for the four unpinned behaviors in the JDBC
refactor Wave 0:

- attached-schema table metadata through pragma_database_list
- date/time text tails (offset, Z, bare time) and BLOB rejection
- duplicate savepoint names and releasing the outermost savepoint of
  an auto-commit-started transaction
- executeBatch racing statement close

Tests only; no production code, pom or dependency change.
Move the two read-only Blob and Clob implementations out of
LibsqlResultSet into package-private top-level classes. Constructors
become package-private so getBlob and getClob can still build them.
Pure move: messages, checks, stream encodings, and free() semantics
are unchanged. Also drops the four imports that only the moved code
used (OutputStream, Writer, StandardCharsets, Arrays).
@magnusp

magnusp commented Oct 24, 2026

Copy link
Copy Markdown
Owner Author

Root verdict: PASS at 7e09784.

Verifier proved both class bodies moved verbatim, constructors stay package-private, free() still nulls the backing store, and getAsciiStream still uses US_ASCII. 383 unit tests and 272 conformance tests green. A blob-length probe bit at LibsqlResultSetTest.blobAndClobExposeTheColumnValue and was reverted. Countersign: root, 7e09784.

Coverage note for a later pass: no test calls Blob.free() or Clob.free(), so the freed-path messages are pinned only by inspection. Recorded, not fixed here.

Base automatically changed from refactor/i198-sqlite-real-text to main October 24, 2026 14:39
@magnusp
magnusp merged commit 850f437 into main Oct 24, 2026
6 checks passed
@magnusp
magnusp deleted the refactor/i199-lob-classes branch October 24, 2026 14:39
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.

Extract LibsqlBlob and LibsqlClob from LibsqlResultSet

1 participant