use crate::error::Result;
use crate::traits::{ExecuteResult, Pool, ToParams};
use crate::value::Value;
pub struct BatchInsert<'a, T> {
table: &'a str,
entities: &'a [T],
}
impl<'a, T: ToParams> BatchInsert<'a, T> {
pub fn new(table: &'a str, entities: &'a [T]) -> Self {
Self { table, entities }
}
pub async fn execute<P: Pool>(self, pool: &P) -> Result<ExecuteResult> {
if self.entities.is_empty() {
return Ok(ExecuteResult {
rows_affected: 0,
last_insert_id: None,
});
}
let column_names = T::insert_column_names();
if column_names.is_empty() {
return Ok(ExecuteResult {
rows_affected: 0,
last_insert_id: None,
});
}
let columns = column_names
.iter()
.map(|c| format!("`{}`", c))
.collect::<Vec<_>>()
.join(", ");
let single_placeholder = column_names
.iter()
.map(|_| "?")
.collect::<Vec<_>>()
.join(", ");
let single_placeholder = format!("({})", single_placeholder);
let all_placeholders = self
.entities
.iter()
.map(|_| single_placeholder.as_str())
.collect::<Vec<_>>()
.join(", ");
let sql = format!(
"INSERT INTO `{}` ({}) VALUES {}",
self.table, columns, all_placeholders
);
let mut params: Vec<Value> = Vec::with_capacity(self.entities.len() * column_names.len());
for entity in self.entities {
params.extend(entity.insert_values());
}
pool.execute(&sql, params).await
}
}
pub struct BatchUpsert<'a, T> {
table: &'a str,
entities: &'a [T],
update_columns: Option<Vec<&'a str>>,
}
impl<'a, T: ToParams> BatchUpsert<'a, T> {
pub fn new(table: &'a str, entities: &'a [T]) -> Self {
Self {
table,
entities,
update_columns: None,
}
}
pub fn update_columns(mut self, columns: Vec<&'a str>) -> Self {
self.update_columns = Some(columns);
self
}
pub async fn execute<P: Pool>(self, pool: &P) -> Result<ExecuteResult> {
if self.entities.is_empty() {
return Ok(ExecuteResult {
rows_affected: 0,
last_insert_id: None,
});
}
let column_names = T::insert_column_names();
if column_names.is_empty() {
return Ok(ExecuteResult {
rows_affected: 0,
last_insert_id: None,
});
}
let update_cols: Vec<&str> = self.update_columns.unwrap_or_else(|| column_names.to_vec());
let columns = column_names
.iter()
.map(|c| format!("`{}`", c))
.collect::<Vec<_>>()
.join(", ");
let single_placeholder = column_names
.iter()
.map(|_| "?")
.collect::<Vec<_>>()
.join(", ");
let single_placeholder = format!("({})", single_placeholder);
let all_placeholders = self
.entities
.iter()
.map(|_| single_placeholder.as_str())
.collect::<Vec<_>>()
.join(", ");
let update_clause = update_cols
.iter()
.map(|c| format!("`{name}` = VALUES(`{name}`)", name = c))
.collect::<Vec<_>>()
.join(", ");
let sql = format!(
"INSERT INTO `{}` ({}) VALUES {} ON DUPLICATE KEY UPDATE {}",
self.table, columns, all_placeholders, update_clause
);
let mut params: Vec<Value> = Vec::with_capacity(self.entities.len() * column_names.len());
for entity in self.entities {
params.extend(entity.insert_values());
}
pool.execute(&sql, params).await
}
}