Skip to content
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

Add SyncConnectionWrapper type #146

Merged
merged 9 commits into from
Apr 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
122 changes: 101 additions & 21 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,19 +21,16 @@ jobs:
fail-fast: false
matrix:
rust: ["stable", "beta", "nightly"]
backend: ["postgres", "mysql"]
os: [ubuntu-latest, macos-latest, windows-latest]
backend: ["postgres", "mysql", "sqlite"]
os: [ubuntu-latest, macos-latest, macos-14, windows-2019]
runs-on: ${{ matrix.os }}
steps:
- name: Checkout sources
uses: actions/checkout@v2
uses: actions/checkout@v4

- name: Cache cargo registry
uses: actions/cache@v2
uses: Swatinem/rust-cache@v2
with:
path: |
~/.cargo/registry
~/.cargo/git
key: ${{ runner.os }}-${{ matrix.backend }}-cargo-${{ hashFiles('**/Cargo.toml') }}

- name: Set environment variables
Expand Down Expand Up @@ -66,24 +63,86 @@ jobs:
mysql -e "create database diesel_test; create database diesel_unit_test; grant all on \`diesel_%\`.* to 'root'@'localhost';" -uroot -proot
echo "DATABASE_URL=mysql://root:root@localhost/diesel_test" >> $GITHUB_ENV

- name: Install sqlite (Linux)
if: runner.os == 'Linux' && matrix.backend == 'sqlite'
run: |
curl -fsS --retry 3 -o sqlite-autoconf-3400100.tar.gz https://www.sqlite.org/2022/sqlite-autoconf-3400100.tar.gz
tar zxf sqlite-autoconf-3400100.tar.gz
cd sqlite-autoconf-3400100
CFLAGS="$CFLAGS -O2 -fno-strict-aliasing \
-DSQLITE_DEFAULT_FOREIGN_KEYS=1 \
-DSQLITE_SECURE_DELETE \
-DSQLITE_ENABLE_COLUMN_METADATA \
-DSQLITE_ENABLE_FTS3_PARENTHESIS \
-DSQLITE_ENABLE_RTREE=1 \
-DSQLITE_SOUNDEX=1 \
-DSQLITE_ENABLE_UNLOCK_NOTIFY \
-DSQLITE_OMIT_LOOKASIDE=1 \
-DSQLITE_ENABLE_DBSTAT_VTAB \
-DSQLITE_ENABLE_UPDATE_DELETE_LIMIT=1 \
-DSQLITE_ENABLE_LOAD_EXTENSION \
-DSQLITE_ENABLE_JSON1 \
-DSQLITE_LIKE_DOESNT_MATCH_BLOBS \
-DSQLITE_THREADSAFE=1 \
-DSQLITE_ENABLE_FTS3_TOKENIZER=1 \
-DSQLITE_MAX_SCHEMA_RETRY=25 \
-DSQLITE_ENABLE_PREUPDATE_HOOK \
-DSQLITE_ENABLE_SESSION \
-DSQLITE_ENABLE_STMTVTAB \
-DSQLITE_MAX_VARIABLE_NUMBER=250000" \
./configure --prefix=/usr \
--enable-threadsafe \
--enable-dynamic-extensions \
--libdir=/usr/lib/x86_64-linux-gnu \
--libexecdir=/usr/lib/x86_64-linux-gnu/sqlite3
sudo make
sudo make install
echo "DATABASE_URL=/tmp/test.db" >> $GITHUB_ENV

- name: Install postgres (MacOS)
if: runner.os == 'macOS' && matrix.backend == 'postgres'
if: matrix.os == 'macos-latest' && matrix.backend == 'postgres'
run: |
initdb -D /usr/local/var/postgres
pg_ctl -D /usr/local/var/postgres start
sleep 3
createuser -s postgres
echo "DATABASE_URL=postgres://postgres@localhost/" >> $GITHUB_ENV

- name: Install postgres (MacOS M1)
if: matrix.os == 'macos-14' && matrix.backend == 'postgres'
run: |
brew install postgresql
brew services start postgresql@14
sleep 3
createuser -s postgres
echo "DATABASE_URL=postgres://postgres@localhost/" >> $GITHUB_ENV
- name: Install sqlite (MacOS)
if: runner.os == 'macOS' && matrix.backend == 'sqlite'
run: |
brew install sqlite
echo "DATABASE_URL=/tmp/test.db" >> $GITHUB_ENV

- name: Install mysql (MacOS)
if: runner.os == 'macOS' && matrix.backend == 'mysql'
if: matrix.os == 'macos-latest' && matrix.backend == 'mysql'
run: |
brew install [email protected]
/usr/local/opt/[email protected]/bin/mysql_install_db
/usr/local/opt/[email protected]/bin/mysql.server start
sleep 3
/usr/local/opt/[email protected]/bin/mysqladmin -u runner password diesel
/usr/local/opt/[email protected]/bin/mysql -e "create database diesel_test; create database diesel_unit_test; grant all on \`diesel_%\`.* to 'runner'@'localhost';" -urunner
echo "DATABASE_URL=mysql://runner:diesel@localhost/diesel_test" >> $GITHUB_ENV

- name: Install mysql (MacOS M1)
if: matrix.os == 'macos-14' && matrix.backend == 'mysql'
run: |
brew install --overwrite [email protected]
/usr/local/opt/[email protected]/bin/mysql_install_db
/usr/local/opt/[email protected]/bin/mysql.server start
brew install [email protected]
ls /opt/homebrew/opt/[email protected]
/opt/homebrew/opt/[email protected]/bin/mysql_install_db
/opt/homebrew/opt/[email protected]/bin/mysql.server start
sleep 3
/usr/local/opt/mariadb@10.8/bin/mysql -e "ALTER USER 'runner'@'localhost' IDENTIFIED BY 'diesel';" -urunner
/usr/local/opt/mariadb@10.8/bin/mysql -e "create database diesel_test; create database diesel_unit_test; grant all on \`diesel_%\`.* to 'runner'@'localhost';" -urunner -pdiesel
/opt/homebrew/opt/mariadb@11.3/bin/mysqladmin -u runner password diesel
/opt/homebrew/opt/mariadb@11.3/bin/mysql -e "create database diesel_test; create database diesel_unit_test; grant all on \`diesel_%\`.* to 'runner'@'localhost';" -urunner
echo "DATABASE_URL=mysql://runner:diesel@localhost/diesel_test" >> $GITHUB_ENV

- name: Install postgres (Windows)
Expand All @@ -106,6 +165,22 @@ jobs:
run: |
echo "DATABASE_URL=mysql://root@localhost/diesel_test" >> $GITHUB_ENV

- name: Install sqlite (Windows)
if: runner.os == 'Windows' && matrix.backend == 'sqlite'
shell: cmd
run: |
choco install sqlite
cd /D C:\ProgramData\chocolatey\lib\SQLite\tools
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat"
lib /machine:x64 /def:sqlite3.def /out:sqlite3.lib
- name: Set variables for sqlite (Windows)
if: runner.os == 'Windows' && matrix.backend == 'sqlite'
shell: bash
run: |
echo "C:\ProgramData\chocolatey\lib\SQLite\tools" >> $GITHUB_PATH
echo "SQLITE3_LIB_DIR=C:\ProgramData\chocolatey\lib\SQLite\tools" >> $GITHUB_ENV
echo "DATABASE_URL=C:\test.db" >> $GITHUB_ENV

- name: Install rust toolchain
uses: dtolnay/rust-toolchain@master
with:
Expand All @@ -115,26 +190,29 @@ jobs:

- name: Test diesel_async
run: cargo +${{ matrix.rust }} test --manifest-path Cargo.toml --no-default-features --features "${{ matrix.backend }} deadpool bb8 mobc"
- name: Run examples

- name: Run examples (Postgres)
if: matrix.backend == 'postgres'
run: |
cargo +${{ matrix.rust }} check --manifest-path examples/postgres/pooled-with-rustls/Cargo.toml
cargo +${{ matrix.rust }} check --manifest-path examples/postgres/run-pending-migrations-with-rustls/Cargo.toml

- name: Run examples (Sqlite)
if: matrix.backend == 'sqlite'
run: |
cargo +${{ matrix.rust }} check --manifest-path examples/sync-wrapper/Cargo.toml

rustfmt_and_clippy:
name: Check rustfmt style && run clippy
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
with:
components: clippy, rustfmt
- name: Cache cargo registry
uses: actions/cache@v2
uses: Swatinem/rust-cache@v2
with:
path: |
~/.cargo/registry
~/.cargo/git
key: clippy-cargo-${{ hashFiles('**/Cargo.toml') }}

- name: Remove potential newer clippy.toml from dependencies
Expand All @@ -152,12 +230,14 @@ jobs:
name: Check Minimal supported rust version (1.65.0)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- uses: dtolnay/[email protected]
- uses: dtolnay/rust-toolchain@nightly
- uses: taiki-e/install-action@cargo-hack
- uses: taiki-e/install-action@cargo-minimal-versions
- name: Check diesel-async
# cannot test mysql yet as that crate
# has broken min-version dependencies
# cannot test sqlite yet as that crate
# as broken min-version dependencies as well
run: cargo +stable minimal-versions check -p diesel-async --features "postgres bb8 deadpool mobc"
10 changes: 8 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,9 @@ diesel_migrations = "2.1.0"
default = []
mysql = ["diesel/mysql_backend", "mysql_async", "mysql_common", "futures-channel", "tokio"]
postgres = ["diesel/postgres_backend", "tokio-postgres", "tokio", "tokio/rt"]
async-connection-wrapper = []
sqlite = ["diesel/sqlite", "sync-connection-wrapper"]
sync-connection-wrapper = ["tokio/rt"]
async-connection-wrapper = ["tokio/net"]
r2d2 = ["diesel/r2d2"]

[[test]]
Expand All @@ -47,7 +49,7 @@ path = "tests/lib.rs"
harness = true

[package.metadata.docs.rs]
features = ["postgres", "mysql", "deadpool", "bb8", "mobc", "async-connection-wrapper", "r2d2"]
features = ["postgres", "mysql", "sqlite", "deadpool", "bb8", "mobc", "async-connection-wrapper", "sync-connection-wrapper", "r2d2"]
no-default-features = true
rustc-args = ["--cfg", "doc_cfg"]
rustdoc-args = ["--cfg", "doc_cfg"]
Expand All @@ -57,4 +59,8 @@ members = [
".",
"examples/postgres/pooled-with-rustls",
"examples/postgres/run-pending-migrations-with-rustls",
"examples/sync-wrapper",
]

[patch.crates-io]
diesel = { git = "http://github.com/diesel-rs/diesel", rev = "793de72" }
17 changes: 17 additions & 0 deletions examples/sync-wrapper/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
[package]
name = "sync-wrapper"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
diesel = { version = "2.1.0", default-features = false }
diesel-async = { version = "0.4.0", path = "../../", features = ["sync-connection-wrapper", "async-connection-wrapper"] }
diesel_migrations = "2.1.0"
futures-util = "0.3.21"
tokio = { version = "1.2.0", default-features = false, features = ["macros", "rt-multi-thread"] }

[features]
default = ["sqlite"]
sqlite = ["diesel-async/sqlite"]
9 changes: 9 additions & 0 deletions examples/sync-wrapper/diesel.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# For documentation on how to configure this file,
# see https://diesel.rs/guides/configuring-diesel-cli

[print_schema]
file = "src/schema.rs"
custom_type_derives = ["diesel::query_builder::QueryId"]

[migrations_directory]
dir = "migrations"
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
DROP TABLE IF EXISTS users;
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT);

INSERT INTO users(id, name) VALUES(123, 'hello world');
89 changes: 89 additions & 0 deletions examples/sync-wrapper/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
use diesel::prelude::*;
use diesel::sqlite::{Sqlite, SqliteConnection};
use diesel_async::async_connection_wrapper::AsyncConnectionWrapper;
use diesel_async::sync_connection_wrapper::SyncConnectionWrapper;
use diesel_async::{AsyncConnection, RunQueryDsl, SimpleAsyncConnection};
use diesel_migrations::{embed_migrations, EmbeddedMigrations, MigrationHarness};

// ordinary diesel model setup

table! {
users {
id -> Integer,
name -> Text,
}
}

#[derive(Debug, Queryable, Selectable)]
#[diesel(table_name = users)]
struct User {
id: i32,
name: String,
}

const MIGRATIONS: EmbeddedMigrations = embed_migrations!();

type InnerConnection = SqliteConnection;

type InnerDB = Sqlite;

async fn establish(db_url: &str) -> ConnectionResult<SyncConnectionWrapper<InnerConnection>> {
// It is necessary to specify the specific inner connection type because of inference issues
SyncConnectionWrapper::<SqliteConnection>::establish(db_url).await
}

async fn run_migrations<A>(async_connection: A) -> Result<(), Box<dyn std::error::Error>>
where
A: AsyncConnection<Backend = InnerDB> + 'static,
{
let mut async_wrapper: AsyncConnectionWrapper<A> =
AsyncConnectionWrapper::from(async_connection);

tokio::task::spawn_blocking(move || {
async_wrapper.run_pending_migrations(MIGRATIONS).unwrap();
})
.await
.map_err(|e| Box::new(e) as Box<dyn std::error::Error>)
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let db_url = std::env::var("DATABASE_URL").expect("Env var `DATABASE_URL` not set");

// create an async connection for the migrations
let sync_wrapper: SyncConnectionWrapper<InnerConnection> = establish(&db_url).await?;
run_migrations(sync_wrapper).await?;

let mut sync_wrapper: SyncConnectionWrapper<InnerConnection> = establish(&db_url).await?;

sync_wrapper.batch_execute("DELETE FROM users").await?;

sync_wrapper
.batch_execute("INSERT INTO users(id, name) VALUES (3, 'toto')")
.await?;

let data: Vec<User> = users::table
.select(User::as_select())
.load(&mut sync_wrapper)
.await?;
println!("{data:?}");

diesel::delete(users::table)
.execute(&mut sync_wrapper)
.await?;

diesel::insert_into(users::table)
.values((users::id.eq(1), users::name.eq("iLuke")))
.execute(&mut sync_wrapper)
.await?;

let data: Vec<User> = users::table
.filter(users::id.gt(0))
.or_filter(users::name.like("%Luke"))
.select(User::as_select())
.load(&mut sync_wrapper)
.await?;
println!("{data:?}");

Ok(())
}
18 changes: 16 additions & 2 deletions src/async_connection_wrapper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,13 +100,14 @@ pub type AsyncConnectionWrapper<C, B = self::implementation::Tokio> =
pub use self::implementation::AsyncConnectionWrapper;

mod implementation {
use diesel::connection::SimpleConnection;
use diesel::connection::{Instrumentation, SimpleConnection};

use super::*;

pub struct AsyncConnectionWrapper<C, B> {
inner: C,
runtime: B,
instrumentation: Option<Box<dyn Instrumentation>>,
}

impl<C, B> From<C> for AsyncConnectionWrapper<C, B>
Expand All @@ -118,6 +119,7 @@ mod implementation {
Self {
inner,
runtime: B::get_runtime(),
instrumentation: None,
}
}
}
Expand Down Expand Up @@ -148,7 +150,11 @@ mod implementation {
let runtime = B::get_runtime();
let f = C::establish(database_url);
let inner = runtime.block_on(f)?;
Ok(Self { inner, runtime })
Ok(Self {
inner,
runtime,
instrumentation: None,
})
}

fn execute_returning_count<T>(&mut self, source: &T) -> diesel::QueryResult<usize>
Expand All @@ -164,6 +170,14 @@ mod implementation {
) -> &mut <Self::TransactionManager as diesel::connection::TransactionManager<Self>>::TransactionStateData{
self.inner.transaction_state()
}

fn instrumentation(&mut self) -> &mut dyn Instrumentation {
&mut self.instrumentation
}

fn set_instrumentation(&mut self, instrumentation: impl Instrumentation) {
self.instrumentation = Some(Box::new(instrumentation));
}
}

impl<C, B> diesel::connection::LoadConnection for AsyncConnectionWrapper<C, B>
Expand Down
Loading