Просмотр исходного кода

Add simpler with_cache function to QueryInterface.

Kestrel 2 лет назад
Родитель
Сommit
07e65230aa
2 измененных файлов с 106 добавлено и 38 удалено
  1. 22 0
      microrm/src/query.rs
  2. 84 38
      microrm/src/query/build.rs

+ 22 - 0
microrm/src/query.rs

@@ -123,6 +123,22 @@ impl<'l> QueryInterface<'l> {
 }
 
 impl<'l> QueryInterface<'l> {
+    fn with_cache<
+        Return,
+        Create: Fn() -> sqlite::Statement<'l>,
+        With: FnMut(&mut sqlite::Statement<'l>) -> Return,
+    >(
+        &self,
+        ty: std::any::TypeId,
+        create: Create,
+        mut with: With) -> Return {
+
+        let mut cache = self.cache.lock().expect("Couldn't acquire cache?");
+        let key = ("", ty, NO_HASH);
+        let query = cache.entry(key).or_insert_with(create);
+        with(query)
+    }
+
     fn cached_query<Return>(
         &self,
         context: &'static str,
@@ -425,6 +441,12 @@ impl<'l> QueryInterface<'l> {
     }
 }
 
+impl<'l> QueryInterface<'l> {
+    pub fn get<'a, 'b, T: Entity>(&'a self) -> build::Select<'b, 'l, T> where 'a: 'b {
+        build::Select::new(self)
+    }
+}
+
 /*
 qi.get().by(Table::Column, value).by(Table::OtherColumn, value).one()
 qi.get().id(some_id).one()

+ 84 - 38
microrm/src/query/build.rs

@@ -7,7 +7,7 @@
 use crate::{entity::EntityColumn, model::Modelable, Entity, Error};
 use std::{marker::PhantomData, collections::HashMap, hash::Hasher};
 
-use super::QueryInterface;
+use super::{WithID, QueryInterface};
 
 
 #[derive(Debug,PartialEq,Eq,PartialOrd,Ord,Hash)]
@@ -16,7 +16,35 @@ pub enum QueryPart {
     Where
 }
 
-pub type DerivedQuery = HashMap<QueryPart, Vec<String>>;
+#[derive(Debug)]
+pub struct DerivedQuery (HashMap<QueryPart, Vec<String>>);
+
+impl DerivedQuery {
+    fn new() -> Self {
+        Self { 0: HashMap::new() }
+    }
+
+    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 {
+        let root = self.0.remove(&QueryPart::Root).unwrap().remove(0);
+        let where_ = match self.0.remove(&QueryPart::Where) {
+            None => String::new(),
+            Some(v) => {
+                format!("WHERE {}",
+                    v.into_iter().reduce(|a, b| format!("{} AND {}", a, b)).unwrap())
+            }
+        };
+
+        format!("{} {}",
+            root,
+            where_
+        )
+    }
+}
 
 trait StaticVersion {
     type Is: 'static;
@@ -36,25 +64,53 @@ trait QueryComponent: StaticVersion {
     fn bind(&self, stmt: &mut sqlite::Statement<'_>) -> Result<usize, Error>;
 }
 
-/// Any query that can be completed/executed
-trait Resolvable<'r, 'q, T: Entity>: QueryComponent {
+// helper functions
+
+fn get_stmt<'a, 'l>(qi: &'a QueryInterface<'l>) -> &'a mut sqlite::Statement<'l> {
+    todo!()
+}
 
+/// Any query that can be completed/executed
+trait Resolvable<'r, 'q, T: Entity>: QueryComponent where 'q: 'r {
     fn qi(&self) -> &'r QueryInterface<'q>;
 
     fn no_result(self) where Self: Sized { todo!() }
-    fn result(self) -> Option<T> where Self: Sized { todo!() }
+    fn result(self) -> Option<WithID<T>> where Self: Sized {
+        self.qi().with_cache(
+            Self::type_id(),
+            || {
+                self.qi().db.conn.prepare(self.derive().assemble()).unwrap()
+            },
+            |stmt| {
+                self.bind(stmt);
+
+                self.qi().expect_one_result(stmt, &mut |stmt| {
+                    let id: i64 = stmt.read(0).ok()?;
+                    Some(WithID::wrap(
+                        T::build_from(stmt).ok()?,
+                        id,
+                    ))
+                })
+            }
+        )
+    }
     fn results(self) -> Vec<T> where Self: Sized {
-
-        // std::any::TypeId::of::<T>();
-
-        println!("derivation: {:?}", self.derive());
-
-        todo!()
+        self.qi().with_cache(
+            Self::type_id(),
+            || {
+                self.qi().db.conn.prepare(self.derive().assemble()).unwrap()
+            },
+            |stmt| {
+                self.bind(stmt);
+
+                todo!()
+            }
+        )
     }
 }
 
 /// Any query that can have a WHERE clause attached to it
-trait Filterable<'r, 'q>: Resolvable<'r, 'q, Self::Table> {
+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 {
@@ -62,20 +118,24 @@ trait Filterable<'r, 'q>: Resolvable<'r, 'q, Self::Table> {
     }
 }
 
-struct Select<'r, 'q, T: Entity> {
+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 {
-        HashMap::from([
-            (QueryPart::Root, vec![format!("SELECT * FROM {}", T::table_name())])
-        ])
+        DerivedQuery::new().add(QueryPart::Root, format!("SELECT * FROM {}", T::table_name()))
     }
 
     // next binding point is the first
@@ -91,7 +151,7 @@ impl<'r, 'q, T: Entity> Resolvable<'r, 'q, T> for Select<'r, 'q, T> {
 }
 
 /// A concrete WHERE clause
-struct Filter<'r, 'q, F: Filterable<'r, 'q>, C: EntityColumn, G: Modelable + ?Sized> {
+struct Filter<'r, 'q, F: Filterable<'r, 'q>, C: EntityColumn, G: Modelable + ?Sized> where 'q: 'r {
     wrap: F,
     col: C,
     given: &'r G,
@@ -102,16 +162,9 @@ impl<'r, 'q, F: Filterable<'r, 'q>, C: EntityColumn, G: Modelable + ?Sized> Stat
     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> {
+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 {
-        let mut parent = self.wrap.derive();
-
-        parent.entry(QueryPart::Where).or_insert_with(|| Vec::new()).push(
-            format!("{} = ?", self.col.name()));
-
-        // println!("derivation: {:?}", self.derive());
-
-        parent
+        self.wrap.derive().add(QueryPart::Where, format!("{} = ?", self.col.name()))
     }
 
     fn bind(&self, stmt: &mut sqlite::Statement<'_>) -> Result<usize, crate::Error> {
@@ -123,11 +176,11 @@ impl<'r, 'q, F: Filterable<'r, 'q>, C: EntityColumn, G: Modelable + ?Sized> Quer
     }
 }
 
-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> {
+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> {
+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;
 }
 
@@ -145,19 +198,12 @@ mod test_build {
         value: String,
     }
 
-    fn simple_test() {
+    #[test]
+    fn simple_get() {
         use super::*;
         let db = crate::DB::new_in_memory(crate::Schema::new().entity::<KVStore>()).unwrap();
         let qi = db.query_interface();
 
-        let select = Select::<KVStore> { qi: &qi, _ghost: PhantomData };
-        // drop(select);
-        let filter = select.by(KVStore::Key, "abc");
-        filter.results();
-    }
-
-    #[test]
-    fn simple_get() {
-        simple_test()
+        assert!(qi.get().by(KVStore::Key, "abc").result().is_none());
     }
 }