Austin Bonander 25cbeedab4
feat: create sqlx.toml format (#3383)
* feat: create `sqlx.toml` format

* feat: add support for ignored_chars config to sqlx_core::migrate

* chore: test ignored_chars with `U+FEFF` (ZWNBSP/BOM)

https://en.wikipedia.org/wiki/Byte_order_mark

* refactor: make `Config` always compiled

simplifies usage while still making parsing optional for less generated code

* refactor: add origin information to `Column`

* feat(macros): implement `type_override` and `column_override` from `sqlx.toml`

* refactor(sqlx.toml): make all keys kebab-case, create `macros.preferred-crates`

* feat: make macros aware of `macros.preferred-crates`

* feat: make `sqlx-cli` aware of `database-url-var`

* feat: teach macros about `migrate.table-name`, `migrations-dir`

* feat: teach macros about `migrate.ignored-chars`

* chore: delete unused source file `sqlx-cli/src/migration.rs`

* feat: teach `sqlx-cli` about `migrate.defaults`

* feat: teach `sqlx-cli` about `migrate.migrations-dir`

* feat: teach `sqlx-cli` about `migrate.table-name`

* feat: introduce `migrate.create-schemas`

* WIP feat: create multi-tenant database example

* fix(postgres): don't fetch `ColumnOrigin` for transparently-prepared statements

* feat: progress on axum-multi-tenant example

* feat(config): better errors for mislabeled fields

* WIP feat: filling out axum-multi-tenant example

* feat: multi-tenant example

No longer Axum-based because filling out the request routes would have distracted from the purpose of the example.

* chore(ci): test multi-tenant example

* fixup after merge

* fix(ci): enable `sqlx-toml` in CLI build for examples

* fix: CI, README for `multi-tenant`

* fix: clippy warnings

* fix: multi-tenant README

* fix: sequential versioning inference for migrations

* fix: migration versioning with explicit overrides

* fix: only warn on ambiguous crates if the invocation relies on it

* fix: remove unused imports

* fix: doctest

* fix: `sqlx mig add` behavior and tests

* fix: restore original type-checking order

* fix: deprecation warning in `tests/postgres/macros.rs`

* feat: create postgres/multi-database example

* fix: examples/postgres/multi-database

* fix: cargo fmt

* chore: add tests for config `migrate.defaults`

* fix: sqlx-cli/tests/add.rs

* feat(cli): add `--config` override to all relevant commands

* chore: run `sqlx mig add` test with `RUST_BACKTRACE=1`

* fix: properly canonicalize config path for `sqlx mig add` test

* fix: get `sqlx mig add` test passing

* fix(cli): test `migrate.ignored-chars`, fix bugs

* feat: create `macros.preferred-crates` example

* fix(examples): use workspace `sqlx`

* fix: examples

* fix(sqlite): unexpected feature flags in `type_checking.rs`

* fix: run `cargo fmt`

* fix: more example fixes

* fix(ci): preferred-crates setup

* fix(examples): enable default-features for workspace `sqlx`

* fix(examples): issues in `preferred-crates`

* chore: adjust error message for missing param type in `query!()`

* doc: mention new `sqlx.toml` configuration

* chore: add `CHANGELOG` entry

Normally I generate these when cutting the release, but I wanted to take time to editorialize this one.

* doc: fix new example titles

* refactor: make `sqlx-toml` feature non-default, improve errors

* refactor: eliminate panics in `Config` read path

* chore: remove unused `axum` dependency from new examples

* fix(config): restore fallback to default config for macros

* chore(config): remove use of `once_cell` (to match `main`)
2025-06-30 16:34:46 -07:00
..
2025-06-30 16:34:46 -07:00

Multi-tenant Databases with sqlx.toml

This example project involves three crates, each owning a different schema in one database, with their own set of migrations.

  • The main crate, a simple binary simulating the action of a REST API.
    • Owns the public schema (tables are referenced unqualified).
    • Migrations are moved to src/migrations using config key migrate.migrations-dir to visually separate them from the subcrate folders.
  • accounts: a subcrate simulating a reusable account-management crate.
    • Owns schema accounts.
  • payments: a subcrate simulating a wrapper for a payments API.
    • Owns schema payments.

Note: Schema-Qualified Names

This example uses schema-qualified names everywhere for clarity.

It can be tempting to change the search_path of the connection (MySQL, Postgres) to eliminate the need for schema prefixes, but this can cause some really confusing issues when names conflict.

This example will generate a _sqlx_migrations table in three different schemas; if search_path is set to public,accounts,payments and the migrator for the main application attempts to reference the table unqualified, it would throw an error.

Setup

This example requires running three different sets of migrations.

Ensure sqlx-cli is installed with Postgres and sqlx.toml support:

cargo install sqlx-cli --features postgres,sqlx-toml

Start a Postgres server (shown here using Docker, run command also works with podman):

docker run -d -e POSTGRES_PASSWORD=password -p 5432:5432 --name postgres postgres:latest

Create .env with DATABASE_URL or set the variable in your shell environment;

DATABASE_URL=postgres://postgres:password@localhost/example-multi-tenant

Run the following commands:

(cd accounts && sqlx db setup)
(cd payments && sqlx migrate run)
sqlx migrate run

It is an open question how to make this more convenient; sqlx-cli could gain a --recursive flag that checks subdirectories for sqlx.toml files, but that would only work for crates within the same workspace. If the accounts and payments crates were instead crates.io dependencies, we would need Cargo's help to resolve that information.

An issue has been opened for discussion: https://github.com/launchbadge/sqlx/issues/3761