|
@@ -22,16 +22,16 @@ pub enum QueryPart {
|
|
|
pub struct DerivedQuery(HashMap<QueryPart, Vec<String>>);
|
|
|
|
|
|
impl DerivedQuery {
|
|
|
- fn new() -> Self {
|
|
|
+ pub(crate) fn new() -> Self {
|
|
|
Self { 0: HashMap::new() }
|
|
|
}
|
|
|
|
|
|
- fn add(mut self, to: QueryPart, what: String) -> Self {
|
|
|
+ pub(crate) fn add(mut self, to: QueryPart, what: String) -> Self {
|
|
|
self.0.entry(to).or_insert_with(|| Vec::new()).push(what);
|
|
|
self
|
|
|
}
|
|
|
|
|
|
- fn assemble(mut self) -> String {
|
|
|
+ pub(crate) fn assemble(mut self) -> String {
|
|
|
let root = self.0.remove(&QueryPart::Root).unwrap().remove(0);
|
|
|
let where_ = match self.0.remove(&QueryPart::Where) {
|
|
|
None => String::new(),
|
|
@@ -69,85 +69,6 @@ pub trait QueryComponent: StaticVersion {
|
|
|
fn bind(&self, stmt: &mut sqlite::Statement<'_>) -> Result<usize, Error>;
|
|
|
}
|
|
|
|
|
|
-/// Any query that can be completed/executed
|
|
|
-pub trait Resolvable<'r, 'q, T: Entity>: QueryComponent
|
|
|
-where
|
|
|
- 'q: 'r,
|
|
|
-{
|
|
|
- fn qi(&self) -> &'r QueryInterface<'q>;
|
|
|
-
|
|
|
- fn exec(self) -> Result<(), crate::Error> where Self: Sized { self.no_result() }
|
|
|
- fn one(self) -> Result<Option<WithID<T>>, crate::Error> where Self: Sized { self.result() }
|
|
|
- fn all(self) -> Result<Vec<WithID<T>>, crate::Error> where Self: Sized { self.results() }
|
|
|
-
|
|
|
- fn no_result(self) -> Result<(), crate::Error>
|
|
|
- where
|
|
|
- Self: Sized,
|
|
|
- {
|
|
|
- let mut hasher = std::collections::hash_map::DefaultHasher::new();
|
|
|
- self.contribute(&mut hasher);
|
|
|
-
|
|
|
- self.qi().with_cache(
|
|
|
- hasher.finish(),
|
|
|
- || self.qi().db.conn.prepare(self.derive().assemble()).unwrap(),
|
|
|
- |stmt| {
|
|
|
- self.bind(stmt)?;
|
|
|
-
|
|
|
- self.qi().expect_no_result(stmt)
|
|
|
- },
|
|
|
- )
|
|
|
- }
|
|
|
- fn result(self) -> Result<Option<WithID<T>>, crate::Error>
|
|
|
- where
|
|
|
- Self: Sized,
|
|
|
- {
|
|
|
- let mut hasher = std::collections::hash_map::DefaultHasher::new();
|
|
|
- self.contribute(&mut hasher);
|
|
|
- self.qi().with_cache(
|
|
|
- hasher.finish(),
|
|
|
- || {
|
|
|
- let query = self.derive().assemble();
|
|
|
- self.qi().db.conn.prepare(query).unwrap()
|
|
|
- },
|
|
|
- |stmt| {
|
|
|
- self.bind(stmt)?;
|
|
|
-
|
|
|
- self.qi().expect_one_result(stmt, &mut |stmt| {
|
|
|
- let id: i64 = stmt.read(0)?;
|
|
|
- Ok(WithID::wrap(T::build_from(stmt)?, id))
|
|
|
- })
|
|
|
- },
|
|
|
- )
|
|
|
- }
|
|
|
- fn results(self) -> Result<Vec<WithID<T>>, crate::Error>
|
|
|
- where
|
|
|
- Self: Sized,
|
|
|
- {
|
|
|
- let mut hasher = std::collections::hash_map::DefaultHasher::new();
|
|
|
- self.contribute(&mut hasher);
|
|
|
- self.qi().with_cache(
|
|
|
- hasher.finish(),
|
|
|
- || self.qi().db.conn.prepare(self.derive().assemble()).unwrap(),
|
|
|
- |stmt| {
|
|
|
- self.bind(stmt)?;
|
|
|
-
|
|
|
- let mut res = Vec::new();
|
|
|
- loop {
|
|
|
- let state = stmt.next()?;
|
|
|
- if state == sqlite::State::Done {
|
|
|
- break;
|
|
|
- }
|
|
|
-
|
|
|
- let id: i64 = stmt.read(0)?;
|
|
|
- res.push(WithID::wrap(T::build_from(stmt)?, id));
|
|
|
- }
|
|
|
-
|
|
|
- Ok(res)
|
|
|
- },
|
|
|
- )
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
#[derive(Debug)]
|
|
|
pub enum CompareOp {
|
|
|
LessThan,
|
|
@@ -158,7 +79,7 @@ pub enum CompareOp {
|
|
|
}
|
|
|
|
|
|
impl CompareOp {
|
|
|
- fn ch(&self) -> &'static str {
|
|
|
+ pub(crate) fn ch(&self) -> &'static str {
|
|
|
match self {
|
|
|
Self::LessThan => "<",
|
|
|
Self::AtMost => "<=",
|
|
@@ -168,204 +89,3 @@ impl CompareOp {
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
-/// Any query that can have a WHERE clause attached to it
|
|
|
-pub trait Filterable<'r, 'q>: Resolvable<'r, 'q, Self::Table>
|
|
|
-where
|
|
|
- 'q: 'r,
|
|
|
-{
|
|
|
- type Table: Entity;
|
|
|
-
|
|
|
- fn by<C: EntityColumn<Entity = Self::Table>, G: Modelable + ?Sized>(
|
|
|
- self,
|
|
|
- col: C,
|
|
|
- given: &'r G,
|
|
|
- ) -> Filter<'r, 'q, Self, C, G>
|
|
|
- where
|
|
|
- Self: Sized,
|
|
|
- {
|
|
|
- Filter {
|
|
|
- wrap: self,
|
|
|
- col,
|
|
|
- op: CompareOp::Equals,
|
|
|
- given,
|
|
|
- _ghost: PhantomData,
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- fn by_op<C: EntityColumn<Entity = Self::Table>, G: Modelable + ?Sized>(
|
|
|
- self,
|
|
|
- col: C,
|
|
|
- op: CompareOp,
|
|
|
- given: &'r G
|
|
|
- ) -> Filter<'r, 'q, Self, C, G>
|
|
|
- where
|
|
|
- Self: Sized,
|
|
|
- {
|
|
|
- Filter {
|
|
|
- wrap: self,
|
|
|
- col,
|
|
|
- op,
|
|
|
- given,
|
|
|
- _ghost: PhantomData,
|
|
|
- }
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-pub struct Select<'r, 'q, T: Entity> {
|
|
|
- qi: &'r QueryInterface<'q>,
|
|
|
- _ghost: PhantomData<T>,
|
|
|
-}
|
|
|
-
|
|
|
-impl<'r, 'q, T: Entity> Select<'r, 'q, T> {
|
|
|
- pub fn new(qi: &'r QueryInterface<'q>) -> Self {
|
|
|
- Self {
|
|
|
- qi,
|
|
|
- _ghost: std::marker::PhantomData,
|
|
|
- }
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-impl<'r, 'q, T: Entity> StaticVersion for Select<'r, 'q, T> {
|
|
|
- type Is = Select<'static, 'static, T>;
|
|
|
-}
|
|
|
-
|
|
|
-impl<'r, 'q, T: Entity> QueryComponent for Select<'r, 'q, T> {
|
|
|
- fn derive(&self) -> DerivedQuery {
|
|
|
- DerivedQuery::new().add(
|
|
|
- QueryPart::Root,
|
|
|
- format!("SELECT * FROM {}", T::table_name()),
|
|
|
- )
|
|
|
- }
|
|
|
-
|
|
|
- fn contribute<H: Hasher>(&self, hasher: &mut H) {
|
|
|
- "select".hash(hasher);
|
|
|
- std::any::TypeId::of::<T>().hash(hasher);
|
|
|
- }
|
|
|
-
|
|
|
- // next binding point is the first
|
|
|
- fn bind(&self, _stmt: &mut sqlite::Statement<'_>) -> Result<usize, Error> {
|
|
|
- Ok(1)
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-impl<'r, 'q, T: Entity> Filterable<'r, 'q> for Select<'r, 'q, T> {
|
|
|
- type Table = T;
|
|
|
-}
|
|
|
-
|
|
|
-impl<'r, 'q, T: Entity> Resolvable<'r, 'q, T> for Select<'r, 'q, T> {
|
|
|
- fn qi(&self) -> &'r QueryInterface<'q> {
|
|
|
- self.qi
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-pub struct Delete<'r, 'q, T: Entity> {
|
|
|
- qi: &'r QueryInterface<'q>,
|
|
|
- _ghost: PhantomData<T>,
|
|
|
-}
|
|
|
-
|
|
|
-impl<'r, 'q, T: Entity> Delete<'r, 'q, T> {
|
|
|
- pub fn new(qi: &'r QueryInterface<'q>) -> Self {
|
|
|
- Self {
|
|
|
- qi,
|
|
|
- _ghost: std::marker::PhantomData,
|
|
|
- }
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-impl<'r, 'q, T: Entity> StaticVersion for Delete<'r, 'q, T> {
|
|
|
- type Is = Select<'static, 'static, T>;
|
|
|
-}
|
|
|
-
|
|
|
-impl<'r, 'q, T: Entity> QueryComponent for Delete<'r, 'q, T> {
|
|
|
- fn derive(&self) -> DerivedQuery {
|
|
|
- DerivedQuery::new().add(QueryPart::Root, format!("DELETE FROM {}", T::table_name()))
|
|
|
- }
|
|
|
-
|
|
|
- fn contribute<H: Hasher>(&self, hasher: &mut H) {
|
|
|
- "delete".hash(hasher);
|
|
|
- std::any::TypeId::of::<T>().hash(hasher);
|
|
|
- }
|
|
|
-
|
|
|
- // next binding point is the first
|
|
|
- fn bind(&self, _stmt: &mut sqlite::Statement<'_>) -> Result<usize, Error> {
|
|
|
- Ok(1)
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-impl<'r, 'q, T: Entity> Filterable<'r, 'q> for Delete<'r, 'q, T> {
|
|
|
- type Table = T;
|
|
|
-}
|
|
|
-
|
|
|
-impl<'r, 'q, T: Entity> Resolvable<'r, 'q, T> for Delete<'r, 'q, T> {
|
|
|
- fn qi(&self) -> &'r QueryInterface<'q> {
|
|
|
- self.qi
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-/// A concrete WHERE clause
|
|
|
-pub struct Filter<'r, 'q, F: Filterable<'r, 'q>, C: EntityColumn, G: Modelable + ?Sized>
|
|
|
-where
|
|
|
- 'q: 'r,
|
|
|
-{
|
|
|
- wrap: F,
|
|
|
- col: C,
|
|
|
- op: CompareOp,
|
|
|
- given: &'r G,
|
|
|
- _ghost: PhantomData<&'q ()>,
|
|
|
-}
|
|
|
-
|
|
|
-impl<'r, 'q, F: Filterable<'r, 'q>, C: EntityColumn, G: Modelable + ?Sized> StaticVersion
|
|
|
- for Filter<'r, 'q, F, C, G>
|
|
|
-where
|
|
|
- <F as StaticVersion>::Is: Filterable<'static, 'static>,
|
|
|
-{
|
|
|
- type Is = Filter<'static, 'static, <F as StaticVersion>::Is, C, u64>;
|
|
|
-}
|
|
|
-
|
|
|
-impl<'r, 'q, F: Filterable<'r, 'q>, C: EntityColumn, G: Modelable + ?Sized> QueryComponent
|
|
|
- for Filter<'r, 'q, F, C, G>
|
|
|
-where
|
|
|
- <F as StaticVersion>::Is: Filterable<'static, 'static>,
|
|
|
- 'q: 'r,
|
|
|
-{
|
|
|
- fn derive(&self) -> DerivedQuery {
|
|
|
- self.wrap
|
|
|
- .derive()
|
|
|
- .add(QueryPart::Where, format!("{} {} ?", self.col.name(), self.op.ch()))
|
|
|
- }
|
|
|
-
|
|
|
- fn contribute<H: Hasher>(&self, hasher: &mut H) {
|
|
|
- self.wrap.contribute(hasher);
|
|
|
- std::any::TypeId::of::<Self::Is>().hash(hasher);
|
|
|
- std::any::TypeId::of::<C>().hash(hasher);
|
|
|
- }
|
|
|
-
|
|
|
- fn bind(&self, stmt: &mut sqlite::Statement<'_>) -> Result<usize, crate::Error> {
|
|
|
- let next_index = self.wrap.bind(stmt)?;
|
|
|
-
|
|
|
- self.given.bind_to(stmt, next_index)?;
|
|
|
-
|
|
|
- Ok(next_index + 1)
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-impl<'r, 'q, F: Filterable<'r, 'q>, C: EntityColumn, G: Modelable + ?Sized>
|
|
|
- Resolvable<'r, 'q, C::Entity> for Filter<'r, 'q, F, C, G>
|
|
|
-where
|
|
|
- <F as StaticVersion>::Is: Filterable<'static, 'static>,
|
|
|
- 'q: 'r,
|
|
|
-{
|
|
|
- fn qi(&self) -> &'r QueryInterface<'q> {
|
|
|
- self.wrap.qi()
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-impl<'r, 'q, F: Filterable<'r, 'q>, C: EntityColumn, G: Modelable + ?Sized> Filterable<'r, 'q>
|
|
|
- for Filter<'r, 'q, F, C, G>
|
|
|
-where
|
|
|
- <F as StaticVersion>::Is: Filterable<'static, 'static>,
|
|
|
- 'q: 'r,
|
|
|
-{
|
|
|
- type Table = C::Entity;
|
|
|
-}
|