Skip to content

Commit

Permalink
Unify (Update|Delete)Statement with \1Query
Browse files Browse the repository at this point in the history
Reducing the number of types which have to implement various traits is
always a win. We can unify these the same way we handled
`InsertStatement` and `InsertQuery`.
  • Loading branch information
sgrif committed Mar 6, 2017
1 parent 86b0664 commit 3c6c764
Show file tree
Hide file tree
Showing 3 changed files with 80 additions and 127 deletions.
115 changes: 47 additions & 68 deletions diesel/src/query_builder/delete_statement.rs
Original file line number Diff line number Diff line change
@@ -1,63 +1,80 @@
use backend::{Backend, SupportsReturningClause};
use expression::{Expression, SelectableExpression, NonAggregate};
use backend::Backend;
use expression::SelectableExpression;
use query_builder::*;
use query_builder::returning_clause::*;
use query_source::Table;
use result::QueryResult;

#[derive(Debug)]
pub struct DeleteStatement<T, U>(UpdateTarget<T, U>);
pub struct DeleteStatement<T, U, Ret = NoReturningClause> {
table: T,
where_clause: U,
returning: Ret,
}

impl<T, U> DeleteStatement<T, U> {
impl<T, U> DeleteStatement<T, U, NoReturningClause> {
#[doc(hidden)]
pub fn new(t: UpdateTarget<T, U>) -> Self {
DeleteStatement(t)
pub fn new(table: T, where_clause: U) -> Self {
DeleteStatement {
table: table,
where_clause: where_clause,
returning: NoReturningClause,
}
}
}

impl<T, U, DB> QueryFragment<DB> for DeleteStatement<T, U> where
impl<T, U, Ret, DB> QueryFragment<DB> for DeleteStatement<T, U, Ret> where
DB: Backend,
T: Table,
T::FromClause: QueryFragment<DB>,
U: QueryFragment<DB>,
Ret: QueryFragment<DB>,
{
fn to_sql(&self, out: &mut DB::QueryBuilder) -> BuildQueryResult {
out.push_sql("DELETE FROM ");
try!(self.0.table.from_clause().to_sql(out));
try!(self.0.where_clause.to_sql(out));
try!(self.table.from_clause().to_sql(out));
try!(self.where_clause.to_sql(out));
try!(self.returning.to_sql(out));
Ok(())
}

fn collect_binds(&self, out: &mut DB::BindCollector) -> QueryResult<()> {
try!(self.0.table.from_clause().collect_binds(out));
try!(self.0.where_clause.collect_binds(out));
try!(self.table.from_clause().collect_binds(out));
try!(self.where_clause.collect_binds(out));
try!(self.returning.collect_binds(out));
Ok(())
}

fn is_safe_to_cache_prepared(&self) -> bool {
self.0.table.from_clause().is_safe_to_cache_prepared() &&
self.0.where_clause.is_safe_to_cache_prepared()
self.table.from_clause().is_safe_to_cache_prepared() &&
self.where_clause.is_safe_to_cache_prepared() &&
self.returning.is_safe_to_cache_prepared()
}
}

impl_query_id!(noop: DeleteStatement<T, U>);
impl_query_id!(DeleteStatement<T, U, Ret>);

impl<T, U> AsQuery for DeleteStatement<T, U> where
impl<T, U> AsQuery for DeleteStatement<T, U, NoReturningClause> where
T: Table,
<T as Table>::AllColumns: Expression + SelectableExpression<T>,
DeleteQuery<<T as Table>::AllColumns, DeleteStatement<T, U>>: Query,
T::AllColumns: SelectableExpression<T>,
DeleteStatement<T, U, ReturningClause<T::AllColumns>>: Query,
{
type SqlType = <Self::Query as Query>::SqlType;
type Query = DeleteQuery<<T as Table>::AllColumns, DeleteStatement<T, U>>;
type Query = DeleteStatement<T, U, ReturningClause<T::AllColumns>>;

fn as_query(self) -> Self::Query {
DeleteQuery {
returning: T::all_columns(),
statement: self,
}
self.returning(T::all_columns())
}
}

impl<T, U> DeleteStatement<T, U> {
impl<T, U, Ret> Query for DeleteStatement<T, U, ReturningClause<Ret>> where
T: Table,
Ret: SelectableExpression<T>,
{
type SqlType = Ret::SqlType;
}

impl<T, U> DeleteStatement<T, U, NoReturningClause> {
/// Specify what expression is returned after execution of the `delete`.
///
/// # Examples
Expand Down Expand Up @@ -87,52 +104,14 @@ impl<T, U> DeleteStatement<T, U> {
/// # #[cfg(not(feature = "postgres"))]
/// # fn main() {}
/// ```
pub fn returning<E>(self, returns: E) -> DeleteQuery<E, Self> where
E: Expression + SelectableExpression<T>,
DeleteQuery<E, Self>: Query,
pub fn returning<E>(self, returns: E) -> DeleteStatement<T, U, ReturningClause<E>> where
E: SelectableExpression<T>,
DeleteStatement<T, U, ReturningClause<E>>: Query,
{
DeleteQuery {
returning: returns,
statement: self,
DeleteStatement {
table: self.table,
where_clause: self.where_clause,
returning: ReturningClause(returns),
}
}
}

#[doc(hidden)]
#[derive(Debug, Copy, Clone)]
pub struct DeleteQuery<T, U> {
returning: T,
statement: U,
}

impl<T, U> Query for DeleteQuery<T, U> where
T: Expression + NonAggregate,
{
type SqlType = T::SqlType;
}

impl<T, U, DB> QueryFragment<DB> for DeleteQuery<T, U> where
DB: Backend + SupportsReturningClause,
T: QueryFragment<DB>,
U: QueryFragment<DB>,
{
fn to_sql(&self, out: &mut DB::QueryBuilder) -> BuildQueryResult {
try!(self.statement.to_sql(out));
out.push_sql(" RETURNING ");
try!(self.returning.to_sql(out));
Ok(())
}

fn collect_binds(&self, out: &mut DB::BindCollector) -> QueryResult<()> {
try!(self.statement.collect_binds(out));
try!(self.returning.collect_binds(out));
Ok(())
}

fn is_safe_to_cache_prepared(&self) -> bool {
self.statement.is_safe_to_cache_prepared() &&
self.returning.is_safe_to_cache_prepared()
}
}

impl_query_id!(noop: DeleteQuery<T, U>);
3 changes: 2 additions & 1 deletion diesel/src/query_builder/functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,8 @@ pub fn update<T: IntoUpdateTarget>(source: T) -> IncompleteUpdateStatement<T::Ta
/// # }
/// ```
pub fn delete<T: IntoUpdateTarget>(source: T) -> DeleteStatement<T::Table, T::WhereClause> {
DeleteStatement::new(source.into_update_target())
let target = source.into_update_target();
DeleteStatement::new(target.table, target.where_clause)
}

/// Creates an insert statement. Will add the given data to a table. This
Expand Down
89 changes: 31 additions & 58 deletions diesel/src/query_builder/update_statement/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ pub mod target;
pub use self::changeset::{Changeset, AsChangeset};
pub use self::target::{UpdateTarget, IntoUpdateTarget};

use backend::{Backend, SupportsReturningClause};
use backend::Backend;
use expression::{Expression, SelectableExpression, NonAggregate};
use query_builder::{Query, AsQuery, QueryFragment, QueryBuilder, BuildQueryResult};
use query_builder::returning_clause::*;
use query_source::Table;
use result::QueryResult;

Expand All @@ -23,32 +24,35 @@ impl<T, U> IncompleteUpdateStatement<T, U> {
}

impl<T, U> IncompleteUpdateStatement<T, U> {
pub fn set<V>(self, values: V) -> UpdateStatement<T, U, V::Changeset> where
pub fn set<V>(self, values: V) -> UpdateStatement<T, U, V::Changeset, NoReturningClause> where
T: Table,
V: changeset::AsChangeset<Target=T>,
UpdateStatement<T, U, V::Changeset>: AsQuery,
UpdateStatement<T, U, V::Changeset, NoReturningClause>: AsQuery,
{
UpdateStatement {
table: self.0.table,
where_clause: self.0.where_clause,
values: values.as_changeset(),
returning: NoReturningClause,
}
}
}

#[derive(Debug, Copy, Clone)]
pub struct UpdateStatement<T, U, V> {
pub struct UpdateStatement<T, U, V, Ret = NoReturningClause> {
table: T,
where_clause: U,
values: V,
returning: Ret,
}

impl<T, U, V, DB> QueryFragment<DB> for UpdateStatement<T, U, V> where
impl<T, U, V, Ret, DB> QueryFragment<DB> for UpdateStatement<T, U, V, Ret> where
DB: Backend,
T: Table,
T::FromClause: QueryFragment<DB>,
U: QueryFragment<DB>,
V: changeset::Changeset<DB>,
Ret: QueryFragment<DB>,
{
fn to_sql(&self, out: &mut DB::QueryBuilder) -> BuildQueryResult {
if self.values.is_noop() {
Expand All @@ -61,13 +65,15 @@ impl<T, U, V, DB> QueryFragment<DB> for UpdateStatement<T, U, V> where
out.push_sql(" SET ");
try!(self.values.to_sql(out));
try!(self.where_clause.to_sql(out));
try!(self.returning.to_sql(out));
Ok(())
}

fn collect_binds(&self, out: &mut DB::BindCollector) -> QueryResult<()> {
try!(self.table.from_clause().collect_binds(out));
try!(self.values.collect_binds(out));
try!(self.where_clause.collect_binds(out));
try!(self.returning.collect_binds(out));
Ok(())
}

Expand All @@ -76,24 +82,28 @@ impl<T, U, V, DB> QueryFragment<DB> for UpdateStatement<T, U, V> where
}
}

impl_query_id!(noop: UpdateStatement<T, U, V>);
impl_query_id!(noop: UpdateStatement<T, U, V, Ret>);

impl<T, U, V> AsQuery for UpdateStatement<T, U, V> where
impl<T, U, V> AsQuery for UpdateStatement<T, U, V, NoReturningClause> where
T: Table,
UpdateQuery<T::AllColumns, UpdateStatement<T, U, V>>: Query,
UpdateStatement<T, U, V, ReturningClause<T::AllColumns>>: Query,
{
type SqlType = <Self::Query as Query>::SqlType;
type Query = UpdateQuery<T::AllColumns, Self>;
type Query = UpdateStatement<T, U, V, ReturningClause<T::AllColumns>>;

fn as_query(self) -> Self::Query {
UpdateQuery {
returning: T::all_columns(),
statement: self,
}
self.returning(T::all_columns())
}
}

impl<T, U, V> UpdateStatement<T, U, V> {
impl<T, U, V, Ret> Query for UpdateStatement<T, U, V, ReturningClause<Ret>> where
T: Table,
Ret: Expression + SelectableExpression<T> + NonAggregate,
{
type SqlType = Ret::SqlType;
}

impl<T, U, V> UpdateStatement<T, U, V, NoReturningClause> {
/// Specify what expression is returned after execution of the `update`.
/// # Examples
///
Expand Down Expand Up @@ -123,52 +133,15 @@ impl<T, U, V> UpdateStatement<T, U, V> {
/// # #[cfg(not(feature = "postgres"))]
/// # fn main() {}
/// ```
pub fn returning<E>(self, returns: E) -> UpdateQuery<E, Self> where
pub fn returning<E>(self, returns: E) -> UpdateStatement<T, U, V, ReturningClause<E>> where
T: Table,
UpdateQuery<E, Self>: Query,
UpdateStatement<T, U, V, ReturningClause<E>>: Query,
{
UpdateQuery {
returning: returns,
statement: self,
UpdateStatement {
table: self.table,
where_clause: self.where_clause,
values: self.values,
returning: ReturningClause(returns),
}
}
}

#[doc(hidden)]
#[derive(Debug, Copy, Clone)]
pub struct UpdateQuery<T, U> {
returning: T,
statement: U,
}

impl<Ret, T, U, V> Query for UpdateQuery<Ret, UpdateStatement<T, U, V>> where
T: Table,
Ret: Expression + SelectableExpression<T> + NonAggregate,
{
type SqlType = Ret::SqlType;
}

impl<T, U, DB> QueryFragment<DB> for UpdateQuery<T, U> where
DB: Backend + SupportsReturningClause,
T: QueryFragment<DB>,
U: QueryFragment<DB>,
{
fn to_sql(&self, out: &mut DB::QueryBuilder) -> BuildQueryResult {
try!(self.statement.to_sql(out));
out.push_sql(" RETURNING ");
try!(self.returning.to_sql(out));
Ok(())
}

fn collect_binds(&self, out: &mut DB::BindCollector) -> QueryResult<()> {
try!(self.statement.collect_binds(out));
try!(self.returning.collect_binds(out));
Ok(())
}

fn is_safe_to_cache_prepared(&self) -> bool {
false
}
}

impl_query_id!(noop: UpdateQuery<T, U>);

0 comments on commit 3c6c764

Please sign in to comment.