Skip to content

Add SQLite as the seventh supported dialect - #44

Merged
oyvindberg merged 3 commits into
mainfrom
feature/sqlite-dialect
May 2, 2026
Merged

Add SQLite as the seventh supported dialect#44
oyvindberg merged 3 commits into
mainfrom
feature/sqlite-dialect

Conversation

@oyvindberg

Copy link
Copy Markdown
Contributor

Summary

  • Adds SQLite (org.xerial:sqlite-jdbc:3.49.1.0) as the seventh dialect alongside PostgreSQL, MariaDB, DuckDB, Oracle, SQL Server, and DB2. New SqliteType / SqliteTypes / SqliteConfig plus Kotlin and Scala wrappers, full docs page and snippets, and an SqliteTypeTest that exercises every type in every "position" SQLite supports (column read/write, WHERE-parameter, NULL via opt(), non-null via opt(), query analysis, query analysis with bound parameter — there are no others, since SQLite has no nested types).
  • Fixes a latent SingleConnection.transactor() routing bug uncovered by SQLite's :memory: tests: the wrapper delegated to underlying.transactor(), which built the transactor against the unwrapped SimpleDataSource and bypassed the singleton-connection proxy. Each transact() then opened a fresh in-memory database. Affects DuckDB :memory: too — masked because DuckDbTypeTest poked raw JDBC instead of going through the transactor. Added transactorEndToEnd() smoke tests to both SqliteTypeTest and DuckDbTypeTest so future regressions of this shape fail loudly.
  • Fixes a QueryAnalysis reporting bug: PrepareFailure was added to allErrors() for the summary indicator but never rendered in the report body, so users saw a with no driver message or hint. Now emitted before the parameters/columns tables.

Three commits

  1. feat(sqlite): add SQLite dialect support — core dialect (SqliteType/Read/Write/Json/Types/Typename/Config), Kotlin & Scala wrappers, DatabaseKind.SQLITE, OTel DatabaseSystem.SQLITE, build wiring, full type-roundtrip test
  2. docs(sqlite): add SQLite docs page, snippets, and analyzer matrix updatessite/docs/sqlite.md, 36 snippet files (12 sections × 3 languages), landing page (Six → Seven databases), sidebar, database-types.md, stored-procedures.md, and the four analyzer-behavior matrices in query-analysis-database-behavior.md (backed by direct probes of the xerial driver, not guesses)
  3. fix: SingleConnection routing + PrepareFailure rendering, end-to-end tests — the two bug fixes above plus three pre-existing QueryAnalysisTest assertion updates that referenced the old report format

SQLite-specific design notes

  • STRICT tables recommended. A non-strict SQLite table accepts any storage class regardless of declared type. The docs spell this out and the transactorEndToEnd test creates a STRICT table.
  • Date/time pinned to text. SqliteTypes.date/.time/.datetime/.timestamp/.instant all read and write ISO-8601 TEXT, matching the xerial driver's default date_class=TEXT. Sub-millisecond precision is silently truncated by LocalDateTime writes; Instant round-trips at full nanosecond precision because Instant.toString() does.
  • BigDecimal stored as TEXT. The xerial driver's setBigDecimal/getBigDecimal throw column -1 out of bounds; storing as plain text via setString/getString + BigDecimal::toPlainString round-trips and preserves arbitrary precision (a side benefit, since SQLite has no enforced precision either way).
  • No procedures, no arrays, no structs, no maps. SqliteType.outParam() returns Optional.empty(). The docs page enumerates the absent features.
  • Affinity-based vendor aliases for query analysis. Each type registers the alternate spellings SQLite recognises — e.g. SqliteTypes.integer accepts INTEGER, INT, INT2, INT4, INT8, BIGINT, SMALLINT, TINYINT, MEDIUMINT, UNSIGNED BIG INT so CREATE TABLE t (x BIGINT) analyses cleanly.

Test plan

  • bleep compile foundations-jdbc foundations-jdbc-hikari foundations-jdbc-spring foundations-jdbc-kotlin foundations-jdbc-scala — clean
  • bleep test foundations-jdbc-test -o dev.typr.foundations.SqliteTypeTest — 2 passed (matrix + transactor end-to-end)
  • bleep test foundations-jdbc-test -o dev.typr.foundations.DuckDbTypeTest — 4 passed (now includes transactor end-to-end)
  • bleep test foundations-jdbc-test -o dev.typr.foundations.QueryAnalysisTest — 70 passed (was 67 + 3 failing pre-existing)
  • All 125 embedded tests across SQLite + DuckDB + analyzer + parser suites pass together
  • npm run build in site/ — Docusaurus build clean, /docs/sqlite/index.html rendered, all 36 snippets resolved

🤖 Generated with Claude Code

oyvindberg and others added 3 commits May 2, 2026 13:09
Adds SQLite as the seventh supported dialect via the xerial sqlite-jdbc
driver (3.49.1.0). SQLite has only five storage classes (NULL, INTEGER,
REAL, TEXT, BLOB), no procedures, and no nested types — the implementation
is correspondingly smaller than the others.

- SqliteTypename / Read / Write / Json / Types / SqliteType
- SqliteConfig with builder for in-memory, shared in-memory, and file
  paths plus journal_mode, synchronous, busy_timeout, foreign_keys,
  date_class, and date_string_format options
- DatabaseKind.SQLITE + product/driver detection
- DatabaseSystem.SQLITE for OpenTelemetry
- Kotlin and Scala wrappers (SqliteType + SqliteTypes) with the standard
  primitive-conversion pattern
- SqliteTypeTest covers JSON roundtrip, DB roundtrip, opt() round-trip
  with a non-null value, query analysis (column-only and with parameter)
  for every type — exhausting all seven "positions" SQLite has
- BigDecimal stored as plain TEXT because the xerial driver's
  setBigDecimal/getBigDecimal throw "column -1 out of bounds"; storing
  as TEXT preserves precision exactly

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ates

- New site/docs/sqlite.md mirroring the DuckDB page structure: storage
  classes, type affinity rules, every SqliteType, date/time text-storage
  rationale, BigDecimal text-storage workaround, what SQLite doesn't have
  (no procedures/arrays/structs/maps), connection config, and query
  analysis behavior
- 12 snippet sections × 3 languages = 36 new compile-checked example
  files in documentation-examples-{java,kotlin,scala}/.../docs/sqlite/
- Landing page (site/src/pages/index.js): "Six databases" → "Seven
  databases", Hero list and typeGrid include SQLite (INTEGER, REAL, TEXT,
  BLOB, NUMERIC, BOOLEAN, DATE/DATETIME/TIMESTAMP, UUID, JSON, STRICT
  tables)
- sidebars.js + database-types.md add the SQLite entry under "Database
  Types"
- stored-procedures.md adds SQLite as N/A row alongside DuckDB
- query-analysis-database-behavior.md gets SQLite columns in all four
  matrices, backed by direct probes of the xerial driver: column types
  full, nullability partial (base columns OK, expressions/aggregates all
  reported nullable), outer-join nullability not adjusted (same trap as
  PostgreSQL), parameter types unusable (driver throws until all params
  bound, then echoes back the bound type rather than the column type)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…tests

Two latent bugs surfaced while wiring SQLite, both because SQLite's
:memory: connection-per-database semantics expose paths that DuckDB's
test suite never exercised.

* SingleConnection.transactor() returned underlying.transactor(), which
  built TransactorJdbcImpl against the wrapped SimpleDataSource and
  bypassed the singleton-connection proxy entirely. Each transact() then
  opened a fresh in-memory database, so the table from the previous
  transact was gone. Fix: hold a typed reference to SimpleDataSource and
  build the transactor against `this`. Affects SQLite and DuckDB :memory:.

* QueryAnalysis.styledReportBody() never rendered the prepareFailure;
  it was added to allErrors() to mark the report with ✗ but the
  sqlState, driver message, and parsed hint never appeared in the body.
  Users saw a ✗ with no explanation. Fix: emit prepareFailure.styledMessage()
  before the parameters/columns tables.

* QueryAnalysisTest assertions for testReportFormatting and
  testReportColoredVsPlain were written against the pre-CheckReport
  refactor format ("Parameters", "Columns", "error" literals); updated
  to match the current format which uses ✗ glyphs and a "declared"
  table header.

* New transactorEndToEnd() test in both DuckDbTypeTest and
  SqliteTypeTest exercises the public ConnectionSource → Transactor →
  Fragment flow. The original DuckDbTypeTest pokes raw JDBC via
  ConnectionJdbc, which is why the SingleConnection routing bug stayed
  hidden until SQLite triggered it.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@oyvindberg
oyvindberg merged commit 9eae207 into main May 2, 2026
10 checks passed
@oyvindberg
oyvindberg deleted the feature/sqlite-dialect branch May 3, 2026 12:25
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.

1 participant