use super::SqlStatement;
use crate::{
sql::{
FieldsConsListItem, Insertable, ParameterBinder, SelectedValues, UniqueConstraint,
UpdateSet,
},
util::{TypedBool, TypedConsListNil, TypedFalse, TypedTrue},
Table,
};
pub trait InsertStatement: Sized {
type OutputFields: FieldsConsListItem;
type HasReturningClause: TypedBool;
type HasOnConflictClause: TypedBool;
type Insertable: Insertable;
fn get_insertable(&self) -> &Self::Insertable;
fn write_returning_clause<'s, 'a>(
&'s self,
f: &mut String,
parameter_binder: &mut ParameterBinder<'a>,
) -> std::fmt::Result
where
's: 'a;
fn write_on_conflict_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,
{
use std::fmt::Write;
write!(
f,
"INSERT INTO {}(",
<<Self::Insertable as Insertable>::Table as Table>::TABLE_NAME,
)?;
self.get_insertable().write_value_names(f)?;
write!(f, ") VALUES(")?;
self.get_insertable().write_values(f, parameter_binder)?;
write!(f, ")")?;
self.write_on_conflict_clause(f, parameter_binder)?;
self.write_returning_clause(f, parameter_binder)?;
Ok(())
}
}
macro_rules! impl_sql_statement_for_insert_statement {
{} => {
type OutputFields = <Self as InsertStatement>::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 InsertStatement>::write_sql_string(&self, f, parameter_binder)
}
};
}
pub struct EmptyInsertStatement<I: Insertable>(I);
impl<I: Insertable> EmptyInsertStatement<I> {
pub fn new(insertable: I) -> Self {
Self(insertable)
}
}
impl<I: Insertable> InsertStatement for EmptyInsertStatement<I> {
type HasOnConflictClause = TypedFalse;
type HasReturningClause = TypedFalse;
type Insertable = I;
type OutputFields = TypedConsListNil;
fn get_insertable(&self) -> &Self::Insertable {
&self.0
}
fn write_returning_clause<'s, 'a>(
&'s self,
_f: &mut String,
_parameter_binder: &mut ParameterBinder<'a>,
) -> std::fmt::Result
where
's: 'a,
{
Ok(())
}
fn write_on_conflict_clause<'s, 'a>(
&'s self,
_f: &mut String,
_parameter_binder: &mut ParameterBinder<'a>,
) -> std::fmt::Result
where
's: 'a,
{
Ok(())
}
}
impl<I: Insertable> SqlStatement for EmptyInsertStatement<I> {
impl_sql_statement_for_insert_statement! {}
}
pub struct InsertWithReturningClause<
S: InsertStatement<HasReturningClause = TypedFalse>,
R: SelectedValues<<S::Insertable as Insertable>::Table>,
> {
statement: S,
returning: R,
}
impl<
S: InsertStatement<HasReturningClause = TypedFalse>,
R: SelectedValues<<S::Insertable as Insertable>::Table>,
> InsertStatement for InsertWithReturningClause<S, R>
{
type HasOnConflictClause = S::HasOnConflictClause;
type HasReturningClause = TypedTrue;
type Insertable = S::Insertable;
type OutputFields = R::Fields;
fn get_insertable(&self) -> &Self::Insertable {
self.statement.get_insertable()
}
fn write_returning_clause<'s, 'a>(
&'s self,
f: &mut String,
parameter_binder: &mut ParameterBinder<'a>,
) -> std::fmt::Result
where
's: 'a,
{
use std::fmt::Write;
write!(f, " RETURNING ")?;
self.returning.write_sql_string(f, parameter_binder)
}
fn write_on_conflict_clause<'s, 'a>(
&'s self,
f: &mut String,
parameter_binder: &mut ParameterBinder<'a>,
) -> std::fmt::Result
where
's: 'a,
{
self.statement.write_on_conflict_clause(f, parameter_binder)
}
}
impl<
S: InsertStatement<HasReturningClause = TypedFalse>,
R: SelectedValues<<S::Insertable as Insertable>::Table>,
> SqlStatement for InsertWithReturningClause<S, R>
{
impl_sql_statement_for_insert_statement! {}
}
pub trait InsertStatementReturning: InsertStatement<HasReturningClause = TypedFalse> {
fn returning<R: SelectedValues<<Self::Insertable as Insertable>::Table>>(
self,
returning: R,
) -> InsertWithReturningClause<Self, R> {
InsertWithReturningClause {
statement: self,
returning,
}
}
}
impl<T: InsertStatement<HasReturningClause = TypedFalse>> InsertStatementReturning for T {}
pub struct InsertWithOnConflictClauseBuilder<
S: InsertStatement<HasOnConflictClause = TypedFalse>,
C: UniqueConstraint<Table = <S::Insertable as Insertable>::Table>,
> {
statement: S,
constraint: C,
}
impl<
S: InsertStatement<HasOnConflictClause = TypedFalse>,
C: UniqueConstraint<Table = <S::Insertable as Insertable>::Table>,
> InsertWithOnConflictClauseBuilder<S, C>
{
pub fn do_update<U: UpdateSet<UpdateTable = <S::Insertable as Insertable>::Table>>(
self,
update_set: U,
) -> InsertWithOnConflictClause<S, C, U> {
InsertWithOnConflictClause {
statement: self.statement,
_constraint: self.constraint,
update_set,
}
}
}
pub trait InsertStatementOnConflict: InsertStatement<HasOnConflictClause = TypedFalse> {
fn on_conflict<C: UniqueConstraint<Table = <Self::Insertable as Insertable>::Table>>(
self,
constraint: C,
) -> InsertWithOnConflictClauseBuilder<Self, C> {
InsertWithOnConflictClauseBuilder {
statement: self,
constraint,
}
}
fn on_conflict_do_nothing(self) -> InsertWithOnConflictDoNothingClause<Self> {
InsertWithOnConflictDoNothingClause { statement: self }
}
}
impl<T: InsertStatement<HasOnConflictClause = TypedFalse>> InsertStatementOnConflict for T {}
pub struct InsertWithOnConflictClause<
S: InsertStatement<HasOnConflictClause = TypedFalse>,
C: UniqueConstraint<Table = <S::Insertable as Insertable>::Table>,
U: UpdateSet<UpdateTable = <S::Insertable as Insertable>::Table>,
> {
statement: S,
_constraint: C,
update_set: U,
}
impl<
S: InsertStatement<HasOnConflictClause = TypedFalse>,
C: UniqueConstraint<Table = <S::Insertable as Insertable>::Table>,
U: UpdateSet<UpdateTable = <S::Insertable as Insertable>::Table>,
> InsertStatement for InsertWithOnConflictClause<S, C, U>
{
type HasOnConflictClause = TypedTrue;
type HasReturningClause = S::HasReturningClause;
type Insertable = S::Insertable;
type OutputFields = S::OutputFields;
fn get_insertable(&self) -> &Self::Insertable {
self.statement.get_insertable()
}
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)
}
fn write_on_conflict_clause<'s, 'a>(
&'s self,
f: &mut String,
parameter_binder: &mut ParameterBinder<'a>,
) -> std::fmt::Result
where
's: 'a,
{
use std::fmt::Write;
write!(
f,
" ON CONFLICT({}) DO UPDATE SET ",
C::FIELDS_COMMA_SEPERATED
)?;
self.update_set.write_sql_string(f, parameter_binder)
}
}
impl<
S: InsertStatement<HasOnConflictClause = TypedFalse>,
C: UniqueConstraint<Table = <S::Insertable as Insertable>::Table>,
U: UpdateSet<UpdateTable = <S::Insertable as Insertable>::Table>,
> SqlStatement for InsertWithOnConflictClause<S, C, U>
{
impl_sql_statement_for_insert_statement! {}
}
pub struct InsertWithOnConflictDoNothingClause<S: InsertStatement<HasOnConflictClause = TypedFalse>>
{
statement: S,
}
impl<S: InsertStatement<HasOnConflictClause = TypedFalse>> InsertStatement
for InsertWithOnConflictDoNothingClause<S>
{
type HasOnConflictClause = TypedTrue;
type HasReturningClause = S::HasReturningClause;
type Insertable = S::Insertable;
type OutputFields = S::OutputFields;
fn get_insertable(&self) -> &Self::Insertable {
self.statement.get_insertable()
}
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)
}
fn write_on_conflict_clause<'s, 'a>(
&'s self,
f: &mut String,
_parameter_binder: &mut ParameterBinder<'a>,
) -> std::fmt::Result
where
's: 'a,
{
use std::fmt::Write;
write!(f, " ON CONFLICT DO NOTHING")
}
}
impl<S: InsertStatement<HasOnConflictClause = TypedFalse>> SqlStatement
for InsertWithOnConflictDoNothingClause<S>
{
impl_sql_statement_for_insert_statement! {}
}