Skip to content

Commit 5c6302a

Browse files
committed
feat: support attached databases
- The connection manager needs to enforce connection policies and reference counting so we need a first-class way to attach databases
1 parent e8eeca8 commit 5c6302a

8 files changed

Lines changed: 945 additions & 2 deletions

File tree

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -379,8 +379,8 @@ To see logs during development:
379379

380380
```toml
381381
[dependencies]
382-
tracing = { version = "0.1.x", default-features = false, features = ["std", "release_max_level_off"] }
383-
tracing-subscriber = { version = "0.3.x", features = ["fmt", "env-filter"] }
382+
tracing = { version = "0.1.41", default-features = false, features = ["std", "release_max_level_off"] }
383+
tracing-subscriber = { version = "0.3.20", features = ["fmt", "env-filter"] }
384384
```
385385

386386
```rust

crates/sqlx-sqlite-conn-mgr/README.md

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,67 @@ times is safe (already-applied migrations are skipped).
9292
> `Builder::add_migrations()`. The plugin starts migrations at setup and waits for
9393
> completion when `load()` is called.
9494
95+
### Attached Databases
96+
97+
Attach other SQLite databases to enable cross-database queries. Attachments are
98+
connection-scoped and automatically detached when the guard is dropped.
99+
100+
```rust
101+
use sqlx_sqlite_conn_mgr::{SqliteDatabase, AttachedSpec, AttachedMode, acquire_reader_with_attached};
102+
use sqlx::query;
103+
use std::sync::Arc;
104+
105+
async fn example() -> Result<(), sqlx_sqlite_conn_mgr::Error> {
106+
// You must first connect to the main database and any database(s) you intend to
107+
// attach.
108+
let main_db = SqliteDatabase::connect("main.db", None).await?;
109+
let orders_db = SqliteDatabase::connect("orders.db", None).await?;
110+
111+
// Attach orders database for read-only access
112+
let specs = vec![AttachedSpec {
113+
database: orders_db,
114+
schema_name: "orders".to_string(),
115+
mode: AttachedMode::ReadOnly,
116+
}];
117+
118+
let mut conn = acquire_reader_with_attached(&main_db, specs).await?;
119+
120+
// Cross-database query
121+
let rows = query(
122+
"SELECT u.name, o.total
123+
FROM main.users u
124+
JOIN orders.orders o ON u.id = o.user_id"
125+
)
126+
.fetch_all(&mut *conn)
127+
.await?;
128+
129+
// Attached database automatically detached when conn is dropped
130+
Ok(())
131+
}
132+
```
133+
134+
#### Attached Modes
135+
136+
* **`AttachedMode::ReadOnly`**: Attach for read access only. Can be used with
137+
both reader and writer connections.
138+
* **`AttachedMode::ReadWrite`**: Attach for write access. Can only be used with
139+
writer connections. Acquires the attached database's writer lock to ensure
140+
exclusive access.
141+
142+
#### Safety Guarantees
143+
144+
1. **Lock ordering**: Multiple attachments are acquired in alphabetical order by
145+
schema name to prevent deadlocks
146+
2. **Mode validation**: Read-only connections cannot attach databases in
147+
read-write mode (returns `CannotAttachReadWriteToReader` error)
148+
3. **Automatic cleanup**: SQLite automatically detaches databases when connections
149+
close; no manual cleanup required
150+
151+
> **Caution:** Do not bypass this API by executing raw
152+
> `ATTACH DATABASE '/path/to/db.db' AS alias` SQL commands directly. Doing so
153+
> circumvents the connection manager's policies and will result in
154+
> unpredictable behavior, including potential deadlocks.
155+
95156
## API Reference
96157

97158
### `SqliteDatabase`
@@ -110,6 +171,16 @@ times is safe (already-applied migrations are skipped).
110171
RAII guard for exclusive write access. Derefs to `SqliteConnection`. Connection
111172
returned to pool on drop.
112173

174+
### Attached Database Functions
175+
176+
| Function | Description |
177+
| -------- | ----------- |
178+
| `acquire_reader_with_attached(db, specs)` | Acquire read connection with attached databases |
179+
| `acquire_writer_with_attached(db, specs)` | Acquire writer connection with attached databases |
180+
181+
Returns `AttachedConnection` or `AttachedWriteGuard` respectively. Both guards
182+
deref to `SqliteConnection` and automatically detach databases on drop.
183+
113184
## Design Details
114185

115186
### Read-Only Pool

0 commit comments

Comments
 (0)