use sqlx::database::HasArguments;
use sqlx::query::Query;
use sqlx::{Database, Error, Pool};
use std::vec;
use super::{DbType, FieldItem, ModelTableField, ModelTableName};
use sqlx::{Arguments, Executor, IntoArguments};
pub trait UpdateData<'t,DB>
where DB:Database
{
fn diff_columns(&self) -> Vec<FieldItem>;
fn sqlx_bind<'q>(
&'q self,
res:Query<'q,DB,<DB as HasArguments>::Arguments>,
) -> Query<'q,DB,<DB as HasArguments>::Arguments>;
}
pub trait ModelUpdateData<'t,DB, CT>: ModelTableField<DB>+ModelTableName
where
CT: UpdateData<'t,DB>,
DB:Database
{
fn diff(&'t self, source: Option<&Self>) -> CT;
}
pub struct Update<'t,DB, T, CT>
where
T: ModelUpdateData<'t,DB, CT>,
CT: UpdateData<'t,DB>,
DB:Database
{
pub change: CT,
_marker: (std::marker::PhantomData<&'t CT>,std::marker::PhantomData<&'t T>,std::marker::PhantomData<DB>)
}
impl<'t,DB, T, CT> Update<'t,DB, T, CT>
where
T: ModelUpdateData<'t,DB, CT>,
CT: UpdateData<'t,DB>,
DB:Database
{
pub fn new(
val: CT,
) -> Update<'t,DB,T,CT>
{
Update {
change:val,
_marker: Default::default()
}
}
pub fn model<'q:'t>(
val: &'q T,
source: Option<&T>,
) -> Update<'t,DB, T, CT>
{
Update {
change: val.diff(source),
_marker: Default::default()
}
}
pub fn sql_sets(&self) -> String {
let diff = self.change.diff_columns();
let mut values = Vec::<String>::with_capacity(diff.len());
for (pos, val) in diff.iter().enumerate() {
let bst = DbType::type_new::<DB>().mark(pos);
values.push(format!("{}={}", val.name, bst));
}
values.join(",")
}
pub fn bind_values<'q>(
&'q self,
res:Query<'q,DB,<DB as HasArguments>::Arguments>,
) -> Query<'q,DB,<DB as HasArguments>::Arguments> {
self.change.sqlx_bind(res)
}
execute_by_sql!(Update<DB,T,CT>);
pub async fn execute<'c>(self,pool:&'c Pool<DB>) -> Result<<DB as Database>::QueryResult, Error>
where
for<'n> <DB as HasArguments<'n>>::Arguments:
Arguments<'n>+IntoArguments<'n,DB>,
&'c Pool<DB>: Executor<'c, Database = DB>
{
let table = T::table_name();
let values = self.sql_sets();
let sql = format!("UPDATE {} SET {}", table.full_name(), values);
let mut res = sqlx::query(sql.as_str());
res = self.bind_values(res);
res.execute(pool).await
}
pub async fn execute_by_where<'c,RB>(
self,
where_sql: &str,
where_bind: RB,
pool:&'c Pool<DB>
) -> Result<<DB as Database>::QueryResult, Error>
where
for<'q> RB: FnOnce(
Query<'q,DB,<DB as HasArguments>::Arguments>,
&'q Update<DB,T, CT>,
) -> Query<'q,DB,<DB as HasArguments<'q>>::Arguments>,
for<'n> <DB as HasArguments<'n>>::Arguments:
Arguments<'n>+IntoArguments<'n,DB>,
&'c Pool<DB>: Executor<'c, Database = DB>
{
let table = T::table_name();
let values = self.sql_sets();
let sql = format!(
"UPDATE {} SET {} WHERE {}",
table.full_name(),
values,
where_sql
);
let mut res = sqlx::query(sql.as_str());
res = self.bind_values(res);
res = where_bind(res,&self);
res.execute(pool).await
}
pub async fn execute_by_scalar_pk<'c,PT>(&self, pk_scalar:PT,pool:&'c Pool<DB>) -> Result<<DB as Database>::QueryResult, Error>
where for<'q> PT:'q+ Send + sqlx::Encode<'q, DB> + sqlx::Type<DB>,
for<'n> <DB as HasArguments<'n>>::Arguments:
Arguments<'n>+IntoArguments<'n,DB>,
&'c Pool<DB>: Executor<'c, Database = DB>
{
let table = T::table_name();
let values = self.sql_sets();
let sql = format!(
"UPDATE {} SET {} WHERE {}",
table.full_name(),
values,
scalar_pk_where!(DB,T::table_pk())
);
let mut res = sqlx::query(sql.as_str());
res = self.bind_values(res);
res=res.bind(pk_scalar);
res.execute(pool).await
}
}
use sqlx::{MySql};
impl<'t,T, CT> Update<'t,MySql,T, CT>
where
T: ModelUpdateData<'t,MySql, CT>,
CT: UpdateData<'t,MySql>
{
pub async fn execute_by_pk<'c>(&self, source: &T,pool:&'c Pool<MySql>) -> Result<<MySql as Database>::QueryResult, Error>
{
let table = T::table_name();
let pkf = T::table_pk();
let mut where_sql = vec![];
for (pos, val) in pkf.0.iter().enumerate() {
let bst = DbType::type_new::<MySql>().mark(pos);
where_sql.push(format!("{}={}", val.name, bst));
}
let values = self.sql_sets();
let sql = format!(
"UPDATE {} SET {} WHERE {}",
table.full_name(),
values,
where_sql.join(" and ")
);
let mut res = sqlx::query(sql.as_str());
res = self.bind_values(res);
for val in pkf.0.iter() {
res = source.query_sqlx_bind(val, res);
}
res.execute(pool).await
}
}