|
@@ -5,115 +5,68 @@ use crate::{
|
|
prelude::Queryable,
|
|
prelude::Queryable,
|
|
query::{AssocInterface, QueryPart},
|
|
query::{AssocInterface, QueryPart},
|
|
schema::{
|
|
schema::{
|
|
- datum::{Datum, DatumList, DatumListRef, DatumVisitor},
|
|
|
|
|
|
+ datum::{
|
|
|
|
+ Datum, DatumList, DatumListRef, DatumVisitor, QueryEquivalent, QueryEquivalentList,
|
|
|
|
+ },
|
|
entity::{Entity, EntityPart, EntityPartList, EntityPartVisitor},
|
|
entity::{Entity, EntityPart, EntityPartList, EntityPartVisitor},
|
|
- DatumDiscriminator, IDMap, IDWrap,
|
|
|
|
|
|
+ DatumDiscriminator, IDWrap,
|
|
},
|
|
},
|
|
};
|
|
};
|
|
|
|
|
|
use super::Query;
|
|
use super::Query;
|
|
|
|
|
|
-/// Concrete implementation of Queryable for an IDMap
|
|
|
|
-pub(crate) struct MapQueryable<'a, E: Entity> {
|
|
|
|
- map: &'a IDMap<E>,
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-impl<'a, E: Entity> MapQueryable<'a, E> {
|
|
|
|
- pub fn new(map: &'a IDMap<E>) -> Self {
|
|
|
|
- Self { map }
|
|
|
|
- }
|
|
|
|
|
|
+/// Filter on a Datum
|
|
|
|
+pub(crate) struct WithComponent<WEP: EntityPart, Parent: Queryable, QE: QueryEquivalent<WEP::Datum>>
|
|
|
|
+{
|
|
|
|
+ // datum: &'a WEP::Datum,
|
|
|
|
+ datum: QE,
|
|
|
|
+ parent: Parent,
|
|
|
|
+ _ghost: std::marker::PhantomData<WEP>,
|
|
}
|
|
}
|
|
|
|
|
|
-impl<'a, E: Entity> Queryable for MapQueryable<'a, E> {
|
|
|
|
- type EntityOutput = E;
|
|
|
|
- type OutputContainer = Vec<IDWrap<E>>;
|
|
|
|
- type StaticVersion = MapQueryable<'static, E>;
|
|
|
|
-
|
|
|
|
- fn build(&self) -> Query {
|
|
|
|
- Query::new()
|
|
|
|
- .attach(QueryPart::Root, "SELECT DISTINCT".into())
|
|
|
|
- .attach(QueryPart::Columns, "*".into())
|
|
|
|
- .attach(QueryPart::From, format!("`{}`", E::entity_name()))
|
|
|
|
- }
|
|
|
|
- fn bind(&self, _stmt: &mut StatementContext, _index: &mut i32) {}
|
|
|
|
-
|
|
|
|
- fn conn(&self) -> &Connection {
|
|
|
|
- self.map.conn()
|
|
|
|
|
|
+impl<WEP: EntityPart, Parent: Queryable, QE: QueryEquivalent<WEP::Datum>>
|
|
|
|
+ WithComponent<WEP, Parent, QE>
|
|
|
|
+{
|
|
|
|
+ pub fn new(parent: Parent, _part: WEP, datum: QE) -> Self {
|
|
|
|
+ Self {
|
|
|
|
+ datum,
|
|
|
|
+ parent,
|
|
|
|
+ _ghost: Default::default(),
|
|
|
|
+ }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
-/// Concrete implementation of Queryable for an IDMap
|
|
|
|
-pub(crate) struct AssocQueryable<'a, AI: AssocInterface> {
|
|
|
|
- assoc: &'a AI,
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-impl<'a, AI: AssocInterface> AssocQueryable<'a, AI> {
|
|
|
|
- pub fn new(assoc: &'a AI) -> Self {
|
|
|
|
- Self { assoc }
|
|
|
|
- }
|
|
|
|
|
|
+/// this workaround is needed because we very explicitly would like EL to not be a
|
|
|
|
+/// 'static-restricted type, and it doesn't matter for the purposes of actually distinguishing
|
|
|
|
+/// between queries.
|
|
|
|
+pub(crate) struct CanonicalWithComponent<WEP: EntityPart, Parent: Queryable> {
|
|
|
|
+ _ghost: std::marker::PhantomData<(WEP, Parent)>,
|
|
}
|
|
}
|
|
|
|
|
|
-impl<'a, AI: AssocInterface> Queryable for AssocQueryable<'a, AI> {
|
|
|
|
- type EntityOutput = AI::RemoteEntity;
|
|
|
|
- type OutputContainer = Vec<IDWrap<AI::RemoteEntity>>;
|
|
|
|
- type StaticVersion = AssocQueryable<'static, AI>;
|
|
|
|
|
|
+impl<WEP: EntityPart, Parent: Queryable + 'static> Queryable
|
|
|
|
+ for CanonicalWithComponent<WEP, Parent>
|
|
|
|
+{
|
|
|
|
+ type EntityOutput = WEP::Entity;
|
|
|
|
+ type OutputContainer = Option<IDWrap<WEP::Entity>>;
|
|
|
|
+ type StaticVersion = Self;
|
|
|
|
|
|
fn build(&self) -> Query {
|
|
fn build(&self) -> Query {
|
|
- let anames = super::AssocNames::collect(self.assoc).unwrap();
|
|
|
|
- let assoc_name = anames.assoc_name();
|
|
|
|
- Query::new()
|
|
|
|
- .attach(QueryPart::Root, "SELECT DISTINCT".into())
|
|
|
|
- .attach(QueryPart::Columns, format!("`{}`.*", anames.remote_name))
|
|
|
|
- .attach(QueryPart::From, format!("`{}`", assoc_name))
|
|
|
|
- .attach(
|
|
|
|
- QueryPart::Join,
|
|
|
|
- format!(
|
|
|
|
- "`{}` ON `{}`.`id` = `{}`.`{}`",
|
|
|
|
- anames.remote_name, anames.remote_name, assoc_name, anames.remote_field
|
|
|
|
- ),
|
|
|
|
- )
|
|
|
|
- .attach(
|
|
|
|
- QueryPart::Where,
|
|
|
|
- format!("`{}`.`{}` = ?", assoc_name, anames.local_field),
|
|
|
|
- )
|
|
|
|
|
|
+ unreachable!()
|
|
}
|
|
}
|
|
- fn bind(&self, ctx: &mut StatementContext, index: &mut i32) {
|
|
|
|
- let adata = self
|
|
|
|
- .assoc
|
|
|
|
- .get_data()
|
|
|
|
- .expect("binding query for assoc with no data");
|
|
|
|
-
|
|
|
|
- ctx.bind(*index, adata.local_id)
|
|
|
|
- .expect("couldn't bind assoc id");
|
|
|
|
- *index += 1;
|
|
|
|
|
|
+ fn bind(&self, _stmt: &mut StatementContext, _index: &mut i32) {
|
|
|
|
+ unreachable!()
|
|
}
|
|
}
|
|
-
|
|
|
|
fn conn(&self) -> &Connection {
|
|
fn conn(&self) -> &Connection {
|
|
- &self.assoc.get_data().unwrap().conn
|
|
|
|
|
|
+ unreachable!()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
-/// Filter on a Datum
|
|
|
|
-pub(crate) struct WithComponent<'a, WEP: EntityPart, Parent: Queryable> {
|
|
|
|
- datum: &'a WEP::Datum,
|
|
|
|
- parent: Parent,
|
|
|
|
- _ghost: std::marker::PhantomData<WEP>,
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-impl<'a, WEP: EntityPart, Parent: Queryable> WithComponent<'a, WEP, Parent> {
|
|
|
|
- pub fn new(parent: Parent, _part: WEP, datum: &'a WEP::Datum) -> Self {
|
|
|
|
- Self {
|
|
|
|
- datum,
|
|
|
|
- parent,
|
|
|
|
- _ghost: Default::default(),
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-impl<'a, WEP: EntityPart, Parent: Queryable> Queryable for WithComponent<'a, WEP, Parent> {
|
|
|
|
|
|
+impl<WEP: EntityPart, Parent: Queryable, QE: QueryEquivalent<WEP::Datum>> Queryable
|
|
|
|
+ for WithComponent<WEP, Parent, QE>
|
|
|
|
+{
|
|
type EntityOutput = WEP::Entity;
|
|
type EntityOutput = WEP::Entity;
|
|
type OutputContainer = Parent::OutputContainer;
|
|
type OutputContainer = Parent::OutputContainer;
|
|
- type StaticVersion = WithComponent<'static, WEP, Parent::StaticVersion>;
|
|
|
|
|
|
+ type StaticVersion = CanonicalWithComponent<WEP, Parent::StaticVersion>;
|
|
|
|
|
|
fn build(&self) -> Query {
|
|
fn build(&self) -> Query {
|
|
self.parent.build().attach(
|
|
self.parent.build().attach(
|
|
@@ -137,24 +90,68 @@ impl<'a, WEP: EntityPart, Parent: Queryable> Queryable for WithComponent<'a, WEP
|
|
}
|
|
}
|
|
|
|
|
|
/// Filter on the unique index
|
|
/// Filter on the unique index
|
|
-pub(crate) struct UniqueComponent<'a, E: Entity, Parent: Queryable> {
|
|
|
|
- datum: <<E::Uniques as EntityPartList>::DatumList as DatumList>::Ref<'a>,
|
|
|
|
|
|
+pub(crate) struct UniqueComponent<
|
|
|
|
+ E: Entity,
|
|
|
|
+ Parent: Queryable,
|
|
|
|
+ EL: QueryEquivalentList<<E::Uniques as EntityPartList>::DatumList>,
|
|
|
|
+> {
|
|
|
|
+ // datum: <<E::Uniques as EntityPartList>::DatumList as DatumList>::Ref<'a>,
|
|
|
|
+ datum: EL,
|
|
parent: Parent,
|
|
parent: Parent,
|
|
|
|
+ _ghost: std::marker::PhantomData<E>,
|
|
}
|
|
}
|
|
|
|
|
|
-impl<'a, E: Entity, Parent: Queryable> UniqueComponent<'a, E, Parent> {
|
|
|
|
|
|
+impl<
|
|
|
|
+ E: Entity,
|
|
|
|
+ Parent: Queryable,
|
|
|
|
+ EL: QueryEquivalentList<<E::Uniques as EntityPartList>::DatumList>,
|
|
|
|
+ > UniqueComponent<E, Parent, EL>
|
|
|
|
+{
|
|
pub fn new(
|
|
pub fn new(
|
|
parent: Parent,
|
|
parent: Parent,
|
|
- datum: <<E::Uniques as EntityPartList>::DatumList as DatumList>::Ref<'a>,
|
|
|
|
|
|
+ // datum: <<E::Uniques as EntityPartList>::DatumList as DatumList>::Ref<'a>,
|
|
|
|
+ datum: EL,
|
|
) -> Self {
|
|
) -> Self {
|
|
- Self { datum, parent }
|
|
|
|
|
|
+ Self {
|
|
|
|
+ datum,
|
|
|
|
+ parent,
|
|
|
|
+ _ghost: Default::default(),
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/// this workaround is needed because we very explicitly would like EL to not be a
|
|
|
|
+/// 'static-restricted type, and it doesn't matter for the purposes of actually distinguishing
|
|
|
|
+/// between queries.
|
|
|
|
+pub(crate) struct CanonicalUniqueComponent<E: Entity, Parent: Queryable> {
|
|
|
|
+ _ghost: std::marker::PhantomData<(E, Parent)>,
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+impl<E: Entity, Parent: Queryable + 'static> Queryable for CanonicalUniqueComponent<E, Parent> {
|
|
|
|
+ type EntityOutput = E;
|
|
|
|
+ type OutputContainer = Option<IDWrap<E>>;
|
|
|
|
+ type StaticVersion = Self;
|
|
|
|
+
|
|
|
|
+ fn build(&self) -> Query {
|
|
|
|
+ unreachable!()
|
|
|
|
+ }
|
|
|
|
+ fn bind(&self, _stmt: &mut StatementContext, _index: &mut i32) {
|
|
|
|
+ unreachable!()
|
|
|
|
+ }
|
|
|
|
+ fn conn(&self) -> &Connection {
|
|
|
|
+ unreachable!()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
-impl<'a, E: Entity, Parent: Queryable> Queryable for UniqueComponent<'a, E, Parent> {
|
|
|
|
|
|
+impl<
|
|
|
|
+ E: Entity,
|
|
|
|
+ Parent: Queryable,
|
|
|
|
+ EL: QueryEquivalentList<<E::Uniques as EntityPartList>::DatumList>,
|
|
|
|
+ > Queryable for UniqueComponent<E, Parent, EL>
|
|
|
|
+{
|
|
type EntityOutput = E;
|
|
type EntityOutput = E;
|
|
type OutputContainer = Option<IDWrap<E>>;
|
|
type OutputContainer = Option<IDWrap<E>>;
|
|
- type StaticVersion = UniqueComponent<'static, E, Parent::StaticVersion>;
|
|
|
|
|
|
+ type StaticVersion = CanonicalUniqueComponent<E, Parent::StaticVersion>;
|
|
|
|
|
|
fn build(&self) -> Query {
|
|
fn build(&self) -> Query {
|
|
let mut query = self.parent.build();
|
|
let mut query = self.parent.build();
|
|
@@ -245,8 +242,13 @@ impl<R: Entity, L: Entity, EP: EntityPart<Entity = L>, Parent: Queryable>
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
-impl<R: Entity, L: Entity, EP: EntityPart<Entity = L>, Parent: Queryable> Queryable
|
|
|
|
- for JoinComponent<R, L, EP, Parent>
|
|
|
|
|
|
+impl<
|
|
|
|
+ R: Entity,
|
|
|
|
+ L: Entity,
|
|
|
|
+ EP: EntityPart<Entity = L, Datum = AI>,
|
|
|
|
+ AI: AssocInterface + Datum,
|
|
|
|
+ Parent: Queryable,
|
|
|
|
+ > Queryable for JoinComponent<R, L, EP, Parent>
|
|
{
|
|
{
|
|
type EntityOutput = R;
|
|
type EntityOutput = R;
|
|
type OutputContainer = Vec<IDWrap<R>>;
|
|
type OutputContainer = Vec<IDWrap<R>>;
|
|
@@ -255,10 +257,8 @@ impl<R: Entity, L: Entity, EP: EntityPart<Entity = L>, Parent: Queryable> Querya
|
|
fn build(&self) -> Query {
|
|
fn build(&self) -> Query {
|
|
let remote_name = R::entity_name();
|
|
let remote_name = R::entity_name();
|
|
let local_name = L::entity_name();
|
|
let local_name = L::entity_name();
|
|
- let part_name = EP::part_name();
|
|
|
|
- let assoc_name = format!("{local_name}_{remote_name}_assoc_{part_name}");
|
|
|
|
|
|
|
|
- struct Discriminator(Option<(&'static str, &'static str)>);
|
|
|
|
|
|
+ struct Discriminator(Option<(&'static str, &'static str)>, Option<&'static str>);
|
|
impl DatumDiscriminator for Discriminator {
|
|
impl DatumDiscriminator for Discriminator {
|
|
fn visit_entity_id<E: Entity>(&mut self) {
|
|
fn visit_entity_id<E: Entity>(&mut self) {
|
|
unreachable!()
|
|
unreachable!()
|
|
@@ -275,17 +275,24 @@ impl<R: Entity, L: Entity, EP: EntityPart<Entity = L>, Parent: Queryable> Querya
|
|
}
|
|
}
|
|
fn visit_assoc_domain<R: crate::schema::Relation>(&mut self) {
|
|
fn visit_assoc_domain<R: crate::schema::Relation>(&mut self) {
|
|
self.0 = Some(("domain", "range"));
|
|
self.0 = Some(("domain", "range"));
|
|
|
|
+ self.1 = Some(R::NAME);
|
|
}
|
|
}
|
|
fn visit_assoc_range<R: crate::schema::Relation>(&mut self) {
|
|
fn visit_assoc_range<R: crate::schema::Relation>(&mut self) {
|
|
self.0 = Some(("range", "domain"));
|
|
self.0 = Some(("range", "domain"));
|
|
|
|
+ self.1 = Some(R::NAME);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
- let mut d = Discriminator(None);
|
|
|
|
|
|
+ let mut d = Discriminator(None, None);
|
|
<EP::Datum>::accept_discriminator(&mut d);
|
|
<EP::Datum>::accept_discriminator(&mut d);
|
|
|
|
|
|
let (local_field, remote_field) = d.0.unwrap();
|
|
let (local_field, remote_field) = d.0.unwrap();
|
|
|
|
|
|
|
|
+ let assoc_name = format!(
|
|
|
|
+ "{local_name}_{remote_name}_assoc_{}",
|
|
|
|
+ d.1.unwrap_or(EP::part_name())
|
|
|
|
+ );
|
|
|
|
+
|
|
self.parent
|
|
self.parent
|
|
.build()
|
|
.build()
|
|
.attach(
|
|
.attach(
|