#![allow(
clippy::module_inception,
clippy::type_complexity,
clippy::wrong_self_convention
)]
pub mod alias;
pub mod compat;
pub use compat::{Compatible, DefaultMeta, LowerCompatible};
pub use quex;
pub mod cte;
pub use cte::{Cte, CteDefinition, IntoCtes, WithClause, with, with_recursive};
pub mod expression;
mod ty;
pub use builder::delete_from;
pub use builder::insert_into;
pub use builder::update::update;
pub use query::Query;
pub use query::QueryOf;
pub use ty::*;
mod emitter;
mod instr;
pub(crate) mod lower;
pub(crate) mod param;
pub mod raw;
pub mod relation;
pub(crate) mod span;
pub use instr::RpnInstr;
mod dialect;
pub use dialect::{Dialect, HasDialect, MySql, Postgres, Sqlite};
mod precedence;
pub mod aggregate;
pub mod builder;
pub mod macros;
#[path = "select/mod.rs"]
pub mod query;
mod quex_exec;
use builder::{Insert, Insertable, VisitParam};
use crate::{alias::Aliased, expression::In as _, query::ColumnOf};
mod from_row;
pub use from_row::{FromPivotRow, FromPrefixedRow};
pub use quex::FromRow;
#[cfg(feature = "hash")]
mod hashed;
#[cfg(feature = "hash")]
pub use hashed::*;
mod value;
pub(crate) use value::{DbValue, ValueKind};
pub use raw::{Raw, RawQuery, RawScalar, raw};
pub use ty::{
AsCast, BigInt, Blob, Bool, Boolean, Comparable, Date, Double, Float, Int, Likeable,
Logical, MathType, NotNull, Null, NullOf, Nullability, Nullable, Numeric, Orderable,
PredicateType, Required, Text, Time, Timestamp, TypeCast, TypeMeta, UBigInt, UInt, Untyped,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum QueryVisibility {
Default,
WithDeleted,
OnlyDeleted,
}
pub trait ModelQueryPolicy<M>
where
M: Qrafting,
{
fn base_query(visibility: QueryVisibility) -> Query {
Self::apply_visibility(Query::from(M::TABLE), visibility)
}
fn apply_visibility(query: Query, _visibility: QueryVisibility) -> Query {
query
}
fn soft_delete(_query: Query) -> Option<builder::Update<M>> {
None
}
fn restore(_query: Query) -> Option<builder::Update<M>> {
None
}
}
pub struct DefaultQueryPolicy<M>(std::marker::PhantomData<fn() -> M>);
impl<M> ModelQueryPolicy<M> for DefaultQueryPolicy<M> where M: Qrafting {}
pub trait Qrafting: Sized {
type Schema;
type QueryPolicy: ModelQueryPolicy<Self>;
const FIELD_COUNT: usize;
const TABLE: &'static str;
#[doc(hidden)]
fn __qraft_query() -> QueryOf<Self>
where
Self: Sized + FromRow,
{
<Self::QueryPolicy as ModelQueryPolicy<Self>>::base_query(QueryVisibility::Default)
.typed::<Self>()
}
}
pub trait Pivot<const N: usize>: Sized {
const FIELDS: [Aliased<ColumnOf>; N];
}
pub trait RelationQuery<T> {
fn into_query(self) -> Option<QueryOf<T>>;
}
pub trait AttachInput<'a, Related, Key> {
fn into_related_keys(self, related_key: fn(&Related) -> Key) -> Vec<Key>;
}
pub enum SaveResult {
Clean,
Saved(quex::ExecResult),
}
pub struct DraftField<Model> {
name: &'static str,
index: usize,
_marker: std::marker::PhantomData<fn() -> Model>,
}
impl<Model> DraftField<Model> {
pub const fn new(name: &'static str, index: usize) -> Self {
Self {
name,
index,
_marker: std::marker::PhantomData,
}
}
pub const fn name(self) -> &'static str {
self.name
}
pub const fn index(self) -> usize {
self.index
}
}
impl<Model> Clone for DraftField<Model> {
fn clone(&self) -> Self {
*self
}
}
impl<Model> Copy for DraftField<Model> {}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct SyncResult {
pub attached: u64,
pub detached: u64,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ToggleResult {
pub attached: u64,
pub detached: u64,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct SyncWithoutDetachingResult {
pub attached: u64,
}
pub trait SaveDraft {
type Model: Qrafting;
fn is_dirty(&self) -> bool;
fn save_update(&self) -> Option<builder::Update<Self::Model>>;
fn save<E>(self, exec: E) -> impl std::future::Future<Output = quex::Result<SaveResult>>
where
Self: Sized,
E: quex::Executor,
{
async move {
match self.save_update() {
Some(update) => update.execute(exec).await.map(SaveResult::Saved),
None => Ok(SaveResult::Clean),
}
}
}
}
pub trait Draftable: Sized {
type Draft<'a>: SaveDraft<Model = Self>
where
Self: 'a;
fn draft(&mut self) -> Self::Draft<'_>;
fn draft_mark(&mut self, field: DraftField<Self>) -> Self::Draft<'_>;
fn draft_mark_many(&mut self, fields: &[DraftField<Self>]) -> Self::Draft<'_>;
#[doc(hidden)]
fn __qraft_draft_with(&mut self, field: usize) -> Self::Draft<'_>;
#[doc(hidden)]
fn __qraft_draft_for_field(&mut self, field: &'static str) -> Self::Draft<'_>;
#[doc(hidden)]
fn __qraft_draft_for_fields(&mut self, fields: &[&'static str]) -> Self::Draft<'_>;
}
pub trait ModelKey {
type Key: Clone;
#[doc(hidden)]
fn __qraft_key(&self) -> &Self::Key;
#[doc(hidden)]
fn __qraft_key_column() -> &'static str;
}
pub trait FindsByKey: Sized {
type Key;
fn find(id: &Self::Key) -> QueryOf<Self>;
}
pub trait MorphName {
fn morph_name() -> &'static str;
}
pub trait RelationForeignKey<Key> {
fn from_relation_key(key: Key) -> Self;
}
pub trait RelationForeignKeyRef<Key: ?Sized> {
fn from_relation_key_ref(key: &Key) -> Self;
}
impl<Key> RelationForeignKey<Key> for Key {
fn from_relation_key(key: Key) -> Self {
key
}
}
impl<Key> RelationForeignKey<Key> for Option<Key> {
fn from_relation_key(key: Key) -> Self {
Some(key)
}
}
impl<Key: Clone, T> RelationForeignKeyRef<Key> for T
where
T: RelationForeignKey<Key>,
{
fn from_relation_key_ref(key: &Key) -> Self {
T::from_relation_key(key.clone())
}
}
pub trait RelationMorphType {
fn from_relation_morph_type(value: &'static str) -> Self;
}
impl RelationMorphType for String {
fn from_relation_morph_type(value: &'static str) -> Self {
value.to_owned()
}
}
impl RelationMorphType for Option<String> {
fn from_relation_morph_type(value: &'static str) -> Self {
Some(value.to_owned())
}
}
pub struct InjectMorphValues<V, Key> {
values: V,
id_field: &'static str,
type_field: &'static str,
key: Key,
morph_type: &'static str,
}
struct SkipFieldVisitor<'a, V> {
inner: &'a mut V,
fields: &'a [&'static str],
}
impl<'a, 'v, V> VisitParam<'v> for SkipFieldVisitor<'a, V>
where
V: VisitParam<'v>,
{
fn param(&mut self, field: &'static str, param: impl quex::Encode) {
if self.fields.contains(&field) {
return;
}
self.inner.param(field, param)
}
fn param_typed<T>(&mut self, field: &'static str, value: &'v (impl Compatible<T> + ?Sized))
where
T: TypeMeta,
{
if self.fields.contains(&field) {
return;
}
self.inner.param_typed::<T>(field, value)
}
}
impl<M, V, Key> Insertable<M> for InjectMorphValues<V, Key>
where
M: Qrafting,
V: Insertable<M>,
Key: DefaultMeta + Compatible<<Key as DefaultMeta>::Meta>,
{
fn values<'v>(&'v self, visitor: &mut impl VisitParam<'v>) {
visitor.param_typed::<<Key as DefaultMeta>::Meta>(self.id_field, &self.key);
visitor.param(self.type_field, self.morph_type);
let mut visitor = SkipFieldVisitor {
inner: visitor,
fields: &[self.id_field, self.type_field],
};
self.values.values(&mut visitor)
}
}
impl<'a, Related, Key> AttachInput<'a, Related, Key> for &'a Related {
fn into_related_keys(self, related_key: fn(&Related) -> Key) -> Vec<Key> {
vec![related_key(self)]
}
}
impl<'a, Related, Key> AttachInput<'a, Related, Key> for Vec<&'a Related> {
fn into_related_keys(self, related_key: fn(&Related) -> Key) -> Vec<Key> {
self.into_iter().map(related_key).collect()
}
}
impl<'a, Related, Key> AttachInput<'a, Related, Key> for &'a [&'a Related] {
fn into_related_keys(self, related_key: fn(&Related) -> Key) -> Vec<Key> {
self.iter().copied().map(related_key).collect()
}
}
impl<'a, Related, Key, const N: usize> AttachInput<'a, Related, Key> for [&'a Related; N] {
fn into_related_keys(self, related_key: fn(&Related) -> Key) -> Vec<Key> {
self.into_iter().map(related_key).collect()
}
}
macro_rules! impl_attach_input_tuple {
($($T:ident),+) => {
impl<'a, Related, Key, $($T),+> AttachInput<'a, Related, Key> for ($($T,)+)
where
Related: 'a,
$($T: ::std::borrow::Borrow<Related>,)+
{
fn into_related_keys(self, related_key: fn(&Related) -> Key) -> Vec<Key> {
#[allow(non_snake_case)]
let ($($T,)+) = self;
vec![$(related_key($T.borrow()),)+]
}
}
};
}
impl_for_all_tuples!(impl_attach_input_tuple);
pub type ManyToManyValues<Pivot, ParentMeta, RelatedMeta, ParentKey, RelatedKey> = (
expression::Binary<expression::op::Eq, expression::Column<Pivot, ParentMeta>, ParentKey>,
expression::Binary<expression::op::Eq, expression::Column<Pivot, RelatedMeta>, RelatedKey>,
);
pub type MorphToManyValues<Pivot, ParentMeta, RelatedMeta, ParentKey, RelatedKey> = (
expression::Binary<expression::op::Eq, expression::Column<Pivot, ParentMeta>, ParentKey>,
expression::Binary<expression::op::Eq, expression::Column<Pivot, Text>, &'static str>,
expression::Binary<expression::op::Eq, expression::Column<Pivot, RelatedMeta>, RelatedKey>,
);
pub(crate) fn build_many_to_many_values<
Pivot: Qrafting,
ParentMeta: TypeMeta + Comparable + Nullability,
RelatedMeta: TypeMeta + Comparable + Nullability,
ParentKey,
RelatedKey,
>(
parent_column: expression::Column<Pivot, ParentMeta>,
related_column: expression::Column<Pivot, RelatedMeta>,
parent_key: &ParentKey,
related_keys: Vec<RelatedKey>,
) -> Vec<ManyToManyValues<Pivot, ParentMeta, RelatedMeta, ParentKey, RelatedKey>>
where
ParentKey: Clone + Compatible<ParentMeta>,
RelatedKey: Compatible<RelatedMeta>,
BinaryType<NullOf<ParentMeta>, NullOf<ParentMeta>>: PredicateType,
BinaryType<NullOf<RelatedMeta>, NullOf<RelatedMeta>>: PredicateType,
{
let mut values = Vec::with_capacity(related_keys.len());
for related_key in related_keys {
values.push((
parent_column.eq(parent_key.clone()),
related_column.eq(related_key),
));
}
values
}
pub(crate) fn build_morph_to_many_values<
Pivot: Qrafting,
ParentMeta: TypeMeta + Comparable + Nullability,
RelatedMeta: TypeMeta + Comparable + Nullability,
ParentKey,
RelatedKey,
>(
spec: MorphToManyValueSpec<Pivot, ParentMeta, RelatedMeta, ParentKey, RelatedKey>,
) -> Vec<MorphToManyValues<Pivot, ParentMeta, RelatedMeta, ParentKey, RelatedKey>>
where
ParentKey: Clone + Compatible<ParentMeta>,
RelatedKey: Compatible<RelatedMeta>,
BinaryType<NullOf<ParentMeta>, NullOf<ParentMeta>>: PredicateType,
BinaryType<NullOf<RelatedMeta>, NullOf<RelatedMeta>>: PredicateType,
{
let MorphToManyValueSpec {
parent_column,
type_column,
related_column,
parent_key,
morph_type,
related_keys,
} = spec;
let mut values = Vec::with_capacity(related_keys.len());
for related_key in related_keys {
values.push((
parent_column.eq(parent_key.clone()),
type_column.eq(morph_type),
related_column.eq(related_key),
));
}
values
}
pub(crate) struct MorphToManyValueSpec<
Pivot: Qrafting,
ParentMeta: TypeMeta + Comparable + Nullability,
RelatedMeta: TypeMeta + Comparable + Nullability,
ParentKey,
RelatedKey,
> {
pub(crate) parent_column: expression::Column<Pivot, ParentMeta>,
pub(crate) type_column: expression::Column<Pivot, Text>,
pub(crate) related_column: expression::Column<Pivot, RelatedMeta>,
pub(crate) parent_key: ParentKey,
pub(crate) morph_type: &'static str,
pub(crate) related_keys: Vec<RelatedKey>,
}
pub struct AttachBelongsToMany<
Pivot,
ParentMeta: TypeMeta,
RelatedMeta: TypeMeta,
ParentKey,
RelatedKey,
> {
pivot_table: query::Table<Pivot>,
parent_column: expression::Column<Pivot, ParentMeta>,
related_column: expression::Column<Pivot, RelatedMeta>,
parent_key: ParentKey,
related_keys: Vec<RelatedKey>,
}
pub struct AttachMorphToMany<
Pivot,
ParentMeta: TypeMeta,
RelatedMeta: TypeMeta,
ParentKey,
RelatedKey,
> {
pivot_table: query::Table<Pivot>,
parent_column: expression::Column<Pivot, ParentMeta>,
type_column: expression::Column<Pivot, Text>,
related_column: expression::Column<Pivot, RelatedMeta>,
parent_key: ParentKey,
morph_type: &'static str,
related_keys: Vec<RelatedKey>,
}
pub struct SyncBelongsToMany<
Pivot,
ParentMeta: TypeMeta,
RelatedMeta: TypeMeta,
ParentKey,
RelatedKey,
> {
pivot_table: query::Table<Pivot>,
parent_column: expression::Column<Pivot, ParentMeta>,
related_column: expression::Column<Pivot, RelatedMeta>,
parent_key: ParentKey,
related_keys: Vec<RelatedKey>,
}
pub struct SyncMorphToMany<
Pivot,
ParentMeta: TypeMeta,
RelatedMeta: TypeMeta,
ParentKey,
RelatedKey,
> {
pivot_table: query::Table<Pivot>,
parent_column: expression::Column<Pivot, ParentMeta>,
type_column: expression::Column<Pivot, Text>,
related_column: expression::Column<Pivot, RelatedMeta>,
parent_key: ParentKey,
morph_type: &'static str,
related_keys: Vec<RelatedKey>,
}
pub struct ToggleBelongsToMany<
Pivot,
ParentMeta: TypeMeta,
RelatedMeta: TypeMeta,
ParentKey,
RelatedKey,
> {
pivot_table: query::Table<Pivot>,
parent_column: expression::Column<Pivot, ParentMeta>,
related_column: expression::Column<Pivot, RelatedMeta>,
parent_key: ParentKey,
related_keys: Vec<RelatedKey>,
}
pub struct ToggleMorphToMany<
Pivot,
ParentMeta: TypeMeta,
RelatedMeta: TypeMeta,
ParentKey,
RelatedKey,
> {
pivot_table: query::Table<Pivot>,
parent_column: expression::Column<Pivot, ParentMeta>,
type_column: expression::Column<Pivot, Text>,
related_column: expression::Column<Pivot, RelatedMeta>,
parent_key: ParentKey,
morph_type: &'static str,
related_keys: Vec<RelatedKey>,
}
pub struct SyncWithoutDetachingBelongsToMany<
Pivot,
ParentMeta: TypeMeta,
RelatedMeta: TypeMeta,
ParentKey,
RelatedKey,
> {
pivot_table: query::Table<Pivot>,
parent_column: expression::Column<Pivot, ParentMeta>,
related_column: expression::Column<Pivot, RelatedMeta>,
parent_key: ParentKey,
related_keys: Vec<RelatedKey>,
}
pub struct SyncWithoutDetachingMorphToMany<
Pivot,
ParentMeta: TypeMeta,
RelatedMeta: TypeMeta,
ParentKey,
RelatedKey,
> {
pivot_table: query::Table<Pivot>,
parent_column: expression::Column<Pivot, ParentMeta>,
type_column: expression::Column<Pivot, Text>,
related_column: expression::Column<Pivot, RelatedMeta>,
parent_key: ParentKey,
morph_type: &'static str,
related_keys: Vec<RelatedKey>,
}
impl<
Pivot,
ParentMeta: TypeMeta + Comparable + Nullability,
RelatedMeta: TypeMeta + Comparable + Nullability,
ParentKey,
RelatedKey,
> AttachBelongsToMany<Pivot, ParentMeta, RelatedMeta, ParentKey, RelatedKey>
{
pub fn related_keys(&self) -> &[RelatedKey] {
&self.related_keys
}
pub fn insert_for(&self, related_key: &RelatedKey) -> builder::Insert<Pivot>
where
Pivot: Qrafting,
ParentKey: Clone + Compatible<ParentMeta>,
RelatedKey: Clone + Compatible<RelatedMeta>,
BinaryType<NullOf<ParentMeta>, NullOf<ParentMeta>>: PredicateType,
BinaryType<NullOf<RelatedMeta>, NullOf<RelatedMeta>>: PredicateType,
{
insert_into(self.pivot_table)
.values((
self.parent_column.eq(self.parent_key.clone()),
self.related_column.eq(related_key.clone()),
))
.no_returning()
}
pub fn insert_many(&self, related_keys: Vec<RelatedKey>) -> builder::Insert<Pivot>
where
Pivot: Qrafting,
ParentKey: Clone + Compatible<ParentMeta>,
RelatedKey: Clone + Compatible<RelatedMeta>,
BinaryType<NullOf<ParentMeta>, NullOf<ParentMeta>>: PredicateType,
BinaryType<NullOf<RelatedMeta>, NullOf<RelatedMeta>>: PredicateType,
{
let values = build_many_to_many_values(
self.parent_column,
self.related_column,
&self.parent_key,
related_keys,
);
insert_into(self.pivot_table).values(values).no_returning()
}
}
impl<
Pivot,
ParentMeta: TypeMeta + Comparable + Nullability,
RelatedMeta: TypeMeta + Comparable + Nullability,
ParentKey,
RelatedKey,
> AttachMorphToMany<Pivot, ParentMeta, RelatedMeta, ParentKey, RelatedKey>
{
pub fn related_keys(&self) -> &[RelatedKey] {
&self.related_keys
}
pub fn insert_for(&self, related_key: &RelatedKey) -> builder::Insert<Pivot>
where
Pivot: Qrafting,
ParentKey: Clone + Compatible<ParentMeta>,
RelatedKey: Clone + Compatible<RelatedMeta>,
BinaryType<NullOf<ParentMeta>, NullOf<ParentMeta>>: PredicateType,
BinaryType<NullOf<RelatedMeta>, NullOf<RelatedMeta>>: PredicateType,
{
insert_into(self.pivot_table)
.values((
self.parent_column.eq(self.parent_key.clone()),
self.type_column.eq(self.morph_type),
self.related_column.eq(related_key.clone()),
))
.no_returning()
}
pub fn insert_many(&self, related_keys: Vec<RelatedKey>) -> builder::Insert<Pivot>
where
Pivot: Qrafting,
ParentKey: Clone + Compatible<ParentMeta>,
RelatedKey: Clone + Compatible<RelatedMeta>,
BinaryType<NullOf<ParentMeta>, NullOf<ParentMeta>>: PredicateType,
BinaryType<NullOf<RelatedMeta>, NullOf<RelatedMeta>>: PredicateType,
{
let values = build_morph_to_many_values(MorphToManyValueSpec {
parent_column: self.parent_column,
type_column: self.type_column,
related_column: self.related_column,
parent_key: self.parent_key.clone(),
morph_type: self.morph_type,
related_keys,
});
insert_into(self.pivot_table).values(values).no_returning()
}
}
impl<
Pivot: Qrafting,
ParentMeta: Comparable + Nullability,
RelatedMeta: Comparable + Nullability,
ParentKey,
RelatedKey,
> SyncBelongsToMany<Pivot, ParentMeta, RelatedMeta, ParentKey, RelatedKey>
where
BinaryType<NullOf<ParentMeta>, NullOf<ParentMeta>>: PredicateType,
BinaryType<NullOf<RelatedMeta>, NullOf<RelatedMeta>>: PredicateType,
{
pub fn existing_query(&self) -> QueryOf<RelatedKey>
where
Pivot: Qrafting,
RelatedKey: FromRow + LowerCompatible<RelatedMeta>,
ParentKey: Clone + LowerCompatible<ParentMeta>,
{
Query::from(self.pivot_table)
.select(self.related_column)
.filter(self.parent_column.eq(self.parent_key.clone()))
.typed::<RelatedKey>()
}
pub fn desired_keys(&self) -> &[RelatedKey] {
&self.related_keys
}
pub fn attach_insert(&self, related_key: &RelatedKey) -> builder::Insert<Pivot>
where
ParentKey: Clone + Compatible<ParentMeta>,
RelatedKey: Clone + Compatible<RelatedMeta>,
{
insert_into(self.pivot_table)
.values((
self.parent_column.eq(self.parent_key.clone()),
self.related_column.eq(related_key.clone()),
))
.no_returning()
}
pub fn attach_insert_many(&self, related_keys: Vec<RelatedKey>) -> builder::Insert<Pivot>
where
ParentKey: Clone + Compatible<ParentMeta>,
RelatedKey: Clone + Compatible<RelatedMeta>,
{
let values = build_many_to_many_values(
self.parent_column,
self.related_column,
&self.parent_key,
related_keys,
);
insert_into(self.pivot_table).values(values).no_returning()
}
pub fn detach_query(&self, related_key: &RelatedKey) -> builder::Delete<Pivot>
where
ParentKey: Clone + LowerCompatible<ParentMeta>,
RelatedKey: Clone + LowerCompatible<RelatedMeta>,
{
builder::delete::delete_from(self.pivot_table)
.filter(self.parent_column.eq(self.parent_key.clone()))
.filter(self.related_column.eq(related_key.clone()))
}
pub fn detach_query_many(&self, related_keys: Vec<RelatedKey>) -> builder::Delete<Pivot>
where
ParentKey: Clone + LowerCompatible<ParentMeta>,
RelatedKey: Clone + LowerCompatible<RelatedMeta>,
UnaryType<NullOf<RelatedMeta>>: PredicateType,
{
builder::delete::delete_from(self.pivot_table)
.filter(self.parent_column.eq(self.parent_key.clone()))
.filter(self.related_column.in_(related_keys))
}
}
impl<
Pivot: Qrafting,
ParentMeta: Comparable + Nullability,
RelatedMeta: Comparable + Nullability,
ParentKey,
RelatedKey,
> SyncMorphToMany<Pivot, ParentMeta, RelatedMeta, ParentKey, RelatedKey>
where
BinaryType<NullOf<ParentMeta>, NullOf<ParentMeta>>: PredicateType,
BinaryType<NullOf<RelatedMeta>, NullOf<RelatedMeta>>: PredicateType,
{
pub fn existing_query(&self) -> QueryOf<RelatedKey>
where
Pivot: Qrafting,
RelatedKey: FromRow + LowerCompatible<RelatedMeta>,
ParentKey: Clone + LowerCompatible<ParentMeta>,
{
Query::from(self.pivot_table)
.select(self.related_column)
.filter(self.parent_column.eq(self.parent_key.clone()))
.filter(self.type_column.eq(self.morph_type))
.typed::<RelatedKey>()
}
pub fn desired_keys(&self) -> &[RelatedKey] {
&self.related_keys
}
pub fn attach_insert(&self, related_key: &RelatedKey) -> builder::Insert<Pivot>
where
ParentKey: Clone + Compatible<ParentMeta>,
RelatedKey: Clone + Compatible<RelatedMeta>,
{
insert_into(self.pivot_table)
.values((
self.parent_column.eq(self.parent_key.clone()),
self.type_column.eq(self.morph_type),
self.related_column.eq(related_key.clone()),
))
.no_returning()
}
pub fn attach_insert_many(&self, related_keys: Vec<RelatedKey>) -> builder::Insert<Pivot>
where
ParentKey: Clone + Compatible<ParentMeta>,
RelatedKey: Clone + Compatible<RelatedMeta>,
{
let values = build_morph_to_many_values(MorphToManyValueSpec {
parent_column: self.parent_column,
type_column: self.type_column,
related_column: self.related_column,
parent_key: self.parent_key.clone(),
morph_type: self.morph_type,
related_keys,
});
insert_into(self.pivot_table).values(values).no_returning()
}
pub fn detach_query(&self, related_key: &RelatedKey) -> builder::Delete<Pivot>
where
ParentKey: Clone + LowerCompatible<ParentMeta>,
RelatedKey: Clone + LowerCompatible<RelatedMeta>,
{
builder::delete::delete_from(self.pivot_table)
.filter(self.parent_column.eq(self.parent_key.clone()))
.filter(self.type_column.eq(self.morph_type))
.filter(self.related_column.eq(related_key.clone()))
}
pub fn detach_query_many(&self, related_keys: Vec<RelatedKey>) -> builder::Delete<Pivot>
where
ParentKey: Clone + LowerCompatible<ParentMeta>,
RelatedKey: Clone + LowerCompatible<RelatedMeta>,
UnaryType<NullOf<RelatedMeta>>: PredicateType,
{
builder::delete::delete_from(self.pivot_table)
.filter(self.parent_column.eq(self.parent_key.clone()))
.filter(self.type_column.eq(self.morph_type))
.filter(self.related_column.in_(related_keys))
}
}
macro_rules! impl_many_to_many_action_helpers {
($name:ident) => {
impl<
Pivot: Qrafting,
ParentMeta: Comparable + Nullability,
RelatedMeta: Comparable + Nullability,
ParentKey,
RelatedKey,
> $name<Pivot, ParentMeta, RelatedMeta, ParentKey, RelatedKey>
where
BinaryType<NullOf<ParentMeta>, NullOf<ParentMeta>>: PredicateType,
BinaryType<NullOf<RelatedMeta>, NullOf<RelatedMeta>>: PredicateType,
{
pub fn existing_query(&self) -> QueryOf<RelatedKey>
where
Pivot: Qrafting,
RelatedKey: FromRow + LowerCompatible<RelatedMeta>,
ParentKey: Clone + LowerCompatible<ParentMeta>,
{
Query::from(self.pivot_table)
.select(self.related_column)
.filter(self.parent_column.eq(self.parent_key.clone()))
.typed::<RelatedKey>()
}
pub fn desired_keys(&self) -> &[RelatedKey] {
&self.related_keys
}
pub fn attach_insert(&self, related_key: &RelatedKey) -> builder::Insert<Pivot>
where
ParentKey: Clone + Compatible<ParentMeta>,
RelatedKey: Clone + Compatible<RelatedMeta>,
{
insert_into(self.pivot_table)
.values((
self.parent_column.eq(self.parent_key.clone()),
self.related_column.eq(related_key.clone()),
))
.no_returning()
}
pub fn detach_query(&self, related_key: &RelatedKey) -> builder::Delete<Pivot>
where
ParentKey: Clone + LowerCompatible<ParentMeta>,
RelatedKey: Clone + LowerCompatible<RelatedMeta>,
{
builder::delete::delete_from(self.pivot_table)
.filter(self.parent_column.eq(self.parent_key.clone()))
.filter(self.related_column.eq(related_key.clone()))
}
pub fn attach_insert_many(
&self,
related_keys: Vec<RelatedKey>,
) -> builder::Insert<Pivot>
where
ParentKey: Clone + Compatible<ParentMeta>,
RelatedKey: Clone + Compatible<RelatedMeta>,
{
let values = build_many_to_many_values(
self.parent_column,
self.related_column,
&self.parent_key,
related_keys,
);
insert_into(self.pivot_table).values(values).no_returning()
}
pub fn detach_query_many(&self, related_keys: Vec<RelatedKey>) -> builder::Delete<Pivot>
where
ParentKey: Clone + LowerCompatible<ParentMeta>,
RelatedKey: Clone + LowerCompatible<RelatedMeta>,
UnaryType<NullOf<RelatedMeta>>: PredicateType,
{
builder::delete::delete_from(self.pivot_table)
.filter(self.parent_column.eq(self.parent_key.clone()))
.filter(self.related_column.in_(related_keys))
}
}
};
}
impl_many_to_many_action_helpers!(ToggleBelongsToMany);
impl_many_to_many_action_helpers!(SyncWithoutDetachingBelongsToMany);
macro_rules! impl_morph_to_many_action_helpers {
($name:ident) => {
impl<
Pivot: Qrafting,
ParentMeta: Comparable + Nullability,
RelatedMeta: Comparable + Nullability,
ParentKey,
RelatedKey,
> $name<Pivot, ParentMeta, RelatedMeta, ParentKey, RelatedKey>
where
BinaryType<NullOf<ParentMeta>, NullOf<ParentMeta>>: PredicateType,
BinaryType<NullOf<RelatedMeta>, NullOf<RelatedMeta>>: PredicateType,
{
pub fn existing_query(&self) -> QueryOf<RelatedKey>
where
Pivot: Qrafting,
RelatedKey: FromRow + LowerCompatible<RelatedMeta>,
ParentKey: Clone + LowerCompatible<ParentMeta>,
{
Query::from(self.pivot_table)
.select(self.related_column)
.filter(self.parent_column.eq(self.parent_key.clone()))
.filter(self.type_column.eq(self.morph_type))
.typed::<RelatedKey>()
}
pub fn desired_keys(&self) -> &[RelatedKey] {
&self.related_keys
}
pub fn attach_insert(&self, related_key: &RelatedKey) -> builder::Insert<Pivot>
where
ParentKey: Clone + Compatible<ParentMeta>,
RelatedKey: Clone + Compatible<RelatedMeta>,
{
insert_into(self.pivot_table)
.values((
self.parent_column.eq(self.parent_key.clone()),
self.type_column.eq(self.morph_type),
self.related_column.eq(related_key.clone()),
))
.no_returning()
}
pub fn detach_query(&self, related_key: &RelatedKey) -> builder::Delete<Pivot>
where
ParentKey: Clone + LowerCompatible<ParentMeta>,
RelatedKey: Clone + LowerCompatible<RelatedMeta>,
{
builder::delete::delete_from(self.pivot_table)
.filter(self.parent_column.eq(self.parent_key.clone()))
.filter(self.type_column.eq(self.morph_type))
.filter(self.related_column.eq(related_key.clone()))
}
pub fn attach_insert_many(
&self,
related_keys: Vec<RelatedKey>,
) -> builder::Insert<Pivot>
where
ParentKey: Clone + Compatible<ParentMeta>,
RelatedKey: Clone + Compatible<RelatedMeta>,
{
let values = build_morph_to_many_values(MorphToManyValueSpec {
parent_column: self.parent_column,
type_column: self.type_column,
related_column: self.related_column,
parent_key: self.parent_key.clone(),
morph_type: self.morph_type,
related_keys,
});
insert_into(self.pivot_table).values(values).no_returning()
}
pub fn detach_query_many(&self, related_keys: Vec<RelatedKey>) -> builder::Delete<Pivot>
where
ParentKey: Clone + LowerCompatible<ParentMeta>,
RelatedKey: Clone + LowerCompatible<RelatedMeta>,
UnaryType<NullOf<RelatedMeta>>: PredicateType,
{
builder::delete::delete_from(self.pivot_table)
.filter(self.parent_column.eq(self.parent_key.clone()))
.filter(self.type_column.eq(self.morph_type))
.filter(self.related_column.in_(related_keys))
}
}
};
}
impl_morph_to_many_action_helpers!(ToggleMorphToMany);
impl_morph_to_many_action_helpers!(SyncWithoutDetachingMorphToMany);
pub type BoxDynError = Box<dyn ::std::error::Error + 'static + Send + Sync>;
pub struct Unset;
pub struct Set;
#[cfg(test)]
mod tests {
use crate::{BigInt, FromRow, Nullable, Qrafting, Text, expression::Column, query::Table};
#[allow(dead_code)]
#[derive(Debug)]
pub struct User {
pub id: i64,
pub name: Option<String>,
pub username: String,
}
impl FromRow for User {
fn from_row(row: &crate::quex::Row) -> crate::quex::Result<Self> {
Ok(Self {
id: row.get("id")?,
name: row.get("name")?,
username: row.get("username")?,
})
}
}
impl Qrafting for User {
type Schema = ();
type QueryPolicy = crate::DefaultQueryPolicy<Self>;
const FIELD_COUNT: usize = 3;
const TABLE: &'static str = "users";
}
#[allow(non_upper_case_globals)]
pub const table: Table<User> = Table::new("users");
#[allow(non_upper_case_globals)]
pub const id: Column<User, BigInt> = Column::new("id");
#[allow(non_upper_case_globals)]
pub const name: Column<User, Nullable<Text>> = Column::new("name");
#[allow(non_upper_case_globals)]
pub const username: Column<User, Text> = Column::new("username");
}