use std::marker::PhantomData;
use field_access::FieldAccess;
use sqlx::{Database, Encode, Error, QueryBuilder, Type};
use crate::{common::{
conversion::ValueConvert, error::QueryError, fields::batch_extract, helper::get_table_name, types::PrimaryKey
}};
pub struct Upsert<'a, ET, DB, VAL>
where
ET: FieldAccess,
DB: Database,
VAL: Encode<'a, DB> + Type<DB> + ValueConvert + 'a,
{
_phantom: PhantomData<(&'a ET, DB, VAL)>,
}
impl<'a, ET, DB, VAL> Upsert<'a, ET, DB, VAL>
where
ET: FieldAccess,
DB: Database,
VAL: Encode<'a, DB> + Type<DB> + ValueConvert + 'a,
{
pub fn many(
models: impl IntoIterator<Item = &'a ET>,
primary_key: &PrimaryKey<'a>,
) -> Result<QueryBuilder<'a, DB>, Error> {
let models: Vec<_> = models.into_iter().collect();
if models.is_empty() {
return Err(QueryError::NoEntitiesProvided.into());
}
let keys = if primary_key.auto_generate() {
primary_key.get_keys()
} else {
vec![]
};
let (names, values) = batch_extract::<ET, VAL>(&models, &keys, false);
let table_name = get_table_name::<ET>();
let mut query_builder = QueryBuilder::new(
format!("INSERT INTO {} ({}) ", table_name, names.join(", "))
);
query_builder.push_values(
values,
|mut b, row| {
for value in row {
b.push_bind(value);
}
}
);
if !keys.is_empty() {
query_builder.push(" ON DUPLICATE KEY UPDATE ");
let mut first = true;
for name in &names {
if !first {
query_builder.push(", ");
}
first = false;
query_builder.push(format!("{} = VALUES({})", name, name));
}
}
Ok(query_builder)
}
pub fn one(
model: &'a ET,
primary_key: &PrimaryKey<'a>,
) -> Result<QueryBuilder<'a, DB>, Error>
{
Self::many(std::iter::once(model), primary_key)
}
}