use diesel::insertable::{
CanInsertInSingleQuery, ColumnInsertValue, DefaultableColumnInsertValue, InsertValues,
};
use diesel::query_builder::{
AstPass, BatchInsert, InsertStatement, QueryFragment, QueryId, ValuesClause,
};
use diesel::query_dsl::methods::ExecuteDsl;
use diesel::result::QueryResult;
use diesel::{Connection, QuerySource, RunQueryDsl, Table};
use super::backend::Oracle;
use super::connection::OciConnection;
#[allow(missing_debug_implementations, missing_copy_implementations)]
pub struct Yes;
impl Default for Yes {
fn default() -> Self {
Yes
}
}
#[allow(missing_debug_implementations, missing_copy_implementations)]
pub struct No;
impl Default for No {
fn default() -> Self {
No
}
}
pub trait Any<Rhs> {
type Out: Any<Yes> + Any<No>;
}
impl Any<No> for No {
type Out = No;
}
impl Any<Yes> for No {
type Out = Yes;
}
impl Any<No> for Yes {
type Out = Yes;
}
impl Any<Yes> for Yes {
type Out = Yes;
}
pub trait ContainsDefaultableValue {
type Out: Any<Yes> + Any<No>;
}
impl<C, B> ContainsDefaultableValue for ColumnInsertValue<C, B> {
type Out = No;
}
impl<I> ContainsDefaultableValue for DefaultableColumnInsertValue<I> {
type Out = Yes;
}
impl<I, const SIZE: usize> ContainsDefaultableValue for [I; SIZE]
where
I: ContainsDefaultableValue,
{
type Out = I::Out;
}
impl<I, T> ContainsDefaultableValue for ValuesClause<I, T>
where
I: ContainsDefaultableValue,
{
type Out = I::Out;
}
impl<'a, T> ContainsDefaultableValue for &'a T
where
T: ContainsDefaultableValue,
{
type Out = T::Out;
}
macro_rules! tuple_impls {
($(
$Tuple:tt {
$(($idx:tt) -> $T:ident, $ST:ident, $TT:ident,)+
}
)+) => {
$(
impl_contains_defaultable_value!($($T,)*);
)*
}
}
macro_rules! impl_contains_defaultable_value {
(
@build
start_ts = [$($ST: ident,)*],
ts = [$T1: ident,],
bounds = [$($bounds: tt)*],
out = [$($out: tt)*],
)=> {
impl<$($ST,)*> ContainsDefaultableValue for ($($ST,)*)
where
$($ST: ContainsDefaultableValue,)*
$($bounds)*
$T1::Out: Any<$($out)*>,
{
type Out = <$T1::Out as Any<$($out)*>>::Out;
}
};
(
@build
start_ts = [$($ST: ident,)*],
ts = [$T1: ident, $($T: ident,)+],
bounds = [$($bounds: tt)*],
out = [$($out: tt)*],
)=> {
impl_contains_defaultable_value! {
@build
start_ts = [$($ST,)*],
ts = [$($T,)*],
bounds = [$($bounds)* $T1::Out: Any<$($out)*>,],
out = [<$T1::Out as Any<$($out)*>>::Out],
}
};
($T1: ident, $($T: ident,)+) => {
impl_contains_defaultable_value! {
@build
start_ts = [$T1, $($T,)*],
ts = [$($T,)*],
bounds = [],
out = [$T1::Out],
}
};
($T1: ident,) => {
impl<$T1> ContainsDefaultableValue for ($T1,)
where $T1: ContainsDefaultableValue,
{
type Out = <$T1 as ContainsDefaultableValue>::Out;
}
}
}
diesel_derives::__diesel_for_each_tuple!(tuple_impls);
impl<V, T, QId, Op, O, const STATIC_QUERY_ID: bool> ExecuteDsl<OciConnection, Oracle>
for InsertStatement<T, BatchInsert<Vec<ValuesClause<V, T>>, T, QId, STATIC_QUERY_ID>, Op>
where
T: QuerySource,
V: ContainsDefaultableValue<Out = O>,
O: Default,
(O, Self): ExecuteDsl<OciConnection, Oracle>,
{
fn execute(query: Self, conn: &mut OciConnection) -> QueryResult<usize> {
<(O, Self) as ExecuteDsl<OciConnection, Oracle>>::execute((O::default(), query), conn)
}
}
impl<V, T, QId, Op, const STATIC_QUERY_ID: bool> ExecuteDsl<OciConnection>
for (
Yes,
InsertStatement<T, BatchInsert<Vec<ValuesClause<V, T>>, T, QId, STATIC_QUERY_ID>, Op>,
)
where
T: Table + Copy + QueryId + 'static,
T::FromClause: QueryFragment<Oracle>,
Op: Copy + QueryId + QueryFragment<Oracle>,
V: InsertValues<T, Oracle> + CanInsertInSingleQuery<Oracle> + QueryId,
{
fn execute((Yes, query): Self, conn: &mut OciConnection) -> QueryResult<usize> {
conn.transaction(|conn| conn.batch_insert(query))
}
}
#[allow(missing_debug_implementations, missing_copy_implementations)]
#[repr(transparent)]
pub struct OracleBatchInsertWrapper<V, T, QId, const STATIC_QUERY_ID: bool>(
BatchInsert<V, T, QId, STATIC_QUERY_ID>,
);
impl<V, Tab, QId, const STATIC_QUERY_ID: bool> QueryFragment<Oracle>
for OracleBatchInsertWrapper<Vec<ValuesClause<V, Tab>>, Tab, QId, STATIC_QUERY_ID>
where
ValuesClause<V, Tab>: QueryFragment<Oracle>,
Tab: Table,
V: QueryFragment<Oracle> + InsertValues<Tab, Oracle>,
{
fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, Oracle>) -> QueryResult<()> {
if !STATIC_QUERY_ID {
out.unsafe_to_cache_prepared();
}
let mut records = self.0.values.iter();
if let Some(record) = records.next() {
out.push_sql("(");
record.values.column_names(out.reborrow())?;
out.push_sql(") ");
out.push_sql("select ");
record.values.walk_ast(out.reborrow())?;
out.push_sql(" from dual");
}
for record in records {
out.push_sql(" union all select ");
record.values.walk_ast(out.reborrow())?;
out.push_sql(" from dual");
}
Ok(())
}
}
#[allow(missing_copy_implementations, missing_debug_implementations)]
#[repr(transparent)]
pub struct OracleCanInsertInSingleQueryHelper<T: ?Sized>(T);
impl<V, T, QId, const STATIC_QUERY_ID: bool> CanInsertInSingleQuery<Oracle>
for OracleBatchInsertWrapper<Vec<ValuesClause<V, T>>, T, QId, STATIC_QUERY_ID>
where
OracleCanInsertInSingleQueryHelper<V>: CanInsertInSingleQuery<Oracle>,
{
fn rows_to_insert(&self) -> Option<usize> {
Some(self.0.values.len())
}
}
impl<T> CanInsertInSingleQuery<Oracle> for OracleCanInsertInSingleQueryHelper<T>
where
T: CanInsertInSingleQuery<Oracle>,
{
fn rows_to_insert(&self) -> Option<usize> {
self.0.rows_to_insert()
}
}
impl<V, T, QId, const STATIC_QUERY_ID: bool> QueryId
for OracleBatchInsertWrapper<V, T, QId, STATIC_QUERY_ID>
where
BatchInsert<V, T, QId, STATIC_QUERY_ID>: QueryId,
{
type QueryId = <BatchInsert<V, T, QId, STATIC_QUERY_ID> as QueryId>::QueryId;
const HAS_STATIC_QUERY_ID: bool =
<BatchInsert<V, T, QId, STATIC_QUERY_ID> as QueryId>::HAS_STATIC_QUERY_ID;
}
impl<V, T, QId, Op, const STATIC_QUERY_ID: bool> ExecuteDsl<OciConnection, Oracle>
for (
No,
InsertStatement<T, BatchInsert<V, T, QId, STATIC_QUERY_ID>, Op>,
)
where
T: Table + QueryId + 'static,
T::FromClause: QueryFragment<Oracle>,
Op: QueryFragment<Oracle> + QueryId,
OracleBatchInsertWrapper<V, T, QId, STATIC_QUERY_ID>:
QueryFragment<Oracle> + QueryId + CanInsertInSingleQuery<Oracle>,
{
fn execute((No, query): Self, conn: &mut OciConnection) -> QueryResult<usize> {
let query = InsertStatement::new(
query.target,
OracleBatchInsertWrapper(query.records),
query.operator,
query.returning,
);
query.execute(conn)
}
}