|
@@ -6,7 +6,7 @@ use crate::{
|
|
|
schema::{
|
|
|
datum::{Datum, DatumList, DatumListRef, DatumVisitor},
|
|
|
entity::{Entity, EntityPart, EntityPartList, EntityPartVisitor},
|
|
|
- IDMap, IDWrap,
|
|
|
+ DatumDiscriminator, IDMap, IDWrap,
|
|
|
},
|
|
|
DBResult,
|
|
|
};
|
|
@@ -29,13 +29,17 @@ impl<'a, E: Entity> Queryable for MapQueryable<'a, E> {
|
|
|
type OutputContainer = Vec<IDWrap<E>>;
|
|
|
type StaticVersion = MapQueryable<'static, E>;
|
|
|
|
|
|
- fn build<'s, 'q: 's>(&'s self) -> Query<'s> {
|
|
|
- Query::new(&self.map.conn())
|
|
|
- .attach(QueryPart::Root, "SELECT".into())
|
|
|
+ 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 sqlite::Statement, _index: &mut usize) {}
|
|
|
+
|
|
|
+ fn conn(&self) -> &crate::db::DBConnection {
|
|
|
+ self.map.conn()
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
/// Concrete implementation of Queryable for an IDMap
|
|
@@ -54,15 +58,15 @@ impl<'a, AI: AssocInterface> Queryable for AssocQueryable<'a, AI> {
|
|
|
type OutputContainer = Vec<IDWrap<AI::RemoteEntity>>;
|
|
|
type StaticVersion = AssocQueryable<'static, AI>;
|
|
|
|
|
|
- fn build<'s, 'q: 's>(&'s self) -> Query<'s> {
|
|
|
+ fn build(&self) -> Query {
|
|
|
let adata = self
|
|
|
.assoc
|
|
|
.get_data()
|
|
|
.expect("building query for assoc with no data");
|
|
|
let anames = super::AssocNames::collect(self.assoc).unwrap();
|
|
|
let assoc_name = anames.assoc_name();
|
|
|
- Query::new(&adata.conn)
|
|
|
- .attach(QueryPart::Root, "SELECT".into())
|
|
|
+ Query::new()
|
|
|
+ .attach(QueryPart::Root, "SELECT DISTINCT".into())
|
|
|
.attach(QueryPart::Columns, format!("`{}`.*", anames.remote_name))
|
|
|
.attach(QueryPart::From, format!("`{}`", assoc_name))
|
|
|
.attach(
|
|
@@ -87,6 +91,10 @@ impl<'a, AI: AssocInterface> Queryable for AssocQueryable<'a, AI> {
|
|
|
.expect("couldn't bind assoc id");
|
|
|
*index += 1;
|
|
|
}
|
|
|
+
|
|
|
+ fn conn(&self) -> &crate::db::DBConnection {
|
|
|
+ &self.assoc.get_data().unwrap().conn
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
/// Filter on a Datum
|
|
@@ -111,7 +119,7 @@ impl<'a, WEP: EntityPart, Parent: Queryable> Queryable for WithComponent<'a, WEP
|
|
|
type OutputContainer = Parent::OutputContainer;
|
|
|
type StaticVersion = WithComponent<'static, WEP, Parent::StaticVersion>;
|
|
|
|
|
|
- fn build<'s, 'q: 's>(&'s self) -> Query<'s> {
|
|
|
+ fn build(&self) -> Query {
|
|
|
self.parent.build().attach(
|
|
|
QueryPart::Where,
|
|
|
format!(
|
|
@@ -126,6 +134,10 @@ impl<'a, WEP: EntityPart, Parent: Queryable> Queryable for WithComponent<'a, WEP
|
|
|
self.datum.bind_to(stmt, *index);
|
|
|
*index += 1;
|
|
|
}
|
|
|
+
|
|
|
+ fn conn(&self) -> &crate::db::DBConnection {
|
|
|
+ self.parent.conn()
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
/// Filter on the unique index
|
|
@@ -148,11 +160,11 @@ impl<'a, E: Entity, Parent: Queryable> Queryable for UniqueComponent<'a, E, Pare
|
|
|
type OutputContainer = Option<IDWrap<E>>;
|
|
|
type StaticVersion = UniqueComponent<'static, E, Parent::StaticVersion>;
|
|
|
|
|
|
- fn build<'s, 'q: 's>(&'s self) -> Query<'s> {
|
|
|
+ fn build(&self) -> Query {
|
|
|
let mut query = self.parent.build();
|
|
|
|
|
|
- struct PartVisitor<'a, 'b>(&'a mut Query<'b>);
|
|
|
- impl<'a, 'b> EntityPartVisitor for PartVisitor<'a, 'b> {
|
|
|
+ struct PartVisitor<'a>(&'a mut Query);
|
|
|
+ impl<'a> EntityPartVisitor for PartVisitor<'a> {
|
|
|
fn visit<EP: EntityPart>(&mut self) {
|
|
|
self.0.attach_mut(
|
|
|
QueryPart::Where,
|
|
@@ -183,6 +195,10 @@ impl<'a, E: Entity, Parent: Queryable> Queryable for UniqueComponent<'a, E, Pare
|
|
|
|
|
|
self.datum.accept(&mut Visitor(stmt, index));
|
|
|
}
|
|
|
+
|
|
|
+ fn conn(&self) -> &crate::db::DBConnection {
|
|
|
+ self.parent.conn()
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
pub(crate) struct SingleComponent<Parent: Queryable> {
|
|
@@ -200,7 +216,7 @@ impl<Parent: Queryable> Queryable for SingleComponent<Parent> {
|
|
|
type OutputContainer = Option<IDWrap<Self::EntityOutput>>;
|
|
|
type StaticVersion = SingleComponent<Parent::StaticVersion>;
|
|
|
|
|
|
- fn build<'s, 'q: 's>(&'s self) -> Query<'s> {
|
|
|
+ fn build(&self) -> Query {
|
|
|
self.parent
|
|
|
.build()
|
|
|
.attach(QueryPart::Trailing, "LIMIT 1".into())
|
|
@@ -209,6 +225,10 @@ impl<Parent: Queryable> Queryable for SingleComponent<Parent> {
|
|
|
fn bind(&self, stmt: &mut sqlite::Statement, index: &mut usize) {
|
|
|
self.parent.bind(stmt, index)
|
|
|
}
|
|
|
+
|
|
|
+ fn conn(&self) -> &crate::db::DBConnection {
|
|
|
+ self.parent.conn()
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
/// Join with another entity via an association
|
|
@@ -236,13 +256,60 @@ impl<R: Entity, L: Entity, EP: EntityPart<Entity = L>, Parent: Queryable> Querya
|
|
|
type OutputContainer = Vec<IDWrap<R>>;
|
|
|
type StaticVersion = JoinComponent<R, L, EP, Parent::StaticVersion>;
|
|
|
|
|
|
- fn build<'s, 'q: 's>(&'s self) -> Query<'s> {
|
|
|
- // self.parent.build()
|
|
|
- todo!()
|
|
|
+ fn build(&self) -> Query {
|
|
|
+ let remote_name = R::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)>);
|
|
|
+ impl DatumDiscriminator for Discriminator {
|
|
|
+ fn visit_entity_id<E: Entity>(&mut self) {
|
|
|
+ unreachable!()
|
|
|
+ }
|
|
|
+ fn visit_serialized<T: serde::Serialize + serde::de::DeserializeOwned>(&mut self) {
|
|
|
+ unreachable!()
|
|
|
+ }
|
|
|
+ fn visit_bare_field<T: Datum>(&mut self) {
|
|
|
+ unreachable!()
|
|
|
+ }
|
|
|
+
|
|
|
+ fn visit_assoc_map<E: Entity>(&mut self) {
|
|
|
+ self.0 = Some(("domain", "range"));
|
|
|
+ }
|
|
|
+ fn visit_assoc_domain<R: crate::schema::Relation>(&mut self) {
|
|
|
+ self.0 = Some(("domain", "range"));
|
|
|
+ }
|
|
|
+ fn visit_assoc_range<R: crate::schema::Relation>(&mut self) {
|
|
|
+ self.0 = Some(("range", "domain"));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ let mut d = Discriminator(None);
|
|
|
+ <EP::Datum>::accept_discriminator(&mut d);
|
|
|
+
|
|
|
+ let (local_field, remote_field) = d.0.unwrap();
|
|
|
+
|
|
|
+ self.parent
|
|
|
+ .build()
|
|
|
+ .attach(
|
|
|
+ QueryPart::Join,
|
|
|
+ format!("`{assoc_name}` ON `{local_name}`.`id` = `{assoc_name}`.`{local_field}`"),
|
|
|
+ )
|
|
|
+ .attach(
|
|
|
+ QueryPart::Join,
|
|
|
+ format!(
|
|
|
+ "`{remote_name}` ON `{assoc_name}`.`{remote_field}` = `{remote_name}`.`id`"
|
|
|
+ ),
|
|
|
+ )
|
|
|
+ .replace(QueryPart::Columns, format!("`{remote_name}`.*"))
|
|
|
}
|
|
|
fn bind(&self, stmt: &mut sqlite::Statement, index: &mut usize) {
|
|
|
self.parent.bind(stmt, index);
|
|
|
- todo!()
|
|
|
+ }
|
|
|
+
|
|
|
+ fn conn(&self) -> &crate::db::DBConnection {
|
|
|
+ self.parent.conn()
|
|
|
}
|
|
|
}
|
|
|
|