pub(crate) mod batch_update;
pub(crate) mod changeset;
pub(super) mod target;
use private::AllowFilterForUpdate;
use crate::QuerySource;
use crate::backend::DieselReserveSpecialization;
use crate::dsl::{Filter, IntoBoxed, IntoBoxedClone};
use crate::expression::{
AppearsOnTable, Expression, MixedAggregates, SelectableExpression, ValidGrouping, is_aggregate,
};
use crate::query_builder::returning::{
NoReturningClause, ReturningClause, ReturningQuerySource, UpdateStmt,
};
use crate::query_builder::where_clause::*;
use crate::query_builder::*;
use crate::query_dsl::RunQueryDslSupport;
use crate::query_dsl::methods::{BoxedCloneDsl, BoxedDsl, FilterDsl};
use crate::query_source::Table;
use crate::result::EmptyChangeset;
use crate::result::Error::QueryBuilderError;
pub(crate) use self::private::SetAutoTypeHelper;
impl<T: QuerySource, U> UpdateStatement<T, U, SetNotCalled> {
pub(crate) fn new(target: UpdateTarget<T, U>) -> Self {
UpdateStatement {
from_clause: target.table.from_clause(),
where_clause: target.where_clause,
set_clause: SetClause::Immediate,
values: SetNotCalled,
returning: NoReturningClause,
}
}
pub fn set<V>(self, values: V) -> crate::dsl::Set<Self, V>
where
T: Table,
V: changeset::AsChangeset<Target = T>,
UpdateStatement<T, U, V::Changeset>: AsQuery,
{
UpdateStatement {
from_clause: self.from_clause,
where_clause: self.where_clause,
set_clause: <V as AsChangeset>::SET_CLAUSE,
values: values.as_changeset(),
returning: self.returning,
}
}
}
#[derive(Clone, Debug)]
#[must_use = "Queries are only executed when calling `load`, `get_result` or similar."]
pub struct UpdateStatement<T: QuerySource, U, V = SetNotCalled, Ret = NoReturningClause> {
from_clause: T::FromClause,
where_clause: U,
set_clause: SetClause,
values: V,
returning: Ret,
}
pub type BoxedUpdateStatement<'a, DB, T, V = SetNotCalled, Ret = NoReturningClause> =
UpdateStatement<T, BoxedWhereClause<'a, DB>, V, Ret>;
pub type BoxedCloneUpdateStatement<'a, DB, T, V = SetNotCalled, Ret = NoReturningClause> =
UpdateStatement<T, BoxedCloneWhereClause<'a, DB>, V, Ret>;
impl<T: QuerySource, U, V, Ret> UpdateStatement<T, U, V, Ret> {
pub fn filter<Predicate>(self, predicate: Predicate) -> Filter<Self, Predicate>
where
Self: FilterDsl<Predicate>,
{
FilterDsl::filter(self, predicate)
}
pub fn into_boxed<'a, DB>(self) -> IntoBoxed<'a, Self, DB>
where
DB: Backend,
Self: BoxedDsl<'a, DB>,
{
BoxedDsl::internal_into_boxed(self)
}
pub fn into_boxed_clone<'a, DB>(self) -> IntoBoxedClone<'a, Self, DB>
where
DB: Backend,
Self: BoxedCloneDsl<'a, DB>,
{
BoxedCloneDsl::internal_into_boxed_clone(self)
}
}
impl<T, U, V, Ret, Predicate> FilterDsl<Predicate> for UpdateStatement<T, U, V, Ret>
where
T: QuerySource,
U: WhereAnd<Predicate>,
Predicate: AppearsOnTable<T>,
{
type Output = UpdateStatement<T, U::Output, V, Ret>;
fn filter(self, predicate: Predicate) -> Self::Output {
UpdateStatement {
from_clause: self.from_clause,
where_clause: self.where_clause.and(predicate),
set_clause: self.set_clause,
values: self.values,
returning: self.returning,
}
}
}
impl<'a, T, U, V, Ret, DB> BoxedDsl<'a, DB> for UpdateStatement<T, U, V, Ret>
where
T: QuerySource,
U: Into<BoxedWhereClause<'a, DB>>,
{
type Output = BoxedUpdateStatement<'a, DB, T, V, Ret>;
fn internal_into_boxed(self) -> Self::Output {
UpdateStatement {
from_clause: self.from_clause,
where_clause: self.where_clause.into(),
set_clause: self.set_clause,
values: self.values,
returning: self.returning,
}
}
}
impl<'a, T, U, V, Ret, DB> BoxedCloneDsl<'a, DB> for UpdateStatement<T, U, V, Ret>
where
T: QuerySource,
U: Into<BoxedCloneWhereClause<'a, DB>>,
{
type Output = BoxedCloneUpdateStatement<'a, DB, T, V, Ret>;
fn internal_into_boxed_clone(self) -> Self::Output {
UpdateStatement {
from_clause: self.from_clause,
where_clause: self.where_clause.into(),
set_clause: self.set_clause,
values: self.values,
returning: self.returning,
}
}
}
impl<T, U, V, Ret, DB> QueryFragment<DB> for UpdateStatement<T, U, V, Ret>
where
DB: Backend + DieselReserveSpecialization,
T: Table,
T::FromClause: QueryFragment<DB>,
U: QueryFragment<DB>,
V: QueryFragment<DB> + AllowFilterForUpdate<U>,
Ret: QueryFragment<DB>,
{
fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> {
if self.values.is_noop(out.backend())? {
return Err(QueryBuilderError(Box::new(EmptyChangeset)));
}
out.unsafe_to_cache_prepared();
out.push_sql("UPDATE ");
self.from_clause.walk_ast(out.reborrow())?;
self.set_clause.walk_ast(out.reborrow())?;
self.values.walk_ast(out.reborrow())?;
self.where_clause.walk_ast(out.reborrow())?;
self.returning.walk_ast(out.reborrow())?;
Ok(())
}
}
impl<T, U, V, Ret> QueryId for UpdateStatement<T, U, V, Ret>
where
T: QuerySource,
{
type QueryId = ();
const HAS_STATIC_QUERY_ID: bool = false;
}
impl<T, U, V> AsQuery for UpdateStatement<T, U, V, NoReturningClause>
where
T: Table,
UpdateStatement<T, U, V, ReturningClause<T::AllColumns>>: Query,
T::AllColumns: SelectableExpression<ReturningQuerySource<UpdateStmt, T>> + ValidGrouping<()>,
<T::AllColumns as ValidGrouping<()>>::IsAggregate:
MixedAggregates<is_aggregate::No, Output = is_aggregate::No>,
{
type SqlType = <Self::Query as Query>::SqlType;
type Query = UpdateStatement<T, U, V, ReturningClause<T::AllColumns>>;
fn as_query(self) -> Self::Query {
self.returning(T::all_columns())
}
}
impl<T, U, V, Ret> Query for UpdateStatement<T, U, V, ReturningClause<Ret>>
where
T: Table,
Ret: SelectableExpression<ReturningQuerySource<UpdateStmt, T>> + ValidGrouping<()>,
Ret::IsAggregate: MixedAggregates<is_aggregate::No, Output = is_aggregate::No>,
{
type SqlType = <Ret as Expression>::SqlType;
}
impl<T: QuerySource, U, V, Ret> RunQueryDslSupport for UpdateStatement<T, U, V, Ret> {}
impl<T: QuerySource, U, V> UpdateStatement<T, U, V, NoReturningClause> {
pub fn returning<E>(self, returns: E) -> UpdateStatement<T, U, V, ReturningClause<E>>
where
T: Table,
UpdateStatement<T, U, V, ReturningClause<E>>: Query,
{
UpdateStatement {
from_clause: self.from_clause,
where_clause: self.where_clause,
set_clause: self.set_clause,
values: self.values,
returning: ReturningClause(returns),
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct SetNotCalled;
pub(crate) mod private {
use crate::backend::Backend;
use crate::query_builder::where_clause::{
BoxedCloneWhereClause, BoxedWhereClause, NoWhereClause, WhereClause,
};
use super::changeset::Assign;
#[allow(unreachable_pub)]
pub trait SetAutoTypeHelper<Changes> {
type Out;
}
impl<T, W, Changes> SetAutoTypeHelper<Changes> for crate::query_builder::UpdateStatement<T, W>
where
T: crate::QuerySource,
Changes: crate::AsChangeset,
{
type Out = crate::query_builder::UpdateStatement<T, W, Changes::Changeset>;
}
#[diagnostic::on_unimplemented(
message = "cannot apply a `WHERE` clause to batch updates",
note = "the information about which rows to update are provided as part of the values"
)]
pub trait AllowFilterForUpdate<P> {}
impl<U> AllowFilterForUpdate<NoWhereClause> for U {}
impl<W, C, B> AllowFilterForUpdate<WhereClause<W>> for Assign<C, B> {}
impl<W, T> AllowFilterForUpdate<WhereClause<W>> for Option<T> where
T: AllowFilterForUpdate<WhereClause<W>>
{
}
impl<'a, DB, C, B> AllowFilterForUpdate<BoxedWhereClause<'a, DB>> for Assign<C, B> where DB: Backend {}
impl<'a, DB, T> AllowFilterForUpdate<BoxedWhereClause<'a, DB>> for Option<T>
where
DB: Backend,
T: AllowFilterForUpdate<BoxedWhereClause<'a, DB>>,
{
}
impl<'a, DB, C, B> AllowFilterForUpdate<BoxedCloneWhereClause<'a, DB>> for Assign<C, B> where
DB: Backend
{
}
impl<'a, DB, T> AllowFilterForUpdate<BoxedCloneWhereClause<'a, DB>> for Option<T>
where
DB: Backend,
T: AllowFilterForUpdate<BoxedCloneWhereClause<'a, DB>>,
{
}
}
#[derive(Clone, Copy, Debug)]
pub enum SetClause {
Immediate,
Delegated,
}
impl<DB> QueryFragment<DB> for SetClause
where
DB: Backend,
{
fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> {
if let SetClause::Immediate = self {
out.push_sql(" SET ");
}
Ok(())
}
}