Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 668b070

Browse files
czochercljoly
authored andcommittedFeb 28, 2023
Add support for tokio-rusqlite
1 parent c0ea464 commit 668b070

21 files changed

+610
-153
lines changed
 

‎.github/workflows/ci.yml

+36-6
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ jobs:
2121
with:
2222
command: check
2323

24-
test:
25-
name: Test Suite
24+
test_no_features:
25+
name: Test no features
2626
runs-on: ubuntu-latest
2727
steps:
2828
- uses: actions/checkout@v2
@@ -35,6 +35,36 @@ jobs:
3535
with:
3636
command: test
3737

38+
test_all_features:
39+
name: Test all features
40+
runs-on: ubuntu-latest
41+
steps:
42+
- uses: actions/checkout@v2
43+
- uses: actions-rs/toolchain@v1
44+
with:
45+
profile: minimal
46+
toolchain: stable
47+
override: true
48+
- uses: actions-rs/cargo@v1
49+
with:
50+
command: test
51+
args: --all-features
52+
53+
test_async_tokio_rusqlite:
54+
name: Test feature async-tokio-rusqlite
55+
runs-on: ubuntu-latest
56+
steps:
57+
- uses: actions/checkout@v2
58+
- uses: actions-rs/toolchain@v1
59+
with:
60+
profile: minimal
61+
toolchain: stable
62+
override: true
63+
- uses: actions-rs/cargo@v1
64+
with:
65+
command: test
66+
args: --features async-tokio-rusqlite
67+
3868
min_rust_version:
3969
name: Check with minimal Rust version
4070
runs-on: ubuntu-latest
@@ -68,6 +98,9 @@ jobs:
6898
readme-sync:
6999
name: Sync Readme
70100
runs-on: ubuntu-latest
101+
defaults:
102+
run:
103+
working-directory: ./rusqlite_migration
71104
steps:
72105
- uses: actions/checkout@v2
73106
- uses: actions-rs/toolchain@v1
@@ -77,10 +110,7 @@ jobs:
77110
override: true
78111
- uses: Swatinem/rust-cache@v1
79112
- run: cargo install cargo-sync-readme
80-
- uses: actions-rs/cargo@v1
81-
with:
82-
command: sync-readme
83-
args: --check
113+
- run: cargo sync-readme --check
84114
clippy:
85115
name: Clippy
86116
runs-on: ubuntu-latest

‎.gitignore

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1-
/target
2-
Cargo.lock
1+
**/target
2+
**/Cargo.lock
33

44
# Output of some examples
55
my_db.db3
6+
my_db.wal
7+
my_db.shm
68

79
# Local IDE & editor files
810
.idea/

‎Cargo.toml

+2-30
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,2 @@
1-
[package]
2-
name = "rusqlite_migration"
3-
version = "1.1.0-alpha.2"
4-
authors = ["Clément Joly <l@131719.xyz>"]
5-
edition = "2018"
6-
license = "Apache-2.0"
7-
description = "Simple schema migration library for rusqlite using user_version instead of an SQL table to maintain the current schema version."
8-
keywords = ["rusqlite", "sqlite", "user_version", "database", "migration"]
9-
categories = ["database"]
10-
readme = "README.md"
11-
homepage = "https://cj.rs/rusqlite_migration"
12-
repository = "https://github.com/cljoly/rusqlite_migration"
13-
rust-version = "1.61"
14-
15-
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
16-
17-
[dependencies]
18-
log = "0.4"
19-
20-
[dependencies.rusqlite]
21-
version = ">=0.23.0"
22-
default-features = false
23-
features = []
24-
25-
[dev-dependencies]
26-
simple-logging = "2.0.2"
27-
env_logger = "0.10"
28-
anyhow = "1"
29-
lazy_static = "1.4.0"
30-
mktemp = "0.5"
1+
[workspace]
2+
members = ["rusqlite_migration", "rusqlite_migration_tests"]

‎README.md

-113
This file was deleted.

‎README.md

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
rusqlite_migration/README.md

‎examples/Cargo.toml

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
[workspace]
2+
members = ["*"]
3+
exclude = ["target"]
4+
resolver = "2"
5+

‎examples/async/Cargo.toml

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
[package]
2+
name = "example-async"
3+
version = "0.1.0"
4+
edition = "2018"
5+
publish = false
6+
7+
[dependencies]
8+
log = "0.4"
9+
simple-logging = "2.0.2"
10+
env_logger = "0.10"
11+
anyhow = "1"
12+
lazy_static = "1.4.0"
13+
mktemp = "0.5"
14+
tokio-rusqlite = "0.3.0"
15+
tokio = { version = "1.25.0", features = ["full"] }
16+
17+
[dependencies.rusqlite_migration]
18+
path = "../../rusqlite_migration"
19+
features = ["async-tokio-rusqlite"]
20+
21+
[dependencies.rusqlite]
22+
version = ">=0.23.0"
23+
default-features = false
24+
features = []

‎examples/async/src/main.rs

+91
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
use anyhow::Result;
2+
use lazy_static::lazy_static;
3+
use rusqlite::params;
4+
use rusqlite_migration::{AsyncMigrations, M};
5+
use tokio_rusqlite::Connection;
6+
7+
// Test that migrations are working
8+
#[cfg(test)]
9+
mod tests {
10+
use super::*;
11+
12+
#[test]
13+
fn migrations_test() {
14+
assert!(MIGRATIONS.validate().is_ok());
15+
}
16+
}
17+
18+
// Define migrations. These are applied atomically.
19+
lazy_static! {
20+
static ref MIGRATIONS: AsyncMigrations =
21+
AsyncMigrations::new(vec![
22+
M::up(include_str!("../../friend_car.sql")),
23+
// PRAGMA are better applied outside of migrations, see below for details.
24+
M::up(r#"
25+
ALTER TABLE friend ADD COLUMN birthday TEXT;
26+
ALTER TABLE friend ADD COLUMN comment TEXT;
27+
"#),
28+
29+
// This migration can be reverted
30+
M::up("CREATE TABLE animal(name TEXT);")
31+
.down("DROP TABLE animal;")
32+
33+
// In the future, if the need to change the schema arises, put
34+
// migrations here, like so:
35+
// M::up("CREATE INDEX UX_friend_email ON friend(email);"),
36+
// M::up("CREATE INDEX UX_friend_name ON friend(name);"),
37+
]);
38+
}
39+
40+
pub async fn init_db() -> Result<Connection> {
41+
let mut async_conn = Connection::open("./my_db.db3").await?;
42+
43+
// Update the database schema, atomically
44+
MIGRATIONS.to_latest(&mut async_conn).await?;
45+
46+
Ok(async_conn)
47+
}
48+
49+
#[tokio::main]
50+
async fn main() {
51+
env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("trace")).init();
52+
53+
let mut async_conn = init_db().await.unwrap();
54+
55+
// Apply some PRAGMA. These are often better applied outside of migrations, as some needs to be
56+
// executed for each connection (like `foreign_keys`) or to be executed outside transactions
57+
// (`journal_mode` is a noop in a transaction).
58+
async_conn
59+
.call(|conn| conn.pragma_update(None, "journal_mode", "WAL"))
60+
.await
61+
.unwrap();
62+
async_conn
63+
.call(|conn| conn.pragma_update(None, "foreign_keys", "ON"))
64+
.await
65+
.unwrap();
66+
67+
// Use the db 🥳
68+
async_conn
69+
.call(|conn| {
70+
conn.execute(
71+
"INSERT INTO friend (name, birthday) VALUES (?1, ?2)",
72+
params!["John", "1970-01-01"],
73+
)
74+
})
75+
.await
76+
.unwrap();
77+
78+
async_conn
79+
.call(|conn| conn.execute("INSERT INTO animal (name) VALUES (?1)", params!["dog"]))
80+
.await
81+
.unwrap();
82+
83+
// If we want to revert the last migration
84+
MIGRATIONS.to_version(&mut async_conn, 2).await.unwrap();
85+
86+
// The table was removed
87+
async_conn
88+
.call(|conn| conn.execute("INSERT INTO animal (name) VALUES (?1)", params!["cat"]))
89+
.await
90+
.unwrap_err();
91+
}

‎examples/simple/Cargo.toml

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
[package]
2+
name = "example-simple"
3+
version = "0.1.0"
4+
edition = "2018"
5+
publish = false
6+
7+
[dependencies]
8+
rusqlite_migration = { path = "../../rusqlite_migration" }
9+
log = "0.4"
10+
simple-logging = "2.0.2"
11+
env_logger = "0.10"
12+
anyhow = "1"
13+
lazy_static = "1.4.0"
14+
mktemp = "0.5"
15+
16+
[dependencies.rusqlite]
17+
version = ">=0.23.0"
18+
default-features = false
19+
features = []

‎examples/quick_start.rs ‎examples/simple/src/main.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ mod tests {
1818
lazy_static! {
1919
static ref MIGRATIONS: Migrations<'static> =
2020
Migrations::new(vec![
21-
M::up(include_str!("friend_car.sql")),
21+
M::up(include_str!("../../friend_car.sql")),
2222
// PRAGMA are better applied outside of migrations, see below for details.
2323
M::up(r#"
2424
ALTER TABLE friend ADD COLUMN birthday TEXT;

‎rusqlite_migration/Cargo.toml

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
[package]
2+
name = "rusqlite_migration"
3+
version = "1.1.0-alpha.2"
4+
authors = ["Clément Joly <l@131719.xyz>"]
5+
edition = "2018"
6+
license = "Apache-2.0"
7+
description = "Simple schema migration library for rusqlite using user_version instead of an SQL table to maintain the current schema version."
8+
keywords = ["rusqlite", "sqlite", "user_version", "database", "migration"]
9+
categories = ["database"]
10+
readme = "README.md"
11+
homepage = "https://cj.rs/rusqlite_migration"
12+
repository = "https://github.com/cljoly/rusqlite_migration"
13+
rust-version = "1.61"
14+
15+
[features]
16+
default = []
17+
### Enable support for async migrations with the use of `tokio-rusqlite`
18+
async-tokio-rusqlite = ["dep:tokio-rusqlite", "dep:tokio"]
19+
20+
[dependencies]
21+
tokio = { version = "1.25", features = ["macros"], optional = true }
22+
tokio-rusqlite = { version = "0.3.0", optional = true }
23+
log = "0.4"
24+
25+
[dependencies.rusqlite]
26+
version = ">=0.23.0"
27+
default-features = false
28+
features = []
29+
30+
[dev-dependencies]
31+
tokio = { version = "1.25", features = ["full"] }
32+
tokio-test = "0.4.2"
33+
simple-logging = "2.0.2"
34+
env_logger = "0.10"
35+
anyhow = "1"
36+
lazy_static = "1.4.0"
37+
mktemp = "0.5"

‎rusqlite_migration/README.md

+114
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
<!-- insert
2+
---
3+
title: "Rusqlite Migration"
4+
date: 2021-08-21T15:32:05
5+
description: "↕️ Simple database schema migration library for rusqlite, written with performance in mind."
6+
aliases:
7+
- /rusqlite-migration
8+
tags:
9+
- Rust
10+
- SQLite
11+
- Library
12+
---
13+
end_insert -->
14+
15+
<!-- remove -->
16+
<div align="center">
17+
18+
# Rusqlite Migration
19+
<!-- end_remove -->
20+
21+
<!-- insert
22+
{{< github_badge >}}
23+
24+
{{< rawhtml >}}
25+
<div class="badges">
26+
{{< /rawhtml >}}
27+
end_insert -->
28+
29+
[![docs.rs](https://img.shields.io/docsrs/rusqlite_migration?style=flat-square)](https://docs.rs/rusqlite_migration) [![Crates.io](https://img.shields.io/crates/v/rusqlite_migration?style=flat-square)](https://crates.io/crates/rusqlite_migration) ![](https://img.shields.io/github/languages/code-size/cljoly/rusqlite_migration?style=flat-square) [![unsafe forbidden](https://img.shields.io/badge/unsafe-forbidden-success.svg?style=flat-square)](https://github.com/rust-secure-code/safety-dance/) [![dependency status](https://deps.rs/repo/github/cljoly/rusqlite_migration/status.svg)](https://deps.rs/repo/github/cljoly/rusqlite_migration)
30+
31+
<!-- insert
32+
{{< rawhtml >}}
33+
end_insert -->
34+
</div>
35+
<!-- insert
36+
{{< /rawhtml >}}
37+
end_insert -->
38+
39+
<!-- cargo-sync-readme start -->
40+
41+
Rusqlite Migration is a simple and performant schema migration library for [rusqlite](https://lib.rs/crates/rusqlite).
42+
43+
* **Performance**:
44+
* *Fast database opening*: to keep track of the current migration state, most tools create one or more tables in the database. These tables require parsing by SQLite and are queried with SQL statements. This library uses the [`user_version`][uv] value instead. It’s much lighter as it is just an integer at a [fixed offset][uv_offset] in the SQLite file.
45+
* *Fast compilation*: this crate is very small and does not use macros to define the migrations.
46+
* **Simplicity**: this crate strives for simplicity. Just define a set of SQL statements as strings in your Rust code. Add more SQL statements over time as needed. No external CLI required. Additionally, rusqlite_migration works especially well with other small libraries complementing rusqlite, like [serde_rusqlite][].
47+
48+
[diesel_migrations]: https://lib.rs/crates/diesel_migrations
49+
[pgfine]: https://crates.io/crates/pgfine
50+
[movine]: https://crates.io/crates/movine
51+
[uv]: https://sqlite.org/pragma.html#pragma_user_version
52+
[uv_offset]: https://www.sqlite.org/fileformat.html#user_version_number
53+
[serde_rusqlite]: https://crates.io/crates/serde_rusqlite
54+
55+
## Example
56+
57+
Here, we define SQL statements to run with [`Migrations::new()`][migrations_new] and run these (if necessary) with [`Migrations::to_latest()`][migrations_to_latest].
58+
59+
[migrations_new]: https://docs.rs/rusqlite_migration/latest/rusqlite_migration/struct.Migrations.html#method.new
60+
[migrations_to_latest]: https://docs.rs/rusqlite_migration/latest/rusqlite_migration/struct.Migrations.html#method.to_latest
61+
62+
``` rust
63+
use rusqlite::{params, Connection};
64+
use rusqlite_migration::{Migrations, M};
65+
66+
// 1️⃣ Define migrations
67+
let migrations = Migrations::new(vec![
68+
M::up("CREATE TABLE friend(name TEXT NOT NULL);"),
69+
// In the future, add more migrations here:
70+
//M::up("ALTER TABLE friend ADD COLUMN email TEXT;"),
71+
]);
72+
73+
let mut conn = Connection::open_in_memory().unwrap();
74+
75+
// Apply some PRAGMA, often better to do it outside of migrations
76+
conn.pragma_update(None, "journal_mode", &"WAL").unwrap();
77+
78+
// 2️⃣ Update the database schema, atomically
79+
migrations.to_latest(&mut conn).unwrap();
80+
81+
// 3️⃣ Use the database 🥳
82+
conn.execute("INSERT INTO friend (name) VALUES (?1)", params!["John"])
83+
.unwrap();
84+
```
85+
86+
Please see the [examples](https://github.com/cljoly/rusqlite_migrate/tree/master/examples) folder for more, in particular:
87+
- `async` migrations in the (quick_start_async.rs)[https://github.com/cljoly/rusqlite_migrate/tree/master/examples/quick_start_async.rs)] file
88+
- migrations with multiple SQL statements (using for instance `r#"…"` or `include_str!(…)`)
89+
- use of lazy_static
90+
- migrations to previous versions (downward migrations)
91+
92+
I’ve also made a [cheatsheet of SQLite pragma for improved performance and consistency](https://cj.rs/blog/sqlite-pragma-cheatsheet-for-performance-and-consistency/).
93+
94+
### Built-in tests
95+
96+
To test that the migrations are working, you can add this in your test module:
97+
98+
``` rust
99+
#[test]
100+
fn migrations_test() {
101+
assert!(MIGRATIONS.validate().is_ok());
102+
}
103+
```
104+
105+
## Contributing
106+
107+
Contributions (documentation or code improvements in particular) are welcome, see [contributing](https://cj.rs/docs/contribute/)!
108+
109+
## Acknowledgments
110+
111+
I would like to thank all the contributors, as well as the authors of the dependencies this crate uses.
112+
113+
114+
<!-- cargo-sync-readme end -->

‎rusqlite_migration/src/asynch.rs

+143
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
use tokio_rusqlite::Connection as AsyncConnection;
2+
3+
use crate::errors::Result;
4+
use crate::{Migrations, SchemaVersion, M};
5+
6+
/// Adapter to make `Migrations` available in an async context.
7+
#[derive(Debug, PartialEq, Eq, Clone)]
8+
pub struct AsyncMigrations {
9+
migrations: Migrations<'static>,
10+
}
11+
12+
impl AsyncMigrations {
13+
/// Adapt a [Migrations](crate::Migrations) instance for use in an asynchronous context.
14+
///
15+
/// # Example
16+
///
17+
/// ```rust
18+
/// use rusqlite_migration::{Migrations, AsyncMigrations, M};
19+
///
20+
/// let migrations = AsyncMigrations::new(vec![
21+
/// M::up("CREATE TABLE animals (name TEXT);"),
22+
/// M::up("CREATE TABLE food (name TEXT);"),
23+
/// ]);
24+
/// ```
25+
pub fn new(ms: Vec<M<'static>>) -> Self {
26+
Self {
27+
migrations: Migrations::new(ms),
28+
}
29+
}
30+
31+
/// Asynchronous version of the same method in the [Migrations](crate::Migrations::current_version) struct.
32+
///
33+
/// # Example
34+
///
35+
/// ```rust
36+
/// # tokio_test::block_on(async {
37+
/// use rusqlite_migration::{Migrations, AsyncMigrations, M, SchemaVersion};
38+
/// use std::num::NonZeroUsize;
39+
///
40+
/// let mut conn = tokio_rusqlite::Connection::open_in_memory().await.unwrap();
41+
///
42+
/// let migrations = AsyncMigrations::new(vec![
43+
/// M::up("CREATE TABLE animals (name TEXT);"),
44+
/// M::up("CREATE TABLE food (name TEXT);"),
45+
/// ]);
46+
///
47+
/// assert_eq!(SchemaVersion::NoneSet, migrations.current_version(&conn).await.unwrap());
48+
///
49+
/// // Go to the latest version
50+
/// migrations.to_latest(&mut conn).await.unwrap();
51+
///
52+
/// assert_eq!(SchemaVersion::Inside(NonZeroUsize::new(2).unwrap()), migrations.current_version(&conn).await.unwrap());
53+
/// # })
54+
/// ```
55+
pub async fn current_version(&self, async_conn: &AsyncConnection) -> Result<SchemaVersion> {
56+
let m = self.migrations.clone();
57+
async_conn.call(move |conn| m.current_version(conn)).await
58+
}
59+
60+
/// Asynchronous version of the same method in the [Migrations](super::Migrations::to_latest) struct.
61+
///
62+
/// # Example
63+
///
64+
/// ```rust
65+
/// # tokio_test::block_on(async {
66+
/// use rusqlite_migration::{Migrations, AsyncMigrations, M};
67+
/// let mut conn = tokio_rusqlite::Connection::open_in_memory().await.unwrap();
68+
///
69+
/// let migrations = AsyncMigrations::new(vec![
70+
/// M::up("CREATE TABLE animals (name TEXT);"),
71+
/// M::up("CREATE TABLE food (name TEXT);"),
72+
/// ]);
73+
///
74+
/// // Go to the latest version
75+
/// migrations.to_latest(&mut conn).await.unwrap();
76+
///
77+
/// // You can then insert values in the database
78+
/// conn.call(|conn| conn.execute("INSERT INTO animals (name) VALUES (?)", ["dog"])).await.unwrap();
79+
/// conn.call(|conn| conn.execute("INSERT INTO food (name) VALUES (?)", ["carrot"])).await.unwrap();
80+
/// # });
81+
/// ```
82+
pub async fn to_latest(&self, async_conn: &mut AsyncConnection) -> Result<()> {
83+
let m = self.migrations.clone();
84+
async_conn.call(move |conn| m.to_latest(conn)).await
85+
}
86+
87+
/// Asynchronous version of the same method in the [Migrations](crate::Migrations::to_version) struct.
88+
///
89+
/// # Example
90+
///
91+
/// ```rust
92+
/// # tokio_test::block_on(async {
93+
/// use rusqlite_migration::{Migrations, AsyncMigrations, M};
94+
/// let mut conn = tokio_rusqlite::Connection::open_in_memory().await.unwrap();
95+
/// let migrations = AsyncMigrations::new(vec![
96+
/// // 0: version 0, before having run any migration
97+
/// M::up("CREATE TABLE animals (name TEXT);").down("DROP TABLE animals;"),
98+
/// // 1: version 1, after having created the “animals” table
99+
/// M::up("CREATE TABLE food (name TEXT);").down("DROP TABLE food;"),
100+
/// // 2: version 2, after having created the food table
101+
/// ]);
102+
///
103+
/// migrations.to_latest(&mut conn).await.unwrap(); // Create all tables
104+
///
105+
/// // Go back to version 1, i.e. after running the first migration
106+
/// migrations.to_version(&mut conn, 1).await;
107+
/// conn.call(|conn| conn.execute("INSERT INTO animals (name) VALUES (?)", ["dog"])).await.unwrap();
108+
/// conn.call(|conn| conn.execute("INSERT INTO food (name) VALUES (?)", ["carrot"]).unwrap_err()).await;
109+
///
110+
/// // Go back to an empty database
111+
/// migrations.to_version(&mut conn, 0).await;
112+
/// conn.call(|conn| conn.execute("INSERT INTO animals (name) VALUES (?)", ["cat"]).unwrap_err()).await;
113+
/// conn.call(|conn| conn.execute("INSERT INTO food (name) VALUES (?)", ["milk"]).unwrap_err()).await;
114+
/// # })
115+
/// ```
116+
pub async fn to_version(&self, async_conn: &mut AsyncConnection, version: usize) -> Result<()> {
117+
let m = self.migrations.clone();
118+
async_conn
119+
.call(move |conn| m.to_version(conn, version))
120+
.await
121+
}
122+
123+
/// Asynchronous version of the same method in the [Migrations](crate::Migrations::validate) struct.
124+
///
125+
/// # Example
126+
///
127+
/// ```rust
128+
/// #[cfg(test)]
129+
/// mod tests {
130+
///
131+
/// // … Other tests …
132+
///
133+
/// #[tokio::test]
134+
/// fn migrations_test() {
135+
/// assert!(migrations.validate().await.is_ok());
136+
/// }
137+
/// }
138+
/// ```
139+
pub async fn validate(&self) -> Result<()> {
140+
let mut async_conn = AsyncConnection::open_in_memory().await?;
141+
self.to_latest(&mut async_conn).await
142+
}
143+
}
File renamed without changes.

‎src/lib.rs ‎rusqlite_migration/src/lib.rs

+5
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ limitations under the License.
6363
//! ```
6464
//!
6565
//! Please see the [examples](https://github.com/cljoly/rusqlite_migrate/tree/master/examples) folder for more, in particular:
66+
//! - `async` migrations in the (quick_start_async.rs)[https://github.com/cljoly/rusqlite_migrate/tree/master/examples/quick_start_async.rs)] file
6667
//! - migrations with multiple SQL statements (using for instance `r#"…"` or `include_str!(…)`)
6768
//! - use of lazy_static
6869
//! - migrations to previous versions (downward migrations)
@@ -94,10 +95,14 @@ use log::{debug, info, trace, warn};
9495
use rusqlite::NO_PARAMS;
9596
use rusqlite::{Connection, OptionalExtension, Transaction};
9697

98+
#[cfg(feature = "async-tokio-rusqlite")]
99+
mod asynch;
97100
mod errors;
98101

99102
#[cfg(test)]
100103
mod tests;
104+
#[cfg(feature = "async-tokio-rusqlite")]
105+
pub use asynch::AsyncMigrations;
101106
pub use errors::{
102107
Error, ForeignKeyCheckError, HookError, HookResult, MigrationDefinitionError, Result,
103108
SchemaVersionError,
File renamed without changes.

‎rusqlite_migration_tests/Cargo.toml

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
[package]
2+
name = "rusqlite_migration_tests"
3+
version = "1.1.0"
4+
authors = ["Clément Joly <l@131719.xyz>"]
5+
edition = "2018"
6+
license = "Apache-2.0"
7+
description = "Simple schema migration library for rusqlite using user_version instead of an SQL table to maintain the current schema version."
8+
keywords = ["rusqlite", "sqlite", "user_version", "database", "migration"]
9+
categories = ["database"]
10+
readme = "README.md"
11+
homepage = "https://cj.rs/rusqlite_migration"
12+
repository = "https://github.com/cljoly/rusqlite_migration"
13+
rust-version = "1.61"
14+
publish = false
15+
16+
[dependencies]
17+
tokio = { version = "1.25", features = ["macros"] }
18+
tokio-rusqlite = { version = "0.3.0" }
19+
log = "0.4"
20+
21+
[dependencies.rusqlite_migration]
22+
path = "../rusqlite_migration"
23+
features = ["async-tokio-rusqlite"]
24+
25+
[dependencies.rusqlite]
26+
version = ">=0.23.0"
27+
default-features = false
28+
features = []
29+
30+
[dev-dependencies]
31+
tokio-test = "0.4.2"
32+
simple-logging = "2.0.2"
33+
env_logger = "0.10"
34+
anyhow = "1"
35+
lazy_static = "1.4.0"
36+
mktemp = "0.5"
37+
38+
[[test]]
39+
name = "integration_tests"
40+
path = "tests/lib.rs"
41+
harness = true
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
use std::num::NonZeroUsize;
2+
3+
use rusqlite::params;
4+
use rusqlite_migration::{AsyncMigrations, SchemaVersion, M};
5+
use tokio_rusqlite::Connection;
6+
7+
#[tokio::test]
8+
async fn main_test() {
9+
simple_logging::log_to_stderr(log::LevelFilter::Trace);
10+
11+
let mut conn = Connection::open_in_memory().await.unwrap();
12+
// Define migrations
13+
let mut ms = vec![
14+
M::up("CREATE TABLE t(a);"),
15+
M::up(include_str!("../../examples/friend_car.sql")),
16+
M::up("ALTER TABLE friend ADD COLUMN birthday TEXT;"),
17+
];
18+
19+
{
20+
let migrations = AsyncMigrations::new(ms.clone());
21+
migrations.to_latest(&mut conn).await.unwrap();
22+
23+
assert_eq!(
24+
Ok(SchemaVersion::Inside(NonZeroUsize::new(3).unwrap())),
25+
migrations.current_version(&conn).await
26+
);
27+
28+
conn.call(|c| {
29+
c.execute(
30+
"INSERT INTO friend (name, birthday) VALUES (?1, ?2)",
31+
params!["John", "1970-01-01"],
32+
)
33+
})
34+
.await
35+
.unwrap();
36+
}
37+
38+
// Later, we add things to the schema
39+
ms.push(M::up("CREATE INDEX UX_friend_email ON friend(email);"));
40+
ms.push(M::up("ALTER TABLE friend RENAME COLUMN birthday TO birth;"));
41+
42+
{
43+
let migrations = AsyncMigrations::new(ms.clone());
44+
migrations.to_latest(&mut conn).await.unwrap();
45+
46+
assert_eq!(
47+
Ok(SchemaVersion::Inside(NonZeroUsize::new(5).unwrap())),
48+
migrations.current_version(&conn).await
49+
);
50+
51+
conn.call(|c| {
52+
c.execute(
53+
"INSERT INTO friend (name, birth) VALUES (?1, ?2)",
54+
params!["Alice", "2000-01-01"],
55+
)
56+
})
57+
.await
58+
.unwrap();
59+
}
60+
61+
// Later still
62+
ms.push(M::up("DROP INDEX UX_friend_email;"));
63+
64+
{
65+
let migrations = AsyncMigrations::new(ms.clone());
66+
migrations.to_latest(&mut conn).await.unwrap();
67+
68+
assert_eq!(
69+
Ok(SchemaVersion::Inside(NonZeroUsize::new(6).unwrap())),
70+
migrations.current_version(&conn).await
71+
);
72+
73+
conn.call(|c| {
74+
c.execute(
75+
"INSERT INTO friend (name, birth) VALUES (?1, ?2)",
76+
params!["Alice", "2000-01-01"],
77+
)
78+
})
79+
.await
80+
.unwrap();
81+
}
82+
}

‎tests/integration_test.rs ‎rusqlite_migration_tests/tests/integration_test.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ fn main_test() {
1111
// Define migrations
1212
let mut ms = vec![
1313
M::up("CREATE TABLE t(a);"),
14-
M::up(include_str!("../examples/friend_car.sql")),
14+
M::up(include_str!("../../examples/friend_car.sql")),
1515
M::up("ALTER TABLE friend ADD COLUMN birthday TEXT;"),
1616
];
1717

‎rusqlite_migration_tests/tests/lib.rs

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
mod async_integration_test;
2+
mod integration_test;
3+
mod multiline_test;
4+
mod up_and_down;
File renamed without changes.
File renamed without changes.

0 commit comments

Comments
 (0)
Please sign in to comment.