Skip to content

fix: replace better-sqlite3 with bun:sqlite for schema cache and SQLite driver#323

Merged
anandgupta42 merged 3 commits intomainfrom
fix/schema-cache-bun-sqlite
Mar 20, 2026
Merged

fix: replace better-sqlite3 with bun:sqlite for schema cache and SQLite driver#323
anandgupta42 merged 3 commits intomainfrom
fix/schema-cache-bun-sqlite

Conversation

@anandgupta42
Copy link
Contributor

@anandgupta42 anandgupta42 commented Mar 20, 2026

What does this PR do?

Replaces better-sqlite3 (native addon, incompatible with Bun runtime) with bun:sqlite (built-in, zero-install) in both the schema cache and the SQLite warehouse driver. This fixes schema_index, schema_search, schema_cache_status, and SQLite connections for all users.

Type of change

  • Bug fix (non-breaking change that fixes an issue)

Issue for this PR

Closes #314

How did you verify your code works?

  • Schema cache smoke test: SchemaCache.createInMemory()cacheStatus()search()close() all succeed
  • SQLite driver E2E: 11/11 pass (previously skipped on Bun)
  • Fixed PRAGMA+LIMIT syntax error in SQLite driver (PRAGMA doesn't support LIMIT clause)
  • Typecheck passes (turbo typecheck)
  • Branding tests: 280/280 pass

Files changed

File Change
src/altimate/native/schema/cache.ts better-sqlite3bun:sqlite, sync create()/createInMemory()
packages/drivers/src/sqlite.ts better-sqlite3bun:sqlite, fix PRAGMA+LIMIT bug
packages/drivers/package.json Remove better-sqlite3 optional dep
packages/opencode/script/build.ts Remove from esbuild externals
packages/opencode/script/publish.ts Remove from peer dependencies
package.json Remove @types/better-sqlite3 devDep
docs/docs/drivers.md Update SQLite driver docs
test/altimate/drivers-e2e.test.ts Remove isBetterSqlite3Available skip guards

Checklist

  • My code follows the style guidelines of this project
  • I have performed a self-review of my code
  • New and existing tests pass locally with my changes

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Documentation

    • SQLite driver docs updated to reflect built‑in, zero‑install runtime support.
  • Chores

    • External SQLite driver references removed from project configuration and packaging.
  • Tests

    • SQLite-related tests made unconditional and expanded with new coverage for schema cache, robustness, and edge cases.

…SQLite driver

`better-sqlite3` is a native addon that doesn't work on Bun ("not yet supported").
This broke `schema_index`, `schema_search`, `schema_cache_status`, and the SQLite
driver for all users on the released CLI binary.

Switch to `bun:sqlite` which is built into the Bun runtime — zero-install, no
native compilation, same synchronous API. The storage layer (`db.ts`) already
uses this pattern successfully.

Changes:
- `schema/cache.ts`: direct `bun:sqlite` import, sync `create()`/`createInMemory()`
- `drivers/sqlite.ts`: `bun:sqlite` import, fix PRAGMA+LIMIT syntax error
- Remove `better-sqlite3` from optional deps, peer deps, build externals, types
- Update driver docs and E2E tests (SQLite tests no longer need skip guards)

Closes #314
Copy link

@claude claude bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude Code Review

This repository is configured for manual code reviews. Comment @claude review to trigger a review.

@coderabbitai
Copy link

coderabbitai bot commented Mar 20, 2026

Warning

Rate limit exceeded

@anandgupta42 has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 0 minutes and 16 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 46730f7c-a81d-4ab7-89b8-94f74c08fc21

📥 Commits

Reviewing files that changed from the base of the PR and between e63d963 and 4720e66.

📒 Files selected for processing (3)
  • packages/drivers/src/sqlite.ts
  • packages/opencode/src/altimate/native/schema/cache.ts
  • packages/opencode/test/altimate/schema-cache.test.ts
📝 Walkthrough

Walkthrough

Replaces better-sqlite3 with Bun’s built-in bun:sqlite across docs, driver code, schema cache, build/publish scripts, and tests; converts cache initialization from async to sync; removes dependency declarations and runtime probes for better-sqlite3; adds extensive schema-cache tests using bun:sqlite.

Changes

Cohort / File(s) Summary
Docs & Manifests
docs/docs/drivers.md, package.json, packages/drivers/package.json
Removed better-sqlite3 references and installation instructions; documented SQLite as bun:sqlite built-in; removed @types/better-sqlite3 and optional better-sqlite3 dependency.
Driver Implementation
packages/drivers/src/sqlite.ts
Switched from dynamic better-sqlite3 loading to static import { Database } from "bun:sqlite"; updated DB construction, PRAGMA handling, LIMIT injection logic, connection null guards, and typings.
Schema Cache
packages/opencode/src/altimate/native/schema/cache.ts
Replaced dynamic better-sqlite3 import with bun:sqlite; changed SchemaCache.create/createInMemory from async→sync and updated db typing and call sites.
Build & Publish Scripts
packages/opencode/script/build.ts, packages/opencode/script/publish.ts
Removed better-sqlite3 from bundler externals and from driverPeerDependencies (and derived peerDependenciesMeta) so it is no longer published as an optional peer dependency.
E2E Tests Update
packages/opencode/test/altimate/drivers-e2e.test.ts
Removed runtime probe for better-sqlite3; always run SQLite E2E tests; adjusted expectation for INTEGER PRIMARY KEY column nullable from falsetrue.
New Schema-Cache Tests
packages/opencode/test/altimate/schema-cache.test.ts
Added extensive Bun-based tests covering migrations, corruption handling, injection resilience, unicode/identifier edge cases, large dataset behavior, re-indexing semantics, failure modes, persistence, and singleton lifecycle using the bun:sqlite backend.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐇 I hopped from node to Bun today,
swapped a crate for something built-in, hooray!
The cache woke up with a sunny grin,
no installs needed — let indexing begin.
— a rabbit, nibbling on a PR carrot 🥕

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 42.86% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: replacing better-sqlite3 with bun:sqlite across schema cache and SQLite driver components.
Description check ✅ Passed The PR description follows the template with clear Summary, Test Plan, and Checklist sections, providing detailed context on what changed and how it was verified.
Linked Issues check ✅ Passed The PR successfully addresses issue #314 by replacing better-sqlite3 with bun:sqlite throughout the codebase, enabling schema cache and SQLite features to work on Bun runtimes.
Out of Scope Changes check ✅ Passed All changes are directly scoped to replacing better-sqlite3 with bun:sqlite and removing related dependencies; no unrelated modifications detected.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/schema-cache-bun-sqlite
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

34 new tests covering:
- Upgrade path from `better-sqlite3` (legacy DB files opened by `bun:sqlite`)
- Corrupted/truncated/zero-byte database files
- SQL injection resistance via search queries (7 injection vectors + null bytes)
- Unicode and special character identifiers in schema/table/column names
- Large dataset stress (1000 tables x 10 columns)
- Re-indexing data replacement and multi-warehouse isolation
- Connector failure modes (listSchemas/listTables/describeTable errors)
- Search edge cases (empty, stop words, long query, case insensitivity, FQN)
- `listColumns` with limits and unknown warehouses
- File-based cache persistence across close/reopen
- Singleton lifecycle (getCache/resetCache)
- PRAGMA LIMIT syntax fix verification
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
packages/opencode/src/altimate/native/schema/cache.ts (1)

117-125: Consider adding a guard against using a closed database.

Unlike sqlite.ts which guards against db being null, the SchemaCache class doesn't have similar protection. After close() is called (lines 368-374), the db reference becomes invalid but isn't nullified. Subsequent method calls would throw opaque SQLite errors rather than a clear "cache closed" message.

♻️ Optional: Add closed-state guard
 export class SchemaCache {
   private db: Database
   private dbPath: string
+  private closed = false

   private constructor(db: Database, dbPath: string) {
     this.db = db
     this.dbPath = dbPath
   }
+
+  private ensureOpen(): void {
+    if (this.closed) throw new Error("SchemaCache has been closed")
+  }

Then call this.ensureOpen() at the start of indexWarehouse, search, cacheStatus, and listColumns.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/opencode/src/altimate/native/schema/cache.ts` around lines 117 -
125, SchemaCache lacks a closed-state guard so after close() the db stays set
and subsequent calls throw opaque SQLite errors; add an internal ensureOpen()
method on the SchemaCache class that throws a clear "SchemaCache is closed"
error if this.db is null/closed, call this.ensureOpen() at the start of public
methods indexWarehouse, search, cacheStatus, and listColumns, and update close()
to null out or mark the db field (e.g., set this.db = null or this._closed =
true) so ensureOpen() can detect the closed state.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/opencode/test/altimate/drivers-e2e.test.ts`:
- Around line 550-555: The test "handles invalid SQL gracefully" incorrectly
uses expect(() => connector.execute(...)).toThrow() for the async method
connector.execute; change the assertion to handle the returned Promise by using
either await expect(connector.execute("INVALID SQL
STATEMENT")).rejects.toThrow() or return expect(connector.execute("INVALID SQL
STATEMENT")).rejects.toThrow() so the rejection is asserted correctly.

---

Nitpick comments:
In `@packages/opencode/src/altimate/native/schema/cache.ts`:
- Around line 117-125: SchemaCache lacks a closed-state guard so after close()
the db stays set and subsequent calls throw opaque SQLite errors; add an
internal ensureOpen() method on the SchemaCache class that throws a clear
"SchemaCache is closed" error if this.db is null/closed, call this.ensureOpen()
at the start of public methods indexWarehouse, search, cacheStatus, and
listColumns, and update close() to null out or mark the db field (e.g., set
this.db = null or this._closed = true) so ensureOpen() can detect the closed
state.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 53cfda2d-d4d7-431a-99c9-be1d9cc82e53

📥 Commits

Reviewing files that changed from the base of the PR and between 7a16e73 and acfa15d.

📒 Files selected for processing (8)
  • docs/docs/drivers.md
  • package.json
  • packages/drivers/package.json
  • packages/drivers/src/sqlite.ts
  • packages/opencode/script/build.ts
  • packages/opencode/script/publish.ts
  • packages/opencode/src/altimate/native/schema/cache.ts
  • packages/opencode/test/altimate/drivers-e2e.test.ts
💤 Files with no reviewable changes (2)
  • package.json
  • packages/opencode/script/publish.ts

Comment on lines +550 to 555
test(
"handles invalid SQL gracefully",
async () => {
expect(() => connector.execute("INVALID SQL STATEMENT")).toThrow()
},
)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Incorrect assertion: expect(...).toThrow() should be expect(...).rejects.toThrow() for async function.

connector.execute() is an async function that returns a Promise. Using expect(...).toThrow() synchronously will not catch the rejection—it will likely pass incorrectly or throw unexpectedly. The test should use rejects.toThrow() consistent with line 569.

🐛 Proposed fix
 test(
   "handles invalid SQL gracefully",
   async () => {
-    expect(() => connector.execute("INVALID SQL STATEMENT")).toThrow()
+    await expect(connector.execute("INVALID SQL STATEMENT")).rejects.toThrow()
   },
 )
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
test(
"handles invalid SQL gracefully",
async () => {
expect(() => connector.execute("INVALID SQL STATEMENT")).toThrow()
},
)
test(
"handles invalid SQL gracefully",
async () => {
await expect(connector.execute("INVALID SQL STATEMENT")).rejects.toThrow()
},
)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/opencode/test/altimate/drivers-e2e.test.ts` around lines 550 - 555,
The test "handles invalid SQL gracefully" incorrectly uses expect(() =>
connector.execute(...)).toThrow() for the async method connector.execute; change
the assertion to handle the returned Promise by using either await
expect(connector.execute("INVALID SQL STATEMENT")).rejects.toThrow() or return
expect(connector.execute("INVALID SQL STATEMENT")).rejects.toThrow() so the
rejection is asserted correctly.

@dev-punia-altimate
Copy link

✅ Tests — All Passed

TypeScript — passed

cc @anandgupta42
Tested at e63d963f | Run log | Powered by QA Autopilot

…g, tests

Fixes from 6-model consensus code review:

1. **CRITICAL** — SQLite driver `readonly` + `create: true` bug: gate `create`
   and WAL pragma on `!readonly` so readonly connections don't silently open
   read-write or crash on PRAGMA WAL.

2. **MAJOR** — Wrap `indexWarehouse` inserts in `db.transaction()` per-table
   to avoid per-statement disk fsyncs (~200x slowdown for large warehouses).

3. **MAJOR** — Fix no-op parent directory test (was creating dir before testing).
   Add 3 readonly connection tests (read existing, reject writes, refuse create).

4. **MINOR** — Extend `idx_columns_table` covering index to include
   `column_name` for `listColumns()` ORDER BY.
@anandgupta42 anandgupta42 merged commit df24e73 into main Mar 20, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] schema_index fails on Bun build because schema cache depends on better-sqlite3

2 participants