Skip to content

(BLOCKED: merge after 0.9.0) Add example for jiff-sqlx using sqlx.toml #3818

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 41 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
afa3a93
feat: create `sqlx.toml` format
abonander Jul 26, 2024
062a06f
feat: add support for ignored_chars config to sqlx_core::migrate
abonander Sep 9, 2024
9f34fc8
chore: test ignored_chars with `U+FEFF` (ZWNBSP/BOM)
abonander Sep 9, 2024
e775d2a
refactor: make `Config` always compiled
abonander Sep 18, 2024
bf90a47
refactor: add origin information to `Column`
abonander Sep 18, 2024
5cb3de3
feat(macros): implement `type_override` and `column_override` from `s…
abonander Sep 19, 2024
8604b51
refactor(sqlx.toml): make all keys kebab-case, create `macros.preferr…
abonander Sep 20, 2024
13f6ef0
feat: make macros aware of `macros.preferred-crates`
abonander Sep 20, 2024
65ef27f
feat: make `sqlx-cli` aware of `database-url-var`
abonander Sep 20, 2024
9d1bc64
feat: teach macros about `migrate.table-name`, `migrations-dir`
abonander Sep 23, 2024
ba7740d
feat: teach macros about `migrate.ignored-chars`
abonander Sep 23, 2024
e951d8e
chore: delete unused source file `sqlx-cli/src/migration.rs`
abonander Oct 5, 2024
367f2cc
feat: teach `sqlx-cli` about `migrate.defaults`
abonander Oct 5, 2024
1ff6a8a
feat: teach `sqlx-cli` about `migrate.migrations-dir`
abonander Jan 15, 2025
45c0b85
feat: teach `sqlx-cli` about `migrate.table-name`
abonander Jan 22, 2025
3765f67
feat: introduce `migrate.create-schemas`
abonander Jan 22, 2025
017ffce
Merge branch 'main' into sqlx-toml
abonander Jan 22, 2025
28b6450
WIP feat: create multi-tenant database example
abonander Jan 26, 2025
7d646a9
fix(postgres): don't fetch `ColumnOrigin` for transparently-prepared …
abonander Feb 2, 2025
c2b9f87
feat: progress on axum-multi-tenant example
abonander Feb 2, 2025
d9fc489
feat(config): better errors for mislabeled fields
abonander Feb 21, 2025
0b79b51
WIP feat: filling out axum-multi-tenant example
abonander Feb 26, 2025
46878e8
feat: multi-tenant example
abonander Feb 28, 2025
1b0c64a
chore(ci): test multi-tenant example
abonander Feb 28, 2025
8429f2e
Merge remote-tracking branch 'origin/main' into sqlx-toml
abonander Feb 28, 2025
f4d22fb
fixup after merge
abonander Feb 28, 2025
15df159
fix(ci): enable `sqlx-toml` in CLI build for examples
abonander Feb 28, 2025
4fb7102
fix: CI, README for `multi-tenant`
abonander Feb 28, 2025
2b69150
fix: clippy warnings
abonander Feb 28, 2025
a9a4d00
fix: multi-tenant README
abonander Feb 28, 2025
ac7f270
fix: sequential versioning inference for migrations
abonander Feb 28, 2025
8ddcd06
fix: migration versioning with explicit overrides
abonander Feb 28, 2025
4e95baf
Merge remote-tracking branch 'origin/main' into sqlx-toml
abonander Mar 29, 2025
732233a
fix: only warn on ambiguous crates if the invocation relies on it
abonander Mar 30, 2025
0a32d01
fix: remove unused imports
abonander Mar 30, 2025
1eb0f49
fix: doctest
abonander Mar 31, 2025
52bb9d0
fix: `sqlx mig add` behavior and tests
abonander Mar 31, 2025
80abc5b
fix: restore original type-checking order
abonander Mar 31, 2025
b989b36
fix: deprecation warning in `tests/postgres/macros.rs`
abonander Mar 31, 2025
db6d28b
feat: create postgres/multi-database example
abonander Apr 11, 2025
febc62a
feat: create postgres/jiff-sqlx example
abonander Apr 11, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
feat: create postgres/multi-database example
  • Loading branch information
abonander committed Apr 11, 2025
commit db6d28bfb166c1b8aa6252495060e1dc422499a8
18 changes: 18 additions & 0 deletions .github/workflows/examples.yml
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,24 @@ jobs:
DATABASE_URL: postgres://postgres:password@localhost:5432/mockable-todos
run: cargo run -p sqlx-example-postgres-mockable-todos

- name: Multi-Database (Setup)
working-directory: examples/postgres/multi-database
env:
DATABASE_URL: postgres://postgres:password@localhost:5432/multi-database
ACCOUNTS_DATABASE_URL: postgres://postgres:password@localhost:5432/multi-database-accounts
PAYMENTS_DATABASE_URL: postgres://postgres:password@localhost:5432/multi-database-payments
run: |
(cd accounts && sqlx db setup)
(cd payments && sqlx db setup)
sqlx db setup

- name: Multi-Database (Run)
env:
DATABASE_URL: postgres://postgres:password@localhost:5432/multi-database
ACCOUNTS_DATABASE_URL: postgres://postgres:password@localhost:5432/multi-database-accounts
PAYMENTS_DATABASE_URL: postgres://postgres:password@localhost:5432/multi-database-payments
run: cargo run -p sqlx-example-postgres-multi-database

- name: Multi-Tenant (Setup)
working-directory: examples/postgres/multi-tenant
env:
Expand Down
42 changes: 42 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ members = [
"examples/postgres/json",
"examples/postgres/listen",
"examples/postgres/mockable-todos",
"examples/postgres/multi-database",
"examples/postgres/multi-tenant",
"examples/postgres/todos",
"examples/postgres/transaction",
Expand Down
35 changes: 35 additions & 0 deletions examples/postgres/multi-database/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
[package]
name = "sqlx-example-postgres-multi-database"
version.workspace = true
license.workspace = true
edition.workspace = true
repository.workspace = true
keywords.workspace = true
categories.workspace = true
authors.workspace = true

[dependencies]
tokio = { version = "1", features = ["rt-multi-thread", "macros"] }

sqlx = { path = "../../..", version = "0.8.3", features = ["runtime-tokio", "postgres"] }

axum = { version = "0.8.1", features = ["macros"] }

color-eyre = "0.6.3"
dotenvy = "0.15.7"
tracing-subscriber = "0.3.19"

rust_decimal = "1.36.0"

rand = "0.8.5"

[dependencies.accounts]
path = "accounts"
package = "sqlx-example-postgres-multi-database-accounts"

[dependencies.payments]
path = "payments"
package = "sqlx-example-postgres-multi-database-payments"

[lints]
workspace = true
62 changes: 62 additions & 0 deletions examples/postgres/multi-database/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# Axum App with multi-database Database

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 the various database URLs or set them in your shell environment;

```
DATABASE_URL=postgres://postgres:password@localhost/example-multi-database
ACCOUNTS_DATABASE_URL=postgres://postgres:password@localhost/example-multi-database-accounts
PAYMENTS_DATABASE_URL=postgres://postgres:password@localhost/example-multi-database-payments
```

Run the following commands:

```
(cd accounts && sqlx db setup)
(cd payments && sqlx db setup)
sqlx db setup
```

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>
22 changes: 22 additions & 0 deletions examples/postgres/multi-database/accounts/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
[package]
name = "sqlx-example-postgres-multi-database-accounts"
version = "0.1.0"
edition = "2021"

[dependencies]
sqlx = { workspace = true, features = ["postgres", "time", "uuid", "macros", "sqlx-toml"] }
tokio = { version = "1", features = ["rt", "sync"] }

argon2 = { version = "0.5.3", features = ["password-hash"] }
password-hash = { version = "0.5", features = ["std"] }

uuid = { version = "1", features = ["serde"] }
thiserror = "1"
rand = "0.8"

time = { version = "0.3.37", features = ["serde"] }

serde = { version = "1.0.218", features = ["derive"] }

[dev-dependencies]
sqlx = { workspace = true, features = ["runtime-tokio"] }
30 changes: 30 additions & 0 deletions examples/postgres/multi-database/accounts/migrations/01_setup.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
-- We try to ensure every table has `created_at` and `updated_at` columns, which can help immensely with debugging
-- and auditing.
--
-- While `created_at` can just be `default now()`, setting `updated_at` on update requires a trigger which
-- is a lot of boilerplate. These two functions save us from writing that every time as instead we can just do
--
-- select trigger_updated_at('<table name>');
--
-- after a `CREATE TABLE`.
create or replace function set_updated_at()
returns trigger as
$$
begin
NEW.updated_at = now();
return NEW;
end;
$$ language plpgsql;

create or replace function trigger_updated_at(tablename regclass)
returns void as
$$
begin
execute format('CREATE TRIGGER set_updated_at
BEFORE UPDATE
ON %s
FOR EACH ROW
WHEN (OLD is distinct from NEW)
EXECUTE FUNCTION set_updated_at();', tablename);
end;
$$ language plpgsql;
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
create table account
(
account_id uuid primary key default gen_random_uuid(),
email text unique not null,
password_hash text not null,
created_at timestamptz not null default now(),
updated_at timestamptz
);

select trigger_updated_at('account');
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
create table session
(
session_token text primary key, -- random alphanumeric string
account_id uuid not null references account (account_id),
created_at timestamptz not null default now()
);
10 changes: 10 additions & 0 deletions examples/postgres/multi-database/accounts/sqlx.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[common]
database-url-var = "ACCOUNTS_DATABASE_URL"

[macros.table-overrides.'account']
'account_id' = "crate::AccountId"
'password_hash' = "sqlx::types::Text<password_hash::PasswordHashString>"

[macros.table-overrides.'session']
'session_token' = "crate::SessionToken"
'account_id' = "crate::AccountId"
Loading
Loading