Skip to content

Commit 7f99fbb

Browse files
authored
Merge pull request #5 from boringSQL/snapshot-store-refactor
refactor: snapshot storage now supports multiple databases per project
2 parents e54589b + 1c5ecb4 commit 7f99fbb

52 files changed

Lines changed: 3932 additions & 1385 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.cargo-husky/hooks/pre-commit

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#!/bin/sh
2+
3+
set -e
4+
5+
echo '+cargo fmt --all -- --check'
6+
cargo fmt --all -- --check

Cargo.lock

Lines changed: 62 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ dbg_macro = "deny"
1313

1414
[workspace.dependencies]
1515
dry_run_core = { path = "crates/dry_run_core" }
16+
async-trait = "0.1"
1617
chrono = { version = "0.4", features = ["serde"] }
1718
clap = { version = "4", features = ["derive", "env"] }
1819
pg_query = "6.1"
@@ -26,6 +27,7 @@ thiserror = "2"
2627
tokio = { version = "1", features = ["full"] }
2728
toml = "0.8"
2829
tracing = "0.1"
30+
zstd = "0.13"
2931
reqwest = { version = "0.12", features = ["json", "rustls-tls"], default-features = false }
3032
rmcp = { version = "0.8", features = ["server", "transport-io", "transport-sse-server", "macros"] }
3133
schemars = "1"

README.md

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ LLM/AI coding assistants are very good in writing code/SQL queries. But they are
1212

1313
Some PostgreSQL MCP server ask you for the database connection. And to perform the administrative tasks you might need SUPERUSER permission. But that's like asking for problem.
1414

15-
1615
We've already seen where this leads: [production databases wiped by AI agents](https://fortune.com/2025/07/23/ai-coding-tool-replit-wiped-database-called-it-a-catastrophic-failure/), and [SQL injection in MCP servers](https://securitylabs.datadoghq.com/articles/mcp-vulnerability-case-study-SQL-injection-in-the-postgresql-mcp-server/) that were supposed to be read-only.
1716

1817
The model doesn't need to *query* your database. It needs to *understand* your schema: the structure, constraints, statistics, and version-specific behavior. That knowledge is structural. It changes when you deploy a migration, not between queries.
@@ -107,7 +106,7 @@ If you can connect to a PostgreSQL instance (local, dev, or production), one com
107106
dryrun init --db "$DATABASE_URL"
108107
```
109108

110-
This creates `dryrun.toml`, the `.dryrun/` data directory, and introspects the database into `.dryrun/schema.json`. You're ready to go.
109+
This creates `dryrun.toml` (with `[project] id` and default profile), the `.dryrun/` data directory, and introspects the database into `.dryrun/schema.json`. Snapshots are keyed by `(project_id, database_id)`; set `database_id` per profile when a project has multiple databases (e.g. `auth`, `billing`). See [`docs/dryrun-toml.md`](docs/dryrun-toml.md) for the full config reference.
111110

112111
### Option B: Someone else has database access
113112

@@ -134,6 +133,44 @@ dryrun lint
134133

135134
All commands work offline from the schema file. Each project has its own `dryrun.toml` and `.dryrun/`, there is no global state. Add `.dryrun/` to your `.gitignore`.
136135

136+
### Multiple databases per project
137+
138+
`dryrun snapshot take` keys snapshots by `(project_id, database_id)`. The defaults work — `project_id` is your folder name, `database_id` is the actual database name from `current_database()`:
139+
140+
```sh
141+
dryrun init --db "$AUTH_DB" # captures auth
142+
dryrun snapshot take --db "$BILLING_DB" # captures billing into its own stream
143+
dryrun snapshot list --db "$AUTH_DB" # only auth snapshots
144+
```
145+
146+
For stable refs (and so `list` / `diff` can run without retyping URLs), declare profiles in `dryrun.toml`:
147+
148+
```toml
149+
[project]
150+
id = "myapp"
151+
152+
[profiles.auth]
153+
db_url = "${AUTH_DATABASE_URL}"
154+
database_id = "auth"
155+
156+
[profiles.billing]
157+
db_url = "${BILLING_DATABASE_URL}"
158+
database_id = "billing"
159+
```
160+
161+
Then:
162+
163+
```sh
164+
dryrun --profile billing snapshot list
165+
dryrun --profile billing snapshot diff --latest
166+
```
167+
168+
See [`docs/dryrun-toml.md`](docs/dryrun-toml.md) for all profile options.
169+
170+
Every DB-related command (`init`, `import`, `probe`, `dump-schema`, `lint`, `drift`, `stats apply`, all `snapshot` subcommands) accepts `--profile` and falls back to the resolved profile's `db_url` and `schema_file` when the corresponding CLI flag is not provider.
171+
172+
> **Note:** the MCP server is currently single-database. Using the default profile. Or the option is to run one `dryrun mcp-serve` process per database. Native multi-database support inside one MCP process is tracked in [#4](https://github.com/boringSQL/dryrun/issues/7).
173+
137174
## MCP server
138175

139176
Add `dryrun` to your AI assistant. If you installed via Homebrew, `dryrun` is already on your PATH:
@@ -150,6 +187,8 @@ claude mcp add dryrun -- /path/to/dryrun mcp-serve
150187

151188
That's it. The server auto-discovers `.dryrun/schema.json` in the current project. No database credentials needed, your AI assistant gets full schema intelligence from the offline snapshot.
152189

190+
For projects with multiple databases, run one `dryrun mcp-serve` per database and add an entry per server in your client config. Native multi-database serving inside one MCP process is tracked in [#4](https://github.com/boringSQL/dryrun/issues/4).
191+
153192
See the [Tutorial](TUTORIAL.md) for live database setup, SSE transport, and Claude Desktop configuration.
154193

155194
## More

crates/dry_run_cli/Cargo.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,8 @@ serde_json = { workspace = true }
2222
tokio = { workspace = true }
2323
tracing = { workspace = true }
2424
tracing-subscriber = { workspace = true }
25+
zstd = { workspace = true }
26+
27+
[dev-dependencies]
28+
cargo-husky = { version = "1", default-features = false, features = ["user-hooks"] }
29+
tempfile = "3"

0 commit comments

Comments
 (0)