use crate::{Columns, Parameterized, PgParams, Query, Sql};
#[derive(Clone)]
pub enum InsertSource<'a> {
Values(Vec<Vec<&'a str>>),
Select(Box<Query<'a>>),
}
#[derive(Clone)]
pub enum OnConflict<'a> {
DoNothing(Vec<&'a str>),
DoUpdate(Vec<&'a str>, Vec<(&'a str, &'a str)>),
}
impl<'a> Sql for OnConflict<'a> {
fn sql(&self) -> String {
match self {
OnConflict::DoNothing(columns) => {
format!("ON CONFLICT ({}) DO NOTHING", columns.join(", "))
}
OnConflict::DoUpdate(columns, updates) => {
let mut result = format!("ON CONFLICT ({}) DO UPDATE SET ", columns.join(", "));
let mut first = true;
for (col, val) in updates {
if !first {
result.push_str(", ");
}
first = false;
result.push_str(&format!("{} = {}", col, val));
}
result
}
}
}
}
#[derive(Clone)]
pub struct Insert<'a> {
pub table: &'a str,
pub columns: Vec<&'a str>,
pub source: InsertSource<'a>,
pub on_conflict: Option<OnConflict<'a>>,
pub returning: Option<Columns<'a>>,
}
impl<'a> Sql for Insert<'a> {
fn sql(&self) -> String {
let mut result = format!("INSERT INTO {} (", self.table);
let mut first = true;
for c in &self.columns {
if !first {
result.push_str(", ");
}
first = false;
result.push_str(c.as_ref());
}
result.push_str(") ");
match &self.source {
InsertSource::Values(rows) => {
result.push_str("VALUES ");
let mut first_row = true;
for row in rows {
if !first_row {
result.push_str(", ");
}
first_row = false;
result.push('(');
let mut first_val = true;
for v in row {
if !first_val {
result.push_str(", ");
}
first_val = false;
result.push_str(v.as_ref());
}
result.push(')');
}
}
InsertSource::Select(query) => {
result.push_str(&query.sql());
}
}
if let Some(on_conflict) = &self.on_conflict {
result.push_str(&format!(" {}", on_conflict.sql()));
}
if self.returning.is_some() {
result.push_str(&format!(
" RETURNING {}",
self.returning.as_ref().unwrap().sql()
));
}
result
}
}
pub struct InsertBuilder<'a> {
table: &'a str,
columns: Vec<&'a str>,
source: Option<InsertSource<'a>>,
on_conflict: Option<OnConflict<'a>>,
returning: Option<Columns<'a>>,
params: PgParams,
}
#[allow(non_snake_case)]
pub fn I<'a>(table: &'a str) -> InsertBuilder<'a> {
InsertBuilder {
table,
columns: Vec::new(),
source: None,
on_conflict: None,
returning: None,
params: PgParams::new(),
}
}
impl<'a> InsertBuilder<'a> {
pub fn build(&self) -> Insert<'a> {
Insert {
table: self.table,
columns: self.columns.clone(),
source: self
.source
.clone()
.unwrap_or(InsertSource::Values(vec![Vec::new()])),
on_conflict: self.on_conflict.clone(),
returning: self.returning.clone(),
}
}
pub fn columns(&'a mut self, columns: Vec<&'a str>) -> &'a mut InsertBuilder<'a> {
for c in columns {
self.columns.push(c);
}
self
}
pub fn values(&'a mut self, values: Vec<&'a str>) -> &'a mut InsertBuilder<'a> {
self.source = Some(InsertSource::Values(vec![values]));
self
}
pub fn rows(&'a mut self, rows: Vec<Vec<&'a str>>) -> &'a mut InsertBuilder<'a> {
self.source = Some(InsertSource::Values(rows));
self
}
pub fn select(&'a mut self, query: Query<'a>) -> &'a mut InsertBuilder<'a> {
self.source = Some(InsertSource::Select(Box::new(query)));
self
}
pub fn returning(&'a mut self, columns: Columns<'a>) -> &'a mut InsertBuilder<'a> {
self.returning = Some(columns);
self
}
pub fn on_conflict_do_nothing(
&'a mut self,
columns: Vec<&'a str>,
) -> &'a mut InsertBuilder<'a> {
self.on_conflict = Some(OnConflict::DoNothing(columns));
self
}
pub fn on_conflict_do_update(
&'a mut self,
conflict_columns: Vec<&'a str>,
updates: Vec<(&'a str, &'a str)>,
) -> &'a mut InsertBuilder<'a> {
self.on_conflict = Some(OnConflict::DoUpdate(conflict_columns, updates));
self
}
}
impl<'a> Parameterized for InsertBuilder<'a> {
fn param(&mut self) -> String {
self.params.seq()
}
}