|
@@ -339,17 +339,14 @@ impl<AI: RelationInterface> Insertable<AI::RemoteEntity> for AI {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
-/// Represents a searchable context of a given entity. See [`QueryVerbs`] for query verbs.
|
|
|
|
-pub trait Queryable {
|
|
|
|
|
|
+/// Represents a searchable context of a given entity.
|
|
|
|
+pub trait Queryable: Clone {
|
|
/// The entity that results from a search in this context.
|
|
/// The entity that results from a search in this context.
|
|
type EntityOutput: Entity;
|
|
type EntityOutput: Entity;
|
|
/// How results will be provided. This is either a `Vec` or an `Option`.
|
|
/// How results will be provided. This is either a `Vec` or an `Option`.
|
|
type OutputContainer: OutputContainer<Self::EntityOutput>;
|
|
type OutputContainer: OutputContainer<Self::EntityOutput>;
|
|
/// A `'static`-version of `Self`, used for `TypeId`-based caching.
|
|
/// A `'static`-version of `Self`, used for `TypeId`-based caching.
|
|
type StaticVersion: Queryable + 'static;
|
|
type StaticVersion: Queryable + 'static;
|
|
- /// A possible tag-along type stored in the query object. See [`Self::carry_lease`] for one
|
|
|
|
- /// use.
|
|
|
|
- type Carrier;
|
|
|
|
|
|
|
|
/// True if the query result is guaranteed to be either unique or absent.
|
|
/// True if the query result is guaranteed to be either unique or absent.
|
|
const IS_UNIQUE: bool;
|
|
const IS_UNIQUE: bool;
|
|
@@ -361,202 +358,9 @@ pub trait Queryable {
|
|
#[doc(hidden)]
|
|
#[doc(hidden)]
|
|
fn bind(&self, stmt: &mut StatementContext, index: &mut i32);
|
|
fn bind(&self, stmt: &mut StatementContext, index: &mut i32);
|
|
|
|
|
|
- #[doc(hidden)]
|
|
|
|
- fn carried(&mut self) -> &mut Self::Carrier;
|
|
|
|
-
|
|
|
|
- /// Construct a new [`Queryable`] that carries a connection lease along with it.
|
|
|
|
- ///
|
|
|
|
- /// This allows the use of the [`CarrierQueryVerbs`] trait instead of the [`QueryVerbs`] trait,
|
|
|
|
- /// which reduces the number of objects that need be passed around.
|
|
|
|
- fn carry_lease<'r, 'l: 'r>(
|
|
|
|
- self,
|
|
|
|
- lease: &'r mut ConnectionLease<'l>,
|
|
|
|
- ) -> impl Queryable<
|
|
|
|
- EntityOutput = Self::EntityOutput,
|
|
|
|
- OutputContainer = Self::OutputContainer,
|
|
|
|
- Carrier = Option<&'r mut ConnectionLease<'l>>,
|
|
|
|
- >
|
|
|
|
- where
|
|
|
|
- Self: Sized,
|
|
|
|
- {
|
|
|
|
- components::LeaseCarrierComponent::new(self, lease)
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- // Verbs are now implemented in [`QueryVerbs`]
|
|
|
|
-
|
|
|
|
- // ----------------------------------------------------------------------
|
|
|
|
- // Filtering methods
|
|
|
|
- // ----------------------------------------------------------------------
|
|
|
|
- /// Filter using the keying index on the entity.
|
|
|
|
- fn keyed(
|
|
|
|
- self,
|
|
|
|
- values: impl QueryEquivalentList<
|
|
|
|
- <<Self::EntityOutput as Entity>::Keys as EntityPartList>::DatumList,
|
|
|
|
- >,
|
|
|
|
- ) -> impl Queryable<
|
|
|
|
- EntityOutput = Self::EntityOutput,
|
|
|
|
- OutputContainer = Option<Stored<Self::EntityOutput>>,
|
|
|
|
- Carrier = Self::Carrier,
|
|
|
|
- >
|
|
|
|
- where
|
|
|
|
- Self: Sized,
|
|
|
|
- {
|
|
|
|
- components::IndexComponent::<_, _, <Self::EntityOutput as Entity>::Keys, _>::new(
|
|
|
|
- self, values,
|
|
|
|
- )
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /// Filter using an arbitrary index on the entity.
|
|
|
|
- fn indexed<EPL: EntityPartList<Entity = Self::EntityOutput>>(
|
|
|
|
- self,
|
|
|
|
- _index: &Index<Self::EntityOutput, EPL>,
|
|
|
|
- values: impl QueryEquivalentList<EPL::DatumList>,
|
|
|
|
- ) -> impl Queryable<
|
|
|
|
- EntityOutput = Self::EntityOutput,
|
|
|
|
- OutputContainer = Option<Stored<Self::EntityOutput>>,
|
|
|
|
- Carrier = Self::Carrier,
|
|
|
|
- >
|
|
|
|
- where
|
|
|
|
- Self: Sized,
|
|
|
|
- {
|
|
|
|
- components::IndexComponent::<_, _, EPL, _>::new(self, values)
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /// Filter using an arbitrary column on the entity.
|
|
|
|
- fn with<EP: EntityPart<Entity = Self::EntityOutput>>(
|
|
|
|
- self,
|
|
|
|
- part: EP,
|
|
|
|
- value: impl QueryEquivalent<EP::Datum>,
|
|
|
|
- ) -> impl Queryable<
|
|
|
|
- EntityOutput = Self::EntityOutput,
|
|
|
|
- OutputContainer = Self::OutputContainer,
|
|
|
|
- Carrier = Self::Carrier,
|
|
|
|
- >
|
|
|
|
- where
|
|
|
|
- Self: Sized,
|
|
|
|
- {
|
|
|
|
- components::WithComponent::new(self, part, value)
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /// Filter exactly on an entity ID.
|
|
|
|
- fn with_id(
|
|
|
|
- self,
|
|
|
|
- id: <Self::EntityOutput as Entity>::ID,
|
|
|
|
- ) -> impl Queryable<
|
|
|
|
- EntityOutput = Self::EntityOutput,
|
|
|
|
- OutputContainer = Option<Stored<Self::EntityOutput>>,
|
|
|
|
- Carrier = Self::Carrier,
|
|
|
|
- >
|
|
|
|
- where
|
|
|
|
- Self: Sized,
|
|
|
|
- {
|
|
|
|
- self.with(<Self::EntityOutput as Entity>::IDPart::default(), id)
|
|
|
|
- .first()
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /// Ask to return at most a single result.
|
|
|
|
- fn first(
|
|
|
|
- self,
|
|
|
|
- ) -> impl Queryable<
|
|
|
|
- EntityOutput = Self::EntityOutput,
|
|
|
|
- OutputContainer = Option<Stored<Self::EntityOutput>>,
|
|
|
|
- Carrier = Self::Carrier,
|
|
|
|
- >
|
|
|
|
- where
|
|
|
|
- Self: Sized,
|
|
|
|
- {
|
|
|
|
- components::SingleComponent::new(self)
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
// ----------------------------------------------------------------------
|
|
// ----------------------------------------------------------------------
|
|
- // Relation-following and joining methods
|
|
|
|
|
|
+ // Verbs
|
|
// ----------------------------------------------------------------------
|
|
// ----------------------------------------------------------------------
|
|
- /// Join based on an existing relation.
|
|
|
|
- fn join<
|
|
|
|
- AD: RelationInterface + Datum,
|
|
|
|
- EP: EntityPart<Entity = Self::EntityOutput, Datum = AD>,
|
|
|
|
- >(
|
|
|
|
- self,
|
|
|
|
- part: EP,
|
|
|
|
- ) -> impl Queryable<
|
|
|
|
- EntityOutput = AD::RemoteEntity,
|
|
|
|
- OutputContainer = Vec<Stored<AD::RemoteEntity>>,
|
|
|
|
- Carrier = Self::Carrier,
|
|
|
|
- >
|
|
|
|
- where
|
|
|
|
- Self: Sized,
|
|
|
|
- {
|
|
|
|
- components::JoinComponent::<AD::RemoteEntity, Self::EntityOutput, _, Self>::new(self, part)
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /// Follow a foreign key.
|
|
|
|
- fn foreign<EP: EntityPart<Entity = Self::EntityOutput>>(
|
|
|
|
- self,
|
|
|
|
- part: EP,
|
|
|
|
- ) -> impl Queryable<
|
|
|
|
- EntityOutput = <EP::Datum as EntityID>::Entity,
|
|
|
|
- OutputContainer = <Self::OutputContainer as OutputContainer<
|
|
|
|
- Self::EntityOutput,
|
|
|
|
- >>::ReplacedEntity<<EP::Datum as EntityID>::Entity>,
|
|
|
|
- Carrier = Self::Carrier,
|
|
|
|
- >
|
|
|
|
- where
|
|
|
|
- Self: Sized,
|
|
|
|
- EP::Datum: EntityID,
|
|
|
|
- {
|
|
|
|
- components::ForeignComponent::<_, EP, Self>::new(self, part)
|
|
|
|
- }
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-// Generic implementation for all relation specification types
|
|
|
|
-impl<'a, AI: RelationInterface> Queryable for &'a AI {
|
|
|
|
- type EntityOutput = AI::RemoteEntity;
|
|
|
|
- type OutputContainer = Vec<Stored<AI::RemoteEntity>>;
|
|
|
|
- type StaticVersion = &'static AI::StaticVersion;
|
|
|
|
- type Carrier = ();
|
|
|
|
-
|
|
|
|
- const IS_UNIQUE: bool = false;
|
|
|
|
-
|
|
|
|
- fn carried(&mut self) -> &mut Self::Carrier {
|
|
|
|
- unreachable!()
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- fn build(&self) -> Query {
|
|
|
|
- let anames = RelationNames::collect(*self).unwrap();
|
|
|
|
- let relation_name = anames.relation_name();
|
|
|
|
- Query::new()
|
|
|
|
- .attach(QueryPart::Root, "SELECT DISTINCT")
|
|
|
|
- .attach(QueryPart::Columns, format!("`{}`.*", anames.remote_name))
|
|
|
|
- .attach(QueryPart::From, format!("`{}`", relation_name))
|
|
|
|
- .attach(
|
|
|
|
- QueryPart::Join,
|
|
|
|
- format!(
|
|
|
|
- "`{}` ON `{}`.`id` = `{}`.`{}`",
|
|
|
|
- anames.remote_name, anames.remote_name, relation_name, anames.remote_field
|
|
|
|
- ),
|
|
|
|
- )
|
|
|
|
- .attach(
|
|
|
|
- QueryPart::Where,
|
|
|
|
- format!("`{}`.`{}` = ?", relation_name, anames.local_field),
|
|
|
|
- )
|
|
|
|
- }
|
|
|
|
- fn bind(&self, ctx: &mut StatementContext, index: &mut i32) {
|
|
|
|
- let rdata = self
|
|
|
|
- .get_data()
|
|
|
|
- .expect("binding query for relation with no data");
|
|
|
|
-
|
|
|
|
- ctx.bind(*index, rdata.local_id)
|
|
|
|
- .expect("couldn't bind relation id");
|
|
|
|
- *index += 1;
|
|
|
|
- }
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-// ----------------------------------------------------------------------
|
|
|
|
-// Verb traits
|
|
|
|
-// ----------------------------------------------------------------------
|
|
|
|
-
|
|
|
|
-/// Completion verbs for a [`Queryable`] object.
|
|
|
|
-pub trait QueryVerbs: Queryable<Carrier = ()> {
|
|
|
|
/// Count all entities in the current context.
|
|
/// Count all entities in the current context.
|
|
///
|
|
///
|
|
/// Returns the number of entities.
|
|
/// Returns the number of entities.
|
|
@@ -679,7 +483,8 @@ pub trait QueryVerbs: Queryable<Carrier = ()> {
|
|
)?;
|
|
)?;
|
|
txn.commit()
|
|
txn.commit()
|
|
}
|
|
}
|
|
- /// Delete all entities in the current context and return them.
|
|
|
|
|
|
+
|
|
|
|
+ /// Delete all entities in the current context and return them
|
|
fn remove(self, lease: &mut ConnectionLease) -> DBResult<Self::OutputContainer>
|
|
fn remove(self, lease: &mut ConnectionLease) -> DBResult<Self::OutputContainer>
|
|
where
|
|
where
|
|
Self: Sized,
|
|
Self: Sized,
|
|
@@ -712,72 +517,155 @@ pub trait QueryVerbs: Queryable<Carrier = ()> {
|
|
txn.commit()?;
|
|
txn.commit()?;
|
|
Ok(out)
|
|
Ok(out)
|
|
}
|
|
}
|
|
-}
|
|
|
|
|
|
|
|
-impl<T: Queryable<Carrier = ()>> QueryVerbs for T {}
|
|
|
|
|
|
+ // ----------------------------------------------------------------------
|
|
|
|
+ // Filtering methods
|
|
|
|
+ // ----------------------------------------------------------------------
|
|
|
|
+ /// Filter using the keying index on the entity.
|
|
|
|
+ fn keyed(
|
|
|
|
+ self,
|
|
|
|
+ values: impl QueryEquivalentList<
|
|
|
|
+ <<Self::EntityOutput as Entity>::Keys as EntityPartList>::DatumList,
|
|
|
|
+ >,
|
|
|
|
+ ) -> impl Queryable<
|
|
|
|
+ EntityOutput = Self::EntityOutput,
|
|
|
|
+ OutputContainer = Option<Stored<Self::EntityOutput>>,
|
|
|
|
+ >
|
|
|
|
+ where
|
|
|
|
+ Self: Sized,
|
|
|
|
+ {
|
|
|
|
+ components::IndexComponent::<_, _, <Self::EntityOutput as Entity>::Keys, _>::new(
|
|
|
|
+ self, values,
|
|
|
|
+ )
|
|
|
|
+ }
|
|
|
|
|
|
-/// Completion verbs for a [`Queryable`] object that carries a [`ConnectionLease`].
|
|
|
|
-pub trait CarrierQueryVerbs<'r, 'l: 'r>:
|
|
|
|
- Queryable<Carrier = components::LeaseCarrier<'r, 'l>>
|
|
|
|
-{
|
|
|
|
- /// Count all entities in the current context.
|
|
|
|
- ///
|
|
|
|
- /// Returns the number of entities.
|
|
|
|
- fn count(self) -> DBResult<usize>
|
|
|
|
|
|
+ /// Filter using an arbitrary index on the entity.
|
|
|
|
+ fn indexed<EPL: EntityPartList<Entity = Self::EntityOutput>>(
|
|
|
|
+ self,
|
|
|
|
+ _index: &Index<Self::EntityOutput, EPL>,
|
|
|
|
+ values: impl QueryEquivalentList<EPL::DatumList>,
|
|
|
|
+ ) -> impl Queryable<
|
|
|
|
+ EntityOutput = Self::EntityOutput,
|
|
|
|
+ OutputContainer = Option<Stored<Self::EntityOutput>>,
|
|
|
|
+ >
|
|
where
|
|
where
|
|
Self: Sized,
|
|
Self: Sized,
|
|
{
|
|
{
|
|
- let (query, lease) = components::ExtractLeaseComponent::new(self);
|
|
|
|
- query.count(lease)
|
|
|
|
|
|
+ components::IndexComponent::<_, _, EPL, _>::new(self, values)
|
|
}
|
|
}
|
|
|
|
|
|
- /// Get all entities in the current context.
|
|
|
|
- fn get(self) -> DBResult<Self::OutputContainer>
|
|
|
|
|
|
+ /// Filter using an arbitrary column on the entity.
|
|
|
|
+ fn with<EP: EntityPart<Entity = Self::EntityOutput>>(
|
|
|
|
+ self,
|
|
|
|
+ part: EP,
|
|
|
|
+ value: impl QueryEquivalent<EP::Datum>,
|
|
|
|
+ ) -> impl Queryable<EntityOutput = Self::EntityOutput, OutputContainer = Self::OutputContainer>
|
|
where
|
|
where
|
|
Self: Sized,
|
|
Self: Sized,
|
|
{
|
|
{
|
|
- let (query, lease) = components::ExtractLeaseComponent::new(self);
|
|
|
|
- query.get(lease)
|
|
|
|
|
|
+ components::WithComponent::new(self, part, value)
|
|
}
|
|
}
|
|
|
|
|
|
- /// Get IDs of all entities in the current context.
|
|
|
|
- fn get_ids(
|
|
|
|
|
|
+ /// Filter exactly on an entity ID.
|
|
|
|
+ fn with_id(
|
|
self,
|
|
self,
|
|
- ) -> DBResult<<Self::OutputContainer as OutputContainer<Self::EntityOutput>>::IDContainer>
|
|
|
|
|
|
+ id: <Self::EntityOutput as Entity>::ID,
|
|
|
|
+ ) -> impl Queryable<
|
|
|
|
+ EntityOutput = Self::EntityOutput,
|
|
|
|
+ OutputContainer = Option<Stored<Self::EntityOutput>>,
|
|
|
|
+ >
|
|
where
|
|
where
|
|
Self: Sized,
|
|
Self: Sized,
|
|
{
|
|
{
|
|
- let (query, lease) = components::ExtractLeaseComponent::new(self);
|
|
|
|
- query.get_ids(lease)
|
|
|
|
|
|
+ self.with(<Self::EntityOutput as Entity>::IDPart::default(), id)
|
|
|
|
+ .first()
|
|
}
|
|
}
|
|
|
|
|
|
- /// Delete all entities in the current context.
|
|
|
|
- fn delete(self) -> DBResult<()>
|
|
|
|
|
|
+ /// Ask to return at most a single result.
|
|
|
|
+ fn first(
|
|
|
|
+ self,
|
|
|
|
+ ) -> impl Queryable<
|
|
|
|
+ EntityOutput = Self::EntityOutput,
|
|
|
|
+ OutputContainer = Option<Stored<Self::EntityOutput>>,
|
|
|
|
+ >
|
|
where
|
|
where
|
|
Self: Sized,
|
|
Self: Sized,
|
|
{
|
|
{
|
|
- let (query, lease) = components::ExtractLeaseComponent::new(self);
|
|
|
|
- query.delete(lease)
|
|
|
|
|
|
+ components::SingleComponent::new(self)
|
|
}
|
|
}
|
|
|
|
|
|
- /// Delete all entities in the current context and return them.
|
|
|
|
- fn remove(self) -> DBResult<Self::OutputContainer>
|
|
|
|
|
|
+ // ----------------------------------------------------------------------
|
|
|
|
+ // Relation-following and joining methods
|
|
|
|
+ // ----------------------------------------------------------------------
|
|
|
|
+ /// Join based on an existing relation.
|
|
|
|
+ fn join<
|
|
|
|
+ AD: RelationInterface + Datum,
|
|
|
|
+ EP: EntityPart<Entity = Self::EntityOutput, Datum = AD>,
|
|
|
|
+ >(
|
|
|
|
+ self,
|
|
|
|
+ part: EP,
|
|
|
|
+ ) -> impl Queryable<EntityOutput = AD::RemoteEntity, OutputContainer = Vec<Stored<AD::RemoteEntity>>>
|
|
where
|
|
where
|
|
Self: Sized,
|
|
Self: Sized,
|
|
{
|
|
{
|
|
- let (query, lease) = components::ExtractLeaseComponent::new(self);
|
|
|
|
- query.remove(lease)
|
|
|
|
|
|
+ components::JoinComponent::<AD::RemoteEntity, Self::EntityOutput, _, Self>::new(self, part)
|
|
}
|
|
}
|
|
-}
|
|
|
|
|
|
|
|
-impl<'r, 'l: 'r, T: Queryable<Carrier = components::LeaseCarrier<'r, 'l>>> CarrierQueryVerbs<'r, 'l>
|
|
|
|
- for T
|
|
|
|
-{
|
|
|
|
|
|
+ /// Follow a foreign key.
|
|
|
|
+ fn foreign<EP: EntityPart<Entity = Self::EntityOutput>>(
|
|
|
|
+ self,
|
|
|
|
+ part: EP,
|
|
|
|
+ ) -> impl Queryable<
|
|
|
|
+ EntityOutput = <EP::Datum as EntityID>::Entity,
|
|
|
|
+ OutputContainer = <Self::OutputContainer as OutputContainer<
|
|
|
|
+ Self::EntityOutput,
|
|
|
|
+ >>::ReplacedEntity<<EP::Datum as EntityID>::Entity>,
|
|
|
|
+ >
|
|
|
|
+ where
|
|
|
|
+ Self: Sized,
|
|
|
|
+ EP::Datum: EntityID,
|
|
|
|
+ {
|
|
|
|
+ components::ForeignComponent::<_, EP, Self>::new(self, part)
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
-// ----------------------------------------------------------------------
|
|
|
|
-// Implementations on Index
|
|
|
|
-// ----------------------------------------------------------------------
|
|
|
|
|
|
+// Generic implementation for all relation specification types
|
|
|
|
+impl<'a, AI: RelationInterface> Queryable for &'a AI {
|
|
|
|
+ type EntityOutput = AI::RemoteEntity;
|
|
|
|
+ type OutputContainer = Vec<Stored<AI::RemoteEntity>>;
|
|
|
|
+ type StaticVersion = &'static AI::StaticVersion;
|
|
|
|
+
|
|
|
|
+ const IS_UNIQUE: bool = false;
|
|
|
|
+
|
|
|
|
+ fn build(&self) -> Query {
|
|
|
|
+ let anames = RelationNames::collect(*self).unwrap();
|
|
|
|
+ let relation_name = anames.relation_name();
|
|
|
|
+ Query::new()
|
|
|
|
+ .attach(QueryPart::Root, "SELECT DISTINCT")
|
|
|
|
+ .attach(QueryPart::Columns, format!("`{}`.*", anames.remote_name))
|
|
|
|
+ .attach(QueryPart::From, format!("`{}`", relation_name))
|
|
|
|
+ .attach(
|
|
|
|
+ QueryPart::Join,
|
|
|
|
+ format!(
|
|
|
|
+ "`{}` ON `{}`.`id` = `{}`.`{}`",
|
|
|
|
+ anames.remote_name, anames.remote_name, relation_name, anames.remote_field
|
|
|
|
+ ),
|
|
|
|
+ )
|
|
|
|
+ .attach(
|
|
|
|
+ QueryPart::Where,
|
|
|
|
+ format!("`{}`.`{}` = ?", relation_name, anames.local_field),
|
|
|
|
+ )
|
|
|
|
+ }
|
|
|
|
+ fn bind(&self, ctx: &mut StatementContext, index: &mut i32) {
|
|
|
|
+ let rdata = self
|
|
|
|
+ .get_data()
|
|
|
|
+ .expect("binding query for relation with no data");
|
|
|
|
+
|
|
|
|
+ ctx.bind(*index, rdata.local_id)
|
|
|
|
+ .expect("couldn't bind relation id");
|
|
|
|
+ *index += 1;
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
|
|
impl<E: Entity, EPL: EntityPartList<Entity = E>> Index<E, EPL> {
|
|
impl<E: Entity, EPL: EntityPartList<Entity = E>> Index<E, EPL> {
|
|
/// Perform a search through this index
|
|
/// Perform a search through this index
|
|
@@ -795,14 +683,9 @@ impl<'a, E: Entity, EPL: EntityPartList<Entity = E>> Queryable for &'a Index<E,
|
|
type EntityOutput = E;
|
|
type EntityOutput = E;
|
|
type OutputContainer = Vec<Stored<E>>;
|
|
type OutputContainer = Vec<Stored<E>>;
|
|
type StaticVersion = &'static Index<E, EPL>;
|
|
type StaticVersion = &'static Index<E, EPL>;
|
|
- type Carrier = ();
|
|
|
|
|
|
|
|
const IS_UNIQUE: bool = false;
|
|
const IS_UNIQUE: bool = false;
|
|
|
|
|
|
- fn carried(&mut self) -> &mut Self::Carrier {
|
|
|
|
- unreachable!()
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
fn build(&self) -> Query {
|
|
fn build(&self) -> Query {
|
|
Query::new()
|
|
Query::new()
|
|
.attach(QueryPart::Root, "SELECT DISTINCT")
|
|
.attach(QueryPart::Root, "SELECT DISTINCT")
|