Browse Source

Rustfmt pass and removed legacy interface.

Kestrel 2 years ago
parent
commit
5e31a525c0
5 changed files with 227 additions and 357 deletions
  1. 2 1
      README.md
  2. 1 0
      microrm/src/error.rs
  3. 44 32
      microrm/src/lib.rs
  4. 42 267
      microrm/src/query.rs
  5. 138 57
      microrm/src/query/build.rs

+ 2 - 1
README.md

@@ -16,6 +16,7 @@ Querying supports a small subset of SQL expressed as type composition.
 A simple example using an SQLite table as an (indexed) key/value store
 might look something like this:
 ```rust
+use microrm::prelude::*;
 use microrm::{Entity,make_index};
 #[derive(Debug,Entity,serde::Serialize,serde::Deserialize)]
 pub struct KVStore {
@@ -44,7 +45,7 @@ qi.add(&KVStore {
 });
 
 // because KVStoreIndex indexes key, this is a logarithmic lookup
-let qr = qi.get_one_by(KVStore::Key, "a_key");
+let qr = qi.get().by(KVStore::Key, "a_key").one().expect("No errors encountered");
 
 assert_eq!(qr.is_some(), true);
 assert_eq!(qr.as_ref().unwrap().key, "a_key");

+ 1 - 0
microrm/src/error.rs

@@ -5,6 +5,7 @@ pub enum Error {
     StoreError(String),
     EmptyStoreError,
     CreateError,
+    ExecFailure
 }
 
 impl From<sqlite::Error> for Error {

+ 44 - 32
microrm/src/lib.rs

@@ -16,6 +16,12 @@ pub use error::Error;
 pub use query::{QueryInterface, WithID};
 pub use schema::Schema;
 
+pub mod prelude {
+    pub use crate::query::build::{Filterable,Resolvable};
+}
+
+use prelude::*;
+
 #[macro_export]
 macro_rules! value_list {
     ( $( $element:expr ),* ) => {
@@ -41,6 +47,13 @@ pub enum DBError {
     DropFailure,
     CreateFailure,
     SanityCheckFailure,
+    InternalFailure(crate::Error)
+}
+
+impl From<crate::Error> for DBError {
+    fn from(err: crate::Error) -> Self {
+        Self::InternalFailure(err)
+    }
 }
 
 #[derive(PartialEq, Debug)]
@@ -150,7 +163,7 @@ impl DB {
         }
 
         let qi = query::QueryInterface::new(self);
-        let hash: Option<WithID<Metaschema>> = qi.get_one_by(meta::Metaschema::Key, "schema_hash");
+        let hash: Option<WithID<Metaschema>> = qi.get().by(meta::Metaschema::Key, "schema_hash").one()?;
 
         if hash.is_none() {
             if mode == CreateMode::MustExist {
@@ -187,11 +200,11 @@ impl DB {
             value: self.schema_hash.clone(),
         });
 
-        assert!(add_result.is_some());
+        assert!(add_result.is_ok());
 
-        let sanity_check = qi.get_one_by(meta::Metaschema::Key, "schema_hash");
-        assert!(sanity_check.is_some());
-        assert_eq!(sanity_check.unwrap().value, self.schema_hash);
+        let sanity_check = qi.get().by(meta::Metaschema::Key, "schema_hash").one();
+        assert!(sanity_check.is_ok() && sanity_check.as_ref().unwrap().is_some());
+        assert_eq!(sanity_check.unwrap().unwrap().value, self.schema_hash);
 
         Ok(())
     }
@@ -262,6 +275,8 @@ mod pool_test {
 
 #[cfg(test)]
 mod test {
+    use crate::prelude::*;
+
     use super::DB;
 
     #[derive(serde::Serialize, serde::Deserialize, crate::Entity)]
@@ -296,7 +311,7 @@ mod test {
         let id = qi.add(&S1 { an_id: -1 }).expect("Can't add S1");
         let child_id = qi.add(&S2 { parent_id: id }).expect("Can't add S2");
 
-        qi.get_one_by_id(child_id).expect("Can't get S2 instance");
+        qi.get().by(S2::ID, &child_id).one().expect("Can't get S2 instance");
     }
 
     microrm_macros::make_index_internal!(S2ParentIndex, S2::ParentId);
@@ -304,6 +319,8 @@ mod test {
 
 #[cfg(test)]
 mod test2 {
+    use crate::prelude::*;
+
     #[derive(Debug, crate::Entity, serde::Serialize, serde::Deserialize)]
     #[microrm_internal]
     pub struct KVStore {
@@ -331,19 +348,22 @@ mod test2 {
         qi.add(&KVStore {
             key: "a_key".to_string(),
             value: "a_value".to_string(),
-        });
+        }).unwrap();
 
         // because KVStoreIndex indexes key, this is a logarithmic lookup
-        let qr = qi.get_one_by(KVStore::Key, "a_key");
+        let qr = qi.get().by(KVStore::Key, "a_key").one();
 
-        assert_eq!(qr.is_some(), true);
-        assert_eq!(qr.as_ref().unwrap().key, "a_key");
-        assert_eq!(qr.as_ref().unwrap().value, "a_value");
+        assert_eq!(qr.is_ok(), true);
+        assert_eq!(qr.as_ref().unwrap().is_some(), true);
+        assert_eq!(qr.as_ref().unwrap().as_ref().unwrap().key, "a_key");
+        assert_eq!(qr.as_ref().unwrap().as_ref().unwrap().value, "a_value");
     }
 }
 
 #[cfg(test)]
 mod delete_test {
+    use crate::prelude::*;
+
     #[derive(Debug, crate::Entity, serde::Serialize, serde::Deserialize)]
     #[microrm_internal]
     pub struct KVStore {
@@ -361,47 +381,39 @@ mod delete_test {
         qi.add(&KVStore {
             key: "a".to_string(),
             value: "a_value".to_string(),
-        });
+        }).unwrap();
 
         let insert_two = || {
             qi.add(&KVStore {
                 key: "a".to_string(),
                 value: "a_value".to_string(),
-            });
+            }).unwrap();
 
             qi.add(&KVStore {
                 key: "a".to_string(),
                 value: "another_value".to_string(),
-            });
+            }).unwrap();
         };
 
-        assert!(qi.get_one_by(KVStore::Key, "a").is_some());
-        // is_some() implies no errors were encountered
-        assert!(qi.delete_by(KVStore::Key, "a").is_some());
-        assert!(qi.get_one_by(KVStore::Key, "a").is_none());
+        assert!(qi.get().by(KVStore::Key, "a").one().is_ok());
+        assert!(qi.delete().by(KVStore::Key, "a").exec().is_ok());
+        assert!(qi.get().by(KVStore::Key, "a").one().unwrap().is_none());
 
         insert_two();
 
-        // this should fail as there is more than one thing matching key='a'
-        assert!(qi.get_one_by(KVStore::Key, "a").is_none());
-        let all = qi.get_all_by(KVStore::Key, "a");
-        assert!(all.is_some());
+        let all = qi.get().by(KVStore::Key, "a").all();
+        assert!(all.is_ok());
         assert_eq!(all.unwrap().len(), 2);
 
-        assert!(qi.delete_by(KVStore::Key, "b").is_some());
+        assert!(qi.delete().by(KVStore::Key, "b").exec().is_ok());
 
-        let all = qi.get_all_by(KVStore::Key, "a");
-        assert!(all.is_some());
+        let all = qi.get().by(KVStore::Key, "a").all();
+        assert!(all.is_ok());
         assert_eq!(all.unwrap().len(), 2);
 
-        assert!(qi
-            .delete_by_multi(
-                &[&KVStore::Key, &KVStore::Value],
-                &crate::value_list![&"a", &"another_value"]
-            )
-            .is_some());
+        assert!(qi.delete().by(KVStore::Key, &"a").by(KVStore::Value, &"another_value").exec().is_ok());
 
-        let one = qi.get_one_by(KVStore::Key, "a");
+        let one = qi.get().by(KVStore::Key, "a").one().unwrap();
         assert!(one.is_some());
         assert_eq!(one.unwrap().value, "a_value");
     }

+ 42 - 267
microrm/src/query.rs

@@ -1,14 +1,13 @@
-use std::hash::Hash;
+use crate::prelude::*;
+use std::hash::{Hash,Hasher};
 
 use crate::entity::{Entity, EntityColumn, EntityID};
 use crate::model::Modelable;
 
-// pub mod expr;
-// pub mod condition;
-// pub mod builder;
+
 pub mod build;
 
-pub use build::{Filterable,Resolvable};
+pub use build::{Filterable, Resolvable};
 
 /// Wraps an entity with its ID, for example as a query result.
 ///
@@ -62,7 +61,7 @@ impl<T: Entity> std::ops::DerefMut for WithID<T> {
     }
 }
 
-type CacheIndex = (&'static str, std::any::TypeId, u64);
+type CacheIndex = u64;
 
 /// The query interface for a database.
 ///
@@ -79,8 +78,6 @@ pub struct QueryInterface<'l> {
     prevent_send: std::marker::PhantomData<*mut ()>,
 }
 
-const NO_HASH: u64 = 0;
-
 impl<'l> QueryInterface<'l> {
     pub fn new(db: &'l crate::DB) -> Self {
         Self {
@@ -90,37 +87,30 @@ impl<'l> QueryInterface<'l> {
         }
     }
 
-    /// Helper function to process an expected single result
-    /// Note that this errors out if there is more than a single result
+    /// Helper function to process an expected single result, discarding the rest
     fn expect_one_result<T>(
         &self,
         stmt: &mut sqlite::Statement,
-        with_result: &mut dyn FnMut(&mut sqlite::Statement) -> Option<T>,
-    ) -> Option<T> {
-        let state = stmt.next().ok()?;
+        with_result: &mut dyn FnMut(&mut sqlite::Statement) -> Result<T, crate::Error>,
+    ) -> Result<Option<T>, crate::Error> {
+        let state = stmt.next()?;
         if state != sqlite::State::Row {
-            return None;
-        }
-
-        let res = with_result(stmt);
-
-        let state = stmt.next().ok()?;
-        if state != sqlite::State::Done {
-            return None;
+            return Ok(None)
+            // return Err(crate::Error::ExecFailure)
         }
 
-        res
+        Ok(Some(with_result(stmt)?))
     }
 
     /// Helper function to process an expected zero results
     /// Note that this errors out if there is any result
-    fn expect_no_result(&self, stmt: &mut sqlite::Statement) -> Option<()> {
-        let state = stmt.next().ok()?;
+    fn expect_no_result(&self, stmt: &mut sqlite::Statement) -> Result<(), crate::Error> {
+        let state = stmt.next()?;
         if state != sqlite::State::Done {
-            return None;
+            return Err(crate::Error::ExecFailure);
         }
 
-        Some(())
+        Ok(())
     }
 }
 
@@ -133,251 +123,28 @@ impl<'l> QueryInterface<'l> {
         &self,
         hash: u64,
         create: Create,
-        mut with: With) -> Return {
-
-        let mut cache = self.cache.lock().expect("Couldn't acquire cache?");
-        let key = ("", std::any::TypeId::of::<()>(), hash);
-        let query = cache.entry(key).or_insert_with(create);
-        query.reset().expect("Couldn't reset query");
-        with(query)
-    }
-
-    fn cached_query<Return>(
-        &self,
-        context: &'static str,
-        ty: std::any::TypeId,
-        create: &dyn Fn() -> sqlite::Statement<'l>,
-        with: &mut dyn FnMut(&mut sqlite::Statement<'l>) -> Return,
-    ) -> Return {
-        let mut cache = self.cache.lock().expect("Couldn't acquire cache?");
-        let key = (context, ty, NO_HASH);
-        let query = cache.entry(key).or_insert_with(create);
-
-        query.reset().expect("Couldn't reset query");
-        with(query)
-    }
-
-    fn cached_query_column<T: Entity, Return>(
-        &self,
-        context: &'static str,
-        ty: std::any::TypeId,
-        variant: &[&dyn EntityColumn<Entity = T>],
-        create: &dyn Fn() -> sqlite::Statement<'l>,
-        with: &mut dyn FnMut(&mut sqlite::Statement<'l>) -> Return,
-    ) -> Return {
-        use std::hash::Hasher;
-
-        let mut hasher = std::collections::hash_map::DefaultHasher::new();
-        for v in variant {
-            v.column_typeid().hash(&mut hasher);
-        }
-        let hash = hasher.finish();
-
+        mut with: With,
+    ) -> Return
+    where {
         let mut cache = self.cache.lock().expect("Couldn't acquire cache?");
-        let key = (context, ty, hash);
-        let query = cache.entry(key).or_insert_with(create);
-
+        let query = cache.entry(hash).or_insert_with(create);
         query.reset().expect("Couldn't reset query");
         with(query)
     }
 }
 
-// Legacy interface
 impl<'l> QueryInterface<'l> {
-    /// Search for an entity by a property
-    pub fn get_one_by<C: EntityColumn, V: Modelable>(
-        &self,
-        col: C,
-        val: V,
-    ) -> Option<WithID<C::Entity>> {
-        self.get().by(col, &val).result()
-    }
-
-    /// Search for an entity by multiple properties
-    pub fn get_one_by_multi<T: Entity>(
-        &self,
-        c: &[&dyn EntityColumn<Entity = T>],
-        val: &[&dyn crate::model::Modelable],
-    ) -> Option<WithID<T>> {
-        let table_name = T::table_name();
-
-        assert_eq!(c.len(), val.len());
-
-        self.cached_query_column(
-            "get_one_by_multi",
-            std::any::TypeId::of::<T>(),
-            c,
-            &|| {
-                let query = format!(
-                    "SELECT * FROM \"{}\" WHERE {}",
-                    table_name,
-                    c.iter()
-                        .map(|col| format!("\"{}\" = ?", col.name()))
-                        .collect::<Vec<_>>()
-                        .join(" AND ")
-                );
-                self.db
-                    .conn
-                    .prepare(&query)
-                    .expect(format!("Failed to prepare SQL query: {}", query).as_str())
-            },
-            &mut |stmt| {
-                for index in 0..val.len() {
-                    val[index].bind_to(stmt, index + 1).ok()?;
-                }
-
-                self.expect_one_result(stmt, &mut |stmt| {
-                    let id: i64 = stmt.read(0).ok()?;
-                    Some(WithID::wrap(T::build_from(stmt).ok()?, id))
-                })
-            },
-        )
-    }
-
-    /// Delete entities by searching with a single property
-    pub fn delete_by<C: EntityColumn, V: Modelable>(&self, c: C, val: V) -> Option<()> {
-        self.delete().by(c, &val).no_result();
-        Some(())
-    }
-
-    /// Delete entities by searching with a single property
-    pub fn delete_by_id<I: EntityID<Entity = T>, T: Entity<ID = I>>(
-        &self,
-        id: <T as Entity>::ID,
-    ) -> Option<()> {
-        let table_name = <T as Entity>::table_name();
-
-        self.cached_query(
-            "delete_by_id",
-            std::any::TypeId::of::<T>(),
-            &|| {
-                let query = format!("DELETE FROM \"{}\" WHERE id = ?", table_name);
-                self.db
-                    .conn
-                    .prepare(&query)
-                    .expect(format!("Failed to prepare SQL query: {}", query).as_str())
-            },
-            &mut |stmt| {
-                id.bind_to(stmt, 1).ok()?;
-
-                self.expect_no_result(stmt)
-            },
-        )
-    }
-
-    /// Delete entities by searching with multiple properties
-    pub fn delete_by_multi<T: Entity>(
-        &self,
-        c: &[&dyn EntityColumn<Entity = T>],
-        val: &[&dyn crate::model::Modelable],
-    ) -> Option<()> {
-        let table_name = <T>::table_name();
-
-        assert_eq!(c.len(), val.len());
-
-        self.cached_query_column(
-            "delete_by_multi",
-            std::any::TypeId::of::<T>(),
-            c,
-            &|| {
-                let query = format!(
-                    "DELETE FROM \"{}\" WHERE {}",
-                    table_name,
-                    c.iter()
-                        .map(|col| format!("\"{}\" = ?", col.name()))
-                        .collect::<Vec<_>>()
-                        .join(" AND ")
-                );
-                self.db
-                    .conn
-                    .prepare(&query)
-                    .expect(format!("Failed to prepare SQL query: {}", query).as_str())
-            },
-            &mut |stmt| {
-                for index in 0..val.len() {
-                    val[index].bind_to(stmt, index + 1).ok()?;
-                }
-
-                self.expect_no_result(stmt)
-            },
-        )
-    }
-
-    /// Search for an entity by ID
-    pub fn get_one_by_id<I: EntityID<Entity = T>, T: Entity>(&self, id: I) -> Option<WithID<T>> {
-        let table_name = <T as Entity>::table_name();
-
-        self.cached_query(
-            "get_one_by_id",
-            std::any::TypeId::of::<T>(),
-            &|| {
-                self.db
-                    .conn
-                    .prepare(&format!("SELECT * FROM \"{}\" WHERE id = ?", table_name))
-                    .expect("")
-            },
-            &mut |stmt| {
-                id.bind_to(stmt, 1).ok()?;
-
-                self.expect_one_result(stmt, &mut |stmt| {
-                    let id: i64 = stmt.read(0).ok()?;
-                    Some(WithID::wrap(T::build_from(stmt).ok()?, id))
-                })
-            },
-        )
-    }
-
-    /// Search for all entities matching a property
-    pub fn get_all_by<C: EntityColumn, V: Modelable>(
-        &self,
-        c: C,
-        val: V,
-    ) -> Option<Vec<WithID<C::Entity>>> {
-        let table_name = <C::Entity>::table_name();
-        let column_name = c.name();
-
-        self.cached_query_column(
-            "get_all_by",
-            std::any::TypeId::of::<C::Entity>(),
-            &[&c],
-            &|| {
-                self.db
-                    .conn
-                    .prepare(&format!(
-                        "SELECT * FROM \"{}\" WHERE {} = ?",
-                        table_name, column_name
-                    ))
-                    .expect("")
-            },
-            &mut |stmt| {
-                val.bind_to(stmt, 1).ok()?;
-
-                let mut res = Vec::new();
-                loop {
-                    let state = stmt.next().ok()?;
-                    if state == sqlite::State::Done {
-                        break;
-                    }
-
-                    let id: i64 = stmt.read(0).ok()?;
-                    res.push(WithID::wrap(C::Entity::build_from(stmt).ok()?, id));
-                }
-
-                Some(res)
-            },
-        )
-    }
-
-    
-    pub fn insert<T: Entity + serde::Serialize>(&self, m: &T) -> Option<<T as Entity>::ID> {
+    pub fn insert<T: Entity + serde::Serialize>(&self, m: &T) -> Result<<T as Entity>::ID, crate::Error> {
         self.add(m)
     }
 
     /// Add an entity to its table
-    pub fn add<T: Entity + serde::Serialize>(&self, m: &T) -> Option<<T as Entity>::ID> {
-        self.cached_query(
-            "add",
-            std::any::TypeId::of::<T>(),
+    pub fn add<T: Entity + serde::Serialize>(&self, m: &T) -> Result<<T as Entity>::ID, crate::Error> {
+        let mut hasher = std::collections::hash_map::DefaultHasher::new();
+        "add".hash(&mut hasher);
+        std::any::TypeId::of::<T>().hash(&mut hasher);
+        self.with_cache(
+            hasher.finish(),
             &|| {
                 let placeholders = (0..(<T as Entity>::column_count() - 1))
                     .map(|_| "?".to_string())
@@ -393,23 +160,31 @@ impl<'l> QueryInterface<'l> {
                     ))
                     .expect("")
             },
-            &mut |stmt| {
-                crate::model::store::serialize_into(stmt, m).ok()?;
+            &mut |mut stmt: &mut sqlite::Statement<'_>| {
+                crate::model::store::serialize_into(&mut stmt, m)?;
 
-                let rowid = self.expect_one_result(stmt, &mut |stmt| stmt.read::<i64>(0).ok())?;
+                let rowid = self.expect_one_result(&mut stmt, &mut |stmt| {
+                    stmt.read::<i64>(0).map_err(|x| crate::Error::DBError(x))
+                })?;
 
-                Some(<T as Entity>::ID::from_raw_id(rowid))
+                Ok(<T as Entity>::ID::from_raw_id(rowid.unwrap()))
             },
         )
     }
 }
 
 impl<'l> QueryInterface<'l> {
-    pub fn get<'a, 'b, T: Entity>(&'a self) -> build::Select<'b, 'l, T> where 'a: 'b {
+    pub fn get<'a, 'b, T: Entity>(&'a self) -> build::Select<'b, 'l, T>
+    where
+        'a: 'b,
+    {
         build::Select::new(self)
     }
 
-    pub fn delete<'a, 'b, T: Entity>(&'a self) -> build::Delete<'b, 'l, T> where 'a: 'b {
+    pub fn delete<'a, 'b, T: Entity>(&'a self) -> build::Delete<'b, 'l, T>
+    where
+        'a: 'b,
+    {
         build::Delete::new(self)
     }
 }
@@ -434,6 +209,6 @@ mod test_build {
         let db = crate::DB::new_in_memory(crate::Schema::new().entity::<KVStore>()).unwrap();
         let qi = db.query_interface();
 
-        assert!(qi.get().by(KVStore::Key, "abc").result().is_none());
+        assert!(qi.get().by(KVStore::Key, "abc").result().is_ok());
     }
 }

+ 138 - 57
microrm/src/query/build.rs

@@ -1,22 +1,25 @@
 //! Static in-place query construction.
 //!
-//! Potential interfaces:
-//! - `microrm::select().by(KVStore::Key, &key).result(qi)`
-//! - `qi.select().by(KVStore::Key, &key).result()`
+//! Interface:
+//! - `qi.get().by(KVStore::Key, &key).result()`
 
 use crate::{entity::EntityColumn, model::Modelable, Entity, Error};
-use std::{marker::PhantomData, collections::HashMap, hash::{Hasher, Hash}};
+use std::{
+    collections::HashMap,
+    hash::{Hash, Hasher},
+    marker::PhantomData,
+};
 
-use super::{WithID, QueryInterface};
+use super::{QueryInterface, WithID};
 
-#[derive(Debug,PartialEq,Eq,PartialOrd,Ord,Hash)]
+#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
 pub enum QueryPart {
     Root,
-    Where
+    Where,
 }
 
 #[derive(Debug)]
-pub struct DerivedQuery (HashMap<QueryPart, Vec<String>>);
+pub struct DerivedQuery(HashMap<QueryPart, Vec<String>>);
 
 impl DerivedQuery {
     fn new() -> Self {
@@ -33,15 +36,16 @@ impl DerivedQuery {
         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!(
+                    "WHERE {}",
+                    v.into_iter()
+                        .reduce(|a, b| format!("{} AND {}", a, b))
+                        .unwrap()
+                )
             }
         };
 
-        format!("{} {}",
-            root,
-            where_
-        )
+        format!("{} {}", root, where_)
     }
 }
 
@@ -66,26 +70,37 @@ pub trait QueryComponent: StaticVersion {
 }
 
 /// Any query that can be completed/executed
-pub trait Resolvable<'r, 'q, T: Entity>: QueryComponent where 'q: 'r {
+pub trait Resolvable<'r, 'q, T: Entity>: QueryComponent
+where
+    'q: 'r,
+{
     fn qi(&self) -> &'r QueryInterface<'q>;
 
-    fn no_result(self) -> Option<()> where Self: Sized {
+    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()
-            },
+            || self.qi().db.conn.prepare(self.derive().assemble()).unwrap(),
             |stmt| {
-                self.bind(stmt).ok()?;
+                self.bind(stmt)?;
 
                 self.qi().expect_no_result(stmt)
-            }
+            },
         )
     }
-    fn result(self) -> Option<WithID<T>> where Self: Sized {
+    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(
@@ -95,41 +110,65 @@ pub trait Resolvable<'r, 'q, T: Entity>: QueryComponent where 'q: 'r {
                 self.qi().db.conn.prepare(query).unwrap()
             },
             |stmt| {
-                self.bind(stmt).ok()?;
+                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,
-                    ))
+                    let id: i64 = stmt.read(0)?;
+                    Ok(WithID::wrap(T::build_from(stmt)?, id))
                 })
-            }
+            },
         )
     }
-    fn results(self) -> Vec<T> where Self: Sized {
+    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()
-            },
+            || self.qi().db.conn.prepare(self.derive().assemble()).unwrap(),
             |stmt| {
-                self.bind(stmt);
+                self.bind(stmt)?;
 
-                todo!()
-            }
+                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)
+            },
         )
     }
 }
 
 /// Any query that can have a WHERE clause attached to it
-pub trait Filterable<'r, 'q>: Resolvable<'r, 'q, Self::Table>  where 'q: 'r{
+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, given, _ghost: PhantomData }
+    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,
+            given,
+            _ghost: PhantomData,
+        }
     }
 }
 
@@ -140,17 +179,23 @@ pub struct Select<'r, 'q, T: Entity> {
 
 impl<'r, 'q, T: Entity> Select<'r, 'q, T> {
     pub fn new(qi: &'r QueryInterface<'q>) -> Self {
-        Self { qi, _ghost: std::marker::PhantomData }
+        Self {
+            qi,
+            _ghost: std::marker::PhantomData,
+        }
     }
 }
 
-impl <'r, 'q, T: Entity> StaticVersion for Select<'r, 'q, T> {
+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()))
+        DerivedQuery::new().add(
+            QueryPart::Root,
+            format!("SELECT * FROM {}", T::table_name()),
+        )
     }
 
     fn contribute<H: Hasher>(&self, hasher: &mut H) {
@@ -159,7 +204,9 @@ impl<'r, 'q, T: Entity> QueryComponent for Select<'r, 'q, T> {
     }
 
     // next binding point is the first
-    fn bind(&self, _stmt: &mut sqlite::Statement<'_>) -> Result<usize, Error> { Ok(1) }
+    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> {
@@ -167,7 +214,9 @@ impl<'r, 'q, T: Entity> Filterable<'r, 'q> for Select<'r, 'q, T> {
 }
 
 impl<'r, 'q, T: Entity> Resolvable<'r, 'q, T> for Select<'r, 'q, T> {
-    fn qi(&self) -> &'r QueryInterface<'q> { self.qi }
+    fn qi(&self) -> &'r QueryInterface<'q> {
+        self.qi
+    }
 }
 
 pub struct Delete<'r, 'q, T: Entity> {
@@ -177,11 +226,14 @@ pub struct Delete<'r, 'q, T: Entity> {
 
 impl<'r, 'q, T: Entity> Delete<'r, 'q, T> {
     pub fn new(qi: &'r QueryInterface<'q>) -> Self {
-        Self { qi, _ghost: std::marker::PhantomData }
+        Self {
+            qi,
+            _ghost: std::marker::PhantomData,
+        }
     }
 }
 
-impl <'r, 'q, T: Entity> StaticVersion for Delete<'r, 'q, T> {
+impl<'r, 'q, T: Entity> StaticVersion for Delete<'r, 'q, T> {
     type Is = Select<'static, 'static, T>;
 }
 
@@ -196,7 +248,9 @@ impl<'r, 'q, T: Entity> QueryComponent for Delete<'r, 'q, T> {
     }
 
     // next binding point is the first
-    fn bind(&self, _stmt: &mut sqlite::Statement<'_>) -> Result<usize, Error> { Ok(1) }
+    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> {
@@ -204,24 +258,40 @@ impl<'r, 'q, T: Entity> Filterable<'r, 'q> for Delete<'r, 'q, T> {
 }
 
 impl<'r, 'q, T: Entity> Resolvable<'r, 'q, T> for Delete<'r, 'q, T> {
-    fn qi(&self) -> &'r QueryInterface<'q> { self.qi }
+    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 {
+pub struct Filter<'r, 'q, F: Filterable<'r, 'q>, C: EntityColumn, G: Modelable + ?Sized>
+where
+    'q: 'r,
+{
     wrap: F,
     col: C,
     given: &'r G,
-    _ghost: PhantomData<&'q ()>
+    _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> {
+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 {
+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.wrap
+            .derive()
+            .add(QueryPart::Where, format!("{} = ?", self.col.name()))
     }
 
     fn contribute<H: Hasher>(&self, hasher: &mut H) {
@@ -239,11 +309,22 @@ 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>, 'q: 'r {
-    fn qi(&self) -> &'r QueryInterface<'q> { self.wrap.qi() }
+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 {
+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;
 }
-