Skip to content

Latest commit

 

History

History
56 lines (42 loc) · 2.12 KB

File metadata and controls

56 lines (42 loc) · 2.12 KB

SQLx

Offline mode (SQLX_OFFLINE=true)

The nix dev shell sets SQLX_OFFLINE=true (in flake.nix), which tells sqlx compile-time macros (query!, query_scalar!, etc.) to use cached metadata from the .sqlx/ directory instead of connecting to the database. This means:

  • Regular cargo check/cargo nextest run typically don't need a running database -- they read from .sqlx/ cache files checked into version control. Exception: test code using query! macros (see below).
  • If you add or change a query! macro invocation, you must regenerate the cache before the change will compile under SQLX_OFFLINE=true.

Regenerating the query cache

cargo sqlx prepare --workspace -- --all-targets

Then check the updated .sqlx/ files into version control.

Pitfall: #[cfg(test)] queries don't work with offline mode

cargo sqlx prepare does NOT collect queries from #[cfg(test)] code, even with -- --all-targets. This is a known limitation -- the --all-targets flag is supposed to compile test targets during preparation, but in practice the test-only queries are silently skipped.

When you then run cargo nextest run (which enables cfg(test)), the compiler sees the query macro, finds no cached metadata, and fails with:

`SQLX_OFFLINE=true` but there is no cached data for this query

The fix: use runtime query functions in test code. Instead of the compile-time macro sqlx::query_scalar!("..."), use the runtime function sqlx::query_scalar("..."). The non-macro version doesn't need offline cache entries. Since test code runs against a real in-memory database anyway, compile-time query verification adds no value.

// BROKEN in offline mode -- macro needs cache entry that prepare won't generate
#[cfg(test)]
let count = sqlx::query_scalar!("SELECT COUNT(*) FROM my_table")
    .fetch_one(pool).await?;

// WORKS -- runtime query, no cache needed
#[cfg(test)]
let count: i64 = sqlx::query_scalar("SELECT COUNT(*) FROM my_table")
    .fetch_one(pool).await?;

Note the type annotation on the let binding -- the runtime function doesn't infer return types like the macro does.