Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
eaf21f1
implement `LoadQuery` for `InsertStatement` with `BatchInsert` for `S…
bwqr May 25, 2025
dd92b09
put LoadQuery implementation behind `returning_clauses_for_sqlite_3_3…
bwqr May 25, 2025
2bd74c7
implement RunQueryDsl on batch insert statements with sqlite connecti…
bwqr May 29, 2025
18ed4ed
use `LoadIter` as iterator for `LoadQuery` implementation of sqlite's…
bwqr May 29, 2025
b70cbcf
implement `LoadQuery` for `InsertStatement` with `BatchInsert` for `S…
bwqr May 29, 2025
585e5ab
Merge branch 'master' of https://github.com/bwqr/diesel into impl-loa…
bwqr May 29, 2025
d92b6c6
bump minimal supported Rust version to 1.84.0
bwqr May 29, 2025
bc80aa7
implement LoadQuery for SqliteBatchInsertWrapper with on conflict clause
bwqr May 29, 2025
5834854
implement ExecuteDsl on batch insert statements using on conflict cla…
bwqr May 29, 2025
4dc6d23
fix clippy warnings
bwqr May 29, 2025
8436523
remove ExecuteDsl implementation for on conflict clause.
bwqr May 29, 2025
446af6a
update sqlite/all_about_inserts examples to reflect latest `LoadQuery…
bwqr May 29, 2025
8744dd0
early bubble up any errors other than deserialization in transaction …
bwqr May 30, 2025
9050d32
add tests related for sqlite related to batch insert with returning c…
bwqr May 30, 2025
b3def36
remove unnecessary trait bounds, reorganize impl blocks for consistency
bwqr May 30, 2025
e181641
revert making LoadIter pub(crate) since it is not required anymore
bwqr May 30, 2025
2aabce0
run rustfmt on diesel_tests/tests/insert.rs
bwqr May 30, 2025
0bc4518
Add changelog entry
weiznich Jun 1, 2025
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
14 changes: 7 additions & 7 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -472,12 +472,12 @@ jobs:
cargo +stable -Z build-std test --manifest-path diesel/Cargo.toml --no-default-features --features "mysql extras __with_asan_tests" --target x86_64-unknown-linux-gnu

minimal_rust_version:
name: Check Minimal supported rust version (1.82.0)
name: Check Minimal supported rust version (1.84.0)
runs-on: ubuntu-latest
needs: [rustfmt_and_clippy]
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@1.82.0
- uses: dtolnay/rust-toolchain@1.84.0
- uses: dtolnay/rust-toolchain@nightly
- uses: taiki-e/install-action@cargo-hack
- uses: taiki-e/install-action@cargo-minimal-versions
Expand All @@ -490,12 +490,12 @@ jobs:
sudo apt-get update
sudo apt-get -y install libsqlite3-dev libpq-dev libmysqlclient-dev
- name: Check diesel_derives
run: cargo +1.82.0 minimal-versions check -p diesel_derives --features "postgres sqlite mysql 32-column-tables 64-column-tables 128-column-tables without-deprecated with-deprecated r2d2"
run: cargo +1.84.0 minimal-versions check -p diesel_derives --features "postgres sqlite mysql 32-column-tables 64-column-tables 128-column-tables without-deprecated with-deprecated r2d2"
- name: Check diesel
run: cargo +1.82.0 minimal-versions check -p diesel --features "postgres mysql sqlite extras"
run: cargo +1.84.0 minimal-versions check -p diesel --features "postgres mysql sqlite extras"
- name: Check diesel_dynamic_schema
run: cargo +1.82.0 minimal-versions check -p diesel-dynamic-schema --all-features
run: cargo +1.84.0 minimal-versions check -p diesel-dynamic-schema --all-features
- name: Check diesel_migrations
run: cargo +1.82.0 minimal-versions check -p diesel_migrations --all-features
run: cargo +1.84.0 minimal-versions check -p diesel_migrations --all-features
- name: Check diesel_cli
run: cargo +1.82.0 minimal-versions check -p diesel_cli --features "default sqlite-bundled"
run: cargo +1.84.0 minimal-versions check -p diesel_cli --features "default sqlite-bundled"
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ in a way that makes the pools suitable for use in parallel tests.
* Add support for the `CAST` operator
* Support `[print_schema] allow_tables_to_appear_in_same_query_config = "none"` to generate no `allow_tables_to_appear_in_same_query!` calls. (Default: `"all_tables"`.). ([#4333](https://github.com/diesel-rs/diesel/issues/4333))
* Add `[print_schema] pg_domains_as_custom_types` parameter to generate custom types for [PostgreSQL domains](https://www.postgresql.org/docs/current/domains.html) that matches any of the regexes in the given list. (Default: `[]`.) This option allows an application to selectively give special meaning for the serialization/deserialization of these types, avoiding the default behavior of treating the domain as the underlying type. ([#4592](https://github.com/diesel-rs/diesel/discussions/4592))
* Add support for batch insert and upsert statements with returning for SQLite

### Fixed

Expand All @@ -43,7 +44,7 @@ in a way that makes the pools suitable for use in parallel tests.
### Changed

* Use distinct `DIESEL_LOG` logging filter env variable instead of the default `RUST_LOG` one (#4575)
* The minimal supported Rust version is now 1.82.0
* The minimal supported Rust version is now 1.84.0

## [2.2.2] 2024-07-19

Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ members = [
]

[workspace.package]
rust-version = "1.82.0"
rust-version = "1.84.0"
include = ["src/**/*.rs", "tests/**/*.rs", "LICENSE-*", "README.md"]
edition = "2021"

Expand Down
6 changes: 1 addition & 5 deletions diesel/src/pg/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,7 @@ impl TypeOidLookup for NonZeroU32 {
impl<'a> PgValue<'a> {
#[cfg(test)]
pub(crate) fn for_test(raw_value: &'a [u8]) -> Self {
#[allow(unsafe_code)] // that's actual safe
static FAKE_OID: NonZeroU32 = unsafe {
// 42 != 0, so this is actually safe
NonZeroU32::new_unchecked(42)
};
static FAKE_OID: NonZeroU32 = NonZeroU32::new(42).unwrap();
Self {
raw_value,
type_oid_lookup: &FAKE_OID,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use crate::prelude::*;
use crate::query_builder::upsert::on_conflict_clause::OnConflictValues;
use crate::query_builder::{AstPass, QueryId, ValuesClause};
use crate::query_builder::{DebugQuery, QueryFragment};
use crate::query_dsl::methods::ExecuteDsl;
use crate::query_dsl::{methods::ExecuteDsl, LoadQuery};
use crate::sqlite::Sqlite;
use std::fmt::{self, Debug, Display};

Expand Down Expand Up @@ -273,6 +273,206 @@ where
}
}

impl<'query, V, T, QId, Op, O, U, B, const STATIC_QUERY_ID: bool>
LoadQuery<'query, SqliteConnection, U, B>
for InsertStatement<T, BatchInsert<Vec<ValuesClause<V, T>>, T, QId, STATIC_QUERY_ID>, Op>
where
T: QuerySource,
V: ContainsDefaultableValue<Out = O>,
O: Default,
(O, Self): LoadQuery<'query, SqliteConnection, U, B>,
{
type RowIter<'conn> = <(O, Self) as LoadQuery<'query, SqliteConnection, U, B>>::RowIter<'conn>;

fn internal_load(self, conn: &mut SqliteConnection) -> QueryResult<Self::RowIter<'_>> {
<(O, Self) as LoadQuery<'query, SqliteConnection, U, B>>::internal_load(
(O::default(), self),
conn,
)
}
}

impl<'query, V, T, QId, Op, O, U, B, Target, ConflictOpt, const STATIC_QUERY_ID: bool>
LoadQuery<'query, SqliteConnection, U, B>
for InsertStatement<
T,
OnConflictValues<
BatchInsert<Vec<ValuesClause<V, T>>, T, QId, STATIC_QUERY_ID>,
Target,
ConflictOpt,
>,
Op,
>
where
T: QuerySource,
V: ContainsDefaultableValue<Out = O>,
O: Default,
(O, Self): LoadQuery<'query, SqliteConnection, U, B>,
{
type RowIter<'conn> = <(O, Self) as LoadQuery<'query, SqliteConnection, U, B>>::RowIter<'conn>;

fn internal_load(self, conn: &mut SqliteConnection) -> QueryResult<Self::RowIter<'_>> {
<(O, Self) as LoadQuery<'query, SqliteConnection, U, B>>::internal_load(
(O::default(), self),
conn,
)
}
}

impl<V, T, QId, Op, O, const STATIC_QUERY_ID: bool> RunQueryDsl<SqliteConnection>
for (
O,
InsertStatement<T, BatchInsert<Vec<ValuesClause<V, T>>, T, QId, STATIC_QUERY_ID>, Op>,
)
where
T: QuerySource,
V: ContainsDefaultableValue<Out = O>,
O: Default,
InsertStatement<T, BatchInsert<Vec<ValuesClause<V, T>>, T, QId, STATIC_QUERY_ID>, Op>:
RunQueryDsl<SqliteConnection>,
{
}

impl<V, T, QId, Op, O, Target, ConflictOpt, const STATIC_QUERY_ID: bool>
RunQueryDsl<SqliteConnection>
for (
O,
InsertStatement<
T,
OnConflictValues<
BatchInsert<Vec<ValuesClause<V, T>>, T, QId, STATIC_QUERY_ID>,
Target,
ConflictOpt,
>,
Op,
>,
)
where
T: QuerySource,
V: ContainsDefaultableValue<Out = O>,
O: Default,
InsertStatement<
T,
OnConflictValues<
BatchInsert<Vec<ValuesClause<V, T>>, T, QId, STATIC_QUERY_ID>,
Target,
ConflictOpt,
>,
Op,
>: RunQueryDsl<SqliteConnection>,
{
}

impl<'query, V, T, QId, Op, U, B, const STATIC_QUERY_ID: bool>
LoadQuery<'query, SqliteConnection, U, B>
for (
Yes,
InsertStatement<T, BatchInsert<Vec<ValuesClause<V, T>>, T, QId, STATIC_QUERY_ID>, Op>,
)
where
T: Table + Copy + QueryId + 'static,
Op: Copy + QueryId + QueryFragment<Sqlite>,
InsertStatement<T, ValuesClause<V, T>, Op>: LoadQuery<'query, SqliteConnection, U, B>,
Self: RunQueryDsl<SqliteConnection>,
{
type RowIter<'conn> = std::vec::IntoIter<QueryResult<U>>;

fn internal_load(self, conn: &mut SqliteConnection) -> QueryResult<Self::RowIter<'_>> {
let (Yes, query) = self;

conn.transaction(|conn| {
let mut results = Vec::with_capacity(query.records.values.len());

for record in query.records.values {
let stmt =
InsertStatement::new(query.target, record, query.operator, query.returning);

let result = stmt
.internal_load(conn)?
.next()
.ok_or(crate::result::Error::NotFound)?;

match &result {
Ok(_) | Err(crate::result::Error::DeserializationError(_)) => {
results.push(result)
}
Err(_) => {
result?;
}
};
}

Ok(results.into_iter())
})
}
}

impl<'query, V, T, QId, Op, U, B, Target, ConflictOpt, const STATIC_QUERY_ID: bool>
LoadQuery<'query, SqliteConnection, U, B>
for (
Yes,
InsertStatement<
T,
OnConflictValues<
BatchInsert<Vec<ValuesClause<V, T>>, T, QId, STATIC_QUERY_ID>,
Target,
ConflictOpt,
>,
Op,
>,
)
where
T: Table + Copy + QueryId + 'static,
T::FromClause: Copy,
Op: Copy,
Target: Copy,
ConflictOpt: Copy,
InsertStatement<T, OnConflictValues<ValuesClause<V, T>, Target, ConflictOpt>, Op>:
LoadQuery<'query, SqliteConnection, U, B>,
Self: RunQueryDsl<SqliteConnection>,
{
type RowIter<'conn> = std::vec::IntoIter<QueryResult<U>>;

fn internal_load(self, conn: &mut SqliteConnection) -> QueryResult<Self::RowIter<'_>> {
let (Yes, query) = self;

conn.transaction(|conn| {
let mut results = Vec::with_capacity(query.records.values.values.len());

for record in query.records.values.values {
let stmt = InsertStatement {
operator: query.operator,
target: query.target,
records: OnConflictValues {
values: record,
target: query.records.target,
action: query.records.action,
where_clause: query.records.where_clause,
},
returning: query.returning,
into_clause: query.into_clause,
};

let result = stmt
.internal_load(conn)?
.next()
.ok_or(crate::result::Error::NotFound)?;

match &result {
Ok(_) | Err(crate::result::Error::DeserializationError(_)) => {
results.push(result)
}
Err(_) => {
result?;
}
};
}

Ok(results.into_iter())
})
}
}

#[allow(missing_debug_implementations, missing_copy_implementations)]
#[repr(transparent)]
pub struct SqliteBatchInsertWrapper<V, T, QId, const STATIC_QUERY_ID: bool>(
Expand Down Expand Up @@ -400,6 +600,84 @@ where
}
}

impl<'query, V, T, QId, Op, U, B, const STATIC_QUERY_ID: bool>
LoadQuery<'query, SqliteConnection, U, B>
for (
No,
InsertStatement<T, BatchInsert<V, T, QId, STATIC_QUERY_ID>, Op>,
)
where
T: Table + QueryId + 'static,
InsertStatement<T, SqliteBatchInsertWrapper<V, T, QId, STATIC_QUERY_ID>, Op>:
LoadQuery<'query, SqliteConnection, U, B>,
Self: RunQueryDsl<SqliteConnection>,
{
type RowIter<'conn> = <InsertStatement<
T,
SqliteBatchInsertWrapper<V, T, QId, STATIC_QUERY_ID>,
Op,
> as LoadQuery<'query, SqliteConnection, U, B>>::RowIter<'conn>;

fn internal_load(self, conn: &mut SqliteConnection) -> QueryResult<Self::RowIter<'_>> {
let (No, query) = self;

let query = InsertStatement {
records: SqliteBatchInsertWrapper(query.records),
operator: query.operator,
target: query.target,
returning: query.returning,
into_clause: query.into_clause,
};

query.internal_load(conn)
}
}

impl<'query, V, T, QId, Op, U, B, Target, ConflictOpt, const STATIC_QUERY_ID: bool>
LoadQuery<'query, SqliteConnection, U, B>
for (
No,
InsertStatement<
T,
OnConflictValues<BatchInsert<V, T, QId, STATIC_QUERY_ID>, Target, ConflictOpt>,
Op,
>,
)
where
T: Table + QueryId + 'static,
InsertStatement<
T,
OnConflictValues<SqliteBatchInsertWrapper<V, T, QId, STATIC_QUERY_ID>, Target, ConflictOpt>,
Op,
>: LoadQuery<'query, SqliteConnection, U, B>,
Self: RunQueryDsl<SqliteConnection>,
{
type RowIter<'conn> = <InsertStatement<
T,
OnConflictValues<SqliteBatchInsertWrapper<V, T, QId, STATIC_QUERY_ID>, Target, ConflictOpt>,
Op,
> as LoadQuery<'query, SqliteConnection, U, B>>::RowIter<'conn>;

fn internal_load(self, conn: &mut SqliteConnection) -> QueryResult<Self::RowIter<'_>> {
let (No, query) = self;

let query = InsertStatement {
operator: query.operator,
target: query.target,
records: OnConflictValues {
values: SqliteBatchInsertWrapper(query.records.values),
target: query.records.target,
action: query.records.action,
where_clause: query.records.where_clause,
},
returning: query.returning,
into_clause: query.into_clause,
};

query.internal_load(conn)
}
}

macro_rules! tuple_impls {
($(
$Tuple:tt {
Expand Down
6 changes: 3 additions & 3 deletions diesel_dynamic_schema/tests/dynamic_values.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ impl FromSql<Any, diesel::pg::Pg> for MyDynamicValue {
use diesel::pg::Pg;
use std::num::NonZeroU32;

const VARCHAR_OID: NonZeroU32 = unsafe { NonZeroU32::new_unchecked(1043) };
const TEXT_OID: NonZeroU32 = unsafe { NonZeroU32::new_unchecked(25) };
const INTEGER_OID: NonZeroU32 = unsafe { NonZeroU32::new_unchecked(23) };
const VARCHAR_OID: NonZeroU32 = NonZeroU32::new(1043).unwrap();
const TEXT_OID: NonZeroU32 = NonZeroU32::new(25).unwrap();
const INTEGER_OID: NonZeroU32 = NonZeroU32::new(23).unwrap();

match value.get_oid() {
VARCHAR_OID | TEXT_OID => {
Expand Down
Loading
Loading