use std::{fmt::Write, marker::PhantomData};
use super::SqlStatement;
use crate::{
sql::{FieldsConsListItem, ParameterBinder, SelectedValues, SqlBool, SqlExpression, UpdateSet},
util::{TypedBool, TypedConsListNil, TypedFalse, TypedTrue},
Table,
};
pub trait UpdateStatement: Sized {
type OutputFields: FieldsConsListItem;
type UpdateTable: Table;
type HasWhereClause: TypedBool;
type HasReturningClause: TypedBool;
type UpdateSet: UpdateSet;
fn write_update_set<'s, 'a>(
&'s self,
f: &mut String,
parameter_binder: &mut ParameterBinder<'a>,
) -> std::fmt::Result
where
's: 'a;
fn write_where_clause<'s, 'a>(
&'s self,
f: &mut String,
parameter_binder: &mut ParameterBinder<'a>,
) -> std::fmt::Result
where
's: 'a;
fn write_returning_clause<'s, 'a>(
&'s self,
f: &mut String,
parameter_binder: &mut ParameterBinder<'a>,
) -> std::fmt::Result
where
's: 'a;
fn write_sql_string<'s, 'a>(
&'s self,
f: &mut String,
parameter_binder: &mut ParameterBinder<'a>,
) -> std::fmt::Result
where
's: 'a,
{
write!(
f,
"UPDATE {} SET ",
<Self::UpdateTable as Table>::TABLE_NAME
)?;
self.write_update_set(f, parameter_binder)?;
self.write_where_clause(f, parameter_binder)?;
self.write_returning_clause(f, parameter_binder)
}
}
macro_rules! impl_sql_statement_for_update_statement {
{} => {
type OutputFields = <Self as UpdateStatement>::OutputFields;
fn write_sql_string<'s, 'a>(
&'s self,
f: &mut String,
parameter_binder: &mut ParameterBinder<'a>,
) -> std::fmt::Result
where
's: 'a,
{
<Self as UpdateStatement>::write_sql_string(&self, f, parameter_binder)
}
};
}
pub struct EmptyUpdateStatement<T: Table>(PhantomData<T>);
impl<T: Table> EmptyUpdateStatement<T> {
pub fn new() -> Self {
Self(PhantomData)
}
pub fn set<U: UpdateSet<UpdateTable = T>>(
self,
update_set: U,
) -> UpdateStatementWithUpdateSet<U> {
UpdateStatementWithUpdateSet::new(update_set)
}
}
pub struct UpdateStatementWithUpdateSet<U: UpdateSet> {
update_set: U,
}
impl<U: UpdateSet> UpdateStatementWithUpdateSet<U> {
pub fn new(update_set: U) -> Self {
Self { update_set }
}
}
impl<U: UpdateSet> UpdateStatement for UpdateStatementWithUpdateSet<U> {
type HasReturningClause = TypedFalse;
type HasWhereClause = TypedFalse;
type OutputFields = TypedConsListNil;
type UpdateSet = U;
type UpdateTable = U::UpdateTable;
fn write_update_set<'s, 'a>(
&'s self,
f: &mut String,
parameter_binder: &mut ParameterBinder<'a>,
) -> std::fmt::Result
where
's: 'a,
{
self.update_set.write_sql_string(f, parameter_binder)
}
fn write_where_clause<'s, 'a>(
&'s self,
_f: &mut String,
_parameter_binder: &mut ParameterBinder<'a>,
) -> std::fmt::Result
where
's: 'a,
{
Ok(())
}
fn write_returning_clause<'s, 'a>(
&'s self,
_f: &mut String,
_parameter_binder: &mut ParameterBinder<'a>,
) -> std::fmt::Result
where
's: 'a,
{
Ok(())
}
}
impl<U: UpdateSet> SqlStatement for UpdateStatementWithUpdateSet<U> {
impl_sql_statement_for_update_statement! {}
}
pub struct UpdateWithWhereClause<
S: UpdateStatement<HasWhereClause = TypedFalse>,
C: SqlExpression<S::UpdateTable, SqlType = SqlBool>,
> {
statement: S,
condition: C,
}
impl<
S: UpdateStatement<HasWhereClause = TypedFalse>,
C: SqlExpression<S::UpdateTable, SqlType = SqlBool>,
> UpdateStatement for UpdateWithWhereClause<S, C>
{
type HasReturningClause = S::HasReturningClause;
type HasWhereClause = TypedTrue;
type OutputFields = S::OutputFields;
type UpdateSet = S::UpdateSet;
type UpdateTable = S::UpdateTable;
fn write_update_set<'s, 'a>(
&'s self,
f: &mut String,
parameter_binder: &mut ParameterBinder<'a>,
) -> std::fmt::Result
where
's: 'a,
{
self.statement.write_update_set(f, parameter_binder)
}
fn write_where_clause<'s, 'a>(
&'s self,
f: &mut String,
parameter_binder: &mut ParameterBinder<'a>,
) -> std::fmt::Result
where
's: 'a,
{
write!(f, " WHERE ")?;
self.condition.write_sql_string(f, parameter_binder)
}
fn write_returning_clause<'s, 'a>(
&'s self,
f: &mut String,
parameter_binder: &mut ParameterBinder<'a>,
) -> std::fmt::Result
where
's: 'a,
{
self.statement.write_returning_clause(f, parameter_binder)
}
}
impl<
S: UpdateStatement<HasWhereClause = TypedFalse> + 'static,
C: SqlExpression<S::UpdateTable, SqlType = SqlBool> + 'static,
> SqlStatement for UpdateWithWhereClause<S, C>
{
impl_sql_statement_for_update_statement! {}
}
pub trait FilterUpdateStatement: UpdateStatement<HasWhereClause = TypedFalse> {
fn filter<C: SqlExpression<Self::UpdateTable, SqlType = SqlBool>>(
self,
condition: C,
) -> UpdateWithWhereClause<Self, C> {
UpdateWithWhereClause {
statement: self,
condition,
}
}
}
impl<T: UpdateStatement<HasWhereClause = TypedFalse>> FilterUpdateStatement for T {}
pub struct UpdateWithReturningClause<
S: UpdateStatement<HasReturningClause = TypedFalse>,
R: SelectedValues<S::UpdateTable>,
> {
statement: S,
returning: R,
}
impl<S: UpdateStatement<HasReturningClause = TypedFalse>, R: SelectedValues<S::UpdateTable>>
UpdateStatement for UpdateWithReturningClause<S, R>
{
type HasReturningClause = TypedTrue;
type HasWhereClause = S::HasWhereClause;
type OutputFields = R::Fields;
type UpdateSet = S::UpdateSet;
type UpdateTable = S::UpdateTable;
fn write_update_set<'s, 'a>(
&'s self,
f: &mut String,
parameter_binder: &mut ParameterBinder<'a>,
) -> std::fmt::Result
where
's: 'a,
{
self.statement.write_update_set(f, parameter_binder)
}
fn write_where_clause<'s, 'a>(
&'s self,
f: &mut String,
parameter_binder: &mut ParameterBinder<'a>,
) -> std::fmt::Result
where
's: 'a,
{
self.statement.write_where_clause(f, parameter_binder)
}
fn write_returning_clause<'s, 'a>(
&'s self,
f: &mut String,
parameter_binder: &mut ParameterBinder<'a>,
) -> std::fmt::Result
where
's: 'a,
{
write!(f, " RETURNING ")?;
self.returning.write_sql_string(f, parameter_binder)
}
}
impl<S: UpdateStatement<HasReturningClause = TypedFalse>, R: SelectedValues<S::UpdateTable>>
SqlStatement for UpdateWithReturningClause<S, R>
{
impl_sql_statement_for_update_statement! {}
}
pub trait UpdateStatementReturning: UpdateStatement<HasReturningClause = TypedFalse> {
fn returning<R: SelectedValues<Self::UpdateTable>>(
self,
returning: R,
) -> UpdateWithReturningClause<Self, R> {
UpdateWithReturningClause {
statement: self,
returning,
}
}
}
impl<T: UpdateStatement<HasReturningClause = TypedFalse>> UpdateStatementReturning for T {}