Skip to content

Commit a7b424c

Browse files
committed
Unify InsertStatement and InsertQuery into a single struct
I'm working on adding support for batch insert on SQLite, and fixing the insert empty slice bug (diesel-rs#384). My approach is to override `ExecuteDsl` and `LoadDsl` at the point where `InsertStatement` currently lives. I expect that we'll have `into` perform some polymorphic dispatch to get back either a `SingleInsertStatement` or `BatchInsertStatement`. If we also have `InsertQuery` on top of that, the amount of redundant structs and impls will get quite large. This change should make that refactoring much easier. I also took this opportunity to make sure our compile tests were up to snuff around this, and made sure that the tests failed in the expected fashion when I introduced some mistakes as part of this refactor.
1 parent 9d90d6d commit a7b424c

6 files changed

Lines changed: 82 additions & 58 deletions

File tree

diesel/src/query_builder/clause_macro.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
macro_rules! simple_clause {
22
($no_clause:ident, $clause:ident, $sql:expr) => {
3+
simple_clause!($no_clause, $clause, $sql, backend_bounds = );
4+
};
5+
6+
($no_clause:ident, $clause:ident, $sql:expr, backend_bounds = $($backend_bounds:ident),*) => {
37
use backend::Backend;
48
use result::QueryResult;
59
use super::{QueryFragment, QueryBuilder, BuildQueryResult};
@@ -27,7 +31,7 @@ macro_rules! simple_clause {
2731
pub struct $clause<Expr>(pub Expr);
2832

2933
impl<Expr, DB> QueryFragment<DB> for $clause<Expr> where
30-
DB: Backend,
34+
DB: Backend $(+ $backend_bounds)*,
3135
Expr: QueryFragment<DB>,
3236
{
3337
fn to_sql(&self, out: &mut DB::QueryBuilder) -> BuildQueryResult {

diesel/src/query_builder/insert_statement.rs

Lines changed: 31 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
use backend::{Backend, SupportsReturningClause};
1+
use backend::Backend;
22
use expression::{Expression, SelectableExpression, NonAggregate};
33
use persistable::{Insertable, InsertValues};
44
use query_builder::*;
55
use query_source::Table;
66
use result::QueryResult;
7+
use super::returning_clause::*;
78

89
/// The structure returned by [`insert`](fn.insert.html). The only thing that can be done with it
910
/// is call `into`.
@@ -30,23 +31,26 @@ impl<T, Op> IncompleteInsertStatement<T, Op> {
3031
operator: self.operator,
3132
target: target,
3233
records: self.records,
34+
returning: NoReturningClause,
3335
}
3436
}
3537
}
3638

3739
#[derive(Debug, Copy, Clone)]
38-
pub struct InsertStatement<T, U, Op> {
40+
pub struct InsertStatement<T, U, Op, Ret=NoReturningClause> {
3941
operator: Op,
4042
target: T,
4143
records: U,
44+
returning: Ret,
4245
}
4346

44-
impl<T, U, Op, DB> QueryFragment<DB> for InsertStatement<T, U, Op> where
47+
impl<T, U, Op, Ret, DB> QueryFragment<DB> for InsertStatement<T, U, Op, Ret> where
4548
DB: Backend,
4649
T: Table,
4750
T::FromClause: QueryFragment<DB>,
4851
U: Insertable<T, DB> + Copy,
4952
Op: QueryFragment<DB>,
53+
Ret: QueryFragment<DB>,
5054
{
5155
fn to_sql(&self, out: &mut DB::QueryBuilder) -> BuildQueryResult {
5256
let values = self.records.values();
@@ -57,6 +61,7 @@ impl<T, U, Op, DB> QueryFragment<DB> for InsertStatement<T, U, Op> where
5761
try!(values.column_names(out));
5862
out.push_sql(") VALUES ");
5963
try!(values.values_clause(out));
64+
try!(self.returning.to_sql(out));
6065
Ok(())
6166
}
6267

@@ -65,6 +70,7 @@ impl<T, U, Op, DB> QueryFragment<DB> for InsertStatement<T, U, Op> where
6570
try!(self.operator.collect_binds(out));
6671
try!(self.target.from_clause().collect_binds(out));
6772
try!(values.values_bind_params(out));
73+
try!(self.returning.collect_binds(out));
6874
Ok(())
6975
}
7076

@@ -73,25 +79,30 @@ impl<T, U, Op, DB> QueryFragment<DB> for InsertStatement<T, U, Op> where
7379
}
7480
}
7581

76-
impl_query_id!(noop: InsertStatement<T, U, Op>);
82+
impl_query_id!(noop: InsertStatement<T, U, Op, Ret>);
7783

78-
impl<T, U, Op> AsQuery for InsertStatement<T, U, Op> where
84+
impl<T, U, Op> AsQuery for InsertStatement<T, U, Op, NoReturningClause> where
7985
T: Table,
80-
InsertQuery<T::AllColumns, InsertStatement<T, U, Op>>: Query,
86+
InsertStatement<T, U, Op, ReturningClause<T::AllColumns>>: Query,
8187
{
8288
type SqlType = <Self::Query as Query>::SqlType;
83-
type Query = InsertQuery<T::AllColumns, Self>;
89+
type Query = InsertStatement<T, U, Op, ReturningClause<T::AllColumns>>;
8490

8591
fn as_query(self) -> Self::Query {
86-
InsertQuery {
87-
returning: T::all_columns(),
88-
statement: self,
89-
}
92+
self.returning(T::all_columns())
9093
}
9194
}
9295

93-
impl<T, U, Op> InsertStatement<T, U, Op> {
96+
impl<T, U, Op, Ret> Query for InsertStatement<T, U, Op, ReturningClause<Ret>> where
97+
Ret: Expression + SelectableExpression<T> + NonAggregate,
98+
{
99+
type SqlType = Ret::SqlType;
100+
}
101+
102+
impl<T, U, Op> InsertStatement<T, U, Op, NoReturningClause> {
94103
/// Specify what expression is returned after execution of the `insert`.
104+
/// This method can only be called once.
105+
///
95106
/// # Examples
96107
///
97108
/// ### Inserting a record:
@@ -124,55 +135,19 @@ impl<T, U, Op> InsertStatement<T, U, Op> {
124135
/// # #[cfg(not(feature = "postgres"))]
125136
/// # fn main() {}
126137
/// ```
127-
pub fn returning<E>(self, returns: E) -> InsertQuery<E, Self> where
128-
E: Expression + SelectableExpression<T>,
129-
InsertQuery<E, Self>: Query,
138+
pub fn returning<E>(self, returns: E)
139+
-> InsertStatement<T, U, Op, ReturningClause<E>> where
140+
InsertStatement<T, U, Op, ReturningClause<E>>: Query,
130141
{
131-
InsertQuery {
132-
returning: returns,
133-
statement: self,
142+
InsertStatement {
143+
operator: self.operator,
144+
target: self.target,
145+
records: self.records,
146+
returning: ReturningClause(returns),
134147
}
135148
}
136149
}
137150

138-
#[doc(hidden)]
139-
#[derive(Debug, Copy, Clone)]
140-
pub struct InsertQuery<T, U> {
141-
returning: T,
142-
statement: U,
143-
}
144-
145-
impl<T, U> Query for InsertQuery<T, U> where
146-
T: Expression + NonAggregate,
147-
{
148-
type SqlType = T::SqlType;
149-
}
150-
151-
impl<T, U, DB> QueryFragment<DB> for InsertQuery<T, U> where
152-
DB: Backend + SupportsReturningClause,
153-
T: QueryFragment<DB>,
154-
U: QueryFragment<DB>,
155-
{
156-
fn to_sql(&self, out: &mut DB::QueryBuilder) -> BuildQueryResult {
157-
try!(self.statement.to_sql(out));
158-
out.push_sql(" RETURNING ");
159-
try!(self.returning.to_sql(out));
160-
Ok(())
161-
}
162-
163-
fn collect_binds(&self, out: &mut DB::BindCollector) -> QueryResult<()> {
164-
try!(self.statement.collect_binds(out));
165-
try!(self.returning.collect_binds(out));
166-
Ok(())
167-
}
168-
169-
fn is_safe_to_cache_prepared(&self) -> bool {
170-
false
171-
}
172-
}
173-
174-
impl_query_id!(noop: InsertQuery<T, U>);
175-
176151
#[derive(Debug, Copy, Clone)]
177152
pub struct Insert;
178153

diesel/src/query_builder/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ mod group_by_clause;
1717
mod limit_clause;
1818
mod offset_clause;
1919
mod order_clause;
20+
mod returning_clause;
2021
mod select_statement;
2122
pub mod where_clause;
2223
pub mod insert_statement;
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
use backend::SupportsReturningClause;
2+
3+
simple_clause!(NoReturningClause, ReturningClause, " RETURNING ", backend_bounds = SupportsReturningClause);

diesel_compile_tests/tests/compile-fail/insert_statement_does_not_support_returning_methods_on_sqlite.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,6 @@ fn main() {
5151
insert(&NewUser("Hello".into()))
5252
.into(users::table)
5353
.returning(users::name)
54-
.get_result(&connection);
54+
.get_result::<String>(&connection);
5555
//~^ ERROR: SupportsReturningClause
5656
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
#[macro_use] extern crate diesel;
2+
3+
use diesel::*;
4+
use diesel::pg::PgConnection;
5+
6+
table! {
7+
users {
8+
id -> Integer,
9+
name -> VarChar,
10+
}
11+
}
12+
13+
pub struct NewUser(String);
14+
15+
Insertable! {
16+
(users)
17+
pub struct NewUser(#[column_name(name)] String,);
18+
}
19+
20+
fn main() {
21+
use self::users::dsl::*;
22+
23+
let connection = PgConnection::establish("").unwrap();
24+
25+
let query = delete(users.filter(name.eq("Bill")))
26+
.returning(id);
27+
query.returning(name);
28+
//~^ ERROR: no method named `returning`
29+
30+
let query = insert(&NewUser("Hello".into()))
31+
.into(users)
32+
.returning(id);
33+
query.returning(name);
34+
//~^ ERROR: no method named `returning`
35+
36+
let query = update(users)
37+
.set(name.eq("Bill"))
38+
.returning(id);
39+
query.returning(name);
40+
//~^ ERROR: no method named `returning`
41+
}

0 commit comments

Comments
 (0)