Skip to content

Commit e08477d

Browse files
committed
Add support for running migrations via diesel_migrations
This commit provides an `AsyncMigrationHarness` which enables running migrations via `diesel_migrations` with any `AsyncConnection` This commit also provides some additional documenation and other minor fixes.
1 parent ba8ad28 commit e08477d

File tree

16 files changed

+436
-204
lines changed

16 files changed

+436
-204
lines changed

.github/workflows/ci.yml

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ on:
44
push:
55
branches:
66
- main
7+
- 0.7.x
8+
- 0.6.x
9+
- 0.5.x
710
- 0.3.x
811
- 0.4.x
912
- 0.2.x
@@ -25,7 +28,13 @@ jobs:
2528
rust: ["stable"]
2629
backend: ["postgres", "mysql", "sqlite"]
2730
os:
28-
[ubuntu-latest, macos-13, macos-15, windows-latest, ubuntu-22.04-arm]
31+
[
32+
ubuntu-latest,
33+
macos-15-intel,
34+
macos-15,
35+
windows-latest,
36+
ubuntu-22.04-arm,
37+
]
2938
include:
3039
- rust: "beta"
3140
backend: "postgres"
@@ -100,22 +109,13 @@ jobs:
100109
run: |
101110
sudo apt-get update
102111
sudo apt-get install libsqlite3-dev
103-
echo "DATABASE_URL=/tmp/test.db" >> $GITHUB_ENV
112+
echo "DATABASE_URL=:memory:" >> $GITHUB_ENV
104113
105114
- name: Install postgres (MacOS)
106-
if: matrix.os == 'macos-13' && matrix.backend == 'postgres'
115+
if: runner.os == 'macOS' && matrix.backend == 'postgres'
107116
run: |
108117
brew install postgresql@14
109-
brew services start postgresql@14
110-
sleep 3
111-
createuser -s postgres
112-
echo "DATABASE_URL=postgres://postgres@localhost/" >> $GITHUB_ENV
113-
114-
- name: Install postgres (MacOS M1)
115-
if: matrix.os == 'macos-15' && matrix.backend == 'postgres'
116-
run: |
117-
brew install postgresql@14
118-
brew services start postgresql@14
118+
brew services restart postgresql@14
119119
sleep 3
120120
createuser -s postgres
121121
echo "DATABASE_URL=postgres://postgres@localhost/" >> $GITHUB_ENV
@@ -124,10 +124,10 @@ jobs:
124124
if: runner.os == 'macOS' && matrix.backend == 'sqlite'
125125
run: |
126126
brew install sqlite
127-
echo "DATABASE_URL=/tmp/test.db" >> $GITHUB_ENV
127+
echo "DATABASE_URL=:memory:" >> $GITHUB_ENV
128128
129-
- name: Install mysql (MacOS)
130-
if: matrix.os == 'macos-13' && matrix.backend == 'mysql'
129+
- name: Install mysql (MacOS Intel)
130+
if: matrix.os == 'macos-15-intel' && matrix.backend == 'mysql'
131131
run: |
132132
brew install mariadb@11.4
133133
/usr/local/opt/mariadb@11.4/bin/mysql_install_db
@@ -184,7 +184,7 @@ jobs:
184184
run: |
185185
echo "C:\ProgramData\chocolatey\lib\SQLite\tools" >> $GITHUB_PATH
186186
echo "SQLITE3_LIB_DIR=C:\ProgramData\chocolatey\lib\SQLite\tools" >> $GITHUB_ENV
187-
echo "DATABASE_URL=C:\test.db" >> $GITHUB_ENV
187+
echo "DATABASE_URL=:memory:" >> $GITHUB_ENV
188188
189189
- name: Install rust toolchain
190190
uses: dtolnay/rust-toolchain@master
@@ -194,7 +194,7 @@ jobs:
194194
run: cargo +${{ matrix.rust }} version
195195

196196
- name: Test diesel_async
197-
run: cargo +${{ matrix.rust }} test --manifest-path Cargo.toml --no-default-features --features "${{ matrix.backend }} deadpool bb8 mobc async-connection-wrapper"
197+
run: cargo +${{ matrix.rust }} test --manifest-path Cargo.toml --no-default-features --features "${{ matrix.backend }} deadpool bb8 mobc async-connection-wrapper migrations"
198198

199199
- name: Run examples (Postgres)
200200
if: matrix.backend == 'postgres'

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ for Rust libraries in [RFC #1105](https://github.com/rust-lang/rfcs/blob/master/
66

77
## [Unreleased]
88

9+
* Added support for running migrations via `AsyncMigrationHarness`
10+
911
## [0.6.1] - 2025-07-03
1012

1113
* Fix features for some dependencies

Cargo.toml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,10 @@ features = [
4444
"i-implement-a-third-party-backend-and-opt-into-breaking-changes",
4545
]
4646

47+
[dependencies.diesel_migrations]
48+
version = "~2.3.0"
49+
optional = true
50+
4751
[dev-dependencies]
4852
tokio = { version = "1.12.0", features = ["rt", "macros", "rt-multi-thread"] }
4953
cfg-if = "1"
@@ -73,6 +77,7 @@ postgres = ["diesel/postgres_backend", "tokio-postgres", "tokio", "tokio/rt"]
7377
sqlite = ["diesel/sqlite", "sync-connection-wrapper"]
7478
sync-connection-wrapper = ["tokio/rt"]
7579
async-connection-wrapper = ["tokio/net", "tokio/rt"]
80+
migrations = ["diesel_migrations", "async-connection-wrapper", "tokio/rt-multi-thread"]
7681
pool = []
7782
r2d2 = ["pool", "diesel/r2d2"]
7883
bb8 = ["pool", "dep:bb8"]
@@ -95,10 +100,12 @@ features = [
95100
"async-connection-wrapper",
96101
"sync-connection-wrapper",
97102
"r2d2",
103+
"migrations",
104+
"tokio/macros",
98105
]
99106
no-default-features = true
100107
rustc-args = ["--cfg", "docsrs"]
101-
rustdoc-args = ["--cfg", "docsrs"]
108+
rustdoc-args = ["--cfg", "docsrs", " -Z", "unstable-options", "--generate-link-to-definition"]
102109

103110
[workspace]
104111
members = [

examples/postgres/run-pending-migrations-with-rustls/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ edition = "2021"
66
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
77

88
[dependencies]
9-
diesel-async = { version = "0.6.0", path = "../../../", features = ["bb8", "postgres", "async-connection-wrapper"] }
9+
diesel-async = { version = "0.6.0", path = "../../../", features = ["bb8", "postgres", "migrations"] }
1010
futures-util = "0.3.21"
1111
rustls = "0.23.8"
1212
rustls-platform-verifier = "0.5.0"

examples/postgres/run-pending-migrations-with-rustls/src/main.rs

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
use diesel::{ConnectionError, ConnectionResult};
2-
use diesel_async::async_connection_wrapper::AsyncConnectionWrapper;
3-
use diesel_async::AsyncPgConnection;
2+
use diesel_async::{AsyncMigrationHarness, AsyncPgConnection};
43
use diesel_migrations::{embed_migrations, EmbeddedMigrations, MigrationHarness};
54
use futures_util::future::BoxFuture;
65
use futures_util::FutureExt;
@@ -10,19 +9,15 @@ use rustls_platform_verifier::ConfigVerifierExt;
109
pub const MIGRATIONS: EmbeddedMigrations = embed_migrations!();
1110

1211
#[tokio::main]
13-
async fn main() -> Result<(), Box<dyn std::error::Error>> {
12+
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
1413
// Should be in the form of postgres://user:password@localhost/database?sslmode=require
1514
let db_url = std::env::var("DATABASE_URL").expect("Env var `DATABASE_URL` not set");
1615

1716
let async_connection = establish_connection(db_url.as_str()).await?;
1817

19-
let mut async_wrapper: AsyncConnectionWrapper<AsyncPgConnection> =
20-
AsyncConnectionWrapper::from(async_connection);
21-
22-
tokio::task::spawn_blocking(move || {
23-
async_wrapper.run_pending_migrations(MIGRATIONS).unwrap();
24-
})
25-
.await?;
18+
let mut harness = AsyncMigrationHarness::new(async_connection);
19+
harness.run_pending_migrations(MIGRATIONS)?;
20+
let _async_connection = harness.into_inner();
2621

2722
Ok(())
2823
}

src/async_connection_wrapper.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ pub type AsyncConnectionWrapper<C, B = self::implementation::Tokio> =
9999
#[cfg(not(feature = "tokio"))]
100100
pub use self::implementation::AsyncConnectionWrapper;
101101

102-
mod implementation {
102+
pub(crate) mod implementation {
103103
use diesel::connection::{CacheSize, Instrumentation, SimpleConnection};
104104
use std::ops::{Deref, DerefMut};
105105

src/deref_connection.rs

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
use crate::UpdateAndFetchResults;
2+
use crate::{AsyncConnection, AsyncConnectionCore, SimpleAsyncConnection, TransactionManager};
3+
use diesel::associations::HasTable;
4+
use diesel::connection::CacheSize;
5+
use diesel::connection::Instrumentation;
6+
use diesel::QueryResult;
7+
use futures_util::future::BoxFuture;
8+
use std::ops::DerefMut;
9+
10+
impl<C> SimpleAsyncConnection for C
11+
where
12+
C: DerefMut + Send,
13+
C::Target: SimpleAsyncConnection + Send,
14+
{
15+
async fn batch_execute(&mut self, query: &str) -> diesel::QueryResult<()> {
16+
let conn = self.deref_mut();
17+
conn.batch_execute(query).await
18+
}
19+
}
20+
21+
impl<C> AsyncConnectionCore for C
22+
where
23+
C: DerefMut + Send,
24+
C::Target: AsyncConnectionCore,
25+
{
26+
type ExecuteFuture<'conn, 'query> =
27+
<C::Target as AsyncConnectionCore>::ExecuteFuture<'conn, 'query>;
28+
type LoadFuture<'conn, 'query> = <C::Target as AsyncConnectionCore>::LoadFuture<'conn, 'query>;
29+
type Stream<'conn, 'query> = <C::Target as AsyncConnectionCore>::Stream<'conn, 'query>;
30+
type Row<'conn, 'query> = <C::Target as AsyncConnectionCore>::Row<'conn, 'query>;
31+
32+
type Backend = <C::Target as AsyncConnectionCore>::Backend;
33+
34+
fn load<'conn, 'query, T>(&'conn mut self, source: T) -> Self::LoadFuture<'conn, 'query>
35+
where
36+
T: diesel::query_builder::AsQuery + 'query,
37+
T::Query: diesel::query_builder::QueryFragment<Self::Backend>
38+
+ diesel::query_builder::QueryId
39+
+ 'query,
40+
{
41+
let conn = self.deref_mut();
42+
conn.load(source)
43+
}
44+
45+
fn execute_returning_count<'conn, 'query, T>(
46+
&'conn mut self,
47+
source: T,
48+
) -> Self::ExecuteFuture<'conn, 'query>
49+
where
50+
T: diesel::query_builder::QueryFragment<Self::Backend>
51+
+ diesel::query_builder::QueryId
52+
+ 'query,
53+
{
54+
let conn = self.deref_mut();
55+
conn.execute_returning_count(source)
56+
}
57+
}
58+
59+
#[diagnostic::do_not_recommend]
60+
impl<C> AsyncConnection for C
61+
where
62+
C: DerefMut + Send,
63+
C::Target: AsyncConnection,
64+
{
65+
type TransactionManager =
66+
PoolTransactionManager<<C::Target as AsyncConnection>::TransactionManager>;
67+
68+
async fn establish(_database_url: &str) -> diesel::ConnectionResult<Self> {
69+
Err(diesel::result::ConnectionError::BadConnection(
70+
String::from("Cannot directly establish a pooled connection"),
71+
))
72+
}
73+
74+
fn transaction_state(
75+
&mut self,
76+
) -> &mut <Self::TransactionManager as crate::transaction_manager::TransactionManager<Self>>::TransactionStateData{
77+
let conn = self.deref_mut();
78+
conn.transaction_state()
79+
}
80+
81+
async fn begin_test_transaction(&mut self) -> diesel::QueryResult<()> {
82+
self.deref_mut().begin_test_transaction().await
83+
}
84+
85+
fn instrumentation(&mut self) -> &mut dyn Instrumentation {
86+
self.deref_mut().instrumentation()
87+
}
88+
89+
fn set_instrumentation(&mut self, instrumentation: impl Instrumentation) {
90+
self.deref_mut().set_instrumentation(instrumentation);
91+
}
92+
93+
fn set_prepared_statement_cache_size(&mut self, size: CacheSize) {
94+
self.deref_mut().set_prepared_statement_cache_size(size);
95+
}
96+
}
97+
98+
#[doc(hidden)]
99+
#[allow(missing_debug_implementations)]
100+
pub struct PoolTransactionManager<TM>(std::marker::PhantomData<TM>);
101+
102+
impl<C, TM> TransactionManager<C> for PoolTransactionManager<TM>
103+
where
104+
C: DerefMut + Send,
105+
C::Target: AsyncConnection<TransactionManager = TM>,
106+
TM: TransactionManager<C::Target>,
107+
{
108+
type TransactionStateData = TM::TransactionStateData;
109+
110+
async fn begin_transaction(conn: &mut C) -> diesel::QueryResult<()> {
111+
TM::begin_transaction(&mut **conn).await
112+
}
113+
114+
async fn rollback_transaction(conn: &mut C) -> diesel::QueryResult<()> {
115+
TM::rollback_transaction(&mut **conn).await
116+
}
117+
118+
async fn commit_transaction(conn: &mut C) -> diesel::QueryResult<()> {
119+
TM::commit_transaction(&mut **conn).await
120+
}
121+
122+
fn transaction_manager_status_mut(
123+
conn: &mut C,
124+
) -> &mut diesel::connection::TransactionManagerStatus {
125+
TM::transaction_manager_status_mut(&mut **conn)
126+
}
127+
128+
fn is_broken_transaction_manager(conn: &mut C) -> bool {
129+
TM::is_broken_transaction_manager(&mut **conn)
130+
}
131+
}
132+
133+
impl<Changes, Output, Conn> UpdateAndFetchResults<Changes, Output> for Conn
134+
where
135+
Conn: DerefMut + Send,
136+
Changes: diesel::prelude::Identifiable + HasTable + Send,
137+
Conn::Target: UpdateAndFetchResults<Changes, Output>,
138+
{
139+
fn update_and_fetch<'conn, 'changes>(
140+
&'conn mut self,
141+
changeset: Changes,
142+
) -> BoxFuture<'changes, QueryResult<Output>>
143+
where
144+
Changes: 'changes,
145+
'conn: 'changes,
146+
Self: 'changes,
147+
{
148+
self.deref_mut().update_and_fetch(changeset)
149+
}
150+
}

src/lib.rs

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,12 @@
55
//! with the main diesel crate. It only provides async variants of core diesel traits,
66
//! that perform actual io-work.
77
//! This includes async counterparts the following traits:
8-
//! * [`diesel::prelude::RunQueryDsl`](https://docs.diesel.rs/2.0.x/diesel/prelude/trait.RunQueryDsl.html)
9-
//! -> [`diesel_async::RunQueryDsl`](crate::RunQueryDsl)
10-
//! * [`diesel::connection::Connection`](https://docs.diesel.rs/2.0.x/diesel/connection/trait.Connection.html)
11-
//! -> [`diesel_async::AsyncConnection`](crate::AsyncConnection)
12-
//! * [`diesel::query_dsl::UpdateAndFetchResults`](https://docs.diesel.rs/2.0.x/diesel/query_dsl/trait.UpdateAndFetchResults.html)
13-
//! -> [`diesel_async::UpdateAndFetchResults`](crate::UpdateAndFetchResults)
8+
//! * [`diesel::prelude::RunQueryDsl`](https://docs.diesel.rs/2.3.x/diesel/prelude/trait.RunQueryDsl.html)
9+
//! -> [`diesel_async::RunQueryDsl`](crate::RunQueryDsl)
10+
//! * [`diesel::connection::Connection`](https://docs.diesel.rs/2.3.x/diesel/connection/trait.Connection.html)
11+
//! -> [`diesel_async::AsyncConnection`](crate::AsyncConnection)
12+
//! * [`diesel::query_dsl::UpdateAndFetchResults`](https://docs.diesel.rs/2.3.x/diesel/query_dsl/trait.UpdateAndFetchResults.html)
13+
//! -> [`diesel_async::UpdateAndFetchResults`](crate::UpdateAndFetchResults)
1414
//!
1515
//! These traits closely mirror their diesel counter parts while providing async functionality.
1616
//!
@@ -65,6 +65,26 @@
6565
//! # Ok(())
6666
//! # }
6767
//! ```
68+
//!
69+
//! ## Crate features:
70+
//!
71+
//! * `postgres`: Enables the [`AsyncPgConnection`] implementation
72+
//! * `mysql`: Enables the [`AsyncMysqlConnection`] implementation
73+
//! * `sqlite`: Enables the [`SyncConnectionWrapper`](crate::sync_connection_wrapper::SyncConnectionWrapper)
74+
//! and everything required to work with SQLite
75+
//! * `sync-connection-wrapper`: Enables the
76+
//! [`SyncConnectionWrapper`](crate::sync_connection_wrapper::SyncConnectionWrapper) which allows to
77+
//! wrap sync connections from [`diesel`] into async connection wrapper
78+
//! * `async-connection-wrapper`: Enables the [`AsyncConnectionWrapper`](crate::async_connection_wrapper::AsyncConnectionWrapper)
79+
//! which allows
80+
//! to use connection implementations from this crate as sync [`diesel::Connection`]
81+
//! * `migrations`: Enables the [`AsyncMigrationHarness`] to execute migrations via
82+
//! [`diesel_migrations`]
83+
//! * `pool`: Enables general support for connection pools
84+
//! * `r2d2`: Enables support for pooling via the [`r2d2`] crate
85+
//! * `bb8`: Enables support for pooling via the [`bb8`] crate
86+
//! * `mobc`: Enables support for pooling via the [`mobc`] crate
87+
//! * `deadpool`: Enables support for pooling via the [`deadpool`] crate
6888
6989
#![warn(
7090
missing_docs,
@@ -89,6 +109,9 @@ use scoped_futures::{ScopedBoxFuture, ScopedFutureExt};
89109

90110
#[cfg(feature = "async-connection-wrapper")]
91111
pub mod async_connection_wrapper;
112+
mod deref_connection;
113+
#[cfg(feature = "migrations")]
114+
mod migrations;
92115
#[cfg(feature = "mysql")]
93116
mod mysql;
94117
#[cfg(feature = "postgres")]
@@ -111,6 +134,9 @@ pub use self::pg::AsyncPgConnection;
111134
#[doc(inline)]
112135
pub use self::run_query_dsl::*;
113136

137+
#[doc(inline)]
138+
#[cfg(feature = "migrations")]
139+
pub use self::migrations::AsyncMigrationHarness;
114140
#[doc(inline)]
115141
pub use self::transaction_manager::{AnsiTransactionManager, TransactionManager};
116142

0 commit comments

Comments
 (0)