Skip to content

fix(expo-sqlite): subscribe useLiveQuery to every joined table - #6497

Open
61465 wants to merge 1 commit into
drizzle-team:mainfrom
61465:fix/expo-sqlite-use-live-query-joined-tables
Open

61465 wants to merge 1 commit into
drizzle-team:mainfrom
61465:fix/expo-sqlite-use-live-query-joined-tables

Conversation

@61465

@61465 61465 commented Oct 13, 2026

Copy link
Copy Markdown

Summary

Fixes #2660.

useLiveQuery on expo-sqlite only re-ran a query when the root table changed. If the query joined other tables — either through innerJoin/leftJoin on a plain select or through nested with on db.query.* — mutations to those joined tables did not retrigger the hook and the UI showed stale data.

That has bitten enough users that the thread already contains two working community prototypes (@doughsay 2024-10, @jsteiner 2024-11). This PR lands the same idea upstream, using existing drizzle-orm primitives, so no copy-pasting into user codebases.

What changed

  • drizzle-orm/src/sqlite-core/query-builders/query.ts — new /** @internal */ getReferencedTables() method on SQLiteRelationalQuery. It starts from the root table name (Table.Symbol.BaseName) and walks the with tree, resolving each enabled relation to its referenced table via the schema's relational config. The walk is cycle-guarded (visited: Set<TableRelationalConfig>) so a self-referencing relation can't loop.
  • drizzle-orm/src/expo-sqlite/query.ts — the useLiveQuery hook now builds a Set<string> of listening tables:
    • For SQLiteRelationalQuery (the db.query.* API) it folds in query.getReferencedTables().
    • For AnySQLiteSelect (the SQL-like builder) it iterates config.joins and reuses the existing public extractUsedTable from ~/sqlite-core/utils.ts.
    • The addDatabaseChangeListener callback uses tableSet.has(tableName) instead of a single equality check.
    • Zero API change on the hook signature.

Tests

Added drizzle-orm/tests/sqlite/use-live-query-tables.test.ts, five cases built on the sqlite-proxy driver (no native deps, so it runs anywhere):

✓ getReferencedTables returns only the root table when no `with` is passed
✓ getReferencedTables returns the root table and every table reached through `with`
✓ getReferencedTables walks nested `with` relations
✓ getReferencedTables ignores relations whose value is a falsy flag
✓ extractUsedTable returns the base name of a joined SQLite table

The existing 507 unit tests in the sibling directories still pass locally. The two files that fail locally (casing/sqlite-to-camel.test.ts, casing/sqlite-to-snake.test.ts) do so because better-sqlite3's native binary doesn't build on my toolchain; both are untouched by this PR.

Rationale notes

  • Table.Symbol.BaseName was chosen (over Table.Symbol.Name) because that is what extractUsedTable already uses and what expo-sqlite's addDatabaseChangeListener emits — avoids an alias mismatch.
  • Nested with is handled, not just top-level — the community prototypes only did one level.
  • SQLiteSelectConfig.joins is already /** @internal */ public, so no visibility change is needed on the SQL-like path.
  • No v1 (alternation-engine) overlap: the expo-sqlite driver is outside the kit rewrite, so there's no risk of this PR colliding with that work.

Changelog

Added changelogs/drizzle-orm/0.45.5.md.

Checklist

  • Commit is signed (SSH, ed25519).
  • All four touched files use LF line endings.
  • Tests added and passing locally (node_modules/vitest/dist/cli.js run tests/sqlite/use-live-query-tables.test.ts).
  • No breaking API change.

Credit to @doughsay for the first community diagnosis and to @jsteiner for the two-pass prototype that proved both the SQL-like-join and relational-with paths. This PR is the same shape, lifted onto getReferencedTables + extractUsedTable so it only touches drizzle-orm, not every user's codebase.

🤖 Used Claude Code for research + review; all code and reasoning signed off by me on content.

The expo-sqlite useLiveQuery hook only re-ran its query when the main
table changed, so views built on a join or a relational `with` were
silently stale until the main table was touched. Added
SQLiteRelationalQuery#getReferencedTables and reuse extractUsedTable so
the hook now subscribes to every table the query actually reads.

Closes drizzle-team#2660.
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.

[BUG]: useLiveQuery doesn't see change when querying joined tables

1 participant