use std::{collections::HashSet, hash::Hash};
use quex::{Driver, Executor, ParamRef, ParamSource, PreparedStatement, RowStream};
use crate::{
AttachBelongsToMany, AttachMorphToMany, MySql, Postgres, Query, Sqlite, SyncBelongsToMany,
SyncMorphToMany, SyncResult, SyncWithoutDetachingBelongsToMany,
SyncWithoutDetachingMorphToMany, SyncWithoutDetachingResult, ToggleBelongsToMany,
ToggleMorphToMany, ToggleResult,
aggregate::count,
alias::Alias,
MorphToManyValueSpec, build_many_to_many_values, build_morph_to_many_values,
builder::{Delete, Insert, ReturningInsert, ReturningUpdate, Update},
insert_into,
lower::LowerCtx,
param::Param,
query::{
Compiled, LockedQuery, LockedQueryOf, LowerProject, QueryOf, TypedCompiled, select, star,
},
raw::{Raw, RawQuery, RawScalar},
span::{Span, TextSource, TextSpan},
};
struct CompiledParams<'a> {
params: &'a [Param],
data: &'a [u8],
}
impl CompiledParams<'_> {
fn blob(&self, span: Span) -> &[u8] {
&self.data[span.start as usize..(span.start + span.len) as usize]
}
fn text(&self, span: TextSpan) -> &str {
match span.0 {
TextSource::StaticText(value) => value,
TextSource::Text(span) => {
let bytes = self.blob(span);
unsafe { std::str::from_utf8_unchecked(bytes) }
}
}
}
}
impl ParamSource for CompiledParams<'_> {
fn len(&self) -> usize {
self.params.len()
}
fn value_at(&self, index: usize) -> ParamRef<'_> {
match self.params[index] {
Param::Null => ParamRef::Null,
Param::Bool(Some(value)) => ParamRef::I64(i64::from(value)),
Param::Bool(None) => ParamRef::Null,
Param::Float(Some(value)) => ParamRef::F64(f64::from(value)),
Param::Float(None) => ParamRef::Null,
Param::Double(Some(value)) => ParamRef::F64(value),
Param::Double(None) => ParamRef::Null,
Param::Int(Some(value)) => ParamRef::I64(i64::from(value)),
Param::Int(None) => ParamRef::Null,
Param::UInt(Some(value)) => ParamRef::U64(u64::from(value)),
Param::UInt(None) => ParamRef::Null,
Param::UBigInt(Some(value)) => ParamRef::U64(value),
Param::UBigInt(None) => ParamRef::Null,
Param::BigInt(Some(value)) => ParamRef::I64(value),
Param::BigInt(None) => ParamRef::Null,
Param::Text(Some(span)) => ParamRef::Str(self.text(span)),
Param::Text(None) => ParamRef::Null,
Param::Blob(Some(span)) => ParamRef::Bytes(self.blob(span)),
Param::Blob(None) => ParamRef::Null,
}
}
}
fn compiled_params(compiled: &Compiled) -> CompiledParams<'_> {
CompiledParams {
params: &compiled.params,
data: compiled.data.as_slice(),
}
}
async fn compiled_fetch<'e, E>(compiled: Compiled, exec: &'e mut E) -> quex::Result<E::Rows<'e>>
where
E: Executor + ?Sized,
{
if compiled.params.is_empty() {
exec.query(&compiled.sql).await
} else {
let params = compiled_params(&compiled);
exec.query_prepared_source(&compiled.sql, ¶ms).await
}
}
async fn compiled_execute<E>(compiled: Compiled, mut exec: E) -> quex::Result<quex::ExecResult>
where
E: Executor,
{
let mut stmt = exec.prepare(&compiled.sql).await?;
let params = compiled_params(&compiled);
stmt.exec_source(¶ms).await
}
async fn compiled_all<T, E>(compiled: Compiled, mut exec: E) -> quex::Result<Vec<T>>
where
T: quex::FromRow,
E: Executor,
{
let mut rows = if compiled.params.is_empty() {
exec.query(&compiled.sql).await?
} else {
let params = compiled_params(&compiled);
exec.query_prepared_source(&compiled.sql, ¶ms).await?
};
let mut out = Vec::new();
while let Some(row) = rows.next().await? {
out.push(row.decode::<T>()?);
}
Ok(out)
}
async fn compiled_one<T, E>(compiled: Compiled, mut exec: E) -> quex::Result<T>
where
T: quex::FromRow,
E: Executor,
{
let mut rows = if compiled.params.is_empty() {
exec.query(&compiled.sql).await?
} else {
let params = compiled_params(&compiled);
exec.query_prepared_source(&compiled.sql, ¶ms).await?
};
match rows.next().await? {
Some(row) => Ok(row.decode::<T>()?),
None => Err(quex::Error::Unsupported("query returned no rows".into())),
}
}
async fn compiled_optional<T, E>(compiled: Compiled, mut exec: E) -> quex::Result<Option<T>>
where
T: quex::FromRow,
E: Executor,
{
let mut rows = if compiled.params.is_empty() {
exec.query(&compiled.sql).await?
} else {
let params = compiled_params(&compiled);
exec.query_prepared_source(&compiled.sql, ¶ms).await?
};
match rows.next().await? {
Some(row) => Ok(Some(row.decode::<T>()?)),
None => Ok(None),
}
}
fn can_direct_count(query: &Query) -> bool {
!query.distinct
&& query.group_by.is_empty()
&& query.havings.is_empty()
&& query.limit.is_empty()
&& query.offset.is_empty()
&& query.lock.is_none()
&& query.compound.is_none()
}
fn into_count_query(query: Query) -> Query {
if !can_direct_count(&query) {
return select(count(star()).alias("aggregate")).from(query.alias("__qraft_count"));
}
let Query {
from,
filters,
ctes,
params,
data,
..
} = query;
let mut count_query = Query::default();
count_query.from = from;
count_query.filters = filters;
count_query.ctes = ctes;
count_query.params = params;
count_query.data = data;
let mut ctx = LowerCtx {
instrs: &mut count_query.project,
params: &mut count_query.params,
data: &mut count_query.data,
};
count(star()).alias("aggregate").lower_project(&mut ctx);
count_query
}
async fn count_query<E>(query: Query, exec: E) -> quex::Result<i64>
where
E: Executor,
{
into_count_query(query).one(exec).await
}
impl Compiled {
pub async fn fetch<'e, E>(self, exec: &'e mut E) -> quex::Result<E::Rows<'e>>
where
E: Executor + ?Sized,
{
compiled_fetch(self, exec).await
}
pub async fn execute<E>(self, exec: E) -> quex::Result<quex::ExecResult>
where
E: Executor,
{
compiled_execute(self, exec).await
}
}
impl<T> TypedCompiled<T> {
pub async fn fetch<'e, E>(self, exec: &'e mut E) -> quex::Result<E::Rows<'e>>
where
T: quex::FromRow,
E: Executor + ?Sized,
{
compiled_fetch(
Compiled {
sql: self.sql,
params: self.params,
data: self.data,
},
exec,
)
.await
}
pub async fn execute<E>(self, exec: E) -> quex::Result<quex::ExecResult>
where
E: Executor,
{
compiled_execute(
Compiled {
sql: self.sql,
params: self.params,
data: self.data,
},
exec,
)
.await
}
}
impl<T> TypedCompiled<T>
where
T: quex::FromRow,
{
pub async fn all<E>(self, exec: E) -> quex::Result<Vec<T>>
where
E: Executor,
{
compiled_all(
Compiled {
sql: self.sql,
params: self.params,
data: self.data,
},
exec,
)
.await
}
pub async fn one<E>(self, exec: E) -> quex::Result<T>
where
E: Executor,
{
compiled_one(
Compiled {
sql: self.sql,
params: self.params,
data: self.data,
},
exec,
)
.await
}
pub async fn optional<E>(self, exec: E) -> quex::Result<Option<T>>
where
E: Executor,
{
compiled_optional(
Compiled {
sql: self.sql,
params: self.params,
data: self.data,
},
exec,
)
.await
}
}
macro_rules! impl_runtime_typed_query {
($ty:ident$(<$m:ident>)?) => {
impl$(<$m>)? $ty$(<$m>)?
where
$($m: quex::FromRow,)?
{
pub async fn fetch<'e, E>(self, exec: &'e mut E) -> quex::Result<E::Rows<'e>>
where
E: Executor + ?Sized,
{
match exec.driver() {
Driver::Pgsql => self.into_compiled::<Postgres>().fetch(exec).await,
Driver::Sqlite => self.into_compiled::<Sqlite>().fetch(exec).await,
Driver::Mysql => self.into_compiled::<MySql>().fetch(exec).await,
}
}
pub async fn all(self, exec: impl Executor) -> quex::Result<Vec<$($m)?>>
{
let driver = exec.driver();
match driver {
Driver::Pgsql => self.into_compiled::<Postgres>().all(exec).await,
Driver::Sqlite => self.into_compiled::<Sqlite>().all(exec).await,
Driver::Mysql => self.into_compiled::<MySql>().all(exec).await,
}
}
pub async fn one(self, exec: impl Executor) -> quex::Result<$($m)?>
{
let driver = exec.driver();
match driver {
Driver::Pgsql => self.into_compiled::<Postgres>().one(exec).await,
Driver::Sqlite => self.into_compiled::<Sqlite>().one(exec).await,
Driver::Mysql => self.into_compiled::<MySql>().one(exec).await,
}
}
pub async fn optional(self, exec: impl Executor) -> quex::Result<Option<$($m)?>>
{
let driver = exec.driver();
match driver {
Driver::Pgsql => self.into_compiled::<Postgres>().optional(exec).await,
Driver::Sqlite => self.into_compiled::<Sqlite>().optional(exec).await,
Driver::Mysql => self.into_compiled::<MySql>().optional(exec).await,
}
}
}
};
}
impl_runtime_typed_query!(QueryOf<M>);
impl_runtime_typed_query!(LockedQueryOf<M>);
impl Query {
pub async fn fetch<'e, E>(self, exec: &'e mut E) -> quex::Result<E::Rows<'e>>
where
E: Executor + ?Sized,
{
match exec.driver() {
Driver::Pgsql => self.into_compiled::<Postgres>().fetch(exec).await,
Driver::Sqlite => self.into_compiled::<Sqlite>().fetch(exec).await,
Driver::Mysql => self.into_compiled::<MySql>().fetch(exec).await,
}
}
pub async fn all<T>(self, exec: impl Executor) -> quex::Result<Vec<T>>
where
T: quex::FromRow,
{
let driver = exec.driver();
match driver {
Driver::Pgsql => {
self.typed::<T>()
.into_compiled::<Postgres>()
.all(exec)
.await
}
Driver::Sqlite => self.typed::<T>().into_compiled::<Sqlite>().all(exec).await,
Driver::Mysql => self.typed::<T>().into_compiled::<MySql>().all(exec).await,
}
}
pub async fn one<T>(self, exec: impl Executor) -> quex::Result<T>
where
T: quex::FromRow,
{
let driver = exec.driver();
match driver {
Driver::Pgsql => {
self.typed::<T>()
.into_compiled::<Postgres>()
.one(exec)
.await
}
Driver::Sqlite => self.typed::<T>().into_compiled::<Sqlite>().one(exec).await,
Driver::Mysql => self.typed::<T>().into_compiled::<MySql>().one(exec).await,
}
}
pub async fn optional<T>(self, exec: impl Executor) -> quex::Result<Option<T>>
where
T: quex::FromRow,
{
let driver = exec.driver();
match driver {
Driver::Pgsql => {
self.typed::<T>()
.into_compiled::<Postgres>()
.optional(exec)
.await
}
Driver::Sqlite => {
self.typed::<T>()
.into_compiled::<Sqlite>()
.optional(exec)
.await
}
Driver::Mysql => {
self.typed::<T>()
.into_compiled::<MySql>()
.optional(exec)
.await
}
}
}
pub async fn count<E>(self, exec: E) -> quex::Result<i64>
where
E: Executor,
{
count_query(self, exec).await
}
}
impl LockedQuery {
pub async fn fetch<'e, E>(self, exec: &'e mut E) -> quex::Result<E::Rows<'e>>
where
E: Executor + ?Sized,
{
match exec.driver() {
Driver::Pgsql => self.into_compiled::<Postgres>().fetch(exec).await,
Driver::Sqlite => self.into_compiled::<Sqlite>().fetch(exec).await,
Driver::Mysql => self.into_compiled::<MySql>().fetch(exec).await,
}
}
pub async fn all<T>(self, exec: impl Executor) -> quex::Result<Vec<T>>
where
T: quex::FromRow,
{
let driver = exec.driver();
match driver {
Driver::Pgsql => {
self.typed::<T>()
.into_compiled::<Postgres>()
.all(exec)
.await
}
Driver::Sqlite => self.typed::<T>().into_compiled::<Sqlite>().all(exec).await,
Driver::Mysql => self.typed::<T>().into_compiled::<MySql>().all(exec).await,
}
}
pub async fn one<T>(self, exec: impl Executor) -> quex::Result<T>
where
T: quex::FromRow,
{
let driver = exec.driver();
match driver {
Driver::Pgsql => {
self.typed::<T>()
.into_compiled::<Postgres>()
.one(exec)
.await
}
Driver::Sqlite => self.typed::<T>().into_compiled::<Sqlite>().one(exec).await,
Driver::Mysql => self.typed::<T>().into_compiled::<MySql>().one(exec).await,
}
}
pub async fn optional<T>(self, exec: impl Executor) -> quex::Result<Option<T>>
where
T: quex::FromRow,
{
let driver = exec.driver();
match driver {
Driver::Pgsql => {
self.typed::<T>()
.into_compiled::<Postgres>()
.optional(exec)
.await
}
Driver::Sqlite => {
self.typed::<T>()
.into_compiled::<Sqlite>()
.optional(exec)
.await
}
Driver::Mysql => {
self.typed::<T>()
.into_compiled::<MySql>()
.optional(exec)
.await
}
}
}
pub async fn count<E>(self, exec: E) -> quex::Result<i64>
where
E: Executor,
{
count_query(<Query as From<LockedQuery>>::from(self), exec).await
}
}
impl<M> Insert<M>
where
M: crate::Qrafting,
{
pub async fn execute<E>(self, exec: E) -> quex::Result<quex::ExecResult>
where
E: Executor,
{
let driver = exec.driver();
let sql = match driver {
Driver::Pgsql => self.to_sql::<Postgres>(),
Driver::Sqlite => self.to_sql::<Sqlite>(),
Driver::Mysql => self.to_sql::<MySql>(),
};
Compiled {
sql,
params: self.query.params,
data: self.query.data,
}
.execute(exec)
.await
}
}
impl<M, T> ReturningInsert<M, T>
where
M: crate::Qrafting,
T: quex::FromRow,
{
pub async fn fetch<'e, E>(self, exec: &'e mut E) -> quex::Result<E::Rows<'e>>
where
E: Executor + ?Sized,
{
match exec.driver() {
Driver::Pgsql => self.into_compiled::<Postgres>().fetch(exec).await,
Driver::Sqlite => self.into_compiled::<Sqlite>().fetch(exec).await,
Driver::Mysql => self.into_compiled::<MySql>().fetch(exec).await,
}
}
pub async fn all<R>(self, exec: impl Executor) -> quex::Result<Vec<R>>
where
R: quex::FromRow,
{
let driver = exec.driver();
match driver {
Driver::Pgsql => {
self.typed::<R>()
.into_compiled::<Postgres>()
.all(exec)
.await
}
Driver::Sqlite => self.typed::<R>().into_compiled::<Sqlite>().all(exec).await,
Driver::Mysql => self.typed::<R>().into_compiled::<MySql>().all(exec).await,
}
}
pub async fn one<R>(self, exec: impl Executor) -> quex::Result<R>
where
R: quex::FromRow,
{
let driver = exec.driver();
match driver {
Driver::Pgsql => {
self.typed::<R>()
.into_compiled::<Postgres>()
.one(exec)
.await
}
Driver::Sqlite => self.typed::<R>().into_compiled::<Sqlite>().one(exec).await,
Driver::Mysql => self.typed::<R>().into_compiled::<MySql>().one(exec).await,
}
}
pub async fn optional<R>(self, exec: impl Executor) -> quex::Result<Option<R>>
where
R: quex::FromRow,
{
let driver = exec.driver();
match driver {
Driver::Pgsql => {
self.typed::<R>()
.into_compiled::<Postgres>()
.optional(exec)
.await
}
Driver::Sqlite => {
self.typed::<R>()
.into_compiled::<Sqlite>()
.optional(exec)
.await
}
Driver::Mysql => {
self.typed::<R>()
.into_compiled::<MySql>()
.optional(exec)
.await
}
}
}
}
impl<M> Update<M> {
pub async fn execute<E>(self, exec: E) -> quex::Result<quex::ExecResult>
where
E: Executor,
{
let driver = exec.driver();
match driver {
Driver::Pgsql => self.into_compiled::<Postgres>().execute(exec).await,
Driver::Sqlite => self.into_compiled::<Sqlite>().execute(exec).await,
Driver::Mysql => self.into_compiled::<MySql>().execute(exec).await,
}
}
}
impl<M, T> ReturningUpdate<M, T>
where
T: quex::FromRow,
{
pub async fn fetch<'e, E>(self, exec: &'e mut E) -> quex::Result<E::Rows<'e>>
where
E: Executor + ?Sized,
{
match exec.driver() {
Driver::Pgsql => self.into_compiled::<Postgres>().fetch(exec).await,
Driver::Sqlite => self.into_compiled::<Sqlite>().fetch(exec).await,
Driver::Mysql => self.into_compiled::<MySql>().fetch(exec).await,
}
}
pub async fn all<R>(self, exec: impl Executor) -> quex::Result<Vec<R>>
where
R: quex::FromRow,
{
let driver = exec.driver();
match driver {
Driver::Pgsql => {
self.typed::<R>()
.into_compiled::<Postgres>()
.all(exec)
.await
}
Driver::Sqlite => self.typed::<R>().into_compiled::<Sqlite>().all(exec).await,
Driver::Mysql => self.typed::<R>().into_compiled::<MySql>().all(exec).await,
}
}
pub async fn one<R>(self, exec: impl Executor) -> quex::Result<R>
where
R: quex::FromRow,
{
let driver = exec.driver();
match driver {
Driver::Pgsql => {
self.typed::<R>()
.into_compiled::<Postgres>()
.one(exec)
.await
}
Driver::Sqlite => self.typed::<R>().into_compiled::<Sqlite>().one(exec).await,
Driver::Mysql => self.typed::<R>().into_compiled::<MySql>().one(exec).await,
}
}
pub async fn optional<R>(self, exec: impl Executor) -> quex::Result<Option<R>>
where
R: quex::FromRow,
{
let driver = exec.driver();
match driver {
Driver::Pgsql => {
self.typed::<R>()
.into_compiled::<Postgres>()
.optional(exec)
.await
}
Driver::Sqlite => {
self.typed::<R>()
.into_compiled::<Sqlite>()
.optional(exec)
.await
}
Driver::Mysql => {
self.typed::<R>()
.into_compiled::<MySql>()
.optional(exec)
.await
}
}
}
}
impl<T> Delete<T> {
pub async fn execute<E>(self, exec: E) -> quex::Result<quex::ExecResult>
where
E: Executor,
{
let driver = exec.driver();
match driver {
Driver::Pgsql => self.into_compiled::<Postgres>().execute(exec).await,
Driver::Sqlite => self.into_compiled::<Sqlite>().execute(exec).await,
Driver::Mysql => self.into_compiled::<MySql>().execute(exec).await,
}
}
}
impl Raw {
pub async fn execute(self, exec: impl Executor) -> quex::Result<quex::ExecResult> {
let driver = exec.driver();
match driver {
Driver::Pgsql => self.into_compiled().execute(exec).await,
Driver::Sqlite => self.into_compiled().execute(exec).await,
Driver::Mysql => self.into_compiled().execute(exec).await,
}
}
}
impl<T> RawQuery<T>
where
T: quex::FromRow,
{
pub async fn fetch<'e, E>(self, exec: &'e mut E) -> quex::Result<E::Rows<'e>>
where
E: Executor + ?Sized,
{
match exec.driver() {
Driver::Pgsql => self.into_compiled().fetch(exec).await,
Driver::Sqlite => self.into_compiled().fetch(exec).await,
Driver::Mysql => self.into_compiled().fetch(exec).await,
}
}
pub async fn all(self, exec: impl Executor) -> quex::Result<Vec<T>> {
let driver = exec.driver();
match driver {
Driver::Pgsql => self.into_compiled().all(exec).await,
Driver::Sqlite => self.into_compiled().all(exec).await,
Driver::Mysql => self.into_compiled().all(exec).await,
}
}
pub async fn one(self, exec: impl Executor) -> quex::Result<T> {
let driver = exec.driver();
match driver {
Driver::Pgsql => self.into_compiled().one(exec).await,
Driver::Sqlite => self.into_compiled().one(exec).await,
Driver::Mysql => self.into_compiled().one(exec).await,
}
}
pub async fn optional(self, exec: impl Executor) -> quex::Result<Option<T>> {
let driver = exec.driver();
match driver {
Driver::Pgsql => self.into_compiled().optional(exec).await,
Driver::Sqlite => self.into_compiled().optional(exec).await,
Driver::Mysql => self.into_compiled().optional(exec).await,
}
}
}
impl<T> RawScalar<T>
where
T: crate::TypeMeta + crate::TypeCast,
<T as crate::TypeCast>::From: quex::FromRow,
{
pub async fn fetch<'e, E>(self, exec: &'e mut E) -> quex::Result<E::Rows<'e>>
where
E: Executor + ?Sized,
{
match exec.driver() {
Driver::Pgsql => self.into_compiled().fetch(exec).await,
Driver::Sqlite => self.into_compiled().fetch(exec).await,
Driver::Mysql => self.into_compiled().fetch(exec).await,
}
}
pub async fn all(self, exec: impl Executor) -> quex::Result<Vec<<T as crate::TypeCast>::From>> {
let driver = exec.driver();
match driver {
Driver::Pgsql => self.into_compiled().all(exec).await,
Driver::Sqlite => self.into_compiled().all(exec).await,
Driver::Mysql => self.into_compiled().all(exec).await,
}
}
pub async fn one(self, exec: impl Executor) -> quex::Result<<T as crate::TypeCast>::From> {
let driver = exec.driver();
match driver {
Driver::Pgsql => self.into_compiled().one(exec).await,
Driver::Sqlite => self.into_compiled().one(exec).await,
Driver::Mysql => self.into_compiled().one(exec).await,
}
}
pub async fn optional(
self,
exec: impl Executor,
) -> quex::Result<Option<<T as crate::TypeCast>::From>> {
let driver = exec.driver();
match driver {
Driver::Pgsql => self.into_compiled().optional(exec).await,
Driver::Sqlite => self.into_compiled().optional(exec).await,
Driver::Mysql => self.into_compiled().optional(exec).await,
}
}
}
impl<M> QueryOf<M>
where
M: crate::Qrafting,
{
pub async fn count<E>(self, exec: E) -> quex::Result<i64>
where
E: Executor,
{
count_query(self.into(), exec).await
}
}
impl<M> LockedQueryOf<M>
where
M: crate::Qrafting + quex::FromRow,
{
pub async fn count<E>(self, exec: E) -> quex::Result<i64>
where
E: Executor,
{
count_query(<Query as From<LockedQueryOf<M>>>::from(self), exec).await
}
}
impl<Pivot, ParentMeta, RelatedMeta, ParentKey, RelatedKey>
AttachBelongsToMany<Pivot, ParentMeta, RelatedMeta, ParentKey, RelatedKey>
where
Pivot: crate::Qrafting + 'static,
ParentMeta: crate::TypeMeta + crate::Comparable + crate::Nullability,
RelatedMeta: crate::TypeMeta + crate::Comparable + crate::Nullability,
ParentKey: Clone + crate::Compatible<ParentMeta>,
RelatedKey: Clone + crate::Compatible<RelatedMeta>,
crate::BinaryType<crate::NullOf<ParentMeta>, crate::NullOf<ParentMeta>>: crate::PredicateType,
crate::BinaryType<crate::NullOf<RelatedMeta>, crate::NullOf<RelatedMeta>>: crate::PredicateType,
{
pub async fn execute<E>(self, exec: E) -> quex::Result<u64>
where
E: Executor,
{
let AttachBelongsToMany {
pivot_table,
parent_column,
related_column,
parent_key,
related_keys,
} = self;
let attached = related_keys.len() as u64;
if attached > 0 {
let values =
build_many_to_many_values(parent_column, related_column, &parent_key, related_keys);
insert_into(pivot_table)
.values(values)
.no_returning()
.execute(exec)
.await?;
}
Ok(attached)
}
}
impl<Pivot, ParentMeta, RelatedMeta, ParentKey, RelatedKey>
AttachMorphToMany<Pivot, ParentMeta, RelatedMeta, ParentKey, RelatedKey>
where
Pivot: crate::Qrafting + 'static,
ParentMeta: crate::TypeMeta + crate::Comparable + crate::Nullability,
RelatedMeta: crate::TypeMeta + crate::Comparable + crate::Nullability,
ParentKey: Clone + crate::Compatible<ParentMeta>,
RelatedKey: Clone + crate::Compatible<RelatedMeta>,
crate::BinaryType<crate::NullOf<ParentMeta>, crate::NullOf<ParentMeta>>: crate::PredicateType,
crate::BinaryType<crate::NullOf<RelatedMeta>, crate::NullOf<RelatedMeta>>: crate::PredicateType,
{
pub async fn execute<E>(self, exec: E) -> quex::Result<u64>
where
E: Executor,
{
let AttachMorphToMany {
pivot_table,
parent_column,
type_column,
related_column,
parent_key,
morph_type,
related_keys,
} = self;
let attached = related_keys.len() as u64;
if attached > 0 {
let values = build_morph_to_many_values(MorphToManyValueSpec {
parent_column,
type_column,
related_column,
parent_key,
morph_type,
related_keys,
});
insert_into(pivot_table)
.values(values)
.no_returning()
.execute(exec)
.await?;
}
Ok(attached)
}
}
macro_rules! impl_sync_action {
($ty:ident, $result:ty, $detach_iter:ident) => {
impl<Pivot, ParentMeta, RelatedMeta, ParentKey, RelatedKey>
$ty<Pivot, ParentMeta, RelatedMeta, ParentKey, RelatedKey>
where
Pivot: crate::Qrafting + 'static,
ParentMeta: crate::TypeMeta + crate::Comparable + crate::Nullability,
RelatedMeta: crate::TypeMeta + crate::Comparable + crate::Nullability,
ParentKey: Clone + crate::Compatible<ParentMeta>,
RelatedKey: Clone + Eq + Hash + quex::FromRow + crate::Compatible<RelatedMeta>,
crate::BinaryType<crate::NullOf<ParentMeta>, crate::NullOf<ParentMeta>>:
crate::PredicateType,
crate::BinaryType<crate::NullOf<RelatedMeta>, crate::NullOf<RelatedMeta>>:
crate::PredicateType,
crate::UnaryType<crate::NullOf<RelatedMeta>>: crate::PredicateType,
{
pub async fn execute<E>(self, exec: E) -> quex::Result<$result>
where
E: Executor + Clone,
{
let existing: Vec<RelatedKey> = self.existing_query().all(exec.clone()).await?;
let desired = self.desired_keys();
let mut existing_set = HashSet::with_capacity(existing.len());
existing_set.extend(existing);
let mut desired_set = HashSet::with_capacity(desired.len());
desired_set.extend(desired.iter().cloned());
let to_attach = desired_set
.difference(&existing_set)
.cloned()
.collect::<Vec<_>>();
let attached = to_attach.len() as u64;
if !to_attach.is_empty() {
self.attach_insert_many(to_attach)
.execute(exec.clone())
.await?;
}
let to_detach = $detach_iter(&desired_set, &existing_set)
.cloned()
.collect::<Vec<_>>();
let detached = if to_detach.is_empty() {
0
} else {
self.detach_query_many(to_detach)
.execute(exec.clone())
.await?
.rows_affected
};
Ok(sync_action_result::<$result>(attached, detached))
}
}
};
}
fn existing_minus_desired<'a, T: Eq + Hash>(
desired: &'a HashSet<T>,
existing: &'a HashSet<T>,
) -> std::collections::hash_set::Difference<'a, T, std::collections::hash_map::RandomState> {
existing.difference(desired)
}
fn desired_intersection_existing<'a, T: Eq + Hash>(
desired: &'a HashSet<T>,
existing: &'a HashSet<T>,
) -> std::collections::hash_set::Intersection<'a, T, std::collections::hash_map::RandomState> {
desired.intersection(existing)
}
trait SyncActionResultCtor {
fn new(attached: u64, detached: u64) -> Self;
}
impl SyncActionResultCtor for SyncResult {
fn new(attached: u64, detached: u64) -> Self {
Self { attached, detached }
}
}
impl SyncActionResultCtor for ToggleResult {
fn new(attached: u64, detached: u64) -> Self {
Self { attached, detached }
}
}
fn sync_action_result<R: SyncActionResultCtor>(attached: u64, detached: u64) -> R {
R::new(attached, detached)
}
impl_sync_action!(SyncBelongsToMany, SyncResult, existing_minus_desired);
impl_sync_action!(SyncMorphToMany, SyncResult, existing_minus_desired);
impl_sync_action!(
ToggleBelongsToMany,
ToggleResult,
desired_intersection_existing
);
impl_sync_action!(
ToggleMorphToMany,
ToggleResult,
desired_intersection_existing
);
macro_rules! impl_sync_without_detaching_action {
($ty:ident) => {
impl<Pivot, ParentMeta, RelatedMeta, ParentKey, RelatedKey>
$ty<Pivot, ParentMeta, RelatedMeta, ParentKey, RelatedKey>
where
Pivot: crate::Qrafting,
ParentMeta: crate::TypeMeta + crate::Comparable + crate::Nullability,
RelatedMeta: crate::TypeMeta + crate::Comparable + crate::Nullability,
ParentKey: Clone + crate::Compatible<ParentMeta>,
RelatedKey: Clone + Eq + Hash + quex::FromRow + crate::Compatible<RelatedMeta>,
crate::BinaryType<crate::NullOf<ParentMeta>, crate::NullOf<ParentMeta>>:
crate::PredicateType,
crate::BinaryType<crate::NullOf<RelatedMeta>, crate::NullOf<RelatedMeta>>:
crate::PredicateType,
crate::UnaryType<crate::NullOf<RelatedMeta>>: crate::PredicateType,
{
pub async fn execute<E>(self, exec: E) -> quex::Result<SyncWithoutDetachingResult>
where
E: Executor + Clone,
{
let existing: Vec<RelatedKey> = self.existing_query().all(exec.clone()).await?;
let desired = self.desired_keys();
let mut existing_set = HashSet::with_capacity(existing.len());
existing_set.extend(existing);
let to_attach = desired
.iter()
.filter(|related_key| !existing_set.contains(*related_key))
.cloned()
.collect::<Vec<_>>();
let attached = to_attach.len() as u64;
if !to_attach.is_empty() {
self.attach_insert_many(to_attach).execute(exec).await?;
}
Ok(SyncWithoutDetachingResult { attached })
}
}
};
}
impl_sync_without_detaching_action!(SyncWithoutDetachingBelongsToMany);
impl_sync_without_detaching_action!(SyncWithoutDetachingMorphToMany);