use std::marker::PhantomData;
use rorm_db::database;
use rorm_db::error::Error;
use rorm_db::executor::Executor;
use crate::conditions::Value;
use crate::crud::decoder::Decoder;
use crate::crud::selector::Selector;
use crate::fields::proxy;
use crate::fields::proxy::FieldProxy;
use crate::fields::utils::column_name::ColumnName;
use crate::internal::patch::{IntoPatchCow, PatchCow};
use crate::internal::query_context::QueryContext;
use crate::model::{Model, Patch};
pub fn insert<'ex, E, S>(executor: E, selector: S) -> InsertBuilder<E, S::Model, S>
where
E: Executor<'ex>,
S: Selector<Model: Patch<ValueSpaceImpl = S>>,
{
InsertBuilder {
executor,
selector,
model: PhantomData,
}
}
#[must_use]
pub struct InsertBuilder<E, M, S> {
executor: E,
selector: S,
model: PhantomData<M>,
}
impl<'ex, E, M> InsertBuilder<E, M, M::ValueSpaceImpl>
where
E: Executor<'ex>,
M: Model,
{
fn set_return<S>(self, selector: S) -> InsertBuilder<E, M, S>
where
S: Selector<Model = M>,
{
InsertBuilder {
executor: self.executor,
selector,
model: PhantomData,
}
}
pub fn return_nothing(self) -> InsertReturningNothing<E, M> {
InsertReturningNothing {
executor: self.executor,
model: PhantomData,
}
}
pub fn return_primary_key(self) -> InsertBuilder<E, M, FieldProxy<(M::Primary, M)>> {
self.set_return(proxy::new())
}
pub fn return_tuple<Return>(self, tuple: Return) -> InsertBuilder<E, M, Return>
where
Return: Selector<Model = M>,
{
self.set_return(tuple)
}
pub fn return_patch<Return>(self) -> InsertBuilder<E, M, Return::ValueSpaceImpl>
where
Return: Patch<Model = M>,
{
self.set_return(Default::default())
}
}
impl<'ex, E, M, S> InsertBuilder<E, M, S>
where
E: Executor<'ex>,
M: Model,
S: Selector<Model = M>,
{
const CHECK: () = {
if !S::INSERT_COMPATIBLE {
panic!("An invalid selector was passed to an InsertBuilder! Please check you're insert!(..).return_tuple(..) calls!");
}
};
pub async fn single<P: Patch<Model = M>>(self, patch: &P) -> Result<S::Result, Error> {
#[allow(clippy::let_unit_value)]
let _check = Self::CHECK;
let columns = P::columns();
let columns = columns.iter().map(ColumnName::as_str).collect::<Vec<_>>();
let values = patch.references();
let values: Vec<_> = values.iter().map(Value::as_sql).collect();
let mut ctx = QueryContext::new();
let decoder = self.selector.select(&mut ctx);
let returning = ctx
.get_returning()
.expect("Should have been checked in set_select");
let row = database::insert_returning(
self.executor,
P::Model::TABLE,
&columns,
&values,
&returning,
)
.await?;
Ok(decoder.by_index(&row)?)
}
pub async fn bulk<'p, I, P>(self, patches: I) -> Result<Vec<S::Result>, Error>
where
I: IntoIterator,
I::Item: IntoPatchCow<'p, Patch = P>,
P: Patch<Model = M>,
{
#[allow(clippy::let_unit_value)]
let _check = Self::CHECK;
let mut values: Vec<Value<'p>> = Vec::new();
for patch in patches {
match patch.into_patch_cow() {
PatchCow::Borrowed(patch) => patch.push_references(&mut values),
PatchCow::Owned(patch) => patch.push_values(&mut values),
}
}
let columns = P::columns();
let columns = columns.iter().map(ColumnName::as_str).collect::<Vec<_>>();
let values: Vec<_> = values.iter().map(Value::as_sql).collect();
let values_slices: Vec<_> = values.chunks(columns.len()).collect();
let mut ctx = QueryContext::new();
let decoder = self.selector.select(&mut ctx);
let returning = ctx
.get_returning()
.expect("Should have been checked in set_select");
let rows = database::insert_bulk_returning(
self.executor,
M::TABLE,
&columns,
&values_slices,
&returning,
)
.await?;
rows.iter()
.map(|row| decoder.by_index(row).map_err(Into::into))
.collect()
}
}
#[must_use]
pub struct InsertReturningNothing<E, M> {
executor: E,
model: PhantomData<M>,
}
impl<'ex, E, M> InsertReturningNothing<E, M>
where
E: Executor<'ex>,
M: Model,
{
pub async fn single<P: Patch<Model = M>>(self, patch: &P) -> Result<(), Error> {
let columns = P::columns();
let columns = columns.iter().map(ColumnName::as_str).collect::<Vec<_>>();
let values = patch.references();
let values: Vec<_> = values.iter().map(Value::as_sql).collect();
database::insert(self.executor, M::TABLE, &columns, &values).await
}
pub async fn bulk<'p, I, P>(self, patches: I) -> Result<(), Error>
where
I: IntoIterator,
I::Item: IntoPatchCow<'p, Patch = P>,
P: Patch<Model = M>,
{
let mut values: Vec<Value<'p>> = Vec::new();
for patch in patches {
match patch.into_patch_cow() {
PatchCow::Borrowed(patch) => patch.push_references(&mut values),
PatchCow::Owned(patch) => patch.push_values(&mut values),
}
}
let columns = P::columns();
let columns = columns.iter().map(ColumnName::as_str).collect::<Vec<_>>();
let values: Vec<_> = values.iter().map(Value::as_sql).collect();
let values_slices: Vec<_> = values.chunks(columns.len()).collect();
database::insert_bulk(self.executor, M::TABLE, &columns, &values_slices).await
}
}